From d381be3868ef3aabaa7ace24c06d97d6a196c47c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 26 Oct 2022 15:50:59 -0500 Subject: [PATCH 0001/1033] Fix homekit diagnostics test when version changes (#81046) --- tests/components/homekit/test_diagnostics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/homekit/test_diagnostics.py b/tests/components/homekit/test_diagnostics.py index 15d4a6f6e2e..1f6f7c584f3 100644 --- a/tests/components/homekit/test_diagnostics.py +++ b/tests/components/homekit/test_diagnostics.py @@ -190,7 +190,7 @@ async def test_config_entry_accessory( "iid": 7, "perms": ["pr"], "type": "52", - "value": "2022.11.0", + "value": ANY, }, ], "iid": 1, From cee7b788d444781ed23e986a15e07d7a318c7b03 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 26 Oct 2022 23:44:38 +0200 Subject: [PATCH 0002/1033] Bump version to 2022.12.0dev0 (#81044) --- .github/workflows/ci.yaml | 2 +- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fc0ca593fbd..262229e6ade 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,7 +22,7 @@ on: env: CACHE_VERSION: 3 PIP_CACHE_VERSION: 3 - HA_SHORT_VERSION: 2022.11 + HA_SHORT_VERSION: 2022.12 DEFAULT_PYTHON: 3.9 ALL_PYTHON_VERSIONS: "['3.9', '3.10']" PRE_COMMIT_CACHE: ~/.cache/pre-commit diff --git a/homeassistant/const.py b/homeassistant/const.py index 112b1637c46..6fe8f3730ff 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2022 -MINOR_VERSION: Final = 11 +MINOR_VERSION: Final = 12 PATCH_VERSION: Final = "0.dev0" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" diff --git a/pyproject.toml b/pyproject.toml index 3ca463d6fc1..f5b5908f0d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.11.0.dev0" +version = "2022.12.0.dev0" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From 28b3d1fc7a9670f84a7bc4ee41dc74660a5ba51b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 26 Oct 2022 17:05:09 -0500 Subject: [PATCH 0003/1033] Bump aiohomekit to 2.2.5 (#81048) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 0bad4ed9f7b..24b2eebe615 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.4"], + "requirements": ["aiohomekit==2.2.5"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index 4a6b3708aae..709b686cbf1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.4 +aiohomekit==2.2.5 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1b39523ca42..7992fe48b6e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.4 +aiohomekit==2.2.5 # homeassistant.components.emulated_hue # homeassistant.components.http From 6e19629539d0dfecd7500e84ff067b2f6b856770 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 26 Oct 2022 18:03:13 -0500 Subject: [PATCH 0004/1033] Bump zeroconf to 0.39.3 (#81049) --- homeassistant/components/zeroconf/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zeroconf/manifest.json b/homeassistant/components/zeroconf/manifest.json index f0e2005b20e..967dd761ac7 100644 --- a/homeassistant/components/zeroconf/manifest.json +++ b/homeassistant/components/zeroconf/manifest.json @@ -2,7 +2,7 @@ "domain": "zeroconf", "name": "Zero-configuration networking (zeroconf)", "documentation": "https://www.home-assistant.io/integrations/zeroconf", - "requirements": ["zeroconf==0.39.2"], + "requirements": ["zeroconf==0.39.3"], "dependencies": ["network", "api"], "codeowners": ["@bdraco"], "quality_scale": "internal", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index c7dd51d76df..0a4ccf0f58e 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -42,7 +42,7 @@ typing-extensions>=4.4.0,<5.0 voluptuous-serialize==2.5.0 voluptuous==0.13.1 yarl==1.8.1 -zeroconf==0.39.2 +zeroconf==0.39.3 # Constrain pycryptodome to avoid vulnerability # see https://github.com/home-assistant/core/pull/16238 diff --git a/requirements_all.txt b/requirements_all.txt index 709b686cbf1..a5415a0e50f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2604,7 +2604,7 @@ zamg==0.1.1 zengge==0.2 # homeassistant.components.zeroconf -zeroconf==0.39.2 +zeroconf==0.39.3 # homeassistant.components.zha zha-quirks==0.0.84 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7992fe48b6e..522a69b3b1c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1805,7 +1805,7 @@ youless-api==0.16 zamg==0.1.1 # homeassistant.components.zeroconf -zeroconf==0.39.2 +zeroconf==0.39.3 # homeassistant.components.zha zha-quirks==0.0.84 From ce1b56e345cb0564e8a7da7a735c995fe9905c17 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 27 Oct 2022 00:30:06 +0000 Subject: [PATCH 0005/1033] [ci skip] Translation update --- .../components/anthemav/translations/en.json | 6 +++++ .../components/coinbase/translations/en.json | 6 +++++ .../generic/translations/pt-BR.json | 4 +-- .../components/oralb/translations/pt-BR.json | 4 +-- .../statistics/translations/es.json | 12 +++++++++ .../statistics/translations/et.json | 12 +++++++++ .../components/zamg/translations/ca.json | 26 +++++++++++++++++++ .../components/zamg/translations/de.json | 13 +++++++--- .../components/zamg/translations/es.json | 26 +++++++++++++++++++ .../components/zamg/translations/pl.json | 26 +++++++++++++++++++ 10 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 homeassistant/components/statistics/translations/es.json create mode 100644 homeassistant/components/statistics/translations/et.json create mode 100644 homeassistant/components/zamg/translations/ca.json create mode 100644 homeassistant/components/zamg/translations/es.json create mode 100644 homeassistant/components/zamg/translations/pl.json diff --git a/homeassistant/components/anthemav/translations/en.json b/homeassistant/components/anthemav/translations/en.json index 9177d5a6e70..af4c83eb2a8 100644 --- a/homeassistant/components/anthemav/translations/en.json +++ b/homeassistant/components/anthemav/translations/en.json @@ -15,5 +15,11 @@ } } } + }, + "issues": { + "deprecated_yaml": { + "description": "Configuring Anthem A/V Receivers using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Anthem A/V Receivers YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", + "title": "The Anthem A/V Receivers YAML configuration is being removed" + } } } \ No newline at end of file diff --git a/homeassistant/components/coinbase/translations/en.json b/homeassistant/components/coinbase/translations/en.json index 019159c8057..d755427d446 100644 --- a/homeassistant/components/coinbase/translations/en.json +++ b/homeassistant/components/coinbase/translations/en.json @@ -21,6 +21,12 @@ } } }, + "issues": { + "removed_yaml": { + "description": "Configuring Coinbase using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", + "title": "The Coinbase YAML configuration has been removed" + } + }, "options": { "error": { "currency_unavailable": "One or more of the requested currency balances is not provided by your Coinbase API.", diff --git a/homeassistant/components/generic/translations/pt-BR.json b/homeassistant/components/generic/translations/pt-BR.json index de9be64fd2a..e6d5260b49e 100644 --- a/homeassistant/components/generic/translations/pt-BR.json +++ b/homeassistant/components/generic/translations/pt-BR.json @@ -77,9 +77,9 @@ "step": { "confirm_still": { "data": { - "confirmed_ok": "Esta imagem parece boa." + "confirmed_ok": "Essa imagem parece boa." }, - "description": "![Camera Still Image Preview]({preview_url})", + "description": "![Visualiza\u00e7\u00e3o da imagem est\u00e1tica da c\u00e2mera]({preview_url})", "title": "Visualizar" }, "content_type": { diff --git a/homeassistant/components/oralb/translations/pt-BR.json b/homeassistant/components/oralb/translations/pt-BR.json index 0da7639fa2a..5b654163201 100644 --- a/homeassistant/components/oralb/translations/pt-BR.json +++ b/homeassistant/components/oralb/translations/pt-BR.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "already_in_progress": "A configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", + "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", "no_devices_found": "Nenhum dispositivo encontrado na rede", "not_supported": "Dispositivo n\u00e3o suportado" }, diff --git a/homeassistant/components/statistics/translations/es.json b/homeassistant/components/statistics/translations/es.json new file mode 100644 index 00000000000..da1b1833f57 --- /dev/null +++ b/homeassistant/components/statistics/translations/es.json @@ -0,0 +1,12 @@ +{ + "issues": { + "deprecation_warning_characteristic": { + "description": "El par\u00e1metro de configuraci\u00f3n `state_characteristic` de la integraci\u00f3n de estad\u00edsticas pasar\u00e1 a ser obligatorio.\n\nPor favor, a\u00f1ade `state_characteristic: {characteristic}` a la configuraci\u00f3n del sensor `{entity}` para mantener el comportamiento actual.\n\nLee la documentaci\u00f3n de la integraci\u00f3n de estad\u00edsticas para obtener m\u00e1s detalles: https://www.home-assistant.io/integrations/statistics/", + "title": "'state_characteristic' obligatorio asumido para una entidad de estad\u00edsticas" + }, + "deprecation_warning_size": { + "description": "El par\u00e1metro de configuraci\u00f3n `sampling_size` de la integraci\u00f3n de estad\u00edsticas ten\u00eda por defecto el valor 20 hasta ahora, algo que cambiar\u00e1.\n\nPor favor, verifica la configuraci\u00f3n del sensor `{entity}` y a\u00f1ade l\u00edmites adecuados, por ejemplo, `sampling_size: 20` para mantener el comportamiento actual. La configuraci\u00f3n de la integraci\u00f3n de estad\u00edsticas ser\u00e1 m\u00e1s flexible con la versi\u00f3n 2022.12.0 y aceptar\u00e1 `sampling_size` o `max_age`, o ambas configuraciones. La solicitud anterior prepara tu configuraci\u00f3n para este cambio que, de lo contrario, ser\u00eda importante.\n\nLee la documentaci\u00f3n de la integraci\u00f3n de estad\u00edsticas para obtener m\u00e1s detalles: https://www.home-assistant.io/integrations/statistics/", + "title": "'sampling_size' impl\u00edcito asumido para una entidad de estad\u00edsticas" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/statistics/translations/et.json b/homeassistant/components/statistics/translations/et.json new file mode 100644 index 00000000000..2c7fcbe3298 --- /dev/null +++ b/homeassistant/components/statistics/translations/et.json @@ -0,0 +1,12 @@ +{ + "issues": { + "deprecation_warning_characteristic": { + "description": "Statistika integreerimise konfiguratsiooniparameeter \u201estate_characteristic\u201d muutub kohustuslikuks. \n\n Praeguse k\u00e4itumise s\u00e4ilitamiseks lisa anduri {entity} konfiguratsioonile \"state_characteristic: {characteristic} \". \n\n Lisateabe saamiseks loe statistika integreerimise dokumentatsiooni: https://www.home-assistant.io/integrations/statistics/", + "title": "Statistika olemi puhul eeldatakse kohustuslikku \u201estate_characteristic\u201d." + }, + "deprecation_warning_size": { + "description": "Statistika integratsiooni konfiguratsiooniparameetri \u201esampling_size\u201d vaikev\u00e4\u00e4rtus on seni olnud 20, mis muutub. \n\n Kontrolli anduri ` {entity} ` konfiguratsiooni ja lisa praeguse k\u00e4itumise s\u00e4ilitamiseks sobivad piirid, nt `sampling_size: 20`. Statistika integreerimise konfiguratsioon muutub versiooniga 2022.12.0 paindlikumaks ja aktsepteeritakse kas \u201esampling_size\u201d v\u00f5i \u201emax_age\u201d v\u00f5i m\u00f5lemat seadet. \u00dclaltoodud taotlus valmistab teie konfiguratsiooni ette selle muidu puruneva muudatuse jaoks. \n\n Lisateabe saamiseks loe statistika integreerimise dokumentatsiooni: https://www.home-assistant.io/integrations/statistics/", + "title": "Kaudne \"sampling_size\" mida eeldatakse statistika\u00fcksuse puhul" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/ca.json b/homeassistant/components/zamg/translations/ca.json new file mode 100644 index 00000000000..cc116086966 --- /dev/null +++ b/homeassistant/components/zamg/translations/ca.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat", + "cannot_connect": "Ha fallat la connexi\u00f3" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "ID de l'estaci\u00f3 (per defecte el de l'estaci\u00f3 m\u00e9s propera)" + }, + "description": "Configura la integraci\u00f3 de ZAMG amb Home Assistant." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "La configuraci\u00f3 de ZAMG mitjan\u00e7ant YAML s'eliminar\u00e0 de Home Assistant.\n\nLa configuraci\u00f3 YAML existent s'ha importat autom\u00e0ticament a la interf\u00edcie d'usuari.\n\nElimina la configuraci\u00f3 YAML de ZAMG del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", + "title": "La configuraci\u00f3 YAML de ZAMG est\u00e0 sent eliminada" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/de.json b/homeassistant/components/zamg/translations/de.json index 084d65de978..abccc76860c 100644 --- a/homeassistant/components/zamg/translations/de.json +++ b/homeassistant/components/zamg/translations/de.json @@ -1,21 +1,26 @@ { "config": { "abort": { - "already_configured": "Wetterstation ist bereits konfiguriert", + "already_configured": "Ger\u00e4t ist bereits konfiguriert", "cannot_connect": "Verbindung fehlgeschlagen" }, "error": { - "unknown": "ID der Wetterstation ist unbekannt", "cannot_connect": "Verbindung fehlgeschlagen" }, "flow_title": "{name}", "step": { "user": { "data": { - "station_id": "ID der Wetterstation (nächstgelegene Station as Defaultwert)" + "station_id": "Stations-ID (standardm\u00e4\u00dfig n\u00e4chste Station)" }, - "description": "Richte zamg f\u00fcr die Integration mit Home Assistant ein." + "description": "Richte ZAMG f\u00fcr die Integration mit Home Assistant ein." } } + }, + "issues": { + "deprecated_yaml": { + "description": "Die Konfiguration von ZAMG mit YAML wird entfernt. \n\nDeine vorhandene YAML-Konfiguration wurde automatisch in die Benutzeroberfl\u00e4che importiert. \n\nEntferne die ZAMG-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", + "title": "Die ZAMG YAML-Konfiguration wird entfernt" + } } } \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/es.json b/homeassistant/components/zamg/translations/es.json new file mode 100644 index 00000000000..dbd8b9af040 --- /dev/null +++ b/homeassistant/components/zamg/translations/es.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "cannot_connect": "No se pudo conectar" + }, + "error": { + "cannot_connect": "No se pudo conectar" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "ID de la estaci\u00f3n (por defecto, la estaci\u00f3n m\u00e1s cercana)" + }, + "description": "Configura ZAMG para que se integre con Home Assistant." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Se va a eliminar la configuraci\u00f3n de ZAMG mediante YAML. \n\nTu configuraci\u00f3n YAML existente se ha importado a la IU autom\u00e1ticamente. \n\nElimina la configuraci\u00f3n YAML de ZAMG de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", + "title": "Se va a eliminar la configuraci\u00f3n YAML de ZAMG" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/pl.json b/homeassistant/components/zamg/translations/pl.json new file mode 100644 index 00000000000..8837a14ecd6 --- /dev/null +++ b/homeassistant/components/zamg/translations/pl.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "Identyfikator stacji (domy\u015blnie najbli\u017csza stacja)" + }, + "description": "Skonfiguruj ZAMG, aby zintegrowa\u0107 go z Home Assistantem." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfiguracja ZAMG przy u\u017cyciu YAML zostanie usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML zosta\u0142a automatycznie zaimportowana do interfejsu u\u017cytkownika. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", + "title": "Konfiguracja YAML dla ZAMG zostanie usuni\u0119ta" + } + } +} \ No newline at end of file From b8edc86500f3208b48663c16fd274dc316d8dc8b Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 26 Oct 2022 21:29:48 -0400 Subject: [PATCH 0006/1033] Handle sending ZCL commands with empty bitmap options (#81051) Handle sending commands with empty bitmaps --- homeassistant/components/zha/core/helpers.py | 30 +++++++------------- tests/components/zha/test_helpers.py | 14 +++++++++ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/zha/core/helpers.py b/homeassistant/components/zha/core/helpers.py index 409d45789b5..1ea9a2a4c9b 100644 --- a/homeassistant/components/zha/core/helpers.py +++ b/homeassistant/components/zha/core/helpers.py @@ -14,7 +14,6 @@ import enum import functools import itertools import logging -import operator from random import uniform import re from typing import TYPE_CHECKING, Any, TypeVar @@ -163,25 +162,16 @@ def convert_to_zcl_values( if field.name not in fields: continue value = fields[field.name] - if issubclass(field.type, enum.Flag): - if isinstance(value, list): - value = field.type( - functools.reduce( - operator.ior, - [ - field.type[flag.replace(" ", "_")] - if isinstance(flag, str) - else field.type(flag) - for flag in value - ], - ) - ) - else: - value = ( - field.type[value.replace(" ", "_")] - if isinstance(value, str) - else field.type(value) - ) + if issubclass(field.type, enum.Flag) and isinstance(value, list): + new_value = 0 + + for flag in value: + if isinstance(flag, str): + new_value |= field.type[flag.replace(" ", "_")] + else: + new_value |= flag + + value = field.type(new_value) elif issubclass(field.type, enum.Enum): value = ( field.type[value.replace(" ", "_")] diff --git a/tests/components/zha/test_helpers.py b/tests/components/zha/test_helpers.py index f5fb5c4f5c0..64f8c732ca9 100644 --- a/tests/components/zha/test_helpers.py +++ b/tests/components/zha/test_helpers.py @@ -195,3 +195,17 @@ async def test_zcl_schema_conversions(hass, device_light): assert isinstance(converted_data["start_hue"], uint16_t) assert converted_data["start_hue"] == 196 + + # This time, the update flags bitmap is empty + raw_data = { + "update_flags": [], + "action": 0x02, + "direction": 0x01, + "time": 20, + "start_hue": 196, + } + + converted_data = convert_to_zcl_values(raw_data, command_schema) + + # No flags are passed through + assert converted_data["update_flags"] == 0 From 91c6892001e340bf05dd44269e94f7defd9a419b Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 26 Oct 2022 21:47:38 -0400 Subject: [PATCH 0007/1033] Bump zigpy to 0.51.4 (#81050) Bump zigpy from 0.51.3 to 0.51.4 --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 4698c78d384..1c86fe52c5e 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -9,7 +9,7 @@ "pyserial-asyncio==0.6", "zha-quirks==0.0.84", "zigpy-deconz==0.19.0", - "zigpy==0.51.3", + "zigpy==0.51.4", "zigpy-xbee==0.16.2", "zigpy-zigate==0.10.2", "zigpy-znp==0.9.1" diff --git a/requirements_all.txt b/requirements_all.txt index a5415a0e50f..387e9e33360 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2628,7 +2628,7 @@ zigpy-zigate==0.10.2 zigpy-znp==0.9.1 # homeassistant.components.zha -zigpy==0.51.3 +zigpy==0.51.4 # homeassistant.components.zoneminder zm-py==0.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 522a69b3b1c..fd785aa01c3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1823,7 +1823,7 @@ zigpy-zigate==0.10.2 zigpy-znp==0.9.1 # homeassistant.components.zha -zigpy==0.51.3 +zigpy==0.51.4 # homeassistant.components.zwave_js zwave-js-server-python==0.43.0 From f9c16fab1a68d4a67ebf6b0d366331cf23f73130 Mon Sep 17 00:00:00 2001 From: Avi Miller Date: Thu, 27 Oct 2022 15:01:15 +1100 Subject: [PATCH 0008/1033] Bump aiolifx-themes to 0.2.0 (#81059) --- homeassistant/components/lifx/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/lifx/manifest.json b/homeassistant/components/lifx/manifest.json index da07a2ffc8b..fc5422757b9 100644 --- a/homeassistant/components/lifx/manifest.json +++ b/homeassistant/components/lifx/manifest.json @@ -6,7 +6,7 @@ "requirements": [ "aiolifx==0.8.6", "aiolifx_effects==0.3.0", - "aiolifx_themes==0.1.1" + "aiolifx_themes==0.2.0" ], "quality_scale": "platinum", "dependencies": ["network"], diff --git a/requirements_all.txt b/requirements_all.txt index 387e9e33360..d08127cc558 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -196,7 +196,7 @@ aiolifx==0.8.6 aiolifx_effects==0.3.0 # homeassistant.components.lifx -aiolifx_themes==0.1.1 +aiolifx_themes==0.2.0 # homeassistant.components.lookin aiolookin==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fd785aa01c3..9a139e5b727 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -174,7 +174,7 @@ aiolifx==0.8.6 aiolifx_effects==0.3.0 # homeassistant.components.lifx -aiolifx_themes==0.1.1 +aiolifx_themes==0.2.0 # homeassistant.components.lookin aiolookin==0.1.1 From e785d04abf0fb447d7edf7ff696973c320a7ab79 Mon Sep 17 00:00:00 2001 From: shbatm Date: Wed, 26 Oct 2022 23:27:08 -0500 Subject: [PATCH 0009/1033] Add RainMachine config option to use default run times from app (#80984) --- .../components/rainmachine/__init__.py | 11 ++++--- .../components/rainmachine/config_flow.py | 24 +++++++++++---- homeassistant/components/rainmachine/const.py | 4 ++- .../components/rainmachine/strings.json | 3 +- .../components/rainmachine/switch.py | 29 +++++++++++++++++-- .../rainmachine/translations/en.json | 5 ++-- .../rainmachine/test_config_flow.py | 18 ++++++++---- .../rainmachine/test_diagnostics.py | 4 +-- 8 files changed, 74 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index a0b11653272..321a3a057af 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -38,7 +38,9 @@ from homeassistant.util.network import is_ip_address from .config_flow import get_client_controller from .const import ( - CONF_ZONE_RUN_TIME, + CONF_DEFAULT_ZONE_RUN_TIME, + CONF_DURATION, + CONF_USE_APP_RUN_TIMES, DATA_API_VERSIONS, DATA_MACHINE_FIRMWARE_UPDATE_STATUS, DATA_PROGRAMS, @@ -67,7 +69,6 @@ PLATFORMS = [ CONF_CONDITION = "condition" CONF_DEWPOINT = "dewpoint" -CONF_DURATION = "duration" CONF_ET = "et" CONF_MAXRH = "maxrh" CONF_MAXTEMP = "maxtemp" @@ -237,15 +238,17 @@ async def async_setup_entry( # noqa: C901 if not entry.unique_id or is_ip_address(entry.unique_id): # If the config entry doesn't already have a unique ID, set one: entry_updates["unique_id"] = controller.mac - if CONF_ZONE_RUN_TIME in entry.data: + if CONF_DEFAULT_ZONE_RUN_TIME in entry.data: # If a zone run time exists in the config entry's data, pop it and move it to # options: data = {**entry.data} entry_updates["data"] = data entry_updates["options"] = { **entry.options, - CONF_ZONE_RUN_TIME: data.pop(CONF_ZONE_RUN_TIME), + CONF_DEFAULT_ZONE_RUN_TIME: data.pop(CONF_DEFAULT_ZONE_RUN_TIME), } + if CONF_USE_APP_RUN_TIMES not in entry.options: + entry_updates["options"] = {**entry.options, CONF_USE_APP_RUN_TIMES: False} if entry_updates: hass.config_entries.async_update_entry(entry, **entry_updates) diff --git a/homeassistant/components/rainmachine/config_flow.py b/homeassistant/components/rainmachine/config_flow.py index eed80b9c145..b5ae42559bb 100644 --- a/homeassistant/components/rainmachine/config_flow.py +++ b/homeassistant/components/rainmachine/config_flow.py @@ -16,7 +16,13 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import aiohttp_client, config_validation as cv -from .const import CONF_ZONE_RUN_TIME, DEFAULT_PORT, DEFAULT_ZONE_RUN, DOMAIN +from .const import ( + CONF_DEFAULT_ZONE_RUN_TIME, + CONF_USE_APP_RUN_TIMES, + DEFAULT_PORT, + DEFAULT_ZONE_RUN, + DOMAIN, +) @callback @@ -138,8 +144,8 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): CONF_PASSWORD: user_input[CONF_PASSWORD], CONF_PORT: user_input[CONF_PORT], CONF_SSL: user_input.get(CONF_SSL, True), - CONF_ZONE_RUN_TIME: user_input.get( - CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN + CONF_DEFAULT_ZONE_RUN_TIME: user_input.get( + CONF_DEFAULT_ZONE_RUN_TIME, DEFAULT_ZONE_RUN ), }, ) @@ -173,9 +179,15 @@ class RainMachineOptionsFlowHandler(config_entries.OptionsFlow): data_schema=vol.Schema( { vol.Optional( - CONF_ZONE_RUN_TIME, - default=self.config_entry.options.get(CONF_ZONE_RUN_TIME), - ): cv.positive_int + CONF_DEFAULT_ZONE_RUN_TIME, + default=self.config_entry.options.get( + CONF_DEFAULT_ZONE_RUN_TIME + ), + ): cv.positive_int, + vol.Optional( + CONF_USE_APP_RUN_TIMES, + default=self.config_entry.options.get(CONF_USE_APP_RUN_TIMES), + ): bool, } ), ) diff --git a/homeassistant/components/rainmachine/const.py b/homeassistant/components/rainmachine/const.py index d1b5bd9bd52..00af0bd0b75 100644 --- a/homeassistant/components/rainmachine/const.py +++ b/homeassistant/components/rainmachine/const.py @@ -5,7 +5,9 @@ LOGGER = logging.getLogger(__package__) DOMAIN = "rainmachine" -CONF_ZONE_RUN_TIME = "zone_run_time" +CONF_DURATION = "duration" +CONF_DEFAULT_ZONE_RUN_TIME = "zone_run_time" +CONF_USE_APP_RUN_TIMES = "use_app_run_times" DATA_API_VERSIONS = "api.versions" DATA_MACHINE_FIRMWARE_UPDATE_STATUS = "machine.firmware_update_status" diff --git a/homeassistant/components/rainmachine/strings.json b/homeassistant/components/rainmachine/strings.json index 7634c0a69c5..9991fd31e03 100644 --- a/homeassistant/components/rainmachine/strings.json +++ b/homeassistant/components/rainmachine/strings.json @@ -23,7 +23,8 @@ "init": { "title": "Configure RainMachine", "data": { - "zone_run_time": "Default zone run time (in seconds)" + "zone_run_time": "Default zone run time (in seconds)", + "use_app_run_times": "Use zone run times from RainMachine app" } } } diff --git a/homeassistant/components/rainmachine/switch.py b/homeassistant/components/rainmachine/switch.py index db560c3c64c..35b0e5eab2b 100644 --- a/homeassistant/components/rainmachine/switch.py +++ b/homeassistant/components/rainmachine/switch.py @@ -22,8 +22,11 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import RainMachineData, RainMachineEntity, async_update_programs_and_zones from .const import ( - CONF_ZONE_RUN_TIME, + CONF_DEFAULT_ZONE_RUN_TIME, + CONF_DURATION, + CONF_USE_APP_RUN_TIMES, DATA_PROGRAMS, + DATA_PROVISION_SETTINGS, DATA_RESTRICTIONS_UNIVERSAL, DATA_ZONES, DEFAULT_ZONE_RUN, @@ -40,6 +43,7 @@ ATTR_AREA = "area" ATTR_CS_ON = "cs_on" ATTR_CURRENT_CYCLE = "current_cycle" ATTR_CYCLES = "cycles" +ATTR_ZONE_RUN_TIME = "zone_run_time_from_app" ATTR_DELAY = "delay" ATTR_DELAY_ON = "delay_on" ATTR_FIELD_CAPACITY = "field_capacity" @@ -186,7 +190,7 @@ async def async_setup_entry( "start_zone", { vol.Optional( - CONF_ZONE_RUN_TIME, default=DEFAULT_ZONE_RUN + CONF_DEFAULT_ZONE_RUN_TIME, default=DEFAULT_ZONE_RUN ): cv.positive_int }, "async_start_zone", @@ -459,9 +463,21 @@ class RainMachineZone(RainMachineActivitySwitch): @raise_on_request_error async def async_turn_on_when_active(self, **kwargs: Any) -> None: """Turn the switch on when its associated activity is active.""" + # 1. Use duration parameter if provided from service call + duration = kwargs.get(CONF_DURATION) + if not duration: + if ( + self._entry.options[CONF_USE_APP_RUN_TIMES] + and ATTR_ZONE_RUN_TIME in self._attr_extra_state_attributes + ): + # 2. Use app's zone-specific default, if enabled and available + duration = self._attr_extra_state_attributes[ATTR_ZONE_RUN_TIME] + else: + # 3. Fall back to global zone default duration + duration = self._entry.options[CONF_DEFAULT_ZONE_RUN_TIME] await self._data.controller.zones.start( self.entity_description.uid, - kwargs.get("duration", self._entry.options[CONF_ZONE_RUN_TIME]), + duration, ) self._update_activities() @@ -497,6 +513,13 @@ class RainMachineZone(RainMachineActivitySwitch): data["waterSense"]["precipitationRate"], 2 ) + if self._entry.options[CONF_USE_APP_RUN_TIMES]: + provision_data = self._data.coordinators[DATA_PROVISION_SETTINGS].data + if zone_durations := provision_data.get("system", {}).get("zoneDuration"): + attrs[ATTR_ZONE_RUN_TIME] = zone_durations[ + list(self.coordinator.data).index(self.entity_description.uid) + ] + self._attr_extra_state_attributes.update(attrs) diff --git a/homeassistant/components/rainmachine/translations/en.json b/homeassistant/components/rainmachine/translations/en.json index 3e5d824ee08..15155f52d08 100644 --- a/homeassistant/components/rainmachine/translations/en.json +++ b/homeassistant/components/rainmachine/translations/en.json @@ -35,10 +35,11 @@ "step": { "init": { "data": { - "zone_run_time": "Default zone run time (in seconds)" + "zone_run_time": "Default zone run time (in seconds)", + "use_app_run_times": "Use zone run times from RainMachine app" }, "title": "Configure RainMachine" } } } -} \ No newline at end of file +} diff --git a/tests/components/rainmachine/test_config_flow.py b/tests/components/rainmachine/test_config_flow.py index deb03d65cb5..5f5b769b6dd 100644 --- a/tests/components/rainmachine/test_config_flow.py +++ b/tests/components/rainmachine/test_config_flow.py @@ -6,7 +6,11 @@ from regenmaschine.errors import RainMachineError from homeassistant import config_entries, data_entry_flow, setup from homeassistant.components import zeroconf -from homeassistant.components.rainmachine import CONF_ZONE_RUN_TIME, DOMAIN +from homeassistant.components.rainmachine import ( + CONF_DEFAULT_ZONE_RUN_TIME, + CONF_USE_APP_RUN_TIMES, + DOMAIN, +) from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, CONF_SSL from homeassistant.helpers import entity_registry as er @@ -99,10 +103,14 @@ async def test_options_flow(hass, config, config_entry): assert result["step_id"] == "init" result = await hass.config_entries.options.async_configure( - result["flow_id"], user_input={CONF_ZONE_RUN_TIME: 600} + result["flow_id"], + user_input={CONF_DEFAULT_ZONE_RUN_TIME: 600, CONF_USE_APP_RUN_TIMES: False}, ) assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert config_entry.options == {CONF_ZONE_RUN_TIME: 600} + assert config_entry.options == { + CONF_DEFAULT_ZONE_RUN_TIME: 600, + CONF_USE_APP_RUN_TIMES: False, + } async def test_show_form(hass): @@ -130,7 +138,7 @@ async def test_step_user(hass, config, setup_rainmachine): CONF_PASSWORD: "password", CONF_PORT: 8080, CONF_SSL: True, - CONF_ZONE_RUN_TIME: 600, + CONF_DEFAULT_ZONE_RUN_TIME: 600, } @@ -238,7 +246,7 @@ async def test_step_homekit_zeroconf_new_controller_when_some_exist( CONF_PASSWORD: "password", CONF_PORT: 8080, CONF_SSL: True, - CONF_ZONE_RUN_TIME: 600, + CONF_DEFAULT_ZONE_RUN_TIME: 600, } diff --git a/tests/components/rainmachine/test_diagnostics.py b/tests/components/rainmachine/test_diagnostics.py index a3c03c956a4..084818eeef1 100644 --- a/tests/components/rainmachine/test_diagnostics.py +++ b/tests/components/rainmachine/test_diagnostics.py @@ -20,7 +20,7 @@ async def test_entry_diagnostics(hass, config_entry, hass_client, setup_rainmach "port": 8080, "ssl": True, }, - "options": {}, + "options": {"use_app_run_times": False}, "pref_disable_new_entities": False, "pref_disable_polling": False, "source": "user", @@ -642,7 +642,7 @@ async def test_entry_diagnostics_failed_controller_diagnostics( "port": 8080, "ssl": True, }, - "options": {}, + "options": {"use_app_run_times": False}, "pref_disable_new_entities": False, "pref_disable_polling": False, "source": "user", From 24ab6fc31da2d1eb5d89d48f91c078af3015b809 Mon Sep 17 00:00:00 2001 From: mezz64 <2854333+mezz64@users.noreply.github.com> Date: Thu, 27 Oct 2022 01:37:48 -0400 Subject: [PATCH 0010/1033] Eight Sleep catch missing keys (#81058) Catch missing keys --- homeassistant/components/eight_sleep/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/eight_sleep/sensor.py b/homeassistant/components/eight_sleep/sensor.py index b184cd2496f..b07865d8591 100644 --- a/homeassistant/components/eight_sleep/sensor.py +++ b/homeassistant/components/eight_sleep/sensor.py @@ -146,7 +146,7 @@ def _get_breakdown_percent( """Get a breakdown percent.""" try: return round((attr["breakdown"][key] / denominator) * 100, 2) - except ZeroDivisionError: + except (ZeroDivisionError, KeyError): return 0 From 44e368ad55466d6579bedc7a8c5ddc8602788216 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 27 Oct 2022 00:38:03 -0500 Subject: [PATCH 0011/1033] Bump nexia to 2.0.5 (#81061) fixes #80988 --- homeassistant/components/nexia/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nexia/manifest.json b/homeassistant/components/nexia/manifest.json index 77280b1f503..78576e06b8a 100644 --- a/homeassistant/components/nexia/manifest.json +++ b/homeassistant/components/nexia/manifest.json @@ -1,7 +1,7 @@ { "domain": "nexia", "name": "Nexia/American Standard/Trane", - "requirements": ["nexia==2.0.4"], + "requirements": ["nexia==2.0.5"], "codeowners": ["@bdraco"], "documentation": "https://www.home-assistant.io/integrations/nexia", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index d08127cc558..bbaed415da6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1135,7 +1135,7 @@ nettigo-air-monitor==1.4.2 neurio==0.3.1 # homeassistant.components.nexia -nexia==2.0.4 +nexia==2.0.5 # homeassistant.components.nextcloud nextcloudmonitor==1.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9a139e5b727..0c7b4d8a9a6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -825,7 +825,7 @@ netmap==0.7.0.2 nettigo-air-monitor==1.4.2 # homeassistant.components.nexia -nexia==2.0.4 +nexia==2.0.5 # homeassistant.components.discord nextcord==2.0.0a8 From 7213102c51ddb4bac91dc32b22c518e0b8099659 Mon Sep 17 00:00:00 2001 From: Sean Vig Date: Thu, 27 Oct 2022 03:31:51 -0400 Subject: [PATCH 0012/1033] Update pymonoprice version to 0.4.0 (#81062) Update pymonoprice version --- homeassistant/components/monoprice/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/monoprice/manifest.json b/homeassistant/components/monoprice/manifest.json index 85910b0eb9a..338c976534e 100644 --- a/homeassistant/components/monoprice/manifest.json +++ b/homeassistant/components/monoprice/manifest.json @@ -2,7 +2,7 @@ "domain": "monoprice", "name": "Monoprice 6-Zone Amplifier", "documentation": "https://www.home-assistant.io/integrations/monoprice", - "requirements": ["pymonoprice==0.3"], + "requirements": ["pymonoprice==0.4"], "codeowners": ["@etsinko", "@OnFreund"], "config_flow": true, "iot_class": "local_polling", diff --git a/requirements_all.txt b/requirements_all.txt index bbaed415da6..1f72a73f43d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1724,7 +1724,7 @@ pymochad==0.2.0 pymodbus==2.5.3 # homeassistant.components.monoprice -pymonoprice==0.3 +pymonoprice==0.4 # homeassistant.components.msteams pymsteams==0.1.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0c7b4d8a9a6..63061ad9b00 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1216,7 +1216,7 @@ pymochad==0.2.0 pymodbus==2.5.3 # homeassistant.components.monoprice -pymonoprice==0.3 +pymonoprice==0.4 # homeassistant.components.myq pymyq==3.1.4 From 1967ce67d7e28c3a04d29faaeed3032f2f0441e5 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 27 Oct 2022 02:19:07 -0600 Subject: [PATCH 0013/1033] Remove previously-deprecated Guardian services and binary sensor (#81056) --- homeassistant/components/guardian/__init__.py | 30 ------------------- .../components/guardian/binary_sensor.py | 18 ++--------- 2 files changed, 2 insertions(+), 46 deletions(-) diff --git a/homeassistant/components/guardian/__init__.py b/homeassistant/components/guardian/__init__.py index 63fc66f685d..24999f98e16 100644 --- a/homeassistant/components/guardian/__init__.py +++ b/homeassistant/components/guardian/__init__.py @@ -44,15 +44,11 @@ from .util import GuardianDataUpdateCoordinator DATA_PAIRED_SENSOR_MANAGER = "paired_sensor_manager" -SERVICE_NAME_DISABLE_AP = "disable_ap" -SERVICE_NAME_ENABLE_AP = "enable_ap" SERVICE_NAME_PAIR_SENSOR = "pair_sensor" SERVICE_NAME_UNPAIR_SENSOR = "unpair_sensor" SERVICE_NAME_UPGRADE_FIRMWARE = "upgrade_firmware" SERVICES = ( - SERVICE_NAME_DISABLE_AP, - SERVICE_NAME_ENABLE_AP, SERVICE_NAME_PAIR_SENSOR, SERVICE_NAME_UNPAIR_SENSOR, SERVICE_NAME_UPGRADE_FIRMWARE, @@ -231,30 +227,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return wrapper - @call_with_data - async def async_disable_ap(call: ServiceCall, data: GuardianData) -> None: - """Disable the onboard AP.""" - async_log_deprecated_service_call( - hass, - call, - "switch.turn_off", - f"switch.guardian_valve_controller_{entry.data[CONF_UID]}_onboard_ap", - "2022.12.0", - ) - await data.client.wifi.disable_ap() - - @call_with_data - async def async_enable_ap(call: ServiceCall, data: GuardianData) -> None: - """Enable the onboard AP.""" - async_log_deprecated_service_call( - hass, - call, - "switch.turn_on", - f"switch.guardian_valve_controller_{entry.data[CONF_UID]}_onboard_ap", - "2022.12.0", - ) - await data.client.wifi.enable_ap() - @call_with_data async def async_pair_sensor(call: ServiceCall, data: GuardianData) -> None: """Add a new paired sensor.""" @@ -279,8 +251,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) for service_name, schema, method in ( - (SERVICE_NAME_DISABLE_AP, SERVICE_BASE_SCHEMA, async_disable_ap), - (SERVICE_NAME_ENABLE_AP, SERVICE_BASE_SCHEMA, async_enable_ap), ( SERVICE_NAME_PAIR_SENSOR, SERVICE_PAIR_UNPAIR_SENSOR_SCHEMA, diff --git a/homeassistant/components/guardian/binary_sensor.py b/homeassistant/components/guardian/binary_sensor.py index 6425ecd46a6..a2f2a7a593e 100644 --- a/homeassistant/components/guardian/binary_sensor.py +++ b/homeassistant/components/guardian/binary_sensor.py @@ -23,7 +23,6 @@ from . import ( ) from .const import ( API_SYSTEM_ONBOARD_SENSOR_STATUS, - API_WIFI_STATUS, CONF_UID, DOMAIN, SIGNAL_PAIRED_SENSOR_COORDINATOR_ADDED, @@ -36,7 +35,6 @@ from .util import ( ATTR_CONNECTED_CLIENTS = "connected_clients" -SENSOR_KIND_AP_INFO = "ap_enabled" SENSOR_KIND_LEAK_DETECTED = "leak_detected" SENSOR_KIND_MOVED = "moved" @@ -69,13 +67,6 @@ VALVE_CONTROLLER_DESCRIPTIONS = ( device_class=BinarySensorDeviceClass.MOISTURE, api_category=API_SYSTEM_ONBOARD_SENSOR_STATUS, ), - ValveControllerBinarySensorDescription( - key=SENSOR_KIND_AP_INFO, - name="Onboard AP enabled", - device_class=BinarySensorDeviceClass.CONNECTIVITY, - entity_category=EntityCategory.DIAGNOSTIC, - api_category=API_WIFI_STATUS, - ), ) @@ -95,7 +86,7 @@ async def async_setup_entry( f"{uid}_ap_enabled", f"switch.guardian_valve_controller_{uid}_onboard_ap", "2022.12.0", - remove_old_entity=False, + remove_old_entity=True, ), ), ) @@ -183,10 +174,5 @@ class ValveControllerBinarySensor(ValveControllerEntity, BinarySensorEntity): @callback def _async_update_from_latest_data(self) -> None: """Update the entity.""" - if self.entity_description.key == SENSOR_KIND_AP_INFO: - self._attr_is_on = self.coordinator.data["station_connected"] - self._attr_extra_state_attributes[ - ATTR_CONNECTED_CLIENTS - ] = self.coordinator.data.get("ap_clients") - elif self.entity_description.key == SENSOR_KIND_LEAK_DETECTED: + if self.entity_description.key == SENSOR_KIND_LEAK_DETECTED: self._attr_is_on = self.coordinator.data["wet"] From 4e8e53f35791b3b68686a5e84b03465b71bca905 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 27 Oct 2022 02:22:44 -0600 Subject: [PATCH 0014/1033] Remove previously-deprecated OpenUV services (#81055) --- homeassistant/components/openuv/__init__.py | 173 +----------------- homeassistant/components/openuv/services.yaml | 36 ---- tests/components/openuv/conftest.py | 2 - 3 files changed, 4 insertions(+), 207 deletions(-) delete mode 100644 homeassistant/components/openuv/services.yaml diff --git a/homeassistant/components/openuv/__init__.py b/homeassistant/components/openuv/__init__.py index fb649bce759..c4a9d347a40 100644 --- a/homeassistant/components/openuv/__init__.py +++ b/homeassistant/components/openuv/__init__.py @@ -5,9 +5,8 @@ import asyncio from typing import Any from pyopenuv import Client -import voluptuous as vol -from homeassistant.config_entries import ConfigEntry, ConfigEntryState +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_API_KEY, CONF_BINARY_SENSORS, @@ -17,17 +16,10 @@ from homeassistant.const import ( CONF_SENSORS, Platform, ) -from homeassistant.core import HomeAssistant, ServiceCall, callback -from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import ( - aiohttp_client, - config_validation as cv, - entity_registry, -) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import aiohttp_client from homeassistant.helpers.entity import EntityDescription -from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue -from homeassistant.helpers.service import verify_domain_control -from homeassistant.helpers.update_coordinator import CoordinatorEntity, UpdateFailed +from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import ( CONF_FROM_WINDOW, @@ -41,82 +33,11 @@ from .const import ( ) from .coordinator import OpenUvCoordinator -CONF_ENTRY_ID = "entry_id" - PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] -SERVICE_NAME_UPDATE_DATA = "update_data" -SERVICE_NAME_UPDATE_PROTECTION_DATA = "update_protection_data" -SERVICE_NAME_UPDATE_UV_INDEX_DATA = "update_uv_index_data" - -SERVICES = ( - SERVICE_NAME_UPDATE_DATA, - SERVICE_NAME_UPDATE_PROTECTION_DATA, - SERVICE_NAME_UPDATE_UV_INDEX_DATA, -) - - -@callback -def async_get_entity_id_from_unique_id_suffix( - hass: HomeAssistant, entry: ConfigEntry, unique_id_suffix: str -) -> str: - """Get the entity ID for a config entry based on unique ID suffix.""" - ent_reg = entity_registry.async_get(hass) - [registry_entry] = [ - registry_entry - for registry_entry in ent_reg.entities.values() - if registry_entry.config_entry_id == entry.entry_id - and registry_entry.unique_id.endswith(unique_id_suffix) - ] - return registry_entry.entity_id - - -@callback -def async_log_deprecated_service_call( - hass: HomeAssistant, - call: ServiceCall, - alternate_service: str, - alternate_targets: list[str], - breaks_in_ha_version: str, -) -> None: - """Log a warning about a deprecated service call.""" - deprecated_service = f"{call.domain}.{call.service}" - - if len(alternate_targets) > 1: - translation_key = "deprecated_service_multiple_alternate_targets" - else: - translation_key = "deprecated_service_single_alternate_target" - - async_create_issue( - hass, - DOMAIN, - f"deprecated_service_{deprecated_service}", - breaks_in_ha_version=breaks_in_ha_version, - is_fixable=False, - is_persistent=True, - severity=IssueSeverity.WARNING, - translation_key=translation_key, - translation_placeholders={ - "alternate_service": alternate_service, - "alternate_targets": ", ".join(alternate_targets), - "deprecated_service": deprecated_service, - }, - ) - - LOGGER.warning( - ( - 'The "%s" service is deprecated and will be removed in %s; review the ' - "Repairs item in the UI for more information" - ), - deprecated_service, - breaks_in_ha_version, - ) - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up OpenUV as config entry.""" - _verify_domain_control = verify_domain_control(hass, DOMAIN) - websession = aiohttp_client.async_get_clientsession(hass) client = Client( entry.data[CONF_API_KEY], @@ -162,81 +83,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - # We determine entity IDs needed to help the user migrate from deprecated services: - current_uv_index_entity_id = async_get_entity_id_from_unique_id_suffix( - hass, entry, "current_uv_index" - ) - protection_window_entity_id = async_get_entity_id_from_unique_id_suffix( - hass, entry, "protection_window" - ) - - @_verify_domain_control - async def update_data(call: ServiceCall) -> None: - """Refresh all OpenUV data.""" - LOGGER.debug("Refreshing all OpenUV data") - async_log_deprecated_service_call( - hass, - call, - "homeassistant.update_entity", - [protection_window_entity_id, current_uv_index_entity_id], - "2022.12.0", - ) - - tasks = [coordinator.async_refresh() for coordinator in coordinators.values()] - try: - await asyncio.gather(*tasks) - except UpdateFailed as err: - raise HomeAssistantError(err) from err - - @_verify_domain_control - async def update_uv_index_data(call: ServiceCall) -> None: - """Refresh OpenUV UV index data.""" - LOGGER.debug("Refreshing OpenUV UV index data") - async_log_deprecated_service_call( - hass, - call, - "homeassistant.update_entity", - [current_uv_index_entity_id], - "2022.12.0", - ) - - try: - await coordinators[DATA_UV].async_request_refresh() - except UpdateFailed as err: - raise HomeAssistantError(err) from err - - @_verify_domain_control - async def update_protection_data(call: ServiceCall) -> None: - """Refresh OpenUV protection window data.""" - LOGGER.debug("Refreshing OpenUV protection window data") - async_log_deprecated_service_call( - hass, - call, - "homeassistant.update_entity", - [protection_window_entity_id], - "2022.12.0", - ) - - try: - await coordinators[DATA_PROTECTION_WINDOW].async_request_refresh() - except UpdateFailed as err: - raise HomeAssistantError(err) from err - - service_schema = vol.Schema( - { - vol.Optional(CONF_ENTRY_ID, default=entry.entry_id): cv.string, - } - ) - - for service, method in ( - (SERVICE_NAME_UPDATE_DATA, update_data), - (SERVICE_NAME_UPDATE_UV_INDEX_DATA, update_uv_index_data), - (SERVICE_NAME_UPDATE_PROTECTION_DATA, update_protection_data), - ): - if hass.services.has_service(DOMAIN, service): - continue - hass.services.async_register(DOMAIN, service, method, schema=service_schema) - return True @@ -246,17 +92,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if unload_ok: hass.data[DOMAIN].pop(entry.entry_id) - loaded_entries = [ - entry - for entry in hass.config_entries.async_entries(DOMAIN) - if entry.state == ConfigEntryState.LOADED - ] - if len(loaded_entries) == 1: - # If this is the last loaded instance of OpenUV, deregister any services - # defined during integration setup: - for service_name in SERVICES: - hass.services.async_remove(DOMAIN, service_name) - return unload_ok diff --git a/homeassistant/components/openuv/services.yaml b/homeassistant/components/openuv/services.yaml deleted file mode 100644 index 3e2e6ab0087..00000000000 --- a/homeassistant/components/openuv/services.yaml +++ /dev/null @@ -1,36 +0,0 @@ -# Describes the format for available OpenUV services -update_data: - name: Update data - description: Request new data from OpenUV. Consumes two API calls. - fields: - entry_id: - name: Config Entry - description: The configured instance of the OpenUV integration to use - required: true - selector: - config_entry: - integration: openuv - -update_uv_index_data: - name: Update UV index data - description: Request new UV index data from OpenUV. - fields: - entry_id: - name: Config Entry - description: The configured instance of the OpenUV integration to use - required: true - selector: - config_entry: - integration: openuv - -update_protection_data: - name: Update protection data - description: Request new protection window data from OpenUV. - fields: - entry_id: - name: Config Entry - description: The configured instance of the OpenUV integration to use - required: true - selector: - config_entry: - integration: openuv diff --git a/tests/components/openuv/conftest.py b/tests/components/openuv/conftest.py index 3caa41749ee..b2c0a6c7ec5 100644 --- a/tests/components/openuv/conftest.py +++ b/tests/components/openuv/conftest.py @@ -56,8 +56,6 @@ def data_uv_index_fixture(): async def setup_openuv_fixture(hass, config, data_protection_window, data_uv_index): """Define a fixture to set up OpenUV.""" with patch( - "homeassistant.components.openuv.async_get_entity_id_from_unique_id_suffix", - ), patch( "homeassistant.components.openuv.Client.uv_index", return_value=data_uv_index ), patch( "homeassistant.components.openuv.Client.uv_protection_window", From d72b8a025fc13ec4cb1dbb647f83681d07c551dc Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 27 Oct 2022 02:25:07 -0600 Subject: [PATCH 0015/1033] Remove previously-deprecated RainMachine sensors (#81053) --- .../components/rainmachine/binary_sensor.py | 44 ++----------------- .../components/rainmachine/sensor.py | 39 ++-------------- 2 files changed, 7 insertions(+), 76 deletions(-) diff --git a/homeassistant/components/rainmachine/binary_sensor.py b/homeassistant/components/rainmachine/binary_sensor.py index 212d87f2982..8b75a354e29 100644 --- a/homeassistant/components/rainmachine/binary_sensor.py +++ b/homeassistant/components/rainmachine/binary_sensor.py @@ -12,12 +12,7 @@ from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import RainMachineData, RainMachineEntity -from .const import ( - DATA_PROVISION_SETTINGS, - DATA_RESTRICTIONS_CURRENT, - DATA_RESTRICTIONS_UNIVERSAL, - DOMAIN, -) +from .const import DATA_PROVISION_SETTINGS, DATA_RESTRICTIONS_CURRENT, DOMAIN from .model import ( RainMachineEntityDescription, RainMachineEntityDescriptionMixinDataKey, @@ -30,8 +25,6 @@ from .util import ( TYPE_FLOW_SENSOR = "flow_sensor" TYPE_FREEZE = "freeze" -TYPE_FREEZE_PROTECTION = "freeze_protection" -TYPE_HOT_DAYS = "extra_water_on_hot_days" TYPE_HOURLY = "hourly" TYPE_MONTH = "month" TYPE_RAINDELAY = "raindelay" @@ -64,22 +57,6 @@ BINARY_SENSOR_DESCRIPTIONS = ( api_category=DATA_RESTRICTIONS_CURRENT, data_key="freeze", ), - RainMachineBinarySensorDescription( - key=TYPE_FREEZE_PROTECTION, - name="Freeze protection", - icon="mdi:weather-snowy", - entity_category=EntityCategory.DIAGNOSTIC, - api_category=DATA_RESTRICTIONS_UNIVERSAL, - data_key="freezeProtectEnabled", - ), - RainMachineBinarySensorDescription( - key=TYPE_HOT_DAYS, - name="Extra water on hot days", - icon="mdi:thermometer-lines", - entity_category=EntityCategory.DIAGNOSTIC, - api_category=DATA_RESTRICTIONS_UNIVERSAL, - data_key="hotDaysExtraWatering", - ), RainMachineBinarySensorDescription( key=TYPE_HOURLY, name="Hourly restrictions", @@ -139,14 +116,14 @@ async def async_setup_entry( f"{data.controller.mac}_freeze_protection", f"switch.{data.controller.name.lower()}_freeze_protect_enabled", breaks_in_ha_version="2022.12.0", - remove_old_entity=False, + remove_old_entity=True, ), EntityDomainReplacementStrategy( BINARY_SENSOR_DOMAIN, f"{data.controller.mac}_extra_water_on_hot_days", f"switch.{data.controller.name.lower()}_hot_days_extra_watering", breaks_in_ha_version="2022.12.0", - remove_old_entity=False, + remove_old_entity=True, ), ), ) @@ -154,7 +131,6 @@ async def async_setup_entry( api_category_sensor_map = { DATA_PROVISION_SETTINGS: ProvisionSettingsBinarySensor, DATA_RESTRICTIONS_CURRENT: CurrentRestrictionsBinarySensor, - DATA_RESTRICTIONS_UNIVERSAL: UniversalRestrictionsBinarySensor, } async_add_entities( @@ -204,17 +180,3 @@ class ProvisionSettingsBinarySensor(RainMachineEntity, BinarySensorEntity): self._attr_is_on = self.coordinator.data.get("system", {}).get( "useFlowSensor" ) - - -class UniversalRestrictionsBinarySensor(RainMachineEntity, BinarySensorEntity): - """Define a binary sensor that handles universal restrictions data.""" - - entity_description: RainMachineBinarySensorDescription - - @callback - def update_from_latest_data(self) -> None: - """Update the state.""" - if self.entity_description.key == TYPE_FREEZE_PROTECTION: - self._attr_is_on = self.coordinator.data.get("freezeProtectEnabled") - elif self.entity_description.key == TYPE_HOT_DAYS: - self._attr_is_on = self.coordinator.data.get("hotDaysExtraWatering") diff --git a/homeassistant/components/rainmachine/sensor.py b/homeassistant/components/rainmachine/sensor.py index 191ace64625..0830fe6bc6a 100644 --- a/homeassistant/components/rainmachine/sensor.py +++ b/homeassistant/components/rainmachine/sensor.py @@ -14,20 +14,14 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS, VOLUME_CUBIC_METERS, VOLUME_LITERS +from homeassistant.const import VOLUME_CUBIC_METERS, VOLUME_LITERS from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util.dt import utc_from_timestamp, utcnow from . import RainMachineData, RainMachineEntity -from .const import ( - DATA_PROGRAMS, - DATA_PROVISION_SETTINGS, - DATA_RESTRICTIONS_UNIVERSAL, - DATA_ZONES, - DOMAIN, -) +from .const import DATA_PROGRAMS, DATA_PROVISION_SETTINGS, DATA_ZONES, DOMAIN from .model import ( RainMachineEntityDescription, RainMachineEntityDescriptionMixinDataKey, @@ -49,7 +43,6 @@ TYPE_FLOW_SENSOR_LEAK_CLICKS = "flow_sensor_leak_clicks" TYPE_FLOW_SENSOR_LEAK_VOLUME = "flow_sensor_leak_volume" TYPE_FLOW_SENSOR_START_INDEX = "flow_sensor_start_index" TYPE_FLOW_SENSOR_WATERING_CLICKS = "flow_sensor_watering_clicks" -TYPE_FREEZE_TEMP = "freeze_protect_temp" TYPE_LAST_LEAK_DETECTED = "last_leak_detected" TYPE_PROGRAM_RUN_COMPLETION_TIME = "program_run_completion_time" TYPE_RAIN_SENSOR_RAIN_START = "rain_sensor_rain_start" @@ -140,17 +133,6 @@ SENSOR_DESCRIPTIONS = ( api_category=DATA_PROVISION_SETTINGS, data_key="flowSensorWateringClicks", ), - RainMachineSensorDataDescription( - key=TYPE_FREEZE_TEMP, - name="Freeze protect temperature", - icon="mdi:thermometer", - entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - api_category=DATA_RESTRICTIONS_UNIVERSAL, - data_key="freezeProtectTemp", - ), RainMachineSensorDataDescription( key=TYPE_LAST_LEAK_DETECTED, name="Last leak detected", @@ -191,17 +173,16 @@ async def async_setup_entry( f"{data.controller.mac}_freeze_protect_temp", f"select.{data.controller.name.lower()}_freeze_protect_temperature", breaks_in_ha_version="2022.12.0", - remove_old_entity=False, + remove_old_entity=True, ), ), ) api_category_sensor_map = { DATA_PROVISION_SETTINGS: ProvisionSettingsSensor, - DATA_RESTRICTIONS_UNIVERSAL: UniversalRestrictionsSensor, } - sensors = [ + sensors: list[ProvisionSettingsSensor | TimeRemainingSensor] = [ api_category_sensor_map[description.api_category](entry, data, description) for description in SENSOR_DESCRIPTIONS if ( @@ -373,18 +354,6 @@ class ProvisionSettingsSensor(RainMachineEntity, SensorEntity): self._attr_native_value = new_value -class UniversalRestrictionsSensor(RainMachineEntity, SensorEntity): - """Define a sensor that handles universal restrictions data.""" - - entity_description: RainMachineSensorDataDescription - - @callback - def update_from_latest_data(self) -> None: - """Update the state.""" - if self.entity_description.key == TYPE_FREEZE_TEMP: - self._attr_native_value = self.coordinator.data.get("freezeProtectTemp") - - class ZoneTimeRemainingSensor(TimeRemainingSensor): """Define a sensor that shows the amount of time remaining for a zone.""" From fe4da8d584ccf3f05fe1c6cb38a7411e27edaa0c Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 27 Oct 2022 02:39:57 -0600 Subject: [PATCH 0016/1033] Remove previously-deprecated SimpliSafe service (#81054) --- .../components/simplisafe/__init__.py | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index 53aa9e84054..b04dcfbd984 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -139,24 +139,16 @@ VOLUME_MAP = { "off": Volume.OFF, } -SERVICE_NAME_CLEAR_NOTIFICATIONS = "clear_notifications" SERVICE_NAME_REMOVE_PIN = "remove_pin" SERVICE_NAME_SET_PIN = "set_pin" SERVICE_NAME_SET_SYSTEM_PROPERTIES = "set_system_properties" SERVICES = ( - SERVICE_NAME_CLEAR_NOTIFICATIONS, SERVICE_NAME_REMOVE_PIN, SERVICE_NAME_SET_PIN, SERVICE_NAME_SET_SYSTEM_PROPERTIES, ) -SERVICE_CLEAR_NOTIFICATIONS_SCHEMA = vol.Schema( - { - vol.Required(ATTR_DEVICE_ID): cv.string, - }, -) - SERVICE_REMOVE_PIN_SCHEMA = vol.Schema( { vol.Required(ATTR_DEVICE_ID): cv.string, @@ -384,19 +376,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return wrapper - @_verify_domain_control - @extract_system - async def async_clear_notifications(call: ServiceCall, system: SystemType) -> None: - """Clear all active notifications.""" - _async_log_deprecated_service_call( - hass, - call, - "button.press", - "button.alarm_control_panel_clear_notifications", - "2022.12.0", - ) - await system.async_clear_notifications() - @_verify_domain_control @extract_system async def async_remove_pin(call: ServiceCall, system: SystemType) -> None: @@ -423,11 +402,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) for service, method, schema in ( - ( - SERVICE_NAME_CLEAR_NOTIFICATIONS, - async_clear_notifications, - SERVICE_CLEAR_NOTIFICATIONS_SCHEMA, - ), (SERVICE_NAME_REMOVE_PIN, async_remove_pin, SERVICE_REMOVE_PIN_SCHEMA), (SERVICE_NAME_SET_PIN, async_set_pin, SERVICE_SET_PIN_SCHEMA), ( From 7df8ad5c66ee7224d17d539d34be969d3f745b53 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 27 Oct 2022 11:43:05 +0200 Subject: [PATCH 0017/1033] Update sentry-sdk to 1.10.1 (#81072) --- homeassistant/components/sentry/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sentry/manifest.json b/homeassistant/components/sentry/manifest.json index 1c4b00e25cc..567a0350c4e 100644 --- a/homeassistant/components/sentry/manifest.json +++ b/homeassistant/components/sentry/manifest.json @@ -3,7 +3,7 @@ "name": "Sentry", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/sentry", - "requirements": ["sentry-sdk==1.10.0"], + "requirements": ["sentry-sdk==1.10.1"], "codeowners": ["@dcramer", "@frenck"], "integration_type": "service", "iot_class": "cloud_polling" diff --git a/requirements_all.txt b/requirements_all.txt index 1f72a73f43d..48f1fa539c0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2241,7 +2241,7 @@ sensorpro-ble==0.5.0 sensorpush-ble==1.5.2 # homeassistant.components.sentry -sentry-sdk==1.10.0 +sentry-sdk==1.10.1 # homeassistant.components.sharkiq sharkiq==0.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 63061ad9b00..c839089fc86 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1544,7 +1544,7 @@ sensorpro-ble==0.5.0 sensorpush-ble==1.5.2 # homeassistant.components.sentry -sentry-sdk==1.10.0 +sentry-sdk==1.10.1 # homeassistant.components.sharkiq sharkiq==0.0.1 From 147fe6f856a15ea4412866d3ce2d3e5fb7599cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Huryn?= Date: Thu, 27 Oct 2022 14:12:51 +0200 Subject: [PATCH 0018/1033] Update blebox_uniapi to 2.1.3 (#81071) fix: #80124 blebox_uniapi dependency version bump --- homeassistant/components/blebox/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/blebox/manifest.json b/homeassistant/components/blebox/manifest.json index 328f15abdac..78c7186eb31 100644 --- a/homeassistant/components/blebox/manifest.json +++ b/homeassistant/components/blebox/manifest.json @@ -3,7 +3,7 @@ "name": "BleBox devices", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/blebox", - "requirements": ["blebox_uniapi==2.1.0"], + "requirements": ["blebox_uniapi==2.1.3"], "codeowners": ["@bbx-a", "@riokuu"], "iot_class": "local_polling", "loggers": ["blebox_uniapi"] diff --git a/requirements_all.txt b/requirements_all.txt index 48f1fa539c0..9be095b51e0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -419,7 +419,7 @@ bleak-retry-connector==2.4.2 bleak==0.19.0 # homeassistant.components.blebox -blebox_uniapi==2.1.0 +blebox_uniapi==2.1.3 # homeassistant.components.blink blinkpy==0.19.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c839089fc86..0342b345f50 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -343,7 +343,7 @@ bleak-retry-connector==2.4.2 bleak==0.19.0 # homeassistant.components.blebox -blebox_uniapi==2.1.0 +blebox_uniapi==2.1.3 # homeassistant.components.blink blinkpy==0.19.2 From dbd875cd25f9d7dd065bc240474483479cc1910c Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Thu, 27 Oct 2022 17:37:52 +0200 Subject: [PATCH 0019/1033] Bring back Netatmo force update code (#81098) --- homeassistant/components/netatmo/data_handler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/netatmo/data_handler.py b/homeassistant/components/netatmo/data_handler.py index a376e6ee187..b4be2a147f7 100644 --- a/homeassistant/components/netatmo/data_handler.py +++ b/homeassistant/components/netatmo/data_handler.py @@ -176,8 +176,8 @@ class NetatmoDataHandler: @callback def async_force_update(self, signal_name: str) -> None: """Prioritize data retrieval for given data class entry.""" - # self.publisher[signal_name].next_scan = time() - # self._queue.rotate(-(self._queue.index(self.publisher[signal_name]))) + self.publisher[signal_name].next_scan = time() + self._queue.rotate(-(self._queue.index(self.publisher[signal_name]))) async def handle_event(self, event: dict) -> None: """Handle webhook events.""" From 3d42a468714d8689362ae00ddfffd32a403d4c91 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 27 Oct 2022 17:48:35 +0200 Subject: [PATCH 0020/1033] Align Scrape resource model with Rest integration (#81074) --- homeassistant/components/rest/__init__.py | 2 +- homeassistant/components/scrape/sensor.py | 32 +++++++---------------- tests/components/scrape/test_sensor.py | 18 ++++++------- 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/rest/__init__.py b/homeassistant/components/rest/__init__.py index 5549abc2143..c9efbc30d4a 100644 --- a/homeassistant/components/rest/__init__.py +++ b/homeassistant/components/rest/__init__.py @@ -41,7 +41,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import COORDINATOR, DOMAIN, PLATFORM_IDX, REST, REST_DATA, REST_IDX from .data import RestData -from .schema import CONFIG_SCHEMA # noqa: F401 +from .schema import CONFIG_SCHEMA, RESOURCE_SCHEMA # noqa: F401 _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index b13b5d8463b..48b5ba53dd4 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -5,10 +5,9 @@ from datetime import timedelta import logging from typing import Any -import httpx import voluptuous as vol -from homeassistant.components.rest.data import RestData +from homeassistant.components.rest import RESOURCE_SCHEMA, create_rest_data_from_config from homeassistant.components.sensor import ( CONF_STATE_CLASS, DEVICE_CLASSES_SCHEMA, @@ -82,12 +81,15 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Web scrape sensor.""" + resource_config = vol.Schema(RESOURCE_SCHEMA, extra=vol.REMOVE_EXTRA)(config) + rest = create_rest_data_from_config(hass, resource_config) + + coordinator = ScrapeCoordinator(hass, rest, SCAN_INTERVAL) + await coordinator.async_refresh() + if coordinator.data is None: + raise PlatformNotReady + name: str = config[CONF_NAME] - resource: str = config[CONF_RESOURCE] - method: str = "GET" - payload: str | None = None - headers: dict[str, str] | None = config.get(CONF_HEADERS) - verify_ssl: bool = config[CONF_VERIFY_SSL] select: str | None = config.get(CONF_SELECT) attr: str | None = config.get(CONF_ATTR) index: int = config[CONF_INDEX] @@ -95,27 +97,11 @@ async def async_setup_platform( device_class: str | None = config.get(CONF_DEVICE_CLASS) state_class: str | None = config.get(CONF_STATE_CLASS) unique_id: str | None = config.get(CONF_UNIQUE_ID) - username: str | None = config.get(CONF_USERNAME) - password: str | None = config.get(CONF_PASSWORD) value_template: Template | None = config.get(CONF_VALUE_TEMPLATE) if value_template is not None: value_template.hass = hass - auth: httpx.DigestAuth | tuple[str, str] | None = None - if username and password: - if config.get(CONF_AUTHENTICATION) == HTTP_DIGEST_AUTHENTICATION: - auth = httpx.DigestAuth(username, password) - else: - auth = (username, password) - - rest = RestData(hass, method, resource, auth, headers, None, payload, verify_ssl) - - coordinator = ScrapeCoordinator(hass, rest, SCAN_INTERVAL) - await coordinator.async_refresh() - if coordinator.data is None: - raise PlatformNotReady - async_add_entities( [ ScrapeSensor( diff --git a/tests/components/scrape/test_sensor.py b/tests/components/scrape/test_sensor.py index aacd89b2eb9..00fdc7eb900 100644 --- a/tests/components/scrape/test_sensor.py +++ b/tests/components/scrape/test_sensor.py @@ -34,7 +34,7 @@ async def test_scrape_sensor(hass: HomeAssistant) -> None: mocker = MockRestData("test_scrape_sensor") with patch( - "homeassistant.components.scrape.sensor.RestData", + "homeassistant.components.rest.RestData", return_value=mocker, ): assert await async_setup_component(hass, "sensor", config) @@ -56,7 +56,7 @@ async def test_scrape_sensor_value_template(hass: HomeAssistant) -> None: mocker = MockRestData("test_scrape_sensor") with patch( - "homeassistant.components.scrape.sensor.RestData", + "homeassistant.components.rest.RestData", return_value=mocker, ): assert await async_setup_component(hass, "sensor", config) @@ -81,7 +81,7 @@ async def test_scrape_uom_and_classes(hass: HomeAssistant) -> None: mocker = MockRestData("test_scrape_uom_and_classes") with patch( - "homeassistant.components.scrape.sensor.RestData", + "homeassistant.components.rest.RestData", return_value=mocker, ): assert await async_setup_component(hass, "sensor", config) @@ -107,7 +107,7 @@ async def test_scrape_unique_id(hass: HomeAssistant) -> None: mocker = MockRestData("test_scrape_uom_and_classes") with patch( - "homeassistant.components.scrape.sensor.RestData", + "homeassistant.components.rest.RestData", return_value=mocker, ): assert await async_setup_component(hass, "sensor", config) @@ -144,7 +144,7 @@ async def test_scrape_sensor_authentication(hass: HomeAssistant) -> None: mocker = MockRestData("test_scrape_sensor_authentication") with patch( - "homeassistant.components.scrape.sensor.RestData", + "homeassistant.components.rest.RestData", return_value=mocker, ): assert await async_setup_component(hass, "sensor", config) @@ -162,7 +162,7 @@ async def test_scrape_sensor_no_data(hass: HomeAssistant) -> None: mocker = MockRestData("test_scrape_sensor_no_data") with patch( - "homeassistant.components.scrape.sensor.RestData", + "homeassistant.components.rest.RestData", return_value=mocker, ): assert await async_setup_component(hass, "sensor", config) @@ -178,7 +178,7 @@ async def test_scrape_sensor_no_data_refresh(hass: HomeAssistant) -> None: mocker = MockRestData("test_scrape_sensor") with patch( - "homeassistant.components.scrape.sensor.RestData", + "homeassistant.components.rest.RestData", return_value=mocker, ): assert await async_setup_component(hass, "sensor", config) @@ -208,7 +208,7 @@ async def test_scrape_sensor_attribute_and_tag(hass: HomeAssistant) -> None: mocker = MockRestData("test_scrape_sensor") with patch( - "homeassistant.components.scrape.sensor.RestData", + "homeassistant.components.rest.RestData", return_value=mocker, ): assert await async_setup_component(hass, "sensor", config) @@ -231,7 +231,7 @@ async def test_scrape_sensor_errors(hass: HomeAssistant) -> None: mocker = MockRestData("test_scrape_sensor") with patch( - "homeassistant.components.scrape.sensor.RestData", + "homeassistant.components.rest.RestData", return_value=mocker, ): assert await async_setup_component(hass, "sensor", config) From 663c2ef99395550cee2123def4e467eb5519ad6c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 27 Oct 2022 18:11:40 +0200 Subject: [PATCH 0021/1033] Cleanup scrape constants (#81076) * Cleanup scrape constants * Adjust comments * Revert removal of DEFAULT_VERIFY_SSL constant --- .../components/scrape/coordinator.py | 2 +- homeassistant/components/scrape/sensor.py | 32 +++++++++---------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/scrape/coordinator.py b/homeassistant/components/scrape/coordinator.py index 3e81ba798ae..d947e6ac519 100644 --- a/homeassistant/components/scrape/coordinator.py +++ b/homeassistant/components/scrape/coordinator.py @@ -6,7 +6,7 @@ import logging from bs4 import BeautifulSoup -from homeassistant.components.rest.data import RestData +from homeassistant.components.rest import RestData from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index 48b5ba53dd4..6da1cfea9f5 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -16,6 +16,7 @@ from homeassistant.components.sensor import ( SensorEntity, ) from homeassistant.const import ( + CONF_ATTRIBUTE, CONF_AUTHENTICATION, CONF_DEVICE_CLASS, CONF_HEADERS, @@ -38,38 +39,35 @@ from homeassistant.helpers.template import Template from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.update_coordinator import CoordinatorEntity +from .const import CONF_INDEX, CONF_SELECT, DEFAULT_NAME, DEFAULT_VERIFY_SSL from .coordinator import ScrapeCoordinator _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(minutes=10) -CONF_ATTR = "attribute" -CONF_SELECT = "select" -CONF_INDEX = "index" - -DEFAULT_NAME = "Web scrape" -DEFAULT_VERIFY_SSL = True - PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend( { - vol.Required(CONF_RESOURCE): cv.string, - vol.Required(CONF_SELECT): cv.string, - vol.Optional(CONF_ATTR): cv.string, - vol.Optional(CONF_INDEX, default=0): cv.positive_int, + # Linked to the loading of the page (can be linked to RestData) vol.Optional(CONF_AUTHENTICATION): vol.In( [HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION] ), vol.Optional(CONF_HEADERS): vol.Schema({cv.string: cv.string}), - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PASSWORD): cv.string, - vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, + vol.Required(CONF_RESOURCE): cv.string, + vol.Optional(CONF_USERNAME): cv.string, + vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, + # Linked to the parsing of the page (specific to scrape) + vol.Optional(CONF_ATTRIBUTE): cv.string, + vol.Optional(CONF_INDEX, default=0): cv.positive_int, + vol.Required(CONF_SELECT): cv.string, + vol.Optional(CONF_VALUE_TEMPLATE): cv.template, + # Linked to the sensor definition (can be linked to TemplateSensor) vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_STATE_CLASS): STATE_CLASSES_SCHEMA, vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_USERNAME): cv.string, - vol.Optional(CONF_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, + vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, } ) @@ -91,7 +89,7 @@ async def async_setup_platform( name: str = config[CONF_NAME] select: str | None = config.get(CONF_SELECT) - attr: str | None = config.get(CONF_ATTR) + attr: str | None = config.get(CONF_ATTRIBUTE) index: int = config[CONF_INDEX] unit: str | None = config.get(CONF_UNIT_OF_MEASUREMENT) device_class: str | None = config.get(CONF_DEVICE_CLASS) From 7feb463a4c845e94d5c1df29252a825ae859cd39 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 27 Oct 2022 18:18:09 +0200 Subject: [PATCH 0022/1033] Add integration_type to samsungtv (#81101) --- homeassistant/components/samsungtv/manifest.json | 1 + homeassistant/generated/integrations.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/samsungtv/manifest.json b/homeassistant/components/samsungtv/manifest.json index a7aa842f485..5f70e912d35 100644 --- a/homeassistant/components/samsungtv/manifest.json +++ b/homeassistant/components/samsungtv/manifest.json @@ -1,6 +1,7 @@ { "domain": "samsungtv", "name": "Samsung Smart TV", + "integration_type": "device", "documentation": "https://www.home-assistant.io/integrations/samsungtv", "requirements": [ "getmac==0.8.2", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 08317d06a5c..a7f78e45493 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -4468,7 +4468,7 @@ "name": "Samsung Family Hub" }, "samsungtv": { - "integration_type": "hub", + "integration_type": "device", "config_flow": true, "iot_class": "local_push", "name": "Samsung Smart TV" From 60b3c36775b54d5a16cb635751ad9a6c7a87c1b5 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 27 Oct 2022 18:19:45 +0200 Subject: [PATCH 0023/1033] Add integration_type to onewire (#81097) --- homeassistant/components/onewire/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/onewire/manifest.json b/homeassistant/components/onewire/manifest.json index 87adc23ae18..a40b19f2055 100644 --- a/homeassistant/components/onewire/manifest.json +++ b/homeassistant/components/onewire/manifest.json @@ -1,6 +1,7 @@ { "domain": "onewire", "name": "1-Wire", + "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/onewire", "config_flow": true, "requirements": ["pyownet==0.10.0.post1"], From b490f47334a92efdfabe205bdc72bb03ad7d2592 Mon Sep 17 00:00:00 2001 From: Willem-Jan van Rootselaar Date: Thu, 27 Oct 2022 18:25:14 +0200 Subject: [PATCH 0024/1033] Bump python-bsblan to 0.5.6 (#80956) * Bump python-bsblan to 0.5.6 * update requirements --- homeassistant/components/bsblan/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/bsblan/manifest.json b/homeassistant/components/bsblan/manifest.json index 7c5422d3eff..e2b0e5bebfa 100644 --- a/homeassistant/components/bsblan/manifest.json +++ b/homeassistant/components/bsblan/manifest.json @@ -3,7 +3,7 @@ "name": "BSB-Lan", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/bsblan", - "requirements": ["python-bsblan==0.5.5"], + "requirements": ["python-bsblan==0.5.6"], "codeowners": ["@liudger"], "iot_class": "local_polling", "loggers": ["bsblan"] diff --git a/requirements_all.txt b/requirements_all.txt index 9be095b51e0..8d1516ed5cb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1950,7 +1950,7 @@ pythinkingcleaner==0.0.3 python-blockchain-api==0.0.2 # homeassistant.components.bsblan -python-bsblan==0.5.5 +python-bsblan==0.5.6 # homeassistant.components.clementine python-clementine-remote==1.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0342b345f50..e2d7e4e9a6a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1370,7 +1370,7 @@ pytankerkoenig==0.0.6 pytautulli==21.11.0 # homeassistant.components.bsblan -python-bsblan==0.5.5 +python-bsblan==0.5.6 # homeassistant.components.ecobee python-ecobee-api==0.2.14 From fa350b984a9ea9bdd7de289d1e34288ee46de2a7 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Thu, 27 Oct 2022 20:42:16 +0200 Subject: [PATCH 0025/1033] Clean up superfluous Netatmo API calls (#81095) --- homeassistant/components/netatmo/data_handler.py | 5 ++++- homeassistant/components/netatmo/netatmo_entity_base.py | 8 +++++--- tests/components/netatmo/test_camera.py | 2 +- tests/components/netatmo/test_init.py | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/netatmo/data_handler.py b/homeassistant/components/netatmo/data_handler.py index b4be2a147f7..1a322f8d8db 100644 --- a/homeassistant/components/netatmo/data_handler.py +++ b/homeassistant/components/netatmo/data_handler.py @@ -252,7 +252,7 @@ class NetatmoDataHandler: self, signal_name: str, update_callback: CALLBACK_TYPE | None ) -> None: """Unsubscribe from publisher.""" - if update_callback in self.publisher[signal_name].subscriptions: + if update_callback not in self.publisher[signal_name].subscriptions: return self.publisher[signal_name].subscriptions.remove(update_callback) @@ -288,6 +288,9 @@ class NetatmoDataHandler: person.entity_id: person.pseudo for person in home.persons.values() } + await self.unsubscribe(WEATHER, None) + await self.unsubscribe(AIR_CARE, None) + def setup_air_care(self) -> None: """Set up home coach/air care modules.""" for module in self.account.modules.values(): diff --git a/homeassistant/components/netatmo/netatmo_entity_base.py b/homeassistant/components/netatmo/netatmo_entity_base.py index d0359d739fd..c434d370e27 100644 --- a/homeassistant/components/netatmo/netatmo_entity_base.py +++ b/homeassistant/components/netatmo/netatmo_entity_base.py @@ -63,9 +63,11 @@ class NetatmoBase(Entity): publisher["name"], signal_name, self.async_update_callback ) - for sub in self.data_handler.publisher[signal_name].subscriptions: - if sub is None: - await self.data_handler.unsubscribe(signal_name, None) + if any( + sub is None + for sub in self.data_handler.publisher[signal_name].subscriptions + ): + await self.data_handler.unsubscribe(signal_name, None) registry = dr.async_get(self.hass) if device := registry.async_get_device({(DOMAIN, self._id)}): diff --git a/tests/components/netatmo/test_camera.py b/tests/components/netatmo/test_camera.py index 027b0907d50..beb91c7565e 100644 --- a/tests/components/netatmo/test_camera.py +++ b/tests/components/netatmo/test_camera.py @@ -472,7 +472,7 @@ async def test_setup_component_no_devices(hass, config_entry): assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() - assert fake_post_hits == 9 + assert fake_post_hits == 11 async def test_camera_image_raises_exception(hass, config_entry, requests_mock): diff --git a/tests/components/netatmo/test_init.py b/tests/components/netatmo/test_init.py index 187a89afeb6..65cc991ec67 100644 --- a/tests/components/netatmo/test_init.py +++ b/tests/components/netatmo/test_init.py @@ -110,7 +110,7 @@ async def test_setup_component_with_config(hass, config_entry): await hass.async_block_till_done() - assert fake_post_hits == 8 + assert fake_post_hits == 10 mock_impl.assert_called_once() mock_webhook.assert_called_once() From c21fb222f9ec014ae2c5c176a0075da9a80ccd8c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 27 Oct 2022 14:38:42 -0500 Subject: [PATCH 0026/1033] Add support for oralb IO Series 4 (#81110) --- homeassistant/components/oralb/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/oralb/__init__.py | 11 ++++++++ tests/components/oralb/test_config_flow.py | 21 ++++++++++++++- tests/components/oralb/test_sensor.py | 27 +++++++++++++++++++- 6 files changed, 60 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/oralb/manifest.json b/homeassistant/components/oralb/manifest.json index b3dfedde532..bf6879733f5 100644 --- a/homeassistant/components/oralb/manifest.json +++ b/homeassistant/components/oralb/manifest.json @@ -8,7 +8,7 @@ "manufacturer_id": 220 } ], - "requirements": ["oralb-ble==0.5.0"], + "requirements": ["oralb-ble==0.6.0"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 8d1516ed5cb..e28875e86a5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1238,7 +1238,7 @@ openwrt-luci-rpc==1.1.11 openwrt-ubus-rpc==0.0.2 # homeassistant.components.oralb -oralb-ble==0.5.0 +oralb-ble==0.6.0 # homeassistant.components.oru oru==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e2d7e4e9a6a..85da0c65ae4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -883,7 +883,7 @@ open-meteo==0.2.1 openerz-api==0.1.0 # homeassistant.components.oralb -oralb-ble==0.5.0 +oralb-ble==0.6.0 # homeassistant.components.ovo_energy ovoenergy==1.2.0 diff --git a/tests/components/oralb/__init__.py b/tests/components/oralb/__init__.py index 567b9d7328e..5525a859f21 100644 --- a/tests/components/oralb/__init__.py +++ b/tests/components/oralb/__init__.py @@ -22,3 +22,14 @@ ORALB_SERVICE_INFO = BluetoothServiceInfo( service_data={}, source="local", ) + + +ORALB_IO_SERIES_4_SERVICE_INFO = BluetoothServiceInfo( + name="GXB772CD\x00\x00\x00\x00\x00\x00\x00\x00\x00", + address="78:DB:2F:C2:48:BE", + rssi=-63, + manufacturer_data={220: b"\x074\x0c\x038\x00\x00\x02\x01\x00\x04"}, + service_uuids=[], + service_data={}, + source="local", +) diff --git a/tests/components/oralb/test_config_flow.py b/tests/components/oralb/test_config_flow.py index e4af11faddb..cb7f97a5089 100644 --- a/tests/components/oralb/test_config_flow.py +++ b/tests/components/oralb/test_config_flow.py @@ -6,7 +6,7 @@ from homeassistant import config_entries from homeassistant.components.oralb.const import DOMAIN from homeassistant.data_entry_flow import FlowResultType -from . import NOT_ORALB_SERVICE_INFO, ORALB_SERVICE_INFO +from . import NOT_ORALB_SERVICE_INFO, ORALB_IO_SERIES_4_SERVICE_INFO, ORALB_SERVICE_INFO from tests.common import MockConfigEntry @@ -30,6 +30,25 @@ async def test_async_step_bluetooth_valid_device(hass): assert result2["result"].unique_id == "78:DB:2F:C2:48:BE" +async def test_async_step_bluetooth_valid_io_series4_device(hass): + """Test discovery via bluetooth with a valid device.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=ORALB_IO_SERIES_4_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "bluetooth_confirm" + with patch("homeassistant.components.oralb.async_setup_entry", return_value=True): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={} + ) + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == "IO Series 4 48BE" + assert result2["data"] == {} + assert result2["result"].unique_id == "78:DB:2F:C2:48:BE" + + async def test_async_step_bluetooth_not_oralb(hass): """Test discovery via bluetooth not oralb.""" result = await hass.config_entries.flow.async_init( diff --git a/tests/components/oralb/test_sensor.py b/tests/components/oralb/test_sensor.py index 4e37005f65a..2122ad9bbff 100644 --- a/tests/components/oralb/test_sensor.py +++ b/tests/components/oralb/test_sensor.py @@ -4,7 +4,7 @@ from homeassistant.components.oralb.const import DOMAIN from homeassistant.const import ATTR_FRIENDLY_NAME -from . import ORALB_SERVICE_INFO +from . import ORALB_IO_SERIES_4_SERVICE_INFO, ORALB_SERVICE_INFO from tests.common import MockConfigEntry from tests.components.bluetooth import inject_bluetooth_service_info @@ -38,3 +38,28 @@ async def test_sensors(hass, entity_registry_enabled_by_default): assert await hass.config_entries.async_unload(entry.entry_id) await hass.async_block_till_done() + + +async def test_sensors_io_series_4(hass, entity_registry_enabled_by_default): + """Test setting up creates the sensors with an io series 4.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=ORALB_IO_SERIES_4_SERVICE_INFO.address, + ) + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.states.async_all("sensor")) == 0 + inject_bluetooth_service_info(hass, ORALB_IO_SERIES_4_SERVICE_INFO) + await hass.async_block_till_done() + assert len(hass.states.async_all("sensor")) == 8 + + toothbrush_sensor = hass.states.get("sensor.io_series_4_48be_mode") + toothbrush_sensor_attrs = toothbrush_sensor.attributes + assert toothbrush_sensor.state == "gum care" + assert toothbrush_sensor_attrs[ATTR_FRIENDLY_NAME] == "IO Series 4 48BE Mode" + + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() From 588277623f00dcdc6a6fde18316d129f0f54f419 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 27 Oct 2022 14:38:53 -0500 Subject: [PATCH 0027/1033] Bump dbus-fast to 1.51.0 (#81109) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index a3348a1611b..60b260baf36 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.4.2", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", - "dbus-fast==1.49.0" + "dbus-fast==1.51.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 0a4ccf0f58e..d4a2dbc438b 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.1 -dbus-fast==1.49.0 +dbus-fast==1.51.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index e28875e86a5..39c85bec0a5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -540,7 +540,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.49.0 +dbus-fast==1.51.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 85da0c65ae4..fcc9542cc6a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -420,7 +420,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.49.0 +dbus-fast==1.51.0 # homeassistant.components.debugpy debugpy==1.6.3 From 68346599d2802f67df516700551a67b6ef97f512 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 27 Oct 2022 21:51:09 +0200 Subject: [PATCH 0028/1033] Add WS API recorder/statistic_during_period (#80663) --- .../components/recorder/statistics.py | 373 ++++++++++++- .../components/recorder/websocket_api.py | 148 +++++- .../components/recorder/test_websocket_api.py | 491 +++++++++++++++++- 3 files changed, 987 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 2b249aeeb14..8a744fd4daa 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -1113,6 +1113,377 @@ def _statistics_during_period_stmt_short_term( return stmt +def _get_max_mean_min_statistic_in_sub_period( + session: Session, + result: dict[str, float], + start_time: datetime | None, + end_time: datetime | None, + table: type[Statistics | StatisticsShortTerm], + types: set[str], + metadata_id: int, +) -> None: + """Return max, mean and min during the period.""" + # Calculate max, mean, min + columns = [] + if "max" in types: + columns.append(func.max(table.max)) + if "mean" in types: + columns.append(func.avg(table.mean)) + columns.append(func.count(table.mean)) + if "min" in types: + columns.append(func.min(table.min)) + stmt = lambda_stmt(lambda: select(columns).filter(table.metadata_id == metadata_id)) + if start_time is not None: + stmt += lambda q: q.filter(table.start >= start_time) + if end_time is not None: + stmt += lambda q: q.filter(table.start < end_time) + stats = execute_stmt_lambda_element(session, stmt) + if "max" in types and stats and (new_max := stats[0].max) is not None: + old_max = result.get("max") + result["max"] = max(new_max, old_max) if old_max is not None else new_max + if "mean" in types and stats and stats[0].avg is not None: + duration = stats[0].count * table.duration.total_seconds() + result["duration"] = result.get("duration", 0.0) + duration + result["mean_acc"] = result.get("mean_acc", 0.0) + stats[0].avg * duration + if "min" in types and stats and (new_min := stats[0].min) is not None: + old_min = result.get("min") + result["min"] = min(new_min, old_min) if old_min is not None else new_min + + +def _get_max_mean_min_statistic( + session: Session, + head_start_time: datetime | None, + head_end_time: datetime | None, + main_start_time: datetime | None, + main_end_time: datetime | None, + tail_start_time: datetime | None, + tail_end_time: datetime | None, + tail_only: bool, + metadata_id: int, + types: set[str], +) -> dict[str, float | None]: + """Return max, mean and min during the period. + + The mean is a time weighted average, combining hourly and 5-minute statistics if + necessary. + """ + max_mean_min: dict[str, float] = {} + result: dict[str, float | None] = {} + + if tail_start_time is not None: + # Calculate max, mean, min + _get_max_mean_min_statistic_in_sub_period( + session, + max_mean_min, + tail_start_time, + tail_end_time, + StatisticsShortTerm, + types, + metadata_id, + ) + + if not tail_only: + _get_max_mean_min_statistic_in_sub_period( + session, + max_mean_min, + main_start_time, + main_end_time, + Statistics, + types, + metadata_id, + ) + + if head_start_time is not None: + _get_max_mean_min_statistic_in_sub_period( + session, + max_mean_min, + head_start_time, + head_end_time, + StatisticsShortTerm, + types, + metadata_id, + ) + + if "max" in types: + result["max"] = max_mean_min.get("max") + if "mean" in types: + if "mean_acc" not in max_mean_min: + result["mean"] = None + else: + result["mean"] = max_mean_min["mean_acc"] / max_mean_min["duration"] + if "min" in types: + result["min"] = max_mean_min.get("min") + return result + + +def _get_oldest_sum_statistic( + session: Session, + head_start_time: datetime | None, + main_start_time: datetime | None, + tail_start_time: datetime | None, + tail_only: bool, + metadata_id: int, +) -> float | None: + """Return the oldest non-NULL sum during the period.""" + + def _get_oldest_sum_statistic_in_sub_period( + session: Session, + start_time: datetime | None, + table: type[Statistics | StatisticsShortTerm], + metadata_id: int, + ) -> tuple[float | None, datetime | None]: + """Return the oldest non-NULL sum during the period.""" + stmt = lambda_stmt( + lambda: select(table.sum, table.start) + .filter(table.metadata_id == metadata_id) + .filter(table.sum.is_not(None)) + .order_by(table.start.asc()) + .limit(1) + ) + if start_time is not None: + start_time = start_time + table.duration - timedelta.resolution + if table == StatisticsShortTerm: + minutes = start_time.minute - start_time.minute % 5 + period = start_time.replace(minute=minutes, second=0, microsecond=0) + else: + period = start_time.replace(minute=0, second=0, microsecond=0) + prev_period = period - table.duration + stmt += lambda q: q.filter(table.start == prev_period) + stats = execute_stmt_lambda_element(session, stmt) + return ( + (stats[0].sum, process_timestamp(stats[0].start)) if stats else (None, None) + ) + + oldest_start: datetime | None + oldest_sum: float | None = None + + if head_start_time is not None: + oldest_sum, oldest_start = _get_oldest_sum_statistic_in_sub_period( + session, head_start_time, StatisticsShortTerm, metadata_id + ) + if ( + oldest_start is not None + and oldest_start < head_start_time + and oldest_sum is not None + ): + return oldest_sum + + if not tail_only: + assert main_start_time is not None + oldest_sum, oldest_start = _get_oldest_sum_statistic_in_sub_period( + session, main_start_time, Statistics, metadata_id + ) + if ( + oldest_start is not None + and oldest_start < main_start_time + and oldest_sum is not None + ): + return oldest_sum + return 0 + + if tail_start_time is not None: + oldest_sum, oldest_start = _get_oldest_sum_statistic_in_sub_period( + session, tail_start_time, StatisticsShortTerm, metadata_id + ) + if ( + oldest_start is not None + and oldest_start < tail_start_time + and oldest_sum is not None + ): + return oldest_sum + + return 0 + + +def _get_newest_sum_statistic( + session: Session, + head_start_time: datetime | None, + head_end_time: datetime | None, + main_start_time: datetime | None, + main_end_time: datetime | None, + tail_start_time: datetime | None, + tail_end_time: datetime | None, + tail_only: bool, + metadata_id: int, +) -> float | None: + """Return the newest non-NULL sum during the period.""" + + def _get_newest_sum_statistic_in_sub_period( + session: Session, + start_time: datetime | None, + end_time: datetime | None, + table: type[Statistics | StatisticsShortTerm], + metadata_id: int, + ) -> float | None: + """Return the newest non-NULL sum during the period.""" + stmt = lambda_stmt( + lambda: select( + table.sum, + ) + .filter(table.metadata_id == metadata_id) + .filter(table.sum.is_not(None)) + .order_by(table.start.desc()) + .limit(1) + ) + if start_time is not None: + stmt += lambda q: q.filter(table.start >= start_time) + if end_time is not None: + stmt += lambda q: q.filter(table.start < end_time) + stats = execute_stmt_lambda_element(session, stmt) + + return stats[0].sum if stats else None + + newest_sum: float | None = None + + if tail_start_time is not None: + newest_sum = _get_newest_sum_statistic_in_sub_period( + session, tail_start_time, tail_end_time, StatisticsShortTerm, metadata_id + ) + if newest_sum is not None: + return newest_sum + + if not tail_only: + newest_sum = _get_newest_sum_statistic_in_sub_period( + session, main_start_time, main_end_time, Statistics, metadata_id + ) + if newest_sum is not None: + return newest_sum + + if head_start_time is not None: + newest_sum = _get_newest_sum_statistic_in_sub_period( + session, head_start_time, head_end_time, StatisticsShortTerm, metadata_id + ) + + return newest_sum + + +def statistic_during_period( + hass: HomeAssistant, + start_time: datetime | None, + end_time: datetime | None, + statistic_id: str, + types: set[str] | None, + units: dict[str, str] | None, +) -> dict[str, Any]: + """Return a statistic data point for the UTC period start_time - end_time.""" + metadata = None + + if not types: + types = {"max", "mean", "min", "change"} + + result: dict[str, Any] = {} + + # To calculate the summary, data from the statistics (hourly) and short_term_statistics + # (5 minute) tables is combined + # - The short term statistics table is used for the head and tail of the period, + # if the period it doesn't start or end on a full hour + # - The statistics table is used for the remainder of the time + now = dt_util.utcnow() + if end_time is not None and end_time > now: + end_time = now + + tail_only = ( + start_time is not None + and end_time is not None + and end_time - start_time < timedelta(hours=1) + ) + + # Calculate the head period + head_start_time: datetime | None = None + head_end_time: datetime | None = None + if not tail_only and start_time is not None and start_time.minute: + head_start_time = start_time + head_end_time = start_time.replace( + minute=0, second=0, microsecond=0 + ) + timedelta(hours=1) + + # Calculate the tail period + tail_start_time: datetime | None = None + tail_end_time: datetime | None = None + if end_time is None: + tail_start_time = now.replace(minute=0, second=0, microsecond=0) + elif end_time.minute: + tail_start_time = ( + start_time + if tail_only + else end_time.replace(minute=0, second=0, microsecond=0) + ) + tail_end_time = end_time + + # Calculate the main period + main_start_time: datetime | None = None + main_end_time: datetime | None = None + if not tail_only: + main_start_time = start_time if head_end_time is None else head_end_time + main_end_time = end_time if tail_start_time is None else tail_start_time + + with session_scope(hass=hass) as session: + # Fetch metadata for the given statistic_id + metadata = get_metadata_with_session(session, statistic_ids=[statistic_id]) + if not metadata: + return result + + metadata_id = metadata[statistic_id][0] + + if not types.isdisjoint({"max", "mean", "min"}): + result = _get_max_mean_min_statistic( + session, + head_start_time, + head_end_time, + main_start_time, + main_end_time, + tail_start_time, + tail_end_time, + tail_only, + metadata_id, + types, + ) + + if "change" in types: + oldest_sum: float | None + if start_time is None: + oldest_sum = 0.0 + else: + oldest_sum = _get_oldest_sum_statistic( + session, + head_start_time, + main_start_time, + tail_start_time, + tail_only, + metadata_id, + ) + newest_sum = _get_newest_sum_statistic( + session, + head_start_time, + head_end_time, + main_start_time, + main_end_time, + tail_start_time, + tail_end_time, + tail_only, + metadata_id, + ) + # Calculate the difference between the oldest and newest sum + if oldest_sum is not None and newest_sum is not None: + result["change"] = newest_sum - oldest_sum + else: + result["change"] = None + + def no_conversion(val: float | None) -> float | None: + """Return val.""" + return val + + state_unit = unit = metadata[statistic_id][1]["unit_of_measurement"] + if state := hass.states.get(statistic_id): + state_unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) + if unit is not None: + convert = _get_statistic_to_display_unit_converter(unit, state_unit, units) + else: + convert = no_conversion + + return {key: convert(value) for key, value in result.items()} + + def statistics_during_period( hass: HomeAssistant, start_time: datetime, @@ -1122,7 +1493,7 @@ def statistics_during_period( start_time_as_datetime: bool = False, units: dict[str, str] | None = None, ) -> dict[str, list[dict[str, Any]]]: - """Return statistics during UTC period start_time - end_time for the statistic_ids. + """Return statistic data points during UTC period start_time - end_time. If end_time is omitted, returns statistics newer than or equal to start_time. If statistic_ids is omitted, returns statistics for all statistics ids. diff --git a/homeassistant/components/recorder/websocket_api.py b/homeassistant/components/recorder/websocket_api.py index 2079d9537b5..9b2ef417755 100644 --- a/homeassistant/components/recorder/websocket_api.py +++ b/homeassistant/components/recorder/websocket_api.py @@ -1,7 +1,7 @@ """The Recorder websocket API.""" from __future__ import annotations -from datetime import datetime as dt +from datetime import datetime as dt, timedelta import logging from typing import Any, Literal @@ -10,6 +10,7 @@ import voluptuous as vol from homeassistant.components import websocket_api from homeassistant.components.websocket_api import messages from homeassistant.core import HomeAssistant, callback, valid_entity_id +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv from homeassistant.helpers.json import JSON_DUMP from homeassistant.util import dt as dt_util @@ -31,6 +32,7 @@ from .statistics import ( async_change_statistics_unit, async_import_statistics, list_statistic_ids, + statistic_during_period, statistics_during_period, validate_statistics, ) @@ -47,6 +49,7 @@ def async_setup(hass: HomeAssistant) -> None: websocket_api.async_register_command(hass, ws_backup_start) websocket_api.async_register_command(hass, ws_change_statistics_unit) websocket_api.async_register_command(hass, ws_clear_statistics) + websocket_api.async_register_command(hass, ws_get_statistic_during_period) websocket_api.async_register_command(hass, ws_get_statistics_during_period) websocket_api.async_register_command(hass, ws_get_statistics_metadata) websocket_api.async_register_command(hass, ws_list_statistic_ids) @@ -56,6 +59,149 @@ def async_setup(hass: HomeAssistant) -> None: websocket_api.async_register_command(hass, ws_validate_statistics) +def _ws_get_statistic_during_period( + hass: HomeAssistant, + msg_id: int, + start_time: dt | None, + end_time: dt | None, + statistic_id: str, + types: set[str] | None, + units: dict[str, str], +) -> str: + """Fetch statistics and convert them to json in the executor.""" + return JSON_DUMP( + messages.result_message( + msg_id, + statistic_during_period( + hass, start_time, end_time, statistic_id, types, units=units + ), + ) + ) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "recorder/statistic_during_period", + vol.Exclusive("calendar", "period"): vol.Schema( + { + vol.Required("period"): vol.Any("hour", "day", "week", "month", "year"), + vol.Optional("offset"): int, + } + ), + vol.Exclusive("fixed_period", "period"): vol.Schema( + { + vol.Optional("start_time"): str, + vol.Optional("end_time"): str, + } + ), + vol.Exclusive("rolling_window", "period"): vol.Schema( + { + vol.Required("duration"): cv.time_period_dict, + vol.Optional("offset"): cv.time_period_dict, + } + ), + vol.Optional("statistic_id"): str, + vol.Optional("types"): vol.All([str], vol.Coerce(set)), + vol.Optional("units"): vol.Schema( + { + vol.Optional("distance"): vol.In(DistanceConverter.VALID_UNITS), + vol.Optional("energy"): vol.In(EnergyConverter.VALID_UNITS), + vol.Optional("mass"): vol.In(MassConverter.VALID_UNITS), + vol.Optional("power"): vol.In(PowerConverter.VALID_UNITS), + vol.Optional("pressure"): vol.In(PressureConverter.VALID_UNITS), + vol.Optional("speed"): vol.In(SpeedConverter.VALID_UNITS), + vol.Optional("temperature"): vol.In(TemperatureConverter.VALID_UNITS), + vol.Optional("volume"): vol.In(VolumeConverter.VALID_UNITS), + } + ), + } +) +@websocket_api.async_response +async def ws_get_statistic_during_period( + hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any] +) -> None: + """Handle statistics websocket command.""" + if ("start_time" in msg or "end_time" in msg) and "duration" in msg: + raise HomeAssistantError + if "offset" in msg and "duration" not in msg: + raise HomeAssistantError + + start_time = None + end_time = None + + if "calendar" in msg: + calendar_period = msg["calendar"]["period"] + start_of_day = dt_util.start_of_local_day() + offset = msg["calendar"].get("offset", 0) + if calendar_period == "hour": + start_time = dt_util.now().replace(minute=0, second=0, microsecond=0) + start_time += timedelta(hours=offset) + end_time = start_time + timedelta(hours=1) + elif calendar_period == "day": + start_time = start_of_day + start_time += timedelta(days=offset) + end_time = start_time + timedelta(days=1) + elif calendar_period == "week": + start_time = start_of_day - timedelta(days=start_of_day.weekday()) + start_time += timedelta(days=offset * 7) + end_time = start_time + timedelta(weeks=1) + elif calendar_period == "month": + start_time = start_of_day.replace(day=28) + # This works for up to 48 months of offset + start_time = (start_time + timedelta(days=offset * 31)).replace(day=1) + end_time = (start_time + timedelta(days=31)).replace(day=1) + else: # calendar_period = "year" + start_time = start_of_day.replace(month=12, day=31) + # This works for 100+ years of offset + start_time = (start_time + timedelta(days=offset * 366)).replace( + month=1, day=1 + ) + end_time = (start_time + timedelta(days=365)).replace(day=1) + + start_time = dt_util.as_utc(start_time) + end_time = dt_util.as_utc(end_time) + + elif "fixed_period" in msg: + if start_time_str := msg["fixed_period"].get("start_time"): + if start_time := dt_util.parse_datetime(start_time_str): + start_time = dt_util.as_utc(start_time) + else: + connection.send_error( + msg["id"], "invalid_start_time", "Invalid start_time" + ) + return + + if end_time_str := msg["fixed_period"].get("end_time"): + if end_time := dt_util.parse_datetime(end_time_str): + end_time = dt_util.as_utc(end_time) + else: + connection.send_error(msg["id"], "invalid_end_time", "Invalid end_time") + return + + elif "rolling_window" in msg: + duration = msg["rolling_window"]["duration"] + now = dt_util.utcnow() + start_time = now - duration + end_time = start_time + duration + + if offset := msg["rolling_window"].get("offset"): + start_time += offset + end_time += offset + + connection.send_message( + await get_instance(hass).async_add_executor_job( + _ws_get_statistic_during_period, + hass, + msg["id"], + start_time, + end_time, + msg.get("statistic_id"), + msg.get("types"), + msg.get("units"), + ) + ) + + def _ws_get_statistics_during_period( hass: HomeAssistant, msg_id: int, diff --git a/tests/components/recorder/test_websocket_api.py b/tests/components/recorder/test_websocket_api.py index 58cf0e5c663..00e9d0d35b4 100644 --- a/tests/components/recorder/test_websocket_api.py +++ b/tests/components/recorder/test_websocket_api.py @@ -1,14 +1,17 @@ """The tests for sensor recorder platform.""" # pylint: disable=protected-access,invalid-name +import datetime from datetime import timedelta +from statistics import fmean import threading -from unittest.mock import patch +from unittest.mock import ANY, patch from freezegun import freeze_time import pytest from pytest import approx from homeassistant.components import recorder +from homeassistant.components.recorder.db_schema import Statistics, StatisticsShortTerm from homeassistant.components.recorder.statistics import ( async_add_external_statistics, get_last_statistics, @@ -178,6 +181,448 @@ async def test_statistics_during_period(recorder_mock, hass, hass_ws_client): } +@freeze_time(datetime.datetime(2022, 10, 21, 7, 25, tzinfo=datetime.timezone.utc)) +async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): + """Test statistic_during_period.""" + id = 1 + + def next_id(): + nonlocal id + id += 1 + return id + + now = dt_util.utcnow() + + await async_recorder_block_till_done(hass) + client = await hass_ws_client() + + zero = now + start = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=-3) + + imported_stats_5min = [ + { + "start": (start + timedelta(minutes=5 * i)), + "max": i * 2, + "mean": i, + "min": -76 + i * 2, + "sum": i, + } + for i in range(0, 39) + ] + imported_stats = [ + { + "start": imported_stats_5min[i * 12]["start"], + "max": max( + stat["max"] for stat in imported_stats_5min[i * 12 : (i + 1) * 12] + ), + "mean": fmean( + stat["mean"] for stat in imported_stats_5min[i * 12 : (i + 1) * 12] + ), + "min": min( + stat["min"] for stat in imported_stats_5min[i * 12 : (i + 1) * 12] + ), + "sum": imported_stats_5min[i * 12 + 11]["sum"], + } + for i in range(0, 3) + ] + imported_metadata = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "recorder", + "statistic_id": "sensor.test", + "unit_of_measurement": "kWh", + } + + recorder.get_instance(hass).async_import_statistics( + imported_metadata, + imported_stats, + Statistics, + ) + recorder.get_instance(hass).async_import_statistics( + imported_metadata, + imported_stats_5min, + StatisticsShortTerm, + ) + await async_wait_recording_done(hass) + + # No data for this period yet + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "fixed_period": { + "start_time": now.isoformat(), + "end_time": now.isoformat(), + }, + "statistic_id": "sensor.test", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": None, + "mean": None, + "min": None, + "change": None, + } + + # This should include imported_statistics_5min[:] + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "statistic_id": "sensor.test", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[:]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[:]), + "min": min(stat["min"] for stat in imported_stats_5min[:]), + "change": imported_stats_5min[-1]["sum"] - imported_stats_5min[0]["sum"], + } + + # This should also include imported_statistics_5min[:] + start_time = "2022-10-21T04:00:00+00:00" + end_time = "2022-10-21T07:15:00+00:00" + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "statistic_id": "sensor.test", + "fixed_period": { + "start_time": start_time, + "end_time": end_time, + }, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[:]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[:]), + "min": min(stat["min"] for stat in imported_stats_5min[:]), + "change": imported_stats_5min[-1]["sum"] - imported_stats_5min[0]["sum"], + } + + # This should also include imported_statistics_5min[:] + start_time = "2022-10-20T04:00:00+00:00" + end_time = "2022-10-21T08:20:00+00:00" + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "statistic_id": "sensor.test", + "fixed_period": { + "start_time": start_time, + "end_time": end_time, + }, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[:]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[:]), + "min": min(stat["min"] for stat in imported_stats_5min[:]), + "change": imported_stats_5min[-1]["sum"] - imported_stats_5min[0]["sum"], + } + + # This should include imported_statistics_5min[26:] + start_time = "2022-10-21T06:10:00+00:00" + assert imported_stats_5min[26]["start"].isoformat() == start_time + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "fixed_period": { + "start_time": start_time, + }, + "statistic_id": "sensor.test", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[26:]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[26:]), + "min": min(stat["min"] for stat in imported_stats_5min[26:]), + "change": imported_stats_5min[-1]["sum"] - imported_stats_5min[25]["sum"], + } + + # This should also include imported_statistics_5min[26:] + start_time = "2022-10-21T06:09:00+00:00" + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "fixed_period": { + "start_time": start_time, + }, + "statistic_id": "sensor.test", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[26:]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[26:]), + "min": min(stat["min"] for stat in imported_stats_5min[26:]), + "change": imported_stats_5min[-1]["sum"] - imported_stats_5min[25]["sum"], + } + + # This should include imported_statistics_5min[:26] + end_time = "2022-10-21T06:10:00+00:00" + assert imported_stats_5min[26]["start"].isoformat() == end_time + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "fixed_period": { + "end_time": end_time, + }, + "statistic_id": "sensor.test", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[:26]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[:26]), + "min": min(stat["min"] for stat in imported_stats_5min[:26]), + "change": imported_stats_5min[25]["sum"] - 0, + } + + # This should include imported_statistics_5min[26:32] (less than a full hour) + start_time = "2022-10-21T06:10:00+00:00" + assert imported_stats_5min[26]["start"].isoformat() == start_time + end_time = "2022-10-21T06:40:00+00:00" + assert imported_stats_5min[32]["start"].isoformat() == end_time + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "fixed_period": { + "start_time": start_time, + "end_time": end_time, + }, + "statistic_id": "sensor.test", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[26:32]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[26:32]), + "min": min(stat["min"] for stat in imported_stats_5min[26:32]), + "change": imported_stats_5min[31]["sum"] - imported_stats_5min[25]["sum"], + } + + # This should include imported_statistics[2:] + imported_statistics_5min[36:] + start_time = "2022-10-21T06:00:00+00:00" + assert imported_stats_5min[24]["start"].isoformat() == start_time + assert imported_stats[2]["start"].isoformat() == start_time + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "fixed_period": { + "start_time": start_time, + }, + "statistic_id": "sensor.test", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[24:]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[24:]), + "min": min(stat["min"] for stat in imported_stats_5min[24:]), + "change": imported_stats_5min[-1]["sum"] - imported_stats_5min[23]["sum"], + } + + # This should also include imported_statistics[2:] + imported_statistics_5min[36:] + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "rolling_window": { + "duration": {"hours": 1, "minutes": 25}, + }, + "statistic_id": "sensor.test", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[24:]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[24:]), + "min": min(stat["min"] for stat in imported_stats_5min[24:]), + "change": imported_stats_5min[-1]["sum"] - imported_stats_5min[23]["sum"], + } + + # This should include imported_statistics[2:3] + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "rolling_window": { + "duration": {"hours": 1}, + "offset": {"minutes": -25}, + }, + "statistic_id": "sensor.test", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[24:36]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[24:36]), + "min": min(stat["min"] for stat in imported_stats_5min[24:36]), + "change": imported_stats_5min[35]["sum"] - imported_stats_5min[23]["sum"], + } + + # Test we can get only selected types + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "statistic_id": "sensor.test", + "types": ["max", "change"], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[:]), + "change": imported_stats_5min[-1]["sum"] - imported_stats_5min[0]["sum"], + } + + # Test we can convert units + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "statistic_id": "sensor.test", + "units": {"energy": "MWh"}, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[:]) / 1000, + "mean": fmean(stat["mean"] for stat in imported_stats_5min[:]) / 1000, + "min": min(stat["min"] for stat in imported_stats_5min[:]) / 1000, + "change": (imported_stats_5min[-1]["sum"] - imported_stats_5min[0]["sum"]) + / 1000, + } + + # Test we can automatically convert units + hass.states.async_set("sensor.test", None, attributes=ENERGY_SENSOR_WH_ATTRIBUTES) + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "statistic_id": "sensor.test", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats_5min[:]) * 1000, + "mean": fmean(stat["mean"] for stat in imported_stats_5min[:]) * 1000, + "min": min(stat["min"] for stat in imported_stats_5min[:]) * 1000, + "change": (imported_stats_5min[-1]["sum"] - imported_stats_5min[0]["sum"]) + * 1000, + } + + +@freeze_time(datetime.datetime(2022, 10, 21, 7, 25, tzinfo=datetime.timezone.utc)) +@pytest.mark.parametrize( + "calendar_period, start_time, end_time", + ( + ( + {"period": "hour"}, + "2022-10-21T07:00:00+00:00", + "2022-10-21T08:00:00+00:00", + ), + ( + {"period": "hour", "offset": -1}, + "2022-10-21T06:00:00+00:00", + "2022-10-21T07:00:00+00:00", + ), + ( + {"period": "day"}, + "2022-10-21T07:00:00+00:00", + "2022-10-22T07:00:00+00:00", + ), + ( + {"period": "day", "offset": -1}, + "2022-10-20T07:00:00+00:00", + "2022-10-21T07:00:00+00:00", + ), + ( + {"period": "week"}, + "2022-10-17T07:00:00+00:00", + "2022-10-24T07:00:00+00:00", + ), + ( + {"period": "week", "offset": -1}, + "2022-10-10T07:00:00+00:00", + "2022-10-17T07:00:00+00:00", + ), + ( + {"period": "month"}, + "2022-10-01T07:00:00+00:00", + "2022-11-01T07:00:00+00:00", + ), + ( + {"period": "month", "offset": -1}, + "2022-09-01T07:00:00+00:00", + "2022-10-01T07:00:00+00:00", + ), + ( + {"period": "year"}, + "2022-01-01T08:00:00+00:00", + "2023-01-01T08:00:00+00:00", + ), + ( + {"period": "year", "offset": -1}, + "2021-01-01T08:00:00+00:00", + "2022-01-01T08:00:00+00:00", + ), + ), +) +async def test_statistic_during_period_calendar( + recorder_mock, hass, hass_ws_client, calendar_period, start_time, end_time +): + """Test statistic_during_period.""" + client = await hass_ws_client() + + # Try requesting data for the current hour + with patch( + "homeassistant.components.recorder.websocket_api.statistic_during_period", + return_value={}, + ) as statistic_during_period: + await client.send_json( + { + "id": 1, + "type": "recorder/statistic_during_period", + "calendar": calendar_period, + "statistic_id": "sensor.test", + } + ) + response = await client.receive_json() + statistic_during_period.assert_called_once_with( + hass, ANY, ANY, "sensor.test", None, units=None + ) + assert statistic_during_period.call_args[0][1].isoformat() == start_time + assert statistic_during_period.call_args[0][2].isoformat() == end_time + assert response["success"] + + @pytest.mark.parametrize( "attributes, state, value, custom_units, converted_value", [ @@ -1595,20 +2040,20 @@ async def test_import_statistics( period1 = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1) period2 = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=2) - external_statistics1 = { + imported_statistics1 = { "start": period1.isoformat(), "last_reset": None, "state": 0, "sum": 2, } - external_statistics2 = { + imported_statistics2 = { "start": period2.isoformat(), "last_reset": None, "state": 1, "sum": 3, } - external_metadata = { + imported_metadata = { "has_mean": False, "has_sum": True, "name": "Total imported energy", @@ -1621,8 +2066,8 @@ async def test_import_statistics( { "id": 1, "type": "recorder/import_statistics", - "metadata": external_metadata, - "stats": [external_statistics1, external_statistics2], + "metadata": imported_metadata, + "stats": [imported_statistics1, imported_statistics2], } ) response = await client.receive_json() @@ -1712,7 +2157,7 @@ async def test_import_statistics( { "id": 2, "type": "recorder/import_statistics", - "metadata": external_metadata, + "metadata": imported_metadata, "stats": [external_statistics], } ) @@ -1764,7 +2209,7 @@ async def test_import_statistics( { "id": 3, "type": "recorder/import_statistics", - "metadata": external_metadata, + "metadata": imported_metadata, "stats": [external_statistics], } ) @@ -1822,20 +2267,20 @@ async def test_adjust_sum_statistics_energy( period1 = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1) period2 = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=2) - external_statistics1 = { + imported_statistics1 = { "start": period1.isoformat(), "last_reset": None, "state": 0, "sum": 2, } - external_statistics2 = { + imported_statistics2 = { "start": period2.isoformat(), "last_reset": None, "state": 1, "sum": 3, } - external_metadata = { + imported_metadata = { "has_mean": False, "has_sum": True, "name": "Total imported energy", @@ -1848,8 +2293,8 @@ async def test_adjust_sum_statistics_energy( { "id": 1, "type": "recorder/import_statistics", - "metadata": external_metadata, - "stats": [external_statistics1, external_statistics2], + "metadata": imported_metadata, + "stats": [imported_statistics1, imported_statistics2], } ) response = await client.receive_json() @@ -2018,20 +2463,20 @@ async def test_adjust_sum_statistics_gas( period1 = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1) period2 = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=2) - external_statistics1 = { + imported_statistics1 = { "start": period1.isoformat(), "last_reset": None, "state": 0, "sum": 2, } - external_statistics2 = { + imported_statistics2 = { "start": period2.isoformat(), "last_reset": None, "state": 1, "sum": 3, } - external_metadata = { + imported_metadata = { "has_mean": False, "has_sum": True, "name": "Total imported energy", @@ -2044,8 +2489,8 @@ async def test_adjust_sum_statistics_gas( { "id": 1, "type": "recorder/import_statistics", - "metadata": external_metadata, - "stats": [external_statistics1, external_statistics2], + "metadata": imported_metadata, + "stats": [imported_statistics1, imported_statistics2], } ) response = await client.receive_json() @@ -2229,20 +2674,20 @@ async def test_adjust_sum_statistics_errors( period1 = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1) period2 = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=2) - external_statistics1 = { + imported_statistics1 = { "start": period1.isoformat(), "last_reset": None, "state": 0, "sum": 2, } - external_statistics2 = { + imported_statistics2 = { "start": period2.isoformat(), "last_reset": None, "state": 1, "sum": 3, } - external_metadata = { + imported_metadata = { "has_mean": False, "has_sum": True, "name": "Total imported energy", @@ -2255,8 +2700,8 @@ async def test_adjust_sum_statistics_errors( { "id": 1, "type": "recorder/import_statistics", - "metadata": external_metadata, - "stats": [external_statistics1, external_statistics2], + "metadata": imported_metadata, + "stats": [imported_statistics1, imported_statistics2], } ) response = await client.receive_json() From 428a33c00f17830d3e86f5d620a720411d75e408 Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Thu, 27 Oct 2022 22:00:34 +0200 Subject: [PATCH 0029/1033] Migrate KNX to use kelvin for color temperature (#81112) --- homeassistant/components/knx/light.py | 61 +++++++++++++-------------- tests/components/knx/test_light.py | 36 ++++++++++++---- 2 files changed, 58 insertions(+), 39 deletions(-) diff --git a/homeassistant/components/knx/light.py b/homeassistant/components/knx/light.py index 9268b53581b..e4260f5e868 100644 --- a/homeassistant/components/knx/light.py +++ b/homeassistant/components/knx/light.py @@ -9,7 +9,7 @@ from xknx.devices.light import Light as XknxLight, XYYColor from homeassistant import config_entries from homeassistant.components.light import ( ATTR_BRIGHTNESS, - ATTR_COLOR_TEMP, + ATTR_COLOR_TEMP_KELVIN, ATTR_HS_COLOR, ATTR_RGB_COLOR, ATTR_RGBW_COLOR, @@ -153,15 +153,8 @@ class KNXLight(KnxEntity, LightEntity): def __init__(self, xknx: XKNX, config: ConfigType) -> None: """Initialize of KNX light.""" super().__init__(_create_light(xknx, config)) - self._max_kelvin: int = config[LightSchema.CONF_MAX_KELVIN] - self._min_kelvin: int = config[LightSchema.CONF_MIN_KELVIN] - - self._attr_max_mireds = color_util.color_temperature_kelvin_to_mired( - self._min_kelvin - ) - self._attr_min_mireds = color_util.color_temperature_kelvin_to_mired( - self._max_kelvin - ) + self._attr_max_color_temp_kelvin: int = config[LightSchema.CONF_MAX_KELVIN] + self._attr_min_color_temp_kelvin: int = config[LightSchema.CONF_MIN_KELVIN] self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_unique_id = self._device_unique_id() @@ -242,21 +235,23 @@ class KNXLight(KnxEntity, LightEntity): return None @property - def color_temp(self) -> int | None: - """Return the color temperature in mireds.""" + def color_temp_kelvin(self) -> int | None: + """Return the color temperature in Kelvin.""" if self._device.supports_color_temperature: - kelvin = self._device.current_color_temperature - # Avoid division by zero if actuator reported 0 Kelvin (e.g., uninitialized DALI-Gateway) - if kelvin is not None and kelvin > 0: - return color_util.color_temperature_kelvin_to_mired(kelvin) + if kelvin := self._device.current_color_temperature: + return kelvin if self._device.supports_tunable_white: relative_ct = self._device.current_tunable_white if relative_ct is not None: - # as KNX devices typically use Kelvin we use it as base for - # calculating ct from percent - return color_util.color_temperature_kelvin_to_mired( - self._min_kelvin - + ((relative_ct / 255) * (self._max_kelvin - self._min_kelvin)) + return int( + self._attr_min_color_temp_kelvin + + ( + (relative_ct / 255) + * ( + self._attr_max_color_temp_kelvin + - self._attr_min_color_temp_kelvin + ) + ) ) return None @@ -288,7 +283,7 @@ class KNXLight(KnxEntity, LightEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn the light on.""" brightness = kwargs.get(ATTR_BRIGHTNESS) - mireds = kwargs.get(ATTR_COLOR_TEMP) + color_temp = kwargs.get(ATTR_COLOR_TEMP_KELVIN) rgb = kwargs.get(ATTR_RGB_COLOR) rgbw = kwargs.get(ATTR_RGBW_COLOR) hs_color = kwargs.get(ATTR_HS_COLOR) @@ -297,7 +292,7 @@ class KNXLight(KnxEntity, LightEntity): if ( not self.is_on and brightness is None - and mireds is None + and color_temp is None and rgb is None and rgbw is None and hs_color is None @@ -335,17 +330,21 @@ class KNXLight(KnxEntity, LightEntity): await set_color(rgb, None, brightness) return - if mireds is not None: - kelvin = int(color_util.color_temperature_mired_to_kelvin(mireds)) - kelvin = min(self._max_kelvin, max(self._min_kelvin, kelvin)) - + if color_temp is not None: + color_temp = min( + self._attr_max_color_temp_kelvin, + max(self._attr_min_color_temp_kelvin, color_temp), + ) if self._device.supports_color_temperature: - await self._device.set_color_temperature(kelvin) + await self._device.set_color_temperature(color_temp) elif self._device.supports_tunable_white: - relative_ct = int( + relative_ct = round( 255 - * (kelvin - self._min_kelvin) - / (self._max_kelvin - self._min_kelvin) + * (color_temp - self._attr_min_color_temp_kelvin) + / ( + self._attr_max_color_temp_kelvin + - self._attr_min_color_temp_kelvin + ) ) await self._device.set_tunable_white(relative_ct) diff --git a/tests/components/knx/test_light.py b/tests/components/knx/test_light.py index 56cf5b2c00a..2f7484fad8b 100644 --- a/tests/components/knx/test_light.py +++ b/tests/components/knx/test_light.py @@ -11,7 +11,7 @@ from homeassistant.components.knx.schema import LightSchema from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_NAME, - ATTR_COLOR_TEMP, + ATTR_COLOR_TEMP_KELVIN, ATTR_HS_COLOR, ATTR_RGBW_COLOR, ColorMode, @@ -166,19 +166,25 @@ async def test_light_color_temp_absolute(hass: HomeAssistant, knx: KNXTestKit): brightness=255, color_mode=ColorMode.COLOR_TEMP, color_temp=370, + color_temp_kelvin=2700, ) # change color temperature from HA await hass.services.async_call( "light", "turn_on", - {"entity_id": "light.test", ATTR_COLOR_TEMP: 250}, # 4000 Kelvin - 0x0FA0 + {"entity_id": "light.test", ATTR_COLOR_TEMP_KELVIN: 4000}, # 4000 - 0x0FA0 blocking=True, ) await knx.assert_write(test_ct, (0x0F, 0xA0)) knx.assert_state("light.test", STATE_ON, color_temp=250) # change color temperature from KNX await knx.receive_write(test_ct_state, (0x17, 0x70)) # 6000 Kelvin - 166 Mired - knx.assert_state("light.test", STATE_ON, color_temp=166) + knx.assert_state( + "light.test", + STATE_ON, + color_temp=166, + color_temp_kelvin=6000, + ) async def test_light_color_temp_relative(hass: HomeAssistant, knx: KNXTestKit): @@ -222,19 +228,33 @@ async def test_light_color_temp_relative(hass: HomeAssistant, knx: KNXTestKit): brightness=255, color_mode=ColorMode.COLOR_TEMP, color_temp=250, + color_temp_kelvin=4000, ) # change color temperature from HA await hass.services.async_call( "light", "turn_on", - {"entity_id": "light.test", ATTR_COLOR_TEMP: 300}, # 3333 Kelvin - 33 % - 0x54 + { + "entity_id": "light.test", + ATTR_COLOR_TEMP_KELVIN: 3333, # 3333 Kelvin - 33.3 % - 0x55 + }, blocking=True, ) - await knx.assert_write(test_ct, (0x54,)) - knx.assert_state("light.test", STATE_ON, color_temp=300) + await knx.assert_write(test_ct, (0x55,)) + knx.assert_state( + "light.test", + STATE_ON, + color_temp=300, + color_temp_kelvin=3333, + ) # change color temperature from KNX - await knx.receive_write(test_ct_state, (0xE6,)) # 3900 Kelvin - 90 % - 256 Mired - knx.assert_state("light.test", STATE_ON, color_temp=256) + await knx.receive_write(test_ct_state, (0xE6,)) # 3901 Kelvin - 90.1 % - 256 Mired + knx.assert_state( + "light.test", + STATE_ON, + color_temp=256, + color_temp_kelvin=3901, + ) async def test_light_hs_color(hass: HomeAssistant, knx: KNXTestKit): From c7d342a719fca64262106ecb8fab6d7ef2ad6608 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 27 Oct 2022 23:13:43 +0200 Subject: [PATCH 0030/1033] Update frontend to 20221027.0 (#81114) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 390b0ccfc18..c8d3645435f 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20221026.0"], + "requirements": ["home-assistant-frontend==20221027.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index d4a2dbc438b..79e6340ed41 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -21,7 +21,7 @@ dbus-fast==1.51.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 -home-assistant-frontend==20221026.0 +home-assistant-frontend==20221027.0 httpx==0.23.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index 39c85bec0a5..b10a8f07be5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -868,7 +868,7 @@ hole==0.7.0 holidays==0.16 # homeassistant.components.frontend -home-assistant-frontend==20221026.0 +home-assistant-frontend==20221027.0 # homeassistant.components.home_connect homeconnect==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fcc9542cc6a..ca4268d549f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -648,7 +648,7 @@ hole==0.7.0 holidays==0.16 # homeassistant.components.frontend -home-assistant-frontend==20221026.0 +home-assistant-frontend==20221027.0 # homeassistant.components.home_connect homeconnect==0.7.2 From f39938514766e03c375c4fc15dbbeb508a97ed67 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 28 Oct 2022 00:30:47 +0000 Subject: [PATCH 0031/1033] [ci skip] Translation update --- .../components/brother/translations/bg.json | 3 ++- .../components/generic/translations/hu.json | 9 ++++++- .../components/mqtt/translations/it.json | 6 +++++ .../components/mqtt/translations/ru.json | 3 +++ .../nibe_heatpump/translations/it.json | 9 ++++++- .../components/oralb/translations/nl.json | 22 ++++++++++++++++ .../components/pushover/translations/hu.json | 4 +++ .../components/pushover/translations/it.json | 1 + .../rainmachine/translations/de.json | 1 + .../rainmachine/translations/en.json | 6 ++--- .../rainmachine/translations/es.json | 1 + .../rainmachine/translations/et.json | 1 + .../rainmachine/translations/hu.json | 1 + .../rainmachine/translations/it.json | 1 + .../rainmachine/translations/pl.json | 1 + .../rainmachine/translations/pt-BR.json | 1 + .../components/sensor/translations/it.json | 2 ++ .../statistics/translations/hu.json | 12 +++++++++ .../statistics/translations/it.json | 12 +++++++++ .../components/zamg/translations/bg.json | 12 +++++++++ .../components/zamg/translations/et.json | 26 +++++++++++++++++++ .../components/zamg/translations/hu.json | 26 +++++++++++++++++++ .../components/zamg/translations/it.json | 26 +++++++++++++++++++ .../components/zamg/translations/nl.json | 11 ++++++++ .../components/zamg/translations/pt-BR.json | 26 +++++++++++++++++++ 25 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 homeassistant/components/oralb/translations/nl.json create mode 100644 homeassistant/components/statistics/translations/hu.json create mode 100644 homeassistant/components/statistics/translations/it.json create mode 100644 homeassistant/components/zamg/translations/bg.json create mode 100644 homeassistant/components/zamg/translations/et.json create mode 100644 homeassistant/components/zamg/translations/hu.json create mode 100644 homeassistant/components/zamg/translations/it.json create mode 100644 homeassistant/components/zamg/translations/nl.json create mode 100644 homeassistant/components/zamg/translations/pt-BR.json diff --git a/homeassistant/components/brother/translations/bg.json b/homeassistant/components/brother/translations/bg.json index 4a51b0abde1..06e4073839d 100644 --- a/homeassistant/components/brother/translations/bg.json +++ b/homeassistant/components/brother/translations/bg.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "unsupported_model": "\u0422\u043e\u0437\u0438 \u043c\u043e\u0434\u0435\u043b \u043f\u0440\u0438\u043d\u0442\u0435\u0440 \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430." }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/generic/translations/hu.json b/homeassistant/components/generic/translations/hu.json index a36457999ca..0b643a2e492 100644 --- a/homeassistant/components/generic/translations/hu.json +++ b/homeassistant/components/generic/translations/hu.json @@ -50,7 +50,7 @@ "data": { "confirmed_ok": "A k\u00e9p megfelel\u0151" }, - "description": "![Kamerak\u00e9p el\u0151n\u00e9zet] ({preview_url})", + "description": "![Kamerak\u00e9p el\u0151n\u00e9zet]({preview_url})", "title": "El\u0151n\u00e9zet" } } @@ -75,6 +75,13 @@ "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { + "confirm_still": { + "data": { + "confirmed_ok": "A k\u00e9p megfelel\u0151" + }, + "description": "![Kamerak\u00e9p el\u0151n\u00e9zet]({preview_url})", + "title": "El\u0151n\u00e9zet" + }, "content_type": { "data": { "content_type": "Tartalom t\u00edpus" diff --git a/homeassistant/components/mqtt/translations/it.json b/homeassistant/components/mqtt/translations/it.json index 9b94a95bdc1..33886d83086 100644 --- a/homeassistant/components/mqtt/translations/it.json +++ b/homeassistant/components/mqtt/translations/it.json @@ -70,12 +70,17 @@ "deprecated_yaml": { "description": "{platform} MQTT configurata manualmente trovata sotto la voce della piattaforma `{platform}`. \n\nSposta la configurazione sulla voce di integrazione `mqtt` e riavvia Home Assistant per risolvere questo problema. Consulta la [documentazione]({more_info_url}), per ulteriori informazioni.", "title": "La/e tua/e {platform} MQTT configurata/e manualmente ha/hanno bisogno di attenzione" + }, + "deprecated_yaml_broker_settings": { + "description": "Le seguenti impostazioni trovate in 'configuration.yaml' sono state migrate alla voce di configurazione MQTT e ora sovrascriveranno le impostazioni in 'configuration.yaml':\n'{deprecated_settings}'\n\nRimuovi queste impostazioni da 'configuration.yaml' e riavvia Home Assistant per risolvere il problema. Per ulteriori informazioni, vedere la [documentazione]({more_info_url}).", + "title": "Impostazioni MQTT obsolete trovate in `configuration.yaml`" } }, "options": { "error": { "bad_birth": "Argomento birth non valido.", "bad_certificate": "Certificato CA non valido", + "bad_client_cert": "Certificato cliente non valido, assicurarsi che venga fornito un file con codice PEM", "bad_client_cert_key": "Il certificato del client e il certificato privato non sono accoppiati in modo valido", "bad_client_key": "Chiave privata non valida, assicurarsi che venga fornito un file codificato PEM senza password", "bad_discovery_prefix": "Prefisso di ricerca non valido", @@ -92,6 +97,7 @@ "client_cert": "Carica il file del certificato cliente", "client_id": "ID cliente (lasciare vuoto per generarne uno in modo casuale)", "client_key": "Carica il file della chiave privata", + "keepalive": "Il tempo che intercorre tra l'invio di messaggi di mantenimento comunicazioni", "password": "Password", "port": "Porta", "protocol": "Protocollo MQTT", diff --git a/homeassistant/components/mqtt/translations/ru.json b/homeassistant/components/mqtt/translations/ru.json index f1f241e4c78..0e8ff8255e3 100644 --- a/homeassistant/components/mqtt/translations/ru.json +++ b/homeassistant/components/mqtt/translations/ru.json @@ -29,6 +29,9 @@ "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "port": "\u041f\u043e\u0440\u0442", "protocol": "\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b MQTT", + "set_ca_cert": "\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0431\u0440\u043e\u043a\u0435\u0440\u0430", + "set_client_cert": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430", + "tls_insecure": "\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0431\u0440\u043e\u043a\u0435\u0440\u0430", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a \u0412\u0430\u0448\u0435\u043c\u0443 \u0431\u0440\u043e\u043a\u0435\u0440\u0443 MQTT." diff --git a/homeassistant/components/nibe_heatpump/translations/it.json b/homeassistant/components/nibe_heatpump/translations/it.json index 9de61113160..e7648a9d6fe 100644 --- a/homeassistant/components/nibe_heatpump/translations/it.json +++ b/homeassistant/components/nibe_heatpump/translations/it.json @@ -18,7 +18,14 @@ "listening_port": "Porta di ascolto locale", "remote_read_port": "Porta di lettura remota", "remote_write_port": "Porta di scrittura remota" - } + }, + "data_description": { + "ip_address": "L'indirizzo dell'unit\u00e0 NibeGW. Il dispositivo dovrebbe essere stato configurato con un indirizzo statico.", + "listening_port": "La porta locale su questo sistema a cui l'unit\u00e0 NibeGW \u00e8 configurata per inviare i dati.", + "remote_read_port": "La porta su cui l'unit\u00e0 NibeGW \u00e8 in ascolto per le richieste di lettura.", + "remote_write_port": "La porta su cui l'unit\u00e0 NibeGW \u00e8 in ascolto per le richieste di scrittura." + }, + "description": "Prima di tentare di configurare l'integrazione, verificare che:\n - L'unit\u00e0 NibeGW \u00e8 collegata a una pompa di calore.\n - Nella configurazione della pompa di calore \u00e8 stato abilitato l'accessorio MODBUS40.\n - La pompa non \u00e8 andata in stato di allarme per la mancanza dell'accessorio MODBUS40." } } } diff --git a/homeassistant/components/oralb/translations/nl.json b/homeassistant/components/oralb/translations/nl.json new file mode 100644 index 00000000000..02365f4ef8c --- /dev/null +++ b/homeassistant/components/oralb/translations/nl.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Apparaat is al geconfigureerd", + "already_in_progress": "De configuratie is momenteel al bezig", + "no_devices_found": "Geen apparaten gevonden op het netwerk", + "not_supported": "Apparaat wordt niet ondersteund" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Wilt u {name} instellen?" + }, + "user": { + "data": { + "address": "Apparaat" + }, + "description": "Kies een apparaat om in te stellen" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/hu.json b/homeassistant/components/pushover/translations/hu.json index cdb09291937..aa7624eca0b 100644 --- a/homeassistant/components/pushover/translations/hu.json +++ b/homeassistant/components/pushover/translations/hu.json @@ -29,6 +29,10 @@ "deprecated_yaml": { "description": "A Pushover konfigur\u00e1l\u00e1sa YAML haszn\u00e1lat\u00e1val elt\u00e1vol\u00edt\u00e1sra ker\u00fcl.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3 automatikusan import\u00e1l\u00e1sra ker\u00fclt a felhaszn\u00e1l\u00f3i fel\u00fcletre.\n\nA hiba kijav\u00edt\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a Pushover YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", "title": "A Pushover YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" + }, + "removed_yaml": { + "description": "A Pushover YAML haszn\u00e1lat\u00e1val t\u00f6rt\u00e9n\u0151 konfigur\u00e1l\u00e1sa elt\u00e1vol\u00edt\u00e1sra ker\u00fclt.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3j\u00e1t a Home Assistant nem haszn\u00e1lja.\n\nA hiba kijav\u00edt\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a Pushover YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", + "title": "A Pushover YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fclt" } } } \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/it.json b/homeassistant/components/pushover/translations/it.json index 8eec84415b7..b42f4e62b77 100644 --- a/homeassistant/components/pushover/translations/it.json +++ b/homeassistant/components/pushover/translations/it.json @@ -31,6 +31,7 @@ "title": "La configurazione YAML di Pushover sar\u00e0 rimossa" }, "removed_yaml": { + "description": "La configurazione di Pushover tramite YAML \u00e8 stata rimossa.\n\nLa configurazione YAML esistente non sar\u00e0 utilizzata da Home Assistant.\n\nRimuovere la configurazione YAML di Pushover dal file configuration.yaml e riavviare Home Assistant per risolvere il problema.", "title": "La configurazione Pushover YAML \u00e8 stata rimossa" } } diff --git a/homeassistant/components/rainmachine/translations/de.json b/homeassistant/components/rainmachine/translations/de.json index 51b6f0814af..bff980b82e2 100644 --- a/homeassistant/components/rainmachine/translations/de.json +++ b/homeassistant/components/rainmachine/translations/de.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "Zonenlaufzeiten aus der RainMachine App verwenden", "zone_run_time": "Standard-Zonenlaufzeit (in Sekunden)" }, "title": "RainMachine konfigurieren" diff --git a/homeassistant/components/rainmachine/translations/en.json b/homeassistant/components/rainmachine/translations/en.json index 15155f52d08..f1883a725d2 100644 --- a/homeassistant/components/rainmachine/translations/en.json +++ b/homeassistant/components/rainmachine/translations/en.json @@ -35,11 +35,11 @@ "step": { "init": { "data": { - "zone_run_time": "Default zone run time (in seconds)", - "use_app_run_times": "Use zone run times from RainMachine app" + "use_app_run_times": "Use zone run times from RainMachine app", + "zone_run_time": "Default zone run time (in seconds)" }, "title": "Configure RainMachine" } } } -} +} \ No newline at end of file diff --git a/homeassistant/components/rainmachine/translations/es.json b/homeassistant/components/rainmachine/translations/es.json index a8bcc7cbd0d..dfd0691ad96 100644 --- a/homeassistant/components/rainmachine/translations/es.json +++ b/homeassistant/components/rainmachine/translations/es.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "Utilizar los tiempos de ejecuci\u00f3n de las zonas desde la aplicaci\u00f3n RainMachine", "zone_run_time": "Tiempo de ejecuci\u00f3n de zona predeterminado (en segundos)" }, "title": "Configurar RainMachine" diff --git a/homeassistant/components/rainmachine/translations/et.json b/homeassistant/components/rainmachine/translations/et.json index 0a9d6e007f1..84ec424e716 100644 --- a/homeassistant/components/rainmachine/translations/et.json +++ b/homeassistant/components/rainmachine/translations/et.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "Kasuta tsoonide t\u00f6\u00f6aegu rakendusest RainMachine", "zone_run_time": "Vaiketsooni k\u00e4itamisaeg (sekundites)" }, "title": "Seadista RainMachine" diff --git a/homeassistant/components/rainmachine/translations/hu.json b/homeassistant/components/rainmachine/translations/hu.json index f86c3905108..f458009997c 100644 --- a/homeassistant/components/rainmachine/translations/hu.json +++ b/homeassistant/components/rainmachine/translations/hu.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "Z\u00f3na fut\u00e1si id\u0151k haszn\u00e1lata a RainMachine alkalmaz\u00e1sb\u00f3l", "zone_run_time": "Alap\u00e9rtelmezett z\u00f3nafut\u00e1si id\u0151 (m\u00e1sodpercben)" }, "title": "RainMachine konfigur\u00e1l\u00e1sa" diff --git a/homeassistant/components/rainmachine/translations/it.json b/homeassistant/components/rainmachine/translations/it.json index 9cca839ea00..daef760ca42 100644 --- a/homeassistant/components/rainmachine/translations/it.json +++ b/homeassistant/components/rainmachine/translations/it.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "Usa i tempi di esecuzione delle zone dall'app RainMachine", "zone_run_time": "Tempo di esecuzione della zona di default (in secondi)" }, "title": "Configura RainMachine" diff --git a/homeassistant/components/rainmachine/translations/pl.json b/homeassistant/components/rainmachine/translations/pl.json index e96ccd64ef0..8f81ed34ea8 100644 --- a/homeassistant/components/rainmachine/translations/pl.json +++ b/homeassistant/components/rainmachine/translations/pl.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "U\u017cyj czas\u00f3w dla strefy z aplikacji RainMachine", "zone_run_time": "Domy\u015blny czas dzia\u0142ania dla strefy (w sekundach)" }, "title": "Konfiguracja RainMachine" diff --git a/homeassistant/components/rainmachine/translations/pt-BR.json b/homeassistant/components/rainmachine/translations/pt-BR.json index 7128423202e..a274c77a4ca 100644 --- a/homeassistant/components/rainmachine/translations/pt-BR.json +++ b/homeassistant/components/rainmachine/translations/pt-BR.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "Use os tempos de execu\u00e7\u00e3o da zona do aplicativo RainMachine", "zone_run_time": "Tempo de execu\u00e7\u00e3o da zona padr\u00e3o (em segundos)" }, "title": "Configurar RainMachine" diff --git a/homeassistant/components/sensor/translations/it.json b/homeassistant/components/sensor/translations/it.json index ca319a3437a..edd6b85c4ff 100644 --- a/homeassistant/components/sensor/translations/it.json +++ b/homeassistant/components/sensor/translations/it.json @@ -32,6 +32,7 @@ "is_volatile_organic_compounds": "Attuale livello di concentrazione di composti organici volatili di {entity_name}", "is_voltage": "Tensione attuale di {entity_name}", "is_volume": "Volume attuale di {entity_name}", + "is_water": "Attuale {entity_name} acqua", "is_weight": "Peso attuale di {entity_name}" }, "trigger_type": { @@ -66,6 +67,7 @@ "volatile_organic_compounds": "Variazioni della concentrazione di composti organici volatili di {entity_name}", "voltage": "variazioni di tensione di {entity_name}", "volume": "Variazioni di volume di {entity_name}", + "water": "{entity_name} variazioni d'acqua", "weight": "Variazioni di peso di {entity_name}" } }, diff --git a/homeassistant/components/statistics/translations/hu.json b/homeassistant/components/statistics/translations/hu.json new file mode 100644 index 00000000000..df6ff936d58 --- /dev/null +++ b/homeassistant/components/statistics/translations/hu.json @@ -0,0 +1,12 @@ +{ + "issues": { + "deprecation_warning_characteristic": { + "description": "A statisztikai integr\u00e1ci\u00f3 `state_characteristic` konfigur\u00e1ci\u00f3s param\u00e9tere k\u00f6telez\u0151v\u00e9 v\u00e1lik.\n\nK\u00e9rj\u00fck, a jelenlegi m\u0171k\u00f6d\u00e9s megtart\u00e1s\u00e1hoz adja hozz\u00e1 a `state_characteristic: {characteristic}` param\u00e9tert a `{entity}` \u00e9rz\u00e9kel\u0151 konfigur\u00e1ci\u00f3j\u00e1hoz.\n\nTov\u00e1bbi r\u00e9szletek\u00e9rt olvassa el a statisztikai integr\u00e1ci\u00f3 dokument\u00e1ci\u00f3j\u00e1t: https://www.home-assistant.io/integrations/statistics/", + "title": "K\u00f6telez\u0151 'state_characteristic' felt\u00e9telezve egy statisztikai entit\u00e1sn\u00e1l" + }, + "deprecation_warning_size": { + "description": "A statisztikai integr\u00e1ci\u00f3 `sampling_size` konfigur\u00e1ci\u00f3s param\u00e9tere eddig 20-as \u00e9rt\u00e9kre volt alap\u00e9rtelmezve, ami v\u00e1ltozni fog.\n\nK\u00e9rj\u00fck, ellen\u0151rizze `{entity}` \u00e9rz\u00e9kel\u0151 konfigur\u00e1ci\u00f3j\u00e1t, \u00e9s adjon hozz\u00e1 megfelel\u0151 hat\u00e1rokat, pl. `sampling_size: 20` a jelenlegi m\u0171k\u00f6d\u00e9s megtart\u00e1s\u00e1hoz. A 2022.12.0 verzi\u00f3val a statisztika integr\u00e1ci\u00f3 konfigur\u00e1ci\u00f3ja rugalmasabb\u00e1 v\u00e1lik, \u00e9s elfogadja a `sampling_size` vagy a `max_age`, vagy mindk\u00e9t be\u00e1ll\u00edt\u00e1st. Ezzel k\u00f6vetelm\u00e9nyyel felk\u00e9sz\u00edtj\u00fck a konfigur\u00e1ci\u00f3t erre a k\u00f6telez\u0151 v\u00e1ltoz\u00e1sra.\n\nTov\u00e1bbi r\u00e9szletek\u00e9rt olvassa el a statisztikai integr\u00e1ci\u00f3 dokument\u00e1ci\u00f3j\u00e1t: https://www.home-assistant.io/integrations/statistics/", + "title": "Implicit 'sampling_size' felt\u00e9telezve egy Statisztikai entit\u00e1shoz" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/statistics/translations/it.json b/homeassistant/components/statistics/translations/it.json new file mode 100644 index 00000000000..f3f6ecfac53 --- /dev/null +++ b/homeassistant/components/statistics/translations/it.json @@ -0,0 +1,12 @@ +{ + "issues": { + "deprecation_warning_characteristic": { + "description": "Il parametro di configurazione `state_characteristic` dell'integrazione delle statistiche diventer\u00e0 obbligatorio. \n\nSi prega di aggiungere `state_characteristic: {characteristic}` alla configurazione del sensore `{entity}` per mantenere il comportamento corrente. \n\nLeggi la documentazione dell'integrazione delle statistiche per ulteriori dettagli: https://www.home-assistant.io/integrations/statistics/", + "title": "\"state_characteristic\" obbligatoria da assumere per un'entit\u00e0 statistica" + }, + "deprecation_warning_size": { + "description": "Il parametro di configurazione `sampling_size` dell'integrazione delle statistiche \u00e8 stato impostato finora sul valore 20, il quale cambier\u00e0. \n\nSi prega di controllare la configurazione per il sensore `{entity}` e aggiungere i limiti adatti, ad esempio `sampling_size: 20`, per mantenere il comportamento corrente. La configurazione dell'integrazione delle statistiche diventer\u00e0 pi\u00f9 flessibile con la versione 2022.12.0 e accetter\u00e0 `sampling_size` o `max_age` o entrambe le impostazioni. La richiesta di cui sopra prepara la tua configurazione per questa modifica che potrebbe altrimenti causare un'interruzione. \n\nLeggi la documentazione dell'integrazione delle statistiche per ulteriori dettagli: https://www.home-assistant.io/integrations/statistics/", + "title": "\"sampling_size\" implicito assunto per un'entit\u00e0 statistica" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/bg.json b/homeassistant/components/zamg/translations/bg.json new file mode 100644 index 00000000000..c6e5cbe9ec1 --- /dev/null +++ b/homeassistant/components/zamg/translations/bg.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, + "flow_title": "{name}" + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/et.json b/homeassistant/components/zamg/translations/et.json new file mode 100644 index 00000000000..b3e0006f63b --- /dev/null +++ b/homeassistant/components/zamg/translations/et.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Seade on juba h\u00e4\u00e4lestatud", + "cannot_connect": "\u00dchendamine nurjus" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "Jaama ID (vaikimisi l\u00e4him jaam)" + }, + "description": "Seadista ZAMG integreerimiseks koduassistendiga." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "ZAMG konfigureerimine YAML-i abil eemaldatakse. \n\n Teie olemasolev YAML-i konfiguratsioon imporditi kasutajaliidesesse automaatselt. \n\n Eemaldage ZAMG YAML-i konfiguratsioon failist configuration.yaml ja taask\u00e4ivitage selle probleemi lahendamiseks Home Assistant.", + "title": "ZAMG YAML-i konfiguratsioon eemaldatakse" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/hu.json b/homeassistant/components/zamg/translations/hu.json new file mode 100644 index 00000000000..339739ea266 --- /dev/null +++ b/homeassistant/components/zamg/translations/hu.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", + "cannot_connect": "Sikertelen csatlakoz\u00e1s" + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "\u00c1llom\u00e1s azonos\u00edt\u00f3ja (alap\u00e9rtelmez\u00e9s szerint a legk\u00f6zelebbi \u00e1llom\u00e1s)" + }, + "description": "ZAMG \u00e9s Home Assistant integr\u00e1ci\u00f3ja." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "A ZAMG YAML haszn\u00e1lat\u00e1val t\u00f6rt\u00e9n\u0151 konfigur\u00e1l\u00e1sa elt\u00e1vol\u00edt\u00e1sra ker\u00fcl.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3 automatikusan import\u00e1l\u00e1sra ker\u00fclt a felhaszn\u00e1l\u00f3i fel\u00fcletre.\n\nA probl\u00e9ma megold\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a ZAMG YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", + "title": "A ZAMG YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/it.json b/homeassistant/components/zamg/translations/it.json new file mode 100644 index 00000000000..8ec3f4a93bc --- /dev/null +++ b/homeassistant/components/zamg/translations/it.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", + "cannot_connect": "Impossibile connettersi" + }, + "error": { + "cannot_connect": "Impossibile connettersi" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "ID stazione (predefinito alla stazione pi\u00f9 vicina)" + }, + "description": "Configura ZAMG per l'integrazione con Home Assistant." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "La configurazione di ZAMG tramite YAML \u00e8 stata rimossa. \n\nLa tua configurazione YAML esistente \u00e8 stata importata automaticamente nell'interfaccia utente. \n\nRimuovere la configurazione YAML di ZAMG dal file configuration.yaml e riavviare Home Assistant per risolvere questo problema.", + "title": "La configurazione YAML di ZAMG \u00e8 stata rimossa" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/nl.json b/homeassistant/components/zamg/translations/nl.json new file mode 100644 index 00000000000..f6a0344cbc4 --- /dev/null +++ b/homeassistant/components/zamg/translations/nl.json @@ -0,0 +1,11 @@ +{ + "config": { + "abort": { + "already_configured": "Apparaat is al geconfigureerd", + "cannot_connect": "Kan geen verbinding maken" + }, + "error": { + "cannot_connect": "Kan geen verbinding maken" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/pt-BR.json b/homeassistant/components/zamg/translations/pt-BR.json new file mode 100644 index 00000000000..a57030ce56d --- /dev/null +++ b/homeassistant/components/zamg/translations/pt-BR.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "cannot_connect": "Falhou ao conectar" + }, + "error": { + "cannot_connect": "Falhou ao conectar" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "ID da esta\u00e7\u00e3o (Padr\u00e3o para a esta\u00e7\u00e3o mais pr\u00f3xima)" + }, + "description": "Configure o ZAMG para integrar com o Home Assistant." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "A configura\u00e7\u00e3o do ZAMG usando YAML est\u00e1 sendo removida. \n\n Sua configura\u00e7\u00e3o YAML existente foi importada para a interface do usu\u00e1rio automaticamente. \n\n Remova a configura\u00e7\u00e3o ZAMG YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", + "title": "A configura\u00e7\u00e3o YAML de ZAMG est\u00e1 sendo removida" + } + } +} \ No newline at end of file From 5eb3473d4191ffd04eeec3ef7f63c0adaea4afb3 Mon Sep 17 00:00:00 2001 From: Glenn Waters Date: Fri, 28 Oct 2022 00:38:11 -0400 Subject: [PATCH 0032/1033] Add diagnostics to Environment Canada integration (#79517) * Add diagnostics to EC integration. * Add test for diagnostics. * Simplify diagnostics returned. * Fix for comment. * Update datetime to fixed date. --- .../environment_canada/diagnostics.py | 26 ++ .../fixtures/config_entry_data.json | 110 ++++++++ .../fixtures/current_conditions_data.json | 235 ++++++++++++++++++ .../environment_canada/test_diagnostics.py | 85 +++++++ 4 files changed, 456 insertions(+) create mode 100644 homeassistant/components/environment_canada/diagnostics.py create mode 100644 tests/components/environment_canada/fixtures/config_entry_data.json create mode 100644 tests/components/environment_canada/fixtures/current_conditions_data.json create mode 100644 tests/components/environment_canada/test_diagnostics.py diff --git a/homeassistant/components/environment_canada/diagnostics.py b/homeassistant/components/environment_canada/diagnostics.py new file mode 100644 index 00000000000..f1064cea52e --- /dev/null +++ b/homeassistant/components/environment_canada/diagnostics.py @@ -0,0 +1,26 @@ +"""Diagnostics support for Environment Canada.""" +from __future__ import annotations + +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE +from homeassistant.core import HomeAssistant + +from .const import DOMAIN + +TO_REDACT = {CONF_LATITUDE, CONF_LONGITUDE} + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, config_entry: ConfigEntry +) -> dict: + """Return diagnostics for a config entry.""" + coordinators = hass.data[DOMAIN][config_entry.entry_id] + weather_coord = coordinators["weather_coordinator"] + + diagnostics_data = { + "config_entry_data": async_redact_data(dict(config_entry.data), TO_REDACT), + "weather_data": dict(weather_coord.ec_data.conditions), + } + + return diagnostics_data diff --git a/tests/components/environment_canada/fixtures/config_entry_data.json b/tests/components/environment_canada/fixtures/config_entry_data.json new file mode 100644 index 00000000000..085a3394dce --- /dev/null +++ b/tests/components/environment_canada/fixtures/config_entry_data.json @@ -0,0 +1,110 @@ +{ + "config_entry_data": { + "latitude": "**REDACTED**", + "longitude": "**REDACTED**", + "station": "XX/1234567", + "language": "Gibberish" + }, + "weather_data": { + "temperature": { + "label": "Temperature", + "value": 14.9, + "unit": "C" + }, + "dewpoint": { + "label": "Dew Point", + "value": 1.4, + "unit": "C" + }, + "wind_chill": { + "label": "Wind Chill", + "value": null + }, + "humidex": { + "label": "Humidex", + "value": null + }, + "pressure": { + "label": "Pressure", + "value": 102.7, + "unit": "kPa" + }, + "tendency": { + "label": "Tendency", + "value": "falling" + }, + "humidity": { + "label": "Humidity", + "value": 40, + "unit": "%" + }, + "visibility": { + "label": "Visibility", + "value": 24.1, + "unit": "km" + }, + "condition": { + "label": "Condition", + "value": "Mainly Sunny" + }, + "wind_speed": { + "label": "Wind Speed", + "value": 1, + "unit": "km/h" + }, + "wind_gust": { + "label": "Wind Gust", + "value": null + }, + "wind_dir": { + "label": "Wind Direction", + "value": "N" + }, + "wind_bearing": { + "label": "Wind Bearing", + "value": 0, + "unit": "degrees" + }, + "high_temp": { + "label": "High Temperature", + "value": 18, + "unit": "C" + }, + "low_temp": { + "label": "Low Temperature", + "value": -1, + "unit": "C" + }, + "uv_index": { + "label": "UV Index", + "value": 5 + }, + "pop": { + "label": "Chance of Precip.", + "value": null + }, + "icon_code": { + "label": "Icon Code", + "value": "01" + }, + "precip_yesterday": { + "label": "Precipitation Yesterday", + "value": 0.0, + "unit": "mm" + }, + "normal_high": { + "label": "Normal High Temperature", + "value": 15, + "unit": "C" + }, + "normal_low": { + "label": "Normal Low Temperature", + "value": 6, + "unit": "C" + }, + "text_summary": { + "label": "Forecast", + "value": "Tonight. Clear. Fog patches developing after midnight. Low minus 1 with frost." + } + } +} diff --git a/tests/components/environment_canada/fixtures/current_conditions_data.json b/tests/components/environment_canada/fixtures/current_conditions_data.json new file mode 100644 index 00000000000..f3a18869940 --- /dev/null +++ b/tests/components/environment_canada/fixtures/current_conditions_data.json @@ -0,0 +1,235 @@ +{ + "alerts": { + "warnings": { + "value": [], + "label": "Warnings" + }, + "watches": { + "value": [], + "label": "Watches" + }, + "advisories": { + "value": [ + { + "title": "Frost Advisory", + "date": "Monday October 03, 2022 at 15:05 EDT" + } + ], + "label": "Advisories" + }, + "statements": { + "value": [], + "label": "Statements" + }, + "endings": { + "value": [], + "label": "Endings" + } + }, + "conditions": { + "temperature": { + "label": "Temperature", + "value": 14.9, + "unit": "C" + }, + "dewpoint": { + "label": "Dew Point", + "value": 1.4, + "unit": "C" + }, + "wind_chill": { + "label": "Wind Chill", + "value": null + }, + "humidex": { + "label": "Humidex", + "value": null + }, + "pressure": { + "label": "Pressure", + "value": 102.7, + "unit": "kPa" + }, + "tendency": { + "label": "Tendency", + "value": "falling" + }, + "humidity": { + "label": "Humidity", + "value": 40, + "unit": "%" + }, + "visibility": { + "label": "Visibility", + "value": 24.1, + "unit": "km" + }, + "condition": { + "label": "Condition", + "value": "Mainly Sunny" + }, + "wind_speed": { + "label": "Wind Speed", + "value": 1, + "unit": "km/h" + }, + "wind_gust": { + "label": "Wind Gust", + "value": null + }, + "wind_dir": { + "label": "Wind Direction", + "value": "N" + }, + "wind_bearing": { + "label": "Wind Bearing", + "value": 0, + "unit": "degrees" + }, + "high_temp": { + "label": "High Temperature", + "value": 18, + "unit": "C" + }, + "low_temp": { + "label": "Low Temperature", + "value": -1, + "unit": "C" + }, + "uv_index": { + "label": "UV Index", + "value": 5 + }, + "pop": { + "label": "Chance of Precip.", + "value": null + }, + "icon_code": { + "label": "Icon Code", + "value": "01" + }, + "precip_yesterday": { + "label": "Precipitation Yesterday", + "value": 0.0, + "unit": "mm" + }, + "normal_high": { + "label": "Normal High Temperature", + "value": 15, + "unit": "C" + }, + "normal_low": { + "label": "Normal Low Temperature", + "value": 6, + "unit": "C" + }, + "text_summary": { + "label": "Forecast", + "value": "Tonight. Clear. Fog patches developing after midnight. Low minus 1 with frost." + } + }, + "daily_forecasts": [ + { + "period": "Monday night", + "text_summary": "Clear. Fog patches developing after midnight. Low minus 1 with frost.", + "icon_code": "30", + "temperature": -1, + "temperature_class": "low", + "precip_probability": 0 + }, + { + "period": "Tuesday", + "text_summary": "Sunny. Fog patches dissipating in the morning. High 18. UV index 5 or moderate.", + "icon_code": "00", + "temperature": 18, + "temperature_class": "high", + "precip_probability": 0 + }, + { + "period": "Tuesday night", + "text_summary": "Clear. Fog patches developing overnight. Low plus 3.", + "icon_code": "30", + "temperature": 3, + "temperature_class": "low", + "precip_probability": 0 + }, + { + "period": "Wednesday", + "text_summary": "Sunny. High 20.", + "icon_code": "00", + "temperature": 20, + "temperature_class": "high", + "precip_probability": 0 + }, + { + "period": "Wednesday night", + "text_summary": "Clear. Low 9.", + "icon_code": "30", + "temperature": 9, + "temperature_class": "low", + "precip_probability": 0 + }, + { + "period": "Thursday", + "text_summary": "A mix of sun and cloud. High 20.", + "icon_code": "02", + "temperature": 20, + "temperature_class": "high", + "precip_probability": 0 + }, + { + "period": "Thursday night", + "text_summary": "Showers. Low 7.", + "icon_code": "12", + "temperature": 7, + "temperature_class": "low", + "precip_probability": 0 + }, + { + "period": "Friday", + "text_summary": "Cloudy with 40 percent chance of showers. High 13.", + "icon_code": "12", + "temperature": 13, + "temperature_class": "high", + "precip_probability": 40 + }, + { + "period": "Friday night", + "text_summary": "Cloudy periods. Low plus 1.", + "icon_code": "32", + "temperature": 1, + "temperature_class": "low", + "precip_probability": 0 + }, + { + "period": "Saturday", + "text_summary": "A mix of sun and cloud. High 10.", + "icon_code": "02", + "temperature": 10, + "temperature_class": "high", + "precip_probability": 0 + }, + { + "period": "Saturday night", + "text_summary": "Cloudy periods. Low plus 3.", + "icon_code": "32", + "temperature": 3, + "temperature_class": "low", + "precip_probability": 0 + }, + { + "period": "Sunday", + "text_summary": "A mix of sun and cloud. High 12.", + "icon_code": "02", + "temperature": 12, + "temperature_class": "high", + "precip_probability": 0 + } + ], + "metadata": { + "attribution": "Data provided by Environment Canada", + "timestamp": "2022/10/3", + "location": "Ottawa (Kanata - Orl\u00e9ans)", + "station": "Ottawa Macdonald-Cartier Intl Airport" + } +} diff --git a/tests/components/environment_canada/test_diagnostics.py b/tests/components/environment_canada/test_diagnostics.py new file mode 100644 index 00000000000..a1f3539a5e4 --- /dev/null +++ b/tests/components/environment_canada/test_diagnostics.py @@ -0,0 +1,85 @@ +"""Test Environment Canada diagnostics.""" + +from datetime import datetime, timezone +import json +from unittest.mock import AsyncMock, MagicMock, patch + +from homeassistant.components.environment_canada.const import ( + CONF_LANGUAGE, + CONF_STATION, + DOMAIN, +) +from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry, load_fixture +from tests.components.diagnostics import get_diagnostics_for_config_entry + +FIXTURE_USER_INPUT = { + CONF_LATITUDE: 55.55, + CONF_LONGITUDE: 42.42, + CONF_STATION: "XX/1234567", + CONF_LANGUAGE: "Gibberish", +} + + +async def init_integration(hass: HomeAssistant) -> MockConfigEntry: + """Set up the Environment Canada integration in Home Assistant.""" + + def mock_ec(): + ec_mock = MagicMock() + ec_mock.station_id = FIXTURE_USER_INPUT[CONF_STATION] + ec_mock.lat = FIXTURE_USER_INPUT[CONF_LATITUDE] + ec_mock.lon = FIXTURE_USER_INPUT[CONF_LONGITUDE] + ec_mock.language = FIXTURE_USER_INPUT[CONF_LANGUAGE] + ec_mock.update = AsyncMock() + return ec_mock + + config_entry = MockConfigEntry(domain=DOMAIN, data=FIXTURE_USER_INPUT) + config_entry.add_to_hass(hass) + + ec_data = json.loads( + load_fixture("environment_canada/current_conditions_data.json") + ) + + weather_mock = mock_ec() + ec_data["metadata"]["timestamp"] = datetime(2022, 10, 4, tzinfo=timezone.utc) + weather_mock.conditions = ec_data["conditions"] + weather_mock.alerts = ec_data["alerts"] + weather_mock.daily_forecasts = ec_data["daily_forecasts"] + weather_mock.metadata = ec_data["metadata"] + + radar_mock = mock_ec() + radar_mock.image = b"GIF..." + radar_mock.timestamp = datetime(2022, 10, 4, tzinfo=timezone.utc) + + with patch( + "homeassistant.components.environment_canada.ECWeather", + return_value=weather_mock, + ), patch( + "homeassistant.components.environment_canada.ECAirQuality", + return_value=mock_ec(), + ), patch( + "homeassistant.components.environment_canada.ECRadar", return_value=radar_mock + ), patch( + "homeassistant.components.environment_canada.config_flow.ECWeather", + return_value=weather_mock, + ): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + return config_entry + + +async def test_entry_diagnostics(hass, hass_client): + """Test config entry diagnostics.""" + + config_entry = await init_integration(hass) + diagnostics = await get_diagnostics_for_config_entry( + hass, hass_client, config_entry + ) + redacted_entry = json.loads( + load_fixture("environment_canada/config_entry_data.json") + ) + + assert diagnostics == redacted_entry From f10f74a65a14582b5c3229ee23f7fda25dc01243 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 28 Oct 2022 10:49:01 +0200 Subject: [PATCH 0033/1033] Add default `source_type` for MQTT device_tracker (#81128) Make sure the source_type is set - default to gps --- .../mqtt/device_tracker/schema_discovery.py | 15 +++++++++++---- .../mqtt/test_device_tracker_discovery.py | 8 ++++++-- tests/components/mqtt/test_diagnostics.py | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/mqtt/device_tracker/schema_discovery.py b/homeassistant/components/mqtt/device_tracker/schema_discovery.py index 1d3f9d109f6..673a9cb04b7 100644 --- a/homeassistant/components/mqtt/device_tracker/schema_discovery.py +++ b/homeassistant/components/mqtt/device_tracker/schema_discovery.py @@ -7,7 +7,10 @@ import voluptuous as vol from homeassistant.components import device_tracker from homeassistant.components.device_tracker import SOURCE_TYPES -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker.config_entry import ( + SourceType, + TrackerEntity, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_GPS_ACCURACY, @@ -35,12 +38,16 @@ CONF_PAYLOAD_HOME = "payload_home" CONF_PAYLOAD_NOT_HOME = "payload_not_home" CONF_SOURCE_TYPE = "source_type" +DEFAULT_SOURCE_TYPE = SourceType.GPS + PLATFORM_SCHEMA_MODERN = MQTT_RO_SCHEMA.extend( { vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_PAYLOAD_HOME, default=STATE_HOME): cv.string, vol.Optional(CONF_PAYLOAD_NOT_HOME, default=STATE_NOT_HOME): cv.string, - vol.Optional(CONF_SOURCE_TYPE): vol.In(SOURCE_TYPES), + vol.Optional(CONF_SOURCE_TYPE, default=DEFAULT_SOURCE_TYPE): vol.In( + SOURCE_TYPES + ), } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) @@ -161,6 +168,6 @@ class MqttDeviceTracker(MqttEntity, TrackerEntity): return self._location_name @property - def source_type(self): + def source_type(self) -> SourceType | str: """Return the source type, eg gps or router, of the device.""" - return self._config.get(CONF_SOURCE_TYPE) + return self._config[CONF_SOURCE_TYPE] diff --git a/tests/components/mqtt/test_device_tracker_discovery.py b/tests/components/mqtt/test_device_tracker_discovery.py index a39b9b696c5..6324afeb3ef 100644 --- a/tests/components/mqtt/test_device_tracker_discovery.py +++ b/tests/components/mqtt/test_device_tracker_discovery.py @@ -356,11 +356,12 @@ async def test_setting_device_tracker_location_via_mqtt_message( async_fire_mqtt_message( hass, "homeassistant/device_tracker/bla/config", - '{ "name": "test", "state_topic": "test-topic" }', + '{ "name": "test", "state_topic": "test-topic", "source_type": "router" }', ) await hass.async_block_till_done() state = hass.states.get("device_tracker.test") + assert state.attributes["source_type"] == "router" assert state.state == STATE_UNKNOWN @@ -386,6 +387,7 @@ async def test_setting_device_tracker_location_via_lat_lon_message( await hass.async_block_till_done() state = hass.states.get("device_tracker.test") + assert state.attributes["source_type"] == "gps" assert state.state == STATE_UNKNOWN @@ -395,12 +397,14 @@ async def test_setting_device_tracker_location_via_lat_lon_message( async_fire_mqtt_message( hass, "attributes-topic", - '{"latitude":32.87336,"longitude": -117.22743, "gps_accuracy":1.5}', + '{"latitude":32.87336,"longitude": -117.22743, "gps_accuracy":1.5, "source_type": "router"}', ) state = hass.states.get("device_tracker.test") assert state.attributes["latitude"] == 32.87336 assert state.attributes["longitude"] == -117.22743 assert state.attributes["gps_accuracy"] == 1.5 + # assert source_type is overridden by discovery + assert state.attributes["source_type"] == "router" assert state.state == STATE_HOME async_fire_mqtt_message( diff --git a/tests/components/mqtt/test_diagnostics.py b/tests/components/mqtt/test_diagnostics.py index 7c486f9af74..49d3fe0b4b6 100644 --- a/tests/components/mqtt/test_diagnostics.py +++ b/tests/components/mqtt/test_diagnostics.py @@ -248,7 +248,7 @@ async def test_redact_diagnostics( "gps_accuracy": 1.5, "latitude": "**REDACTED**", "longitude": "**REDACTED**", - "source_type": None, + "source_type": "gps", }, "entity_id": "device_tracker.mqtt_unique", "last_changed": ANY, From 30fe4826ff9e564ca4839f61e7ddedfef7b1b9c7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 28 Oct 2022 11:55:53 +0200 Subject: [PATCH 0034/1033] Refactor Scrape sensor to inherit TemplateSensor (#81130) --- homeassistant/components/scrape/sensor.py | 46 ++++++++++++----------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index 6da1cfea9f5..fe6837c0db5 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -13,7 +13,6 @@ from homeassistant.components.sensor import ( DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA, STATE_CLASSES_SCHEMA, - SensorEntity, ) from homeassistant.const import ( CONF_ATTRIBUTE, @@ -36,6 +35,10 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.template import Template +from homeassistant.helpers.template_entity import ( + TEMPLATE_SENSOR_BASE_SCHEMA, + TemplateSensor, +) from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -87,14 +90,16 @@ async def async_setup_platform( if coordinator.data is None: raise PlatformNotReady + sensor_config = vol.Schema( + TEMPLATE_SENSOR_BASE_SCHEMA.schema, extra=vol.REMOVE_EXTRA + )(config) + name: str = config[CONF_NAME] + unique_id: str | None = config.get(CONF_UNIQUE_ID) + select: str | None = config.get(CONF_SELECT) attr: str | None = config.get(CONF_ATTRIBUTE) index: int = config[CONF_INDEX] - unit: str | None = config.get(CONF_UNIT_OF_MEASUREMENT) - device_class: str | None = config.get(CONF_DEVICE_CLASS) - state_class: str | None = config.get(CONF_STATE_CLASS) - unique_id: str | None = config.get(CONF_UNIQUE_ID) value_template: Template | None = config.get(CONF_VALUE_TEMPLATE) if value_template is not None: @@ -103,49 +108,48 @@ async def async_setup_platform( async_add_entities( [ ScrapeSensor( + hass, coordinator, - unique_id, + sensor_config, name, + unique_id, select, attr, index, value_template, - unit, - device_class, - state_class, ) ], ) -class ScrapeSensor(CoordinatorEntity[ScrapeCoordinator], SensorEntity): +class ScrapeSensor(CoordinatorEntity[ScrapeCoordinator], TemplateSensor): """Representation of a web scrape sensor.""" def __init__( self, + hass: HomeAssistant, coordinator: ScrapeCoordinator, - unique_id: str | None, + config: ConfigType, name: str, + unique_id: str | None, select: str | None, attr: str | None, index: int, value_template: Template | None, - unit: str | None, - device_class: str | None, - state_class: str | None, ) -> None: """Initialize a web scrape sensor.""" - super().__init__(coordinator) - self._attr_native_value = None + CoordinatorEntity.__init__(self, coordinator) + TemplateSensor.__init__( + self, + hass, + config=config, + fallback_name=name, + unique_id=unique_id, + ) self._select = select self._attr = attr self._index = index self._value_template = value_template - self._attr_name = name - self._attr_unique_id = unique_id - self._attr_native_unit_of_measurement = unit - self._attr_device_class = device_class - self._attr_state_class = state_class def _extract_value(self) -> Any: """Parse the html extraction in the executor.""" From 16ac063b714869bd70522c559ff135dc78f2e683 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 28 Oct 2022 05:31:50 -0500 Subject: [PATCH 0035/1033] Bump oralb-ble to 0.8.0 (#81123) --- homeassistant/components/oralb/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/oralb/manifest.json b/homeassistant/components/oralb/manifest.json index bf6879733f5..e25f407add1 100644 --- a/homeassistant/components/oralb/manifest.json +++ b/homeassistant/components/oralb/manifest.json @@ -8,7 +8,7 @@ "manufacturer_id": 220 } ], - "requirements": ["oralb-ble==0.6.0"], + "requirements": ["oralb-ble==0.8.0"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index b10a8f07be5..efa1e3ec1f1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1238,7 +1238,7 @@ openwrt-luci-rpc==1.1.11 openwrt-ubus-rpc==0.0.2 # homeassistant.components.oralb -oralb-ble==0.6.0 +oralb-ble==0.8.0 # homeassistant.components.oru oru==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ca4268d549f..ddba8c37f41 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -883,7 +883,7 @@ open-meteo==0.2.1 openerz-api==0.1.0 # homeassistant.components.oralb -oralb-ble==0.6.0 +oralb-ble==0.8.0 # homeassistant.components.ovo_energy ovoenergy==1.2.0 From 67644f4bc513f4c0de0eb52f8a777fa37712c629 Mon Sep 17 00:00:00 2001 From: Dave T <17680170+davet2001@users.noreply.github.com> Date: Fri, 28 Oct 2022 11:35:09 +0100 Subject: [PATCH 0036/1033] Remove unused strings from generic camera (#80796) Co-authored-by: Dave T --- homeassistant/components/generic/strings.json | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/homeassistant/components/generic/strings.json b/homeassistant/components/generic/strings.json index 7c7e44a67e4..5fddd2d78fe 100644 --- a/homeassistant/components/generic/strings.json +++ b/homeassistant/components/generic/strings.json @@ -8,8 +8,6 @@ "invalid_still_image": "URL did not return a valid still image", "malformed_url": "Malformed URL", "relative_url": "Relative URLs are not allowed", - "stream_file_not_found": "File not found while trying to connect to stream (is ffmpeg installed?)", - "stream_http_not_found": "HTTP 404 Not found while trying to connect to stream", "template_error": "Error rendering template. Review log for more info.", "timeout": "Timeout while loading URL", "stream_no_route_to_host": "Could not find host while trying to connect to stream", @@ -34,12 +32,6 @@ "verify_ssl": "[%key:common::config_flow::data::verify_ssl%]" } }, - "content_type": { - "description": "Specify the content type for the stream.", - "data": { - "content_type": "Content Type" - } - }, "user_confirm_still": { "title": "Preview", "description": "![Camera Still Image Preview]({preview_url})", @@ -68,12 +60,6 @@ "use_wallclock_as_timestamps": "This option may correct segmenting or crashing issues arising from buggy timestamp implementations on some cameras" } }, - "content_type": { - "description": "[%key:component::generic::config::step::content_type::description%]", - "data": { - "content_type": "[%key:component::generic::config::step::content_type::data::content_type%]" - } - }, "confirm_still": { "title": "[%key:component::generic::config::step::user_confirm_still::title%]", "description": "[%key:component::generic::config::step::user_confirm_still::description%]", From 2bce44c4d71a64eb8aaba3db04f4bb10a48ae84a Mon Sep 17 00:00:00 2001 From: Thibaut Date: Fri, 28 Oct 2022 13:02:33 +0200 Subject: [PATCH 0037/1033] Bump pyoverkiz to 1.5.6 (#81129) --- homeassistant/components/overkiz/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/overkiz/manifest.json b/homeassistant/components/overkiz/manifest.json index f09142c86f0..d19495d82a2 100644 --- a/homeassistant/components/overkiz/manifest.json +++ b/homeassistant/components/overkiz/manifest.json @@ -3,7 +3,7 @@ "name": "Overkiz", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/overkiz", - "requirements": ["pyoverkiz==1.5.5"], + "requirements": ["pyoverkiz==1.5.6"], "zeroconf": [ { "type": "_kizbox._tcp.local.", diff --git a/requirements_all.txt b/requirements_all.txt index efa1e3ec1f1..f5a71d35219 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1789,7 +1789,7 @@ pyotgw==2.1.1 pyotp==2.7.0 # homeassistant.components.overkiz -pyoverkiz==1.5.5 +pyoverkiz==1.5.6 # homeassistant.components.openweathermap pyowm==3.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ddba8c37f41..2025350ee0f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1266,7 +1266,7 @@ pyotgw==2.1.1 pyotp==2.7.0 # homeassistant.components.overkiz -pyoverkiz==1.5.5 +pyoverkiz==1.5.6 # homeassistant.components.openweathermap pyowm==3.2.0 From a34148104acd3210932644fd33350f32f61d0789 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Fri, 28 Oct 2022 15:48:16 +0300 Subject: [PATCH 0038/1033] Add diagnostics to webostv (#81133) --- .../components/webostv/diagnostics.py | 52 ++++++++++++++++ tests/components/webostv/conftest.py | 2 + tests/components/webostv/test_diagnostics.py | 61 +++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 homeassistant/components/webostv/diagnostics.py create mode 100644 tests/components/webostv/test_diagnostics.py diff --git a/homeassistant/components/webostv/diagnostics.py b/homeassistant/components/webostv/diagnostics.py new file mode 100644 index 00000000000..ce62f51b540 --- /dev/null +++ b/homeassistant/components/webostv/diagnostics.py @@ -0,0 +1,52 @@ +"""Diagnostics support for LG webOS Smart TV.""" +from __future__ import annotations + +from typing import Any + +from aiowebostv import WebOsClient + +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_CLIENT_SECRET, CONF_HOST, CONF_UNIQUE_ID +from homeassistant.core import HomeAssistant + +from .const import DATA_CONFIG_ENTRY, DOMAIN + +TO_REDACT = { + CONF_CLIENT_SECRET, + CONF_UNIQUE_ID, + CONF_HOST, + "device_id", + "deviceUUID", + "icon", + "largeIcon", +} + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + client: WebOsClient = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id].client + + client_data = { + "is_registered": client.is_registered(), + "is_connected": client.is_connected(), + "current_app_id": client.current_app_id, + "current_channel": client.current_channel, + "apps": client.apps, + "inputs": client.inputs, + "system_info": client.system_info, + "software_info": client.software_info, + "hello_info": client.hello_info, + "sound_output": client.sound_output, + "is_on": client.is_on, + } + + return async_redact_data( + { + "entry": entry.as_dict(), + "client": client_data, + }, + TO_REDACT, + ) diff --git a/tests/components/webostv/conftest.py b/tests/components/webostv/conftest.py index 05f1be66d00..c8333c84447 100644 --- a/tests/components/webostv/conftest.py +++ b/tests/components/webostv/conftest.py @@ -39,6 +39,8 @@ def client_fixture(): client.sound_output = "speaker" client.muted = False client.is_on = True + client.is_registered = Mock(return_value=True) + client.is_connected = Mock(return_value=True) async def mock_state_update_callback(): await client.register_state_update_callback.call_args[0][0](client) diff --git a/tests/components/webostv/test_diagnostics.py b/tests/components/webostv/test_diagnostics.py new file mode 100644 index 00000000000..707f83b2fcf --- /dev/null +++ b/tests/components/webostv/test_diagnostics.py @@ -0,0 +1,61 @@ +"""Tests for the diagnostics data provided by LG webOS Smart TV.""" +from aiohttp import ClientSession + +from homeassistant.components.diagnostics import REDACTED +from homeassistant.core import HomeAssistant + +from . import setup_webostv + +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_diagnostics( + hass: HomeAssistant, hass_client: ClientSession, client +) -> None: + """Test diagnostics.""" + entry = await setup_webostv(hass) + assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == { + "client": { + "is_registered": True, + "is_connected": True, + "current_app_id": "com.webos.app.livetv", + "current_channel": { + "channelId": "ch1id", + "channelName": "Channel 1", + "channelNumber": "1", + }, + "apps": { + "com.webos.app.livetv": { + "icon": REDACTED, + "id": "com.webos.app.livetv", + "largeIcon": REDACTED, + "title": "Live TV", + } + }, + "inputs": { + "in1": {"appId": "app0", "id": "in1", "label": "Input01"}, + "in2": {"appId": "app1", "id": "in2", "label": "Input02"}, + }, + "system_info": {"modelName": "TVFAKE"}, + "software_info": {"major_ver": "major", "minor_ver": "minor"}, + "hello_info": {"deviceUUID": "**REDACTED**"}, + "sound_output": "speaker", + "is_on": True, + }, + "entry": { + "entry_id": entry.entry_id, + "version": 1, + "domain": "webostv", + "title": "fake_webos", + "data": { + "client_secret": "**REDACTED**", + "host": "**REDACTED**", + }, + "options": {}, + "pref_disable_new_entities": False, + "pref_disable_polling": False, + "source": "user", + "unique_id": REDACTED, + "disabled_by": None, + }, + } From b39ff4a0207d96e4dfb2fc48862b1f7ce9f1671f Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 28 Oct 2022 15:21:06 +0200 Subject: [PATCH 0039/1033] Expose device_tracker base entities (#81135) * Expose TrackerEntity in device_tracker * Also expose BaseTrackerEntity * Add ScannerEntity * Revert BaseTrackerEntity --- .../components/bmw_connected_drive/device_tracker.py | 3 +-- homeassistant/components/device_tracker/__init__.py | 7 ++++++- homeassistant/components/freebox/device_tracker.py | 3 +-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/bmw_connected_drive/device_tracker.py b/homeassistant/components/bmw_connected_drive/device_tracker.py index d26a63f8c0e..fc33335fe24 100644 --- a/homeassistant/components/bmw_connected_drive/device_tracker.py +++ b/homeassistant/components/bmw_connected_drive/device_tracker.py @@ -6,8 +6,7 @@ from typing import Any from bimmer_connected.vehicle import MyBMWVehicle -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import SourceType, TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 2d3353a1110..a6a8e9d2d8c 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -6,7 +6,12 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass -from .config_entry import async_setup_entry, async_unload_entry # noqa: F401 +from .config_entry import ( # noqa: F401 + ScannerEntity, + TrackerEntity, + async_setup_entry, + async_unload_entry, +) from .const import ( # noqa: F401 ATTR_ATTRIBUTES, ATTR_BATTERY, diff --git a/homeassistant/components/freebox/device_tracker.py b/homeassistant/components/freebox/device_tracker.py index 0b48d08170a..7232f16696e 100644 --- a/homeassistant/components/freebox/device_tracker.py +++ b/homeassistant/components/freebox/device_tracker.py @@ -4,8 +4,7 @@ from __future__ import annotations from datetime import datetime from typing import Any -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import ScannerEntity +from homeassistant.components.device_tracker import ScannerEntity, SourceType from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect From 11ea834cbe13d08e3d99d9914e4cbe7e81550a38 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Fri, 28 Oct 2022 17:05:43 +0200 Subject: [PATCH 0040/1033] Improve MQTT update platform (#81131) * Allow JSON as state_topic payload * Add title * Add release_url * Add release_summary * Add entity_picture * Fix typo * Add abbreviations --- .../components/mqtt/abbreviations.py | 4 + homeassistant/components/mqtt/update.py | 82 +++++++++-- tests/components/mqtt/test_update.py | 134 +++++++++++++++++- 3 files changed, 211 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/mqtt/abbreviations.py b/homeassistant/components/mqtt/abbreviations.py index 67fffec1106..00f6d357553 100644 --- a/homeassistant/components/mqtt/abbreviations.py +++ b/homeassistant/components/mqtt/abbreviations.py @@ -52,6 +52,7 @@ ABBREVIATIONS = { "e": "encoding", "en": "enabled_by_default", "ent_cat": "entity_category", + "ent_pic": "entity_picture", "err_t": "error_topic", "err_tpl": "error_template", "fanspd_t": "fan_speed_topic", @@ -169,6 +170,8 @@ ABBREVIATIONS = { "pr_mode_val_tpl": "preset_mode_value_template", "pr_modes": "preset_modes", "r_tpl": "red_template", + "rel_s": "release_summary", + "rel_u": "release_url", "ret": "retain", "rgb_cmd_tpl": "rgb_command_template", "rgb_cmd_t": "rgb_command_topic", @@ -242,6 +245,7 @@ ABBREVIATIONS = { "tilt_opt": "tilt_optimistic", "tilt_status_t": "tilt_status_topic", "tilt_status_tpl": "tilt_status_template", + "tit": "title", "t": "topic", "uniq_id": "unique_id", "unit_of_meas": "unit_of_measurement", diff --git a/homeassistant/components/mqtt/update.py b/homeassistant/components/mqtt/update.py index 8fdc6393e0b..986ad013520 100644 --- a/homeassistant/components/mqtt/update.py +++ b/homeassistant/components/mqtt/update.py @@ -19,6 +19,7 @@ from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME, CONF_VALUE_TEMPLAT from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.json import JSON_DECODE_EXCEPTIONS, json_loads from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -30,6 +31,7 @@ from .const import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, + PAYLOAD_EMPTY_JSON, ) from .debug_info import log_messages from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper @@ -40,20 +42,28 @@ _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = "MQTT Update" +CONF_ENTITY_PICTURE = "entity_picture" CONF_LATEST_VERSION_TEMPLATE = "latest_version_template" CONF_LATEST_VERSION_TOPIC = "latest_version_topic" CONF_PAYLOAD_INSTALL = "payload_install" +CONF_RELEASE_SUMMARY = "release_summary" +CONF_RELEASE_URL = "release_url" +CONF_TITLE = "title" PLATFORM_SCHEMA_MODERN = MQTT_RO_SCHEMA.extend( { - vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, + vol.Optional(CONF_ENTITY_PICTURE): cv.string, vol.Optional(CONF_LATEST_VERSION_TEMPLATE): cv.template, - vol.Required(CONF_LATEST_VERSION_TOPIC): valid_subscribe_topic, + vol.Optional(CONF_LATEST_VERSION_TOPIC): valid_subscribe_topic, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PAYLOAD_INSTALL): cv.string, + vol.Optional(CONF_RELEASE_SUMMARY): cv.string, + vol.Optional(CONF_RELEASE_URL): cv.string, vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_TITLE): cv.string, }, ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) @@ -99,10 +109,22 @@ class MqttUpdate(MqttEntity, UpdateEntity, RestoreEntity): """Initialize the MQTT update.""" self._config = config self._attr_device_class = self._config.get(CONF_DEVICE_CLASS) + self._attr_release_summary = self._config.get(CONF_RELEASE_SUMMARY) + self._attr_release_url = self._config.get(CONF_RELEASE_URL) + self._attr_title = self._config.get(CONF_TITLE) + self._entity_picture: str | None = self._config.get(CONF_ENTITY_PICTURE) UpdateEntity.__init__(self) MqttEntity.__init__(self, hass, config, config_entry, discovery_data) + @property + def entity_picture(self) -> str | None: + """Return the entity picture to use in the frontend.""" + if self._entity_picture is not None: + return self._entity_picture + + return super().entity_picture + @staticmethod def config_schema() -> vol.Schema: """Return the config schema.""" @@ -138,15 +160,59 @@ class MqttUpdate(MqttEntity, UpdateEntity, RestoreEntity): @callback @log_messages(self.hass, self.entity_id) - def handle_installed_version_received(msg: ReceiveMessage) -> None: - """Handle receiving installed version via MQTT.""" - installed_version = self._templates[CONF_VALUE_TEMPLATE](msg.payload) + def handle_state_message_received(msg: ReceiveMessage) -> None: + """Handle receiving state message via MQTT.""" + payload = self._templates[CONF_VALUE_TEMPLATE](msg.payload) - if isinstance(installed_version, str) and installed_version != "": - self._attr_installed_version = installed_version + if not payload or payload == PAYLOAD_EMPTY_JSON: + _LOGGER.debug( + "Ignoring empty payload '%s' after rendering for topic %s", + payload, + msg.topic, + ) + return + + json_payload = {} + try: + json_payload = json_loads(payload) + _LOGGER.debug( + "JSON payload detected after processing payload '%s' on topic %s", + json_payload, + msg.topic, + ) + except JSON_DECODE_EXCEPTIONS: + _LOGGER.warning( + "No valid (JSON) payload detected after processing payload '%s' on topic %s", + json_payload, + msg.topic, + ) + json_payload["installed_version"] = payload + + if "installed_version" in json_payload: + self._attr_installed_version = json_payload["installed_version"] get_mqtt_data(self.hass).state_write_requests.write_state_request(self) - add_subscription(topics, CONF_STATE_TOPIC, handle_installed_version_received) + if "latest_version" in json_payload: + self._attr_latest_version = json_payload["latest_version"] + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + + if CONF_TITLE in json_payload and not self._attr_title: + self._attr_title = json_payload[CONF_TITLE] + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + + if CONF_RELEASE_SUMMARY in json_payload and not self._attr_release_summary: + self._attr_release_summary = json_payload[CONF_RELEASE_SUMMARY] + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + + if CONF_RELEASE_URL in json_payload and not self._attr_release_url: + self._attr_release_url = json_payload[CONF_RELEASE_URL] + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + + if CONF_ENTITY_PICTURE in json_payload and not self._entity_picture: + self._entity_picture = json_payload[CONF_ENTITY_PICTURE] + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + + add_subscription(topics, CONF_STATE_TOPIC, handle_state_message_received) @callback @log_messages(self.hass, self.entity_id) diff --git a/tests/components/mqtt/test_update.py b/tests/components/mqtt/test_update.py index 9b008f093d0..e7d75ee7cc8 100644 --- a/tests/components/mqtt/test_update.py +++ b/tests/components/mqtt/test_update.py @@ -6,7 +6,13 @@ import pytest from homeassistant.components import mqtt, update from homeassistant.components.update import DOMAIN as UPDATE_DOMAIN, SERVICE_INSTALL -from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, Platform +from homeassistant.const import ( + ATTR_ENTITY_ID, + STATE_OFF, + STATE_ON, + STATE_UNKNOWN, + Platform, +) from homeassistant.setup import async_setup_component from .test_common import ( @@ -68,6 +74,10 @@ async def test_run_update_setup(hass, mqtt_mock_entry_with_yaml_config): "state_topic": installed_version_topic, "latest_version_topic": latest_version_topic, "name": "Test Update", + "release_summary": "Test release summary", + "release_url": "https://example.com/release", + "title": "Test Update Title", + "entity_picture": "https://example.com/icon.png", } } }, @@ -84,6 +94,10 @@ async def test_run_update_setup(hass, mqtt_mock_entry_with_yaml_config): assert state.state == STATE_OFF assert state.attributes.get("installed_version") == "1.9.0" assert state.attributes.get("latest_version") == "1.9.0" + assert state.attributes.get("release_summary") == "Test release summary" + assert state.attributes.get("release_url") == "https://example.com/release" + assert state.attributes.get("title") == "Test Update Title" + assert state.attributes.get("entity_picture") == "https://example.com/icon.png" async_fire_mqtt_message(hass, latest_version_topic, "2.0.0") @@ -126,6 +140,10 @@ async def test_value_template(hass, mqtt_mock_entry_with_yaml_config): assert state.state == STATE_OFF assert state.attributes.get("installed_version") == "1.9.0" assert state.attributes.get("latest_version") == "1.9.0" + assert ( + state.attributes.get("entity_picture") + == "https://brands.home-assistant.io/_/mqtt/icon.png" + ) async_fire_mqtt_message(hass, latest_version_topic, '{"latest":"2.0.0"}') @@ -137,6 +155,120 @@ async def test_value_template(hass, mqtt_mock_entry_with_yaml_config): assert state.attributes.get("latest_version") == "2.0.0" +async def test_empty_json_state_message(hass, mqtt_mock_entry_with_yaml_config): + """Test an empty JSON payload.""" + state_topic = "test/state-topic" + await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + update.DOMAIN: { + "state_topic": state_topic, + "name": "Test Update", + } + } + }, + ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + + async_fire_mqtt_message(hass, state_topic, "{}") + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_UNKNOWN + + +async def test_json_state_message(hass, mqtt_mock_entry_with_yaml_config): + """Test whether it fetches data from a JSON payload.""" + state_topic = "test/state-topic" + await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + update.DOMAIN: { + "state_topic": state_topic, + "name": "Test Update", + } + } + }, + ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + + async_fire_mqtt_message( + hass, + state_topic, + '{"installed_version":"1.9.0","latest_version":"1.9.0",' + '"title":"Test Update Title","release_url":"https://example.com/release",' + '"release_summary":"Test release summary"}', + ) + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_OFF + assert state.attributes.get("installed_version") == "1.9.0" + assert state.attributes.get("latest_version") == "1.9.0" + assert state.attributes.get("release_summary") == "Test release summary" + assert state.attributes.get("release_url") == "https://example.com/release" + assert state.attributes.get("title") == "Test Update Title" + + async_fire_mqtt_message( + hass, + state_topic, + '{"installed_version":"1.9.0","latest_version":"2.0.0","title":"Test Update Title"}', + ) + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_ON + assert state.attributes.get("installed_version") == "1.9.0" + assert state.attributes.get("latest_version") == "2.0.0" + + +async def test_json_state_message_with_template(hass, mqtt_mock_entry_with_yaml_config): + """Test whether it fetches data from a JSON payload with template.""" + state_topic = "test/state-topic" + await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + update.DOMAIN: { + "state_topic": state_topic, + "value_template": '{{ {"installed_version": value_json.installed, "latest_version": value_json.latest} | to_json }}', + "name": "Test Update", + } + } + }, + ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + + async_fire_mqtt_message(hass, state_topic, '{"installed":"1.9.0","latest":"1.9.0"}') + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_OFF + assert state.attributes.get("installed_version") == "1.9.0" + assert state.attributes.get("latest_version") == "1.9.0" + + async_fire_mqtt_message(hass, state_topic, '{"installed":"1.9.0","latest":"2.0.0"}') + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_ON + assert state.attributes.get("installed_version") == "1.9.0" + assert state.attributes.get("latest_version") == "2.0.0" + + async def test_run_install_service(hass, mqtt_mock_entry_with_yaml_config): """Test that install service works.""" installed_version_topic = "test/installed-version" From 2214fff3b492a9b24ef4679653b4f2e53f79ee69 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 28 Oct 2022 17:40:37 +0200 Subject: [PATCH 0041/1033] Cleanup device_tracker imports (#81139) --- homeassistant/components/asuswrt/device_tracker.py | 3 +-- .../components/devolo_home_network/device_tracker.py | 2 +- homeassistant/components/fritz/device_tracker.py | 3 +-- homeassistant/components/geofency/device_tracker.py | 3 +-- homeassistant/components/gpslogger/device_tracker.py | 3 +-- homeassistant/components/huawei_lte/device_tracker.py | 2 +- homeassistant/components/icloud/device_tracker.py | 3 +-- homeassistant/components/keenetic_ndms2/device_tracker.py | 2 +- homeassistant/components/life360/device_tracker.py | 3 +-- homeassistant/components/locative/device_tracker.py | 3 +-- homeassistant/components/mazda/device_tracker.py | 3 +-- homeassistant/components/mikrotik/device_tracker.py | 7 +++++-- homeassistant/components/mobile_app/device_tracker.py | 2 +- homeassistant/components/netgear/device_tracker.py | 3 +-- homeassistant/components/nmap_tracker/device_tracker.py | 3 +-- homeassistant/components/owntracks/device_tracker.py | 8 ++++++-- homeassistant/components/renault/device_tracker.py | 3 +-- .../components/ruckus_unleashed/device_tracker.py | 3 +-- homeassistant/components/starline/device_tracker.py | 3 +-- homeassistant/components/tile/device_tracker.py | 7 +++++-- homeassistant/components/traccar/device_tracker.py | 2 +- homeassistant/components/tractive/device_tracker.py | 3 +-- homeassistant/components/unifi/device_tracker.py | 3 +-- homeassistant/components/volvooncall/device_tracker.py | 3 +-- homeassistant/components/zha/device_tracker.py | 3 +-- 25 files changed, 38 insertions(+), 45 deletions(-) diff --git a/homeassistant/components/asuswrt/device_tracker.py b/homeassistant/components/asuswrt/device_tracker.py index efdf4993927..3a9abdd7e85 100644 --- a/homeassistant/components/asuswrt/device_tracker.py +++ b/homeassistant/components/asuswrt/device_tracker.py @@ -1,8 +1,7 @@ """Support for ASUSWRT routers.""" from __future__ import annotations -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import ScannerEntity +from homeassistant.components.device_tracker import ScannerEntity, SourceType from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect diff --git a/homeassistant/components/devolo_home_network/device_tracker.py b/homeassistant/components/devolo_home_network/device_tracker.py index f535136680a..26b30ec43d5 100644 --- a/homeassistant/components/devolo_home_network/device_tracker.py +++ b/homeassistant/components/devolo_home_network/device_tracker.py @@ -7,9 +7,9 @@ from devolo_plc_api.device import Device from homeassistant.components.device_tracker import ( DOMAIN as DEVICE_TRACKER_DOMAIN, + ScannerEntity, SourceType, ) -from homeassistant.components.device_tracker.config_entry import ScannerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import FREQUENCY_GIGAHERTZ, STATE_UNKNOWN from homeassistant.core import HomeAssistant, callback diff --git a/homeassistant/components/fritz/device_tracker.py b/homeassistant/components/fritz/device_tracker.py index 998eab2ede7..212710a638c 100644 --- a/homeassistant/components/fritz/device_tracker.py +++ b/homeassistant/components/fritz/device_tracker.py @@ -4,8 +4,7 @@ from __future__ import annotations import datetime import logging -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import ScannerEntity +from homeassistant.components.device_tracker import ScannerEntity, SourceType from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect diff --git a/homeassistant/components/geofency/device_tracker.py b/homeassistant/components/geofency/device_tracker.py index 85197239ccd..cc47883d05a 100644 --- a/homeassistant/components/geofency/device_tracker.py +++ b/homeassistant/components/geofency/device_tracker.py @@ -1,6 +1,5 @@ """Support for the Geofency device tracker platform.""" -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import SourceType, TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE from homeassistant.core import HomeAssistant, callback diff --git a/homeassistant/components/gpslogger/device_tracker.py b/homeassistant/components/gpslogger/device_tracker.py index f18b486917a..a452d32e544 100644 --- a/homeassistant/components/gpslogger/device_tracker.py +++ b/homeassistant/components/gpslogger/device_tracker.py @@ -1,6 +1,5 @@ """Support for the GPSLogger device tracking.""" -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import SourceType, TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_BATTERY_LEVEL, diff --git a/homeassistant/components/huawei_lte/device_tracker.py b/homeassistant/components/huawei_lte/device_tracker.py index f97bda7481e..917250eae79 100644 --- a/homeassistant/components/huawei_lte/device_tracker.py +++ b/homeassistant/components/huawei_lte/device_tracker.py @@ -10,9 +10,9 @@ from stringcase import snakecase from homeassistant.components.device_tracker import ( DOMAIN as DEVICE_TRACKER_DOMAIN, + ScannerEntity, SourceType, ) -from homeassistant.components.device_tracker.config_entry import ScannerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry diff --git a/homeassistant/components/icloud/device_tracker.py b/homeassistant/components/icloud/device_tracker.py index fc1de213a69..d9bd215d2a1 100644 --- a/homeassistant/components/icloud/device_tracker.py +++ b/homeassistant/components/icloud/device_tracker.py @@ -3,8 +3,7 @@ from __future__ import annotations from typing import Any -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import SourceType, TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect diff --git a/homeassistant/components/keenetic_ndms2/device_tracker.py b/homeassistant/components/keenetic_ndms2/device_tracker.py index 4491b644b27..fd4265a4ef0 100644 --- a/homeassistant/components/keenetic_ndms2/device_tracker.py +++ b/homeassistant/components/keenetic_ndms2/device_tracker.py @@ -7,9 +7,9 @@ from ndms2_client import Device from homeassistant.components.device_tracker import ( DOMAIN as DEVICE_TRACKER_DOMAIN, + ScannerEntity, SourceType, ) -from homeassistant.components.device_tracker.config_entry import ScannerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry diff --git a/homeassistant/components/life360/device_tracker.py b/homeassistant/components/life360/device_tracker.py index a6ca0a16aa3..74ab63f88a1 100644 --- a/homeassistant/components/life360/device_tracker.py +++ b/homeassistant/components/life360/device_tracker.py @@ -6,8 +6,7 @@ from collections.abc import Mapping from contextlib import suppress from typing import Any, cast -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import SourceType, TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_BATTERY_CHARGING from homeassistant.core import HomeAssistant, callback diff --git a/homeassistant/components/locative/device_tracker.py b/homeassistant/components/locative/device_tracker.py index f8fa1671034..2ec1e7437de 100644 --- a/homeassistant/components/locative/device_tracker.py +++ b/homeassistant/components/locative/device_tracker.py @@ -1,6 +1,5 @@ """Support for the Locative platform.""" -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import SourceType, TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect diff --git a/homeassistant/components/mazda/device_tracker.py b/homeassistant/components/mazda/device_tracker.py index 946cff72f5b..67702ba5455 100644 --- a/homeassistant/components/mazda/device_tracker.py +++ b/homeassistant/components/mazda/device_tracker.py @@ -1,6 +1,5 @@ """Platform for Mazda device tracker integration.""" -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import SourceType, TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback diff --git a/homeassistant/components/mikrotik/device_tracker.py b/homeassistant/components/mikrotik/device_tracker.py index cfa2e216d1d..2c3e480c297 100644 --- a/homeassistant/components/mikrotik/device_tracker.py +++ b/homeassistant/components/mikrotik/device_tracker.py @@ -3,8 +3,11 @@ from __future__ import annotations from typing import Any -from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER, SourceType -from homeassistant.components.device_tracker.config_entry import ScannerEntity +from homeassistant.components.device_tracker import ( + DOMAIN as DEVICE_TRACKER, + ScannerEntity, + SourceType, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry diff --git a/homeassistant/components/mobile_app/device_tracker.py b/homeassistant/components/mobile_app/device_tracker.py index 6f9c68224ec..d347a0cc4db 100644 --- a/homeassistant/components/mobile_app/device_tracker.py +++ b/homeassistant/components/mobile_app/device_tracker.py @@ -5,8 +5,8 @@ from homeassistant.components.device_tracker import ( ATTR_GPS_ACCURACY, ATTR_LOCATION_NAME, SourceType, + TrackerEntity, ) -from homeassistant.components.device_tracker.config_entry import TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_BATTERY_LEVEL, diff --git a/homeassistant/components/netgear/device_tracker.py b/homeassistant/components/netgear/device_tracker.py index 5feff521efa..ffb33d5ebeb 100644 --- a/homeassistant/components/netgear/device_tracker.py +++ b/homeassistant/components/netgear/device_tracker.py @@ -3,8 +3,7 @@ from __future__ import annotations import logging -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import ScannerEntity +from homeassistant.components.device_tracker import ScannerEntity, SourceType from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback diff --git a/homeassistant/components/nmap_tracker/device_tracker.py b/homeassistant/components/nmap_tracker/device_tracker.py index 9203288f03a..bada45256a8 100644 --- a/homeassistant/components/nmap_tracker/device_tracker.py +++ b/homeassistant/components/nmap_tracker/device_tracker.py @@ -4,8 +4,7 @@ from __future__ import annotations import logging from typing import Any -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import ScannerEntity +from homeassistant.components.device_tracker import ScannerEntity, SourceType from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect diff --git a/homeassistant/components/owntracks/device_tracker.py b/homeassistant/components/owntracks/device_tracker.py index b65ac9ccbe7..f983d0f98d4 100644 --- a/homeassistant/components/owntracks/device_tracker.py +++ b/homeassistant/components/owntracks/device_tracker.py @@ -1,6 +1,10 @@ """Device tracker platform that adds support for OwnTracks over MQTT.""" -from homeassistant.components.device_tracker import ATTR_SOURCE_TYPE, DOMAIN, SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import ( + ATTR_SOURCE_TYPE, + DOMAIN, + SourceType, + TrackerEntity, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_BATTERY_LEVEL, diff --git a/homeassistant/components/renault/device_tracker.py b/homeassistant/components/renault/device_tracker.py index da267277d10..87ca3c9eb5f 100644 --- a/homeassistant/components/renault/device_tracker.py +++ b/homeassistant/components/renault/device_tracker.py @@ -3,8 +3,7 @@ from __future__ import annotations from renault_api.kamereon.models import KamereonVehicleLocationData -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import SourceType, TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback diff --git a/homeassistant/components/ruckus_unleashed/device_tracker.py b/homeassistant/components/ruckus_unleashed/device_tracker.py index c9d9df6068e..5e8998c47dd 100644 --- a/homeassistant/components/ruckus_unleashed/device_tracker.py +++ b/homeassistant/components/ruckus_unleashed/device_tracker.py @@ -1,8 +1,7 @@ """Support for Ruckus Unleashed devices.""" from __future__ import annotations -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import ScannerEntity +from homeassistant.components.device_tracker import ScannerEntity, SourceType from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry diff --git a/homeassistant/components/starline/device_tracker.py b/homeassistant/components/starline/device_tracker.py index e01807ba702..6dadfdbd3ea 100644 --- a/homeassistant/components/starline/device_tracker.py +++ b/homeassistant/components/starline/device_tracker.py @@ -1,6 +1,5 @@ """StarLine device tracker.""" -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import SourceType, TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback diff --git a/homeassistant/components/tile/device_tracker.py b/homeassistant/components/tile/device_tracker.py index 86afee18505..7931bbb8797 100644 --- a/homeassistant/components/tile/device_tracker.py +++ b/homeassistant/components/tile/device_tracker.py @@ -5,8 +5,11 @@ import logging from pytile.tile import Tile -from homeassistant.components.device_tracker import AsyncSeeCallback, SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import ( + AsyncSeeCallback, + SourceType, + TrackerEntity, +) from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant, callback diff --git a/homeassistant/components/traccar/device_tracker.py b/homeassistant/components/traccar/device_tracker.py index 1f6b0b828bd..601229ed2e0 100644 --- a/homeassistant/components/traccar/device_tracker.py +++ b/homeassistant/components/traccar/device_tracker.py @@ -22,8 +22,8 @@ from homeassistant.components.device_tracker import ( PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA, AsyncSeeCallback, SourceType, + TrackerEntity, ) -from homeassistant.components.device_tracker.config_entry import TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_EVENT, diff --git a/homeassistant/components/tractive/device_tracker.py b/homeassistant/components/tractive/device_tracker.py index f92a8e71df3..0cb73723369 100644 --- a/homeassistant/components/tractive/device_tracker.py +++ b/homeassistant/components/tractive/device_tracker.py @@ -3,8 +3,7 @@ from __future__ import annotations from typing import Any -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import SourceType, TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect diff --git a/homeassistant/components/unifi/device_tracker.py b/homeassistant/components/unifi/device_tracker.py index 1c91ea4724b..ea8db77e124 100644 --- a/homeassistant/components/unifi/device_tracker.py +++ b/homeassistant/components/unifi/device_tracker.py @@ -6,8 +6,7 @@ import logging from aiounifi.models.api import SOURCE_DATA, SOURCE_EVENT from aiounifi.models.event import EventKey -from homeassistant.components.device_tracker import DOMAIN, SourceType -from homeassistant.components.device_tracker.config_entry import ScannerEntity +from homeassistant.components.device_tracker import DOMAIN, ScannerEntity, SourceType from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect diff --git a/homeassistant/components/volvooncall/device_tracker.py b/homeassistant/components/volvooncall/device_tracker.py index 159cb39cf6a..0cd61a336b7 100644 --- a/homeassistant/components/volvooncall/device_tracker.py +++ b/homeassistant/components/volvooncall/device_tracker.py @@ -3,8 +3,7 @@ from __future__ import annotations from volvooncall.dashboard import Instrument -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import TrackerEntity +from homeassistant.components.device_tracker import SourceType, TrackerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect diff --git a/homeassistant/components/zha/device_tracker.py b/homeassistant/components/zha/device_tracker.py index 8ecc85a4e56..1a636ce65a2 100644 --- a/homeassistant/components/zha/device_tracker.py +++ b/homeassistant/components/zha/device_tracker.py @@ -4,8 +4,7 @@ from __future__ import annotations import functools import time -from homeassistant.components.device_tracker import SourceType -from homeassistant.components.device_tracker.config_entry import ScannerEntity +from homeassistant.components.device_tracker import ScannerEntity, SourceType from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant, callback From bcae6d604e2967c7475f0caa4b1b5e4e76ab88bf Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 28 Oct 2022 18:20:33 +0200 Subject: [PATCH 0042/1033] Improve MQTT type hints part 8 (#81034) * Improve typing device_tracker discovery * Improve typing device_tracker yaml * Add test source_type attribute * Follow up comment * Initialize at `__init__` not at class level. * Use full name for return variable * Correct import, remove assert * Use AsyncSeeCallback --- .../mqtt/device_tracker/schema_discovery.py | 62 +++++++++++-------- .../mqtt/device_tracker/schema_yaml.py | 33 ++++++---- .../mqtt/test_device_tracker_discovery.py | 4 +- 3 files changed, 60 insertions(+), 39 deletions(-) diff --git a/homeassistant/components/mqtt/device_tracker/schema_discovery.py b/homeassistant/components/mqtt/device_tracker/schema_discovery.py index 673a9cb04b7..ba088a59c44 100644 --- a/homeassistant/components/mqtt/device_tracker/schema_discovery.py +++ b/homeassistant/components/mqtt/device_tracker/schema_discovery.py @@ -1,13 +1,14 @@ """Support for tracking MQTT enabled devices identified through discovery.""" from __future__ import annotations +from collections.abc import Callable import functools import voluptuous as vol from homeassistant.components import device_tracker -from homeassistant.components.device_tracker import SOURCE_TYPES -from homeassistant.components.device_tracker.config_entry import ( +from homeassistant.components.device_tracker import ( + SOURCE_TYPES, SourceType, TrackerEntity, ) @@ -24,14 +25,14 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from .. import subscription from ..config import MQTT_RO_SCHEMA from ..const import CONF_QOS, CONF_STATE_TOPIC from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper -from ..models import MqttValueTemplate +from ..models import MqttValueTemplate, ReceiveMessage, ReceivePayloadType from ..util import get_mqtt_data CONF_PAYLOAD_HOME = "payload_home" @@ -70,8 +71,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT Device Tracker entity.""" async_add_entities([MqttDeviceTracker(hass, config, config_entry, discovery_data)]) @@ -81,37 +82,44 @@ class MqttDeviceTracker(MqttEntity, TrackerEntity): """Representation of a device tracker using MQTT.""" _entity_id_format = device_tracker.ENTITY_ID_FORMAT + _value_template: Callable[..., ReceivePayloadType] - def __init__(self, hass, config, config_entry, discovery_data): + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the tracker.""" - self._location_name = None - + self._location_name: str | None = None MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" self._value_template = MqttValueTemplate( - self._config.get(CONF_VALUE_TEMPLATE), entity=self + config.get(CONF_VALUE_TEMPLATE), entity=self ).async_render_with_possible_json_value - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" @callback @log_messages(self.hass, self.entity_id) - def message_received(msg): + def message_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages.""" - payload = self._value_template(msg.payload) + payload: ReceivePayloadType = self._value_template(msg.payload) if payload == self._config[CONF_PAYLOAD_HOME]: self._location_name = STATE_HOME elif payload == self._config[CONF_PAYLOAD_NOT_HOME]: self._location_name = STATE_NOT_HOME else: + assert isinstance(msg.payload, str) self._location_name = msg.payload get_mqtt_data(self.hass).state_write_requests.write_state_request(self) @@ -128,46 +136,50 @@ class MqttDeviceTracker(MqttEntity, TrackerEntity): }, ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) @property - def latitude(self): + def latitude(self) -> float | None: """Return latitude if provided in extra_state_attributes or None.""" if ( self.extra_state_attributes is not None and ATTR_LATITUDE in self.extra_state_attributes ): - return self.extra_state_attributes[ATTR_LATITUDE] + latitude: float = self.extra_state_attributes[ATTR_LATITUDE] + return latitude return None @property - def location_accuracy(self): + def location_accuracy(self) -> int: """Return location accuracy if provided in extra_state_attributes or None.""" if ( self.extra_state_attributes is not None and ATTR_GPS_ACCURACY in self.extra_state_attributes ): - return self.extra_state_attributes[ATTR_GPS_ACCURACY] - return None + accuracy: int = self.extra_state_attributes[ATTR_GPS_ACCURACY] + return accuracy + return 0 @property - def longitude(self): + def longitude(self) -> float | None: """Return longitude if provided in extra_state_attributes or None.""" if ( self.extra_state_attributes is not None and ATTR_LONGITUDE in self.extra_state_attributes ): - return self.extra_state_attributes[ATTR_LONGITUDE] + longitude: float = self.extra_state_attributes[ATTR_LONGITUDE] + return longitude return None @property - def location_name(self): + def location_name(self) -> str | None: """Return a location name for the current location of the device.""" return self._location_name @property def source_type(self) -> SourceType | str: """Return the source type, eg gps or router, of the device.""" - return self._config[CONF_SOURCE_TYPE] + source_type: SourceType | str = self._config[CONF_SOURCE_TYPE] + return source_type diff --git a/homeassistant/components/mqtt/device_tracker/schema_yaml.py b/homeassistant/components/mqtt/device_tracker/schema_yaml.py index c005a82dbeb..d88a82e3002 100644 --- a/homeassistant/components/mqtt/device_tracker/schema_yaml.py +++ b/homeassistant/components/mqtt/device_tracker/schema_yaml.py @@ -1,14 +1,19 @@ """Support for tracking MQTT enabled devices defined in YAML.""" from __future__ import annotations -from collections.abc import Awaitable, Callable +from collections.abc import Callable, Coroutine import dataclasses import logging from typing import Any import voluptuous as vol -from homeassistant.components.device_tracker import PLATFORM_SCHEMA, SOURCE_TYPES +from homeassistant.components.device_tracker import ( + PLATFORM_SCHEMA, + SOURCE_TYPES, + AsyncSeeCallback, + SourceType, +) from homeassistant.const import CONF_DEVICES, STATE_HOME, STATE_NOT_HOME from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import config_validation as cv @@ -18,6 +23,7 @@ from ... import mqtt from ..client import async_subscribe from ..config import SCHEMA_BASE from ..const import CONF_QOS, MQTT_DATA_DEVICE_TRACKER_LEGACY +from ..models import ReceiveMessage from ..util import mqtt_config_entry_enabled, valid_subscribe_topic _LOGGER = logging.getLogger(__name__) @@ -40,22 +46,22 @@ PLATFORM_SCHEMA_YAML = PLATFORM_SCHEMA.extend(SCHEMA_BASE).extend( class MQTTLegacyDeviceTrackerData: """Class to hold device tracker data.""" - async_see: Callable[..., Awaitable[None]] + async_see: Callable[..., Coroutine[Any, Any, None]] config: ConfigType async def async_setup_scanner_from_yaml( hass: HomeAssistant, config: ConfigType, - async_see: Callable[..., Awaitable[None]], + async_see: AsyncSeeCallback, discovery_info: DiscoveryInfoType | None = None, ) -> bool: """Set up the MQTT tracker.""" - devices = config[CONF_DEVICES] - qos = config[CONF_QOS] - payload_home = config[CONF_PAYLOAD_HOME] - payload_not_home = config[CONF_PAYLOAD_NOT_HOME] - source_type = config.get(CONF_SOURCE_TYPE) + devices: dict[str, str] = config[CONF_DEVICES] + qos: int = config[CONF_QOS] + payload_home: str = config[CONF_PAYLOAD_HOME] + payload_not_home: str = config[CONF_PAYLOAD_NOT_HOME] + source_type: SourceType | str | None = config.get(CONF_SOURCE_TYPE) config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] subscriptions: list[Callable] = [] @@ -78,16 +84,19 @@ async def async_setup_scanner_from_yaml( for dev_id, topic in devices.items(): @callback - def async_message_received(msg, dev_id=dev_id): + def async_message_received(msg: ReceiveMessage, dev_id: str = dev_id) -> None: """Handle received MQTT message.""" if msg.payload == payload_home: location_name = STATE_HOME elif msg.payload == payload_not_home: location_name = STATE_NOT_HOME else: - location_name = msg.payload + location_name = str(msg.payload) - see_args = {"dev_id": dev_id, "location_name": location_name} + see_args: dict[str, Any] = { + "dev_id": dev_id, + "location_name": location_name, + } if source_type: see_args["source_type"] = source_type diff --git a/tests/components/mqtt/test_device_tracker_discovery.py b/tests/components/mqtt/test_device_tracker_discovery.py index 6324afeb3ef..873ca1ed9a1 100644 --- a/tests/components/mqtt/test_device_tracker_discovery.py +++ b/tests/components/mqtt/test_device_tracker_discovery.py @@ -410,12 +410,12 @@ async def test_setting_device_tracker_location_via_lat_lon_message( async_fire_mqtt_message( hass, "attributes-topic", - '{"latitude":50.1,"longitude": -2.1, "gps_accuracy":1.5}', + '{"latitude":50.1,"longitude": -2.1}', ) state = hass.states.get("device_tracker.test") assert state.attributes["latitude"] == 50.1 assert state.attributes["longitude"] == -2.1 - assert state.attributes["gps_accuracy"] == 1.5 + assert state.attributes["gps_accuracy"] == 0 assert state.state == STATE_NOT_HOME async_fire_mqtt_message(hass, "attributes-topic", '{"longitude": -117.22743}') From 5cf4483df519846f9b13b3a1bb984c1593bdc69e Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Fri, 28 Oct 2022 19:48:27 +0300 Subject: [PATCH 0043/1033] Fix Shelly Plus H&T sleep period on external power state change (#81121) --- .../components/shelly/coordinator.py | 30 ++++++++++++++++++- homeassistant/components/shelly/utils.py | 5 ++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py index 014355116c1..23f905b0fd9 100644 --- a/homeassistant/components/shelly/coordinator.py +++ b/homeassistant/components/shelly/coordinator.py @@ -41,7 +41,12 @@ from .const import ( SLEEP_PERIOD_MULTIPLIER, UPDATE_PERIOD_MULTIPLIER, ) -from .utils import device_update_info, get_block_device_name, get_rpc_device_name +from .utils import ( + device_update_info, + get_block_device_name, + get_rpc_device_name, + get_rpc_device_wakeup_period, +) @dataclass @@ -355,6 +360,24 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): LOGGER.debug("Reloading entry %s", self.name) await self.hass.config_entries.async_reload(self.entry.entry_id) + def update_sleep_period(self) -> bool: + """Check device sleep period & update if changed.""" + if ( + not self.device.initialized + or not (wakeup_period := get_rpc_device_wakeup_period(self.device.status)) + or wakeup_period == self.entry.data.get(CONF_SLEEP_PERIOD) + ): + return False + + data = {**self.entry.data} + data[CONF_SLEEP_PERIOD] = wakeup_period + self.hass.config_entries.async_update_entry(self.entry, data=data) + + update_interval = SLEEP_PERIOD_MULTIPLIER * wakeup_period + self.update_interval = timedelta(seconds=update_interval) + + return True + @callback def _async_device_updates_handler(self) -> None: """Handle device updates.""" @@ -365,6 +388,8 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): ): return + self.update_sleep_period() + self._last_event = self.device.event for event in self.device.event["events"]: @@ -393,6 +418,9 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): async def _async_update_data(self) -> None: """Fetch data.""" + if self.update_sleep_period(): + return + if sleep_period := self.entry.data.get(CONF_SLEEP_PERIOD): # Sleeping device, no point polling it, just mark it unavailable raise UpdateFailed( diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index 79f5a5848f0..c3b6d24752f 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -272,6 +272,11 @@ def get_rpc_device_sleep_period(config: dict[str, Any]) -> int: return cast(int, config["sys"].get("sleep", {}).get("wakeup_period", 0)) +def get_rpc_device_wakeup_period(status: dict[str, Any]) -> int: + """Return the device wakeup period in seconds or 0 for non sleeping devices.""" + return cast(int, status["sys"].get("wakeup_period", 0)) + + def get_info_auth(info: dict[str, Any]) -> bool: """Return true if device has authorization enabled.""" return cast(bool, info.get("auth") or info.get("auth_en")) From b7a651519b8dcfbafe5bd75b3d8eed7cbc8e5796 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 28 Oct 2022 12:05:48 -0500 Subject: [PATCH 0044/1033] Bump aiohomekit to 2.2.6 (#81144) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 24b2eebe615..34d47d6d835 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.5"], + "requirements": ["aiohomekit==2.2.6"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index f5a71d35219..9005c0755fb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.5 +aiohomekit==2.2.6 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2025350ee0f..e4b7eaded27 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.5 +aiohomekit==2.2.6 # homeassistant.components.emulated_hue # homeassistant.components.http From 4dbb265590ca82c523ea8a929f358fb8213191fc Mon Sep 17 00:00:00 2001 From: javicalle <31999997+javicalle@users.noreply.github.com> Date: Sat, 29 Oct 2022 00:20:26 +0200 Subject: [PATCH 0045/1033] Fix RFLink unit conversions (#79436) * Fix unit conversions * Fix tests * Address requests changes * Address requests changes * Address requests changes * Address requests changes * migrate to SensorEntityDescription * forgotten test * forgotten test * chango to WIND_SPEED * manage UOM * fix test * address comments * fix implementation * prevent from mutate config --- homeassistant/components/rflink/__init__.py | 1 + homeassistant/components/rflink/sensor.py | 98 ++++++++++++++++-- tests/components/rflink/test_init.py | 38 +++++++ tests/components/rflink/test_sensor.py | 106 ++++++++++++++++++-- 4 files changed, 222 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/rflink/__init__.py b/homeassistant/components/rflink/__init__.py index 7bc87de9f46..cfa76a57e67 100644 --- a/homeassistant/components/rflink/__init__.py +++ b/homeassistant/components/rflink/__init__.py @@ -337,6 +337,7 @@ class RflinkDevice(Entity): # Rflink specific attributes for every component type self._initial_event = initial_event self._device_id = device_id + self._attr_unique_id = device_id if name: self._name = name else: diff --git a/homeassistant/components/rflink/sensor.py b/homeassistant/components/rflink/sensor.py index 2420e933653..d1c3682228c 100644 --- a/homeassistant/components/rflink/sensor.py +++ b/homeassistant/components/rflink/sensor.py @@ -1,16 +1,25 @@ """Support for Rflink sensors.""" from __future__ import annotations +from typing import Any + from rflink.parser import PACKET_FIELDS, UNITS import voluptuous as vol -from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity +from homeassistant.components.sensor import ( + PLATFORM_SCHEMA, + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) from homeassistant.const import ( - ATTR_UNIT_OF_MEASUREMENT, CONF_DEVICES, CONF_NAME, CONF_SENSOR_TYPE, CONF_UNIT_OF_MEASUREMENT, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -35,9 +44,68 @@ from . import ( SENSOR_ICONS = { "humidity": "mdi:water-percent", "battery": "mdi:battery", - "temperature": "mdi:thermometer", } +SENSOR_TYPES = ( + # check new descriptors against PACKET_FIELDS & UNITS from rflink.parser + SensorEntityDescription( + key="distance", + name="Distance", + device_class=SensorDeviceClass.DISTANCE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key="barometric_pressure", + name="Barometric pressure", + device_class=SensorDeviceClass.PRESSURE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key="average_windspeed", + name="Average windspeed", + device_class=SensorDeviceClass.WIND_SPEED, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + ), + SensorEntityDescription( + key="windgusts", + name="Wind gusts", + device_class=SensorDeviceClass.WIND_SPEED, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + ), + SensorEntityDescription( + key="windspeed", + name="Wind speed", + device_class=SensorDeviceClass.WIND_SPEED, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + ), + SensorEntityDescription( + key="temperature", + name="Temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + ), + SensorEntityDescription( + key="windtemp", + name="Wind temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + ), + SensorEntityDescription( + key="windchill", + name="Wind chill", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + ), +) + +SENSOR_TYPES_DICT = {desc.key: desc for desc in SENSOR_TYPES} + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Optional(CONF_AUTOMATIC_ADD, default=True): cv.boolean, @@ -72,10 +140,6 @@ def devices_from_config(domain_config): """Parse configuration and add Rflink sensor devices.""" devices = [] for device_id, config in domain_config[CONF_DEVICES].items(): - if ATTR_UNIT_OF_MEASUREMENT not in config: - config[ATTR_UNIT_OF_MEASUREMENT] = lookup_unit_for_sensor_type( - config[CONF_SENSOR_TYPE] - ) device = RflinkSensor(device_id, **config) devices.append(device) @@ -112,11 +176,21 @@ class RflinkSensor(RflinkDevice, SensorEntity): """Representation of a Rflink sensor.""" def __init__( - self, device_id, sensor_type, unit_of_measurement, initial_event=None, **kwargs - ): + self, + device_id: str, + sensor_type: str, + unit_of_measurement: str | None = None, + initial_event=None, + **kwargs: Any, + ) -> None: """Handle sensor specific args and super init.""" self._sensor_type = sensor_type self._unit_of_measurement = unit_of_measurement + if sensor_type in SENSOR_TYPES_DICT: + self.entity_description = SENSOR_TYPES_DICT[sensor_type] + elif not unit_of_measurement: + self._unit_of_measurement = lookup_unit_for_sensor_type(sensor_type) + super().__init__(device_id, initial_event=initial_event, **kwargs) def _handle_event(self, event): @@ -164,7 +238,11 @@ class RflinkSensor(RflinkDevice, SensorEntity): @property def native_unit_of_measurement(self): """Return measurement unit.""" - return self._unit_of_measurement + if self._unit_of_measurement: + return self._unit_of_measurement + if hasattr(self, "entity_description"): + return self.entity_description.native_unit_of_measurement + return None @property def native_value(self): diff --git a/tests/components/rflink/test_init.py b/tests/components/rflink/test_init.py index 73def144aba..cd0dfe094d4 100644 --- a/tests/components/rflink/test_init.py +++ b/tests/components/rflink/test_init.py @@ -25,6 +25,7 @@ from homeassistant.const import ( SERVICE_STOP_COVER, SERVICE_TURN_OFF, ) +from homeassistant.helpers import entity_registry as er async def mock_rflink( @@ -476,3 +477,40 @@ async def test_default_keepalive(hass, monkeypatch, caplog): == DEFAULT_TCP_KEEPALIVE_IDLE_TIMER ) # no keepalive config will default it assert "TCP Keepalive IDLE timer was provided" not in caplog.text + + +async def test_unique_id(hass, monkeypatch): + """Validate the device unique_id.""" + + DOMAIN = "sensor" + config = { + "rflink": {"port": "/dev/ttyABC0"}, + DOMAIN: { + "platform": "rflink", + "devices": { + "my_humidity_device_unique_id": { + "name": "humidity_device", + "sensor_type": "humidity", + "aliases": ["test_alias_02_0"], + }, + "my_temperature_device_unique_id": { + "name": "temperature_device", + "sensor_type": "temperature", + "aliases": ["test_alias_02_0"], + }, + }, + }, + } + + registry = er.async_get(hass) + + # setup mocking rflink module + event_callback, _, _, _ = await mock_rflink(hass, config, DOMAIN, monkeypatch) + + humidity_entry = registry.async_get("sensor.humidity_device") + assert humidity_entry + assert humidity_entry.unique_id == "my_humidity_device_unique_id" + + temperature_entry = registry.async_get("sensor.temperature_device") + assert temperature_entry + assert temperature_entry.unique_id == "my_temperature_device_unique_id" diff --git a/tests/components/rflink/test_sensor.py b/tests/components/rflink/test_sensor.py index 13d7e4b300d..4faa1eb6d07 100644 --- a/tests/components/rflink/test_sensor.py +++ b/tests/components/rflink/test_sensor.py @@ -12,11 +12,13 @@ from homeassistant.components.rflink import ( EVENT_KEY_SENSOR, TMP_ENTITY, ) +from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass from homeassistant.const import ( + ATTR_ICON, ATTR_UNIT_OF_MEASUREMENT, PERCENTAGE, STATE_UNKNOWN, - TEMP_CELSIUS, + UnitOfTemperature, ) from .test_init import mock_rflink @@ -47,11 +49,18 @@ async def test_default_setup(hass, monkeypatch): config_sensor = hass.states.get("sensor.test") assert config_sensor assert config_sensor.state == "unknown" - assert config_sensor.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS + assert ( + config_sensor.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfTemperature.CELSIUS + ) # test event for config sensor event_callback( - {"id": "test", "sensor": "temperature", "value": 1, "unit": TEMP_CELSIUS} + { + "id": "test", + "sensor": "temperature", + "value": 1, + "unit": UnitOfTemperature.CELSIUS, + } ) await hass.async_block_till_done() @@ -59,16 +68,36 @@ async def test_default_setup(hass, monkeypatch): # test event for new unconfigured sensor event_callback( - {"id": "test2", "sensor": "temperature", "value": 0, "unit": TEMP_CELSIUS} + { + "id": "test2", + "sensor": "temperature", + "value": 0, + "unit": UnitOfTemperature.CELSIUS, + } ) await hass.async_block_till_done() - # test state of new sensor - new_sensor = hass.states.get("sensor.test2") - assert new_sensor - assert new_sensor.state == "0" - assert new_sensor.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS - assert new_sensor.attributes["icon"] == "mdi:thermometer" + # test state of temp sensor + temp_sensor = hass.states.get("sensor.test2") + assert temp_sensor + assert temp_sensor.state == "0" + assert temp_sensor.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfTemperature.CELSIUS + assert ( + ATTR_ICON not in temp_sensor.attributes + ) # temperature uses SensorEntityDescription + + # test event for new unconfigured sensor + event_callback( + {"id": "test3", "sensor": "humidity", "value": 43, "unit": PERCENTAGE} + ) + await hass.async_block_till_done() + + # test state of hum sensor + hum_sensor = hass.states.get("sensor.test3") + assert hum_sensor + assert hum_sensor.state == "43" + assert hum_sensor.attributes[ATTR_UNIT_OF_MEASUREMENT] == PERCENTAGE + assert hum_sensor.attributes[ATTR_ICON] == "mdi:water-percent" async def test_disable_automatic_add(hass, monkeypatch): @@ -83,7 +112,12 @@ async def test_disable_automatic_add(hass, monkeypatch): # test event for new unconfigured sensor event_callback( - {"id": "test2", "sensor": "temperature", "value": 0, "unit": TEMP_CELSIUS} + { + "id": "test2", + "sensor": "temperature", + "value": 0, + "unit": UnitOfTemperature.CELSIUS, + } ) await hass.async_block_till_done() @@ -205,3 +239,53 @@ async def test_race_condition(hass, monkeypatch): new_sensor = hass.states.get(f"{DOMAIN}.test3") assert new_sensor assert new_sensor.state == "ko" + + +async def test_sensor_attributes(hass, monkeypatch): + """Validate the sensor attributes.""" + + config = { + "rflink": {"port": "/dev/ttyABC0"}, + DOMAIN: { + "platform": "rflink", + "devices": { + "my_humidity_device_unique_id": { + "name": "humidity_device", + "sensor_type": "humidity", + }, + "my_temperature_device_unique_id": { + "name": "temperature_device", + "sensor_type": "temperature", + }, + "another_temperature_device_unique_id": { + "name": "fahrenheit_device", + "sensor_type": "temperature", + "unit_of_measurement": "F", + }, + }, + }, + } + + # setup mocking rflink module + event_callback, _, _, _ = await mock_rflink(hass, config, DOMAIN, monkeypatch) + + # test sensor loaded from config + humidity_state = hass.states.get("sensor.humidity_device") + assert humidity_state + assert "device_class" not in humidity_state.attributes + assert "state_class" not in humidity_state.attributes + assert humidity_state.attributes["unit_of_measurement"] == PERCENTAGE + + temperature_state = hass.states.get("sensor.temperature_device") + assert temperature_state + assert temperature_state.attributes["device_class"] == SensorDeviceClass.TEMPERATURE + assert temperature_state.attributes["state_class"] == SensorStateClass.MEASUREMENT + assert ( + temperature_state.attributes["unit_of_measurement"] == UnitOfTemperature.CELSIUS + ) + + fahrenheit_state = hass.states.get("sensor.fahrenheit_device") + assert fahrenheit_state + assert fahrenheit_state.attributes["device_class"] == SensorDeviceClass.TEMPERATURE + assert fahrenheit_state.attributes["state_class"] == SensorStateClass.MEASUREMENT + assert fahrenheit_state.attributes["unit_of_measurement"] == "F" From 6225f1f73738875e2b72f11046339f53f80bf422 Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Fri, 28 Oct 2022 18:25:44 -0400 Subject: [PATCH 0046/1033] Bump aiopyarr to 22.10.0 (#81153) --- homeassistant/components/lidarr/manifest.json | 2 +- homeassistant/components/radarr/manifest.json | 2 +- homeassistant/components/sonarr/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/radarr/fixtures/movie.json | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/lidarr/manifest.json b/homeassistant/components/lidarr/manifest.json index 7d4e9bcede7..4c07e0e1762 100644 --- a/homeassistant/components/lidarr/manifest.json +++ b/homeassistant/components/lidarr/manifest.json @@ -2,7 +2,7 @@ "domain": "lidarr", "name": "Lidarr", "documentation": "https://www.home-assistant.io/integrations/lidarr", - "requirements": ["aiopyarr==22.9.0"], + "requirements": ["aiopyarr==22.10.0"], "codeowners": ["@tkdrob"], "config_flow": true, "iot_class": "local_polling", diff --git a/homeassistant/components/radarr/manifest.json b/homeassistant/components/radarr/manifest.json index 5bc15b24069..9b140def96a 100644 --- a/homeassistant/components/radarr/manifest.json +++ b/homeassistant/components/radarr/manifest.json @@ -2,7 +2,7 @@ "domain": "radarr", "name": "Radarr", "documentation": "https://www.home-assistant.io/integrations/radarr", - "requirements": ["aiopyarr==22.9.0"], + "requirements": ["aiopyarr==22.10.0"], "codeowners": ["@tkdrob"], "config_flow": true, "iot_class": "local_polling", diff --git a/homeassistant/components/sonarr/manifest.json b/homeassistant/components/sonarr/manifest.json index 0c5b68a7949..daf9e20586b 100644 --- a/homeassistant/components/sonarr/manifest.json +++ b/homeassistant/components/sonarr/manifest.json @@ -3,7 +3,7 @@ "name": "Sonarr", "documentation": "https://www.home-assistant.io/integrations/sonarr", "codeowners": ["@ctalkington"], - "requirements": ["aiopyarr==22.9.0"], + "requirements": ["aiopyarr==22.10.0"], "config_flow": true, "quality_scale": "silver", "iot_class": "local_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 9005c0755fb..a1ad63f5a59 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -237,7 +237,7 @@ aiopvpc==3.0.0 # homeassistant.components.lidarr # homeassistant.components.radarr # homeassistant.components.sonarr -aiopyarr==22.9.0 +aiopyarr==22.10.0 # homeassistant.components.qnap_qsw aioqsw==0.2.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e4b7eaded27..507a682f476 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -212,7 +212,7 @@ aiopvpc==3.0.0 # homeassistant.components.lidarr # homeassistant.components.radarr # homeassistant.components.sonarr -aiopyarr==22.9.0 +aiopyarr==22.10.0 # homeassistant.components.qnap_qsw aioqsw==0.2.2 diff --git a/tests/components/radarr/fixtures/movie.json b/tests/components/radarr/fixtures/movie.json index 0f974859631..b33ff6fc481 100644 --- a/tests/components/radarr/fixtures/movie.json +++ b/tests/components/radarr/fixtures/movie.json @@ -21,8 +21,8 @@ "sortTitle": "string", "sizeOnDisk": 0, "overview": "string", - "inCinemas": "string", - "physicalRelease": "string", + "inCinemas": "2020-11-06T00:00:00Z", + "physicalRelease": "2019-03-19T00:00:00Z", "images": [ { "coverType": "poster", @@ -50,7 +50,7 @@ "certification": "string", "genres": ["string"], "tags": [0], - "added": "string", + "added": "2018-12-28T05:56:49Z", "ratings": { "votes": 0, "value": 0 From 5c521a4c0ea7c3e67f3c24cad4c508e28ec764d1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 28 Oct 2022 17:31:30 -0500 Subject: [PATCH 0047/1033] Bump aiohomekit to 2.2.7 (#81163) changelog: https://github.com/Jc2k/aiohomekit/compare/2.2.6...2.2.7 --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 34d47d6d835..5aaae67d1d3 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.6"], + "requirements": ["aiohomekit==2.2.7"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index a1ad63f5a59..c1ad582c0d0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.6 +aiohomekit==2.2.7 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 507a682f476..0dee17d8ea2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.6 +aiohomekit==2.2.7 # homeassistant.components.emulated_hue # homeassistant.components.http From 647900150160ede21a8fafb599201ad4fd327fd4 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Fri, 28 Oct 2022 18:31:53 -0400 Subject: [PATCH 0048/1033] Bump zigpy to 0.51.5 (#81164) Bump zigpy from 0.51.4 to 0.51.5 --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 1c86fe52c5e..79980d763e7 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -9,7 +9,7 @@ "pyserial-asyncio==0.6", "zha-quirks==0.0.84", "zigpy-deconz==0.19.0", - "zigpy==0.51.4", + "zigpy==0.51.5", "zigpy-xbee==0.16.2", "zigpy-zigate==0.10.2", "zigpy-znp==0.9.1" diff --git a/requirements_all.txt b/requirements_all.txt index c1ad582c0d0..d626e1a7494 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2628,7 +2628,7 @@ zigpy-zigate==0.10.2 zigpy-znp==0.9.1 # homeassistant.components.zha -zigpy==0.51.4 +zigpy==0.51.5 # homeassistant.components.zoneminder zm-py==0.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0dee17d8ea2..93d1d7dd467 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1823,7 +1823,7 @@ zigpy-zigate==0.10.2 zigpy-znp==0.9.1 # homeassistant.components.zha -zigpy==0.51.4 +zigpy==0.51.5 # homeassistant.components.zwave_js zwave-js-server-python==0.43.0 From 674655bf7d091e3d5d490585487e8fb5832e8f3a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 28 Oct 2022 17:32:38 -0500 Subject: [PATCH 0049/1033] Bump oralb-ble to 0.9.0 (#81166) * Bump oralb-ble to 0.9.0 changelog: https://github.com/Bluetooth-Devices/oralb-ble/compare/v0.8.0...v0.9.0 * empty --- homeassistant/components/oralb/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/oralb/manifest.json b/homeassistant/components/oralb/manifest.json index e25f407add1..8f694946804 100644 --- a/homeassistant/components/oralb/manifest.json +++ b/homeassistant/components/oralb/manifest.json @@ -8,7 +8,7 @@ "manufacturer_id": 220 } ], - "requirements": ["oralb-ble==0.8.0"], + "requirements": ["oralb-ble==0.9.0"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index d626e1a7494..3d01908f0ab 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1238,7 +1238,7 @@ openwrt-luci-rpc==1.1.11 openwrt-ubus-rpc==0.0.2 # homeassistant.components.oralb -oralb-ble==0.8.0 +oralb-ble==0.9.0 # homeassistant.components.oru oru==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 93d1d7dd467..f02e27d56a4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -883,7 +883,7 @@ open-meteo==0.2.1 openerz-api==0.1.0 # homeassistant.components.oralb -oralb-ble==0.8.0 +oralb-ble==0.9.0 # homeassistant.components.ovo_energy ovoenergy==1.2.0 From 08b3df56a0afca369e14a297086f5ab58722bf00 Mon Sep 17 00:00:00 2001 From: muppet3000 Date: Fri, 28 Oct 2022 23:32:57 +0100 Subject: [PATCH 0050/1033] Growatt version bump - fixes #80950 (#81161) --- homeassistant/components/growatt_server/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/growatt_server/manifest.json b/homeassistant/components/growatt_server/manifest.json index 4127b48ae64..f3f17804fc1 100644 --- a/homeassistant/components/growatt_server/manifest.json +++ b/homeassistant/components/growatt_server/manifest.json @@ -3,7 +3,7 @@ "name": "Growatt", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/growatt_server/", - "requirements": ["growattServer==1.2.2"], + "requirements": ["growattServer==1.2.3"], "codeowners": ["@indykoning", "@muppet3000", "@JasperPlant"], "iot_class": "cloud_polling", "loggers": ["growattServer"] diff --git a/requirements_all.txt b/requirements_all.txt index 3d01908f0ab..58c89d52b5f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -804,7 +804,7 @@ greenwavereality==0.5.1 gridnet==4.0.0 # homeassistant.components.growatt_server -growattServer==1.2.2 +growattServer==1.2.3 # homeassistant.components.google_sheets gspread==5.5.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f02e27d56a4..299071d7273 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -599,7 +599,7 @@ greeneye_monitor==3.0.3 gridnet==4.0.0 # homeassistant.components.growatt_server -growattServer==1.2.2 +growattServer==1.2.3 # homeassistant.components.google_sheets gspread==5.5.0 From 4b5de420ede840cd0539346c278486827e7f6a7f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 29 Oct 2022 00:24:41 +0000 Subject: [PATCH 0051/1033] [ci skip] Translation update --- .../airthings_ble/translations/nl.json | 6 +-- .../components/apcupsd/translations/bg.json | 1 + .../components/generic/translations/cs.json | 6 +++ .../components/generic/translations/ru.json | 7 +++ .../components/github/translations/it.json | 4 +- .../components/harmony/translations/ca.json | 2 +- .../components/mqtt/translations/it.json | 9 ++-- .../components/mqtt/translations/ru.json | 25 +++++++++- .../components/mqtt/translations/tr.json | 47 +++++++++++++++++-- .../nibe_heatpump/translations/it.json | 4 +- .../nibe_heatpump/translations/ru.json | 9 +++- .../nibe_heatpump/translations/tr.json | 7 ++- .../components/oralb/translations/ru.json | 22 +++++++++ .../plugwise/translations/select.tr.json | 6 +++ .../components/pushover/translations/bg.json | 6 +++ .../components/pushover/translations/ru.json | 4 ++ .../rainmachine/translations/ca.json | 1 + .../rainmachine/translations/tr.json | 1 + .../components/sensibo/translations/it.json | 2 +- .../sensibo/translations/sensor.tr.json | 5 ++ .../components/sensibo/translations/tr.json | 4 +- .../components/sensor/translations/cs.json | 6 ++- .../components/sensor/translations/ru.json | 2 + .../statistics/translations/ru.json | 8 ++++ .../statistics/translations/tr.json | 12 +++++ .../components/uptime/translations/bg.json | 6 +++ .../xiaomi_miio/translations/tr.json | 3 +- .../components/zamg/translations/bg.json | 6 +++ .../components/zamg/translations/tr.json | 26 ++++++++++ 29 files changed, 221 insertions(+), 26 deletions(-) create mode 100644 homeassistant/components/oralb/translations/ru.json create mode 100644 homeassistant/components/statistics/translations/ru.json create mode 100644 homeassistant/components/statistics/translations/tr.json create mode 100644 homeassistant/components/zamg/translations/tr.json diff --git a/homeassistant/components/airthings_ble/translations/nl.json b/homeassistant/components/airthings_ble/translations/nl.json index 19c9a433f99..1e1e61a3b39 100644 --- a/homeassistant/components/airthings_ble/translations/nl.json +++ b/homeassistant/components/airthings_ble/translations/nl.json @@ -10,13 +10,13 @@ "flow_title": "Nederlands", "step": { "bluetooth_confirm": { - "description": "Nederlands" + "description": "Wilt u {name} instellen?" }, "user": { "data": { - "address": "Nederlands" + "address": "Apparaat" }, - "description": "Nederlands" + "description": "Kies een apparaat om in te stellen" } } } diff --git a/homeassistant/components/apcupsd/translations/bg.json b/homeassistant/components/apcupsd/translations/bg.json index 0160e0fee55..da0aaf6631c 100644 --- a/homeassistant/components/apcupsd/translations/bg.json +++ b/homeassistant/components/apcupsd/translations/bg.json @@ -17,6 +17,7 @@ }, "issues": { "deprecated_yaml": { + "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 APC UPS Daemon \u0441 \u043f\u043e\u043c\u043e\u0449\u0442\u0430 \u043d\u0430 YAML \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430.\n\n\u0412\u0430\u0448\u0430\u0442\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0430\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0432 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u041f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 APC UPS Daemon \u043e\u0442 \u0432\u0430\u0448\u0438\u044f \u0444\u0430\u0439\u043b configuration.yaml \u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant, \u0437\u0430 \u0434\u0430 \u043a\u043e\u0440\u0438\u0433\u0438\u0440\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.", "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 APC UPS Daemon \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430" } } diff --git a/homeassistant/components/generic/translations/cs.json b/homeassistant/components/generic/translations/cs.json index f6756ac66a9..3e7973c2d8d 100644 --- a/homeassistant/components/generic/translations/cs.json +++ b/homeassistant/components/generic/translations/cs.json @@ -18,6 +18,9 @@ "username": "U\u017eivatelsk\u00e9 jm\u00e9no" }, "description": "Zadejte nastaven\u00ed pro p\u0159ipojen\u00ed ke kame\u0159e." + }, + "user_confirm_still": { + "title": "N\u00e1hled" } } }, @@ -26,6 +29,9 @@ "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { + "confirm_still": { + "title": "N\u00e1hled" + }, "content_type": { "data": { "content_type": "Typ obsahu" diff --git a/homeassistant/components/generic/translations/ru.json b/homeassistant/components/generic/translations/ru.json index ad7126d85b6..b40123785dd 100644 --- a/homeassistant/components/generic/translations/ru.json +++ b/homeassistant/components/generic/translations/ru.json @@ -75,6 +75,13 @@ "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { + "confirm_still": { + "data": { + "confirmed_ok": "\u042d\u0442\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0445\u043e\u0440\u043e\u0448\u043e." + }, + "description": "![\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441 \u043a\u0430\u043c\u0435\u0440\u044b]({preview_url})", + "title": "\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440" + }, "content_type": { "data": { "content_type": "\u0422\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e" diff --git a/homeassistant/components/github/translations/it.json b/homeassistant/components/github/translations/it.json index a1cb5006305..08eee73fc1c 100644 --- a/homeassistant/components/github/translations/it.json +++ b/homeassistant/components/github/translations/it.json @@ -10,9 +10,9 @@ "step": { "repositories": { "data": { - "repositories": "Seleziona i repository da tracciare." + "repositories": "Seleziona gli archivi digitali da monitorare." }, - "title": "Configura repository" + "title": "Configura gli archivi digitali" } } } diff --git a/homeassistant/components/harmony/translations/ca.json b/homeassistant/components/harmony/translations/ca.json index e5a15705bac..f5648d99dba 100644 --- a/homeassistant/components/harmony/translations/ca.json +++ b/homeassistant/components/harmony/translations/ca.json @@ -29,7 +29,7 @@ "activity": "Activitat predeterminada a executar quan no se n'especifica cap.", "delay_secs": "Retard entre l'enviament d'ordres." }, - "description": "Ajusta les opcions de Harmony Hub" + "description": "Ajusta les opcions d'Harmony Hub" } } } diff --git a/homeassistant/components/mqtt/translations/it.json b/homeassistant/components/mqtt/translations/it.json index 33886d83086..b9504a80a03 100644 --- a/homeassistant/components/mqtt/translations/it.json +++ b/homeassistant/components/mqtt/translations/it.json @@ -11,6 +11,7 @@ "bad_client_cert_key": "Il certificato del client e il privato non sono accoppiati in modo valido", "bad_client_key": "Chiave privata non valida, assicurarsi che venga fornito un file codificato PEM senza password", "bad_discovery_prefix": "Prefisso di ricerca non valido", + "bad_will": "Argomento testamento non valido", "cannot_connect": "Impossibile connettersi", "invalid_inclusion": "Il certificato del client e la chiave privata devono essere configurati insieme" }, @@ -72,19 +73,19 @@ "title": "La/e tua/e {platform} MQTT configurata/e manualmente ha/hanno bisogno di attenzione" }, "deprecated_yaml_broker_settings": { - "description": "Le seguenti impostazioni trovate in 'configuration.yaml' sono state migrate alla voce di configurazione MQTT e ora sovrascriveranno le impostazioni in 'configuration.yaml':\n'{deprecated_settings}'\n\nRimuovi queste impostazioni da 'configuration.yaml' e riavvia Home Assistant per risolvere il problema. Per ulteriori informazioni, vedere la [documentazione]({more_info_url}).", + "description": "Le seguenti impostazioni trovate in 'configuration.yaml' sono state migrate alla voce di configurazione MQTT e ora sovrascriveranno le impostazioni in 'configuration.yaml':\n`{deprecated_settings}`\n\nRimuovi queste impostazioni da 'configuration.yaml' e riavvia Home Assistant per risolvere il problema. Per ulteriori informazioni, vedere la [documentazione]({more_info_url}).", "title": "Impostazioni MQTT obsolete trovate in `configuration.yaml`" } }, "options": { "error": { - "bad_birth": "Argomento birth non valido.", + "bad_birth": "Argomento di nascita non valido", "bad_certificate": "Certificato CA non valido", "bad_client_cert": "Certificato cliente non valido, assicurarsi che venga fornito un file con codice PEM", "bad_client_cert_key": "Il certificato del client e il certificato privato non sono accoppiati in modo valido", "bad_client_key": "Chiave privata non valida, assicurarsi che venga fornito un file codificato PEM senza password", "bad_discovery_prefix": "Prefisso di ricerca non valido", - "bad_will": "Argomento will non valido.", + "bad_will": "Argomento di testamento non valido", "cannot_connect": "Impossibile connettersi", "invalid_inclusion": "Il certificato e la chiave privata del client devono essere configurati insieme" }, @@ -124,7 +125,7 @@ "will_retain": "Persistenza del messaggio will", "will_topic": "Argomento del messaggio will" }, - "description": "Rilevamento: se il rilevamento \u00e8 abilitato (consigliato), Home Assistant rilever\u00e0 automaticamente i dispositivi e le entit\u00e0 che pubblicano la loro configurazione sul broker MQTT. Se il rilevamento \u00e8 disabilitato, tutta la configurazione deve essere eseguita manualmente.\nMessaggio di nascita: il messaggio di nascita verr\u00e0 inviato ogni volta che Home Assistant si (ri)collega al broker MQTT.\nMessaggio testamento: Il messaggio testamento verr\u00e0 inviato ogni volta che Home Assistant perde la connessione al broker, sia in caso di buona (es. arresto di Home Assistant) sia in caso di cattiva (es. Home Assistant in crash o perdita della connessione di rete) disconnessione.", + "description": "Rilevamento - Se il rilevamento \u00e8 abilitato (consigliato), Home Assistant rilever\u00e0 automaticamente i dispositivi e le entit\u00e0 che pubblicano la loro configurazione sul broker MQTT. Se il rilevamento \u00e8 disabilitato, tutta la configurazione deve essere fatta manualmente.\nPrefisso di rilevamento - Il prefisso con cui deve iniziare un argomento di configurazione per il rilevamento automatico.\nMessaggio di nascita - Il messaggio di nascita viene inviato ogni volta che Home Assistant si (ri)connette al broker MQTT.\nMessaggio di testamento - Il messaggio di testamento verr\u00e0 inviato ogni volta che Home Assistant perde la connessione al broker, sia in caso di disconnessione pulita (ad esempio, Home Assistant si spegne) sia in caso di disconnessione non pulita (ad esempio, Home Assistant si blocca o perde la connessione di rete).", "title": "Opzioni MQTT" } } diff --git a/homeassistant/components/mqtt/translations/ru.json b/homeassistant/components/mqtt/translations/ru.json index 0e8ff8255e3..9c037b97b23 100644 --- a/homeassistant/components/mqtt/translations/ru.json +++ b/homeassistant/components/mqtt/translations/ru.json @@ -67,18 +67,40 @@ "button_triple_press": "{subtype} \u043d\u0430\u0436\u0430\u0442\u0430 \u0442\u0440\u0438 \u0440\u0430\u0437\u0430" } }, + "issues": { + "deprecated_yaml_broker_settings": { + "description": "\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0435 \u0432 `configuration.yaml`, \u0431\u044b\u043b\u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043d\u044b \u0432 \u0437\u0430\u043f\u0438\u0441\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 MQTT \u0438 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432 `configuration.yaml`:\n`{deprecated_settings}` \n\n\u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u044d\u0442\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438\u0437 `configuration.yaml` \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443. \u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439]({more_info_url}).", + "title": "\u0423\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b MQTT \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 `configuration.yaml`" + } + }, "options": { "error": { "bad_birth": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u043e\u043f\u0438\u043a \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438.", + "bad_certificate": "\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u0426\u0421 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u0435\u043d.", + "bad_client_cert": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0444\u0430\u0439\u043b, \u0437\u0430\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 PEM.", + "bad_client_cert_key": "\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u043d\u0435 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0430\u0440\u043e\u0439.", + "bad_client_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0444\u0430\u0439\u043b \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 PEM \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f.", + "bad_discovery_prefix": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u0442\u043e\u043f\u0438\u043a\u0430 \u0430\u0432\u0442\u043e\u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f.", "bad_will": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u043e\u043f\u0438\u043a \u043e\u0431 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438.", - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_inclusion": "\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0432\u043c\u0435\u0441\u0442\u0435." }, "step": { "broker": { "data": { + "advanced_options": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438", "broker": "\u0411\u0440\u043e\u043a\u0435\u0440", + "certificate": "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0426\u0421", + "client_cert": "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430", + "client_id": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 (\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c)", + "client_key": "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430", + "keepalive": "\u0412\u0440\u0435\u043c\u044f \u043c\u0435\u0436\u0434\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 Keep Alive", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "port": "\u041f\u043e\u0440\u0442", + "protocol": "\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b MQTT", + "set_ca_cert": "\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0431\u0440\u043e\u043a\u0435\u0440\u0430", + "set_client_cert": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430", + "tls_insecure": "\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0431\u0440\u043e\u043a\u0435\u0440\u0430", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a \u0412\u0430\u0448\u0435\u043c\u0443 \u0431\u0440\u043e\u043a\u0435\u0440\u0443 MQTT.", @@ -92,6 +114,7 @@ "birth_retain": "\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0442\u043e\u043f\u0438\u043a \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438", "birth_topic": "\u0422\u043e\u043f\u0438\u043a \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 (LWT)", "discovery": "\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435", + "discovery_prefix": "\u041f\u0440\u0435\u0444\u0438\u043a\u0441 \u0430\u0432\u0442\u043e\u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f", "will_enable": "\u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0442\u043e\u043f\u0438\u043a \u043e\u0431 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438", "will_payload": "\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0442\u043e\u043f\u0438\u043a\u0430 \u043e\u0431 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438", "will_qos": "QoS \u0442\u043e\u043f\u0438\u043a\u0430 \u043e\u0431 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438", diff --git a/homeassistant/components/mqtt/translations/tr.json b/homeassistant/components/mqtt/translations/tr.json index eda134d8220..c6ed1221a05 100644 --- a/homeassistant/components/mqtt/translations/tr.json +++ b/homeassistant/components/mqtt/translations/tr.json @@ -5,15 +5,33 @@ "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." }, "error": { - "cannot_connect": "Ba\u011flanma hatas\u0131" + "bad_birth": "Ge\u00e7ersiz do\u011fum konusu", + "bad_certificate": "CA sertifikas\u0131 ge\u00e7ersiz", + "bad_client_cert": "Ge\u00e7ersiz m\u00fc\u015fteri sertifikas\u0131, PEM kodlu bir dosyan\u0131n sa\u011fland\u0131\u011f\u0131ndan emin olun", + "bad_client_cert_key": "\u0130stemci sertifikas\u0131 ve \u00f6zel ge\u00e7erli bir \u00e7ift de\u011fil", + "bad_client_key": "Ge\u00e7ersiz \u00f6zel anahtar, PEM kodlu bir dosyan\u0131n parola olmadan sa\u011fland\u0131\u011f\u0131ndan emin olun", + "bad_discovery_prefix": "Ge\u00e7ersiz ke\u015fif \u00f6neki", + "bad_will": "Ge\u00e7ersiz irade konusu", + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_inclusion": "\u0130stemci sertifikas\u0131 ve \u00f6zel anahtar birlikte yap\u0131land\u0131r\u0131lmal\u0131d\u0131r" }, "step": { "broker": { "data": { + "advanced_options": "Geli\u015fmi\u015f se\u00e7enekler", "broker": "Broker", + "certificate": "\u00d6zel CA sertifika dosyas\u0131n\u0131n yolu", + "client_cert": "\u0130stemci sertifika dosyas\u0131n\u0131n yolu", + "client_id": "M\u00fc\u015fteri Kimli\u011fi (rastgele olu\u015fturulmu\u015f olana kadar bo\u015f b\u0131rak\u0131n)", + "client_key": "\u00d6zel anahtar dosyas\u0131n\u0131n yolu", "discovery": "Ke\u015ffetmeyi etkinle\u015ftir", + "keepalive": "Canl\u0131 tutma mesajlar\u0131 g\u00f6nderme aras\u0131ndaki s\u00fcre", "password": "Parola", "port": "Port", + "protocol": "MQTT protokol\u00fc", + "set_ca_cert": "Arac\u0131 sertifikas\u0131 do\u011frulama", + "set_client_cert": "\u0130stemci sertifikas\u0131 kullan", + "tls_insecure": "Arac\u0131 sertifika do\u011frulamas\u0131n\u0131 yoksay", "username": "Kullan\u0131c\u0131 Ad\u0131" }, "description": "L\u00fctfen MQTT brokerinizin ba\u011flant\u0131 bilgilerini girin." @@ -53,20 +71,40 @@ "deprecated_yaml": { "description": "El ile yap\u0131land\u0131r\u0131lm\u0131\u015f MQTT {platform} (lar) ` {platform} ` platform anahtar\u0131 alt\u0131nda bulundu. \n\n Bu sorunu gidermek i\u00e7in l\u00fctfen yap\u0131land\u0131rmay\u0131 `mqtt` entegrasyon anahtar\u0131na ta\u015f\u0131y\u0131n ve Home Assistant'\u0131 yeniden ba\u015flat\u0131n. Daha fazla bilgi i\u00e7in [belgelere]( {more_info_url} ) bak\u0131n.", "title": "Manuel olarak yap\u0131land\u0131r\u0131lan MQTT {platform} (lar)\u0131n\u0131zla ilgilenilmesi gerekiyor" + }, + "deprecated_yaml_broker_settings": { + "description": "\"configuration.yaml\" dosyas\u0131nda bulunan a\u015fa\u011f\u0131daki ayarlar MQTT yap\u0131land\u0131rma giri\u015fine ta\u015f\u0131nd\u0131 ve \u015fimdi \"configuration.yaml\" i\u00e7indeki ayarlar\u0131 ge\u00e7ersiz k\u0131lacakt\u0131r:\n ` {deprecated_settings} ` \n\n L\u00fctfen bu ayarlar\u0131 `configuration.yaml` dosyas\u0131ndan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n. Daha fazla bilgi i\u00e7in [belgelere]( {more_info_url} ) bak\u0131n.", + "title": "\"configuration.yaml\" i\u00e7inde bulunan kullan\u0131mdan kald\u0131r\u0131lm\u0131\u015f MQTT ayarlar\u0131" } }, "options": { "error": { "bad_birth": "Ge\u00e7ersiz do\u011fum konusu.", - "bad_will": "Ge\u00e7ersiz olacak konu.", - "cannot_connect": "Ba\u011flanma hatas\u0131" + "bad_certificate": "CA sertifikas\u0131 ge\u00e7ersiz", + "bad_client_cert": "Ge\u00e7ersiz m\u00fc\u015fteri sertifikas\u0131, PEM kodlu bir dosyan\u0131n sa\u011fland\u0131\u011f\u0131ndan emin olun", + "bad_client_cert_key": "\u0130stemci sertifikas\u0131 ve \u00f6zel ge\u00e7erli bir \u00e7ift de\u011fil", + "bad_client_key": "Ge\u00e7ersiz \u00f6zel anahtar, PEM kodlu bir dosyan\u0131n parola olmadan sa\u011fland\u0131\u011f\u0131ndan emin olun", + "bad_discovery_prefix": "Ge\u00e7ersiz ke\u015fif \u00f6neki", + "bad_will": "Ge\u00e7ersiz olacak konu", + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_inclusion": "\u0130stemci sertifikas\u0131 ve \u00f6zel anahtar birlikte yap\u0131land\u0131r\u0131lmal\u0131d\u0131r" }, "step": { "broker": { "data": { + "advanced_options": "Geli\u015fmi\u015f se\u00e7enekler", "broker": "Broker", + "certificate": "\u00d6zel CA sertifika dosyas\u0131 y\u00fckleyin", + "client_cert": "\u0130stemci sertifika dosyas\u0131n\u0131 y\u00fckleyin", + "client_id": "M\u00fc\u015fteri Kimli\u011fi (rastgele olu\u015fturulmu\u015f olana kadar bo\u015f b\u0131rak\u0131n)", + "client_key": "\u00d6zel anahtar dosyas\u0131n\u0131 y\u00fckleyin", + "keepalive": "Canl\u0131 tutma mesajlar\u0131 g\u00f6nderme aras\u0131ndaki s\u00fcre", "password": "Parola", "port": "Port", + "protocol": "MQTT protokol\u00fc", + "set_ca_cert": "Arac\u0131 sertifikas\u0131 do\u011frulama", + "set_client_cert": "\u0130stemci sertifikas\u0131 kullan", + "tls_insecure": "Arac\u0131 sertifika do\u011frulamas\u0131n\u0131 yoksay", "username": "Kullan\u0131c\u0131 Ad\u0131" }, "description": "L\u00fctfen MQTT brokerinizin ba\u011flant\u0131 bilgilerini girin.", @@ -80,13 +118,14 @@ "birth_retain": "Do\u011fum mesaj\u0131 saklama", "birth_topic": "Do\u011fum mesaj\u0131 konusu", "discovery": "Ke\u015ffetmeyi etkinle\u015ftir", + "discovery_prefix": "Ke\u015fif \u00f6neki", "will_enable": "Etkinle\u015ftir iletisi", "will_payload": "\u0130leti y\u00fckl\u00fc", "will_qos": "QoS mesaj\u0131 g\u00f6nderecek", "will_retain": "Mesaj korunacak m\u0131", "will_topic": "Mesaj konusu olacak" }, - "description": "Ke\u015fif - Ke\u015fif etkinle\u015ftirilirse (\u00f6nerilir), Home Assistant, yap\u0131land\u0131rmalar\u0131n\u0131 MQTT arac\u0131s\u0131nda yay\u0131nlayan cihazlar\u0131 ve varl\u0131klar\u0131 otomatik olarak ke\u015ffeder. Ke\u015fif devre d\u0131\u015f\u0131 b\u0131rak\u0131l\u0131rsa, t\u00fcm yap\u0131land\u0131rma manuel olarak yap\u0131lmal\u0131d\u0131r.\n Do\u011fum mesaj\u0131 - Do\u011fum mesaj\u0131, Home Assistant (yeniden) MQTT arac\u0131s\u0131na her ba\u011fland\u0131\u011f\u0131nda g\u00f6nderilir.\n Will mesaj\u0131 - Will mesaj\u0131, Home Assistant arac\u0131yla olan ba\u011flant\u0131s\u0131n\u0131 her kaybetti\u011finde, hem temizlik durumunda (\u00f6rn. Home Assistant kapan\u0131yor) hem de kirli bir durumda (\u00f6rn. ba\u011flant\u0131y\u0131 kes.", + "description": "Ke\u015fif - Ke\u015fif etkinle\u015ftirilirse (\u00f6nerilir), Home Assistant, yap\u0131land\u0131rmalar\u0131n\u0131 MQTT arac\u0131s\u0131nda yay\u0131nlayan cihazlar\u0131 ve varl\u0131klar\u0131 otomatik olarak ke\u015ffeder. Ke\u015fif devre d\u0131\u015f\u0131 b\u0131rak\u0131l\u0131rsa, t\u00fcm yap\u0131land\u0131rma manuel olarak yap\u0131lmal\u0131d\u0131r.\n Ke\u015fif \u00f6neki - Otomatik ke\u015fif i\u00e7in bir yap\u0131land\u0131rma konusunun ba\u015flamas\u0131 gereken \u00f6nek.\n Do\u011fum mesaj\u0131 - Do\u011fum mesaj\u0131, Home Assistant (yeniden) MQTT arac\u0131s\u0131na her ba\u011fland\u0131\u011f\u0131nda g\u00f6nderilir.\n Will mesaj\u0131 - Will mesaj\u0131, Home Assistant arac\u0131yla olan ba\u011flant\u0131s\u0131n\u0131 her kaybetti\u011finde, hem temizlik durumunda (\u00f6rn. Home Assistant kapan\u0131yor) hem de kirli bir durumda (\u00f6rn. ba\u011flant\u0131y\u0131 kes.", "title": "MQTT se\u00e7enekleri" } } diff --git a/homeassistant/components/nibe_heatpump/translations/it.json b/homeassistant/components/nibe_heatpump/translations/it.json index e7648a9d6fe..4884c5c3efc 100644 --- a/homeassistant/components/nibe_heatpump/translations/it.json +++ b/homeassistant/components/nibe_heatpump/translations/it.json @@ -4,7 +4,7 @@ "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" }, "error": { - "address": "Indirizzo IP remoto specificato non valido. L'indirizzo deve essere un indirizzo IPV4.", + "address": "Indirizzo remoto specificato non valido. L'indirizzo deve essere un indirizzo IP o un nome host risolvibile.", "address_in_use": "La porta di ascolto selezionata \u00e8 gi\u00e0 in uso su questo sistema.", "model": "Il modello selezionato non sembra supportare il modbus40", "read": "Errore su richiesta di lettura dalla pompa. Verifica la tua \"Porta di lettura remota\" o \"Indirizzo IP remoto\".", @@ -14,7 +14,7 @@ "step": { "user": { "data": { - "ip_address": "Indirizzo IP remoto", + "ip_address": "Indirizzo remoto", "listening_port": "Porta di ascolto locale", "remote_read_port": "Porta di lettura remota", "remote_write_port": "Porta di scrittura remota" diff --git a/homeassistant/components/nibe_heatpump/translations/ru.json b/homeassistant/components/nibe_heatpump/translations/ru.json index e1192c7e08c..72c3e327dd5 100644 --- a/homeassistant/components/nibe_heatpump/translations/ru.json +++ b/homeassistant/components/nibe_heatpump/translations/ru.json @@ -18,7 +18,14 @@ "listening_port": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f", "remote_read_port": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0447\u0442\u0435\u043d\u0438\u044f", "remote_write_port": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0437\u0430\u043f\u0438\u0441\u0438" - } + }, + "data_description": { + "ip_address": "\u0410\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 NibeGW. \u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u0441\u043e \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0430\u0434\u0440\u0435\u0441\u043e\u043c.", + "listening_port": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.", + "remote_read_port": "\u041f\u043e\u0440\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435.", + "remote_write_port": "\u041f\u043e\u0440\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c." + }, + "description": "\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e:\n - \u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e \u043a \u0442\u0435\u043f\u043b\u043e\u0432\u043e\u043c\u0443 \u043d\u0430\u0441\u043e\u0441\u0443.\n - \u0410\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440 MODBUS40 \u0431\u044b\u043b \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0442\u0435\u043f\u043b\u043e\u0432\u043e\u0433\u043e \u043d\u0430\u0441\u043e\u0441\u0430.\n - \u041d\u0430\u0441\u043e\u0441 \u043d\u0435 \u043f\u0435\u0440\u0435\u0448\u0435\u043b \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0442\u0440\u0435\u0432\u043e\u0433\u0438 \u0438\u0437 \u0437\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044f \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u0430 MODBUS40." } } } diff --git a/homeassistant/components/nibe_heatpump/translations/tr.json b/homeassistant/components/nibe_heatpump/translations/tr.json index 3e7c744ca31..53722d2a50a 100644 --- a/homeassistant/components/nibe_heatpump/translations/tr.json +++ b/homeassistant/components/nibe_heatpump/translations/tr.json @@ -4,7 +4,7 @@ "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" }, "error": { - "address": "Ge\u00e7ersiz uzak IP adresi belirtildi. Adres bir IPV4 adresi olmal\u0131d\u0131r.", + "address": "Ge\u00e7ersiz uzak adres belirtildi. Adres bir IP adresi veya \u00e7\u00f6z\u00fclebilir bir ana bilgisayar ad\u0131 olmal\u0131d\u0131r.", "address_in_use": "Se\u00e7ilen dinleme ba\u011flant\u0131 noktas\u0131 bu sistemde zaten kullan\u0131l\u0131yor.", "model": "Se\u00e7ilen model modbus40'\u0131 desteklemiyor gibi g\u00f6r\u00fcn\u00fcyor", "read": "Pompadan okuma iste\u011finde hata. 'Uzaktan okuma ba\u011flant\u0131 noktas\u0131' veya 'Uzak IP adresinizi' do\u011frulay\u0131n.", @@ -14,12 +14,15 @@ "step": { "user": { "data": { - "ip_address": "Uzak IP adresi", + "ip_address": "Uzak adres", "listening_port": "Yerel dinleme ba\u011flant\u0131 noktas\u0131", "remote_read_port": "Uzaktan okuma ba\u011flant\u0131 noktas\u0131", "remote_write_port": "Uzaktan yazma ba\u011flant\u0131 noktas\u0131" }, "data_description": { + "ip_address": "NibeGW biriminin adresi. Cihaz statik bir adresle yap\u0131land\u0131r\u0131lm\u0131\u015f olmal\u0131d\u0131r.", + "listening_port": "NibeGW biriminin veri g\u00f6ndermek \u00fczere yap\u0131land\u0131r\u0131ld\u0131\u011f\u0131 bu sistemdeki yerel ba\u011flant\u0131 noktas\u0131.", + "remote_read_port": "NibeGW biriminin okuma isteklerini dinledi\u011fi ba\u011flant\u0131 noktas\u0131.", "remote_write_port": "NibeGW biriminin yazma isteklerini dinledi\u011fi ba\u011flant\u0131 noktas\u0131." }, "description": "Entegrasyonu yap\u0131land\u0131rmaya \u00e7al\u0131\u015fmadan \u00f6nce \u015funlar\u0131 do\u011frulay\u0131n:\n - NibeGW \u00fcnitesi bir \u0131s\u0131 pompas\u0131na ba\u011fl\u0131d\u0131r.\n - Is\u0131 pompas\u0131 konfig\u00fcrasyonunda MODBUS40 aksesuar\u0131 etkinle\u015ftirildi.\n - Pompa, eksik MODBUS40 aksesuar\u0131 ile ilgili alarm durumuna ge\u00e7medi." diff --git a/homeassistant/components/oralb/translations/ru.json b/homeassistant/components/oralb/translations/ru.json new file mode 100644 index 00000000000..887499e5f2e --- /dev/null +++ b/homeassistant/components/oralb/translations/ru.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", + "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", + "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", + "not_supported": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f." + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name}?" + }, + "user": { + "data": { + "address": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + }, + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/select.tr.json b/homeassistant/components/plugwise/translations/select.tr.json index 9ae8b443ebd..0941019ce07 100644 --- a/homeassistant/components/plugwise/translations/select.tr.json +++ b/homeassistant/components/plugwise/translations/select.tr.json @@ -1,5 +1,11 @@ { "state": { + "plugwise__dhw_mode": { + "auto": "Otomatik", + "boost": "G\u00fc\u00e7l\u00fc", + "comfort": "Konfor", + "off": "Kapal\u0131" + }, "plugwise__regulation_mode": { "bleeding_cold": "So\u011futma", "bleeding_hot": "Is\u0131tma", diff --git a/homeassistant/components/pushover/translations/bg.json b/homeassistant/components/pushover/translations/bg.json index 36e77da587c..d8148026d9b 100644 --- a/homeassistant/components/pushover/translations/bg.json +++ b/homeassistant/components/pushover/translations/bg.json @@ -24,5 +24,11 @@ } } } + }, + "issues": { + "removed_yaml": { + "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Pushover \u0441 \u043f\u043e\u043c\u043e\u0449\u0442\u0430 \u043d\u0430 YAML \u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u043e.\n\n\u0412\u0430\u0448\u0430\u0442\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u043e\u0442 Home Assistant.\n\n\u041f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Pushover \u043e\u0442 \u0432\u0430\u0448\u0438\u044f \u0444\u0430\u0439\u043b configuration.yaml \u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant, \u0437\u0430 \u0434\u0430 \u043a\u043e\u0440\u0438\u0433\u0438\u0440\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.", + "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Pushover \u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u0430" + } } } \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/ru.json b/homeassistant/components/pushover/translations/ru.json index c6a1aff57b0..55fae74e217 100644 --- a/homeassistant/components/pushover/translations/ru.json +++ b/homeassistant/components/pushover/translations/ru.json @@ -29,6 +29,10 @@ "deprecated_yaml": { "description": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Pushover \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", "title": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Pushover \u0447\u0435\u0440\u0435\u0437 YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" + }, + "removed_yaml": { + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \"Pushover\" \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f YAML \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Home Assistant.\n\n\u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", + "title": "\u0423\u0434\u0430\u043b\u0435\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Pushover \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML" } } } \ No newline at end of file diff --git a/homeassistant/components/rainmachine/translations/ca.json b/homeassistant/components/rainmachine/translations/ca.json index e776a0bcdf3..a96548c5f0c 100644 --- a/homeassistant/components/rainmachine/translations/ca.json +++ b/homeassistant/components/rainmachine/translations/ca.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "Utilitza els temps d'execuci\u00f3 de zona de l'aplicaci\u00f3 RainMachine", "zone_run_time": "Temps d'execuci\u00f3 predeterminat de la zona (en segons)" }, "title": "Configuraci\u00f3 de RainMachine" diff --git a/homeassistant/components/rainmachine/translations/tr.json b/homeassistant/components/rainmachine/translations/tr.json index fa181de3505..1b81ef2c09b 100644 --- a/homeassistant/components/rainmachine/translations/tr.json +++ b/homeassistant/components/rainmachine/translations/tr.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "RainMachine uygulamas\u0131ndan b\u00f6lge \u00e7al\u0131\u015ft\u0131rma s\u00fcrelerini kullan\u0131n", "zone_run_time": "Varsay\u0131lan b\u00f6lge \u00e7al\u0131\u015fma s\u00fcresi (saniye cinsinden)" }, "title": "RainMachine'i konf\u0131g\u00fcre et" diff --git a/homeassistant/components/sensibo/translations/it.json b/homeassistant/components/sensibo/translations/it.json index f2c45f9b5ae..8b34b5eaf50 100644 --- a/homeassistant/components/sensibo/translations/it.json +++ b/homeassistant/components/sensibo/translations/it.json @@ -17,7 +17,7 @@ "api_key": "Chiave API" }, "data_description": { - "api_key": "Segui la documentazione per ottenere una nuova chiave API." + "api_key": "Segui la documentazione per ottenere la tua chiave API." } }, "user": { diff --git a/homeassistant/components/sensibo/translations/sensor.tr.json b/homeassistant/components/sensibo/translations/sensor.tr.json index 3364a75abe2..bb9f87e0063 100644 --- a/homeassistant/components/sensibo/translations/sensor.tr.json +++ b/homeassistant/components/sensibo/translations/sensor.tr.json @@ -3,6 +3,11 @@ "sensibo__sensitivity": { "n": "Normal", "s": "Duyarl\u0131" + }, + "sensibo__smart_type": { + "feelslike": "Hissedilen", + "humidity": "Nem", + "temperature": "S\u0131cakl\u0131k" } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/tr.json b/homeassistant/components/sensibo/translations/tr.json index 47270a1dece..fc2abb330e4 100644 --- a/homeassistant/components/sensibo/translations/tr.json +++ b/homeassistant/components/sensibo/translations/tr.json @@ -17,7 +17,7 @@ "api_key": "API Anahtar\u0131" }, "data_description": { - "api_key": "Yeni bir api anahtar\u0131 almak i\u00e7in belgeleri izleyin." + "api_key": "API anahtar\u0131n\u0131z\u0131 almak i\u00e7in belgeleri izleyin" } }, "user": { @@ -25,7 +25,7 @@ "api_key": "API Anahtar\u0131" }, "data_description": { - "api_key": "API anahtar\u0131n\u0131z\u0131 almak i\u00e7in belgeleri izleyin." + "api_key": "API anahtar\u0131n\u0131z\u0131 almak i\u00e7in belgeleri izleyin" } } } diff --git a/homeassistant/components/sensor/translations/cs.json b/homeassistant/components/sensor/translations/cs.json index d8f6dd25b57..240f03942ce 100644 --- a/homeassistant/components/sensor/translations/cs.json +++ b/homeassistant/components/sensor/translations/cs.json @@ -18,7 +18,8 @@ "is_temperature": "Aktu\u00e1ln\u00ed teplota {entity_name}", "is_value": "Aktu\u00e1ln\u00ed hodnota {entity_name}", "is_voltage": "Aktu\u00e1ln\u00ed nap\u011bt\u00ed {entity_name}", - "is_volume": "Aktu\u00e1ln\u00ed objem {entity_name}" + "is_volume": "Aktu\u00e1ln\u00ed objem {entity_name}", + "is_water": "Aktu\u00e1ln\u00ed mno\u017estv\u00ed vody {entity_name}" }, "trigger_type": { "battery_level": "P\u0159i zm\u011bn\u011b \u00farovn\u011b baterie {entity_name}", @@ -38,7 +39,8 @@ "temperature": "P\u0159i zm\u011bn\u011b teploty {entity_name}", "value": "P\u0159i zm\u011bn\u011b hodnoty {entity_name}", "voltage": "P\u0159i zm\u011bn\u011b nap\u011bt\u00ed {entity_name}", - "volume": "P\u0159i zm\u011bn\u011b objemu {entity_name}" + "volume": "P\u0159i zm\u011bn\u011b objemu {entity_name}", + "water": "P\u0159i zm\u011bn\u011b mno\u017estv\u00ed vody {entity_name}" } }, "state": { diff --git a/homeassistant/components/sensor/translations/ru.json b/homeassistant/components/sensor/translations/ru.json index 9b7a1f7dbe8..1961ef86ca0 100644 --- a/homeassistant/components/sensor/translations/ru.json +++ b/homeassistant/components/sensor/translations/ru.json @@ -32,6 +32,7 @@ "is_volatile_organic_compounds": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0443\u0440\u043e\u0432\u043d\u044f \u043a\u043e\u043d\u0446\u0435\u043d\u0442\u0440\u0430\u0446\u0438\u0438 \u043b\u0435\u0442\u0443\u0447\u0438\u0445 \u043e\u0440\u0433\u0430\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0432\u0435\u0449\u0435\u0441\u0442\u0432", "is_voltage": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0430\u043f\u0440\u044f\u0436\u0435\u043d\u0438\u044f", "is_volume": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", + "is_water": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_weight": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435" }, "trigger_type": { @@ -66,6 +67,7 @@ "volatile_organic_compounds": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0446\u0435\u043d\u0442\u0440\u0430\u0446\u0438\u0438 \u043b\u0435\u0442\u0443\u0447\u0438\u0445 \u043e\u0440\u0433\u0430\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0432\u0435\u0449\u0435\u0441\u0442\u0432", "voltage": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0430\u043f\u0440\u044f\u0436\u0435\u043d\u0438\u044f", "volume": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u043e\u0431\u044a\u0451\u043c", + "water": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "weight": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0432\u0435\u0441" } }, diff --git a/homeassistant/components/statistics/translations/ru.json b/homeassistant/components/statistics/translations/ru.json new file mode 100644 index 00000000000..10cc86dbda5 --- /dev/null +++ b/homeassistant/components/statistics/translations/ru.json @@ -0,0 +1,8 @@ +{ + "issues": { + "deprecation_warning_characteristic": { + "description": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438 `state_characteristic` \u0441\u0442\u0430\u043d\u0435\u0442 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c. \n\n\u0427\u0442\u043e\u0431\u044b \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u043a\u0430\u043a \u0438 \u043f\u0440\u0435\u0436\u0434\u0435, \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 `state_characteristic: {characteristic}` \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0441\u0435\u043d\u0441\u043e\u0440\u0430 `{entity}`. \n\n\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439: https://www.home-assistant.io/integrations/statistics/", + "title": "\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 'state_characteristic', \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u043c\u044b\u0439 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/statistics/translations/tr.json b/homeassistant/components/statistics/translations/tr.json new file mode 100644 index 00000000000..8a178c28404 --- /dev/null +++ b/homeassistant/components/statistics/translations/tr.json @@ -0,0 +1,12 @@ +{ + "issues": { + "deprecation_warning_characteristic": { + "description": "\u0130statistik entegrasyonunun 'state_characteristic' yap\u0131land\u0131rma parametresi zorunlu hale gelecektir. \n\n Mevcut davran\u0131\u015f\u0131 korumak i\u00e7in l\u00fctfen ` {entity} ` sens\u00f6r\u00fcn\u00fcn yap\u0131land\u0131rmas\u0131na `state_characteristic: {characteristic} ` ekleyin. \n\n Daha fazla ayr\u0131nt\u0131 i\u00e7in istatistik entegrasyonunun belgelerini okuyun: https://www.home-assistant.io/integrations/statistics/", + "title": "Bir \u0130statistik varl\u0131\u011f\u0131 i\u00e7in zorunlu 'state_characteristic' varsay\u0131ld\u0131" + }, + "deprecation_warning_size": { + "description": "\u0130statistik entegrasyonunun 'sampling_size' yap\u0131land\u0131rma parametresi \u015fu ana kadar varsay\u0131lan olarak 20 de\u011ferine ayarlanm\u0131\u015ft\u0131r ve bu de\u011fi\u015fecektir. \n\n L\u00fctfen ` {entity} ` sens\u00f6r\u00fcn\u00fcn yap\u0131land\u0131rmas\u0131n\u0131 kontrol edin ve mevcut davran\u0131\u015f\u0131 korumak i\u00e7in \u00f6rne\u011fin `sampling_size: 20` gibi uygun s\u0131n\u0131rlar ekleyin. \u0130statistik entegrasyonunun yap\u0131land\u0131rmas\u0131, 2022.12.0 s\u00fcr\u00fcm\u00fcyle daha esnek hale gelecek ve \"sampling_size\" veya \"max_age\" veya her iki ayar\u0131 da kabul edecektir. Yukar\u0131daki istek, yap\u0131land\u0131rman\u0131z\u0131 bu aksi takdirde bozulan de\u011fi\u015fiklik i\u00e7in haz\u0131rlar. \n\n Daha fazla ayr\u0131nt\u0131 i\u00e7in istatistik entegrasyonunun belgelerini okuyun: https://www.home-assistant.io/integrations/statistics/", + "title": "Bir \u0130statistik varl\u0131\u011f\u0131 i\u00e7in varsay\u0131lan \u00f6rt\u00fck 'sampling_size'" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/uptime/translations/bg.json b/homeassistant/components/uptime/translations/bg.json index 1290144ec04..8a6bd7e5072 100644 --- a/homeassistant/components/uptime/translations/bg.json +++ b/homeassistant/components/uptime/translations/bg.json @@ -8,5 +8,11 @@ "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u0437\u0430\u043f\u043e\u0447\u043d\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0432\u0430\u043d\u0435\u0442\u043e?" } } + }, + "issues": { + "removed_yaml": { + "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Uptime \u0441 \u043f\u043e\u043c\u043e\u0449\u0442\u0430 \u043d\u0430 YAML \u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u043e.\n\n\u0412\u0430\u0448\u0430\u0442\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u043e\u0442 Home Assistant.\n\n\u041f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043e\u0442 \u0432\u0430\u0448\u0438\u044f \u0444\u0430\u0439\u043b configuration.yaml \u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant, \u0437\u0430 \u0434\u0430 \u043a\u043e\u0440\u0438\u0433\u0438\u0440\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.", + "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Uptime \u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u0430" + } } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/tr.json b/homeassistant/components/xiaomi_miio/translations/tr.json index 8c81f08ee9f..ebd9ce377c8 100644 --- a/homeassistant/components/xiaomi_miio/translations/tr.json +++ b/homeassistant/components/xiaomi_miio/translations/tr.json @@ -5,7 +5,8 @@ "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", "incomplete_info": "Kurulum cihaz\u0131 i\u00e7in eksik bilgi, ana bilgisayar veya anahtar sa\u011flanmad\u0131.", "not_xiaomi_miio": "Cihaz (hen\u00fcz) Xiaomi Miio taraf\u0131ndan desteklenmiyor.", - "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu", + "unknown": "Beklenmeyen hata" }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", diff --git a/homeassistant/components/zamg/translations/bg.json b/homeassistant/components/zamg/translations/bg.json index c6e5cbe9ec1..8e7a1467b4b 100644 --- a/homeassistant/components/zamg/translations/bg.json +++ b/homeassistant/components/zamg/translations/bg.json @@ -8,5 +8,11 @@ "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "flow_title": "{name}" + }, + "issues": { + "deprecated_yaml": { + "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 ZAMG \u0441 \u043f\u043e\u043c\u043e\u0449\u0442\u0430 \u043d\u0430 YAML \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430.\n\n\u0412\u0430\u0448\u0430\u0442\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0430\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0432 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u041f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 ZAMG \u043e\u0442 \u0432\u0430\u0448\u0438\u044f \u0444\u0430\u0439\u043b configuration.yaml \u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant, \u0437\u0430 \u0434\u0430 \u043a\u043e\u0440\u0438\u0433\u0438\u0440\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.", + "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 ZAMG \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430" + } } } \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/tr.json b/homeassistant/components/zamg/translations/tr.json new file mode 100644 index 00000000000..3ebc2a2788b --- /dev/null +++ b/homeassistant/components/zamg/translations/tr.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "cannot_connect": "Ba\u011flanma hatas\u0131" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "\u0130stasyon Kimli\u011fi (Varsay\u0131lan olarak en yak\u0131n istasyon)" + }, + "description": "ZAMG'yi Home Assistant ile entegre olacak \u015fekilde ayarlay\u0131n." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "ZAMG'nin YAML kullan\u0131larak yap\u0131land\u0131r\u0131lmas\u0131 kald\u0131r\u0131l\u0131yor. \n\n Mevcut YAML yap\u0131land\u0131rman\u0131z otomatik olarak kullan\u0131c\u0131 aray\u00fcz\u00fcne aktar\u0131ld\u0131. \n\n ZAMG YAML yap\u0131land\u0131rmas\u0131n\u0131 configuration.yaml dosyan\u0131zdan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", + "title": "ZAMG YAML yap\u0131land\u0131rmas\u0131 kald\u0131r\u0131l\u0131yor" + } + } +} \ No newline at end of file From e47f06fde8e47acbc891dfbaff39bebb8f8e5ba6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 28 Oct 2022 20:01:03 -0500 Subject: [PATCH 0052/1033] Bump dbus-fast to 1.54.0 (#81148) * Bump dbus-fast to 1.53.0 changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v1.51.0...v1.53.0 * 54 --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 60b260baf36..a706d777bc6 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.4.2", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", - "dbus-fast==1.51.0" + "dbus-fast==1.54.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 79e6340ed41..d8aef241616 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.1 -dbus-fast==1.51.0 +dbus-fast==1.54.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 58c89d52b5f..6566fa2c7f6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -540,7 +540,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.51.0 +dbus-fast==1.54.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 299071d7273..d79d77dc5a7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -420,7 +420,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.51.0 +dbus-fast==1.54.0 # homeassistant.components.debugpy debugpy==1.6.3 From 77585686e030c10bb85fe95886706e4709cca46e Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Sat, 29 Oct 2022 03:01:53 +0200 Subject: [PATCH 0053/1033] Allow empty string for filters for waze_travel_time (#80953) * Allow empty string for filters Signed-off-by: Kevin Stillhammer * Apply PR feedback Signed-off-by: Kevin Stillhammer Signed-off-by: Kevin Stillhammer --- .../waze_travel_time/config_flow.py | 2 +- .../components/waze_travel_time/sensor.py | 4 +- .../waze_travel_time/test_config_flow.py | 54 +++++++++++++++++-- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/waze_travel_time/config_flow.py b/homeassistant/components/waze_travel_time/config_flow.py index fd6747cc1c8..b26732e4cb1 100644 --- a/homeassistant/components/waze_travel_time/config_flow.py +++ b/homeassistant/components/waze_travel_time/config_flow.py @@ -52,7 +52,7 @@ class WazeOptionsFlow(config_entries.OptionsFlow): if user_input is not None: return self.async_create_entry( title="", - data={k: v for k, v in user_input.items() if v not in (None, "")}, + data=user_input, ) return self.async_show_form( diff --git a/homeassistant/components/waze_travel_time/sensor.py b/homeassistant/components/waze_travel_time/sensor.py index 942c1bccb36..c8d3e308435 100644 --- a/homeassistant/components/waze_travel_time/sensor.py +++ b/homeassistant/components/waze_travel_time/sensor.py @@ -185,14 +185,14 @@ class WazeTravelTimeData: ) routes = params.calc_all_routes_info(real_time=realtime) - if incl_filter is not None: + if incl_filter not in {None, ""}: routes = { k: v for k, v in routes.items() if incl_filter.lower() in k.lower() } - if excl_filter is not None: + if excl_filter not in {None, ""}: routes = { k: v for k, v in routes.items() diff --git a/tests/components/waze_travel_time/test_config_flow.py b/tests/components/waze_travel_time/test_config_flow.py index 51bf1ae8319..d58f8d9a34d 100644 --- a/tests/components/waze_travel_time/test_config_flow.py +++ b/tests/components/waze_travel_time/test_config_flow.py @@ -19,6 +19,7 @@ from homeassistant.components.waze_travel_time.const import ( IMPERIAL_UNITS, ) from homeassistant.const import CONF_NAME, CONF_REGION +from homeassistant.core import HomeAssistant from .const import MOCK_CONFIG @@ -26,7 +27,7 @@ from tests.common import MockConfigEntry @pytest.mark.usefixtures("validate_config_entry") -async def test_minimum_fields(hass): +async def test_minimum_fields(hass: HomeAssistant) -> None: """Test we get the form.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -50,7 +51,7 @@ async def test_minimum_fields(hass): } -async def test_options(hass): +async def test_options(hass: HomeAssistant) -> None: """Test options flow.""" entry = MockConfigEntry( domain=DOMAIN, @@ -105,7 +106,7 @@ async def test_options(hass): @pytest.mark.usefixtures("validate_config_entry") -async def test_dupe(hass): +async def test_dupe(hass: HomeAssistant) -> None: """Test setting up the same entry data twice is OK.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -138,7 +139,9 @@ async def test_dupe(hass): @pytest.mark.usefixtures("invalidate_config_entry") -async def test_invalid_config_entry(hass, caplog): +async def test_invalid_config_entry( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: """Test we get the form.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -154,3 +157,46 @@ async def test_invalid_config_entry(hass, caplog): assert result2["errors"] == {"base": "cannot_connect"} assert "Error trying to validate entry" in caplog.text + + +@pytest.mark.usefixtures("mock_update") +async def test_reset_filters(hass: HomeAssistant) -> None: + """Test resetting inclusive and exclusive filters to empty string.""" + options = {**DEFAULT_OPTIONS} + options[CONF_INCL_FILTER] = "test" + options[CONF_EXCL_FILTER] = "test" + config_entry = MockConfigEntry( + domain=DOMAIN, data=MOCK_CONFIG, options=options, entry_id="test" + ) + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + result = await hass.config_entries.options.async_init( + config_entry.entry_id, data=None + ) + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_AVOID_FERRIES: True, + CONF_AVOID_SUBSCRIPTION_ROADS: True, + CONF_AVOID_TOLL_ROADS: True, + CONF_EXCL_FILTER: "", + CONF_INCL_FILTER: "", + CONF_REALTIME: False, + CONF_UNITS: IMPERIAL_UNITS, + CONF_VEHICLE_TYPE: "taxi", + }, + ) + + assert config_entry.options == { + CONF_AVOID_FERRIES: True, + CONF_AVOID_SUBSCRIPTION_ROADS: True, + CONF_AVOID_TOLL_ROADS: True, + CONF_EXCL_FILTER: "", + CONF_INCL_FILTER: "", + CONF_REALTIME: False, + CONF_UNITS: IMPERIAL_UNITS, + CONF_VEHICLE_TYPE: "taxi", + } From 9dedbb2a24aeda96a325375a0dab8508d4f3589b Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Sat, 29 Oct 2022 05:05:21 +0300 Subject: [PATCH 0054/1033] Add diagnostics to Switcher (#81146) --- .../components/switcher_kis/diagnostics.py | 28 +++++++++ .../switcher_kis/test_diagnostics.py | 59 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 homeassistant/components/switcher_kis/diagnostics.py create mode 100644 tests/components/switcher_kis/test_diagnostics.py diff --git a/homeassistant/components/switcher_kis/diagnostics.py b/homeassistant/components/switcher_kis/diagnostics.py new file mode 100644 index 00000000000..93b3c36bd21 --- /dev/null +++ b/homeassistant/components/switcher_kis/diagnostics.py @@ -0,0 +1,28 @@ +"""Diagnostics support for Switcher.""" +from __future__ import annotations + +from dataclasses import asdict +from typing import Any + +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from .const import DATA_DEVICE, DOMAIN + +TO_REDACT = {"device_id", "ip_address", "mac_address"} + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + devices = hass.data[DOMAIN][DATA_DEVICE] + + return async_redact_data( + { + "entry": entry.as_dict(), + "devices": [asdict(devices[d].data) for d in devices], + }, + TO_REDACT, + ) diff --git a/tests/components/switcher_kis/test_diagnostics.py b/tests/components/switcher_kis/test_diagnostics.py new file mode 100644 index 00000000000..8655ba7ee1f --- /dev/null +++ b/tests/components/switcher_kis/test_diagnostics.py @@ -0,0 +1,59 @@ +"""Tests for the diagnostics data provided by Switcher.""" +from aiohttp import ClientSession + +from homeassistant.components.diagnostics import REDACTED +from homeassistant.core import HomeAssistant + +from . import init_integration +from .consts import DUMMY_WATER_HEATER_DEVICE + +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_diagnostics( + hass: HomeAssistant, hass_client: ClientSession, mock_bridge, monkeypatch +) -> None: + """Test diagnostics.""" + entry = await init_integration(hass) + device = DUMMY_WATER_HEATER_DEVICE + monkeypatch.setattr(device, "last_data_update", "2022-09-28T16:42:12.706017") + mock_bridge.mock_callbacks([device]) + await hass.async_block_till_done() + + assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == { + "devices": [ + { + "auto_shutdown": "02:00:00", + "device_id": REDACTED, + "device_state": { + "__type": "", + "repr": "", + }, + "device_type": { + "__type": "", + "repr": ")>", + }, + "electric_current": 12.8, + "ip_address": REDACTED, + "last_data_update": "2022-09-28T16:42:12.706017", + "mac_address": REDACTED, + "name": "Heater FE12", + "power_consumption": 2780, + "remaining_time": "01:29:32", + } + ], + "entry": { + "entry_id": entry.entry_id, + "version": 1, + "domain": "switcher_kis", + "title": "Mock Title", + "data": {}, + "options": {}, + "pref_disable_new_entities": False, + "pref_disable_polling": False, + "source": "user", + "unique_id": "switcher_kis", + "disabled_by": None, + }, + } From a786b32857cb415e0d0d16dd5f257253afc77278 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 28 Oct 2022 23:58:02 -0400 Subject: [PATCH 0055/1033] Set date in test to fixed one (#81175) --- tests/components/history_stats/test_sensor.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/components/history_stats/test_sensor.py b/tests/components/history_stats/test_sensor.py index b384b7c730b..6bae61b5fd8 100644 --- a/tests/components/history_stats/test_sensor.py +++ b/tests/components/history_stats/test_sensor.py @@ -1388,7 +1388,9 @@ async def test_measure_cet(recorder_mock, hass): async def test_end_time_with_microseconds_zeroed(time_zone, recorder_mock, hass): """Test the history statistics sensor that has the end time microseconds zeroed out.""" hass.config.set_time_zone(time_zone) - start_of_today = dt_util.now().replace(hour=0, minute=0, second=0, microsecond=0) + start_of_today = dt_util.now().replace( + day=9, month=7, year=1986, hour=0, minute=0, second=0, microsecond=0 + ) start_time = start_of_today + timedelta(minutes=60) t0 = start_time + timedelta(minutes=20) t1 = t0 + timedelta(minutes=10) From 1d9bbdcbf3d1d677f50d2c1b6a0f7568a2a20281 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sat, 29 Oct 2022 08:33:04 +0200 Subject: [PATCH 0056/1033] Add integration type to Overkiz integration (#81147) --- homeassistant/components/overkiz/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/overkiz/manifest.json b/homeassistant/components/overkiz/manifest.json index d19495d82a2..3025e8d060e 100644 --- a/homeassistant/components/overkiz/manifest.json +++ b/homeassistant/components/overkiz/manifest.json @@ -2,6 +2,7 @@ "domain": "overkiz", "name": "Overkiz", "config_flow": true, + "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/overkiz", "requirements": ["pyoverkiz==1.5.6"], "zeroconf": [ From 482b5459dc6e875c14c1384abb98a5df46dff9b0 Mon Sep 17 00:00:00 2001 From: mezz64 <2854333+mezz64@users.noreply.github.com> Date: Sat, 29 Oct 2022 03:09:12 -0400 Subject: [PATCH 0057/1033] Bump pyEight to 0.3.2 (#81172) Co-authored-by: J. Nick Koston --- homeassistant/components/eight_sleep/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/eight_sleep/manifest.json b/homeassistant/components/eight_sleep/manifest.json index c1833b222df..4f97b99b2e7 100644 --- a/homeassistant/components/eight_sleep/manifest.json +++ b/homeassistant/components/eight_sleep/manifest.json @@ -2,7 +2,7 @@ "domain": "eight_sleep", "name": "Eight Sleep", "documentation": "https://www.home-assistant.io/integrations/eight_sleep", - "requirements": ["pyeight==0.3.0"], + "requirements": ["pyeight==0.3.2"], "codeowners": ["@mezz64", "@raman325"], "iot_class": "cloud_polling", "loggers": ["pyeight"], diff --git a/requirements_all.txt b/requirements_all.txt index 6566fa2c7f6..0e8a59cb90a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1538,7 +1538,7 @@ pyedimax==0.2.1 pyefergy==22.1.1 # homeassistant.components.eight_sleep -pyeight==0.3.0 +pyeight==0.3.2 # homeassistant.components.emby pyemby==1.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d79d77dc5a7..5ea3e9dd443 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1081,7 +1081,7 @@ pyeconet==0.1.15 pyefergy==22.1.1 # homeassistant.components.eight_sleep -pyeight==0.3.0 +pyeight==0.3.2 # homeassistant.components.everlights pyeverlights==0.1.0 From effe8d9207365fa45b69274a06ff57b5ac154dcf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 29 Oct 2022 02:40:40 -0500 Subject: [PATCH 0058/1033] Bump dbus-fast to 1.56.0 (#81177) * Bump dbus-fast to 1.56.0 Addes optimized readers for manufacturer data and interfaces added messages changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v1.55.0...v1.56.0 * empty --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index a706d777bc6..3ac8ac513c1 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.4.2", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", - "dbus-fast==1.54.0" + "dbus-fast==1.56.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index d8aef241616..b50deca16bd 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.1 -dbus-fast==1.54.0 +dbus-fast==1.56.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 0e8a59cb90a..cfaf5e7fdf5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -540,7 +540,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.54.0 +dbus-fast==1.56.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5ea3e9dd443..7301dd34cbd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -420,7 +420,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.54.0 +dbus-fast==1.56.0 # homeassistant.components.debugpy debugpy==1.6.3 From 591d13bc494b6faef8096bb7a16199319ad2edd1 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sat, 29 Oct 2022 10:22:59 +0200 Subject: [PATCH 0059/1033] Move MQTT CONF_SCHEMA from light and vacuum to .const (#81152) Move CONF_SCHEMA to .const --- homeassistant/components/mqtt/const.py | 1 + homeassistant/components/mqtt/light/schema.py | 2 +- homeassistant/components/mqtt/vacuum/schema.py | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index 1dc25c1e78c..dc8aa2f2c74 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -19,6 +19,7 @@ CONF_ENCODING = "encoding" CONF_KEEPALIVE = "keepalive" CONF_QOS = ATTR_QOS CONF_RETAIN = ATTR_RETAIN +CONF_SCHEMA = "schema" CONF_STATE_TOPIC = "state_topic" CONF_STATE_VALUE_TEMPLATE = "state_value_template" CONF_TOPIC = "topic" diff --git a/homeassistant/components/mqtt/light/schema.py b/homeassistant/components/mqtt/light/schema.py index a7ab5e986a7..6e2ac60b28d 100644 --- a/homeassistant/components/mqtt/light/schema.py +++ b/homeassistant/components/mqtt/light/schema.py @@ -1,7 +1,7 @@ """Shared schema code.""" import voluptuous as vol -CONF_SCHEMA = "schema" +from ..const import CONF_SCHEMA MQTT_LIGHT_SCHEMA_SCHEMA = vol.Schema( { diff --git a/homeassistant/components/mqtt/vacuum/schema.py b/homeassistant/components/mqtt/vacuum/schema.py index 949b5cede9c..d2ed91fa631 100644 --- a/homeassistant/components/mqtt/vacuum/schema.py +++ b/homeassistant/components/mqtt/vacuum/schema.py @@ -1,7 +1,8 @@ """Shared schema code.""" import voluptuous as vol -CONF_SCHEMA = "schema" +from ..const import CONF_SCHEMA + LEGACY = "legacy" STATE = "state" From 74e476a070bb84f12df2c7f25c921cb601b34157 Mon Sep 17 00:00:00 2001 From: Brian Norman Date: Sat, 29 Oct 2022 12:45:00 +0100 Subject: [PATCH 0060/1033] Add optional support for users with multiple ovo accounts (#80901) * Added optional support for users with multiple ovo accounts * Made changes from code review around naming * Renaming config variable * Removing account from async_step_reauth * Removing more account from async_step_reauth * Fixing code now I better understand and can test async_step_reauth * Storing account id returned by ovoenergy client in config to handle when user has single account but then another gets added * Putting account into async_step_user --- .../components/ovo_energy/__init__.py | 10 +++++++--- .../components/ovo_energy/config_flow.py | 20 +++++++++++++++---- homeassistant/components/ovo_energy/const.py | 1 + .../components/ovo_energy/strings.json | 3 ++- .../ovo_energy/translations/en.json | 3 ++- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/ovo_energy/__init__.py b/homeassistant/components/ovo_energy/__init__.py index cb2ded6fcef..cc19b0454b1 100644 --- a/homeassistant/components/ovo_energy/__init__.py +++ b/homeassistant/components/ovo_energy/__init__.py @@ -21,7 +21,7 @@ from homeassistant.helpers.update_coordinator import ( UpdateFailed, ) -from .const import DATA_CLIENT, DATA_COORDINATOR, DOMAIN +from .const import CONF_ACCOUNT, DATA_CLIENT, DATA_COORDINATOR, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -35,7 +35,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: try: authenticated = await client.authenticate( - entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD] + entry.data[CONF_USERNAME], + entry.data[CONF_PASSWORD], + entry.data[CONF_ACCOUNT], ) except aiohttp.ClientError as exception: _LOGGER.warning(exception) @@ -49,7 +51,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async with async_timeout.timeout(10): try: authenticated = await client.authenticate( - entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD] + entry.data[CONF_USERNAME], + entry.data[CONF_PASSWORD], + entry.data[CONF_ACCOUNT], ) except aiohttp.ClientError as exception: raise UpdateFailed(exception) from exception diff --git a/homeassistant/components/ovo_energy/config_flow.py b/homeassistant/components/ovo_energy/config_flow.py index f4dcc5301d7..9e406ce6b96 100644 --- a/homeassistant/components/ovo_energy/config_flow.py +++ b/homeassistant/components/ovo_energy/config_flow.py @@ -6,11 +6,15 @@ import voluptuous as vol from homeassistant.config_entries import ConfigFlow from homeassistant.const import CONF_PASSWORD, CONF_USERNAME -from .const import DOMAIN +from .const import CONF_ACCOUNT, DOMAIN REAUTH_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str}) USER_SCHEMA = vol.Schema( - {vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str} + { + vol.Required(CONF_USERNAME): str, + vol.Required(CONF_PASSWORD): str, + vol.Optional(CONF_ACCOUNT): str, + } ) @@ -22,6 +26,7 @@ class OVOEnergyFlowHandler(ConfigFlow, domain=DOMAIN): def __init__(self): """Initialize the flow.""" self.username = None + self.account = None async def async_step_user(self, user_input=None): """Handle a flow initiated by the user.""" @@ -30,7 +35,9 @@ class OVOEnergyFlowHandler(ConfigFlow, domain=DOMAIN): client = OVOEnergy() try: authenticated = await client.authenticate( - user_input[CONF_USERNAME], user_input[CONF_PASSWORD] + user_input[CONF_USERNAME], + user_input[CONF_PASSWORD], + user_input.get(CONF_ACCOUNT, None), ) except aiohttp.ClientError: errors["base"] = "cannot_connect" @@ -44,6 +51,7 @@ class OVOEnergyFlowHandler(ConfigFlow, domain=DOMAIN): data={ CONF_USERNAME: user_input[CONF_USERNAME], CONF_PASSWORD: user_input[CONF_PASSWORD], + CONF_ACCOUNT: client.account_id, }, ) @@ -60,13 +68,16 @@ class OVOEnergyFlowHandler(ConfigFlow, domain=DOMAIN): if user_input and user_input.get(CONF_USERNAME): self.username = user_input[CONF_USERNAME] + if user_input and user_input.get(CONF_ACCOUNT): + self.account = user_input[CONF_ACCOUNT] + self.context["title_placeholders"] = {CONF_USERNAME: self.username} if user_input is not None and user_input.get(CONF_PASSWORD) is not None: client = OVOEnergy() try: authenticated = await client.authenticate( - self.username, user_input[CONF_PASSWORD] + self.username, user_input[CONF_PASSWORD], self.account ) except aiohttp.ClientError: errors["base"] = "connection_error" @@ -78,6 +89,7 @@ class OVOEnergyFlowHandler(ConfigFlow, domain=DOMAIN): data={ CONF_USERNAME: self.username, CONF_PASSWORD: user_input[CONF_PASSWORD], + CONF_ACCOUNT: self.account, }, ) return self.async_abort(reason="reauth_successful") diff --git a/homeassistant/components/ovo_energy/const.py b/homeassistant/components/ovo_energy/const.py index f691eb9bc49..1068c5443fd 100644 --- a/homeassistant/components/ovo_energy/const.py +++ b/homeassistant/components/ovo_energy/const.py @@ -3,3 +3,4 @@ DOMAIN = "ovo_energy" DATA_CLIENT = "ovo_client" DATA_COORDINATOR = "coordinator" +CONF_ACCOUNT = "account" diff --git a/homeassistant/components/ovo_energy/strings.json b/homeassistant/components/ovo_energy/strings.json index 69392295899..810602b1412 100644 --- a/homeassistant/components/ovo_energy/strings.json +++ b/homeassistant/components/ovo_energy/strings.json @@ -10,7 +10,8 @@ "user": { "data": { "username": "[%key:common::config_flow::data::username%]", - "password": "[%key:common::config_flow::data::password%]" + "password": "[%key:common::config_flow::data::password%]", + "account": "OVO account id (only add if you have multiple accounts)" }, "description": "Set up an OVO Energy instance to access your energy usage.", "title": "Add OVO Energy Account" diff --git a/homeassistant/components/ovo_energy/translations/en.json b/homeassistant/components/ovo_energy/translations/en.json index 3539d91220d..666c6fe6772 100644 --- a/homeassistant/components/ovo_energy/translations/en.json +++ b/homeassistant/components/ovo_energy/translations/en.json @@ -17,7 +17,8 @@ "user": { "data": { "password": "Password", - "username": "Username" + "username": "Username", + "account": "OVO account id (only add if you have multiple accounts)" }, "description": "Set up an OVO Energy instance to access your energy usage.", "title": "Add OVO Energy Account" From 19debda059b1a964c50cf296c9632b6e759faf70 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sat, 29 Oct 2022 14:43:02 +0200 Subject: [PATCH 0061/1033] Bump nettigo-air-monitor to 1.5.0 (#81183) * Bump NAM library * Update tests --- homeassistant/components/nam/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/nam/__init__.py | 3 +++ tests/components/nam/fixtures/diagnostics_data.json | 5 +++++ 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nam/manifest.json b/homeassistant/components/nam/manifest.json index 43c217e2a4d..70da6555a1e 100644 --- a/homeassistant/components/nam/manifest.json +++ b/homeassistant/components/nam/manifest.json @@ -3,7 +3,7 @@ "name": "Nettigo Air Monitor", "documentation": "https://www.home-assistant.io/integrations/nam", "codeowners": ["@bieniu"], - "requirements": ["nettigo-air-monitor==1.4.2"], + "requirements": ["nettigo-air-monitor==1.5.0"], "zeroconf": [ { "type": "_http._tcp.local.", diff --git a/requirements_all.txt b/requirements_all.txt index cfaf5e7fdf5..ab82a20b14a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1129,7 +1129,7 @@ netdisco==3.0.0 netmap==0.7.0.2 # homeassistant.components.nam -nettigo-air-monitor==1.4.2 +nettigo-air-monitor==1.5.0 # homeassistant.components.neurio_energy neurio==0.3.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7301dd34cbd..f36e533d7bf 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -822,7 +822,7 @@ netdisco==3.0.0 netmap==0.7.0.2 # homeassistant.components.nam -nettigo-air-monitor==1.4.2 +nettigo-air-monitor==1.5.0 # homeassistant.components.nexia nexia==2.0.5 diff --git a/tests/components/nam/__init__.py b/tests/components/nam/__init__.py index eb723405076..0f5befcac09 100644 --- a/tests/components/nam/__init__.py +++ b/tests/components/nam/__init__.py @@ -14,6 +14,9 @@ nam_data = { "software_version": "NAMF-2020-36", "uptime": "456987", "sensordatavalues": [ + {"value_type": "PMS_P0", "value": "6.00"}, + {"value_type": "PMS_P1", "value": "10.00"}, + {"value_type": "PMS_P2", "value": "11.00"}, {"value_type": "SDS_P1", "value": "18.65"}, {"value_type": "SDS_P2", "value": "11.03"}, {"value_type": "SPS30_P0", "value": "31.23"}, diff --git a/tests/components/nam/fixtures/diagnostics_data.json b/tests/components/nam/fixtures/diagnostics_data.json index 1506adaa824..67ba64576fd 100644 --- a/tests/components/nam/fixtures/diagnostics_data.json +++ b/tests/components/nam/fixtures/diagnostics_data.json @@ -11,6 +11,11 @@ "heca_humidity": 50.0, "heca_temperature": 8.0, "mhz14a_carbon_dioxide": 865, + "pms_caqi": 19, + "pms_caqi_level": "very low", + "pms_p0": 6, + "pms_p1": 10, + "pms_p2": 11, "sds011_caqi": 19, "sds011_caqi_level": "very low", "sds011_p1": 19, From 26d174adaeaa292867dd216b98541dcc5ce69fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Klomp?= Date: Sat, 29 Oct 2022 17:04:05 +0200 Subject: [PATCH 0062/1033] Bump pysma to 0.7.2 (#81188) --- homeassistant/components/sma/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sma/manifest.json b/homeassistant/components/sma/manifest.json index c65f3b81d3b..83bf4258a95 100644 --- a/homeassistant/components/sma/manifest.json +++ b/homeassistant/components/sma/manifest.json @@ -3,7 +3,7 @@ "name": "SMA Solar", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/sma", - "requirements": ["pysma==0.7.1"], + "requirements": ["pysma==0.7.2"], "codeowners": ["@kellerza", "@rklomp"], "iot_class": "local_polling", "loggers": ["pysma"] diff --git a/requirements_all.txt b/requirements_all.txt index ab82a20b14a..d8b57adc524 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1890,7 +1890,7 @@ pysignalclirestapi==0.3.18 pyskyqhub==0.1.4 # homeassistant.components.sma -pysma==0.7.1 +pysma==0.7.2 # homeassistant.components.smappee pysmappee==0.2.29 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f36e533d7bf..c5dce2a3807 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1331,7 +1331,7 @@ pysiaalarm==3.0.2 pysignalclirestapi==0.3.18 # homeassistant.components.sma -pysma==0.7.1 +pysma==0.7.2 # homeassistant.components.smappee pysmappee==0.2.29 From b16094b93f22117d43690646b30da97a225854a9 Mon Sep 17 00:00:00 2001 From: rappenze Date: Sat, 29 Oct 2022 18:53:50 +0200 Subject: [PATCH 0063/1033] Add integration type to fibaro integration (#81202) --- homeassistant/components/fibaro/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/fibaro/manifest.json b/homeassistant/components/fibaro/manifest.json index 218a6aad857..c7b80585548 100644 --- a/homeassistant/components/fibaro/manifest.json +++ b/homeassistant/components/fibaro/manifest.json @@ -1,6 +1,7 @@ { "domain": "fibaro", "name": "Fibaro", + "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/fibaro", "requirements": ["fiblary3==0.1.8"], "codeowners": ["@rappenze"], From 7bd89621ce8b67978d9f6764249aecb3c1da2528 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 29 Oct 2022 20:21:28 +0200 Subject: [PATCH 0064/1033] Change IoT Class for Min/Max to calculated (#81199) --- homeassistant/components/min_max/manifest.json | 2 +- homeassistant/generated/integrations.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/min_max/manifest.json b/homeassistant/components/min_max/manifest.json index dd3e846aa84..7324d682caa 100644 --- a/homeassistant/components/min_max/manifest.json +++ b/homeassistant/components/min_max/manifest.json @@ -5,6 +5,6 @@ "documentation": "https://www.home-assistant.io/integrations/min_max", "codeowners": ["@fabaff"], "quality_scale": "internal", - "iot_class": "local_push", + "iot_class": "calculated", "config_flow": true } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index a7f78e45493..3ef8c874882 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -6212,7 +6212,7 @@ "min_max": { "integration_type": "helper", "config_flow": true, - "iot_class": "local_push" + "iot_class": "calculated" }, "schedule": { "integration_type": "helper", From 91efe68cdd0a9c5e879cb58cc3a648daf1c5d356 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 29 Oct 2022 13:22:46 -0500 Subject: [PATCH 0065/1033] Bump dbus-fast to 1.58.0 (#81195) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 3ac8ac513c1..0db0433de2b 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.4.2", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", - "dbus-fast==1.56.0" + "dbus-fast==1.58.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index b50deca16bd..2d33e11a547 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.1 -dbus-fast==1.56.0 +dbus-fast==1.58.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index d8b57adc524..f405f22f216 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -540,7 +540,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.56.0 +dbus-fast==1.58.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c5dce2a3807..9ab21e994b5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -420,7 +420,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.56.0 +dbus-fast==1.58.0 # homeassistant.components.debugpy debugpy==1.6.3 From ccfa314e8708c206799104c7ccf5bbfb31a0525d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 29 Oct 2022 13:25:35 -0500 Subject: [PATCH 0066/1033] Update to bleak 0.19.1 and bleak-retry-connector 2.5.0 (#81198) --- homeassistant/components/bluetooth/manifest.json | 4 ++-- homeassistant/package_constraints.txt | 4 ++-- requirements_all.txt | 4 ++-- requirements_test_all.txt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 0db0433de2b..442759382d7 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -6,8 +6,8 @@ "after_dependencies": ["hassio"], "quality_scale": "internal", "requirements": [ - "bleak==0.19.0", - "bleak-retry-connector==2.4.2", + "bleak==0.19.1", + "bleak-retry-connector==2.5.0", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", "dbus-fast==1.58.0" diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 2d33e11a547..e8e520c29ba 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,8 +10,8 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.4.2 -bleak==0.19.0 +bleak-retry-connector==2.5.0 +bleak==0.19.1 bluetooth-adapters==0.6.0 bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 diff --git a/requirements_all.txt b/requirements_all.txt index f405f22f216..b73003525ab 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -413,10 +413,10 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.4.2 +bleak-retry-connector==2.5.0 # homeassistant.components.bluetooth -bleak==0.19.0 +bleak==0.19.1 # homeassistant.components.blebox blebox_uniapi==2.1.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9ab21e994b5..3c73d1e8e70 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -337,10 +337,10 @@ bellows==0.34.2 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.4.2 +bleak-retry-connector==2.5.0 # homeassistant.components.bluetooth -bleak==0.19.0 +bleak==0.19.1 # homeassistant.components.blebox blebox_uniapi==2.1.3 From 871c1bc952f7d54f8cd336ce08295fecc91e191f Mon Sep 17 00:00:00 2001 From: Menco Bolt Date: Sat, 29 Oct 2022 20:25:46 +0200 Subject: [PATCH 0067/1033] Today's Consumption is INCREASING (#81204) Co-authored-by: Paulus Schoutsen --- homeassistant/components/enphase_envoy/const.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/enphase_envoy/const.py b/homeassistant/components/enphase_envoy/const.py index c79c3af604b..7c493168526 100644 --- a/homeassistant/components/enphase_envoy/const.py +++ b/homeassistant/components/enphase_envoy/const.py @@ -34,7 +34,7 @@ SENSORS = ( key="seven_days_production", name="Last Seven Days Energy Production", native_unit_of_measurement=ENERGY_WATT_HOUR, - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), SensorEntityDescription( @@ -54,14 +54,14 @@ SENSORS = ( key="daily_consumption", name="Today's Energy Consumption", native_unit_of_measurement=ENERGY_WATT_HOUR, - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), SensorEntityDescription( key="seven_days_consumption", name="Last Seven Days Energy Consumption", native_unit_of_measurement=ENERGY_WATT_HOUR, - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), SensorEntityDescription( From 770aefbd52fca9adf294253b646706164a051500 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 29 Oct 2022 14:26:12 -0400 Subject: [PATCH 0068/1033] SSDP to allow more URLs (#81171) Co-authored-by: J. Nick Koston --- homeassistant/components/ssdp/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/ssdp/__init__.py b/homeassistant/components/ssdp/__init__.py index 195bebb8321..d081ef877de 100644 --- a/homeassistant/components/ssdp/__init__.py +++ b/homeassistant/components/ssdp/__init__.py @@ -697,7 +697,7 @@ class Server: udn = await self._async_get_instance_udn() system_info = await async_get_system_info(self.hass) model_name = system_info["installation_type"] - presentation_url = get_url(self.hass) + presentation_url = get_url(self.hass, allow_ip=True, prefer_external=False) serial_number = await async_get_instance_id(self.hass) HassUpnpServiceDevice.DEVICE_DEFINITION = ( HassUpnpServiceDevice.DEVICE_DEFINITION._replace( From 208150c353ff750cd3d62a6bc8c5730571d02e37 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 29 Oct 2022 14:05:59 -0500 Subject: [PATCH 0069/1033] Restore homekit_controller BLE broadcast_key from disk (#81211) * Restore homekit_controller BLE broadcast_key from disk Some accessories will sleep for a long time and only send broadcasted events which makes them have very long connection intervals to save battery. Since we need to connect to get a new broadcast key we now save the broadcast key between restarts to ensure we can decrypt the advertisments coming in even though we cannot make a connection to the device during startup. When we get a disconnected event later we will try again to connect and the device will be awake which will trigger a full sync * bump bump --- .../homekit_controller/config_flow.py | 3 ++- .../homekit_controller/manifest.json | 2 +- .../components/homekit_controller/storage.py | 27 ++++++++----------- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/homekit_controller/config_flow.py b/homeassistant/components/homekit_controller/config_flow.py index 62144077a94..da4ccfe9f9a 100644 --- a/homeassistant/components/homekit_controller/config_flow.py +++ b/homeassistant/components/homekit_controller/config_flow.py @@ -15,7 +15,7 @@ from aiohomekit.controller.abstract import ( from aiohomekit.exceptions import AuthenticationError from aiohomekit.model.categories import Categories from aiohomekit.model.status_flags import StatusFlags -from aiohomekit.utils import domain_supported, domain_to_name +from aiohomekit.utils import domain_supported, domain_to_name, serialize_broadcast_key import voluptuous as vol from homeassistant import config_entries @@ -577,6 +577,7 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): pairing.id, accessories_state.config_num, accessories_state.accessories.serialize(), + serialize_broadcast_key(accessories_state.broadcast_key), ) return self.async_create_entry(title=name, data=pairing_data) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 5aaae67d1d3..224b24f6077 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.7"], + "requirements": ["aiohomekit==2.2.8"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/homeassistant/components/homekit_controller/storage.py b/homeassistant/components/homekit_controller/storage.py index 51d8ce4ffd3..a5afb07620a 100644 --- a/homeassistant/components/homekit_controller/storage.py +++ b/homeassistant/components/homekit_controller/storage.py @@ -3,7 +3,9 @@ from __future__ import annotations import logging -from typing import Any, TypedDict +from typing import Any + +from aiohomekit.characteristic_cache import Pairing, StorageLayout from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.storage import Store @@ -16,19 +18,6 @@ ENTITY_MAP_SAVE_DELAY = 10 _LOGGER = logging.getLogger(__name__) -class Pairing(TypedDict): - """A versioned map of entity metadata as presented by aiohomekit.""" - - config_num: int - accessories: list[Any] - - -class StorageLayout(TypedDict): - """Cached pairing metadata needed by aiohomekit.""" - - pairings: dict[str, Pairing] - - class EntityMapStorage: """ Holds a cache of entity structure data from a paired HomeKit device. @@ -67,11 +56,17 @@ class EntityMapStorage: @callback def async_create_or_update_map( - self, homekit_id: str, config_num: int, accessories: list[Any] + self, + homekit_id: str, + config_num: int, + accessories: list[Any], + broadcast_key: str | None = None, ) -> Pairing: """Create a new pairing cache.""" _LOGGER.debug("Creating or updating entity map for %s", homekit_id) - data = Pairing(config_num=config_num, accessories=accessories) + data = Pairing( + config_num=config_num, accessories=accessories, broadcast_key=broadcast_key + ) self.storage_data[homekit_id] = data self._async_schedule_save() return data diff --git a/requirements_all.txt b/requirements_all.txt index b73003525ab..c01eacf5701 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.7 +aiohomekit==2.2.8 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3c73d1e8e70..42e58ca2d38 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.7 +aiohomekit==2.2.8 # homeassistant.components.emulated_hue # homeassistant.components.http From 7f047cf95c5bcbb84ce022ac9f307ac91e3ad11c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 29 Oct 2022 14:06:17 -0500 Subject: [PATCH 0070/1033] Bump dbus-fast to 1.59.0 (#81215) * Bump dbus-fast to 1.59.0 changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v1.58.0...v1.59.0 * empty --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 442759382d7..a5ea8c171d8 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.5.0", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", - "dbus-fast==1.58.0" + "dbus-fast==1.59.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index e8e520c29ba..413a86be041 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.1 -dbus-fast==1.58.0 +dbus-fast==1.59.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index c01eacf5701..4ebea47c866 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -540,7 +540,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.58.0 +dbus-fast==1.59.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 42e58ca2d38..706e81b7595 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -420,7 +420,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.58.0 +dbus-fast==1.59.0 # homeassistant.components.debugpy debugpy==1.6.3 From 494cbf0dbe66cd6408d87b5975359010c55f8763 Mon Sep 17 00:00:00 2001 From: Garrett <7310260+G-Two@users.noreply.github.com> Date: Sat, 29 Oct 2022 16:02:32 -0400 Subject: [PATCH 0071/1033] Add diagnostics to Subaru integration (#81169) Co-authored-by: J. Nick Koston --- .../components/subaru/diagnostics.py | 56 +++++++++++++ .../fixtures/diagnostics_config_entry.json | 82 +++++++++++++++++++ .../subaru/fixtures/diagnostics_device.json | 80 ++++++++++++++++++ tests/components/subaru/test_diagnostics.py | 78 ++++++++++++++++++ 4 files changed, 296 insertions(+) create mode 100644 homeassistant/components/subaru/diagnostics.py create mode 100644 tests/components/subaru/fixtures/diagnostics_config_entry.json create mode 100644 tests/components/subaru/fixtures/diagnostics_device.json create mode 100644 tests/components/subaru/test_diagnostics.py diff --git a/homeassistant/components/subaru/diagnostics.py b/homeassistant/components/subaru/diagnostics.py new file mode 100644 index 00000000000..79ffcbe1792 --- /dev/null +++ b/homeassistant/components/subaru/diagnostics.py @@ -0,0 +1,56 @@ +"""Diagnostics for the Subaru integration.""" +from __future__ import annotations + +from typing import Any + +from subarulink.const import LATITUDE, LONGITUDE, ODOMETER, VEHICLE_NAME + +from homeassistant.components.diagnostics.util import async_redact_data +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_DEVICE_ID, CONF_PASSWORD, CONF_PIN, CONF_USERNAME +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.device_registry import DeviceEntry + +from .const import DOMAIN, ENTRY_COORDINATOR, VEHICLE_VIN + +CONFIG_FIELDS_TO_REDACT = [CONF_USERNAME, CONF_PASSWORD, CONF_PIN, CONF_DEVICE_ID] +DATA_FIELDS_TO_REDACT = [VEHICLE_VIN, VEHICLE_NAME, LATITUDE, LONGITUDE, ODOMETER] + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, config_entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + coordinator = hass.data[DOMAIN][config_entry.entry_id][ENTRY_COORDINATOR] + + diagnostics_data = { + "config_entry": async_redact_data(config_entry.data, CONFIG_FIELDS_TO_REDACT), + "options": async_redact_data(config_entry.options, []), + "data": [ + async_redact_data(info, DATA_FIELDS_TO_REDACT) + for info in coordinator.data.values() + ], + } + + return diagnostics_data + + +async def async_get_device_diagnostics( + hass: HomeAssistant, config_entry: ConfigEntry, device: DeviceEntry +) -> dict[str, Any]: + """Return diagnostics for a device.""" + coordinator = hass.data[DOMAIN][config_entry.entry_id][ENTRY_COORDINATOR] + + vin = next(iter(device.identifiers))[1] + + if info := coordinator.data.get(vin): + return { + "config_entry": async_redact_data( + config_entry.data, CONFIG_FIELDS_TO_REDACT + ), + "options": async_redact_data(config_entry.options, []), + "data": async_redact_data(info, DATA_FIELDS_TO_REDACT), + } + + raise HomeAssistantError("Device not found") diff --git a/tests/components/subaru/fixtures/diagnostics_config_entry.json b/tests/components/subaru/fixtures/diagnostics_config_entry.json new file mode 100644 index 00000000000..09e8119d669 --- /dev/null +++ b/tests/components/subaru/fixtures/diagnostics_config_entry.json @@ -0,0 +1,82 @@ +{ + "config_entry": { + "username": "**REDACTED**", + "password": "**REDACTED**", + "country": "USA", + "pin": "**REDACTED**", + "device_id": "**REDACTED**" + }, + "options": { "update_enabled": true }, + "data": [ + { + "status": { + "AVG_FUEL_CONSUMPTION": 2.3, + "BATTERY_VOLTAGE": 12.0, + "DISTANCE_TO_EMPTY_FUEL": 707, + "DOOR_BOOT_LOCK_STATUS": "UNKNOWN", + "DOOR_BOOT_POSITION": "CLOSED", + "DOOR_ENGINE_HOOD_LOCK_STATUS": "UNKNOWN", + "DOOR_ENGINE_HOOD_POSITION": "CLOSED", + "DOOR_FRONT_LEFT_LOCK_STATUS": "UNKNOWN", + "DOOR_FRONT_LEFT_POSITION": "CLOSED", + "DOOR_FRONT_RIGHT_LOCK_STATUS": "UNKNOWN", + "DOOR_FRONT_RIGHT_POSITION": "CLOSED", + "DOOR_REAR_LEFT_LOCK_STATUS": "UNKNOWN", + "DOOR_REAR_LEFT_POSITION": "CLOSED", + "DOOR_REAR_RIGHT_LOCK_STATUS": "UNKNOWN", + "DOOR_REAR_RIGHT_POSITION": "CLOSED", + "EV_CHARGER_STATE_TYPE": "CHARGING", + "EV_CHARGE_SETTING_AMPERE_TYPE": "MAXIMUM", + "EV_CHARGE_VOLT_TYPE": "CHARGE_LEVEL_1", + "EV_DISTANCE_TO_EMPTY": 1, + "EV_IS_PLUGGED_IN": "UNLOCKED_CONNECTED", + "EV_STATE_OF_CHARGE_MODE": "EV_MODE", + "EV_STATE_OF_CHARGE_PERCENT": 20, + "EV_TIME_TO_FULLY_CHARGED_UTC": "2020-07-24T03:06:40+00:00", + "EXT_EXTERNAL_TEMP": 21.5, + "ODOMETER": "**REDACTED**", + "POSITION_HEADING_DEGREE": 150, + "POSITION_SPEED_KMPH": "0", + "POSITION_TIMESTAMP": 1595560000.0, + "SEAT_BELT_STATUS_FRONT_LEFT": "BELTED", + "SEAT_BELT_STATUS_FRONT_MIDDLE": "NOT_EQUIPPED", + "SEAT_BELT_STATUS_FRONT_RIGHT": "BELTED", + "SEAT_BELT_STATUS_SECOND_LEFT": "UNKNOWN", + "SEAT_BELT_STATUS_SECOND_MIDDLE": "UNKNOWN", + "SEAT_BELT_STATUS_SECOND_RIGHT": "UNKNOWN", + "SEAT_BELT_STATUS_THIRD_LEFT": "UNKNOWN", + "SEAT_BELT_STATUS_THIRD_MIDDLE": "UNKNOWN", + "SEAT_BELT_STATUS_THIRD_RIGHT": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_FRONT_LEFT": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_FRONT_MIDDLE": "NOT_EQUIPPED", + "SEAT_OCCUPATION_STATUS_FRONT_RIGHT": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_SECOND_LEFT": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_SECOND_MIDDLE": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_SECOND_RIGHT": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_THIRD_LEFT": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_THIRD_MIDDLE": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_THIRD_RIGHT": "UNKNOWN", + "TIMESTAMP": 1595560000.0, + "TRANSMISSION_MODE": "UNKNOWN", + "TYRE_PRESSURE_FRONT_LEFT": 0, + "TYRE_PRESSURE_FRONT_RIGHT": 2550, + "TYRE_PRESSURE_REAR_LEFT": 2450, + "TYRE_PRESSURE_REAR_RIGHT": 2350, + "TYRE_STATUS_FRONT_LEFT": "UNKNOWN", + "TYRE_STATUS_FRONT_RIGHT": "UNKNOWN", + "TYRE_STATUS_REAR_LEFT": "UNKNOWN", + "TYRE_STATUS_REAR_RIGHT": "UNKNOWN", + "VEHICLE_STATE_TYPE": "IGNITION_OFF", + "WINDOW_BACK_STATUS": "UNKNOWN", + "WINDOW_FRONT_LEFT_STATUS": "VENTED", + "WINDOW_FRONT_RIGHT_STATUS": "VENTED", + "WINDOW_REAR_LEFT_STATUS": "UNKNOWN", + "WINDOW_REAR_RIGHT_STATUS": "UNKNOWN", + "WINDOW_SUNROOF_STATUS": "UNKNOWN", + "heading": 170, + "latitude": "**REDACTED**", + "longitude": "**REDACTED**" + } + } + ] +} diff --git a/tests/components/subaru/fixtures/diagnostics_device.json b/tests/components/subaru/fixtures/diagnostics_device.json new file mode 100644 index 00000000000..83b2c2a836e --- /dev/null +++ b/tests/components/subaru/fixtures/diagnostics_device.json @@ -0,0 +1,80 @@ +{ + "config_entry": { + "username": "**REDACTED**", + "password": "**REDACTED**", + "country": "USA", + "pin": "**REDACTED**", + "device_id": "**REDACTED**" + }, + "options": { "update_enabled": true }, + "data": { + "status": { + "AVG_FUEL_CONSUMPTION": 2.3, + "BATTERY_VOLTAGE": 12.0, + "DISTANCE_TO_EMPTY_FUEL": 707, + "DOOR_BOOT_LOCK_STATUS": "UNKNOWN", + "DOOR_BOOT_POSITION": "CLOSED", + "DOOR_ENGINE_HOOD_LOCK_STATUS": "UNKNOWN", + "DOOR_ENGINE_HOOD_POSITION": "CLOSED", + "DOOR_FRONT_LEFT_LOCK_STATUS": "UNKNOWN", + "DOOR_FRONT_LEFT_POSITION": "CLOSED", + "DOOR_FRONT_RIGHT_LOCK_STATUS": "UNKNOWN", + "DOOR_FRONT_RIGHT_POSITION": "CLOSED", + "DOOR_REAR_LEFT_LOCK_STATUS": "UNKNOWN", + "DOOR_REAR_LEFT_POSITION": "CLOSED", + "DOOR_REAR_RIGHT_LOCK_STATUS": "UNKNOWN", + "DOOR_REAR_RIGHT_POSITION": "CLOSED", + "EV_CHARGER_STATE_TYPE": "CHARGING", + "EV_CHARGE_SETTING_AMPERE_TYPE": "MAXIMUM", + "EV_CHARGE_VOLT_TYPE": "CHARGE_LEVEL_1", + "EV_DISTANCE_TO_EMPTY": 1, + "EV_IS_PLUGGED_IN": "UNLOCKED_CONNECTED", + "EV_STATE_OF_CHARGE_MODE": "EV_MODE", + "EV_STATE_OF_CHARGE_PERCENT": 20, + "EV_TIME_TO_FULLY_CHARGED_UTC": "2020-07-24T03:06:40+00:00", + "EXT_EXTERNAL_TEMP": 21.5, + "ODOMETER": "**REDACTED**", + "POSITION_HEADING_DEGREE": 150, + "POSITION_SPEED_KMPH": "0", + "POSITION_TIMESTAMP": 1595560000.0, + "SEAT_BELT_STATUS_FRONT_LEFT": "BELTED", + "SEAT_BELT_STATUS_FRONT_MIDDLE": "NOT_EQUIPPED", + "SEAT_BELT_STATUS_FRONT_RIGHT": "BELTED", + "SEAT_BELT_STATUS_SECOND_LEFT": "UNKNOWN", + "SEAT_BELT_STATUS_SECOND_MIDDLE": "UNKNOWN", + "SEAT_BELT_STATUS_SECOND_RIGHT": "UNKNOWN", + "SEAT_BELT_STATUS_THIRD_LEFT": "UNKNOWN", + "SEAT_BELT_STATUS_THIRD_MIDDLE": "UNKNOWN", + "SEAT_BELT_STATUS_THIRD_RIGHT": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_FRONT_LEFT": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_FRONT_MIDDLE": "NOT_EQUIPPED", + "SEAT_OCCUPATION_STATUS_FRONT_RIGHT": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_SECOND_LEFT": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_SECOND_MIDDLE": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_SECOND_RIGHT": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_THIRD_LEFT": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_THIRD_MIDDLE": "UNKNOWN", + "SEAT_OCCUPATION_STATUS_THIRD_RIGHT": "UNKNOWN", + "TIMESTAMP": 1595560000.0, + "TRANSMISSION_MODE": "UNKNOWN", + "TYRE_PRESSURE_FRONT_LEFT": 0, + "TYRE_PRESSURE_FRONT_RIGHT": 2550, + "TYRE_PRESSURE_REAR_LEFT": 2450, + "TYRE_PRESSURE_REAR_RIGHT": 2350, + "TYRE_STATUS_FRONT_LEFT": "UNKNOWN", + "TYRE_STATUS_FRONT_RIGHT": "UNKNOWN", + "TYRE_STATUS_REAR_LEFT": "UNKNOWN", + "TYRE_STATUS_REAR_RIGHT": "UNKNOWN", + "VEHICLE_STATE_TYPE": "IGNITION_OFF", + "WINDOW_BACK_STATUS": "UNKNOWN", + "WINDOW_FRONT_LEFT_STATUS": "VENTED", + "WINDOW_FRONT_RIGHT_STATUS": "VENTED", + "WINDOW_REAR_LEFT_STATUS": "UNKNOWN", + "WINDOW_REAR_RIGHT_STATUS": "UNKNOWN", + "WINDOW_SUNROOF_STATUS": "UNKNOWN", + "heading": 170, + "latitude": "**REDACTED**", + "longitude": "**REDACTED**" + } + } +} diff --git a/tests/components/subaru/test_diagnostics.py b/tests/components/subaru/test_diagnostics.py new file mode 100644 index 00000000000..547bb0edc4f --- /dev/null +++ b/tests/components/subaru/test_diagnostics.py @@ -0,0 +1,78 @@ +"""Test Subaru diagnostics.""" +import json +from unittest.mock import patch + +import pytest + +from homeassistant.components.subaru.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr + +from .api_responses import TEST_VIN_2_EV + +from tests.common import load_fixture +from tests.components.diagnostics import ( + get_diagnostics_for_config_entry, + get_diagnostics_for_device, +) +from tests.components.subaru.conftest import ( + MOCK_API_FETCH, + MOCK_API_GET_DATA, + advance_time_to_next_fetch, +) + + +async def test_config_entry_diagnostics(hass: HomeAssistant, hass_client, ev_entry): + """Test config entry diagnostics.""" + + config_entry = hass.config_entries.async_entries(DOMAIN)[0] + + diagnostics_fixture = json.loads( + load_fixture("subaru/diagnostics_config_entry.json") + ) + + assert ( + await get_diagnostics_for_config_entry(hass, hass_client, config_entry) + == diagnostics_fixture + ) + + +async def test_device_diagnostics(hass: HomeAssistant, hass_client, ev_entry): + """Test device diagnostics.""" + + config_entry = hass.config_entries.async_entries(DOMAIN)[0] + + device_registry = dr.async_get(hass) + reg_device = device_registry.async_get_device( + identifiers={(DOMAIN, TEST_VIN_2_EV)}, + ) + assert reg_device is not None + + diagnostics_fixture = json.loads(load_fixture("subaru/diagnostics_device.json")) + + assert ( + await get_diagnostics_for_device(hass, hass_client, config_entry, reg_device) + == diagnostics_fixture + ) + + +async def test_device_diagnostics_vehicle_not_found( + hass: HomeAssistant, hass_client, ev_entry +): + """Test device diagnostics when the vehicle cannot be found.""" + + config_entry = hass.config_entries.async_entries(DOMAIN)[0] + + device_registry = dr.async_get(hass) + reg_device = device_registry.async_get_device( + identifiers={(DOMAIN, TEST_VIN_2_EV)}, + ) + assert reg_device is not None + + # Simulate case where Subaru API does not return vehicle data + with patch(MOCK_API_FETCH), patch(MOCK_API_GET_DATA, return_value=None): + advance_time_to_next_fetch(hass) + await hass.async_block_till_done() + + with pytest.raises(AssertionError): + await get_diagnostics_for_device(hass, hass_client, config_entry, reg_device) From a2c38b9d040ca6c6a1413b3c9a9957e6783565dc Mon Sep 17 00:00:00 2001 From: Vincent Knoop Pathuis <48653141+vpathuis@users.noreply.github.com> Date: Sat, 29 Oct 2022 23:23:09 +0200 Subject: [PATCH 0072/1033] Bump ultraheat api to version 0.5.1 (#81194) --- homeassistant/components/landisgyr_heat_meter/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/landisgyr_heat_meter/manifest.json b/homeassistant/components/landisgyr_heat_meter/manifest.json index dc6444b478d..7be3115a6d3 100644 --- a/homeassistant/components/landisgyr_heat_meter/manifest.json +++ b/homeassistant/components/landisgyr_heat_meter/manifest.json @@ -3,7 +3,7 @@ "name": "Landis+Gyr Heat Meter", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/landisgyr_heat_meter", - "requirements": ["ultraheat-api==0.5.0"], + "requirements": ["ultraheat-api==0.5.1"], "ssdp": [], "zeroconf": [], "homekit": {}, diff --git a/requirements_all.txt b/requirements_all.txt index 4ebea47c866..e80a736383d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2455,7 +2455,7 @@ twitchAPI==2.5.2 uasiren==0.0.1 # homeassistant.components.landisgyr_heat_meter -ultraheat-api==0.5.0 +ultraheat-api==0.5.1 # homeassistant.components.unifiprotect unifi-discovery==1.1.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 706e81b7595..98b568782b4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1692,7 +1692,7 @@ twitchAPI==2.5.2 uasiren==0.0.1 # homeassistant.components.landisgyr_heat_meter -ultraheat-api==0.5.0 +ultraheat-api==0.5.1 # homeassistant.components.unifiprotect unifi-discovery==1.1.7 From 82f03f60890bac6e0fcd020fd19348512017cf02 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Sat, 29 Oct 2022 23:47:35 +0200 Subject: [PATCH 0073/1033] Add integration_type to netatmo (#81225) --- homeassistant/components/netatmo/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/netatmo/manifest.json b/homeassistant/components/netatmo/manifest.json index 5ad0fca3d7a..8beb7bc521a 100644 --- a/homeassistant/components/netatmo/manifest.json +++ b/homeassistant/components/netatmo/manifest.json @@ -1,6 +1,7 @@ { "domain": "netatmo", "name": "Netatmo", + "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/netatmo", "requirements": ["pyatmo==7.2.0"], "after_dependencies": ["cloud", "media_source"], From 833d271eb3015dd28f3cccce58fd8875f60b4b88 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Sat, 29 Oct 2022 23:50:34 +0200 Subject: [PATCH 0074/1033] Add missing Netatmo config url for public weather (#81220) Add missing config url for public weather --- homeassistant/components/netatmo/const.py | 1 + homeassistant/components/netatmo/sensor.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/homeassistant/components/netatmo/const.py b/homeassistant/components/netatmo/const.py index e93d0c91a07..21f821040dd 100644 --- a/homeassistant/components/netatmo/const.py +++ b/homeassistant/components/netatmo/const.py @@ -21,6 +21,7 @@ CONF_URL_SECURITY = "https://home.netatmo.com/security" CONF_URL_ENERGY = "https://my.netatmo.com/app/energy" CONF_URL_WEATHER = "https://my.netatmo.com/app/weather" CONF_URL_CONTROL = "https://home.netatmo.com/control" +CONF_URL_PUBLIC_WEATHER = "https://weathermap.netatmo.com/" AUTH = "netatmo_auth" CONF_PUBLIC = "public_sensor_config" diff --git a/homeassistant/components/netatmo/sensor.py b/homeassistant/components/netatmo/sensor.py index 82f6c95b699..1fed7cf5e0e 100644 --- a/homeassistant/components/netatmo/sensor.py +++ b/homeassistant/components/netatmo/sensor.py @@ -39,6 +39,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( CONF_URL_ENERGY, + CONF_URL_PUBLIC_WEATHER, CONF_URL_WEATHER, CONF_WEATHER_AREAS, DATA_HANDLER, @@ -702,6 +703,7 @@ class NetatmoPublicSensor(NetatmoBase, SensorEntity): self._device_name = f"{self._area_name}" self._attr_name = f"{description.name}" self._show_on_map = area.show_on_map + self._config_url = CONF_URL_PUBLIC_WEATHER self._attr_unique_id = ( f"{self._device_name.replace(' ', '-')}-{description.key}" ) From 782148423c2367a7913cfddf2ba96ffd847e78e8 Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Sat, 29 Oct 2022 23:51:11 +0200 Subject: [PATCH 0075/1033] Add missing string for option traffic_mode for google_travel_time (#81213) Add missing string for option traffic_mode --- homeassistant/components/google_travel_time/strings.json | 1 + homeassistant/components/google_travel_time/translations/en.json | 1 + 2 files changed, 2 insertions(+) diff --git a/homeassistant/components/google_travel_time/strings.json b/homeassistant/components/google_travel_time/strings.json index 22a122b9a53..78b84038c7f 100644 --- a/homeassistant/components/google_travel_time/strings.json +++ b/homeassistant/components/google_travel_time/strings.json @@ -30,6 +30,7 @@ "time_type": "Time Type", "time": "Time", "avoid": "Avoid", + "traffic_mode": "Traffic Mode", "transit_mode": "Transit Mode", "transit_routing_preference": "Transit Routing Preference", "units": "Units" diff --git a/homeassistant/components/google_travel_time/translations/en.json b/homeassistant/components/google_travel_time/translations/en.json index 8e91fbf1df0..dd03dca1d2f 100644 --- a/homeassistant/components/google_travel_time/translations/en.json +++ b/homeassistant/components/google_travel_time/translations/en.json @@ -28,6 +28,7 @@ "mode": "Travel Mode", "time": "Time", "time_type": "Time Type", + "traffic_mode": "Traffic Mode", "transit_mode": "Transit Mode", "transit_routing_preference": "Transit Routing Preference", "units": "Units" From b043406b740757793a27b6bc66e610d0368ad023 Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Sat, 29 Oct 2022 23:51:53 +0200 Subject: [PATCH 0076/1033] Fix Danfoss thermostat support in devolo Home Control (#81200) Fix Danfoss thermostat --- homeassistant/components/devolo_home_control/climate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/devolo_home_control/climate.py b/homeassistant/components/devolo_home_control/climate.py index 95e0628d534..6c566aa45e3 100644 --- a/homeassistant/components/devolo_home_control/climate.py +++ b/homeassistant/components/devolo_home_control/climate.py @@ -35,6 +35,7 @@ async def async_setup_entry( "devolo.model.Thermostat:Valve", "devolo.model.Room:Thermostat", "devolo.model.Eurotronic:Spirit:Device", + "unk.model.Danfoss:Thermostat", ): entities.append( DevoloClimateDeviceEntity( From f10b36873a440cc10adddca8cd60e0d7aa7899eb Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Sun, 30 Oct 2022 00:04:01 +0200 Subject: [PATCH 0077/1033] Mute superfluous exception when no Netatmo webhook is to be dropped (#81221) * Mute superfluous exception when no webhook is to be droped * Update homeassistant/components/netatmo/__init__.py Co-authored-by: Paulus Schoutsen --- homeassistant/components/netatmo/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/netatmo/__init__.py b/homeassistant/components/netatmo/__init__.py index eb0e93c4b38..aa8728d548d 100644 --- a/homeassistant/components/netatmo/__init__.py +++ b/homeassistant/components/netatmo/__init__.py @@ -271,7 +271,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if CONF_WEBHOOK_ID in entry.data: webhook_unregister(hass, entry.data[CONF_WEBHOOK_ID]) - await data[entry.entry_id][AUTH].async_dropwebhook() + try: + await data[entry.entry_id][AUTH].async_dropwebhook() + except pyatmo.ApiError: + _LOGGER.debug("No webhook to be dropped") _LOGGER.info("Unregister Netatmo webhook") unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) From 6e69c57ef518c38148f2eb8deaa900b3db3551cb Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sun, 30 Oct 2022 00:05:18 +0200 Subject: [PATCH 0078/1033] Code Quality for Wake on Lan (#81206) Wake on Lan cleanup --- .strict-typing | 1 + .../components/wake_on_lan/switch.py | 71 +++++++------------ mypy.ini | 10 +++ 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/.strict-typing b/.strict-typing index e79fb6b1a26..dce6fcc6745 100644 --- a/.strict-typing +++ b/.strict-typing @@ -283,6 +283,7 @@ homeassistant.components.vacuum.* homeassistant.components.vallox.* homeassistant.components.velbus.* homeassistant.components.vlc_telnet.* +homeassistant.components.wake_on_lan.* homeassistant.components.wallbox.* homeassistant.components.water_heater.* homeassistant.components.watttime.* diff --git a/homeassistant/components/wake_on_lan/switch.py b/homeassistant/components/wake_on_lan/switch.py index 1b3904f63e6..446df402c87 100644 --- a/homeassistant/components/wake_on_lan/switch.py +++ b/homeassistant/components/wake_on_lan/switch.py @@ -8,7 +8,10 @@ from typing import Any import voluptuous as vol import wakeonlan -from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchEntity +from homeassistant.components.switch import ( + PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA, + SwitchEntity, +) from homeassistant.const import ( CONF_BROADCAST_ADDRESS, CONF_BROADCAST_PORT, @@ -32,7 +35,7 @@ CONF_OFF_ACTION = "turn_off" DEFAULT_NAME = "Wake on LAN" DEFAULT_PING_TIMEOUT = 1 -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend( { vol.Required(CONF_MAC): cv.string, vol.Optional(CONF_BROADCAST_ADDRESS): cv.string, @@ -51,12 +54,12 @@ def setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up a wake on lan switch.""" - broadcast_address = config.get(CONF_BROADCAST_ADDRESS) - broadcast_port = config.get(CONF_BROADCAST_PORT) - host = config.get(CONF_HOST) - mac_address = config[CONF_MAC] - name = config[CONF_NAME] - off_action = config.get(CONF_OFF_ACTION) + broadcast_address: str | None = config.get(CONF_BROADCAST_ADDRESS) + broadcast_port: int | None = config.get(CONF_BROADCAST_PORT) + host: str | None = config.get(CONF_HOST) + mac_address: str = config[CONF_MAC] + name: str = config[CONF_NAME] + off_action: list[Any] | None = config.get(CONF_OFF_ACTION) add_entities( [ @@ -79,17 +82,16 @@ class WolSwitch(SwitchEntity): def __init__( self, - hass, - name, - host, - mac_address, - off_action, - broadcast_address, - broadcast_port, - ): + hass: HomeAssistant, + name: str, + host: str | None, + mac_address: str, + off_action: list[Any] | None, + broadcast_address: str | None, + broadcast_port: int | None, + ) -> None: """Initialize the WOL switch.""" - self._hass = hass - self._name = name + self._attr_name = name self._host = host self._mac_address = mac_address self._broadcast_address = broadcast_address @@ -98,37 +100,18 @@ class WolSwitch(SwitchEntity): Script(hass, off_action, name, DOMAIN) if off_action else None ) self._state = False - self._assumed_state = host is None - self._unique_id = dr.format_mac(mac_address) + self._attr_assumed_state = host is None + self._attr_should_poll = bool(not self._attr_assumed_state) + self._attr_unique_id = dr.format_mac(mac_address) @property - def is_on(self): + def is_on(self) -> bool: """Return true if switch is on.""" return self._state - @property - def name(self): - """Return the name of the switch.""" - return self._name - - @property - def assumed_state(self): - """Return true if no host is provided.""" - return self._assumed_state - - @property - def should_poll(self): - """Return false if assumed state is true.""" - return not self._assumed_state - - @property - def unique_id(self): - """Return the unique id of this switch.""" - return self._unique_id - def turn_on(self, **kwargs: Any) -> None: """Turn the device on.""" - service_kwargs = {} + service_kwargs: dict[str, Any] = {} if self._broadcast_address is not None: service_kwargs["ip_address"] = self._broadcast_address if self._broadcast_port is not None: @@ -143,7 +126,7 @@ class WolSwitch(SwitchEntity): wakeonlan.send_magic_packet(self._mac_address, **service_kwargs) - if self._assumed_state: + if self._attr_assumed_state: self._state = True self.async_write_ha_state() @@ -152,7 +135,7 @@ class WolSwitch(SwitchEntity): if self._off_script is not None: self._off_script.run(context=self._context) - if self._assumed_state: + if self._attr_assumed_state: self._state = False self.async_write_ha_state() diff --git a/mypy.ini b/mypy.ini index cd6bc14169d..c546377ae4f 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2584,6 +2584,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.wake_on_lan.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.wallbox.*] check_untyped_defs = true disallow_incomplete_defs = true From 1c7800c224a12fbb3ab6ac7077fa6fe69d03c769 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 30 Oct 2022 00:32:36 +0000 Subject: [PATCH 0079/1033] [ci skip] Translation update --- .../components/bluetooth/translations/he.json | 6 ++ .../components/cast/translations/sv.json | 4 +- .../components/generic/translations/he.json | 14 +++++ .../components/group/translations/he.json | 4 +- .../homeassistant/translations/he.json | 1 + .../components/ibeacon/translations/he.json | 15 +++++ .../components/mqtt/translations/he.json | 59 +++++++++++++++---- .../components/mqtt/translations/ru.json | 2 +- .../components/mqtt/translations/zh-Hant.json | 47 +++++++++++++-- .../nibe_heatpump/translations/ru.json | 4 +- .../nibe_heatpump/translations/zh-Hant.json | 13 +++- .../components/oralb/translations/he.json | 22 +++++++ .../ovo_energy/translations/de.json | 1 + .../ovo_energy/translations/en.json | 4 +- .../ovo_energy/translations/es.json | 1 + .../ovo_energy/translations/hu.json | 1 + .../ovo_energy/translations/ru.json | 1 + .../ovo_energy/translations/zh-Hant.json | 1 + .../pushover/translations/zh-Hant.json | 3 +- .../rainmachine/translations/ru.json | 1 + .../rainmachine/translations/zh-Hant.json | 1 + .../components/sensibo/translations/ru.json | 4 +- .../sensibo/translations/sensor.he.json | 7 +++ .../sensibo/translations/zh-Hant.json | 4 +- .../components/sensor/translations/he.json | 3 + .../statistics/translations/he.json | 11 ++++ .../statistics/translations/ru.json | 4 ++ .../statistics/translations/zh-Hant.json | 12 ++++ .../components/tasmota/translations/he.json | 10 ++++ .../components/zamg/translations/fr.json | 12 ++++ .../components/zamg/translations/he.json | 12 ++++ .../components/zamg/translations/ru.json | 26 ++++++++ .../components/zamg/translations/zh-Hant.json | 26 ++++++++ 33 files changed, 305 insertions(+), 31 deletions(-) create mode 100644 homeassistant/components/oralb/translations/he.json create mode 100644 homeassistant/components/sensibo/translations/sensor.he.json create mode 100644 homeassistant/components/statistics/translations/he.json create mode 100644 homeassistant/components/statistics/translations/zh-Hant.json create mode 100644 homeassistant/components/zamg/translations/fr.json create mode 100644 homeassistant/components/zamg/translations/he.json create mode 100644 homeassistant/components/zamg/translations/ru.json create mode 100644 homeassistant/components/zamg/translations/zh-Hant.json diff --git a/homeassistant/components/bluetooth/translations/he.json b/homeassistant/components/bluetooth/translations/he.json index ad9bb727c62..0bf1deb0adf 100644 --- a/homeassistant/components/bluetooth/translations/he.json +++ b/homeassistant/components/bluetooth/translations/he.json @@ -29,6 +29,12 @@ } } }, + "issues": { + "haos_outdated": { + "description": "\u05db\u05d3\u05d9 \u05dc\u05e9\u05e4\u05e8 \u05d0\u05ea \u05d4\u05de\u05d4\u05d9\u05de\u05e0\u05d5\u05ea \u05d5\u05d4\u05d1\u05d9\u05e6\u05d5\u05e2\u05d9\u05dd \u05e9\u05dc Bluetooth, \u05de\u05d5\u05de\u05dc\u05e5 \u05de\u05d0\u05d5\u05d3 \u05dc\u05e2\u05d3\u05db\u05df \u05dc\u05d2\u05e8\u05e1\u05d4 9.0 \u05d5\u05d0\u05d9\u05dc\u05da \u05e9\u05dc \u05de\u05e2\u05e8\u05db\u05ea \u05d4\u05d4\u05e4\u05e2\u05dc\u05d4 Home Assistant.", + "title": "\u05e2\u05d3\u05db\u05d5\u05df \u05dc\u05de\u05e2\u05e8\u05db\u05ea \u05d4\u05d4\u05e4\u05e2\u05dc\u05d4 Home Assistant 9.0 \u05d5\u05d0\u05d9\u05dc\u05da" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/cast/translations/sv.json b/homeassistant/components/cast/translations/sv.json index 1bcc2ec4f72..2d2b2c68a45 100644 --- a/homeassistant/components/cast/translations/sv.json +++ b/homeassistant/components/cast/translations/sv.json @@ -9,7 +9,7 @@ "step": { "config": { "data": { - "known_hosts": "K\u00e4nad v\u00e4rdar" + "known_hosts": "K\u00e4nda v\u00e4rdar" }, "description": "K\u00e4nda v\u00e4rdar - En kommaseparerad lista \u00f6ver v\u00e4rdnamn eller IP-adresser f\u00f6r cast-enheter, anv\u00e4nd om mDNS-uppt\u00e4ckt inte fungerar.", "title": "Google Cast-konfiguration" @@ -34,7 +34,7 @@ }, "basic_options": { "data": { - "known_hosts": "K\u00e4nad v\u00e4rdar" + "known_hosts": "K\u00e4nda v\u00e4rdar" }, "description": "K\u00e4nda v\u00e4rdar - En kommaseparerad lista \u00f6ver v\u00e4rdnamn eller IP-adresser f\u00f6r cast-enheter, anv\u00e4nd om mDNS-uppt\u00e4ckt inte fungerar.", "title": "Google Cast-konfiguration" diff --git a/homeassistant/components/generic/translations/he.json b/homeassistant/components/generic/translations/he.json index 2a3458cd3e7..d4e2e495855 100644 --- a/homeassistant/components/generic/translations/he.json +++ b/homeassistant/components/generic/translations/he.json @@ -45,6 +45,13 @@ "verify_ssl": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 SSL" }, "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05db\u05d3\u05d9 \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05de\u05e6\u05dc\u05de\u05d4." + }, + "user_confirm_still": { + "data": { + "confirmed_ok": "\u05d4\u05ea\u05de\u05d5\u05e0\u05d4 \u05d4\u05d6\u05d5 \u05e0\u05e8\u05d0\u05d9\u05ea \u05d8\u05d5\u05d1." + }, + "description": "![\u05ea\u05e6\u05d5\u05d2\u05d4 \u05de\u05e7\u05d3\u05d9\u05de\u05d4 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 \u05d1\u05de\u05e6\u05dc\u05de\u05d4]({preview_url})", + "title": "\u05ea\u05e6\u05d5\u05d2\u05d4 \u05de\u05e7\u05d3\u05d9\u05de\u05d4" } } }, @@ -68,6 +75,13 @@ "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { + "confirm_still": { + "data": { + "confirmed_ok": "\u05d4\u05ea\u05de\u05d5\u05e0\u05d4 \u05d4\u05d6\u05d5 \u05e0\u05e8\u05d0\u05d9\u05ea \u05d8\u05d5\u05d1." + }, + "description": "![\u05ea\u05e6\u05d5\u05d2\u05d4 \u05de\u05e7\u05d3\u05d9\u05de\u05d4 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 \u05d1\u05de\u05e6\u05dc\u05de\u05d4]({preview_url})", + "title": "\u05ea\u05e6\u05d5\u05d2\u05d4 \u05de\u05e7\u05d3\u05d9\u05de\u05d4" + }, "content_type": { "data": { "content_type": "\u05e1\u05d5\u05d2 \u05ea\u05d5\u05db\u05df" diff --git a/homeassistant/components/group/translations/he.json b/homeassistant/components/group/translations/he.json index a2507082dda..9d0f16b4f61 100644 --- a/homeassistant/components/group/translations/he.json +++ b/homeassistant/components/group/translations/he.json @@ -66,7 +66,9 @@ "cover": "\u05e7\u05d1\u05d5\u05e6\u05ea \u05d5\u05d9\u05dc\u05d5\u05e0\u05d5\u05ea", "fan": "\u05e7\u05d1\u05d5\u05e6\u05ea \u05d0\u05d9\u05d5\u05d5\u05e8\u05d5\u05e8", "light": "\u05e7\u05d1\u05d5\u05e6\u05ea \u05ea\u05d0\u05d5\u05e8\u05d4", - "media_player": "\u05e7\u05d1\u05d5\u05e6\u05ea \u05e0\u05d2\u05e0\u05d9 \u05de\u05d3\u05d9\u05d4" + "lock": "\u05e0\u05e2\u05d9\u05dc\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4", + "media_player": "\u05e7\u05d1\u05d5\u05e6\u05ea \u05e0\u05d2\u05e0\u05d9 \u05de\u05d3\u05d9\u05d4", + "switch": "\u05d4\u05d7\u05dc\u05e4\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4" }, "title": "\u05d4\u05d5\u05e1\u05e4\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4" } diff --git a/homeassistant/components/homeassistant/translations/he.json b/homeassistant/components/homeassistant/translations/he.json index 20de5a2d1b7..0b9afe54386 100644 --- a/homeassistant/components/homeassistant/translations/he.json +++ b/homeassistant/components/homeassistant/translations/he.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "config_dir": "\u05e1\u05e4\u05e8\u05d9\u05d9\u05ea \u05ea\u05e6\u05d5\u05e8\u05d4", "docker": "Docker", "hassio": "\u05de\u05e4\u05e7\u05d7", "installation_type": "\u05e1\u05d5\u05d2 \u05d4\u05ea\u05e7\u05e0\u05d4", diff --git a/homeassistant/components/ibeacon/translations/he.json b/homeassistant/components/ibeacon/translations/he.json index d0c3523da94..04f865d0d69 100644 --- a/homeassistant/components/ibeacon/translations/he.json +++ b/homeassistant/components/ibeacon/translations/he.json @@ -1,7 +1,22 @@ { "config": { "abort": { + "bluetooth_not_available": "\u05d9\u05e9 \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05dc\u05e4\u05d7\u05d5\u05ea \u05de\u05ea\u05d0\u05dd Bluetooth \u05d0\u05d5 \u05e9\u05dc\u05d8 \u05e8\u05d7\u05d5\u05e7 \u05d0\u05d7\u05d3 \u05dc\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1-iBeacon Tracker.", "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." + }, + "step": { + "user": { + "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05de\u05e2\u05e7\u05d1 iBeacon?" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "min_rssi": "RSSI \u05de\u05d9\u05e0\u05d9\u05de\u05dc\u05d9" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/mqtt/translations/he.json b/homeassistant/components/mqtt/translations/he.json index f67b83b243e..53e67968d95 100644 --- a/homeassistant/components/mqtt/translations/he.json +++ b/homeassistant/components/mqtt/translations/he.json @@ -5,25 +5,43 @@ "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." }, "error": { - "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" + "bad_birth": "\u05e0\u05d5\u05e9\u05d0 \u05dc\u05d9\u05d3\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "bad_certificate": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05d4-CA \u05d0\u05d9\u05e0\u05d5 \u05d7\u05d5\u05e7\u05d9", + "bad_client_cert": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d9\u05e9 \u05dc\u05d5\u05d5\u05d3\u05d0 \u05e9\u05e7\u05d5\u05d1\u05e5 \u05de\u05e7\u05d5\u05d3\u05d3 PEM \u05de\u05e1\u05d5\u05e4\u05e7", + "bad_client_cert_key": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7 \u05d5\u05e4\u05e8\u05d8\u05d9 \u05d0\u05d9\u05e0\u05dd \u05d6\u05d5\u05d2 \u05d7\u05d5\u05e7\u05d9", + "bad_client_key": "\u05de\u05e4\u05ea\u05d7 \u05e4\u05e8\u05d8\u05d9 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d9\u05e9 \u05dc\u05d5\u05d5\u05d3\u05d0 \u05e9\u05e7\u05d5\u05d1\u05e5 \u05de\u05e7\u05d5\u05d3\u05d3 PEM \u05de\u05e1\u05d5\u05e4\u05e7 \u05dc\u05dc\u05d0 \u05e1\u05d9\u05e1\u05de\u05d4", + "bad_discovery_prefix": "\u05d2\u05d9\u05dc\u05d5\u05d9 \u05dc\u05e4\u05d9 \u05e7\u05d9\u05d3\u05d5\u05de\u05ea \u05d0\u05d9\u05e0\u05d5 \u05d7\u05d5\u05e7\u05d9", + "bad_will": "\u05e0\u05d5\u05e9\u05d0 \u05e6\u05d5\u05d5\u05d0\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05ea", + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_inclusion": "\u05d9\u05e9 \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 \u05d4\u05dc\u05e7\u05d5\u05d7 \u05d5\u05d4\u05de\u05e4\u05ea\u05d7 \u05d4\u05e4\u05e8\u05d8\u05d9 \u05d9\u05d7\u05d3" }, "step": { "broker": { "data": { - "broker": "\u05d1\u05e8\u05d5\u05e7\u05e8", + "advanced_options": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05de\u05ea\u05e7\u05d3\u05de\u05d5\u05ea", + "broker": "\u05de\u05ea\u05d5\u05d5\u05da", + "certificate": "\u05e0\u05ea\u05d9\u05d1 \u05dc\u05e7\u05d5\u05d1\u05e5 \u05d0\u05d9\u05e9\u05d5\u05e8 CA \u05de\u05d5\u05ea\u05d0\u05dd \u05d0\u05d9\u05e9\u05d9\u05ea", + "client_cert": "\u05e0\u05ea\u05d9\u05d1 \u05dc\u05e7\u05d5\u05d1\u05e5 \u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7", + "client_id": "\u05de\u05d6\u05d4\u05d4 \u05dc\u05e7\u05d5\u05d7 (\u05dc\u05d4\u05e9\u05d0\u05d9\u05e8 \u05e8\u05d9\u05e7 \u05dc\u05de\u05d6\u05d4\u05d4 \u05e9\u05e0\u05d5\u05e6\u05e8 \u05d1\u05d0\u05d5\u05e4\u05df \u05d0\u05e7\u05e8\u05d0\u05d9)", + "client_key": "\u05e0\u05ea\u05d9\u05d1 \u05dc\u05e7\u05d5\u05d1\u05e5 \u05de\u05e4\u05ea\u05d7 \u05e4\u05e8\u05d8\u05d9", "discovery": "\u05d0\u05e4\u05e9\u05e8 \u05d2\u05d9\u05dc\u05d5\u05d9", + "keepalive": "\u05d4\u05d6\u05de\u05df \u05e9\u05d1\u05d9\u05df \u05e9\u05dc\u05d9\u05d7\u05ea \u05d4\u05d5\u05d3\u05e2\u05d5\u05ea \u05dc\u05e9\u05de\u05d5\u05e8 \u05d1\u05d7\u05d9\u05d9\u05dd", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", "port": "\u05e4\u05ea\u05d7\u05d4", + "protocol": "\u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc MQTT", + "set_ca_cert": "\u05d0\u05d9\u05de\u05d5\u05ea \u05ea\u05e2\u05d5\u05d3\u05ea \u05de\u05ea\u05d5\u05d5\u05da", + "set_client_cert": "\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05ea\u05e2\u05d5\u05d3\u05ea \u05dc\u05e7\u05d5\u05d7", + "tls_insecure": "\u05d4\u05ea\u05e2\u05dc\u05de\u05d5\u05ea \u05de\u05d0\u05d9\u05de\u05d5\u05ea \u05ea\u05e2\u05d5\u05d3\u05ea \u05d4\u05de\u05ea\u05d5\u05d5\u05da", "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" }, - "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05e4\u05e8\u05d8\u05d9 \u05d4\u05d7\u05d9\u05d1\u05d5\u05e8 \u05e9\u05dc \u05d4\u05d1\u05e8\u05d5\u05e7\u05e8 MQTT \u05e9\u05dc\u05da." + "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05e4\u05e8\u05d8\u05d9 \u05d4\u05d7\u05d9\u05d1\u05d5\u05e8 \u05e9\u05dc \u05de\u05ea\u05d5\u05d5\u05da MQTT \u05e9\u05dc\u05da." }, "hassio_confirm": { "data": { "discovery": "\u05d0\u05d9\u05e4\u05e9\u05d5\u05e8 \u05d2\u05d9\u05dc\u05d5\u05d9" }, "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea \u05ea\u05e6\u05d5\u05e8\u05ea \u05d4-Home Assistant \u05db\u05da \u05e9\u05ea\u05ea\u05d7\u05d1\u05e8 \u05dc\u05de\u05ea\u05d5\u05d5\u05da MQTT \u05d4\u05de\u05e1\u05d5\u05e4\u05e7 \u05e2\u05dc \u05d9\u05d3\u05d9 \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 {addon}?", - "title": "MQTT \u05d1\u05e8\u05d5\u05e7\u05e8 \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea \u05d4\u05e8\u05d7\u05d1\u05ea Home Assistant" + "title": "\u05de\u05ea\u05d5\u05d5\u05da MQTT \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea \u05d4\u05e8\u05d7\u05d1\u05ea Home Assistant" } } }, @@ -53,23 +71,43 @@ "deprecated_yaml": { "description": "\u05de\u05d5\u05d2\u05d3\u05e8 \u05d1\u05d0\u05d5\u05e4\u05df \u05d9\u05d3\u05e0\u05d9 MQTT {platform} \u05e0\u05de\u05e6\u05d0 \u05ea\u05d7\u05ea \u05de\u05e4\u05ea\u05d7 \u05e4\u05dc\u05d8\u05e4\u05d5\u05e8\u05de\u05d4 `{platform}`.\n\n\u05d9\u05e9 \u05dc\u05d4\u05e2\u05d1\u05d9\u05e8 \u05d0\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05dc\u05de\u05e4\u05ea\u05d7 \u05d4\u05d0\u05d9\u05e0\u05d8\u05d2\u05e8\u05e6\u05d9\u05d4 `mqtt`\u05d5\u05dc\u05d4\u05e4\u05e2\u05d9\u05dc \u05de\u05d7\u05d3\u05e9 \u05d0\u05ea Home Assistant \u05db\u05d3\u05d9 \u05dc\u05e4\u05ea\u05d5\u05e8 \u05d1\u05e2\u05d9\u05d4 \u05d6\u05d5. \u05d9\u05e9 \u05dc\u05e2\u05d9\u05d9\u05df \u05d1[\u05ea\u05d9\u05e2\u05d5\u05d3]({more_info_url}), \u05dc\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", "title": "MQTT {platform} \u05d4\u05de\u05d5\u05d2\u05d3\u05e8 \u05d1\u05d0\u05d5\u05e4\u05df \u05d9\u05d3\u05e0\u05d9 \u05d6\u05e7\u05d5\u05e7 \u05dc\u05ea\u05e9\u05d5\u05de\u05ea \u05dc\u05d1" + }, + "deprecated_yaml_broker_settings": { + "description": "\u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05d4\u05d1\u05d0\u05d5\u05ea \u05e9\u05e0\u05de\u05e6\u05d0\u05d5 \u05d1-`configuration.yaml` \u05d4\u05d5\u05e2\u05d1\u05e8\u05d5 \u05dc\u05e2\u05e8\u05da MQTT config \u05d5\u05db\u05e2\u05ea \u05d9\u05e2\u05e7\u05e4\u05d5 \u05d0\u05ea \u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05d1-`configuration.yaml`:\n`{deprecated_settings}`\n\n\u05d9\u05e9 \u05dc\u05d4\u05e1\u05d9\u05e8 \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05d0\u05dc\u05d5 \u05de-`configuration.yaml` \u05d5\u05dc\u05d4\u05e4\u05e2\u05d9\u05dc \u05de\u05d7\u05d3\u05e9 \u05d0\u05ea Home Assistant \u05db\u05d3\u05d9 \u05dc\u05e4\u05ea\u05d5\u05e8 \u05d1\u05e2\u05d9\u05d4 \u05d6\u05d5. \u05e0\u05d9\u05ea\u05df \u05dc\u05e2\u05d9\u05d9\u05df \u05d1[\u05ea\u05d9\u05e2\u05d5\u05d3]({more_info_url}), \u05dc\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", + "title": "\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea MQTT \u05e9\u05d4\u05d5\u05e6\u05d0\u05d5 \u05de\u05e9\u05d9\u05de\u05d5\u05e9 \u05e0\u05de\u05e6\u05d0\u05d5\u05ea \u05d1'configuration.yaml'" } }, "options": { "error": { - "bad_birth": "\u05e0\u05d5\u05e9\u05d0 \u05dc\u05d9\u05d3\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9.", - "bad_will": "\u05e0\u05d5\u05e9\u05d0 \u05d7\u05d5\u05e7 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9.", - "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" + "bad_birth": "\u05e0\u05d5\u05e9\u05d0 \u05dc\u05d9\u05d3\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "bad_certificate": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05d4-CA \u05d0\u05d9\u05e0\u05d5 \u05d7\u05d5\u05e7\u05d9", + "bad_client_cert": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d9\u05e9 \u05dc\u05d5\u05d5\u05d3\u05d0 \u05e9\u05e7\u05d5\u05d1\u05e5 \u05de\u05e7\u05d5\u05d3\u05d3 PEM \u05de\u05e1\u05d5\u05e4\u05e7", + "bad_client_cert_key": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7 \u05d5\u05e4\u05e8\u05d8\u05d9 \u05d0\u05d9\u05e0\u05dd \u05d6\u05d5\u05d2 \u05d7\u05d5\u05e7\u05d9", + "bad_client_key": "\u05de\u05e4\u05ea\u05d7 \u05e4\u05e8\u05d8\u05d9 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d9\u05e9 \u05dc\u05d5\u05d5\u05d3\u05d0 \u05e9\u05e7\u05d5\u05d1\u05e5 \u05de\u05e7\u05d5\u05d3\u05d3 PEM \u05de\u05e1\u05d5\u05e4\u05e7 \u05dc\u05dc\u05d0 \u05e1\u05d9\u05e1\u05de\u05d4", + "bad_discovery_prefix": "\u05e7\u05d9\u05d3\u05d5\u05de\u05ea \u05d2\u05d9\u05dc\u05d5\u05d9 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05ea", + "bad_will": "\u05e0\u05d5\u05e9\u05d0 \u05e6\u05d5\u05d5\u05d0\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05ea", + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_inclusion": "\u05d9\u05e9 \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 \u05d4\u05dc\u05e7\u05d5\u05d7 \u05d5\u05d4\u05de\u05e4\u05ea\u05d7 \u05d4\u05e4\u05e8\u05d8\u05d9 \u05d9\u05d7\u05d3" }, "step": { "broker": { "data": { - "broker": "\u05d1\u05e8\u05d5\u05e7\u05e8", + "advanced_options": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05de\u05ea\u05e7\u05d3\u05de\u05d5\u05ea", + "broker": "\u05de\u05ea\u05d5\u05d5\u05da", + "certificate": "\u05d4\u05e2\u05dc\u05d0\u05ea \u05e7\u05d5\u05d1\u05e5 \u05d0\u05d9\u05e9\u05d5\u05e8 CA \u05de\u05d5\u05ea\u05d0\u05dd \u05d0\u05d9\u05e9\u05d9\u05ea", + "client_cert": "\u05d4\u05e2\u05dc\u05d0\u05ea \u05e7\u05d5\u05d1\u05e5 \u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7", + "client_id": "\u05de\u05d6\u05d4\u05d4 \u05dc\u05e7\u05d5\u05d7 (\u05dc\u05d4\u05e9\u05d0\u05d9\u05e8 \u05e8\u05d9\u05e7 \u05dc\u05de\u05d6\u05d4\u05d4 \u05e9\u05e0\u05d5\u05e6\u05e8 \u05d1\u05d0\u05d5\u05e4\u05df \u05d0\u05e7\u05e8\u05d0\u05d9)", + "client_key": "\u05d4\u05e2\u05dc\u05d0\u05ea \u05e7\u05d5\u05d1\u05e5 \u05de\u05e4\u05ea\u05d7 \u05e4\u05e8\u05d8\u05d9", + "keepalive": "\u05d4\u05d6\u05de\u05df \u05e9\u05d1\u05d9\u05df \u05e9\u05dc\u05d9\u05d7\u05ea \u05d4\u05d5\u05d3\u05e2\u05d5\u05ea \u05dc\u05e9\u05de\u05d5\u05e8 \u05d1\u05d7\u05d9\u05d9\u05dd", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", "port": "\u05e4\u05ea\u05d7\u05d4", + "protocol": "\u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc MQTT", + "set_ca_cert": "\u05d0\u05d9\u05de\u05d5\u05ea \u05ea\u05e2\u05d5\u05d3\u05ea \u05de\u05ea\u05d5\u05d5\u05da", + "set_client_cert": "\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05ea\u05e2\u05d5\u05d3\u05ea \u05dc\u05e7\u05d5\u05d7", + "tls_insecure": "\u05d4\u05ea\u05e2\u05dc\u05de\u05d5\u05ea \u05de\u05d0\u05d9\u05de\u05d5\u05ea \u05ea\u05e2\u05d5\u05d3\u05ea \u05d4\u05de\u05ea\u05d5\u05d5\u05da", "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" }, - "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05e4\u05e8\u05d8\u05d9 \u05d4\u05d7\u05d9\u05d1\u05d5\u05e8 \u05e9\u05dc \u05d4\u05d1\u05e8\u05d5\u05e7\u05e8 MQTT \u05e9\u05dc\u05da.", + "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05e4\u05e8\u05d8\u05d9 \u05d4\u05d7\u05d9\u05d1\u05d5\u05e8 \u05e9\u05dc \u05de\u05ea\u05d5\u05d5\u05da MQTT \u05e9\u05dc\u05da.", "title": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05de\u05ea\u05d5\u05d5\u05da" }, "options": { @@ -80,13 +118,14 @@ "birth_retain": "\u05d4\u05d5\u05d3\u05e2\u05ea \u05dc\u05d9\u05d3\u05d4 \u05e0\u05e9\u05de\u05e8\u05ea", "birth_topic": "\u05e0\u05d5\u05e9\u05d0 \u05d4\u05d5\u05d3\u05e2\u05ea \u05dc\u05d9\u05d3\u05d4", "discovery": "\u05d0\u05d9\u05e4\u05e9\u05d5\u05e8 \u05d2\u05d9\u05dc\u05d5\u05d9", + "discovery_prefix": "\u05d2\u05d9\u05dc\u05d5\u05d9 \u05dc\u05e4\u05d9 \u05e7\u05d9\u05d3\u05d5\u05de\u05ea", "will_enable": "\u05d4\u05e4\u05d9\u05db\u05ea \u05d4\u05d5\u05d3\u05e2\u05d4 \u05dc\u05d6\u05de\u05d9\u05e0\u05d4", "will_payload": "\u05d4\u05d0\u05dd \u05ea\u05d5\u05db\u05df \u05de\u05e0\u05d4 \u05e9\u05dc \u05d4\u05d5\u05d3\u05e2\u05d4", "will_qos": "\u05d4\u05d0\u05dd \u05d4\u05d5\u05d3\u05e2\u05ea QoS", "will_retain": "\u05d4\u05d0\u05dd \u05d4\u05d4\u05d5\u05d3\u05e2\u05d4 \u05ea\u05d9\u05e9\u05de\u05e8", "will_topic": "\u05d4\u05d0\u05dd \u05e0\u05d5\u05e9\u05d0 \u05d4\u05d5\u05d3\u05e2\u05d4" }, - "description": "\u05d2\u05d9\u05dc\u05d5\u05d9 - \u05d0\u05dd \u05d2\u05d9\u05dc\u05d5\u05d9 \u05de\u05d5\u05e4\u05e2\u05dc (\u05de\u05d5\u05de\u05dc\u05e5), Home Assistant \u05d9\u05d2\u05dc\u05d4 \u05d1\u05d0\u05d5\u05e4\u05df \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d5\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea \u05d4\u05de\u05e4\u05e8\u05e1\u05de\u05d9\u05dd \u05d0\u05ea \u05ea\u05e6\u05d5\u05e8\u05ea\u05dd \u05d1\u05de\u05ea\u05d5\u05d5\u05da MQTT. \u05d0\u05dd \u05d4\u05d2\u05d9\u05dc\u05d5\u05d9 \u05d0\u05d9\u05e0\u05d5 \u05de\u05d5\u05e4\u05e2\u05dc, \u05db\u05dc \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05d7\u05d9\u05d9\u05d1\u05ea \u05dc\u05d4\u05d9\u05e2\u05e9\u05d5\u05ea \u05d1\u05d0\u05d5\u05e4\u05df \u05d9\u05d3\u05e0\u05d9.\n\u05d4\u05d5\u05d3\u05e2\u05ea \u05dc\u05d9\u05d3\u05d4 - \u05d4\u05d5\u05d3\u05e2\u05ea \u05d4\u05dc\u05d9\u05d3\u05d4 \u05ea\u05d9\u05e9\u05dc\u05d7 \u05d1\u05db\u05dc \u05e4\u05e2\u05dd \u05e9-Home Assistant \u05de\u05ea\u05d7\u05d1\u05e8 (\u05de\u05d7\u05d3\u05e9) \u05dc\u05de\u05ea\u05d5\u05d5\u05da MQTT.\n\u05d4\u05d5\u05d3\u05e2\u05ea \u05e8\u05e6\u05d5\u05df - \u05d4\u05d5\u05d3\u05e2\u05ea \u05d4\u05e8\u05e6\u05d5\u05df \u05ea\u05d9\u05e9\u05dc\u05d7 \u05d1\u05db\u05dc \u05e4\u05e2\u05dd \u05e9-Home Assistant \u05d9\u05d0\u05d1\u05d3 \u05d0\u05ea \u05d4\u05e7\u05e9\u05e8 \u05e9\u05dc\u05d5 \u05dc\u05de\u05ea\u05d5\u05d5\u05da, \u05d2\u05dd \u05d1\u05de\u05e7\u05e8\u05d4 \u05e9\u05dc \u05e0\u05d9\u05ea\u05d5\u05e7 \u05e0\u05e7\u05d9 (\u05dc\u05de\u05e9\u05dc \u05db\u05d9\u05d1\u05d5\u05d9 \u05e9\u05dc Home Assistant) \u05d5\u05d2\u05dd \u05d1\u05de\u05e7\u05e8\u05d4 \u05e9\u05dc \u05e0\u05d9\u05ea\u05d5\u05e7 \u05dc\u05d0 \u05e0\u05e7\u05d9 (\u05dc\u05de\u05e9\u05dc Home Assistant \u05de\u05ea\u05e8\u05e1\u05e7 \u05d0\u05d5 \u05de\u05d0\u05d1\u05d3 \u05d0\u05ea \u05d7\u05d9\u05d1\u05d5\u05e8 \u05d4\u05e8\u05e9\u05ea \u05e9\u05dc\u05d5).", + "description": "\u05d2\u05d9\u05dc\u05d5\u05d9 - \u05d0\u05dd \u05d4\u05d2\u05d9\u05dc\u05d5\u05d9 \u05d6\u05de\u05d9\u05df (\u05de\u05d5\u05de\u05dc\u05e5), Home Assistant \u05d9\u05d2\u05dc\u05d4 \u05d1\u05d0\u05d5\u05e4\u05df \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d5\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea \u05d4\u05de\u05e4\u05e8\u05e1\u05de\u05d9\u05dd \u05d0\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc\u05d4\u05dd \u05d1\u05de\u05ea\u05d5\u05d5\u05da MQTT. \u05d0\u05dd \u05d4\u05d2\u05d9\u05dc\u05d5\u05d9 \u05de\u05d5\u05e9\u05d1\u05ea, \u05db\u05dc \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05d7\u05d9\u05d9\u05d1\u05ea \u05dc\u05d4\u05d9\u05e2\u05e9\u05d5\u05ea \u05d1\u05d0\u05d5\u05e4\u05df \u05d9\u05d3\u05e0\u05d9.\n\u05d2\u05d9\u05dc\u05d5\u05d9 \u05dc\u05e4\u05d9 \u05e7\u05d9\u05d3\u05d5\u05de\u05ea - \u05d4\u05e7\u05d9\u05d3\u05d5\u05de\u05ea \u05e9\u05e0\u05d5\u05e9\u05d0 \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05d2\u05d9\u05dc\u05d5\u05d9 \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9 \u05d7\u05d9\u05d9\u05d1 \u05dc\u05d4\u05ea\u05d7\u05d9\u05dc \u05d1\u05d4.\n\u05d4\u05d5\u05d3\u05e2\u05ea \u05dc\u05d9\u05d3\u05d4 - \u05d4\u05d5\u05d3\u05e2\u05ea \u05d4\u05dc\u05d9\u05d3\u05d4 \u05ea\u05d9\u05e9\u05dc\u05d7 \u05d1\u05db\u05dc \u05e4\u05e2\u05dd \u05e9-Home Assistant (\u05de\u05d7\u05d3\u05e9) \u05d9\u05ea\u05d7\u05d1\u05e8 \u05dc\u05de\u05ea\u05d5\u05d5\u05da MQTT.\n\u05d4\u05d5\u05d3\u05e2\u05ea \u05e6\u05d5\u05d5\u05d0\u05d4 - \u05d4\u05d5\u05d3\u05e2\u05ea \u05d4\u05e6\u05d5\u05d5\u05d0\u05d4 \u05ea\u05d9\u05e9\u05dc\u05d7 \u05d1\u05db\u05dc \u05e4\u05e2\u05dd \u05e9-Home Assistant \u05de\u05d0\u05d1\u05d3 \u05d0\u05ea \u05d4\u05d7\u05d9\u05d1\u05d5\u05e8 \u05e9\u05dc\u05d5 \u05dc\u05de\u05ea\u05d5\u05d5\u05da, \u05d4\u05df \u05d1\u05de\u05e7\u05e8\u05d4 \u05e9\u05dc \u05e0\u05d9\u05e7\u05d5\u05d9 (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, \u05db\u05d9\u05d1\u05d5\u05d9 Home Assistant) \u05d5\u05d4\u05df \u05d1\u05de\u05e7\u05e8\u05d4 \u05e9\u05dc \u05e0\u05d9\u05ea\u05d5\u05e7 \u05dc\u05d0 \u05e0\u05e7\u05d9 (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, Home Assistant \u05e7\u05d5\u05e8\u05e1 \u05d0\u05d5 \u05de\u05d0\u05d1\u05d3 \u05d0\u05ea \u05d7\u05d9\u05d1\u05d5\u05e8 \u05d4\u05e8\u05e9\u05ea \u05e9\u05dc\u05d5).", "title": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea MQTT" } } diff --git a/homeassistant/components/mqtt/translations/ru.json b/homeassistant/components/mqtt/translations/ru.json index 9c037b97b23..069a1066c86 100644 --- a/homeassistant/components/mqtt/translations/ru.json +++ b/homeassistant/components/mqtt/translations/ru.json @@ -121,7 +121,7 @@ "will_retain": "\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0442\u043e\u043f\u0438\u043a \u043e\u0431 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438", "will_topic": "\u0422\u043e\u043f\u0438\u043a \u043e\u0431 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 (LWT)" }, - "description": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u2014 \u0435\u0441\u043b\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f), Home Assistant \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u044e\u0442 \u0441\u0432\u043e\u044e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043d\u0430 \u0431\u0440\u043e\u043a\u0435\u0440\u0435 MQTT. \u0415\u0441\u043b\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e, \u0432\u0441\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432\u0440\u0443\u0447\u043d\u0443\u044e.\n\u0422\u043e\u043f\u0438\u043a \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u2014 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 Home Assistant \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043a \u0431\u0440\u043e\u043a\u0435\u0440\u0443 MQTT.\n\u0422\u043e\u043f\u0438\u043a \u043e\u0431 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u2014 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u043e\u0433\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043e\u0442 \u0431\u0440\u043e\u043a\u0435\u0440\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 Home Assistant), \u0442\u0430\u043a \u0438 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u043e\u0433\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u0438 \u0441\u0431\u043e\u0435 Home Assistant \u0438\u043b\u0438 \u043f\u043e\u0442\u0435\u0440\u0435 \u0441\u0435\u0442\u0435\u0432\u043e\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f).", + "description": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u2014 \u0435\u0441\u043b\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f), Home Assistant \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u044e\u0442 \u0441\u0432\u043e\u044e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043d\u0430 \u0431\u0440\u043e\u043a\u0435\u0440\u0435 MQTT. \u0415\u0441\u043b\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e, \u0432\u0441\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432\u0440\u0443\u0447\u043d\u0443\u044e.\n\u041f\u0440\u0435\u0444\u0438\u043a\u0441 \u0430\u0432\u0442\u043e\u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u2014 \u043f\u0440\u0435\u0444\u0438\u043a\u0441, \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043f\u0438\u043a \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f.\n\u0422\u043e\u043f\u0438\u043a \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u2014 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 Home Assistant \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043a \u0431\u0440\u043e\u043a\u0435\u0440\u0443 MQTT.\n\u0422\u043e\u043f\u0438\u043a \u043e\u0431 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u2014 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u043e\u0433\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043e\u0442 \u0431\u0440\u043e\u043a\u0435\u0440\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 Home Assistant), \u0442\u0430\u043a \u0438 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u043e\u0433\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u0438 \u0441\u0431\u043e\u0435 Home Assistant \u0438\u043b\u0438 \u043f\u043e\u0442\u0435\u0440\u0435 \u0441\u0435\u0442\u0435\u0432\u043e\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f).", "title": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b MQTT" } } diff --git a/homeassistant/components/mqtt/translations/zh-Hant.json b/homeassistant/components/mqtt/translations/zh-Hant.json index c7d08ce7482..eadaebee738 100644 --- a/homeassistant/components/mqtt/translations/zh-Hant.json +++ b/homeassistant/components/mqtt/translations/zh-Hant.json @@ -7,15 +7,31 @@ "error": { "bad_birth": "Birth \u4e3b\u984c\u7121\u6548", "bad_certificate": "CA \u8a8d\u8b49\u7121\u6548", - "cannot_connect": "\u9023\u7dda\u5931\u6557" + "bad_client_cert": "\u5ba2\u6236\u7aef\u6191\u8b49\u7121\u6548\u3001\u8acb\u78ba\u5b9a\u63d0\u4f9b\u70ba PEM \u7de8\u78bc\u6a94\u6848", + "bad_client_cert_key": "\u5ba2\u6236\u7aef\u6191\u8b49\u8207\u79c1\u9470\u4e0d\u7b26\u5408", + "bad_client_key": "\u79c1\u9470\u7121\u6548\u3001\u8acb\u78ba\u5b9a\u63d0\u4f9b\u70ba PEM \u7de8\u78bc\u6a94\u6848\u4e26\u4e0d\u5177\u5bc6\u78bc", + "bad_discovery_prefix": "\u641c\u7d22\u4e3b\u984c prefix \u7121\u6548", + "bad_will": "Will \u4e3b\u984c\u7121\u6548", + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_inclusion": "\u5ba2\u6236\u7aef\u6191\u8b49\u8207\u79c1\u9470\u5fc5\u9808\u4e00\u8d77\u8a2d\u5b9a" }, "step": { "broker": { "data": { + "advanced_options": "\u9032\u968e\u8a2d\u5b9a", "broker": "Broker", + "certificate": "\u81ea\u8a02 CA \u6191\u8b49\u6a94\u6848\u8def\u5f91", + "client_cert": "\u5ba2\u6236\u7aef\u6191\u8b49\u6a94\u6848\u8def\u5f91", + "client_id": "\u5ba2\u6236\u7aef ID (\u4fdd\u6301\u7a7a\u767d\u5c07\u81ea\u52d5\u7522\u751f)", + "client_key": "\u79c1\u9470\u6a94\u6848\u8def\u5f91", "discovery": "\u958b\u555f\u641c\u5c0b", + "keepalive": "\u50b3\u9001\u4fdd\u6301\u6d3b\u52d5\u8a0a\u606f\u9593\u9694\u6642\u9593", "password": "\u5bc6\u78bc", "port": "\u901a\u8a0a\u57e0", + "protocol": "MQTT \u901a\u8a0a\u5354\u5b9a", + "set_ca_cert": "\u4ee3\u7406\u4f3a\u670d\u5668\u6191\u8b49\u9a57\u8b49", + "set_client_cert": "\u4f7f\u7528\u5ba2\u6236\u7aef\u6191\u8b49", + "tls_insecure": "\u5ffd\u7565\u4ee3\u7406\u4f3a\u670d\u5668\u6191\u8b49\u9a57\u8b49", "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, "description": "\u8acb\u8f38\u5165 MQTT Broker \u9023\u7dda\u8cc7\u8a0a\u3002" @@ -55,20 +71,40 @@ "deprecated_yaml": { "description": "\u65bc\u5e73\u53f0\u9375 `{platform}` \u5167\u627e\u5230\u624b\u52d5\u8a2d\u5b9a\u4e4b MQTT {platform}\u3002\n\n\u8acb\u5c07\u8a2d\u5b9a\u79fb\u52d5\u81f3 `mqtt` \u6574\u5408\u9375\u4e26\u91cd\u555f Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002\u8acb\u53c3\u95b1 [\u6587\u4ef6]({more_info_url}) \u4ee5\u7372\u5f97\u8a73\u7d30\u8cc7\u8a0a\u3002", "title": "\u624b\u52d5\u8a2d\u5b9a\u4e4b MQTT {platform} \u6709\u4e9b\u554f\u984c\u9700\u8981\u8655\u7406" + }, + "deprecated_yaml_broker_settings": { + "description": "\u65bc `configuration.yaml` \u4e2d\u767c\u73fe\u5df2\u7d93\u9077\u79fb\u81f3 MQTT \u8a2d\u5b9a\u5be6\u9ad4\u4e4b\u8a2d\u5b9a\u3001\u5c07\u8986\u84cb `configuration.yaml` \u4e2d\u7684\u8a2d\u5b9a\uff1a`{deprecated_settings}`\n\n\u8acb\u79fb\u9664 `configuration.yaml` \u4e2d\u7684\u8a2d\u5b9a\u3001\u4e26\u91cd\u65b0\u555f\u52d5 Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002\u53c3\u95b1 [\u6587\u4ef6]({more_info_url}) \u4ee5\u7372\u5f97\u8a73\u7d30\u8cc7\u8a0a\u3002", + "title": "\u65bc `configuration.yaml` \u4e2d\u767c\u73fe\u5df2\u505c\u7528\u7684 MQTT \u8a2d\u5b9a" } }, "options": { "error": { - "bad_birth": "Birth \u4e3b\u984c\u7121\u6548\u3002", - "bad_will": "Will \u4e3b\u984c\u7121\u6548\u3002", - "cannot_connect": "\u9023\u7dda\u5931\u6557" + "bad_birth": "Birth \u4e3b\u984c\u7121\u6548", + "bad_certificate": "CA \u8a8d\u8b49\u7121\u6548", + "bad_client_cert": "\u5ba2\u6236\u7aef\u6191\u8b49\u7121\u6548\u3001\u8acb\u78ba\u5b9a\u63d0\u4f9b\u70ba PEM \u7de8\u78bc\u6a94\u6848", + "bad_client_cert_key": "\u5ba2\u6236\u7aef\u6191\u8b49\u8207\u79c1\u9470\u4e0d\u7b26\u5408", + "bad_client_key": "\u79c1\u9470\u7121\u6548\u3001\u8acb\u78ba\u5b9a\u63d0\u4f9b\u70ba PEM \u7de8\u78bc\u6a94\u6848\u4e26\u4e0d\u5177\u5bc6\u78bc", + "bad_discovery_prefix": "\u641c\u7d22\u4e3b\u984c prefix \u7121\u6548", + "bad_will": "Will \u4e3b\u984c\u7121\u6548", + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_inclusion": "\u5ba2\u6236\u7aef\u6191\u8b49\u8207\u79c1\u9470\u5fc5\u9808\u4e00\u8d77\u8a2d\u5b9a" }, "step": { "broker": { "data": { + "advanced_options": "\u9032\u968e\u8a2d\u5b9a", "broker": "Broker", + "certificate": "\u4e0a\u50b3 CA \u6191\u8b49\u6a94\u6848", + "client_cert": "\u4e0a\u50b3\u5ba2\u6236\u7aef\u6191\u8b49\u6a94\u6848", + "client_id": "\u5ba2\u6236\u7aef ID (\u4fdd\u6301\u7a7a\u767d\u5c07\u81ea\u52d5\u7522\u751f)", + "client_key": "\u4e0a\u50b3\u79c1\u9470\u6a94\u6848", + "keepalive": "\u50b3\u9001\u4fdd\u6301\u6d3b\u52d5\u8a0a\u606f\u9593\u9694\u6642\u9593", "password": "\u5bc6\u78bc", "port": "\u901a\u8a0a\u57e0", + "protocol": "MQTT \u901a\u8a0a\u5354\u5b9a", + "set_ca_cert": "\u4ee3\u7406\u4f3a\u670d\u5668\u6191\u8b49\u9a57\u8b49", + "set_client_cert": "\u4f7f\u7528\u5ba2\u6236\u7aef\u6191\u8b49", + "tls_insecure": "\u5ffd\u7565\u4ee3\u7406\u4f3a\u670d\u5668\u6191\u8b49\u9a57\u8b49", "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, "description": "\u8acb\u8f38\u5165 MQTT Broker \u9023\u7dda\u8cc7\u8a0a\u3002", @@ -82,13 +118,14 @@ "birth_retain": "Birth \u8a0a\u606f Retain", "birth_topic": "Birth \u8a0a\u606f\u4e3b\u984c", "discovery": "\u958b\u555f\u63a2\u7d22", + "discovery_prefix": "\u63a2\u7d22 prefix", "will_enable": "\u958b\u555f Will \u8a0a\u606f", "will_payload": "Will \u8a0a\u606f payload", "will_qos": "Will \u8a0a\u606f QoS", "will_retain": "Will \u8a0a\u606f Retain", "will_topic": "Will \u8a0a\u606f\u4e3b\u984c" }, - "description": "Discovery - \u5047\u5982\u641c\u7d22\uff08Discovery\uff09\u529f\u80fd\u958b\u555f\uff08\u5efa\u8b70\uff09\uff0cHome Assistant \u5c07\u6703\u81ea\u52d5\u767c\u73fe\u88dd\u7f6e\u8207\u5be6\u9ad4\u3001\u4e26\u767c\u5e03\u5176\u8a2d\u5b9a\u81f3 MQTT Broker\u3002\u5047\u5982\u641c\u7d22\u95dc\u9589\u7684\u8a71\uff0c\u6240\u6709\u8a2d\u5b9a\u5fc5\u9808\u624b\u52d5\u9032\u884c\u3002\nBirth \u8a0a\u606f - Birth \u8a0a\u606f\u5c07\u6703\u65bc\u6bcf\u6b21 Home Assistant \u9023\u7dda\u81f3 MQTT Broker \u6642\u50b3\u9001\u3002\nWill \u8a0a\u606f - Will \u8a0a\u606f\u5c07\u6703\u65bc\u6bcf\u6b21 Home Assistant \u81ea Broker \u65b7\u7dda\u6642\u50b3\u9001\u3001\u540c\u6642\u5305\u542b\u5b89\u5168\u65b7\u7dda\uff08\u4f8b\u5982 Home Assistant \u95dc\u6a5f\uff09\u53ca\u975e\u5b89\u5168\u65b7\u7dda\uff08\u4f8b\u5982 Home Assistant \u7576\u6a5f\u6216\u65b7\u7dda\uff09\u72c0\u6cc1\u3002", + "description": "Discovery - \u5047\u5982\u641c\u7d22\uff08Discovery\uff09\u529f\u80fd\u958b\u555f\uff08\u5efa\u8b70\uff09\uff0cHome Assistant \u5c07\u6703\u81ea\u52d5\u767c\u73fe\u88dd\u7f6e\u8207\u5be6\u9ad4\u3001\u4e26\u767c\u5e03\u5176\u8a2d\u5b9a\u81f3 MQTT \u4ee3\u7406\u4f3a\u670d\u5668\u3002\u5047\u5982\u641c\u7d22\u95dc\u9589\u7684\u8a71\uff0c\u6240\u6709\u8a2d\u5b9a\u5fc5\u9808\u624b\u52d5\u9032\u884c\u3002\n\u63a2\u7d22 prefix - \u5fc5\u9808\u4f7f\u7528 prefix \u8a2d\u5b9a\u4e3b\u984c\u958b\u982d\u624d\u80fd\u88ab\u81ea\u52d5\u63a2\u7d22\u767c\u73fe\u3002\nBirth \u8a0a\u606f - Birth \u8a0a\u606f\u5c07\u6703\u65bc\u6bcf\u6b21 Home Assistant \u9023\u7dda\u81f3 MQTT \u4ee3\u7406\u4f3a\u670d\u5668\u6642\u50b3\u9001\u3002\nWill \u8a0a\u606f - Will \u8a0a\u606f\u5c07\u6703\u65bc\u6bcf\u6b21 Home Assistant \u81ea\u4ee3\u7406\u4f3a\u670d\u5668\u65b7\u7dda\u6642\u50b3\u9001\u3001\u540c\u6642\u5305\u542b\u5b89\u5168\u65b7\u7dda\uff08\u4f8b\u5982 Home Assistant \u95dc\u6a5f\uff09\u53ca\u975e\u5b89\u5168\u65b7\u7dda\uff08\u4f8b\u5982 Home Assistant \u7576\u6a5f\u6216\u65b7\u7dda\uff09\u72c0\u6cc1\u3002", "title": "MQTT \u9078\u9805" } } diff --git a/homeassistant/components/nibe_heatpump/translations/ru.json b/homeassistant/components/nibe_heatpump/translations/ru.json index 72c3e327dd5..f280ae2ba57 100644 --- a/homeassistant/components/nibe_heatpump/translations/ru.json +++ b/homeassistant/components/nibe_heatpump/translations/ru.json @@ -4,7 +4,7 @@ "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." }, "error": { - "address": "\u0423\u043a\u0430\u0437\u0430\u043d \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441. \u0421\u043b\u0435\u0434\u0443\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 IPV4.", + "address": "\u0423\u043a\u0430\u0437\u0430\u043d \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441. \u0421\u043b\u0435\u0434\u0443\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f.", "address_in_use": "\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u044d\u0442\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435.", "model": "\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 modbus40.", "read": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b `\u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0447\u0442\u0435\u043d\u0438\u044f` \u0438\u043b\u0438 `\u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441`.", @@ -14,7 +14,7 @@ "step": { "user": { "data": { - "ip_address": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441", + "ip_address": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441", "listening_port": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f", "remote_read_port": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0447\u0442\u0435\u043d\u0438\u044f", "remote_write_port": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0437\u0430\u043f\u0438\u0441\u0438" diff --git a/homeassistant/components/nibe_heatpump/translations/zh-Hant.json b/homeassistant/components/nibe_heatpump/translations/zh-Hant.json index a2bb8c8f023..c00ea79de1c 100644 --- a/homeassistant/components/nibe_heatpump/translations/zh-Hant.json +++ b/homeassistant/components/nibe_heatpump/translations/zh-Hant.json @@ -4,7 +4,7 @@ "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" }, "error": { - "address": "\u6307\u5b9a\u7684\u9060\u7aef IP \u4f4d\u5740\u7121\u6548\u3002\u4f4d\u5740\u5fc5\u9808\u70ba IPV4 \u4f4d\u5740\u3002", + "address": "\u6307\u5b9a\u7684\u9060\u7aef\u4f4d\u5740\u7121\u6548\u3002\u4f4d\u5740\u5fc5\u9808\u70ba IP \u4f4d\u5740\u6216\u53ef\u89e3\u6790\u7684\u4e3b\u6a5f\u540d\u7a31\u3002", "address_in_use": "\u6240\u9078\u64c7\u7684\u76e3\u807d\u901a\u8a0a\u57e0\u5df2\u7d93\u88ab\u7cfb\u7d71\u6240\u4f7f\u7528\u3002", "model": "\u6240\u9078\u64c7\u7684\u578b\u865f\u4f3c\u4e4e\u4e0d\u652f\u63f4 modbus40", "read": "\u8b80\u53d6\u8acb\u6c42\u767c\u751f\u932f\u8aa4\uff0c\u8acb\u78ba\u8a8d `\u9060\u7aef\u8b80\u53d6\u57e0` \u6216 `\u9060\u7aef IP \u4f4d\u5740`\u3002", @@ -14,11 +14,18 @@ "step": { "user": { "data": { - "ip_address": "\u9060\u7aef IP \u4f4d\u5740", + "ip_address": "\u9060\u7aef\u4f4d\u5740", "listening_port": "\u672c\u5730\u76e3\u807d\u901a\u8a0a\u57e0", "remote_read_port": "\u9060\u7aef\u8b80\u53d6\u57e0", "remote_write_port": "\u9060\u7aef\u5beb\u5165\u57e0" - } + }, + "data_description": { + "ip_address": "NibeGW \u8a2d\u5099\u4f4d\u5740\u3002\u88dd\u7f6e\u61c9\u8a72\u5df2\u7d93\u8a2d\u5b9a\u70ba\u975c\u614b\u4f4d\u5740\uff0c", + "listening_port": "\u7cfb\u7d71\u672c\u5730\u901a\u8a0a\u57e0\u3001\u4f9b NibeGW \u8a2d\u5099\u8a2d\u5b9a\u50b3\u9001\u8cc7\u6599\u3002", + "remote_read_port": "NibeGW \u8a2d\u5099\u76e3\u807d\u8b80\u53d6\u8acb\u6c42\u901a\u8a0a\u57e0\u3002", + "remote_write_port": "NibeGW \u8a2d\u5099\u76e3\u807d\u5beb\u5165\u8acb\u6c42\u901a\u8a0a\u57e0\u3002" + }, + "description": "\u65bc\u5617\u8a66\u8a2d\u5b9a\u6574\u5408\u524d\u3001\u8acb\u78ba\u8a8d\uff1a\n - NibeGW \u8a2d\u5099\u5df2\u7d93\u9023\u7dda\u81f3\u71b1\u6cf5\u3002\n - \u5df2\u7d93\u65bc\u71b1\u6cf5\u8a2d\u5b9a\u4e2d\u555f\u7528 MODBUS40 \u914d\u4ef6\u3002\n - \u6cf5\u4e26\u6c92\u6709\u51fa\u73fe\u7f3a\u5c11 MODBUS40 \u914d\u4ef6\u4e4b\u8b66\u544a\u3002" } } } diff --git a/homeassistant/components/oralb/translations/he.json b/homeassistant/components/oralb/translations/he.json new file mode 100644 index 00000000000..b182a698234 --- /dev/null +++ b/homeassistant/components/oralb/translations/he.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "not_supported": "\u05d4\u05ea\u05e7\u05df \u05d0\u05d9\u05e0\u05d5 \u05e0\u05ea\u05de\u05da" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea {name}?" + }, + "user": { + "data": { + "address": "\u05d4\u05ea\u05e7\u05df" + }, + "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d4\u05ea\u05e7\u05df \u05dc\u05d4\u05d2\u05d3\u05e8\u05d4" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ovo_energy/translations/de.json b/homeassistant/components/ovo_energy/translations/de.json index 74d3007430f..9ddfa873cd9 100644 --- a/homeassistant/components/ovo_energy/translations/de.json +++ b/homeassistant/components/ovo_energy/translations/de.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "OVO-Konto-ID (nur hinzuf\u00fcgen, wenn mehrere Konten vorhanden sind)", "password": "Passwort", "username": "Benutzername" }, diff --git a/homeassistant/components/ovo_energy/translations/en.json b/homeassistant/components/ovo_energy/translations/en.json index 666c6fe6772..e3d88a6a85f 100644 --- a/homeassistant/components/ovo_energy/translations/en.json +++ b/homeassistant/components/ovo_energy/translations/en.json @@ -16,9 +16,9 @@ }, "user": { "data": { + "account": "OVO account id (only add if you have multiple accounts)", "password": "Password", - "username": "Username", - "account": "OVO account id (only add if you have multiple accounts)" + "username": "Username" }, "description": "Set up an OVO Energy instance to access your energy usage.", "title": "Add OVO Energy Account" diff --git a/homeassistant/components/ovo_energy/translations/es.json b/homeassistant/components/ovo_energy/translations/es.json index d3ffba9b2e5..1a975921b25 100644 --- a/homeassistant/components/ovo_energy/translations/es.json +++ b/homeassistant/components/ovo_energy/translations/es.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "ID de la cuenta OVO (s\u00f3lo a\u00f1adir si tienes varias cuentas)", "password": "Contrase\u00f1a", "username": "Nombre de usuario" }, diff --git a/homeassistant/components/ovo_energy/translations/hu.json b/homeassistant/components/ovo_energy/translations/hu.json index a58669fe11c..5be71f15ac7 100644 --- a/homeassistant/components/ovo_energy/translations/hu.json +++ b/homeassistant/components/ovo_energy/translations/hu.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "OVO-fi\u00f3k azonos\u00edt\u00f3 (csak akkor adja meg, ha t\u00f6bb fi\u00f3kkal rendelkezik)", "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, diff --git a/homeassistant/components/ovo_energy/translations/ru.json b/homeassistant/components/ovo_energy/translations/ru.json index f44b0e27110..b1457aeba68 100644 --- a/homeassistant/components/ovo_energy/translations/ru.json +++ b/homeassistant/components/ovo_energy/translations/ru.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "ID \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430 OVO (\u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0439\u0442\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0443 \u0412\u0430\u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u043e\u0432)", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, diff --git a/homeassistant/components/ovo_energy/translations/zh-Hant.json b/homeassistant/components/ovo_energy/translations/zh-Hant.json index 0b1c218d94c..3bc858fd24e 100644 --- a/homeassistant/components/ovo_energy/translations/zh-Hant.json +++ b/homeassistant/components/ovo_energy/translations/zh-Hant.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "OVO \u5e33\u865f ID\uff08\u7576\u6709\u591a\u500b\u5e33\u865f\u6642\u624d\u9700\u586b\u5beb\uff09", "password": "\u5bc6\u78bc", "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, diff --git a/homeassistant/components/pushover/translations/zh-Hant.json b/homeassistant/components/pushover/translations/zh-Hant.json index 82f4f8f2d94..6294000ccbb 100644 --- a/homeassistant/components/pushover/translations/zh-Hant.json +++ b/homeassistant/components/pushover/translations/zh-Hant.json @@ -31,7 +31,8 @@ "title": "Pushover YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" }, "removed_yaml": { - "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a Pushover \u7684\u529f\u80fd\u5373\u5c07\u79fb\u9664\u3002\n\nHome Assistant \u5c07\u4e0d\u518d\u4f7f\u7528\u73fe\u6709\u7684 YAML \u8a2d\u5b9a\u3002\n\n\u7531 configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Pushover YAML \u8a2d\u5b9a\u4e26\u91cd\u555f Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002" + "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a Pushover \u7684\u529f\u80fd\u5373\u5c07\u79fb\u9664\u3002\n\nHome Assistant \u5c07\u4e0d\u518d\u4f7f\u7528\u73fe\u6709\u7684 YAML \u8a2d\u5b9a\u3002\n\n\u7531 configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Pushover YAML \u8a2d\u5b9a\u4e26\u91cd\u555f Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", + "title": "Pushover YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" } } } \ No newline at end of file diff --git a/homeassistant/components/rainmachine/translations/ru.json b/homeassistant/components/rainmachine/translations/ru.json index 2ed8c6df530..03fbe1fb074 100644 --- a/homeassistant/components/rainmachine/translations/ru.json +++ b/homeassistant/components/rainmachine/translations/ru.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0437\u043e\u043d \u0438\u0437 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f RainMachine", "zone_run_time": "\u0412\u0440\u0435\u043c\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0437\u043e\u043d\u044b \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)" }, "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 RainMachine" diff --git a/homeassistant/components/rainmachine/translations/zh-Hant.json b/homeassistant/components/rainmachine/translations/zh-Hant.json index c0ead98a13e..a912bdb40c9 100644 --- a/homeassistant/components/rainmachine/translations/zh-Hant.json +++ b/homeassistant/components/rainmachine/translations/zh-Hant.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "Use zone run times \u4f7f\u7528 RainMachine App \u4e2d\u7684\u5340\u57df\u57f7\u884c\u6b21\u6578", "zone_run_time": "\u9810\u8a2d\u5340\u57df\u57f7\u884c\u6642\u9593\uff08\u79d2\uff09" }, "title": "\u8a2d\u5b9a RainMachine" diff --git a/homeassistant/components/sensibo/translations/ru.json b/homeassistant/components/sensibo/translations/ru.json index a374be60719..b478b6095f8 100644 --- a/homeassistant/components/sensibo/translations/ru.json +++ b/homeassistant/components/sensibo/translations/ru.json @@ -17,7 +17,7 @@ "api_key": "\u041a\u043b\u044e\u0447 API" }, "data_description": { - "api_key": "\u0421\u043b\u0435\u0434\u0443\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043a\u043b\u044e\u0447 API." + "api_key": "\u0421\u043b\u0435\u0434\u0443\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u043b\u044e\u0447 API" } }, "user": { @@ -25,7 +25,7 @@ "api_key": "\u041a\u043b\u044e\u0447 API" }, "data_description": { - "api_key": "\u0421\u043b\u0435\u0434\u0443\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u043b\u044e\u0447 API." + "api_key": "\u0421\u043b\u0435\u0434\u0443\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u043b\u044e\u0447 API" } } } diff --git a/homeassistant/components/sensibo/translations/sensor.he.json b/homeassistant/components/sensibo/translations/sensor.he.json new file mode 100644 index 00000000000..3a45f273e0d --- /dev/null +++ b/homeassistant/components/sensibo/translations/sensor.he.json @@ -0,0 +1,7 @@ +{ + "state": { + "sensibo__smart_type": { + "temperature": "\u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05d4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/zh-Hant.json b/homeassistant/components/sensibo/translations/zh-Hant.json index 4ad41a2e3f3..b40fad85fad 100644 --- a/homeassistant/components/sensibo/translations/zh-Hant.json +++ b/homeassistant/components/sensibo/translations/zh-Hant.json @@ -17,7 +17,7 @@ "api_key": "API \u91d1\u9470" }, "data_description": { - "api_key": "\u8acb\u8ddf\u96a8\u6587\u4ef6\u4ee5\u53d6\u5f97\u65b0 API \u91d1\u9470\u3002" + "api_key": "\u8acb\u8ddf\u96a8\u6587\u4ef6\u4ee5\u53d6\u5f97 API \u91d1\u9470" } }, "user": { @@ -25,7 +25,7 @@ "api_key": "API \u91d1\u9470" }, "data_description": { - "api_key": "\u8acb\u8ddf\u96a8\u6587\u4ef6\u4ee5\u53d6\u5f97 API \u91d1\u9470\u3002" + "api_key": "\u8acb\u8ddf\u96a8\u6587\u4ef6\u4ee5\u53d6\u5f97 API \u91d1\u9470" } } } diff --git a/homeassistant/components/sensor/translations/he.json b/homeassistant/components/sensor/translations/he.json index 6adc9a6da87..a3dda234d0c 100644 --- a/homeassistant/components/sensor/translations/he.json +++ b/homeassistant/components/sensor/translations/he.json @@ -28,6 +28,7 @@ "is_value": "\u05e2\u05e8\u05da \u05e0\u05d5\u05db\u05d7\u05d9 {entity_name}", "is_volatile_organic_compounds": "\u05e8\u05de\u05ea \u05e8\u05d9\u05db\u05d5\u05d6 \u05d4\u05ea\u05e8\u05db\u05d5\u05d1\u05d5\u05ea \u05d4\u05d0\u05d5\u05e8\u05d2\u05e0\u05d9\u05d5\u05ea \u05d4\u05e0\u05d3\u05d9\u05e4\u05d5\u05ea \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05d5\u05ea {entity_name}", "is_volume": "\u05e0\u05e4\u05d7 \u05e0\u05d5\u05db\u05d7\u05d9 \u05e9\u05dc {entity_name}", + "is_water": "\u05de\u05d9\u05dd \u05e0\u05d5\u05db\u05d7\u05d9 {entity_name}", "is_weight": "\u05de\u05e9\u05e7\u05dc \u05e0\u05d5\u05db\u05d7\u05d9 {entity_name}" }, "trigger_type": { @@ -40,6 +41,7 @@ "gas": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05d2\u05d6", "humidity": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05dc\u05d7\u05d5\u05ea", "illuminance": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05e2\u05d5\u05e6\u05de\u05ea \u05d4\u05d0\u05e8\u05d4", + "moisture": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05dc\u05d7\u05d5\u05ea", "nitrogen_dioxide": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e8\u05d9\u05db\u05d5\u05d6 \u05d4\u05d7\u05e0\u05e7\u05df \u05d4\u05d3\u05d5-\u05d7\u05de\u05e6\u05e0\u05d9", "nitrogen_monoxide": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e8\u05d9\u05db\u05d5\u05d6 \u05d7\u05d3 \u05ea\u05d7\u05de\u05d5\u05e6\u05ea \u05d4\u05d7\u05e0\u05e7\u05df", "nitrous_oxide": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e8\u05d9\u05db\u05d5\u05d6 \u05ea\u05d7\u05de\u05d5\u05e6\u05ea \u05d4\u05d7\u05e0\u05e7\u05df", @@ -58,6 +60,7 @@ "value": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05e2\u05e8\u05da", "voltage": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05de\u05ea\u05d7", "volume": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05e0\u05e4\u05d7", + "water": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05de\u05d9\u05dd", "weight": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05de\u05e9\u05e7\u05dc" } }, diff --git a/homeassistant/components/statistics/translations/he.json b/homeassistant/components/statistics/translations/he.json new file mode 100644 index 00000000000..fca507bcc19 --- /dev/null +++ b/homeassistant/components/statistics/translations/he.json @@ -0,0 +1,11 @@ +{ + "issues": { + "deprecation_warning_characteristic": { + "description": "\u05e4\u05e8\u05de\u05d8\u05e8 \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 `state_characteristic` \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05d9\u05d4\u05e4\u05d5\u05da \u05dc\u05d7\u05d5\u05d1\u05d4.\n\n\u05d9\u05e9 \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 `state_characteristic: {characteristic}` \u05dc\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05d7\u05d9\u05d9\u05e9\u05df `{entity}` \u05db\u05d3\u05d9 \u05dc\u05e9\u05de\u05d5\u05e8 \u05e2\u05dc \u05d0\u05d5\u05e4\u05df \u05d4\u05e4\u05e2\u05d5\u05dc\u05d4 \u05d4\u05e0\u05d5\u05db\u05d7\u05d9.\n\n\u05d9\u05e9 \u05dc\u05e7\u05e8\u05d5\u05d0 \u05d0\u05ea \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05dc\u05e4\u05e8\u05d8\u05d9\u05dd \u05e0\u05d5\u05e1\u05e4\u05d9\u05dd: https://www.home-assistant.io/integrations/statistics/", + "title": "\u05d7\u05d5\u05d1\u05d4 'state_characteristic' \u05e9\u05d4\u05d5\u05e0\u05d7\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d9\u05e9\u05d5\u05ea \u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4" + }, + "deprecation_warning_size": { + "description": "\u05e4\u05e8\u05de\u05d8\u05e8 \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 `sampling_size` \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05d1\u05e8\u05d9\u05e8\u05ea \u05d4\u05de\u05d7\u05d3\u05dc \u05dc\u05e2\u05e8\u05da 20 \u05e2\u05d3 \u05db\u05d4, \u05d0\u05e9\u05e8 \u05d9\u05e9\u05ea\u05e0\u05d4.\n\n\u05d0\u05e0\u05d0 \u05d1\u05d3\u05d5\u05e7 \u05d0\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d7\u05d9\u05d9\u05e9\u05df `{entity}` \u05d5\u05d4\u05d5\u05e1\u05e3 \u05d2\u05d1\u05d5\u05dc\u05d5\u05ea \u05de\u05ea\u05d0\u05d9\u05de\u05d9\u05dd, \u05dc\u05d3\u05d5\u05d2\u05de\u05d4, `sampling_size: 20` \u05db\u05d3\u05d9 \u05dc\u05e9\u05de\u05d5\u05e8 \u05e2\u05dc \u05d4\u05d4\u05ea\u05e0\u05d4\u05d2\u05d5\u05ea \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05ea. \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05ea\u05d4\u05e4\u05d5\u05da \u05dc\u05d2\u05de\u05d9\u05e9\u05d4 \u05d9\u05d5\u05ea\u05e8 \u05e2\u05dd \u05d2\u05e8\u05e1\u05d4 2022.12.0 \u05d5\u05ea\u05e7\u05d1\u05dc `sampling_size` \u05d0\u05d5 `max_age`, \u05d0\u05d5 \u05d0\u05ea \u05e9\u05ea\u05d9 \u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea. \u05d4\u05d1\u05e7\u05e9\u05d4 \u05e9\u05dc\u05e2\u05d9\u05dc \u05de\u05db\u05d9\u05e0\u05d4 \u05d0\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc\u05da \u05dc\u05e9\u05d9\u05e0\u05d5\u05d9 \u05d6\u05d4 \u05e9\u05d0\u05d7\u05e8\u05ea \u05e0\u05e9\u05d1\u05e8.\n\n\u05dc\u05e7\u05e8\u05d5\u05d0 \u05d0\u05ea \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05dc\u05e4\u05e8\u05d8\u05d9\u05dd \u05e0\u05d5\u05e1\u05e4\u05d9\u05dd: https://www.home-assistant.io/integrations/statistics/" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/statistics/translations/ru.json b/homeassistant/components/statistics/translations/ru.json index 10cc86dbda5..0ac16d2b3e3 100644 --- a/homeassistant/components/statistics/translations/ru.json +++ b/homeassistant/components/statistics/translations/ru.json @@ -3,6 +3,10 @@ "deprecation_warning_characteristic": { "description": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438 `state_characteristic` \u0441\u0442\u0430\u043d\u0435\u0442 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c. \n\n\u0427\u0442\u043e\u0431\u044b \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u043a\u0430\u043a \u0438 \u043f\u0440\u0435\u0436\u0434\u0435, \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 `state_characteristic: {characteristic}` \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0441\u0435\u043d\u0441\u043e\u0440\u0430 `{entity}`. \n\n\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439: https://www.home-assistant.io/integrations/statistics/", "title": "\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 'state_characteristic', \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u043c\u044b\u0439 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438" + }, + "deprecation_warning_size": { + "description": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438 `sampling_size` \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438\u043c\u0435\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 20, \u043d\u043e \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043e.\n\n\u0427\u0442\u043e\u0431\u044b \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u043a\u0430\u043a \u0438 \u043f\u0440\u0435\u0436\u0434\u0435, \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0434\u0430\u0442\u0447\u0438\u043a\u0430 `{entity}` \u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u043b\u0438\u043c\u0438\u0442\u044b (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, `sampling_size: 20`). \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438 \u0441\u0442\u0430\u043d\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 \u0433\u0438\u0431\u043a\u043e\u0439 \u0441 \u0432\u0435\u0440\u0441\u0438\u0438 2022.12.0 \u0438 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043b\u0438\u0431\u043e `sampling_size`, \u043b\u0438\u0431\u043e `max_age`, \u043b\u0438\u0431\u043e \u043e\u0431\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430. \u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u044c\u0442\u0435 \u0412\u0430\u0448\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043a \u044d\u0442\u043e\u043c\u0443 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044e, \u0432 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043f\u043e\u043b\u043e\u043c\u043a\u0435.\n\n\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439: https://www.home-assistant.io/integrations/statistics/.", + "title": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 'sampling_size', \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u043c\u044b\u0439 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438" } } } \ No newline at end of file diff --git a/homeassistant/components/statistics/translations/zh-Hant.json b/homeassistant/components/statistics/translations/zh-Hant.json new file mode 100644 index 00000000000..a396f200323 --- /dev/null +++ b/homeassistant/components/statistics/translations/zh-Hant.json @@ -0,0 +1,12 @@ +{ + "issues": { + "deprecation_warning_characteristic": { + "description": "\u7d71\u8a08\u8cc7\u6599\u6574\u5408\u8a2d\u5b9a\u53c3\u6578 `state_characteristic`\u5c07\u8b8a\u70ba\u5f37\u5236\u8a2d\u5b9a\u3002\n\n\u8acb\u8a2d\u5b9a\u611f\u6e2c\u5668 `{entity}` \u65b0\u589e `state_characteristic: {characteristic}` \u4ee5\u4fdd\u6301\u76ee\u524d\u7684\u884c\u70ba\u3002\n\n\u8acb\u53c3\u95b1\u7d71\u8a08\u8cc7\u6599\u6574\u5408\u6587\u4ef6\u4ee5\u53d6\u5f97\u8a73\u7d30\u8cc7\u8a0a\uff1ahttps://www.home-assistant.io/integrations/statistics/", + "title": "\u7d71\u8a08\u8cc7\u6599\u5be6\u9ad4\u5f37\u5236\u96b1\u542b 'sampling_size'" + }, + "deprecation_warning_size": { + "description": "\u7d71\u8a08\u8cc7\u6599\u6574\u5408\u8a2d\u5b9a\u53c3\u6578 `sampling_size` \u9810\u8a2d\u70ba 20\u3002\u6578\u503c\u5c07\u6703\u8b8a\u66f4\u3002\n\n\u8acb\u6aa2\u67e5\u611f\u6e2c\u5668 `{entity}` \u8a2d\u5b9a\u4e26\u65b0\u589e\u9069\u7576\u7684\u7bc4\u570d\u3002\u4f8b\u5982 `sampling_size: 20` \u4ee5\u4fdd\u6301\u76ee\u524d\u7684\u884c\u70ba\u3002\u7d71\u8a08\u8cc7\u6599\u6574\u5408\u8a2d\u5b9a\u5c07\u6703\u65bc 2022.12.0 \u7248\u672c\u958b\u59cb\u8b8a\u5f97\u66f4\u70ba\u9748\u6d3b\u3001\u4e26\u63a5\u53d7 `sampling_size` \u6216 `max_age`\u3001\u6216\u8005\u5169\u500b\u8a2d\u5b9a\u3002\u4e0a\u8a34\u7684\u8981\u6c42\u8a2d\u5b9a\u5c07\u53ef\u63d0\u524d\u70ba\u65b0\u7248\u672c\u8b8a\u66f4\u505a\u597d\u6e96\u5099\u3002\n\n\u8acb\u53c3\u95b1\u7d71\u8a08\u8cc7\u6599\u6574\u5408\u6587\u4ef6\u4ee5\u53d6\u5f97\u8a73\u7d30\u8cc7\u8a0a\uff1ahttps://www.home-assistant.io/integrations/statistics/", + "title": "\u7d71\u8a08\u8cc7\u6599\u5be6\u9ad4\u5047\u5b9a\u96b1\u542b 'sampling_size'" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tasmota/translations/he.json b/homeassistant/components/tasmota/translations/he.json index 2bc04cca267..bfded0c6ac1 100644 --- a/homeassistant/components/tasmota/translations/he.json +++ b/homeassistant/components/tasmota/translations/he.json @@ -16,5 +16,15 @@ "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea Tasmota?" } } + }, + "issues": { + "topic_duplicated": { + "description": "\u05de\u05e1\u05e4\u05e8 \u05d4\u05ea\u05e7\u05e0\u05d9 Tasmota \u05de\u05e9\u05ea\u05e4\u05d9\u05dd \u05d0\u05ea \u05d4\u05e0\u05d5\u05e9\u05d0 {topic}.\n\n\u05d4\u05ea\u05e7\u05e0\u05d9 Tasmota \u05e2\u05dd \u05d1\u05e2\u05d9\u05d4 \u05d6\u05d5: {offenders}.", + "title": "\u05de\u05e1\u05e4\u05e8 \u05d4\u05ea\u05e7\u05e0\u05d9 Tasmota \u05d7\u05d5\u05dc\u05e7\u05d9\u05dd \u05d0\u05ea \u05d0\u05d5\u05ea\u05d5 \u05e0\u05d5\u05e9\u05d0" + }, + "topic_no_prefix": { + "description": "\u05d4\u05ea\u05e7\u05df Tasmota {name} \u05e2\u05dd IP {ip} \u05d0\u05d9\u05e0\u05d5 \u05db\u05d5\u05dc\u05dc `%prefix%` \u05d1\u05de\u05dc\u05d5\u05d0\u05d4.\n\n\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea \u05e2\u05d1\u05d5\u05e8 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d0\u05dc\u05d4 \u05de\u05d5\u05e9\u05d1\u05ea\u05d5\u05ea \u05e2\u05d3 \u05dc\u05ea\u05d9\u05e7\u05d5\u05df \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4.", + "title": "\u05dc\u05d4\u05ea\u05e7\u05df Tasmota {name} \u05d9\u05e9 \u05e0\u05d5\u05e9\u05d0 MQTT \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" + } } } \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/fr.json b/homeassistant/components/zamg/translations/fr.json new file mode 100644 index 00000000000..4e3c83100cd --- /dev/null +++ b/homeassistant/components/zamg/translations/fr.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", + "cannot_connect": "\u00c9chec de connexion" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion" + }, + "flow_title": "{name}" + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/he.json b/homeassistant/components/zamg/translations/he.json new file mode 100644 index 00000000000..df773347ba2 --- /dev/null +++ b/homeassistant/components/zamg/translations/he.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" + }, + "flow_title": "{name}" + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/ru.json b/homeassistant/components/zamg/translations/ru.json new file mode 100644 index 00000000000..59439e4789b --- /dev/null +++ b/homeassistant/components/zamg/translations/ru.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "ID \u0441\u0442\u0430\u043d\u0446\u0438\u0438 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0430\u044f \u0441\u0442\u0430\u043d\u0446\u0438\u044f)" + }, + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 ZAMG." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 ZAMG \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", + "title": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 ZAMG \u0447\u0435\u0440\u0435\u0437 YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/zh-Hant.json b/homeassistant/components/zamg/translations/zh-Hant.json new file mode 100644 index 00000000000..3e8bccdb4f5 --- /dev/null +++ b/homeassistant/components/zamg/translations/zh-Hant.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "cannot_connect": "\u9023\u7dda\u5931\u6557" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "\u76e3\u6e2c\u7ad9 ID\uff08\u9810\u8a2d\u70ba\u6700\u8fd1\u7684\u76e3\u6e2c\u7ad9\uff09" + }, + "description": "\u8a2d\u5b9a ZAMG \u4ee5\u6574\u5408\u81f3 Home Assistant\u3002" + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a\u7684 ZAMG \u5373\u5c07\u9032\u884c\u79fb\u9664\u3002\n\n\u65e2\u6709\u7684 YAML \u8a2d\u5b9a\u5c07\u81ea\u52d5\u532f\u5165\u81f3 UI \u5167\u3002\n\n\u8acb\u65bc configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 ZAMG YAML \u8a2d\u5b9a\u4e26\u91cd\u65b0\u555f\u52d5 Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", + "title": "ZAMG YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" + } + } +} \ No newline at end of file From 52f592a7d97e0f02ace8f9a1c32fb73527d61dca Mon Sep 17 00:00:00 2001 From: mezz64 <2854333+mezz64@users.noreply.github.com> Date: Sun, 30 Oct 2022 01:13:54 -0400 Subject: [PATCH 0080/1033] Bump pyHik to 0.3.1 (#81173) Co-authored-by: J. Nick Koston --- homeassistant/components/hikvision/binary_sensor.py | 1 - homeassistant/components/hikvision/manifest.json | 2 +- requirements_all.txt | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/hikvision/binary_sensor.py b/homeassistant/components/hikvision/binary_sensor.py index e7f3c49fb08..ef8a18ec3ae 100644 --- a/homeassistant/components/hikvision/binary_sensor.py +++ b/homeassistant/components/hikvision/binary_sensor.py @@ -46,7 +46,6 @@ DEVICE_CLASS_MAP = { "Motion": BinarySensorDeviceClass.MOTION, "Line Crossing": BinarySensorDeviceClass.MOTION, "Field Detection": BinarySensorDeviceClass.MOTION, - "Video Loss": None, "Tamper Detection": BinarySensorDeviceClass.MOTION, "Shelter Alarm": None, "Disk Full": None, diff --git a/homeassistant/components/hikvision/manifest.json b/homeassistant/components/hikvision/manifest.json index 7209ee00024..4b27e37b0f6 100644 --- a/homeassistant/components/hikvision/manifest.json +++ b/homeassistant/components/hikvision/manifest.json @@ -2,7 +2,7 @@ "domain": "hikvision", "name": "Hikvision", "documentation": "https://www.home-assistant.io/integrations/hikvision", - "requirements": ["pyhik==0.3.0"], + "requirements": ["pyhik==0.3.1"], "codeowners": ["@mezz64"], "iot_class": "local_push", "loggers": ["pyhik"] diff --git a/requirements_all.txt b/requirements_all.txt index e80a736383d..9626faf8d46 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1604,7 +1604,7 @@ pyhaversion==22.8.0 pyheos==0.7.2 # homeassistant.components.hikvision -pyhik==0.3.0 +pyhik==0.3.1 # homeassistant.components.hive pyhiveapi==0.5.14 From dd8b4e2a9b6b2309f0ebedafe758a7a1100c10e3 Mon Sep 17 00:00:00 2001 From: Raj Laud <50647620+rajlaud@users.noreply.github.com> Date: Sun, 30 Oct 2022 01:23:46 -0400 Subject: [PATCH 0081/1033] Fix Squeezebox media browsing (#81197) * Squeezebox media browser fix icons * Update pysqueezebox to 0.6.1 --- homeassistant/components/squeezebox/browse_media.py | 1 - homeassistant/components/squeezebox/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/squeezebox/browse_media.py b/homeassistant/components/squeezebox/browse_media.py index 979b4c36a98..c66bc8af9a5 100644 --- a/homeassistant/components/squeezebox/browse_media.py +++ b/homeassistant/components/squeezebox/browse_media.py @@ -156,7 +156,6 @@ async def library_payload(hass, player): media_content_type=item, can_play=True, can_expand=True, - thumbnail="https://brands.home-assistant.io/_/squeezebox/logo.png", ) ) diff --git a/homeassistant/components/squeezebox/manifest.json b/homeassistant/components/squeezebox/manifest.json index 018333d420b..2c1692b6085 100644 --- a/homeassistant/components/squeezebox/manifest.json +++ b/homeassistant/components/squeezebox/manifest.json @@ -3,7 +3,7 @@ "name": "Squeezebox (Logitech Media Server)", "documentation": "https://www.home-assistant.io/integrations/squeezebox", "codeowners": ["@rajlaud"], - "requirements": ["pysqueezebox==0.6.0"], + "requirements": ["pysqueezebox==0.6.1"], "config_flow": true, "dhcp": [ { diff --git a/requirements_all.txt b/requirements_all.txt index 9626faf8d46..825e74aa1ee 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1920,7 +1920,7 @@ pysoma==0.0.10 pyspcwebgw==0.4.0 # homeassistant.components.squeezebox -pysqueezebox==0.6.0 +pysqueezebox==0.6.1 # homeassistant.components.stiebel_eltron pystiebeleltron==0.0.1.dev2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 98b568782b4..785c4448e5a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1355,7 +1355,7 @@ pysoma==0.0.10 pyspcwebgw==0.4.0 # homeassistant.components.squeezebox -pysqueezebox==0.6.0 +pysqueezebox==0.6.1 # homeassistant.components.switchbee pyswitchbee==1.5.5 From 2c190cf3238760bc7fe29c7b1b1c053097737c9e Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Sun, 30 Oct 2022 08:27:47 +0100 Subject: [PATCH 0082/1033] Add integration_type to renault (#81099) --- homeassistant/components/renault/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/renault/manifest.json b/homeassistant/components/renault/manifest.json index 5b2fc146e92..a9f65197502 100644 --- a/homeassistant/components/renault/manifest.json +++ b/homeassistant/components/renault/manifest.json @@ -1,6 +1,7 @@ { "domain": "renault", "name": "Renault", + "integration_type": "hub", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/renault", "requirements": ["renault-api==0.1.11"], From d9d6902803a838c20a9d4ead14a460776c0261ab Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 30 Oct 2022 05:32:57 -0500 Subject: [PATCH 0083/1033] Bump dbus-fast to 1.59.1 (#81229) * Bump dbus-fast to 1.59.1 fixes incorrect logging of an exception when it was already handled changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v1.59.0...v1.59.1 * empty --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index a5ea8c171d8..f8d1867035d 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.5.0", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", - "dbus-fast==1.59.0" + "dbus-fast==1.59.1" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 413a86be041..d48b85e346b 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.1 -dbus-fast==1.59.0 +dbus-fast==1.59.1 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 825e74aa1ee..493d4b00eae 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -540,7 +540,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.59.0 +dbus-fast==1.59.1 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 785c4448e5a..3edaf01774d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -420,7 +420,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.59.0 +dbus-fast==1.59.1 # homeassistant.components.debugpy debugpy==1.6.3 From 06773efcbddcf82623b1766013afaf510eaf3734 Mon Sep 17 00:00:00 2001 From: Rami Mosleh Date: Sun, 30 Oct 2022 13:00:47 +0200 Subject: [PATCH 0084/1033] Deprecate name key in `Transmission` services (#78577) * Use entry_id instead of name in Transmission services * fix return type for _get_client * address comments * combine conditions --- .../components/transmission/__init__.py | 131 +++++++++++------- .../components/transmission/const.py | 2 +- .../components/transmission/services.yaml | 27 +++- .../components/transmission/strings.json | 13 ++ .../transmission/translations/en.json | 13 ++ 5 files changed, 130 insertions(+), 56 deletions(-) diff --git a/homeassistant/components/transmission/__init__.py b/homeassistant/components/transmission/__init__.py index 48d4a0e5163..8f81382012d 100644 --- a/homeassistant/components/transmission/__init__.py +++ b/homeassistant/components/transmission/__init__.py @@ -3,12 +3,13 @@ from __future__ import annotations from datetime import timedelta import logging +from typing import Any import transmissionrpc from transmissionrpc.error import TransmissionError import voluptuous as vol -from homeassistant.config_entries import ConfigEntry +from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.const import ( CONF_HOST, CONF_ID, @@ -21,13 +22,15 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady -from homeassistant.helpers import config_validation as cv +from homeassistant.helpers import config_validation as cv, selector from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import async_track_time_interval +from homeassistant.helpers.issue_registry import IssueSeverity, create_issue from .const import ( ATTR_DELETE_DATA, ATTR_TORRENT, + CONF_ENTRY_ID, CONF_LIMIT, CONF_ORDER, DATA_UPDATED, @@ -49,30 +52,41 @@ from .errors import AuthenticationError, CannotConnect, UnknownError _LOGGER = logging.getLogger(__name__) -SERVICE_ADD_TORRENT_SCHEMA = vol.Schema( - {vol.Required(ATTR_TORRENT): cv.string, vol.Required(CONF_NAME): cv.string} -) - -SERVICE_REMOVE_TORRENT_SCHEMA = vol.Schema( +SERVICE_BASE_SCHEMA = vol.Schema( { - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_ID): cv.positive_int, - vol.Optional(ATTR_DELETE_DATA, default=DEFAULT_DELETE_DATA): cv.boolean, + vol.Exclusive(CONF_ENTRY_ID, "identifier"): selector.ConfigEntrySelector(), + vol.Exclusive(CONF_NAME, "identifier"): selector.TextSelector(), } ) -SERVICE_START_TORRENT_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_ID): cv.positive_int, - } +SERVICE_ADD_TORRENT_SCHEMA = vol.All( + SERVICE_BASE_SCHEMA.extend({vol.Required(ATTR_TORRENT): cv.string}), + cv.has_at_least_one_key(CONF_ENTRY_ID, CONF_NAME), ) -SERVICE_STOP_TORRENT_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_ID): cv.positive_int, - } + +SERVICE_REMOVE_TORRENT_SCHEMA = vol.All( + SERVICE_BASE_SCHEMA.extend( + { + vol.Required(CONF_ID): cv.positive_int, + vol.Optional(ATTR_DELETE_DATA, default=DEFAULT_DELETE_DATA): cv.boolean, + } + ), + cv.has_at_least_one_key(CONF_ENTRY_ID, CONF_NAME), +) + +SERVICE_START_TORRENT_SCHEMA = vol.All( + SERVICE_BASE_SCHEMA.extend({vol.Required(CONF_ID): cv.positive_int}), + cv.has_at_least_one_key(CONF_ENTRY_ID, CONF_NAME), +) + +SERVICE_STOP_TORRENT_SCHEMA = vol.All( + SERVICE_BASE_SCHEMA.extend( + { + vol.Required(CONF_ID): cv.positive_int, + } + ), + cv.has_at_least_one_key(CONF_ENTRY_ID, CONF_NAME), ) CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) @@ -135,6 +149,39 @@ async def get_api(hass, entry): raise UnknownError from error +def _get_client(hass: HomeAssistant, data: dict[str, Any]) -> TransmissionClient | None: + """Return client from integration name or entry_id.""" + if ( + (entry_id := data.get(CONF_ENTRY_ID)) + and (entry := hass.config_entries.async_get_entry(entry_id)) + and entry.state == ConfigEntryState.LOADED + ): + return hass.data[DOMAIN][entry_id] + + # to be removed once name key is removed + if CONF_NAME in data: + create_issue( + hass, + DOMAIN, + "deprecated_key", + breaks_in_ha_version="2023.1.0", + is_fixable=True, + is_persistent=True, + severity=IssueSeverity.WARNING, + translation_key="deprecated_key", + ) + + _LOGGER.warning( + 'The "name" key in the Transmission services is deprecated and will be removed in "2023.1.0"; ' + 'use the "entry_id" key instead to identity which entry to call' + ) + for entry in hass.config_entries.async_entries(DOMAIN): + if entry.data[CONF_NAME] == data[CONF_NAME]: + return hass.data[DOMAIN][entry.entry_id] + + return None + + class TransmissionClient: """Transmission Client Object.""" @@ -174,14 +221,9 @@ class TransmissionClient: def add_torrent(service: ServiceCall) -> None: """Add new torrent to download.""" - tm_client = None - for entry in self.hass.config_entries.async_entries(DOMAIN): - if entry.data[CONF_NAME] == service.data[CONF_NAME]: - tm_client = self.hass.data[DOMAIN][entry.entry_id] - break - if tm_client is None: - _LOGGER.error("Transmission instance is not found") - return + if not (tm_client := _get_client(self.hass, service.data)): + raise ValueError("Transmission instance is not found") + torrent = service.data[ATTR_TORRENT] if torrent.startswith( ("http", "ftp:", "magnet:") @@ -195,42 +237,27 @@ class TransmissionClient: def start_torrent(service: ServiceCall) -> None: """Start torrent.""" - tm_client = None - for entry in self.hass.config_entries.async_entries(DOMAIN): - if entry.data[CONF_NAME] == service.data[CONF_NAME]: - tm_client = self.hass.data[DOMAIN][entry.entry_id] - break - if tm_client is None: - _LOGGER.error("Transmission instance is not found") - return + if not (tm_client := _get_client(self.hass, service.data)): + raise ValueError("Transmission instance is not found") + torrent_id = service.data[CONF_ID] tm_client.tm_api.start_torrent(torrent_id) tm_client.api.update() def stop_torrent(service: ServiceCall) -> None: """Stop torrent.""" - tm_client = None - for entry in self.hass.config_entries.async_entries(DOMAIN): - if entry.data[CONF_NAME] == service.data[CONF_NAME]: - tm_client = self.hass.data[DOMAIN][entry.entry_id] - break - if tm_client is None: - _LOGGER.error("Transmission instance is not found") - return + if not (tm_client := _get_client(self.hass, service.data)): + raise ValueError("Transmission instance is not found") + torrent_id = service.data[CONF_ID] tm_client.tm_api.stop_torrent(torrent_id) tm_client.api.update() def remove_torrent(service: ServiceCall) -> None: """Remove torrent.""" - tm_client = None - for entry in self.hass.config_entries.async_entries(DOMAIN): - if entry.data[CONF_NAME] == service.data[CONF_NAME]: - tm_client = self.hass.data[DOMAIN][entry.entry_id] - break - if tm_client is None: - _LOGGER.error("Transmission instance is not found") - return + if not (tm_client := _get_client(self.hass, service.data)): + raise ValueError("Transmission instance is not found") + torrent_id = service.data[CONF_ID] delete_data = service.data[ATTR_DELETE_DATA] tm_client.tm_api.remove_torrent(torrent_id, delete_data=delete_data) diff --git a/homeassistant/components/transmission/const.py b/homeassistant/components/transmission/const.py index 185148f3bd9..742ef874a35 100644 --- a/homeassistant/components/transmission/const.py +++ b/homeassistant/components/transmission/const.py @@ -18,7 +18,7 @@ SUPPORTED_ORDER_MODES = { torrents, key=lambda t: t.ratio, reverse=True ), } - +CONF_ENTRY_ID = "entry_id" CONF_LIMIT = "limit" CONF_ORDER = "order" diff --git a/homeassistant/components/transmission/services.yaml b/homeassistant/components/transmission/services.yaml index 74861df5a70..2fd4793c785 100644 --- a/homeassistant/components/transmission/services.yaml +++ b/homeassistant/components/transmission/services.yaml @@ -2,10 +2,15 @@ add_torrent: name: Add torrent description: Add a new torrent to download (URL, magnet link or Base64 encoded). fields: + entry_id: + name: Transmission entry + description: Config entry id + selector: + config_entry: + integration: transmission name: name: Name description: Instance name as entered during entry config - required: true example: Transmission selector: text: @@ -21,10 +26,15 @@ remove_torrent: name: Remove torrent description: Remove a torrent fields: + entry_id: + name: Transmission entry + description: Config entry id + selector: + config_entry: + integration: transmission name: name: Name description: Instance name as entered during entry config - required: true example: Transmission selector: text: @@ -46,6 +56,12 @@ start_torrent: name: Start torrent description: Start a torrent fields: + entry_id: + name: Transmission entry + description: Config entry id + selector: + config_entry: + integration: transmission name: name: Name description: Instance name as entered during entry config @@ -63,10 +79,15 @@ stop_torrent: name: Stop torrent description: Stop a torrent fields: + entry_id: + name: Transmission entry + description: Config entry id + selector: + config_entry: + integration: transmission name: name: Name description: Instance name as entered during entry config - required: true example: Transmission selector: text: diff --git a/homeassistant/components/transmission/strings.json b/homeassistant/components/transmission/strings.json index 0194917c416..a8ba9e5fcb3 100644 --- a/homeassistant/components/transmission/strings.json +++ b/homeassistant/components/transmission/strings.json @@ -40,5 +40,18 @@ } } } + }, + "issues": { + "deprecated_key": { + "title": "The name key in Transmission services is being removed", + "fix_flow": { + "step": { + "confirm": { + "title": "The name key in Transmission services is being removed", + "description": "Update any automations or scripts that use this service and replace the name key with the entry_id key." + } + } + } + } } } diff --git a/homeassistant/components/transmission/translations/en.json b/homeassistant/components/transmission/translations/en.json index 3726f6f0a7e..46ab2c64ad9 100644 --- a/homeassistant/components/transmission/translations/en.json +++ b/homeassistant/components/transmission/translations/en.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "Update any automations or scripts that use this service and replace the name key with the entry_id key.", + "title": "The name key in Transmission services is being removed" + } + } + }, + "title": "The name key in Transmission services is being removed" + } + }, "options": { "step": { "init": { From 662aee17a66c2edccce16e5149b6fe87be225b6f Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sun, 30 Oct 2022 13:02:11 +0100 Subject: [PATCH 0085/1033] Scrape move yaml config to integration key (#74325) * Scrape take 2 * cleanup * new entity name * Fix name, add tests * Use FlowResultType * Add test abort * issue * hassfest * Remove not needed test * clean * Remove config entry and implement datacoordinator * fix codeowners * fix codeowners * codeowners reset * Fix coordinator * Remove test config_flow * Fix tests * hassfest * reset config flow * reset strings * reset sensor * next version * Reconfig * Adjust sensor * cleanup sensor * cleanup init * Fix tests * coverage * Guard against empty sensor * naming * Remove coverage * Review comments * Remove print * Move sensor check --- homeassistant/components/scrape/__init__.py | 85 ++++ homeassistant/components/scrape/const.py | 1 + homeassistant/components/scrape/sensor.py | 50 ++- homeassistant/components/scrape/strings.json | 6 + .../components/scrape/translations/en.json | 6 + tests/components/scrape/__init__.py | 31 ++ tests/components/scrape/test_init.py | 67 ++++ tests/components/scrape/test_sensor.py | 366 +++++++++++++----- 8 files changed, 498 insertions(+), 114 deletions(-) create mode 100644 tests/components/scrape/test_init.py diff --git a/homeassistant/components/scrape/__init__.py b/homeassistant/components/scrape/__init__.py index f9222c126b5..9b6fb9210f3 100644 --- a/homeassistant/components/scrape/__init__.py +++ b/homeassistant/components/scrape/__init__.py @@ -1 +1,86 @@ """The scrape component.""" +from __future__ import annotations + +from datetime import timedelta +import logging + +import voluptuous as vol + +from homeassistant.components.rest import RESOURCE_SCHEMA, create_rest_data_from_config +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.const import ( + CONF_ATTRIBUTE, + CONF_SCAN_INTERVAL, + CONF_VALUE_TEMPLATE, + Platform, +) +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import PlatformNotReady +from homeassistant.helpers import discovery +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.template_entity import TEMPLATE_SENSOR_BASE_SCHEMA +from homeassistant.helpers.typing import ConfigType + +from .const import CONF_INDEX, CONF_SELECT, DEFAULT_SCAN_INTERVAL, DOMAIN +from .coordinator import ScrapeCoordinator + +_LOGGER = logging.getLogger(__name__) + + +SENSOR_SCHEMA = vol.Schema( + { + **TEMPLATE_SENSOR_BASE_SCHEMA.schema, + vol.Optional(CONF_ATTRIBUTE): cv.string, + vol.Optional(CONF_INDEX, default=0): cv.positive_int, + vol.Required(CONF_SELECT): cv.string, + vol.Optional(CONF_VALUE_TEMPLATE): cv.template, + } +) + +COMBINED_SCHEMA = vol.Schema( + { + vol.Optional(CONF_SCAN_INTERVAL): cv.time_period, + **RESOURCE_SCHEMA, + vol.Optional(SENSOR_DOMAIN): vol.All( + cv.ensure_list, [vol.Schema(SENSOR_SCHEMA)] + ), + } +) + +CONFIG_SCHEMA = vol.Schema( + {vol.Optional(DOMAIN): vol.All(cv.ensure_list, [COMBINED_SCHEMA])}, + extra=vol.ALLOW_EXTRA, +) + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up Scrape from yaml config.""" + if not (scrape_config := config.get(DOMAIN)): + return True + + for resource_config in scrape_config: + if not (sensors := resource_config.get(SENSOR_DOMAIN)): + raise PlatformNotReady("No sensors configured") + + rest = create_rest_data_from_config(hass, resource_config) + coordinator = ScrapeCoordinator( + hass, + rest, + timedelta( + seconds=resource_config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) + ), + ) + await coordinator.async_refresh() + if coordinator.data is None: + raise PlatformNotReady + + for sensor_config in sensors: + discovery.load_platform( + hass, + Platform.SENSOR, + DOMAIN, + {"coordinator": coordinator, "config": sensor_config}, + config, + ) + + return True diff --git a/homeassistant/components/scrape/const.py b/homeassistant/components/scrape/const.py index 88eb661d29a..26926729309 100644 --- a/homeassistant/components/scrape/const.py +++ b/homeassistant/components/scrape/const.py @@ -6,6 +6,7 @@ from homeassistant.const import Platform DOMAIN = "scrape" DEFAULT_NAME = "Web scrape" DEFAULT_VERIFY_SSL = True +DEFAULT_SCAN_INTERVAL = 60 * 10 PLATFORMS = [Platform.SENSOR] diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index fe6837c0db5..d6e5a60d339 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -34,6 +34,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.template import Template from homeassistant.helpers.template_entity import ( TEMPLATE_SENSOR_BASE_SCHEMA, @@ -42,7 +43,7 @@ from homeassistant.helpers.template_entity import ( from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import CONF_INDEX, CONF_SELECT, DEFAULT_NAME, DEFAULT_VERIFY_SSL +from .const import CONF_INDEX, CONF_SELECT, DEFAULT_NAME, DEFAULT_VERIFY_SSL, DOMAIN from .coordinator import ScrapeCoordinator _LOGGER = logging.getLogger(__name__) @@ -82,25 +83,40 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Web scrape sensor.""" - resource_config = vol.Schema(RESOURCE_SCHEMA, extra=vol.REMOVE_EXTRA)(config) - rest = create_rest_data_from_config(hass, resource_config) + if discovery_info is None: + async_create_issue( + hass, + DOMAIN, + "moved_yaml", + breaks_in_ha_version="2022.12.0", + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key="moved_yaml", + ) + resource_config = vol.Schema(RESOURCE_SCHEMA, extra=vol.REMOVE_EXTRA)(config) + rest = create_rest_data_from_config(hass, resource_config) - coordinator = ScrapeCoordinator(hass, rest, SCAN_INTERVAL) - await coordinator.async_refresh() - if coordinator.data is None: - raise PlatformNotReady + coordinator = ScrapeCoordinator(hass, rest, SCAN_INTERVAL) + await coordinator.async_refresh() + if coordinator.data is None: + raise PlatformNotReady - sensor_config = vol.Schema( - TEMPLATE_SENSOR_BASE_SCHEMA.schema, extra=vol.REMOVE_EXTRA - )(config) + sensor_config = config + template_config = vol.Schema( + TEMPLATE_SENSOR_BASE_SCHEMA.schema, extra=vol.REMOVE_EXTRA + )(sensor_config) - name: str = config[CONF_NAME] - unique_id: str | None = config.get(CONF_UNIQUE_ID) + else: + coordinator = discovery_info["coordinator"] + sensor_config = discovery_info["config"] + template_config = sensor_config - select: str | None = config.get(CONF_SELECT) - attr: str | None = config.get(CONF_ATTRIBUTE) - index: int = config[CONF_INDEX] - value_template: Template | None = config.get(CONF_VALUE_TEMPLATE) + name: str = template_config[CONF_NAME] + unique_id: str | None = template_config.get(CONF_UNIQUE_ID) + select: str | None = sensor_config.get(CONF_SELECT) + attr: str | None = sensor_config.get(CONF_ATTRIBUTE) + index: int = sensor_config[CONF_INDEX] + value_template: Template | None = sensor_config.get(CONF_VALUE_TEMPLATE) if value_template is not None: value_template.hass = hass @@ -110,7 +126,7 @@ async def async_setup_platform( ScrapeSensor( hass, coordinator, - sensor_config, + template_config, name, unique_id, select, diff --git a/homeassistant/components/scrape/strings.json b/homeassistant/components/scrape/strings.json index f328423f5b6..11c17a176a3 100644 --- a/homeassistant/components/scrape/strings.json +++ b/homeassistant/components/scrape/strings.json @@ -69,5 +69,11 @@ } } } + }, + "issues": { + "moved_yaml": { + "title": "The Scrape YAML configuration has been moved", + "description": "Configuring Scrape using YAML has been moved to integration key.\n\nYour existing YAML configuration will be working for 2 more versions.\n\nMigrate your YAML configuration to the integration key according to the documentation." + } } } diff --git a/homeassistant/components/scrape/translations/en.json b/homeassistant/components/scrape/translations/en.json index 20831f5251a..b64310f0251 100644 --- a/homeassistant/components/scrape/translations/en.json +++ b/homeassistant/components/scrape/translations/en.json @@ -36,6 +36,12 @@ } } }, + "issues": { + "moved_yaml": { + "description": "Configuring Scrape using YAML has been moved to integration key.\n\nYour existing YAML configuration will be working for 2 more versions.\n\nMigrate your YAML configuration to the integration key according to the documentation.", + "title": "The Scrape YAML configuration has been moved" + } + }, "options": { "step": { "init": { diff --git a/tests/components/scrape/__init__.py b/tests/components/scrape/__init__.py index 644ea84854a..d4eac856d7d 100644 --- a/tests/components/scrape/__init__.py +++ b/tests/components/scrape/__init__.py @@ -4,6 +4,31 @@ from __future__ import annotations from typing import Any +def return_integration_config( + *, + authentication=None, + username=None, + password=None, + headers=None, + sensors=None, +) -> dict[str, dict[str, Any]]: + """Return config.""" + config = { + "resource": "https://www.home-assistant.io", + "verify_ssl": True, + "sensor": sensors, + } + if authentication: + config["authentication"] = authentication + if username: + config["username"] = username + config["password"] = password + if headers: + config["headers"] = headers + + return config + + def return_config( select, name, @@ -19,6 +44,7 @@ def return_config( password=None, headers=None, unique_id=None, + remove_platform=False, ) -> dict[str, dict[str, Any]]: """Return config.""" config = { @@ -26,7 +52,11 @@ def return_config( "resource": "https://www.home-assistant.io", "select": select, "name": name, + "index": 0, + "verify_ssl": True, } + if remove_platform: + config.pop("platform") if attribute: config["attribute"] = attribute if index: @@ -41,6 +71,7 @@ def return_config( config["state_class"] = state_class if authentication: config["authentication"] = authentication + if username: config["username"] = username config["password"] = password if headers: diff --git a/tests/components/scrape/test_init.py b/tests/components/scrape/test_init.py new file mode 100644 index 00000000000..22c07a2acaf --- /dev/null +++ b/tests/components/scrape/test_init.py @@ -0,0 +1,67 @@ +"""Test Scrape component setup process.""" +from __future__ import annotations + +from unittest.mock import patch + +from homeassistant.components.scrape.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er +from homeassistant.setup import async_setup_component + +from . import MockRestData, return_integration_config + + +async def test_setup_config(hass: HomeAssistant) -> None: + """Test setup from yaml.""" + config = { + DOMAIN: [ + return_integration_config( + sensors=[{"select": ".current-version h1", "name": "HA version"}] + ) + ] + } + + mocker = MockRestData("test_scrape_sensor") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ) as mock_setup: + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.ha_version") + assert state.state == "Current Version: 2021.12.10" + + assert len(mock_setup.mock_calls) == 1 + + +async def test_setup_no_data_fails(hass: HomeAssistant) -> None: + """Test setup entry no data fails.""" + config = { + DOMAIN: [ + return_integration_config( + sensors=[{"select": ".current-version h1", "name": "HA version"}] + ), + ] + } + + with patch( + "homeassistant.components.scrape.coordinator.RestData", + return_value=MockRestData("test_scrape_sensor_no_data"), + ): + assert not await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.ha_version") + assert state is None + + +async def test_setup_config_no_configuration(hass: HomeAssistant) -> None: + """Test setup from yaml missing configuration options.""" + config = {DOMAIN: None} + + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + entities = er.async_get(hass) + assert entities.entities == {} diff --git a/tests/components/scrape/test_sensor.py b/tests/components/scrape/test_sensor.py index 00fdc7eb900..24e583f5a87 100644 --- a/tests/components/scrape/test_sensor.py +++ b/tests/components/scrape/test_sensor.py @@ -7,6 +7,7 @@ from unittest.mock import patch from homeassistant.components.scrape.sensor import SCAN_INTERVAL from homeassistant.components.sensor import ( CONF_STATE_CLASS, + DOMAIN as SENSOR_DOMAIN, SensorDeviceClass, SensorStateClass, ) @@ -21,7 +22,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component -from . import MockRestData, return_config +from . import MockRestData, return_config, return_integration_config from tests.common import async_fire_time_changed @@ -30,102 +31,30 @@ DOMAIN = "scrape" async def test_scrape_sensor(hass: HomeAssistant) -> None: """Test Scrape sensor minimal.""" - config = {"sensor": return_config(select=".current-version h1", name="HA version")} + config = { + DOMAIN: [ + return_integration_config( + sensors=[{"select": ".current-version h1", "name": "HA version"}] + ) + ] + } mocker = MockRestData("test_scrape_sensor") with patch( "homeassistant.components.rest.RestData", return_value=mocker, ): - assert await async_setup_component(hass, "sensor", config) + assert await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() state = hass.states.get("sensor.ha_version") assert state.state == "Current Version: 2021.12.10" -async def test_scrape_sensor_value_template(hass: HomeAssistant) -> None: - """Test Scrape sensor with value template.""" +async def test_scrape_sensor_platform_yaml(hass: HomeAssistant) -> None: + """Test Scrape sensor load from sensor platform.""" config = { - "sensor": return_config( - select=".current-version h1", - name="HA version", - template="{{ value.split(':')[1] }}", - ) - } - - mocker = MockRestData("test_scrape_sensor") - with patch( - "homeassistant.components.rest.RestData", - return_value=mocker, - ): - assert await async_setup_component(hass, "sensor", config) - await hass.async_block_till_done() - - state = hass.states.get("sensor.ha_version") - assert state.state == "2021.12.10" - - -async def test_scrape_uom_and_classes(hass: HomeAssistant) -> None: - """Test Scrape sensor for unit of measurement, device class and state class.""" - config = { - "sensor": return_config( - select=".current-temp h3", - name="Current Temp", - template="{{ value.split(':')[1] }}", - uom="°C", - device_class="temperature", - state_class="measurement", - ) - } - - mocker = MockRestData("test_scrape_uom_and_classes") - with patch( - "homeassistant.components.rest.RestData", - return_value=mocker, - ): - assert await async_setup_component(hass, "sensor", config) - await hass.async_block_till_done() - - state = hass.states.get("sensor.current_temp") - assert state.state == "22.1" - assert state.attributes[CONF_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS - assert state.attributes[CONF_DEVICE_CLASS] == SensorDeviceClass.TEMPERATURE - assert state.attributes[CONF_STATE_CLASS] == SensorStateClass.MEASUREMENT - - -async def test_scrape_unique_id(hass: HomeAssistant) -> None: - """Test Scrape sensor for unique id.""" - config = { - "sensor": return_config( - select=".current-temp h3", - name="Current Temp", - template="{{ value.split(':')[1] }}", - unique_id="very_unique_id", - ) - } - - mocker = MockRestData("test_scrape_uom_and_classes") - with patch( - "homeassistant.components.rest.RestData", - return_value=mocker, - ): - assert await async_setup_component(hass, "sensor", config) - await hass.async_block_till_done() - - state = hass.states.get("sensor.current_temp") - assert state.state == "22.1" - - registry = er.async_get(hass) - entry = registry.async_get("sensor.current_temp") - assert entry - assert entry.unique_id == "very_unique_id" - - -async def test_scrape_sensor_authentication(hass: HomeAssistant) -> None: - """Test Scrape sensor with authentication.""" - config = { - "sensor": [ + SENSOR_DOMAIN: [ return_config( select=".return", name="Auth page", @@ -147,7 +76,170 @@ async def test_scrape_sensor_authentication(hass: HomeAssistant) -> None: "homeassistant.components.rest.RestData", return_value=mocker, ): - assert await async_setup_component(hass, "sensor", config) + assert await async_setup_component(hass, SENSOR_DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.auth_page") + assert state.state == "secret text" + state2 = hass.states.get("sensor.auth_page2") + assert state2.state == "secret text" + + +async def test_scrape_sensor_platform_yaml_no_data(hass: HomeAssistant, caplog) -> None: + """Test Scrape sensor load from sensor platform fetching no data.""" + config = { + SENSOR_DOMAIN: [ + return_config( + select=".return", + name="Auth page", + username="user@secret.com", + password="12345678", + authentication="digest", + ), + ] + } + + mocker = MockRestData("test_scrape_sensor_no_data") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ): + assert await async_setup_component(hass, SENSOR_DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.auth_page") + assert not state + assert "Platform scrape not ready yet: None; Retrying in background" in caplog.text + + +async def test_scrape_sensor_value_template(hass: HomeAssistant) -> None: + """Test Scrape sensor with value template.""" + config = { + DOMAIN: [ + return_integration_config( + sensors=[ + { + "select": ".current-version h1", + "name": "HA version", + "value_template": "{{ value.split(':')[1] }}", + } + ] + ) + ] + } + + mocker = MockRestData("test_scrape_sensor") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.ha_version") + assert state.state == "2021.12.10" + + +async def test_scrape_uom_and_classes(hass: HomeAssistant) -> None: + """Test Scrape sensor for unit of measurement, device class and state class.""" + config = { + DOMAIN: [ + return_integration_config( + sensors=[ + { + "select": ".current-temp h3", + "name": "Current Temp", + "value_template": "{{ value.split(':')[1] }}", + "unit_of_measurement": "°C", + "device_class": "temperature", + "state_class": "measurement", + } + ] + ) + ] + } + + mocker = MockRestData("test_scrape_uom_and_classes") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.current_temp") + assert state.state == "22.1" + assert state.attributes[CONF_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS + assert state.attributes[CONF_DEVICE_CLASS] == SensorDeviceClass.TEMPERATURE + assert state.attributes[CONF_STATE_CLASS] == SensorStateClass.MEASUREMENT + + +async def test_scrape_unique_id(hass: HomeAssistant) -> None: + """Test Scrape sensor for unique id.""" + config = { + DOMAIN: return_integration_config( + sensors=[ + { + "select": ".current-temp h3", + "name": "Current Temp", + "value_template": "{{ value.split(':')[1] }}", + "unique_id": "very_unique_id", + } + ] + ) + } + + mocker = MockRestData("test_scrape_uom_and_classes") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.current_temp") + assert state.state == "22.1" + + registry = er.async_get(hass) + entry = registry.async_get("sensor.current_temp") + assert entry + assert entry.unique_id == "very_unique_id" + + +async def test_scrape_sensor_authentication(hass: HomeAssistant) -> None: + """Test Scrape sensor with authentication.""" + config = { + DOMAIN: [ + return_integration_config( + authentication="digest", + username="user@secret.com", + password="12345678", + sensors=[ + { + "select": ".return", + "name": "Auth page", + }, + ], + ), + return_integration_config( + username="user@secret.com", + password="12345678", + sensors=[ + { + "select": ".return", + "name": "Auth page2", + }, + ], + ), + ] + } + + mocker = MockRestData("test_scrape_sensor_authentication") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ): + assert await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() state = hass.states.get("sensor.auth_page") @@ -158,14 +250,18 @@ async def test_scrape_sensor_authentication(hass: HomeAssistant) -> None: async def test_scrape_sensor_no_data(hass: HomeAssistant) -> None: """Test Scrape sensor fails on no data.""" - config = {"sensor": return_config(select=".current-version h1", name="HA version")} + config = { + DOMAIN: return_integration_config( + sensors=[{"select": ".current-version h1", "name": "HA version"}] + ) + } mocker = MockRestData("test_scrape_sensor_no_data") with patch( "homeassistant.components.rest.RestData", return_value=mocker, ): - assert await async_setup_component(hass, "sensor", config) + assert not await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() state = hass.states.get("sensor.ha_version") @@ -174,14 +270,20 @@ async def test_scrape_sensor_no_data(hass: HomeAssistant) -> None: async def test_scrape_sensor_no_data_refresh(hass: HomeAssistant) -> None: """Test Scrape sensor no data on refresh.""" - config = {"sensor": return_config(select=".current-version h1", name="HA version")} + config = { + DOMAIN: [ + return_integration_config( + sensors=[{"select": ".current-version h1", "name": "HA version"}] + ) + ] + } mocker = MockRestData("test_scrape_sensor") with patch( "homeassistant.components.rest.RestData", return_value=mocker, ): - assert await async_setup_component(hass, "sensor", config) + assert await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() state = hass.states.get("sensor.ha_version") @@ -200,9 +302,18 @@ async def test_scrape_sensor_no_data_refresh(hass: HomeAssistant) -> None: async def test_scrape_sensor_attribute_and_tag(hass: HomeAssistant) -> None: """Test Scrape sensor with attribute and tag.""" config = { - "sensor": [ - return_config(select="div", name="HA class", index=1, attribute="class"), - return_config(select="template", name="HA template"), + DOMAIN: [ + return_integration_config( + sensors=[ + { + "index": 1, + "select": "div", + "name": "HA class", + "attribute": "class", + }, + {"select": "template", "name": "HA template"}, + ], + ), ] } @@ -211,7 +322,7 @@ async def test_scrape_sensor_attribute_and_tag(hass: HomeAssistant) -> None: "homeassistant.components.rest.RestData", return_value=mocker, ): - assert await async_setup_component(hass, "sensor", config) + assert await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() state = hass.states.get("sensor.ha_class") @@ -223,9 +334,22 @@ async def test_scrape_sensor_attribute_and_tag(hass: HomeAssistant) -> None: async def test_scrape_sensor_errors(hass: HomeAssistant) -> None: """Test Scrape sensor handle errors.""" config = { - "sensor": [ - return_config(select="div", name="HA class", index=5, attribute="class"), - return_config(select="div", name="HA class2", attribute="classes"), + DOMAIN: [ + return_integration_config( + sensors=[ + { + "index": 5, + "select": "div", + "name": "HA class", + "attribute": "class", + }, + { + "select": "div", + "name": "HA class2", + "attribute": "classes", + }, + ], + ), ] } @@ -234,10 +358,58 @@ async def test_scrape_sensor_errors(hass: HomeAssistant) -> None: "homeassistant.components.rest.RestData", return_value=mocker, ): - assert await async_setup_component(hass, "sensor", config) + assert await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() state = hass.states.get("sensor.ha_class") assert state.state == STATE_UNKNOWN state2 = hass.states.get("sensor.ha_class2") assert state2.state == STATE_UNKNOWN + + +async def test_scrape_sensor_unique_id(hass: HomeAssistant) -> None: + """Test Scrape sensor with unique_id.""" + config = { + DOMAIN: [ + return_integration_config( + sensors=[ + { + "select": ".current-version h1", + "name": "HA version", + "unique_id": "ha_version_unique_id", + } + ] + ) + ] + } + + mocker = MockRestData("test_scrape_sensor") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.ha_version") + assert state.state == "Current Version: 2021.12.10" + + entity_reg = er.async_get(hass) + entity = entity_reg.async_get("sensor.ha_version") + + assert entity.unique_id == "ha_version_unique_id" + + +async def test_scrape_sensor_not_configured_sensor(hass: HomeAssistant, caplog) -> None: + """Test Scrape sensor with missing configured sensors.""" + config = {DOMAIN: [return_integration_config(sensors=None)]} + + mocker = MockRestData("test_scrape_sensor") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ): + assert not await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + assert "No sensors configured" in caplog.text From df29a8388df8b6f8a8749daf1f8876e348347ca8 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Sun, 30 Oct 2022 13:27:42 +0100 Subject: [PATCH 0086/1033] Make Netatmo/Legrande/BTicino lights and switches optimistic (#81246) * Make Netatmo lights optimistic * Same for switches --- homeassistant/components/netatmo/light.py | 7 +++++-- homeassistant/components/netatmo/switch.py | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/netatmo/light.py b/homeassistant/components/netatmo/light.py index b3e352eb7d8..e3bd8952b55 100644 --- a/homeassistant/components/netatmo/light.py +++ b/homeassistant/components/netatmo/light.py @@ -193,17 +193,20 @@ class NetatmoLight(NetatmoBase, LightEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn light on.""" - _LOGGER.debug("Turn light '%s' on", self.name) if ATTR_BRIGHTNESS in kwargs: await self._dimmer.async_set_brightness(kwargs[ATTR_BRIGHTNESS]) else: await self._dimmer.async_on() + self._attr_is_on = True + self.async_write_ha_state() + async def async_turn_off(self, **kwargs: Any) -> None: """Turn light off.""" - _LOGGER.debug("Turn light '%s' off", self.name) await self._dimmer.async_off() + self._attr_is_on = False + self.async_write_ha_state() @callback def async_update_callback(self) -> None: diff --git a/homeassistant/components/netatmo/switch.py b/homeassistant/components/netatmo/switch.py index 338d073c205..a2e2e67db39 100644 --- a/homeassistant/components/netatmo/switch.py +++ b/homeassistant/components/netatmo/switch.py @@ -77,7 +77,11 @@ class NetatmoSwitch(NetatmoBase, SwitchEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn the zone on.""" await self._switch.async_on() + self._attr_is_on = True + self.async_write_ha_state() async def async_turn_off(self, **kwargs: Any) -> None: """Turn the zone off.""" await self._switch.async_off() + self._attr_is_on = False + self.async_write_ha_state() From d5a46c3a1068ec22dd2b0181a6af690668145f4b Mon Sep 17 00:00:00 2001 From: Jafar Atili Date: Sun, 30 Oct 2022 17:06:00 +0200 Subject: [PATCH 0087/1033] Bump pyswitchbee to 1.6.1 (#81067) * Bumped pyswitchbee to 1.6.0 * library typing fix --- homeassistant/components/switchbee/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbee/manifest.json b/homeassistant/components/switchbee/manifest.json index 75e5b2e9bfd..f7bcfef85a4 100644 --- a/homeassistant/components/switchbee/manifest.json +++ b/homeassistant/components/switchbee/manifest.json @@ -3,7 +3,7 @@ "name": "SwitchBee", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/switchbee", - "requirements": ["pyswitchbee==1.5.5"], + "requirements": ["pyswitchbee==1.6.1"], "codeowners": ["@jafar-atili"], "iot_class": "local_polling" } diff --git a/requirements_all.txt b/requirements_all.txt index 493d4b00eae..a7a86581a70 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1929,7 +1929,7 @@ pystiebeleltron==0.0.1.dev2 pysuez==0.1.19 # homeassistant.components.switchbee -pyswitchbee==1.5.5 +pyswitchbee==1.6.1 # homeassistant.components.syncthru pysyncthru==0.7.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3edaf01774d..ecf86c90253 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1358,7 +1358,7 @@ pyspcwebgw==0.4.0 pysqueezebox==0.6.1 # homeassistant.components.switchbee -pyswitchbee==1.5.5 +pyswitchbee==1.6.1 # homeassistant.components.syncthru pysyncthru==0.7.10 From eb76f9b3cb2d6260136467bec3a59f18eef98dde Mon Sep 17 00:00:00 2001 From: Jc2k Date: Sun, 30 Oct 2022 15:32:19 +0000 Subject: [PATCH 0088/1033] Set the correct state class for Eve Energy in homekit_controller (#81255) --- homeassistant/components/homekit_controller/sensor.py | 2 +- .../homekit_controller/specific_devices/test_eve_energy.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/homekit_controller/sensor.py b/homeassistant/components/homekit_controller/sensor.py index 150f2badc6b..49047b28eae 100644 --- a/homeassistant/components/homekit_controller/sensor.py +++ b/homeassistant/components/homekit_controller/sensor.py @@ -183,7 +183,7 @@ SIMPLE_SENSOR: dict[str, HomeKitSensorEntityDescription] = { key=CharacteristicsTypes.VENDOR_EVE_ENERGY_KW_HOUR, name="Energy kWh", device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL_INCREASING, native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, ), CharacteristicsTypes.VENDOR_EVE_ENERGY_VOLTAGE: HomeKitSensorEntityDescription( diff --git a/tests/components/homekit_controller/specific_devices/test_eve_energy.py b/tests/components/homekit_controller/specific_devices/test_eve_energy.py index 65e5c16179f..e678b3bbbaa 100644 --- a/tests/components/homekit_controller/specific_devices/test_eve_energy.py +++ b/tests/components/homekit_controller/specific_devices/test_eve_energy.py @@ -70,7 +70,7 @@ async def test_eve_energy_setup(hass): entity_id="sensor.eve_energy_50ff_energy_kwh", unique_id="00:00:00:00:00:00_1_28_35", friendly_name="Eve Energy 50FF Energy kWh", - capabilities={"state_class": SensorStateClass.MEASUREMENT}, + capabilities={"state_class": SensorStateClass.TOTAL_INCREASING}, unit_of_measurement=ENERGY_KILO_WATT_HOUR, state="0.28999999165535", ), From 225be6fc2eaefc3c826a9358e9e0cde547df2f15 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 30 Oct 2022 10:35:39 -0500 Subject: [PATCH 0089/1033] Bump aiohomekit to 2.2.9 (#81254) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 224b24f6077..58e258294a0 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.8"], + "requirements": ["aiohomekit==2.2.9"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index a7a86581a70..cc8200f00f9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.8 +aiohomekit==2.2.9 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ecf86c90253..396b0063911 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.8 +aiohomekit==2.2.9 # homeassistant.components.emulated_hue # homeassistant.components.http From 5d282db439cc5f2bb3ca6e6116bda19d74ae86b8 Mon Sep 17 00:00:00 2001 From: Pascal Reeb Date: Sun, 30 Oct 2022 16:36:19 +0100 Subject: [PATCH 0090/1033] Handle HTTPError on nuki integration (#80801) fix(nuki): handle requests errors --- homeassistant/components/nuki/config_flow.py | 12 +---- homeassistant/components/nuki/helpers.py | 9 ++++ homeassistant/components/nuki/lock.py | 48 +++++++++++++++----- 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/nuki/config_flow.py b/homeassistant/components/nuki/config_flow.py index 85144d9bb77..310197d55d8 100644 --- a/homeassistant/components/nuki/config_flow.py +++ b/homeassistant/components/nuki/config_flow.py @@ -8,13 +8,13 @@ from pynuki.bridge import InvalidCredentialsException from requests.exceptions import RequestException import voluptuous as vol -from homeassistant import config_entries, exceptions +from homeassistant import config_entries from homeassistant.components import dhcp from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TOKEN from homeassistant.data_entry_flow import FlowResult from .const import DEFAULT_PORT, DEFAULT_TIMEOUT, DOMAIN -from .helpers import parse_id +from .helpers import CannotConnect, InvalidAuth, parse_id _LOGGER = logging.getLogger(__name__) @@ -153,11 +153,3 @@ class NukiConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_show_form( step_id="user", data_schema=data_schema, errors=errors ) - - -class CannotConnect(exceptions.HomeAssistantError): - """Error to indicate we cannot connect.""" - - -class InvalidAuth(exceptions.HomeAssistantError): - """Error to indicate there is invalid auth.""" diff --git a/homeassistant/components/nuki/helpers.py b/homeassistant/components/nuki/helpers.py index 3deedf9d8db..45b7420754a 100644 --- a/homeassistant/components/nuki/helpers.py +++ b/homeassistant/components/nuki/helpers.py @@ -1,6 +1,15 @@ """nuki integration helpers.""" +from homeassistant import exceptions def parse_id(hardware_id): """Parse Nuki ID.""" return hex(hardware_id).split("x")[-1].upper() + + +class CannotConnect(exceptions.HomeAssistantError): + """Error to indicate we cannot connect.""" + + +class InvalidAuth(exceptions.HomeAssistantError): + """Error to indicate there is invalid auth.""" diff --git a/homeassistant/components/nuki/lock.py b/homeassistant/components/nuki/lock.py index 8b6c843f48a..4b89b0d3535 100644 --- a/homeassistant/components/nuki/lock.py +++ b/homeassistant/components/nuki/lock.py @@ -6,6 +6,7 @@ from typing import Any from pynuki import NukiLock, NukiOpener from pynuki.constants import MODE_OPENER_CONTINUOUS +from requests.exceptions import RequestException import voluptuous as vol from homeassistant.components.lock import LockEntity, LockEntityFeature @@ -26,6 +27,7 @@ from .const import ( DOMAIN as NUKI_DOMAIN, ERROR_STATES, ) +from .helpers import CannotConnect async def async_setup_entry( @@ -114,15 +116,24 @@ class NukiLockEntity(NukiDeviceEntity): def lock(self, **kwargs: Any) -> None: """Lock the device.""" - self._nuki_device.lock() + try: + self._nuki_device.lock() + except RequestException as err: + raise CannotConnect from err def unlock(self, **kwargs: Any) -> None: """Unlock the device.""" - self._nuki_device.unlock() + try: + self._nuki_device.unlock() + except RequestException as err: + raise CannotConnect from err def open(self, **kwargs: Any) -> None: """Open the door latch.""" - self._nuki_device.unlatch() + try: + self._nuki_device.unlatch() + except RequestException as err: + raise CannotConnect from err def lock_n_go(self, unlatch: bool) -> None: """Lock and go. @@ -130,7 +141,10 @@ class NukiLockEntity(NukiDeviceEntity): This will first unlock the door, then wait for 20 seconds (or another amount of time depending on the lock settings) and relock. """ - self._nuki_device.lock_n_go(unlatch) + try: + self._nuki_device.lock_n_go(unlatch) + except RequestException as err: + raise CannotConnect from err class NukiOpenerEntity(NukiDeviceEntity): @@ -148,15 +162,24 @@ class NukiOpenerEntity(NukiDeviceEntity): def lock(self, **kwargs: Any) -> None: """Disable ring-to-open.""" - self._nuki_device.deactivate_rto() + try: + self._nuki_device.deactivate_rto() + except RequestException as err: + raise CannotConnect from err def unlock(self, **kwargs: Any) -> None: """Enable ring-to-open.""" - self._nuki_device.activate_rto() + try: + self._nuki_device.activate_rto() + except RequestException as err: + raise CannotConnect from err def open(self, **kwargs: Any) -> None: """Buzz open the door.""" - self._nuki_device.electric_strike_actuation() + try: + self._nuki_device.electric_strike_actuation() + except RequestException as err: + raise CannotConnect from err def lock_n_go(self, unlatch: bool) -> None: """Stub service.""" @@ -168,7 +191,10 @@ class NukiOpenerEntity(NukiDeviceEntity): rings the bell. This is similar to ring-to-open, except that it does not automatically deactivate """ - if enable: - self._nuki_device.activate_continuous_mode() - else: - self._nuki_device.deactivate_continuous_mode() + try: + if enable: + self._nuki_device.activate_continuous_mode() + else: + self._nuki_device.deactivate_continuous_mode() + except RequestException as err: + raise CannotConnect from err From a1eec7b55dd8dc1e271bc078e34eace86f8d3c73 Mon Sep 17 00:00:00 2001 From: Stackie Jia Date: Mon, 31 Oct 2022 00:33:06 +0800 Subject: [PATCH 0091/1033] Expose NO2 and VOCs sensors to homekit (#81217) Co-authored-by: J. Nick Koston --- .../components/homekit/accessories.py | 4 + homeassistant/components/homekit/const.py | 2 + .../components/homekit/type_sensors.py | 64 +++++++++- homeassistant/components/homekit/util.py | 36 +++++- .../homekit/test_get_accessories.py | 12 ++ tests/components/homekit/test_type_sensors.py | 116 ++++++++++++++++-- 6 files changed, 219 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/homekit/accessories.py b/homeassistant/components/homekit/accessories.py index 61c2e3cd5dd..7d0de1a5740 100644 --- a/homeassistant/components/homekit/accessories.py +++ b/homeassistant/components/homekit/accessories.py @@ -200,6 +200,10 @@ def get_accessory( # noqa: C901 or SensorDeviceClass.PM25 in state.entity_id ): a_type = "PM25Sensor" + elif device_class == SensorDeviceClass.NITROGEN_DIOXIDE: + a_type = "NitrogenDioxideSensor" + elif device_class == SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS: + a_type = "VolatileOrganicCompoundsSensor" elif ( device_class == SensorDeviceClass.GAS or SensorDeviceClass.GAS in state.entity_id diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index 264801c521f..58e1e13a3f3 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -196,6 +196,7 @@ CHAR_MODEL = "Model" CHAR_MOTION_DETECTED = "MotionDetected" CHAR_MUTE = "Mute" CHAR_NAME = "Name" +CHAR_NITROGEN_DIOXIDE_DENSITY = "NitrogenDioxideDensity" CHAR_OBSTRUCTION_DETECTED = "ObstructionDetected" CHAR_OCCUPANCY_DETECTED = "OccupancyDetected" CHAR_ON = "On" @@ -226,6 +227,7 @@ CHAR_TARGET_TILT_ANGLE = "TargetHorizontalTiltAngle" CHAR_HOLD_POSITION = "HoldPosition" CHAR_TEMP_DISPLAY_UNITS = "TemperatureDisplayUnits" CHAR_VALVE_TYPE = "ValveType" +CHAR_VOC_DENSITY = "VOCDensity" CHAR_VOLUME = "Volume" CHAR_VOLUME_SELECTOR = "VolumeSelector" CHAR_VOLUME_CONTROL_TYPE = "VolumeControlType" diff --git a/homeassistant/components/homekit/type_sensors.py b/homeassistant/components/homekit/type_sensors.py index e877ffff07a..4e9c897dff9 100644 --- a/homeassistant/components/homekit/type_sensors.py +++ b/homeassistant/components/homekit/type_sensors.py @@ -33,10 +33,12 @@ from .const import ( CHAR_CURRENT_TEMPERATURE, CHAR_LEAK_DETECTED, CHAR_MOTION_DETECTED, + CHAR_NITROGEN_DIOXIDE_DENSITY, CHAR_OCCUPANCY_DETECTED, CHAR_PM10_DENSITY, CHAR_PM25_DENSITY, CHAR_SMOKE_DETECTED, + CHAR_VOC_DENSITY, PROP_CELSIUS, SERV_AIR_QUALITY_SENSOR, SERV_CARBON_DIOXIDE_SENSOR, @@ -55,7 +57,9 @@ from .const import ( from .util import ( convert_to_float, density_to_air_quality, + density_to_air_quality_nitrogen_dioxide, density_to_air_quality_pm10, + density_to_air_quality_voc, temperature_to_homekit, ) @@ -206,7 +210,7 @@ class PM10Sensor(AirQualitySensor): def async_update_state(self, new_state): """Update accessory after state change.""" density = convert_to_float(new_state.state) - if not density: + if density is None: return if self.char_density.value != density: self.char_density.set_value(density) @@ -233,7 +237,7 @@ class PM25Sensor(AirQualitySensor): def async_update_state(self, new_state): """Update accessory after state change.""" density = convert_to_float(new_state.state) - if not density: + if density is None: return if self.char_density.value != density: self.char_density.set_value(density) @@ -244,6 +248,62 @@ class PM25Sensor(AirQualitySensor): _LOGGER.debug("%s: Set air_quality to %d", self.entity_id, air_quality) +@TYPES.register("NitrogenDioxideSensor") +class NitrogenDioxideSensor(AirQualitySensor): + """Generate a NitrogenDioxideSensor accessory as NO2 sensor.""" + + def create_services(self): + """Override the init function for PM 2.5 Sensor.""" + serv_air_quality = self.add_preload_service( + SERV_AIR_QUALITY_SENSOR, [CHAR_NITROGEN_DIOXIDE_DENSITY] + ) + self.char_quality = serv_air_quality.configure_char(CHAR_AIR_QUALITY, value=0) + self.char_density = serv_air_quality.configure_char( + CHAR_NITROGEN_DIOXIDE_DENSITY, value=0 + ) + + @callback + def async_update_state(self, new_state): + """Update accessory after state change.""" + density = convert_to_float(new_state.state) + if density is None: + return + if self.char_density.value != density: + self.char_density.set_value(density) + _LOGGER.debug("%s: Set density to %d", self.entity_id, density) + air_quality = density_to_air_quality_nitrogen_dioxide(density) + if self.char_quality.value != air_quality: + self.char_quality.set_value(air_quality) + _LOGGER.debug("%s: Set air_quality to %d", self.entity_id, air_quality) + + +@TYPES.register("VolatileOrganicCompoundsSensor") +class VolatileOrganicCompoundsSensor(AirQualitySensor): + """Generate a VolatileOrganicCompoundsSensor accessory as VOCs sensor.""" + + def create_services(self): + """Override the init function for PM 2.5 Sensor.""" + serv_air_quality = self.add_preload_service( + SERV_AIR_QUALITY_SENSOR, [CHAR_VOC_DENSITY] + ) + self.char_quality = serv_air_quality.configure_char(CHAR_AIR_QUALITY, value=0) + self.char_density = serv_air_quality.configure_char(CHAR_VOC_DENSITY, value=0) + + @callback + def async_update_state(self, new_state): + """Update accessory after state change.""" + density = convert_to_float(new_state.state) + if density is None: + return + if self.char_density.value != density: + self.char_density.set_value(density) + _LOGGER.debug("%s: Set density to %d", self.entity_id, density) + air_quality = density_to_air_quality_voc(density) + if self.char_quality.value != air_quality: + self.char_quality.set_value(air_quality) + _LOGGER.debug("%s: Set air_quality to %d", self.entity_id, air_quality) + + @TYPES.register("CarbonMonoxideSensor") class CarbonMonoxideSensor(HomeAccessory): """Generate a CarbonMonoxidSensor accessory as CO sensor.""" diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index ee02ea1a576..413786c22c4 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -413,14 +413,40 @@ def density_to_air_quality(density: float) -> int: def density_to_air_quality_pm10(density: float) -> int: - """Map PM10 density to HomeKit AirQuality level.""" - if density <= 40: + """Map PM10 µg/m3 density to HomeKit AirQuality level.""" + if density <= 54: # US AQI 0-50 (HomeKit: Excellent) return 1 - if density <= 80: + if density <= 154: # US AQI 51-100 (HomeKit: Good) return 2 - if density <= 120: + if density <= 254: # US AQI 101-150 (HomeKit: Fair) return 3 - if density <= 300: + if density <= 354: # US AQI 151-200 (HomeKit: Inferior) + return 4 + return 5 # US AQI 201+ (HomeKit: Poor) + + +def density_to_air_quality_nitrogen_dioxide(density: float) -> int: + """Map nitrogen dioxide µg/m3 to HomeKit AirQuality level.""" + if density <= 30: + return 1 + if density <= 60: + return 2 + if density <= 80: + return 3 + if density <= 90: + return 4 + return 5 + + +def density_to_air_quality_voc(density: float) -> int: + """Map VOCs µg/m3 to HomeKit AirQuality level.""" + if density <= 24: + return 1 + if density <= 48: + return 2 + if density <= 64: + return 3 + if density <= 96: return 4 return 5 diff --git a/tests/components/homekit/test_get_accessories.py b/tests/components/homekit/test_get_accessories.py index 32f4abe98f1..12113ada5cb 100644 --- a/tests/components/homekit/test_get_accessories.py +++ b/tests/components/homekit/test_get_accessories.py @@ -226,6 +226,18 @@ def test_type_media_player(type_name, entity_id, state, attrs, config): "40", {ATTR_DEVICE_CLASS: "pm25"}, ), + ( + "NitrogenDioxideSensor", + "sensor.air_quality_nitrogen_dioxide", + "50", + {ATTR_DEVICE_CLASS: SensorDeviceClass.NITROGEN_DIOXIDE}, + ), + ( + "VolatileOrganicCompoundsSensor", + "sensor.air_quality_volatile_organic_compounds", + "55", + {ATTR_DEVICE_CLASS: SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS}, + ), ( "CarbonMonoxideSensor", "sensor.co", diff --git a/tests/components/homekit/test_type_sensors.py b/tests/components/homekit/test_type_sensors.py index 4997a35910d..28dfe04932f 100644 --- a/tests/components/homekit/test_type_sensors.py +++ b/tests/components/homekit/test_type_sensors.py @@ -15,9 +15,11 @@ from homeassistant.components.homekit.type_sensors import ( CarbonMonoxideSensor, HumiditySensor, LightSensor, + NitrogenDioxideSensor, PM10Sensor, PM25Sensor, TemperatureSensor, + VolatileOrganicCompoundsSensor, ) from homeassistant.const import ( ATTR_DEVICE_CLASS, @@ -155,24 +157,24 @@ async def test_pm10(hass, hk_driver): assert acc.char_density.value == 0 assert acc.char_quality.value == 0 - hass.states.async_set(entity_id, "34") + hass.states.async_set(entity_id, "54") await hass.async_block_till_done() - assert acc.char_density.value == 34 + assert acc.char_density.value == 54 assert acc.char_quality.value == 1 - hass.states.async_set(entity_id, "70") + hass.states.async_set(entity_id, "154") await hass.async_block_till_done() - assert acc.char_density.value == 70 + assert acc.char_density.value == 154 assert acc.char_quality.value == 2 - hass.states.async_set(entity_id, "110") + hass.states.async_set(entity_id, "254") await hass.async_block_till_done() - assert acc.char_density.value == 110 + assert acc.char_density.value == 254 assert acc.char_quality.value == 3 - hass.states.async_set(entity_id, "200") + hass.states.async_set(entity_id, "354") await hass.async_block_till_done() - assert acc.char_density.value == 200 + assert acc.char_density.value == 354 assert acc.char_quality.value == 4 hass.states.async_set(entity_id, "400") @@ -228,6 +230,104 @@ async def test_pm25(hass, hk_driver): assert acc.char_quality.value == 5 +async def test_no2(hass, hk_driver): + """Test if accessory is updated after state change.""" + entity_id = "sensor.air_quality_nitrogen_dioxide" + + hass.states.async_set(entity_id, None) + await hass.async_block_till_done() + acc = NitrogenDioxideSensor( + hass, hk_driver, "Nitrogen Dioxide Sensor", entity_id, 2, None + ) + await acc.run() + await hass.async_block_till_done() + + assert acc.aid == 2 + assert acc.category == 10 # Sensor + + assert acc.char_density.value == 0 + assert acc.char_quality.value == 0 + + hass.states.async_set(entity_id, STATE_UNKNOWN) + await hass.async_block_till_done() + assert acc.char_density.value == 0 + assert acc.char_quality.value == 0 + + hass.states.async_set(entity_id, "30") + await hass.async_block_till_done() + assert acc.char_density.value == 30 + assert acc.char_quality.value == 1 + + hass.states.async_set(entity_id, "60") + await hass.async_block_till_done() + assert acc.char_density.value == 60 + assert acc.char_quality.value == 2 + + hass.states.async_set(entity_id, "80") + await hass.async_block_till_done() + assert acc.char_density.value == 80 + assert acc.char_quality.value == 3 + + hass.states.async_set(entity_id, "90") + await hass.async_block_till_done() + assert acc.char_density.value == 90 + assert acc.char_quality.value == 4 + + hass.states.async_set(entity_id, "100") + await hass.async_block_till_done() + assert acc.char_density.value == 100 + assert acc.char_quality.value == 5 + + +async def test_voc(hass, hk_driver): + """Test if accessory is updated after state change.""" + entity_id = "sensor.air_quality_volatile_organic_compounds" + + hass.states.async_set(entity_id, None) + await hass.async_block_till_done() + acc = VolatileOrganicCompoundsSensor( + hass, hk_driver, "Volatile Organic Compounds Sensor", entity_id, 2, None + ) + await acc.run() + await hass.async_block_till_done() + + assert acc.aid == 2 + assert acc.category == 10 # Sensor + + assert acc.char_density.value == 0 + assert acc.char_quality.value == 0 + + hass.states.async_set(entity_id, STATE_UNKNOWN) + await hass.async_block_till_done() + assert acc.char_density.value == 0 + assert acc.char_quality.value == 0 + + hass.states.async_set(entity_id, "24") + await hass.async_block_till_done() + assert acc.char_density.value == 24 + assert acc.char_quality.value == 1 + + hass.states.async_set(entity_id, "48") + await hass.async_block_till_done() + assert acc.char_density.value == 48 + assert acc.char_quality.value == 2 + + hass.states.async_set(entity_id, "64") + await hass.async_block_till_done() + assert acc.char_density.value == 64 + assert acc.char_quality.value == 3 + + hass.states.async_set(entity_id, "96") + await hass.async_block_till_done() + assert acc.char_density.value == 96 + assert acc.char_quality.value == 4 + + hass.states.async_set(entity_id, "128") + await hass.async_block_till_done() + assert acc.char_density.value == 128 + assert acc.char_quality.value == 5 + + async def test_co(hass, hk_driver): """Test if accessory is updated after state change.""" entity_id = "sensor.co" From ec038835f63c012688838e67dbf1120d2b8462ef Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sun, 30 Oct 2022 20:01:10 +0100 Subject: [PATCH 0092/1033] Catch `ApiError` while checking credentials in NAM integration (#81243) * Catch ApiError while checking credentials * Update tests * Suggested change --- homeassistant/components/nam/__init__.py | 2 ++ tests/components/nam/test_init.py | 22 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/nam/__init__.py b/homeassistant/components/nam/__init__.py index 25615db6eed..0fbc9384634 100644 --- a/homeassistant/components/nam/__init__.py +++ b/homeassistant/components/nam/__init__.py @@ -56,6 +56,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: try: await nam.async_check_credentials() + except ApiError as err: + raise ConfigEntryNotReady from err except AuthFailed as err: raise ConfigEntryAuthFailed from err diff --git a/tests/components/nam/test_init.py b/tests/components/nam/test_init.py index b6f278d4e94..a6d11305599 100644 --- a/tests/components/nam/test_init.py +++ b/tests/components/nam/test_init.py @@ -32,12 +32,30 @@ async def test_config_not_ready(hass): unique_id="aa:bb:cc:dd:ee:ff", data={"host": "10.10.2.3"}, ) + entry.add_to_hass(hass) with patch( "homeassistant.components.nam.NettigoAirMonitor.initialize", side_effect=ApiError("API Error"), ): - entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + assert entry.state is ConfigEntryState.SETUP_RETRY + + +async def test_config_not_ready_while_checking_credentials(hass): + """Test for setup failure if the connection fails while checking credentials.""" + entry = MockConfigEntry( + domain=DOMAIN, + title="10.10.2.3", + unique_id="aa:bb:cc:dd:ee:ff", + data={"host": "10.10.2.3"}, + ) + entry.add_to_hass(hass) + + with patch("homeassistant.components.nam.NettigoAirMonitor.initialize"), patch( + "homeassistant.components.nam.NettigoAirMonitor.async_check_credentials", + side_effect=ApiError("API Error"), + ): await hass.config_entries.async_setup(entry.entry_id) assert entry.state is ConfigEntryState.SETUP_RETRY @@ -50,12 +68,12 @@ async def test_config_auth_failed(hass): unique_id="aa:bb:cc:dd:ee:ff", data={"host": "10.10.2.3"}, ) + entry.add_to_hass(hass) with patch( "homeassistant.components.nam.NettigoAirMonitor.async_check_credentials", side_effect=AuthFailed("Authorization has failed"), ): - entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) assert entry.state is ConfigEntryState.SETUP_ERROR From 03f74b32341fe74d0d080943be7ada39f57533b6 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Sun, 30 Oct 2022 22:46:16 +0100 Subject: [PATCH 0093/1033] Bump pyatmo to 7.3.0 (#81271) --- homeassistant/components/netatmo/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/netatmo/manifest.json b/homeassistant/components/netatmo/manifest.json index 8beb7bc521a..436b6329c1d 100644 --- a/homeassistant/components/netatmo/manifest.json +++ b/homeassistant/components/netatmo/manifest.json @@ -3,7 +3,7 @@ "name": "Netatmo", "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/netatmo", - "requirements": ["pyatmo==7.2.0"], + "requirements": ["pyatmo==7.3.0"], "after_dependencies": ["cloud", "media_source"], "dependencies": ["application_credentials", "webhook"], "codeowners": ["@cgtobi"], diff --git a/requirements_all.txt b/requirements_all.txt index cc8200f00f9..fd452c7ad9a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1442,7 +1442,7 @@ pyalmond==0.0.2 pyatag==0.3.5.3 # homeassistant.components.netatmo -pyatmo==7.2.0 +pyatmo==7.3.0 # homeassistant.components.atome pyatome==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 396b0063911..74a6fb35137 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1030,7 +1030,7 @@ pyalmond==0.0.2 pyatag==0.3.5.3 # homeassistant.components.netatmo -pyatmo==7.2.0 +pyatmo==7.3.0 # homeassistant.components.apple_tv pyatv==0.10.3 From ba8fd6b01e09fe558041b3cc2a185b953b36e2d1 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 30 Oct 2022 15:07:10 -0700 Subject: [PATCH 0094/1033] Google calendar test cleanup, avoiding dupe config entry setup (#81256) --- tests/components/google/test_calendar.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/components/google/test_calendar.py b/tests/components/google/test_calendar.py index 3bd584f4c6f..90ec8f44850 100644 --- a/tests/components/google/test_calendar.py +++ b/tests/components/google/test_calendar.py @@ -62,15 +62,11 @@ TEST_EVENT = { @pytest.fixture(autouse=True) def mock_test_setup( - hass, test_api_calendar, mock_calendars_list, - config_entry, ): - """Fixture that pulls in the default fixtures for tests in this file.""" + """Fixture that sets up the default API responses during integration setup.""" mock_calendars_list({"items": [test_api_calendar]}) - config_entry.add_to_hass(hass) - return def get_events_url(entity: str, start: str, end: str) -> str: From 1106df158dd2fe4b7443a40131a6b216fb538d2c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 30 Oct 2022 17:38:09 -0500 Subject: [PATCH 0095/1033] Bump bleak-retry-connector to 2.6.0 (#81270) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index f8d1867035d..261b4480671 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.1", - "bleak-retry-connector==2.5.0", + "bleak-retry-connector==2.6.0", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", "dbus-fast==1.59.1" diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index d48b85e346b..6762357d58d 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.5.0 +bleak-retry-connector==2.6.0 bleak==0.19.1 bluetooth-adapters==0.6.0 bluetooth-auto-recovery==0.3.6 diff --git a/requirements_all.txt b/requirements_all.txt index fd452c7ad9a..b47a16ff875 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -413,7 +413,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.5.0 +bleak-retry-connector==2.6.0 # homeassistant.components.bluetooth bleak==0.19.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 74a6fb35137..bfd4149cc41 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -337,7 +337,7 @@ bellows==0.34.2 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.5.0 +bleak-retry-connector==2.6.0 # homeassistant.components.bluetooth bleak==0.19.1 From 11d7e1e45fbabf0a73ea27472ae156c6948f98ce Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 30 Oct 2022 17:43:09 -0500 Subject: [PATCH 0096/1033] Provide a human readable error when an esphome ble proxy connection fails (#81266) --- homeassistant/components/esphome/bluetooth/client.py | 12 +++++++++++- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index 9094186226f..cffeae8d3eb 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -7,6 +7,7 @@ import logging from typing import Any, TypeVar, cast import uuid +from aioesphomeapi import ESP_CONNECTION_ERROR_DESCRIPTION, BLEConnectionError from aioesphomeapi.connection import APIConnectionError, TimeoutAPIError import async_timeout from bleak.backends.characteristic import BleakGATTCharacteristic @@ -182,8 +183,17 @@ class ESPHomeClient(BaseBleakClient): return if error: + try: + ble_connection_error = BLEConnectionError(error) + ble_connection_error_name = ble_connection_error.name + human_error = ESP_CONNECTION_ERROR_DESCRIPTION[ble_connection_error] + except (KeyError, ValueError): + ble_connection_error_name = str(error) + human_error = f"Unknown error code {error}" connected_future.set_exception( - BleakError(f"Error while connecting: {error}") + BleakError( + f"Error {ble_connection_error_name} while connecting: {human_error}" + ) ) return diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index ab33ed8585a..c0230ce8410 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==11.2.0"], + "requirements": ["aioesphomeapi==11.3.0"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/requirements_all.txt b/requirements_all.txt index b47a16ff875..b3846580ded 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -153,7 +153,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.2.0 +aioesphomeapi==11.3.0 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bfd4149cc41..83169340cfe 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -140,7 +140,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.2.0 +aioesphomeapi==11.3.0 # homeassistant.components.flo aioflo==2021.11.0 From c8a3392471ee00633103bcb081b2c3d305251752 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 30 Oct 2022 18:02:54 -0500 Subject: [PATCH 0097/1033] Move esphome gatt services cache to be per device (#81265) --- .../components/esphome/bluetooth/client.py | 6 +++--- .../components/esphome/domain_data.py | 20 ------------------- .../components/esphome/entry_data.py | 20 ++++++++++++++++++- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index cffeae8d3eb..918d93f3d2c 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -265,9 +265,9 @@ class ESPHomeClient(BaseBleakClient): A :py:class:`bleak.backends.service.BleakGATTServiceCollection` with this device's services tree. """ address_as_int = self._address_as_int - domain_data = self.domain_data + entry_data = self.entry_data if dangerous_use_bleak_cache and ( - cached_services := domain_data.get_gatt_services_cache(address_as_int) + cached_services := entry_data.get_gatt_services_cache(address_as_int) ): _LOGGER.debug( "Cached services hit for %s - %s", @@ -311,7 +311,7 @@ class ESPHomeClient(BaseBleakClient): self._ble_device.name, self._ble_device.address, ) - domain_data.set_gatt_services_cache(address_as_int, services) + entry_data.set_gatt_services_cache(address_as_int, services) return services def _resolve_characteristic( diff --git a/homeassistant/components/esphome/domain_data.py b/homeassistant/components/esphome/domain_data.py index acaa76185e7..01f0a4d6b13 100644 --- a/homeassistant/components/esphome/domain_data.py +++ b/homeassistant/components/esphome/domain_data.py @@ -1,13 +1,9 @@ """Support for esphome domain data.""" from __future__ import annotations -from collections.abc import MutableMapping from dataclasses import dataclass, field from typing import TypeVar, cast -from bleak.backends.service import BleakGATTServiceCollection -from lru import LRU # pylint: disable=no-name-in-module - from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.json import JSONEncoder @@ -17,7 +13,6 @@ from .entry_data import RuntimeEntryData STORAGE_VERSION = 1 DOMAIN = "esphome" -MAX_CACHED_SERVICES = 128 _DomainDataSelfT = TypeVar("_DomainDataSelfT", bound="DomainData") @@ -29,21 +24,6 @@ class DomainData: _entry_datas: dict[str, RuntimeEntryData] = field(default_factory=dict) _stores: dict[str, Store] = field(default_factory=dict) _entry_by_unique_id: dict[str, ConfigEntry] = field(default_factory=dict) - _gatt_services_cache: MutableMapping[int, BleakGATTServiceCollection] = field( - default_factory=lambda: LRU(MAX_CACHED_SERVICES) # type: ignore[no-any-return] - ) - - def get_gatt_services_cache( - self, address: int - ) -> BleakGATTServiceCollection | None: - """Get the BleakGATTServiceCollection for the given address.""" - return self._gatt_services_cache.get(address) - - def set_gatt_services_cache( - self, address: int, services: BleakGATTServiceCollection - ) -> None: - """Set the BleakGATTServiceCollection for the given address.""" - self._gatt_services_cache[address] = services def get_by_unique_id(self, unique_id: str) -> ConfigEntry: """Get the config entry by its unique ID.""" diff --git a/homeassistant/components/esphome/entry_data.py b/homeassistant/components/esphome/entry_data.py index ac2a148d899..5d474b0fb15 100644 --- a/homeassistant/components/esphome/entry_data.py +++ b/homeassistant/components/esphome/entry_data.py @@ -2,7 +2,7 @@ from __future__ import annotations import asyncio -from collections.abc import Callable +from collections.abc import Callable, MutableMapping from dataclasses import dataclass, field import logging from typing import Any, cast @@ -30,6 +30,8 @@ from aioesphomeapi import ( UserService, ) from aioesphomeapi.model import ButtonInfo +from bleak.backends.service import BleakGATTServiceCollection +from lru import LRU # pylint: disable=no-name-in-module from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform @@ -57,6 +59,7 @@ INFO_TYPE_TO_PLATFORM: dict[type[EntityInfo], str] = { SwitchInfo: Platform.SWITCH, TextSensorInfo: Platform.SENSOR, } +MAX_CACHED_SERVICES = 128 @dataclass @@ -92,6 +95,21 @@ class RuntimeEntryData: _ble_connection_free_futures: list[asyncio.Future[int]] = field( default_factory=list ) + _gatt_services_cache: MutableMapping[int, BleakGATTServiceCollection] = field( + default_factory=lambda: LRU(MAX_CACHED_SERVICES) # type: ignore[no-any-return] + ) + + def get_gatt_services_cache( + self, address: int + ) -> BleakGATTServiceCollection | None: + """Get the BleakGATTServiceCollection for the given address.""" + return self._gatt_services_cache.get(address) + + def set_gatt_services_cache( + self, address: int, services: BleakGATTServiceCollection + ) -> None: + """Set the BleakGATTServiceCollection for the given address.""" + self._gatt_services_cache[address] = services @callback def async_update_ble_connection_limits(self, free: int, limit: int) -> None: From 4899f1d6327ad76a3157a5a9fedf9fb69b5f7562 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Mon, 31 Oct 2022 00:27:12 +0100 Subject: [PATCH 0098/1033] Revert 81271 (#81275) --- homeassistant/components/netatmo/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/netatmo/manifest.json b/homeassistant/components/netatmo/manifest.json index 436b6329c1d..8beb7bc521a 100644 --- a/homeassistant/components/netatmo/manifest.json +++ b/homeassistant/components/netatmo/manifest.json @@ -3,7 +3,7 @@ "name": "Netatmo", "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/netatmo", - "requirements": ["pyatmo==7.3.0"], + "requirements": ["pyatmo==7.2.0"], "after_dependencies": ["cloud", "media_source"], "dependencies": ["application_credentials", "webhook"], "codeowners": ["@cgtobi"], diff --git a/requirements_all.txt b/requirements_all.txt index b3846580ded..6a7d7b18ba6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1442,7 +1442,7 @@ pyalmond==0.0.2 pyatag==0.3.5.3 # homeassistant.components.netatmo -pyatmo==7.3.0 +pyatmo==7.2.0 # homeassistant.components.atome pyatome==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 83169340cfe..4cf57dc39c8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1030,7 +1030,7 @@ pyalmond==0.0.2 pyatag==0.3.5.3 # homeassistant.components.netatmo -pyatmo==7.3.0 +pyatmo==7.2.0 # homeassistant.components.apple_tv pyatv==0.10.3 From 7e47aff316786a656244d87fd5c938c603774403 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 30 Oct 2022 19:24:14 -0500 Subject: [PATCH 0099/1033] Bump aioesphomeapi to 11.4.0 (#81277) --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index c0230ce8410..cab81882788 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==11.3.0"], + "requirements": ["aioesphomeapi==11.4.0"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/requirements_all.txt b/requirements_all.txt index 6a7d7b18ba6..9679c5a32fa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -153,7 +153,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.3.0 +aioesphomeapi==11.4.0 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4cf57dc39c8..8f8abc44030 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -140,7 +140,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.3.0 +aioesphomeapi==11.4.0 # homeassistant.components.flo aioflo==2021.11.0 From 4fb6fa9cca21067f1011745a38ec9f43fbf8d334 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 30 Oct 2022 19:24:32 -0500 Subject: [PATCH 0100/1033] Bump bleak-retry-connector to 2.7.0 (#81280) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 261b4480671..6b799e94e55 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.1", - "bleak-retry-connector==2.6.0", + "bleak-retry-connector==2.7.0", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", "dbus-fast==1.59.1" diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 6762357d58d..f75f7ba60da 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.6.0 +bleak-retry-connector==2.7.0 bleak==0.19.1 bluetooth-adapters==0.6.0 bluetooth-auto-recovery==0.3.6 diff --git a/requirements_all.txt b/requirements_all.txt index 9679c5a32fa..c9203f8962e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -413,7 +413,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.6.0 +bleak-retry-connector==2.7.0 # homeassistant.components.bluetooth bleak==0.19.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8f8abc44030..4aacb6ec9e6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -337,7 +337,7 @@ bellows==0.34.2 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.6.0 +bleak-retry-connector==2.7.0 # homeassistant.components.bluetooth bleak==0.19.1 From e63616987848772c10c2e5fbb607278e80b6bfc5 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 31 Oct 2022 00:32:43 +0000 Subject: [PATCH 0101/1033] [ci skip] Translation update --- .../google_travel_time/translations/ca.json | 1 + .../google_travel_time/translations/de.json | 1 + .../google_travel_time/translations/es.json | 1 + .../google_travel_time/translations/et.json | 1 + .../google_travel_time/translations/hu.json | 1 + .../google_travel_time/translations/ru.json | 1 + .../components/ovo_energy/translations/es.json | 2 +- .../components/ovo_energy/translations/et.json | 1 + .../components/scrape/translations/de.json | 6 ++++++ .../components/scrape/translations/es.json | 6 ++++++ .../components/transmission/translations/de.json | 13 +++++++++++++ .../components/transmission/translations/es.json | 13 +++++++++++++ .../components/transmission/translations/et.json | 13 +++++++++++++ .../components/transmission/translations/ru.json | 13 +++++++++++++ 14 files changed, 72 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/google_travel_time/translations/ca.json b/homeassistant/components/google_travel_time/translations/ca.json index cfd24ef02a8..34ca784b3ec 100644 --- a/homeassistant/components/google_travel_time/translations/ca.json +++ b/homeassistant/components/google_travel_time/translations/ca.json @@ -28,6 +28,7 @@ "mode": "Mode de transport", "time": "Temps", "time_type": "Tipus de temps", + "traffic_mode": "Mode tr\u00e0nsit", "transit_mode": "Tipus de transport", "transit_routing_preference": "Prefer\u00e8ncia de rutes de tr\u00e0nsit", "units": "Unitats" diff --git a/homeassistant/components/google_travel_time/translations/de.json b/homeassistant/components/google_travel_time/translations/de.json index 24bf9799ee0..13ecac74e3c 100644 --- a/homeassistant/components/google_travel_time/translations/de.json +++ b/homeassistant/components/google_travel_time/translations/de.json @@ -28,6 +28,7 @@ "mode": "Reisemodus", "time": "Uhrzeit", "time_type": "Zeittyp", + "traffic_mode": "Verkehrsmodus", "transit_mode": "Transit-Modus", "transit_routing_preference": "Transit-Routing-Einstellungen", "units": "Einheiten" diff --git a/homeassistant/components/google_travel_time/translations/es.json b/homeassistant/components/google_travel_time/translations/es.json index 54c8320665c..508aee06b7b 100644 --- a/homeassistant/components/google_travel_time/translations/es.json +++ b/homeassistant/components/google_travel_time/translations/es.json @@ -28,6 +28,7 @@ "mode": "Modo de viaje", "time": "Hora", "time_type": "Tipo de tiempo", + "traffic_mode": "Modo de tr\u00e1fico", "transit_mode": "Modo de tr\u00e1nsito", "transit_routing_preference": "Preferencia de ruta de tr\u00e1nsito", "units": "Unidades" diff --git a/homeassistant/components/google_travel_time/translations/et.json b/homeassistant/components/google_travel_time/translations/et.json index 0c8a90e8949..27adfb1e1b9 100644 --- a/homeassistant/components/google_travel_time/translations/et.json +++ b/homeassistant/components/google_travel_time/translations/et.json @@ -28,6 +28,7 @@ "mode": "Reisimise viis", "time": "Aeg", "time_type": "Aja t\u00fc\u00fcp", + "traffic_mode": "S\u00f5iduvahend", "transit_mode": "Liikumisviis", "transit_routing_preference": "Teekonna eelistused", "units": "\u00dchikud" diff --git a/homeassistant/components/google_travel_time/translations/hu.json b/homeassistant/components/google_travel_time/translations/hu.json index 3ad294f4fab..73d55885854 100644 --- a/homeassistant/components/google_travel_time/translations/hu.json +++ b/homeassistant/components/google_travel_time/translations/hu.json @@ -28,6 +28,7 @@ "mode": "Utaz\u00e1si m\u00f3d", "time": "Id\u0151", "time_type": "Id\u0151 t\u00edpusa", + "traffic_mode": "Forgalmi m\u00f3d", "transit_mode": "Tranzit m\u00f3d", "transit_routing_preference": "Tranzit \u00fatv\u00e1laszt\u00e1si be\u00e1ll\u00edt\u00e1s", "units": "Egys\u00e9gek" diff --git a/homeassistant/components/google_travel_time/translations/ru.json b/homeassistant/components/google_travel_time/translations/ru.json index d506ed4ca5e..31494ff1c1d 100644 --- a/homeassistant/components/google_travel_time/translations/ru.json +++ b/homeassistant/components/google_travel_time/translations/ru.json @@ -28,6 +28,7 @@ "mode": "\u0421\u043f\u043e\u0441\u043e\u0431 \u043f\u0435\u0440\u0435\u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f", "time": "\u0412\u0440\u0435\u043c\u044f", "time_type": "\u0422\u0438\u043f \u0432\u0440\u0435\u043c\u0435\u043d\u0438", + "traffic_mode": "\u0420\u0435\u0436\u0438\u043c \u0442\u0440\u0430\u0444\u0438\u043a\u0430", "transit_mode": "\u0420\u0435\u0436\u0438\u043c \u0442\u0440\u0430\u043d\u0437\u0438\u0442\u0430", "transit_routing_preference": "\u041f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0435\u043d\u0438\u0435 \u043f\u043e \u0442\u0440\u0430\u043d\u0437\u0438\u0442\u043d\u043e\u043c\u0443 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0443", "units": "\u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f" diff --git a/homeassistant/components/ovo_energy/translations/es.json b/homeassistant/components/ovo_energy/translations/es.json index 1a975921b25..a41cff3a535 100644 --- a/homeassistant/components/ovo_energy/translations/es.json +++ b/homeassistant/components/ovo_energy/translations/es.json @@ -16,7 +16,7 @@ }, "user": { "data": { - "account": "ID de la cuenta OVO (s\u00f3lo a\u00f1adir si tienes varias cuentas)", + "account": "ID de la cuenta OVO (solo a\u00f1adir si tienes varias cuentas)", "password": "Contrase\u00f1a", "username": "Nombre de usuario" }, diff --git a/homeassistant/components/ovo_energy/translations/et.json b/homeassistant/components/ovo_energy/translations/et.json index ab33d27f337..f7ce2be8a12 100644 --- a/homeassistant/components/ovo_energy/translations/et.json +++ b/homeassistant/components/ovo_energy/translations/et.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "OVO konto ID (lisa ainult siis, kui on mitu kontot)", "password": "Salas\u00f5na", "username": "Kasutajanimi" }, diff --git a/homeassistant/components/scrape/translations/de.json b/homeassistant/components/scrape/translations/de.json index d4e2f37f88d..17aeb0816a1 100644 --- a/homeassistant/components/scrape/translations/de.json +++ b/homeassistant/components/scrape/translations/de.json @@ -36,6 +36,12 @@ } } }, + "issues": { + "moved_yaml": { + "description": "Die Konfiguration von Scrape mit YAML wurde in den Integrationsschl\u00fcssel verschoben. \n\nDeine vorhandene YAML-Konfiguration funktioniert f\u00fcr zwei weitere Versionen.\n\nMigriere deine YAML-Konfiguration gem\u00e4\u00df der Dokumentation zum Integrationsschl\u00fcssel.", + "title": "Die Scrape-YAML-Konfiguration wurde verschoben" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/scrape/translations/es.json b/homeassistant/components/scrape/translations/es.json index d88197473d3..a001498c7ef 100644 --- a/homeassistant/components/scrape/translations/es.json +++ b/homeassistant/components/scrape/translations/es.json @@ -36,6 +36,12 @@ } } }, + "issues": { + "moved_yaml": { + "description": "La configuraci\u00f3n de Scrape usando YAML se ha movido a la clave de integraci\u00f3n. \n\nTu configuraci\u00f3n YAML existente funcionar\u00e1 durante 2 versiones m\u00e1s. \n\nMigra tu configuraci\u00f3n YAML a la clave de integraci\u00f3n de acuerdo con la documentaci\u00f3n.", + "title": "La configuraci\u00f3n YAML de Scrape se ha movido" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/transmission/translations/de.json b/homeassistant/components/transmission/translations/de.json index 04274f2c1cb..3f0a2364ce8 100644 --- a/homeassistant/components/transmission/translations/de.json +++ b/homeassistant/components/transmission/translations/de.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "Aktualisiere alle Automatisierungen oder Skripte, die diesen Dienst verwenden, und ersetze den Namensschl\u00fcssel durch den Entry_id-Schl\u00fcssel.", + "title": "Der Namensschl\u00fcssel in den \u00dcbertragungsdiensten wird entfernt" + } + } + }, + "title": "Der Namensschl\u00fcssel in den \u00dcbertragungsdiensten wird entfernt" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/transmission/translations/es.json b/homeassistant/components/transmission/translations/es.json index 30180811cb4..69242bda413 100644 --- a/homeassistant/components/transmission/translations/es.json +++ b/homeassistant/components/transmission/translations/es.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "Actualiza cualquier automatizaci\u00f3n o script que use este servicio y sustituye la clave nombre por la clave entry_id.", + "title": "Se va a eliminar la clave nombre en los servicios de Transmission" + } + } + }, + "title": "Se va a eliminar la clave nombre en los servicios de Transmission" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/transmission/translations/et.json b/homeassistant/components/transmission/translations/et.json index 745ef1030af..3fab9d169db 100644 --- a/homeassistant/components/transmission/translations/et.json +++ b/homeassistant/components/transmission/translations/et.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "V\u00e4rskenda k\u00f5iki seda teenust kasutavaid automatiseerimisi v\u00f5i skripte ja asenda nimev\u00f5ti v\u00f5tmega entry_id-ga.", + "title": "Transmission teenuste nimev\u00f5ti eemaldatakse" + } + } + }, + "title": "Transmission teenuste nimev\u00f5ti eemaldatakse" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/transmission/translations/ru.json b/homeassistant/components/transmission/translations/ru.json index ba6787eed7d..cfd1c7e0e84 100644 --- a/homeassistant/components/transmission/translations/ru.json +++ b/homeassistant/components/transmission/translations/ru.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "\u0412 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f\u0445 \u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u0430\u0445, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0445 \u044d\u0442\u0443 \u0441\u043b\u0443\u0436\u0431\u0443, \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u043a\u043b\u044e\u0447 name \u043d\u0430 \u043a\u043b\u044e\u0447 entry_id.", + "title": "\u0412 \u0441\u043b\u0443\u0436\u0431\u0430\u0445 Transmission \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0451\u043d \u043a\u043b\u044e\u0447 name" + } + } + }, + "title": "\u0412 \u0441\u043b\u0443\u0436\u0431\u0430\u0445 Transmission \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0451\u043d \u043a\u043b\u044e\u0447 name" + } + }, "options": { "step": { "init": { From e709b74c3f836b271cc5141be2128e440a709812 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 30 Oct 2022 20:39:34 -0500 Subject: [PATCH 0102/1033] Bump aioesphomeapi to 11.4.1 (#81282) --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index cab81882788..c27e3b8dc3e 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==11.4.0"], + "requirements": ["aioesphomeapi==11.4.1"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/requirements_all.txt b/requirements_all.txt index c9203f8962e..c0d58c5b6a1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -153,7 +153,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.4.0 +aioesphomeapi==11.4.1 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4aacb6ec9e6..561bd9531c7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -140,7 +140,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.4.0 +aioesphomeapi==11.4.1 # homeassistant.components.flo aioflo==2021.11.0 From 1d94fbb176eb353c968aeeb6aa71bf4867bbf545 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 30 Oct 2022 20:40:01 -0500 Subject: [PATCH 0103/1033] Bump bleak-retry-connector to 2.8.0 (#81283) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 6b799e94e55..660345606c8 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.1", - "bleak-retry-connector==2.7.0", + "bleak-retry-connector==2.8.0", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", "dbus-fast==1.59.1" diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index f75f7ba60da..994a8d44019 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.7.0 +bleak-retry-connector==2.8.0 bleak==0.19.1 bluetooth-adapters==0.6.0 bluetooth-auto-recovery==0.3.6 diff --git a/requirements_all.txt b/requirements_all.txt index c0d58c5b6a1..5286d267937 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -413,7 +413,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.7.0 +bleak-retry-connector==2.8.0 # homeassistant.components.bluetooth bleak==0.19.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 561bd9531c7..91d62550d4e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -337,7 +337,7 @@ bellows==0.34.2 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.7.0 +bleak-retry-connector==2.8.0 # homeassistant.components.bluetooth bleak==0.19.1 From ccefc510c37875824cb4c768c48753b2bda534ce Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 30 Oct 2022 22:10:30 -0500 Subject: [PATCH 0104/1033] Do not fire the esphome ble disconnected callback if we were not connected (#81286) --- homeassistant/components/esphome/bluetooth/client.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index 918d93f3d2c..68f1788afdb 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -127,13 +127,15 @@ class ESPHomeClient(BaseBleakClient): def _async_ble_device_disconnected(self) -> None: """Handle the BLE device disconnecting from the ESP.""" - _LOGGER.debug("%s: BLE device disconnected", self._source) - self._is_connected = False + was_connected = self._is_connected self.services = BleakGATTServiceCollection() # type: ignore[no-untyped-call] + self._is_connected = False if self._disconnected_event: self._disconnected_event.set() self._disconnected_event = None - self._async_call_bleak_disconnected_callback() + if was_connected: + _LOGGER.debug("%s: BLE device disconnected", self._source) + self._async_call_bleak_disconnected_callback() self._unsubscribe_connection_state() def _async_esp_disconnected(self) -> None: From a766b41b13e6f1aae0e5c779061281fd87656605 Mon Sep 17 00:00:00 2001 From: aschmitz <29508+aschmitz@users.noreply.github.com> Date: Sun, 30 Oct 2022 22:50:46 -0500 Subject: [PATCH 0105/1033] Add basic Aranet integration (#80865) --- CODEOWNERS | 2 + homeassistant/components/aranet/__init__.py | 56 ++++ .../components/aranet/config_flow.py | 123 +++++++++ homeassistant/components/aranet/const.py | 3 + homeassistant/components/aranet/manifest.json | 23 ++ homeassistant/components/aranet/sensor.py | 169 ++++++++++++ homeassistant/components/aranet/strings.json | 25 ++ .../components/aranet/translations/en.json | 25 ++ homeassistant/generated/bluetooth.py | 12 + homeassistant/generated/config_flows.py | 1 + homeassistant/generated/integrations.json | 6 + requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/aranet/__init__.py | 58 ++++ tests/components/aranet/conftest.py | 8 + tests/components/aranet/test_config_flow.py | 252 ++++++++++++++++++ tests/components/aranet/test_sensor.py | 111 ++++++++ 17 files changed, 880 insertions(+) create mode 100644 homeassistant/components/aranet/__init__.py create mode 100644 homeassistant/components/aranet/config_flow.py create mode 100644 homeassistant/components/aranet/const.py create mode 100644 homeassistant/components/aranet/manifest.json create mode 100644 homeassistant/components/aranet/sensor.py create mode 100644 homeassistant/components/aranet/strings.json create mode 100644 homeassistant/components/aranet/translations/en.json create mode 100644 tests/components/aranet/__init__.py create mode 100644 tests/components/aranet/conftest.py create mode 100644 tests/components/aranet/test_config_flow.py create mode 100644 tests/components/aranet/test_sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index acc723a3493..4012868c712 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -94,6 +94,8 @@ build.json @home-assistant/supervisor /tests/components/apprise/ @caronc /homeassistant/components/aprs/ @PhilRW /tests/components/aprs/ @PhilRW +/homeassistant/components/aranet/ @aschmitz +/tests/components/aranet/ @aschmitz /homeassistant/components/arcam_fmj/ @elupus /tests/components/arcam_fmj/ @elupus /homeassistant/components/arris_tg2492lg/ @vanbalken diff --git a/homeassistant/components/aranet/__init__.py b/homeassistant/components/aranet/__init__.py new file mode 100644 index 00000000000..07e19ca2618 --- /dev/null +++ b/homeassistant/components/aranet/__init__.py @@ -0,0 +1,56 @@ +"""The Aranet integration.""" +from __future__ import annotations + +import logging + +from aranet4.client import Aranet4Advertisement + +from homeassistant.components.bluetooth import BluetoothScanningMode +from homeassistant.components.bluetooth.models import BluetoothServiceInfoBleak +from homeassistant.components.bluetooth.passive_update_processor import ( + PassiveBluetoothProcessorCoordinator, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant + +from .const import DOMAIN + +PLATFORMS: list[Platform] = [Platform.SENSOR] + +_LOGGER = logging.getLogger(__name__) + + +def _service_info_to_adv( + service_info: BluetoothServiceInfoBleak, +) -> Aranet4Advertisement: + return Aranet4Advertisement(service_info.device, service_info.advertisement) + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Aranet from a config entry.""" + + address = entry.unique_id + assert address is not None + coordinator = hass.data.setdefault(DOMAIN, {})[ + entry.entry_id + ] = PassiveBluetoothProcessorCoordinator( + hass, + _LOGGER, + address=address, + mode=BluetoothScanningMode.PASSIVE, + update_method=_service_info_to_adv, + ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + entry.async_on_unload( + coordinator.async_start() + ) # only start after all platforms have had a chance to subscribe + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_ok diff --git a/homeassistant/components/aranet/config_flow.py b/homeassistant/components/aranet/config_flow.py new file mode 100644 index 00000000000..029ee251ae7 --- /dev/null +++ b/homeassistant/components/aranet/config_flow.py @@ -0,0 +1,123 @@ +"""Config flow for Aranet integration.""" +from __future__ import annotations + +import logging +from typing import Any + +from aranet4.client import Aranet4Advertisement, Version as AranetVersion +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.components.bluetooth import ( + BluetoothServiceInfoBleak, + async_discovered_service_info, +) +from homeassistant.const import CONF_ADDRESS +from homeassistant.data_entry_flow import AbortFlow, FlowResult + +from .const import DOMAIN + +_LOGGER = logging.getLogger(__name__) + +MIN_VERSION = AranetVersion(1, 2, 0) + + +class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for Aranet.""" + + VERSION = 1 + + def __init__(self) -> None: + """Set up a new config flow for Aranet.""" + self._discovery_info: BluetoothServiceInfoBleak | None = None + self._discovered_device: Aranet4Advertisement | None = None + self._discovered_devices: dict[str, tuple[str, Aranet4Advertisement]] = {} + + def _raise_for_advertisement_errors(self, adv: Aranet4Advertisement) -> None: + """Raise any configuration errors that apply to an advertisement.""" + # Old versions of firmware don't expose sensor data in advertisements. + if not adv.manufacturer_data or adv.manufacturer_data.version < MIN_VERSION: + raise AbortFlow("outdated_version") + + # If integrations are disabled, we get no sensor data. + if not adv.manufacturer_data.integrations: + raise AbortFlow("integrations_disabled") + + async def async_step_bluetooth( + self, discovery_info: BluetoothServiceInfoBleak + ) -> FlowResult: + """Handle the Bluetooth discovery step.""" + await self.async_set_unique_id(discovery_info.address) + self._abort_if_unique_id_configured() + adv = Aranet4Advertisement(discovery_info.device, discovery_info.advertisement) + self._raise_for_advertisement_errors(adv) + + self._discovery_info = discovery_info + self._discovered_device = adv + return await self.async_step_bluetooth_confirm() + + async def async_step_bluetooth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Confirm discovery.""" + assert self._discovered_device is not None + adv = self._discovered_device + assert self._discovery_info is not None + discovery_info = self._discovery_info + title = adv.readings.name if adv.readings else discovery_info.name + if user_input is not None: + return self.async_create_entry(title=title, data={}) + + self._set_confirm_only() + placeholders = {"name": title} + self.context["title_placeholders"] = placeholders + return self.async_show_form( + step_id="bluetooth_confirm", description_placeholders=placeholders + ) + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the user step to pick discovered device.""" + if user_input is not None: + address = user_input[CONF_ADDRESS] + adv = self._discovered_devices[address][1] + self._raise_for_advertisement_errors(adv) + + await self.async_set_unique_id(address, raise_on_progress=False) + self._abort_if_unique_id_configured() + return self.async_create_entry( + title=self._discovered_devices[address][0], data={} + ) + + current_addresses = self._async_current_ids() + for discovery_info in async_discovered_service_info(self.hass, False): + address = discovery_info.address + if address in current_addresses or address in self._discovered_devices: + continue + + adv = Aranet4Advertisement( + discovery_info.device, discovery_info.advertisement + ) + if adv.manufacturer_data: + self._discovered_devices[address] = ( + adv.readings.name if adv.readings else discovery_info.name, + adv, + ) + + if not self._discovered_devices: + return self.async_abort(reason="no_devices_found") + + return self.async_show_form( + step_id="user", + data_schema=vol.Schema( + { + vol.Required(CONF_ADDRESS): vol.In( + { + addr: dev[0] + for (addr, dev) in self._discovered_devices.items() + } + ) + } + ), + ) diff --git a/homeassistant/components/aranet/const.py b/homeassistant/components/aranet/const.py new file mode 100644 index 00000000000..056c627daa8 --- /dev/null +++ b/homeassistant/components/aranet/const.py @@ -0,0 +1,3 @@ +"""Constants for the Aranet integration.""" + +DOMAIN = "aranet" diff --git a/homeassistant/components/aranet/manifest.json b/homeassistant/components/aranet/manifest.json new file mode 100644 index 00000000000..6dc5cbe903c --- /dev/null +++ b/homeassistant/components/aranet/manifest.json @@ -0,0 +1,23 @@ +{ + "domain": "aranet", + "name": "Aranet", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/aranet", + "requirements": ["aranet4==2.1.3"], + "dependencies": ["bluetooth"], + "codeowners": ["@aschmitz"], + "iot_class": "local_push", + "integration_type": "device", + "bluetooth": [ + { + "manufacturer_id": 1794, + "service_uuid": "f0cd1400-95da-4f4b-9ac8-aa55d312af0c", + "connectable": false + }, + { + "manufacturer_id": 1794, + "service_uuid": "0000fce0-0000-1000-8000-00805f9b34fb", + "connectable": false + } + ] +} diff --git a/homeassistant/components/aranet/sensor.py b/homeassistant/components/aranet/sensor.py new file mode 100644 index 00000000000..6d8c7feb0ac --- /dev/null +++ b/homeassistant/components/aranet/sensor.py @@ -0,0 +1,169 @@ +"""Support for Aranet sensors.""" +from __future__ import annotations + +from typing import Optional, Union + +from aranet4.client import Aranet4Advertisement +from bleak.backends.device import BLEDevice + +from homeassistant import config_entries +from homeassistant.components.bluetooth.passive_update_processor import ( + PassiveBluetoothDataProcessor, + PassiveBluetoothDataUpdate, + PassiveBluetoothEntityKey, + PassiveBluetoothProcessorCoordinator, + PassiveBluetoothProcessorEntity, +) +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.const import ( + ATTR_NAME, + ATTR_SW_VERSION, + CONCENTRATION_PARTS_PER_MILLION, + PERCENTAGE, + PRESSURE_HPA, + TEMP_CELSIUS, + TIME_SECONDS, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN + +SENSOR_DESCRIPTIONS = { + "temperature": SensorEntityDescription( + key="temperature", + name="Temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=TEMP_CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + "humidity": SensorEntityDescription( + key="humidity", + name="Humidity", + device_class=SensorDeviceClass.HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + "pressure": SensorEntityDescription( + key="pressure", + name="Pressure", + device_class=SensorDeviceClass.PRESSURE, + native_unit_of_measurement=PRESSURE_HPA, + state_class=SensorStateClass.MEASUREMENT, + ), + "co2": SensorEntityDescription( + key="co2", + name="Carbon Dioxide", + device_class=SensorDeviceClass.CO2, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, + state_class=SensorStateClass.MEASUREMENT, + ), + "battery": SensorEntityDescription( + key="battery", + name="Battery", + device_class=SensorDeviceClass.BATTERY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + "interval": SensorEntityDescription( + key="update_interval", + name="Update Interval", + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=TIME_SECONDS, + state_class=SensorStateClass.MEASUREMENT, + ), +} + + +def _device_key_to_bluetooth_entity_key( + device: BLEDevice, + key: str, +) -> PassiveBluetoothEntityKey: + """Convert a device key to an entity key.""" + return PassiveBluetoothEntityKey(key, device.address) + + +def _sensor_device_info_to_hass( + adv: Aranet4Advertisement, +) -> DeviceInfo: + """Convert a sensor device info to hass device info.""" + hass_device_info = DeviceInfo({}) + if adv.readings and adv.readings.name: + hass_device_info[ATTR_NAME] = adv.readings.name + if adv.manufacturer_data: + hass_device_info[ATTR_SW_VERSION] = str(adv.manufacturer_data.version) + return hass_device_info + + +def sensor_update_to_bluetooth_data_update( + adv: Aranet4Advertisement, +) -> PassiveBluetoothDataUpdate: + """Convert a sensor update to a Bluetooth data update.""" + return PassiveBluetoothDataUpdate( + devices={adv.device.address: _sensor_device_info_to_hass(adv)}, + entity_descriptions={ + _device_key_to_bluetooth_entity_key(adv.device, key): desc + for key, desc in SENSOR_DESCRIPTIONS.items() + }, + entity_data={ + _device_key_to_bluetooth_entity_key(adv.device, key): getattr( + adv.readings, key, None + ) + for key in SENSOR_DESCRIPTIONS + }, + entity_names={ + _device_key_to_bluetooth_entity_key(adv.device, key): desc.name + for key, desc in SENSOR_DESCRIPTIONS.items() + }, + ) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: config_entries.ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Aranet sensors.""" + coordinator: PassiveBluetoothProcessorCoordinator = hass.data[DOMAIN][ + entry.entry_id + ] + processor = PassiveBluetoothDataProcessor(sensor_update_to_bluetooth_data_update) + entry.async_on_unload( + processor.async_add_entities_listener( + Aranet4BluetoothSensorEntity, async_add_entities + ) + ) + entry.async_on_unload(coordinator.async_register_processor(processor)) + + +class Aranet4BluetoothSensorEntity( + PassiveBluetoothProcessorEntity[ + PassiveBluetoothDataProcessor[Optional[Union[float, int]]] + ], + SensorEntity, +): + """Representation of an Aranet sensor.""" + + @property + def available(self) -> bool: + """Return whether the entity was available in the last update.""" + # Our superclass covers "did the device disappear entirely", but if the + # device has smart home integrations disabled, it will send BLE beacons + # without data, which we turn into Nones here. Because None is never a + # valid value for any of the Aranet sensors, that means the entity is + # actually unavailable. + return ( + super().available + and self.processor.entity_data.get(self.entity_key) is not None + ) + + @property + def native_value(self) -> int | float | None: + """Return the native value.""" + return self.processor.entity_data.get(self.entity_key) diff --git a/homeassistant/components/aranet/strings.json b/homeassistant/components/aranet/strings.json new file mode 100644 index 00000000000..1970beec210 --- /dev/null +++ b/homeassistant/components/aranet/strings.json @@ -0,0 +1,25 @@ +{ + "config": { + "step": { + "user": { + "description": "[%key:component::bluetooth::config::step::user::description%]", + "data": { + "address": "[%key:component::bluetooth::config::step::user::data::address%]" + } + }, + "bluetooth_confirm": { + "description": "[%key:component::bluetooth::config::step::bluetooth_confirm::description%]" + } + }, + "flow_title": "[%key:component::bluetooth::config::flow_title%]", + "error": { + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", + "integrations_diabled": "This device doesn't have integrations enabled. Please enable smart home integrations using the app and try again.", + "no_devices_found": "No unconfigured Aranet devices found.", + "outdated_version": "This device is using outdated firmware. Please update it to at least v1.2.0 and try again." + } + } +} diff --git a/homeassistant/components/aranet/translations/en.json b/homeassistant/components/aranet/translations/en.json new file mode 100644 index 00000000000..303fd56f1c8 --- /dev/null +++ b/homeassistant/components/aranet/translations/en.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Device is already configured", + "integrations_diabled": "This device doesn't have integrations enabled. Please enable smart home integrations using the app and try again.", + "no_devices_found": "No unconfigured Aranet4 devices found.", + "outdated_version": "This device is using outdated firmware. Please update it to at least v1.2.0 and try again." + }, + "error": { + "unknown": "Unexpected error" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Do you want to setup {name}?" + }, + "user": { + "data": { + "address": "Device" + }, + "description": "Choose a device to setup" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/generated/bluetooth.py b/homeassistant/generated/bluetooth.py index c4dd22cef17..4a0b9529ee7 100644 --- a/homeassistant/generated/bluetooth.py +++ b/homeassistant/generated/bluetooth.py @@ -9,6 +9,18 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ "domain": "airthings_ble", "manufacturer_id": 820, }, + { + "domain": "aranet", + "manufacturer_id": 1794, + "service_uuid": "f0cd1400-95da-4f4b-9ac8-aa55d312af0c", + "connectable": False, + }, + { + "domain": "aranet", + "manufacturer_id": 1794, + "service_uuid": "0000fce0-0000-1000-8000-00805f9b34fb", + "connectable": False, + }, { "domain": "bluemaestro", "manufacturer_id": 307, diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 772068401a5..5279f343691 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -31,6 +31,7 @@ FLOWS = { "anthemav", "apcupsd", "apple_tv", + "aranet", "arcam_fmj", "aseko_pool_live", "asuswrt", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 3ef8c874882..5e245ad9734 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -313,6 +313,12 @@ "config_flow": false, "iot_class": "local_polling" }, + "aranet": { + "name": "Aranet", + "integration_type": "device", + "config_flow": true, + "iot_class": "local_push" + }, "arcam_fmj": { "name": "Arcam FMJ Receivers", "integration_type": "hub", diff --git a/requirements_all.txt b/requirements_all.txt index 5286d267937..e7996679c09 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -335,6 +335,9 @@ aprslib==0.7.0 # homeassistant.components.aqualogic aqualogic==2.6 +# homeassistant.components.aranet +aranet4==2.1.3 + # homeassistant.components.arcam_fmj arcam-fmj==0.12.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 91d62550d4e..33db3ca737c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -298,6 +298,9 @@ apprise==1.1.0 # homeassistant.components.aprs aprslib==0.7.0 +# homeassistant.components.aranet +aranet4==2.1.3 + # homeassistant.components.arcam_fmj arcam-fmj==0.12.0 diff --git a/tests/components/aranet/__init__.py b/tests/components/aranet/__init__.py new file mode 100644 index 00000000000..2fe27329bda --- /dev/null +++ b/tests/components/aranet/__init__.py @@ -0,0 +1,58 @@ +"""Tests for the Aranet integration.""" + +from time import time + +from bleak.backends.device import BLEDevice +from bleak.backends.scanner import AdvertisementData + +from homeassistant.components.bluetooth import BluetoothServiceInfoBleak + + +def fake_service_info(name, service_uuid, manufacturer_data): + """Return a BluetoothServiceInfoBleak for use in testing.""" + return BluetoothServiceInfoBleak( + name=name, + address="aa:bb:cc:dd:ee:ff", + rssi=-60, + manufacturer_data=manufacturer_data, + service_data={}, + service_uuids=[service_uuid], + source="local", + connectable=False, + time=time(), + device=BLEDevice("aa:bb:cc:dd:ee:ff", name=name), + advertisement=AdvertisementData( + local_name=name, + manufacturer_data=manufacturer_data, + service_data={}, + service_uuids=[service_uuid], + rssi=-60, + tx_power=-127, + platform_data=(), + ), + ) + + +NOT_ARANET4_SERVICE_INFO = fake_service_info( + "Not it", "61DE521B-F0BF-9F44-64D4-75BBE1738105", {3234: b"\x00\x01"} +) + +OLD_FIRMWARE_SERVICE_INFO = fake_service_info( + "Aranet4 12345", + "f0cd1400-95da-4f4b-9ac8-aa55d312af0c", + {1794: b"\x21\x0a\x04\x00\x00\x00\x00\x00"}, +) + +DISABLED_INTEGRATIONS_SERVICE_INFO = fake_service_info( + "Aranet4 12345", + "0000fce0-0000-1000-8000-00805f9b34fb", + {1794: b"\x01\x00\x02\x01\x00\x00\x00\x00"}, +) + +VALID_DATA_SERVICE_INFO = fake_service_info( + "Aranet4 12345", + "0000fce0-0000-1000-8000-00805f9b34fb", + { + 1794: b'\x21\x00\x02\x01\x00\x00\x00\x01\x8a\x02\xa5\x01\xb1&"Y\x01,\x01\xe8\x00\x88' + }, +) diff --git a/tests/components/aranet/conftest.py b/tests/components/aranet/conftest.py new file mode 100644 index 00000000000..fca081d2e2a --- /dev/null +++ b/tests/components/aranet/conftest.py @@ -0,0 +1,8 @@ +"""Aranet session fixtures.""" + +import pytest + + +@pytest.fixture(autouse=True) +def mock_bluetooth(enable_bluetooth): + """Auto mock bluetooth.""" diff --git a/tests/components/aranet/test_config_flow.py b/tests/components/aranet/test_config_flow.py new file mode 100644 index 00000000000..2b4172c30cd --- /dev/null +++ b/tests/components/aranet/test_config_flow.py @@ -0,0 +1,252 @@ +"""Test the Aranet config flow.""" +from unittest.mock import patch + +from homeassistant import config_entries +from homeassistant.components.aranet.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType + +from . import ( + DISABLED_INTEGRATIONS_SERVICE_INFO, + NOT_ARANET4_SERVICE_INFO, + OLD_FIRMWARE_SERVICE_INFO, + VALID_DATA_SERVICE_INFO, +) + +from tests.common import MockConfigEntry + + +async def test_async_step_bluetooth_valid_device(hass): + """Test discovery via bluetooth with a valid device.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=VALID_DATA_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "bluetooth_confirm" + with patch("homeassistant.components.aranet.async_setup_entry", return_value=True): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={} + ) + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == "Aranet4 12345" + assert result2["data"] == {} + assert result2["result"].unique_id == "aa:bb:cc:dd:ee:ff" + + +async def test_async_step_bluetooth_not_aranet4(hass): + """Test that we reject discovery via Bluetooth for an unrelated device.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=NOT_ARANET4_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.ABORT + + +async def test_async_step_bluetooth_devices_already_setup(hass): + """Test we can't start a flow if there is already a config entry.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="aa:bb:cc:dd:ee:ff", + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=VALID_DATA_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_configured" + + +async def test_async_step_bluetooth_already_in_progress(hass): + """Test we can't start a flow for the same device twice.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=VALID_DATA_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "bluetooth_confirm" + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=VALID_DATA_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_in_progress" + + +async def test_async_step_user_takes_precedence_over_discovery(hass): + """Test manual setup takes precedence over discovery.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=VALID_DATA_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "bluetooth_confirm" + + with patch( + "homeassistant.components.aranet.config_flow.async_discovered_service_info", + return_value=[VALID_DATA_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.FORM + + with patch("homeassistant.components.aranet.async_setup_entry", return_value=True): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"address": "aa:bb:cc:dd:ee:ff"}, + ) + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == "Aranet4 12345" + assert result2["data"] == {} + assert result2["result"].unique_id == "aa:bb:cc:dd:ee:ff" + + # Verify the original one was aborted + assert not hass.config_entries.flow.async_progress(DOMAIN) + + +async def test_async_step_user_no_devices_found(hass: HomeAssistant): + """Test setup from service info cache with no devices found.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "no_devices_found" + + +async def test_async_step_user_only_other_devices_found(hass: HomeAssistant): + """Test setup from service info cache with only other devices found.""" + with patch( + "homeassistant.components.aranet.config_flow.async_discovered_service_info", + return_value=[NOT_ARANET4_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "no_devices_found" + + +async def test_async_step_user_with_found_devices(hass: HomeAssistant): + """Test setup from service info cache with devices found.""" + with patch( + "homeassistant.components.aranet.config_flow.async_discovered_service_info", + return_value=[VALID_DATA_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + with patch("homeassistant.components.aranet.async_setup_entry", return_value=True): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"address": "aa:bb:cc:dd:ee:ff"}, + ) + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == "Aranet4 12345" + assert result2["data"] == {} + assert result2["result"].unique_id == "aa:bb:cc:dd:ee:ff" + + +async def test_async_step_user_device_added_between_steps(hass: HomeAssistant): + """Test the device gets added via another flow between steps.""" + with patch( + "homeassistant.components.aranet.config_flow.async_discovered_service_info", + return_value=[VALID_DATA_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="aa:bb:cc:dd:ee:ff", + ) + entry.add_to_hass(hass) + + with patch("homeassistant.components.aranet.async_setup_entry", return_value=True): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"address": "aa:bb:cc:dd:ee:ff"}, + ) + assert result2["type"] == FlowResultType.ABORT + assert result2["reason"] == "already_configured" + + +async def test_async_step_user_with_found_devices_already_setup(hass: HomeAssistant): + """Test setup from service info cache with devices found.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="aa:bb:cc:dd:ee:ff", + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.aranet.config_flow.async_discovered_service_info", + return_value=[VALID_DATA_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "no_devices_found" + + +async def test_async_step_user_old_firmware(hass: HomeAssistant): + """Test we can't set up a device with firmware too old to report measurements.""" + with patch( + "homeassistant.components.aranet.config_flow.async_discovered_service_info", + return_value=[OLD_FIRMWARE_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + with patch("homeassistant.components.aranet.async_setup_entry", return_value=True): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"address": "aa:bb:cc:dd:ee:ff"}, + ) + assert result2["type"] == FlowResultType.ABORT + assert result2["reason"] == "outdated_version" + + +async def test_async_step_user_integrations_disabled(hass: HomeAssistant): + """Test we can't set up a device the device's integration setting disabled.""" + with patch( + "homeassistant.components.aranet.config_flow.async_discovered_service_info", + return_value=[DISABLED_INTEGRATIONS_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + with patch("homeassistant.components.aranet.async_setup_entry", return_value=True): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"address": "aa:bb:cc:dd:ee:ff"}, + ) + assert result2["type"] == FlowResultType.ABORT + assert result2["reason"] == "integrations_disabled" diff --git a/tests/components/aranet/test_sensor.py b/tests/components/aranet/test_sensor.py new file mode 100644 index 00000000000..a0edcca4803 --- /dev/null +++ b/tests/components/aranet/test_sensor.py @@ -0,0 +1,111 @@ +"""Test the Aranet sensors.""" + + +from homeassistant.components.aranet.const import DOMAIN +from homeassistant.components.sensor import ATTR_STATE_CLASS +from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT + +from . import DISABLED_INTEGRATIONS_SERVICE_INFO, VALID_DATA_SERVICE_INFO + +from tests.common import MockConfigEntry +from tests.components.bluetooth import inject_bluetooth_service_info + + +async def test_sensors(hass): + """Test setting up creates the sensors.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="aa:bb:cc:dd:ee:ff", + ) + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.states.async_all("sensor")) == 0 + inject_bluetooth_service_info(hass, VALID_DATA_SERVICE_INFO) + await hass.async_block_till_done() + assert len(hass.states.async_all("sensor")) == 6 + + batt_sensor = hass.states.get("sensor.aranet4_12345_battery") + batt_sensor_attrs = batt_sensor.attributes + assert batt_sensor.state == "89" + assert batt_sensor_attrs[ATTR_FRIENDLY_NAME] == "Aranet4 12345 Battery" + assert batt_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "%" + assert batt_sensor_attrs[ATTR_STATE_CLASS] == "measurement" + + co2_sensor = hass.states.get("sensor.aranet4_12345_carbon_dioxide") + co2_sensor_attrs = co2_sensor.attributes + assert co2_sensor.state == "650" + assert co2_sensor_attrs[ATTR_FRIENDLY_NAME] == "Aranet4 12345 Carbon Dioxide" + assert co2_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "ppm" + assert co2_sensor_attrs[ATTR_STATE_CLASS] == "measurement" + + humid_sensor = hass.states.get("sensor.aranet4_12345_humidity") + humid_sensor_attrs = humid_sensor.attributes + assert humid_sensor.state == "34" + assert humid_sensor_attrs[ATTR_FRIENDLY_NAME] == "Aranet4 12345 Humidity" + assert humid_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "%" + assert humid_sensor_attrs[ATTR_STATE_CLASS] == "measurement" + + temp_sensor = hass.states.get("sensor.aranet4_12345_temperature") + temp_sensor_attrs = temp_sensor.attributes + assert temp_sensor.state == "21.1" + assert temp_sensor_attrs[ATTR_FRIENDLY_NAME] == "Aranet4 12345 Temperature" + assert temp_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "°C" + assert temp_sensor_attrs[ATTR_STATE_CLASS] == "measurement" + + press_sensor = hass.states.get("sensor.aranet4_12345_pressure") + press_sensor_attrs = press_sensor.attributes + assert press_sensor.state == "990.5" + assert press_sensor_attrs[ATTR_FRIENDLY_NAME] == "Aranet4 12345 Pressure" + assert press_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "hPa" + assert press_sensor_attrs[ATTR_STATE_CLASS] == "measurement" + + interval_sensor = hass.states.get("sensor.aranet4_12345_update_interval") + interval_sensor_attrs = interval_sensor.attributes + assert interval_sensor.state == "300" + assert interval_sensor_attrs[ATTR_FRIENDLY_NAME] == "Aranet4 12345 Update Interval" + assert interval_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "s" + assert interval_sensor_attrs[ATTR_STATE_CLASS] == "measurement" + + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + + +async def test_smart_home_integration_disabled(hass): + """Test disabling smart home integration marks entities as unavailable.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="aa:bb:cc:dd:ee:ff", + ) + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.states.async_all("sensor")) == 0 + inject_bluetooth_service_info(hass, DISABLED_INTEGRATIONS_SERVICE_INFO) + await hass.async_block_till_done() + assert len(hass.states.async_all("sensor")) == 6 + + batt_sensor = hass.states.get("sensor.aranet4_12345_battery") + assert batt_sensor.state == "unavailable" + + co2_sensor = hass.states.get("sensor.aranet4_12345_carbon_dioxide") + assert co2_sensor.state == "unavailable" + + humid_sensor = hass.states.get("sensor.aranet4_12345_humidity") + assert humid_sensor.state == "unavailable" + + temp_sensor = hass.states.get("sensor.aranet4_12345_temperature") + assert temp_sensor.state == "unavailable" + + press_sensor = hass.states.get("sensor.aranet4_12345_pressure") + assert press_sensor.state == "unavailable" + + interval_sensor = hass.states.get("sensor.aranet4_12345_update_interval") + assert interval_sensor.state == "unavailable" + + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() From 1bdd8fff445e67fa50157c5087b20cd00277fc76 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 31 Oct 2022 00:28:38 -0500 Subject: [PATCH 0106/1033] Bump bleak-retry-connector to 2.8.1 (#81285) * Bump bleak-retry-connector to 2.8.1 reduces logging now that we have found the problem with esphome devices not disconnecting ble devices after timeout changelog: https://github.com/Bluetooth-Devices/bleak-retry-connector/compare/v2.8.0...v2.8.1 * empty --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 660345606c8..091962fbc83 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.1", - "bleak-retry-connector==2.8.0", + "bleak-retry-connector==2.8.1", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", "dbus-fast==1.59.1" diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 994a8d44019..914731a8164 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.8.0 +bleak-retry-connector==2.8.1 bleak==0.19.1 bluetooth-adapters==0.6.0 bluetooth-auto-recovery==0.3.6 diff --git a/requirements_all.txt b/requirements_all.txt index e7996679c09..192db4138a6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -416,7 +416,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.0 +bleak-retry-connector==2.8.1 # homeassistant.components.bluetooth bleak==0.19.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 33db3ca737c..cc5431c3d81 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -340,7 +340,7 @@ bellows==0.34.2 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.0 +bleak-retry-connector==2.8.1 # homeassistant.components.bluetooth bleak==0.19.1 From 8db7afb2e0106f1f8a1f085d45d143024f77daf1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 31 Oct 2022 00:31:37 -0500 Subject: [PATCH 0107/1033] Include esphome device name in BLE logs (#81284) * Include esphome device name in BLE logs This makes it easier to debug what is going on when there are multiple esphome proxies * revert unintended change --- homeassistant/components/esphome/__init__.py | 2 + .../components/esphome/bluetooth/__init__.py | 8 +-- .../components/esphome/bluetooth/client.py | 53 +++++++++++++++---- .../components/esphome/entry_data.py | 17 ++++-- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index a5428f7d6c5..23b6a6550e4 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -249,6 +249,8 @@ async def async_setup_entry( # noqa: C901 async def on_disconnect() -> None: """Run disconnect callbacks on API disconnect.""" + name = entry_data.device_info.name if entry_data.device_info else host + _LOGGER.debug("%s: %s disconnected, running disconnected callbacks", name, host) for disconnect_cb in entry_data.disconnect_callbacks: disconnect_cb() entry_data.disconnect_callbacks = [] diff --git a/homeassistant/components/esphome/bluetooth/__init__.py b/homeassistant/components/esphome/bluetooth/__init__.py index b4d5fdbd04d..b5be5362474 100644 --- a/homeassistant/components/esphome/bluetooth/__init__.py +++ b/homeassistant/components/esphome/bluetooth/__init__.py @@ -30,13 +30,15 @@ def _async_can_connect_factory( @hass_callback def _async_can_connect() -> bool: """Check if a given source can make another connection.""" + can_connect = bool(entry_data.available and entry_data.ble_connections_free) _LOGGER.debug( - "Checking if %s can connect, available=%s, ble_connections_free=%s", + "%s: Checking can connect, available=%s, ble_connections_free=%s result=%s", source, entry_data.available, entry_data.ble_connections_free, + can_connect, ) - return bool(entry_data.available and entry_data.ble_connections_free) + return can_connect return _async_can_connect @@ -55,7 +57,7 @@ async def async_connect_scanner( version = entry_data.device_info.bluetooth_proxy_version connectable = version >= 2 _LOGGER.debug( - "Connecting scanner for %s, version=%s, connectable=%s", + "%s: Connecting scanner version=%s, connectable=%s", source, version, connectable, diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index 68f1788afdb..5f20a73f4d6 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -61,7 +61,7 @@ def verify_connected(func: _WrapFuncType) -> _WrapFuncType: if disconnected_event.is_set(): task.cancel() raise BleakError( - f"{self._ble_device.name} ({self._ble_device.address}): " # pylint: disable=protected-access + f"{self._source}: {self._ble_device.name} - {self._ble_device.address}: " # pylint: disable=protected-access "Disconnected during operation" ) return next(iter(done)).result() @@ -120,7 +120,10 @@ class ESPHomeClient(BaseBleakClient): self._cancel_connection_state() except (AssertionError, ValueError) as ex: _LOGGER.debug( - "Failed to unsubscribe from connection state (likely connection dropped): %s", + "%s: %s - %s: Failed to unsubscribe from connection state (likely connection dropped): %s", + self._source, + self._ble_device.name, + self._ble_device.address, ex, ) self._cancel_connection_state = None @@ -134,13 +137,23 @@ class ESPHomeClient(BaseBleakClient): self._disconnected_event.set() self._disconnected_event = None if was_connected: - _LOGGER.debug("%s: BLE device disconnected", self._source) + _LOGGER.debug( + "%s: %s - %s: BLE device disconnected", + self._source, + self._ble_device.name, + self._ble_device.address, + ) self._async_call_bleak_disconnected_callback() self._unsubscribe_connection_state() def _async_esp_disconnected(self) -> None: """Handle the esp32 client disconnecting from hass.""" - _LOGGER.debug("%s: ESP device disconnected", self._source) + _LOGGER.debug( + "%s: %s - %s: ESP device disconnected", + self._source, + self._ble_device.name, + self._ble_device.address, + ) self.entry_data.disconnect_callbacks.remove(self._async_esp_disconnected) self._async_ble_device_disconnected() @@ -170,7 +183,10 @@ class ESPHomeClient(BaseBleakClient): ) -> None: """Handle a connect or disconnect.""" _LOGGER.debug( - "Connection state changed: connected=%s mtu=%s error=%s", + "%s: %s - %s: Connection state changed to connected=%s mtu=%s error=%s", + self._source, + self._ble_device.name, + self._ble_device.address, connected, mtu, error, @@ -203,6 +219,12 @@ class ESPHomeClient(BaseBleakClient): connected_future.set_exception(BleakError("Disconnected")) return + _LOGGER.debug( + "%s: %s - %s: connected, registering for disconnected callbacks", + self._source, + self._ble_device.name, + self._ble_device.address, + ) self.entry_data.disconnect_callbacks.append(self._async_esp_disconnected) connected_future.set_result(connected) @@ -230,7 +252,10 @@ class ESPHomeClient(BaseBleakClient): if self.entry_data.ble_connections_free: return _LOGGER.debug( - "%s: Out of connection slots, waiting for a free one", self._source + "%s: %s - %s: Out of connection slots, waiting for a free one", + self._source, + self._ble_device.name, + self._ble_device.address, ) async with async_timeout.timeout(timeout): await self.entry_data.wait_for_ble_connections_free() @@ -272,20 +297,29 @@ class ESPHomeClient(BaseBleakClient): cached_services := entry_data.get_gatt_services_cache(address_as_int) ): _LOGGER.debug( - "Cached services hit for %s - %s", + "%s: %s - %s: Cached services hit", + self._source, self._ble_device.name, self._ble_device.address, ) self.services = cached_services return self.services _LOGGER.debug( - "Cached services miss for %s - %s", + "%s: %s - %s: Cached services miss", + self._source, self._ble_device.name, self._ble_device.address, ) esphome_services = await self._client.bluetooth_gatt_get_services( address_as_int ) + _LOGGER.debug( + "%s: %s - %s: Got services: %s", + self._source, + self._ble_device.name, + self._ble_device.address, + esphome_services, + ) max_write_without_response = self.mtu_size - GATT_HEADER_SIZE services = BleakGATTServiceCollection() # type: ignore[no-untyped-call] for service in esphome_services.services: @@ -309,7 +343,8 @@ class ESPHomeClient(BaseBleakClient): ) self.services = services _LOGGER.debug( - "Cached services saved for %s - %s", + "%s: %s - %s: Cached services saved", + self._source, self._ble_device.name, self._ble_device.address, ) diff --git a/homeassistant/components/esphome/entry_data.py b/homeassistant/components/esphome/entry_data.py index 5d474b0fb15..faa9074b880 100644 --- a/homeassistant/components/esphome/entry_data.py +++ b/homeassistant/components/esphome/entry_data.py @@ -99,6 +99,11 @@ class RuntimeEntryData: default_factory=lambda: LRU(MAX_CACHED_SERVICES) # type: ignore[no-any-return] ) + @property + def name(self) -> str: + """Return the name of the device.""" + return self.device_info.name if self.device_info else self.entry_id + def get_gatt_services_cache( self, address: int ) -> BleakGATTServiceCollection | None: @@ -114,8 +119,13 @@ class RuntimeEntryData: @callback def async_update_ble_connection_limits(self, free: int, limit: int) -> None: """Update the BLE connection limits.""" - name = self.device_info.name if self.device_info else self.entry_id - _LOGGER.debug("%s: BLE connection limits: %s/%s", name, free, limit) + _LOGGER.debug( + "%s: BLE connection limits: used=%s free=%s limit=%s", + self.name, + limit - free, + free, + limit, + ) self.ble_connections_free = free self.ble_connections_limit = limit if free: @@ -186,7 +196,8 @@ class RuntimeEntryData: subscription_key = (type(state), state.key) self.state[type(state)][state.key] = state _LOGGER.debug( - "Dispatching update with key %s: %s", + "%s: dispatching update with key %s: %s", + self.name, subscription_key, state, ) From 47a0f89adabf27c76cfffe4ff9dce77fe6457201 Mon Sep 17 00:00:00 2001 From: Chris Talkington Date: Mon, 31 Oct 2022 03:23:05 -0500 Subject: [PATCH 0108/1033] Bump pyipp to 0.12.1 (#81287) bump pyipp to 0.12.1 --- homeassistant/components/ipp/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ipp/manifest.json b/homeassistant/components/ipp/manifest.json index aadfdc8feea..b673a2d5a6d 100644 --- a/homeassistant/components/ipp/manifest.json +++ b/homeassistant/components/ipp/manifest.json @@ -3,7 +3,7 @@ "name": "Internet Printing Protocol (IPP)", "documentation": "https://www.home-assistant.io/integrations/ipp", "integration_type": "device", - "requirements": ["pyipp==0.12.0"], + "requirements": ["pyipp==0.12.1"], "codeowners": ["@ctalkington"], "config_flow": true, "quality_scale": "platinum", diff --git a/requirements_all.txt b/requirements_all.txt index 192db4138a6..93cee8ab028 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1634,7 +1634,7 @@ pyintesishome==1.8.0 pyipma==3.0.5 # homeassistant.components.ipp -pyipp==0.12.0 +pyipp==0.12.1 # homeassistant.components.iqvia pyiqvia==2022.04.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cc5431c3d81..ae0bbe72346 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1150,7 +1150,7 @@ pyinsteon==1.2.0 pyipma==3.0.5 # homeassistant.components.ipp -pyipp==0.12.0 +pyipp==0.12.1 # homeassistant.components.iqvia pyiqvia==2022.04.0 From ae2419b56912abb3034dad5ca824b99b8a1adcdb Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Mon, 31 Oct 2022 10:22:45 +0100 Subject: [PATCH 0109/1033] Add support for PMSx003 sensors in NAM integration (#81289) * Add support for PMSx003 * Organize the order of tests --- homeassistant/components/nam/const.py | 6 ++ homeassistant/components/nam/sensor.py | 37 ++++++++ tests/components/nam/test_sensor.py | 121 +++++++++++++++++++------ 3 files changed, 137 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/nam/const.py b/homeassistant/components/nam/const.py index 2b6a74383b5..5e18b94745c 100644 --- a/homeassistant/components/nam/const.py +++ b/homeassistant/components/nam/const.py @@ -22,6 +22,12 @@ ATTR_DHT22_TEMPERATURE: Final = "dht22_temperature" ATTR_HECA_HUMIDITY: Final = "heca_humidity" ATTR_HECA_TEMPERATURE: Final = "heca_temperature" ATTR_MHZ14A_CARBON_DIOXIDE: Final = "mhz14a_carbon_dioxide" +ATTR_PMSX003: Final = "pms" +ATTR_PMSX003_CAQI: Final = f"{ATTR_PMSX003}{SUFFIX_CAQI}" +ATTR_PMSX003_CAQI_LEVEL: Final = f"{ATTR_PMSX003}{SUFFIX_CAQI}_level" +ATTR_PMSX003_P0: Final = f"{ATTR_PMSX003}{SUFFIX_P0}" +ATTR_PMSX003_P1: Final = f"{ATTR_PMSX003}{SUFFIX_P1}" +ATTR_PMSX003_P2: Final = f"{ATTR_PMSX003}{SUFFIX_P2}" ATTR_SDS011: Final = "sds011" ATTR_SDS011_CAQI: Final = f"{ATTR_SDS011}{SUFFIX_CAQI}" ATTR_SDS011_CAQI_LEVEL: Final = f"{ATTR_SDS011}{SUFFIX_CAQI}_level" diff --git a/homeassistant/components/nam/sensor.py b/homeassistant/components/nam/sensor.py index 01107baf31b..ce3fdbf16a8 100644 --- a/homeassistant/components/nam/sensor.py +++ b/homeassistant/components/nam/sensor.py @@ -43,6 +43,11 @@ from .const import ( ATTR_HECA_HUMIDITY, ATTR_HECA_TEMPERATURE, ATTR_MHZ14A_CARBON_DIOXIDE, + ATTR_PMSX003_CAQI, + ATTR_PMSX003_CAQI_LEVEL, + ATTR_PMSX003_P0, + ATTR_PMSX003_P1, + ATTR_PMSX003_P2, ATTR_SDS011_CAQI, ATTR_SDS011_CAQI_LEVEL, ATTR_SDS011_P1, @@ -136,6 +141,38 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( device_class=SensorDeviceClass.CO2, state_class=SensorStateClass.MEASUREMENT, ), + SensorEntityDescription( + key=ATTR_PMSX003_CAQI, + name="PMSx003 CAQI", + icon="mdi:air-filter", + ), + SensorEntityDescription( + key=ATTR_PMSX003_CAQI_LEVEL, + name="PMSx003 CAQI level", + icon="mdi:air-filter", + device_class="nam__caqi_level", + ), + SensorEntityDescription( + key=ATTR_PMSX003_P0, + name="PMSx003 particulate matter 1.0", + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + device_class=SensorDeviceClass.PM1, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_PMSX003_P1, + name="PMSx003 particulate matter 10", + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + device_class=SensorDeviceClass.PM10, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_PMSX003_P2, + name="PMSx003 particulate matter 2.5", + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + device_class=SensorDeviceClass.PM25, + state_class=SensorStateClass.MEASUREMENT, + ), SensorEntityDescription( key=ATTR_SDS011_CAQI, name="SDS011 CAQI", diff --git a/tests/components/nam/test_sensor.py b/tests/components/nam/test_sensor.py index bee4c515cd0..dc9e9a76d76 100644 --- a/tests/components/nam/test_sensor.py +++ b/tests/components/nam/test_sensor.py @@ -228,6 +228,73 @@ async def test_sensor(hass): assert entry assert entry.unique_id == "aa:bb:cc:dd:ee:ff-uptime" + state = hass.states.get("sensor.nettigo_air_monitor_pmsx003_caqi_level") + assert state + assert state.state == "very low" + assert state.attributes.get(ATTR_DEVICE_CLASS) == "nam__caqi_level" + assert state.attributes.get(ATTR_ICON) == "mdi:air-filter" + + entry = registry.async_get("sensor.nettigo_air_monitor_pmsx003_caqi_level") + assert entry + assert entry.unique_id == "aa:bb:cc:dd:ee:ff-pms_caqi_level" + + state = hass.states.get("sensor.nettigo_air_monitor_pmsx003_caqi") + assert state + assert state.state == "19" + assert state.attributes.get(ATTR_ICON) == "mdi:air-filter" + + entry = registry.async_get("sensor.nettigo_air_monitor_pmsx003_caqi") + assert entry + assert entry.unique_id == "aa:bb:cc:dd:ee:ff-pms_caqi" + + state = hass.states.get("sensor.nettigo_air_monitor_pmsx003_particulate_matter_10") + assert state + assert state.state == "10" + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PM10 + assert state.attributes.get(ATTR_STATE_CLASS) is SensorStateClass.MEASUREMENT + assert ( + state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) + == CONCENTRATION_MICROGRAMS_PER_CUBIC_METER + ) + + entry = registry.async_get( + "sensor.nettigo_air_monitor_pmsx003_particulate_matter_10" + ) + assert entry + assert entry.unique_id == "aa:bb:cc:dd:ee:ff-pms_p1" + + state = hass.states.get("sensor.nettigo_air_monitor_pmsx003_particulate_matter_2_5") + assert state + assert state.state == "11" + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PM25 + assert state.attributes.get(ATTR_STATE_CLASS) is SensorStateClass.MEASUREMENT + assert ( + state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) + == CONCENTRATION_MICROGRAMS_PER_CUBIC_METER + ) + + entry = registry.async_get( + "sensor.nettigo_air_monitor_pmsx003_particulate_matter_2_5" + ) + assert entry + assert entry.unique_id == "aa:bb:cc:dd:ee:ff-pms_p2" + + state = hass.states.get("sensor.nettigo_air_monitor_pmsx003_particulate_matter_1_0") + assert state + assert state.state == "6" + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PM1 + assert state.attributes.get(ATTR_STATE_CLASS) is SensorStateClass.MEASUREMENT + assert ( + state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) + == CONCENTRATION_MICROGRAMS_PER_CUBIC_METER + ) + + entry = registry.async_get( + "sensor.nettigo_air_monitor_pmsx003_particulate_matter_1_0" + ) + assert entry + assert entry.unique_id == "aa:bb:cc:dd:ee:ff-pms_p0" + state = hass.states.get("sensor.nettigo_air_monitor_sds011_particulate_matter_10") assert state assert state.state == "19" @@ -238,18 +305,20 @@ async def test_sensor(hass): == CONCENTRATION_MICROGRAMS_PER_CUBIC_METER ) - entry = registry.async_get("sensor.nettigo_air_monitor_sds011_caqi") + entry = registry.async_get( + "sensor.nettigo_air_monitor_sds011_particulate_matter_10" + ) assert entry - assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sds011_caqi" + assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sds011_p1" state = hass.states.get("sensor.nettigo_air_monitor_sds011_caqi") assert state assert state.state == "19" assert state.attributes.get(ATTR_ICON) == "mdi:air-filter" - entry = registry.async_get("sensor.nettigo_air_monitor_sds011_caqi_level") + entry = registry.async_get("sensor.nettigo_air_monitor_sds011_caqi") assert entry - assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sds011_caqi_level" + assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sds011_caqi" state = hass.states.get("sensor.nettigo_air_monitor_sds011_caqi_level") assert state @@ -257,11 +326,9 @@ async def test_sensor(hass): assert state.attributes.get(ATTR_DEVICE_CLASS) == "nam__caqi_level" assert state.attributes.get(ATTR_ICON) == "mdi:air-filter" - entry = registry.async_get( - "sensor.nettigo_air_monitor_sds011_particulate_matter_10" - ) + entry = registry.async_get("sensor.nettigo_air_monitor_sds011_caqi_level") assert entry - assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sds011_p1" + assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sds011_caqi_level" state = hass.states.get("sensor.nettigo_air_monitor_sds011_particulate_matter_2_5") assert state @@ -279,6 +346,25 @@ async def test_sensor(hass): assert entry assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sds011_p2" + state = hass.states.get("sensor.nettigo_air_monitor_sps30_caqi") + assert state + assert state.state == "54" + assert state.attributes.get(ATTR_ICON) == "mdi:air-filter" + + entry = registry.async_get("sensor.nettigo_air_monitor_sps30_caqi") + assert entry + assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sps30_caqi" + + state = hass.states.get("sensor.nettigo_air_monitor_sps30_caqi_level") + assert state + assert state.state == "medium" + assert state.attributes.get(ATTR_DEVICE_CLASS) == "nam__caqi_level" + assert state.attributes.get(ATTR_ICON) == "mdi:air-filter" + + entry = registry.async_get("sensor.nettigo_air_monitor_sps30_caqi_level") + assert entry + assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sps30_caqi_level" + state = hass.states.get("sensor.nettigo_air_monitor_sps30_particulate_matter_1_0") assert state assert state.state == "31" @@ -289,25 +375,6 @@ async def test_sensor(hass): == CONCENTRATION_MICROGRAMS_PER_CUBIC_METER ) - entry = registry.async_get("sensor.nettigo_air_monitor_sps30_caqi") - assert entry - assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sps30_caqi" - - state = hass.states.get("sensor.nettigo_air_monitor_sps30_caqi") - assert state - assert state.state == "54" - assert state.attributes.get(ATTR_ICON) == "mdi:air-filter" - - entry = registry.async_get("sensor.nettigo_air_monitor_sps30_caqi_level") - assert entry - assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sps30_caqi_level" - - state = hass.states.get("sensor.nettigo_air_monitor_sps30_caqi_level") - assert state - assert state.state == "medium" - assert state.attributes.get(ATTR_DEVICE_CLASS) == "nam__caqi_level" - assert state.attributes.get(ATTR_ICON) == "mdi:air-filter" - entry = registry.async_get( "sensor.nettigo_air_monitor_sps30_particulate_matter_1_0" ) From 927b8b2eef1435c78e710dd509b9703e93b9d6b4 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Mon, 31 Oct 2022 11:09:15 +0100 Subject: [PATCH 0110/1033] Bump pyatmo to 7.3.0 (#81290) * Bump pyatmo to 7.3.0 * Update test fixture data and tests --- .../components/netatmo/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../netatmo/fixtures/getstationsdata.json | 222 ++++-- .../netatmo/fixtures/homesdata.json | 252 +++---- .../homestatus_91763b24c43d3e344f424e8b.json | 676 ++---------------- .../homestatus_91763b24c43d3e344f424e8c.json | 18 +- tests/components/netatmo/test_camera.py | 21 +- tests/components/netatmo/test_climate.py | 18 +- tests/components/netatmo/test_light.py | 12 +- tests/components/netatmo/test_sensor.py | 34 +- 11 files changed, 381 insertions(+), 878 deletions(-) diff --git a/homeassistant/components/netatmo/manifest.json b/homeassistant/components/netatmo/manifest.json index 8beb7bc521a..436b6329c1d 100644 --- a/homeassistant/components/netatmo/manifest.json +++ b/homeassistant/components/netatmo/manifest.json @@ -3,7 +3,7 @@ "name": "Netatmo", "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/netatmo", - "requirements": ["pyatmo==7.2.0"], + "requirements": ["pyatmo==7.3.0"], "after_dependencies": ["cloud", "media_source"], "dependencies": ["application_credentials", "webhook"], "codeowners": ["@cgtobi"], diff --git a/requirements_all.txt b/requirements_all.txt index 93cee8ab028..eedb8fd71d5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1445,7 +1445,7 @@ pyalmond==0.0.2 pyatag==0.3.5.3 # homeassistant.components.netatmo -pyatmo==7.2.0 +pyatmo==7.3.0 # homeassistant.components.atome pyatome==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ae0bbe72346..ac8162f39bf 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1033,7 +1033,7 @@ pyalmond==0.0.2 pyatag==0.3.5.3 # homeassistant.components.netatmo -pyatmo==7.2.0 +pyatmo==7.3.0 # homeassistant.components.apple_tv pyatv==0.10.3 diff --git a/tests/components/netatmo/fixtures/getstationsdata.json b/tests/components/netatmo/fixtures/getstationsdata.json index 822a4c11a50..10c3ca85e06 100644 --- a/tests/components/netatmo/fixtures/getstationsdata.json +++ b/tests/components/netatmo/fixtures/getstationsdata.json @@ -114,7 +114,7 @@ "battery_percent": 79 }, { - "_id": "12:34:56:03:1b:e4", + "_id": "12:34:56:03:1b:e5", "type": "NAModule2", "module_name": "Garden", "data_type": ["Wind"], @@ -430,63 +430,203 @@ "modules": [] }, { - "_id": "12:34:56:58:c8:54", - "date_setup": 1605594014, - "last_setup": 1605594014, + "_id": "12:34:56:80:bb:26", + "station_name": "MYHOME (Palier)", + "date_setup": 1558709904, + "last_setup": 1558709904, "type": "NAMain", - "last_status_store": 1605878352, - "firmware": 178, - "wifi_status": 47, + "last_status_store": 1644582700, + "module_name": "Palier", + "firmware": 181, + "last_upgrade": 1558709906, + "wifi_status": 57, "reachable": true, "co2_calibrating": false, "data_type": ["Temperature", "CO2", "Humidity", "Noise", "Pressure"], "place": { - "altitude": 65, - "city": "Njurunda District", - "country": "SE", - "timezone": "Europe/Stockholm", - "location": [17.123456, 62.123456] + "altitude": 329, + "city": "Someplace", + "country": "FR", + "timezone": "Europe/Paris", + "location": [6.1234567, 46.123456] }, - "station_name": "Njurunda (Indoor)", - "home_id": "5fb36b9ec68fd10c6467ca65", - "home_name": "Njurunda", + "home_id": "91763b24c43d3e344f424e8b", + "home_name": "MYHOME", "dashboard_data": { - "time_utc": 1605878349, - "Temperature": 19.7, - "CO2": 993, - "Humidity": 40, - "Noise": 40, - "Pressure": 1015.6, - "AbsolutePressure": 1007.8, - "min_temp": 19.7, - "max_temp": 20.4, - "date_max_temp": 1605826917, - "date_min_temp": 1605873207, + "time_utc": 1644582694, + "Temperature": 21.1, + "CO2": 1339, + "Humidity": 45, + "Noise": 35, + "Pressure": 1026.8, + "AbsolutePressure": 974.5, + "min_temp": 21, + "max_temp": 21.8, + "date_max_temp": 1644534255, + "date_min_temp": 1644550420, "temp_trend": "stable", "pressure_trend": "up" }, "modules": [ { - "_id": "12:34:56:58:e6:38", + "_id": "12:34:56:80:1c:42", "type": "NAModule1", - "last_setup": 1605594034, + "module_name": "Outdoor", + "last_setup": 1558709954, "data_type": ["Temperature", "Humidity"], - "battery_percent": 100, + "battery_percent": 27, "reachable": true, "firmware": 50, - "last_message": 1605878347, - "last_seen": 1605878328, - "rf_status": 62, - "battery_vp": 6198, + "last_message": 1644582699, + "last_seen": 1644582699, + "rf_status": 68, + "battery_vp": 4678, "dashboard_data": { - "time_utc": 1605878328, - "Temperature": 0.6, - "Humidity": 77, - "min_temp": -2.1, - "max_temp": 1.5, - "date_max_temp": 1605865920, - "date_min_temp": 1605826904, - "temp_trend": "down" + "time_utc": 1644582648, + "Temperature": 9.4, + "Humidity": 57, + "min_temp": 6.7, + "max_temp": 9.8, + "date_max_temp": 1644534223, + "date_min_temp": 1644569369, + "temp_trend": "up" + } + }, + { + "_id": "12:34:56:80:c1:ea", + "type": "NAModule3", + "module_name": "Rain", + "last_setup": 1563734531, + "data_type": ["Rain"], + "battery_percent": 21, + "reachable": true, + "firmware": 12, + "last_message": 1644582699, + "last_seen": 1644582699, + "rf_status": 79, + "battery_vp": 4256, + "dashboard_data": { + "time_utc": 1644582686, + "Rain": 3.7, + "sum_rain_1": 0, + "sum_rain_24": 6.9 + } + }, + { + "_id": "12:34:56:80:44:92", + "type": "NAModule4", + "module_name": "Bedroom", + "last_setup": 1575915890, + "data_type": ["Temperature", "CO2", "Humidity"], + "battery_percent": 28, + "reachable": true, + "firmware": 51, + "last_message": 1644582699, + "last_seen": 1644582654, + "rf_status": 67, + "battery_vp": 4695, + "dashboard_data": { + "time_utc": 1644582654, + "Temperature": 19.3, + "CO2": 1076, + "Humidity": 53, + "min_temp": 19.2, + "max_temp": 19.7, + "date_max_temp": 1644534243, + "date_min_temp": 1644553418, + "temp_trend": "stable" + } + }, + { + "_id": "12:34:56:80:7e:18", + "type": "NAModule4", + "module_name": "Bathroom", + "last_setup": 1575915955, + "data_type": ["Temperature", "CO2", "Humidity"], + "battery_percent": 55, + "reachable": true, + "firmware": 51, + "last_message": 1644582699, + "last_seen": 1644582654, + "rf_status": 59, + "battery_vp": 5184, + "dashboard_data": { + "time_utc": 1644582654, + "Temperature": 19.4, + "CO2": 1930, + "Humidity": 55, + "min_temp": 19.4, + "max_temp": 21.8, + "date_max_temp": 1644534224, + "date_min_temp": 1644582039, + "temp_trend": "stable" + } + }, + { + "_id": "12:34:56:03:1b:e4", + "type": "NAModule2", + "module_name": "Garden", + "data_type": ["Wind"], + "last_setup": 1549193862, + "reachable": true, + "dashboard_data": { + "time_utc": 1559413170, + "WindStrength": 4, + "WindAngle": 217, + "GustStrength": 9, + "GustAngle": 206, + "max_wind_str": 21, + "max_wind_angle": 217, + "date_max_wind_str": 1559386669 + }, + "firmware": 19, + "last_message": 1559413177, + "last_seen": 1559413177, + "rf_status": 59, + "battery_vp": 5689, + "battery_percent": 85 + } + ] + }, + { + "_id": "00:11:22:2c:be:c8", + "station_name": "Zuhause (Kinderzimmer)", + "type": "NAMain", + "last_status_store": 1649146022, + "reachable": true, + "favorite": true, + "data_type": ["Pressure"], + "place": { + "altitude": 127, + "city": "Wiesbaden", + "country": "DE", + "timezone": "Europe/Berlin", + "location": [8.238054275512695, 50.07585525512695] + }, + "read_only": true, + "dashboard_data": { + "time_utc": 1649146022, + "Pressure": 1015.6, + "AbsolutePressure": 1000.4, + "pressure_trend": "stable" + }, + "modules": [ + { + "_id": "00:11:22:2c:ce:b6", + "type": "NAModule1", + "data_type": ["Temperature", "Humidity"], + "reachable": true, + "last_message": 1649146022, + "last_seen": 1649145996, + "dashboard_data": { + "time_utc": 1649145996, + "Temperature": 7.8, + "Humidity": 87, + "min_temp": 6.5, + "max_temp": 7.8, + "date_max_temp": 1649145996, + "date_min_temp": 1649118465, + "temp_trend": "up" } } ] diff --git a/tests/components/netatmo/fixtures/homesdata.json b/tests/components/netatmo/fixtures/homesdata.json index 93c04388f4c..6b24a7f8f9d 100644 --- a/tests/components/netatmo/fixtures/homesdata.json +++ b/tests/components/netatmo/fixtures/homesdata.json @@ -23,7 +23,6 @@ "12:34:56:00:f1:62", "12:34:56:10:f1:66", "12:34:56:00:e3:9b", - "12:34:56:00:86:99", "0009999992" ] }, @@ -39,12 +38,6 @@ "type": "kitchen", "module_ids": ["12:34:56:03:a0:ac"] }, - { - "id": "2940411588", - "name": "Child", - "type": "custom", - "module_ids": ["12:34:56:26:cc:01"] - }, { "id": "222452125", "name": "Bureau", @@ -76,6 +69,12 @@ "name": "Corridor", "type": "corridor", "module_ids": ["10:20:30:bd:b8:1e"] + }, + { + "id": "100007520", + "name": "Toilettes", + "type": "toilets", + "module_ids": ["00:11:22:33:00:11:45:fe"] } ], "modules": [ @@ -120,15 +119,29 @@ "name": "Hall", "setup_date": 1544828430, "room_id": "3688132631", - "reachable": true, "modules_bridged": ["12:34:56:00:86:99", "12:34:56:00:e3:9b"] }, { - "id": "12:34:56:00:a5:a4", + "id": "12:34:56:10:f1:66", + "type": "NDB", + "name": "Netatmo-Doorbell", + "setup_date": 1602691361, + "room_id": "3688132631", + "reachable": true, + "hk_device_id": "123456007df1", + "customer_id": "1000010", + "network_lock": false, + "quick_display_zone": 62 + }, + { + "id": "12:34:56:10:b9:0e", "type": "NOC", - "name": "Garden", - "setup_date": 1544828430, - "reachable": true + "name": "Front", + "setup_date": 1509290599, + "reachable": true, + "customer_id": "A00010", + "network_lock": false, + "use_pincode": false }, { "id": "12:34:56:20:f5:44", @@ -155,33 +168,6 @@ "room_id": "222452125", "bridge": "12:34:56:20:f5:44" }, - { - "id": "12:34:56:10:f1:66", - "type": "NDB", - "name": "Netatmo-Doorbell", - "setup_date": 1602691361, - "room_id": "3688132631", - "reachable": true, - "hk_device_id": "123456007df1", - "customer_id": "1000010", - "network_lock": false, - "quick_display_zone": 62 - }, - { - "id": "12:34:56:00:e3:9b", - "type": "NIS", - "setup_date": 1620479901, - "bridge": "12:34:56:00:f1:62", - "name": "Sirene in hall" - }, - { - "id": "12:34:56:00:86:99", - "type": "NACamDoorTag", - "name": "Window Hall", - "setup_date": 1581177375, - "bridge": "12:34:56:00:f1:62", - "category": "window" - }, { "id": "12:34:56:30:d5:d4", "type": "NBG", @@ -199,16 +185,17 @@ "bridge": "12:34:56:30:d5:d4" }, { - "id": "12:34:56:37:11:ca", + "id": "12:34:56:80:bb:26", "type": "NAMain", - "name": "NetatmoIndoor", + "name": "Villa", "setup_date": 1419453350, + "room_id": "4122897288", "reachable": true, "modules_bridged": [ - "12:34:56:07:bb:3e", - "12:34:56:03:1b:e4", - "12:34:56:36:fc:de", - "12:34:56:05:51:20" + "12:34:56:80:44:92", + "12:34:56:80:7e:18", + "12:34:56:80:1c:42", + "12:34:56:80:c1:ea" ], "customer_id": "C00016", "hardware_version": 251, @@ -271,48 +258,46 @@ "module_offset": { "12:34:56:80:bb:26": { "a": 0.1 + }, + "03:00:00:03:1b:0e": { + "a": 0 } } }, { - "id": "12:34:56:36:fc:de", + "id": "12:34:56:80:1c:42", "type": "NAModule1", "name": "Outdoor", "setup_date": 1448565785, - "bridge": "12:34:56:37:11:ca" + "bridge": "12:34:56:80:bb:26" + }, + { + "id": "12:34:56:80:c1:ea", + "type": "NAModule3", + "name": "Rain", + "setup_date": 1591770206, + "bridge": "12:34:56:80:bb:26" + }, + { + "id": "12:34:56:80:44:92", + "type": "NAModule4", + "name": "Bedroom", + "setup_date": 1484997703, + "bridge": "12:34:56:80:bb:26" + }, + { + "id": "12:34:56:80:7e:18", + "type": "NAModule4", + "name": "Bathroom", + "setup_date": 1543579864, + "bridge": "12:34:56:80:bb:26" }, { "id": "12:34:56:03:1b:e4", "type": "NAModule2", "name": "Garden", "setup_date": 1543579864, - "bridge": "12:34:56:37:11:ca" - }, - { - "id": "12:34:56:05:51:20", - "type": "NAModule3", - "name": "Rain", - "setup_date": 1591770206, - "bridge": "12:34:56:37:11:ca" - }, - { - "id": "12:34:56:07:bb:3e", - "type": "NAModule4", - "name": "Bedroom", - "setup_date": 1484997703, - "bridge": "12:34:56:37:11:ca" - }, - { - "id": "12:34:56:26:68:92", - "type": "NHC", - "name": "Indoor", - "setup_date": 1571342643 - }, - { - "id": "12:34:56:26:cc:01", - "type": "BNS", - "name": "Child", - "setup_date": 1571634243 + "bridge": "12:34:56:80:bb:26" }, { "id": "12:34:56:80:60:40", @@ -324,7 +309,8 @@ "12:34:56:80:00:12:ac:f2", "12:34:56:80:00:c3:69:3c", "12:34:56:00:00:a1:4c:da", - "12:34:56:00:01:01:01:a1" + "12:34:56:00:01:01:01:a1", + "00:11:22:33:00:11:45:fe" ] }, { @@ -342,6 +328,21 @@ "setup_date": 1641841262, "bridge": "12:34:56:80:60:40" }, + { + "id": "12:34:56:00:86:99", + "type": "NACamDoorTag", + "name": "Window Hall", + "setup_date": 1581177375, + "bridge": "12:34:56:00:f1:62", + "category": "window" + }, + { + "id": "12:34:56:00:e3:9b", + "type": "NIS", + "setup_date": 1620479901, + "bridge": "12:34:56:00:f1:62", + "name": "Sirene in hall" + }, { "id": "12:34:56:00:16:0e", "type": "NLE", @@ -440,6 +441,24 @@ "room_id": "100008999", "bridge": "12:34:56:80:60:40" }, + { + "id": "10:20:30:bd:b8:1e", + "type": "BNS", + "name": "Smarther", + "setup_date": 1638022197, + "room_id": "1002003001" + }, + { + "id": "00:11:22:33:00:11:45:fe", + "type": "NLF", + "on": false, + "brightness": 63, + "firmware_revision": 57, + "last_seen": 1657086939, + "power": 0, + "reachable": true, + "bridge": "12:34:56:80:60:40" + }, { "id": "12:34:56:00:01:01:01:a1", "type": "NLFN", @@ -761,80 +780,13 @@ "therm_mode": "schedule" }, { - "id": "111111111111111111111401", - "name": "Home with no modules", - "altitude": 9, - "coordinates": [1.23456789, 50.0987654], - "country": "BE", - "timezone": "Europe/Brussels", - "rooms": [ - { - "id": "1111111401", - "name": "Livingroom", - "type": "livingroom" - } - ], - "temperature_control_mode": "heating", - "therm_mode": "away", - "therm_setpoint_default_duration": 120, - "cooling_mode": "schedule", - "schedules": [ - { - "away_temp": 14, - "hg_temp": 7, - "name": "Week", - "timetable": [ - { - "zone_id": 1, - "m_offset": 0 - }, - { - "zone_id": 6, - "m_offset": 420 - } - ], - "zones": [ - { - "type": 0, - "name": "Comfort", - "rooms_temp": [], - "id": 0, - "rooms": [] - }, - { - "type": 1, - "name": "Nacht", - "rooms_temp": [], - "id": 1, - "rooms": [] - }, - { - "type": 5, - "name": "Eco", - "rooms_temp": [], - "id": 4, - "rooms": [] - }, - { - "type": 4, - "name": "Tussenin", - "rooms_temp": [], - "id": 5, - "rooms": [] - }, - { - "type": 4, - "name": "Ochtend", - "rooms_temp": [], - "id": 6, - "rooms": [] - } - ], - "id": "700000000000000000000401", - "selected": true, - "type": "therm" - } - ] + "id": "91763b24c43d3e344f424e8c", + "altitude": 112, + "coordinates": [52.516263, 13.377726], + "country": "DE", + "timezone": "Europe/Berlin", + "therm_setpoint_default_duration": 180, + "therm_mode": "schedule" } ], "user": { @@ -845,6 +797,8 @@ "unit_pressure": 0, "unit_system": 0, "unit_wind": 0, + "all_linked": false, + "type": "netatmo", "id": "91763b24c43d3e344f424e8b" } }, diff --git a/tests/components/netatmo/fixtures/homestatus_91763b24c43d3e344f424e8b.json b/tests/components/netatmo/fixtures/homestatus_91763b24c43d3e344f424e8b.json index 4cd5dceec3b..736d70be11c 100644 --- a/tests/components/netatmo/fixtures/homestatus_91763b24c43d3e344f424e8b.json +++ b/tests/components/netatmo/fixtures/homestatus_91763b24c43d3e344f424e8b.json @@ -14,25 +14,6 @@ "vpn_url": "https://prodvpn-eu-2.netatmo.net/restricted/10.255.123.45/609e27de5699fb18147ab47d06846631/MTRPn_BeWCav5RBq4U1OMDruTW4dkQ0NuMwNDAw11g,,", "is_local": true }, - { - "type": "NOC", - "firmware_revision": 3002000, - "monitoring": "on", - "sd_status": 4, - "connection": "wifi", - "homekit_status": "upgradable", - "floodlight": "auto", - "timelapse_available": true, - "id": "12:34:56:00:a5:a4", - "vpn_url": "https://prodvpn-eu-6.netatmo.net/10.20.30.41/333333333333/444444444444,,", - "is_local": false, - "network_lock": false, - "firmware_name": "3.2.0", - "wifi_strength": 62, - "alim_status": 2, - "locked": false, - "wifi_state": "high" - }, { "id": "12:34:56:00:fa:d0", "type": "NAPlug", @@ -46,6 +27,7 @@ "type": "NATherm1", "firmware_revision": 65, "rf_strength": 58, + "battery_level": 3793, "boiler_valve_comfort_boost": false, "boiler_status": false, "anticipating": false, @@ -58,6 +40,7 @@ "type": "NRV", "firmware_revision": 79, "rf_strength": 51, + "battery_level": 3025, "bridge": "12:34:56:00:fa:d0", "battery_state": "full" }, @@ -67,18 +50,10 @@ "type": "NRV", "firmware_revision": 79, "rf_strength": 59, + "battery_level": 3029, "bridge": "12:34:56:00:fa:d0", "battery_state": "full" }, - { - "id": "12:34:56:26:cc:01", - "type": "BNS", - "firmware_revision": 32, - "wifi_strength": 50, - "boiler_valve_comfort_boost": false, - "boiler_status": true, - "cooler_status": false - }, { "type": "NDB", "last_ftp_event": { @@ -100,6 +75,25 @@ "wifi_strength": 66, "wifi_state": "medium" }, + { + "type": "NOC", + "firmware_revision": 3002000, + "monitoring": "on", + "sd_status": 4, + "connection": "wifi", + "homekit_status": "upgradable", + "floodlight": "auto", + "timelapse_available": true, + "id": "12:34:56:10:b9:0e", + "vpn_url": "https://prodvpn-eu-6.netatmo.net/10.20.30.41/333333333333/444444444444,,", + "is_local": false, + "network_lock": false, + "firmware_name": "3.2.0", + "wifi_strength": 62, + "alim_status": 2, + "locked": false, + "wifi_state": "high" + }, { "boiler_control": "onoff", "dhw_control": "none", @@ -264,609 +258,23 @@ "bridge": "12:34:56:80:60:40" }, { - "id": "12:34:56:00:01:01:01:a1", - "brightness": 100, - "firmware_revision": 52, - "last_seen": 1604940167, - "on": false, - "power": 0, - "reachable": true, - "type": "NLFN", - "bridge": "12:34:56:80:60:40" - }, - { - "type": "NDB", - "last_ftp_event": { - "type": 3, - "time": 1631444443, - "id": 3 - }, - "id": "12:34:56:10:f1:66", - "websocket_connected": true, - "vpn_url": "https://prodvpn-eu-6.netatmo.net/10.20.30.40/1111111111111/2222222222222,,", - "is_local": false, - "alim_status": 2, - "connection": "wifi", - "firmware_name": "2.18.0", - "firmware_revision": 2018000, - "homekit_status": "configured", - "max_peers_reached": false, - "sd_status": 4, - "wifi_strength": 66, - "wifi_state": "medium" - }, - { - "boiler_control": "onoff", - "dhw_control": "none", - "firmware_revision": 22, - "hardware_version": 222, - "id": "12:34:56:20:f5:44", - "outdoor_temperature": 8.2, - "sequence_id": 19764, - "type": "OTH", - "wifi_strength": 57 - }, - { - "battery_level": 4176, - "boiler_status": false, + "id": "10:20:30:bd:b8:1e", + "type": "BNS", + "firmware_revision": 32, + "wifi_strength": 49, "boiler_valve_comfort_boost": false, - "firmware_revision": 6, - "id": "12:34:56:20:f5:8c", - "last_message": 1637684297, - "last_seen": 1637684297, - "radio_id": 2, - "reachable": true, - "rf_strength": 64, - "type": "OTM", - "bridge": "12:34:56:20:f5:44", - "battery_state": "full" + "boiler_status": true, + "cooler_status": false }, { - "id": "12:34:56:30:d5:d4", - "type": "NBG", - "firmware_revision": 39, - "wifi_strength": 65, - "reachable": true - }, - { - "id": "0009999992", - "type": "NBR", - "current_position": 0, - "target_position": 0, - "target_position_step": 100, - "firmware_revision": 16, - "rf_strength": 0, - "last_seen": 1638353156, - "reachable": true, - "therm_measured_temperature": 5, - "heating_power_request": 1, - "therm_setpoint_temperature": 7, - "therm_setpoint_mode": "away", - "therm_setpoint_start_time": 0, - "therm_setpoint_end_time": 0, - "anticipating": false, - "open_window": false - }, - { - "id": "12:34:56:00:86:99", - "type": "NACamDoorTag", - "battery_state": "high", - "battery_level": 5240, - "firmware_revision": 58, - "rf_state": "full", - "rf_strength": 58, - "last_seen": 1642698124, - "last_activity": 1627757310, - "reachable": false, - "bridge": "12:34:56:00:f1:62", - "status": "no_news" - }, - { - "id": "12:34:56:00:e3:9b", - "type": "NIS", - "battery_state": "low", - "battery_level": 5438, - "firmware_revision": 209, - "rf_state": "medium", - "rf_strength": 62, - "last_seen": 1644569790, - "reachable": true, - "bridge": "12:34:56:00:f1:62", - "status": "no_sound", - "monitoring": "off" - }, - { - "id": "12:34:56:80:60:40", - "type": "NLG", - "offload": false, - "firmware_revision": 211, - "last_seen": 1644567372, - "wifi_strength": 51, - "reachable": true - }, - { - "id": "12:34:56:80:00:12:ac:f2", - "type": "NLP", - "on": true, - "offload": false, - "firmware_revision": 62, - "last_seen": 1644569425, - "power": 0, - "reachable": true, - "bridge": "12:34:56:80:60:40" - }, - { - "id": "12:34:56:80:00:c3:69:3c", - "type": "NLT", - "battery_state": "full", - "battery_level": 3300, - "firmware_revision": 42, - "last_seen": 0, - "reachable": false, - "bridge": "12:34:56:80:60:40" - }, - { - "id": "12:34:56:00:16:0e", - "type": "NLE", - "firmware_revision": 14, - "wifi_strength": 38 - }, - { - "id": "12:34:56:00:16:0e#0", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#1", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#2", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#3", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#4", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#5", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#6", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#7", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#8", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:00:a1:4c:da", - "type": "NLPC", - "firmware_revision": 62, - "last_seen": 1646511241, - "power": 476, - "reachable": true, - "bridge": "12:34:56:80:60:40" - }, - { - "id": "12:34:56:00:01:01:01:a1", - "brightness": 100, - "firmware_revision": 52, - "last_seen": 1604940167, + "id": "00:11:22:33:00:11:45:fe", + "type": "NLF", "on": false, + "brightness": 63, + "firmware_revision": 57, + "last_seen": 1657086939, "power": 0, "reachable": true, - "type": "NLFN", - "bridge": "12:34:56:80:60:40" - }, - { - "type": "NDB", - "last_ftp_event": { - "type": 3, - "time": 1631444443, - "id": 3 - }, - "id": "12:34:56:10:f1:66", - "websocket_connected": true, - "vpn_url": "https://prodvpn-eu-6.netatmo.net/10.20.30.40/1111111111111/2222222222222,,", - "is_local": false, - "alim_status": 2, - "connection": "wifi", - "firmware_name": "2.18.0", - "firmware_revision": 2018000, - "homekit_status": "configured", - "max_peers_reached": false, - "sd_status": 4, - "wifi_strength": 66, - "wifi_state": "medium" - }, - { - "boiler_control": "onoff", - "dhw_control": "none", - "firmware_revision": 22, - "hardware_version": 222, - "id": "12:34:56:20:f5:44", - "outdoor_temperature": 8.2, - "sequence_id": 19764, - "type": "OTH", - "wifi_strength": 57 - }, - { - "battery_level": 4176, - "boiler_status": false, - "boiler_valve_comfort_boost": false, - "firmware_revision": 6, - "id": "12:34:56:20:f5:8c", - "last_message": 1637684297, - "last_seen": 1637684297, - "radio_id": 2, - "reachable": true, - "rf_strength": 64, - "type": "OTM", - "bridge": "12:34:56:20:f5:44", - "battery_state": "full" - }, - { - "id": "12:34:56:30:d5:d4", - "type": "NBG", - "firmware_revision": 39, - "wifi_strength": 65, - "reachable": true - }, - { - "id": "0009999992", - "type": "NBR", - "current_position": 0, - "target_position": 0, - "target_position_step": 100, - "firmware_revision": 16, - "rf_strength": 0, - "last_seen": 1638353156, - "reachable": true, - "therm_measured_temperature": 5, - "heating_power_request": 1, - "therm_setpoint_temperature": 7, - "therm_setpoint_mode": "away", - "therm_setpoint_start_time": 0, - "therm_setpoint_end_time": 0, - "anticipating": false, - "open_window": false - }, - { - "id": "12:34:56:00:86:99", - "type": "NACamDoorTag", - "battery_state": "high", - "battery_level": 5240, - "firmware_revision": 58, - "rf_state": "full", - "rf_strength": 58, - "last_seen": 1642698124, - "last_activity": 1627757310, - "reachable": false, - "bridge": "12:34:56:00:f1:62", - "status": "no_news" - }, - { - "id": "12:34:56:00:e3:9b", - "type": "NIS", - "battery_state": "low", - "battery_level": 5438, - "firmware_revision": 209, - "rf_state": "medium", - "rf_strength": 62, - "last_seen": 1644569790, - "reachable": true, - "bridge": "12:34:56:00:f1:62", - "status": "no_sound", - "monitoring": "off" - }, - { - "id": "12:34:56:80:60:40", - "type": "NLG", - "offload": false, - "firmware_revision": 211, - "last_seen": 1644567372, - "wifi_strength": 51, - "reachable": true - }, - { - "id": "12:34:56:80:00:12:ac:f2", - "type": "NLP", - "on": true, - "offload": false, - "firmware_revision": 62, - "last_seen": 1644569425, - "power": 0, - "reachable": true, - "bridge": "12:34:56:80:60:40" - }, - { - "id": "12:34:56:80:00:c3:69:3c", - "type": "NLT", - "battery_state": "full", - "battery_level": 3300, - "firmware_revision": 42, - "last_seen": 0, - "reachable": false, - "bridge": "12:34:56:80:60:40" - }, - { - "id": "12:34:56:00:16:0e", - "type": "NLE", - "firmware_revision": 14, - "wifi_strength": 38 - }, - { - "id": "12:34:56:00:16:0e#0", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#1", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#2", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#3", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#4", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#5", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#6", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#7", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#8", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:00:a1:4c:da", - "type": "NLPC", - "firmware_revision": 62, - "last_seen": 1646511241, - "power": 476, - "reachable": true, - "bridge": "12:34:56:80:60:40" - }, - { - "id": "12:34:56:00:01:01:01:a1", - "brightness": 100, - "firmware_revision": 52, - "last_seen": 1604940167, - "on": false, - "power": 0, - "reachable": true, - "type": "NLFN", - "bridge": "12:34:56:80:60:40" - }, - { - "type": "NDB", - "last_ftp_event": { - "type": 3, - "time": 1631444443, - "id": 3 - }, - "id": "12:34:56:10:f1:66", - "websocket_connected": true, - "vpn_url": "https://prodvpn-eu-6.netatmo.net/10.20.30.40/1111111111111/2222222222222,,", - "is_local": false, - "alim_status": 2, - "connection": "wifi", - "firmware_name": "2.18.0", - "firmware_revision": 2018000, - "homekit_status": "configured", - "max_peers_reached": false, - "sd_status": 4, - "wifi_strength": 66, - "wifi_state": "medium" - }, - { - "boiler_control": "onoff", - "dhw_control": "none", - "firmware_revision": 22, - "hardware_version": 222, - "id": "12:34:56:20:f5:44", - "outdoor_temperature": 8.2, - "sequence_id": 19764, - "type": "OTH", - "wifi_strength": 57 - }, - { - "battery_level": 4176, - "boiler_status": false, - "boiler_valve_comfort_boost": false, - "firmware_revision": 6, - "id": "12:34:56:20:f5:8c", - "last_message": 1637684297, - "last_seen": 1637684297, - "radio_id": 2, - "reachable": true, - "rf_strength": 64, - "type": "OTM", - "bridge": "12:34:56:20:f5:44", - "battery_state": "full" - }, - { - "id": "12:34:56:30:d5:d4", - "type": "NBG", - "firmware_revision": 39, - "wifi_strength": 65, - "reachable": true - }, - { - "id": "0009999992", - "type": "NBR", - "current_position": 0, - "target_position": 0, - "target_position_step": 100, - "firmware_revision": 16, - "rf_strength": 0, - "last_seen": 1638353156, - "reachable": true, - "therm_measured_temperature": 5, - "heating_power_request": 1, - "therm_setpoint_temperature": 7, - "therm_setpoint_mode": "away", - "therm_setpoint_start_time": 0, - "therm_setpoint_end_time": 0, - "anticipating": false, - "open_window": false - }, - { - "id": "12:34:56:00:86:99", - "type": "NACamDoorTag", - "battery_state": "high", - "battery_level": 5240, - "firmware_revision": 58, - "rf_state": "full", - "rf_strength": 58, - "last_seen": 1642698124, - "last_activity": 1627757310, - "reachable": false, - "bridge": "12:34:56:00:f1:62", - "status": "no_news" - }, - { - "id": "12:34:56:00:e3:9b", - "type": "NIS", - "battery_state": "low", - "battery_level": 5438, - "firmware_revision": 209, - "rf_state": "medium", - "rf_strength": 62, - "last_seen": 1644569790, - "reachable": true, - "bridge": "12:34:56:00:f1:62", - "status": "no_sound", - "monitoring": "off" - }, - { - "id": "12:34:56:80:60:40", - "type": "NLG", - "offload": false, - "firmware_revision": 211, - "last_seen": 1644567372, - "wifi_strength": 51, - "reachable": true - }, - { - "id": "12:34:56:80:00:12:ac:f2", - "type": "NLP", - "on": true, - "offload": false, - "firmware_revision": 62, - "last_seen": 1644569425, - "power": 0, - "reachable": true, - "bridge": "12:34:56:80:60:40" - }, - { - "id": "12:34:56:80:00:c3:69:3c", - "type": "NLT", - "battery_state": "full", - "battery_level": 3300, - "firmware_revision": 42, - "last_seen": 0, - "reachable": false, - "bridge": "12:34:56:80:60:40" - }, - { - "id": "12:34:56:00:16:0e", - "type": "NLE", - "firmware_revision": 14, - "wifi_strength": 38 - }, - { - "id": "12:34:56:00:16:0e#0", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#1", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#2", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#3", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#4", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#5", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#6", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#7", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:16:0e#8", - "type": "NLE", - "bridge": "12:34:56:00:16:0e" - }, - { - "id": "12:34:56:00:00:a1:4c:da", - "type": "NLPC", - "firmware_revision": 62, - "last_seen": 1646511241, - "power": 476, - "reachable": true, "bridge": "12:34:56:80:60:40" } ], @@ -876,17 +284,17 @@ "reachable": true, "therm_measured_temperature": 19.8, "therm_setpoint_temperature": 12, - "therm_setpoint_mode": "schedule", + "therm_setpoint_mode": "away", "therm_setpoint_start_time": 1559229567, "therm_setpoint_end_time": 0 }, { "id": "2940411577", "reachable": true, - "therm_measured_temperature": 5, - "heating_power_request": 1, + "therm_measured_temperature": 27, + "heating_power_request": 0, "therm_setpoint_temperature": 7, - "therm_setpoint_mode": "away", + "therm_setpoint_mode": "hg", "therm_setpoint_start_time": 0, "therm_setpoint_end_time": 0, "anticipating": false, @@ -905,15 +313,15 @@ "open_window": false }, { - "id": "2940411588", + "id": "1002003001", "reachable": true, "anticipating": false, "heating_power_request": 0, "open_window": false, - "humidity": 68, - "therm_measured_temperature": 19.9, - "therm_setpoint_temperature": 21.5, - "therm_setpoint_start_time": 1647793285, + "humidity": 67, + "therm_measured_temperature": 22, + "therm_setpoint_temperature": 22, + "therm_setpoint_start_time": 1647462737, "therm_setpoint_end_time": null, "therm_setpoint_mode": "home" } diff --git a/tests/components/netatmo/fixtures/homestatus_91763b24c43d3e344f424e8c.json b/tests/components/netatmo/fixtures/homestatus_91763b24c43d3e344f424e8c.json index d950c82a6a5..406e24bc107 100644 --- a/tests/components/netatmo/fixtures/homestatus_91763b24c43d3e344f424e8c.json +++ b/tests/components/netatmo/fixtures/homestatus_91763b24c43d3e344f424e8c.json @@ -1,12 +1,20 @@ { "status": "ok", - "time_server": 1559292041, + "time_server": 1642952130, "body": { "home": { - "modules": [], - "rooms": [], - "id": "91763b24c43d3e344f424e8c", - "persons": [] + "persons": [ + { + "id": "abcdef12-1111-0000-0000-000111222333", + "last_seen": 1489050910, + "out_of_sight": true + }, + { + "id": "abcdef12-2222-0000-0000-000111222333", + "last_seen": 1489078776, + "out_of_sight": true + } + ] } } } diff --git a/tests/components/netatmo/test_camera.py b/tests/components/netatmo/test_camera.py index beb91c7565e..76397988187 100644 --- a/tests/components/netatmo/test_camera.py +++ b/tests/components/netatmo/test_camera.py @@ -33,7 +33,7 @@ async def test_setup_component_with_webhook(hass, config_entry, netatmo_auth): await hass.async_block_till_done() camera_entity_indoor = "camera.hall" - camera_entity_outdoor = "camera.garden" + camera_entity_outdoor = "camera.front" assert hass.states.get(camera_entity_indoor).state == "streaming" response = { "event_type": "off", @@ -59,8 +59,8 @@ async def test_setup_component_with_webhook(hass, config_entry, netatmo_auth): response = { "event_type": "light_mode", - "device_id": "12:34:56:00:a5:a4", - "camera_id": "12:34:56:00:a5:a4", + "device_id": "12:34:56:10:b9:0e", + "camera_id": "12:34:56:10:b9:0e", "event_id": "601dce1560abca1ebad9b723", "push_type": "NOC-light_mode", "sub_type": "on", @@ -72,8 +72,8 @@ async def test_setup_component_with_webhook(hass, config_entry, netatmo_auth): response = { "event_type": "light_mode", - "device_id": "12:34:56:00:a5:a4", - "camera_id": "12:34:56:00:a5:a4", + "device_id": "12:34:56:10:b9:0e", + "camera_id": "12:34:56:10:b9:0e", "event_id": "601dce1560abca1ebad9b723", "push_type": "NOC-light_mode", "sub_type": "auto", @@ -84,7 +84,7 @@ async def test_setup_component_with_webhook(hass, config_entry, netatmo_auth): response = { "event_type": "light_mode", - "device_id": "12:34:56:00:a5:a4", + "device_id": "12:34:56:10:b9:0e", "event_id": "601dce1560abca1ebad9b723", "push_type": "NOC-light_mode", } @@ -166,7 +166,7 @@ async def test_camera_image_vpn(hass, config_entry, requests_mock, netatmo_auth) uri = "https://prodvpn-eu-6.netatmo.net/10.20.30.41/333333333333/444444444444,," stream_uri = uri + "/live/files/high/index.m3u8" - camera_entity_indoor = "camera.garden" + camera_entity_indoor = "camera.front" cam = hass.states.get(camera_entity_indoor) assert cam is not None @@ -304,14 +304,14 @@ async def test_service_set_camera_light(hass, config_entry, netatmo_auth): await hass.async_block_till_done() data = { - "entity_id": "camera.garden", + "entity_id": "camera.front", "camera_light_mode": "on", } expected_data = { "modules": [ { - "id": "12:34:56:00:a5:a4", + "id": "12:34:56:10:b9:0e", "floodlight": "on", }, ], @@ -353,7 +353,6 @@ async def test_service_set_camera_light_invalid_type(hass, config_entry, netatmo assert excinfo.value.args == ("NACamera does not have a floodlight",) -@pytest.mark.skip async def test_camera_reconnect_webhook(hass, config_entry): """Test webhook event on camera reconnect.""" fake_post_hits = 0 @@ -406,7 +405,7 @@ async def test_camera_reconnect_webhook(hass, config_entry): dt.utcnow() + timedelta(seconds=60), ) await hass.async_block_till_done() - assert fake_post_hits > calls + assert fake_post_hits >= calls async def test_webhook_person_event(hass, config_entry, netatmo_auth): diff --git a/tests/components/netatmo/test_climate.py b/tests/components/netatmo/test_climate.py index d37bab929e1..afe85049f95 100644 --- a/tests/components/netatmo/test_climate.py +++ b/tests/components/netatmo/test_climate.py @@ -36,8 +36,7 @@ async def test_webhook_event_handling_thermostats(hass, config_entry, netatmo_au assert hass.states.get(climate_entity_livingroom).state == "auto" assert ( - hass.states.get(climate_entity_livingroom).attributes["preset_mode"] - == "Schedule" + hass.states.get(climate_entity_livingroom).attributes["preset_mode"] == "away" ) assert hass.states.get(climate_entity_livingroom).attributes["temperature"] == 12 @@ -80,8 +79,7 @@ async def test_webhook_event_handling_thermostats(hass, config_entry, netatmo_au assert hass.states.get(climate_entity_livingroom).state == "heat" assert ( - hass.states.get(climate_entity_livingroom).attributes["preset_mode"] - == "Schedule" + hass.states.get(climate_entity_livingroom).attributes["preset_mode"] == "away" ) assert hass.states.get(climate_entity_livingroom).attributes["temperature"] == 21 @@ -194,8 +192,7 @@ async def test_webhook_event_handling_thermostats(hass, config_entry, netatmo_au assert hass.states.get(climate_entity_livingroom).state == "auto" assert ( - hass.states.get(climate_entity_livingroom).attributes["preset_mode"] - == "Schedule" + hass.states.get(climate_entity_livingroom).attributes["preset_mode"] == "away" ) @@ -213,8 +210,7 @@ async def test_service_preset_mode_frost_guard_thermostat( assert hass.states.get(climate_entity_livingroom).state == "auto" assert ( - hass.states.get(climate_entity_livingroom).attributes["preset_mode"] - == "Schedule" + hass.states.get(climate_entity_livingroom).attributes["preset_mode"] == "away" ) # Test service setting the preset mode to "frost guard" @@ -269,8 +265,7 @@ async def test_service_preset_mode_frost_guard_thermostat( assert hass.states.get(climate_entity_livingroom).state == "auto" assert ( - hass.states.get(climate_entity_livingroom).attributes["preset_mode"] - == "Schedule" + hass.states.get(climate_entity_livingroom).attributes["preset_mode"] == "away" ) @@ -286,8 +281,7 @@ async def test_service_preset_modes_thermostat(hass, config_entry, netatmo_auth) assert hass.states.get(climate_entity_livingroom).state == "auto" assert ( - hass.states.get(climate_entity_livingroom).attributes["preset_mode"] - == "Schedule" + hass.states.get(climate_entity_livingroom).attributes["preset_mode"] == "away" ) # Test service setting the preset mode to "away" diff --git a/tests/components/netatmo/test_light.py b/tests/components/netatmo/test_light.py index b1a5270745c..526fb2fe518 100644 --- a/tests/components/netatmo/test_light.py +++ b/tests/components/netatmo/test_light.py @@ -27,14 +27,14 @@ async def test_camera_light_setup_and_services(hass, config_entry, netatmo_auth) await simulate_webhook(hass, webhook_id, FAKE_WEBHOOK_ACTIVATION) await hass.async_block_till_done() - light_entity = "light.garden" + light_entity = "light.front" assert hass.states.get(light_entity).state == "unavailable" # Trigger light mode change response = { "event_type": "light_mode", - "device_id": "12:34:56:00:a5:a4", - "camera_id": "12:34:56:00:a5:a4", + "device_id": "12:34:56:10:b9:0e", + "camera_id": "12:34:56:10:b9:0e", "event_id": "601dce1560abca1ebad9b723", "push_type": "NOC-light_mode", "sub_type": "on", @@ -46,7 +46,7 @@ async def test_camera_light_setup_and_services(hass, config_entry, netatmo_auth) # Trigger light mode change with erroneous webhook data response = { "event_type": "light_mode", - "device_id": "12:34:56:00:a5:a4", + "device_id": "12:34:56:10:b9:0e", } await simulate_webhook(hass, webhook_id, response) @@ -62,7 +62,7 @@ async def test_camera_light_setup_and_services(hass, config_entry, netatmo_auth) ) await hass.async_block_till_done() mock_set_state.assert_called_once_with( - {"modules": [{"id": "12:34:56:00:a5:a4", "floodlight": "auto"}]} + {"modules": [{"id": "12:34:56:10:b9:0e", "floodlight": "auto"}]} ) # Test turning light on @@ -75,7 +75,7 @@ async def test_camera_light_setup_and_services(hass, config_entry, netatmo_auth) ) await hass.async_block_till_done() mock_set_state.assert_called_once_with( - {"modules": [{"id": "12:34:56:00:a5:a4", "floodlight": "on"}]} + {"modules": [{"id": "12:34:56:10:b9:0e", "floodlight": "on"}]} ) diff --git a/tests/components/netatmo/test_sensor.py b/tests/components/netatmo/test_sensor.py index d3ea8fb8167..9ef56372316 100644 --- a/tests/components/netatmo/test_sensor.py +++ b/tests/components/netatmo/test_sensor.py @@ -16,12 +16,12 @@ async def test_weather_sensor(hass, config_entry, netatmo_auth): await hass.async_block_till_done() - prefix = "sensor.netatmoindoor_" + prefix = "sensor.parents_bedroom_" - assert hass.states.get(f"{prefix}temperature").state == "24.6" - assert hass.states.get(f"{prefix}humidity").state == "36" - assert hass.states.get(f"{prefix}co2").state == "749" - assert hass.states.get(f"{prefix}pressure").state == "1017.3" + assert hass.states.get(f"{prefix}temperature").state == "20.3" + assert hass.states.get(f"{prefix}humidity").state == "63" + assert hass.states.get(f"{prefix}co2").state == "494" + assert hass.states.get(f"{prefix}pressure").state == "1014.5" async def test_public_weather_sensor(hass, config_entry, netatmo_auth): @@ -104,25 +104,25 @@ async def test_process_health(health, expected): @pytest.mark.parametrize( "uid, name, expected", [ - ("12:34:56:37:11:ca-reachable", "mystation_reachable", "True"), - ("12:34:56:03:1b:e4-rf_status", "mystation_yard_radio", "Full"), + ("12:34:56:03:1b:e4-reachable", "villa_garden_reachable", "True"), + ("12:34:56:03:1b:e4-rf_status", "villa_garden_radio", "Full"), ( - "12:34:56:37:11:ca-wifi_status", - "mystation_wifi_strength", - "Full", + "12:34:56:80:bb:26-wifi_status", + "villa_wifi_strength", + "High", ), ( - "12:34:56:37:11:ca-temp_trend", - "mystation_temperature_trend", + "12:34:56:80:bb:26-temp_trend", + "villa_temperature_trend", "stable", ), ( - "12:34:56:37:11:ca-pressure_trend", - "netatmo_mystation_pressure_trend", - "down", + "12:34:56:80:bb:26-pressure_trend", + "villa_pressure_trend", + "up", ), - ("12:34:56:05:51:20-sum_rain_1", "netatmo_mystation_yard_rain_last_hour", "0"), - ("12:34:56:05:51:20-sum_rain_24", "netatmo_mystation_yard_rain_today", "0"), + ("12:34:56:80:c1:ea-sum_rain_1", "villa_rain_rain_last_hour", "0"), + ("12:34:56:80:c1:ea-sum_rain_24", "villa_rain_rain_today", "6.9"), ("12:34:56:03:1b:e4-windangle", "netatmoindoor_garden_direction", "SW"), ( "12:34:56:03:1b:e4-windangle_value", From dd092d86d47c9a78129ee9e31f03062ec03c9340 Mon Sep 17 00:00:00 2001 From: Josh Anderson Date: Mon, 31 Oct 2022 12:19:52 +0000 Subject: [PATCH 0111/1033] Update supported and deprecated IBM Watson voices (#81247) --- homeassistant/components/watson_tts/tts.py | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/homeassistant/components/watson_tts/tts.py b/homeassistant/components/watson_tts/tts.py index 93e6b98fbd6..efd20e37e83 100644 --- a/homeassistant/components/watson_tts/tts.py +++ b/homeassistant/components/watson_tts/tts.py @@ -23,6 +23,7 @@ CONF_TEXT_TYPE = "text" SUPPORTED_VOICES = [ "ar-AR_OmarVoice", "ar-MS_OmarVoice", + "cs-CZ_AlenaVoice", "de-DE_BirgitV2Voice", "de-DE_BirgitV3Voice", "de-DE_BirgitVoice", @@ -32,21 +33,26 @@ SUPPORTED_VOICES = [ "de-DE_ErikaV3Voice", "en-AU_CraigVoice", "en-AU_MadisonVoice", + "en-AU_SteveVoice", "en-GB_KateV3Voice", "en-GB_KateVoice", "en-GB_CharlotteV3Voice", "en-GB_JamesV3Voice", "en-GB_KateV3Voice", "en-GB_KateVoice", + "en-US_AllisonExpressive", "en-US_AllisonV2Voice", "en-US_AllisonV3Voice", "en-US_AllisonVoice", "en-US_EmilyV3Voice", + "en-US_EmmaExpressive", "en-US_HenryV3Voice", "en-US_KevinV3Voice", + "en-US_LisaExpressive", "en-US_LisaV2Voice", "en-US_LisaV3Voice", "en-US_LisaVoice", + "en-US_MichaelExpressive", "en-US_MichaelV2Voice", "en-US_MichaelV3Voice", "en-US_MichaelVoice", @@ -72,10 +78,13 @@ SUPPORTED_VOICES = [ "ko-KR_SiWooVoice", "ko-KR_YoungmiVoice", "ko-KR_YunaVoice", + "nl-BE_AdeleVoice", + "nl-BE_BramVoice", "nl-NL_EmmaVoice", "nl-NL_LiamVoice", "pt-BR_IsabelaV3Voice", "pt-BR_IsabelaVoice", + "sv-SE_IngridVoice", "zh-CN_LiNaVoice", "zh-CN_WangWeiVoice", "zh-CN_ZhangJingVoice", @@ -83,8 +92,13 @@ SUPPORTED_VOICES = [ DEPRECATED_VOICES = [ "ar-AR_OmarVoice", + "ar-MS_OmarVoice", + "cs-CZ_AlenaVoice", "de-DE_BirgitVoice", "de-DE_DieterVoice", + "en-AU_CraigVoice", + "en-AU_MadisonVoice", + "en-AU_SteveVoice", "en-GB_KateVoice", "en-GB_KateV3Voice", "en-US_AllisonVoice", @@ -97,7 +111,19 @@ DEPRECATED_VOICES = [ "fr-FR_ReneeVoice", "it-IT_FrancescaVoice", "ja-JP_EmiVoice", + "ko-KR_HyunjunVoice", + "ko-KR_SiWooVoice", + "ko-KR_YoungmiVoice", + "ko-KR_YunaVoice", + "nl-BE_AdeleVoice", + "nl-BE_BramVoice", + "nl-NL_EmmaVoice", + "nl-NL_LiamVoice", "pt-BR_IsabelaVoice", + "sv-SE_IngridVoice", + "zh-CN_LiNaVoice", + "zh-CN_WangWeiVoice", + "zh-CN_ZhangJingVoice", ] SUPPORTED_OUTPUT_FORMATS = [ From 283f8585b89d274ab2dbd9b9a7d54d0e2b0b5c58 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 31 Oct 2022 13:21:37 +0100 Subject: [PATCH 0112/1033] Adjust scrape coordinator logging (#81299) --- homeassistant/components/scrape/coordinator.py | 4 +++- homeassistant/components/scrape/sensor.py | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/scrape/coordinator.py b/homeassistant/components/scrape/coordinator.py index d947e6ac519..9fc66db3481 100644 --- a/homeassistant/components/scrape/coordinator.py +++ b/homeassistant/components/scrape/coordinator.py @@ -33,4 +33,6 @@ class ScrapeCoordinator(DataUpdateCoordinator[BeautifulSoup]): await self._rest.async_update() if (data := self._rest.data) is None: raise UpdateFailed("REST data is not available") - return await self.hass.async_add_executor_job(BeautifulSoup, data, "lxml") + soup = await self.hass.async_add_executor_job(BeautifulSoup, data, "lxml") + _LOGGER.debug("Raw beautiful soup: %s", soup) + return soup diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index d6e5a60d339..176b556e189 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -170,7 +170,6 @@ class ScrapeSensor(CoordinatorEntity[ScrapeCoordinator], TemplateSensor): def _extract_value(self) -> Any: """Parse the html extraction in the executor.""" raw_data = self.coordinator.data - _LOGGER.debug("Raw beautiful soup: %s", raw_data) try: if self._attr is not None: value = raw_data.select(self._select)[self._index][self._attr] From be68412c6490fd0c6e3ab3c922431e205650e349 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 31 Oct 2022 13:26:05 +0100 Subject: [PATCH 0113/1033] Update pytest to 7.2.0 (#81295) --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index b15ceb3b002..06bd7e878ce 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -24,7 +24,7 @@ pytest-test-groups==1.0.3 pytest-sugar==0.9.5 pytest-timeout==2.1.0 pytest-xdist==2.5.0 -pytest==7.1.3 +pytest==7.2.0 requests_mock==1.10.0 respx==0.19.2 stdlib-list==0.7.0 From fbc8f0a2cf6b914d3d8a6c6b6d43db39e05bde90 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 31 Oct 2022 14:06:09 +0100 Subject: [PATCH 0114/1033] Improve type hints in rest integration (#81291) --- homeassistant/components/rest/__init__.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/rest/__init__.py b/homeassistant/components/rest/__init__.py index c9efbc30d4a..4ec519dd709 100644 --- a/homeassistant/components/rest/__init__.py +++ b/homeassistant/components/rest/__init__.py @@ -2,9 +2,11 @@ from __future__ import annotations import asyncio +from collections.abc import Coroutine import contextlib from datetime import timedelta import logging +from typing import Any import httpx import voluptuous as vol @@ -88,15 +90,15 @@ async def _async_process_config(hass: HomeAssistant, config: ConfigType) -> bool if DOMAIN not in config: return True - refresh_tasks = [] - load_tasks = [] + refresh_coroutines: list[Coroutine[Any, Any, None]] = [] + load_coroutines: list[Coroutine[Any, Any, None]] = [] rest_config: list[ConfigType] = config[DOMAIN] for rest_idx, conf in enumerate(rest_config): scan_interval: timedelta = conf.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) resource_template: template.Template | None = conf.get(CONF_RESOURCE_TEMPLATE) rest = create_rest_data_from_config(hass, conf) coordinator = _rest_coordinator(hass, rest, resource_template, scan_interval) - refresh_tasks.append(coordinator.async_refresh()) + refresh_coroutines.append(coordinator.async_refresh()) hass.data[DOMAIN][REST_DATA].append({REST: rest, COORDINATOR: coordinator}) for platform_domain in COORDINATOR_AWARE_PLATFORMS: @@ -107,20 +109,20 @@ async def _async_process_config(hass: HomeAssistant, config: ConfigType) -> bool hass.data[DOMAIN][platform_domain].append(platform_conf) platform_idx = len(hass.data[DOMAIN][platform_domain]) - 1 - load = discovery.async_load_platform( + load_coroutine = discovery.async_load_platform( hass, platform_domain, DOMAIN, {REST_IDX: rest_idx, PLATFORM_IDX: platform_idx}, config, ) - load_tasks.append(load) + load_coroutines.append(load_coroutine) - if refresh_tasks: - await asyncio.gather(*refresh_tasks) + if refresh_coroutines: + await asyncio.gather(*refresh_coroutines) - if load_tasks: - await asyncio.gather(*load_tasks) + if load_coroutines: + await asyncio.gather(*load_coroutines) return True From 5ba3b499fe77f47e3385112238c02fa830bd8f79 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 31 Oct 2022 08:18:49 -0500 Subject: [PATCH 0115/1033] Bump dbus-fast to 1.60.0 (#81296) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 091962fbc83..bca2f7f9a8d 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.8.1", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", - "dbus-fast==1.59.1" + "dbus-fast==1.60.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 914731a8164..e2c57d87c19 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.1 -dbus-fast==1.59.1 +dbus-fast==1.60.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index eedb8fd71d5..f3db2448dd0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -543,7 +543,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.59.1 +dbus-fast==1.60.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ac8162f39bf..45b8e84298a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -423,7 +423,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.59.1 +dbus-fast==1.60.0 # homeassistant.components.debugpy debugpy==1.6.3 From 8416cc1906daf184bb5416759ebee52b108992c3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 31 Oct 2022 08:27:04 -0500 Subject: [PATCH 0116/1033] Try to switch to a different esphome BLE proxy if we run out of slots while connecting (#81268) --- homeassistant/components/bluetooth/models.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/bluetooth/models.py b/homeassistant/components/bluetooth/models.py index a2e50fe1182..a63a704baf6 100644 --- a/homeassistant/components/bluetooth/models.py +++ b/homeassistant/components/bluetooth/models.py @@ -264,6 +264,7 @@ class HaBleakClientWrapper(BleakClient): self.__address = address_or_ble_device self.__disconnected_callback = disconnected_callback self.__timeout = timeout + self.__ble_device: BLEDevice | None = None self._backend: BaseBleakClient | None = None # type: ignore[assignment] @property @@ -283,14 +284,21 @@ class HaBleakClientWrapper(BleakClient): async def connect(self, **kwargs: Any) -> bool: """Connect to the specified GATT server.""" - if not self._backend: + if ( + not self._backend + or not self.__ble_device + or not self._async_get_backend_for_ble_device(self.__ble_device) + ): assert MANAGER is not None wrapped_backend = ( self._async_get_backend() or self._async_get_fallback_backend() ) - self._backend = wrapped_backend.client( + self.__ble_device = ( await freshen_ble_device(wrapped_backend.device) - or wrapped_backend.device, + or wrapped_backend.device + ) + self._backend = wrapped_backend.client( + self.__ble_device, disconnected_callback=self.__disconnected_callback, timeout=self.__timeout, hass=MANAGER.hass, From 1589c06203c0bc9f87adcc97fe34d5c52aaf403a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 31 Oct 2022 08:35:08 -0500 Subject: [PATCH 0117/1033] Significantly reduce clock_gettime syscalls on platforms with broken vdso (#81257) --- .../bluetooth/active_update_coordinator.py | 6 ++--- homeassistant/components/bluetooth/manager.py | 4 +-- homeassistant/components/bluetooth/scanner.py | 4 +-- homeassistant/components/bluetooth/util.py | 4 +-- .../components/esphome/bluetooth/scanner.py | 3 ++- homeassistant/util/dt.py | 26 +++++++++++++++++++ tests/util/test_dt.py | 6 +++++ 7 files changed, 43 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/bluetooth/active_update_coordinator.py b/homeassistant/components/bluetooth/active_update_coordinator.py index 37f049d3e07..ab26a0260f3 100644 --- a/homeassistant/components/bluetooth/active_update_coordinator.py +++ b/homeassistant/components/bluetooth/active_update_coordinator.py @@ -3,13 +3,13 @@ from __future__ import annotations from collections.abc import Callable, Coroutine import logging -import time from typing import Any, Generic, TypeVar from bleak import BleakError from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.debounce import Debouncer +from homeassistant.util.dt import monotonic_time_coarse from . import BluetoothChange, BluetoothScanningMode, BluetoothServiceInfoBleak from .passive_update_processor import PassiveBluetoothProcessorCoordinator @@ -94,7 +94,7 @@ class ActiveBluetoothProcessorCoordinator( """Return true if time to try and poll.""" poll_age: float | None = None if self._last_poll: - poll_age = time.monotonic() - self._last_poll + poll_age = monotonic_time_coarse() - self._last_poll return self._needs_poll_method(service_info, poll_age) async def _async_poll_data( @@ -124,7 +124,7 @@ class ActiveBluetoothProcessorCoordinator( self.last_poll_successful = False return finally: - self._last_poll = time.monotonic() + self._last_poll = monotonic_time_coarse() if not self.last_poll_successful: self.logger.debug("%s: Polling recovered") diff --git a/homeassistant/components/bluetooth/manager.py b/homeassistant/components/bluetooth/manager.py index aaefd3dcfc4..c3a0e0998f1 100644 --- a/homeassistant/components/bluetooth/manager.py +++ b/homeassistant/components/bluetooth/manager.py @@ -7,7 +7,6 @@ from dataclasses import replace from datetime import datetime, timedelta import itertools import logging -import time from typing import TYPE_CHECKING, Any, Final from bleak.backends.scanner import AdvertisementDataCallback @@ -22,6 +21,7 @@ from homeassistant.core import ( ) from homeassistant.helpers import discovery_flow from homeassistant.helpers.event import async_track_time_interval +from homeassistant.util.dt import monotonic_time_coarse from .advertisement_tracker import AdvertisementTracker from .const import ( @@ -69,7 +69,7 @@ APPLE_START_BYTES_WANTED: Final = { APPLE_DEVICE_ID_START_BYTE, } -MONOTONIC_TIME: Final = time.monotonic +MONOTONIC_TIME: Final = monotonic_time_coarse _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/bluetooth/scanner.py b/homeassistant/components/bluetooth/scanner.py index fe795f7ace5..6b23cae0218 100644 --- a/homeassistant/components/bluetooth/scanner.py +++ b/homeassistant/components/bluetooth/scanner.py @@ -6,7 +6,6 @@ from collections.abc import Callable from datetime import datetime import logging import platform -import time from typing import Any import async_timeout @@ -22,6 +21,7 @@ from dbus_fast import InvalidMessageError from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.event import async_track_time_interval +from homeassistant.util.dt import monotonic_time_coarse from homeassistant.util.package import is_docker_env from .const import ( @@ -35,7 +35,7 @@ from .models import BaseHaScanner, BluetoothScanningMode, BluetoothServiceInfoBl from .util import adapter_human_name, async_reset_adapter OriginalBleakScanner = bleak.BleakScanner -MONOTONIC_TIME = time.monotonic +MONOTONIC_TIME = monotonic_time_coarse # or_patterns is a workaround for the fact that passive scanning # needs at least one matcher to be set. The below matcher diff --git a/homeassistant/components/bluetooth/util.py b/homeassistant/components/bluetooth/util.py index 860428a6106..181796d3d2d 100644 --- a/homeassistant/components/bluetooth/util.py +++ b/homeassistant/components/bluetooth/util.py @@ -2,11 +2,11 @@ from __future__ import annotations import platform -import time from bluetooth_auto_recovery import recover_adapter from homeassistant.core import callback +from homeassistant.util.dt import monotonic_time_coarse from .const import ( DEFAULT_ADAPTER_BY_PLATFORM, @@ -29,7 +29,7 @@ async def async_load_history_from_system() -> dict[str, BluetoothServiceInfoBlea bluez_dbus = BlueZDBusObjects() await bluez_dbus.load() - now = time.monotonic() + now = monotonic_time_coarse() return { address: BluetoothServiceInfoBleak( name=history.advertisement_data.local_name diff --git a/homeassistant/components/esphome/bluetooth/scanner.py b/homeassistant/components/esphome/bluetooth/scanner.py index 284e605fdfa..7c8064d5583 100644 --- a/homeassistant/components/esphome/bluetooth/scanner.py +++ b/homeassistant/components/esphome/bluetooth/scanner.py @@ -19,6 +19,7 @@ from homeassistant.components.bluetooth import ( ) from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.helpers.event import async_track_time_interval +from homeassistant.util.dt import monotonic_time_coarse TWO_CHAR = re.compile("..") @@ -84,7 +85,7 @@ class ESPHomeScanner(BaseHaScanner): @callback def async_on_advertisement(self, adv: BluetoothLEAdvertisement) -> None: """Call the registered callback.""" - now = time.monotonic() + now = monotonic_time_coarse() address = ":".join(TWO_CHAR.findall("%012X" % adv.address)) # must be upper name = adv.name if prev_discovery := self._discovered_device_advertisement_datas.get(address): diff --git a/homeassistant/util/dt.py b/homeassistant/util/dt.py index 80b322c1a14..44e4403d689 100644 --- a/homeassistant/util/dt.py +++ b/homeassistant/util/dt.py @@ -4,7 +4,9 @@ from __future__ import annotations import bisect from contextlib import suppress import datetime as dt +import platform import re +import time from typing import Any import zoneinfo @@ -13,6 +15,7 @@ import ciso8601 DATE_STR_FORMAT = "%Y-%m-%d" UTC = dt.timezone.utc DEFAULT_TIME_ZONE: dt.tzinfo = dt.timezone.utc +CLOCK_MONOTONIC_COARSE = 6 # EPOCHORDINAL is not exposed as a constant # https://github.com/python/cpython/blob/3.10/Lib/zoneinfo/_zoneinfo.py#L12 @@ -461,3 +464,26 @@ def _datetime_ambiguous(dattim: dt.datetime) -> bool: assert dattim.tzinfo is not None opposite_fold = dattim.replace(fold=not dattim.fold) return _datetime_exists(dattim) and dattim.utcoffset() != opposite_fold.utcoffset() + + +def __monotonic_time_coarse() -> float: + """Return a monotonic time in seconds. + + This is the coarse version of time_monotonic, which is faster but less accurate. + + Since many arm64 and 32-bit platforms don't support VDSO with time.monotonic + because of errata, we can't rely on the kernel to provide a fast + monotonic time. + + https://lore.kernel.org/lkml/20170404171826.25030-1-marc.zyngier@arm.com/ + """ + return time.clock_gettime(CLOCK_MONOTONIC_COARSE) + + +monotonic_time_coarse = time.monotonic +with suppress(Exception): + if ( + platform.system() == "Linux" + and abs(time.monotonic() - __monotonic_time_coarse()) < 1 + ): + monotonic_time_coarse = __monotonic_time_coarse diff --git a/tests/util/test_dt.py b/tests/util/test_dt.py index 79cd4e5e0df..e902176bb35 100644 --- a/tests/util/test_dt.py +++ b/tests/util/test_dt.py @@ -2,6 +2,7 @@ from __future__ import annotations from datetime import datetime, timedelta +import time import pytest @@ -719,3 +720,8 @@ def test_find_next_time_expression_tenth_second_pattern_does_not_drift_entering_ assert (next_target - prev_target).total_seconds() == 60 assert next_target.second == 10 prev_target = next_target + + +def test_monotonic_time_coarse(): + """Test monotonic time coarse.""" + assert abs(time.monotonic() - dt_util.monotonic_time_coarse()) < 1 From 82151bfd40f7b64044a5a9f82f020411430df97b Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Mon, 31 Oct 2022 09:57:54 -0400 Subject: [PATCH 0118/1033] Create repairs for unsupported and unhealthy (#80747) --- homeassistant/components/hassio/__init__.py | 6 + homeassistant/components/hassio/const.py | 21 +- homeassistant/components/hassio/handler.py | 8 + homeassistant/components/hassio/repairs.py | 138 ++++++ homeassistant/components/hassio/strings.json | 10 + tests/components/hassio/test_binary_sensor.py | 13 + tests/components/hassio/test_diagnostics.py | 13 + tests/components/hassio/test_init.py | 43 +- tests/components/hassio/test_repairs.py | 395 ++++++++++++++++++ tests/components/hassio/test_sensor.py | 13 + tests/components/hassio/test_update.py | 13 + tests/components/hassio/test_websocket_api.py | 13 + tests/components/http/test_ban.py | 12 +- tests/components/onboarding/test_views.py | 13 + 14 files changed, 690 insertions(+), 21 deletions(-) create mode 100644 homeassistant/components/hassio/repairs.py create mode 100644 tests/components/hassio/test_repairs.py diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 8535a0c3cc6..c811b35812e 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -77,6 +77,7 @@ from .discovery import HassioServiceInfo, async_setup_discovery_view # noqa: F4 from .handler import HassIO, HassioAPIError, api_data from .http import HassIOView from .ingress import async_setup_ingress_view +from .repairs import SupervisorRepairs from .websocket_api import async_load_websocket_api _LOGGER = logging.getLogger(__name__) @@ -103,6 +104,7 @@ DATA_SUPERVISOR_INFO = "hassio_supervisor_info" DATA_ADDONS_CHANGELOGS = "hassio_addons_changelogs" DATA_ADDONS_INFO = "hassio_addons_info" DATA_ADDONS_STATS = "hassio_addons_stats" +DATA_SUPERVISOR_REPAIRS = "supervisor_repairs" HASSIO_UPDATE_INTERVAL = timedelta(minutes=5) ADDONS_COORDINATOR = "hassio_addons_coordinator" @@ -758,6 +760,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa: hass.config_entries.flow.async_init(DOMAIN, context={"source": "system"}) ) + # Start listening for problems with supervisor and making repairs + hass.data[DATA_SUPERVISOR_REPAIRS] = repairs = SupervisorRepairs(hass, hassio) + await repairs.setup() + return True diff --git a/homeassistant/components/hassio/const.py b/homeassistant/components/hassio/const.py index e37a31ddbd6..64ef7a718a5 100644 --- a/homeassistant/components/hassio/const.py +++ b/homeassistant/components/hassio/const.py @@ -11,19 +11,26 @@ ATTR_CONFIG = "config" ATTR_DATA = "data" ATTR_DISCOVERY = "discovery" ATTR_ENABLE = "enable" +ATTR_ENDPOINT = "endpoint" ATTR_FOLDERS = "folders" +ATTR_HEALTHY = "healthy" ATTR_HOMEASSISTANT = "homeassistant" ATTR_INPUT = "input" +ATTR_METHOD = "method" ATTR_PANELS = "panels" ATTR_PASSWORD = "password" +ATTR_RESULT = "result" +ATTR_SUPPORTED = "supported" +ATTR_TIMEOUT = "timeout" ATTR_TITLE = "title" +ATTR_UNHEALTHY = "unhealthy" +ATTR_UNHEALTHY_REASONS = "unhealthy_reasons" +ATTR_UNSUPPORTED = "unsupported" +ATTR_UNSUPPORTED_REASONS = "unsupported_reasons" +ATTR_UPDATE_KEY = "update_key" ATTR_USERNAME = "username" ATTR_UUID = "uuid" ATTR_WS_EVENT = "event" -ATTR_ENDPOINT = "endpoint" -ATTR_METHOD = "method" -ATTR_RESULT = "result" -ATTR_TIMEOUT = "timeout" X_AUTH_TOKEN = "X-Supervisor-Token" X_INGRESS_PATH = "X-Ingress-Path" @@ -38,6 +45,11 @@ WS_TYPE_EVENT = "supervisor/event" WS_TYPE_SUBSCRIBE = "supervisor/subscribe" EVENT_SUPERVISOR_EVENT = "supervisor_event" +EVENT_SUPERVISOR_UPDATE = "supervisor_update" +EVENT_HEALTH_CHANGED = "health_changed" +EVENT_SUPPORTED_CHANGED = "supported_changed" + +UPDATE_KEY_SUPERVISOR = "supervisor" ATTR_AUTO_UPDATE = "auto_update" ATTR_VERSION = "version" @@ -51,7 +63,6 @@ ATTR_STARTED = "started" ATTR_URL = "url" ATTR_REPOSITORY = "repository" - DATA_KEY_ADDONS = "addons" DATA_KEY_OS = "os" DATA_KEY_SUPERVISOR = "supervisor" diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py index 7b3ed697227..ee16bdf8158 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -190,6 +190,14 @@ class HassIO: """ return self.send_command(f"/discovery/{uuid}", method="get") + @api_data + def get_resolution_info(self): + """Return data for Supervisor resolution center. + + This method return a coroutine. + """ + return self.send_command("/resolution/info", method="get") + @_api_bool async def update_hass_api(self, http_config, refresh_token): """Update Home Assistant API data on Hass.io.""" diff --git a/homeassistant/components/hassio/repairs.py b/homeassistant/components/hassio/repairs.py new file mode 100644 index 00000000000..a8c6788f4d5 --- /dev/null +++ b/homeassistant/components/hassio/repairs.py @@ -0,0 +1,138 @@ +"""Supervisor events monitor.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.issue_registry import ( + IssueSeverity, + async_create_issue, + async_delete_issue, +) + +from .const import ( + ATTR_DATA, + ATTR_HEALTHY, + ATTR_SUPPORTED, + ATTR_UNHEALTHY, + ATTR_UNHEALTHY_REASONS, + ATTR_UNSUPPORTED, + ATTR_UNSUPPORTED_REASONS, + ATTR_UPDATE_KEY, + ATTR_WS_EVENT, + DOMAIN, + EVENT_HEALTH_CHANGED, + EVENT_SUPERVISOR_EVENT, + EVENT_SUPERVISOR_UPDATE, + EVENT_SUPPORTED_CHANGED, + UPDATE_KEY_SUPERVISOR, +) +from .handler import HassIO + +ISSUE_ID_UNHEALTHY = "unhealthy_system" +ISSUE_ID_UNSUPPORTED = "unsupported_system" + +INFO_URL_UNHEALTHY = "https://www.home-assistant.io/more-info/unhealthy" +INFO_URL_UNSUPPORTED = "https://www.home-assistant.io/more-info/unsupported" + + +class SupervisorRepairs: + """Create repairs from supervisor events.""" + + def __init__(self, hass: HomeAssistant, client: HassIO) -> None: + """Initialize supervisor repairs.""" + self._hass = hass + self._client = client + self._unsupported_reasons: set[str] = set() + self._unhealthy_reasons: set[str] = set() + + @property + def unhealthy_reasons(self) -> set[str]: + """Get unhealthy reasons. Returns empty set if system is healthy.""" + return self._unhealthy_reasons + + @unhealthy_reasons.setter + def unhealthy_reasons(self, reasons: set[str]) -> None: + """Set unhealthy reasons. Create or delete repairs as necessary.""" + for unhealthy in reasons - self.unhealthy_reasons: + async_create_issue( + self._hass, + DOMAIN, + f"{ISSUE_ID_UNHEALTHY}_{unhealthy}", + is_fixable=False, + learn_more_url=f"{INFO_URL_UNHEALTHY}/{unhealthy}", + severity=IssueSeverity.CRITICAL, + translation_key="unhealthy", + translation_placeholders={"reason": unhealthy}, + ) + + for fixed in self.unhealthy_reasons - reasons: + async_delete_issue(self._hass, DOMAIN, f"{ISSUE_ID_UNHEALTHY}_{fixed}") + + self._unhealthy_reasons = reasons + + @property + def unsupported_reasons(self) -> set[str]: + """Get unsupported reasons. Returns empty set if system is supported.""" + return self._unsupported_reasons + + @unsupported_reasons.setter + def unsupported_reasons(self, reasons: set[str]) -> None: + """Set unsupported reasons. Create or delete repairs as necessary.""" + for unsupported in reasons - self.unsupported_reasons: + async_create_issue( + self._hass, + DOMAIN, + f"{ISSUE_ID_UNSUPPORTED}_{unsupported}", + is_fixable=False, + learn_more_url=f"{INFO_URL_UNSUPPORTED}/{unsupported}", + severity=IssueSeverity.WARNING, + translation_key="unsupported", + translation_placeholders={"reason": unsupported}, + ) + + for fixed in self.unsupported_reasons - reasons: + async_delete_issue(self._hass, DOMAIN, f"{ISSUE_ID_UNSUPPORTED}_{fixed}") + + self._unsupported_reasons = reasons + + async def setup(self) -> None: + """Create supervisor events listener.""" + await self.update() + + async_dispatcher_connect( + self._hass, EVENT_SUPERVISOR_EVENT, self._supervisor_events_to_repairs + ) + + async def update(self) -> None: + """Update repairs from Supervisor resolution center.""" + data = await self._client.get_resolution_info() + self.unhealthy_reasons = set(data[ATTR_UNHEALTHY]) + self.unsupported_reasons = set(data[ATTR_UNSUPPORTED]) + + @callback + def _supervisor_events_to_repairs(self, event: dict[str, Any]) -> None: + """Create repairs from supervisor events.""" + if ATTR_WS_EVENT not in event: + return + + if ( + event[ATTR_WS_EVENT] == EVENT_SUPERVISOR_UPDATE + and event.get(ATTR_UPDATE_KEY) == UPDATE_KEY_SUPERVISOR + ): + self._hass.async_create_task(self.update()) + + elif event[ATTR_WS_EVENT] == EVENT_HEALTH_CHANGED: + self.unhealthy_reasons = ( + set() + if event[ATTR_DATA][ATTR_HEALTHY] + else set(event[ATTR_DATA][ATTR_UNHEALTHY_REASONS]) + ) + + elif event[ATTR_WS_EVENT] == EVENT_SUPPORTED_CHANGED: + self.unsupported_reasons = ( + set() + if event[ATTR_DATA][ATTR_SUPPORTED] + else set(event[ATTR_DATA][ATTR_UNSUPPORTED_REASONS]) + ) diff --git a/homeassistant/components/hassio/strings.json b/homeassistant/components/hassio/strings.json index 90142bd453f..81b5ce01b79 100644 --- a/homeassistant/components/hassio/strings.json +++ b/homeassistant/components/hassio/strings.json @@ -15,5 +15,15 @@ "update_channel": "Update Channel", "version_api": "Version API" } + }, + "issues": { + "unhealthy": { + "title": "Unhealthy system - {reason}", + "description": "System is currently unhealthy due to '{reason}'. Use the link to learn more about what is wrong and how to fix it." + }, + "unsupported": { + "title": "Unsupported system - {reason}", + "description": "System is unsupported due to '{reason}'. Use the link to learn more about what this means and how to return to a supported system." + } } } diff --git a/tests/components/hassio/test_binary_sensor.py b/tests/components/hassio/test_binary_sensor.py index a601f98f1c5..c2dab178ad8 100644 --- a/tests/components/hassio/test_binary_sensor.py +++ b/tests/components/hassio/test_binary_sensor.py @@ -133,6 +133,19 @@ def mock_all(aioclient_mock, request): "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} ) aioclient_mock.post("http://127.0.0.1/refresh_updates", json={"result": "ok"}) + aioclient_mock.get( + "http://127.0.0.1/resolution/info", + json={ + "result": "ok", + "data": { + "unsupported": [], + "unhealthy": [], + "suggestions": [], + "issues": [], + "checks": [], + }, + }, + ) @pytest.mark.parametrize( diff --git a/tests/components/hassio/test_diagnostics.py b/tests/components/hassio/test_diagnostics.py index 1f915e17e61..9eaaf5f97d9 100644 --- a/tests/components/hassio/test_diagnostics.py +++ b/tests/components/hassio/test_diagnostics.py @@ -139,6 +139,19 @@ def mock_all(aioclient_mock, request): "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} ) aioclient_mock.post("http://127.0.0.1/refresh_updates", json={"result": "ok"}) + aioclient_mock.get( + "http://127.0.0.1/resolution/info", + json={ + "result": "ok", + "data": { + "unsupported": [], + "unhealthy": [], + "suggestions": [], + "issues": [], + "checks": [], + }, + }, + ) async def test_diagnostics( diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index f0f94661d50..371398e32c9 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -183,6 +183,19 @@ def mock_all(aioclient_mock, request, os_info): "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} ) aioclient_mock.post("http://127.0.0.1/refresh_updates", json={"result": "ok"}) + aioclient_mock.get( + "http://127.0.0.1/resolution/info", + json={ + "result": "ok", + "data": { + "unsupported": [], + "unhealthy": [], + "suggestions": [], + "issues": [], + "checks": [], + }, + }, + ) async def test_setup_api_ping(hass, aioclient_mock): @@ -191,7 +204,7 @@ async def test_setup_api_ping(hass, aioclient_mock): result = await async_setup_component(hass, "hassio", {}) assert result - assert aioclient_mock.call_count == 15 + assert aioclient_mock.call_count == 16 assert hass.components.hassio.get_core_info()["version_latest"] == "1.0.0" assert hass.components.hassio.is_hassio() @@ -230,7 +243,7 @@ async def test_setup_api_push_api_data(hass, aioclient_mock): ) assert result - assert aioclient_mock.call_count == 15 + assert aioclient_mock.call_count == 16 assert not aioclient_mock.mock_calls[1][2]["ssl"] assert aioclient_mock.mock_calls[1][2]["port"] == 9999 assert aioclient_mock.mock_calls[1][2]["watchdog"] @@ -246,7 +259,7 @@ async def test_setup_api_push_api_data_server_host(hass, aioclient_mock): ) assert result - assert aioclient_mock.call_count == 15 + assert aioclient_mock.call_count == 16 assert not aioclient_mock.mock_calls[1][2]["ssl"] assert aioclient_mock.mock_calls[1][2]["port"] == 9999 assert not aioclient_mock.mock_calls[1][2]["watchdog"] @@ -258,7 +271,7 @@ async def test_setup_api_push_api_data_default(hass, aioclient_mock, hass_storag result = await async_setup_component(hass, "hassio", {"http": {}, "hassio": {}}) assert result - assert aioclient_mock.call_count == 15 + assert aioclient_mock.call_count == 16 assert not aioclient_mock.mock_calls[1][2]["ssl"] assert aioclient_mock.mock_calls[1][2]["port"] == 8123 refresh_token = aioclient_mock.mock_calls[1][2]["refresh_token"] @@ -325,7 +338,7 @@ async def test_setup_api_existing_hassio_user(hass, aioclient_mock, hass_storage result = await async_setup_component(hass, "hassio", {"http": {}, "hassio": {}}) assert result - assert aioclient_mock.call_count == 15 + assert aioclient_mock.call_count == 16 assert not aioclient_mock.mock_calls[1][2]["ssl"] assert aioclient_mock.mock_calls[1][2]["port"] == 8123 assert aioclient_mock.mock_calls[1][2]["refresh_token"] == token.token @@ -339,7 +352,7 @@ async def test_setup_core_push_timezone(hass, aioclient_mock): result = await async_setup_component(hass, "hassio", {"hassio": {}}) assert result - assert aioclient_mock.call_count == 15 + assert aioclient_mock.call_count == 16 assert aioclient_mock.mock_calls[2][2]["timezone"] == "testzone" with patch("homeassistant.util.dt.set_default_time_zone"): @@ -356,7 +369,7 @@ async def test_setup_hassio_no_additional_data(hass, aioclient_mock): result = await async_setup_component(hass, "hassio", {"hassio": {}}) assert result - assert aioclient_mock.call_count == 15 + assert aioclient_mock.call_count == 16 assert aioclient_mock.mock_calls[-1][3]["Authorization"] == "Bearer 123456" @@ -426,14 +439,14 @@ async def test_service_calls(hassio_env, hass, aioclient_mock, caplog): ) await hass.async_block_till_done() - assert aioclient_mock.call_count == 9 + assert aioclient_mock.call_count == 10 assert aioclient_mock.mock_calls[-1][2] == "test" await hass.services.async_call("hassio", "host_shutdown", {}) await hass.services.async_call("hassio", "host_reboot", {}) await hass.async_block_till_done() - assert aioclient_mock.call_count == 11 + assert aioclient_mock.call_count == 12 await hass.services.async_call("hassio", "backup_full", {}) await hass.services.async_call( @@ -448,7 +461,7 @@ async def test_service_calls(hassio_env, hass, aioclient_mock, caplog): ) await hass.async_block_till_done() - assert aioclient_mock.call_count == 13 + assert aioclient_mock.call_count == 14 assert aioclient_mock.mock_calls[-1][2] == { "homeassistant": True, "addons": ["test"], @@ -472,7 +485,7 @@ async def test_service_calls(hassio_env, hass, aioclient_mock, caplog): ) await hass.async_block_till_done() - assert aioclient_mock.call_count == 15 + assert aioclient_mock.call_count == 16 assert aioclient_mock.mock_calls[-1][2] == { "addons": ["test"], "folders": ["ssl"], @@ -491,12 +504,12 @@ async def test_service_calls_core(hassio_env, hass, aioclient_mock): await hass.services.async_call("homeassistant", "stop") await hass.async_block_till_done() - assert aioclient_mock.call_count == 5 + assert aioclient_mock.call_count == 6 await hass.services.async_call("homeassistant", "check_config") await hass.async_block_till_done() - assert aioclient_mock.call_count == 5 + assert aioclient_mock.call_count == 6 with patch( "homeassistant.config.async_check_ha_config_file", return_value=None @@ -505,7 +518,7 @@ async def test_service_calls_core(hassio_env, hass, aioclient_mock): await hass.async_block_till_done() assert mock_check_config.called - assert aioclient_mock.call_count == 6 + assert aioclient_mock.call_count == 7 async def test_entry_load_and_unload(hass): @@ -758,7 +771,7 @@ async def test_setup_hardware_integration(hass, aioclient_mock, integration): assert result await hass.async_block_till_done() - assert aioclient_mock.call_count == 15 + assert aioclient_mock.call_count == 16 assert len(mock_setup_entry.mock_calls) == 1 diff --git a/tests/components/hassio/test_repairs.py b/tests/components/hassio/test_repairs.py new file mode 100644 index 00000000000..ebaf46be3b5 --- /dev/null +++ b/tests/components/hassio/test_repairs.py @@ -0,0 +1,395 @@ +"""Test repairs from supervisor issues.""" + +from __future__ import annotations + +import os +from typing import Any +from unittest.mock import ANY, patch + +import pytest + +from homeassistant.components.repairs import DOMAIN as REPAIRS_DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from .test_init import MOCK_ENVIRON + +from tests.test_util.aiohttp import AiohttpClientMocker + + +@pytest.fixture(autouse=True) +async def setup_repairs(hass): + """Set up the repairs integration.""" + assert await async_setup_component(hass, REPAIRS_DOMAIN, {REPAIRS_DOMAIN: {}}) + + +@pytest.fixture(autouse=True) +def mock_all(aioclient_mock: AiohttpClientMocker, request: pytest.FixtureRequest): + """Mock all setup requests.""" + aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"}) + aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={"result": "ok"}) + aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"}) + aioclient_mock.get( + "http://127.0.0.1/info", + json={ + "result": "ok", + "data": { + "supervisor": "222", + "homeassistant": "0.110.0", + "hassos": "1.2.3", + }, + }, + ) + aioclient_mock.get( + "http://127.0.0.1/store", + json={ + "result": "ok", + "data": {"addons": [], "repositories": []}, + }, + ) + aioclient_mock.get( + "http://127.0.0.1/host/info", + json={ + "result": "ok", + "data": { + "result": "ok", + "data": { + "chassis": "vm", + "operating_system": "Debian GNU/Linux 10 (buster)", + "kernel": "4.19.0-6-amd64", + }, + }, + }, + ) + aioclient_mock.get( + "http://127.0.0.1/core/info", + json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}}, + ) + aioclient_mock.get( + "http://127.0.0.1/os/info", + json={ + "result": "ok", + "data": { + "version_latest": "1.0.0", + "version": "1.0.0", + "update_available": False, + }, + }, + ) + aioclient_mock.get( + "http://127.0.0.1/supervisor/info", + json={ + "result": "ok", + "data": { + "result": "ok", + "version": "1.0.0", + "version_latest": "1.0.0", + "auto_update": True, + "addons": [], + }, + }, + ) + aioclient_mock.get( + "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} + ) + aioclient_mock.post("http://127.0.0.1/refresh_updates", json={"result": "ok"}) + + +@pytest.fixture(autouse=True) +async def fixture_supervisor_environ(): + """Mock os environ for supervisor.""" + with patch.dict(os.environ, MOCK_ENVIRON): + yield + + +def mock_resolution_info( + aioclient_mock: AiohttpClientMocker, + unsupported: list[str] | None = None, + unhealthy: list[str] | None = None, +): + """Mock resolution/info endpoint with unsupported/unhealthy reasons.""" + aioclient_mock.get( + "http://127.0.0.1/resolution/info", + json={ + "result": "ok", + "data": { + "unsupported": unsupported or [], + "unhealthy": unhealthy or [], + "suggestions": [], + "issues": [], + "checks": [ + {"enabled": True, "slug": "supervisor_trust"}, + {"enabled": True, "slug": "free_space"}, + ], + }, + }, + ) + + +def assert_repair_in_list(issues: list[dict[str, Any]], unhealthy: bool, reason: str): + """Assert repair for unhealthy/unsupported in list.""" + repair_type = "unhealthy" if unhealthy else "unsupported" + assert { + "breaks_in_ha_version": None, + "created": ANY, + "dismissed_version": None, + "domain": "hassio", + "ignored": False, + "is_fixable": False, + "issue_id": f"{repair_type}_system_{reason}", + "issue_domain": None, + "learn_more_url": f"https://www.home-assistant.io/more-info/{repair_type}/{reason}", + "severity": "critical" if unhealthy else "warning", + "translation_key": repair_type, + "translation_placeholders": { + "reason": reason, + }, + } in issues + + +async def test_unhealthy_repairs( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + hass_ws_client, +): + """Test repairs added for unhealthy systems.""" + mock_resolution_info(aioclient_mock, unhealthy=["docker", "setup"]) + + result = await async_setup_component(hass, "hassio", {}) + assert result + + client = await hass_ws_client(hass) + + await client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await client.receive_json() + assert msg["success"] + assert len(msg["result"]["issues"]) == 2 + assert_repair_in_list(msg["result"]["issues"], unhealthy=True, reason="docker") + assert_repair_in_list(msg["result"]["issues"], unhealthy=True, reason="setup") + + +async def test_unsupported_repairs( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + hass_ws_client, +): + """Test repairs added for unsupported systems.""" + mock_resolution_info(aioclient_mock, unsupported=["content_trust", "os"]) + + result = await async_setup_component(hass, "hassio", {}) + assert result + + client = await hass_ws_client(hass) + + await client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await client.receive_json() + assert msg["success"] + assert len(msg["result"]["issues"]) == 2 + assert_repair_in_list( + msg["result"]["issues"], unhealthy=False, reason="content_trust" + ) + assert_repair_in_list(msg["result"]["issues"], unhealthy=False, reason="os") + + +async def test_unhealthy_repairs_add_remove( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + hass_ws_client, +): + """Test unhealthy repairs added and removed from dispatches.""" + mock_resolution_info(aioclient_mock) + + result = await async_setup_component(hass, "hassio", {}) + assert result + + client = await hass_ws_client(hass) + + await client.send_json( + { + "id": 1, + "type": "supervisor/event", + "data": { + "event": "health_changed", + "data": { + "healthy": False, + "unhealthy_reasons": ["docker"], + }, + }, + } + ) + msg = await client.receive_json() + assert msg["success"] + await hass.async_block_till_done() + + await client.send_json({"id": 2, "type": "repairs/list_issues"}) + msg = await client.receive_json() + assert msg["success"] + assert len(msg["result"]["issues"]) == 1 + assert_repair_in_list(msg["result"]["issues"], unhealthy=True, reason="docker") + + await client.send_json( + { + "id": 3, + "type": "supervisor/event", + "data": { + "event": "health_changed", + "data": {"healthy": True}, + }, + } + ) + msg = await client.receive_json() + assert msg["success"] + await hass.async_block_till_done() + + await client.send_json({"id": 4, "type": "repairs/list_issues"}) + msg = await client.receive_json() + assert msg["success"] + assert msg["result"] == {"issues": []} + + +async def test_unsupported_repairs_add_remove( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + hass_ws_client, +): + """Test unsupported repairs added and removed from dispatches.""" + mock_resolution_info(aioclient_mock) + + result = await async_setup_component(hass, "hassio", {}) + assert result + + client = await hass_ws_client(hass) + + await client.send_json( + { + "id": 1, + "type": "supervisor/event", + "data": { + "event": "supported_changed", + "data": { + "supported": False, + "unsupported_reasons": ["os"], + }, + }, + } + ) + msg = await client.receive_json() + assert msg["success"] + await hass.async_block_till_done() + + await client.send_json({"id": 2, "type": "repairs/list_issues"}) + msg = await client.receive_json() + assert msg["success"] + assert len(msg["result"]["issues"]) == 1 + assert_repair_in_list(msg["result"]["issues"], unhealthy=False, reason="os") + + await client.send_json( + { + "id": 3, + "type": "supervisor/event", + "data": { + "event": "supported_changed", + "data": {"supported": True}, + }, + } + ) + msg = await client.receive_json() + assert msg["success"] + await hass.async_block_till_done() + + await client.send_json({"id": 4, "type": "repairs/list_issues"}) + msg = await client.receive_json() + assert msg["success"] + assert msg["result"] == {"issues": []} + + +async def test_reset_repairs_supervisor_restart( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + hass_ws_client, +): + """Unsupported/unhealthy repairs reset on supervisor restart.""" + mock_resolution_info(aioclient_mock, unsupported=["os"], unhealthy=["docker"]) + + result = await async_setup_component(hass, "hassio", {}) + assert result + + client = await hass_ws_client(hass) + + await client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await client.receive_json() + assert msg["success"] + assert len(msg["result"]["issues"]) == 2 + assert_repair_in_list(msg["result"]["issues"], unhealthy=True, reason="docker") + assert_repair_in_list(msg["result"]["issues"], unhealthy=False, reason="os") + + aioclient_mock.clear_requests() + mock_resolution_info(aioclient_mock) + await client.send_json( + { + "id": 2, + "type": "supervisor/event", + "data": { + "event": "supervisor_update", + "update_key": "supervisor", + "data": {}, + }, + } + ) + msg = await client.receive_json() + assert msg["success"] + await hass.async_block_till_done() + + await client.send_json({"id": 3, "type": "repairs/list_issues"}) + msg = await client.receive_json() + assert msg["success"] + assert msg["result"] == {"issues": []} + + +async def test_reasons_added_and_removed( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + hass_ws_client, +): + """Test an unsupported/unhealthy reasons being added and removed at same time.""" + mock_resolution_info(aioclient_mock, unsupported=["os"], unhealthy=["docker"]) + + result = await async_setup_component(hass, "hassio", {}) + assert result + + client = await hass_ws_client(hass) + + await client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await client.receive_json() + assert msg["success"] + assert len(msg["result"]["issues"]) == 2 + assert_repair_in_list(msg["result"]["issues"], unhealthy=True, reason="docker") + assert_repair_in_list(msg["result"]["issues"], unhealthy=False, reason="os") + + aioclient_mock.clear_requests() + mock_resolution_info( + aioclient_mock, unsupported=["content_trust"], unhealthy=["setup"] + ) + await client.send_json( + { + "id": 2, + "type": "supervisor/event", + "data": { + "event": "supervisor_update", + "update_key": "supervisor", + "data": {}, + }, + } + ) + msg = await client.receive_json() + assert msg["success"] + await hass.async_block_till_done() + + await client.send_json({"id": 3, "type": "repairs/list_issues"}) + msg = await client.receive_json() + assert msg["success"] + assert len(msg["result"]["issues"]) == 2 + assert_repair_in_list(msg["result"]["issues"], unhealthy=True, reason="setup") + assert_repair_in_list( + msg["result"]["issues"], unhealthy=False, reason="content_trust" + ) diff --git a/tests/components/hassio/test_sensor.py b/tests/components/hassio/test_sensor.py index 16cce09b800..e9f0bd631b0 100644 --- a/tests/components/hassio/test_sensor.py +++ b/tests/components/hassio/test_sensor.py @@ -126,6 +126,19 @@ def mock_all(aioclient_mock, request): "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} ) aioclient_mock.post("http://127.0.0.1/refresh_updates", json={"result": "ok"}) + aioclient_mock.get( + "http://127.0.0.1/resolution/info", + json={ + "result": "ok", + "data": { + "unsupported": [], + "unhealthy": [], + "suggestions": [], + "issues": [], + "checks": [], + }, + }, + ) @pytest.mark.parametrize( diff --git a/tests/components/hassio/test_update.py b/tests/components/hassio/test_update.py index aaa77cde129..02d6b1dbf6b 100644 --- a/tests/components/hassio/test_update.py +++ b/tests/components/hassio/test_update.py @@ -139,6 +139,19 @@ def mock_all(aioclient_mock, request): "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} ) aioclient_mock.post("http://127.0.0.1/refresh_updates", json={"result": "ok"}) + aioclient_mock.get( + "http://127.0.0.1/resolution/info", + json={ + "result": "ok", + "data": { + "unsupported": [], + "unhealthy": [], + "suggestions": [], + "issues": [], + "checks": [], + }, + }, + ) @pytest.mark.parametrize( diff --git a/tests/components/hassio/test_websocket_api.py b/tests/components/hassio/test_websocket_api.py index 5d11d13166e..767f0abaf35 100644 --- a/tests/components/hassio/test_websocket_api.py +++ b/tests/components/hassio/test_websocket_api.py @@ -61,6 +61,19 @@ def mock_all(aioclient_mock): aioclient_mock.get( "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} ) + aioclient_mock.get( + "http://127.0.0.1/resolution/info", + json={ + "result": "ok", + "data": { + "unsupported": [], + "unhealthy": [], + "suggestions": [], + "issues": [], + "checks": [], + }, + }, + ) async def test_ws_subscription(hassio_env, hass: HomeAssistant, hass_ws_client): diff --git a/tests/components/http/test_ban.py b/tests/components/http/test_ban.py index 7a4202c1a67..a4249a1efb6 100644 --- a/tests/components/http/test_ban.py +++ b/tests/components/http/test_ban.py @@ -198,7 +198,17 @@ async def test_access_from_supervisor_ip( manager: IpBanManager = app[KEY_BAN_MANAGER] - assert await async_setup_component(hass, "hassio", {"hassio": {}}) + with patch( + "homeassistant.components.hassio.HassIO.get_resolution_info", + return_value={ + "unsupported": [], + "unhealthy": [], + "suggestions": [], + "issues": [], + "checks": [], + }, + ): + assert await async_setup_component(hass, "hassio", {"hassio": {}}) m_open = mock_open() diff --git a/tests/components/onboarding/test_views.py b/tests/components/onboarding/test_views.py index 204eb6bf772..40d889185dd 100644 --- a/tests/components/onboarding/test_views.py +++ b/tests/components/onboarding/test_views.py @@ -57,6 +57,19 @@ async def mock_supervisor_fixture(hass, aioclient_mock): """Mock supervisor.""" aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"}) aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"}) + aioclient_mock.get( + "http://127.0.0.1/resolution/info", + json={ + "result": "ok", + "data": { + "unsupported": [], + "unhealthy": [], + "suggestions": [], + "issues": [], + "checks": [], + }, + }, + ) with patch.dict(os.environ, {"SUPERVISOR": "127.0.0.1"}), patch( "homeassistant.components.hassio.HassIO.is_connected", return_value=True, From a0ed91e30c0d4f3db1d1961fd6433328c64002b7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 31 Oct 2022 15:30:29 +0100 Subject: [PATCH 0119/1033] Add type hints to rest tests (#81304) --- tests/components/rest/test_binary_sensor.py | 35 ++++----- tests/components/rest/test_init.py | 13 ++-- tests/components/rest/test_notify.py | 3 +- tests/components/rest/test_sensor.py | 75 ++++++++++++-------- tests/components/rest/test_switch.py | 78 +++++++++++++++------ 5 files changed, 130 insertions(+), 74 deletions(-) diff --git a/tests/components/rest/test_binary_sensor.py b/tests/components/rest/test_binary_sensor.py index a6655f6ddbc..757c331529e 100644 --- a/tests/components/rest/test_binary_sensor.py +++ b/tests/components/rest/test_binary_sensor.py @@ -5,6 +5,7 @@ from http import HTTPStatus from unittest.mock import MagicMock, patch import httpx +import pytest import respx from homeassistant import config as hass_config @@ -26,7 +27,7 @@ from homeassistant.setup import async_setup_component from tests.common import get_fixture_path -async def test_setup_missing_basic_config(hass): +async def test_setup_missing_basic_config(hass: HomeAssistant) -> None: """Test setup with configuration missing required entries.""" assert await async_setup_component( hass, Platform.BINARY_SENSOR, {"binary_sensor": {"platform": "rest"}} @@ -35,7 +36,7 @@ async def test_setup_missing_basic_config(hass): assert len(hass.states.async_all("binary_sensor")) == 0 -async def test_setup_missing_config(hass): +async def test_setup_missing_config(hass: HomeAssistant) -> None: """Test setup with configuration missing required entries.""" assert await async_setup_component( hass, @@ -53,7 +54,9 @@ async def test_setup_missing_config(hass): @respx.mock -async def test_setup_failed_connect(hass, caplog): +async def test_setup_failed_connect( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: """Test setup when connection error occurs.""" respx.get("http://localhost").mock( @@ -76,7 +79,7 @@ async def test_setup_failed_connect(hass, caplog): @respx.mock -async def test_setup_timeout(hass): +async def test_setup_timeout(hass: HomeAssistant) -> None: """Test setup when connection timeout occurs.""" respx.get("http://localhost").mock(side_effect=asyncio.TimeoutError()) assert await async_setup_component( @@ -95,7 +98,7 @@ async def test_setup_timeout(hass): @respx.mock -async def test_setup_minimum(hass): +async def test_setup_minimum(hass: HomeAssistant) -> None: """Test setup with minimum configuration.""" respx.get("http://localhost") % HTTPStatus.OK assert await async_setup_component( @@ -114,7 +117,7 @@ async def test_setup_minimum(hass): @respx.mock -async def test_setup_minimum_resource_template(hass): +async def test_setup_minimum_resource_template(hass: HomeAssistant) -> None: """Test setup with minimum configuration (resource_template).""" respx.get("http://localhost") % HTTPStatus.OK assert await async_setup_component( @@ -132,7 +135,7 @@ async def test_setup_minimum_resource_template(hass): @respx.mock -async def test_setup_duplicate_resource_template(hass): +async def test_setup_duplicate_resource_template(hass: HomeAssistant) -> None: """Test setup with duplicate resources.""" respx.get("http://localhost") % HTTPStatus.OK assert await async_setup_component( @@ -151,7 +154,7 @@ async def test_setup_duplicate_resource_template(hass): @respx.mock -async def test_setup_get(hass): +async def test_setup_get(hass: HomeAssistant) -> None: """Test setup with valid configuration.""" respx.get("http://localhost").respond(status_code=HTTPStatus.OK, json={}) assert await async_setup_component( @@ -184,7 +187,7 @@ async def test_setup_get(hass): @respx.mock -async def test_setup_get_template_headers_params(hass): +async def test_setup_get_template_headers_params(hass: HomeAssistant) -> None: """Test setup with valid configuration.""" respx.get("http://localhost").respond(status_code=200, json={}) assert await async_setup_component( @@ -218,7 +221,7 @@ async def test_setup_get_template_headers_params(hass): @respx.mock -async def test_setup_get_digest_auth(hass): +async def test_setup_get_digest_auth(hass: HomeAssistant) -> None: """Test setup with valid configuration.""" respx.get("http://localhost").respond(status_code=HTTPStatus.OK, json={}) assert await async_setup_component( @@ -246,7 +249,7 @@ async def test_setup_get_digest_auth(hass): @respx.mock -async def test_setup_post(hass): +async def test_setup_post(hass: HomeAssistant) -> None: """Test setup with valid configuration.""" respx.post("http://localhost").respond(status_code=HTTPStatus.OK, json={}) assert await async_setup_component( @@ -274,7 +277,7 @@ async def test_setup_post(hass): @respx.mock -async def test_setup_get_off(hass): +async def test_setup_get_off(hass: HomeAssistant) -> None: """Test setup with valid off configuration.""" respx.get("http://localhost").respond( status_code=HTTPStatus.OK, @@ -304,7 +307,7 @@ async def test_setup_get_off(hass): @respx.mock -async def test_setup_get_on(hass): +async def test_setup_get_on(hass: HomeAssistant) -> None: """Test setup with valid on configuration.""" respx.get("http://localhost").respond( status_code=HTTPStatus.OK, @@ -334,7 +337,7 @@ async def test_setup_get_on(hass): @respx.mock -async def test_setup_with_exception(hass): +async def test_setup_with_exception(hass: HomeAssistant) -> None: """Test setup with exception.""" respx.get("http://localhost").respond(status_code=HTTPStatus.OK, json={}) assert await async_setup_component( @@ -376,7 +379,7 @@ async def test_setup_with_exception(hass): @respx.mock -async def test_reload(hass): +async def test_reload(hass: HomeAssistant) -> None: """Verify we can reload reset sensors.""" respx.get("http://localhost") % HTTPStatus.OK @@ -416,7 +419,7 @@ async def test_reload(hass): @respx.mock -async def test_setup_query_params(hass): +async def test_setup_query_params(hass: HomeAssistant) -> None: """Test setup with query params.""" respx.get("http://localhost", params={"search": "something"}) % HTTPStatus.OK assert await async_setup_component( diff --git a/tests/components/rest/test_init.py b/tests/components/rest/test_init.py index 988f88b348e..6dd2650c25c 100644 --- a/tests/components/rest/test_init.py +++ b/tests/components/rest/test_init.py @@ -15,6 +15,7 @@ from homeassistant.const import ( SERVICE_RELOAD, STATE_UNAVAILABLE, ) +from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component from homeassistant.util.dt import utcnow @@ -22,7 +23,7 @@ from tests.common import async_fire_time_changed, get_fixture_path @respx.mock -async def test_setup_with_endpoint_timeout_with_recovery(hass): +async def test_setup_with_endpoint_timeout_with_recovery(hass: HomeAssistant) -> None: """Test setup with an endpoint that times out that recovers.""" await async_setup_component(hass, "homeassistant", {}) @@ -129,7 +130,7 @@ async def test_setup_with_endpoint_timeout_with_recovery(hass): @respx.mock -async def test_setup_minimum_resource_template(hass): +async def test_setup_minimum_resource_template(hass: HomeAssistant) -> None: """Test setup with minimum configuration (resource_template).""" respx.get("http://localhost").respond( @@ -187,7 +188,7 @@ async def test_setup_minimum_resource_template(hass): @respx.mock -async def test_reload(hass): +async def test_reload(hass: HomeAssistant) -> None: """Verify we can reload.""" respx.get("http://localhost") % HTTPStatus.OK @@ -236,7 +237,7 @@ async def test_reload(hass): @respx.mock -async def test_reload_and_remove_all(hass): +async def test_reload_and_remove_all(hass: HomeAssistant) -> None: """Verify we can reload and remove all.""" respx.get("http://localhost") % HTTPStatus.OK @@ -283,7 +284,7 @@ async def test_reload_and_remove_all(hass): @respx.mock -async def test_reload_fails_to_read_configuration(hass): +async def test_reload_fails_to_read_configuration(hass: HomeAssistant) -> None: """Verify reload when configuration is missing or broken.""" respx.get("http://localhost") % HTTPStatus.OK @@ -327,7 +328,7 @@ async def test_reload_fails_to_read_configuration(hass): @respx.mock -async def test_multiple_rest_endpoints(hass): +async def test_multiple_rest_endpoints(hass: HomeAssistant) -> None: """Test multiple rest endpoints.""" respx.get("http://date.jsontest.com").respond( diff --git a/tests/components/rest/test_notify.py b/tests/components/rest/test_notify.py index 31567ae63f0..f9a2e88c732 100644 --- a/tests/components/rest/test_notify.py +++ b/tests/components/rest/test_notify.py @@ -7,13 +7,14 @@ from homeassistant import config as hass_config import homeassistant.components.notify as notify from homeassistant.components.rest import DOMAIN from homeassistant.const import SERVICE_RELOAD +from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component from tests.common import get_fixture_path @respx.mock -async def test_reload_notify(hass): +async def test_reload_notify(hass: HomeAssistant) -> None: """Verify we can reload the notify service.""" respx.get("http://localhost") % 200 diff --git a/tests/components/rest/test_sensor.py b/tests/components/rest/test_sensor.py index a89d20f2510..49ad69b1caa 100644 --- a/tests/components/rest/test_sensor.py +++ b/tests/components/rest/test_sensor.py @@ -4,6 +4,7 @@ from http import HTTPStatus from unittest.mock import MagicMock, patch import httpx +import pytest import respx from homeassistant import config as hass_config @@ -31,14 +32,14 @@ from homeassistant.setup import async_setup_component from tests.common import get_fixture_path -async def test_setup_missing_config(hass): +async def test_setup_missing_config(hass: HomeAssistant) -> None: """Test setup with configuration missing required entries.""" assert await async_setup_component(hass, DOMAIN, {"sensor": {"platform": "rest"}}) await hass.async_block_till_done() assert len(hass.states.async_all("sensor")) == 0 -async def test_setup_missing_schema(hass): +async def test_setup_missing_schema(hass: HomeAssistant) -> None: """Test setup with resource missing schema.""" assert await async_setup_component( hass, @@ -50,7 +51,9 @@ async def test_setup_missing_schema(hass): @respx.mock -async def test_setup_failed_connect(hass, caplog): +async def test_setup_failed_connect( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: """Test setup when connection error occurs.""" respx.get("http://localhost").mock( side_effect=httpx.RequestError("server offline", request=MagicMock()) @@ -72,7 +75,7 @@ async def test_setup_failed_connect(hass, caplog): @respx.mock -async def test_setup_timeout(hass): +async def test_setup_timeout(hass: HomeAssistant) -> None: """Test setup when connection timeout occurs.""" respx.get("http://localhost").mock(side_effect=asyncio.TimeoutError()) assert await async_setup_component( @@ -85,7 +88,7 @@ async def test_setup_timeout(hass): @respx.mock -async def test_setup_minimum(hass): +async def test_setup_minimum(hass: HomeAssistant) -> None: """Test setup with minimum configuration.""" respx.get("http://localhost") % HTTPStatus.OK assert await async_setup_component( @@ -104,7 +107,7 @@ async def test_setup_minimum(hass): @respx.mock -async def test_manual_update(hass): +async def test_manual_update(hass: HomeAssistant) -> None: """Test setup with minimum configuration.""" await async_setup_component(hass, "homeassistant", {}) respx.get("http://localhost").respond( @@ -140,7 +143,7 @@ async def test_manual_update(hass): @respx.mock -async def test_setup_minimum_resource_template(hass): +async def test_setup_minimum_resource_template(hass: HomeAssistant) -> None: """Test setup with minimum configuration (resource_template).""" respx.get("http://localhost") % HTTPStatus.OK assert await async_setup_component( @@ -158,7 +161,7 @@ async def test_setup_minimum_resource_template(hass): @respx.mock -async def test_setup_duplicate_resource_template(hass): +async def test_setup_duplicate_resource_template(hass: HomeAssistant) -> None: """Test setup with duplicate resources.""" respx.get("http://localhost") % HTTPStatus.OK assert await async_setup_component( @@ -177,7 +180,7 @@ async def test_setup_duplicate_resource_template(hass): @respx.mock -async def test_setup_get(hass): +async def test_setup_get(hass: HomeAssistant) -> None: """Test setup with valid configuration.""" respx.get("http://localhost").respond(status_code=HTTPStatus.OK, json={}) assert await async_setup_component( @@ -223,7 +226,9 @@ async def test_setup_get(hass): @respx.mock -async def test_setup_timestamp(hass, caplog): +async def test_setup_timestamp( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: """Test setup with valid configuration.""" respx.get("http://localhost").respond( status_code=HTTPStatus.OK, json={"key": "2021-11-11 11:39Z"} @@ -286,7 +291,7 @@ async def test_setup_timestamp(hass, caplog): @respx.mock -async def test_setup_get_templated_headers_params(hass): +async def test_setup_get_templated_headers_params(hass: HomeAssistant) -> None: """Test setup with valid configuration.""" respx.get("http://localhost").respond(status_code=200, json={}) assert await async_setup_component( @@ -320,7 +325,7 @@ async def test_setup_get_templated_headers_params(hass): @respx.mock -async def test_setup_get_digest_auth(hass): +async def test_setup_get_digest_auth(hass: HomeAssistant) -> None: """Test setup with valid configuration.""" respx.get("http://localhost").respond(status_code=HTTPStatus.OK, json={}) assert await async_setup_component( @@ -349,7 +354,7 @@ async def test_setup_get_digest_auth(hass): @respx.mock -async def test_setup_post(hass): +async def test_setup_post(hass: HomeAssistant) -> None: """Test setup with valid configuration.""" respx.post("http://localhost").respond(status_code=HTTPStatus.OK, json={}) assert await async_setup_component( @@ -378,7 +383,7 @@ async def test_setup_post(hass): @respx.mock -async def test_setup_get_xml(hass): +async def test_setup_get_xml(hass: HomeAssistant) -> None: """Test setup with valid xml configuration.""" respx.get("http://localhost").respond( status_code=HTTPStatus.OK, @@ -410,7 +415,7 @@ async def test_setup_get_xml(hass): @respx.mock -async def test_setup_query_params(hass): +async def test_setup_query_params(hass: HomeAssistant) -> None: """Test setup with query params.""" respx.get("http://localhost", params={"search": "something"}) % HTTPStatus.OK assert await async_setup_component( @@ -430,7 +435,7 @@ async def test_setup_query_params(hass): @respx.mock -async def test_update_with_json_attrs(hass): +async def test_update_with_json_attrs(hass: HomeAssistant) -> None: """Test attributes get extracted from a JSON result.""" respx.get("http://localhost").respond( @@ -463,7 +468,7 @@ async def test_update_with_json_attrs(hass): @respx.mock -async def test_update_with_no_template(hass): +async def test_update_with_no_template(hass: HomeAssistant) -> None: """Test update when there is no value template.""" respx.get("http://localhost").respond( @@ -495,7 +500,9 @@ async def test_update_with_no_template(hass): @respx.mock -async def test_update_with_json_attrs_no_data(hass, caplog): +async def test_update_with_json_attrs_no_data( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: """Test attributes when no JSON result fetched.""" respx.get("http://localhost").respond( @@ -531,7 +538,9 @@ async def test_update_with_json_attrs_no_data(hass, caplog): @respx.mock -async def test_update_with_json_attrs_not_dict(hass, caplog): +async def test_update_with_json_attrs_not_dict( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: """Test attributes get extracted from a JSON result.""" respx.get("http://localhost").respond( @@ -566,7 +575,9 @@ async def test_update_with_json_attrs_not_dict(hass, caplog): @respx.mock -async def test_update_with_json_attrs_bad_JSON(hass, caplog): +async def test_update_with_json_attrs_bad_JSON( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: """Test attributes get extracted from a JSON result.""" respx.get("http://localhost").respond( @@ -602,7 +613,7 @@ async def test_update_with_json_attrs_bad_JSON(hass, caplog): @respx.mock -async def test_update_with_json_attrs_with_json_attrs_path(hass): +async def test_update_with_json_attrs_with_json_attrs_path(hass: HomeAssistant) -> None: """Test attributes get extracted from a JSON result with a template for the attributes.""" respx.get("http://localhost").respond( @@ -646,7 +657,9 @@ async def test_update_with_json_attrs_with_json_attrs_path(hass): @respx.mock -async def test_update_with_xml_convert_json_attrs_with_json_attrs_path(hass): +async def test_update_with_xml_convert_json_attrs_with_json_attrs_path( + hass: HomeAssistant, +) -> None: """Test attributes get extracted from a JSON result that was converted from XML with a template for the attributes.""" respx.get("http://localhost").respond( @@ -682,7 +695,9 @@ async def test_update_with_xml_convert_json_attrs_with_json_attrs_path(hass): @respx.mock -async def test_update_with_xml_convert_json_attrs_with_jsonattr_template(hass): +async def test_update_with_xml_convert_json_attrs_with_jsonattr_template( + hass: HomeAssistant, +) -> None: """Test attributes get extracted from a JSON result that was converted from XML.""" respx.get("http://localhost").respond( @@ -722,8 +737,8 @@ async def test_update_with_xml_convert_json_attrs_with_jsonattr_template(hass): @respx.mock async def test_update_with_application_xml_convert_json_attrs_with_jsonattr_template( - hass, -): + hass: HomeAssistant, +) -> None: """Test attributes get extracted from a JSON result that was converted from XML with application/xml mime type.""" respx.get("http://localhost").respond( @@ -759,7 +774,9 @@ async def test_update_with_application_xml_convert_json_attrs_with_jsonattr_temp @respx.mock -async def test_update_with_xml_convert_bad_xml(hass, caplog): +async def test_update_with_xml_convert_bad_xml( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: """Test attributes get extracted from a XML result with bad xml.""" respx.get("http://localhost").respond( @@ -794,7 +811,9 @@ async def test_update_with_xml_convert_bad_xml(hass, caplog): @respx.mock -async def test_update_with_failed_get(hass, caplog): +async def test_update_with_failed_get( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: """Test attributes get extracted from a XML result with bad xml.""" respx.get("http://localhost").respond( @@ -829,7 +848,7 @@ async def test_update_with_failed_get(hass, caplog): @respx.mock -async def test_reload(hass): +async def test_reload(hass: HomeAssistant) -> None: """Verify we can reload reset sensors.""" respx.get("http://localhost") % HTTPStatus.OK diff --git a/tests/components/rest/test_switch.py b/tests/components/rest/test_switch.py index a3c0f78db1c..6275314bcf0 100644 --- a/tests/components/rest/test_switch.py +++ b/tests/components/rest/test_switch.py @@ -33,12 +33,12 @@ STATE_RESOURCE = RESOURCE PARAMS = None -async def test_setup_missing_config(hass): +async def test_setup_missing_config(hass: HomeAssistant) -> None: """Test setup with configuration missing required entries.""" assert not await rest.async_setup_platform(hass, {CONF_PLATFORM: DOMAIN}, None) -async def test_setup_missing_schema(hass): +async def test_setup_missing_schema(hass: HomeAssistant) -> None: """Test setup with resource missing schema.""" assert not await rest.async_setup_platform( hass, @@ -47,7 +47,9 @@ async def test_setup_missing_schema(hass): ) -async def test_setup_failed_connect(hass, aioclient_mock): +async def test_setup_failed_connect( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test setup when connection error occurs.""" aioclient_mock.get("http://localhost", exc=aiohttp.ClientError) assert not await rest.async_setup_platform( @@ -57,7 +59,9 @@ async def test_setup_failed_connect(hass, aioclient_mock): ) -async def test_setup_timeout(hass, aioclient_mock): +async def test_setup_timeout( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test setup when connection timeout occurs.""" aioclient_mock.get("http://localhost", exc=asyncio.TimeoutError()) assert not await rest.async_setup_platform( @@ -67,7 +71,9 @@ async def test_setup_timeout(hass, aioclient_mock): ) -async def test_setup_minimum(hass, aioclient_mock): +async def test_setup_minimum( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test setup with minimum configuration.""" aioclient_mock.get("http://localhost", status=HTTPStatus.OK) with assert_setup_component(1, Platform.SWITCH): @@ -85,7 +91,9 @@ async def test_setup_minimum(hass, aioclient_mock): assert aioclient_mock.call_count == 1 -async def test_setup_query_params(hass, aioclient_mock): +async def test_setup_query_params( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test setup with query params.""" aioclient_mock.get("http://localhost/?search=something", status=HTTPStatus.OK) with assert_setup_component(1, Platform.SWITCH): @@ -105,7 +113,7 @@ async def test_setup_query_params(hass, aioclient_mock): assert aioclient_mock.call_count == 1 -async def test_setup(hass, aioclient_mock): +async def test_setup(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker) -> None: """Test setup with valid configuration.""" aioclient_mock.get("http://localhost", status=HTTPStatus.OK) assert await async_setup_component( @@ -127,7 +135,9 @@ async def test_setup(hass, aioclient_mock): assert_setup_component(1, Platform.SWITCH) -async def test_setup_with_state_resource(hass, aioclient_mock): +async def test_setup_with_state_resource( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test setup with valid configuration.""" aioclient_mock.get("http://localhost", status=HTTPStatus.NOT_FOUND) aioclient_mock.get("http://localhost/state", status=HTTPStatus.OK) @@ -151,7 +161,9 @@ async def test_setup_with_state_resource(hass, aioclient_mock): assert_setup_component(1, Platform.SWITCH) -async def test_setup_with_templated_headers_params(hass, aioclient_mock): +async def test_setup_with_templated_headers_params( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test setup with valid configuration.""" aioclient_mock.get("http://localhost", status=HTTPStatus.OK) assert await async_setup_component( @@ -185,7 +197,7 @@ async def test_setup_with_templated_headers_params(hass, aioclient_mock): """Tests for REST switch platform.""" -def _setup_test_switch(hass): +def _setup_test_switch(hass: HomeAssistant) -> None: body_on = Template("on", hass) body_off = Template("off", hass) headers = {"Content-type": Template(CONTENT_TYPE_JSON, hass)} @@ -211,25 +223,27 @@ def _setup_test_switch(hass): return switch, body_on, body_off -def test_name(hass): +def test_name(hass: HomeAssistant) -> None: """Test the name.""" switch, body_on, body_off = _setup_test_switch(hass) assert switch.name == NAME -def test_device_class(hass): +def test_device_class(hass: HomeAssistant) -> None: """Test the name.""" switch, body_on, body_off = _setup_test_switch(hass) assert switch.device_class == DEVICE_CLASS -def test_is_on_before_update(hass): +def test_is_on_before_update(hass: HomeAssistant) -> None: """Test is_on in initial state.""" switch, body_on, body_off = _setup_test_switch(hass) assert switch.is_on is None -async def test_turn_on_success(hass, aioclient_mock): +async def test_turn_on_success( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test turn_on.""" aioclient_mock.post(RESOURCE, status=HTTPStatus.OK) switch, body_on, body_off = _setup_test_switch(hass) @@ -239,7 +253,9 @@ async def test_turn_on_success(hass, aioclient_mock): assert switch.is_on -async def test_turn_on_status_not_ok(hass, aioclient_mock): +async def test_turn_on_status_not_ok( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test turn_on when error status returned.""" aioclient_mock.post(RESOURCE, status=HTTPStatus.INTERNAL_SERVER_ERROR) switch, body_on, body_off = _setup_test_switch(hass) @@ -249,7 +265,9 @@ async def test_turn_on_status_not_ok(hass, aioclient_mock): assert switch.is_on is None -async def test_turn_on_timeout(hass, aioclient_mock): +async def test_turn_on_timeout( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test turn_on when timeout occurs.""" aioclient_mock.post(RESOURCE, status=HTTPStatus.INTERNAL_SERVER_ERROR) switch, body_on, body_off = _setup_test_switch(hass) @@ -258,7 +276,9 @@ async def test_turn_on_timeout(hass, aioclient_mock): assert switch.is_on is None -async def test_turn_off_success(hass, aioclient_mock): +async def test_turn_off_success( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test turn_off.""" aioclient_mock.post(RESOURCE, status=HTTPStatus.OK) switch, body_on, body_off = _setup_test_switch(hass) @@ -268,7 +288,9 @@ async def test_turn_off_success(hass, aioclient_mock): assert not switch.is_on -async def test_turn_off_status_not_ok(hass, aioclient_mock): +async def test_turn_off_status_not_ok( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test turn_off when error status returned.""" aioclient_mock.post(RESOURCE, status=HTTPStatus.INTERNAL_SERVER_ERROR) switch, body_on, body_off = _setup_test_switch(hass) @@ -278,7 +300,9 @@ async def test_turn_off_status_not_ok(hass, aioclient_mock): assert switch.is_on is None -async def test_turn_off_timeout(hass, aioclient_mock): +async def test_turn_off_timeout( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test turn_off when timeout occurs.""" aioclient_mock.post(RESOURCE, exc=asyncio.TimeoutError()) switch, body_on, body_off = _setup_test_switch(hass) @@ -287,7 +311,9 @@ async def test_turn_off_timeout(hass, aioclient_mock): assert switch.is_on is None -async def test_update_when_on(hass, aioclient_mock): +async def test_update_when_on( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test update when switch is on.""" switch, body_on, body_off = _setup_test_switch(hass) aioclient_mock.get(RESOURCE, text=body_on.template) @@ -296,7 +322,9 @@ async def test_update_when_on(hass, aioclient_mock): assert switch.is_on -async def test_update_when_off(hass, aioclient_mock): +async def test_update_when_off( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test update when switch is off.""" switch, body_on, body_off = _setup_test_switch(hass) aioclient_mock.get(RESOURCE, text=body_off.template) @@ -305,7 +333,9 @@ async def test_update_when_off(hass, aioclient_mock): assert not switch.is_on -async def test_update_when_unknown(hass, aioclient_mock): +async def test_update_when_unknown( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test update when unknown status returned.""" aioclient_mock.get(RESOURCE, text="unknown status") switch, body_on, body_off = _setup_test_switch(hass) @@ -314,7 +344,9 @@ async def test_update_when_unknown(hass, aioclient_mock): assert switch.is_on is None -async def test_update_timeout(hass, aioclient_mock): +async def test_update_timeout( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test update when timeout occurs.""" aioclient_mock.get(RESOURCE, exc=asyncio.TimeoutError()) switch, body_on, body_off = _setup_test_switch(hass) From fee3898f648d4fffdf9dbec748aab2410a0bd227 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 31 Oct 2022 15:36:43 +0100 Subject: [PATCH 0120/1033] Use _attr_is_on in rest (#81305) --- .../components/rest/binary_sensor.py | 20 +++++++--------- homeassistant/components/rest/switch.py | 23 +++++++------------ 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/rest/binary_sensor.py b/homeassistant/components/rest/binary_sensor.py index bc51433c3c5..fc40d76a21d 100644 --- a/homeassistant/components/rest/binary_sensor.py +++ b/homeassistant/components/rest/binary_sensor.py @@ -100,24 +100,17 @@ class RestBinarySensor(RestEntity, TemplateEntity, BinarySensorEntity): fallback_name=DEFAULT_BINARY_SENSOR_NAME, unique_id=unique_id, ) - self._state = False self._previous_data = None self._value_template = config.get(CONF_VALUE_TEMPLATE) if (value_template := self._value_template) is not None: value_template.hass = hass - self._is_on = None self._attr_device_class = config.get(CONF_DEVICE_CLASS) - @property - def is_on(self): - """Return true if the binary sensor is on.""" - return self._is_on - def _update_from_rest_data(self): """Update state from the rest data.""" if self.rest.data is None: - self._is_on = False + self._attr_is_on = False response = self.rest.data @@ -127,8 +120,11 @@ class RestBinarySensor(RestEntity, TemplateEntity, BinarySensorEntity): ) try: - self._is_on = bool(int(response)) + self._attr_is_on = bool(int(response)) except ValueError: - self._is_on = {"true": True, "on": True, "open": True, "yes": True}.get( - response.lower(), False - ) + self._attr_is_on = { + "true": True, + "on": True, + "open": True, + "yes": True, + }.get(response.lower(), False) diff --git a/homeassistant/components/rest/switch.py b/homeassistant/components/rest/switch.py index f2a5d93cd22..7e470674b1e 100644 --- a/homeassistant/components/rest/switch.py +++ b/homeassistant/components/rest/switch.py @@ -119,8 +119,6 @@ class RestSwitch(TemplateEntity, SwitchEntity): unique_id=unique_id, ) - self._state = None - auth = None if username := config.get(CONF_USERNAME): auth = aiohttp.BasicAuth(username, password=config[CONF_PASSWORD]) @@ -149,11 +147,6 @@ class RestSwitch(TemplateEntity, SwitchEntity): template.attach(hass, self._headers) template.attach(hass, self._params) - @property - def is_on(self): - """Return true if device is on.""" - return self._state - async def async_turn_on(self, **kwargs: Any) -> None: """Turn the device on.""" body_on_t = self._body_on.async_render(parse_result=False) @@ -162,7 +155,7 @@ class RestSwitch(TemplateEntity, SwitchEntity): req = await self.set_device_state(body_on_t) if req.status == HTTPStatus.OK: - self._state = True + self._attr_is_on = True else: _LOGGER.error( "Can't turn on %s. Is resource/endpoint offline?", self._resource @@ -177,7 +170,7 @@ class RestSwitch(TemplateEntity, SwitchEntity): try: req = await self.set_device_state(body_off_t) if req.status == HTTPStatus.OK: - self._state = False + self._attr_is_on = False else: _LOGGER.error( "Can't turn off %s. Is resource/endpoint offline?", self._resource @@ -233,17 +226,17 @@ class RestSwitch(TemplateEntity, SwitchEntity): ) text = text.lower() if text == "true": - self._state = True + self._attr_is_on = True elif text == "false": - self._state = False + self._attr_is_on = False else: - self._state = None + self._attr_is_on = None else: if text == self._body_on.template: - self._state = True + self._attr_is_on = True elif text == self._body_off.template: - self._state = False + self._attr_is_on = False else: - self._state = None + self._attr_is_on = None return req From 94e2646c875a539f2c48d40eacf24f93f2e69026 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 31 Oct 2022 15:56:13 +0100 Subject: [PATCH 0121/1033] Implement reauth_confirm in fireservicerota (#77487) --- .../components/fireservicerota/config_flow.py | 30 +++++++++++-------- .../components/fireservicerota/strings.json | 2 +- .../fireservicerota/translations/en.json | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/fireservicerota/config_flow.py b/homeassistant/components/fireservicerota/config_flow.py index 3fcd3870f6a..d4d2b0763d9 100644 --- a/homeassistant/components/fireservicerota/config_flow.py +++ b/homeassistant/components/fireservicerota/config_flow.py @@ -1,9 +1,15 @@ """Config flow for FireServiceRota.""" +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any + from pyfireservicerota import FireServiceRota, InvalidAuthError import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_PASSWORD, CONF_TOKEN, CONF_URL, CONF_USERNAME +from homeassistant.data_entry_flow import FlowResult from .const import DOMAIN, URL_LIST @@ -110,18 +116,18 @@ class FireServiceRotaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): description_placeholders=self._description_placeholders, ) - async def async_step_reauth(self, user_input=None): + async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: + """Initialise re-authentication.""" + await self.async_set_unique_id(entry_data[CONF_USERNAME]) + self._existing_entry = {**entry_data} + self._description_placeholders = {CONF_USERNAME: entry_data[CONF_USERNAME]} + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, str] | None = None + ) -> FlowResult: """Get new tokens for a config entry that can't authenticate.""" - - if not self._existing_entry: - await self.async_set_unique_id(user_input[CONF_USERNAME]) - self._existing_entry = user_input.copy() - self._description_placeholders = {"username": user_input[CONF_USERNAME]} - user_input = None - if user_input is None: - return self._show_setup_form(step_id=config_entries.SOURCE_REAUTH) + return self._show_setup_form(step_id="reauth_confirm") - return await self._validate_and_create_entry( - user_input, config_entries.SOURCE_REAUTH - ) + return await self._validate_and_create_entry(user_input, "reauth_confirm") diff --git a/homeassistant/components/fireservicerota/strings.json b/homeassistant/components/fireservicerota/strings.json index aef6f1b6849..7c60b438264 100644 --- a/homeassistant/components/fireservicerota/strings.json +++ b/homeassistant/components/fireservicerota/strings.json @@ -8,7 +8,7 @@ "url": "Website" } }, - "reauth": { + "reauth_confirm": { "description": "Authentication tokens became invalid, login to recreate them.", "data": { "password": "[%key:common::config_flow::data::password%]" diff --git a/homeassistant/components/fireservicerota/translations/en.json b/homeassistant/components/fireservicerota/translations/en.json index a059081760d..38762b614f4 100644 --- a/homeassistant/components/fireservicerota/translations/en.json +++ b/homeassistant/components/fireservicerota/translations/en.json @@ -11,7 +11,7 @@ "invalid_auth": "Invalid authentication" }, "step": { - "reauth": { + "reauth_confirm": { "data": { "password": "Password" }, From 4f5aad9d6d450f1dd9786f126057e0028d4a9c3e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 31 Oct 2022 12:29:12 -0500 Subject: [PATCH 0122/1033] Bump aiohomekit to 2.2.10 (#81312) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 58e258294a0..93aae62daab 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.9"], + "requirements": ["aiohomekit==2.2.10"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index f3db2448dd0..d4176e4d471 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.9 +aiohomekit==2.2.10 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 45b8e84298a..591b48704f5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.9 +aiohomekit==2.2.10 # homeassistant.components.emulated_hue # homeassistant.components.http From 3764f7d95bd1a88cf8b04d5808f506372150852c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 31 Oct 2022 12:35:43 -0500 Subject: [PATCH 0123/1033] Bump zeroconf to 0.39.4 (#81313) --- homeassistant/components/zeroconf/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zeroconf/manifest.json b/homeassistant/components/zeroconf/manifest.json index 967dd761ac7..382cf42b54f 100644 --- a/homeassistant/components/zeroconf/manifest.json +++ b/homeassistant/components/zeroconf/manifest.json @@ -2,7 +2,7 @@ "domain": "zeroconf", "name": "Zero-configuration networking (zeroconf)", "documentation": "https://www.home-assistant.io/integrations/zeroconf", - "requirements": ["zeroconf==0.39.3"], + "requirements": ["zeroconf==0.39.4"], "dependencies": ["network", "api"], "codeowners": ["@bdraco"], "quality_scale": "internal", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index e2c57d87c19..411b0a06646 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -42,7 +42,7 @@ typing-extensions>=4.4.0,<5.0 voluptuous-serialize==2.5.0 voluptuous==0.13.1 yarl==1.8.1 -zeroconf==0.39.3 +zeroconf==0.39.4 # Constrain pycryptodome to avoid vulnerability # see https://github.com/home-assistant/core/pull/16238 diff --git a/requirements_all.txt b/requirements_all.txt index d4176e4d471..602b391e0e1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2607,7 +2607,7 @@ zamg==0.1.1 zengge==0.2 # homeassistant.components.zeroconf -zeroconf==0.39.3 +zeroconf==0.39.4 # homeassistant.components.zha zha-quirks==0.0.84 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 591b48704f5..4c68a58dff9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1808,7 +1808,7 @@ youless-api==0.16 zamg==0.1.1 # homeassistant.components.zeroconf -zeroconf==0.39.3 +zeroconf==0.39.4 # homeassistant.components.zha zha-quirks==0.0.84 From 82e90587c7be757d8ede9c210b9fa04a3d3f4a97 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 31 Oct 2022 13:38:57 -0500 Subject: [PATCH 0124/1033] Bump oralb-ble to 0.10.0 (#81315) --- homeassistant/components/oralb/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/oralb/manifest.json b/homeassistant/components/oralb/manifest.json index 8f694946804..cad6167228c 100644 --- a/homeassistant/components/oralb/manifest.json +++ b/homeassistant/components/oralb/manifest.json @@ -8,7 +8,7 @@ "manufacturer_id": 220 } ], - "requirements": ["oralb-ble==0.9.0"], + "requirements": ["oralb-ble==0.10.0"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 602b391e0e1..5915510eed8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1241,7 +1241,7 @@ openwrt-luci-rpc==1.1.11 openwrt-ubus-rpc==0.0.2 # homeassistant.components.oralb -oralb-ble==0.9.0 +oralb-ble==0.10.0 # homeassistant.components.oru oru==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4c68a58dff9..9094f51cc1a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -886,7 +886,7 @@ open-meteo==0.2.1 openerz-api==0.1.0 # homeassistant.components.oralb -oralb-ble==0.9.0 +oralb-ble==0.10.0 # homeassistant.components.ovo_energy ovoenergy==1.2.0 From c08848b22ea4e9d19b41cc9fa5118c9585f812c8 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 31 Oct 2022 20:36:59 +0100 Subject: [PATCH 0125/1033] Update base image to 2022.10.0 (#81317) --- build.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.yaml b/build.yaml index 9cf66e2621a..14a59641388 100644 --- a/build.yaml +++ b/build.yaml @@ -1,11 +1,11 @@ image: homeassistant/{arch}-homeassistant shadow_repository: ghcr.io/home-assistant build_from: - aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2022.07.0 - armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2022.07.0 - armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2022.07.0 - amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2022.07.0 - i386: ghcr.io/home-assistant/i386-homeassistant-base:2022.07.0 + aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2022.10.0 + armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2022.10.0 + armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2022.10.0 + amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2022.10.0 + i386: ghcr.io/home-assistant/i386-homeassistant-base:2022.10.0 codenotary: signer: notary@home-assistant.io base_image: notary@home-assistant.io From 8044b9587afc9bf2bb09c880f7b12c6cd6bc7664 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Mon, 31 Oct 2022 19:41:12 +0000 Subject: [PATCH 0126/1033] Add integration type to System Bridge (#81186) --- homeassistant/components/system_bridge/manifest.json | 1 + homeassistant/generated/integrations.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/system_bridge/manifest.json b/homeassistant/components/system_bridge/manifest.json index 9370de70787..f386ed57085 100644 --- a/homeassistant/components/system_bridge/manifest.json +++ b/homeassistant/components/system_bridge/manifest.json @@ -9,6 +9,7 @@ "dependencies": ["media_source"], "after_dependencies": ["zeroconf"], "quality_scale": "silver", + "integration_type": "device", "iot_class": "local_push", "loggers": ["systembridgeconnector"] } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 5e245ad9734..3614f5d7afb 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -5151,7 +5151,7 @@ }, "system_bridge": { "name": "System Bridge", - "integration_type": "hub", + "integration_type": "device", "config_flow": true, "iot_class": "local_push" }, From d6689937a34d4ab0f30c36f7f9c5d0f20d8f424f Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Mon, 31 Oct 2022 19:42:02 +0000 Subject: [PATCH 0127/1033] Add integration type to OVO Energy (#81187) --- homeassistant/components/ovo_energy/manifest.json | 1 + homeassistant/generated/integrations.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/ovo_energy/manifest.json b/homeassistant/components/ovo_energy/manifest.json index 6d994e472c8..e61aaebe190 100644 --- a/homeassistant/components/ovo_energy/manifest.json +++ b/homeassistant/components/ovo_energy/manifest.json @@ -5,6 +5,7 @@ "documentation": "https://www.home-assistant.io/integrations/ovo_energy", "requirements": ["ovoenergy==1.2.0"], "codeowners": ["@timmo001"], + "integration_type": "service", "iot_class": "cloud_polling", "loggers": ["ovoenergy"] } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 3614f5d7afb..57452743af1 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -3831,7 +3831,7 @@ }, "ovo_energy": { "name": "OVO Energy", - "integration_type": "hub", + "integration_type": "service", "config_flow": true, "iot_class": "cloud_polling" }, From 4a9859bf54c7b255cc1c739c32f0a6bd197ffc4b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 31 Oct 2022 20:42:18 +0100 Subject: [PATCH 0128/1033] Update frontend to 20221031.0 (#81324) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index c8d3645435f..aed26eb5de1 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20221027.0"], + "requirements": ["home-assistant-frontend==20221031.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 411b0a06646..adff342729d 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -21,7 +21,7 @@ dbus-fast==1.60.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 -home-assistant-frontend==20221027.0 +home-assistant-frontend==20221031.0 httpx==0.23.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index 5915510eed8..5fb9331bd29 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -871,7 +871,7 @@ hole==0.7.0 holidays==0.16 # homeassistant.components.frontend -home-assistant-frontend==20221027.0 +home-assistant-frontend==20221031.0 # homeassistant.components.home_connect homeconnect==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9094f51cc1a..ca71daa39be 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -651,7 +651,7 @@ hole==0.7.0 holidays==0.16 # homeassistant.components.frontend -home-assistant-frontend==20221027.0 +home-assistant-frontend==20221031.0 # homeassistant.components.home_connect homeconnect==0.7.2 From f8de4c3931fbc3c4d9cfde3b9297b23a98571648 Mon Sep 17 00:00:00 2001 From: On Freund Date: Tue, 1 Nov 2022 00:01:22 +0200 Subject: [PATCH 0129/1033] Reauth flow for Risco cloud (#81264) * Risco reauth flow * Address code review comments * Remove redundant log --- homeassistant/components/risco/__init__.py | 9 ++- homeassistant/components/risco/config_flow.py | 26 ++++++- tests/components/risco/conftest.py | 5 +- tests/components/risco/test_config_flow.py | 70 +++++++++++++++++++ 4 files changed, 101 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/risco/__init__.py b/homeassistant/components/risco/__init__.py index 0e631cc4a93..a9a462bf916 100644 --- a/homeassistant/components/risco/__init__.py +++ b/homeassistant/components/risco/__init__.py @@ -26,7 +26,7 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.storage import Store @@ -127,10 +127,9 @@ async def _async_setup_cloud_entry(hass: HomeAssistant, entry: ConfigEntry) -> b try: await risco.login(async_get_clientsession(hass)) except CannotConnectError as error: - raise ConfigEntryNotReady() from error - except UnauthorizedError: - _LOGGER.exception("Failed to login to Risco cloud") - return False + raise ConfigEntryNotReady from error + except UnauthorizedError as error: + raise ConfigEntryAuthFailed from error scan_interval = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) coordinator = RiscoDataUpdateCoordinator(hass, risco, scan_interval) diff --git a/homeassistant/components/risco/config_flow.py b/homeassistant/components/risco/config_flow.py index 5e1cdb75b5a..91e12a2548a 100644 --- a/homeassistant/components/risco/config_flow.py +++ b/homeassistant/components/risco/config_flow.py @@ -3,6 +3,7 @@ from __future__ import annotations from collections.abc import Mapping import logging +from typing import Any from pyrisco import CannotConnectError, RiscoCloud, RiscoLocal, UnauthorizedError import voluptuous as vol @@ -21,6 +22,7 @@ from homeassistant.const import ( STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, ) +from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import ( @@ -93,6 +95,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 + def __init__(self) -> None: + """Init the config flow.""" + self._reauth_entry: config_entries.ConfigEntry | None = None + @staticmethod @core.callback def async_get_options_flow( @@ -112,8 +118,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Configure a cloud based alarm.""" errors = {} if user_input is not None: - await self.async_set_unique_id(user_input[CONF_USERNAME]) - self._abort_if_unique_id_configured() + if not self._reauth_entry: + await self.async_set_unique_id(user_input[CONF_USERNAME]) + self._abort_if_unique_id_configured() try: info = await validate_cloud_input(self.hass, user_input) @@ -125,12 +132,25 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" else: - return self.async_create_entry(title=info["title"], data=user_input) + if not self._reauth_entry: + return self.async_create_entry(title=info["title"], data=user_input) + self.hass.config_entries.async_update_entry( + self._reauth_entry, + data=user_input, + unique_id=user_input[CONF_USERNAME], + ) + await self.hass.config_entries.async_reload(self._reauth_entry.entry_id) + return self.async_abort(reason="reauth_successful") return self.async_show_form( step_id="cloud", data_schema=CLOUD_SCHEMA, errors=errors ) + async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: + """Handle configuration by re-auth.""" + self._reauth_entry = await self.async_set_unique_id(entry_data[CONF_USERNAME]) + return await self.async_step_cloud() + async def async_step_local(self, user_input=None): """Configure a local based alarm.""" errors = {} diff --git a/tests/components/risco/conftest.py b/tests/components/risco/conftest.py index 006e57b9ae5..b22768a1cd0 100644 --- a/tests/components/risco/conftest.py +++ b/tests/components/risco/conftest.py @@ -70,7 +70,10 @@ def events(): def cloud_config_entry(hass, options): """Fixture for a cloud config entry.""" config_entry = MockConfigEntry( - domain=DOMAIN, data=TEST_CLOUD_CONFIG, options=options + domain=DOMAIN, + data=TEST_CLOUD_CONFIG, + options=options, + unique_id=TEST_CLOUD_CONFIG[CONF_USERNAME], ) config_entry.add_to_hass(hass) return config_entry diff --git a/tests/components/risco/test_config_flow.py b/tests/components/risco/test_config_flow.py index 396aad8015d..0c71ba9efdc 100644 --- a/tests/components/risco/test_config_flow.py +++ b/tests/components/risco/test_config_flow.py @@ -10,6 +10,7 @@ from homeassistant.components.risco.config_flow import ( UnauthorizedError, ) from homeassistant.components.risco.const import DOMAIN +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.data_entry_flow import FlowResultType from tests.common import MockConfigEntry @@ -142,6 +143,75 @@ async def test_form_cloud_already_exists(hass): assert result3["reason"] == "already_configured" +async def test_form_reauth(hass, cloud_config_entry): + """Test reauthenticate.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_REAUTH}, + data=cloud_config_entry.data, + ) + assert result["type"] == "form" + assert result["errors"] == {} + + with patch( + "homeassistant.components.risco.config_flow.RiscoCloud.login", + return_value=True, + ), patch( + "homeassistant.components.risco.config_flow.RiscoCloud.site_name", + new_callable=PropertyMock(return_value=TEST_SITE_NAME), + ), patch( + "homeassistant.components.risco.config_flow.RiscoCloud.close" + ), patch( + "homeassistant.components.risco.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], {**TEST_CLOUD_DATA, CONF_PASSWORD: "new_password"} + ) + await hass.async_block_till_done() + + assert result2["type"] == "abort" + assert result2["reason"] == "reauth_successful" + assert cloud_config_entry.data[CONF_PASSWORD] == "new_password" + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_form_reauth_with_new_username(hass, cloud_config_entry): + """Test reauthenticate with new username.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_REAUTH}, + data=cloud_config_entry.data, + ) + assert result["type"] == "form" + assert result["errors"] == {} + + with patch( + "homeassistant.components.risco.config_flow.RiscoCloud.login", + return_value=True, + ), patch( + "homeassistant.components.risco.config_flow.RiscoCloud.site_name", + new_callable=PropertyMock(return_value=TEST_SITE_NAME), + ), patch( + "homeassistant.components.risco.config_flow.RiscoCloud.close" + ), patch( + "homeassistant.components.risco.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], {**TEST_CLOUD_DATA, CONF_USERNAME: "new_user"} + ) + await hass.async_block_till_done() + + assert result2["type"] == "abort" + assert result2["reason"] == "reauth_successful" + assert cloud_config_entry.data[CONF_USERNAME] == "new_user" + assert cloud_config_entry.unique_id == "new_user" + assert len(mock_setup_entry.mock_calls) == 1 + + async def test_local_form(hass): """Test we get the local form.""" result = await hass.config_entries.flow.async_init( From 009d5aedd51e1aedfee5294cde4ece278b62bd8a Mon Sep 17 00:00:00 2001 From: On Freund Date: Tue, 1 Nov 2022 01:29:00 +0200 Subject: [PATCH 0130/1033] Extract `bypassed` attribute in Risco zones to a switch (#81137) * Split bypassed to a switch * Address code review comments --- homeassistant/components/risco/__init__.py | 7 +- .../components/risco/alarm_control_panel.py | 4 +- .../components/risco/binary_sensor.py | 132 +++------------ homeassistant/components/risco/entity.py | 102 +++++++++++- homeassistant/components/risco/sensor.py | 8 +- homeassistant/components/risco/services.yaml | 17 -- homeassistant/components/risco/switch.py | 104 ++++++++++++ tests/components/risco/conftest.py | 34 ++++ tests/components/risco/test_binary_sensor.py | 132 ++------------- tests/components/risco/test_switch.py | 151 ++++++++++++++++++ 10 files changed, 430 insertions(+), 261 deletions(-) delete mode 100644 homeassistant/components/risco/services.yaml create mode 100644 homeassistant/components/risco/switch.py create mode 100644 tests/components/risco/test_switch.py diff --git a/homeassistant/components/risco/__init__.py b/homeassistant/components/risco/__init__.py index a9a462bf916..f143244d31d 100644 --- a/homeassistant/components/risco/__init__.py +++ b/homeassistant/components/risco/__init__.py @@ -40,7 +40,12 @@ from .const import ( TYPE_LOCAL, ) -PLATFORMS = [Platform.ALARM_CONTROL_PANEL, Platform.BINARY_SENSOR, Platform.SENSOR] +PLATFORMS = [ + Platform.ALARM_CONTROL_PANEL, + Platform.BINARY_SENSOR, + Platform.SENSOR, + Platform.SWITCH, +] LAST_EVENT_STORAGE_VERSION = 1 LAST_EVENT_TIMESTAMP_KEY = "last_event_timestamp" _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/risco/alarm_control_panel.py b/homeassistant/components/risco/alarm_control_panel.py index 79da100d6e1..7f4241048d7 100644 --- a/homeassistant/components/risco/alarm_control_panel.py +++ b/homeassistant/components/risco/alarm_control_panel.py @@ -40,7 +40,7 @@ from .const import ( RISCO_GROUPS, RISCO_PARTIAL_ARM, ) -from .entity import RiscoEntity +from .entity import RiscoCloudEntity _LOGGER = logging.getLogger(__name__) @@ -178,7 +178,7 @@ class RiscoAlarm(AlarmControlPanelEntity): raise NotImplementedError -class RiscoCloudAlarm(RiscoAlarm, RiscoEntity): +class RiscoCloudAlarm(RiscoAlarm, RiscoCloudEntity): """Representation of a Risco partition.""" def __init__( diff --git a/homeassistant/components/risco/binary_sensor.py b/homeassistant/components/risco/binary_sensor.py index bc021c2c364..b1f55dd8693 100644 --- a/homeassistant/components/risco/binary_sensor.py +++ b/homeassistant/components/risco/binary_sensor.py @@ -12,21 +12,11 @@ from homeassistant.components.binary_sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_platform -from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import LocalData, RiscoDataUpdateCoordinator, is_local, zone_update_signal +from . import LocalData, RiscoDataUpdateCoordinator, is_local from .const import DATA_COORDINATOR, DOMAIN -from .entity import RiscoEntity, binary_sensor_unique_id - -SERVICE_BYPASS_ZONE = "bypass_zone" -SERVICE_UNBYPASS_ZONE = "unbypass_zone" - - -def _unique_id_for_local(system_id: str, zone_id: int) -> str: - return f"{system_id}_zone_{zone_id}_local" +from .entity import RiscoCloudZoneEntity, RiscoLocalZoneEntity async def async_setup_entry( @@ -35,12 +25,6 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up the Risco alarm control panel.""" - platform = entity_platform.async_get_current_platform() - platform.async_register_entity_service(SERVICE_BYPASS_ZONE, {}, "async_bypass_zone") - platform.async_register_entity_service( - SERVICE_UNBYPASS_ZONE, {}, "async_unbypass_zone" - ) - if is_local(config_entry): local_data: LocalData = hass.data[DOMAIN][config_entry.entry_id] async_add_entities( @@ -61,85 +45,34 @@ async def async_setup_entry( ) -class RiscoBinarySensor(BinarySensorEntity): - """Representation of a Risco zone as a binary sensor.""" +class RiscoCloudBinarySensor(RiscoCloudZoneEntity, BinarySensorEntity): + """Representation of a Risco cloud zone as a binary sensor.""" _attr_device_class = BinarySensorDeviceClass.MOTION - def __init__(self, *, zone_id: int, zone: Zone, **kwargs: Any) -> None: + def __init__( + self, coordinator: RiscoDataUpdateCoordinator, zone_id: int, zone: Zone + ) -> None: """Init the zone.""" - super().__init__(**kwargs) - self._zone_id = zone_id - self._zone = zone - self._attr_has_entity_name = True - self._attr_name = None - - @property - def extra_state_attributes(self) -> Mapping[str, Any] | None: - """Return the state attributes.""" - return {"zone_id": self._zone_id, "bypassed": self._zone.bypassed} + super().__init__( + coordinator=coordinator, name=None, suffix="", zone_id=zone_id, zone=zone + ) @property def is_on(self) -> bool | None: """Return true if sensor is on.""" return self._zone.triggered - async def async_bypass_zone(self) -> None: - """Bypass this zone.""" - await self._bypass(True) - async def async_unbypass_zone(self) -> None: - """Unbypass this zone.""" - await self._bypass(False) - - async def _bypass(self, bypass: bool) -> None: - raise NotImplementedError - - -class RiscoCloudBinarySensor(RiscoBinarySensor, RiscoEntity): - """Representation of a Risco cloud zone as a binary sensor.""" - - def __init__( - self, coordinator: RiscoDataUpdateCoordinator, zone_id: int, zone: Zone - ) -> None: - """Init the zone.""" - super().__init__(zone_id=zone_id, zone=zone, coordinator=coordinator) - self._attr_unique_id = binary_sensor_unique_id(self._risco, zone_id) - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, self._attr_unique_id)}, - manufacturer="Risco", - name=self._zone.name, - ) - - def _get_data_from_coordinator(self) -> None: - self._zone = self.coordinator.data.zones[self._zone_id] - - async def _bypass(self, bypass: bool) -> None: - alarm = await self._risco.bypass_zone(self._zone_id, bypass) - self._zone = alarm.zones[self._zone_id] - self.async_write_ha_state() - - -class RiscoLocalBinarySensor(RiscoBinarySensor): +class RiscoLocalBinarySensor(RiscoLocalZoneEntity, BinarySensorEntity): """Representation of a Risco local zone as a binary sensor.""" - _attr_should_poll = False + _attr_device_class = BinarySensorDeviceClass.MOTION def __init__(self, system_id: str, zone_id: int, zone: Zone) -> None: """Init the zone.""" - super().__init__(zone_id=zone_id, zone=zone) - self._attr_unique_id = _unique_id_for_local(system_id, zone_id) - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, self._attr_unique_id)}, - manufacturer="Risco", - name=self._zone.name, - ) - - async def async_added_to_hass(self) -> None: - """Subscribe to updates.""" - signal = zone_update_signal(self._zone_id) - self.async_on_remove( - async_dispatcher_connect(self.hass, signal, self.async_write_ha_state) + super().__init__( + system_id=system_id, name=None, suffix="", zone_id=zone_id, zone=zone ) @property @@ -150,42 +83,27 @@ class RiscoLocalBinarySensor(RiscoBinarySensor): "groups": self._zone.groups, } - async def _bypass(self, bypass: bool) -> None: - await self._zone.bypass(bypass) + @property + def is_on(self) -> bool | None: + """Return true if sensor is on.""" + return self._zone.triggered -class RiscoLocalAlarmedBinarySensor(BinarySensorEntity): +class RiscoLocalAlarmedBinarySensor(RiscoLocalZoneEntity, BinarySensorEntity): """Representation whether a zone in Risco local is currently triggering an alarm.""" _attr_should_poll = False def __init__(self, system_id: str, zone_id: int, zone: Zone) -> None: """Init the zone.""" - super().__init__() - self._zone_id = zone_id - self._zone = zone - self._attr_has_entity_name = True - self._attr_name = "Alarmed" - device_unique_id = _unique_id_for_local(system_id, zone_id) - self._attr_unique_id = device_unique_id + "_alarmed" - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, device_unique_id)}, - manufacturer="Risco", - name=self._zone.name, + super().__init__( + system_id=system_id, + name="Alarmed", + suffix="_alarmed", + zone_id=zone_id, + zone=zone, ) - async def async_added_to_hass(self) -> None: - """Subscribe to updates.""" - signal = zone_update_signal(self._zone_id) - self.async_on_remove( - async_dispatcher_connect(self.hass, signal, self.async_write_ha_state) - ) - - @property - def extra_state_attributes(self) -> Mapping[str, Any] | None: - """Return the state attributes.""" - return {"zone_id": self._zone_id} - @property def is_on(self) -> bool | None: """Return true if sensor is on.""" diff --git a/homeassistant/components/risco/entity.py b/homeassistant/components/risco/entity.py index e49b632ac78..a4ac260887c 100644 --- a/homeassistant/components/risco/entity.py +++ b/homeassistant/components/risco/entity.py @@ -1,25 +1,40 @@ """A risco entity base class.""" +from __future__ import annotations + +from typing import Any + +from pyrisco.common import Zone + +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import RiscoDataUpdateCoordinator +from . import RiscoDataUpdateCoordinator, zone_update_signal +from .const import DOMAIN -def binary_sensor_unique_id(risco, zone_id: int) -> str: - """Return unique id for the binary sensor.""" +def zone_unique_id(risco, zone_id: int) -> str: + """Return unique id for a cloud zone.""" return f"{risco.site_uuid}_zone_{zone_id}" -class RiscoEntity(CoordinatorEntity[RiscoDataUpdateCoordinator]): - """Risco entity base class.""" +class RiscoCloudEntity(CoordinatorEntity[RiscoDataUpdateCoordinator]): + """Risco cloud entity base class.""" - def _get_data_from_coordinator(self): + def __init__( + self, *, coordinator: RiscoDataUpdateCoordinator, **kwargs: Any + ) -> None: + """Init the entity.""" + super().__init__(coordinator=coordinator, **kwargs) + + def _get_data_from_coordinator(self) -> None: raise NotImplementedError - def _refresh_from_coordinator(self): + def _refresh_from_coordinator(self) -> None: self._get_data_from_coordinator() self.async_write_ha_state() - async def async_added_to_hass(self): + async def async_added_to_hass(self) -> None: """When entity is added to hass.""" self.async_on_remove( self.coordinator.async_add_listener(self._refresh_from_coordinator) @@ -29,3 +44,74 @@ class RiscoEntity(CoordinatorEntity[RiscoDataUpdateCoordinator]): def _risco(self): """Return the Risco API object.""" return self.coordinator.risco + + +class RiscoCloudZoneEntity(RiscoCloudEntity): + """Risco cloud zone entity base class.""" + + _attr_has_entity_name = True + + def __init__( + self, + *, + coordinator: RiscoDataUpdateCoordinator, + name: str | None, + suffix: str, + zone_id: int, + zone: Zone, + **kwargs: Any, + ) -> None: + """Init the zone.""" + super().__init__(coordinator=coordinator, **kwargs) + self._zone_id = zone_id + self._zone = zone + self._attr_name = name + device_unique_id = zone_unique_id(self._risco, zone_id) + self._attr_unique_id = f"{device_unique_id}{suffix}" + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, device_unique_id)}, + manufacturer="Risco", + name=self._zone.name, + ) + self._attr_extra_state_attributes = {"zone_id": zone_id} + + def _get_data_from_coordinator(self) -> None: + self._zone = self.coordinator.data.zones[self._zone_id] + + +class RiscoLocalZoneEntity(Entity): + """Risco local zone entity base class.""" + + _attr_should_poll = False + _attr_has_entity_name = True + + def __init__( + self, + *, + system_id: str, + name: str | None, + suffix: str, + zone_id: int, + zone: Zone, + **kwargs: Any, + ) -> None: + """Init the zone.""" + super().__init__(**kwargs) + self._zone_id = zone_id + self._zone = zone + self._attr_name = name + device_unique_id = f"{system_id}_zone_{zone_id}_local" + self._attr_unique_id = f"{device_unique_id}{suffix}" + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, device_unique_id)}, + manufacturer="Risco", + name=zone.name, + ) + self._attr_extra_state_attributes = {"zone_id": zone_id} + + async def async_added_to_hass(self) -> None: + """Subscribe to updates.""" + signal = zone_update_signal(self._zone_id) + self.async_on_remove( + async_dispatcher_connect(self.hass, signal, self.async_write_ha_state) + ) diff --git a/homeassistant/components/risco/sensor.py b/homeassistant/components/risco/sensor.py index c4bd047e260..f2cb9821166 100644 --- a/homeassistant/components/risco/sensor.py +++ b/homeassistant/components/risco/sensor.py @@ -15,7 +15,7 @@ from homeassistant.util import dt as dt_util from . import RiscoEventsDataUpdateCoordinator, is_local from .const import DOMAIN, EVENTS_COORDINATOR -from .entity import binary_sensor_unique_id +from .entity import zone_unique_id CATEGORIES = { 2: "Alarm", @@ -115,11 +115,9 @@ class RiscoSensor(CoordinatorEntity, SensorEntity): attrs = {atr: getattr(self._event, atr, None) for atr in EVENT_ATTRIBUTES} if self._event.zone_id is not None: - zone_unique_id = binary_sensor_unique_id( - self.coordinator.risco, self._event.zone_id - ) + uid = zone_unique_id(self.coordinator.risco, self._event.zone_id) zone_entity_id = self._entity_registry.async_get_entity_id( - BS_DOMAIN, DOMAIN, zone_unique_id + BS_DOMAIN, DOMAIN, uid ) if zone_entity_id is not None: attrs["zone_entity_id"] = zone_entity_id diff --git a/homeassistant/components/risco/services.yaml b/homeassistant/components/risco/services.yaml deleted file mode 100644 index c271df7b462..00000000000 --- a/homeassistant/components/risco/services.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Describes the format for available Risco services - -bypass_zone: - name: Bypass zone - description: Bypass a Risco Zone - target: - entity: - integration: risco - domain: binary_sensor - -unbypass_zone: - name: Unbypass zone - description: Unbypass a Risco Zone - target: - entity: - integration: risco - domain: binary_sensor diff --git a/homeassistant/components/risco/switch.py b/homeassistant/components/risco/switch.py new file mode 100644 index 00000000000..2ed07b9f34b --- /dev/null +++ b/homeassistant/components/risco/switch.py @@ -0,0 +1,104 @@ +"""Support for bypassing Risco alarm zones.""" +from __future__ import annotations + +from pyrisco.common import Zone + +from homeassistant.components.switch import SwitchEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import LocalData, RiscoDataUpdateCoordinator, is_local +from .const import DATA_COORDINATOR, DOMAIN +from .entity import RiscoCloudZoneEntity, RiscoLocalZoneEntity + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Risco switch.""" + if is_local(config_entry): + local_data: LocalData = hass.data[DOMAIN][config_entry.entry_id] + async_add_entities( + RiscoLocalSwitch(local_data.system.id, zone_id, zone) + for zone_id, zone in local_data.system.zones.items() + ) + else: + coordinator: RiscoDataUpdateCoordinator = hass.data[DOMAIN][ + config_entry.entry_id + ][DATA_COORDINATOR] + async_add_entities( + RiscoCloudSwitch(coordinator, zone_id, zone) + for zone_id, zone in coordinator.data.zones.items() + ) + + +class RiscoCloudSwitch(RiscoCloudZoneEntity, SwitchEntity): + """Representation of a bypass switch for a Risco cloud zone.""" + + _attr_entity_category = EntityCategory.CONFIG + + def __init__( + self, coordinator: RiscoDataUpdateCoordinator, zone_id: int, zone: Zone + ) -> None: + """Init the zone.""" + super().__init__( + coordinator=coordinator, + name="Bypassed", + suffix="_bypassed", + zone_id=zone_id, + zone=zone, + ) + + @property + def is_on(self) -> bool | None: + """Return true if the zone is bypassed.""" + return self._zone.bypassed + + async def async_turn_on(self, **kwargs): + """Turn the entity on.""" + await self._bypass(True) + + async def async_turn_off(self, **kwargs): + """Turn the entity off.""" + await self._bypass(False) + + async def _bypass(self, bypass: bool) -> None: + alarm = await self._risco.bypass_zone(self._zone_id, bypass) + self._zone = alarm.zones[self._zone_id] + self.async_write_ha_state() + + +class RiscoLocalSwitch(RiscoLocalZoneEntity, SwitchEntity): + """Representation of a bypass switch for a Risco local zone.""" + + _attr_entity_category = EntityCategory.CONFIG + + def __init__(self, system_id: str, zone_id: int, zone: Zone) -> None: + """Init the zone.""" + super().__init__( + system_id=system_id, + name="Bypassed", + suffix="_bypassed", + zone_id=zone_id, + zone=zone, + ) + + @property + def is_on(self) -> bool | None: + """Return true if the zone is bypassed.""" + return self._zone.bypassed + + async def async_turn_on(self, **kwargs): + """Turn the entity on.""" + await self._bypass(True) + + async def async_turn_off(self, **kwargs): + """Turn the entity off.""" + await self._bypass(False) + + async def _bypass(self, bypass: bool) -> None: + await self._zone.bypass(bypass) diff --git a/tests/components/risco/conftest.py b/tests/components/risco/conftest.py index b22768a1cd0..cc65efd9b55 100644 --- a/tests/components/risco/conftest.py +++ b/tests/components/risco/conftest.py @@ -39,10 +39,14 @@ def two_zone_cloud(): zone_mocks[0], "id", new_callable=PropertyMock(return_value=0) ), patch.object( zone_mocks[0], "name", new_callable=PropertyMock(return_value="Zone 0") + ), patch.object( + zone_mocks[0], "bypassed", new_callable=PropertyMock(return_value=False) ), patch.object( zone_mocks[1], "id", new_callable=PropertyMock(return_value=1) ), patch.object( zone_mocks[1], "name", new_callable=PropertyMock(return_value="Zone 1") + ), patch.object( + zone_mocks[1], "bypassed", new_callable=PropertyMock(return_value=False) ), patch.object( alarm_mock, "zones", @@ -54,6 +58,36 @@ def two_zone_cloud(): yield zone_mocks +@fixture +def two_zone_local(): + """Fixture to mock alarm with two zones.""" + zone_mocks = {0: zone_mock(), 1: zone_mock()} + with patch.object( + zone_mocks[0], "id", new_callable=PropertyMock(return_value=0) + ), patch.object( + zone_mocks[0], "name", new_callable=PropertyMock(return_value="Zone 0") + ), patch.object( + zone_mocks[0], "alarmed", new_callable=PropertyMock(return_value=False) + ), patch.object( + zone_mocks[0], "bypassed", new_callable=PropertyMock(return_value=False) + ), patch.object( + zone_mocks[1], "id", new_callable=PropertyMock(return_value=1) + ), patch.object( + zone_mocks[1], "name", new_callable=PropertyMock(return_value="Zone 1") + ), patch.object( + zone_mocks[1], "alarmed", new_callable=PropertyMock(return_value=False) + ), patch.object( + zone_mocks[1], "bypassed", new_callable=PropertyMock(return_value=False) + ), patch( + "homeassistant.components.risco.RiscoLocal.partitions", + new_callable=PropertyMock(return_value={}), + ), patch( + "homeassistant.components.risco.RiscoLocal.zones", + new_callable=PropertyMock(return_value=zone_mocks), + ): + yield zone_mocks + + @fixture def options(): """Fixture for default (empty) options.""" diff --git a/tests/components/risco/test_binary_sensor.py b/tests/components/risco/test_binary_sensor.py index 71cbd04f391..00d10f6059e 100644 --- a/tests/components/risco/test_binary_sensor.py +++ b/tests/components/risco/test_binary_sensor.py @@ -9,7 +9,7 @@ from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.entity_component import async_update_entity -from .util import TEST_SITE_UUID, zone_mock +from .util import TEST_SITE_UUID FIRST_ENTITY_ID = "binary_sensor.zone_0" SECOND_ENTITY_ID = "binary_sensor.zone_1" @@ -17,32 +17,6 @@ FIRST_ALARMED_ENTITY_ID = FIRST_ENTITY_ID + "_alarmed" SECOND_ALARMED_ENTITY_ID = SECOND_ENTITY_ID + "_alarmed" -@pytest.fixture -def two_zone_local(): - """Fixture to mock alarm with two zones.""" - zone_mocks = {0: zone_mock(), 1: zone_mock()} - with patch.object( - zone_mocks[0], "id", new_callable=PropertyMock(return_value=0) - ), patch.object( - zone_mocks[0], "name", new_callable=PropertyMock(return_value="Zone 0") - ), patch.object( - zone_mocks[0], "alarmed", new_callable=PropertyMock(return_value=False) - ), patch.object( - zone_mocks[1], "id", new_callable=PropertyMock(return_value=1) - ), patch.object( - zone_mocks[1], "name", new_callable=PropertyMock(return_value="Zone 1") - ), patch.object( - zone_mocks[1], "alarmed", new_callable=PropertyMock(return_value=False) - ), patch( - "homeassistant.components.risco.RiscoLocal.partitions", - new_callable=PropertyMock(return_value={}), - ), patch( - "homeassistant.components.risco.RiscoLocal.zones", - new_callable=PropertyMock(return_value=zone_mocks), - ): - yield zone_mocks - - @pytest.mark.parametrize("exception", [CannotConnectError, UnauthorizedError]) async def test_error_on_login(hass, login_with_error, cloud_config_entry): """Test error on login.""" @@ -69,59 +43,26 @@ async def test_cloud_setup(hass, two_zone_cloud, setup_risco_cloud): assert device.manufacturer == "Risco" -async def _check_cloud_state(hass, zones, triggered, bypassed, entity_id, zone_id): +async def _check_cloud_state(hass, zones, triggered, entity_id, zone_id): with patch.object( zones[zone_id], "triggered", new_callable=PropertyMock(return_value=triggered), - ), patch.object( - zones[zone_id], - "bypassed", - new_callable=PropertyMock(return_value=bypassed), ): await async_update_entity(hass, entity_id) await hass.async_block_till_done() expected_triggered = STATE_ON if triggered else STATE_OFF assert hass.states.get(entity_id).state == expected_triggered - assert hass.states.get(entity_id).attributes["bypassed"] == bypassed assert hass.states.get(entity_id).attributes["zone_id"] == zone_id async def test_cloud_states(hass, two_zone_cloud, setup_risco_cloud): """Test the various alarm states.""" - await _check_cloud_state(hass, two_zone_cloud, True, True, FIRST_ENTITY_ID, 0) - await _check_cloud_state(hass, two_zone_cloud, True, False, FIRST_ENTITY_ID, 0) - await _check_cloud_state(hass, two_zone_cloud, False, True, FIRST_ENTITY_ID, 0) - await _check_cloud_state(hass, two_zone_cloud, False, False, FIRST_ENTITY_ID, 0) - await _check_cloud_state(hass, two_zone_cloud, True, True, SECOND_ENTITY_ID, 1) - await _check_cloud_state(hass, two_zone_cloud, True, False, SECOND_ENTITY_ID, 1) - await _check_cloud_state(hass, two_zone_cloud, False, True, SECOND_ENTITY_ID, 1) - await _check_cloud_state(hass, two_zone_cloud, False, False, SECOND_ENTITY_ID, 1) - - -async def test_cloud_bypass(hass, two_zone_cloud, setup_risco_cloud): - """Test bypassing a zone.""" - with patch("homeassistant.components.risco.RiscoCloud.bypass_zone") as mock: - data = {"entity_id": FIRST_ENTITY_ID} - - await hass.services.async_call( - DOMAIN, "bypass_zone", service_data=data, blocking=True - ) - - mock.assert_awaited_once_with(0, True) - - -async def test_cloud_unbypass(hass, two_zone_cloud, setup_risco_cloud): - """Test unbypassing a zone.""" - with patch("homeassistant.components.risco.RiscoCloud.bypass_zone") as mock: - data = {"entity_id": FIRST_ENTITY_ID} - - await hass.services.async_call( - DOMAIN, "unbypass_zone", service_data=data, blocking=True - ) - - mock.assert_awaited_once_with(0, False) + await _check_cloud_state(hass, two_zone_cloud, True, FIRST_ENTITY_ID, 0) + await _check_cloud_state(hass, two_zone_cloud, False, FIRST_ENTITY_ID, 0) + await _check_cloud_state(hass, two_zone_cloud, True, SECOND_ENTITY_ID, 1) + await _check_cloud_state(hass, two_zone_cloud, False, SECOND_ENTITY_ID, 1) @pytest.mark.parametrize("exception", [CannotConnectError, UnauthorizedError]) @@ -154,24 +95,17 @@ async def test_local_setup(hass, two_zone_local, setup_risco_local): assert device.manufacturer == "Risco" -async def _check_local_state( - hass, zones, triggered, bypassed, entity_id, zone_id, callback -): +async def _check_local_state(hass, zones, triggered, entity_id, zone_id, callback): with patch.object( zones[zone_id], "triggered", new_callable=PropertyMock(return_value=triggered), - ), patch.object( - zones[zone_id], - "bypassed", - new_callable=PropertyMock(return_value=bypassed), ): await callback(zone_id, zones[zone_id]) await hass.async_block_till_done() expected_triggered = STATE_ON if triggered else STATE_OFF assert hass.states.get(entity_id).state == expected_triggered - assert hass.states.get(entity_id).attributes["bypassed"] == bypassed assert hass.states.get(entity_id).attributes["zone_id"] == zone_id @@ -205,30 +139,10 @@ async def test_local_states( assert callback is not None - await _check_local_state( - hass, two_zone_local, True, True, FIRST_ENTITY_ID, 0, callback - ) - await _check_local_state( - hass, two_zone_local, True, False, FIRST_ENTITY_ID, 0, callback - ) - await _check_local_state( - hass, two_zone_local, False, True, FIRST_ENTITY_ID, 0, callback - ) - await _check_local_state( - hass, two_zone_local, False, False, FIRST_ENTITY_ID, 0, callback - ) - await _check_local_state( - hass, two_zone_local, True, True, SECOND_ENTITY_ID, 1, callback - ) - await _check_local_state( - hass, two_zone_local, True, False, SECOND_ENTITY_ID, 1, callback - ) - await _check_local_state( - hass, two_zone_local, False, True, SECOND_ENTITY_ID, 1, callback - ) - await _check_local_state( - hass, two_zone_local, False, False, SECOND_ENTITY_ID, 1, callback - ) + await _check_local_state(hass, two_zone_local, True, FIRST_ENTITY_ID, 0, callback) + await _check_local_state(hass, two_zone_local, False, FIRST_ENTITY_ID, 0, callback) + await _check_local_state(hass, two_zone_local, True, SECOND_ENTITY_ID, 1, callback) + await _check_local_state(hass, two_zone_local, False, SECOND_ENTITY_ID, 1, callback) async def test_alarmed_local_states( @@ -251,27 +165,3 @@ async def test_alarmed_local_states( await _check_alarmed_local_state( hass, two_zone_local, False, SECOND_ALARMED_ENTITY_ID, 1, callback ) - - -async def test_local_bypass(hass, two_zone_local, setup_risco_local): - """Test bypassing a zone.""" - with patch.object(two_zone_local[0], "bypass") as mock: - data = {"entity_id": FIRST_ENTITY_ID} - - await hass.services.async_call( - DOMAIN, "bypass_zone", service_data=data, blocking=True - ) - - mock.assert_awaited_once_with(True) - - -async def test_local_unbypass(hass, two_zone_local, setup_risco_local): - """Test unbypassing a zone.""" - with patch.object(two_zone_local[0], "bypass") as mock: - data = {"entity_id": FIRST_ENTITY_ID} - - await hass.services.async_call( - DOMAIN, "unbypass_zone", service_data=data, blocking=True - ) - - mock.assert_awaited_once_with(False) diff --git a/tests/components/risco/test_switch.py b/tests/components/risco/test_switch.py new file mode 100644 index 00000000000..5ea4e72abca --- /dev/null +++ b/tests/components/risco/test_switch.py @@ -0,0 +1,151 @@ +"""Tests for the Risco binary sensors.""" +from unittest.mock import PropertyMock, patch + +import pytest + +from homeassistant.components.risco import CannotConnectError, UnauthorizedError +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN +from homeassistant.const import SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_OFF, STATE_ON +from homeassistant.helpers import entity_registry as er +from homeassistant.helpers.entity_component import async_update_entity + +FIRST_ENTITY_ID = "switch.zone_0_bypassed" +SECOND_ENTITY_ID = "switch.zone_1_bypassed" + + +@pytest.mark.parametrize("exception", [CannotConnectError, UnauthorizedError]) +async def test_error_on_login(hass, login_with_error, cloud_config_entry): + """Test error on login.""" + await hass.config_entries.async_setup(cloud_config_entry.entry_id) + await hass.async_block_till_done() + registry = er.async_get(hass) + assert not registry.async_is_registered(FIRST_ENTITY_ID) + assert not registry.async_is_registered(SECOND_ENTITY_ID) + + +async def test_cloud_setup(hass, two_zone_cloud, setup_risco_cloud): + """Test entity setup.""" + registry = er.async_get(hass) + assert registry.async_is_registered(FIRST_ENTITY_ID) + assert registry.async_is_registered(SECOND_ENTITY_ID) + + +async def _check_cloud_state(hass, zones, bypassed, entity_id, zone_id): + with patch.object( + zones[zone_id], + "bypassed", + new_callable=PropertyMock(return_value=bypassed), + ): + await async_update_entity(hass, entity_id) + await hass.async_block_till_done() + + expected_bypassed = STATE_ON if bypassed else STATE_OFF + assert hass.states.get(entity_id).state == expected_bypassed + assert hass.states.get(entity_id).attributes["zone_id"] == zone_id + + +async def test_cloud_states(hass, two_zone_cloud, setup_risco_cloud): + """Test the various alarm states.""" + await _check_cloud_state(hass, two_zone_cloud, True, FIRST_ENTITY_ID, 0) + await _check_cloud_state(hass, two_zone_cloud, False, FIRST_ENTITY_ID, 0) + await _check_cloud_state(hass, two_zone_cloud, True, SECOND_ENTITY_ID, 1) + await _check_cloud_state(hass, two_zone_cloud, False, SECOND_ENTITY_ID, 1) + + +async def test_cloud_bypass(hass, two_zone_cloud, setup_risco_cloud): + """Test bypassing a zone.""" + with patch("homeassistant.components.risco.RiscoCloud.bypass_zone") as mock: + data = {"entity_id": FIRST_ENTITY_ID} + + await hass.services.async_call( + SWITCH_DOMAIN, SERVICE_TURN_ON, service_data=data, blocking=True + ) + + mock.assert_awaited_once_with(0, True) + + +async def test_cloud_unbypass(hass, two_zone_cloud, setup_risco_cloud): + """Test unbypassing a zone.""" + with patch("homeassistant.components.risco.RiscoCloud.bypass_zone") as mock: + data = {"entity_id": FIRST_ENTITY_ID} + + await hass.services.async_call( + SWITCH_DOMAIN, SERVICE_TURN_OFF, service_data=data, blocking=True + ) + + mock.assert_awaited_once_with(0, False) + + +@pytest.mark.parametrize("exception", [CannotConnectError, UnauthorizedError]) +async def test_error_on_connect(hass, connect_with_error, local_config_entry): + """Test error on connect.""" + await hass.config_entries.async_setup(local_config_entry.entry_id) + await hass.async_block_till_done() + registry = er.async_get(hass) + assert not registry.async_is_registered(FIRST_ENTITY_ID) + assert not registry.async_is_registered(SECOND_ENTITY_ID) + + +async def test_local_setup(hass, two_zone_local, setup_risco_local): + """Test entity setup.""" + registry = er.async_get(hass) + assert registry.async_is_registered(FIRST_ENTITY_ID) + assert registry.async_is_registered(SECOND_ENTITY_ID) + + +async def _check_local_state(hass, zones, bypassed, entity_id, zone_id, callback): + with patch.object( + zones[zone_id], + "bypassed", + new_callable=PropertyMock(return_value=bypassed), + ): + await callback(zone_id, zones[zone_id]) + await hass.async_block_till_done() + + expected_bypassed = STATE_ON if bypassed else STATE_OFF + assert hass.states.get(entity_id).state == expected_bypassed + assert hass.states.get(entity_id).attributes["zone_id"] == zone_id + + +@pytest.fixture +def _mock_zone_handler(): + with patch("homeassistant.components.risco.RiscoLocal.add_zone_handler") as mock: + yield mock + + +async def test_local_states( + hass, two_zone_local, _mock_zone_handler, setup_risco_local +): + """Test the various alarm states.""" + callback = _mock_zone_handler.call_args.args[0] + + assert callback is not None + + await _check_local_state(hass, two_zone_local, True, FIRST_ENTITY_ID, 0, callback) + await _check_local_state(hass, two_zone_local, False, FIRST_ENTITY_ID, 0, callback) + await _check_local_state(hass, two_zone_local, True, SECOND_ENTITY_ID, 1, callback) + await _check_local_state(hass, two_zone_local, False, SECOND_ENTITY_ID, 1, callback) + + +async def test_local_bypass(hass, two_zone_local, setup_risco_local): + """Test bypassing a zone.""" + with patch.object(two_zone_local[0], "bypass") as mock: + data = {"entity_id": FIRST_ENTITY_ID} + + await hass.services.async_call( + SWITCH_DOMAIN, SERVICE_TURN_ON, service_data=data, blocking=True + ) + + mock.assert_awaited_once_with(True) + + +async def test_local_unbypass(hass, two_zone_local, setup_risco_local): + """Test unbypassing a zone.""" + with patch.object(two_zone_local[0], "bypass") as mock: + data = {"entity_id": FIRST_ENTITY_ID} + + await hass.services.async_call( + SWITCH_DOMAIN, SERVICE_TURN_OFF, service_data=data, blocking=True + ) + + mock.assert_awaited_once_with(False) From 0bca9a614cdc5b76e29e279c9a4b5f4612d3752c Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 1 Nov 2022 00:33:48 +0000 Subject: [PATCH 0131/1033] [ci skip] Translation update --- .../components/aranet/translations/bg.json | 24 +++++++++++++++++ .../components/aranet/translations/ca.json | 22 ++++++++++++++++ .../components/aranet/translations/de.json | 25 ++++++++++++++++++ .../components/aranet/translations/en.json | 2 +- .../components/aranet/translations/es.json | 25 ++++++++++++++++++ .../components/aranet/translations/et.json | 25 ++++++++++++++++++ .../components/aranet/translations/hu.json | 25 ++++++++++++++++++ .../components/aranet/translations/no.json | 25 ++++++++++++++++++ .../components/aranet/translations/pt-BR.json | 25 ++++++++++++++++++ .../components/aranet/translations/ru.json | 25 ++++++++++++++++++ .../fireservicerota/translations/ca.json | 6 +++++ .../fireservicerota/translations/en.json | 6 +++++ .../fireservicerota/translations/es.json | 6 +++++ .../fireservicerota/translations/hu.json | 6 +++++ .../fireservicerota/translations/pt-BR.json | 6 +++++ .../google_travel_time/translations/no.json | 1 + .../translations/pt-BR.json | 1 + .../components/hassio/translations/ca.json | 10 +++++++ .../components/hassio/translations/en.json | 10 +++++++ .../components/hassio/translations/es.json | 10 +++++++ .../components/hassio/translations/et.json | 10 +++++++ .../components/hassio/translations/hu.json | 10 +++++++ .../components/hassio/translations/pt-BR.json | 10 +++++++ .../components/hassio/translations/ru.json | 10 +++++++ .../components/mqtt/translations/bg.json | 13 ++++++++-- .../ovo_energy/translations/no.json | 1 + .../ovo_energy/translations/pt-BR.json | 1 + .../rainmachine/translations/no.json | 1 + .../components/scrape/translations/bg.json | 5 ++++ .../components/scrape/translations/et.json | 6 +++++ .../components/scrape/translations/hu.json | 6 +++++ .../components/scrape/translations/no.json | 6 +++++ .../components/scrape/translations/pt-BR.json | 6 +++++ .../statistics/translations/no.json | 12 +++++++++ .../transmission/translations/hu.json | 13 ++++++++++ .../transmission/translations/no.json | 13 ++++++++++ .../transmission/translations/pt-BR.json | 13 ++++++++++ .../components/zamg/translations/no.json | 26 +++++++++++++++++++ .../components/zha/translations/es.json | 2 +- 39 files changed, 445 insertions(+), 4 deletions(-) create mode 100644 homeassistant/components/aranet/translations/bg.json create mode 100644 homeassistant/components/aranet/translations/ca.json create mode 100644 homeassistant/components/aranet/translations/de.json create mode 100644 homeassistant/components/aranet/translations/es.json create mode 100644 homeassistant/components/aranet/translations/et.json create mode 100644 homeassistant/components/aranet/translations/hu.json create mode 100644 homeassistant/components/aranet/translations/no.json create mode 100644 homeassistant/components/aranet/translations/pt-BR.json create mode 100644 homeassistant/components/aranet/translations/ru.json create mode 100644 homeassistant/components/statistics/translations/no.json create mode 100644 homeassistant/components/zamg/translations/no.json diff --git a/homeassistant/components/aranet/translations/bg.json b/homeassistant/components/aranet/translations/bg.json new file mode 100644 index 00000000000..19937c000c8 --- /dev/null +++ b/homeassistant/components/aranet/translations/bg.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "no_devices_found": "\u041d\u044f\u043c\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u043d\u0435\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0438 Aranet \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430.", + "outdated_version": "\u0422\u043e\u0432\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u043e\u0441\u0442\u0430\u0440\u044f\u043b \u0444\u044a\u0440\u043c\u0443\u0435\u0440. \u041c\u043e\u043b\u044f, \u0430\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u0439\u0442\u0435 \u0433\u043e \u043f\u043e\u043d\u0435 \u0434\u043e v1.2.0 \u0438 \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e." + }, + "error": { + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 {name}?" + }, + "user": { + "data": { + "address": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + }, + "description": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0437\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/ca.json b/homeassistant/components/aranet/translations/ca.json new file mode 100644 index 00000000000..1bbc15cc1b7 --- /dev/null +++ b/homeassistant/components/aranet/translations/ca.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat" + }, + "error": { + "unknown": "Error inesperat" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Vols configurar {name}?" + }, + "user": { + "data": { + "address": "Dispositiu" + }, + "description": "Tria un dispositiu a configurar" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/de.json b/homeassistant/components/aranet/translations/de.json new file mode 100644 index 00000000000..cc8a35e3761 --- /dev/null +++ b/homeassistant/components/aranet/translations/de.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert", + "integrations_diabled": "Auf diesem Ger\u00e4t sind keine Integrationen aktiviert. Bitte aktiviere die Smart-Home-Integration \u00fcber die App und versuche es erneut.", + "no_devices_found": "Keine unkonfigurierten Aranet-Ger\u00e4te gefunden.", + "outdated_version": "Dieses Ger\u00e4t verwendet eine veraltete Firmware. Bitte aktualisiere es auf mindestens v1.2.0 und versuche es erneut." + }, + "error": { + "unknown": "Unerwarteter Fehler" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "M\u00f6chtest du {name} einrichten?" + }, + "user": { + "data": { + "address": "Ger\u00e4t" + }, + "description": "W\u00e4hle ein Ger\u00e4t zum Einrichten aus" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/en.json b/homeassistant/components/aranet/translations/en.json index 303fd56f1c8..00d5aacf11c 100644 --- a/homeassistant/components/aranet/translations/en.json +++ b/homeassistant/components/aranet/translations/en.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Device is already configured", "integrations_diabled": "This device doesn't have integrations enabled. Please enable smart home integrations using the app and try again.", - "no_devices_found": "No unconfigured Aranet4 devices found.", + "no_devices_found": "No unconfigured Aranet devices found.", "outdated_version": "This device is using outdated firmware. Please update it to at least v1.2.0 and try again." }, "error": { diff --git a/homeassistant/components/aranet/translations/es.json b/homeassistant/components/aranet/translations/es.json new file mode 100644 index 00000000000..c2e96ecfc7a --- /dev/null +++ b/homeassistant/components/aranet/translations/es.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "integrations_diabled": "Este dispositivo no tiene integraciones habilitadas. Por favor, habilita las integraciones de hogares inteligentes usando la aplicaci\u00f3n y int\u00e9ntalo de nuevo.", + "no_devices_found": "No se han encontrado dispositivos Aranet no configurados.", + "outdated_version": "Este dispositivo est\u00e1 utilizando firmware obsoleto. Por favor, actual\u00edzalo al menos a v1.2.0 e int\u00e9ntalo de nuevo." + }, + "error": { + "unknown": "Error inesperado" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "\u00bfQuieres configurar {name}?" + }, + "user": { + "data": { + "address": "Dispositivo" + }, + "description": "Elige un dispositivo para configurar" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/et.json b/homeassistant/components/aranet/translations/et.json new file mode 100644 index 00000000000..989add44454 --- /dev/null +++ b/homeassistant/components/aranet/translations/et.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Seade on juba h\u00e4\u00e4lestatud", + "integrations_diabled": "Sellel seadmel pole sidumised lubatud. Luba rakenduse abil nutika kodu sidumine ja proovi uuesti.", + "no_devices_found": "H\u00e4\u00e4lestamata Araneti seadmeid ei leitud.", + "outdated_version": "See seade kasutab aegunud p\u00fcsivara. V\u00e4rskenda see v\u00e4hemalt versioonile 1.2.0 ja proovi uuesti." + }, + "error": { + "unknown": "Ootamatu t\u00f5rge" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Kas seadistada {name}?" + }, + "user": { + "data": { + "address": "Seade" + }, + "description": "Vali h\u00e4\u00e4lestatav seade" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/hu.json b/homeassistant/components/aranet/translations/hu.json new file mode 100644 index 00000000000..2773a11e3dc --- /dev/null +++ b/homeassistant/components/aranet/translations/hu.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", + "integrations_diabled": "Ezen az eszk\u00f6z\u00f6n nincs enged\u00e9lyezve az integr\u00e1ci\u00f3. K\u00e9rj\u00fck, enged\u00e9lyezze az okosotthon-integr\u00e1ci\u00f3kat az alkalmaz\u00e1s seg\u00edts\u00e9g\u00e9vel, \u00e9s pr\u00f3b\u00e1lja meg \u00fajra.", + "no_devices_found": "Nem tal\u00e1lhat\u00f3 konfigur\u00e1latlan Aranet eszk\u00f6z.", + "outdated_version": "Ez az eszk\u00f6z elavult firmware-t haszn\u00e1l. K\u00e9rj\u00fck, friss\u00edtse legal\u00e1bb 1.2.0-s verzi\u00f3ra, \u00e9s pr\u00f3b\u00e1lja \u00fajra." + }, + "error": { + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name}?" + }, + "user": { + "data": { + "address": "Eszk\u00f6z" + }, + "description": "V\u00e1lasszon egy be\u00e1ll\u00edtand\u00f3 eszk\u00f6zt" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/no.json b/homeassistant/components/aranet/translations/no.json new file mode 100644 index 00000000000..8e4a732972a --- /dev/null +++ b/homeassistant/components/aranet/translations/no.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten er allerede konfigurert", + "integrations_diabled": "Denne enheten har ikke integrasjoner aktivert. Aktiver smarthus-integrasjoner ved hjelp av appen og pr\u00f8v igjen.", + "no_devices_found": "Fant ingen ukonfigurerte Aranet-enheter.", + "outdated_version": "Denne enheten bruker utdatert fastvare. Oppdater den til minst v1.2.0 og pr\u00f8v igjen." + }, + "error": { + "unknown": "Uventet feil" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Vil du konfigurere {name}?" + }, + "user": { + "data": { + "address": "Enhet" + }, + "description": "Velg en enhet du vil konfigurere" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/pt-BR.json b/homeassistant/components/aranet/translations/pt-BR.json new file mode 100644 index 00000000000..f98a79dabaa --- /dev/null +++ b/homeassistant/components/aranet/translations/pt-BR.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "integrations_diabled": "Este dispositivo n\u00e3o tem integra\u00e7\u00f5es ativadas. Ative as integra\u00e7\u00f5es de casa inteligente usando o aplicativo e tente novamente.", + "no_devices_found": "Nenhum dispositivo Aranet n\u00e3o configurado encontrado.", + "outdated_version": "Este dispositivo est\u00e1 usando um firmware desatualizado. Atualize-o para pelo menos a v1.2.0 e tente novamente." + }, + "error": { + "unknown": "Erro inesperado" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Deseja configurar {name}?" + }, + "user": { + "data": { + "address": "Dispositivo" + }, + "description": "Escolha um dispositivo para configurar" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/ru.json b/homeassistant/components/aranet/translations/ru.json new file mode 100644 index 00000000000..11477454e63 --- /dev/null +++ b/homeassistant/components/aranet/translations/ru.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", + "integrations_diabled": "\u041d\u0430 \u044d\u0442\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435 \u043d\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f. \u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0443\u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443.", + "no_devices_found": "\u041d\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e.", + "outdated_version": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0443\u044e \u043f\u0440\u043e\u0448\u0438\u0432\u043a\u0443. \u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0435\u0451 \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u0434\u043e \u0432\u0435\u0440\u0441\u0438\u0438 1.2.0 \u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443." + }, + "error": { + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name}?" + }, + "user": { + "data": { + "address": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + }, + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fireservicerota/translations/ca.json b/homeassistant/components/fireservicerota/translations/ca.json index 261350db3f8..9961f00f38f 100644 --- a/homeassistant/components/fireservicerota/translations/ca.json +++ b/homeassistant/components/fireservicerota/translations/ca.json @@ -17,6 +17,12 @@ }, "description": "Els tokens d'autenticaci\u00f3 ja no s\u00f3n v\u00e0lids, inicia sessi\u00f3 per tornar-los a generar." }, + "reauth_confirm": { + "data": { + "password": "Contrasenya" + }, + "description": "Els 'tokens' d'autenticaci\u00f3 ja no s\u00f3n v\u00e0lids, inicia sessi\u00f3 per tornar-los a generar." + }, "user": { "data": { "password": "Contrasenya", diff --git a/homeassistant/components/fireservicerota/translations/en.json b/homeassistant/components/fireservicerota/translations/en.json index 38762b614f4..71c4f757b53 100644 --- a/homeassistant/components/fireservicerota/translations/en.json +++ b/homeassistant/components/fireservicerota/translations/en.json @@ -11,6 +11,12 @@ "invalid_auth": "Invalid authentication" }, "step": { + "reauth": { + "data": { + "password": "Password" + }, + "description": "Authentication tokens became invalid, login to recreate them." + }, "reauth_confirm": { "data": { "password": "Password" diff --git a/homeassistant/components/fireservicerota/translations/es.json b/homeassistant/components/fireservicerota/translations/es.json index ddd231ce700..297653c7708 100644 --- a/homeassistant/components/fireservicerota/translations/es.json +++ b/homeassistant/components/fireservicerota/translations/es.json @@ -17,6 +17,12 @@ }, "description": "Los tokens de autenticaci\u00f3n dejaron de ser v\u00e1lidos, inicia sesi\u00f3n para volver a crearlos." }, + "reauth_confirm": { + "data": { + "password": "Contrase\u00f1a" + }, + "description": "Los tokens de autenticaci\u00f3n dejaron de ser v\u00e1lidos, inicia sesi\u00f3n para volver a crearlos." + }, "user": { "data": { "password": "Contrase\u00f1a", diff --git a/homeassistant/components/fireservicerota/translations/hu.json b/homeassistant/components/fireservicerota/translations/hu.json index 3bda2225400..47ce01be5d3 100644 --- a/homeassistant/components/fireservicerota/translations/hu.json +++ b/homeassistant/components/fireservicerota/translations/hu.json @@ -17,6 +17,12 @@ }, "description": "A hiteles\u00edt\u00e9si tokenek \u00e9rv\u00e9nytelenn\u00e9 v\u00e1ltak, a l\u00e9trehoz\u00e1shoz jelentkezzen be." }, + "reauth_confirm": { + "data": { + "password": "Jelsz\u00f3" + }, + "description": "A hiteles\u00edt\u00e9si tokenek \u00e9rv\u00e9nytelenn\u00e9 v\u00e1ltak, a l\u00e9trehoz\u00e1shoz jelentkezzen be." + }, "user": { "data": { "password": "Jelsz\u00f3", diff --git a/homeassistant/components/fireservicerota/translations/pt-BR.json b/homeassistant/components/fireservicerota/translations/pt-BR.json index 45b55aa5324..9e6abd2964a 100644 --- a/homeassistant/components/fireservicerota/translations/pt-BR.json +++ b/homeassistant/components/fireservicerota/translations/pt-BR.json @@ -17,6 +17,12 @@ }, "description": "Os tokens de autentica\u00e7\u00e3o se tornaram inv\u00e1lidos, fa\u00e7a login para recri\u00e1-los." }, + "reauth_confirm": { + "data": { + "password": "Senha" + }, + "description": "Os tokens de autentica\u00e7\u00e3o se tornaram inv\u00e1lidos, fa\u00e7a login para recri\u00e1-los." + }, "user": { "data": { "password": "Senha", diff --git a/homeassistant/components/google_travel_time/translations/no.json b/homeassistant/components/google_travel_time/translations/no.json index 2a056451bbe..0a9cb3e6d83 100644 --- a/homeassistant/components/google_travel_time/translations/no.json +++ b/homeassistant/components/google_travel_time/translations/no.json @@ -28,6 +28,7 @@ "mode": "Reisemodus", "time": "Tid", "time_type": "Tidstype", + "traffic_mode": "Trafikkmodus", "transit_mode": "Transittmodus", "transit_routing_preference": "Ruteinnstillinger for kollektivtransport", "units": "Enheter" diff --git a/homeassistant/components/google_travel_time/translations/pt-BR.json b/homeassistant/components/google_travel_time/translations/pt-BR.json index 9b8249b1b51..baa16f38f35 100644 --- a/homeassistant/components/google_travel_time/translations/pt-BR.json +++ b/homeassistant/components/google_travel_time/translations/pt-BR.json @@ -28,6 +28,7 @@ "mode": "Modo de viagem", "time": "Tempo", "time_type": "Tipo de tempo", + "traffic_mode": "Modo de tr\u00e1fego", "transit_mode": "Modo de tr\u00e2nsito", "transit_routing_preference": "Prefer\u00eancia de rota de tr\u00e2nsito", "units": "Unidades" diff --git a/homeassistant/components/hassio/translations/ca.json b/homeassistant/components/hassio/translations/ca.json index 2c4285d4908..14679301993 100644 --- a/homeassistant/components/hassio/translations/ca.json +++ b/homeassistant/components/hassio/translations/ca.json @@ -1,4 +1,14 @@ { + "issues": { + "unhealthy": { + "description": "El sistema no \u00e9s saludable a causa de '{reason}'. Clica l'enlla\u00e7 per obtenir m\u00e9s informaci\u00f3 sobre qu\u00e8 falla aix\u00f2 i com solucionar-ho.", + "title": "Sistema no saludable - {reason}" + }, + "unsupported": { + "description": "El sistema no \u00e9s compatible a causa de '{reason}'. Clica l'enlla\u00e7 per obtenir m\u00e9s informaci\u00f3 sobre qu\u00e8 significa aix\u00f2 i com tornar a un sistema compatible.", + "title": "Sistema no compatible - {reason}" + } + }, "system_health": { "info": { "agent_version": "Versi\u00f3 de l'agent", diff --git a/homeassistant/components/hassio/translations/en.json b/homeassistant/components/hassio/translations/en.json index 14d79f0d8d6..b6f006e3093 100644 --- a/homeassistant/components/hassio/translations/en.json +++ b/homeassistant/components/hassio/translations/en.json @@ -1,4 +1,14 @@ { + "issues": { + "unhealthy": { + "description": "System is currently unhealthy due to '{reason}'. Use the link to learn more about what is wrong and how to fix it.", + "title": "Unhealthy system - {reason}" + }, + "unsupported": { + "description": "System is unsupported due to '{reason}'. Use the link to learn more about what this means and how to return to a supported system.", + "title": "Unsupported system - {reason}" + } + }, "system_health": { "info": { "agent_version": "Agent Version", diff --git a/homeassistant/components/hassio/translations/es.json b/homeassistant/components/hassio/translations/es.json index 102256ef117..f2aef9d7214 100644 --- a/homeassistant/components/hassio/translations/es.json +++ b/homeassistant/components/hassio/translations/es.json @@ -1,4 +1,14 @@ { + "issues": { + "unhealthy": { + "description": "Actualmente el sistema no est\u00e1 en buen estado debido a ''{reason}''. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n sobre lo que est\u00e1 mal y c\u00f3mo solucionarlo.", + "title": "Sistema en mal estado: {reason}" + }, + "unsupported": { + "description": "El sistema no es compatible debido a ''{reason}''. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n sobre lo que esto significa y c\u00f3mo volver a un sistema compatible.", + "title": "Sistema no compatible: {reason}" + } + }, "system_health": { "info": { "agent_version": "Versi\u00f3n del agente", diff --git a/homeassistant/components/hassio/translations/et.json b/homeassistant/components/hassio/translations/et.json index b86eef353b9..ea0f78c0c57 100644 --- a/homeassistant/components/hassio/translations/et.json +++ b/homeassistant/components/hassio/translations/et.json @@ -1,4 +1,14 @@ { + "issues": { + "unhealthy": { + "description": "S\u00fcsteem ei ole praegu korras '{reason}' t\u00f5ttu. Kasuta linki, et saada rohkem teavet selle kohta, mis on valesti ja kuidas seda parandada.", + "title": "Vigane s\u00fcsteem \u2013 {reason}" + }, + "unsupported": { + "description": "S\u00fcsteemi ei toetata '{reason}' t\u00f5ttu. Kasuta linki, et saada lisateavet selle kohta, mida see t\u00e4hendab ja kuidas toetatud s\u00fcsteemi naasta.", + "title": "Toetamata s\u00fcsteem \u2013 {reason}" + } + }, "system_health": { "info": { "agent_version": "Agendi versioon", diff --git a/homeassistant/components/hassio/translations/hu.json b/homeassistant/components/hassio/translations/hu.json index 4c83b94935d..604a8ae59e6 100644 --- a/homeassistant/components/hassio/translations/hu.json +++ b/homeassistant/components/hassio/translations/hu.json @@ -1,4 +1,14 @@ { + "issues": { + "unhealthy": { + "description": "A rendszer jelenleg renellenes \u00e1llapotban van '{reason}' miatt. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet is megtudhat arr\u00f3l, hogy mi a probl\u00e9ma, \u00e9s hogyan jav\u00edthatja ki.", + "title": "Rendellenes \u00e1llapot \u2013 {reason}" + }, + "unsupported": { + "description": "A rendszer nem t\u00e1mogatott a k\u00f6vetkez\u0151 miatt: '{reason}'. A hivatkoz\u00e1s seg\u00edts\u00e9g\u00e9vel t\u00f6bbet megtudhat arr\u00f3l, mit jelent ez, \u00e9s hogyan t\u00e9rhet vissza egy t\u00e1mogatott rendszerhez.", + "title": "Nem t\u00e1mogatott rendszer \u2013 {reason}" + } + }, "system_health": { "info": { "agent_version": "\u00dcgyn\u00f6k verzi\u00f3", diff --git a/homeassistant/components/hassio/translations/pt-BR.json b/homeassistant/components/hassio/translations/pt-BR.json index 4f3e5d84ec1..47e0b6df4ae 100644 --- a/homeassistant/components/hassio/translations/pt-BR.json +++ b/homeassistant/components/hassio/translations/pt-BR.json @@ -1,4 +1,14 @@ { + "issues": { + "unhealthy": { + "description": "O sistema n\u00e3o est\u00e1 \u00edntegro devido a '{reason}'. Use o link para saber mais sobre o que est\u00e1 errado e como corrigi-lo.", + "title": "Sistema insalubre - {reason}" + }, + "unsupported": { + "description": "O sistema n\u00e3o \u00e9 suportado devido a '{reason}'. Use o link para saber mais sobre o que isso significa e como retornar a um sistema compat\u00edvel.", + "title": "Sistema n\u00e3o suportado - {reason}" + } + }, "system_health": { "info": { "agent_version": "Vers\u00e3o do Agent", diff --git a/homeassistant/components/hassio/translations/ru.json b/homeassistant/components/hassio/translations/ru.json index 5e1caa41ebf..0ab366c1775 100644 --- a/homeassistant/components/hassio/translations/ru.json +++ b/homeassistant/components/hassio/translations/ru.json @@ -1,4 +1,14 @@ { + "issues": { + "unhealthy": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0435\u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u0430 \u043f\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 '{reason}'. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u0447\u0442\u043e \u043d\u0435 \u0442\u0430\u043a \u0438 \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - {reason}" + }, + "unsupported": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 '{reason}'. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u0447\u0442\u043e \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442 \u0438 \u043a\u0430\u043a \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - {reason}" + } + }, "system_health": { "info": { "agent_version": "\u0412\u0435\u0440\u0441\u0438\u044f \u0430\u0433\u0435\u043d\u0442\u0430", diff --git a/homeassistant/components/mqtt/translations/bg.json b/homeassistant/components/mqtt/translations/bg.json index f99f120d951..06751555b29 100644 --- a/homeassistant/components/mqtt/translations/bg.json +++ b/homeassistant/components/mqtt/translations/bg.json @@ -6,17 +6,20 @@ }, "error": { "bad_certificate": "CA \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u044a\u0442 \u0435 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d", - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u0431\u0440\u043e\u043a\u0435\u0440\u0430." + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u0431\u0440\u043e\u043a\u0435\u0440\u0430.", + "invalid_inclusion": "\u041a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u044f\u0442 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u0438 \u0447\u0430\u0441\u0442\u043d\u0438\u044f\u0442 \u043a\u043b\u044e\u0447 \u0442\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0431\u044a\u0434\u0430\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0438 \u0437\u0430\u0435\u0434\u043d\u043e" }, "step": { "broker": { "data": { "advanced_options": "\u0420\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438 \u043e\u043f\u0446\u0438\u0438", "broker": "\u0411\u0440\u043e\u043a\u0435\u0440", + "client_id": "ID \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 (\u043e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e \u0437\u0430 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u043d)", "discovery": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e\u0442\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "port": "\u041f\u043e\u0440\u0442", "protocol": "MQTT \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b", + "set_client_cert": "\u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" }, "description": "\u041c\u043e\u043b\u044f, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\u0442\u0430 \u0437\u0430 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u0412\u0430\u0448\u0438\u044f MQTT \u0431\u0440\u043e\u043a\u0435\u0440." @@ -40,15 +43,21 @@ "options": { "error": { "bad_certificate": "CA \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u044a\u0442 \u0435 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d", - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_inclusion": "\u041a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u044f\u0442 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u0438 \u0447\u0430\u0441\u0442\u043d\u0438\u044f\u0442 \u043a\u043b\u044e\u0447 \u0442\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0431\u044a\u0434\u0430\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0438 \u0437\u0430\u0435\u0434\u043d\u043e" }, "step": { "broker": { "data": { "advanced_options": "\u0420\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438 \u043e\u043f\u0446\u0438\u0438", + "certificate": "\u041a\u0430\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0444\u0430\u0439\u043b \u0441 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u043d \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043d\u0430 CA", + "client_cert": "\u041a\u0430\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0444\u0430\u0439\u043b \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442", + "client_id": "ID \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 (\u043e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e \u0437\u0430 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u043d)", + "client_key": "\u041a\u0430\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0444\u0430\u0439\u043b \u0441 \u0447\u0430\u0441\u0442\u0435\u043d \u043a\u043b\u044e\u0447", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "port": "\u041f\u043e\u0440\u0442", "protocol": "MQTT \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b", + "set_client_cert": "\u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" } } diff --git a/homeassistant/components/ovo_energy/translations/no.json b/homeassistant/components/ovo_energy/translations/no.json index 64b989de78b..23473cca581 100644 --- a/homeassistant/components/ovo_energy/translations/no.json +++ b/homeassistant/components/ovo_energy/translations/no.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "OVO-konto-ID (bare legg til hvis du har flere kontoer)", "password": "Passord", "username": "Brukernavn" }, diff --git a/homeassistant/components/ovo_energy/translations/pt-BR.json b/homeassistant/components/ovo_energy/translations/pt-BR.json index 4b73ecca8b9..7af4adf2389 100644 --- a/homeassistant/components/ovo_energy/translations/pt-BR.json +++ b/homeassistant/components/ovo_energy/translations/pt-BR.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "ID da conta OVO (adicione apenas se voc\u00ea tiver v\u00e1rias contas)", "password": "Senha", "username": "Usu\u00e1rio" }, diff --git a/homeassistant/components/rainmachine/translations/no.json b/homeassistant/components/rainmachine/translations/no.json index f7e0a758ee0..f738be0ad7d 100644 --- a/homeassistant/components/rainmachine/translations/no.json +++ b/homeassistant/components/rainmachine/translations/no.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "Bruk sonekj\u00f8ringstider fra RainMachine-appen", "zone_run_time": "Standard sonekj\u00f8ringstid (i sekunder)" }, "title": "Konfigurer RainMachine" diff --git a/homeassistant/components/scrape/translations/bg.json b/homeassistant/components/scrape/translations/bg.json index 1599a1918d7..095249f5317 100644 --- a/homeassistant/components/scrape/translations/bg.json +++ b/homeassistant/components/scrape/translations/bg.json @@ -18,6 +18,11 @@ } } }, + "issues": { + "moved_yaml": { + "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Scrape \u0435 \u043f\u0440\u0435\u043c\u0435\u0441\u0442\u0435\u043d\u0430" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/scrape/translations/et.json b/homeassistant/components/scrape/translations/et.json index 14daf835af8..7168041b566 100644 --- a/homeassistant/components/scrape/translations/et.json +++ b/homeassistant/components/scrape/translations/et.json @@ -36,6 +36,12 @@ } } }, + "issues": { + "moved_yaml": { + "description": "Scrape'i konfigureerimine YAML-i abil on viidud integratsiooniv\u00f5tmesse.\n\nOlemasolev YAML-konfiguratsioon t\u00f6\u00f6tab veel 2 versiooni.\n\nMigreeri YAML-konfiguratsioon integratsiooniv\u00f5tmesse vastavalt dokumentatsioonile.", + "title": "Scrape YAML-i konfiguratsioon on teisaldatud" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/scrape/translations/hu.json b/homeassistant/components/scrape/translations/hu.json index 7af59751b98..002891fb47d 100644 --- a/homeassistant/components/scrape/translations/hu.json +++ b/homeassistant/components/scrape/translations/hu.json @@ -36,6 +36,12 @@ } } }, + "issues": { + "moved_yaml": { + "description": "A Scrape konfigur\u00e1l\u00e1sa YAML haszn\u00e1lat\u00e1val \u00e1tker\u00fclt az integr\u00e1ci\u00f3ba.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3 m\u00e9g 2 verzi\u00f3n kereszt\u00fcl fog m\u0171k\u00f6dni.\n\nA dokument\u00e1ci\u00f3nak megfelel\u0151en migr\u00e1lja a YAML konfigur\u00e1ci\u00f3j\u00e1t az integr\u00e1ci\u00f3s kulcsra.", + "title": "A Scrape YAML konfigur\u00e1ci\u00f3 \u00e1thelyez\u00e9sre ker\u00fclt" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/scrape/translations/no.json b/homeassistant/components/scrape/translations/no.json index 6738c8a630a..abf5580074a 100644 --- a/homeassistant/components/scrape/translations/no.json +++ b/homeassistant/components/scrape/translations/no.json @@ -36,6 +36,12 @@ } } }, + "issues": { + "moved_yaml": { + "description": "Konfigurering av Scrape ved hjelp av YAML har blitt flyttet til integrasjonsn\u00f8kkel. \n\n Din eksisterende YAML-konfigurasjon vil fungere for 2 flere versjoner. \n\n Migrer YAML-konfigurasjonen til integrasjonsn\u00f8kkelen i henhold til dokumentasjonen.", + "title": "Scrape YAML-konfigurasjonen er flyttet" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/scrape/translations/pt-BR.json b/homeassistant/components/scrape/translations/pt-BR.json index 84d7aaf6807..55e322ab6c9 100644 --- a/homeassistant/components/scrape/translations/pt-BR.json +++ b/homeassistant/components/scrape/translations/pt-BR.json @@ -36,6 +36,12 @@ } } }, + "issues": { + "moved_yaml": { + "description": "A configura\u00e7\u00e3o do Scrape usando YAML foi movida para a chave de integra\u00e7\u00e3o. \n\n Sua configura\u00e7\u00e3o YAML existente funcionar\u00e1 para mais duas vers\u00f5es. \n\n Migre sua configura\u00e7\u00e3o YAML para a chave de integra\u00e7\u00e3o de acordo com a documenta\u00e7\u00e3o.", + "title": "A configura\u00e7\u00e3o YAML de Scrape foi movida" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/statistics/translations/no.json b/homeassistant/components/statistics/translations/no.json new file mode 100644 index 00000000000..c3e3369a59a --- /dev/null +++ b/homeassistant/components/statistics/translations/no.json @@ -0,0 +1,12 @@ +{ + "issues": { + "deprecation_warning_characteristic": { + "description": "Konfigurasjonsparameteren `state_characteristic` for statistikkintegrasjonen vil bli obligatorisk. \n\n Vennligst legg til `state_characteristic: {characteristic} ` til konfigurasjonen av sensor ` {entity} ` for \u00e5 beholde gjeldende oppf\u00f8rsel. \n\n Les dokumentasjonen av statistikkintegrasjonen for ytterligere detaljer: https://www.home-assistant.io/integrations/statistics/", + "title": "Obligatorisk \"state_characteristic\" antatt for en statistikkenhet" + }, + "deprecation_warning_size": { + "description": "Konfigurasjonsparameteren `sampling_size` for statistikkintegrasjonen har standardverdien 20 s\u00e5 langt, som vil endres. \n\n Vennligst sjekk konfigurasjonen for sensor ` {entity} ` og legg til passende grenser, f.eks. `sampling_size: 20` for \u00e5 beholde gjeldende virkem\u00e5te. Konfigurasjonen av statistikkintegrasjonen vil bli mer fleksibel med versjon 2022.12.0 og godta enten `sampling_size` eller `max_age`, eller begge innstillingene. Foresp\u00f8rselen ovenfor forbereder konfigurasjonen din for denne ellers \u00f8deleggende endringen. \n\n Les dokumentasjonen av statistikkintegrasjonen for ytterligere detaljer: https://www.home-assistant.io/integrations/statistics/", + "title": "Implisitt 'sampling_size' antatt for en statistikkenhet" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/transmission/translations/hu.json b/homeassistant/components/transmission/translations/hu.json index 1bd7129ed6b..b9eae6b7f6f 100644 --- a/homeassistant/components/transmission/translations/hu.json +++ b/homeassistant/components/transmission/translations/hu.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "Friss\u00edtsen minden olyan automatiz\u00e1l\u00e1st vagy szkriptet, amely ezt a szolg\u00e1ltat\u00e1st haszn\u00e1lja, \u00e9s cser\u00e9lje ki a name kulcsot a entry_id kulcsra.", + "title": "A n\u00e9vkulcs a Transmission szolg\u00e1ltat\u00e1sokban elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" + } + } + }, + "title": "A n\u00e9vkulcs a Transmission szolg\u00e1ltat\u00e1sokban elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/transmission/translations/no.json b/homeassistant/components/transmission/translations/no.json index dfe188f6e3b..2a03ee48b74 100644 --- a/homeassistant/components/transmission/translations/no.json +++ b/homeassistant/components/transmission/translations/no.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "Oppdater eventuelle automatiseringer eller skript som bruker denne tjenesten og erstatt navnen\u00f8kkelen med entry_id-n\u00f8kkelen.", + "title": "Navnen\u00f8kkelen i overf\u00f8ringstjenester fjernes" + } + } + }, + "title": "Navnen\u00f8kkelen i overf\u00f8ringstjenester fjernes" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/transmission/translations/pt-BR.json b/homeassistant/components/transmission/translations/pt-BR.json index 5579b64e2d9..878e911564d 100644 --- a/homeassistant/components/transmission/translations/pt-BR.json +++ b/homeassistant/components/transmission/translations/pt-BR.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "Atualize quaisquer automa\u00e7\u00f5es ou scripts que usam esse servi\u00e7o e substitua a chave de nome pela chave entry_id.", + "title": "A chave de nome nos servi\u00e7os de transmiss\u00e3o est\u00e1 sendo removida" + } + } + }, + "title": "A chave de nome nos servi\u00e7os de transmiss\u00e3o est\u00e1 sendo removida" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/zamg/translations/no.json b/homeassistant/components/zamg/translations/no.json new file mode 100644 index 00000000000..265fff5c70d --- /dev/null +++ b/homeassistant/components/zamg/translations/no.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten er allerede konfigurert", + "cannot_connect": "Tilkobling mislyktes" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "Stasjons-ID (standard til n\u00e6rmeste stasjon)" + }, + "description": "Konfigurer ZAMG for \u00e5 integrere med Home Assistant." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfigurering av ZAMG med YAML blir fjernet. \n\n Din eksisterende YAML-konfigurasjon har blitt importert til brukergrensesnittet automatisk. \n\n Fjern ZAMG YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", + "title": "ZAMG YAML-konfigurasjonen blir fjernet" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zha/translations/es.json b/homeassistant/components/zha/translations/es.json index 2919302a1ac..b31f1c10d78 100644 --- a/homeassistant/components/zha/translations/es.json +++ b/homeassistant/components/zha/translations/es.json @@ -150,7 +150,7 @@ "device_flipped": "Dispositivo volteado \"{subtype}\"", "device_knocked": "Dispositivo golpeado \"{subtype}\"", "device_offline": "Dispositivo sin conexi\u00f3n", - "device_rotated": "Dispositivo girado \"{subtype}\"", + "device_rotated": "Dispositivo rotado \"{subtype}\"", "device_shaken": "Dispositivo agitado", "device_slid": "Dispositivo deslizado \"{subtype}\"", "device_tilted": "Dispositivo inclinado", From d87ca0b09992e444b192074fef38e73e9341efe3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 31 Oct 2022 20:21:11 -0500 Subject: [PATCH 0132/1033] Improve esphome bluetooth error reporting (#81326) --- homeassistant/components/esphome/bluetooth/client.py | 10 ++++++++-- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index 5f20a73f4d6..72531a2503a 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -7,7 +7,11 @@ import logging from typing import Any, TypeVar, cast import uuid -from aioesphomeapi import ESP_CONNECTION_ERROR_DESCRIPTION, BLEConnectionError +from aioesphomeapi import ( + ESP_CONNECTION_ERROR_DESCRIPTION, + ESPHOME_GATT_ERRORS, + BLEConnectionError, +) from aioesphomeapi.connection import APIConnectionError, TimeoutAPIError import async_timeout from bleak.backends.characteristic import BleakGATTCharacteristic @@ -207,7 +211,9 @@ class ESPHomeClient(BaseBleakClient): human_error = ESP_CONNECTION_ERROR_DESCRIPTION[ble_connection_error] except (KeyError, ValueError): ble_connection_error_name = str(error) - human_error = f"Unknown error code {error}" + human_error = ESPHOME_GATT_ERRORS.get( + error, f"Unknown error code {error}" + ) connected_future.set_exception( BleakError( f"Error {ble_connection_error_name} while connecting: {human_error}" diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index c27e3b8dc3e..64cd6b4029c 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==11.4.1"], + "requirements": ["aioesphomeapi==11.4.2"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/requirements_all.txt b/requirements_all.txt index 5fb9331bd29..be619a1239c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -153,7 +153,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.4.1 +aioesphomeapi==11.4.2 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ca71daa39be..73be12c5fea 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -140,7 +140,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.4.1 +aioesphomeapi==11.4.2 # homeassistant.components.flo aioflo==2021.11.0 From eea47195444b53f100d83a2a67ecba149b0acc1d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 31 Oct 2022 20:21:40 -0500 Subject: [PATCH 0133/1033] Fix Yale Access Bluetooth not being available again after being unavailable (#81320) --- homeassistant/components/yalexs_ble/__init__.py | 13 +++++++++++++ homeassistant/components/yalexs_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/yalexs_ble/__init__.py b/homeassistant/components/yalexs_ble/__init__.py index 6073bf7a032..7a2b3146265 100644 --- a/homeassistant/components/yalexs_ble/__init__.py +++ b/homeassistant/components/yalexs_ble/__init__.py @@ -94,6 +94,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: entry.title, push_lock ) + @callback + def _async_device_unavailable( + _service_info: bluetooth.BluetoothServiceInfoBleak, + ) -> None: + """Handle device not longer being seen by the bluetooth stack.""" + push_lock.reset_advertisement_state() + + entry.async_on_unload( + bluetooth.async_track_unavailable( + hass, _async_device_unavailable, push_lock.address + ) + ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) entry.async_on_unload(entry.add_update_listener(_async_update_listener)) return True diff --git a/homeassistant/components/yalexs_ble/manifest.json b/homeassistant/components/yalexs_ble/manifest.json index 7bc8bde5b30..b43ce18a7e9 100644 --- a/homeassistant/components/yalexs_ble/manifest.json +++ b/homeassistant/components/yalexs_ble/manifest.json @@ -3,7 +3,7 @@ "name": "Yale Access Bluetooth", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yalexs_ble", - "requirements": ["yalexs-ble==1.9.4"], + "requirements": ["yalexs-ble==1.9.5"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [ diff --git a/requirements_all.txt b/requirements_all.txt index be619a1239c..5b5b94d966b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2580,7 +2580,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.9.4 +yalexs-ble==1.9.5 # homeassistant.components.august yalexs==1.2.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 73be12c5fea..4a949ae7d13 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1790,7 +1790,7 @@ xmltodict==0.13.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.9.4 +yalexs-ble==1.9.5 # homeassistant.components.august yalexs==1.2.6 From 52fe40d53913ab484578252014fb2a2442cf7f6e Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Tue, 1 Nov 2022 02:22:21 +0100 Subject: [PATCH 0134/1033] Only try initializing Hue motion LED on endpoint 2 with ZHA (#81205) --- homeassistant/components/zha/core/channels/general.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index ded51455af8..c028a6021da 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -156,7 +156,7 @@ class BasicChannel(ZigbeeChannel): def __init__(self, cluster: zigpy.zcl.Cluster, ch_pool: ChannelPool) -> None: """Initialize Basic channel.""" super().__init__(cluster, ch_pool) - if is_hue_motion_sensor(self): + if is_hue_motion_sensor(self) and self.cluster.endpoint.endpoint_id == 2: self.ZCL_INIT_ATTRS = ( # pylint: disable=invalid-name self.ZCL_INIT_ATTRS.copy() ) From ed2696f03e40c2175bf42c6ad315c4fc72e51071 Mon Sep 17 00:00:00 2001 From: Willem-Jan van Rootselaar Date: Tue, 1 Nov 2022 09:28:02 +0100 Subject: [PATCH 0135/1033] Bump python-bsblan to version 0.5.7 (#81330) --- homeassistant/components/bsblan/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/bsblan/manifest.json b/homeassistant/components/bsblan/manifest.json index e2b0e5bebfa..87fb7f8c08f 100644 --- a/homeassistant/components/bsblan/manifest.json +++ b/homeassistant/components/bsblan/manifest.json @@ -3,7 +3,7 @@ "name": "BSB-Lan", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/bsblan", - "requirements": ["python-bsblan==0.5.6"], + "requirements": ["python-bsblan==0.5.7"], "codeowners": ["@liudger"], "iot_class": "local_polling", "loggers": ["bsblan"] diff --git a/requirements_all.txt b/requirements_all.txt index 5b5b94d966b..d0f0572b9f5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1953,7 +1953,7 @@ pythinkingcleaner==0.0.3 python-blockchain-api==0.0.2 # homeassistant.components.bsblan -python-bsblan==0.5.6 +python-bsblan==0.5.7 # homeassistant.components.clementine python-clementine-remote==1.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4a949ae7d13..f18b83b13fe 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1373,7 +1373,7 @@ pytankerkoenig==0.0.6 pytautulli==21.11.0 # homeassistant.components.bsblan -python-bsblan==0.5.6 +python-bsblan==0.5.7 # homeassistant.components.ecobee python-ecobee-api==0.2.14 From 16beed25654d4bd0829b9020ffa1fa6837784216 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Tue, 1 Nov 2022 09:29:38 +0100 Subject: [PATCH 0136/1033] Always use Celsius in Shelly integration (#80842) --- homeassistant/components/shelly/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/homeassistant/components/shelly/__init__.py b/homeassistant/components/shelly/__init__.py index 921ffb352d5..b65c314789a 100644 --- a/homeassistant/components/shelly/__init__.py +++ b/homeassistant/components/shelly/__init__.py @@ -16,7 +16,6 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import aiohttp_client, device_registry import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType -from homeassistant.util.unit_system import METRIC_SYSTEM from .const import ( CONF_COAP_PORT, @@ -113,13 +112,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def _async_setup_block_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Shelly block based device from a config entry.""" - temperature_unit = "C" if hass.config.units is METRIC_SYSTEM else "F" - options = aioshelly.common.ConnectionOptions( entry.data[CONF_HOST], entry.data.get(CONF_USERNAME), entry.data.get(CONF_PASSWORD), - temperature_unit, ) coap_context = await get_coap_context(hass) From 2b935564c2cbe139e4591e5ca21eebdc28022b58 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Tue, 1 Nov 2022 02:08:36 -0700 Subject: [PATCH 0137/1033] Bump gcal_sync to 2.2.2 and fix recurring event bug (#81339) * Bump gcal_sync to 2.2.2 and fix recurring event bug * Bump to 2.2.2 --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index ce95e3112ee..9a184bdd636 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==2.2.0", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==2.2.2", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index d0f0572b9f5..e16974be627 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -728,7 +728,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==2.2.0 +gcal-sync==2.2.2 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f18b83b13fe..c48ecf6bbd2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -544,7 +544,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==2.2.0 +gcal-sync==2.2.2 # homeassistant.components.geocaching geocachingapi==0.2.1 From 3afef1f8fe65ad6c13efcea85425837360c4a466 Mon Sep 17 00:00:00 2001 From: Ron Klinkien Date: Tue, 1 Nov 2022 10:10:30 +0100 Subject: [PATCH 0138/1033] Add task id attribute to fireservicerota sensor (#81323) --- homeassistant/components/fireservicerota/sensor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/fireservicerota/sensor.py b/homeassistant/components/fireservicerota/sensor.py index 36455da9fb7..1484ff7f154 100644 --- a/homeassistant/components/fireservicerota/sensor.py +++ b/homeassistant/components/fireservicerota/sensor.py @@ -79,6 +79,7 @@ class IncidentsSensor(RestoreEntity, SensorEntity): "type", "responder_mode", "can_respond_until", + "task_ids", ): if data.get(value): attr[value] = data[value] From 9dd6d5d0abe21445d985c5d2fd01ff2b6d1a67d5 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 1 Nov 2022 10:17:01 +0100 Subject: [PATCH 0139/1033] Fix power/energy mixup in Youless (#81345) --- homeassistant/components/youless/sensor.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/youless/sensor.py b/homeassistant/components/youless/sensor.py index 19e9c635dce..53ffb223939 100644 --- a/homeassistant/components/youless/sensor.py +++ b/homeassistant/components/youless/sensor.py @@ -39,13 +39,13 @@ async def async_setup_entry( async_add_entities( [ GasSensor(coordinator, device), - PowerMeterSensor( + EnergyMeterSensor( coordinator, device, "low", SensorStateClass.TOTAL_INCREASING ), - PowerMeterSensor( + EnergyMeterSensor( coordinator, device, "high", SensorStateClass.TOTAL_INCREASING ), - PowerMeterSensor(coordinator, device, "total", SensorStateClass.TOTAL), + EnergyMeterSensor(coordinator, device, "total", SensorStateClass.TOTAL), CurrentPowerSensor(coordinator, device), DeliveryMeterSensor(coordinator, device, "low"), DeliveryMeterSensor(coordinator, device, "high"), @@ -68,10 +68,6 @@ class YoulessBaseSensor(CoordinatorEntity, SensorEntity): ) -> None: """Create the sensor.""" super().__init__(coordinator) - self._device = device - self._device_group = device_group - self._sensor_id = sensor_id - self._attr_unique_id = f"{DOMAIN}_{device}_{sensor_id}" self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, f"{device}_{device_group}")}, @@ -149,10 +145,10 @@ class DeliveryMeterSensor(YoulessBaseSensor): ) -> None: """Instantiate a delivery meter sensor.""" super().__init__( - coordinator, device, "delivery", "Power delivery", f"delivery_{dev_type}" + coordinator, device, "delivery", "Energy delivery", f"delivery_{dev_type}" ) self._type = dev_type - self._attr_name = f"Power delivery {dev_type}" + self._attr_name = f"Energy delivery {dev_type}" @property def get_sensor(self) -> YoulessSensor | None: @@ -163,7 +159,7 @@ class DeliveryMeterSensor(YoulessBaseSensor): return getattr(self.coordinator.data.delivery_meter, f"_{self._type}", None) -class PowerMeterSensor(YoulessBaseSensor): +class EnergyMeterSensor(YoulessBaseSensor): """The Youless low meter value sensor.""" _attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR @@ -177,13 +173,13 @@ class PowerMeterSensor(YoulessBaseSensor): dev_type: str, state_class: SensorStateClass, ) -> None: - """Instantiate a power meter sensor.""" + """Instantiate a energy meter sensor.""" super().__init__( - coordinator, device, "power", "Power usage", f"power_{dev_type}" + coordinator, device, "power", "Energy usage", f"power_{dev_type}" ) self._device = device self._type = dev_type - self._attr_name = f"Power {dev_type}" + self._attr_name = f"Energy {dev_type}" self._attr_state_class = state_class @property From e22f69ea8c7921c9f1351a916ca6561c3913754d Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 1 Nov 2022 10:34:44 +0100 Subject: [PATCH 0140/1033] Update Pillow to 9.3.0 (#81343) --- homeassistant/components/doods/manifest.json | 2 +- homeassistant/components/generic/manifest.json | 2 +- homeassistant/components/image/manifest.json | 2 +- homeassistant/components/proxy/manifest.json | 2 +- homeassistant/components/qrcode/manifest.json | 2 +- homeassistant/components/seven_segments/manifest.json | 2 +- homeassistant/components/sighthound/manifest.json | 2 +- homeassistant/components/tensorflow/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/doods/manifest.json b/homeassistant/components/doods/manifest.json index 766167b7af9..39ed9c552bb 100644 --- a/homeassistant/components/doods/manifest.json +++ b/homeassistant/components/doods/manifest.json @@ -2,7 +2,7 @@ "domain": "doods", "name": "DOODS - Dedicated Open Object Detection Service", "documentation": "https://www.home-assistant.io/integrations/doods", - "requirements": ["pydoods==1.0.2", "pillow==9.2.0"], + "requirements": ["pydoods==1.0.2", "pillow==9.3.0"], "codeowners": [], "iot_class": "local_polling", "loggers": ["pydoods"] diff --git a/homeassistant/components/generic/manifest.json b/homeassistant/components/generic/manifest.json index 83b34f73dc8..938e685dcab 100644 --- a/homeassistant/components/generic/manifest.json +++ b/homeassistant/components/generic/manifest.json @@ -2,7 +2,7 @@ "domain": "generic", "name": "Generic Camera", "config_flow": true, - "requirements": ["ha-av==10.0.0", "pillow==9.2.0"], + "requirements": ["ha-av==10.0.0", "pillow==9.3.0"], "dependencies": ["http"], "documentation": "https://www.home-assistant.io/integrations/generic", "codeowners": ["@davet2001"], diff --git a/homeassistant/components/image/manifest.json b/homeassistant/components/image/manifest.json index ed500c89011..888d6fc1fab 100644 --- a/homeassistant/components/image/manifest.json +++ b/homeassistant/components/image/manifest.json @@ -3,7 +3,7 @@ "name": "Image", "config_flow": false, "documentation": "https://www.home-assistant.io/integrations/image", - "requirements": ["pillow==9.2.0"], + "requirements": ["pillow==9.3.0"], "dependencies": ["http"], "codeowners": ["@home-assistant/core"], "quality_scale": "internal", diff --git a/homeassistant/components/proxy/manifest.json b/homeassistant/components/proxy/manifest.json index 59d19bc785e..e863122b872 100644 --- a/homeassistant/components/proxy/manifest.json +++ b/homeassistant/components/proxy/manifest.json @@ -2,6 +2,6 @@ "domain": "proxy", "name": "Camera Proxy", "documentation": "https://www.home-assistant.io/integrations/proxy", - "requirements": ["pillow==9.2.0"], + "requirements": ["pillow==9.3.0"], "codeowners": [] } diff --git a/homeassistant/components/qrcode/manifest.json b/homeassistant/components/qrcode/manifest.json index 666cc0c93ce..1a394e17f29 100644 --- a/homeassistant/components/qrcode/manifest.json +++ b/homeassistant/components/qrcode/manifest.json @@ -2,7 +2,7 @@ "domain": "qrcode", "name": "QR Code", "documentation": "https://www.home-assistant.io/integrations/qrcode", - "requirements": ["pillow==9.2.0", "pyzbar==0.1.7"], + "requirements": ["pillow==9.3.0", "pyzbar==0.1.7"], "codeowners": [], "iot_class": "calculated", "loggers": ["pyzbar"] diff --git a/homeassistant/components/seven_segments/manifest.json b/homeassistant/components/seven_segments/manifest.json index 09457fc4ca5..cfe834bd648 100644 --- a/homeassistant/components/seven_segments/manifest.json +++ b/homeassistant/components/seven_segments/manifest.json @@ -2,7 +2,7 @@ "domain": "seven_segments", "name": "Seven Segments OCR", "documentation": "https://www.home-assistant.io/integrations/seven_segments", - "requirements": ["pillow==9.2.0"], + "requirements": ["pillow==9.3.0"], "codeowners": ["@fabaff"], "iot_class": "local_polling" } diff --git a/homeassistant/components/sighthound/manifest.json b/homeassistant/components/sighthound/manifest.json index 400664079e2..042a642429f 100644 --- a/homeassistant/components/sighthound/manifest.json +++ b/homeassistant/components/sighthound/manifest.json @@ -2,7 +2,7 @@ "domain": "sighthound", "name": "Sighthound", "documentation": "https://www.home-assistant.io/integrations/sighthound", - "requirements": ["pillow==9.2.0", "simplehound==0.3"], + "requirements": ["pillow==9.3.0", "simplehound==0.3"], "codeowners": ["@robmarkcole"], "iot_class": "cloud_polling", "loggers": ["simplehound"] diff --git a/homeassistant/components/tensorflow/manifest.json b/homeassistant/components/tensorflow/manifest.json index 9f719b7e1b3..ef02f208e8c 100644 --- a/homeassistant/components/tensorflow/manifest.json +++ b/homeassistant/components/tensorflow/manifest.json @@ -7,7 +7,7 @@ "tf-models-official==2.5.0", "pycocotools==2.0.1", "numpy==1.23.2", - "pillow==9.2.0" + "pillow==9.3.0" ], "codeowners": [], "iot_class": "local_polling", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index adff342729d..3a2f4b5a7f8 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -28,7 +28,7 @@ jinja2==3.1.2 lru-dict==1.1.8 orjson==3.8.1 paho-mqtt==1.6.1 -pillow==9.2.0 +pillow==9.3.0 pip>=21.0,<22.4 psutil-home-assistant==0.0.1 pyserial==3.5 diff --git a/requirements_all.txt b/requirements_all.txt index e16974be627..ee39f1fc66d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1300,7 +1300,7 @@ pilight==0.1.1 # homeassistant.components.seven_segments # homeassistant.components.sighthound # homeassistant.components.tensorflow -pillow==9.2.0 +pillow==9.3.0 # homeassistant.components.dominos pizzapi==0.0.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c48ecf6bbd2..9ad0ce653c6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -930,7 +930,7 @@ pilight==0.1.1 # homeassistant.components.seven_segments # homeassistant.components.sighthound # homeassistant.components.tensorflow -pillow==9.2.0 +pillow==9.3.0 # homeassistant.components.plex plexapi==4.13.0 From f5f96535ad1ae00df83d8f4b5dcf6f5a592f979f Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Tue, 1 Nov 2022 12:53:44 +0200 Subject: [PATCH 0141/1033] Bump aioshelly to 4.1.2 (#81342) --- homeassistant/components/shelly/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/shelly/manifest.json b/homeassistant/components/shelly/manifest.json index 07499ce1e9d..70970e73e30 100644 --- a/homeassistant/components/shelly/manifest.json +++ b/homeassistant/components/shelly/manifest.json @@ -3,7 +3,7 @@ "name": "Shelly", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/shelly", - "requirements": ["aioshelly==4.1.1"], + "requirements": ["aioshelly==4.1.2"], "dependencies": ["http"], "zeroconf": [ { diff --git a/requirements_all.txt b/requirements_all.txt index ee39f1fc66d..0df45788f9e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -255,7 +255,7 @@ aiosenseme==0.6.1 aiosenz==1.0.0 # homeassistant.components.shelly -aioshelly==4.1.1 +aioshelly==4.1.2 # homeassistant.components.skybell aioskybell==22.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9ad0ce653c6..2732638cedb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -230,7 +230,7 @@ aiosenseme==0.6.1 aiosenz==1.0.0 # homeassistant.components.shelly -aioshelly==4.1.1 +aioshelly==4.1.2 # homeassistant.components.skybell aioskybell==22.7.0 From 514f619cff20bb1e58db7a05e0bf0514d00ec5b6 Mon Sep 17 00:00:00 2001 From: javicalle <31999997+javicalle@users.noreply.github.com> Date: Tue, 1 Nov 2022 13:51:20 +0100 Subject: [PATCH 0142/1033] Tuya configuration for `tuya_manufacturer` cluster (#81311) * Tuya configuration for tuya_manufacturer cluster * fix codespell * Add attributes initialization * Fix pylint complaints --- .../zha/core/channels/manufacturerspecific.py | 35 +++++++++++ .../components/zha/core/registries.py | 1 + homeassistant/components/zha/select.py | 59 +++++++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/homeassistant/components/zha/core/channels/manufacturerspecific.py b/homeassistant/components/zha/core/channels/manufacturerspecific.py index 5139854d66a..814e7700d01 100644 --- a/homeassistant/components/zha/core/channels/manufacturerspecific.py +++ b/homeassistant/components/zha/core/channels/manufacturerspecific.py @@ -59,6 +59,41 @@ class PhillipsRemote(ZigbeeChannel): REPORT_CONFIG = () +@registries.CHANNEL_ONLY_CLUSTERS.register(registries.TUYA_MANUFACTURER_CLUSTER) +@registries.ZIGBEE_CHANNEL_REGISTRY.register(registries.TUYA_MANUFACTURER_CLUSTER) +class TuyaChannel(ZigbeeChannel): + """Channel for the Tuya manufacturer Zigbee cluster.""" + + REPORT_CONFIG = () + + def __init__(self, cluster: zigpy.zcl.Cluster, ch_pool: ChannelPool) -> None: + """Initialize TuyaChannel.""" + super().__init__(cluster, ch_pool) + + if self.cluster.endpoint.manufacturer in ( + "_TZE200_7tdtqgwv", + "_TZE200_amp6tsvy", + "_TZE200_oisqyl4o", + "_TZE200_vhy3iakz", + "_TZ3000_uim07oem", + "_TZE200_wfxuhoea", + "_TZE200_tviaymwx", + "_TZE200_g1ib5ldv", + "_TZE200_wunufsil", + "_TZE200_7deq70b8", + "_TZE200_tz32mtza", + "_TZE200_2hf7x9n3", + "_TZE200_aqnazj70", + "_TZE200_1ozguk6x", + "_TZE200_k6jhsr0q", + "_TZE200_9mahtqtg", + ): + self.ZCL_INIT_ATTRS = { # pylint: disable=invalid-name + "backlight_mode": True, + "power_on_state": True, + } + + @registries.CHANNEL_ONLY_CLUSTERS.register(0xFCC0) @registries.ZIGBEE_CHANNEL_REGISTRY.register(0xFCC0) class OppleRemote(ZigbeeChannel): diff --git a/homeassistant/components/zha/core/registries.py b/homeassistant/components/zha/core/registries.py index 2480cf1cd43..42f6bb55f51 100644 --- a/homeassistant/components/zha/core/registries.py +++ b/homeassistant/components/zha/core/registries.py @@ -33,6 +33,7 @@ PHILLIPS_REMOTE_CLUSTER = 0xFC00 SMARTTHINGS_ACCELERATION_CLUSTER = 0xFC02 SMARTTHINGS_ARRIVAL_SENSOR_DEVICE_TYPE = 0x8000 SMARTTHINGS_HUMIDITY_CLUSTER = 0xFC45 +TUYA_MANUFACTURER_CLUSTER = 0xEF00 VOC_LEVEL_CLUSTER = 0x042E REMOTE_DEVICE_TYPES = { diff --git a/homeassistant/components/zha/select.py b/homeassistant/components/zha/select.py index 38f2f417643..5ac0ec6d164 100644 --- a/homeassistant/components/zha/select.py +++ b/homeassistant/components/zha/select.py @@ -240,6 +240,27 @@ class TuyaPowerOnState(types.enum8): channel_names=CHANNEL_ON_OFF, models={"TS011F", "TS0121", "TS0001", "TS0002", "TS0003", "TS0004"}, ) +@CONFIG_DIAGNOSTIC_MATCH( + channel_names="tuya_manufacturer", + manufacturers={ + "_TZE200_7tdtqgwv", + "_TZE200_amp6tsvy", + "_TZE200_oisqyl4o", + "_TZE200_vhy3iakz", + "_TZ3000_uim07oem", + "_TZE200_wfxuhoea", + "_TZE200_tviaymwx", + "_TZE200_g1ib5ldv", + "_TZE200_wunufsil", + "_TZE200_7deq70b8", + "_TZE200_tz32mtza", + "_TZE200_2hf7x9n3", + "_TZE200_aqnazj70", + "_TZE200_1ozguk6x", + "_TZE200_k6jhsr0q", + "_TZE200_9mahtqtg", + }, +) class TuyaPowerOnStateSelectEntity(ZCLEnumSelectEntity, id_suffix="power_on_state"): """Representation of a ZHA power on state select entity.""" @@ -248,6 +269,44 @@ class TuyaPowerOnStateSelectEntity(ZCLEnumSelectEntity, id_suffix="power_on_stat _attr_name = "Power on state" +class MoesBacklightMode(types.enum8): + """MOES switch backlight mode enum.""" + + Off = 0x00 + LightWhenOn = 0x01 + LightWhenOff = 0x02 + Freeze = 0x03 + + +@CONFIG_DIAGNOSTIC_MATCH( + channel_names="tuya_manufacturer", + manufacturers={ + "_TZE200_7tdtqgwv", + "_TZE200_amp6tsvy", + "_TZE200_oisqyl4o", + "_TZE200_vhy3iakz", + "_TZ3000_uim07oem", + "_TZE200_wfxuhoea", + "_TZE200_tviaymwx", + "_TZE200_g1ib5ldv", + "_TZE200_wunufsil", + "_TZE200_7deq70b8", + "_TZE200_tz32mtza", + "_TZE200_2hf7x9n3", + "_TZE200_aqnazj70", + "_TZE200_1ozguk6x", + "_TZE200_k6jhsr0q", + "_TZE200_9mahtqtg", + }, +) +class MoesBacklightModeSelectEntity(ZCLEnumSelectEntity, id_suffix="backlight_mode"): + """Moes devices have a different backlight mode select options.""" + + _select_attr = "backlight_mode" + _enum = MoesBacklightMode + _attr_name = "Backlight mode" + + class AqaraMotionSensitivities(types.enum8): """Aqara motion sensitivities.""" From 5f1c92ce51e5138e2a1bc5c2c581b2c053518659 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Tue, 1 Nov 2022 09:06:55 -0400 Subject: [PATCH 0143/1033] Fix individual LED range for ZHA device action (#81351) The inovelli individual LED effect device action can address 7 LEDs. I had set the range 1-7 but it should be 0-6. --- homeassistant/components/zha/device_action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/zha/device_action.py b/homeassistant/components/zha/device_action.py index 1cb988b1c15..3e2a3591c80 100644 --- a/homeassistant/components/zha/device_action.py +++ b/homeassistant/components/zha/device_action.py @@ -93,7 +93,7 @@ DEVICE_ACTION_SCHEMAS = { ), INOVELLI_INDIVIDUAL_LED_EFFECT: vol.Schema( { - vol.Required("led_number"): vol.All(vol.Coerce(int), vol.Range(1, 7)), + vol.Required("led_number"): vol.All(vol.Coerce(int), vol.Range(0, 6)), vol.Required("effect_type"): vol.In( InovelliConfigEntityChannel.LEDEffectType.__members__.keys() ), From 8d50b05d0d9058abb21ac2fcc0f4631d39a0578c Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Tue, 1 Nov 2022 14:30:42 +0100 Subject: [PATCH 0144/1033] Add ability to set device class on knx sensor (#81278) Add ability to set device class on sensor --- homeassistant/components/knx/schema.py | 7 ++++++- homeassistant/components/knx/sensor.py | 21 +++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/knx/schema.py b/homeassistant/components/knx/schema.py index c1615b7e8e2..13f6f153daf 100644 --- a/homeassistant/components/knx/schema.py +++ b/homeassistant/components/knx/schema.py @@ -21,7 +21,11 @@ from homeassistant.components.cover import ( DEVICE_CLASSES_SCHEMA as COVER_DEVICE_CLASSES_SCHEMA, ) from homeassistant.components.number import NumberMode -from homeassistant.components.sensor import CONF_STATE_CLASS, STATE_CLASSES_SCHEMA +from homeassistant.components.sensor import ( + CONF_STATE_CLASS, + DEVICE_CLASSES_SCHEMA as SENSOR_DEVICE_CLASSES_SCHEMA, + STATE_CLASSES_SCHEMA, +) from homeassistant.components.switch import ( DEVICE_CLASSES_SCHEMA as SWITCH_DEVICE_CLASSES_SCHEMA, ) @@ -855,6 +859,7 @@ class SensorSchema(KNXPlatformSchema): vol.Optional(CONF_STATE_CLASS): STATE_CLASSES_SCHEMA, vol.Required(CONF_TYPE): sensor_type_validator, vol.Required(CONF_STATE_ADDRESS): ga_list_validator, + vol.Optional(CONF_DEVICE_CLASS): SENSOR_DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, } ) diff --git a/homeassistant/components/knx/sensor.py b/homeassistant/components/knx/sensor.py index ceb9f435d83..20bbddf14a1 100644 --- a/homeassistant/components/knx/sensor.py +++ b/homeassistant/components/knx/sensor.py @@ -12,7 +12,13 @@ from homeassistant.components.sensor import ( DEVICE_CLASSES, SensorEntity, ) -from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME, CONF_TYPE, Platform +from homeassistant.const import ( + CONF_DEVICE_CLASS, + CONF_ENTITY_CATEGORY, + CONF_NAME, + CONF_TYPE, + Platform, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, StateType @@ -54,11 +60,14 @@ class KNXSensor(KnxEntity, SensorEntity): def __init__(self, xknx: XKNX, config: ConfigType) -> None: """Initialize of a KNX sensor.""" super().__init__(_create_sensor(xknx, config)) - self._attr_device_class = ( - self._device.ha_device_class() - if self._device.ha_device_class() in DEVICE_CLASSES - else None - ) + if device_class := config.get(CONF_DEVICE_CLASS): + self._attr_device_class = device_class + else: + self._attr_device_class = ( + self._device.ha_device_class() + if self._device.ha_device_class() in DEVICE_CLASSES + else None + ) self._attr_force_update = self._device.always_callback self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_unique_id = str(self._device.sensor_value.group_address_state) From 509d5fd69d09cff311750420879fc18a02955bf1 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Tue, 1 Nov 2022 15:57:48 +0100 Subject: [PATCH 0145/1033] Lower log level for non-JSON payload in MQTT update (#81348) Change log level --- homeassistant/components/mqtt/update.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/update.py b/homeassistant/components/mqtt/update.py index 986ad013520..5536d16d1c7 100644 --- a/homeassistant/components/mqtt/update.py +++ b/homeassistant/components/mqtt/update.py @@ -181,9 +181,9 @@ class MqttUpdate(MqttEntity, UpdateEntity, RestoreEntity): msg.topic, ) except JSON_DECODE_EXCEPTIONS: - _LOGGER.warning( + _LOGGER.debug( "No valid (JSON) payload detected after processing payload '%s' on topic %s", - json_payload, + payload, msg.topic, ) json_payload["installed_version"] = payload From db0785827f6bab45de7d6f5860aaedeeb64d3b94 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 1 Nov 2022 16:25:01 +0100 Subject: [PATCH 0146/1033] Revert "Do not write state if payload is `''`" for MQTT sensor (#81347) * Revert "Do not write state if payload is ''" This reverts commit 869c11884e2b06d5f5cb5a8a4f78247a6972149e. * Add test --- homeassistant/components/mqtt/sensor.py | 4 ++-- tests/components/mqtt/test_sensor.py | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index d95d669e72f..52ba1a7e3c2 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -271,8 +271,8 @@ class MqttSensor(MqttEntity, RestoreSensor): ) elif self.device_class == SensorDeviceClass.DATE: payload = payload.date() - if payload != "": - self._state = payload + + self._state = payload def _update_last_reset(msg): payload = self._last_reset_template(msg.payload) diff --git a/tests/components/mqtt/test_sensor.py b/tests/components/mqtt/test_sensor.py index 6cfaa9678bb..1884d04efc3 100644 --- a/tests/components/mqtt/test_sensor.py +++ b/tests/components/mqtt/test_sensor.py @@ -313,6 +313,12 @@ async def test_setting_sensor_value_via_mqtt_json_message( assert state.state == "100" + # Make sure the state is written when a sensor value is reset to '' + async_fire_mqtt_message(hass, "test-topic", '{ "val": "" }') + state = hass.states.get("sensor.test") + + assert state.state == "" + async def test_setting_sensor_value_via_mqtt_json_message_and_default_current_state( hass, mqtt_mock_entry_with_yaml_config From 9be204629bb4e11a4f385bd003cac9fb46d76e76 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Nov 2022 11:44:58 -0500 Subject: [PATCH 0147/1033] Bump aiohomekit to 2.2.11 (#81358) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 93aae62daab..6533d7f29be 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.10"], + "requirements": ["aiohomekit==2.2.11"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index 0df45788f9e..aea2dbd2563 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.10 +aiohomekit==2.2.11 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2732638cedb..80ef0d3228e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.10 +aiohomekit==2.2.11 # homeassistant.components.emulated_hue # homeassistant.components.http From 5b09ab93dcd50026b3b4c33dd920dc788ee77adf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Nov 2022 12:07:03 -0500 Subject: [PATCH 0148/1033] Immediately prefer advertisements from alternate sources when a scanner goes away (#81357) --- homeassistant/components/bluetooth/manager.py | 5 + tests/components/bluetooth/test_manager.py | 92 +++++++++++++++++-- 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/bluetooth/manager.py b/homeassistant/components/bluetooth/manager.py index c3a0e0998f1..d29023acef7 100644 --- a/homeassistant/components/bluetooth/manager.py +++ b/homeassistant/components/bluetooth/manager.py @@ -127,6 +127,7 @@ class BluetoothManager: self._non_connectable_scanners: list[BaseHaScanner] = [] self._connectable_scanners: list[BaseHaScanner] = [] self._adapters: dict[str, AdapterDetails] = {} + self._sources: set[str] = set() @property def supports_passive_scan(self) -> bool: @@ -379,6 +380,7 @@ class BluetoothManager: if ( (old_service_info := all_history.get(address)) and source != old_service_info.source + and old_service_info.source in self._sources and self._prefer_previous_adv_from_different_source( old_service_info, service_info ) @@ -398,6 +400,7 @@ class BluetoothManager: # the old connectable advertisement or ( source != old_connectable_service_info.source + and old_connectable_service_info.source in self._sources and self._prefer_previous_adv_from_different_source( old_connectable_service_info, service_info ) @@ -597,8 +600,10 @@ class BluetoothManager: def _unregister_scanner() -> None: self._advertisement_tracker.async_remove_source(scanner.source) scanners.remove(scanner) + self._sources.remove(scanner.source) scanners.append(scanner) + self._sources.add(scanner.source) return _unregister_scanner @hass_callback diff --git a/tests/components/bluetooth/test_manager.py b/tests/components/bluetooth/test_manager.py index c6a65046ef9..0375f68309f 100644 --- a/tests/components/bluetooth/test_manager.py +++ b/tests/components/bluetooth/test_manager.py @@ -5,11 +5,14 @@ from unittest.mock import AsyncMock, MagicMock, patch from bleak.backends.scanner import BLEDevice from bluetooth_adapters import AdvertisementHistory +import pytest from homeassistant.components import bluetooth +from homeassistant.components.bluetooth import models from homeassistant.components.bluetooth.manager import ( FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, ) +from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component from . import ( @@ -20,8 +23,28 @@ from . import ( ) +@pytest.fixture +def register_hci0_scanner(hass: HomeAssistant) -> None: + """Register an hci0 scanner.""" + cancel = bluetooth.async_register_scanner( + hass, models.BaseHaScanner(hass, "hci0"), True + ) + yield + cancel() + + +@pytest.fixture +def register_hci1_scanner(hass: HomeAssistant) -> None: + """Register an hci1 scanner.""" + cancel = bluetooth.async_register_scanner( + hass, models.BaseHaScanner(hass, "hci1"), True + ) + yield + cancel() + + async def test_advertisements_do_not_switch_adapters_for_no_reason( - hass, enable_bluetooth + hass, enable_bluetooth, register_hci0_scanner, register_hci1_scanner ): """Test we only switch adapters when needed.""" @@ -68,7 +91,9 @@ async def test_advertisements_do_not_switch_adapters_for_no_reason( ) -async def test_switching_adapters_based_on_rssi(hass, enable_bluetooth): +async def test_switching_adapters_based_on_rssi( + hass, enable_bluetooth, register_hci0_scanner, register_hci1_scanner +): """Test switching adapters based on rssi.""" address = "44:44:33:11:23:45" @@ -122,7 +147,9 @@ async def test_switching_adapters_based_on_rssi(hass, enable_bluetooth): ) -async def test_switching_adapters_based_on_zero_rssi(hass, enable_bluetooth): +async def test_switching_adapters_based_on_zero_rssi( + hass, enable_bluetooth, register_hci0_scanner, register_hci1_scanner +): """Test switching adapters based on zero rssi.""" address = "44:44:33:11:23:45" @@ -176,7 +203,9 @@ async def test_switching_adapters_based_on_zero_rssi(hass, enable_bluetooth): ) -async def test_switching_adapters_based_on_stale(hass, enable_bluetooth): +async def test_switching_adapters_based_on_stale( + hass, enable_bluetooth, register_hci0_scanner, register_hci1_scanner +): """Test switching adapters based on the previous advertisement being stale.""" address = "44:44:33:11:23:41" @@ -256,7 +285,7 @@ async def test_restore_history_from_dbus(hass, one_adapter): async def test_switching_adapters_based_on_rssi_connectable_to_non_connectable( - hass, enable_bluetooth + hass, enable_bluetooth, register_hci0_scanner, register_hci1_scanner ): """Test switching adapters based on rssi from connectable to non connectable.""" @@ -339,7 +368,7 @@ async def test_switching_adapters_based_on_rssi_connectable_to_non_connectable( async def test_connectable_advertisement_can_be_retrieved_with_best_path_is_non_connectable( - hass, enable_bluetooth + hass, enable_bluetooth, register_hci0_scanner, register_hci1_scanner ): """Test we can still get a connectable BLEDevice when the best path is non-connectable. @@ -384,3 +413,54 @@ async def test_connectable_advertisement_can_be_retrieved_with_best_path_is_non_ bluetooth.async_ble_device_from_address(hass, address, True) is switchbot_device_poor_signal ) + + +async def test_switching_adapters_when_one_goes_away( + hass, enable_bluetooth, register_hci0_scanner +): + """Test switching adapters when one goes away.""" + cancel_hci2 = bluetooth.async_register_scanner( + hass, models.BaseHaScanner(hass, "hci2"), True + ) + + address = "44:44:33:11:23:45" + + switchbot_device_good_signal = BLEDevice(address, "wohand_good_signal") + switchbot_adv_good_signal = generate_advertisement_data( + local_name="wohand_good_signal", service_uuids=[], rssi=-60 + ) + inject_advertisement_with_source( + hass, switchbot_device_good_signal, switchbot_adv_good_signal, "hci2" + ) + + assert ( + bluetooth.async_ble_device_from_address(hass, address) + is switchbot_device_good_signal + ) + + switchbot_device_poor_signal = BLEDevice(address, "wohand_poor_signal") + switchbot_adv_poor_signal = generate_advertisement_data( + local_name="wohand_poor_signal", service_uuids=[], rssi=-100 + ) + inject_advertisement_with_source( + hass, switchbot_device_poor_signal, switchbot_adv_poor_signal, "hci0" + ) + + # We want to prefer the good signal when we have options + assert ( + bluetooth.async_ble_device_from_address(hass, address) + is switchbot_device_good_signal + ) + + cancel_hci2() + + inject_advertisement_with_source( + hass, switchbot_device_poor_signal, switchbot_adv_poor_signal, "hci0" + ) + + # Now that hci2 is gone, we should prefer the poor signal + # since no poor signal is better than no signal + assert ( + bluetooth.async_ble_device_from_address(hass, address) + is switchbot_device_poor_signal + ) From 972b36b469d0212f3de2231b8e5f0e5c2029f63d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Nov 2022 12:07:42 -0500 Subject: [PATCH 0149/1033] Adjust time to remove stale connectable devices from the esphome ble to closer match bluez (#81356) --- .../components/esphome/bluetooth/scanner.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/esphome/bluetooth/scanner.py b/homeassistant/components/esphome/bluetooth/scanner.py index 7c8064d5583..4fbaf7cabb6 100644 --- a/homeassistant/components/esphome/bluetooth/scanner.py +++ b/homeassistant/components/esphome/bluetooth/scanner.py @@ -6,6 +6,7 @@ import datetime from datetime import timedelta import re import time +from typing import Final from aioesphomeapi import BluetoothLEAdvertisement from bleak.backends.device import BLEDevice @@ -23,6 +24,15 @@ from homeassistant.util.dt import monotonic_time_coarse TWO_CHAR = re.compile("..") +# The maximum time between advertisements for a device to be considered +# stale when the advertisement tracker can determine the interval for +# connectable devices. +# +# BlueZ uses 180 seconds by default but we give it a bit more time +# to account for the esp32's bluetooth stack being a bit slower +# than BlueZ's. +CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS: Final = 195 + class ESPHomeScanner(BaseHaScanner): """Scanner for esphome.""" @@ -45,8 +55,12 @@ class ESPHomeScanner(BaseHaScanner): self._connector = connector self._connectable = connectable self._details: dict[str, str | HaBluetoothConnector] = {"source": scanner_id} + self._fallback_seconds = FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS if connectable: self._details["connector"] = connector + self._fallback_seconds = ( + CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + ) @callback def async_setup(self) -> CALLBACK_TYPE: @@ -61,7 +75,7 @@ class ESPHomeScanner(BaseHaScanner): expired = [ address for address, timestamp in self._discovered_device_timestamps.items() - if now - timestamp > FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + if now - timestamp > self._fallback_seconds ] for address in expired: del self._discovered_device_advertisement_datas[address] From b4637fae37661dac6af5685912dac34a4ae4dc7c Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 1 Nov 2022 13:57:53 -0400 Subject: [PATCH 0150/1033] Bump zigpy-zigate to 0.10.3 (#81363) --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 79980d763e7..e40a54c11bc 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -11,7 +11,7 @@ "zigpy-deconz==0.19.0", "zigpy==0.51.5", "zigpy-xbee==0.16.2", - "zigpy-zigate==0.10.2", + "zigpy-zigate==0.10.3", "zigpy-znp==0.9.1" ], "usb": [ diff --git a/requirements_all.txt b/requirements_all.txt index aea2dbd2563..5cd546685f0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2625,7 +2625,7 @@ zigpy-deconz==0.19.0 zigpy-xbee==0.16.2 # homeassistant.components.zha -zigpy-zigate==0.10.2 +zigpy-zigate==0.10.3 # homeassistant.components.zha zigpy-znp==0.9.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 80ef0d3228e..593e262b8bd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1820,7 +1820,7 @@ zigpy-deconz==0.19.0 zigpy-xbee==0.16.2 # homeassistant.components.zha -zigpy-zigate==0.10.2 +zigpy-zigate==0.10.3 # homeassistant.components.zha zigpy-znp==0.9.1 From 5c99e2e5d3ae40c7107ab0e7576f330f8721ee88 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 1 Nov 2022 19:11:50 +0100 Subject: [PATCH 0151/1033] Improve error logging of WebSocket API (#81360) --- .../components/websocket_api/connection.py | 12 ++- .../websocket_api/test_connection.py | 92 +++++++++++++++---- 2 files changed, 82 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/websocket_api/connection.py b/homeassistant/components/websocket_api/connection.py index c344e1c6a9f..ab4dda845db 100644 --- a/homeassistant/components/websocket_api/connection.py +++ b/homeassistant/components/websocket_api/connection.py @@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any import voluptuous as vol from homeassistant.auth.models import RefreshToken, User +from homeassistant.components.http import current_request from homeassistant.core import Context, HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError, Unauthorized @@ -137,6 +138,13 @@ class ActiveConnection: err_message = "Unknown error" log_handler = self.logger.exception - log_handler("Error handling message: %s (%s)", err_message, code) - self.send_message(messages.error_message(msg["id"], code, err_message)) + + if code: + err_message += f" ({code})" + if request := current_request.get(): + err_message += f" from {request.remote}" + if user_agent := request.headers.get("user-agent"): + err_message += f" ({user_agent})" + + log_handler("Error handling message: %s", err_message) diff --git a/tests/components/websocket_api/test_connection.py b/tests/components/websocket_api/test_connection.py index fd9af99c1a4..8f2cd43fdb8 100644 --- a/tests/components/websocket_api/test_connection.py +++ b/tests/components/websocket_api/test_connection.py @@ -1,8 +1,11 @@ """Test WebSocket Connection class.""" import asyncio import logging -from unittest.mock import Mock +from typing import Any +from unittest.mock import AsyncMock, Mock, patch +from aiohttp.test_utils import make_mocked_request +import pytest import voluptuous as vol from homeassistant import exceptions @@ -11,37 +14,86 @@ from homeassistant.components import websocket_api from tests.common import MockUser -async def test_exception_handling(): - """Test handling of exceptions.""" - send_messages = [] - user = MockUser() - refresh_token = Mock() - conn = websocket_api.ActiveConnection( - logging.getLogger(__name__), None, send_messages.append, user, refresh_token - ) - - for (exc, code, err) in ( - (exceptions.Unauthorized(), websocket_api.ERR_UNAUTHORIZED, "Unauthorized"), +@pytest.mark.parametrize( + "exc,code,err,log", + [ + ( + exceptions.Unauthorized(), + websocket_api.ERR_UNAUTHORIZED, + "Unauthorized", + "Error handling message: Unauthorized (unauthorized) from 127.0.0.42 (Browser)", + ), ( vol.Invalid("Invalid something"), websocket_api.ERR_INVALID_FORMAT, "Invalid something. Got {'id': 5}", + "Error handling message: Invalid something. Got {'id': 5} (invalid_format) from 127.0.0.42 (Browser)", + ), + ( + asyncio.TimeoutError(), + websocket_api.ERR_TIMEOUT, + "Timeout", + "Error handling message: Timeout (timeout) from 127.0.0.42 (Browser)", ), - (asyncio.TimeoutError(), websocket_api.ERR_TIMEOUT, "Timeout"), ( exceptions.HomeAssistantError("Failed to do X"), websocket_api.ERR_UNKNOWN_ERROR, "Failed to do X", + "Error handling message: Failed to do X (unknown_error) from 127.0.0.42 (Browser)", ), - (ValueError("Really bad"), websocket_api.ERR_UNKNOWN_ERROR, "Unknown error"), ( - exceptions.HomeAssistantError(), + ValueError("Really bad"), websocket_api.ERR_UNKNOWN_ERROR, "Unknown error", + "Error handling message: Unknown error (unknown_error) from 127.0.0.42 (Browser)", ), - ): - send_messages.clear() + ( + exceptions.HomeAssistantError, + websocket_api.ERR_UNKNOWN_ERROR, + "Unknown error", + "Error handling message: Unknown error (unknown_error) from 127.0.0.42 (Browser)", + ), + ], +) +async def test_exception_handling( + caplog: pytest.LogCaptureFixture, + exc: Exception, + code: str, + err: str, + log: str, +): + """Test handling of exceptions.""" + send_messages = [] + user = MockUser() + refresh_token = Mock() + current_request = AsyncMock() + + def get_extra_info(key: str) -> Any: + if key == "sslcontext": + return True + + if key == "peername": + return ("127.0.0.42", 8123) + + mocked_transport = Mock() + mocked_transport.get_extra_info = get_extra_info + mocked_request = make_mocked_request( + "GET", + "/api/websocket", + headers={"Host": "example.com", "User-Agent": "Browser"}, + transport=mocked_transport, + ) + + with patch( + "homeassistant.components.websocket_api.connection.current_request", + ) as current_request: + current_request.get.return_value = mocked_request + conn = websocket_api.ActiveConnection( + logging.getLogger(__name__), None, send_messages.append, user, refresh_token + ) + conn.async_handle_exception({"id": 5}, exc) - assert len(send_messages) == 1 - assert send_messages[0]["error"]["code"] == code - assert send_messages[0]["error"]["message"] == err + assert len(send_messages) == 1 + assert send_messages[0]["error"]["code"] == code + assert send_messages[0]["error"]["message"] == err + assert log in caplog.text From 914ccdbc4f931603aebc009983315ed15e695a59 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Nov 2022 13:21:20 -0500 Subject: [PATCH 0152/1033] Fix unload race in unifiprotect tests (#81361) --- .../unifiprotect/test_config_flow.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/tests/components/unifiprotect/test_config_flow.py b/tests/components/unifiprotect/test_config_flow.py index d0fb0dba9f2..dc91b0f6dde 100644 --- a/tests/components/unifiprotect/test_config_flow.py +++ b/tests/components/unifiprotect/test_config_flow.py @@ -254,23 +254,28 @@ async def test_form_options(hass: HomeAssistant, ufp_client: ProtectApiClient) - await hass.async_block_till_done() assert mock_config.state == config_entries.ConfigEntryState.LOADED - result = await hass.config_entries.options.async_init(mock_config.entry_id) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] - assert result["step_id"] == "init" + result = await hass.config_entries.options.async_init(mock_config.entry_id) + assert result["type"] == FlowResultType.FORM + assert not result["errors"] + assert result["step_id"] == "init" - result2 = await hass.config_entries.options.async_configure( - result["flow_id"], - {CONF_DISABLE_RTSP: True, CONF_ALL_UPDATES: True, CONF_OVERRIDE_CHOST: True}, - ) + result2 = await hass.config_entries.options.async_configure( + result["flow_id"], + { + CONF_DISABLE_RTSP: True, + CONF_ALL_UPDATES: True, + CONF_OVERRIDE_CHOST: True, + }, + ) - assert result2["type"] == FlowResultType.CREATE_ENTRY - assert result2["data"] == { - "all_updates": True, - "disable_rtsp": True, - "override_connection_host": True, - "max_media": 1000, - } + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["data"] == { + "all_updates": True, + "disable_rtsp": True, + "override_connection_host": True, + "max_media": 1000, + } + await hass.config_entries.async_unload(mock_config.entry_id) @pytest.mark.parametrize( From 54df052699b02ba32e80610cc6a10a3f20506c70 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Nov 2022 15:49:05 -0500 Subject: [PATCH 0153/1033] Bump bleak-retry-connector to 2.8.2 (#81370) * Bump bleak-retry-connector to 2.8.2 Tweaks for the esp32 proxies now that we have better error reporting. This change improves the retry cases a bit with the new https://github.com/esphome/esphome/pull/3971 * empty --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index bca2f7f9a8d..9c2438b8b18 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.1", - "bleak-retry-connector==2.8.1", + "bleak-retry-connector==2.8.2", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", "dbus-fast==1.60.0" diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 3a2f4b5a7f8..db85880ffbd 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.8.1 +bleak-retry-connector==2.8.2 bleak==0.19.1 bluetooth-adapters==0.6.0 bluetooth-auto-recovery==0.3.6 diff --git a/requirements_all.txt b/requirements_all.txt index 5cd546685f0..494967f62e1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -416,7 +416,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.1 +bleak-retry-connector==2.8.2 # homeassistant.components.bluetooth bleak==0.19.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 593e262b8bd..3afe6ee54b3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -340,7 +340,7 @@ bellows==0.34.2 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.1 +bleak-retry-connector==2.8.2 # homeassistant.components.bluetooth bleak==0.19.1 From 329466d131e57a2572f791b302601bfb62aa75c0 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Tue, 1 Nov 2022 22:58:07 +0100 Subject: [PATCH 0154/1033] Enable strict typing for NextDNS (#81378) --- .strict-typing | 1 + mypy.ini | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/.strict-typing b/.strict-typing index dce6fcc6745..330424faec7 100644 --- a/.strict-typing +++ b/.strict-typing @@ -191,6 +191,7 @@ homeassistant.components.neato.* homeassistant.components.nest.* homeassistant.components.netatmo.* homeassistant.components.network.* +homeassistant.components.nextdns.* homeassistant.components.nfandroidtv.* homeassistant.components.nissan_leaf.* homeassistant.components.no_ip.* diff --git a/mypy.ini b/mypy.ini index c546377ae4f..3f0e917f167 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1662,6 +1662,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.nextdns.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.nfandroidtv.*] check_untyped_defs = true disallow_incomplete_defs = true From 054a271bd2e84001ffac7e4e1e9b5ae7bc6e2f7b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Nov 2022 17:00:04 -0500 Subject: [PATCH 0155/1033] Bump aiohomekit to 2.2.12 (#81372) * Bump aiohomekit to 2.2.12 Fixes a missing lock which was noticable on the esp32s since they disconnect right away when you ask for gatt notify. https://github.com/Jc2k/aiohomekit/compare/2.2.11...2.2.12 * empty --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 6533d7f29be..09f2a15871f 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.11"], + "requirements": ["aiohomekit==2.2.12"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index 494967f62e1..50c50794384 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.11 +aiohomekit==2.2.12 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3afe6ee54b3..e0230fa1d27 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.11 +aiohomekit==2.2.12 # homeassistant.components.emulated_hue # homeassistant.components.http From 697a81c4a361a5407bb51f7828b07315492f247e Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 2 Nov 2022 00:30:01 +0000 Subject: [PATCH 0156/1033] [ci skip] Translation update --- .../components/aranet/translations/ca.json | 5 +++- .../components/aranet/translations/it.json | 25 +++++++++++++++++++ .../components/aranet/translations/pl.json | 25 +++++++++++++++++++ .../aranet/translations/zh-Hant.json | 25 +++++++++++++++++++ .../fireservicerota/translations/bg.json | 5 ++++ .../fireservicerota/translations/de.json | 6 +++++ .../fireservicerota/translations/et.json | 6 +++++ .../fireservicerota/translations/it.json | 6 +++++ .../fireservicerota/translations/no.json | 6 +++++ .../fireservicerota/translations/pl.json | 6 +++++ .../fireservicerota/translations/ru.json | 6 +++++ .../fireservicerota/translations/zh-Hant.json | 6 +++++ .../google_travel_time/translations/it.json | 1 + .../google_travel_time/translations/pl.json | 1 + .../translations/zh-Hant.json | 1 + .../components/hassio/translations/bg.json | 5 ++++ .../components/hassio/translations/de.json | 10 ++++++++ .../components/hassio/translations/it.json | 10 ++++++++ .../components/hassio/translations/no.json | 10 ++++++++ .../components/hassio/translations/pl.json | 10 ++++++++ .../hassio/translations/zh-Hant.json | 10 ++++++++ .../here_travel_time/translations/pl.json | 2 +- .../components/motioneye/translations/bg.json | 5 +++- .../ovo_energy/translations/ca.json | 1 + .../ovo_energy/translations/it.json | 1 + .../ovo_energy/translations/pl.json | 1 + .../components/scrape/translations/ca.json | 5 ++++ .../components/scrape/translations/it.json | 6 +++++ .../components/scrape/translations/pl.json | 6 +++++ .../scrape/translations/zh-Hant.json | 6 +++++ .../components/subaru/translations/bg.json | 3 +++ .../transmission/translations/it.json | 13 ++++++++++ .../transmission/translations/pl.json | 13 ++++++++++ .../transmission/translations/zh-Hant.json | 13 ++++++++++ 34 files changed, 257 insertions(+), 3 deletions(-) create mode 100644 homeassistant/components/aranet/translations/it.json create mode 100644 homeassistant/components/aranet/translations/pl.json create mode 100644 homeassistant/components/aranet/translations/zh-Hant.json diff --git a/homeassistant/components/aranet/translations/ca.json b/homeassistant/components/aranet/translations/ca.json index 1bbc15cc1b7..ceaa1750762 100644 --- a/homeassistant/components/aranet/translations/ca.json +++ b/homeassistant/components/aranet/translations/ca.json @@ -1,7 +1,10 @@ { "config": { "abort": { - "already_configured": "El dispositiu ja est\u00e0 configurat" + "already_configured": "El dispositiu ja est\u00e0 configurat", + "integrations_diabled": "Aquest dispositiu no t\u00e9 les integracions activades. Activa les integracions de llar intel\u00b7ligent a trav\u00e9s de l'aplicaci\u00f3 i torna-ho a provar.", + "no_devices_found": "No s'han trobat dispositius Aranet no configurats.", + "outdated_version": "El dispositiu est\u00e0 utilitzant programari obsolet. Actualitza'l com a m\u00ednim a la versi\u00f3 1.2.0 i torna-ho a provar." }, "error": { "unknown": "Error inesperat" diff --git a/homeassistant/components/aranet/translations/it.json b/homeassistant/components/aranet/translations/it.json new file mode 100644 index 00000000000..372e6266b5b --- /dev/null +++ b/homeassistant/components/aranet/translations/it.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", + "integrations_diabled": "Questo dispositivo non ha integrazioni abilitate. Si prega di abilitare le integrazioni di smart home utilizzando l'app e riprovare.", + "no_devices_found": "Non sono stati trovati dispositivi Aranet non configurati.", + "outdated_version": "Questo dispositivo utilizza un firmware obsoleto. Aggiornalo almeno alla v1.2.0 e riprova." + }, + "error": { + "unknown": "Errore imprevisto" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Vuoi configurare {name}?" + }, + "user": { + "data": { + "address": "Dispositivo" + }, + "description": "Seleziona un dispositivo da configurare" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/pl.json b/homeassistant/components/aranet/translations/pl.json new file mode 100644 index 00000000000..3737a66471e --- /dev/null +++ b/homeassistant/components/aranet/translations/pl.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", + "integrations_diabled": "To urz\u0105dzenie nie ma w\u0142\u0105czonej integracji. W\u0142\u0105cz integracj\u0119 z inteligentnym domem za pomoc\u0105 aplikacji i spr\u00f3buj ponownie.", + "no_devices_found": "Nie znaleziono nieskonfigurowanych urz\u0105dze\u0144 Aranet.", + "outdated_version": "To urz\u0105dzenie korzysta z przestarza\u0142ego oprogramowania. Zaktualizuj go do wersji co najmniej 1.2.0 i spr\u00f3buj ponownie." + }, + "error": { + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Czy chcesz skonfigurowa\u0107 {name}?" + }, + "user": { + "data": { + "address": "Urz\u0105dzenie" + }, + "description": "Wybierz urz\u0105dzenie do skonfigurowania" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/zh-Hant.json b/homeassistant/components/aranet/translations/zh-Hant.json new file mode 100644 index 00000000000..586c871e043 --- /dev/null +++ b/homeassistant/components/aranet/translations/zh-Hant.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "integrations_diabled": "\u88dd\u7f6e\u4e26\u672a\u555f\u7528\u4efb\u4f55\u6574\u5408\uff0c\u8acb\u5148\u4f7f\u7528 App \u555f\u7528\u667a\u80fd\u5bb6\u5ead\u6574\u5408\u5f8c\u3001\u518d\u8a66\u4e00\u6b21\u3002", + "no_devices_found": "\u627e\u4e0d\u5230\u4efb\u4f55\u672a\u8a2d\u5b9a Aranet \u88dd\u7f6e\u3002", + "outdated_version": "\u88dd\u7f6e\u4f7f\u7528\u4e86\u904e\u820a\u7684\u97cc\u9ad4\uff0c\u8acb\u66f4\u65b0\u81f3 v1.2.0 \u7248\u4ee5\u4e0a\u4e26\u518d\u8a66\u4e00\u6b21\u3002" + }, + "error": { + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "\u662f\u5426\u8981\u8a2d\u5b9a {name}\uff1f" + }, + "user": { + "data": { + "address": "\u88dd\u7f6e" + }, + "description": "\u9078\u64c7\u6240\u8981\u8a2d\u5b9a\u7684\u88dd\u7f6e" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fireservicerota/translations/bg.json b/homeassistant/components/fireservicerota/translations/bg.json index 22cc783d4e9..422988d551b 100644 --- a/homeassistant/components/fireservicerota/translations/bg.json +++ b/homeassistant/components/fireservicerota/translations/bg.json @@ -16,6 +16,11 @@ "password": "\u041f\u0430\u0440\u043e\u043b\u0430" } }, + "reauth_confirm": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u0430" + } + }, "user": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u0430", diff --git a/homeassistant/components/fireservicerota/translations/de.json b/homeassistant/components/fireservicerota/translations/de.json index c8c18c4c372..5be147663ff 100644 --- a/homeassistant/components/fireservicerota/translations/de.json +++ b/homeassistant/components/fireservicerota/translations/de.json @@ -17,6 +17,12 @@ }, "description": "Authentifizierungs-Tokens sind ung\u00fcltig, melde dich an, um sie neu zu erstellen." }, + "reauth_confirm": { + "data": { + "password": "Passwort" + }, + "description": "Die Authentifizierungs-Tokens wurden ung\u00fcltig, melde dich an, um sie neu zu erstellen." + }, "user": { "data": { "password": "Passwort", diff --git a/homeassistant/components/fireservicerota/translations/et.json b/homeassistant/components/fireservicerota/translations/et.json index dedd74e8701..c949db5fd9c 100644 --- a/homeassistant/components/fireservicerota/translations/et.json +++ b/homeassistant/components/fireservicerota/translations/et.json @@ -17,6 +17,12 @@ }, "description": "Tuvastusstring aegus, taasloomiseks logi sisse." }, + "reauth_confirm": { + "data": { + "password": "Salas\u00f5na" + }, + "description": "Autentimise m\u00e4rgised muutusid kehtetuks, logi sisse, et neid uuesti luua." + }, "user": { "data": { "password": "Salas\u00f5na", diff --git a/homeassistant/components/fireservicerota/translations/it.json b/homeassistant/components/fireservicerota/translations/it.json index 8a437e45900..1dbd077521e 100644 --- a/homeassistant/components/fireservicerota/translations/it.json +++ b/homeassistant/components/fireservicerota/translations/it.json @@ -17,6 +17,12 @@ }, "description": "I token di autenticazione non sono validi, esegui l'accesso per ricrearli." }, + "reauth_confirm": { + "data": { + "password": "Password" + }, + "description": "I token di autenticazione non sono pi\u00f9 validi, accedi per ricrearli." + }, "user": { "data": { "password": "Password", diff --git a/homeassistant/components/fireservicerota/translations/no.json b/homeassistant/components/fireservicerota/translations/no.json index 03ecc365e74..9b228f15ccd 100644 --- a/homeassistant/components/fireservicerota/translations/no.json +++ b/homeassistant/components/fireservicerota/translations/no.json @@ -17,6 +17,12 @@ }, "description": "Autentiseringstokener ble ugyldige, logg inn for \u00e5 gjenskape dem." }, + "reauth_confirm": { + "data": { + "password": "Passord" + }, + "description": "Autentiseringstokener ble ugyldige, logg p\u00e5 for \u00e5 gjenskape dem." + }, "user": { "data": { "password": "Passord", diff --git a/homeassistant/components/fireservicerota/translations/pl.json b/homeassistant/components/fireservicerota/translations/pl.json index 2e5e480fcc1..b0c45bea46b 100644 --- a/homeassistant/components/fireservicerota/translations/pl.json +++ b/homeassistant/components/fireservicerota/translations/pl.json @@ -17,6 +17,12 @@ }, "description": "Tokeny uwierzytelniaj\u0105ce straci\u0142y wa\u017cno\u015b\u0107. Zaloguj si\u0119, aby je odtworzy\u0107." }, + "reauth_confirm": { + "data": { + "password": "Has\u0142o" + }, + "description": "Tokeny uwierzytelniaj\u0105ce straci\u0142y wa\u017cno\u015b\u0107. Zaloguj si\u0119, aby je odtworzy\u0107." + }, "user": { "data": { "password": "Has\u0142o", diff --git a/homeassistant/components/fireservicerota/translations/ru.json b/homeassistant/components/fireservicerota/translations/ru.json index 046a65081ec..b6ae8c4280e 100644 --- a/homeassistant/components/fireservicerota/translations/ru.json +++ b/homeassistant/components/fireservicerota/translations/ru.json @@ -17,6 +17,12 @@ }, "description": "\u0422\u043e\u043a\u0435\u043d\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b, \u0432\u043e\u0439\u0434\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0438\u0445 \u0437\u0430\u043d\u043e\u0432\u043e." }, + "reauth_confirm": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u044c" + }, + "description": "\u0422\u043e\u043a\u0435\u043d\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b, \u0432\u043e\u0439\u0434\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0438\u0445 \u0437\u0430\u043d\u043e\u0432\u043e." + }, "user": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c", diff --git a/homeassistant/components/fireservicerota/translations/zh-Hant.json b/homeassistant/components/fireservicerota/translations/zh-Hant.json index 8e5f4d9f20d..af84ae5eab3 100644 --- a/homeassistant/components/fireservicerota/translations/zh-Hant.json +++ b/homeassistant/components/fireservicerota/translations/zh-Hant.json @@ -17,6 +17,12 @@ }, "description": "\u8a8d\u8b49\u6b0a\u6756\u5df2\u7d93\u5931\u6548\uff0c\u8acb\u767b\u5165\u91cd\u65b0\u65b0\u589e\u3002" }, + "reauth_confirm": { + "data": { + "password": "\u5bc6\u78bc" + }, + "description": "\u8a8d\u8b49\u6b0a\u6756\u5df2\u7d93\u5931\u6548\uff0c\u8acb\u767b\u5165\u91cd\u65b0\u65b0\u589e\u3002" + }, "user": { "data": { "password": "\u5bc6\u78bc", diff --git a/homeassistant/components/google_travel_time/translations/it.json b/homeassistant/components/google_travel_time/translations/it.json index 16dc9f85f66..8648bf33d6d 100644 --- a/homeassistant/components/google_travel_time/translations/it.json +++ b/homeassistant/components/google_travel_time/translations/it.json @@ -28,6 +28,7 @@ "mode": "Modalit\u00e0 di viaggio", "time": "Ora", "time_type": "Tipo di ora", + "traffic_mode": "Modalit\u00e0 traffico", "transit_mode": "Modalit\u00e0 di transito", "transit_routing_preference": "Preferenza percorso di transito", "units": "Unit\u00e0" diff --git a/homeassistant/components/google_travel_time/translations/pl.json b/homeassistant/components/google_travel_time/translations/pl.json index 0890a4fd350..1ced0b912e6 100644 --- a/homeassistant/components/google_travel_time/translations/pl.json +++ b/homeassistant/components/google_travel_time/translations/pl.json @@ -28,6 +28,7 @@ "mode": "Tryb podr\u00f3\u017cy", "time": "Czas", "time_type": "Typ czasu", + "traffic_mode": "Tryb nat\u0119\u017cenia ruchu", "transit_mode": "Tryb tranzytu", "transit_routing_preference": "Preferencje trasy tranzytowej", "units": "Jednostki" diff --git a/homeassistant/components/google_travel_time/translations/zh-Hant.json b/homeassistant/components/google_travel_time/translations/zh-Hant.json index 29bf50f3376..a964f0a09f1 100644 --- a/homeassistant/components/google_travel_time/translations/zh-Hant.json +++ b/homeassistant/components/google_travel_time/translations/zh-Hant.json @@ -28,6 +28,7 @@ "mode": "\u65c5\u884c\u6a21\u5f0f", "time": "\u6642\u9593", "time_type": "\u6642\u9593\u985e\u5225", + "traffic_mode": "\u4ea4\u901a\u6a21\u5f0f", "transit_mode": "\u79fb\u52d5\u6a21\u5f0f", "transit_routing_preference": "\u504f\u597d\u79fb\u52d5\u8def\u7dda", "units": "\u55ae\u4f4d" diff --git a/homeassistant/components/hassio/translations/bg.json b/homeassistant/components/hassio/translations/bg.json index 68fcf5f343e..451dd51d125 100644 --- a/homeassistant/components/hassio/translations/bg.json +++ b/homeassistant/components/hassio/translations/bg.json @@ -1,4 +1,9 @@ { + "issues": { + "unsupported": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - {reason}" + } + }, "system_health": { "info": { "agent_version": "\u0412\u0435\u0440\u0441\u0438\u044f \u043d\u0430 \u0430\u0433\u0435\u043d\u0442\u0430", diff --git a/homeassistant/components/hassio/translations/de.json b/homeassistant/components/hassio/translations/de.json index f25ae73b423..54940b93448 100644 --- a/homeassistant/components/hassio/translations/de.json +++ b/homeassistant/components/hassio/translations/de.json @@ -1,4 +1,14 @@ { + "issues": { + "unhealthy": { + "description": "Das System ist derzeit aufgrund von \u201e{reason}\u201c fehlerhaft. Verwende den Link, um mehr dar\u00fcber zu erfahren, was falsch ist und wie du es beheben kannst.", + "title": "Fehlerhaftes System - {reason}" + }, + "unsupported": { + "description": "Das System wird aufgrund von \u201e{reason}\u201c nicht unterst\u00fctzt. Verwende den Link, um mehr dar\u00fcber zu erfahren, was dies bedeutet und wie du zu einem unterst\u00fctzten System zur\u00fcckkehren kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 {reason}" + } + }, "system_health": { "info": { "agent_version": "Agent-Version", diff --git a/homeassistant/components/hassio/translations/it.json b/homeassistant/components/hassio/translations/it.json index 3dc55d0f525..c305b9fce34 100644 --- a/homeassistant/components/hassio/translations/it.json +++ b/homeassistant/components/hassio/translations/it.json @@ -1,4 +1,14 @@ { + "issues": { + "unhealthy": { + "description": "Il sistema non \u00e8 attualmente integro a causa di '{reason}'. Usa il collegamento per saperne di pi\u00f9 sul problema e su come risolverlo.", + "title": "Sistema non pi\u00f9 integro - {reason}" + }, + "unsupported": { + "description": "Il sistema non \u00e8 supportato a causa di '{reason}'. Utilizzare il collegamento per ulteriori informazioni sul significato e su come tornare a un sistema supportato.", + "title": "Sistema non supportato - {reason}" + } + }, "system_health": { "info": { "agent_version": "Versione agente", diff --git a/homeassistant/components/hassio/translations/no.json b/homeassistant/components/hassio/translations/no.json index 1fa10a98921..8dd5d471860 100644 --- a/homeassistant/components/hassio/translations/no.json +++ b/homeassistant/components/hassio/translations/no.json @@ -1,4 +1,14 @@ { + "issues": { + "unhealthy": { + "description": "Systemet er for \u00f8yeblikket usunt p\u00e5 grunn av '{reason}'. Bruk linken for \u00e5 l\u00e6re mer om hva som er galt og hvordan du kan fikse det.", + "title": "Usunt system \u2013 {reason}" + }, + "unsupported": { + "description": "Systemet st\u00f8ttes ikke p\u00e5 grunn av '{reason}'. Bruk lenken for \u00e5 l\u00e6re mer om hva dette betyr og hvordan du g\u00e5r tilbake til et st\u00f8ttet system.", + "title": "Systemet st\u00f8ttes ikke \u2013 {reason}" + } + }, "system_health": { "info": { "agent_version": "Agentversjon", diff --git a/homeassistant/components/hassio/translations/pl.json b/homeassistant/components/hassio/translations/pl.json index 8850b7066fd..0fdbfa2ab68 100644 --- a/homeassistant/components/hassio/translations/pl.json +++ b/homeassistant/components/hassio/translations/pl.json @@ -1,4 +1,14 @@ { + "issues": { + "unhealthy": { + "description": "System jest obecnie \"niezdrowy\" z powodu \u201e{reason}\u201d. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej o tym, co jest nie tak i jak to naprawi\u0107.", + "title": "Niezdrowy system \u2013 {reason}" + }, + "unsupported": { + "description": "System nie jest obs\u0142ugiwany z powodu \u201e{pow\u00f3d}\u201d. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej o tym, co to oznacza i jak wr\u00f3ci\u0107 do obs\u0142ugiwanego systemu.", + "title": "Nieobs\u0142ugiwany system \u2013 {reason}" + } + }, "system_health": { "info": { "agent_version": "Wersja agenta", diff --git a/homeassistant/components/hassio/translations/zh-Hant.json b/homeassistant/components/hassio/translations/zh-Hant.json index 5a503e54937..8194ea37e6d 100644 --- a/homeassistant/components/hassio/translations/zh-Hant.json +++ b/homeassistant/components/hassio/translations/zh-Hant.json @@ -1,4 +1,14 @@ { + "issues": { + "unhealthy": { + "description": "System is currently unhealthy \u7531\u65bc '{reason}' \u7de3\u6545\u3001\u7cfb\u7d71\u76ee\u524d\u88ab\u8a8d\u70ba\u4e0d\u5065\u5eb7\u3002\u8acb\u53c3\u8003\u9023\u7d50\u4ee5\u4e86\u89e3\u54ea\u88e1\u51fa\u4e86\u554f\u984c\u3001\u53ca\u5982\u4f55\u9032\u884c\u4fee\u6b63\u3002", + "title": "\u4e0d\u5065\u5eb7\u7cfb\u7d71 - {reason}" + }, + "unsupported": { + "description": "System is unsupported \u7531\u65bc '{reason}' \u7de3\u6545\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u8003\u9023\u7d50\u4ee5\u4e86\u89e3\u76f8\u95dc\u8aaa\u660e\u3001\u53ca\u5982\u4f55\u56de\u5fa9\u81f3\u652f\u63f4\u7cfb\u7d71\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - {reason}" + } + }, "system_health": { "info": { "agent_version": "Agent \u7248\u672c", diff --git a/homeassistant/components/here_travel_time/translations/pl.json b/homeassistant/components/here_travel_time/translations/pl.json index 17b91417007..e409971f3b3 100644 --- a/homeassistant/components/here_travel_time/translations/pl.json +++ b/homeassistant/components/here_travel_time/translations/pl.json @@ -72,7 +72,7 @@ "init": { "data": { "route_mode": "Tryb trasy", - "traffic_mode": "Tryb ruchu", + "traffic_mode": "Tryb nat\u0119\u017cenia ruchu", "unit_system": "System metryczny" } }, diff --git a/homeassistant/components/motioneye/translations/bg.json b/homeassistant/components/motioneye/translations/bg.json index f5716dcf951..e3acc778fd3 100644 --- a/homeassistant/components/motioneye/translations/bg.json +++ b/homeassistant/components/motioneye/translations/bg.json @@ -13,7 +13,10 @@ "step": { "user": { "data": { - "admin_password": "\u0410\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0441\u043a\u0430 \u043f\u0430\u0440\u043e\u043b\u0430", + "admin_password": "\u041f\u0430\u0440\u043e\u043b\u0430 \u043d\u0430 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430", + "admin_username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435 \u043d\u0430 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430", + "surveillance_password": "\u041f\u0430\u0440\u043e\u043b\u0430 \u0437\u0430 \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435", + "surveillance_username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435 \u0437\u0430 \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435", "url": "URL" } } diff --git a/homeassistant/components/ovo_energy/translations/ca.json b/homeassistant/components/ovo_energy/translations/ca.json index 0d0677ec522..87278926984 100644 --- a/homeassistant/components/ovo_energy/translations/ca.json +++ b/homeassistant/components/ovo_energy/translations/ca.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "Identificador de compte OVO (afegeix-lo nom\u00e9s si tens diversos comptes)", "password": "Contrasenya", "username": "Nom d'usuari" }, diff --git a/homeassistant/components/ovo_energy/translations/it.json b/homeassistant/components/ovo_energy/translations/it.json index be21bde5665..c5069e5da86 100644 --- a/homeassistant/components/ovo_energy/translations/it.json +++ b/homeassistant/components/ovo_energy/translations/it.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "ID account OVO (aggiungi solo se hai pi\u00f9 account)", "password": "Password", "username": "Nome utente" }, diff --git a/homeassistant/components/ovo_energy/translations/pl.json b/homeassistant/components/ovo_energy/translations/pl.json index c426d140417..a14ae9d2b6b 100644 --- a/homeassistant/components/ovo_energy/translations/pl.json +++ b/homeassistant/components/ovo_energy/translations/pl.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "Identyfikator konta OVO (dodaj tylko, je\u015bli masz wiele kont)", "password": "Has\u0142o", "username": "Nazwa u\u017cytkownika" }, diff --git a/homeassistant/components/scrape/translations/ca.json b/homeassistant/components/scrape/translations/ca.json index ff6a0dba168..2503602ab33 100644 --- a/homeassistant/components/scrape/translations/ca.json +++ b/homeassistant/components/scrape/translations/ca.json @@ -36,6 +36,11 @@ } } }, + "issues": { + "moved_yaml": { + "title": "La configuraci\u00f3 YAML de Scrape s'ha eliminat" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/scrape/translations/it.json b/homeassistant/components/scrape/translations/it.json index e64ee3022d8..32d2fdcbd31 100644 --- a/homeassistant/components/scrape/translations/it.json +++ b/homeassistant/components/scrape/translations/it.json @@ -36,6 +36,12 @@ } } }, + "issues": { + "moved_yaml": { + "description": "La configurazione di Scrape tramite YAML \u00e8 stata spostata nella chiave di integrazione. \n\nLa tua configurazione YAML esistente funzioner\u00e0 per altre 2 versioni. \n\nMigra la tua configurazione YAML alla chiave di integrazione in base alla documentazione.", + "title": "La configurazione YAML di Scrape \u00e8 stata spostata" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/scrape/translations/pl.json b/homeassistant/components/scrape/translations/pl.json index 67b2a3db685..aa551e8f08c 100644 --- a/homeassistant/components/scrape/translations/pl.json +++ b/homeassistant/components/scrape/translations/pl.json @@ -36,6 +36,12 @@ } } }, + "issues": { + "moved_yaml": { + "description": "Konfiguracja Scrape za pomoc\u0105 YAML zosta\u0142a przeniesiona do klucza integracji. \n\nTwoja istniej\u0105ca konfiguracja YAML b\u0119dzie dzia\u0142a\u0107 przez kolejne 2 wersje. \n\nPrzenie\u015b swoj\u0105 konfiguracj\u0119 YAML do klucza integracji zgodnie z dokumentacj\u0105.", + "title": "Konfiguracja YAML dla Scrape zostaje przeniesiona" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/scrape/translations/zh-Hant.json b/homeassistant/components/scrape/translations/zh-Hant.json index 499ca44d334..d188fc7894e 100644 --- a/homeassistant/components/scrape/translations/zh-Hant.json +++ b/homeassistant/components/scrape/translations/zh-Hant.json @@ -36,6 +36,12 @@ } } }, + "issues": { + "moved_yaml": { + "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a\u7684 Scrape \u5373\u5c07\u8f49\u79fb\u81f3\u6574\u5408\u3002\n\n\u73fe\u6709\u7684 YAML \u8a2d\u5b9a\u53ea\u80fd\u518d\u4f7f\u7528\u5169\u500b\u66f4\u65b0\u7248\u672c\u3002\n\n\u8ddf\u96a8\u6587\u4ef6\u8aaa\u660e\u9077\u79fb YAML \u8a2d\u5b9a\u81f3\u6574\u5408\u3002", + "title": "Scrape YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/subaru/translations/bg.json b/homeassistant/components/subaru/translations/bg.json index c43cb84d5f6..86e02b8cc07 100644 --- a/homeassistant/components/subaru/translations/bg.json +++ b/homeassistant/components/subaru/translations/bg.json @@ -10,6 +10,9 @@ "incorrect_validation_code": "\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u0435\u043d \u043a\u043e\u0434 \u0437\u0430 \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0430\u043d\u0435" }, "step": { + "two_factor": { + "description": "\u0418\u0437\u0438\u0441\u043a\u0432\u0430 \u0441\u0435 \u0434\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + }, "user": { "data": { "country": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0434\u044a\u0440\u0436\u0430\u0432\u0430", diff --git a/homeassistant/components/transmission/translations/it.json b/homeassistant/components/transmission/translations/it.json index 2cefcdfb290..41eaa4efdf5 100644 --- a/homeassistant/components/transmission/translations/it.json +++ b/homeassistant/components/transmission/translations/it.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "Aggiorna eventuali automazioni o script che utilizzano questo servizio e sostituisci la chiave del nome con la chiave entry_id.", + "title": "La chiave del nome nei servizi di trasmissione \u00e8 stata rimossa" + } + } + }, + "title": "La chiave del nome nei servizi di trasmissione \u00e8 stata rimossa" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/transmission/translations/pl.json b/homeassistant/components/transmission/translations/pl.json index 994744a3547..7ae84ea4f4e 100644 --- a/homeassistant/components/transmission/translations/pl.json +++ b/homeassistant/components/transmission/translations/pl.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "Zaktualizuj wszystkie automatyzacje lub skrypty korzystaj\u0105ce z tej us\u0142ugi i zast\u0105p klucz nazwy kluczem entry_id.", + "title": "Klucz nazwy w us\u0142ugach Transmission zostanie usuni\u0119ty" + } + } + }, + "title": "Klucz nazwy w us\u0142ugach Transmission zostanie usuni\u0119ty" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/transmission/translations/zh-Hant.json b/homeassistant/components/transmission/translations/zh-Hant.json index fd3d3a909aa..235a13f2c01 100644 --- a/homeassistant/components/transmission/translations/zh-Hant.json +++ b/homeassistant/components/transmission/translations/zh-Hant.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "\u4f7f\u7528\u6b64\u670d\u52d9\u4ee5\u66f4\u65b0\u4efb\u4f55\u81ea\u52d5\u5316\u6216\u8173\u672c\u3001\u4ee5\u53d6\u4ee3 name key \u70ba entry_id key\u3002", + "title": "Transmission \u4e2d\u7684 name key \u670d\u52d9\u5373\u5c07\u79fb\u9664" + } + } + }, + "title": "Transmission \u4e2d\u7684 name key \u670d\u52d9\u5373\u5c07\u79fb\u9664" + } + }, "options": { "step": { "init": { From 10aa1d386a22e926edeb53122be16896cf5179c3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Nov 2022 19:52:13 -0500 Subject: [PATCH 0157/1033] Bump dbus-fast to 1.61.1 (#81386) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 9c2438b8b18..89281323541 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.8.2", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", - "dbus-fast==1.60.0" + "dbus-fast==1.61.1" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index db85880ffbd..668f30a67d9 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.1 -dbus-fast==1.60.0 +dbus-fast==1.61.1 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 50c50794384..e8c59a1162e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -543,7 +543,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.60.0 +dbus-fast==1.61.1 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e0230fa1d27..f0b688f4a63 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -423,7 +423,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.60.0 +dbus-fast==1.61.1 # homeassistant.components.debugpy debugpy==1.6.3 From 0bfb0c25f6ff9a108aa9cf71d3001042f49a0380 Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Tue, 1 Nov 2022 21:29:11 -0400 Subject: [PATCH 0158/1033] Improve supervisor repairs (#81387) Co-authored-by: Paulus Schoutsen --- homeassistant/components/hassio/repairs.py | 59 +++++++++- homeassistant/components/hassio/strings.json | 104 +++++++++++++++++- .../components/hassio/translations/en.json | 104 +++++++++++++++++- tests/components/hassio/test_repairs.py | 77 ++++++++++++- 4 files changed, 330 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/hassio/repairs.py b/homeassistant/components/hassio/repairs.py index a8c6788f4d5..21120d8d522 100644 --- a/homeassistant/components/hassio/repairs.py +++ b/homeassistant/components/hassio/repairs.py @@ -36,6 +36,39 @@ ISSUE_ID_UNSUPPORTED = "unsupported_system" INFO_URL_UNHEALTHY = "https://www.home-assistant.io/more-info/unhealthy" INFO_URL_UNSUPPORTED = "https://www.home-assistant.io/more-info/unsupported" +UNSUPPORTED_REASONS = { + "apparmor", + "connectivity_check", + "content_trust", + "dbus", + "dns_server", + "docker_configuration", + "docker_version", + "cgroup_version", + "job_conditions", + "lxc", + "network_manager", + "os", + "os_agent", + "restart_policy", + "software", + "source_mods", + "supervisor_version", + "systemd", + "systemd_journal", + "systemd_resolved", +} +# Some unsupported reasons also mark the system as unhealthy. If the unsupported reason +# provides no additional information beyond the unhealthy one then skip that repair. +UNSUPPORTED_SKIP_REPAIR = {"privileged"} +UNHEALTHY_REASONS = { + "docker", + "supervisor", + "setup", + "privileged", + "untrusted", +} + class SupervisorRepairs: """Create repairs from supervisor events.""" @@ -56,6 +89,13 @@ class SupervisorRepairs: def unhealthy_reasons(self, reasons: set[str]) -> None: """Set unhealthy reasons. Create or delete repairs as necessary.""" for unhealthy in reasons - self.unhealthy_reasons: + if unhealthy in UNHEALTHY_REASONS: + translation_key = f"unhealthy_{unhealthy}" + translation_placeholders = None + else: + translation_key = "unhealthy" + translation_placeholders = {"reason": unhealthy} + async_create_issue( self._hass, DOMAIN, @@ -63,8 +103,8 @@ class SupervisorRepairs: is_fixable=False, learn_more_url=f"{INFO_URL_UNHEALTHY}/{unhealthy}", severity=IssueSeverity.CRITICAL, - translation_key="unhealthy", - translation_placeholders={"reason": unhealthy}, + translation_key=translation_key, + translation_placeholders=translation_placeholders, ) for fixed in self.unhealthy_reasons - reasons: @@ -80,7 +120,14 @@ class SupervisorRepairs: @unsupported_reasons.setter def unsupported_reasons(self, reasons: set[str]) -> None: """Set unsupported reasons. Create or delete repairs as necessary.""" - for unsupported in reasons - self.unsupported_reasons: + for unsupported in reasons - UNSUPPORTED_SKIP_REPAIR - self.unsupported_reasons: + if unsupported in UNSUPPORTED_REASONS: + translation_key = f"unsupported_{unsupported}" + translation_placeholders = None + else: + translation_key = "unsupported" + translation_placeholders = {"reason": unsupported} + async_create_issue( self._hass, DOMAIN, @@ -88,11 +135,11 @@ class SupervisorRepairs: is_fixable=False, learn_more_url=f"{INFO_URL_UNSUPPORTED}/{unsupported}", severity=IssueSeverity.WARNING, - translation_key="unsupported", - translation_placeholders={"reason": unsupported}, + translation_key=translation_key, + translation_placeholders=translation_placeholders, ) - for fixed in self.unsupported_reasons - reasons: + for fixed in self.unsupported_reasons - (reasons - UNSUPPORTED_SKIP_REPAIR): async_delete_issue(self._hass, DOMAIN, f"{ISSUE_ID_UNSUPPORTED}_{fixed}") self._unsupported_reasons = reasons diff --git a/homeassistant/components/hassio/strings.json b/homeassistant/components/hassio/strings.json index 81b5ce01b79..7cda053f43a 100644 --- a/homeassistant/components/hassio/strings.json +++ b/homeassistant/components/hassio/strings.json @@ -19,11 +19,111 @@ "issues": { "unhealthy": { "title": "Unhealthy system - {reason}", - "description": "System is currently unhealthy due to '{reason}'. Use the link to learn more about what is wrong and how to fix it." + "description": "System is currently unhealthy due to {reason}. Use the link to learn more and how to fix this." + }, + "unhealthy_docker": { + "title": "Unhealthy system - Docker misconfigured", + "description": "System is currently unhealthy because Docker is configured incorrectly. Use the link to learn more and how to fix this." + }, + "unhealthy_supervisor": { + "title": "Unhealthy system - Supervisor update failed", + "description": "System is currently unhealthy because an attempt to update Supervisor to the latest version has failed. Use the link to learn more and how to fix this." + }, + "unhealthy_setup": { + "title": "Unhealthy system - Setup failed", + "description": "System is currently unhealthy because setup failed to complete. There are a number of reasons this can occur, use the link to learn more and how to fix this." + }, + "unhealthy_privileged": { + "title": "Unhealthy system - Not privileged", + "description": "System is currently unhealthy because it does not have privileged access to the docker runtime. Use the link to learn more and how to fix this." + }, + "unhealthy_untrusted": { + "title": "Unhealthy system - Untrusted code", + "description": "System is currently unhealthy because it has detected untrusted code or images in use. Use the link to learn more and how to fix this." }, "unsupported": { "title": "Unsupported system - {reason}", - "description": "System is unsupported due to '{reason}'. Use the link to learn more about what this means and how to return to a supported system." + "description": "System is unsupported due to {reason}. Use the link to learn more and how to fix this." + }, + "unsupported_apparmor": { + "title": "Unsupported system - AppArmor issues", + "description": "System is unsupported because AppArmor is working incorrectly and add-ons are running in an unprotected and insecure way. Use the link to learn more and how to fix this." + }, + "unsupported_cgroup_version": { + "title": "Unsupported system - CGroup version", + "description": "System is unsupported because the wrong version of Docker CGroup is in use. Use the link to learn the correct version and how to fix this." + }, + "unsupported_connectivity_check": { + "title": "Unsupported system - Connectivity check disabled", + "description": "System is unsupported because Home Assistant cannot determine when an internet connection is available. Use the link to learn more and how to fix this." + }, + "unsupported_content_trust": { + "title": "Unsupported system - Content-trust check disabled", + "description": "System is unsupported because Home Assistant cannot verify content being run is trusted and not modified by attackers. Use the link to learn more and how to fix this." + }, + "unsupported_dbus": { + "title": "Unsupported system - D-Bus issues", + "description": "System is unsupported because D-Bus is working incorrectly. Many things fail without this as Supervisor cannot communicate with the host. Use the link to learn more and how to fix this." + }, + "unsupported_dns_server": { + "title": "Unsupported system - DNS server issues", + "description": "System is unsupported because the provided DNS server does not work correctly and the fallback DNS option has been disabled. Use the link to learn more and how to fix this." + }, + "unsupported_docker_configuration": { + "title": "Unsupported system - Docker misconfigured", + "description": "System is unsupported because the Docker daemon is running in an unexpected way. Use the link to learn more and how to fix this." + }, + "unsupported_docker_version": { + "title": "Unsupported system - Docker version", + "description": "System is unsupported because the wrong version of Docker is in use. Use the link to learn the correct version and how to fix this." + }, + "unsupported_job_conditions": { + "title": "Unsupported system - Protections disabled", + "description": "System is unsupported because one or more job conditions have been disabled which protect from unexpected failures and breakages. Use the link to learn more and how to fix this." + }, + "unsupported_lxc": { + "title": "Unsupported system - LXC detected", + "description": "System is unsupported because it is being run in an LXC virtual machine. Use the link to learn more and how to fix this." + }, + "unsupported_network_manager": { + "title": "Unsupported system - Network Manager issues", + "description": "System is unsupported because Network Manager is missing, inactive or misconfigured. Use the link to learn more and how to fix this." + }, + "unsupported_os": { + "title": "Unsupported system - Operating System", + "description": "System is unsupported because the operating system in use is not tested or maintained for use with Supervisor. Use the link to which operating systems are supported and how to fix this." + }, + "unsupported_os_agent": { + "title": "Unsupported system - OS-Agent issues", + "description": "System is unsupported because OS-Agent is missing, inactive or misconfigured. Use the link to learn more and how to fix this." + }, + "unsupported_restart_policy": { + "title": "Unsupported system - Container restart policy", + "description": "System is unsupported because a Docker container has a restart policy set which could cause issues on startup. Use the link to learn more and how to fix this." + }, + "unsupported_software": { + "title": "Unsupported system - Unsupported software", + "description": "System is unsupported because additional software outside the Home Assistant ecosystem has been detected. Use the link to learn more and how to fix this." + }, + "unsupported_source_mods": { + "title": "Unsupported system - Supervisor source modifications", + "description": "System is unsupported because Supervisor source code has been modified. Use the link to learn more and how to fix this." + }, + "unsupported_supervisor_version": { + "title": "Unsupported system - Supervisor version", + "description": "System is unsupported because an out-of-date version of Supervisor is in use and auto-update has been disabled. Use the link to learn more and how to fix this." + }, + "unsupported_systemd": { + "title": "Unsupported system - Systemd issues", + "description": "System is unsupported because Systemd is missing, inactive or misconfigured. Use the link to learn more and how to fix this." + }, + "unsupported_systemd_journal": { + "title": "Unsupported system - Systemd Journal issues", + "description": "System is unsupported because Systemd Journal and/or the gateway service is missing, inactive or misconfigured . Use the link to learn more and how to fix this." + }, + "unsupported_systemd_resolved": { + "title": "Unsupported system - Systemd-Resolved issues", + "description": "System is unsupported because Systemd Resolved is missing, inactive or misconfigured. Use the link to learn more and how to fix this." } } } diff --git a/homeassistant/components/hassio/translations/en.json b/homeassistant/components/hassio/translations/en.json index b6f006e3093..243467b9f22 100644 --- a/homeassistant/components/hassio/translations/en.json +++ b/homeassistant/components/hassio/translations/en.json @@ -1,12 +1,112 @@ { "issues": { "unhealthy": { - "description": "System is currently unhealthy due to '{reason}'. Use the link to learn more about what is wrong and how to fix it.", + "description": "System is currently unhealthy due to {reason}. Use the link to learn more and how to fix this.", "title": "Unhealthy system - {reason}" }, + "unhealthy_docker": { + "description": "System is currently unhealthy because Docker is configured incorrectly. Use the link to learn more and how to fix this.", + "title": "Unhealthy system - Docker misconfigured" + }, + "unhealthy_privileged": { + "description": "System is currently unhealthy because it does not have privileged access to the docker runtime. Use the link to learn more and how to fix this.", + "title": "Unhealthy system - Not privileged" + }, + "unhealthy_setup": { + "description": "System is currently because setup failed to complete. There are a number of reasons this can occur, use the link to learn more and how to fix this.", + "title": "Unhealthy system - Setup failed" + }, + "unhealthy_supervisor": { + "description": "System is currently unhealthy because an attempt to update Supervisor to the latest version has failed. Use the link to learn more and how to fix this.", + "title": "Unhealthy system - Supervisor update failed" + }, + "unhealthy_untrusted": { + "description": "System is currently unhealthy because it has detected untrusted code or images in use. Use the link to learn more and how to fix this.", + "title": "Unhealthy system - Untrusted code" + }, "unsupported": { - "description": "System is unsupported due to '{reason}'. Use the link to learn more about what this means and how to return to a supported system.", + "description": "System is unsupported due to {reason}. Use the link to learn more and how to fix this.", "title": "Unsupported system - {reason}" + }, + "unsupported_apparmor": { + "description": "System is unsupported because AppArmor is working incorrectly and add-ons are running in an unprotected and insecure way. Use the link to learn more and how to fix this.", + "title": "Unsupported system - AppArmor issues" + }, + "unsupported_cgroup_version": { + "description": "System is unsupported because the wrong version of Docker CGroup is in use. Use the link to learn the correct version and how to fix this.", + "title": "Unsupported system - CGroup version" + }, + "unsupported_connectivity_check": { + "description": "System is unsupported because Home Assistant cannot determine when an internet connection is available. Use the link to learn more and how to fix this.", + "title": "Unsupported system - Connectivity check disabled" + }, + "unsupported_content_trust": { + "description": "System is unsupported because Home Assistant cannot verify content being run is trusted and not modified by attackers. Use the link to learn more and how to fix this.", + "title": "Unsupported system - Content-trust check disabled" + }, + "unsupported_dbus": { + "description": "System is unsupported because D-Bus is working incorrectly. Many things fail without this as Supervisor cannot communicate with the host. Use the link to learn more and how to fix this.", + "title": "Unsupported system - D-Bus issues" + }, + "unsupported_dns_server": { + "description": "System is unsupported because the provided DNS server does not work correctly and the fallback DNS option has been disabled. Use the link to learn more and how to fix this.", + "title": "Unsupported system - DNS server issues" + }, + "unsupported_docker_configuration": { + "description": "System is unsupported because the Docker daemon is running in an unexpected way. Use the link to learn more and how to fix this.", + "title": "Unsupported system - Docker misconfigured" + }, + "unsupported_docker_version": { + "description": "System is unsupported because the wrong version of Docker is in use. Use the link to learn the correct version and how to fix this.", + "title": "Unsupported system - Docker version" + }, + "unsupported_job_conditions": { + "description": "System is unsupported because one or more job conditions have been disabled which protect from unexpected failures and breakages. Use the link to learn more and how to fix this.", + "title": "Unsupported system - Protections disabled" + }, + "unsupported_lxc": { + "description": "System is unsupported because it is being run in an LXC virtual machine. Use the link to learn more and how to fix this.", + "title": "Unsupported system - LXC detected" + }, + "unsupported_network_manager": { + "description": "System is unsupported because Network Manager is missing, inactive or misconfigured. Use the link to learn more and how to fix this.", + "title": "Unsupported system - Network Manager issues" + }, + "unsupported_os": { + "description": "System is unsupported because the operating system in use is not tested or maintained for use with Supervisor. Use the link to which operating systems are supported and how to fix this.", + "title": "Unsupported system - Operating System" + }, + "unsupported_os_agent": { + "description": "System is unsupported because OS-Agent is missing, inactive or misconfigured. Use the link to learn more and how to fix this.", + "title": "Unsupported system - OS-Agent issues" + }, + "unsupported_restart_policy": { + "description": "System is unsupported because a Docker container has a restart policy set which could cause issues on startup. Use the link to learn more and how to fix this.", + "title": "Unsupported system - Container restart policy" + }, + "unsupported_software": { + "description": "System is unsupported because additional software outside the Home Assistant ecosystem has been detected. Use the link to learn more and how to fix this.", + "title": "Unsupported system - Unsupported software" + }, + "unsupported_source_mods": { + "description": "System is unsupported because Supervisor source code has been modified. Use the link to learn more and how to fix this.", + "title": "Unsupported system - Supervisor source modifications" + }, + "unsupported_supervisor_version": { + "description": "System is unsupported because an out-of-date version of Supervisor is in use and auto-update has been disabled. Use the link to learn more and how to fix this.", + "title": "Unsupported system - Supervisor version" + }, + "unsupported_systemd": { + "description": "System is unsupported because Systemd is missing, inactive or misconfigured. Use the link to learn more and how to fix this.", + "title": "Unsupported system - Systemd issues" + }, + "unsupported_systemd_journal": { + "description": "System is unsupported because Systemd Journal and/or the gateway service is missing, inactive or misconfigured . Use the link to learn more and how to fix this.", + "title": "Unsupported system - Systemd Journal issues" + }, + "unsupported_systemd_resolved": { + "description": "System is unsupported because Systemd Resolved is missing, inactive or misconfigured. Use the link to learn more and how to fix this.", + "title": "Unsupported system - Systemd-Resolved issues" } }, "system_health": { diff --git a/tests/components/hassio/test_repairs.py b/tests/components/hassio/test_repairs.py index ebaf46be3b5..f420e926b09 100644 --- a/tests/components/hassio/test_repairs.py +++ b/tests/components/hassio/test_repairs.py @@ -140,10 +140,8 @@ def assert_repair_in_list(issues: list[dict[str, Any]], unhealthy: bool, reason: "issue_domain": None, "learn_more_url": f"https://www.home-assistant.io/more-info/{repair_type}/{reason}", "severity": "critical" if unhealthy else "warning", - "translation_key": repair_type, - "translation_placeholders": { - "reason": reason, - }, + "translation_key": f"{repair_type}_{reason}", + "translation_placeholders": None, } in issues @@ -393,3 +391,74 @@ async def test_reasons_added_and_removed( assert_repair_in_list( msg["result"]["issues"], unhealthy=False, reason="content_trust" ) + + +async def test_ignored_unsupported_skipped( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + hass_ws_client, +): + """Unsupported reasons which have an identical unhealthy reason are ignored.""" + mock_resolution_info( + aioclient_mock, unsupported=["privileged"], unhealthy=["privileged"] + ) + + result = await async_setup_component(hass, "hassio", {}) + assert result + + client = await hass_ws_client(hass) + + await client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await client.receive_json() + assert msg["success"] + assert len(msg["result"]["issues"]) == 1 + assert_repair_in_list(msg["result"]["issues"], unhealthy=True, reason="privileged") + + +async def test_new_unsupported_unhealthy_reason( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + hass_ws_client, +): + """New unsupported/unhealthy reasons result in a generic repair until next core update.""" + mock_resolution_info( + aioclient_mock, unsupported=["fake_unsupported"], unhealthy=["fake_unhealthy"] + ) + + result = await async_setup_component(hass, "hassio", {}) + assert result + + client = await hass_ws_client(hass) + + await client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await client.receive_json() + assert msg["success"] + assert len(msg["result"]["issues"]) == 2 + assert { + "breaks_in_ha_version": None, + "created": ANY, + "dismissed_version": None, + "domain": "hassio", + "ignored": False, + "is_fixable": False, + "issue_id": "unhealthy_system_fake_unhealthy", + "issue_domain": None, + "learn_more_url": "https://www.home-assistant.io/more-info/unhealthy/fake_unhealthy", + "severity": "critical", + "translation_key": "unhealthy", + "translation_placeholders": {"reason": "fake_unhealthy"}, + } in msg["result"]["issues"] + assert { + "breaks_in_ha_version": None, + "created": ANY, + "dismissed_version": None, + "domain": "hassio", + "ignored": False, + "is_fixable": False, + "issue_id": "unsupported_system_fake_unsupported", + "issue_domain": None, + "learn_more_url": "https://www.home-assistant.io/more-info/unsupported/fake_unsupported", + "severity": "warning", + "translation_key": "unsupported", + "translation_placeholders": {"reason": "fake_unsupported"}, + } in msg["result"]["issues"] From f445b96a4ea0232805062e703153752379131240 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 2 Nov 2022 05:08:16 -0500 Subject: [PATCH 0159/1033] Bump aiohomekit to 2.2.13 (#81398) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 09f2a15871f..18884d59307 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.12"], + "requirements": ["aiohomekit==2.2.13"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index e8c59a1162e..b83434bc3c2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.12 +aiohomekit==2.2.13 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f0b688f4a63..2565b535db2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.12 +aiohomekit==2.2.13 # homeassistant.components.emulated_hue # homeassistant.components.http From 44f63252e7266839072840db4c16fd73c0106bd9 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 2 Nov 2022 11:52:19 +0100 Subject: [PATCH 0160/1033] Update frontend to 20221102.0 (#81405) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index aed26eb5de1..be97ceee522 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20221031.0"], + "requirements": ["home-assistant-frontend==20221102.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 668f30a67d9..5b5ee9c0e7b 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -21,7 +21,7 @@ dbus-fast==1.61.1 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 -home-assistant-frontend==20221031.0 +home-assistant-frontend==20221102.0 httpx==0.23.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index b83434bc3c2..581f3cd12fd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -871,7 +871,7 @@ hole==0.7.0 holidays==0.16 # homeassistant.components.frontend -home-assistant-frontend==20221031.0 +home-assistant-frontend==20221102.0 # homeassistant.components.home_connect homeconnect==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2565b535db2..c7aad37a8ad 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -651,7 +651,7 @@ hole==0.7.0 holidays==0.16 # homeassistant.components.frontend -home-assistant-frontend==20221031.0 +home-assistant-frontend==20221102.0 # homeassistant.components.home_connect homeconnect==0.7.2 From a8c527f6f3994f36edc71b59e5cee1136cef7f23 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 2 Nov 2022 07:18:50 -0400 Subject: [PATCH 0161/1033] Add unit conversion for energy costs (#81379) Co-authored-by: Franck Nijhof --- homeassistant/components/energy/sensor.py | 107 +++++++++++++--------- tests/components/energy/test_sensor.py | 24 ++++- 2 files changed, 82 insertions(+), 49 deletions(-) diff --git a/homeassistant/components/energy/sensor.py b/homeassistant/components/energy/sensor.py index c97b67287d1..71e385f2fec 100644 --- a/homeassistant/components/energy/sensor.py +++ b/homeassistant/components/energy/sensor.py @@ -2,6 +2,7 @@ from __future__ import annotations import asyncio +from collections.abc import Callable import copy from dataclasses import dataclass import logging @@ -22,6 +23,7 @@ from homeassistant.const import ( VOLUME_GALLONS, VOLUME_LITERS, UnitOfEnergy, + UnitOfVolume, ) from homeassistant.core import ( HomeAssistant, @@ -34,29 +36,35 @@ from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_track_state_change_event from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.util import unit_conversion import homeassistant.util.dt as dt_util +from homeassistant.util.unit_system import METRIC_SYSTEM from .const import DOMAIN from .data import EnergyManager, async_get_manager -SUPPORTED_STATE_CLASSES = [ +SUPPORTED_STATE_CLASSES = { SensorStateClass.MEASUREMENT, SensorStateClass.TOTAL, SensorStateClass.TOTAL_INCREASING, -] -VALID_ENERGY_UNITS = [ +} +VALID_ENERGY_UNITS: set[str] = { UnitOfEnergy.WATT_HOUR, UnitOfEnergy.KILO_WATT_HOUR, UnitOfEnergy.MEGA_WATT_HOUR, UnitOfEnergy.GIGA_JOULE, -] -VALID_ENERGY_UNITS_GAS = [VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS] + VALID_ENERGY_UNITS -VALID_VOLUME_UNITS_WATER = [ +} +VALID_ENERGY_UNITS_GAS = { + VOLUME_CUBIC_FEET, + VOLUME_CUBIC_METERS, + *VALID_ENERGY_UNITS, +} +VALID_VOLUME_UNITS_WATER = { VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS, VOLUME_GALLONS, VOLUME_LITERS, -] +} _LOGGER = logging.getLogger(__name__) @@ -252,8 +260,24 @@ class EnergyCostSensor(SensorEntity): self.async_write_ha_state() @callback - def _update_cost(self) -> None: # noqa: C901 + def _update_cost(self) -> None: """Update incurred costs.""" + if self._adapter.source_type == "grid": + valid_units = VALID_ENERGY_UNITS + default_price_unit: str | None = UnitOfEnergy.KILO_WATT_HOUR + + elif self._adapter.source_type == "gas": + valid_units = VALID_ENERGY_UNITS_GAS + # No conversion for gas. + default_price_unit = None + + elif self._adapter.source_type == "water": + valid_units = VALID_VOLUME_UNITS_WATER + if self.hass.config.units is METRIC_SYSTEM: + default_price_unit = UnitOfVolume.CUBIC_METERS + else: + default_price_unit = UnitOfVolume.GALLONS + energy_state = self.hass.states.get( cast(str, self._config[self._adapter.stat_energy_key]) ) @@ -298,52 +322,27 @@ class EnergyCostSensor(SensorEntity): except ValueError: return - if energy_price_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT, "").endswith( - f"/{UnitOfEnergy.WATT_HOUR}" - ): - energy_price *= 1000.0 + energy_price_unit: str | None = energy_price_state.attributes.get( + ATTR_UNIT_OF_MEASUREMENT, "" + ).partition("/")[2] - if energy_price_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT, "").endswith( - f"/{UnitOfEnergy.MEGA_WATT_HOUR}" - ): - energy_price /= 1000.0 - - if energy_price_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT, "").endswith( - f"/{UnitOfEnergy.GIGA_JOULE}" - ): - energy_price /= 1000 / 3.6 + # For backwards compatibility we don't validate the unit of the price + # If it is not valid, we assume it's our default price unit. + if energy_price_unit not in valid_units: + energy_price_unit = default_price_unit else: - energy_price_state = None energy_price = cast(float, self._config["number_energy_price"]) + energy_price_unit = default_price_unit if self._last_energy_sensor_state is None: # Initialize as it's the first time all required entities are in place. self._reset(energy_state) return - energy_unit = energy_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) + energy_unit: str | None = energy_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) - if self._adapter.source_type == "grid": - if energy_unit not in VALID_ENERGY_UNITS: - energy_unit = None - - elif self._adapter.source_type == "gas": - if energy_unit not in VALID_ENERGY_UNITS_GAS: - energy_unit = None - - elif self._adapter.source_type == "water": - if energy_unit not in VALID_VOLUME_UNITS_WATER: - energy_unit = None - - if energy_unit == UnitOfEnergy.WATT_HOUR: - energy_price /= 1000 - elif energy_unit == UnitOfEnergy.MEGA_WATT_HOUR: - energy_price *= 1000 - elif energy_unit == UnitOfEnergy.GIGA_JOULE: - energy_price *= 1000 / 3.6 - - if energy_unit is None: + if energy_unit is None or energy_unit not in valid_units: if not self._wrong_unit_reported: self._wrong_unit_reported = True _LOGGER.warning( @@ -373,10 +372,30 @@ class EnergyCostSensor(SensorEntity): energy_state_copy = copy.copy(energy_state) energy_state_copy.state = "0.0" self._reset(energy_state_copy) + # Update with newly incurred cost old_energy_value = float(self._last_energy_sensor_state.state) cur_value = cast(float, self._attr_native_value) - self._attr_native_value = cur_value + (energy - old_energy_value) * energy_price + + if energy_price_unit is None: + converted_energy_price = energy_price + else: + if self._adapter.source_type == "grid": + converter: Callable[ + [float, str, str], float + ] = unit_conversion.EnergyConverter.convert + elif self._adapter.source_type in ("gas", "water"): + converter = unit_conversion.VolumeConverter.convert + + converted_energy_price = converter( + energy_price, + energy_unit, + energy_price_unit, + ) + + self._attr_native_value = ( + cur_value + (energy - old_energy_value) * converted_energy_price + ) self._last_energy_sensor_state = energy_state diff --git a/tests/components/energy/test_sensor.py b/tests/components/energy/test_sensor.py index 14a04ea74c6..0108dd1de76 100644 --- a/tests/components/energy/test_sensor.py +++ b/tests/components/energy/test_sensor.py @@ -19,11 +19,13 @@ from homeassistant.const import ( STATE_UNKNOWN, VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS, + VOLUME_GALLONS, UnitOfEnergy, ) from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util +from homeassistant.util.unit_system import METRIC_SYSTEM, US_CUSTOMARY_SYSTEM from tests.components.recorder.common import async_wait_recording_done @@ -832,7 +834,10 @@ async def test_cost_sensor_handle_price_units( assert state.state == "20.0" -@pytest.mark.parametrize("unit", (VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS)) +@pytest.mark.parametrize( + "unit", + (VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS), +) async def test_cost_sensor_handle_gas( setup_integration, hass, hass_storage, unit ) -> None: @@ -933,13 +938,22 @@ async def test_cost_sensor_handle_gas_kwh( assert state.state == "50.0" -@pytest.mark.parametrize("unit", (VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS)) +@pytest.mark.parametrize( + "unit_system,usage_unit,growth", + ( + # 1 cubic foot = 7.47 gl, 100 ft3 growth @ 0.5/ft3: + (US_CUSTOMARY_SYSTEM, VOLUME_CUBIC_FEET, 374.025974025974), + (US_CUSTOMARY_SYSTEM, VOLUME_GALLONS, 50.0), + (METRIC_SYSTEM, VOLUME_CUBIC_METERS, 50.0), + ), +) async def test_cost_sensor_handle_water( - setup_integration, hass, hass_storage, unit + setup_integration, hass, hass_storage, unit_system, usage_unit, growth ) -> None: """Test water cost price from sensor entity.""" + hass.config.units = unit_system energy_attributes = { - ATTR_UNIT_OF_MEASUREMENT: unit, + ATTR_UNIT_OF_MEASUREMENT: usage_unit, ATTR_STATE_CLASS: SensorStateClass.TOTAL_INCREASING, } energy_data = data.EnergyManager.default_preferences() @@ -981,7 +995,7 @@ async def test_cost_sensor_handle_water( await hass.async_block_till_done() state = hass.states.get("sensor.water_consumption_cost") - assert state.state == "50.0" + assert float(state.state) == pytest.approx(growth) @pytest.mark.parametrize("state_class", [None]) From dd4b843d474ac585dc1cdd5bb12b0ccb242fafcf Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 2 Nov 2022 13:39:21 +0100 Subject: [PATCH 0162/1033] Use attr in mqtt number (#81399) --- homeassistant/components/mqtt/number.py | 50 +++++-------------------- 1 file changed, 9 insertions(+), 41 deletions(-) diff --git a/homeassistant/components/mqtt/number.py b/homeassistant/components/mqtt/number.py index 25ef7af8d6e..95dbe970430 100644 --- a/homeassistant/components/mqtt/number.py +++ b/homeassistant/components/mqtt/number.py @@ -12,7 +12,6 @@ from homeassistant.components.number import ( DEFAULT_MIN_VALUE, DEFAULT_STEP, DEVICE_CLASSES_SCHEMA, - NumberDeviceClass, NumberMode, RestoreNumber, ) @@ -167,8 +166,6 @@ class MqttNumber(MqttEntity, RestoreNumber): self._optimistic = False self._sub_state = None - self._current_number = None - RestoreNumber.__init__(self) MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @@ -190,6 +187,12 @@ class MqttNumber(MqttEntity, RestoreNumber): entity=self, ).async_render_with_possible_json_value, } + self._attr_device_class = config.get(CONF_DEVICE_CLASS) + self._attr_mode = config[CONF_MODE] + self._attr_native_max_value = config[CONF_MAX] + self._attr_native_min_value = config[CONF_MIN] + self._attr_native_step = config[CONF_STEP] + self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT) def _prepare_subscribe_topics(self): """(Re)Subscribe to topics.""" @@ -222,7 +225,7 @@ class MqttNumber(MqttEntity, RestoreNumber): ) return - self._current_number = num_value + self._attr_native_value = num_value get_mqtt_data(self.hass).state_write_requests.write_state_request(self) if self._config.get(CONF_STATE_TOPIC) is None: @@ -249,37 +252,7 @@ class MqttNumber(MqttEntity, RestoreNumber): if self._optimistic and ( last_number_data := await self.async_get_last_number_data() ): - self._current_number = last_number_data.native_value - - @property - def native_min_value(self) -> float: - """Return the minimum value.""" - return self._config[CONF_MIN] - - @property - def native_max_value(self) -> float: - """Return the maximum value.""" - return self._config[CONF_MAX] - - @property - def native_step(self) -> float: - """Return the increment/decrement step.""" - return self._config[CONF_STEP] - - @property - def native_unit_of_measurement(self) -> str | None: - """Return the unit of measurement.""" - return self._config.get(CONF_UNIT_OF_MEASUREMENT) - - @property - def native_value(self) -> float | None: - """Return the current value.""" - return self._current_number - - @property - def mode(self) -> NumberMode: - """Return the mode of the entity.""" - return self._config[CONF_MODE] + self._attr_native_value = last_number_data.native_value async def async_set_native_value(self, value: float) -> None: """Update the current value.""" @@ -290,7 +263,7 @@ class MqttNumber(MqttEntity, RestoreNumber): payload = self._templates[CONF_COMMAND_TEMPLATE](current_number) if self._optimistic: - self._current_number = current_number + self._attr_native_value = current_number self.async_write_ha_state() await self.async_publish( @@ -305,8 +278,3 @@ class MqttNumber(MqttEntity, RestoreNumber): def assumed_state(self) -> bool: """Return true if we do optimistic updates.""" return self._optimistic - - @property - def device_class(self) -> NumberDeviceClass | None: - """Return the device class of the sensor.""" - return self._config.get(CONF_DEVICE_CLASS) From 7a930d7e79665eb1796213893e0c0ff0b3a47a87 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 2 Nov 2022 13:39:36 +0100 Subject: [PATCH 0163/1033] Use attr in mqtt humidifier (#81400) --- homeassistant/components/mqtt/humidifier.py | 59 +++++++-------------- 1 file changed, 18 insertions(+), 41 deletions(-) diff --git a/homeassistant/components/mqtt/humidifier.py b/homeassistant/components/mqtt/humidifier.py index 7514f0ff672..e3e94c07dae 100644 --- a/homeassistant/components/mqtt/humidifier.py +++ b/homeassistant/components/mqtt/humidifier.py @@ -211,10 +211,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): def __init__(self, hass, config, config_entry, discovery_data): """Initialize the MQTT humidifier.""" - self._state = None - self._target_humidity = None - self._mode = None - self._supported_features = 0 + self._attr_mode = None self._topic = None self._payload = None @@ -265,10 +262,10 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): "MODE_RESET": config[CONF_PAYLOAD_RESET_MODE], } if CONF_MODE_COMMAND_TOPIC in config and CONF_AVAILABLE_MODES_LIST in config: - self._available_modes = config[CONF_AVAILABLE_MODES_LIST] + self._attr_available_modes = config[CONF_AVAILABLE_MODES_LIST] else: - self._available_modes = [] - if self._available_modes: + self._attr_available_modes = [] + if self._attr_available_modes: self._attr_supported_features = HumidifierEntityFeature.MODES else: self._attr_supported_features = 0 @@ -304,11 +301,11 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): _LOGGER.debug("Ignoring empty state from '%s'", msg.topic) return if payload == self._payload["STATE_ON"]: - self._state = True + self._attr_is_on = True elif payload == self._payload["STATE_OFF"]: - self._state = False + self._attr_is_on = False elif payload == PAYLOAD_NONE: - self._state = None + self._attr_is_on = None get_mqtt_data(self.hass).state_write_requests.write_state_request(self) if self._topic[CONF_STATE_TOPIC] is not None: @@ -330,7 +327,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): _LOGGER.debug("Ignoring empty target humidity from '%s'", msg.topic) return if rendered_target_humidity_payload == self._payload["HUMIDITY_RESET"]: - self._target_humidity = None + self._attr_target_humidity = None get_mqtt_data(self.hass).state_write_requests.write_state_request(self) return try: @@ -354,7 +351,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): rendered_target_humidity_payload, ) return - self._target_humidity = target_humidity + self._attr_target_humidity = target_humidity get_mqtt_data(self.hass).state_write_requests.write_state_request(self) if self._topic[CONF_TARGET_HUMIDITY_STATE_TOPIC] is not None: @@ -364,7 +361,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, } - self._target_humidity = None + self._attr_target_humidity = None @callback @log_messages(self.hass, self.entity_id) @@ -372,7 +369,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): """Handle new received MQTT message for mode.""" mode = self._value_templates[ATTR_MODE](msg.payload) if mode == self._payload["MODE_RESET"]: - self._mode = None + self._attr_mode = None get_mqtt_data(self.hass).state_write_requests.write_state_request(self) return if not mode: @@ -387,7 +384,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): ) return - self._mode = mode + self._attr_mode = mode get_mqtt_data(self.hass).state_write_requests.write_state_request(self) if self._topic[CONF_MODE_STATE_TOPIC] is not None: @@ -397,7 +394,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, } - self._mode = None + self._attr_mode = None self._sub_state = subscription.async_prepare_subscribe_topics( self.hass, self._sub_state, topics @@ -412,26 +409,6 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): """Return true if we do optimistic updates.""" return self._optimistic - @property - def available_modes(self) -> list: - """Get the list of available modes.""" - return self._available_modes - - @property - def is_on(self) -> bool | None: - """Return true if device is on.""" - return self._state - - @property - def target_humidity(self): - """Return the current target humidity.""" - return self._target_humidity - - @property - def mode(self): - """Return the current mode.""" - return self._mode - async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the entity. @@ -446,7 +423,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): self._config[CONF_ENCODING], ) if self._optimistic: - self._state = True + self._attr_is_on = True self.async_write_ha_state() async def async_turn_off(self, **kwargs: Any) -> None: @@ -463,7 +440,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): self._config[CONF_ENCODING], ) if self._optimistic: - self._state = False + self._attr_is_on = False self.async_write_ha_state() async def async_set_humidity(self, humidity: int) -> None: @@ -481,7 +458,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): ) if self._optimistic_target_humidity: - self._target_humidity = humidity + self._attr_target_humidity = humidity self.async_write_ha_state() async def async_set_mode(self, mode: str) -> None: @@ -489,7 +466,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): This method is a coroutine. """ - if mode not in self.available_modes: + if not self.available_modes or mode not in self.available_modes: _LOGGER.warning("'%s'is not a valid mode", mode) return @@ -504,5 +481,5 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): ) if self._optimistic_mode: - self._mode = mode + self._attr_mode = mode self.async_write_ha_state() From a255546e9d76a95c7c670a6d6e761f5eefa49801 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 2 Nov 2022 13:41:14 +0100 Subject: [PATCH 0164/1033] Use attr in mqtt binary sensor and switch (#81403) --- .../components/mqtt/binary_sensor.py | 28 +++++---------- homeassistant/components/mqtt/switch.py | 34 ++++++------------- 2 files changed, 19 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 915a2780283..d9351908665 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -10,7 +10,6 @@ import voluptuous as vol from homeassistant.components import binary_sensor from homeassistant.components.binary_sensor import ( DEVICE_CLASSES_SCHEMA, - BinarySensorDeviceClass, BinarySensorEntity, ) from homeassistant.config_entries import ConfigEntry @@ -126,7 +125,6 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): def __init__(self, hass, config, config_entry, discovery_data): """Initialize the MQTT binary sensor.""" - self._state: bool | None = None self._expiration_trigger = None self._delay_listener = None expire_after = config.get(CONF_EXPIRE_AFTER) @@ -154,7 +152,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): _LOGGER.debug("Skip state recovery after reload for %s", self.entity_id) return self._expired = False - self._state = last_state.state == STATE_ON + self._attr_is_on = last_state.state == STATE_ON self._expiration_trigger = async_track_point_in_utc_time( self.hass, self._value_is_expired, expiration_at @@ -180,8 +178,10 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: + self._attr_device_class = config.get(CONF_DEVICE_CLASS) self._attr_force_update = config[CONF_FORCE_UPDATE] + self._value_template = MqttValueTemplate( self._config.get(CONF_VALUE_TEMPLATE), entity=self, @@ -194,7 +194,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): def off_delay_listener(now): """Switch device off after a delay.""" self._delay_listener = None - self._state = False + self._attr_is_on = False self.async_write_ha_state() @callback @@ -233,11 +233,11 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): return if payload == self._config[CONF_PAYLOAD_ON]: - self._state = True + self._attr_is_on = True elif payload == self._config[CONF_PAYLOAD_OFF]: - self._state = False + self._attr_is_on = False elif payload == PAYLOAD_NONE: - self._state = None + self._attr_is_on = None else: # Payload is not for this entity template_info = "" if self._config.get(CONF_VALUE_TEMPLATE) is not None: @@ -256,7 +256,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): self._delay_listener = None off_delay = self._config.get(CONF_OFF_DELAY) - if self._state and off_delay is not None: + if self._attr_is_on and off_delay is not None: self._delay_listener = evt.async_call_later( self.hass, off_delay, off_delay_listener ) @@ -288,16 +288,6 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): self.async_write_ha_state() - @property - def is_on(self) -> bool | None: - """Return true if the binary sensor is on.""" - return self._state - - @property - def device_class(self) -> BinarySensorDeviceClass | None: - """Return the class of this sensor.""" - return self._config.get(CONF_DEVICE_CLASS) - @property def available(self) -> bool: """Return true if the device is available and value has not expired.""" diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index f8bf2f5bc6a..f2a40facd8b 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -7,11 +7,7 @@ from typing import Any import voluptuous as vol from homeassistant.components import switch -from homeassistant.components.switch import ( - DEVICE_CLASSES_SCHEMA, - SwitchDeviceClass, - SwitchEntity, -) +from homeassistant.components.switch import DEVICE_CLASSES_SCHEMA, SwitchEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_DEVICE_CLASS, @@ -125,8 +121,6 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity): def __init__(self, hass, config, config_entry, discovery_data): """Initialize the MQTT switch.""" - self._state = None - self._state_on = None self._state_off = None self._optimistic = None @@ -138,8 +132,10 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity): """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" + self._attr_device_class = config.get(CONF_DEVICE_CLASS) + state_on = config.get(CONF_STATE_ON) self._state_on = state_on if state_on else config[CONF_PAYLOAD_ON] @@ -163,11 +159,11 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity): """Handle new MQTT state messages.""" payload = self._value_template(msg.payload) if payload == self._state_on: - self._state = True + self._attr_is_on = True elif payload == self._state_off: - self._state = False + self._attr_is_on = False elif payload == PAYLOAD_NONE: - self._state = None + self._attr_is_on = None get_mqtt_data(self.hass).state_write_requests.write_state_request(self) @@ -193,23 +189,13 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity): await subscription.async_subscribe_topics(self.hass, self._sub_state) if self._optimistic and (last_state := await self.async_get_last_state()): - self._state = last_state.state == STATE_ON - - @property - def is_on(self) -> bool | None: - """Return true if device is on.""" - return self._state + self._attr_is_on = last_state.state == STATE_ON @property def assumed_state(self) -> bool: """Return true if we do optimistic updates.""" return self._optimistic - @property - def device_class(self) -> SwitchDeviceClass | None: - """Return the device class of the sensor.""" - return self._config.get(CONF_DEVICE_CLASS) - async def async_turn_on(self, **kwargs: Any) -> None: """Turn the device on. @@ -224,7 +210,7 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity): ) if self._optimistic: # Optimistically assume that switch has changed state. - self._state = True + self._attr_is_on = True self.async_write_ha_state() async def async_turn_off(self, **kwargs: Any) -> None: @@ -241,5 +227,5 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity): ) if self._optimistic: # Optimistically assume that switch has changed state. - self._state = False + self._attr_is_on = False self.async_write_ha_state() From b2a4228dae6fa07e749fc278efd158a9d547498f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Wed, 2 Nov 2022 14:50:38 +0100 Subject: [PATCH 0165/1033] Update adax library to 0.1.5 (#81407) --- homeassistant/components/adax/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/adax/manifest.json b/homeassistant/components/adax/manifest.json index 408c099b8ac..cbe14f0d7a5 100644 --- a/homeassistant/components/adax/manifest.json +++ b/homeassistant/components/adax/manifest.json @@ -3,7 +3,7 @@ "name": "Adax", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/adax", - "requirements": ["adax==0.2.0", "Adax-local==0.1.4"], + "requirements": ["adax==0.2.0", "Adax-local==0.1.5"], "codeowners": ["@danielhiversen"], "iot_class": "local_polling", "loggers": ["adax", "adax_local"] diff --git a/requirements_all.txt b/requirements_all.txt index 581f3cd12fd..b056708b791 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -8,7 +8,7 @@ AEMET-OpenData==0.2.1 AIOAladdinConnect==0.1.46 # homeassistant.components.adax -Adax-local==0.1.4 +Adax-local==0.1.5 # homeassistant.components.mastodon Mastodon.py==1.5.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c7aad37a8ad..a977cfd710e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -10,7 +10,7 @@ AEMET-OpenData==0.2.1 AIOAladdinConnect==0.1.46 # homeassistant.components.adax -Adax-local==0.1.4 +Adax-local==0.1.5 # homeassistant.components.flick_electric PyFlick==0.0.2 From ab14e55c052433e42224199798b026637614685f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 2 Nov 2022 14:57:59 +0100 Subject: [PATCH 0166/1033] Ensure we do not actually create a BleakScanner in the usage test (#81362) Avoids a failure when bluetooth is turned off when testing on macos: bleak.exc.BleakError: Bluetooth device is turned off --- tests/components/bluetooth/test_usage.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/components/bluetooth/test_usage.py b/tests/components/bluetooth/test_usage.py index 1bea3b149cd..7e0d97d3d91 100644 --- a/tests/components/bluetooth/test_usage.py +++ b/tests/components/bluetooth/test_usage.py @@ -33,7 +33,8 @@ async def test_multiple_bleak_scanner_instances(hass): uninstall_multiple_bleak_catcher() - instance = bleak.BleakScanner() + with patch("bleak.get_platform_scanner_backend_type"): + instance = bleak.BleakScanner() assert not isinstance(instance, HaBleakScannerWrapper) From 71920cd6878ff9363477fa4ee97dc2807875e987 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 2 Nov 2022 15:02:09 +0100 Subject: [PATCH 0167/1033] Update spotipy to 2.21.0 (#81395) --- homeassistant/components/spotify/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/spotify/manifest.json b/homeassistant/components/spotify/manifest.json index 0ce71f371df..be662e969f2 100644 --- a/homeassistant/components/spotify/manifest.json +++ b/homeassistant/components/spotify/manifest.json @@ -2,7 +2,7 @@ "domain": "spotify", "name": "Spotify", "documentation": "https://www.home-assistant.io/integrations/spotify", - "requirements": ["spotipy==2.20.0"], + "requirements": ["spotipy==2.21.0"], "zeroconf": ["_spotify-connect._tcp.local."], "dependencies": ["application_credentials"], "codeowners": ["@frenck"], diff --git a/requirements_all.txt b/requirements_all.txt index b056708b791..dd54335dd71 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2310,7 +2310,7 @@ speedtest-cli==2.1.3 spiderpy==1.6.1 # homeassistant.components.spotify -spotipy==2.20.0 +spotipy==2.21.0 # homeassistant.components.recorder # homeassistant.components.sql diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a977cfd710e..3ec5e21e4f6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1595,7 +1595,7 @@ speedtest-cli==2.1.3 spiderpy==1.6.1 # homeassistant.components.spotify -spotipy==2.20.0 +spotipy==2.21.0 # homeassistant.components.recorder # homeassistant.components.sql From fc3843f5e2de3a22d1d5170e747ee905524ec65b Mon Sep 17 00:00:00 2001 From: Rami Mosleh Date: Wed, 2 Nov 2022 17:11:44 +0200 Subject: [PATCH 0168/1033] Add config flow to `pushbullet` (#74240) Co-authored-by: Martin Hjelmare --- .coveragerc | 1 + CODEOWNERS | 2 + .../components/pushbullet/__init__.py | 78 +++++++ homeassistant/components/pushbullet/api.py | 32 +++ .../components/pushbullet/config_flow.py | 63 +++++ homeassistant/components/pushbullet/const.py | 12 + .../components/pushbullet/manifest.json | 3 +- homeassistant/components/pushbullet/notify.py | 172 +++++++------- homeassistant/components/pushbullet/sensor.py | 138 +++++------ .../components/pushbullet/strings.json | 25 ++ .../pushbullet/translations/en.json | 25 ++ homeassistant/generated/config_flows.py | 1 + homeassistant/generated/integrations.json | 2 +- tests/components/pushbullet/__init__.py | 4 + tests/components/pushbullet/conftest.py | 28 +++ .../pushbullet/fixtures/channels.json | 14 ++ .../components/pushbullet/fixtures/chats.json | 18 ++ .../pushbullet/fixtures/user_info.json | 10 + .../components/pushbullet/test_config_flow.py | 134 +++++++++++ tests/components/pushbullet/test_init.py | 84 +++++++ tests/components/pushbullet/test_notify.py | 219 +++++------------- 21 files changed, 755 insertions(+), 310 deletions(-) create mode 100644 homeassistant/components/pushbullet/api.py create mode 100644 homeassistant/components/pushbullet/config_flow.py create mode 100644 homeassistant/components/pushbullet/const.py create mode 100644 homeassistant/components/pushbullet/strings.json create mode 100644 homeassistant/components/pushbullet/translations/en.json create mode 100644 tests/components/pushbullet/conftest.py create mode 100644 tests/components/pushbullet/fixtures/channels.json create mode 100644 tests/components/pushbullet/fixtures/chats.json create mode 100644 tests/components/pushbullet/fixtures/user_info.json create mode 100644 tests/components/pushbullet/test_config_flow.py create mode 100644 tests/components/pushbullet/test_init.py diff --git a/.coveragerc b/.coveragerc index 9d33acf6c75..b782f8444db 100644 --- a/.coveragerc +++ b/.coveragerc @@ -999,6 +999,7 @@ omit = homeassistant/components/proxmoxve/* homeassistant/components/proxy/camera.py homeassistant/components/pulseaudio_loopback/switch.py + homeassistant/components/pushbullet/api.py homeassistant/components/pushbullet/notify.py homeassistant/components/pushbullet/sensor.py homeassistant/components/pushover/notify.py diff --git a/CODEOWNERS b/CODEOWNERS index 4012868c712..cb4e68f1535 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -880,6 +880,8 @@ build.json @home-assistant/supervisor /tests/components/pure_energie/ @klaasnicolaas /homeassistant/components/push/ @dgomes /tests/components/push/ @dgomes +/homeassistant/components/pushbullet/ @engrbm87 +/tests/components/pushbullet/ @engrbm87 /homeassistant/components/pushover/ @engrbm87 /tests/components/pushover/ @engrbm87 /homeassistant/components/pvoutput/ @frenck diff --git a/homeassistant/components/pushbullet/__init__.py b/homeassistant/components/pushbullet/__init__.py index 153fa389fcc..bed0e94ccd9 100644 --- a/homeassistant/components/pushbullet/__init__.py +++ b/homeassistant/components/pushbullet/__init__.py @@ -1 +1,79 @@ """The pushbullet component.""" +from __future__ import annotations + +import logging + +from pushbullet import InvalidKeyError, PushBullet, PushbulletError + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + CONF_API_KEY, + CONF_NAME, + EVENT_HOMEASSISTANT_START, + Platform, +) +from homeassistant.core import Event, HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers import discovery +from homeassistant.helpers.typing import ConfigType + +from .api import PushBulletNotificationProvider +from .const import DATA_HASS_CONFIG, DOMAIN + +PLATFORMS = [Platform.SENSOR] + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up the pushbullet component.""" + + hass.data[DATA_HASS_CONFIG] = config + return True + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up pushbullet from a config entry.""" + + try: + pushbullet = await hass.async_add_executor_job( + PushBullet, entry.data[CONF_API_KEY] + ) + except InvalidKeyError: + _LOGGER.error("Invalid API key for Pushbullet") + return False + except PushbulletError as err: + raise ConfigEntryNotReady from err + + pb_provider = PushBulletNotificationProvider(hass, pushbullet) + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = pb_provider + + def start_listener(event: Event) -> None: + """Start the listener thread.""" + _LOGGER.debug("Starting listener for pushbullet") + pb_provider.start() + + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, start_listener) + + hass.async_create_task( + discovery.async_load_platform( + hass, + Platform.NOTIFY, + DOMAIN, + {CONF_NAME: entry.data[CONF_NAME], "entry_id": entry.entry_id}, + hass.data[DATA_HASS_CONFIG], + ) + ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + pb_provider: PushBulletNotificationProvider = hass.data[DOMAIN].pop( + entry.entry_id + ) + await hass.async_add_executor_job(pb_provider.close) + return unload_ok diff --git a/homeassistant/components/pushbullet/api.py b/homeassistant/components/pushbullet/api.py new file mode 100644 index 00000000000..ff6a57aa931 --- /dev/null +++ b/homeassistant/components/pushbullet/api.py @@ -0,0 +1,32 @@ +"""Pushbullet Notification provider.""" + +from typing import Any + +from pushbullet import Listener, PushBullet + +from homeassistant.core import HomeAssistant +from homeassistant.helpers.dispatcher import dispatcher_send + +from .const import DATA_UPDATED + + +class PushBulletNotificationProvider(Listener): + """Provider for an account, leading to one or more sensors.""" + + def __init__(self, hass: HomeAssistant, pushbullet: PushBullet) -> None: + """Start to retrieve pushes from the given Pushbullet instance.""" + self.hass = hass + self.pushbullet = pushbullet + self.data: dict[str, Any] = {} + super().__init__(account=pushbullet, on_push=self.update_data) + self.daemon = True + + def update_data(self, data: dict[str, Any]) -> None: + """Update the current data. + + Currently only monitors pushes but might be extended to monitor + different kinds of Pushbullet events. + """ + if data["type"] == "push": + self.data = data["push"] + dispatcher_send(self.hass, DATA_UPDATED) diff --git a/homeassistant/components/pushbullet/config_flow.py b/homeassistant/components/pushbullet/config_flow.py new file mode 100644 index 00000000000..bfa12a911b6 --- /dev/null +++ b/homeassistant/components/pushbullet/config_flow.py @@ -0,0 +1,63 @@ +"""Config flow for pushbullet integration.""" +from __future__ import annotations + +from typing import Any + +from pushbullet import InvalidKeyError, PushBullet, PushbulletError +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.const import CONF_API_KEY, CONF_NAME +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import selector + +from .const import DEFAULT_NAME, DOMAIN + +CONFIG_SCHEMA = vol.Schema( + { + vol.Optional(CONF_NAME, default=DEFAULT_NAME): selector.TextSelector(), + vol.Required(CONF_API_KEY): selector.TextSelector(), + } +) + + +class PushBulletConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for pushbullet integration.""" + + async def async_step_import(self, import_config: dict[str, Any]) -> FlowResult: + """Handle import from config.""" + import_config[CONF_NAME] = import_config.get(CONF_NAME, DEFAULT_NAME) + return await self.async_step_user(import_config) + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the initial step.""" + errors = {} + + if user_input is not None: + + self._async_abort_entries_match({CONF_NAME: user_input[CONF_NAME]}) + + try: + pushbullet = await self.hass.async_add_executor_job( + PushBullet, user_input[CONF_API_KEY] + ) + except InvalidKeyError: + errors[CONF_API_KEY] = "invalid_api_key" + except PushbulletError: + errors["base"] = "cannot_connect" + + if not errors: + await self.async_set_unique_id(pushbullet.user_info["iden"]) + self._abort_if_unique_id_configured() + return self.async_create_entry( + title=user_input[CONF_NAME], + data=user_input, + ) + + return self.async_show_form( + step_id="user", + data_schema=CONFIG_SCHEMA, + errors=errors, + ) diff --git a/homeassistant/components/pushbullet/const.py b/homeassistant/components/pushbullet/const.py new file mode 100644 index 00000000000..de81f56e862 --- /dev/null +++ b/homeassistant/components/pushbullet/const.py @@ -0,0 +1,12 @@ +"""Constants for the pushbullet integration.""" + +from typing import Final + +DOMAIN: Final = "pushbullet" +DEFAULT_NAME: Final = "Pushbullet" +DATA_HASS_CONFIG: Final = "pushbullet_hass_config" +DATA_UPDATED: Final = "pushbullet_data_updated" + +ATTR_URL: Final = "url" +ATTR_FILE: Final = "file" +ATTR_FILE_URL: Final = "file_url" diff --git a/homeassistant/components/pushbullet/manifest.json b/homeassistant/components/pushbullet/manifest.json index 7931cca70cc..7fcaa59fbb8 100644 --- a/homeassistant/components/pushbullet/manifest.json +++ b/homeassistant/components/pushbullet/manifest.json @@ -3,7 +3,8 @@ "name": "Pushbullet", "documentation": "https://www.home-assistant.io/integrations/pushbullet", "requirements": ["pushbullet.py==0.11.0"], - "codeowners": [], + "codeowners": ["@engrbm87"], + "config_flow": true, "iot_class": "cloud_polling", "loggers": ["pushbullet"] } diff --git a/homeassistant/components/pushbullet/notify.py b/homeassistant/components/pushbullet/notify.py index 6f851f8000e..fcc9d00dc7a 100644 --- a/homeassistant/components/pushbullet/notify.py +++ b/homeassistant/components/pushbullet/notify.py @@ -1,8 +1,13 @@ """Pushbullet platform for notify component.""" +from __future__ import annotations + import logging import mimetypes +from typing import Any -from pushbullet import InvalidKeyError, PushBullet, PushError +from pushbullet import PushBullet, PushError +from pushbullet.channel import Channel +from pushbullet.device import Device import voluptuous as vol from homeassistant.components.notify import ( @@ -13,59 +18,69 @@ from homeassistant.components.notify import ( PLATFORM_SCHEMA, BaseNotificationService, ) +from homeassistant.config_entries import SOURCE_IMPORT from homeassistant.const import CONF_API_KEY +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType + +from .const import ATTR_FILE, ATTR_FILE_URL, ATTR_URL, DOMAIN _LOGGER = logging.getLogger(__name__) -ATTR_URL = "url" -ATTR_FILE = "file" -ATTR_FILE_URL = "file_url" -ATTR_LIST = "list" - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({vol.Required(CONF_API_KEY): cv.string}) -def get_service(hass, config, discovery_info=None): +async def async_get_service( + hass: HomeAssistant, + config: ConfigType, + discovery_info: DiscoveryInfoType | None = None, +) -> PushBulletNotificationService | None: """Get the Pushbullet notification service.""" - - try: - pushbullet = PushBullet(config[CONF_API_KEY]) - except InvalidKeyError: - _LOGGER.error("Wrong API key supplied") + if discovery_info is None: + async_create_issue( + hass, + DOMAIN, + "deprecated_yaml", + breaks_in_ha_version="2023.2.0", + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key="deprecated_yaml", + ) + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data=config, + ) + ) return None - return PushBulletNotificationService(pushbullet) + pushbullet: PushBullet = hass.data[DOMAIN][discovery_info["entry_id"]].pushbullet + return PushBulletNotificationService(hass, pushbullet) class PushBulletNotificationService(BaseNotificationService): """Implement the notification service for Pushbullet.""" - def __init__(self, pb): # pylint: disable=invalid-name + def __init__(self, hass: HomeAssistant, pushbullet: PushBullet) -> None: """Initialize the service.""" - self.pushbullet = pb - self.pbtargets = {} - self.refresh() + self.hass = hass + self.pushbullet = pushbullet - def refresh(self): - """Refresh devices, contacts, etc. - - pbtargets stores all targets available from this Pushbullet instance - into a dict. These are Pushbullet objects!. It sacrifices a bit of - memory for faster processing at send_message. - - As of sept 2015, contacts were replaced by chats. This is not - implemented in the module yet. - """ - self.pushbullet.refresh() - self.pbtargets = { + @property + def pbtargets(self) -> dict[str, dict[str, Device | Channel]]: + """Return device and channel detected targets.""" + return { "device": {tgt.nickname.lower(): tgt for tgt in self.pushbullet.devices}, "channel": { tgt.channel_tag.lower(): tgt for tgt in self.pushbullet.channels }, } - def send_message(self, message=None, **kwargs): + def send_message(self, message: str, **kwargs: Any) -> None: """Send a message to a specified target. If no target specified, a 'normal' push will be sent to all devices @@ -73,24 +88,25 @@ class PushBulletNotificationService(BaseNotificationService): Email is special, these are assumed to always exist. We use a special call which doesn't require a push object. """ - targets = kwargs.get(ATTR_TARGET) - title = kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT) - data = kwargs.get(ATTR_DATA) - refreshed = False + targets: list[str] = kwargs.get(ATTR_TARGET, []) + title: str = kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT) + data: dict[str, Any] = kwargs[ATTR_DATA] or {} if not targets: # Backward compatibility, notify all devices in own account. self._push_data(message, title, data, self.pushbullet) - _LOGGER.info("Sent notification to self") + _LOGGER.debug("Sent notification to self") return + # refresh device and channel targets + self.pushbullet.refresh() + # Main loop, process all targets specified. for target in targets: try: ttype, tname = target.split("/", 1) - except ValueError: - _LOGGER.error("Invalid target syntax: %s", target) - continue + except ValueError as err: + raise ValueError(f"Invalid target syntax: '{target}'") from err # Target is email, send directly, don't use a target object. # This also seems to work to send to all devices in own account. @@ -107,71 +123,57 @@ class PushBulletNotificationService(BaseNotificationService): _LOGGER.info("Sent sms notification to %s", tname) continue - # Refresh if name not found. While awaiting periodic refresh - # solution in component, poor mans refresh. if ttype not in self.pbtargets: - _LOGGER.error("Invalid target syntax: %s", target) - continue + raise ValueError(f"Invalid target syntax: {target}") tname = tname.lower() - if tname not in self.pbtargets[ttype] and not refreshed: - self.refresh() - refreshed = True + if tname not in self.pbtargets[ttype]: + raise ValueError(f"Target: {target} doesn't exist") # Attempt push_note on a dict value. Keys are types & target # name. Dict pbtargets has all *actual* targets. - try: - self._push_data(message, title, data, self.pbtargets[ttype][tname]) - _LOGGER.info("Sent notification to %s/%s", ttype, tname) - except KeyError: - _LOGGER.error("No such target: %s/%s", ttype, tname) - continue + self._push_data(message, title, data, self.pbtargets[ttype][tname]) + _LOGGER.debug("Sent notification to %s/%s", ttype, tname) - def _push_data(self, message, title, data, pusher, email=None, phonenumber=None): + def _push_data( + self, + message: str, + title: str, + data: dict[str, Any], + pusher: PushBullet, + email: str | None = None, + phonenumber: str | None = None, + ): """Create the message content.""" + kwargs = {"body": message, "title": title} + if email: + kwargs["email"] = email - if data is None: - data = {} - data_list = data.get(ATTR_LIST) - url = data.get(ATTR_URL) - filepath = data.get(ATTR_FILE) - file_url = data.get(ATTR_FILE_URL) try: - email_kwargs = {} - if email: - email_kwargs["email"] = email - if phonenumber: - device = pusher.devices[0] - pusher.push_sms(device, phonenumber, message) - elif url: - pusher.push_link(title, url, body=message, **email_kwargs) - elif filepath: + if phonenumber and pusher.devices: + pusher.push_sms(pusher.devices[0], phonenumber, message) + return + if url := data.get(ATTR_URL): + pusher.push_link(url=url, **kwargs) + return + if filepath := data.get(ATTR_FILE): if not self.hass.config.is_allowed_path(filepath): - _LOGGER.error("Filepath is not valid or allowed") - return + raise ValueError("Filepath is not valid or allowed") with open(filepath, "rb") as fileh: filedata = self.pushbullet.upload_file(fileh, filepath) - if filedata.get("file_type") == "application/x-empty": - _LOGGER.error("Can not send an empty file") - return - filedata.update(email_kwargs) - pusher.push_file(title=title, body=message, **filedata) - elif file_url: - if not file_url.startswith("http"): - _LOGGER.error("URL should start with http or https") - return + if filedata.get("file_type") == "application/x-empty": + raise ValueError("Cannot send an empty file") + kwargs.update(filedata) + pusher.push_file(**kwargs) + elif (file_url := data.get(ATTR_FILE_URL)) and vol.Url(file_url): pusher.push_file( - title=title, - body=message, file_name=file_url, file_url=file_url, file_type=(mimetypes.guess_type(file_url)[0]), - **email_kwargs, + **kwargs, ) - elif data_list: - pusher.push_list(title, data_list, **email_kwargs) else: - pusher.push_note(title, message, **email_kwargs) + pusher.push_note(**kwargs) except PushError as err: - _LOGGER.error("Notify failed: %s", err) + raise HomeAssistantError(f"Notify failed: {err}") from err diff --git a/homeassistant/components/pushbullet/sensor.py b/homeassistant/components/pushbullet/sensor.py index 51a18f1aaea..aef97991c66 100644 --- a/homeassistant/components/pushbullet/sensor.py +++ b/homeassistant/components/pushbullet/sensor.py @@ -1,10 +1,6 @@ """Pushbullet platform for sensor component.""" from __future__ import annotations -import logging -import threading - -from pushbullet import InvalidKeyError, Listener, PushBullet import voluptuous as vol from homeassistant.components.sensor import ( @@ -12,18 +8,25 @@ from homeassistant.components.sensor import ( SensorEntity, SensorEntityDescription, ) -from homeassistant.const import CONF_API_KEY, CONF_MONITORED_CONDITIONS -from homeassistant.core import HomeAssistant +from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry +from homeassistant.const import CONF_API_KEY, CONF_MONITORED_CONDITIONS, CONF_NAME +from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.device_registry import DeviceEntryType +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -_LOGGER = logging.getLogger(__name__) +from .api import PushBulletNotificationProvider +from .const import DATA_UPDATED, DOMAIN SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="application_name", name="Application name", + entity_registry_enabled_default=False, ), SensorEntityDescription( key="body", @@ -32,26 +35,32 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="notification_id", name="Notification ID", + entity_registry_enabled_default=False, ), SensorEntityDescription( key="notification_tag", name="Notification tag", + entity_registry_enabled_default=False, ), SensorEntityDescription( key="package_name", name="Package name", + entity_registry_enabled_default=False, ), SensorEntityDescription( key="receiver_email", name="Receiver email", + entity_registry_enabled_default=False, ), SensorEntityDescription( key="sender_email", name="Sender email", + entity_registry_enabled_default=False, ), SensorEntityDescription( key="source_device_iden", name="Sender device ID", + entity_registry_enabled_default=False, ), SensorEntityDescription( key="title", @@ -60,6 +69,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="type", name="Type", + entity_registry_enabled_default=False, ), ) @@ -75,94 +85,88 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( ) -def setup_platform( +async def async_setup_platform( hass: HomeAssistant, config: ConfigType, - add_entities: AddEntitiesCallback, + async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Pushbullet Sensor platform.""" + async_create_issue( + hass, + DOMAIN, + "deprecated_yaml", + breaks_in_ha_version="2023.2.0", + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key="deprecated_yaml", + ) + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data=config, + ) + ) - try: - pushbullet = PushBullet(config.get(CONF_API_KEY)) - except InvalidKeyError: - _LOGGER.error("Wrong API key for Pushbullet supplied") - return - pbprovider = PushBulletNotificationProvider(pushbullet) +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up the Pushbullet sensors from config entry.""" + + pb_provider: PushBulletNotificationProvider = hass.data[DOMAIN][entry.entry_id] - monitored_conditions = config[CONF_MONITORED_CONDITIONS] entities = [ - PushBulletNotificationSensor(pbprovider, description) + PushBulletNotificationSensor(entry.data[CONF_NAME], pb_provider, description) for description in SENSOR_TYPES - if description.key in monitored_conditions ] - add_entities(entities) + + async_add_entities(entities) class PushBulletNotificationSensor(SensorEntity): """Representation of a Pushbullet Sensor.""" + _attr_should_poll = False + _attr_has_entity_name = True + def __init__( self, - pb, # pylint: disable=invalid-name + name: str, + pb_provider: PushBulletNotificationProvider, description: SensorEntityDescription, - ): + ) -> None: """Initialize the Pushbullet sensor.""" self.entity_description = description - self.pushbullet = pb + self.pb_provider = pb_provider + self._attr_unique_id = ( + f"{pb_provider.pushbullet.user_info['iden']}-{description.key}" + ) + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, pb_provider.pushbullet.user_info["iden"])}, + name=name, + entry_type=DeviceEntryType.SERVICE, + ) - self._attr_name = f"Pushbullet {description.key}" - - def update(self) -> None: + @callback + def async_update_callback(self) -> None: """Fetch the latest data from the sensor. This will fetch the 'sensor reading' into self._state but also all attributes into self._state_attributes. """ try: - self._attr_native_value = self.pushbullet.data[self.entity_description.key] - self._attr_extra_state_attributes = self.pushbullet.data + self._attr_native_value = self.pb_provider.data[self.entity_description.key] + self._attr_extra_state_attributes = self.pb_provider.data except (KeyError, TypeError): pass + self.async_write_ha_state() - -class PushBulletNotificationProvider: - """Provider for an account, leading to one or more sensors.""" - - def __init__(self, pushbullet): - """Start to retrieve pushes from the given Pushbullet instance.""" - - self.pushbullet = pushbullet - self._data = None - self.listener = None - self.thread = threading.Thread(target=self.retrieve_pushes) - self.thread.daemon = True - self.thread.start() - - def on_push(self, data): - """Update the current data. - - Currently only monitors pushes but might be extended to monitor - different kinds of Pushbullet events. - """ - if data["type"] == "push": - self._data = data["push"] - - @property - def data(self): - """Return the current data stored in the provider.""" - return self._data - - def retrieve_pushes(self): - """Retrieve_pushes. - - Spawn a new Listener and links it to self.on_push. - """ - - self.listener = Listener(account=self.pushbullet, on_push=self.on_push) - _LOGGER.debug("Getting pushes") - try: - self.listener.run_forever() - finally: - self.listener.close() + async def async_added_to_hass(self) -> None: + """Register callbacks.""" + self.async_on_remove( + async_dispatcher_connect( + self.hass, DATA_UPDATED, self.async_update_callback + ) + ) diff --git a/homeassistant/components/pushbullet/strings.json b/homeassistant/components/pushbullet/strings.json new file mode 100644 index 00000000000..92d22d117dc --- /dev/null +++ b/homeassistant/components/pushbullet/strings.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_service%]" + }, + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]" + }, + "step": { + "user": { + "data": { + "name": "[%key:common::config_flow::data::name%]", + "api_key": "[%key:common::config_flow::data::api_key%]" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "title": "The Pushbullet YAML configuration is being removed", + "description": "Configuring Pushbullet using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Pushbullet YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue." + } + } +} diff --git a/homeassistant/components/pushbullet/translations/en.json b/homeassistant/components/pushbullet/translations/en.json new file mode 100644 index 00000000000..97175ddf0b0 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/en.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Service is already configured" + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_api_key": "Invalid API key" + }, + "step": { + "user": { + "data": { + "api_key": "API Key", + "name": "Name" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Configuring Pushbullet using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Pushbullet YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", + "title": "The Pushbullet YAML configuration is being removed" + } + } +} \ No newline at end of file diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 5279f343691..5214436be42 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -304,6 +304,7 @@ FLOWS = { "prusalink", "ps4", "pure_energie", + "pushbullet", "pushover", "pvoutput", "pvpc_hourly_pricing", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 57452743af1..a955193624c 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -4085,7 +4085,7 @@ "pushbullet": { "name": "Pushbullet", "integration_type": "hub", - "config_flow": false, + "config_flow": true, "iot_class": "cloud_polling" }, "pushover": { diff --git a/tests/components/pushbullet/__init__.py b/tests/components/pushbullet/__init__.py index c7f7911950c..74c89f33f2b 100644 --- a/tests/components/pushbullet/__init__.py +++ b/tests/components/pushbullet/__init__.py @@ -1 +1,5 @@ """Tests for the pushbullet component.""" + +from homeassistant.const import CONF_API_KEY, CONF_NAME + +MOCK_CONFIG = {CONF_NAME: "pushbullet", CONF_API_KEY: "MYAPIKEY"} diff --git a/tests/components/pushbullet/conftest.py b/tests/components/pushbullet/conftest.py new file mode 100644 index 00000000000..5fadff3a825 --- /dev/null +++ b/tests/components/pushbullet/conftest.py @@ -0,0 +1,28 @@ +"""Conftest for pushbullet integration.""" + +from pushbullet import PushBullet +import pytest +from requests_mock import Mocker + +from tests.common import load_fixture + + +@pytest.fixture(autouse=True) +def requests_mock_fixture(requests_mock: Mocker) -> None: + """Fixture to provide a aioclient mocker.""" + requests_mock.get( + PushBullet.DEVICES_URL, + text=load_fixture("devices.json", "pushbullet"), + ) + requests_mock.get( + PushBullet.ME_URL, + text=load_fixture("user_info.json", "pushbullet"), + ) + requests_mock.get( + PushBullet.CHATS_URL, + text=load_fixture("chats.json", "pushbullet"), + ) + requests_mock.get( + PushBullet.CHANNELS_URL, + text=load_fixture("channels.json", "pushbullet"), + ) diff --git a/tests/components/pushbullet/fixtures/channels.json b/tests/components/pushbullet/fixtures/channels.json new file mode 100644 index 00000000000..b95ca50b7c2 --- /dev/null +++ b/tests/components/pushbullet/fixtures/channels.json @@ -0,0 +1,14 @@ +{ + "channels": [ + { + "active": true, + "created": 1412047948.579029, + "description": "Sample channel.", + "iden": "ujxPklLhvyKsjAvkMyTVh6", + "image_url": "https://dl.pushbulletusercontent.com/abc123/image.jpg", + "modified": 1412047948.579031, + "name": "Sample channel", + "tag": "sample-channel" + } + ] +} diff --git a/tests/components/pushbullet/fixtures/chats.json b/tests/components/pushbullet/fixtures/chats.json new file mode 100644 index 00000000000..4c52bcc58cc --- /dev/null +++ b/tests/components/pushbullet/fixtures/chats.json @@ -0,0 +1,18 @@ +{ + "chats": [ + { + "active": true, + "created": 1412047948.579029, + "iden": "ujpah72o0sjAoRtnM0jc", + "modified": 1412047948.579031, + "with": { + "email": "someone@example.com", + "email_normalized": "someone@example.com", + "iden": "ujlMns72k", + "image_url": "https://dl.pushbulletusercontent.com/acb123/example.jpg", + "name": "Someone", + "type": "user" + } + } + ] +} diff --git a/tests/components/pushbullet/fixtures/user_info.json b/tests/components/pushbullet/fixtures/user_info.json new file mode 100644 index 00000000000..3a17cccbf07 --- /dev/null +++ b/tests/components/pushbullet/fixtures/user_info.json @@ -0,0 +1,10 @@ +{ + "created": 1381092887.398433, + "email": "example@email.com", + "email_normalized": "example@email.com", + "iden": "ujpah72o0", + "image_url": "https://static.pushbullet.com/missing-image/55a7dc-45", + "max_upload_size": 26214400, + "modified": 1441054560.741007, + "name": "Some name" +} diff --git a/tests/components/pushbullet/test_config_flow.py b/tests/components/pushbullet/test_config_flow.py new file mode 100644 index 00000000000..a19c424c8be --- /dev/null +++ b/tests/components/pushbullet/test_config_flow.py @@ -0,0 +1,134 @@ +"""Test pushbullet config flow.""" +from unittest.mock import patch + +from pushbullet import InvalidKeyError, PushbulletError +import pytest + +from homeassistant import config_entries, data_entry_flow +from homeassistant.components.pushbullet.const import DOMAIN +from homeassistant.const import CONF_API_KEY +from homeassistant.core import HomeAssistant + +from . import MOCK_CONFIG + +from tests.common import MockConfigEntry + + +@pytest.fixture(autouse=True) +def pushbullet_setup_fixture(): + """Patch pushbullet setup entry.""" + with patch( + "homeassistant.components.pushbullet.async_setup_entry", return_value=True + ): + yield + + +async def test_flow_user(hass: HomeAssistant, requests_mock_fixture) -> None: + """Test user initialized flow.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input=MOCK_CONFIG, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == "pushbullet" + assert result["data"] == MOCK_CONFIG + + +async def test_flow_user_already_configured( + hass: HomeAssistant, requests_mock_fixture +) -> None: + """Test user initialized flow with duplicate server.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_CONFIG, + unique_id="ujpah72o0", + ) + + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input=MOCK_CONFIG, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + + +async def test_flow_name_already_configured(hass: HomeAssistant) -> None: + """Test user initialized flow with duplicate server.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_CONFIG, + unique_id="MYAPIKEY", + ) + + entry.add_to_hass(hass) + + new_config = MOCK_CONFIG.copy() + new_config[CONF_API_KEY] = "NEWKEY" + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input=new_config, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + + +async def test_flow_invalid_key(hass: HomeAssistant) -> None: + """Test user initialized flow with invalid api key.""" + + with patch( + "homeassistant.components.pushbullet.config_flow.PushBullet", + side_effect=InvalidKeyError, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + data=MOCK_CONFIG, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + assert result["errors"] == {CONF_API_KEY: "invalid_api_key"} + + +async def test_flow_conn_error(hass: HomeAssistant) -> None: + """Test user initialized flow with conn error.""" + + with patch( + "homeassistant.components.pushbullet.config_flow.PushBullet", + side_effect=PushbulletError, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + data=MOCK_CONFIG, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + assert result["errors"] == {"base": "cannot_connect"} + + +async def test_import(hass: HomeAssistant, requests_mock_fixture) -> None: + """Test user initialized flow with unreachable server.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data=MOCK_CONFIG, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == "pushbullet" + assert result["data"] == MOCK_CONFIG diff --git a/tests/components/pushbullet/test_init.py b/tests/components/pushbullet/test_init.py new file mode 100644 index 00000000000..6f8e3776b35 --- /dev/null +++ b/tests/components/pushbullet/test_init.py @@ -0,0 +1,84 @@ +"""Test pushbullet integration.""" +from unittest.mock import patch + +from pushbullet import InvalidKeyError, PushbulletError + +from homeassistant.components.pushbullet.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import EVENT_HOMEASSISTANT_START +from homeassistant.core import HomeAssistant + +from . import MOCK_CONFIG + +from tests.common import MockConfigEntry + + +async def test_async_setup_entry_success( + hass: HomeAssistant, requests_mock_fixture +) -> None: + """Test pushbullet successful setup.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_CONFIG, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + assert entry.state == ConfigEntryState.LOADED + + with patch( + "homeassistant.components.pushbullet.api.PushBulletNotificationProvider.start" + ) as mock_start: + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + mock_start.assert_called_once() + + +async def test_setup_entry_failed_invalid_key(hass: HomeAssistant) -> None: + """Test pushbullet failed setup due to invalid key.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_CONFIG, + ) + entry.add_to_hass(hass) + with patch( + "homeassistant.components.pushbullet.PushBullet", + side_effect=InvalidKeyError, + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + assert entry.state == ConfigEntryState.SETUP_ERROR + + +async def test_setup_entry_failed_conn_error(hass: HomeAssistant) -> None: + """Test pushbullet failed setup due to conn error.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_CONFIG, + ) + entry.add_to_hass(hass) + with patch( + "homeassistant.components.pushbullet.PushBullet", + side_effect=PushbulletError, + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + assert entry.state == ConfigEntryState.SETUP_RETRY + + +async def test_async_unload_entry(hass: HomeAssistant, requests_mock_fixture) -> None: + """Test pushbullet unload entry.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_CONFIG, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + assert entry.state == ConfigEntryState.LOADED + + await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + assert entry.state == ConfigEntryState.NOT_LOADED diff --git a/tests/components/pushbullet/test_notify.py b/tests/components/pushbullet/test_notify.py index a9186652f64..2773679f22a 100644 --- a/tests/components/pushbullet/test_notify.py +++ b/tests/components/pushbullet/test_notify.py @@ -1,109 +1,65 @@ -"""The tests for the pushbullet notification platform.""" +"""Test pushbullet notification platform.""" from http import HTTPStatus -import json -from unittest.mock import patch -from pushbullet import PushBullet -import pytest +from requests_mock import Mocker -import homeassistant.components.notify as notify -from homeassistant.setup import async_setup_component +from homeassistant.components.notify import DOMAIN as NOTIFY_DOMAIN +from homeassistant.components.pushbullet.const import DOMAIN -from tests.common import assert_setup_component, load_fixture +from . import MOCK_CONFIG + +from tests.common import MockConfigEntry -@pytest.fixture -def mock_pushbullet(): - """Mock pushbullet.""" - with patch.object( - PushBullet, - "_get_data", - return_value=json.loads(load_fixture("devices.json", "pushbullet")), - ): - yield - - -async def test_pushbullet_config(hass, mock_pushbullet): - """Test setup.""" - config = { - notify.DOMAIN: { - "name": "test", - "platform": "pushbullet", - "api_key": "MYFAKEKEY", - } - } - with assert_setup_component(1) as handle_config: - assert await async_setup_component(hass, notify.DOMAIN, config) - await hass.async_block_till_done() - assert handle_config[notify.DOMAIN] - - -async def test_pushbullet_config_bad(hass): - """Test set up the platform with bad/missing configuration.""" - config = {notify.DOMAIN: {"platform": "pushbullet"}} - with assert_setup_component(0) as handle_config: - assert await async_setup_component(hass, notify.DOMAIN, config) - await hass.async_block_till_done() - assert not handle_config[notify.DOMAIN] - - -async def test_pushbullet_push_default(hass, requests_mock, mock_pushbullet): +async def test_pushbullet_push_default(hass, requests_mock: Mocker): """Test pushbullet push to default target.""" - config = { - notify.DOMAIN: { - "name": "test", - "platform": "pushbullet", - "api_key": "MYFAKEKEY", - } - } - with assert_setup_component(1) as handle_config: - assert await async_setup_component(hass, notify.DOMAIN, config) - await hass.async_block_till_done() - assert handle_config[notify.DOMAIN] requests_mock.register_uri( "POST", "https://api.pushbullet.com/v2/pushes", status_code=HTTPStatus.OK, json={"mock_response": "Ok"}, ) - data = {"title": "Test Title", "message": "Test Message"} - await hass.services.async_call(notify.DOMAIN, "test", data) + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_CONFIG, + ) + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + data = {"title": "Test Title", "message": "Test Message"} + await hass.services.async_call(NOTIFY_DOMAIN, "pushbullet", data) await hass.async_block_till_done() - assert requests_mock.called - assert requests_mock.call_count == 1 expected_body = {"body": "Test Message", "title": "Test Title", "type": "note"} + assert requests_mock.last_request assert requests_mock.last_request.json() == expected_body -async def test_pushbullet_push_device(hass, requests_mock, mock_pushbullet): +async def test_pushbullet_push_device(hass, requests_mock): """Test pushbullet push to default target.""" - config = { - notify.DOMAIN: { - "name": "test", - "platform": "pushbullet", - "api_key": "MYFAKEKEY", - } - } - with assert_setup_component(1) as handle_config: - assert await async_setup_component(hass, notify.DOMAIN, config) - await hass.async_block_till_done() - assert handle_config[notify.DOMAIN] requests_mock.register_uri( "POST", "https://api.pushbullet.com/v2/pushes", status_code=HTTPStatus.OK, json={"mock_response": "Ok"}, ) + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_CONFIG, + ) + entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + data = { "title": "Test Title", "message": "Test Message", "target": ["device/DESKTOP"], } - await hass.services.async_call(notify.DOMAIN, "test", data) + await hass.services.async_call(NOTIFY_DOMAIN, "pushbullet", data) await hass.async_block_till_done() - assert requests_mock.called - assert requests_mock.call_count == 1 expected_body = { "body": "Test Message", @@ -114,35 +70,29 @@ async def test_pushbullet_push_device(hass, requests_mock, mock_pushbullet): assert requests_mock.last_request.json() == expected_body -async def test_pushbullet_push_devices(hass, requests_mock, mock_pushbullet): +async def test_pushbullet_push_devices(hass, requests_mock): """Test pushbullet push to default target.""" - config = { - notify.DOMAIN: { - "name": "test", - "platform": "pushbullet", - "api_key": "MYFAKEKEY", - } - } - with assert_setup_component(1) as handle_config: - assert await async_setup_component(hass, notify.DOMAIN, config) - await hass.async_block_till_done() - assert handle_config[notify.DOMAIN] requests_mock.register_uri( "POST", "https://api.pushbullet.com/v2/pushes", status_code=HTTPStatus.OK, json={"mock_response": "Ok"}, ) + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_CONFIG, + ) + entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + data = { "title": "Test Title", "message": "Test Message", "target": ["device/DESKTOP", "device/My iPhone"], } - await hass.services.async_call(notify.DOMAIN, "test", data) + await hass.services.async_call(NOTIFY_DOMAIN, "pushbullet", data) await hass.async_block_till_done() - assert requests_mock.called - assert requests_mock.call_count == 2 - assert len(requests_mock.request_history) == 2 expected_body = { "body": "Test Message", @@ -150,45 +100,39 @@ async def test_pushbullet_push_devices(hass, requests_mock, mock_pushbullet): "title": "Test Title", "type": "note", } - assert requests_mock.request_history[0].json() == expected_body + assert requests_mock.request_history[-2].json() == expected_body expected_body = { "body": "Test Message", "device_iden": "identity2", "title": "Test Title", "type": "note", } - assert requests_mock.request_history[1].json() == expected_body + assert requests_mock.request_history[-1].json() == expected_body -async def test_pushbullet_push_email(hass, requests_mock, mock_pushbullet): +async def test_pushbullet_push_email(hass, requests_mock): """Test pushbullet push to default target.""" - config = { - notify.DOMAIN: { - "name": "test", - "platform": "pushbullet", - "api_key": "MYFAKEKEY", - } - } - with assert_setup_component(1) as handle_config: - assert await async_setup_component(hass, notify.DOMAIN, config) - await hass.async_block_till_done() - assert handle_config[notify.DOMAIN] requests_mock.register_uri( "POST", "https://api.pushbullet.com/v2/pushes", status_code=HTTPStatus.OK, json={"mock_response": "Ok"}, ) + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_CONFIG, + ) + entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + data = { "title": "Test Title", "message": "Test Message", "target": ["email/user@host.net"], } - await hass.services.async_call(notify.DOMAIN, "test", data) + await hass.services.async_call(NOTIFY_DOMAIN, "pushbullet", data) await hass.async_block_till_done() - assert requests_mock.called - assert requests_mock.call_count == 1 - assert len(requests_mock.request_history) == 1 expected_body = { "body": "Test Message", @@ -196,38 +140,30 @@ async def test_pushbullet_push_email(hass, requests_mock, mock_pushbullet): "title": "Test Title", "type": "note", } - assert requests_mock.request_history[0].json() == expected_body + assert requests_mock.last_request.json() == expected_body -async def test_pushbullet_push_mixed(hass, requests_mock, mock_pushbullet): +async def test_pushbullet_push_mixed(hass, requests_mock): """Test pushbullet push to default target.""" - config = { - notify.DOMAIN: { - "name": "test", - "platform": "pushbullet", - "api_key": "MYFAKEKEY", - } - } - with assert_setup_component(1) as handle_config: - assert await async_setup_component(hass, notify.DOMAIN, config) - await hass.async_block_till_done() - assert handle_config[notify.DOMAIN] requests_mock.register_uri( "POST", "https://api.pushbullet.com/v2/pushes", status_code=HTTPStatus.OK, json={"mock_response": "Ok"}, ) + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_CONFIG, + ) + entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(entry.entry_id) data = { "title": "Test Title", "message": "Test Message", "target": ["device/DESKTOP", "email/user@host.net"], } - await hass.services.async_call(notify.DOMAIN, "test", data) + await hass.services.async_call(NOTIFY_DOMAIN, "pushbullet", data) await hass.async_block_till_done() - assert requests_mock.called - assert requests_mock.call_count == 2 - assert len(requests_mock.request_history) == 2 expected_body = { "body": "Test Message", @@ -235,40 +171,11 @@ async def test_pushbullet_push_mixed(hass, requests_mock, mock_pushbullet): "title": "Test Title", "type": "note", } - assert requests_mock.request_history[0].json() == expected_body + assert requests_mock.request_history[-2].json() == expected_body expected_body = { "body": "Test Message", "email": "user@host.net", "title": "Test Title", "type": "note", } - assert requests_mock.request_history[1].json() == expected_body - - -async def test_pushbullet_push_no_file(hass, requests_mock, mock_pushbullet): - """Test pushbullet push to default target.""" - config = { - notify.DOMAIN: { - "name": "test", - "platform": "pushbullet", - "api_key": "MYFAKEKEY", - } - } - with assert_setup_component(1) as handle_config: - assert await async_setup_component(hass, notify.DOMAIN, config) - await hass.async_block_till_done() - assert handle_config[notify.DOMAIN] - requests_mock.register_uri( - "POST", - "https://api.pushbullet.com/v2/pushes", - status_code=HTTPStatus.OK, - json={"mock_response": "Ok"}, - ) - data = { - "title": "Test Title", - "message": "Test Message", - "target": ["device/DESKTOP", "device/My iPhone"], - "data": {"file": "not_a_file"}, - } - assert not await hass.services.async_call(notify.DOMAIN, "test", data) - await hass.async_block_till_done() + assert requests_mock.request_history[-1].json() == expected_body From 442c5ccc0634a40ae2fa75eb51218ea68e3331bf Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 2 Nov 2022 16:25:18 +0100 Subject: [PATCH 0169/1033] Use attr in mqtt fan (#81401) * Use attr in mqtt fan * Fix is_on --- homeassistant/components/mqtt/fan.py | 88 +++++++++------------------- 1 file changed, 28 insertions(+), 60 deletions(-) diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 866b429c68f..1b6c3e425a4 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -265,11 +265,8 @@ class MqttFan(MqttEntity, FanEntity): def __init__(self, hass, config, config_entry, discovery_data): """Initialize the MQTT fan.""" - self._state = None - self._percentage = None - self._preset_mode = None - self._oscillation = None - self._supported_features = 0 + self._attr_percentage = None + self._attr_preset_mode = None self._topic = None self._payload = None @@ -330,11 +327,11 @@ class MqttFan(MqttEntity, FanEntity): self._feature_percentage = CONF_PERCENTAGE_COMMAND_TOPIC in config self._feature_preset_mode = CONF_PRESET_MODE_COMMAND_TOPIC in config if self._feature_preset_mode: - self._preset_modes = config[CONF_PRESET_MODES_LIST] + self._attr_preset_modes = config[CONF_PRESET_MODES_LIST] else: - self._preset_modes = [] + self._attr_preset_modes = [] - self._speed_count = ( + self._attr_speed_count = ( min(int_states_in_range(self._speed_range), 100) if self._feature_percentage else 100 @@ -352,15 +349,15 @@ class MqttFan(MqttEntity, FanEntity): optimistic or self._topic[CONF_PRESET_MODE_STATE_TOPIC] is None ) - self._supported_features = 0 - self._supported_features |= ( + self._attr_supported_features = 0 + self._attr_supported_features |= ( self._topic[CONF_OSCILLATION_COMMAND_TOPIC] is not None and FanEntityFeature.OSCILLATE ) if self._feature_percentage: - self._supported_features |= FanEntityFeature.SET_SPEED + self._attr_supported_features |= FanEntityFeature.SET_SPEED if self._feature_preset_mode: - self._supported_features |= FanEntityFeature.PRESET_MODE + self._attr_supported_features |= FanEntityFeature.PRESET_MODE for key, tpl in self._command_templates.items(): self._command_templates[key] = MqttCommandTemplate( @@ -386,11 +383,11 @@ class MqttFan(MqttEntity, FanEntity): _LOGGER.debug("Ignoring empty state from '%s'", msg.topic) return if payload == self._payload["STATE_ON"]: - self._state = True + self._attr_is_on = True elif payload == self._payload["STATE_OFF"]: - self._state = False + self._attr_is_on = False elif payload == PAYLOAD_NONE: - self._state = None + self._attr_is_on = None get_mqtt_data(self.hass).state_write_requests.write_state_request(self) if self._topic[CONF_STATE_TOPIC] is not None: @@ -412,7 +409,7 @@ class MqttFan(MqttEntity, FanEntity): _LOGGER.debug("Ignoring empty speed from '%s'", msg.topic) return if rendered_percentage_payload == self._payload["PERCENTAGE_RESET"]: - self._percentage = None + self._attr_percentage = None get_mqtt_data(self.hass).state_write_requests.write_state_request(self) return try: @@ -435,7 +432,7 @@ class MqttFan(MqttEntity, FanEntity): rendered_percentage_payload, ) return - self._percentage = percentage + self._attr_percentage = percentage get_mqtt_data(self.hass).state_write_requests.write_state_request(self) if self._topic[CONF_PERCENTAGE_STATE_TOPIC] is not None: @@ -445,7 +442,7 @@ class MqttFan(MqttEntity, FanEntity): "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, } - self._percentage = None + self._attr_percentage = None @callback @log_messages(self.hass, self.entity_id) @@ -453,7 +450,7 @@ class MqttFan(MqttEntity, FanEntity): """Handle new received MQTT message for preset mode.""" preset_mode = self._value_templates[ATTR_PRESET_MODE](msg.payload) if preset_mode == self._payload["PRESET_MODE_RESET"]: - self._preset_mode = None + self._attr_preset_mode = None self.async_write_ha_state() return if not preset_mode: @@ -468,7 +465,7 @@ class MqttFan(MqttEntity, FanEntity): ) return - self._preset_mode = preset_mode + self._attr_preset_mode = preset_mode get_mqtt_data(self.hass).state_write_requests.write_state_request(self) if self._topic[CONF_PRESET_MODE_STATE_TOPIC] is not None: @@ -478,7 +475,7 @@ class MqttFan(MqttEntity, FanEntity): "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, } - self._preset_mode = None + self._attr_preset_mode = None @callback @log_messages(self.hass, self.entity_id) @@ -489,9 +486,9 @@ class MqttFan(MqttEntity, FanEntity): _LOGGER.debug("Ignoring empty oscillation from '%s'", msg.topic) return if payload == self._payload["OSCILLATE_ON_PAYLOAD"]: - self._oscillation = True + self._attr_oscillating = True elif payload == self._payload["OSCILLATE_OFF_PAYLOAD"]: - self._oscillation = False + self._attr_oscillating = False get_mqtt_data(self.hass).state_write_requests.write_state_request(self) if self._topic[CONF_OSCILLATION_STATE_TOPIC] is not None: @@ -501,7 +498,7 @@ class MqttFan(MqttEntity, FanEntity): "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, } - self._oscillation = False + self._attr_oscillating = False self._sub_state = subscription.async_prepare_subscribe_topics( self.hass, self._sub_state, topics @@ -519,37 +516,8 @@ class MqttFan(MqttEntity, FanEntity): @property def is_on(self) -> bool | None: """Return true if device is on.""" - return self._state - - @property - def percentage(self) -> int | None: - """Return the current percentage.""" - return self._percentage - - @property - def preset_mode(self) -> str | None: - """Return the current preset _mode.""" - return self._preset_mode - - @property - def preset_modes(self) -> list[str]: - """Get the list of available preset modes.""" - return self._preset_modes - - @property - def supported_features(self) -> int: - """Flag supported features.""" - return self._supported_features - - @property - def speed_count(self) -> int: - """Return the number of speeds the fan supports.""" - return self._speed_count - - @property - def oscillating(self) -> bool | None: - """Return the oscillation state.""" - return self._oscillation + # The default for FanEntity is to compute it based on percentage + return self._attr_is_on # The speed attribute deprecated in the schema, support will be removed after a quarter (2021.7) async def async_turn_on( @@ -575,7 +543,7 @@ class MqttFan(MqttEntity, FanEntity): if preset_mode: await self.async_set_preset_mode(preset_mode) if self._optimistic: - self._state = True + self._attr_is_on = True self.async_write_ha_state() async def async_turn_off(self, **kwargs: Any) -> None: @@ -592,7 +560,7 @@ class MqttFan(MqttEntity, FanEntity): self._config[CONF_ENCODING], ) if self._optimistic: - self._state = False + self._attr_is_on = False self.async_write_ha_state() async def async_set_percentage(self, percentage: int) -> None: @@ -613,7 +581,7 @@ class MqttFan(MqttEntity, FanEntity): ) if self._optimistic_percentage: - self._percentage = percentage + self._attr_percentage = percentage self.async_write_ha_state() async def async_set_preset_mode(self, preset_mode: str) -> None: @@ -634,7 +602,7 @@ class MqttFan(MqttEntity, FanEntity): ) if self._optimistic_preset_mode: - self._preset_mode = preset_mode + self._attr_preset_mode = preset_mode self.async_write_ha_state() async def async_oscillate(self, oscillating: bool) -> None: @@ -660,5 +628,5 @@ class MqttFan(MqttEntity, FanEntity): ) if self._optimistic_oscillation: - self._oscillation = oscillating + self._attr_oscillating = oscillating self.async_write_ha_state() From 93d74cafdc1f6c34b164b6674e03cd98eda427f7 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Wed, 2 Nov 2022 17:52:36 +0100 Subject: [PATCH 0170/1033] Fix late review comments for Scrape (#81259) * Review comments from #74325 * Remove moved test * Fix init * Handle no data * Remove print * Fix tests * PlatformNotReady if no data * Recover failed platform setup * Fix broken test * patch context * reset test init * Move to platform * asyncio gather * Remove duplicate code --- homeassistant/components/scrape/__init__.py | 36 ++++++------- homeassistant/components/scrape/sensor.py | 55 ++++++++++---------- tests/components/scrape/test_init.py | 57 ++++++++++++++++++--- tests/components/scrape/test_sensor.py | 38 ++++++-------- 4 files changed, 113 insertions(+), 73 deletions(-) diff --git a/homeassistant/components/scrape/__init__.py b/homeassistant/components/scrape/__init__.py index 9b6fb9210f3..25a734dbd24 100644 --- a/homeassistant/components/scrape/__init__.py +++ b/homeassistant/components/scrape/__init__.py @@ -1,8 +1,10 @@ """The scrape component.""" from __future__ import annotations +import asyncio +from collections.abc import Coroutine from datetime import timedelta -import logging +from typing import Any import voluptuous as vol @@ -15,7 +17,6 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant -from homeassistant.exceptions import PlatformNotReady from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv from homeassistant.helpers.template_entity import TEMPLATE_SENSOR_BASE_SCHEMA @@ -24,9 +25,6 @@ from homeassistant.helpers.typing import ConfigType from .const import CONF_INDEX, CONF_SELECT, DEFAULT_SCAN_INTERVAL, DOMAIN from .coordinator import ScrapeCoordinator -_LOGGER = logging.getLogger(__name__) - - SENSOR_SCHEMA = vol.Schema( { **TEMPLATE_SENSOR_BASE_SCHEMA.schema, @@ -55,13 +53,12 @@ CONFIG_SCHEMA = vol.Schema( async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up Scrape from yaml config.""" + scrape_config: list[ConfigType] | None if not (scrape_config := config.get(DOMAIN)): return True + load_coroutines: list[Coroutine[Any, Any, None]] = [] for resource_config in scrape_config: - if not (sensors := resource_config.get(SENSOR_DOMAIN)): - raise PlatformNotReady("No sensors configured") - rest = create_rest_data_from_config(hass, resource_config) coordinator = ScrapeCoordinator( hass, @@ -70,17 +67,20 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: seconds=resource_config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) ), ) - await coordinator.async_refresh() - if coordinator.data is None: - raise PlatformNotReady - for sensor_config in sensors: - discovery.load_platform( - hass, - Platform.SENSOR, - DOMAIN, - {"coordinator": coordinator, "config": sensor_config}, - config, + sensors: list[ConfigType] = resource_config.get(SENSOR_DOMAIN, []) + if sensors: + load_coroutines.append( + discovery.async_load_platform( + hass, + Platform.SENSOR, + DOMAIN, + {"coordinator": coordinator, "configs": sensors}, + config, + ) ) + if load_coroutines: + await asyncio.gather(*load_coroutines) + return True diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index 176b556e189..e911ade5d72 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -83,6 +83,7 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Web scrape sensor.""" + entities: list[ScrapeSensor] = [] if discovery_info is None: async_create_issue( hass, @@ -97,45 +98,47 @@ async def async_setup_platform( rest = create_rest_data_from_config(hass, resource_config) coordinator = ScrapeCoordinator(hass, rest, SCAN_INTERVAL) - await coordinator.async_refresh() - if coordinator.data is None: - raise PlatformNotReady - sensor_config = config - template_config = vol.Schema( - TEMPLATE_SENSOR_BASE_SCHEMA.schema, extra=vol.REMOVE_EXTRA - )(sensor_config) + sensors_config: list[tuple[ConfigType, ConfigType]] = [ + ( + config, + vol.Schema(TEMPLATE_SENSOR_BASE_SCHEMA.schema, extra=vol.REMOVE_EXTRA)( + config + ), + ) + ] else: coordinator = discovery_info["coordinator"] - sensor_config = discovery_info["config"] - template_config = sensor_config + sensors_config = [ + (sensor_config, sensor_config) + for sensor_config in discovery_info["configs"] + ] - name: str = template_config[CONF_NAME] - unique_id: str | None = template_config.get(CONF_UNIQUE_ID) - select: str | None = sensor_config.get(CONF_SELECT) - attr: str | None = sensor_config.get(CONF_ATTRIBUTE) - index: int = sensor_config[CONF_INDEX] - value_template: Template | None = sensor_config.get(CONF_VALUE_TEMPLATE) + await coordinator.async_refresh() + if coordinator.data is None: + raise PlatformNotReady - if value_template is not None: - value_template.hass = hass + for sensor_config, template_config in sensors_config: + value_template: Template | None = sensor_config.get(CONF_VALUE_TEMPLATE) + if value_template is not None: + value_template.hass = hass - async_add_entities( - [ + entities.append( ScrapeSensor( hass, coordinator, template_config, - name, - unique_id, - select, - attr, - index, + template_config[CONF_NAME], + template_config.get(CONF_UNIQUE_ID), + sensor_config.get(CONF_SELECT), + sensor_config.get(CONF_ATTRIBUTE), + sensor_config[CONF_INDEX], value_template, ) - ], - ) + ) + + async_add_entities(entities) class ScrapeSensor(CoordinatorEntity[ScrapeCoordinator], TemplateSensor): diff --git a/tests/components/scrape/test_init.py b/tests/components/scrape/test_init.py index 22c07a2acaf..e66b9a65fd4 100644 --- a/tests/components/scrape/test_init.py +++ b/tests/components/scrape/test_init.py @@ -1,15 +1,21 @@ """Test Scrape component setup process.""" from __future__ import annotations +from datetime import datetime from unittest.mock import patch +import pytest + from homeassistant.components.scrape.const import DOMAIN +from homeassistant.components.scrape.sensor import SCAN_INTERVAL from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component from . import MockRestData, return_integration_config +from tests.common import async_fire_time_changed + async def test_setup_config(hass: HomeAssistant) -> None: """Test setup from yaml.""" @@ -35,8 +41,10 @@ async def test_setup_config(hass: HomeAssistant) -> None: assert len(mock_setup.mock_calls) == 1 -async def test_setup_no_data_fails(hass: HomeAssistant) -> None: - """Test setup entry no data fails.""" +async def test_setup_no_data_fails_with_recovery( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test setup entry no data fails and recovers.""" config = { DOMAIN: [ return_integration_config( @@ -45,15 +53,25 @@ async def test_setup_no_data_fails(hass: HomeAssistant) -> None: ] } + mocker = MockRestData("test_scrape_sensor_no_data") with patch( - "homeassistant.components.scrape.coordinator.RestData", - return_value=MockRestData("test_scrape_sensor_no_data"), + "homeassistant.components.rest.RestData", + return_value=mocker, ): - assert not await async_setup_component(hass, DOMAIN, config) + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.ha_version") + assert state is None + + assert "Platform scrape not ready yet" in caplog.text + + mocker.payload = "test_scrape_sensor" + async_fire_time_changed(hass, datetime.utcnow() + SCAN_INTERVAL) await hass.async_block_till_done() state = hass.states.get("sensor.ha_version") - assert state is None + assert state.state == "Current Version: 2021.12.10" async def test_setup_config_no_configuration(hass: HomeAssistant) -> None: @@ -65,3 +83,30 @@ async def test_setup_config_no_configuration(hass: HomeAssistant) -> None: entities = er.async_get(hass) assert entities.entities == {} + + +async def test_setup_config_no_sensors( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test setup from yaml with no configured sensors finalize properly.""" + config = { + DOMAIN: [ + { + "resource": "https://www.address.com", + "verify_ssl": True, + }, + { + "resource": "https://www.address2.com", + "verify_ssl": True, + "sensor": None, + }, + ] + } + + mocker = MockRestData("test_scrape_sensor") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() diff --git a/tests/components/scrape/test_sensor.py b/tests/components/scrape/test_sensor.py index 24e583f5a87..2d0c7b4c61d 100644 --- a/tests/components/scrape/test_sensor.py +++ b/tests/components/scrape/test_sensor.py @@ -4,6 +4,8 @@ from __future__ import annotations from datetime import datetime from unittest.mock import patch +import pytest + from homeassistant.components.scrape.sensor import SCAN_INTERVAL from homeassistant.components.sensor import ( CONF_STATE_CLASS, @@ -67,6 +69,7 @@ async def test_scrape_sensor_platform_yaml(hass: HomeAssistant) -> None: name="Auth page2", username="user@secret.com", password="12345678", + template="{{value}}", ), ] } @@ -248,7 +251,9 @@ async def test_scrape_sensor_authentication(hass: HomeAssistant) -> None: assert state2.state == "secret text" -async def test_scrape_sensor_no_data(hass: HomeAssistant) -> None: +async def test_scrape_sensor_no_data( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: """Test Scrape sensor fails on no data.""" config = { DOMAIN: return_integration_config( @@ -261,12 +266,14 @@ async def test_scrape_sensor_no_data(hass: HomeAssistant) -> None: "homeassistant.components.rest.RestData", return_value=mocker, ): - assert not await async_setup_component(hass, DOMAIN, config) + assert await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() state = hass.states.get("sensor.ha_version") assert state is None + assert "Platform scrape not ready yet" in caplog.text + async def test_scrape_sensor_no_data_refresh(hass: HomeAssistant) -> None: """Test Scrape sensor no data on refresh.""" @@ -286,13 +293,13 @@ async def test_scrape_sensor_no_data_refresh(hass: HomeAssistant) -> None: assert await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() - state = hass.states.get("sensor.ha_version") - assert state - assert state.state == "Current Version: 2021.12.10" + state = hass.states.get("sensor.ha_version") + assert state + assert state.state == "Current Version: 2021.12.10" - mocker.payload = "test_scrape_sensor_no_data" - async_fire_time_changed(hass, datetime.utcnow() + SCAN_INTERVAL) - await hass.async_block_till_done() + mocker.payload = "test_scrape_sensor_no_data" + async_fire_time_changed(hass, datetime.utcnow() + SCAN_INTERVAL) + await hass.async_block_till_done() state = hass.states.get("sensor.ha_version") assert state is not None @@ -398,18 +405,3 @@ async def test_scrape_sensor_unique_id(hass: HomeAssistant) -> None: entity = entity_reg.async_get("sensor.ha_version") assert entity.unique_id == "ha_version_unique_id" - - -async def test_scrape_sensor_not_configured_sensor(hass: HomeAssistant, caplog) -> None: - """Test Scrape sensor with missing configured sensors.""" - config = {DOMAIN: [return_integration_config(sensors=None)]} - - mocker = MockRestData("test_scrape_sensor") - with patch( - "homeassistant.components.rest.RestData", - return_value=mocker, - ): - assert not await async_setup_component(hass, DOMAIN, config) - await hass.async_block_till_done() - - assert "No sensors configured" in caplog.text From 7af0f16f79e1155723f095171b8796b227a9ebf1 Mon Sep 17 00:00:00 2001 From: Poltorak Serguei Date: Wed, 2 Nov 2022 19:53:10 +0300 Subject: [PATCH 0171/1033] Rework Z-Wave.Me to group entities of one physical devices (#78553) Co-authored-by: Martin Hjelmare Co-authored-by: Dmitry Vlasov --- homeassistant/components/zwave_me/__init__.py | 35 ++++++--- .../components/zwave_me/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../zwave_me/test_remove_stale_devices.py | 74 +++++++++++++++++++ 5 files changed, 101 insertions(+), 14 deletions(-) create mode 100644 tests/components/zwave_me/test_remove_stale_devices.py diff --git a/homeassistant/components/zwave_me/__init__.py b/homeassistant/components/zwave_me/__init__.py index 10312f36dfc..ed3d538d052 100644 --- a/homeassistant/components/zwave_me/__init__.py +++ b/homeassistant/components/zwave_me/__init__.py @@ -8,6 +8,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_TOKEN, CONF_URL from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers import device_registry +from homeassistant.helpers.device_registry import DeviceRegistry from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send from homeassistant.helpers.entity import DeviceInfo, Entity @@ -23,6 +25,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: controller = hass.data[DOMAIN][entry.entry_id] = ZWaveMeController(hass, entry) if await controller.async_establish_connection(): hass.async_create_task(async_setup_platforms(hass, entry, controller)) + registry = device_registry.async_get(hass) + controller.remove_stale_devices(registry) return True raise ConfigEntryNotReady() @@ -62,24 +66,33 @@ class ZWaveMeController: def add_device(self, device: ZWaveMeData) -> None: """Send signal to create device.""" - if device.deviceType in ZWAVE_ME_PLATFORMS and self.platforms_inited: - if device.id in self.device_ids: - dispatcher_send(self._hass, f"ZWAVE_ME_INFO_{device.id}", device) - else: - dispatcher_send( - self._hass, f"ZWAVE_ME_NEW_{device.deviceType.upper()}", device - ) - self.device_ids.add(device.id) + if device.id in self.device_ids: + dispatcher_send(self._hass, f"ZWAVE_ME_INFO_{device.id}", device) + else: + dispatcher_send( + self._hass, f"ZWAVE_ME_NEW_{device.deviceType.upper()}", device + ) + self.device_ids.add(device.id) - def on_device_create(self, devices: list) -> None: + def on_device_create(self, devices: list[ZWaveMeData]) -> None: """Create multiple devices.""" for device in devices: - self.add_device(device) + if device.deviceType in ZWAVE_ME_PLATFORMS and self.platforms_inited: + self.add_device(device) def on_device_update(self, new_info: ZWaveMeData) -> None: """Send signal to update device.""" dispatcher_send(self._hass, f"ZWAVE_ME_INFO_{new_info.id}", new_info) + def remove_stale_devices(self, registry: DeviceRegistry): + """Remove old-format devices in the registry.""" + for device_id in self.device_ids: + device = registry.async_get_device( + {(DOMAIN, f"{self.config.unique_id}-{device_id}")} + ) + if device is not None: + registry.async_remove_device(device.id) + async def async_setup_platforms( hass: HomeAssistant, entry: ConfigEntry, controller: ZWaveMeController @@ -113,7 +126,7 @@ class ZWaveMeEntity(Entity): def device_info(self) -> DeviceInfo: """Return device specific attributes.""" return DeviceInfo( - identifiers={(DOMAIN, self._attr_unique_id)}, + identifiers={(DOMAIN, self.device.deviceIdentifier)}, name=self._attr_name, manufacturer=self.device.manufacturer, sw_version=self.device.firmware, diff --git a/homeassistant/components/zwave_me/manifest.json b/homeassistant/components/zwave_me/manifest.json index 4ca933f43bc..9aeeb7b2a40 100644 --- a/homeassistant/components/zwave_me/manifest.json +++ b/homeassistant/components/zwave_me/manifest.json @@ -3,7 +3,7 @@ "name": "Z-Wave.Me", "documentation": "https://www.home-assistant.io/integrations/zwave_me", "iot_class": "local_push", - "requirements": ["zwave_me_ws==0.2.6", "url-normalize==1.4.3"], + "requirements": ["zwave_me_ws==0.3.0", "url-normalize==1.4.3"], "after_dependencies": ["zeroconf"], "zeroconf": [{ "type": "_hap._tcp.local.", "name": "*z.wave-me*" }], "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index dd54335dd71..29e9d79aa0d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2640,4 +2640,4 @@ zm-py==0.5.2 zwave-js-server-python==0.43.0 # homeassistant.components.zwave_me -zwave_me_ws==0.2.6 +zwave_me_ws==0.3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3ec5e21e4f6..691b19b8b12 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1832,4 +1832,4 @@ zigpy==0.51.5 zwave-js-server-python==0.43.0 # homeassistant.components.zwave_me -zwave_me_ws==0.2.6 +zwave_me_ws==0.3.0 diff --git a/tests/components/zwave_me/test_remove_stale_devices.py b/tests/components/zwave_me/test_remove_stale_devices.py new file mode 100644 index 00000000000..484c38b9f33 --- /dev/null +++ b/tests/components/zwave_me/test_remove_stale_devices.py @@ -0,0 +1,74 @@ +"""Test the zwave_me removal of stale devices.""" +from unittest.mock import patch +import uuid + +import pytest as pytest +from zwave_me_ws import ZWaveMeData + +from homeassistant.components.zwave_me import ZWaveMePlatform +from homeassistant.const import CONF_TOKEN, CONF_URL +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry, mock_device_registry + +DEFAULT_DEVICE_INFO = ZWaveMeData( + id="DummyDevice", + deviceType=ZWaveMePlatform.BINARY_SENSOR, + title="DeviceDevice", + level=100, + deviceIdentifier="16-23", +) + + +@pytest.fixture +def device_reg(hass): + """Return an empty, loaded, registry.""" + return mock_device_registry(hass) + + +async def mock_connection(controller): + """Mock established connection and setting identifiers.""" + controller.on_new_device(DEFAULT_DEVICE_INFO) + return True + + +@pytest.mark.parametrize( + "identifier,should_exist", + [ + (DEFAULT_DEVICE_INFO.id, False), + (DEFAULT_DEVICE_INFO.deviceIdentifier, True), + ], +) +async def test_remove_stale_devices( + hass: HomeAssistant, device_reg, identifier, should_exist +): + """Test removing devices with old-format ids.""" + + config_entry = MockConfigEntry( + unique_id=uuid.uuid4(), + domain="zwave_me", + data={CONF_TOKEN: "test_token", CONF_URL: "http://test_test"}, + ) + config_entry.add_to_hass(hass) + device_reg.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections={("mac", "12:34:56:AB:CD:EF")}, + identifiers={("zwave_me", f"{config_entry.unique_id}-{identifier}")}, + ) + with patch("zwave_me_ws.ZWaveMe.get_connection", mock_connection,), patch( + "homeassistant.components.zwave_me.async_setup_platforms", + ): + await hass.config_entries.async_setup(config_entry.entry_id) + assert ( + bool( + device_reg.async_get_device( + { + ( + "zwave_me", + f"{config_entry.unique_id}-{identifier}", + ) + } + ) + ) + == should_exist + ) From e1be63f26c6d24adb152196498fbbf0cfc948a7d Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 2 Nov 2022 09:57:23 -0700 Subject: [PATCH 0172/1033] Bump gcal_sync to 2.2.3 (#81414) --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index 9a184bdd636..f6ebc665cd7 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==2.2.2", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==2.2.3", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index 29e9d79aa0d..1712fa513e6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -728,7 +728,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==2.2.2 +gcal-sync==2.2.3 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 691b19b8b12..a7e33c24d90 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -544,7 +544,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==2.2.2 +gcal-sync==2.2.3 # homeassistant.components.geocaching geocachingapi==0.2.1 From 466365c8de4b4b519b89bcbc8efc67778d495d1f Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 2 Nov 2022 18:07:51 +0100 Subject: [PATCH 0173/1033] Fix Renault charging power sensor (#81412) --- homeassistant/components/renault/sensor.py | 12 ++++++++---- tests/components/renault/const.py | 13 ++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/renault/sensor.py b/homeassistant/components/renault/sensor.py index 3076dfc9f10..436b9209e89 100644 --- a/homeassistant/components/renault/sensor.py +++ b/homeassistant/components/renault/sensor.py @@ -23,7 +23,6 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, ENERGY_KILO_WATT_HOUR, LENGTH_KILOMETERS, PERCENTAGE, @@ -189,17 +188,22 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( state_class=SensorStateClass.MEASUREMENT, ), RenaultSensorEntityDescription( + # For vehicles that DO NOT report charging power in watts, this seems to + # correspond to the maximum power that would be admissible by the car based + # on the battery state, regardless of the type of charger. key="charging_power", condition_lambda=lambda a: not a.details.reports_charging_power_in_watts(), coordinator="battery", data_key="chargingInstantaneousPower", - device_class=SensorDeviceClass.CURRENT, + device_class=SensorDeviceClass.POWER, entity_class=RenaultSensor[KamereonVehicleBatteryStatusData], - name="Charging power", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + name="Admissible charging power", + native_unit_of_measurement=POWER_KILO_WATT, state_class=SensorStateClass.MEASUREMENT, ), RenaultSensorEntityDescription( + # For vehicles that DO report charging power in watts, this is the power + # effectively being transferred to the car. key="charging_power", condition_lambda=lambda a: a.details.reports_charging_power_in_watts(), coordinator="battery", diff --git a/tests/components/renault/const.py b/tests/components/renault/const.py index 785e27e1ea6..97499d19bea 100644 --- a/tests/components/renault/const.py +++ b/tests/components/renault/const.py @@ -27,7 +27,6 @@ from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, CONF_PASSWORD, CONF_USERNAME, - ELECTRIC_CURRENT_AMPERE, ENERGY_KILO_WATT_HOUR, LENGTH_KILOMETERS, PERCENTAGE, @@ -398,12 +397,12 @@ MOCK_VEHICLES = { ATTR_UNIQUE_ID: "vf1aaaaa555777999_charge_state", }, { - ATTR_DEVICE_CLASS: SensorDeviceClass.CURRENT, - ATTR_ENTITY_ID: "sensor.reg_number_charging_power", + ATTR_DEVICE_CLASS: SensorDeviceClass.POWER, + ATTR_ENTITY_ID: "sensor.reg_number_admissible_charging_power", ATTR_STATE: STATE_UNKNOWN, ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, ATTR_UNIQUE_ID: "vf1aaaaa555777999_charging_power", - ATTR_UNIT_OF_MEASUREMENT: ELECTRIC_CURRENT_AMPERE, + ATTR_UNIT_OF_MEASUREMENT: POWER_KILO_WATT, }, { ATTR_ENTITY_ID: "sensor.reg_number_charging_remaining_time", @@ -617,12 +616,12 @@ MOCK_VEHICLES = { ATTR_UNIQUE_ID: "vf1aaaaa555777123_charge_state", }, { - ATTR_DEVICE_CLASS: SensorDeviceClass.CURRENT, - ATTR_ENTITY_ID: "sensor.reg_number_charging_power", + ATTR_DEVICE_CLASS: SensorDeviceClass.POWER, + ATTR_ENTITY_ID: "sensor.reg_number_admissible_charging_power", ATTR_STATE: "27.0", ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, ATTR_UNIQUE_ID: "vf1aaaaa555777123_charging_power", - ATTR_UNIT_OF_MEASUREMENT: ELECTRIC_CURRENT_AMPERE, + ATTR_UNIT_OF_MEASUREMENT: POWER_KILO_WATT, }, { ATTR_ENTITY_ID: "sensor.reg_number_charging_remaining_time", From d385a85ccbddaab9c4ba3754812b110f08b88b70 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 2 Nov 2022 18:46:03 +0100 Subject: [PATCH 0174/1033] Cleanup schema validation in scrape sensor (#81419) --- homeassistant/components/scrape/sensor.py | 26 ++++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index e911ade5d72..b606f00aaa7 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -83,7 +83,8 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Web scrape sensor.""" - entities: list[ScrapeSensor] = [] + coordinator: ScrapeCoordinator + sensors_config: list[ConfigType] if discovery_info is None: async_create_issue( hass, @@ -99,27 +100,22 @@ async def async_setup_platform( coordinator = ScrapeCoordinator(hass, rest, SCAN_INTERVAL) - sensors_config: list[tuple[ConfigType, ConfigType]] = [ - ( - config, - vol.Schema(TEMPLATE_SENSOR_BASE_SCHEMA.schema, extra=vol.REMOVE_EXTRA)( - config - ), + sensors_config = [ + vol.Schema(TEMPLATE_SENSOR_BASE_SCHEMA.schema, extra=vol.ALLOW_EXTRA)( + config ) ] else: coordinator = discovery_info["coordinator"] - sensors_config = [ - (sensor_config, sensor_config) - for sensor_config in discovery_info["configs"] - ] + sensors_config = discovery_info["configs"] await coordinator.async_refresh() if coordinator.data is None: raise PlatformNotReady - for sensor_config, template_config in sensors_config: + entities: list[ScrapeSensor] = [] + for sensor_config in sensors_config: value_template: Template | None = sensor_config.get(CONF_VALUE_TEMPLATE) if value_template is not None: value_template.hass = hass @@ -128,9 +124,9 @@ async def async_setup_platform( ScrapeSensor( hass, coordinator, - template_config, - template_config[CONF_NAME], - template_config.get(CONF_UNIQUE_ID), + sensor_config, + sensor_config[CONF_NAME], + sensor_config.get(CONF_UNIQUE_ID), sensor_config.get(CONF_SELECT), sensor_config.get(CONF_ATTRIBUTE), sensor_config[CONF_INDEX], From 9afabc17ae112c37ea1e0a56953c03ec3bd40460 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 2 Nov 2022 19:50:11 +0100 Subject: [PATCH 0175/1033] Use attr in mqtt sensor (#81402) --- homeassistant/components/mqtt/sensor.py | 37 +++++++------------------ 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index 52ba1a7e3c2..69077d30eee 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -1,7 +1,7 @@ """Support for MQTT sensors.""" from __future__ import annotations -from datetime import datetime, timedelta +from datetime import timedelta import functools import logging @@ -30,7 +30,7 @@ from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_track_point_in_utc_time -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util import dt as dt_util from . import subscription @@ -171,7 +171,6 @@ class MqttSensor(MqttEntity, RestoreSensor): def __init__(self, hass, config, config_entry, discovery_data): """Initialize the sensor.""" - self._state = None self._expiration_trigger = None expire_after = config.get(CONF_EXPIRE_AFTER) @@ -201,7 +200,7 @@ class MqttSensor(MqttEntity, RestoreSensor): _LOGGER.debug("Skip state recovery after reload for %s", self.entity_id) return self._expired = False - self._state = last_sensor_data.native_value + self._attr_native_value = last_sensor_data.native_value self._expiration_trigger = async_track_point_in_utc_time( self.hass, self._value_is_expired, expiration_at @@ -227,9 +226,13 @@ class MqttSensor(MqttEntity, RestoreSensor): """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" + self._attr_device_class = config.get(CONF_DEVICE_CLASS) self._attr_force_update = config[CONF_FORCE_UPDATE] + self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT) + self._attr_state_class = config.get(CONF_STATE_CLASS) + self._template = MqttValueTemplate( self._config.get(CONF_VALUE_TEMPLATE), entity=self ).async_render_with_possible_json_value @@ -259,7 +262,7 @@ class MqttSensor(MqttEntity, RestoreSensor): self.hass, self._value_is_expired, expiration_at ) - payload = self._template(msg.payload, default=self._state) + payload = self._template(msg.payload, default=self.native_value) if payload is not None and self.device_class in ( SensorDeviceClass.DATE, @@ -272,7 +275,7 @@ class MqttSensor(MqttEntity, RestoreSensor): elif self.device_class == SensorDeviceClass.DATE: payload = payload.date() - self._state = payload + self._attr_native_value = payload def _update_last_reset(msg): payload = self._last_reset_template(msg.payload) @@ -342,26 +345,6 @@ class MqttSensor(MqttEntity, RestoreSensor): self._expired = True self.async_write_ha_state() - @property - def native_unit_of_measurement(self) -> str | None: - """Return the unit this state is expressed in.""" - return self._config.get(CONF_UNIT_OF_MEASUREMENT) - - @property - def native_value(self) -> StateType | datetime: - """Return the state of the entity.""" - return self._state - - @property - def device_class(self) -> str | None: - """Return the device class of the sensor.""" - return self._config.get(CONF_DEVICE_CLASS) - - @property - def state_class(self) -> str | None: - """Return the state class of the sensor.""" - return self._config.get(CONF_STATE_CLASS) - @property def available(self) -> bool: """Return true if the device is available and value has not expired.""" From 76819d81bec95e7776a34b420e8e457748690dcc Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 2 Nov 2022 20:25:31 +0100 Subject: [PATCH 0176/1033] Update frontend to 20221102.1 (#81422) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index be97ceee522..f4f46a1f89b 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20221102.0"], + "requirements": ["home-assistant-frontend==20221102.1"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 5b5ee9c0e7b..626e88420ea 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -21,7 +21,7 @@ dbus-fast==1.61.1 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 -home-assistant-frontend==20221102.0 +home-assistant-frontend==20221102.1 httpx==0.23.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index 1712fa513e6..a3a177edef5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -871,7 +871,7 @@ hole==0.7.0 holidays==0.16 # homeassistant.components.frontend -home-assistant-frontend==20221102.0 +home-assistant-frontend==20221102.1 # homeassistant.components.home_connect homeconnect==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a7e33c24d90..c8ceeb06fbd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -651,7 +651,7 @@ hole==0.7.0 holidays==0.16 # homeassistant.components.frontend -home-assistant-frontend==20221102.0 +home-assistant-frontend==20221102.1 # homeassistant.components.home_connect homeconnect==0.7.2 From b4ad03784f1d02995da39f3094c80adb4a60492b Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 2 Nov 2022 20:33:18 +0100 Subject: [PATCH 0177/1033] Improve MQTT type hints part 1 (#80523) * Improve typing alarm_control_panel * Improve typing binary_sensor * Improve typing button * Add misssed annotation * Move CONF_EXPIRE_AFTER to _setup_from_config * Use CALL_BACK type * Remove assert, improve code style --- .../components/mqtt/alarm_control_panel.py | 68 +++++++++++-------- .../components/mqtt/binary_sensor.py | 59 +++++++++------- homeassistant/components/mqtt/button.py | 20 ++++-- 3 files changed, 86 insertions(+), 61 deletions(-) diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index ed1990d919e..8a15c2587f1 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -48,7 +48,7 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttCommandTemplate, MqttValueTemplate +from .models import MqttCommandTemplate, MqttValueTemplate, ReceiveMessage from .util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic _LOGGER = logging.getLogger(__name__) @@ -155,8 +155,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT Alarm Control Panel platform.""" async_add_entities([MqttAlarm(hass, config, config_entry, discovery_data)]) @@ -168,32 +168,39 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): _entity_id_format = alarm.ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_ALARM_ATTRIBUTES_BLOCKED - def __init__(self, hass, config, config_entry, discovery_data): + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Init the MQTT Alarm Control Panel.""" self._state: str | None = None MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: + """(Re)Setup the entity.""" self._value_template = MqttValueTemplate( - self._config.get(CONF_VALUE_TEMPLATE), + config.get(CONF_VALUE_TEMPLATE), entity=self, ).async_render_with_possible_json_value self._command_template = MqttCommandTemplate( - self._config[CONF_COMMAND_TEMPLATE], entity=self + config[CONF_COMMAND_TEMPLATE], entity=self ).async_render - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" @callback @log_messages(self.hass, self.entity_id) - def message_received(msg): + def message_received(msg: ReceiveMessage) -> None: """Run when new MQTT message has been received.""" payload = self._value_template(msg.payload) if payload not in ( @@ -210,7 +217,7 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): ): _LOGGER.warning("Received unexpected payload: %s", msg.payload) return - self._state = payload + self._state = str(payload) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) self._sub_state = subscription.async_prepare_subscribe_topics( @@ -226,7 +233,7 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): }, ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) @@ -250,6 +257,7 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): @property def code_format(self) -> alarm.CodeFormat | None: """Return one or more digits/characters.""" + code: str | None if (code := self._config.get(CONF_CODE)) is None: return None if code == REMOTE_CODE or (isinstance(code, str) and re.search("^\\d+$", code)): @@ -266,10 +274,10 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): This method is a coroutine. """ - code_required = self._config[CONF_CODE_DISARM_REQUIRED] + code_required: bool = self._config[CONF_CODE_DISARM_REQUIRED] if code_required and not self._validate_code(code, "disarming"): return - payload = self._config[CONF_PAYLOAD_DISARM] + payload: str = self._config[CONF_PAYLOAD_DISARM] await self._publish(code, payload) async def async_alarm_arm_home(self, code: str | None = None) -> None: @@ -277,10 +285,10 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): This method is a coroutine. """ - code_required = self._config[CONF_CODE_ARM_REQUIRED] + code_required: bool = self._config[CONF_CODE_ARM_REQUIRED] if code_required and not self._validate_code(code, "arming home"): return - action = self._config[CONF_PAYLOAD_ARM_HOME] + action: str = self._config[CONF_PAYLOAD_ARM_HOME] await self._publish(code, action) async def async_alarm_arm_away(self, code: str | None = None) -> None: @@ -288,10 +296,10 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): This method is a coroutine. """ - code_required = self._config[CONF_CODE_ARM_REQUIRED] + code_required: bool = self._config[CONF_CODE_ARM_REQUIRED] if code_required and not self._validate_code(code, "arming away"): return - action = self._config[CONF_PAYLOAD_ARM_AWAY] + action: str = self._config[CONF_PAYLOAD_ARM_AWAY] await self._publish(code, action) async def async_alarm_arm_night(self, code: str | None = None) -> None: @@ -299,10 +307,10 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): This method is a coroutine. """ - code_required = self._config[CONF_CODE_ARM_REQUIRED] + code_required: bool = self._config[CONF_CODE_ARM_REQUIRED] if code_required and not self._validate_code(code, "arming night"): return - action = self._config[CONF_PAYLOAD_ARM_NIGHT] + action: str = self._config[CONF_PAYLOAD_ARM_NIGHT] await self._publish(code, action) async def async_alarm_arm_vacation(self, code: str | None = None) -> None: @@ -310,10 +318,10 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): This method is a coroutine. """ - code_required = self._config[CONF_CODE_ARM_REQUIRED] + code_required: bool = self._config[CONF_CODE_ARM_REQUIRED] if code_required and not self._validate_code(code, "arming vacation"): return - action = self._config[CONF_PAYLOAD_ARM_VACATION] + action: str = self._config[CONF_PAYLOAD_ARM_VACATION] await self._publish(code, action) async def async_alarm_arm_custom_bypass(self, code: str | None = None) -> None: @@ -321,10 +329,10 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): This method is a coroutine. """ - code_required = self._config[CONF_CODE_ARM_REQUIRED] + code_required: bool = self._config[CONF_CODE_ARM_REQUIRED] if code_required and not self._validate_code(code, "arming custom bypass"): return - action = self._config[CONF_PAYLOAD_ARM_CUSTOM_BYPASS] + action: str = self._config[CONF_PAYLOAD_ARM_CUSTOM_BYPASS] await self._publish(code, action) async def async_alarm_trigger(self, code: str | None = None) -> None: @@ -332,13 +340,13 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): This method is a coroutine. """ - code_required = self._config[CONF_CODE_TRIGGER_REQUIRED] + code_required: bool = self._config[CONF_CODE_TRIGGER_REQUIRED] if code_required and not self._validate_code(code, "triggering"): return - action = self._config[CONF_PAYLOAD_TRIGGER] + action: str = self._config[CONF_PAYLOAD_TRIGGER] await self._publish(code, action) - async def _publish(self, code, action): + async def _publish(self, code: str | None, action: str) -> None: """Publish via mqtt.""" variables = {"action": action, "code": code} payload = self._command_template(None, variables=variables) @@ -350,10 +358,10 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): self._config[CONF_ENCODING], ) - def _validate_code(self, code, state): + def _validate_code(self, code: str | None, state: str) -> bool: """Validate given code.""" - conf_code = self._config.get(CONF_CODE) - check = ( + conf_code: str | None = self._config.get(CONF_CODE) + check = bool( conf_code is None or code == conf_code or (conf_code == REMOTE_CODE and code) diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index d9351908665..82818c5b706 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -1,9 +1,10 @@ """Support for MQTT binary sensors.""" from __future__ import annotations -from datetime import timedelta +from datetime import datetime, timedelta import functools import logging +from typing import Any import voluptuous as vol @@ -24,7 +25,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback import homeassistant.helpers.event as evt @@ -45,7 +46,7 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttValueTemplate +from .models import MqttValueTemplate, ReceiveMessage from .util import get_mqtt_data _LOGGER = logging.getLogger(__name__) @@ -111,8 +112,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT binary sensor.""" async_add_entities([MqttBinarySensor(hass, config, config_entry, discovery_data)]) @@ -122,16 +123,18 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): """Representation a binary sensor that is updated by MQTT.""" _entity_id_format = binary_sensor.ENTITY_ID_FORMAT + _expired: bool | None - def __init__(self, hass, config, config_entry, discovery_data): + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the MQTT binary sensor.""" - self._expiration_trigger = None - self._delay_listener = None - expire_after = config.get(CONF_EXPIRE_AFTER) - if expire_after is not None and expire_after > 0: - self._expired = True - else: - self._expired = None + self._expiration_trigger: CALLBACK_TYPE | None = None + self._delay_listener: CALLBACK_TYPE | None = None MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @@ -146,7 +149,9 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): # MqttEntity.async_added_to_hass(), then we should not restore state and not self._expiration_trigger ): - expiration_at = last_state.last_changed + timedelta(seconds=expire_after) + expiration_at: datetime = last_state.last_changed + timedelta( + seconds=expire_after + ) if expiration_at < (time_now := dt_util.utcnow()): # Skip reactivating the binary_sensor _LOGGER.debug("Skip state recovery after reload for %s", self.entity_id) @@ -174,24 +179,30 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): await MqttEntity.async_will_remove_from_hass(self) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA def _setup_from_config(self, config: ConfigType) -> None: - self._attr_device_class = config.get(CONF_DEVICE_CLASS) + """(Re)Setup the entity.""" + expire_after: int | None = config.get(CONF_EXPIRE_AFTER) + if expire_after is not None and expire_after > 0: + self._expired = True + else: + self._expired = None self._attr_force_update = config[CONF_FORCE_UPDATE] + self._attr_device_class = config.get(CONF_DEVICE_CLASS) self._value_template = MqttValueTemplate( self._config.get(CONF_VALUE_TEMPLATE), entity=self, ).async_render_with_possible_json_value - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" @callback - def off_delay_listener(now): + def off_delay_listener(now: datetime) -> None: """Switch device off after a delay.""" self._delay_listener = None self._attr_is_on = False @@ -199,10 +210,10 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): @callback @log_messages(self.hass, self.entity_id) - def state_message_received(msg): + def state_message_received(msg: ReceiveMessage) -> None: """Handle a new received MQTT state message.""" # auto-expire enabled? - expire_after = self._config.get(CONF_EXPIRE_AFTER) + expire_after: int | None = self._config.get(CONF_EXPIRE_AFTER) if expire_after is not None and expire_after > 0: @@ -241,7 +252,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): else: # Payload is not for this entity template_info = "" if self._config.get(CONF_VALUE_TEMPLATE) is not None: - template_info = f", template output: '{payload}', with value template '{str(self._config.get(CONF_VALUE_TEMPLATE))}'" + template_info = f", template output: '{str(payload)}', with value template '{str(self._config.get(CONF_VALUE_TEMPLATE))}'" _LOGGER.info( "No matching payload found for entity: %s with state topic: %s. Payload: '%s'%s", self._config[CONF_NAME], @@ -276,12 +287,12 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): }, ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) @callback - def _value_is_expired(self, *_): + def _value_is_expired(self, *_: Any) -> None: """Triggered when value is expired.""" self._expiration_trigger = None self._expired = True @@ -291,7 +302,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): @property def available(self) -> bool: """Return true if the device is available and value has not expired.""" - expire_after = self._config.get(CONF_EXPIRE_AFTER) + expire_after: int | None = self._config.get(CONF_EXPIRE_AFTER) # mypy doesn't know about fget: https://github.com/python/mypy/issues/6185 return MqttAvailability.available.fget(self) and ( # type: ignore[attr-defined] expire_after is None or not self._expired diff --git a/homeassistant/components/mqtt/button.py b/homeassistant/components/mqtt/button.py index a14bf87c3be..d2743560411 100644 --- a/homeassistant/components/mqtt/button.py +++ b/homeassistant/components/mqtt/button.py @@ -90,8 +90,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT button.""" async_add_entities([MqttButton(hass, config, config_entry, discovery_data)]) @@ -102,25 +102,31 @@ class MqttButton(MqttEntity, ButtonEntity): _entity_id_format = button.ENTITY_ID_FORMAT - def __init__(self, hass, config, config_entry, discovery_data): + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the MQTT button.""" MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" self._command_template = MqttCommandTemplate( config.get(CONF_COMMAND_TEMPLATE), entity=self ).async_render - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" @property From bda7e416c441022eefabd18433debe7a80320dd3 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 2 Nov 2022 20:33:46 +0100 Subject: [PATCH 0178/1033] Improve MQTT type hints part 2 (#80529) * Improve typing camera * Improve typing cover * b64 encoding can be either bytes or a string. --- homeassistant/components/mqtt/camera.py | 28 +++++--- homeassistant/components/mqtt/cover.py | 86 ++++++++++++++----------- 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index f6039251882..5044f5abfaf 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -27,6 +27,7 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) +from .models import ReceiveMessage from .util import valid_subscribe_topic _LOGGER = logging.getLogger(__name__) @@ -114,8 +115,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT Camera.""" async_add_entities([MqttCamera(hass, config, config_entry, discovery_data)]) @@ -124,31 +125,38 @@ async def _async_setup_entity( class MqttCamera(MqttEntity, Camera): """representation of a MQTT camera.""" - _entity_id_format = camera.ENTITY_ID_FORMAT - _attributes_extra_blocked = MQTT_CAMERA_ATTRIBUTES_BLOCKED + _entity_id_format: str = camera.ENTITY_ID_FORMAT + _attributes_extra_blocked: frozenset[str] = MQTT_CAMERA_ATTRIBUTES_BLOCKED - def __init__(self, hass, config, config_entry, discovery_data): + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the MQTT Camera.""" - self._last_image = None + self._last_image: bytes | None = None Camera.__init__(self) MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" @callback @log_messages(self.hass, self.entity_id) - def message_received(msg): + def message_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages.""" if CONF_IMAGE_ENCODING in self._config: self._last_image = b64decode(msg.payload) else: + assert isinstance(msg.payload, bytes) self._last_image = msg.payload self._sub_state = subscription.async_prepare_subscribe_topics( @@ -164,7 +172,7 @@ class MqttCamera(MqttEntity, Camera): }, ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 7d7d4f61c4a..ef8b0090015 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -31,6 +31,7 @@ from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.json import JSON_DECODE_EXCEPTIONS, json_loads +from homeassistant.helpers.service_info.mqtt import ReceivePayloadType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import subscription @@ -50,7 +51,7 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttCommandTemplate, MqttValueTemplate +from .models import MqttCommandTemplate, MqttValueTemplate, ReceiveMessage from .util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic _LOGGER = logging.getLogger(__name__) @@ -113,44 +114,44 @@ MQTT_COVER_ATTRIBUTES_BLOCKED = frozenset( ) -def validate_options(value): +def validate_options(config: ConfigType) -> ConfigType: """Validate options. If set position topic is set then get position topic is set as well. """ - if CONF_SET_POSITION_TOPIC in value and CONF_GET_POSITION_TOPIC not in value: + if CONF_SET_POSITION_TOPIC in config and CONF_GET_POSITION_TOPIC not in config: raise vol.Invalid( f"'{CONF_SET_POSITION_TOPIC}' must be set together with '{CONF_GET_POSITION_TOPIC}'." ) # if templates are set make sure the topic for the template is also set - if CONF_VALUE_TEMPLATE in value and CONF_STATE_TOPIC not in value: + if CONF_VALUE_TEMPLATE in config and CONF_STATE_TOPIC not in config: raise vol.Invalid( f"'{CONF_VALUE_TEMPLATE}' must be set together with '{CONF_STATE_TOPIC}'." ) - if CONF_GET_POSITION_TEMPLATE in value and CONF_GET_POSITION_TOPIC not in value: + if CONF_GET_POSITION_TEMPLATE in config and CONF_GET_POSITION_TOPIC not in config: raise vol.Invalid( f"'{CONF_GET_POSITION_TEMPLATE}' must be set together with '{CONF_GET_POSITION_TOPIC}'." ) - if CONF_SET_POSITION_TEMPLATE in value and CONF_SET_POSITION_TOPIC not in value: + if CONF_SET_POSITION_TEMPLATE in config and CONF_SET_POSITION_TOPIC not in config: raise vol.Invalid( f"'{CONF_SET_POSITION_TEMPLATE}' must be set together with '{CONF_SET_POSITION_TOPIC}'." ) - if CONF_TILT_COMMAND_TEMPLATE in value and CONF_TILT_COMMAND_TOPIC not in value: + if CONF_TILT_COMMAND_TEMPLATE in config and CONF_TILT_COMMAND_TOPIC not in config: raise vol.Invalid( f"'{CONF_TILT_COMMAND_TEMPLATE}' must be set together with '{CONF_TILT_COMMAND_TOPIC}'." ) - if CONF_TILT_STATUS_TEMPLATE in value and CONF_TILT_STATUS_TOPIC not in value: + if CONF_TILT_STATUS_TEMPLATE in config and CONF_TILT_STATUS_TOPIC not in config: raise vol.Invalid( f"'{CONF_TILT_STATUS_TEMPLATE}' must be set together with '{CONF_TILT_STATUS_TOPIC}'." ) - return value + return config _PLATFORM_SCHEMA_BASE = MQTT_BASE_SCHEMA.extend( @@ -251,8 +252,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT Cover.""" async_add_entities([MqttCover(hass, config, config_entry, discovery_data)]) @@ -261,26 +262,32 @@ async def _async_setup_entity( class MqttCover(MqttEntity, CoverEntity): """Representation of a cover that can be controlled using MQTT.""" - _entity_id_format = cover.ENTITY_ID_FORMAT - _attributes_extra_blocked = MQTT_COVER_ATTRIBUTES_BLOCKED + _entity_id_format: str = cover.ENTITY_ID_FORMAT + _attributes_extra_blocked: frozenset[str] = MQTT_COVER_ATTRIBUTES_BLOCKED - def __init__(self, hass, config, config_entry, discovery_data): + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the cover.""" - self._position = None - self._state = None + self._position: int | None = None + self._state: str | None = None - self._optimistic = None - self._tilt_value = None - self._tilt_optimistic = None + self._optimistic: bool | None = None + self._tilt_value: int | None = None + self._tilt_optimistic: bool | None = None MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: no_position = ( config.get(CONF_SET_POSITION_TOPIC) is None and config.get(CONF_GET_POSITION_TOPIC) is None @@ -353,13 +360,13 @@ class MqttCover(MqttEntity, CoverEntity): config_attributes=template_config_attributes, ).async_render_with_possible_json_value - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" topics = {} @callback @log_messages(self.hass, self.entity_id) - def tilt_message_received(msg): + def tilt_message_received(msg: ReceiveMessage) -> None: """Handle tilt updates.""" payload = self._tilt_status_template(msg.payload) @@ -371,7 +378,7 @@ class MqttCover(MqttEntity, CoverEntity): @callback @log_messages(self.hass, self.entity_id) - def state_message_received(msg): + def state_message_received(msg: ReceiveMessage) -> None: """Handle new MQTT state messages.""" payload = self._value_template(msg.payload) @@ -409,31 +416,32 @@ class MqttCover(MqttEntity, CoverEntity): @callback @log_messages(self.hass, self.entity_id) - def position_message_received(msg): + def position_message_received(msg: ReceiveMessage) -> None: """Handle new MQTT position messages.""" - payload = self._get_position_template(msg.payload) + payload: ReceivePayloadType = self._get_position_template(msg.payload) + payload_dict: Any = None if not payload: _LOGGER.debug("Ignoring empty position message from '%s'", msg.topic) return try: - payload = json_loads(payload) + payload_dict = json_loads(payload) except JSON_DECODE_EXCEPTIONS: pass - if isinstance(payload, dict): - if "position" not in payload: + if payload_dict and isinstance(payload_dict, dict): + if "position" not in payload_dict: _LOGGER.warning( "Template (position_template) returned JSON without position attribute" ) return - if "tilt_position" in payload: + if "tilt_position" in payload_dict: if not self._config.get(CONF_TILT_STATE_OPTIMISTIC): # reset forced set tilt optimistic self._tilt_optimistic = False - self.tilt_payload_received(payload["tilt_position"]) - payload = payload["position"] + self.tilt_payload_received(payload_dict["tilt_position"]) + payload = payload_dict["position"] try: percentage_payload = self.find_percentage_in_range( @@ -481,7 +489,7 @@ class MqttCover(MqttEntity, CoverEntity): self.hass, self._sub_state, topics ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) @@ -719,13 +727,15 @@ class MqttCover(MqttEntity, CoverEntity): else: await self.async_close_cover_tilt(**kwargs) - def is_tilt_closed(self): + def is_tilt_closed(self) -> bool: """Return if the cover is tilted closed.""" return self._tilt_value == self.find_percentage_in_range( float(self._config[CONF_TILT_CLOSED_POSITION]) ) - def find_percentage_in_range(self, position, range_type=TILT_PAYLOAD): + def find_percentage_in_range( + self, position: float, range_type: str = TILT_PAYLOAD + ) -> int: """Find the 0-100% value within the specified range.""" # the range of motion as defined by the min max values if range_type == COVER_PAYLOAD: @@ -745,7 +755,9 @@ class MqttCover(MqttEntity, CoverEntity): return position_percentage - def find_in_range_from_percent(self, percentage, range_type=TILT_PAYLOAD): + def find_in_range_from_percent( + self, percentage: float, range_type: str = TILT_PAYLOAD + ) -> int: """ Find the adjusted value for 0-100% within the specified range. @@ -768,7 +780,7 @@ class MqttCover(MqttEntity, CoverEntity): return position @callback - def tilt_payload_received(self, _payload): + def tilt_payload_received(self, _payload: Any) -> None: """Set the tilt value.""" try: From 1beab9694605ae9a9b55dfd1a812c81449bb200b Mon Sep 17 00:00:00 2001 From: rappenze Date: Wed, 2 Nov 2022 23:02:44 +0100 Subject: [PATCH 0179/1033] Replace deprecated unit constants in fibaro sensor (#81425) --- homeassistant/components/fibaro/sensor.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/fibaro/sensor.py b/homeassistant/components/fibaro/sensor.py index 797fc6d8d44..da1b66c6528 100644 --- a/homeassistant/components/fibaro/sensor.py +++ b/homeassistant/components/fibaro/sensor.py @@ -14,13 +14,12 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, - ENERGY_KILO_WATT_HOUR, LIGHT_LUX, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, Platform, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -67,7 +66,7 @@ MAIN_SENSOR_TYPES: dict[str, SensorEntityDescription] = { "com.fibaro.energyMeter": SensorEntityDescription( key="com.fibaro.energyMeter", name="Energy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -79,14 +78,14 @@ ADDITIONAL_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="energy", name="Energy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="power", name="Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), @@ -94,8 +93,8 @@ ADDITIONAL_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( FIBARO_TO_HASS_UNIT: dict[str, str] = { "lux": LIGHT_LUX, - "C": TEMP_CELSIUS, - "F": TEMP_FAHRENHEIT, + "C": UnitOfTemperature.CELSIUS, + "F": UnitOfTemperature.FAHRENHEIT, } From 7995f0e414a04966c29a9854f86a427164118ed7 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 3 Nov 2022 00:28:45 +0000 Subject: [PATCH 0180/1033] [ci skip] Translation update --- .../components/aranet/translations/id.json | 24 ++++ .../fireservicerota/translations/id.json | 6 + .../components/generic/translations/id.json | 7 ++ .../google_travel_time/translations/id.json | 4 +- .../components/hassio/translations/bg.json | 42 +++++++ .../components/hassio/translations/ca.json | 36 ++++++ .../components/hassio/translations/de.json | 104 ++++++++++++++++- .../components/hassio/translations/en.json | 2 +- .../components/hassio/translations/es.json | 104 ++++++++++++++++- .../components/hassio/translations/et.json | 104 ++++++++++++++++- .../components/hassio/translations/hu.json | 104 ++++++++++++++++- .../components/hassio/translations/id.json | 110 ++++++++++++++++++ .../components/hassio/translations/it.json | 104 ++++++++++++++++- .../components/hassio/translations/no.json | 104 ++++++++++++++++- .../components/hassio/translations/pt-BR.json | 104 ++++++++++++++++- .../components/hassio/translations/ru.json | 108 ++++++++++++++++- .../hassio/translations/zh-Hant.json | 104 ++++++++++++++++- .../components/mqtt/translations/id.json | 34 +++++- .../pushbullet/translations/ca.json | 25 ++++ .../pushbullet/translations/de.json | 25 ++++ .../pushbullet/translations/es.json | 25 ++++ .../pushbullet/translations/hu.json | 25 ++++ .../pushbullet/translations/it.json | 25 ++++ .../pushbullet/translations/pt-BR.json | 25 ++++ .../pushbullet/translations/ru.json | 25 ++++ 25 files changed, 1356 insertions(+), 24 deletions(-) create mode 100644 homeassistant/components/aranet/translations/id.json create mode 100644 homeassistant/components/pushbullet/translations/ca.json create mode 100644 homeassistant/components/pushbullet/translations/de.json create mode 100644 homeassistant/components/pushbullet/translations/es.json create mode 100644 homeassistant/components/pushbullet/translations/hu.json create mode 100644 homeassistant/components/pushbullet/translations/it.json create mode 100644 homeassistant/components/pushbullet/translations/pt-BR.json create mode 100644 homeassistant/components/pushbullet/translations/ru.json diff --git a/homeassistant/components/aranet/translations/id.json b/homeassistant/components/aranet/translations/id.json new file mode 100644 index 00000000000..04e65296a12 --- /dev/null +++ b/homeassistant/components/aranet/translations/id.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi", + "no_devices_found": "Tidak ditemukan perangkat Aranet yang tidak dikonfigurasi.", + "outdated_version": "Perangkat ini menggunakan firmware usang. Perbarui setidaknya ke firmware v1.2.0 dan coba lagi." + }, + "error": { + "unknown": "Kesalahan yang tidak diharapkan" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Ingin menyiapkan {name}?" + }, + "user": { + "data": { + "address": "Perangkat" + }, + "description": "Pilih perangkat untuk disiapkan" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fireservicerota/translations/id.json b/homeassistant/components/fireservicerota/translations/id.json index 0c4462a1ea7..c7a5c894f62 100644 --- a/homeassistant/components/fireservicerota/translations/id.json +++ b/homeassistant/components/fireservicerota/translations/id.json @@ -17,6 +17,12 @@ }, "description": "Token autentikasi menjadi tidak valid, masuk untuk membuat token lagi." }, + "reauth_confirm": { + "data": { + "password": "Kata Sandi" + }, + "description": "Token autentikasi menjadi tidak valid, masuk untuk membuat token lagi." + }, "user": { "data": { "password": "Kata Sandi", diff --git a/homeassistant/components/generic/translations/id.json b/homeassistant/components/generic/translations/id.json index 8cc0ca6aefc..f3796310744 100644 --- a/homeassistant/components/generic/translations/id.json +++ b/homeassistant/components/generic/translations/id.json @@ -75,6 +75,13 @@ "unknown": "Kesalahan yang tidak diharapkan" }, "step": { + "confirm_still": { + "data": { + "confirmed_ok": "Gambar ini terlihat bagus." + }, + "description": "![Pratinjau Gambar Diam Kamera]({preview_url})", + "title": "Pratinjau" + }, "content_type": { "data": { "content_type": "Jenis Konten" diff --git a/homeassistant/components/google_travel_time/translations/id.json b/homeassistant/components/google_travel_time/translations/id.json index e960d03c341..bcfbafae4b7 100644 --- a/homeassistant/components/google_travel_time/translations/id.json +++ b/homeassistant/components/google_travel_time/translations/id.json @@ -4,7 +4,8 @@ "already_configured": "Lokasi sudah dikonfigurasi" }, "error": { - "cannot_connect": "Gagal terhubung" + "cannot_connect": "Gagal terhubung", + "invalid_auth": "Autentikasi tidak valid" }, "step": { "user": { @@ -27,6 +28,7 @@ "mode": "Mode Perjalanan", "time": "Waktu", "time_type": "Jenis Waktu", + "traffic_mode": "Mode Lalu Lintas", "transit_mode": "Mode Transit", "transit_routing_preference": "Preferensi Perutean Transit", "units": "Unit" diff --git a/homeassistant/components/hassio/translations/bg.json b/homeassistant/components/hassio/translations/bg.json index 451dd51d125..cd7aa5c1923 100644 --- a/homeassistant/components/hassio/translations/bg.json +++ b/homeassistant/components/hassio/translations/bg.json @@ -2,6 +2,48 @@ "issues": { "unsupported": { "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - {reason}" + }, + "unsupported_apparmor": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u0441 AppArmor" + }, + "unsupported_cgroup_version": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u0432\u0435\u0440\u0441\u0438\u044f \u043d\u0430 CGroup" + }, + "unsupported_dbus": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u0441 D-Bus" + }, + "unsupported_dns_server": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u0441 DNS \u0441\u044a\u0440\u0432\u044a\u0440\u0430" + }, + "unsupported_docker_version": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u0432\u0435\u0440\u0441\u0438\u044f \u043d\u0430 Docker" + }, + "unsupported_job_conditions": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u0417\u0430\u0449\u0438\u0442\u0438\u0442\u0435 \u0441\u0430 \u0434\u0435\u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0438" + }, + "unsupported_network_manager": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u0441 Network Manager" + }, + "unsupported_os": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u041e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430" + }, + "unsupported_os_agent": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u0441 OS-Agent" + }, + "unsupported_software": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d \u0441\u043e\u0444\u0442\u0443\u0435\u0440" + }, + "unsupported_supervisor_version": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u0412\u0435\u0440\u0441\u0438\u044f \u043d\u0430 Supervisor" + }, + "unsupported_systemd": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u0441\u044a\u0441 Systemd" + }, + "unsupported_systemd_journal": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u0441\u044a\u0441 Systemd Journal" + }, + "unsupported_systemd_resolved": { + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u0441\u044a\u0441 Systemd-Resolved" } }, "system_health": { diff --git a/homeassistant/components/hassio/translations/ca.json b/homeassistant/components/hassio/translations/ca.json index 14679301993..40a1c9435aa 100644 --- a/homeassistant/components/hassio/translations/ca.json +++ b/homeassistant/components/hassio/translations/ca.json @@ -4,9 +4,45 @@ "description": "El sistema no \u00e9s saludable a causa de '{reason}'. Clica l'enlla\u00e7 per obtenir m\u00e9s informaci\u00f3 sobre qu\u00e8 falla aix\u00f2 i com solucionar-ho.", "title": "Sistema no saludable - {reason}" }, + "unhealthy_docker": { + "description": "El sistema no \u00e9s saludable perqu\u00e8 Docker no est\u00e0 configurat correctament. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no saludable - Docker mal configurat" + }, + "unhealthy_privileged": { + "description": "El sistema no \u00e9s saludable perqu\u00e8 no t\u00e9 acc\u00e9s privilegiat a l'execuci\u00f3 de Docker. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no saludable - Sense privilegis" + }, + "unhealthy_setup": { + "description": "El sistema no \u00e9s saludable perqu\u00e8 no s'ha pogut completar la configuraci\u00f3 correctament. Pot ser degut a diferents motius, clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no saludable - Configuraci\u00f3 fallida" + }, + "unhealthy_supervisor": { + "description": "El sistema no \u00e9s saludable perqu\u00e8 ha fallat un intent d'actualitzaci\u00f3 del Supervisor a l'\u00faltima versi\u00f3. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no saludable - Actualitzaci\u00f3 del Supervisor fallida" + }, + "unhealthy_untrusted": { + "description": "El sistema no \u00e9s saludable perqu\u00e8 s'ha detectat codi o imatges no fiables. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no saludable - Codi no fiable" + }, "unsupported": { "description": "El sistema no \u00e9s compatible a causa de '{reason}'. Clica l'enlla\u00e7 per obtenir m\u00e9s informaci\u00f3 sobre qu\u00e8 significa aix\u00f2 i com tornar a un sistema compatible.", "title": "Sistema no compatible - {reason}" + }, + "unsupported_apparmor": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 AppArmor no est\u00e0 funcionant correctament i els complements s'executen en un entorn insegur i no protegit. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Problemes amb AppArmor" + }, + "unsupported_cgroup_version": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 s'utilitza una versi\u00f3 de Docker CGroup incorrecta. Clica l'enlla\u00e7 per con\u00e8ixer la versi\u00f3 correcta i sobre com solucionar-ho.", + "title": "Sistema no compatible - Versi\u00f3 de CGroup" + }, + "unsupported_docker_configuration": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 el 'deamon' de Docker est\u00e0 funcionant de manera inesperada. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Docker mal configurat" + }, + "unsupported_docker_version": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 s'utilitza una versi\u00f3 de Docker incorrecta. Clica l'enlla\u00e7 per con\u00e8ixer la versi\u00f3 correcta i sobre com solucionar-ho.", + "title": "Sistema no compatible - Versi\u00f3 de Docker" } }, "system_health": { diff --git a/homeassistant/components/hassio/translations/de.json b/homeassistant/components/hassio/translations/de.json index 54940b93448..325009e4814 100644 --- a/homeassistant/components/hassio/translations/de.json +++ b/homeassistant/components/hassio/translations/de.json @@ -1,12 +1,112 @@ { "issues": { "unhealthy": { - "description": "Das System ist derzeit aufgrund von \u201e{reason}\u201c fehlerhaft. Verwende den Link, um mehr dar\u00fcber zu erfahren, was falsch ist und wie du es beheben kannst.", + "description": "Das System ist derzeit aufgrund von \u201e{reason}\u201c fehlerhaft. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", "title": "Fehlerhaftes System - {reason}" }, + "unhealthy_docker": { + "description": "Das System ist derzeit fehlerhaft, da Docker falsch konfiguriert ist. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Ungesundes System - Docker falsch konfiguriert" + }, + "unhealthy_privileged": { + "description": "Das System ist derzeit fehlerhaft, da es keinen privilegierten Zugriff auf die Docker-Laufzeit hat. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Fehlerhaftes System \u2013 Nicht privilegiert" + }, + "unhealthy_setup": { + "description": "Das System ist derzeit fehlerhaft, da die Einrichtung nicht abgeschlossen werden konnte. Dies kann mehrere Gr\u00fcnde haben. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Fehlerhaftes System \u2013 Setup fehlgeschlagen" + }, + "unhealthy_supervisor": { + "description": "Das System ist derzeit fehlerhaft, weil ein Versuch, Supervisor auf die neueste Version zu aktualisieren, fehlgeschlagen ist. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Fehlerhaftes System \u2013 Supervisor-Update fehlgeschlagen" + }, + "unhealthy_untrusted": { + "description": "Das System ist derzeit nicht fehlerfrei, da es nicht vertrauensw\u00fcrdigen Code oder verwendete Images erkannt hat. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Fehlerhaftes System \u2013 Nicht vertrauensw\u00fcrdiger Code" + }, "unsupported": { - "description": "Das System wird aufgrund von \u201e{reason}\u201c nicht unterst\u00fctzt. Verwende den Link, um mehr dar\u00fcber zu erfahren, was dies bedeutet und wie du zu einem unterst\u00fctzten System zur\u00fcckkehren kannst.", + "description": "Das System wird aufgrund von \u201e{reason}\u201c nicht unterst\u00fctzt. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", "title": "Nicht unterst\u00fctztes System \u2013 {reason}" + }, + "unsupported_apparmor": { + "description": "Das System wird nicht unterst\u00fctzt, da AppArmor nicht ordnungsgem\u00e4\u00df funktioniert und Add-Ons ungesch\u00fctzt und unsicher ausgef\u00fchrt werden. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System - AppArmor-Probleme" + }, + "unsupported_cgroup_version": { + "description": "Das System wird nicht unterst\u00fctzt, da die falsche Version von Docker CGroup verwendet wird. Verwende den Link, um die richtige Version zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 CGroup-Version" + }, + "unsupported_connectivity_check": { + "description": "Das System wird nicht unterst\u00fctzt, weil Home Assistant nicht feststellen kann, wann eine Internetverbindung verf\u00fcgbar ist. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System - Konnektivit\u00e4tspr\u00fcfung deaktiviert" + }, + "unsupported_content_trust": { + "description": "Das System wird nicht unterst\u00fctzt, da Home Assistant nicht \u00fcberpr\u00fcfen kann, ob der ausgef\u00fchrte Inhalt vertrauensw\u00fcrdig ist und nicht von Angreifern ge\u00e4ndert wurde. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 Inhaltsvertrauenspr\u00fcfung deaktiviert" + }, + "unsupported_dbus": { + "description": "System wird nicht unterst\u00fctzt, da D-Bus nicht richtig funktioniert. Viele Dinge schlagen ohne dies fehl, da Supervisor nicht mit dem Host kommunizieren kann. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 D-Bus-Probleme" + }, + "unsupported_dns_server": { + "description": "Das System wird nicht unterst\u00fctzt, da der bereitgestellte DNS-Server nicht ordnungsgem\u00e4\u00df funktioniert und die Fallback-DNS-Option deaktiviert wurde. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System - DNS-Server-Probleme" + }, + "unsupported_docker_configuration": { + "description": "Das System wird nicht unterst\u00fctzt, da der Docker-Daemon auf unerwartete Weise ausgef\u00fchrt wird. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System - Docker falsch konfiguriert" + }, + "unsupported_docker_version": { + "description": "Das System wird nicht unterst\u00fctzt, da die falsche Version von Docker verwendet wird. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 Docker-Version" + }, + "unsupported_job_conditions": { + "description": "Das System wird nicht unterst\u00fctzt, da eine oder mehrere Jobbedingungen deaktiviert wurden, die vor unerwarteten Ausf\u00e4llen und Unterbrechungen sch\u00fctzen. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 Schutz deaktiviert" + }, + "unsupported_lxc": { + "description": "Das System wird nicht unterst\u00fctzt, da es in einer virtuellen LXC-Maschine ausgef\u00fchrt wird. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 LXC erkannt" + }, + "unsupported_network_manager": { + "description": "Das System wird nicht unterst\u00fctzt, weil Network Manager fehlt, inaktiv oder falsch konfiguriert ist. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 Probleme mit Network Manager" + }, + "unsupported_os": { + "description": "Das System wird nicht unterst\u00fctzt, da das verwendete Betriebssystem nicht f\u00fcr die Verwendung mit Supervisor getestet oder gewartet wurde. Verwende den Link, um zu erfahren, welche Betriebssysteme unterst\u00fctzt werden und wie du das Problem beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 Betriebssystem" + }, + "unsupported_os_agent": { + "description": "Das System wird nicht unterst\u00fctzt, weil OS-Agent fehlt, inaktiv oder falsch konfiguriert ist. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System - Probleme mit OS-Agenten" + }, + "unsupported_restart_policy": { + "description": "Das System wird nicht unterst\u00fctzt, da f\u00fcr einen Docker-Container eine Neustartrichtlinie festgelegt ist, die beim Start Probleme verursachen k\u00f6nnte. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 Container-Neustartrichtlinie" + }, + "unsupported_software": { + "description": "Das System wird nicht unterst\u00fctzt, da zus\u00e4tzliche Software au\u00dferhalb des Home Assistant-\u00d6kosystems erkannt wurde. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 Nicht unterst\u00fctzte Software" + }, + "unsupported_source_mods": { + "description": "Das System wird nicht unterst\u00fctzt, da der Supervisor-Quellcode ge\u00e4ndert wurde. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 Modifikation des Supervisor-Quellcodes" + }, + "unsupported_supervisor_version": { + "description": "Das System wird nicht unterst\u00fctzt, da eine veraltete Version von Supervisor verwendet wird und die automatische Aktualisierung deaktiviert wurde. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 Supervisor-Version" + }, + "unsupported_systemd": { + "description": "System wird nicht unterst\u00fctzt, weil Systemd fehlt, inaktiv oder falsch konfiguriert ist. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System - Systemd-Probleme" + }, + "unsupported_systemd_journal": { + "description": "Das System wird nicht unterst\u00fctzt, da das Systemd-Journal und/oder der Gateway-Dienst fehlt, inaktiv oder falsch konfiguriert ist. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 Systemd Journal-Probleme" + }, + "unsupported_systemd_resolved": { + "description": "Das System wird nicht unterst\u00fctzt, weil Systemd Resolved fehlt, inaktiv oder falsch konfiguriert ist. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "title": "Nicht unterst\u00fctztes System \u2013 Von Systemd behobene Probleme" } }, "system_health": { diff --git a/homeassistant/components/hassio/translations/en.json b/homeassistant/components/hassio/translations/en.json index 243467b9f22..cdfe7f17a44 100644 --- a/homeassistant/components/hassio/translations/en.json +++ b/homeassistant/components/hassio/translations/en.json @@ -13,7 +13,7 @@ "title": "Unhealthy system - Not privileged" }, "unhealthy_setup": { - "description": "System is currently because setup failed to complete. There are a number of reasons this can occur, use the link to learn more and how to fix this.", + "description": "System is currently unhealthy because setup failed to complete. There are a number of reasons this can occur, use the link to learn more and how to fix this.", "title": "Unhealthy system - Setup failed" }, "unhealthy_supervisor": { diff --git a/homeassistant/components/hassio/translations/es.json b/homeassistant/components/hassio/translations/es.json index f2aef9d7214..202a362fbbc 100644 --- a/homeassistant/components/hassio/translations/es.json +++ b/homeassistant/components/hassio/translations/es.json @@ -1,12 +1,112 @@ { "issues": { "unhealthy": { - "description": "Actualmente el sistema no est\u00e1 en buen estado debido a ''{reason}''. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n sobre lo que est\u00e1 mal y c\u00f3mo solucionarlo.", + "description": "Actualmente el sistema no est\u00e1 en buen estado debido a {reason}. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", "title": "Sistema en mal estado: {reason}" }, + "unhealthy_docker": { + "description": "Actualmente el sistema no est\u00e1 en buen estado porque Docker est\u00e1 configurado incorrectamente. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema en mal estado - Docker mal configurado" + }, + "unhealthy_privileged": { + "description": "Actualmente el sistema no est\u00e1 en buen estado porque no tiene acceso privilegiado al runtime de Docker. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema en mal estado - No privilegiado" + }, + "unhealthy_setup": { + "description": "Actualmente el sistema no est\u00e1 en buen estado porque la configuraci\u00f3n no se complet\u00f3. Hay varias razones por las que esto puede ocurrir, utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema en mal estado: La configuraci\u00f3n fall\u00f3" + }, + "unhealthy_supervisor": { + "description": "Actualmente el sistema no est\u00e1 en buen estado porque fall\u00f3 un intento de actualizar Supervisor a la \u00faltima versi\u00f3n. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema en mal estado: La actualizaci\u00f3n del supervisor fall\u00f3" + }, + "unhealthy_untrusted": { + "description": "Actualmente el sistema no est\u00e1 en buen estado porque ha detectado c\u00f3digo o im\u00e1genes en uso que no son de confianza. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no saludable: C\u00f3digo no confiable" + }, "unsupported": { - "description": "El sistema no es compatible debido a ''{reason}''. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n sobre lo que esto significa y c\u00f3mo volver a un sistema compatible.", + "description": "El sistema no es compatible debido a {reason}. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", "title": "Sistema no compatible: {reason}" + }, + "unsupported_apparmor": { + "description": "El sistema no es compatible porque AppArmor no funciona correctamente y los complementos se ejecutan sin protecci\u00f3n ni seguridad. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Problemas con AppArmor" + }, + "unsupported_cgroup_version": { + "description": "El sistema no es compatible porque se est\u00e1 utilizando una versi\u00f3n incorrecta de Docker CGroup. Utiliza el enlace para conocer la versi\u00f3n correcta y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: versi\u00f3n CGroup" + }, + "unsupported_connectivity_check": { + "description": "El sistema no es compatible porque Home Assistant no puede determinar cu\u00e1ndo hay una conexi\u00f3n a Internet disponible. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible - Verificaci\u00f3n de conectividad deshabilitada" + }, + "unsupported_content_trust": { + "description": "El sistema no es compatible porque Home Assistant no puede verificar que el contenido que se est\u00e1 ejecutando sea confiable y que los atacantes no lo hayan modificado. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Verificaci\u00f3n de confianza de contenido deshabilitada" + }, + "unsupported_dbus": { + "description": "El sistema no es compatible porque D-Bus no funciona correctamente. Muchas cosas fallan sin esto, ya que Supervisor no puede comunicarse con el host. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Problemas con D-Bus" + }, + "unsupported_dns_server": { + "description": "El sistema no es compatible porque el servidor DNS proporcionado no funciona correctamente y la opci\u00f3n de DNS de respaldo se ha deshabilitado. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Problemas con el servidor DNS" + }, + "unsupported_docker_configuration": { + "description": "El sistema no es compatible porque el demonio de Docker se est\u00e1 ejecutando de forma inesperada. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible - Docker mal configurado" + }, + "unsupported_docker_version": { + "description": "El sistema no es compatible porque se est\u00e1 utilizando una versi\u00f3n incorrecta de Docker. Utiliza el enlace para conocer la versi\u00f3n correcta y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Versi\u00f3n de Docker" + }, + "unsupported_job_conditions": { + "description": "El sistema no es compatible porque se han deshabilitado una o m\u00e1s condiciones de trabajo que protegen contra fallos y roturas inesperadas. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible - Protecciones deshabilitadas" + }, + "unsupported_lxc": { + "description": "El sistema no es compatible porque se est\u00e1 ejecutando en una m\u00e1quina virtual LXC. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible - LXC detectado" + }, + "unsupported_network_manager": { + "description": "El sistema no es compatible porque falta Network Manager, est\u00e1 inactivo o mal configurado. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Problemas con Network Manager" + }, + "unsupported_os": { + "description": "El sistema no es compatible porque el sistema operativo en uso no se ha probado ni mantenido para su uso con Supervisor. Utiliza el enlace para saber qu\u00e9 sistemas operativos son compatibles y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible - Sistema operativo" + }, + "unsupported_os_agent": { + "description": "El sistema no es compatible porque OS-Agent falta, est\u00e1 inactivo o mal configurado. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Problemas con OS-Agent" + }, + "unsupported_restart_policy": { + "description": "El sistema no es compatible porque un contenedor Docker tiene un conjunto de pol\u00edticas de reinicio que podr\u00eda causar problemas en el inicio. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Pol\u00edtica de reinicio del contenedor" + }, + "unsupported_software": { + "description": "El sistema no es compatible porque se detect\u00f3 software adicional fuera del ecosistema de Home Assistant. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Software no compatible" + }, + "unsupported_source_mods": { + "description": "El sistema no es compatible porque se modific\u00f3 el c\u00f3digo fuente de Supervisor. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Modificaciones de la fuente del supervisor" + }, + "unsupported_supervisor_version": { + "description": "El sistema no es compatible porque se est\u00e1 utilizando una versi\u00f3n obsoleta de Supervisor y se ha desactivado la actualizaci\u00f3n autom\u00e1tica. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Versi\u00f3n de supervisor" + }, + "unsupported_systemd": { + "description": "El sistema no es compatible porque falta Systemd, est\u00e1 inactivo o est\u00e1 mal configurado. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Problemas con Systemd" + }, + "unsupported_systemd_journal": { + "description": "El sistema no es compatible porque Systemd Journal y/o el servicio de puerta de enlace faltan, est\u00e1n inactivos o mal configurados. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Problemas con Systemd Journal" + }, + "unsupported_systemd_resolved": { + "description": "El sistema no es compatible porque falta Systemd Resolved, est\u00e1 inactivo o est\u00e1 mal configurado. Utiliza el enlace para obtener m\u00e1s informaci\u00f3n y c\u00f3mo solucionarlo.", + "title": "Sistema no compatible: Problemas con Systemd-Resolved" } }, "system_health": { diff --git a/homeassistant/components/hassio/translations/et.json b/homeassistant/components/hassio/translations/et.json index ea0f78c0c57..d0acf51857c 100644 --- a/homeassistant/components/hassio/translations/et.json +++ b/homeassistant/components/hassio/translations/et.json @@ -1,12 +1,112 @@ { "issues": { "unhealthy": { - "description": "S\u00fcsteem ei ole praegu korras '{reason}' t\u00f5ttu. Kasuta linki, et saada rohkem teavet selle kohta, mis on valesti ja kuidas seda parandada.", + "description": "S\u00fcsteem ei ole praegu korras {reason} t\u00f5ttu. Kasuta linki, et saada rohkem teavet ja kuidas seda parandada.", "title": "Vigane s\u00fcsteem \u2013 {reason}" }, + "unhealthy_docker": { + "description": "S\u00fcsteem on praegu ebatervislik, sest Docker on valesti konfigureeritud. Kasuta linki, et rohkem teada saada ja kuidas seda parandada.", + "title": "Ebatervislik s\u00fcsteem \u2013 Docker on valesti konfigureeritud" + }, + "unhealthy_privileged": { + "description": "S\u00fcsteem on praegu ebatervislik, sest tal puudub privilegeeritud juurdep\u00e4\u00e4s dokkerite k\u00e4ivituskoodile. Kasuta linki, et rohkem teada saada ja kuidas seda parandada.", + "title": "Ebaterve s\u00fcsteem - \u00f5igused puuduvad" + }, + "unhealthy_setup": { + "description": "S\u00fcsteem on hetkel ebatervislik, sest seadistamist ei \u00f5nnestunud l\u00f5pule viia. Sellel v\u00f5ib olla mitu p\u00f5hjust, kasuta linki, et saada rohkem teavet ja teada, kuidas seda parandada.", + "title": "Ebaterve s\u00fcsteem - seadistamine eba\u00f5nnestus" + }, + "unhealthy_supervisor": { + "description": "S\u00fcsteem on hetkel ebatervislik, sest Supervisori uuendamise katse viimasele versioonile eba\u00f5nnestus. Kasuta linki, et teada saada rohkem ja kuidas seda parandada.", + "title": "Ebaterve s\u00fcsteem - Supervisori uuendamine eba\u00f5nnestus" + }, + "unhealthy_untrusted": { + "description": "S\u00fcsteem on hetkel ebatervislik, sest on tuvastanud kasutuses oleva ebausaldusv\u00e4\u00e4rse koodi v\u00f5i kujutiste kasutamise. Kasuta linki, et rohkem teada saada ja kuidas seda parandada.", + "title": "Ebaterve s\u00fcsteem - ebausaldusv\u00e4\u00e4rne kood" + }, "unsupported": { - "description": "S\u00fcsteemi ei toetata '{reason}' t\u00f5ttu. Kasuta linki, et saada lisateavet selle kohta, mida see t\u00e4hendab ja kuidas toetatud s\u00fcsteemi naasta.", + "description": "S\u00fcsteemi ei toetata {reason} t\u00f5ttu. Kasuta linki, et saada lisateavet ja kuidas seda parandada.", "title": "Toetamata s\u00fcsteem \u2013 {reason}" + }, + "unsupported_apparmor": { + "description": "S\u00fcsteem ei ole toetatud, sest AppArmor t\u00f6\u00f6tab valesti ja lisad t\u00f6\u00f6tavad kaitsmata ja ebaturvaliselt. Kasuta linki, et rohkem teada saada ja kuidas seda parandada.", + "title": "Toetamata s\u00fcsteem \u2013 AppArmori probleemid" + }, + "unsupported_cgroup_version": { + "description": "S\u00fcsteem ei ole toetatud, sest kasutusel on vale Docker CGroupi versioon. Kasuta linki, et teada saada, milline on \u00f5ige versioon ja kuidas seda parandada.", + "title": "Toetamata s\u00fcsteem \u2013 CGroupi versioon" + }, + "unsupported_connectivity_check": { + "description": "S\u00fcsteem ei ole toetatud, sest Home Assistant ei suuda kindlaks teha, millal interneti\u00fchendus on saadaval. Kasuta linki, et rohkem teada saada ja kuidas seda parandada.", + "title": "S\u00fcsteemi ei toetata \u2013 \u00fchenduvuse kontroll on keelatud" + }, + "unsupported_content_trust": { + "description": "S\u00fcsteemi ei toetata, kuna Home Assistant ei saa kontrollida, kas k\u00e4itatav sisu on usaldusv\u00e4\u00e4rne ja seda pole r\u00fcndajad muutnud. Lisateabe saamiseks ja selle parandamiseks kasuta linki.", + "title": "Toetamata s\u00fcsteem \u2013 sisu usaldusv\u00e4\u00e4rsuse kontroll on keelatud" + }, + "unsupported_dbus": { + "description": "S\u00fcsteem ei ole toetatud, sest D-Bus t\u00f6\u00f6tab valesti. Paljud asjad ei \u00f5nnestu ilma selleta, sest Supervisor ei saa suhelda hostiga. Kasuta linki, et rohkem teada saada ja kuidas seda parandada.", + "title": "Toetamata s\u00fcsteem - D-Bus probleemid" + }, + "unsupported_dns_server": { + "description": "S\u00fcsteem ei ole toetatud, sest pakutav DNS-server ei t\u00f6\u00f6ta \u00f5igesti ja varu-DNS-variant on v\u00e4lja l\u00fclitatud. Kasuta linki, et rohkem teada saada ja kuidas seda parandada.", + "title": "Toetamata s\u00fcsteem \u2013 DNS-serveri probleemid" + }, + "unsupported_docker_configuration": { + "description": "S\u00fcsteem ei ole toetatud, sest Dockeri deemon t\u00f6\u00f6tab ootamatul viisil. Kasuta linki, et rohkem teada saada ja kuidas seda parandada.", + "title": "Toetamata s\u00fcsteem \u2013 Docker on valesti konfigureeritud" + }, + "unsupported_docker_version": { + "description": "S\u00fcsteemi ei toetata kuna kasutusel on vale Dockeri versioon. Kasuta linki, et saada teavet \u00f5ige versiooni ja selle parandamise kohta.", + "title": "Toetamata s\u00fcsteem \u2013 Dockeri versioon" + }, + "unsupported_job_conditions": { + "description": "S\u00fcsteemi ei toetata kuna \u00fcks v\u00f5i mitu t\u00f6\u00f6tingimust on blokeeritud, mis kaitsevad ootamatute rikete ja purunemiste eest. Kasuta linki, et saada lisateavet ja kuidas seda parandada.", + "title": "Toetamata s\u00fcsteem - kaitsed v\u00e4lja l\u00fclitatud" + }, + "unsupported_lxc": { + "description": "S\u00fcsteemi ei toetata, kuna seda k\u00e4itatakse LXC virtuaalmasinas. Lisateabe saamiseks ja selle parandamiseks kasuta linki.", + "title": "Toetamata s\u00fcsteem \u2013 tuvastati LXC" + }, + "unsupported_network_manager": { + "description": "S\u00fcsteemi ei toetata, kuna Network Manager puudub, on passiivne v\u00f5i valesti konfigureeritud. Lisateabe saamiseks ja selle parandamiseks kasuta linki.", + "title": "Toetamata s\u00fcsteem \u2013 v\u00f5rguhalduri probleemid" + }, + "unsupported_os": { + "description": "S\u00fcsteem ei ole toetatud, sest kasutatavat operatsioonis\u00fcsteemi ei ole testitud ega hooldatud Supervisoriga kasutamiseks. Kasuta linki, milliseid operatsioonis\u00fcsteeme toetatakse ja kuidas seda parandada.", + "title": "Toetamata s\u00fcsteem \u2013 operatsioonis\u00fcsteem" + }, + "unsupported_os_agent": { + "description": "S\u00fcsteem ei ole toetatud, sest OS-Agent puudub, on mitteaktiivne v\u00f5i valesti konfigureeritud. Kasuta linki, et rohkem teada saada ja kuidas seda parandada.", + "title": "Toetamata s\u00fcsteem \u2013 OS-agendi probleemid" + }, + "unsupported_restart_policy": { + "description": "S\u00fcsteem ei ole toetatud, sest Dockeri konteinerile on m\u00e4\u00e4ratud taask\u00e4ivitamise poliitika, mis v\u00f5ib k\u00e4ivitamisel probleeme tekitada. Kasuta linki, et rohkem teada saada ja kuidas seda parandada.", + "title": "Toetamata s\u00fcsteem \u2013 konteineri taask\u00e4ivitamise reegel" + }, + "unsupported_software": { + "description": "S\u00fcsteem ei ole toetatud, sest on tuvastatud lisatarkvara v\u00e4ljaspool Home Assistant'i \u00f6kos\u00fcsteemi. Kasuta linki, et rohkem teada saada ja kuidas seda parandada.", + "title": "Toetamata s\u00fcsteem \u2013 toetamata tarkvara" + }, + "unsupported_source_mods": { + "description": "S\u00fcsteem ei ole toetatud, sest Supervisori l\u00e4htekoodi on muudetud. Kasuta linki, et rohkem teada saada ja kuidas seda parandada.", + "title": "Toetamata s\u00fcsteem \u2013 Supervisori allika muudatused" + }, + "unsupported_supervisor_version": { + "description": "S\u00fcsteem ei ole toetatud, sest kasutusel on vananenud Supervisori versioon ja automaatne uuendamine on v\u00e4lja l\u00fclitatud. Kasuta linki, et saada rohkem teavet ja teada, kuidas seda parandada.", + "title": "Toetamata s\u00fcsteem - Supervisori versioon" + }, + "unsupported_systemd": { + "description": "S\u00fcsteemi ei toetata kuna Systemd puudub, on passiivne v\u00f5i valesti konfigureeritud. Lisateabe saamiseks ja selle parandamiseks kasuta linki.", + "title": "Toetamata s\u00fcsteem - Systemd probleemid" + }, + "unsupported_systemd_journal": { + "description": "S\u00fcsteemi ei toetata kuna Systemd Journal ja/v\u00f5i l\u00fc\u00fcsiteenus puudub, on passiivne v\u00f5i valesti konfigureeritud. Lisateabe saamiseks ja selle parandamiseks kasuta linki.", + "title": "Toetamata s\u00fcsteem \u2013 Systemd Journali probleemid" + }, + "unsupported_systemd_resolved": { + "description": "S\u00fcsteemi ei toetata kuna Systemd Resolved puudub, on passiivne v\u00f5i valesti konfigureeritud. Lisateabe saamiseks ja selle parandamiseks kasuta linki.", + "title": "Toetamata s\u00fcsteem \u2013 Systemd lahendatud probleemid" } }, "system_health": { diff --git a/homeassistant/components/hassio/translations/hu.json b/homeassistant/components/hassio/translations/hu.json index 604a8ae59e6..14f2d995ff6 100644 --- a/homeassistant/components/hassio/translations/hu.json +++ b/homeassistant/components/hassio/translations/hu.json @@ -1,12 +1,112 @@ { "issues": { "unhealthy": { - "description": "A rendszer jelenleg renellenes \u00e1llapotban van '{reason}' miatt. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet is megtudhat arr\u00f3l, hogy mi a probl\u00e9ma, \u00e9s hogyan jav\u00edthatja ki.", + "description": "A rendszer jelenleg rendellenes \u00e1llapotban van a k\u00f6vetkez\u0151 miatt: {reason}. Haszn\u00e1lja a linket, ha t\u00f6bbet szeretne megtudni, \u00e9s hogyan jav\u00edthatja meg.", "title": "Rendellenes \u00e1llapot \u2013 {reason}" }, + "unhealthy_docker": { + "description": "A rendszer jelenleg nem megfelel\u0151, mert a Docker helytelen\u00fcl van konfigur\u00e1lva. A link seg\u00edts\u00e9g\u00e9vel tov\u00e1bbi inform\u00e1ci\u00f3t \u00e9s a probl\u00e9ma megold\u00e1s\u00e1nak m\u00f3dj\u00e1t ismerheti meg.", + "title": "Rendellenes rendszer \u2013 A Docker rosszul lett konfigur\u00e1lva" + }, + "unhealthy_privileged": { + "description": "A rendszer jelenleg nem megfelel\u0151, mert nem rendelkezik emelt szint\u0171 hozz\u00e1f\u00e9r\u00e9ssel a Docker-futtat\u00f3k\u00f6rnyezethez. A link seg\u00edts\u00e9g\u00e9vel tov\u00e1bbi inform\u00e1ci\u00f3t \u00e9s a probl\u00e9ma megold\u00e1s\u00e1nak m\u00f3dj\u00e1t ismerheti meg.", + "title": "Rendellenes rendszer - Nem privilegiz\u00e1lt" + }, + "unhealthy_setup": { + "description": "A rendszer jelenleg nem megfelel\u0151, mert a telep\u00edt\u00e9s nem fejez\u0151d\u00f6tt be. Ennek sz\u00e1mos oka lehet, haszn\u00e1lja a linket, hogy t\u00f6bbet megtudjon, \u00e9s hogyan jav\u00edthatja ki ezt.", + "title": "Rendellenes rendszer \u2013 A telep\u00edt\u00e9s sikertelen" + }, + "unhealthy_supervisor": { + "description": "A rendszer jelenleg rendellenes \u00e1llapotban van, mert a Supervisor leg\u00fajabb verzi\u00f3ra t\u00f6rt\u00e9n\u0151 friss\u00edt\u00e9s\u00e9nek k\u00eds\u00e9rlete sikertelen volt. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet megtudhat, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Rendellenes rendszer \u2013 A Supervisor friss\u00edt\u00e9se nem siker\u00fclt" + }, + "unhealthy_untrusted": { + "description": "A rendszer jelenleg nem megfelel\u0151, mert nem megb\u00edzhat\u00f3 k\u00f3dot vagy haszn\u00e1latban l\u00e9v\u0151 image-et \u00e9szlelt. A link seg\u00edts\u00e9g\u00e9vel tov\u00e1bbi inform\u00e1ci\u00f3t \u00e9s a probl\u00e9ma megold\u00e1s\u00e1nak m\u00f3dj\u00e1t ismerheti meg.", + "title": "Rendellenes rendszer - Nem megb\u00edzhat\u00f3 k\u00f3d" + }, "unsupported": { - "description": "A rendszer nem t\u00e1mogatott a k\u00f6vetkez\u0151 miatt: '{reason}'. A hivatkoz\u00e1s seg\u00edts\u00e9g\u00e9vel t\u00f6bbet megtudhat arr\u00f3l, mit jelent ez, \u00e9s hogyan t\u00e9rhet vissza egy t\u00e1mogatott rendszerhez.", + "description": "A rendszer nem t\u00e1mogatott a k\u00f6vetkez\u0151 miatt: {reason} . Haszn\u00e1lja a linket, ha t\u00f6bbet szeretne megtudni, \u00e9s hogyan jav\u00edthatja meg.", "title": "Nem t\u00e1mogatott rendszer \u2013 {reason}" + }, + "unsupported_apparmor": { + "description": "A rendszer nem t\u00e1mogatott, mert az AppArmor helytelen\u00fcl m\u0171k\u00f6dik, \u00e9s a b\u0151v\u00edtm\u00e9nyek nem v\u00e9dett \u00e9s nem biztons\u00e1gos m\u00f3don futnak. A link seg\u00edts\u00e9g\u00e9vel tov\u00e1bbi inform\u00e1ci\u00f3t \u00e9s a probl\u00e9ma megold\u00e1s\u00e1nak m\u00f3dj\u00e1t ismerheti meg.", + "title": "Nem t\u00e1mogatott rendszer \u2013 AppArmor-probl\u00e9m\u00e1k" + }, + "unsupported_cgroup_version": { + "description": "A rendszer nem t\u00e1mogatott, mert a Docker CGroup nem megfelel\u0151 verzi\u00f3ja van haszn\u00e1latban. A link seg\u00edts\u00e9g\u00e9vel megtudhatja a helyes verzi\u00f3t \u00e9s a jav\u00edt\u00e1s m\u00f3dj\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer - CGroup verzi\u00f3" + }, + "unsupported_connectivity_check": { + "description": "A rendszer nem t\u00e1mogatott, mert az Otthoni asszisztens nem tudja meghat\u00e1rozni, hogy van-e m\u0171k\u00f6d\u0151 internetkapcsolat. A link seg\u00edts\u00e9g\u00e9vel tov\u00e1bbi inform\u00e1ci\u00f3t \u00e9s a probl\u00e9ma megold\u00e1s\u00e1nak m\u00f3dj\u00e1t ismerheti meg.", + "title": "Nem t\u00e1mogatott rendszer - Csatlakoz\u00e1si ellen\u0151rz\u00e9s letiltva" + }, + "unsupported_content_trust": { + "description": "A rendszer nem t\u00e1mogatott, mivel a Home Assistant nem tudja ellen\u0151rizni, hogy a futtatott tartalom megb\u00edzhat\u00f3 \u00e9s nem t\u00e1mad\u00f3k \u00e1ltal m\u00f3dos\u00edtott. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet tudhat meg, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer - Tartalom-megb\u00edzhat\u00f3s\u00e1gi ellen\u0151rz\u00e9s letiltva" + }, + "unsupported_dbus": { + "description": "A rendszer nem t\u00e1mogatott, mert a D-Bus hib\u00e1san m\u0171k\u00f6dik. E n\u00e9lk\u00fcl sok minden meghib\u00e1sodik, mivel a Supervisor nem tud kommunik\u00e1lni a rendszerrel. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet megtudhat, \u00e9s megtudhatja, hogyan lehet ezt kijav\u00edtani.", + "title": "Nem t\u00e1mogatott rendszer - D-Bus probl\u00e9m\u00e1k" + }, + "unsupported_dns_server": { + "description": "A rendszer nem t\u00e1mogatott, mert a megadott DNS-kiszolg\u00e1l\u00f3 nem m\u0171k\u00f6dik megfelel\u0151en, \u00e9s a tartal\u00e9k DNS opci\u00f3t letiltott\u00e1k. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet megtudhat, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer - DNS-kiszolg\u00e1l\u00f3 probl\u00e9m\u00e1k" + }, + "unsupported_docker_configuration": { + "description": "A rendszer nem t\u00e1mogatott, mert a Docker d\u00e9mon nem az elv\u00e1rt m\u00f3don fut. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet tudhat meg, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer \u2013 A Docker helytelen\u00fcl van konfigur\u00e1lva" + }, + "unsupported_docker_version": { + "description": "A rendszer nem t\u00e1mogatott, mert a Docker nem megfelel\u0151 verzi\u00f3ja van haszn\u00e1latban. A link seg\u00edts\u00e9g\u00e9vel megtudhatja a helyes verzi\u00f3t \u00e9s a jav\u00edt\u00e1s m\u00f3dj\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer - Docker verzi\u00f3" + }, + "unsupported_job_conditions": { + "description": "A rendszer nem t\u00e1mogatott, mert egy vagy t\u00f6bb feladatfelt\u00e9tel le van tiltva, amelyek v\u00e9delmet ny\u00fajtanak a v\u00e1ratlan hib\u00e1kt\u00f3l. A link seg\u00edts\u00e9g\u00e9vel tov\u00e1bbi inform\u00e1ci\u00f3t \u00e9s a probl\u00e9ma megold\u00e1s\u00e1nak m\u00f3dj\u00e1t ismerheti meg.", + "title": "Nem t\u00e1mogatott rendszer \u2013 A v\u00e9delem le van tiltva" + }, + "unsupported_lxc": { + "description": "A rendszer nem t\u00e1mogatott, mert LXC virtu\u00e1lis g\u00e9pben fut. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet tudhat meg, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer - LXC \u00e9szlelve" + }, + "unsupported_network_manager": { + "description": "A rendszer nem t\u00e1mogatott, mert a H\u00e1l\u00f3zatkezel\u0151 hi\u00e1nyzik, inakt\u00edv vagy rosszul van konfigur\u00e1lva. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet tudhat meg, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer - Network Manager probl\u00e9m\u00e1k" + }, + "unsupported_os": { + "description": "A rendszer nem t\u00e1mogatott, mert a haszn\u00e1lt oper\u00e1ci\u00f3s rendszert nem tesztelt\u00e9k vagy nem tartj\u00e1k karban a Supervisorral val\u00f3 haszn\u00e1latra. Haszn\u00e1lja a linket, hogy mely oper\u00e1ci\u00f3s rendszerek t\u00e1mogatottak \u00e9s hogyan lehet ezt kijav\u00edtani.", + "title": "Nem t\u00e1mogatott rendszer - Oper\u00e1ci\u00f3s rendszer" + }, + "unsupported_os_agent": { + "description": "A rendszer nem t\u00e1mogatott, mert az OS-\u00dcgyn\u00f6k (agent) hi\u00e1nyzik, inakt\u00edv vagy rosszul van konfigur\u00e1lva. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet tudhat meg, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer - OS-\u00dcgyn\u00f6k probl\u00e9m\u00e1k" + }, + "unsupported_restart_policy": { + "description": "A rendszer nem t\u00e1mogatott, mivel a Docker kont\u00e9nerben olyan \u00fajraind\u00edt\u00e1si h\u00e1zirend van be\u00e1ll\u00edtva, amely ind\u00edt\u00e1skor probl\u00e9m\u00e1kat okozhat. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet tudhat meg, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer - Kont\u00e9ner \u00fajraind\u00edt\u00e1si szab\u00e1lyzat" + }, + "unsupported_software": { + "description": "A rendszer nem t\u00e1mogatott, mert a rendszer a Home Assistant \u00f6kosziszt\u00e9m\u00e1n k\u00edv\u00fcli tov\u00e1bbi szoftvereket \u00e9szlelt. Haszn\u00e1lja a linket, ha t\u00f6bbet szeretne megtudni, \u00e9s hogyan jav\u00edthatja ezt.", + "title": "Rendellenes rendszer - Nem t\u00e1mogatott szoftver" + }, + "unsupported_source_mods": { + "description": "A rendszer nem t\u00e1mogatott, mert a Supervisor forr\u00e1sk\u00f3dj\u00e1t m\u00f3dos\u00edtott\u00e1k. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet megtudhat, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer - Supervisor forr\u00e1sm\u00f3dos\u00edt\u00e1sok" + }, + "unsupported_supervisor_version": { + "description": "A rendszer nem t\u00e1mogatott, mert a Supervisor egy elavult verzi\u00f3ja van haszn\u00e1latban, \u00e9s az automatikus friss\u00edt\u00e9s le van tiltva. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet megtudhat, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer - Supervisor verzi\u00f3" + }, + "unsupported_systemd": { + "description": "A rendszer nem t\u00e1mogatott, mert a Systemd hi\u00e1nyzik, inakt\u00edv vagy rosszul van konfigur\u00e1lva. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet tudhat meg, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer - Systemd probl\u00e9m\u00e1k" + }, + "unsupported_systemd_journal": { + "description": "A rendszer nem t\u00e1mogatott, mert a Systemd Journal \u00e9s/vagy az \u00e1tj\u00e1r\u00f3 szolg\u00e1ltat\u00e1s hi\u00e1nyzik, inakt\u00edv vagy rosszul van konfigur\u00e1lva . A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet megtudhat, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer - Systemd Journal probl\u00e9m\u00e1k" + }, + "unsupported_systemd_resolved": { + "description": "A rendszer nem t\u00e1mogatott, mert a Systemd Resolved hi\u00e1nyzik, inakt\u00edv vagy rosszul van konfigur\u00e1lva. A link seg\u00edts\u00e9g\u00e9vel t\u00f6bbet tudhat meg, \u00e9s megtudhatja, hogyan jav\u00edthatja ezt a hib\u00e1t.", + "title": "Nem t\u00e1mogatott rendszer \u2013 Systemd Resolved probl\u00e9m\u00e1k" } }, "system_health": { diff --git a/homeassistant/components/hassio/translations/id.json b/homeassistant/components/hassio/translations/id.json index 250e6e7d4ad..f18ae6f84c6 100644 --- a/homeassistant/components/hassio/translations/id.json +++ b/homeassistant/components/hassio/translations/id.json @@ -1,4 +1,114 @@ { + "issues": { + "unhealthy": { + "description": "Sistem saat ini tidak sehat karena {reason}. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak sehat - {reason}" + }, + "unhealthy_docker": { + "description": "Sistem saat ini tidak sehat karena Docker tidak dikonfigurasi dengan benar. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak sehat - Docker salah dikonfigurasi" + }, + "unhealthy_privileged": { + "description": "Sistem saat ini tidak sehat karena tidak memiliki akses istimewa ke docker runtime. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak sehat - Tidak memiliki akses istimewa" + }, + "unhealthy_setup": { + "description": "Sistem saat ini tidak sehat karena penyiapan gagal diselesaikan. Ada sejumlah alasan mengapa hal ini bisa terjadi, gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak sehat - Penyiapan gagal" + }, + "unhealthy_supervisor": { + "description": "Sistem saat ini tidak sehat karena upaya untuk memperbarui Supervisor ke versi terbaru telah gagal. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak sehat - Pembaruan supervisor gagal" + }, + "unhealthy_untrusted": { + "description": "Sistem saat ini tidak sehat karena telah mendeteksi kode atau gambar yang tidak dipercaya yang digunakan. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak sehat - Kode tidak tepercaya" + }, + "unsupported": { + "description": "Sistem tidak didukung karena {reason}. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - {reason}" + }, + "unsupported_apparmor": { + "description": "Sistem tidak didukung karena AppArmor tidak berfungsi dengan benar dan add-on berjalan dengan cara yang tidak terlindungi dan tidak aman. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Masalah AppArmor" + }, + "unsupported_cgroup_version": { + "description": "Sistem tidak didukung karena versi Docker CGroup yang digunakan salah. Gunakan tautan untuk mempelajari versi yang benar dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Versi CGroup" + }, + "unsupported_connectivity_check": { + "description": "Sistem tidak didukung karena Home Assistant tidak dapat menentukan kapan koneksi internet tersedia. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Pemeriksaan konektivitas dinonaktifkan" + }, + "unsupported_content_trust": { + "description": "Sistem tidak didukung karena Home Assistant tidak dapat memverifikasi konten yang sedang dijalankan adalah dipercaya dan tidak dimodifikasi oleh penyerang. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Pemeriksaan kepercayaan konten dinonaktifkan" + }, + "unsupported_dbus": { + "description": "Sistem tidak didukung karena D-Bus bekerja secara tidak benar. Banyak hal gagal tanpa D-Bus ini karena Supervisor tidak dapat berkomunikasi dengan host. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Masalah D-Bus" + }, + "unsupported_dns_server": { + "description": "Sistem tidak didukung karena server DNS yang disediakan tidak berfungsi dengan benar dan opsi DNS fallback telah dinonaktifkan. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Masalah server DNS" + }, + "unsupported_docker_configuration": { + "description": "Sistem tidak didukung karena daemon Docker berjalan dengan cara yang tidak terduga. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Docker salah konfigurasi" + }, + "unsupported_docker_version": { + "description": "Sistem tidak didukung karena versi Docker yang salah sedang digunakan. Gunakan tautan untuk mempelajari versi yang benar dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Versi Docker" + }, + "unsupported_job_conditions": { + "description": "Sistem tidak didukung karena satu atau beberapa kondisi pekerjaan telah dinonaktifkan, yang melindungi dari kegagalan dan kerusakan yang tidak terduga. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Perlindungan dinonaktifkan" + }, + "unsupported_lxc": { + "description": "Sistem tidak didukung karena dijalankan di mesin virtual LXC. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - LXC terdeteksi" + }, + "unsupported_network_manager": { + "description": "Sistem tidak didukung karena Network Manager tidak ada, tidak aktif atau salah dikonfigurasi. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Masalah Manajer Jaringan" + }, + "unsupported_os": { + "description": "Sistem tidak didukung karena sistem operasi yang digunakan tidak diuji atau dipelihara untuk digunakan dengan Supervisor. Gunakan tautan ke sistem operasi mana yang didukung dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Sistem Operasi" + }, + "unsupported_os_agent": { + "description": "Sistem tidak didukung karena OS-Agent tidak ada, tidak aktif, atau salah dikonfigurasi. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Masalah OS-Agent" + }, + "unsupported_restart_policy": { + "description": "Sistem tidak didukung karena kontainer Docker memiliki kebijakan mulai ulang yang ditetapkan, yang dapat menyebabkan masalah saat mulai. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Kebijakan mulai ulang kontainer" + }, + "unsupported_software": { + "description": "Sistem tidak didukung karena perangkat lunak tambahan di luar ekosistem Home Assistant telah terdeteksi. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Perangkat lunak tidak didukung" + }, + "unsupported_source_mods": { + "description": "Sistem tidak didukung karena kode sumber Supervisor telah dimodifikasi. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Modifikasi kode sumber Supervisor" + }, + "unsupported_supervisor_version": { + "description": "Sistem tidak didukung karena versi Supervisor yang kedaluwarsa sedang digunakan dan pembaruan otomatis telah dinonaktifkan. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Versi Supervisor" + }, + "unsupported_systemd": { + "description": "Sistem tidak didukung karena Systemd tidak ada, tidak aktif, atau salah dikonfigurasi. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Masalah Systemd" + }, + "unsupported_systemd_journal": { + "description": "Sistem tidak didukung karena Jurnal Systemd dan/atau layanan gateway tidak ada, tidak aktif, atau salah dikonfigurasi. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Masalah Journal Systemd" + }, + "unsupported_systemd_resolved": { + "description": "Sistem tidak didukung karena Systemd Resolved tidak ada, tidak aktif, atau salah dikonfigurasi. Gunakan tautan untuk mempelajari lebih lanjut dan cara memperbaikinya.", + "title": "Sistem tidak didukung - Masalah Systemd-Resolved" + } + }, "system_health": { "info": { "agent_version": "Versi Agen", diff --git a/homeassistant/components/hassio/translations/it.json b/homeassistant/components/hassio/translations/it.json index c305b9fce34..20460b1d4dc 100644 --- a/homeassistant/components/hassio/translations/it.json +++ b/homeassistant/components/hassio/translations/it.json @@ -1,12 +1,112 @@ { "issues": { "unhealthy": { - "description": "Il sistema non \u00e8 attualmente integro a causa di '{reason}'. Usa il collegamento per saperne di pi\u00f9 sul problema e su come risolverlo.", + "description": "Il sistema non \u00e8 attualmente integro a causa di {reason}. Usa il collegamento per saperne di pi\u00f9 e come risolvere il problema.", "title": "Sistema non pi\u00f9 integro - {reason}" }, + "unhealthy_docker": { + "description": "Il sistema non \u00e8 attualmente integro perch\u00e9 Docker \u00e8 configurato in modo errato. Usa il link per saperne di pi\u00f9 e come risolvere questo problema.", + "title": "Sistema non integro - Docker configurato in modo errato" + }, + "unhealthy_privileged": { + "description": "Il sistema non \u00e8 attualmente integro perch\u00e9 non ha accesso privilegiato al runtime docker. Usa il link per saperne di pi\u00f9 e come risolvere questo problema.", + "title": "Sistema non integro - Non privilegiato" + }, + "unhealthy_setup": { + "description": "Il sistema non \u00e8 attualmente integro perch\u00e9 l'installazione non \u00e8 stata completata. Ci sono una serie di ragioni per cui ci\u00f2 pu\u00f2 verificarsi, usa il link per saperne di pi\u00f9 e come risolverlo.", + "title": "Sistema non integro - Installazione non riuscita" + }, + "unhealthy_supervisor": { + "description": "Il sistema non \u00e8 attualmente integro perch\u00e9 un tentativo di aggiornare Supervisor all'ultima versione non \u00e8 riuscito. Utilizza il collegamento per saperne di pi\u00f9 e come risolvere questo problema.", + "title": "Sistema non integro - Aggiornamento del Supervisor non riuscito" + }, + "unhealthy_untrusted": { + "description": "Il sistema non \u00e8 attualmente integro perch\u00e9 ha rilevato codice o immagini in uso non attendibili. Utilizza il collegamento per saperne di pi\u00f9 e come risolvere questo problema.", + "title": "Sistema non integro - Codice non attendibile" + }, "unsupported": { - "description": "Il sistema non \u00e8 supportato a causa di '{reason}'. Utilizzare il collegamento per ulteriori informazioni sul significato e su come tornare a un sistema supportato.", + "description": "Il sistema non \u00e8 supportato a causa di {reason}. Utilizzare il collegamento per ulteriori informazioni sul significato e su come tornare a un sistema supportato.", "title": "Sistema non supportato - {reason}" + }, + "unsupported_apparmor": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 AppArmor non funziona correttamente e i componenti aggiuntivi sono eseguiti in modo non protetto e non sicuro. Utilizza il collegamento per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Problemi con AppArmor" + }, + "unsupported_cgroup_version": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 \u00e8 in uso una versione errata di Docker CGroup. Utilizza il collegamento per conoscere la versione corretta e come risolvere il problema.", + "title": "Sistema non supportato - Versione di CGroup" + }, + "unsupported_connectivity_check": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 Home Assistant non \u00e8 in grado di determinare quando \u00e8 disponibile una connessione a Internet. Utilizza il collegamento per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Controllo connettivit\u00e0 disabilitato" + }, + "unsupported_content_trust": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 Home Assistant non \u00e8 in grado di verificare che il contenuto in esecuzione sia attendibile e non modificato da malintenzionati. Utilizza il collegamento per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Controllo dell'attendibilit\u00e0 del contenuto disabilitato" + }, + "unsupported_dbus": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 D-Bus non funziona correttamente. Molte cose non funzionano senza di esso perch\u00e9 il Supervisor non pu\u00f2 comunicare con l'host. Utilizza il collegamento per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Problemi con D-Bus" + }, + "unsupported_dns_server": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 il server DNS fornito non funziona correttamente e l'opzione DNS di fallback \u00e8 stata disattivata. Utilizza il collegamento per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Problemi con il server DNS" + }, + "unsupported_docker_configuration": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 il daemon Docker viene eseguito in modo imprevisto. Utilizza il collegamento per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Docker configurato in modo errato" + }, + "unsupported_docker_version": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 \u00e8 in uso una versione errata di Docker. Utilizza il collegamento per conoscere la versione corretta e come risolvere il problema.", + "title": "Sistema non supportato - Versione Docker" + }, + "unsupported_job_conditions": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 sono state disattivate una o pi\u00f9 condizioni di lavoro che proteggono da guasti e rotture impreviste. Utilizza il collegamento per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Protezioni disabilitate" + }, + "unsupported_lxc": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 viene eseguito in una macchina virtuale LXC. Utilizza il link per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - rilevato LXC" + }, + "unsupported_network_manager": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 Network Manager \u00e8 mancante, inattivo o mal configurato. Utilizza il link per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Problemi con Network Manager" + }, + "unsupported_os": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 il sistema operativo in uso non \u00e8 stato testato o mantenuto per l'uso con Supervisor. Utilizza il link per sapere quali sistemi operativi sono supportati e come risolvere il problema.", + "title": "Sistema non supportato - Sistema operativo" + }, + "unsupported_os_agent": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 OS-Agent \u00e8 mancante, inattivo o mal configurato. Utilizza il link per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Problemi con OS-Agent" + }, + "unsupported_restart_policy": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 un container Docker ha impostato un criterio di riavvio che potrebbe causare problemi all'avvio. Utilizza il link per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Criterio di riavvio del Container" + }, + "unsupported_software": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 \u00e8 stato rilevato un software aggiuntivo esterno all'ecosistema Home Assistant. Utilizza il link per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Software non supportato" + }, + "unsupported_source_mods": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 il codice sorgente del Supervisor \u00e8 stato modificato. Utilizza il link per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Modifiche al codice sorgente del Supervisor" + }, + "unsupported_supervisor_version": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 \u00e8 in uso una versione non aggiornata del Supervisor e l'aggiornamento automatico \u00e8 stato disabilitato. Utilizza il link per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Versione Supervisor" + }, + "unsupported_systemd": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 Systemd \u00e8 mancante, inattivo o mal configurato. Utilizza il link per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Problemi con Systemd" + }, + "unsupported_systemd_journal": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 Systemd Journal e/o il servizio gateway sono mancanti, inattivi o mal configurati. Utilizza il link per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Problemi con Systemd Journal" + }, + "unsupported_systemd_resolved": { + "description": "Il sistema non \u00e8 supportato perch\u00e9 Systemd Resolved \u00e8 mancante, inattivo o mal configurato. Utilizza il link per saperne di pi\u00f9 e per capire come risolvere il problema.", + "title": "Sistema non supportato - Problemi con Systemd Resolved" } }, "system_health": { diff --git a/homeassistant/components/hassio/translations/no.json b/homeassistant/components/hassio/translations/no.json index 8dd5d471860..ee5d5328085 100644 --- a/homeassistant/components/hassio/translations/no.json +++ b/homeassistant/components/hassio/translations/no.json @@ -1,12 +1,112 @@ { "issues": { "unhealthy": { - "description": "Systemet er for \u00f8yeblikket usunt p\u00e5 grunn av '{reason}'. Bruk linken for \u00e5 l\u00e6re mer om hva som er galt og hvordan du kan fikse det.", + "description": "Systemet er for \u00f8yeblikket usunt p\u00e5 grunn av {reason} . Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", "title": "Usunt system \u2013 {reason}" }, + "unhealthy_docker": { + "description": "Systemet er for \u00f8yeblikket usunt fordi Docker er feil konfigurert. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "Usunt system - Docker feilkonfigurert" + }, + "unhealthy_privileged": { + "description": "Systemet er for \u00f8yeblikket usunt fordi det ikke har privilegert tilgang til docker-kj\u00f8retiden. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "Usunt system - Ikke privilegert" + }, + "unhealthy_setup": { + "description": "Systemet er for \u00f8yeblikket usunt fordi oppsettet ikke ble fullf\u00f8rt. Det er flere grunner til at dette kan skje. Bruk lenken for \u00e5 l\u00e6re mer og hvordan du kan fikse dette.", + "title": "Usunt system - Konfigurasjonen mislyktes" + }, + "unhealthy_supervisor": { + "description": "Systemet er for \u00f8yeblikket usunt fordi et fors\u00f8k p\u00e5 \u00e5 oppdatere Supervisor til den nyeste versjonen har mislyktes. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "Usunt system - Supervisor-oppdatering mislyktes" + }, + "unhealthy_untrusted": { + "description": "Systemet er for \u00f8yeblikket usunt fordi det har oppdaget uklarert kode eller bilder som er i bruk. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "Usunt system - Ubetrodd kode" + }, "unsupported": { - "description": "Systemet st\u00f8ttes ikke p\u00e5 grunn av '{reason}'. Bruk lenken for \u00e5 l\u00e6re mer om hva dette betyr og hvordan du g\u00e5r tilbake til et st\u00f8ttet system.", + "description": "Systemet st\u00f8ttes ikke p\u00e5 grunn av {reason} . Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", "title": "Systemet st\u00f8ttes ikke \u2013 {reason}" + }, + "unsupported_apparmor": { + "description": "Systemet st\u00f8ttes ikke fordi AppArmor fungerer feil og tillegg kj\u00f8rer p\u00e5 en ubeskyttet og usikker m\u00e5te. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 AppArmor-problemer" + }, + "unsupported_cgroup_version": { + "description": "Systemet st\u00f8ttes ikke fordi feil versjon av Docker CGroup er i bruk. Bruk linken for \u00e5 l\u00e6re riktig versjon og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes - CGroup-versjon" + }, + "unsupported_connectivity_check": { + "description": "Systemet st\u00f8ttes ikke fordi Home Assistant ikke kan fastsl\u00e5 n\u00e5r en Internett-tilkobling er tilgjengelig. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 tilkoblingskontroll er deaktivert" + }, + "unsupported_content_trust": { + "description": "Systemet st\u00f8ttes ikke fordi Home Assistant ikke kan bekrefte at innhold som kj\u00f8res er klarert og ikke endret av angripere. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 kontroll av innhold og klarering er deaktivert" + }, + "unsupported_dbus": { + "description": "Systemet st\u00f8ttes ikke fordi D-Bus fungerer feil. Mange ting feiler uten dette da veileder ikke kan kommunisere med verten. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "Ust\u00f8ttet system - D-Bus-problemer" + }, + "unsupported_dns_server": { + "description": "Systemet st\u00f8ttes ikke fordi den angitte DNS-serveren ikke fungerer som den skal og alternativet for reserve-DNS er deaktivert. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 DNS-serverproblemer" + }, + "unsupported_docker_configuration": { + "description": "Systemet st\u00f8ttes ikke fordi Docker-demonen kj\u00f8rer p\u00e5 en uventet m\u00e5te. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 Docker er feilkonfigurert" + }, + "unsupported_docker_version": { + "description": "Systemet st\u00f8ttes ikke fordi feil versjon av Docker er i bruk. Bruk linken for \u00e5 l\u00e6re riktig versjon og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 Docker-versjon" + }, + "unsupported_job_conditions": { + "description": "Systemet st\u00f8ttes ikke fordi en eller flere jobbbetingelser er deaktivert som beskytter mot uventede feil og brudd. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "Systemet som ikke st\u00f8ttes \u2013 Beskyttelse er deaktivert" + }, + "unsupported_lxc": { + "description": "Systemet st\u00f8ttes ikke fordi det kj\u00f8res i en virtuell LXC-maskin. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 LXC oppdaget" + }, + "unsupported_network_manager": { + "description": "Systemet st\u00f8ttes ikke fordi Network Manager mangler, er inaktiv eller feilkonfigurert. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 problemer med Network Manager" + }, + "unsupported_os": { + "description": "Systemet st\u00f8ttes ikke fordi operativsystemet som er i bruk ikke er testet eller vedlikeholdt for bruk med Supervisor. Bruk lenken til hvilke operativsystemer som st\u00f8ttes og hvordan du fikser dette.", + "title": "Ikke-st\u00f8ttet system - Operativsystem" + }, + "unsupported_os_agent": { + "description": "Systemet st\u00f8ttes ikke fordi OS-Agent mangler, er inaktivt eller feilkonfigurert. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 OS-Agent-problemer" + }, + "unsupported_restart_policy": { + "description": "Systemet st\u00f8ttes ikke fordi en Docker-beholder har et omstartspolicysett som kan for\u00e5rsake problemer ved oppstart. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 policy for omstart av container" + }, + "unsupported_software": { + "description": "Systemet st\u00f8ttes ikke fordi tilleggsprogramvare utenfor Home Assistant-\u00f8kosystemet er oppdaget. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "Systemet som ikke st\u00f8ttes \u2013 programvare som ikke st\u00f8ttes" + }, + "unsupported_source_mods": { + "description": "Systemet st\u00f8ttes ikke fordi Supervisor-kildekoden er endret. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 endringer i veilederkilder" + }, + "unsupported_supervisor_version": { + "description": "Systemet st\u00f8ttes ikke fordi en utdatert versjon av Supervisor er i bruk og automatisk oppdatering er deaktivert. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes - Supervisor-versjon" + }, + "unsupported_systemd": { + "description": "Systemet st\u00f8ttes ikke fordi Systemd mangler, er inaktivt eller er feilkonfigurert. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 Systemd-problemer" + }, + "unsupported_systemd_journal": { + "description": "Systemet st\u00f8ttes ikke fordi Systemd Journal og/eller gatewaytjenesten mangler, er inaktiv eller feilkonfigurert . Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "Systemet som ikke st\u00f8ttes \u2013 Systemd Journal-problemer" + }, + "unsupported_systemd_resolved": { + "description": "Systemet st\u00f8ttes ikke fordi Systemd Resolved mangler, er inaktivt eller feilkonfigurert. Bruk linken for \u00e5 l\u00e6re mer og hvordan du fikser dette.", + "title": "System som ikke st\u00f8ttes \u2013 Systemd-l\u00f8ste problemer" } }, "system_health": { diff --git a/homeassistant/components/hassio/translations/pt-BR.json b/homeassistant/components/hassio/translations/pt-BR.json index 47e0b6df4ae..9696b9ca2bb 100644 --- a/homeassistant/components/hassio/translations/pt-BR.json +++ b/homeassistant/components/hassio/translations/pt-BR.json @@ -1,12 +1,112 @@ { "issues": { "unhealthy": { - "description": "O sistema n\u00e3o est\u00e1 \u00edntegro devido a '{reason}'. Use o link para saber mais sobre o que est\u00e1 errado e como corrigi-lo.", + "description": "O sistema n\u00e3o est\u00e1 \u00edntegro devido a {reason}. Use o link para saber mais e como corrigir isso.", "title": "Sistema insalubre - {reason}" }, + "unhealthy_docker": { + "description": "O sistema n\u00e3o est\u00e1 \u00edntegro no momento porque o Docker est\u00e1 configurado incorretamente. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o \u00edntegro - Docker mal configurado" + }, + "unhealthy_privileged": { + "description": "O sistema n\u00e3o est\u00e1 \u00edntegro no momento porque n\u00e3o tem acesso privilegiado ao tempo de execu\u00e7\u00e3o do docker. Use o link para saber mais e como corrigir isso.", + "title": "Sistema insalubre - N\u00e3o privilegiado" + }, + "unhealthy_setup": { + "description": "O sistema n\u00e3o est\u00e1 \u00edntegro no momento porque a instala\u00e7\u00e3o n\u00e3o foi conclu\u00edda. Existem v\u00e1rias raz\u00f5es pelas quais isso pode ocorrer, use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o \u00edntegro - Falha na configura\u00e7\u00e3o" + }, + "unhealthy_supervisor": { + "description": "O sistema n\u00e3o est\u00e1 \u00edntegro no momento porque uma tentativa de atualizar o Supervisor para a vers\u00e3o mais recente falhou. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o \u00edntegro - Falha na atualiza\u00e7\u00e3o do supervisor" + }, + "unhealthy_untrusted": { + "description": "O sistema n\u00e3o est\u00e1 \u00edntegro no momento porque detectou c\u00f3digo ou imagens n\u00e3o confi\u00e1veis em uso. Use o link para saber mais e como corrigir isso.", + "title": "Sistema insalubre - C\u00f3digo n\u00e3o confi\u00e1vel" + }, "unsupported": { - "description": "O sistema n\u00e3o \u00e9 suportado devido a '{reason}'. Use o link para saber mais sobre o que isso significa e como retornar a um sistema compat\u00edvel.", + "description": "O sistema n\u00e3o \u00e9 compat\u00edvel devido a {reason}. Use o link para saber mais e como corrigir isso.", "title": "Sistema n\u00e3o suportado - {reason}" + }, + "unsupported_apparmor": { + "description": "O sistema n\u00e3o \u00e9 compat\u00edvel porque o AppArmor est\u00e1 funcionando incorretamente e os complementos est\u00e3o sendo executados de maneira desprotegida e insegura. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Problemas no AppArmor" + }, + "unsupported_cgroup_version": { + "description": "O sistema n\u00e3o \u00e9 compat\u00edvel porque a vers\u00e3o errada do Docker CGroup est\u00e1 em uso. Use o link para saber a vers\u00e3o correta e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Vers\u00e3o do CGroup" + }, + "unsupported_connectivity_check": { + "description": "O sistema n\u00e3o \u00e9 compat\u00edvel porque o Home Assistant n\u00e3o pode determinar quando uma conex\u00e3o com a Internet est\u00e1 dispon\u00edvel. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Verifica\u00e7\u00e3o de conectividade desativada" + }, + "unsupported_content_trust": { + "description": "O sistema n\u00e3o \u00e9 compat\u00edvel porque o Home Assistant n\u00e3o pode verificar se o conte\u00fado em execu\u00e7\u00e3o \u00e9 confi\u00e1vel e n\u00e3o foi modificado por invasores. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Verifica\u00e7\u00e3o de confian\u00e7a de conte\u00fado desabilitada" + }, + "unsupported_dbus": { + "description": "O sistema n\u00e3o \u00e9 suportado porque o D-Bus est\u00e1 funcionando incorretamente. Muitas coisas falham sem isso, pois o Supervisor n\u00e3o pode se comunicar com o host. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Problemas de D-Bus" + }, + "unsupported_dns_server": { + "description": "O sistema n\u00e3o \u00e9 compat\u00edvel porque o servidor DNS fornecido n\u00e3o funciona corretamente e a op\u00e7\u00e3o DNS de fallback foi desativada. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Problemas no servidor DNS" + }, + "unsupported_docker_configuration": { + "description": "O sistema n\u00e3o tem suporte porque o daemon do Docker est\u00e1 sendo executado de maneira inesperada. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Docker configurado incorretamente" + }, + "unsupported_docker_version": { + "description": "O sistema n\u00e3o \u00e9 compat\u00edvel porque a vers\u00e3o errada do Docker est\u00e1 em uso. Use o link para saber a vers\u00e3o correta e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Vers\u00e3o do Docker" + }, + "unsupported_job_conditions": { + "description": "O sistema n\u00e3o \u00e9 suportado porque uma ou mais condi\u00e7\u00f5es de trabalho foram desativadas, protegendo contra falhas e quebras inesperadas. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Prote\u00e7\u00f5es desativadas" + }, + "unsupported_lxc": { + "description": "O sistema n\u00e3o \u00e9 suportado porque est\u00e1 sendo executado em uma m\u00e1quina virtual LXC. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - LXC detectado" + }, + "unsupported_network_manager": { + "description": "O sistema n\u00e3o \u00e9 suportado porque o Network Manager est\u00e1 ausente, inativo ou configurado incorretamente. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Problemas no Network Manager" + }, + "unsupported_os": { + "description": "O sistema n\u00e3o \u00e9 compat\u00edvel porque o sistema operacional em uso n\u00e3o foi testado ou mantido para uso com o Supervisor. Use o link para quais sistemas operacionais s\u00e3o suportados e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Sistema operacional" + }, + "unsupported_os_agent": { + "description": "O sistema n\u00e3o \u00e9 suportado porque o OS Agent est\u00e1 ausente, inativo ou configurado incorretamente. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Problemas com o OS Agent" + }, + "unsupported_restart_policy": { + "description": "O sistema n\u00e3o \u00e9 compat\u00edvel porque um cont\u00eainer do Docker tem uma pol\u00edtica de reinicializa\u00e7\u00e3o definida que pode causar problemas na inicializa\u00e7\u00e3o. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o compat\u00edvel - Pol\u00edtica de reinicializa\u00e7\u00e3o de cont\u00eainer" + }, + "unsupported_software": { + "description": "O sistema n\u00e3o \u00e9 compat\u00edvel porque foi detectado software adicional fora do ecossistema do Home Assistant. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Software n\u00e3o suportado" + }, + "unsupported_source_mods": { + "description": "O sistema n\u00e3o \u00e9 suportado porque o c\u00f3digo-fonte do Supervisor foi modificado. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Modifica\u00e7\u00f5es de origem do supervisor" + }, + "unsupported_supervisor_version": { + "description": "O sistema n\u00e3o \u00e9 suportado porque uma vers\u00e3o desatualizada do Supervisor est\u00e1 em uso e a atualiza\u00e7\u00e3o autom\u00e1tica foi desabilitada. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - Vers\u00e3o do Supervisor" + }, + "unsupported_systemd": { + "description": "O sistema n\u00e3o \u00e9 suportado porque o Systemd est\u00e1 ausente, inativo ou configurado incorretamente. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - problemas no Systemd" + }, + "unsupported_systemd_journal": { + "description": "O sistema n\u00e3o \u00e9 suportado porque o Systemd Journal e/ou o servi\u00e7o de gateway est\u00e1 ausente, inativo ou configurado incorretamente. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - problemas do Systemd Journal" + }, + "unsupported_systemd_resolved": { + "description": "O sistema n\u00e3o \u00e9 suportado porque o Systemd Resolved est\u00e1 ausente, inativo ou configurado incorretamente. Use o link para saber mais e como corrigir isso.", + "title": "Sistema n\u00e3o suportado - problemas resolvidos pelo Systemd" } }, "system_health": { diff --git a/homeassistant/components/hassio/translations/ru.json b/homeassistant/components/hassio/translations/ru.json index 0ab366c1775..e41f77b51ef 100644 --- a/homeassistant/components/hassio/translations/ru.json +++ b/homeassistant/components/hassio/translations/ru.json @@ -1,12 +1,112 @@ { "issues": { "unhealthy": { - "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0435\u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u0430 \u043f\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 '{reason}'. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u0447\u0442\u043e \u043d\u0435 \u0442\u0430\u043a \u0438 \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", - "title": "\u041d\u0435\u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - {reason}" + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0430 \u043f\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 {reason}. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u0447\u0442\u043e \u043d\u0435 \u0442\u0430\u043a \u0438 \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - {reason}" + }, + "unhealthy_docker": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0430, \u0442\u0430\u043a \u043a\u0430\u043a Docker \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Docker" + }, + "unhealthy_privileged": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0430, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u043f\u0440\u0438\u0432\u0438\u043b\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u0440\u0435\u0434\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Docker. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043d\u0435\u0442 \u043f\u0440\u0438\u0432\u0438\u043b\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430" + }, + "unhealthy_setup": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0430, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0442\u0438 \u043f\u043e \u0440\u044f\u0434\u0443 \u043f\u0440\u0438\u0447\u0438\u043d, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u0441\u0431\u043e\u0439 \u043f\u0440\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435" + }, + "unhealthy_supervisor": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0430, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c Supervisor \u0434\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u0441\u0431\u043e\u0439 \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 Supervisor" + }, + "unhealthy_untrusted": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0430, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043d\u0435\u0434\u043e\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u043b\u0438 \u043e\u0431\u0440\u0430\u0437\u044b. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043d\u0435\u0434\u043e\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u0434" }, "unsupported": { - "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 '{reason}'. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u0447\u0442\u043e \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442 \u0438 \u043a\u0430\u043a \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435.", - "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 - {reason}" + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 {reason}. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u0447\u0442\u043e \u043d\u0435 \u0442\u0430\u043a \u0438 \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 {reason}" + }, + "unsupported_apparmor": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 AppArmor \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e, \u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0442\u0441\u044f \u043d\u0435\u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u043c \u0438 \u043d\u0435\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 AppArmor" + }, + "unsupported_cgroup_version": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f Docker CGroup. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u0432\u0435\u0440\u0441\u0438\u044f CGroup" + }, + "unsupported_connectivity_check": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 Home Assistant \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u043b\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0443. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f" + }, + "unsupported_content_trust": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 Home Assistant \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0434\u043e\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u043c \u0438 \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0451\u043d \u0437\u043b\u043e\u0443\u043c\u044b\u0448\u043b\u0435\u043d\u043d\u0438\u043a\u0430\u043c\u0438. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430" + }, + "unsupported_dbus": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 D-Bus \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e. \u041e\u0442 \u044d\u0442\u043e\u0433\u043e Supervisor \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0441\u0432\u044f\u0437\u0430\u0442\u044c\u0441\u044f \u0441 \u0445\u043e\u0441\u0442\u043e\u043c \u0438 \u043c\u043d\u043e\u0433\u043e\u0435 \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 D-Bus" + }, + "unsupported_dns_server": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 DNS-\u0441\u0435\u0440\u0432\u0435\u0440 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e, \u0430 DNS fallback \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 DNS-\u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c" + }, + "unsupported_docker_configuration": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0441\u043b\u0443\u0436\u0431\u0430 Docker \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0435\u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Docker" + }, + "unsupported_docker_version": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f Docker. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u0432\u0435\u0440\u0441\u0438\u044f Docker" + }, + "unsupported_job_conditions": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e \u043e\u0434\u043d\u043e \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443\u0441\u043b\u043e\u0432\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u0430\u0449\u0438\u0449\u0430\u044e\u0442 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043e\u0442 \u043d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u044b\u0445 \u0441\u0431\u043e\u0435\u0432. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u0437\u0430\u0449\u0438\u0442\u0430" + }, + "unsupported_lxc": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u043d\u0430 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u0430 \u043d\u0430 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u0435 LXC. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0430 LXC" + }, + "unsupported_network_manager": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 Network Manager \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442, \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d \u0438\u043b\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 Network Manager" + }, + "unsupported_os": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0430\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0430\u0441\u044c \u0438 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Supervisor. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430" + }, + "unsupported_os_agent": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 OS-Agent \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442, \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d \u0438\u043b\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 OS-Agent" + }, + "unsupported_restart_policy": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435 Docker \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430 \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0430 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0430 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430" + }, + "unsupported_software": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0435 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u0435, \u043d\u0435 \u0432\u0445\u043e\u0434\u044f\u0449\u0435\u0435 \u0432 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0443 Home Assistant. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u043e\u0435 \u041f\u041e" + }, + "unsupported_source_mods": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 Supervisor \u0431\u044b\u043b \u0438\u0437\u043c\u0435\u043d\u0451\u043d. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043c\u043e\u0434\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 Supervisor" + }, + "unsupported_supervisor_version": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f Supervisor, \u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u0432\u0435\u0440\u0441\u0438\u044f Supervisor" + }, + "unsupported_systemd": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 Systemd \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442, \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d \u0438\u043b\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 Systemd" + }, + "unsupported_systemd_journal": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0436\u0443\u0440\u043d\u0430\u043b Systemd \u0438/\u0438\u043b\u0438 \u0441\u043b\u0443\u0436\u0431\u0430 \u0448\u043b\u044e\u0437\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442, \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0438\u043b\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u0436\u0443\u0440\u043d\u0430\u043b\u043e\u043c Systemd" + }, + "unsupported_systemd_resolved": { + "description": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 Systemd Resolved \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442, \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d \u0438\u043b\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.", + "title": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u2014 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 Systemd-Resolved" } }, "system_health": { diff --git a/homeassistant/components/hassio/translations/zh-Hant.json b/homeassistant/components/hassio/translations/zh-Hant.json index 8194ea37e6d..e2df1d8f916 100644 --- a/homeassistant/components/hassio/translations/zh-Hant.json +++ b/homeassistant/components/hassio/translations/zh-Hant.json @@ -1,12 +1,112 @@ { "issues": { "unhealthy": { - "description": "System is currently unhealthy \u7531\u65bc '{reason}' \u7de3\u6545\u3001\u7cfb\u7d71\u76ee\u524d\u88ab\u8a8d\u70ba\u4e0d\u5065\u5eb7\u3002\u8acb\u53c3\u8003\u9023\u7d50\u4ee5\u4e86\u89e3\u54ea\u88e1\u51fa\u4e86\u554f\u984c\u3001\u53ca\u5982\u4f55\u9032\u884c\u4fee\u6b63\u3002", + "description": "\u7531\u65bc {reason}\u3001\u7cfb\u7d71\u76ee\u524d\u88ab\u8a8d\u70ba\u4e0d\u5065\u5eb7\u3002\u8acb\u53c3\u8003\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", "title": "\u4e0d\u5065\u5eb7\u7cfb\u7d71 - {reason}" }, + "unhealthy_docker": { + "description": "\u7531\u65bc Docker \u672a\u6b63\u78ba\u8a2d\u5b9a\u3001\u7cfb\u7d71\u76ee\u524d\u88ab\u8a8d\u70ba\u4e0d\u5065\u5eb7\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u5065\u5eb7\u7cfb\u7d71 - Docker \u8a2d\u5b9a\u932f\u8aa4" + }, + "unhealthy_privileged": { + "description": "\u7531\u65bc docker runtime \u672a\u7372\u5f97\u5b58\u53d6\u6b0a\u9650\u3001\u7cfb\u7d71\u76ee\u524d\u88ab\u8a8d\u70ba\u4e0d\u5065\u5eb7\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u5065\u5eb7\u7cfb\u7d71 - \u672a\u53d6\u5f97\u6b0a\u9650" + }, + "unhealthy_setup": { + "description": "\u7531\u65bc\u8a2d\u5b9a\u5931\u6557\u7121\u6cd5\u5b8c\u6210\u3001\u7cfb\u7d71\u76ee\u524d\u88ab\u8a8d\u70ba\u4e0d\u5065\u5eb7\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u5065\u5eb7\u7cfb\u7d71 - \u8a2d\u5b9a\u5931\u6557" + }, + "unhealthy_supervisor": { + "description": "\u7531\u65bc\u8a66\u5716\u66f4\u65b0\u81f3\u6700\u65b0\u7248\u672c Supervisor \u5931\u6557\u3001\u7cfb\u7d71\u76ee\u524d\u88ab\u8a8d\u70ba\u4e0d\u5065\u5eb7\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u5065\u5eb7\u7cfb\u7d71 - Supervisor \u66f4\u65b0\u5931\u6557" + }, + "unhealthy_untrusted": { + "description": "\u7531\u65bc\u767c\u73fe\u4f7f\u4f7f\u7528\u4e0d\u53d7\u4fe1\u4efb\u7684\u7a0b\u5f0f\u78bc\u6216\u5716\u50cf\u3001\u7cfb\u7d71\u76ee\u524d\u88ab\u8a8d\u70ba\u4e0d\u5065\u5eb7\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u5065\u5eb7\u7cfb\u7d71 - \u4e0d\u53d7\u4fe1\u4efb\u4e4b\u7a0b\u5f0f\u78bc" + }, "unsupported": { - "description": "System is unsupported \u7531\u65bc '{reason}' \u7de3\u6545\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u8003\u9023\u7d50\u4ee5\u4e86\u89e3\u76f8\u95dc\u8aaa\u660e\u3001\u53ca\u5982\u4f55\u56de\u5fa9\u81f3\u652f\u63f4\u7cfb\u7d71\u3002", + "description": "\u7531\u65bc {reason}\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u8003\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - {reason}" + }, + "unsupported_apparmor": { + "description": "\u7531\u65bc AppArmor \u672a\u6b63\u5e38\u5de5\u4f5c\u3001\u9644\u52a0\u5143\u4ef6\u4ee5\u672a\u53d7\u4fdd\u8b77\u53ca\u4e0d\u5b89\u5168\u65b9\u5f0f\u57f7\u884c\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - AppArmor \u554f\u984c" + }, + "unsupported_cgroup_version": { + "description": "\u7531\u65bc\u4f7f\u7528\u932f\u8aa4\u7248\u672c\u4e4b Docker CGroup\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - CGroup \u7248\u672c" + }, + "unsupported_connectivity_check": { + "description": "\u7531\u65bc Home Assistant \u7121\u6cd5\u78ba\u5b9a\u7db2\u969b\u7db2\u8def\u9023\u7dda\u662f\u5426\u53ef\u7528\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u7372\u5f97\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - \u9023\u7dda\u6aa2\u67e5\u5df2\u95dc\u9589" + }, + "unsupported_content_trust": { + "description": "\u7531\u65bc Home Assistant \u7121\u6cd5\u9a57\u8b49\u57f7\u884c\u7684\u70ba\u4fe1\u4efb\u5167\u5bb9\uff0c\u672a\u53d7\u5230\u653b\u64ca\u8005\u4fee\u6539\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u7372\u5f97\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - \u5167\u5bb9\u4fe1\u4efb\u6aa2\u67e5\u5df2\u95dc\u9589" + }, + "unsupported_dbus": { + "description": "System is unsupported \u7531\u65bc D-Bus \u672a\u6b63\u5e38\u5de5\u4f5c\u3001\u7cfb\u7d71\u4e0d\u5065\u5eb7\u3002\u7f3a\u4e4f\u6b64\u529f\u80fd\u3001Supervisor \u5c07\u7121\u6cd5\u6b63\u5e38\u8207\u4e3b\u6a5f\u9032\u884c\u901a\u8a0a\u3001\u8a31\u591a\u529f\u80fd\u7686\u7121\u6cd5\u6b63\u5e38\u57f7\u884c\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u6b64\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - D-Bus \u554f\u984c" + }, + "unsupported_dns_server": { + "description": "\u7531\u65bc\u63d0\u4f9b\u7684 DNS \u4f3a\u670d\u5668\u672a\u6b63\u5e38\u5de5\u4f5c\u3001fallback DNS \u9078\u9805\u5df2\u7d93\u95dc\u9589\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - DNS \u4f3a\u670d\u5668\u554f\u984c" + }, + "unsupported_docker_configuration": { + "description": "\u7531\u65bc Docker daemon \u6b63\u4ee5\u672a\u9810\u671f\u65b9\u5f0f\u57f7\u884c\u4e2d\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - Docker \u8a2d\u5b9a\u932f\u8aa4" + }, + "unsupported_docker_version": { + "description": "System is unsupported \u7531\u65bc\u4f7f\u7528\u932f\u8aa4\u7248\u672c\u4e4b Docker\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - Docker \u7248\u672c" + }, + "unsupported_job_conditions": { + "description": "\u7531\u65bc\u4e00\u500b\u6216\u4ee5\u4e0a\u4fdd\u8b77\u672a\u9810\u671f\u5931\u6557\u6216\u640d\u6bc0\u7684\u689d\u4ef6\u5df2\u906d\u5230\u95dc\u9589\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - \u4fdd\u8b77\u5df2\u95dc\u9589" + }, + "unsupported_lxc": { + "description": "\u7531\u65bc\u7cfb\u7d71\u6b63\u4ee5 LXC \u865b\u64ec\u6a5f\u57f7\u884c\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - \u5075\u6e2c\u5230 LXC" + }, + "unsupported_network_manager": { + "description": "\u7531\u65bc\u7f3a\u5c11\u3001\u672a\u555f\u7528\u7db2\u8def\u7ba1\u7406\u54e1\uff0c\u6216\u8a2d\u5b9a\u932f\u8aa4\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - \u7db2\u8def\u7ba1\u7406\u54e1\u554f\u984c" + }, + "unsupported_os": { + "description": "\u7531\u65bc\u4f5c\u696d\u7cfb\u7d71\u672a\u91dd\u5c0d\u4f7f\u7528 Supervisor \u9032\u884c\u904e\u6e2c\u8a66\u6216\u7dad\u8b77\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - \u4f5c\u696d\u7cfb\u7d71" + }, + "unsupported_os_agent": { + "description": "\u7531\u65bc\u7f3a\u5c11\u3001\u672a\u555f\u7528 OS-Agent \u6216\u8a2d\u5b9a\u932f\u8aa4\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - OS-Agent \u554f\u984c" + }, + "unsupported_restart_policy": { + "description": "\u7531\u65bc Docker container \u8a2d\u5b9a\u4e86\u91cd\u555f\u653f\u7b56\uff0c\u53ef\u80fd\u6703\u5c0e\u81f4\u555f\u52d5\u554f\u984c\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - Container \u91cd\u555f\u653f\u7b56" + }, + "unsupported_software": { + "description": "\u7531\u65bc\u5075\u6e2c\u5230 Home Assistant \u751f\u614b\u7cfb\u7d71\u4e4b\u5916\u7684\u9644\u52a0\u8edf\u9ad4\u7248\u672c\u904e\u820a\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - \u4e0d\u652f\u63f4\u8edf\u9ad4" + }, + "unsupported_source_mods": { + "description": "\u7531\u65bc Supervisor \u4f86\u6e90\u78bc\u906d\u5230\u4fee\u6539\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - Supervisor \u4f86\u6e90\u4fee\u6539" + }, + "unsupported_supervisor_version": { + "description": "\u7531\u65bc\u6240\u4f7f\u7528\u7684 Supervisor \u7248\u672c\u904e\u820a\u3001\u4e26\u4e14\u5df2\u95dc\u9589\u81ea\u52d5\u66f4\u65b0\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u8a0a\u3001\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - Supervisor \u7248\u672c" + }, + "unsupported_systemd": { + "description": "\u7531\u65bc\u7f3a\u5c11\u3001\u672a\u555f\u7528 Systemd \u6216\u8a2d\u5b9a\u932f\u8aa4\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u591a\u8a73\u7d30\u8cc7\u8a0a\uff0c\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - Systemd \u554f\u984c" + }, + "unsupported_systemd_journal": { + "description": "\u7531\u65bc Systemd \u65e5\u8a8c\u53ca/\u6216\u7f3a\u5c11\u3001\u672a\u555f\u7528\u9598\u9053\u5668\u6216\u8a2d\u5b9a\u932f\u8aa4\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002 \u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u591a\u8a73\u7d30\u8cc7\u8a0a\uff0c\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - Systemd \u65e5\u8a8c\u554f\u984c" + }, + "unsupported_systemd_resolved": { + "description": "\u7531\u65bc\u7f3a\u5c11\u3001\u672a\u555f\u7528 Systemd \u672c\u6a5f\u89e3\u6790\u6216\u8a2d\u5b9a\u932f\u8aa4\u7684\u3001\u7cfb\u7d71\u4e0d\u652f\u63f4\u3002\u8acb\u53c3\u95b1\u9023\u7d50\u4ee5\u4e86\u89e3\u66f4\u591a\u8a73\u7d30\u8cc7\u8a0a\uff0c\u53ca\u5982\u4f55\u4fee\u6b63\u554f\u984c\u3002", + "title": "\u4e0d\u652f\u63f4\u7cfb\u7d71 - Systemd \u672c\u6a5f\u89e3\u6790\u554f\u984c" } }, "system_health": { diff --git a/homeassistant/components/mqtt/translations/id.json b/homeassistant/components/mqtt/translations/id.json index 4cb0fda8a91..08b9c85b110 100644 --- a/homeassistant/components/mqtt/translations/id.json +++ b/homeassistant/components/mqtt/translations/id.json @@ -5,15 +5,33 @@ "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." }, "error": { - "cannot_connect": "Gagal terhubung" + "bad_birth": "Topik birth tidak valid", + "bad_certificate": "Sertifikat CA tidak valid", + "bad_client_cert": "Sertifikat klien tidak valid, pastikan file dengan format PEM diberikan", + "bad_client_cert_key": "Sertifikat klien dan pribadi bukan pasangan yang valid", + "bad_client_key": "Kunci pribadi tidak valid, pastikan file dengan format PEM diberikan tanpa kata sandi", + "bad_discovery_prefix": "Prefiks topik penemuan tidak valid", + "bad_will": "Topik will tidak valid", + "cannot_connect": "Gagal terhubung", + "invalid_inclusion": "Sertifikat klien dan kunci pribadi harus dikonfigurasi bersama" }, "step": { "broker": { "data": { + "advanced_options": "Opsi tingkat lanjut", "broker": "Broker", + "certificate": "Jalur ke file sertifikat CA khusus", + "client_cert": "Jalur ke file sertifikat klien", + "client_id": "ID Klien (biarkan kosong agar dihasilkan secara acak)", + "client_key": "Jalur ke file kunci pribadi", "discovery": "Aktifkan penemuan", + "keepalive": "Waktu antara mengirim pesan tetap hidup", "password": "Kata Sandi", "port": "Port", + "protocol": "Protokol MQTT", + "set_ca_cert": "Validasi sertifikat broker", + "set_client_cert": "Gunakan sertifikat klien", + "tls_insecure": "Abaikan validasi sertifikat broker", "username": "Nama Pengguna" }, "description": "Masukkan informasi koneksi broker MQTT Anda." @@ -53,18 +71,30 @@ "deprecated_yaml": { "description": "MQTT {platform} yang dikonfigurasi secara manual ditemukan di bawah kunci platform `{platform}`.\n\nPindahkan konfigurasi ke kunci integrasi `mqtt` dan mulai ulang Home Assistant untuk memperbaiki masalah ini. Lihat [dokumentasi]({more_info_url}), untuk informasi lebih lanjut.", "title": "Entitas MQTT {platform} yang dikonfigurasi secara manual membutuhkan perhatian" + }, + "deprecated_yaml_broker_settings": { + "title": "Pengaturan MQTT yang usang ditemukan di `configuration.yaml`" } }, "options": { "error": { "bad_birth": "Topik birth tidak valid", + "bad_certificate": "Sertifikat CA tidak valid", + "bad_client_cert": "Sertifikat klien tidak valid, pastikan file dengan format PEM diberikan", + "bad_client_cert_key": "Sertifikat klien dan pribadi bukan pasangan yang valid", + "bad_client_key": "Kunci pribadi tidak valid, pastikan file dengan format PEM diberikan tanpa kata sandi", + "bad_discovery_prefix": "Prefiks topik penemuan tidak valid", "bad_will": "Topik will tidak valid", - "cannot_connect": "Gagal terhubung" + "cannot_connect": "Gagal terhubung", + "invalid_inclusion": "Sertifikat klien dan kunci pribadi harus dikonfigurasi bersama" }, "step": { "broker": { "data": { + "advanced_options": "Opsi tingkat lanjut", "broker": "Broker", + "certificate": "Unggah file sertifikat CA khusus", + "client_cert": "Unggah file sertifikat klien", "password": "Kata Sandi", "port": "Port", "username": "Nama Pengguna" diff --git a/homeassistant/components/pushbullet/translations/ca.json b/homeassistant/components/pushbullet/translations/ca.json new file mode 100644 index 00000000000..a9ed110f004 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/ca.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "El servei ja est\u00e0 configurat" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_api_key": "Clau API inv\u00e0lida" + }, + "step": { + "user": { + "data": { + "api_key": "Clau API", + "name": "Nom" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "La configuraci\u00f3 de Pushbullet mitjan\u00e7ant YAML s'eliminar\u00e0 de Home Assistant.\n\nLa configuraci\u00f3 YAML existent s'ha importat autom\u00e0ticament a la interf\u00edcie d'usuari.\n\nElimina la configuraci\u00f3 YAML de Pushbullet del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", + "title": "La configuraci\u00f3 YAML de Pushbullet est\u00e0 sent eliminada" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/de.json b/homeassistant/components/pushbullet/translations/de.json new file mode 100644 index 00000000000..7e7fa2f0e14 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/de.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Der Dienst ist bereits konfiguriert" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel" + }, + "step": { + "user": { + "data": { + "api_key": "API-Schl\u00fcssel", + "name": "Name" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Die Konfiguration von Pushbullet mit YAML wird entfernt. \n\nDeine vorhandene YAML-Konfiguration wurde automatisch in die Benutzeroberfl\u00e4che importiert. \n\nEntferne die Pushbullet-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", + "title": "Die Pushbullet-YAML-Konfiguration wird entfernt" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/es.json b/homeassistant/components/pushbullet/translations/es.json new file mode 100644 index 00000000000..4321936dbca --- /dev/null +++ b/homeassistant/components/pushbullet/translations/es.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "El servicio ya est\u00e1 configurado" + }, + "error": { + "cannot_connect": "No se pudo conectar", + "invalid_api_key": "Clave API no v\u00e1lida" + }, + "step": { + "user": { + "data": { + "api_key": "Clave API", + "name": "Nombre" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Se va a eliminar la configuraci\u00f3n de Pushbullet mediante YAML. \n\nTu configuraci\u00f3n YAML existente se ha importado a la IU autom\u00e1ticamente. \n\nElimina la configuraci\u00f3n YAML de Pushbullet de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", + "title": "La configuraci\u00f3n YAML de Pushbullet se va a eliminar" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/hu.json b/homeassistant/components/pushbullet/translations/hu.json new file mode 100644 index 00000000000..09325e78e70 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/hu.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van" + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs" + }, + "step": { + "user": { + "data": { + "api_key": "API kulcs", + "name": "Elnevez\u00e9s" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "A Pushbullet konfigur\u00e1l\u00e1sa YAML haszn\u00e1lat\u00e1val elt\u00e1vol\u00edt\u00e1sra ker\u00fcl.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3 automatikusan import\u00e1l\u00e1sra ker\u00fclt a felhaszn\u00e1l\u00f3i fel\u00fcletre.\n\nA hiba kijav\u00edt\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a Pushbullet YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", + "title": "A Pushbullet YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/it.json b/homeassistant/components/pushbullet/translations/it.json new file mode 100644 index 00000000000..94911243672 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/it.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Il servizio \u00e8 gi\u00e0 configurato" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_api_key": "Chiave API non valida" + }, + "step": { + "user": { + "data": { + "api_key": "Chiave API", + "name": "Nome" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "La configurazione di Pushbullet tramite YAML \u00e8 stata rimossa. \n\nLa tua configurazione YAML esistente \u00e8 stata importata automaticamente nell'interfaccia utente. \n\nRimuovere la configurazione YAML di Pushbullet dal file configuration.yaml e riavviare Home Assistant per risolvere questo problema.", + "title": "La configurazione YAML di Pushbullet \u00e8 stata rimossa" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/pt-BR.json b/homeassistant/components/pushbullet/translations/pt-BR.json new file mode 100644 index 00000000000..16a928353cd --- /dev/null +++ b/homeassistant/components/pushbullet/translations/pt-BR.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" + }, + "error": { + "cannot_connect": "Falhou ao conectar", + "invalid_api_key": "Chave de API inv\u00e1lida" + }, + "step": { + "user": { + "data": { + "api_key": "Chave de API", + "name": "Nome" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "A configura\u00e7\u00e3o do Pushbullet usando YAML est\u00e1 sendo removida. \n\n Sua configura\u00e7\u00e3o YAML existente foi importada para a interface do usu\u00e1rio automaticamente. \n\n Remova a configura\u00e7\u00e3o YAML do Pushbullet do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", + "title": "A configura\u00e7\u00e3o YAML do Pushbullet est\u00e1 sendo removida" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/ru.json b/homeassistant/components/pushbullet/translations/ru.json new file mode 100644 index 00000000000..815e732f7dd --- /dev/null +++ b/homeassistant/components/pushbullet/translations/ru.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API." + }, + "step": { + "user": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Pushbullet \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", + "title": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Pushbullet \u0447\u0435\u0440\u0435\u0437 YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" + } + } +} \ No newline at end of file From 28989754cda667d0828416d41ef4eb1a3a707ebb Mon Sep 17 00:00:00 2001 From: Austin Brunkhorst Date: Wed, 2 Nov 2022 19:10:07 -0700 Subject: [PATCH 0181/1033] Update pysnooz to 0.8.3 (#81428) --- homeassistant/components/snooz/config_flow.py | 2 +- homeassistant/components/snooz/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/snooz/{test_config.py => test_init.py} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename tests/components/snooz/{test_config.py => test_init.py} (100%) diff --git a/homeassistant/components/snooz/config_flow.py b/homeassistant/components/snooz/config_flow.py index 48f9370e403..eb05edcbefa 100644 --- a/homeassistant/components/snooz/config_flow.py +++ b/homeassistant/components/snooz/config_flow.py @@ -82,7 +82,7 @@ class SnoozConfigFlow(ConfigFlow, domain=DOMAIN): if user_input is not None: name = user_input[CONF_NAME] - discovered = self._discovered_devices.get(name) + discovered = self._discovered_devices[name] assert discovered is not None diff --git a/homeassistant/components/snooz/manifest.json b/homeassistant/components/snooz/manifest.json index 1384767e8b8..91185bcd5b2 100644 --- a/homeassistant/components/snooz/manifest.json +++ b/homeassistant/components/snooz/manifest.json @@ -3,7 +3,7 @@ "name": "Snooz", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/snooz", - "requirements": ["pysnooz==0.8.2"], + "requirements": ["pysnooz==0.8.3"], "dependencies": ["bluetooth"], "codeowners": ["@AustinBrunkhorst"], "bluetooth": [ diff --git a/requirements_all.txt b/requirements_all.txt index a3a177edef5..e5973baf0cb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1914,7 +1914,7 @@ pysml==0.0.8 pysnmplib==5.0.15 # homeassistant.components.snooz -pysnooz==0.8.2 +pysnooz==0.8.3 # homeassistant.components.soma pysoma==0.0.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c8ceeb06fbd..8dd8d3592fc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1349,7 +1349,7 @@ pysmartthings==0.7.6 pysnmplib==5.0.15 # homeassistant.components.snooz -pysnooz==0.8.2 +pysnooz==0.8.3 # homeassistant.components.soma pysoma==0.0.10 diff --git a/tests/components/snooz/test_config.py b/tests/components/snooz/test_init.py similarity index 100% rename from tests/components/snooz/test_config.py rename to tests/components/snooz/test_init.py From 6bd8cf00729aff12978e97115182acfbc66db54d Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 3 Nov 2022 08:27:46 +0100 Subject: [PATCH 0182/1033] Use 'kWh' as unit for 'IEC_ENERGY_COUNTER' (#81427) The standard unit for the 'IEC_ENERGY_COUNTER' type is 'kWh' instead of 'Wh' --- homeassistant/components/homematic/sensor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/homematic/sensor.py b/homeassistant/components/homematic/sensor.py index c7a78c7bbcf..806d56b2c1d 100644 --- a/homeassistant/components/homematic/sensor.py +++ b/homeassistant/components/homematic/sensor.py @@ -17,6 +17,7 @@ from homeassistant.const import ( DEGREE, ELECTRIC_CURRENT_MILLIAMPERE, ELECTRIC_POTENTIAL_VOLT, + ENERGY_KILO_WATT_HOUR, ENERGY_WATT_HOUR, FREQUENCY_HERTZ, LENGTH_MILLIMETERS, @@ -141,7 +142,7 @@ SENSOR_DESCRIPTIONS: dict[str, SensorEntityDescription] = { ), "IEC_ENERGY_COUNTER": SensorEntityDescription( key="IEC_ENERGY_COUNTER", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), From adf35e5ec2019dae10954283dd0ab6ba82f2b026 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 3 Nov 2022 08:51:08 +0100 Subject: [PATCH 0183/1033] Skip flume devices with location missing (#81441) fixes #81438 --- homeassistant/components/flume/util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/flume/util.py b/homeassistant/components/flume/util.py index b943124b877..58b3920c9be 100644 --- a/homeassistant/components/flume/util.py +++ b/homeassistant/components/flume/util.py @@ -14,5 +14,6 @@ def get_valid_flume_devices(flume_devices: FlumeDeviceList) -> list[dict[str, An return [ device for device in flume_devices.device_list - if KEY_DEVICE_LOCATION_NAME in device[KEY_DEVICE_LOCATION] + if KEY_DEVICE_LOCATION in device + and KEY_DEVICE_LOCATION_NAME in device[KEY_DEVICE_LOCATION] ] From ee4d28000da5d59b7162cb434d210bafa72cdbc9 Mon Sep 17 00:00:00 2001 From: Malte Franken Date: Thu, 3 Nov 2022 19:32:40 +1100 Subject: [PATCH 0184/1033] Add integration_type to gdacs (#81451) --- homeassistant/components/gdacs/manifest.json | 3 ++- homeassistant/generated/integrations.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/gdacs/manifest.json b/homeassistant/components/gdacs/manifest.json index 57c275f2beb..b378368a326 100644 --- a/homeassistant/components/gdacs/manifest.json +++ b/homeassistant/components/gdacs/manifest.json @@ -6,5 +6,6 @@ "requirements": ["aio_georss_gdacs==0.7"], "codeowners": ["@exxamalte"], "quality_scale": "platinum", - "iot_class": "cloud_polling" + "iot_class": "cloud_polling", + "integration_type": "service" } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index a955193624c..1d04bde132a 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -1773,7 +1773,7 @@ }, "gdacs": { "name": "Global Disaster Alert and Coordination System (GDACS)", - "integration_type": "hub", + "integration_type": "service", "config_flow": true, "iot_class": "cloud_polling" }, From 739ed6a6c86fed85de61562bb20344adc66a4ed6 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 3 Nov 2022 04:43:48 -0400 Subject: [PATCH 0185/1033] Fix eight sleep client creation (#81440) Fix eight sleep bug --- homeassistant/components/eight_sleep/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/eight_sleep/__init__.py b/homeassistant/components/eight_sleep/__init__.py index 67ff6c59a54..2642505fbea 100644 --- a/homeassistant/components/eight_sleep/__init__.py +++ b/homeassistant/components/eight_sleep/__init__.py @@ -95,7 +95,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD], hass.config.time_zone, - async_get_clientsession(hass), + client_session=async_get_clientsession(hass), ) # Authenticate, build sensors From 328eda044a46dd69d46400139976554b6911fad0 Mon Sep 17 00:00:00 2001 From: Rami Mosleh Date: Thu, 3 Nov 2022 11:02:25 +0200 Subject: [PATCH 0186/1033] Use DataUpdateCoordinator for glances (#72748) * use DataUpdateCoordinator for glances add tests to increase coverage fix test_config_flow.py fix codecov/patch remove unused const, minor tweaks remove invalid_auth test as it is not implemented fix type hints * change to async_forward_entry_setups * Use Dataupdatecoordinator for glances * minor fixex * minor fixes * minor fix * remove support_versions const * coe cleanup * address comments * fix sensor native_value * Rename entry to entry_data in `get_api` * Remove whitespace in sensor name --- .coveragerc | 2 +- homeassistant/components/glances/__init__.py | 127 ++----------- .../components/glances/config_flow.py | 48 +---- homeassistant/components/glances/const.py | 1 - .../components/glances/coordinator.py | 42 +++++ homeassistant/components/glances/sensor.py | 168 ++++++++---------- homeassistant/components/glances/strings.json | 13 +- .../components/glances/translations/en.json | 13 +- tests/components/glances/__init__.py | 41 +++++ tests/components/glances/conftest.py | 15 ++ tests/components/glances/test_config_flow.py | 99 +++-------- tests/components/glances/test_init.py | 49 +++++ 12 files changed, 279 insertions(+), 339 deletions(-) create mode 100644 homeassistant/components/glances/coordinator.py create mode 100644 tests/components/glances/conftest.py create mode 100644 tests/components/glances/test_init.py diff --git a/.coveragerc b/.coveragerc index b782f8444db..78168feced0 100644 --- a/.coveragerc +++ b/.coveragerc @@ -455,7 +455,7 @@ omit = homeassistant/components/github/sensor.py homeassistant/components/gitlab_ci/sensor.py homeassistant/components/gitter/sensor.py - homeassistant/components/glances/__init__.py + homeassistant/components/glances/const.py homeassistant/components/glances/sensor.py homeassistant/components/goalfeed/* homeassistant/components/goodwe/__init__.py diff --git a/homeassistant/components/glances/__init__.py b/homeassistant/components/glances/__init__.py index 0747db89cd2..bda1baf797a 100644 --- a/homeassistant/components/glances/__init__.py +++ b/homeassistant/components/glances/__init__.py @@ -1,27 +1,16 @@ """The Glances component.""" -from datetime import timedelta -import logging +from typing import Any -from glances_api import Glances, exceptions +from glances_api import Glances from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - CONF_HOST, - CONF_NAME, - CONF_SCAN_INTERVAL, - CONF_VERIFY_SSL, - Platform, -) +from homeassistant.const import CONF_NAME, CONF_VERIFY_SSL, Platform from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryNotReady import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.dispatcher import async_dispatcher_send -from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.httpx_client import get_async_client -from .const import DATA_UPDATED, DEFAULT_SCAN_INTERVAL, DOMAIN - -_LOGGER = logging.getLogger(__name__) +from .const import DOMAIN +from .coordinator import GlancesDataUpdateCoordinator PLATFORMS = [Platform.SENSOR] @@ -30,106 +19,28 @@ CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Set up Glances from config entry.""" - client = GlancesData(hass, config_entry) - hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = client - if not await client.async_setup(): - return False + api = get_api(hass, dict(config_entry.data)) + coordinator = GlancesDataUpdateCoordinator(hass, config_entry, api) + await coordinator.async_config_entry_first_refresh() + + hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = coordinator + + await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" - unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) - if unload_ok: + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): hass.data[DOMAIN].pop(entry.entry_id) + if not hass.data[DOMAIN]: + del hass.data[DOMAIN] return unload_ok -class GlancesData: - """Get the latest data from Glances api.""" - - def __init__(self, hass, config_entry): - """Initialize the Glances data.""" - self.hass = hass - self.config_entry = config_entry - self.api = None - self.unsub_timer = None - self.available = False - - @property - def host(self): - """Return client host.""" - return self.config_entry.data[CONF_HOST] - - async def async_update(self): - """Get the latest data from the Glances REST API.""" - try: - await self.api.get_data("all") - self.available = True - except exceptions.GlancesApiError: - _LOGGER.error("Unable to fetch data from Glances") - self.available = False - _LOGGER.debug("Glances data updated") - async_dispatcher_send(self.hass, DATA_UPDATED) - - async def async_setup(self): - """Set up the Glances client.""" - try: - self.api = get_api(self.hass, self.config_entry.data) - await self.api.get_data("all") - self.available = True - _LOGGER.debug("Successfully connected to Glances") - - except exceptions.GlancesApiConnectionError as err: - _LOGGER.debug("Can not connect to Glances") - raise ConfigEntryNotReady from err - - self.add_options() - self.set_scan_interval(self.config_entry.options[CONF_SCAN_INTERVAL]) - self.config_entry.async_on_unload( - self.config_entry.add_update_listener(self.async_options_updated) - ) - - await self.hass.config_entries.async_forward_entry_setups( - self.config_entry, PLATFORMS - ) - - return True - - def add_options(self): - """Add options for Glances integration.""" - if not self.config_entry.options: - options = {CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL} - self.hass.config_entries.async_update_entry( - self.config_entry, options=options - ) - - def set_scan_interval(self, scan_interval): - """Update scan interval.""" - - async def refresh(event_time): - """Get the latest data from Glances api.""" - await self.async_update() - - if self.unsub_timer is not None: - self.unsub_timer() - self.unsub_timer = async_track_time_interval( - self.hass, refresh, timedelta(seconds=scan_interval) - ) - - @staticmethod - async def async_options_updated(hass: HomeAssistant, entry: ConfigEntry) -> None: - """Triggered by config entry options updates.""" - hass.data[DOMAIN][entry.entry_id].set_scan_interval( - entry.options[CONF_SCAN_INTERVAL] - ) - - -def get_api(hass, entry): +def get_api(hass: HomeAssistant, entry_data: dict[str, Any]) -> Glances: """Return the api from glances_api.""" - params = entry.copy() - params.pop(CONF_NAME, None) - verify_ssl = params.pop(CONF_VERIFY_SSL, True) - httpx_client = get_async_client(hass, verify_ssl=verify_ssl) - return Glances(httpx_client=httpx_client, **params) + entry_data.pop(CONF_NAME, None) + httpx_client = get_async_client(hass, verify_ssl=entry_data[CONF_VERIFY_SSL]) + return Glances(httpx_client=httpx_client, **entry_data) diff --git a/homeassistant/components/glances/config_flow.py b/homeassistant/components/glances/config_flow.py index a56fa795491..cf55118a913 100644 --- a/homeassistant/components/glances/config_flow.py +++ b/homeassistant/components/glances/config_flow.py @@ -3,20 +3,19 @@ from __future__ import annotations from typing import Any -import glances_api +from glances_api.exceptions import GlancesApiError import voluptuous as vol -from homeassistant import config_entries, core, exceptions +from homeassistant import config_entries, exceptions from homeassistant.const import ( CONF_HOST, CONF_PASSWORD, CONF_PORT, - CONF_SCAN_INTERVAL, CONF_SSL, CONF_USERNAME, CONF_VERIFY_SSL, ) -from homeassistant.core import callback +from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResult from . import get_api @@ -24,7 +23,6 @@ from .const import ( CONF_VERSION, DEFAULT_HOST, DEFAULT_PORT, - DEFAULT_SCAN_INTERVAL, DEFAULT_VERSION, DOMAIN, SUPPORTED_VERSIONS, @@ -43,12 +41,12 @@ DATA_SCHEMA = vol.Schema( ) -async def validate_input(hass: core.HomeAssistant, data): +async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None: """Validate the user input allows us to connect.""" + api = get_api(hass, data) try: - api = get_api(hass, data) await api.get_data("all") - except glances_api.exceptions.GlancesApiConnectionError as err: + except GlancesApiError as err: raise CannotConnect from err @@ -57,14 +55,6 @@ class GlancesFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 - @staticmethod - @callback - def async_get_options_flow( - config_entry: config_entries.ConfigEntry, - ) -> GlancesOptionsFlowHandler: - """Get the options flow for this handler.""" - return GlancesOptionsFlowHandler(config_entry) - async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -85,31 +75,5 @@ class GlancesFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): ) -class GlancesOptionsFlowHandler(config_entries.OptionsFlow): - """Handle Glances client options.""" - - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: - """Initialize Glances options flow.""" - self.config_entry = config_entry - - async def async_step_init( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Manage the Glances options.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - options = { - vol.Optional( - CONF_SCAN_INTERVAL, - default=self.config_entry.options.get( - CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL - ), - ): int - } - - return self.async_show_form(step_id="init", data_schema=vol.Schema(options)) - - class CannotConnect(exceptions.HomeAssistantError): """Error to indicate we cannot connect.""" diff --git a/homeassistant/components/glances/const.py b/homeassistant/components/glances/const.py index efcc30c057b..b704ab326f4 100644 --- a/homeassistant/components/glances/const.py +++ b/homeassistant/components/glances/const.py @@ -10,7 +10,6 @@ DEFAULT_PORT = 61208 DEFAULT_VERSION = 3 DEFAULT_SCAN_INTERVAL = 60 -DATA_UPDATED = "glances_data_updated" SUPPORTED_VERSIONS = [2, 3] CPU_ICON = f"mdi:cpu-{64 if sys.maxsize > 2**32 else 32}-bit" diff --git a/homeassistant/components/glances/coordinator.py b/homeassistant/components/glances/coordinator.py new file mode 100644 index 00000000000..8ffd2a2da6e --- /dev/null +++ b/homeassistant/components/glances/coordinator.py @@ -0,0 +1,42 @@ +"""Coordinator for Glances integration.""" +from datetime import timedelta +import logging +from typing import Any + +from glances_api import Glances, exceptions + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_HOST +from homeassistant.core import HomeAssistant +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +from .const import DOMAIN + +_LOGGER = logging.getLogger(__name__) + + +class GlancesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): + """Get the latest data from Glances api.""" + + config_entry: ConfigEntry + + def __init__(self, hass: HomeAssistant, entry: ConfigEntry, api: Glances) -> None: + """Initialize the Glances data.""" + self.hass = hass + self.config_entry = entry + self.host: str = entry.data[CONF_HOST] + self.api = api + super().__init__( + hass, + _LOGGER, + name=f"{DOMAIN} - {self.host}", + update_interval=timedelta(seconds=60), + ) + + async def _async_update_data(self) -> dict[str, Any]: + """Get the latest data from the Glances REST API.""" + try: + await self.api.get_data("all") + except exceptions.GlancesApiError as err: + raise UpdateFailed from err + return self.api.data diff --git a/homeassistant/components/glances/sensor.py b/homeassistant/components/glances/sensor.py index 13f4284acd3..a479cb260de 100644 --- a/homeassistant/components/glances/sensor.py +++ b/homeassistant/components/glances/sensor.py @@ -8,10 +8,10 @@ from homeassistant.components.sensor import ( SensorEntity, SensorEntityDescription, SensorStateClass, + StateType, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - CONF_HOST, CONF_NAME, DATA_GIBIBYTES, DATA_MEBIBYTES, @@ -21,22 +21,29 @@ from homeassistant.const import ( TEMP_CELSIUS, Platform, ) -from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import GlancesData -from .const import CPU_ICON, DATA_UPDATED, DOMAIN +from . import GlancesDataUpdateCoordinator +from .const import CPU_ICON, DOMAIN @dataclass -class GlancesSensorEntityDescription(SensorEntityDescription): - """Describe Glances sensor entity.""" +class GlancesSensorEntityDescriptionMixin: + """Mixin for required keys.""" - type: str | None = None - name_suffix: str | None = None + type: str + name_suffix: str + + +@dataclass +class GlancesSensorEntityDescription( + SensorEntityDescription, GlancesSensorEntityDescriptionMixin +): + """Describe Glances sensor entity.""" SENSOR_TYPES: tuple[GlancesSensorEntityDescription, ...] = ( @@ -234,9 +241,9 @@ async def async_setup_entry( ) -> None: """Set up the Glances sensors.""" - client: GlancesData = hass.data[DOMAIN][config_entry.entry_id] + coordinator: GlancesDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] name = config_entry.data.get(CONF_NAME) - dev = [] + entities = [] @callback def _migrate_old_unique_ids( @@ -256,15 +263,15 @@ async def async_setup_entry( for description in SENSOR_TYPES: if description.type == "fs": # fs will provide a list of disks attached - for disk in client.api.data[description.type]: + for disk in coordinator.data[description.type]: _migrate_old_unique_ids( hass, - f"{client.host}-{name} {disk['mnt_point']} {description.name_suffix}", + f"{coordinator.host}-{name} {disk['mnt_point']} {description.name_suffix}", f"{disk['mnt_point']}-{description.key}", ) - dev.append( + entities.append( GlancesSensor( - client, + coordinator, name, disk["mnt_point"], description, @@ -272,101 +279,80 @@ async def async_setup_entry( ) elif description.type == "sensors": # sensors will provide temp for different devices - for sensor in client.api.data[description.type]: + for sensor in coordinator.data[description.type]: if sensor["type"] == description.key: _migrate_old_unique_ids( hass, - f"{client.host}-{name} {sensor['label']} {description.name_suffix}", + f"{coordinator.host}-{name} {sensor['label']} {description.name_suffix}", f"{sensor['label']}-{description.key}", ) - dev.append( + entities.append( GlancesSensor( - client, + coordinator, name, sensor["label"], description, ) ) elif description.type == "raid": - for raid_device in client.api.data[description.type]: + for raid_device in coordinator.data[description.type]: _migrate_old_unique_ids( hass, - f"{client.host}-{name} {raid_device} {description.name_suffix}", + f"{coordinator.host}-{name} {raid_device} {description.name_suffix}", f"{raid_device}-{description.key}", ) - dev.append(GlancesSensor(client, name, raid_device, description)) - elif client.api.data[description.type]: + entities.append( + GlancesSensor(coordinator, name, raid_device, description) + ) + elif coordinator.data[description.type]: _migrate_old_unique_ids( hass, - f"{client.host}-{name} {description.name_suffix}", + f"{coordinator.host}-{name} {description.name_suffix}", f"-{description.key}", ) - dev.append( + entities.append( GlancesSensor( - client, + coordinator, name, "", description, ) ) - async_add_entities(dev, True) + async_add_entities(entities) -class GlancesSensor(SensorEntity): +class GlancesSensor(CoordinatorEntity[GlancesDataUpdateCoordinator], SensorEntity): """Implementation of a Glances sensor.""" entity_description: GlancesSensorEntityDescription _attr_has_entity_name = True - _attr_should_poll = False def __init__( self, - glances_data: GlancesData, + coordinator: GlancesDataUpdateCoordinator, name: str | None, sensor_name_prefix: str, description: GlancesSensorEntityDescription, ) -> None: """Initialize the sensor.""" - self.glances_data = glances_data + super().__init__(coordinator) self._sensor_name_prefix = sensor_name_prefix - self.unsub_update: CALLBACK_TYPE | None = None - self.entity_description = description - self._attr_name = f"{sensor_name_prefix} {description.name_suffix}" + self._attr_name = f"{sensor_name_prefix} {description.name_suffix}".strip() self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, glances_data.config_entry.entry_id)}, + identifiers={(DOMAIN, coordinator.config_entry.entry_id)}, manufacturer="Glances", - name=name or glances_data.config_entry.data[CONF_HOST], + name=name or coordinator.host, ) - self._attr_unique_id = f"{self.glances_data.config_entry.entry_id}-{sensor_name_prefix}-{description.key}" + self._attr_unique_id = f"{coordinator.config_entry.entry_id}-{sensor_name_prefix}-{description.key}" @property - def available(self) -> bool: - """Could the device be accessed during the last update call.""" - return self.glances_data.available - - async def async_added_to_hass(self) -> None: - """Handle entity which will be added.""" - self.unsub_update = async_dispatcher_connect( - self.hass, DATA_UPDATED, self._schedule_immediate_update - ) - - @callback - def _schedule_immediate_update(self) -> None: - self.async_schedule_update_ha_state(True) - - async def will_remove_from_hass(self) -> None: - """Unsubscribe from update dispatcher.""" - if self.unsub_update: - self.unsub_update() - self.unsub_update = None - - async def async_update(self) -> None: # noqa: C901 - """Get the latest data from REST API.""" - if (value := self.glances_data.api.data) is None: - return - + def native_value(self) -> StateType: # noqa: C901 + """Return the state of the resources.""" + if (value := self.coordinator.data) is None: + return None + state: StateType = None if self.entity_description.type == "fs": for var in value["fs"]: if var["mnt_point"] == self._sensor_name_prefix: @@ -374,100 +360,102 @@ class GlancesSensor(SensorEntity): break if self.entity_description.key == "disk_free": try: - self._attr_native_value = round(disk["free"] / 1024**3, 1) + state = round(disk["free"] / 1024**3, 1) except KeyError: - self._attr_native_value = round( + state = round( (disk["size"] - disk["used"]) / 1024**3, 1, ) elif self.entity_description.key == "disk_use": - self._attr_native_value = round(disk["used"] / 1024**3, 1) + state = round(disk["used"] / 1024**3, 1) elif self.entity_description.key == "disk_use_percent": - self._attr_native_value = disk["percent"] + state = disk["percent"] elif self.entity_description.key == "battery": for sensor in value["sensors"]: if ( sensor["type"] == "battery" and sensor["label"] == self._sensor_name_prefix ): - self._attr_native_value = sensor["value"] + state = sensor["value"] elif self.entity_description.key == "fan_speed": for sensor in value["sensors"]: if ( sensor["type"] == "fan_speed" and sensor["label"] == self._sensor_name_prefix ): - self._attr_native_value = sensor["value"] + state = sensor["value"] elif self.entity_description.key == "temperature_core": for sensor in value["sensors"]: if ( sensor["type"] == "temperature_core" and sensor["label"] == self._sensor_name_prefix ): - self._attr_native_value = sensor["value"] + state = sensor["value"] elif self.entity_description.key == "temperature_hdd": for sensor in value["sensors"]: if ( sensor["type"] == "temperature_hdd" and sensor["label"] == self._sensor_name_prefix ): - self._attr_native_value = sensor["value"] + state = sensor["value"] elif self.entity_description.key == "memory_use_percent": - self._attr_native_value = value["mem"]["percent"] + state = value["mem"]["percent"] elif self.entity_description.key == "memory_use": - self._attr_native_value = round(value["mem"]["used"] / 1024**2, 1) + state = round(value["mem"]["used"] / 1024**2, 1) elif self.entity_description.key == "memory_free": - self._attr_native_value = round(value["mem"]["free"] / 1024**2, 1) + state = round(value["mem"]["free"] / 1024**2, 1) elif self.entity_description.key == "swap_use_percent": - self._attr_native_value = value["memswap"]["percent"] + state = value["memswap"]["percent"] elif self.entity_description.key == "swap_use": - self._attr_native_value = round(value["memswap"]["used"] / 1024**3, 1) + state = round(value["memswap"]["used"] / 1024**3, 1) elif self.entity_description.key == "swap_free": - self._attr_native_value = round(value["memswap"]["free"] / 1024**3, 1) + state = round(value["memswap"]["free"] / 1024**3, 1) elif self.entity_description.key == "processor_load": # Windows systems don't provide load details try: - self._attr_native_value = value["load"]["min15"] + state = value["load"]["min15"] except KeyError: - self._attr_native_value = value["cpu"]["total"] + state = value["cpu"]["total"] elif self.entity_description.key == "process_running": - self._attr_native_value = value["processcount"]["running"] + state = value["processcount"]["running"] elif self.entity_description.key == "process_total": - self._attr_native_value = value["processcount"]["total"] + state = value["processcount"]["total"] elif self.entity_description.key == "process_thread": - self._attr_native_value = value["processcount"]["thread"] + state = value["processcount"]["thread"] elif self.entity_description.key == "process_sleeping": - self._attr_native_value = value["processcount"]["sleeping"] + state = value["processcount"]["sleeping"] elif self.entity_description.key == "cpu_use_percent": - self._attr_native_value = value["quicklook"]["cpu"] + state = value["quicklook"]["cpu"] elif self.entity_description.key == "docker_active": count = 0 try: for container in value["docker"]["containers"]: if container["Status"] == "running" or "Up" in container["Status"]: count += 1 - self._attr_native_value = count + state = count except KeyError: - self._attr_native_value = count + state = count elif self.entity_description.key == "docker_cpu_use": cpu_use = 0.0 try: for container in value["docker"]["containers"]: if container["Status"] == "running" or "Up" in container["Status"]: cpu_use += container["cpu"]["total"] - self._attr_native_value = round(cpu_use, 1) + state = round(cpu_use, 1) except KeyError: - self._attr_native_value = STATE_UNAVAILABLE + state = STATE_UNAVAILABLE elif self.entity_description.key == "docker_memory_use": mem_use = 0.0 try: for container in value["docker"]["containers"]: if container["Status"] == "running" or "Up" in container["Status"]: mem_use += container["memory"]["usage"] - self._attr_native_value = round(mem_use / 1024**2, 1) + state = round(mem_use / 1024**2, 1) except KeyError: - self._attr_native_value = STATE_UNAVAILABLE + state = STATE_UNAVAILABLE elif self.entity_description.type == "raid": for raid_device, raid in value["raid"].items(): if raid_device == self._sensor_name_prefix: - self._attr_native_value = raid[self.entity_description.key] + state = raid[self.entity_description.key] + + return state diff --git a/homeassistant/components/glances/strings.json b/homeassistant/components/glances/strings.json index 11c9792f364..b46716b43c0 100644 --- a/homeassistant/components/glances/strings.json +++ b/homeassistant/components/glances/strings.json @@ -14,21 +14,10 @@ } }, "error": { - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", - "wrong_version": "Version not supported (2 or 3 only)" + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" }, "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" } - }, - "options": { - "step": { - "init": { - "description": "Configure options for Glances", - "data": { - "scan_interval": "Update frequency" - } - } - } } } diff --git a/homeassistant/components/glances/translations/en.json b/homeassistant/components/glances/translations/en.json index 87c53c3cf48..726e4716224 100644 --- a/homeassistant/components/glances/translations/en.json +++ b/homeassistant/components/glances/translations/en.json @@ -4,8 +4,7 @@ "already_configured": "Device is already configured" }, "error": { - "cannot_connect": "Failed to connect", - "wrong_version": "Version not supported (2 or 3 only)" + "cannot_connect": "Failed to connect" }, "step": { "user": { @@ -22,15 +21,5 @@ "title": "Setup Glances" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Update frequency" - }, - "description": "Configure options for Glances" - } - } } } \ No newline at end of file diff --git a/tests/components/glances/__init__.py b/tests/components/glances/__init__.py index 488265f970b..4818e9258de 100644 --- a/tests/components/glances/__init__.py +++ b/tests/components/glances/__init__.py @@ -1 +1,42 @@ """Tests for Glances.""" + +MOCK_USER_INPUT = { + "host": "0.0.0.0", + "username": "username", + "password": "password", + "version": 3, + "port": 61208, + "ssl": False, + "verify_ssl": True, +} + +MOCK_DATA = { + "cpu": { + "total": 10.6, + "user": 7.6, + "system": 2.1, + "idle": 88.8, + "nice": 0.0, + "iowait": 0.6, + }, + "diskio": [ + { + "time_since_update": 1, + "disk_name": "nvme0n1", + "read_count": 12, + "write_count": 466, + "read_bytes": 184320, + "write_bytes": 23863296, + "key": "disk_name", + }, + ], + "system": { + "os_name": "Linux", + "hostname": "fedora-35", + "platform": "64bit", + "linux_distro": "Fedora Linux 35", + "os_version": "5.15.6-200.fc35.x86_64", + "hr_name": "Fedora Linux 35 64bit", + }, + "uptime": "3 days, 10:25:20", +} diff --git a/tests/components/glances/conftest.py b/tests/components/glances/conftest.py new file mode 100644 index 00000000000..d92d3cc33d4 --- /dev/null +++ b/tests/components/glances/conftest.py @@ -0,0 +1,15 @@ +"""Conftest for speedtestdotnet.""" +from unittest.mock import AsyncMock, patch + +import pytest + +from . import MOCK_DATA + + +@pytest.fixture(autouse=True) +def mock_api(): + """Mock glances api.""" + with patch("homeassistant.components.glances.Glances") as mock_api: + mock_api.return_value.get_data = AsyncMock(return_value=None) + mock_api.return_value.data.return_value = MOCK_DATA + yield mock_api diff --git a/tests/components/glances/test_config_flow.py b/tests/components/glances/test_config_flow.py index 40e40b45e11..ab642055059 100644 --- a/tests/components/glances/test_config_flow.py +++ b/tests/components/glances/test_config_flow.py @@ -1,38 +1,22 @@ """Tests for Glances config flow.""" -from unittest.mock import patch +from unittest.mock import MagicMock -from glances_api import exceptions +from glances_api.exceptions import GlancesApiConnectionError import pytest -from homeassistant import config_entries, data_entry_flow +from homeassistant import config_entries from homeassistant.components import glances -from homeassistant.const import CONF_SCAN_INTERVAL from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType -from tests.common import MockConfigEntry +from . import MOCK_USER_INPUT -NAME = "Glances" -HOST = "0.0.0.0" -USERNAME = "username" -PASSWORD = "password" -PORT = 61208 -VERSION = 3 -SCAN_INTERVAL = 10 - -DEMO_USER_INPUT = { - "host": HOST, - "username": USERNAME, - "password": PASSWORD, - "version": VERSION, - "port": PORT, - "ssl": False, - "verify_ssl": True, -} +from tests.common import MockConfigEntry, patch @pytest.fixture(autouse=True) def glances_setup_fixture(): - """Mock transmission entry setup.""" + """Mock glances entry setup.""" with patch("homeassistant.components.glances.async_setup_entry", return_value=True): yield @@ -43,74 +27,43 @@ async def test_form(hass: HomeAssistant) -> None: result = await hass.config_entries.flow.async_init( glances.DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["type"] == FlowResultType.FORM assert result["step_id"] == "user" - with patch("homeassistant.components.glances.Glances.get_data", autospec=True): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=MOCK_USER_INPUT + ) - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=DEMO_USER_INPUT - ) - - assert result["type"] == "create_entry" - assert result["title"] == HOST - assert result["data"] == DEMO_USER_INPUT + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "0.0.0.0" + assert result["data"] == MOCK_USER_INPUT -async def test_form_cannot_connect(hass: HomeAssistant) -> None: +async def test_form_cannot_connect(hass: HomeAssistant, mock_api: MagicMock) -> None: """Test to return error if we cannot connect.""" - with patch( - "homeassistant.components.glances.Glances.get_data", - side_effect=exceptions.GlancesApiConnectionError, - ): - result = await hass.config_entries.flow.async_init( - glances.DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=DEMO_USER_INPUT - ) + mock_api.return_value.get_data.side_effect = GlancesApiConnectionError + result = await hass.config_entries.flow.async_init( + glances.DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=MOCK_USER_INPUT + ) - assert result["type"] == "form" + assert result["type"] == FlowResultType.FORM assert result["errors"] == {"base": "cannot_connect"} async def test_form_already_configured(hass: HomeAssistant) -> None: """Test host is already configured.""" - entry = MockConfigEntry( - domain=glances.DOMAIN, data=DEMO_USER_INPUT, options={CONF_SCAN_INTERVAL: 60} - ) + entry = MockConfigEntry(domain=glances.DOMAIN, data=MOCK_USER_INPUT) entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( glances.DOMAIN, context={"source": config_entries.SOURCE_USER} ) result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=DEMO_USER_INPUT + result["flow_id"], user_input=MOCK_USER_INPUT ) - assert result["type"] == "abort" + assert result["type"] == FlowResultType.ABORT assert result["reason"] == "already_configured" - - -async def test_options(hass: HomeAssistant) -> None: - """Test options for Glances.""" - entry = MockConfigEntry( - domain=glances.DOMAIN, data=DEMO_USER_INPUT, options={CONF_SCAN_INTERVAL: 60} - ) - entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - result = await hass.config_entries.options.async_init(entry.entry_id) - - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["step_id"] == "init" - - result = await hass.config_entries.options.async_configure( - result["flow_id"], user_input={glances.CONF_SCAN_INTERVAL: 10} - ) - - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["data"] == { - glances.CONF_SCAN_INTERVAL: 10, - } diff --git a/tests/components/glances/test_init.py b/tests/components/glances/test_init.py new file mode 100644 index 00000000000..944d9d55ae2 --- /dev/null +++ b/tests/components/glances/test_init.py @@ -0,0 +1,49 @@ +"""Tests for Glances integration.""" +from unittest.mock import MagicMock + +from glances_api.exceptions import GlancesApiConnectionError + +from homeassistant.components.glances.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.core import HomeAssistant + +from . import MOCK_USER_INPUT + +from tests.common import MockConfigEntry + + +async def test_successful_config_entry(hass: HomeAssistant) -> None: + """Test that Glances is configured successfully.""" + + entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_INPUT) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + + assert entry.state == ConfigEntryState.LOADED + + +async def test_conn_error(hass: HomeAssistant, mock_api: MagicMock) -> None: + """Test Glances failed due to connection error.""" + + entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_INPUT) + entry.add_to_hass(hass) + + mock_api.return_value.get_data.side_effect = GlancesApiConnectionError + await hass.config_entries.async_setup(entry.entry_id) + assert entry.state is ConfigEntryState.SETUP_RETRY + + +async def test_unload_entry(hass: HomeAssistant) -> None: + """Test removing Glances.""" + entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_INPUT) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.NOT_LOADED + assert DOMAIN not in hass.data From 203c83b6f0be1fde43c2c869bd05823026a17d4a Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 3 Nov 2022 10:58:37 +0100 Subject: [PATCH 0187/1033] Use _attr_ in MQTT climate (#81406) * Use _attr_ in MQTT climate * Follow up comment * Do not change code --- homeassistant/components/mqtt/climate.py | 844 ++++++++++------------- 1 file changed, 373 insertions(+), 471 deletions(-) diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 9f98fcfebdc..e46c8e31565 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -374,16 +374,6 @@ class MqttClimate(MqttEntity, ClimateEntity): def __init__(self, hass, config, config_entry, discovery_data): """Initialize the climate device.""" - self._action = None - self._aux = False - self._current_fan_mode = None - self._current_operation = None - self._current_swing_mode = None - self._current_temp = None - self._preset_mode = None - self._target_temp = None - self._target_temp_high = None - self._target_temp_low = None self._topic = None self._value_templates = None self._command_templates = None @@ -399,36 +389,54 @@ class MqttClimate(MqttEntity, ClimateEntity): def _setup_from_config(self, config): """(Re)Setup the entity.""" + self._attr_hvac_modes = config[CONF_MODE_LIST] + self._attr_min_temp = config[CONF_TEMP_MIN] + self._attr_max_temp = config[CONF_TEMP_MAX] + self._attr_precision = config.get(CONF_PRECISION, super().precision) + self._attr_fan_modes = config[CONF_FAN_MODE_LIST] + self._attr_swing_modes = config[CONF_SWING_MODE_LIST] + self._attr_target_temperature_step = config[CONF_TEMP_STEP] + self._attr_temperature_unit = config.get( + CONF_TEMPERATURE_UNIT, self.hass.config.units.temperature_unit + ) + self._topic = {key: config.get(key) for key in TOPIC_KEYS} # set to None in non-optimistic mode - self._target_temp = ( - self._current_fan_mode - ) = self._current_operation = self._current_swing_mode = None - self._target_temp_low = None - self._target_temp_high = None + self._attr_target_temperature = None + self._attr_fan_mode = None + self._attr_hvac_mode = None + self._attr_swing_mode = None + self._attr_target_temperature_low = None + self._attr_target_temperature_high = None if self._topic[CONF_TEMP_STATE_TOPIC] is None: - self._target_temp = config[CONF_TEMP_INITIAL] + self._attr_target_temperature = config[CONF_TEMP_INITIAL] if self._topic[CONF_TEMP_LOW_STATE_TOPIC] is None: - self._target_temp_low = config[CONF_TEMP_INITIAL] + self._attr_target_temperature_low = config[CONF_TEMP_INITIAL] if self._topic[CONF_TEMP_HIGH_STATE_TOPIC] is None: - self._target_temp_high = config[CONF_TEMP_INITIAL] + self._attr_target_temperature_high = config[CONF_TEMP_INITIAL] if self._topic[CONF_FAN_MODE_STATE_TOPIC] is None: - self._current_fan_mode = FAN_LOW + self._attr_fan_mode = FAN_LOW if self._topic[CONF_SWING_MODE_STATE_TOPIC] is None: - self._current_swing_mode = SWING_OFF + self._attr_swing_mode = SWING_OFF if self._topic[CONF_MODE_STATE_TOPIC] is None: - self._current_operation = HVACMode.OFF + self._attr_hvac_mode = HVACMode.OFF self._feature_preset_mode = CONF_PRESET_MODE_COMMAND_TOPIC in config if self._feature_preset_mode: - self._preset_modes = config[CONF_PRESET_MODES_LIST] + presets = [] + presets.extend(config[CONF_PRESET_MODES_LIST]) + if presets: + presets.insert(0, PRESET_NONE) + self._attr_preset_modes = presets + self._attr_preset_mode = PRESET_NONE else: - self._preset_modes = [] + self._attr_preset_modes = [] self._optimistic_preset_mode = CONF_PRESET_MODE_STATE_TOPIC not in config - self._action = None - self._aux = False + self._attr_hvac_action = None + + self._attr_is_aux_heat = False value_templates = {} for key in VALUE_TEMPLATE_KEYS: @@ -455,437 +463,7 @@ class MqttClimate(MqttEntity, ClimateEntity): self._command_templates = command_templates - def _prepare_subscribe_topics(self): # noqa: C901 - """(Re)Subscribe to topics.""" - topics = {} - qos = self._config[CONF_QOS] - - def add_subscription(topics, topic, msg_callback): - if self._topic[topic] is not None: - topics[topic] = { - "topic": self._topic[topic], - "msg_callback": msg_callback, - "qos": qos, - "encoding": self._config[CONF_ENCODING] or None, - } - - def render_template(msg, template_name): - template = self._value_templates[template_name] - return template(msg.payload) - - @callback - @log_messages(self.hass, self.entity_id) - def handle_action_received(msg): - """Handle receiving action via MQTT.""" - payload = render_template(msg, CONF_ACTION_TEMPLATE) - if not payload or payload == PAYLOAD_NONE: - _LOGGER.debug( - "Invalid %s action: %s, ignoring", - [e.value for e in HVACAction], - payload, - ) - return - try: - self._action = HVACAction(payload) - except ValueError: - _LOGGER.warning( - "Invalid %s action: %s", - [e.value for e in HVACAction], - payload, - ) - return - get_mqtt_data(self.hass).state_write_requests.write_state_request(self) - - add_subscription(topics, CONF_ACTION_TOPIC, handle_action_received) - - @callback - def handle_temperature_received(msg, template_name, attr): - """Handle temperature coming via MQTT.""" - payload = render_template(msg, template_name) - - try: - setattr(self, attr, float(payload)) - get_mqtt_data(self.hass).state_write_requests.write_state_request(self) - except ValueError: - _LOGGER.error("Could not parse temperature from %s", payload) - - @callback - @log_messages(self.hass, self.entity_id) - def handle_current_temperature_received(msg): - """Handle current temperature coming via MQTT.""" - handle_temperature_received( - msg, CONF_CURRENT_TEMP_TEMPLATE, "_current_temp" - ) - - add_subscription( - topics, CONF_CURRENT_TEMP_TOPIC, handle_current_temperature_received - ) - - @callback - @log_messages(self.hass, self.entity_id) - def handle_target_temperature_received(msg): - """Handle target temperature coming via MQTT.""" - handle_temperature_received(msg, CONF_TEMP_STATE_TEMPLATE, "_target_temp") - - add_subscription( - topics, CONF_TEMP_STATE_TOPIC, handle_target_temperature_received - ) - - @callback - @log_messages(self.hass, self.entity_id) - def handle_temperature_low_received(msg): - """Handle target temperature low coming via MQTT.""" - handle_temperature_received( - msg, CONF_TEMP_LOW_STATE_TEMPLATE, "_target_temp_low" - ) - - add_subscription( - topics, CONF_TEMP_LOW_STATE_TOPIC, handle_temperature_low_received - ) - - @callback - @log_messages(self.hass, self.entity_id) - def handle_temperature_high_received(msg): - """Handle target temperature high coming via MQTT.""" - handle_temperature_received( - msg, CONF_TEMP_HIGH_STATE_TEMPLATE, "_target_temp_high" - ) - - add_subscription( - topics, CONF_TEMP_HIGH_STATE_TOPIC, handle_temperature_high_received - ) - - @callback - def handle_mode_received(msg, template_name, attr, mode_list): - """Handle receiving listed mode via MQTT.""" - payload = render_template(msg, template_name) - - if payload not in self._config[mode_list]: - _LOGGER.error("Invalid %s mode: %s", mode_list, payload) - else: - setattr(self, attr, payload) - get_mqtt_data(self.hass).state_write_requests.write_state_request(self) - - @callback - @log_messages(self.hass, self.entity_id) - def handle_current_mode_received(msg): - """Handle receiving mode via MQTT.""" - handle_mode_received( - msg, CONF_MODE_STATE_TEMPLATE, "_current_operation", CONF_MODE_LIST - ) - - add_subscription(topics, CONF_MODE_STATE_TOPIC, handle_current_mode_received) - - @callback - @log_messages(self.hass, self.entity_id) - def handle_fan_mode_received(msg): - """Handle receiving fan mode via MQTT.""" - handle_mode_received( - msg, - CONF_FAN_MODE_STATE_TEMPLATE, - "_current_fan_mode", - CONF_FAN_MODE_LIST, - ) - - add_subscription(topics, CONF_FAN_MODE_STATE_TOPIC, handle_fan_mode_received) - - @callback - @log_messages(self.hass, self.entity_id) - def handle_swing_mode_received(msg): - """Handle receiving swing mode via MQTT.""" - handle_mode_received( - msg, - CONF_SWING_MODE_STATE_TEMPLATE, - "_current_swing_mode", - CONF_SWING_MODE_LIST, - ) - - add_subscription( - topics, CONF_SWING_MODE_STATE_TOPIC, handle_swing_mode_received - ) - - @callback - def handle_onoff_mode_received(msg, template_name, attr): - """Handle receiving on/off mode via MQTT.""" - payload = render_template(msg, template_name) - payload_on = self._config[CONF_PAYLOAD_ON] - payload_off = self._config[CONF_PAYLOAD_OFF] - - if payload == "True": - payload = payload_on - elif payload == "False": - payload = payload_off - - if payload == payload_on: - setattr(self, attr, True) - elif payload == payload_off: - setattr(self, attr, False) - else: - _LOGGER.error("Invalid %s mode: %s", attr, payload) - - get_mqtt_data(self.hass).state_write_requests.write_state_request(self) - - @callback - @log_messages(self.hass, self.entity_id) - def handle_aux_mode_received(msg): - """Handle receiving aux mode via MQTT.""" - handle_onoff_mode_received(msg, CONF_AUX_STATE_TEMPLATE, "_aux") - - add_subscription(topics, CONF_AUX_STATE_TOPIC, handle_aux_mode_received) - - @callback - @log_messages(self.hass, self.entity_id) - def handle_preset_mode_received(msg): - """Handle receiving preset mode via MQTT.""" - preset_mode = render_template(msg, CONF_PRESET_MODE_VALUE_TEMPLATE) - if preset_mode in [PRESET_NONE, PAYLOAD_NONE]: - self._preset_mode = None - get_mqtt_data(self.hass).state_write_requests.write_state_request(self) - return - if not preset_mode: - _LOGGER.debug("Ignoring empty preset_mode from '%s'", msg.topic) - return - if preset_mode not in self._preset_modes: - _LOGGER.warning( - "'%s' received on topic %s. '%s' is not a valid preset mode", - msg.payload, - msg.topic, - preset_mode, - ) - else: - self._preset_mode = preset_mode - get_mqtt_data(self.hass).state_write_requests.write_state_request(self) - - add_subscription( - topics, CONF_PRESET_MODE_STATE_TOPIC, handle_preset_mode_received - ) - - self._sub_state = subscription.async_prepare_subscribe_topics( - self.hass, self._sub_state, topics - ) - - async def _subscribe_topics(self): - """(Re)Subscribe to topics.""" - await subscription.async_subscribe_topics(self.hass, self._sub_state) - - @property - def temperature_unit(self) -> str: - """Return the unit of measurement.""" - if unit := self._config.get(CONF_TEMPERATURE_UNIT): - return unit - return self.hass.config.units.temperature_unit - - @property - def current_temperature(self) -> float | None: - """Return the current temperature.""" - return self._current_temp - - @property - def target_temperature(self) -> float | None: - """Return the temperature we try to reach.""" - return self._target_temp - - @property - def target_temperature_low(self) -> float | None: - """Return the low target temperature we try to reach.""" - return self._target_temp_low - - @property - def target_temperature_high(self) -> float | None: - """Return the high target temperature we try to reach.""" - return self._target_temp_high - - @property - def hvac_action(self) -> HVACAction | None: - """Return the current running hvac operation if supported.""" - return self._action - - @property - def hvac_mode(self) -> HVACMode: - """Return current operation ie. heat, cool, idle.""" - return self._current_operation - - @property - def hvac_modes(self) -> list[HVACMode]: - """Return the list of available operation modes.""" - return self._config[CONF_MODE_LIST] - - @property - def target_temperature_step(self) -> float: - """Return the supported step of target temperature.""" - return self._config[CONF_TEMP_STEP] - - @property - def preset_mode(self) -> str | None: - """Return preset mode.""" - if self._feature_preset_mode and self._preset_mode is not None: - return self._preset_mode - return PRESET_NONE - - @property - def preset_modes(self) -> list[str]: - """Return preset modes.""" - presets = [] - presets.extend(self._preset_modes) - if presets: - presets.insert(0, PRESET_NONE) - - return presets - - @property - def is_aux_heat(self) -> bool | None: - """Return true if away mode is on.""" - return self._aux - - @property - def fan_mode(self) -> str | None: - """Return the fan setting.""" - return self._current_fan_mode - - @property - def fan_modes(self) -> list[str]: - """Return the list of available fan modes.""" - return self._config[CONF_FAN_MODE_LIST] - - async def _publish(self, topic, payload): - if self._topic[topic] is not None: - await self.async_publish( - self._topic[topic], - payload, - self._config[CONF_QOS], - self._config[CONF_RETAIN], - self._config[CONF_ENCODING], - ) - - async def _set_temperature( - self, temp, cmnd_topic, cmnd_template, state_topic, attr - ): - if temp is not None: - if self._topic[state_topic] is None: - # optimistic mode - setattr(self, attr, temp) - - payload = self._command_templates[cmnd_template](temp) - await self._publish(cmnd_topic, payload) - - async def async_set_temperature(self, **kwargs: Any) -> None: - """Set new target temperatures.""" - if (operation_mode := kwargs.get(ATTR_HVAC_MODE)) is not None: - await self.async_set_hvac_mode(operation_mode) - - await self._set_temperature( - kwargs.get(ATTR_TEMPERATURE), - CONF_TEMP_COMMAND_TOPIC, - CONF_TEMP_COMMAND_TEMPLATE, - CONF_TEMP_STATE_TOPIC, - "_target_temp", - ) - - await self._set_temperature( - kwargs.get(ATTR_TARGET_TEMP_LOW), - CONF_TEMP_LOW_COMMAND_TOPIC, - CONF_TEMP_LOW_COMMAND_TEMPLATE, - CONF_TEMP_LOW_STATE_TOPIC, - "_target_temp_low", - ) - - await self._set_temperature( - kwargs.get(ATTR_TARGET_TEMP_HIGH), - CONF_TEMP_HIGH_COMMAND_TOPIC, - CONF_TEMP_HIGH_COMMAND_TEMPLATE, - CONF_TEMP_HIGH_STATE_TOPIC, - "_target_temp_high", - ) - - self.async_write_ha_state() - - async def async_set_swing_mode(self, swing_mode: str) -> None: - """Set new swing mode.""" - payload = self._command_templates[CONF_SWING_MODE_COMMAND_TEMPLATE](swing_mode) - await self._publish(CONF_SWING_MODE_COMMAND_TOPIC, payload) - - if self._topic[CONF_SWING_MODE_STATE_TOPIC] is None: - self._current_swing_mode = swing_mode - self.async_write_ha_state() - - async def async_set_fan_mode(self, fan_mode: str) -> None: - """Set new target temperature.""" - payload = self._command_templates[CONF_FAN_MODE_COMMAND_TEMPLATE](fan_mode) - await self._publish(CONF_FAN_MODE_COMMAND_TOPIC, payload) - - if self._topic[CONF_FAN_MODE_STATE_TOPIC] is None: - self._current_fan_mode = fan_mode - self.async_write_ha_state() - - async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: - """Set new operation mode.""" - if hvac_mode == HVACMode.OFF: - await self._publish( - CONF_POWER_COMMAND_TOPIC, self._config[CONF_PAYLOAD_OFF] - ) - else: - await self._publish(CONF_POWER_COMMAND_TOPIC, self._config[CONF_PAYLOAD_ON]) - - payload = self._command_templates[CONF_MODE_COMMAND_TEMPLATE](hvac_mode) - await self._publish(CONF_MODE_COMMAND_TOPIC, payload) - - if self._topic[CONF_MODE_STATE_TOPIC] is None: - self._current_operation = hvac_mode - self.async_write_ha_state() - - @property - def swing_mode(self) -> str | None: - """Return the swing setting.""" - return self._current_swing_mode - - @property - def swing_modes(self) -> list[str]: - """List of available swing modes.""" - return self._config[CONF_SWING_MODE_LIST] - - async def async_set_preset_mode(self, preset_mode: str) -> None: - """Set a preset mode.""" - if self._feature_preset_mode: - if preset_mode not in self.preset_modes and preset_mode is not PRESET_NONE: - _LOGGER.warning("'%s' is not a valid preset mode", preset_mode) - return - mqtt_payload = self._command_templates[CONF_PRESET_MODE_COMMAND_TEMPLATE]( - preset_mode - ) - await self._publish( - CONF_PRESET_MODE_COMMAND_TOPIC, - mqtt_payload, - ) - - if self._optimistic_preset_mode: - self._preset_mode = preset_mode if preset_mode != PRESET_NONE else None - self.async_write_ha_state() - - return - - async def _set_aux_heat(self, state): - await self._publish( - CONF_AUX_COMMAND_TOPIC, - self._config[CONF_PAYLOAD_ON] if state else self._config[CONF_PAYLOAD_OFF], - ) - - if self._topic[CONF_AUX_STATE_TOPIC] is None: - self._aux = state - self.async_write_ha_state() - - async def async_turn_aux_heat_on(self) -> None: - """Turn auxiliary heater on.""" - await self._set_aux_heat(True) - - async def async_turn_aux_heat_off(self) -> None: - """Turn auxiliary heater off.""" - await self._set_aux_heat(False) - - @property - def supported_features(self) -> int: - """Return the list of supported features.""" - support = 0 - + support: int = 0 if (self._topic[CONF_TEMP_STATE_TOPIC] is not None) or ( self._topic[CONF_TEMP_COMMAND_TOPIC] is not None ): @@ -918,22 +496,346 @@ class MqttClimate(MqttEntity, ClimateEntity): self._topic[CONF_AUX_COMMAND_TOPIC] is not None ): support |= ClimateEntityFeature.AUX_HEAT + self._attr_supported_features = support - return support + def _prepare_subscribe_topics(self): # noqa: C901 + """(Re)Subscribe to topics.""" + topics = {} + qos = self._config[CONF_QOS] - @property - def min_temp(self) -> float: - """Return the minimum temperature.""" - return self._config[CONF_TEMP_MIN] + def add_subscription(topics, topic, msg_callback): + if self._topic[topic] is not None: + topics[topic] = { + "topic": self._topic[topic], + "msg_callback": msg_callback, + "qos": qos, + "encoding": self._config[CONF_ENCODING] or None, + } - @property - def max_temp(self) -> float: - """Return the maximum temperature.""" - return self._config[CONF_TEMP_MAX] + def render_template(msg, template_name): + template = self._value_templates[template_name] + return template(msg.payload) - @property - def precision(self) -> float: - """Return the precision of the system.""" - if (precision := self._config.get(CONF_PRECISION)) is not None: - return precision - return super().precision + @callback + @log_messages(self.hass, self.entity_id) + def handle_action_received(msg): + """Handle receiving action via MQTT.""" + payload = render_template(msg, CONF_ACTION_TEMPLATE) + if not payload or payload == PAYLOAD_NONE: + _LOGGER.debug( + "Invalid %s action: %s, ignoring", + [e.value for e in HVACAction], + payload, + ) + return + try: + self._attr_hvac_action = HVACAction(payload) + except ValueError: + _LOGGER.warning( + "Invalid %s action: %s", + [e.value for e in HVACAction], + payload, + ) + return + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + + add_subscription(topics, CONF_ACTION_TOPIC, handle_action_received) + + @callback + def handle_temperature_received(msg, template_name, attr): + """Handle temperature coming via MQTT.""" + payload = render_template(msg, template_name) + + try: + setattr(self, attr, float(payload)) + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + except ValueError: + _LOGGER.error("Could not parse temperature from %s", payload) + + @callback + @log_messages(self.hass, self.entity_id) + def handle_current_temperature_received(msg): + """Handle current temperature coming via MQTT.""" + handle_temperature_received( + msg, CONF_CURRENT_TEMP_TEMPLATE, "_attr_current_temperature" + ) + + add_subscription( + topics, CONF_CURRENT_TEMP_TOPIC, handle_current_temperature_received + ) + + @callback + @log_messages(self.hass, self.entity_id) + def handle_target_temperature_received(msg): + """Handle target temperature coming via MQTT.""" + handle_temperature_received( + msg, CONF_TEMP_STATE_TEMPLATE, "_attr_target_temperature" + ) + + add_subscription( + topics, CONF_TEMP_STATE_TOPIC, handle_target_temperature_received + ) + + @callback + @log_messages(self.hass, self.entity_id) + def handle_temperature_low_received(msg): + """Handle target temperature low coming via MQTT.""" + handle_temperature_received( + msg, CONF_TEMP_LOW_STATE_TEMPLATE, "_attr_target_temperature_low" + ) + + add_subscription( + topics, CONF_TEMP_LOW_STATE_TOPIC, handle_temperature_low_received + ) + + @callback + @log_messages(self.hass, self.entity_id) + def handle_temperature_high_received(msg): + """Handle target temperature high coming via MQTT.""" + handle_temperature_received( + msg, CONF_TEMP_HIGH_STATE_TEMPLATE, "_attr_target_temperature_high" + ) + + add_subscription( + topics, CONF_TEMP_HIGH_STATE_TOPIC, handle_temperature_high_received + ) + + @callback + def handle_mode_received(msg, template_name, attr, mode_list): + """Handle receiving listed mode via MQTT.""" + payload = render_template(msg, template_name) + + if payload not in self._config[mode_list]: + _LOGGER.error("Invalid %s mode: %s", mode_list, payload) + else: + setattr(self, attr, payload) + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + + @callback + @log_messages(self.hass, self.entity_id) + def handle_current_mode_received(msg): + """Handle receiving mode via MQTT.""" + handle_mode_received( + msg, CONF_MODE_STATE_TEMPLATE, "_attr_hvac_mode", CONF_MODE_LIST + ) + + add_subscription(topics, CONF_MODE_STATE_TOPIC, handle_current_mode_received) + + @callback + @log_messages(self.hass, self.entity_id) + def handle_fan_mode_received(msg): + """Handle receiving fan mode via MQTT.""" + handle_mode_received( + msg, + CONF_FAN_MODE_STATE_TEMPLATE, + "_attr_fan_mode", + CONF_FAN_MODE_LIST, + ) + + add_subscription(topics, CONF_FAN_MODE_STATE_TOPIC, handle_fan_mode_received) + + @callback + @log_messages(self.hass, self.entity_id) + def handle_swing_mode_received(msg): + """Handle receiving swing mode via MQTT.""" + handle_mode_received( + msg, + CONF_SWING_MODE_STATE_TEMPLATE, + "_attr_swing_mode", + CONF_SWING_MODE_LIST, + ) + + add_subscription( + topics, CONF_SWING_MODE_STATE_TOPIC, handle_swing_mode_received + ) + + @callback + def handle_onoff_mode_received(msg, template_name, attr): + """Handle receiving on/off mode via MQTT.""" + payload = render_template(msg, template_name) + payload_on = self._config[CONF_PAYLOAD_ON] + payload_off = self._config[CONF_PAYLOAD_OFF] + + if payload == "True": + payload = payload_on + elif payload == "False": + payload = payload_off + + if payload == payload_on: + setattr(self, attr, True) + elif payload == payload_off: + setattr(self, attr, False) + else: + _LOGGER.error("Invalid %s mode: %s", attr, payload) + + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + + @callback + @log_messages(self.hass, self.entity_id) + def handle_aux_mode_received(msg): + """Handle receiving aux mode via MQTT.""" + handle_onoff_mode_received( + msg, CONF_AUX_STATE_TEMPLATE, "_attr_is_aux_heat" + ) + + add_subscription(topics, CONF_AUX_STATE_TOPIC, handle_aux_mode_received) + + @callback + @log_messages(self.hass, self.entity_id) + def handle_preset_mode_received(msg): + """Handle receiving preset mode via MQTT.""" + preset_mode = render_template(msg, CONF_PRESET_MODE_VALUE_TEMPLATE) + if preset_mode in [PRESET_NONE, PAYLOAD_NONE]: + self._attr_preset_mode = PRESET_NONE + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + return + if not preset_mode: + _LOGGER.debug("Ignoring empty preset_mode from '%s'", msg.topic) + return + if preset_mode not in self.preset_modes: + _LOGGER.warning( + "'%s' received on topic %s. '%s' is not a valid preset mode", + msg.payload, + msg.topic, + preset_mode, + ) + else: + self._attr_preset_mode = preset_mode + + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + + add_subscription( + topics, CONF_PRESET_MODE_STATE_TOPIC, handle_preset_mode_received + ) + + self._sub_state = subscription.async_prepare_subscribe_topics( + self.hass, self._sub_state, topics + ) + + async def _subscribe_topics(self): + """(Re)Subscribe to topics.""" + await subscription.async_subscribe_topics(self.hass, self._sub_state) + + async def _publish(self, topic, payload): + if self._topic[topic] is not None: + await self.async_publish( + self._topic[topic], + payload, + self._config[CONF_QOS], + self._config[CONF_RETAIN], + self._config[CONF_ENCODING], + ) + + async def _set_temperature( + self, temp, cmnd_topic, cmnd_template, state_topic, attr + ): + if temp is not None: + if self._topic[state_topic] is None: + # optimistic mode + setattr(self, attr, temp) + + payload = self._command_templates[cmnd_template](temp) + await self._publish(cmnd_topic, payload) + + async def async_set_temperature(self, **kwargs: Any) -> None: + """Set new target temperatures.""" + if (operation_mode := kwargs.get(ATTR_HVAC_MODE)) is not None: + await self.async_set_hvac_mode(operation_mode) + + await self._set_temperature( + kwargs.get(ATTR_TEMPERATURE), + CONF_TEMP_COMMAND_TOPIC, + CONF_TEMP_COMMAND_TEMPLATE, + CONF_TEMP_STATE_TOPIC, + "_attr_target_temperature", + ) + + await self._set_temperature( + kwargs.get(ATTR_TARGET_TEMP_LOW), + CONF_TEMP_LOW_COMMAND_TOPIC, + CONF_TEMP_LOW_COMMAND_TEMPLATE, + CONF_TEMP_LOW_STATE_TOPIC, + "_attr_target_temperature_low", + ) + + await self._set_temperature( + kwargs.get(ATTR_TARGET_TEMP_HIGH), + CONF_TEMP_HIGH_COMMAND_TOPIC, + CONF_TEMP_HIGH_COMMAND_TEMPLATE, + CONF_TEMP_HIGH_STATE_TOPIC, + "_attr_target_temperature_high", + ) + + self.async_write_ha_state() + + async def async_set_swing_mode(self, swing_mode: str) -> None: + """Set new swing mode.""" + payload = self._command_templates[CONF_SWING_MODE_COMMAND_TEMPLATE](swing_mode) + await self._publish(CONF_SWING_MODE_COMMAND_TOPIC, payload) + + if self._topic[CONF_SWING_MODE_STATE_TOPIC] is None: + self._attr_swing_mode = swing_mode + self.async_write_ha_state() + + async def async_set_fan_mode(self, fan_mode: str) -> None: + """Set new target temperature.""" + payload = self._command_templates[CONF_FAN_MODE_COMMAND_TEMPLATE](fan_mode) + await self._publish(CONF_FAN_MODE_COMMAND_TOPIC, payload) + + if self._topic[CONF_FAN_MODE_STATE_TOPIC] is None: + self._attr_fan_mode = fan_mode + self.async_write_ha_state() + + async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: + """Set new operation mode.""" + if hvac_mode == HVACMode.OFF: + await self._publish( + CONF_POWER_COMMAND_TOPIC, self._config[CONF_PAYLOAD_OFF] + ) + else: + await self._publish(CONF_POWER_COMMAND_TOPIC, self._config[CONF_PAYLOAD_ON]) + + payload = self._command_templates[CONF_MODE_COMMAND_TEMPLATE](hvac_mode) + await self._publish(CONF_MODE_COMMAND_TOPIC, payload) + + if self._topic[CONF_MODE_STATE_TOPIC] is None: + self._attr_hvac_mode = hvac_mode + self.async_write_ha_state() + + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Set a preset mode.""" + if self._feature_preset_mode and self.preset_modes: + if preset_mode not in self.preset_modes and preset_mode is not PRESET_NONE: + _LOGGER.warning("'%s' is not a valid preset mode", preset_mode) + return + mqtt_payload = self._command_templates[CONF_PRESET_MODE_COMMAND_TEMPLATE]( + preset_mode + ) + await self._publish( + CONF_PRESET_MODE_COMMAND_TOPIC, + mqtt_payload, + ) + + if self._optimistic_preset_mode: + self._attr_preset_mode = preset_mode + self.async_write_ha_state() + + return + + async def _set_aux_heat(self, state): + await self._publish( + CONF_AUX_COMMAND_TOPIC, + self._config[CONF_PAYLOAD_ON] if state else self._config[CONF_PAYLOAD_OFF], + ) + + if self._topic[CONF_AUX_STATE_TOPIC] is None: + self._attr_is_aux_heat = state + self.async_write_ha_state() + + async def async_turn_aux_heat_on(self) -> None: + """Turn auxiliary heater on.""" + await self._set_aux_heat(True) + + async def async_turn_aux_heat_off(self) -> None: + """Turn auxiliary heater off.""" + await self._set_aux_heat(False) From 08772004b360d83122021df54b9b6e3a47bff39b Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 3 Nov 2022 11:13:23 +0100 Subject: [PATCH 0188/1033] Fix SSDP failure to start on missing URLs (#81453) --- homeassistant/components/ssdp/__init__.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/ssdp/__init__.py b/homeassistant/components/ssdp/__init__.py index d081ef877de..8e037602b90 100644 --- a/homeassistant/components/ssdp/__init__.py +++ b/homeassistant/components/ssdp/__init__.py @@ -52,7 +52,7 @@ from homeassistant.helpers import discovery_flow from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.instance_id import async_get as async_get_instance_id -from homeassistant.helpers.network import get_url +from homeassistant.helpers.network import NoURLAvailableError, get_url from homeassistant.helpers.system_info import async_get_system_info from homeassistant.helpers.typing import ConfigType from homeassistant.loader import async_get_ssdp, bind_hass @@ -697,7 +697,16 @@ class Server: udn = await self._async_get_instance_udn() system_info = await async_get_system_info(self.hass) model_name = system_info["installation_type"] - presentation_url = get_url(self.hass, allow_ip=True, prefer_external=False) + try: + presentation_url = get_url(self.hass, allow_ip=True, prefer_external=False) + except NoURLAvailableError: + _LOGGER.warning( + "Could not set up UPnP/SSDP server, as a presentation URL could" + " not be determined; Please configure your internal URL" + " in the Home Assistant general configuration" + ) + return + serial_number = await async_get_instance_id(self.hass) HassUpnpServiceDevice.DEVICE_DEFINITION = ( HassUpnpServiceDevice.DEVICE_DEFINITION._replace( From 454f328a36f0b3a19058a50ad2b76c1a6dae73db Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 3 Nov 2022 11:14:01 +0100 Subject: [PATCH 0189/1033] Bump aiohomekit to 2.2.14 (#81454) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 18884d59307..b2aec75c3ad 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.13"], + "requirements": ["aiohomekit==2.2.14"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index e5973baf0cb..9e0c18e2988 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.13 +aiohomekit==2.2.14 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8dd8d3592fc..5023fce634d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.13 +aiohomekit==2.2.14 # homeassistant.components.emulated_hue # homeassistant.components.http From 7556f2b84e892178cf8ba02c2ee91e0ddfb9e870 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 3 Nov 2022 11:18:25 +0100 Subject: [PATCH 0190/1033] Update cryptography to 38.0.3 (#81455) --- homeassistant/package_constraints.txt | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 626e88420ea..17674a0b1b4 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -16,7 +16,7 @@ bluetooth-adapters==0.6.0 bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 -cryptography==38.0.1 +cryptography==38.0.3 dbus-fast==1.61.1 fnvhash==0.1.0 hass-nabucasa==0.56.0 diff --git a/pyproject.toml b/pyproject.toml index f5b5908f0d2..3a74b2c5994 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ dependencies = [ "lru-dict==1.1.8", "PyJWT==2.5.0", # PyJWT has loose dependency. We want the latest one. - "cryptography==38.0.1", + "cryptography==38.0.3", "orjson==3.8.1", "pip>=21.0,<22.4", "python-slugify==4.0.1", diff --git a/requirements.txt b/requirements.txt index 962a9d59dc6..96a9f801df9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ ifaddr==0.1.7 jinja2==3.1.2 lru-dict==1.1.8 PyJWT==2.5.0 -cryptography==38.0.1 +cryptography==38.0.3 orjson==3.8.1 pip>=21.0,<22.4 python-slugify==4.0.1 From 918940a0c681a453a4da46b8afbc5d6300eb1e4f Mon Sep 17 00:00:00 2001 From: hahn-th Date: Thu, 3 Nov 2022 12:03:49 +0100 Subject: [PATCH 0191/1033] Add HmIP-WGC to homematicip_cloud integration (#75733) * Add HmIP-WGC * remove unused code * removed test test_manually_configured_platform --- .../components/homematicip_cloud/button.py | 41 +++++++ .../components/homematicip_cloud/const.py | 1 + .../homematicip_cloud/test_button.py | 39 +++++++ .../homematicip_cloud/test_device.py | 2 +- tests/fixtures/homematicip_cloud.json | 105 ++++++++++++++++++ 5 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/homematicip_cloud/button.py create mode 100644 tests/components/homematicip_cloud/test_button.py diff --git a/homeassistant/components/homematicip_cloud/button.py b/homeassistant/components/homematicip_cloud/button.py new file mode 100644 index 00000000000..3fb8ebe20bd --- /dev/null +++ b/homeassistant/components/homematicip_cloud/button.py @@ -0,0 +1,41 @@ +"""Support for HomematicIP Cloud button devices.""" +from __future__ import annotations + +from homematicip.aio.device import AsyncWallMountedGarageDoorController + +from homeassistant.components.button import ButtonEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import DOMAIN as HMIPC_DOMAIN, HomematicipGenericEntity +from .hap import HomematicipHAP + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the HomematicIP button from a config entry.""" + hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id] + entities: list[HomematicipGenericEntity] = [] + for device in hap.home.devices: + if isinstance(device, AsyncWallMountedGarageDoorController): + entities.append(HomematicipGarageDoorControllerButton(hap, device)) + + if entities: + async_add_entities(entities) + + +class HomematicipGarageDoorControllerButton(HomematicipGenericEntity, ButtonEntity): + """Representation of the HomematicIP Wall mounted Garage Door Controller.""" + + def __init__(self, hap: HomematicipHAP, device) -> None: + """Initialize a wall mounted garage door controller.""" + super().__init__(hap, device) + self._attr_icon = "mdi:arrow-up-down" + + async def async_press(self) -> None: + """Handle the button press.""" + await self._device.send_start_impulse() diff --git a/homeassistant/components/homematicip_cloud/const.py b/homeassistant/components/homematicip_cloud/const.py index a0f1c84015f..055db90a68c 100644 --- a/homeassistant/components/homematicip_cloud/const.py +++ b/homeassistant/components/homematicip_cloud/const.py @@ -10,6 +10,7 @@ DOMAIN = "homematicip_cloud" PLATFORMS = [ Platform.ALARM_CONTROL_PANEL, Platform.BINARY_SENSOR, + Platform.BUTTON, Platform.CLIMATE, Platform.COVER, Platform.LIGHT, diff --git a/tests/components/homematicip_cloud/test_button.py b/tests/components/homematicip_cloud/test_button.py new file mode 100644 index 00000000000..c399bc99327 --- /dev/null +++ b/tests/components/homematicip_cloud/test_button.py @@ -0,0 +1,39 @@ +"""Tests for HomematicIP Cloud button.""" + +from unittest.mock import patch + +from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN +from homeassistant.components.button.const import SERVICE_PRESS +from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN +from homeassistant.util import dt as dt_util + +from .helper import get_and_check_entity_basics + + +async def test_hmip_garage_door_controller_button(hass, default_mock_hap_factory): + """Test HomematicipGarageDoorControllerButton.""" + entity_id = "button.garagentor" + entity_name = "Garagentor" + device_model = "HmIP-WGC" + mock_hap = await default_mock_hap_factory.async_get_mock_hap( + test_devices=[entity_name] + ) + + get_and_check_entity_basics(hass, mock_hap, entity_id, entity_name, device_model) + + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_UNKNOWN + + now = dt_util.parse_datetime("2021-01-09 12:00:00+00:00") + with patch("homeassistant.util.dt.utcnow", return_value=now): + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + state = hass.states.get(entity_id) + assert state + assert state.state == now.isoformat() diff --git a/tests/components/homematicip_cloud/test_device.py b/tests/components/homematicip_cloud/test_device.py index 44b91c4ed47..52b92fdff9a 100644 --- a/tests/components/homematicip_cloud/test_device.py +++ b/tests/components/homematicip_cloud/test_device.py @@ -22,7 +22,7 @@ async def test_hmip_load_all_supported_devices(hass, default_mock_hap_factory): test_devices=None, test_groups=None ) - assert len(mock_hap.hmip_device_by_entity_id) == 262 + assert len(mock_hap.hmip_device_by_entity_id) == 264 async def test_hmip_remove_device(hass, default_mock_hap_factory): diff --git a/tests/fixtures/homematicip_cloud.json b/tests/fixtures/homematicip_cloud.json index b0037aa3800..5a7b0516763 100644 --- a/tests/fixtures/homematicip_cloud.json +++ b/tests/fixtures/homematicip_cloud.json @@ -6725,6 +6725,111 @@ "serializedGlobalTradeItemNumber": "3014F7110000000000STE2015", "type": "TEMPERATURE_SENSOR_2_EXTERNAL_DELTA", "updateState": "TRANSFERING_UPDATE" + }, + "3014F7110000000000000WGC": { + "availableFirmwareVersion": "1.0.2", + "connectionType": "HMIP_RF", + "firmwareVersion": "1.0.2", + "firmwareVersionInteger": 65538, + "functionalChannels": { + "0": { + "busConfigMismatch": null, + "coProFaulty": false, + "coProRestartNeeded": false, + "coProUpdateFailure": false, + "configPending": false, + "deviceId": "3014F7110000000000000WGC", + "deviceOverheated": false, + "deviceOverloaded": false, + "devicePowerFailureDetected": false, + "deviceUndervoltage": false, + "displayContrast": null, + "dutyCycle": false, + "functionalChannelType": "DEVICE_BASE", + "groupIndex": 0, + "groups": ["00000000-0000-0000-0000-000000000011"], + "index": 0, + "label": "", + "lowBat": false, + "mountingOrientation": null, + "multicastRoutingEnabled": false, + "particulateMatterSensorCommunicationError": null, + "particulateMatterSensorError": null, + "powerShortCircuit": null, + "profilePeriodLimitReached": null, + "routerModuleEnabled": false, + "routerModuleSupported": false, + "rssiDeviceValue": -83, + "rssiPeerValue": -77, + "shortCircuitDataLine": null, + "supportedOptionalFeatures": { + "IFeatureBusConfigMismatch": false, + "IFeatureDeviceCoProError": false, + "IFeatureDeviceCoProRestart": false, + "IFeatureDeviceCoProUpdate": false, + "IFeatureDeviceIdentify": false, + "IFeatureDeviceOverheated": false, + "IFeatureDeviceOverloaded": false, + "IFeatureDeviceParticulateMatterSensorCommunicationError": false, + "IFeatureDeviceParticulateMatterSensorError": false, + "IFeatureDevicePowerFailure": false, + "IFeatureDeviceTemperatureHumiditySensorCommunicationError": false, + "IFeatureDeviceTemperatureHumiditySensorError": false, + "IFeatureDeviceTemperatureOutOfRange": false, + "IFeatureDeviceUndervoltage": true, + "IFeatureMulticastRouter": false, + "IFeaturePowerShortCircuit": false, + "IFeatureProfilePeriodLimit": false, + "IFeatureRssiValue": true, + "IFeatureShortCircuitDataLine": false, + "IOptionalFeatureDisplayContrast": false, + "IOptionalFeatureDutyCycle": true, + "IOptionalFeatureLowBat": true, + "IOptionalFeatureMountingOrientation": false + }, + "temperatureHumiditySensorCommunicationError": null, + "temperatureHumiditySensorError": null, + "temperatureOutOfRange": false, + "unreach": false + }, + "1": { + "deviceId": "3014F7110000000000000WGC", + "functionalChannelType": "SINGLE_KEY_CHANNEL", + "groupIndex": 1, + "groups": [ + "00000000-0000-0000-0000-000000000012", + "00000000-0000-0000-0000-000000000013" + ], + "index": 1, + "label": "" + }, + "2": { + "deviceId": "3014F7110000000000000WGC", + "functionalChannelType": "IMPULSE_OUTPUT_CHANNEL", + "groupIndex": 2, + "groups": [ + "00000000-0000-0000-0000-000000000012", + "00000000-0000-0000-0000-000000000013" + ], + "impulseDuration": 0.4, + "index": 2, + "label": "Taste", + "processing": false + } + }, + "homeId": "00000000-0000-0000-0000-000000000001", + "id": "3014F7110000000000000WGC", + "label": "Garagentor", + "lastStatusUpdate": 1630920800279, + "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", + "manufacturerCode": 1, + "modelId": 331, + "modelType": "HmIP-WGC", + "oem": "eQ-3", + "permanentlyReachable": true, + "serializedGlobalTradeItemNumber": "3014F7110000000000000WGC", + "type": "WALL_MOUNTED_GARAGE_DOOR_CONTROLLER", + "updateState": "UP_TO_DATE" } }, "groups": { From 905005e1e80c61e3262c3f3e13af455b52fab59f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 3 Nov 2022 12:51:38 +0100 Subject: [PATCH 0192/1033] Bump dbus-fast 1.64.0 (#81462) - Performance improvements changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v1.61.1...v1.64.0 --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 89281323541..bcb7ce9b156 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.8.2", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", - "dbus-fast==1.61.1" + "dbus-fast==1.64.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 17674a0b1b4..160f313d883 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.3 -dbus-fast==1.61.1 +dbus-fast==1.64.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 9e0c18e2988..5977ba97286 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -543,7 +543,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.61.1 +dbus-fast==1.64.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5023fce634d..a90f9060d06 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -423,7 +423,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.61.1 +dbus-fast==1.64.0 # homeassistant.components.debugpy debugpy==1.6.3 From dcd1ab7ec3ec0518493045d2e66db2cb258e589c Mon Sep 17 00:00:00 2001 From: Dennis Schroer Date: Thu, 3 Nov 2022 12:53:58 +0100 Subject: [PATCH 0193/1033] Update energyflip-client dependency to 0.2.2 (#81426) --- homeassistant/components/huisbaasje/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/huisbaasje/manifest.json b/homeassistant/components/huisbaasje/manifest.json index 2963a82512b..47d47da182b 100644 --- a/homeassistant/components/huisbaasje/manifest.json +++ b/homeassistant/components/huisbaasje/manifest.json @@ -3,7 +3,7 @@ "name": "Huisbaasje", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/huisbaasje", - "requirements": ["energyflip-client==0.2.1"], + "requirements": ["energyflip-client==0.2.2"], "codeowners": ["@dennisschroer"], "iot_class": "cloud_polling", "loggers": ["huisbaasje"] diff --git a/requirements_all.txt b/requirements_all.txt index 5977ba97286..6bb67baf2ef 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -626,7 +626,7 @@ elmax_api==0.0.2 emulated_roku==0.2.1 # homeassistant.components.huisbaasje -energyflip-client==0.2.1 +energyflip-client==0.2.2 # homeassistant.components.enocean enocean==0.50 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a90f9060d06..e4fc9f18e79 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -479,7 +479,7 @@ elmax_api==0.0.2 emulated_roku==0.2.1 # homeassistant.components.huisbaasje -energyflip-client==0.2.1 +energyflip-client==0.2.2 # homeassistant.components.enocean enocean==0.50 From b3403d7fca602603fe92c862b852ad25fcc64788 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 3 Nov 2022 13:06:53 +0100 Subject: [PATCH 0194/1033] Improve MQTT type hints part 3 (#80542) * Improve typing debug_info * Improve typing device_automation * Improve typing device_trigger * Improve typing fan * Additional type hints device_trigger * Set fan type hints to class level * Cleanup and mypy * Follow up and missed hint * Follow up comment --- homeassistant/components/mqtt/debug_info.py | 2 +- .../components/mqtt/device_automation.py | 16 ++- .../components/mqtt/device_trigger.py | 21 ++-- homeassistant/components/mqtt/fan.py | 109 ++++++++++-------- 4 files changed, 90 insertions(+), 58 deletions(-) diff --git a/homeassistant/components/mqtt/debug_info.py b/homeassistant/components/mqtt/debug_info.py index 5fae98eaea5..bdbdd74de96 100644 --- a/homeassistant/components/mqtt/debug_info.py +++ b/homeassistant/components/mqtt/debug_info.py @@ -28,7 +28,7 @@ def log_messages( debug_info_entities = get_mqtt_data(hass).debug_info_entities - def _log_message(msg): + def _log_message(msg: Any) -> None: """Log message.""" messages = debug_info_entities[entity_id]["subscriptions"][ msg.subscribed_topic diff --git a/homeassistant/components/mqtt/device_automation.py b/homeassistant/components/mqtt/device_automation.py index 0646a5bda0c..a1bc2cdeb3e 100644 --- a/homeassistant/components/mqtt/device_automation.py +++ b/homeassistant/components/mqtt/device_automation.py @@ -1,9 +1,14 @@ """Provides device automations for MQTT.""" +from __future__ import annotations + import functools import voluptuous as vol +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import device_trigger from .config import MQTT_BASE_SCHEMA @@ -20,14 +25,19 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend( ).extend(MQTT_BASE_SCHEMA.schema) -async def async_setup_entry(hass, config_entry): +async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> None: """Set up MQTT device automation dynamically through MQTT discovery.""" setup = functools.partial(_async_setup_automation, hass, config_entry=config_entry) await async_setup_entry_helper(hass, "device_automation", setup, PLATFORM_SCHEMA) -async def _async_setup_automation(hass, config, config_entry, discovery_data): +async def _async_setup_automation( + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType, +) -> None: """Set up an MQTT device automation.""" if config[CONF_AUTOMATION_TYPE] == AUTOMATION_TYPE_TRIGGER: await device_trigger.async_setup_trigger( @@ -35,6 +45,6 @@ async def _async_setup_automation(hass, config, config_entry, discovery_data): ) -async def async_removed_from_device(hass, device_id): +async def async_removed_from_device(hass: HomeAssistant, device_id: str) -> None: """Handle Mqtt removed from a device.""" await device_trigger.async_removed_from_device(hass, device_id) diff --git a/homeassistant/components/mqtt/device_trigger.py b/homeassistant/components/mqtt/device_trigger.py index f51731284cc..e8bcf1abc48 100644 --- a/homeassistant/components/mqtt/device_trigger.py +++ b/homeassistant/components/mqtt/device_trigger.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Callable import logging -from typing import cast +from typing import Any, cast import attr import voluptuous as vol @@ -23,7 +23,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import debug_info, trigger as mqtt_trigger from .config import MQTT_BASE_SCHEMA @@ -35,7 +35,7 @@ from .const import ( CONF_TOPIC, DOMAIN, ) -from .discovery import MQTT_DISCOVERY_DONE +from .discovery import MQTT_DISCOVERY_DONE, MQTTDiscoveryPayload from .mixins import ( MQTT_ENTITY_DEVICE_INFO_SCHEMA, MqttDiscoveryDeviceUpdate, @@ -96,7 +96,7 @@ class TriggerInstance: async def async_attach_trigger(self) -> None: """Attach MQTT trigger.""" - mqtt_config = { + mqtt_config: dict[str, Any] = { CONF_PLATFORM: DOMAIN, CONF_TOPIC: self.trigger.topic, CONF_ENCODING: DEFAULT_ENCODING, @@ -123,7 +123,7 @@ class Trigger: """Device trigger settings.""" device_id: str = attr.ib() - discovery_data: dict | None = attr.ib() + discovery_data: DiscoveryInfoType | None = attr.ib() hass: HomeAssistant = attr.ib() payload: str | None = attr.ib() qos: int | None = attr.ib() @@ -193,7 +193,7 @@ class MqttDeviceTrigger(MqttDiscoveryDeviceUpdate): hass: HomeAssistant, config: ConfigType, device_id: str, - discovery_data: dict, + discovery_data: DiscoveryInfoType, config_entry: ConfigEntry, ) -> None: """Initialize.""" @@ -237,7 +237,7 @@ class MqttDeviceTrigger(MqttDiscoveryDeviceUpdate): self.hass, discovery_hash, self.discovery_data, self.device_id ) - async def async_update(self, discovery_data: dict) -> None: + async def async_update(self, discovery_data: MQTTDiscoveryPayload) -> None: """Handle MQTT device trigger discovery updates.""" discovery_hash = self.discovery_data[ATTR_DISCOVERY_HASH] discovery_id = discovery_hash[1] @@ -261,11 +261,14 @@ class MqttDeviceTrigger(MqttDiscoveryDeviceUpdate): async def async_setup_trigger( - hass, config: ConfigType, config_entry: ConfigEntry, discovery_data: dict + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType, ) -> None: """Set up the MQTT device trigger.""" config = TRIGGER_DISCOVERY_SCHEMA(config) - discovery_hash = discovery_data[ATTR_DISCOVERY_HASH] + discovery_hash: tuple[str, str] = discovery_data[ATTR_DISCOVERY_HASH] if (device_id := update_device(hass, config_entry, config)) is None: async_dispatcher_send(hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None) diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 1b6c3e425a4..8a65b909eb8 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -1,6 +1,7 @@ """Support for MQTT fans.""" from __future__ import annotations +from collections.abc import Callable import functools import logging import math @@ -27,6 +28,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.template import Template from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util.percentage import ( int_states_in_range, @@ -54,7 +56,13 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttCommandTemplate, MqttValueTemplate +from .models import ( + MqttCommandTemplate, + MqttValueTemplate, + PublishPayloadType, + ReceiveMessage, + ReceivePayloadType, +) from .util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic CONF_PERCENTAGE_STATE_TOPIC = "percentage_state_topic" @@ -110,18 +118,18 @@ MQTT_FAN_ATTRIBUTES_BLOCKED = frozenset( _LOGGER = logging.getLogger(__name__) -def valid_speed_range_configuration(config): +def valid_speed_range_configuration(config: ConfigType) -> ConfigType: """Validate that the fan speed_range configuration is valid, throws if it isn't.""" - if config.get(CONF_SPEED_RANGE_MIN) == 0: + if config[CONF_SPEED_RANGE_MIN] == 0: raise ValueError("speed_range_min must be > 0") - if config.get(CONF_SPEED_RANGE_MIN) >= config.get(CONF_SPEED_RANGE_MAX): + if config[CONF_SPEED_RANGE_MIN] >= config[CONF_SPEED_RANGE_MAX]: raise ValueError("speed_range_max must be > speed_range_min") return config -def valid_preset_mode_configuration(config): +def valid_preset_mode_configuration(config: ConfigType) -> ConfigType: """Validate that the preset mode reset payload is not one of the preset modes.""" - if config.get(CONF_PAYLOAD_RESET_PRESET_MODE) in config.get(CONF_PRESET_MODES_LIST): + if config[CONF_PAYLOAD_RESET_PRESET_MODE] in config[CONF_PRESET_MODES_LIST]: raise ValueError("preset_modes must not contain payload_reset_preset_mode") return config @@ -250,8 +258,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT fan.""" async_add_entities([MqttFan(hass, config, config_entry, discovery_data)]) @@ -263,32 +271,41 @@ class MqttFan(MqttEntity, FanEntity): _entity_id_format = fan.ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_FAN_ATTRIBUTES_BLOCKED - def __init__(self, hass, config, config_entry, discovery_data): + _command_templates: dict[str, Callable[[PublishPayloadType], PublishPayloadType]] + _value_templates: dict[str, Callable[[ReceivePayloadType], ReceivePayloadType]] + _feature_percentage: bool + _feature_preset_mode: bool + _topic: dict[str, Any] + _optimistic: bool + _optimistic_oscillation: bool + _optimistic_percentage: bool + _optimistic_preset_mode: bool + _payload: dict[str, Any] + _speed_range: tuple[int, int] + + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the MQTT fan.""" self._attr_percentage = None self._attr_preset_mode = None - self._topic = None - self._payload = None - self._value_templates = None - self._command_templates = None - self._optimistic = None - self._optimistic_oscillation = None - self._optimistic_percentage = None - self._optimistic_preset_mode = None - MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" self._speed_range = ( - config.get(CONF_SPEED_RANGE_MIN), - config.get(CONF_SPEED_RANGE_MAX), + config[CONF_SPEED_RANGE_MIN], + config[CONF_SPEED_RANGE_MAX], ) self._topic = { key: config.get(key) @@ -303,18 +320,6 @@ class MqttFan(MqttEntity, FanEntity): CONF_OSCILLATION_COMMAND_TOPIC, ) } - self._value_templates = { - CONF_STATE: config.get(CONF_STATE_VALUE_TEMPLATE), - ATTR_PERCENTAGE: config.get(CONF_PERCENTAGE_VALUE_TEMPLATE), - ATTR_PRESET_MODE: config.get(CONF_PRESET_MODE_VALUE_TEMPLATE), - ATTR_OSCILLATING: config.get(CONF_OSCILLATION_VALUE_TEMPLATE), - } - self._command_templates = { - CONF_STATE: config.get(CONF_COMMAND_TEMPLATE), - ATTR_PERCENTAGE: config.get(CONF_PERCENTAGE_COMMAND_TEMPLATE), - ATTR_PRESET_MODE: config.get(CONF_PRESET_MODE_COMMAND_TEMPLATE), - ATTR_OSCILLATING: config.get(CONF_OSCILLATION_COMMAND_TEMPLATE), - } self._payload = { "STATE_ON": config[CONF_PAYLOAD_ON], "STATE_OFF": config[CONF_PAYLOAD_OFF], @@ -359,24 +364,38 @@ class MqttFan(MqttEntity, FanEntity): if self._feature_preset_mode: self._attr_supported_features |= FanEntityFeature.PRESET_MODE - for key, tpl in self._command_templates.items(): + command_templates: dict[str, Template | None] = { + CONF_STATE: config.get(CONF_COMMAND_TEMPLATE), + ATTR_PERCENTAGE: config.get(CONF_PERCENTAGE_COMMAND_TEMPLATE), + ATTR_PRESET_MODE: config.get(CONF_PRESET_MODE_COMMAND_TEMPLATE), + ATTR_OSCILLATING: config.get(CONF_OSCILLATION_COMMAND_TEMPLATE), + } + self._command_templates = {} + for key, tpl in command_templates.items(): self._command_templates[key] = MqttCommandTemplate( tpl, entity=self ).async_render - for key, tpl in self._value_templates.items(): + self._value_templates = {} + value_templates: dict[str, Template | None] = { + CONF_STATE: config.get(CONF_STATE_VALUE_TEMPLATE), + ATTR_PERCENTAGE: config.get(CONF_PERCENTAGE_VALUE_TEMPLATE), + ATTR_PRESET_MODE: config.get(CONF_PRESET_MODE_VALUE_TEMPLATE), + ATTR_OSCILLATING: config.get(CONF_OSCILLATION_VALUE_TEMPLATE), + } + for key, tpl in value_templates.items(): self._value_templates[key] = MqttValueTemplate( tpl, entity=self, ).async_render_with_possible_json_value - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" - topics = {} + topics: dict[str, Any] = {} @callback @log_messages(self.hass, self.entity_id) - def state_received(msg): + def state_received(msg: ReceiveMessage) -> None: """Handle new received MQTT message.""" payload = self._value_templates[CONF_STATE](msg.payload) if not payload: @@ -400,7 +419,7 @@ class MqttFan(MqttEntity, FanEntity): @callback @log_messages(self.hass, self.entity_id) - def percentage_received(msg): + def percentage_received(msg: ReceiveMessage) -> None: """Handle new received MQTT message for the percentage.""" rendered_percentage_payload = self._value_templates[ATTR_PERCENTAGE]( msg.payload @@ -446,9 +465,9 @@ class MqttFan(MqttEntity, FanEntity): @callback @log_messages(self.hass, self.entity_id) - def preset_mode_received(msg): + def preset_mode_received(msg: ReceiveMessage) -> None: """Handle new received MQTT message for preset mode.""" - preset_mode = self._value_templates[ATTR_PRESET_MODE](msg.payload) + preset_mode = str(self._value_templates[ATTR_PRESET_MODE](msg.payload)) if preset_mode == self._payload["PRESET_MODE_RESET"]: self._attr_preset_mode = None self.async_write_ha_state() @@ -456,7 +475,7 @@ class MqttFan(MqttEntity, FanEntity): if not preset_mode: _LOGGER.debug("Ignoring empty preset_mode from '%s'", msg.topic) return - if preset_mode not in self.preset_modes: + if not self.preset_modes or preset_mode not in self.preset_modes: _LOGGER.warning( "'%s' received on topic %s. '%s' is not a valid preset mode", msg.payload, @@ -479,7 +498,7 @@ class MqttFan(MqttEntity, FanEntity): @callback @log_messages(self.hass, self.entity_id) - def oscillation_received(msg): + def oscillation_received(msg: ReceiveMessage) -> None: """Handle new received MQTT message for the oscillation.""" payload = self._value_templates[ATTR_OSCILLATING](msg.payload) if not payload: @@ -504,7 +523,7 @@ class MqttFan(MqttEntity, FanEntity): self.hass, self._sub_state, topics ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) From cd5901e0d03a80d1911bc5a7bca87736bef1a990 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 3 Nov 2022 14:49:12 +0100 Subject: [PATCH 0195/1033] Fix HomeKit thermostat to take priority over fans (#81473) --- homeassistant/components/homekit/type_thermostats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/homekit/type_thermostats.py b/homeassistant/components/homekit/type_thermostats.py index a924548816b..a8c7a53718a 100644 --- a/homeassistant/components/homekit/type_thermostats.py +++ b/homeassistant/components/homekit/type_thermostats.py @@ -306,7 +306,7 @@ class Thermostat(HomeAccessory): if attributes.get(ATTR_HVAC_ACTION) is not None: self.fan_chars.append(CHAR_CURRENT_FAN_STATE) serv_fan = self.add_preload_service(SERV_FANV2, self.fan_chars) - serv_thermostat.add_linked_service(serv_fan) + serv_fan.add_linked_service(serv_thermostat) self.char_active = serv_fan.configure_char( CHAR_ACTIVE, value=1, setter_callback=self._set_fan_active ) From 9c85d22bab333556efd4194fc74842b044412cfe Mon Sep 17 00:00:00 2001 From: mkmer Date: Thu, 3 Nov 2022 10:39:02 -0400 Subject: [PATCH 0196/1033] Bump AIOAladdinConnect to 0.1.47 (#81479) --- homeassistant/components/aladdin_connect/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/aladdin_connect/manifest.json b/homeassistant/components/aladdin_connect/manifest.json index 50ab6af6f85..6888eb4d8b2 100644 --- a/homeassistant/components/aladdin_connect/manifest.json +++ b/homeassistant/components/aladdin_connect/manifest.json @@ -2,7 +2,7 @@ "domain": "aladdin_connect", "name": "Aladdin Connect", "documentation": "https://www.home-assistant.io/integrations/aladdin_connect", - "requirements": ["AIOAladdinConnect==0.1.46"], + "requirements": ["AIOAladdinConnect==0.1.47"], "codeowners": ["@mkmer"], "iot_class": "cloud_polling", "loggers": ["aladdin_connect"], diff --git a/requirements_all.txt b/requirements_all.txt index 6bb67baf2ef..9a43c575b9a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -5,7 +5,7 @@ AEMET-OpenData==0.2.1 # homeassistant.components.aladdin_connect -AIOAladdinConnect==0.1.46 +AIOAladdinConnect==0.1.47 # homeassistant.components.adax Adax-local==0.1.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e4fc9f18e79..bf7be0f33a6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -7,7 +7,7 @@ AEMET-OpenData==0.2.1 # homeassistant.components.aladdin_connect -AIOAladdinConnect==0.1.46 +AIOAladdinConnect==0.1.47 # homeassistant.components.adax Adax-local==0.1.5 From 08936e40414843f8140f305d49d01885ec968f45 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 3 Nov 2022 20:35:05 +0100 Subject: [PATCH 0197/1033] Bump oralb-ble to 0.10.1 (#81491) fixes #81489 changelog: https://github.com/Bluetooth-Devices/oralb-ble/compare/v0.10.0...v0.10.1 --- homeassistant/components/oralb/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/oralb/manifest.json b/homeassistant/components/oralb/manifest.json index cad6167228c..520306aed03 100644 --- a/homeassistant/components/oralb/manifest.json +++ b/homeassistant/components/oralb/manifest.json @@ -8,7 +8,7 @@ "manufacturer_id": 220 } ], - "requirements": ["oralb-ble==0.10.0"], + "requirements": ["oralb-ble==0.10.1"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 9a43c575b9a..13087d43f40 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1241,7 +1241,7 @@ openwrt-luci-rpc==1.1.11 openwrt-ubus-rpc==0.0.2 # homeassistant.components.oralb -oralb-ble==0.10.0 +oralb-ble==0.10.1 # homeassistant.components.oru oru==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bf7be0f33a6..98ee447b345 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -886,7 +886,7 @@ open-meteo==0.2.1 openerz-api==0.1.0 # homeassistant.components.oralb -oralb-ble==0.10.0 +oralb-ble==0.10.1 # homeassistant.components.ovo_energy ovoenergy==1.2.0 From dd5baa6e488d8f104482fdd4f70bfe0fac13a4b2 Mon Sep 17 00:00:00 2001 From: Renat Sibgatulin Date: Thu, 3 Nov 2022 22:13:57 +0000 Subject: [PATCH 0198/1033] Add air-Q integration (air quality sensors) (#76999) * Added initial files for air-Q integration * Allow FIXME comments in pylint (temporary) Also reintroduce --ignore-missing-annotations=y * Set up air-q entry to connect to aioairq's API (initial attempt) Also add necessary constants * Implement a class for sensors and its update logic Very early stage, WIP * Zeroconf and authentication are working * Complete the bare-bone minimal working version Specifically, make AirQSensor update its values. * Handle invalid authentication gracefully * Handle ClientConnectionError gracefully * Add field hint for the login form The key in the schema, which defines the form in `ConfigFlow.async_show_form` is looked up in both `airq/strings/json` and `airq/translations/en.json`. I am still not 100% sure how this lookup is performed. WIP * Minor cleanups * Extend sensor list to all supported by SensorDeviceClass Also manage warming up sensors * aioairq is published to PyPI and mentioned in requirements * Reordered constants and list content alphabetically As required by style guides. Also turned SENSOR_TYPES to a list * Updated file docstrings for a dev unfamiliar w/homeassistant like myself * Adding a bit of logging for the integration setup process * Expose scan interval & smoothing flag Also streamline test_authentication in config_flow. * Fix a type annotation mistake * Use as many constants from homeassistant.const as possible My only concern is using CONST_IP_ADDRESS = "ip_address" for smth which stands for both IP address and mDNS... * Temporarily rollback ConfigFlow.async_step_configure and use defaults TODO: implement OptionFlowHandler instead * Define custom Coordinator, w subset of airq config The latter is then accessed from entity / sensor constructors to define correct DeviceInfo * Provide translations to de & fr + minor changes to en * Provide translations to ru + a minor en changes * Make translation a little more helpful and polite * Fix devicename and entry title * Remove stale commented out code * Test config_flow At this point two helper functions which interact with the external library are not tested * Clean up unrelated and meant as temporary changes * Clean up unnecessary comments meant for internal use * Move fetching config to a dedicated async coordinator method As opposed to it being a potentially poorly justified step in async_setup_entry * Remove zeroconf support since it is not yet ready * Remove translations other than en * Remove unnecessary comments, manifest.json entries, and constants * Improve exception handling - `_LOGGER` uses `debug` and not `error` levels. - Drop `ClientConnect` and catch `aiohttop.ClientConnectError` directly - Drop `Exception` as it is not expected from `aioairq` (remove the corresponding test too) * Drop strings for obsolete errors and steps Specifically, `unknown` error isn't caught any more. `configure` step has also been removed. * Refactor en.json to be consistent with strings.json * Move target_route from a coordinator argument to a constant At this point a user cannot configure the target_route route, thus it does not make sense to expose it half-heartedly in `AirQCoordinator.__init__`, since it cannot be accessed. * Fix an async call in `AirQCoordinator.async_setup_entry` * Refactor underlying aioairq API - Use `homeassistant.helpers.aiohttp.async_get_clientsession` and pass a single persistent session to `aioariq.AirQ.__init__` - `aioairq.AirQ.fetch_device_info` now returns a `DeviceInfo` object heavily inspired and almost compatible with `homeassistant.helpers.entity.DeviceInfo`. Make heavier use of this object and define a single `DeviceInfo` in the `AirQCoordinator` (instead of recreating the same object for each sensor of the device in `sensor.AirQSensor`) - Drop two helper functions in `config_flow.py` and operate on `aioariq.AirQ` methods directly * Fix the version of aioairq * Add 15 more sensors + icons * Remove cnt* & TypPS, change units of health & performance * Add 12 more sensors * Add a missing icon * Gracefully handle device not being available on setup If the device and the host are not on the same WiFi, ServerTimeoutError is raised, which is caught by ClientConnectionError. If the device is powered off, ClientConnectionError is expected. In both cases, ConfigEntryNotReady is raised, as prescribed by the docs. Newer version of aioairq times-out far quicker than the default 5 mins. * Rename two sensors * Validate provided IP address / mDNS aioairq now raises InvalidInput if provided IP / mDNS does not seem valid. Handle this exception correctly * Apply suggestions from code review Clean up the comments and rename the logger Co-authored-by: Erik Montnemery Co-authored-by: Artem Draft * Only fetch device info during the first refresh - Fetched info is stored in AirQCoordinator.device_info. - In `AirQSensor.native_value` only multiply by the factor if the sensor reading is not None - Fix the tests for ConfigFlow for aioairq==0.2.3. Specifically make the dummy data pass the new validation step upstream + add a test which fails it * Drop custom device classes for now * Apply suggestions from code review Co-authored-by: Artem Draft * Only fetch device info during ConfigFlow.async_step_user Store the result obtained by `airq.fetch_device_info` it in `config_entry.data`. Pass the entire config entry to `AirQCoordinator` and build the entire `homeassistant.helpers.entity.DeviceInfo` in the `AirQCoordinator.__init__`. This makes `AirQCoordinator._async_fetch_device_info` and overloaded `AirQCoordinator._async_config_entry_first_refresh` obsolete. Bump aioairq version. Turn update_interval from `AirQCoordinator.__init__` argument into a contestant. * Custom entity description exposing a hook to modify sensor value Use a `AirQEntityDescription` with a callable `value_fn` which allows to change the sensor value retrieved from the device. Note that the callable does not handle data retrieval itself (even from `coordinator.data`). Instead it is purely a hook to transform obtained value. * Avoid duplicated use of unique_id Device info is fetched during the `ConfigFlow.async_user_step`. `unique_id` is taken from the device info and is **not** stored in `config_entry.data`. Subsequently `config_entry.unique_id` is used instead. * Drop unnecessary try-except Co-authored-by: Artem Draft * Clarify the use of value_transform_fn * Refactor the use of lambdas in AirQEntityDescription Now it is the job of the callable under `value` to get the sensor reading from the coordinator's data. Factoring this functionality into a callback decouples the key of the description from the key of dict, returned by the API, so `AirQEntityDescription` no longer requires its key to be set to smth clearly internal (e.g. `nh3_MR100`). * Use a callback to update native_value Since all `native_value`s are updated synchronously, it can as well be done in a callback for better state consistency (right?) * Revert the description keys to match data keys Must match given the current way of identifying available sensors. On a broader scale, they must match to be able to relate the descriptions to sensors, unless a separate lookup table is maintained. * Reduce number of loops when adding sensors Filtering warming up sensors and non-sensor keys can be combined with adding entities. * Remove obsolete imports * Update integrations.json * Add integration_type Integration supports multiple devices => hub Co-authored-by: dl2080 Co-authored-by: Erik Montnemery Co-authored-by: Artem Draft Co-authored-by: Daniel Lehmann <43613560+dl2080@users.noreply.github.com> Co-authored-by: Martin Selbmann --- .coveragerc | 2 + CODEOWNERS | 2 + homeassistant/components/airq/__init__.py | 78 ++++ homeassistant/components/airq/config_flow.py | 84 ++++ homeassistant/components/airq/const.py | 9 + homeassistant/components/airq/manifest.json | 11 + homeassistant/components/airq/sensor.py | 361 ++++++++++++++++++ homeassistant/components/airq/strings.json | 22 ++ .../components/airq/translations/en.json | 23 ++ homeassistant/generated/config_flows.py | 1 + homeassistant/generated/integrations.json | 6 + requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/airq/__init__.py | 1 + tests/components/airq/test_config_flow.py | 93 +++++ 15 files changed, 699 insertions(+) create mode 100644 homeassistant/components/airq/__init__.py create mode 100644 homeassistant/components/airq/config_flow.py create mode 100644 homeassistant/components/airq/const.py create mode 100644 homeassistant/components/airq/manifest.json create mode 100644 homeassistant/components/airq/sensor.py create mode 100644 homeassistant/components/airq/strings.json create mode 100644 homeassistant/components/airq/translations/en.json create mode 100644 tests/components/airq/__init__.py create mode 100644 tests/components/airq/test_config_flow.py diff --git a/.coveragerc b/.coveragerc index 78168feced0..eb60f320a74 100644 --- a/.coveragerc +++ b/.coveragerc @@ -35,6 +35,8 @@ omit = homeassistant/components/agent_dvr/helpers.py homeassistant/components/airnow/__init__.py homeassistant/components/airnow/sensor.py + homeassistant/components/airq/__init__.py + homeassistant/components/airq/sensor.py homeassistant/components/airthings/__init__.py homeassistant/components/airthings/sensor.py homeassistant/components/airthings_ble/__init__.py diff --git a/CODEOWNERS b/CODEOWNERS index cb4e68f1535..cbff4badc9f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -45,6 +45,8 @@ build.json @home-assistant/supervisor /tests/components/airly/ @bieniu /homeassistant/components/airnow/ @asymworks /tests/components/airnow/ @asymworks +/homeassistant/components/airq/ @Sibgatulin @dl2080 +/tests/components/airq/ @Sibgatulin @dl2080 /homeassistant/components/airthings/ @danielhiversen /tests/components/airthings/ @danielhiversen /homeassistant/components/airthings_ble/ @vincegio diff --git a/homeassistant/components/airq/__init__.py b/homeassistant/components/airq/__init__.py new file mode 100644 index 00000000000..4bc64e1e825 --- /dev/null +++ b/homeassistant/components/airq/__init__.py @@ -0,0 +1,78 @@ +"""The air-Q integration.""" +from __future__ import annotations + +from datetime import timedelta +import logging + +from aioairq import AirQ + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + +from .const import DOMAIN, MANUFACTURER, TARGET_ROUTE, UPDATE_INTERVAL + +_LOGGER = logging.getLogger(__name__) + +PLATFORMS: list[Platform] = [Platform.SENSOR] + + +class AirQCoordinator(DataUpdateCoordinator): + """Coordinator is responsible for querying the device at a specified route.""" + + def __init__( + self, + hass: HomeAssistant, + entry: ConfigEntry, + ) -> None: + """Initialise a custom coordinator.""" + super().__init__( + hass, + _LOGGER, + name=DOMAIN, + update_interval=timedelta(seconds=UPDATE_INTERVAL), + ) + session = async_get_clientsession(hass) + self.airq = AirQ( + entry.data[CONF_IP_ADDRESS], entry.data[CONF_PASSWORD], session + ) + self.device_id = entry.unique_id + assert self.device_id is not None + self.device_info = DeviceInfo( + manufacturer=MANUFACTURER, + identifiers={(DOMAIN, self.device_id)}, + ) + self.device_info.update(entry.data["device_info"]) + + async def _async_update_data(self) -> dict: + """Fetch the data from the device.""" + data = await self.airq.get(TARGET_ROUTE) + return self.airq.drop_uncertainties_from_data(data) + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up air-Q from a config entry.""" + + coordinator = AirQCoordinator(hass, entry) + + # Query the device for the first time and initialise coordinator.data + await coordinator.async_config_entry_first_refresh() + + # Record the coordinator in a global store + hass.data.setdefault(DOMAIN, {}) + hass.data[DOMAIN][entry.entry_id] = coordinator + + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_ok diff --git a/homeassistant/components/airq/config_flow.py b/homeassistant/components/airq/config_flow.py new file mode 100644 index 00000000000..05af6825233 --- /dev/null +++ b/homeassistant/components/airq/config_flow.py @@ -0,0 +1,84 @@ +"""Config flow for air-Q integration.""" +from __future__ import annotations + +import logging +from typing import Any + +from aioairq import AirQ, InvalidAuth, InvalidInput +from aiohttp.client_exceptions import ClientConnectionError +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.aiohttp_client import async_get_clientsession + +from .const import DOMAIN + +_LOGGER = logging.getLogger(__name__) + +STEP_USER_DATA_SCHEMA = vol.Schema( + { + vol.Required(CONF_IP_ADDRESS): str, + vol.Required(CONF_PASSWORD): str, + } +) + + +class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for air-Q.""" + + VERSION = 1 + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the initial (authentication) configuration step.""" + if user_input is None: + return self.async_show_form( + step_id="user", data_schema=STEP_USER_DATA_SCHEMA + ) + + errors: dict[str, str] = {} + + session = async_get_clientsession(self.hass) + try: + airq = AirQ(user_input[CONF_IP_ADDRESS], user_input[CONF_PASSWORD], session) + except InvalidInput: + _LOGGER.debug( + "%s does not appear to be a valid IP address or mDNS name", + user_input[CONF_IP_ADDRESS], + ) + errors["base"] = "invalid_input" + else: + try: + await airq.validate() + except ClientConnectionError: + _LOGGER.debug( + "Failed to connect to device %s. Check the IP address / device ID " + "as well as whether the device is connected to power and the WiFi", + user_input[CONF_IP_ADDRESS], + ) + errors["base"] = "cannot_connect" + except InvalidAuth: + _LOGGER.debug( + "Incorrect password for device %s", user_input[CONF_IP_ADDRESS] + ) + errors["base"] = "invalid_auth" + else: + _LOGGER.debug( + "Successfully connected to %s", user_input[CONF_IP_ADDRESS] + ) + + device_info = await airq.fetch_device_info() + await self.async_set_unique_id(device_info.pop("id")) + self._abort_if_unique_id_configured() + + return self.async_create_entry( + title=device_info["name"], + data=user_input | {"device_info": device_info}, + ) + + return self.async_show_form( + step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors + ) diff --git a/homeassistant/components/airq/const.py b/homeassistant/components/airq/const.py new file mode 100644 index 00000000000..82719515cbf --- /dev/null +++ b/homeassistant/components/airq/const.py @@ -0,0 +1,9 @@ +"""Constants for the air-Q integration.""" +from typing import Final + +DOMAIN: Final = "airq" +MANUFACTURER: Final = "CorantGmbH" +TARGET_ROUTE: Final = "average" +CONCENTRATION_GRAMS_PER_CUBIC_METER: Final = "g/m³" +ACTIVITY_BECQUEREL_PER_CUBIC_METER: Final = "Bq/m³" +UPDATE_INTERVAL: float = 10.0 diff --git a/homeassistant/components/airq/manifest.json b/homeassistant/components/airq/manifest.json new file mode 100644 index 00000000000..932b404278d --- /dev/null +++ b/homeassistant/components/airq/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "airq", + "name": "air-Q", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/airq", + "requirements": ["aioairq==0.2.4"], + "codeowners": ["@Sibgatulin", "@dl2080"], + "iot_class": "local_polling", + "loggers": ["aioairq"], + "integration_type": "hub" +} diff --git a/homeassistant/components/airq/sensor.py b/homeassistant/components/airq/sensor.py new file mode 100644 index 00000000000..c524050ea66 --- /dev/null +++ b/homeassistant/components/airq/sensor.py @@ -0,0 +1,361 @@ +"""Definition of air-Q sensor platform.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +import logging +from typing import Literal + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER, + CONCENTRATION_PARTS_PER_BILLION, + CONCENTRATION_PARTS_PER_MILLION, + PERCENTAGE, + PRESSURE_HPA, + SOUND_PRESSURE_WEIGHTED_DBA, + TEMP_CELSIUS, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from . import AirQCoordinator +from .const import ( + ACTIVITY_BECQUEREL_PER_CUBIC_METER, + CONCENTRATION_GRAMS_PER_CUBIC_METER, + DOMAIN, +) + +_LOGGER = logging.getLogger(__name__) + + +@dataclass +class AirQEntityDescriptionMixin: + """Class for keys required by AirQ entity.""" + + value: Callable[[dict], float | int | None] + + +@dataclass +class AirQEntityDescription(SensorEntityDescription, AirQEntityDescriptionMixin): + """Describes AirQ sensor entity.""" + + +# Keys must match those in the data dictionary +SENSOR_TYPES: list[AirQEntityDescription] = [ + AirQEntityDescription( + key="nh3_MR100", + name="Ammonia", + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("nh3_MR100"), + ), + AirQEntityDescription( + key="cl2_M20", + name="Chlorine", + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("cl2_M20"), + ), + AirQEntityDescription( + key="co", + name="CO", + device_class=SensorDeviceClass.CO, + native_unit_of_measurement=CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("co"), + ), + AirQEntityDescription( + key="co2", + name="CO2", + device_class=SensorDeviceClass.CO2, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("co2"), + ), + AirQEntityDescription( + key="dewpt", + name="Dew point", + native_unit_of_measurement=TEMP_CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("dewpt"), + icon="mdi:water-thermometer", + ), + AirQEntityDescription( + key="ethanol", + name="Ethanol", + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("ethanol"), + ), + AirQEntityDescription( + key="ch2o_M10", + name="Formaldehyde", + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("ch2o_M10"), + ), + AirQEntityDescription( + key="h2s", + name="H2S", + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("h2s"), + ), + AirQEntityDescription( + key="health", + name="Health Index", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:heart-pulse", + value=lambda data: data.get("health", 0.0) / 10.0, + ), + AirQEntityDescription( + key="humidity", + name="Humidity", + device_class=SensorDeviceClass.HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("humidity"), + ), + AirQEntityDescription( + key="humidity_abs", + name="Absolute humidity", + native_unit_of_measurement=CONCENTRATION_GRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("humidity_abs"), + icon="mdi:water", + ), + AirQEntityDescription( + key="h2_M1000", + name="Hydrogen", + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("h2_M1000"), + ), + AirQEntityDescription( + key="ch4_MIPEX", + name="Methane", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("ch4_MIPEX"), + ), + AirQEntityDescription( + key="n2o", + name="N2O", + device_class=SensorDeviceClass.NITROUS_OXIDE, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("n2o"), + ), + AirQEntityDescription( + key="no_M250", + name="NO", + device_class=SensorDeviceClass.NITROGEN_MONOXIDE, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("no_M250"), + ), + AirQEntityDescription( + key="no2", + name="NO2", + device_class=SensorDeviceClass.NITROGEN_DIOXIDE, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("no2"), + ), + AirQEntityDescription( + key="o3", + name="Ozone", + device_class=SensorDeviceClass.OZONE, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("o3"), + ), + AirQEntityDescription( + key="oxygen", + name="Oxygen", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("oxygen"), + icon="mdi:leaf", + ), + AirQEntityDescription( + key="performance", + name="Performance Index", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:head-check", + value=lambda data: data.get("performance", 0.0) / 10.0, + ), + AirQEntityDescription( + key="pm1", + name="PM1", + device_class=SensorDeviceClass.PM1, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("pm1"), + icon="mdi:dots-hexagon", + ), + AirQEntityDescription( + key="pm2_5", + name="PM2.5", + device_class=SensorDeviceClass.PM25, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("pm2_5"), + icon="mdi:dots-hexagon", + ), + AirQEntityDescription( + key="pm10", + name="PM10", + device_class=SensorDeviceClass.PM10, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("pm10"), + icon="mdi:dots-hexagon", + ), + AirQEntityDescription( + key="pressure", + name="Pressure", + device_class=SensorDeviceClass.PRESSURE, + native_unit_of_measurement=PRESSURE_HPA, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("pressure"), + ), + AirQEntityDescription( + key="pressure_rel", + name="Relative pressure", + native_unit_of_measurement=PRESSURE_HPA, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("pressure_rel"), + icon="mdi:gauge", + ), + AirQEntityDescription( + key="c3h8_MIPEX", + name="Propane", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("c3h8_MIPEX"), + ), + AirQEntityDescription( + key="so2", + name="SO2", + device_class=SensorDeviceClass.SULPHUR_DIOXIDE, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("so2"), + ), + AirQEntityDescription( + key="sound", + name="Noise", + native_unit_of_measurement=SOUND_PRESSURE_WEIGHTED_DBA, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("sound"), + icon="mdi:ear-hearing", + ), + AirQEntityDescription( + key="sound_max", + name="Noise (Maximum)", + native_unit_of_measurement=SOUND_PRESSURE_WEIGHTED_DBA, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("sound_max"), + icon="mdi:ear-hearing", + ), + AirQEntityDescription( + key="radon", + name="Radon", + native_unit_of_measurement=ACTIVITY_BECQUEREL_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("radon"), + icon="mdi:radioactive", + ), + AirQEntityDescription( + key="temperature", + name="Temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=TEMP_CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("temperature"), + ), + AirQEntityDescription( + key="tvoc", + name="VOC", + device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("tvoc"), + ), + AirQEntityDescription( + key="tvoc_ionsc", + name="VOC (Industrial)", + device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION, + state_class=SensorStateClass.MEASUREMENT, + value=lambda data: data.get("tvoc_ionsc"), + ), +] + + +async def async_setup_entry( + hass: HomeAssistant, + config: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up sensor entities based on a config entry.""" + + coordinator = hass.data[DOMAIN][config.entry_id] + + entities: list[AirQSensor] = [] + + device_status: dict[str, str] | Literal["OK"] = coordinator.data["Status"] + + for description in SENSOR_TYPES: + if description.key not in coordinator.data: + if isinstance( + device_status, dict + ) and "sensor still in warm up phase" in device_status.get( + description.key, "OK" + ): + # warming up sensors do not contribute keys to coordinator.data + # but still must be added + _LOGGER.debug("Following sensor is warming up: %s", description.key) + else: + continue + entities.append(AirQSensor(coordinator, description)) + + async_add_entities(entities) + + +class AirQSensor(CoordinatorEntity, SensorEntity): + """Representation of a Sensor.""" + + _attr_has_entity_name = True + + def __init__( + self, + coordinator: AirQCoordinator, + description: AirQEntityDescription, + ) -> None: + """Initialize a single sensor.""" + super().__init__(coordinator) + self.entity_description: AirQEntityDescription = description + + self._attr_device_info = coordinator.device_info + self._attr_name = description.name + self._attr_unique_id = f"{coordinator.device_id}_{description.key}" + self._attr_native_value = description.value(coordinator.data) + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + self._attr_native_value = self.entity_description.value(self.coordinator.data) + self.async_write_ha_state() diff --git a/homeassistant/components/airq/strings.json b/homeassistant/components/airq/strings.json new file mode 100644 index 00000000000..3618d9d517e --- /dev/null +++ b/homeassistant/components/airq/strings.json @@ -0,0 +1,22 @@ +{ + "config": { + "step": { + "user": { + "title": "Identify the device", + "description": "Provide the IP address or mDNS of the device and its password", + "data": { + "ip_address": "[%key:common::config_flow::data::ip%]", + "password": "[%key:common::config_flow::data::password%]" + } + } + }, + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", + "invalid_input": "[%key:common::config_flow::error::invalid_host%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + } + } +} diff --git a/homeassistant/components/airq/translations/en.json b/homeassistant/components/airq/translations/en.json new file mode 100644 index 00000000000..81b8c1ff83e --- /dev/null +++ b/homeassistant/components/airq/translations/en.json @@ -0,0 +1,23 @@ +{ + "config": { + "step": { + "user": { + "title": "Identify the device", + "description": "Provide the IP address or mDNS of the device and its password", + "data": { + "ip_address": "Device IP or mDNS (e.g. '123ab_air-q.local')", + "password": "Password" + } + } + }, + "error": { + "cannot_connect": "Failed to connect, please check the IP or mDNS", + "invalid_auth": "Wrong password", + "invalid_input": "Invalid IP address or mDNS " + }, + "abort": { + "already_configured": "This device is already configured and available to Home Assistant" + } + } +} + diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 5214436be42..b0cd687b54e 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -15,6 +15,7 @@ FLOWS = { "agent_dvr", "airly", "airnow", + "airq", "airthings", "airthings_ble", "airtouch4", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 1d04bde132a..4d1b9da50af 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -89,6 +89,12 @@ "config_flow": true, "iot_class": "cloud_polling" }, + "airq": { + "name": "air-Q", + "integration_type": "hub", + "config_flow": true, + "iot_class": "local_polling" + }, "airthings": { "name": "Airthings", "integrations": { diff --git a/requirements_all.txt b/requirements_all.txt index 13087d43f40..225d0096684 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -112,6 +112,9 @@ aio_geojson_usgs_earthquakes==0.1 # homeassistant.components.gdacs aio_georss_gdacs==0.7 +# homeassistant.components.airq +aioairq==0.2.4 + # homeassistant.components.airzone aioairzone==0.4.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 98ee447b345..5a7f2351050 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -99,6 +99,9 @@ aio_geojson_usgs_earthquakes==0.1 # homeassistant.components.gdacs aio_georss_gdacs==0.7 +# homeassistant.components.airq +aioairq==0.2.4 + # homeassistant.components.airzone aioairzone==0.4.8 diff --git a/tests/components/airq/__init__.py b/tests/components/airq/__init__.py new file mode 100644 index 00000000000..612761c0653 --- /dev/null +++ b/tests/components/airq/__init__.py @@ -0,0 +1 @@ +"""Tests for the air-Q integration.""" diff --git a/tests/components/airq/test_config_flow.py b/tests/components/airq/test_config_flow.py new file mode 100644 index 00000000000..38fc15fdae3 --- /dev/null +++ b/tests/components/airq/test_config_flow.py @@ -0,0 +1,93 @@ +"""Test the air-Q config flow.""" +from unittest.mock import patch + +from aioairq.core import DeviceInfo, InvalidAuth, InvalidInput +from aiohttp.client_exceptions import ClientConnectionError + +from homeassistant import config_entries +from homeassistant.components.airq.const import DOMAIN +from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType + +TEST_USER_DATA = { + CONF_IP_ADDRESS: "192.168.0.0", + CONF_PASSWORD: "password", +} +TEST_DEVICE_INFO = DeviceInfo( + id="id", + name="name", + model="model", + sw_version="sw", + hw_version="hw", +) +TEST_DATA_OUT = TEST_USER_DATA | { + "device_info": {k: v for k, v in TEST_DEVICE_INFO.items() if k != "id"} +} + + +async def test_form(hass: HomeAssistant) -> None: + """Test we get the form.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert result["errors"] is None + + with patch("aioairq.AirQ.validate"), patch( + "aioairq.AirQ.fetch_device_info", return_value=TEST_DEVICE_INFO + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + TEST_USER_DATA, + ) + await hass.async_block_till_done() + + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == TEST_DEVICE_INFO["name"] + assert result2["data"] == TEST_DATA_OUT + + +async def test_form_invalid_auth(hass: HomeAssistant) -> None: + """Test we handle invalid auth.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch("aioairq.AirQ.validate", side_effect=InvalidAuth): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_USER_DATA | {CONF_PASSWORD: "wrong_password"} + ) + + assert result2["type"] == FlowResultType.FORM + assert result2["errors"] == {"base": "invalid_auth"} + + +async def test_form_cannot_connect(hass: HomeAssistant) -> None: + """Test we handle cannot connect error.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch("aioairq.AirQ.validate", side_effect=ClientConnectionError): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_USER_DATA + ) + + assert result2["type"] == FlowResultType.FORM + assert result2["errors"] == {"base": "cannot_connect"} + + +async def test_form_invalid_input(hass: HomeAssistant) -> None: + """Test we handle cannot connect error.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch("aioairq.AirQ.validate", side_effect=InvalidInput): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_USER_DATA | {CONF_IP_ADDRESS: "invalid_ip"} + ) + + assert result2["type"] == FlowResultType.FORM + assert result2["errors"] == {"base": "invalid_input"} From 71c18ec5271e6501667deb8d929df50954553dea Mon Sep 17 00:00:00 2001 From: krazos Date: Thu, 3 Nov 2022 18:23:42 -0400 Subject: [PATCH 0199/1033] Fix errant reference to "Solar.Forecast" in "Forecast.Solar" config options (#81252) Fix errant reference to Solar.Forecast Config options string referred to "Solar.Forecast". That reference has been corrected to "Forecast.Solar". --- homeassistant/components/forecast_solar/strings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/forecast_solar/strings.json b/homeassistant/components/forecast_solar/strings.json index b10e927eb8b..041935c59ef 100644 --- a/homeassistant/components/forecast_solar/strings.json +++ b/homeassistant/components/forecast_solar/strings.json @@ -17,7 +17,7 @@ "options": { "step": { "init": { - "description": "These values allow tweaking the Solar.Forecast result. Please refer to the documentation if a field is unclear.", + "description": "These values allow tweaking the Forecast.Solar result. Please refer to the documentation if a field is unclear.", "data": { "api_key": "Forecast.Solar API Key (optional)", "azimuth": "Azimuth (360 degrees, 0 = North, 90 = East, 180 = South, 270 = West)", From c7fc51cfa5f53a4d866aae195bface626fa9ebc8 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 4 Nov 2022 00:29:52 +0000 Subject: [PATCH 0200/1033] [ci skip] Translation update --- .../components/acmeda/translations/he.json | 2 +- .../components/airq/translations/en.json | 39 +++--- .../components/airq/translations/hr.json | 22 ++++ .../components/airq/translations/id.json | 22 ++++ .../airthings_ble/translations/he.json | 2 +- .../ambient_station/translations/hr.json | 11 ++ .../components/apple_tv/translations/he.json | 4 +- .../components/aranet/translations/he.json | 22 ++++ .../components/aranet/translations/hr.json | 22 ++++ .../components/aranet/translations/id.json | 1 + .../components/auth/translations/hr.json | 29 +++++ .../components/awair/translations/he.json | 2 +- .../bluemaestro/translations/he.json | 2 +- .../components/braviatv/translations/id.json | 4 +- .../components/bthome/translations/he.json | 2 +- .../devolo_home_network/translations/hr.json | 14 +++ .../components/dlna_dms/translations/he.json | 2 +- .../components/elkm1/translations/hr.json | 8 ++ .../components/escea/translations/he.json | 8 ++ .../components/esphome/translations/hr.json | 17 +++ .../fireservicerota/translations/he.json | 5 + .../fireservicerota/translations/hr.json | 12 ++ .../fjaraskupan/translations/he.json | 2 +- .../components/flux_led/translations/he.json | 2 +- .../forecast_solar/translations/en.json | 2 +- .../forecast_solar/translations/id.json | 2 +- .../components/fritzbox/translations/he.json | 2 +- .../fritzbox_callmonitor/translations/he.json | 2 +- .../components/generic/translations/he.json | 2 +- .../components/generic/translations/hr.json | 32 +++++ .../components/glances/translations/en.json | 13 +- .../google_travel_time/translations/he.json | 1 + .../google_travel_time/translations/hr.json | 7 ++ .../components/govee_ble/translations/he.json | 2 +- .../components/gree/translations/he.json | 2 +- .../components/hangouts/translations/hr.json | 12 ++ .../components/hassio/translations/he.json | 111 ++++++++++++++++++ .../components/hassio/translations/hr.json | 87 ++++++++++++++ .../components/hassio/translations/pl.json | 104 +++++++++++++++- .../hisense_aehw4a1/translations/he.json | 2 +- .../homeassistant/translations/he.json | 5 +- .../homematicip_cloud/translations/hr.json | 1 + .../huawei_lte/translations/hr.json | 17 +++ .../components/hue/translations/hr.json | 11 +- .../components/icloud/translations/he.json | 6 +- .../components/ifttt/translations/hr.json | 10 ++ .../components/inkbird/translations/he.json | 2 +- .../intellifire/translations/id.json | 2 +- .../components/isy994/translations/he.json | 2 +- .../components/izone/translations/he.json | 2 +- .../components/kegtron/translations/he.json | 2 +- .../components/kulersky/translations/he.json | 2 +- .../lacrosse_view/translations/he.json | 3 +- .../components/lametric/translations/he.json | 11 ++ .../components/lametric/translations/hr.json | 7 ++ .../lametric/translations/select.hr.json | 8 ++ .../landisgyr_heat_meter/translations/he.json | 23 ++++ .../components/led_ble/translations/he.json | 3 +- .../components/lifx/translations/he.json | 2 +- .../components/lifx/translations/hr.json | 13 ++ .../components/lookin/translations/he.json | 4 +- .../lutron_caseta/translations/he.json | 2 +- .../components/mailgun/translations/hr.json | 10 ++ .../components/mazda/translations/he.json | 2 +- .../meteoclimatic/translations/he.json | 2 +- .../components/moat/translations/he.json | 2 +- .../components/mqtt/translations/hr.json | 65 ++++++++++ .../components/mqtt/translations/id.json | 9 ++ .../components/mullvad/translations/he.json | 2 +- .../components/nest/translations/hr.json | 7 ++ .../nibe_heatpump/translations/id.json | 13 +- .../components/onvif/translations/he.json | 2 +- .../openexchangerates/translations/he.json | 22 ++++ .../components/openuv/translations/hr.json | 7 +- .../components/oralb/translations/he.json | 2 +- .../components/oralb/translations/hr.json | 22 ++++ .../components/oralb/translations/id.json | 22 ++++ .../components/overkiz/translations/hr.json | 7 ++ .../ovo_energy/translations/id.json | 1 + .../plugwise/translations/select.hr.json | 17 +++ .../plugwise/translations/select.id.json | 6 + .../components/ps4/translations/he.json | 2 +- .../pushbullet/translations/bg.json | 25 ++++ .../pushbullet/translations/et.json | 25 ++++ .../pushbullet/translations/he.json | 19 +++ .../pushbullet/translations/hr.json | 24 ++++ .../pushbullet/translations/id.json | 25 ++++ .../pushbullet/translations/no.json | 25 ++++ .../pushbullet/translations/pl.json | 25 ++++ .../pushbullet/translations/zh-Hant.json | 25 ++++ .../components/pushover/translations/he.json | 13 ++ .../components/pushover/translations/id.json | 4 + .../components/qingping/translations/he.json | 2 +- .../rainmachine/translations/id.json | 1 + .../components/scrape/translations/id.json | 6 + .../components/sensibo/translations/id.json | 4 +- .../sensibo/translations/sensor.hr.json | 9 ++ .../sensibo/translations/sensor.id.json | 5 + .../components/sensor/translations/id.json | 2 + .../components/sensorpro/translations/he.json | 2 +- .../sensorpush/translations/he.json | 2 +- .../simplisafe/translations/hr.json | 12 ++ .../components/skybell/translations/he.json | 3 +- .../components/smhi/translations/hr.json | 16 +++ .../components/snooz/translations/he.json | 2 +- .../components/snooz/translations/hr.json | 21 ++++ .../components/songpal/translations/he.json | 2 +- .../components/sonos/translations/he.json | 2 +- .../soundtouch/translations/id.json | 2 +- .../statistics/translations/he.json | 3 +- .../statistics/translations/id.json | 12 ++ .../steam_online/translations/bg.json | 1 + .../components/steamist/translations/he.json | 2 +- .../switcher_kis/translations/he.json | 2 +- .../components/tautulli/translations/hr.json | 11 ++ .../tellduslive/translations/hr.json | 14 +++ .../thermobeacon/translations/he.json | 2 +- .../components/thermopro/translations/he.json | 5 + .../components/tilt_ble/translations/he.json | 2 +- .../components/tplink/translations/he.json | 2 +- .../components/tradfri/translations/hr.json | 11 +- .../transmission/translations/id.json | 13 ++ .../components/twilio/translations/hr.json | 10 ++ .../components/unifi/translations/hr.json | 10 +- .../components/upcloud/translations/id.json | 2 +- .../components/upnp/translations/he.json | 2 +- .../components/upnp/translations/hr.json | 11 ++ .../utility_meter/translations/hr.json | 11 ++ .../utility_meter/translations/id.json | 2 +- .../volvooncall/translations/he.json | 2 + .../volvooncall/translations/id.json | 2 +- .../components/wemo/translations/he.json | 2 +- .../components/wiz/translations/he.json | 2 +- .../wolflink/translations/sensor.hr.json | 7 ++ .../xiaomi_ble/translations/he.json | 2 +- .../xiaomi_miio/translations/hr.json | 7 ++ .../xiaomi_miio/translations/id.json | 5 +- .../yalexs_ble/translations/he.json | 2 +- .../components/yeelight/translations/he.json | 2 +- .../components/zamg/translations/hr.json | 12 ++ .../components/zamg/translations/id.json | 26 ++++ .../components/zerproc/translations/he.json | 2 +- .../components/zha/translations/he.json | 3 + .../components/zha/translations/hr.json | 36 ++++++ .../components/zha/translations/id.json | 8 +- 145 files changed, 1443 insertions(+), 107 deletions(-) create mode 100644 homeassistant/components/airq/translations/hr.json create mode 100644 homeassistant/components/airq/translations/id.json create mode 100644 homeassistant/components/ambient_station/translations/hr.json create mode 100644 homeassistant/components/aranet/translations/he.json create mode 100644 homeassistant/components/aranet/translations/hr.json create mode 100644 homeassistant/components/auth/translations/hr.json create mode 100644 homeassistant/components/devolo_home_network/translations/hr.json create mode 100644 homeassistant/components/elkm1/translations/hr.json create mode 100644 homeassistant/components/escea/translations/he.json create mode 100644 homeassistant/components/esphome/translations/hr.json create mode 100644 homeassistant/components/fireservicerota/translations/hr.json create mode 100644 homeassistant/components/generic/translations/hr.json create mode 100644 homeassistant/components/google_travel_time/translations/hr.json create mode 100644 homeassistant/components/hangouts/translations/hr.json create mode 100644 homeassistant/components/hassio/translations/hr.json create mode 100644 homeassistant/components/huawei_lte/translations/hr.json create mode 100644 homeassistant/components/ifttt/translations/hr.json create mode 100644 homeassistant/components/lametric/translations/hr.json create mode 100644 homeassistant/components/lametric/translations/select.hr.json create mode 100644 homeassistant/components/landisgyr_heat_meter/translations/he.json create mode 100644 homeassistant/components/lifx/translations/hr.json create mode 100644 homeassistant/components/mailgun/translations/hr.json create mode 100644 homeassistant/components/openexchangerates/translations/he.json create mode 100644 homeassistant/components/oralb/translations/hr.json create mode 100644 homeassistant/components/oralb/translations/id.json create mode 100644 homeassistant/components/overkiz/translations/hr.json create mode 100644 homeassistant/components/plugwise/translations/select.hr.json create mode 100644 homeassistant/components/pushbullet/translations/bg.json create mode 100644 homeassistant/components/pushbullet/translations/et.json create mode 100644 homeassistant/components/pushbullet/translations/he.json create mode 100644 homeassistant/components/pushbullet/translations/hr.json create mode 100644 homeassistant/components/pushbullet/translations/id.json create mode 100644 homeassistant/components/pushbullet/translations/no.json create mode 100644 homeassistant/components/pushbullet/translations/pl.json create mode 100644 homeassistant/components/pushbullet/translations/zh-Hant.json create mode 100644 homeassistant/components/sensibo/translations/sensor.hr.json create mode 100644 homeassistant/components/simplisafe/translations/hr.json create mode 100644 homeassistant/components/smhi/translations/hr.json create mode 100644 homeassistant/components/snooz/translations/hr.json create mode 100644 homeassistant/components/statistics/translations/id.json create mode 100644 homeassistant/components/tautulli/translations/hr.json create mode 100644 homeassistant/components/tellduslive/translations/hr.json create mode 100644 homeassistant/components/twilio/translations/hr.json create mode 100644 homeassistant/components/utility_meter/translations/hr.json create mode 100644 homeassistant/components/wolflink/translations/sensor.hr.json create mode 100644 homeassistant/components/xiaomi_miio/translations/hr.json create mode 100644 homeassistant/components/zamg/translations/hr.json create mode 100644 homeassistant/components/zamg/translations/id.json create mode 100644 homeassistant/components/zha/translations/hr.json diff --git a/homeassistant/components/acmeda/translations/he.json b/homeassistant/components/acmeda/translations/he.json index 498f322a7b0..15b5629aa52 100644 --- a/homeassistant/components/acmeda/translations/he.json +++ b/homeassistant/components/acmeda/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "step": { "user": { diff --git a/homeassistant/components/airq/translations/en.json b/homeassistant/components/airq/translations/en.json index 81b8c1ff83e..c8ae857d10d 100644 --- a/homeassistant/components/airq/translations/en.json +++ b/homeassistant/components/airq/translations/en.json @@ -1,23 +1,22 @@ { - "config": { - "step": { - "user": { - "title": "Identify the device", - "description": "Provide the IP address or mDNS of the device and its password", - "data": { - "ip_address": "Device IP or mDNS (e.g. '123ab_air-q.local')", - "password": "Password" + "config": { + "abort": { + "already_configured": "Device is already configured" + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication", + "invalid_input": "Invalid hostname or IP address" + }, + "step": { + "user": { + "data": { + "ip_address": "IP Address", + "password": "Password" + }, + "description": "Provide the IP address or mDNS of the device and its password", + "title": "Identify the device" + } } - } - }, - "error": { - "cannot_connect": "Failed to connect, please check the IP or mDNS", - "invalid_auth": "Wrong password", - "invalid_input": "Invalid IP address or mDNS " - }, - "abort": { - "already_configured": "This device is already configured and available to Home Assistant" } - } -} - +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/hr.json b/homeassistant/components/airq/translations/hr.json new file mode 100644 index 00000000000..aedbaf7ed09 --- /dev/null +++ b/homeassistant/components/airq/translations/hr.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Ure\u0111aj je ve\u0107 konfiguriran" + }, + "error": { + "cannot_connect": "Povezivanje nije uspjelo", + "invalid_auth": "Neva\u017ee\u0107a provjera autenti\u010dnosti", + "invalid_input": "Neva\u017ee\u0107i naziv hosta ili IP adresa" + }, + "step": { + "user": { + "data": { + "ip_address": "IP adresa", + "password": "Lozinka" + }, + "description": "Navedite IP adresu ili mDNS ure\u0111aja i njegovu lozinku", + "title": "Identificirajte ure\u0111aj" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/id.json b/homeassistant/components/airq/translations/id.json new file mode 100644 index 00000000000..db210a13491 --- /dev/null +++ b/homeassistant/components/airq/translations/id.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_auth": "Autentikasi tidak valid", + "invalid_input": "Nama host atau alamat IP tidak valid" + }, + "step": { + "user": { + "data": { + "ip_address": "Alamat IP", + "password": "Kata Sandi" + }, + "description": "Berikan alamat IP atau mDNS perangkat dan kata sandinya", + "title": "Identifikasi perangkat" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airthings_ble/translations/he.json b/homeassistant/components/airthings_ble/translations/he.json index 3ba358c4465..467b7ec0499 100644 --- a/homeassistant/components/airthings_ble/translations/he.json +++ b/homeassistant/components/airthings_ble/translations/he.json @@ -4,7 +4,7 @@ "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "flow_title": "{name}", diff --git a/homeassistant/components/ambient_station/translations/hr.json b/homeassistant/components/ambient_station/translations/hr.json new file mode 100644 index 00000000000..b4c376c3855 --- /dev/null +++ b/homeassistant/components/ambient_station/translations/hr.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "api_key": "API klju\u010d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/he.json b/homeassistant/components/apple_tv/translations/he.json index 26ec85e8dc7..3af73091d3e 100644 --- a/homeassistant/components/apple_tv/translations/he.json +++ b/homeassistant/components/apple_tv/translations/he.json @@ -4,14 +4,14 @@ "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", "ipv6_not_supported": "IPv6 \u05d0\u05d9\u05e0\u05d5 \u05e0\u05ea\u05de\u05da.", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "error": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "flow_title": "{name} ({type})", diff --git a/homeassistant/components/aranet/translations/he.json b/homeassistant/components/aranet/translations/he.json new file mode 100644 index 00000000000..b4afd666d40 --- /dev/null +++ b/homeassistant/components/aranet/translations/he.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + }, + "error": { + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea {name}?" + }, + "user": { + "data": { + "address": "\u05d4\u05ea\u05e7\u05df" + }, + "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d4\u05ea\u05e7\u05df \u05dc\u05d4\u05d2\u05d3\u05e8\u05d4" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/hr.json b/homeassistant/components/aranet/translations/hr.json new file mode 100644 index 00000000000..ccdbb4f1906 --- /dev/null +++ b/homeassistant/components/aranet/translations/hr.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Ure\u0111aj je ve\u0107 konfiguriran" + }, + "error": { + "unknown": "Neo\u010dekivana gre\u0161ka" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "\u017delite li postaviti {name}?" + }, + "user": { + "data": { + "address": "Ure\u0111aj" + }, + "description": "Odaberite ure\u0111aj za postavljanje" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/id.json b/homeassistant/components/aranet/translations/id.json index 04e65296a12..c41c46f5138 100644 --- a/homeassistant/components/aranet/translations/id.json +++ b/homeassistant/components/aranet/translations/id.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Perangkat sudah dikonfigurasi", + "integrations_diabled": "Perangkat ini tidak memiliki integrasi yang diaktifkan. Aktifkan integrasi rumah cerdas menggunakan aplikasi dan coba lagi.", "no_devices_found": "Tidak ditemukan perangkat Aranet yang tidak dikonfigurasi.", "outdated_version": "Perangkat ini menggunakan firmware usang. Perbarui setidaknya ke firmware v1.2.0 dan coba lagi." }, diff --git a/homeassistant/components/auth/translations/hr.json b/homeassistant/components/auth/translations/hr.json new file mode 100644 index 00000000000..548b2bfdb4f --- /dev/null +++ b/homeassistant/components/auth/translations/hr.json @@ -0,0 +1,29 @@ +{ + "mfa_setup": { + "notify": { + "abort": { + "no_available_service": "Nema dostupnih usluga obavijesti." + }, + "error": { + "invalid_code": "Kod nije valjan, poku\u0161ajte ponovo." + }, + "step": { + "init": { + "description": "Odaberite jednu od usluga obavijesti:", + "title": "Postavite jednokratnu lozinku koju isporu\u010duje komponenta obavijesti" + }, + "setup": { + "title": "Provjera postavki" + } + } + }, + "totp": { + "step": { + "init": { + "title": "Postavite dvofaktorsku autentifikaciju pomo\u0107u TOTP-a" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/awair/translations/he.json b/homeassistant/components/awair/translations/he.json index 56e562de0c5..387e7fe9a62 100644 --- a/homeassistant/components/awair/translations/he.json +++ b/homeassistant/components/awair/translations/he.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" }, "error": { diff --git a/homeassistant/components/bluemaestro/translations/he.json b/homeassistant/components/bluemaestro/translations/he.json index de780eb221a..26219169d12 100644 --- a/homeassistant/components/bluemaestro/translations/he.json +++ b/homeassistant/components/bluemaestro/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/braviatv/translations/id.json b/homeassistant/components/braviatv/translations/id.json index 853dd7da29e..61b5b17ff1e 100644 --- a/homeassistant/components/braviatv/translations/id.json +++ b/homeassistant/components/braviatv/translations/id.json @@ -19,7 +19,7 @@ "pin": "Kode PIN", "use_psk": "Gunakan autentikasi PSK" }, - "description": "Masukkan kode PIN yang ditampilkan di TV Sony Bravia. \n\nJika kode PIN tidak ditampilkan, Anda harus membatalkan pendaftaran Home Assistant di TV, buka: Pengaturan -> Jaringan -> Pengaturan perangkat jarak jauh -> Batalkan pendaftaran perangkat jarak jauh.\n\nAnda bisa menggunakan PSK (Pre-Shared-Key) alih-alih menggunakan PIN. PSK merupakan kunci rahasia yang ditentukan pengguna untuk mengakses kontrol. Metode autentikasi ini disarankan karena lebih stabil. Untuk mengaktifkan PSK di TV Anda, buka Pengaturan -> Jaringan -> Penyiapan Jaringan Rumah -> Kontrol IP, lalu centang \u00abGunakan autentikasi PSK\u00bb dan masukkan PSK Anda, bukan PIN.", + "description": "Masukkan kode PIN yang ditampilkan di TV Sony Bravia.\n\nJika kode PIN tidak ditampilkan, Anda harus membatalkan pendaftaran Home Assistant di TV, buka: Pengaturan -> Jaringan -> Pengaturan perangkat jarak jauh -> Batalkan pendaftaran perangkat jarak jauh.\n\nAnda bisa menggunakan PSK (Pre-Shared-Key) alih-alih menggunakan PIN. PSK merupakan kunci rahasia yang ditentukan pengguna untuk mengakses kontrol. Metode autentikasi ini disarankan karena lebih stabil. Untuk mengaktifkan PSK di TV Anda, buka Pengaturan -> Jaringan -> Penyiapan Jaringan Rumah -> Kontrol IP, lalu centang \u00abGunakan autentikasi PSK\u00bb dan masukkan PSK Anda, bukan PIN.", "title": "Otorisasi TV Sony Bravia" }, "confirm": { @@ -30,7 +30,7 @@ "pin": "Kode PIN", "use_psk": "Gunakan autentikasi PSK" }, - "description": "Masukkan kode PIN yang ditampilkan di TV Sony Bravia. \n\nJika kode PIN tidak ditampilkan, Anda harus membatalkan pendaftaran Home Assistant di TV, buka: Pengaturan -> Jaringan -> Pengaturan perangkat jarak jauh -> Batalkan pendaftaran perangkat jarak jauh.\n\nAnda bisa menggunakan PSK (Pre-Shared-Key) alih-alih menggunakan PIN. PSK merupakan kunci rahasia yang ditentukan pengguna untuk mengakses kontrol. Metode autentikasi ini disarankan karena lebih stabil. Untuk mengaktifkan PSK di TV Anda, buka Pengaturan -> Jaringan -> Penyiapan Jaringan Rumah -> Kontrol IP, lalu centang \u00abGunakan autentikasi PSK\u00bb dan masukkan PSK Anda, bukan PIN." + "description": "Masukkan kode PIN yang ditampilkan di TV Sony Bravia.\n\nJika kode PIN tidak ditampilkan, Anda harus membatalkan pendaftaran Home Assistant di TV, buka: Pengaturan -> Jaringan -> Pengaturan perangkat jarak jauh -> Batalkan pendaftaran perangkat jarak jauh.\n\nAnda bisa menggunakan PSK (Pre-Shared-Key) alih-alih menggunakan PIN. PSK merupakan kunci rahasia yang ditentukan pengguna untuk mengakses kontrol. Metode autentikasi ini disarankan karena lebih stabil. Untuk mengaktifkan PSK di TV Anda, buka Pengaturan -> Jaringan -> Penyiapan Jaringan Rumah -> Kontrol IP, lalu centang \u00abGunakan autentikasi PSK\u00bb dan masukkan PSK Anda, bukan PIN." }, "user": { "data": { diff --git a/homeassistant/components/bthome/translations/he.json b/homeassistant/components/bthome/translations/he.json index b90a366130a..0df85dd1fe5 100644 --- a/homeassistant/components/bthome/translations/he.json +++ b/homeassistant/components/bthome/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" }, "flow_title": "{name}", diff --git a/homeassistant/components/devolo_home_network/translations/hr.json b/homeassistant/components/devolo_home_network/translations/hr.json new file mode 100644 index 00000000000..3e7836e5961 --- /dev/null +++ b/homeassistant/components/devolo_home_network/translations/hr.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "reauth_successful": "Ponovna provjera autenti\u010dnosti je uspje\u0161na" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Lozinka" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dlna_dms/translations/he.json b/homeassistant/components/dlna_dms/translations/he.json index cfe995b8921..41bb4ddbf8b 100644 --- a/homeassistant/components/dlna_dms/translations/he.json +++ b/homeassistant/components/dlna_dms/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/elkm1/translations/hr.json b/homeassistant/components/elkm1/translations/hr.json new file mode 100644 index 00000000000..06224788ca6 --- /dev/null +++ b/homeassistant/components/elkm1/translations/hr.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "invalid_auth": "Neva\u017ee\u0107a provjera autenti\u010dnosti", + "unknown": "Neo\u010dekivana gre\u0161ka" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/escea/translations/he.json b/homeassistant/components/escea/translations/he.json new file mode 100644 index 00000000000..032c9c9fa17 --- /dev/null +++ b/homeassistant/components/escea/translations/he.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/hr.json b/homeassistant/components/esphome/translations/hr.json new file mode 100644 index 00000000000..5984e832e28 --- /dev/null +++ b/homeassistant/components/esphome/translations/hr.json @@ -0,0 +1,17 @@ +{ + "config": { + "step": { + "authenticate": { + "data": { + "password": "Lozinka" + } + }, + "user": { + "data": { + "host": "Host", + "port": "Port" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fireservicerota/translations/he.json b/homeassistant/components/fireservicerota/translations/he.json index 61dee20d1ce..313b46455f2 100644 --- a/homeassistant/components/fireservicerota/translations/he.json +++ b/homeassistant/components/fireservicerota/translations/he.json @@ -16,6 +16,11 @@ "password": "\u05e1\u05d9\u05e1\u05de\u05d4" } }, + "reauth_confirm": { + "data": { + "password": "\u05e1\u05d9\u05e1\u05de\u05d4" + } + }, "user": { "data": { "password": "\u05e1\u05d9\u05e1\u05de\u05d4", diff --git a/homeassistant/components/fireservicerota/translations/hr.json b/homeassistant/components/fireservicerota/translations/hr.json new file mode 100644 index 00000000000..e1fd87e1f76 --- /dev/null +++ b/homeassistant/components/fireservicerota/translations/hr.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "data": { + "password": "Lozinka" + }, + "description": "Tokeni za provjeru autenti\u010dnosti postali su neva\u017ee\u0107i, prijavite se da ih ponovno izradite." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fjaraskupan/translations/he.json b/homeassistant/components/fjaraskupan/translations/he.json index 380dbc5d7fc..032c9c9fa17 100644 --- a/homeassistant/components/fjaraskupan/translations/he.json +++ b/homeassistant/components/fjaraskupan/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." } } diff --git a/homeassistant/components/flux_led/translations/he.json b/homeassistant/components/flux_led/translations/he.json index aa2d7877791..d8290fd672b 100644 --- a/homeassistant/components/flux_led/translations/he.json +++ b/homeassistant/components/flux_led/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" diff --git a/homeassistant/components/forecast_solar/translations/en.json b/homeassistant/components/forecast_solar/translations/en.json index 2aa5a37cd1c..56c210a7c19 100644 --- a/homeassistant/components/forecast_solar/translations/en.json +++ b/homeassistant/components/forecast_solar/translations/en.json @@ -25,7 +25,7 @@ "inverter_size": "Inverter size (Watt)", "modules power": "Total Watt peak power of your solar modules" }, - "description": "These values allow tweaking the Solar.Forecast result. Please refer to the documentation if a field is unclear." + "description": "These values allow tweaking the Forecast.Solar result. Please refer to the documentation if a field is unclear." } } } diff --git a/homeassistant/components/forecast_solar/translations/id.json b/homeassistant/components/forecast_solar/translations/id.json index 5bd1236d6a6..9c30668d1de 100644 --- a/homeassistant/components/forecast_solar/translations/id.json +++ b/homeassistant/components/forecast_solar/translations/id.json @@ -25,7 +25,7 @@ "inverter_size": "Ukuran inverter (Watt)", "modules power": "Total daya puncak modul surya Anda dalam Watt" }, - "description": "Nilai-nilai ini memungkinkan penyesuaian hasil Solar.Forecast. Rujuk ke dokumentasi jika bidang isian tidak jelas." + "description": "Nilai-nilai ini memungkinkan penyesuaian hasil Forecast.Solar. Rujuk ke dokumentasi jika bidang isian tidak jelas." } } } diff --git a/homeassistant/components/fritzbox/translations/he.json b/homeassistant/components/fritzbox/translations/he.json index ec9248b5ea6..c2c0d4be015 100644 --- a/homeassistant/components/fritzbox/translations/he.json +++ b/homeassistant/components/fritzbox/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" }, "error": { diff --git a/homeassistant/components/fritzbox_callmonitor/translations/he.json b/homeassistant/components/fritzbox_callmonitor/translations/he.json index 7951a71054c..fc8b733cb1a 100644 --- a/homeassistant/components/fritzbox_callmonitor/translations/he.json +++ b/homeassistant/components/fritzbox_callmonitor/translations/he.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "error": { "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" diff --git a/homeassistant/components/generic/translations/he.json b/homeassistant/components/generic/translations/he.json index d4e2e495855..6eaec7b7c9d 100644 --- a/homeassistant/components/generic/translations/he.json +++ b/homeassistant/components/generic/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." }, "error": { diff --git a/homeassistant/components/generic/translations/hr.json b/homeassistant/components/generic/translations/hr.json new file mode 100644 index 00000000000..2dd06c640c9 --- /dev/null +++ b/homeassistant/components/generic/translations/hr.json @@ -0,0 +1,32 @@ +{ + "config": { + "step": { + "user_confirm_still": { + "data": { + "confirmed_ok": "Ova slika izgleda dobro." + }, + "description": "![Pregled fotografije s kamere]( {preview_url} )", + "title": "Pregled" + } + } + }, + "options": { + "step": { + "confirm_still": { + "data": { + "confirmed_ok": "Ova slika izgleda dobro." + }, + "description": "![Pregled fotografije s kamere]({preview_url})", + "title": "Pregled" + }, + "init": { + "data": { + "password": "Lozinka", + "rtsp_transport": "RTSP transportni protokol", + "username": "Korisni\u010dko ime", + "verify_ssl": "Provjera SSL certifikata" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/glances/translations/en.json b/homeassistant/components/glances/translations/en.json index 726e4716224..87c53c3cf48 100644 --- a/homeassistant/components/glances/translations/en.json +++ b/homeassistant/components/glances/translations/en.json @@ -4,7 +4,8 @@ "already_configured": "Device is already configured" }, "error": { - "cannot_connect": "Failed to connect" + "cannot_connect": "Failed to connect", + "wrong_version": "Version not supported (2 or 3 only)" }, "step": { "user": { @@ -21,5 +22,15 @@ "title": "Setup Glances" } } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Update frequency" + }, + "description": "Configure options for Glances" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google_travel_time/translations/he.json b/homeassistant/components/google_travel_time/translations/he.json index b10c8890fd4..3f8d7bd1257 100644 --- a/homeassistant/components/google_travel_time/translations/he.json +++ b/homeassistant/components/google_travel_time/translations/he.json @@ -24,6 +24,7 @@ "data": { "language": "\u05e9\u05e4\u05d4", "time": "\u05d6\u05de\u05df", + "traffic_mode": "\u05de\u05e6\u05d1 \u05ea\u05e0\u05d5\u05e2\u05d4", "units": "\u05d9\u05d7\u05d9\u05d3\u05d5\u05ea" } } diff --git a/homeassistant/components/google_travel_time/translations/hr.json b/homeassistant/components/google_travel_time/translations/hr.json new file mode 100644 index 00000000000..6e71d340dc1 --- /dev/null +++ b/homeassistant/components/google_travel_time/translations/hr.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "invalid_auth": "Neva\u017ee\u0107a provjera autenti\u010dnosti" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/govee_ble/translations/he.json b/homeassistant/components/govee_ble/translations/he.json index de780eb221a..26219169d12 100644 --- a/homeassistant/components/govee_ble/translations/he.json +++ b/homeassistant/components/govee_ble/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/gree/translations/he.json b/homeassistant/components/gree/translations/he.json index d3d68dccc93..4eafc6dc29b 100644 --- a/homeassistant/components/gree/translations/he.json +++ b/homeassistant/components/gree/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." }, "step": { diff --git a/homeassistant/components/hangouts/translations/hr.json b/homeassistant/components/hangouts/translations/hr.json new file mode 100644 index 00000000000..59b98ae9ae9 --- /dev/null +++ b/homeassistant/components/hangouts/translations/hr.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "email": "Email", + "password": "Lozinka" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hassio/translations/he.json b/homeassistant/components/hassio/translations/he.json index 8926338221a..13c9cfd949c 100644 --- a/homeassistant/components/hassio/translations/he.json +++ b/homeassistant/components/hassio/translations/he.json @@ -1,6 +1,117 @@ { + "issues": { + "unhealthy": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05db\u05e8\u05d2\u05e2 \u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0\u05d4 \u05d1\u05d2\u05dc\u05dc {reason}. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0\u05d4 - {reason}" + }, + "unhealthy_docker": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05ea\u05e7\u05d9\u05e0\u05d4 \u05db\u05e2\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05ea\u05e6\u05d5\u05e8\u05ea Docker \u05e0\u05e7\u05d1\u05e2\u05d4 \u05d1\u05d0\u05d5\u05e4\u05df \u05e9\u05d2\u05d5\u05d9. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0\u05d4 - Docker \u05de\u05d5\u05d2\u05d3\u05e8 \u05d1\u05d0\u05d5\u05e4\u05df \u05e9\u05d2\u05d5\u05d9" + }, + "unhealthy_privileged": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05d1\u05e8\u05d9\u05d0\u05d4 \u05db\u05e2\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05d0\u05d9\u05df \u05dc\u05d4 \u05d2\u05d9\u05e9\u05d4 \u05de\u05d5\u05e8\u05e9\u05d9\u05ea \u05dc\u05d6\u05de\u05df \u05d4\u05e8\u05d9\u05e6\u05d4 \u05e9\u05dc docker. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0\u05d4 - \u05dc\u05d0 \u05de\u05d9\u05d5\u05d7\u05e1" + }, + "unhealthy_setup": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05d1\u05e8\u05d9\u05d0\u05d4 \u05db\u05e2\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05d4\u05d4\u05ea\u05e7\u05e0\u05d4 \u05dc\u05d0 \u05d4\u05d5\u05e9\u05dc\u05de\u05d4. \u05d9\u05e9\u05e0\u05df \u05de\u05e1\u05e4\u05e8 \u05e1\u05d9\u05d1\u05d5\u05ea \u05dc\u05db\u05da \u05e9\u05d6\u05d4 \u05d9\u05db\u05d5\u05dc \u05dc\u05d4\u05ea\u05e8\u05d7\u05e9, \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05dc\u05de\u05d5\u05d3 \u05e2\u05d5\u05d3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0\u05d4 - \u05d4\u05d4\u05ea\u05e7\u05e0\u05d4 \u05e0\u05db\u05e9\u05dc\u05d4" + }, + "unhealthy_supervisor": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05d1\u05e8\u05d9\u05d0\u05d4 \u05db\u05e2\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05e2\u05d3\u05db\u05df \u05d0\u05ea \u05d4\u05de\u05e4\u05e7\u05d7 \u05dc\u05d2\u05d9\u05e8\u05e1\u05d4 \u05d4\u05e2\u05d3\u05db\u05e0\u05d9\u05ea \u05d1\u05d9\u05d5\u05ea\u05e8 \u05e0\u05db\u05e9\u05dc. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0\u05d4 - \u05e2\u05d3\u05db\u05d5\u05df \u05d4\u05de\u05e4\u05e7\u05d7 \u05e0\u05db\u05e9\u05dc" + }, + "unhealthy_untrusted": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05d1\u05e8\u05d9\u05d0\u05d4 \u05db\u05e2\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05d4\u05d9\u05d0 \u05d6\u05d9\u05d4\u05ea\u05d4 \u05e7\u05d5\u05d3 \u05d0\u05d5 \u05ea\u05de\u05d5\u05e0\u05d5\u05ea \u05dc\u05d0 \u05de\u05d4\u05d9\u05de\u05e0\u05d9\u05dd \u05d1\u05e9\u05d9\u05de\u05d5\u05e9. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0\u05d4 - \u05e7\u05d5\u05d3 \u05dc\u05d0 \u05de\u05d4\u05d9\u05de\u05df" + }, + "unsupported": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05e2\u05e7\u05d1 {reason}. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05dc\u05de\u05d5\u05d3 \u05e2\u05d5\u05d3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - {reason}" + }, + "unsupported_apparmor": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9-AppArmor \u05e4\u05d5\u05e2\u05dc \u05d1\u05d0\u05d5\u05e4\u05df \u05e9\u05d2\u05d5\u05d9 \u05d5\u05d4\u05e8\u05d7\u05d1\u05d5\u05ea \u05e4\u05d5\u05e2\u05dc\u05d5\u05ea \u05d1\u05e6\u05d5\u05e8\u05d4 \u05dc\u05d0 \u05de\u05d5\u05d2\u05e0\u05ea \u05d5\u05dc\u05d0 \u05de\u05d0\u05d5\u05d1\u05d8\u05d7\u05ea. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d1\u05e2\u05d9\u05d5\u05ea AppArmor" + }, + "unsupported_cgroup_version": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05d4\u05d2\u05d9\u05e8\u05e1\u05d4 \u05d4\u05dc\u05d0 \u05e0\u05db\u05d5\u05e0\u05d4 \u05e9\u05dc Docker CGroup \u05e0\u05de\u05e6\u05d0\u05ea \u05d1\u05e9\u05d9\u05de\u05d5\u05e9. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05dc\u05de\u05d5\u05d3 \u05d0\u05d5\u05d3\u05d5\u05ea \u05d4\u05d2\u05d9\u05e8\u05e1\u05d4 \u05d4\u05e0\u05db\u05d5\u05e0\u05d4 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d2\u05e8\u05e1\u05ea CGroup" + }, + "unsupported_connectivity_check": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9-Home Assistant \u05d0\u05d9\u05e0\u05d5 \u05d9\u05db\u05d5\u05dc \u05dc\u05e7\u05d1\u05d5\u05e2 \u05de\u05ea\u05d9 \u05d7\u05d9\u05d1\u05d5\u05e8 \u05dc\u05d0\u05d9\u05e0\u05d8\u05e8\u05e0\u05d8 \u05d6\u05de\u05d9\u05df. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d1\u05d3\u05d9\u05e7\u05ea \u05e7\u05d9\u05e9\u05d5\u05e8\u05d9\u05d5\u05ea \u05de\u05d5\u05e9\u05d1\u05ea\u05ea" + }, + "unsupported_content_trust": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9-Home Assistant \u05d0\u05d9\u05e0\u05d5 \u05d9\u05db\u05d5\u05dc \u05dc\u05d0\u05de\u05ea \u05e9\u05d4\u05ea\u05d5\u05db\u05df \u05d4\u05de\u05d5\u05e4\u05e2\u05dc \u05d4\u05d5\u05d0 \u05de\u05d4\u05d9\u05de\u05df \u05d5\u05d0\u05d9\u05e0\u05d5 \u05e9\u05d5\u05e0\u05d4 \u05e2\u05dc \u05d9\u05d3\u05d9 \u05ea\u05d5\u05e7\u05e4\u05d9\u05dd. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d1\u05d3\u05d9\u05e7\u05ea \u05d0\u05de\u05d5\u05df \u05ea\u05d5\u05db\u05df \u05de\u05d5\u05e9\u05d1\u05ea" + }, + "unsupported_dbus": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9-D-Bus \u05e4\u05d5\u05e2\u05dc \u05d1\u05d0\u05d5\u05e4\u05df \u05e9\u05d2\u05d5\u05d9. \u05d3\u05d1\u05e8\u05d9\u05dd \u05e8\u05d1\u05d9\u05dd \u05e0\u05db\u05e9\u05dc\u05d9\u05dd \u05dc\u05dc\u05d0 \u05d6\u05d4 \u05db\u05d2\u05d5\u05df \u05d4\u05de\u05e4\u05e7\u05d7 \u05d0\u05d9\u05e0\u05d5 \u05d9\u05db\u05d5\u05dc \u05dc\u05ea\u05e7\u05e9\u05e8 \u05e2\u05dd \u05d4\u05de\u05d0\u05e8\u05d7. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d1\u05e2\u05d9\u05d5\u05ea D-Bus" + }, + "unsupported_dns_server": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05e9\u05e8\u05ea \u05d4-DNS \u05e9\u05e1\u05d5\u05e4\u05e7 \u05d0\u05d9\u05e0\u05d5 \u05e4\u05d5\u05e2\u05dc \u05db\u05e8\u05d0\u05d5\u05d9 \u05d5\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05d4-DNS \u05d4\u05d7\u05d5\u05d6\u05e8\u05ea \u05d4\u05e4\u05db\u05d4 \u05dc\u05dc\u05d0 \u05d6\u05de\u05d9\u05e0\u05d4. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d1\u05e2\u05d9\u05d5\u05ea \u05d1\u05e9\u05e8\u05ea DNS" + }, + "unsupported_docker_configuration": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05d4-Docker daemon \u05e4\u05d5\u05e2\u05dc \u05d1\u05d0\u05d5\u05e4\u05df \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - Docker \u05de\u05d5\u05d2\u05d3\u05e8 \u05d1\u05d0\u05d5\u05e4\u05df \u05e9\u05d2\u05d5\u05d9" + }, + "unsupported_docker_version": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05d4\u05d2\u05d9\u05e8\u05e1\u05d4 \u05d4\u05dc\u05d0 \u05e0\u05db\u05d5\u05e0\u05d4 \u05e9\u05dc Docker \u05e0\u05de\u05e6\u05d0\u05ea \u05d1\u05e9\u05d9\u05de\u05d5\u05e9. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05dc\u05de\u05d5\u05d3 \u05de\u05d4\u05d9 \u05d4\u05d2\u05d9\u05e8\u05e1\u05d4 \u05d4\u05e0\u05db\u05d5\u05e0\u05d4 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d2\u05e8\u05e1\u05ea Docker" + }, + "unsupported_job_conditions": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05ea\u05e0\u05d0\u05d9 \u05e2\u05d1\u05d5\u05d3\u05d4 \u05d0\u05d7\u05d3 \u05d0\u05d5 \u05d9\u05d5\u05ea\u05e8 \u05d4\u05d5\u05e9\u05d1\u05ea\u05d5 \u05d0\u05e9\u05e8 \u05de\u05d2\u05e0\u05d9\u05dd \u05de\u05e4\u05e0\u05d9 \u05db\u05e9\u05dc\u05d9\u05dd \u05d5\u05e9\u05d1\u05e8\u05d9\u05dd \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d9\u05dd. \u05d9\u05e9 \u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d4\u05d4\u05d2\u05e0\u05d5\u05ea \u05de\u05d5\u05e9\u05d1\u05ea\u05d5\u05ea" + }, + "unsupported_lxc": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05d4\u05d9\u05d0 \u05de\u05d5\u05e4\u05e2\u05dc\u05ea \u05d1\u05de\u05d7\u05e9\u05d1 \u05d5\u05d9\u05e8\u05d8\u05d5\u05d0\u05dc\u05d9 LXC. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d6\u05d5\u05d4\u05ea\u05d4 LXC" + }, + "unsupported_network_manager": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05de\u05e0\u05d4\u05dc \u05d4\u05e8\u05e9\u05ea \u05d7\u05e1\u05e8, \u05dc\u05d0 \u05e4\u05e2\u05d9\u05dc \u05d0\u05d5 \u05e9\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05e0\u05e7\u05d1\u05e2\u05d4 \u05d1\u05d0\u05d5\u05e4\u05df \u05e9\u05d2\u05d5\u05d9. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d1\u05e2\u05d9\u05d5\u05ea \u05d1\u05de\u05e0\u05d4\u05dc \u05d4\u05e8\u05e9\u05ea" + }, + "unsupported_os": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05de\u05e2\u05e8\u05db\u05ea \u05d4\u05d4\u05e4\u05e2\u05dc\u05d4 \u05e9\u05d1\u05e9\u05d9\u05de\u05d5\u05e9 \u05d0\u05d9\u05e0\u05d4 \u05e0\u05d1\u05d3\u05e7\u05ea \u05d0\u05d5 \u05de\u05ea\u05d5\u05d7\u05d6\u05e7\u05ea \u05dc\u05e9\u05d9\u05de\u05d5\u05e9 \u05e2\u05dd \u05d4\u05de\u05e4\u05e7\u05d7. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05dc\u05d1\u05d3\u05d5\u05e7 \u05d0\u05dc\u05d5 \u05de\u05e2\u05e8\u05db\u05d5\u05ea \u05d4\u05e4\u05e2\u05dc\u05d4 \u05e0\u05ea\u05de\u05db\u05d5\u05ea \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05de\u05e2\u05e8\u05db\u05ea \u05d4\u05e4\u05e2\u05dc\u05d4" + }, + "unsupported_os_agent": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9-OS-Agent \u05d7\u05e1\u05e8, \u05dc\u05d0 \u05e4\u05e2\u05d9\u05dc \u05d0\u05d5 \u05de\u05d5\u05d2\u05d3\u05e8 \u05d1\u05d0\u05d5\u05e4\u05df \u05e9\u05d2\u05d5\u05d9. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d1\u05e2\u05d9\u05d5\u05ea OS-Agent" + }, + "unsupported_restart_policy": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05dc Docker \u05e7\u05d5\u05e0\u05d8\u05d9\u05d9\u05e0\u05e8 \u05d9\u05e9 \u05e2\u05e8\u05db\u05ea \u05de\u05d3\u05d9\u05e0\u05d9\u05d5\u05ea \u05d4\u05e4\u05e2\u05dc\u05d4 \u05de\u05d7\u05d3\u05e9 \u05e9\u05e2\u05dc\u05d5\u05dc\u05d4 \u05dc\u05d2\u05e8\u05d5\u05dd \u05dc\u05d1\u05e2\u05d9\u05d5\u05ea \u05d1\u05e2\u05ea \u05d4\u05d0\u05ea\u05d7\u05d5\u05dc. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05de\u05d3\u05d9\u05e0\u05d9\u05d5\u05ea \u05d4\u05e4\u05e2\u05dc\u05d4 \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e7\u05d5\u05e0\u05d8\u05d9\u05d9\u05e0\u05e8" + }, + "unsupported_software": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05d6\u05d5\u05d4\u05ea\u05d4 \u05ea\u05d5\u05db\u05e0\u05d4 \u05e0\u05d5\u05e1\u05e4\u05ea \u05de\u05d7\u05d5\u05e5 \u05dc\u05de\u05e2\u05e8\u05db\u05ea \u05d4\u05d0\u05e7\u05d5\u05dc\u05d5\u05d2\u05d9\u05ea \u05e9\u05dc Home Assistant. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05dc\u05de\u05d5\u05d3 \u05e2\u05d5\u05d3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05ea\u05d5\u05db\u05e0\u05d4 \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea" + }, + "unsupported_source_mods": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05e7\u05d5\u05d3 \u05d4\u05de\u05e7\u05d5\u05e8 \u05e9\u05dc \u05d4\u05de\u05e4\u05e7\u05d7 \u05d4\u05e9\u05ea\u05e0\u05d4. \u05d9\u05e9 \u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05de\u05e7\u05d5\u05e8 \u05d4\u05de\u05e4\u05e7\u05d7" + }, + "unsupported_supervisor_version": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9\u05d2\u05d9\u05e8\u05e1\u05d4 \u05dc\u05d0 \u05de\u05e2\u05d5\u05d3\u05db\u05e0\u05ea \u05e9\u05dc \u05de\u05e4\u05e7\u05d7 \u05e0\u05de\u05e6\u05d0\u05ea \u05d1\u05e9\u05d9\u05de\u05d5\u05e9 \u05d5\u05e2\u05d3\u05db\u05d5\u05df \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9 \u05d4\u05d5\u05e9\u05d1\u05ea. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d2\u05e8\u05e1\u05ea \u05de\u05e4\u05e7\u05d7" + }, + "unsupported_systemd": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9-Systemd \u05d7\u05e1\u05e8, \u05dc\u05d0 \u05e4\u05e2\u05d9\u05dc \u05d0\u05d5 \u05e9\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05e0\u05e7\u05d1\u05e2\u05d4 \u05d1\u05d0\u05d5\u05e4\u05df \u05e9\u05d2\u05d5\u05d9. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d1\u05e2\u05d9\u05d5\u05ea \u05d1\u05de\u05e2\u05e8\u05db\u05ea" + }, + "unsupported_systemd_journal": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9-Systemd Journal \u05d5/\u05d0\u05d5 \u05e9\u05d9\u05e8\u05d5\u05ea \u05d4\u05e9\u05e2\u05e8 \u05d7\u05e1\u05e8\u05d9\u05dd, \u05d0\u05d9\u05e0\u05dd \u05e4\u05e2\u05d9\u05dc\u05d9\u05dd \u05d0\u05d5 \u05de\u05d5\u05d2\u05d3\u05e8\u05d9\u05dd \u05d1\u05e6\u05d5\u05e8\u05d4 \u05e9\u05d2\u05d5\u05d9\u05d4. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05dc\u05de\u05d5\u05d3 \u05e2\u05d5\u05d3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d1\u05e2\u05d9\u05d5\u05ea \u05e9\u05dc Systemd Journal" + }, + "unsupported_systemd_resolved": { + "description": "\u05d4\u05de\u05e2\u05e8\u05db\u05ea \u05d0\u05d9\u05e0\u05d4 \u05e0\u05ea\u05de\u05db\u05ea \u05de\u05db\u05d9\u05d5\u05d5\u05df \u05e9-Systemd Resolved \u05d7\u05e1\u05e8, \u05dc\u05d0 \u05e4\u05e2\u05d9\u05dc \u05d0\u05d5 \u05e9\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05e0\u05e7\u05d1\u05e2\u05d4 \u05d1\u05d0\u05d5\u05e4\u05df \u05e9\u05d2\u05d5\u05d9. \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e7\u05d9\u05e9\u05d5\u05e8 \u05db\u05d3\u05d9 \u05dc\u05e7\u05d1\u05dc \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05ea\u05e7\u05df \u05d6\u05d0\u05ea.", + "title": "\u05de\u05e2\u05e8\u05db\u05ea \u05dc\u05d0 \u05e0\u05ea\u05de\u05db\u05ea - \u05d1\u05e2\u05d9\u05d5\u05ea \u05e9\u05e0\u05e4\u05ea\u05e8\u05d5 \u05e2\u05dc \u05d9\u05d3\u05d9 \u05d4\u05de\u05e2\u05e8\u05db\u05ea" + } + }, "system_health": { "info": { + "agent_version": "\u05d2\u05e8\u05e1\u05ea \u05d4\u05e1\u05d5\u05db\u05df", "board": "\u05dc\u05d5\u05d7", "disk_total": "\u05e1\u05d4\"\u05db \u05d3\u05d9\u05e1\u05e7", "disk_used": "\u05d3\u05d9\u05e1\u05e7 \u05d1\u05e9\u05d9\u05de\u05d5\u05e9", diff --git a/homeassistant/components/hassio/translations/hr.json b/homeassistant/components/hassio/translations/hr.json new file mode 100644 index 00000000000..c028223588b --- /dev/null +++ b/homeassistant/components/hassio/translations/hr.json @@ -0,0 +1,87 @@ +{ + "issues": { + "unhealthy": { + "description": "Sustav je trenutno nezdrav zbog {reason}. Sustav nije podr\u017ean zbog {reason}. Koristite vezu da saznate vi\u0161e i kako to popraviti.", + "title": "Nezdrav sustav - {reason}" + }, + "unhealthy_docker": { + "title": "Nezdrav sustav - Docker je pogre\u0161no konfiguriran" + }, + "unhealthy_privileged": { + "title": "Nezdrav sustav - Nije privilegiran" + }, + "unhealthy_setup": { + "title": "Neispravan sustav - Postavljanje nije uspjelo" + }, + "unhealthy_supervisor": { + "title": "Nezdravi sustav - A\u017euriranje Supervisora nije uspjelo" + }, + "unhealthy_untrusted": { + "title": "Nezdravi sustav - Nepouzdan kod" + }, + "unsupported": { + "description": "Sustav nije podr\u017ean zbog {reason}. Koristite vezu da saznate vi\u0161e i kako to popraviti.", + "title": "Nepodr\u017eani sustav - {reason}" + }, + "unsupported_apparmor": { + "title": "Nepodr\u017eani sustav - Problemi s AppArmorom" + }, + "unsupported_cgroup_version": { + "title": "Nepodr\u017eani sustav - verzija CGroup" + }, + "unsupported_connectivity_check": { + "title": "Nepodr\u017eani sustav \u2013 Provjera povezivosti je onemogu\u0107ena" + }, + "unsupported_content_trust": { + "title": "Nepodr\u017eani sustav - Provjera pouzdanosti sadr\u017eaja onemogu\u0107ena" + }, + "unsupported_dbus": { + "title": "Nepodr\u017eani sustav - D-Bus problemi" + }, + "unsupported_dns_server": { + "title": "Nepodr\u017eani sustav - Problemi s DNS poslu\u017eiteljem" + }, + "unsupported_docker_configuration": { + "title": "Nepodr\u017eani sustav - Docker je pogre\u0161no konfiguriran" + }, + "unsupported_docker_version": { + "title": "Nepodr\u017eani sustav - Docker verzija" + }, + "unsupported_job_conditions": { + "title": "Nepodr\u017eani sustav - Za\u0161tite onemogu\u0107ene" + }, + "unsupported_lxc": { + "title": "Nepodr\u017eani sustav - LXC otkriven" + }, + "unsupported_network_manager": { + "title": "Nepodr\u017eani sustav - Problemi s upraviteljem mre\u017ee" + }, + "unsupported_os": { + "title": "Nepodr\u017eani sustav - Operativni sustav" + }, + "unsupported_os_agent": { + "title": "Nepodr\u017eani sustav - problemi s OS-Agentom" + }, + "unsupported_restart_policy": { + "title": "Nepodr\u017eani sustav - Pravila ponovnog pokretanja kontejnera" + }, + "unsupported_software": { + "title": "Nepodr\u017eani sustav - Nepodr\u017eani softver" + }, + "unsupported_source_mods": { + "title": "Nepodr\u017eani sustav - Izmjene izvornog koda supervizora" + }, + "unsupported_supervisor_version": { + "title": "Nepodr\u017eani sustav - Supervisor verzija" + }, + "unsupported_systemd": { + "title": "Nepodr\u017eani sustav - Systemd problemi" + }, + "unsupported_systemd_journal": { + "title": "Nepodr\u017eani sustav - Systemd Journal problemi" + }, + "unsupported_systemd_resolved": { + "title": "Nepodr\u017eani sustav - Systemd-Resolved problemi" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hassio/translations/pl.json b/homeassistant/components/hassio/translations/pl.json index 0fdbfa2ab68..7ee470bc9bd 100644 --- a/homeassistant/components/hassio/translations/pl.json +++ b/homeassistant/components/hassio/translations/pl.json @@ -1,12 +1,112 @@ { "issues": { "unhealthy": { - "description": "System jest obecnie \"niezdrowy\" z powodu \u201e{reason}\u201d. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej o tym, co jest nie tak i jak to naprawi\u0107.", + "description": "System jest obecnie \"niezdrowy\" z powodu \u201e{reason}\u201d. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", "title": "Niezdrowy system \u2013 {reason}" }, + "unhealthy_docker": { + "description": "System jest obecnie \"niezdrowy\", poniewa\u017c Docker jest niepoprawnie skonfigurowany. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "\"Niezdrowy\" system \u2014 b\u0142\u0119dna konfiguracja Dockera" + }, + "unhealthy_privileged": { + "description": "System jest obecnie \"niezdrowy\", poniewa\u017c nie ma uprzywilejowanego dost\u0119pu do Docker runtime. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "\"Niezdrowy\" system \u2014 brak uprzywilejowania" + }, + "unhealthy_setup": { + "description": "System jest obecnie \"niezdrowy\", poniewa\u017c konfiguracja nie zosta\u0142a uko\u0144czona. Mo\u017ce si\u0119 tak zdarzy\u0107 z wielu powod\u00f3w, skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "\"Niezdrowy\" system \u2014 konfiguracja nie powiod\u0142a si\u0119" + }, + "unhealthy_supervisor": { + "description": "System jest obecnie \"niezdrowy\", poniewa\u017c pr\u00f3ba aktualizacji Supervisora do najnowszej wersji nie powiod\u0142a si\u0119. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "\"Niezdrowy\" system \u2014 aktualizacja Supervisora nie powiod\u0142a si\u0119" + }, + "unhealthy_untrusted": { + "description": "System jest obecnie \"niezdrowy\", poniewa\u017c wykry\u0142 niezaufany kod lub obrazy w u\u017cyciu. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "\"Niezdrowy\" system \u2014 niezaufany kod" + }, "unsupported": { - "description": "System nie jest obs\u0142ugiwany z powodu \u201e{pow\u00f3d}\u201d. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej o tym, co to oznacza i jak wr\u00f3ci\u0107 do obs\u0142ugiwanego systemu.", + "description": "System nie jest obs\u0142ugiwany z powodu \u201e{pow\u00f3d}\u201d. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", "title": "Nieobs\u0142ugiwany system \u2013 {reason}" + }, + "unsupported_apparmor": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c AppArmor dzia\u0142a nieprawid\u0142owo, a dodatki dzia\u0142aj\u0105 w spos\u00f3b niezabezpieczony i niezabezpieczony. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 problemy z AppArmor" + }, + "unsupported_cgroup_version": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c u\u017cywana jest niew\u0142a\u015bciwa wersja Docker CGroup. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119, jaka jest poprawna wersja i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 wersja CGroup" + }, + "unsupported_connectivity_check": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c Home Assistant nie mo\u017ce okre\u015bli\u0107, kiedy po\u0142\u0105czenie internetowe jest dost\u0119pne. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 sprawdzanie \u0142\u0105czno\u015bci wy\u0142\u0105czone" + }, + "unsupported_content_trust": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c Home Assistant nie mo\u017ce zweryfikowa\u0107, czy uruchamiana zawarto\u015b\u0107 jest zaufana i nie jest modyfikowana przez atakuj\u0105cych. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2013 kontrola zaufania do tre\u015bci wy\u0142\u0105czona" + }, + "unsupported_dbus": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c D-Bus dzia\u0142a nieprawid\u0142owo. Wiele rzeczy zawodzi bez tego, poniewa\u017c Supervisor nie mo\u017ce komunikowa\u0107 si\u0119 z hostem. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 problemy z D-Bus" + }, + "unsupported_dns_server": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c podany serwer DNS nie dzia\u0142a poprawnie, a opcja rezerwowego DNS zosta\u0142a wy\u0142\u0105czona. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 problemy z serwerem DNS" + }, + "unsupported_docker_configuration": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c Docker darmon dzia\u0142a w nieoczekiwany spos\u00f3b. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 b\u0142\u0119dna konfiguracja Dockera" + }, + "unsupported_docker_version": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c u\u017cywana jest niew\u0142a\u015bciwa wersja Dockera. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119, jaka jest poprawna wersja i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 wersja Dockera" + }, + "unsupported_job_conditions": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c co najmniej jeden warunek pracy zosta\u0142 wy\u0142\u0105czony, co chroni przed nieoczekiwanymi awariami i uszkodzeniami. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 ochrona wy\u0142\u0105czona" + }, + "unsupported_lxc": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c jest uruchomiony na maszynie wirtualnej LXC. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2013 wykryto LXC" + }, + "unsupported_network_manager": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c brakuje Mened\u017cera Sieci, jest on nieaktywny lub b\u0142\u0119dnie skonfigurowany. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 problemy z Mened\u017cerem Sieci" + }, + "unsupported_os": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c u\u017cywany system operacyjny nie jest przetestowany ani wspierany do u\u017cytku z Supervisorem. Skorzystaj z linku, aby sprawdzi\u0107 obs\u0142ugiwane systemy operacyjne i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 system operacyjny" + }, + "unsupported_os_agent": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c brakuje OS-Agent, jest on nieaktywny lub b\u0142\u0119dnie skonfigurowany. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system - problemy z OS-Agent" + }, + "unsupported_restart_policy": { + "description": "System jest nieobs\u0142ugiwany, poniewa\u017c kontener Docker ma ustawion\u0105 polityk\u0119 restartu, kt\u00f3ra mo\u017ce powodowa\u0107 problemy podczas uruchamiania. U\u017cyj linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 polityka restartu kontenera" + }, + "unsupported_software": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c wykryto dodatkowe oprogramowanie spoza ekosystemu Home Assistant. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 nieobs\u0142ugiwane oprogramowanie" + }, + "unsupported_source_mods": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c kod \u017ar\u00f3d\u0142owy Supervisora zosta\u0142 zmodyfikowany. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 modyfikacja kodu \u017ar\u00f3d\u0142owego Supervisora" + }, + "unsupported_supervisor_version": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c u\u017cywana jest nieaktualna wersja Supervisora, a automatyczna aktualizacja zosta\u0142a wy\u0142\u0105czona. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 wersja Supervisora" + }, + "unsupported_systemd": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c brakuje Systemd, jest on nieaktywny lub b\u0142\u0119dnie skonfigurowany. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 problemy z Systemd" + }, + "unsupported_systemd_journal": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c brakuje Systemd Journal i/lub us\u0142ugi bramki, jest ona nieaktywna lub b\u0142\u0119dnie skonfigurowana . Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 problemy z Systemd Journal" + }, + "unsupported_systemd_resolved": { + "description": "System nie jest obs\u0142ugiwany, poniewa\u017c brakuje Systemd Resolved, jest on nieaktywny lub b\u0142\u0119dnie skonfigurowany. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "title": "Nieobs\u0142ugiwany system \u2014 problemy z Systemd Resolved" } }, "system_health": { diff --git a/homeassistant/components/hisense_aehw4a1/translations/he.json b/homeassistant/components/hisense_aehw4a1/translations/he.json index 380dbc5d7fc..032c9c9fa17 100644 --- a/homeassistant/components/hisense_aehw4a1/translations/he.json +++ b/homeassistant/components/hisense_aehw4a1/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." } } diff --git a/homeassistant/components/homeassistant/translations/he.json b/homeassistant/components/homeassistant/translations/he.json index 0b9afe54386..75f487a964e 100644 --- a/homeassistant/components/homeassistant/translations/he.json +++ b/homeassistant/components/homeassistant/translations/he.json @@ -1,7 +1,9 @@ { "system_health": { "info": { + "arch": "\u05d0\u05e8\u05db\u05d9\u05d8\u05e7\u05d8\u05d5\u05e8\u05ea \u05de\u05e2\u05d1\u05d3", "config_dir": "\u05e1\u05e4\u05e8\u05d9\u05d9\u05ea \u05ea\u05e6\u05d5\u05e8\u05d4", + "dev": "\u05e4\u05d9\u05ea\u05d5\u05d7", "docker": "Docker", "hassio": "\u05de\u05e4\u05e7\u05d7", "installation_type": "\u05e1\u05d5\u05d2 \u05d4\u05ea\u05e7\u05e0\u05d4", @@ -10,7 +12,8 @@ "python_version": "\u05d2\u05e8\u05e1\u05ea \u05e4\u05d9\u05d9\u05ea\u05d5\u05df", "timezone": "\u05d0\u05d6\u05d5\u05e8 \u05d6\u05de\u05df", "user": "\u05de\u05e9\u05ea\u05de\u05e9", - "version": "\u05d2\u05d9\u05e8\u05e1\u05d4" + "version": "\u05d2\u05d9\u05e8\u05e1\u05d4", + "virtualenv": "\u05e1\u05d1\u05d9\u05d1\u05d4 \u05d5\u05d9\u05e8\u05d8\u05d5\u05d0\u05dc\u05d9\u05ea" } } } \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/translations/hr.json b/homeassistant/components/homematicip_cloud/translations/hr.json index 648dbfe73f9..a1e99ac9642 100644 --- a/homeassistant/components/homematicip_cloud/translations/hr.json +++ b/homeassistant/components/homematicip_cloud/translations/hr.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "connection_aborted": "Povezivanje nije uspjelo", "unknown": "Do\u0161lo je do nepoznate pogre\u0161ke." } } diff --git a/homeassistant/components/huawei_lte/translations/hr.json b/homeassistant/components/huawei_lte/translations/hr.json new file mode 100644 index 00000000000..e5211ba1d01 --- /dev/null +++ b/homeassistant/components/huawei_lte/translations/hr.json @@ -0,0 +1,17 @@ +{ + "config": { + "abort": { + "reauth_successful": "Ponovna provjera autenti\u010dnosti je uspje\u0161na" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Lozinka", + "username": "Korisni\u010dko ime" + }, + "description": "Unesite pristupne podatke za ure\u0111aj.", + "title": "Ponovno autentificirajte integraciju" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hue/translations/hr.json b/homeassistant/components/hue/translations/hr.json index aa28e012caf..1fe00c2f528 100644 --- a/homeassistant/components/hue/translations/hr.json +++ b/homeassistant/components/hue/translations/hr.json @@ -1,5 +1,13 @@ { "config": { + "abort": { + "all_configured": "Sva Philips Hue \u010dvori\u0161ta su ve\u0107 konfigurirana", + "already_configured": "Ure\u0111aj je ve\u0107 konfiguriran", + "cannot_connect": "Povezivanje nije uspjelo", + "discover_timeout": "Nije mogu\u0107e otkriti Hue \u010dvori\u0161ta", + "no_bridges": "Nisu otkrivena Philips Hue \u010dvori\u0161ta", + "unknown": "Neo\u010dekivana gre\u0161ka" + }, "error": { "linking": "Do\u0161lo je do nepoznate pogre\u0161ke u povezivanju.", "register_failed": "Registracija nije uspjela. Poku\u0161ajte ponovo" @@ -8,7 +16,8 @@ "init": { "data": { "host": "Host" - } + }, + "title": "Odaberite Hue \u010dvori\u0161te" } } } diff --git a/homeassistant/components/icloud/translations/he.json b/homeassistant/components/icloud/translations/he.json index eae7fa97a83..95ed8490521 100644 --- a/homeassistant/components/icloud/translations/he.json +++ b/homeassistant/components/icloud/translations/he.json @@ -23,10 +23,10 @@ }, "trusted_device": { "data": { - "trusted_device": "\u05de\u05db\u05e9\u05d9\u05e8 \u05de\u05d4\u05d9\u05de\u05df" + "trusted_device": "\u05d4\u05ea\u05e7\u05df \u05de\u05d4\u05d9\u05de\u05df" }, - "description": "\u05d1\u05d7\u05e8 \u05d0\u05ea \u05d4\u05de\u05db\u05e9\u05d9\u05e8 \u05d4\u05de\u05d4\u05d9\u05de\u05df \u05e9\u05dc\u05da", - "title": "\u05de\u05db\u05e9\u05d9\u05e8 \u05de\u05d4\u05d9\u05de\u05df \u05e9\u05dc iCloud" + "description": "\u05d9\u05e9 \u05dc\u05d1\u05d7\u05d5\u05e8 \u05d0\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05d4\u05de\u05d4\u05d9\u05de\u05df \u05e9\u05dc\u05da", + "title": "\u05d4\u05ea\u05e7\u05df \u05de\u05d4\u05d9\u05de\u05df \u05e9\u05dc iCloud" }, "user": { "data": { diff --git a/homeassistant/components/ifttt/translations/hr.json b/homeassistant/components/ifttt/translations/hr.json new file mode 100644 index 00000000000..9b1af1faf2d --- /dev/null +++ b/homeassistant/components/ifttt/translations/hr.json @@ -0,0 +1,10 @@ +{ + "config": { + "step": { + "user": { + "description": "Jeste li sigurni da \u017eelite postaviti IFTTT?", + "title": "Postavljanje IFTTT Webhook apleta" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/inkbird/translations/he.json b/homeassistant/components/inkbird/translations/he.json index de780eb221a..26219169d12 100644 --- a/homeassistant/components/inkbird/translations/he.json +++ b/homeassistant/components/inkbird/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/intellifire/translations/id.json b/homeassistant/components/intellifire/translations/id.json index 6a38f501811..70205984aaa 100644 --- a/homeassistant/components/intellifire/translations/id.json +++ b/homeassistant/components/intellifire/translations/id.json @@ -19,7 +19,7 @@ } }, "dhcp_confirm": { - "description": "Ingin menyiapkan {host}\nSerial: ({serial})?" + "description": "Ingin menyiapkan {host}\nSerial: {serial}?" }, "manual_device_entry": { "data": { diff --git a/homeassistant/components/isy994/translations/he.json b/homeassistant/components/isy994/translations/he.json index e72724785cc..20e1da4fb79 100644 --- a/homeassistant/components/isy994/translations/he.json +++ b/homeassistant/components/isy994/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u05d4\u05de\u05db\u05e9\u05d9\u05e8 \u05db\u05d1\u05e8 \u05d4\u05d5\u05d2\u05d3\u05e8" + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", diff --git a/homeassistant/components/izone/translations/he.json b/homeassistant/components/izone/translations/he.json index 380dbc5d7fc..032c9c9fa17 100644 --- a/homeassistant/components/izone/translations/he.json +++ b/homeassistant/components/izone/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." } } diff --git a/homeassistant/components/kegtron/translations/he.json b/homeassistant/components/kegtron/translations/he.json index de780eb221a..26219169d12 100644 --- a/homeassistant/components/kegtron/translations/he.json +++ b/homeassistant/components/kegtron/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/kulersky/translations/he.json b/homeassistant/components/kulersky/translations/he.json index d3d68dccc93..4eafc6dc29b 100644 --- a/homeassistant/components/kulersky/translations/he.json +++ b/homeassistant/components/kulersky/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." }, "step": { diff --git a/homeassistant/components/lacrosse_view/translations/he.json b/homeassistant/components/lacrosse_view/translations/he.json index fe6357d0150..081dd5a3725 100644 --- a/homeassistant/components/lacrosse_view/translations/he.json +++ b/homeassistant/components/lacrosse_view/translations/he.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" }, "error": { "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", diff --git a/homeassistant/components/lametric/translations/he.json b/homeassistant/components/lametric/translations/he.json index 97c060f6062..0912073e131 100644 --- a/homeassistant/components/lametric/translations/he.json +++ b/homeassistant/components/lametric/translations/he.json @@ -10,6 +10,17 @@ "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "manual_entry": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API", + "host": "\u05de\u05d0\u05e8\u05d7" + } + }, + "pick_implementation": { + "title": "\u05d1\u05d7\u05e8 \u05e9\u05d9\u05d8\u05ea \u05d0\u05d9\u05de\u05d5\u05ea" + } } } } \ No newline at end of file diff --git a/homeassistant/components/lametric/translations/hr.json b/homeassistant/components/lametric/translations/hr.json new file mode 100644 index 00000000000..27a5722e0b4 --- /dev/null +++ b/homeassistant/components/lametric/translations/hr.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "reauth_successful": "Ponovna provjera autenti\u010dnosti je uspje\u0161na" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lametric/translations/select.hr.json b/homeassistant/components/lametric/translations/select.hr.json new file mode 100644 index 00000000000..b4957eae992 --- /dev/null +++ b/homeassistant/components/lametric/translations/select.hr.json @@ -0,0 +1,8 @@ +{ + "state": { + "lametric__brightness_mode": { + "auto": "Automatski", + "manual": "Ru\u010dno" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/landisgyr_heat_meter/translations/he.json b/homeassistant/components/landisgyr_heat_meter/translations/he.json new file mode 100644 index 00000000000..25d9c78d8cf --- /dev/null +++ b/homeassistant/components/landisgyr_heat_meter/translations/he.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "setup_serial_manual_path": { + "data": { + "device": "\u05e0\u05ea\u05d9\u05d1 \u05d4\u05ea\u05e7\u05df USB" + } + }, + "user": { + "data": { + "device": "\u05d1\u05d7\u05e8 \u05d4\u05ea\u05e7\u05df" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/led_ble/translations/he.json b/homeassistant/components/led_ble/translations/he.json index 6dc5ae75df8..3f0c3c6d1f2 100644 --- a/homeassistant/components/led_ble/translations/he.json +++ b/homeassistant/components/led_ble/translations/he.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", - "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea" + "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", diff --git a/homeassistant/components/lifx/translations/he.json b/homeassistant/components/lifx/translations/he.json index 0ea2e6e551b..e40655a5fbd 100644 --- a/homeassistant/components/lifx/translations/he.json +++ b/homeassistant/components/lifx/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." }, "error": { diff --git a/homeassistant/components/lifx/translations/hr.json b/homeassistant/components/lifx/translations/hr.json new file mode 100644 index 00000000000..d7692d2d6cf --- /dev/null +++ b/homeassistant/components/lifx/translations/hr.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "no_devices_found": "Nijedan ure\u0111aj nije prona\u0111en na mre\u017ei", + "single_instance_allowed": "Ve\u0107 konfigurirano. Mogu\u0107a samo jedna konfiguracija." + }, + "step": { + "confirm": { + "description": "\u017delite li postaviti LIFX?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lookin/translations/he.json b/homeassistant/components/lookin/translations/he.json index 3110857a512..e44e85f614d 100644 --- a/homeassistant/components/lookin/translations/he.json +++ b/homeassistant/components/lookin/translations/he.json @@ -4,11 +4,11 @@ "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "flow_title": "{name} ({host})", diff --git a/homeassistant/components/lutron_caseta/translations/he.json b/homeassistant/components/lutron_caseta/translations/he.json index cb742b61b72..5bebea072fc 100644 --- a/homeassistant/components/lutron_caseta/translations/he.json +++ b/homeassistant/components/lutron_caseta/translations/he.json @@ -19,7 +19,7 @@ "data": { "host": "\u05de\u05d0\u05e8\u05d7" }, - "description": "\u05d4\u05d6\u05df \u05d0\u05ea \u05db\u05ea\u05d5\u05d1\u05ea \u05d4- IP \u05e9\u05dc \u05d4\u05de\u05db\u05e9\u05d9\u05e8." + "description": "\u05d9\u05e9 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05db\u05ea\u05d5\u05d1\u05ea \u05d4-IP \u05e9\u05dc \u05d4\u05d4\u05ea\u05e7\u05df." } } } diff --git a/homeassistant/components/mailgun/translations/hr.json b/homeassistant/components/mailgun/translations/hr.json new file mode 100644 index 00000000000..90563173adf --- /dev/null +++ b/homeassistant/components/mailgun/translations/hr.json @@ -0,0 +1,10 @@ +{ + "config": { + "step": { + "user": { + "description": "Jeste li sigurni da \u017eelite postaviti Mailgun?", + "title": "Postavite Mailgun Webhook" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/he.json b/homeassistant/components/mazda/translations/he.json index 9856fb9034c..269b5952679 100644 --- a/homeassistant/components/mazda/translations/he.json +++ b/homeassistant/components/mazda/translations/he.json @@ -16,7 +16,7 @@ "email": "\u05d3\u05d5\u05d0\"\u05dc", "password": "\u05e1\u05d9\u05e1\u05de\u05d4" }, - "description": "\u05e0\u05d0 \u05d4\u05d6\u05df \u05d0\u05ea \u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d3\u05d5\u05d0\"\u05dc \u05d5\u05d4\u05e1\u05d9\u05e1\u05de\u05d4 \u05e9\u05d1\u05d4\u05df \u05d0\u05ea\u05d4 \u05de\u05e9\u05ea\u05de\u05e9 \u05db\u05d3\u05d9 \u05dc\u05d4\u05d9\u05db\u05e0\u05e1 \u05dc\u05d9\u05d9\u05e9\u05d5\u05dd MyMazda \u05dc\u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05e0\u05d9\u05d9\u05d3\u05d9\u05dd." + "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d3\u05d5\u05d0\"\u05dc \u05d5\u05d4\u05e1\u05d9\u05e1\u05de\u05d4 \u05e9\u05d1\u05d4\u05df \u05d4\u05d9\u05e0\u05da \u05de\u05e9\u05ea\u05de\u05e9 \u05db\u05d3\u05d9 \u05dc\u05d4\u05d9\u05db\u05e0\u05e1 \u05dc\u05d9\u05d9\u05e9\u05d5\u05dd MyMazda \u05dc\u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05e0\u05d9\u05d9\u05d3\u05d9\u05dd." } } } diff --git a/homeassistant/components/meteoclimatic/translations/he.json b/homeassistant/components/meteoclimatic/translations/he.json index db961a2f14c..71e6e6b6943 100644 --- a/homeassistant/components/meteoclimatic/translations/he.json +++ b/homeassistant/components/meteoclimatic/translations/he.json @@ -5,7 +5,7 @@ "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "error": { - "not_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "not_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "step": { "user": { diff --git a/homeassistant/components/moat/translations/he.json b/homeassistant/components/moat/translations/he.json index de780eb221a..26219169d12 100644 --- a/homeassistant/components/moat/translations/he.json +++ b/homeassistant/components/moat/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/mqtt/translations/hr.json b/homeassistant/components/mqtt/translations/hr.json index 6ddc827fff9..c18a994254f 100644 --- a/homeassistant/components/mqtt/translations/hr.json +++ b/homeassistant/components/mqtt/translations/hr.json @@ -1,11 +1,76 @@ { "config": { + "error": { + "bad_certificate": "CA certifikat nije valjan", + "bad_client_cert": "Klijentski certifikat nije valjan, osigurajte da je isporu\u010dena PEM kodirana datoteka", + "bad_client_cert_key": "Klijentski certifikat i privatni klju\u010d nisu valjani par", + "bad_client_key": "Privatni klju\u010d nije valjan, osigurajte da je PEM kodirana datoteka isporu\u010dena bez lozinke", + "bad_discovery_prefix": "Prefiks otkrivanja nije valjan", + "cannot_connect": "Povezivanje nije uspjelo", + "invalid_inclusion": "Klijentski certifikat i privatni klju\u010d moraju biti konfigurirani zajedno" + }, "step": { "broker": { "data": { + "advanced_options": "Napredne opcije", + "broker": "Broker", + "certificate": "Put do datoteke CA certifikata", + "client_cert": "Put do datoteke klijentskog certifikata", + "client_id": "ID klijenta (ostavite prazno za nasumi\u010dno generiran)", + "client_key": "Put do datoteke privatnog klju\u010da", + "discovery": "Omogu\u0107i otkrivanje", + "keepalive": "Vrijeme izme\u0111u slanja poruka keep alive", "password": "Lozinka", "port": "Port", + "protocol": "MQTT protokol", + "set_ca_cert": "Provjera valjanosti brokerskog certifikata", + "set_client_cert": "Kori\u0161tenje klijentskog certifikata", + "tls_insecure": "Zanemari provjeru valjanosti certifikata brokera", "username": "Korisni\u010dko ime" + }, + "description": "Unesite podatke o povezivanju svog MQTT brokera." + }, + "hassio_confirm": { + "data": { + "discovery": "Omogu\u0107i otkrivanje" + }, + "description": "\u017delite li konfigurirati Home Assistant za povezivanje s MQTT brokerom koji pru\u017ea dodatak {addon}?", + "title": "MQTT Broker putem dodatka Home Assistant" + } + } + }, + "issues": { + "deprecated_yaml_broker_settings": { + "title": "Zastarjele MQTT postavke prona\u0111ene u 'configuration.yaml'" + } + }, + "options": { + "error": { + "bad_certificate": "CA certifikat nije valjan", + "bad_client_cert": "Klijentski certifikat nije valjan, osigurajte da je isporu\u010dena PEM kodirana datoteka", + "bad_client_cert_key": "Klijentski certifikat i privatni klju\u010d nisu valjani par", + "bad_client_key": "Privatni klju\u010d nije valjan, osigurajte da je PEM kodirana datoteka isporu\u010dena bez lozinke", + "bad_discovery_prefix": "Prefiks otkrivanja nije valjan", + "invalid_inclusion": "Klijentski certifikat i privatni klju\u010d moraju biti konfigurirani zajedno" + }, + "step": { + "broker": { + "data": { + "advanced_options": "Napredne opcije", + "certificate": "Prijenos datoteke CA certifikata", + "client_cert": "Prijenos datoteke klijentskog certifikata", + "client_id": "ID klijenta (ostavite prazno za nasumi\u010dno generiran)", + "client_key": "Prijenos datoteke privatnog klju\u010da", + "keepalive": "Vrijeme izme\u0111u slanja poruka keep alive", + "protocol": "MQTT protokol", + "set_ca_cert": "Provjera valjanosti certifikata brokera", + "set_client_cert": "Kori\u0161tenje klijentskog certifikata", + "tls_insecure": "Zanemari provjeru valjanosti certifikata brokera" + } + }, + "options": { + "data": { + "discovery_prefix": "Prefiks otkrivanja" } } } diff --git a/homeassistant/components/mqtt/translations/id.json b/homeassistant/components/mqtt/translations/id.json index 08b9c85b110..c6c67cea9c7 100644 --- a/homeassistant/components/mqtt/translations/id.json +++ b/homeassistant/components/mqtt/translations/id.json @@ -73,6 +73,7 @@ "title": "Entitas MQTT {platform} yang dikonfigurasi secara manual membutuhkan perhatian" }, "deprecated_yaml_broker_settings": { + "description": "Pengaturan berikut yang ditemukan di `configuration.yaml` dimigrasikan ke entri konfigurasi MQTT dan sekarang akan menimpa pengaturan di `configuration.yaml`:\n`{deprecated_settings}`\n\nHapus pengaturan ini dari `configuration.yaml` dan mulai ulang Home Assistant untuk memperbaiki masalah ini. Lihat [dokumentasi]({more_info_url}), untuk informasi lebih lanjut.", "title": "Pengaturan MQTT yang usang ditemukan di `configuration.yaml`" } }, @@ -95,8 +96,15 @@ "broker": "Broker", "certificate": "Unggah file sertifikat CA khusus", "client_cert": "Unggah file sertifikat klien", + "client_id": "ID Klien (biarkan kosong agar dibuat secara acak)", + "client_key": "Unggah file kunci pribadi", + "keepalive": "Waktu antara mengirim pesan keep alive", "password": "Kata Sandi", "port": "Port", + "protocol": "Protokol MQTT", + "set_ca_cert": "Validasi sertifikat broker", + "set_client_cert": "Gunakan sertifikat klien", + "tls_insecure": "Abaikan validasi sertifikat broker", "username": "Nama Pengguna" }, "description": "Masukkan informasi koneksi broker MQTT Anda.", @@ -110,6 +118,7 @@ "birth_retain": "Simpan pesan birth", "birth_topic": "Topik pesan birth", "discovery": "Aktifkan penemuan", + "discovery_prefix": "Prefiks penemuan", "will_enable": "Aktifkan pesan 'will'", "will_payload": "Payload pesan will", "will_qos": "QoS pesan will", diff --git a/homeassistant/components/mullvad/translations/he.json b/homeassistant/components/mullvad/translations/he.json index 1551f5e6bb0..4b761e279f9 100644 --- a/homeassistant/components/mullvad/translations/he.json +++ b/homeassistant/components/mullvad/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u05d4\u05de\u05db\u05e9\u05d9\u05e8 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8" + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" }, "error": { "cannot_connect": "\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", diff --git a/homeassistant/components/nest/translations/hr.json b/homeassistant/components/nest/translations/hr.json index d12df4db83b..00eb2bf0d16 100644 --- a/homeassistant/components/nest/translations/hr.json +++ b/homeassistant/components/nest/translations/hr.json @@ -1,10 +1,17 @@ { "config": { + "abort": { + "authorize_url_timeout": "Isteklo je generiranje URL-a za autorizaciju." + }, + "error": { + "unknown": "Neo\u010dekivana gre\u0161ka" + }, "step": { "init": { "data": { "flow_impl": "Pru\u017eatelj usluge" }, + "description": "Odaberite metodu za autorizaciju", "title": "Pru\u017eatelj usluge autentifikacije" }, "link": { diff --git a/homeassistant/components/nibe_heatpump/translations/id.json b/homeassistant/components/nibe_heatpump/translations/id.json index 53e3d202877..3ee3210ee76 100644 --- a/homeassistant/components/nibe_heatpump/translations/id.json +++ b/homeassistant/components/nibe_heatpump/translations/id.json @@ -4,7 +4,7 @@ "already_configured": "Perangkat sudah dikonfigurasi" }, "error": { - "address": "Alamat IP jarak jauh yang ditentukan tidak valid. Alamat harus berupa alamat IPv4.", + "address": "Alamat IP jarak jauh yang ditentukan tidak valid. Alamat harus berupa alamat IP atau nama host yang dapat ditemukan.", "address_in_use": "Port mendengarkan yang dipilih sudah digunakan pada sistem ini.", "model": "Model yang dipilih tampaknya tidak mendukung modbus40", "read": "Kesalahan pada permintaan baca dari pompa. Verifikasi `Port baca jarak jauh` atau `Alamat IP jarak jauh` Anda.", @@ -14,11 +14,18 @@ "step": { "user": { "data": { - "ip_address": "Alamat IP jarak jauh", + "ip_address": "Alamat jarak jauh", "listening_port": "Port mendengarkan lokal", "remote_read_port": "Port baca jarak jauh", "remote_write_port": "Port tulis jarak jauh" - } + }, + "data_description": { + "ip_address": "Alamat unit NibeGW. Perangkat harus dikonfigurasi dengan alamat statis.", + "listening_port": "Port lokal pada sistem ini, tempat unit NibeGW dikonfigurasi untuk mengirim data.", + "remote_read_port": "Port yang digunakan unit NibeGW untuk mendengarkan permintaan baca.", + "remote_write_port": "Port yang digunakan unit NibeGW untuk mendengarkan permintaan tulis." + }, + "description": "Sebelum mencoba mengkonfigurasi integrasi, pastikan bahwa:\n- Unit NibeGW terhubung ke pompa pemanas.\n- Aksesori MODBUS40 telah diaktifkan dalam konfigurasi pompa pemanas.\n- Pompa belum masuk ke dalam kondisi alarm tentang ketiadaan aksesori MODBUS40." } } } diff --git a/homeassistant/components/onvif/translations/he.json b/homeassistant/components/onvif/translations/he.json index 727457fac34..76a421947d7 100644 --- a/homeassistant/components/onvif/translations/he.json +++ b/homeassistant/components/onvif/translations/he.json @@ -38,7 +38,7 @@ "data": { "auto": "\u05d7\u05d9\u05e4\u05d5\u05e9 \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9" }, - "description": "\u05d1\u05dc\u05d7\u05d9\u05e6\u05d4 \u05e2\u05dc \u05e9\u05dc\u05d7, \u05e0\u05d7\u05e4\u05e9 \u05d1\u05e8\u05e9\u05ea \u05e9\u05dc\u05da \u05de\u05db\u05e9\u05d9\u05e8\u05d9 ONVIF \u05d4\u05ea\u05d5\u05de\u05db\u05d9\u05dd \u05d1\u05e4\u05e8\u05d5\u05e4\u05d9\u05dc S.\n\n\u05d9\u05e6\u05e8\u05e0\u05d9\u05dd \u05de\u05e1\u05d5\u05d9\u05de\u05d9\u05dd \u05d4\u05d7\u05dc\u05d5 \u05dc\u05d4\u05e9\u05d1\u05d9\u05ea \u05d0\u05ea ONVIF \u05db\u05d1\u05e8\u05d9\u05e8\u05ea \u05de\u05d7\u05d3\u05dc. \u05e0\u05d0 \u05d5\u05d3\u05d0 \u05e9\u05ea\u05e6\u05d5\u05e8\u05ea ONVIF \u05d6\u05de\u05d9\u05e0\u05d4 \u05d1\u05de\u05e6\u05dc\u05de\u05d4 \u05e9\u05dc\u05da.", + "description": "\u05d1\u05dc\u05d7\u05d9\u05e6\u05d4 \u05e2\u05dc \u05e9\u05dc\u05d7, \u05e0\u05d7\u05e4\u05e9 \u05d1\u05e8\u05e9\u05ea \u05e9\u05dc\u05da \u05d4\u05ea\u05e7\u05e0\u05d9 ONVIF \u05d4\u05ea\u05d5\u05de\u05db\u05d9\u05dd \u05d1\u05e4\u05e8\u05d5\u05e4\u05d9\u05dc S.\n\n\u05d9\u05e6\u05e8\u05e0\u05d9\u05dd \u05de\u05e1\u05d5\u05d9\u05de\u05d9\u05dd \u05d4\u05d7\u05dc\u05d5 \u05dc\u05d4\u05e9\u05d1\u05d9\u05ea \u05d0\u05ea ONVIF \u05db\u05d1\u05e8\u05d9\u05e8\u05ea \u05de\u05d7\u05d3\u05dc. \u05e0\u05d0 \u05dc\u05d5\u05d5\u05d3\u05d0 \u05e9\u05ea\u05e6\u05d5\u05e8\u05ea ONVIF \u05d6\u05de\u05d9\u05e0\u05d4 \u05d1\u05de\u05e6\u05dc\u05de\u05d4 \u05e9\u05dc\u05da.", "title": "\u05d4\u05d2\u05d3\u05e8\u05ea \u05d4\u05ea\u05e7\u05df ONVIF" } } diff --git a/homeassistant/components/openexchangerates/translations/he.json b/homeassistant/components/openexchangerates/translations/he.json new file mode 100644 index 00000000000..34122738a71 --- /dev/null +++ b/homeassistant/components/openexchangerates/translations/he.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7", + "timeout_connect": "\u05e4\u05e1\u05e7 \u05d6\u05de\u05df \u05dc\u05d9\u05e6\u05d9\u05e8\u05ea \u05d7\u05d9\u05d1\u05d5\u05e8" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "timeout_connect": "\u05e4\u05e1\u05e7 \u05d6\u05de\u05df \u05dc\u05d9\u05e6\u05d9\u05e8\u05ea \u05d7\u05d9\u05d1\u05d5\u05e8", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "user": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/openuv/translations/hr.json b/homeassistant/components/openuv/translations/hr.json index 835929d26df..e18b0015d51 100644 --- a/homeassistant/components/openuv/translations/hr.json +++ b/homeassistant/components/openuv/translations/hr.json @@ -1,12 +1,17 @@ { "config": { + "error": { + "invalid_api_key": "Neva\u017ee\u0107i API klju\u010d" + }, "step": { "user": { "data": { + "api_key": "API klju\u010d", "elevation": "Elevacija", "latitude": "Zemljopisna \u0161irina", "longitude": "Zemljopisna du\u017eina" - } + }, + "title": "Ispunite svoje podatke" } } } diff --git a/homeassistant/components/oralb/translations/he.json b/homeassistant/components/oralb/translations/he.json index b182a698234..e34a0c9d525 100644 --- a/homeassistant/components/oralb/translations/he.json +++ b/homeassistant/components/oralb/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "not_supported": "\u05d4\u05ea\u05e7\u05df \u05d0\u05d9\u05e0\u05d5 \u05e0\u05ea\u05de\u05da" }, "flow_title": "{name}", diff --git a/homeassistant/components/oralb/translations/hr.json b/homeassistant/components/oralb/translations/hr.json new file mode 100644 index 00000000000..e592ffe7bb5 --- /dev/null +++ b/homeassistant/components/oralb/translations/hr.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Ure\u0111aj je ve\u0107 konfiguriran", + "already_in_progress": "Konfiguracije je ve\u0107 u tijeku", + "no_devices_found": "Nijedan ure\u0111aj nije prona\u0111en na mre\u017ei", + "not_supported": "Ure\u0111aj nije podr\u017ean" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "\u017delite li postaviti {name}?" + }, + "user": { + "data": { + "address": "Ure\u0111aj" + }, + "description": "Odaberite ure\u0111aj za postavljanje" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/oralb/translations/id.json b/homeassistant/components/oralb/translations/id.json new file mode 100644 index 00000000000..573eb39ed15 --- /dev/null +++ b/homeassistant/components/oralb/translations/id.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi", + "already_in_progress": "Alur konfigurasi sedang berlangsung", + "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan", + "not_supported": "Perangkat tidak didukung" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Ingin menyiapkan {name}?" + }, + "user": { + "data": { + "address": "Perangkat" + }, + "description": "Pilih perangkat untuk disiapkan" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/hr.json b/homeassistant/components/overkiz/translations/hr.json new file mode 100644 index 00000000000..4dc5421011a --- /dev/null +++ b/homeassistant/components/overkiz/translations/hr.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "unsupported_hardware": "Va\u0161 {unsupported_device} hardver nije podr\u017ean ovom integracijom." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ovo_energy/translations/id.json b/homeassistant/components/ovo_energy/translations/id.json index fa072b59236..644dc357d72 100644 --- a/homeassistant/components/ovo_energy/translations/id.json +++ b/homeassistant/components/ovo_energy/translations/id.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "ID akun OVO (hanya tambahkan jika Anda memiliki beberapa akun)", "password": "Kata Sandi", "username": "Nama Pengguna" }, diff --git a/homeassistant/components/plugwise/translations/select.hr.json b/homeassistant/components/plugwise/translations/select.hr.json new file mode 100644 index 00000000000..65d546a205d --- /dev/null +++ b/homeassistant/components/plugwise/translations/select.hr.json @@ -0,0 +1,17 @@ +{ + "state": { + "plugwise__dhw_mode": { + "auto": "Auto", + "boost": "Poja\u010dano", + "comfort": "Udobnost", + "off": "Isklju\u010deno" + }, + "plugwise__regulation_mode": { + "bleeding_cold": "Jako hladno", + "bleeding_hot": "Jako vru\u0107e", + "cooling": "Hla\u0111enje", + "heating": "Grijanje", + "off": "Isklju\u010deno" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/select.id.json b/homeassistant/components/plugwise/translations/select.id.json index 0be50360a0f..3eef4c30aa5 100644 --- a/homeassistant/components/plugwise/translations/select.id.json +++ b/homeassistant/components/plugwise/translations/select.id.json @@ -1,5 +1,11 @@ { "state": { + "plugwise__dhw_mode": { + "auto": "Otomatis", + "boost": "Kencang", + "comfort": "Nyaman", + "off": "Mati" + }, "plugwise__regulation_mode": { "bleeding_cold": "Dingin sekali", "bleeding_hot": "Panas sekali", diff --git a/homeassistant/components/ps4/translations/he.json b/homeassistant/components/ps4/translations/he.json index 0aaba028b7e..fe4f5d7b8d2 100644 --- a/homeassistant/components/ps4/translations/he.json +++ b/homeassistant/components/ps4/translations/he.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" diff --git a/homeassistant/components/pushbullet/translations/bg.json b/homeassistant/components/pushbullet/translations/bg.json new file mode 100644 index 00000000000..11cf3c2b1ed --- /dev/null +++ b/homeassistant/components/pushbullet/translations/bg.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447" + }, + "step": { + "user": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447", + "name": "\u0418\u043c\u0435" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Pushbullet \u0441 \u043f\u043e\u043c\u043e\u0449\u0442\u0430 \u043d\u0430 YAML \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430.\n\n\u0412\u0430\u0448\u0430\u0442\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0430\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0432 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u041f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Pushbullet \u043e\u0442 \u0432\u0430\u0448\u0438\u044f \u0444\u0430\u0439\u043b configuration.yaml \u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant, \u0437\u0430 \u0434\u0430 \u043a\u043e\u0440\u0438\u0433\u0438\u0440\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.", + "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Pushbullet \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/et.json b/homeassistant/components/pushbullet/translations/et.json new file mode 100644 index 00000000000..85b090c2a47 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/et.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Teenus on juba h\u00e4\u00e4lestatud" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_api_key": "Kehtetu API v\u00f5ti" + }, + "step": { + "user": { + "data": { + "api_key": "API v\u00f5ti", + "name": "Nimi" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Pushbulleti konfigureerimine YAML-i abil eemaldatakse.\n\nOlemasolev YAML-i konfiguratsioon imporditakse kasutajaliidesesse automaatselt.\n\nSelle probleemi lahendamiseks eemalda failist configuration.yaml Pushbullet YAML konfiguratsioon ja taask\u00e4ivita Home Assistant.", + "title": "Pushbulleti YAML-i konfiguratsioon eemaldatakse" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/he.json b/homeassistant/components/pushbullet/translations/he.json new file mode 100644 index 00000000000..5f98f90ec8a --- /dev/null +++ b/homeassistant/components/pushbullet/translations/he.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_api_key": "\u05de\u05e4\u05ea\u05d7 API \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" + }, + "step": { + "user": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API", + "name": "\u05e9\u05dd" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/hr.json b/homeassistant/components/pushbullet/translations/hr.json new file mode 100644 index 00000000000..01f02cb4496 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/hr.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "Servis je ve\u0107 konfiguriran" + }, + "error": { + "cannot_connect": "Povezivanje nije uspjelo", + "invalid_api_key": "Neispravan API klju\u010d" + }, + "step": { + "user": { + "data": { + "api_key": "API klju\u010d", + "name": "Ime" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "title": "Pushbullet YAML konfiguracija se uklanja" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/id.json b/homeassistant/components/pushbullet/translations/id.json new file mode 100644 index 00000000000..6a5922193f9 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/id.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Layanan sudah dikonfigurasi" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_api_key": "Kunci API tidak valid" + }, + "step": { + "user": { + "data": { + "api_key": "Kunci API", + "name": "Nama" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Proses konfigurasi Integrasi Pushbullet lewat YAML dalam proses penghapusan.\n\nKonfigurasi YAML yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML Integrasi Pushbullet dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", + "title": "Konfigurasi YAML Integrasi Pushbullet dalam proses penghapusan" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/no.json b/homeassistant/components/pushbullet/translations/no.json new file mode 100644 index 00000000000..347ffdd05a5 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/no.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Tjenesten er allerede konfigurert" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_api_key": "Ugyldig API-n\u00f8kkel" + }, + "step": { + "user": { + "data": { + "api_key": "API-n\u00f8kkel", + "name": "Navn" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfigurering av Pushbullet ved hjelp av YAML blir fjernet. \n\n Din eksisterende YAML-konfigurasjon har blitt importert til brukergrensesnittet automatisk. \n\n Fjern Pushbullet YAML-konfigurasjonen fra filen configuration.yaml og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", + "title": "Pushbullet YAML-konfigurasjonen blir fjernet" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/pl.json b/homeassistant/components/pushbullet/translations/pl.json new file mode 100644 index 00000000000..eedc36d33e4 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/pl.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Us\u0142uga jest ju\u017c skonfigurowana" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_api_key": "Nieprawid\u0142owy klucz API" + }, + "step": { + "user": { + "data": { + "api_key": "Klucz API", + "name": "Nazwa" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfiguracja Pushbullet przy u\u017cyciu YAML zostanie usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML zosta\u0142a automatycznie zaimportowana do interfejsu u\u017cytkownika. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", + "title": "Konfiguracja YAML dla Pushbullet zostanie usuni\u0119ta" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/zh-Hant.json b/homeassistant/components/pushbullet/translations/zh-Hant.json new file mode 100644 index 00000000000..2c641160218 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/zh-Hant.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_api_key": "API \u91d1\u9470\u7121\u6548" + }, + "step": { + "user": { + "data": { + "api_key": "API \u91d1\u9470", + "name": "\u540d\u7a31" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a\u7684 Pushbullet \u5373\u5c07\u9032\u884c\u79fb\u9664\u3002\n\n\u65e2\u6709\u7684 YAML \u8a2d\u5b9a\u5c07\u81ea\u52d5\u532f\u5165\u81f3 UI \u5167\u3002\n\n\u8acb\u65bc configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Pushbullet YAML \u8a2d\u5b9a\u4e26\u91cd\u65b0\u555f\u52d5 Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", + "title": "Pushbullet YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/he.json b/homeassistant/components/pushover/translations/he.json index 9cdb8c5afcd..954ccefdde0 100644 --- a/homeassistant/components/pushover/translations/he.json +++ b/homeassistant/components/pushover/translations/he.json @@ -1,6 +1,19 @@ { "config": { + "abort": { + "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_api_key": "\u05de\u05e4\u05ea\u05d7 API \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" + }, "step": { + "reauth_confirm": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API" + } + }, "user": { "data": { "api_key": "\u05de\u05e4\u05ea\u05d7 API", diff --git a/homeassistant/components/pushover/translations/id.json b/homeassistant/components/pushover/translations/id.json index 347f4ef5d3e..b7aa1d9fd33 100644 --- a/homeassistant/components/pushover/translations/id.json +++ b/homeassistant/components/pushover/translations/id.json @@ -29,6 +29,10 @@ "deprecated_yaml": { "description": "Proses konfigurasi Integrasi Pushover lewat YAML dalam proses penghapusan.\n\nKonfigurasi YAML yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML Integrasi Pushover dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", "title": "Konfigurasi YAML Integrasi Pushover dalam proses penghapusan" + }, + "removed_yaml": { + "description": "Proses konfigurasi Integrasi Pushover lewat YAML telah dihapus.\n\nKonfigurasi YAML yang ada tidak digunakan oleh Home Assistant.\n\nHapus konfigurasi YAML Pushover dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", + "title": "Konfigurasi YAML Integrasi Pushover telah dihapus" } } } \ No newline at end of file diff --git a/homeassistant/components/qingping/translations/he.json b/homeassistant/components/qingping/translations/he.json index de780eb221a..26219169d12 100644 --- a/homeassistant/components/qingping/translations/he.json +++ b/homeassistant/components/qingping/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/rainmachine/translations/id.json b/homeassistant/components/rainmachine/translations/id.json index 8223fb4a792..3c1434b7b4b 100644 --- a/homeassistant/components/rainmachine/translations/id.json +++ b/homeassistant/components/rainmachine/translations/id.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "Gunakan waktu berjalan zona dari aplikasi RainMachine", "zone_run_time": "Waktu berjalan zona default (dalam detik)" }, "title": "Konfigurasikan RainMachine" diff --git a/homeassistant/components/scrape/translations/id.json b/homeassistant/components/scrape/translations/id.json index e7761f73a1f..0ee76592dfe 100644 --- a/homeassistant/components/scrape/translations/id.json +++ b/homeassistant/components/scrape/translations/id.json @@ -36,6 +36,12 @@ } } }, + "issues": { + "moved_yaml": { + "description": "Konfigurasi Integrasi Scrape menggunakan YAML telah dipindahkan ke kunci integrasi.\n\nKonfigurasi YAML Anda yang ada saat ini akan berfungsi hingga 2 versi berikutnya.\n\nMigrasikan konfigurasi YAML Anda ke kunci integrasi sesuai dengan dokumentasi.", + "title": "Konfigurasi YAML Integrasi Scrape telah dihapus" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/sensibo/translations/id.json b/homeassistant/components/sensibo/translations/id.json index fc33b6e7698..64558ebb852 100644 --- a/homeassistant/components/sensibo/translations/id.json +++ b/homeassistant/components/sensibo/translations/id.json @@ -17,7 +17,7 @@ "api_key": "Kunci API" }, "data_description": { - "api_key": "Ikuti petunjuk dalam dokumentasi untuk mendapatkan kunci API baru." + "api_key": "Ikuti petunjuk dalam dokumentasi untuk mendapatkan kunci API" } }, "user": { @@ -25,7 +25,7 @@ "api_key": "Kunci API" }, "data_description": { - "api_key": "Ikuti petunjuk dalam dokumentasi untuk mendapatkan kunci API." + "api_key": "Ikuti petunjuk dalam dokumentasi untuk mendapatkan kunci API" } } } diff --git a/homeassistant/components/sensibo/translations/sensor.hr.json b/homeassistant/components/sensibo/translations/sensor.hr.json new file mode 100644 index 00000000000..26f61fd8d34 --- /dev/null +++ b/homeassistant/components/sensibo/translations/sensor.hr.json @@ -0,0 +1,9 @@ +{ + "state": { + "sensibo__smart_type": { + "feelslike": "Osje\u0107aj", + "humidity": "Vla\u017enost", + "temperature": "Temperatura" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/sensor.id.json b/homeassistant/components/sensibo/translations/sensor.id.json index 54a0554ce41..29fb9777117 100644 --- a/homeassistant/components/sensibo/translations/sensor.id.json +++ b/homeassistant/components/sensibo/translations/sensor.id.json @@ -3,6 +3,11 @@ "sensibo__sensitivity": { "n": "Normal", "s": "Sensitif" + }, + "sensibo__smart_type": { + "feelslike": "Terasa seperti", + "humidity": "Kelembaban", + "temperature": "Suhu" } } } \ No newline at end of file diff --git a/homeassistant/components/sensor/translations/id.json b/homeassistant/components/sensor/translations/id.json index e9e2340fed0..7a782767547 100644 --- a/homeassistant/components/sensor/translations/id.json +++ b/homeassistant/components/sensor/translations/id.json @@ -32,6 +32,7 @@ "is_volatile_organic_compounds": "Tingkat konsentrasi senyawa organik volatil {entity_name} saat ini", "is_voltage": "Tegangan {entity_name} saat ini", "is_volume": "Volume {entity_name} saat ini", + "is_water": "Air pada {entity_name} saat ini", "is_weight": "Berat {entity_name} saat ini" }, "trigger_type": { @@ -66,6 +67,7 @@ "volatile_organic_compounds": "Perubahan konsentrasi senyawa organik volatil {entity_name}", "voltage": "Perubahan tegangan {entity_name}", "volume": "Perubahan volume {entity_name}", + "water": "Perubahan air {entity_name}", "weight": "Perubahan berat {entity_name}" } }, diff --git a/homeassistant/components/sensorpro/translations/he.json b/homeassistant/components/sensorpro/translations/he.json index b182a698234..e34a0c9d525 100644 --- a/homeassistant/components/sensorpro/translations/he.json +++ b/homeassistant/components/sensorpro/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "not_supported": "\u05d4\u05ea\u05e7\u05df \u05d0\u05d9\u05e0\u05d5 \u05e0\u05ea\u05de\u05da" }, "flow_title": "{name}", diff --git a/homeassistant/components/sensorpush/translations/he.json b/homeassistant/components/sensorpush/translations/he.json index de780eb221a..26219169d12 100644 --- a/homeassistant/components/sensorpush/translations/he.json +++ b/homeassistant/components/sensorpush/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/simplisafe/translations/hr.json b/homeassistant/components/simplisafe/translations/hr.json new file mode 100644 index 00000000000..addfc0cbe81 --- /dev/null +++ b/homeassistant/components/simplisafe/translations/hr.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "password": "Lozinka", + "username": "Korisni\u010dko ime" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/skybell/translations/he.json b/homeassistant/components/skybell/translations/he.json index 0e3ced77bc3..f85a3c23279 100644 --- a/homeassistant/components/skybell/translations/he.json +++ b/homeassistant/components/skybell/translations/he.json @@ -13,7 +13,8 @@ "reauth_confirm": { "data": { "password": "\u05e1\u05d9\u05e1\u05de\u05d4" - } + }, + "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1" }, "user": { "data": { diff --git a/homeassistant/components/smhi/translations/hr.json b/homeassistant/components/smhi/translations/hr.json new file mode 100644 index 00000000000..0ceab312f1e --- /dev/null +++ b/homeassistant/components/smhi/translations/hr.json @@ -0,0 +1,16 @@ +{ + "config": { + "error": { + "wrong_location": "Lokacija samo \u0160vedska" + }, + "step": { + "user": { + "data": { + "latitude": "Zemljopisna \u0161irina", + "longitude": "Zemljopisna du\u017eina" + }, + "title": "Lokacija u \u0160vedskoj" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/snooz/translations/he.json b/homeassistant/components/snooz/translations/he.json index de780eb221a..26219169d12 100644 --- a/homeassistant/components/snooz/translations/he.json +++ b/homeassistant/components/snooz/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/snooz/translations/hr.json b/homeassistant/components/snooz/translations/hr.json new file mode 100644 index 00000000000..ff7217c5352 --- /dev/null +++ b/homeassistant/components/snooz/translations/hr.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Ure\u0111aj je ve\u0107 konfiguriran", + "already_in_progress": "Konfiguracije je ve\u0107 u tijeku", + "no_devices_found": "Nijedan ure\u0111aj nije prona\u0111en na mre\u017ei" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "\u017delite li postaviti {name}?" + }, + "user": { + "data": { + "address": "Ure\u0111aj" + }, + "description": "Odaberite ure\u0111aj za postavljanje" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/songpal/translations/he.json b/homeassistant/components/songpal/translations/he.json index a8c8d1d0294..a90c9b90858 100644 --- a/homeassistant/components/songpal/translations/he.json +++ b/homeassistant/components/songpal/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u05de\u05db\u05e9\u05d9\u05e8 \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8" + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" diff --git a/homeassistant/components/sonos/translations/he.json b/homeassistant/components/sonos/translations/he.json index 64824b942ec..5e29646acfb 100644 --- a/homeassistant/components/sonos/translations/he.json +++ b/homeassistant/components/sonos/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "not_sonos_device": "\u05d4\u05ea\u05e7\u05df \u05e9\u05d4\u05ea\u05d2\u05dc\u05d4 \u05d0\u05d9\u05e0\u05d5 \u05d4\u05ea\u05e7\u05df Sonos", "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." }, diff --git a/homeassistant/components/soundtouch/translations/id.json b/homeassistant/components/soundtouch/translations/id.json index cabbb3a6224..f267a63ba1a 100644 --- a/homeassistant/components/soundtouch/translations/id.json +++ b/homeassistant/components/soundtouch/translations/id.json @@ -13,7 +13,7 @@ } }, "zeroconf_confirm": { - "description": "Anda akan menambahkan perangkat SoundTouch bernama `{name}` ke Home Assistant.", + "description": "Anda akan menambahkan perangkat SoundTouch bernama `{name}` ke Home Assistant.", "title": "Konfirmasi penambahan perangkat Bose SoundTouch" } } diff --git a/homeassistant/components/statistics/translations/he.json b/homeassistant/components/statistics/translations/he.json index fca507bcc19..b5cb3b24754 100644 --- a/homeassistant/components/statistics/translations/he.json +++ b/homeassistant/components/statistics/translations/he.json @@ -5,7 +5,8 @@ "title": "\u05d7\u05d5\u05d1\u05d4 'state_characteristic' \u05e9\u05d4\u05d5\u05e0\u05d7\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d9\u05e9\u05d5\u05ea \u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4" }, "deprecation_warning_size": { - "description": "\u05e4\u05e8\u05de\u05d8\u05e8 \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 `sampling_size` \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05d1\u05e8\u05d9\u05e8\u05ea \u05d4\u05de\u05d7\u05d3\u05dc \u05dc\u05e2\u05e8\u05da 20 \u05e2\u05d3 \u05db\u05d4, \u05d0\u05e9\u05e8 \u05d9\u05e9\u05ea\u05e0\u05d4.\n\n\u05d0\u05e0\u05d0 \u05d1\u05d3\u05d5\u05e7 \u05d0\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d7\u05d9\u05d9\u05e9\u05df `{entity}` \u05d5\u05d4\u05d5\u05e1\u05e3 \u05d2\u05d1\u05d5\u05dc\u05d5\u05ea \u05de\u05ea\u05d0\u05d9\u05de\u05d9\u05dd, \u05dc\u05d3\u05d5\u05d2\u05de\u05d4, `sampling_size: 20` \u05db\u05d3\u05d9 \u05dc\u05e9\u05de\u05d5\u05e8 \u05e2\u05dc \u05d4\u05d4\u05ea\u05e0\u05d4\u05d2\u05d5\u05ea \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05ea. \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05ea\u05d4\u05e4\u05d5\u05da \u05dc\u05d2\u05de\u05d9\u05e9\u05d4 \u05d9\u05d5\u05ea\u05e8 \u05e2\u05dd \u05d2\u05e8\u05e1\u05d4 2022.12.0 \u05d5\u05ea\u05e7\u05d1\u05dc `sampling_size` \u05d0\u05d5 `max_age`, \u05d0\u05d5 \u05d0\u05ea \u05e9\u05ea\u05d9 \u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea. \u05d4\u05d1\u05e7\u05e9\u05d4 \u05e9\u05dc\u05e2\u05d9\u05dc \u05de\u05db\u05d9\u05e0\u05d4 \u05d0\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc\u05da \u05dc\u05e9\u05d9\u05e0\u05d5\u05d9 \u05d6\u05d4 \u05e9\u05d0\u05d7\u05e8\u05ea \u05e0\u05e9\u05d1\u05e8.\n\n\u05dc\u05e7\u05e8\u05d5\u05d0 \u05d0\u05ea \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05dc\u05e4\u05e8\u05d8\u05d9\u05dd \u05e0\u05d5\u05e1\u05e4\u05d9\u05dd: https://www.home-assistant.io/integrations/statistics/" + "description": "\u05e4\u05e8\u05de\u05d8\u05e8 \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 `sampling_size` \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05d1\u05e8\u05d9\u05e8\u05ea \u05d4\u05de\u05d7\u05d3\u05dc \u05dc\u05e2\u05e8\u05da 20 \u05e2\u05d3 \u05db\u05d4, \u05d0\u05e9\u05e8 \u05d9\u05e9\u05ea\u05e0\u05d4.\n\n\u05d0\u05e0\u05d0 \u05d1\u05d3\u05d5\u05e7 \u05d0\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d7\u05d9\u05d9\u05e9\u05df `{entity}` \u05d5\u05d4\u05d5\u05e1\u05e3 \u05d2\u05d1\u05d5\u05dc\u05d5\u05ea \u05de\u05ea\u05d0\u05d9\u05de\u05d9\u05dd, \u05dc\u05d3\u05d5\u05d2\u05de\u05d4, `sampling_size: 20` \u05db\u05d3\u05d9 \u05dc\u05e9\u05de\u05d5\u05e8 \u05e2\u05dc \u05d4\u05d4\u05ea\u05e0\u05d4\u05d2\u05d5\u05ea \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05ea. \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05ea\u05d4\u05e4\u05d5\u05da \u05dc\u05d2\u05de\u05d9\u05e9\u05d4 \u05d9\u05d5\u05ea\u05e8 \u05e2\u05dd \u05d2\u05e8\u05e1\u05d4 2022.12.0 \u05d5\u05ea\u05e7\u05d1\u05dc `sampling_size` \u05d0\u05d5 `max_age`, \u05d0\u05d5 \u05d0\u05ea \u05e9\u05ea\u05d9 \u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea. \u05d4\u05d1\u05e7\u05e9\u05d4 \u05e9\u05dc\u05e2\u05d9\u05dc \u05de\u05db\u05d9\u05e0\u05d4 \u05d0\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc\u05da \u05dc\u05e9\u05d9\u05e0\u05d5\u05d9 \u05d6\u05d4 \u05e9\u05d0\u05d7\u05e8\u05ea \u05e0\u05e9\u05d1\u05e8.\n\n\u05dc\u05e7\u05e8\u05d5\u05d0 \u05d0\u05ea \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05dc\u05e4\u05e8\u05d8\u05d9\u05dd \u05e0\u05d5\u05e1\u05e4\u05d9\u05dd: https://www.home-assistant.io/integrations/statistics/", + "title": "'sampling_size' \u05de\u05e8\u05d5\u05de\u05d6\u05ea \u05e9\u05d4\u05d5\u05e0\u05d7\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d9\u05e9\u05d5\u05ea \u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05ea" } } } \ No newline at end of file diff --git a/homeassistant/components/statistics/translations/id.json b/homeassistant/components/statistics/translations/id.json new file mode 100644 index 00000000000..13ac93d21dd --- /dev/null +++ b/homeassistant/components/statistics/translations/id.json @@ -0,0 +1,12 @@ +{ + "issues": { + "deprecation_warning_characteristic": { + "description": "Parameter konfigurasi `state_characteristic` dari integrasi statistik akan menjadi wajib.\n\nTambahkan `state_characteristic: {characteristic}` ke dalam konfigurasi sensor `{entity}` untuk menjaga perilaku saat ini.\n\nBaca dokumentasi integrasi Statistik untuk detail lebih lanjut: https://www.home-assistant.io/integrations/statistics/", + "title": "Parameter wajib 'state_characteristic' diasumsikan untuk entitas Statistik" + }, + "deprecation_warning_size": { + "description": "Parameter konfigurasi `sampling_size` dari integrasi Statistik yang bernilai default 20 sejauh ini, akan berubah.\n\nPeriksa konfigurasi untuk sensor `{entity}` dan tambahkan batas-batas yang sesuai, misalnya, `sampling_size: 20` untuk menjaga perilaku saat ini. Konfigurasi integrasi Statistik akan menjadi lebih fleksibel mulai versi 2022.12.0 dan menerima baik parameter `sampling_size` atau `max_age`, atau keduanya. Permintaan di atas akan mempersiapkan konfigurasi Anda untuk menghadapi perubahan besar ini.\n\nBaca dokumentasi integrasi Statistik untuk detail lebih lanjut: https://www.home-assistant.io/integrations/statistics/", + "title": "Parameter implisit 'sampling_size' diasumsikan untuk entitas Statistik" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/bg.json b/homeassistant/components/steam_online/translations/bg.json index 8d946452ca0..5cfc98b4803 100644 --- a/homeassistant/components/steam_online/translations/bg.json +++ b/homeassistant/components/steam_online/translations/bg.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_account": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d ID \u043d\u0430 \u0430\u043a\u0430\u0443\u043d\u0442\u0430", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, diff --git a/homeassistant/components/steamist/translations/he.json b/homeassistant/components/steamist/translations/he.json index 7b8528476e1..405f691d4b3 100644 --- a/homeassistant/components/steamist/translations/he.json +++ b/homeassistant/components/steamist/translations/he.json @@ -4,7 +4,7 @@ "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", diff --git a/homeassistant/components/switcher_kis/translations/he.json b/homeassistant/components/switcher_kis/translations/he.json index d3d68dccc93..4eafc6dc29b 100644 --- a/homeassistant/components/switcher_kis/translations/he.json +++ b/homeassistant/components/switcher_kis/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." }, "step": { diff --git a/homeassistant/components/tautulli/translations/hr.json b/homeassistant/components/tautulli/translations/hr.json new file mode 100644 index 00000000000..b4c376c3855 --- /dev/null +++ b/homeassistant/components/tautulli/translations/hr.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "api_key": "API klju\u010d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tellduslive/translations/hr.json b/homeassistant/components/tellduslive/translations/hr.json new file mode 100644 index 00000000000..4b26b20a8eb --- /dev/null +++ b/homeassistant/components/tellduslive/translations/hr.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "unknown": "Neo\u010dekivana gre\u0161ka" + }, + "step": { + "user": { + "data": { + "host": "Host" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/thermobeacon/translations/he.json b/homeassistant/components/thermobeacon/translations/he.json index b182a698234..e34a0c9d525 100644 --- a/homeassistant/components/thermobeacon/translations/he.json +++ b/homeassistant/components/thermobeacon/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "not_supported": "\u05d4\u05ea\u05e7\u05df \u05d0\u05d9\u05e0\u05d5 \u05e0\u05ea\u05de\u05da" }, "flow_title": "{name}", diff --git a/homeassistant/components/thermopro/translations/he.json b/homeassistant/components/thermopro/translations/he.json index 47308062d0d..26219169d12 100644 --- a/homeassistant/components/thermopro/translations/he.json +++ b/homeassistant/components/thermopro/translations/he.json @@ -1,5 +1,10 @@ { "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + }, "flow_title": "{name}", "step": { "bluetooth_confirm": { diff --git a/homeassistant/components/tilt_ble/translations/he.json b/homeassistant/components/tilt_ble/translations/he.json index de780eb221a..26219169d12 100644 --- a/homeassistant/components/tilt_ble/translations/he.json +++ b/homeassistant/components/tilt_ble/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/tplink/translations/he.json b/homeassistant/components/tplink/translations/he.json index fc44b0d7ae7..8a4dcfb5134 100644 --- a/homeassistant/components/tplink/translations/he.json +++ b/homeassistant/components/tplink/translations/he.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" diff --git a/homeassistant/components/tradfri/translations/hr.json b/homeassistant/components/tradfri/translations/hr.json index bb242ca60f0..32fb3fd7fb4 100644 --- a/homeassistant/components/tradfri/translations/hr.json +++ b/homeassistant/components/tradfri/translations/hr.json @@ -1,13 +1,20 @@ { "config": { "abort": { + "already_configured": "Ure\u0111aj je ve\u0107 konfiguriran", "already_in_progress": "Konfiguracija premosnice je ve\u0107 u tijeku." }, + "error": { + "cannot_connect": "Povezivanje nije uspjelo" + }, "step": { "auth": { "data": { - "host": "Host" - } + "host": "Host", + "security_code": "Sigurnosni kod" + }, + "description": "Sigurnosni k\u00f4d mo\u017eete prona\u0107i na pole\u0111ini pristupnika.", + "title": "Unesite sigurnosni kod" } } } diff --git a/homeassistant/components/transmission/translations/id.json b/homeassistant/components/transmission/translations/id.json index 7b5fa3a703f..98a5e918740 100644 --- a/homeassistant/components/transmission/translations/id.json +++ b/homeassistant/components/transmission/translations/id.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "Perbarui semua otomasi atau skrip yang menggunakan layanan ini dan ganti kunci name dengan kunci entry_id.", + "title": "Kunci name dalam layanan Transmission sedang dihapus" + } + } + }, + "title": "Kunci name dalam layanan Transmission sedang dihapus" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/twilio/translations/hr.json b/homeassistant/components/twilio/translations/hr.json new file mode 100644 index 00000000000..5307b9f4eb0 --- /dev/null +++ b/homeassistant/components/twilio/translations/hr.json @@ -0,0 +1,10 @@ +{ + "config": { + "step": { + "user": { + "description": "\u017delite li zapo\u010deti s postavljanjem?", + "title": "Postavite Twilio Webhook" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifi/translations/hr.json b/homeassistant/components/unifi/translations/hr.json index 94a064f34b4..47ba1743f71 100644 --- a/homeassistant/components/unifi/translations/hr.json +++ b/homeassistant/components/unifi/translations/hr.json @@ -1,13 +1,19 @@ { "config": { + "error": { + "service_unavailable": "Povezivanje nije uspjelo" + }, "step": { "user": { "data": { "host": "Host", "password": "Lozinka", "port": "Port", - "username": "Korisni\u010dko ime" - } + "site": "ID lokacije", + "username": "Korisni\u010dko ime", + "verify_ssl": "Provjerite SSL certifikat" + }, + "title": "Postavite UniFi mre\u017eu" } } } diff --git a/homeassistant/components/upcloud/translations/id.json b/homeassistant/components/upcloud/translations/id.json index 4ff6a8c7d92..d51e6a2e712 100644 --- a/homeassistant/components/upcloud/translations/id.json +++ b/homeassistant/components/upcloud/translations/id.json @@ -17,7 +17,7 @@ "step": { "init": { "data": { - "scan_interval": "Interval pembaruan (dalam detik, minimal 30)" + "scan_interval": "Interval pembaruan dalam detik, minimal 30" } } } diff --git a/homeassistant/components/upnp/translations/he.json b/homeassistant/components/upnp/translations/he.json index a8501a7de41..36640a492a9 100644 --- a/homeassistant/components/upnp/translations/he.json +++ b/homeassistant/components/upnp/translations/he.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/upnp/translations/hr.json b/homeassistant/components/upnp/translations/hr.json index 941f72f2e7d..785e860e69b 100644 --- a/homeassistant/components/upnp/translations/hr.json +++ b/homeassistant/components/upnp/translations/hr.json @@ -1,9 +1,20 @@ { "config": { + "abort": { + "already_configured": "Ure\u0111aj je ve\u0107 konfiguriran", + "no_devices_found": "Nijedan ure\u0111aj nije prona\u0111en na mre\u017ei" + }, "error": { "few": "Nekoliko", "one": "Jedan", "other": "Ostalo" + }, + "step": { + "init": { + "few": "Nekoliko", + "one": "Jedan", + "other": "Ostalo" + } } } } \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/hr.json b/homeassistant/components/utility_meter/translations/hr.json new file mode 100644 index 00000000000..d4e640e4069 --- /dev/null +++ b/homeassistant/components/utility_meter/translations/hr.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Ime" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/id.json b/homeassistant/components/utility_meter/translations/id.json index d2f9bab72c5..4375e6f9764 100644 --- a/homeassistant/components/utility_meter/translations/id.json +++ b/homeassistant/components/utility_meter/translations/id.json @@ -26,7 +26,7 @@ "step": { "init": { "data": { - "source": "Sensor Input" + "source": "Sensor input" } } } diff --git a/homeassistant/components/volvooncall/translations/he.json b/homeassistant/components/volvooncall/translations/he.json index 6f2cdbf82e1..ad8caad16ae 100644 --- a/homeassistant/components/volvooncall/translations/he.json +++ b/homeassistant/components/volvooncall/translations/he.json @@ -1,9 +1,11 @@ { "config": { "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" }, "error": { + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { diff --git a/homeassistant/components/volvooncall/translations/id.json b/homeassistant/components/volvooncall/translations/id.json index d4b60911401..f1090bd2509 100644 --- a/homeassistant/components/volvooncall/translations/id.json +++ b/homeassistant/components/volvooncall/translations/id.json @@ -23,7 +23,7 @@ }, "issues": { "deprecated_yaml": { - "description": "Proses konfigurasi platform Volvo On Call lewat YAML dalam proses penghapusan di versi mendatang Home Assistant.\n\nKonfigurasi yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", + "description": "Proses konfigurasi platform Volvo On Call lewat YAML dalam proses penghapusan di versi mendatang Home Assistant.\n\nKonfigurasi yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", "title": "Konfigurasi YAML Volvo On Call dalam proses penghapusan" } } diff --git a/homeassistant/components/wemo/translations/he.json b/homeassistant/components/wemo/translations/he.json index 380dbc5d7fc..032c9c9fa17 100644 --- a/homeassistant/components/wemo/translations/he.json +++ b/homeassistant/components/wemo/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." } } diff --git a/homeassistant/components/wiz/translations/he.json b/homeassistant/components/wiz/translations/he.json index 81b954067f7..44f40330fcd 100644 --- a/homeassistant/components/wiz/translations/he.json +++ b/homeassistant/components/wiz/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", diff --git a/homeassistant/components/wolflink/translations/sensor.hr.json b/homeassistant/components/wolflink/translations/sensor.hr.json new file mode 100644 index 00000000000..98aa80601c8 --- /dev/null +++ b/homeassistant/components/wolflink/translations/sensor.hr.json @@ -0,0 +1,7 @@ +{ + "state": { + "wolflink__state": { + "permanent": "Trajno" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xiaomi_ble/translations/he.json b/homeassistant/components/xiaomi_ble/translations/he.json index b90a366130a..0df85dd1fe5 100644 --- a/homeassistant/components/xiaomi_ble/translations/he.json +++ b/homeassistant/components/xiaomi_ble/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" }, "flow_title": "{name}", diff --git a/homeassistant/components/xiaomi_miio/translations/hr.json b/homeassistant/components/xiaomi_miio/translations/hr.json new file mode 100644 index 00000000000..be104f26c77 --- /dev/null +++ b/homeassistant/components/xiaomi_miio/translations/hr.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "unknown": "Neo\u010dekivana gre\u0161ka" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/id.json b/homeassistant/components/xiaomi_miio/translations/id.json index 8d1d70642aa..f3dbc9877dd 100644 --- a/homeassistant/components/xiaomi_miio/translations/id.json +++ b/homeassistant/components/xiaomi_miio/translations/id.json @@ -5,7 +5,8 @@ "already_in_progress": "Alur konfigurasi sedang berlangsung", "incomplete_info": "Informasi tidak lengkap untuk menyiapkan perangkat, tidak ada host atau token yang disediakan.", "not_xiaomi_miio": "Perangkat (masih) tidak didukung oleh Xiaomi Miio.", - "reauth_successful": "Autentikasi ulang berhasil" + "reauth_successful": "Autentikasi ulang berhasil", + "unknown": "Kesalahan yang tidak diharapkan" }, "error": { "cannot_connect": "Gagal terhubung", @@ -36,7 +37,7 @@ "host": "Alamat IP", "token": "Token API" }, - "description": "Anda akan membutuhkan Token API 32 karakter, baca petunjuknya di https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token. Perhatikan bahwa Token API ini berbeda dengan kunci yang digunakan untuk integrasi Xiaomi Aqara." + "description": "Anda akan membutuhkan Token API 32 karakter, baca petunjuknya di https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token. Perhatikan bahwa Token API ini berbeda dengan kunci yang digunakan untuk integrasi Xiaomi Aqara." }, "reauth_confirm": { "description": "Integrasi Xiaomi Miio perlu mengautentikasi ulang akun Anda untuk memperbarui token atau menambahkan kredensial cloud yang hilang.", diff --git a/homeassistant/components/yalexs_ble/translations/he.json b/homeassistant/components/yalexs_ble/translations/he.json index a447b36c3ec..5ca18ddcb96 100644 --- a/homeassistant/components/yalexs_ble/translations/he.json +++ b/homeassistant/components/yalexs_ble/translations/he.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", diff --git a/homeassistant/components/yeelight/translations/he.json b/homeassistant/components/yeelight/translations/he.json index 94e0e87d87a..bb11e283db3 100644 --- a/homeassistant/components/yeelight/translations/he.json +++ b/homeassistant/components/yeelight/translations/he.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" diff --git a/homeassistant/components/zamg/translations/hr.json b/homeassistant/components/zamg/translations/hr.json new file mode 100644 index 00000000000..bc981183e94 --- /dev/null +++ b/homeassistant/components/zamg/translations/hr.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "already_configured": "Ure\u0111aj je ve\u0107 konfiguriran", + "cannot_connect": "Povezivanje nije uspjelo" + }, + "error": { + "cannot_connect": "Povezivanje nije uspjelo" + }, + "flow_title": "{name}" + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/id.json b/homeassistant/components/zamg/translations/id.json new file mode 100644 index 00000000000..4330ae8c3de --- /dev/null +++ b/homeassistant/components/zamg/translations/id.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi", + "cannot_connect": "Gagal terhubung" + }, + "error": { + "cannot_connect": "Gagal terhubung" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "ID Stasiun (Default ke stasiun terdekat)" + }, + "description": "Siapkan ZAMG untuk diintegrasikan dengan Home Assistant." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Proses konfigurasi Integrasi ZAMG lewat YAML dalam proses penghapusan.\n\nKonfigurasi YAML yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML Integrasi ZAMG dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", + "title": "Konfigurasi YAML ZAMG dalam proses penghapusan" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zerproc/translations/he.json b/homeassistant/components/zerproc/translations/he.json index 459053197a6..c6da7f61442 100644 --- a/homeassistant/components/zerproc/translations/he.json +++ b/homeassistant/components/zerproc/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "single_instance_allowed": "\u05db\u05d1\u05e8 \u05d4\u05d5\u05d2\u05d3\u05e8. \u05e8\u05e7 \u05d4\u05d2\u05d3\u05e8\u05d4 \u05d9\u05d7\u05d9\u05d3\u05d4 \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." }, "step": { diff --git a/homeassistant/components/zha/translations/he.json b/homeassistant/components/zha/translations/he.json index f48b30bd826..93c614699f2 100644 --- a/homeassistant/components/zha/translations/he.json +++ b/homeassistant/components/zha/translations/he.json @@ -57,6 +57,9 @@ "abort": { "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" + }, "flow_title": "{name}", "step": { "init": { diff --git a/homeassistant/components/zha/translations/hr.json b/homeassistant/components/zha/translations/hr.json new file mode 100644 index 00000000000..2098f53ca2b --- /dev/null +++ b/homeassistant/components/zha/translations/hr.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ve\u0107 konfigurirano. Mogu\u0107a samo jedna konfiguracija." + }, + "error": { + "cannot_connect": "Povezivanje nije uspjelo" + }, + "step": { + "user": { + "title": "ZHA" + } + } + }, + "options": { + "abort": { + "single_instance_allowed": "Ve\u0107 konfigurirano. Mogu\u0107a samo jedna konfiguracija." + }, + "error": { + "cannot_connect": "Povezivanje nije uspjelo" + }, + "step": { + "intent_migrate": { + "title": "Migracija na novi radio" + }, + "prompt_migrate_or_reconfigure": { + "description": "Migrirate li na novi radio ili rekonfigurirate trenutni radio?", + "menu_options": { + "intent_migrate": "Migracija na novi radio", + "intent_reconfigure": "Ponovno konfiguriranje trenutnog radija" + }, + "title": "Migracija ili ponovna konfiguracija" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zha/translations/id.json b/homeassistant/components/zha/translations/id.json index ab496b3be53..10ad82bc980 100644 --- a/homeassistant/components/zha/translations/id.json +++ b/homeassistant/components/zha/translations/id.json @@ -61,7 +61,7 @@ "data": { "overwrite_coordinator_ieee": "Ganti alamat radio IEEE secara permanen" }, - "description": "Cadangan Anda memiliki alamat IEEE yang berbeda dari radio Anda. Agar jaringan berfungsi dengan baik, alamat IEEE radio Anda juga harus diubah.\n\nOperasi ini bersifat permanen.", + "description": "Cadangan Anda memiliki alamat IEEE yang berbeda dari radio Anda. Agar jaringan berfungsi dengan baik, alamat IEEE radio Anda juga harus diubah.\n\nOperasi ini bersifat permanen.", "title": "Timpa Alamat IEEE Radio" }, "pick_radio": { @@ -207,7 +207,7 @@ "title": "Pilih Port Serial" }, "init": { - "description": "ZHA akan dihentikan. Ingin melanjutkan?", + "description": "ZHA akan dihentikan. Ingin melanjutkan?", "title": "Konfigurasi Ulang ZHA" }, "instruct_unplug": { @@ -215,7 +215,7 @@ "title": "Cabut radio lama Anda" }, "intent_migrate": { - "description": "Radio lama Anda akan disetel ulang ke setelan pabrikan. Jika Anda menggunakan adaptor gabungan Z-Wave dan Zigbee seperti HUSBZB-1, ini hanya akan mengatur ulang bagian Zigbee.\n\nApakah Anda ingin melanjutkan?", + "description": "Radio lama Anda akan disetel ulang ke setelan pabrikan. Jika Anda menggunakan adaptor gabungan Z-Wave dan Zigbee seperti HUSBZB-1, ini hanya akan mengatur ulang bagian Zigbee.\n\nApakah Anda ingin melanjutkan?", "title": "Migrasikan ke radio baru" }, "manual_pick_radio_type": { @@ -238,7 +238,7 @@ "data": { "overwrite_coordinator_ieee": "Ganti alamat radio IEEE secara permanen" }, - "description": "Cadangan Anda memiliki alamat IEEE yang berbeda dari radio Anda. Agar jaringan berfungsi dengan baik, alamat IEEE radio Anda juga harus diubah.\n\nOperasi ini bersifat permanen.", + "description": "Cadangan Anda memiliki alamat IEEE yang berbeda dari radio Anda. Agar jaringan berfungsi dengan baik, alamat IEEE radio Anda juga harus diubah.\n\nOperasi ini bersifat permanen.", "title": "Timpa Alamat IEEE Radio" }, "prompt_migrate_or_reconfigure": { From 93072d8ac5fa34e57eade45ca227ad9800890da8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 4 Nov 2022 06:34:23 +0100 Subject: [PATCH 0201/1033] Bump dbus-fast to 1.67.0 (#81517) Bump dbus to 1.67.0 The bleak BlueZ clients use the negotiate_unix_fd path which was not optimized but is now. (The scanners already used a fast path) changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v1.64.0...v1.67.0 --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index bcb7ce9b156..49731df2f9b 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.8.2", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", - "dbus-fast==1.64.0" + "dbus-fast==1.67.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 160f313d883..ae7e535dbc0 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.3 -dbus-fast==1.64.0 +dbus-fast==1.67.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 225d0096684..5a866deb8a6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -546,7 +546,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.64.0 +dbus-fast==1.67.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5a7f2351050..c6e08474217 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -426,7 +426,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.64.0 +dbus-fast==1.67.0 # homeassistant.components.debugpy debugpy==1.6.3 From 1a5eeb2db11db68d5da59830503b6f3f823ec5e5 Mon Sep 17 00:00:00 2001 From: Nyro Date: Fri, 4 Nov 2022 10:21:30 +0100 Subject: [PATCH 0202/1033] Add Overkiz AtlanticPassAPCHeatingAndCoolingZone (#78659) * Add Overkiz AtlanticPassAPCHeatingAndCoolingZone * Fix commands instanciations to be simpler * Update AtlanticPassAPCHeatingAndCoolingZone to show temperature and fix HA threads * Simplify async_execute_commands in order to receive simpler list * Fix get and set temperature in derogation or auto mode * Remove hvac_action from AtlanticPassAPCHeatingAndCoolingZone * Remove unused lines * Update async_execute_commands to work like async_execute_command Implement cancel for multiple commands * Improve to use preset_home for internal scheduling * Remove async_execute_commands * Improvement for AtlanticPassAPCHeatingAndCoolingZone * Update homeassistant/components/overkiz/climate_entities/__init__.py Co-authored-by: Quentame * Update homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_and_cooling_zone.py Co-authored-by: Quentame * Update homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_and_cooling_zone.py Co-authored-by: Quentame * Update homeassistant/components/overkiz/const.py Co-authored-by: Quentame Co-authored-by: Quentame --- .../overkiz/climate_entities/__init__.py | 4 + ...antic_pass_apc_heating_and_cooling_zone.py | 203 ++++++++++++++++++ homeassistant/components/overkiz/const.py | 1 + homeassistant/components/overkiz/entity.py | 5 +- 4 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_and_cooling_zone.py diff --git a/homeassistant/components/overkiz/climate_entities/__init__.py b/homeassistant/components/overkiz/climate_entities/__init__.py index 32fae234be1..359f7629a7b 100644 --- a/homeassistant/components/overkiz/climate_entities/__init__.py +++ b/homeassistant/components/overkiz/climate_entities/__init__.py @@ -4,6 +4,9 @@ from pyoverkiz.enums.ui import UIWidget from .atlantic_electrical_heater import AtlanticElectricalHeater from .atlantic_electrical_towel_dryer import AtlanticElectricalTowelDryer from .atlantic_heat_recovery_ventilation import AtlanticHeatRecoveryVentilation +from .atlantic_pass_apc_heating_and_cooling_zone import ( + AtlanticPassAPCHeatingAndCoolingZone, +) from .atlantic_pass_apc_zone_control import AtlanticPassAPCZoneControl from .somfy_thermostat import SomfyThermostat @@ -11,6 +14,7 @@ WIDGET_TO_CLIMATE_ENTITY = { UIWidget.ATLANTIC_ELECTRICAL_HEATER: AtlanticElectricalHeater, UIWidget.ATLANTIC_ELECTRICAL_TOWEL_DRYER: AtlanticElectricalTowelDryer, UIWidget.ATLANTIC_HEAT_RECOVERY_VENTILATION: AtlanticHeatRecoveryVentilation, + UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: AtlanticPassAPCHeatingAndCoolingZone, UIWidget.ATLANTIC_PASS_APC_ZONE_CONTROL: AtlanticPassAPCZoneControl, UIWidget.SOMFY_THERMOSTAT: SomfyThermostat, } diff --git a/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_and_cooling_zone.py b/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_and_cooling_zone.py new file mode 100644 index 00000000000..efdbf94d9f7 --- /dev/null +++ b/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_and_cooling_zone.py @@ -0,0 +1,203 @@ +"""Support for Atlantic Pass APC Heating And Cooling Zone Control.""" +from __future__ import annotations + +from typing import Any, cast + +from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState + +from homeassistant.components.climate import ( + PRESET_AWAY, + PRESET_COMFORT, + PRESET_ECO, + PRESET_HOME, + PRESET_SLEEP, + ClimateEntity, + ClimateEntityFeature, + HVACMode, +) +from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS + +from ..coordinator import OverkizDataUpdateCoordinator +from ..entity import OverkizEntity + +OVERKIZ_TO_HVAC_MODE: dict[str, str] = { + OverkizCommandParam.AUTO: HVACMode.AUTO, + OverkizCommandParam.ECO: HVACMode.AUTO, + OverkizCommandParam.MANU: HVACMode.HEAT, + OverkizCommandParam.HEATING: HVACMode.HEAT, + OverkizCommandParam.STOP: HVACMode.OFF, + OverkizCommandParam.INTERNAL_SCHEDULING: HVACMode.AUTO, + OverkizCommandParam.COMFORT: HVACMode.HEAT, +} + +HVAC_MODE_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_HVAC_MODE.items()} + +OVERKIZ_TO_PRESET_MODES: dict[str, str] = { + OverkizCommandParam.OFF: PRESET_ECO, + OverkizCommandParam.STOP: PRESET_ECO, + OverkizCommandParam.MANU: PRESET_COMFORT, + OverkizCommandParam.COMFORT: PRESET_COMFORT, + OverkizCommandParam.ABSENCE: PRESET_AWAY, + OverkizCommandParam.ECO: PRESET_ECO, + OverkizCommandParam.INTERNAL_SCHEDULING: PRESET_HOME, +} + +PRESET_MODES_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_PRESET_MODES.items()} + +OVERKIZ_TO_PROFILE_MODES: dict[str, str] = { + OverkizCommandParam.OFF: PRESET_SLEEP, + OverkizCommandParam.STOP: PRESET_SLEEP, + OverkizCommandParam.ECO: PRESET_ECO, + OverkizCommandParam.ABSENCE: PRESET_AWAY, + OverkizCommandParam.MANU: PRESET_COMFORT, + OverkizCommandParam.DEROGATION: PRESET_COMFORT, + OverkizCommandParam.COMFORT: PRESET_COMFORT, +} + +OVERKIZ_TEMPERATURE_STATE_BY_PROFILE: dict[str, str] = { + OverkizCommandParam.ECO: OverkizState.CORE_ECO_HEATING_TARGET_TEMPERATURE, + OverkizCommandParam.COMFORT: OverkizState.CORE_COMFORT_HEATING_TARGET_TEMPERATURE, + OverkizCommandParam.DEROGATION: OverkizState.CORE_DEROGATED_TARGET_TEMPERATURE, +} + + +class AtlanticPassAPCHeatingAndCoolingZone(OverkizEntity, ClimateEntity): + """Representation of Atlantic Pass APC Heating And Cooling Zone Control.""" + + _attr_hvac_modes = [*HVAC_MODE_TO_OVERKIZ] + _attr_preset_modes = [*PRESET_MODES_TO_OVERKIZ] + _attr_supported_features = ( + ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE + ) + _attr_temperature_unit = TEMP_CELSIUS + + def __init__( + self, device_url: str, coordinator: OverkizDataUpdateCoordinator + ) -> None: + """Init method.""" + super().__init__(device_url, coordinator) + + # Temperature sensor use the same base_device_url and use the n+1 index + self.temperature_device = self.executor.linked_device( + int(self.index_device_url) + 1 + ) + + @property + def current_temperature(self) -> float | None: + """Return the current temperature.""" + if temperature := self.temperature_device.states[OverkizState.CORE_TEMPERATURE]: + return cast(float, temperature.value) + + return None + + @property + def hvac_mode(self) -> str: + """Return hvac operation ie. heat, cool mode.""" + return OVERKIZ_TO_HVAC_MODE[ + cast(str, self.executor.select_state(OverkizState.IO_PASS_APC_HEATING_MODE)) + ] + + @property + def current_heating_profile(self) -> str: + """Return current heating profile.""" + return cast( + str, + self.executor.select_state(OverkizState.IO_PASS_APC_HEATING_PROFILE), + ) + + async def async_set_heating_mode(self, mode: str) -> None: + """Set new heating mode and refresh states.""" + await self.executor.async_execute_command( + OverkizCommand.SET_PASS_APC_HEATING_MODE, mode + ) + + if self.current_heating_profile == OverkizCommandParam.DEROGATION: + # If current mode is in derogation, disable it + await self.executor.async_execute_command( + OverkizCommand.SET_DEROGATION_ON_OFF_STATE, OverkizCommandParam.OFF + ) + + # We also needs to execute these 2 commands to make it work correctly + await self.executor.async_execute_command( + OverkizCommand.REFRESH_PASS_APC_HEATING_MODE + ) + await self.executor.async_execute_command( + OverkizCommand.REFRESH_PASS_APC_HEATING_PROFILE + ) + + async def async_set_hvac_mode(self, hvac_mode: str) -> None: + """Set new target hvac mode.""" + await self.async_set_heating_mode(HVAC_MODE_TO_OVERKIZ[hvac_mode]) + + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Set new preset mode.""" + await self.async_set_heating_mode(PRESET_MODES_TO_OVERKIZ[preset_mode]) + + @property + def preset_mode(self) -> str: + """Return the current preset mode, e.g., home, away, temp.""" + heating_mode = cast( + str, self.executor.select_state(OverkizState.IO_PASS_APC_HEATING_MODE) + ) + + if heating_mode == OverkizCommandParam.INTERNAL_SCHEDULING: + # In Internal scheduling, it could be comfort or eco + return OVERKIZ_TO_PROFILE_MODES[ + cast( + str, + self.executor.select_state( + OverkizState.IO_PASS_APC_HEATING_PROFILE + ), + ) + ] + + return OVERKIZ_TO_PRESET_MODES[heating_mode] + + @property + def target_temperature(self) -> float: + """Return hvac target temperature.""" + current_heating_profile = self.current_heating_profile + if current_heating_profile in OVERKIZ_TEMPERATURE_STATE_BY_PROFILE: + return cast( + float, + self.executor.select_state( + OVERKIZ_TEMPERATURE_STATE_BY_PROFILE[current_heating_profile] + ), + ) + return cast( + float, self.executor.select_state(OverkizState.CORE_TARGET_TEMPERATURE) + ) + + async def async_set_temperature(self, **kwargs: Any) -> None: + """Set new temperature.""" + temperature = kwargs[ATTR_TEMPERATURE] + + if self.hvac_mode == HVACMode.AUTO: + await self.executor.async_execute_command( + OverkizCommand.SET_COMFORT_HEATING_TARGET_TEMPERATURE, + temperature, + ) + await self.executor.async_execute_command( + OverkizCommand.REFRESH_COMFORT_HEATING_TARGET_TEMPERATURE + ) + await self.executor.async_execute_command( + OverkizCommand.REFRESH_TARGET_TEMPERATURE + ) + else: + await self.executor.async_execute_command( + OverkizCommand.SET_DEROGATED_TARGET_TEMPERATURE, + temperature, + ) + await self.executor.async_execute_command( + OverkizCommand.SET_DEROGATION_ON_OFF_STATE, + OverkizCommandParam.ON, + ) + await self.executor.async_execute_command( + OverkizCommand.REFRESH_TARGET_TEMPERATURE + ) + await self.executor.async_execute_command( + OverkizCommand.REFRESH_PASS_APC_HEATING_MODE + ) + await self.executor.async_execute_command( + OverkizCommand.REFRESH_PASS_APC_HEATING_PROFILE + ) diff --git a/homeassistant/components/overkiz/const.py b/homeassistant/components/overkiz/const.py index d98709ba2b6..70477bbcdb2 100644 --- a/homeassistant/components/overkiz/const.py +++ b/homeassistant/components/overkiz/const.py @@ -64,6 +64,7 @@ OVERKIZ_DEVICE_TO_PLATFORM: dict[UIClass | UIWidget, Platform | None] = { UIWidget.ATLANTIC_ELECTRICAL_HEATER: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.ATLANTIC_ELECTRICAL_TOWEL_DRYER: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.ATLANTIC_HEAT_RECOVERY_VENTILATION: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) + UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.ATLANTIC_PASS_APC_ZONE_CONTROL: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.DOMESTIC_HOT_WATER_TANK: Platform.SWITCH, # widgetName, uiClass is WaterHeatingSystem (not supported) UIWidget.MY_FOX_ALARM_CONTROLLER: Platform.ALARM_CONTROL_PANEL, # widgetName, uiClass is Alarm (not supported) diff --git a/homeassistant/components/overkiz/entity.py b/homeassistant/components/overkiz/entity.py index c17f30393fc..85e5a3fdf57 100644 --- a/homeassistant/components/overkiz/entity.py +++ b/homeassistant/components/overkiz/entity.py @@ -27,7 +27,10 @@ class OverkizEntity(CoordinatorEntity[OverkizDataUpdateCoordinator]): """Initialize the device.""" super().__init__(coordinator) self.device_url = device_url - self.base_device_url, *_ = self.device_url.split("#") + split_device_url = self.device_url.split("#") + self.base_device_url = split_device_url[0] + if len(split_device_url) == 2: + self.index_device_url = split_device_url[1] self.executor = OverkizExecutor(device_url, coordinator) self._attr_assumed_state = not self.device.states From ddbfed354e2a2d878e8c730059487961f97c847f Mon Sep 17 00:00:00 2001 From: Nyro Date: Fri, 4 Nov 2022 10:42:58 +0100 Subject: [PATCH 0203/1033] Add Overkiz AtlanticPassAPCDHW (#78665) * Add Overkiz AtlanticPassAPCDHW * Remove unnecessary line * Improve atlantic pass_apcdhw for operation and target temprature * Remove async_execute_commands * Fix small code issues for Overkiz AtlanticPassAPCDHW * Update homeassistant/components/overkiz/const.py Co-authored-by: Mick Vleeshouwer * Update homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py Co-authored-by: Quentame * Update homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py Co-authored-by: Quentame * Fix small issues Co-authored-by: Mick Vleeshouwer Co-authored-by: Quentame --- .coveragerc | 2 + homeassistant/components/overkiz/const.py | 2 + .../components/overkiz/water_heater.py | 28 ++++ .../overkiz/water_heater_entities/__init__.py | 8 + .../atlantic_pass_apc_dhw.py | 145 ++++++++++++++++++ 5 files changed, 185 insertions(+) create mode 100644 homeassistant/components/overkiz/water_heater.py create mode 100644 homeassistant/components/overkiz/water_heater_entities/__init__.py create mode 100644 homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py diff --git a/.coveragerc b/.coveragerc index eb60f320a74..84f061fbd35 100644 --- a/.coveragerc +++ b/.coveragerc @@ -953,6 +953,8 @@ omit = homeassistant/components/overkiz/sensor.py homeassistant/components/overkiz/siren.py homeassistant/components/overkiz/switch.py + homeassistant/components/overkiz/water_heater.py + homeassistant/components/overkiz/water_heater_entities/* homeassistant/components/ovo_energy/__init__.py homeassistant/components/ovo_energy/const.py homeassistant/components/ovo_energy/sensor.py diff --git a/homeassistant/components/overkiz/const.py b/homeassistant/components/overkiz/const.py index 70477bbcdb2..4645b058182 100644 --- a/homeassistant/components/overkiz/const.py +++ b/homeassistant/components/overkiz/const.py @@ -32,6 +32,7 @@ PLATFORMS: list[Platform] = [ Platform.SENSOR, Platform.SIREN, Platform.SWITCH, + Platform.WATER_HEATER, ] IGNORED_OVERKIZ_DEVICES: list[UIClass | UIWidget] = [ @@ -64,6 +65,7 @@ OVERKIZ_DEVICE_TO_PLATFORM: dict[UIClass | UIWidget, Platform | None] = { UIWidget.ATLANTIC_ELECTRICAL_HEATER: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.ATLANTIC_ELECTRICAL_TOWEL_DRYER: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.ATLANTIC_HEAT_RECOVERY_VENTILATION: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) + UIWidget.ATLANTIC_PASS_APC_DHW: Platform.WATER_HEATER, # widgetName, uiClass is WaterHeatingSystem (not supported) UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.ATLANTIC_PASS_APC_ZONE_CONTROL: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.DOMESTIC_HOT_WATER_TANK: Platform.SWITCH, # widgetName, uiClass is WaterHeatingSystem (not supported) diff --git a/homeassistant/components/overkiz/water_heater.py b/homeassistant/components/overkiz/water_heater.py new file mode 100644 index 00000000000..e22f442c266 --- /dev/null +++ b/homeassistant/components/overkiz/water_heater.py @@ -0,0 +1,28 @@ +"""Support for Overkiz water heater devices.""" +from __future__ import annotations + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import HomeAssistantOverkizData +from .const import DOMAIN +from .water_heater_entities import WIDGET_TO_WATER_HEATER_ENTITY + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Overkiz DHW from a config entry.""" + data: HomeAssistantOverkizData = hass.data[DOMAIN][entry.entry_id] + + async_add_entities( + WIDGET_TO_WATER_HEATER_ENTITY[device.widget]( + device.device_url, data.coordinator + ) + for device in data.platforms[Platform.WATER_HEATER] + if device.widget in WIDGET_TO_WATER_HEATER_ENTITY + ) diff --git a/homeassistant/components/overkiz/water_heater_entities/__init__.py b/homeassistant/components/overkiz/water_heater_entities/__init__.py new file mode 100644 index 00000000000..e03585da56d --- /dev/null +++ b/homeassistant/components/overkiz/water_heater_entities/__init__.py @@ -0,0 +1,8 @@ +"""Water heater entities for the Overkiz (by Somfy) integration.""" +from pyoverkiz.enums.ui import UIWidget + +from .atlantic_pass_apc_dhw import AtlanticPassAPCDHW + +WIDGET_TO_WATER_HEATER_ENTITY = { + UIWidget.ATLANTIC_PASS_APC_DHW: AtlanticPassAPCDHW, +} diff --git a/homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py b/homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py new file mode 100644 index 00000000000..7c2ea6ff2d8 --- /dev/null +++ b/homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py @@ -0,0 +1,145 @@ +"""Support for Atlantic Pass APC DHW.""" + +from typing import Any, cast + +from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState + +from homeassistant.components.water_heater import ( + STATE_ECO, + STATE_HEAT_PUMP, + STATE_OFF, + STATE_PERFORMANCE, + WaterHeaterEntity, + WaterHeaterEntityFeature, +) +from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS + +from ..entity import OverkizEntity + + +class AtlanticPassAPCDHW(OverkizEntity, WaterHeaterEntity): + """Representation of Atlantic Pass APC DHW.""" + + _attr_temperature_unit = TEMP_CELSIUS + _attr_supported_features = ( + WaterHeaterEntityFeature.TARGET_TEMPERATURE + | WaterHeaterEntityFeature.OPERATION_MODE + | WaterHeaterEntityFeature.AWAY_MODE + ) + _attr_operation_list = [STATE_OFF, STATE_HEAT_PUMP, STATE_PERFORMANCE] + + @property + def target_temperature(self) -> float: + """Return the temperature corresponding to the PRESET.""" + if self.is_boost_mode_on: + return cast( + float, + self.executor.select_state( + OverkizState.CORE_COMFORT_TARGET_DWH_TEMPERATURE + ), + ) + + if self.is_eco_mode_on: + return cast( + float, + self.executor.select_state( + OverkizState.CORE_ECO_TARGET_DWH_TEMPERATURE + ), + ) + + return cast( + float, + self.executor.select_state(OverkizState.CORE_TARGET_DWH_TEMPERATURE), + ) + + async def async_set_temperature(self, **kwargs: Any) -> None: + """Set new temperature.""" + temperature = kwargs[ATTR_TEMPERATURE] + + if self.is_eco_mode_on: + await self.executor.async_execute_command( + OverkizCommand.SET_ECO_TARGET_DHW_TEMPERATURE, temperature + ) + await self.executor.async_execute_command( + OverkizCommand.REFRESH_ECO_TARGET_DWH_TEMPERATURE + ) + else: + await self.executor.async_execute_command( + OverkizCommand.SET_COMFORT_TARGET_DHW_TEMPERATURE, temperature + ) + await self.executor.async_execute_command( + OverkizCommand.REFRESH_COMFORT_TARGET_DWH_TEMPERATURE + ) + await self.executor.async_execute_command( + OverkizCommand.REFRESH_TARGET_DWH_TEMPERATURE + ) + + @property + def is_boost_mode_on(self) -> bool: + """Return true if boost mode is on.""" + return ( + self.executor.select_state(OverkizState.CORE_BOOST_ON_OFF) + == OverkizCommandParam.ON + ) + + @property + def is_eco_mode_on(self) -> bool: + """Return true if eco mode is on.""" + return ( + self.executor.select_state(OverkizState.IO_PASS_APCDWH_MODE) + == OverkizCommandParam.ECO + ) + + @property + def is_away_mode_on(self) -> bool: + """Return true if away mode is on.""" + return ( + self.executor.select_state(OverkizState.CORE_DWH_ON_OFF) + == OverkizCommandParam.OFF + ) + + @property + def current_operation(self) -> str: + """Return current operation.""" + if self.is_boost_mode_on: + return STATE_PERFORMANCE + if self.is_eco_mode_on: + return STATE_ECO + if self.is_away_mode_on: + return STATE_OFF + return STATE_HEAT_PUMP + + async def async_set_operation_mode(self, operation_mode: str) -> None: + """Set new operation mode.""" + boost_state = OverkizCommandParam.OFF + regular_state = OverkizCommandParam.OFF + if operation_mode == STATE_PERFORMANCE: + boost_state = OverkizCommandParam.ON + regular_state = OverkizCommandParam.ON + elif operation_mode == STATE_HEAT_PUMP: + regular_state = OverkizCommandParam.ON + + await self.executor.async_execute_command( + OverkizCommand.SET_BOOST_ON_OFF_STATE, boost_state + ) + await self.executor.async_execute_command( + OverkizCommand.SET_DHW_ON_OFF_STATE, regular_state + ) + + async def async_turn_away_mode_on(self) -> None: + """Turn away mode on.""" + await self.executor.async_execute_command( + OverkizCommand.SET_BOOST_ON_OFF_STATE, OverkizCommandParam.OFF + ) + await self.executor.async_execute_command( + OverkizCommand.SET_DHW_ON_OFF_STATE, OverkizCommandParam.OFF + ) + + async def async_turn_away_mode_off(self) -> None: + """Turn away mode off.""" + await self.executor.async_execute_command( + OverkizCommand.SET_BOOST_ON_OFF_STATE, OverkizCommandParam.OFF + ) + await self.executor.async_execute_command( + OverkizCommand.SET_DHW_ON_OFF_STATE, OverkizCommandParam.ON + ) From 2e92a0d1c2f5657ac5e7a7cd0313a7f980808505 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 4 Nov 2022 14:42:37 +0100 Subject: [PATCH 0204/1033] Bump oralb-ble to 0.10.2 (#81537) Fixes some more missing pressure mappings changelog: https://github.com/Bluetooth-Devices/oralb-ble/compare/v0.10.1...v0.10.2 --- homeassistant/components/oralb/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/oralb/manifest.json b/homeassistant/components/oralb/manifest.json index 520306aed03..ba89c73a240 100644 --- a/homeassistant/components/oralb/manifest.json +++ b/homeassistant/components/oralb/manifest.json @@ -8,7 +8,7 @@ "manufacturer_id": 220 } ], - "requirements": ["oralb-ble==0.10.1"], + "requirements": ["oralb-ble==0.10.2"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 5a866deb8a6..2515838e131 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1244,7 +1244,7 @@ openwrt-luci-rpc==1.1.11 openwrt-ubus-rpc==0.0.2 # homeassistant.components.oralb -oralb-ble==0.10.1 +oralb-ble==0.10.2 # homeassistant.components.oru oru==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c6e08474217..f5ccd0c0b06 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -889,7 +889,7 @@ open-meteo==0.2.1 openerz-api==0.1.0 # homeassistant.components.oralb -oralb-ble==0.10.1 +oralb-ble==0.10.2 # homeassistant.components.ovo_energy ovoenergy==1.2.0 From 52c80b7c5b2ae437710693c4b671675dda08851d Mon Sep 17 00:00:00 2001 From: javicalle <31999997+javicalle@users.noreply.github.com> Date: Fri, 4 Nov 2022 14:54:19 +0100 Subject: [PATCH 0205/1033] Add Tuya Backlight mode configuration (#81218) * Tuya backlight configuration * fix codespell --- .../components/zha/core/channels/general.py | 1 + homeassistant/components/zha/select.py | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index c028a6021da..de68ed7d8ef 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -352,6 +352,7 @@ class OnOffChannel(ZigbeeChannel): self.ZCL_INIT_ATTRS = ( # pylint: disable=invalid-name self.ZCL_INIT_ATTRS.copy() ) + self.ZCL_INIT_ATTRS["backlight_mode"] = True self.ZCL_INIT_ATTRS["power_on_state"] = True @property diff --git a/homeassistant/components/zha/select.py b/homeassistant/components/zha/select.py index 5ac0ec6d164..334040a2b4d 100644 --- a/homeassistant/components/zha/select.py +++ b/homeassistant/components/zha/select.py @@ -269,6 +269,26 @@ class TuyaPowerOnStateSelectEntity(ZCLEnumSelectEntity, id_suffix="power_on_stat _attr_name = "Power on state" +class TuyaBacklightMode(types.enum8): + """Tuya switch backlight mode enum.""" + + Off = 0x00 + LightWhenOn = 0x01 + LightWhenOff = 0x02 + + +@CONFIG_DIAGNOSTIC_MATCH( + channel_names=CHANNEL_ON_OFF, + models={"TS011F", "TS0121", "TS0001", "TS0002", "TS0003", "TS0004"}, +) +class TuyaBacklightModeSelectEntity(ZCLEnumSelectEntity, id_suffix="backlight_mode"): + """Representation of a ZHA backlight mode select entity.""" + + _select_attr = "backlight_mode" + _enum = TuyaBacklightMode + _attr_name = "Backlight mode" + + class MoesBacklightMode(types.enum8): """MOES switch backlight mode enum.""" From ca905a8c054fbab18b58428cab99f6e7b06ff7ad Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 4 Nov 2022 15:29:15 +0100 Subject: [PATCH 0206/1033] Bump dbus-fast to 1.71.0 (#81541) performance improvements changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v1.67.0...v1.71.0 --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 49731df2f9b..c47675f3a81 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.8.2", "bluetooth-adapters==0.6.0", "bluetooth-auto-recovery==0.3.6", - "dbus-fast==1.67.0" + "dbus-fast==1.71.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index ae7e535dbc0..7250822ec22 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.3 -dbus-fast==1.67.0 +dbus-fast==1.71.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 2515838e131..b2c40f90ab7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -546,7 +546,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.67.0 +dbus-fast==1.71.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f5ccd0c0b06..9f7f3e34bfc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -426,7 +426,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.67.0 +dbus-fast==1.71.0 # homeassistant.components.debugpy debugpy==1.6.3 From 59ec52a0798e27b015b4e04f73482a1645a514f5 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Fri, 4 Nov 2022 18:29:00 +0200 Subject: [PATCH 0207/1033] Fix Shelly Plus HT missing battery entity (#81564) --- homeassistant/components/shelly/sensor.py | 8 +------- homeassistant/components/shelly/utils.py | 7 ------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index 3ddabf7ca2b..cf1eb7508c7 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -47,12 +47,7 @@ from .entity import ( async_setup_entry_rest, async_setup_entry_rpc, ) -from .utils import ( - get_device_entry_gen, - get_device_uptime, - is_rpc_device_externally_powered, - temperature_unit, -) +from .utils import get_device_entry_gen, get_device_uptime, temperature_unit @dataclass @@ -407,7 +402,6 @@ RPC_SENSORS: Final = { value=lambda status, _: status["percent"], device_class=SensorDeviceClass.BATTERY, state_class=SensorStateClass.MEASUREMENT, - removal_condition=is_rpc_device_externally_powered, entity_registry_enabled_default=True, entity_category=EntityCategory.DIAGNOSTIC, ), diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index c3b6d24752f..a13d84d32be 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -364,13 +364,6 @@ def is_rpc_channel_type_light(config: dict[str, Any], channel: int) -> bool: return con_types is not None and con_types[channel].lower().startswith("light") -def is_rpc_device_externally_powered( - config: dict[str, Any], status: dict[str, Any], key: str -) -> bool: - """Return true if device has external power instead of battery.""" - return cast(bool, status[key]["external"]["present"]) - - def get_rpc_input_triggers(device: RpcDevice) -> list[tuple[str, str]]: """Return list of input triggers for RPC device.""" triggers = [] From 0311063c4417e70b566eef8e4156c362fec1a199 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 5 Nov 2022 00:28:23 +0000 Subject: [PATCH 0208/1033] [ci skip] Translation update --- .../components/airq/translations/bg.json | 22 +++++++++ .../components/airq/translations/ca.json | 22 +++++++++ .../components/airq/translations/de.json | 22 +++++++++ .../components/airq/translations/es.json | 22 +++++++++ .../components/airq/translations/et.json | 22 +++++++++ .../components/airq/translations/no.json | 22 +++++++++ .../components/airq/translations/pt-BR.json | 22 +++++++++ .../components/airq/translations/ru.json | 22 +++++++++ .../components/airq/translations/zh-Hant.json | 22 +++++++++ .../components/bluetooth/translations/fr.json | 3 ++ .../fireservicerota/translations/fr.json | 5 ++ .../forecast_solar/translations/de.json | 2 +- .../forecast_solar/translations/es.json | 2 +- .../forecast_solar/translations/et.json | 2 +- .../forecast_solar/translations/no.json | 2 +- .../forecast_solar/translations/pt-BR.json | 4 +- .../forecast_solar/translations/zh-Hant.json | 2 +- .../components/hassio/translations/ca.json | 48 +++++++++++++++++++ .../huawei_lte/translations/id.json | 2 +- 19 files changed, 262 insertions(+), 8 deletions(-) create mode 100644 homeassistant/components/airq/translations/bg.json create mode 100644 homeassistant/components/airq/translations/ca.json create mode 100644 homeassistant/components/airq/translations/de.json create mode 100644 homeassistant/components/airq/translations/es.json create mode 100644 homeassistant/components/airq/translations/et.json create mode 100644 homeassistant/components/airq/translations/no.json create mode 100644 homeassistant/components/airq/translations/pt-BR.json create mode 100644 homeassistant/components/airq/translations/ru.json create mode 100644 homeassistant/components/airq/translations/zh-Hant.json diff --git a/homeassistant/components/airq/translations/bg.json b/homeassistant/components/airq/translations/bg.json new file mode 100644 index 00000000000..df43ab876ba --- /dev/null +++ b/homeassistant/components/airq/translations/bg.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", + "invalid_input": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0438\u043c\u0435 \u043d\u0430 \u0445\u043e\u0441\u0442 \u0438\u043b\u0438 IP \u0430\u0434\u0440\u0435\u0441" + }, + "step": { + "user": { + "data": { + "ip_address": "IP \u0430\u0434\u0440\u0435\u0441", + "password": "\u041f\u0430\u0440\u043e\u043b\u0430" + }, + "description": "\u041f\u043e\u0441\u043e\u0447\u0435\u0442\u0435 IP \u0430\u0434\u0440\u0435\u0441\u0430 \u0438\u043b\u0438 mDNS \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0438 \u043d\u0435\u0433\u043e\u0432\u0430\u0442\u0430 \u043f\u0430\u0440\u043e\u043b\u0430", + "title": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/ca.json b/homeassistant/components/airq/translations/ca.json new file mode 100644 index 00000000000..b9784605a9b --- /dev/null +++ b/homeassistant/components/airq/translations/ca.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "invalid_input": "Nom de l'amfitri\u00f3 o adre\u00e7a IP inv\u00e0lids" + }, + "step": { + "user": { + "data": { + "ip_address": "Adre\u00e7a IP", + "password": "Contrasenya" + }, + "description": "Proporciona l'adre\u00e7a IP o el mDNS del dispositiu i la seva contrasenya", + "title": "Identificaci\u00f3 del dispositiu" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/de.json b/homeassistant/components/airq/translations/de.json new file mode 100644 index 00000000000..bccfc24be6c --- /dev/null +++ b/homeassistant/components/airq/translations/de.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "invalid_input": "Ung\u00fcltiger Hostname oder IP-Adresse" + }, + "step": { + "user": { + "data": { + "ip_address": "IP-Adresse", + "password": "Passwort" + }, + "description": "Gib die IP-Adresse oder den mDNS des Ger\u00e4ts und sein Passwort an", + "title": "Identifizieren des Ger\u00e4ts" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/es.json b/homeassistant/components/airq/translations/es.json new file mode 100644 index 00000000000..e3a543daecb --- /dev/null +++ b/homeassistant/components/airq/translations/es.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado" + }, + "error": { + "cannot_connect": "No se pudo conectar", + "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", + "invalid_input": "Nombre de host o direcci\u00f3n IP no v\u00e1lidos" + }, + "step": { + "user": { + "data": { + "ip_address": "Direcci\u00f3n IP", + "password": "Contrase\u00f1a" + }, + "description": "Proporciona la direcci\u00f3n IP o mDNS del dispositivo y su contrase\u00f1a", + "title": "Identificar el dispositivo" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/et.json b/homeassistant/components/airq/translations/et.json new file mode 100644 index 00000000000..7045d3d5278 --- /dev/null +++ b/homeassistant/components/airq/translations/et.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Seade on juba h\u00e4\u00e4lestatud" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_auth": "Tuvastamine nurjus", + "invalid_input": "Hostinimi v\u00f5i IP aadress vigane" + }, + "step": { + "user": { + "data": { + "ip_address": "IP aadress", + "password": "Salas\u00f5na" + }, + "description": "Sisesta seadme IP-aadress v\u00f5i mDNS ja parool", + "title": "Seadme tuvastamine" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/no.json b/homeassistant/components/airq/translations/no.json new file mode 100644 index 00000000000..00f07077294 --- /dev/null +++ b/homeassistant/components/airq/translations/no.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten er allerede konfigurert" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_auth": "Ugyldig godkjenning", + "invalid_input": "Ugyldig vertsnavn eller IP-adresse" + }, + "step": { + "user": { + "data": { + "ip_address": "IP adresse", + "password": "Passord" + }, + "description": "Oppgi IP-adressen eller mDNS til enheten og passordet", + "title": "Identifiser enheten" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/pt-BR.json b/homeassistant/components/airq/translations/pt-BR.json new file mode 100644 index 00000000000..bf556a86b0d --- /dev/null +++ b/homeassistant/components/airq/translations/pt-BR.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + }, + "error": { + "cannot_connect": "Falhou ao conectar", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "invalid_input": "Nome do host ou endere\u00e7o IP inv\u00e1lido" + }, + "step": { + "user": { + "data": { + "ip_address": "Endere\u00e7o IP", + "password": "Senha" + }, + "description": "Forne\u00e7a o endere\u00e7o IP ou mDNS do dispositivo e sua senha", + "title": "Identifique o dispositivo" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/ru.json b/homeassistant/components/airq/translations/ru.json new file mode 100644 index 00000000000..416787acc7f --- /dev/null +++ b/homeassistant/components/airq/translations/ru.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "invalid_input": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." + }, + "step": { + "user": { + "data": { + "ip_address": "IP-\u0430\u0434\u0440\u0435\u0441", + "password": "\u041f\u0430\u0440\u043e\u043b\u044c" + }, + "description": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 mDNS \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438 \u0435\u0433\u043e \u043f\u0430\u0440\u043e\u043b\u044c.", + "title": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/zh-Hant.json b/homeassistant/components/airq/translations/zh-Hant.json new file mode 100644 index 00000000000..db4d2d8fcf0 --- /dev/null +++ b/homeassistant/components/airq/translations/zh-Hant.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "invalid_input": "\u7121\u6548\u4e3b\u6a5f\u540d\u7a31\u6216 IP \u4f4d\u5740" + }, + "step": { + "user": { + "data": { + "ip_address": "IP \u4f4d\u5740", + "password": "\u5bc6\u78bc" + }, + "description": "\u63d0\u4f9b\u88dd\u7f6e\u4e4b IP \u4f4d\u5740\u6216 mDNS \u53ca\u5bc6\u78bc", + "title": "\u8b58\u5225\u88dd\u7f6e" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/bluetooth/translations/fr.json b/homeassistant/components/bluetooth/translations/fr.json index c7a1155b216..025b00bb0cf 100644 --- a/homeassistant/components/bluetooth/translations/fr.json +++ b/homeassistant/components/bluetooth/translations/fr.json @@ -17,6 +17,9 @@ }, "description": "S\u00e9lectionner un adaptateur Bluetooth \u00e0 configurer" }, + "single_adapter": { + "description": "Voulez-vous configurer l\u2019adaptateur Bluetooth {name}\u00a0?" + }, "user": { "data": { "address": "Appareil" diff --git a/homeassistant/components/fireservicerota/translations/fr.json b/homeassistant/components/fireservicerota/translations/fr.json index bf663e8ad90..835af421158 100644 --- a/homeassistant/components/fireservicerota/translations/fr.json +++ b/homeassistant/components/fireservicerota/translations/fr.json @@ -17,6 +17,11 @@ }, "description": "Les jetons d'authentification ne sont plus valides, connectez-vous pour les recr\u00e9er." }, + "reauth_confirm": { + "data": { + "password": "Mot de passe" + } + }, "user": { "data": { "password": "Mot de passe", diff --git a/homeassistant/components/forecast_solar/translations/de.json b/homeassistant/components/forecast_solar/translations/de.json index 06e51e04659..4e683878c11 100644 --- a/homeassistant/components/forecast_solar/translations/de.json +++ b/homeassistant/components/forecast_solar/translations/de.json @@ -25,7 +25,7 @@ "inverter_size": "Wechselrichtergr\u00f6\u00dfe (Watt)", "modules power": "Gesamt-Watt-Spitzenleistung deiner Solarmodule" }, - "description": "Mit diesen Werten kann das Solar.Forecast-Ergebnis angepasst werden. Wenn ein Feld unklar ist, lies bitte in der Dokumentation nach." + "description": "Mit diesen Werten kann das Ergebnis von Forecast.Solar angepasst werden. Wenn ein Feld unklar ist, lies bitte in der Dokumentation nach." } } } diff --git a/homeassistant/components/forecast_solar/translations/es.json b/homeassistant/components/forecast_solar/translations/es.json index 08e67ec95d1..a2f08dbc489 100644 --- a/homeassistant/components/forecast_solar/translations/es.json +++ b/homeassistant/components/forecast_solar/translations/es.json @@ -25,7 +25,7 @@ "inverter_size": "Tama\u00f1o del inversor (vatios)", "modules power": "Potencia pico total en vatios de tus m\u00f3dulos solares" }, - "description": "Estos valores permiten modificar el resultado de Solar.Forecast. Por favor, consulta la documentaci\u00f3n si un campo no est\u00e1 claro." + "description": "Estos valores permiten modificar el resultado de Forecast.Solar. Por favor, consulta la documentaci\u00f3n si un campo no est\u00e1 claro." } } } diff --git a/homeassistant/components/forecast_solar/translations/et.json b/homeassistant/components/forecast_solar/translations/et.json index d2b72b30708..3a3b3f4c689 100644 --- a/homeassistant/components/forecast_solar/translations/et.json +++ b/homeassistant/components/forecast_solar/translations/et.json @@ -25,7 +25,7 @@ "inverter_size": "Inverteri v\u00f5imsus (vatti)", "modules power": "P\u00e4ikesemoodulite koguv\u00f5imsus vattides" }, - "description": "Need v\u00e4\u00e4rtused v\u00f5imaldavad muuta Solar.Forecast tulemust. Vaata dokumentatsiooni kui asi on ebaselge." + "description": "Need v\u00e4\u00e4rtused v\u00f5imaldavad muuta Solar.Forecast tulemust. Vaata dokumentatsiooni kui see v\u00e4li on ebaselge." } } } diff --git a/homeassistant/components/forecast_solar/translations/no.json b/homeassistant/components/forecast_solar/translations/no.json index a9acbb86f00..6b3a58574a8 100644 --- a/homeassistant/components/forecast_solar/translations/no.json +++ b/homeassistant/components/forecast_solar/translations/no.json @@ -25,7 +25,7 @@ "inverter_size": "Inverterst\u00f8rrelse (Watt)", "modules power": "Total Watt-toppeffekt i solcellemodulene dine" }, - "description": "Disse verdiene tillater justering av Solar.Forecast -resultatet. Se dokumentasjonen hvis et felt er uklart." + "description": "Disse verdiene gj\u00f8r det mulig \u00e5 justere Forecast.Solar-resultatet. Vennligst se dokumentasjonen hvis et felt er uklart." } } } diff --git a/homeassistant/components/forecast_solar/translations/pt-BR.json b/homeassistant/components/forecast_solar/translations/pt-BR.json index 6761e17e8bd..a6be7f053e1 100644 --- a/homeassistant/components/forecast_solar/translations/pt-BR.json +++ b/homeassistant/components/forecast_solar/translations/pt-BR.json @@ -10,7 +10,7 @@ "modules power": "Pot\u00eancia de pico total em Watt de seus m\u00f3dulos solares", "name": "Nome" }, - "description": "Preencha os dados de seus pain\u00e9is solares. Consulte a documenta\u00e7\u00e3o se um campo n\u00e3o estiver claro." + "description": "Esses valores permitem ajustar o resultado do Forecast.Solar. Consulte a documenta\u00e7\u00e3o se um campo n\u00e3o estiver claro." } } }, @@ -25,7 +25,7 @@ "inverter_size": "Pot\u00eancia do inversor (Watt)", "modules power": "Pot\u00eancia de pico total em Watt de seus m\u00f3dulos solares" }, - "description": "Preencha os dados de seus pain\u00e9is solares. Consulte a documenta\u00e7\u00e3o se um campo n\u00e3o estiver claro." + "description": "Esses valores permitem ajustar o resultado do Forecast.Solar. Consulte a documenta\u00e7\u00e3o se um campo n\u00e3o estiver claro." } } } diff --git a/homeassistant/components/forecast_solar/translations/zh-Hant.json b/homeassistant/components/forecast_solar/translations/zh-Hant.json index 3870ca29846..ede97887887 100644 --- a/homeassistant/components/forecast_solar/translations/zh-Hant.json +++ b/homeassistant/components/forecast_solar/translations/zh-Hant.json @@ -25,7 +25,7 @@ "inverter_size": "\u8b8a\u6d41\u5668\u5c3a\u5bf8\uff08Watt\uff09", "modules power": "\u7e3d\u5cf0\u503c\u529f\u7387" }, - "description": "\u6b64\u4e9b\u6578\u503c\u5141\u8a31\u5fae\u8abf Solar.Forecast \u7d50\u679c\u3002\u5982\u679c\u6709\u4e0d\u6e05\u695a\u7684\u5730\u65b9\u3001\u8acb\u53c3\u8003\u6587\u4ef6\u8aaa\u660e\u3002" + "description": "\u6b64\u4e9b\u6578\u503c\u5141\u8a31\u5fae\u8abf Forecast.Solar \u7d50\u679c\u3002\u5982\u679c\u6709\u4e0d\u6e05\u695a\u7684\u5730\u65b9\u3001\u8acb\u53c3\u8003\u6587\u4ef6\u8aaa\u660e\u3002" } } } diff --git a/homeassistant/components/hassio/translations/ca.json b/homeassistant/components/hassio/translations/ca.json index 40a1c9435aa..13e928e998b 100644 --- a/homeassistant/components/hassio/translations/ca.json +++ b/homeassistant/components/hassio/translations/ca.json @@ -43,6 +43,54 @@ "unsupported_docker_version": { "description": "El sistema no \u00e9s compatible perqu\u00e8 s'utilitza una versi\u00f3 de Docker incorrecta. Clica l'enlla\u00e7 per con\u00e8ixer la versi\u00f3 correcta i sobre com solucionar-ho.", "title": "Sistema no compatible - Versi\u00f3 de Docker" + }, + "unsupported_job_conditions": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 una o m\u00e9s condicions de treball ('job conditions'), que protegeixen de fallades i errors inesperats, s'han desactivat. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Proteccions desactivades" + }, + "unsupported_lxc": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 s'est\u00e0 executant en una m\u00e0quina virtual LXC. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - LXC detectat" + }, + "unsupported_network_manager": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 el gestor de xarxa ('Network Manager') no est\u00e0 instal\u00b7lat, no est\u00e0 activat o no est\u00e0 ben configurat. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Problemes amb el gestor de xarxa" + }, + "unsupported_os": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 el sistema operatiu utilitzat no s'ha provat o no est\u00e0 fet per utilitzar-se amb el Supervisor. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre els sitemes operatius compatibles i com solucionar-ho.", + "title": "Sistema no compatible - Sistema Operatiu" + }, + "unsupported_os_agent": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 OS-Agent no est\u00e0 instal\u00b7lat, no est\u00e0 activat o no est\u00e0 ben configurat. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Problemes amb OS-Agent" + }, + "unsupported_restart_policy": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 un contenidor Docker t\u00e9 una pol\u00edtica de reinici que podria provocar problemes en l'arrencada. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Pol\u00edtica de reinici dels contenidors" + }, + "unsupported_software": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 s'ha detectat programari extern al sistema Home Assistant. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Programari no compatible" + }, + "unsupported_source_mods": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 s'ha modificat el codi font del Supervisor. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Modificaci\u00f3 de codi font del Supervisor" + }, + "unsupported_supervisor_version": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 s'utilitza una versi\u00f3 del Supervisor obsoleta i l'actualitzaci\u00f3 autom\u00e0tica est\u00e0 desactivada. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Versi\u00f3 del Supervisor" + }, + "unsupported_systemd": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 Systemd no est\u00e0 instal\u00b7lat, no est\u00e0 activat o no est\u00e0 ben configurat. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Problemes amb Systemd" + }, + "unsupported_systemd_journal": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 Systemd Journal i/o el servei d'enlla\u00e7 ('gateway') no est\u00e0 instal\u00b7lat, no est\u00e0 activat o no est\u00e0 ben configurat. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Problemes amb Systemd Journal" + }, + "unsupported_systemd_resolved": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 Systemd Resolved no est\u00e0 instal\u00b7lat, no est\u00e0 activat o no est\u00e0 ben configurat. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Problemes amb Systemd-Resolved" } }, "system_health": { diff --git a/homeassistant/components/huawei_lte/translations/id.json b/homeassistant/components/huawei_lte/translations/id.json index 5bb08d626d0..d87b2bba339 100644 --- a/homeassistant/components/huawei_lte/translations/id.json +++ b/homeassistant/components/huawei_lte/translations/id.json @@ -39,7 +39,7 @@ "step": { "init": { "data": { - "name": "Nama layanan notifikasi (perubahan harus dimulai ulang)", + "name": "Nama layanan notifikasi (perubahan membutuhkan proses mulai ulang)", "recipient": "Penerima notifikasi SMS", "track_wired_clients": "Lacak klien jaringan kabel", "unauthenticated_mode": "Mode tidak diautentikasi (perubahan memerlukan pemuatan ulang)" From 9a747bafa398185eb3d4fe041c52acfbb8264372 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Sat, 5 Nov 2022 05:33:56 -0400 Subject: [PATCH 0209/1033] Use enums instead of deprecated constants (#81591) --- .../components/eight_sleep/sensor.py | 6 +- homeassistant/components/tomorrowio/sensor.py | 38 +++++----- .../components/tomorrowio/weather.py | 19 +++-- homeassistant/components/zwave_js/climate.py | 15 ++-- .../zwave_js/discovery_data_template.py | 71 ++++++++----------- 5 files changed, 67 insertions(+), 82 deletions(-) diff --git a/homeassistant/components/eight_sleep/sensor.py b/homeassistant/components/eight_sleep/sensor.py index b07865d8591..7cce1293707 100644 --- a/homeassistant/components/eight_sleep/sensor.py +++ b/homeassistant/components/eight_sleep/sensor.py @@ -9,7 +9,7 @@ import voluptuous as vol from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_platform as ep from homeassistant.helpers.update_coordinator import DataUpdateCoordinator @@ -175,7 +175,7 @@ class EightUserSensor(EightSleepBaseEntity, SensorEntity): if self._sensor == "bed_temperature": self._attr_icon = "mdi:thermometer" self._attr_device_class = SensorDeviceClass.TEMPERATURE - self._attr_native_unit_of_measurement = TEMP_CELSIUS + self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS elif self._sensor in ("current_sleep", "last_sleep", "current_sleep_fitness"): self._attr_native_unit_of_measurement = "Score" @@ -272,7 +272,7 @@ class EightRoomSensor(EightSleepBaseEntity, SensorEntity): _attr_icon = "mdi:thermometer" _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS def __init__( self, diff --git a/homeassistant/components/tomorrowio/sensor.py b/homeassistant/components/tomorrowio/sensor.py index 07b922e72ed..a174d983131 100644 --- a/homeassistant/components/tomorrowio/sensor.py +++ b/homeassistant/components/tomorrowio/sensor.py @@ -26,13 +26,11 @@ from homeassistant.const import ( CONF_NAME, IRRADIATION_BTUS_PER_HOUR_SQUARE_FOOT, IRRADIATION_WATTS_PER_SQUARE_METER, - LENGTH_KILOMETERS, - LENGTH_MILES, PERCENTAGE, - PRESSURE_HPA, - SPEED_METERS_PER_SECOND, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, + UnitOfLength, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -103,20 +101,20 @@ SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key=TMRW_ATTR_FEELS_LIKE, name="Feels Like", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), TomorrowioSensorEntityDescription( key=TMRW_ATTR_DEW_POINT, name="Dew Point", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), # Data comes in as hPa TomorrowioSensorEntityDescription( key=TMRW_ATTR_PRESSURE_SURFACE_LEVEL, name="Pressure (Surface Level)", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, device_class=SensorDeviceClass.PRESSURE, ), # Data comes in as W/m^2, convert to BTUs/(hr * ft^2) for imperial @@ -132,20 +130,24 @@ SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key=TMRW_ATTR_CLOUD_BASE, name="Cloud Base", - unit_imperial=LENGTH_MILES, - unit_metric=LENGTH_KILOMETERS, + unit_imperial=UnitOfLength.MILES, + unit_metric=UnitOfLength.KILOMETERS, imperial_conversion=lambda val: DistanceConverter.convert( - val, LENGTH_KILOMETERS, LENGTH_MILES + val, + UnitOfLength.KILOMETERS, + UnitOfLength.MILES, ), ), # Data comes in as km, convert to miles for imperial TomorrowioSensorEntityDescription( key=TMRW_ATTR_CLOUD_CEILING, name="Cloud Ceiling", - unit_imperial=LENGTH_MILES, - unit_metric=LENGTH_KILOMETERS, + unit_imperial=UnitOfLength.MILES, + unit_metric=UnitOfLength.KILOMETERS, imperial_conversion=lambda val: DistanceConverter.convert( - val, LENGTH_KILOMETERS, LENGTH_MILES + val, + UnitOfLength.KILOMETERS, + UnitOfLength.MILES, ), ), TomorrowioSensorEntityDescription( @@ -157,10 +159,10 @@ SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key=TMRW_ATTR_WIND_GUST, name="Wind Gust", - unit_imperial=SPEED_MILES_PER_HOUR, - unit_metric=SPEED_METERS_PER_SECOND, + unit_imperial=UnitOfSpeed.MILES_PER_HOUR, + unit_metric=UnitOfSpeed.METERS_PER_SECOND, imperial_conversion=lambda val: SpeedConverter.convert( - val, SPEED_METERS_PER_SECOND, SPEED_MILES_PER_HOUR + val, UnitOfSpeed.METERS_PER_SECOND, UnitOfSpeed.MILES_PER_HOUR ), ), TomorrowioSensorEntityDescription( diff --git a/homeassistant/components/tomorrowio/weather.py b/homeassistant/components/tomorrowio/weather.py index 07ea079b1ce..1acc42e900b 100644 --- a/homeassistant/components/tomorrowio/weather.py +++ b/homeassistant/components/tomorrowio/weather.py @@ -21,11 +21,10 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_API_KEY, CONF_NAME, - LENGTH_KILOMETERS, - LENGTH_MILLIMETERS, - PRESSURE_HPA, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, + UnitOfLength, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -74,11 +73,11 @@ async def async_setup_entry( class TomorrowioWeatherEntity(TomorrowioEntity, WeatherEntity): """Entity that talks to Tomorrow.io v4 API to retrieve weather data.""" - _attr_native_precipitation_unit = LENGTH_MILLIMETERS - _attr_native_pressure_unit = PRESSURE_HPA - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_visibility_unit = LENGTH_KILOMETERS - _attr_native_wind_speed_unit = SPEED_METERS_PER_SECOND + _attr_native_precipitation_unit = UnitOfLength.MILLIMETERS + _attr_native_pressure_unit = UnitOfPressure.HPA + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_visibility_unit = UnitOfLength.KILOMETERS + _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND def __init__( self, diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py index 6cbb1ea3016..06f2d225742 100644 --- a/homeassistant/components/zwave_js/climate.py +++ b/homeassistant/components/zwave_js/climate.py @@ -34,12 +34,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_TEMPERATURE, - PRECISION_TENTHS, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, -) +from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -260,8 +255,8 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): and self._unit_value.metadata.unit and "f" in self._unit_value.metadata.unit.lower() ): - return TEMP_FAHRENHEIT - return TEMP_CELSIUS + return UnitOfTemperature.FAHRENHEIT + return UnitOfTemperature.CELSIUS @property def hvac_mode(self) -> HVACMode: @@ -398,7 +393,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): def min_temp(self) -> float: """Return the minimum temperature.""" min_temp = DEFAULT_MIN_TEMP - base_unit = TEMP_CELSIUS + base_unit: str = UnitOfTemperature.CELSIUS try: temp = self._setpoint_value_or_raise(self._current_mode_setpoint_enums[0]) if temp.metadata.min: @@ -414,7 +409,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): def max_temp(self) -> float: """Return the maximum temperature.""" max_temp = DEFAULT_MAX_TEMP - base_unit = TEMP_CELSIUS + base_unit: str = UnitOfTemperature.CELSIUS try: temp = self._setpoint_value_or_raise(self._current_mode_setpoint_enums[0]) if temp.metadata.max: diff --git a/homeassistant/components/zwave_js/discovery_data_template.py b/homeassistant/components/zwave_js/discovery_data_template.py index 5b20572c2d8..0ee7c3d758e 100644 --- a/homeassistant/components/zwave_js/discovery_data_template.py +++ b/homeassistant/components/zwave_js/discovery_data_template.py @@ -96,35 +96,24 @@ from homeassistant.const import ( ELECTRIC_CURRENT_MILLIAMPERE, ELECTRIC_POTENTIAL_MILLIVOLT, ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, FREQUENCY_HERTZ, FREQUENCY_KILOHERTZ, IRRADIATION_WATTS_PER_SQUARE_METER, - LENGTH_CENTIMETERS, - LENGTH_FEET, - LENGTH_METERS, LIGHT_LUX, - MASS_KILOGRAMS, - MASS_POUNDS, PERCENTAGE, - POWER_BTU_PER_HOUR, - POWER_WATT, - PRESSURE_INHG, - PRESSURE_MMHG, - PRESSURE_PSI, SIGNAL_STRENGTH_DECIBELS, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - SPEED_METERS_PER_SECOND, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, TIME_SECONDS, - VOLUME_CUBIC_FEET, - VOLUME_CUBIC_METERS, VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE, VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, - VOLUME_GALLONS, - VOLUME_LITERS, + UnitOfEnergy, + UnitOfLength, + UnitOfMass, + UnitOfPower, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, + UnitOfVolume, UnitOfVolumetricFlux, ) @@ -173,21 +162,21 @@ MULTILEVEL_SENSOR_DEVICE_CLASS_MAP: dict[str, set[MultilevelSensorType]] = { METER_UNIT_MAP: dict[str, set[MeterScaleType]] = { ELECTRIC_CURRENT_AMPERE: METER_UNIT_AMPERE, - VOLUME_CUBIC_FEET: UNIT_CUBIC_FEET, - VOLUME_CUBIC_METERS: METER_UNIT_CUBIC_METER, - VOLUME_GALLONS: UNIT_US_GALLON, - ENERGY_KILO_WATT_HOUR: UNIT_KILOWATT_HOUR, + UnitOfVolume.CUBIC_FEET: UNIT_CUBIC_FEET, + UnitOfVolume.CUBIC_METERS: METER_UNIT_CUBIC_METER, + UnitOfVolume.GALLONS: UNIT_US_GALLON, + UnitOfEnergy.KILO_WATT_HOUR: UNIT_KILOWATT_HOUR, ELECTRIC_POTENTIAL_VOLT: METER_UNIT_VOLT, - POWER_WATT: METER_UNIT_WATT, + UnitOfPower.WATT: METER_UNIT_WATT, } MULTILEVEL_SENSOR_UNIT_MAP: dict[str, set[MultilevelSensorScaleType]] = { ELECTRIC_CURRENT_AMPERE: SENSOR_UNIT_AMPERE, - POWER_BTU_PER_HOUR: UNIT_BTU_H, - TEMP_CELSIUS: UNIT_CELSIUS, - LENGTH_CENTIMETERS: UNIT_CENTIMETER, + UnitOfPower.BTU_PER_HOUR: UNIT_BTU_H, + UnitOfTemperature.CELSIUS: UNIT_CELSIUS, + UnitOfLength.CENTIMETERS: UNIT_CENTIMETER, VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE: UNIT_CUBIC_FEET_PER_MINUTE, - VOLUME_CUBIC_METERS: SENSOR_UNIT_CUBIC_METER, + UnitOfVolume.CUBIC_METERS: SENSOR_UNIT_CUBIC_METER, VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR: UNIT_CUBIC_METER_PER_HOUR, SIGNAL_STRENGTH_DECIBELS: UNIT_DECIBEL, DEGREE: UNIT_DEGREES, @@ -195,31 +184,31 @@ MULTILEVEL_SENSOR_UNIT_MAP: dict[str, set[MultilevelSensorScaleType]] = { *UNIT_DENSITY, *UNIT_MICROGRAM_PER_CUBIC_METER, }, - TEMP_FAHRENHEIT: UNIT_FAHRENHEIT, - LENGTH_FEET: UNIT_FEET, - VOLUME_GALLONS: UNIT_GALLONS, + UnitOfTemperature.FAHRENHEIT: UNIT_FAHRENHEIT, + UnitOfLength.FEET: UNIT_FEET, + UnitOfVolume.GALLONS: UNIT_GALLONS, FREQUENCY_HERTZ: UNIT_HERTZ, - PRESSURE_INHG: UNIT_INCHES_OF_MERCURY, + UnitOfPressure.INHG: UNIT_INCHES_OF_MERCURY, UnitOfVolumetricFlux.INCHES_PER_HOUR: UNIT_INCHES_PER_HOUR, - MASS_KILOGRAMS: UNIT_KILOGRAM, + UnitOfMass.KILOGRAMS: UNIT_KILOGRAM, FREQUENCY_KILOHERTZ: UNIT_KILOHERTZ, - VOLUME_LITERS: UNIT_LITER, + UnitOfVolume.LITERS: UNIT_LITER, LIGHT_LUX: UNIT_LUX, - LENGTH_METERS: UNIT_METER, + UnitOfLength.METERS: UNIT_METER, ELECTRIC_CURRENT_MILLIAMPERE: UNIT_MILLIAMPERE, UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR: UNIT_MILLIMETER_HOUR, ELECTRIC_POTENTIAL_MILLIVOLT: UNIT_MILLIVOLT, - SPEED_MILES_PER_HOUR: UNIT_MPH, - SPEED_METERS_PER_SECOND: UNIT_M_S, + UnitOfSpeed.MILES_PER_HOUR: UNIT_MPH, + UnitOfSpeed.METERS_PER_SECOND: UNIT_M_S, CONCENTRATION_PARTS_PER_MILLION: UNIT_PARTS_MILLION, PERCENTAGE: {*UNIT_PERCENTAGE_VALUE, *UNIT_RSSI}, - MASS_POUNDS: UNIT_POUNDS, - PRESSURE_PSI: UNIT_POUND_PER_SQUARE_INCH, + UnitOfMass.POUNDS: UNIT_POUNDS, + UnitOfPressure.PSI: UNIT_POUND_PER_SQUARE_INCH, SIGNAL_STRENGTH_DECIBELS_MILLIWATT: UNIT_POWER_LEVEL, TIME_SECONDS: UNIT_SECOND, - PRESSURE_MMHG: UNIT_SYSTOLIC, + UnitOfPressure.MMHG: UNIT_SYSTOLIC, ELECTRIC_POTENTIAL_VOLT: SENSOR_UNIT_VOLT, - POWER_WATT: SENSOR_UNIT_WATT, + UnitOfPower.WATT: SENSOR_UNIT_WATT, IRRADIATION_WATTS_PER_SQUARE_METER: UNIT_WATT_PER_SQUARE_METER, } From 089c4a7da2a955b177015d3189231736d9fb89cb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 5 Nov 2022 04:44:05 -0500 Subject: [PATCH 0210/1033] Bump bluetooth-adapters to 0.7.0 (#81576) changelog: https://github.com/Bluetooth-Devices/bluetooth-adapters/releases/tag/v0.7.0 --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index c47675f3a81..0b94e1bdfd9 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -8,7 +8,7 @@ "requirements": [ "bleak==0.19.1", "bleak-retry-connector==2.8.2", - "bluetooth-adapters==0.6.0", + "bluetooth-adapters==0.7.0", "bluetooth-auto-recovery==0.3.6", "dbus-fast==1.71.0" ], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 7250822ec22..b5b7350d4db 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ awesomeversion==22.9.0 bcrypt==3.1.7 bleak-retry-connector==2.8.2 bleak==0.19.1 -bluetooth-adapters==0.6.0 +bluetooth-adapters==0.7.0 bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index b2c40f90ab7..660366902ed 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -444,7 +444,7 @@ bluemaestro-ble==0.2.0 # bluepy==1.3.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.6.0 +bluetooth-adapters==0.7.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.3.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9f7f3e34bfc..36710491d15 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -358,7 +358,7 @@ blinkpy==0.19.2 bluemaestro-ble==0.2.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.6.0 +bluetooth-adapters==0.7.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.3.6 From 64a508be7bc5034d5786caea0238512d0599e6e4 Mon Sep 17 00:00:00 2001 From: Malte Franken Date: Sat, 5 Nov 2022 21:05:19 +1100 Subject: [PATCH 0211/1033] Add integration_type to geonetnz_quakes (#81548) define integration type --- homeassistant/components/geonetnz_quakes/manifest.json | 3 ++- homeassistant/generated/integrations.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/geonetnz_quakes/manifest.json b/homeassistant/components/geonetnz_quakes/manifest.json index ba8eecc4ae9..dd62682e304 100644 --- a/homeassistant/components/geonetnz_quakes/manifest.json +++ b/homeassistant/components/geonetnz_quakes/manifest.json @@ -7,5 +7,6 @@ "codeowners": ["@exxamalte"], "quality_scale": "platinum", "iot_class": "cloud_polling", - "loggers": ["aio_geojson_geonetnz_quakes"] + "loggers": ["aio_geojson_geonetnz_quakes"], + "integration_type": "service" } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 4d1b9da50af..55a330685d5 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -1835,7 +1835,7 @@ "name": "GeoNet", "integrations": { "geonetnz_quakes": { - "integration_type": "hub", + "integration_type": "service", "config_flow": true, "iot_class": "cloud_polling", "name": "GeoNet NZ Quakes" From 83c6a7e18b1b0e4d5a302e304f117dee11d3aa51 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Sat, 5 Nov 2022 08:40:28 -0400 Subject: [PATCH 0212/1033] Fix invalid min and max color temp in bad ZHA light devices (#81604) * Fix ZHA default color temps * update test --- .../components/zha/core/channels/lighting.py | 18 ++++++++++++++++-- tests/components/zha/test_light.py | 6 +++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/core/channels/lighting.py b/homeassistant/components/zha/core/channels/lighting.py index e70eea11a87..ffbbc32a7a5 100644 --- a/homeassistant/components/zha/core/channels/lighting.py +++ b/homeassistant/components/zha/core/channels/lighting.py @@ -98,12 +98,26 @@ class ColorChannel(ZigbeeChannel): @property def min_mireds(self) -> int: """Return the coldest color_temp that this channel supports.""" - return self.cluster.get("color_temp_physical_min", self.MIN_MIREDS) + min_mireds = self.cluster.get("color_temp_physical_min", self.MIN_MIREDS) + if min_mireds == 0: + self.warning( + "[Min mireds is 0, setting to %s] Please open an issue on the quirks repo to have this device corrected", + self.MIN_MIREDS, + ) + min_mireds = self.MIN_MIREDS + return min_mireds @property def max_mireds(self) -> int: """Return the warmest color_temp that this channel supports.""" - return self.cluster.get("color_temp_physical_max", self.MAX_MIREDS) + max_mireds = self.cluster.get("color_temp_physical_max", self.MAX_MIREDS) + if max_mireds == 0: + self.warning( + "[Max mireds is 0, setting to %s] Please open an issue on the quirks repo to have this device corrected", + self.MAX_MIREDS, + ) + max_mireds = self.MAX_MIREDS + return max_mireds @property def hs_supported(self) -> bool: diff --git a/tests/components/zha/test_light.py b/tests/components/zha/test_light.py index a9b8c7a14ee..d40605f81dc 100644 --- a/tests/components/zha/test_light.py +++ b/tests/components/zha/test_light.py @@ -242,7 +242,9 @@ async def eWeLink_light(hass, zigpy_device_mock, zha_device_joined): color_cluster = zigpy_device.endpoints[1].light_color color_cluster.PLUGGED_ATTR_READS = { "color_capabilities": lighting.Color.ColorCapabilities.Color_temperature - | lighting.Color.ColorCapabilities.XY_attributes + | lighting.Color.ColorCapabilities.XY_attributes, + "color_temp_physical_min": 0, + "color_temp_physical_max": 0, } zha_device = await zha_device_joined(zigpy_device) zha_device.available = True @@ -1192,6 +1194,8 @@ async def test_transitions( assert eWeLink_state.state == STATE_ON assert eWeLink_state.attributes["color_temp"] == 235 assert eWeLink_state.attributes["color_mode"] == ColorMode.COLOR_TEMP + assert eWeLink_state.attributes["min_mireds"] == 153 + assert eWeLink_state.attributes["max_mireds"] == 500 async def async_test_on_off_from_light(hass, cluster, entity_id): From b313f3794692fd5edd97e5637efabb1efeeff14d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 5 Nov 2022 09:55:43 -0500 Subject: [PATCH 0213/1033] Bump nexia to 2.0.6 (#81474) * Bump nexia to 2.0.6 - Marks thermostat unavailable when it is offline * is property --- homeassistant/components/nexia/entity.py | 5 +++++ homeassistant/components/nexia/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nexia/entity.py b/homeassistant/components/nexia/entity.py index 4f806d03eda..6b017db4d34 100644 --- a/homeassistant/components/nexia/entity.py +++ b/homeassistant/components/nexia/entity.py @@ -80,6 +80,11 @@ class NexiaThermostatEntity(NexiaEntity): self.hass, f"{SIGNAL_THERMOSTAT_UPDATE}-{self._thermostat.thermostat_id}" ) + @property + def available(self) -> bool: + """Return True if thermostat is available and data is available.""" + return super().available and self._thermostat.is_online + class NexiaThermostatZoneEntity(NexiaThermostatEntity): """Base class for nexia devices attached to a thermostat.""" diff --git a/homeassistant/components/nexia/manifest.json b/homeassistant/components/nexia/manifest.json index 78576e06b8a..99eb7c14798 100644 --- a/homeassistant/components/nexia/manifest.json +++ b/homeassistant/components/nexia/manifest.json @@ -1,7 +1,7 @@ { "domain": "nexia", "name": "Nexia/American Standard/Trane", - "requirements": ["nexia==2.0.5"], + "requirements": ["nexia==2.0.6"], "codeowners": ["@bdraco"], "documentation": "https://www.home-assistant.io/integrations/nexia", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 660366902ed..156a47e9731 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1141,7 +1141,7 @@ nettigo-air-monitor==1.5.0 neurio==0.3.1 # homeassistant.components.nexia -nexia==2.0.5 +nexia==2.0.6 # homeassistant.components.nextcloud nextcloudmonitor==1.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 36710491d15..a8e30ddc07c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -831,7 +831,7 @@ netmap==0.7.0.2 nettigo-air-monitor==1.5.0 # homeassistant.components.nexia -nexia==2.0.5 +nexia==2.0.6 # homeassistant.components.discord nextcord==2.0.0a8 From 883ac12bcbf252c9ee64afd3e78472fe7ae60b27 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 5 Nov 2022 09:57:32 -0500 Subject: [PATCH 0214/1033] Add additional coverage for adding multiple elkm1 instances (#81528) * Add additional coverage for adding multiple elkm1 instances * fix copy error --- tests/components/elkm1/test_config_flow.py | 137 +++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/tests/components/elkm1/test_config_flow.py b/tests/components/elkm1/test_config_flow.py index e47dc402b64..7ce0e2163ac 100644 --- a/tests/components/elkm1/test_config_flow.py +++ b/tests/components/elkm1/test_config_flow.py @@ -2,6 +2,7 @@ from dataclasses import asdict from unittest.mock import patch +from elkm1_lib.discovery import ElkSystem import pytest from homeassistant import config_entries @@ -1317,3 +1318,139 @@ async def test_discovered_by_dhcp_no_udp_response(hass): assert result["type"] == FlowResultType.ABORT assert result["reason"] == "cannot_connect" + + +async def test_multiple_instances_with_discovery(hass): + """Test we can setup a secure elk.""" + + elk_discovery_1 = ElkSystem("aa:bb:cc:dd:ee:ff", "127.0.0.1", 2601) + elk_discovery_2 = ElkSystem("aa:bb:cc:dd:ee:fe", "127.0.0.2", 2601) + + with _patch_discovery(device=elk_discovery_1): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + await hass.async_block_till_done() + + assert result["type"] == "form" + assert not result["errors"] + assert result["step_id"] == "user" + + mocked_elk = mock_elk(invalid_auth=False, sync_complete=True) + + with _patch_elk(elk=mocked_elk): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"device": elk_discovery_1.mac_address}, + ) + await hass.async_block_till_done() + + with _patch_discovery(device=elk_discovery_1), _patch_elk(elk=mocked_elk), patch( + "homeassistant.components.elkm1.async_setup", return_value=True + ) as mock_setup, patch( + "homeassistant.components.elkm1.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + { + "username": "test-username", + "password": "test-password", + }, + ) + await hass.async_block_till_done() + + assert result3["type"] == "create_entry" + assert result3["title"] == "ElkM1 ddeeff" + assert result3["data"] == { + "auto_configure": True, + "host": "elks://127.0.0.1", + "password": "test-password", + "prefix": "", + "username": "test-username", + } + assert len(mock_setup.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + + # Now try to add another instance with the different discovery info + with _patch_discovery(device=elk_discovery_2): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + await hass.async_block_till_done() + + assert result["type"] == "form" + assert not result["errors"] + assert result["step_id"] == "user" + + mocked_elk = mock_elk(invalid_auth=False, sync_complete=True) + + with _patch_elk(elk=mocked_elk): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"device": elk_discovery_2.mac_address}, + ) + await hass.async_block_till_done() + + with _patch_discovery(device=elk_discovery_2), _patch_elk(elk=mocked_elk), patch( + "homeassistant.components.elkm1.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + { + "username": "test-username", + "password": "test-password", + }, + ) + await hass.async_block_till_done() + + assert result3["type"] == "create_entry" + assert result3["title"] == "ElkM1 ddeefe" + assert result3["data"] == { + "auto_configure": True, + "host": "elks://127.0.0.2", + "password": "test-password", + "prefix": "ddeefe", + "username": "test-username", + } + assert len(mock_setup_entry.mock_calls) == 1 + + # Finally, try to add another instance manually with no discovery info + + with _patch_discovery(no_device=True): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + await hass.async_block_till_done() + + assert result["type"] == "form" + assert result["errors"] == {} + assert result["step_id"] == "manual_connection" + + mocked_elk = mock_elk(invalid_auth=None, sync_complete=True) + + with _patch_discovery(no_device=True), _patch_elk(elk=mocked_elk), patch( + "homeassistant.components.elkm1.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "protocol": "non-secure", + "address": "1.2.3.4", + "prefix": "guest_house", + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == "create_entry" + assert result2["title"] == "guest_house" + assert result2["data"] == { + "auto_configure": True, + "host": "elk://1.2.3.4", + "prefix": "guest_house", + "username": "", + "password": "", + } + assert len(mock_setup_entry.mock_calls) == 1 From 24f218c46b350ec0f1e9f6a681ad144be0db271a Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Sat, 5 Nov 2022 08:57:57 -0600 Subject: [PATCH 0215/1033] Bump pylitterbot to 2022.11.0 (#81572) --- homeassistant/components/litterrobot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/litterrobot/manifest.json b/homeassistant/components/litterrobot/manifest.json index 0965670e569..6384df2f25a 100644 --- a/homeassistant/components/litterrobot/manifest.json +++ b/homeassistant/components/litterrobot/manifest.json @@ -3,7 +3,7 @@ "name": "Litter-Robot", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/litterrobot", - "requirements": ["pylitterbot==2022.10.2"], + "requirements": ["pylitterbot==2022.11.0"], "codeowners": ["@natekspencer", "@tkdrob"], "dhcp": [{ "hostname": "litter-robot4" }], "iot_class": "cloud_push", diff --git a/requirements_all.txt b/requirements_all.txt index 156a47e9731..aba4a370d3c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1694,7 +1694,7 @@ pylibrespot-java==0.1.1 pylitejet==0.3.0 # homeassistant.components.litterrobot -pylitterbot==2022.10.2 +pylitterbot==2022.11.0 # homeassistant.components.lutron_caseta pylutron-caseta==0.17.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a8e30ddc07c..3f124f3feeb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1195,7 +1195,7 @@ pylibrespot-java==0.1.1 pylitejet==0.3.0 # homeassistant.components.litterrobot -pylitterbot==2022.10.2 +pylitterbot==2022.11.0 # homeassistant.components.lutron_caseta pylutron-caseta==0.17.1 From 5884f50a82f8296f51a0ebf630b265e6dab6d264 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sat, 5 Nov 2022 11:58:47 -0600 Subject: [PATCH 0216/1033] Bump pyairvisual to 2022.11.1 (#81556) --- homeassistant/components/airvisual/__init__.py | 10 +++------- homeassistant/components/airvisual/config_flow.py | 6 +++--- homeassistant/components/airvisual/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/airvisual/test_config_flow.py | 6 +++--- 6 files changed, 12 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/airvisual/__init__.py b/homeassistant/components/airvisual/__init__.py index a2a3d76c3db..2a544edb20a 100644 --- a/homeassistant/components/airvisual/__init__.py +++ b/homeassistant/components/airvisual/__init__.py @@ -7,13 +7,9 @@ from math import ceil from typing import Any from pyairvisual import CloudAPI, NodeSamba -from pyairvisual.errors import ( - AirVisualError, - InvalidKeyError, - KeyExpiredError, - NodeProError, - UnauthorizedError, -) +from pyairvisual.cloud_api import InvalidKeyError, KeyExpiredError, UnauthorizedError +from pyairvisual.errors import AirVisualError +from pyairvisual.node import NodeProError from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( diff --git a/homeassistant/components/airvisual/config_flow.py b/homeassistant/components/airvisual/config_flow.py index 385c9f55753..9510c938cb0 100644 --- a/homeassistant/components/airvisual/config_flow.py +++ b/homeassistant/components/airvisual/config_flow.py @@ -6,14 +6,14 @@ from collections.abc import Mapping from typing import Any from pyairvisual import CloudAPI, NodeSamba -from pyairvisual.errors import ( - AirVisualError, +from pyairvisual.cloud_api import ( InvalidKeyError, KeyExpiredError, - NodeProError, NotFoundError, UnauthorizedError, ) +from pyairvisual.errors import AirVisualError +from pyairvisual.node import NodeProError import voluptuous as vol from homeassistant import config_entries diff --git a/homeassistant/components/airvisual/manifest.json b/homeassistant/components/airvisual/manifest.json index 73bbf0cd589..ae9eeb270a8 100644 --- a/homeassistant/components/airvisual/manifest.json +++ b/homeassistant/components/airvisual/manifest.json @@ -3,7 +3,7 @@ "name": "AirVisual", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/airvisual", - "requirements": ["pyairvisual==2022.07.0"], + "requirements": ["pyairvisual==2022.11.1"], "codeowners": ["@bachya"], "iot_class": "cloud_polling", "loggers": ["pyairvisual", "pysmb"], diff --git a/requirements_all.txt b/requirements_all.txt index aba4a370d3c..126bc615b28 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1439,7 +1439,7 @@ pyaftership==21.11.0 pyairnow==1.1.0 # homeassistant.components.airvisual -pyairvisual==2022.07.0 +pyairvisual==2022.11.1 # homeassistant.components.almond pyalmond==0.0.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3f124f3feeb..2d1fb93d2d2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1027,7 +1027,7 @@ pyaehw4a1==0.3.9 pyairnow==1.1.0 # homeassistant.components.airvisual -pyairvisual==2022.07.0 +pyairvisual==2022.11.1 # homeassistant.components.almond pyalmond==0.0.2 diff --git a/tests/components/airvisual/test_config_flow.py b/tests/components/airvisual/test_config_flow.py index f97ee845dba..7603917eddc 100644 --- a/tests/components/airvisual/test_config_flow.py +++ b/tests/components/airvisual/test_config_flow.py @@ -1,14 +1,14 @@ """Define tests for the AirVisual config flow.""" from unittest.mock import patch -from pyairvisual.errors import ( - AirVisualError, +from pyairvisual.cloud_api import ( InvalidKeyError, KeyExpiredError, - NodeProError, NotFoundError, UnauthorizedError, ) +from pyairvisual.errors import AirVisualError +from pyairvisual.node import NodeProError import pytest from homeassistant import data_entry_flow From 3a33d3646644fbf01b0a6aeaafb98410d0f9842c Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sat, 5 Nov 2022 21:23:36 +0100 Subject: [PATCH 0217/1033] Fix situation where deCONZ sensor platform setup would fail (#81629) * Fix situation where deCONZ sensor platform setup would fail * Don't use try --- homeassistant/components/deconz/sensor.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index 66c186e20d7..f1bd0118030 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -89,6 +89,7 @@ T = TypeVar( class DeconzSensorDescriptionMixin(Generic[T]): """Required values when describing secondary sensor attributes.""" + supported_fn: Callable[[T], bool] update_key: str value_fn: Callable[[T], datetime | StateType] @@ -105,6 +106,7 @@ class DeconzSensorDescription(SensorEntityDescription, DeconzSensorDescriptionMi ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( DeconzSensorDescription[AirQuality]( key="air_quality", + supported_fn=lambda device: device.air_quality is not None, update_key="airquality", value_fn=lambda device: device.air_quality, instance_check=AirQuality, @@ -112,6 +114,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( ), DeconzSensorDescription[AirQuality]( key="air_quality_ppb", + supported_fn=lambda device: device.air_quality_ppb is not None, update_key="airqualityppb", value_fn=lambda device: device.air_quality_ppb, instance_check=AirQuality, @@ -122,6 +125,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( ), DeconzSensorDescription[Consumption]( key="consumption", + supported_fn=lambda device: device.consumption is not None, update_key="consumption", value_fn=lambda device: device.scaled_consumption, instance_check=Consumption, @@ -131,6 +135,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( ), DeconzSensorDescription[Daylight]( key="daylight_status", + supported_fn=lambda device: True, update_key="status", value_fn=lambda device: DAYLIGHT_STATUS[device.daylight_status], instance_check=Daylight, @@ -139,12 +144,14 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( ), DeconzSensorDescription[GenericStatus]( key="status", + supported_fn=lambda device: device.status is not None, update_key="status", value_fn=lambda device: device.status, instance_check=GenericStatus, ), DeconzSensorDescription[Humidity]( key="humidity", + supported_fn=lambda device: device.humidity is not None, update_key="humidity", value_fn=lambda device: device.scaled_humidity, instance_check=Humidity, @@ -154,6 +161,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( ), DeconzSensorDescription[LightLevel]( key="light_level", + supported_fn=lambda device: device.light_level is not None, update_key="lightlevel", value_fn=lambda device: device.scaled_light_level, instance_check=LightLevel, @@ -163,6 +171,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( ), DeconzSensorDescription[Power]( key="power", + supported_fn=lambda device: device.power is not None, update_key="power", value_fn=lambda device: device.power, instance_check=Power, @@ -172,6 +181,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( ), DeconzSensorDescription[Pressure]( key="pressure", + supported_fn=lambda device: device.pressure is not None, update_key="pressure", value_fn=lambda device: device.pressure, instance_check=Pressure, @@ -181,6 +191,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( ), DeconzSensorDescription[Temperature]( key="temperature", + supported_fn=lambda device: device.temperature is not None, update_key="temperature", value_fn=lambda device: device.scaled_temperature, instance_check=Temperature, @@ -190,6 +201,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( ), DeconzSensorDescription[Time]( key="last_set", + supported_fn=lambda device: device.last_set is not None, update_key="lastset", value_fn=lambda device: dt_util.parse_datetime(device.last_set), instance_check=Time, @@ -197,6 +209,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( ), DeconzSensorDescription[SensorResources]( key="battery", + supported_fn=lambda device: device.battery is not None, update_key="battery", value_fn=lambda device: device.battery, name_suffix="Battery", @@ -208,6 +221,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( ), DeconzSensorDescription[SensorResources]( key="internal_temperature", + supported_fn=lambda device: device.internal_temperature is not None, update_key="temperature", value_fn=lambda device: device.internal_temperature, name_suffix="Temperature", @@ -268,7 +282,7 @@ async def async_setup_entry( continue no_sensor_data = False - if description.value_fn(sensor) is None: + if not description.supported_fn(sensor): no_sensor_data = True if description.instance_check is None: From 5f9f95602369fc3f1d6a2071600cc35259aa0192 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk <11290930+bouwew@users.noreply.github.com> Date: Sat, 5 Nov 2022 21:26:19 +0100 Subject: [PATCH 0218/1033] Bump plugwise to v0.25.7 (#81612) --- homeassistant/components/plugwise/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plugwise/manifest.json b/homeassistant/components/plugwise/manifest.json index 7f3e979ab7d..6bb1c941bf3 100644 --- a/homeassistant/components/plugwise/manifest.json +++ b/homeassistant/components/plugwise/manifest.json @@ -2,7 +2,7 @@ "domain": "plugwise", "name": "Plugwise", "documentation": "https://www.home-assistant.io/integrations/plugwise", - "requirements": ["plugwise==0.25.3"], + "requirements": ["plugwise==0.25.7"], "codeowners": ["@CoMPaTech", "@bouwew", "@brefra", "@frenck"], "zeroconf": ["_plugwise._tcp.local."], "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 126bc615b28..46fc167d1f4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1318,7 +1318,7 @@ plexauth==0.0.6 plexwebsocket==0.0.13 # homeassistant.components.plugwise -plugwise==0.25.3 +plugwise==0.25.7 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2d1fb93d2d2..fd4f33edf02 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -945,7 +945,7 @@ plexauth==0.0.6 plexwebsocket==0.0.13 # homeassistant.components.plugwise -plugwise==0.25.3 +plugwise==0.25.7 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 From 6495c65d102075aea11ed68ae293ffaf1dde5c4f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 5 Nov 2022 15:28:47 -0500 Subject: [PATCH 0219/1033] Align esphome ble client notify behavior to match BlueZ (#81463) --- .../components/esphome/bluetooth/client.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index 72531a2503a..c6b60831577 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -137,6 +137,7 @@ class ESPHomeClient(BaseBleakClient): was_connected = self._is_connected self.services = BleakGATTServiceCollection() # type: ignore[no-untyped-call] self._is_connected = False + self._notify_cancels.clear() if self._disconnected_event: self._disconnected_event.set() self._disconnected_event = None @@ -463,12 +464,20 @@ class ESPHomeClient(BaseBleakClient): UUID or directly by the BleakGATTCharacteristic object representing it. callback (function): The function to be called on notification. """ + ble_handle = characteristic.handle + if ble_handle in self._notify_cancels: + raise BleakError( + "Notifications are already enabled on " + f"service:{characteristic.service_uuid} " + f"characteristic:{characteristic.uuid} " + f"handle:{ble_handle}" + ) cancel_coro = await self._client.bluetooth_gatt_start_notify( self._address_as_int, - characteristic.handle, + ble_handle, lambda handle, data: callback(data), ) - self._notify_cancels[characteristic.handle] = cancel_coro + self._notify_cancels[ble_handle] = cancel_coro @api_error_as_bleak_error async def stop_notify( @@ -483,5 +492,7 @@ class ESPHomeClient(BaseBleakClient): directly by the BleakGATTCharacteristic object representing it. """ characteristic = self._resolve_characteristic(char_specifier) - coro = self._notify_cancels.pop(characteristic.handle) - await coro() + # Do not raise KeyError if notifications are not enabled on this characteristic + # to be consistent with the behavior of the BlueZ backend + if coro := self._notify_cancels.pop(characteristic.handle, None): + await coro() From 89ebca759465c612a2eae7c393ecd10448b704cb Mon Sep 17 00:00:00 2001 From: Tim Rightnour <6556271+garbled1@users.noreply.github.com> Date: Sat, 5 Nov 2022 13:29:20 -0700 Subject: [PATCH 0220/1033] Bump venstarcolortouch to 0.19 to fix API rev 3 devices (#81614) --- homeassistant/components/venstar/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/venstar/manifest.json b/homeassistant/components/venstar/manifest.json index 4a6eea28e24..ce40e53105a 100644 --- a/homeassistant/components/venstar/manifest.json +++ b/homeassistant/components/venstar/manifest.json @@ -3,7 +3,7 @@ "name": "Venstar", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/venstar", - "requirements": ["venstarcolortouch==0.18"], + "requirements": ["venstarcolortouch==0.19"], "codeowners": ["@garbled1"], "iot_class": "local_polling", "loggers": ["venstarcolortouch"] diff --git a/requirements_all.txt b/requirements_all.txt index 46fc167d1f4..b8b0b62b691 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2493,7 +2493,7 @@ vehicle==0.4.0 velbus-aio==2022.10.4 # homeassistant.components.venstar -venstarcolortouch==0.18 +venstarcolortouch==0.19 # homeassistant.components.vilfo vilfo-api-client==0.3.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fd4f33edf02..c4f438b3d34 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1727,7 +1727,7 @@ vehicle==0.4.0 velbus-aio==2022.10.4 # homeassistant.components.venstar -venstarcolortouch==0.18 +venstarcolortouch==0.19 # homeassistant.components.vilfo vilfo-api-client==0.3.2 From 64a29fddb417afeb31bc529b3ca55b71634759c4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 5 Nov 2022 15:58:11 -0500 Subject: [PATCH 0221/1033] Bump aiohomekit to 2.2.16 (#81621) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index b2aec75c3ad..e4a2b5d79bb 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.14"], + "requirements": ["aiohomekit==2.2.16"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index b8b0b62b691..ff5b72fbae4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.14 +aiohomekit==2.2.16 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c4f438b3d34..88277960d5b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.14 +aiohomekit==2.2.16 # homeassistant.components.emulated_hue # homeassistant.components.http From f66009c77dd901c1124ef4b8c213d67f8f113f39 Mon Sep 17 00:00:00 2001 From: Avi Miller Date: Sun, 6 Nov 2022 08:46:00 +1100 Subject: [PATCH 0222/1033] Fix lifx.set_state so it works with kelvin and color_temp_kelvin and color names (#81515) --- homeassistant/components/lifx/util.py | 26 +++++- tests/components/lifx/test_light.py | 128 ++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/lifx/util.py b/homeassistant/components/lifx/util.py index 135e1a7e8e9..6a9bff465ee 100644 --- a/homeassistant/components/lifx/util.py +++ b/homeassistant/components/lifx/util.py @@ -14,8 +14,11 @@ from awesomeversion import AwesomeVersion from homeassistant.components.light import ( ATTR_BRIGHTNESS, + ATTR_BRIGHTNESS_PCT, + ATTR_COLOR_NAME, ATTR_COLOR_TEMP_KELVIN, ATTR_HS_COLOR, + ATTR_KELVIN, ATTR_RGB_COLOR, ATTR_XY_COLOR, ) @@ -24,7 +27,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry as dr import homeassistant.util.color as color_util -from .const import DOMAIN, INFRARED_BRIGHTNESS_VALUES_MAP, OVERALL_TIMEOUT +from .const import _LOGGER, DOMAIN, INFRARED_BRIGHTNESS_VALUES_MAP, OVERALL_TIMEOUT FIX_MAC_FW = AwesomeVersion("3.70") @@ -80,6 +83,17 @@ def find_hsbk(hass: HomeAssistant, **kwargs: Any) -> list[float | int | None] | """ hue, saturation, brightness, kelvin = [None] * 4 + if (color_name := kwargs.get(ATTR_COLOR_NAME)) is not None: + try: + hue, saturation = color_util.color_RGB_to_hs( + *color_util.color_name_to_rgb(color_name) + ) + except ValueError: + _LOGGER.warning( + "Got unknown color %s, falling back to neutral white", color_name + ) + hue, saturation = (0, 0) + if ATTR_HS_COLOR in kwargs: hue, saturation = kwargs[ATTR_HS_COLOR] elif ATTR_RGB_COLOR in kwargs: @@ -93,6 +107,13 @@ def find_hsbk(hass: HomeAssistant, **kwargs: Any) -> list[float | int | None] | saturation = int(saturation / 100 * 65535) kelvin = 3500 + if ATTR_KELVIN in kwargs: + _LOGGER.warning( + "The 'kelvin' parameter is deprecated. Please use 'color_temp_kelvin' for all service calls" + ) + kelvin = kwargs.pop(ATTR_KELVIN) + saturation = 0 + if ATTR_COLOR_TEMP_KELVIN in kwargs: kelvin = kwargs.pop(ATTR_COLOR_TEMP_KELVIN) saturation = 0 @@ -100,6 +121,9 @@ def find_hsbk(hass: HomeAssistant, **kwargs: Any) -> list[float | int | None] | if ATTR_BRIGHTNESS in kwargs: brightness = convert_8_to_16(kwargs[ATTR_BRIGHTNESS]) + if ATTR_BRIGHTNESS_PCT in kwargs: + brightness = convert_8_to_16(round(255 * kwargs[ATTR_BRIGHTNESS_PCT] / 100)) + hsbk = [hue, saturation, brightness, kelvin] return None if hsbk == [None] * 4 else hsbk diff --git a/tests/components/lifx/test_light.py b/tests/components/lifx/test_light.py index 6fe63b14b6a..5a9b250034a 100644 --- a/tests/components/lifx/test_light.py +++ b/tests/components/lifx/test_light.py @@ -21,11 +21,14 @@ from homeassistant.components.lifx.manager import ( ) from homeassistant.components.light import ( ATTR_BRIGHTNESS, + ATTR_BRIGHTNESS_PCT, ATTR_COLOR_MODE, + ATTR_COLOR_NAME, ATTR_COLOR_TEMP, ATTR_COLOR_TEMP_KELVIN, ATTR_EFFECT, ATTR_HS_COLOR, + ATTR_KELVIN, ATTR_RGB_COLOR, ATTR_SUPPORTED_COLOR_MODES, ATTR_TRANSITION, @@ -1397,6 +1400,131 @@ async def test_transitions_color_bulb(hass: HomeAssistant) -> None: bulb.set_color.reset_mock() +async def test_lifx_set_state_color(hass: HomeAssistant) -> None: + """Test lifx.set_state works with color names and RGB.""" + config_entry = MockConfigEntry( + domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=SERIAL + ) + config_entry.add_to_hass(hass) + bulb = _mocked_bulb_new_firmware() + bulb.power_level = 65535 + bulb.color = [32000, None, 32000, 2700] + with _patch_discovery(device=bulb), _patch_config_flow_try_connect( + device=bulb + ), _patch_device(device=bulb): + await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}}) + await hass.async_block_till_done() + + entity_id = "light.my_bulb" + + # brightness should convert from 8 to 16 bits + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 255}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [32000, None, 65535, 2700] + bulb.set_color.reset_mock() + + # brightness_pct should convert into 16 bit + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS_PCT: 90}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [32000, None, 59110, 2700] + bulb.set_color.reset_mock() + + # color name should turn into hue, saturation + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_COLOR_NAME: "red", ATTR_BRIGHTNESS_PCT: 100}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [0, 65535, 65535, 3500] + bulb.set_color.reset_mock() + + # unknown color name should reset back to neutral white, i.e. 3500K + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_COLOR_NAME: "deepblack"}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [0, 0, 32000, 3500] + bulb.set_color.reset_mock() + + # RGB should convert to hue, saturation + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_RGB_COLOR: (0, 255, 0)}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [21845, 65535, 32000, 3500] + bulb.set_color.reset_mock() + + # XY should convert to hue, saturation + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_XY_COLOR: (0.34, 0.339)}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [5461, 5139, 32000, 3500] + bulb.set_color.reset_mock() + + +async def test_lifx_set_state_kelvin(hass: HomeAssistant) -> None: + """Test set_state works with old and new kelvin parameter names.""" + already_migrated_config_entry = MockConfigEntry( + domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=SERIAL + ) + already_migrated_config_entry.add_to_hass(hass) + bulb = _mocked_bulb_new_firmware() + bulb.power_level = 65535 + bulb.color = [32000, None, 32000, 6000] + with _patch_discovery(device=bulb), _patch_config_flow_try_connect( + device=bulb + ), _patch_device(device=bulb): + await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}}) + await hass.async_block_till_done() + + entity_id = "light.my_bulb" + + state = hass.states.get(entity_id) + assert state.state == "on" + attributes = state.attributes + assert attributes[ATTR_BRIGHTNESS] == 125 + assert attributes[ATTR_COLOR_MODE] == ColorMode.COLOR_TEMP + await hass.services.async_call( + LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True + ) + assert bulb.set_power.calls[0][0][0] is False + bulb.set_power.reset_mock() + + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 255, ATTR_KELVIN: 3500}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [32000, 0, 65535, 3500] + bulb.set_color.reset_mock() + + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100, ATTR_COLOR_TEMP_KELVIN: 2700}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [32000, 0, 25700, 2700] + bulb.set_color.reset_mock() + + async def test_infrared_color_bulb(hass: HomeAssistant) -> None: """Test setting infrared with a color bulb.""" already_migrated_config_entry = MockConfigEntry( From a635e9c9d239cd81dde79b250d01fcf393bcfa1c Mon Sep 17 00:00:00 2001 From: Steven Looman Date: Sat, 5 Nov 2022 23:11:59 +0100 Subject: [PATCH 0223/1033] Fix repeating SSDP errors by checking address scope_ids and proper hostname (#81611) --- homeassistant/components/dlna_dmr/manifest.json | 2 +- homeassistant/components/dlna_dms/manifest.json | 2 +- homeassistant/components/samsungtv/manifest.json | 2 +- homeassistant/components/ssdp/manifest.json | 2 +- homeassistant/components/upnp/manifest.json | 2 +- homeassistant/components/yeelight/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/dlna_dmr/manifest.json b/homeassistant/components/dlna_dmr/manifest.json index 9d05b02000b..e4620386b98 100644 --- a/homeassistant/components/dlna_dmr/manifest.json +++ b/homeassistant/components/dlna_dmr/manifest.json @@ -3,7 +3,7 @@ "name": "DLNA Digital Media Renderer", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dlna_dmr", - "requirements": ["async-upnp-client==0.32.1"], + "requirements": ["async-upnp-client==0.32.2"], "dependencies": ["ssdp"], "after_dependencies": ["media_source"], "ssdp": [ diff --git a/homeassistant/components/dlna_dms/manifest.json b/homeassistant/components/dlna_dms/manifest.json index 98ad81e653a..7c7a312159b 100644 --- a/homeassistant/components/dlna_dms/manifest.json +++ b/homeassistant/components/dlna_dms/manifest.json @@ -3,7 +3,7 @@ "name": "DLNA Digital Media Server", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dlna_dms", - "requirements": ["async-upnp-client==0.32.1"], + "requirements": ["async-upnp-client==0.32.2"], "dependencies": ["ssdp"], "after_dependencies": ["media_source"], "ssdp": [ diff --git a/homeassistant/components/samsungtv/manifest.json b/homeassistant/components/samsungtv/manifest.json index 5f70e912d35..0a65a47bd23 100644 --- a/homeassistant/components/samsungtv/manifest.json +++ b/homeassistant/components/samsungtv/manifest.json @@ -8,7 +8,7 @@ "samsungctl[websocket]==0.7.1", "samsungtvws[async,encrypted]==2.5.0", "wakeonlan==2.1.0", - "async-upnp-client==0.32.1" + "async-upnp-client==0.32.2" ], "ssdp": [ { diff --git a/homeassistant/components/ssdp/manifest.json b/homeassistant/components/ssdp/manifest.json index 59d9d6ddad8..3b30146e756 100644 --- a/homeassistant/components/ssdp/manifest.json +++ b/homeassistant/components/ssdp/manifest.json @@ -2,7 +2,7 @@ "domain": "ssdp", "name": "Simple Service Discovery Protocol (SSDP)", "documentation": "https://www.home-assistant.io/integrations/ssdp", - "requirements": ["async-upnp-client==0.32.1"], + "requirements": ["async-upnp-client==0.32.2"], "dependencies": ["network"], "after_dependencies": ["zeroconf"], "codeowners": [], diff --git a/homeassistant/components/upnp/manifest.json b/homeassistant/components/upnp/manifest.json index 9b4151c35c5..4c45b099193 100644 --- a/homeassistant/components/upnp/manifest.json +++ b/homeassistant/components/upnp/manifest.json @@ -3,7 +3,7 @@ "name": "UPnP/IGD", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/upnp", - "requirements": ["async-upnp-client==0.32.1", "getmac==0.8.2"], + "requirements": ["async-upnp-client==0.32.2", "getmac==0.8.2"], "dependencies": ["network", "ssdp"], "codeowners": ["@StevenLooman"], "ssdp": [ diff --git a/homeassistant/components/yeelight/manifest.json b/homeassistant/components/yeelight/manifest.json index 16fe2ae7700..6c450548135 100644 --- a/homeassistant/components/yeelight/manifest.json +++ b/homeassistant/components/yeelight/manifest.json @@ -2,7 +2,7 @@ "domain": "yeelight", "name": "Yeelight", "documentation": "https://www.home-assistant.io/integrations/yeelight", - "requirements": ["yeelight==0.7.10", "async-upnp-client==0.32.1"], + "requirements": ["yeelight==0.7.10", "async-upnp-client==0.32.2"], "codeowners": ["@zewelor", "@shenxn", "@starkillerOG", "@alexyao2015"], "config_flow": true, "dependencies": ["network"], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index b5b7350d4db..eb580796a97 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -4,7 +4,7 @@ aiodiscover==1.4.13 aiohttp==3.8.1 aiohttp_cors==0.7.0 astral==2.2 -async-upnp-client==0.32.1 +async-upnp-client==0.32.2 async_timeout==4.0.2 atomicwrites-homeassistant==1.4.1 attrs==21.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index ff5b72fbae4..54c0db9a92d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -359,7 +359,7 @@ asterisk_mbox==0.5.0 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.32.1 +async-upnp-client==0.32.2 # homeassistant.components.supla asyncpysupla==0.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 88277960d5b..c95d21e4c1f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -313,7 +313,7 @@ arcam-fmj==0.12.0 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.32.1 +async-upnp-client==0.32.2 # homeassistant.components.sleepiq asyncsleepiq==1.2.3 From 2ad1d3111930f89a95a7dd86badcd24837077da2 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 5 Nov 2022 17:06:34 -0700 Subject: [PATCH 0224/1033] Bump gcal_sync to 4.0.0 (#81562) * Bump gcal_sync to 2.2.4 * Bump gcal sync to 4.0.0 * Add Calendar accessRole fields which are now required --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/google/test_config_flow.py | 2 +- tests/components/google/test_init.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index f6ebc665cd7..9fc265fa287 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==2.2.3", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==4.0.0", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index 54c0db9a92d..f54c88aad8e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -731,7 +731,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==2.2.3 +gcal-sync==4.0.0 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c95d21e4c1f..7793088d47c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -547,7 +547,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==2.2.3 +gcal-sync==4.0.0 # homeassistant.components.geocaching geocachingapi==0.2.1 diff --git a/tests/components/google/test_config_flow.py b/tests/components/google/test_config_flow.py index d8ddd6fe588..bce3f4855c7 100644 --- a/tests/components/google/test_config_flow.py +++ b/tests/components/google/test_config_flow.py @@ -104,7 +104,7 @@ async def primary_calendar( """Fixture to return the primary calendar.""" mock_calendar_get( "primary", - {"id": primary_calendar_email, "summary": "Personal"}, + {"id": primary_calendar_email, "summary": "Personal", "accessRole": "owner"}, exc=primary_calendar_error, ) diff --git a/tests/components/google/test_init.py b/tests/components/google/test_init.py index 5e7696eec68..a2f16f778fd 100644 --- a/tests/components/google/test_init.py +++ b/tests/components/google/test_init.py @@ -768,7 +768,7 @@ async def test_assign_unique_id( mock_calendar_get( "primary", - {"id": EMAIL_ADDRESS, "summary": "Personal"}, + {"id": EMAIL_ADDRESS, "summary": "Personal", "accessRole": "reader"}, ) mock_calendars_list({"items": [test_api_calendar]}) From fc472eb040892ac9ab13a9a7ee6d2ae5bfecce66 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 6 Nov 2022 00:27:15 +0000 Subject: [PATCH 0225/1033] [ci skip] Translation update --- .../accuweather/translations/ja.json | 2 +- .../components/airq/translations/it.json | 22 +++++++ .../components/airq/translations/nl.json | 22 +++++++ .../airthings_ble/translations/nl.json | 2 +- .../components/brother/translations/it.json | 2 +- .../components/dlna_dms/translations/it.json | 2 +- .../components/esphome/translations/it.json | 2 +- .../forecast_solar/translations/it.json | 2 +- .../components/fritz/translations/it.json | 2 +- .../components/generic/translations/it.json | 4 +- .../components/hassio/translations/ca.json | 4 ++ .../components/hue/translations/it.json | 2 +- .../components/insteon/translations/it.json | 2 +- .../components/ipp/translations/it.json | 2 +- .../components/melnor/translations/it.json | 2 +- .../components/mqtt/translations/it.json | 16 ++--- .../components/mqtt/translations/nl.json | 58 ++++++++++++++++--- .../components/mysensors/translations/nl.json | 4 +- .../components/nobo_hub/translations/it.json | 2 +- .../components/oralb/translations/it.json | 10 ++-- .../plugwise/translations/select.it.json | 4 +- .../pure_energie/translations/it.json | 2 +- .../components/pushover/translations/it.json | 2 +- .../sensibo/translations/sensor.it.json | 2 +- .../components/smappee/translations/it.json | 2 +- .../components/wled/translations/it.json | 2 +- .../xiaomi_ble/translations/nl.json | 20 +++++++ .../xiaomi_miio/translations/select.nl.json | 3 +- .../components/zha/translations/nl.json | 2 + .../components/zwave_js/translations/it.json | 2 +- 30 files changed, 159 insertions(+), 46 deletions(-) create mode 100644 homeassistant/components/airq/translations/it.json create mode 100644 homeassistant/components/airq/translations/nl.json diff --git a/homeassistant/components/accuweather/translations/ja.json b/homeassistant/components/accuweather/translations/ja.json index b9c7819e78a..3583f9760b9 100644 --- a/homeassistant/components/accuweather/translations/ja.json +++ b/homeassistant/components/accuweather/translations/ja.json @@ -28,7 +28,7 @@ "data": { "forecast": "\u5929\u6c17\u4e88\u5831" }, - "description": "\u5236\u9650\u306b\u3088\u308a\u7121\u6599\u30d0\u30fc\u30b8\u30e7\u30f3\u306eAccuWeather API\u30ad\u30fc\u3067\u306f\u3001\u5929\u6c17\u4e88\u5831\u3092\u6709\u52b9\u306b\u3057\u3066\u3082\u30c7\u30fc\u30bf\u306e\u66f4\u65b0\u306f40\u5206\u3067\u306f\u306a\u304f80\u5206\u3054\u3068\u3067\u3059\u3002" + "description": "\u7121\u6599\u7248\u306eAccuWeather API\u30ad\u30fc\u306e\u5236\u9650\u306b\u3088\u308a\u3001\u5929\u6c17\u4e88\u5831\u3092\u6709\u52b9\u306b\u3057\u305f\u5834\u5408\u3001\u30c7\u30fc\u30bf\u306e\u66f4\u65b0\u306f40\u5206\u6bce\u3067\u306f\u306a\u304f80\u5206\u6bce\u306b\u5b9f\u884c\u3055\u308c\u307e\u3059\u3002" } } }, diff --git a/homeassistant/components/airq/translations/it.json b/homeassistant/components/airq/translations/it.json new file mode 100644 index 00000000000..782f631bb09 --- /dev/null +++ b/homeassistant/components/airq/translations/it.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_auth": "Autenticazione non valida", + "invalid_input": "Nome host o indirizzo IP non valido" + }, + "step": { + "user": { + "data": { + "ip_address": "Indirizzo IP", + "password": "Password" + }, + "description": "Fornire l'indirizzo IP o mDNS del dispositivo e la relativa password", + "title": "Identifica il dispositivo" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/nl.json b/homeassistant/components/airq/translations/nl.json new file mode 100644 index 00000000000..07fe49d3708 --- /dev/null +++ b/homeassistant/components/airq/translations/nl.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Apparaat is al geconfigureerd" + }, + "error": { + "cannot_connect": "Verbinding mislukt", + "invalid_auth": "Ongeldige authenticatie poging", + "invalid_input": "Ongeldige hostnaam of IP adres" + }, + "step": { + "user": { + "data": { + "ip_address": "IP adres", + "password": "Wachtwoord" + }, + "description": "Geef het IP adress of mDNS van het apparaat en het bijbehorend wachtwoord", + "title": "Identificeer het apparaat" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airthings_ble/translations/nl.json b/homeassistant/components/airthings_ble/translations/nl.json index 1e1e61a3b39..785b57f8bb8 100644 --- a/homeassistant/components/airthings_ble/translations/nl.json +++ b/homeassistant/components/airthings_ble/translations/nl.json @@ -5,7 +5,7 @@ "already_in_progress": "Nederlands", "cannot_connect": "Nederlands", "no_devices_found": "Nederlands", - "unknown": "Nederlands" + "unknown": "Onverwachte fout" }, "flow_title": "Nederlands", "step": { diff --git a/homeassistant/components/brother/translations/it.json b/homeassistant/components/brother/translations/it.json index 21fd1bf15ab..038815ec142 100644 --- a/homeassistant/components/brother/translations/it.json +++ b/homeassistant/components/brother/translations/it.json @@ -22,7 +22,7 @@ "type": "Tipo di stampante" }, "description": "Vuoi aggiungere la stampante {model} con il numero seriale `{serial_number}` a Home Assistant?", - "title": "Trovata stampante Brother" + "title": "Rilevata stampante Brother" } } } diff --git a/homeassistant/components/dlna_dms/translations/it.json b/homeassistant/components/dlna_dms/translations/it.json index a8c0d50c0cf..c671bc9b619 100644 --- a/homeassistant/components/dlna_dms/translations/it.json +++ b/homeassistant/components/dlna_dms/translations/it.json @@ -17,7 +17,7 @@ "host": "Host" }, "description": "Seleziona un dispositivo da configurare", - "title": "Dispositivi DLNA DMA rilevati" + "title": "Rilevati dispositivi DLNA DMA" } } } diff --git a/homeassistant/components/esphome/translations/it.json b/homeassistant/components/esphome/translations/it.json index b1c4e12d088..23727d1510f 100644 --- a/homeassistant/components/esphome/translations/it.json +++ b/homeassistant/components/esphome/translations/it.json @@ -21,7 +21,7 @@ }, "discovery_confirm": { "description": "Vuoi aggiungere il nodo ESPHome `{name}` a Home Assistant?", - "title": "Trovato nodo ESPHome" + "title": "Rilevato nodo ESPHome" }, "encryption_key": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/it.json b/homeassistant/components/forecast_solar/translations/it.json index 598c67695cc..8d6d266fa95 100644 --- a/homeassistant/components/forecast_solar/translations/it.json +++ b/homeassistant/components/forecast_solar/translations/it.json @@ -25,7 +25,7 @@ "inverter_size": "Dimensioni inverter (Watt)", "modules power": "Potenza di picco totale in Watt dei tuoi moduli solari" }, - "description": "Questi valori consentono di modificare il risultato di Solar.Forecast. Fai riferimento alla documentazione se un campo non \u00e8 chiaro." + "description": "Questi valori consentono di modificare il risultato di Forecast.Solar. Se un campo non \u00e8 chiaro, consultare la documentazione." } } } diff --git a/homeassistant/components/fritz/translations/it.json b/homeassistant/components/fritz/translations/it.json index b1fa200731e..bb18e38d1eb 100644 --- a/homeassistant/components/fritz/translations/it.json +++ b/homeassistant/components/fritz/translations/it.json @@ -20,7 +20,7 @@ "password": "Password", "username": "Nome utente" }, - "description": "FRITZ! Box rilevato: {name} \n\n Configura gli strumenti del FRITZ! Box per controllare il tuo {name}", + "description": "FRITZ! Box rilevato: {name} \n\nConfigura gli strumenti del FRITZ! Box per controllare il tuo {name}", "title": "Configura gli strumenti del FRITZ!Box" }, "reauth_confirm": { diff --git a/homeassistant/components/generic/translations/it.json b/homeassistant/components/generic/translations/it.json index 0fd99c649a1..4e8cdb513cd 100644 --- a/homeassistant/components/generic/translations/it.json +++ b/homeassistant/components/generic/translations/it.json @@ -77,9 +77,9 @@ "step": { "confirm_still": { "data": { - "confirmed_ok": "Questa immagine appare bene" + "confirmed_ok": "Questa immagine sembra buona." }, - "description": "![Anteprima dell' immagine fissa della fotocamera]({preview_url})", + "description": "![Anteprima immagine fissa fotocamera]({preview_url})", "title": "Anteprima" }, "content_type": { diff --git a/homeassistant/components/hassio/translations/ca.json b/homeassistant/components/hassio/translations/ca.json index 13e928e998b..71a0066b58a 100644 --- a/homeassistant/components/hassio/translations/ca.json +++ b/homeassistant/components/hassio/translations/ca.json @@ -36,6 +36,10 @@ "description": "El sistema no \u00e9s compatible perqu\u00e8 s'utilitza una versi\u00f3 de Docker CGroup incorrecta. Clica l'enlla\u00e7 per con\u00e8ixer la versi\u00f3 correcta i sobre com solucionar-ho.", "title": "Sistema no compatible - Versi\u00f3 de CGroup" }, + "unsupported_dns_server": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 el servidor DNS proporcionat no funciona correctament i la segona opci\u00f3 est\u00e0 desactivada. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Problemes el servidor DNS" + }, "unsupported_docker_configuration": { "description": "El sistema no \u00e9s compatible perqu\u00e8 el 'deamon' de Docker est\u00e0 funcionant de manera inesperada. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", "title": "Sistema no compatible - Docker mal configurat" diff --git a/homeassistant/components/hue/translations/it.json b/homeassistant/components/hue/translations/it.json index be64a3b0624..3698677ef8c 100644 --- a/homeassistant/components/hue/translations/it.json +++ b/homeassistant/components/hue/translations/it.json @@ -7,7 +7,7 @@ "cannot_connect": "Impossibile connettersi", "discover_timeout": "Impossibile trovare i bridge Hue", "invalid_host": "Host non valido", - "no_bridges": "Nessun bridge di Philips Hue trovato", + "no_bridges": "Nessun bridge di Philips Hue rilevato", "not_hue_bridge": "Non \u00e8 un bridge Hue", "unknown": "Errore imprevisto" }, diff --git a/homeassistant/components/insteon/translations/it.json b/homeassistant/components/insteon/translations/it.json index c5bd0f1af87..38a04fa1fe0 100644 --- a/homeassistant/components/insteon/translations/it.json +++ b/homeassistant/components/insteon/translations/it.json @@ -2,7 +2,7 @@ "config": { "abort": { "cannot_connect": "Impossibile connettersi", - "not_insteon_device": "Dispositivo rilevato non \u00e8 un dispositivo Insteon", + "not_insteon_device": "Il dispositivo rilevato non \u00e8 un dispositivo Insteon", "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." }, "error": { diff --git a/homeassistant/components/ipp/translations/it.json b/homeassistant/components/ipp/translations/it.json index 96319641c6b..3c2df9a6ee0 100644 --- a/homeassistant/components/ipp/translations/it.json +++ b/homeassistant/components/ipp/translations/it.json @@ -28,7 +28,7 @@ }, "zeroconf_confirm": { "description": "Vuoi configurare {name}?", - "title": "Stampante rilevata" + "title": "Rilevata stampante" } } } diff --git a/homeassistant/components/melnor/translations/it.json b/homeassistant/components/melnor/translations/it.json index 9124fdfce13..c4929c9c230 100644 --- a/homeassistant/components/melnor/translations/it.json +++ b/homeassistant/components/melnor/translations/it.json @@ -6,7 +6,7 @@ "step": { "bluetooth_confirm": { "description": "Vuoi aggiungere la valvola Bluetooth Melnor `{name}` a Home Assistant?", - "title": "Scoperta la valvola Bluetooth Melnor" + "title": "Rilevata valvola Bluetooth Melnor" } } } diff --git a/homeassistant/components/mqtt/translations/it.json b/homeassistant/components/mqtt/translations/it.json index b9504a80a03..f8add8256bc 100644 --- a/homeassistant/components/mqtt/translations/it.json +++ b/homeassistant/components/mqtt/translations/it.json @@ -8,7 +8,7 @@ "bad_birth": "Argomento di nascita non valido", "bad_certificate": "Il certificato CA non \u00e8 valido", "bad_client_cert": "Certificato client non valido, assicurarsi che venga fornito un file codificato PEM", - "bad_client_cert_key": "Il certificato del client e il privato non sono accoppiati in modo valido", + "bad_client_cert_key": "Il certificato del client e il certificato privato non sono una coppia valida", "bad_client_key": "Chiave privata non valida, assicurarsi che venga fornito un file codificato PEM senza password", "bad_discovery_prefix": "Prefisso di ricerca non valido", "bad_will": "Argomento testamento non valido", @@ -25,13 +25,13 @@ "client_id": "ID cliente (lasciare vuoto per generarne uno in modo casuale)", "client_key": "Percorso per un file della chiave privata", "discovery": "Attiva il rilevamento", - "keepalive": "L'intervallo di tempo tra l'invio di messaggi di mantenimento attivo", + "keepalive": "L'intervallo di tempo tra l'invio di messaggi di mantenimento attivit\u00e0", "password": "Password", "port": "Porta", "protocol": "Protocollo MQTT", "set_ca_cert": "Convalida del certificato del broker", - "set_client_cert": "Utilizzare un certificato client", - "tls_insecure": "Ignorare la convalida del certificato del broker", + "set_client_cert": "Usa un certificato client", + "tls_insecure": "Ignora la convalida del certificato del broker", "username": "Nome utente" }, "description": "Inserisci le informazioni di connessione del tuo broker MQTT." @@ -80,11 +80,11 @@ "options": { "error": { "bad_birth": "Argomento di nascita non valido", - "bad_certificate": "Certificato CA non valido", + "bad_certificate": "Il certificato CA non \u00e8 valido", "bad_client_cert": "Certificato cliente non valido, assicurarsi che venga fornito un file con codice PEM", - "bad_client_cert_key": "Il certificato del client e il certificato privato non sono accoppiati in modo valido", + "bad_client_cert_key": "Il certificato del client e il certificato privato non sono una coppia valida", "bad_client_key": "Chiave privata non valida, assicurarsi che venga fornito un file codificato PEM senza password", - "bad_discovery_prefix": "Prefisso di ricerca non valido", + "bad_discovery_prefix": "Prefisso di rilevamento non valido", "bad_will": "Argomento di testamento non valido", "cannot_connect": "Impossibile connettersi", "invalid_inclusion": "Il certificato e la chiave privata del client devono essere configurati insieme" @@ -118,7 +118,7 @@ "birth_retain": "Persistenza del messaggio birth", "birth_topic": "Argomento del messaggio birth", "discovery": "Attiva il rilevamento", - "discovery_prefix": "Scopri il prefisso", + "discovery_prefix": "Rileva prefisso", "will_enable": "Abilita il messaggio testamento", "will_payload": "Payload del messaggio testamento", "will_qos": "QoS del messaggio testamento", diff --git a/homeassistant/components/mqtt/translations/nl.json b/homeassistant/components/mqtt/translations/nl.json index 155cf0bf07a..13fcd4a9595 100644 --- a/homeassistant/components/mqtt/translations/nl.json +++ b/homeassistant/components/mqtt/translations/nl.json @@ -5,15 +5,30 @@ "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { - "cannot_connect": "Kan geen verbinding maken" + "bad_birth": "Ongeldig `birth` topic", + "bad_certificate": "Het CA certificaat bestand is ongeldig", + "bad_client_cert": "Ongeldig client-certificaat, zorg voor een PEM gecodeerd bestand", + "bad_client_cert_key": "Client certificaat en priv\u00e9sleutel zijn geen geldig paar", + "bad_client_key": "Ongeldige priv\u00e9sleutel (private key), zorg voor een PEM gecodeerd bestand zonder wachtwoord", + "bad_discovery_prefix": "Ongeldig discovery voorvoegsel", + "bad_will": "Ongeldig `will` topic", + "cannot_connect": "Kan geen verbinding maken", + "invalid_inclusion": "Het client-certificaat en priv\u00e9sleutel moeten samen worden geconfigureerd" }, "step": { "broker": { "data": { + "advanced_options": "Geavanceerde opties", "broker": "Broker", + "client_id": "Client ID (leeg laten voor een willekeurig ID)", "discovery": "Detectie inschakelen", + "keepalive": "Tijd tussen het verzenden van keep-a-live berichten", "password": "Wachtwoord", "port": "Poort", + "protocol": "MQTT protocol", + "set_ca_cert": "Broker certificaatvalidatie", + "set_client_cert": "Gebruik een client-certificaat", + "tls_insecure": "Negeer validatie van brokercertificaten", "username": "Gebruikersnaam" }, "description": "MQTT" @@ -49,18 +64,44 @@ "button_triple_press": "\" {subtype} \" driemaal geklikt" } }, + "issues": { + "deprecated_yaml": { + "description": "Handmatig geconfigureerd MQTT {platform}(en) gevonden onder de platform sleutel `{platform}`.\n\nVerplaats a.u.b. deze configuratieinstellingen naar de `mqtt` configuratie sleutel en herstart Home Assistant om het probleem te verhelpen. Zie de [documentatie]({more_info_url}), voor meer informatie.", + "title": "Handmatig geconfigureerd platform {platform}(s) vereist aandacht" + }, + "deprecated_yaml_broker_settings": { + "description": "De volgende instellingen gevonden in `configuration.yaml` zijn gemigreerd naar de MQTT configuratieinstellingen en overschrijven nu de instellingen in `configuration.yaml`:\n`{deprecated_settings}`\n\nVerwijderd deze instellingen van `configuration.yaml` en herstart Home Assistant om dit probleem op te lossen. Zie de [documentatie]({more_info_url}), voor meer informatie.", + "title": "Verouderde MQTT instellingen gevonden in `configuration.yaml`" + } + }, "options": { "error": { "bad_birth": "Ongeldig birth topic", + "bad_certificate": "Het CA certificaat bestand is ongeldig", + "bad_client_cert": "Ongeldig client certificaat, zorg voor een PEM gecodeerd bestand", + "bad_client_cert_key": "Client-certificaat en priv\u00e9sleutel zijn geen geldig paar", + "bad_client_key": "Ongeldige priv\u00e9sleutel (private key), zorg voor een PEM gecodeerd bestand zonder wachtwoord", + "bad_discovery_prefix": "Ongeldig discovery voorvoegsel", "bad_will": "Ongeldig will topic", - "cannot_connect": "Kan geen verbinding maken" + "cannot_connect": "Kan geen verbinding maken", + "invalid_inclusion": "Het client-certificaat en priv\u00e9sleutel moeten samen worden geconfigureerd" }, "step": { "broker": { "data": { + "advanced_options": "Geavanceerde opties", "broker": "Broker", + "certificate": "Upload CA certificaat bestand", + "client_cert": "Upload private certificaat bestand", + "client_id": "Client ID (leeg laten voor een willekeurig ID)", + "client_key": "Upload private key bestand", + "keepalive": "Tijd tussen het verzenden van keep-a-live berichten", "password": "Wachtwoord", "port": "Poort", + "protocol": "MQTT protocol", + "set_ca_cert": "Broker certificaatvalidatie", + "set_client_cert": "Gebruik een client-certificaat", + "tls_insecure": "Negeer validatie van brokercertificaten", "username": "Gebruikersnaam" }, "description": "Voer de verbindingsgegevens van uw MQTT-broker in.", @@ -68,14 +109,15 @@ }, "options": { "data": { - "birth_enable": "Geboortebericht inschakelen", - "birth_payload": "Birth message payload", - "birth_qos": "Birth message QoS", + "birth_enable": "Geboortebericht inschakelen (birth)", + "birth_payload": "Birth bericht inhoud", + "birth_qos": "Birth bericht QoS", "birth_retain": "Verbind bericht onthouden", - "birth_topic": "Birth message onderwerp", + "birth_topic": "Birth bericht topic", "discovery": "Discovery inschakelen", - "will_enable": "Offline bericht inschakelen", - "will_payload": "Offline bericht inhoud", + "discovery_prefix": "Discovery-voorvoegsel", + "will_enable": "Offline bericht inschakelen (will)", + "will_payload": "Will bericht inhoud", "will_qos": "Offline bericht QoS", "will_retain": "Offline bericht onthouden", "will_topic": "Offline bericht topic" diff --git a/homeassistant/components/mysensors/translations/nl.json b/homeassistant/components/mysensors/translations/nl.json index 44bd06022b9..51ddd56e6d6 100644 --- a/homeassistant/components/mysensors/translations/nl.json +++ b/homeassistant/components/mysensors/translations/nl.json @@ -44,9 +44,9 @@ "gw_mqtt": { "data": { "persistence_file": "persistentiebestand (leeg laten om automatisch te genereren)", - "retain": "mqtt behouden", + "retain": "MQTT retentie", "topic_in_prefix": "prefix voor inkomende topics (topic_in_prefix)", - "topic_out_prefix": "prefix voor uitgaande topics (topic_out_prefix)", + "topic_out_prefix": "voorvoegsel voor uitgaande topics (topic_out_prefix)", "version": "MySensors-versie" }, "description": "MQTT-gateway instellen" diff --git a/homeassistant/components/nobo_hub/translations/it.json b/homeassistant/components/nobo_hub/translations/it.json index 2b8100c49e2..28bb8f94f34 100644 --- a/homeassistant/components/nobo_hub/translations/it.json +++ b/homeassistant/components/nobo_hub/translations/it.json @@ -25,7 +25,7 @@ }, "user": { "data": { - "device": "Hub scoperti" + "device": "Rilevati hub" }, "description": "Seleziona Nob\u00f8 Ecohub da configurare." } diff --git a/homeassistant/components/oralb/translations/it.json b/homeassistant/components/oralb/translations/it.json index b19851b36ee..7784ed3a240 100644 --- a/homeassistant/components/oralb/translations/it.json +++ b/homeassistant/components/oralb/translations/it.json @@ -2,20 +2,20 @@ "config": { "abort": { "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", - "already_in_progress": "Il processo di configurazione \u00e8 gi\u00e0 in corso", - "no_devices_found": "Nessun dispositivo trovato in rete", + "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", + "no_devices_found": "Nessun dispositivo trovato sulla rete", "not_supported": "Dispositivo non supportato" }, - "flow_title": "{nome}", + "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vuoi impostare {nome}?" + "description": "Vuoi configurare {name}?" }, "user": { "data": { "address": "Dispositivo" }, - "description": "Scegliere un dispositivo da configurare" + "description": "Seleziona un dispositivo da configurare" } } } diff --git a/homeassistant/components/plugwise/translations/select.it.json b/homeassistant/components/plugwise/translations/select.it.json index 97106b6c5b9..3407312874e 100644 --- a/homeassistant/components/plugwise/translations/select.it.json +++ b/homeassistant/components/plugwise/translations/select.it.json @@ -1,10 +1,10 @@ { "state": { "plugwise__dhw_mode": { - "auto": "Automatico", + "auto": "Automatica", "boost": "Velocizza", "comfort": "Comfort", - "off": "Spento" + "off": "Spenta" }, "plugwise__regulation_mode": { "bleeding_cold": "Sfiatamento freddo", diff --git a/homeassistant/components/pure_energie/translations/it.json b/homeassistant/components/pure_energie/translations/it.json index f3b7419fc1d..ebef5a12c99 100644 --- a/homeassistant/components/pure_energie/translations/it.json +++ b/homeassistant/components/pure_energie/translations/it.json @@ -19,7 +19,7 @@ }, "zeroconf_confirm": { "description": "Vuoi aggiungere Pure Energie Meter (`{model}`) a Home Assistant?", - "title": "Scoperto dispositivo Pure Energie Meter" + "title": "Rilevato dispositivo Pure Energie Meter" } } } diff --git a/homeassistant/components/pushover/translations/it.json b/homeassistant/components/pushover/translations/it.json index b42f4e62b77..1fe83629c68 100644 --- a/homeassistant/components/pushover/translations/it.json +++ b/homeassistant/components/pushover/translations/it.json @@ -32,7 +32,7 @@ }, "removed_yaml": { "description": "La configurazione di Pushover tramite YAML \u00e8 stata rimossa.\n\nLa configurazione YAML esistente non sar\u00e0 utilizzata da Home Assistant.\n\nRimuovere la configurazione YAML di Pushover dal file configuration.yaml e riavviare Home Assistant per risolvere il problema.", - "title": "La configurazione Pushover YAML \u00e8 stata rimossa" + "title": "La configurazione YAML di Pushover \u00e8 stata rimossa" } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/sensor.it.json b/homeassistant/components/sensibo/translations/sensor.it.json index cb611c4cf42..1da3b54d850 100644 --- a/homeassistant/components/sensibo/translations/sensor.it.json +++ b/homeassistant/components/sensibo/translations/sensor.it.json @@ -5,7 +5,7 @@ "s": "Sensibile" }, "sensibo__smart_type": { - "feelslike": "Sembra che", + "feelslike": "Percepita", "humidity": "Umidit\u00e0", "temperature": "Temperatura" } diff --git a/homeassistant/components/smappee/translations/it.json b/homeassistant/components/smappee/translations/it.json index 7c18d944ab7..7b3a0472f41 100644 --- a/homeassistant/components/smappee/translations/it.json +++ b/homeassistant/components/smappee/translations/it.json @@ -28,7 +28,7 @@ }, "zeroconf_confirm": { "description": "Vuoi aggiungere il dispositivo Smappee con numero di serie `{serialnumber}` a Home Assistant?", - "title": "Dispositivo Smappee rilevato" + "title": "Rilevato dispositivo Smappee" } } } diff --git a/homeassistant/components/wled/translations/it.json b/homeassistant/components/wled/translations/it.json index 36227a1e9bc..2025bf2f336 100644 --- a/homeassistant/components/wled/translations/it.json +++ b/homeassistant/components/wled/translations/it.json @@ -18,7 +18,7 @@ }, "zeroconf_confirm": { "description": "Vuoi aggiungere il WLED chiamato `{name}` a Home Assistant?", - "title": "Dispositivo WLED rilevato" + "title": "Rilevato dispositivo WLED" } } }, diff --git a/homeassistant/components/xiaomi_ble/translations/nl.json b/homeassistant/components/xiaomi_ble/translations/nl.json index 6b79e0311de..2378ad0d84b 100644 --- a/homeassistant/components/xiaomi_ble/translations/nl.json +++ b/homeassistant/components/xiaomi_ble/translations/nl.json @@ -3,14 +3,34 @@ "abort": { "already_configured": "Apparaat is al geconfigureerd", "already_in_progress": "De configuratie is momenteel al bezig", + "decryption_failed": "De opgegeven `bindkey` werkte niet, de sensorgegevens konden niet worden ontsleuteld. Controleer dit en probeer het opnieuw.", + "expected_24_characters": "Verwachtte een hexadecimale `bindkey` van 24 karakters.", + "expected_32_characters": "Verwachtte een hexadecimale `bindkey` van 32 karakters.", "no_devices_found": "Geen apparaten gevonden op het netwerk", "reauth_successful": "Herauthenticatie geslaagd" }, + "error": { + "decryption_failed": "De opgegeven `bindkey` werkte niet, de sensorgegevens konden niet worden ontsleuteld. Controleer dit en probeer het opnieuw.", + "expected_24_characters": "Verwachtte een hexadecimale `bindkey` van 24 karakters.", + "expected_32_characters": "Verwachtte een hexadecimale `bindkey` van 32 karakters." + }, "flow_title": "{name}", "step": { "bluetooth_confirm": { "description": "Wilt u {name} instellen?" }, + "confirm_slow": { + "description": "Er is de laatste minuut geen aankondigingsbericht van dit apparaat ontvangen, dus we weten niet zeker of dit apparaat encryptie gebruikt of niet. Dit kan zijn omdat het apparaat een trage aankodigings interval heeft. Bevestig om dit apparaat hoe dan ook toe te voegen, dan zal wanneer binnenkort een aankodiging wordt ontvangen worden gevraagd om de `bindkey` als dat nodig is." + }, + "get_encryption_key_4_5": { + "description": "De sensorgegevens van de sensor zijn versleuteld. Om te ontcijferen is een hexadecimale sleutel van 32 tekens nodig." + }, + "get_encryption_key_legacy": { + "description": "De sensorgegevens van de sensor zijn versleuteld. Om te ontcijferen is een hexadecimale sleutel van 24 tekens nodig." + }, + "slow_confirm": { + "description": "Er is de laatste minuut geen aankondigingsbericht van dit apparaat ontvangen, dus we weten niet zeker of dit apparaat encryptie gebruikt of niet. Dit kan zijn omdat het apparaat een trage aankodigings interval heeft. Bevestig om dit apparaat hoe dan ook toe te voegen, dan zal wanneer binnenkort een aankodiging wordt ontvangen worden gevraagd om de `bindkey` als dat nodig is." + }, "user": { "data": { "address": "Apparaat" diff --git a/homeassistant/components/xiaomi_miio/translations/select.nl.json b/homeassistant/components/xiaomi_miio/translations/select.nl.json index 8041e47ab3e..71ffb1a02be 100644 --- a/homeassistant/components/xiaomi_miio/translations/select.nl.json +++ b/homeassistant/components/xiaomi_miio/translations/select.nl.json @@ -1,12 +1,13 @@ { "state": { "xiaomi_miio__display_orientation": { + "forward": "Vooruit", "left": "Links", "right": "Rechts" }, "xiaomi_miio__led_brightness": { "bright": "Helder", - "dim": "Dim", + "dim": "Dimmen", "off": "Uit" }, "xiaomi_miio__ptc_level": { diff --git a/homeassistant/components/zha/translations/nl.json b/homeassistant/components/zha/translations/nl.json index e408a9949bc..f7bb65db547 100644 --- a/homeassistant/components/zha/translations/nl.json +++ b/homeassistant/components/zha/translations/nl.json @@ -55,6 +55,7 @@ "data": { "uploaded_backup_file": "Een bestand uploaden" }, + "description": "Herstel je netwerkinstellingen van ge-upload backup-JSON-bestand. Je kunt deze dpwnloaden van een andere ZHA installatie via **Netwerkinstellingen**, of gebruik een Zigbee2MQTT `coordinator_backup.json` bestand.", "title": "Upload een handmatige back-up" }, "user": { @@ -174,6 +175,7 @@ "data": { "uploaded_backup_file": "Een bestand uploaden" }, + "description": "Herstel je netwerkinstellingen van ge-upload backup-JSON-bestand. Je kunt deze dpwnloaden van een andere ZHA installatie via **Netwerkinstellingen**, of gebruik een Zigbee2MQTT `coordinator_backup.json` bestand.", "title": "Upload een handmatige back-up" } } diff --git a/homeassistant/components/zwave_js/translations/it.json b/homeassistant/components/zwave_js/translations/it.json index d104f510eaf..d4377134977 100644 --- a/homeassistant/components/zwave_js/translations/it.json +++ b/homeassistant/components/zwave_js/translations/it.json @@ -62,7 +62,7 @@ }, "zeroconf_confirm": { "description": "Vuoi aggiungere il server Z-Wave JS con l'ID casa {home_id} trovato in {url} a Home Assistant?", - "title": "Server JS Z-Wave rilevato" + "title": "Rilevato Server JS Z-Wave" } } }, From 8e965eb56f8504fcacefc20f1fa298897dfb40dc Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Sun, 6 Nov 2022 10:21:48 +0100 Subject: [PATCH 0226/1033] Bump pyatmo to 7.4.0 (#81636) --- homeassistant/components/netatmo/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/netatmo/manifest.json b/homeassistant/components/netatmo/manifest.json index 436b6329c1d..ac478282614 100644 --- a/homeassistant/components/netatmo/manifest.json +++ b/homeassistant/components/netatmo/manifest.json @@ -3,7 +3,7 @@ "name": "Netatmo", "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/netatmo", - "requirements": ["pyatmo==7.3.0"], + "requirements": ["pyatmo==7.4.0"], "after_dependencies": ["cloud", "media_source"], "dependencies": ["application_credentials", "webhook"], "codeowners": ["@cgtobi"], diff --git a/requirements_all.txt b/requirements_all.txt index f54c88aad8e..7b48b82d628 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1448,7 +1448,7 @@ pyalmond==0.0.2 pyatag==0.3.5.3 # homeassistant.components.netatmo -pyatmo==7.3.0 +pyatmo==7.4.0 # homeassistant.components.atome pyatome==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7793088d47c..a06d89a41e0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1036,7 +1036,7 @@ pyalmond==0.0.2 pyatag==0.3.5.3 # homeassistant.components.netatmo -pyatmo==7.3.0 +pyatmo==7.4.0 # homeassistant.components.apple_tv pyatv==0.10.3 From cdec4fe110a3a05afdda911e72e466c5902ac821 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Nov 2022 05:38:01 -0600 Subject: [PATCH 0227/1033] Bump oralb-ble to 0.13.0 (#81622) * Bump oralb-ble to 0.11.1 adds some more missing pressure mappings changelog: https://github.com/Bluetooth-Devices/oralb-ble/compare/v0.10.2...v0.11.1 * bump again to update for additional reports * bump again for more data from issue reports --- homeassistant/components/oralb/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/oralb/manifest.json b/homeassistant/components/oralb/manifest.json index ba89c73a240..1738558770e 100644 --- a/homeassistant/components/oralb/manifest.json +++ b/homeassistant/components/oralb/manifest.json @@ -8,7 +8,7 @@ "manufacturer_id": 220 } ], - "requirements": ["oralb-ble==0.10.2"], + "requirements": ["oralb-ble==0.13.0"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 7b48b82d628..c5bccac4cc0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1244,7 +1244,7 @@ openwrt-luci-rpc==1.1.11 openwrt-ubus-rpc==0.0.2 # homeassistant.components.oralb -oralb-ble==0.10.2 +oralb-ble==0.13.0 # homeassistant.components.oru oru==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a06d89a41e0..ed13813fdf5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -889,7 +889,7 @@ open-meteo==0.2.1 openerz-api==0.1.0 # homeassistant.components.oralb -oralb-ble==0.10.2 +oralb-ble==0.13.0 # homeassistant.components.ovo_energy ovoenergy==1.2.0 From 4c65a2f455fe689814266597190bbc8270419dc7 Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Sun, 6 Nov 2022 13:02:59 +0100 Subject: [PATCH 0228/1033] Bump PyXiaomiGateway to 0.14.3 (#81603) Fixes: #80249 --- homeassistant/components/xiaomi_aqara/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/xiaomi_aqara/manifest.json b/homeassistant/components/xiaomi_aqara/manifest.json index a70fb90f961..8152a77a73e 100644 --- a/homeassistant/components/xiaomi_aqara/manifest.json +++ b/homeassistant/components/xiaomi_aqara/manifest.json @@ -3,7 +3,7 @@ "name": "Xiaomi Gateway (Aqara)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/xiaomi_aqara", - "requirements": ["PyXiaomiGateway==0.14.1"], + "requirements": ["PyXiaomiGateway==0.14.3"], "after_dependencies": ["discovery"], "codeowners": ["@danielhiversen", "@syssi"], "zeroconf": ["_miio._udp.local."], diff --git a/requirements_all.txt b/requirements_all.txt index c5bccac4cc0..4cff3722acc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -50,7 +50,7 @@ PyTurboJPEG==1.6.7 PyViCare==2.17.0 # homeassistant.components.xiaomi_aqara -PyXiaomiGateway==0.14.1 +PyXiaomiGateway==0.14.3 # homeassistant.components.remember_the_milk RtmAPI==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ed13813fdf5..94ded26c853 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -46,7 +46,7 @@ PyTurboJPEG==1.6.7 PyViCare==2.17.0 # homeassistant.components.xiaomi_aqara -PyXiaomiGateway==0.14.1 +PyXiaomiGateway==0.14.3 # homeassistant.components.remember_the_milk RtmAPI==0.7.2 From 4056f673b83c7cef774e72efee1f4c2f8668ac16 Mon Sep 17 00:00:00 2001 From: Klaas Schoute Date: Sun, 6 Nov 2022 14:51:19 +0100 Subject: [PATCH 0229/1033] Fix watermeter issue for old P1 Monitor versions (#81570) * Bump the python package version * Add exception to check if user has a water meter --- homeassistant/components/p1_monitor/__init__.py | 5 +++-- homeassistant/components/p1_monitor/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/p1_monitor/__init__.py b/homeassistant/components/p1_monitor/__init__.py index b157f3e8116..e6178ffeb41 100644 --- a/homeassistant/components/p1_monitor/__init__.py +++ b/homeassistant/components/p1_monitor/__init__.py @@ -5,6 +5,7 @@ from typing import TypedDict from p1monitor import ( P1Monitor, + P1MonitorConnectionError, P1MonitorNoDataError, Phases, Settings, @@ -101,8 +102,8 @@ class P1MonitorDataUpdateCoordinator(DataUpdateCoordinator[P1MonitorData]): try: data[SERVICE_WATERMETER] = await self.p1monitor.watermeter() self.has_water_meter = True - except P1MonitorNoDataError: - LOGGER.debug("No watermeter data received from P1 Monitor") + except (P1MonitorNoDataError, P1MonitorConnectionError): + LOGGER.debug("No water meter data received from P1 Monitor") if self.has_water_meter is None: self.has_water_meter = False diff --git a/homeassistant/components/p1_monitor/manifest.json b/homeassistant/components/p1_monitor/manifest.json index 626cff15dfa..2e699276caa 100644 --- a/homeassistant/components/p1_monitor/manifest.json +++ b/homeassistant/components/p1_monitor/manifest.json @@ -3,7 +3,7 @@ "name": "P1 Monitor", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/p1_monitor", - "requirements": ["p1monitor==2.1.0"], + "requirements": ["p1monitor==2.1.1"], "codeowners": ["@klaasnicolaas"], "quality_scale": "platinum", "iot_class": "local_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 4cff3722acc..61246422853 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1256,7 +1256,7 @@ orvibo==1.1.1 ovoenergy==1.2.0 # homeassistant.components.p1_monitor -p1monitor==2.1.0 +p1monitor==2.1.1 # homeassistant.components.mqtt # homeassistant.components.shiftr diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 94ded26c853..5a7285ced42 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -895,7 +895,7 @@ oralb-ble==0.13.0 ovoenergy==1.2.0 # homeassistant.components.p1_monitor -p1monitor==2.1.0 +p1monitor==2.1.1 # homeassistant.components.mqtt # homeassistant.components.shiftr From b18a1e6d7c1c89375659cfd39e99e26e42d23bf5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Nov 2022 08:12:58 -0600 Subject: [PATCH 0230/1033] Bump dbus-fast to 1.72.0 (#81574) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 0b94e1bdfd9..a0a9629c9f4 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.8.2", "bluetooth-adapters==0.7.0", "bluetooth-auto-recovery==0.3.6", - "dbus-fast==1.71.0" + "dbus-fast==1.72.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index eb580796a97..f6b700be209 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.3 -dbus-fast==1.71.0 +dbus-fast==1.72.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 61246422853..71e3e4725a2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -546,7 +546,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.71.0 +dbus-fast==1.72.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5a7285ced42..87a427efa45 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -426,7 +426,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.71.0 +dbus-fast==1.72.0 # homeassistant.components.debugpy debugpy==1.6.3 From 6c659c0d68174a516ae09a833bf9fa98074d0d50 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Sun, 6 Nov 2022 10:11:49 -0500 Subject: [PATCH 0231/1033] Add repair warning about UniFi Protect Early Access (#81658) --- .../components/unifiprotect/__init__.py | 17 ++++++++++++++++- .../components/unifiprotect/strings.json | 6 ++++++ .../unifiprotect/translations/en.json | 10 ++++++---- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/unifiprotect/__init__.py b/homeassistant/components/unifiprotect/__init__.py index 30b1d1ad56d..649d62c6fee 100644 --- a/homeassistant/components/unifiprotect/__init__.py +++ b/homeassistant/components/unifiprotect/__init__.py @@ -21,8 +21,9 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady -from homeassistant.helpers import device_registry as dr +from homeassistant.helpers import device_registry as dr, issue_registry as ir from homeassistant.helpers.aiohttp_client import async_create_clientsession +from homeassistant.helpers.issue_registry import IssueSeverity from .const import ( CONF_ALL_UPDATES, @@ -101,6 +102,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, data_service.async_stop) ) + protect_version = data_service.api.bootstrap.nvr.version + if protect_version.is_prerelease: + ir.async_create_issue( + hass, + DOMAIN, + f"ea_warning_{protect_version}", + is_fixable=False, + is_persistent=False, + learn_more_url="https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access", + severity=IssueSeverity.WARNING, + translation_key="ea_warning", + translation_placeholders={"version": str(protect_version)}, + ) + return True diff --git a/homeassistant/components/unifiprotect/strings.json b/homeassistant/components/unifiprotect/strings.json index d3cfe24abd2..2967ba8c82e 100644 --- a/homeassistant/components/unifiprotect/strings.json +++ b/homeassistant/components/unifiprotect/strings.json @@ -54,5 +54,11 @@ } } } + }, + "issues": { + "ea_warning": { + "title": "{version} is an Early Access version of UniFi Protect", + "description": "You are using {version} of UniFi Protect. Early Access versions are not supported by Home Assistant and may cause your UniFi Protect integration to break or not work as expected." + } } } diff --git a/homeassistant/components/unifiprotect/translations/en.json b/homeassistant/components/unifiprotect/translations/en.json index c6050d05284..9fd074ac585 100644 --- a/homeassistant/components/unifiprotect/translations/en.json +++ b/homeassistant/components/unifiprotect/translations/en.json @@ -41,16 +41,18 @@ } } }, + "issues": { + "ea_warning": { + "description": "You are using {version} of UniFi Protect. Early Access versions are not supported by Home Assistant and may cause your UniFi Protect integration to break or not work as expected.", + "title": "{version} is an Early Access version of UniFi Protect" + } + }, "options": { - "error": { - "invalid_mac_list": "Must be a list of MAC addresses seperated by commas" - }, "step": { "init": { "data": { "all_updates": "Realtime metrics (WARNING: Greatly increases CPU usage)", "disable_rtsp": "Disable the RTSP stream", - "ignored_devices": "Comma separated list of MAC addresses of devices to ignore", "max_media": "Max number of event to load for Media Browser (increases RAM usage)", "override_connection_host": "Override Connection Host" }, From 3630de909cc311912cee44e1c450506d71ffa1cf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Nov 2022 09:47:07 -0600 Subject: [PATCH 0232/1033] Bump aiohomekit to 2.2.17 (#81657) Improve BLE pairing reliability, especially with esp32 proxies changelog: https://github.com/Jc2k/aiohomekit/compare/2.2.16...2.2.17 --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index e4a2b5d79bb..7a088993e9f 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.16"], + "requirements": ["aiohomekit==2.2.17"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index 71e3e4725a2..0ac216fc7db 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.16 +aiohomekit==2.2.17 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 87a427efa45..fdb7b49514b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.16 +aiohomekit==2.2.17 # homeassistant.components.emulated_hue # homeassistant.components.http From d3529cb346b2d55a7b12d6a5c4cd57cdd24d593e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Nov 2022 12:59:32 -0600 Subject: [PATCH 0233/1033] Add missing h2 dep to iaqualink (#81630) fixes https://github.com/home-assistant/core/issues/81439 --- homeassistant/components/iaqualink/manifest.json | 2 +- requirements_all.txt | 3 +++ requirements_test_all.txt | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/iaqualink/manifest.json b/homeassistant/components/iaqualink/manifest.json index d5b7d7de0d8..f274cd5ea1c 100644 --- a/homeassistant/components/iaqualink/manifest.json +++ b/homeassistant/components/iaqualink/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/iaqualink/", "codeowners": ["@flz"], - "requirements": ["iaqualink==0.5.0"], + "requirements": ["iaqualink==0.5.0", "h2==4.1.0"], "iot_class": "cloud_polling", "loggers": ["iaqualink"] } diff --git a/requirements_all.txt b/requirements_all.txt index 0ac216fc7db..9db4423df2d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -821,6 +821,9 @@ gstreamer-player==1.1.2 # homeassistant.components.profiler guppy3==3.1.2 +# homeassistant.components.iaqualink +h2==4.1.0 + # homeassistant.components.homekit ha-HAP-python==4.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fdb7b49514b..80e2f39ba74 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -613,6 +613,9 @@ gspread==5.5.0 # homeassistant.components.profiler guppy3==3.1.2 +# homeassistant.components.iaqualink +h2==4.1.0 + # homeassistant.components.homekit ha-HAP-python==4.5.2 From 496f78bae5f475dfc44c571893d9a612ae0b63a1 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sun, 6 Nov 2022 21:37:44 +0200 Subject: [PATCH 0234/1033] FIX: patch correct async_setup_entry in tilt_ble (#81671) --- tests/components/tilt_ble/test_config_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/tilt_ble/test_config_flow.py b/tests/components/tilt_ble/test_config_flow.py index c2bb20e0dd6..789bb86e30c 100644 --- a/tests/components/tilt_ble/test_config_flow.py +++ b/tests/components/tilt_ble/test_config_flow.py @@ -21,7 +21,7 @@ async def test_async_step_bluetooth_valid_device(hass): assert result["type"] == FlowResultType.FORM assert result["step_id"] == "bluetooth_confirm" with patch( - "homeassistant.components.govee_ble.async_setup_entry", return_value=True + "homeassistant.components.tilt_ble.async_setup_entry", return_value=True ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} From d62bac9c5948cb7d9c89cb25ac8a0cd1d63906e0 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 6 Nov 2022 12:38:55 -0700 Subject: [PATCH 0235/1033] Fix missing RainMachine restrictions switches (#81673) --- homeassistant/components/rainmachine/switch.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/rainmachine/switch.py b/homeassistant/components/rainmachine/switch.py index 35b0e5eab2b..66081588f27 100644 --- a/homeassistant/components/rainmachine/switch.py +++ b/homeassistant/components/rainmachine/switch.py @@ -241,6 +241,7 @@ async def async_setup_entry( # Add switches to control restrictions: for description in RESTRICTIONS_SWITCH_DESCRIPTIONS: + coordinator = data.coordinators[description.api_category] if not key_exists(coordinator.data, description.data_key): continue entities.append(RainMachineRestrictionSwitch(entry, data, description)) From df7000f96d5c3f7a01b545eb19c33eb9d9367d0f Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sun, 6 Nov 2022 21:23:48 +0100 Subject: [PATCH 0236/1033] Always use Celsius in Shelly integration, part 2 (#81602) * Always use Celsius in Shelly integration * Update homeassistant/components/shelly/sensor.py Co-authored-by: Aarni Koskela * Restore unit from the registry during HA startup Co-authored-by: Aarni Koskela --- homeassistant/components/shelly/entity.py | 5 ++++ homeassistant/components/shelly/sensor.py | 30 +++++++++++++++-------- homeassistant/components/shelly/utils.py | 11 ++------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index fd92ea41408..96f566f6a2e 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -9,6 +9,7 @@ from aioshelly.block_device import Block from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import device_registry, entity, entity_registry @@ -615,6 +616,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti """Initialize the sleeping sensor.""" self.sensors = sensors self.last_state: StateType = None + self.last_unit: str | None = None self.coordinator = coordinator self.attribute = attribute self.block: Block | None = block # type: ignore[assignment] @@ -644,6 +646,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti if last_state is not None: self.last_state = last_state.state + self.last_unit = last_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) @callback def _update_callback(self) -> None: @@ -696,6 +699,7 @@ class ShellySleepingRpcAttributeEntity(ShellyRpcAttributeEntity, RestoreEntity): ) -> None: """Initialize the sleeping sensor.""" self.last_state: StateType = None + self.last_unit: str | None = None self.coordinator = coordinator self.key = key self.attribute = attribute @@ -725,3 +729,4 @@ class ShellySleepingRpcAttributeEntity(ShellyRpcAttributeEntity, RestoreEntity): if last_state is not None: self.last_state = last_state.state + self.last_unit = last_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index cf1eb7508c7..922a5ac8b5c 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -47,7 +47,7 @@ from .entity import ( async_setup_entry_rest, async_setup_entry_rpc, ) -from .utils import get_device_entry_gen, get_device_uptime, temperature_unit +from .utils import get_device_entry_gen, get_device_uptime @dataclass @@ -79,7 +79,7 @@ SENSORS: Final = { ("device", "deviceTemp"): BlockSensorDescription( key="device|deviceTemp", name="Device Temperature", - unit_fn=temperature_unit, + native_unit_of_measurement=TEMP_CELSIUS, value=lambda value: round(value, 1), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -221,7 +221,7 @@ SENSORS: Final = { ("sensor", "temp"): BlockSensorDescription( key="sensor|temp", name="Temperature", - unit_fn=temperature_unit, + native_unit_of_measurement=TEMP_CELSIUS, value=lambda value: round(value, 1), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -230,7 +230,7 @@ SENSORS: Final = { ("sensor", "extTemp"): BlockSensorDescription( key="sensor|extTemp", name="Temperature", - unit_fn=temperature_unit, + native_unit_of_measurement=TEMP_CELSIUS, value=lambda value: round(value, 1), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -499,8 +499,6 @@ class BlockSensor(ShellyBlockAttributeEntity, SensorEntity): super().__init__(coordinator, block, attribute, description) self._attr_native_unit_of_measurement = description.native_unit_of_measurement - if unit_fn := description.unit_fn: - self._attr_native_unit_of_measurement = unit_fn(block.info(attribute)) @property def native_value(self) -> StateType: @@ -547,10 +545,6 @@ class BlockSleepingSensor(ShellySleepingBlockAttributeEntity, SensorEntity): """Initialize the sleeping sensor.""" super().__init__(coordinator, block, attribute, description, entry, sensors) - self._attr_native_unit_of_measurement = description.native_unit_of_measurement - if block and (unit_fn := description.unit_fn): - self._attr_native_unit_of_measurement = unit_fn(block.info(attribute)) - @property def native_value(self) -> StateType: """Return value of sensor.""" @@ -559,6 +553,14 @@ class BlockSleepingSensor(ShellySleepingBlockAttributeEntity, SensorEntity): return self.last_state + @property + def native_unit_of_measurement(self) -> str | None: + """Return the unit of measurement of the sensor, if any.""" + if self.block is not None: + return self.entity_description.native_unit_of_measurement + + return self.last_unit + class RpcSleepingSensor(ShellySleepingRpcAttributeEntity, SensorEntity): """Represent a RPC sleeping sensor.""" @@ -572,3 +574,11 @@ class RpcSleepingSensor(ShellySleepingRpcAttributeEntity, SensorEntity): return self.attribute_value return self.last_state + + @property + def native_unit_of_measurement(self) -> str | None: + """Return the unit of measurement of the sensor, if any.""" + if self.coordinator.device.initialized: + return self.entity_description.native_unit_of_measurement + + return self.last_unit diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index a13d84d32be..bf242d47e6c 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -5,13 +5,13 @@ from datetime import datetime, timedelta from typing import Any, cast from aiohttp.web import Request, WebSocketResponse -from aioshelly.block_device import BLOCK_VALUE_UNIT, COAP, Block, BlockDevice +from aioshelly.block_device import COAP, Block, BlockDevice from aioshelly.const import MODEL_NAMES from aioshelly.rpc_device import RpcDevice, WsServer from homeassistant.components.http import HomeAssistantView from homeassistant.config_entries import ConfigEntry -from homeassistant.const import EVENT_HOMEASSISTANT_STOP, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry, entity_registry, singleton from homeassistant.helpers.typing import EventType @@ -43,13 +43,6 @@ def async_remove_shelly_entity( entity_reg.async_remove(entity_id) -def temperature_unit(block_info: dict[str, Any]) -> str: - """Detect temperature unit.""" - if block_info[BLOCK_VALUE_UNIT] == "F": - return TEMP_FAHRENHEIT - return TEMP_CELSIUS - - def get_block_device_name(device: BlockDevice) -> str: """Naming for device.""" return cast(str, device.settings["name"] or device.settings["device"]["hostname"]) From 04d01cefb24b27af1039ba2adc8889dfa0943cd9 Mon Sep 17 00:00:00 2001 From: Artem Draft Date: Sun, 6 Nov 2022 23:26:40 +0300 Subject: [PATCH 0237/1033] Fix Bravia TV options flow when device is off (#81644) * Fix options flow when tv is off * abort with message --- homeassistant/components/braviatv/config_flow.py | 6 +++++- homeassistant/components/braviatv/strings.json | 3 +++ homeassistant/components/braviatv/translations/en.json | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/braviatv/config_flow.py b/homeassistant/components/braviatv/config_flow.py index f5c7826b825..45a2bad0036 100644 --- a/homeassistant/components/braviatv/config_flow.py +++ b/homeassistant/components/braviatv/config_flow.py @@ -262,7 +262,11 @@ class BraviaTVOptionsFlowHandler(config_entries.OptionsFlow): self.config_entry.entry_id ] - await coordinator.async_update_sources() + try: + await coordinator.async_update_sources() + except BraviaTVError: + return self.async_abort(reason="failed_update") + sources = coordinator.source_map.values() self.source_list = [item["title"] for item in sources] return await self.async_step_user() diff --git a/homeassistant/components/braviatv/strings.json b/homeassistant/components/braviatv/strings.json index 4dd08135896..ac651955166 100644 --- a/homeassistant/components/braviatv/strings.json +++ b/homeassistant/components/braviatv/strings.json @@ -48,6 +48,9 @@ "ignored_sources": "List of ignored sources" } } + }, + "abort": { + "failed_update": "An error occurred while updating the list of sources.\n\n Ensure that your TV is turned on before trying to set it up." } } } diff --git a/homeassistant/components/braviatv/translations/en.json b/homeassistant/components/braviatv/translations/en.json index c3341d33112..39c95c706de 100644 --- a/homeassistant/components/braviatv/translations/en.json +++ b/homeassistant/components/braviatv/translations/en.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "An error occurred while updating the list of sources.\n\n Ensure that your TV is turned on before trying to set it up." + }, "step": { "user": { "data": { From e7b5aaec47feb1ebb167c5ad73a313e4225f9608 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Nov 2022 17:13:14 -0600 Subject: [PATCH 0238/1033] Bump aioesphomeapi to 11.4.3 (#81676) --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 64cd6b4029c..2070b8ae362 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==11.4.2"], + "requirements": ["aioesphomeapi==11.4.3"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/requirements_all.txt b/requirements_all.txt index 9db4423df2d..eecb2b498e6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -156,7 +156,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.4.2 +aioesphomeapi==11.4.3 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 80e2f39ba74..375c30ff5e1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -143,7 +143,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.4.2 +aioesphomeapi==11.4.3 # homeassistant.components.flo aioflo==2021.11.0 From c3946159d8f6d639e3d3dfbb64dad72feda5fd57 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Nov 2022 17:45:53 -0600 Subject: [PATCH 0239/1033] Bump bleak-retry-connector to 2.8.3 (#81675) Improves chances of making a BLE connection with the ESP32s changelog: https://github.com/Bluetooth-Devices/bleak-retry-connector/compare/v2.8.2...v2.8.3 --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index a0a9629c9f4..fbf9276c9eb 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.1", - "bleak-retry-connector==2.8.2", + "bleak-retry-connector==2.8.3", "bluetooth-adapters==0.7.0", "bluetooth-auto-recovery==0.3.6", "dbus-fast==1.72.0" diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index f6b700be209..14a4f31f521 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.8.2 +bleak-retry-connector==2.8.3 bleak==0.19.1 bluetooth-adapters==0.7.0 bluetooth-auto-recovery==0.3.6 diff --git a/requirements_all.txt b/requirements_all.txt index eecb2b498e6..dd99cec3e3a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -419,7 +419,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.2 +bleak-retry-connector==2.8.3 # homeassistant.components.bluetooth bleak==0.19.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 375c30ff5e1..79d8224df05 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -343,7 +343,7 @@ bellows==0.34.2 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.2 +bleak-retry-connector==2.8.3 # homeassistant.components.bluetooth bleak==0.19.1 From 3cfcb93d706d407ce2bd98a2dc58bd5681d7c914 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Nov 2022 18:04:45 -0600 Subject: [PATCH 0240/1033] Bump aiohomekit to 2.2.18 (#81693) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 7a088993e9f..b18f35390b7 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.17"], + "requirements": ["aiohomekit==2.2.18"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index dd99cec3e3a..58c86250c42 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.17 +aiohomekit==2.2.18 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 79d8224df05..19c0777a59f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.17 +aiohomekit==2.2.18 # homeassistant.components.emulated_hue # homeassistant.components.http From 2a42a58ec4ea26802c637fc80d00999989bdf8cd Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Mon, 7 Nov 2022 01:16:58 +0100 Subject: [PATCH 0241/1033] Restore negative values for shelly power factors (#81689) fixes undefined --- homeassistant/components/shelly/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index 922a5ac8b5c..7d99f015c5e 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -140,7 +140,7 @@ SENSORS: Final = { key="emeter|powerFactor", name="Power Factor", native_unit_of_measurement=PERCENTAGE, - value=lambda value: abs(round(value * 100, 1)), + value=lambda value: round(value * 100, 1), device_class=SensorDeviceClass.POWER_FACTOR, state_class=SensorStateClass.MEASUREMENT, ), From e2788f83210bce44d73036f331c17e9d102c02c8 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 7 Nov 2022 00:26:17 +0000 Subject: [PATCH 0242/1033] [ci skip] Translation update --- .../components/airq/translations/ja.json | 20 +++++++++++++++++++ .../components/auth/translations/bg.json | 2 +- .../forecast_solar/translations/ca.json | 2 +- .../google_sheets/translations/nl.json | 6 ++++++ .../components/hassio/translations/ca.json | 16 +++++++++++++-- .../components/netatmo/translations/it.json | 2 +- .../components/scrape/translations/ca.json | 1 + .../unifiprotect/translations/ca.json | 6 ++++++ .../unifiprotect/translations/en.json | 4 ++++ .../unifiprotect/translations/et.json | 6 ++++++ .../unifiprotect/translations/it.json | 6 ++++++ .../unifiprotect/translations/ru.json | 6 ++++++ 12 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 homeassistant/components/airq/translations/ja.json diff --git a/homeassistant/components/airq/translations/ja.json b/homeassistant/components/airq/translations/ja.json new file mode 100644 index 00000000000..8bbc8791dde --- /dev/null +++ b/homeassistant/components/airq/translations/ja.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "invalid_input": "\u7121\u52b9\u306a\u30db\u30b9\u30c8\u540d\u307e\u305f\u306fIP\u30a2\u30c9\u30ec\u30b9" + }, + "step": { + "user": { + "data": { + "ip_address": "IP\u30a2\u30c9\u30ec\u30b9", + "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/translations/bg.json b/homeassistant/components/auth/translations/bg.json index d07e20a854c..4ccfc0ae79b 100644 --- a/homeassistant/components/auth/translations/bg.json +++ b/homeassistant/components/auth/translations/bg.json @@ -25,7 +25,7 @@ }, "step": { "init": { - "description": "\u0417\u0430 \u0434\u0430 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u0442\u0435 \u0434\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u0432\u043e-\u0431\u0430\u0437\u0438\u0440\u0430\u043d\u0438 \u0435\u0434\u043d\u043e\u043a\u0440\u0430\u0442\u043d\u0438 \u043f\u0430\u0440\u043e\u043b\u0438, \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u0439\u0442\u0435 QR \u043a\u043e\u0434\u0430 \u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0442\u043e\u0440\u0430. \u0410\u043a\u043e \u043d\u044f\u043c\u0430\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0412\u0438 \u043f\u0440\u0435\u043f\u043e\u0440\u044a\u0447\u0432\u0430\u043c\u0435 \u0438\u043b\u0438 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \u0438\u043b\u0438 [Authy](https://authy.com/).\n\n{qr_code}\n\n\u0421\u043b\u0435\u0434 \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u0434\u0430, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 6-\u0442\u0435 \u0446\u0438\u0444\u0440\u0438 \u043e\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u0437\u0430 \u0434\u0430 \u043f\u043e\u0442\u0432\u044a\u0440\u0434\u0438\u0442\u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435\u0442\u043e. \u0410\u043a\u043e \u0438\u043c\u0430\u0442\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u043f\u0440\u0438 \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 QR \u043a\u043e\u0434\u0430, \u043d\u0430\u043f\u0440\u0430\u0432\u0435\u0442\u0435 \u0440\u044a\u0447\u043d\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441 \u043a\u043e\u0434 **`{code}`**.", + "description": "\u0417\u0430 \u0434\u0430 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u0442\u0435 \u0434\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u0432\u043e-\u0431\u0430\u0437\u0438\u0440\u0430\u043d\u0438 \u0435\u0434\u043d\u043e\u043a\u0440\u0430\u0442\u043d\u0438 \u043f\u0430\u0440\u043e\u043b\u0438, \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u0439\u0442\u0435 QR \u043a\u043e\u0434\u0430 \u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0442\u043e\u0440\u0430. \u0410\u043a\u043e \u043d\u044f\u043c\u0430\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0412\u0438 \u043f\u0440\u0435\u043f\u043e\u0440\u044a\u0447\u0432\u0430\u043c\u0435 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \u0438\u043b\u0438 [Authy](https://authy.com/).\n\n{qr_code}\n\n\u0421\u043b\u0435\u0434 \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u0434\u0430, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 6-\u0442\u0435 \u0446\u0438\u0444\u0440\u0438 \u043e\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u0437\u0430 \u0434\u0430 \u043f\u043e\u0442\u0432\u044a\u0440\u0434\u0438\u0442\u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435\u0442\u043e. \u0410\u043a\u043e \u0438\u043c\u0430\u0442\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u043f\u0440\u0438 \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 QR \u043a\u043e\u0434\u0430, \u043d\u0430\u043f\u0440\u0430\u0432\u0435\u0442\u0435 \u0440\u044a\u0447\u043d\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441 \u043a\u043e\u0434 **`{code}`**.", "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043d\u0430 \u0434\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0447\u0440\u0435\u0437 TOTP" } }, diff --git a/homeassistant/components/forecast_solar/translations/ca.json b/homeassistant/components/forecast_solar/translations/ca.json index 141ad11c165..b2561bb0a79 100644 --- a/homeassistant/components/forecast_solar/translations/ca.json +++ b/homeassistant/components/forecast_solar/translations/ca.json @@ -25,7 +25,7 @@ "inverter_size": "Pot\u00e8ncia de l'inversor (Watts)", "modules power": "Pot\u00e8ncia m\u00e0xima total dels panells solars" }, - "description": "Aquests valors permeten ajustar els resultats de Solar.Forecast. Consulta la documentaci\u00f3 si tens dubtes sobre algun camp." + "description": "Aquests valors permeten ajustar els resultats de Forecast.Solar. Consulta la documentaci\u00f3 si tens dubtes sobre algun camp." } } } diff --git a/homeassistant/components/google_sheets/translations/nl.json b/homeassistant/components/google_sheets/translations/nl.json index d530b4e3add..884233629ea 100644 --- a/homeassistant/components/google_sheets/translations/nl.json +++ b/homeassistant/components/google_sheets/translations/nl.json @@ -4,13 +4,18 @@ "already_configured": "Account is al geconfigureerd", "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken", + "create_spreadsheet_failure": "Fout bij het openen van een nieuw werkblad, zie de error log voor details", "invalid_access_token": "Ongeldig toegangstoken", "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", "oauth_error": "Ongeldige tokengegevens ontvangen.", + "open_spreadsheet_failure": "Fout bij het openen van een nieuw werkblad, zie de error log voor details", "reauth_successful": "Herauthenticatie geslaagd", "timeout_connect": "Time-out bij het maken van verbinding", "unknown": "Onverwachte fout" }, + "create_entry": { + "default": "Succesvol aangemeld en werkblad gemaakt op: {url}" + }, "step": { "auth": { "title": "Google-account koppelen" @@ -19,6 +24,7 @@ "title": "Kies een authenticatie methode" }, "reauth_confirm": { + "description": "De Google Sheets integratie vereist dat je opnieuw inlogt met je account", "title": "Integratie herauthenticeren" } } diff --git a/homeassistant/components/hassio/translations/ca.json b/homeassistant/components/hassio/translations/ca.json index 71a0066b58a..9b10bbc55e5 100644 --- a/homeassistant/components/hassio/translations/ca.json +++ b/homeassistant/components/hassio/translations/ca.json @@ -1,7 +1,7 @@ { "issues": { "unhealthy": { - "description": "El sistema no \u00e9s saludable a causa de '{reason}'. Clica l'enlla\u00e7 per obtenir m\u00e9s informaci\u00f3 sobre qu\u00e8 falla aix\u00f2 i com solucionar-ho.", + "description": "El sistema no \u00e9s saludable a causa de {reason}. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 i per saber com solucionar-ho.", "title": "Sistema no saludable - {reason}" }, "unhealthy_docker": { @@ -25,7 +25,7 @@ "title": "Sistema no saludable - Codi no fiable" }, "unsupported": { - "description": "El sistema no \u00e9s compatible a causa de '{reason}'. Clica l'enlla\u00e7 per obtenir m\u00e9s informaci\u00f3 sobre qu\u00e8 significa aix\u00f2 i com tornar a un sistema compatible.", + "description": "El sistema no \u00e9s compatible a causa de {reason}. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 i per saber com solucionar-ho.", "title": "Sistema no compatible - {reason}" }, "unsupported_apparmor": { @@ -36,6 +36,18 @@ "description": "El sistema no \u00e9s compatible perqu\u00e8 s'utilitza una versi\u00f3 de Docker CGroup incorrecta. Clica l'enlla\u00e7 per con\u00e8ixer la versi\u00f3 correcta i sobre com solucionar-ho.", "title": "Sistema no compatible - Versi\u00f3 de CGroup" }, + "unsupported_connectivity_check": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 Home Assistant no pot determinar quan hi ha connexi\u00f3 a internet disponible. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Comprovaci\u00f3 de connectivitat desactivada" + }, + "unsupported_content_trust": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 Home Assistant no pot verificar que els continguts en execuci\u00f3 s\u00f3n fiables i no han estat modificats per cap atacant. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Comprovaci\u00f3 de continguts fiables desactivada" + }, + "unsupported_dbus": { + "description": "El sistema no \u00e9s compatible perqu\u00e8 D-Bus no est\u00e0 funcionant correctament. Les comunicacions entre el Supervisor i l'amfitri\u00f3 no poden funcionar sense D-Bus. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", + "title": "Sistema no compatible - Problemes amb D-Bus" + }, "unsupported_dns_server": { "description": "El sistema no \u00e9s compatible perqu\u00e8 el servidor DNS proporcionat no funciona correctament i la segona opci\u00f3 est\u00e0 desactivada. Clica l'enlla\u00e7 per a m\u00e9s informaci\u00f3 sobre com solucionar-ho.", "title": "Sistema no compatible - Problemes el servidor DNS" diff --git a/homeassistant/components/netatmo/translations/it.json b/homeassistant/components/netatmo/translations/it.json index b2210a4375c..4511296efa5 100644 --- a/homeassistant/components/netatmo/translations/it.json +++ b/homeassistant/components/netatmo/translations/it.json @@ -24,7 +24,7 @@ "trigger_subtype": { "away": "Fuori casa", "hg": "protezione antigelo", - "schedule": "programma" + "schedule": "calendarizzazione" }, "trigger_type": { "alarm_started": "{entity_name} ha rilevato un allarme", diff --git a/homeassistant/components/scrape/translations/ca.json b/homeassistant/components/scrape/translations/ca.json index 2503602ab33..8998a7a9c69 100644 --- a/homeassistant/components/scrape/translations/ca.json +++ b/homeassistant/components/scrape/translations/ca.json @@ -38,6 +38,7 @@ }, "issues": { "moved_yaml": { + "description": "La configuraci\u00f3 de Scrape mitjan\u00e7ant YAML s'ha mogut a una integraci\u00f3.\n\nLa configuraci\u00f3 YAML actual continuar\u00e0 funcionant en les dues pr\u00f2ximes versions de Home Assistant.\n\nMigra la teva configuraci\u00f3 YAML a la nova integraci\u00f3, consulta la documentaci\u00f3 per saber com fer-ho.", "title": "La configuraci\u00f3 YAML de Scrape s'ha eliminat" } }, diff --git a/homeassistant/components/unifiprotect/translations/ca.json b/homeassistant/components/unifiprotect/translations/ca.json index 1d43956510a..7532b2d9e60 100644 --- a/homeassistant/components/unifiprotect/translations/ca.json +++ b/homeassistant/components/unifiprotect/translations/ca.json @@ -41,6 +41,12 @@ } } }, + "issues": { + "ea_warning": { + "description": "Est\u00e0s utilitzant la {version} d'UniFi Protect. Les versions d'acc\u00e9s anticipat no s\u00f3n compatibles amb Home Assistant i poden fer que la teva integraci\u00f3 d'UniFi Protect s'espatlli o no funcioni correctament.", + "title": "{version} \u00e9s una versi\u00f3 d'acc\u00e9s anticipat d'UniFi Protect" + } + }, "options": { "error": { "invalid_mac_list": "Ha de ser una llista d'adreces MAC separades per comes" diff --git a/homeassistant/components/unifiprotect/translations/en.json b/homeassistant/components/unifiprotect/translations/en.json index 9fd074ac585..3b6e74db341 100644 --- a/homeassistant/components/unifiprotect/translations/en.json +++ b/homeassistant/components/unifiprotect/translations/en.json @@ -48,11 +48,15 @@ } }, "options": { + "error": { + "invalid_mac_list": "Must be a list of MAC addresses seperated by commas" + }, "step": { "init": { "data": { "all_updates": "Realtime metrics (WARNING: Greatly increases CPU usage)", "disable_rtsp": "Disable the RTSP stream", + "ignored_devices": "Comma separated list of MAC addresses of devices to ignore", "max_media": "Max number of event to load for Media Browser (increases RAM usage)", "override_connection_host": "Override Connection Host" }, diff --git a/homeassistant/components/unifiprotect/translations/et.json b/homeassistant/components/unifiprotect/translations/et.json index 4bd402fa1c2..9ddf72f925d 100644 --- a/homeassistant/components/unifiprotect/translations/et.json +++ b/homeassistant/components/unifiprotect/translations/et.json @@ -41,6 +41,12 @@ } } }, + "issues": { + "ea_warning": { + "description": "Kasutad UniFi Protecti {version}. Home Assistant ei toeta varajase juurdep\u00e4\u00e4su versioone ja see v\u00f5ib p\u00f5hjustada UniFi Protecti sidumise katkemist v\u00f5i ei t\u00f6\u00f6ta see ootusp\u00e4raselt.", + "title": "{version} on UniFi Protecti varajase juurdep\u00e4\u00e4su versioon" + } + }, "options": { "error": { "invalid_mac_list": "Peab olema komadega eraldatud MAC-aadresside loend" diff --git a/homeassistant/components/unifiprotect/translations/it.json b/homeassistant/components/unifiprotect/translations/it.json index 712df7b7f2a..d44b3fccf31 100644 --- a/homeassistant/components/unifiprotect/translations/it.json +++ b/homeassistant/components/unifiprotect/translations/it.json @@ -41,6 +41,12 @@ } } }, + "issues": { + "ea_warning": { + "description": "Stai utilizzando {version} di UniFi Protect. Le versioni di accesso anticipato non sono supportate da Home Assistant e potrebbero causare l'interruzione dell'integrazione di UniFi Protect o non funzionare come previsto.", + "title": "{version} \u00e8 una versione di accesso anticipato di UniFi Protect" + } + }, "options": { "error": { "invalid_mac_list": "Deve essere un elenco di indirizzi MAC separati da virgole" diff --git a/homeassistant/components/unifiprotect/translations/ru.json b/homeassistant/components/unifiprotect/translations/ru.json index c1e5558c204..2e8e0687f0a 100644 --- a/homeassistant/components/unifiprotect/translations/ru.json +++ b/homeassistant/components/unifiprotect/translations/ru.json @@ -41,6 +41,12 @@ } } }, + "issues": { + "ea_warning": { + "description": "\u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 UniFi Protect {version}. \u0412\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f Home Assistant \u0438 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.", + "title": "UniFi Protect {version} \u2014 \u044d\u0442\u043e \u0432\u0435\u0440\u0441\u0438\u044f \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430" + } + }, "options": { "error": { "invalid_mac_list": "\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0431\u043e\u0439 \u0441\u043f\u0438\u0441\u043e\u043a MAC-\u0430\u0434\u0440\u0435\u0441\u043e\u0432, \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u044f\u0442\u044b\u043c\u0438." From 9c3bd22e7746a681d1d564fabbd6c66eeb273614 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Nov 2022 19:07:21 -0600 Subject: [PATCH 0243/1033] Bump bleak to 0.19.2 (#81688) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index fbf9276c9eb..923ab248a2d 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -6,7 +6,7 @@ "after_dependencies": ["hassio"], "quality_scale": "internal", "requirements": [ - "bleak==0.19.1", + "bleak==0.19.2", "bleak-retry-connector==2.8.3", "bluetooth-adapters==0.7.0", "bluetooth-auto-recovery==0.3.6", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 14a4f31f521..2aa2179a890 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -11,7 +11,7 @@ attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 bleak-retry-connector==2.8.3 -bleak==0.19.1 +bleak==0.19.2 bluetooth-adapters==0.7.0 bluetooth-auto-recovery==0.3.6 certifi>=2021.5.30 diff --git a/requirements_all.txt b/requirements_all.txt index 58c86250c42..bdebb6f8652 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -422,7 +422,7 @@ bizkaibus==0.1.1 bleak-retry-connector==2.8.3 # homeassistant.components.bluetooth -bleak==0.19.1 +bleak==0.19.2 # homeassistant.components.blebox blebox_uniapi==2.1.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 19c0777a59f..c05a57e8508 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -346,7 +346,7 @@ bimmer_connected==0.10.4 bleak-retry-connector==2.8.3 # homeassistant.components.bluetooth -bleak==0.19.1 +bleak==0.19.2 # homeassistant.components.blebox blebox_uniapi==2.1.3 From 499839c5969971ec8ca172d29bf5f21af77e775d Mon Sep 17 00:00:00 2001 From: tstabrawa <59430211+tstabrawa@users.noreply.github.com> Date: Sun, 6 Nov 2022 19:16:22 -0600 Subject: [PATCH 0244/1033] Fix nuheat temporary hold time (#81635) Co-authored-by: J. Nick Koston --- CODEOWNERS | 2 ++ homeassistant/components/nuheat/climate.py | 30 ++----------------- homeassistant/components/nuheat/const.py | 7 ----- homeassistant/components/nuheat/manifest.json | 4 +-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/nuheat/test_climate.py | 2 +- 7 files changed, 10 insertions(+), 39 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index cbff4badc9f..d0e4fc993ad 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -780,6 +780,8 @@ build.json @home-assistant/supervisor /tests/components/nsw_fuel_station/ @nickw444 /homeassistant/components/nsw_rural_fire_service_feed/ @exxamalte /tests/components/nsw_rural_fire_service_feed/ @exxamalte +/homeassistant/components/nuheat/ @tstabrawa +/tests/components/nuheat/ @tstabrawa /homeassistant/components/nuki/ @pschmitt @pvizeli @pree /tests/components/nuki/ @pschmitt @pvizeli @pree /homeassistant/components/numato/ @clssn diff --git a/homeassistant/components/nuheat/climate.py b/homeassistant/components/nuheat/climate.py index c731e3472d6..0c053a8c67f 100644 --- a/homeassistant/components/nuheat/climate.py +++ b/homeassistant/components/nuheat/climate.py @@ -1,7 +1,5 @@ """Support for NuHeat thermostats.""" -from datetime import datetime import logging -import time from typing import Any from nuheat.config import SCHEDULE_HOLD, SCHEDULE_RUN, SCHEDULE_TEMPORARY_HOLD @@ -27,16 +25,7 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import ( - DOMAIN, - MANUFACTURER, - NUHEAT_API_STATE_SHIFT_DELAY, - NUHEAT_DATETIME_FORMAT, - NUHEAT_KEY_HOLD_SET_POINT_DATE_TIME, - NUHEAT_KEY_SCHEDULE_MODE, - NUHEAT_KEY_SET_POINT_TEMP, - TEMP_HOLD_TIME_SEC, -) +from .const import DOMAIN, MANUFACTURER, NUHEAT_API_STATE_SHIFT_DELAY _LOGGER = logging.getLogger(__name__) @@ -225,22 +214,9 @@ class NuHeatThermostat(CoordinatorEntity, ClimateEntity): target_schedule_mode, ) - target_temperature = max( - min(self._thermostat.max_temperature, target_temperature), - self._thermostat.min_temperature, + self._thermostat.set_target_temperature( + target_temperature, target_schedule_mode ) - - request = { - NUHEAT_KEY_SET_POINT_TEMP: target_temperature, - NUHEAT_KEY_SCHEDULE_MODE: target_schedule_mode, - } - - if target_schedule_mode == SCHEDULE_TEMPORARY_HOLD: - request[NUHEAT_KEY_HOLD_SET_POINT_DATE_TIME] = datetime.fromtimestamp( - time.time() + TEMP_HOLD_TIME_SEC - ).strftime(NUHEAT_DATETIME_FORMAT) - - self._thermostat.set_data(request) self._schedule_mode = target_schedule_mode self._target_temperature = target_temperature self._schedule_update() diff --git a/homeassistant/components/nuheat/const.py b/homeassistant/components/nuheat/const.py index 619d4a11e2a..ea43c33d9b0 100644 --- a/homeassistant/components/nuheat/const.py +++ b/homeassistant/components/nuheat/const.py @@ -10,10 +10,3 @@ CONF_SERIAL_NUMBER = "serial_number" MANUFACTURER = "NuHeat" NUHEAT_API_STATE_SHIFT_DELAY = 2 - -TEMP_HOLD_TIME_SEC = 43200 - -NUHEAT_KEY_SET_POINT_TEMP = "SetPointTemp" -NUHEAT_KEY_SCHEDULE_MODE = "ScheduleMode" -NUHEAT_KEY_HOLD_SET_POINT_DATE_TIME = "HoldSetPointDateTime" -NUHEAT_DATETIME_FORMAT = "%a, %d %b %Y %H:%M:%S GMT" diff --git a/homeassistant/components/nuheat/manifest.json b/homeassistant/components/nuheat/manifest.json index aea63a692a5..90d18b87af2 100644 --- a/homeassistant/components/nuheat/manifest.json +++ b/homeassistant/components/nuheat/manifest.json @@ -2,8 +2,8 @@ "domain": "nuheat", "name": "NuHeat", "documentation": "https://www.home-assistant.io/integrations/nuheat", - "requirements": ["nuheat==0.3.0"], - "codeowners": [], + "requirements": ["nuheat==1.0.0"], + "codeowners": ["@tstabrawa"], "config_flow": true, "dhcp": [ { diff --git a/requirements_all.txt b/requirements_all.txt index bdebb6f8652..ef90ca110e9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1180,7 +1180,7 @@ nsapi==3.0.5 nsw-fuel-api-client==1.1.0 # homeassistant.components.nuheat -nuheat==0.3.0 +nuheat==1.0.0 # homeassistant.components.numato numato-gpio==0.10.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c05a57e8508..010ff5a6b7d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -855,7 +855,7 @@ notify-events==1.0.4 nsw-fuel-api-client==1.1.0 # homeassistant.components.nuheat -nuheat==0.3.0 +nuheat==1.0.0 # homeassistant.components.numato numato-gpio==0.10.0 diff --git a/tests/components/nuheat/test_climate.py b/tests/components/nuheat/test_climate.py index 133c3a15ccb..4a6e9551b0b 100644 --- a/tests/components/nuheat/test_climate.py +++ b/tests/components/nuheat/test_climate.py @@ -159,7 +159,7 @@ async def test_climate_thermostat_schedule_temporary_hold(hass): # opportunistic set state = hass.states.get("climate.temp_bathroom") assert state.attributes["preset_mode"] == "Temporary Hold" - assert state.attributes["temperature"] == 50.0 + assert state.attributes["temperature"] == 90.0 # and the api poll returns it to the mock async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=3)) From ff4456cb295f52d432db166d474d567299d98b39 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 7 Nov 2022 08:24:49 +0100 Subject: [PATCH 0245/1033] Improve MQTT type hints part 4 (#80971) * Improve typing humidifier * Improve typing lock * Improve typing number * Set humidifier type hints at class level * Set lock type hints at class level * Set number type hints at class level * Some small updates * Follow up comment * Remove assert --- homeassistant/components/mqtt/humidifier.py | 100 ++++++++++++-------- homeassistant/components/mqtt/lock.py | 58 ++++++------ homeassistant/components/mqtt/number.py | 66 ++++++++----- 3 files changed, 128 insertions(+), 96 deletions(-) diff --git a/homeassistant/components/mqtt/humidifier.py b/homeassistant/components/mqtt/humidifier.py index e3e94c07dae..69b6d3e3e89 100644 --- a/homeassistant/components/mqtt/humidifier.py +++ b/homeassistant/components/mqtt/humidifier.py @@ -1,6 +1,7 @@ """Support for MQTT humidifiers.""" from __future__ import annotations +from collections.abc import Callable import functools import logging from typing import Any @@ -28,6 +29,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.template import Template from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import subscription @@ -50,7 +52,13 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttCommandTemplate, MqttValueTemplate +from .models import ( + MqttCommandTemplate, + MqttValueTemplate, + PublishPayloadType, + ReceiveMessage, + ReceivePayloadType, +) from .util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic CONF_AVAILABLE_MODES_LIST = "modes" @@ -87,18 +95,18 @@ MQTT_HUMIDIFIER_ATTRIBUTES_BLOCKED = frozenset( _LOGGER = logging.getLogger(__name__) -def valid_mode_configuration(config): +def valid_mode_configuration(config: ConfigType) -> ConfigType: """Validate that the mode reset payload is not one of the available modes.""" - if config.get(CONF_PAYLOAD_RESET_MODE) in config.get(CONF_AVAILABLE_MODES_LIST): + if config[CONF_PAYLOAD_RESET_MODE] in config[CONF_AVAILABLE_MODES_LIST]: raise ValueError("modes must not contain payload_reset_mode") return config -def valid_humidity_range_configuration(config): +def valid_humidity_range_configuration(config: ConfigType) -> ConfigType: """Validate that the target_humidity range configuration is valid, throws if it isn't.""" - if config.get(CONF_TARGET_HUMIDITY_MIN) >= config.get(CONF_TARGET_HUMIDITY_MAX): + if config[CONF_TARGET_HUMIDITY_MIN] >= config[CONF_TARGET_HUMIDITY_MAX]: raise ValueError("target_humidity_max must be > target_humidity_min") - if config.get(CONF_TARGET_HUMIDITY_MAX) > 100: + if config[CONF_TARGET_HUMIDITY_MAX] > 100: raise ValueError("max_humidity must be <= 100") return config @@ -196,8 +204,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT humidifier.""" async_add_entities([MqttHumidifier(hass, config, config_entry, discovery_data)]) @@ -209,30 +217,36 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): _entity_id_format = humidifier.ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_HUMIDIFIER_ATTRIBUTES_BLOCKED - def __init__(self, hass, config, config_entry, discovery_data): + _command_templates: dict[str, Callable[[PublishPayloadType], PublishPayloadType]] + _value_templates: dict[str, Callable[[ReceivePayloadType], ReceivePayloadType]] + _optimistic: bool + _optimistic_target_humidity: bool + _optimistic_mode: bool + _payload: dict[str, str] + _topic: dict[str, Any] + + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the MQTT humidifier.""" self._attr_mode = None - self._topic = None - self._payload = None - self._value_templates = None - self._command_templates = None - self._optimistic = None - self._optimistic_target_humidity = None - self._optimistic_mode = None - MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" self._attr_device_class = config.get(CONF_DEVICE_CLASS) - self._attr_min_humidity = config.get(CONF_TARGET_HUMIDITY_MIN) - self._attr_max_humidity = config.get(CONF_TARGET_HUMIDITY_MAX) + self._attr_min_humidity = config[CONF_TARGET_HUMIDITY_MIN] + self._attr_max_humidity = config[CONF_TARGET_HUMIDITY_MAX] self._topic = { key: config.get(key) @@ -245,16 +259,6 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): CONF_MODE_COMMAND_TOPIC, ) } - self._value_templates = { - CONF_STATE: config.get(CONF_STATE_VALUE_TEMPLATE), - ATTR_HUMIDITY: config.get(CONF_TARGET_HUMIDITY_STATE_TEMPLATE), - ATTR_MODE: config.get(CONF_MODE_STATE_TEMPLATE), - } - self._command_templates = { - CONF_STATE: config.get(CONF_COMMAND_TEMPLATE), - ATTR_HUMIDITY: config.get(CONF_TARGET_HUMIDITY_COMMAND_TEMPLATE), - ATTR_MODE: config.get(CONF_MODE_COMMAND_TEMPLATE), - } self._payload = { "STATE_ON": config[CONF_PAYLOAD_ON], "STATE_OFF": config[CONF_PAYLOAD_OFF], @@ -270,31 +274,43 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): else: self._attr_supported_features = 0 - optimistic = config[CONF_OPTIMISTIC] + optimistic: bool = config[CONF_OPTIMISTIC] self._optimistic = optimistic or self._topic[CONF_STATE_TOPIC] is None self._optimistic_target_humidity = ( optimistic or self._topic[CONF_TARGET_HUMIDITY_STATE_TOPIC] is None ) self._optimistic_mode = optimistic or self._topic[CONF_MODE_STATE_TOPIC] is None - for key, tpl in self._command_templates.items(): + self._command_templates = {} + command_templates: dict[str, Template | None] = { + CONF_STATE: config.get(CONF_COMMAND_TEMPLATE), + ATTR_HUMIDITY: config.get(CONF_TARGET_HUMIDITY_COMMAND_TEMPLATE), + ATTR_MODE: config.get(CONF_MODE_COMMAND_TEMPLATE), + } + for key, tpl in command_templates.items(): self._command_templates[key] = MqttCommandTemplate( tpl, entity=self ).async_render - for key, tpl in self._value_templates.items(): + self._value_templates = {} + value_templates: dict[str, Template | None] = { + CONF_STATE: config.get(CONF_STATE_VALUE_TEMPLATE), + ATTR_HUMIDITY: config.get(CONF_TARGET_HUMIDITY_STATE_TEMPLATE), + ATTR_MODE: config.get(CONF_MODE_STATE_TEMPLATE), + } + for key, tpl in value_templates.items(): self._value_templates[key] = MqttValueTemplate( tpl, entity=self, ).async_render_with_possible_json_value - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" - topics = {} + topics: dict[str, Any] = {} @callback @log_messages(self.hass, self.entity_id) - def state_received(msg): + def state_received(msg: ReceiveMessage) -> None: """Handle new received MQTT message.""" payload = self._value_templates[CONF_STATE](msg.payload) if not payload: @@ -318,7 +334,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): @callback @log_messages(self.hass, self.entity_id) - def target_humidity_received(msg): + def target_humidity_received(msg: ReceiveMessage) -> None: """Handle new received MQTT message for the target humidity.""" rendered_target_humidity_payload = self._value_templates[ATTR_HUMIDITY]( msg.payload @@ -365,9 +381,9 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): @callback @log_messages(self.hass, self.entity_id) - def mode_received(msg): + def mode_received(msg: ReceiveMessage) -> None: """Handle new received MQTT message for mode.""" - mode = self._value_templates[ATTR_MODE](msg.payload) + mode = str(self._value_templates[ATTR_MODE](msg.payload)) if mode == self._payload["MODE_RESET"]: self._attr_mode = None get_mqtt_data(self.hass).state_write_requests.write_state_request(self) @@ -375,7 +391,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): if not mode: _LOGGER.debug("Ignoring empty mode from '%s'", msg.topic) return - if mode not in self.available_modes: + if not self.available_modes or mode not in self.available_modes: _LOGGER.warning( "'%s' received on topic %s. '%s' is not a valid mode", msg.payload, @@ -400,7 +416,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): self.hass, self._sub_state, topics ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index c9bdd696896..e141fcbd693 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -1,6 +1,7 @@ """Support for MQTT locks.""" from __future__ import annotations +from collections.abc import Callable import functools from typing import Any @@ -32,7 +33,7 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttValueTemplate +from .models import MqttValueTemplate, ReceiveMessage, ReceivePayloadType from .util import get_mqtt_data CONF_PAYLOAD_LOCK = "payload_lock" @@ -112,8 +113,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT Lock platform.""" async_add_entities([MqttLock(hass, config, config_entry, discovery_data)]) @@ -125,39 +126,50 @@ class MqttLock(MqttEntity, LockEntity): _entity_id_format = lock.ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_LOCK_ATTRIBUTES_BLOCKED - def __init__(self, hass, config, config_entry, discovery_data): - """Initialize the lock.""" - self._state = False - self._optimistic = False + _optimistic: bool + _value_template: Callable[[ReceivePayloadType], ReceivePayloadType] + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: + """Initialize the lock.""" + self._attr_is_locked = False MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" self._optimistic = config[CONF_OPTIMISTIC] self._value_template = MqttValueTemplate( - self._config.get(CONF_VALUE_TEMPLATE), + config.get(CONF_VALUE_TEMPLATE), entity=self, ).async_render_with_possible_json_value - def _prepare_subscribe_topics(self): + self._attr_supported_features = ( + LockEntityFeature.OPEN if CONF_PAYLOAD_OPEN in config else 0 + ) + + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" @callback @log_messages(self.hass, self.entity_id) - def message_received(msg): + def message_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages.""" payload = self._value_template(msg.payload) if payload == self._config[CONF_STATE_LOCKED]: - self._state = True + self._attr_is_locked = True elif payload == self._config[CONF_STATE_UNLOCKED]: - self._state = False + self._attr_is_locked = False get_mqtt_data(self.hass).state_write_requests.write_state_request(self) @@ -178,25 +190,15 @@ class MqttLock(MqttEntity, LockEntity): }, ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) - @property - def is_locked(self) -> bool: - """Return true if lock is locked.""" - return self._state - @property def assumed_state(self) -> bool: """Return true if we do optimistic updates.""" return self._optimistic - @property - def supported_features(self) -> int: - """Flag supported features.""" - return LockEntityFeature.OPEN if CONF_PAYLOAD_OPEN in self._config else 0 - async def async_lock(self, **kwargs: Any) -> None: """Lock the device. @@ -211,7 +213,7 @@ class MqttLock(MqttEntity, LockEntity): ) if self._optimistic: # Optimistically assume that the lock has changed state. - self._state = True + self._attr_is_locked = True self.async_write_ha_state() async def async_unlock(self, **kwargs: Any) -> None: @@ -228,7 +230,7 @@ class MqttLock(MqttEntity, LockEntity): ) if self._optimistic: # Optimistically assume that the lock has changed state. - self._state = False + self._attr_is_locked = False self.async_write_ha_state() async def async_open(self, **kwargs: Any) -> None: @@ -245,5 +247,5 @@ class MqttLock(MqttEntity, LockEntity): ) if self._optimistic: # Optimistically assume that the lock unlocks when opened. - self._state = False + self._attr_is_locked = False self.async_write_ha_state() diff --git a/homeassistant/components/mqtt/number.py b/homeassistant/components/mqtt/number.py index 95dbe970430..51b480c036e 100644 --- a/homeassistant/components/mqtt/number.py +++ b/homeassistant/components/mqtt/number.py @@ -1,6 +1,7 @@ """Configure number in a device through MQTT topic.""" from __future__ import annotations +from collections.abc import Callable import functools import logging @@ -47,7 +48,13 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttCommandTemplate, MqttValueTemplate +from .models import ( + MqttCommandTemplate, + MqttValueTemplate, + PublishPayloadType, + ReceiveMessage, + ReceivePayloadType, +) from .util import get_mqtt_data _LOGGER = logging.getLogger(__name__) @@ -70,9 +77,9 @@ MQTT_NUMBER_ATTRIBUTES_BLOCKED = frozenset( ) -def validate_config(config): +def validate_config(config: ConfigType) -> ConfigType: """Validate that the configuration is valid, throws if it isn't.""" - if config.get(CONF_MIN) >= config.get(CONF_MAX): + if config[CONF_MIN] >= config[CONF_MAX]: raise vol.Invalid(f"'{CONF_MAX}' must be > '{CONF_MIN}'") return config @@ -147,8 +154,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT number.""" async_add_entities([MqttNumber(hass, config, config_entry, discovery_data)]) @@ -160,33 +167,39 @@ class MqttNumber(MqttEntity, RestoreNumber): _entity_id_format = number.ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_NUMBER_ATTRIBUTES_BLOCKED - def __init__(self, hass, config, config_entry, discovery_data): - """Initialize the MQTT Number.""" - self._config = config - self._optimistic = False - self._sub_state = None + _optimistic: bool + _command_template: Callable[[PublishPayloadType], PublishPayloadType] + _value_template: Callable[[ReceivePayloadType], ReceivePayloadType] + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: + """Initialize the MQTT Number.""" RestoreNumber.__init__(self) MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" + self._config = config self._optimistic = config[CONF_OPTIMISTIC] - self._templates = { - CONF_COMMAND_TEMPLATE: MqttCommandTemplate( - config.get(CONF_COMMAND_TEMPLATE), entity=self - ).async_render, - CONF_VALUE_TEMPLATE: MqttValueTemplate( - config.get(CONF_VALUE_TEMPLATE), - entity=self, - ).async_render_with_possible_json_value, - } + self._command_template = MqttCommandTemplate( + config.get(CONF_COMMAND_TEMPLATE), entity=self + ).async_render + self._value_template = MqttValueTemplate( + config.get(CONF_VALUE_TEMPLATE), + entity=self, + ).async_render_with_possible_json_value + self._attr_device_class = config.get(CONF_DEVICE_CLASS) self._attr_mode = config[CONF_MODE] self._attr_native_max_value = config[CONF_MAX] @@ -194,14 +207,15 @@ class MqttNumber(MqttEntity, RestoreNumber): self._attr_native_step = config[CONF_STEP] self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT) - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" @callback @log_messages(self.hass, self.entity_id) - def message_received(msg): + def message_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages.""" - payload = self._templates[CONF_VALUE_TEMPLATE](msg.payload) + num_value: int | float | None + payload = str(self._value_template(msg.payload)) try: if payload == self._config[CONF_PAYLOAD_RESET]: num_value = None @@ -245,7 +259,7 @@ class MqttNumber(MqttEntity, RestoreNumber): }, ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) @@ -260,7 +274,7 @@ class MqttNumber(MqttEntity, RestoreNumber): if value.is_integer(): current_number = int(value) - payload = self._templates[CONF_COMMAND_TEMPLATE](current_number) + payload = self._command_template(current_number) if self._optimistic: self._attr_native_value = current_number From efd60de1ac41c4da68b3ebaa8bd6385a4bb26c0b Mon Sep 17 00:00:00 2001 From: Malte Franken Date: Mon, 7 Nov 2022 19:01:07 +1100 Subject: [PATCH 0246/1033] Add integration_type to geonetnz_volcano (#81607) define integration type --- homeassistant/components/geonetnz_volcano/manifest.json | 3 ++- homeassistant/generated/integrations.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/geonetnz_volcano/manifest.json b/homeassistant/components/geonetnz_volcano/manifest.json index a365237561a..7c765ecb939 100644 --- a/homeassistant/components/geonetnz_volcano/manifest.json +++ b/homeassistant/components/geonetnz_volcano/manifest.json @@ -6,5 +6,6 @@ "requirements": ["aio_geojson_geonetnz_volcano==0.6"], "codeowners": ["@exxamalte"], "iot_class": "cloud_polling", - "loggers": ["aio_geojson_geonetnz_volcano"] + "loggers": ["aio_geojson_geonetnz_volcano"], + "integration_type": "service" } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 55a330685d5..89de4fa92fc 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -1841,7 +1841,7 @@ "name": "GeoNet NZ Quakes" }, "geonetnz_volcano": { - "integration_type": "hub", + "integration_type": "service", "config_flow": true, "iot_class": "cloud_polling", "name": "GeoNet NZ Volcano" From 9bc029000aa123fb645392c8aca4a6f1b6be6704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Mon, 7 Nov 2022 10:09:47 +0200 Subject: [PATCH 0247/1033] Upgrade prettier to v2.7.1 (#81682) No formatting changes. https://github.com/prettier/prettier/blob/2.7.1/CHANGELOG.md --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 751c97ebbb4..1a2e4d15482 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -65,7 +65,7 @@ repos: hooks: - id: yamllint - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.6.1 + rev: v2.7.1 hooks: - id: prettier - repo: https://github.com/cdce8p/python-typing-update From 5e05739019774f0647f8e442dccfd210ae168cf0 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 7 Nov 2022 09:41:53 +0100 Subject: [PATCH 0248/1033] Add type hints to template helper (#81308) * Add type hints to template helper * Update homeassistant/helpers/template.py * Adjust use of ensure_compiled --- homeassistant/helpers/template.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index dfab80e5223..24852ab5cf3 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -559,8 +559,11 @@ class Template: @callback def async_render_with_possible_json_value( - self, value, error_value=_SENTINEL, variables=None - ): + self, + value: Any, + error_value: Any = _SENTINEL, + variables: dict[str, Any] | None = None, + ) -> Any: """Render template with value exposed. If valid JSON will expose value_json too. @@ -570,8 +573,7 @@ class Template: if self.is_static: return self.template - if self._compiled is None: - self._ensure_compiled() + compiled = self._compiled or self._ensure_compiled() variables = dict(variables or {}) variables["value"] = value @@ -580,9 +582,7 @@ class Template: variables["value_json"] = json_loads(value) try: - return _render_with_context( - self.template, self._compiled, **variables - ).strip() + return _render_with_context(self.template, compiled, **variables).strip() except jinja2.TemplateError as ex: if error_value is _SENTINEL: _LOGGER.error( From 1d633ac484e61564dc6fdf87030072cc69bb480c Mon Sep 17 00:00:00 2001 From: Benjamin Salchow <43908980+benjamin-salchow@users.noreply.github.com> Date: Mon, 7 Nov 2022 10:14:12 +0100 Subject: [PATCH 0249/1033] Accept input register in Modbus binary sensor (#81352) Adds input register as valid option for modbus binary_sensor Co-authored-by: jan iversen --- homeassistant/components/modbus/__init__.py | 7 ++++++- tests/components/modbus/test_binary_sensor.py | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/modbus/__init__.py b/homeassistant/components/modbus/__init__.py index 8aa2903506f..cda0bb64703 100644 --- a/homeassistant/components/modbus/__init__.py +++ b/homeassistant/components/modbus/__init__.py @@ -287,7 +287,12 @@ BINARY_SENSOR_SCHEMA = BASE_COMPONENT_SCHEMA.extend( { vol.Optional(CONF_DEVICE_CLASS): BINARY_SENSOR_DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_INPUT_TYPE, default=CALL_TYPE_COIL): vol.In( - [CALL_TYPE_COIL, CALL_TYPE_DISCRETE, CALL_TYPE_REGISTER_HOLDING] + [ + CALL_TYPE_COIL, + CALL_TYPE_DISCRETE, + CALL_TYPE_REGISTER_HOLDING, + CALL_TYPE_REGISTER_INPUT, + ] ), vol.Optional(CONF_SLAVE_COUNT, default=0): cv.positive_int, } diff --git a/tests/components/modbus/test_binary_sensor.py b/tests/components/modbus/test_binary_sensor.py index 777d284e20f..cdd019c73f3 100644 --- a/tests/components/modbus/test_binary_sensor.py +++ b/tests/components/modbus/test_binary_sensor.py @@ -6,6 +6,7 @@ from homeassistant.components.modbus.const import ( CALL_TYPE_COIL, CALL_TYPE_DISCRETE, CALL_TYPE_REGISTER_HOLDING, + CALL_TYPE_REGISTER_INPUT, CONF_INPUT_TYPE, CONF_LAZY_ERROR, CONF_SLAVE_COUNT, @@ -54,6 +55,16 @@ ENTITY_ID = f"{SENSOR_DOMAIN}.{TEST_ENTITY_NAME}".replace(" ", "_") } ] }, + { + CONF_BINARY_SENSORS: [ + { + CONF_NAME: TEST_ENTITY_NAME, + CONF_ADDRESS: 51, + CONF_SLAVE: 10, + CONF_INPUT_TYPE: CALL_TYPE_REGISTER_INPUT, + } + ] + }, ], ) async def test_config_binary_sensor(hass, mock_modbus): @@ -91,6 +102,15 @@ async def test_config_binary_sensor(hass, mock_modbus): }, ], }, + { + CONF_BINARY_SENSORS: [ + { + CONF_NAME: TEST_ENTITY_NAME, + CONF_ADDRESS: 51, + CONF_INPUT_TYPE: CALL_TYPE_REGISTER_INPUT, + }, + ], + }, ], ) @pytest.mark.parametrize( From 11a55d6d4c22eae254c2781405b2f4c483111cb5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 7 Nov 2022 04:41:02 -0600 Subject: [PATCH 0250/1033] Fix flapping logbook tests (#81695) --- .../components/logbook/test_websocket_api.py | 91 ++++++++++++++----- 1 file changed, 67 insertions(+), 24 deletions(-) diff --git a/tests/components/logbook/test_websocket_api.py b/tests/components/logbook/test_websocket_api.py index fb0defca93c..42f604b3d4c 100644 --- a/tests/components/logbook/test_websocket_api.py +++ b/tests/components/logbook/test_websocket_api.py @@ -24,6 +24,7 @@ from homeassistant.const import ( CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE, + EVENT_HOMEASSISTANT_FINAL_WRITE, EVENT_HOMEASSISTANT_START, STATE_OFF, STATE_ON, @@ -52,6 +53,15 @@ def set_utc(hass): hass.config.set_time_zone("UTC") +def listeners_without_writes(listeners: dict[str, int]) -> dict[str, int]: + """Return listeners without final write listeners since we are not testing for these.""" + return { + key: value + for key, value in listeners.items() + if key != EVENT_HOMEASSISTANT_FINAL_WRITE + } + + async def _async_mock_logbook_platform(hass: HomeAssistant) -> None: class MockLogbookPlatform: """Mock a logbook platform.""" @@ -684,7 +694,9 @@ async def test_subscribe_unsubscribe_logbook_stream_excluded_entities( assert msg["success"] # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -892,7 +904,9 @@ async def test_subscribe_unsubscribe_logbook_stream_included_entities( assert msg["success"] # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -1083,7 +1097,9 @@ async def test_logbook_stream_excluded_entities_inherits_filters_from_recorder( assert msg["success"] # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -1386,7 +1402,9 @@ async def test_subscribe_unsubscribe_logbook_stream( assert msg["success"] # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -1484,7 +1502,9 @@ async def test_subscribe_unsubscribe_logbook_stream_entities( assert msg["success"] # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -1586,12 +1606,9 @@ async def test_subscribe_unsubscribe_logbook_stream_entities_with_end_time( assert msg["success"] # Check our listener got unsubscribed - listeners = hass.bus.async_listeners() - # The async_fire_time_changed above triggers unsubscribe from - # homeassistant_final_write, don't worry about those - init_listeners.pop("homeassistant_final_write") - listeners.pop("homeassistant_final_write") - assert listeners == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -1659,7 +1676,9 @@ async def test_subscribe_unsubscribe_logbook_stream_entities_past_only( assert msg["success"] # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -1759,7 +1778,9 @@ async def test_subscribe_unsubscribe_logbook_stream_big_query( assert msg["success"] # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -1853,7 +1874,9 @@ async def test_subscribe_unsubscribe_logbook_stream_device( assert msg["success"] # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) async def test_event_stream_bad_start_time(recorder_mock, hass, hass_ws_client): @@ -1968,7 +1991,9 @@ async def test_logbook_stream_match_multiple_entities( assert msg["success"] # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) async def test_event_stream_bad_end_time(recorder_mock, hass, hass_ws_client): @@ -2091,7 +2116,9 @@ async def test_live_stream_with_one_second_commit_interval( assert msg["success"] # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -2146,7 +2173,9 @@ async def test_subscribe_disconnected(recorder_mock, hass, hass_ws_client): await hass.async_block_till_done() # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -2189,7 +2218,9 @@ async def test_stream_consumer_stop_processing(recorder_mock, hass, hass_ws_clie assert msg["type"] == TYPE_RESULT assert msg["success"] - assert hass.bus.async_listeners() != init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) != listeners_without_writes(init_listeners) for _ in range(5): hass.states.async_set("binary_sensor.is_light", STATE_ON) hass.states.async_set("binary_sensor.is_light", STATE_OFF) @@ -2197,9 +2228,13 @@ async def test_stream_consumer_stop_processing(recorder_mock, hass, hass_ws_clie # Check our listener got unsubscribed because # the queue got full and the overload safety tripped - assert hass.bus.async_listeners() == after_ws_created_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(after_ws_created_listeners) await websocket_client.close() - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -2332,7 +2367,9 @@ async def test_subscribe_all_entities_are_continuous( await hass.async_block_till_done() # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -2494,7 +2531,9 @@ async def test_subscribe_entities_some_have_uom_multiple( await hass.async_block_till_done() # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -2608,7 +2647,9 @@ async def test_logbook_stream_ignores_forced_updates( assert msg["success"] # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) @@ -2703,4 +2744,6 @@ async def test_subscribe_all_entities_are_continuous_with_device( await hass.async_block_till_done() # Check our listener got unsubscribed - assert hass.bus.async_listeners() == init_listeners + assert listeners_without_writes( + hass.bus.async_listeners() + ) == listeners_without_writes(init_listeners) From 3184c8a5266d66cf65df69f58d723f692f4334e8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 7 Nov 2022 05:06:38 -0600 Subject: [PATCH 0251/1033] Fix use of deprecated device.rssi in bluetooth scanner (#81690) --- homeassistant/components/bluetooth/scanner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/bluetooth/scanner.py b/homeassistant/components/bluetooth/scanner.py index 6b23cae0218..43c4706474a 100644 --- a/homeassistant/components/bluetooth/scanner.py +++ b/homeassistant/components/bluetooth/scanner.py @@ -209,7 +209,7 @@ class HaScanner(BaseHaScanner): service_info = BluetoothServiceInfoBleak( name=advertisement_data.local_name or device.name or device.address, address=device.address, - rssi=device.rssi, + rssi=advertisement_data.rssi, manufacturer_data=advertisement_data.manufacturer_data, service_data=advertisement_data.service_data, service_uuids=advertisement_data.service_uuids, From 2bea77549d02c61211decd62b7a5a0c8ee2e293a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 7 Nov 2022 05:09:36 -0600 Subject: [PATCH 0252/1033] Fix creating multiple ElkM1 systems with TLS 1.2 (#81627) fixes https://github.com/home-assistant/core/issues/81516 --- homeassistant/components/elkm1/__init__.py | 9 +- homeassistant/components/elkm1/config_flow.py | 13 +- tests/components/elkm1/test_config_flow.py | 146 ++++++++++++++++++ 3 files changed, 159 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/elkm1/__init__.py b/homeassistant/components/elkm1/__init__.py index 7833bfd66b3..7b14c7e85ed 100644 --- a/homeassistant/components/elkm1/__init__.py +++ b/homeassistant/components/elkm1/__init__.py @@ -7,11 +7,11 @@ import logging import re from types import MappingProxyType from typing import Any, cast -from urllib.parse import urlparse import async_timeout from elkm1_lib.elements import Element from elkm1_lib.elk import Elk +from elkm1_lib.util import parse_url import voluptuous as vol from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry @@ -96,6 +96,11 @@ SET_TIME_SERVICE_SCHEMA = vol.Schema( ) +def hostname_from_url(url: str) -> str: + """Return the hostname from a url.""" + return parse_url(url)[1] + + def _host_validator(config: dict[str, str]) -> dict[str, str]: """Validate that a host is properly configured.""" if config[CONF_HOST].startswith("elks://"): @@ -231,7 +236,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Elk-M1 Control from a config entry.""" conf: MappingProxyType[str, Any] = entry.data - host = urlparse(entry.data[CONF_HOST]).hostname + host = hostname_from_url(entry.data[CONF_HOST]) _LOGGER.debug("Setting up elkm1 %s", conf["host"]) diff --git a/homeassistant/components/elkm1/config_flow.py b/homeassistant/components/elkm1/config_flow.py index 8675ff45ee7..ac7fc903330 100644 --- a/homeassistant/components/elkm1/config_flow.py +++ b/homeassistant/components/elkm1/config_flow.py @@ -4,7 +4,6 @@ from __future__ import annotations import asyncio import logging from typing import Any -from urllib.parse import urlparse from elkm1_lib.discovery import ElkSystem from elkm1_lib.elk import Elk @@ -26,7 +25,7 @@ from homeassistant.helpers.typing import DiscoveryInfoType from homeassistant.util import slugify from homeassistant.util.network import is_ip_address -from . import async_wait_for_elk_to_sync +from . import async_wait_for_elk_to_sync, hostname_from_url from .const import CONF_AUTO_CONFIGURE, DISCOVER_SCAN_TIMEOUT, DOMAIN, LOGIN_TIMEOUT from .discovery import ( _short_mac, @@ -170,7 +169,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): for entry in self._async_current_entries(include_ignore=False): if ( entry.unique_id == mac - or urlparse(entry.data[CONF_HOST]).hostname == host + or hostname_from_url(entry.data[CONF_HOST]) == host ): if async_update_entry_from_discovery(self.hass, entry, device): self.hass.async_create_task( @@ -214,7 +213,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): current_unique_ids = self._async_current_ids() current_hosts = { - urlparse(entry.data[CONF_HOST]).hostname + hostname_from_url(entry.data[CONF_HOST]) for entry in self._async_current_entries(include_ignore=False) } discovered_devices = await async_discover_devices( @@ -344,7 +343,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if self._url_already_configured(url): return self.async_abort(reason="address_already_configured") - host = urlparse(url).hostname + host = hostname_from_url(url) _LOGGER.debug( "Importing is trying to fill unique id from discovery for %s", host ) @@ -367,10 +366,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): def _url_already_configured(self, url: str) -> bool: """See if we already have a elkm1 matching user input configured.""" existing_hosts = { - urlparse(entry.data[CONF_HOST]).hostname + hostname_from_url(entry.data[CONF_HOST]) for entry in self._async_current_entries() } - return urlparse(url).hostname in existing_hosts + return hostname_from_url(url) in existing_hosts class InvalidAuth(exceptions.HomeAssistantError): diff --git a/tests/components/elkm1/test_config_flow.py b/tests/components/elkm1/test_config_flow.py index 7ce0e2163ac..a58528b700a 100644 --- a/tests/components/elkm1/test_config_flow.py +++ b/tests/components/elkm1/test_config_flow.py @@ -1454,3 +1454,149 @@ async def test_multiple_instances_with_discovery(hass): "password": "", } assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_multiple_instances_with_tls_v12(hass): + """Test we can setup a secure elk with tls v1_2.""" + + elk_discovery_1 = ElkSystem("aa:bb:cc:dd:ee:ff", "127.0.0.1", 2601) + elk_discovery_2 = ElkSystem("aa:bb:cc:dd:ee:fe", "127.0.0.2", 2601) + + with _patch_discovery(device=elk_discovery_1): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + await hass.async_block_till_done() + + assert result["type"] == "form" + assert not result["errors"] + assert result["step_id"] == "user" + + mocked_elk = mock_elk(invalid_auth=False, sync_complete=True) + + with _patch_elk(elk=mocked_elk): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"device": elk_discovery_1.mac_address}, + ) + await hass.async_block_till_done() + + assert result2["type"] == "form" + assert not result["errors"] + assert result2["step_id"] == "discovered_connection" + with _patch_discovery(device=elk_discovery_1), _patch_elk(elk=mocked_elk), patch( + "homeassistant.components.elkm1.async_setup", return_value=True + ) as mock_setup, patch( + "homeassistant.components.elkm1.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + { + "protocol": "TLS 1.2", + "username": "test-username", + "password": "test-password", + }, + ) + await hass.async_block_till_done() + + assert result3["type"] == "create_entry" + assert result3["title"] == "ElkM1 ddeeff" + assert result3["data"] == { + "auto_configure": True, + "host": "elksv1_2://127.0.0.1", + "password": "test-password", + "prefix": "", + "username": "test-username", + } + assert len(mock_setup.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + + # Now try to add another instance with the different discovery info + with _patch_discovery(device=elk_discovery_2): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + await hass.async_block_till_done() + + assert result["type"] == "form" + assert not result["errors"] + assert result["step_id"] == "user" + + mocked_elk = mock_elk(invalid_auth=False, sync_complete=True) + + with _patch_elk(elk=mocked_elk): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"device": elk_discovery_2.mac_address}, + ) + await hass.async_block_till_done() + + with _patch_discovery(device=elk_discovery_2), _patch_elk(elk=mocked_elk), patch( + "homeassistant.components.elkm1.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + { + "protocol": "TLS 1.2", + "username": "test-username", + "password": "test-password", + }, + ) + await hass.async_block_till_done() + + assert result3["type"] == "create_entry" + assert result3["title"] == "ElkM1 ddeefe" + assert result3["data"] == { + "auto_configure": True, + "host": "elksv1_2://127.0.0.2", + "password": "test-password", + "prefix": "ddeefe", + "username": "test-username", + } + assert len(mock_setup_entry.mock_calls) == 1 + + # Finally, try to add another instance manually with no discovery info + + with _patch_discovery(no_device=True): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + await hass.async_block_till_done() + + assert result["type"] == "form" + assert result["errors"] == {} + assert result["step_id"] == "manual_connection" + + mocked_elk = mock_elk(invalid_auth=False, sync_complete=True) + + with _patch_discovery(no_device=True), _patch_elk(elk=mocked_elk), patch( + "homeassistant.components.elkm1.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "protocol": "TLS 1.2", + "address": "1.2.3.4", + "prefix": "guest_house", + "password": "test-password", + "username": "test-username", + }, + ) + await hass.async_block_till_done() + + import pprint + + pprint.pprint(result2) + assert result2["type"] == "create_entry" + assert result2["title"] == "guest_house" + assert result2["data"] == { + "auto_configure": True, + "host": "elksv1_2://1.2.3.4", + "prefix": "guest_house", + "password": "test-password", + "username": "test-username", + } + assert len(mock_setup_entry.mock_calls) == 1 From 190840cd335bec9b12a6bd338d0983cf6af1dd07 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Mon, 7 Nov 2022 06:12:41 -0500 Subject: [PATCH 0253/1033] Bump pyunifiprotect to 4.4.0 (#81696) --- homeassistant/components/unifiprotect/__init__.py | 4 ++-- homeassistant/components/unifiprotect/manifest.json | 3 ++- homeassistant/components/unifiprotect/strings.json | 4 ++-- .../components/unifiprotect/translations/en.json | 8 ++------ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 6 files changed, 10 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/unifiprotect/__init__.py b/homeassistant/components/unifiprotect/__init__.py index 649d62c6fee..174c5476cff 100644 --- a/homeassistant/components/unifiprotect/__init__.py +++ b/homeassistant/components/unifiprotect/__init__.py @@ -102,8 +102,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, data_service.async_stop) ) - protect_version = data_service.api.bootstrap.nvr.version - if protect_version.is_prerelease: + if await data_service.api.bootstrap.get_is_prerelease(): + protect_version = data_service.api.bootstrap.nvr.version ir.async_create_issue( hass, DOMAIN, diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index ae37360c5ee..62baf74b6a7 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -1,9 +1,10 @@ { "domain": "unifiprotect", "name": "UniFi Protect", + "integration_type": "hub", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/unifiprotect", - "requirements": ["pyunifiprotect==4.3.4", "unifi-discovery==1.1.7"], + "requirements": ["pyunifiprotect==4.4.0", "unifi-discovery==1.1.7"], "dependencies": ["http"], "codeowners": ["@briis", "@AngellusMortis", "@bdraco"], "quality_scale": "platinum", diff --git a/homeassistant/components/unifiprotect/strings.json b/homeassistant/components/unifiprotect/strings.json index 2967ba8c82e..5c9416440de 100644 --- a/homeassistant/components/unifiprotect/strings.json +++ b/homeassistant/components/unifiprotect/strings.json @@ -57,8 +57,8 @@ }, "issues": { "ea_warning": { - "title": "{version} is an Early Access version of UniFi Protect", - "description": "You are using {version} of UniFi Protect. Early Access versions are not supported by Home Assistant and may cause your UniFi Protect integration to break or not work as expected." + "title": "UniFi Protect v{version} is an Early Access version", + "description": "You are using v{version} of UniFi Protect which is an Early Access version. Early Access versions are not supported by Home Assistant and may cause your UniFi Protect integration to break or not work as expected." } } } diff --git a/homeassistant/components/unifiprotect/translations/en.json b/homeassistant/components/unifiprotect/translations/en.json index 3b6e74db341..03362629879 100644 --- a/homeassistant/components/unifiprotect/translations/en.json +++ b/homeassistant/components/unifiprotect/translations/en.json @@ -43,20 +43,16 @@ }, "issues": { "ea_warning": { - "description": "You are using {version} of UniFi Protect. Early Access versions are not supported by Home Assistant and may cause your UniFi Protect integration to break or not work as expected.", - "title": "{version} is an Early Access version of UniFi Protect" + "description": "You are using v{version} of UniFi Protect which is an Early Access version. Early Access versions are not supported by Home Assistant and may cause your UniFi Protect integration to break or not work as expected.", + "title": "UniFi Protect v{version} is an Early Access version" } }, "options": { - "error": { - "invalid_mac_list": "Must be a list of MAC addresses seperated by commas" - }, "step": { "init": { "data": { "all_updates": "Realtime metrics (WARNING: Greatly increases CPU usage)", "disable_rtsp": "Disable the RTSP stream", - "ignored_devices": "Comma separated list of MAC addresses of devices to ignore", "max_media": "Max number of event to load for Media Browser (increases RAM usage)", "override_connection_host": "Override Connection Host" }, diff --git a/requirements_all.txt b/requirements_all.txt index ef90ca110e9..08c7fa7a7ac 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2084,7 +2084,7 @@ pytrafikverket==0.2.1 pyudev==0.23.2 # homeassistant.components.unifiprotect -pyunifiprotect==4.3.4 +pyunifiprotect==4.4.0 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 010ff5a6b7d..34c694a23f3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1447,7 +1447,7 @@ pytrafikverket==0.2.1 pyudev==0.23.2 # homeassistant.components.unifiprotect -pyunifiprotect==4.3.4 +pyunifiprotect==4.4.0 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 From 9860b0691353ec954d44bab829fcae5efd0868f7 Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Mon, 7 Nov 2022 06:23:49 -0500 Subject: [PATCH 0254/1033] Bump aiopyarr to 22.11.0 (#81694) --- homeassistant/components/lidarr/manifest.json | 2 +- homeassistant/components/radarr/manifest.json | 2 +- homeassistant/components/sonarr/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/lidarr/manifest.json b/homeassistant/components/lidarr/manifest.json index 4c07e0e1762..eab24ef7e42 100644 --- a/homeassistant/components/lidarr/manifest.json +++ b/homeassistant/components/lidarr/manifest.json @@ -2,7 +2,7 @@ "domain": "lidarr", "name": "Lidarr", "documentation": "https://www.home-assistant.io/integrations/lidarr", - "requirements": ["aiopyarr==22.10.0"], + "requirements": ["aiopyarr==22.11.0"], "codeowners": ["@tkdrob"], "config_flow": true, "iot_class": "local_polling", diff --git a/homeassistant/components/radarr/manifest.json b/homeassistant/components/radarr/manifest.json index 9b140def96a..5117fd161d3 100644 --- a/homeassistant/components/radarr/manifest.json +++ b/homeassistant/components/radarr/manifest.json @@ -2,7 +2,7 @@ "domain": "radarr", "name": "Radarr", "documentation": "https://www.home-assistant.io/integrations/radarr", - "requirements": ["aiopyarr==22.10.0"], + "requirements": ["aiopyarr==22.11.0"], "codeowners": ["@tkdrob"], "config_flow": true, "iot_class": "local_polling", diff --git a/homeassistant/components/sonarr/manifest.json b/homeassistant/components/sonarr/manifest.json index daf9e20586b..511842a3e99 100644 --- a/homeassistant/components/sonarr/manifest.json +++ b/homeassistant/components/sonarr/manifest.json @@ -3,7 +3,7 @@ "name": "Sonarr", "documentation": "https://www.home-assistant.io/integrations/sonarr", "codeowners": ["@ctalkington"], - "requirements": ["aiopyarr==22.10.0"], + "requirements": ["aiopyarr==22.11.0"], "config_flow": true, "quality_scale": "silver", "iot_class": "local_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 08c7fa7a7ac..550dc61e18f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -240,7 +240,7 @@ aiopvpc==3.0.0 # homeassistant.components.lidarr # homeassistant.components.radarr # homeassistant.components.sonarr -aiopyarr==22.10.0 +aiopyarr==22.11.0 # homeassistant.components.qnap_qsw aioqsw==0.2.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 34c694a23f3..e626dfe50b1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -215,7 +215,7 @@ aiopvpc==3.0.0 # homeassistant.components.lidarr # homeassistant.components.radarr # homeassistant.components.sonarr -aiopyarr==22.10.0 +aiopyarr==22.11.0 # homeassistant.components.qnap_qsw aioqsw==0.2.2 From d1fd141e8c1d1e34f35a187a70fde13caee57589 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Mon, 7 Nov 2022 06:28:59 -0500 Subject: [PATCH 0255/1033] Bump ZHA quirks and associated changes (#81587) --- .../zha/core/channels/manufacturerspecific.py | 74 ++++++++----------- homeassistant/components/zha/device_action.py | 31 +++++--- homeassistant/components/zha/manifest.json | 2 +- homeassistant/components/zha/switch.py | 12 +++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/zha/test_device_action.py | 2 +- 7 files changed, 67 insertions(+), 58 deletions(-) diff --git a/homeassistant/components/zha/core/channels/manufacturerspecific.py b/homeassistant/components/zha/core/channels/manufacturerspecific.py index 814e7700d01..c4baccf4ae6 100644 --- a/homeassistant/components/zha/core/channels/manufacturerspecific.py +++ b/homeassistant/components/zha/core/channels/manufacturerspecific.py @@ -4,7 +4,7 @@ from __future__ import annotations import logging from typing import TYPE_CHECKING, Any -from zigpy import types +from zhaquirks.inovelli.types import AllLEDEffectType, SingleLEDEffectType from zigpy.exceptions import ZigbeeException import zigpy.zcl @@ -183,59 +183,47 @@ class InovelliNotificationChannel(ClientChannel): class InovelliConfigEntityChannel(ZigbeeChannel): """Inovelli Configuration Entity channel.""" - class LEDEffectType(types.enum8): - """Effect type for Inovelli Blue Series switch.""" - - Off = 0x00 - Solid = 0x01 - Fast_Blink = 0x02 - Slow_Blink = 0x03 - Pulse = 0x04 - Chase = 0x05 - Open_Close = 0x06 - Small_To_Big = 0x07 - Clear = 0xFF - REPORT_CONFIG = () ZCL_INIT_ATTRS = { - "dimming_speed_up_remote": False, - "dimming_speed_up_local": False, - "ramp_rate_off_to_on_local": False, - "ramp_rate_off_to_on_remote": False, - "dimming_speed_down_remote": False, - "dimming_speed_down_local": False, - "ramp_rate_on_to_off_local": False, - "ramp_rate_on_to_off_remote": False, - "minimum_level": False, - "maximum_level": False, - "invert_switch": False, - "auto_off_timer": False, - "default_level_local": False, - "default_level_remote": False, - "state_after_power_restored": False, - "load_level_indicator_timeout": False, - "active_power_reports": False, - "periodic_power_and_energy_reports": False, - "active_energy_reports": False, + "dimming_speed_up_remote": True, + "dimming_speed_up_local": True, + "ramp_rate_off_to_on_local": True, + "ramp_rate_off_to_on_remote": True, + "dimming_speed_down_remote": True, + "dimming_speed_down_local": True, + "ramp_rate_on_to_off_local": True, + "ramp_rate_on_to_off_remote": True, + "minimum_level": True, + "maximum_level": True, + "invert_switch": True, + "auto_off_timer": True, + "default_level_local": True, + "default_level_remote": True, + "state_after_power_restored": True, + "load_level_indicator_timeout": True, + "active_power_reports": True, + "periodic_power_and_energy_reports": True, + "active_energy_reports": True, "power_type": False, "switch_type": False, "button_delay": False, "smart_bulb_mode": False, - "double_tap_up_for_full_brightness": False, - "led_color_when_on": False, - "led_color_when_off": False, - "led_intensity_when_on": False, - "led_intensity_when_off": False, + "double_tap_up_for_full_brightness": True, + "led_color_when_on": True, + "led_color_when_off": True, + "led_intensity_when_on": True, + "led_intensity_when_off": True, "local_protection": False, "output_mode": False, - "on_off_led_mode": False, - "firmware_progress_led": False, - "relay_click_in_on_off_mode": False, + "on_off_led_mode": True, + "firmware_progress_led": True, + "relay_click_in_on_off_mode": True, + "disable_clear_notifications_double_tap": True, } async def issue_all_led_effect( self, - effect_type: LEDEffectType | int = LEDEffectType.Fast_Blink, + effect_type: AllLEDEffectType | int = AllLEDEffectType.Fast_Blink, color: int = 200, level: int = 100, duration: int = 3, @@ -251,7 +239,7 @@ class InovelliConfigEntityChannel(ZigbeeChannel): async def issue_individual_led_effect( self, led_number: int = 1, - effect_type: LEDEffectType | int = LEDEffectType.Fast_Blink, + effect_type: SingleLEDEffectType | int = SingleLEDEffectType.Fast_Blink, color: int = 200, level: int = 100, duration: int = 3, diff --git a/homeassistant/components/zha/device_action.py b/homeassistant/components/zha/device_action.py index 3e2a3591c80..01a08bc2f32 100644 --- a/homeassistant/components/zha/device_action.py +++ b/homeassistant/components/zha/device_action.py @@ -13,7 +13,7 @@ from homeassistant.helpers.typing import ConfigType, TemplateVarsType from . import DOMAIN from .api import SERVICE_WARNING_DEVICE_SQUAWK, SERVICE_WARNING_DEVICE_WARN -from .core.channels.manufacturerspecific import InovelliConfigEntityChannel +from .core.channels.manufacturerspecific import AllLEDEffectType, SingleLEDEffectType from .core.const import CHANNEL_IAS_WD, CHANNEL_INOVELLI from .core.helpers import async_get_zha_device @@ -40,9 +40,7 @@ INOVELLI_ALL_LED_EFFECT_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend( { vol.Required(CONF_TYPE): INOVELLI_ALL_LED_EFFECT, vol.Required(CONF_DOMAIN): DOMAIN, - vol.Required( - "effect_type" - ): InovelliConfigEntityChannel.LEDEffectType.__getitem__, + vol.Required("effect_type"): AllLEDEffectType.__getitem__, vol.Required("color"): vol.All(vol.Coerce(int), vol.Range(0, 255)), vol.Required("level"): vol.All(vol.Coerce(int), vol.Range(0, 100)), vol.Required("duration"): vol.All(vol.Coerce(int), vol.Range(1, 255)), @@ -52,10 +50,16 @@ INOVELLI_ALL_LED_EFFECT_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend( INOVELLI_INDIVIDUAL_LED_EFFECT_SCHEMA = INOVELLI_ALL_LED_EFFECT_SCHEMA.extend( { vol.Required(CONF_TYPE): INOVELLI_INDIVIDUAL_LED_EFFECT, - vol.Required("led_number"): vol.All(vol.Coerce(int), vol.Range(1, 7)), + vol.Required("effect_type"): SingleLEDEffectType.__getitem__, + vol.Required("led_number"): vol.All(vol.Coerce(int), vol.Range(0, 6)), } ) +ACTION_SCHEMA_MAP = { + INOVELLI_ALL_LED_EFFECT: INOVELLI_ALL_LED_EFFECT_SCHEMA, + INOVELLI_INDIVIDUAL_LED_EFFECT: INOVELLI_INDIVIDUAL_LED_EFFECT_SCHEMA, +} + ACTION_SCHEMA = vol.Any( INOVELLI_ALL_LED_EFFECT_SCHEMA, INOVELLI_INDIVIDUAL_LED_EFFECT_SCHEMA, @@ -83,9 +87,7 @@ DEVICE_ACTION_TYPES = { DEVICE_ACTION_SCHEMAS = { INOVELLI_ALL_LED_EFFECT: vol.Schema( { - vol.Required("effect_type"): vol.In( - InovelliConfigEntityChannel.LEDEffectType.__members__.keys() - ), + vol.Required("effect_type"): vol.In(AllLEDEffectType.__members__.keys()), vol.Required("color"): vol.All(vol.Coerce(int), vol.Range(0, 255)), vol.Required("level"): vol.All(vol.Coerce(int), vol.Range(0, 100)), vol.Required("duration"): vol.All(vol.Coerce(int), vol.Range(1, 255)), @@ -94,9 +96,7 @@ DEVICE_ACTION_SCHEMAS = { INOVELLI_INDIVIDUAL_LED_EFFECT: vol.Schema( { vol.Required("led_number"): vol.All(vol.Coerce(int), vol.Range(0, 6)), - vol.Required("effect_type"): vol.In( - InovelliConfigEntityChannel.LEDEffectType.__members__.keys() - ), + vol.Required("effect_type"): vol.In(SingleLEDEffectType.__members__.keys()), vol.Required("color"): vol.All(vol.Coerce(int), vol.Range(0, 255)), vol.Required("level"): vol.All(vol.Coerce(int), vol.Range(0, 100)), vol.Required("duration"): vol.All(vol.Coerce(int), vol.Range(1, 255)), @@ -127,6 +127,15 @@ async def async_call_action_from_config( ) +async def async_validate_action_config( + hass: HomeAssistant, config: ConfigType +) -> ConfigType: + """Validate config.""" + schema = ACTION_SCHEMA_MAP.get(config[CONF_TYPE], DEFAULT_ACTION_SCHEMA) + config = schema(config) + return config + + async def async_get_actions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index e40a54c11bc..c8aebe3b0c0 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -7,7 +7,7 @@ "bellows==0.34.2", "pyserial==3.5", "pyserial-asyncio==0.6", - "zha-quirks==0.0.84", + "zha-quirks==0.0.85", "zigpy-deconz==0.19.0", "zigpy==0.51.5", "zigpy-xbee==0.16.2", diff --git a/homeassistant/components/zha/switch.py b/homeassistant/components/zha/switch.py index 0bd55cdbe68..0c2e5e7ebe2 100644 --- a/homeassistant/components/zha/switch.py +++ b/homeassistant/components/zha/switch.py @@ -418,3 +418,15 @@ class InovelliRelayClickInOnOffMode( _zcl_attribute: str = "relay_click_in_on_off_mode" _attr_name: str = "Disable relay click in on off mode" + + +@CONFIG_DIAGNOSTIC_MATCH( + channel_names=CHANNEL_INOVELLI, +) +class InovelliDisableDoubleTapClearNotificationsMode( + ZHASwitchConfigurationEntity, id_suffix="disable_clear_notifications_double_tap" +): + """Inovelli disable clear notifications double tap control.""" + + _zcl_attribute: str = "disable_clear_notifications_double_tap" + _attr_name: str = "Disable config 2x tap to clear notifications" diff --git a/requirements_all.txt b/requirements_all.txt index 550dc61e18f..0b1541dd077 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2616,7 +2616,7 @@ zengge==0.2 zeroconf==0.39.4 # homeassistant.components.zha -zha-quirks==0.0.84 +zha-quirks==0.0.85 # homeassistant.components.zhong_hong zhong_hong_hvac==1.0.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e626dfe50b1..7061345c0f3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1817,7 +1817,7 @@ zamg==0.1.1 zeroconf==0.39.4 # homeassistant.components.zha -zha-quirks==0.0.84 +zha-quirks==0.0.85 # homeassistant.components.zha zigpy-deconz==0.19.0 diff --git a/tests/components/zha/test_device_action.py b/tests/components/zha/test_device_action.py index 584abbaecdb..19125558b52 100644 --- a/tests/components/zha/test_device_action.py +++ b/tests/components/zha/test_device_action.py @@ -290,7 +290,7 @@ async def test_action(hass, device_ias, device_inovelli): "domain": DOMAIN, "device_id": inovelli_reg_device.id, "type": "issue_individual_led_effect", - "effect_type": "Open_Close", + "effect_type": "Falling", "led_number": 1, "duration": 5, "level": 10, From 9b2a8901b17539d6b46b1c929d563686fb6d5794 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 7 Nov 2022 12:31:11 +0100 Subject: [PATCH 0256/1033] Adjust payload sentinel in mqtt (#81553) * Adjust payload sentinel in mqtt * Add type hints * Update sensor.py * Adjust vacuum * Add type hints * Adjust schema basic * Remove invalid hint --- .../components/mqtt/light/schema_basic.py | 53 +++++++++++-------- homeassistant/components/mqtt/models.py | 13 +++-- homeassistant/components/mqtt/sensor.py | 33 +++++++----- .../components/mqtt/vacuum/schema_legacy.py | 40 +++++++++----- 4 files changed, 87 insertions(+), 52 deletions(-) diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index d435d4e91ad..bf2ae33ca1d 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -50,7 +50,12 @@ from ..const import ( ) from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity -from ..models import MqttCommandTemplate, MqttValueTemplate +from ..models import ( + MqttCommandTemplate, + MqttValueTemplate, + PayloadSentinel, + ReceiveMessage, +) from ..util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic from .schema import MQTT_LIGHT_SCHEMA_SCHEMA @@ -450,12 +455,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): @callback @log_messages(self.hass, self.entity_id) - def brightness_received(msg): + def brightness_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages for the brightness.""" payload = self._value_templates[CONF_BRIGHTNESS_VALUE_TEMPLATE]( - msg.payload, None + msg.payload, PayloadSentinel.DEFAULT ) - if not payload: + if payload is PayloadSentinel.DEFAULT or not payload: _LOGGER.debug("Ignoring empty brightness message from '%s'", msg.topic) return @@ -468,8 +473,10 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): def _rgbx_received(msg, template, color_mode, convert_color): """Handle new MQTT messages for RGBW and RGBWW.""" - payload = self._value_templates[template](msg.payload, None) - if not payload: + payload = self._value_templates[template]( + msg.payload, PayloadSentinel.DEFAULT + ) + if payload is PayloadSentinel.DEFAULT or not payload: _LOGGER.debug( "Ignoring empty %s message from '%s'", color_mode, msg.topic ) @@ -533,12 +540,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): @callback @log_messages(self.hass, self.entity_id) - def color_mode_received(msg): + def color_mode_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages for color mode.""" payload = self._value_templates[CONF_COLOR_MODE_VALUE_TEMPLATE]( - msg.payload, None + msg.payload, PayloadSentinel.DEFAULT ) - if not payload: + if payload is PayloadSentinel.DEFAULT or not payload: _LOGGER.debug("Ignoring empty color mode message from '%s'", msg.topic) return @@ -549,12 +556,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): @callback @log_messages(self.hass, self.entity_id) - def color_temp_received(msg): + def color_temp_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages for color temperature.""" payload = self._value_templates[CONF_COLOR_TEMP_VALUE_TEMPLATE]( - msg.payload, None + msg.payload, PayloadSentinel.DEFAULT ) - if not payload: + if payload is PayloadSentinel.DEFAULT or not payload: _LOGGER.debug("Ignoring empty color temp message from '%s'", msg.topic) return @@ -567,12 +574,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): @callback @log_messages(self.hass, self.entity_id) - def effect_received(msg): + def effect_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages for effect.""" payload = self._value_templates[CONF_EFFECT_VALUE_TEMPLATE]( - msg.payload, None + msg.payload, PayloadSentinel.DEFAULT ) - if not payload: + if payload is PayloadSentinel.DEFAULT or not payload: _LOGGER.debug("Ignoring empty effect message from '%s'", msg.topic) return @@ -583,10 +590,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): @callback @log_messages(self.hass, self.entity_id) - def hs_received(msg): + def hs_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages for hs color.""" - payload = self._value_templates[CONF_HS_VALUE_TEMPLATE](msg.payload, None) - if not payload: + payload = self._value_templates[CONF_HS_VALUE_TEMPLATE]( + msg.payload, PayloadSentinel.DEFAULT + ) + if payload is PayloadSentinel.DEFAULT or not payload: _LOGGER.debug("Ignoring empty hs message from '%s'", msg.topic) return try: @@ -602,10 +611,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): @callback @log_messages(self.hass, self.entity_id) - def xy_received(msg): + def xy_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages for xy color.""" - payload = self._value_templates[CONF_XY_VALUE_TEMPLATE](msg.payload, None) - if not payload: + payload = self._value_templates[CONF_XY_VALUE_TEMPLATE]( + msg.payload, PayloadSentinel.DEFAULT + ) + if payload is PayloadSentinel.DEFAULT or not payload: _LOGGER.debug("Ignoring empty xy-color message from '%s'", msg.topic) return diff --git a/homeassistant/components/mqtt/models.py b/homeassistant/components/mqtt/models.py index 363956cc732..a00097a6839 100644 --- a/homeassistant/components/mqtt/models.py +++ b/homeassistant/components/mqtt/models.py @@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, Any, TypedDict, Union import attr +from homeassistant.backports.enum import StrEnum from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.helpers import template @@ -26,7 +27,13 @@ if TYPE_CHECKING: from .discovery import MQTTDiscoveryPayload from .tag import MQTTTagScanner -_SENTINEL = object() + +class PayloadSentinel(StrEnum): + """Sentinel for `async_render_with_possible_json_value`.""" + + NONE = "none" + DEFAULT = "default" + _LOGGER = logging.getLogger(__name__) @@ -189,7 +196,7 @@ class MqttValueTemplate: def async_render_with_possible_json_value( self, payload: ReceivePayloadType, - default: ReceivePayloadType | object = _SENTINEL, + default: ReceivePayloadType | PayloadSentinel = PayloadSentinel.NONE, variables: TemplateVarsType = None, ) -> ReceivePayloadType: """Render with possible json value or pass-though a received MQTT value.""" @@ -213,7 +220,7 @@ class MqttValueTemplate: ) values[ATTR_THIS] = self._template_state - if default == _SENTINEL: + if default is PayloadSentinel.NONE: _LOGGER.debug( "Rendering incoming payload '%s' with variables %s and %s", payload, diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index 69077d30eee..4c6b5409962 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -45,7 +45,7 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttValueTemplate +from .models import MqttValueTemplate, PayloadSentinel, ReceiveMessage from .util import get_mqtt_data, valid_subscribe_topic _LOGGER = logging.getLogger(__name__) @@ -244,7 +244,7 @@ class MqttSensor(MqttEntity, RestoreSensor): """(Re)Subscribe to topics.""" topics = {} - def _update_state(msg): + def _update_state(msg: ReceiveMessage) -> None: # auto-expire enabled? expire_after = self._config.get(CONF_EXPIRE_AFTER) if expire_after is not None and expire_after > 0: @@ -262,20 +262,25 @@ class MqttSensor(MqttEntity, RestoreSensor): self.hass, self._value_is_expired, expiration_at ) - payload = self._template(msg.payload, default=self.native_value) - - if payload is not None and self.device_class in ( + payload = self._template(msg.payload, default=PayloadSentinel.DEFAULT) + if payload is PayloadSentinel.DEFAULT: + return + if self.device_class not in { SensorDeviceClass.DATE, SensorDeviceClass.TIMESTAMP, - ): - if (payload := dt_util.parse_datetime(payload)) is None: - _LOGGER.warning( - "Invalid state message '%s' from '%s'", msg.payload, msg.topic - ) - elif self.device_class == SensorDeviceClass.DATE: - payload = payload.date() - - self._attr_native_value = payload + }: + self._attr_native_value = str(payload) + return + if (payload_datetime := dt_util.parse_datetime(str(payload))) is None: + _LOGGER.warning( + "Invalid state message '%s' from '%s'", msg.payload, msg.topic + ) + self._attr_native_value = None + return + if self.device_class == SensorDeviceClass.DATE: + self._attr_native_value = payload_datetime.date() + return + self._attr_native_value = payload_datetime def _update_last_reset(msg): payload = self._last_reset_template(msg.payload) diff --git a/homeassistant/components/mqtt/vacuum/schema_legacy.py b/homeassistant/components/mqtt/vacuum/schema_legacy.py index 09c4448fda7..a15367c3cad 100644 --- a/homeassistant/components/mqtt/vacuum/schema_legacy.py +++ b/homeassistant/components/mqtt/vacuum/schema_legacy.py @@ -19,7 +19,7 @@ from ..config import MQTT_BASE_SCHEMA from ..const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema -from ..models import MqttValueTemplate +from ..models import MqttValueTemplate, PayloadSentinel, ReceiveMessage from ..util import get_mqtt_data, valid_publish_topic from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services @@ -246,7 +246,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): @callback @log_messages(self.hass, self.entity_id) - def message_received(msg): + def message_received(msg: ReceiveMessage) -> None: """Handle new MQTT message.""" if ( msg.topic == self._state_topics[CONF_BATTERY_LEVEL_TOPIC] @@ -254,8 +254,10 @@ class MqttVacuum(MqttEntity, VacuumEntity): ): battery_level = self._templates[ CONF_BATTERY_LEVEL_TEMPLATE - ].async_render_with_possible_json_value(msg.payload, None) - if battery_level: + ].async_render_with_possible_json_value( + msg.payload, PayloadSentinel.DEFAULT + ) + if battery_level and battery_level is not PayloadSentinel.DEFAULT: self._battery_level = int(battery_level) if ( @@ -264,8 +266,10 @@ class MqttVacuum(MqttEntity, VacuumEntity): ): charging = self._templates[ CONF_CHARGING_TEMPLATE - ].async_render_with_possible_json_value(msg.payload, None) - if charging: + ].async_render_with_possible_json_value( + msg.payload, PayloadSentinel.DEFAULT + ) + if charging and charging is not PayloadSentinel.DEFAULT: self._charging = cv.boolean(charging) if ( @@ -274,8 +278,10 @@ class MqttVacuum(MqttEntity, VacuumEntity): ): cleaning = self._templates[ CONF_CLEANING_TEMPLATE - ].async_render_with_possible_json_value(msg.payload, None) - if cleaning: + ].async_render_with_possible_json_value( + msg.payload, PayloadSentinel.DEFAULT + ) + if cleaning and cleaning is not PayloadSentinel.DEFAULT: self._cleaning = cv.boolean(cleaning) if ( @@ -284,8 +290,10 @@ class MqttVacuum(MqttEntity, VacuumEntity): ): docked = self._templates[ CONF_DOCKED_TEMPLATE - ].async_render_with_possible_json_value(msg.payload, None) - if docked: + ].async_render_with_possible_json_value( + msg.payload, PayloadSentinel.DEFAULT + ) + if docked and docked is not PayloadSentinel.DEFAULT: self._docked = cv.boolean(docked) if ( @@ -294,8 +302,10 @@ class MqttVacuum(MqttEntity, VacuumEntity): ): error = self._templates[ CONF_ERROR_TEMPLATE - ].async_render_with_possible_json_value(msg.payload, None) - if error is not None: + ].async_render_with_possible_json_value( + msg.payload, PayloadSentinel.DEFAULT + ) + if error is not PayloadSentinel.DEFAULT: self._error = cv.string(error) if self._docked: @@ -316,8 +326,10 @@ class MqttVacuum(MqttEntity, VacuumEntity): ): fan_speed = self._templates[ CONF_FAN_SPEED_TEMPLATE - ].async_render_with_possible_json_value(msg.payload, None) - if fan_speed: + ].async_render_with_possible_json_value( + msg.payload, PayloadSentinel.DEFAULT + ) + if fan_speed and fan_speed is not PayloadSentinel.DEFAULT: self._fan_speed = fan_speed get_mqtt_data(self.hass).state_write_requests.write_state_request(self) From 934cec9778c220355bc3d407bf062a668aa81c92 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 7 Nov 2022 13:08:05 +0100 Subject: [PATCH 0257/1033] Modernize rest switch tests (#81306) * Adjust rest switch tests to use schema validation * Simplify * Use async_setup_component in tests * Rewrite tests * Add patch * Remove patch * Adjust mock --- tests/components/rest/test_switch.py | 471 ++++++++++++++++----------- 1 file changed, 272 insertions(+), 199 deletions(-) diff --git a/tests/components/rest/test_switch.py b/tests/components/rest/test_switch.py index 6275314bcf0..ae7d507e857 100644 --- a/tests/components/rest/test_switch.py +++ b/tests/components/rest/test_switch.py @@ -3,90 +3,114 @@ import asyncio from http import HTTPStatus import aiohttp +import pytest from homeassistant.components.rest import DOMAIN -import homeassistant.components.rest.switch as rest -from homeassistant.components.switch import SwitchDeviceClass +from homeassistant.components.rest.switch import ( + CONF_BODY_OFF, + CONF_BODY_ON, + CONF_STATE_RESOURCE, +) +from homeassistant.components.switch import ( + DOMAIN as SWITCH_DOMAIN, + SCAN_INTERVAL, + SwitchDeviceClass, +) from homeassistant.const import ( + ATTR_DEVICE_CLASS, + ATTR_ENTITY_ID, + ATTR_ENTITY_PICTURE, + ATTR_FRIENDLY_NAME, + ATTR_ICON, CONF_DEVICE_CLASS, CONF_HEADERS, + CONF_ICON, + CONF_METHOD, CONF_NAME, CONF_PARAMS, CONF_PLATFORM, CONF_RESOURCE, + CONF_UNIQUE_ID, CONTENT_TYPE_JSON, - Platform, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, + STATE_OFF, + STATE_ON, + STATE_UNKNOWN, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er -from homeassistant.helpers.template import Template +from homeassistant.helpers.template_entity import CONF_PICTURE from homeassistant.setup import async_setup_component +from homeassistant.util.dt import utcnow -from tests.common import assert_setup_component +from tests.common import assert_setup_component, async_fire_time_changed from tests.test_util.aiohttp import AiohttpClientMocker NAME = "foo" DEVICE_CLASS = SwitchDeviceClass.SWITCH -METHOD = "post" RESOURCE = "http://localhost/" STATE_RESOURCE = RESOURCE -PARAMS = None -async def test_setup_missing_config(hass: HomeAssistant) -> None: +async def test_setup_missing_config( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: """Test setup with configuration missing required entries.""" - assert not await rest.async_setup_platform(hass, {CONF_PLATFORM: DOMAIN}, None) + config = {SWITCH_DOMAIN: {CONF_PLATFORM: DOMAIN}} + assert await async_setup_component(hass, SWITCH_DOMAIN, config) + await hass.async_block_till_done() + assert_setup_component(0, SWITCH_DOMAIN) + assert "Invalid config for [switch.rest]: required key not provided" in caplog.text -async def test_setup_missing_schema(hass: HomeAssistant) -> None: +async def test_setup_missing_schema( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: """Test setup with resource missing schema.""" - assert not await rest.async_setup_platform( - hass, - {CONF_PLATFORM: DOMAIN, CONF_RESOURCE: "localhost"}, - None, - ) + config = {SWITCH_DOMAIN: {CONF_PLATFORM: DOMAIN, CONF_RESOURCE: "localhost"}} + assert await async_setup_component(hass, SWITCH_DOMAIN, config) + await hass.async_block_till_done() + assert_setup_component(0, SWITCH_DOMAIN) + assert "Invalid config for [switch.rest]: invalid url" in caplog.text async def test_setup_failed_connect( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + caplog: pytest.LogCaptureFixture, ) -> None: """Test setup when connection error occurs.""" - aioclient_mock.get("http://localhost", exc=aiohttp.ClientError) - assert not await rest.async_setup_platform( - hass, - {CONF_PLATFORM: DOMAIN, CONF_RESOURCE: "http://localhost"}, - None, - ) + aioclient_mock.get(RESOURCE, exc=aiohttp.ClientError) + config = {SWITCH_DOMAIN: {CONF_PLATFORM: DOMAIN, CONF_RESOURCE: RESOURCE}} + assert await async_setup_component(hass, SWITCH_DOMAIN, config) + await hass.async_block_till_done() + assert_setup_component(0, SWITCH_DOMAIN) + assert "No route to resource/endpoint" in caplog.text async def test_setup_timeout( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + caplog: pytest.LogCaptureFixture, ) -> None: """Test setup when connection timeout occurs.""" - aioclient_mock.get("http://localhost", exc=asyncio.TimeoutError()) - assert not await rest.async_setup_platform( - hass, - {CONF_PLATFORM: DOMAIN, CONF_RESOURCE: "http://localhost"}, - None, - ) + aioclient_mock.get(RESOURCE, exc=asyncio.TimeoutError()) + config = {SWITCH_DOMAIN: {CONF_PLATFORM: DOMAIN, CONF_RESOURCE: RESOURCE}} + assert await async_setup_component(hass, SWITCH_DOMAIN, config) + await hass.async_block_till_done() + assert_setup_component(0, SWITCH_DOMAIN) + assert "No route to resource/endpoint" in caplog.text async def test_setup_minimum( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test setup with minimum configuration.""" - aioclient_mock.get("http://localhost", status=HTTPStatus.OK) - with assert_setup_component(1, Platform.SWITCH): - assert await async_setup_component( - hass, - Platform.SWITCH, - { - Platform.SWITCH: { - CONF_PLATFORM: DOMAIN, - CONF_RESOURCE: "http://localhost", - } - }, - ) + aioclient_mock.get(RESOURCE, status=HTTPStatus.OK) + config = {SWITCH_DOMAIN: {CONF_PLATFORM: DOMAIN, CONF_RESOURCE: RESOURCE}} + with assert_setup_component(1, SWITCH_DOMAIN): + assert await async_setup_component(hass, SWITCH_DOMAIN, config) await hass.async_block_till_done() assert aioclient_mock.call_count == 1 @@ -96,18 +120,15 @@ async def test_setup_query_params( ) -> None: """Test setup with query params.""" aioclient_mock.get("http://localhost/?search=something", status=HTTPStatus.OK) - with assert_setup_component(1, Platform.SWITCH): - assert await async_setup_component( - hass, - Platform.SWITCH, - { - Platform.SWITCH: { - CONF_PLATFORM: DOMAIN, - CONF_RESOURCE: "http://localhost", - CONF_PARAMS: {"search": "something"}, - } - }, - ) + config = { + SWITCH_DOMAIN: { + CONF_PLATFORM: DOMAIN, + CONF_RESOURCE: RESOURCE, + CONF_PARAMS: {"search": "something"}, + } + } + with assert_setup_component(1, SWITCH_DOMAIN): + assert await async_setup_component(hass, SWITCH_DOMAIN, config) await hass.async_block_till_done() assert aioclient_mock.call_count == 1 @@ -115,244 +136,296 @@ async def test_setup_query_params( async def test_setup(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker) -> None: """Test setup with valid configuration.""" - aioclient_mock.get("http://localhost", status=HTTPStatus.OK) - assert await async_setup_component( - hass, - Platform.SWITCH, - { - Platform.SWITCH: { - CONF_PLATFORM: DOMAIN, - CONF_NAME: "foo", - CONF_RESOURCE: "http://localhost", - CONF_HEADERS: {"Content-type": CONTENT_TYPE_JSON}, - rest.CONF_BODY_ON: "custom on text", - rest.CONF_BODY_OFF: "custom off text", - } - }, - ) + aioclient_mock.get(RESOURCE, status=HTTPStatus.OK) + config = { + SWITCH_DOMAIN: { + CONF_PLATFORM: DOMAIN, + CONF_NAME: "foo", + CONF_RESOURCE: RESOURCE, + CONF_HEADERS: {"Content-type": CONTENT_TYPE_JSON}, + CONF_BODY_ON: "custom on text", + CONF_BODY_OFF: "custom off text", + } + } + assert await async_setup_component(hass, SWITCH_DOMAIN, config) await hass.async_block_till_done() assert aioclient_mock.call_count == 1 - assert_setup_component(1, Platform.SWITCH) + assert_setup_component(1, SWITCH_DOMAIN) async def test_setup_with_state_resource( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test setup with valid configuration.""" - aioclient_mock.get("http://localhost", status=HTTPStatus.NOT_FOUND) + aioclient_mock.get(RESOURCE, status=HTTPStatus.NOT_FOUND) aioclient_mock.get("http://localhost/state", status=HTTPStatus.OK) - assert await async_setup_component( - hass, - Platform.SWITCH, - { - Platform.SWITCH: { - CONF_PLATFORM: DOMAIN, - CONF_NAME: "foo", - CONF_RESOURCE: "http://localhost", - rest.CONF_STATE_RESOURCE: "http://localhost/state", - CONF_HEADERS: {"Content-type": CONTENT_TYPE_JSON}, - rest.CONF_BODY_ON: "custom on text", - rest.CONF_BODY_OFF: "custom off text", - } - }, - ) + config = { + SWITCH_DOMAIN: { + CONF_PLATFORM: DOMAIN, + CONF_NAME: "foo", + CONF_RESOURCE: RESOURCE, + CONF_STATE_RESOURCE: "http://localhost/state", + CONF_HEADERS: {"Content-type": CONTENT_TYPE_JSON}, + CONF_BODY_ON: "custom on text", + CONF_BODY_OFF: "custom off text", + } + } + assert await async_setup_component(hass, SWITCH_DOMAIN, config) await hass.async_block_till_done() assert aioclient_mock.call_count == 1 - assert_setup_component(1, Platform.SWITCH) + assert_setup_component(1, SWITCH_DOMAIN) async def test_setup_with_templated_headers_params( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test setup with valid configuration.""" - aioclient_mock.get("http://localhost", status=HTTPStatus.OK) - assert await async_setup_component( - hass, - Platform.SWITCH, - { - Platform.SWITCH: { - CONF_PLATFORM: DOMAIN, - CONF_NAME: "foo", - CONF_RESOURCE: "http://localhost", - CONF_HEADERS: { - "Accept": CONTENT_TYPE_JSON, - "User-Agent": "Mozilla/{{ 3 + 2 }}.0", - }, - CONF_PARAMS: { - "start": 0, - "end": "{{ 3 + 2 }}", - }, - } - }, - ) + aioclient_mock.get(RESOURCE, status=HTTPStatus.OK) + config = { + SWITCH_DOMAIN: { + CONF_PLATFORM: DOMAIN, + CONF_NAME: "foo", + CONF_RESOURCE: "http://localhost", + CONF_HEADERS: { + "Accept": CONTENT_TYPE_JSON, + "User-Agent": "Mozilla/{{ 3 + 2 }}.0", + }, + CONF_PARAMS: { + "start": 0, + "end": "{{ 3 + 2 }}", + }, + } + } + assert await async_setup_component(hass, SWITCH_DOMAIN, config) await hass.async_block_till_done() assert aioclient_mock.call_count == 1 assert aioclient_mock.mock_calls[-1][3].get("Accept") == CONTENT_TYPE_JSON assert aioclient_mock.mock_calls[-1][3].get("User-Agent") == "Mozilla/5.0" assert aioclient_mock.mock_calls[-1][1].query["start"] == "0" assert aioclient_mock.mock_calls[-1][1].query["end"] == "5" - assert_setup_component(1, Platform.SWITCH) + assert_setup_component(1, SWITCH_DOMAIN) -"""Tests for REST switch platform.""" +# Tests for REST switch platform. -def _setup_test_switch(hass: HomeAssistant) -> None: - body_on = Template("on", hass) - body_off = Template("off", hass) - headers = {"Content-type": Template(CONTENT_TYPE_JSON, hass)} - switch = rest.RestSwitch( - hass, - { - CONF_NAME: Template(NAME, hass), - CONF_DEVICE_CLASS: DEVICE_CLASS, - CONF_RESOURCE: RESOURCE, - rest.CONF_STATE_RESOURCE: STATE_RESOURCE, - rest.CONF_METHOD: METHOD, - rest.CONF_HEADERS: headers, - rest.CONF_PARAMS: PARAMS, - rest.CONF_BODY_ON: body_on, - rest.CONF_BODY_OFF: body_off, - rest.CONF_IS_ON_TEMPLATE: None, - rest.CONF_TIMEOUT: 10, - rest.CONF_VERIFY_SSL: True, - }, - None, - ) - switch.hass = hass - return switch, body_on, body_off +async def _async_setup_test_switch( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + aioclient_mock.get(RESOURCE, status=HTTPStatus.OK) + + headers = {"Content-type": CONTENT_TYPE_JSON} + config = { + CONF_PLATFORM: DOMAIN, + CONF_NAME: NAME, + CONF_DEVICE_CLASS: DEVICE_CLASS, + CONF_RESOURCE: RESOURCE, + CONF_STATE_RESOURCE: STATE_RESOURCE, + CONF_HEADERS: headers, + } + + assert await async_setup_component(hass, SWITCH_DOMAIN, {SWITCH_DOMAIN: config}) + await hass.async_block_till_done() + assert_setup_component(1, SWITCH_DOMAIN) + + assert hass.states.get("switch.foo").state == STATE_UNKNOWN + aioclient_mock.clear_requests() -def test_name(hass: HomeAssistant) -> None: +async def test_name(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker) -> None: """Test the name.""" - switch, body_on, body_off = _setup_test_switch(hass) - assert switch.name == NAME + await _async_setup_test_switch(hass, aioclient_mock) + + state = hass.states.get("switch.foo") + assert state.attributes[ATTR_FRIENDLY_NAME] == NAME -def test_device_class(hass: HomeAssistant) -> None: - """Test the name.""" - switch, body_on, body_off = _setup_test_switch(hass) - assert switch.device_class == DEVICE_CLASS +async def test_device_class( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + """Test the device class.""" + await _async_setup_test_switch(hass, aioclient_mock) + + state = hass.states.get("switch.foo") + assert state.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS -def test_is_on_before_update(hass: HomeAssistant) -> None: +async def test_is_on_before_update( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test is_on in initial state.""" - switch, body_on, body_off = _setup_test_switch(hass) - assert switch.is_on is None + await _async_setup_test_switch(hass, aioclient_mock) + + state = hass.states.get("switch.foo") + assert state.state == STATE_UNKNOWN async def test_turn_on_success( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test turn_on.""" - aioclient_mock.post(RESOURCE, status=HTTPStatus.OK) - switch, body_on, body_off = _setup_test_switch(hass) - await switch.async_turn_on() + await _async_setup_test_switch(hass, aioclient_mock) - assert body_on.template == aioclient_mock.mock_calls[-1][2].decode() - assert switch.is_on + aioclient_mock.post(RESOURCE, status=HTTPStatus.OK) + aioclient_mock.get(RESOURCE, exc=aiohttp.ClientError) + assert await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.foo"}, + blocking=True, + ) + await hass.async_block_till_done() + + assert aioclient_mock.mock_calls[-2][2].decode() == "ON" + assert hass.states.get("switch.foo").state == STATE_ON async def test_turn_on_status_not_ok( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test turn_on when error status returned.""" - aioclient_mock.post(RESOURCE, status=HTTPStatus.INTERNAL_SERVER_ERROR) - switch, body_on, body_off = _setup_test_switch(hass) - await switch.async_turn_on() + await _async_setup_test_switch(hass, aioclient_mock) - assert body_on.template == aioclient_mock.mock_calls[-1][2].decode() - assert switch.is_on is None + aioclient_mock.post(RESOURCE, status=HTTPStatus.INTERNAL_SERVER_ERROR) + assert await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.foo"}, + blocking=True, + ) + await hass.async_block_till_done() + + assert aioclient_mock.mock_calls[-1][2].decode() == "ON" + assert hass.states.get("switch.foo").state == STATE_UNKNOWN async def test_turn_on_timeout( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test turn_on when timeout occurs.""" - aioclient_mock.post(RESOURCE, status=HTTPStatus.INTERNAL_SERVER_ERROR) - switch, body_on, body_off = _setup_test_switch(hass) - await switch.async_turn_on() + await _async_setup_test_switch(hass, aioclient_mock) - assert switch.is_on is None + aioclient_mock.post(RESOURCE, status=HTTPStatus.INTERNAL_SERVER_ERROR) + assert await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.foo"}, + blocking=True, + ) + await hass.async_block_till_done() + + assert hass.states.get("switch.foo").state == STATE_UNKNOWN async def test_turn_off_success( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test turn_off.""" - aioclient_mock.post(RESOURCE, status=HTTPStatus.OK) - switch, body_on, body_off = _setup_test_switch(hass) - await switch.async_turn_off() + await _async_setup_test_switch(hass, aioclient_mock) - assert body_off.template == aioclient_mock.mock_calls[-1][2].decode() - assert not switch.is_on + aioclient_mock.post(RESOURCE, status=HTTPStatus.OK) + aioclient_mock.get(RESOURCE, exc=aiohttp.ClientError) + assert await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.foo"}, + blocking=True, + ) + await hass.async_block_till_done() + + assert aioclient_mock.mock_calls[-2][2].decode() == "OFF" + + assert hass.states.get("switch.foo").state == STATE_OFF async def test_turn_off_status_not_ok( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test turn_off when error status returned.""" - aioclient_mock.post(RESOURCE, status=HTTPStatus.INTERNAL_SERVER_ERROR) - switch, body_on, body_off = _setup_test_switch(hass) - await switch.async_turn_off() + await _async_setup_test_switch(hass, aioclient_mock) - assert body_off.template == aioclient_mock.mock_calls[-1][2].decode() - assert switch.is_on is None + aioclient_mock.post(RESOURCE, status=HTTPStatus.INTERNAL_SERVER_ERROR) + assert await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.foo"}, + blocking=True, + ) + await hass.async_block_till_done() + + assert aioclient_mock.mock_calls[-1][2].decode() == "OFF" + + assert hass.states.get("switch.foo").state == STATE_UNKNOWN async def test_turn_off_timeout( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test turn_off when timeout occurs.""" - aioclient_mock.post(RESOURCE, exc=asyncio.TimeoutError()) - switch, body_on, body_off = _setup_test_switch(hass) - await switch.async_turn_on() + await _async_setup_test_switch(hass, aioclient_mock) - assert switch.is_on is None + aioclient_mock.post(RESOURCE, exc=asyncio.TimeoutError()) + assert await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.foo"}, + blocking=True, + ) + await hass.async_block_till_done() + + assert hass.states.get("switch.foo").state == STATE_UNKNOWN async def test_update_when_on( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test update when switch is on.""" - switch, body_on, body_off = _setup_test_switch(hass) - aioclient_mock.get(RESOURCE, text=body_on.template) - await switch.async_update() + await _async_setup_test_switch(hass, aioclient_mock) - assert switch.is_on + aioclient_mock.get(RESOURCE, text="ON") + async_fire_time_changed(hass, utcnow() + SCAN_INTERVAL) + await hass.async_block_till_done() + + assert hass.states.get("switch.foo").state == STATE_ON async def test_update_when_off( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test update when switch is off.""" - switch, body_on, body_off = _setup_test_switch(hass) - aioclient_mock.get(RESOURCE, text=body_off.template) - await switch.async_update() + await _async_setup_test_switch(hass, aioclient_mock) - assert not switch.is_on + aioclient_mock.get(RESOURCE, text="OFF") + async_fire_time_changed(hass, utcnow() + SCAN_INTERVAL) + await hass.async_block_till_done() + + assert hass.states.get("switch.foo").state == STATE_OFF async def test_update_when_unknown( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test update when unknown status returned.""" - aioclient_mock.get(RESOURCE, text="unknown status") - switch, body_on, body_off = _setup_test_switch(hass) - await switch.async_update() + await _async_setup_test_switch(hass, aioclient_mock) - assert switch.is_on is None + aioclient_mock.get(RESOURCE, text="unknown status") + async_fire_time_changed(hass, utcnow() + SCAN_INTERVAL) + await hass.async_block_till_done() + + assert hass.states.get("switch.foo").state == STATE_UNKNOWN async def test_update_timeout( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test update when timeout occurs.""" - aioclient_mock.get(RESOURCE, exc=asyncio.TimeoutError()) - switch, body_on, body_off = _setup_test_switch(hass) - await switch.async_update() + await _async_setup_test_switch(hass, aioclient_mock) - assert switch.is_on is None + aioclient_mock.get(RESOURCE, exc=asyncio.TimeoutError()) + async_fire_time_changed(hass, utcnow() + SCAN_INTERVAL) + await hass.async_block_till_done() + + assert hass.states.get("switch.foo").state == STATE_UNKNOWN async def test_entity_config( @@ -360,22 +433,22 @@ async def test_entity_config( ) -> None: """Test entity configuration.""" - aioclient_mock.get("http://localhost", status=HTTPStatus.OK) + aioclient_mock.get(RESOURCE, status=HTTPStatus.OK) config = { - Platform.SWITCH: { + SWITCH_DOMAIN: { # REST configuration - "platform": "rest", - "method": "POST", - "resource": "http://localhost", + CONF_PLATFORM: "rest", + CONF_METHOD: "POST", + CONF_RESOURCE: "http://localhost", # Entity configuration - "icon": "{{'mdi:one_two_three'}}", - "picture": "{{'blabla.png'}}", - "name": "{{'REST' + ' ' + 'Switch'}}", - "unique_id": "very_unique", + CONF_ICON: "{{'mdi:one_two_three'}}", + CONF_PICTURE: "{{'blabla.png'}}", + CONF_NAME: "{{'REST' + ' ' + 'Switch'}}", + CONF_UNIQUE_ID: "very_unique", }, } - assert await async_setup_component(hass, Platform.SWITCH, config) + assert await async_setup_component(hass, SWITCH_DOMAIN, config) await hass.async_block_till_done() entity_registry = er.async_get(hass) @@ -384,7 +457,7 @@ async def test_entity_config( state = hass.states.get("switch.rest_switch") assert state.state == "unknown" assert state.attributes == { - "entity_picture": "blabla.png", - "friendly_name": "REST Switch", - "icon": "mdi:one_two_three", + ATTR_ENTITY_PICTURE: "blabla.png", + ATTR_FRIENDLY_NAME: "REST Switch", + ATTR_ICON: "mdi:one_two_three", } From f479b2385efd7b88d9d593b3102de76580dec3ab Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 7 Nov 2022 14:02:49 +0100 Subject: [PATCH 0258/1033] Add type hints to rest switch (#81307) --- homeassistant/components/rest/switch.py | 50 ++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/rest/switch.py b/homeassistant/components/rest/switch.py index 7e470674b1e..cda35d1f918 100644 --- a/homeassistant/components/rest/switch.py +++ b/homeassistant/components/rest/switch.py @@ -81,8 +81,8 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the RESTful switch.""" - resource = config.get(CONF_RESOURCE) - unique_id = config.get(CONF_UNIQUE_ID) + resource: str = config[CONF_RESOURCE] + unique_id: str | None = config.get(CONF_UNIQUE_ID) try: switch = RestSwitch(hass, config, unique_id) @@ -106,10 +106,10 @@ class RestSwitch(TemplateEntity, SwitchEntity): def __init__( self, - hass, - config, - unique_id, - ): + hass: HomeAssistant, + config: ConfigType, + unique_id: str | None, + ) -> None: """Initialize the REST switch.""" TemplateEntity.__init__( self, @@ -119,30 +119,30 @@ class RestSwitch(TemplateEntity, SwitchEntity): unique_id=unique_id, ) - auth = None + auth: aiohttp.BasicAuth | None = None + username: str | None = None if username := config.get(CONF_USERNAME): - auth = aiohttp.BasicAuth(username, password=config[CONF_PASSWORD]) + password: str = config[CONF_PASSWORD] + auth = aiohttp.BasicAuth(username, password=password) - self._resource = config.get(CONF_RESOURCE) - self._state_resource = config.get(CONF_STATE_RESOURCE) or self._resource - self._method = config.get(CONF_METHOD) - self._headers = config.get(CONF_HEADERS) - self._params = config.get(CONF_PARAMS) + self._resource: str = config[CONF_RESOURCE] + self._state_resource: str = config.get(CONF_STATE_RESOURCE) or self._resource + self._method: str = config[CONF_METHOD] + self._headers: dict[str, template.Template] | None = config.get(CONF_HEADERS) + self._params: dict[str, template.Template] | None = config.get(CONF_PARAMS) self._auth = auth - self._body_on = config.get(CONF_BODY_ON) - self._body_off = config.get(CONF_BODY_OFF) - self._is_on_template = config.get(CONF_IS_ON_TEMPLATE) - self._timeout = config.get(CONF_TIMEOUT) - self._verify_ssl = config.get(CONF_VERIFY_SSL) + self._body_on: template.Template = config[CONF_BODY_ON] + self._body_off: template.Template = config[CONF_BODY_OFF] + self._is_on_template: template.Template | None = config.get(CONF_IS_ON_TEMPLATE) + self._timeout: int = config[CONF_TIMEOUT] + self._verify_ssl: bool = config[CONF_VERIFY_SSL] self._attr_device_class = config.get(CONF_DEVICE_CLASS) + self._body_on.hass = hass + self._body_off.hass = hass if (is_on_template := self._is_on_template) is not None: is_on_template.hass = hass - if (body_on := self._body_on) is not None: - body_on.hass = hass - if (body_off := self._body_off) is not None: - body_off.hass = hass template.attach(hass, self._headers) template.attach(hass, self._params) @@ -178,7 +178,7 @@ class RestSwitch(TemplateEntity, SwitchEntity): except (asyncio.TimeoutError, aiohttp.ClientError): _LOGGER.error("Error while switching off %s", self._resource) - async def set_device_state(self, body): + async def set_device_state(self, body: Any) -> aiohttp.ClientResponse: """Send a state update to the device.""" websession = async_get_clientsession(self.hass, self._verify_ssl) @@ -186,7 +186,7 @@ class RestSwitch(TemplateEntity, SwitchEntity): rendered_params = template.render_complex(self._params) async with async_timeout.timeout(self._timeout): - req = await getattr(websession, self._method)( + req: aiohttp.ClientResponse = await getattr(websession, self._method)( self._resource, auth=self._auth, data=bytes(body, "utf-8"), @@ -204,7 +204,7 @@ class RestSwitch(TemplateEntity, SwitchEntity): except aiohttp.ClientError as err: _LOGGER.exception("Error while fetching data: %s", err) - async def get_device_state(self, hass): + async def get_device_state(self, hass: HomeAssistant) -> aiohttp.ClientResponse: """Get the latest data from REST API and update the state.""" websession = async_get_clientsession(hass, self._verify_ssl) From 902e075d5880b6fec559239950ee4f12133f400a Mon Sep 17 00:00:00 2001 From: StefanIacobLivisi <109964424+StefanIacobLivisi@users.noreply.github.com> Date: Mon, 7 Nov 2022 15:40:23 +0200 Subject: [PATCH 0259/1033] Add livisi integration (#76863) --- CODEOWNERS | 2 + homeassistant/components/livisi/__init__.py | 57 +++++++ .../components/livisi/config_flow.py | 88 ++++++++++ homeassistant/components/livisi/const.py | 18 ++ .../components/livisi/coordinator.py | 132 ++++++++++++++ homeassistant/components/livisi/manifest.json | 9 + homeassistant/components/livisi/strings.json | 18 ++ homeassistant/components/livisi/switch.py | 161 ++++++++++++++++++ .../components/livisi/translations/en.json | 18 ++ homeassistant/generated/config_flows.py | 1 + homeassistant/generated/integrations.json | 6 + requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/livisi/__init__.py | 37 ++++ tests/components/livisi/test_config_flow.py | 68 ++++++++ 15 files changed, 621 insertions(+) create mode 100644 homeassistant/components/livisi/__init__.py create mode 100644 homeassistant/components/livisi/config_flow.py create mode 100644 homeassistant/components/livisi/const.py create mode 100644 homeassistant/components/livisi/coordinator.py create mode 100644 homeassistant/components/livisi/manifest.json create mode 100644 homeassistant/components/livisi/strings.json create mode 100644 homeassistant/components/livisi/switch.py create mode 100644 homeassistant/components/livisi/translations/en.json create mode 100644 tests/components/livisi/__init__.py create mode 100644 tests/components/livisi/test_config_flow.py diff --git a/CODEOWNERS b/CODEOWNERS index d0e4fc993ad..d33cf545038 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -635,6 +635,8 @@ build.json @home-assistant/supervisor /tests/components/litejet/ @joncar /homeassistant/components/litterrobot/ @natekspencer @tkdrob /tests/components/litterrobot/ @natekspencer @tkdrob +/homeassistant/components/livisi/ @StefanIacobLivisi +/tests/components/livisi/ @StefanIacobLivisi /homeassistant/components/local_ip/ @issacg /tests/components/local_ip/ @issacg /homeassistant/components/lock/ @home-assistant/core diff --git a/homeassistant/components/livisi/__init__.py b/homeassistant/components/livisi/__init__.py new file mode 100644 index 00000000000..38e0c9f8e7d --- /dev/null +++ b/homeassistant/components/livisi/__init__.py @@ -0,0 +1,57 @@ +"""The Livisi Smart Home integration.""" +from __future__ import annotations + +import asyncio +from typing import Final + +from aiohttp import ClientConnectorError +from aiolivisi import AioLivisi + +from homeassistant import core +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers import aiohttp_client, device_registry as dr + +from .const import DOMAIN, SWITCH_PLATFORM +from .coordinator import LivisiDataUpdateCoordinator + +PLATFORMS: Final = [SWITCH_PLATFORM] + + +async def async_setup_entry(hass: core.HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Livisi Smart Home from a config entry.""" + web_session = aiohttp_client.async_get_clientsession(hass) + aiolivisi = AioLivisi(web_session) + coordinator = LivisiDataUpdateCoordinator(hass, entry, aiolivisi) + try: + await coordinator.async_setup() + await coordinator.async_set_all_rooms() + except ClientConnectorError as exception: + raise ConfigEntryNotReady from exception + + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator + device_registry = dr.async_get(hass) + device_registry.async_get_or_create( + config_entry_id=coordinator.serial_number, + identifiers={(DOMAIN, entry.entry_id)}, + manufacturer="Livisi", + name=f"SHC {coordinator.controller_type} {coordinator.serial_number}", + ) + + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + await coordinator.async_config_entry_first_refresh() + asyncio.create_task(coordinator.ws_connect()) + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + coordinator = hass.data[DOMAIN][entry.entry_id] + + unload_success = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + await coordinator.websocket.disconnect() + if unload_success: + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_success diff --git a/homeassistant/components/livisi/config_flow.py b/homeassistant/components/livisi/config_flow.py new file mode 100644 index 00000000000..16cccaacfd1 --- /dev/null +++ b/homeassistant/components/livisi/config_flow.py @@ -0,0 +1,88 @@ +"""Config flow for Livisi Home Assistant.""" +from __future__ import annotations + +from contextlib import suppress +from typing import Any + +from aiohttp import ClientConnectorError +from aiolivisi import AioLivisi, errors as livisi_errors +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import aiohttp_client + +from .const import CONF_HOST, CONF_PASSWORD, DOMAIN, LOGGER + + +class LivisiFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a Livisi Smart Home config flow.""" + + def __init__(self) -> None: + """Create the configuration file.""" + self.aio_livisi: AioLivisi = None + self.data_schema = vol.Schema( + { + vol.Required(CONF_HOST): str, + vol.Required(CONF_PASSWORD): str, + } + ) + + async def async_step_user( + self, user_input: dict[str, str] | None = None + ) -> FlowResult: + """Handle the initial step.""" + if user_input is None: + return self.async_show_form(step_id="user", data_schema=self.data_schema) + + errors = {} + try: + await self._login(user_input) + except livisi_errors.WrongCredentialException: + errors["base"] = "wrong_password" + except livisi_errors.ShcUnreachableException: + errors["base"] = "cannot_connect" + except livisi_errors.IncorrectIpAddressException: + errors["base"] = "wrong_ip_address" + else: + controller_info: dict[str, Any] = {} + with suppress(ClientConnectorError): + controller_info = await self.aio_livisi.async_get_controller() + if controller_info: + return await self.create_entity(user_input, controller_info) + errors["base"] = "cannot_connect" + + return self.async_show_form( + step_id="user", data_schema=self.data_schema, errors=errors + ) + + async def _login(self, user_input: dict[str, str]) -> None: + """Login into Livisi Smart Home.""" + web_session = aiohttp_client.async_get_clientsession(self.hass) + self.aio_livisi = AioLivisi(web_session) + livisi_connection_data = { + "ip_address": user_input[CONF_HOST], + "password": user_input[CONF_PASSWORD], + } + + await self.aio_livisi.async_set_token(livisi_connection_data) + + async def create_entity( + self, user_input: dict[str, str], controller_info: dict[str, Any] + ) -> FlowResult: + """Create LIVISI entity.""" + if (controller_data := controller_info.get("gateway")) is None: + controller_data = controller_info + controller_type = controller_data["controllerType"] + LOGGER.debug( + "Integrating SHC %s with serial number: %s", + controller_type, + controller_data["serialNumber"], + ) + + return self.async_create_entry( + title=f"SHC {controller_type}", + data={ + **user_input, + }, + ) diff --git a/homeassistant/components/livisi/const.py b/homeassistant/components/livisi/const.py new file mode 100644 index 00000000000..e6abc5118de --- /dev/null +++ b/homeassistant/components/livisi/const.py @@ -0,0 +1,18 @@ +"""Constants for the Livisi Smart Home integration.""" +import logging +from typing import Final + +LOGGER = logging.getLogger(__package__) +DOMAIN = "livisi" + +CONF_HOST = "host" +CONF_PASSWORD: Final = "password" +AVATAR_PORT: Final = 9090 +CLASSIC_PORT: Final = 8080 +DEVICE_POLLING_DELAY: Final = 60 +LIVISI_STATE_CHANGE: Final = "livisi_state_change" +LIVISI_REACHABILITY_CHANGE: Final = "livisi_reachability_change" + +SWITCH_PLATFORM: Final = "switch" + +PSS_DEVICE_TYPE: Final = "PSS" diff --git a/homeassistant/components/livisi/coordinator.py b/homeassistant/components/livisi/coordinator.py new file mode 100644 index 00000000000..70640c260fb --- /dev/null +++ b/homeassistant/components/livisi/coordinator.py @@ -0,0 +1,132 @@ +"""Code to manage fetching LIVISI data API.""" +from __future__ import annotations + +from datetime import timedelta +from typing import Any + +from aiohttp import ClientConnectorError +from aiolivisi import AioLivisi, LivisiEvent, Websocket + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +from .const import ( + AVATAR_PORT, + CLASSIC_PORT, + CONF_HOST, + CONF_PASSWORD, + DEVICE_POLLING_DELAY, + LIVISI_REACHABILITY_CHANGE, + LIVISI_STATE_CHANGE, + LOGGER, +) + + +class LivisiDataUpdateCoordinator(DataUpdateCoordinator[list[dict[str, Any]]]): + """Class to manage fetching LIVISI data API.""" + + config_entry: ConfigEntry + + def __init__( + self, hass: HomeAssistant, config_entry: ConfigEntry, aiolivisi: AioLivisi + ) -> None: + """Initialize my coordinator.""" + super().__init__( + hass, + LOGGER, + name="Livisi devices", + update_interval=timedelta(seconds=DEVICE_POLLING_DELAY), + ) + self.config_entry = config_entry + self.hass = hass + self.aiolivisi = aiolivisi + self.websocket = Websocket(aiolivisi) + self.devices: set[str] = set() + self.rooms: dict[str, Any] = {} + self.serial_number: str = "" + self.controller_type: str = "" + self.is_avatar: bool = False + self.port: int = 0 + + async def _async_update_data(self) -> list[dict[str, Any]]: + """Get device configuration from LIVISI.""" + try: + return await self.async_get_devices() + except ClientConnectorError as exc: + raise UpdateFailed("Failed to get LIVISI the devices") from exc + + async def async_setup(self) -> None: + """Set up the Livisi Smart Home Controller.""" + if not self.aiolivisi.livisi_connection_data: + livisi_connection_data = { + "ip_address": self.config_entry.data[CONF_HOST], + "password": self.config_entry.data[CONF_PASSWORD], + } + + await self.aiolivisi.async_set_token( + livisi_connection_data=livisi_connection_data + ) + controller_data = await self.aiolivisi.async_get_controller() + if controller_data["controllerType"] == "Avatar": + self.port = AVATAR_PORT + self.is_avatar = True + else: + self.port = CLASSIC_PORT + self.is_avatar = False + self.serial_number = controller_data["serialNumber"] + self.controller_type = controller_data["controllerType"] + + async def async_get_devices(self) -> list[dict[str, Any]]: + """Set the discovered devices list.""" + return await self.aiolivisi.async_get_devices() + + async def async_get_pss_state(self, capability: str) -> bool | None: + """Set the PSS state.""" + response: dict[str, Any] = await self.aiolivisi.async_get_pss_state( + capability[1:] + ) + if response is None: + return None + on_state = response["onState"] + return on_state["value"] + + async def async_set_all_rooms(self) -> None: + """Set the room list.""" + response: list[dict[str, Any]] = await self.aiolivisi.async_get_all_rooms() + + for available_room in response: + available_room_config: dict[str, Any] = available_room["config"] + self.rooms[available_room["id"]] = available_room_config["name"] + + def on_data(self, event_data: LivisiEvent) -> None: + """Define a handler to fire when the data is received.""" + if event_data.onState is not None: + async_dispatcher_send( + self.hass, + f"{LIVISI_STATE_CHANGE}_{event_data.source}", + event_data.onState, + ) + if event_data.isReachable is not None: + async_dispatcher_send( + self.hass, + f"{LIVISI_REACHABILITY_CHANGE}_{event_data.source}", + event_data.isReachable, + ) + + async def on_close(self) -> None: + """Define a handler to fire when the websocket is closed.""" + for device_id in self.devices: + is_reachable: bool = False + async_dispatcher_send( + self.hass, + f"{LIVISI_REACHABILITY_CHANGE}_{device_id}", + is_reachable, + ) + + await self.websocket.connect(self.on_data, self.on_close, self.port) + + async def ws_connect(self) -> None: + """Connect the websocket.""" + await self.websocket.connect(self.on_data, self.on_close, self.port) diff --git a/homeassistant/components/livisi/manifest.json b/homeassistant/components/livisi/manifest.json new file mode 100644 index 00000000000..83045d9eb60 --- /dev/null +++ b/homeassistant/components/livisi/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "livisi", + "name": "LIVISI Smart Home", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/livisi", + "requirements": ["aiolivisi==0.0.14"], + "codeowners": ["@StefanIacobLivisi"], + "iot_class": "local_polling" +} diff --git a/homeassistant/components/livisi/strings.json b/homeassistant/components/livisi/strings.json new file mode 100644 index 00000000000..260ef07234b --- /dev/null +++ b/homeassistant/components/livisi/strings.json @@ -0,0 +1,18 @@ +{ + "config": { + "step": { + "user": { + "description": "Enter the IP address and the (local) password of the SHC.", + "data": { + "host": "[%key:common::config_flow::data::ip%]", + "password": "[%key:common::config_flow::data::password%]" + } + } + }, + "error": { + "wrong_password": "The password is incorrect.", + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "wrong_ip_address": "The IP address is incorrect or the SHC cannot be reached locally." + } + } +} diff --git a/homeassistant/components/livisi/switch.py b/homeassistant/components/livisi/switch.py new file mode 100644 index 00000000000..bcb9a204411 --- /dev/null +++ b/homeassistant/components/livisi/switch.py @@ -0,0 +1,161 @@ +"""Code to handle a Livisi switches.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.components.switch import SwitchEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import ( + DOMAIN, + LIVISI_REACHABILITY_CHANGE, + LIVISI_STATE_CHANGE, + LOGGER, + PSS_DEVICE_TYPE, +) +from .coordinator import LivisiDataUpdateCoordinator + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up switch device.""" + coordinator: LivisiDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] + + @callback + def handle_coordinator_update() -> None: + """Add switch.""" + shc_devices: list[dict[str, Any]] = coordinator.data + entities: list[SwitchEntity] = [] + for device in shc_devices: + if ( + device["type"] == PSS_DEVICE_TYPE + and device["id"] not in coordinator.devices + ): + livisi_switch: SwitchEntity = create_entity( + config_entry, device, coordinator + ) + LOGGER.debug("Include device type: %s", device["type"]) + coordinator.devices.add(device["id"]) + entities.append(livisi_switch) + async_add_entities(entities) + + config_entry.async_on_unload( + coordinator.async_add_listener(handle_coordinator_update) + ) + + +def create_entity( + config_entry: ConfigEntry, + device: dict[str, Any], + coordinator: LivisiDataUpdateCoordinator, +) -> SwitchEntity: + """Create Switch Entity.""" + config_details: dict[str, Any] = device["config"] + capabilities: list = device["capabilities"] + room_id: str = device["location"] + room_name: str = coordinator.rooms[room_id] + livisi_switch = LivisiSwitch( + config_entry, + coordinator, + unique_id=device["id"], + manufacturer=device["manufacturer"], + device_type=device["type"], + name=config_details["name"], + capability_id=capabilities[0], + room=room_name, + ) + return livisi_switch + + +class LivisiSwitch(CoordinatorEntity[LivisiDataUpdateCoordinator], SwitchEntity): + """Represents the Livisi Switch.""" + + def __init__( + self, + config_entry: ConfigEntry, + coordinator: LivisiDataUpdateCoordinator, + unique_id: str, + manufacturer: str, + device_type: str, + name: str, + capability_id: str, + room: str, + ) -> None: + """Initialize the Livisi Switch.""" + self.config_entry = config_entry + self._attr_unique_id = unique_id + self._attr_name = name + self._capability_id = capability_id + self.aio_livisi = coordinator.aiolivisi + self._attr_available = False + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, unique_id)}, + manufacturer=manufacturer, + model=device_type, + name=name, + suggested_area=room, + via_device=(DOMAIN, config_entry.entry_id), + ) + super().__init__(coordinator) + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the entity on.""" + response = await self.aio_livisi.async_pss_set_state( + self._capability_id, is_on=True + ) + if response is None: + self._attr_available = False + raise HomeAssistantError(f"Failed to turn on {self._attr_name}") + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the entity off.""" + response = await self.aio_livisi.async_pss_set_state( + self._capability_id, is_on=False + ) + if response is None: + self._attr_available = False + raise HomeAssistantError(f"Failed to turn off {self._attr_name}") + + async def async_added_to_hass(self) -> None: + """Register callbacks.""" + response = await self.coordinator.async_get_pss_state(self._capability_id) + if response is None: + self._attr_is_on = False + self._attr_available = False + else: + self._attr_is_on = response + self.async_on_remove( + async_dispatcher_connect( + self.hass, + f"{LIVISI_STATE_CHANGE}_{self._capability_id}", + self.update_states, + ) + ) + self.async_on_remove( + async_dispatcher_connect( + self.hass, + f"{LIVISI_REACHABILITY_CHANGE}_{self.unique_id}", + self.update_reachability, + ) + ) + + @callback + def update_states(self, state: bool) -> None: + """Update the states of the switch device.""" + self._attr_is_on = state + self.async_write_ha_state() + + @callback + def update_reachability(self, is_reachable: bool) -> None: + """Update the reachability of the switch device.""" + self._attr_available = is_reachable + self.async_write_ha_state() diff --git a/homeassistant/components/livisi/translations/en.json b/homeassistant/components/livisi/translations/en.json new file mode 100644 index 00000000000..d561f09dd06 --- /dev/null +++ b/homeassistant/components/livisi/translations/en.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Failed to connect", + "wrong_ip_address": "The IP address is incorrect or the SHC cannot be reached locally.", + "wrong_password": "The password is incorrect." + }, + "step": { + "user": { + "data": { + "host": "IP Address", + "password": "Password" + }, + "description": "Enter the IP address and the (local) password of the SHC." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index b0cd687b54e..22dd4f491e0 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -215,6 +215,7 @@ FLOWS = { "lifx", "litejet", "litterrobot", + "livisi", "local_ip", "locative", "logi_circle", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 89de4fa92fc..d033c262fbd 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -2821,6 +2821,12 @@ "config_flow": true, "iot_class": "cloud_push" }, + "livisi": { + "name": "LIVISI Smart Home", + "integration_type": "hub", + "config_flow": true, + "iot_class": "local_polling" + }, "llamalab_automate": { "name": "LlamaLab Automate", "integration_type": "hub", diff --git a/requirements_all.txt b/requirements_all.txt index 0b1541dd077..b60e9e1792e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -201,6 +201,9 @@ aiolifx_effects==0.3.0 # homeassistant.components.lifx aiolifx_themes==0.2.0 +# homeassistant.components.livisi +aiolivisi==0.0.14 + # homeassistant.components.lookin aiolookin==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7061345c0f3..f485e7453d5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -179,6 +179,9 @@ aiolifx_effects==0.3.0 # homeassistant.components.lifx aiolifx_themes==0.2.0 +# homeassistant.components.livisi +aiolivisi==0.0.14 + # homeassistant.components.lookin aiolookin==0.1.1 diff --git a/tests/components/livisi/__init__.py b/tests/components/livisi/__init__.py new file mode 100644 index 00000000000..3d28d1db708 --- /dev/null +++ b/tests/components/livisi/__init__.py @@ -0,0 +1,37 @@ +"""Tests for the LIVISI Smart Home integration.""" +from unittest.mock import patch + +from homeassistant.components.livisi.const import CONF_HOST, CONF_PASSWORD + +VALID_CONFIG = { + CONF_HOST: "1.1.1.1", + CONF_PASSWORD: "test", +} + +DEVICE_CONFIG = { + "serialNumber": "1234", + "controllerType": "Classic", +} + + +def mocked_livisi_login(): + """Create mock for LIVISI login.""" + return patch( + "homeassistant.components.livisi.config_flow.AioLivisi.async_set_token" + ) + + +def mocked_livisi_controller(): + """Create mock data for LIVISI controller.""" + return patch( + "homeassistant.components.livisi.config_flow.AioLivisi.async_get_controller", + return_value=DEVICE_CONFIG, + ) + + +def mocked_livisi_setup_entry(): + """Create mock for LIVISI setup entry.""" + return patch( + "homeassistant.components.livisi.async_setup_entry", + return_value=True, + ) diff --git a/tests/components/livisi/test_config_flow.py b/tests/components/livisi/test_config_flow.py new file mode 100644 index 00000000000..c9924d39b9b --- /dev/null +++ b/tests/components/livisi/test_config_flow.py @@ -0,0 +1,68 @@ +"""Test the Livisi Home Assistant config flow.""" + +from unittest.mock import patch + +from aiolivisi import errors as livisi_errors +import pytest + +from homeassistant import data_entry_flow +from homeassistant.components.livisi.const import DOMAIN +from homeassistant.config_entries import SOURCE_USER + +from . import ( + VALID_CONFIG, + mocked_livisi_controller, + mocked_livisi_login, + mocked_livisi_setup_entry, +) + + +async def test_create_entry(hass): + """Test create LIVISI entity.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + + with mocked_livisi_login(), mocked_livisi_controller(), mocked_livisi_setup_entry(): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + VALID_CONFIG, + ) + + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["title"] == "SHC Classic" + assert result["data"]["host"] == "1.1.1.1" + assert result["data"]["password"] == "test" + + +@pytest.mark.parametrize( + "exception,expected_reason", + [ + (livisi_errors.ShcUnreachableException(), "cannot_connect"), + (livisi_errors.IncorrectIpAddressException(), "wrong_ip_address"), + (livisi_errors.WrongCredentialException(), "wrong_password"), + ], +) +async def test_create_entity_after_login_error( + hass, exception: livisi_errors.LivisiException, expected_reason: str +): + """Test the LIVISI integration can create an entity after the user had login errors.""" + with patch( + "homeassistant.components.livisi.config_flow.AioLivisi.async_set_token", + side_effect=exception, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_USER}, + ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], VALID_CONFIG + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["errors"]["base"] == expected_reason + with mocked_livisi_login(), mocked_livisi_controller(), mocked_livisi_setup_entry(): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input=VALID_CONFIG, + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY From 523c3089f7aaab51edaad157989b109d97b88095 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Mon, 7 Nov 2022 14:54:43 +0100 Subject: [PATCH 0260/1033] Add TI router transmit power config entity to ZHA (#81520) Make TI Router Transmit Power configurable in ZHA --- .../components/zha/core/channels/general.py | 6 ++++++ homeassistant/components/zha/number.py | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index de68ed7d8ef..3756e3c1233 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -161,6 +161,12 @@ class BasicChannel(ZigbeeChannel): self.ZCL_INIT_ATTRS.copy() ) self.ZCL_INIT_ATTRS["trigger_indicator"] = True + elif ( + self.cluster.endpoint.manufacturer == "TexasInstruments" + and self.cluster.endpoint.model == "ti.router" + ): + self.ZCL_INIT_ATTRS = self.ZCL_INIT_ATTRS.copy() + self.ZCL_INIT_ATTRS["transmit_power"] = True @registries.ZIGBEE_CHANNEL_REGISTRY.register(general.BinaryInput.cluster_id) diff --git a/homeassistant/components/zha/number.py b/homeassistant/components/zha/number.py index 1776cabf125..853ab189bbf 100644 --- a/homeassistant/components/zha/number.py +++ b/homeassistant/components/zha/number.py @@ -19,6 +19,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .core import discovery from .core.const import ( CHANNEL_ANALOG_OUTPUT, + CHANNEL_BASIC, CHANNEL_COLOR, CHANNEL_INOVELLI, CHANNEL_LEVEL, @@ -585,6 +586,20 @@ class FilterLifeTime(ZHANumberConfigurationEntity, id_suffix="filter_life_time") _attr_name = "Filter life time" +@CONFIG_DIAGNOSTIC_MATCH( + channel_names=CHANNEL_BASIC, + manufacturers={"TexasInstruments"}, + models={"ti.router"}, +) +class TiRouterTransmitPower(ZHANumberConfigurationEntity, id_suffix="transmit_power"): + """Representation of a ZHA TI transmit power configuration entity.""" + + _attr_native_min_value: float = -20 + _attr_native_max_value: float = 20 + _zcl_attribute: str = "transmit_power" + _attr_name = "Transmit power" + + @CONFIG_DIAGNOSTIC_MATCH(channel_names=CHANNEL_INOVELLI) class InovelliRemoteDimmingUpSpeed( ZHANumberConfigurationEntity, id_suffix="dimming_speed_up_remote" From ff18cece7b7d87eeeda4a72ffac4fdd624bc6ca4 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Mon, 7 Nov 2022 16:17:56 +0100 Subject: [PATCH 0261/1033] Add Brandt, Hexaom, SIMU & Ubiwizz as virtuals integrations of Overkiz (#79566) * Match supported brands with Overkiz libs * Add missing files for mypy --- homeassistant/components/brandt/__init__.py | 1 + homeassistant/components/brandt/manifest.json | 6 ++++++ homeassistant/components/hexaom/__init__.py | 1 + homeassistant/components/hexaom/manifest.json | 6 ++++++ homeassistant/components/simu/__init__.py | 1 + homeassistant/components/simu/manifest.json | 6 ++++++ homeassistant/components/ubiwizz/__init__.py | 1 + .../components/ubiwizz/manifest.json | 6 ++++++ homeassistant/generated/integrations.json | 20 +++++++++++++++++++ 9 files changed, 48 insertions(+) create mode 100644 homeassistant/components/brandt/__init__.py create mode 100644 homeassistant/components/brandt/manifest.json create mode 100644 homeassistant/components/hexaom/__init__.py create mode 100644 homeassistant/components/hexaom/manifest.json create mode 100644 homeassistant/components/simu/__init__.py create mode 100644 homeassistant/components/simu/manifest.json create mode 100644 homeassistant/components/ubiwizz/__init__.py create mode 100644 homeassistant/components/ubiwizz/manifest.json diff --git a/homeassistant/components/brandt/__init__.py b/homeassistant/components/brandt/__init__.py new file mode 100644 index 00000000000..d2c9435c863 --- /dev/null +++ b/homeassistant/components/brandt/__init__.py @@ -0,0 +1 @@ +"""Virtual integration: Brandt Smart Control.""" diff --git a/homeassistant/components/brandt/manifest.json b/homeassistant/components/brandt/manifest.json new file mode 100644 index 00000000000..4fab6b2ec16 --- /dev/null +++ b/homeassistant/components/brandt/manifest.json @@ -0,0 +1,6 @@ +{ + "domain": "brandt", + "name": "Brandt Smart Control", + "integration_type": "virtual", + "supported_by": "overkiz" +} diff --git a/homeassistant/components/hexaom/__init__.py b/homeassistant/components/hexaom/__init__.py new file mode 100644 index 00000000000..9b46a4f0e1c --- /dev/null +++ b/homeassistant/components/hexaom/__init__.py @@ -0,0 +1 @@ +"""Virtual integration: Hexaom Hexaconnect.""" diff --git a/homeassistant/components/hexaom/manifest.json b/homeassistant/components/hexaom/manifest.json new file mode 100644 index 00000000000..738ffdb4fbf --- /dev/null +++ b/homeassistant/components/hexaom/manifest.json @@ -0,0 +1,6 @@ +{ + "domain": "hexaom", + "name": "Hexaom Hexaconnect", + "integration_type": "virtual", + "supported_by": "overkiz" +} diff --git a/homeassistant/components/simu/__init__.py b/homeassistant/components/simu/__init__.py new file mode 100644 index 00000000000..7b02a1109f7 --- /dev/null +++ b/homeassistant/components/simu/__init__.py @@ -0,0 +1 @@ +"""Virtual integration: SIMU LiveIn2.""" diff --git a/homeassistant/components/simu/manifest.json b/homeassistant/components/simu/manifest.json new file mode 100644 index 00000000000..a1cf964f438 --- /dev/null +++ b/homeassistant/components/simu/manifest.json @@ -0,0 +1,6 @@ +{ + "domain": "simu", + "name": "SIMU LiveIn2", + "integration_type": "virtual", + "supported_by": "overkiz" +} diff --git a/homeassistant/components/ubiwizz/__init__.py b/homeassistant/components/ubiwizz/__init__.py new file mode 100644 index 00000000000..0126a15b983 --- /dev/null +++ b/homeassistant/components/ubiwizz/__init__.py @@ -0,0 +1 @@ +"""Virtual integration: Ubiwizz.""" diff --git a/homeassistant/components/ubiwizz/manifest.json b/homeassistant/components/ubiwizz/manifest.json new file mode 100644 index 00000000000..a6b5d6e7317 --- /dev/null +++ b/homeassistant/components/ubiwizz/manifest.json @@ -0,0 +1,6 @@ +{ + "domain": "ubiwizz", + "name": "Ubiwizz", + "integration_type": "virtual", + "supported_by": "overkiz" +} diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index d033c262fbd..69e7d22ba82 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -608,6 +608,11 @@ "config_flow": true, "iot_class": "local_push" }, + "brandt": { + "name": "Brandt Smart Control", + "integration_type": "virtual", + "supported_by": "overkiz" + }, "brel_home": { "name": "Brel Home", "integration_type": "virtual", @@ -2121,6 +2126,11 @@ "config_flow": true, "iot_class": "cloud_polling" }, + "hexaom": { + "name": "Hexaom Hexaconnect", + "integration_type": "virtual", + "supported_by": "overkiz" + }, "hi_kumo": { "name": "Hitachi Hi Kumo", "integration_type": "virtual", @@ -4699,6 +4709,11 @@ "integration_type": "virtual", "supported_by": "upb" }, + "simu": { + "name": "SIMU LiveIn2", + "integration_type": "virtual", + "supported_by": "overkiz" + }, "simulated": { "name": "Simulated", "integration_type": "hub", @@ -5607,6 +5622,11 @@ } } }, + "ubiwizz": { + "name": "Ubiwizz", + "integration_type": "virtual", + "supported_by": "overkiz" + }, "uk_transport": { "name": "UK Transport", "integration_type": "hub", From 43745dbc6c86f14993575449deda15f8bae4df4f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 7 Nov 2022 09:26:38 -0600 Subject: [PATCH 0262/1033] Pass explict time in logbook tests (#81725) The CI sets the timezone to US/Pacific and the logbook uses start_of_local_day when called without a time. We now call the logbook api with a specific time to avoid them being out of sync since the test would fail at CET 8:55am on Mon Nov 7th 2022 (and probably other dates) --- tests/components/logbook/test_init.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index fb7ac217867..366b4b30ed5 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -489,7 +489,15 @@ async def test_logbook_describe_event(recorder_mock, hass, hass_client): await async_wait_recording_done(hass) client = await hass_client() - response = await client.get("/api/logbook") + # Today time 00:00:00 + start = dt_util.utcnow().date() + start_date = datetime(start.year, start.month, start.day) + + # Test today entries with filter by end_time + end_time = start + timedelta(hours=24) + response = await client.get( + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}" + ) results = await response.json() assert len(results) == 1 event = results[0] @@ -553,7 +561,15 @@ async def test_exclude_described_event(recorder_mock, hass, hass_client): await async_wait_recording_done(hass) client = await hass_client() - response = await client.get("/api/logbook") + # Today time 00:00:00 + start = dt_util.utcnow().date() + start_date = datetime(start.year, start.month, start.day) + + # Test today entries with filter by end_time + end_time = start + timedelta(hours=24) + response = await client.get( + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}" + ) results = await response.json() assert len(results) == 1 event = results[0] From 3788a950e62fe3284ecf74d12bd61dcac933bce1 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Mon, 7 Nov 2022 11:14:57 -0500 Subject: [PATCH 0263/1033] Validate matcher field case in `usb.async_is_plugged_in` (#81514) * Support case-insensitive matching * Revert "Support case-insensitive matching" This reverts commit 0fdb2aa6bc6165d9adae39ecbe7f6698e7b94715. * Explicitly check the case of matcher fields in `async_is_plugged_in` --- homeassistant/components/usb/__init__.py | 18 +++++++++++++++ tests/components/usb/test_init.py | 29 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/homeassistant/components/usb/__init__.py b/homeassistant/components/usb/__init__.py index 7c0355fa24c..0f81d2e42d6 100644 --- a/homeassistant/components/usb/__init__.py +++ b/homeassistant/components/usb/__init__.py @@ -64,6 +64,24 @@ def async_register_scan_request_callback( @hass_callback def async_is_plugged_in(hass: HomeAssistant, matcher: USBCallbackMatcher) -> bool: """Return True is a USB device is present.""" + + vid = matcher.get("vid", "") + pid = matcher.get("pid", "") + serial_number = matcher.get("serial_number", "") + manufacturer = matcher.get("manufacturer", "") + description = matcher.get("description", "") + + if ( + vid != vid.upper() + or pid != pid.upper() + or serial_number != serial_number.lower() + or manufacturer != manufacturer.lower() + or description != description.lower() + ): + raise ValueError( + f"vid and pid must be uppercase, the rest lowercase in matcher {matcher!r}" + ) + usb_discovery: USBDiscovery = hass.data[DOMAIN] return any( _is_matching(USBDevice(*device_tuple), matcher) diff --git a/tests/components/usb/test_init.py b/tests/components/usb/test_init.py index ca978af75f2..c7196fed0c5 100644 --- a/tests/components/usb/test_init.py +++ b/tests/components/usb/test_init.py @@ -877,6 +877,35 @@ async def test_async_is_plugged_in(hass, hass_ws_client): assert usb.async_is_plugged_in(hass, matcher) +@pytest.mark.parametrize( + "matcher", + [ + {"vid": "abcd"}, + {"pid": "123a"}, + {"serial_number": "1234ABCD"}, + {"manufacturer": "Some Manufacturer"}, + {"description": "A description"}, + ], +) +async def test_async_is_plugged_in_case_enforcement(hass, matcher): + """Test `async_is_plugged_in` throws an error when incorrect cases are used.""" + + new_usb = [{"domain": "test1", "vid": "ABCD"}] + + with patch("pyudev.Context", side_effect=ImportError), patch( + "homeassistant.components.usb.async_get_usb", return_value=new_usb + ), patch("homeassistant.components.usb.comports", return_value=[]), patch.object( + hass.config_entries.flow, "async_init" + ): + assert await async_setup_component(hass, "usb", {"usb": {}}) + await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) + await hass.async_block_till_done() + + with pytest.raises(ValueError): + usb.async_is_plugged_in(hass, matcher) + + async def test_web_socket_triggers_discovery_request_callbacks(hass, hass_ws_client): """Test the websocket call triggers a discovery request callback.""" mock_callback = Mock() From 604cd46ec90266f188fe25c6f46aa555a5e016d2 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Mon, 7 Nov 2022 11:26:16 -0500 Subject: [PATCH 0264/1033] Enable SkyConnect config flow and use correct case in USB matching (#81522) * Ensure `USBCallbackMatcher` uses the appropriate case for each field * Enable the config flow for the SkyConnect integration * Update unit test --- .../homeassistant_sky_connect/__init__.py | 20 ++++++++++++---- .../homeassistant_sky_connect/manifest.json | 2 +- homeassistant/generated/config_flows.py | 1 + homeassistant/generated/usb.py | 6 +++++ .../homeassistant_sky_connect/test_init.py | 23 ++++++++++++------- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/homeassistant_sky_connect/__init__.py b/homeassistant/components/homeassistant_sky_connect/__init__.py index dd4cf013fab..cb00f0e32ec 100644 --- a/homeassistant/components/homeassistant_sky_connect/__init__.py +++ b/homeassistant/components/homeassistant_sky_connect/__init__.py @@ -1,16 +1,29 @@ """The Home Assistant Sky Connect integration.""" from __future__ import annotations -from typing import cast - from homeassistant.components import usb from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady +from .const import DOMAIN + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up a Home Assistant Sky Connect config entry.""" + matcher = usb.USBCallbackMatcher( + domain=DOMAIN, + vid=entry.data["vid"].upper(), + pid=entry.data["pid"].upper(), + serial_number=entry.data["serial_number"].lower(), + manufacturer=entry.data["manufacturer"].lower(), + description=entry.data["description"].lower(), + ) + + if not usb.async_is_plugged_in(hass, matcher): + # The USB dongle is not plugged in + raise ConfigEntryNotReady + usb_info = usb.UsbServiceInfo( device=entry.data["device"], vid=entry.data["vid"], @@ -19,9 +32,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: manufacturer=entry.data["manufacturer"], description=entry.data["description"], ) - if not usb.async_is_plugged_in(hass, cast(usb.USBCallbackMatcher, entry.data)): - # The USB dongle is not plugged in - raise ConfigEntryNotReady await hass.config_entries.flow.async_init( "zha", diff --git a/homeassistant/components/homeassistant_sky_connect/manifest.json b/homeassistant/components/homeassistant_sky_connect/manifest.json index 5ccb8bd5331..8fdc1d3c1d1 100644 --- a/homeassistant/components/homeassistant_sky_connect/manifest.json +++ b/homeassistant/components/homeassistant_sky_connect/manifest.json @@ -1,7 +1,7 @@ { "domain": "homeassistant_sky_connect", "name": "Home Assistant Sky Connect", - "config_flow": false, + "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homeassistant_sky_connect", "dependencies": ["hardware", "usb"], "codeowners": ["@home-assistant/core"], diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 22dd4f491e0..05e352ab2b7 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -161,6 +161,7 @@ FLOWS = { "hlk_sw16", "home_connect", "home_plus_control", + "homeassistant_sky_connect", "homekit", "homekit_controller", "homematicip_cloud", diff --git a/homeassistant/generated/usb.py b/homeassistant/generated/usb.py index 901f9f72da5..59b59bb7604 100644 --- a/homeassistant/generated/usb.py +++ b/homeassistant/generated/usb.py @@ -4,6 +4,12 @@ To update, run python3 -m script.hassfest """ USB = [ + { + "domain": "homeassistant_sky_connect", + "vid": "10C4", + "pid": "EA60", + "description": "*skyconnect v1.0*", + }, { "domain": "insteon", "vid": "10BF", diff --git a/tests/components/homeassistant_sky_connect/test_init.py b/tests/components/homeassistant_sky_connect/test_init.py index 05b883a9726..179a0eff8da 100644 --- a/tests/components/homeassistant_sky_connect/test_init.py +++ b/tests/components/homeassistant_sky_connect/test_init.py @@ -13,12 +13,12 @@ from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry CONFIG_ENTRY_DATA = { - "device": "bla_device", - "vid": "bla_vid", - "pid": "bla_pid", - "serial_number": "bla_serial_number", - "manufacturer": "bla_manufacturer", - "description": "bla_description", + "device": "/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_9e2adbd75b8beb119fe564a0f320645d-if00-port0", + "vid": "10C4", + "pid": "EA60", + "serial_number": "3c0ed67c628beb11b1cd64a0f320645d", + "manufacturer": "Nabu Casa", + "description": "SkyConnect v1.0", } @@ -67,6 +67,13 @@ async def test_setup_entry( await hass.async_block_till_done() assert len(mock_is_plugged_in.mock_calls) == 1 + matcher = mock_is_plugged_in.mock_calls[0].args[1] + assert matcher["vid"].isupper() + assert matcher["pid"].isupper() + assert matcher["serial_number"].islower() + assert matcher["manufacturer"].islower() + assert matcher["description"].islower() + # Finish setting up ZHA if num_entries > 0: zha_flows = hass.config_entries.flow.async_progress_by_handler("zha") @@ -119,12 +126,12 @@ async def test_setup_zha(mock_zha_config_flow_setup, hass: HomeAssistant) -> Non "device": { "baudrate": 115200, "flow_control": "software", - "path": "bla_device", + "path": CONFIG_ENTRY_DATA["device"], }, "radio_type": "ezsp", } assert config_entry.options == {} - assert config_entry.title == "bla_description" + assert config_entry.title == CONFIG_ENTRY_DATA["description"] async def test_setup_entry_wait_usb(hass: HomeAssistant) -> None: From 74357bef15f0e58fe6a2404e0510298062a12a2d Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Mon, 7 Nov 2022 11:28:28 -0500 Subject: [PATCH 0265/1033] Use a unique ID for the Yellow ZHA hardware discovery (#81523) * Set unique ID for hardware discovery * Use the provided `name` for discovered hardware --- homeassistant/components/zha/config_flow.py | 106 ++++++++++++-------- tests/components/zha/test_config_flow.py | 1 + 2 files changed, 65 insertions(+), 42 deletions(-) diff --git a/homeassistant/components/zha/config_flow.py b/homeassistant/components/zha/config_flow.py index 85f03b9f1f5..be422778e2d 100644 --- a/homeassistant/components/zha/config_flow.py +++ b/homeassistant/components/zha/config_flow.py @@ -76,6 +76,14 @@ ESPHOME_API_PORT = 6053 CONNECT_DELAY_S = 1.0 +HARDWARE_DISCOVERY_SCHEMA = vol.Schema( + { + vol.Required("name"): str, + vol.Required("port"): dict, + vol.Required("radio_type"): str, + } +) + _LOGGER = logging.getLogger(__name__) @@ -182,6 +190,13 @@ class BaseZhaFlow(FlowHandler): async with self._connect_zigpy_app() as app: await app.backups.restore_backup(backup, **kwargs) + def _parse_radio_type(self, radio_type: str) -> RadioType: + """Parse a radio type name, accounting for past aliases.""" + if radio_type == "efr32": + return RadioType.ezsp + + return RadioType[radio_type] + async def _detect_radio_type(self) -> bool: """Probe all radio types on the current port.""" for radio in AUTOPROBE_RADIOS: @@ -544,6 +559,24 @@ class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN VERSION = 3 + async def _set_unique_id_or_update_path( + self, unique_id: str, device_path: str + ) -> None: + """Set the flow's unique ID and update the device path if it isn't unique.""" + current_entry = await self.async_set_unique_id(unique_id) + + if not current_entry: + return + + self._abort_if_unique_id_configured( + updates={ + CONF_DEVICE: { + **current_entry.data.get(CONF_DEVICE, {}), + CONF_DEVICE_PATH: device_path, + }, + } + ) + @staticmethod @callback def async_get_options_flow( @@ -600,16 +633,11 @@ class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN manufacturer = discovery_info.manufacturer description = discovery_info.description dev_path = await self.hass.async_add_executor_job(usb.get_serial_by_id, device) - unique_id = f"{vid}:{pid}_{serial_number}_{manufacturer}_{description}" - if current_entry := await self.async_set_unique_id(unique_id): - self._abort_if_unique_id_configured( - updates={ - CONF_DEVICE: { - **current_entry.data.get(CONF_DEVICE, {}), - CONF_DEVICE_PATH: dev_path, - }, - } - ) + + await self._set_unique_id_or_update_path( + unique_id=f"{vid}:{pid}_{serial_number}_{manufacturer}_{description}", + device_path=dev_path, + ) # If they already have a discovery for deconz we ignore the usb discovery as # they probably want to use it there instead @@ -645,7 +673,9 @@ class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN port = DEFAULT_ZHA_ZEROCONF_PORT if "radio_type" in discovery_info.properties: - self._radio_type = RadioType[discovery_info.properties["radio_type"]] + self._radio_type = self._parse_radio_type( + discovery_info.properties["radio_type"] + ) elif "efr32" in local_name: self._radio_type = RadioType.ezsp else: @@ -654,15 +684,10 @@ class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN node_name = local_name[: -len(".local")] device_path = f"socket://{discovery_info.host}:{port}" - if current_entry := await self.async_set_unique_id(node_name): - self._abort_if_unique_id_configured( - updates={ - CONF_DEVICE: { - **current_entry.data.get(CONF_DEVICE, {}), - CONF_DEVICE_PATH: device_path, - }, - } - ) + await self._set_unique_id_or_update_path( + unique_id=node_name, + device_path=device_path, + ) self.context["title_placeholders"] = {CONF_NAME: node_name} self._title = device_path @@ -674,34 +699,31 @@ class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN self, data: dict[str, Any] | None = None ) -> FlowResult: """Handle hardware flow.""" - if not data: - return self.async_abort(reason="invalid_hardware_data") - if data.get("radio_type") != "efr32": - return self.async_abort(reason="invalid_hardware_data") - - self._radio_type = RadioType.ezsp - - schema = { - vol.Required( - CONF_DEVICE_PATH, default=self._device_path or vol.UNDEFINED - ): str - } - - radio_schema = self._radio_type.controller.SCHEMA_DEVICE.schema - assert not isinstance(radio_schema, vol.Schema) - - for param, value in radio_schema.items(): - if param in SUPPORTED_PORT_SETTINGS: - schema[param] = value - try: - device_settings = vol.Schema(schema)(data.get("port")) + discovery_data = HARDWARE_DISCOVERY_SCHEMA(data) except vol.Invalid: return self.async_abort(reason="invalid_hardware_data") - self._title = data.get("name", data["port"]["path"]) + name = discovery_data["name"] + radio_type = self._parse_radio_type(discovery_data["radio_type"]) + + try: + device_settings = radio_type.controller.SCHEMA_DEVICE( + discovery_data["port"] + ) + except vol.Invalid: + return self.async_abort(reason="invalid_hardware_data") + + await self._set_unique_id_or_update_path( + unique_id=f"{name}_{radio_type.name}_{device_settings[CONF_DEVICE_PATH]}", + device_path=device_settings[CONF_DEVICE_PATH], + ) + + self._title = name + self._radio_type = radio_type self._device_path = device_settings[CONF_DEVICE_PATH] self._device_settings = device_settings + self.context["title_placeholders"] = {CONF_NAME: name} return await self.async_step_confirm() diff --git a/tests/components/zha/test_config_flow.py b/tests/components/zha/test_config_flow.py index 725f9cc0917..dd6498fde43 100644 --- a/tests/components/zha/test_config_flow.py +++ b/tests/components/zha/test_config_flow.py @@ -987,6 +987,7 @@ async def test_hardware_already_setup(hass): ).add_to_hass(hass) data = { + "name": "Yellow", "radio_type": "efr32", "port": { "path": "/dev/ttyAMA1", From b9c47ed3c3c24da889b76e3d326bd26855246a32 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 7 Nov 2022 17:45:27 +0100 Subject: [PATCH 0266/1033] Align MQTT config entry setup strings with option flow (#81616) --- homeassistant/components/mqtt/strings.json | 48 +++++++++---------- .../components/mqtt/translations/en.json | 8 ++-- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/mqtt/strings.json b/homeassistant/components/mqtt/strings.json index 4799b45e631..8d3a4ad3445 100644 --- a/homeassistant/components/mqtt/strings.json +++ b/homeassistant/components/mqtt/strings.json @@ -19,10 +19,10 @@ "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]", "advanced_options": "Advanced options", - "certificate": "Path to custom CA certificate file", + "certificate": "Upload custom CA certificate file", "client_id": "Client ID (leave empty to randomly generated one)", - "client_cert": "Path to a client certificate file", - "client_key": "Path to a private key file", + "client_cert": "Upload client certificate file", + "client_key": "Upload private key file", "keepalive": "The time between sending keep alive messages", "tls_insecure": "Ignore broker certificate validation", "protocol": "MQTT protocol", @@ -80,29 +80,29 @@ "step": { "broker": { "title": "Broker options", - "description": "Please enter the connection information of your MQTT broker.", + "description": "[%key:component::mqtt::config::step::broker::description%]", "data": { - "broker": "Broker", + "broker": "[%key:component::mqtt::config::step::broker::data::broker%]", "port": "[%key:common::config_flow::data::port%]", "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]", - "advanced_options": "Advanced options", - "certificate": "Upload custom CA certificate file", - "client_id": "Client ID (leave empty to randomly generated one)", - "client_cert": "Upload client certificate file", - "client_key": "Upload private key file", - "keepalive": "The time between sending keep alive messages", - "tls_insecure": "Ignore broker certificate validation", - "protocol": "MQTT protocol", - "set_ca_cert": "Broker certificate validation", - "set_client_cert": "Use a client certificate" + "advanced_options": "[%key:component::mqtt::config::step::broker::data::advanced_options%]", + "certificate": "[%key:component::mqtt::config::step::broker::data::certificate%]", + "client_id": "[%key:component::mqtt::config::step::broker::data::client_id%]", + "client_cert": "[%key:component::mqtt::config::step::broker::data::client_cert%]", + "client_key": "[%key:component::mqtt::config::step::broker::data::client_key%]", + "keepalive": "[%key:component::mqtt::config::step::broker::data::keepalive%]", + "tls_insecure": "[%key:component::mqtt::config::step::broker::data::tls_insecure%]", + "protocol": "[%key:component::mqtt::config::step::broker::data::protocol%]", + "set_ca_cert": "[%key:component::mqtt::config::step::broker::data::set_ca_cert%]", + "set_client_cert": "[%key:component::mqtt::config::step::broker::data::set_client_cert%]" } }, "options": { "title": "MQTT options", "description": "Discovery - If discovery is enabled (recommended), Home Assistant will automatically discover devices and entities which publish their configuration on the MQTT broker. If discovery is disabled, all configuration must be done manually.\nDiscovery prefix - The prefix a configuration topic for automatic discovery must start with.\nBirth message - The birth message will be sent each time Home Assistant (re)connects to the MQTT broker.\nWill message - The will message will be sent each time Home Assistant loses its connection to the broker, both in case of a clean (e.g. Home Assistant shutting down) and in case of an unclean (e.g. Home Assistant crashing or losing its network connection) disconnect.", "data": { - "discovery": "Enable discovery", + "discovery": "[%key:component::mqtt::config::step::hassio_confirm::data::discovery%]", "discovery_prefix": "Discovery prefix", "birth_enable": "Enable birth message", "birth_topic": "Birth message topic", @@ -118,15 +118,15 @@ } }, "error": { - "bad_birth": "Invalid birth topic", - "bad_will": "Invalid will topic", - "bad_discovery_prefix": "Invalid discovery prefix", - "bad_certificate": "The CA certificate is invalid", - "bad_client_cert": "Invalid client certiticate, ensure a PEM coded file is supplied", - "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", - "bad_client_cert_key": "Client certificate and private are no valid pair", + "bad_birth": "[%key:component::mqtt::config::error::bad_birth%]", + "bad_will": "[%key:component::mqtt::config::error::bad_will%]", + "bad_discovery_prefix": "[%key:component::mqtt::config::error::bad_discovery_prefix%]", + "bad_certificate": "[%key:component::mqtt::config::error::bad_certificate%]", + "bad_client_cert": "[%key:component::mqtt::config::error::bad_client_cert%]", + "bad_client_key": "[%key:component::mqtt::config::error::bad_client_key%]", + "bad_client_cert_key": "[%key:component::mqtt::config::error::bad_client_cert_key%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", - "invalid_inclusion": "The client certificate and private key must be configured together" + "invalid_inclusion": "[%key:component::mqtt::config::error::invalid_inclusion%]" } } } diff --git a/homeassistant/components/mqtt/translations/en.json b/homeassistant/components/mqtt/translations/en.json index f60f457abd3..599fbf5be07 100644 --- a/homeassistant/components/mqtt/translations/en.json +++ b/homeassistant/components/mqtt/translations/en.json @@ -13,17 +13,17 @@ "bad_discovery_prefix": "Invalid discovery prefix", "bad_will": "Invalid will topic", "cannot_connect": "Failed to connect", - "invalid_inclusion": "The client certificate and private key must be configurered together" + "invalid_inclusion": "The client certificate and private key must be configured together" }, "step": { "broker": { "data": { "advanced_options": "Advanced options", "broker": "Broker", - "certificate": "Path to custom CA certificate file", - "client_cert": "Path to a client certificate file", + "certificate": "Upload custom CA certificate file", + "client_cert": "Upload client certificate file", "client_id": "Client ID (leave empty to randomly generated one)", - "client_key": "Path to a private key file", + "client_key": "Upload private key file", "discovery": "Enable discovery", "keepalive": "The time between sending keep alive messages", "password": "Password", From 1b9c2dfb6869ade1b9b910413e54b46886f9562f Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Mon, 7 Nov 2022 12:10:42 -0500 Subject: [PATCH 0267/1033] Bump pyunifiprotect to 4.4.1 (#81732) --- homeassistant/components/unifiprotect/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index 62baf74b6a7..7a412fede1d 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -4,7 +4,7 @@ "integration_type": "hub", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/unifiprotect", - "requirements": ["pyunifiprotect==4.4.0", "unifi-discovery==1.1.7"], + "requirements": ["pyunifiprotect==4.4.1", "unifi-discovery==1.1.7"], "dependencies": ["http"], "codeowners": ["@briis", "@AngellusMortis", "@bdraco"], "quality_scale": "platinum", diff --git a/requirements_all.txt b/requirements_all.txt index b60e9e1792e..61919196caf 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2087,7 +2087,7 @@ pytrafikverket==0.2.1 pyudev==0.23.2 # homeassistant.components.unifiprotect -pyunifiprotect==4.4.0 +pyunifiprotect==4.4.1 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f485e7453d5..fdb0a01bd23 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1450,7 +1450,7 @@ pytrafikverket==0.2.1 pyudev==0.23.2 # homeassistant.components.unifiprotect -pyunifiprotect==4.4.0 +pyunifiprotect==4.4.1 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 From e7a616b8ff1246331fba1d08711cfd955729df2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 7 Nov 2022 19:31:47 +0100 Subject: [PATCH 0268/1033] Use location info helper for IP in Cloudflare DNS (#81714) * Use location info helper for IP in Cloudflare DNS * simplify * Blow up * coverage --- .../components/cloudflare/__init__.py | 29 ++++++++-- tests/components/cloudflare/test_init.py | 58 ++++++++++++++++--- 2 files changed, 74 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/cloudflare/__init__.py b/homeassistant/components/cloudflare/__init__.py index df5b08e9395..3a8f6b39ae7 100644 --- a/homeassistant/components/cloudflare/__init__.py +++ b/homeassistant/components/cloudflare/__init__.py @@ -4,6 +4,7 @@ from __future__ import annotations from datetime import timedelta import logging +from aiohttp import ClientSession from pycfdns import CloudflareUpdater from pycfdns.exceptions import ( CloudflareAuthenticationException, @@ -14,10 +15,16 @@ from pycfdns.exceptions import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_TOKEN, CONF_ZONE from homeassistant.core import HomeAssistant, ServiceCall -from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.exceptions import ( + ConfigEntryAuthFailed, + ConfigEntryNotReady, + HomeAssistantError, +) from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import async_track_time_interval +from homeassistant.util.location import async_detect_location_info +from homeassistant.util.network import is_ipv4_address from .const import CONF_RECORDS, DEFAULT_UPDATE_INTERVAL, DOMAIN, SERVICE_UPDATE_RECORDS @@ -28,8 +35,9 @@ CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Cloudflare from a config entry.""" + session = async_get_clientsession(hass) cfupdate = CloudflareUpdater( - async_get_clientsession(hass), + session, entry.data[CONF_API_TOKEN], entry.data[CONF_ZONE], entry.data[CONF_RECORDS], @@ -45,14 +53,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def update_records(now): """Set up recurring update.""" try: - await _async_update_cloudflare(cfupdate, zone_id) + await _async_update_cloudflare(session, cfupdate, zone_id) except CloudflareException as error: _LOGGER.error("Error updating zone %s: %s", entry.data[CONF_ZONE], error) async def update_records_service(call: ServiceCall) -> None: """Set up service for manual trigger.""" try: - await _async_update_cloudflare(cfupdate, zone_id) + await _async_update_cloudflare(session, cfupdate, zone_id) except CloudflareException as error: _LOGGER.error("Error updating zone %s: %s", entry.data[CONF_ZONE], error) @@ -76,11 +84,20 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True -async def _async_update_cloudflare(cfupdate: CloudflareUpdater, zone_id: str): +async def _async_update_cloudflare( + session: ClientSession, + cfupdate: CloudflareUpdater, + zone_id: str, +) -> None: _LOGGER.debug("Starting update for zone %s", cfupdate.zone) records = await cfupdate.get_record_info(zone_id) _LOGGER.debug("Records: %s", records) - await cfupdate.update_records(zone_id, records) + location_info = await async_detect_location_info(session) + + if not location_info or not is_ipv4_address(location_info.ip): + raise HomeAssistantError("Could not get external IPv4 address") + + await cfupdate.update_records(zone_id, records, location_info.ip) _LOGGER.debug("Update for zone %s is complete", cfupdate.zone) diff --git a/tests/components/cloudflare/test_init.py b/tests/components/cloudflare/test_init.py index ab7dbdab78e..6e7f450d711 100644 --- a/tests/components/cloudflare/test_init.py +++ b/tests/components/cloudflare/test_init.py @@ -1,11 +1,16 @@ """Test the Cloudflare integration.""" +from unittest.mock import patch + from pycfdns.exceptions import ( CloudflareAuthenticationException, CloudflareConnectionException, ) +import pytest from homeassistant.components.cloudflare.const import DOMAIN, SERVICE_UPDATE_RECORDS from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState +from homeassistant.exceptions import HomeAssistantError +from homeassistant.util.location import LocationInfo from . import ENTRY_CONFIG, init_integration @@ -70,12 +75,51 @@ async def test_integration_services(hass, cfupdate): entry = await init_integration(hass) assert entry.state is ConfigEntryState.LOADED - await hass.services.async_call( - DOMAIN, - SERVICE_UPDATE_RECORDS, - {}, - blocking=True, - ) - await hass.async_block_till_done() + with patch( + "homeassistant.components.cloudflare.async_detect_location_info", + return_value=LocationInfo( + "0.0.0.0", + "US", + "USD", + "CA", + "California", + "San Diego", + "92122", + "America/Los_Angeles", + 32.8594, + -117.2073, + True, + ), + ): + + await hass.services.async_call( + DOMAIN, + SERVICE_UPDATE_RECORDS, + {}, + blocking=True, + ) + await hass.async_block_till_done() instance.update_records.assert_called_once() + + +async def test_integration_services_with_issue(hass, cfupdate): + """Test integration services with issue.""" + instance = cfupdate.return_value + + entry = await init_integration(hass) + assert entry.state is ConfigEntryState.LOADED + + with patch( + "homeassistant.components.cloudflare.async_detect_location_info", + return_value=None, + ), pytest.raises(HomeAssistantError, match="Could not get external IPv4 address"): + await hass.services.async_call( + DOMAIN, + SERVICE_UPDATE_RECORDS, + {}, + blocking=True, + ) + await hass.async_block_till_done() + + instance.update_records.assert_not_called() From bc146a09dba6df6eaec429039f6e1efcfbd1123f Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Mon, 7 Nov 2022 13:39:29 -0500 Subject: [PATCH 0269/1033] Add integration_type for some integrations (#81499) --- homeassistant/components/deluge/manifest.json | 3 ++- homeassistant/components/discord/manifest.json | 3 ++- homeassistant/components/efergy/manifest.json | 3 ++- .../components/goalzero/manifest.json | 3 ++- .../components/google_sheets/manifest.json | 3 ++- homeassistant/components/lidarr/manifest.json | 3 ++- .../components/litterrobot/manifest.json | 3 ++- .../components/modem_callerid/manifest.json | 3 ++- .../components/nfandroidtv/manifest.json | 3 ++- homeassistant/components/radarr/manifest.json | 3 ++- homeassistant/components/skybell/manifest.json | 3 ++- .../components/steam_online/manifest.json | 3 ++- .../components/tautulli/manifest.json | 3 ++- homeassistant/generated/integrations.json | 18 +++++++++--------- 14 files changed, 35 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/deluge/manifest.json b/homeassistant/components/deluge/manifest.json index 920e560b70f..89302d4cd48 100644 --- a/homeassistant/components/deluge/manifest.json +++ b/homeassistant/components/deluge/manifest.json @@ -6,5 +6,6 @@ "codeowners": ["@tkdrob"], "config_flow": true, "iot_class": "local_polling", - "loggers": ["deluge_client"] + "loggers": ["deluge_client"], + "integration_type": "service" } diff --git a/homeassistant/components/discord/manifest.json b/homeassistant/components/discord/manifest.json index b631c5fa7e7..022cb5fd933 100644 --- a/homeassistant/components/discord/manifest.json +++ b/homeassistant/components/discord/manifest.json @@ -6,5 +6,6 @@ "requirements": ["nextcord==2.0.0a8"], "codeowners": ["@tkdrob"], "iot_class": "cloud_push", - "loggers": ["discord"] + "loggers": ["discord"], + "integration_type": "service" } diff --git a/homeassistant/components/efergy/manifest.json b/homeassistant/components/efergy/manifest.json index fc90591cae6..b0cff7e203f 100644 --- a/homeassistant/components/efergy/manifest.json +++ b/homeassistant/components/efergy/manifest.json @@ -6,5 +6,6 @@ "requirements": ["pyefergy==22.1.1"], "codeowners": ["@tkdrob"], "iot_class": "cloud_polling", - "loggers": ["iso4217", "pyefergy"] + "loggers": ["iso4217", "pyefergy"], + "integration_type": "hub" } diff --git a/homeassistant/components/goalzero/manifest.json b/homeassistant/components/goalzero/manifest.json index bb26567b8cc..67e8bca2acc 100644 --- a/homeassistant/components/goalzero/manifest.json +++ b/homeassistant/components/goalzero/manifest.json @@ -8,5 +8,6 @@ "codeowners": ["@tkdrob"], "quality_scale": "silver", "iot_class": "local_polling", - "loggers": ["goalzero"] + "loggers": ["goalzero"], + "integration_type": "device" } diff --git a/homeassistant/components/google_sheets/manifest.json b/homeassistant/components/google_sheets/manifest.json index c8d86210b42..1c7790b1f25 100644 --- a/homeassistant/components/google_sheets/manifest.json +++ b/homeassistant/components/google_sheets/manifest.json @@ -6,5 +6,6 @@ "documentation": "https://www.home-assistant.io/integrations/google_sheets/", "requirements": ["gspread==5.5.0"], "codeowners": ["@tkdrob"], - "iot_class": "cloud_polling" + "iot_class": "cloud_polling", + "integration_type": "service" } diff --git a/homeassistant/components/lidarr/manifest.json b/homeassistant/components/lidarr/manifest.json index eab24ef7e42..d9333470b00 100644 --- a/homeassistant/components/lidarr/manifest.json +++ b/homeassistant/components/lidarr/manifest.json @@ -6,5 +6,6 @@ "codeowners": ["@tkdrob"], "config_flow": true, "iot_class": "local_polling", - "loggers": ["aiopyarr"] + "loggers": ["aiopyarr"], + "integration_type": "service" } diff --git a/homeassistant/components/litterrobot/manifest.json b/homeassistant/components/litterrobot/manifest.json index 6384df2f25a..889c7edfd9c 100644 --- a/homeassistant/components/litterrobot/manifest.json +++ b/homeassistant/components/litterrobot/manifest.json @@ -7,5 +7,6 @@ "codeowners": ["@natekspencer", "@tkdrob"], "dhcp": [{ "hostname": "litter-robot4" }], "iot_class": "cloud_push", - "loggers": ["pylitterbot"] + "loggers": ["pylitterbot"], + "integration_type": "hub" } diff --git a/homeassistant/components/modem_callerid/manifest.json b/homeassistant/components/modem_callerid/manifest.json index 024759791f4..58078a4ddb0 100644 --- a/homeassistant/components/modem_callerid/manifest.json +++ b/homeassistant/components/modem_callerid/manifest.json @@ -8,5 +8,6 @@ "dependencies": ["usb"], "iot_class": "local_polling", "usb": [{ "vid": "0572", "pid": "1340" }], - "loggers": ["phone_modem"] + "loggers": ["phone_modem"], + "integration_type": "device" } diff --git a/homeassistant/components/nfandroidtv/manifest.json b/homeassistant/components/nfandroidtv/manifest.json index df285bea228..fc05c2c12a1 100644 --- a/homeassistant/components/nfandroidtv/manifest.json +++ b/homeassistant/components/nfandroidtv/manifest.json @@ -6,5 +6,6 @@ "codeowners": ["@tkdrob"], "config_flow": true, "iot_class": "local_push", - "loggers": ["notifications_android_tv"] + "loggers": ["notifications_android_tv"], + "integration_type": "service" } diff --git a/homeassistant/components/radarr/manifest.json b/homeassistant/components/radarr/manifest.json index 5117fd161d3..1d1ce5b0289 100644 --- a/homeassistant/components/radarr/manifest.json +++ b/homeassistant/components/radarr/manifest.json @@ -6,5 +6,6 @@ "codeowners": ["@tkdrob"], "config_flow": true, "iot_class": "local_polling", - "loggers": ["aiopyarr"] + "loggers": ["aiopyarr"], + "integration_type": "service" } diff --git a/homeassistant/components/skybell/manifest.json b/homeassistant/components/skybell/manifest.json index bfef4bc3422..059ba76febc 100644 --- a/homeassistant/components/skybell/manifest.json +++ b/homeassistant/components/skybell/manifest.json @@ -7,5 +7,6 @@ "dependencies": ["ffmpeg"], "codeowners": ["@tkdrob"], "iot_class": "cloud_polling", - "loggers": ["aioskybell"] + "loggers": ["aioskybell"], + "integration_type": "hub" } diff --git a/homeassistant/components/steam_online/manifest.json b/homeassistant/components/steam_online/manifest.json index f8aba1aee07..4fb91943725 100644 --- a/homeassistant/components/steam_online/manifest.json +++ b/homeassistant/components/steam_online/manifest.json @@ -6,5 +6,6 @@ "requirements": ["steamodd==4.21"], "codeowners": ["@tkdrob"], "iot_class": "cloud_polling", - "loggers": ["steam"] + "loggers": ["steam"], + "integration_type": "service" } diff --git a/homeassistant/components/tautulli/manifest.json b/homeassistant/components/tautulli/manifest.json index bbdaa4c8ebb..a77639e3a58 100644 --- a/homeassistant/components/tautulli/manifest.json +++ b/homeassistant/components/tautulli/manifest.json @@ -6,5 +6,6 @@ "config_flow": true, "codeowners": ["@ludeeus", "@tkdrob"], "iot_class": "local_polling", - "loggers": ["pytautulli"] + "loggers": ["pytautulli"], + "integration_type": "hub" } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 69e7d22ba82..21e19326dc6 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -947,7 +947,7 @@ }, "deluge": { "name": "Deluge", - "integration_type": "hub", + "integration_type": "service", "config_flow": true, "iot_class": "local_polling" }, @@ -1046,7 +1046,7 @@ }, "discord": { "name": "Discord", - "integration_type": "hub", + "integration_type": "service", "config_flow": true, "iot_class": "cloud_push" }, @@ -1906,7 +1906,7 @@ }, "goalzero": { "name": "Goal Zero Yeti", - "integration_type": "hub", + "integration_type": "device", "config_flow": true, "iot_class": "local_polling" }, @@ -1951,7 +1951,7 @@ "name": "Google Pub/Sub" }, "google_sheets": { - "integration_type": "hub", + "integration_type": "service", "config_flow": true, "iot_class": "cloud_polling", "name": "Google Sheets" @@ -2761,7 +2761,7 @@ }, "lidarr": { "name": "Lidarr", - "integration_type": "hub", + "integration_type": "service", "config_flow": true, "iot_class": "local_polling" }, @@ -3233,7 +3233,7 @@ }, "modem_callerid": { "name": "Phone Modem", - "integration_type": "hub", + "integration_type": "device", "config_flow": true, "iot_class": "local_polling" }, @@ -3480,7 +3480,7 @@ }, "nfandroidtv": { "name": "Notifications for Android TV / Fire TV", - "integration_type": "hub", + "integration_type": "service", "config_flow": true, "iot_class": "local_push" }, @@ -4211,7 +4211,7 @@ }, "radarr": { "name": "Radarr", - "integration_type": "hub", + "integration_type": "service", "config_flow": true, "iot_class": "local_polling" }, @@ -5038,7 +5038,7 @@ }, "steam_online": { "name": "Steam", - "integration_type": "hub", + "integration_type": "service", "config_flow": true, "iot_class": "cloud_polling" }, From 7f23ab9d86178a9fe00cfedd14cfee088270ce97 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Mon, 7 Nov 2022 15:46:41 -0500 Subject: [PATCH 0270/1033] Add measurement state class to eight_sleep sensors (#81589) * Add measurement state class to eight_sleep sensors * tweaks --- homeassistant/components/eight_sleep/sensor.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/eight_sleep/sensor.py b/homeassistant/components/eight_sleep/sensor.py index 7cce1293707..58648123dcf 100644 --- a/homeassistant/components/eight_sleep/sensor.py +++ b/homeassistant/components/eight_sleep/sensor.py @@ -7,7 +7,11 @@ from typing import Any from pyeight.eight import EightSleep import voluptuous as vol -from homeassistant.components.sensor import SensorDeviceClass, SensorEntity +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorStateClass, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant @@ -179,6 +183,9 @@ class EightUserSensor(EightSleepBaseEntity, SensorEntity): elif self._sensor in ("current_sleep", "last_sleep", "current_sleep_fitness"): self._attr_native_unit_of_measurement = "Score" + if self._sensor != "sleep_stage": + self._attr_state_class = SensorStateClass.MEASUREMENT + _LOGGER.debug( "User Sensor: %s, Side: %s, User: %s", self._sensor, @@ -272,6 +279,7 @@ class EightRoomSensor(EightSleepBaseEntity, SensorEntity): _attr_icon = "mdi:thermometer" _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS def __init__( From 5c38321c4fd33d1c61cc5bf695ef389950cd77df Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 7 Nov 2022 15:50:45 -0600 Subject: [PATCH 0271/1033] Ignore unspecified addresses from zeroconf (#81620) --- homeassistant/components/zeroconf/__init__.py | 12 ++++++++++-- tests/components/zeroconf/test_init.py | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zeroconf/__init__.py b/homeassistant/components/zeroconf/__init__.py index 62783e641d3..82a9604a08c 100644 --- a/homeassistant/components/zeroconf/__init__.py +++ b/homeassistant/components/zeroconf/__init__.py @@ -552,12 +552,20 @@ def _first_non_link_local_address( """Return the first ipv6 or non-link local ipv4 address, preferring IPv4.""" for address in addresses: ip_addr = ip_address(address) - if not ip_addr.is_link_local and ip_addr.version == 4: + if ( + not ip_addr.is_link_local + and not ip_addr.is_unspecified + and ip_addr.version == 4 + ): return str(ip_addr) # If we didn't find a good IPv4 address, check for IPv6 addresses. for address in addresses: ip_addr = ip_address(address) - if not ip_addr.is_link_local and ip_addr.version == 6: + if ( + not ip_addr.is_link_local + and not ip_addr.is_unspecified + and ip_addr.version == 6 + ): return str(ip_addr) return None diff --git a/tests/components/zeroconf/test_init.py b/tests/components/zeroconf/test_init.py index 039672b9955..a0684d6e10e 100644 --- a/tests/components/zeroconf/test_init.py +++ b/tests/components/zeroconf/test_init.py @@ -819,6 +819,24 @@ async def test_info_from_service_with_link_local_address_first(hass): assert info.host == "192.168.66.12" +async def test_info_from_service_with_unspecified_address_first(hass): + """Test that the unspecified address is ignored.""" + service_type = "_test._tcp.local." + service_info = get_service_info_mock(service_type, f"test.{service_type}") + service_info.addresses = ["0.0.0.0", "192.168.66.12"] + info = zeroconf.info_from_service(service_info) + assert info.host == "192.168.66.12" + + +async def test_info_from_service_with_unspecified_address_only(hass): + """Test that the unspecified address is ignored.""" + service_type = "_test._tcp.local." + service_info = get_service_info_mock(service_type, f"test.{service_type}") + service_info.addresses = ["0.0.0.0"] + info = zeroconf.info_from_service(service_info) + assert info is None + + async def test_info_from_service_with_link_local_address_second(hass): """Test that the link local address is ignored.""" service_type = "_test._tcp.local." From 55cad465b209aee179ad1edfb3ee7de0ffc84342 Mon Sep 17 00:00:00 2001 From: Thibaut Date: Tue, 8 Nov 2022 00:07:02 +0100 Subject: [PATCH 0272/1033] Add support for AEH with adjustable temperature in Overkiz integration (#72790) * Import and clean from ha-tahoma * Fix wrong search and replace * Clean code * Use elif * Sort import * Fix imports * Update homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater_with_adjustable_temperature_setpoint.py Co-authored-by: Mick Vleeshouwer * Fix import * Remove cast * Remove useless constructor * Use constant * Remove now useless conditions Co-authored-by: Mick Vleeshouwer --- .../overkiz/climate_entities/__init__.py | 4 + ...er_with_adjustable_temperature_setpoint.py | 134 ++++++++++++++++++ homeassistant/components/overkiz/const.py | 1 + 3 files changed, 139 insertions(+) create mode 100644 homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater_with_adjustable_temperature_setpoint.py diff --git a/homeassistant/components/overkiz/climate_entities/__init__.py b/homeassistant/components/overkiz/climate_entities/__init__.py index 359f7629a7b..5d676e49747 100644 --- a/homeassistant/components/overkiz/climate_entities/__init__.py +++ b/homeassistant/components/overkiz/climate_entities/__init__.py @@ -2,6 +2,9 @@ from pyoverkiz.enums.ui import UIWidget from .atlantic_electrical_heater import AtlanticElectricalHeater +from .atlantic_electrical_heater_with_adjustable_temperature_setpoint import ( + AtlanticElectricalHeaterWithAdjustableTemperatureSetpoint, +) from .atlantic_electrical_towel_dryer import AtlanticElectricalTowelDryer from .atlantic_heat_recovery_ventilation import AtlanticHeatRecoveryVentilation from .atlantic_pass_apc_heating_and_cooling_zone import ( @@ -12,6 +15,7 @@ from .somfy_thermostat import SomfyThermostat WIDGET_TO_CLIMATE_ENTITY = { UIWidget.ATLANTIC_ELECTRICAL_HEATER: AtlanticElectricalHeater, + UIWidget.ATLANTIC_ELECTRICAL_HEATER_WITH_ADJUSTABLE_TEMPERATURE_SETPOINT: AtlanticElectricalHeaterWithAdjustableTemperatureSetpoint, UIWidget.ATLANTIC_ELECTRICAL_TOWEL_DRYER: AtlanticElectricalTowelDryer, UIWidget.ATLANTIC_HEAT_RECOVERY_VENTILATION: AtlanticHeatRecoveryVentilation, UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: AtlanticPassAPCHeatingAndCoolingZone, diff --git a/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater_with_adjustable_temperature_setpoint.py b/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater_with_adjustable_temperature_setpoint.py new file mode 100644 index 00000000000..80c2f418331 --- /dev/null +++ b/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater_with_adjustable_temperature_setpoint.py @@ -0,0 +1,134 @@ +"""Support for Atlantic Electrical Heater (With Adjustable Temperature Setpoint).""" +from __future__ import annotations + +from typing import Any + +from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState + +from homeassistant.components.climate import ( + PRESET_BOOST, + PRESET_COMFORT, + PRESET_ECO, + PRESET_NONE, + ClimateEntity, + ClimateEntityFeature, + HVACMode, +) +from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS + +from ..coordinator import OverkizDataUpdateCoordinator +from ..entity import OverkizEntity + +PRESET_AUTO = "auto" +PRESET_COMFORT1 = "comfort-1" +PRESET_COMFORT2 = "comfort-2" +PRESET_FROST_PROTECTION = "frost_protection" +PRESET_PROG = "prog" + + +# Map Overkiz presets to Home Assistant presets +OVERKIZ_TO_PRESET_MODE: dict[str, str] = { + OverkizCommandParam.OFF: PRESET_NONE, + OverkizCommandParam.FROSTPROTECTION: PRESET_FROST_PROTECTION, + OverkizCommandParam.ECO: PRESET_ECO, + OverkizCommandParam.COMFORT: PRESET_COMFORT, + OverkizCommandParam.COMFORT_1: PRESET_COMFORT1, + OverkizCommandParam.COMFORT_2: PRESET_COMFORT2, + OverkizCommandParam.AUTO: PRESET_AUTO, + OverkizCommandParam.BOOST: PRESET_BOOST, + OverkizCommandParam.INTERNAL: PRESET_PROG, +} + +PRESET_MODE_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_PRESET_MODE.items()} + +# Map Overkiz HVAC modes to Home Assistant HVAC modes +OVERKIZ_TO_HVAC_MODE: dict[str, str] = { + OverkizCommandParam.ON: HVACMode.HEAT, + OverkizCommandParam.OFF: HVACMode.OFF, + OverkizCommandParam.AUTO: HVACMode.AUTO, + OverkizCommandParam.BASIC: HVACMode.HEAT, + OverkizCommandParam.STANDBY: HVACMode.OFF, + OverkizCommandParam.INTERNAL: HVACMode.AUTO, +} + +HVAC_MODE_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_HVAC_MODE.items()} + +TEMPERATURE_SENSOR_DEVICE_INDEX = 2 + + +class AtlanticElectricalHeaterWithAdjustableTemperatureSetpoint( + OverkizEntity, ClimateEntity +): + """Representation of Atlantic Electrical Heater (With Adjustable Temperature Setpoint).""" + + _attr_hvac_modes = [*HVAC_MODE_TO_OVERKIZ] + _attr_preset_modes = [*PRESET_MODE_TO_OVERKIZ] + _attr_temperature_unit = TEMP_CELSIUS + _attr_supported_features = ( + ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE + ) + + def __init__( + self, device_url: str, coordinator: OverkizDataUpdateCoordinator + ) -> None: + """Init method.""" + super().__init__(device_url, coordinator) + self.temperature_device = self.executor.linked_device( + TEMPERATURE_SENSOR_DEVICE_INDEX + ) + + @property + def hvac_mode(self) -> str: + """Return hvac operation ie. heat, cool mode.""" + states = self.device.states + if (state := states[OverkizState.CORE_OPERATING_MODE]) and state.value_as_str: + return OVERKIZ_TO_HVAC_MODE[state.value_as_str] + if (state := states[OverkizState.CORE_ON_OFF]) and state.value_as_str: + return OVERKIZ_TO_HVAC_MODE[state.value_as_str] + return HVACMode.OFF + + async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: + """Set new target hvac mode.""" + await self.executor.async_execute_command( + OverkizCommand.SET_OPERATING_MODE, HVAC_MODE_TO_OVERKIZ[hvac_mode] + ) + + @property + def preset_mode(self) -> str | None: + """Return the current preset mode, e.g., home, away, temp.""" + if ( + state := self.device.states[OverkizState.IO_TARGET_HEATING_LEVEL] + ) and state.value_as_str: + return OVERKIZ_TO_PRESET_MODE[state.value_as_str] + return None + + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Set new preset mode.""" + if preset_mode in [PRESET_AUTO, PRESET_PROG]: + command = OverkizCommand.SET_OPERATING_MODE + else: + command = OverkizCommand.SET_HEATING_LEVEL + await self.executor.async_execute_command( + command, PRESET_MODE_TO_OVERKIZ[preset_mode] + ) + + @property + def target_temperature(self) -> float | None: + """Return the temperature.""" + if state := self.device.states[OverkizState.CORE_TARGET_TEMPERATURE]: + return state.value_as_float + return None + + @property + def current_temperature(self) -> float | None: + """Return the current temperature.""" + if temperature := self.temperature_device.states[OverkizState.CORE_TEMPERATURE]: + return temperature.value_as_float + return None + + async def async_set_temperature(self, **kwargs: Any) -> None: + """Set new temperature.""" + temperature = kwargs[ATTR_TEMPERATURE] + await self.executor.async_execute_command( + OverkizCommand.SET_TARGET_TEMPERATURE, temperature + ) diff --git a/homeassistant/components/overkiz/const.py b/homeassistant/components/overkiz/const.py index 4645b058182..f207fc305e0 100644 --- a/homeassistant/components/overkiz/const.py +++ b/homeassistant/components/overkiz/const.py @@ -63,6 +63,7 @@ OVERKIZ_DEVICE_TO_PLATFORM: dict[UIClass | UIWidget, Platform | None] = { UIClass.WINDOW: Platform.COVER, UIWidget.ALARM_PANEL_CONTROLLER: Platform.ALARM_CONTROL_PANEL, # widgetName, uiClass is Alarm (not supported) UIWidget.ATLANTIC_ELECTRICAL_HEATER: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) + UIWidget.ATLANTIC_ELECTRICAL_HEATER_WITH_ADJUSTABLE_TEMPERATURE_SETPOINT: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.ATLANTIC_ELECTRICAL_TOWEL_DRYER: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.ATLANTIC_HEAT_RECOVERY_VENTILATION: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.ATLANTIC_PASS_APC_DHW: Platform.WATER_HEATER, # widgetName, uiClass is WaterHeatingSystem (not supported) From 785cf0e29caa7aea1e5b9cbc0e02e1cd9515cac1 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 8 Nov 2022 00:26:39 +0000 Subject: [PATCH 0273/1033] [ci skip] Translation update --- .../components/airq/translations/hu.json | 22 +++++++++++++++++++ .../components/airq/translations/ru.json | 2 +- .../components/androidtv/translations/ru.json | 2 +- .../components/asuswrt/translations/ru.json | 2 +- .../components/braviatv/translations/de.json | 3 +++ .../components/braviatv/translations/es.json | 3 +++ .../components/braviatv/translations/et.json | 3 +++ .../components/braviatv/translations/id.json | 3 +++ .../components/braviatv/translations/no.json | 3 +++ .../braviatv/translations/pt-BR.json | 3 +++ .../components/braviatv/translations/ru.json | 5 ++++- .../braviatv/translations/zh-Hant.json | 3 +++ .../components/broadlink/translations/ru.json | 4 ++-- .../components/brother/translations/ru.json | 2 +- .../components/dnsip/translations/ru.json | 4 ++-- .../components/dunehd/translations/ru.json | 2 +- .../components/ezviz/translations/ru.json | 2 +- .../forecast_solar/translations/hu.json | 2 +- .../components/fronius/translations/ru.json | 4 ++-- .../components/goalzero/translations/ru.json | 4 ++-- .../components/livisi/translations/de.json | 18 +++++++++++++++ .../components/livisi/translations/es.json | 18 +++++++++++++++ .../components/livisi/translations/hu.json | 18 +++++++++++++++ .../components/livisi/translations/pt-BR.json | 18 +++++++++++++++ .../components/livisi/translations/ru.json | 18 +++++++++++++++ .../components/mqtt/translations/de.json | 10 ++++----- .../components/mqtt/translations/en.json | 4 ++-- .../components/mqtt/translations/es.json | 6 ++--- .../components/mqtt/translations/hu.json | 12 +++++----- .../nibe_heatpump/translations/ru.json | 2 +- .../p1_monitor/translations/ru.json | 2 +- .../pure_energie/translations/ru.json | 2 +- .../rainmachine/translations/ru.json | 2 +- .../components/senseme/translations/ru.json | 2 +- .../components/tolo/translations/ru.json | 2 +- .../unifiprotect/translations/de.json | 6 +++++ .../unifiprotect/translations/en.json | 4 ++++ .../unifiprotect/translations/es.json | 6 +++++ .../unifiprotect/translations/et.json | 4 ++-- .../unifiprotect/translations/hu.json | 6 +++++ .../unifiprotect/translations/id.json | 6 +++++ .../unifiprotect/translations/no.json | 6 +++++ .../unifiprotect/translations/pt-BR.json | 6 +++++ .../unifiprotect/translations/zh-Hant.json | 6 +++++ .../components/vallox/translations/ru.json | 4 ++-- .../xiaomi_aqara/translations/ru.json | 2 +- 46 files changed, 225 insertions(+), 43 deletions(-) create mode 100644 homeassistant/components/airq/translations/hu.json create mode 100644 homeassistant/components/livisi/translations/de.json create mode 100644 homeassistant/components/livisi/translations/es.json create mode 100644 homeassistant/components/livisi/translations/hu.json create mode 100644 homeassistant/components/livisi/translations/pt-BR.json create mode 100644 homeassistant/components/livisi/translations/ru.json diff --git a/homeassistant/components/airq/translations/hu.json b/homeassistant/components/airq/translations/hu.json new file mode 100644 index 00000000000..adfcb73e289 --- /dev/null +++ b/homeassistant/components/airq/translations/hu.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "invalid_input": "\u00c9rv\u00e9nytelen hosztn\u00e9v vagy IP-c\u00edm" + }, + "step": { + "user": { + "data": { + "ip_address": "IP c\u00edm", + "password": "Jelsz\u00f3" + }, + "description": "Adja meg az eszk\u00f6z IP-c\u00edm\u00e9t vagy mDNS c\u00edm\u00e9t \u00e9s jelszav\u00e1t.", + "title": "Az eszk\u00f6z azonos\u00edt\u00e1sa" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/ru.json b/homeassistant/components/airq/translations/ru.json index 416787acc7f..b2456f01a33 100644 --- a/homeassistant/components/airq/translations/ru.json +++ b/homeassistant/components/airq/translations/ru.json @@ -6,7 +6,7 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", - "invalid_input": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." + "invalid_input": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." }, "step": { "user": { diff --git a/homeassistant/components/androidtv/translations/ru.json b/homeassistant/components/androidtv/translations/ru.json index 61eb431fbf4..4bb20ad0374 100644 --- a/homeassistant/components/androidtv/translations/ru.json +++ b/homeassistant/components/androidtv/translations/ru.json @@ -7,7 +7,7 @@ "error": { "adbkey_not_file": "\u0424\u0430\u0439\u043b \u043a\u043b\u044e\u0447\u0430 ADB \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", "key_and_server": "\u041d\u0443\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u044e\u0447 ADB \u0438\u043b\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 ADB.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, diff --git a/homeassistant/components/asuswrt/translations/ru.json b/homeassistant/components/asuswrt/translations/ru.json index 0253cd20d1b..770cff7eb39 100644 --- a/homeassistant/components/asuswrt/translations/ru.json +++ b/homeassistant/components/asuswrt/translations/ru.json @@ -6,7 +6,7 @@ }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", "pwd_and_ssh": "\u041d\u0443\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0430\u0440\u043e\u043b\u044c \u0438\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0444\u0430\u0439\u043b \u043a\u043b\u044e\u0447\u0430 SSH.", "pwd_or_ssh": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u044c \u0438\u043b\u0438 \u0444\u0430\u0439\u043b \u043a\u043b\u044e\u0447\u0430 SSH.", "ssh_not_file": "\u0424\u0430\u0439\u043b \u043a\u043b\u044e\u0447\u0430 SSH \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d.", diff --git a/homeassistant/components/braviatv/translations/de.json b/homeassistant/components/braviatv/translations/de.json index f62d496f2d3..492e647f302 100644 --- a/homeassistant/components/braviatv/translations/de.json +++ b/homeassistant/components/braviatv/translations/de.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "Beim Aktualisieren der Quellenliste ist ein Fehler aufgetreten. \n\nStelle sicher, dass dein Fernseher eingeschaltet ist, bevor du versuchst, ihn einzurichten." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/es.json b/homeassistant/components/braviatv/translations/es.json index fbcd69a0bec..c467c5dafca 100644 --- a/homeassistant/components/braviatv/translations/es.json +++ b/homeassistant/components/braviatv/translations/es.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "Ocurri\u00f3 un error al actualizar la lista de fuentes. \n\nAseg\u00farate de que tu TV est\u00e9 encendida antes de intentar configurarla." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/et.json b/homeassistant/components/braviatv/translations/et.json index 4e3ca6333d4..c650b6abd9f 100644 --- a/homeassistant/components/braviatv/translations/et.json +++ b/homeassistant/components/braviatv/translations/et.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "Allikate loendi v\u00e4rskendamisel ilmnes viga. \n\n Enne teleri seadistamist veendu, et see oleks sisse l\u00fclitatud." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/id.json b/homeassistant/components/braviatv/translations/id.json index 61b5b17ff1e..6c357e6e2bb 100644 --- a/homeassistant/components/braviatv/translations/id.json +++ b/homeassistant/components/braviatv/translations/id.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "Terjadi kesalahan saat memperbarui daftar sumber.\n\nPastikan TV Anda sudah dihidupkan sebelum menyiapkan." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/no.json b/homeassistant/components/braviatv/translations/no.json index dec2157d6a3..a568e364c12 100644 --- a/homeassistant/components/braviatv/translations/no.json +++ b/homeassistant/components/braviatv/translations/no.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "Det oppsto en feil under oppdatering av kildelisten. \n\n S\u00f8rg for at TV-en er sl\u00e5tt p\u00e5 f\u00f8r du pr\u00f8ver \u00e5 sette den opp." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/pt-BR.json b/homeassistant/components/braviatv/translations/pt-BR.json index 7c5af6e2694..e048568e351 100644 --- a/homeassistant/components/braviatv/translations/pt-BR.json +++ b/homeassistant/components/braviatv/translations/pt-BR.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "Ocorreu um erro ao atualizar a lista de fontes. \n\n Certifique-se de que sua TV esteja ligada antes de tentar configur\u00e1-la." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/ru.json b/homeassistant/components/braviatv/translations/ru.json index 8416d9e5ede..299ad538bc6 100644 --- a/homeassistant/components/braviatv/translations/ru.json +++ b/homeassistant/components/braviatv/translations/ru.json @@ -10,7 +10,7 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", "unsupported_model": "\u042d\u0442\u0430 \u043c\u043e\u0434\u0435\u043b\u044c \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f." }, "step": { @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0441\u043f\u0438\u0441\u043a\u0430 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432. \n\n\u041f\u0435\u0440\u0435\u0434 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0412\u0430\u0448 \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440 \u0432\u043a\u043b\u044e\u0447\u0435\u043d." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/zh-Hant.json b/homeassistant/components/braviatv/translations/zh-Hant.json index e30142c947b..c66ba705db1 100644 --- a/homeassistant/components/braviatv/translations/zh-Hant.json +++ b/homeassistant/components/braviatv/translations/zh-Hant.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "\u66f4\u65b0\u4f86\u6e90\u5217\u8868\u6642\u767c\u751f\u932f\u8aa4\u3002\n\n\u8acb\u78ba\u5b9a\u96fb\u8996\u5df2\u7d93\u65bc\u8a2d\u5b9a\u524d\u958b\u555f\u3002" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/broadlink/translations/ru.json b/homeassistant/components/broadlink/translations/ru.json index 65ee1f4db1d..85fd5db28e4 100644 --- a/homeassistant/components/broadlink/translations/ru.json +++ b/homeassistant/components/broadlink/translations/ru.json @@ -4,13 +4,13 @@ "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", "not_supported": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "flow_title": "{name} ({model}, {host})", diff --git a/homeassistant/components/brother/translations/ru.json b/homeassistant/components/brother/translations/ru.json index a9f6158ccf8..1469e9962ed 100644 --- a/homeassistant/components/brother/translations/ru.json +++ b/homeassistant/components/brother/translations/ru.json @@ -7,7 +7,7 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "snmp_error": "\u0421\u0435\u0440\u0432\u0435\u0440 SNMP \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d \u0438\u043b\u0438 \u043f\u0440\u0438\u043d\u0442\u0435\u0440 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f.", - "wrong_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." + "wrong_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." }, "flow_title": "{model} {serial_number}", "step": { diff --git a/homeassistant/components/dnsip/translations/ru.json b/homeassistant/components/dnsip/translations/ru.json index a4421b566f6..0882153776a 100644 --- a/homeassistant/components/dnsip/translations/ru.json +++ b/homeassistant/components/dnsip/translations/ru.json @@ -1,12 +1,12 @@ { "config": { "error": { - "invalid_hostname": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f." + "invalid_hostname": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430." }, "step": { "user": { "data": { - "hostname": "\u0414\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f DNS-\u0437\u0430\u043f\u0440\u043e\u0441", + "hostname": "\u0418\u043c\u044f \u0445\u043e\u0441\u0442\u0430, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f DNS-\u0437\u0430\u043f\u0440\u043e\u0441", "resolver": "\u0420\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u0442\u0435\u043b\u044c \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 IPV4", "resolver_ipv6": "\u0420\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u0442\u0435\u043b\u044c \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 IPV6" } diff --git a/homeassistant/components/dunehd/translations/ru.json b/homeassistant/components/dunehd/translations/ru.json index 8c32af72af7..f0d5e989dd2 100644 --- a/homeassistant/components/dunehd/translations/ru.json +++ b/homeassistant/components/dunehd/translations/ru.json @@ -6,7 +6,7 @@ "error": { "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." }, "step": { "user": { diff --git a/homeassistant/components/ezviz/translations/ru.json b/homeassistant/components/ezviz/translations/ru.json index 13bdf601817..77c3332d0bc 100644 --- a/homeassistant/components/ezviz/translations/ru.json +++ b/homeassistant/components/ezviz/translations/ru.json @@ -8,7 +8,7 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." }, "flow_title": "{serial}", "step": { diff --git a/homeassistant/components/forecast_solar/translations/hu.json b/homeassistant/components/forecast_solar/translations/hu.json index 3aac09afe1b..7ff078d1301 100644 --- a/homeassistant/components/forecast_solar/translations/hu.json +++ b/homeassistant/components/forecast_solar/translations/hu.json @@ -25,7 +25,7 @@ "inverter_size": "Inverter m\u00e9rete (Watt)", "modules power": "A napelemmodulok teljes cs\u00facsteljes\u00edtm\u00e9nye (Watt)" }, - "description": "Ezek az \u00e9rt\u00e9kek lehet\u0151v\u00e9 teszik a Solar.Forecast eredm\u00e9ny m\u00f3dos\u00edt\u00e1s\u00e1t. K\u00e9rem, olvassa el a dokument\u00e1ci\u00f3t, ha egy mez\u0151 kit\u00f6lt\u00e9se nem egy\u00e9rtelm\u0171." + "description": "Ezek az \u00e9rt\u00e9kek lehet\u0151v\u00e9 teszik a Forecast.Solar eredm\u00e9ny\u00e9nek finomhangol\u00e1s\u00e1t. Ha egy mez\u0151 nem egy\u00e9rtelm\u0171, k\u00e9rj\u00fck, olvassa el a dokument\u00e1ci\u00f3t." } } } diff --git a/homeassistant/components/fronius/translations/ru.json b/homeassistant/components/fronius/translations/ru.json index 473834c8797..12d0e012083 100644 --- a/homeassistant/components/fronius/translations/ru.json +++ b/homeassistant/components/fronius/translations/ru.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", @@ -17,7 +17,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442" }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0412\u0430\u0448\u0435\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Fronius.", + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0412\u0430\u0448\u0435\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Fronius.", "title": "Fronius SolarNet" } } diff --git a/homeassistant/components/goalzero/translations/ru.json b/homeassistant/components/goalzero/translations/ru.json index 7bd8c3df311..ed028936aa3 100644 --- a/homeassistant/components/goalzero/translations/ru.json +++ b/homeassistant/components/goalzero/translations/ru.json @@ -2,12 +2,12 @@ "config": { "abort": { "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { diff --git a/homeassistant/components/livisi/translations/de.json b/homeassistant/components/livisi/translations/de.json new file mode 100644 index 00000000000..d85e61a13db --- /dev/null +++ b/homeassistant/components/livisi/translations/de.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "wrong_ip_address": "Die IP-Adresse ist falsch oder der SHC ist lokal nicht erreichbar.", + "wrong_password": "Das Passwort ist falsch." + }, + "step": { + "user": { + "data": { + "host": "IP-Adresse", + "password": "Passwort" + }, + "description": "Gib die IP-Adresse und das (lokale) Passwort des SHC ein." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/es.json b/homeassistant/components/livisi/translations/es.json new file mode 100644 index 00000000000..87a51cc79fa --- /dev/null +++ b/homeassistant/components/livisi/translations/es.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "No se pudo conectar", + "wrong_ip_address": "La direcci\u00f3n IP es incorrecta o no se puede acceder localmente al SHC.", + "wrong_password": "La contrase\u00f1a es incorrecta." + }, + "step": { + "user": { + "data": { + "host": "Direcci\u00f3n IP", + "password": "Contrase\u00f1a" + }, + "description": "Introduce la direcci\u00f3n IP y la contrase\u00f1a (local) del SHC." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/hu.json b/homeassistant/components/livisi/translations/hu.json new file mode 100644 index 00000000000..d203bc3c7c4 --- /dev/null +++ b/homeassistant/components/livisi/translations/hu.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "wrong_ip_address": "Az IP-c\u00edm helytelen, vagy az SHC nem \u00e9rhet\u0151 el a helyi h\u00e1l\u00f3zatban.", + "wrong_password": "A jelsz\u00f3 helytelen." + }, + "step": { + "user": { + "data": { + "host": "IP c\u00edm", + "password": "Jelsz\u00f3" + }, + "description": "Adja meg az SHC helyi IP-c\u00edm\u00e9t \u00e9s jelszav\u00e1t." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/pt-BR.json b/homeassistant/components/livisi/translations/pt-BR.json new file mode 100644 index 00000000000..ff56d2c5aca --- /dev/null +++ b/homeassistant/components/livisi/translations/pt-BR.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Falhou ao conectar", + "wrong_ip_address": "O endere\u00e7o IP est\u00e1 incorreto ou o SHC n\u00e3o pode ser alcan\u00e7ado localmente.", + "wrong_password": "A senha est\u00e1 incorreta." + }, + "step": { + "user": { + "data": { + "host": "Endere\u00e7o IP", + "password": "Senha" + }, + "description": "Digite o endere\u00e7o IP e a senha (local) do SHC." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/ru.json b/homeassistant/components/livisi/translations/ru.json new file mode 100644 index 00000000000..9fcbe468154 --- /dev/null +++ b/homeassistant/components/livisi/translations/ru.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "wrong_ip_address": "IP-\u0430\u0434\u0440\u0435\u0441 \u043d\u0435\u0432\u0435\u0440\u0435\u043d \u0438\u043b\u0438 SHC \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e.", + "wrong_password": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c." + }, + "step": { + "user": { + "data": { + "host": "IP-\u0430\u0434\u0440\u0435\u0441", + "password": "\u041f\u0430\u0440\u043e\u043b\u044c" + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441 \u0438 (\u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439) \u043f\u0430\u0440\u043e\u043b\u044c SHC." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mqtt/translations/de.json b/homeassistant/components/mqtt/translations/de.json index b193f18b39e..425425a24a1 100644 --- a/homeassistant/components/mqtt/translations/de.json +++ b/homeassistant/components/mqtt/translations/de.json @@ -20,10 +20,10 @@ "data": { "advanced_options": "Erweiterte Optionen", "broker": "Server", - "certificate": "Pfad zur benutzerdefinierten CA-Zertifikatsdatei", - "client_cert": "Pfad zur Client-Zertifikatsdatei", + "certificate": "Hochladen einer benutzerdefinierten CA-Zertifikatsdatei", + "client_cert": "Client-Zertifikatsdatei hochladen", "client_id": "Client-ID (leer lassen, um eine zuf\u00e4llig generierte zu erhalten)", - "client_key": "Pfad zur privaten Schl\u00fcsseldatei", + "client_key": "Private Schl\u00fcsseldatei hochladen", "discovery": "Suche aktivieren", "keepalive": "Die Zeit zwischen dem Senden von Keep-Alive-Nachrichten", "password": "Passwort", @@ -93,7 +93,7 @@ "broker": { "data": { "advanced_options": "Erweiterte Optionen", - "broker": "Broker", + "broker": "Server", "certificate": "Hochladen einer benutzerdefinierten CA-Zertifikatsdatei", "client_cert": "Client-Zertifikatsdatei hochladen", "client_id": "Client-ID (leer lassen, um eine zuf\u00e4llig generierte zu erhalten)", @@ -117,7 +117,7 @@ "birth_qos": "Birth Nachricht QoS", "birth_retain": "Birth Nachricht zwischenspeichern", "birth_topic": "Thema der Birth Nachricht", - "discovery": "Erkennung aktivieren", + "discovery": "Suche aktivieren", "discovery_prefix": "Discovery-Pr\u00e4fix", "will_enable": "Letzten Willen aktivieren", "will_payload": "Nutzdaten der Letzter-Wille Nachricht", diff --git a/homeassistant/components/mqtt/translations/en.json b/homeassistant/components/mqtt/translations/en.json index 599fbf5be07..74ecf3ccb11 100644 --- a/homeassistant/components/mqtt/translations/en.json +++ b/homeassistant/components/mqtt/translations/en.json @@ -13,7 +13,7 @@ "bad_discovery_prefix": "Invalid discovery prefix", "bad_will": "Invalid will topic", "cannot_connect": "Failed to connect", - "invalid_inclusion": "The client certificate and private key must be configured together" + "invalid_inclusion": "The client certificate and private key must be configurered together" }, "step": { "broker": { @@ -87,7 +87,7 @@ "bad_discovery_prefix": "Invalid discovery prefix", "bad_will": "Invalid will topic", "cannot_connect": "Failed to connect", - "invalid_inclusion": "The client certificate and private key must be configured together" + "invalid_inclusion": "The client certificate and private key must be configurered together" }, "step": { "broker": { diff --git a/homeassistant/components/mqtt/translations/es.json b/homeassistant/components/mqtt/translations/es.json index 8c05afe997a..8ad74693dde 100644 --- a/homeassistant/components/mqtt/translations/es.json +++ b/homeassistant/components/mqtt/translations/es.json @@ -20,10 +20,10 @@ "data": { "advanced_options": "Opciones avanzadas", "broker": "Br\u00f3ker", - "certificate": "Ruta al archivo de certificado de la CA personalizado", - "client_cert": "Ruta a un archivo de certificado de cliente", + "certificate": "Subir archivo de certificado de la CA personalizado", + "client_cert": "Subir archivo de certificado de cliente", "client_id": "ID de cliente (dejar vac\u00edo para generar uno aleatoriamente)", - "client_key": "Ruta a un archivo de clave privada", + "client_key": "Subir archivo de clave privada", "discovery": "Habilitar descubrimiento", "keepalive": "El tiempo entre el env\u00edo de mensajes keep alive", "password": "Contrase\u00f1a", diff --git a/homeassistant/components/mqtt/translations/hu.json b/homeassistant/components/mqtt/translations/hu.json index c643f0ef404..cbc557d4021 100644 --- a/homeassistant/components/mqtt/translations/hu.json +++ b/homeassistant/components/mqtt/translations/hu.json @@ -20,10 +20,10 @@ "data": { "advanced_options": "Speci\u00e1lis be\u00e1ll\u00edt\u00e1sok", "broker": "Br\u00f3ker", - "certificate": "Az egy\u00e9ni CA-tan\u00fas\u00edtv\u00e1nyf\u00e1jl el\u00e9r\u00e9si \u00fatja", - "client_cert": "Az \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny f\u00e1jl el\u00e9r\u00e9si \u00fatja", + "certificate": "Egy\u00e9ni CA-tan\u00fas\u00edtv\u00e1nyf\u00e1jl felt\u00f6lt\u00e9se", + "client_cert": "\u00dcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny f\u00e1jl felt\u00f6lt\u00e9se", "client_id": "\u00dcgyf\u00e9l azonos\u00edt\u00f3 (hagyja \u00fcresen a v\u00e9letlenszer\u0171en gener\u00e1lt azonos\u00edt\u00f3hoz)", - "client_key": "A priv\u00e1t kulcsf\u00e1jl el\u00e9r\u00e9si \u00fatvonala", + "client_key": "Priv\u00e1t kulcsf\u00e1jl felt\u00f6lt\u00e9se", "discovery": "Felfedez\u00e9s enged\u00e9lyez\u00e9se", "keepalive": "A keep alive \u00fczenetek k\u00fcld\u00e9se k\u00f6z\u00f6tti id\u0151", "password": "Jelsz\u00f3", @@ -79,15 +79,15 @@ }, "options": { "error": { - "bad_birth": "\u00c9rv\u00e9nytelen 'birth' topik.", + "bad_birth": "\u00c9rv\u00e9nytelen 'birth' topik", "bad_certificate": "A CA-tan\u00fas\u00edtv\u00e1ny \u00e9rv\u00e9nytelen", "bad_client_cert": "\u00c9rv\u00e9nytelen \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny. Gy\u0151z\u0151dj\u00f6n meg r\u00f3la, hogy PEM k\u00f3dolt f\u00e1jl van megadva", "bad_client_cert_key": "Az \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny \u00e9s a priv\u00e1t tan\u00fas\u00edtv\u00e1ny nem \u00e9rv\u00e9nyes p\u00e1r", "bad_client_key": "\u00c9rv\u00e9nytelen priv\u00e1t kulcs, gy\u0151z\u0151dj\u00f6n meg r\u00f3la, hogy PEM k\u00f3dol\u00e1s\u00fa f\u00e1jlt k\u00fcld\u00f6tt jelsz\u00f3 n\u00e9lk\u00fcl", "bad_discovery_prefix": "\u00c9rv\u00e9nytelen felfedez\u00e9si el\u0151tag", - "bad_will": "\u00c9rv\u00e9nytelen 'will' topik.", + "bad_will": "\u00c9rv\u00e9nytelen 'will' topik", "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "invalid_inclusion": "Az \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1nyt \u00e9s a priv\u00e1t kulcsot egy\u00fctt kell konfigur\u00e1lni" + "invalid_inclusion": "Az \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1nyt \u00e9s a mag\u00e1nkulcsot egy\u00fctt kell konfigur\u00e1lni" }, "step": { "broker": { diff --git a/homeassistant/components/nibe_heatpump/translations/ru.json b/homeassistant/components/nibe_heatpump/translations/ru.json index f280ae2ba57..59f228b3746 100644 --- a/homeassistant/components/nibe_heatpump/translations/ru.json +++ b/homeassistant/components/nibe_heatpump/translations/ru.json @@ -4,7 +4,7 @@ "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." }, "error": { - "address": "\u0423\u043a\u0430\u0437\u0430\u043d \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441. \u0421\u043b\u0435\u0434\u0443\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f.", + "address": "\u0423\u043a\u0430\u0437\u0430\u043d \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441. \u0421\u043b\u0435\u0434\u0443\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430.", "address_in_use": "\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u044d\u0442\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435.", "model": "\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 modbus40.", "read": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b `\u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0447\u0442\u0435\u043d\u0438\u044f` \u0438\u043b\u0438 `\u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441`.", diff --git a/homeassistant/components/p1_monitor/translations/ru.json b/homeassistant/components/p1_monitor/translations/ru.json index 75a179abdc3..10524a44fa5 100644 --- a/homeassistant/components/p1_monitor/translations/ru.json +++ b/homeassistant/components/p1_monitor/translations/ru.json @@ -10,7 +10,7 @@ "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, "data_description": { - "host": "IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0412\u0430\u0448\u0435\u0439 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 P1 Monitor." + "host": "IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0412\u0430\u0448\u0435\u0439 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 P1 Monitor." }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 P1 Monitor." } diff --git a/homeassistant/components/pure_energie/translations/ru.json b/homeassistant/components/pure_energie/translations/ru.json index 2aa39757104..e2f7bf36745 100644 --- a/homeassistant/components/pure_energie/translations/ru.json +++ b/homeassistant/components/pure_energie/translations/ru.json @@ -14,7 +14,7 @@ "host": "\u0425\u043e\u0441\u0442" }, "data_description": { - "host": "IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0412\u0430\u0448\u0435\u0433\u043e Pure Energie Meter." + "host": "IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0412\u0430\u0448\u0435\u0433\u043e Pure Energie Meter." } }, "zeroconf_confirm": { diff --git a/homeassistant/components/rainmachine/translations/ru.json b/homeassistant/components/rainmachine/translations/ru.json index 03fbe1fb074..c2c904fd0a8 100644 --- a/homeassistant/components/rainmachine/translations/ru.json +++ b/homeassistant/components/rainmachine/translations/ru.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "ip_address": "\u0414\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441", + "ip_address": "\u0418\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "port": "\u041f\u043e\u0440\u0442" }, diff --git a/homeassistant/components/senseme/translations/ru.json b/homeassistant/components/senseme/translations/ru.json index 8debd33481f..905f670a1f9 100644 --- a/homeassistant/components/senseme/translations/ru.json +++ b/homeassistant/components/senseme/translations/ru.json @@ -6,7 +6,7 @@ }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." }, "flow_title": "{name} - {model} ({host})", "step": { diff --git a/homeassistant/components/tolo/translations/ru.json b/homeassistant/components/tolo/translations/ru.json index 0243a40cf7e..e9ff9c6552f 100644 --- a/homeassistant/components/tolo/translations/ru.json +++ b/homeassistant/components/tolo/translations/ru.json @@ -15,7 +15,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442" }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430." + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430." } } } diff --git a/homeassistant/components/unifiprotect/translations/de.json b/homeassistant/components/unifiprotect/translations/de.json index f44ad32a2b3..e283364599d 100644 --- a/homeassistant/components/unifiprotect/translations/de.json +++ b/homeassistant/components/unifiprotect/translations/de.json @@ -41,6 +41,12 @@ } } }, + "issues": { + "ea_warning": { + "description": "Du verwendest v{version} von UniFi Protect, einer Early-Access-Version. Early-Access-Versionen werden von Home Assistant nicht unterst\u00fctzt und k\u00f6nnen dazu f\u00fchren, dass deine UniFi Protect-Integration nicht oder nicht wie erwartet funktioniert.", + "title": "UniFi Protect v{version} ist eine Early-Access-Version" + } + }, "options": { "error": { "invalid_mac_list": "Muss eine durch Kommas getrennte Liste von MAC-Adressen sein" diff --git a/homeassistant/components/unifiprotect/translations/en.json b/homeassistant/components/unifiprotect/translations/en.json index 03362629879..742a1b3a851 100644 --- a/homeassistant/components/unifiprotect/translations/en.json +++ b/homeassistant/components/unifiprotect/translations/en.json @@ -48,11 +48,15 @@ } }, "options": { + "error": { + "invalid_mac_list": "Must be a list of MAC addresses seperated by commas" + }, "step": { "init": { "data": { "all_updates": "Realtime metrics (WARNING: Greatly increases CPU usage)", "disable_rtsp": "Disable the RTSP stream", + "ignored_devices": "Comma separated list of MAC addresses of devices to ignore", "max_media": "Max number of event to load for Media Browser (increases RAM usage)", "override_connection_host": "Override Connection Host" }, diff --git a/homeassistant/components/unifiprotect/translations/es.json b/homeassistant/components/unifiprotect/translations/es.json index e278fb6ecf0..d3a3f9a2329 100644 --- a/homeassistant/components/unifiprotect/translations/es.json +++ b/homeassistant/components/unifiprotect/translations/es.json @@ -41,6 +41,12 @@ } } }, + "issues": { + "ea_warning": { + "description": "Est\u00e1s utilizando v{version} de UniFi Protect, que es una versi\u00f3n Early Access. Las versiones Early Access no son compatibles con Home Assistant y pueden causar que tu integraci\u00f3n con UniFi Protect no funcione por completo o como se esperaba.", + "title": "UniFi Protect v{version} es una versi\u00f3n Early Access" + } + }, "options": { "error": { "invalid_mac_list": "Debe ser una lista de direcciones MAC separadas por comas" diff --git a/homeassistant/components/unifiprotect/translations/et.json b/homeassistant/components/unifiprotect/translations/et.json index 9ddf72f925d..54225737c77 100644 --- a/homeassistant/components/unifiprotect/translations/et.json +++ b/homeassistant/components/unifiprotect/translations/et.json @@ -43,8 +43,8 @@ }, "issues": { "ea_warning": { - "description": "Kasutad UniFi Protecti {version}. Home Assistant ei toeta varajase juurdep\u00e4\u00e4su versioone ja see v\u00f5ib p\u00f5hjustada UniFi Protecti sidumise katkemist v\u00f5i ei t\u00f6\u00f6ta see ootusp\u00e4raselt.", - "title": "{version} on UniFi Protecti varajase juurdep\u00e4\u00e4su versioon" + "description": "Kasutad UniFi Protecti v{version}. Home Assistant ei toeta varajase juurdep\u00e4\u00e4su versioone ja see v\u00f5ib p\u00f5hjustada UniFi Protecti sidumise katkemist v\u00f5i ei t\u00f6\u00f6ta see ootusp\u00e4raselt.", + "title": "v{version} on UniFi Protecti varajase juurdep\u00e4\u00e4su versioon" } }, "options": { diff --git a/homeassistant/components/unifiprotect/translations/hu.json b/homeassistant/components/unifiprotect/translations/hu.json index dac543f97c2..477374a074f 100644 --- a/homeassistant/components/unifiprotect/translations/hu.json +++ b/homeassistant/components/unifiprotect/translations/hu.json @@ -41,6 +41,12 @@ } } }, + "issues": { + "ea_warning": { + "description": "\u00d6n az UniFi Protect {version} verzi\u00f3j\u00e1t haszn\u00e1lja. A korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3kat Home Assistant nem t\u00e1mogatja, \u00e9s az UniFi Protect integr\u00e1ci\u00f3ja le\u00e1llhat, vagy nem az elv\u00e1rt m\u00f3don m\u0171k\u00f6dhet.", + "title": "{version} UniFi Protect korai hozz\u00e1f\u00e9r\u00e9si verzi\u00f3" + } + }, "options": { "error": { "invalid_mac_list": "Egy list\u00e1ra van sz\u00fcks\u00e9g, melyben a MAC-c\u00edmek vessz\u0151vel vannak elv\u00e1lasztva" diff --git a/homeassistant/components/unifiprotect/translations/id.json b/homeassistant/components/unifiprotect/translations/id.json index 772c339f3df..cae7dfd3253 100644 --- a/homeassistant/components/unifiprotect/translations/id.json +++ b/homeassistant/components/unifiprotect/translations/id.json @@ -41,6 +41,12 @@ } } }, + "issues": { + "ea_warning": { + "description": "Anda menggunakan UniFi Protect v{version} yang merupakan versi Early Access. Versi Early Access tidak didukung oleh Home Assistant dan dapat menyebabkan integrasi UniFi Protect rusak atau tidak berfungsi seperti yang diharapkan.", + "title": "UniFi Protect v{version} adalah versi Early Access" + } + }, "options": { "error": { "invalid_mac_list": "Harus berupa daftar alamat MAC yang dipisahkan dengan koma" diff --git a/homeassistant/components/unifiprotect/translations/no.json b/homeassistant/components/unifiprotect/translations/no.json index 7eac0b2dca1..1ac28d4221a 100644 --- a/homeassistant/components/unifiprotect/translations/no.json +++ b/homeassistant/components/unifiprotect/translations/no.json @@ -41,6 +41,12 @@ } } }, + "issues": { + "ea_warning": { + "description": "Du bruker v {version} av UniFi Protect som er en tidlig tilgangsversjon. Early Access-versjoner st\u00f8ttes ikke av Home Assistant og kan f\u00f8re til at UniFi Protect-integrasjonen din g\u00e5r i stykker eller ikke fungerer som forventet.", + "title": "UniFi Protect v {version} er en versjon med tidlig tilgang" + } + }, "options": { "error": { "invalid_mac_list": "M\u00e5 v\u00e6re en liste over MAC-adresser atskilt med komma" diff --git a/homeassistant/components/unifiprotect/translations/pt-BR.json b/homeassistant/components/unifiprotect/translations/pt-BR.json index 3bc780b57b7..441ef2e7ab6 100644 --- a/homeassistant/components/unifiprotect/translations/pt-BR.json +++ b/homeassistant/components/unifiprotect/translations/pt-BR.json @@ -41,6 +41,12 @@ } } }, + "issues": { + "ea_warning": { + "description": "Voc\u00ea est\u00e1 usando v {version} do UniFi Protect, que \u00e9 uma vers\u00e3o de acesso antecipado. As vers\u00f5es de acesso antecipado n\u00e3o s\u00e3o suportadas pelo Home Assistant e podem fazer com que a integra\u00e7\u00e3o do UniFi Protect seja interrompida ou n\u00e3o funcione conforme o esperado.", + "title": "UniFi Protect v {version} \u00e9 uma vers\u00e3o de acesso antecipado" + } + }, "options": { "error": { "invalid_mac_list": "Deve ser uma lista de endere\u00e7os MAC separados por v\u00edrgulas" diff --git a/homeassistant/components/unifiprotect/translations/zh-Hant.json b/homeassistant/components/unifiprotect/translations/zh-Hant.json index 0688a40d0c8..ce1498044f3 100644 --- a/homeassistant/components/unifiprotect/translations/zh-Hant.json +++ b/homeassistant/components/unifiprotect/translations/zh-Hant.json @@ -41,6 +41,12 @@ } } }, + "issues": { + "ea_warning": { + "description": "\u6b63\u5728\u4f7f\u7528\u7684 UniFi Protect {version} \u7248\u3001\u70ba Home Assistant \u4e0d\u652f\u63f4\u7684\u6436\u5148\u9ad4\u9a57\u7248\u672c\uff0c\u53ef\u80fd\u6703\u5c0e\u81f4 UniFi Protect \u6574\u5408\u51fa\u73fe\u554f\u984c\u3001\u6216\u7121\u6cd5\u6b63\u5e38\u5de5\u4f5c\u3002", + "title": "UniFi Protect {version} \u7248\u70ba\u6436\u5148\u9ad4\u9a57\u7248" + } + }, "options": { "error": { "invalid_mac_list": "\u5fc5\u9808\u70ba\u4ee5\u9017\u865f\uff08\uff1a\uff09\u5206\u9694\u958b\u7684 MAC \u5730\u5740\u5217\u8868" diff --git a/homeassistant/components/vallox/translations/ru.json b/homeassistant/components/vallox/translations/ru.json index a1165b287d1..990357a015e 100644 --- a/homeassistant/components/vallox/translations/ru.json +++ b/homeassistant/components/vallox/translations/ru.json @@ -3,12 +3,12 @@ "abort": { "already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { diff --git a/homeassistant/components/xiaomi_aqara/translations/ru.json b/homeassistant/components/xiaomi_aqara/translations/ru.json index 3483bf2d3f8..6bef9c3d4b5 100644 --- a/homeassistant/components/xiaomi_aqara/translations/ru.json +++ b/homeassistant/components/xiaomi_aqara/translations/ru.json @@ -7,7 +7,7 @@ }, "error": { "discovery_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u0448\u043b\u044e\u0437 Xiaomi Aqara, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0441 HomeAssistant \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430.", - "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.. \u0421\u043f\u043e\u0441\u043e\u0431\u044b \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0437\u0434\u0435\u0441\u044c: https://www.home-assistant.io/integrations/xiaomi_aqara/#connection-problem.", + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441.. \u0421\u043f\u043e\u0441\u043e\u0431\u044b \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0437\u0434\u0435\u0441\u044c: https://www.home-assistant.io/integrations/xiaomi_aqara/#connection-problem.", "invalid_interface": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.", "invalid_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u0448\u043b\u044e\u0437\u0430.", "invalid_mac": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 MAC-\u0430\u0434\u0440\u0435\u0441." From c2c26e2608b3f676424dd08f730ce4c49c8b319e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 7 Nov 2022 21:19:57 -0600 Subject: [PATCH 0274/1033] Fix check for duplicate config entry reauth when context is passed or augmented (#81753) fixes https://github.com/home-assistant/core/issues/77578 --- homeassistant/config_entries.py | 29 +++++++++++++++-------------- tests/test_config_entries.py | 15 ++++++++++++++- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 8e618deb3d2..b7ee9a4d654 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -660,24 +660,25 @@ class ConfigEntry: data: dict[str, Any] | None = None, ) -> None: """Start a reauth flow.""" - flow_context = { - "source": SOURCE_REAUTH, - "entry_id": self.entry_id, - "title_placeholders": {"name": self.title}, - "unique_id": self.unique_id, - } - - if context: - flow_context.update(context) - - for flow in hass.config_entries.flow.async_progress_by_handler(self.domain): - if flow["context"] == flow_context: - return + if any( + flow + for flow in hass.config_entries.flow.async_progress() + if flow["context"].get("source") == SOURCE_REAUTH + and flow["context"].get("entry_id") == self.entry_id + ): + # Reauth flow already in progress for this entry + return hass.async_create_task( hass.config_entries.flow.async_init( self.domain, - context=flow_context, + context={ + "source": SOURCE_REAUTH, + "entry_id": self.entry_id, + "title_placeholders": {"name": self.title}, + "unique_id": self.unique_id, + } + | (context or {}), data=self.data | (data or {}), ) ) diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 83343146d47..99e26be6d75 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -3287,6 +3287,7 @@ async def test_disallow_entry_reload_with_setup_in_progresss(hass, manager): async def test_reauth(hass): """Test the async_reauth_helper.""" entry = MockConfigEntry(title="test_title", domain="test") + entry2 = MockConfigEntry(title="test_title", domain="test") mock_setup_entry = AsyncMock(return_value=True) mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry)) @@ -3313,7 +3314,19 @@ async def test_reauth(hass): assert mock_init.call_args.kwargs["data"]["extra_data"] == 1234 + assert entry.entry_id != entry2.entry_id + # Check we can't start duplicate flows entry.async_start_reauth(hass, {"extra_context": "some_extra_context"}) await hass.async_block_till_done() - assert len(flows) == 1 + assert len(hass.config_entries.flow.async_progress()) == 1 + + # Check we can't start duplicate when the context context is different + entry.async_start_reauth(hass, {"diff": "diff"}) + await hass.async_block_till_done() + assert len(hass.config_entries.flow.async_progress()) == 1 + + # Check we can start a reauth for a different entry + entry2.async_start_reauth(hass, {"extra_context": "some_extra_context"}) + await hass.async_block_till_done() + assert len(hass.config_entries.flow.async_progress()) == 2 From 0ce301ae7ba97b73be6b22598c0e6431151d0e0f Mon Sep 17 00:00:00 2001 From: Jon Gilmore <7232986+JonGilmore@users.noreply.github.com> Date: Mon, 7 Nov 2022 21:22:03 -0600 Subject: [PATCH 0275/1033] Remove JonGilmore from lutron codeowners (#81727) --- CODEOWNERS | 1 - homeassistant/components/lutron/manifest.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index d33cf545038..db01573e808 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -655,7 +655,6 @@ build.json @home-assistant/supervisor /homeassistant/components/luftdaten/ @fabaff @frenck /tests/components/luftdaten/ @fabaff @frenck /homeassistant/components/lupusec/ @majuss -/homeassistant/components/lutron/ @JonGilmore /homeassistant/components/lutron_caseta/ @swails @bdraco @danaues /tests/components/lutron_caseta/ @swails @bdraco @danaues /homeassistant/components/lyric/ @timmo001 diff --git a/homeassistant/components/lutron/manifest.json b/homeassistant/components/lutron/manifest.json index 7c3e66c7127..d46a47cf38d 100644 --- a/homeassistant/components/lutron/manifest.json +++ b/homeassistant/components/lutron/manifest.json @@ -3,7 +3,7 @@ "name": "Lutron", "documentation": "https://www.home-assistant.io/integrations/lutron", "requirements": ["pylutron==0.2.8"], - "codeowners": ["@JonGilmore"], + "codeowners": [], "iot_class": "local_polling", "loggers": ["pylutron"] } From c3d4a9cd995e0d2dc7b7ce7f5f505aea65125b72 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 8 Nov 2022 07:21:09 +0100 Subject: [PATCH 0276/1033] Create repairs issue if an outdated currency code is configured (#81717) * Create repairs issue if an outdated currency code is configured * Add script for updating list of currencies * Use black for formatting * Move currency codes to a separate file * Address review comments --- .pre-commit-config.yaml | 2 +- .../components/homeassistant/strings.json | 6 + homeassistant/config.py | 35 ++- homeassistant/generated/currencies.py | 290 ++++++++++++++++++ homeassistant/helpers/config_validation.py | 168 +--------- script/currencies.py | 55 ++++ tests/helpers/test_config_validation.py | 12 + tests/test_config.py | 18 +- 8 files changed, 418 insertions(+), 168 deletions(-) create mode 100644 homeassistant/generated/currencies.py create mode 100644 script/currencies.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1a2e4d15482..1676b60a802 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: hooks: - id: codespell args: - - --ignore-words-list=hass,alot,datas,dof,dur,ether,farenheit,hist,iff,iif,ines,ist,lightsensor,mut,nd,pres,referer,rime,ser,serie,te,technik,ue,uint,visability,wan,wanna,withing,iam,incomfort,ba,haa,pullrequests + - --ignore-words-list=hass,alot,bre,datas,dof,dur,ether,farenheit,hist,iff,iif,ines,ist,lightsensor,mut,nd,pres,referer,rime,ser,serie,sur,te,technik,ue,uint,visability,wan,wanna,withing,iam,incomfort,ba,haa,pullrequests - --skip="./.*,*.csv,*.json" - --quiet-level=2 exclude_types: [csv, json] diff --git a/homeassistant/components/homeassistant/strings.json b/homeassistant/components/homeassistant/strings.json index 317e9d1dfcd..2db00081eaa 100644 --- a/homeassistant/components/homeassistant/strings.json +++ b/homeassistant/components/homeassistant/strings.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "title": "The configured currency is no longer in use", + "description": "The currency {currency} is no longer in use, please reconfigure the currency configuration." + } + }, "system_health": { "info": { "arch": "CPU Architecture", diff --git a/homeassistant/config.py b/homeassistant/config.py index e56dff4e491..8e06c2c47a2 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -3,6 +3,7 @@ from __future__ import annotations from collections import OrderedDict from collections.abc import Callable, Sequence +from contextlib import suppress import logging import os from pathlib import Path @@ -47,12 +48,19 @@ from .const import ( LEGACY_CONF_WHITELIST_EXTERNAL_DIRS, __version__, ) -from .core import DOMAIN as CONF_CORE, ConfigSource, HomeAssistant, callback +from .core import ( + DOMAIN as CONF_CORE, + ConfigSource, + HomeAssistant, + async_get_hass, + callback, +) from .exceptions import HomeAssistantError from .helpers import ( config_per_platform, config_validation as cv, extract_domain_configs, + issue_registry as ir, ) from .helpers.entity_values import EntityValues from .helpers.typing import ConfigType @@ -199,6 +207,27 @@ CUSTOMIZE_CONFIG_SCHEMA = vol.Schema( } ) + +def _validate_currency(data: Any) -> Any: + hass = async_get_hass() + try: + return cv.currency(data) + except vol.InInvalid: + with suppress(vol.InInvalid): + currency = cv.historic_currency(data) + ir.async_create_issue( + hass, + "homeassistant", + "historic_currency", + is_fixable=False, + severity=ir.IssueSeverity.WARNING, + translation_key="historic_currency", + translation_placeholders={"currency": currency}, + ) + return currency + raise + + CORE_CONFIG_SCHEMA = vol.All( CUSTOMIZE_CONFIG_SCHEMA.extend( { @@ -250,10 +279,10 @@ CORE_CONFIG_SCHEMA = vol.All( ], _no_duplicate_auth_mfa_module, ), - # pylint: disable=no-value-for-parameter + # pylint: disable-next=no-value-for-parameter vol.Optional(CONF_MEDIA_DIRS): cv.schema_with_slug_keys(vol.IsDir()), vol.Optional(CONF_LEGACY_TEMPLATES): cv.boolean, - vol.Optional(CONF_CURRENCY): cv.currency, + vol.Optional(CONF_CURRENCY): _validate_currency, } ), _filter_bad_internal_external_urls, diff --git a/homeassistant/generated/currencies.py b/homeassistant/generated/currencies.py new file mode 100644 index 00000000000..7a5a6a31bb5 --- /dev/null +++ b/homeassistant/generated/currencies.py @@ -0,0 +1,290 @@ +"""Automatically generated by currencies.py. + +To update, run python3 -m script.currencies +""" + +ACTIVE_CURRENCIES = { + "AED", + "AFN", + "ALL", + "AMD", + "ANG", + "AOA", + "ARS", + "AUD", + "AWG", + "AZN", + "BAM", + "BBD", + "BDT", + "BGN", + "BHD", + "BIF", + "BMD", + "BND", + "BOB", + "BRL", + "BSD", + "BTN", + "BWP", + "BYN", + "BZD", + "CAD", + "CDF", + "CHF", + "CLP", + "CNY", + "COP", + "CRC", + "CUC", + "CUP", + "CVE", + "CZK", + "DJF", + "DKK", + "DOP", + "DZD", + "EGP", + "ERN", + "ETB", + "EUR", + "FJD", + "FKP", + "GBP", + "GEL", + "GHS", + "GIP", + "GMD", + "GNF", + "GTQ", + "GYD", + "HKD", + "HNL", + "HRK", + "HTG", + "HUF", + "IDR", + "ILS", + "INR", + "IQD", + "IRR", + "ISK", + "JMD", + "JOD", + "JPY", + "KES", + "KGS", + "KHR", + "KMF", + "KPW", + "KRW", + "KWD", + "KYD", + "KZT", + "LAK", + "LBP", + "LKR", + "LRD", + "LSL", + "LYD", + "MAD", + "MDL", + "MGA", + "MKD", + "MMK", + "MNT", + "MOP", + "MRU", + "MUR", + "MVR", + "MWK", + "MXN", + "MYR", + "MZN", + "NAD", + "NGN", + "NIO", + "NOK", + "NPR", + "NZD", + "OMR", + "PAB", + "PEN", + "PGK", + "PHP", + "PKR", + "PLN", + "PYG", + "QAR", + "RON", + "RSD", + "RUB", + "RWF", + "SAR", + "SBD", + "SCR", + "SDG", + "SEK", + "SGD", + "SHP", + "SLE", + "SLL", + "SOS", + "SRD", + "SSP", + "STN", + "SVC", + "SYP", + "SZL", + "THB", + "TJS", + "TMT", + "TND", + "TOP", + "TRY", + "TTD", + "TWD", + "TZS", + "UAH", + "UGX", + "USD", + "UYU", + "UZS", + "VED", + "VES", + "VND", + "VUV", + "WST", + "XAF", + "XCD", + "XOF", + "XPF", + "YER", + "ZAR", + "ZMW", + "ZWL", +} + +HISTORIC_CURRENCIES = { + "ADP", + "AFA", + "ALK", + "AOK", + "AON", + "AOR", + "ARA", + "ARP", + "ARY", + "ATS", + "AYM", + "AZM", + "BAD", + "BEC", + "BEF", + "BEL", + "BGJ", + "BGK", + "BGL", + "BOP", + "BRB", + "BRC", + "BRE", + "BRN", + "BRR", + "BUK", + "BYB", + "BYR", + "CHC", + "CSD", + "CSJ", + "CSK", + "CYP", + "DDM", + "DEM", + "ECS", + "ECV", + "EEK", + "ESA", + "ESB", + "ESP", + "FIM", + "FRF", + "GEK", + "GHC", + "GHP", + "GNE", + "GNS", + "GQE", + "GRD", + "GWE", + "GWP", + "HRD", + "IEP", + "ILP", + "ILR", + "ISJ", + "ITL", + "LAJ", + "LSM", + "LTL", + "LTT", + "LUC", + "LUF", + "LUL", + "LVL", + "LVR", + "MGF", + "MLF", + "MRO", + "MTL", + "MTP", + "MVQ", + "MXP", + "MZE", + "MZM", + "NIC", + "NLG", + "PEH", + "PEI", + "PES", + "PLZ", + "PTE", + "RHD", + "ROK", + "ROL", + "RUR", + "SDD", + "SDP", + "SIT", + "SKK", + "SRG", + "STD", + "SUR", + "TJR", + "TMM", + "TPE", + "TRL", + "UAK", + "UGS", + "UGW", + "USS", + "UYN", + "UYP", + "VEB", + "VEF", + "VNC", + "XEU", + "XFO", + "YDD", + "YUD", + "YUM", + "YUN", + "ZAL", + "ZMK", + "ZRN", + "ZRZ", + "ZWC", + "ZWD", + "ZWN", + "ZWR", +} diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 35191d77042..48f26c2b768 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -88,6 +88,7 @@ from homeassistant.const import ( ) from homeassistant.core import split_entity_id, valid_entity_id from homeassistant.exceptions import TemplateError +from homeassistant.generated import currencies from homeassistant.util import raise_if_invalid_path, slugify as util_slugify import homeassistant.util.dt as dt_util @@ -1654,167 +1655,10 @@ ACTION_TYPE_SCHEMAS: dict[str, Callable[[Any], dict]] = { } -# Validate currencies adopted by countries currency = vol.In( - { - "AED", - "AFN", - "ALL", - "AMD", - "ANG", - "AOA", - "ARS", - "AUD", - "AWG", - "AZN", - "BAM", - "BBD", - "BDT", - "BGN", - "BHD", - "BIF", - "BMD", - "BND", - "BOB", - "BRL", - "BSD", - "BTN", - "BWP", - "BYN", - "BYR", - "BZD", - "CAD", - "CDF", - "CHF", - "CLP", - "CNY", - "COP", - "CRC", - "CUP", - "CVE", - "CZK", - "DJF", - "DKK", - "DOP", - "DZD", - "EGP", - "ERN", - "ETB", - "EUR", - "FJD", - "FKP", - "GBP", - "GEL", - "GHS", - "GIP", - "GMD", - "GNF", - "GTQ", - "GYD", - "HKD", - "HNL", - "HRK", - "HTG", - "HUF", - "IDR", - "ILS", - "INR", - "IQD", - "IRR", - "ISK", - "JMD", - "JOD", - "JPY", - "KES", - "KGS", - "KHR", - "KMF", - "KPW", - "KRW", - "KWD", - "KYD", - "KZT", - "LAK", - "LBP", - "LKR", - "LRD", - "LSL", - "LTL", - "LYD", - "MAD", - "MDL", - "MGA", - "MKD", - "MMK", - "MNT", - "MOP", - "MRO", - "MUR", - "MVR", - "MWK", - "MXN", - "MYR", - "MZN", - "NAD", - "NGN", - "NIO", - "NOK", - "NPR", - "NZD", - "OMR", - "PAB", - "PEN", - "PGK", - "PHP", - "PKR", - "PLN", - "PYG", - "QAR", - "RON", - "RSD", - "RUB", - "RWF", - "SAR", - "SBD", - "SCR", - "SDG", - "SEK", - "SGD", - "SHP", - "SLL", - "SOS", - "SRD", - "SSP", - "STD", - "SYP", - "SZL", - "THB", - "TJS", - "TMT", - "TND", - "TOP", - "TRY", - "TTD", - "TWD", - "TZS", - "UAH", - "UGX", - "USD", - "UYU", - "UZS", - "VEF", - "VND", - "VUV", - "WST", - "XAF", - "XCD", - "XOF", - "XPF", - "YER", - "ZAR", - "ZMK", - "ZMW", - "ZWL", - }, - msg="invalid ISO 4217 formatted currency", + currencies.ACTIVE_CURRENCIES, msg="invalid ISO 4217 formatted currency" +) + +historic_currency = vol.In( + currencies.HISTORIC_CURRENCIES, msg="invalid ISO 4217 formatted historic currency" ) diff --git a/script/currencies.py b/script/currencies.py new file mode 100644 index 00000000000..2e538ff7c97 --- /dev/null +++ b/script/currencies.py @@ -0,0 +1,55 @@ +"""Helper script to update currency list from the official source.""" +import pathlib + +import black +from bs4 import BeautifulSoup +import requests + +BASE = """ +\"\"\"Automatically generated by currencies.py. + +To update, run python3 -m script.currencies +\"\"\" + +ACTIVE_CURRENCIES = {{ {} }} + +HISTORIC_CURRENCIES = {{ {} }} +""".strip() + +req = requests.get( + "https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/lists/list-one.xml" +) +soup = BeautifulSoup(req.content, "xml") +active_currencies = sorted( + { + x.Ccy.contents[0] + for x in soup.ISO_4217.CcyTbl.children + if x.name == "CcyNtry" + and x.Ccy + and x.CcyMnrUnts.contents[0] != "N.A." + and "IsFund" not in x.CcyNm.attrs + and x.Ccy.contents[0] != "UYW" + } +) + +req = requests.get( + "https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/lists/list-three.xml" +) +soup = BeautifulSoup(req.content, "xml") +historic_currencies = sorted( + { + x.Ccy.contents[0] + for x in soup.ISO_4217.HstrcCcyTbl.children + if x.name == "HstrcCcyNtry" + and x.Ccy + and "IsFund" not in x.CcyNm.attrs + and x.Ccy.contents[0] not in active_currencies + } +) + +pathlib.Path("homeassistant/generated/currencies.py").write_text( + black.format_str( + BASE.format(repr(active_currencies)[1:-1], repr(historic_currencies)[1:-1]), + mode=black.Mode(), + ) +) diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index a5d2223a3d2..da9fa2cc68d 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -1332,3 +1332,15 @@ def test_currency(): for value in ("EUR", "USD"): assert schema(value) + + +def test_historic_currency(): + """Test historic currency validator.""" + schema = vol.Schema(cv.historic_currency) + + for value in (None, "BTC", "EUR"): + with pytest.raises(vol.MultipleInvalid): + schema(value) + + for value in ("DEM", "NLG"): + assert schema(value) diff --git a/tests/test_config.py b/tests/test_config.py index 0a125d8f121..75ad227a641 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -28,7 +28,7 @@ from homeassistant.const import ( __version__, ) from homeassistant.core import ConfigSource, HomeAssistant, HomeAssistantError -from homeassistant.helpers import config_validation as cv +from homeassistant.helpers import config_validation as cv, issue_registry as ir import homeassistant.helpers.check_config as check_config from homeassistant.helpers.entity import Entity from homeassistant.loader import async_get_integration @@ -445,7 +445,7 @@ async def test_loading_configuration_from_storage_with_yaml_only(hass, hass_stor assert hass.config.config_source is ConfigSource.STORAGE -async def test_igration_and_updating_configuration(hass, hass_storage): +async def test_migration_and_updating_configuration(hass, hass_storage): """Test updating configuration stores the new configuration.""" core_data = { "data": { @@ -1205,3 +1205,17 @@ def test_identify_config_schema(domain, schema, expected): config_util._identify_config_schema(Mock(DOMAIN=domain, CONFIG_SCHEMA=schema)) == expected ) + + +def test_core_config_schema_historic_currency(hass): + """Test core config schema.""" + config_util.CORE_CONFIG_SCHEMA( + { + "currency": "LTT", + } + ) + + issue_registry = ir.async_get(hass) + issue = issue_registry.async_get_issue("homeassistant", "historic_currency") + assert issue + assert issue.translation_placeholders == {"currency": "LTT"} From 3444d2af1aff29a09b86b7046d71cb8ba8a1c3b3 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 8 Nov 2022 07:38:31 +0100 Subject: [PATCH 0277/1033] UniFi switch entity description (#81680) * Consolidate switch entities to one class * Move turn on/off into UnifiSwitchEntity * Add event subscription Remove storing entity for everything but legacy poe switch * Only one entity class * Improve generics naming * Rename loader to description * Improve control_fn naming * Move wrongfully placed method that should only react to dpi apps being emptied * Improve different methods * Minor renaming and sorting * Mark callbacks properly --- homeassistant/components/unifi/diagnostics.py | 1 - homeassistant/components/unifi/switch.py | 722 ++++++++---------- tests/components/unifi/test_diagnostics.py | 30 - 3 files changed, 308 insertions(+), 445 deletions(-) diff --git a/homeassistant/components/unifi/diagnostics.py b/homeassistant/components/unifi/diagnostics.py index b35fd520ab0..495613f3b81 100644 --- a/homeassistant/components/unifi/diagnostics.py +++ b/homeassistant/components/unifi/diagnostics.py @@ -95,7 +95,6 @@ async def async_get_config_entry_diagnostics( async_replace_dict_data(config_entry.as_dict(), macs_to_redact), REDACT_CONFIG ) diag["site_role"] = controller.site_role - diag["entities"] = async_replace_dict_data(controller.entities, macs_to_redact) diag["clients"] = { macs_to_redact[k]: async_redact_data( async_replace_dict_data(v.raw, macs_to_redact), REDACT_CLIENTS diff --git a/homeassistant/components/unifi/switch.py b/homeassistant/components/unifi/switch.py index 65d0041187e..e63d5548ebc 100644 --- a/homeassistant/components/unifi/switch.py +++ b/homeassistant/components/unifi/switch.py @@ -7,24 +7,33 @@ Support for controlling deep packet inspection (DPI) restriction groups. from __future__ import annotations import asyncio -from collections.abc import Callable +from collections.abc import Callable, Coroutine from dataclasses import dataclass from typing import Any, Generic, TypeVar -from aiounifi.interfaces.api_handlers import ItemEvent +import aiounifi +from aiounifi.interfaces.api_handlers import CallbackType, ItemEvent, UnsubscribeType from aiounifi.interfaces.clients import Clients from aiounifi.interfaces.dpi_restriction_groups import DPIRestrictionGroups from aiounifi.interfaces.outlets import Outlets from aiounifi.interfaces.ports import Ports -from aiounifi.models.client import ClientBlockRequest +from aiounifi.models.client import Client, ClientBlockRequest from aiounifi.models.device import ( DeviceSetOutletRelayRequest, DeviceSetPoePortModeRequest, ) from aiounifi.models.dpi_restriction_app import DPIRestrictionAppEnableRequest +from aiounifi.models.dpi_restriction_group import DPIRestrictionGroup from aiounifi.models.event import Event, EventKey +from aiounifi.models.outlet import Outlet +from aiounifi.models.port import Port -from homeassistant.components.switch import DOMAIN, SwitchDeviceClass, SwitchEntity +from homeassistant.components.switch import ( + DOMAIN, + SwitchDeviceClass, + SwitchEntity, + SwitchEntityDescription, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er @@ -37,33 +46,219 @@ from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity -from .const import ( - ATTR_MANUFACTURER, - BLOCK_SWITCH, - DOMAIN as UNIFI_DOMAIN, - DPI_SWITCH, - OUTLET_SWITCH, - POE_SWITCH, -) +from .const import ATTR_MANUFACTURER, DOMAIN as UNIFI_DOMAIN, POE_SWITCH from .controller import UniFiController from .unifi_client import UniFiClient CLIENT_BLOCKED = (EventKey.WIRED_CLIENT_BLOCKED, EventKey.WIRELESS_CLIENT_BLOCKED) CLIENT_UNBLOCKED = (EventKey.WIRED_CLIENT_UNBLOCKED, EventKey.WIRELESS_CLIENT_UNBLOCKED) -T = TypeVar("T") +Data = TypeVar("Data") +Handler = TypeVar("Handler") + +Subscription = Callable[[CallbackType, ItemEvent], UnsubscribeType] + + +@callback +def async_dpi_group_is_on_fn( + api: aiounifi.Controller, dpi_group: DPIRestrictionGroup +) -> bool: + """Calculate if all apps are enabled.""" + return all( + api.dpi_apps[app_id].enabled + for app_id in dpi_group.dpiapp_ids or [] + if app_id in api.dpi_apps + ) + + +@callback +def async_sub_device_available_fn(controller: UniFiController, obj_id: str) -> bool: + """Check if sub device object is disabled.""" + device_id = obj_id.partition("_")[0] + device = controller.api.devices[device_id] + return controller.available and not device.disabled + + +@callback +def async_client_device_info_fn(api: aiounifi.Controller, obj_id: str) -> DeviceInfo: + """Create device registry entry for client.""" + client = api.clients[obj_id] + return DeviceInfo( + connections={(CONNECTION_NETWORK_MAC, obj_id)}, + default_manufacturer=client.oui, + default_name=client.name or client.hostname, + ) + + +@callback +def async_device_device_info_fn(api: aiounifi.Controller, obj_id: str) -> DeviceInfo: + """Create device registry entry for device.""" + if "_" in obj_id: # Sub device + obj_id = obj_id.partition("_")[0] + + device = api.devices[obj_id] + return DeviceInfo( + connections={(CONNECTION_NETWORK_MAC, device.mac)}, + manufacturer=ATTR_MANUFACTURER, + model=device.model, + name=device.name or None, + sw_version=device.version, + hw_version=str(device.board_revision), + ) + + +@callback +def async_dpi_group_device_info_fn(api: aiounifi.Controller, obj_id: str) -> DeviceInfo: + """Create device registry entry for DPI group.""" + return DeviceInfo( + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, f"unifi_controller_{obj_id}")}, + manufacturer=ATTR_MANUFACTURER, + model="UniFi Network", + name="UniFi Network", + ) + + +async def async_block_client_control_fn( + api: aiounifi.Controller, obj_id: str, target: bool +) -> None: + """Control network access of client.""" + await api.request(ClientBlockRequest.create(obj_id, not target)) + + +async def async_dpi_group_control_fn( + api: aiounifi.Controller, obj_id: str, target: bool +) -> None: + """Enable or disable DPI group.""" + dpi_group = api.dpi_groups[obj_id] + await asyncio.gather( + *[ + api.request(DPIRestrictionAppEnableRequest.create(app_id, target)) + for app_id in dpi_group.dpiapp_ids or [] + ] + ) + + +async def async_outlet_control_fn( + api: aiounifi.Controller, obj_id: str, target: bool +) -> None: + """Control outlet relay.""" + mac, _, index = obj_id.partition("_") + device = api.devices[mac] + await api.request(DeviceSetOutletRelayRequest.create(device, int(index), target)) + + +async def async_poe_port_control_fn( + api: aiounifi.Controller, obj_id: str, target: bool +) -> None: + """Control poe state.""" + mac, _, index = obj_id.partition("_") + device = api.devices[mac] + state = "auto" if target else "off" + await api.request(DeviceSetPoePortModeRequest.create(device, int(index), state)) @dataclass -class UnifiEntityLoader(Generic[T]): +class UnifiEntityLoader(Generic[Handler, Data]): """Validate and load entities from different UniFi handlers.""" allowed_fn: Callable[[UniFiController, str], bool] - entity_cls: type[UnifiBlockClientSwitch] | type[UnifiDPIRestrictionSwitch] | type[ - UnifiOutletSwitch - ] | type[UnifiPoePortSwitch] | type[UnifiDPIRestrictionSwitch] - handler_fn: Callable[[UniFiController], T] - supported_fn: Callable[[T, str], bool | None] + api_handler_fn: Callable[[aiounifi.Controller], Handler] + available_fn: Callable[[UniFiController, str], bool] + control_fn: Callable[[aiounifi.Controller, str, bool], Coroutine[Any, Any, None]] + device_info_fn: Callable[[aiounifi.Controller, str], DeviceInfo] + event_is_on: tuple[EventKey, ...] | None + event_to_subscribe: tuple[EventKey, ...] | None + is_on_fn: Callable[[aiounifi.Controller, Data], bool] + name_fn: Callable[[Data], str | None] + object_fn: Callable[[aiounifi.Controller, str], Data] + supported_fn: Callable[[aiounifi.Controller, str], bool | None] + unique_id_fn: Callable[[str], str] + + +@dataclass +class UnifiEntityDescription(SwitchEntityDescription, UnifiEntityLoader[Handler, Data]): + """Class describing UniFi switch entity.""" + + custom_subscribe: Callable[[aiounifi.Controller], Subscription] | None = None + + +ENTITY_DESCRIPTIONS: tuple[UnifiEntityDescription, ...] = ( + UnifiEntityDescription[Clients, Client]( + key="Block client", + device_class=SwitchDeviceClass.SWITCH, + entity_category=EntityCategory.CONFIG, + has_entity_name=True, + icon="mdi:ethernet", + allowed_fn=lambda controller, obj_id: obj_id in controller.option_block_clients, + api_handler_fn=lambda api: api.clients, + available_fn=lambda controller, obj_id: controller.available, + control_fn=async_block_client_control_fn, + device_info_fn=async_client_device_info_fn, + event_is_on=CLIENT_UNBLOCKED, + event_to_subscribe=CLIENT_BLOCKED + CLIENT_UNBLOCKED, + is_on_fn=lambda api, client: not client.blocked, + name_fn=lambda client: None, + object_fn=lambda api, obj_id: api.clients[obj_id], + supported_fn=lambda api, obj_id: True, + unique_id_fn=lambda obj_id: f"block-{obj_id}", + ), + UnifiEntityDescription[DPIRestrictionGroups, DPIRestrictionGroup]( + key="DPI restriction", + entity_category=EntityCategory.CONFIG, + icon="mdi:network", + allowed_fn=lambda controller, obj_id: controller.option_dpi_restrictions, + api_handler_fn=lambda api: api.dpi_groups, + available_fn=lambda controller, obj_id: controller.available, + control_fn=async_dpi_group_control_fn, + custom_subscribe=lambda api: api.dpi_apps.subscribe, + device_info_fn=async_dpi_group_device_info_fn, + event_is_on=None, + event_to_subscribe=None, + is_on_fn=async_dpi_group_is_on_fn, + name_fn=lambda group: group.name, + object_fn=lambda api, obj_id: api.dpi_groups[obj_id], + supported_fn=lambda api, obj_id: bool(api.dpi_groups[obj_id].dpiapp_ids), + unique_id_fn=lambda obj_id: obj_id, + ), + UnifiEntityDescription[Outlets, Outlet]( + key="Outlet control", + device_class=SwitchDeviceClass.OUTLET, + has_entity_name=True, + allowed_fn=lambda controller, obj_id: True, + api_handler_fn=lambda api: api.outlets, + available_fn=async_sub_device_available_fn, + control_fn=async_outlet_control_fn, + device_info_fn=async_device_device_info_fn, + event_is_on=None, + event_to_subscribe=None, + is_on_fn=lambda api, outlet: outlet.relay_state, + name_fn=lambda outlet: outlet.name, + object_fn=lambda api, obj_id: api.outlets[obj_id], + supported_fn=lambda api, obj_id: api.outlets[obj_id].has_relay, + unique_id_fn=lambda obj_id: f"{obj_id.split('_', 1)[0]}-outlet-{obj_id.split('_', 1)[1]}", + ), + UnifiEntityDescription[Ports, Port]( + key="PoE port control", + device_class=SwitchDeviceClass.OUTLET, + entity_category=EntityCategory.CONFIG, + has_entity_name=True, + entity_registry_enabled_default=False, + icon="mdi:ethernet", + allowed_fn=lambda controller, obj_id: True, + api_handler_fn=lambda api: api.ports, + available_fn=async_sub_device_available_fn, + control_fn=async_poe_port_control_fn, + device_info_fn=async_device_device_info_fn, + event_is_on=None, + event_to_subscribe=None, + is_on_fn=lambda api, port: port.poe_mode != "off", + name_fn=lambda port: f"{port.name} PoE", + object_fn=lambda api, obj_id: api.ports[obj_id], + supported_fn=lambda api, obj_id: api.ports[obj_id].port_poe, + unique_id_fn=lambda obj_id: f"{obj_id.split('_', 1)[0]}-poe-{obj_id.split('_', 1)[1]}", + ), +) async def async_setup_entry( @@ -71,17 +266,9 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up switches for UniFi Network integration. - - Switches are controlling network access and switch ports with POE. - """ + """Set up switches for UniFi Network integration.""" controller: UniFiController = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - controller.entities[DOMAIN] = { - BLOCK_SWITCH: set(), - POE_SWITCH: set(), - DPI_SWITCH: set(), - OUTLET_SWITCH: set(), - } + controller.entities[DOMAIN] = {POE_SWITCH: set()} if controller.site_role != "admin": return @@ -125,20 +312,20 @@ async def async_setup_entry( known_poe_clients.clear() @callback - def async_load_entities(loader: UnifiEntityLoader) -> None: + def async_load_entities(description: UnifiEntityDescription) -> None: """Load and subscribe to UniFi devices.""" entities: list[SwitchEntity] = [] - api_handler = loader.handler_fn(controller) + api_handler = description.api_handler_fn(controller.api) @callback def async_create_entity(event: ItemEvent, obj_id: str) -> None: """Create UniFi entity.""" - if not loader.allowed_fn(controller, obj_id) or not loader.supported_fn( - api_handler, obj_id - ): + if not description.allowed_fn( + controller, obj_id + ) or not description.supported_fn(controller.api, obj_id): return - entity = loader.entity_cls(obj_id, controller) + entity = UnifiSwitchEntity(obj_id, controller, description) if event == ItemEvent.ADDED: async_add_entities([entity]) return @@ -150,8 +337,8 @@ async def async_setup_entry( api_handler.subscribe(async_create_entity, ItemEvent.ADDED) - for unifi_loader in UNIFI_LOADERS: - async_load_entities(unifi_loader) + for description in ENTITY_DESCRIPTIONS: + async_load_entities(description) @callback @@ -301,51 +488,52 @@ class UniFiPOEClientSwitch(UniFiClient, SwitchEntity, RestoreEntity): await self.remove_item({self.client.mac}) -class UnifiBlockClientSwitch(SwitchEntity): - """Representation of a blockable client.""" +class UnifiSwitchEntity(SwitchEntity): + """Base representation of a UniFi switch.""" - _attr_device_class = SwitchDeviceClass.SWITCH - _attr_entity_category = EntityCategory.CONFIG - _attr_has_entity_name = True - _attr_icon = "mdi:ethernet" + entity_description: UnifiEntityDescription _attr_should_poll = False - def __init__(self, obj_id: str, controller: UniFiController) -> None: - """Set up block switch.""" - controller.entities[DOMAIN][BLOCK_SWITCH].add(obj_id) + def __init__( + self, + obj_id: str, + controller: UniFiController, + description: UnifiEntityDescription, + ) -> None: + """Set up UniFi switch entity.""" self._obj_id = obj_id self.controller = controller + self.entity_description = description self._removed = False - client = controller.api.clients[obj_id] - self._attr_available = controller.available - self._attr_is_on = not client.blocked - self._attr_unique_id = f"{BLOCK_SWITCH}-{obj_id}" - self._attr_device_info = DeviceInfo( - connections={(CONNECTION_NETWORK_MAC, obj_id)}, - default_manufacturer=client.oui, - default_name=client.name or client.hostname, + self._attr_available = description.available_fn(controller, obj_id) + self._attr_device_info = description.device_info_fn(controller.api, obj_id) + self._attr_unique_id = description.unique_id_fn(obj_id) + + obj = description.object_fn(self.controller.api, obj_id) + self._attr_is_on = description.is_on_fn(controller.api, obj) + self._attr_name = description.name_fn(obj) + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn on switch.""" + await self.entity_description.control_fn( + self.controller.api, self._obj_id, True + ) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn off switch.""" + await self.entity_description.control_fn( + self.controller.api, self._obj_id, False ) async def async_added_to_hass(self) -> None: - """Entity created.""" + """Register callbacks.""" + description = self.entity_description + handler = description.api_handler_fn(self.controller.api) self.async_on_remove( - self.controller.api.clients.subscribe(self.async_signalling_callback) - ) - self.async_on_remove( - self.controller.api.events.subscribe( - self.async_event_callback, CLIENT_BLOCKED + CLIENT_UNBLOCKED - ) - ) - self.async_on_remove( - async_dispatcher_connect( - self.hass, self.controller.signal_remove, self.remove_item - ) - ) - self.async_on_remove( - async_dispatcher_connect( - self.hass, self.controller.signal_options_update, self.options_updated + handler.subscribe( + self.async_signalling_callback, ) ) self.async_on_remove( @@ -355,31 +543,49 @@ class UnifiBlockClientSwitch(SwitchEntity): self.async_signal_reachable_callback, ) ) - - async def async_will_remove_from_hass(self) -> None: - """Disconnect object when removed.""" - self.controller.entities[DOMAIN][BLOCK_SWITCH].remove(self._obj_id) + self.async_on_remove( + async_dispatcher_connect( + self.hass, + self.controller.signal_options_update, + self.options_updated, + ) + ) + self.async_on_remove( + async_dispatcher_connect( + self.hass, + self.controller.signal_remove, + self.remove_item, + ) + ) + if description.event_to_subscribe is not None: + self.async_on_remove( + self.controller.api.events.subscribe( + self.async_event_callback, + description.event_to_subscribe, + ) + ) + if description.custom_subscribe is not None: + self.async_on_remove( + description.custom_subscribe(self.controller.api)( + self.async_signalling_callback, ItemEvent.CHANGED + ), + ) @callback def async_signalling_callback(self, event: ItemEvent, obj_id: str) -> None: - """Update the clients state.""" - if event == ItemEvent.DELETED: + """Update the switch state.""" + if event == ItemEvent.DELETED and obj_id == self._obj_id: self.hass.async_create_task(self.remove_item({self._obj_id})) return - client = self.controller.api.clients[self._obj_id] - self._attr_is_on = not client.blocked - self._attr_available = self.controller.available - self.async_write_ha_state() - - @callback - def async_event_callback(self, event: Event) -> None: - """Event subscription callback.""" - if event.mac != self._obj_id: + description = self.entity_description + if not description.supported_fn(self.controller.api, self._obj_id): + self.hass.async_create_task(self.remove_item({self._obj_id})) return - if event.key in CLIENT_BLOCKED + CLIENT_UNBLOCKED: - self._attr_is_on = event.key in CLIENT_UNBLOCKED - self._attr_available = self.controller.available + + obj = description.object_fn(self.controller.api, self._obj_id) + self._attr_is_on = description.is_on_fn(self.controller.api, obj) + self._attr_available = description.available_fn(self.controller, self._obj_id) self.async_write_ha_state() @callback @@ -387,30 +593,28 @@ class UnifiBlockClientSwitch(SwitchEntity): """Call when controller connection state change.""" self.async_signalling_callback(ItemEvent.ADDED, self._obj_id) - async def async_turn_on(self, **kwargs: Any) -> None: - """Turn on connectivity for client.""" - await self.controller.api.request( - ClientBlockRequest.create(self._obj_id, False) - ) + @callback + def async_event_callback(self, event: Event) -> None: + """Event subscription callback.""" + if event.mac != self._obj_id: + return - async def async_turn_off(self, **kwargs: Any) -> None: - """Turn off connectivity for client.""" - await self.controller.api.request(ClientBlockRequest.create(self._obj_id, True)) + description = self.entity_description + assert isinstance(description.event_to_subscribe, tuple) + assert isinstance(description.event_is_on, tuple) - @property - def icon(self) -> str: - """Return the icon to use in the frontend.""" - if not self.is_on: - return "mdi:network-off" - return "mdi:network" + if event.key in description.event_to_subscribe: + self._attr_is_on = event.key in description.event_is_on + self._attr_available = description.available_fn(self.controller, self._obj_id) + self.async_write_ha_state() async def options_updated(self) -> None: """Config entry options are updated, remove entity if option is disabled.""" - if self._obj_id not in self.controller.option_block_clients: + if not self.entity_description.allowed_fn(self.controller, self._obj_id): await self.remove_item({self._obj_id}) async def remove_item(self, keys: set) -> None: - """Remove entity if key is part of set.""" + """Remove entity if object ID is part of set.""" if self._obj_id not in keys or self._removed: return self._removed = True @@ -418,313 +622,3 @@ class UnifiBlockClientSwitch(SwitchEntity): er.async_get(self.hass).async_remove(self.entity_id) else: await self.async_remove(force_remove=True) - - -class UnifiDPIRestrictionSwitch(SwitchEntity): - """Representation of a DPI restriction group.""" - - _attr_entity_category = EntityCategory.CONFIG - - def __init__(self, obj_id: str, controller: UniFiController) -> None: - """Set up dpi switch.""" - controller.entities[DOMAIN][DPI_SWITCH].add(obj_id) - self._obj_id = obj_id - self.controller = controller - - dpi_group = controller.api.dpi_groups[obj_id] - self._known_app_ids = dpi_group.dpiapp_ids - - self._attr_available = controller.available - self._attr_is_on = self.calculate_enabled() - self._attr_name = dpi_group.name - self._attr_unique_id = dpi_group.id - self._attr_device_info = DeviceInfo( - entry_type=DeviceEntryType.SERVICE, - identifiers={(DOMAIN, f"unifi_controller_{obj_id}")}, - manufacturer=ATTR_MANUFACTURER, - model="UniFi Network", - name="UniFi Network", - ) - - async def async_added_to_hass(self) -> None: - """Register callback to known apps.""" - self.async_on_remove( - self.controller.api.dpi_groups.subscribe(self.async_signalling_callback) - ) - self.async_on_remove( - self.controller.api.dpi_apps.subscribe( - self.async_signalling_callback, ItemEvent.CHANGED - ), - ) - self.async_on_remove( - async_dispatcher_connect( - self.hass, self.controller.signal_remove, self.remove_item - ) - ) - self.async_on_remove( - async_dispatcher_connect( - self.hass, self.controller.signal_options_update, self.options_updated - ) - ) - self.async_on_remove( - async_dispatcher_connect( - self.hass, - self.controller.signal_reachable, - self.async_signal_reachable_callback, - ) - ) - - async def async_will_remove_from_hass(self) -> None: - """Disconnect object when removed.""" - self.controller.entities[DOMAIN][DPI_SWITCH].remove(self._obj_id) - - @callback - def async_signalling_callback(self, event: ItemEvent, obj_id: str) -> None: - """Object has new event.""" - if event == ItemEvent.DELETED: - self.hass.async_create_task(self.remove_item({self._obj_id})) - return - - dpi_group = self.controller.api.dpi_groups[self._obj_id] - if not dpi_group.dpiapp_ids: - self.hass.async_create_task(self.remove_item({self._obj_id})) - return - - self._attr_available = self.controller.available - self._attr_is_on = self.calculate_enabled() - self.async_write_ha_state() - - @callback - def async_signal_reachable_callback(self) -> None: - """Call when controller connection state change.""" - self.async_signalling_callback(ItemEvent.ADDED, self._obj_id) - - @property - def icon(self): - """Return the icon to use in the frontend.""" - if self.is_on: - return "mdi:network" - return "mdi:network-off" - - def calculate_enabled(self) -> bool: - """Calculate if all apps are enabled.""" - dpi_group = self.controller.api.dpi_groups[self._obj_id] - return all( - self.controller.api.dpi_apps[app_id].enabled - for app_id in dpi_group.dpiapp_ids - if app_id in self.controller.api.dpi_apps - ) - - async def async_turn_on(self, **kwargs: Any) -> None: - """Restrict access of apps related to DPI group.""" - dpi_group = self.controller.api.dpi_groups[self._obj_id] - return await asyncio.gather( - *[ - self.controller.api.request( - DPIRestrictionAppEnableRequest.create(app_id, True) - ) - for app_id in dpi_group.dpiapp_ids - ] - ) - - async def async_turn_off(self, **kwargs: Any) -> None: - """Remove restriction of apps related to DPI group.""" - dpi_group = self.controller.api.dpi_groups[self._obj_id] - return await asyncio.gather( - *[ - self.controller.api.request( - DPIRestrictionAppEnableRequest.create(app_id, False) - ) - for app_id in dpi_group.dpiapp_ids - ] - ) - - async def options_updated(self) -> None: - """Config entry options are updated, remove entity if option is disabled.""" - if not self.controller.option_dpi_restrictions: - await self.remove_item({self._attr_unique_id}) - - async def remove_item(self, keys: set) -> None: - """Remove entity if key is part of set.""" - if self._attr_unique_id not in keys: - return - - if self.registry_entry: - er.async_get(self.hass).async_remove(self.entity_id) - else: - await self.async_remove(force_remove=True) - - -class UnifiOutletSwitch(SwitchEntity): - """Representation of a outlet relay.""" - - _attr_device_class = SwitchDeviceClass.OUTLET - _attr_has_entity_name = True - _attr_should_poll = False - - def __init__(self, obj_id: str, controller: UniFiController) -> None: - """Set up UniFi Network entity base.""" - self._device_mac, index = obj_id.split("_", 1) - self._index = int(index) - self._obj_id = obj_id - self.controller = controller - - outlet = self.controller.api.outlets[self._obj_id] - self._attr_name = outlet.name - self._attr_is_on = outlet.relay_state - self._attr_unique_id = f"{self._device_mac}-outlet-{index}" - - device = self.controller.api.devices[self._device_mac] - self._attr_available = controller.available and not device.disabled - self._attr_device_info = DeviceInfo( - connections={(CONNECTION_NETWORK_MAC, device.mac)}, - manufacturer=ATTR_MANUFACTURER, - model=device.model, - name=device.name or None, - sw_version=device.version, - hw_version=device.board_revision, - ) - - async def async_added_to_hass(self) -> None: - """Entity created.""" - self.async_on_remove( - self.controller.api.outlets.subscribe(self.async_signalling_callback) - ) - self.async_on_remove( - async_dispatcher_connect( - self.hass, - self.controller.signal_reachable, - self.async_signal_reachable_callback, - ) - ) - - @callback - def async_signalling_callback(self, event: ItemEvent, obj_id: str) -> None: - """Object has new event.""" - device = self.controller.api.devices[self._device_mac] - outlet = self.controller.api.outlets[self._obj_id] - self._attr_available = self.controller.available and not device.disabled - self._attr_is_on = outlet.relay_state - self.async_write_ha_state() - - @callback - def async_signal_reachable_callback(self) -> None: - """Call when controller connection state change.""" - self.async_signalling_callback(ItemEvent.ADDED, self._obj_id) - - async def async_turn_on(self, **kwargs: Any) -> None: - """Enable outlet relay.""" - device = self.controller.api.devices[self._device_mac] - await self.controller.api.request( - DeviceSetOutletRelayRequest.create(device, self._index, True) - ) - - async def async_turn_off(self, **kwargs: Any) -> None: - """Disable outlet relay.""" - device = self.controller.api.devices[self._device_mac] - await self.controller.api.request( - DeviceSetOutletRelayRequest.create(device, self._index, False) - ) - - -class UnifiPoePortSwitch(SwitchEntity): - """Representation of a Power-over-Ethernet source port on an UniFi device.""" - - _attr_device_class = SwitchDeviceClass.OUTLET - _attr_entity_category = EntityCategory.CONFIG - _attr_entity_registry_enabled_default = False - _attr_has_entity_name = True - _attr_icon = "mdi:ethernet" - _attr_should_poll = False - - def __init__(self, obj_id: str, controller: UniFiController) -> None: - """Set up UniFi Network entity base.""" - self._device_mac, index = obj_id.split("_", 1) - self._index = int(index) - self._obj_id = obj_id - self.controller = controller - - port = self.controller.api.ports[self._obj_id] - self._attr_name = f"{port.name} PoE" - self._attr_is_on = port.poe_mode != "off" - self._attr_unique_id = f"{self._device_mac}-poe-{index}" - - device = self.controller.api.devices[self._device_mac] - self._attr_available = controller.available and not device.disabled - self._attr_device_info = DeviceInfo( - connections={(CONNECTION_NETWORK_MAC, device.mac)}, - manufacturer=ATTR_MANUFACTURER, - model=device.model, - name=device.name or None, - sw_version=device.version, - hw_version=device.board_revision, - ) - - async def async_added_to_hass(self) -> None: - """Entity created.""" - self.async_on_remove( - self.controller.api.ports.subscribe(self.async_signalling_callback) - ) - self.async_on_remove( - async_dispatcher_connect( - self.hass, - self.controller.signal_reachable, - self.async_signal_reachable_callback, - ) - ) - - @callback - def async_signalling_callback(self, event: ItemEvent, obj_id: str) -> None: - """Object has new event.""" - device = self.controller.api.devices[self._device_mac] - port = self.controller.api.ports[self._obj_id] - self._attr_available = self.controller.available and not device.disabled - self._attr_is_on = port.poe_mode != "off" - self.async_write_ha_state() - - @callback - def async_signal_reachable_callback(self) -> None: - """Call when controller connection state change.""" - self.async_signalling_callback(ItemEvent.ADDED, self._obj_id) - - async def async_turn_on(self, **kwargs: Any) -> None: - """Enable POE for client.""" - device = self.controller.api.devices[self._device_mac] - await self.controller.api.request( - DeviceSetPoePortModeRequest.create(device, self._index, "auto") - ) - - async def async_turn_off(self, **kwargs: Any) -> None: - """Disable POE for client.""" - device = self.controller.api.devices[self._device_mac] - await self.controller.api.request( - DeviceSetPoePortModeRequest.create(device, self._index, "off") - ) - - -UNIFI_LOADERS: tuple[UnifiEntityLoader, ...] = ( - UnifiEntityLoader[Clients]( - allowed_fn=lambda controller, obj_id: obj_id in controller.option_block_clients, - entity_cls=UnifiBlockClientSwitch, - handler_fn=lambda contrlr: contrlr.api.clients, - supported_fn=lambda handler, obj_id: True, - ), - UnifiEntityLoader[DPIRestrictionGroups]( - allowed_fn=lambda controller, obj_id: controller.option_dpi_restrictions, - entity_cls=UnifiDPIRestrictionSwitch, - handler_fn=lambda controller: controller.api.dpi_groups, - supported_fn=lambda handler, obj_id: bool(handler[obj_id].dpiapp_ids), - ), - UnifiEntityLoader[Outlets]( - allowed_fn=lambda controller, obj_id: True, - entity_cls=UnifiOutletSwitch, - handler_fn=lambda controller: controller.api.outlets, - supported_fn=lambda handler, obj_id: handler[obj_id].has_relay, - ), - UnifiEntityLoader[Ports]( - allowed_fn=lambda controller, obj_id: True, - entity_cls=UnifiPoePortSwitch, - handler_fn=lambda controller: controller.api.ports, - supported_fn=lambda handler, obj_id: handler[obj_id].port_poe, - ), -) diff --git a/tests/components/unifi/test_diagnostics.py b/tests/components/unifi/test_diagnostics.py index 6584b947293..9de0e4b6154 100644 --- a/tests/components/unifi/test_diagnostics.py +++ b/tests/components/unifi/test_diagnostics.py @@ -6,16 +6,6 @@ from homeassistant.components.unifi.const import ( CONF_ALLOW_UPTIME_SENSORS, CONF_BLOCK_CLIENT, ) -from homeassistant.components.unifi.device_tracker import CLIENT_TRACKER, DEVICE_TRACKER -from homeassistant.components.unifi.sensor import RX_SENSOR, TX_SENSOR, UPTIME_SENSOR -from homeassistant.components.unifi.switch import ( - BLOCK_SWITCH, - DPI_SWITCH, - OUTLET_SWITCH, - POE_SWITCH, -) -from homeassistant.components.unifi.update import DEVICE_UPDATE -from homeassistant.const import Platform from .test_controller import setup_unifi_integration @@ -146,26 +136,6 @@ async def test_entry_diagnostics(hass, hass_client, aioclient_mock): "version": 1, }, "site_role": "admin", - "entities": { - str(Platform.DEVICE_TRACKER): { - CLIENT_TRACKER: ["00:00:00:00:00:00"], - DEVICE_TRACKER: ["00:00:00:00:00:01"], - }, - str(Platform.SENSOR): { - RX_SENSOR: ["00:00:00:00:00:00"], - TX_SENSOR: ["00:00:00:00:00:00"], - UPTIME_SENSOR: ["00:00:00:00:00:00"], - }, - str(Platform.SWITCH): { - BLOCK_SWITCH: ["00:00:00:00:00:00"], - DPI_SWITCH: ["5f976f4ae3c58f018ec7dff6"], - POE_SWITCH: ["00:00:00:00:00:00"], - OUTLET_SWITCH: [], - }, - str(Platform.UPDATE): { - DEVICE_UPDATE: ["00:00:00:00:00:01"], - }, - }, "clients": { "00:00:00:00:00:00": { "blocked": False, From 23bed25e5206b1ec16c241e1825aad12752e268e Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 8 Nov 2022 07:48:54 +0100 Subject: [PATCH 0278/1033] Remove old UniFi POE client implementation (#81749) Remove all references to POE client implementation --- homeassistant/components/unifi/__init__.py | 22 ++ homeassistant/components/unifi/config_flow.py | 6 - homeassistant/components/unifi/const.py | 3 - homeassistant/components/unifi/controller.py | 10 +- homeassistant/components/unifi/switch.py | 185 +--------- tests/components/unifi/test_config_flow.py | 3 - tests/components/unifi/test_switch.py | 324 +++--------------- 7 files changed, 66 insertions(+), 487 deletions(-) diff --git a/homeassistant/components/unifi/__init__.py b/homeassistant/components/unifi/__init__.py index 84540f7bea4..e37e89b3da5 100644 --- a/homeassistant/components/unifi/__init__.py +++ b/homeassistant/components/unifi/__init__.py @@ -6,6 +6,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType @@ -36,6 +37,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b """Set up the UniFi Network integration.""" hass.data.setdefault(UNIFI_DOMAIN, {}) + # Removal of legacy PoE control was introduced with 2022.12 + async_remove_poe_client_entities(hass, config_entry) + # Flat configuration was introduced with 2021.3 await async_flatten_entry_data(hass, config_entry) @@ -82,6 +86,24 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> return await controller.async_reset() +@callback +def async_remove_poe_client_entities( + hass: HomeAssistant, config_entry: ConfigEntry +) -> None: + """Remove PoE client entities.""" + ent_reg = er.async_get(hass) + + entity_ids_to_be_removed = [ + entry.entity_id + for entry in ent_reg.entities.values() + if entry.config_entry_id == config_entry.entry_id + and entry.unique_id.startswith("poe-") + ] + + for entity_id in entity_ids_to_be_removed: + ent_reg.async_remove(entity_id) + + async def async_flatten_entry_data( hass: HomeAssistant, config_entry: ConfigEntry ) -> None: diff --git a/homeassistant/components/unifi/config_flow.py b/homeassistant/components/unifi/config_flow.py index 4944dd91296..caf256ded20 100644 --- a/homeassistant/components/unifi/config_flow.py +++ b/homeassistant/components/unifi/config_flow.py @@ -37,14 +37,12 @@ from .const import ( CONF_DETECTION_TIME, CONF_DPI_RESTRICTIONS, CONF_IGNORE_WIRED_BUG, - CONF_POE_CLIENTS, CONF_SITE_ID, CONF_SSID_FILTER, CONF_TRACK_CLIENTS, CONF_TRACK_DEVICES, CONF_TRACK_WIRED_CLIENTS, DEFAULT_DPI_RESTRICTIONS, - DEFAULT_POE_CLIENTS, DOMAIN as UNIFI_DOMAIN, ) from .controller import UniFiController, get_unifi_controller @@ -396,10 +394,6 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): vol.Optional( CONF_BLOCK_CLIENT, default=selected_clients_to_block ): cv.multi_select(clients_to_block), - vol.Optional( - CONF_POE_CLIENTS, - default=self.options.get(CONF_POE_CLIENTS, DEFAULT_POE_CLIENTS), - ): bool, vol.Optional( CONF_DPI_RESTRICTIONS, default=self.options.get( diff --git a/homeassistant/components/unifi/const.py b/homeassistant/components/unifi/const.py index bf0aaef45dd..85f744e481f 100644 --- a/homeassistant/components/unifi/const.py +++ b/homeassistant/components/unifi/const.py @@ -25,7 +25,6 @@ CONF_BLOCK_CLIENT = "block_client" CONF_DETECTION_TIME = "detection_time" CONF_DPI_RESTRICTIONS = "dpi_restrictions" CONF_IGNORE_WIRED_BUG = "ignore_wired_bug" -CONF_POE_CLIENTS = "poe_clients" CONF_TRACK_CLIENTS = "track_clients" CONF_TRACK_DEVICES = "track_devices" CONF_TRACK_WIRED_CLIENTS = "track_wired_clients" @@ -35,7 +34,6 @@ DEFAULT_ALLOW_BANDWIDTH_SENSORS = False DEFAULT_ALLOW_UPTIME_SENSORS = False DEFAULT_DPI_RESTRICTIONS = True DEFAULT_IGNORE_WIRED_BUG = False -DEFAULT_POE_CLIENTS = True DEFAULT_TRACK_CLIENTS = True DEFAULT_TRACK_DEVICES = True DEFAULT_TRACK_WIRED_CLIENTS = True @@ -45,5 +43,4 @@ ATTR_MANUFACTURER = "Ubiquiti Networks" BLOCK_SWITCH = "block" DPI_SWITCH = "dpi" -POE_SWITCH = "poe" OUTLET_SWITCH = "outlet" diff --git a/homeassistant/components/unifi/controller.py b/homeassistant/components/unifi/controller.py index c421cb5391a..8aae95bda41 100644 --- a/homeassistant/components/unifi/controller.py +++ b/homeassistant/components/unifi/controller.py @@ -44,7 +44,6 @@ from .const import ( CONF_DETECTION_TIME, CONF_DPI_RESTRICTIONS, CONF_IGNORE_WIRED_BUG, - CONF_POE_CLIENTS, CONF_SITE_ID, CONF_SSID_FILTER, CONF_TRACK_CLIENTS, @@ -55,14 +54,12 @@ from .const import ( DEFAULT_DETECTION_TIME, DEFAULT_DPI_RESTRICTIONS, DEFAULT_IGNORE_WIRED_BUG, - DEFAULT_POE_CLIENTS, DEFAULT_TRACK_CLIENTS, DEFAULT_TRACK_DEVICES, DEFAULT_TRACK_WIRED_CLIENTS, DOMAIN as UNIFI_DOMAIN, LOGGER, PLATFORMS, - POE_SWITCH, UNIFI_WIRELESS_CLIENTS, ) from .errors import AuthenticationRequired, CannotConnect @@ -140,8 +137,6 @@ class UniFiController: # Client control options - # Config entry option to control poe clients. - self.option_poe_clients = options.get(CONF_POE_CLIENTS, DEFAULT_POE_CLIENTS) # Config entry option with list of clients to control network access. self.option_block_clients = options.get(CONF_BLOCK_CLIENT, []) # Config entry option to control DPI restriction groups. @@ -305,9 +300,8 @@ class UniFiController: ): if entry.domain == Platform.DEVICE_TRACKER: mac = entry.unique_id.split("-", 1)[0] - elif entry.domain == Platform.SWITCH and ( - entry.unique_id.startswith(BLOCK_SWITCH) - or entry.unique_id.startswith(POE_SWITCH) + elif entry.domain == Platform.SWITCH and entry.unique_id.startswith( + BLOCK_SWITCH ): mac = entry.unique_id.split("-", 1)[1] else: diff --git a/homeassistant/components/unifi/switch.py b/homeassistant/components/unifi/switch.py index e63d5548ebc..5a88caca807 100644 --- a/homeassistant/components/unifi/switch.py +++ b/homeassistant/components/unifi/switch.py @@ -44,11 +44,9 @@ from homeassistant.helpers.device_registry import ( from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.restore_state import RestoreEntity -from .const import ATTR_MANUFACTURER, DOMAIN as UNIFI_DOMAIN, POE_SWITCH +from .const import ATTR_MANUFACTURER, DOMAIN as UNIFI_DOMAIN from .controller import UniFiController -from .unifi_client import UniFiClient CLIENT_BLOCKED = (EventKey.WIRED_CLIENT_BLOCKED, EventKey.WIRELESS_CLIENT_BLOCKED) CLIENT_UNBLOCKED = (EventKey.WIRED_CLIENT_UNBLOCKED, EventKey.WIRELESS_CLIENT_UNBLOCKED) @@ -268,49 +266,15 @@ async def async_setup_entry( ) -> None: """Set up switches for UniFi Network integration.""" controller: UniFiController = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - controller.entities[DOMAIN] = {POE_SWITCH: set()} if controller.site_role != "admin": return - # Store previously known POE control entities in case their POE are turned off. - known_poe_clients = [] - entity_registry = er.async_get(hass) - for entry in er.async_entries_for_config_entry( - entity_registry, config_entry.entry_id - ): - - if not entry.unique_id.startswith(POE_SWITCH): - continue - - mac = entry.unique_id.replace(f"{POE_SWITCH}-", "") - if mac not in controller.api.clients: - continue - - known_poe_clients.append(mac) - for mac in controller.option_block_clients: if mac not in controller.api.clients and mac in controller.api.clients_all: client = controller.api.clients_all[mac] controller.api.clients.process_raw([client.raw]) - @callback - def items_added( - clients: set = controller.api.clients, - devices: set = controller.api.devices, - ) -> None: - """Update the values of the controller.""" - if controller.option_poe_clients: - add_poe_entities(controller, async_add_entities, clients, known_poe_clients) - - for signal in (controller.signal_update, controller.signal_options_update): - config_entry.async_on_unload( - async_dispatcher_connect(hass, signal, items_added) - ) - - items_added() - known_poe_clients.clear() - @callback def async_load_entities(description: UnifiEntityDescription) -> None: """Load and subscribe to UniFi devices.""" @@ -341,153 +305,6 @@ async def async_setup_entry( async_load_entities(description) -@callback -def add_poe_entities(controller, async_add_entities, clients, known_poe_clients): - """Add new switch entities from the controller.""" - switches = [] - - devices = controller.api.devices - - for mac in clients: - if mac in controller.entities[DOMAIN][POE_SWITCH]: - continue - - client = controller.api.clients[mac] - - # Try to identify new clients powered by POE. - # Known POE clients have been created in previous HASS sessions. - # If port_poe is None the port does not support POE - # If poe_enable is False we can't know if a POE client is available for control. - if mac not in known_poe_clients and ( - mac in controller.wireless_clients - or client.switch_mac not in devices - or not devices[client.switch_mac].ports[client.switch_port].port_poe - or not devices[client.switch_mac].ports[client.switch_port].poe_enable - or controller.mac == client.mac - ): - continue - - # Multiple POE-devices on same port means non UniFi POE driven switch - multi_clients_on_port = False - for client2 in controller.api.clients.values(): - - if mac in known_poe_clients: - break - - if ( - client2.is_wired - and client.mac != client2.mac - and client.switch_mac == client2.switch_mac - and client.switch_port == client2.switch_port - ): - multi_clients_on_port = True - break - - if multi_clients_on_port: - continue - - switches.append(UniFiPOEClientSwitch(client, controller)) - - async_add_entities(switches) - - -class UniFiPOEClientSwitch(UniFiClient, SwitchEntity, RestoreEntity): - """Representation of a client that uses POE.""" - - DOMAIN = DOMAIN - TYPE = POE_SWITCH - - _attr_entity_category = EntityCategory.CONFIG - - def __init__(self, client, controller): - """Set up POE switch.""" - super().__init__(client, controller) - - self.poe_mode = None - if client.switch_port and self.port.poe_mode != "off": - self.poe_mode = self.port.poe_mode - - async def async_added_to_hass(self) -> None: - """Call when entity about to be added to Home Assistant.""" - await super().async_added_to_hass() - - if self.poe_mode: # POE is enabled and client in a known state - return - - if (state := await self.async_get_last_state()) is None: - return - - self.poe_mode = state.attributes.get("poe_mode") - - if not self.client.switch_mac: - self.client.raw["sw_mac"] = state.attributes.get("switch") - - if not self.client.switch_port: - self.client.raw["sw_port"] = state.attributes.get("port") - - @property - def is_on(self): - """Return true if POE is active.""" - return self.port.poe_mode != "off" - - @property - def available(self) -> bool: - """Return if switch is available. - - Poe_mode None means its POE state is unknown. - Sw_mac unavailable means restored client. - """ - return ( - self.poe_mode is not None - and self.controller.available - and self.client.switch_port - and self.client.switch_mac - and self.client.switch_mac in self.controller.api.devices - ) - - async def async_turn_on(self, **kwargs: Any) -> None: - """Enable POE for client.""" - await self.controller.api.request( - DeviceSetPoePortModeRequest.create( - self.device, self.client.switch_port, self.poe_mode - ) - ) - - async def async_turn_off(self, **kwargs: Any) -> None: - """Disable POE for client.""" - await self.controller.api.request( - DeviceSetPoePortModeRequest.create( - self.device, self.client.switch_port, "off" - ) - ) - - @property - def extra_state_attributes(self): - """Return the device state attributes.""" - attributes = { - "power": self.port.poe_power, - "switch": self.client.switch_mac, - "port": self.client.switch_port, - "poe_mode": self.poe_mode, - } - return attributes - - @property - def device(self): - """Shortcut to the switch that client is connected to.""" - return self.controller.api.devices[self.client.switch_mac] - - @property - def port(self): - """Shortcut to the switch port that client is connected to.""" - return self.device.ports[self.client.switch_port] - - async def options_updated(self) -> None: - """Config entry options are updated, remove entity if option is disabled.""" - if not self.controller.option_poe_clients: - await self.remove_item({self.client.mac}) - - class UnifiSwitchEntity(SwitchEntity): """Base representation of a UniFi switch.""" diff --git a/tests/components/unifi/test_config_flow.py b/tests/components/unifi/test_config_flow.py index a1f6f3d4b02..078c068c8ed 100644 --- a/tests/components/unifi/test_config_flow.py +++ b/tests/components/unifi/test_config_flow.py @@ -16,7 +16,6 @@ from homeassistant.components.unifi.const import ( CONF_DETECTION_TIME, CONF_DPI_RESTRICTIONS, CONF_IGNORE_WIRED_BUG, - CONF_POE_CLIENTS, CONF_SITE_ID, CONF_SSID_FILTER, CONF_TRACK_CLIENTS, @@ -473,7 +472,6 @@ async def test_advanced_option_flow(hass, aioclient_mock): result["flow_id"], user_input={ CONF_BLOCK_CLIENT: [CLIENTS[0]["mac"]], - CONF_POE_CLIENTS: False, CONF_DPI_RESTRICTIONS: False, }, ) @@ -498,7 +496,6 @@ async def test_advanced_option_flow(hass, aioclient_mock): CONF_SSID_FILTER: ["SSID 1", "SSID 2_IOT", "SSID 3"], CONF_DETECTION_TIME: 100, CONF_IGNORE_WIRED_BUG: False, - CONF_POE_CLIENTS: False, CONF_DPI_RESTRICTIONS: False, CONF_BLOCK_CLIENT: [CLIENTS[0]["mac"]], CONF_ALLOW_BANDWIDTH_SENSORS: True, diff --git a/tests/components/unifi/test_switch.py b/tests/components/unifi/test_switch.py index e6357b03172..5178e3dda37 100644 --- a/tests/components/unifi/test_switch.py +++ b/tests/components/unifi/test_switch.py @@ -6,7 +6,7 @@ from datetime import timedelta from aiounifi.models.message import MessageKey from aiounifi.websocket import WebsocketState -from homeassistant import config_entries, core +from homeassistant import config_entries from homeassistant.components.switch import ( DOMAIN as SWITCH_DOMAIN, SERVICE_TURN_OFF, @@ -16,12 +16,10 @@ from homeassistant.components.switch import ( from homeassistant.components.unifi.const import ( CONF_BLOCK_CLIENT, CONF_DPI_RESTRICTIONS, - CONF_POE_CLIENTS, CONF_TRACK_CLIENTS, CONF_TRACK_DEVICES, DOMAIN as UNIFI_DOMAIN, ) -from homeassistant.components.unifi.switch import POE_SWITCH from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY from homeassistant.const import ( ATTR_DEVICE_CLASS, @@ -38,13 +36,12 @@ from homeassistant.util import dt from .test_controller import ( CONTROLLER_HOST, - DEFAULT_CONFIG_ENTRY_ID, DESCRIPTION, ENTRY_CONFIG, setup_unifi_integration, ) -from tests.common import async_fire_time_changed, mock_restore_cache +from tests.common import async_fire_time_changed CLIENT_1 = { "hostname": "client_1", @@ -636,23 +633,14 @@ async def test_switches(hass, aioclient_mock): CONF_TRACK_CLIENTS: False, CONF_TRACK_DEVICES: False, }, - clients_response=[CLIENT_1, CLIENT_4], - devices_response=[DEVICE_1], + clients_response=[CLIENT_4], clients_all_response=[BLOCKED, UNBLOCKED, CLIENT_1], dpigroup_response=DPI_GROUPS, dpiapp_response=DPI_APPS, ) controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 4 - - switch_1 = hass.states.get("switch.poe_client_1") - assert switch_1 is not None - assert switch_1.state == "on" - assert switch_1.attributes["power"] == "2.56" - assert switch_1.attributes[SWITCH_DOMAIN] == "10:00:00:00:01:01" - assert switch_1.attributes["port"] == 1 - assert switch_1.attributes["poe_mode"] == "auto" + assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 3 switch_4 = hass.states.get("switch.poe_client_4") assert switch_4 is None @@ -671,11 +659,7 @@ async def test_switches(hass, aioclient_mock): assert dpi_switch.attributes["icon"] == "mdi:network" ent_reg = er.async_get(hass) - for entry_id in ( - "switch.poe_client_1", - "switch.block_client_1", - "switch.block_media_streaming", - ): + for entry_id in ("switch.block_client_1", "switch.block_media_streaming"): assert ent_reg.async_get(entry_id).entity_category is EntityCategory.CONFIG # Block and unblock client @@ -729,7 +713,7 @@ async def test_switches(hass, aioclient_mock): # Make sure no duplicates arise on generic signal update async_dispatcher_send(hass, controller.signal_update) await hass.async_block_till_done() - assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 4 + assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 3 async def test_remove_switches(hass, aioclient_mock, mock_unifi_websocket): @@ -738,24 +722,21 @@ async def test_remove_switches(hass, aioclient_mock, mock_unifi_websocket): hass, aioclient_mock, options={CONF_BLOCK_CLIENT: [UNBLOCKED["mac"]]}, - clients_response=[CLIENT_1, UNBLOCKED], - devices_response=[DEVICE_1], + clients_response=[UNBLOCKED], dpigroup_response=DPI_GROUPS, dpiapp_response=DPI_APPS, ) - assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 3 + assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 2 - assert hass.states.get("switch.poe_client_1") is not None assert hass.states.get("switch.block_client_2") is not None assert hass.states.get("switch.block_media_streaming") is not None - mock_unifi_websocket(message=MessageKey.CLIENT_REMOVED, data=[CLIENT_1, UNBLOCKED]) + mock_unifi_websocket(message=MessageKey.CLIENT_REMOVED, data=[UNBLOCKED]) await hass.async_block_till_done() assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 1 - assert hass.states.get("switch.poe_client_1") is None assert hass.states.get("switch.block_client_2") is None assert hass.states.get("switch.block_media_streaming") is not None @@ -1089,273 +1070,20 @@ async def test_option_remove_switches(hass, aioclient_mock): CONF_TRACK_DEVICES: False, }, clients_response=[CLIENT_1], - devices_response=[DEVICE_1], dpigroup_response=DPI_GROUPS, dpiapp_response=DPI_APPS, ) - assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 2 + assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 1 # Disable DPI Switches hass.config_entries.async_update_entry( config_entry, - options={CONF_DPI_RESTRICTIONS: False, CONF_POE_CLIENTS: False}, + options={CONF_DPI_RESTRICTIONS: False}, ) await hass.async_block_till_done() assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 0 -async def test_new_client_discovered_on_poe_control( - hass, aioclient_mock, mock_unifi_websocket -): - """Test if 2nd update has a new client.""" - config_entry = await setup_unifi_integration( - hass, - aioclient_mock, - options={CONF_TRACK_CLIENTS: False, CONF_TRACK_DEVICES: False}, - clients_response=[CLIENT_1], - devices_response=[DEVICE_1], - ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - - assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 1 - - mock_unifi_websocket(message=MessageKey.CLIENT, data=CLIENT_2) - await hass.async_block_till_done() - - assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 1 - - mock_unifi_websocket(message=MessageKey.EVENT, data=EVENT_CLIENT_2_CONNECTED) - await hass.async_block_till_done() - - assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 2 - switch_2 = hass.states.get("switch.poe_client_2") - assert switch_2 is not None - - aioclient_mock.put( - f"https://{controller.host}:1234/api/s/{controller.site}/rest/device/mock-id", - ) - - await hass.services.async_call( - SWITCH_DOMAIN, "turn_off", {"entity_id": "switch.poe_client_1"}, blocking=True - ) - assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 2 - assert aioclient_mock.call_count == 11 - assert aioclient_mock.mock_calls[10][2] == { - "port_overrides": [{"port_idx": 1, "portconf_id": "1a1", "poe_mode": "off"}] - } - - await hass.services.async_call( - SWITCH_DOMAIN, "turn_on", {"entity_id": "switch.poe_client_1"}, blocking=True - ) - assert aioclient_mock.call_count == 12 - assert aioclient_mock.mock_calls[11][2] == { - "port_overrides": [{"port_idx": 1, "portconf_id": "1a1", "poe_mode": "auto"}] - } - - -async def test_ignore_multiple_poe_clients_on_same_port(hass, aioclient_mock): - """Ignore when there are multiple POE driven clients on same port. - - If there is a non-UniFi switch powered by POE, - clients will be transparently marked as having POE as well. - """ - await setup_unifi_integration( - hass, - aioclient_mock, - clients_response=POE_SWITCH_CLIENTS, - devices_response=[DEVICE_1], - ) - - switch_1 = hass.states.get("switch.poe_client_1") - switch_2 = hass.states.get("switch.poe_client_2") - assert switch_1 is None - assert switch_2 is None - - -async def test_restore_client_succeed(hass, aioclient_mock): - """Test that RestoreEntity works as expected.""" - POE_DEVICE = { - "device_id": "12345", - "ip": "1.0.1.1", - "mac": "00:00:00:00:01:01", - "last_seen": 1562600145, - "model": "US16P150", - "name": "POE Switch", - "port_overrides": [ - { - "poe_mode": "off", - "port_idx": 1, - "portconf_id": "5f3edd2aba4cc806a19f2db2", - } - ], - "port_table": [ - { - "media": "GE", - "name": "Port 1", - "op_mode": "switch", - "poe_caps": 7, - "poe_class": "Unknown", - "poe_current": "0.00", - "poe_enable": False, - "poe_good": False, - "poe_mode": "off", - "poe_power": "0.00", - "poe_voltage": "0.00", - "port_idx": 1, - "port_poe": True, - "portconf_id": "5f3edd2aba4cc806a19f2db2", - "up": False, - }, - ], - "state": 1, - "type": "usw", - "version": "4.0.42.10433", - } - POE_CLIENT = { - "hostname": "poe_client", - "ip": "1.0.0.1", - "is_wired": True, - "last_seen": 1562600145, - "mac": "00:00:00:00:00:01", - "name": "POE Client", - "oui": "Producer", - } - - fake_state = core.State( - "switch.poe_client", - "off", - { - "power": "0.00", - "switch": POE_DEVICE["mac"], - "port": 1, - "poe_mode": "auto", - }, - ) - mock_restore_cache(hass, (fake_state,)) - - config_entry = config_entries.ConfigEntry( - version=1, - domain=UNIFI_DOMAIN, - title="Mock Title", - data=ENTRY_CONFIG, - source="test", - options={}, - entry_id=DEFAULT_CONFIG_ENTRY_ID, - ) - - registry = er.async_get(hass) - registry.async_get_or_create( - SWITCH_DOMAIN, - UNIFI_DOMAIN, - f'{POE_SWITCH}-{POE_CLIENT["mac"]}', - suggested_object_id=POE_CLIENT["hostname"], - config_entry=config_entry, - ) - - await setup_unifi_integration( - hass, - aioclient_mock, - options={ - CONF_TRACK_CLIENTS: False, - CONF_TRACK_DEVICES: False, - }, - clients_response=[], - devices_response=[POE_DEVICE], - clients_all_response=[POE_CLIENT], - ) - - assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 1 - - poe_client = hass.states.get("switch.poe_client") - assert poe_client.state == "off" - - -async def test_restore_client_no_old_state(hass, aioclient_mock): - """Test that RestoreEntity without old state makes entity unavailable.""" - POE_DEVICE = { - "device_id": "12345", - "ip": "1.0.1.1", - "mac": "00:00:00:00:01:01", - "last_seen": 1562600145, - "model": "US16P150", - "name": "POE Switch", - "port_overrides": [ - { - "poe_mode": "off", - "port_idx": 1, - "portconf_id": "5f3edd2aba4cc806a19f2db2", - } - ], - "port_table": [ - { - "media": "GE", - "name": "Port 1", - "op_mode": "switch", - "poe_caps": 7, - "poe_class": "Unknown", - "poe_current": "0.00", - "poe_enable": False, - "poe_good": False, - "poe_mode": "off", - "poe_power": "0.00", - "poe_voltage": "0.00", - "port_idx": 1, - "port_poe": True, - "portconf_id": "5f3edd2aba4cc806a19f2db2", - "up": False, - }, - ], - "state": 1, - "type": "usw", - "version": "4.0.42.10433", - } - POE_CLIENT = { - "hostname": "poe_client", - "ip": "1.0.0.1", - "is_wired": True, - "last_seen": 1562600145, - "mac": "00:00:00:00:00:01", - "name": "POE Client", - "oui": "Producer", - } - - config_entry = config_entries.ConfigEntry( - version=1, - domain=UNIFI_DOMAIN, - title="Mock Title", - data=ENTRY_CONFIG, - source="test", - options={}, - entry_id=DEFAULT_CONFIG_ENTRY_ID, - ) - - registry = er.async_get(hass) - registry.async_get_or_create( - SWITCH_DOMAIN, - UNIFI_DOMAIN, - f'{POE_SWITCH}-{POE_CLIENT["mac"]}', - suggested_object_id=POE_CLIENT["hostname"], - config_entry=config_entry, - ) - - await setup_unifi_integration( - hass, - aioclient_mock, - options={ - CONF_TRACK_CLIENTS: False, - CONF_TRACK_DEVICES: False, - }, - clients_response=[], - devices_response=[POE_DEVICE], - clients_all_response=[POE_CLIENT], - ) - - assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 1 - - poe_client = hass.states.get("switch.poe_client") - assert poe_client.state == "unavailable" # self.poe_mode is None - - async def test_poe_port_switches(hass, aioclient_mock, mock_unifi_websocket): """Test the update_items function with some clients.""" config_entry = await setup_unifi_integration( @@ -1447,3 +1175,33 @@ async def test_poe_port_switches(hass, aioclient_mock, mock_unifi_websocket): mock_unifi_websocket(message=MessageKey.DEVICE, data=device_1) await hass.async_block_till_done() assert hass.states.get("switch.mock_name_port_1_poe").state == STATE_OFF + + +async def test_remove_poe_client_switches(hass, aioclient_mock): + """Test old PoE client switches are removed.""" + + config_entry = config_entries.ConfigEntry( + version=1, + domain=UNIFI_DOMAIN, + title="Mock Title", + data=ENTRY_CONFIG, + source="test", + options={}, + entry_id="1", + ) + + ent_reg = er.async_get(hass) + ent_reg.async_get_or_create( + SWITCH_DOMAIN, + UNIFI_DOMAIN, + "poe-123", + config_entry=config_entry, + ) + + await setup_unifi_integration(hass, aioclient_mock) + + assert not [ + entry + for entry in ent_reg.entities.values() + if entry.config_entry_id == config_entry.entry_id + ] From 88faf33cb86ba4bebdf0e8a6199969d45071f76c Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 8 Nov 2022 09:17:03 +0100 Subject: [PATCH 0279/1033] Improve type hints for MQTT climate (#81396) * Improve typing climate * Move climate type hints to class level * Apply suggestions from code review Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> * remove stale command after applying suggestions * cleanup * Update homeassistant/components/mqtt/climate.py Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- homeassistant/components/mqtt/climate.py | 123 ++++++++++++++--------- 1 file changed, 76 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index e46c8e31565..ef5a25d959b 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -1,6 +1,7 @@ """Support for MQTT climate devices.""" from __future__ import annotations +from collections.abc import Callable import functools import logging from typing import Any @@ -41,6 +42,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.template import Template from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import subscription @@ -54,7 +56,13 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttCommandTemplate, MqttValueTemplate +from .models import ( + MqttCommandTemplate, + MqttValueTemplate, + PublishPayloadType, + ReceiveMessage, + ReceivePayloadType, +) from .util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic _LOGGER = logging.getLogger(__name__) @@ -197,9 +205,9 @@ TOPIC_KEYS = ( ) -def valid_preset_mode_configuration(config): +def valid_preset_mode_configuration(config: ConfigType) -> ConfigType: """Validate that the preset mode reset payload is not one of the preset modes.""" - if PRESET_NONE in config.get(CONF_PRESET_MODES_LIST): + if PRESET_NONE in config[CONF_PRESET_MODES_LIST]: raise ValueError("preset_modes must not include preset mode 'none'") return config @@ -359,8 +367,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT climate devices.""" async_add_entities([MqttClimate(hass, config, config_entry, discovery_data)]) @@ -372,22 +380,28 @@ class MqttClimate(MqttEntity, ClimateEntity): _entity_id_format = climate.ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_CLIMATE_ATTRIBUTES_BLOCKED - def __init__(self, hass, config, config_entry, discovery_data): - """Initialize the climate device.""" - self._topic = None - self._value_templates = None - self._command_templates = None - self._feature_preset_mode = False - self._optimistic_preset_mode = None + _command_templates: dict[str, Callable[[PublishPayloadType], PublishPayloadType]] + _value_templates: dict[str, Callable[[ReceivePayloadType], ReceivePayloadType]] + _feature_preset_mode: bool + _optimistic_preset_mode: bool + _topic: dict[str, Any] + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: + """Initialize the climate device.""" MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" self._attr_hvac_modes = config[CONF_MODE_LIST] self._attr_min_temp = config[CONF_TEMP_MIN] @@ -438,7 +452,7 @@ class MqttClimate(MqttEntity, ClimateEntity): self._attr_is_aux_heat = False - value_templates = {} + value_templates: dict[str, Template | None] = {} for key in VALUE_TEMPLATE_KEYS: value_templates[key] = None if CONF_VALUE_TEMPLATE in config: @@ -455,14 +469,12 @@ class MqttClimate(MqttEntity, ClimateEntity): for key, template in value_templates.items() } - command_templates = {} + self._command_templates = {} for key in COMMAND_TEMPLATE_KEYS: - command_templates[key] = MqttCommandTemplate( + self._command_templates[key] = MqttCommandTemplate( config.get(key), entity=self ).async_render - self._command_templates = command_templates - support: int = 0 if (self._topic[CONF_TEMP_STATE_TOPIC] is not None) or ( self._topic[CONF_TEMP_COMMAND_TOPIC] is not None @@ -498,12 +510,16 @@ class MqttClimate(MqttEntity, ClimateEntity): support |= ClimateEntityFeature.AUX_HEAT self._attr_supported_features = support - def _prepare_subscribe_topics(self): # noqa: C901 + def _prepare_subscribe_topics(self) -> None: # noqa: C901 """(Re)Subscribe to topics.""" - topics = {} - qos = self._config[CONF_QOS] + topics: dict[str, dict[str, Any]] = {} + qos: int = self._config[CONF_QOS] - def add_subscription(topics, topic, msg_callback): + def add_subscription( + topics: dict[str, dict[str, Any]], + topic: str, + msg_callback: Callable[[ReceiveMessage], None], + ) -> None: if self._topic[topic] is not None: topics[topic] = { "topic": self._topic[topic], @@ -512,13 +528,15 @@ class MqttClimate(MqttEntity, ClimateEntity): "encoding": self._config[CONF_ENCODING] or None, } - def render_template(msg, template_name): + def render_template( + msg: ReceiveMessage, template_name: str + ) -> ReceivePayloadType: template = self._value_templates[template_name] return template(msg.payload) @callback @log_messages(self.hass, self.entity_id) - def handle_action_received(msg): + def handle_action_received(msg: ReceiveMessage) -> None: """Handle receiving action via MQTT.""" payload = render_template(msg, CONF_ACTION_TEMPLATE) if not payload or payload == PAYLOAD_NONE: @@ -529,7 +547,7 @@ class MqttClimate(MqttEntity, ClimateEntity): ) return try: - self._attr_hvac_action = HVACAction(payload) + self._attr_hvac_action = HVACAction(str(payload)) except ValueError: _LOGGER.warning( "Invalid %s action: %s", @@ -542,7 +560,9 @@ class MqttClimate(MqttEntity, ClimateEntity): add_subscription(topics, CONF_ACTION_TOPIC, handle_action_received) @callback - def handle_temperature_received(msg, template_name, attr): + def handle_temperature_received( + msg: ReceiveMessage, template_name: str, attr: str + ) -> None: """Handle temperature coming via MQTT.""" payload = render_template(msg, template_name) @@ -554,7 +574,7 @@ class MqttClimate(MqttEntity, ClimateEntity): @callback @log_messages(self.hass, self.entity_id) - def handle_current_temperature_received(msg): + def handle_current_temperature_received(msg: ReceiveMessage) -> None: """Handle current temperature coming via MQTT.""" handle_temperature_received( msg, CONF_CURRENT_TEMP_TEMPLATE, "_attr_current_temperature" @@ -566,7 +586,7 @@ class MqttClimate(MqttEntity, ClimateEntity): @callback @log_messages(self.hass, self.entity_id) - def handle_target_temperature_received(msg): + def handle_target_temperature_received(msg: ReceiveMessage) -> None: """Handle target temperature coming via MQTT.""" handle_temperature_received( msg, CONF_TEMP_STATE_TEMPLATE, "_attr_target_temperature" @@ -578,7 +598,7 @@ class MqttClimate(MqttEntity, ClimateEntity): @callback @log_messages(self.hass, self.entity_id) - def handle_temperature_low_received(msg): + def handle_temperature_low_received(msg: ReceiveMessage) -> None: """Handle target temperature low coming via MQTT.""" handle_temperature_received( msg, CONF_TEMP_LOW_STATE_TEMPLATE, "_attr_target_temperature_low" @@ -590,7 +610,7 @@ class MqttClimate(MqttEntity, ClimateEntity): @callback @log_messages(self.hass, self.entity_id) - def handle_temperature_high_received(msg): + def handle_temperature_high_received(msg: ReceiveMessage) -> None: """Handle target temperature high coming via MQTT.""" handle_temperature_received( msg, CONF_TEMP_HIGH_STATE_TEMPLATE, "_attr_target_temperature_high" @@ -601,7 +621,9 @@ class MqttClimate(MqttEntity, ClimateEntity): ) @callback - def handle_mode_received(msg, template_name, attr, mode_list): + def handle_mode_received( + msg: ReceiveMessage, template_name: str, attr: str, mode_list: str + ) -> None: """Handle receiving listed mode via MQTT.""" payload = render_template(msg, template_name) @@ -613,7 +635,7 @@ class MqttClimate(MqttEntity, ClimateEntity): @callback @log_messages(self.hass, self.entity_id) - def handle_current_mode_received(msg): + def handle_current_mode_received(msg: ReceiveMessage) -> None: """Handle receiving mode via MQTT.""" handle_mode_received( msg, CONF_MODE_STATE_TEMPLATE, "_attr_hvac_mode", CONF_MODE_LIST @@ -623,7 +645,7 @@ class MqttClimate(MqttEntity, ClimateEntity): @callback @log_messages(self.hass, self.entity_id) - def handle_fan_mode_received(msg): + def handle_fan_mode_received(msg: ReceiveMessage) -> None: """Handle receiving fan mode via MQTT.""" handle_mode_received( msg, @@ -636,7 +658,7 @@ class MqttClimate(MqttEntity, ClimateEntity): @callback @log_messages(self.hass, self.entity_id) - def handle_swing_mode_received(msg): + def handle_swing_mode_received(msg: ReceiveMessage) -> None: """Handle receiving swing mode via MQTT.""" handle_mode_received( msg, @@ -650,11 +672,13 @@ class MqttClimate(MqttEntity, ClimateEntity): ) @callback - def handle_onoff_mode_received(msg, template_name, attr): + def handle_onoff_mode_received( + msg: ReceiveMessage, template_name: str, attr: str + ) -> None: """Handle receiving on/off mode via MQTT.""" payload = render_template(msg, template_name) - payload_on = self._config[CONF_PAYLOAD_ON] - payload_off = self._config[CONF_PAYLOAD_OFF] + payload_on: str = self._config[CONF_PAYLOAD_ON] + payload_off: str = self._config[CONF_PAYLOAD_OFF] if payload == "True": payload = payload_on @@ -672,7 +696,7 @@ class MqttClimate(MqttEntity, ClimateEntity): @callback @log_messages(self.hass, self.entity_id) - def handle_aux_mode_received(msg): + def handle_aux_mode_received(msg: ReceiveMessage) -> None: """Handle receiving aux mode via MQTT.""" handle_onoff_mode_received( msg, CONF_AUX_STATE_TEMPLATE, "_attr_is_aux_heat" @@ -682,7 +706,7 @@ class MqttClimate(MqttEntity, ClimateEntity): @callback @log_messages(self.hass, self.entity_id) - def handle_preset_mode_received(msg): + def handle_preset_mode_received(msg: ReceiveMessage) -> None: """Handle receiving preset mode via MQTT.""" preset_mode = render_template(msg, CONF_PRESET_MODE_VALUE_TEMPLATE) if preset_mode in [PRESET_NONE, PAYLOAD_NONE]: @@ -692,7 +716,7 @@ class MqttClimate(MqttEntity, ClimateEntity): if not preset_mode: _LOGGER.debug("Ignoring empty preset_mode from '%s'", msg.topic) return - if preset_mode not in self.preset_modes: + if not self.preset_modes or preset_mode not in self.preset_modes: _LOGGER.warning( "'%s' received on topic %s. '%s' is not a valid preset mode", msg.payload, @@ -700,7 +724,7 @@ class MqttClimate(MqttEntity, ClimateEntity): preset_mode, ) else: - self._attr_preset_mode = preset_mode + self._attr_preset_mode = str(preset_mode) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) @@ -712,11 +736,11 @@ class MqttClimate(MqttEntity, ClimateEntity): self.hass, self._sub_state, topics ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) - async def _publish(self, topic, payload): + async def _publish(self, topic: str, payload: PublishPayloadType) -> None: if self._topic[topic] is not None: await self.async_publish( self._topic[topic], @@ -727,8 +751,13 @@ class MqttClimate(MqttEntity, ClimateEntity): ) async def _set_temperature( - self, temp, cmnd_topic, cmnd_template, state_topic, attr - ): + self, + temp: float | None, + cmnd_topic: str, + cmnd_template: str, + state_topic: str, + attr: str, + ) -> None: if temp is not None: if self._topic[state_topic] is None: # optimistic mode @@ -822,7 +851,7 @@ class MqttClimate(MqttEntity, ClimateEntity): return - async def _set_aux_heat(self, state): + async def _set_aux_heat(self, state: bool) -> None: await self._publish( CONF_AUX_COMMAND_TOPIC, self._config[CONF_PAYLOAD_ON] if state else self._config[CONF_PAYLOAD_OFF], From 8c0a7b9d7fb98084e72c3fc00bab9704019f90f2 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 8 Nov 2022 09:24:16 +0100 Subject: [PATCH 0280/1033] Add type hints for MQTT tag (#81495) * Improve typing tag * Additional type hints for tag platform * Follow up comment tag * Folow up comments * Revert comment removal --- homeassistant/components/mqtt/tag.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mqtt/tag.py b/homeassistant/components/mqtt/tag.py index 23afae35cc9..26cb66e9f2f 100644 --- a/homeassistant/components/mqtt/tag.py +++ b/homeassistant/components/mqtt/tag.py @@ -1,6 +1,7 @@ """Provides tag scanning for MQTT.""" from __future__ import annotations +from collections.abc import Callable import functools import voluptuous as vol @@ -9,11 +10,12 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_DEVICE, CONF_PLATFORM, CONF_VALUE_TEMPLATE from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import subscription from .config import MQTT_BASE_SCHEMA from .const import ATTR_DISCOVERY_HASH, CONF_QOS, CONF_TOPIC +from .discovery import MQTTDiscoveryPayload from .mixins import ( MQTT_ENTITY_DEVICE_INFO_SCHEMA, MqttDiscoveryDeviceUpdate, @@ -21,7 +23,7 @@ from .mixins import ( send_discovery_done, update_device, ) -from .models import MqttValueTemplate, ReceiveMessage +from .models import MqttValueTemplate, ReceiveMessage, ReceivePayloadType from .subscription import EntitySubscription from .util import get_mqtt_data, valid_subscribe_topic @@ -87,12 +89,14 @@ def async_has_tags(hass: HomeAssistant, device_id: str) -> bool: class MQTTTagScanner(MqttDiscoveryDeviceUpdate): """MQTT Tag scanner.""" + _value_template: Callable[[ReceivePayloadType, str], ReceivePayloadType] + def __init__( self, hass: HomeAssistant, config: ConfigType, device_id: str | None, - discovery_data: dict, + discovery_data: DiscoveryInfoType, config_entry: ConfigEntry, ) -> None: """Initialize.""" @@ -111,10 +115,10 @@ class MQTTTagScanner(MqttDiscoveryDeviceUpdate): self, hass, discovery_data, device_id, config_entry, LOG_NAME ) - async def async_update(self, discovery_data: dict) -> None: + async def async_update(self, discovery_data: MQTTDiscoveryPayload) -> None: """Handle MQTT tag discovery updates.""" # Update tag scanner - config = PLATFORM_SCHEMA(discovery_data) + config: DiscoveryInfoType = PLATFORM_SCHEMA(discovery_data) self._config = config self._value_template = MqttValueTemplate( config.get(CONF_VALUE_TEMPLATE), @@ -127,7 +131,7 @@ class MQTTTagScanner(MqttDiscoveryDeviceUpdate): """Subscribe to MQTT topics.""" async def tag_scanned(msg: ReceiveMessage) -> None: - tag_id = self._value_template(msg.payload, "").strip() + tag_id = str(self._value_template(msg.payload, "")).strip() if not tag_id: # No output from template, ignore return From d66d079330b92c02c38fb1c9dca539617161fdbc Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 8 Nov 2022 10:16:05 +0100 Subject: [PATCH 0281/1033] Use `_attr_` for MQTT light (#81465) * Schema basic * Schema json * Schema template * add color_mode - follow up comments * Fix regression * Follow up comments 2 * Fix mypy errors * Update homeassistant/components/mqtt/light/schema_template.py Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- .../components/mqtt/light/schema_basic.py | 202 +++++---------- .../components/mqtt/light/schema_json.py | 236 +++++++----------- .../components/mqtt/light/schema_template.py | 144 ++++------- 3 files changed, 195 insertions(+), 387 deletions(-) diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index bf2ae33ca1d..19d059ae8cc 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -1,5 +1,6 @@ """Support for MQTT lights.""" import logging +from typing import cast import voluptuous as vol @@ -256,18 +257,6 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): def __init__(self, hass, config, config_entry, discovery_data): """Initialize MQTT light.""" - self._brightness = None - self._color_mode = None - self._color_temp = None - self._effect = None - self._hs_color = None - self._rgb_color = None - self._rgbw_color = None - self._rgbww_color = None - self._state = None - self._supported_color_modes = None - self._xy_color = None - self._topic = None self._payload = None self._command_templates = None @@ -292,6 +281,10 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): def _setup_from_config(self, config): """(Re)Setup the entity.""" + self._attr_min_mireds = config.get(CONF_MIN_MIREDS, super().min_mireds) + self._attr_max_mireds = config.get(CONF_MAX_MIREDS, super().max_mireds) + self._attr_effect_list = config.get(CONF_EFFECT_LIST) + if CONF_STATE_VALUE_TEMPLATE not in config and CONF_VALUE_TEMPLATE in config: config[CONF_STATE_VALUE_TEMPLATE] = config[CONF_VALUE_TEMPLATE] @@ -378,39 +371,47 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): supported_color_modes = set() if topic[CONF_COLOR_TEMP_COMMAND_TOPIC] is not None: supported_color_modes.add(ColorMode.COLOR_TEMP) - self._color_mode = ColorMode.COLOR_TEMP + self._attr_color_mode = ColorMode.COLOR_TEMP if topic[CONF_HS_COMMAND_TOPIC] is not None: supported_color_modes.add(ColorMode.HS) - self._color_mode = ColorMode.HS + self._attr_color_mode = ColorMode.HS if topic[CONF_RGB_COMMAND_TOPIC] is not None: supported_color_modes.add(ColorMode.RGB) - self._color_mode = ColorMode.RGB + self._attr_color_mode = ColorMode.RGB if topic[CONF_RGBW_COMMAND_TOPIC] is not None: supported_color_modes.add(ColorMode.RGBW) - self._color_mode = ColorMode.RGBW + self._attr_color_mode = ColorMode.RGBW if topic[CONF_RGBWW_COMMAND_TOPIC] is not None: supported_color_modes.add(ColorMode.RGBWW) - self._color_mode = ColorMode.RGBWW + self._attr_color_mode = ColorMode.RGBWW if topic[CONF_WHITE_COMMAND_TOPIC] is not None: supported_color_modes.add(ColorMode.WHITE) if topic[CONF_XY_COMMAND_TOPIC] is not None: supported_color_modes.add(ColorMode.XY) - self._color_mode = ColorMode.XY + self._attr_color_mode = ColorMode.XY if len(supported_color_modes) > 1: - self._color_mode = ColorMode.UNKNOWN + self._attr_color_mode = ColorMode.UNKNOWN if not supported_color_modes: if topic[CONF_BRIGHTNESS_COMMAND_TOPIC] is not None: - self._color_mode = ColorMode.BRIGHTNESS + self._attr_color_mode = ColorMode.BRIGHTNESS supported_color_modes.add(ColorMode.BRIGHTNESS) else: - self._color_mode = ColorMode.ONOFF + self._attr_color_mode = ColorMode.ONOFF supported_color_modes.add(ColorMode.ONOFF) # Validate the color_modes configuration - self._supported_color_modes = valid_supported_color_modes(supported_color_modes) + self._attr_supported_color_modes = valid_supported_color_modes( + supported_color_modes + ) - def _is_optimistic(self, attribute): + supported_features: int = 0 + supported_features |= ( + topic[CONF_EFFECT_COMMAND_TOPIC] is not None and LightEntityFeature.EFFECT + ) + self._attr_supported_features = supported_features + + def _is_optimistic(self, attribute: str) -> bool: """Return True if the attribute is optimistically updated.""" return getattr(self, f"_optimistic_{attribute}") @@ -438,11 +439,11 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): return if payload == self._payload["on"]: - self._state = True + self._attr_is_on = True elif payload == self._payload["off"]: - self._state = False + self._attr_is_on = False elif payload == PAYLOAD_NONE: - self._state = None + self._attr_is_on = None get_mqtt_data(self.hass).state_write_requests.write_state_request(self) if self._topic[CONF_STATE_TOPIC] is not None: @@ -466,7 +467,8 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): device_value = float(payload) percent_bright = device_value / self._config[CONF_BRIGHTNESS_SCALE] - self._brightness = percent_bright * 255 + self._attr_brightness = min(round(percent_bright * 255), 255) + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_BRIGHTNESS_STATE_TOPIC, brightness_received) @@ -483,11 +485,11 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): return None color = tuple(int(val) for val in payload.split(",")) if self._optimistic_color_mode: - self._color_mode = color_mode + self._attr_color_mode = color_mode if self._topic[CONF_BRIGHTNESS_STATE_TOPIC] is None: rgb = convert_color(*color) percent_bright = float(color_util.color_RGB_to_hsv(*rgb)[2]) / 100.0 - self._brightness = percent_bright * 255 + self._attr_brightness = min(round(percent_bright * 255), 255) return color @callback @@ -499,7 +501,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): ) if not rgb: return - self._rgb_color = rgb + self._attr_rgb_color = rgb get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_RGB_STATE_TOPIC, rgb_received) @@ -516,7 +518,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): ) if not rgbw: return - self._rgbw_color = rgbw + self._attr_rgbw_color = rgbw get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_RGBW_STATE_TOPIC, rgbw_received) @@ -533,7 +535,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): ) if not rgbww: return - self._rgbww_color = rgbww + self._attr_rgbww_color = rgbww get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_RGBWW_STATE_TOPIC, rgbww_received) @@ -549,7 +551,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): _LOGGER.debug("Ignoring empty color mode message from '%s'", msg.topic) return - self._color_mode = payload + self._attr_color_mode = payload get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_COLOR_MODE_STATE_TOPIC, color_mode_received) @@ -566,8 +568,8 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): return if self._optimistic_color_mode: - self._color_mode = ColorMode.COLOR_TEMP - self._color_temp = int(payload) + self._attr_color_mode = ColorMode.COLOR_TEMP + self._attr_color_temp = int(payload) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_COLOR_TEMP_STATE_TOPIC, color_temp_received) @@ -583,7 +585,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): _LOGGER.debug("Ignoring empty effect message from '%s'", msg.topic) return - self._effect = payload + self._attr_effect = payload get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_EFFECT_STATE_TOPIC, effect_received) @@ -599,10 +601,13 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): _LOGGER.debug("Ignoring empty hs message from '%s'", msg.topic) return try: - hs_color = tuple(float(val) for val in payload.split(",", 2)) + hs_color = cast( + tuple[float, float], + tuple(float(val) for val in payload.split(",", 2)), + ) if self._optimistic_color_mode: - self._color_mode = ColorMode.HS - self._hs_color = hs_color + self._attr_color_mode = ColorMode.HS + self._attr_hs_color = hs_color get_mqtt_data(self.hass).state_write_requests.write_state_request(self) except ValueError: _LOGGER.debug("Failed to parse hs state update: '%s'", payload) @@ -620,10 +625,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): _LOGGER.debug("Ignoring empty xy-color message from '%s'", msg.topic) return - xy_color = tuple(float(val) for val in payload.split(",")) + xy_color = cast( + tuple[float, float], tuple(float(val) for val in payload.split(",", 2)) + ) if self._optimistic_color_mode: - self._color_mode = ColorMode.XY - self._xy_color = xy_color + self._attr_color_mode = ColorMode.XY + self._attr_xy_color = xy_color get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_XY_STATE_TOPIC, xy_received) @@ -643,10 +650,10 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): condition_attribute = attribute optimistic = self._is_optimistic(condition_attribute) if optimistic and last_state and last_state.attributes.get(attribute): - setattr(self, f"_{attribute}", last_state.attributes[attribute]) + setattr(self, f"_attr_{attribute}", last_state.attributes[attribute]) if self._topic[CONF_STATE_TOPIC] is None and self._optimistic and last_state: - self._state = last_state.state == STATE_ON + self._attr_is_on = last_state.state == STATE_ON restore_state(ATTR_BRIGHTNESS) restore_state(ATTR_RGB_COLOR) restore_state(ATTR_HS_COLOR, ATTR_RGB_COLOR) @@ -659,93 +666,11 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): restore_state(ATTR_XY_COLOR) restore_state(ATTR_HS_COLOR, ATTR_XY_COLOR) - @property - def brightness(self): - """Return the brightness of this light between 0..255.""" - if brightness := self._brightness: - brightness = min(round(brightness), 255) - return brightness - - @property - def color_mode(self): - """Return current color mode.""" - return self._color_mode - - @property - def hs_color(self): - """Return the hs color value.""" - return self._hs_color - - @property - def rgb_color(self): - """Return the rgb color value.""" - return self._rgb_color - - @property - def rgbw_color(self): - """Return the rgbw color value.""" - return self._rgbw_color - - @property - def rgbww_color(self): - """Return the rgbww color value.""" - return self._rgbww_color - - @property - def xy_color(self): - """Return the xy color value.""" - return self._xy_color - - @property - def color_temp(self): - """Return the color temperature in mired.""" - return self._color_temp - - @property - def min_mireds(self): - """Return the coldest color_temp that this light supports.""" - return self._config.get(CONF_MIN_MIREDS, super().min_mireds) - - @property - def max_mireds(self): - """Return the warmest color_temp that this light supports.""" - return self._config.get(CONF_MAX_MIREDS, super().max_mireds) - - @property - def is_on(self): - """Return true if device is on.""" - return self._state - @property def assumed_state(self): """Return true if we do optimistic updates.""" return self._optimistic - @property - def effect_list(self): - """Return the list of supported effects.""" - return self._config.get(CONF_EFFECT_LIST) - - @property - def effect(self): - """Return the current effect.""" - return self._effect - - @property - def supported_color_modes(self): - """Flag supported color modes.""" - return self._supported_color_modes - - @property - def supported_features(self): - """Flag supported features.""" - supported_features = 0 - supported_features |= ( - self._topic[CONF_EFFECT_COMMAND_TOPIC] is not None - and LightEntityFeature.EFFECT - ) - return supported_features - async def async_turn_on(self, **kwargs): # noqa: C901 """Turn the device on. @@ -772,9 +697,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): if self._topic[CONF_BRIGHTNESS_COMMAND_TOPIC] is not None: brightness = 255 else: - brightness = kwargs.get( - ATTR_BRIGHTNESS, self._brightness if self._brightness else 255 - ) + brightness = kwargs.get(ATTR_BRIGHTNESS, self.brightness or 255) return tuple(int(channel * brightness / 255) for channel in color) def render_rgbx(color, template, color_mode): @@ -797,8 +720,9 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): if not self._is_optimistic(condition_attribute): return False if color_mode and self._optimistic_color_mode: - self._color_mode = color_mode - setattr(self, f"_{attribute}", value) + self._attr_color_mode = color_mode + + setattr(self, f"_attr_{attribute}", value) return True if on_command_type == "first": @@ -813,7 +737,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): and ATTR_BRIGHTNESS not in kwargs and ATTR_WHITE not in kwargs ): - kwargs[ATTR_BRIGHTNESS] = self._brightness if self._brightness else 255 + kwargs[ATTR_BRIGHTNESS] = self.brightness or 255 hs_color = kwargs.get(ATTR_HS_COLOR) @@ -871,7 +795,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): and ATTR_RGB_COLOR not in kwargs and self._topic[CONF_RGB_COMMAND_TOPIC] is not None ): - rgb_color = self._rgb_color if self._rgb_color is not None else (255,) * 3 + rgb_color = self.rgb_color or (255,) * 3 rgb = scale_rgbx(rgb_color, kwargs[ATTR_BRIGHTNESS]) rgb_s = render_rgbx(rgb, CONF_RGB_COMMAND_TEMPLATE, ColorMode.RGB) await publish(CONF_RGB_COMMAND_TOPIC, rgb_s) @@ -881,9 +805,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): and ATTR_RGBW_COLOR not in kwargs and self._topic[CONF_RGBW_COMMAND_TOPIC] is not None ): - rgbw_color = ( - self._rgbw_color if self._rgbw_color is not None else (255,) * 4 - ) + rgbw_color = self.rgbw_color or (255,) * 4 rgbw = scale_rgbx(rgbw_color, kwargs[ATTR_BRIGHTNESS]) rgbw_s = render_rgbx(rgbw, CONF_RGBW_COMMAND_TEMPLATE, ColorMode.RGBW) await publish(CONF_RGBW_COMMAND_TOPIC, rgbw_s) @@ -893,9 +815,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): and ATTR_RGBWW_COLOR not in kwargs and self._topic[CONF_RGBWW_COMMAND_TOPIC] is not None ): - rgbww_color = ( - self._rgbww_color if self._rgbww_color is not None else (255,) * 5 - ) + rgbww_color = self.rgbww_color or (255,) * 5 rgbww = scale_rgbx(rgbww_color, kwargs[ATTR_BRIGHTNESS]) rgbww_s = render_rgbx(rgbww, CONF_RGBWW_COMMAND_TEMPLATE, ColorMode.RGBWW) await publish(CONF_RGBWW_COMMAND_TOPIC, rgbww_s) @@ -938,7 +858,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): if self._optimistic: # Optimistically assume that the light has changed state. - self._state = True + self._attr_is_on = True should_update = True if should_update: @@ -959,5 +879,5 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): if self._optimistic: # Optimistically assume that the light has changed state. - self._state = False + self._attr_is_on = False self.async_write_ha_state() diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index a4a76673176..b5824e5e456 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -188,22 +188,10 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): def __init__(self, hass, config, config_entry, discovery_data): """Initialize MQTT JSON light.""" - self._state = None - self._supported_features = 0 - self._topic = None self._optimistic = False - self._brightness = None - self._color_mode = None - self._color_temp = None - self._effect = None self._fixed_color_mode = None self._flash_times = None - self._hs = None - self._rgb = None - self._rgbw = None - self._rgbww = None - self._xy = None MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @@ -214,6 +202,10 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): def _setup_from_config(self, config): """(Re)Setup the entity.""" + self._attr_max_mireds = config.get(CONF_MAX_MIREDS, super().max_mireds) + self._attr_min_mireds = config.get(CONF_MIN_MIREDS, super().min_mireds) + self._attr_effect_list = config.get(CONF_EFFECT_LIST) + self._topic = { key: config.get(key) for key in (CONF_STATE_TOPIC, CONF_COMMAND_TOPIC) } @@ -225,10 +217,12 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): for key in (CONF_FLASH_TIME_SHORT, CONF_FLASH_TIME_LONG) } - self._supported_features = ( + self._attr_supported_features = ( LightEntityFeature.TRANSITION | LightEntityFeature.FLASH ) - self._supported_features |= config[CONF_EFFECT] and LightEntityFeature.EFFECT + self._attr_supported_features |= ( + config[CONF_EFFECT] and LightEntityFeature.EFFECT + ) if not self._config[CONF_COLOR_MODE]: color_modes = {ColorMode.ONOFF} if config[CONF_BRIGHTNESS]: @@ -237,13 +231,13 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): color_modes.add(ColorMode.COLOR_TEMP) if config[CONF_HS] or config[CONF_RGB] or config[CONF_XY]: color_modes.add(ColorMode.HS) - self._supported_color_modes = filter_supported_color_modes(color_modes) - if len(self._supported_color_modes) == 1: - self._fixed_color_mode = next(iter(self._supported_color_modes)) + self._attr_supported_color_modes = filter_supported_color_modes(color_modes) + if len(self.supported_color_modes) == 1: + self._fixed_color_mode = next(iter(self.supported_color_modes)) else: - self._supported_color_modes = self._config[CONF_SUPPORTED_COLOR_MODES] - if len(self._supported_color_modes) == 1: - self._color_mode = next(iter(self._supported_color_modes)) + self._attr_supported_color_modes = self._config[CONF_SUPPORTED_COLOR_MODES] + if len(self.supported_color_modes) == 1: + self._attr_color_mode = next(iter(self.supported_color_modes)) def _update_color(self, values): if not self._config[CONF_COLOR_MODE]: @@ -252,7 +246,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): red = int(values["color"]["r"]) green = int(values["color"]["g"]) blue = int(values["color"]["b"]) - self._hs = color_util.color_RGB_to_hs(red, green, blue) + self._attr_hs_color = color_util.color_RGB_to_hs(red, green, blue) except KeyError: pass except ValueError: @@ -264,7 +258,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): try: x_color = float(values["color"]["x"]) y_color = float(values["color"]["y"]) - self._hs = color_util.color_xy_to_hs(x_color, y_color) + self._attr_hs_color = color_util.color_xy_to_hs(x_color, y_color) except KeyError: pass except ValueError: @@ -276,7 +270,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): try: hue = float(values["color"]["h"]) saturation = float(values["color"]["s"]) - self._hs = (hue, saturation) + self._attr_hs_color = (hue, saturation) except KeyError: pass except ValueError: @@ -293,41 +287,41 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): return try: if color_mode == ColorMode.COLOR_TEMP: - self._color_temp = int(values["color_temp"]) - self._color_mode = ColorMode.COLOR_TEMP + self._attr_color_temp = int(values["color_temp"]) + self._attr_color_mode = ColorMode.COLOR_TEMP elif color_mode == ColorMode.HS: hue = float(values["color"]["h"]) saturation = float(values["color"]["s"]) - self._color_mode = ColorMode.HS - self._hs = (hue, saturation) + self._attr_color_mode = ColorMode.HS + self._attr_hs_color = (hue, saturation) elif color_mode == ColorMode.RGB: r = int(values["color"]["r"]) # pylint: disable=invalid-name g = int(values["color"]["g"]) # pylint: disable=invalid-name b = int(values["color"]["b"]) # pylint: disable=invalid-name - self._color_mode = ColorMode.RGB - self._rgb = (r, g, b) + self._attr_color_mode = ColorMode.RGB + self._attr_rgb_color = (r, g, b) elif color_mode == ColorMode.RGBW: r = int(values["color"]["r"]) # pylint: disable=invalid-name g = int(values["color"]["g"]) # pylint: disable=invalid-name b = int(values["color"]["b"]) # pylint: disable=invalid-name w = int(values["color"]["w"]) # pylint: disable=invalid-name - self._color_mode = ColorMode.RGBW - self._rgbw = (r, g, b, w) + self._attr_color_mode = ColorMode.RGBW + self._attr_rgbw_color = (r, g, b, w) elif color_mode == ColorMode.RGBWW: r = int(values["color"]["r"]) # pylint: disable=invalid-name g = int(values["color"]["g"]) # pylint: disable=invalid-name b = int(values["color"]["b"]) # pylint: disable=invalid-name c = int(values["color"]["c"]) # pylint: disable=invalid-name w = int(values["color"]["w"]) # pylint: disable=invalid-name - self._color_mode = ColorMode.RGBWW - self._rgbww = (r, g, b, c, w) + self._attr_color_mode = ColorMode.RGBWW + self._attr_rgbww_color = (r, g, b, c, w) elif color_mode == ColorMode.WHITE: - self._color_mode = ColorMode.WHITE + self._attr_color_mode = ColorMode.WHITE elif color_mode == ColorMode.XY: x = float(values["color"]["x"]) # pylint: disable=invalid-name y = float(values["color"]["y"]) # pylint: disable=invalid-name - self._color_mode = ColorMode.XY - self._xy = (x, y) + self._attr_color_mode = ColorMode.XY + self._attr_xy_color = (x, y) except (KeyError, ValueError): _LOGGER.warning( "Invalid or incomplete color value received for entity %s", @@ -344,29 +338,29 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): values = json_loads(msg.payload) if values["state"] == "ON": - self._state = True + self._attr_is_on = True elif values["state"] == "OFF": - self._state = False + self._attr_is_on = False elif values["state"] is None: - self._state = None + self._attr_is_on = None if ( not self._config[CONF_COLOR_MODE] - and color_supported(self._supported_color_modes) + and color_supported(self.supported_color_modes) and "color" in values ): # Deprecated color handling if values["color"] is None: - self._hs = None + self._attr_hs_color = None else: self._update_color(values) if self._config[CONF_COLOR_MODE] and "color_mode" in values: self._update_color(values) - if brightness_supported(self._supported_color_modes): + if brightness_supported(self.supported_color_modes): try: - self._brightness = int( + self._attr_brightness = int( values["brightness"] / float(self._config[CONF_BRIGHTNESS_SCALE]) * 255 @@ -380,15 +374,15 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): ) if ( - ColorMode.COLOR_TEMP in self._supported_color_modes + ColorMode.COLOR_TEMP in self.supported_color_modes and not self._config[CONF_COLOR_MODE] ): # Deprecated color handling try: if values["color_temp"] is None: - self._color_temp = None + self._attr_color_temp = None else: - self._color_temp = int(values["color_temp"]) + self._attr_color_temp = int(values["color_temp"]) except KeyError: pass except ValueError: @@ -397,9 +391,9 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): self.entity_id, ) - if self._supported_features and LightEntityFeature.EFFECT: + if self.supported_features and LightEntityFeature.EFFECT: with suppress(KeyError): - self._effect = values["effect"] + self._attr_effect = values["effect"] get_mqtt_data(self.hass).state_write_requests.write_state_request(self) @@ -423,77 +417,27 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): last_state = await self.async_get_last_state() if self._optimistic and last_state: - self._state = last_state.state == STATE_ON + self._attr_is_on = last_state.state == STATE_ON last_attributes = last_state.attributes - self._brightness = last_attributes.get(ATTR_BRIGHTNESS, self._brightness) - self._color_mode = last_attributes.get(ATTR_COLOR_MODE, self._color_mode) - self._color_temp = last_attributes.get(ATTR_COLOR_TEMP, self._color_temp) - self._effect = last_attributes.get(ATTR_EFFECT, self._effect) - self._hs = last_attributes.get(ATTR_HS_COLOR, self._hs) - self._rgb = last_attributes.get(ATTR_RGB_COLOR, self._rgb) - self._rgbw = last_attributes.get(ATTR_RGBW_COLOR, self._rgbw) - self._rgbww = last_attributes.get(ATTR_RGBWW_COLOR, self._rgbww) - self._xy = last_attributes.get(ATTR_XY_COLOR, self._xy) - - @property - def brightness(self): - """Return the brightness of this light between 0..255.""" - return self._brightness - - @property - def color_temp(self): - """Return the color temperature in mired.""" - return self._color_temp - - @property - def min_mireds(self): - """Return the coldest color_temp that this light supports.""" - return self._config.get(CONF_MIN_MIREDS, super().min_mireds) - - @property - def max_mireds(self): - """Return the warmest color_temp that this light supports.""" - return self._config.get(CONF_MAX_MIREDS, super().max_mireds) - - @property - def effect(self): - """Return the current effect.""" - return self._effect - - @property - def effect_list(self): - """Return the list of supported effects.""" - return self._config.get(CONF_EFFECT_LIST) - - @property - def hs_color(self): - """Return the hs color value.""" - return self._hs - - @property - def rgb_color(self): - """Return the hs color value.""" - return self._rgb - - @property - def rgbw_color(self): - """Return the hs color value.""" - return self._rgbw - - @property - def rgbww_color(self): - """Return the hs color value.""" - return self._rgbww - - @property - def xy_color(self): - """Return the hs color value.""" - return self._xy - - @property - def is_on(self): - """Return true if device is on.""" - return self._state + self._attr_brightness = last_attributes.get( + ATTR_BRIGHTNESS, self.brightness + ) + self._attr_color_mode = last_attributes.get( + ATTR_COLOR_MODE, self.color_mode + ) + self._attr_color_temp = last_attributes.get( + ATTR_COLOR_TEMP, self.color_temp + ) + self._attr_effect = last_attributes.get(ATTR_EFFECT, self.effect) + self._attr_hs_color = last_attributes.get(ATTR_HS_COLOR, self.hs_color) + self._attr_rgb_color = last_attributes.get(ATTR_RGB_COLOR, self.rgb_color) + self._attr_rgbw_color = last_attributes.get( + ATTR_RGBW_COLOR, self.rgbw_color + ) + self._attr_rgbww_color = last_attributes.get( + ATTR_RGBWW_COLOR, self.rgbww_color + ) + self._attr_xy_color = last_attributes.get(ATTR_XY_COLOR, self.xy_color) @property def assumed_state(self): @@ -504,25 +448,15 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): def color_mode(self): """Return current color mode.""" if self._config[CONF_COLOR_MODE]: - return self._color_mode + return self._attr_color_mode if self._fixed_color_mode: # Legacy light with support for a single color mode return self._fixed_color_mode # Legacy light with support for ct + hs, prioritize hs - if self._hs is not None: + if self.hs_color is not None: return ColorMode.HS return ColorMode.COLOR_TEMP - @property - def supported_color_modes(self): - """Flag supported color modes.""" - return self._supported_color_modes - - @property - def supported_features(self): - """Flag supported features.""" - return self._supported_features - def _set_flash_and_transition(self, message, **kwargs): if ATTR_TRANSITION in kwargs: message["transition"] = kwargs[ATTR_TRANSITION] @@ -587,32 +521,32 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): message["color"]["s"] = hs_color[1] if self._optimistic: - self._color_temp = None - self._hs = kwargs[ATTR_HS_COLOR] + self._attr_color_temp = None + self._attr_hs_color = kwargs[ATTR_HS_COLOR] should_update = True if ATTR_HS_COLOR in kwargs and self._supports_color_mode(ColorMode.HS): hs_color = kwargs[ATTR_HS_COLOR] message["color"] = {"h": hs_color[0], "s": hs_color[1]} if self._optimistic: - self._color_mode = ColorMode.HS - self._hs = hs_color + self._attr_color_mode = ColorMode.HS + self._attr_hs_color = hs_color should_update = True if ATTR_RGB_COLOR in kwargs and self._supports_color_mode(ColorMode.RGB): rgb = self._scale_rgbxx(kwargs[ATTR_RGB_COLOR], kwargs) message["color"] = {"r": rgb[0], "g": rgb[1], "b": rgb[2]} if self._optimistic: - self._color_mode = ColorMode.RGB - self._rgb = rgb + self._attr_color_mode = ColorMode.RGB + self._attr_rgb_color = rgb should_update = True if ATTR_RGBW_COLOR in kwargs and self._supports_color_mode(ColorMode.RGBW): rgb = self._scale_rgbxx(kwargs[ATTR_RGBW_COLOR], kwargs) message["color"] = {"r": rgb[0], "g": rgb[1], "b": rgb[2], "w": rgb[3]} if self._optimistic: - self._color_mode = ColorMode.RGBW - self._rgbw = rgb + self._attr_color_mode = ColorMode.RGBW + self._attr_rgbw_color = rgb should_update = True if ATTR_RGBWW_COLOR in kwargs and self._supports_color_mode(ColorMode.RGBWW): @@ -625,16 +559,16 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): "w": rgb[4], } if self._optimistic: - self._color_mode = ColorMode.RGBWW - self._rgbww = rgb + self._attr_color_mode = ColorMode.RGBWW + self._attr_rgbww_color = rgb should_update = True if ATTR_XY_COLOR in kwargs and self._supports_color_mode(ColorMode.XY): xy = kwargs[ATTR_XY_COLOR] # pylint: disable=invalid-name message["color"] = {"x": xy[0], "y": xy[1]} if self._optimistic: - self._color_mode = ColorMode.XY - self._xy = xy + self._attr_color_mode = ColorMode.XY + self._attr_xy_color = xy should_update = True self._set_flash_and_transition(message, **kwargs) @@ -650,23 +584,23 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): message["brightness"] = device_brightness if self._optimistic: - self._brightness = kwargs[ATTR_BRIGHTNESS] + self._attr_brightness = kwargs[ATTR_BRIGHTNESS] should_update = True if ATTR_COLOR_TEMP in kwargs: message["color_temp"] = int(kwargs[ATTR_COLOR_TEMP]) if self._optimistic: - self._color_mode = ColorMode.COLOR_TEMP - self._color_temp = kwargs[ATTR_COLOR_TEMP] - self._hs = None + self._attr_color_mode = ColorMode.COLOR_TEMP + self._attr_color_temp = kwargs[ATTR_COLOR_TEMP] + self._attr_hs_color = None should_update = True if ATTR_EFFECT in kwargs: message["effect"] = kwargs[ATTR_EFFECT] if self._optimistic: - self._effect = kwargs[ATTR_EFFECT] + self._attr_effect = kwargs[ATTR_EFFECT] should_update = True if ATTR_WHITE in kwargs and self._supports_color_mode(ColorMode.WHITE): @@ -678,8 +612,8 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): message["white"] = device_white_level if self._optimistic: - self._color_mode = ColorMode.WHITE - self._brightness = kwargs[ATTR_WHITE] + self._attr_color_mode = ColorMode.WHITE + self._attr_brightness = kwargs[ATTR_WHITE] should_update = True await self.async_publish( @@ -692,7 +626,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): if self._optimistic: # Optimistically assume that the light has changed state. - self._state = True + self._attr_is_on = True should_update = True if should_update: @@ -717,5 +651,5 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): if self._optimistic: # Optimistically assume that the light has changed state. - self._state = False + self._attr_is_on = False self.async_write_ha_state() diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 33c7f1cea1b..b57e09e0c1f 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -121,18 +121,12 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): def __init__(self, hass, config, config_entry, discovery_data): """Initialize a MQTT Template light.""" - self._state = None - self._topics = None self._templates = None self._optimistic = False # features - self._brightness = None self._fixed_color_mode = None - self._color_temp = None - self._hs = None - self._effect = None MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @@ -143,6 +137,10 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): def _setup_from_config(self, config): """(Re)Setup the entity.""" + self._attr_max_mireds = config.get(CONF_MAX_MIREDS, super().max_mireds) + self._attr_min_mireds = config.get(CONF_MIN_MIREDS, super().min_mireds) + self._attr_effect_list = config.get(CONF_EFFECT_LIST) + self._topics = { key: config.get(key) for key in (CONF_STATE_TOPIC, CONF_COMMAND_TOPIC) } @@ -178,9 +176,23 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): and self._templates[CONF_BLUE_TEMPLATE] is not None ): color_modes.add(ColorMode.HS) - self._supported_color_modes = filter_supported_color_modes(color_modes) - if len(self._supported_color_modes) == 1: - self._fixed_color_mode = next(iter(self._supported_color_modes)) + self._attr_supported_color_modes = filter_supported_color_modes(color_modes) + self._fixed_color_mode = None + if len(self.supported_color_modes) == 1: + self._fixed_color_mode = next(iter(self.supported_color_modes)) + self._attr_color_mode = self._fixed_color_mode + + features = LightEntityFeature.FLASH | LightEntityFeature.TRANSITION + if config.get(CONF_EFFECT_LIST) is not None: + features = features | LightEntityFeature.EFFECT + self._attr_supported_features = features + + def _update_color_mode(self): + """Update the color_mode attribute.""" + if self._fixed_color_mode: + return + # Support for ct + hs, prioritize hs + self._attr_color_mode = ColorMode.HS if self.hs_color else ColorMode.COLOR_TEMP def _prepare_subscribe_topics(self): """(Re)Subscribe to topics.""" @@ -196,17 +208,17 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): CONF_STATE_TEMPLATE ].async_render_with_possible_json_value(msg.payload) if state == STATE_ON: - self._state = True + self._attr_is_on = True elif state == STATE_OFF: - self._state = False + self._attr_is_on = False elif state == PAYLOAD_NONE: - self._state = None + self._attr_is_on = None else: _LOGGER.warning("Invalid state value received") if self._templates[CONF_BRIGHTNESS_TEMPLATE] is not None: try: - self._brightness = int( + self._attr_brightness = int( self._templates[ CONF_BRIGHTNESS_TEMPLATE ].async_render_with_possible_json_value(msg.payload) @@ -219,7 +231,9 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): color_temp = self._templates[ CONF_COLOR_TEMP_TEMPLATE ].async_render_with_possible_json_value(msg.payload) - self._color_temp = int(color_temp) if color_temp != "None" else None + self._attr_color_temp = ( + int(color_temp) if color_temp != "None" else None + ) except ValueError: _LOGGER.warning("Invalid color temperature value received") @@ -239,11 +253,12 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): CONF_BLUE_TEMPLATE ].async_render_with_possible_json_value(msg.payload) if red == "None" and green == "None" and blue == "None": - self._hs = None + self._attr_hs_color = None else: - self._hs = color_util.color_RGB_to_hs( + self._attr_hs_color = color_util.color_RGB_to_hs( int(red), int(green), int(blue) ) + self._update_color_mode() except ValueError: _LOGGER.warning("Invalid color value received") @@ -253,7 +268,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): ].async_render_with_possible_json_value(msg.payload) if effect in self._config.get(CONF_EFFECT_LIST): - self._effect = effect + self._attr_effect = effect else: _LOGGER.warning("Unsupported effect value received") @@ -279,61 +294,22 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): last_state = await self.async_get_last_state() if self._optimistic and last_state: - self._state = last_state.state == STATE_ON + self._attr_is_on = last_state.state == STATE_ON if last_state.attributes.get(ATTR_BRIGHTNESS): - self._brightness = last_state.attributes.get(ATTR_BRIGHTNESS) + self._attr_brightness = last_state.attributes.get(ATTR_BRIGHTNESS) if last_state.attributes.get(ATTR_HS_COLOR): - self._hs = last_state.attributes.get(ATTR_HS_COLOR) + self._attr_hs_color = last_state.attributes.get(ATTR_HS_COLOR) + self._update_color_mode() if last_state.attributes.get(ATTR_COLOR_TEMP): - self._color_temp = last_state.attributes.get(ATTR_COLOR_TEMP) + self._attr_color_temp = last_state.attributes.get(ATTR_COLOR_TEMP) if last_state.attributes.get(ATTR_EFFECT): - self._effect = last_state.attributes.get(ATTR_EFFECT) - - @property - def brightness(self): - """Return the brightness of this light between 0..255.""" - return self._brightness - - @property - def color_temp(self): - """Return the color temperature in mired.""" - return self._color_temp - - @property - def min_mireds(self): - """Return the coldest color_temp that this light supports.""" - return self._config.get(CONF_MIN_MIREDS, super().min_mireds) - - @property - def max_mireds(self): - """Return the warmest color_temp that this light supports.""" - return self._config.get(CONF_MAX_MIREDS, super().max_mireds) - - @property - def hs_color(self): - """Return the hs color value [int, int].""" - return self._hs - - @property - def is_on(self): - """Return True if entity is on.""" - return self._state + self._attr_effect = last_state.attributes.get(ATTR_EFFECT) @property def assumed_state(self): """Return True if unable to access real state of the entity.""" return self._optimistic - @property - def effect_list(self): - """Return the list of supported effects.""" - return self._config.get(CONF_EFFECT_LIST) - - @property - def effect(self): - """Return the current effect.""" - return self._effect - async def async_turn_on(self, **kwargs): """Turn the entity on. @@ -341,20 +317,21 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): """ values = {"state": True} if self._optimistic: - self._state = True + self._attr_is_on = True if ATTR_BRIGHTNESS in kwargs: values["brightness"] = int(kwargs[ATTR_BRIGHTNESS]) if self._optimistic: - self._brightness = kwargs[ATTR_BRIGHTNESS] + self._attr_brightness = kwargs[ATTR_BRIGHTNESS] if ATTR_COLOR_TEMP in kwargs: values["color_temp"] = int(kwargs[ATTR_COLOR_TEMP]) if self._optimistic: - self._color_temp = kwargs[ATTR_COLOR_TEMP] - self._hs = None + self._attr_color_temp = kwargs[ATTR_COLOR_TEMP] + self._attr_hs_color = None + self._update_color_mode() if ATTR_HS_COLOR in kwargs: hs_color = kwargs[ATTR_HS_COLOR] @@ -366,7 +343,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): else: brightness = kwargs.get( ATTR_BRIGHTNESS, - self._brightness if self._brightness is not None else 255, + self._attr_brightness if self._attr_brightness is not None else 255, ) rgb = color_util.color_hsv_to_RGB( hs_color[0], hs_color[1], brightness / 255 * 100 @@ -378,14 +355,15 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): values["sat"] = hs_color[1] if self._optimistic: - self._color_temp = None - self._hs = kwargs[ATTR_HS_COLOR] + self._attr_color_temp = None + self._attr_hs_color = kwargs[ATTR_HS_COLOR] + self._update_color_mode() if ATTR_EFFECT in kwargs: values["effect"] = kwargs.get(ATTR_EFFECT) if self._optimistic: - self._effect = kwargs[ATTR_EFFECT] + self._attr_effect = kwargs[ATTR_EFFECT] if ATTR_FLASH in kwargs: values["flash"] = kwargs.get(ATTR_FLASH) @@ -413,7 +391,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): """ values = {"state": False} if self._optimistic: - self._state = False + self._attr_is_on = False if ATTR_TRANSITION in kwargs: values["transition"] = kwargs[ATTR_TRANSITION] @@ -430,27 +408,3 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): if self._optimistic: self.async_write_ha_state() - - @property - def color_mode(self): - """Return current color mode.""" - if self._fixed_color_mode: - return self._fixed_color_mode - # Support for ct + hs, prioritize hs - if self._hs is not None: - return ColorMode.HS - return ColorMode.COLOR_TEMP - - @property - def supported_color_modes(self): - """Flag supported color modes.""" - return self._supported_color_modes - - @property - def supported_features(self): - """Flag supported features.""" - features = LightEntityFeature.FLASH | LightEntityFeature.TRANSITION - if self._config.get(CONF_EFFECT_LIST) is not None: - features = features | LightEntityFeature.EFFECT - - return features From 318122fe531ae021480da29e5d26e1b9cf1213bd Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 8 Nov 2022 10:38:29 +0100 Subject: [PATCH 0282/1033] Fix coordinator TypeVar definition (#81298) * Adjust coordinator TypeVar definition * Adjust again --- homeassistant/helpers/update_coordinator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/helpers/update_coordinator.py b/homeassistant/helpers/update_coordinator.py index 768b8040729..d0d1cb90454 100644 --- a/homeassistant/helpers/update_coordinator.py +++ b/homeassistant/helpers/update_coordinator.py @@ -25,7 +25,7 @@ REQUEST_REFRESH_DEFAULT_IMMEDIATE = True _T = TypeVar("_T") _DataUpdateCoordinatorT = TypeVar( - "_DataUpdateCoordinatorT", bound="DataUpdateCoordinator[Any]" + "_DataUpdateCoordinatorT", bound="DataUpdateCoordinator" ) From fc0e0bf099c082a12b11578696a3b016cdc7a93f Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Tue, 8 Nov 2022 10:49:47 +0100 Subject: [PATCH 0283/1033] Support for direct modbus connection to nibe pumps (#80557) * Initial support for modbus * Complete test coverage of config flow * Bump to 1.1.0 with corrected numbers * Handle missing mapping for reset-alarm * Fixup type checks after library bump * Adjust coil number * Move word_swap to nibegw * Adjust to 1.1.2 with fixes * Add series property * Add S series models * Correct test for invalid host * Apply suggestions from code review Co-authored-by: J. Nick Koston * Move some things into library * Adjust strings somewhat * Correct black * Correct test after validation change Co-authored-by: J. Nick Koston --- .../components/nibe_heatpump/__init__.py | 72 +++--- .../components/nibe_heatpump/config_flow.py | 154 ++++++++--- .../components/nibe_heatpump/const.py | 3 + .../components/nibe_heatpump/manifest.json | 2 +- .../components/nibe_heatpump/number.py | 4 + .../components/nibe_heatpump/select.py | 5 + .../components/nibe_heatpump/strings.json | 22 +- .../nibe_heatpump/translations/en.json | 22 +- requirements_all.txt | 5 +- requirements_test_all.txt | 5 +- .../nibe_heatpump/test_config_flow.py | 241 +++++++++++++----- 11 files changed, 394 insertions(+), 141 deletions(-) diff --git a/homeassistant/components/nibe_heatpump/__init__.py b/homeassistant/components/nibe_heatpump/__init__.py index 053d6db2a34..68e16871549 100644 --- a/homeassistant/components/nibe_heatpump/__init__.py +++ b/homeassistant/components/nibe_heatpump/__init__.py @@ -10,10 +10,10 @@ from typing import Any, Generic, TypeVar from nibe.coil import Coil from nibe.connection import Connection +from nibe.connection.modbus import Modbus from nibe.connection.nibegw import NibeGW from nibe.exceptions import CoilNotFoundException, CoilReadException -from nibe.heatpump import HeatPump, Model -from tenacity import RetryError, retry, retry_if_exception_type, stop_after_attempt +from nibe.heatpump import HeatPump, Model, Series from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -34,8 +34,11 @@ from homeassistant.helpers.update_coordinator import ( from .const import ( CONF_CONNECTION_TYPE, + CONF_CONNECTION_TYPE_MODBUS, CONF_CONNECTION_TYPE_NIBEGW, CONF_LISTENING_PORT, + CONF_MODBUS_UNIT, + CONF_MODBUS_URL, CONF_REMOTE_READ_PORT, CONF_REMOTE_WRITE_PORT, CONF_WORD_SWAP, @@ -57,12 +60,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Nibe Heat Pump from a config entry.""" heatpump = HeatPump(Model[entry.data[CONF_MODEL]]) - heatpump.word_swap = entry.data[CONF_WORD_SWAP] - await hass.async_add_executor_job(heatpump.initialize) + await heatpump.initialize() + connection: Connection connection_type = entry.data[CONF_CONNECTION_TYPE] if connection_type == CONF_CONNECTION_TYPE_NIBEGW: + heatpump.word_swap = entry.data[CONF_WORD_SWAP] connection = NibeGW( heatpump, entry.data[CONF_IP_ADDRESS], @@ -70,13 +74,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: entry.data[CONF_REMOTE_WRITE_PORT], listening_port=entry.data[CONF_LISTENING_PORT], ) + elif connection_type == CONF_CONNECTION_TYPE_MODBUS: + connection = Modbus( + heatpump, entry.data[CONF_MODBUS_URL], entry.data[CONF_MODBUS_UNIT] + ) else: raise HomeAssistantError(f"Connection type {connection_type} is not supported.") await connection.start() + assert heatpump.model + + async def _async_stop(_): + await connection.stop() + entry.async_on_unload( - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, connection.stop) + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop) ) coordinator = Coordinator(hass, heatpump, connection) @@ -184,6 +197,11 @@ class Coordinator(ContextCoordinator[dict[int, Coil], int]): self.seed[coil.address] = coil self.async_update_context_listeners([coil.address]) + @property + def series(self) -> Series: + """Return which series of pump we are connected to.""" + return self.heatpump.series + @property def coils(self) -> list[Coil]: """Return the full coil database.""" @@ -201,8 +219,8 @@ class Coordinator(ContextCoordinator[dict[int, Coil], int]): def get_coil_value(self, coil: Coil) -> int | str | float | None: """Return a coil with data and check for validity.""" - if coil := self.data.get(coil.address): - return coil.value + if coil_with_data := self.data.get(coil.address): + return coil_with_data.value return None def get_coil_float(self, coil: Coil) -> float | None: @@ -228,33 +246,29 @@ class Coordinator(ContextCoordinator[dict[int, Coil], int]): self.task = None async def _async_update_data_internal(self) -> dict[int, Coil]: - @retry( - retry=retry_if_exception_type(CoilReadException), - stop=stop_after_attempt(COIL_READ_RETRIES), - ) - async def read_coil(coil: Coil): - return await self.connection.read_coil(coil) result: dict[int, Coil] = {} - for address in self.context_callbacks.keys(): - if seed := self.seed.pop(address, None): - self.logger.debug("Skipping seeded coil: %d", address) - result[address] = seed - continue + def _get_coils() -> Iterable[Coil]: + for address in sorted(self.context_callbacks.keys()): + if seed := self.seed.pop(address, None): + self.logger.debug("Skipping seeded coil: %d", address) + result[address] = seed + continue - try: - coil = self.heatpump.get_coil_by_address(address) - except CoilNotFoundException as exception: - self.logger.debug("Skipping missing coil: %s", exception) - continue + try: + coil = self.heatpump.get_coil_by_address(address) + except CoilNotFoundException as exception: + self.logger.debug("Skipping missing coil: %s", exception) + continue + yield coil - try: - result[coil.address] = await read_coil(coil) - except (CoilReadException, RetryError) as exception: - raise UpdateFailed(f"Failed to update: {exception}") from exception - - self.seed.pop(coil.address, None) + try: + async for coil in self.connection.read_coils(_get_coils()): + result[coil.address] = coil + self.seed.pop(coil.address, None) + except CoilReadException as exception: + raise UpdateFailed(f"Failed to update: {exception}") from exception return result diff --git a/homeassistant/components/nibe_heatpump/config_flow.py b/homeassistant/components/nibe_heatpump/config_flow.py index d68def046fd..6050010b20d 100644 --- a/homeassistant/components/nibe_heatpump/config_flow.py +++ b/homeassistant/components/nibe_heatpump/config_flow.py @@ -1,14 +1,21 @@ """Config flow for Nibe Heat Pump integration.""" from __future__ import annotations -import errno -from socket import gaierror from typing import Any +from nibe.connection.modbus import Modbus from nibe.connection.nibegw import NibeGW -from nibe.exceptions import CoilNotFoundException, CoilReadException, CoilWriteException +from nibe.exceptions import ( + AddressInUseException, + CoilNotFoundException, + CoilReadException, + CoilReadSendException, + CoilWriteException, + CoilWriteSendException, +) from nibe.heatpump import HeatPump, Model import voluptuous as vol +import yarl from homeassistant import config_entries from homeassistant.const import CONF_IP_ADDRESS, CONF_MODEL @@ -18,8 +25,11 @@ from homeassistant.helpers import selector from .const import ( CONF_CONNECTION_TYPE, + CONF_CONNECTION_TYPE_MODBUS, CONF_CONNECTION_TYPE_NIBEGW, CONF_LISTENING_PORT, + CONF_MODBUS_UNIT, + CONF_MODBUS_URL, CONF_REMOTE_READ_PORT, CONF_REMOTE_WRITE_PORT, CONF_WORD_SWAP, @@ -36,7 +46,7 @@ PORT_SELECTOR = vol.All( vol.Coerce(int), ) -STEP_USER_DATA_SCHEMA = vol.Schema( +STEP_NIBEGW_DATA_SCHEMA = vol.Schema( { vol.Required(CONF_MODEL): vol.In(list(Model.__members__)), vol.Required(CONF_IP_ADDRESS): selector.TextSelector(), @@ -47,6 +57,22 @@ STEP_USER_DATA_SCHEMA = vol.Schema( ) +STEP_MODBUS_DATA_SCHEMA = vol.Schema( + { + vol.Required(CONF_MODEL): vol.In(list(Model.__members__)), + vol.Required(CONF_MODBUS_URL): selector.TextSelector(), + vol.Required(CONF_MODBUS_UNIT, default=0): vol.All( + selector.NumberSelector( + selector.NumberSelectorConfig( + min=0, step=1, mode=selector.NumberSelectorMode.BOX + ) + ), + vol.Coerce(int), + ), + } +) + + class FieldError(Exception): """Field with invalid data.""" @@ -57,11 +83,13 @@ class FieldError(Exception): self.error = error -async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]: +async def validate_nibegw_input( + hass: HomeAssistant, data: dict[str, Any] +) -> tuple[str, dict[str, Any]]: """Validate the user input allows us to connect.""" heatpump = HeatPump(Model[data[CONF_MODEL]]) - heatpump.initialize() + await heatpump.initialize() connection = NibeGW( heatpump, @@ -73,24 +101,17 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, try: await connection.start() - except OSError as exception: - if exception.errno == errno.EADDRINUSE: - raise FieldError( - "Address already in use", "listening_port", "address_in_use" - ) from exception - raise + except AddressInUseException as exception: + raise FieldError( + "Address already in use", "listening_port", "address_in_use" + ) from exception try: - coil = heatpump.get_coil_by_name("modbus40-word-swap-48852") - coil = await connection.read_coil(coil) - word_swap = coil.value == "ON" - coil = await connection.write_coil(coil) - except gaierror as exception: - raise FieldError(str(exception), "ip_address", "address") from exception + await connection.verify_connectivity() + except (CoilReadSendException, CoilWriteSendException) as exception: + raise FieldError(str(exception), CONF_IP_ADDRESS, "address") from exception except CoilNotFoundException as exception: - raise FieldError( - "Model selected doesn't seem to support expected coils", "base", "model" - ) from exception + raise FieldError("Coils not found", "base", "model") from exception except CoilReadException as exception: raise FieldError("Timeout on read from pump", "base", "read") from exception except CoilWriteException as exception: @@ -98,9 +119,49 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, finally: await connection.stop() - return { - "title": f"{data[CONF_MODEL]} at {data[CONF_IP_ADDRESS]}", - CONF_WORD_SWAP: word_swap, + return f"{data[CONF_MODEL]} at {data[CONF_IP_ADDRESS]}", { + **data, + CONF_WORD_SWAP: heatpump.word_swap, + CONF_CONNECTION_TYPE: CONF_CONNECTION_TYPE_NIBEGW, + } + + +async def validate_modbus_input( + hass: HomeAssistant, data: dict[str, Any] +) -> tuple[str, dict[str, Any]]: + """Validate the user input allows us to connect.""" + + heatpump = HeatPump(Model[data[CONF_MODEL]]) + await heatpump.initialize() + + try: + connection = Modbus( + heatpump, + data[CONF_MODBUS_URL], + data[CONF_MODBUS_UNIT], + ) + except ValueError as exc: + raise FieldError("Not a valid modbus url", CONF_MODBUS_URL, "url") from exc + + await connection.start() + + try: + await connection.verify_connectivity() + except (CoilReadSendException, CoilWriteSendException) as exception: + raise FieldError(str(exception), CONF_MODBUS_URL, "address") from exception + except CoilNotFoundException as exception: + raise FieldError("Coils not found", "base", "model") from exception + except CoilReadException as exception: + raise FieldError("Timeout on read from pump", "base", "read") from exception + except CoilWriteException as exception: + raise FieldError("Timeout on writing to pump", "base", "write") from exception + finally: + await connection.stop() + + host = yarl.URL(data[CONF_MODBUS_URL]).host + return f"{data[CONF_MODEL]} at {host}", { + **data, + CONF_CONNECTION_TYPE: CONF_CONNECTION_TYPE_MODBUS, } @@ -113,15 +174,21 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle the initial step.""" + return self.async_show_menu(step_id="user", menu_options=["modbus", "nibegw"]) + + async def async_step_modbus( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the modbus step.""" if user_input is None: return self.async_show_form( - step_id="user", data_schema=STEP_USER_DATA_SCHEMA + step_id="modbus", data_schema=STEP_MODBUS_DATA_SCHEMA ) errors = {} try: - info = await validate_input(self.hass, user_input) + title, data = await validate_modbus_input(self.hass, user_input) except FieldError as exception: LOGGER.debug("Validation error %s", exception) errors[exception.field] = exception.error @@ -129,13 +196,34 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): LOGGER.exception("Unexpected exception") errors["base"] = "unknown" else: - data = { - **user_input, - CONF_WORD_SWAP: info[CONF_WORD_SWAP], - CONF_CONNECTION_TYPE: CONF_CONNECTION_TYPE_NIBEGW, - } - return self.async_create_entry(title=info["title"], data=data) + return self.async_create_entry(title=title, data=data) return self.async_show_form( - step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors + step_id="modbus", data_schema=STEP_MODBUS_DATA_SCHEMA, errors=errors + ) + + async def async_step_nibegw( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the nibegw step.""" + if user_input is None: + return self.async_show_form( + step_id="nibegw", data_schema=STEP_NIBEGW_DATA_SCHEMA + ) + + errors = {} + + try: + title, data = await validate_nibegw_input(self.hass, user_input) + except FieldError as exception: + LOGGER.exception("Validation error") + errors[exception.field] = exception.error + except Exception: # pylint: disable=broad-except + LOGGER.exception("Unexpected exception") + errors["base"] = "unknown" + else: + return self.async_create_entry(title=title, data=data) + + return self.async_show_form( + step_id="nibegw", data_schema=STEP_NIBEGW_DATA_SCHEMA, errors=errors ) diff --git a/homeassistant/components/nibe_heatpump/const.py b/homeassistant/components/nibe_heatpump/const.py index f1bcbf11127..381ad7ba0c2 100644 --- a/homeassistant/components/nibe_heatpump/const.py +++ b/homeassistant/components/nibe_heatpump/const.py @@ -10,3 +10,6 @@ CONF_REMOTE_WRITE_PORT = "remote_write_port" CONF_WORD_SWAP = "word_swap" CONF_CONNECTION_TYPE = "connection_type" CONF_CONNECTION_TYPE_NIBEGW = "nibegw" +CONF_CONNECTION_TYPE_MODBUS = "modbus" +CONF_MODBUS_URL = "modbus_url" +CONF_MODBUS_UNIT = "modbus_unit" diff --git a/homeassistant/components/nibe_heatpump/manifest.json b/homeassistant/components/nibe_heatpump/manifest.json index 4b66b93d31b..68dc8c7a06c 100644 --- a/homeassistant/components/nibe_heatpump/manifest.json +++ b/homeassistant/components/nibe_heatpump/manifest.json @@ -3,7 +3,7 @@ "name": "Nibe Heat Pump", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/nibe_heatpump", - "requirements": ["nibe==0.5.0", "tenacity==8.0.1"], + "requirements": ["nibe==1.2.0"], "codeowners": ["@elupus"], "iot_class": "local_polling" } diff --git a/homeassistant/components/nibe_heatpump/number.py b/homeassistant/components/nibe_heatpump/number.py index 11c6917ec1c..606588f7142 100644 --- a/homeassistant/components/nibe_heatpump/number.py +++ b/homeassistant/components/nibe_heatpump/number.py @@ -58,6 +58,10 @@ class Number(CoilEntity, NumberEntity): self._attr_native_value = None def _async_read_coil(self, coil: Coil) -> None: + if coil.value is None: + self._attr_native_value = None + return + try: self._attr_native_value = float(coil.value) except ValueError: diff --git a/homeassistant/components/nibe_heatpump/select.py b/homeassistant/components/nibe_heatpump/select.py index 27df1980287..412c1579586 100644 --- a/homeassistant/components/nibe_heatpump/select.py +++ b/homeassistant/components/nibe_heatpump/select.py @@ -35,11 +35,16 @@ class Select(CoilEntity, SelectEntity): def __init__(self, coordinator: Coordinator, coil: Coil) -> None: """Initialize entity.""" + assert coil.mappings super().__init__(coordinator, coil, ENTITY_ID_FORMAT) self._attr_options = list(coil.mappings.values()) self._attr_current_option = None def _async_read_coil(self, coil: Coil) -> None: + if not isinstance(coil.value, str): + self._attr_current_option = None + return + self._attr_current_option = coil.value async def async_select_option(self, option: str) -> None: diff --git a/homeassistant/components/nibe_heatpump/strings.json b/homeassistant/components/nibe_heatpump/strings.json index 08a049cb17a..d6e93af689a 100644 --- a/homeassistant/components/nibe_heatpump/strings.json +++ b/homeassistant/components/nibe_heatpump/strings.json @@ -2,8 +2,27 @@ "config": { "step": { "user": { + "menu_options": { + "nibegw": "NibeGW", + "modbus": "Modbus" + }, + "description": "Pick the connection method to your pump. In general, F-series pumps require a Nibe GW custom accessory, while an S-series pump has Modbus support built-in." + }, + "modbus": { + "data": { + "model": "Model of Heat Pump", + "modbus_url": "Modbus URL", + "modbus_unit": "Modbus Unit Identifier" + }, + "data_description": { + "modbus_url": "Modbus URL that describes the connection to your Heat Pump or MODBUS40 unit. It should be on the form:\n - `tcp://[HOST]:[PORT]` for Modbus TCP connection\n - `serial://[LOCAL DEVICE]` for a local Modbus RTU connection\n - `rfc2217://[HOST]:[PORT]` for a remote telnet based Modbus RTU connection.", + "modbus_unit": "Unit identification for you Heat Pump. Can usually be left at 0." + } + }, + "nibegw": { "description": "Before attempting to configure the integration, verify that:\n - The NibeGW unit is connected to a heat pump.\n - The MODBUS40 accessory has been enabled in the heat pump configuration.\n - The pump has not gone into an alarm state about missing MODBUS40 accessory.", "data": { + "model": "Model of Heat Pump", "ip_address": "Remote address", "remote_read_port": "Remote read port", "remote_write_port": "Remote write port", @@ -23,7 +42,8 @@ "address": "Invalid remote address specified. Address must be an IP address or a resolvable hostname.", "address_in_use": "The selected listening port is already in use on this system.", "model": "The model selected doesn't seem to support modbus40", - "unknown": "[%key:common::config_flow::error::unknown%]" + "unknown": "[%key:common::config_flow::error::unknown%]", + "url": "The url specified is not a well formed and supported url" } } } diff --git a/homeassistant/components/nibe_heatpump/translations/en.json b/homeassistant/components/nibe_heatpump/translations/en.json index 4c6e86720f1..167a8341cd3 100644 --- a/homeassistant/components/nibe_heatpump/translations/en.json +++ b/homeassistant/components/nibe_heatpump/translations/en.json @@ -9,13 +9,26 @@ "model": "The model selected doesn't seem to support modbus40", "read": "Error on read request from pump. Verify your `Remote read port` or `Remote IP address`.", "unknown": "Unexpected error", + "url": "The url specified is not a well formed and supported url", "write": "Error on write request to pump. Verify your `Remote write port` or `Remote IP address`." }, "step": { - "user": { + "modbus": { + "data": { + "modbus_unit": "Modbus Unit Identifier", + "modbus_url": "Modbus URL", + "model": "Model of Heat Pump" + }, + "data_description": { + "modbus_unit": "Unit identification for you Heat Pump. Can usually be left at 0.", + "modbus_url": "Modbus URL that describes the connection to your Heat Pump or MODBUS40 unit. It should be on the form:\n - `tcp://[HOST]:[PORT]` for Modbus TCP connection\n - `serial://[LOCAL DEVICE]` for a local Modbus RTU connection\n - `rfc2217://[HOST]:[PORT]` for a remote telnet based Modbus RTU connection." + } + }, + "nibegw": { "data": { "ip_address": "Remote address", "listening_port": "Local listening port", + "model": "Model of Heat Pump", "remote_read_port": "Remote read port", "remote_write_port": "Remote write port" }, @@ -26,6 +39,13 @@ "remote_write_port": "The port the NibeGW unit is listening for write requests on." }, "description": "Before attempting to configure the integration, verify that:\n - The NibeGW unit is connected to a heat pump.\n - The MODBUS40 accessory has been enabled in the heat pump configuration.\n - The pump has not gone into an alarm state about missing MODBUS40 accessory." + }, + "user": { + "description": "Pick the connection method to your pump. In general, F-series pumps require a Nibe GW custom accessory, while an S-series pump has Modbus support built-in.", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" + } } } } diff --git a/requirements_all.txt b/requirements_all.txt index 61919196caf..d8243dad9c6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1159,7 +1159,7 @@ nextcord==2.0.0a8 nextdns==1.1.1 # homeassistant.components.nibe_heatpump -nibe==0.5.0 +nibe==1.2.0 # homeassistant.components.niko_home_control niko-home-control==0.2.1 @@ -2394,9 +2394,6 @@ temescal==0.5 # homeassistant.components.temper temperusb==1.6.0 -# homeassistant.components.nibe_heatpump -tenacity==8.0.1 - # homeassistant.components.tensorflow # tensorflow==2.5.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fdb0a01bd23..9270aa1a925 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -846,7 +846,7 @@ nextcord==2.0.0a8 nextdns==1.1.1 # homeassistant.components.nibe_heatpump -nibe==0.5.0 +nibe==1.2.0 # homeassistant.components.nfandroidtv notifications-android-tv==0.1.5 @@ -1652,9 +1652,6 @@ tellduslive==0.10.11 # homeassistant.components.lg_soundbar temescal==0.5 -# homeassistant.components.nibe_heatpump -tenacity==8.0.1 - # homeassistant.components.powerwall tesla-powerwall==0.3.18 diff --git a/tests/components/nibe_heatpump/test_config_flow.py b/tests/components/nibe_heatpump/test_config_flow.py index f7dc08c41bb..4a0751ea74b 100644 --- a/tests/components/nibe_heatpump/test_config_flow.py +++ b/tests/components/nibe_heatpump/test_config_flow.py @@ -1,11 +1,16 @@ """Test the Nibe Heat Pump config flow.""" -import errno -from socket import gaierror -from unittest.mock import Mock, patch +from unittest.mock import AsyncMock, Mock, patch from nibe.coil import Coil from nibe.connection import Connection -from nibe.exceptions import CoilNotFoundException, CoilReadException, CoilWriteException +from nibe.exceptions import ( + AddressInUseException, + CoilNotFoundException, + CoilReadException, + CoilReadSendException, + CoilWriteException, +) +import pytest from pytest import fixture from homeassistant import config_entries @@ -13,7 +18,7 @@ from homeassistant.components.nibe_heatpump import DOMAIN from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType -MOCK_FLOW_USERDATA = { +MOCK_FLOW_NIBEGW_USERDATA = { "model": "F1155", "ip_address": "127.0.0.1", "listening_port": 9999, @@ -22,13 +27,33 @@ MOCK_FLOW_USERDATA = { } -@fixture(autouse=True, name="mock_connection") -async def fixture_mock_connection(): +MOCK_FLOW_MODBUS_USERDATA = { + "model": "S1155", + "modbus_url": "tcp://127.0.0.1", + "modbus_unit": 0, +} + + +@fixture(autouse=True, name="mock_connection_constructor") +async def fixture_mock_connection_constructor(): """Make sure we have a dummy connection.""" + mock_constructor = Mock() with patch( - "homeassistant.components.nibe_heatpump.config_flow.NibeGW", spec=Connection - ) as mock_connection: - yield mock_connection + "homeassistant.components.nibe_heatpump.config_flow.NibeGW", + new=mock_constructor, + ), patch( + "homeassistant.components.nibe_heatpump.config_flow.Modbus", + new=mock_constructor, + ): + yield mock_constructor + + +@fixture(name="mock_connection") +def fixture_mock_connection(mock_connection_constructor: Mock): + """Make sure we have a dummy connection.""" + mock_connection = AsyncMock(spec=Connection) + mock_connection_constructor.return_value = mock_connection + return mock_connection @fixture(autouse=True, name="mock_setup_entry") @@ -40,24 +65,38 @@ async def fixture_mock_setup(): yield mock_setup_entry -async def test_form( - hass: HomeAssistant, mock_connection: Mock, mock_setup_entry: Mock -) -> None: +async def _get_connection_form( + hass: HomeAssistant, connection_type: str +) -> FlowResultType: """Test we get the form.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) + assert result["type"] == FlowResultType.MENU + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"next_step_id": connection_type} + ) + assert result["type"] == FlowResultType.FORM assert result["errors"] is None + return result + + +async def test_nibegw_form( + hass: HomeAssistant, mock_connection: Mock, mock_setup_entry: Mock +) -> None: + """Test we get the form.""" + result = await _get_connection_form(hass, "nibegw") coil_wordswap = Coil( 48852, "modbus40-word-swap-48852", "Modbus40 Word Swap", "u8", min=0, max=1 ) coil_wordswap.value = "ON" - mock_connection.return_value.read_coil.return_value = coil_wordswap + mock_connection.read_coil.return_value = coil_wordswap result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], MOCK_FLOW_USERDATA + result["flow_id"], MOCK_FLOW_NIBEGW_USERDATA ) await hass.async_block_till_done() @@ -75,109 +114,175 @@ async def test_form( assert len(mock_setup_entry.mock_calls) == 1 -async def test_address_inuse(hass: HomeAssistant, mock_connection: Mock) -> None: - """Test we handle invalid auth.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) +async def test_modbus_form( + hass: HomeAssistant, mock_connection: Mock, mock_setup_entry: Mock +) -> None: + """Test we get the form.""" + result = await _get_connection_form(hass, "modbus") - error = OSError() - error.errno = errno.EADDRINUSE - mock_connection.return_value.start.side_effect = error + coil = Coil( + 40022, "reset-alarm-40022", "Reset Alarm", "u8", min=0, max=1, write=True + ) + coil.value = "ON" + mock_connection.read_coil.return_value = coil result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], MOCK_FLOW_USERDATA + result["flow_id"], MOCK_FLOW_MODBUS_USERDATA + ) + await hass.async_block_till_done() + + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == "S1155 at 127.0.0.1" + assert result2["data"] == { + "model": "S1155", + "modbus_url": "tcp://127.0.0.1", + "modbus_unit": 0, + "connection_type": "modbus", + } + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_modbus_invalid_url( + hass: HomeAssistant, mock_connection_constructor: Mock +) -> None: + """Test we handle invalid auth.""" + result = await _get_connection_form(hass, "modbus") + + mock_connection_constructor.side_effect = ValueError() + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], {**MOCK_FLOW_MODBUS_USERDATA, "modbus_url": "invalid://url"} + ) + + assert result2["type"] == FlowResultType.FORM + assert result2["errors"] == {"modbus_url": "url"} + + +async def test_nibegw_address_inuse(hass: HomeAssistant, mock_connection: Mock) -> None: + """Test we handle invalid auth.""" + result = await _get_connection_form(hass, "nibegw") + + mock_connection.start.side_effect = AddressInUseException() + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], MOCK_FLOW_NIBEGW_USERDATA ) assert result2["type"] == FlowResultType.FORM assert result2["errors"] == {"listening_port": "address_in_use"} - error.errno = errno.EACCES - mock_connection.return_value.start.side_effect = error + mock_connection.start.side_effect = Exception() result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], MOCK_FLOW_USERDATA + result["flow_id"], MOCK_FLOW_NIBEGW_USERDATA ) assert result2["type"] == FlowResultType.FORM assert result2["errors"] == {"base": "unknown"} -async def test_read_timeout(hass: HomeAssistant, mock_connection: Mock) -> None: +@pytest.mark.parametrize( + "connection_type,data", + ( + ("nibegw", MOCK_FLOW_NIBEGW_USERDATA), + ("modbus", MOCK_FLOW_MODBUS_USERDATA), + ), +) +async def test_read_timeout( + hass: HomeAssistant, mock_connection: Mock, connection_type: str, data: dict +) -> None: """Test we handle cannot connect error.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) + result = await _get_connection_form(hass, connection_type) - mock_connection.return_value.read_coil.side_effect = CoilReadException() + mock_connection.verify_connectivity.side_effect = CoilReadException() - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], MOCK_FLOW_USERDATA - ) + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data) assert result2["type"] == FlowResultType.FORM assert result2["errors"] == {"base": "read"} -async def test_write_timeout(hass: HomeAssistant, mock_connection: Mock) -> None: +@pytest.mark.parametrize( + "connection_type,data", + ( + ("nibegw", MOCK_FLOW_NIBEGW_USERDATA), + ("modbus", MOCK_FLOW_MODBUS_USERDATA), + ), +) +async def test_write_timeout( + hass: HomeAssistant, mock_connection: Mock, connection_type: str, data: dict +) -> None: """Test we handle cannot connect error.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) + result = await _get_connection_form(hass, connection_type) - mock_connection.return_value.write_coil.side_effect = CoilWriteException() + mock_connection.verify_connectivity.side_effect = CoilWriteException() - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], MOCK_FLOW_USERDATA - ) + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data) assert result2["type"] == FlowResultType.FORM assert result2["errors"] == {"base": "write"} -async def test_unexpected_exception(hass: HomeAssistant, mock_connection: Mock) -> None: +@pytest.mark.parametrize( + "connection_type,data", + ( + ("nibegw", MOCK_FLOW_NIBEGW_USERDATA), + ("modbus", MOCK_FLOW_MODBUS_USERDATA), + ), +) +async def test_unexpected_exception( + hass: HomeAssistant, mock_connection: Mock, connection_type: str, data: dict +) -> None: """Test we handle cannot connect error.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) + result = await _get_connection_form(hass, connection_type) - mock_connection.return_value.read_coil.side_effect = Exception() + mock_connection.verify_connectivity.side_effect = Exception() - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], MOCK_FLOW_USERDATA - ) + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data) assert result2["type"] == FlowResultType.FORM assert result2["errors"] == {"base": "unknown"} -async def test_invalid_host(hass: HomeAssistant, mock_connection: Mock) -> None: +@pytest.mark.parametrize( + "connection_type,data", + ( + ("nibegw", MOCK_FLOW_NIBEGW_USERDATA), + ("modbus", MOCK_FLOW_MODBUS_USERDATA), + ), +) +async def test_nibegw_invalid_host( + hass: HomeAssistant, mock_connection: Mock, connection_type: str, data: dict +) -> None: """Test we handle cannot connect error.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) + result = await _get_connection_form(hass, connection_type) - mock_connection.return_value.read_coil.side_effect = gaierror() + mock_connection.verify_connectivity.side_effect = CoilReadSendException() - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], {**MOCK_FLOW_USERDATA, "ip_address": "abcd"} - ) + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data) assert result2["type"] == FlowResultType.FORM - assert result2["errors"] == {"ip_address": "address"} + if connection_type == "nibegw": + assert result2["errors"] == {"ip_address": "address"} + else: + assert result2["errors"] == {"modbus_url": "address"} -async def test_model_missing_coil(hass: HomeAssistant, mock_connection: Mock) -> None: +@pytest.mark.parametrize( + "connection_type,data", + ( + ("nibegw", MOCK_FLOW_NIBEGW_USERDATA), + ("modbus", MOCK_FLOW_MODBUS_USERDATA), + ), +) +async def test_model_missing_coil( + hass: HomeAssistant, mock_connection: Mock, connection_type: str, data: dict +) -> None: """Test we handle cannot connect error.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) + result = await _get_connection_form(hass, connection_type) - mock_connection.return_value.read_coil.side_effect = CoilNotFoundException() + mock_connection.verify_connectivity.side_effect = CoilNotFoundException() - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], {**MOCK_FLOW_USERDATA} - ) + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data) assert result2["type"] == FlowResultType.FORM assert result2["errors"] == {"base": "model"} From 274049cc8edeb82b737bbf92a094fd2f0e4de1e6 Mon Sep 17 00:00:00 2001 From: Steven Looman Date: Tue, 8 Nov 2022 11:02:53 +0100 Subject: [PATCH 0284/1033] Fix ignored upnp discoveries not being matched when device changes its unique identifier (#81240) Fixes https://github.com/home-assistant/core/issues/78454 fixes undefined --- homeassistant/components/upnp/config_flow.py | 60 ++++++++++++++------ homeassistant/components/upnp/const.py | 1 + tests/components/upnp/test_config_flow.py | 38 +++++++++++++ 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/upnp/config_flow.py b/homeassistant/components/upnp/config_flow.py index 6b488398461..b7d6425707d 100644 --- a/homeassistant/components/upnp/config_flow.py +++ b/homeassistant/components/upnp/config_flow.py @@ -20,6 +20,7 @@ from .const import ( CONFIG_ENTRY_ST, CONFIG_ENTRY_UDN, DOMAIN, + DOMAIN_DISCOVERIES, LOGGER, ST_IGD_V1, ST_IGD_V2, @@ -47,7 +48,7 @@ def _is_complete_discovery(discovery_info: ssdp.SsdpServiceInfo) -> bool: ) -async def _async_discover_igd_devices( +async def _async_discovered_igd_devices( hass: HomeAssistant, ) -> list[ssdp.SsdpServiceInfo]: """Discovery IGD devices.""" @@ -79,9 +80,19 @@ class UpnpFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): # - ssdp(discovery_info) --> ssdp_confirm(None) --> ssdp_confirm({}) --> create_entry() # - user(None): scan --> user({...}) --> create_entry() - def __init__(self) -> None: - """Initialize the UPnP/IGD config flow.""" - self._discoveries: list[SsdpServiceInfo] | None = None + @property + def _discoveries(self) -> dict[str, SsdpServiceInfo]: + """Get current discoveries.""" + domain_data: dict = self.hass.data.setdefault(DOMAIN, {}) + return domain_data.setdefault(DOMAIN_DISCOVERIES, {}) + + def _add_discovery(self, discovery: SsdpServiceInfo) -> None: + """Add a discovery.""" + self._discoveries[discovery.ssdp_usn] = discovery + + def _remove_discovery(self, usn: str) -> SsdpServiceInfo: + """Remove a discovery by its USN/unique_id.""" + return self._discoveries.pop(usn) async def async_step_user( self, user_input: Mapping[str, Any] | None = None @@ -95,7 +106,7 @@ class UpnpFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): discovery = next( iter( discovery - for discovery in self._discoveries + for discovery in self._discoveries.values() if discovery.ssdp_usn == user_input["unique_id"] ) ) @@ -103,21 +114,19 @@ class UpnpFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return await self._async_create_entry_from_discovery(discovery) # Discover devices. - discoveries = await _async_discover_igd_devices(self.hass) + discoveries = await _async_discovered_igd_devices(self.hass) # Store discoveries which have not been configured. current_unique_ids = { entry.unique_id for entry in self._async_current_entries() } - self._discoveries = [ - discovery - for discovery in discoveries + for discovery in discoveries: if ( _is_complete_discovery(discovery) and _is_igd_device(discovery) and discovery.ssdp_usn not in current_unique_ids - ) - ] + ): + self._add_discovery(discovery) # Ensure anything to add. if not self._discoveries: @@ -128,7 +137,7 @@ class UpnpFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): vol.Required("unique_id"): vol.In( { discovery.ssdp_usn: _friendly_name_from_discovery(discovery) - for discovery in self._discoveries + for discovery in self._discoveries.values() } ), } @@ -163,12 +172,13 @@ class UpnpFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): mac_address = await _async_mac_address_from_discovery(self.hass, discovery_info) host = discovery_info.ssdp_headers["_host"] self._abort_if_unique_id_configured( - # Store mac address for older entries. + # Store mac address and other data for older entries. # The location is stored in the config entry such that when the location changes, the entry is reloaded. updates={ CONFIG_ENTRY_MAC_ADDRESS: mac_address, CONFIG_ENTRY_LOCATION: discovery_info.ssdp_location, CONFIG_ENTRY_HOST: host, + CONFIG_ENTRY_ST: discovery_info.ssdp_st, }, ) @@ -204,7 +214,7 @@ class UpnpFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return self.async_abort(reason="config_entry_updated") # Store discovery. - self._discoveries = [discovery_info] + self._add_discovery(discovery_info) # Ensure user recognizable. self.context["title_placeholders"] = { @@ -221,10 +231,27 @@ class UpnpFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if user_input is None: return self.async_show_form(step_id="ssdp_confirm") - assert self._discoveries - discovery = self._discoveries[0] + assert self.unique_id + discovery = self._remove_discovery(self.unique_id) return await self._async_create_entry_from_discovery(discovery) + async def async_step_ignore(self, user_input: dict[str, Any]) -> FlowResult: + """Ignore this config flow.""" + usn = user_input["unique_id"] + discovery = self._remove_discovery(usn) + mac_address = await _async_mac_address_from_discovery(self.hass, discovery) + data = { + CONFIG_ENTRY_UDN: discovery.upnp[ssdp.ATTR_UPNP_UDN], + CONFIG_ENTRY_ST: discovery.ssdp_st, + CONFIG_ENTRY_ORIGINAL_UDN: discovery.upnp[ssdp.ATTR_UPNP_UDN], + CONFIG_ENTRY_MAC_ADDRESS: mac_address, + CONFIG_ENTRY_HOST: discovery.ssdp_headers["_host"], + CONFIG_ENTRY_LOCATION: discovery.ssdp_location, + } + + await self.async_set_unique_id(user_input["unique_id"], raise_on_progress=False) + return self.async_create_entry(title=user_input["title"], data=data) + async def _async_create_entry_from_discovery( self, discovery: SsdpServiceInfo, @@ -243,5 +270,6 @@ class UpnpFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): CONFIG_ENTRY_ORIGINAL_UDN: discovery.upnp[ssdp.ATTR_UPNP_UDN], CONFIG_ENTRY_LOCATION: discovery.ssdp_location, CONFIG_ENTRY_MAC_ADDRESS: mac_address, + CONFIG_ENTRY_HOST: discovery.ssdp_headers["_host"], } return self.async_create_entry(title=title, data=data) diff --git a/homeassistant/components/upnp/const.py b/homeassistant/components/upnp/const.py index 8d98790983a..5f73b1e63c9 100644 --- a/homeassistant/components/upnp/const.py +++ b/homeassistant/components/upnp/const.py @@ -7,6 +7,7 @@ from homeassistant.const import TIME_SECONDS LOGGER = logging.getLogger(__package__) DOMAIN = "upnp" +DOMAIN_DISCOVERIES = "discoveries" BYTES_RECEIVED = "bytes_received" BYTES_SENT = "bytes_sent" PACKETS_RECEIVED = "packets_received" diff --git a/tests/components/upnp/test_config_flow.py b/tests/components/upnp/test_config_flow.py index f0a1de1ce37..7850554f751 100644 --- a/tests/components/upnp/test_config_flow.py +++ b/tests/components/upnp/test_config_flow.py @@ -63,6 +63,42 @@ async def test_flow_ssdp(hass: HomeAssistant): CONFIG_ENTRY_ORIGINAL_UDN: TEST_UDN, CONFIG_ENTRY_LOCATION: TEST_LOCATION, CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS, + CONFIG_ENTRY_HOST: TEST_HOST, + } + + +@pytest.mark.usefixtures( + "ssdp_instant_discovery", + "mock_setup_entry", + "mock_get_source_ip", + "mock_mac_address_from_host", +) +async def test_flow_ssdp_ignore(hass: HomeAssistant): + """Test config flow: discovered + ignore through ssdp.""" + # Discovered via step ssdp. + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_SSDP}, + data=TEST_DISCOVERY, + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "ssdp_confirm" + + # Ignore entry. + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IGNORE}, + data={"unique_id": TEST_USN, "title": TEST_FRIENDLY_NAME}, + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["title"] == TEST_FRIENDLY_NAME + assert result["data"] == { + CONFIG_ENTRY_ST: TEST_ST, + CONFIG_ENTRY_UDN: TEST_UDN, + CONFIG_ENTRY_ORIGINAL_UDN: TEST_UDN, + CONFIG_ENTRY_LOCATION: TEST_LOCATION, + CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS, + CONFIG_ENTRY_HOST: TEST_HOST, } @@ -138,6 +174,7 @@ async def test_flow_ssdp_no_mac_address(hass: HomeAssistant): CONFIG_ENTRY_ORIGINAL_UDN: TEST_UDN, CONFIG_ENTRY_LOCATION: TEST_LOCATION, CONFIG_ENTRY_MAC_ADDRESS: None, + CONFIG_ENTRY_HOST: TEST_HOST, } @@ -382,6 +419,7 @@ async def test_flow_user(hass: HomeAssistant): CONFIG_ENTRY_ORIGINAL_UDN: TEST_UDN, CONFIG_ENTRY_LOCATION: TEST_LOCATION, CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS, + CONFIG_ENTRY_HOST: TEST_HOST, } From 462e2a8ea1a9461389b2c286c072d38e0738aad2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 8 Nov 2022 04:03:37 -0600 Subject: [PATCH 0285/1033] Fix HomeKit reset accessory procedure (#81573) fixes https://github.com/home-assistant/core/issues/81571 --- homeassistant/components/homekit/__init__.py | 28 ++++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index b809f6db205..333d6052d17 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -10,8 +10,10 @@ import os from typing import Any, cast from aiohttp import web +from pyhap.characteristic import Characteristic from pyhap.const import STANDALONE_AID from pyhap.loader import get_loader +from pyhap.service import Service import voluptuous as vol from zeroconf.asyncio import AsyncZeroconf @@ -139,7 +141,7 @@ STATUS_WAIT = 3 PORT_CLEANUP_CHECK_INTERVAL_SECS = 1 _HOMEKIT_CONFIG_UPDATE_TIME = ( - 5 # number of seconds to wait for homekit to see the c# change + 10 # number of seconds to wait for homekit to see the c# change ) @@ -529,6 +531,7 @@ class HomeKit: self.status = STATUS_READY self.driver: HomeDriver | None = None self.bridge: HomeBridge | None = None + self._reset_lock = asyncio.Lock() def setup(self, async_zeroconf_instance: AsyncZeroconf, uuid: str) -> None: """Set up bridge and accessory driver.""" @@ -558,21 +561,24 @@ class HomeKit: async def async_reset_accessories(self, entity_ids: Iterable[str]) -> None: """Reset the accessory to load the latest configuration.""" - if not self.bridge: - await self.async_reset_accessories_in_accessory_mode(entity_ids) - return - await self.async_reset_accessories_in_bridge_mode(entity_ids) + async with self._reset_lock: + if not self.bridge: + await self.async_reset_accessories_in_accessory_mode(entity_ids) + return + await self.async_reset_accessories_in_bridge_mode(entity_ids) async def _async_shutdown_accessory(self, accessory: HomeAccessory) -> None: """Shutdown an accessory.""" assert self.driver is not None await accessory.stop() # Deallocate the IIDs for the accessory - iid_manager = self.driver.iid_manager - for service in accessory.services: - iid_manager.remove_iid(iid_manager.remove_obj(service)) - for char in service.characteristics: - iid_manager.remove_iid(iid_manager.remove_obj(char)) + iid_manager = accessory.iid_manager + services: list[Service] = accessory.services + for service in services: + iid_manager.remove_obj(service) + characteristics: list[Characteristic] = service.characteristics + for char in characteristics: + iid_manager.remove_obj(char) async def async_reset_accessories_in_accessory_mode( self, entity_ids: Iterable[str] @@ -581,7 +587,6 @@ class HomeKit: assert self.driver is not None acc = cast(HomeAccessory, self.driver.accessory) - await self._async_shutdown_accessory(acc) if acc.entity_id not in entity_ids: return if not (state := self.hass.states.get(acc.entity_id)): @@ -589,6 +594,7 @@ class HomeKit: "The underlying entity %s disappeared during reset", acc.entity_id ) return + await self._async_shutdown_accessory(acc) if new_acc := self._async_create_single_accessory([state]): self.driver.accessory = new_acc self.hass.async_add_job(new_acc.run) From 11034f56dc2167e14f31d93128f8d46d748baef1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 8 Nov 2022 04:04:24 -0600 Subject: [PATCH 0286/1033] Ensure HomeKit temperature controls appear before fan controls on thermostat accessories (#81586) --- homeassistant/components/homekit/type_thermostats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/homekit/type_thermostats.py b/homeassistant/components/homekit/type_thermostats.py index a8c7a53718a..a924548816b 100644 --- a/homeassistant/components/homekit/type_thermostats.py +++ b/homeassistant/components/homekit/type_thermostats.py @@ -306,7 +306,7 @@ class Thermostat(HomeAccessory): if attributes.get(ATTR_HVAC_ACTION) is not None: self.fan_chars.append(CHAR_CURRENT_FAN_STATE) serv_fan = self.add_preload_service(SERV_FANV2, self.fan_chars) - serv_fan.add_linked_service(serv_thermostat) + serv_thermostat.add_linked_service(serv_fan) self.char_active = serv_fan.configure_char( CHAR_ACTIVE, value=1, setter_callback=self._set_fan_active ) From 53c1c2eb56558eb934ee37efbd8ceb59bd702cab Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 8 Nov 2022 04:15:16 -0600 Subject: [PATCH 0287/1033] Fix homekit bridge iid allocations (#81613) fixes undefined --- homeassistant/components/homekit/__init__.py | 10 +- .../components/homekit/accessories.py | 8 +- .../components/homekit/diagnostics.py | 2 + .../components/homekit/iidmanager.py | 72 ++++- tests/components/homekit/conftest.py | 8 +- tests/components/homekit/fixtures/iids_v1 | 249 ++++++++++++++++ .../homekit/fixtures/iids_v1_with_underscore | 50 ++++ tests/components/homekit/fixtures/iids_v2 | 273 ++++++++++++++++++ .../homekit/fixtures/iids_v2_with_underscore | 54 ++++ tests/components/homekit/test_accessories.py | 22 +- tests/components/homekit/test_diagnostics.py | 28 ++ tests/components/homekit/test_homekit.py | 6 +- tests/components/homekit/test_iidmanager.py | 83 +++++- 13 files changed, 806 insertions(+), 59 deletions(-) create mode 100644 tests/components/homekit/fixtures/iids_v1 create mode 100644 tests/components/homekit/fixtures/iids_v1_with_underscore create mode 100644 tests/components/homekit/fixtures/iids_v2 create mode 100644 tests/components/homekit/fixtures/iids_v2_with_underscore diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 333d6052d17..ca73c7dc242 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -76,13 +76,7 @@ from . import ( # noqa: F401 type_switches, type_thermostats, ) -from .accessories import ( - HomeAccessory, - HomeBridge, - HomeDriver, - HomeIIDManager, - get_accessory, -) +from .accessories import HomeAccessory, HomeBridge, HomeDriver, get_accessory from .aidmanager import AccessoryAidStorage from .const import ( ATTR_INTEGRATION, @@ -551,7 +545,7 @@ class HomeKit: async_zeroconf_instance=async_zeroconf_instance, zeroconf_server=f"{uuid}-hap.local.", loader=get_loader(), - iid_manager=HomeIIDManager(self.iid_storage), + iid_storage=self.iid_storage, ) # If we do not load the mac address will be wrong diff --git a/homeassistant/components/homekit/accessories.py b/homeassistant/components/homekit/accessories.py index 7d0de1a5740..3832d9d31c2 100644 --- a/homeassistant/components/homekit/accessories.py +++ b/homeassistant/components/homekit/accessories.py @@ -274,7 +274,7 @@ class HomeAccessory(Accessory): # type: ignore[misc] driver=driver, display_name=cleanup_name_for_homekit(name), aid=aid, - iid_manager=driver.iid_manager, + iid_manager=HomeIIDManager(driver.iid_storage), *args, **kwargs, ) @@ -574,7 +574,7 @@ class HomeBridge(Bridge): # type: ignore[misc] def __init__(self, hass: HomeAssistant, driver: HomeDriver, name: str) -> None: """Initialize a Bridge object.""" - super().__init__(driver, name, iid_manager=driver.iid_manager) + super().__init__(driver, name, iid_manager=HomeIIDManager(driver.iid_storage)) self.set_info_service( firmware_revision=format_version(__version__), manufacturer=MANUFACTURER, @@ -607,7 +607,7 @@ class HomeDriver(AccessoryDriver): # type: ignore[misc] entry_id: str, bridge_name: str, entry_title: str, - iid_manager: HomeIIDManager, + iid_storage: AccessoryIIDStorage, **kwargs: Any, ) -> None: """Initialize a AccessoryDriver object.""" @@ -616,7 +616,7 @@ class HomeDriver(AccessoryDriver): # type: ignore[misc] self._entry_id = entry_id self._bridge_name = bridge_name self._entry_title = entry_title - self.iid_manager = iid_manager + self.iid_storage = iid_storage @pyhap_callback # type: ignore[misc] def pair( diff --git a/homeassistant/components/homekit/diagnostics.py b/homeassistant/components/homekit/diagnostics.py index dbd40c1d6f5..1d0bfb92fcc 100644 --- a/homeassistant/components/homekit/diagnostics.py +++ b/homeassistant/components/homekit/diagnostics.py @@ -31,6 +31,8 @@ async def async_get_config_entry_diagnostics( "options": dict(entry.options), }, } + if homekit.iid_storage: + data["iid_storage"] = homekit.iid_storage.allocations if not homekit.driver: # not started yet or startup failed return data driver: AccessoryDriver = homekit.driver diff --git a/homeassistant/components/homekit/iidmanager.py b/homeassistant/components/homekit/iidmanager.py index 1b5cc7d6722..3805748225a 100644 --- a/homeassistant/components/homekit/iidmanager.py +++ b/homeassistant/components/homekit/iidmanager.py @@ -17,7 +17,7 @@ from homeassistant.helpers.storage import Store from .util import get_iid_storage_filename_for_entry_id -IID_MANAGER_STORAGE_VERSION = 1 +IID_MANAGER_STORAGE_VERSION = 2 IID_MANAGER_SAVE_DELAY = 2 ALLOCATIONS_KEY = "allocations" @@ -26,6 +26,40 @@ IID_MIN = 1 IID_MAX = 18446744073709551615 +ACCESSORY_INFORMATION_SERVICE = "3E" + + +class IIDStorage(Store): + """Storage class for IIDManager.""" + + async def _async_migrate_func( + self, + old_major_version: int, + old_minor_version: int, + old_data: dict, + ): + """Migrate to the new version.""" + if old_major_version == 1: + # Convert v1 to v2 format which uses a unique iid set per accessory + # instead of per pairing since we need the ACCESSORY_INFORMATION_SERVICE + # to always have iid 1 for each bridged accessory as well as the bridge + old_allocations: dict[str, int] = old_data.pop(ALLOCATIONS_KEY, {}) + new_allocation: dict[str, dict[str, int]] = {} + old_data[ALLOCATIONS_KEY] = new_allocation + for allocation_key, iid in old_allocations.items(): + aid_str, new_allocation_key = allocation_key.split("_", 1) + service_type, _, char_type, *_ = new_allocation_key.split("_") + accessory_allocation = new_allocation.setdefault(aid_str, {}) + if service_type == ACCESSORY_INFORMATION_SERVICE and not char_type: + accessory_allocation[new_allocation_key] = 1 + elif iid != 1: + accessory_allocation[new_allocation_key] = iid + + return old_data + + raise NotImplementedError + + class AccessoryIIDStorage: """ Provide stable allocation of IIDs for the lifetime of an accessory. @@ -37,15 +71,15 @@ class AccessoryIIDStorage: def __init__(self, hass: HomeAssistant, entry_id: str) -> None: """Create a new iid store.""" self.hass = hass - self.allocations: dict[str, int] = {} - self.allocated_iids: list[int] = [] + self.allocations: dict[str, dict[str, int]] = {} + self.allocated_iids: dict[str, list[int]] = {} self.entry_id = entry_id - self.store: Store | None = None + self.store: IIDStorage | None = None async def async_initialize(self) -> None: """Load the latest IID data.""" iid_store = get_iid_storage_filename_for_entry_id(self.entry_id) - self.store = Store(self.hass, IID_MANAGER_STORAGE_VERSION, iid_store) + self.store = IIDStorage(self.hass, IID_MANAGER_STORAGE_VERSION, iid_store) if not (raw_storage := await self.store.async_load()): # There is no data about iid allocations yet @@ -53,7 +87,8 @@ class AccessoryIIDStorage: assert isinstance(raw_storage, dict) self.allocations = raw_storage.get(ALLOCATIONS_KEY, {}) - self.allocated_iids = sorted(self.allocations.values()) + for aid_str, allocations in self.allocations.items(): + self.allocated_iids[aid_str] = sorted(allocations.values()) def get_or_allocate_iid( self, @@ -68,16 +103,25 @@ class AccessoryIIDStorage: char_hap_type: str | None = uuid_to_hap_type(char_uuid) if char_uuid else None # Allocation key must be a string since we are saving it to JSON allocation_key = ( - f'{aid}_{service_hap_type}_{service_unique_id or ""}_' + f'{service_hap_type}_{service_unique_id or ""}_' f'{char_hap_type or ""}_{char_unique_id or ""}' ) - if allocation_key in self.allocations: - return self.allocations[allocation_key] - next_iid = self.allocated_iids[-1] + 1 if self.allocated_iids else 1 - self.allocations[allocation_key] = next_iid - self.allocated_iids.append(next_iid) + # AID must be a string since JSON keys cannot be int + aid_str = str(aid) + accessory_allocation = self.allocations.setdefault(aid_str, {}) + accessory_allocated_iids = self.allocated_iids.setdefault(aid_str, []) + if service_hap_type == ACCESSORY_INFORMATION_SERVICE and char_uuid is None: + allocated_iid = 1 + elif allocation_key in accessory_allocation: + return accessory_allocation[allocation_key] + elif accessory_allocated_iids: + allocated_iid = accessory_allocated_iids[-1] + 1 + else: + allocated_iid = 2 + accessory_allocation[allocation_key] = allocated_iid + accessory_allocated_iids.append(allocated_iid) self._async_schedule_save() - return next_iid + return allocated_iid @callback def _async_schedule_save(self) -> None: @@ -91,6 +135,6 @@ class AccessoryIIDStorage: return await self.store.async_save(self._data_to_save()) @callback - def _data_to_save(self) -> dict[str, dict[str, int]]: + def _data_to_save(self) -> dict[str, dict[str, dict[str, int]]]: """Return data of entity map to store in a file.""" return {ALLOCATIONS_KEY: self.allocations} diff --git a/tests/components/homekit/conftest.py b/tests/components/homekit/conftest.py index 7b79e0f9b6b..b0422a40f72 100644 --- a/tests/components/homekit/conftest.py +++ b/tests/components/homekit/conftest.py @@ -6,7 +6,7 @@ from unittest.mock import patch import pytest from homeassistant.components.device_tracker.legacy import YAML_DEVICES -from homeassistant.components.homekit.accessories import HomeDriver, HomeIIDManager +from homeassistant.components.homekit.accessories import HomeDriver from homeassistant.components.homekit.const import BRIDGE_NAME, EVENT_HOMEKIT_CHANGED from homeassistant.components.homekit.iidmanager import AccessoryIIDStorage @@ -39,7 +39,7 @@ def run_driver(hass, loop, iid_storage): entry_id="", entry_title="mock entry", bridge_name=BRIDGE_NAME, - iid_manager=HomeIIDManager(iid_storage), + iid_storage=iid_storage, address="127.0.0.1", loop=loop, ) @@ -63,7 +63,7 @@ def hk_driver(hass, loop, iid_storage): entry_id="", entry_title="mock entry", bridge_name=BRIDGE_NAME, - iid_manager=HomeIIDManager(iid_storage), + iid_storage=iid_storage, address="127.0.0.1", loop=loop, ) @@ -91,7 +91,7 @@ def mock_hap(hass, loop, iid_storage, mock_zeroconf): entry_id="", entry_title="mock entry", bridge_name=BRIDGE_NAME, - iid_manager=HomeIIDManager(iid_storage), + iid_storage=iid_storage, address="127.0.0.1", loop=loop, ) diff --git a/tests/components/homekit/fixtures/iids_v1 b/tests/components/homekit/fixtures/iids_v1 new file mode 100644 index 00000000000..1da11d8de67 --- /dev/null +++ b/tests/components/homekit/fixtures/iids_v1 @@ -0,0 +1,249 @@ +{ + "version": 1, + "minor_version": 1, + "key": "homekit.v1.iids", + "data": { + "allocations": { + "1_3E___": 1, + "1_3E__14_": 2, + "1_3E__20_": 3, + "1_3E__21_": 4, + "1_3E__23_": 5, + "1_3E__30_": 6, + "1_3E__52_": 7, + "1_A2___": 8, + "1_A2__37_": 9, + "935391877_3E___": 10, + "935391877_3E__14_": 11, + "935391877_3E__20_": 12, + "935391877_3E__21_": 13, + "935391877_3E__23_": 14, + "935391877_3E__30_": 15, + "935391877_3E__52_": 16, + "935391877_4A___": 17, + "935391877_4A__F_": 18, + "935391877_4A__33_": 19, + "935391877_4A__11_": 20, + "935391877_4A__35_": 21, + "935391877_4A__36_": 22, + "935391877_4A__D_": 23, + "935391877_4A__12_": 24, + "935391877_4A__34_": 25, + "935391877_4A__10_": 26, + "935391877_B7___": 27, + "935391877_B7__B0_": 28, + "935391877_B7__BF_": 29, + "935391877_B7__AF_": 30, + "985724734_3E___": 31, + "985724734_3E__14_": 32, + "985724734_3E__20_": 33, + "985724734_3E__21_": 34, + "985724734_3E__23_": 35, + "985724734_3E__30_": 36, + "985724734_3E__52_": 37, + "985724734_4A___": 38, + "985724734_4A__F_": 39, + "985724734_4A__33_": 40, + "985724734_4A__11_": 41, + "985724734_4A__35_": 42, + "985724734_4A__36_": 43, + "985724734_4A__D_": 44, + "985724734_4A__12_": 45, + "985724734_4A__34_": 46, + "985724734_4A__10_": 47, + "985724734_B7___": 48, + "985724734_B7__B0_": 49, + "985724734_B7__BF_": 50, + "985724734_B7__AF_": 51, + "3083074204_3E___": 52, + "3083074204_3E__14_": 53, + "3083074204_3E__20_": 54, + "3083074204_3E__21_": 55, + "3083074204_3E__23_": 56, + "3083074204_3E__30_": 57, + "3083074204_3E__52_": 58, + "3083074204_4A___": 59, + "3083074204_4A__F_": 60, + "3083074204_4A__33_": 61, + "3083074204_4A__11_": 62, + "3083074204_4A__35_": 63, + "3083074204_4A__36_": 64, + "3083074204_4A__D_": 65, + "3083074204_4A__12_": 66, + "3083074204_4A__34_": 67, + "3083074204_4A__10_": 68, + "3083074204_B7___": 69, + "3083074204_B7__B0_": 70, + "3083074204_B7__BF_": 71, + "3083074204_B7__AF_": 72, + "3032741347_3E___": 73, + "3032741347_3E__14_": 74, + "3032741347_3E__20_": 75, + "3032741347_3E__21_": 76, + "3032741347_3E__23_": 77, + "3032741347_3E__30_": 78, + "3032741347_3E__52_": 79, + "3032741347_4A___": 80, + "3032741347_4A__F_": 81, + "3032741347_4A__33_": 82, + "3032741347_4A__11_": 83, + "3032741347_4A__35_": 84, + "3032741347_4A__36_": 85, + "3032741347_4A__D_": 86, + "3032741347_4A__12_": 87, + "3032741347_4A__34_": 88, + "3032741347_4A__10_": 89, + "3032741347_B7___": 90, + "3032741347_B7__B0_": 91, + "3032741347_B7__BF_": 92, + "3032741347_B7__AF_": 93, + "3681509609_3E___": 94, + "3681509609_3E__14_": 95, + "3681509609_3E__20_": 96, + "3681509609_3E__21_": 97, + "3681509609_3E__23_": 98, + "3681509609_3E__30_": 99, + "3681509609_3E__52_": 100, + "3681509609_4A___": 101, + "3681509609_4A__F_": 102, + "3681509609_4A__33_": 103, + "3681509609_4A__11_": 104, + "3681509609_4A__35_": 105, + "3681509609_4A__36_": 106, + "3681509609_4A__D_": 107, + "3681509609_4A__12_": 108, + "3681509609_4A__34_": 109, + "3681509609_4A__10_": 110, + "3681509609_B7___": 111, + "3681509609_B7__B0_": 112, + "3681509609_B7__BF_": 113, + "3681509609_B7__AF_": 114, + "3866063418_3E___": 115, + "3866063418_3E__14_": 116, + "3866063418_3E__20_": 117, + "3866063418_3E__21_": 118, + "3866063418_3E__23_": 119, + "3866063418_3E__30_": 120, + "3866063418_3E__52_": 121, + "3866063418_4A___": 122, + "3866063418_4A__F_": 123, + "3866063418_4A__33_": 124, + "3866063418_4A__11_": 125, + "3866063418_4A__35_": 126, + "3866063418_4A__36_": 127, + "3866063418_4A__D_": 128, + "3866063418_4A__12_": 129, + "3866063418_4A__34_": 130, + "3866063418_4A__10_": 131, + "3866063418_B7___": 132, + "3866063418_B7__B0_": 133, + "3866063418_B7__BF_": 134, + "3866063418_B7__AF_": 135, + "3239498961_3E___": 136, + "3239498961_3E__14_": 137, + "3239498961_3E__20_": 138, + "3239498961_3E__21_": 139, + "3239498961_3E__23_": 140, + "3239498961_3E__30_": 141, + "3239498961_3E__52_": 142, + "3239498961_4A___": 143, + "3239498961_4A__F_": 144, + "3239498961_4A__33_": 145, + "3239498961_4A__11_": 146, + "3239498961_4A__35_": 147, + "3239498961_4A__36_": 148, + "3239498961_4A__D_": 149, + "3239498961_4A__12_": 150, + "3239498961_4A__34_": 151, + "3239498961_4A__10_": 152, + "3239498961_B7___": 153, + "3239498961_B7__B0_": 154, + "3239498961_B7__BF_": 155, + "3239498961_B7__AF_": 156, + "3289831818_3E___": 157, + "3289831818_3E__14_": 158, + "3289831818_3E__20_": 159, + "3289831818_3E__21_": 160, + "3289831818_3E__23_": 161, + "3289831818_3E__30_": 162, + "3289831818_3E__52_": 163, + "3289831818_4A___": 164, + "3289831818_4A__F_": 165, + "3289831818_4A__33_": 166, + "3289831818_4A__11_": 167, + "3289831818_4A__35_": 168, + "3289831818_4A__36_": 169, + "3289831818_4A__D_": 170, + "3289831818_4A__12_": 171, + "3289831818_4A__34_": 172, + "3289831818_4A__10_": 173, + "3289831818_B7___": 174, + "3289831818_B7__B0_": 175, + "3289831818_B7__BF_": 176, + "3289831818_B7__AF_": 177, + "3071722771_3E___": 178, + "3071722771_3E__14_": 179, + "3071722771_3E__20_": 180, + "3071722771_3E__21_": 181, + "3071722771_3E__23_": 182, + "3071722771_3E__30_": 183, + "3071722771_3E__52_": 184, + "3071722771_4A___": 185, + "3071722771_4A__F_": 186, + "3071722771_4A__33_": 187, + "3071722771_4A__11_": 188, + "3071722771_4A__35_": 189, + "3071722771_4A__36_": 190, + "3071722771_4A__D_": 191, + "3071722771_4A__12_": 192, + "3071722771_4A__34_": 193, + "3071722771_4A__10_": 194, + "3071722771_B7___": 195, + "3071722771_B7__B0_": 196, + "3071722771_B7__BF_": 197, + "3071722771_B7__AF_": 198, + "3391630365_3E___": 199, + "3391630365_3E__14_": 200, + "3391630365_3E__20_": 201, + "3391630365_3E__21_": 202, + "3391630365_3E__23_": 203, + "3391630365_3E__30_": 204, + "3391630365_3E__52_": 205, + "3391630365_4A___": 206, + "3391630365_4A__F_": 207, + "3391630365_4A__33_": 208, + "3391630365_4A__11_": 209, + "3391630365_4A__35_": 210, + "3391630365_4A__36_": 211, + "3391630365_4A__D_": 212, + "3391630365_4A__12_": 213, + "3391630365_4A__34_": 214, + "3391630365_4A__10_": 215, + "3391630365_B7___": 216, + "3391630365_B7__B0_": 217, + "3391630365_B7__BF_": 218, + "3391630365_B7__AF_": 219, + "3274187032_3E___": 220, + "3274187032_3E__14_": 221, + "3274187032_3E__20_": 222, + "3274187032_3E__21_": 223, + "3274187032_3E__23_": 224, + "3274187032_3E__30_": 225, + "3274187032_3E__52_": 226, + "3274187032_4A___": 227, + "3274187032_4A__F_": 228, + "3274187032_4A__33_": 229, + "3274187032_4A__11_": 230, + "3274187032_4A__35_": 231, + "3274187032_4A__36_": 232, + "3274187032_4A__D_": 233, + "3274187032_4A__12_": 234, + "3274187032_4A__34_": 235, + "3274187032_4A__10_": 236, + "3274187032_B7___": 237, + "3274187032_B7__B0_": 238, + "3274187032_B7__BF_": 239, + "3274187032_B7__AF_": 240 + } + } +} diff --git a/tests/components/homekit/fixtures/iids_v1_with_underscore b/tests/components/homekit/fixtures/iids_v1_with_underscore new file mode 100644 index 00000000000..844c17a4746 --- /dev/null +++ b/tests/components/homekit/fixtures/iids_v1_with_underscore @@ -0,0 +1,50 @@ +{ + "version": 1, + "minor_version": 1, + "key": "homekit.8a47205bd97c07d7a908f10166ebe636.iids", + "data": { + "allocations": { + "1_3E___": 1, + "1_3E__14_": 2, + "1_3E__20_": 3, + "1_3E__21_": 4, + "1_3E__23_": 5, + "1_3E__30_": 6, + "1_3E__52_": 7, + "1_A2___": 8, + "1_A2__37_": 9, + "1973560704_3E___": 10, + "1973560704_3E__14_": 11, + "1973560704_3E__20_": 12, + "1973560704_3E__21_": 13, + "1973560704_3E__23_": 14, + "1973560704_3E__30_": 15, + "1973560704_3E__52_": 16, + "1973560704_3E__53_": 17, + "1973560704_89_pressed-__": 18, + "1973560704_89_pressed-_73_": 19, + "1973560704_89_pressed-_23_": 20, + "1973560704_89_pressed-_CB_": 21, + "1973560704_CC_pressed-__": 22, + "1973560704_CC_pressed-_CD_": 23, + "1973560704_89_changed_states-__": 24, + "1973560704_89_changed_states-_73_": 25, + "1973560704_89_changed_states-_23_": 26, + "1973560704_89_changed_states-_CB_": 27, + "1973560704_CC_changed_states-__": 28, + "1973560704_CC_changed_states-_CD_": 29, + "1973560704_89_turned_off-__": 30, + "1973560704_89_turned_off-_73_": 31, + "1973560704_89_turned_off-_23_": 32, + "1973560704_89_turned_off-_CB_": 33, + "1973560704_CC_turned_off-__": 34, + "1973560704_CC_turned_off-_CD_": 35, + "1973560704_89_turned_on-__": 36, + "1973560704_89_turned_on-_73_": 37, + "1973560704_89_turned_on-_23_": 38, + "1973560704_89_turned_on-_CB_": 39, + "1973560704_CC_turned_on-__": 40, + "1973560704_CC_turned_on-_CD_": 41 + } + } +} \ No newline at end of file diff --git a/tests/components/homekit/fixtures/iids_v2 b/tests/components/homekit/fixtures/iids_v2 new file mode 100644 index 00000000000..76bff55e935 --- /dev/null +++ b/tests/components/homekit/fixtures/iids_v2 @@ -0,0 +1,273 @@ +{ + "version": 2, + "minor_version": 1, + "key": "homekit.v2.iids", + "data": { + "allocations": { + "1": { + "3E___": 1, + "3E__14_": 2, + "3E__20_": 3, + "3E__21_": 4, + "3E__23_": 5, + "3E__30_": 6, + "3E__52_": 7, + "A2___": 8, + "A2__37_": 9 + }, + "935391877": { + "3E___": 1, + "3E__14_": 11, + "3E__20_": 12, + "3E__21_": 13, + "3E__23_": 14, + "3E__30_": 15, + "3E__52_": 16, + "4A___": 17, + "4A__F_": 18, + "4A__33_": 19, + "4A__11_": 20, + "4A__35_": 21, + "4A__36_": 22, + "4A__D_": 23, + "4A__12_": 24, + "4A__34_": 25, + "4A__10_": 26, + "B7___": 27, + "B7__B0_": 28, + "B7__BF_": 29, + "B7__AF_": 30 + }, + "985724734": { + "3E___": 1, + "3E__14_": 32, + "3E__20_": 33, + "3E__21_": 34, + "3E__23_": 35, + "3E__30_": 36, + "3E__52_": 37, + "4A___": 38, + "4A__F_": 39, + "4A__33_": 40, + "4A__11_": 41, + "4A__35_": 42, + "4A__36_": 43, + "4A__D_": 44, + "4A__12_": 45, + "4A__34_": 46, + "4A__10_": 47, + "B7___": 48, + "B7__B0_": 49, + "B7__BF_": 50, + "B7__AF_": 51 + }, + "3083074204": { + "3E___": 1, + "3E__14_": 53, + "3E__20_": 54, + "3E__21_": 55, + "3E__23_": 56, + "3E__30_": 57, + "3E__52_": 58, + "4A___": 59, + "4A__F_": 60, + "4A__33_": 61, + "4A__11_": 62, + "4A__35_": 63, + "4A__36_": 64, + "4A__D_": 65, + "4A__12_": 66, + "4A__34_": 67, + "4A__10_": 68, + "B7___": 69, + "B7__B0_": 70, + "B7__BF_": 71, + "B7__AF_": 72 + }, + "3032741347": { + "3E___": 1, + "3E__14_": 74, + "3E__20_": 75, + "3E__21_": 76, + "3E__23_": 77, + "3E__30_": 78, + "3E__52_": 79, + "4A___": 80, + "4A__F_": 81, + "4A__33_": 82, + "4A__11_": 83, + "4A__35_": 84, + "4A__36_": 85, + "4A__D_": 86, + "4A__12_": 87, + "4A__34_": 88, + "4A__10_": 89, + "B7___": 90, + "B7__B0_": 91, + "B7__BF_": 92, + "B7__AF_": 93 + }, + "3681509609": { + "3E___": 1, + "3E__14_": 95, + "3E__20_": 96, + "3E__21_": 97, + "3E__23_": 98, + "3E__30_": 99, + "3E__52_": 100, + "4A___": 101, + "4A__F_": 102, + "4A__33_": 103, + "4A__11_": 104, + "4A__35_": 105, + "4A__36_": 106, + "4A__D_": 107, + "4A__12_": 108, + "4A__34_": 109, + "4A__10_": 110, + "B7___": 111, + "B7__B0_": 112, + "B7__BF_": 113, + "B7__AF_": 114 + }, + "3866063418": { + "3E___": 1, + "3E__14_": 116, + "3E__20_": 117, + "3E__21_": 118, + "3E__23_": 119, + "3E__30_": 120, + "3E__52_": 121, + "4A___": 122, + "4A__F_": 123, + "4A__33_": 124, + "4A__11_": 125, + "4A__35_": 126, + "4A__36_": 127, + "4A__D_": 128, + "4A__12_": 129, + "4A__34_": 130, + "4A__10_": 131, + "B7___": 132, + "B7__B0_": 133, + "B7__BF_": 134, + "B7__AF_": 135 + }, + "3239498961": { + "3E___": 1, + "3E__14_": 137, + "3E__20_": 138, + "3E__21_": 139, + "3E__23_": 140, + "3E__30_": 141, + "3E__52_": 142, + "4A___": 143, + "4A__F_": 144, + "4A__33_": 145, + "4A__11_": 146, + "4A__35_": 147, + "4A__36_": 148, + "4A__D_": 149, + "4A__12_": 150, + "4A__34_": 151, + "4A__10_": 152, + "B7___": 153, + "B7__B0_": 154, + "B7__BF_": 155, + "B7__AF_": 156 + }, + "3289831818": { + "3E___": 1, + "3E__14_": 158, + "3E__20_": 159, + "3E__21_": 160, + "3E__23_": 161, + "3E__30_": 162, + "3E__52_": 163, + "4A___": 164, + "4A__F_": 165, + "4A__33_": 166, + "4A__11_": 167, + "4A__35_": 168, + "4A__36_": 169, + "4A__D_": 170, + "4A__12_": 171, + "4A__34_": 172, + "4A__10_": 173, + "B7___": 174, + "B7__B0_": 175, + "B7__BF_": 176, + "B7__AF_": 177 + }, + "3071722771": { + "3E___": 1, + "3E__14_": 179, + "3E__20_": 180, + "3E__21_": 181, + "3E__23_": 182, + "3E__30_": 183, + "3E__52_": 184, + "4A___": 185, + "4A__F_": 186, + "4A__33_": 187, + "4A__11_": 188, + "4A__35_": 189, + "4A__36_": 190, + "4A__D_": 191, + "4A__12_": 192, + "4A__34_": 193, + "4A__10_": 194, + "B7___": 195, + "B7__B0_": 196, + "B7__BF_": 197, + "B7__AF_": 198 + }, + "3391630365": { + "3E___": 1, + "3E__14_": 200, + "3E__20_": 201, + "3E__21_": 202, + "3E__23_": 203, + "3E__30_": 204, + "3E__52_": 205, + "4A___": 206, + "4A__F_": 207, + "4A__33_": 208, + "4A__11_": 209, + "4A__35_": 210, + "4A__36_": 211, + "4A__D_": 212, + "4A__12_": 213, + "4A__34_": 214, + "4A__10_": 215, + "B7___": 216, + "B7__B0_": 217, + "B7__BF_": 218, + "B7__AF_": 219 + }, + "3274187032": { + "3E___": 1, + "3E__14_": 221, + "3E__20_": 222, + "3E__21_": 223, + "3E__23_": 224, + "3E__30_": 225, + "3E__52_": 226, + "4A___": 227, + "4A__F_": 228, + "4A__33_": 229, + "4A__11_": 230, + "4A__35_": 231, + "4A__36_": 232, + "4A__D_": 233, + "4A__12_": 234, + "4A__34_": 235, + "4A__10_": 236, + "B7___": 237, + "B7__B0_": 238, + "B7__BF_": 239, + "B7__AF_": 240 + } + } + } +} diff --git a/tests/components/homekit/fixtures/iids_v2_with_underscore b/tests/components/homekit/fixtures/iids_v2_with_underscore new file mode 100644 index 00000000000..52e874e41a5 --- /dev/null +++ b/tests/components/homekit/fixtures/iids_v2_with_underscore @@ -0,0 +1,54 @@ +{ + "version": 2, + "minor_version": 1, + "key": "homekit.8a47205bd97c07d7a908f10166ebe636.iids", + "data": { + "allocations": { + "1": { + "3E___": 1, + "3E__14_": 2, + "3E__20_": 3, + "3E__21_": 4, + "3E__23_": 5, + "3E__30_": 6, + "3E__52_": 7, + "A2___": 8, + "A2__37_": 9 + }, + "1973560704": { + "3E___": 1, + "3E__14_": 11, + "3E__20_": 12, + "3E__21_": 13, + "3E__23_": 14, + "3E__30_": 15, + "3E__52_": 16, + "3E__53_": 17, + "89_pressed-__": 18, + "89_pressed-_73_": 19, + "89_pressed-_23_": 20, + "89_pressed-_CB_": 21, + "CC_pressed-__": 22, + "CC_pressed-_CD_": 23, + "89_changed_states-__": 24, + "89_changed_states-_73_": 25, + "89_changed_states-_23_": 26, + "89_changed_states-_CB_": 27, + "CC_changed_states-__": 28, + "CC_changed_states-_CD_": 29, + "89_turned_off-__": 30, + "89_turned_off-_73_": 31, + "89_turned_off-_23_": 32, + "89_turned_off-_CB_": 33, + "CC_turned_off-__": 34, + "CC_turned_off-_CD_": 35, + "89_turned_on-__": 36, + "89_turned_on-_73_": 37, + "89_turned_on-_23_": 38, + "89_turned_on-_CB_": 39, + "CC_turned_on-__": 40, + "CC_turned_on-_CD_": 41 + } + } + } +} \ No newline at end of file diff --git a/tests/components/homekit/test_accessories.py b/tests/components/homekit/test_accessories.py index 2a0f3f2f718..36eaeff91b4 100644 --- a/tests/components/homekit/test_accessories.py +++ b/tests/components/homekit/test_accessories.py @@ -10,7 +10,6 @@ from homeassistant.components.homekit.accessories import ( HomeAccessory, HomeBridge, HomeDriver, - HomeIIDManager, ) from homeassistant.components.homekit.const import ( ATTR_DISPLAY_NAME, @@ -724,7 +723,7 @@ def test_home_driver(iid_storage): "entry_id", "name", "title", - iid_manager=HomeIIDManager(iid_storage), + iid_storage=iid_storage, address=ip_address, port=port, persist_file=path, @@ -752,22 +751,3 @@ def test_home_driver(iid_storage): mock_unpair.assert_called_with("client_uuid") mock_show_msg.assert_called_with("hass", "entry_id", "title (any)", pin, "X-HM://0") - - -async def test_iid_collision_raises(hass, hk_driver): - """Test iid collision raises. - - If we try to allocate the same IID to the an accessory twice, we should - raise an exception. - """ - - entity_id = "light.accessory" - entity_id2 = "light.accessory2" - - hass.states.async_set(entity_id, STATE_OFF) - hass.states.async_set(entity_id2, STATE_OFF) - - HomeAccessory(hass, hk_driver, "Home Accessory", entity_id, 2, {}) - - with pytest.raises(RuntimeError): - HomeAccessory(hass, hk_driver, "Home Accessory", entity_id2, 2, {}) diff --git a/tests/components/homekit/test_diagnostics.py b/tests/components/homekit/test_diagnostics.py index 1f6f7c584f3..be98c3bacdd 100644 --- a/tests/components/homekit/test_diagnostics.py +++ b/tests/components/homekit/test_diagnostics.py @@ -43,6 +43,19 @@ async def test_config_entry_running(hass, hass_client, hk_driver, mock_async_zer diag = await get_diagnostics_for_config_entry(hass, hass_client, entry) assert diag == { "bridge": {}, + "iid_storage": { + "1": { + "3E__14_": 2, + "3E__20_": 3, + "3E__21_": 4, + "3E__23_": 5, + "3E__30_": 6, + "3E__52_": 7, + "3E___": 1, + "A2__37_": 9, + "A2___": 8, + } + }, "accessories": [ { "aid": 1, @@ -257,6 +270,21 @@ async def test_config_entry_accessory( }, "config_version": 2, "pairing_id": ANY, + "iid_storage": { + "1": { + "3E__14_": 2, + "3E__20_": 3, + "3E__21_": 4, + "3E__23_": 5, + "3E__30_": 6, + "3E__52_": 7, + "3E___": 1, + "43__25_": 11, + "43___": 10, + "A2__37_": 9, + "A2___": 8, + } + }, "status": 1, } with patch("pyhap.accessory_driver.AccessoryDriver.async_start"), patch( diff --git a/tests/components/homekit/test_homekit.py b/tests/components/homekit/test_homekit.py index 21dc94a4b54..d8c02aa98c4 100644 --- a/tests/components/homekit/test_homekit.py +++ b/tests/components/homekit/test_homekit.py @@ -262,7 +262,7 @@ async def test_homekit_setup(hass, hk_driver, mock_async_zeroconf): async_zeroconf_instance=zeroconf_mock, zeroconf_server=f"{uuid}-hap.local.", loader=ANY, - iid_manager=ANY, + iid_storage=ANY, ) assert homekit.driver.safe_mode is False @@ -306,7 +306,7 @@ async def test_homekit_setup_ip_address(hass, hk_driver, mock_async_zeroconf): async_zeroconf_instance=mock_async_zeroconf, zeroconf_server=f"{uuid}-hap.local.", loader=ANY, - iid_manager=ANY, + iid_storage=ANY, ) @@ -350,7 +350,7 @@ async def test_homekit_setup_advertise_ip(hass, hk_driver, mock_async_zeroconf): async_zeroconf_instance=async_zeroconf_instance, zeroconf_server=f"{uuid}-hap.local.", loader=ANY, - iid_manager=ANY, + iid_storage=ANY, ) diff --git a/tests/components/homekit/test_iidmanager.py b/tests/components/homekit/test_iidmanager.py index a791c30a341..3e4a19c9045 100644 --- a/tests/components/homekit/test_iidmanager.py +++ b/tests/components/homekit/test_iidmanager.py @@ -1,6 +1,5 @@ """Tests for the HomeKit IID manager.""" - from uuid import UUID from homeassistant.components.homekit.const import DOMAIN @@ -8,9 +7,10 @@ from homeassistant.components.homekit.iidmanager import ( AccessoryIIDStorage, get_iid_storage_filename_for_entry_id, ) +from homeassistant.helpers.json import json_loads from homeassistant.util.uuid import random_uuid_hex -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, load_fixture async def test_iid_generation_and_restore(hass, iid_storage, hass_storage): @@ -77,9 +77,6 @@ async def test_iid_generation_and_restore(hass, iid_storage, hass_storage): unique_service_unique_char_new_aid_iid1 == unique_service_unique_char_new_aid_iid2 ) - assert unique_service_unique_char_new_aid_iid1 != iid1 - assert unique_service_unique_char_new_aid_iid1 != unique_service_unique_char_iid1 - await iid_storage.async_save() iid_storage2 = AccessoryIIDStorage(hass, entry.entry_id) @@ -99,3 +96,79 @@ async def test_iid_storage_filename(hass, iid_storage, hass_storage): assert iid_storage.store.path.endswith( get_iid_storage_filename_for_entry_id(entry.entry_id) ) + + +async def test_iid_migration_to_v2(hass, iid_storage, hass_storage): + """Test iid storage migration.""" + v1_iids = json_loads(load_fixture("iids_v1", DOMAIN)) + v2_iids = json_loads(load_fixture("iids_v2", DOMAIN)) + hass_storage["homekit.v1.iids"] = v1_iids + hass_storage["homekit.v2.iids"] = v2_iids + + iid_storage_v2 = AccessoryIIDStorage(hass, "v1") + await iid_storage_v2.async_initialize() + + iid_storage_v1 = AccessoryIIDStorage(hass, "v2") + await iid_storage_v1.async_initialize() + + assert iid_storage_v1.allocations == iid_storage_v2.allocations + assert iid_storage_v1.allocated_iids == iid_storage_v2.allocated_iids + + assert len(iid_storage_v2.allocations) == 12 + + for allocations in iid_storage_v2.allocations.values(): + assert allocations["3E___"] == 1 + + +async def test_iid_migration_to_v2_with_underscore(hass, iid_storage, hass_storage): + """Test iid storage migration with underscore.""" + v1_iids = json_loads(load_fixture("iids_v1_with_underscore", DOMAIN)) + v2_iids = json_loads(load_fixture("iids_v2_with_underscore", DOMAIN)) + hass_storage["homekit.v1_with_underscore.iids"] = v1_iids + hass_storage["homekit.v2_with_underscore.iids"] = v2_iids + + iid_storage_v2 = AccessoryIIDStorage(hass, "v1_with_underscore") + await iid_storage_v2.async_initialize() + + iid_storage_v1 = AccessoryIIDStorage(hass, "v2_with_underscore") + await iid_storage_v1.async_initialize() + + assert iid_storage_v1.allocations == iid_storage_v2.allocations + assert iid_storage_v1.allocated_iids == iid_storage_v2.allocated_iids + + assert len(iid_storage_v2.allocations) == 2 + + for allocations in iid_storage_v2.allocations.values(): + assert allocations["3E___"] == 1 + + +async def test_iid_generation_and_restore_v2(hass, iid_storage, hass_storage): + """Test generating iids and restoring them from storage.""" + entry = MockConfigEntry(domain=DOMAIN) + + iid_storage = AccessoryIIDStorage(hass, entry.entry_id) + await iid_storage.async_initialize() + not_accessory_info_service_iid = iid_storage.get_or_allocate_iid( + 1, "000000AA-0000-1000-8000-0026BB765291", None, None, None + ) + assert not_accessory_info_service_iid == 2 + not_accessory_info_service_iid_2 = iid_storage.get_or_allocate_iid( + 1, "000000BB-0000-1000-8000-0026BB765291", None, None, None + ) + assert not_accessory_info_service_iid_2 == 3 + not_accessory_info_service_iid_2 = iid_storage.get_or_allocate_iid( + 1, "000000BB-0000-1000-8000-0026BB765291", None, None, None + ) + assert not_accessory_info_service_iid_2 == 3 + accessory_info_service_iid = iid_storage.get_or_allocate_iid( + 1, "0000003E-0000-1000-8000-0026BB765291", None, None, None + ) + assert accessory_info_service_iid == 1 + accessory_info_service_iid = iid_storage.get_or_allocate_iid( + 1, "0000003E-0000-1000-8000-0026BB765291", None, None, None + ) + assert accessory_info_service_iid == 1 + accessory_info_service_iid = iid_storage.get_or_allocate_iid( + 2, "0000003E-0000-1000-8000-0026BB765291", None, None, None + ) + assert accessory_info_service_iid == 1 From c585817e6719ae019468924f88580920ccbfdc05 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 8 Nov 2022 11:18:58 +0100 Subject: [PATCH 0288/1033] Adjust REST schema validation (#81723) fixes undefined --- homeassistant/components/rest/schema.py | 9 ++++++++- tests/components/rest/test_init.py | 20 +++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/rest/schema.py b/homeassistant/components/rest/schema.py index f881dc8b028..d124ce5789c 100644 --- a/homeassistant/components/rest/schema.py +++ b/homeassistant/components/rest/schema.py @@ -89,6 +89,13 @@ COMBINED_SCHEMA = vol.Schema( ) CONFIG_SCHEMA = vol.Schema( - {DOMAIN: vol.All(cv.ensure_list, [COMBINED_SCHEMA])}, + { + DOMAIN: vol.All( + # convert empty dict to empty list + lambda x: [] if x == {} else x, + cv.ensure_list, + [COMBINED_SCHEMA], + ) + }, extra=vol.ALLOW_EXTRA, ) diff --git a/tests/components/rest/test_init.py b/tests/components/rest/test_init.py index 6dd2650c25c..08d538fd163 100644 --- a/tests/components/rest/test_init.py +++ b/tests/components/rest/test_init.py @@ -19,7 +19,11 @@ from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component from homeassistant.util.dt import utcnow -from tests.common import async_fire_time_changed, get_fixture_path +from tests.common import ( + assert_setup_component, + async_fire_time_changed, + get_fixture_path, +) @respx.mock @@ -400,3 +404,17 @@ async def test_multiple_rest_endpoints(hass: HomeAssistant) -> None: assert hass.states.get("sensor.json_date_time").state == "07:11:08 PM" assert hass.states.get("sensor.json_time").state == "07:11:39 PM" assert hass.states.get("binary_sensor.binary_sensor").state == "on" + + +async def test_empty_config(hass: HomeAssistant) -> None: + """Test setup with empty configuration. + + For example (with rest.yaml an empty file): + rest: !include rest.yaml + """ + assert await async_setup_component( + hass, + DOMAIN, + {DOMAIN: {}}, + ) + assert_setup_component(0, DOMAIN) From 4d1fa42a3c96e098e5c08930f8706313e3484e23 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 8 Nov 2022 04:20:54 -0600 Subject: [PATCH 0289/1033] Use more efficient async_progress_by_handler call in async_start_reauth (#81757) --- homeassistant/config_entries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index b7ee9a4d654..bd985517ca7 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -662,7 +662,7 @@ class ConfigEntry: """Start a reauth flow.""" if any( flow - for flow in hass.config_entries.flow.async_progress() + for flow in hass.config_entries.flow.async_progress_by_handler(self.domain) if flow["context"].get("source") == SOURCE_REAUTH and flow["context"].get("entry_id") == self.entry_id ): From dfab3b26514a4c600e108c11ff1e52534ea0b51c Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Tue, 8 Nov 2022 02:21:13 -0800 Subject: [PATCH 0290/1033] Partially revert google local sync for search cases (#81761) fixes undefined --- homeassistant/components/google/calendar.py | 154 +++++++++++++++----- 1 file changed, 118 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index ca1228759cd..4eb57cff49c 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -3,11 +3,12 @@ from __future__ import annotations import asyncio +from collections.abc import Iterable from datetime import datetime, timedelta import logging from typing import Any -from gcal_sync.api import SyncEventsRequest +from gcal_sync.api import GoogleCalendarService, ListEventsRequest, SyncEventsRequest from gcal_sync.exceptions import ApiException from gcal_sync.model import DateOrDatetime, Event from gcal_sync.store import ScopedCalendarStore @@ -196,21 +197,30 @@ async def async_setup_entry( entity_registry.async_remove( entity_entry.entity_id, ) - request_template = SyncEventsRequest( - calendar_id=calendar_id, - search=data.get(CONF_SEARCH), - start_time=dt_util.now() + SYNC_EVENT_MIN_TIME, - ) - sync = CalendarEventSyncManager( - calendar_service, - store=ScopedCalendarStore(store, unique_id or entity_name), - request_template=request_template, - ) - coordinator = CalendarUpdateCoordinator( - hass, - sync, - data[CONF_NAME], - ) + coordinator: CalendarSyncUpdateCoordinator | CalendarQueryUpdateCoordinator + if search := data.get(CONF_SEARCH): + coordinator = CalendarQueryUpdateCoordinator( + hass, + calendar_service, + data[CONF_NAME], + calendar_id, + search, + ) + else: + request_template = SyncEventsRequest( + calendar_id=calendar_id, + start_time=dt_util.now() + SYNC_EVENT_MIN_TIME, + ) + sync = CalendarEventSyncManager( + calendar_service, + store=ScopedCalendarStore(store, unique_id or entity_name), + request_template=request_template, + ) + coordinator = CalendarSyncUpdateCoordinator( + hass, + sync, + data[CONF_NAME], + ) entities.append( GoogleCalendarEntity( coordinator, @@ -242,8 +252,8 @@ async def async_setup_entry( ) -class CalendarUpdateCoordinator(DataUpdateCoordinator[Timeline]): - """Coordinator for calendar RPC calls.""" +class CalendarSyncUpdateCoordinator(DataUpdateCoordinator[Timeline]): + """Coordinator for calendar RPC calls that use an efficient sync.""" def __init__( self, @@ -251,7 +261,7 @@ class CalendarUpdateCoordinator(DataUpdateCoordinator[Timeline]): sync: CalendarEventSyncManager, name: str, ) -> None: - """Create the Calendar event device.""" + """Create the CalendarSyncUpdateCoordinator.""" super().__init__( hass, _LOGGER, @@ -271,6 +281,87 @@ class CalendarUpdateCoordinator(DataUpdateCoordinator[Timeline]): dt_util.DEFAULT_TIME_ZONE ) + async def async_get_events( + self, start_date: datetime, end_date: datetime + ) -> Iterable[Event]: + """Get all events in a specific time frame.""" + if not self.data: + raise HomeAssistantError( + "Unable to get events: Sync from server has not completed" + ) + return self.data.overlapping( + dt_util.as_local(start_date), + dt_util.as_local(end_date), + ) + + @property + def upcoming(self) -> Iterable[Event] | None: + """Return upcoming events if any.""" + if self.data: + return self.data.active_after(dt_util.now()) + return None + + +class CalendarQueryUpdateCoordinator(DataUpdateCoordinator[list[Event]]): + """Coordinator for calendar RPC calls. + + This sends a polling RPC, not using sync, as a workaround + for limitations in the calendar API for supporting search. + """ + + def __init__( + self, + hass: HomeAssistant, + calendar_service: GoogleCalendarService, + name: str, + calendar_id: str, + search: str | None, + ) -> None: + """Create the CalendarQueryUpdateCoordinator.""" + super().__init__( + hass, + _LOGGER, + name=name, + update_interval=MIN_TIME_BETWEEN_UPDATES, + ) + self.calendar_service = calendar_service + self.calendar_id = calendar_id + self._search = search + + async def async_get_events( + self, start_date: datetime, end_date: datetime + ) -> Iterable[Event]: + """Get all events in a specific time frame.""" + request = ListEventsRequest( + calendar_id=self.calendar_id, + start_time=start_date, + end_time=end_date, + search=self._search, + ) + result_items = [] + try: + result = await self.calendar_service.async_list_events(request) + async for result_page in result: + result_items.extend(result_page.items) + except ApiException as err: + self.async_set_update_error(err) + raise HomeAssistantError(str(err)) from err + return result_items + + async def _async_update_data(self) -> list[Event]: + """Fetch data from API endpoint.""" + request = ListEventsRequest(calendar_id=self.calendar_id, search=self._search) + try: + result = await self.calendar_service.async_list_events(request) + except ApiException as err: + raise UpdateFailed(f"Error communicating with API: {err}") from err + return result.items + + @property + def upcoming(self) -> Iterable[Event] | None: + """Return the next upcoming event if any.""" + return self.data + class GoogleCalendarEntity(CoordinatorEntity, CalendarEntity): """A calendar event entity.""" @@ -279,7 +370,7 @@ class GoogleCalendarEntity(CoordinatorEntity, CalendarEntity): def __init__( self, - coordinator: CalendarUpdateCoordinator, + coordinator: CalendarSyncUpdateCoordinator | CalendarQueryUpdateCoordinator, calendar_id: str, data: dict[str, Any], entity_id: str, @@ -352,14 +443,7 @@ class GoogleCalendarEntity(CoordinatorEntity, CalendarEntity): self, hass: HomeAssistant, start_date: datetime, end_date: datetime ) -> list[CalendarEvent]: """Get all events in a specific time frame.""" - if not (timeline := self.coordinator.data): - raise HomeAssistantError( - "Unable to get events: Sync from server has not completed" - ) - result_items = timeline.overlapping( - dt_util.as_local(start_date), - dt_util.as_local(end_date), - ) + result_items = await self.coordinator.async_get_events(start_date, end_date) return [ _get_calendar_event(event) for event in filter(self._event_filter, result_items) @@ -367,14 +451,12 @@ class GoogleCalendarEntity(CoordinatorEntity, CalendarEntity): def _apply_coordinator_update(self) -> None: """Copy state from the coordinator to this entity.""" - if (timeline := self.coordinator.data) and ( - api_event := next( - filter( - self._event_filter, - timeline.active_after(dt_util.now()), - ), - None, - ) + if api_event := next( + filter( + self._event_filter, + self.coordinator.upcoming or [], + ), + None, ): self._event = _get_calendar_event(api_event) (self._event.summary, self._offset_value) = extract_offset( From d6c10cd887000d80bc9a220a2096f74e229488f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 8 Nov 2022 12:28:36 +0100 Subject: [PATCH 0291/1033] Bump pycfdns from 1.2.2 to 2.0.0 (#81776) --- homeassistant/components/cloudflare/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cloudflare/manifest.json b/homeassistant/components/cloudflare/manifest.json index 73b83c24cce..55e43eb4fde 100644 --- a/homeassistant/components/cloudflare/manifest.json +++ b/homeassistant/components/cloudflare/manifest.json @@ -2,7 +2,7 @@ "domain": "cloudflare", "name": "Cloudflare", "documentation": "https://www.home-assistant.io/integrations/cloudflare", - "requirements": ["pycfdns==1.2.2"], + "requirements": ["pycfdns==2.0.0"], "codeowners": ["@ludeeus", "@ctalkington"], "config_flow": true, "iot_class": "cloud_push", diff --git a/requirements_all.txt b/requirements_all.txt index d8243dad9c6..71c41035267 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1487,7 +1487,7 @@ pybravia==0.2.3 pycarwings2==2.13 # homeassistant.components.cloudflare -pycfdns==1.2.2 +pycfdns==2.0.0 # homeassistant.components.channels pychannels==1.2.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9270aa1a925..0fa8d9274cd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1063,7 +1063,7 @@ pybotvac==0.0.23 pybravia==0.2.3 # homeassistant.components.cloudflare -pycfdns==1.2.2 +pycfdns==2.0.0 # homeassistant.components.cast pychromecast==12.1.4 From 47dba6f6bc7ab1a1877b53818120f1698952c1fe Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 8 Nov 2022 12:55:41 +0100 Subject: [PATCH 0292/1033] Improve MQTT type hints part 5 (#80979) * Improve typing scene * Improve typing select * Improve typing sensor * move expire_after - and class level attrs * Follow up comment * Solve type confict * Remove stale sentinel const * Update homeassistant/components/mqtt/sensor.py Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> * Make _expire_after a class attribute * Code styling Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- homeassistant/components/mqtt/scene.py | 20 ++++-- homeassistant/components/mqtt/select.py | 70 +++++++++++--------- homeassistant/components/mqtt/sensor.py | 85 +++++++++++++++---------- 3 files changed, 103 insertions(+), 72 deletions(-) diff --git a/homeassistant/components/mqtt/scene.py b/homeassistant/components/mqtt/scene.py index e237d70e903..9eafd0cdd99 100644 --- a/homeassistant/components/mqtt/scene.py +++ b/homeassistant/components/mqtt/scene.py @@ -88,8 +88,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT scene.""" async_add_entities([MqttScene(hass, config, config_entry, discovery_data)]) @@ -103,23 +103,29 @@ class MqttScene( _entity_id_format = scene.DOMAIN + ".{}" - def __init__(self, hass, config, config_entry, discovery_data): + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the MQTT scene.""" MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" self._config = config - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" async def async_activate(self, **kwargs: Any) -> None: diff --git a/homeassistant/components/mqtt/select.py b/homeassistant/components/mqtt/select.py index 12593550e2f..6dfe5081e74 100644 --- a/homeassistant/components/mqtt/select.py +++ b/homeassistant/components/mqtt/select.py @@ -1,6 +1,7 @@ """Configure select in a device through MQTT topic.""" from __future__ import annotations +from collections.abc import Callable import functools import logging @@ -34,7 +35,13 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttCommandTemplate, MqttValueTemplate +from .models import ( + MqttCommandTemplate, + MqttValueTemplate, + PublishPayloadType, + ReceiveMessage, + ReceivePayloadType, +) from .util import get_mqtt_data _LOGGER = logging.getLogger(__name__) @@ -103,8 +110,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT select.""" async_add_entities([MqttSelect(hass, config, config_entry, discovery_data)]) @@ -114,53 +121,55 @@ class MqttSelect(MqttEntity, SelectEntity, RestoreEntity): """representation of an MQTT select.""" _entity_id_format = select.ENTITY_ID_FORMAT - _attributes_extra_blocked = MQTT_SELECT_ATTRIBUTES_BLOCKED + _command_template: Callable[[PublishPayloadType], PublishPayloadType] + _value_template: Callable[[ReceivePayloadType], ReceivePayloadType] + _optimistic: bool = False - def __init__(self, hass, config, config_entry, discovery_data): + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the MQTT select.""" - self._config = config - self._optimistic = False - self._sub_state = None - - self._attr_current_option = None - SelectEntity.__init__(self) MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" + self._attr_current_option = None self._optimistic = config[CONF_OPTIMISTIC] self._attr_options = config[CONF_OPTIONS] - self._templates = { - CONF_COMMAND_TEMPLATE: MqttCommandTemplate( - config.get(CONF_COMMAND_TEMPLATE), entity=self - ).async_render, - CONF_VALUE_TEMPLATE: MqttValueTemplate( - config.get(CONF_VALUE_TEMPLATE), - entity=self, - ).async_render_with_possible_json_value, - } + self._command_template = MqttCommandTemplate( + config.get(CONF_COMMAND_TEMPLATE), + entity=self, + ).async_render + self._value_template = MqttValueTemplate( + config.get(CONF_VALUE_TEMPLATE), entity=self + ).async_render_with_possible_json_value - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" @callback @log_messages(self.hass, self.entity_id) - def message_received(msg): + def message_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages.""" - payload = self._templates[CONF_VALUE_TEMPLATE](msg.payload) - + payload = str(self._value_template(msg.payload)) if payload.lower() == "none": - payload = None + self._attr_current_option = None + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + return - if payload is not None and payload not in self.options: + if payload not in self.options: _LOGGER.error( "Invalid option for %s: '%s' (valid options: %s)", self.entity_id, @@ -168,7 +177,6 @@ class MqttSelect(MqttEntity, SelectEntity, RestoreEntity): self.options, ) return - self._attr_current_option = payload get_mqtt_data(self.hass).state_write_requests.write_state_request(self) @@ -189,7 +197,7 @@ class MqttSelect(MqttEntity, SelectEntity, RestoreEntity): }, ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) @@ -198,7 +206,7 @@ class MqttSelect(MqttEntity, SelectEntity, RestoreEntity): async def async_select_option(self, option: str) -> None: """Update the current value.""" - payload = self._templates[CONF_COMMAND_TEMPLATE](option) + payload = self._command_template(option) if self._optimistic: self._attr_current_option = option self.async_write_ha_state() diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index 4c6b5409962..ed65b5a42fe 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -1,9 +1,11 @@ """Support for MQTT sensors.""" from __future__ import annotations -from datetime import timedelta +from collections.abc import Callable +from datetime import datetime, timedelta import functools import logging +from typing import Any import voluptuous as vol @@ -15,6 +17,7 @@ from homeassistant.components.sensor import ( STATE_CLASSES_SCHEMA, RestoreSensor, SensorDeviceClass, + SensorExtraStoredData, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -26,7 +29,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import CALLBACK_TYPE, HomeAssistant, State, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_track_point_in_utc_time @@ -45,7 +48,12 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttValueTemplate, PayloadSentinel, ReceiveMessage +from .models import ( + MqttValueTemplate, + PayloadSentinel, + ReceiveMessage, + ReceivePayloadType, +) from .util import get_mqtt_data, valid_subscribe_topic _LOGGER = logging.getLogger(__name__) @@ -65,7 +73,7 @@ DEFAULT_NAME = "MQTT Sensor" DEFAULT_FORCE_UPDATE = False -def validate_options(conf): +def validate_options(conf: ConfigType) -> ConfigType: """Validate options. If last reset topic is present it must be same as the state topic. @@ -155,8 +163,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT sensor.""" async_add_entities([MqttSensor(hass, config, config_entry, discovery_data)]) @@ -168,24 +176,29 @@ class MqttSensor(MqttEntity, RestoreSensor): _entity_id_format = ENTITY_ID_FORMAT _attr_last_reset = None _attributes_extra_blocked = MQTT_SENSOR_ATTRIBUTES_BLOCKED + _expire_after: int | None + _expired: bool | None + _template: Callable[[ReceivePayloadType, PayloadSentinel], ReceivePayloadType] + _last_reset_template: Callable[[ReceivePayloadType], ReceivePayloadType] - def __init__(self, hass, config, config_entry, discovery_data): + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the sensor.""" - self._expiration_trigger = None - - expire_after = config.get(CONF_EXPIRE_AFTER) - if expire_after is not None and expire_after > 0: - self._expired = True - else: - self._expired = None - + self._expiration_trigger: CALLBACK_TYPE | None = None MqttEntity.__init__(self, hass, config, config_entry, discovery_data) async def mqtt_async_added_to_hass(self) -> None: """Restore state for entities with expire_after set.""" + last_state: State | None + last_sensor_data: SensorExtraStoredData | None if ( - (expire_after := self._config.get(CONF_EXPIRE_AFTER)) is not None - and expire_after > 0 + (_expire_after := self._expire_after) is not None + and _expire_after > 0 and (last_state := await self.async_get_last_state()) is not None and last_state.state not in [STATE_UNKNOWN, STATE_UNAVAILABLE] and (last_sensor_data := await self.async_get_last_sensor_data()) @@ -194,7 +207,7 @@ class MqttSensor(MqttEntity, RestoreSensor): # MqttEntity.async_added_to_hass(), then we should not restore state and not self._expiration_trigger ): - expiration_at = last_state.last_changed + timedelta(seconds=expire_after) + expiration_at = last_state.last_changed + timedelta(seconds=_expire_after) if expiration_at < (time_now := dt_util.utcnow()): # Skip reactivating the sensor _LOGGER.debug("Skip state recovery after reload for %s", self.entity_id) @@ -222,7 +235,7 @@ class MqttSensor(MqttEntity, RestoreSensor): await MqttEntity.async_will_remove_from_hass(self) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA @@ -233,6 +246,12 @@ class MqttSensor(MqttEntity, RestoreSensor): self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT) self._attr_state_class = config.get(CONF_STATE_CLASS) + self._expire_after = config.get(CONF_EXPIRE_AFTER) + if self._expire_after is not None and self._expire_after > 0: + self._expired = True + else: + self._expired = None + self._template = MqttValueTemplate( self._config.get(CONF_VALUE_TEMPLATE), entity=self ).async_render_with_possible_json_value @@ -240,15 +259,14 @@ class MqttSensor(MqttEntity, RestoreSensor): self._config.get(CONF_LAST_RESET_VALUE_TEMPLATE), entity=self ).async_render_with_possible_json_value - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" - topics = {} + topics: dict[str, dict[str, Any]] = {} def _update_state(msg: ReceiveMessage) -> None: # auto-expire enabled? - expire_after = self._config.get(CONF_EXPIRE_AFTER) - if expire_after is not None and expire_after > 0: - # When expire_after is set, and we receive a message, assume device is not expired since it has to be to receive the message + if self._expire_after is not None and self._expire_after > 0: + # When self._expire_after is set, and we receive a message, assume device is not expired since it has to be to receive the message self._expired = False # Reset old trigger @@ -256,13 +274,13 @@ class MqttSensor(MqttEntity, RestoreSensor): self._expiration_trigger() # Set new trigger - expiration_at = dt_util.utcnow() + timedelta(seconds=expire_after) + expiration_at = dt_util.utcnow() + timedelta(seconds=self._expire_after) self._expiration_trigger = async_track_point_in_utc_time( self.hass, self._value_is_expired, expiration_at ) - payload = self._template(msg.payload, default=PayloadSentinel.DEFAULT) + payload = self._template(msg.payload, PayloadSentinel.DEFAULT) if payload is PayloadSentinel.DEFAULT: return if self.device_class not in { @@ -282,14 +300,14 @@ class MqttSensor(MqttEntity, RestoreSensor): return self._attr_native_value = payload_datetime - def _update_last_reset(msg): + def _update_last_reset(msg: ReceiveMessage) -> None: payload = self._last_reset_template(msg.payload) if not payload: _LOGGER.debug("Ignoring empty last_reset message from '%s'", msg.topic) return try: - last_reset = dt_util.parse_datetime(payload) + last_reset = dt_util.parse_datetime(str(payload)) if last_reset is None: raise ValueError self._attr_last_reset = last_reset @@ -300,7 +318,7 @@ class MqttSensor(MqttEntity, RestoreSensor): @callback @log_messages(self.hass, self.entity_id) - def message_received(msg): + def message_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages.""" _update_state(msg) if CONF_LAST_RESET_VALUE_TEMPLATE in self._config and ( @@ -319,7 +337,7 @@ class MqttSensor(MqttEntity, RestoreSensor): @callback @log_messages(self.hass, self.entity_id) - def last_reset_message_received(msg): + def last_reset_message_received(msg: ReceiveMessage) -> None: """Handle new last_reset messages.""" _update_last_reset(msg) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) @@ -339,12 +357,12 @@ class MqttSensor(MqttEntity, RestoreSensor): self.hass, self._sub_state, topics ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) @callback - def _value_is_expired(self, *_): + def _value_is_expired(self, *_: datetime) -> None: """Triggered when value is expired.""" self._expiration_trigger = None self._expired = True @@ -353,8 +371,7 @@ class MqttSensor(MqttEntity, RestoreSensor): @property def available(self) -> bool: """Return true if the device is available and value has not expired.""" - expire_after = self._config.get(CONF_EXPIRE_AFTER) # mypy doesn't know about fget: https://github.com/python/mypy/issues/6185 return MqttAvailability.available.fget(self) and ( # type: ignore[attr-defined] - expire_after is None or not self._expired + self._expire_after is None or not self._expired ) From 4293c88fb664f9fce73974da4a279decd450dd07 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 8 Nov 2022 13:11:45 +0100 Subject: [PATCH 0293/1033] Improve MQTT type hints part 6 (#81001) * Improve typing siren * Improve typing switch * Set siren type hints at class level * Set switch type hints at class level * Follow up comment * Improve hints on siren templates * Another cleanup for siren * Follow up comment * Follow up comment --- homeassistant/components/mqtt/siren.py | 109 +++++++++++++----------- homeassistant/components/mqtt/switch.py | 36 +++++--- 2 files changed, 83 insertions(+), 62 deletions(-) diff --git a/homeassistant/components/mqtt/siren.py b/homeassistant/components/mqtt/siren.py index 2ab226e44c0..4a69977df45 100644 --- a/homeassistant/components/mqtt/siren.py +++ b/homeassistant/components/mqtt/siren.py @@ -1,10 +1,11 @@ """Support for MQTT sirens.""" from __future__ import annotations +from collections.abc import Callable import copy import functools import logging -from typing import Any +from typing import Any, cast import voluptuous as vol @@ -17,6 +18,7 @@ from homeassistant.components.siren import ( TURN_ON_SCHEMA, SirenEntity, SirenEntityFeature, + SirenTurnOnServiceParameters, process_turn_on_params, ) from homeassistant.config_entries import ConfigEntry @@ -30,7 +32,8 @@ from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.json import JSON_DECODE_EXCEPTIONS, json_dumps, json_loads -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.helpers.template import Template +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, TemplateVarsType from . import subscription from .config import MQTT_RW_SCHEMA @@ -53,7 +56,13 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttCommandTemplate, MqttValueTemplate +from .models import ( + MqttCommandTemplate, + MqttValueTemplate, + PublishPayloadType, + ReceiveMessage, + ReceivePayloadType, +) from .util import get_mqtt_data DEFAULT_NAME = "MQTT Siren" @@ -150,8 +159,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT siren.""" async_add_entities([MqttSiren(hass, config, config_entry, discovery_data)]) @@ -162,29 +171,32 @@ class MqttSiren(MqttEntity, SirenEntity): _entity_id_format = ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_SIREN_ATTRIBUTES_BLOCKED + _attr_supported_features: int - def __init__(self, hass, config, config_entry, discovery_data): + _command_templates: dict[ + str, Callable[[PublishPayloadType, TemplateVarsType], PublishPayloadType] | None + ] + _value_template: Callable[[ReceivePayloadType], ReceivePayloadType] + _state_on: str + _state_off: str + _optimistic: bool + + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the MQTT siren.""" - self._attr_name = config[CONF_NAME] - self._attr_should_poll = False - self._supported_features = SUPPORTED_BASE - self._attr_is_on = None - self._state_on = None - self._state_off = None - self._optimistic = None - - self._attr_extra_state_attributes: dict[str, Any] = {} - - self.target = None - - super().__init__(hass, config, config_entry, discovery_data) + MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" state_on = config.get(CONF_STATE_ON) @@ -193,25 +205,29 @@ class MqttSiren(MqttEntity, SirenEntity): state_off = config.get(CONF_STATE_OFF) self._state_off = state_off if state_off else config[CONF_PAYLOAD_OFF] + self._attr_extra_state_attributes = {} + + _supported_features: int = SUPPORTED_BASE if config[CONF_SUPPORT_DURATION]: - self._supported_features |= SirenEntityFeature.DURATION + _supported_features |= SirenEntityFeature.DURATION self._attr_extra_state_attributes[ATTR_DURATION] = None if config.get(CONF_AVAILABLE_TONES): - self._supported_features |= SirenEntityFeature.TONES + _supported_features |= SirenEntityFeature.TONES self._attr_available_tones = config[CONF_AVAILABLE_TONES] self._attr_extra_state_attributes[ATTR_TONE] = None if config[CONF_SUPPORT_VOLUME_SET]: - self._supported_features |= SirenEntityFeature.VOLUME_SET + _supported_features |= SirenEntityFeature.VOLUME_SET self._attr_extra_state_attributes[ATTR_VOLUME_LEVEL] = None + self._attr_supported_features = _supported_features self._optimistic = config[CONF_OPTIMISTIC] or CONF_STATE_TOPIC not in config self._attr_is_on = False if self._optimistic else None - command_template = config.get(CONF_COMMAND_TEMPLATE) - command_off_template = config.get(CONF_COMMAND_OFF_TEMPLATE) or config.get( - CONF_COMMAND_TEMPLATE + command_template: Template | None = config.get(CONF_COMMAND_TEMPLATE) + command_off_template: Template | None = ( + config.get(CONF_COMMAND_OFF_TEMPLATE) or command_template ) self._command_templates = { CONF_COMMAND_TEMPLATE: MqttCommandTemplate( @@ -230,12 +246,12 @@ class MqttSiren(MqttEntity, SirenEntity): entity=self, ).async_render_with_possible_json_value - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" @callback @log_messages(self.hass, self.entity_id) - def state_message_received(msg): + def state_message_received(msg: ReceiveMessage) -> None: """Handle new MQTT state messages.""" payload = self._value_template(msg.payload) if not payload or payload == PAYLOAD_EMPTY_JSON: @@ -245,7 +261,7 @@ class MqttSiren(MqttEntity, SirenEntity): msg.topic, ) return - json_payload = {} + json_payload: dict[str, Any] = {} if payload in [self._state_on, self._state_off, PAYLOAD_NONE]: json_payload = {STATE: payload} else: @@ -275,7 +291,8 @@ class MqttSiren(MqttEntity, SirenEntity): if json_payload: # process attributes try: - vol.All(TURN_ON_SCHEMA)(json_payload) + params: SirenTurnOnServiceParameters + params = vol.All(TURN_ON_SCHEMA)(json_payload) except vol.MultipleInvalid as invalid_siren_parameters: _LOGGER.warning( "Unable to update siren state attributes from payload '%s': %s", @@ -283,7 +300,7 @@ class MqttSiren(MqttEntity, SirenEntity): invalid_siren_parameters, ) return - self._update(process_turn_on_params(self, json_payload)) + self._update(process_turn_on_params(self, params)) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) if self._config.get(CONF_STATE_TOPIC) is None: @@ -303,7 +320,7 @@ class MqttSiren(MqttEntity, SirenEntity): }, ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) @@ -322,11 +339,6 @@ class MqttSiren(MqttEntity, SirenEntity): attributes.update(self._attr_extra_state_attributes) return attributes - @property - def supported_features(self) -> int: - """Flag supported features.""" - return self._supported_features - async def _async_publish( self, topic: str, @@ -335,15 +347,14 @@ class MqttSiren(MqttEntity, SirenEntity): variables: dict[str, Any] | None = None, ) -> None: """Publish MQTT payload with optional command template.""" - template_variables = {STATE: value} + template_variables: dict[str, Any] = {STATE: value} if variables is not None: template_variables.update(variables) - payload = ( - self._command_templates[template](value, template_variables) - if self._command_templates[template] - else json_dumps(template_variables) - ) - if payload and payload not in PAYLOAD_NONE: + if command_template := self._command_templates[template]: + payload = command_template(value, template_variables) + else: + payload = json_dumps(template_variables) + if payload and str(payload) != PAYLOAD_NONE: await self.async_publish( self._config[topic], payload, @@ -367,7 +378,7 @@ class MqttSiren(MqttEntity, SirenEntity): # Optimistically assume that siren has changed state. _LOGGER.debug("Writing state attributes %s", kwargs) self._attr_is_on = True - self._update(kwargs) + self._update(cast(SirenTurnOnServiceParameters, kwargs)) self.async_write_ha_state() async def async_turn_off(self, **kwargs: Any) -> None: @@ -386,8 +397,8 @@ class MqttSiren(MqttEntity, SirenEntity): self._attr_is_on = False self.async_write_ha_state() - def _update(self, data: dict[str, Any]) -> None: + def _update(self, data: SirenTurnOnServiceParameters) -> None: """Update the extra siren state attributes.""" for attribute, support in SUPPORTED_ATTRIBUTES.items(): - if self._supported_features & support and attribute in data: - self._attr_extra_state_attributes[attribute] = data[attribute] + if self._attr_supported_features & support and attribute in data: + self._attr_extra_state_attributes[attribute] = data[attribute] # type: ignore[literal-required] diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index f2a40facd8b..a20603e2399 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -1,6 +1,7 @@ """Support for MQTT switches.""" from __future__ import annotations +from collections.abc import Callable import functools from typing import Any @@ -22,6 +23,7 @@ from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity +from homeassistant.helpers.service_info.mqtt import ReceivePayloadType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import subscription @@ -42,7 +44,7 @@ from .mixins import ( async_setup_platform_helper, warn_for_legacy_schema, ) -from .models import MqttValueTemplate +from .models import MqttValueTemplate, ReceiveMessage from .util import get_mqtt_data DEFAULT_NAME = "MQTT Switch" @@ -107,8 +109,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT switch.""" async_add_entities([MqttSwitch(hass, config, config_entry, discovery_data)]) @@ -119,16 +121,24 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity): _entity_id_format = switch.ENTITY_ID_FORMAT - def __init__(self, hass, config, config_entry, discovery_data): + _optimistic: bool + _state_on: str + _state_off: str + _value_template: Callable[[ReceivePayloadType], ReceivePayloadType] + + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the MQTT switch.""" - self._state_on = None - self._state_off = None - self._optimistic = None MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA @@ -136,10 +146,10 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity): """(Re)Setup the entity.""" self._attr_device_class = config.get(CONF_DEVICE_CLASS) - state_on = config.get(CONF_STATE_ON) + state_on: str | None = config.get(CONF_STATE_ON) self._state_on = state_on if state_on else config[CONF_PAYLOAD_ON] - state_off = config.get(CONF_STATE_OFF) + state_off: str | None = config.get(CONF_STATE_OFF) self._state_off = state_off if state_off else config[CONF_PAYLOAD_OFF] self._optimistic = ( @@ -150,12 +160,12 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity): self._config.get(CONF_VALUE_TEMPLATE), entity=self ).async_render_with_possible_json_value - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" @callback @log_messages(self.hass, self.entity_id) - def state_message_received(msg): + def state_message_received(msg: ReceiveMessage) -> None: """Handle new MQTT state messages.""" payload = self._value_template(msg.payload) if payload == self._state_on: @@ -184,7 +194,7 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity): }, ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) From 9b0b8ae9c07ce3b1c04a2c498eb255415793c729 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 8 Nov 2022 13:58:47 +0100 Subject: [PATCH 0294/1033] Add short-hand attributes to vacuum (#81782) Add attributes to vacuum --- homeassistant/components/vacuum/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py index 2942078a875..4488f8062d6 100644 --- a/homeassistant/components/vacuum/__init__.py +++ b/homeassistant/components/vacuum/__init__.py @@ -318,11 +318,12 @@ class VacuumEntity(_BaseVacuum, ToggleEntity): """Representation of a vacuum cleaner robot.""" entity_description: VacuumEntityDescription + _attr_status: str | None = None @property def status(self) -> str | None: """Return the status of the vacuum cleaner.""" - return None + return self._attr_status @property def battery_icon(self) -> str: @@ -394,11 +395,12 @@ class StateVacuumEntity(_BaseVacuum): """Representation of a vacuum cleaner robot that supports states.""" entity_description: StateVacuumEntityDescription + _attr_state: str | None = None @property def state(self) -> str | None: """Return the state of the vacuum cleaner.""" - return None + return self._attr_state @property def battery_icon(self) -> str: From b7533aff486e6902feda1febf30f775f6fbb8ec2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 8 Nov 2022 07:26:16 -0600 Subject: [PATCH 0295/1033] Replace led-ble util with bluetooth-data-tools (#81093) --- .coveragerc | 1 - .../components/bluetooth/manifest.json | 1 + .../components/led_ble/config_flow.py | 2 +- .../components/led_ble/manifest.json | 2 +- homeassistant/components/led_ble/util.py | 51 ------------------- homeassistant/package_constraints.txt | 1 + requirements_all.txt | 4 ++ requirements_test_all.txt | 4 ++ 8 files changed, 12 insertions(+), 54 deletions(-) delete mode 100644 homeassistant/components/led_ble/util.py diff --git a/.coveragerc b/.coveragerc index 84f061fbd35..1a8bfb462d8 100644 --- a/.coveragerc +++ b/.coveragerc @@ -678,7 +678,6 @@ omit = homeassistant/components/lcn/services.py homeassistant/components/led_ble/__init__.py homeassistant/components/led_ble/light.py - homeassistant/components/led_ble/util.py homeassistant/components/lg_netcast/media_player.py homeassistant/components/lg_soundbar/media_player.py homeassistant/components/lidarr/__init__.py diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 923ab248a2d..cc7237d4585 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,6 +10,7 @@ "bleak-retry-connector==2.8.3", "bluetooth-adapters==0.7.0", "bluetooth-auto-recovery==0.3.6", + "bluetooth-data-tools==0.2.0", "dbus-fast==1.72.0" ], "codeowners": ["@bdraco"], diff --git a/homeassistant/components/led_ble/config_flow.py b/homeassistant/components/led_ble/config_flow.py index 19be92f6647..d757b5021af 100644 --- a/homeassistant/components/led_ble/config_flow.py +++ b/homeassistant/components/led_ble/config_flow.py @@ -4,6 +4,7 @@ from __future__ import annotations import logging from typing import Any +from bluetooth_data_tools import human_readable_name from led_ble import BLEAK_EXCEPTIONS, LEDBLE import voluptuous as vol @@ -16,7 +17,6 @@ from homeassistant.const import CONF_ADDRESS from homeassistant.data_entry_flow import FlowResult from .const import DOMAIN, LOCAL_NAMES, UNSUPPORTED_SUB_MODEL -from .util import human_readable_name _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/led_ble/manifest.json b/homeassistant/components/led_ble/manifest.json index 6802eea9bc7..d59570c6257 100644 --- a/homeassistant/components/led_ble/manifest.json +++ b/homeassistant/components/led_ble/manifest.json @@ -3,7 +3,7 @@ "name": "LED BLE", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/led_ble/", - "requirements": ["led-ble==1.0.0"], + "requirements": ["bluetooth-data-tools==0.2.0", "led-ble==1.0.0"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [ diff --git a/homeassistant/components/led_ble/util.py b/homeassistant/components/led_ble/util.py deleted file mode 100644 index e43655e2905..00000000000 --- a/homeassistant/components/led_ble/util.py +++ /dev/null @@ -1,51 +0,0 @@ -"""The yalexs_ble integration models.""" -from __future__ import annotations - -from homeassistant.components.bluetooth import ( - BluetoothScanningMode, - BluetoothServiceInfoBleak, - async_discovered_service_info, - async_process_advertisements, -) -from homeassistant.components.bluetooth.match import ADDRESS, BluetoothCallbackMatcher -from homeassistant.core import HomeAssistant, callback - -from .const import DEVICE_TIMEOUT - - -@callback -def async_find_existing_service_info( - hass: HomeAssistant, local_name: str, address: str -) -> BluetoothServiceInfoBleak | None: - """Return the service info for the given local_name and address.""" - for service_info in async_discovered_service_info(hass): - device = service_info.device - if device.address == address: - return service_info - return None - - -async def async_get_service_info( - hass: HomeAssistant, local_name: str, address: str -) -> BluetoothServiceInfoBleak: - """Wait for the service info for the given local_name and address.""" - if service_info := async_find_existing_service_info(hass, local_name, address): - return service_info - return await async_process_advertisements( - hass, - lambda service_info: True, - BluetoothCallbackMatcher({ADDRESS: address}), - BluetoothScanningMode.ACTIVE, - DEVICE_TIMEOUT, - ) - - -def short_address(address: str) -> str: - """Convert a Bluetooth address to a short address.""" - split_address = address.replace("-", ":").split(":") - return f"{split_address[-2].upper()}{split_address[-1].upper()}"[-4:] - - -def human_readable_name(name: str | None, local_name: str, address: str) -> str: - """Return a human readable name for the given name, local_name, and address.""" - return f"{name or local_name} ({short_address(address)})" diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 2aa2179a890..4a6b8b076f7 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -14,6 +14,7 @@ bleak-retry-connector==2.8.3 bleak==0.19.2 bluetooth-adapters==0.7.0 bluetooth-auto-recovery==0.3.6 +bluetooth-data-tools==0.2.0 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.3 diff --git a/requirements_all.txt b/requirements_all.txt index 71c41035267..512c366f5dc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -452,6 +452,10 @@ bluetooth-adapters==0.7.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.3.6 +# homeassistant.components.bluetooth +# homeassistant.components.led_ble +bluetooth-data-tools==0.2.0 + # homeassistant.components.bond bond-async==0.1.22 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0fa8d9274cd..bc44c0c0b6b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -366,6 +366,10 @@ bluetooth-adapters==0.7.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.3.6 +# homeassistant.components.bluetooth +# homeassistant.components.led_ble +bluetooth-data-tools==0.2.0 + # homeassistant.components.bond bond-async==0.1.22 From 2082026ff5240d89855ca63355043e0e12ef3e98 Mon Sep 17 00:00:00 2001 From: chpego <38792705+chpego@users.noreply.github.com> Date: Tue, 8 Nov 2022 13:39:53 +0000 Subject: [PATCH 0296/1033] Fix Fully Kiosk start application service field (#81738) Fix attributes services to start_application --- homeassistant/components/fully_kiosk/services.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/fully_kiosk/services.yaml b/homeassistant/components/fully_kiosk/services.yaml index b8ea6b371d7..88178e35809 100644 --- a/homeassistant/components/fully_kiosk/services.yaml +++ b/homeassistant/components/fully_kiosk/services.yaml @@ -20,7 +20,7 @@ start_application: device: integration: fully_kiosk fields: - url: + application: name: Application description: Package name of the application to start. example: "de.ozerov.fully" From 0c8eeaa6436b04ba6da46bccab8b11523f314d9b Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 8 Nov 2022 14:41:39 +0100 Subject: [PATCH 0297/1033] Update mypy to 0.990 (#81783) * Update mypy to 0.990 * Remove type ignore - overriding attr with property (13475) * Remove type ignores - hasattr (13544) * Adjust type ignore - assignment (13549) * New error code - type-abstract (13785) * Disable annotation-unchecked (13851) --- homeassistant/auth/__init__.py | 3 +-- homeassistant/auth/mfa_modules/__init__.py | 1 - homeassistant/auth/providers/__init__.py | 4 +--- homeassistant/components/climate/__init__.py | 4 ++-- homeassistant/components/knx/__init__.py | 4 ++-- homeassistant/components/knx/schema.py | 4 ++-- .../components/media_player/__init__.py | 16 ++++------------ homeassistant/components/notify/legacy.py | 2 +- homeassistant/components/recorder/models.py | 6 +++--- homeassistant/helpers/entity.py | 4 ++-- homeassistant/util/yaml/dumper.py | 4 +++- homeassistant/util/yaml/loader.py | 2 +- mypy.ini | 1 + requirements_test.txt | 2 +- script/hassfest/mypy_config.py | 3 ++- 15 files changed, 26 insertions(+), 34 deletions(-) diff --git a/homeassistant/auth/__init__.py b/homeassistant/auth/__init__.py index 12511a7f4a5..bbd23983e2b 100644 --- a/homeassistant/auth/__init__.py +++ b/homeassistant/auth/__init__.py @@ -356,8 +356,7 @@ class AuthManager: provider = self._async_get_auth_provider(credentials) if provider is not None and hasattr(provider, "async_will_remove_credentials"): - # https://github.com/python/mypy/issues/1424 - await provider.async_will_remove_credentials(credentials) # type: ignore[attr-defined] + await provider.async_will_remove_credentials(credentials) await self._store.async_remove_credentials(credentials) diff --git a/homeassistant/auth/mfa_modules/__init__.py b/homeassistant/auth/mfa_modules/__init__.py index 61c36da6e90..ebfe1332cd2 100644 --- a/homeassistant/auth/mfa_modules/__init__.py +++ b/homeassistant/auth/mfa_modules/__init__.py @@ -166,7 +166,6 @@ async def _load_mfa_module(hass: HomeAssistant, module_name: str) -> types.Modul processed = hass.data[DATA_REQS] = set() - # https://github.com/python/mypy/issues/1424 await requirements.async_process_requirements( hass, module_path, module.REQUIREMENTS ) diff --git a/homeassistant/auth/providers/__init__.py b/homeassistant/auth/providers/__init__.py index 6feb4b26759..2448225a284 100644 --- a/homeassistant/auth/providers/__init__.py +++ b/homeassistant/auth/providers/__init__.py @@ -250,9 +250,7 @@ class LoginFlow(data_entry_flow.FlowHandler): auth_module, "async_initialize_login_mfa_step" ): try: - await auth_module.async_initialize_login_mfa_step( # type: ignore[attr-defined] - self.user.id - ) + await auth_module.async_initialize_login_mfa_step(self.user.id) except HomeAssistantError: _LOGGER.exception("Error initializing MFA step") return self.async_abort(reason="unknown_error") diff --git a/homeassistant/components/climate/__init__.py b/homeassistant/components/climate/__init__.py index 67348d38625..be5f33f8cd5 100644 --- a/homeassistant/components/climate/__init__.py +++ b/homeassistant/components/climate/__init__.py @@ -531,7 +531,7 @@ class ClimateEntity(Entity): async def async_turn_on(self) -> None: """Turn the entity on.""" if hasattr(self, "turn_on"): - await self.hass.async_add_executor_job(self.turn_on) # type: ignore[attr-defined] + await self.hass.async_add_executor_job(self.turn_on) return # Fake turn on @@ -544,7 +544,7 @@ class ClimateEntity(Entity): async def async_turn_off(self) -> None: """Turn the entity off.""" if hasattr(self, "turn_off"): - await self.hass.async_add_executor_job(self.turn_off) # type: ignore[attr-defined] + await self.hass.async_add_executor_job(self.turn_off) return # Fake turn off diff --git a/homeassistant/components/knx/__init__.py b/homeassistant/components/knx/__init__.py index fa014335df9..2ca37cc39eb 100644 --- a/homeassistant/components/knx/__init__.py +++ b/homeassistant/components/knx/__init__.py @@ -500,7 +500,7 @@ class KNXModule: transcoder := DPTBase.parse_transcoder(dpt) ): self._address_filter_transcoder.update( - {_filter: transcoder for _filter in _filters} # type: ignore[misc] + {_filter: transcoder for _filter in _filters} # type: ignore[type-abstract] ) return self.xknx.telegram_queue.register_telegram_received_cb( @@ -532,7 +532,7 @@ class KNXModule: transcoder := DPTBase.parse_transcoder(dpt) ): self._group_address_transcoder.update( - {_address: transcoder for _address in group_addresses} # type: ignore[misc] + {_address: transcoder for _address in group_addresses} # type: ignore[type-abstract] ) for group_address in group_addresses: if group_address in self._knx_event_callback.group_addresses: diff --git a/homeassistant/components/knx/schema.py b/homeassistant/components/knx/schema.py index 13f6f153daf..3850119f387 100644 --- a/homeassistant/components/knx/schema.py +++ b/homeassistant/components/knx/schema.py @@ -79,8 +79,8 @@ def dpt_subclass_validator(dpt_base_class: type[DPTBase]) -> Callable[[Any], str return dpt_value_validator -numeric_type_validator = dpt_subclass_validator(DPTNumeric) # type: ignore[misc] -sensor_type_validator = dpt_subclass_validator(DPTBase) # type: ignore[misc] +numeric_type_validator = dpt_subclass_validator(DPTNumeric) # type: ignore[type-abstract] +sensor_type_validator = dpt_subclass_validator(DPTBase) # type: ignore[type-abstract] string_type_validator = dpt_subclass_validator(DPTString) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 5771e1b6938..c43c9980c19 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -920,9 +920,7 @@ class MediaPlayerEntity(Entity): async def async_toggle(self) -> None: """Toggle the power on the media player.""" if hasattr(self, "toggle"): - await self.hass.async_add_executor_job( - self.toggle # type: ignore[attr-defined] - ) + await self.hass.async_add_executor_job(self.toggle) return if self.state in { @@ -940,9 +938,7 @@ class MediaPlayerEntity(Entity): This method is a coroutine. """ if hasattr(self, "volume_up"): - await self.hass.async_add_executor_job( - self.volume_up # type: ignore[attr-defined] - ) + await self.hass.async_add_executor_job(self.volume_up) return if ( @@ -958,9 +954,7 @@ class MediaPlayerEntity(Entity): This method is a coroutine. """ if hasattr(self, "volume_down"): - await self.hass.async_add_executor_job( - self.volume_down # type: ignore[attr-defined] - ) + await self.hass.async_add_executor_job(self.volume_down) return if ( @@ -973,9 +967,7 @@ class MediaPlayerEntity(Entity): async def async_media_play_pause(self) -> None: """Play or pause the media player.""" if hasattr(self, "media_play_pause"): - await self.hass.async_add_executor_job( - self.media_play_pause # type: ignore[attr-defined] - ) + await self.hass.async_add_executor_job(self.media_play_pause) return if self.state == MediaPlayerState.PLAYING: diff --git a/homeassistant/components/notify/legacy.py b/homeassistant/components/notify/legacy.py index c3bb02896e0..0172fbc98d0 100644 --- a/homeassistant/components/notify/legacy.py +++ b/homeassistant/components/notify/legacy.py @@ -282,7 +282,7 @@ class BaseNotificationService: if hasattr(self, "targets"): stale_targets = set(self.registered_targets) - for name, target in self.targets.items(): # type: ignore[attr-defined] + for name, target in self.targets.items(): target_name = slugify(f"{self._target_service_name_prefix}_{name}") if target_name in stale_targets: stale_targets.remove(target_name) diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index cfc797cf7ea..3ab8b890838 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -160,7 +160,7 @@ class LazyState(State): """Set attributes.""" self._attributes = value - @property # type: ignore[override] + @property def context(self) -> Context: """State context.""" if self._context is None: @@ -172,7 +172,7 @@ class LazyState(State): """Set context.""" self._context = value - @property # type: ignore[override] + @property def last_changed(self) -> datetime: """Last changed datetime.""" if self._last_changed is None: @@ -187,7 +187,7 @@ class LazyState(State): """Set last changed datetime.""" self._last_changed = value - @property # type: ignore[override] + @property def last_updated(self) -> datetime: """Last updated datetime.""" if self._last_updated is None: diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 57cfe362231..d4f8128f643 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -705,9 +705,9 @@ class Entity(ABC): try: task: asyncio.Future[None] if hasattr(self, "async_update"): - task = self.hass.async_create_task(self.async_update()) # type: ignore[attr-defined] + task = self.hass.async_create_task(self.async_update()) elif hasattr(self, "update"): - task = self.hass.async_add_executor_job(self.update) # type: ignore[attr-defined] + task = self.hass.async_add_executor_job(self.update) else: return diff --git a/homeassistant/util/yaml/dumper.py b/homeassistant/util/yaml/dumper.py index 9f69c6c346e..db8b496d90e 100644 --- a/homeassistant/util/yaml/dumper.py +++ b/homeassistant/util/yaml/dumper.py @@ -12,7 +12,9 @@ from .objects import Input, NodeListClass try: from yaml import CSafeDumper as FastestAvailableSafeDumper except ImportError: - from yaml import SafeDumper as FastestAvailableSafeDumper # type: ignore[misc] + from yaml import ( # type: ignore[assignment] + SafeDumper as FastestAvailableSafeDumper, + ) def dump(_dict: dict) -> str: diff --git a/homeassistant/util/yaml/loader.py b/homeassistant/util/yaml/loader.py index 09e19af6840..62d754329c4 100644 --- a/homeassistant/util/yaml/loader.py +++ b/homeassistant/util/yaml/loader.py @@ -18,7 +18,7 @@ try: HAS_C_LOADER = True except ImportError: HAS_C_LOADER = False - from yaml import SafeLoader as FastestAvailableSafeLoader # type: ignore[misc] + from yaml import SafeLoader as FastestAvailableSafeLoader # type: ignore[assignment] from homeassistant.exceptions import HomeAssistantError diff --git a/mypy.ini b/mypy.ini index 3f0e917f167..988701393d6 100644 --- a/mypy.ini +++ b/mypy.ini @@ -14,6 +14,7 @@ warn_redundant_casts = true warn_unused_configs = true warn_unused_ignores = true enable_error_code = ignore-without-code +disable_error_code = annotation-unchecked strict_concatenate = false check_untyped_defs = true disallow_incomplete_defs = true diff --git a/requirements_test.txt b/requirements_test.txt index 06bd7e878ce..e0cbafbd6d4 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -12,7 +12,7 @@ codecov==2.1.12 coverage==6.4.4 freezegun==1.2.2 mock-open==1.4.0 -mypy==0.982 +mypy==0.990 pre-commit==2.20.0 pylint==2.15.5 pipdeptree==2.3.1 diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index 0c598df9cd1..99e07bcf118 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -46,7 +46,8 @@ GENERAL_SETTINGS: Final[dict[str, str]] = { "warn_redundant_casts": "true", "warn_unused_configs": "true", "warn_unused_ignores": "true", - "enable_error_code": "ignore-without-code", + "enable_error_code": ", ".join(["ignore-without-code"]), + "disable_error_code": ", ".join(["annotation-unchecked"]), # Strict_concatenate breaks passthrough ParamSpec typing "strict_concatenate": "false", } From 3edaef63b00a8c95abf4034f50f7f2be81f7c85e Mon Sep 17 00:00:00 2001 From: Malte Franken Date: Wed, 9 Nov 2022 00:47:00 +1100 Subject: [PATCH 0298/1033] Add integration_type to ign_sismologia (#81729) define integration type --- homeassistant/components/ign_sismologia/manifest.json | 3 ++- homeassistant/generated/integrations.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/ign_sismologia/manifest.json b/homeassistant/components/ign_sismologia/manifest.json index 97836e7f145..927e52f594d 100644 --- a/homeassistant/components/ign_sismologia/manifest.json +++ b/homeassistant/components/ign_sismologia/manifest.json @@ -5,5 +5,6 @@ "requirements": ["georss_ign_sismologia_client==0.3"], "codeowners": ["@exxamalte"], "iot_class": "cloud_polling", - "loggers": ["georss_ign_sismologia_client"] + "loggers": ["georss_ign_sismologia_client"], + "integration_type": "service" } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 21e19326dc6..91f19977ccb 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -2349,7 +2349,7 @@ }, "ign_sismologia": { "name": "IGN Sismolog\u00eda", - "integration_type": "hub", + "integration_type": "service", "config_flow": false, "iot_class": "cloud_polling" }, From 014c2d487d1075724fce3396a1c9eda36b823721 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 8 Nov 2022 15:00:30 +0100 Subject: [PATCH 0299/1033] Update frontend to 20221108.0 (#81787) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index f4f46a1f89b..ec7001006b1 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20221102.1"], + "requirements": ["home-assistant-frontend==20221108.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 4a6b8b076f7..66ffb1eb85b 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -22,7 +22,7 @@ dbus-fast==1.72.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 -home-assistant-frontend==20221102.1 +home-assistant-frontend==20221108.0 httpx==0.23.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index 512c366f5dc..9946af9a968 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -884,7 +884,7 @@ hole==0.7.0 holidays==0.16 # homeassistant.components.frontend -home-assistant-frontend==20221102.1 +home-assistant-frontend==20221108.0 # homeassistant.components.home_connect homeconnect==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bc44c0c0b6b..9df97b9a7ae 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -664,7 +664,7 @@ hole==0.7.0 holidays==0.16 # homeassistant.components.frontend -home-assistant-frontend==20221102.1 +home-assistant-frontend==20221108.0 # homeassistant.components.home_connect homeconnect==0.7.2 From 45be2a260e6e9cc48e635918332c697d489dd52f Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Tue, 8 Nov 2022 07:41:09 -0700 Subject: [PATCH 0300/1033] Add re-auth flow for OpenUV (#79691) --- homeassistant/components/openuv/__init__.py | 5 +- .../components/openuv/config_flow.py | 123 +++++++++++++++--- .../components/openuv/coordinator.py | 83 +++++++++++- homeassistant/components/openuv/strings.json | 10 +- .../components/openuv/translations/en.json | 10 +- tests/components/openuv/test_config_flow.py | 24 +++- 6 files changed, 228 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/openuv/__init__.py b/homeassistant/components/openuv/__init__.py index c4a9d347a40..3e65f33d8c5 100644 --- a/homeassistant/components/openuv/__init__.py +++ b/homeassistant/components/openuv/__init__.py @@ -31,7 +31,7 @@ from .const import ( DOMAIN, LOGGER, ) -from .coordinator import OpenUvCoordinator +from .coordinator import InvalidApiKeyMonitor, OpenUvCoordinator PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] @@ -53,6 +53,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: high = entry.options.get(CONF_TO_WINDOW, DEFAULT_TO_WINDOW) return await client.uv_protection_window(low=low, high=high) + invalid_api_key_monitor = InvalidApiKeyMonitor(hass, entry) + coordinators: dict[str, OpenUvCoordinator] = { coordinator_name: OpenUvCoordinator( hass, @@ -60,6 +62,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: latitude=client.latitude, longitude=client.longitude, update_method=update_method, + invalid_api_key_monitor=invalid_api_key_monitor, ) for coordinator_name, update_method in ( (DATA_UV, client.uv_index), diff --git a/homeassistant/components/openuv/config_flow.py b/homeassistant/components/openuv/config_flow.py index facbc37986e..2e96ce7c292 100644 --- a/homeassistant/components/openuv/config_flow.py +++ b/homeassistant/components/openuv/config_flow.py @@ -1,6 +1,8 @@ """Config flow to configure the OpenUV component.""" from __future__ import annotations +from collections.abc import Mapping +from dataclasses import dataclass from typing import Any from pyopenuv import Client @@ -27,14 +29,39 @@ from .const import ( DOMAIN, ) +STEP_REAUTH_SCHEMA = vol.Schema( + { + vol.Required(CONF_API_KEY): str, + } +) + + +@dataclass +class OpenUvData: + """Define structured OpenUV data needed to create/re-auth an entry.""" + + api_key: str + latitude: float + longitude: float + elevation: float + + @property + def unique_id(self) -> str: + """Return the unique for this data.""" + return f"{self.latitude}, {self.longitude}" + class OpenUvFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Handle an OpenUV config flow.""" VERSION = 2 + def __init__(self) -> None: + """Initialize.""" + self._reauth_data: Mapping[str, Any] = {} + @property - def config_schema(self) -> vol.Schema: + def step_user_schema(self) -> vol.Schema: """Return the config schema.""" return vol.Schema( { @@ -51,13 +78,41 @@ class OpenUvFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): } ) - async def _show_form(self, errors: dict[str, Any] | None = None) -> FlowResult: - """Show the form to the user.""" - return self.async_show_form( - step_id="user", - data_schema=self.config_schema, - errors=errors if errors else {}, - ) + async def _async_verify( + self, data: OpenUvData, error_step_id: str, error_schema: vol.Schema + ) -> FlowResult: + """Verify the credentials and create/re-auth the entry.""" + websession = aiohttp_client.async_get_clientsession(self.hass) + client = Client(data.api_key, 0, 0, session=websession) + client.disable_request_retries() + + try: + await client.uv_index() + except OpenUvError: + return self.async_show_form( + step_id=error_step_id, + data_schema=error_schema, + errors={CONF_API_KEY: "invalid_api_key"}, + description_placeholders={ + CONF_LATITUDE: str(data.latitude), + CONF_LONGITUDE: str(data.longitude), + }, + ) + + entry_data = { + CONF_API_KEY: data.api_key, + CONF_LATITUDE: data.latitude, + CONF_LONGITUDE: data.longitude, + CONF_ELEVATION: data.elevation, + } + + if existing_entry := await self.async_set_unique_id(data.unique_id): + self.hass.config_entries.async_update_entry(existing_entry, data=entry_data) + self.hass.async_create_task( + self.hass.config_entries.async_reload(existing_entry.entry_id) + ) + return self.async_abort(reason="reauth_successful") + return self.async_create_entry(title=data.unique_id, data=entry_data) @staticmethod @callback @@ -65,26 +120,54 @@ class OpenUvFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Define the config flow to handle options.""" return OpenUvOptionsFlowHandler(config_entry) + async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: + """Handle configuration by re-auth.""" + self._reauth_data = entry_data + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle re-auth completion.""" + if not user_input: + return self.async_show_form( + step_id="reauth_confirm", + data_schema=STEP_REAUTH_SCHEMA, + description_placeholders={ + CONF_LATITUDE: self._reauth_data[CONF_LATITUDE], + CONF_LONGITUDE: self._reauth_data[CONF_LONGITUDE], + }, + ) + + data = OpenUvData( + user_input[CONF_API_KEY], + self._reauth_data[CONF_LATITUDE], + self._reauth_data[CONF_LONGITUDE], + self._reauth_data[CONF_ELEVATION], + ) + + return await self._async_verify(data, "reauth_confirm", STEP_REAUTH_SCHEMA) + async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle the start of the config flow.""" if not user_input: - return await self._show_form() + return self.async_show_form( + step_id="user", data_schema=self.step_user_schema + ) - identifier = f"{user_input[CONF_LATITUDE]}, {user_input[CONF_LONGITUDE]}" - await self.async_set_unique_id(identifier) + data = OpenUvData( + user_input[CONF_API_KEY], + user_input[CONF_LATITUDE], + user_input[CONF_LONGITUDE], + user_input[CONF_ELEVATION], + ) + + await self.async_set_unique_id(data.unique_id) self._abort_if_unique_id_configured() - websession = aiohttp_client.async_get_clientsession(self.hass) - client = Client(user_input[CONF_API_KEY], 0, 0, session=websession) - - try: - await client.uv_index() - except OpenUvError: - return await self._show_form({CONF_API_KEY: "invalid_api_key"}) - - return self.async_create_entry(title=identifier, data=user_input) + return await self._async_verify(data, "user", self.step_user_schema) class OpenUvOptionsFlowHandler(config_entries.OptionsFlow): diff --git a/homeassistant/components/openuv/coordinator.py b/homeassistant/components/openuv/coordinator.py index 993970658ef..36267972f80 100644 --- a/homeassistant/components/openuv/coordinator.py +++ b/homeassistant/components/openuv/coordinator.py @@ -1,23 +1,94 @@ """Define an update coordinator for OpenUV.""" from __future__ import annotations +import asyncio from collections.abc import Awaitable, Callable from typing import Any, cast -from pyopenuv.errors import OpenUvError +from pyopenuv.errors import InvalidApiKeyError, OpenUvError -from homeassistant.core import HomeAssistant +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .const import LOGGER +from .const import DOMAIN, LOGGER DEFAULT_DEBOUNCER_COOLDOWN_SECONDS = 15 * 60 +class InvalidApiKeyMonitor: + """Define a monitor for failed API calls (due to bad keys) across coordinators.""" + + DEFAULT_FAILED_API_CALL_THRESHOLD = 5 + + def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: + """Initialize.""" + self._count = 1 + self._lock = asyncio.Lock() + self._reauth_flow_manager = ReauthFlowManager(hass, entry) + self.entry = entry + + async def async_increment(self) -> None: + """Increment the counter.""" + LOGGER.debug("Invalid API key response detected (number %s)", self._count) + async with self._lock: + self._count += 1 + if self._count > self.DEFAULT_FAILED_API_CALL_THRESHOLD: + self._reauth_flow_manager.start_reauth() + + async def async_reset(self) -> None: + """Reset the counter.""" + async with self._lock: + self._count = 0 + self._reauth_flow_manager.cancel_reauth() + + +class ReauthFlowManager: + """Define an OpenUV reauth flow manager.""" + + def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: + """Initialize.""" + self.entry = entry + self.hass = hass + + @callback + def _get_active_reauth_flow(self) -> FlowResult | None: + """Get an active reauth flow (if it exists).""" + try: + [reauth_flow] = [ + flow + for flow in self.hass.config_entries.flow.async_progress_by_handler( + DOMAIN + ) + if flow["context"]["source"] == "reauth" + and flow["context"]["entry_id"] == self.entry.entry_id + ] + except ValueError: + return None + + return reauth_flow + + @callback + def cancel_reauth(self) -> None: + """Cancel a reauth flow (if appropriate).""" + if reauth_flow := self._get_active_reauth_flow(): + LOGGER.debug("API seems to have recovered; canceling reauth flow") + self.hass.config_entries.flow.async_abort(reauth_flow["flow_id"]) + + @callback + def start_reauth(self) -> None: + """Start a reauth flow (if appropriate).""" + if not self._get_active_reauth_flow(): + LOGGER.debug("Multiple API failures in a row; starting reauth flow") + self.entry.async_start_reauth(self.hass) + + class OpenUvCoordinator(DataUpdateCoordinator): """Define an OpenUV data coordinator.""" + config_entry: ConfigEntry update_method: Callable[[], Awaitable[dict[str, Any]]] def __init__( @@ -28,6 +99,7 @@ class OpenUvCoordinator(DataUpdateCoordinator): latitude: str, longitude: str, update_method: Callable[[], Awaitable[dict[str, Any]]], + invalid_api_key_monitor: InvalidApiKeyMonitor, ) -> None: """Initialize.""" super().__init__( @@ -43,6 +115,7 @@ class OpenUvCoordinator(DataUpdateCoordinator): ), ) + self._invalid_api_key_monitor = invalid_api_key_monitor self.latitude = latitude self.longitude = longitude @@ -50,6 +123,10 @@ class OpenUvCoordinator(DataUpdateCoordinator): """Fetch data from OpenUV.""" try: data = await self.update_method() + except InvalidApiKeyError: + await self._invalid_api_key_monitor.async_increment() except OpenUvError as err: raise UpdateFailed(f"Error during protection data update: {err}") from err + + await self._invalid_api_key_monitor.async_reset() return cast(dict[str, Any], data["result"]) diff --git a/homeassistant/components/openuv/strings.json b/homeassistant/components/openuv/strings.json index 84a093280f3..9542cb8b1a7 100644 --- a/homeassistant/components/openuv/strings.json +++ b/homeassistant/components/openuv/strings.json @@ -1,6 +1,13 @@ { "config": { "step": { + "reauth_confirm": { + "title": "[%key:common::config_flow::title::reauth%]", + "description": "Please re-enter the API key for {latitude}, {longitude}.", + "data": { + "api_key": "[%key:common::config_flow::data::api_key%]" + } + }, "user": { "title": "Fill in your information", "data": { @@ -15,7 +22,8 @@ "invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]" }, "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_location%]" + "already_configured": "[%key:common::config_flow::abort::already_configured_location%]", + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" } }, "options": { diff --git a/homeassistant/components/openuv/translations/en.json b/homeassistant/components/openuv/translations/en.json index 3879a4d7d44..9db83868543 100644 --- a/homeassistant/components/openuv/translations/en.json +++ b/homeassistant/components/openuv/translations/en.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "Location is already configured" + "already_configured": "Location is already configured", + "reauth_successful": "Re-authentication was successful" }, "error": { "invalid_api_key": "Invalid API key" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API Key" + }, + "description": "Please re-enter the API key for {latitude}, {longitude}.", + "title": "Reauthenticate Integration" + }, "user": { "data": { "api_key": "API Key", diff --git a/tests/components/openuv/test_config_flow.py b/tests/components/openuv/test_config_flow.py index 9f51728365b..eeafd82a20f 100644 --- a/tests/components/openuv/test_config_flow.py +++ b/tests/components/openuv/test_config_flow.py @@ -5,7 +5,7 @@ from pyopenuv.errors import InvalidApiKeyError from homeassistant import data_entry_flow from homeassistant.components.openuv import CONF_FROM_WINDOW, CONF_TO_WINDOW, DOMAIN -from homeassistant.config_entries import SOURCE_USER +from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER from homeassistant.const import ( CONF_API_KEY, CONF_ELEVATION, @@ -51,6 +51,28 @@ async def test_options_flow(hass, config_entry): assert config_entry.options == {CONF_FROM_WINDOW: 3.5, CONF_TO_WINDOW: 2.0} +async def test_step_reauth(hass, config, config_entry, setup_openuv): + """Test that the reauth step works.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_REAUTH}, data=config + ) + assert result["step_id"] == "reauth_confirm" + + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "reauth_confirm" + + with patch("homeassistant.components.openuv.async_setup_entry", return_value=True): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_API_KEY: "new_api_key"} + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "reauth_successful" + assert len(hass.config_entries.async_entries()) == 1 + + async def test_step_user(hass, config, setup_openuv): """Test that the user step works.""" result = await hass.config_entries.flow.async_init( From 5040b943305e4cdb2fb0afec7df7bf607ce44aa2 Mon Sep 17 00:00:00 2001 From: ztamas83 <71548739+ztamas83@users.noreply.github.com> Date: Tue, 8 Nov 2022 16:09:58 +0100 Subject: [PATCH 0301/1033] Retry tibber setup (#81785) * Handle integration setup retries * Fix black error * Update to falsy check Co-authored-by: Martin Hjelmare * Remove duplicated log * Update exception message Co-authored-by: Martin Hjelmare Co-authored-by: Martin Hjelmare --- homeassistant/components/tibber/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/tibber/__init__.py b/homeassistant/components/tibber/__init__.py index 35507986f90..4d9c0560682 100644 --- a/homeassistant/components/tibber/__init__.py +++ b/homeassistant/components/tibber/__init__.py @@ -53,6 +53,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: try: await tibber_connection.update_info() + if not tibber_connection.name: + raise ConfigEntryNotReady("Could not fetch Tibber data.") + except asyncio.TimeoutError as err: raise ConfigEntryNotReady from err except aiohttp.ClientError as err: From 88a7c76739d076ada1aec8886e187364841fcb8d Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 8 Nov 2022 17:16:31 +0200 Subject: [PATCH 0302/1033] Fix late-import paho (#81791) fix: late-import MQTTMessage `paho-mqtt` is not listed in main requirements and is imported early by `conftest`. Import it late to avoid an ImportError. Split out from #81678. --- homeassistant/components/mqtt/client.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py index e909a378581..34384614c20 100644 --- a/homeassistant/components/mqtt/client.py +++ b/homeassistant/components/mqtt/client.py @@ -15,7 +15,6 @@ import uuid import attr import certifi -from paho.mqtt.client import MQTTMessage from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -620,7 +619,7 @@ class MQTT: ) def _mqtt_on_message( - self, _mqttc: mqtt.Client, _userdata: None, msg: MQTTMessage + self, _mqttc: mqtt.Client, _userdata: None, msg: mqtt.MQTTMessage ) -> None: """Message received callback.""" self.hass.add_job(self._mqtt_handle_message, msg) @@ -634,7 +633,7 @@ class MQTT: return subscriptions @callback - def _mqtt_handle_message(self, msg: MQTTMessage) -> None: + def _mqtt_handle_message(self, msg: mqtt.MQTTMessage) -> None: _LOGGER.debug( "Received%s message on %s: %s", " retained" if msg.retain else "", From 2cb58b818955a3291e25da62f1b06c2b8f2ce248 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 8 Nov 2022 09:20:03 -0600 Subject: [PATCH 0303/1033] Fix off by one in HomeKit iid allocator (#81793) --- homeassistant/components/homekit/iidmanager.py | 8 ++++---- tests/components/homekit/test_diagnostics.py | 2 -- tests/components/homekit/test_iidmanager.py | 6 ++++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/homekit/iidmanager.py b/homeassistant/components/homekit/iidmanager.py index 3805748225a..8bac268800b 100644 --- a/homeassistant/components/homekit/iidmanager.py +++ b/homeassistant/components/homekit/iidmanager.py @@ -109,12 +109,12 @@ class AccessoryIIDStorage: # AID must be a string since JSON keys cannot be int aid_str = str(aid) accessory_allocation = self.allocations.setdefault(aid_str, {}) - accessory_allocated_iids = self.allocated_iids.setdefault(aid_str, []) + accessory_allocated_iids = self.allocated_iids.setdefault(aid_str, [1]) if service_hap_type == ACCESSORY_INFORMATION_SERVICE and char_uuid is None: - allocated_iid = 1 - elif allocation_key in accessory_allocation: + return 1 + if allocation_key in accessory_allocation: return accessory_allocation[allocation_key] - elif accessory_allocated_iids: + if accessory_allocated_iids: allocated_iid = accessory_allocated_iids[-1] + 1 else: allocated_iid = 2 diff --git a/tests/components/homekit/test_diagnostics.py b/tests/components/homekit/test_diagnostics.py index be98c3bacdd..d114c462e2f 100644 --- a/tests/components/homekit/test_diagnostics.py +++ b/tests/components/homekit/test_diagnostics.py @@ -51,7 +51,6 @@ async def test_config_entry_running(hass, hass_client, hk_driver, mock_async_zer "3E__23_": 5, "3E__30_": 6, "3E__52_": 7, - "3E___": 1, "A2__37_": 9, "A2___": 8, } @@ -278,7 +277,6 @@ async def test_config_entry_accessory( "3E__23_": 5, "3E__30_": 6, "3E__52_": 7, - "3E___": 1, "43__25_": 11, "43___": 10, "A2__37_": 9, diff --git a/tests/components/homekit/test_iidmanager.py b/tests/components/homekit/test_iidmanager.py index 3e4a19c9045..c16cbf01d7d 100644 --- a/tests/components/homekit/test_iidmanager.py +++ b/tests/components/homekit/test_iidmanager.py @@ -152,23 +152,29 @@ async def test_iid_generation_and_restore_v2(hass, iid_storage, hass_storage): 1, "000000AA-0000-1000-8000-0026BB765291", None, None, None ) assert not_accessory_info_service_iid == 2 + assert iid_storage.allocated_iids == {"1": [1, 2]} not_accessory_info_service_iid_2 = iid_storage.get_or_allocate_iid( 1, "000000BB-0000-1000-8000-0026BB765291", None, None, None ) assert not_accessory_info_service_iid_2 == 3 + assert iid_storage.allocated_iids == {"1": [1, 2, 3]} not_accessory_info_service_iid_2 = iid_storage.get_or_allocate_iid( 1, "000000BB-0000-1000-8000-0026BB765291", None, None, None ) assert not_accessory_info_service_iid_2 == 3 + assert iid_storage.allocated_iids == {"1": [1, 2, 3]} accessory_info_service_iid = iid_storage.get_or_allocate_iid( 1, "0000003E-0000-1000-8000-0026BB765291", None, None, None ) assert accessory_info_service_iid == 1 + assert iid_storage.allocated_iids == {"1": [1, 2, 3]} accessory_info_service_iid = iid_storage.get_or_allocate_iid( 1, "0000003E-0000-1000-8000-0026BB765291", None, None, None ) assert accessory_info_service_iid == 1 + assert iid_storage.allocated_iids == {"1": [1, 2, 3]} accessory_info_service_iid = iid_storage.get_or_allocate_iid( 2, "0000003E-0000-1000-8000-0026BB765291", None, None, None ) assert accessory_info_service_iid == 1 + assert iid_storage.allocated_iids == {"1": [1, 2, 3], "2": [1]} From 7d768dc3a6020e645ca29e8e8846f7869eb0a48a Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 8 Nov 2022 17:02:06 +0100 Subject: [PATCH 0304/1033] Improve MQTT type hints / refactor part 7 - trigger (#81019) * Improve typing trigger * Improve hints on templates * Use new sentinel for template * Follow-up comment --- homeassistant/components/mqtt/trigger.py | 74 +++++++++++++----------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/homeassistant/components/mqtt/trigger.py b/homeassistant/components/mqtt/trigger.py index 3530538122d..c10e539f8ec 100644 --- a/homeassistant/components/mqtt/trigger.py +++ b/homeassistant/components/mqtt/trigger.py @@ -1,21 +1,31 @@ """Offer MQTT listening automation rules.""" +from __future__ import annotations + +from collections.abc import Callable from contextlib import suppress import logging +from typing import Any import voluptuous as vol from homeassistant.const import CONF_PAYLOAD, CONF_PLATFORM, CONF_VALUE_TEMPLATE from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback -from homeassistant.helpers import config_validation as cv, template +from homeassistant.helpers import config_validation as cv from homeassistant.helpers.json import json_loads -from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.template import Template +from homeassistant.helpers.trigger import TriggerActionType, TriggerData, TriggerInfo +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from .. import mqtt from .const import CONF_ENCODING, CONF_QOS, CONF_TOPIC, DEFAULT_ENCODING, DEFAULT_QOS - -# mypy: allow-untyped-defs - +from .models import ( + MqttCommandTemplate, + MqttValueTemplate, + PayloadSentinel, + PublishPayloadType, + ReceiveMessage, + ReceivePayloadType, +) TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend( { @@ -40,43 +50,37 @@ async def async_attach_trigger( trigger_info: TriggerInfo, ) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" - trigger_data = trigger_info["trigger_data"] - topic = config[CONF_TOPIC] - wanted_payload = config.get(CONF_PAYLOAD) - value_template = config.get(CONF_VALUE_TEMPLATE) - encoding = config[CONF_ENCODING] or None - qos = config[CONF_QOS] + trigger_data: TriggerData = trigger_info["trigger_data"] + command_template: Callable[ + [PublishPayloadType, TemplateVarsType], PublishPayloadType + ] = MqttCommandTemplate(config.get(CONF_PAYLOAD), hass=hass).async_render + value_template: Callable[[ReceivePayloadType, str], ReceivePayloadType] + value_template = MqttValueTemplate( + config.get(CONF_VALUE_TEMPLATE), hass=hass + ).async_render_with_possible_json_value + encoding: str | None = config[CONF_ENCODING] or None + qos: int = config[CONF_QOS] job = HassJob(action) - variables = None + variables: TemplateVarsType | None = None if trigger_info: variables = trigger_info.get("variables") - template.attach(hass, wanted_payload) - if wanted_payload: - wanted_payload = wanted_payload.async_render( - variables, limited=True, parse_result=False - ) + wanted_payload = command_template(None, variables) - template.attach(hass, topic) - if isinstance(topic, template.Template): - topic = topic.async_render(variables, limited=True, parse_result=False) - topic = mqtt.util.valid_subscribe_topic(topic) - - template.attach(hass, value_template) + topic_template: Template = config[CONF_TOPIC] + topic_template.hass = hass + topic = topic_template.async_render(variables, limited=True, parse_result=False) + mqtt.util.valid_subscribe_topic(topic) @callback - def mqtt_automation_listener(mqttmsg): + def mqtt_automation_listener(mqttmsg: ReceiveMessage) -> None: """Listen for MQTT messages.""" - payload = mqttmsg.payload - - if value_template is not None: - payload = value_template.async_render_with_possible_json_value( - payload, - error_value=None, - ) - - if wanted_payload is None or wanted_payload == payload: - data = { + if wanted_payload is None or ( + (payload := value_template(mqttmsg.payload, PayloadSentinel.DEFAULT)) + and payload is not PayloadSentinel.DEFAULT + and wanted_payload == payload + ): + data: dict[str, Any] = { **trigger_data, "platform": "mqtt", "topic": mqttmsg.topic, From 3cc9ecf1dc0dc5fdace19770c076eb47d2f1c746 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 8 Nov 2022 17:18:40 +0100 Subject: [PATCH 0305/1033] Implement ConfigEntry async_wait_for_states (#81771) * Implement async_wait_for_states * stale docstr, remove hints * Assert return states for tests --- homeassistant/config_entries.py | 34 +++++++++- tests/test_config_entries.py | 107 ++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index bd985517ca7..902fa0d03f2 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -12,6 +12,8 @@ from types import MappingProxyType, MethodType from typing import TYPE_CHECKING, Any, Optional, TypeVar, cast import weakref +import async_timeout + from . import data_entry_flow, loader from .backports.enum import StrEnum from .components import persistent_notification @@ -19,7 +21,7 @@ from .const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP, Platfo from .core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback from .exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady, HomeAssistantError from .helpers import device_registry, entity_registry, storage -from .helpers.dispatcher import async_dispatcher_send +from .helpers.dispatcher import async_dispatcher_connect, async_dispatcher_send from .helpers.event import async_call_later from .helpers.frame import report from .helpers.typing import UNDEFINED, ConfigType, DiscoveryInfoType, UndefinedType @@ -1239,6 +1241,36 @@ class ConfigEntries: await entry.async_setup(self.hass, integration=integration) return True + async def async_wait_for_states( + self, entry: ConfigEntry, states: set[ConfigEntryState], timeout: float = 60.0 + ) -> ConfigEntryState: + """Wait for the setup of an entry to reach one of the supplied states state. + + Returns the state the entry reached or raises asyncio.TimeoutError if the + entry did not reach one of the supplied states within the timeout. + """ + state_reached_future: asyncio.Future[ConfigEntryState] = asyncio.Future() + + @callback + def _async_entry_changed( + change: ConfigEntryChange, event_entry: ConfigEntry + ) -> None: + if ( + event_entry is entry + and change is ConfigEntryChange.UPDATED + and entry.state in states + ): + state_reached_future.set_result(entry.state) + + unsub = async_dispatcher_connect( + self.hass, SIGNAL_CONFIG_ENTRY_CHANGED, _async_entry_changed + ) + try: + async with async_timeout.timeout(timeout): + return await state_reached_future + finally: + unsub() + async def async_unload_platforms( self, entry: ConfigEntry, platforms: Iterable[Platform | str] ) -> bool: diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 99e26be6d75..7684e9ff260 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -3330,3 +3330,110 @@ async def test_reauth(hass): entry2.async_start_reauth(hass, {"extra_context": "some_extra_context"}) await hass.async_block_till_done() assert len(hass.config_entries.flow.async_progress()) == 2 + + +async def test_wait_for_loading_entry(hass): + """Test waiting for entry to be set up.""" + + entry = MockConfigEntry(title="test_title", domain="test") + + mock_setup_entry = AsyncMock(return_value=True) + mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry)) + mock_entity_platform(hass, "config_flow.test", None) + + await entry.async_setup(hass) + await hass.async_block_till_done() + + flow = hass.config_entries.flow + + async def _load_entry(): + # Mock config entry + assert await async_setup_component(hass, "test", {}) + + entry = MockConfigEntry(title="test_title", domain="test") + entry.add_to_hass(hass) + flow = hass.config_entries.flow + with patch.object(flow, "async_init", wraps=flow.async_init): + hass.async_add_job(_load_entry) + new_state = await hass.config_entries.async_wait_for_states( + entry, + { + config_entries.ConfigEntryState.LOADED, + config_entries.ConfigEntryState.SETUP_ERROR, + }, + timeout=1.0, + ) + assert new_state is config_entries.ConfigEntryState.LOADED + assert entry.state is config_entries.ConfigEntryState.LOADED + + +async def test_wait_for_loading_failed_entry(hass): + """Test waiting for entry to be set up that fails loading.""" + + entry = MockConfigEntry(title="test_title", domain="test") + + mock_setup_entry = AsyncMock(side_effect=HomeAssistantError) + mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry)) + mock_entity_platform(hass, "config_flow.test", None) + + await entry.async_setup(hass) + await hass.async_block_till_done() + + flow = hass.config_entries.flow + + async def _load_entry(): + # Mock config entry + assert await async_setup_component(hass, "test", {}) + + entry = MockConfigEntry(title="test_title", domain="test") + entry.add_to_hass(hass) + flow = hass.config_entries.flow + with patch.object(flow, "async_init", wraps=flow.async_init): + hass.async_add_job(_load_entry) + new_state = await hass.config_entries.async_wait_for_states( + entry, + { + config_entries.ConfigEntryState.LOADED, + config_entries.ConfigEntryState.SETUP_ERROR, + }, + timeout=1.0, + ) + assert new_state is config_entries.ConfigEntryState.SETUP_ERROR + assert entry.state is config_entries.ConfigEntryState.SETUP_ERROR + + +async def test_wait_for_loading_timeout(hass): + """Test waiting for entry to be set up that fails with a timeout.""" + + async def _async_setup_entry(hass, entry): + await asyncio.sleep(1) + return True + + entry = MockConfigEntry(title="test_title", domain="test") + + mock_integration(hass, MockModule("test", async_setup_entry=_async_setup_entry)) + mock_entity_platform(hass, "config_flow.test", None) + + await entry.async_setup(hass) + await hass.async_block_till_done() + + flow = hass.config_entries.flow + + async def _load_entry(): + # Mock config entry + assert await async_setup_component(hass, "test", {}) + + entry = MockConfigEntry(title="test_title", domain="test") + entry.add_to_hass(hass) + flow = hass.config_entries.flow + with patch.object(flow, "async_init", wraps=flow.async_init): + hass.async_add_job(_load_entry) + with pytest.raises(asyncio.exceptions.TimeoutError): + await hass.config_entries.async_wait_for_states( + entry, + { + config_entries.ConfigEntryState.LOADED, + config_entries.ConfigEntryState.SETUP_ERROR, + }, + timeout=0.1, + ) From b364ef98a073214aad8deff4ff9b91e9ff041557 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 8 Nov 2022 18:48:08 +0100 Subject: [PATCH 0306/1033] Use `_attr_` for MQTT vacuum (#81534) * Use `_attr_` for MQTT vacuum * Remove unneeded properties * Follow-up comment * Remove default value --- .../components/mqtt/vacuum/schema_legacy.py | 82 ++++++------------- .../components/mqtt/vacuum/schema_state.py | 58 +++++-------- 2 files changed, 45 insertions(+), 95 deletions(-) diff --git a/homeassistant/components/mqtt/vacuum/schema_legacy.py b/homeassistant/components/mqtt/vacuum/schema_legacy.py index a15367c3cad..4425a3775d9 100644 --- a/homeassistant/components/mqtt/vacuum/schema_legacy.py +++ b/homeassistant/components/mqtt/vacuum/schema_legacy.py @@ -173,14 +173,13 @@ class MqttVacuum(MqttEntity, VacuumEntity): def __init__(self, hass, config, config_entry, discovery_data): """Initialize the vacuum.""" - self._cleaning = False + self._attr_battery_level = 0 + self._attr_is_on = False + self._attr_fan_speed = "unknown" + self._charging = False self._docked = False self._error = None - self._status = "Unknown" - self._battery_level = 0 - self._fan_speed = "unknown" - self._fan_speed_list = [] MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @@ -190,11 +189,12 @@ class MqttVacuum(MqttEntity, VacuumEntity): return DISCOVERY_SCHEMA_LEGACY def _setup_from_config(self, config): + """(Re)Setup the entity.""" supported_feature_strings = config[CONF_SUPPORTED_FEATURES] - self._supported_features = strings_to_services( + self._attr_supported_features = strings_to_services( supported_feature_strings, STRING_TO_SERVICE ) - self._fan_speed_list = config[CONF_FAN_SPEED_LIST] + self._attr_fan_speed_list = config[CONF_FAN_SPEED_LIST] self._qos = config[CONF_QOS] self._retain = config[CONF_RETAIN] self._encoding = config[CONF_ENCODING] or None @@ -258,7 +258,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): msg.payload, PayloadSentinel.DEFAULT ) if battery_level and battery_level is not PayloadSentinel.DEFAULT: - self._battery_level = int(battery_level) + self._attr_battery_level = max(0, min(100, int(battery_level))) if ( msg.topic == self._state_topics[CONF_CHARGING_TOPIC] @@ -282,7 +282,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): msg.payload, PayloadSentinel.DEFAULT ) if cleaning and cleaning is not PayloadSentinel.DEFAULT: - self._cleaning = cv.boolean(cleaning) + self._attr_is_on = cv.boolean(cleaning) if ( msg.topic == self._state_topics[CONF_DOCKED_TOPIC] @@ -310,15 +310,15 @@ class MqttVacuum(MqttEntity, VacuumEntity): if self._docked: if self._charging: - self._status = "Docked & Charging" + self._attr_status = "Docked & Charging" else: - self._status = "Docked" - elif self._cleaning: - self._status = "Cleaning" + self._attr_status = "Docked" + elif self.is_on: + self._attr_status = "Cleaning" elif self._error: - self._status = f"Error: {self._error}" + self._attr_status = f"Error: {self._error}" else: - self._status = "Stopped" + self._attr_status = "Stopped" if ( msg.topic == self._state_topics[CONF_FAN_SPEED_TOPIC] @@ -330,7 +330,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): msg.payload, PayloadSentinel.DEFAULT ) if fan_speed and fan_speed is not PayloadSentinel.DEFAULT: - self._fan_speed = fan_speed + self._attr_fan_speed = fan_speed get_mqtt_data(self.hass).state_write_requests.write_state_request(self) @@ -353,31 +353,6 @@ class MqttVacuum(MqttEntity, VacuumEntity): """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) - @property - def is_on(self): - """Return true if vacuum is on.""" - return self._cleaning - - @property - def status(self): - """Return a status string for the vacuum.""" - return self._status - - @property - def fan_speed(self): - """Return the status of the vacuum.""" - return self._fan_speed - - @property - def fan_speed_list(self): - """Return the status of the vacuum.""" - return self._fan_speed_list - - @property - def battery_level(self): - """Return the status of the vacuum.""" - return max(0, min(100, self._battery_level)) - @property def battery_icon(self): """Return the battery icon for the vacuum cleaner. @@ -388,11 +363,6 @@ class MqttVacuum(MqttEntity, VacuumEntity): battery_level=self.battery_level, charging=self._charging ) - @property - def supported_features(self): - """Flag supported features.""" - return self._supported_features - async def async_turn_on(self, **kwargs): """Turn the vacuum on.""" if self.supported_features & VacuumEntityFeature.TURN_ON == 0: @@ -405,7 +375,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): self._retain, self._encoding, ) - self._status = "Cleaning" + self._attr_status = "Cleaning" self.async_write_ha_state() async def async_turn_off(self, **kwargs): @@ -420,7 +390,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): self._retain, self._encoding, ) - self._status = "Turning Off" + self._attr_status = "Turning Off" self.async_write_ha_state() async def async_stop(self, **kwargs): @@ -435,7 +405,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): self._retain, self._encoding, ) - self._status = "Stopping the current task" + self._attr_status = "Stopping the current task" self.async_write_ha_state() async def async_clean_spot(self, **kwargs): @@ -450,7 +420,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): self._retain, self._encoding, ) - self._status = "Cleaning spot" + self._attr_status = "Cleaning spot" self.async_write_ha_state() async def async_locate(self, **kwargs): @@ -465,7 +435,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): self._retain, self._encoding, ) - self._status = "Hi, I'm over here!" + self._attr_status = "Hi, I'm over here!" self.async_write_ha_state() async def async_start_pause(self, **kwargs): @@ -480,7 +450,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): self._retain, self._encoding, ) - self._status = "Pausing/Resuming cleaning..." + self._attr_status = "Pausing/Resuming cleaning..." self.async_write_ha_state() async def async_return_to_base(self, **kwargs): @@ -495,14 +465,14 @@ class MqttVacuum(MqttEntity, VacuumEntity): self._retain, self._encoding, ) - self._status = "Returning home..." + self._attr_status = "Returning home..." self.async_write_ha_state() async def async_set_fan_speed(self, fan_speed, **kwargs): """Set fan speed.""" if ( self.supported_features & VacuumEntityFeature.FAN_SPEED == 0 - ) or fan_speed not in self._fan_speed_list: + ) or fan_speed not in self.fan_speed_list: return None await self.async_publish( @@ -512,7 +482,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): self._retain, self._encoding, ) - self._status = f"Setting fan to {fan_speed}..." + self._attr_status = f"Setting fan to {fan_speed}..." self.async_write_ha_state() async def async_send_command(self, command, params=None, **kwargs): @@ -532,5 +502,5 @@ class MqttVacuum(MqttEntity, VacuumEntity): self._retain, self._encoding, ) - self._status = f"Sending command {message}..." + self._attr_status = f"Sending command {message}..." self.async_write_ha_state() diff --git a/homeassistant/components/mqtt/vacuum/schema_state.py b/homeassistant/components/mqtt/vacuum/schema_state.py index 8dfaba80109..ede258102d7 100644 --- a/homeassistant/components/mqtt/vacuum/schema_state.py +++ b/homeassistant/components/mqtt/vacuum/schema_state.py @@ -161,9 +161,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): def __init__(self, hass, config, config_entry, discovery_data): """Initialize the vacuum.""" - self._state = None self._state_attrs = {} - self._fan_speed_list = [] MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @@ -173,11 +171,12 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): return DISCOVERY_SCHEMA_STATE def _setup_from_config(self, config): + """(Re)Setup the entity.""" supported_feature_strings = config[CONF_SUPPORTED_FEATURES] - self._supported_features = strings_to_services( + self._attr_supported_features = strings_to_services( supported_feature_strings, STRING_TO_SERVICE ) - self._fan_speed_list = config[CONF_FAN_SPEED_LIST] + self._attr_fan_speed_list = config[CONF_FAN_SPEED_LIST] self._command_topic = config.get(CONF_COMMAND_TOPIC) self._set_fan_speed_topic = config.get(CONF_SET_FAN_SPEED_TOPIC) self._send_command_topic = config.get(CONF_SEND_COMMAND_TOPIC) @@ -194,6 +193,12 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): ) } + def _update_state_attributes(self, payload): + """Update the entity state attributes.""" + self._state_attrs.update(payload) + self._attr_fan_speed = self._state_attrs.get(FAN_SPEED, 0) + self._attr_battery_level = max(0, min(100, self._state_attrs.get(BATTERY, 0))) + def _prepare_subscribe_topics(self): """(Re)Subscribe to topics.""" topics = {} @@ -206,11 +211,11 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): if STATE in payload and ( payload[STATE] in POSSIBLE_STATES or payload[STATE] is None ): - self._state = ( + self._attr_state = ( POSSIBLE_STATES[payload[STATE]] if payload[STATE] else None ) del payload[STATE] - self._state_attrs.update(payload) + self._update_state_attributes(payload) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) if self._config.get(CONF_STATE_TOPIC): @@ -228,31 +233,6 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) - @property - def state(self): - """Return state of vacuum.""" - return self._state - - @property - def fan_speed(self): - """Return fan speed of the vacuum.""" - return self._state_attrs.get(FAN_SPEED, 0) - - @property - def fan_speed_list(self): - """Return fan speed list of the vacuum.""" - return self._fan_speed_list - - @property - def battery_level(self): - """Return battery level of the vacuum.""" - return max(0, min(100, self._state_attrs.get(BATTERY, 0))) - - @property - def supported_features(self): - """Flag supported features.""" - return self._supported_features - async def async_start(self): """Start the vacuum.""" if self.supported_features & VacuumEntityFeature.START == 0: @@ -268,7 +248,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): async def async_pause(self): """Pause the vacuum.""" if self.supported_features & VacuumEntityFeature.PAUSE == 0: - return None + return await self.async_publish( self._command_topic, self._config[CONF_PAYLOAD_PAUSE], @@ -280,7 +260,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): async def async_stop(self, **kwargs): """Stop the vacuum.""" if self.supported_features & VacuumEntityFeature.STOP == 0: - return None + return await self.async_publish( self._command_topic, self._config[CONF_PAYLOAD_STOP], @@ -292,9 +272,9 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): async def async_set_fan_speed(self, fan_speed, **kwargs): """Set fan speed.""" if (self.supported_features & VacuumEntityFeature.FAN_SPEED == 0) or ( - fan_speed not in self._fan_speed_list + fan_speed not in self.fan_speed_list ): - return None + return await self.async_publish( self._set_fan_speed_topic, fan_speed, @@ -306,7 +286,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): async def async_return_to_base(self, **kwargs): """Tell the vacuum to return to its dock.""" if self.supported_features & VacuumEntityFeature.RETURN_HOME == 0: - return None + return await self.async_publish( self._command_topic, self._config[CONF_PAYLOAD_RETURN_TO_BASE], @@ -318,7 +298,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): async def async_clean_spot(self, **kwargs): """Perform a spot clean-up.""" if self.supported_features & VacuumEntityFeature.CLEAN_SPOT == 0: - return None + return await self.async_publish( self._command_topic, self._config[CONF_PAYLOAD_CLEAN_SPOT], @@ -330,7 +310,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): async def async_locate(self, **kwargs): """Locate the vacuum (usually by playing a song).""" if self.supported_features & VacuumEntityFeature.LOCATE == 0: - return None + return await self.async_publish( self._command_topic, self._config[CONF_PAYLOAD_LOCATE], @@ -342,7 +322,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): async def async_send_command(self, command, params=None, **kwargs): """Send a command to a vacuum cleaner.""" if self.supported_features & VacuumEntityFeature.SEND_COMMAND == 0: - return None + return if params: message = {"command": command} message.update(params) From 6021cedb09a2b7d3434d307bc2c124c51c8f731a Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 8 Nov 2022 21:54:01 +0200 Subject: [PATCH 0307/1033] deconz: Use partition instead of split where possible (#81804) * deconz: Use partition instead of split where possible With a smattering of code deduplication Split out of #81493 * Update homeassistant/components/deconz/util.py Co-authored-by: Robert Svensson Co-authored-by: Robert Svensson --- homeassistant/components/deconz/binary_sensor.py | 5 ++++- homeassistant/components/deconz/deconz_device.py | 5 ++--- homeassistant/components/deconz/number.py | 3 ++- homeassistant/components/deconz/sensor.py | 7 +++++-- homeassistant/components/deconz/util.py | 9 +++++++++ tests/components/deconz/test_logbook.py | 11 ++++++----- 6 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 homeassistant/components/deconz/util.py diff --git a/homeassistant/components/deconz/binary_sensor.py b/homeassistant/components/deconz/binary_sensor.py index 6e0c4c86d21..ef78e8f1419 100644 --- a/homeassistant/components/deconz/binary_sensor.py +++ b/homeassistant/components/deconz/binary_sensor.py @@ -33,6 +33,7 @@ import homeassistant.helpers.entity_registry as er from .const import ATTR_DARK, ATTR_ON, DOMAIN as DECONZ_DOMAIN from .deconz_device import DeconzDevice from .gateway import DeconzGateway, get_gateway_from_config_entry +from .util import serial_from_unique_id _SensorDeviceT = TypeVar("_SensorDeviceT", bound=PydeconzSensorBase) @@ -187,7 +188,9 @@ def async_update_unique_id( return if description.old_unique_id_suffix: - unique_id = f'{unique_id.split("-", 1)[0]}-{description.old_unique_id_suffix}' + unique_id = ( + f"{serial_from_unique_id(unique_id)}-{description.old_unique_id_suffix}" + ) if entity_id := ent_reg.async_get_entity_id(DOMAIN, DECONZ_DOMAIN, unique_id): ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id) diff --git a/homeassistant/components/deconz/deconz_device.py b/homeassistant/components/deconz/deconz_device.py index c2161baf100..6163db0dc65 100644 --- a/homeassistant/components/deconz/deconz_device.py +++ b/homeassistant/components/deconz/deconz_device.py @@ -17,6 +17,7 @@ from homeassistant.helpers.entity import DeviceInfo, Entity from .const import DOMAIN as DECONZ_DOMAIN from .gateway import DeconzGateway +from .util import serial_from_unique_id _DeviceT = TypeVar( "_DeviceT", @@ -55,9 +56,7 @@ class DeconzBase(Generic[_DeviceT]): def serial(self) -> str | None: """Return a serial number for this device.""" assert isinstance(self._device, PydeconzDevice) - if not self._device.unique_id or self._device.unique_id.count(":") != 7: - return None - return self._device.unique_id.split("-", 1)[0] + return serial_from_unique_id(self._device.unique_id) @property def device_info(self) -> DeviceInfo | None: diff --git a/homeassistant/components/deconz/number.py b/homeassistant/components/deconz/number.py index 789a155477a..154c988e07c 100644 --- a/homeassistant/components/deconz/number.py +++ b/homeassistant/components/deconz/number.py @@ -26,6 +26,7 @@ import homeassistant.helpers.entity_registry as er from .const import DOMAIN as DECONZ_DOMAIN from .deconz_device import DeconzDevice from .gateway import DeconzGateway, get_gateway_from_config_entry +from .util import serial_from_unique_id T = TypeVar("T", Presence, PydeconzSensorBase) @@ -88,7 +89,7 @@ def async_update_unique_id( if ent_reg.async_get_entity_id(DOMAIN, DECONZ_DOMAIN, new_unique_id): return - unique_id = f'{unique_id.split("-", 1)[0]}-{description.key}' + unique_id = f"{serial_from_unique_id(unique_id)}-{description.key}" if entity_id := ent_reg.async_get_entity_id(DOMAIN, DECONZ_DOMAIN, unique_id): ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id) diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index f1bd0118030..5305ea625b8 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -50,6 +50,7 @@ import homeassistant.util.dt as dt_util from .const import ATTR_DARK, ATTR_ON, DOMAIN as DECONZ_DOMAIN from .deconz_device import DeconzDevice from .gateway import DeconzGateway, get_gateway_from_config_entry +from .util import serial_from_unique_id PROVIDES_EXTRA_ATTRIBUTES = ( "battery", @@ -248,7 +249,9 @@ def async_update_unique_id( return if description.old_unique_id_suffix: - unique_id = f'{unique_id.split("-", 1)[0]}-{description.old_unique_id_suffix}' + unique_id = ( + f"{serial_from_unique_id(unique_id)}-{description.old_unique_id_suffix}" + ) if entity_id := ent_reg.async_get_entity_id(DOMAIN, DECONZ_DOMAIN, unique_id): ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id) @@ -290,7 +293,7 @@ async def async_setup_entry( sensor.type.startswith("CLIP") or (no_sensor_data and description.key != "battery") or ( - (unique_id := sensor.unique_id.rsplit("-", 1)[0]) + (unique_id := sensor.unique_id.rpartition("-")[0]) in known_device_entities[description.key] ) ): diff --git a/homeassistant/components/deconz/util.py b/homeassistant/components/deconz/util.py new file mode 100644 index 00000000000..4e7b1e7739f --- /dev/null +++ b/homeassistant/components/deconz/util.py @@ -0,0 +1,9 @@ +"""Utilities for deCONZ integration.""" +from __future__ import annotations + + +def serial_from_unique_id(unique_id: str | None) -> str | None: + """Get a device serial number from a unique ID, if possible.""" + if not unique_id or unique_id.count(":") != 7: + return None + return unique_id.partition("-")[0] diff --git a/tests/components/deconz/test_logbook.py b/tests/components/deconz/test_logbook.py index 9ba0799d04e..1680854302b 100644 --- a/tests/components/deconz/test_logbook.py +++ b/tests/components/deconz/test_logbook.py @@ -7,6 +7,7 @@ from homeassistant.components.deconz.deconz_event import ( CONF_DECONZ_ALARM_EVENT, CONF_DECONZ_EVENT, ) +from homeassistant.components.deconz.util import serial_from_unique_id from homeassistant.const import ( CONF_CODE, CONF_DEVICE_ID, @@ -60,7 +61,7 @@ async def test_humanifying_deconz_alarm_event(hass, aioclient_mock): device_registry = dr.async_get(hass) keypad_event_id = slugify(data["sensors"]["1"]["name"]) - keypad_serial = data["sensors"]["1"]["uniqueid"].split("-", 1)[0] + keypad_serial = serial_from_unique_id(data["sensors"]["1"]["uniqueid"]) keypad_entry = device_registry.async_get_device( identifiers={(DECONZ_DOMAIN, keypad_serial)} ) @@ -131,25 +132,25 @@ async def test_humanifying_deconz_event(hass, aioclient_mock): device_registry = dr.async_get(hass) switch_event_id = slugify(data["sensors"]["1"]["name"]) - switch_serial = data["sensors"]["1"]["uniqueid"].split("-", 1)[0] + switch_serial = serial_from_unique_id(data["sensors"]["1"]["uniqueid"]) switch_entry = device_registry.async_get_device( identifiers={(DECONZ_DOMAIN, switch_serial)} ) hue_remote_event_id = slugify(data["sensors"]["2"]["name"]) - hue_remote_serial = data["sensors"]["2"]["uniqueid"].split("-", 1)[0] + hue_remote_serial = serial_from_unique_id(data["sensors"]["2"]["uniqueid"]) hue_remote_entry = device_registry.async_get_device( identifiers={(DECONZ_DOMAIN, hue_remote_serial)} ) xiaomi_cube_event_id = slugify(data["sensors"]["3"]["name"]) - xiaomi_cube_serial = data["sensors"]["3"]["uniqueid"].split("-", 1)[0] + xiaomi_cube_serial = serial_from_unique_id(data["sensors"]["3"]["uniqueid"]) xiaomi_cube_entry = device_registry.async_get_device( identifiers={(DECONZ_DOMAIN, xiaomi_cube_serial)} ) faulty_event_id = slugify(data["sensors"]["4"]["name"]) - faulty_serial = data["sensors"]["4"]["uniqueid"].split("-", 1)[0] + faulty_serial = serial_from_unique_id(data["sensors"]["4"]["uniqueid"]) faulty_entry = device_registry.async_get_device( identifiers={(DECONZ_DOMAIN, faulty_serial)} ) From 12d76a8a4f1cbc9403798a32dd335d928dd61373 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 8 Nov 2022 22:14:17 +0100 Subject: [PATCH 0308/1033] Address late review of config entry wait for states tests (#81801) * Late review on tests #81771 * Clean-up duplicate assignment --- tests/test_config_entries.py | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 7684e9ff260..080b3cdf5f1 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -3332,7 +3332,7 @@ async def test_reauth(hass): assert len(hass.config_entries.flow.async_progress()) == 2 -async def test_wait_for_loading_entry(hass): +async def test_wait_for_loading_entry(hass: HomeAssistant) -> None: """Test waiting for entry to be set up.""" entry = MockConfigEntry(title="test_title", domain="test") @@ -3346,15 +3346,13 @@ async def test_wait_for_loading_entry(hass): flow = hass.config_entries.flow - async def _load_entry(): - # Mock config entry + async def _load_entry() -> None: assert await async_setup_component(hass, "test", {}) - entry = MockConfigEntry(title="test_title", domain="test") entry.add_to_hass(hass) flow = hass.config_entries.flow with patch.object(flow, "async_init", wraps=flow.async_init): - hass.async_add_job(_load_entry) + hass.async_create_task(_load_entry()) new_state = await hass.config_entries.async_wait_for_states( entry, { @@ -3367,7 +3365,7 @@ async def test_wait_for_loading_entry(hass): assert entry.state is config_entries.ConfigEntryState.LOADED -async def test_wait_for_loading_failed_entry(hass): +async def test_wait_for_loading_failed_entry(hass: HomeAssistant) -> None: """Test waiting for entry to be set up that fails loading.""" entry = MockConfigEntry(title="test_title", domain="test") @@ -3376,20 +3374,17 @@ async def test_wait_for_loading_failed_entry(hass): mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry)) mock_entity_platform(hass, "config_flow.test", None) - await entry.async_setup(hass) await hass.async_block_till_done() flow = hass.config_entries.flow - async def _load_entry(): - # Mock config entry + async def _load_entry() -> None: assert await async_setup_component(hass, "test", {}) - entry = MockConfigEntry(title="test_title", domain="test") entry.add_to_hass(hass) flow = hass.config_entries.flow with patch.object(flow, "async_init", wraps=flow.async_init): - hass.async_add_job(_load_entry) + hass.async_create_task(_load_entry()) new_state = await hass.config_entries.async_wait_for_states( entry, { @@ -3402,7 +3397,7 @@ async def test_wait_for_loading_failed_entry(hass): assert entry.state is config_entries.ConfigEntryState.SETUP_ERROR -async def test_wait_for_loading_timeout(hass): +async def test_wait_for_loading_timeout(hass: HomeAssistant) -> None: """Test waiting for entry to be set up that fails with a timeout.""" async def _async_setup_entry(hass, entry): @@ -3414,20 +3409,17 @@ async def test_wait_for_loading_timeout(hass): mock_integration(hass, MockModule("test", async_setup_entry=_async_setup_entry)) mock_entity_platform(hass, "config_flow.test", None) - await entry.async_setup(hass) await hass.async_block_till_done() flow = hass.config_entries.flow - async def _load_entry(): - # Mock config entry + async def _load_entry() -> None: assert await async_setup_component(hass, "test", {}) - entry = MockConfigEntry(title="test_title", domain="test") entry.add_to_hass(hass) flow = hass.config_entries.flow with patch.object(flow, "async_init", wraps=flow.async_init): - hass.async_add_job(_load_entry) + hass.async_create_task(_load_entry()) with pytest.raises(asyncio.exceptions.TimeoutError): await hass.config_entries.async_wait_for_states( entry, From 58c691be1e6f7966798a572c60579137b3c4cde6 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Wed, 9 Nov 2022 01:20:55 +0100 Subject: [PATCH 0309/1033] Update nibe to 1.2.1 with support for 2120 pumps (#81824) Update nibe to 1.2.1 --- homeassistant/components/nibe_heatpump/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nibe_heatpump/manifest.json b/homeassistant/components/nibe_heatpump/manifest.json index 68dc8c7a06c..511970832ed 100644 --- a/homeassistant/components/nibe_heatpump/manifest.json +++ b/homeassistant/components/nibe_heatpump/manifest.json @@ -3,7 +3,7 @@ "name": "Nibe Heat Pump", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/nibe_heatpump", - "requirements": ["nibe==1.2.0"], + "requirements": ["nibe==1.2.1"], "codeowners": ["@elupus"], "iot_class": "local_polling" } diff --git a/requirements_all.txt b/requirements_all.txt index 9946af9a968..55fc97cb6d3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1163,7 +1163,7 @@ nextcord==2.0.0a8 nextdns==1.1.1 # homeassistant.components.nibe_heatpump -nibe==1.2.0 +nibe==1.2.1 # homeassistant.components.niko_home_control niko-home-control==0.2.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9df97b9a7ae..5b7773c71f5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -850,7 +850,7 @@ nextcord==2.0.0a8 nextdns==1.1.1 # homeassistant.components.nibe_heatpump -nibe==1.2.0 +nibe==1.2.1 # homeassistant.components.nfandroidtv notifications-android-tv==0.1.5 From dcf68d768e4f628d038f1fdd6e40bad713fbc222 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 9 Nov 2022 00:27:20 +0000 Subject: [PATCH 0310/1033] [ci skip] Translation update --- .../components/airq/translations/pl.json | 22 ++++++++++++ .../components/arcam_fmj/translations/bg.json | 3 +- .../components/august/translations/bg.json | 1 + .../components/axis/translations/bg.json | 3 +- .../components/braviatv/translations/it.json | 3 ++ .../components/braviatv/translations/pl.json | 3 ++ .../components/daikin/translations/bg.json | 1 + .../components/directv/translations/bg.json | 3 ++ .../components/doorbird/translations/bg.json | 1 + .../components/econet/translations/bg.json | 3 ++ .../components/elkm1/translations/bg.json | 1 + .../components/epson/translations/bg.json | 3 ++ .../forecast_solar/translations/pl.json | 2 +- .../components/foscam/translations/bg.json | 3 ++ .../components/freebox/translations/bg.json | 1 + .../components/generic/translations/it.json | 4 +-- .../homeassistant/translations/bg.json | 6 ++++ .../homeassistant/translations/en.json | 6 ++++ .../homeassistant/translations/es.json | 6 ++++ .../homeassistant/translations/et.json | 6 ++++ .../homeassistant/translations/hu.json | 6 ++++ .../homeassistant/translations/it.json | 6 ++++ .../homeassistant/translations/no.json | 6 ++++ .../homeassistant/translations/pl.json | 6 ++++ .../homeassistant/translations/pt-BR.json | 6 ++++ .../homeassistant/translations/ru.json | 6 ++++ .../homeassistant/translations/zh-Hant.json | 6 ++++ .../components/iaqualink/translations/bg.json | 1 + .../components/ipp/translations/bg.json | 3 ++ .../components/livisi/translations/bg.json | 18 ++++++++++ .../components/livisi/translations/et.json | 18 ++++++++++ .../components/livisi/translations/it.json | 18 ++++++++++ .../components/livisi/translations/no.json | 18 ++++++++++ .../components/livisi/translations/pl.json | 18 ++++++++++ .../livisi/translations/zh-Hant.json | 18 ++++++++++ .../components/mazda/translations/bg.json | 1 + .../components/mqtt/translations/et.json | 6 ++-- .../components/mqtt/translations/it.json | 34 +++++++++---------- .../components/mqtt/translations/no.json | 8 ++--- .../components/mqtt/translations/pl.json | 8 ++--- .../components/mqtt/translations/pt-BR.json | 8 ++--- .../components/mqtt/translations/ru.json | 8 ++--- .../components/mqtt/translations/zh-Hant.json | 10 +++--- .../components/myq/translations/bg.json | 1 + .../components/nexia/translations/bg.json | 1 + .../nibe_heatpump/translations/bg.json | 7 ++++ .../nibe_heatpump/translations/en.json | 12 +++++++ .../nibe_heatpump/translations/es.json | 34 ++++++++++++++++++- .../nibe_heatpump/translations/et.json | 34 ++++++++++++++++++- .../nibe_heatpump/translations/hu.json | 34 ++++++++++++++++++- .../nibe_heatpump/translations/it.json | 34 ++++++++++++++++++- .../nibe_heatpump/translations/pl.json | 34 ++++++++++++++++++- .../nibe_heatpump/translations/pt-BR.json | 34 ++++++++++++++++++- .../nibe_heatpump/translations/ru.json | 34 ++++++++++++++++++- .../components/nuheat/translations/bg.json | 1 + .../components/nuki/translations/bg.json | 1 + .../components/onvif/translations/bg.json | 3 ++ .../components/openuv/translations/es.json | 10 +++++- .../components/openuv/translations/et.json | 10 +++++- .../components/openuv/translations/hu.json | 10 +++++- .../components/openuv/translations/it.json | 10 +++++- .../components/openuv/translations/pt-BR.json | 10 +++++- .../components/openuv/translations/ru.json | 10 +++++- .../panasonic_viera/translations/bg.json | 1 + .../components/powerwall/translations/bg.json | 3 ++ .../components/rachio/translations/bg.json | 3 ++ .../translations/bg.json | 1 + .../somfy_mylink/translations/bg.json | 5 +++ .../synology_dsm/translations/bg.json | 1 + .../totalconnect/translations/bg.json | 3 ++ .../unifiprotect/translations/it.json | 4 +-- .../unifiprotect/translations/pl.json | 6 ++++ .../unifiprotect/translations/ru.json | 4 +-- .../waze_travel_time/translations/bg.json | 3 ++ .../components/zwave_js/translations/bg.json | 1 + 75 files changed, 614 insertions(+), 63 deletions(-) create mode 100644 homeassistant/components/airq/translations/pl.json create mode 100644 homeassistant/components/livisi/translations/bg.json create mode 100644 homeassistant/components/livisi/translations/et.json create mode 100644 homeassistant/components/livisi/translations/it.json create mode 100644 homeassistant/components/livisi/translations/no.json create mode 100644 homeassistant/components/livisi/translations/pl.json create mode 100644 homeassistant/components/livisi/translations/zh-Hant.json diff --git a/homeassistant/components/airq/translations/pl.json b/homeassistant/components/airq/translations/pl.json new file mode 100644 index 00000000000..bf64c7906d9 --- /dev/null +++ b/homeassistant/components/airq/translations/pl.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "invalid_input": "Nieprawid\u0142owa nazwa hosta lub adres IP" + }, + "step": { + "user": { + "data": { + "ip_address": "Adres IP", + "password": "Has\u0142o" + }, + "description": "Podaj adres IP lub mDNS urz\u0105dzenia i jego has\u0142o", + "title": "Zidentyfikuj urz\u0105dzenie" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/arcam_fmj/translations/bg.json b/homeassistant/components/arcam_fmj/translations/bg.json index f24b5481b2c..60b4c65d0a3 100644 --- a/homeassistant/components/arcam_fmj/translations/bg.json +++ b/homeassistant/components/arcam_fmj/translations/bg.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "flow_title": "{host}", "step": { diff --git a/homeassistant/components/august/translations/bg.json b/homeassistant/components/august/translations/bg.json index f2dccb231c1..7c3899abed7 100644 --- a/homeassistant/components/august/translations/bg.json +++ b/homeassistant/components/august/translations/bg.json @@ -1,6 +1,7 @@ { "config": { "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" }, "step": { diff --git a/homeassistant/components/axis/translations/bg.json b/homeassistant/components/axis/translations/bg.json index 2cbf383cea8..e3ace757b0e 100644 --- a/homeassistant/components/axis/translations/bg.json +++ b/homeassistant/components/axis/translations/bg.json @@ -7,7 +7,8 @@ }, "error": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "already_in_progress": "\u0412 \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u0442\u0435\u0447\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e." + "already_in_progress": "\u0412 \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u0442\u0435\u0447\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e.", + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "flow_title": "Axis \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e: {name} ({host})", "step": { diff --git a/homeassistant/components/braviatv/translations/it.json b/homeassistant/components/braviatv/translations/it.json index 7bf9bb98b5a..e17c961fa45 100644 --- a/homeassistant/components/braviatv/translations/it.json +++ b/homeassistant/components/braviatv/translations/it.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "Si \u00e8 verificato un errore durante l'aggiornamento dell'elenco delle fonti.\n\nAssicurati che il televisore sia acceso prima di provare a configurarlo." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/pl.json b/homeassistant/components/braviatv/translations/pl.json index adc3a67e603..53847bf7b2c 100644 --- a/homeassistant/components/braviatv/translations/pl.json +++ b/homeassistant/components/braviatv/translations/pl.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "Wyst\u0105pi\u0142 b\u0142\u0105d podczas aktualizowania listy \u017ar\u00f3de\u0142.\n\nUpewnij si\u0119, \u017ce telewizor jest w\u0142\u0105czony, zanim spr\u00f3bujesz go skonfigurowa\u0107." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/daikin/translations/bg.json b/homeassistant/components/daikin/translations/bg.json index a07f37ab8d5..4ad69dc8249 100644 --- a/homeassistant/components/daikin/translations/bg.json +++ b/homeassistant/components/daikin/translations/bg.json @@ -6,6 +6,7 @@ }, "error": { "api_password": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435, \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0439\u0442\u0435 API \u043a\u043b\u044e\u0447 \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u0430.", + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { diff --git a/homeassistant/components/directv/translations/bg.json b/homeassistant/components/directv/translations/bg.json index b43da9ecb18..371990a6d32 100644 --- a/homeassistant/components/directv/translations/bg.json +++ b/homeassistant/components/directv/translations/bg.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/doorbird/translations/bg.json b/homeassistant/components/doorbird/translations/bg.json index 628eaf62894..02f7ea25f5f 100644 --- a/homeassistant/components/doorbird/translations/bg.json +++ b/homeassistant/components/doorbird/translations/bg.json @@ -1,6 +1,7 @@ { "config": { "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, diff --git a/homeassistant/components/econet/translations/bg.json b/homeassistant/components/econet/translations/bg.json index 637413ad06d..aeec4d24e19 100644 --- a/homeassistant/components/econet/translations/bg.json +++ b/homeassistant/components/econet/translations/bg.json @@ -4,6 +4,9 @@ "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/elkm1/translations/bg.json b/homeassistant/components/elkm1/translations/bg.json index 46a60e96408..5a7a68927a8 100644 --- a/homeassistant/components/elkm1/translations/bg.json +++ b/homeassistant/components/elkm1/translations/bg.json @@ -6,6 +6,7 @@ "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" }, "flow_title": "{mac_address} ({host})", diff --git a/homeassistant/components/epson/translations/bg.json b/homeassistant/components/epson/translations/bg.json index a051d6ca487..d2c9013bcc5 100644 --- a/homeassistant/components/epson/translations/bg.json +++ b/homeassistant/components/epson/translations/bg.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/pl.json b/homeassistant/components/forecast_solar/translations/pl.json index ad01ce4bb54..c4be17eed34 100644 --- a/homeassistant/components/forecast_solar/translations/pl.json +++ b/homeassistant/components/forecast_solar/translations/pl.json @@ -25,7 +25,7 @@ "inverter_size": "Rozmiar falownika (Wat)", "modules power": "Ca\u0142kowita moc szczytowa modu\u0142\u00f3w fotowoltaicznych w watach" }, - "description": "Te warto\u015bci pozwalaj\u0105 dostosowa\u0107 wyniki dla Solar.Forecast. Prosz\u0119 zapozna\u0107 si\u0119 z dokumentacj\u0105, je\u015bli pole jest niejasne." + "description": "Te warto\u015bci pozwalaj\u0105 dostosowa\u0107 wyniki dla Forecast.Solar. Prosz\u0119 zapozna\u0107 si\u0119 z dokumentacj\u0105, je\u015bli pole jest niejasne." } } } diff --git a/homeassistant/components/foscam/translations/bg.json b/homeassistant/components/foscam/translations/bg.json index 8e0b1bac052..e660bd80f53 100644 --- a/homeassistant/components/foscam/translations/bg.json +++ b/homeassistant/components/foscam/translations/bg.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/freebox/translations/bg.json b/homeassistant/components/freebox/translations/bg.json index 9a63019cd8a..dfb1e6f4932 100644 --- a/homeassistant/components/freebox/translations/bg.json +++ b/homeassistant/components/freebox/translations/bg.json @@ -1,6 +1,7 @@ { "config": { "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "register_failed": "\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0435 \u0431\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430, \u043c\u043e\u043b\u044f, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, diff --git a/homeassistant/components/generic/translations/it.json b/homeassistant/components/generic/translations/it.json index 4e8cdb513cd..092e1aa45a6 100644 --- a/homeassistant/components/generic/translations/it.json +++ b/homeassistant/components/generic/translations/it.json @@ -50,7 +50,7 @@ "data": { "confirmed_ok": "Questa immagine sembra buona." }, - "description": "![Anteprima immagine fissa fotocamera]({preview_url})", + "description": "![Anteprima immagine fissa della fotocamera]({preview_url})", "title": "Anteprima" } } @@ -79,7 +79,7 @@ "data": { "confirmed_ok": "Questa immagine sembra buona." }, - "description": "![Anteprima immagine fissa fotocamera]({preview_url})", + "description": "![Anteprima immagine fissa della fotocamera]({preview_url})", "title": "Anteprima" }, "content_type": { diff --git a/homeassistant/components/homeassistant/translations/bg.json b/homeassistant/components/homeassistant/translations/bg.json index 260c7bcb57c..2c33a7925c2 100644 --- a/homeassistant/components/homeassistant/translations/bg.json +++ b/homeassistant/components/homeassistant/translations/bg.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "\u0412\u0430\u043b\u0443\u0442\u0430\u0442\u0430 {currency} \u0432\u0435\u0447\u0435 \u043d\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430, \u043c\u043e\u043b\u044f, \u043f\u0440\u0435\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0439\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 \u0432\u0430\u043b\u0443\u0442\u0430\u0442\u0430.", + "title": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430\u0442\u0430 \u0432\u0430\u043b\u0443\u0442\u0430 \u0432\u0435\u0447\u0435 \u043d\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430" + } + }, "system_health": { "info": { "arch": "\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043d\u0430 CPU", diff --git a/homeassistant/components/homeassistant/translations/en.json b/homeassistant/components/homeassistant/translations/en.json index 37c4498b32b..8008aa813aa 100644 --- a/homeassistant/components/homeassistant/translations/en.json +++ b/homeassistant/components/homeassistant/translations/en.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "The currency {currency} is no longer in use, please reconfigure the currency configuration.", + "title": "The configured currency is no longer in use" + } + }, "system_health": { "info": { "arch": "CPU Architecture", diff --git a/homeassistant/components/homeassistant/translations/es.json b/homeassistant/components/homeassistant/translations/es.json index 5b447f7177f..e0cb62bee3f 100644 --- a/homeassistant/components/homeassistant/translations/es.json +++ b/homeassistant/components/homeassistant/translations/es.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "La moneda {currency} ya no est\u00e1 en uso, por favor, vuelve a configurar la moneda.", + "title": "La moneda configurada ya no est\u00e1 en uso" + } + }, "system_health": { "info": { "arch": "Arquitectura de CPU", diff --git a/homeassistant/components/homeassistant/translations/et.json b/homeassistant/components/homeassistant/translations/et.json index 7b9b675ed6f..e1c765556cf 100644 --- a/homeassistant/components/homeassistant/translations/et.json +++ b/homeassistant/components/homeassistant/translations/et.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "Valuuta {currency} ei ole enam kasutusel, seadista valuuta uuesti.", + "title": "Seadistatud valuutat enam ei kasutata" + } + }, "system_health": { "info": { "arch": "Protsessori arhitektuur", diff --git a/homeassistant/components/homeassistant/translations/hu.json b/homeassistant/components/homeassistant/translations/hu.json index 7261dfa1f7a..c7fdde1ca44 100644 --- a/homeassistant/components/homeassistant/translations/hu.json +++ b/homeassistant/components/homeassistant/translations/hu.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "{currency} p\u00e9nznem m\u00e1r nincs haszn\u00e1latban, k\u00e9rj\u00fck, konfigur\u00e1lja \u00fajra a p\u00e9nznemet.", + "title": "A be\u00e1ll\u00edtott p\u00e9nznem m\u00e1r nincs haszn\u00e1latban" + } + }, "system_health": { "info": { "arch": "Processzor architekt\u00fara", diff --git a/homeassistant/components/homeassistant/translations/it.json b/homeassistant/components/homeassistant/translations/it.json index 432fb9dea46..abc7c056692 100644 --- a/homeassistant/components/homeassistant/translations/it.json +++ b/homeassistant/components/homeassistant/translations/it.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "La valuta {currency} non \u00e8 pi\u00f9 in uso, riconfigura la configurazione della valuta.", + "title": "La valuta configurata non \u00e8 pi\u00f9 in uso" + } + }, "system_health": { "info": { "arch": "Architettura della CPU", diff --git a/homeassistant/components/homeassistant/translations/no.json b/homeassistant/components/homeassistant/translations/no.json index 0932c2e75a6..e98f298b5f9 100644 --- a/homeassistant/components/homeassistant/translations/no.json +++ b/homeassistant/components/homeassistant/translations/no.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "Valutaen {currency} er ikke lenger i bruk, vennligst konfigurer valutakonfigurasjonen p\u00e5 nytt.", + "title": "Tollet er ugyldig eller ikke lenger autorisert." + } + }, "system_health": { "info": { "arch": "CPU-arkitektur", diff --git a/homeassistant/components/homeassistant/translations/pl.json b/homeassistant/components/homeassistant/translations/pl.json index bdf26a8b49d..b40abbe44e7 100644 --- a/homeassistant/components/homeassistant/translations/pl.json +++ b/homeassistant/components/homeassistant/translations/pl.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "Waluta {currency} nie jest ju\u017c u\u017cywana. Zmie\u0144 konfiguracj\u0119 waluty.", + "title": "Skonfigurowana waluta nie jest ju\u017c u\u017cywana" + } + }, "system_health": { "info": { "arch": "Architektura procesora", diff --git a/homeassistant/components/homeassistant/translations/pt-BR.json b/homeassistant/components/homeassistant/translations/pt-BR.json index 5ea540d67f3..3fb4e740548 100644 --- a/homeassistant/components/homeassistant/translations/pt-BR.json +++ b/homeassistant/components/homeassistant/translations/pt-BR.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "A moeda {currency} n\u00e3o est\u00e1 mais em uso, reconfigure a configura\u00e7\u00e3o da moeda.", + "title": "A moeda configurada n\u00e3o est\u00e1 mais em uso" + } + }, "system_health": { "info": { "arch": "Arquitetura da CPU", diff --git a/homeassistant/components/homeassistant/translations/ru.json b/homeassistant/components/homeassistant/translations/ru.json index b0e27b70861..d6326763387 100644 --- a/homeassistant/components/homeassistant/translations/ru.json +++ b/homeassistant/components/homeassistant/translations/ru.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "\u0412\u0430\u043b\u044e\u0442\u0430 {currency} \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f. \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0432\u0430\u043b\u044e\u0442\u044b.", + "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0432\u0430\u043b\u044e\u0442\u0430 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f" + } + }, "system_health": { "info": { "arch": "\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0426\u041f", diff --git a/homeassistant/components/homeassistant/translations/zh-Hant.json b/homeassistant/components/homeassistant/translations/zh-Hant.json index c42acf960c6..dc43dcaf7d3 100644 --- a/homeassistant/components/homeassistant/translations/zh-Hant.json +++ b/homeassistant/components/homeassistant/translations/zh-Hant.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "\u8ca8\u5e63 {currency} \u4e0d\u518d\u4f7f\u7528\u3001\u8acb\u91cd\u65b0\u8a2d\u5b9a\u5176\u4ed6\u8ca8\u5e63\u3002", + "title": "\u8a2d\u5b9a\u8ca8\u5e63\u4e0d\u518d\u4f7f\u7528" + } + }, "system_health": { "info": { "arch": "CPU \u67b6\u69cb", diff --git a/homeassistant/components/iaqualink/translations/bg.json b/homeassistant/components/iaqualink/translations/bg.json index d6a28310dd1..07af4cdf9a3 100644 --- a/homeassistant/components/iaqualink/translations/bg.json +++ b/homeassistant/components/iaqualink/translations/bg.json @@ -1,6 +1,7 @@ { "config": { "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" }, "step": { diff --git a/homeassistant/components/ipp/translations/bg.json b/homeassistant/components/ipp/translations/bg.json index 19680bdcfb4..bac35896d94 100644 --- a/homeassistant/components/ipp/translations/bg.json +++ b/homeassistant/components/ipp/translations/bg.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, diff --git a/homeassistant/components/livisi/translations/bg.json b/homeassistant/components/livisi/translations/bg.json new file mode 100644 index 00000000000..76c9066450f --- /dev/null +++ b/homeassistant/components/livisi/translations/bg.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "wrong_ip_address": "IP \u0430\u0434\u0440\u0435\u0441\u044a\u0442 \u0435 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u0435\u043d \u0438\u043b\u0438 SHC \u043d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0431\u044a\u0434\u0435 \u0434\u043e\u0441\u0442\u0438\u0433\u043d\u0430\u0442 \u043b\u043e\u043a\u0430\u043b\u043d\u043e.", + "wrong_password": "\u041f\u0430\u0440\u043e\u043b\u0430\u0442\u0430 \u0435 \u0433\u0440\u0435\u0448\u043d\u0430." + }, + "step": { + "user": { + "data": { + "host": "IP \u0430\u0434\u0440\u0435\u0441", + "password": "\u041f\u0430\u0440\u043e\u043b\u0430" + }, + "description": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 IP \u0430\u0434\u0440\u0435\u0441\u0430 \u0438 (\u043b\u043e\u043a\u0430\u043b\u043d\u0430\u0442\u0430) \u043f\u0430\u0440\u043e\u043b\u0430 \u043d\u0430 SHC." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/et.json b/homeassistant/components/livisi/translations/et.json new file mode 100644 index 00000000000..07459d159f4 --- /dev/null +++ b/homeassistant/components/livisi/translations/et.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "wrong_ip_address": "IP-aadress on vale v\u00f5i SHC-d ei ole v\u00f5imalik kohtv\u00f5rgus k\u00e4tte saada.", + "wrong_password": "Salas\u00f5na on vale." + }, + "step": { + "user": { + "data": { + "host": "IP aadress", + "password": "Salas\u00f5na" + }, + "description": "Sisesta SHC IP-aadress ja (kohalik) parool." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/it.json b/homeassistant/components/livisi/translations/it.json new file mode 100644 index 00000000000..aa39f1037a9 --- /dev/null +++ b/homeassistant/components/livisi/translations/it.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Impossibile connettersi", + "wrong_ip_address": "L'indirizzo IP non \u00e8 corretto o l'SHC non pu\u00f2 essere raggiunto localmente.", + "wrong_password": "La password non \u00e8 corretta." + }, + "step": { + "user": { + "data": { + "host": "Indirizzo IP", + "password": "Password" + }, + "description": "Immettere l'indirizzo IP e la password (locale) dell'SHC." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/no.json b/homeassistant/components/livisi/translations/no.json new file mode 100644 index 00000000000..a121f5ba163 --- /dev/null +++ b/homeassistant/components/livisi/translations/no.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Tilkobling mislyktes", + "wrong_ip_address": "IP-adressen er feil eller SHC kan ikke n\u00e5s lokalt.", + "wrong_password": "Passordet er feil." + }, + "step": { + "user": { + "data": { + "host": "IP adresse", + "password": "Passord" + }, + "description": "Skriv inn IP-adressen og det (lokale) passordet til SHC." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/pl.json b/homeassistant/components/livisi/translations/pl.json new file mode 100644 index 00000000000..70fd9de4d7d --- /dev/null +++ b/homeassistant/components/livisi/translations/pl.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "wrong_ip_address": "Adres IP jest nieprawid\u0142owy lub SHC nie jest dost\u0119pne lokalnie.", + "wrong_password": "Has\u0142o jest nieprawid\u0142owe." + }, + "step": { + "user": { + "data": { + "host": "Adres IP", + "password": "Has\u0142o" + }, + "description": "Wprowad\u017a adres IP i (lokalne) has\u0142o SHC." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/zh-Hant.json b/homeassistant/components/livisi/translations/zh-Hant.json new file mode 100644 index 00000000000..b1bced211d4 --- /dev/null +++ b/homeassistant/components/livisi/translations/zh-Hant.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "wrong_ip_address": "IP \u4f4d\u5740\u932f\u8aa4\u6216 SHC \u7121\u6cd5\u900f\u904e\u672c\u5e95\u7aef\u627e\u5230\u88dd\u7f6e\u3002", + "wrong_password": "\u5bc6\u78bc\u932f\u8aa4\u3002" + }, + "step": { + "user": { + "data": { + "host": "IP \u4f4d\u5740", + "password": "\u5bc6\u78bc" + }, + "description": "\u8f38\u5165 IP \u4f4d\u5740\u53ca\u672c\u5730\u7aef SHC \u5bc6\u78bc\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/bg.json b/homeassistant/components/mazda/translations/bg.json index 1eb89184642..2cb991851ca 100644 --- a/homeassistant/components/mazda/translations/bg.json +++ b/homeassistant/components/mazda/translations/bg.json @@ -2,6 +2,7 @@ "config": { "error": { "account_locked": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0435 \u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d. \u041c\u043e\u043b\u044f, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e \u043f\u043e-\u043a\u044a\u0441\u043d\u043e.", + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { diff --git a/homeassistant/components/mqtt/translations/et.json b/homeassistant/components/mqtt/translations/et.json index 373a8a64a95..47bf2cfd264 100644 --- a/homeassistant/components/mqtt/translations/et.json +++ b/homeassistant/components/mqtt/translations/et.json @@ -20,10 +20,10 @@ "data": { "advanced_options": "T\u00e4psemad s\u00e4tted", "broker": "Vahendaja", - "certificate": "Tee kohandatud CA-sertifikaadifaili juurde", - "client_cert": "Kliendi serdifaili tee", + "certificate": "Lae \u00fcles kohandatud CA-sertifikaadi fail", + "client_cert": "Lae \u00fcles kliendi sertifikaadifail", "client_id": "Kliendi ID (juhuslikult genereeritud ID jaoks j\u00e4ta t\u00fchjaks)", - "client_key": "Tee privaatse v\u00f5tme faili juurde", + "client_key": "Lae \u00fcles privaatv\u00f5tme fail", "discovery": "Luba automaatne avastamine", "keepalive": "Aegumiss\u00f5numite saatmise vaheline aeg", "password": "Salas\u00f5na", diff --git a/homeassistant/components/mqtt/translations/it.json b/homeassistant/components/mqtt/translations/it.json index f8add8256bc..74febaf9cae 100644 --- a/homeassistant/components/mqtt/translations/it.json +++ b/homeassistant/components/mqtt/translations/it.json @@ -10,7 +10,7 @@ "bad_client_cert": "Certificato client non valido, assicurarsi che venga fornito un file codificato PEM", "bad_client_cert_key": "Il certificato del client e il certificato privato non sono una coppia valida", "bad_client_key": "Chiave privata non valida, assicurarsi che venga fornito un file codificato PEM senza password", - "bad_discovery_prefix": "Prefisso di ricerca non valido", + "bad_discovery_prefix": "Prefisso di rilevamento non valido", "bad_will": "Argomento testamento non valido", "cannot_connect": "Impossibile connettersi", "invalid_inclusion": "Il certificato del client e la chiave privata devono essere configurati insieme" @@ -20,15 +20,15 @@ "data": { "advanced_options": "Opzioni avanzate", "broker": "Broker", - "certificate": "Percorso del file del certificato CA personalizzato", - "client_cert": "Percorso per un file di certificato cliente", - "client_id": "ID cliente (lasciare vuoto per generarne uno in modo casuale)", - "client_key": "Percorso per un file della chiave privata", + "certificate": "Carica il file del certificato CA personalizzato", + "client_cert": "Carica il file del certificato client", + "client_id": "ID client (lasciare vuoto per generarne uno casualmente)", + "client_key": "Carica il file della chiave privata", "discovery": "Attiva il rilevamento", - "keepalive": "L'intervallo di tempo tra l'invio di messaggi di mantenimento attivit\u00e0", + "keepalive": "L'intervallo di tempo tra l'invio di messaggi di mantenimento in attivit\u00e0", "password": "Password", "port": "Porta", - "protocol": "Protocollo MQTT", + "protocol": "protocollo MQTT", "set_ca_cert": "Convalida del certificato del broker", "set_client_cert": "Usa un certificato client", "tls_insecure": "Ignora la convalida del certificato del broker", @@ -38,7 +38,7 @@ }, "hassio_confirm": { "data": { - "discovery": "Attiva il rilevamento" + "discovery": "Abilita il rilevamento" }, "description": "Vuoi configurare Home Assistant per connettersi al broker MQTT fornito dal componente aggiuntivo: {addon}?", "title": "Broker MQTT tramite il componente aggiuntivo di Home Assistant" @@ -81,13 +81,13 @@ "error": { "bad_birth": "Argomento di nascita non valido", "bad_certificate": "Il certificato CA non \u00e8 valido", - "bad_client_cert": "Certificato cliente non valido, assicurarsi che venga fornito un file con codice PEM", + "bad_client_cert": "Certificato client non valido, assicurarsi che venga fornito un file codificato PEM", "bad_client_cert_key": "Il certificato del client e il certificato privato non sono una coppia valida", "bad_client_key": "Chiave privata non valida, assicurarsi che venga fornito un file codificato PEM senza password", "bad_discovery_prefix": "Prefisso di rilevamento non valido", - "bad_will": "Argomento di testamento non valido", + "bad_will": "Argomento testamento non valido", "cannot_connect": "Impossibile connettersi", - "invalid_inclusion": "Il certificato e la chiave privata del client devono essere configurati insieme" + "invalid_inclusion": "Il certificato del client e la chiave privata devono essere configurati insieme" }, "step": { "broker": { @@ -95,15 +95,15 @@ "advanced_options": "Opzioni avanzate", "broker": "Broker", "certificate": "Carica il file del certificato CA personalizzato", - "client_cert": "Carica il file del certificato cliente", - "client_id": "ID cliente (lasciare vuoto per generarne uno in modo casuale)", + "client_cert": "Carica il file del certificato client", + "client_id": "ID client (lasciare vuoto per generarne uno casualmente)", "client_key": "Carica il file della chiave privata", - "keepalive": "Il tempo che intercorre tra l'invio di messaggi di mantenimento comunicazioni", + "keepalive": "L'intervallo di tempo tra l'invio di messaggi di mantenimento in attivit\u00e0", "password": "Password", "port": "Porta", - "protocol": "Protocollo MQTT", + "protocol": "protocollo MQTT", "set_ca_cert": "Convalida del certificato del broker", - "set_client_cert": "Utilizza un certificato client", + "set_client_cert": "Usa un certificato client", "tls_insecure": "Ignora la convalida del certificato del broker", "username": "Nome utente" }, @@ -117,7 +117,7 @@ "birth_qos": "QoS del messaggio birth", "birth_retain": "Persistenza del messaggio birth", "birth_topic": "Argomento del messaggio birth", - "discovery": "Attiva il rilevamento", + "discovery": "Abilita il rilevamento", "discovery_prefix": "Rileva prefisso", "will_enable": "Abilita il messaggio testamento", "will_payload": "Payload del messaggio testamento", diff --git a/homeassistant/components/mqtt/translations/no.json b/homeassistant/components/mqtt/translations/no.json index 910f47ac02a..e1f27d29ae9 100644 --- a/homeassistant/components/mqtt/translations/no.json +++ b/homeassistant/components/mqtt/translations/no.json @@ -20,10 +20,10 @@ "data": { "advanced_options": "Avanserte instillinger", "broker": "Megler", - "certificate": "Bane til egendefinert CA-sertifikatfil", - "client_cert": "Bane til en klientsertifikatfil", + "certificate": "Last opp egendefinert CA-sertifikatfil", + "client_cert": "Last opp klientsertifikatfil", "client_id": "Klient-ID (la st\u00e5 tomt til tilfeldig generert)", - "client_key": "Bane til en privat n\u00f8kkelfil", + "client_key": "Last opp privat n\u00f8kkelfil", "discovery": "Aktiver oppdagelse", "keepalive": "Tiden mellom sending hold levende meldinger", "password": "Passord", @@ -107,7 +107,7 @@ "tls_insecure": "Ignorer validering av meglersertifikat", "username": "Brukernavn" }, - "description": "Vennligst oppgi tilkoblingsinformasjonen for din MQTT megler.", + "description": "Vennligst fyll ut tilkoblingsinformasjonen for din MQTT megler.", "title": "Megleralternativer" }, "options": { diff --git a/homeassistant/components/mqtt/translations/pl.json b/homeassistant/components/mqtt/translations/pl.json index 0084c0a1b12..33284dcd7c5 100644 --- a/homeassistant/components/mqtt/translations/pl.json +++ b/homeassistant/components/mqtt/translations/pl.json @@ -20,10 +20,10 @@ "data": { "advanced_options": "Opcje zaawansowane", "broker": "Po\u015brednik", - "certificate": "\u015acie\u017cka do pliku z niestandardowym certyfikatem CA", - "client_cert": "\u015acie\u017cka do pliku certyfikatu klienta", + "certificate": "Prze\u015blij plik z niestandardowym certyfikatem CA", + "client_cert": "Prze\u015blij plik certyfikatu klienta", "client_id": "Identyfikator klienta (pozostaw puste, aby wygenerowa\u0107 losowo)", - "client_key": "\u015acie\u017cka do pliku klucza prywatnego", + "client_key": "Prze\u015blij plik klucza prywatnego", "discovery": "W\u0142\u0105cz wykrywanie", "keepalive": "Czas pomi\u0119dzy wys\u0142aniem wiadomo\u015bci \"keep alive\"", "password": "Has\u0142o", @@ -107,7 +107,7 @@ "tls_insecure": "Ignoruj sprawdzanie certyfikatu brokera", "username": "Nazwa u\u017cytkownika" }, - "description": "Wprowad\u017a informacje o po\u0142\u0105czeniu po\u015brednika MQTT", + "description": "Wprowad\u017a informacje o po\u0142\u0105czeniu po\u015brednika MQTT.", "title": "Opcje brokera" }, "options": { diff --git a/homeassistant/components/mqtt/translations/pt-BR.json b/homeassistant/components/mqtt/translations/pt-BR.json index d9e8ac43192..d0fb407331a 100644 --- a/homeassistant/components/mqtt/translations/pt-BR.json +++ b/homeassistant/components/mqtt/translations/pt-BR.json @@ -20,10 +20,10 @@ "data": { "advanced_options": "Op\u00e7\u00f5es avan\u00e7adas", "broker": "Endere\u00e7o do Broker", - "certificate": "Caminho para o arquivo de certificado de CA personalizado", - "client_cert": "Caminho para um arquivo de certificado de cliente", + "certificate": "Carregar arquivo de certificado de CA personalizado", + "client_cert": "Carregar arquivo de certificado do cliente", "client_id": "ID do cliente (deixe em branco para um gerado aleatoriamente)", - "client_key": "Caminho para um arquivo de chave privada", + "client_key": "Carregar arquivo de chave privada", "discovery": "Ativar descoberta", "keepalive": "O tempo entre o envio de mensagens de manuten\u00e7\u00e3o viva", "password": "Senha", @@ -93,7 +93,7 @@ "broker": { "data": { "advanced_options": "Op\u00e7\u00f5es avan\u00e7adas", - "broker": "", + "broker": "Broker", "certificate": "Carregar arquivo de certificado de CA personalizado", "client_cert": "Carregar arquivo de certificado do cliente", "client_id": "ID do cliente (deixe em branco para um gerado aleatoriamente)", diff --git a/homeassistant/components/mqtt/translations/ru.json b/homeassistant/components/mqtt/translations/ru.json index 069a1066c86..7e2fe9ffe55 100644 --- a/homeassistant/components/mqtt/translations/ru.json +++ b/homeassistant/components/mqtt/translations/ru.json @@ -20,10 +20,10 @@ "data": { "advanced_options": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438", "broker": "\u0411\u0440\u043e\u043a\u0435\u0440", - "certificate": "\u041f\u0443\u0442\u044c \u043a \u0444\u0430\u0439\u043b\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0426\u0421", - "client_cert": "\u041f\u0443\u0442\u044c \u043a \u0444\u0430\u0439\u043b\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430", + "certificate": "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0426\u0421", + "client_cert": "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430", "client_id": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 (\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c)", - "client_key": "\u041f\u0443\u0442\u044c \u043a \u0444\u0430\u0439\u043b\u0443 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430", + "client_key": "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430", "discovery": "\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432", "keepalive": "\u0412\u0440\u0435\u043c\u044f \u043c\u0435\u0436\u0434\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 Keep Alive", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", @@ -77,7 +77,7 @@ "error": { "bad_birth": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u043e\u043f\u0438\u043a \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438.", "bad_certificate": "\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u0426\u0421 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u0435\u043d.", - "bad_client_cert": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0444\u0430\u0439\u043b, \u0437\u0430\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 PEM.", + "bad_client_cert": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0444\u0430\u0439\u043b, \u0437\u0430\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 PEM", "bad_client_cert_key": "\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u043d\u0435 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0430\u0440\u043e\u0439.", "bad_client_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0444\u0430\u0439\u043b \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 PEM \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f.", "bad_discovery_prefix": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u0442\u043e\u043f\u0438\u043a\u0430 \u0430\u0432\u0442\u043e\u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f.", diff --git a/homeassistant/components/mqtt/translations/zh-Hant.json b/homeassistant/components/mqtt/translations/zh-Hant.json index eadaebee738..23cd9b3e109 100644 --- a/homeassistant/components/mqtt/translations/zh-Hant.json +++ b/homeassistant/components/mqtt/translations/zh-Hant.json @@ -20,10 +20,10 @@ "data": { "advanced_options": "\u9032\u968e\u8a2d\u5b9a", "broker": "Broker", - "certificate": "\u81ea\u8a02 CA \u6191\u8b49\u6a94\u6848\u8def\u5f91", - "client_cert": "\u5ba2\u6236\u7aef\u6191\u8b49\u6a94\u6848\u8def\u5f91", + "certificate": "\u4e0a\u50b3\u81ea\u8a02 CA \u6191\u8b49\u6a94\u6848", + "client_cert": "\u4e0a\u50b3\u5ba2\u6236\u7aef\u6191\u8b49\u6a94\u6848", "client_id": "\u5ba2\u6236\u7aef ID (\u4fdd\u6301\u7a7a\u767d\u5c07\u81ea\u52d5\u7522\u751f)", - "client_key": "\u79c1\u9470\u6a94\u6848\u8def\u5f91", + "client_key": "\u4e0a\u50b3\u79c1\u9470\u6a94\u6848", "discovery": "\u958b\u555f\u641c\u5c0b", "keepalive": "\u50b3\u9001\u4fdd\u6301\u6d3b\u52d5\u8a0a\u606f\u9593\u9694\u6642\u9593", "password": "\u5bc6\u78bc", @@ -94,7 +94,7 @@ "data": { "advanced_options": "\u9032\u968e\u8a2d\u5b9a", "broker": "Broker", - "certificate": "\u4e0a\u50b3 CA \u6191\u8b49\u6a94\u6848", + "certificate": "\u4e0a\u50b3\u81ea\u8a02 CA \u6191\u8b49\u6a94\u6848", "client_cert": "\u4e0a\u50b3\u5ba2\u6236\u7aef\u6191\u8b49\u6a94\u6848", "client_id": "\u5ba2\u6236\u7aef ID (\u4fdd\u6301\u7a7a\u767d\u5c07\u81ea\u52d5\u7522\u751f)", "client_key": "\u4e0a\u50b3\u79c1\u9470\u6a94\u6848", @@ -117,7 +117,7 @@ "birth_qos": "Birth \u8a0a\u606f QoS", "birth_retain": "Birth \u8a0a\u606f Retain", "birth_topic": "Birth \u8a0a\u606f\u4e3b\u984c", - "discovery": "\u958b\u555f\u63a2\u7d22", + "discovery": "\u958b\u555f\u641c\u5c0b", "discovery_prefix": "\u63a2\u7d22 prefix", "will_enable": "\u958b\u555f Will \u8a0a\u606f", "will_payload": "Will \u8a0a\u606f payload", diff --git a/homeassistant/components/myq/translations/bg.json b/homeassistant/components/myq/translations/bg.json index 728682f531e..f175754fdca 100644 --- a/homeassistant/components/myq/translations/bg.json +++ b/homeassistant/components/myq/translations/bg.json @@ -5,6 +5,7 @@ "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" }, "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { diff --git a/homeassistant/components/nexia/translations/bg.json b/homeassistant/components/nexia/translations/bg.json index 7aa8fb275ea..5ef9f8721aa 100644 --- a/homeassistant/components/nexia/translations/bg.json +++ b/homeassistant/components/nexia/translations/bg.json @@ -4,6 +4,7 @@ "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" }, "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" }, "step": { diff --git a/homeassistant/components/nibe_heatpump/translations/bg.json b/homeassistant/components/nibe_heatpump/translations/bg.json index 88f52d84269..838fde81831 100644 --- a/homeassistant/components/nibe_heatpump/translations/bg.json +++ b/homeassistant/components/nibe_heatpump/translations/bg.json @@ -5,6 +5,13 @@ }, "error": { "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "user": { + "menu_options": { + "nibegw": "NibeGW" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/nibe_heatpump/translations/en.json b/homeassistant/components/nibe_heatpump/translations/en.json index 167a8341cd3..a662743c460 100644 --- a/homeassistant/components/nibe_heatpump/translations/en.json +++ b/homeassistant/components/nibe_heatpump/translations/en.json @@ -41,6 +41,18 @@ "description": "Before attempting to configure the integration, verify that:\n - The NibeGW unit is connected to a heat pump.\n - The MODBUS40 accessory has been enabled in the heat pump configuration.\n - The pump has not gone into an alarm state about missing MODBUS40 accessory." }, "user": { + "data": { + "ip_address": "Remote address", + "listening_port": "Local listening port", + "remote_read_port": "Remote read port", + "remote_write_port": "Remote write port" + }, + "data_description": { + "ip_address": "The address of the NibeGW unit. The device should have been configured with a static address.", + "listening_port": "The local port on this system, that the NibeGW unit is configured to send data to.", + "remote_read_port": "The port the NibeGW unit is listening for read requests on.", + "remote_write_port": "The port the NibeGW unit is listening for write requests on." + }, "description": "Pick the connection method to your pump. In general, F-series pumps require a Nibe GW custom accessory, while an S-series pump has Modbus support built-in.", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/nibe_heatpump/translations/es.json b/homeassistant/components/nibe_heatpump/translations/es.json index 0619471f538..0365e60adea 100644 --- a/homeassistant/components/nibe_heatpump/translations/es.json +++ b/homeassistant/components/nibe_heatpump/translations/es.json @@ -9,9 +9,37 @@ "model": "El modelo seleccionado no parece ser compatible con modbus40", "read": "Error en la solicitud de lectura de la bomba. Verifica tu `Puerto de lectura remoto` o `Direcci\u00f3n IP remota`.", "unknown": "Error inesperado", + "url": "La URL especificada no es una URL bien formada y compatible", "write": "Error en la solicitud de escritura a la bomba. Verifica tu `Puerto de escritura remoto` o `Direcci\u00f3n IP remota`." }, "step": { + "modbus": { + "data": { + "modbus_unit": "Identificador de unidad Modbus", + "modbus_url": "URL Modbus", + "model": "Modelo de bomba de calor" + }, + "data_description": { + "modbus_unit": "Identificaci\u00f3n de la unidad para su bomba de calor. Por lo general, se puede dejar en 0.", + "modbus_url": "URL Modbus que describe la conexi\u00f3n a tu bomba de calor o unidad MODBUS40. Debe estar en el formato:\n - `tcp://[HOST]:[PUERTO]` para conexi\u00f3n Modbus TCP\n - `serial://[DISPOSITIVO LOCAL]` para una conexi\u00f3n Modbus RTU local\n - `rfc2217://[HOST]:[PUERTO]` para una conexi\u00f3n remota Modbus RTU basada en telnet." + } + }, + "nibegw": { + "data": { + "ip_address": "Direcci\u00f3n remota", + "listening_port": "Puerto de escucha local", + "model": "Modelo de bomba de calor", + "remote_read_port": "Puerto de lectura remoto", + "remote_write_port": "Puerto de escritura remoto" + }, + "data_description": { + "ip_address": "La direcci\u00f3n de la unidad NibeGW. El dispositivo deber\u00eda haber sido configurado con una direcci\u00f3n est\u00e1tica.", + "listening_port": "El puerto local en este sistema, al que la unidad NibeGW est\u00e1 configurada para enviar datos.", + "remote_read_port": "El puerto en el que la unidad NibeGW est\u00e1 escuchando peticiones de lectura.", + "remote_write_port": "El puerto en el que la unidad NibeGW est\u00e1 escuchando peticiones de escritura." + }, + "description": "Antes de intentar configurar la integraci\u00f3n, verifica que:\n - La unidad NibeGW est\u00e1 conectada a una bomba de calor.\n - Se ha habilitado el accesorio MODBUS40 en la configuraci\u00f3n de la bomba de calor.\n - La bomba no ha entrado en estado de alarma por falta del accesorio MODBUS40." + }, "user": { "data": { "ip_address": "Direcci\u00f3n remota", @@ -25,7 +53,11 @@ "remote_read_port": "El puerto en el que la unidad NibeGW est\u00e1 escuchando las peticiones de lectura.", "remote_write_port": "El puerto en el que la unidad NibeGW est\u00e1 escuchando peticiones de escritura." }, - "description": "Antes de intentar configurar la integraci\u00f3n, verifica que:\n- La unidad NibeGW est\u00e1 conectada a una bomba de calor.\n- Se ha habilitado el accesorio MODBUS40 en la configuraci\u00f3n de la bomba de calor.\n- La bomba no ha entrado en estado de alarma por falta del accesorio MODBUS40." + "description": "Elige el m\u00e9todo de conexi\u00f3n a tu bomba. En general, las bombas de la serie F requieren un accesorio personalizado Nibe GW, mientras que una bomba de la serie S tiene soporte Modbus incorporado.", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" + } } } } diff --git a/homeassistant/components/nibe_heatpump/translations/et.json b/homeassistant/components/nibe_heatpump/translations/et.json index 223d0f22c1a..8fff7663bb0 100644 --- a/homeassistant/components/nibe_heatpump/translations/et.json +++ b/homeassistant/components/nibe_heatpump/translations/et.json @@ -9,9 +9,37 @@ "model": "Valitud mudel ei n\u00e4i toetavat modbus40.", "read": "Viga pumba lugemistaotlusel. Kinnitage oma \"Kaugloetav port\" v\u00f5i \"Kaug-IP-aadress\".", "unknown": "Ootamatu t\u00f5rge", + "url": "M\u00e4\u00e4ratud URL ei ole h\u00e4sti vormindatud ja toetatud URL", "write": "Viga pumba kirjutamise taotlusel. Kontrollige oma `kaugkirjutusport` v\u00f5i `kaug-IP-aadress`." }, "step": { + "modbus": { + "data": { + "modbus_unit": "Modbus-i \u00fcksuse identifikaator", + "modbus_url": "Modbus-i URL", + "model": "Soojuspumba mudel" + }, + "data_description": { + "modbus_unit": "Soojuspumba seadme identifitseerimine. Tavaliselt v\u00f5ib j\u00e4tta 0-le.", + "modbus_url": "Modbusi URL mis kirjeldab \u00fchendust soojuspumba v\u00f5i MODBUS40 seadmega. See peaks olema vormis:\n - `tcp://[HOST]:[PORT]` Modbusi TCP-\u00fchenduse jaoks\n - \"serial://[LOCAL DEVICE]\" kohaliku Modbus RTU \u00fchenduse jaoks\n - `rfc2217://[HOST]:[PORT]` telnetip\u00f5hise Modbus RTU kaug\u00fchenduse jaoks." + } + }, + "nibegw": { + "data": { + "ip_address": "Kaug-IP-aadress", + "listening_port": "Kohalik kuulamisport", + "model": "Soojuspumba mudel", + "remote_read_port": "Kauglugemise port", + "remote_write_port": "Kaugkirjutusport" + }, + "data_description": { + "ip_address": "NibeGW-\u00fcksuse aadress. Seade peaks olema seadistatud staatilise aadressiga.", + "listening_port": "Selle s\u00fcsteemi kohalik port kuhu NibeGW seade on seadistatud andmeid saatma.", + "remote_read_port": "Port, mille kaudu NibeGW-\u00fcksus loeb lugemisp\u00e4ringuid.", + "remote_write_port": "Port, mille kaudu NibeGW-\u00fcksus kuulab kirjutamisp\u00e4ringuid." + }, + "description": "Enne seadistamist veendu, et:\n - NibeGW seade on \u00fchendatud soojuspumbaga.\n - MODBUS40 lisaseade on soojuspumba konfiguratsioonis lubatud.\n - Pump ei ole MODBUS40 lisaseadme puudumise t\u00f5ttu h\u00e4ireolekusse l\u00e4inud." + }, "user": { "data": { "ip_address": "Kaug-IP-aadress", @@ -25,7 +53,11 @@ "remote_read_port": "Port, mille kaudu NibeGW-\u00fcksus loeb lugemisp\u00e4ringuid.", "remote_write_port": "Port, mille kaudu NibeGW-\u00fcksus kuulab kirjutamisp\u00e4ringuid." }, - "description": "Enne sidumise seadistamist veendu, et:\n - NibeGW seade on \u00fchendatud soojuspumbaga.\n - MODBUS40 tarvik on soojuspumba konfiguratsioonis lubatud.\n - Pump ei ole MODBUS40 lisaseadme puudumise t\u00f5ttu h\u00e4ireolekusse l\u00e4inud." + "description": "Vali pumbaga \u00fchendamise viis. \u00dcldiselt vajavad F-seeria pumbad Nibe GW kohandatud tarvikut, S-seeria pumbal on aga sisseehitatud Modbusi tugi.", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" + } } } } diff --git a/homeassistant/components/nibe_heatpump/translations/hu.json b/homeassistant/components/nibe_heatpump/translations/hu.json index 1dc8ea12179..35d9ac53291 100644 --- a/homeassistant/components/nibe_heatpump/translations/hu.json +++ b/homeassistant/components/nibe_heatpump/translations/hu.json @@ -9,9 +9,37 @@ "model": "\u00dagy t\u0171nik, hogy a kiv\u00e1lasztott modell nem t\u00e1mogatja a modbus40-et", "read": "Hiba a szivatty\u00fa olvas\u00e1si k\u00e9r\u00e9s\u00e9n\u00e9l. Ellen\u0151rizze a \"T\u00e1voli olvas\u00e1si portot\" vagy a \"T\u00e1voli IP-c\u00edmet\".", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt", + "url": "A megadott URL-c\u00edm nem j\u00f3l form\u00e1zott \u00e9s t\u00e1mogatott URL-c\u00edm", "write": "Hiba a h\u0151szivatty\u00fa \u00edr\u00e1si k\u00e9relm\u00e9ben. Ellen\u0151rizze a portot, c\u00edmet." }, "step": { + "modbus": { + "data": { + "modbus_unit": "Modbus egys\u00e9g azonos\u00edt\u00f3ja", + "modbus_url": "Modbus URL", + "model": "A h\u0151szivatty\u00fa modellje" + }, + "data_description": { + "modbus_unit": "A h\u0151szivatty\u00fa egys\u00e9gazonos\u00edt\u00e1sa. \u00c1ltal\u00e1ban 0 \u00e9rt\u00e9ken hagyhat\u00f3.", + "modbus_url": "Modbus URL, amely le\u00edrja a h\u0151szivatty\u00faval vagy a MODBUS40 egys\u00e9ggel val\u00f3 kapcsolatot. Az \u0171rlapon kell lennie:\n - 'tcp://[HOST]:[PORT]' a Modbus TCP kapcsolathoz\n - \"serial://[HELYI ESZK\u00d6Z]\" helyi Modbus RTU kapcsolathoz\n - 'rfc2217://[HOST]:[PORT]' t\u00e1voli telnet alap\u00fa Modbus RTU kapcsolathoz." + } + }, + "nibegw": { + "data": { + "ip_address": "T\u00e1voli IP-c\u00edm", + "listening_port": "Helyi port", + "model": "A h\u0151szivatty\u00fa modellje", + "remote_read_port": "T\u00e1voli olvas\u00e1si port", + "remote_write_port": "T\u00e1voli \u00edr\u00e1si port" + }, + "data_description": { + "ip_address": "A NibeGW egys\u00e9g c\u00edme. A k\u00e9sz\u00fcl\u00e9ket statikus c\u00edmmel kell konfigur\u00e1lni.", + "listening_port": "A rendszer helyi portja, amelyre a NibeGW egys\u00e9g \u00fagy van konfigur\u00e1lva, hogy adatokat k\u00fcldj\u00f6n.", + "remote_read_port": "A port, amelyen a NibeGW egys\u00e9g olvas\u00e1si k\u00e9r\u00e9seket v\u00e1r.", + "remote_write_port": "A port, amelyen a NibeGW egys\u00e9g az \u00edr\u00e1si k\u00e9r\u00e9seket v\u00e1rja." + }, + "description": "Miel\u0151tt megpr\u00f3b\u00e1ln\u00e1 konfigur\u00e1lni az integr\u00e1ci\u00f3t, ellen\u0151rizze, hogy:\n - A NibeGW egys\u00e9g h\u0151szivatty\u00fahoz van csatlakoztatva.\n - A MODBUS40 kieg\u00e9sz\u00edt\u0151 enged\u00e9lyezve van a h\u0151szivatty\u00fa konfigur\u00e1ci\u00f3j\u00e1ban.\n - A szivatty\u00fa nem l\u00e9pett riaszt\u00e1si \u00e1llapotba a MODBUS40 tartoz\u00e9k hi\u00e1nya miatt." + }, "user": { "data": { "ip_address": "T\u00e1voli IP-c\u00edm", @@ -25,7 +53,11 @@ "remote_read_port": "A port, amelyen a NibeGW egys\u00e9g olvas\u00e1si k\u00e9r\u00e9seket fogad.", "remote_write_port": "A port, amelyen a NibeGW egys\u00e9g \u00edr\u00e1si k\u00e9r\u00e9seket fogad." }, - "description": "Miel\u0151tt megpr\u00f3b\u00e1ln\u00e1 konfigur\u00e1lni az integr\u00e1ci\u00f3t, ellen\u0151rizze, hogy:\n - A NibeGW egys\u00e9g h\u0151szivatty\u00fahoz van csatlakoztatva.\n - A MODBUS40 kieg\u00e9sz\u00edt\u0151 enged\u00e9lyezve van a h\u0151szivatty\u00fa konfigur\u00e1ci\u00f3j\u00e1ban.\n - A szivatty\u00fa nem l\u00e9pett riaszt\u00e1si \u00e1llapotba a MODBUS40 tartoz\u00e9k hi\u00e1nya miatt." + "description": "V\u00e1lassza ki a szivatty\u00fahoz val\u00f3 csatlakoz\u00e1si m\u00f3dot. Az F-sorozat\u00fa szivatty\u00fakhoz \u00e1ltal\u00e1ban Nibe GW egyedi tartoz\u00e9kra van sz\u00fcks\u00e9g, m\u00edg az S-sorozat\u00fa szivatty\u00fak be\u00e9p\u00edtett Modbus-t\u00e1mogat\u00e1ssal rendelkeznek.", + "menu_options": { + "modbus": "ModBUS", + "nibegw": "NibeGW" + } } } } diff --git a/homeassistant/components/nibe_heatpump/translations/it.json b/homeassistant/components/nibe_heatpump/translations/it.json index 4884c5c3efc..81da8c2503d 100644 --- a/homeassistant/components/nibe_heatpump/translations/it.json +++ b/homeassistant/components/nibe_heatpump/translations/it.json @@ -9,9 +9,37 @@ "model": "Il modello selezionato non sembra supportare il modbus40", "read": "Errore su richiesta di lettura dalla pompa. Verifica la tua \"Porta di lettura remota\" o \"Indirizzo IP remoto\".", "unknown": "Errore imprevisto", + "url": "L'URL specificato non \u00e8 un URL ben formato e supportato", "write": "Errore nella richiesta di scrittura alla pompa. Verifica la tua \"Porta di scrittura remota\" o \"Indirizzo IP remoto\"." }, "step": { + "modbus": { + "data": { + "modbus_unit": "Identificatore unit\u00e0 Modbus", + "modbus_url": "Modbus URL", + "model": "Modello di pompa di calore" + }, + "data_description": { + "modbus_unit": "Identificazione dell'unit\u00e0 per la tua pompa di calore. Di solito pu\u00f2 essere lasciato a 0.", + "modbus_url": "Modbus URL che descrive la connessione alla pompa di calore o all'unit\u00e0 MODBUS40. Dovrebbe essere nella forma:\n - `tcp://[HOST]:[PORTA]` per la connessione Modbus TCP\n - `serial://[DISPOSITIVO LOCALE]` per una connessione Modbus RTU locale\n - `rfc2217://[HOST]:[PORTA]` per una connessione Modbus RTU remota basata su telnet." + } + }, + "nibegw": { + "data": { + "ip_address": "Indirizzo remoto", + "listening_port": "Porta di ascolto locale", + "model": "Modello di pompa di calore", + "remote_read_port": "Porta di lettura remota", + "remote_write_port": "Porta di scrittura remota" + }, + "data_description": { + "ip_address": "L'indirizzo dell'unit\u00e0 NibeGW. Il dispositivo dovrebbe essere stato configurato con un indirizzo statico.", + "listening_port": "La porta locale su questo sistema a cui l'unit\u00e0 NibeGW \u00e8 configurata per inviare i dati.", + "remote_read_port": "La porta su cui l'unit\u00e0 NibeGW \u00e8 in ascolto per le richieste di lettura.", + "remote_write_port": "La porta su cui l'unit\u00e0 NibeGW \u00e8 in ascolto per le richieste di scrittura." + }, + "description": "Prima di tentare di configurare l'integrazione, verificare che:\n - L'unit\u00e0 NibeGW \u00e8 collegata a una pompa di calore.\n - Nella configurazione della pompa di calore \u00e8 stato abilitato l'accessorio MODBUS40.\n - La pompa non \u00e8 andata in stato di allarme per la mancanza dell'accessorio MODBUS40." + }, "user": { "data": { "ip_address": "Indirizzo remoto", @@ -25,7 +53,11 @@ "remote_read_port": "La porta su cui l'unit\u00e0 NibeGW \u00e8 in ascolto per le richieste di lettura.", "remote_write_port": "La porta su cui l'unit\u00e0 NibeGW \u00e8 in ascolto per le richieste di scrittura." }, - "description": "Prima di tentare di configurare l'integrazione, verificare che:\n - L'unit\u00e0 NibeGW \u00e8 collegata a una pompa di calore.\n - Nella configurazione della pompa di calore \u00e8 stato abilitato l'accessorio MODBUS40.\n - La pompa non \u00e8 andata in stato di allarme per la mancanza dell'accessorio MODBUS40." + "description": "Scegli il metodo di connessione alla tua pompa. In generale, le pompe della serie F richiedono un accessorio personalizzato Nibe GW, mentre le pompe della serie S hanno il supporto Modbus integrato.", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" + } } } } diff --git a/homeassistant/components/nibe_heatpump/translations/pl.json b/homeassistant/components/nibe_heatpump/translations/pl.json index 8298b41c64c..a6cc0785e97 100644 --- a/homeassistant/components/nibe_heatpump/translations/pl.json +++ b/homeassistant/components/nibe_heatpump/translations/pl.json @@ -9,9 +9,37 @@ "model": "Wybrany model nie obs\u0142uguje modbus40", "read": "B\u0142\u0105d przy \u017c\u0105daniu odczytu z pompy. Sprawd\u017a \u201eZdalny port odczytu\u201d lub \u201eZdalny adres IP\u201d.", "unknown": "Nieoczekiwany b\u0142\u0105d", + "url": "Podany adres URL nie jest poprawnie sformu\u0142owanym i obs\u0142ugiwanym adresem URL", "write": "B\u0142\u0105d przy \u017c\u0105daniu zapisu do pompy. Sprawd\u017a \u201eZdalny port zapisu\u201d lub \u201eZdalny adres IP\u201d." }, "step": { + "modbus": { + "data": { + "modbus_unit": "Identyfikator jednostki Modbus", + "modbus_url": "Adres URL Modbus", + "model": "Model pompy ciep\u0142a" + }, + "data_description": { + "modbus_unit": "Identyfikacja jednostki pompa ciep\u0142a. Zwykle mo\u017cna pozostawi\u0107 0.", + "modbus_url": "Adres URL Modbus opisuj\u0105cy po\u0142\u0105czenie z pomp\u0105 ciep\u0142a lub jednostk\u0105 MODBUS40. Powinien by\u0107 w formacie:\n- `tcp://[HOST]:[PORT]` dla po\u0142\u0105czenia Modbus TCP\n- `serial://[LOCAL DEVICE]` dla lokalnego po\u0142\u0105czenia Modbus RTU\n- `rfc2217://[HOST]:[PORT]` dla zdalnego po\u0142\u0105czenia Modbus RTU opartego na telnet." + } + }, + "nibegw": { + "data": { + "ip_address": "Zdalny adres IP", + "listening_port": "Lokalny port nas\u0142uchiwania", + "model": "Model pompy ciep\u0142a", + "remote_read_port": "Zdalny port odczytu", + "remote_write_port": "Zdalny port zapisu" + }, + "data_description": { + "ip_address": "Adres urz\u0105dzenia NibeGW. Urz\u0105dzenie powinno by\u0107 skonfigurowane z adresem statycznym.", + "listening_port": "Port lokalny w tym systemie, do kt\u00f3rego urz\u0105dzenie NibeGW jest skonfigurowane do wysy\u0142ania danych.", + "remote_read_port": "Port, na kt\u00f3rym urz\u0105dzenie NibeGW nas\u0142uchuje \u017c\u0105da\u0144 odczytu.", + "remote_write_port": "Port, na kt\u00f3rym urz\u0105dzenie NibeGW nas\u0142uchuje \u017c\u0105da\u0144 zapisu." + }, + "description": "Przed przyst\u0105pieniem do konfiguracji integracji sprawd\u017a, czy:\n - Urz\u0105dzenie NibeGW jest pod\u0142\u0105czona do pompy ciep\u0142a.\n - Akcesorium MODBUS40 zosta\u0142o w\u0142\u0105czone w konfiguracji pompy ciep\u0142a.\n - Pompa nie wesz\u0142a w stan alarmowy z powodu braku akcesorium MODBUS40." + }, "user": { "data": { "ip_address": "Zdalny adres IP", @@ -25,7 +53,11 @@ "remote_read_port": "Port, na kt\u00f3rym urz\u0105dzenie NibeGW nas\u0142uchuje \u017c\u0105da\u0144 odczytu.", "remote_write_port": "Port, na kt\u00f3rym urz\u0105dzenie NibeGW nas\u0142uchuje \u017c\u0105da\u0144 zapisu." }, - "description": "Przed przyst\u0105pieniem do konfiguracji integracji sprawd\u017a, czy:\n - Urz\u0105dzenie NibeGW jest pod\u0142\u0105czona do pompy ciep\u0142a.\n - Akcesorium MODBUS40 zosta\u0142o w\u0142\u0105czone w konfiguracji pompy ciep\u0142a.\n - Pompa nie wesz\u0142a w stan alarmowy z powodu braku akcesorium MODBUS40." + "description": "Wybierz metod\u0119 po\u0142\u0105czenia z pomp\u0105. Og\u00f3lnie rzecz bior\u0105c, pompy serii F wymagaj\u0105 niestandardowego akcesorium Nibe GW, podczas gdy pompy serii S maj\u0105 wbudowan\u0105 obs\u0142ug\u0119 protoko\u0142u Modbus.", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" + } } } } diff --git a/homeassistant/components/nibe_heatpump/translations/pt-BR.json b/homeassistant/components/nibe_heatpump/translations/pt-BR.json index 9f999846036..1ae021ee11b 100644 --- a/homeassistant/components/nibe_heatpump/translations/pt-BR.json +++ b/homeassistant/components/nibe_heatpump/translations/pt-BR.json @@ -9,9 +9,37 @@ "model": "O modelo selecionado parece n\u00e3o suportar modbus40", "read": "Erro na solicita\u00e7\u00e3o de leitura da bomba. Verifique sua `Porta de leitura remota` ou `Endere\u00e7o IP remoto`.", "unknown": "Erro inesperado", + "url": "A URL especificada n\u00e3o \u00e9 uma URL bem formada e suportada", "write": "Erro na solicita\u00e7\u00e3o de grava\u00e7\u00e3o para bombear. Verifique sua `Porta de grava\u00e7\u00e3o remota` ou `Endere\u00e7o IP remoto`." }, "step": { + "modbus": { + "data": { + "modbus_unit": "Identificador da Unidade Modbus", + "modbus_url": "URL de Modbus", + "model": "Modelo de Bomba de Calor" + }, + "data_description": { + "modbus_unit": "Identifica\u00e7\u00e3o da unidade para a sua Bomba de Calor. Geralmente pode ser deixado em 0.", + "modbus_url": "URL de Modbus que descreve a liga\u00e7\u00e3o \u00e0 sua Bomba de Calor ou unidade MODBUS40. Deve estar no formul\u00e1rio:\n - `tcp://[HOST]:[PORT]` para conex\u00e3o Modbus TCP\n - `serial://[LOCAL DEVICE]` para uma conex\u00e3o Modbus RTU local\n - `rfc2217://[HOST]:[PORT]` para uma conex\u00e3o Modbus RTU baseada em telnet remoto." + } + }, + "nibegw": { + "data": { + "ip_address": "Endere\u00e7o remoto", + "listening_port": "Porta de escuta local", + "model": "Modelo de Bomba de Calor", + "remote_read_port": "Porta de leitura remota", + "remote_write_port": "Porta de grava\u00e7\u00e3o remota" + }, + "data_description": { + "ip_address": "O endere\u00e7o da unidade NibeGW. O dispositivo deve ter sido configurado com um endere\u00e7o est\u00e1tico.", + "listening_port": "A porta local neste sistema para a qual a unidade NibeGW est\u00e1 configurada para enviar dados.", + "remote_read_port": "A porta na qual a unidade NibeGW est\u00e1 escutando solicita\u00e7\u00f5es de leitura.", + "remote_write_port": "A porta na qual a unidade NibeGW est\u00e1 escutando solicita\u00e7\u00f5es de grava\u00e7\u00e3o." + }, + "description": "Antes de tentar configurar a integra\u00e7\u00e3o, verifique se:\n - A unidade NibeGW est\u00e1 conectada a uma bomba de calor.\n - O acess\u00f3rio MODBUS40 foi habilitado na configura\u00e7\u00e3o da bomba de calor.\n - A bomba n\u00e3o entrou em estado de alarme por falta de acess\u00f3rio MODBUS40." + }, "user": { "data": { "ip_address": "Endere\u00e7o IP remoto", @@ -25,7 +53,11 @@ "remote_read_port": "A porta na qual a unidade NibeGW est\u00e1 escutando solicita\u00e7\u00f5es de leitura.", "remote_write_port": "A porta na qual a unidade NibeGW est\u00e1 escutando solicita\u00e7\u00f5es de grava\u00e7\u00e3o." }, - "description": "Antes de tentar configurar a integra\u00e7\u00e3o, verifique se:\n - A unidade NibeGW est\u00e1 conectada a uma bomba de calor.\n - O acess\u00f3rio MODBUS40 foi habilitado na configura\u00e7\u00e3o da bomba de calor.\n - A bomba n\u00e3o entrou em estado de alarme por falta de acess\u00f3rio MODBUS40." + "description": "Escolha o m\u00e9todo de conex\u00e3o para sua bomba. Em geral, as bombas da s\u00e9rie F requerem um acess\u00f3rio personalizado Nibe GW, enquanto uma bomba da s\u00e9rie S possui suporte Modbus integrado.", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" + } } } } diff --git a/homeassistant/components/nibe_heatpump/translations/ru.json b/homeassistant/components/nibe_heatpump/translations/ru.json index 59f228b3746..42c7a853c94 100644 --- a/homeassistant/components/nibe_heatpump/translations/ru.json +++ b/homeassistant/components/nibe_heatpump/translations/ru.json @@ -9,9 +9,37 @@ "model": "\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 modbus40.", "read": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b `\u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0447\u0442\u0435\u043d\u0438\u044f` \u0438\u043b\u0438 `\u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441`.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430.", + "url": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0439 URL-\u0430\u0434\u0440\u0435\u0441.", "write": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b `\u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0437\u0430\u043f\u0438\u0441\u0438` \u0438\u043b\u0438 `\u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441`." }, "step": { + "modbus": { + "data": { + "modbus_unit": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043c\u043e\u0434\u0443\u043b\u044f Modbus", + "modbus_url": "URL-\u0430\u0434\u0440\u0435\u0441 Modbus", + "model": "\u041c\u043e\u0434\u0435\u043b\u044c \u0442\u0435\u043f\u043b\u043e\u0432\u043e\u0433\u043e \u043d\u0430\u0441\u043e\u0441\u0430" + }, + "data_description": { + "modbus_unit": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0412\u0430\u0448\u0435\u0433\u043e \u0442\u0435\u043f\u043b\u043e\u0432\u043e\u0433\u043e \u043d\u0430\u0441\u043e\u0441\u0430. \u041e\u0431\u044b\u0447\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043d\u0430 0.", + "modbus_url": "URL-\u0430\u0434\u0440\u0435\u0441 Modbus, \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0412\u0430\u0448\u0435\u043c\u0443 \u0442\u0435\u043f\u043b\u043e\u0432\u043e\u043c\u0443 \u043d\u0430\u0441\u043e\u0441\u0443 \u0438\u043b\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 MODBUS40. \u041e\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435:\n- `tcp://[HOST]:[PORT]` \u0434\u043b\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f Modbus TCP\n- `serial://[LOCAL DEVICE]` \u0434\u043b\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f Modbus RTU\n- `rfc2217://[HOST]:[PORT]` \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f Modbus RTU \u0447\u0435\u0440\u0435\u0437 telnet." + } + }, + "nibegw": { + "data": { + "ip_address": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441", + "listening_port": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f", + "model": "\u041c\u043e\u0434\u0435\u043b\u044c \u0442\u0435\u043f\u043b\u043e\u0432\u043e\u0433\u043e \u043d\u0430\u0441\u043e\u0441\u0430", + "remote_read_port": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0447\u0442\u0435\u043d\u0438\u044f", + "remote_write_port": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0437\u0430\u043f\u0438\u0441\u0438" + }, + "data_description": { + "ip_address": "\u0410\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 NibeGW. \u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u0441\u043e \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0430\u0434\u0440\u0435\u0441\u043e\u043c.", + "listening_port": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.", + "remote_read_port": "\u041f\u043e\u0440\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435.", + "remote_write_port": "\u041f\u043e\u0440\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c." + }, + "description": "\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e:\n - \u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e \u043a \u0442\u0435\u043f\u043b\u043e\u0432\u043e\u043c\u0443 \u043d\u0430\u0441\u043e\u0441\u0443.\n - \u0410\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440 MODBUS40 \u0431\u044b\u043b \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0442\u0435\u043f\u043b\u043e\u0432\u043e\u0433\u043e \u043d\u0430\u0441\u043e\u0441\u0430.\n - \u041d\u0430\u0441\u043e\u0441 \u043d\u0435 \u043f\u0435\u0440\u0435\u0448\u0435\u043b \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0442\u0440\u0435\u0432\u043e\u0433\u0438 \u0438\u0437 \u0437\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044f \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u0430 MODBUS40." + }, "user": { "data": { "ip_address": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441", @@ -25,7 +53,11 @@ "remote_read_port": "\u041f\u043e\u0440\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435.", "remote_write_port": "\u041f\u043e\u0440\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c." }, - "description": "\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e:\n - \u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e \u043a \u0442\u0435\u043f\u043b\u043e\u0432\u043e\u043c\u0443 \u043d\u0430\u0441\u043e\u0441\u0443.\n - \u0410\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440 MODBUS40 \u0431\u044b\u043b \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0442\u0435\u043f\u043b\u043e\u0432\u043e\u0433\u043e \u043d\u0430\u0441\u043e\u0441\u0430.\n - \u041d\u0430\u0441\u043e\u0441 \u043d\u0435 \u043f\u0435\u0440\u0435\u0448\u0435\u043b \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0442\u0440\u0435\u0432\u043e\u0433\u0438 \u0438\u0437 \u0437\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044f \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u0430 MODBUS40." + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0412\u0430\u0448\u0435\u043c\u0443 \u043d\u0430\u0441\u043e\u0441\u0443. \u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0434\u043b\u044f \u043d\u0430\u0441\u043e\u0441\u043e\u0432 \u0441\u0435\u0440\u0438\u0438 F \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440 Nibe GW, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u043d\u0430\u0441\u043e\u0441\u044b \u0441\u0435\u0440\u0438\u0438 S \u0438\u043c\u0435\u044e\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 Modbus.", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" + } } } } diff --git a/homeassistant/components/nuheat/translations/bg.json b/homeassistant/components/nuheat/translations/bg.json index 03ace4428b1..47f6792c3b0 100644 --- a/homeassistant/components/nuheat/translations/bg.json +++ b/homeassistant/components/nuheat/translations/bg.json @@ -1,6 +1,7 @@ { "config": { "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" } diff --git a/homeassistant/components/nuki/translations/bg.json b/homeassistant/components/nuki/translations/bg.json index 1a6aff3fe4c..fcb95a304ab 100644 --- a/homeassistant/components/nuki/translations/bg.json +++ b/homeassistant/components/nuki/translations/bg.json @@ -4,6 +4,7 @@ "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" }, "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" }, "step": { diff --git a/homeassistant/components/onvif/translations/bg.json b/homeassistant/components/onvif/translations/bg.json index ba0e0dd6277..0fc81f8514f 100644 --- a/homeassistant/components/onvif/translations/bg.json +++ b/homeassistant/components/onvif/translations/bg.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, "step": { "configure": { "data": { diff --git a/homeassistant/components/openuv/translations/es.json b/homeassistant/components/openuv/translations/es.json index 66331b8e5a5..363ccc82ee0 100644 --- a/homeassistant/components/openuv/translations/es.json +++ b/homeassistant/components/openuv/translations/es.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "La ubicaci\u00f3n ya est\u00e1 configurada" + "already_configured": "La ubicaci\u00f3n ya est\u00e1 configurada", + "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente" }, "error": { "invalid_api_key": "Clave API no v\u00e1lida" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "Clave API" + }, + "description": "Por favor, vuelve a introducir la clave API para {latitude}, {longitude}.", + "title": "Volver a autenticar la integraci\u00f3n" + }, "user": { "data": { "api_key": "Clave API", diff --git a/homeassistant/components/openuv/translations/et.json b/homeassistant/components/openuv/translations/et.json index 76145f40ed0..4f6fca0ec01 100644 --- a/homeassistant/components/openuv/translations/et.json +++ b/homeassistant/components/openuv/translations/et.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "Asukoht on juba m\u00e4\u00e4ratud" + "already_configured": "Asukoht on juba m\u00e4\u00e4ratud", + "reauth_successful": "Taastuvastamine \u00f5nnestus" }, "error": { "invalid_api_key": "Vigane API v\u00f5ti" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API v\u00f5ti" + }, + "description": "Sisesta uuesti API v\u00f5ti {latitude} , {longitude} jaoks.", + "title": "Taastuvasta sidumine" + }, "user": { "data": { "api_key": "API v\u00f5ti", diff --git a/homeassistant/components/openuv/translations/hu.json b/homeassistant/components/openuv/translations/hu.json index f6493247a99..6e9a7cc4c1f 100644 --- a/homeassistant/components/openuv/translations/hu.json +++ b/homeassistant/components/openuv/translations/hu.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "A hely m\u00e1r konfigur\u00e1lva van" + "already_configured": "A hely m\u00e1r konfigur\u00e1lva van", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." }, "error": { "invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API kulcs" + }, + "description": "K\u00e9rj\u00fck, adja meg \u00fajra az API-kulcsot a k\u00f6vetkez\u0151h\u00f6z: {latitude}, {longitude}.", + "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" + }, "user": { "data": { "api_key": "API kulcs", diff --git a/homeassistant/components/openuv/translations/it.json b/homeassistant/components/openuv/translations/it.json index 4e51b09aec2..f3f4f290017 100644 --- a/homeassistant/components/openuv/translations/it.json +++ b/homeassistant/components/openuv/translations/it.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "La posizione \u00e8 gi\u00e0 configurata" + "already_configured": "La posizione \u00e8 gi\u00e0 configurata", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" }, "error": { "invalid_api_key": "Chiave API non valida" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "Chiave API" + }, + "description": "Inserisci nuovamente la chiave API per {latitude}, {longitude}.", + "title": "Autentica nuovamente l'integrazione" + }, "user": { "data": { "api_key": "Chiave API", diff --git a/homeassistant/components/openuv/translations/pt-BR.json b/homeassistant/components/openuv/translations/pt-BR.json index 9d0c6dd7e8a..c98e76680e7 100644 --- a/homeassistant/components/openuv/translations/pt-BR.json +++ b/homeassistant/components/openuv/translations/pt-BR.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "Localiza\u00e7\u00e3o j\u00e1 est\u00e1 configurada" + "already_configured": "Localiza\u00e7\u00e3o j\u00e1 est\u00e1 configurada", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" }, "error": { "invalid_api_key": "Chave de API inv\u00e1lida" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "Chave API" + }, + "description": "Insira novamente a chave de API para {latitude} , {longitude} .", + "title": "Reautenticar Integra\u00e7\u00e3o" + }, "user": { "data": { "api_key": "Chave da API", diff --git a/homeassistant/components/openuv/translations/ru.json b/homeassistant/components/openuv/translations/ru.json index ce7da6af147..179707cf96a 100644 --- a/homeassistant/components/openuv/translations/ru.json +++ b/homeassistant/components/openuv/translations/ru.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430." + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." }, "error": { "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API." }, "step": { + "reauth_confirm": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API" + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043b\u044e\u0447 API \u0434\u043b\u044f {latitude}, {longitude}.", + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + }, "user": { "data": { "api_key": "\u041a\u043b\u044e\u0447 API", diff --git a/homeassistant/components/panasonic_viera/translations/bg.json b/homeassistant/components/panasonic_viera/translations/bg.json index 2ceff63752c..c8dab4b29d9 100644 --- a/homeassistant/components/panasonic_viera/translations/bg.json +++ b/homeassistant/components/panasonic_viera/translations/bg.json @@ -6,6 +6,7 @@ "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_pin_code": "\u0412\u044a\u0432\u0435\u0434\u0435\u043d\u0438\u044f\u0442 \u043e\u0442 \u0412\u0430\u0441 \u041f\u0418\u041d \u043a\u043e\u0434 \u0435 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d" }, "step": { diff --git a/homeassistant/components/powerwall/translations/bg.json b/homeassistant/components/powerwall/translations/bg.json index f0092b14bc1..5660ffe8a10 100644 --- a/homeassistant/components/powerwall/translations/bg.json +++ b/homeassistant/components/powerwall/translations/bg.json @@ -3,6 +3,9 @@ "abort": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, "flow_title": "{name} ({ip_address})", "step": { "confirm_discovery": { diff --git a/homeassistant/components/rachio/translations/bg.json b/homeassistant/components/rachio/translations/bg.json index fdbdc5b1cdf..969022c860a 100644 --- a/homeassistant/components/rachio/translations/bg.json +++ b/homeassistant/components/rachio/translations/bg.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/rituals_perfume_genie/translations/bg.json b/homeassistant/components/rituals_perfume_genie/translations/bg.json index 7be659cab0b..51b337ce813 100644 --- a/homeassistant/components/rituals_perfume_genie/translations/bg.json +++ b/homeassistant/components/rituals_perfume_genie/translations/bg.json @@ -1,6 +1,7 @@ { "config": { "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { diff --git a/homeassistant/components/somfy_mylink/translations/bg.json b/homeassistant/components/somfy_mylink/translations/bg.json index 5ee98a5d46c..fdc3b2a9b4c 100644 --- a/homeassistant/components/somfy_mylink/translations/bg.json +++ b/homeassistant/components/somfy_mylink/translations/bg.json @@ -11,5 +11,10 @@ } } } + }, + "options": { + "abort": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + } } } \ No newline at end of file diff --git a/homeassistant/components/synology_dsm/translations/bg.json b/homeassistant/components/synology_dsm/translations/bg.json index dcd0a5ab730..d0f8abc4062 100644 --- a/homeassistant/components/synology_dsm/translations/bg.json +++ b/homeassistant/components/synology_dsm/translations/bg.json @@ -6,6 +6,7 @@ "reconfigure_successful": "\u041f\u0440\u0435\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" }, "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "otp_failed": "\u0414\u0432\u0443\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0435 \u0431\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e \u0441 \u043d\u043e\u0432 \u043a\u043e\u0434 \u0437\u0430 \u0434\u043e\u0441\u0442\u044a\u043f", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" diff --git a/homeassistant/components/totalconnect/translations/bg.json b/homeassistant/components/totalconnect/translations/bg.json index e5aed3bb504..70789b8fe09 100644 --- a/homeassistant/components/totalconnect/translations/bg.json +++ b/homeassistant/components/totalconnect/translations/bg.json @@ -4,6 +4,9 @@ "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" }, + "error": { + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + }, "step": { "locations": { "data": { diff --git a/homeassistant/components/unifiprotect/translations/it.json b/homeassistant/components/unifiprotect/translations/it.json index d44b3fccf31..fedbef4bdfc 100644 --- a/homeassistant/components/unifiprotect/translations/it.json +++ b/homeassistant/components/unifiprotect/translations/it.json @@ -43,8 +43,8 @@ }, "issues": { "ea_warning": { - "description": "Stai utilizzando {version} di UniFi Protect. Le versioni di accesso anticipato non sono supportate da Home Assistant e potrebbero causare l'interruzione dell'integrazione di UniFi Protect o non funzionare come previsto.", - "title": "{version} \u00e8 una versione di accesso anticipato di UniFi Protect" + "description": "Si sta utilizzando v{version} di UniFi Protect che \u00e8 una versione in accesso anticipato. Le versioni in accesso anticipato non sono supportate da Home Assistant e potrebbero causare l'interruzione o il mancato funzionamento dell'integrazione di UniFi Protect.", + "title": "UniFi Protect v{version} \u00e8 una versione in accesso anticipato" } }, "options": { diff --git a/homeassistant/components/unifiprotect/translations/pl.json b/homeassistant/components/unifiprotect/translations/pl.json index a0c4e7eb72c..22ccc97ca07 100644 --- a/homeassistant/components/unifiprotect/translations/pl.json +++ b/homeassistant/components/unifiprotect/translations/pl.json @@ -41,6 +41,12 @@ } } }, + "issues": { + "ea_warning": { + "description": "U\u017cywasz UniFi Protect v{version}, kt\u00f3ra jest wersj\u0105 Early Access. Wersje Early Access nie s\u0105 obs\u0142ugiwane przez Home Assistanta i mog\u0105 spowodowa\u0107 uszkodzenie integracji UniFi Protect lub niedzia\u0142anie zgodnie z oczekiwaniami.", + "title": "UniFi Protect v{version} to wersja Early Access" + } + }, "options": { "error": { "invalid_mac_list": "Musi to by\u0107 lista adres\u00f3w MAC oddzielonych przecinkami" diff --git a/homeassistant/components/unifiprotect/translations/ru.json b/homeassistant/components/unifiprotect/translations/ru.json index 2e8e0687f0a..c90b4eec0f4 100644 --- a/homeassistant/components/unifiprotect/translations/ru.json +++ b/homeassistant/components/unifiprotect/translations/ru.json @@ -43,8 +43,8 @@ }, "issues": { "ea_warning": { - "description": "\u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 UniFi Protect {version}. \u0412\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f Home Assistant \u0438 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.", - "title": "UniFi Protect {version} \u2014 \u044d\u0442\u043e \u0432\u0435\u0440\u0441\u0438\u044f \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430" + "description": "\u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 UniFi Protect v{version}. \u0412\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f Home Assistant \u0438 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.", + "title": "UniFi Protect v{version} \u2014 \u044d\u0442\u043e \u0432\u0435\u0440\u0441\u0438\u044f \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430" } }, "options": { diff --git a/homeassistant/components/waze_travel_time/translations/bg.json b/homeassistant/components/waze_travel_time/translations/bg.json index 5b18b5ba021..295b56e5daa 100644 --- a/homeassistant/components/waze_travel_time/translations/bg.json +++ b/homeassistant/components/waze_travel_time/translations/bg.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/zwave_js/translations/bg.json b/homeassistant/components/zwave_js/translations/bg.json index 0ed3ce16d2f..73cd79beb62 100644 --- a/homeassistant/components/zwave_js/translations/bg.json +++ b/homeassistant/components/zwave_js/translations/bg.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "not_zwave_js_addon": "\u041e\u0442\u043a\u0440\u0438\u0442\u0430\u0442\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430 \u043d\u0435 \u0435 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u043d\u0430\u0442\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430 \u043d\u0430 Z-Wave JS." }, "error": { From ad992f0a8674ff53c52700d8ac45cd372be37210 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 9 Nov 2022 01:09:06 -0600 Subject: [PATCH 0311/1033] Fix switchbot not becoming available again after unavailable (#81822) * Fix switchbot not becoming available again after unavailable If the advertisment was the same and we were previously marked as unavailable we would not mark the device as available again until the advertisment changed. For lights there is a counter but for the bots there is no counter which means the bots would show unavailable even though they were available again * naming * naming --- .../components/switchbot/coordinator.py | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/switchbot/coordinator.py b/homeassistant/components/switchbot/coordinator.py index ee93c74af37..f68c1effc0c 100644 --- a/homeassistant/components/switchbot/coordinator.py +++ b/homeassistant/components/switchbot/coordinator.py @@ -61,6 +61,15 @@ class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator): self.base_unique_id = base_unique_id self.model = model self._ready_event = asyncio.Event() + self._was_unavailable = True + + @callback + def _async_handle_unavailable( + self, service_info: bluetooth.BluetoothServiceInfoBleak + ) -> None: + """Handle the device going unavailable.""" + super()._async_handle_unavailable(service_info) + self._was_unavailable = True @callback def _async_handle_bluetooth_event( @@ -70,16 +79,20 @@ class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator): ) -> None: """Handle a Bluetooth event.""" self.ble_device = service_info.device - if adv := switchbot.parse_advertisement_data( - service_info.device, service_info.advertisement + if not ( + adv := switchbot.parse_advertisement_data( + service_info.device, service_info.advertisement + ) ): - if "modelName" in adv.data: - self._ready_event.set() - _LOGGER.debug("%s: Switchbot data: %s", self.ble_device.address, self.data) - if not self.device.advertisement_changed(adv): - return - self.data = flatten_sensors_data(adv.data) - self.device.update_from_advertisement(adv) + return + if "modelName" in adv.data: + self._ready_event.set() + _LOGGER.debug("%s: Switchbot data: %s", self.ble_device.address, self.data) + if not self.device.advertisement_changed(adv) and not self._was_unavailable: + return + self._was_unavailable = False + self.data = flatten_sensors_data(adv.data) + self.device.update_from_advertisement(adv) super()._async_handle_bluetooth_event(service_info, change) async def async_wait_ready(self) -> bool: From 9f691ab3592376863b98ea888fa62a33682d1542 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 9 Nov 2022 09:03:59 +0100 Subject: [PATCH 0312/1033] Revert "Fix coordinator TypeVar definition (#81298)" (#81834) --- homeassistant/helpers/update_coordinator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/helpers/update_coordinator.py b/homeassistant/helpers/update_coordinator.py index d0d1cb90454..768b8040729 100644 --- a/homeassistant/helpers/update_coordinator.py +++ b/homeassistant/helpers/update_coordinator.py @@ -25,7 +25,7 @@ REQUEST_REFRESH_DEFAULT_IMMEDIATE = True _T = TypeVar("_T") _DataUpdateCoordinatorT = TypeVar( - "_DataUpdateCoordinatorT", bound="DataUpdateCoordinator" + "_DataUpdateCoordinatorT", bound="DataUpdateCoordinator[Any]" ) From 059623c6bf04a3f729caff0bedf85231a404e207 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 9 Nov 2022 10:47:10 +0200 Subject: [PATCH 0313/1033] Remove vestigial move.yml (#81557) --- .github/move.yml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 .github/move.yml diff --git a/.github/move.yml b/.github/move.yml deleted file mode 100644 index e041083c9ae..00000000000 --- a/.github/move.yml +++ /dev/null @@ -1,13 +0,0 @@ -# Configuration for move-issues - https://github.com/dessant/move-issues - -# Delete the command comment. Ignored when the comment also contains other content -deleteCommand: true -# Close the source issue after moving -closeSourceIssue: true -# Lock the source issue after moving -lockSourceIssue: false -# Set custom aliases for targets -# aliases: -# r: repo -# or: owner/repo - From 44e4b8c67693bb467ddf04d72567c216baf12bba Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 9 Nov 2022 11:48:37 +0200 Subject: [PATCH 0314/1033] Omit native_unit_of_measurement=None (#81844) --- homeassistant/components/bthome/sensor.py | 1 - homeassistant/components/ecobee/sensor.py | 1 - homeassistant/components/growatt_server/sensor_types/mix.py | 1 - homeassistant/components/metoffice/sensor.py | 4 ---- homeassistant/components/ondilo_ico/sensor.py | 1 - 5 files changed, 8 deletions(-) diff --git a/homeassistant/components/bthome/sensor.py b/homeassistant/components/bthome/sensor.py index 9d68ce2d3b4..188bd659c6d 100644 --- a/homeassistant/components/bthome/sensor.py +++ b/homeassistant/components/bthome/sensor.py @@ -174,7 +174,6 @@ SENSOR_DESCRIPTIONS = { (BTHomeSensorDeviceClass.COUNT, None): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.COUNT}", device_class=None, - native_unit_of_measurement=None, state_class=SensorStateClass.MEASUREMENT, ), } diff --git a/homeassistant/components/ecobee/sensor.py b/homeassistant/components/ecobee/sensor.py index 9d8793efc29..30949e36f8e 100644 --- a/homeassistant/components/ecobee/sensor.py +++ b/homeassistant/components/ecobee/sensor.py @@ -76,7 +76,6 @@ SENSOR_TYPES: tuple[EcobeeSensorEntityDescription, ...] = ( key="airQuality", name="Air Quality Index", device_class=SensorDeviceClass.AQI, - native_unit_of_measurement=None, state_class=SensorStateClass.MEASUREMENT, runtime_key="actualAQScore", ), diff --git a/homeassistant/components/growatt_server/sensor_types/mix.py b/homeassistant/components/growatt_server/sensor_types/mix.py index 6cb61ea2e08..ce29760b317 100644 --- a/homeassistant/components/growatt_server/sensor_types/mix.py +++ b/homeassistant/components/growatt_server/sensor_types/mix.py @@ -230,7 +230,6 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="mix_last_update", name="Last Data Update", api_key="lastdataupdate", - native_unit_of_measurement=None, device_class=SensorDeviceClass.TIMESTAMP, ), # Values from 'dashboard_data' API call diff --git a/homeassistant/components/metoffice/sensor.py b/homeassistant/components/metoffice/sensor.py index ef9643be96a..3495f7b7c7a 100644 --- a/homeassistant/components/metoffice/sensor.py +++ b/homeassistant/components/metoffice/sensor.py @@ -51,7 +51,6 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="name", name="Station name", device_class=None, - native_unit_of_measurement=None, icon="mdi:label-outline", entity_registry_enabled_default=False, ), @@ -59,7 +58,6 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="weather", name="Weather", device_class=None, - native_unit_of_measurement=None, icon="mdi:weather-sunny", # but will adapt to current conditions entity_registry_enabled_default=True, ), @@ -91,7 +89,6 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="wind_direction", name="Wind direction", - native_unit_of_measurement=None, icon="mdi:compass-outline", entity_registry_enabled_default=False, ), @@ -108,7 +105,6 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="visibility", name="Visibility", device_class=None, - native_unit_of_measurement=None, icon="mdi:eye", entity_registry_enabled_default=False, ), diff --git a/homeassistant/components/ondilo_ico/sensor.py b/homeassistant/components/ondilo_ico/sensor.py index 85a728ee04a..b4c6e02879e 100644 --- a/homeassistant/components/ondilo_ico/sensor.py +++ b/homeassistant/components/ondilo_ico/sensor.py @@ -50,7 +50,6 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="ph", name="pH", - native_unit_of_measurement=None, icon="mdi:pool", device_class=None, state_class=SensorStateClass.MEASUREMENT, From 21d96e00a244c26b4fa378e9117785566a4f07cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Korb?= Date: Wed, 9 Nov 2022 10:51:28 +0100 Subject: [PATCH 0315/1033] Use better icon for system monitor IP sensor (#81779) --- homeassistant/components/systemmonitor/sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/systemmonitor/sensor.py b/homeassistant/components/systemmonitor/sensor.py index eb889264151..d16e2ac3190 100644 --- a/homeassistant/components/systemmonitor/sensor.py +++ b/homeassistant/components/systemmonitor/sensor.py @@ -97,13 +97,13 @@ SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription] = { "ipv4_address": SysMonitorSensorEntityDescription( key="ipv4_address", name="IPv4 address", - icon="mdi:server-network", + icon="mdi:ip-network", mandatory_arg=True, ), "ipv6_address": SysMonitorSensorEntityDescription( key="ipv6_address", name="IPv6 address", - icon="mdi:server-network", + icon="mdi:ip-network", mandatory_arg=True, ), "last_boot": SysMonitorSensorEntityDescription( From 92b5721f8067ad9141737d39b0d9e6eeccaae45e Mon Sep 17 00:00:00 2001 From: Avishay Date: Wed, 9 Nov 2022 12:09:21 +0200 Subject: [PATCH 0316/1033] Fix modbus hvac mode keys (#81747) Change the HVAC mode register conf constants --- homeassistant/components/modbus/__init__.py | 22 +++++--- homeassistant/components/modbus/climate.py | 22 ++++++-- homeassistant/components/modbus/const.py | 7 +++ tests/components/modbus/test_climate.py | 59 ++++++++++++--------- 4 files changed, 75 insertions(+), 35 deletions(-) diff --git a/homeassistant/components/modbus/__init__.py b/homeassistant/components/modbus/__init__.py index cda0bb64703..d905a5f9423 100644 --- a/homeassistant/components/modbus/__init__.py +++ b/homeassistant/components/modbus/__init__.py @@ -9,7 +9,6 @@ import voluptuous as vol from homeassistant.components.binary_sensor import ( DEVICE_CLASSES_SCHEMA as BINARY_SENSOR_DEVICE_CLASSES_SCHEMA, ) -from homeassistant.components.climate import HVACMode from homeassistant.components.cover import ( DEVICE_CLASSES_SCHEMA as COVER_DEVICE_CLASSES_SCHEMA, ) @@ -66,6 +65,13 @@ from .const import ( # noqa: F401 CONF_DATA_TYPE, CONF_FANS, CONF_HUB, + CONF_HVAC_MODE_AUTO, + CONF_HVAC_MODE_COOL, + CONF_HVAC_MODE_DRY, + CONF_HVAC_MODE_FAN_ONLY, + CONF_HVAC_MODE_HEAT, + CONF_HVAC_MODE_HEAT_COOL, + CONF_HVAC_MODE_OFF, CONF_HVAC_MODE_REGISTER, CONF_HVAC_MODE_VALUES, CONF_HVAC_ONOFF_REGISTER, @@ -227,13 +233,13 @@ CLIMATE_SCHEMA = vol.All( { CONF_ADDRESS: cv.positive_int, CONF_HVAC_MODE_VALUES: { - vol.Optional(HVACMode.OFF.value): cv.positive_int, - vol.Optional(HVACMode.HEAT.value): cv.positive_int, - vol.Optional(HVACMode.COOL.value): cv.positive_int, - vol.Optional(HVACMode.HEAT_COOL.value): cv.positive_int, - vol.Optional(HVACMode.AUTO.value): cv.positive_int, - vol.Optional(HVACMode.DRY.value): cv.positive_int, - vol.Optional(HVACMode.FAN_ONLY.value): cv.positive_int, + vol.Optional(CONF_HVAC_MODE_OFF): cv.positive_int, + vol.Optional(CONF_HVAC_MODE_HEAT): cv.positive_int, + vol.Optional(CONF_HVAC_MODE_COOL): cv.positive_int, + vol.Optional(CONF_HVAC_MODE_HEAT_COOL): cv.positive_int, + vol.Optional(CONF_HVAC_MODE_AUTO): cv.positive_int, + vol.Optional(CONF_HVAC_MODE_DRY): cv.positive_int, + vol.Optional(CONF_HVAC_MODE_FAN_ONLY): cv.positive_int, }, } ), diff --git a/homeassistant/components/modbus/climate.py b/homeassistant/components/modbus/climate.py index 92efcfb17d5..4e6fdf6cae7 100644 --- a/homeassistant/components/modbus/climate.py +++ b/homeassistant/components/modbus/climate.py @@ -32,6 +32,13 @@ from .const import ( CALL_TYPE_WRITE_REGISTER, CALL_TYPE_WRITE_REGISTERS, CONF_CLIMATES, + CONF_HVAC_MODE_AUTO, + CONF_HVAC_MODE_COOL, + CONF_HVAC_MODE_DRY, + CONF_HVAC_MODE_FAN_ONLY, + CONF_HVAC_MODE_HEAT, + CONF_HVAC_MODE_HEAT_COOL, + CONF_HVAC_MODE_OFF, CONF_HVAC_MODE_REGISTER, CONF_HVAC_MODE_VALUES, CONF_HVAC_ONOFF_REGISTER, @@ -99,10 +106,19 @@ class ModbusThermostat(BaseStructPlatform, RestoreEntity, ClimateEntity): self._attr_hvac_mode = None self._hvac_mode_mapping: list[tuple[int, HVACMode]] = [] mode_value_config = mode_config[CONF_HVAC_MODE_VALUES] - for hvac_mode in HVACMode: - if hvac_mode.value in mode_value_config: + + for hvac_mode_kw, hvac_mode in ( + (CONF_HVAC_MODE_OFF, HVACMode.OFF), + (CONF_HVAC_MODE_HEAT, HVACMode.HEAT), + (CONF_HVAC_MODE_COOL, HVACMode.COOL), + (CONF_HVAC_MODE_HEAT_COOL, HVACMode.HEAT_COOL), + (CONF_HVAC_MODE_AUTO, HVACMode.AUTO), + (CONF_HVAC_MODE_DRY, HVACMode.DRY), + (CONF_HVAC_MODE_FAN_ONLY, HVACMode.FAN_ONLY), + ): + if hvac_mode_kw in mode_value_config: self._hvac_mode_mapping.append( - (mode_value_config[hvac_mode.value], hvac_mode) + (mode_value_config[hvac_mode_kw], hvac_mode) ) self._attr_hvac_modes.append(hvac_mode) diff --git a/homeassistant/components/modbus/const.py b/homeassistant/components/modbus/const.py index 2ad36f908ce..6ed52ae0544 100644 --- a/homeassistant/components/modbus/const.py +++ b/homeassistant/components/modbus/const.py @@ -56,6 +56,13 @@ CONF_TARGET_TEMP = "target_temp_register" CONF_HVAC_MODE_REGISTER = "hvac_mode_register" CONF_HVAC_MODE_VALUES = "values" CONF_HVAC_ONOFF_REGISTER = "hvac_onoff_register" +CONF_HVAC_MODE_OFF = "state_off" +CONF_HVAC_MODE_HEAT = "state_heat" +CONF_HVAC_MODE_COOL = "state_cool" +CONF_HVAC_MODE_HEAT_COOL = "state_heat_cool" +CONF_HVAC_MODE_AUTO = "state_auto" +CONF_HVAC_MODE_DRY = "state_dry" +CONF_HVAC_MODE_FAN_ONLY = "state_fan_only" CONF_VERIFY = "verify" CONF_VERIFY_REGISTER = "verify_register" CONF_VERIFY_STATE = "verify_state" diff --git a/tests/components/modbus/test_climate.py b/tests/components/modbus/test_climate.py index e554160d5bb..f9e43ae077b 100644 --- a/tests/components/modbus/test_climate.py +++ b/tests/components/modbus/test_climate.py @@ -10,6 +10,13 @@ from homeassistant.components.climate.const import ( from homeassistant.components.modbus.const import ( CONF_CLIMATES, CONF_DATA_TYPE, + CONF_HVAC_MODE_AUTO, + CONF_HVAC_MODE_COOL, + CONF_HVAC_MODE_DRY, + CONF_HVAC_MODE_FAN_ONLY, + CONF_HVAC_MODE_HEAT, + CONF_HVAC_MODE_HEAT_COOL, + CONF_HVAC_MODE_OFF, CONF_HVAC_MODE_REGISTER, CONF_HVAC_MODE_VALUES, CONF_HVAC_ONOFF_REGISTER, @@ -82,13 +89,13 @@ ENTITY_ID = f"{CLIMATE_DOMAIN}.{TEST_ENTITY_NAME}".replace(" ", "_") CONF_HVAC_MODE_REGISTER: { CONF_ADDRESS: 11, CONF_HVAC_MODE_VALUES: { - HVACMode.OFF.value: 0, - HVACMode.HEAT.value: 1, - HVACMode.COOL.value: 2, - HVACMode.HEAT_COOL.value: 3, - HVACMode.DRY.value: 4, - HVACMode.FAN_ONLY.value: 5, - HVACMode.AUTO.value: 6, + "state_off": 0, + "state_heat": 1, + "state_cool": 2, + "state_heat_cool": 3, + "state_dry": 4, + "state_fan_only": 5, + "state_auto": 6, }, }, } @@ -114,10 +121,12 @@ async def test_config_climate(hass, mock_modbus): CONF_HVAC_MODE_REGISTER: { CONF_ADDRESS: 11, CONF_HVAC_MODE_VALUES: { - HVACMode.OFF.value: 0, - HVACMode.HEAT.value: 1, - HVACMode.COOL.value: 2, - HVACMode.HEAT_COOL.value: 3, + CONF_HVAC_MODE_OFF: 0, + CONF_HVAC_MODE_HEAT: 1, + CONF_HVAC_MODE_COOL: 2, + CONF_HVAC_MODE_HEAT_COOL: 3, + CONF_HVAC_MODE_AUTO: 4, + CONF_HVAC_MODE_FAN_ONLY: 5, }, }, } @@ -132,6 +141,8 @@ async def test_config_hvac_mode_register(hass, mock_modbus): assert HVACMode.HEAT in state.attributes[ATTR_HVAC_MODES] assert HVACMode.COOL in state.attributes[ATTR_HVAC_MODES] assert HVACMode.HEAT_COOL in state.attributes[ATTR_HVAC_MODES] + assert HVACMode.AUTO in state.attributes[ATTR_HVAC_MODES] + assert HVACMode.FAN_ONLY in state.attributes[ATTR_HVAC_MODES] @pytest.mark.parametrize( @@ -203,9 +214,9 @@ async def test_temperature_climate(hass, expected, mock_do_cycle): CONF_HVAC_MODE_REGISTER: { CONF_ADDRESS: 118, CONF_HVAC_MODE_VALUES: { - HVACMode.COOL.value: 0, - HVACMode.HEAT.value: 1, - HVACMode.DRY.value: 2, + CONF_HVAC_MODE_COOL: 0, + CONF_HVAC_MODE_HEAT: 1, + CONF_HVAC_MODE_DRY: 2, }, }, }, @@ -227,9 +238,9 @@ async def test_temperature_climate(hass, expected, mock_do_cycle): CONF_HVAC_MODE_REGISTER: { CONF_ADDRESS: 118, CONF_HVAC_MODE_VALUES: { - HVACMode.COOL.value: 0, - HVACMode.HEAT.value: 1, - HVACMode.DRY.value: 2, + CONF_HVAC_MODE_COOL: 0, + CONF_HVAC_MODE_HEAT: 1, + CONF_HVAC_MODE_DRY: 2, }, }, }, @@ -251,9 +262,9 @@ async def test_temperature_climate(hass, expected, mock_do_cycle): CONF_HVAC_MODE_REGISTER: { CONF_ADDRESS: 118, CONF_HVAC_MODE_VALUES: { - HVACMode.COOL.value: 0, - HVACMode.HEAT.value: 2, - HVACMode.DRY.value: 3, + CONF_HVAC_MODE_COOL: 0, + CONF_HVAC_MODE_HEAT: 2, + CONF_HVAC_MODE_DRY: 3, }, }, CONF_HVAC_ONOFF_REGISTER: 119, @@ -374,8 +385,8 @@ async def test_service_climate_set_temperature( CONF_HVAC_MODE_REGISTER: { CONF_ADDRESS: 118, CONF_HVAC_MODE_VALUES: { - HVACMode.COOL.value: 1, - HVACMode.HEAT.value: 2, + CONF_HVAC_MODE_COOL: 1, + CONF_HVAC_MODE_HEAT: 2, }, }, } @@ -395,8 +406,8 @@ async def test_service_climate_set_temperature( CONF_HVAC_MODE_REGISTER: { CONF_ADDRESS: 118, CONF_HVAC_MODE_VALUES: { - HVACMode.COOL.value: 1, - HVACMode.HEAT.value: 2, + CONF_HVAC_MODE_COOL: 1, + CONF_HVAC_MODE_HEAT: 2, }, }, CONF_HVAC_ONOFF_REGISTER: 119, From b7f3eb77dcf6d81ad9c0ea6f8ec98803fb9f6d66 Mon Sep 17 00:00:00 2001 From: Malte Franken Date: Wed, 9 Nov 2022 21:20:27 +1100 Subject: [PATCH 0317/1033] Add integration_type to usgs_earthquakes_feed (#81846) --- homeassistant/components/usgs_earthquakes_feed/manifest.json | 3 ++- homeassistant/generated/integrations.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/usgs_earthquakes_feed/manifest.json b/homeassistant/components/usgs_earthquakes_feed/manifest.json index bd8ec9633bd..ee37381a6fa 100644 --- a/homeassistant/components/usgs_earthquakes_feed/manifest.json +++ b/homeassistant/components/usgs_earthquakes_feed/manifest.json @@ -5,5 +5,6 @@ "requirements": ["aio_geojson_usgs_earthquakes==0.1"], "codeowners": ["@exxamalte"], "iot_class": "cloud_polling", - "loggers": ["aio_geojson_usgs_earthquakes"] + "loggers": ["aio_geojson_usgs_earthquakes"], + "integration_type": "service" } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 91f19977ccb..bbb7928f68f 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -5687,7 +5687,7 @@ }, "usgs_earthquakes_feed": { "name": "U.S. Geological Survey Earthquake Hazards (USGS)", - "integration_type": "hub", + "integration_type": "service", "config_flow": false, "iot_class": "cloud_polling" }, From 402bac5ed7af5752baa28b4ee8906ee45a2b50d8 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 9 Nov 2022 12:14:26 +0100 Subject: [PATCH 0318/1033] Improve type hints in camera (#81794) --- homeassistant/components/camera/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index d860776a797..136cd3b05f1 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -859,8 +859,9 @@ async def websocket_get_prefs( hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] ) -> None: """Handle request for account info.""" - prefs = hass.data[DATA_CAMERA_PREFS].get(msg["entity_id"]) - connection.send_result(msg["id"], prefs.as_dict()) + prefs: CameraPreferences = hass.data[DATA_CAMERA_PREFS] + camera_prefs = prefs.get(msg["entity_id"]) + connection.send_result(msg["id"], camera_prefs.as_dict()) @websocket_api.websocket_command( @@ -876,7 +877,7 @@ async def websocket_update_prefs( hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] ) -> None: """Handle request for account info.""" - prefs = hass.data[DATA_CAMERA_PREFS] + prefs: CameraPreferences = hass.data[DATA_CAMERA_PREFS] changes = dict(msg) changes.pop("id") @@ -955,7 +956,8 @@ async def _async_stream_endpoint_url( ) # Update keepalive setting which manages idle shutdown - camera_prefs = hass.data[DATA_CAMERA_PREFS].get(camera.entity_id) + prefs: CameraPreferences = hass.data[DATA_CAMERA_PREFS] + camera_prefs = prefs.get(camera.entity_id) stream.keepalive = camera_prefs.preload_stream stream.orientation = camera_prefs.orientation From 738419309d3bdb1d60835426cc029194b3767e14 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 9 Nov 2022 13:22:14 +0200 Subject: [PATCH 0319/1033] Add numpy requirement to stream (#81841) Closes https://github.com/home-assistant/core/pull/81790 --- homeassistant/components/stream/manifest.json | 2 +- requirements_all.txt | 1 + requirements_test_all.txt | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/stream/manifest.json b/homeassistant/components/stream/manifest.json index 1f79da20542..815acd5b39c 100644 --- a/homeassistant/components/stream/manifest.json +++ b/homeassistant/components/stream/manifest.json @@ -2,7 +2,7 @@ "domain": "stream", "name": "Stream", "documentation": "https://www.home-assistant.io/integrations/stream", - "requirements": ["PyTurboJPEG==1.6.7", "ha-av==10.0.0"], + "requirements": ["PyTurboJPEG==1.6.7", "ha-av==10.0.0", "numpy==1.23.2"], "dependencies": ["http"], "codeowners": ["@hunterjm", "@uvjustin", "@allenporter"], "quality_scale": "internal", diff --git a/requirements_all.txt b/requirements_all.txt index 55fc97cb6d3..c9b0fa1e77d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1195,6 +1195,7 @@ numato-gpio==0.10.0 # homeassistant.components.compensation # homeassistant.components.iqvia # homeassistant.components.opencv +# homeassistant.components.stream # homeassistant.components.tensorflow # homeassistant.components.trend numpy==1.23.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5b7773c71f5..662cdd1ad4c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -870,6 +870,7 @@ numato-gpio==0.10.0 # homeassistant.components.compensation # homeassistant.components.iqvia # homeassistant.components.opencv +# homeassistant.components.stream # homeassistant.components.tensorflow # homeassistant.components.trend numpy==1.23.2 From 2eb37f527a898692a0b2ebad9815398356eeffb8 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 9 Nov 2022 12:45:33 +0100 Subject: [PATCH 0320/1033] Update psutil to 5.9.4 (#81840) --- homeassistant/components/systemmonitor/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/systemmonitor/manifest.json b/homeassistant/components/systemmonitor/manifest.json index a2db68f11c7..a231c3e83f3 100644 --- a/homeassistant/components/systemmonitor/manifest.json +++ b/homeassistant/components/systemmonitor/manifest.json @@ -2,7 +2,7 @@ "domain": "systemmonitor", "name": "System Monitor", "documentation": "https://www.home-assistant.io/integrations/systemmonitor", - "requirements": ["psutil==5.9.3"], + "requirements": ["psutil==5.9.4"], "codeowners": [], "iot_class": "local_push", "loggers": ["psutil"] diff --git a/requirements_all.txt b/requirements_all.txt index c9b0fa1e77d..612737c03a3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1362,7 +1362,7 @@ proxmoxer==1.3.1 psutil-home-assistant==0.0.1 # homeassistant.components.systemmonitor -psutil==5.9.3 +psutil==5.9.4 # homeassistant.components.pulseaudio_loopback pulsectl==20.2.4 From 0cd9fe3288b4f4a3f027c7462f1896617e3aaeee Mon Sep 17 00:00:00 2001 From: Vincent Knoop Pathuis <48653141+vpathuis@users.noreply.github.com> Date: Wed, 9 Nov 2022 13:45:28 +0100 Subject: [PATCH 0321/1033] Landis+Gyr Heat Meter code improvements (#81184) --- .../landisgyr_heat_meter/__init__.py | 44 ++++++-- .../landisgyr_heat_meter/config_flow.py | 79 +++++++------- .../components/landisgyr_heat_meter/const.py | 45 +++++--- .../landisgyr_heat_meter/manifest.json | 1 + .../components/landisgyr_heat_meter/sensor.py | 33 ++---- .../landisgyr_heat_meter/strings.json | 4 - .../landisgyr_heat_meter/test_config_flow.py | 102 ++++++------------ .../landisgyr_heat_meter/test_init.py | 72 +++++++++++-- .../landisgyr_heat_meter/test_sensor.py | 6 +- 9 files changed, 218 insertions(+), 168 deletions(-) diff --git a/homeassistant/components/landisgyr_heat_meter/__init__.py b/homeassistant/components/landisgyr_heat_meter/__init__.py index 3ef235ff8af..34724c07ca9 100644 --- a/homeassistant/components/landisgyr_heat_meter/__init__.py +++ b/homeassistant/components/landisgyr_heat_meter/__init__.py @@ -4,11 +4,12 @@ from __future__ import annotations from datetime import timedelta import logging -from ultraheat_api import HeatMeterService, UltraheatReader +import ultraheat_api from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_DEVICE, Platform -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_registry import async_migrate_entries from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import DOMAIN @@ -22,13 +23,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up heat meter from a config entry.""" _LOGGER.debug("Initializing %s integration on %s", DOMAIN, entry.data[CONF_DEVICE]) - reader = UltraheatReader(entry.data[CONF_DEVICE]) - - api = HeatMeterService(reader) + reader = ultraheat_api.UltraheatReader(entry.data[CONF_DEVICE]) + api = ultraheat_api.HeatMeterService(reader) async def async_update_data(): """Fetch data from the API.""" - _LOGGER.info("Polling on %s", entry.data[CONF_DEVICE]) + _LOGGER.debug("Polling on %s", entry.data[CONF_DEVICE]) return await hass.async_add_executor_job(api.read) # Polling is only daily to prevent battery drain. @@ -53,3 +53,35 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DOMAIN].pop(entry.entry_id) return unload_ok + + +async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: + """Migrate old entry.""" + _LOGGER.debug("Migrating from version %s", config_entry.version) + + # Removing domain name and config entry id from entity unique id's, replacing it with device number + if config_entry.version == 1: + + config_entry.version = 2 + + device_number = config_entry.data["device_number"] + + @callback + def update_entity_unique_id(entity_entry): + """Update unique ID of entity entry.""" + if entity_entry.platform in entity_entry.unique_id: + return { + "new_unique_id": entity_entry.unique_id.replace( + f"{entity_entry.platform}_{entity_entry.config_entry_id}", + f"{device_number}", + ) + } + + await async_migrate_entries( + hass, config_entry.entry_id, update_entity_unique_id + ) + hass.config_entries.async_update_entry(config_entry) + + _LOGGER.info("Migration to version %s successful", config_entry.version) + + return True diff --git a/homeassistant/components/landisgyr_heat_meter/config_flow.py b/homeassistant/components/landisgyr_heat_meter/config_flow.py index 2e244a9a65f..f12992166fb 100644 --- a/homeassistant/components/landisgyr_heat_meter/config_flow.py +++ b/homeassistant/components/landisgyr_heat_meter/config_flow.py @@ -1,17 +1,21 @@ """Config flow for Landis+Gyr Heat Meter integration.""" from __future__ import annotations +import asyncio import logging -import os +from typing import Any import async_timeout import serial -import serial.tools.list_ports -from ultraheat_api import HeatMeterService, UltraheatReader +from serial.tools import list_ports +import ultraheat_api import voluptuous as vol from homeassistant import config_entries +from homeassistant.components import usb from homeassistant.const import CONF_DEVICE +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResult from homeassistant.exceptions import HomeAssistantError from .const import DOMAIN, ULTRAHEAT_TIMEOUT @@ -30,9 +34,11 @@ STEP_USER_DATA_SCHEMA = vol.Schema( class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for Ultraheat Heat Meter.""" - VERSION = 1 + VERSION = 2 - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Step when setting up serial configuration.""" errors = {} @@ -41,7 +47,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return await self.async_step_setup_serial_manual_path() dev_path = await self.hass.async_add_executor_job( - get_serial_by_id, user_input[CONF_DEVICE] + usb.get_serial_by_id, user_input[CONF_DEVICE] ) _LOGGER.debug("Using this path : %s", dev_path) @@ -50,12 +56,15 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): except CannotConnect: errors["base"] = "cannot_connect" - ports = await self.get_ports() + ports = await get_usb_ports(self.hass) + ports[CONF_MANUAL_PATH] = CONF_MANUAL_PATH schema = vol.Schema({vol.Required(CONF_DEVICE): vol.In(ports)}) return self.async_show_form(step_id="user", data_schema=schema, errors=errors) - async def async_step_setup_serial_manual_path(self, user_input=None): + async def async_step_setup_serial_manual_path( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Set path manually.""" errors = {} @@ -78,7 +87,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): model, device_number = await self.validate_ultraheat(dev_path) _LOGGER.debug("Got model %s and device_number %s", model, device_number) - await self.async_set_unique_id(device_number) + await self.async_set_unique_id(f"{device_number}") self._abort_if_unique_id_configured() data = { CONF_DEVICE: dev_path, @@ -90,48 +99,44 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): data=data, ) - async def validate_ultraheat(self, port: str): + async def validate_ultraheat(self, port: str) -> tuple[str, str]: """Validate the user input allows us to connect.""" - reader = UltraheatReader(port) - heat_meter = HeatMeterService(reader) + reader = ultraheat_api.UltraheatReader(port) + heat_meter = ultraheat_api.HeatMeterService(reader) try: async with async_timeout.timeout(ULTRAHEAT_TIMEOUT): # validate and retrieve the model and device number for a unique id data = await self.hass.async_add_executor_job(heat_meter.read) - _LOGGER.debug("Got data from Ultraheat API: %s", data) - except Exception as err: + except (asyncio.TimeoutError, serial.serialutil.SerialException) as err: _LOGGER.warning("Failed read data from: %s. %s", port, err) raise CannotConnect(f"Error communicating with device: {err}") from err - _LOGGER.debug("Successfully connected to %s", port) + _LOGGER.debug("Successfully connected to %s. Got data: %s", port, data) return data.model, data.device_number - async def get_ports(self) -> dict: - """Get the available ports.""" - ports = await self.hass.async_add_executor_job(serial.tools.list_ports.comports) - formatted_ports = {} - for port in ports: - formatted_ports[ - port.device - ] = f"{port}, s/n: {port.serial_number or 'n/a'}" + ( - f" - {port.manufacturer}" if port.manufacturer else "" + +async def get_usb_ports(hass: HomeAssistant) -> dict[str, str]: + """Return a dict of USB ports and their friendly names.""" + ports = await hass.async_add_executor_job(list_ports.comports) + port_descriptions = {} + for port in ports: + # this prevents an issue with usb_device_from_port not working for ports without vid on RPi + if port.vid: + usb_device = usb.usb_device_from_port(port) + dev_path = usb.get_serial_by_id(usb_device.device) + human_name = usb.human_readable_device_name( + dev_path, + usb_device.serial_number, + usb_device.manufacturer, + usb_device.description, + usb_device.vid, + usb_device.pid, ) - formatted_ports[CONF_MANUAL_PATH] = CONF_MANUAL_PATH - return formatted_ports + port_descriptions[dev_path] = human_name - -def get_serial_by_id(dev_path: str) -> str: - """Return a /dev/serial/by-id match for given device if available.""" - by_id = "/dev/serial/by-id" - if not os.path.isdir(by_id): - return dev_path - - for path in (entry.path for entry in os.scandir(by_id) if entry.is_symlink()): - if os.path.realpath(path) == dev_path: - return path - return dev_path + return port_descriptions class CannotConnect(HomeAssistantError): diff --git a/homeassistant/components/landisgyr_heat_meter/const.py b/homeassistant/components/landisgyr_heat_meter/const.py index 57a8f9d9be4..7767a491f3b 100644 --- a/homeassistant/components/landisgyr_heat_meter/const.py +++ b/homeassistant/components/landisgyr_heat_meter/const.py @@ -5,7 +5,15 @@ from homeassistant.components.sensor import ( SensorEntityDescription, SensorStateClass, ) -from homeassistant.const import ENERGY_MEGA_WATT_HOUR, TEMP_CELSIUS, VOLUME_CUBIC_METERS +from homeassistant.const import ( + ENERGY_MEGA_WATT_HOUR, + POWER_KILO_WATT, + TEMP_CELSIUS, + TIME_HOURS, + TIME_MINUTES, + VOLUME_CUBIC_METERS, + VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, +) from homeassistant.helpers.entity import EntityCategory DOMAIN = "landisgyr_heat_meter" @@ -26,6 +34,7 @@ HEAT_METER_SENSOR_TYPES = ( key="volume_usage_m3", icon="mdi:fire", name="Volume usage", + device_class=SensorDeviceClass.VOLUME, native_unit_of_measurement=VOLUME_CUBIC_METERS, state_class=SensorStateClass.TOTAL, ), @@ -56,12 +65,14 @@ HEAT_METER_SENSOR_TYPES = ( key="volume_previous_year_m3", icon="mdi:fire", name="Volume usage previous year", + device_class=SensorDeviceClass.VOLUME, native_unit_of_measurement=VOLUME_CUBIC_METERS, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="ownership_number", name="Ownership number", + icon="mdi:identifier", entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( @@ -73,41 +84,41 @@ HEAT_METER_SENSOR_TYPES = ( SensorEntityDescription( key="device_number", name="Device number", + icon="mdi:identifier", entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="measurement_period_minutes", name="Measurement period minutes", - icon="mdi:clock-outline", + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=TIME_MINUTES, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="power_max_kw", name="Power max", - native_unit_of_measurement="kW", - icon="mdi:power-plug-outline", + native_unit_of_measurement=POWER_KILO_WATT, device_class=SensorDeviceClass.POWER, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="power_max_previous_year_kw", name="Power max previous year", - native_unit_of_measurement="kW", - icon="mdi:power-plug-outline", + native_unit_of_measurement=POWER_KILO_WATT, device_class=SensorDeviceClass.POWER, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="flowrate_max_m3ph", name="Flowrate max", - native_unit_of_measurement="m3ph", + native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, icon="mdi:water-outline", entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="flowrate_max_previous_year_m3ph", name="Flowrate max previous year", - native_unit_of_measurement="m3ph", + native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, icon="mdi:water-outline", entity_category=EntityCategory.DIAGNOSTIC, ), @@ -115,7 +126,6 @@ HEAT_METER_SENSOR_TYPES = ( key="return_temperature_max_c", name="Return temperature max", native_unit_of_measurement=TEMP_CELSIUS, - icon="mdi:thermometer", device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, ), @@ -123,7 +133,6 @@ HEAT_METER_SENSOR_TYPES = ( key="return_temperature_max_previous_year_c", name="Return temperature max previous year", native_unit_of_measurement=TEMP_CELSIUS, - icon="mdi:thermometer", device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, ), @@ -131,7 +140,6 @@ HEAT_METER_SENSOR_TYPES = ( key="flow_temperature_max_c", name="Flow temperature max", native_unit_of_measurement=TEMP_CELSIUS, - icon="mdi:thermometer", device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, ), @@ -139,32 +147,35 @@ HEAT_METER_SENSOR_TYPES = ( key="flow_temperature_max_previous_year_c", name="Flow temperature max previous year", native_unit_of_measurement=TEMP_CELSIUS, - icon="mdi:thermometer", device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="operating_hours", name="Operating hours", - icon="mdi:clock-outline", + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=TIME_HOURS, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="flow_hours", name="Flow hours", - icon="mdi:clock-outline", + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=TIME_HOURS, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="fault_hours", name="Fault hours", - icon="mdi:clock-outline", + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=TIME_HOURS, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="fault_hours_previous_year", name="Fault hours previous year", - icon="mdi:clock-outline", + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=TIME_HOURS, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( @@ -189,7 +200,7 @@ HEAT_METER_SENSOR_TYPES = ( SensorEntityDescription( key="measuring_range_m3ph", name="Measuring range", - native_unit_of_measurement="m3ph", + native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, icon="mdi:water-outline", entity_category=EntityCategory.DIAGNOSTIC, ), diff --git a/homeassistant/components/landisgyr_heat_meter/manifest.json b/homeassistant/components/landisgyr_heat_meter/manifest.json index 7be3115a6d3..a20225c88b0 100644 --- a/homeassistant/components/landisgyr_heat_meter/manifest.json +++ b/homeassistant/components/landisgyr_heat_meter/manifest.json @@ -9,5 +9,6 @@ "homekit": {}, "dependencies": [], "codeowners": ["@vpathuis"], + "dependencies": ["usb"], "iot_class": "local_polling" } diff --git a/homeassistant/components/landisgyr_heat_meter/sensor.py b/homeassistant/components/landisgyr_heat_meter/sensor.py index 23a6e217458..2b4fc6edea8 100644 --- a/homeassistant/components/landisgyr_heat_meter/sensor.py +++ b/homeassistant/components/landisgyr_heat_meter/sensor.py @@ -4,13 +4,8 @@ from __future__ import annotations from dataclasses import asdict import logging -from homeassistant.components.sensor import ( - ATTR_STATE_CLASS, - RestoreSensor, - SensorDeviceClass, -) +from homeassistant.components.sensor import RestoreSensor, SensorDeviceClass from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_UNIT_OF_MEASUREMENT from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -27,8 +22,6 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the sensor platform.""" - _LOGGER.info("The Landis+Gyr Heat Meter sensor platform is being set up!") - unique_id = entry.entry_id coordinator = hass.data[DOMAIN][entry.entry_id] @@ -44,7 +37,7 @@ async def async_setup_entry( sensors = [] for description in HEAT_METER_SENSOR_TYPES: - sensors.append(HeatMeterSensor(coordinator, unique_id, description, device)) + sensors.append(HeatMeterSensor(coordinator, description, device)) async_add_entities(sensors) @@ -52,24 +45,16 @@ async def async_setup_entry( class HeatMeterSensor(CoordinatorEntity, RestoreSensor): """Representation of a Sensor.""" - def __init__(self, coordinator, unique_id, description, device): + def __init__(self, coordinator, description, device): """Set up the sensor with the initial values.""" super().__init__(coordinator) self.key = description.key - self._attr_unique_id = f"{DOMAIN}_{unique_id}_{description.key}" - self._attr_name = "Heat Meter " + description.name - if hasattr(description, "icon"): - self._attr_icon = description.icon - if hasattr(description, "entity_category"): - self._attr_entity_category = description.entity_category - if hasattr(description, ATTR_STATE_CLASS): - self._attr_state_class = description.state_class - if hasattr(description, ATTR_DEVICE_CLASS): - self._attr_device_class = description.device_class - if hasattr(description, ATTR_UNIT_OF_MEASUREMENT): - self._attr_native_unit_of_measurement = ( - description.native_unit_of_measurement - ) + self._attr_unique_id = ( + f"{coordinator.config_entry.data['device_number']}_{description.key}" + ) + self._attr_name = f"Heat Meter {description.name}" + self.entity_description = description + self._attr_device_info = device self._attr_should_poll = bool(self.key in ("heat_usage", "heat_previous_year")) diff --git a/homeassistant/components/landisgyr_heat_meter/strings.json b/homeassistant/components/landisgyr_heat_meter/strings.json index 61e170af2b3..4bae2490006 100644 --- a/homeassistant/components/landisgyr_heat_meter/strings.json +++ b/homeassistant/components/landisgyr_heat_meter/strings.json @@ -12,10 +12,6 @@ } } }, - "error": { - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", - "unknown": "[%key:common::config_flow::error::unknown%]" - }, "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" } diff --git a/tests/components/landisgyr_heat_meter/test_config_flow.py b/tests/components/landisgyr_heat_meter/test_config_flow.py index 9200a9b3d23..57638868647 100644 --- a/tests/components/landisgyr_heat_meter/test_config_flow.py +++ b/tests/components/landisgyr_heat_meter/test_config_flow.py @@ -1,7 +1,8 @@ """Test the Landis + Gyr Heat Meter config flow.""" from dataclasses import dataclass -from unittest.mock import MagicMock, patch +from unittest.mock import patch +import serial import serial.tools.list_ports from homeassistant import config_entries @@ -9,6 +10,10 @@ from homeassistant.components.landisgyr_heat_meter import DOMAIN from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from tests.common import MockConfigEntry + +API_HEAT_METER_SERVICE = "homeassistant.components.landisgyr_heat_meter.config_flow.ultraheat_api.HeatMeterService" + def mock_serial_port(): """Mock of a serial port.""" @@ -17,6 +22,8 @@ def mock_serial_port(): port.manufacturer = "Virtual serial port" port.device = "/dev/ttyUSB1234" port.description = "Some serial port" + port.pid = 9876 + port.vid = 5678 return port @@ -29,7 +36,7 @@ class MockUltraheatRead: device_number: str -@patch("homeassistant.components.landisgyr_heat_meter.config_flow.HeatMeterService") +@patch(API_HEAT_METER_SERVICE) async def test_manual_entry(mock_heat_meter, hass: HomeAssistant) -> None: """Test manual entry.""" @@ -67,7 +74,7 @@ async def test_manual_entry(mock_heat_meter, hass: HomeAssistant) -> None: } -@patch("homeassistant.components.landisgyr_heat_meter.config_flow.HeatMeterService") +@patch(API_HEAT_METER_SERVICE) @patch("serial.tools.list_ports.comports", return_value=[mock_serial_port()]) async def test_list_entry(mock_port, mock_heat_meter, hass: HomeAssistant) -> None: """Test select from list entry.""" @@ -94,11 +101,11 @@ async def test_list_entry(mock_port, mock_heat_meter, hass: HomeAssistant) -> No } -@patch("homeassistant.components.landisgyr_heat_meter.config_flow.HeatMeterService") +@patch(API_HEAT_METER_SERVICE) async def test_manual_entry_fail(mock_heat_meter, hass: HomeAssistant) -> None: """Test manual entry fails.""" - mock_heat_meter().read.side_effect = Exception + mock_heat_meter().read.side_effect = serial.serialutil.SerialException result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -128,12 +135,12 @@ async def test_manual_entry_fail(mock_heat_meter, hass: HomeAssistant) -> None: assert result["errors"] == {"base": "cannot_connect"} -@patch("homeassistant.components.landisgyr_heat_meter.config_flow.HeatMeterService") +@patch(API_HEAT_METER_SERVICE) @patch("serial.tools.list_ports.comports", return_value=[mock_serial_port()]) async def test_list_entry_fail(mock_port, mock_heat_meter, hass: HomeAssistant) -> None: """Test select from list entry fails.""" - mock_heat_meter().read.side_effect = Exception + mock_heat_meter().read.side_effect = serial.serialutil.SerialException port = mock_serial_port() result = await hass.config_entries.flow.async_init( @@ -151,77 +158,36 @@ async def test_list_entry_fail(mock_port, mock_heat_meter, hass: HomeAssistant) assert result["errors"] == {"base": "cannot_connect"} -@patch("homeassistant.components.landisgyr_heat_meter.config_flow.HeatMeterService") +@patch(API_HEAT_METER_SERVICE) @patch("serial.tools.list_ports.comports", return_value=[mock_serial_port()]) -async def test_get_serial_by_id_realpath( +async def test_already_configured( mock_port, mock_heat_meter, hass: HomeAssistant ) -> None: - """Test getting the serial path name.""" + """Test we abort if the Heat Meter is already configured.""" + # create and add existing entry + entry_data = { + "device": "/dev/USB0", + "model": "LUGCUH50", + "device_number": "123456789", + } + mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="123456789", data=entry_data) + mock_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + + # run flow and see if it aborts mock_heat_meter().read.return_value = MockUltraheatRead("LUGCUH50", "123456789") port = mock_serial_port() result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == FlowResultType.FORM - assert result["step_id"] == "user" - assert result["errors"] == {} - scandir = [MagicMock(), MagicMock()] - scandir[0].path = "/dev/ttyUSB1234" - scandir[0].is_symlink.return_value = True - scandir[1].path = "/dev/ttyUSB5678" - scandir[1].is_symlink.return_value = True - - with patch("os.path") as path: - with patch("os.scandir", return_value=scandir): - path.isdir.return_value = True - path.realpath.side_effect = ["/dev/ttyUSB1234", "/dev/ttyUSB5678"] - result = await hass.config_entries.flow.async_configure( - result["flow_id"], {"device": port.device} - ) - assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == "LUGCUH50" - assert result["data"] == { - "device": port.device, - "model": "LUGCUH50", - "device_number": "123456789", - } - - -@patch("homeassistant.components.landisgyr_heat_meter.config_flow.HeatMeterService") -@patch("serial.tools.list_ports.comports", return_value=[mock_serial_port()]) -async def test_get_serial_by_id_dev_path( - mock_port, mock_heat_meter, hass: HomeAssistant -) -> None: - """Test getting the serial path name with no realpath result.""" - - mock_heat_meter().read.return_value = MockUltraheatRead("LUGCUH50", "123456789") - port = mock_serial_port() - - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"device": port.device} ) - assert result["type"] == FlowResultType.FORM - assert result["step_id"] == "user" - assert result["errors"] == {} - scandir = [MagicMock()] - scandir[0].path.return_value = "/dev/serial/by-id/USB5678" - scandir[0].is_symlink.return_value = True - - with patch("os.path") as path: - with patch("os.scandir", return_value=scandir): - path.isdir.return_value = True - path.realpath.side_effect = ["/dev/ttyUSB5678"] - result = await hass.config_entries.flow.async_configure( - result["flow_id"], {"device": port.device} - ) - assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == "LUGCUH50" - assert result["data"] == { - "device": port.device, - "model": "LUGCUH50", - "device_number": "123456789", - } + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_configured" diff --git a/tests/components/landisgyr_heat_meter/test_init.py b/tests/components/landisgyr_heat_meter/test_init.py index b3630fc4872..6e300ec1332 100644 --- a/tests/components/landisgyr_heat_meter/test_init.py +++ b/tests/components/landisgyr_heat_meter/test_init.py @@ -1,22 +1,78 @@ """Test the Landis + Gyr Heat Meter init.""" -from homeassistant.const import CONF_DEVICE +from unittest.mock import patch + +from homeassistant.components.landisgyr_heat_meter.const import ( + DOMAIN as LANDISGYR_HEAT_METER_DOMAIN, +) +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.helpers import entity_registry as er from tests.common import MockConfigEntry +API_HEAT_METER_SERVICE = ( + "homeassistant.components.landisgyr_heat_meter.ultraheat_api.HeatMeterService" +) -async def test_unload_entry(hass): + +@patch(API_HEAT_METER_SERVICE) +async def test_unload_entry(_, hass): """Test removing config entry.""" - entry = MockConfigEntry( + mock_entry_data = { + "device": "/dev/USB0", + "model": "LUGCUH50", + "device_number": "12345", + } + mock_entry = MockConfigEntry( domain="landisgyr_heat_meter", title="LUGCUH50", - data={CONF_DEVICE: "/dev/1234"}, + entry_id="987654321", + data=mock_entry_data, ) + mock_entry.add_to_hass(hass) - entry.add_to_hass(hass) - - assert await hass.config_entries.async_setup(entry.entry_id) + assert await hass.config_entries.async_setup(mock_entry.entry_id) await hass.async_block_till_done() assert "landisgyr_heat_meter" in hass.config.components - assert await hass.config_entries.async_remove(entry.entry_id) + assert await hass.config_entries.async_remove(mock_entry.entry_id) + + +@patch(API_HEAT_METER_SERVICE) +async def test_migrate_entry(_, hass): + """Test successful migration of entry data from version 1 to 2.""" + + mock_entry_data = { + "device": "/dev/USB0", + "model": "LUGCUH50", + "device_number": "12345", + } + mock_entry = MockConfigEntry( + domain="landisgyr_heat_meter", + title="LUGCUH50", + entry_id="987654321", + data=mock_entry_data, + ) + assert mock_entry.data == mock_entry_data + assert mock_entry.version == 1 + + mock_entry.add_to_hass(hass) + + # Create entity entry to migrate to new unique ID + registry = er.async_get(hass) + registry.async_get_or_create( + SENSOR_DOMAIN, + LANDISGYR_HEAT_METER_DOMAIN, + "landisgyr_heat_meter_987654321_measuring_range_m3ph", + suggested_object_id="heat_meter_measuring_range", + config_entry=mock_entry, + ) + + assert await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + assert "landisgyr_heat_meter" in hass.config.components + + # Check if entity unique id is migrated successfully + assert mock_entry.version == 2 + entity = registry.async_get("sensor.heat_meter_measuring_range") + assert entity.unique_id == "12345_measuring_range_m3ph" diff --git a/tests/components/landisgyr_heat_meter/test_sensor.py b/tests/components/landisgyr_heat_meter/test_sensor.py index 1a068093d0e..cbaca71e52f 100644 --- a/tests/components/landisgyr_heat_meter/test_sensor.py +++ b/tests/components/landisgyr_heat_meter/test_sensor.py @@ -42,7 +42,7 @@ class MockHeatMeterResponse: meter_date_time: datetime.datetime -@patch("homeassistant.components.landisgyr_heat_meter.HeatMeterService") +@patch("homeassistant.components.landisgyr_heat_meter.ultraheat_api.HeatMeterService") async def test_create_sensors(mock_heat_meter, hass): """Test sensor.""" entry_data = { @@ -107,7 +107,7 @@ async def test_create_sensors(mock_heat_meter, hass): assert entity_registry_entry.entity_category == EntityCategory.DIAGNOSTIC -@patch("homeassistant.components.landisgyr_heat_meter.HeatMeterService") +@patch("homeassistant.components.landisgyr_heat_meter.ultraheat_api.HeatMeterService") async def test_restore_state(mock_heat_meter, hass): """Test sensor restore state.""" # Home assistant is not running yet @@ -177,7 +177,6 @@ async def test_restore_state(mock_heat_meter, hass): mock_entry.add_to_hass(hass) await hass.config_entries.async_setup(mock_entry.entry_id) - await async_setup_component(hass, HA_DOMAIN, {}) await hass.async_block_till_done() # restore from cache @@ -195,6 +194,5 @@ async def test_restore_state(mock_heat_meter, hass): state = hass.states.get("sensor.heat_meter_device_number") assert state - print("STATE IS: ", state) assert state.state == "devicenr_789" assert state.attributes.get(ATTR_STATE_CLASS) is None From 4b4bf54994a3d164ce38fd4b014bc4c2b9cd3a86 Mon Sep 17 00:00:00 2001 From: Hessel Date: Wed, 9 Nov 2022 15:22:54 +0100 Subject: [PATCH 0322/1033] Bump wallbox to 0.4.12 (#81852) Version Bump for WALLBOX --- homeassistant/components/wallbox/__init__.py | 6 +++++- homeassistant/components/wallbox/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/wallbox/__init__.py b/homeassistant/components/wallbox/__init__.py index 6382cf05940..d927e7282ed 100644 --- a/homeassistant/components/wallbox/__init__.py +++ b/homeassistant/components/wallbox/__init__.py @@ -194,7 +194,11 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]): async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Wallbox from a config entry.""" - wallbox = Wallbox(entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD]) + wallbox = Wallbox( + entry.data[CONF_USERNAME], + entry.data[CONF_PASSWORD], + jwtTokenDrift=UPDATE_INTERVAL, + ) wallbox_coordinator = WallboxCoordinator( entry.data[CONF_STATION], wallbox, diff --git a/homeassistant/components/wallbox/manifest.json b/homeassistant/components/wallbox/manifest.json index 433a759bea5..e1d64ab9478 100644 --- a/homeassistant/components/wallbox/manifest.json +++ b/homeassistant/components/wallbox/manifest.json @@ -3,7 +3,7 @@ "name": "Wallbox", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/wallbox", - "requirements": ["wallbox==0.4.10"], + "requirements": ["wallbox==0.4.12"], "codeowners": ["@hesselonline"], "iot_class": "cloud_polling", "loggers": ["wallbox"] diff --git a/requirements_all.txt b/requirements_all.txt index 612737c03a3..d8dab654842 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2529,7 +2529,7 @@ vultr==0.1.2 wakeonlan==2.1.0 # homeassistant.components.wallbox -wallbox==0.4.10 +wallbox==0.4.12 # homeassistant.components.waqi waqiasync==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 662cdd1ad4c..041b8577331 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1757,7 +1757,7 @@ vultr==0.1.2 wakeonlan==2.1.0 # homeassistant.components.wallbox -wallbox==0.4.10 +wallbox==0.4.12 # homeassistant.components.folder_watcher watchdog==2.1.9 From b72876d369e4320ab5114f70ba75d4000c28add1 Mon Sep 17 00:00:00 2001 From: Ernst Klamer Date: Wed, 9 Nov 2022 15:31:58 +0100 Subject: [PATCH 0323/1033] Add support for BTHome V2 to bthome (#81811) * Add BTHome v2 support * Add new sensor types * Add new sensor types --- homeassistant/components/bthome/manifest.json | 6 +- homeassistant/components/bthome/sensor.py | 88 ++- homeassistant/generated/bluetooth.py | 5 + requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/bthome/__init__.py | 23 +- tests/components/bthome/test_binary_sensor.py | 98 ++- tests/components/bthome/test_sensor.py | 610 +++++++++++++++++- 8 files changed, 790 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/bthome/manifest.json b/homeassistant/components/bthome/manifest.json index 3b4cbe2f4f4..0111f765014 100644 --- a/homeassistant/components/bthome/manifest.json +++ b/homeassistant/components/bthome/manifest.json @@ -11,9 +11,13 @@ { "connectable": false, "service_data_uuid": "0000181e-0000-1000-8000-00805f9b34fb" + }, + { + "connectable": false, + "service_data_uuid": "0000fcd2-0000-1000-8000-00805f9b34fb" } ], - "requirements": ["bthome-ble==1.2.2"], + "requirements": ["bthome-ble==2.2.1"], "dependencies": ["bluetooth"], "codeowners": ["@Ernst79"], "iot_class": "local_push" diff --git a/homeassistant/components/bthome/sensor.py b/homeassistant/components/bthome/sensor.py index 188bd659c6d..e7757c2e872 100644 --- a/homeassistant/components/bthome/sensor.py +++ b/homeassistant/components/bthome/sensor.py @@ -21,16 +21,20 @@ from homeassistant.components.sensor import ( from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_MILLION, + DEGREE, + ELECTRIC_CURRENT_AMPERE, ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, LIGHT_LUX, - MASS_KILOGRAMS, - MASS_POUNDS, PERCENTAGE, - POWER_WATT, - PRESSURE_MBAR, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + TIME_SECONDS, + UnitOfEnergy, + UnitOfLength, + UnitOfMass, + UnitOfPower, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -43,7 +47,7 @@ SENSOR_DESCRIPTIONS = { (BTHomeSensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), (BTHomeSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( @@ -61,7 +65,7 @@ SENSOR_DESCRIPTIONS = { (BTHomeSensorDeviceClass.PRESSURE, Units.PRESSURE_MBAR): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.PRESSURE}_{Units.PRESSURE_MBAR}", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_MBAR, + native_unit_of_measurement=UnitOfPressure.MBAR, state_class=SensorStateClass.MEASUREMENT, ), (BTHomeSensorDeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription( @@ -86,13 +90,13 @@ SENSOR_DESCRIPTIONS = { ): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.ENERGY}_{Units.ENERGY_KILO_WATT_HOUR}", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), (BTHomeSensorDeviceClass.POWER, Units.POWER_WATT): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.POWER}_{Units.POWER_WATT}", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, ), ( @@ -146,14 +150,14 @@ SENSOR_DESCRIPTIONS = { (BTHomeSensorDeviceClass.MASS, Units.MASS_KILOGRAMS): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.MASS}_{Units.MASS_KILOGRAMS}", device_class=SensorDeviceClass.WEIGHT, - native_unit_of_measurement=MASS_KILOGRAMS, + native_unit_of_measurement=UnitOfMass.KILOGRAMS, state_class=SensorStateClass.MEASUREMENT, ), # Used for mass sensor with lb unit (BTHomeSensorDeviceClass.MASS, Units.MASS_POUNDS): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.MASS}_{Units.MASS_POUNDS}", device_class=SensorDeviceClass.WEIGHT, - native_unit_of_measurement=MASS_POUNDS, + native_unit_of_measurement=UnitOfMass.POUNDS, state_class=SensorStateClass.MEASUREMENT, ), # Used for moisture sensor @@ -167,7 +171,7 @@ SENSOR_DESCRIPTIONS = { (BTHomeSensorDeviceClass.DEW_POINT, Units.TEMP_CELSIUS): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.DEW_POINT}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), # Used for count sensor @@ -176,6 +180,64 @@ SENSOR_DESCRIPTIONS = { device_class=None, state_class=SensorStateClass.MEASUREMENT, ), + # Used for rotation sensor + (BTHomeSensorDeviceClass.ROTATION, Units.DEGREE): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.ROTATION}_{Units.DEGREE}", + device_class=None, + native_unit_of_measurement=DEGREE, + state_class=SensorStateClass.MEASUREMENT, + ), + # Used for distance sensor in mm + ( + BTHomeSensorDeviceClass.DISTANCE, + Units.LENGTH_MILLIMETERS, + ): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.DISTANCE}_{Units.LENGTH_MILLIMETERS}", + device_class=SensorDeviceClass.DISTANCE, + native_unit_of_measurement=UnitOfLength.MILLIMETERS, + state_class=SensorStateClass.MEASUREMENT, + ), + # Used for distance sensor in m + (BTHomeSensorDeviceClass.DISTANCE, Units.LENGTH_METERS): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.DISTANCE}_{Units.LENGTH_METERS}", + device_class=SensorDeviceClass.DISTANCE, + native_unit_of_measurement=UnitOfLength.METERS, + state_class=SensorStateClass.MEASUREMENT, + ), + # Used for duration sensor + (BTHomeSensorDeviceClass.DURATION, Units.TIME_SECONDS): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.DURATION}_{Units.TIME_SECONDS}", + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=TIME_SECONDS, + state_class=SensorStateClass.MEASUREMENT, + ), + # Used for current sensor + ( + BTHomeSensorDeviceClass.CURRENT, + Units.ELECTRIC_CURRENT_AMPERE, + ): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.CURRENT}_{Units.ELECTRIC_CURRENT_AMPERE}", + device_class=SensorDeviceClass.CURRENT, + native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + ), + # Used for speed sensor + ( + BTHomeSensorDeviceClass.SPEED, + Units.SPEED_METERS_PER_SECOND, + ): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.SPEED}_{Units.SPEED_METERS_PER_SECOND}", + device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + state_class=SensorStateClass.MEASUREMENT, + ), + # Used for UV index sensor + (BTHomeSensorDeviceClass.UV_INDEX, None,): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.UV_INDEX}", + device_class=None, + native_unit_of_measurement=None, + state_class=SensorStateClass.MEASUREMENT, + ), } diff --git a/homeassistant/generated/bluetooth.py b/homeassistant/generated/bluetooth.py index 4a0b9529ee7..355340d3ed3 100644 --- a/homeassistant/generated/bluetooth.py +++ b/homeassistant/generated/bluetooth.py @@ -36,6 +36,11 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ "connectable": False, "service_data_uuid": "0000181e-0000-1000-8000-00805f9b34fb", }, + { + "domain": "bthome", + "connectable": False, + "service_data_uuid": "0000fcd2-0000-1000-8000-00805f9b34fb", + }, { "domain": "fjaraskupan", "connectable": False, diff --git a/requirements_all.txt b/requirements_all.txt index d8dab654842..7fea5716280 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -482,7 +482,7 @@ brunt==1.2.0 bt_proximity==0.2.1 # homeassistant.components.bthome -bthome-ble==1.2.2 +bthome-ble==2.2.1 # homeassistant.components.bt_home_hub_5 bthomehub5-devicelist==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 041b8577331..a62c14f197d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -386,7 +386,7 @@ brother==2.0.0 brunt==1.2.0 # homeassistant.components.bthome -bthome-ble==1.2.2 +bthome-ble==2.2.1 # homeassistant.components.buienradar buienradar==1.0.5 diff --git a/tests/components/bthome/__init__.py b/tests/components/bthome/__init__.py index 25ccb72edfa..2951413b0e6 100644 --- a/tests/components/bthome/__init__.py +++ b/tests/components/bthome/__init__.py @@ -85,7 +85,7 @@ NOT_BTHOME_SERVICE_INFO = BluetoothServiceInfoBleak( ) -def make_advertisement(address: str, payload: bytes) -> BluetoothServiceInfoBleak: +def make_bthome_v1_adv(address: str, payload: bytes) -> BluetoothServiceInfoBleak: """Make a dummy advertisement.""" return BluetoothServiceInfoBleak( name="Test Device", @@ -104,7 +104,7 @@ def make_advertisement(address: str, payload: bytes) -> BluetoothServiceInfoBlea ) -def make_encrypted_advertisement( +def make_encrypted_bthome_v1_adv( address: str, payload: bytes ) -> BluetoothServiceInfoBleak: """Make a dummy encrypted advertisement.""" @@ -123,3 +123,22 @@ def make_encrypted_advertisement( time=0, connectable=False, ) + + +def make_bthome_v2_adv(address: str, payload: bytes) -> BluetoothServiceInfoBleak: + """Make a dummy advertisement.""" + return BluetoothServiceInfoBleak( + name="Test Device", + address=address, + device=BLEDevice(address, None), + rssi=-56, + manufacturer_data={}, + service_data={ + "0000fcd2-0000-1000-8000-00805f9b34fb": payload, + }, + service_uuids=["0000fcd2-0000-1000-8000-00805f9b34fb"], + source="local", + advertisement=generate_advertisement_data(local_name="Test Device"), + time=0, + connectable=False, + ) diff --git a/tests/components/bthome/test_binary_sensor.py b/tests/components/bthome/test_binary_sensor.py index 64b19b17a81..99c3b310678 100644 --- a/tests/components/bthome/test_binary_sensor.py +++ b/tests/components/bthome/test_binary_sensor.py @@ -7,7 +7,7 @@ import pytest from homeassistant.components.bthome.const import DOMAIN from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_OFF, STATE_ON -from . import make_advertisement +from . import make_bthome_v1_adv, make_bthome_v2_adv from tests.common import MockConfigEntry from tests.components.bluetooth import inject_bluetooth_service_info @@ -20,7 +20,7 @@ _LOGGER = logging.getLogger(__name__) [ ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x02\x10\x01", ), @@ -35,7 +35,7 @@ _LOGGER = logging.getLogger(__name__) ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x02\x11\x00", ), @@ -50,7 +50,7 @@ _LOGGER = logging.getLogger(__name__) ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x02\x0F\x01", ), @@ -65,14 +65,100 @@ _LOGGER = logging.getLogger(__name__) ), ], ) -async def test_binary_sensors( +async def test_v1_binary_sensors( hass, mac_address, advertisement, bind_key, result, ): - """Test the different binary sensors.""" + """Test the different BTHome v1 binary sensors.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=mac_address, + data={"bindkey": bind_key}, + ) + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 0 + + inject_bluetooth_service_info( + hass, + advertisement, + ) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == len(result) + for meas in result: + binary_sensor = hass.states.get(meas["binary_sensor_entity"]) + binary_sensor_attr = binary_sensor.attributes + assert binary_sensor.state == meas["expected_state"] + assert binary_sensor_attr[ATTR_FRIENDLY_NAME] == meas["friendly_name"] + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + + +@pytest.mark.parametrize( + "mac_address, advertisement, bind_key, result", + [ + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x10\x01", + ), + None, + [ + { + "binary_sensor_entity": "binary_sensor.test_device_18b2_power", + "friendly_name": "Test Device 18B2 Power", + "expected_state": STATE_ON, + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x11\x00", + ), + None, + [ + { + "binary_sensor_entity": "binary_sensor.test_device_18b2_opening", + "friendly_name": "Test Device 18B2 Opening", + "expected_state": STATE_OFF, + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x0F\x01", + ), + None, + [ + { + "binary_sensor_entity": "binary_sensor.test_device_18b2_generic", + "friendly_name": "Test Device 18B2 Generic", + "expected_state": STATE_ON, + }, + ], + ), + ], +) +async def test_v2_binary_sensors( + hass, + mac_address, + advertisement, + bind_key, + result, +): + """Test the different BTHome v2 binary sensors.""" entry = MockConfigEntry( domain=DOMAIN, unique_id=mac_address, diff --git a/tests/components/bthome/test_sensor.py b/tests/components/bthome/test_sensor.py index 78b247aa393..989fff1a25f 100644 --- a/tests/components/bthome/test_sensor.py +++ b/tests/components/bthome/test_sensor.py @@ -1,24 +1,29 @@ """Test the BTHome sensors.""" +import logging + import pytest from homeassistant.components.bthome.const import DOMAIN from homeassistant.components.sensor import ATTR_STATE_CLASS from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT -from . import make_advertisement, make_encrypted_advertisement +from . import make_bthome_v1_adv, make_bthome_v2_adv, make_encrypted_bthome_v1_adv from tests.common import MockConfigEntry from tests.components.bluetooth import inject_bluetooth_service_info +_LOGGER = logging.getLogger(__name__) + +# Tests for BTHome v1 @pytest.mark.parametrize( "mac_address, advertisement, bind_key, result", [ ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"#\x02\xca\t\x03\x03\xbf\x13", ), @@ -42,7 +47,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x02\x00\xa8#\x02]\t\x03\x03\xb7\x18\x02\x01]", ), @@ -73,7 +78,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x02\x00\x0c\x04\x04\x13\x8a\x01", ), @@ -90,7 +95,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "AA:BB:CC:DD:EE:FF", - make_advertisement( + make_bthome_v1_adv( "AA:BB:CC:DD:EE:FF", b"\x04\x05\x13\x8a\x14", ), @@ -107,7 +112,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x03\x06\x5e\x1f", ), @@ -124,7 +129,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x03\x07\x3e\x1d", ), @@ -141,7 +146,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x23\x08\xCA\x06", ), @@ -158,7 +163,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x02\x09\x60", ), @@ -174,7 +179,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x04\n\x13\x8a\x14", ), @@ -191,7 +196,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x04\x0b\x02\x1b\x00", ), @@ -208,7 +213,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x03\x0c\x02\x0c", ), @@ -225,7 +230,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x03\r\x12\x0c\x03\x0e\x02\x1c", ), @@ -249,7 +254,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x03\x12\xe2\x04", ), @@ -266,7 +271,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x03\x133\x01", ), @@ -283,7 +288,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "A4:C1:38:8D:18:B2", - make_advertisement( + make_bthome_v1_adv( "A4:C1:38:8D:18:B2", b"\x03\x14\x02\x0c", ), @@ -300,7 +305,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ( "54:48:E6:8F:80:A5", - make_encrypted_advertisement( + make_encrypted_bthome_v1_adv( "54:48:E6:8F:80:A5", b'\xfb\xa45\xe4\xd3\xc3\x12\xfb\x00\x11"3W\xd9\n\x99', ), @@ -324,14 +329,14 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ], ) -async def test_sensors( +async def test_v1_sensors( hass, mac_address, advertisement, bind_key, result, ): - """Test the different measurement sensors.""" + """Test the different BTHome V1 sensors.""" entry = MockConfigEntry( domain=DOMAIN, unique_id=mac_address, @@ -357,7 +362,572 @@ async def test_sensors( assert sensor.state == meas["expected_state"] assert sensor_attr[ATTR_FRIENDLY_NAME] == meas["friendly_name"] if ATTR_UNIT_OF_MEASUREMENT in sensor_attr: - # Count sensor does not have a unit of measurement + # Some sensors don't have a unit of measurement + assert sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == meas["unit_of_measurement"] + assert sensor_attr[ATTR_STATE_CLASS] == meas["state_class"] + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + + +# Tests for BTHome V2 +@pytest.mark.parametrize( + "mac_address, advertisement, bind_key, result", + [ + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x02\xca\x09\x03\xbf\x13", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_temperature", + "friendly_name": "Test Device 18B2 Temperature", + "unit_of_measurement": "°C", + "state_class": "measurement", + "expected_state": "25.06", + }, + { + "sensor_entity": "sensor.test_device_18b2_humidity", + "friendly_name": "Test Device 18B2 Humidity", + "unit_of_measurement": "%", + "state_class": "measurement", + "expected_state": "50.55", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x01\x5d\x02\x5d\x09\x03\xb7\x18", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_temperature", + "friendly_name": "Test Device 18B2 Temperature", + "unit_of_measurement": "°C", + "state_class": "measurement", + "expected_state": "23.97", + }, + { + "sensor_entity": "sensor.test_device_18b2_humidity", + "friendly_name": "Test Device 18B2 Humidity", + "unit_of_measurement": "%", + "state_class": "measurement", + "expected_state": "63.27", + }, + { + "sensor_entity": "sensor.test_device_18b2_battery", + "friendly_name": "Test Device 18B2 Battery", + "unit_of_measurement": "%", + "state_class": "measurement", + "expected_state": "93", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x04\x13\x8a\x01", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_pressure", + "friendly_name": "Test Device 18B2 Pressure", + "unit_of_measurement": "mbar", + "state_class": "measurement", + "expected_state": "1008.83", + }, + ], + ), + ( + "AA:BB:CC:DD:EE:FF", + make_bthome_v2_adv( + "AA:BB:CC:DD:EE:FF", + b"\x40\x05\x13\x8a\x14", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_eeff_illuminance", + "friendly_name": "Test Device EEFF Illuminance", + "unit_of_measurement": "lx", + "state_class": "measurement", + "expected_state": "13460.67", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x06\x5E\x1F", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_mass", + "friendly_name": "Test Device 18B2 Mass", + "unit_of_measurement": "kg", + "state_class": "measurement", + "expected_state": "80.3", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x07\x3E\x1d", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_mass", + "friendly_name": "Test Device 18B2 Mass", + "unit_of_measurement": "lb", + "state_class": "measurement", + "expected_state": "74.86", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x08\xCA\x06", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_dew_point", + "friendly_name": "Test Device 18B2 Dew Point", + "unit_of_measurement": "°C", + "state_class": "measurement", + "expected_state": "17.38", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x09\x60", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_count", + "friendly_name": "Test Device 18B2 Count", + "state_class": "measurement", + "expected_state": "96", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x0a\x13\x8a\x14", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_energy", + "friendly_name": "Test Device 18B2 Energy", + "unit_of_measurement": "kWh", + "state_class": "total_increasing", + "expected_state": "1346.067", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x0b\x02\x1b\x00", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_power", + "friendly_name": "Test Device 18B2 Power", + "unit_of_measurement": "W", + "state_class": "measurement", + "expected_state": "69.14", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x0c\x02\x0c", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_voltage", + "friendly_name": "Test Device 18B2 Voltage", + "unit_of_measurement": "V", + "state_class": "measurement", + "expected_state": "3.074", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x0d\x12\x0c\x0e\x02\x1c", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_pm10", + "friendly_name": "Test Device 18B2 Pm10", + "unit_of_measurement": "µg/m³", + "state_class": "measurement", + "expected_state": "7170", + }, + { + "sensor_entity": "sensor.test_device_18b2_pm25", + "friendly_name": "Test Device 18B2 Pm25", + "unit_of_measurement": "µg/m³", + "state_class": "measurement", + "expected_state": "3090", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x12\xe2\x04", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_carbon_dioxide", + "friendly_name": "Test Device 18B2 Carbon Dioxide", + "unit_of_measurement": "ppm", + "state_class": "measurement", + "expected_state": "1250", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x133\x01", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_volatile_organic_compounds", + "friendly_name": "Test Device 18B2 Volatile Organic Compounds", + "unit_of_measurement": "µg/m³", + "state_class": "measurement", + "expected_state": "307", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x14\x02\x0c", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_moisture", + "friendly_name": "Test Device 18B2 Moisture", + "unit_of_measurement": "%", + "state_class": "measurement", + "expected_state": "30.74", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x3F\x02\x0c", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_rotation", + "friendly_name": "Test Device 18B2 Rotation", + "unit_of_measurement": "°", + "state_class": "measurement", + "expected_state": "307.4", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x40\x0C\x00", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_distance", + "friendly_name": "Test Device 18B2 Distance", + "unit_of_measurement": "mm", + "state_class": "measurement", + "expected_state": "12", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x41\x4E\x00", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_distance", + "friendly_name": "Test Device 18B2 Distance", + "unit_of_measurement": "m", + "state_class": "measurement", + "expected_state": "7.8", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x42\x4E\x34\x00", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_duration", + "friendly_name": "Test Device 18B2 Duration", + "unit_of_measurement": "s", + "state_class": "measurement", + "expected_state": "13.39", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x43\x4E\x34", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_current", + "friendly_name": "Test Device 18B2 Current", + "unit_of_measurement": "A", + "state_class": "measurement", + "expected_state": "13.39", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x44\x4E\x34", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_speed", + "friendly_name": "Test Device 18B2 Speed", + "unit_of_measurement": "m/s", + "state_class": "measurement", + "expected_state": "133.9", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x45\x11\x01", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_temperature", + "friendly_name": "Test Device 18B2 Temperature", + "unit_of_measurement": "°C", + "state_class": "measurement", + "expected_state": "27.3", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x46\x32", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_uv_index", + "friendly_name": "Test Device 18B2 Uv Index", + "state_class": "measurement", + "expected_state": "5.0", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x02\xca\x09\x02\xcf\x09", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_temperature_1", + "friendly_name": "Test Device 18B2 Temperature 1", + "unit_of_measurement": "°C", + "state_class": "measurement", + "expected_state": "25.06", + }, + { + "sensor_entity": "sensor.test_device_18b2_temperature_2", + "friendly_name": "Test Device 18B2 Temperature 2", + "unit_of_measurement": "°C", + "state_class": "measurement", + "expected_state": "25.11", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x02\xca\x09\x02\xcf\x09\x02\xcf\x08\x03\xb7\x18\x03\xb7\x17\x01\x5d", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_temperature_1", + "friendly_name": "Test Device 18B2 Temperature 1", + "unit_of_measurement": "°C", + "state_class": "measurement", + "expected_state": "25.06", + }, + { + "sensor_entity": "sensor.test_device_18b2_temperature_2", + "friendly_name": "Test Device 18B2 Temperature 2", + "unit_of_measurement": "°C", + "state_class": "measurement", + "expected_state": "25.11", + }, + { + "sensor_entity": "sensor.test_device_18b2_temperature_3", + "friendly_name": "Test Device 18B2 Temperature 3", + "unit_of_measurement": "°C", + "state_class": "measurement", + "expected_state": "22.55", + }, + { + "sensor_entity": "sensor.test_device_18b2_humidity_1", + "friendly_name": "Test Device 18B2 Humidity 1", + "unit_of_measurement": "%", + "state_class": "measurement", + "expected_state": "63.27", + }, + { + "sensor_entity": "sensor.test_device_18b2_humidity_2", + "friendly_name": "Test Device 18B2 Humidity 2", + "unit_of_measurement": "%", + "state_class": "measurement", + "expected_state": "60.71", + }, + { + "sensor_entity": "sensor.test_device_18b2_battery", + "friendly_name": "Test Device 18B2 Battery", + "unit_of_measurement": "%", + "state_class": "measurement", + "expected_state": "93", + }, + ], + ), + ( + "54:48:E6:8F:80:A5", + make_bthome_v2_adv( + "54:48:E6:8F:80:A5", + b"\x41\xa4\x72\x66\xc9\x5f\x73\x00\x11\x22\x33\xb7\xce\xd8\xe5", + ), + "231d39c1d7cc1ab1aee224cd096db932", + [ + { + "sensor_entity": "sensor.test_device_80a5_temperature", + "friendly_name": "Test Device 80A5 Temperature", + "unit_of_measurement": "°C", + "state_class": "measurement", + "expected_state": "25.06", + }, + { + "sensor_entity": "sensor.test_device_80a5_humidity", + "friendly_name": "Test Device 80A5 Humidity", + "unit_of_measurement": "%", + "state_class": "measurement", + "expected_state": "50.55", + }, + ], + ), + ], +) +async def test_v2_sensors( + hass, + mac_address, + advertisement, + bind_key, + result, +): + """Test the different BTHome V2 sensors.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=mac_address, + data={"bindkey": bind_key}, + ) + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 0 + + inject_bluetooth_service_info( + hass, + advertisement, + ) + await hass.async_block_till_done() + assert len(hass.states.async_all()) == len(result) + + for meas in result: + _LOGGER.error(meas) + sensor = hass.states.get(meas["sensor_entity"]) + _LOGGER.error(hass.states) + sensor_attr = sensor.attributes + assert sensor.state == meas["expected_state"] + assert sensor_attr[ATTR_FRIENDLY_NAME] == meas["friendly_name"] + if ATTR_UNIT_OF_MEASUREMENT in sensor_attr: + # Some sensors don't have a unit of measurement assert sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == meas["unit_of_measurement"] assert sensor_attr[ATTR_STATE_CLASS] == meas["state_class"] assert await hass.config_entries.async_unload(entry.entry_id) From ec316e94ed6519e24b3274d4626b4bd8b431be2f Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 9 Nov 2022 16:35:30 +0200 Subject: [PATCH 0324/1033] RuuviTag BLE sensor support (#81327) --- .strict-typing | 1 + CODEOWNERS | 2 + .../components/ruuvitag_ble/__init__.py | 49 +++++ .../components/ruuvitag_ble/config_flow.py | 94 ++++++++ .../components/ruuvitag_ble/const.py | 3 + .../components/ruuvitag_ble/manifest.json | 18 ++ .../components/ruuvitag_ble/sensor.py | 165 ++++++++++++++ homeassistant/generated/bluetooth.py | 8 + homeassistant/generated/config_flows.py | 1 + homeassistant/generated/integrations.json | 6 + mypy.ini | 10 + requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/ruuvitag_ble/__init__.py | 1 + tests/components/ruuvitag_ble/fixtures.py | 26 +++ .../ruuvitag_ble/test_config_flow.py | 202 ++++++++++++++++++ tests/components/ruuvitag_ble/test_sensor.py | 46 ++++ 17 files changed, 638 insertions(+) create mode 100644 homeassistant/components/ruuvitag_ble/__init__.py create mode 100644 homeassistant/components/ruuvitag_ble/config_flow.py create mode 100644 homeassistant/components/ruuvitag_ble/const.py create mode 100644 homeassistant/components/ruuvitag_ble/manifest.json create mode 100644 homeassistant/components/ruuvitag_ble/sensor.py create mode 100644 tests/components/ruuvitag_ble/__init__.py create mode 100644 tests/components/ruuvitag_ble/fixtures.py create mode 100644 tests/components/ruuvitag_ble/test_config_flow.py create mode 100644 tests/components/ruuvitag_ble/test_sensor.py diff --git a/.strict-typing b/.strict-typing index 330424faec7..67b5d44b97b 100644 --- a/.strict-typing +++ b/.strict-typing @@ -229,6 +229,7 @@ homeassistant.components.rituals_perfume_genie.* homeassistant.components.roku.* homeassistant.components.rpi_power.* homeassistant.components.rtsp_to_webrtc.* +homeassistant.components.ruuvitag_ble.* homeassistant.components.samsungtv.* homeassistant.components.scene.* homeassistant.components.schedule.* diff --git a/CODEOWNERS b/CODEOWNERS index db01573e808..1f45098e4f8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -964,6 +964,8 @@ build.json @home-assistant/supervisor /tests/components/rtsp_to_webrtc/ @allenporter /homeassistant/components/ruckus_unleashed/ @gabe565 /tests/components/ruckus_unleashed/ @gabe565 +/homeassistant/components/ruuvitag_ble/ @akx +/tests/components/ruuvitag_ble/ @akx /homeassistant/components/sabnzbd/ @shaiu /tests/components/sabnzbd/ @shaiu /homeassistant/components/safe_mode/ @home-assistant/core diff --git a/homeassistant/components/ruuvitag_ble/__init__.py b/homeassistant/components/ruuvitag_ble/__init__.py new file mode 100644 index 00000000000..5e30820f837 --- /dev/null +++ b/homeassistant/components/ruuvitag_ble/__init__.py @@ -0,0 +1,49 @@ +"""The ruuvitag_ble integration.""" +from __future__ import annotations + +import logging + +from ruuvitag_ble import RuuvitagBluetoothDeviceData + +from homeassistant.components.bluetooth import BluetoothScanningMode +from homeassistant.components.bluetooth.passive_update_processor import ( + PassiveBluetoothProcessorCoordinator, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant + +from .const import DOMAIN + +PLATFORMS: list[Platform] = [Platform.SENSOR] + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Ruuvitag BLE device from a config entry.""" + address = entry.unique_id + assert address is not None + data = RuuvitagBluetoothDeviceData() + coordinator = hass.data.setdefault(DOMAIN, {})[ + entry.entry_id + ] = PassiveBluetoothProcessorCoordinator( + hass, + _LOGGER, + address=address, + mode=BluetoothScanningMode.ACTIVE, + update_method=data.update, + ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + entry.async_on_unload( + coordinator.async_start() + ) # only start after all platforms have had a chance to subscribe + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_ok diff --git a/homeassistant/components/ruuvitag_ble/config_flow.py b/homeassistant/components/ruuvitag_ble/config_flow.py new file mode 100644 index 00000000000..620b901f4fe --- /dev/null +++ b/homeassistant/components/ruuvitag_ble/config_flow.py @@ -0,0 +1,94 @@ +"""Config flow for ruuvitag_ble.""" +from __future__ import annotations + +from typing import Any + +from ruuvitag_ble import RuuvitagBluetoothDeviceData +import voluptuous as vol + +from homeassistant.components.bluetooth import ( + BluetoothServiceInfoBleak, + async_discovered_service_info, +) +from homeassistant.config_entries import ConfigFlow +from homeassistant.const import CONF_ADDRESS +from homeassistant.data_entry_flow import FlowResult + +from .const import DOMAIN + + +class RuuvitagConfigFlow(ConfigFlow, domain=DOMAIN): + """Handle a config flow for ruuvitag_ble.""" + + VERSION = 1 + + def __init__(self) -> None: + """Initialize the config flow.""" + self._discovery_info: BluetoothServiceInfoBleak | None = None + self._discovered_device: RuuvitagBluetoothDeviceData | None = None + self._discovered_devices: dict[str, str] = {} + + async def async_step_bluetooth( + self, discovery_info: BluetoothServiceInfoBleak + ) -> FlowResult: + """Handle the bluetooth discovery step.""" + await self.async_set_unique_id(discovery_info.address) + self._abort_if_unique_id_configured() + device = RuuvitagBluetoothDeviceData() + if not device.supported(discovery_info): + return self.async_abort(reason="not_supported") + self._discovery_info = discovery_info + self._discovered_device = device + return await self.async_step_bluetooth_confirm() + + async def async_step_bluetooth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Confirm discovery.""" + assert self._discovered_device is not None + device = self._discovered_device + assert self._discovery_info is not None + discovery_info = self._discovery_info + title = device.title or device.get_device_name() or discovery_info.name + if user_input is not None: + return self.async_create_entry(title=title, data={}) + + self._set_confirm_only() + placeholders = {"name": title} + self.context["title_placeholders"] = placeholders + return self.async_show_form( + step_id="bluetooth_confirm", description_placeholders=placeholders + ) + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the user step to pick discovered device.""" + if user_input is not None: + address = user_input[CONF_ADDRESS] + await self.async_set_unique_id(address, raise_on_progress=False) + self._abort_if_unique_id_configured() + return self.async_create_entry( + title=self._discovered_devices[address], data={} + ) + + current_addresses = self._async_current_ids() + for discovery_info in async_discovered_service_info(self.hass, False): + address = discovery_info.address + if address in current_addresses or address in self._discovered_devices: + continue + device = RuuvitagBluetoothDeviceData() + if device.supported(discovery_info): + self._discovered_devices[address] = ( + device.title or device.get_device_name() or discovery_info.name + ) + + if not self._discovered_devices: + return self.async_abort(reason="no_devices_found") + + return self.async_show_form( + step_id="user", + data_schema=vol.Schema( + {vol.Required(CONF_ADDRESS): vol.In(self._discovered_devices)} + ), + ) diff --git a/homeassistant/components/ruuvitag_ble/const.py b/homeassistant/components/ruuvitag_ble/const.py new file mode 100644 index 00000000000..0df74a24eea --- /dev/null +++ b/homeassistant/components/ruuvitag_ble/const.py @@ -0,0 +1,3 @@ +"""Constants for the ruuvitag_ble integration.""" + +DOMAIN = "ruuvitag_ble" diff --git a/homeassistant/components/ruuvitag_ble/manifest.json b/homeassistant/components/ruuvitag_ble/manifest.json new file mode 100644 index 00000000000..a3500fca7c6 --- /dev/null +++ b/homeassistant/components/ruuvitag_ble/manifest.json @@ -0,0 +1,18 @@ +{ + "domain": "ruuvitag_ble", + "name": "RuuviTag BLE", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/ruuvitag_ble", + "bluetooth": [ + { + "manufacturer_id": 1177 + }, + { + "local_name": "Ruuvi *" + } + ], + "requirements": ["ruuvitag-ble==0.1.1"], + "dependencies": ["bluetooth"], + "codeowners": ["@akx"], + "iot_class": "local_push" +} diff --git a/homeassistant/components/ruuvitag_ble/sensor.py b/homeassistant/components/ruuvitag_ble/sensor.py new file mode 100644 index 00000000000..463d6da2de2 --- /dev/null +++ b/homeassistant/components/ruuvitag_ble/sensor.py @@ -0,0 +1,165 @@ +"""Support for RuuviTag sensors.""" +from __future__ import annotations + +from typing import Optional, Union + +from sensor_state_data import ( + DeviceKey, + SensorDescription, + SensorDeviceClass, + SensorDeviceInfo, + SensorUpdate, + Units, +) + +from homeassistant import config_entries, const +from homeassistant.components.bluetooth.passive_update_processor import ( + PassiveBluetoothDataProcessor, + PassiveBluetoothDataUpdate, + PassiveBluetoothEntityKey, + PassiveBluetoothProcessorCoordinator, + PassiveBluetoothProcessorEntity, +) +from homeassistant.components.sensor import ( + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN + +SENSOR_DESCRIPTIONS = { + (SensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( + key=f"{SensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=const.TEMP_CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + (SensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( + key=f"{SensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}", + device_class=SensorDeviceClass.HUMIDITY, + native_unit_of_measurement=const.PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + (SensorDeviceClass.PRESSURE, Units.PRESSURE_HPA): SensorEntityDescription( + key=f"{SensorDeviceClass.PRESSURE}_{Units.PRESSURE_HPA}", + device_class=SensorDeviceClass.PRESSURE, + native_unit_of_measurement=const.PRESSURE_HPA, + state_class=SensorStateClass.MEASUREMENT, + ), + ( + SensorDeviceClass.VOLTAGE, + Units.ELECTRIC_POTENTIAL_MILLIVOLT, + ): SensorEntityDescription( + key=f"{SensorDeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_MILLIVOLT}", + device_class=SensorDeviceClass.VOLTAGE, + native_unit_of_measurement=const.ELECTRIC_POTENTIAL_MILLIVOLT, + state_class=SensorStateClass.MEASUREMENT, + ), + ( + SensorDeviceClass.SIGNAL_STRENGTH, + Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + ): SensorEntityDescription( + key=f"{SensorDeviceClass.SIGNAL_STRENGTH}_{Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT}", + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + native_unit_of_measurement=const.SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + ), + (SensorDeviceClass.COUNT, None): SensorEntityDescription( + key="movement_counter", + device_class=SensorDeviceClass.COUNT, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + ), +} + + +def _device_key_to_bluetooth_entity_key( + device_key: DeviceKey, +) -> PassiveBluetoothEntityKey: + """Convert a device key to an entity key.""" + return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) + + +def _sensor_device_info_to_hass( + sensor_device_info: SensorDeviceInfo, +) -> DeviceInfo: + """Convert a sensor device info to a sensor device info.""" + hass_device_info = DeviceInfo() + if sensor_device_info.name is not None: + hass_device_info[const.ATTR_NAME] = sensor_device_info.name + if sensor_device_info.manufacturer is not None: + hass_device_info[const.ATTR_MANUFACTURER] = sensor_device_info.manufacturer + if sensor_device_info.model is not None: + hass_device_info[const.ATTR_MODEL] = sensor_device_info.model + return hass_device_info + + +def _to_sensor_key( + description: SensorDescription, +) -> tuple[SensorDeviceClass, Units | None]: + assert description.device_class is not None + return (description.device_class, description.native_unit_of_measurement) + + +def sensor_update_to_bluetooth_data_update( + sensor_update: SensorUpdate, +) -> PassiveBluetoothDataUpdate: + """Convert a sensor update to a bluetooth data update.""" + return PassiveBluetoothDataUpdate( + devices={ + device_id: _sensor_device_info_to_hass(device_info) + for device_id, device_info in sensor_update.devices.items() + }, + entity_descriptions={ + _device_key_to_bluetooth_entity_key(device_key): SENSOR_DESCRIPTIONS[ + _to_sensor_key(description) + ] + for device_key, description in sensor_update.entity_descriptions.items() + if _to_sensor_key(description) in SENSOR_DESCRIPTIONS + }, + entity_data={ + _device_key_to_bluetooth_entity_key(device_key): sensor_values.native_value + for device_key, sensor_values in sensor_update.entity_values.items() + }, + entity_names={ + _device_key_to_bluetooth_entity_key(device_key): sensor_values.name + for device_key, sensor_values in sensor_update.entity_values.items() + }, + ) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: config_entries.ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Ruuvitag BLE sensors.""" + coordinator: PassiveBluetoothProcessorCoordinator = hass.data[DOMAIN][ + entry.entry_id + ] + processor = PassiveBluetoothDataProcessor(sensor_update_to_bluetooth_data_update) + entry.async_on_unload( + processor.async_add_entities_listener( + RuuvitagBluetoothSensorEntity, async_add_entities + ) + ) + entry.async_on_unload(coordinator.async_register_processor(processor)) + + +class RuuvitagBluetoothSensorEntity( + PassiveBluetoothProcessorEntity[ + PassiveBluetoothDataProcessor[Optional[Union[float, int]]] + ], + SensorEntity, +): + """Representation of a Ruuvitag BLE sensor.""" + + @property + def native_value(self) -> int | float | None: + """Return the native value.""" + return self.processor.entity_data.get(self.entity_key) diff --git a/homeassistant/generated/bluetooth.py b/homeassistant/generated/bluetooth.py index 355340d3ed3..6fa2342b5a4 100644 --- a/homeassistant/generated/bluetooth.py +++ b/homeassistant/generated/bluetooth.py @@ -263,6 +263,14 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ "service_data_uuid": "0000fdcd-0000-1000-8000-00805f9b34fb", "connectable": False, }, + { + "domain": "ruuvitag_ble", + "manufacturer_id": 1177, + }, + { + "domain": "ruuvitag_ble", + "local_name": "Ruuvi *", + }, { "domain": "sensorpro", "manufacturer_id": 43605, diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 05e352ab2b7..46aefeebb22 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -334,6 +334,7 @@ FLOWS = { "rpi_power", "rtsp_to_webrtc", "ruckus_unleashed", + "ruuvitag_ble", "sabnzbd", "samsungtv", "screenlogic", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index bbb7928f68f..ed1aa0a3647 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -4475,6 +4475,12 @@ } } }, + "ruuvitag_ble": { + "name": "RuuviTag BLE", + "integration_type": "hub", + "config_flow": true, + "iot_class": "local_push" + }, "sabnzbd": { "name": "SABnzbd", "integration_type": "hub", diff --git a/mypy.ini b/mypy.ini index 988701393d6..b30d64163bc 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2043,6 +2043,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.ruuvitag_ble.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.samsungtv.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/requirements_all.txt b/requirements_all.txt index 7fea5716280..c9106d2ce39 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2217,6 +2217,9 @@ russound==0.1.9 # homeassistant.components.russound_rio russound_rio==0.1.8 +# homeassistant.components.ruuvitag_ble +ruuvitag-ble==0.1.1 + # homeassistant.components.yamaha rxv==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a62c14f197d..65966609ecc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1532,6 +1532,9 @@ rpi-bad-power==0.1.0 # homeassistant.components.rtsp_to_webrtc rtsp-to-webrtc==0.5.1 +# homeassistant.components.ruuvitag_ble +ruuvitag-ble==0.1.1 + # homeassistant.components.yamaha rxv==0.7.0 diff --git a/tests/components/ruuvitag_ble/__init__.py b/tests/components/ruuvitag_ble/__init__.py new file mode 100644 index 00000000000..e39900689cc --- /dev/null +++ b/tests/components/ruuvitag_ble/__init__.py @@ -0,0 +1 @@ +"""Test package for RuuviTag BLE sensor integration.""" diff --git a/tests/components/ruuvitag_ble/fixtures.py b/tests/components/ruuvitag_ble/fixtures.py new file mode 100644 index 00000000000..26eee1bac5e --- /dev/null +++ b/tests/components/ruuvitag_ble/fixtures.py @@ -0,0 +1,26 @@ +"""Fixtures for testing RuuviTag BLE.""" +from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo + +NOT_RUUVITAG_SERVICE_INFO = BluetoothServiceInfo( + name="Not it", + address="61DE521B-F0BF-9F44-64D4-75BBE1738105", + rssi=-63, + manufacturer_data={3234: b"\x00\x01"}, + service_data={}, + service_uuids=[], + source="local", +) + +RUUVITAG_SERVICE_INFO = BluetoothServiceInfo( + name="RuuviTag 0911", + address="01:03:05:07:09:11", # Ignored (the payload encodes the correct MAC) + rssi=-60, + manufacturer_data={ + 1177: b"\x05\x05\xa0`\xa0\xc8\x9a\xfd4\x02\x8c\xff\x00cvriv\xde\xad{?\xef\xaf" + }, + service_data={}, + service_uuids=[], + source="local", +) +CONFIGURED_NAME = "RuuviTag EFAF" +CONFIGURED_PREFIX = "ruuvitag_efaf" diff --git a/tests/components/ruuvitag_ble/test_config_flow.py b/tests/components/ruuvitag_ble/test_config_flow.py new file mode 100644 index 00000000000..1482f9b61b0 --- /dev/null +++ b/tests/components/ruuvitag_ble/test_config_flow.py @@ -0,0 +1,202 @@ +"""Test the Ruuvitag config flow.""" + +from unittest.mock import patch + +import pytest + +from homeassistant import config_entries +from homeassistant.components.ruuvitag_ble.const import DOMAIN +from homeassistant.data_entry_flow import FlowResultType + +from .fixtures import CONFIGURED_NAME, NOT_RUUVITAG_SERVICE_INFO, RUUVITAG_SERVICE_INFO + +from tests.common import MockConfigEntry + + +@pytest.fixture(autouse=True) +def mock_bluetooth(enable_bluetooth): + """Mock bluetooth for all tests in this module.""" + + +async def test_async_step_bluetooth_valid_device(hass): + """Test discovery via bluetooth with a valid device.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=RUUVITAG_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "bluetooth_confirm" + with patch( + "homeassistant.components.ruuvitag_ble.async_setup_entry", return_value=True + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={} + ) + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == CONFIGURED_NAME + assert result2["result"].unique_id == RUUVITAG_SERVICE_INFO.address + + +async def test_async_step_bluetooth_not_ruuvitag(hass): + """Test discovery via bluetooth not ruuvitag.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=NOT_RUUVITAG_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "not_supported" + + +async def test_async_step_user_no_devices_found(hass): + """Test setup from service info cache with no devices found.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "no_devices_found" + + +async def test_async_step_user_with_found_devices(hass): + """Test setup from service info cache with devices found.""" + with patch( + "homeassistant.components.ruuvitag_ble.config_flow.async_discovered_service_info", + return_value=[RUUVITAG_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + with patch( + "homeassistant.components.ruuvitag_ble.async_setup_entry", return_value=True + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"address": RUUVITAG_SERVICE_INFO.address}, + ) + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == CONFIGURED_NAME + assert result2["result"].unique_id == RUUVITAG_SERVICE_INFO.address + + +async def test_async_step_user_device_added_between_steps(hass): + """Test the device gets added via another flow between steps.""" + with patch( + "homeassistant.components.ruuvitag_ble.config_flow.async_discovered_service_info", + return_value=[RUUVITAG_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=RUUVITAG_SERVICE_INFO.address, + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.ruuvitag_ble.async_setup_entry", return_value=True + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"address": RUUVITAG_SERVICE_INFO.address}, + ) + assert result2["type"] == FlowResultType.ABORT + assert result2["reason"] == "already_configured" + + +async def test_async_step_user_with_found_devices_already_setup(hass): + """Test setup from service info cache with devices found.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=RUUVITAG_SERVICE_INFO.address, + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.ruuvitag_ble.config_flow.async_discovered_service_info", + return_value=[RUUVITAG_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "no_devices_found" + + +async def test_async_step_bluetooth_devices_already_setup(hass): + """Test we can't start a flow if there is already a config entry.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=RUUVITAG_SERVICE_INFO.address, + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=RUUVITAG_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_configured" + + +async def test_async_step_bluetooth_already_in_progress(hass): + """Test we can't start a flow for the same device twice.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=RUUVITAG_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "bluetooth_confirm" + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=RUUVITAG_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_in_progress" + + +async def test_async_step_user_takes_precedence_over_discovery(hass): + """Test manual setup takes precedence over discovery.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=RUUVITAG_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "bluetooth_confirm" + + with patch( + "homeassistant.components.ruuvitag_ble.config_flow.async_discovered_service_info", + return_value=[RUUVITAG_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.FORM + + with patch( + "homeassistant.components.ruuvitag_ble.async_setup_entry", return_value=True + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"address": RUUVITAG_SERVICE_INFO.address}, + ) + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == CONFIGURED_NAME + assert result2["data"] == {} + assert result2["result"].unique_id == RUUVITAG_SERVICE_INFO.address diff --git a/tests/components/ruuvitag_ble/test_sensor.py b/tests/components/ruuvitag_ble/test_sensor.py new file mode 100644 index 00000000000..2f9c027293a --- /dev/null +++ b/tests/components/ruuvitag_ble/test_sensor.py @@ -0,0 +1,46 @@ +"""Test the Ruuvitag BLE sensors.""" + +from __future__ import annotations + +from homeassistant.components.ruuvitag_ble.const import DOMAIN +from homeassistant.components.sensor import ATTR_STATE_CLASS +from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT +from homeassistant.core import HomeAssistant + +from .fixtures import CONFIGURED_NAME, CONFIGURED_PREFIX, RUUVITAG_SERVICE_INFO + +from tests.common import MockConfigEntry +from tests.components.bluetooth import inject_bluetooth_service_info + + +async def test_sensors(enable_bluetooth, hass: HomeAssistant): + """Test the RuuviTag BLE sensors.""" + entry = MockConfigEntry(domain=DOMAIN, unique_id=RUUVITAG_SERVICE_INFO.address) + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 0 + inject_bluetooth_service_info( + hass, + RUUVITAG_SERVICE_INFO, + ) + await hass.async_block_till_done() + assert len(hass.states.async_all()) >= 4 + + for sensor, value, unit, state_class in [ + ("temperature", "7.2", "°C", "measurement"), + ("humidity", "61.84", "%", "measurement"), + ("pressure", "1013.54", "hPa", "measurement"), + ("voltage", "2395", "mV", "measurement"), + ]: + state = hass.states.get(f"sensor.{CONFIGURED_PREFIX}_{sensor}") + assert state is not None + assert state.state == value + name_lower = state.attributes[ATTR_FRIENDLY_NAME].lower() + assert name_lower == f"{CONFIGURED_NAME} {sensor}".lower() + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == unit + assert state.attributes[ATTR_STATE_CLASS] == state_class + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() From 5a6f7e66cb6fcdf324820c14dab34d7bdbf57946 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 9 Nov 2022 16:36:03 +0200 Subject: [PATCH 0325/1033] Refactor + strictly-type image component (#81808) * image: refactor size validation to use partition * image: give _generate_thumbnail types and use partition * image: become strictly typed --- .strict-typing | 1 + homeassistant/components/image/__init__.py | 72 ++++++++++++++++------ mypy.ini | 10 +++ 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/.strict-typing b/.strict-typing index 67b5d44b97b..d45fe269638 100644 --- a/.strict-typing +++ b/.strict-typing @@ -151,6 +151,7 @@ homeassistant.components.http.* homeassistant.components.huawei_lte.* homeassistant.components.hyperion.* homeassistant.components.ibeacon.* +homeassistant.components.image.* homeassistant.components.image_processing.* homeassistant.components.input_button.* homeassistant.components.input_select.* diff --git a/homeassistant/components/image/__init__.py b/homeassistant/components/image/__init__.py index 51263e38ab7..23ab393aabd 100644 --- a/homeassistant/components/image/__init__.py +++ b/homeassistant/components/image/__init__.py @@ -6,6 +6,7 @@ import logging import pathlib import secrets import shutil +from typing import Any from PIL import Image, ImageOps, UnidentifiedImageError from aiohttp import hdrs, web @@ -71,7 +72,7 @@ class ImageStorageCollection(collection.StorageCollection): self.async_add_listener(self._change_listener) self.image_dir = image_dir - async def _process_create_data(self, data: dict) -> dict: + async def _process_create_data(self, data: dict[str, Any]) -> dict[str, Any]: """Validate the config is valid.""" data = self.CREATE_SCHEMA(dict(data)) uploaded_file: FileField = data["file"] @@ -88,7 +89,7 @@ class ImageStorageCollection(collection.StorageCollection): return data - def _move_data(self, data): + def _move_data(self, data: dict[str, Any]) -> int: """Move data.""" uploaded_file: FileField = data.pop("file") @@ -119,15 +120,24 @@ class ImageStorageCollection(collection.StorageCollection): return media_file.stat().st_size @callback - def _get_suggested_id(self, info: dict) -> str: + def _get_suggested_id(self, info: dict[str, Any]) -> str: """Suggest an ID based on the config.""" - return info[CONF_ID] + return str(info[CONF_ID]) - async def _update_data(self, data: dict, update_data: dict) -> dict: + async def _update_data( + self, + data: dict[str, Any], + update_data: dict[str, Any], + ) -> dict[str, Any]: """Return a new updated data object.""" return {**data, **self.UPDATE_SCHEMA(update_data)} - async def _change_listener(self, change_type, item_id, data): + async def _change_listener( + self, + change_type: str, + item_id: str, + data: dict[str, Any], + ) -> None: """Handle change.""" if change_type != collection.CHANGE_REMOVED: return @@ -141,7 +151,7 @@ class ImageUploadView(HomeAssistantView): url = "/api/image/upload" name = "api:image:upload" - async def post(self, request): + async def post(self, request: web.Request) -> web.Response: """Handle upload.""" # Increase max payload request._client_max_size = MAX_SIZE # pylint: disable=protected-access @@ -159,26 +169,27 @@ class ImageServeView(HomeAssistantView): requires_auth = False def __init__( - self, image_folder: pathlib.Path, image_collection: ImageStorageCollection + self, + image_folder: pathlib.Path, + image_collection: ImageStorageCollection, ) -> None: """Initialize image serve view.""" self.transform_lock = asyncio.Lock() self.image_folder = image_folder self.image_collection = image_collection - async def get(self, request: web.Request, image_id: str, filename: str): + async def get( + self, + request: web.Request, + image_id: str, + filename: str, + ) -> web.FileResponse: """Serve image.""" - image_size = filename.split("-", 1)[0] try: - parts = image_size.split("x", 1) - width = int(parts[0]) - height = int(parts[1]) + width, height = _validate_size_from_filename(filename) except (ValueError, IndexError) as err: raise web.HTTPBadRequest from err - if not width or width != height or width not in VALID_SIZES: - raise web.HTTPBadRequest - image_info = self.image_collection.data.get(image_id) if image_info is None: @@ -205,8 +216,33 @@ class ImageServeView(HomeAssistantView): ) -def _generate_thumbnail(original_path, content_type, target_path, target_size): +def _generate_thumbnail( + original_path: pathlib.Path, + content_type: str, + target_path: pathlib.Path, + target_size: tuple[int, int], +) -> None: """Generate a size.""" image = ImageOps.exif_transpose(Image.open(original_path)) image.thumbnail(target_size) - image.save(target_path, format=content_type.split("/", 1)[1]) + image.save(target_path, format=content_type.partition("/")[-1]) + + +def _validate_size_from_filename(filename: str) -> tuple[int, int]: + """Parse image size from the given filename (of the form WIDTHxHEIGHT-filename). + + >>> _validate_size_from_filename("100x100-image.png") + (100, 100) + >>> _validate_size_from_filename("jeff.png") + Traceback (most recent call last): + ... + """ + image_size = filename.partition("-")[0] + if not image_size: + raise ValueError("Invalid filename") + width_s, _, height_s = image_size.partition("x") + width = int(width_s) + height = int(height_s) + if not width or width != height or width not in VALID_SIZES: + raise ValueError(f"Invalid size {image_size}") + return (width, height) diff --git a/mypy.ini b/mypy.ini index b30d64163bc..e30a78dab8c 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1263,6 +1263,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.image.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.image_processing.*] check_untyped_defs = true disallow_incomplete_defs = true From 8874bf77917749a38fe709124515a55673bf3ba5 Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Wed, 9 Nov 2022 08:44:30 -0600 Subject: [PATCH 0326/1033] Change life360 timeouts & retries (#81799) Change from single timeout of 10 to socket timeout of 15, total timeout of 60, and retry up to 3 times. Bump life360 package to 5.3.0. --- homeassistant/components/life360/const.py | 5 ++++- homeassistant/components/life360/coordinator.py | 2 ++ homeassistant/components/life360/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/life360/const.py b/homeassistant/components/life360/const.py index d148a06c634..333ce14fbf6 100644 --- a/homeassistant/components/life360/const.py +++ b/homeassistant/components/life360/const.py @@ -3,11 +3,14 @@ from datetime import timedelta import logging +from aiohttp import ClientTimeout + DOMAIN = "life360" LOGGER = logging.getLogger(__package__) ATTRIBUTION = "Data provided by life360.com" -COMM_TIMEOUT = 10 +COMM_MAX_RETRIES = 3 +COMM_TIMEOUT = ClientTimeout(sock_connect=15, total=60) SPEED_FACTOR_MPH = 2.25 SPEED_DIGITS = 1 UPDATE_INTERVAL = timedelta(seconds=10) diff --git a/homeassistant/components/life360/coordinator.py b/homeassistant/components/life360/coordinator.py index 0b9641bfcae..b7121cc7fdb 100644 --- a/homeassistant/components/life360/coordinator.py +++ b/homeassistant/components/life360/coordinator.py @@ -26,6 +26,7 @@ from homeassistant.util.unit_conversion import DistanceConverter from homeassistant.util.unit_system import METRIC_SYSTEM from .const import ( + COMM_MAX_RETRIES, COMM_TIMEOUT, CONF_AUTHORIZATION, DOMAIN, @@ -106,6 +107,7 @@ class Life360DataUpdateCoordinator(DataUpdateCoordinator[Life360Data]): self._api = Life360( session=async_get_clientsession(hass), timeout=COMM_TIMEOUT, + max_retries=COMM_MAX_RETRIES, authorization=entry.data[CONF_AUTHORIZATION], ) self._missing_loc_reason = hass.data[DOMAIN].missing_loc_reason diff --git a/homeassistant/components/life360/manifest.json b/homeassistant/components/life360/manifest.json index 8f0c44f342b..eb3290e41e1 100644 --- a/homeassistant/components/life360/manifest.json +++ b/homeassistant/components/life360/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/life360", "codeowners": ["@pnbruckner"], - "requirements": ["life360==5.1.1"], + "requirements": ["life360==5.3.0"], "iot_class": "cloud_polling", "loggers": ["life360"] } diff --git a/requirements_all.txt b/requirements_all.txt index c9106d2ce39..bfd44464578 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1019,7 +1019,7 @@ librouteros==3.2.0 libsoundtouch==0.8 # homeassistant.components.life360 -life360==5.1.1 +life360==5.3.0 # homeassistant.components.osramlightify lightify==1.0.7.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 65966609ecc..96c757c9fec 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -757,7 +757,7 @@ librouteros==3.2.0 libsoundtouch==0.8 # homeassistant.components.life360 -life360==5.1.1 +life360==5.3.0 # homeassistant.components.logi_circle logi_circle==0.2.3 From e690db9ba6b07bb36bc1f6c90db18fa22fb3a79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Wed, 9 Nov 2022 16:51:33 +0200 Subject: [PATCH 0327/1033] Upgrade huawei-lte-api to 1.6.7, fixes empty username issues (#81751) Recentish versions of huawei-lte-api behave differently with regards to empty/default username compared to the older versions this integration was originally written against. 1.6.5+ changes the behavior so that our existing implementation works as expected when no username is supplied for the config entry. https://github.com/Salamek/huawei-lte-api/releases/tag/1.6.4 https://github.com/Salamek/huawei-lte-api/releases/tag/1.6.5 https://github.com/Salamek/huawei-lte-api/releases/tag/1.6.6 https://github.com/Salamek/huawei-lte-api/releases/tag/1.6.7 --- homeassistant/components/huawei_lte/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/huawei_lte/manifest.json b/homeassistant/components/huawei_lte/manifest.json index c658fff1b0f..2c777aa4339 100644 --- a/homeassistant/components/huawei_lte/manifest.json +++ b/homeassistant/components/huawei_lte/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/huawei_lte", "requirements": [ - "huawei-lte-api==1.6.3", + "huawei-lte-api==1.6.7", "stringcase==1.2.0", "url-normalize==1.4.3" ], diff --git a/requirements_all.txt b/requirements_all.txt index bfd44464578..d0951302aff 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -902,7 +902,7 @@ horimote==0.4.1 httplib2==0.20.4 # homeassistant.components.huawei_lte -huawei-lte-api==1.6.3 +huawei-lte-api==1.6.7 # homeassistant.components.hydrawise hydrawiser==0.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 96c757c9fec..b34ef5074d4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -679,7 +679,7 @@ homepluscontrol==0.0.5 httplib2==0.20.4 # homeassistant.components.huawei_lte -huawei-lte-api==1.6.3 +huawei-lte-api==1.6.7 # homeassistant.components.hyperion hyperion-py==0.7.5 From 9de4d7cba3177f9abe5634d96ef3edc4048841d6 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Wed, 9 Nov 2022 15:27:36 +0000 Subject: [PATCH 0328/1033] Fix homekit_controller climate entity not becoming active when changing modes (#81868) --- .../components/homekit_controller/climate.py | 1 + .../homekit_controller/test_climate.py | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/homeassistant/components/homekit_controller/climate.py b/homeassistant/components/homekit_controller/climate.py index de42243a6bb..41e88725121 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -209,6 +209,7 @@ class HomeKitHeaterCoolerEntity(HomeKitBaseClimateEntity): ) await self.async_put_characteristics( { + CharacteristicsTypes.ACTIVE: ActivationStateValues.ACTIVE, CharacteristicsTypes.TARGET_HEATER_COOLER_STATE: TARGET_HEATER_COOLER_STATE_HASS_TO_HOMEKIT[ hvac_mode ], diff --git a/tests/components/homekit_controller/test_climate.py b/tests/components/homekit_controller/test_climate.py index 0f10f0f9fa0..bf544c5aff4 100644 --- a/tests/components/homekit_controller/test_climate.py +++ b/tests/components/homekit_controller/test_climate.py @@ -760,6 +760,42 @@ async def test_heater_cooler_change_thermostat_state(hass, utcnow): ) +async def test_can_turn_on_after_off(hass, utcnow): + """ + Test that we always force device from inactive to active when setting mode. + + This is a regression test for #81863. + """ + helper = await setup_test_component(hass, create_heater_cooler_service) + + await hass.services.async_call( + DOMAIN, + SERVICE_SET_HVAC_MODE, + {"entity_id": "climate.testdevice", "hvac_mode": HVACMode.OFF}, + blocking=True, + ) + helper.async_assert_service_values( + ServicesTypes.HEATER_COOLER, + { + CharacteristicsTypes.ACTIVE: ActivationStateValues.INACTIVE, + }, + ) + + await hass.services.async_call( + DOMAIN, + SERVICE_SET_HVAC_MODE, + {"entity_id": "climate.testdevice", "hvac_mode": HVACMode.HEAT}, + blocking=True, + ) + helper.async_assert_service_values( + ServicesTypes.HEATER_COOLER, + { + CharacteristicsTypes.ACTIVE: ActivationStateValues.ACTIVE, + CharacteristicsTypes.TARGET_HEATER_COOLER_STATE: TargetHeaterCoolerStateValues.HEAT, + }, + ) + + async def test_heater_cooler_change_thermostat_temperature(hass, utcnow): """Test that we can change the target temperature.""" helper = await setup_test_component(hass, create_heater_cooler_service) From 84725f15a61f45161706414e0c8dd70856956d27 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Wed, 9 Nov 2022 23:28:28 +0800 Subject: [PATCH 0329/1033] Use IntEnum for stream orientation (#81835) * Use IntEnum for stream orientation * Rename enum values * Add comments * Fix import --- homeassistant/components/camera/__init__.py | 3 ++- homeassistant/components/camera/prefs.py | 19 +++++++------ homeassistant/components/stream/__init__.py | 8 +++--- homeassistant/components/stream/core.py | 18 +++++++++++-- homeassistant/components/stream/fmp4utils.py | 28 +++++++++++--------- tests/components/camera/test_init.py | 4 +-- 6 files changed, 52 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 136cd3b05f1..da88dc49a5b 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -30,6 +30,7 @@ from homeassistant.components.media_player import ( from homeassistant.components.stream import ( FORMAT_CONTENT_TYPE, OUTPUT_FORMATS, + Orientation, Stream, create_stream, ) @@ -869,7 +870,7 @@ async def websocket_get_prefs( vol.Required("type"): "camera/update_prefs", vol.Required("entity_id"): cv.entity_id, vol.Optional(PREF_PRELOAD_STREAM): bool, - vol.Optional(PREF_ORIENTATION): vol.All(int, vol.Range(min=1, max=8)), + vol.Optional(PREF_ORIENTATION): vol.Coerce(Orientation), } ) @websocket_api.async_response diff --git a/homeassistant/components/camera/prefs.py b/homeassistant/components/camera/prefs.py index 1107da2ba38..fac93df474e 100644 --- a/homeassistant/components/camera/prefs.py +++ b/homeassistant/components/camera/prefs.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Final, Union, cast +from homeassistant.components.stream import Orientation from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er @@ -18,11 +19,11 @@ STORAGE_VERSION: Final = 1 class CameraEntityPreferences: """Handle preferences for camera entity.""" - def __init__(self, prefs: dict[str, bool | int]) -> None: + def __init__(self, prefs: dict[str, bool | Orientation]) -> None: """Initialize prefs.""" self._prefs = prefs - def as_dict(self) -> dict[str, bool | int]: + def as_dict(self) -> dict[str, bool | Orientation]: """Return dictionary version.""" return self._prefs @@ -32,9 +33,11 @@ class CameraEntityPreferences: return cast(bool, self._prefs.get(PREF_PRELOAD_STREAM, False)) @property - def orientation(self) -> int: + def orientation(self) -> Orientation: """Return the current stream orientation settings.""" - return self._prefs.get(PREF_ORIENTATION, 1) + return cast( + Orientation, self._prefs.get(PREF_ORIENTATION, Orientation.NO_TRANSFORM) + ) class CameraPreferences: @@ -45,11 +48,11 @@ class CameraPreferences: self._hass = hass # The orientation prefs are stored in in the entity registry options # The preload_stream prefs are stored in this Store - self._store = Store[dict[str, dict[str, Union[bool, int]]]]( + self._store = Store[dict[str, dict[str, Union[bool, Orientation]]]]( hass, STORAGE_VERSION, STORAGE_KEY ) # Local copy of the preload_stream prefs - self._prefs: dict[str, dict[str, bool | int]] | None = None + self._prefs: dict[str, dict[str, bool | Orientation]] | None = None async def async_initialize(self) -> None: """Finish initializing the preferences.""" @@ -63,9 +66,9 @@ class CameraPreferences: entity_id: str, *, preload_stream: bool | UndefinedType = UNDEFINED, - orientation: int | UndefinedType = UNDEFINED, + orientation: Orientation | UndefinedType = UNDEFINED, stream_options: dict[str, str] | UndefinedType = UNDEFINED, - ) -> dict[str, bool | int]: + ) -> dict[str, bool | Orientation]: """Update camera preferences. Returns a dict with the preferences on success. diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index 559de094090..01cd3c2962c 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -63,6 +63,7 @@ from .core import ( STREAM_SETTINGS_NON_LL_HLS, IdleTimer, KeyFrameConverter, + Orientation, StreamOutput, StreamSettings, ) @@ -82,6 +83,7 @@ __all__ = [ "SOURCE_TIMEOUT", "Stream", "create_stream", + "Orientation", ] _LOGGER = logging.getLogger(__name__) @@ -229,7 +231,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: part_target_duration=conf[CONF_PART_DURATION], hls_advance_part_limit=max(int(3 / conf[CONF_PART_DURATION]), 3), hls_part_timeout=2 * conf[CONF_PART_DURATION], - orientation=1, + orientation=Orientation.NO_TRANSFORM, ) else: hass.data[DOMAIN][ATTR_SETTINGS] = STREAM_SETTINGS_NON_LL_HLS @@ -292,12 +294,12 @@ class Stream: self._diagnostics = Diagnostics() @property - def orientation(self) -> int: + def orientation(self) -> Orientation: """Return the current orientation setting.""" return self._stream_settings.orientation @orientation.setter - def orientation(self, value: int) -> None: + def orientation(self, value: Orientation) -> None: """Set the stream orientation setting.""" self._stream_settings.orientation = value diff --git a/homeassistant/components/stream/core.py b/homeassistant/components/stream/core.py index 0fa57913269..31654f7d0db 100644 --- a/homeassistant/components/stream/core.py +++ b/homeassistant/components/stream/core.py @@ -5,6 +5,7 @@ import asyncio from collections import deque from collections.abc import Callable, Coroutine, Iterable import datetime +from enum import IntEnum import logging from typing import TYPE_CHECKING, Any @@ -35,6 +36,19 @@ _LOGGER = logging.getLogger(__name__) PROVIDERS: Registry[str, type[StreamOutput]] = Registry() +class Orientation(IntEnum): + """Orientations for stream transforms. These are based on EXIF orientation tags.""" + + NO_TRANSFORM = 1 + MIRROR = 2 + ROTATE_180 = 3 + FLIP = 4 + ROTATE_LEFT_AND_FLIP = 5 + ROTATE_LEFT = 6 + ROTATE_RIGHT_AND_FLIP = 7 + ROTATE_RIGHT = 8 + + @attr.s(slots=True) class StreamSettings: """Stream settings.""" @@ -44,7 +58,7 @@ class StreamSettings: part_target_duration: float = attr.ib() hls_advance_part_limit: int = attr.ib() hls_part_timeout: float = attr.ib() - orientation: int = attr.ib() + orientation: Orientation = attr.ib() STREAM_SETTINGS_NON_LL_HLS = StreamSettings( @@ -53,7 +67,7 @@ STREAM_SETTINGS_NON_LL_HLS = StreamSettings( part_target_duration=TARGET_SEGMENT_DURATION_NON_LL_HLS, hls_advance_part_limit=3, hls_part_timeout=TARGET_SEGMENT_DURATION_NON_LL_HLS, - orientation=1, + orientation=Orientation.NO_TRANSFORM, ) diff --git a/homeassistant/components/stream/fmp4utils.py b/homeassistant/components/stream/fmp4utils.py index 35d32d5b0e3..5ec27a1768c 100644 --- a/homeassistant/components/stream/fmp4utils.py +++ b/homeassistant/components/stream/fmp4utils.py @@ -6,6 +6,8 @@ from typing import TYPE_CHECKING from homeassistant.exceptions import HomeAssistantError +from .core import Orientation + if TYPE_CHECKING: from io import BufferedIOBase @@ -179,22 +181,24 @@ ROTATE_LEFT_FLIP = (ZERO32 + NEGONE32 + ZERO32) + (NEGONE32 + ZERO32 + ZERO32) ROTATE_RIGHT_FLIP = (ZERO32 + ONE32 + ZERO32) + (ONE32 + ZERO32 + ZERO32) TRANSFORM_MATRIX_TOP = ( - # The first two entries are just to align the indices with the EXIF orientation tags - b"", - b"", - MIRROR, - ROTATE_180, - FLIP, - ROTATE_LEFT_FLIP, - ROTATE_LEFT, - ROTATE_RIGHT_FLIP, - ROTATE_RIGHT, + # The index into this tuple corresponds to the EXIF orientation tag + # Only index values of 2 through 8 are used + # The first two entries are just to keep everything aligned + b"", # 0 + b"", # 1 + MIRROR, # 2 + ROTATE_180, # 3 + FLIP, # 4 + ROTATE_LEFT_FLIP, # 5 + ROTATE_LEFT, # 6 + ROTATE_RIGHT_FLIP, # 7 + ROTATE_RIGHT, # 8 ) -def transform_init(init: bytes, orientation: int) -> bytes: +def transform_init(init: bytes, orientation: Orientation) -> bytes: """Change the transformation matrix in the header.""" - if orientation == 1: + if orientation == Orientation.NO_TRANSFORM: return init # Find moov moov_location = next(find_box(init, b"moov")) diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py index 71415284d35..9bf35ec55fa 100644 --- a/tests/components/camera/test_init.py +++ b/tests/components/camera/test_init.py @@ -367,7 +367,7 @@ async def test_websocket_update_orientation_prefs(hass, hass_ws_client, mock_cam assert response["success"] er_camera_prefs = registry.async_get("camera.demo_uniquecamera").options[DOMAIN] - assert er_camera_prefs[PREF_ORIENTATION] == 3 + assert er_camera_prefs[PREF_ORIENTATION] == camera.Orientation.ROTATE_180 assert response["result"][PREF_ORIENTATION] == er_camera_prefs[PREF_ORIENTATION] # Check that the preference was saved await client.send_json( @@ -375,7 +375,7 @@ async def test_websocket_update_orientation_prefs(hass, hass_ws_client, mock_cam ) msg = await client.receive_json() # orientation entry for this camera should have been added - assert msg["result"]["orientation"] == 3 + assert msg["result"]["orientation"] == camera.Orientation.ROTATE_180 async def test_play_stream_service_no_source(hass, mock_camera, mock_stream): From f9ff23a2c86711b60c03dad8eb88ac9e16296e34 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 9 Nov 2022 09:36:46 -0600 Subject: [PATCH 0330/1033] Fix benign typo in test_config_entries.py (#81789) * Fix typo in test_config_entries.py * touch ups * Update tests/test_config_entries.py Co-authored-by: Martin Hjelmare Co-authored-by: Erik Montnemery Co-authored-by: Martin Hjelmare --- tests/test_config_entries.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 080b3cdf5f1..96d032f771e 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -3316,17 +3316,17 @@ async def test_reauth(hass): assert entry.entry_id != entry2.entry_id - # Check we can't start duplicate flows + # Check that we can't start duplicate reauth flows entry.async_start_reauth(hass, {"extra_context": "some_extra_context"}) await hass.async_block_till_done() assert len(hass.config_entries.flow.async_progress()) == 1 - # Check we can't start duplicate when the context context is different + # Check that we can't start duplicate reauth flows when the context is different entry.async_start_reauth(hass, {"diff": "diff"}) await hass.async_block_till_done() assert len(hass.config_entries.flow.async_progress()) == 1 - # Check we can start a reauth for a different entry + # Check that we can start a reauth flow for a different entry entry2.async_start_reauth(hass, {"extra_context": "some_extra_context"}) await hass.async_block_till_done() assert len(hass.config_entries.flow.async_progress()) == 2 From f3e85b649254733e20aace24a9b6b648651ae1c2 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 9 Nov 2022 17:58:20 +0200 Subject: [PATCH 0331/1033] Deduplicate blackening (#81802) --- .../generated/application_credentials.py | 2 +- homeassistant/generated/bluetooth.py | 131 +-- homeassistant/generated/config_flows.py | 22 +- homeassistant/generated/currencies.py | 2 +- homeassistant/generated/dhcp.py | 979 ++++++++++++++---- homeassistant/generated/mqtt.py | 2 +- homeassistant/generated/ssdp.py | 2 +- homeassistant/generated/usb.py | 128 +-- homeassistant/generated/zeroconf.py | 126 +-- script/currencies.py | 63 +- script/hassfest/application_credentials.py | 15 +- script/hassfest/bluetooth.py | 19 +- script/hassfest/config_flow.py | 15 +- script/hassfest/dhcp.py | 18 +- script/hassfest/mqtt.py | 15 +- script/hassfest/serializer.py | 103 +- script/hassfest/ssdp.py | 22 +- script/hassfest/usb.py | 15 +- script/hassfest/zeroconf.py | 25 +- 19 files changed, 1141 insertions(+), 563 deletions(-) diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index d2e16f2b914..31e73418c5e 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -1,4 +1,4 @@ -"""Automatically generated by hassfest. +"""This file is automatically generated. To update, run python3 -m script.hassfest """ diff --git a/homeassistant/generated/bluetooth.py b/homeassistant/generated/bluetooth.py index 6fa2342b5a4..7fa3f363093 100644 --- a/homeassistant/generated/bluetooth.py +++ b/homeassistant/generated/bluetooth.py @@ -1,7 +1,8 @@ -"""Automatically generated by hassfest. +"""This file is automatically generated. To update, run python3 -m script.hassfest """ + from __future__ import annotations BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ @@ -10,41 +11,40 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ "manufacturer_id": 820, }, { + "connectable": False, "domain": "aranet", "manufacturer_id": 1794, "service_uuid": "f0cd1400-95da-4f4b-9ac8-aa55d312af0c", - "connectable": False, }, { + "connectable": False, "domain": "aranet", "manufacturer_id": 1794, "service_uuid": "0000fce0-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "bluemaestro", "manufacturer_id": 307, - "connectable": False, }, { - "domain": "bthome", "connectable": False, + "domain": "bthome", "service_data_uuid": "0000181c-0000-1000-8000-00805f9b34fb", }, { - "domain": "bthome", "connectable": False, + "domain": "bthome", "service_data_uuid": "0000181e-0000-1000-8000-00805f9b34fb", }, { - "domain": "bthome", "connectable": False, + "domain": "bthome", "service_data_uuid": "0000fcd2-0000-1000-8000-00805f9b34fb", }, { - "domain": "fjaraskupan", "connectable": False, - "manufacturer_id": 20296, + "domain": "fjaraskupan", "manufacturer_data_start": [ 79, 68, @@ -53,143 +53,144 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ 65, 82, ], + "manufacturer_id": 20296, }, { + "connectable": False, "domain": "govee_ble", "local_name": "Govee*", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "local_name": "GVH5*", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "local_name": "B5178*", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 6966, "service_uuid": "00008451-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 63391, "service_uuid": "00008351-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 26589, "service_uuid": "00008351-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 57391, "service_uuid": "00008351-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 18994, "service_uuid": "00008551-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 818, "service_uuid": "00008551-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 53579, "service_uuid": "00008151-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 43682, "service_uuid": "00008151-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 59970, "service_uuid": "00008151-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 63585, "service_uuid": "00008151-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 14474, "service_uuid": "00008151-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 10032, "service_uuid": "00008251-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "govee_ble", "manufacturer_id": 19506, "service_uuid": "00001801-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { "domain": "homekit_controller", - "manufacturer_id": 76, "manufacturer_data_start": [ 6, ], + "manufacturer_id": 76, }, { "domain": "ibeacon", - "manufacturer_id": 76, "manufacturer_data_start": [ 2, 21, ], + "manufacturer_id": 76, }, { + "connectable": False, "domain": "inkbird", "local_name": "sps", - "connectable": False, }, { + "connectable": False, "domain": "inkbird", "local_name": "Inkbird*", - "connectable": False, }, { + "connectable": False, "domain": "inkbird", "local_name": "iBBQ*", - "connectable": False, }, { + "connectable": False, "domain": "inkbird", "local_name": "xBBQ*", - "connectable": False, }, { + "connectable": False, "domain": "inkbird", "local_name": "tps", - "connectable": False, }, { - "domain": "kegtron", "connectable": False, + "domain": "kegtron", "manufacturer_id": 65535, }, { @@ -240,28 +241,28 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ "manufacturer_id": 13, }, { + "connectable": False, "domain": "moat", "local_name": "Moat_S*", - "connectable": False, }, { "domain": "oralb", "manufacturer_id": 220, }, { + "connectable": False, "domain": "qingping", "local_name": "Qingping*", - "connectable": False, }, { + "connectable": False, "domain": "qingping", "local_name": "Lee Guitars*", - "connectable": False, }, { + "connectable": False, "domain": "qingping", "service_data_uuid": "0000fdcd-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { "domain": "ruuvitag_ble", @@ -272,31 +273,31 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ "local_name": "Ruuvi *", }, { + "connectable": False, "domain": "sensorpro", - "manufacturer_id": 43605, "manufacturer_data_start": [ 1, 1, 164, 193, ], - "connectable": False, + "manufacturer_id": 43605, }, { + "connectable": False, "domain": "sensorpro", - "manufacturer_id": 43605, "manufacturer_data_start": [ 1, 5, 164, 193, ], - "connectable": False, + "manufacturer_id": 43605, }, { + "connectable": False, "domain": "sensorpush", "local_name": "SensorPush*", - "connectable": False, }, { "domain": "snooz", @@ -307,65 +308,64 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ "service_uuid": "729f0608-496a-47fe-a124-3a62aaa3fbc0", }, { + "connectable": False, "domain": "switchbot", "service_data_uuid": "00000d00-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "switchbot", "service_data_uuid": "0000fd3d-0000-1000-8000-00805f9b34fb", - "connectable": False, }, { + "connectable": False, "domain": "switchbot", "service_uuid": "cba20d00-224d-11e6-9fb8-0002a5d5c51b", - "connectable": False, }, { + "connectable": False, "domain": "thermobeacon", - "service_uuid": "0000fff0-0000-1000-8000-00805f9b34fb", + "manufacturer_data_start": [ + 0, + ], "manufacturer_id": 16, + "service_uuid": "0000fff0-0000-1000-8000-00805f9b34fb", + }, + { + "connectable": False, + "domain": "thermobeacon", "manufacturer_data_start": [ 0, ], - "connectable": False, - }, - { - "domain": "thermobeacon", - "service_uuid": "0000fff0-0000-1000-8000-00805f9b34fb", "manufacturer_id": 17, - "manufacturer_data_start": [ - 0, - ], - "connectable": False, - }, - { - "domain": "thermobeacon", "service_uuid": "0000fff0-0000-1000-8000-00805f9b34fb", - "manufacturer_id": 21, + }, + { + "connectable": False, + "domain": "thermobeacon", "manufacturer_data_start": [ 0, ], - "connectable": False, + "manufacturer_id": 21, + "service_uuid": "0000fff0-0000-1000-8000-00805f9b34fb", }, { + "connectable": False, "domain": "thermobeacon", "local_name": "ThermoBeacon", - "connectable": False, }, { + "connectable": False, "domain": "thermopro", "local_name": "TP35*", - "connectable": False, }, { + "connectable": False, "domain": "thermopro", "local_name": "TP39*", - "connectable": False, }, { "domain": "tilt_ble", - "manufacturer_id": 76, "manufacturer_data_start": [ 2, 21, @@ -373,10 +373,11 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ 149, 187, ], + "manufacturer_id": 76, }, { - "domain": "xiaomi_ble", "connectable": False, + "domain": "xiaomi_ble", "service_data_uuid": "0000fe95-0000-1000-8000-00805f9b34fb", }, { diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 46aefeebb22..cdcea6cf9b3 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -1,9 +1,19 @@ -"""Automatically generated by hassfest. +"""This file is automatically generated. To update, run python3 -m script.hassfest """ FLOWS = { + "helper": [ + "derivative", + "group", + "integration", + "min_max", + "switch_as_x", + "threshold", + "tod", + "utility_meter", + ], "integration": [ "abode", "accuweather", @@ -473,14 +483,4 @@ FLOWS = { "zwave_js", "zwave_me", ], - "helper": [ - "derivative", - "group", - "integration", - "min_max", - "switch_as_x", - "threshold", - "tod", - "utility_meter", - ], } diff --git a/homeassistant/generated/currencies.py b/homeassistant/generated/currencies.py index 7a5a6a31bb5..546bc125a01 100644 --- a/homeassistant/generated/currencies.py +++ b/homeassistant/generated/currencies.py @@ -1,4 +1,4 @@ -"""Automatically generated by currencies.py. +"""This file is automatically generated. To update, run python3 -m script.currencies """ diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index 4b8dee0d956..08ea9bfe64c 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -1,128 +1,522 @@ -"""Automatically generated by hassfest. +"""This file is automatically generated. To update, run python3 -m script.hassfest """ + from __future__ import annotations DHCP: list[dict[str, str | bool]] = [ - {"domain": "august", "hostname": "connect", "macaddress": "D86162*"}, - {"domain": "august", "hostname": "connect", "macaddress": "B8B7F1*"}, - {"domain": "august", "hostname": "connect", "macaddress": "2C9FFB*"}, - {"domain": "august", "hostname": "august*", "macaddress": "E076D0*"}, - {"domain": "awair", "macaddress": "70886B1*"}, - {"domain": "axis", "registered_devices": True}, - {"domain": "axis", "hostname": "axis-00408c*", "macaddress": "00408C*"}, - {"domain": "axis", "hostname": "axis-accc8e*", "macaddress": "ACCC8E*"}, - {"domain": "axis", "hostname": "axis-b8a44f*", "macaddress": "B8A44F*"}, - {"domain": "blink", "hostname": "blink*", "macaddress": "B85F98*"}, - {"domain": "blink", "hostname": "blink*", "macaddress": "00037F*"}, - {"domain": "blink", "hostname": "blink*", "macaddress": "20A171*"}, - {"domain": "broadlink", "registered_devices": True}, - {"domain": "broadlink", "macaddress": "34EA34*"}, - {"domain": "broadlink", "macaddress": "24DFA7*"}, - {"domain": "broadlink", "macaddress": "A043B0*"}, - {"domain": "broadlink", "macaddress": "B4430D*"}, - {"domain": "broadlink", "macaddress": "C8F742*"}, - {"domain": "elkm1", "registered_devices": True}, - {"domain": "elkm1", "macaddress": "00409D*"}, - {"domain": "emonitor", "hostname": "emonitor*", "macaddress": "0090C2*"}, - {"domain": "emonitor", "registered_devices": True}, - {"domain": "esphome", "registered_devices": True}, - {"domain": "flume", "hostname": "flume-gw-*"}, - {"domain": "flux_led", "registered_devices": True}, - {"domain": "flux_led", "macaddress": "18B905*", "hostname": "[ba][lk]*"}, - {"domain": "flux_led", "macaddress": "249494*", "hostname": "[ba][lk]*"}, - {"domain": "flux_led", "macaddress": "7CB94C*", "hostname": "[ba][lk]*"}, - {"domain": "flux_led", "macaddress": "ACCF23*", "hostname": "[hba][flk]*"}, - {"domain": "flux_led", "macaddress": "B4E842*", "hostname": "[ba][lk]*"}, - {"domain": "flux_led", "macaddress": "F0FE6B*", "hostname": "[hba][flk]*"}, - {"domain": "flux_led", "macaddress": "8CCE4E*", "hostname": "lwip*"}, - {"domain": "flux_led", "hostname": "hf-lpb100-zj*"}, - {"domain": "flux_led", "hostname": "zengge_[0-9a-f][0-9a-f]_*"}, - {"domain": "flux_led", "macaddress": "C82E47*", "hostname": "sta*"}, - {"domain": "fronius", "macaddress": "0003AC*"}, - {"domain": "fully_kiosk", "registered_devices": True}, - {"domain": "goalzero", "registered_devices": True}, - {"domain": "goalzero", "hostname": "yeti*"}, - {"domain": "gogogate2", "hostname": "ismartgate*"}, - {"domain": "guardian", "hostname": "gvc*", "macaddress": "30AEA4*"}, - {"domain": "guardian", "hostname": "gvc*", "macaddress": "B4E62D*"}, - {"domain": "guardian", "hostname": "guardian*", "macaddress": "30AEA4*"}, - {"domain": "hunterdouglas_powerview", "registered_devices": True}, + { + "domain": "august", + "hostname": "connect", + "macaddress": "D86162*", + }, + { + "domain": "august", + "hostname": "connect", + "macaddress": "B8B7F1*", + }, + { + "domain": "august", + "hostname": "connect", + "macaddress": "2C9FFB*", + }, + { + "domain": "august", + "hostname": "august*", + "macaddress": "E076D0*", + }, + { + "domain": "awair", + "macaddress": "70886B1*", + }, + { + "domain": "axis", + "registered_devices": True, + }, + { + "domain": "axis", + "hostname": "axis-00408c*", + "macaddress": "00408C*", + }, + { + "domain": "axis", + "hostname": "axis-accc8e*", + "macaddress": "ACCC8E*", + }, + { + "domain": "axis", + "hostname": "axis-b8a44f*", + "macaddress": "B8A44F*", + }, + { + "domain": "blink", + "hostname": "blink*", + "macaddress": "B85F98*", + }, + { + "domain": "blink", + "hostname": "blink*", + "macaddress": "00037F*", + }, + { + "domain": "blink", + "hostname": "blink*", + "macaddress": "20A171*", + }, + { + "domain": "broadlink", + "registered_devices": True, + }, + { + "domain": "broadlink", + "macaddress": "34EA34*", + }, + { + "domain": "broadlink", + "macaddress": "24DFA7*", + }, + { + "domain": "broadlink", + "macaddress": "A043B0*", + }, + { + "domain": "broadlink", + "macaddress": "B4430D*", + }, + { + "domain": "broadlink", + "macaddress": "C8F742*", + }, + { + "domain": "elkm1", + "registered_devices": True, + }, + { + "domain": "elkm1", + "macaddress": "00409D*", + }, + { + "domain": "emonitor", + "hostname": "emonitor*", + "macaddress": "0090C2*", + }, + { + "domain": "emonitor", + "registered_devices": True, + }, + { + "domain": "esphome", + "registered_devices": True, + }, + { + "domain": "flume", + "hostname": "flume-gw-*", + }, + { + "domain": "flux_led", + "registered_devices": True, + }, + { + "domain": "flux_led", + "hostname": "[ba][lk]*", + "macaddress": "18B905*", + }, + { + "domain": "flux_led", + "hostname": "[ba][lk]*", + "macaddress": "249494*", + }, + { + "domain": "flux_led", + "hostname": "[ba][lk]*", + "macaddress": "7CB94C*", + }, + { + "domain": "flux_led", + "hostname": "[hba][flk]*", + "macaddress": "ACCF23*", + }, + { + "domain": "flux_led", + "hostname": "[ba][lk]*", + "macaddress": "B4E842*", + }, + { + "domain": "flux_led", + "hostname": "[hba][flk]*", + "macaddress": "F0FE6B*", + }, + { + "domain": "flux_led", + "hostname": "lwip*", + "macaddress": "8CCE4E*", + }, + { + "domain": "flux_led", + "hostname": "hf-lpb100-zj*", + }, + { + "domain": "flux_led", + "hostname": "zengge_[0-9a-f][0-9a-f]_*", + }, + { + "domain": "flux_led", + "hostname": "sta*", + "macaddress": "C82E47*", + }, + { + "domain": "fronius", + "macaddress": "0003AC*", + }, + { + "domain": "fully_kiosk", + "registered_devices": True, + }, + { + "domain": "goalzero", + "registered_devices": True, + }, + { + "domain": "goalzero", + "hostname": "yeti*", + }, + { + "domain": "gogogate2", + "hostname": "ismartgate*", + }, + { + "domain": "guardian", + "hostname": "gvc*", + "macaddress": "30AEA4*", + }, + { + "domain": "guardian", + "hostname": "gvc*", + "macaddress": "B4E62D*", + }, + { + "domain": "guardian", + "hostname": "guardian*", + "macaddress": "30AEA4*", + }, + { + "domain": "hunterdouglas_powerview", + "registered_devices": True, + }, { "domain": "hunterdouglas_powerview", "hostname": "hunter*", "macaddress": "002674*", }, - {"domain": "insteon", "macaddress": "000EF3*"}, - {"domain": "insteon", "registered_devices": True}, - {"domain": "intellifire", "hostname": "zentrios-*"}, - {"domain": "isy994", "registered_devices": True}, - {"domain": "isy994", "hostname": "isy*", "macaddress": "0021B9*"}, - {"domain": "isy994", "hostname": "polisy*", "macaddress": "000DB9*"}, - {"domain": "lametric", "registered_devices": True}, - {"domain": "lifx", "macaddress": "D073D5*"}, - {"domain": "lifx", "registered_devices": True}, - {"domain": "litterrobot", "hostname": "litter-robot4"}, - {"domain": "lyric", "hostname": "lyric-*", "macaddress": "48A2E6*"}, - {"domain": "lyric", "hostname": "lyric-*", "macaddress": "B82CA0*"}, - {"domain": "lyric", "hostname": "lyric-*", "macaddress": "00D02D*"}, - {"domain": "motion_blinds", "registered_devices": True}, - {"domain": "motion_blinds", "hostname": "motion_*"}, - {"domain": "motion_blinds", "hostname": "brel_*"}, - {"domain": "motion_blinds", "hostname": "connector_*"}, - {"domain": "myq", "macaddress": "645299*"}, - {"domain": "nest", "macaddress": "18B430*"}, - {"domain": "nest", "macaddress": "641666*"}, - {"domain": "nest", "macaddress": "D8EB46*"}, - {"domain": "nexia", "hostname": "xl857-*", "macaddress": "000231*"}, - {"domain": "nuheat", "hostname": "nuheat", "macaddress": "002338*"}, - {"domain": "nuki", "hostname": "nuki_bridge_*"}, - {"domain": "oncue", "hostname": "kohlergen*", "macaddress": "00146F*"}, - {"domain": "overkiz", "hostname": "gateway*", "macaddress": "F8811A*"}, - {"domain": "powerwall", "hostname": "1118431-*"}, - {"domain": "prusalink", "macaddress": "109C70*"}, - {"domain": "qnap_qsw", "macaddress": "245EBE*"}, - {"domain": "rachio", "hostname": "rachio-*", "macaddress": "009D6B*"}, - {"domain": "rachio", "hostname": "rachio-*", "macaddress": "F0038C*"}, - {"domain": "rachio", "hostname": "rachio-*", "macaddress": "74C63B*"}, - {"domain": "radiotherm", "hostname": "thermostat*", "macaddress": "5CDAD4*"}, - {"domain": "radiotherm", "registered_devices": True}, - {"domain": "rainforest_eagle", "macaddress": "D8D5B9*"}, - {"domain": "ring", "hostname": "ring*", "macaddress": "0CAE7D*"}, - {"domain": "roomba", "hostname": "irobot-*", "macaddress": "501479*"}, - {"domain": "roomba", "hostname": "roomba-*", "macaddress": "80A589*"}, - {"domain": "roomba", "hostname": "roomba-*", "macaddress": "DCF505*"}, - {"domain": "roomba", "hostname": "roomba-*", "macaddress": "204EF6*"}, - {"domain": "samsungtv", "registered_devices": True}, - {"domain": "samsungtv", "hostname": "tizen*"}, - {"domain": "samsungtv", "macaddress": "4844F7*"}, - {"domain": "samsungtv", "macaddress": "606BBD*"}, - {"domain": "samsungtv", "macaddress": "641CB0*"}, - {"domain": "samsungtv", "macaddress": "8CC8CD*"}, - {"domain": "samsungtv", "macaddress": "8CEA48*"}, - {"domain": "samsungtv", "macaddress": "F47B5E*"}, - {"domain": "screenlogic", "registered_devices": True}, - {"domain": "screenlogic", "hostname": "pentair*", "macaddress": "00C033*"}, - {"domain": "sense", "hostname": "sense-*", "macaddress": "009D6B*"}, - {"domain": "sense", "hostname": "sense-*", "macaddress": "DCEFCA*"}, - {"domain": "sense", "hostname": "sense-*", "macaddress": "A4D578*"}, - {"domain": "senseme", "registered_devices": True}, - {"domain": "senseme", "macaddress": "20F85E*"}, - {"domain": "sensibo", "hostname": "sensibo*"}, - {"domain": "simplisafe", "hostname": "simplisafe*", "macaddress": "30AEA4*"}, - {"domain": "sleepiq", "macaddress": "64DBA0*"}, - {"domain": "smartthings", "hostname": "st*", "macaddress": "24FD5B*"}, - {"domain": "smartthings", "hostname": "smartthings*", "macaddress": "24FD5B*"}, - {"domain": "smartthings", "hostname": "hub*", "macaddress": "24FD5B*"}, - {"domain": "smartthings", "hostname": "hub*", "macaddress": "D052A8*"}, - {"domain": "smartthings", "hostname": "hub*", "macaddress": "286D97*"}, - {"domain": "solaredge", "hostname": "target", "macaddress": "002702*"}, - {"domain": "somfy_mylink", "hostname": "somfy_*", "macaddress": "B8B7F1*"}, - {"domain": "squeezebox", "hostname": "squeezebox*", "macaddress": "000420*"}, - {"domain": "steamist", "registered_devices": True}, - {"domain": "steamist", "macaddress": "001E0C*", "hostname": "my[45]50*"}, - {"domain": "tado", "hostname": "tado*"}, + { + "domain": "insteon", + "macaddress": "000EF3*", + }, + { + "domain": "insteon", + "registered_devices": True, + }, + { + "domain": "intellifire", + "hostname": "zentrios-*", + }, + { + "domain": "isy994", + "registered_devices": True, + }, + { + "domain": "isy994", + "hostname": "isy*", + "macaddress": "0021B9*", + }, + { + "domain": "isy994", + "hostname": "polisy*", + "macaddress": "000DB9*", + }, + { + "domain": "lametric", + "registered_devices": True, + }, + { + "domain": "lifx", + "macaddress": "D073D5*", + }, + { + "domain": "lifx", + "registered_devices": True, + }, + { + "domain": "litterrobot", + "hostname": "litter-robot4", + }, + { + "domain": "lyric", + "hostname": "lyric-*", + "macaddress": "48A2E6*", + }, + { + "domain": "lyric", + "hostname": "lyric-*", + "macaddress": "B82CA0*", + }, + { + "domain": "lyric", + "hostname": "lyric-*", + "macaddress": "00D02D*", + }, + { + "domain": "motion_blinds", + "registered_devices": True, + }, + { + "domain": "motion_blinds", + "hostname": "motion_*", + }, + { + "domain": "motion_blinds", + "hostname": "brel_*", + }, + { + "domain": "motion_blinds", + "hostname": "connector_*", + }, + { + "domain": "myq", + "macaddress": "645299*", + }, + { + "domain": "nest", + "macaddress": "18B430*", + }, + { + "domain": "nest", + "macaddress": "641666*", + }, + { + "domain": "nest", + "macaddress": "D8EB46*", + }, + { + "domain": "nexia", + "hostname": "xl857-*", + "macaddress": "000231*", + }, + { + "domain": "nuheat", + "hostname": "nuheat", + "macaddress": "002338*", + }, + { + "domain": "nuki", + "hostname": "nuki_bridge_*", + }, + { + "domain": "oncue", + "hostname": "kohlergen*", + "macaddress": "00146F*", + }, + { + "domain": "overkiz", + "hostname": "gateway*", + "macaddress": "F8811A*", + }, + { + "domain": "powerwall", + "hostname": "1118431-*", + }, + { + "domain": "prusalink", + "macaddress": "109C70*", + }, + { + "domain": "qnap_qsw", + "macaddress": "245EBE*", + }, + { + "domain": "rachio", + "hostname": "rachio-*", + "macaddress": "009D6B*", + }, + { + "domain": "rachio", + "hostname": "rachio-*", + "macaddress": "F0038C*", + }, + { + "domain": "rachio", + "hostname": "rachio-*", + "macaddress": "74C63B*", + }, + { + "domain": "radiotherm", + "hostname": "thermostat*", + "macaddress": "5CDAD4*", + }, + { + "domain": "radiotherm", + "registered_devices": True, + }, + { + "domain": "rainforest_eagle", + "macaddress": "D8D5B9*", + }, + { + "domain": "ring", + "hostname": "ring*", + "macaddress": "0CAE7D*", + }, + { + "domain": "roomba", + "hostname": "irobot-*", + "macaddress": "501479*", + }, + { + "domain": "roomba", + "hostname": "roomba-*", + "macaddress": "80A589*", + }, + { + "domain": "roomba", + "hostname": "roomba-*", + "macaddress": "DCF505*", + }, + { + "domain": "roomba", + "hostname": "roomba-*", + "macaddress": "204EF6*", + }, + { + "domain": "samsungtv", + "registered_devices": True, + }, + { + "domain": "samsungtv", + "hostname": "tizen*", + }, + { + "domain": "samsungtv", + "macaddress": "4844F7*", + }, + { + "domain": "samsungtv", + "macaddress": "606BBD*", + }, + { + "domain": "samsungtv", + "macaddress": "641CB0*", + }, + { + "domain": "samsungtv", + "macaddress": "8CC8CD*", + }, + { + "domain": "samsungtv", + "macaddress": "8CEA48*", + }, + { + "domain": "samsungtv", + "macaddress": "F47B5E*", + }, + { + "domain": "screenlogic", + "registered_devices": True, + }, + { + "domain": "screenlogic", + "hostname": "pentair*", + "macaddress": "00C033*", + }, + { + "domain": "sense", + "hostname": "sense-*", + "macaddress": "009D6B*", + }, + { + "domain": "sense", + "hostname": "sense-*", + "macaddress": "DCEFCA*", + }, + { + "domain": "sense", + "hostname": "sense-*", + "macaddress": "A4D578*", + }, + { + "domain": "senseme", + "registered_devices": True, + }, + { + "domain": "senseme", + "macaddress": "20F85E*", + }, + { + "domain": "sensibo", + "hostname": "sensibo*", + }, + { + "domain": "simplisafe", + "hostname": "simplisafe*", + "macaddress": "30AEA4*", + }, + { + "domain": "sleepiq", + "macaddress": "64DBA0*", + }, + { + "domain": "smartthings", + "hostname": "st*", + "macaddress": "24FD5B*", + }, + { + "domain": "smartthings", + "hostname": "smartthings*", + "macaddress": "24FD5B*", + }, + { + "domain": "smartthings", + "hostname": "hub*", + "macaddress": "24FD5B*", + }, + { + "domain": "smartthings", + "hostname": "hub*", + "macaddress": "D052A8*", + }, + { + "domain": "smartthings", + "hostname": "hub*", + "macaddress": "286D97*", + }, + { + "domain": "solaredge", + "hostname": "target", + "macaddress": "002702*", + }, + { + "domain": "somfy_mylink", + "hostname": "somfy_*", + "macaddress": "B8B7F1*", + }, + { + "domain": "squeezebox", + "hostname": "squeezebox*", + "macaddress": "000420*", + }, + { + "domain": "steamist", + "registered_devices": True, + }, + { + "domain": "steamist", + "hostname": "my[45]50*", + "macaddress": "001E0C*", + }, + { + "domain": "tado", + "hostname": "tado*", + }, { "domain": "tesla_wall_connector", "hostname": "teslawallconnector_*", @@ -138,69 +532,296 @@ DHCP: list[dict[str, str | bool]] = [ "hostname": "teslawallconnector_*", "macaddress": "4CFCAA*", }, - {"domain": "tolo", "hostname": "usr-tcp232-ed2"}, - {"domain": "toon", "hostname": "eneco-*", "macaddress": "74C63B*"}, - {"domain": "tplink", "registered_devices": True}, - {"domain": "tplink", "hostname": "es*", "macaddress": "54AF97*"}, - {"domain": "tplink", "hostname": "ep*", "macaddress": "E848B8*"}, - {"domain": "tplink", "hostname": "ep*", "macaddress": "1C61B4*"}, - {"domain": "tplink", "hostname": "ep*", "macaddress": "003192*"}, - {"domain": "tplink", "hostname": "hs*", "macaddress": "1C3BF3*"}, - {"domain": "tplink", "hostname": "hs*", "macaddress": "50C7BF*"}, - {"domain": "tplink", "hostname": "hs*", "macaddress": "68FF7B*"}, - {"domain": "tplink", "hostname": "hs*", "macaddress": "98DAC4*"}, - {"domain": "tplink", "hostname": "hs*", "macaddress": "B09575*"}, - {"domain": "tplink", "hostname": "hs*", "macaddress": "C006C3*"}, - {"domain": "tplink", "hostname": "lb*", "macaddress": "1C3BF3*"}, - {"domain": "tplink", "hostname": "lb*", "macaddress": "50C7BF*"}, - {"domain": "tplink", "hostname": "lb*", "macaddress": "68FF7B*"}, - {"domain": "tplink", "hostname": "lb*", "macaddress": "98DAC4*"}, - {"domain": "tplink", "hostname": "lb*", "macaddress": "B09575*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "60A4B7*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "005F67*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "1027F5*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "B0A7B9*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "403F8C*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "C0C9E3*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "909A4A*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "E848B8*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "003192*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "1C3BF3*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "50C7BF*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "68FF7B*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "98DAC4*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "B09575*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "C006C3*"}, - {"domain": "tplink", "hostname": "k[lp]*", "macaddress": "6C5AB0*"}, - {"domain": "tuya", "macaddress": "105A17*"}, - {"domain": "tuya", "macaddress": "10D561*"}, - {"domain": "tuya", "macaddress": "1869D8*"}, - {"domain": "tuya", "macaddress": "381F8D*"}, - {"domain": "tuya", "macaddress": "508A06*"}, - {"domain": "tuya", "macaddress": "68572D*"}, - {"domain": "tuya", "macaddress": "708976*"}, - {"domain": "tuya", "macaddress": "7CF666*"}, - {"domain": "tuya", "macaddress": "84E342*"}, - {"domain": "tuya", "macaddress": "D4A651*"}, - {"domain": "tuya", "macaddress": "D81F12*"}, - {"domain": "twinkly", "hostname": "twinkly_*"}, - {"domain": "unifiprotect", "macaddress": "B4FBE4*"}, - {"domain": "unifiprotect", "macaddress": "802AA8*"}, - {"domain": "unifiprotect", "macaddress": "F09FC2*"}, - {"domain": "unifiprotect", "macaddress": "68D79A*"}, - {"domain": "unifiprotect", "macaddress": "18E829*"}, - {"domain": "unifiprotect", "macaddress": "245A4C*"}, - {"domain": "unifiprotect", "macaddress": "784558*"}, - {"domain": "unifiprotect", "macaddress": "E063DA*"}, - {"domain": "unifiprotect", "macaddress": "265A4C*"}, - {"domain": "unifiprotect", "macaddress": "74ACB9*"}, - {"domain": "verisure", "macaddress": "0023C1*"}, - {"domain": "vicare", "macaddress": "B87424*"}, - {"domain": "wiz", "registered_devices": True}, - {"domain": "wiz", "macaddress": "A8BB50*"}, - {"domain": "wiz", "macaddress": "D8A011*"}, - {"domain": "wiz", "macaddress": "444F8E*"}, - {"domain": "wiz", "macaddress": "6C2990*"}, - {"domain": "wiz", "hostname": "wiz_*"}, - {"domain": "yeelight", "hostname": "yeelink-*"}, + { + "domain": "tolo", + "hostname": "usr-tcp232-ed2", + }, + { + "domain": "toon", + "hostname": "eneco-*", + "macaddress": "74C63B*", + }, + { + "domain": "tplink", + "registered_devices": True, + }, + { + "domain": "tplink", + "hostname": "es*", + "macaddress": "54AF97*", + }, + { + "domain": "tplink", + "hostname": "ep*", + "macaddress": "E848B8*", + }, + { + "domain": "tplink", + "hostname": "ep*", + "macaddress": "1C61B4*", + }, + { + "domain": "tplink", + "hostname": "ep*", + "macaddress": "003192*", + }, + { + "domain": "tplink", + "hostname": "hs*", + "macaddress": "1C3BF3*", + }, + { + "domain": "tplink", + "hostname": "hs*", + "macaddress": "50C7BF*", + }, + { + "domain": "tplink", + "hostname": "hs*", + "macaddress": "68FF7B*", + }, + { + "domain": "tplink", + "hostname": "hs*", + "macaddress": "98DAC4*", + }, + { + "domain": "tplink", + "hostname": "hs*", + "macaddress": "B09575*", + }, + { + "domain": "tplink", + "hostname": "hs*", + "macaddress": "C006C3*", + }, + { + "domain": "tplink", + "hostname": "lb*", + "macaddress": "1C3BF3*", + }, + { + "domain": "tplink", + "hostname": "lb*", + "macaddress": "50C7BF*", + }, + { + "domain": "tplink", + "hostname": "lb*", + "macaddress": "68FF7B*", + }, + { + "domain": "tplink", + "hostname": "lb*", + "macaddress": "98DAC4*", + }, + { + "domain": "tplink", + "hostname": "lb*", + "macaddress": "B09575*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "60A4B7*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "005F67*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "1027F5*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "B0A7B9*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "403F8C*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "C0C9E3*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "909A4A*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "E848B8*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "003192*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "1C3BF3*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "50C7BF*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "68FF7B*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "98DAC4*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "B09575*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "C006C3*", + }, + { + "domain": "tplink", + "hostname": "k[lp]*", + "macaddress": "6C5AB0*", + }, + { + "domain": "tuya", + "macaddress": "105A17*", + }, + { + "domain": "tuya", + "macaddress": "10D561*", + }, + { + "domain": "tuya", + "macaddress": "1869D8*", + }, + { + "domain": "tuya", + "macaddress": "381F8D*", + }, + { + "domain": "tuya", + "macaddress": "508A06*", + }, + { + "domain": "tuya", + "macaddress": "68572D*", + }, + { + "domain": "tuya", + "macaddress": "708976*", + }, + { + "domain": "tuya", + "macaddress": "7CF666*", + }, + { + "domain": "tuya", + "macaddress": "84E342*", + }, + { + "domain": "tuya", + "macaddress": "D4A651*", + }, + { + "domain": "tuya", + "macaddress": "D81F12*", + }, + { + "domain": "twinkly", + "hostname": "twinkly_*", + }, + { + "domain": "unifiprotect", + "macaddress": "B4FBE4*", + }, + { + "domain": "unifiprotect", + "macaddress": "802AA8*", + }, + { + "domain": "unifiprotect", + "macaddress": "F09FC2*", + }, + { + "domain": "unifiprotect", + "macaddress": "68D79A*", + }, + { + "domain": "unifiprotect", + "macaddress": "18E829*", + }, + { + "domain": "unifiprotect", + "macaddress": "245A4C*", + }, + { + "domain": "unifiprotect", + "macaddress": "784558*", + }, + { + "domain": "unifiprotect", + "macaddress": "E063DA*", + }, + { + "domain": "unifiprotect", + "macaddress": "265A4C*", + }, + { + "domain": "unifiprotect", + "macaddress": "74ACB9*", + }, + { + "domain": "verisure", + "macaddress": "0023C1*", + }, + { + "domain": "vicare", + "macaddress": "B87424*", + }, + { + "domain": "wiz", + "registered_devices": True, + }, + { + "domain": "wiz", + "macaddress": "A8BB50*", + }, + { + "domain": "wiz", + "macaddress": "D8A011*", + }, + { + "domain": "wiz", + "macaddress": "444F8E*", + }, + { + "domain": "wiz", + "macaddress": "6C2990*", + }, + { + "domain": "wiz", + "hostname": "wiz_*", + }, + { + "domain": "yeelight", + "hostname": "yeelink-*", + }, ] diff --git a/homeassistant/generated/mqtt.py b/homeassistant/generated/mqtt.py index 7c4203eaec2..4d4e47669c2 100644 --- a/homeassistant/generated/mqtt.py +++ b/homeassistant/generated/mqtt.py @@ -1,4 +1,4 @@ -"""Automatically generated by hassfest. +"""This file is automatically generated. To update, run python3 -m script.hassfest """ diff --git a/homeassistant/generated/ssdp.py b/homeassistant/generated/ssdp.py index c77f3f6a68b..210c0c832a2 100644 --- a/homeassistant/generated/ssdp.py +++ b/homeassistant/generated/ssdp.py @@ -1,4 +1,4 @@ -"""Automatically generated by hassfest. +"""This file is automatically generated. To update, run python3 -m script.hassfest """ diff --git a/homeassistant/generated/usb.py b/homeassistant/generated/usb.py index 59b59bb7604..2d0dced8965 100644 --- a/homeassistant/generated/usb.py +++ b/homeassistant/generated/usb.py @@ -1,14 +1,14 @@ -"""Automatically generated by hassfest. +"""This file is automatically generated. To update, run python3 -m script.hassfest """ USB = [ { - "domain": "homeassistant_sky_connect", - "vid": "10C4", - "pid": "EA60", "description": "*skyconnect v1.0*", + "domain": "homeassistant_sky_connect", + "pid": "EA60", + "vid": "10C4", }, { "domain": "insteon", @@ -16,104 +16,104 @@ USB = [ }, { "domain": "modem_callerid", - "vid": "0572", "pid": "1340", + "vid": "0572", }, { "domain": "velbus", - "vid": "10CF", "pid": "0B1B", + "vid": "10CF", }, { "domain": "velbus", - "vid": "10CF", "pid": "0516", + "vid": "10CF", }, { "domain": "velbus", - "vid": "10CF", "pid": "0517", + "vid": "10CF", }, { "domain": "velbus", - "vid": "10CF", "pid": "0518", + "vid": "10CF", }, { - "domain": "zha", - "vid": "10C4", - "pid": "EA60", "description": "*2652*", + "domain": "zha", + "pid": "EA60", + "vid": "10C4", }, { + "description": "*sonoff*plus*", "domain": "zha", - "vid": "1A86", "pid": "55D4", - "description": "*sonoff*plus*", - }, - { - "domain": "zha", - "vid": "10C4", - "pid": "EA60", - "description": "*sonoff*plus*", - }, - { - "domain": "zha", - "vid": "10C4", - "pid": "EA60", - "description": "*tubeszb*", - }, - { - "domain": "zha", "vid": "1A86", - "pid": "7523", - "description": "*tubeszb*", }, { + "description": "*sonoff*plus*", + "domain": "zha", + "pid": "EA60", + "vid": "10C4", + }, + { + "description": "*tubeszb*", + "domain": "zha", + "pid": "EA60", + "vid": "10C4", + }, + { + "description": "*tubeszb*", "domain": "zha", - "vid": "1A86", "pid": "7523", + "vid": "1A86", + }, + { "description": "*zigstar*", + "domain": "zha", + "pid": "7523", + "vid": "1A86", }, { - "domain": "zha", - "vid": "1CF1", - "pid": "0030", "description": "*conbee*", + "domain": "zha", + "pid": "0030", + "vid": "1CF1", }, { - "domain": "zha", - "vid": "10C4", - "pid": "8A2A", "description": "*zigbee*", - }, - { "domain": "zha", - "vid": "0403", - "pid": "6015", - "description": "*zigate*", - }, - { - "domain": "zha", - "vid": "10C4", - "pid": "EA60", - "description": "*zigate*", - }, - { - "domain": "zha", - "vid": "10C4", - "pid": "8B34", - "description": "*bv 2010/10*", - }, - { - "domain": "zwave_js", - "vid": "0658", - "pid": "0200", - }, - { - "domain": "zwave_js", - "vid": "10C4", "pid": "8A2A", + "vid": "10C4", + }, + { + "description": "*zigate*", + "domain": "zha", + "pid": "6015", + "vid": "0403", + }, + { + "description": "*zigate*", + "domain": "zha", + "pid": "EA60", + "vid": "10C4", + }, + { + "description": "*bv 2010/10*", + "domain": "zha", + "pid": "8B34", + "vid": "10C4", + }, + { + "domain": "zwave_js", + "pid": "0200", + "vid": "0658", + }, + { "description": "*z-wave*", + "domain": "zwave_js", + "pid": "8A2A", + "vid": "10C4", }, ] diff --git a/homeassistant/generated/zeroconf.py b/homeassistant/generated/zeroconf.py index fc0c3ea5fa7..0d505bd2409 100644 --- a/homeassistant/generated/zeroconf.py +++ b/homeassistant/generated/zeroconf.py @@ -1,8 +1,70 @@ -"""Automatically generated by hassfest. +"""This file is automatically generated. To update, run python3 -m script.hassfest """ +HOMEKIT = { + "3810X": "roku", + "3820X": "roku", + "4660X": "roku", + "7820X": "roku", + "819LMB": "myq", + "AC02": "tado", + "Abode": "abode", + "BSB002": "hue", + "C105X": "roku", + "C135X": "roku", + "EB-*": "ecobee", + "Escea": "escea", + "HHKBridge*": "hive", + "Healty Home Coach": "netatmo", + "Iota": "abode", + "LIFX A19": "lifx", + "LIFX BR30": "lifx", + "LIFX Beam": "lifx", + "LIFX Candle": "lifx", + "LIFX Clean": "lifx", + "LIFX Color": "lifx", + "LIFX DLCOL": "lifx", + "LIFX DLWW": "lifx", + "LIFX Dlight": "lifx", + "LIFX Downlight": "lifx", + "LIFX Filament": "lifx", + "LIFX GU10": "lifx", + "LIFX Lightstrip": "lifx", + "LIFX Mini": "lifx", + "LIFX Nightvision": "lifx", + "LIFX Pls": "lifx", + "LIFX Plus": "lifx", + "LIFX Tile": "lifx", + "LIFX White": "lifx", + "LIFX Z": "lifx", + "MYQ": "myq", + "NL29": "nanoleaf", + "NL42": "nanoleaf", + "NL47": "nanoleaf", + "NL48": "nanoleaf", + "NL52": "nanoleaf", + "NL59": "nanoleaf", + "Netatmo Relay": "netatmo", + "PowerView": "hunterdouglas_powerview", + "Presence": "netatmo", + "Rachio": "rachio", + "SPK5": "rainmachine", + "Sensibo": "sensibo", + "Smart Bridge": "lutron_caseta", + "Socket": "wemo", + "TRADFRI": "tradfri", + "Touch HD": "rainmachine", + "Welcome": "netatmo", + "Wemo": "wemo", + "YL*": "yeelight", + "ecobee*": "ecobee", + "iSmartGate": "gogogate2", + "iZone": "izone", + "tado": "tado", +} + ZEROCONF = { "_Volumio._tcp.local.": [ { @@ -436,65 +498,3 @@ ZEROCONF = { }, ], } - -HOMEKIT = { - "3810X": "roku", - "3820X": "roku", - "4660X": "roku", - "7820X": "roku", - "819LMB": "myq", - "AC02": "tado", - "Abode": "abode", - "BSB002": "hue", - "C105X": "roku", - "C135X": "roku", - "EB-*": "ecobee", - "Escea": "escea", - "HHKBridge*": "hive", - "Healty Home Coach": "netatmo", - "Iota": "abode", - "LIFX A19": "lifx", - "LIFX BR30": "lifx", - "LIFX Beam": "lifx", - "LIFX Candle": "lifx", - "LIFX Clean": "lifx", - "LIFX Color": "lifx", - "LIFX DLCOL": "lifx", - "LIFX DLWW": "lifx", - "LIFX Dlight": "lifx", - "LIFX Downlight": "lifx", - "LIFX Filament": "lifx", - "LIFX GU10": "lifx", - "LIFX Lightstrip": "lifx", - "LIFX Mini": "lifx", - "LIFX Nightvision": "lifx", - "LIFX Pls": "lifx", - "LIFX Plus": "lifx", - "LIFX Tile": "lifx", - "LIFX White": "lifx", - "LIFX Z": "lifx", - "MYQ": "myq", - "NL29": "nanoleaf", - "NL42": "nanoleaf", - "NL47": "nanoleaf", - "NL48": "nanoleaf", - "NL52": "nanoleaf", - "NL59": "nanoleaf", - "Netatmo Relay": "netatmo", - "PowerView": "hunterdouglas_powerview", - "Presence": "netatmo", - "Rachio": "rachio", - "SPK5": "rainmachine", - "Sensibo": "sensibo", - "Smart Bridge": "lutron_caseta", - "Socket": "wemo", - "TRADFRI": "tradfri", - "Touch HD": "rainmachine", - "Welcome": "netatmo", - "Wemo": "wemo", - "YL*": "yeelight", - "ecobee*": "ecobee", - "iSmartGate": "gogogate2", - "iZone": "izone", - "tado": "tado", -} diff --git a/script/currencies.py b/script/currencies.py index 2e538ff7c97..753b3363626 100644 --- a/script/currencies.py +++ b/script/currencies.py @@ -1,55 +1,44 @@ """Helper script to update currency list from the official source.""" -import pathlib +from pathlib import Path -import black from bs4 import BeautifulSoup import requests -BASE = """ -\"\"\"Automatically generated by currencies.py. - -To update, run python3 -m script.currencies -\"\"\" - -ACTIVE_CURRENCIES = {{ {} }} - -HISTORIC_CURRENCIES = {{ {} }} -""".strip() +from .hassfest.serializer import format_python_namespace req = requests.get( "https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/lists/list-one.xml" ) soup = BeautifulSoup(req.content, "xml") -active_currencies = sorted( - { - x.Ccy.contents[0] - for x in soup.ISO_4217.CcyTbl.children - if x.name == "CcyNtry" - and x.Ccy - and x.CcyMnrUnts.contents[0] != "N.A." - and "IsFund" not in x.CcyNm.attrs - and x.Ccy.contents[0] != "UYW" - } -) +active_currencies = { + x.Ccy.contents[0] + for x in soup.ISO_4217.CcyTbl.children + if x.name == "CcyNtry" + and x.Ccy + and x.CcyMnrUnts.contents[0] != "N.A." + and "IsFund" not in x.CcyNm.attrs + and x.Ccy.contents[0] != "UYW" +} req = requests.get( "https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/lists/list-three.xml" ) soup = BeautifulSoup(req.content, "xml") -historic_currencies = sorted( - { - x.Ccy.contents[0] - for x in soup.ISO_4217.HstrcCcyTbl.children - if x.name == "HstrcCcyNtry" - and x.Ccy - and "IsFund" not in x.CcyNm.attrs - and x.Ccy.contents[0] not in active_currencies - } -) +historic_currencies = { + x.Ccy.contents[0] + for x in soup.ISO_4217.HstrcCcyTbl.children + if x.name == "HstrcCcyNtry" + and x.Ccy + and "IsFund" not in x.CcyNm.attrs + and x.Ccy.contents[0] not in active_currencies +} -pathlib.Path("homeassistant/generated/currencies.py").write_text( - black.format_str( - BASE.format(repr(active_currencies)[1:-1], repr(historic_currencies)[1:-1]), - mode=black.Mode(), +Path("homeassistant/generated/currencies.py").write_text( + format_python_namespace( + { + "ACTIVE_CURRENCIES": active_currencies, + "HISTORIC_CURRENCIES": historic_currencies, + }, + generator="script.currencies", ) ) diff --git a/script/hassfest/application_credentials.py b/script/hassfest/application_credentials.py index 2fb693bf429..aed8b892f50 100644 --- a/script/hassfest/application_credentials.py +++ b/script/hassfest/application_credentials.py @@ -1,19 +1,8 @@ """Generate application_credentials data.""" from __future__ import annotations -import black - from .model import Config, Integration -from .serializer import to_string - -BASE = """ -\"\"\"Automatically generated by hassfest. - -To update, run python3 -m script.hassfest -\"\"\" - -APPLICATION_CREDENTIALS = {} -""".strip() +from .serializer import format_python_namespace def generate_and_validate(integrations: dict[str, Integration], config: Config) -> str: @@ -29,7 +18,7 @@ def generate_and_validate(integrations: dict[str, Integration], config: Config) match_list.append(domain) - return black.format_str(BASE.format(to_string(match_list)), mode=black.Mode()) + return format_python_namespace({"APPLICATION_CREDENTIALS": match_list}) def validate(integrations: dict[str, Integration], config: Config) -> None: diff --git a/script/hassfest/bluetooth.py b/script/hassfest/bluetooth.py index 0b57b1084e8..57772edd7f4 100644 --- a/script/hassfest/bluetooth.py +++ b/script/hassfest/bluetooth.py @@ -1,20 +1,8 @@ """Generate bluetooth file.""" from __future__ import annotations -import black - from .model import Config, Integration -from .serializer import to_string - -BASE = """ -\"\"\"Automatically generated by hassfest. - -To update, run python3 -m script.hassfest -\"\"\" -from __future__ import annotations - -BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = {} -""".strip() +from .serializer import format_python_namespace def generate_and_validate(integrations: list[dict[str, str]]): @@ -35,7 +23,10 @@ def generate_and_validate(integrations: list[dict[str, str]]): for entry in match_types: match_list.append({"domain": domain, **entry}) - return black.format_str(BASE.format(to_string(match_list)), mode=black.Mode()) + return format_python_namespace( + {"BLUETOOTH": match_list}, + annotations={"BLUETOOTH": "list[dict[str, bool | str | int | list[int]]]"}, + ) def validate(integrations: dict[str, Integration], config: Config): diff --git a/script/hassfest/config_flow.py b/script/hassfest/config_flow.py index 9cebb37d371..cf782a413b8 100644 --- a/script/hassfest/config_flow.py +++ b/script/hassfest/config_flow.py @@ -4,20 +4,9 @@ from __future__ import annotations import json import pathlib -import black - from .brand import validate as validate_brands from .model import Brand, Config, Integration -from .serializer import to_string - -BASE = """ -\"\"\"Automatically generated by hassfest. - -To update, run python3 -m script.hassfest -\"\"\" - -FLOWS = {} -""".strip() +from .serializer import format_python_namespace UNIQUE_ID_IGNORE = {"huawei_lte", "mqtt", "adguard"} @@ -91,7 +80,7 @@ def _generate_and_validate(integrations: dict[str, Integration], config: Config) else: domains["integration"].append(domain) - return black.format_str(BASE.format(to_string(domains)), mode=black.Mode()) + return format_python_namespace({"FLOWS": domains}) def _populate_brand_integrations( diff --git a/script/hassfest/dhcp.py b/script/hassfest/dhcp.py index c246acec5f0..992e1f615a1 100644 --- a/script/hassfest/dhcp.py +++ b/script/hassfest/dhcp.py @@ -1,19 +1,8 @@ """Generate dhcp file.""" from __future__ import annotations -import black - from .model import Config, Integration - -BASE = """ -\"\"\"Automatically generated by hassfest. - -To update, run python3 -m script.hassfest -\"\"\" -from __future__ import annotations - -DHCP: list[dict[str, str | bool]] = {} -""".strip() +from .serializer import format_python_namespace def generate_and_validate(integrations: list[dict[str, str]]): @@ -34,7 +23,10 @@ def generate_and_validate(integrations: list[dict[str, str]]): for entry in match_types: match_list.append({"domain": domain, **entry}) - return black.format_str(BASE.format(str(match_list)), mode=black.Mode()) + return format_python_namespace( + {"DHCP": match_list}, + annotations={"DHCP": "list[dict[str, str | bool]]"}, + ) def validate(integrations: dict[str, Integration], config: Config): diff --git a/script/hassfest/mqtt.py b/script/hassfest/mqtt.py index ab5f159026e..46ba4dfcf44 100644 --- a/script/hassfest/mqtt.py +++ b/script/hassfest/mqtt.py @@ -3,19 +3,8 @@ from __future__ import annotations from collections import defaultdict -import black - from .model import Config, Integration -from .serializer import to_string - -BASE = """ -\"\"\"Automatically generated by hassfest. - -To update, run python3 -m script.hassfest -\"\"\" - -MQTT = {} -""".strip() +from .serializer import format_python_namespace def generate_and_validate(integrations: dict[str, Integration]): @@ -37,7 +26,7 @@ def generate_and_validate(integrations: dict[str, Integration]): for topic in mqtt: data[domain].append(topic) - return black.format_str(BASE.format(to_string(data)), mode=black.Mode()) + return format_python_namespace({"MQTT": data}) def validate(integrations: dict[str, Integration], config: Config): diff --git a/script/hassfest/serializer.py b/script/hassfest/serializer.py index 8a6f410c345..5ba386148f3 100644 --- a/script/hassfest/serializer.py +++ b/script/hassfest/serializer.py @@ -1,37 +1,94 @@ """Hassfest utils.""" from __future__ import annotations +from collections.abc import Collection, Iterable, Mapping from typing import Any +import black -def _dict_to_str(data: dict) -> str: - """Return a string representation of a dict.""" - items = [f"'{key}':{to_string(value)}" for key, value in data.items()] - result = "{" - for item in items: - result += str(item) - result += "," - result += "}" - return result +DEFAULT_GENERATOR = "script.hassfest" -def _list_to_str(data: dict) -> str: - """Return a string representation of a list.""" - items = [to_string(value) for value in data] - result = "[" - for item in items: - result += str(item) - result += "," - result += "]" - return result +def _wrap_items( + items: Iterable[str], + opener: str, + closer: str, + sort=False, +) -> str: + """Wrap pre-formatted Python reprs in braces, optionally sorting them.""" + # The trailing comma is imperative so Black doesn't format some items + # on one line and some on multiple. + if sort: + items = sorted(items) + return f"{opener}{','.join(items)},{closer}" + + +def _mapping_to_str(data: Mapping) -> str: + """Return a string representation of a mapping.""" + return _wrap_items( + (f"{to_string(key)}:{to_string(value)}" for key, value in data.items()), + opener="{", + closer="}", + sort=True, + ) + + +def _collection_to_str( + data: Collection, opener: str = "[", closer: str = "]", sort=False +) -> str: + """Return a string representation of a collection.""" + items = (to_string(value) for value in data) + return _wrap_items(items, opener, closer, sort=sort) def to_string(data: Any) -> str: """Return a string representation of the input.""" if isinstance(data, dict): - return _dict_to_str(data) + return _mapping_to_str(data) if isinstance(data, list): - return _list_to_str(data) - if isinstance(data, str): - return "'" + data + "'" - return data + return _collection_to_str(data) + if isinstance(data, set): + return _collection_to_str(data, "{", "}", sort=True) + return repr(data) + + +def format_python( + content: str, + *, + generator: str = DEFAULT_GENERATOR, +) -> str: + """Format Python code with Black. Optionally prepend a generator comment.""" + if generator: + content = f"""\"\"\"This file is automatically generated. + +To update, run python3 -m {generator} +\"\"\" + +{content} +""" + return black.format_str(content.strip(), mode=black.Mode()) + + +def format_python_namespace( + content: dict[str, Any], + *, + annotations: dict[str, str] | None = None, + generator: str = DEFAULT_GENERATOR, +) -> str: + """Generate a nicely formatted "namespace" file. + + The keys of the `content` dict will be used as variable names. + """ + + def _get_annotation(key: str) -> str: + annotation = (annotations or {}).get(key) + return f": {annotation}" if annotation else "" + + code = "\n\n".join( + f"{key}{_get_annotation(key)}" f" = {to_string(value)}" + for key, value in sorted(content.items()) + ) + if annotations: + # If we had any annotations, add the __future__ import. + code = f"from __future__ import annotations\n{code}" + return format_python(code, generator=generator) diff --git a/script/hassfest/ssdp.py b/script/hassfest/ssdp.py index 599746e9874..cbe0c3ee76f 100644 --- a/script/hassfest/ssdp.py +++ b/script/hassfest/ssdp.py @@ -3,24 +3,8 @@ from __future__ import annotations from collections import defaultdict -import black - from .model import Config, Integration -from .serializer import to_string - -BASE = """ -\"\"\"Automatically generated by hassfest. - -To update, run python3 -m script.hassfest -\"\"\" - -SSDP = {} -""".strip() - - -def sort_dict(value): - """Sort a dictionary.""" - return {key: value[key] for key in sorted(value)} +from .serializer import format_python_namespace def generate_and_validate(integrations: dict[str, Integration]): @@ -40,9 +24,9 @@ def generate_and_validate(integrations: dict[str, Integration]): continue for matcher in ssdp: - data[domain].append(sort_dict(matcher)) + data[domain].append(matcher) - return black.format_str(BASE.format(to_string(data)), mode=black.Mode()) + return format_python_namespace({"SSDP": data}) def validate(integrations: dict[str, Integration], config: Config): diff --git a/script/hassfest/usb.py b/script/hassfest/usb.py index e71966d548a..3e5ce6e3963 100644 --- a/script/hassfest/usb.py +++ b/script/hassfest/usb.py @@ -1,19 +1,8 @@ """Generate usb file.""" from __future__ import annotations -import black - from .model import Config, Integration -from .serializer import to_string - -BASE = """ -\"\"\"Automatically generated by hassfest. - -To update, run python3 -m script.hassfest -\"\"\" - -USB = {} -""".strip() +from .serializer import format_python_namespace def generate_and_validate(integrations: list[dict[str, str]]) -> str: @@ -39,7 +28,7 @@ def generate_and_validate(integrations: list[dict[str, str]]) -> str: } ) - return black.format_str(BASE.format(to_string(match_list)), mode=black.Mode()) + return format_python_namespace({"USB": match_list}) def validate(integrations: dict[str, Integration], config: Config) -> None: diff --git a/script/hassfest/zeroconf.py b/script/hassfest/zeroconf.py index 939da08319a..0c372035bdc 100644 --- a/script/hassfest/zeroconf.py +++ b/script/hassfest/zeroconf.py @@ -3,23 +3,10 @@ from __future__ import annotations from collections import defaultdict -import black - from homeassistant.loader import async_process_zeroconf_match_dict from .model import Config, Integration -from .serializer import to_string - -BASE = """ -\"\"\"Automatically generated by hassfest. - -To update, run python3 -m script.hassfest -\"\"\" - -ZEROCONF = {} - -HOMEKIT = {} -""".strip() +from .serializer import format_python_namespace def generate_and_validate(integrations: dict[str, Integration]): @@ -82,11 +69,11 @@ def generate_and_validate(integrations: dict[str, Integration]): warned.add(key_2) break - zeroconf = {key: service_type_dict[key] for key in sorted(service_type_dict)} - homekit = {key: homekit_dict[key] for key in sorted(homekit_dict)} - - return black.format_str( - BASE.format(to_string(zeroconf), to_string(homekit)), mode=black.Mode() + return format_python_namespace( + { + "HOMEKIT": {key: homekit_dict[key] for key in homekit_dict}, + "ZEROCONF": {key: service_type_dict[key] for key in service_type_dict}, + } ) From 438184a43cf212d05db94a560269267324d65c9e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 9 Nov 2022 10:01:12 -0600 Subject: [PATCH 0332/1033] Bump aiohomekit to 2.2.19 (#81867) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index b18f35390b7..f0438a7b841 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.18"], + "requirements": ["aiohomekit==2.2.19"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index d0951302aff..d939123b1a9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.18 +aiohomekit==2.2.19 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b34ef5074d4..a52ca259d5e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.18 +aiohomekit==2.2.19 # homeassistant.components.emulated_hue # homeassistant.components.http From b18c558a803e58a638c56a375ca46b0a81e7f194 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 9 Nov 2022 10:21:31 -0600 Subject: [PATCH 0333/1033] Bump oralb-ble to 0.14.1 (#81869) --- homeassistant/components/oralb/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/oralb/manifest.json b/homeassistant/components/oralb/manifest.json index 1738558770e..eff6c999c30 100644 --- a/homeassistant/components/oralb/manifest.json +++ b/homeassistant/components/oralb/manifest.json @@ -8,7 +8,7 @@ "manufacturer_id": 220 } ], - "requirements": ["oralb-ble==0.13.0"], + "requirements": ["oralb-ble==0.14.1"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index d939123b1a9..2fa5e7d4f8b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1255,7 +1255,7 @@ openwrt-luci-rpc==1.1.11 openwrt-ubus-rpc==0.0.2 # homeassistant.components.oralb -oralb-ble==0.13.0 +oralb-ble==0.14.1 # homeassistant.components.oru oru==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a52ca259d5e..07a299c70b8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -900,7 +900,7 @@ open-meteo==0.2.1 openerz-api==0.1.0 # homeassistant.components.oralb -oralb-ble==0.13.0 +oralb-ble==0.14.1 # homeassistant.components.ovo_energy ovoenergy==1.2.0 From 0941ed076ccafe36e2e8ffbdc480cea4f8fa5650 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 9 Nov 2022 11:59:00 -0800 Subject: [PATCH 0334/1033] Cleanup unnecessary google calendar test fixtures (#81876) --- tests/components/google/test_calendar.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/components/google/test_calendar.py b/tests/components/google/test_calendar.py index 90ec8f44850..79eff393cc7 100644 --- a/tests/components/google/test_calendar.py +++ b/tests/components/google/test_calendar.py @@ -301,15 +301,12 @@ async def test_missing_summary(hass, mock_events_list_items, component_setup): async def test_update_error( hass, component_setup, - mock_calendars_list, mock_events_list, - test_api_calendar, aioclient_mock, ): """Test that the calendar update handles a server error.""" now = dt_util.now() - mock_calendars_list({"items": [test_api_calendar]}) mock_events_list( { "items": [ @@ -516,7 +513,6 @@ async def test_opaque_event( async def test_scan_calendar_error( hass, component_setup, - test_api_calendar, mock_calendars_list, config_entry, ): From adf84b0c62c4d6472d589b086162563f4f27a854 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 9 Nov 2022 15:36:50 -0700 Subject: [PATCH 0335/1033] Add `async_get_active_reauth_flows` helper for config entries (#81881) * Add `async_get_active_reauth_flows` helper for config entries * Code review * Code review + tests --- .../components/openuv/coordinator.py | 21 ++++-------- homeassistant/config_entries.py | 22 +++++++++---- tests/test_config_entries.py | 32 +++++++++++++++++++ 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/openuv/coordinator.py b/homeassistant/components/openuv/coordinator.py index 36267972f80..1df6a91b398 100644 --- a/homeassistant/components/openuv/coordinator.py +++ b/homeassistant/components/openuv/coordinator.py @@ -7,13 +7,13 @@ from typing import Any, cast from pyopenuv.errors import InvalidApiKeyError, OpenUvError -from homeassistant.config_entries import ConfigEntry +from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .const import DOMAIN, LOGGER +from .const import LOGGER DEFAULT_DEBOUNCER_COOLDOWN_SECONDS = 15 * 60 @@ -56,19 +56,10 @@ class ReauthFlowManager: @callback def _get_active_reauth_flow(self) -> FlowResult | None: """Get an active reauth flow (if it exists).""" - try: - [reauth_flow] = [ - flow - for flow in self.hass.config_entries.flow.async_progress_by_handler( - DOMAIN - ) - if flow["context"]["source"] == "reauth" - and flow["context"]["entry_id"] == self.entry.entry_id - ] - except ValueError: - return None - - return reauth_flow + return next( + iter(self.entry.async_get_active_flows(self.hass, {SOURCE_REAUTH})), + None, + ) @callback def cancel_reauth(self) -> None: diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 902fa0d03f2..ddef5d7f226 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -3,7 +3,7 @@ from __future__ import annotations import asyncio from collections import ChainMap -from collections.abc import Callable, Coroutine, Iterable, Mapping +from collections.abc import Callable, Coroutine, Generator, Iterable, Mapping from contextvars import ContextVar from enum import Enum import functools @@ -19,6 +19,7 @@ from .backports.enum import StrEnum from .components import persistent_notification from .const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP, Platform from .core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback +from .data_entry_flow import FlowResult from .exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady, HomeAssistantError from .helpers import device_registry, entity_registry, storage from .helpers.dispatcher import async_dispatcher_connect, async_dispatcher_send @@ -662,12 +663,7 @@ class ConfigEntry: data: dict[str, Any] | None = None, ) -> None: """Start a reauth flow.""" - if any( - flow - for flow in hass.config_entries.flow.async_progress_by_handler(self.domain) - if flow["context"].get("source") == SOURCE_REAUTH - and flow["context"].get("entry_id") == self.entry_id - ): + if any(self.async_get_active_flows(hass, {SOURCE_REAUTH})): # Reauth flow already in progress for this entry return @@ -685,6 +681,18 @@ class ConfigEntry: ) ) + @callback + def async_get_active_flows( + self, hass: HomeAssistant, sources: set[str] + ) -> Generator[FlowResult, None, None]: + """Get any active flows of certain sources for this entry.""" + return ( + flow + for flow in hass.config_entries.flow.async_progress_by_handler(self.domain) + if flow["context"].get("source") in sources + and flow["context"].get("entry_id") == self.entry_id + ) + @callback def async_create_task( self, hass: HomeAssistant, target: Coroutine[Any, Any, _R] diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 96d032f771e..28c3f9c2803 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -3429,3 +3429,35 @@ async def test_wait_for_loading_timeout(hass: HomeAssistant) -> None: }, timeout=0.1, ) + + +async def test_get_active_flows(hass): + """Test the async_get_active_flows helper.""" + entry = MockConfigEntry(title="test_title", domain="test") + mock_setup_entry = AsyncMock(return_value=True) + mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry)) + mock_entity_platform(hass, "config_flow.test", None) + + await entry.async_setup(hass) + await hass.async_block_till_done() + + flow = hass.config_entries.flow + with patch.object(flow, "async_init", wraps=flow.async_init): + entry.async_start_reauth( + hass, + context={"extra_context": "some_extra_context"}, + data={"extra_data": 1234}, + ) + await hass.async_block_till_done() + + # Check that there's an active reauth flow: + active_reauth_flow = next( + iter(entry.async_get_active_flows(hass, {config_entries.SOURCE_REAUTH})), None + ) + assert active_reauth_flow is not None + + # Check that there isn't any other flow (in this case, a user flow): + active_user_flow = next( + iter(entry.async_get_active_flows(hass, {config_entries.SOURCE_USER})), None + ) + assert active_user_flow is None From 7aa4654eb499c3c1f50179f108017c62374a2e47 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 9 Nov 2022 23:59:58 +0100 Subject: [PATCH 0336/1033] Improve docstring for sensor testcase (#81875) --- tests/components/sensor/test_recorder.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/components/sensor/test_recorder.py b/tests/components/sensor/test_recorder.py index 05f8bd40597..f8bdf7acf87 100644 --- a/tests/components/sensor/test_recorder.py +++ b/tests/components/sensor/test_recorder.py @@ -1729,7 +1729,11 @@ def test_compile_hourly_statistics_partially_unavailable(hass_recorder, caplog): def test_compile_hourly_statistics_unavailable( hass_recorder, caplog, device_class, state_unit, value ): - """Test compiling hourly statistics, with the sensor being unavailable.""" + """Test compiling hourly statistics, with one sensor being unavailable. + + sensor.test1 is unavailable and should not have statistics generated + sensor.test2 should have statistics generated + """ zero = dt_util.utcnow() hass = hass_recorder() setup_component(hass, "sensor", {}) From 8b4dbbe593d1c37ebef5678340d8876ded987bed Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 10 Nov 2022 00:27:41 +0000 Subject: [PATCH 0337/1033] [ci skip] Translation update --- .../components/august/translations/bg.json | 2 +- .../components/blink/translations/bg.json | 2 +- .../components/braviatv/translations/bg.json | 6 ++-- .../components/deconz/translations/bg.json | 18 +++++----- .../devolo_home_network/translations/bg.json | 2 +- .../google_travel_time/translations/bg.json | 2 +- .../homeassistant/translations/de.json | 6 ++++ .../homeassistant/translations/id.json | 6 ++++ .../huawei_lte/translations/bg.json | 4 +-- .../components/hue/translations/bg.json | 10 ++++++ .../components/lametric/translations/bg.json | 2 +- .../components/livisi/translations/ca.json | 16 +++++++++ .../components/livisi/translations/id.json | 18 ++++++++++ .../components/mqtt/translations/bg.json | 13 +++++++ .../components/mqtt/translations/id.json | 10 +++--- .../components/mqtt/translations/ru.json | 2 +- .../components/nam/translations/bg.json | 2 +- .../nibe_heatpump/translations/ca.json | 12 ++++++- .../nibe_heatpump/translations/de.json | 34 ++++++++++++++++++- .../nibe_heatpump/translations/id.json | 27 ++++++++++++++- .../nibe_heatpump/translations/no.json | 34 ++++++++++++++++++- .../nibe_heatpump/translations/zh-Hant.json | 34 ++++++++++++++++++- .../components/openuv/translations/bg.json | 9 ++++- .../components/openuv/translations/ca.json | 10 +++++- .../components/openuv/translations/de.json | 10 +++++- .../components/openuv/translations/id.json | 10 +++++- .../components/openuv/translations/no.json | 10 +++++- .../components/openuv/translations/pl.json | 10 +++++- .../openuv/translations/zh-Hant.json | 10 +++++- .../ovo_energy/translations/bg.json | 2 +- .../components/ring/translations/bg.json | 2 +- .../components/sensor/translations/bg.json | 2 +- .../components/shelly/translations/bg.json | 2 +- .../components/subaru/translations/bg.json | 2 +- .../totalconnect/translations/bg.json | 2 +- .../translations/bg.json | 2 +- .../unifiprotect/translations/ca.json | 4 +-- .../components/zha/translations/bg.json | 34 +++++++++++-------- 38 files changed, 323 insertions(+), 60 deletions(-) create mode 100644 homeassistant/components/livisi/translations/ca.json create mode 100644 homeassistant/components/livisi/translations/id.json diff --git a/homeassistant/components/august/translations/bg.json b/homeassistant/components/august/translations/bg.json index 7c3899abed7..110c7ee5598 100644 --- a/homeassistant/components/august/translations/bg.json +++ b/homeassistant/components/august/translations/bg.json @@ -11,7 +11,7 @@ } }, "validation": { - "title": "\u0414\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + "title": "\u0414\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" } } } diff --git a/homeassistant/components/blink/translations/bg.json b/homeassistant/components/blink/translations/bg.json index 60e7c86f621..c302ce972b9 100644 --- a/homeassistant/components/blink/translations/bg.json +++ b/homeassistant/components/blink/translations/bg.json @@ -14,7 +14,7 @@ "2fa": "\u0414\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u0435\u043d \u043a\u043e\u0434" }, "description": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u041f\u0418\u041d \u043a\u043e\u0434\u0430, \u0438\u0437\u043f\u0440\u0430\u0442\u0435\u043d \u043d\u0430 \u0432\u0430\u0448\u0438\u044f \u0438\u043c\u0435\u0439\u043b", - "title": "\u0414\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + "title": "\u0414\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" }, "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/bg.json b/homeassistant/components/braviatv/translations/bg.json index 3a6908b0177..8df7ac6e1c3 100644 --- a/homeassistant/components/braviatv/translations/bg.json +++ b/homeassistant/components/braviatv/translations/bg.json @@ -4,7 +4,7 @@ "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "not_bravia_device": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u043d\u0435 \u0435 \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440 Bravia.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", - "reauth_unsuccessful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430, \u043c\u043e\u043b\u044f, \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0438 \u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e." + "reauth_unsuccessful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430, \u043c\u043e\u043b\u044f, \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0438 \u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e." }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", @@ -16,7 +16,7 @@ "authorize": { "data": { "pin": "\u041f\u0418\u041d \u043a\u043e\u0434", - "use_psk": "\u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 PSK \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + "use_psk": "\u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 PSK \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" } }, "confirm": { @@ -25,7 +25,7 @@ "reauth_confirm": { "data": { "pin": "\u041f\u0418\u041d \u043a\u043e\u0434", - "use_psk": "\u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 PSK \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + "use_psk": "\u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 PSK \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" } }, "user": { diff --git a/homeassistant/components/deconz/translations/bg.json b/homeassistant/components/deconz/translations/bg.json index b047c361681..8b9ae63a7cd 100644 --- a/homeassistant/components/deconz/translations/bg.json +++ b/homeassistant/components/deconz/translations/bg.json @@ -31,6 +31,7 @@ "device_automation": { "trigger_subtype": { "both_buttons": "\u0418 \u0434\u0432\u0430\u0442\u0430 \u0431\u0443\u0442\u043e\u043d\u0430", + "bottom_buttons": "\u0414\u043e\u043b\u043d\u0438 \u0431\u0443\u0442\u043e\u043d\u0438", "button_1": "\u041f\u044a\u0440\u0432\u0438 \u0431\u0443\u0442\u043e\u043d", "button_2": "\u0412\u0442\u043e\u0440\u0438 \u0431\u0443\u0442\u043e\u043d", "button_3": "\u0422\u0440\u0435\u0442\u0438 \u0431\u0443\u0442\u043e\u043d", @@ -51,21 +52,22 @@ "side_4": "\u0421\u0442\u0440\u0430\u043d\u0430 4", "side_5": "\u0421\u0442\u0440\u0430\u043d\u0430 5", "side_6": "\u0421\u0442\u0440\u0430\u043d\u0430 6", + "top_buttons": "\u0413\u043e\u0440\u043d\u0438 \u0431\u0443\u0442\u043e\u043d\u0438", "turn_off": "\u0418\u0437\u043a\u043b\u044e\u0447\u0438", "turn_on": "\u0412\u043a\u043b\u044e\u0447\u0438" }, "trigger_type": { "remote_awakened": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0441\u0435 \u0441\u044a\u0431\u0443\u0434\u0438", - "remote_button_double_press": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442 \u0434\u0432\u0443\u043a\u0440\u0430\u0442\u043d\u043e", - "remote_button_long_press": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e", - "remote_button_long_release": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043e\u0442\u043f\u0443\u0441\u043d\u0430\u0442 \u0441\u043b\u0435\u0434 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", - "remote_button_quadruple_press": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442 \u0447\u0435\u0442\u0438\u0440\u0438\u043a\u0440\u0430\u0442\u043d\u043e", - "remote_button_quintuple_press": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442 \u043f\u0435\u0442\u043a\u0440\u0430\u0442\u043d\u043e", + "remote_button_double_press": "\"{subtype}\" \u043f\u0440\u0438 \u0434\u0432\u0443\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_long_press": "\"{subtype}\" \u043f\u0440\u0438 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_long_release": "\"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u0441\u043b\u0435\u0434 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_quadruple_press": "\"{subtype}\" \u043f\u0440\u0438 \u0447\u0435\u0442\u0438\u0440\u0438\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_quintuple_press": "\"{subtype}\" \u043f\u0440\u0438 \u043f\u0435\u0442\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", "remote_button_rotated": "\u0417\u0430\u0432\u044a\u0440\u0442\u044f\u043d \u0431\u0443\u0442\u043e\u043d \"{subtype}\"", "remote_button_rotation_stopped": "\u0421\u043f\u0440\u044f \u0432\u044a\u0440\u0442\u0435\u043d\u0435\u0442\u043e \u043d\u0430 \u0431\u0443\u0442\u043e\u043d \"{subtype}\"", - "remote_button_short_press": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442", - "remote_button_short_release": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043e\u0442\u043f\u0443\u0441\u043d\u0430\u0442", - "remote_button_triple_press": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442 \u0442\u0440\u0438\u043a\u0440\u0430\u0442\u043d\u043e", + "remote_button_short_press": "\"{subtype}\" \u043f\u0440\u0438 \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_short_release": "\"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435", + "remote_button_triple_press": "\"{subtype}\" \u043f\u0440\u0438 \u0442\u0440\u0438\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", "remote_double_tap": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \"{subtype}\" \u0435 \u043f\u043e\u0447\u0443\u043a\u0430\u043d\u043e \u0434\u0432\u0430 \u043f\u044a\u0442\u0438", "remote_falling": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u043f\u0430\u0434\u0430", "remote_gyro_activated": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0435 \u0440\u0430\u0437\u043a\u043b\u0430\u0442\u0435\u043d\u043e", diff --git a/homeassistant/components/devolo_home_network/translations/bg.json b/homeassistant/components/devolo_home_network/translations/bg.json index a90c099889a..44d409938a0 100644 --- a/homeassistant/components/devolo_home_network/translations/bg.json +++ b/homeassistant/components/devolo_home_network/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/google_travel_time/translations/bg.json b/homeassistant/components/google_travel_time/translations/bg.json index 3965f9c706c..b599c0d4ded 100644 --- a/homeassistant/components/google_travel_time/translations/bg.json +++ b/homeassistant/components/google_travel_time/translations/bg.json @@ -5,7 +5,7 @@ }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" }, "step": { "user": { diff --git a/homeassistant/components/homeassistant/translations/de.json b/homeassistant/components/homeassistant/translations/de.json index 756670e5a47..61ca66f49a9 100644 --- a/homeassistant/components/homeassistant/translations/de.json +++ b/homeassistant/components/homeassistant/translations/de.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "Die W\u00e4hrung {currency} wird nicht mehr verwendet, bitte konfiguriere die W\u00e4hrungskonfiguration neu.", + "title": "Die konfigurierte W\u00e4hrung ist nicht mehr in Gebrauch" + } + }, "system_health": { "info": { "arch": "CPU-Architektur", diff --git a/homeassistant/components/homeassistant/translations/id.json b/homeassistant/components/homeassistant/translations/id.json index 7c2994d8bbb..0be9cd9f286 100644 --- a/homeassistant/components/homeassistant/translations/id.json +++ b/homeassistant/components/homeassistant/translations/id.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "Mata uang {currency} tidak lagi digunakan, konfigurasikan ulang konfigurasi mata uang.", + "title": "Mata uang yang dikonfigurasi tidak lagi digunakan" + } + }, "system_health": { "info": { "arch": "Arsitektur CPU", diff --git a/homeassistant/components/huawei_lte/translations/bg.json b/homeassistant/components/huawei_lte/translations/bg.json index 8f34e808235..2ecb9564113 100644 --- a/homeassistant/components/huawei_lte/translations/bg.json +++ b/homeassistant/components/huawei_lte/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "not_huawei_lte": "\u041d\u0435 \u0435 Huawei LTE \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "connection_timeout": "\u0412\u0440\u0435\u043c\u0435\u0442\u043e \u0437\u0430 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0438\u0437\u0442\u0435\u0447\u0435", @@ -20,7 +20,7 @@ "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" }, - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" }, "user": { "data": { diff --git a/homeassistant/components/hue/translations/bg.json b/homeassistant/components/hue/translations/bg.json index 242c902fde5..ae6ce66fbc0 100644 --- a/homeassistant/components/hue/translations/bg.json +++ b/homeassistant/components/hue/translations/bg.json @@ -39,13 +39,23 @@ "2": "\u0412\u0442\u043e\u0440\u0438 \u0431\u0443\u0442\u043e\u043d", "3": "\u0422\u0440\u0435\u0442\u0438 \u0431\u0443\u0442\u043e\u043d", "4": "\u0427\u0435\u0442\u0432\u044a\u0440\u0442\u0438 \u0431\u0443\u0442\u043e\u043d", + "button_1": "\u041f\u044a\u0440\u0432\u0438 \u0431\u0443\u0442\u043e\u043d", + "button_2": "\u0412\u0442\u043e\u0440\u0438 \u0431\u0443\u0442\u043e\u043d", "button_3": "\u0422\u0440\u0435\u0442\u0438 \u0431\u0443\u0442\u043e\u043d", + "button_4": "\u0427\u0435\u0442\u0432\u044a\u0440\u0442\u0438 \u0431\u0443\u0442\u043e\u043d", "clock_wise": "\u0412\u044a\u0440\u0442\u0435\u043d\u0435 \u043f\u043e \u0447\u0430\u0441\u043e\u0432\u043d\u0438\u043a\u043e\u0432\u0430\u0442\u0430 \u0441\u0442\u0440\u0435\u043b\u043a\u0430", "counter_clock_wise": "\u0412\u044a\u0440\u0442\u0435\u043d\u0435 \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043d\u0430 \u0447\u0430\u0441\u043e\u0432\u043d\u0438\u043a\u043e\u0432\u0430\u0442\u0430 \u0441\u0442\u0440\u0435\u043b\u043a\u0430", "double_buttons_1_3": "\u041f\u044a\u0440\u0432\u0438 \u0438 \u0442\u0440\u0435\u0442\u0438 \u0431\u0443\u0442\u043e\u043d\u0438", "double_buttons_2_4": "\u0412\u0442\u043e\u0440\u0438 \u0438 \u0447\u0435\u0442\u0432\u044a\u0440\u0442\u0438 \u0431\u0443\u0442\u043e\u043d\u0438" }, "trigger_type": { + "long_release": "\"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u0441\u043b\u0435\u0434 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_long_release": "\"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u0441\u043b\u0435\u0434 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_short_press": "\"{subtype}\" \u043f\u0440\u0438 \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_short_release": "\"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435", + "remote_double_button_long_press": "\u0418 \u0434\u0432\u0430\u0442\u0430 \"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u0441\u043b\u0435\u0434 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_double_button_short_press": "\u0418 \u0434\u0432\u0430\u0442\u0430 \"{subtype}\" \u043f\u0440\u0438 \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "short_release": "\"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u0441\u043b\u0435\u0434 \u043a\u0440\u0430\u0442\u043a\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", "start": "\"{subtype}\" \u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442 \u043f\u044a\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u043d\u043e" } }, diff --git a/homeassistant/components/lametric/translations/bg.json b/homeassistant/components/lametric/translations/bg.json index 6f85c1ddaf5..a886ec4fe9a 100644 --- a/homeassistant/components/lametric/translations/bg.json +++ b/homeassistant/components/lametric/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { diff --git a/homeassistant/components/livisi/translations/ca.json b/homeassistant/components/livisi/translations/ca.json new file mode 100644 index 00000000000..359b7eb01b1 --- /dev/null +++ b/homeassistant/components/livisi/translations/ca.json @@ -0,0 +1,16 @@ +{ + "config": { + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "wrong_password": "La contrasenya \u00e9s incorrecta." + }, + "step": { + "user": { + "data": { + "host": "Adre\u00e7a IP", + "password": "Contrasenya" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/id.json b/homeassistant/components/livisi/translations/id.json new file mode 100644 index 00000000000..8be49b10d35 --- /dev/null +++ b/homeassistant/components/livisi/translations/id.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Gagal terhubung", + "wrong_ip_address": "Alamat IP salah atau SHC tidak dapat dihubungi secara lokal.", + "wrong_password": "Kata sandi salah." + }, + "step": { + "user": { + "data": { + "host": "Alamat IP", + "password": "Kata Sandi" + }, + "description": "Masukkan alamat IP dan kata sandi (lokal) SHC." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mqtt/translations/bg.json b/homeassistant/components/mqtt/translations/bg.json index 06751555b29..c3dd9f6ec2f 100644 --- a/homeassistant/components/mqtt/translations/bg.json +++ b/homeassistant/components/mqtt/translations/bg.json @@ -35,9 +35,22 @@ }, "device_automation": { "trigger_subtype": { + "button_1": "\u041f\u044a\u0440\u0432\u043e \u043a\u043e\u043f\u0447\u0435", + "button_2": "\u0412\u0442\u043e\u0440\u0438 \u0431\u0443\u0442\u043e\u043d", "button_3": "\u0422\u0440\u0435\u0442\u0438 \u0431\u0443\u0442\u043e\u043d", "button_4": "\u0427\u0435\u0442\u0432\u044a\u0440\u0442\u0438 \u0431\u0443\u0442\u043e\u043d", + "button_5": "\u041f\u0435\u0442\u0438 \u0431\u0443\u0442\u043e\u043d", "button_6": "\u0428\u0435\u0441\u0442\u0438 \u0431\u0443\u0442\u043e\u043d" + }, + "trigger_type": { + "button_double_press": "\"{subtype}\" \u043f\u0440\u0438 \u0434\u0432\u0443\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "button_long_press": "\"{subtype}\" \u043f\u0440\u0438 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "button_long_release": "\"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u0441\u043b\u0435\u0434 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "button_quadruple_press": "\"{subtype}\" \u043f\u0440\u0438 \u0447\u0435\u0442\u0438\u0440\u0438\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "button_quintuple_press": "\"{subtype}\" \u043f\u0440\u0438 \u043f\u0435\u0442\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "button_short_press": "\"{subtype}\" \u043f\u0440\u0438 \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "button_short_release": "\"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435", + "button_triple_press": "\"{subtype}\" \u043f\u0440\u0438 \u0442\u0440\u0438\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435" } }, "options": { diff --git a/homeassistant/components/mqtt/translations/id.json b/homeassistant/components/mqtt/translations/id.json index c6c67cea9c7..3f2a6cb18cb 100644 --- a/homeassistant/components/mqtt/translations/id.json +++ b/homeassistant/components/mqtt/translations/id.json @@ -20,10 +20,10 @@ "data": { "advanced_options": "Opsi tingkat lanjut", "broker": "Broker", - "certificate": "Jalur ke file sertifikat CA khusus", - "client_cert": "Jalur ke file sertifikat klien", + "certificate": "Unggah file sertifikat CA khusus", + "client_cert": "Unggah file sertifikat klien", "client_id": "ID Klien (biarkan kosong agar dihasilkan secara acak)", - "client_key": "Jalur ke file kunci pribadi", + "client_key": "Unggah file kunci pribadi", "discovery": "Aktifkan penemuan", "keepalive": "Waktu antara mengirim pesan tetap hidup", "password": "Kata Sandi", @@ -96,9 +96,9 @@ "broker": "Broker", "certificate": "Unggah file sertifikat CA khusus", "client_cert": "Unggah file sertifikat klien", - "client_id": "ID Klien (biarkan kosong agar dibuat secara acak)", + "client_id": "ID Klien (biarkan kosong agar dihasilkan secara acak)", "client_key": "Unggah file kunci pribadi", - "keepalive": "Waktu antara mengirim pesan keep alive", + "keepalive": "Waktu antara mengirim pesan tetap hidup", "password": "Kata Sandi", "port": "Port", "protocol": "Protokol MQTT", diff --git a/homeassistant/components/mqtt/translations/ru.json b/homeassistant/components/mqtt/translations/ru.json index 7e2fe9ffe55..e0de1f14936 100644 --- a/homeassistant/components/mqtt/translations/ru.json +++ b/homeassistant/components/mqtt/translations/ru.json @@ -113,7 +113,7 @@ "birth_qos": "QoS \u0442\u043e\u043f\u0438\u043a\u0430 \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438", "birth_retain": "\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0442\u043e\u043f\u0438\u043a \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438", "birth_topic": "\u0422\u043e\u043f\u0438\u043a \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 (LWT)", - "discovery": "\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435", + "discovery": "\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432", "discovery_prefix": "\u041f\u0440\u0435\u0444\u0438\u043a\u0441 \u0430\u0432\u0442\u043e\u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f", "will_enable": "\u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0442\u043e\u043f\u0438\u043a \u043e\u0431 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438", "will_payload": "\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0442\u043e\u043f\u0438\u043a\u0430 \u043e\u0431 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438", diff --git a/homeassistant/components/nam/translations/bg.json b/homeassistant/components/nam/translations/bg.json index 9be1a75603a..57ae4ea05d0 100644 --- a/homeassistant/components/nam/translations/bg.json +++ b/homeassistant/components/nam/translations/bg.json @@ -4,7 +4,7 @@ "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "device_unsupported": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", - "reauth_unsuccessful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430, \u043c\u043e\u043b\u044f, \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0438 \u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e." + "reauth_unsuccessful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430, \u043c\u043e\u043b\u044f, \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0438 \u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e." }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/nibe_heatpump/translations/ca.json b/homeassistant/components/nibe_heatpump/translations/ca.json index d2924212386..7dcf70cc17d 100644 --- a/homeassistant/components/nibe_heatpump/translations/ca.json +++ b/homeassistant/components/nibe_heatpump/translations/ca.json @@ -12,6 +12,12 @@ "write": "Error en la sol\u00b7licitud d'escriptura a la bomba. Verifica el port remot d'escriptura i/o l'adre\u00e7a IP remota." }, "step": { + "modbus": { + "data": { + "modbus_url": "URL de Modbus", + "model": "Model de bomba de calor" + } + }, "user": { "data": { "ip_address": "Adre\u00e7a remota", @@ -25,7 +31,11 @@ "remote_read_port": "Port on la unitat NibeGW espera les sol\u00b7licituds de lectura.", "remote_write_port": "Port on la unitat NibeGW espera les sol\u00b7licituds d'escriptura." }, - "description": "Abans d'intentar configurar la integraci\u00f3, comprova que:\n - La unitat NibeGW est\u00e0 connectada a una bomba de calor.\n - S'ha activat l'accessori MODBUS40 a la configuraci\u00f3 de la bomba de calor.\n - La bomba no ha entrat en estat d'alarma per falta de l'accessori MODBUS40." + "description": "Abans d'intentar configurar la integraci\u00f3, comprova que:\n - La unitat NibeGW est\u00e0 connectada a una bomba de calor.\n - S'ha activat l'accessori MODBUS40 a la configuraci\u00f3 de la bomba de calor.\n - La bomba no ha entrat en estat d'alarma per falta de l'accessori MODBUS40.", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" + } } } } diff --git a/homeassistant/components/nibe_heatpump/translations/de.json b/homeassistant/components/nibe_heatpump/translations/de.json index 5cddee9d912..e5f98b60bdd 100644 --- a/homeassistant/components/nibe_heatpump/translations/de.json +++ b/homeassistant/components/nibe_heatpump/translations/de.json @@ -9,9 +9,37 @@ "model": "Das ausgew\u00e4hlte Modell scheint modbus40 nicht zu unterst\u00fctzen", "read": "Fehler bei Leseanforderung von Pumpe. \u00dcberpr\u00fcfe deinen \u201eRemote-Leseport\u201c oder \u201eRemote-IP-Adresse\u201c.", "unknown": "Unerwarteter Fehler", + "url": "Die angegebene URL ist keine wohlgeformte und unterst\u00fctzte URL", "write": "Fehler bei Schreibanforderung an Pumpe. \u00dcberpr\u00fcfe deinen \u201eRemote-Schreibport\u201c oder \u201eRemote-IP-Adresse\u201c." }, "step": { + "modbus": { + "data": { + "modbus_unit": "Modbus-Einheitenkennung", + "modbus_url": "Modbus-URL", + "model": "Modell der W\u00e4rmepumpe" + }, + "data_description": { + "modbus_unit": "Ger\u00e4teidentifikation f\u00fcr deine W\u00e4rmepumpe. Kann normalerweise auf 0 belassen werden.", + "modbus_url": "Modbus-URL, die die Verbindung zu deiner W\u00e4rmepumpe oder deinem MODBUS40-Ger\u00e4t beschreibt. Sie sollte in folgender Form sein:\n - `tcp://[HOST]:[PORT]` f\u00fcr eine Modbus TCP-Verbindung\n - `serial://[LOKALES GER\u00c4T]` f\u00fcr eine lokale Modbus RTU-Verbindung\n - `rfc2217://[HOST]:[PORT]` f\u00fcr eine Telnet-basierte Modbus-RTU-Fernverbindung." + } + }, + "nibegw": { + "data": { + "ip_address": "Remote-Adresse", + "listening_port": "Lokaler Leseport", + "model": "Modell der W\u00e4rmepumpe", + "remote_read_port": "Remote-Leseport", + "remote_write_port": "Remote-Schreibport" + }, + "data_description": { + "ip_address": "Die Adresse des NibeGW-Ger\u00e4ts. Das Ger\u00e4t sollte mit einer statischen Adresse konfiguriert worden sein.", + "listening_port": "Der lokale Port auf diesem System, an den das NibeGW-Ger\u00e4t Daten senden soll.", + "remote_read_port": "Der Port, an dem das NibeGW-Ger\u00e4t auf Leseanfragen wartet.", + "remote_write_port": "Der Port, an dem das NibeGW-Ger\u00e4t auf Schreibanfragen wartet." + }, + "description": "Bevor du versuchst, die Integration zu konfigurieren, \u00fcberpr\u00fcfe folgendes:\n - Das NibeGW-Ger\u00e4t ist an eine W\u00e4rmepumpe angeschlossen.\n - Das MODBUS40-Zubeh\u00f6r wurde in der Konfiguration der W\u00e4rmepumpe aktiviert.\n - Die Pumpe ist nicht in einen Alarmzustand wegen fehlendem MODBUS40-Zubeh\u00f6r \u00fcbergegangen." + }, "user": { "data": { "ip_address": "Remote-Adresse", @@ -25,7 +53,11 @@ "remote_read_port": "Der Port, an dem das NibeGW-Ger\u00e4t auf Leseanfragen wartet.", "remote_write_port": "Der Port, an dem das NibeGW-Ger\u00e4t auf Schreibanfragen wartet." }, - "description": "Bevor du versuchst, die Integration zu konfigurieren, \u00fcberpr\u00fcfe folgendes:\n - Das NibeGW-Ger\u00e4t ist an eine W\u00e4rmepumpe angeschlossen.\n - Das MODBUS40-Zubeh\u00f6r wurde in der Konfiguration der W\u00e4rmepumpe aktiviert.\n - Die Pumpe ist nicht in einen Alarmzustand wegen fehlendem MODBUS40-Zubeh\u00f6r \u00fcbergegangen." + "description": "W\u00e4hle die Verbindungsmethode zu deiner Pumpe. Im Allgemeinen erfordern Pumpen der F-Serie ein kundenspezifisches Nibe GW-Zubeh\u00f6r, w\u00e4hrend eine Pumpe der S-Serie \u00fcber eine integrierte Modbus-Unterst\u00fctzung verf\u00fcgt.", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" + } } } } diff --git a/homeassistant/components/nibe_heatpump/translations/id.json b/homeassistant/components/nibe_heatpump/translations/id.json index 3ee3210ee76..44e0a8c0247 100644 --- a/homeassistant/components/nibe_heatpump/translations/id.json +++ b/homeassistant/components/nibe_heatpump/translations/id.json @@ -12,6 +12,27 @@ "write": "Kesalahan pada permintaan tulis ke pompa. Verifikasi `Port tulis jarak jauh` atau `Alamat IP jarak jauh` Anda." }, "step": { + "modbus": { + "data": { + "modbus_unit": "Pengidentifikasi Unit Modbus", + "modbus_url": "URL Modbus", + "model": "Model Pompa Panas" + } + }, + "nibegw": { + "data": { + "ip_address": "Alamat jarak jauh", + "listening_port": "Port mendengarkan lokal", + "model": "Model Pompa Panas", + "remote_read_port": "Port baca jarak jauh", + "remote_write_port": "Port tulis jarak jauh" + }, + "data_description": { + "ip_address": "Alamat unit NibeGW. Perangkat seharusnya sudah dikonfigurasi dengan alamat statis.", + "listening_port": "Port lokal pada sistem ini, yang dikonfigurasi untuk mengirim data ke unit NibeGW." + }, + "description": "Sebelum mencoba mengonfigurasi integrasi, pastikan bahwa:\n - Unit NibeGW terhubung ke pompa panas.\n - Aksesori MODBUS40 telah diaktifkan dalam konfigurasi pompa panas.\n - Pompa tidak sedang dalam status alarm tentang aksesori MODBUS40 yang tidak tersedia." + }, "user": { "data": { "ip_address": "Alamat jarak jauh", @@ -25,7 +46,11 @@ "remote_read_port": "Port yang digunakan unit NibeGW untuk mendengarkan permintaan baca.", "remote_write_port": "Port yang digunakan unit NibeGW untuk mendengarkan permintaan tulis." }, - "description": "Sebelum mencoba mengkonfigurasi integrasi, pastikan bahwa:\n- Unit NibeGW terhubung ke pompa pemanas.\n- Aksesori MODBUS40 telah diaktifkan dalam konfigurasi pompa pemanas.\n- Pompa belum masuk ke dalam kondisi alarm tentang ketiadaan aksesori MODBUS40." + "description": "Pilih metode koneksi ke pompa. Secara umum, pompa seri F memerlukan aksesori khusus Nibe GW, sementara pompa seri S memiliki dukungan Modbus bawaan.", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" + } } } } diff --git a/homeassistant/components/nibe_heatpump/translations/no.json b/homeassistant/components/nibe_heatpump/translations/no.json index b0a8f601776..e2ffa8790f7 100644 --- a/homeassistant/components/nibe_heatpump/translations/no.json +++ b/homeassistant/components/nibe_heatpump/translations/no.json @@ -9,9 +9,37 @@ "model": "Den valgte modellen ser ikke ut til \u00e5 st\u00f8tte modbus40", "read": "Feil ved leseforesp\u00f8rsel fra pumpe. Bekreft din \"Ekstern leseport\" eller \"Ekstern IP-adresse\".", "unknown": "Uventet feil", + "url": "Den spesifiserte nettadressen er ikke en godt utformet og st\u00f8ttet nettadresse", "write": "Feil ved skriveforesp\u00f8rsel til pumpen. Bekreft din \"Ekstern skriveport\" eller \"Ekstern IP-adresse\"." }, "step": { + "modbus": { + "data": { + "modbus_unit": "Modbus Unit Identifier", + "modbus_url": "Modbus URL", + "model": "Modell av varmepumpe" + }, + "data_description": { + "modbus_unit": "Enhetsidentifikasjon for din varmepumpe. Kan vanligvis st\u00e5 p\u00e5 0.", + "modbus_url": "Modbus URL som beskriver tilkoblingen til din varmepumpe eller MODBUS40 enhet. Det skal st\u00e5 p\u00e5 skjemaet:\n - `tcp://[HOST]:[PORT]` for Modbus TCP-tilkobling\n - `serial://[LOCAL DEVICE]` for en lokal Modbus RTU-tilkobling\n - `rfc2217://[HOST]:[PORT]` for en ekstern telnet-basert Modbus RTU-tilkobling." + } + }, + "nibegw": { + "data": { + "ip_address": "Ekstern adresse", + "listening_port": "Lokal lytteport", + "model": "Modell av varmepumpe", + "remote_read_port": "Ekstern leseport", + "remote_write_port": "Ekstern skriveport" + }, + "data_description": { + "ip_address": "Adressen til NibeGW-enheten. Enheten skal ha blitt konfigurert med en statisk adresse.", + "listening_port": "Den lokale porten p\u00e5 dette systemet, som NibeGW-enheten er konfigurert til \u00e5 sende data til.", + "remote_read_port": "Porten NibeGW-enheten lytter etter leseforesp\u00f8rsler p\u00e5.", + "remote_write_port": "Porten NibeGW-enheten lytter etter skriveforesp\u00f8rsler p\u00e5." + }, + "description": "F\u00f8r du pr\u00f8ver \u00e5 konfigurere integrasjonen, kontroller at:\n - NibeGW-enheten er koblet til en varmepumpe.\n - MODBUS40-tilbeh\u00f8ret er aktivert i varmepumpekonfigurasjonen.\n - Pumpen har ikke g\u00e5tt i alarmtilstand om manglende MODBUS40-tilbeh\u00f8r." + }, "user": { "data": { "ip_address": "Ekstern adresse", @@ -25,7 +53,11 @@ "remote_read_port": "Porten NibeGW-enheten lytter etter leseforesp\u00f8rsler p\u00e5.", "remote_write_port": "Porten NibeGW-enheten lytter etter skriveforesp\u00f8rsler p\u00e5." }, - "description": "F\u00f8r du pr\u00f8ver \u00e5 konfigurere integrasjonen, kontroller at:\n - NibeGW-enheten er koblet til en varmepumpe.\n - MODBUS40-tilbeh\u00f8ret er aktivert i varmepumpekonfigurasjonen.\n - Pumpen har ikke g\u00e5tt i alarmtilstand om manglende MODBUS40-tilbeh\u00f8r." + "description": "Velg tilkoblingsmetoden til pumpen din. Generelt krever pumper i F-serien et Nibe GW-tilpasset tilbeh\u00f8r, mens en pumpe i S-serien har Modbus-st\u00f8tte innebygd.", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" + } } } } diff --git a/homeassistant/components/nibe_heatpump/translations/zh-Hant.json b/homeassistant/components/nibe_heatpump/translations/zh-Hant.json index c00ea79de1c..fe182397efa 100644 --- a/homeassistant/components/nibe_heatpump/translations/zh-Hant.json +++ b/homeassistant/components/nibe_heatpump/translations/zh-Hant.json @@ -9,9 +9,37 @@ "model": "\u6240\u9078\u64c7\u7684\u578b\u865f\u4f3c\u4e4e\u4e0d\u652f\u63f4 modbus40", "read": "\u8b80\u53d6\u8acb\u6c42\u767c\u751f\u932f\u8aa4\uff0c\u8acb\u78ba\u8a8d `\u9060\u7aef\u8b80\u53d6\u57e0` \u6216 `\u9060\u7aef IP \u4f4d\u5740`\u3002", "unknown": "\u672a\u9810\u671f\u932f\u8aa4", + "url": "\u6307\u5b9a\u7684 URL \u4e0d\u662f\u6b63\u78ba\u683c\u5f0f\u6216\u652f\u63f4\u7684URL", "write": "\u5beb\u5165\u8acb\u6c42\u767c\u751f\u932f\u8aa4\uff0c\u8acb\u78ba\u8a8d `\u9060\u7aef\u5beb\u5165\u57e0` \u6216 `\u9060\u7aef IP \u4f4d\u5740`\u3002" }, "step": { + "modbus": { + "data": { + "modbus_unit": "Modbus \u8a2d\u5099\u8b58\u5225", + "modbus_url": "Modbus URL", + "model": "\u71b1\u6cf5\u578b\u865f" + }, + "data_description": { + "modbus_unit": "\u71b1\u6cf5\u8a2d\u5099\u8b58\u5225\uff0c\u901a\u5e38\u53ef\u4ee5\u4fdd\u7559\u70ba 0\u3002", + "modbus_url": "Modbus URL \u70ba\u9023\u7dda\u81f3\u71b1\u6cf5\u6216 MODBUS40 \u8a2d\u5099\u4e4b\u5167\u5bb9\u3001\u61c9\u8a72\u70ba\u4e0b\u5217\u683c\u5f0f\uff1a\n - `tcp://[HOST]:[PORT]` \u7528\u70ba Modbus TCP \u9023\u7dda\n - `serial://[LOCAL DEVICE]` \u7528\u70ba\u672c\u5730\u7aef Modbus RTU \u9023\u7dda\n - `rfc2217://[HOST]:[PORT]` \u7528\u70ba\u9060\u7aef telnet \u985e\u578b Modbus RTU \u9023\u7dda\u3002" + } + }, + "nibegw": { + "data": { + "ip_address": "\u9060\u7aef\u4f4d\u5740", + "listening_port": "\u672c\u5730\u76e3\u807d\u901a\u8a0a\u57e0", + "model": "\u71b1\u6cf5\u578b\u865f", + "remote_read_port": "\u9060\u7aef\u8b80\u53d6\u57e0", + "remote_write_port": "\u9060\u7aef\u5beb\u5165\u57e0" + }, + "data_description": { + "ip_address": "NibeGW \u8a2d\u5099\u4f4d\u5740\u3002\u88dd\u7f6e\u61c9\u8a72\u5df2\u7d93\u8a2d\u5b9a\u70ba\u975c\u614b\u4f4d\u5740\uff0c", + "listening_port": "\u7cfb\u7d71\u672c\u5730\u901a\u8a0a\u57e0\u3001\u4f9b NibeGW \u8a2d\u5099\u8a2d\u5b9a\u50b3\u9001\u8cc7\u6599\u3002", + "remote_read_port": "NibeGW \u8a2d\u5099\u76e3\u807d\u8b80\u53d6\u8acb\u6c42\u901a\u8a0a\u57e0\u3002", + "remote_write_port": "NibeGW \u8a2d\u5099\u76e3\u807d\u5beb\u5165\u8acb\u6c42\u901a\u8a0a\u57e0\u3002" + }, + "description": "\u65bc\u5617\u8a66\u8a2d\u5b9a\u6574\u5408\u524d\u3001\u8acb\u78ba\u8a8d\uff1a\n - NibeGW \u8a2d\u5099\u5df2\u7d93\u9023\u7dda\u81f3\u71b1\u6cf5\u3002\n - \u5df2\u7d93\u65bc\u71b1\u6cf5\u8a2d\u5b9a\u4e2d\u555f\u7528 MODBUS40 \u914d\u4ef6\u3002\n - \u6cf5\u4e26\u6c92\u6709\u51fa\u73fe\u7f3a\u5c11 MODBUS40 \u914d\u4ef6\u4e4b\u8b66\u544a\u3002" + }, "user": { "data": { "ip_address": "\u9060\u7aef\u4f4d\u5740", @@ -25,7 +53,11 @@ "remote_read_port": "NibeGW \u8a2d\u5099\u76e3\u807d\u8b80\u53d6\u8acb\u6c42\u901a\u8a0a\u57e0\u3002", "remote_write_port": "NibeGW \u8a2d\u5099\u76e3\u807d\u5beb\u5165\u8acb\u6c42\u901a\u8a0a\u57e0\u3002" }, - "description": "\u65bc\u5617\u8a66\u8a2d\u5b9a\u6574\u5408\u524d\u3001\u8acb\u78ba\u8a8d\uff1a\n - NibeGW \u8a2d\u5099\u5df2\u7d93\u9023\u7dda\u81f3\u71b1\u6cf5\u3002\n - \u5df2\u7d93\u65bc\u71b1\u6cf5\u8a2d\u5b9a\u4e2d\u555f\u7528 MODBUS40 \u914d\u4ef6\u3002\n - \u6cf5\u4e26\u6c92\u6709\u51fa\u73fe\u7f3a\u5c11 MODBUS40 \u914d\u4ef6\u4e4b\u8b66\u544a\u3002" + "description": "\u9078\u64c7\u6cf5\u9023\u7dda\u6a21\u5f0f\u3002\u901a\u5e38\u3001F \u7cfb\u5217\u6cf5\u9700\u8981 Nibe GW \u81ea\u8a02\u914d\u4ef6\u3001\u800c S \u7cfb\u5217\u6cf5\u70ba\u5167\u5efa Modbus\u3002", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" + } } } } diff --git a/homeassistant/components/openuv/translations/bg.json b/homeassistant/components/openuv/translations/bg.json index 6959a04bb7a..06c8d9cde09 100644 --- a/homeassistant/components/openuv/translations/bg.json +++ b/homeassistant/components/openuv/translations/bg.json @@ -1,12 +1,19 @@ { "config": { "abort": { - "already_configured": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" + "already_configured": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447" + }, + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" + }, "user": { "data": { "api_key": "API \u043a\u043b\u044e\u0447 \u0437\u0430 OpenUV", diff --git a/homeassistant/components/openuv/translations/ca.json b/homeassistant/components/openuv/translations/ca.json index 36043c3bde3..b7a7f3e7630 100644 --- a/homeassistant/components/openuv/translations/ca.json +++ b/homeassistant/components/openuv/translations/ca.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "La ubicaci\u00f3 ja est\u00e0 configurada" + "already_configured": "La ubicaci\u00f3 ja est\u00e0 configurada", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" }, "error": { "invalid_api_key": "Clau API inv\u00e0lida" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "Clau API" + }, + "description": "Si us plau, torna a introduir la clau API de {latitude}, {longitude}.", + "title": "Reautenticaci\u00f3 de la integraci\u00f3" + }, "user": { "data": { "api_key": "Clau API", diff --git a/homeassistant/components/openuv/translations/de.json b/homeassistant/components/openuv/translations/de.json index 94d8b49b7d5..9e5c9d42b62 100644 --- a/homeassistant/components/openuv/translations/de.json +++ b/homeassistant/components/openuv/translations/de.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "Standort ist bereits konfiguriert" + "already_configured": "Standort ist bereits konfiguriert", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API-Schl\u00fcssel" + }, + "description": "Bitte gib den API-Schl\u00fcssel f\u00fcr {latitude}, {longitude} erneut ein.", + "title": "Integration erneut authentifizieren" + }, "user": { "data": { "api_key": "API-Schl\u00fcssel", diff --git a/homeassistant/components/openuv/translations/id.json b/homeassistant/components/openuv/translations/id.json index 6ad5ee1f8d9..f7ef8c51b69 100644 --- a/homeassistant/components/openuv/translations/id.json +++ b/homeassistant/components/openuv/translations/id.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "Lokasi sudah dikonfigurasi" + "already_configured": "Lokasi sudah dikonfigurasi", + "reauth_successful": "Autentikasi ulang berhasil" }, "error": { "invalid_api_key": "Kunci API tidak valid" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "Kunci API" + }, + "description": "Masukkan kembali kunci API untuk {latitude}, {longitude}.", + "title": "Autentikasi Ulang Integrasi" + }, "user": { "data": { "api_key": "Kunci API", diff --git a/homeassistant/components/openuv/translations/no.json b/homeassistant/components/openuv/translations/no.json index 1fcba27dc9f..948b1e83369 100644 --- a/homeassistant/components/openuv/translations/no.json +++ b/homeassistant/components/openuv/translations/no.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "Plasseringen er allerede konfigurert" + "already_configured": "Plasseringen er allerede konfigurert", + "reauth_successful": "Re-autentisering var vellykket" }, "error": { "invalid_api_key": "Ugyldig API-n\u00f8kkel" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API-n\u00f8kkel" + }, + "description": "Vennligst skriv inn API-n\u00f8kkelen for {latitude} , {longitude} .", + "title": "Godkjenne integrering p\u00e5 nytt" + }, "user": { "data": { "api_key": "API-n\u00f8kkel", diff --git a/homeassistant/components/openuv/translations/pl.json b/homeassistant/components/openuv/translations/pl.json index 6578e6fcf84..530b09aaddc 100644 --- a/homeassistant/components/openuv/translations/pl.json +++ b/homeassistant/components/openuv/translations/pl.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "Lokalizacja jest ju\u017c skonfigurowana" + "already_configured": "Lokalizacja jest ju\u017c skonfigurowana", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" }, "error": { "invalid_api_key": "Nieprawid\u0142owy klucz API" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "Klucz API" + }, + "description": "Wprowad\u017a ponownie klucz API dla {latitude}, {longitude}.", + "title": "Ponownie uwierzytelnij integracj\u0119" + }, "user": { "data": { "api_key": "Klucz API", diff --git a/homeassistant/components/openuv/translations/zh-Hant.json b/homeassistant/components/openuv/translations/zh-Hant.json index eaeeec74e3c..d8b06ae7a2a 100644 --- a/homeassistant/components/openuv/translations/zh-Hant.json +++ b/homeassistant/components/openuv/translations/zh-Hant.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "\u5ea7\u6a19\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + "already_configured": "\u5ea7\u6a19\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, "error": { "invalid_api_key": "API \u91d1\u9470\u7121\u6548" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API \u91d1\u9470" + }, + "description": "\u8acb\u91cd\u65b0\u8f38\u5165 {latitude}\u3001{longitude} API \u5bc6\u9470\u3002", + "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" + }, "user": { "data": { "api_key": "API \u91d1\u9470", diff --git a/homeassistant/components/ovo_energy/translations/bg.json b/homeassistant/components/ovo_energy/translations/bg.json index b0c9e8a77cc..7207f59fef2 100644 --- a/homeassistant/components/ovo_energy/translations/bg.json +++ b/homeassistant/components/ovo_energy/translations/bg.json @@ -11,7 +11,7 @@ "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u0430" }, - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" }, "user": { "data": { diff --git a/homeassistant/components/ring/translations/bg.json b/homeassistant/components/ring/translations/bg.json index dfe9fcc384e..1b9e9ad53d6 100644 --- a/homeassistant/components/ring/translations/bg.json +++ b/homeassistant/components/ring/translations/bg.json @@ -12,7 +12,7 @@ "data": { "2fa": "\u0414\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u0435\u043d \u043a\u043e\u0434" }, - "title": "\u0414\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + "title": "\u0414\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" }, "user": { "data": { diff --git a/homeassistant/components/sensor/translations/bg.json b/homeassistant/components/sensor/translations/bg.json index f4ea74ca57e..c72139b3552 100644 --- a/homeassistant/components/sensor/translations/bg.json +++ b/homeassistant/components/sensor/translations/bg.json @@ -12,7 +12,7 @@ "is_value": "\u0422\u0435\u043a\u0443\u0449\u0430 \u0441\u0442\u043e\u0439\u043d\u043e\u0441\u0442 \u043d\u0430 {entity_name}" }, "trigger_type": { - "battery_level": "{entity_name} \u043d\u0438\u0432\u043e\u0442\u043e \u043d\u0430 \u0431\u0430\u0442\u0435\u0440\u0438\u044f\u0442\u0430 \u0441\u0435 \u043f\u0440\u043e\u043c\u0435\u043d\u044f", + "battery_level": "{entity_name} \u043f\u0440\u0438 \u043f\u0440\u043e\u043c\u044f\u043d\u0430 \u043d\u0430 \u043d\u0438\u0432\u043e\u0442\u043e \u043d\u0430 \u0431\u0430\u0442\u0435\u0440\u0438\u044f\u0442\u0430", "humidity": "{entity_name} \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u0442\u0430 \u0441\u0435 \u043f\u0440\u043e\u043c\u0435\u043d\u0438", "illuminance": "{entity_name} \u043e\u0441\u0432\u0435\u0442\u0435\u043d\u043e\u0441\u0442\u0442\u0430 \u0441\u0435 \u043f\u0440\u043e\u043c\u0435\u043d\u0438", "power": "\u043c\u043e\u0449\u043d\u043e\u0441\u0442\u0442\u0430 \u043d\u0430 {entity_name} \u0441\u0435 \u043f\u0440\u043e\u043c\u0435\u043d\u0438", diff --git a/homeassistant/components/shelly/translations/bg.json b/homeassistant/components/shelly/translations/bg.json index 1cdcd4e5d86..36889af941c 100644 --- a/homeassistant/components/shelly/translations/bg.json +++ b/homeassistant/components/shelly/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", - "reauth_unsuccessful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430, \u043c\u043e\u043b\u044f, \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0438 \u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.", + "reauth_unsuccessful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430, \u043c\u043e\u043b\u044f, \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0438 \u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.", "unsupported_firmware": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u043d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u043d\u0430 \u0444\u044a\u0440\u043c\u0443\u0435\u0440\u0430." }, "error": { diff --git a/homeassistant/components/subaru/translations/bg.json b/homeassistant/components/subaru/translations/bg.json index 86e02b8cc07..00813f34380 100644 --- a/homeassistant/components/subaru/translations/bg.json +++ b/homeassistant/components/subaru/translations/bg.json @@ -11,7 +11,7 @@ }, "step": { "two_factor": { - "description": "\u0418\u0437\u0438\u0441\u043a\u0432\u0430 \u0441\u0435 \u0434\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + "description": "\u0418\u0437\u0438\u0441\u043a\u0432\u0430 \u0441\u0435 \u0434\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" }, "user": { "data": { diff --git a/homeassistant/components/totalconnect/translations/bg.json b/homeassistant/components/totalconnect/translations/bg.json index 70789b8fe09..8213742fc09 100644 --- a/homeassistant/components/totalconnect/translations/bg.json +++ b/homeassistant/components/totalconnect/translations/bg.json @@ -5,7 +5,7 @@ "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" }, "error": { - "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" }, "step": { "locations": { diff --git a/homeassistant/components/trafikverket_weatherstation/translations/bg.json b/homeassistant/components/trafikverket_weatherstation/translations/bg.json index c4f6c0a2f55..bc91923312f 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/bg.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/bg.json @@ -5,7 +5,7 @@ }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" }, "step": { "user": { diff --git a/homeassistant/components/unifiprotect/translations/ca.json b/homeassistant/components/unifiprotect/translations/ca.json index 7532b2d9e60..39e57c2ed49 100644 --- a/homeassistant/components/unifiprotect/translations/ca.json +++ b/homeassistant/components/unifiprotect/translations/ca.json @@ -43,8 +43,8 @@ }, "issues": { "ea_warning": { - "description": "Est\u00e0s utilitzant la {version} d'UniFi Protect. Les versions d'acc\u00e9s anticipat no s\u00f3n compatibles amb Home Assistant i poden fer que la teva integraci\u00f3 d'UniFi Protect s'espatlli o no funcioni correctament.", - "title": "{version} \u00e9s una versi\u00f3 d'acc\u00e9s anticipat d'UniFi Protect" + "description": "Est\u00e0s utilitzant UniFi Protect v{version} que \u00e9s una versi\u00f3 d'acc\u00e9s anticipat. Les versions d'acc\u00e9s anticipat no s\u00f3n compatibles amb Home Assistant i poden fer que la teva integraci\u00f3 d'UniFi Protect s'espatlli o no funcioni correctament.", + "title": "UniFi Protect v{version} \u00e9s una versi\u00f3 d'acc\u00e9s anticipat" } }, "options": { diff --git a/homeassistant/components/zha/translations/bg.json b/homeassistant/components/zha/translations/bg.json index 1c4c44c9dff..8130df0e837 100644 --- a/homeassistant/components/zha/translations/bg.json +++ b/homeassistant/components/zha/translations/bg.json @@ -2,10 +2,10 @@ "config": { "abort": { "not_zha_device": "\u0422\u043e\u0432\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u0435 zha \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", - "single_instance_allowed": "\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0430 ZHA." + "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." }, "error": { - "cannot_connect": "\u041d\u0435\u0432\u044a\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442 \u0437\u0430 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 ZHA \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "flow_title": "{name}", "step": { @@ -100,18 +100,27 @@ "device_dropped": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0435 \u0438\u0437\u0442\u044a\u0440\u0432\u0430\u043d\u043e", "device_flipped": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0435 \u043e\u0431\u044a\u0440\u043d\u0430\u0442\u043e \"{subtype}\"", "device_knocked": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0435 \u043f\u043e\u0447\u0443\u043a\u0430\u043d\u043e \"{subtype}\"", + "device_offline": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0435 \u043e\u0444\u043b\u0430\u0439\u043d", "device_rotated": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0435 \u0437\u0430\u0432\u044a\u0440\u0442\u044f\u043d\u043e \"{subtype}\"", "device_shaken": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0435 \u0440\u0430\u0437\u043a\u043b\u0430\u0442\u0435\u043d\u043e", "device_slid": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0433\u043e \u0435 \u043f\u043b\u044a\u0437\u043d\u0430\u0442\u043e \"{subtype}\"", "device_tilted": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0435 \u043d\u0430\u043a\u043b\u043e\u043d\u0435\u043d\u043e", - "remote_button_double_press": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442 \u0434\u0432\u0443\u043a\u0440\u0430\u0442\u043d\u043e", - "remote_button_long_press": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e", - "remote_button_long_release": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043e\u0442\u043f\u0443\u0441\u043d\u0430\u0442 \u0441\u043b\u0435\u0434 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", - "remote_button_quadruple_press": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442 \u0447\u0435\u0442\u0438\u0440\u0438\u043a\u0440\u0430\u0442\u043d\u043e", - "remote_button_quintuple_press": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442 \u043f\u0435\u0442\u043a\u0440\u0430\u0442\u043d\u043e", - "remote_button_short_press": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442", - "remote_button_short_release": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043e\u0442\u043f\u0443\u0441\u043d\u0430\u0442", - "remote_button_triple_press": "\"{subtype}\" \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0431\u0435\u0448\u0435 \u043d\u0430\u0442\u0438\u0441\u043d\u0430\u0442 \u0442\u0440\u0438\u043a\u0440\u0430\u0442\u043d\u043e" + "remote_button_alt_double_press": "\"{subtype}\" \u043f\u0440\u0438 \u0434\u0432\u0443\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435 (\u0430\u043b\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0435\u043d \u0440\u0435\u0436\u0438\u043c)", + "remote_button_alt_long_press": "\"{subtype}\" \u043f\u0440\u0438 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435 (\u0430\u043b\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0435\u043d \u0440\u0435\u0436\u0438\u043c)", + "remote_button_alt_long_release": "\"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u0441\u043b\u0435\u0434 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435 (\u0430\u043b\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0435\u043d \u0440\u0435\u0436\u0438\u043c)", + "remote_button_alt_quadruple_press": "\"{subtype}\" \u043f\u0440\u0438 \u0447\u0435\u0442\u0438\u0440\u0438\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435 (\u0430\u043b\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0435\u043d \u0440\u0435\u0436\u0438\u043c)", + "remote_button_alt_quintuple_press": "\"{subtype}\" \u043f\u0440\u0438 \u043f\u0435\u0442\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435 (\u0430\u043b\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0435\u043d \u0440\u0435\u0436\u0438\u043c)", + "remote_button_alt_short_press": "\"{subtype}\" \u043f\u0440\u0438 \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435 (\u0430\u043b\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0435\u043d \u0440\u0435\u0436\u0438\u043c)", + "remote_button_alt_short_release": "\"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435 (\u0430\u043b\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0435\u043d \u0440\u0435\u0436\u0438\u043c)", + "remote_button_alt_triple_press": "\"{subtype}\" \u043f\u0440\u0438 \u0442\u0440\u0438\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435 (\u0430\u043b\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0435\u043d \u0440\u0435\u0436\u0438\u043c)", + "remote_button_double_press": "\"{subtype}\" \u043f\u0440\u0438 \u0434\u0432\u0443\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_long_press": "\"{subtype}\" \u043f\u0440\u0438 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_long_release": "\"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u0441\u043b\u0435\u0434 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_quadruple_press": "\"{subtype}\" \u043f\u0440\u0438 \u0447\u0435\u0442\u0438\u0440\u0438\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_quintuple_press": "\"{subtype}\" \u043f\u0440\u0438 \u043f\u0435\u0442\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_short_press": "\"{subtype}\" \u043f\u0440\u0438 \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435", + "remote_button_short_release": "\"{subtype}\" \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0435", + "remote_button_triple_press": "\"{subtype}\" \u043f\u0440\u0438 \u0442\u0440\u0438\u043a\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u0435" } }, "options": { @@ -119,9 +128,6 @@ "not_zha_device": "\u0422\u043e\u0432\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u0435 zha \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." }, - "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" - }, "flow_title": "{name}", "step": { "choose_formation_strategy": { @@ -149,7 +155,7 @@ "data": { "radio_type": "\u0422\u0438\u043f \u0440\u0430\u0434\u0438\u043e" }, - "description": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0441\u0432\u043e\u044f \u0442\u0438\u043f Zigbee \u0440\u0430\u0434\u0438\u043e", + "description": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0442\u0438\u043f\u0430 \u043d\u0430 \u0432\u0430\u0448\u0435\u0442\u043e Zigbee \u0440\u0430\u0434\u0438\u043e", "title": "\u0422\u0438\u043f \u0440\u0430\u0434\u0438\u043e" }, "manual_port_config": { From f34de5072a53fe9b0d36dfe75fa697f4aa263376 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 9 Nov 2022 19:34:31 -0800 Subject: [PATCH 0338/1033] Bump gcal_sync to 4.0.1 to fix Google Calendar config flow (#81873) Bump gcal_sync to 4.0.1 This reverts test chagnes from PR #81562 that were actually incorrect given the calendar "get" API returns less information that the "CalendarList" api. --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/google/test_config_flow.py | 2 +- tests/components/google/test_init.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index 9fc265fa287..2bc84827cd6 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==4.0.0", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==4.0.1", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index 2fa5e7d4f8b..f789ab61f03 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -738,7 +738,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==4.0.0 +gcal-sync==4.0.1 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 07a299c70b8..0f70d132de0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -554,7 +554,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==4.0.0 +gcal-sync==4.0.1 # homeassistant.components.geocaching geocachingapi==0.2.1 diff --git a/tests/components/google/test_config_flow.py b/tests/components/google/test_config_flow.py index bce3f4855c7..d8ddd6fe588 100644 --- a/tests/components/google/test_config_flow.py +++ b/tests/components/google/test_config_flow.py @@ -104,7 +104,7 @@ async def primary_calendar( """Fixture to return the primary calendar.""" mock_calendar_get( "primary", - {"id": primary_calendar_email, "summary": "Personal", "accessRole": "owner"}, + {"id": primary_calendar_email, "summary": "Personal"}, exc=primary_calendar_error, ) diff --git a/tests/components/google/test_init.py b/tests/components/google/test_init.py index a2f16f778fd..5e7696eec68 100644 --- a/tests/components/google/test_init.py +++ b/tests/components/google/test_init.py @@ -768,7 +768,7 @@ async def test_assign_unique_id( mock_calendar_get( "primary", - {"id": EMAIL_ADDRESS, "summary": "Personal", "accessRole": "reader"}, + {"id": EMAIL_ADDRESS, "summary": "Personal"}, ) mock_calendars_list({"items": [test_api_calendar]}) From 3089ca06c5c057a49ee84b910b66b5ad4499314b Mon Sep 17 00:00:00 2001 From: Malte Franken Date: Thu, 10 Nov 2022 18:16:37 +1100 Subject: [PATCH 0339/1033] Add integration_type to qld_bushfire (#81895) define integration type --- homeassistant/components/qld_bushfire/manifest.json | 3 ++- homeassistant/generated/integrations.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/qld_bushfire/manifest.json b/homeassistant/components/qld_bushfire/manifest.json index 366bbdc3479..94e94dcb6ee 100644 --- a/homeassistant/components/qld_bushfire/manifest.json +++ b/homeassistant/components/qld_bushfire/manifest.json @@ -5,5 +5,6 @@ "requirements": ["georss_qld_bushfire_alert_client==0.5"], "codeowners": ["@exxamalte"], "iot_class": "cloud_polling", - "loggers": ["georss_qld_bushfire_alert_client"] + "loggers": ["georss_qld_bushfire_alert_client"], + "integration_type": "service" } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index ed1aa0a3647..aa6221a1140 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -4159,7 +4159,7 @@ }, "qld_bushfire": { "name": "Queensland Bushfire Alert", - "integration_type": "hub", + "integration_type": "service", "config_flow": false, "iot_class": "cloud_polling" }, From 0bd04068de2b9aebce26216b73a0c03584dfa48e Mon Sep 17 00:00:00 2001 From: Ernst Klamer Date: Thu, 10 Nov 2022 09:40:22 +0100 Subject: [PATCH 0340/1033] Omit unit of measurement and device class equal to None (#81880) Omit unit of measurement and dev class none --- homeassistant/components/bthome/sensor.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/homeassistant/components/bthome/sensor.py b/homeassistant/components/bthome/sensor.py index e7757c2e872..61f7603039e 100644 --- a/homeassistant/components/bthome/sensor.py +++ b/homeassistant/components/bthome/sensor.py @@ -177,13 +177,11 @@ SENSOR_DESCRIPTIONS = { # Used for count sensor (BTHomeSensorDeviceClass.COUNT, None): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.COUNT}", - device_class=None, state_class=SensorStateClass.MEASUREMENT, ), # Used for rotation sensor (BTHomeSensorDeviceClass.ROTATION, Units.DEGREE): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.ROTATION}_{Units.DEGREE}", - device_class=None, native_unit_of_measurement=DEGREE, state_class=SensorStateClass.MEASUREMENT, ), @@ -234,8 +232,6 @@ SENSOR_DESCRIPTIONS = { # Used for UV index sensor (BTHomeSensorDeviceClass.UV_INDEX, None,): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.UV_INDEX}", - device_class=None, - native_unit_of_measurement=None, state_class=SensorStateClass.MEASUREMENT, ), } From 9ded2325223de3918e3f69aab8732487323b2214 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Thu, 10 Nov 2022 10:09:52 +0100 Subject: [PATCH 0341/1033] Move zwave_js addon manager to hassio integration (#81354) --- homeassistant/components/hassio/__init__.py | 218 +--- .../components/hassio/addon_manager.py | 373 ++++++ homeassistant/components/hassio/handler.py | 202 +++ homeassistant/components/zwave_js/__init__.py | 3 +- homeassistant/components/zwave_js/addon.py | 358 +----- .../components/zwave_js/config_flow.py | 11 +- tests/components/hassio/test_addon_manager.py | 1128 +++++++++++++++++ tests/components/zwave_js/conftest.py | 20 +- tests/components/zwave_js/test_addon.py | 30 - tests/components/zwave_js/test_config_flow.py | 2 +- 10 files changed, 1747 insertions(+), 598 deletions(-) create mode 100644 homeassistant/components/hassio/addon_manager.py create mode 100644 tests/components/hassio/test_addon_manager.py delete mode 100644 tests/components/zwave_js/test_addon.py diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index c811b35812e..598871f57d5 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -47,6 +47,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from homeassistant.loader import bind_hass from homeassistant.util.dt import utcnow +from .addon_manager import AddonError, AddonInfo, AddonManager, AddonState # noqa: F401 from .addon_panel import async_setup_addon_panel from .auth import async_setup_auth_view from .const import ( @@ -55,7 +56,6 @@ from .const import ( ATTR_AUTO_UPDATE, ATTR_CHANGELOG, ATTR_COMPRESSED, - ATTR_DISCOVERY, ATTR_FOLDERS, ATTR_HOMEASSISTANT, ATTR_INPUT, @@ -74,7 +74,25 @@ from .const import ( SupervisorEntityModel, ) from .discovery import HassioServiceInfo, async_setup_discovery_view # noqa: F401 -from .handler import HassIO, HassioAPIError, api_data +from .handler import ( # noqa: F401 + HassIO, + HassioAPIError, + async_create_backup, + async_get_addon_discovery_info, + async_get_addon_info, + async_get_addon_store_info, + async_install_addon, + async_restart_addon, + async_set_addon_options, + async_start_addon, + async_stop_addon, + async_uninstall_addon, + async_update_addon, + async_update_core, + async_update_diagnostics, + async_update_os, + async_update_supervisor, +) from .http import HassIOView from .ingress import async_setup_ingress_view from .repairs import SupervisorRepairs @@ -221,202 +239,6 @@ HARDWARE_INTEGRATIONS = { } -@bind_hass -async def async_get_addon_info(hass: HomeAssistant, slug: str) -> dict: - """Return add-on info. - - The add-on must be installed. - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - return await hassio.get_addon_info(slug) - - -@api_data -async def async_get_addon_store_info(hass: HomeAssistant, slug: str) -> dict: - """Return add-on store info. - - The caller of the function should handle HassioAPIError. - """ - hassio: HassIO = hass.data[DOMAIN] - command = f"/store/addons/{slug}" - return await hassio.send_command(command, method="get") - - -@bind_hass -async def async_update_diagnostics(hass: HomeAssistant, diagnostics: bool) -> dict: - """Update Supervisor diagnostics toggle. - - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - return await hassio.update_diagnostics(diagnostics) - - -@bind_hass -@api_data -async def async_install_addon(hass: HomeAssistant, slug: str) -> dict: - """Install add-on. - - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - command = f"/addons/{slug}/install" - return await hassio.send_command(command, timeout=None) - - -@bind_hass -@api_data -async def async_uninstall_addon(hass: HomeAssistant, slug: str) -> dict: - """Uninstall add-on. - - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - command = f"/addons/{slug}/uninstall" - return await hassio.send_command(command, timeout=60) - - -@bind_hass -@api_data -async def async_update_addon( - hass: HomeAssistant, - slug: str, - backup: bool = False, -) -> dict: - """Update add-on. - - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - command = f"/addons/{slug}/update" - return await hassio.send_command( - command, - payload={"backup": backup}, - timeout=None, - ) - - -@bind_hass -@api_data -async def async_start_addon(hass: HomeAssistant, slug: str) -> dict: - """Start add-on. - - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - command = f"/addons/{slug}/start" - return await hassio.send_command(command, timeout=60) - - -@bind_hass -@api_data -async def async_restart_addon(hass: HomeAssistant, slug: str) -> dict: - """Restart add-on. - - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - command = f"/addons/{slug}/restart" - return await hassio.send_command(command, timeout=None) - - -@bind_hass -@api_data -async def async_stop_addon(hass: HomeAssistant, slug: str) -> dict: - """Stop add-on. - - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - command = f"/addons/{slug}/stop" - return await hassio.send_command(command, timeout=60) - - -@bind_hass -@api_data -async def async_set_addon_options( - hass: HomeAssistant, slug: str, options: dict -) -> dict: - """Set add-on options. - - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - command = f"/addons/{slug}/options" - return await hassio.send_command(command, payload=options) - - -@bind_hass -async def async_get_addon_discovery_info(hass: HomeAssistant, slug: str) -> dict | None: - """Return discovery data for an add-on.""" - hassio = hass.data[DOMAIN] - data = await hassio.retrieve_discovery_messages() - discovered_addons = data[ATTR_DISCOVERY] - return next((addon for addon in discovered_addons if addon["addon"] == slug), None) - - -@bind_hass -@api_data -async def async_create_backup( - hass: HomeAssistant, payload: dict, partial: bool = False -) -> dict: - """Create a full or partial backup. - - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - backup_type = "partial" if partial else "full" - command = f"/backups/new/{backup_type}" - return await hassio.send_command(command, payload=payload, timeout=None) - - -@bind_hass -@api_data -async def async_update_os(hass: HomeAssistant, version: str | None = None) -> dict: - """Update Home Assistant Operating System. - - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - command = "/os/update" - return await hassio.send_command( - command, - payload={"version": version}, - timeout=None, - ) - - -@bind_hass -@api_data -async def async_update_supervisor(hass: HomeAssistant) -> dict: - """Update Home Assistant Supervisor. - - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - command = "/supervisor/update" - return await hassio.send_command(command, timeout=None) - - -@bind_hass -@api_data -async def async_update_core( - hass: HomeAssistant, version: str | None = None, backup: bool = False -) -> dict: - """Update Home Assistant Core. - - The caller of the function should handle HassioAPIError. - """ - hassio = hass.data[DOMAIN] - command = "/core/update" - return await hassio.send_command( - command, - payload={"version": version, "backup": backup}, - timeout=None, - ) - - @callback @bind_hass def get_info(hass): diff --git a/homeassistant/components/hassio/addon_manager.py b/homeassistant/components/hassio/addon_manager.py new file mode 100644 index 00000000000..ff3e9036018 --- /dev/null +++ b/homeassistant/components/hassio/addon_manager.py @@ -0,0 +1,373 @@ +"""Provide add-on management.""" +from __future__ import annotations + +import asyncio +from collections.abc import Awaitable, Callable, Coroutine +from dataclasses import dataclass +from enum import Enum +from functools import partial, wraps +import logging +from typing import Any, TypeVar + +from typing_extensions import Concatenate, ParamSpec + +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError + +from .handler import ( + HassioAPIError, + async_create_backup, + async_get_addon_discovery_info, + async_get_addon_info, + async_get_addon_store_info, + async_install_addon, + async_restart_addon, + async_set_addon_options, + async_start_addon, + async_stop_addon, + async_uninstall_addon, + async_update_addon, +) + +_AddonManagerT = TypeVar("_AddonManagerT", bound="AddonManager") +_R = TypeVar("_R") +_P = ParamSpec("_P") + + +def api_error( + error_message: str, +) -> Callable[ + [Callable[Concatenate[_AddonManagerT, _P], Awaitable[_R]]], + Callable[Concatenate[_AddonManagerT, _P], Coroutine[Any, Any, _R]], +]: + """Handle HassioAPIError and raise a specific AddonError.""" + + def handle_hassio_api_error( + func: Callable[Concatenate[_AddonManagerT, _P], Awaitable[_R]] + ) -> Callable[Concatenate[_AddonManagerT, _P], Coroutine[Any, Any, _R]]: + """Handle a HassioAPIError.""" + + @wraps(func) + async def wrapper( + self: _AddonManagerT, *args: _P.args, **kwargs: _P.kwargs + ) -> _R: + """Wrap an add-on manager method.""" + try: + return_value = await func(self, *args, **kwargs) + except HassioAPIError as err: + raise AddonError( + f"{error_message.format(addon_name=self.addon_name)}: {err}" + ) from err + + return return_value + + return wrapper + + return handle_hassio_api_error + + +@dataclass +class AddonInfo: + """Represent the current add-on info state.""" + + options: dict[str, Any] + state: AddonState + update_available: bool + version: str | None + + +class AddonState(Enum): + """Represent the current state of the add-on.""" + + NOT_INSTALLED = "not_installed" + INSTALLING = "installing" + UPDATING = "updating" + NOT_RUNNING = "not_running" + RUNNING = "running" + + +class AddonManager: + """Manage the add-on. + + Methods may raise AddonError. + Only one instance of this class may exist per add-on + to keep track of running add-on tasks. + """ + + def __init__( + self, + hass: HomeAssistant, + logger: logging.Logger, + addon_name: str, + addon_slug: str, + ) -> None: + """Set up the add-on manager.""" + self.addon_name = addon_name + self.addon_slug = addon_slug + self._hass = hass + self._logger = logger + self._install_task: asyncio.Task | None = None + self._restart_task: asyncio.Task | None = None + self._start_task: asyncio.Task | None = None + self._update_task: asyncio.Task | None = None + + def task_in_progress(self) -> bool: + """Return True if any of the add-on tasks are in progress.""" + return any( + task and not task.done() + for task in ( + self._restart_task, + self._install_task, + self._start_task, + self._update_task, + ) + ) + + @api_error("Failed to get the {addon_name} add-on discovery info") + async def async_get_addon_discovery_info(self) -> dict: + """Return add-on discovery info.""" + discovery_info = await async_get_addon_discovery_info( + self._hass, self.addon_slug + ) + + if not discovery_info: + raise AddonError(f"Failed to get {self.addon_name} add-on discovery info") + + discovery_info_config: dict = discovery_info["config"] + return discovery_info_config + + @api_error("Failed to get the {addon_name} add-on info") + async def async_get_addon_info(self) -> AddonInfo: + """Return and cache manager add-on info.""" + addon_store_info = await async_get_addon_store_info(self._hass, self.addon_slug) + self._logger.debug("Add-on store info: %s", addon_store_info) + if not addon_store_info["installed"]: + return AddonInfo( + options={}, + state=AddonState.NOT_INSTALLED, + update_available=False, + version=None, + ) + + addon_info = await async_get_addon_info(self._hass, self.addon_slug) + addon_state = self.async_get_addon_state(addon_info) + return AddonInfo( + options=addon_info["options"], + state=addon_state, + update_available=addon_info["update_available"], + version=addon_info["version"], + ) + + @callback + def async_get_addon_state(self, addon_info: dict[str, Any]) -> AddonState: + """Return the current state of the managed add-on.""" + addon_state = AddonState.NOT_RUNNING + + if addon_info["state"] == "started": + addon_state = AddonState.RUNNING + if self._install_task and not self._install_task.done(): + addon_state = AddonState.INSTALLING + if self._update_task and not self._update_task.done(): + addon_state = AddonState.UPDATING + + return addon_state + + @api_error("Failed to set the {addon_name} add-on options") + async def async_set_addon_options(self, config: dict) -> None: + """Set manager add-on options.""" + options = {"options": config} + await async_set_addon_options(self._hass, self.addon_slug, options) + + @api_error("Failed to install the {addon_name} add-on") + async def async_install_addon(self) -> None: + """Install the managed add-on.""" + await async_install_addon(self._hass, self.addon_slug) + + @api_error("Failed to uninstall the {addon_name} add-on") + async def async_uninstall_addon(self) -> None: + """Uninstall the managed add-on.""" + await async_uninstall_addon(self._hass, self.addon_slug) + + @api_error("Failed to update the {addon_name} add-on") + async def async_update_addon(self) -> None: + """Update the managed add-on if needed.""" + addon_info = await self.async_get_addon_info() + + if addon_info.state is AddonState.NOT_INSTALLED: + raise AddonError(f"{self.addon_name} add-on is not installed") + + if not addon_info.update_available: + return + + await self.async_create_backup() + await async_update_addon(self._hass, self.addon_slug) + + @api_error("Failed to start the {addon_name} add-on") + async def async_start_addon(self) -> None: + """Start the managed add-on.""" + await async_start_addon(self._hass, self.addon_slug) + + @api_error("Failed to restart the {addon_name} add-on") + async def async_restart_addon(self) -> None: + """Restart the managed add-on.""" + await async_restart_addon(self._hass, self.addon_slug) + + @api_error("Failed to stop the {addon_name} add-on") + async def async_stop_addon(self) -> None: + """Stop the managed add-on.""" + await async_stop_addon(self._hass, self.addon_slug) + + @api_error("Failed to create a backup of the {addon_name} add-on") + async def async_create_backup(self) -> None: + """Create a partial backup of the managed add-on.""" + addon_info = await self.async_get_addon_info() + name = f"addon_{self.addon_slug}_{addon_info.version}" + + self._logger.debug("Creating backup: %s", name) + await async_create_backup( + self._hass, + {"name": name, "addons": [self.addon_slug]}, + partial=True, + ) + + async def async_configure_addon( + self, + addon_config: dict[str, Any], + ) -> None: + """Configure the manager add-on, if needed.""" + addon_info = await self.async_get_addon_info() + + if addon_info.state is AddonState.NOT_INSTALLED: + raise AddonError(f"{self.addon_name} add-on is not installed") + + if addon_config != addon_info.options: + await self.async_set_addon_options(addon_config) + + @callback + def async_schedule_install_addon(self, catch_error: bool = False) -> asyncio.Task: + """Schedule a task that installs the managed add-on. + + Only schedule a new install task if the there's no running task. + """ + if not self._install_task or self._install_task.done(): + self._logger.info( + "%s add-on is not installed. Installing add-on", self.addon_name + ) + self._install_task = self._async_schedule_addon_operation( + self.async_install_addon, catch_error=catch_error + ) + return self._install_task + + @callback + def async_schedule_install_setup_addon( + self, + addon_config: dict[str, Any], + catch_error: bool = False, + ) -> asyncio.Task: + """Schedule a task that installs and sets up the managed add-on. + + Only schedule a new install task if the there's no running task. + """ + if not self._install_task or self._install_task.done(): + self._logger.info( + "%s add-on is not installed. Installing add-on", self.addon_name + ) + self._install_task = self._async_schedule_addon_operation( + self.async_install_addon, + partial( + self.async_configure_addon, + addon_config, + ), + self.async_start_addon, + catch_error=catch_error, + ) + return self._install_task + + @callback + def async_schedule_update_addon(self, catch_error: bool = False) -> asyncio.Task: + """Schedule a task that updates and sets up the managed add-on. + + Only schedule a new update task if the there's no running task. + """ + if not self._update_task or self._update_task.done(): + self._logger.info("Trying to update the %s add-on", self.addon_name) + self._update_task = self._async_schedule_addon_operation( + self.async_update_addon, + catch_error=catch_error, + ) + return self._update_task + + @callback + def async_schedule_start_addon(self, catch_error: bool = False) -> asyncio.Task: + """Schedule a task that starts the managed add-on. + + Only schedule a new start task if the there's no running task. + """ + if not self._start_task or self._start_task.done(): + self._logger.info( + "%s add-on is not running. Starting add-on", self.addon_name + ) + self._start_task = self._async_schedule_addon_operation( + self.async_start_addon, catch_error=catch_error + ) + return self._start_task + + @callback + def async_schedule_restart_addon(self, catch_error: bool = False) -> asyncio.Task: + """Schedule a task that restarts the managed add-on. + + Only schedule a new restart task if the there's no running task. + """ + if not self._restart_task or self._restart_task.done(): + self._logger.info("Restarting %s add-on", self.addon_name) + self._restart_task = self._async_schedule_addon_operation( + self.async_restart_addon, catch_error=catch_error + ) + return self._restart_task + + @callback + def async_schedule_setup_addon( + self, + addon_config: dict[str, Any], + catch_error: bool = False, + ) -> asyncio.Task: + """Schedule a task that configures and starts the managed add-on. + + Only schedule a new setup task if there's no running task. + """ + if not self._start_task or self._start_task.done(): + self._logger.info( + "%s add-on is not running. Starting add-on", self.addon_name + ) + self._start_task = self._async_schedule_addon_operation( + partial( + self.async_configure_addon, + addon_config, + ), + self.async_start_addon, + catch_error=catch_error, + ) + return self._start_task + + @callback + def _async_schedule_addon_operation( + self, *funcs: Callable, catch_error: bool = False + ) -> asyncio.Task: + """Schedule an add-on task.""" + + async def addon_operation() -> None: + """Do the add-on operation and catch AddonError.""" + for func in funcs: + try: + await func() + except AddonError as err: + if not catch_error: + raise + self._logger.error(err) + break + + return self._hass.async_create_task(addon_operation()) + + +class AddonError(HomeAssistantError): + """Represent an error with the managed add-on.""" diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py index ee16bdf8158..4f300ef16db 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -1,4 +1,6 @@ """Handler for Hass.io.""" +from __future__ import annotations + import asyncio from http import HTTPStatus import logging @@ -12,6 +14,10 @@ from homeassistant.components.http import ( CONF_SSL_CERTIFICATE, ) from homeassistant.const import SERVER_PORT +from homeassistant.core import HomeAssistant +from homeassistant.loader import bind_hass + +from .const import ATTR_DISCOVERY, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -47,6 +53,202 @@ def api_data(funct): return _wrapper +@bind_hass +async def async_get_addon_info(hass: HomeAssistant, slug: str) -> dict: + """Return add-on info. + + The add-on must be installed. + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + return await hassio.get_addon_info(slug) + + +@api_data +async def async_get_addon_store_info(hass: HomeAssistant, slug: str) -> dict: + """Return add-on store info. + + The caller of the function should handle HassioAPIError. + """ + hassio: HassIO = hass.data[DOMAIN] + command = f"/store/addons/{slug}" + return await hassio.send_command(command, method="get") + + +@bind_hass +async def async_update_diagnostics(hass: HomeAssistant, diagnostics: bool) -> dict: + """Update Supervisor diagnostics toggle. + + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + return await hassio.update_diagnostics(diagnostics) + + +@bind_hass +@api_data +async def async_install_addon(hass: HomeAssistant, slug: str) -> dict: + """Install add-on. + + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + command = f"/addons/{slug}/install" + return await hassio.send_command(command, timeout=None) + + +@bind_hass +@api_data +async def async_uninstall_addon(hass: HomeAssistant, slug: str) -> dict: + """Uninstall add-on. + + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + command = f"/addons/{slug}/uninstall" + return await hassio.send_command(command, timeout=60) + + +@bind_hass +@api_data +async def async_update_addon( + hass: HomeAssistant, + slug: str, + backup: bool = False, +) -> dict: + """Update add-on. + + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + command = f"/addons/{slug}/update" + return await hassio.send_command( + command, + payload={"backup": backup}, + timeout=None, + ) + + +@bind_hass +@api_data +async def async_start_addon(hass: HomeAssistant, slug: str) -> dict: + """Start add-on. + + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + command = f"/addons/{slug}/start" + return await hassio.send_command(command, timeout=60) + + +@bind_hass +@api_data +async def async_restart_addon(hass: HomeAssistant, slug: str) -> dict: + """Restart add-on. + + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + command = f"/addons/{slug}/restart" + return await hassio.send_command(command, timeout=None) + + +@bind_hass +@api_data +async def async_stop_addon(hass: HomeAssistant, slug: str) -> dict: + """Stop add-on. + + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + command = f"/addons/{slug}/stop" + return await hassio.send_command(command, timeout=60) + + +@bind_hass +@api_data +async def async_set_addon_options( + hass: HomeAssistant, slug: str, options: dict +) -> dict: + """Set add-on options. + + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + command = f"/addons/{slug}/options" + return await hassio.send_command(command, payload=options) + + +@bind_hass +async def async_get_addon_discovery_info(hass: HomeAssistant, slug: str) -> dict | None: + """Return discovery data for an add-on.""" + hassio = hass.data[DOMAIN] + data = await hassio.retrieve_discovery_messages() + discovered_addons = data[ATTR_DISCOVERY] + return next((addon for addon in discovered_addons if addon["addon"] == slug), None) + + +@bind_hass +@api_data +async def async_create_backup( + hass: HomeAssistant, payload: dict, partial: bool = False +) -> dict: + """Create a full or partial backup. + + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + backup_type = "partial" if partial else "full" + command = f"/backups/new/{backup_type}" + return await hassio.send_command(command, payload=payload, timeout=None) + + +@bind_hass +@api_data +async def async_update_os(hass: HomeAssistant, version: str | None = None) -> dict: + """Update Home Assistant Operating System. + + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + command = "/os/update" + return await hassio.send_command( + command, + payload={"version": version}, + timeout=None, + ) + + +@bind_hass +@api_data +async def async_update_supervisor(hass: HomeAssistant) -> dict: + """Update Home Assistant Supervisor. + + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + command = "/supervisor/update" + return await hassio.send_command(command, timeout=None) + + +@bind_hass +@api_data +async def async_update_core( + hass: HomeAssistant, version: str | None = None, backup: bool = False +) -> dict: + """Update Home Assistant Core. + + The caller of the function should handle HassioAPIError. + """ + hassio = hass.data[DOMAIN] + command = "/core/update" + return await hassio.send_command( + command, + payload={"version": version, "backup": backup}, + timeout=None, + ) + + class HassIO: """Small API wrapper for Hass.io.""" diff --git a/homeassistant/components/zwave_js/__init__.py b/homeassistant/components/zwave_js/__init__.py index cab07f4287f..c492cd8618f 100644 --- a/homeassistant/components/zwave_js/__init__.py +++ b/homeassistant/components/zwave_js/__init__.py @@ -20,6 +20,7 @@ from zwave_js_server.model.notification import ( ) from zwave_js_server.model.value import Value, ValueNotification +from homeassistant.components.hassio import AddonError, AddonManager, AddonState from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_DEVICE_ID, @@ -41,7 +42,7 @@ from homeassistant.helpers.issue_registry import ( ) from homeassistant.helpers.typing import UNDEFINED, ConfigType -from .addon import AddonError, AddonManager, AddonState, get_addon_manager +from .addon import get_addon_manager from .api import async_register_api from .const import ( ATTR_ACKNOWLEDGED_FRAMES, diff --git a/homeassistant/components/zwave_js/addon.py b/homeassistant/components/zwave_js/addon.py index 3e27235ef84..f9adf9f19fb 100644 --- a/homeassistant/components/zwave_js/addon.py +++ b/homeassistant/components/zwave_js/addon.py @@ -1,39 +1,12 @@ """Provide add-on management.""" from __future__ import annotations -import asyncio -from collections.abc import Awaitable, Callable, Coroutine -from dataclasses import dataclass -from enum import Enum -from functools import partial, wraps -from typing import Any, TypeVar - -from typing_extensions import Concatenate, ParamSpec - -from homeassistant.components.hassio import ( - async_create_backup, - async_get_addon_discovery_info, - async_get_addon_info, - async_get_addon_store_info, - async_install_addon, - async_restart_addon, - async_set_addon_options, - async_start_addon, - async_stop_addon, - async_uninstall_addon, - async_update_addon, -) -from homeassistant.components.hassio.handler import HassioAPIError +from homeassistant.components.hassio import AddonManager from homeassistant.core import HomeAssistant, callback -from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.singleton import singleton from .const import ADDON_SLUG, DOMAIN, LOGGER -_AddonManagerT = TypeVar("_AddonManagerT", bound="AddonManager") -_R = TypeVar("_R") -_P = ParamSpec("_P") - DATA_ADDON_MANAGER = f"{DOMAIN}_addon_manager" @@ -41,331 +14,4 @@ DATA_ADDON_MANAGER = f"{DOMAIN}_addon_manager" @callback def get_addon_manager(hass: HomeAssistant) -> AddonManager: """Get the add-on manager.""" - return AddonManager(hass, "Z-Wave JS", ADDON_SLUG) - - -def api_error( - error_message: str, -) -> Callable[ - [Callable[Concatenate[_AddonManagerT, _P], Awaitable[_R]]], - Callable[Concatenate[_AddonManagerT, _P], Coroutine[Any, Any, _R]], -]: - """Handle HassioAPIError and raise a specific AddonError.""" - - def handle_hassio_api_error( - func: Callable[Concatenate[_AddonManagerT, _P], Awaitable[_R]] - ) -> Callable[Concatenate[_AddonManagerT, _P], Coroutine[Any, Any, _R]]: - """Handle a HassioAPIError.""" - - @wraps(func) - async def wrapper( - self: _AddonManagerT, *args: _P.args, **kwargs: _P.kwargs - ) -> _R: - """Wrap an add-on manager method.""" - try: - return_value = await func(self, *args, **kwargs) - except HassioAPIError as err: - raise AddonError( - f"{error_message.format(addon_name=self.addon_name)}: {err}" - ) from err - - return return_value - - return wrapper - - return handle_hassio_api_error - - -@dataclass -class AddonInfo: - """Represent the current add-on info state.""" - - options: dict[str, Any] - state: AddonState - update_available: bool - version: str | None - - -class AddonState(Enum): - """Represent the current state of the add-on.""" - - NOT_INSTALLED = "not_installed" - INSTALLING = "installing" - UPDATING = "updating" - NOT_RUNNING = "not_running" - RUNNING = "running" - - -class AddonManager: - """Manage the add-on. - - Methods may raise AddonError. - Only one instance of this class may exist per add-on - to keep track of running add-on tasks. - """ - - def __init__(self, hass: HomeAssistant, addon_name: str, addon_slug: str) -> None: - """Set up the add-on manager.""" - self.addon_name = addon_name - self.addon_slug = addon_slug - self._hass = hass - self._install_task: asyncio.Task | None = None - self._restart_task: asyncio.Task | None = None - self._start_task: asyncio.Task | None = None - self._update_task: asyncio.Task | None = None - - def task_in_progress(self) -> bool: - """Return True if any of the add-on tasks are in progress.""" - return any( - task and not task.done() - for task in ( - self._install_task, - self._start_task, - self._update_task, - ) - ) - - @api_error("Failed to get {addon_name} add-on discovery info") - async def async_get_addon_discovery_info(self) -> dict: - """Return add-on discovery info.""" - discovery_info = await async_get_addon_discovery_info( - self._hass, self.addon_slug - ) - - if not discovery_info: - raise AddonError(f"Failed to get {self.addon_name} add-on discovery info") - - discovery_info_config: dict = discovery_info["config"] - return discovery_info_config - - @api_error("Failed to get the {addon_name} add-on info") - async def async_get_addon_info(self) -> AddonInfo: - """Return and cache manager add-on info.""" - addon_store_info = await async_get_addon_store_info(self._hass, self.addon_slug) - LOGGER.debug("Add-on store info: %s", addon_store_info) - if not addon_store_info["installed"]: - return AddonInfo( - options={}, - state=AddonState.NOT_INSTALLED, - update_available=False, - version=None, - ) - - addon_info = await async_get_addon_info(self._hass, self.addon_slug) - addon_state = self.async_get_addon_state(addon_info) - return AddonInfo( - options=addon_info["options"], - state=addon_state, - update_available=addon_info["update_available"], - version=addon_info["version"], - ) - - @callback - def async_get_addon_state(self, addon_info: dict[str, Any]) -> AddonState: - """Return the current state of the managed add-on.""" - addon_state = AddonState.NOT_RUNNING - - if addon_info["state"] == "started": - addon_state = AddonState.RUNNING - if self._install_task and not self._install_task.done(): - addon_state = AddonState.INSTALLING - if self._update_task and not self._update_task.done(): - addon_state = AddonState.UPDATING - - return addon_state - - @api_error("Failed to set the {addon_name} add-on options") - async def async_set_addon_options(self, config: dict) -> None: - """Set manager add-on options.""" - options = {"options": config} - await async_set_addon_options(self._hass, self.addon_slug, options) - - @api_error("Failed to install the {addon_name} add-on") - async def async_install_addon(self) -> None: - """Install the managed add-on.""" - await async_install_addon(self._hass, self.addon_slug) - - @callback - def async_schedule_install_addon(self, catch_error: bool = False) -> asyncio.Task: - """Schedule a task that installs the managed add-on. - - Only schedule a new install task if the there's no running task. - """ - if not self._install_task or self._install_task.done(): - LOGGER.info( - "%s add-on is not installed. Installing add-on", self.addon_name - ) - self._install_task = self._async_schedule_addon_operation( - self.async_install_addon, catch_error=catch_error - ) - return self._install_task - - @callback - def async_schedule_install_setup_addon( - self, - addon_config: dict[str, Any], - catch_error: bool = False, - ) -> asyncio.Task: - """Schedule a task that installs and sets up the managed add-on. - - Only schedule a new install task if the there's no running task. - """ - if not self._install_task or self._install_task.done(): - LOGGER.info( - "%s add-on is not installed. Installing add-on", self.addon_name - ) - self._install_task = self._async_schedule_addon_operation( - self.async_install_addon, - partial( - self.async_configure_addon, - addon_config, - ), - self.async_start_addon, - catch_error=catch_error, - ) - return self._install_task - - @api_error("Failed to uninstall the {addon_name} add-on") - async def async_uninstall_addon(self) -> None: - """Uninstall the managed add-on.""" - await async_uninstall_addon(self._hass, self.addon_slug) - - @api_error("Failed to update the {addon_name} add-on") - async def async_update_addon(self) -> None: - """Update the managed add-on if needed.""" - addon_info = await self.async_get_addon_info() - - if addon_info.state is AddonState.NOT_INSTALLED: - raise AddonError(f"{self.addon_name} add-on is not installed") - - if not addon_info.update_available: - return - - await self.async_create_backup() - await async_update_addon(self._hass, self.addon_slug) - - @callback - def async_schedule_update_addon(self, catch_error: bool = False) -> asyncio.Task: - """Schedule a task that updates and sets up the managed add-on. - - Only schedule a new update task if the there's no running task. - """ - if not self._update_task or self._update_task.done(): - LOGGER.info("Trying to update the %s add-on", self.addon_name) - self._update_task = self._async_schedule_addon_operation( - self.async_update_addon, - catch_error=catch_error, - ) - return self._update_task - - @api_error("Failed to start the {addon_name} add-on") - async def async_start_addon(self) -> None: - """Start the managed add-on.""" - await async_start_addon(self._hass, self.addon_slug) - - @api_error("Failed to restart the {addon_name} add-on") - async def async_restart_addon(self) -> None: - """Restart the managed add-on.""" - await async_restart_addon(self._hass, self.addon_slug) - - @callback - def async_schedule_start_addon(self, catch_error: bool = False) -> asyncio.Task: - """Schedule a task that starts the managed add-on. - - Only schedule a new start task if the there's no running task. - """ - if not self._start_task or self._start_task.done(): - LOGGER.info("%s add-on is not running. Starting add-on", self.addon_name) - self._start_task = self._async_schedule_addon_operation( - self.async_start_addon, catch_error=catch_error - ) - return self._start_task - - @callback - def async_schedule_restart_addon(self, catch_error: bool = False) -> asyncio.Task: - """Schedule a task that restarts the managed add-on. - - Only schedule a new restart task if the there's no running task. - """ - if not self._restart_task or self._restart_task.done(): - LOGGER.info("Restarting %s add-on", self.addon_name) - self._restart_task = self._async_schedule_addon_operation( - self.async_restart_addon, catch_error=catch_error - ) - return self._restart_task - - @api_error("Failed to stop the {addon_name} add-on") - async def async_stop_addon(self) -> None: - """Stop the managed add-on.""" - await async_stop_addon(self._hass, self.addon_slug) - - async def async_configure_addon( - self, - addon_config: dict[str, Any], - ) -> None: - """Configure and start manager add-on.""" - addon_info = await self.async_get_addon_info() - - if addon_info.state is AddonState.NOT_INSTALLED: - raise AddonError(f"{self.addon_name} add-on is not installed") - - if addon_config != addon_info.options: - await self.async_set_addon_options(addon_config) - - @callback - def async_schedule_setup_addon( - self, - addon_config: dict[str, Any], - catch_error: bool = False, - ) -> asyncio.Task: - """Schedule a task that configures and starts the managed add-on. - - Only schedule a new setup task if there's no running task. - """ - if not self._start_task or self._start_task.done(): - LOGGER.info("%s add-on is not running. Starting add-on", self.addon_name) - self._start_task = self._async_schedule_addon_operation( - partial( - self.async_configure_addon, - addon_config, - ), - self.async_start_addon, - catch_error=catch_error, - ) - return self._start_task - - @api_error("Failed to create a backup of the {addon_name} add-on.") - async def async_create_backup(self) -> None: - """Create a partial backup of the managed add-on.""" - addon_info = await self.async_get_addon_info() - name = f"addon_{self.addon_slug}_{addon_info.version}" - - LOGGER.debug("Creating backup: %s", name) - await async_create_backup( - self._hass, - {"name": name, "addons": [self.addon_slug]}, - partial=True, - ) - - @callback - def _async_schedule_addon_operation( - self, *funcs: Callable, catch_error: bool = False - ) -> asyncio.Task: - """Schedule an add-on task.""" - - async def addon_operation() -> None: - """Do the add-on operation and catch AddonError.""" - for func in funcs: - try: - await func() - except AddonError as err: - if not catch_error: - raise - LOGGER.error(err) - break - - return self._hass.async_create_task(addon_operation()) - - -class AddonError(HomeAssistantError): - """Represent an error with the managed add-on.""" + return AddonManager(hass, LOGGER, "Z-Wave JS", ADDON_SLUG) diff --git a/homeassistant/components/zwave_js/config_flow.py b/homeassistant/components/zwave_js/config_flow.py index 0a084b3a309..11fd3da0e75 100644 --- a/homeassistant/components/zwave_js/config_flow.py +++ b/homeassistant/components/zwave_js/config_flow.py @@ -14,7 +14,14 @@ from zwave_js_server.version import VersionInfo, get_server_version from homeassistant import config_entries, exceptions from homeassistant.components import usb -from homeassistant.components.hassio import HassioServiceInfo, is_hassio +from homeassistant.components.hassio import ( + AddonError, + AddonInfo, + AddonManager, + AddonState, + HassioServiceInfo, + is_hassio, +) from homeassistant.components.zeroconf import ZeroconfServiceInfo from homeassistant.const import CONF_NAME, CONF_URL from homeassistant.core import HomeAssistant, callback @@ -27,7 +34,7 @@ from homeassistant.data_entry_flow import ( from homeassistant.helpers.aiohttp_client import async_get_clientsession from . import disconnect_client -from .addon import AddonError, AddonInfo, AddonManager, AddonState, get_addon_manager +from .addon import get_addon_manager from .const import ( ADDON_SLUG, CONF_ADDON_DEVICE, diff --git a/tests/components/hassio/test_addon_manager.py b/tests/components/hassio/test_addon_manager.py new file mode 100644 index 00000000000..ffeecd167e6 --- /dev/null +++ b/tests/components/hassio/test_addon_manager.py @@ -0,0 +1,1128 @@ +"""Test the addon manager.""" +from __future__ import annotations + +import asyncio +from collections.abc import Generator +import logging +from typing import Any +from unittest.mock import AsyncMock, call, patch + +import pytest + +from homeassistant.components.hassio.addon_manager import ( + AddonError, + AddonInfo, + AddonManager, + AddonState, +) +from homeassistant.components.hassio.handler import HassioAPIError +from homeassistant.core import HomeAssistant + +LOGGER = logging.getLogger(__name__) + + +@pytest.fixture(name="addon_manager") +def addon_manager_fixture(hass: HomeAssistant) -> AddonManager: + """Return an AddonManager instance.""" + return AddonManager(hass, LOGGER, "Test", "test_addon") + + +@pytest.fixture(name="addon_not_installed") +def addon_not_installed_fixture( + addon_store_info: AsyncMock, addon_info: AsyncMock +) -> AsyncMock: + """Mock add-on not installed.""" + return addon_info + + +@pytest.fixture(name="addon_installed") +def mock_addon_installed( + addon_store_info: AsyncMock, addon_info: AsyncMock +) -> AsyncMock: + """Mock add-on already installed but not running.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "stopped", + "version": "1.0.0", + } + addon_info.return_value["state"] = "stopped" + addon_info.return_value["version"] = "1.0.0" + return addon_info + + +@pytest.fixture(name="get_addon_discovery_info") +def get_addon_discovery_info_fixture() -> Generator[AsyncMock, None, None]: + """Mock get add-on discovery info.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_get_addon_discovery_info" + ) as get_addon_discovery_info: + yield get_addon_discovery_info + + +@pytest.fixture(name="addon_store_info") +def addon_store_info_fixture() -> Generator[AsyncMock, None, None]: + """Mock Supervisor add-on store info.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_get_addon_store_info" + ) as addon_store_info: + addon_store_info.return_value = { + "installed": None, + "state": None, + "version": "1.0.0", + } + yield addon_store_info + + +@pytest.fixture(name="addon_info") +def addon_info_fixture() -> Generator[AsyncMock, None, None]: + """Mock Supervisor add-on info.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_get_addon_info", + ) as addon_info: + addon_info.return_value = { + "options": {}, + "state": None, + "update_available": False, + "version": None, + } + yield addon_info + + +@pytest.fixture(name="set_addon_options") +def set_addon_options_fixture() -> Generator[AsyncMock, None, None]: + """Mock set add-on options.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_set_addon_options" + ) as set_options: + yield set_options + + +@pytest.fixture(name="install_addon") +def install_addon_fixture() -> Generator[AsyncMock, None, None]: + """Mock install add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_install_addon" + ) as install_addon: + yield install_addon + + +@pytest.fixture(name="uninstall_addon") +def uninstall_addon_fixture() -> Generator[AsyncMock, None, None]: + """Mock uninstall add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_uninstall_addon" + ) as uninstall_addon: + yield uninstall_addon + + +@pytest.fixture(name="start_addon") +def start_addon_fixture() -> Generator[AsyncMock, None, None]: + """Mock start add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_start_addon" + ) as start_addon: + yield start_addon + + +@pytest.fixture(name="restart_addon") +def restart_addon_fixture() -> Generator[AsyncMock, None, None]: + """Mock restart add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_restart_addon" + ) as restart_addon: + yield restart_addon + + +@pytest.fixture(name="stop_addon") +def stop_addon_fixture() -> Generator[AsyncMock, None, None]: + """Mock stop add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_stop_addon" + ) as stop_addon: + yield stop_addon + + +@pytest.fixture(name="create_backup") +def create_backup_fixture() -> Generator[AsyncMock, None, None]: + """Mock create backup.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_create_backup" + ) as create_backup: + yield create_backup + + +@pytest.fixture(name="update_addon") +def mock_update_addon() -> Generator[AsyncMock, None, None]: + """Mock update add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_update_addon" + ) as update_addon: + yield update_addon + + +async def test_not_installed_raises_exception( + addon_manager: AddonManager, + addon_not_installed: dict[str, Any], +) -> None: + """Test addon not installed raises exception.""" + addon_config = {"test_key": "test"} + + with pytest.raises(AddonError) as err: + await addon_manager.async_configure_addon(addon_config) + + assert str(err.value) == "Test add-on is not installed" + + with pytest.raises(AddonError) as err: + await addon_manager.async_update_addon() + + assert str(err.value) == "Test add-on is not installed" + + +async def test_get_addon_discovery_info( + addon_manager: AddonManager, get_addon_discovery_info: AsyncMock +) -> None: + """Test get addon discovery info.""" + get_addon_discovery_info.return_value = {"config": {"test_key": "test"}} + + assert await addon_manager.async_get_addon_discovery_info() == {"test_key": "test"} + + assert get_addon_discovery_info.call_count == 1 + + +async def test_missing_addon_discovery_info( + addon_manager: AddonManager, get_addon_discovery_info: AsyncMock +) -> None: + """Test missing addon discovery info.""" + get_addon_discovery_info.return_value = None + + with pytest.raises(AddonError): + await addon_manager.async_get_addon_discovery_info() + + assert get_addon_discovery_info.call_count == 1 + + +async def test_get_addon_discovery_info_error( + addon_manager: AddonManager, get_addon_discovery_info: AsyncMock +) -> None: + """Test get addon discovery info raises error.""" + get_addon_discovery_info.side_effect = HassioAPIError("Boom") + + with pytest.raises(AddonError) as err: + assert await addon_manager.async_get_addon_discovery_info() + + assert str(err.value) == "Failed to get the Test add-on discovery info: Boom" + + assert get_addon_discovery_info.call_count == 1 + + +async def test_get_addon_info_not_installed( + addon_manager: AddonManager, addon_not_installed: AsyncMock +) -> None: + """Test get addon info when addon is not installed..""" + assert await addon_manager.async_get_addon_info() == AddonInfo( + options={}, + state=AddonState.NOT_INSTALLED, + update_available=False, + version=None, + ) + + +@pytest.mark.parametrize( + "addon_info_state, addon_state", + [("started", AddonState.RUNNING), ("stopped", AddonState.NOT_RUNNING)], +) +async def test_get_addon_info( + addon_manager: AddonManager, + addon_installed: AsyncMock, + addon_info_state: str, + addon_state: AddonState, +) -> None: + """Test get addon info when addon is installed.""" + addon_installed.return_value["state"] = addon_info_state + assert await addon_manager.async_get_addon_info() == AddonInfo( + options={}, + state=addon_state, + update_available=False, + version="1.0.0", + ) + + +@pytest.mark.parametrize( + "addon_info_error, addon_info_calls, addon_store_info_error, addon_store_info_calls", + [(HassioAPIError("Boom"), 1, None, 1), (None, 0, HassioAPIError("Boom"), 1)], +) +async def test_get_addon_info_error( + addon_manager: AddonManager, + addon_info: AsyncMock, + addon_store_info: AsyncMock, + addon_installed: AsyncMock, + addon_info_error: Exception | None, + addon_info_calls: int, + addon_store_info_error: Exception | None, + addon_store_info_calls: int, +) -> None: + """Test get addon info raises error.""" + addon_info.side_effect = addon_info_error + addon_store_info.side_effect = addon_store_info_error + + with pytest.raises(AddonError) as err: + await addon_manager.async_get_addon_info() + + assert str(err.value) == "Failed to get the Test add-on info: Boom" + + assert addon_info.call_count == addon_info_calls + assert addon_store_info.call_count == addon_store_info_calls + + +async def test_set_addon_options( + hass: HomeAssistant, addon_manager: AddonManager, set_addon_options: AsyncMock +) -> None: + """Test set addon options.""" + await addon_manager.async_set_addon_options({"test_key": "test"}) + + assert set_addon_options.call_count == 1 + assert set_addon_options.call_args == call( + hass, "test_addon", {"options": {"test_key": "test"}} + ) + + +async def test_set_addon_options_error( + hass: HomeAssistant, addon_manager: AddonManager, set_addon_options: AsyncMock +) -> None: + """Test set addon options raises error.""" + set_addon_options.side_effect = HassioAPIError("Boom") + + with pytest.raises(AddonError) as err: + await addon_manager.async_set_addon_options({"test_key": "test"}) + + assert str(err.value) == "Failed to set the Test add-on options: Boom" + + assert set_addon_options.call_count == 1 + assert set_addon_options.call_args == call( + hass, "test_addon", {"options": {"test_key": "test"}} + ) + + +async def test_install_addon( + addon_manager: AddonManager, install_addon: AsyncMock +) -> None: + """Test install addon.""" + await addon_manager.async_install_addon() + + assert install_addon.call_count == 1 + + +async def test_install_addon_error( + addon_manager: AddonManager, install_addon: AsyncMock +) -> None: + """Test install addon raises error.""" + install_addon.side_effect = HassioAPIError("Boom") + + with pytest.raises(AddonError) as err: + await addon_manager.async_install_addon() + + assert str(err.value) == "Failed to install the Test add-on: Boom" + + assert install_addon.call_count == 1 + + +async def test_schedule_install_addon( + addon_manager: AddonManager, + addon_installed: AsyncMock, + install_addon: AsyncMock, +) -> None: + """Test schedule install addon.""" + install_task = addon_manager.async_schedule_install_addon() + + assert addon_manager.task_in_progress() is True + + assert await addon_manager.async_get_addon_info() == AddonInfo( + options={}, + state=AddonState.INSTALLING, + update_available=False, + version="1.0.0", + ) + + # Make sure that actually only one install task is running. + install_task_two = addon_manager.async_schedule_install_addon() + + await asyncio.gather(install_task, install_task_two) + + assert addon_manager.task_in_progress() is False + assert install_addon.call_count == 1 + + install_addon.reset_mock() + + # Test that another call can be made after the install is done. + await addon_manager.async_schedule_install_addon() + + assert install_addon.call_count == 1 + + +async def test_schedule_install_addon_error( + addon_manager: AddonManager, + addon_installed: AsyncMock, + install_addon: AsyncMock, +) -> None: + """Test schedule install addon raises error.""" + install_addon.side_effect = HassioAPIError("Boom") + + with pytest.raises(AddonError) as err: + await addon_manager.async_schedule_install_addon() + + assert str(err.value) == "Failed to install the Test add-on: Boom" + + assert install_addon.call_count == 1 + + +async def test_schedule_install_addon_logs_error( + addon_manager: AddonManager, + addon_installed: AsyncMock, + install_addon: AsyncMock, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test schedule install addon logs error.""" + install_addon.side_effect = HassioAPIError("Boom") + + await addon_manager.async_schedule_install_addon(catch_error=True) + + assert "Failed to install the Test add-on: Boom" in caplog.text + assert install_addon.call_count == 1 + + +async def test_uninstall_addon( + addon_manager: AddonManager, uninstall_addon: AsyncMock +) -> None: + """Test uninstall addon.""" + await addon_manager.async_uninstall_addon() + + assert uninstall_addon.call_count == 1 + + +async def test_uninstall_addon_error( + addon_manager: AddonManager, uninstall_addon: AsyncMock +) -> None: + """Test uninstall addon raises error.""" + uninstall_addon.side_effect = HassioAPIError("Boom") + + with pytest.raises(AddonError) as err: + await addon_manager.async_uninstall_addon() + + assert str(err.value) == "Failed to uninstall the Test add-on: Boom" + + assert uninstall_addon.call_count == 1 + + +async def test_start_addon(addon_manager: AddonManager, start_addon: AsyncMock) -> None: + """Test start addon.""" + await addon_manager.async_start_addon() + + assert start_addon.call_count == 1 + + +async def test_start_addon_error( + addon_manager: AddonManager, start_addon: AsyncMock +) -> None: + """Test start addon raises error.""" + start_addon.side_effect = HassioAPIError("Boom") + + with pytest.raises(AddonError) as err: + await addon_manager.async_start_addon() + + assert str(err.value) == "Failed to start the Test add-on: Boom" + + assert start_addon.call_count == 1 + + +async def test_schedule_start_addon( + addon_manager: AddonManager, + addon_installed: AsyncMock, + start_addon: AsyncMock, +) -> None: + """Test schedule start addon.""" + start_task = addon_manager.async_schedule_start_addon() + + assert addon_manager.task_in_progress() is True + + # Make sure that actually only one start task is running. + start_task_two = addon_manager.async_schedule_start_addon() + + await asyncio.gather(start_task, start_task_two) + + assert addon_manager.task_in_progress() is False + assert start_addon.call_count == 1 + + start_addon.reset_mock() + + # Test that another call can be made after the start is done. + await addon_manager.async_schedule_start_addon() + + assert start_addon.call_count == 1 + + +async def test_schedule_start_addon_error( + addon_manager: AddonManager, + addon_installed: AsyncMock, + start_addon: AsyncMock, +) -> None: + """Test schedule start addon raises error.""" + start_addon.side_effect = HassioAPIError("Boom") + + with pytest.raises(AddonError) as err: + await addon_manager.async_schedule_start_addon() + + assert str(err.value) == "Failed to start the Test add-on: Boom" + + assert start_addon.call_count == 1 + + +async def test_schedule_start_addon_logs_error( + addon_manager: AddonManager, + addon_installed: AsyncMock, + start_addon: AsyncMock, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test schedule start addon logs error.""" + start_addon.side_effect = HassioAPIError("Boom") + + await addon_manager.async_schedule_start_addon(catch_error=True) + + assert "Failed to start the Test add-on: Boom" in caplog.text + assert start_addon.call_count == 1 + + +async def test_restart_addon( + addon_manager: AddonManager, restart_addon: AsyncMock +) -> None: + """Test restart addon.""" + await addon_manager.async_restart_addon() + + assert restart_addon.call_count == 1 + + +async def test_restart_addon_error( + addon_manager: AddonManager, restart_addon: AsyncMock +) -> None: + """Test restart addon raises error.""" + restart_addon.side_effect = HassioAPIError("Boom") + + with pytest.raises(AddonError) as err: + await addon_manager.async_restart_addon() + + assert str(err.value) == "Failed to restart the Test add-on: Boom" + + assert restart_addon.call_count == 1 + + +async def test_schedule_restart_addon( + addon_manager: AddonManager, + addon_installed: AsyncMock, + restart_addon: AsyncMock, +) -> None: + """Test schedule restart addon.""" + restart_task = addon_manager.async_schedule_restart_addon() + + assert addon_manager.task_in_progress() is True + + # Make sure that actually only one start task is running. + restart_task_two = addon_manager.async_schedule_restart_addon() + + await asyncio.gather(restart_task, restart_task_two) + + assert addon_manager.task_in_progress() is False + assert restart_addon.call_count == 1 + + restart_addon.reset_mock() + + # Test that another call can be made after the restart is done. + await addon_manager.async_schedule_restart_addon() + + assert restart_addon.call_count == 1 + + +async def test_schedule_restart_addon_error( + addon_manager: AddonManager, + addon_installed: AsyncMock, + restart_addon: AsyncMock, +) -> None: + """Test schedule restart addon raises error.""" + restart_addon.side_effect = HassioAPIError("Boom") + + with pytest.raises(AddonError) as err: + await addon_manager.async_schedule_restart_addon() + + assert str(err.value) == "Failed to restart the Test add-on: Boom" + + assert restart_addon.call_count == 1 + + +async def test_schedule_restart_addon_logs_error( + addon_manager: AddonManager, + addon_installed: AsyncMock, + restart_addon: AsyncMock, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test schedule restart addon logs error.""" + restart_addon.side_effect = HassioAPIError("Boom") + + await addon_manager.async_schedule_restart_addon(catch_error=True) + + assert "Failed to restart the Test add-on: Boom" in caplog.text + assert restart_addon.call_count == 1 + + +async def test_stop_addon(addon_manager: AddonManager, stop_addon: AsyncMock) -> None: + """Test stop addon.""" + await addon_manager.async_stop_addon() + + assert stop_addon.call_count == 1 + + +async def test_stop_addon_error( + addon_manager: AddonManager, stop_addon: AsyncMock +) -> None: + """Test stop addon raises error.""" + stop_addon.side_effect = HassioAPIError("Boom") + + with pytest.raises(AddonError) as err: + await addon_manager.async_stop_addon() + + assert str(err.value) == "Failed to stop the Test add-on: Boom" + + assert stop_addon.call_count == 1 + + +async def test_update_addon( + hass: HomeAssistant, + addon_manager: AddonManager, + addon_info: AsyncMock, + addon_installed: AsyncMock, + create_backup: AsyncMock, + update_addon: AsyncMock, +) -> None: + """Test update addon.""" + addon_info.return_value["update_available"] = True + + await addon_manager.async_update_addon() + + assert addon_info.call_count == 2 + assert create_backup.call_count == 1 + assert create_backup.call_args == call( + hass, {"name": "addon_test_addon_1.0.0", "addons": ["test_addon"]}, partial=True + ) + assert update_addon.call_count == 1 + + +async def test_update_addon_no_update( + addon_manager: AddonManager, + addon_info: AsyncMock, + addon_installed: AsyncMock, + create_backup: AsyncMock, + update_addon: AsyncMock, +) -> None: + """Test update addon without update available.""" + addon_info.return_value["update_available"] = False + + await addon_manager.async_update_addon() + + assert addon_info.call_count == 1 + assert create_backup.call_count == 0 + assert update_addon.call_count == 0 + + +async def test_update_addon_error( + hass: HomeAssistant, + addon_manager: AddonManager, + addon_info: AsyncMock, + addon_installed: AsyncMock, + create_backup: AsyncMock, + update_addon: AsyncMock, +) -> None: + """Test update addon raises error.""" + addon_info.return_value["update_available"] = True + update_addon.side_effect = HassioAPIError("Boom") + + with pytest.raises(AddonError) as err: + await addon_manager.async_update_addon() + + assert str(err.value) == "Failed to update the Test add-on: Boom" + + assert addon_info.call_count == 2 + assert create_backup.call_count == 1 + assert create_backup.call_args == call( + hass, {"name": "addon_test_addon_1.0.0", "addons": ["test_addon"]}, partial=True + ) + assert update_addon.call_count == 1 + + +async def test_schedule_update_addon( + hass: HomeAssistant, + addon_manager: AddonManager, + addon_info: AsyncMock, + addon_installed: AsyncMock, + create_backup: AsyncMock, + update_addon: AsyncMock, +) -> None: + """Test schedule update addon.""" + addon_info.return_value["update_available"] = True + + update_task = addon_manager.async_schedule_update_addon() + + assert addon_manager.task_in_progress() is True + + assert await addon_manager.async_get_addon_info() == AddonInfo( + options={}, + state=AddonState.UPDATING, + update_available=True, + version="1.0.0", + ) + + # Make sure that actually only one update task is running. + update_task_two = addon_manager.async_schedule_update_addon() + + await asyncio.gather(update_task, update_task_two) + + assert addon_manager.task_in_progress() is False + assert addon_info.call_count == 3 + assert create_backup.call_count == 1 + assert create_backup.call_args == call( + hass, {"name": "addon_test_addon_1.0.0", "addons": ["test_addon"]}, partial=True + ) + assert update_addon.call_count == 1 + + update_addon.reset_mock() + + # Test that another call can be made after the update is done. + await addon_manager.async_schedule_update_addon() + + assert update_addon.call_count == 1 + + +@pytest.mark.parametrize( + ( + "create_backup_error, create_backup_calls, " + "update_addon_error, update_addon_calls, " + "error_message" + ), + [ + ( + HassioAPIError("Boom"), + 1, + None, + 0, + "Failed to create a backup of the Test add-on: Boom", + ), + ( + None, + 1, + HassioAPIError("Boom"), + 1, + "Failed to update the Test add-on: Boom", + ), + ], +) +async def test_schedule_update_addon_error( + addon_manager: AddonManager, + addon_installed: AsyncMock, + create_backup: AsyncMock, + update_addon: AsyncMock, + create_backup_error: Exception | None, + create_backup_calls: int, + update_addon_error: Exception | None, + update_addon_calls: int, + error_message: str, +) -> None: + """Test schedule update addon raises error.""" + addon_installed.return_value["update_available"] = True + create_backup.side_effect = create_backup_error + update_addon.side_effect = update_addon_error + + with pytest.raises(AddonError) as err: + await addon_manager.async_schedule_update_addon() + + assert str(err.value) == error_message + + assert create_backup.call_count == create_backup_calls + assert update_addon.call_count == update_addon_calls + + +@pytest.mark.parametrize( + ( + "create_backup_error, create_backup_calls, " + "update_addon_error, update_addon_calls, " + "error_log" + ), + [ + ( + HassioAPIError("Boom"), + 1, + None, + 0, + "Failed to create a backup of the Test add-on: Boom", + ), + ( + None, + 1, + HassioAPIError("Boom"), + 1, + "Failed to update the Test add-on: Boom", + ), + ], +) +async def test_schedule_update_addon_logs_error( + addon_manager: AddonManager, + addon_installed: AsyncMock, + create_backup: AsyncMock, + update_addon: AsyncMock, + create_backup_error: Exception | None, + create_backup_calls: int, + update_addon_error: Exception | None, + update_addon_calls: int, + error_log: str, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test schedule update addon logs error.""" + addon_installed.return_value["update_available"] = True + create_backup.side_effect = create_backup_error + update_addon.side_effect = update_addon_error + + await addon_manager.async_schedule_update_addon(catch_error=True) + + assert error_log in caplog.text + assert create_backup.call_count == create_backup_calls + assert update_addon.call_count == update_addon_calls + + +async def test_create_backup( + hass: HomeAssistant, + addon_manager: AddonManager, + addon_info: AsyncMock, + addon_installed: AsyncMock, + create_backup: AsyncMock, +) -> None: + """Test creating a backup of the addon.""" + await addon_manager.async_create_backup() + + assert addon_info.call_count == 1 + assert create_backup.call_count == 1 + assert create_backup.call_args == call( + hass, {"name": "addon_test_addon_1.0.0", "addons": ["test_addon"]}, partial=True + ) + + +async def test_create_backup_error( + hass: HomeAssistant, + addon_manager: AddonManager, + addon_info: AsyncMock, + addon_installed: AsyncMock, + create_backup: AsyncMock, +) -> None: + """Test creating a backup of the addon raises error.""" + create_backup.side_effect = HassioAPIError("Boom") + + with pytest.raises(AddonError) as err: + await addon_manager.async_create_backup() + + assert str(err.value) == "Failed to create a backup of the Test add-on: Boom" + + assert addon_info.call_count == 1 + assert create_backup.call_count == 1 + assert create_backup.call_args == call( + hass, {"name": "addon_test_addon_1.0.0", "addons": ["test_addon"]}, partial=True + ) + + +async def test_schedule_install_setup_addon( + addon_manager: AddonManager, + addon_installed: AsyncMock, + install_addon: AsyncMock, + set_addon_options: AsyncMock, + start_addon: AsyncMock, +) -> None: + """Test schedule install setup addon.""" + install_task = addon_manager.async_schedule_install_setup_addon( + {"test_key": "test"} + ) + + assert addon_manager.task_in_progress() is True + + # Make sure that actually only one install task is running. + install_task_two = addon_manager.async_schedule_install_setup_addon( + {"test_key": "test"} + ) + + await asyncio.gather(install_task, install_task_two) + + assert addon_manager.task_in_progress() is False + assert install_addon.call_count == 1 + assert set_addon_options.call_count == 1 + assert start_addon.call_count == 1 + + install_addon.reset_mock() + set_addon_options.reset_mock() + start_addon.reset_mock() + + # Test that another call can be made after the install is done. + await addon_manager.async_schedule_install_setup_addon({"test_key": "test"}) + + assert install_addon.call_count == 1 + assert set_addon_options.call_count == 1 + assert start_addon.call_count == 1 + + +@pytest.mark.parametrize( + ( + "install_addon_error, install_addon_calls, " + "set_addon_options_error, set_addon_options_calls, " + "start_addon_error, start_addon_calls, " + "error_message" + ), + [ + ( + HassioAPIError("Boom"), + 1, + None, + 0, + None, + 0, + "Failed to install the Test add-on: Boom", + ), + ( + None, + 1, + HassioAPIError("Boom"), + 1, + None, + 0, + "Failed to set the Test add-on options: Boom", + ), + ( + None, + 1, + None, + 1, + HassioAPIError("Boom"), + 1, + "Failed to start the Test add-on: Boom", + ), + ], +) +async def test_schedule_install_setup_addon_error( + addon_manager: AddonManager, + addon_installed: AsyncMock, + install_addon: AsyncMock, + set_addon_options: AsyncMock, + start_addon: AsyncMock, + install_addon_error: Exception | None, + install_addon_calls: int, + set_addon_options_error: Exception | None, + set_addon_options_calls: int, + start_addon_error: Exception | None, + start_addon_calls: int, + error_message: str, +) -> None: + """Test schedule install setup addon raises error.""" + install_addon.side_effect = install_addon_error + set_addon_options.side_effect = set_addon_options_error + start_addon.side_effect = start_addon_error + + with pytest.raises(AddonError) as err: + await addon_manager.async_schedule_install_setup_addon({"test_key": "test"}) + + assert str(err.value) == error_message + + assert install_addon.call_count == install_addon_calls + assert set_addon_options.call_count == set_addon_options_calls + assert start_addon.call_count == start_addon_calls + + +@pytest.mark.parametrize( + ( + "install_addon_error, install_addon_calls, " + "set_addon_options_error, set_addon_options_calls, " + "start_addon_error, start_addon_calls, " + "error_log" + ), + [ + ( + HassioAPIError("Boom"), + 1, + None, + 0, + None, + 0, + "Failed to install the Test add-on: Boom", + ), + ( + None, + 1, + HassioAPIError("Boom"), + 1, + None, + 0, + "Failed to set the Test add-on options: Boom", + ), + ( + None, + 1, + None, + 1, + HassioAPIError("Boom"), + 1, + "Failed to start the Test add-on: Boom", + ), + ], +) +async def test_schedule_install_setup_addon_logs_error( + addon_manager: AddonManager, + addon_installed: AsyncMock, + install_addon: AsyncMock, + set_addon_options: AsyncMock, + start_addon: AsyncMock, + install_addon_error: Exception | None, + install_addon_calls: int, + set_addon_options_error: Exception | None, + set_addon_options_calls: int, + start_addon_error: Exception | None, + start_addon_calls: int, + error_log: str, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test schedule install setup addon logs error.""" + install_addon.side_effect = install_addon_error + set_addon_options.side_effect = set_addon_options_error + start_addon.side_effect = start_addon_error + + await addon_manager.async_schedule_install_setup_addon( + {"test_key": "test"}, catch_error=True + ) + + assert error_log in caplog.text + assert install_addon.call_count == install_addon_calls + assert set_addon_options.call_count == set_addon_options_calls + assert start_addon.call_count == start_addon_calls + + +async def test_schedule_setup_addon( + addon_manager: AddonManager, + addon_installed: AsyncMock, + set_addon_options: AsyncMock, + start_addon: AsyncMock, +) -> None: + """Test schedule setup addon.""" + start_task = addon_manager.async_schedule_setup_addon({"test_key": "test"}) + + assert addon_manager.task_in_progress() is True + + # Make sure that actually only one start task is running. + start_task_two = addon_manager.async_schedule_setup_addon({"test_key": "test"}) + + await asyncio.gather(start_task, start_task_two) + + assert addon_manager.task_in_progress() is False + assert set_addon_options.call_count == 1 + assert start_addon.call_count == 1 + + set_addon_options.reset_mock() + start_addon.reset_mock() + + # Test that another call can be made after the start is done. + await addon_manager.async_schedule_setup_addon({"test_key": "test"}) + + assert set_addon_options.call_count == 1 + assert start_addon.call_count == 1 + + +@pytest.mark.parametrize( + ( + "set_addon_options_error, set_addon_options_calls, " + "start_addon_error, start_addon_calls, " + "error_message" + ), + [ + ( + HassioAPIError("Boom"), + 1, + None, + 0, + "Failed to set the Test add-on options: Boom", + ), + ( + None, + 1, + HassioAPIError("Boom"), + 1, + "Failed to start the Test add-on: Boom", + ), + ], +) +async def test_schedule_setup_addon_error( + addon_manager: AddonManager, + addon_installed: AsyncMock, + set_addon_options: AsyncMock, + start_addon: AsyncMock, + set_addon_options_error: Exception | None, + set_addon_options_calls: int, + start_addon_error: Exception | None, + start_addon_calls: int, + error_message: str, +) -> None: + """Test schedule setup addon raises error.""" + set_addon_options.side_effect = set_addon_options_error + start_addon.side_effect = start_addon_error + + with pytest.raises(AddonError) as err: + await addon_manager.async_schedule_setup_addon({"test_key": "test"}) + + assert str(err.value) == error_message + + assert set_addon_options.call_count == set_addon_options_calls + assert start_addon.call_count == start_addon_calls + + +@pytest.mark.parametrize( + ( + "set_addon_options_error, set_addon_options_calls, " + "start_addon_error, start_addon_calls, " + "error_log" + ), + [ + ( + HassioAPIError("Boom"), + 1, + None, + 0, + "Failed to set the Test add-on options: Boom", + ), + ( + None, + 1, + HassioAPIError("Boom"), + 1, + "Failed to start the Test add-on: Boom", + ), + ], +) +async def test_schedule_setup_addon_logs_error( + addon_manager: AddonManager, + addon_installed: AsyncMock, + set_addon_options: AsyncMock, + start_addon: AsyncMock, + set_addon_options_error: Exception | None, + set_addon_options_calls: int, + start_addon_error: Exception | None, + start_addon_calls: int, + error_log: str, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test schedule setup addon logs error.""" + set_addon_options.side_effect = set_addon_options_error + start_addon.side_effect = start_addon_error + + await addon_manager.async_schedule_setup_addon( + {"test_key": "test"}, catch_error=True + ) + + assert error_log in caplog.text + assert set_addon_options.call_count == set_addon_options_calls + assert start_addon.call_count == start_addon_calls diff --git a/tests/components/zwave_js/conftest.py b/tests/components/zwave_js/conftest.py index 68052aeaab1..74b43e8394a 100644 --- a/tests/components/zwave_js/conftest.py +++ b/tests/components/zwave_js/conftest.py @@ -26,7 +26,7 @@ def addon_info_side_effect_fixture(): def mock_addon_info(addon_info_side_effect): """Mock Supervisor add-on info.""" with patch( - "homeassistant.components.zwave_js.addon.async_get_addon_info", + "homeassistant.components.hassio.addon_manager.async_get_addon_info", side_effect=addon_info_side_effect, ) as addon_info: addon_info.return_value = { @@ -48,7 +48,7 @@ def addon_store_info_side_effect_fixture(): def mock_addon_store_info(addon_store_info_side_effect): """Mock Supervisor add-on info.""" with patch( - "homeassistant.components.zwave_js.addon.async_get_addon_store_info", + "homeassistant.components.hassio.addon_manager.async_get_addon_store_info", side_effect=addon_store_info_side_effect, ) as addon_store_info: addon_store_info.return_value = { @@ -112,7 +112,7 @@ def set_addon_options_side_effect_fixture(addon_options): def mock_set_addon_options(set_addon_options_side_effect): """Mock set add-on options.""" with patch( - "homeassistant.components.zwave_js.addon.async_set_addon_options", + "homeassistant.components.hassio.addon_manager.async_set_addon_options", side_effect=set_addon_options_side_effect, ) as set_options: yield set_options @@ -139,7 +139,7 @@ def install_addon_side_effect_fixture(addon_store_info, addon_info): def mock_install_addon(install_addon_side_effect): """Mock install add-on.""" with patch( - "homeassistant.components.zwave_js.addon.async_install_addon", + "homeassistant.components.hassio.addon_manager.async_install_addon", side_effect=install_addon_side_effect, ) as install_addon: yield install_addon @@ -149,7 +149,7 @@ def mock_install_addon(install_addon_side_effect): def mock_update_addon(): """Mock update add-on.""" with patch( - "homeassistant.components.zwave_js.addon.async_update_addon" + "homeassistant.components.hassio.addon_manager.async_update_addon" ) as update_addon: yield update_addon @@ -174,7 +174,7 @@ def start_addon_side_effect_fixture(addon_store_info, addon_info): def mock_start_addon(start_addon_side_effect): """Mock start add-on.""" with patch( - "homeassistant.components.zwave_js.addon.async_start_addon", + "homeassistant.components.hassio.addon_manager.async_start_addon", side_effect=start_addon_side_effect, ) as start_addon: yield start_addon @@ -184,7 +184,7 @@ def mock_start_addon(start_addon_side_effect): def stop_addon_fixture(): """Mock stop add-on.""" with patch( - "homeassistant.components.zwave_js.addon.async_stop_addon" + "homeassistant.components.hassio.addon_manager.async_stop_addon" ) as stop_addon: yield stop_addon @@ -199,7 +199,7 @@ def restart_addon_side_effect_fixture(): def mock_restart_addon(restart_addon_side_effect): """Mock restart add-on.""" with patch( - "homeassistant.components.zwave_js.addon.async_restart_addon", + "homeassistant.components.hassio.addon_manager.async_restart_addon", side_effect=restart_addon_side_effect, ) as restart_addon: yield restart_addon @@ -209,7 +209,7 @@ def mock_restart_addon(restart_addon_side_effect): def uninstall_addon_fixture(): """Mock uninstall add-on.""" with patch( - "homeassistant.components.zwave_js.addon.async_uninstall_addon" + "homeassistant.components.hassio.addon_manager.async_uninstall_addon" ) as uninstall_addon: yield uninstall_addon @@ -218,7 +218,7 @@ def uninstall_addon_fixture(): def create_backup_fixture(): """Mock create backup.""" with patch( - "homeassistant.components.zwave_js.addon.async_create_backup" + "homeassistant.components.hassio.addon_manager.async_create_backup" ) as create_backup: yield create_backup diff --git a/tests/components/zwave_js/test_addon.py b/tests/components/zwave_js/test_addon.py deleted file mode 100644 index 45f732c1aa2..00000000000 --- a/tests/components/zwave_js/test_addon.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Tests for Z-Wave JS addon module.""" -import pytest - -from homeassistant.components.zwave_js.addon import AddonError, get_addon_manager -from homeassistant.components.zwave_js.const import ( - CONF_ADDON_DEVICE, - CONF_ADDON_S0_LEGACY_KEY, - CONF_ADDON_S2_ACCESS_CONTROL_KEY, - CONF_ADDON_S2_AUTHENTICATED_KEY, - CONF_ADDON_S2_UNAUTHENTICATED_KEY, -) - - -async def test_not_installed_raises_exception(hass, addon_not_installed): - """Test addon not installed raises exception.""" - addon_manager = get_addon_manager(hass) - - addon_config = { - CONF_ADDON_DEVICE: "/test", - CONF_ADDON_S0_LEGACY_KEY: "123", - CONF_ADDON_S2_ACCESS_CONTROL_KEY: "456", - CONF_ADDON_S2_AUTHENTICATED_KEY: "789", - CONF_ADDON_S2_UNAUTHENTICATED_KEY: "012", - } - - with pytest.raises(AddonError): - await addon_manager.async_configure_addon(addon_config) - - with pytest.raises(AddonError): - await addon_manager.async_update_addon() diff --git a/tests/components/zwave_js/test_config_flow.py b/tests/components/zwave_js/test_config_flow.py index f58b4187469..eacf4b61cc8 100644 --- a/tests/components/zwave_js/test_config_flow.py +++ b/tests/components/zwave_js/test_config_flow.py @@ -88,7 +88,7 @@ def discovery_info_side_effect_fixture(): def mock_get_addon_discovery_info(discovery_info, discovery_info_side_effect): """Mock get add-on discovery info.""" with patch( - "homeassistant.components.zwave_js.addon.async_get_addon_discovery_info", + "homeassistant.components.hassio.addon_manager.async_get_addon_discovery_info", side_effect=discovery_info_side_effect, return_value=discovery_info, ) as get_addon_discovery_info: From a954443795766624a888062b851e33f2d8a61bae Mon Sep 17 00:00:00 2001 From: lunmay <28674102+lunmay@users.noreply.github.com> Date: Thu, 10 Nov 2022 10:32:07 +0100 Subject: [PATCH 0342/1033] Fix string typos and consistencies in nibe_heatpump (#81902) * Fix typos and consistency * Fix typos and consistency --- .../components/nibe_heatpump/strings.json | 12 ++++++------ .../components/nibe_heatpump/translations/en.json | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/nibe_heatpump/strings.json b/homeassistant/components/nibe_heatpump/strings.json index d6e93af689a..a863b9596b1 100644 --- a/homeassistant/components/nibe_heatpump/strings.json +++ b/homeassistant/components/nibe_heatpump/strings.json @@ -6,7 +6,7 @@ "nibegw": "NibeGW", "modbus": "Modbus" }, - "description": "Pick the connection method to your pump. In general, F-series pumps require a Nibe GW custom accessory, while an S-series pump has Modbus support built-in." + "description": "Pick the connection method to your pump. In general, F-series pumps require a NibeGW custom accessory, while an S-series pump has Modbus support built-in." }, "modbus": { "data": { @@ -16,7 +16,7 @@ }, "data_description": { "modbus_url": "Modbus URL that describes the connection to your Heat Pump or MODBUS40 unit. It should be on the form:\n - `tcp://[HOST]:[PORT]` for Modbus TCP connection\n - `serial://[LOCAL DEVICE]` for a local Modbus RTU connection\n - `rfc2217://[HOST]:[PORT]` for a remote telnet based Modbus RTU connection.", - "modbus_unit": "Unit identification for you Heat Pump. Can usually be left at 0." + "modbus_unit": "Unit identification for your Heat Pump. Can usually be left at 0." } }, "nibegw": { @@ -37,13 +37,13 @@ } }, "error": { - "write": "Error on write request to pump. Verify your `Remote write port` or `Remote IP address`.", - "read": "Error on read request from pump. Verify your `Remote read port` or `Remote IP address`.", + "write": "Error on write request to pump. Verify your `Remote write port` or `Remote address`.", + "read": "Error on read request from pump. Verify your `Remote read port` or `Remote address`.", "address": "Invalid remote address specified. Address must be an IP address or a resolvable hostname.", "address_in_use": "The selected listening port is already in use on this system.", - "model": "The model selected doesn't seem to support modbus40", + "model": "The selected model doesn't seem to support MODBUS40", "unknown": "[%key:common::config_flow::error::unknown%]", - "url": "The url specified is not a well formed and supported url" + "url": "The specified URL is not well formed nor supported" } } } diff --git a/homeassistant/components/nibe_heatpump/translations/en.json b/homeassistant/components/nibe_heatpump/translations/en.json index a662743c460..afe50efb61a 100644 --- a/homeassistant/components/nibe_heatpump/translations/en.json +++ b/homeassistant/components/nibe_heatpump/translations/en.json @@ -6,11 +6,11 @@ "error": { "address": "Invalid remote address specified. Address must be an IP address or a resolvable hostname.", "address_in_use": "The selected listening port is already in use on this system.", - "model": "The model selected doesn't seem to support modbus40", - "read": "Error on read request from pump. Verify your `Remote read port` or `Remote IP address`.", + "model": "The selected model doesn't seem to support MODBUS40", + "read": "Error on read request from pump. Verify your `Remote read port` or `Remote address`.", "unknown": "Unexpected error", - "url": "The url specified is not a well formed and supported url", - "write": "Error on write request to pump. Verify your `Remote write port` or `Remote IP address`." + "url": "The specified URL is not well formed nor supported", + "write": "Error on write request to pump. Verify your `Remote write port` or `Remote address`." }, "step": { "modbus": { @@ -20,7 +20,7 @@ "model": "Model of Heat Pump" }, "data_description": { - "modbus_unit": "Unit identification for you Heat Pump. Can usually be left at 0.", + "modbus_unit": "Unit identification for your Heat Pump. Can usually be left at 0.", "modbus_url": "Modbus URL that describes the connection to your Heat Pump or MODBUS40 unit. It should be on the form:\n - `tcp://[HOST]:[PORT]` for Modbus TCP connection\n - `serial://[LOCAL DEVICE]` for a local Modbus RTU connection\n - `rfc2217://[HOST]:[PORT]` for a remote telnet based Modbus RTU connection." } }, @@ -53,7 +53,7 @@ "remote_read_port": "The port the NibeGW unit is listening for read requests on.", "remote_write_port": "The port the NibeGW unit is listening for write requests on." }, - "description": "Pick the connection method to your pump. In general, F-series pumps require a Nibe GW custom accessory, while an S-series pump has Modbus support built-in.", + "description": "Pick the connection method to your pump. In general, F-series pumps require a NibeGW custom accessory, while an S-series pump has Modbus support built-in.", "menu_options": { "modbus": "Modbus", "nibegw": "NibeGW" @@ -61,4 +61,4 @@ } } } -} \ No newline at end of file +} From 874ece195e6064bd8f4585e7b936cd5ee1c3f673 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 10 Nov 2022 13:19:21 +0100 Subject: [PATCH 0343/1033] Include config entry id in response to WS API hardware/info (#81906) --- homeassistant/components/hardkernel/hardware.py | 5 +++++ homeassistant/components/hardware/models.py | 1 + .../components/homeassistant_sky_connect/hardware.py | 1 + homeassistant/components/homeassistant_yellow/hardware.py | 7 +++++++ homeassistant/components/raspberry_pi/hardware.py | 5 +++++ tests/components/hardkernel/test_hardware.py | 1 + .../components/homeassistant_sky_connect/test_hardware.py | 2 ++ tests/components/homeassistant_yellow/test_hardware.py | 1 + tests/components/raspberry_pi/test_hardware.py | 1 + 9 files changed, 24 insertions(+) diff --git a/homeassistant/components/hardkernel/hardware.py b/homeassistant/components/hardkernel/hardware.py index 47ff5830a84..eca599960f8 100644 --- a/homeassistant/components/hardkernel/hardware.py +++ b/homeassistant/components/hardkernel/hardware.py @@ -27,6 +27,10 @@ def async_info(hass: HomeAssistant) -> list[HardwareInfo]: if not board.startswith("odroid"): raise HomeAssistantError + config_entries = [ + entry.entry_id for entry in hass.config_entries.async_entries(DOMAIN) + ] + return [ HardwareInfo( board=BoardInfo( @@ -35,6 +39,7 @@ def async_info(hass: HomeAssistant) -> list[HardwareInfo]: model=board, revision=None, ), + config_entries=config_entries, dongle=None, name=BOARD_NAMES.get(board, f"Unknown hardkernel Odroid model '{board}'"), url=None, diff --git a/homeassistant/components/hardware/models.py b/homeassistant/components/hardware/models.py index 8ce5e7be7f3..801bc9b923a 100644 --- a/homeassistant/components/hardware/models.py +++ b/homeassistant/components/hardware/models.py @@ -34,6 +34,7 @@ class HardwareInfo: name: str | None board: BoardInfo | None + config_entries: list[str] | None dongle: USBInfo | None url: str | None diff --git a/homeassistant/components/homeassistant_sky_connect/hardware.py b/homeassistant/components/homeassistant_sky_connect/hardware.py index 6eceb746756..f48e1763dd5 100644 --- a/homeassistant/components/homeassistant_sky_connect/hardware.py +++ b/homeassistant/components/homeassistant_sky_connect/hardware.py @@ -17,6 +17,7 @@ def async_info(hass: HomeAssistant) -> list[HardwareInfo]: return [ HardwareInfo( board=None, + config_entries=[entry.entry_id], dongle=USBInfo( vid=entry.data["vid"], pid=entry.data["pid"], diff --git a/homeassistant/components/homeassistant_yellow/hardware.py b/homeassistant/components/homeassistant_yellow/hardware.py index ad17eccfe7f..b67eb50ff2c 100644 --- a/homeassistant/components/homeassistant_yellow/hardware.py +++ b/homeassistant/components/homeassistant_yellow/hardware.py @@ -6,6 +6,8 @@ from homeassistant.components.hassio import get_os_info from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError +from .const import DOMAIN + BOARD_NAME = "Home Assistant Yellow" MANUFACTURER = "homeassistant" MODEL = "yellow" @@ -22,6 +24,10 @@ def async_info(hass: HomeAssistant) -> list[HardwareInfo]: if not board == "yellow": raise HomeAssistantError + config_entries = [ + entry.entry_id for entry in hass.config_entries.async_entries(DOMAIN) + ] + return [ HardwareInfo( board=BoardInfo( @@ -30,6 +36,7 @@ def async_info(hass: HomeAssistant) -> list[HardwareInfo]: model=MODEL, revision=None, ), + config_entries=config_entries, dongle=None, name=BOARD_NAME, url=None, diff --git a/homeassistant/components/raspberry_pi/hardware.py b/homeassistant/components/raspberry_pi/hardware.py index 6433b15adb5..61417f751ac 100644 --- a/homeassistant/components/raspberry_pi/hardware.py +++ b/homeassistant/components/raspberry_pi/hardware.py @@ -42,6 +42,10 @@ def async_info(hass: HomeAssistant) -> list[HardwareInfo]: if not board.startswith("rpi"): raise HomeAssistantError + config_entries = [ + entry.entry_id for entry in hass.config_entries.async_entries(DOMAIN) + ] + return [ HardwareInfo( board=BoardInfo( @@ -50,6 +54,7 @@ def async_info(hass: HomeAssistant) -> list[HardwareInfo]: model=MODELS.get(board), revision=None, ), + config_entries=config_entries, dongle=None, name=BOARD_NAMES.get(board, f"Unknown Raspberry Pi model '{board}'"), url=None, diff --git a/tests/components/hardkernel/test_hardware.py b/tests/components/hardkernel/test_hardware.py index 33602f92e3f..e35c94e4926 100644 --- a/tests/components/hardkernel/test_hardware.py +++ b/tests/components/hardkernel/test_hardware.py @@ -48,6 +48,7 @@ async def test_hardware_info(hass: HomeAssistant, hass_ws_client) -> None: "model": "odroid-n2", "revision": None, }, + "config_entries": [config_entry.entry_id], "dongle": None, "name": "Home Assistant Blue / Hardkernel Odroid-N2", "url": None, diff --git a/tests/components/homeassistant_sky_connect/test_hardware.py b/tests/components/homeassistant_sky_connect/test_hardware.py index 6226651133a..2ba305afb4a 100644 --- a/tests/components/homeassistant_sky_connect/test_hardware.py +++ b/tests/components/homeassistant_sky_connect/test_hardware.py @@ -62,6 +62,7 @@ async def test_hardware_info(hass: HomeAssistant, hass_ws_client) -> None: "hardware": [ { "board": None, + "config_entries": [config_entry.entry_id], "dongle": { "vid": "bla_vid", "pid": "bla_pid", @@ -74,6 +75,7 @@ async def test_hardware_info(hass: HomeAssistant, hass_ws_client) -> None: }, { "board": None, + "config_entries": [config_entry_2.entry_id], "dongle": { "vid": "bla_vid_2", "pid": "bla_pid_2", diff --git a/tests/components/homeassistant_yellow/test_hardware.py b/tests/components/homeassistant_yellow/test_hardware.py index 45d6fcabdfe..681c031b2e1 100644 --- a/tests/components/homeassistant_yellow/test_hardware.py +++ b/tests/components/homeassistant_yellow/test_hardware.py @@ -48,6 +48,7 @@ async def test_hardware_info(hass: HomeAssistant, hass_ws_client) -> None: "model": "yellow", "revision": None, }, + "config_entries": [config_entry.entry_id], "dongle": None, "name": "Home Assistant Yellow", "url": None, diff --git a/tests/components/raspberry_pi/test_hardware.py b/tests/components/raspberry_pi/test_hardware.py index c36fcbd1642..0f672ef98db 100644 --- a/tests/components/raspberry_pi/test_hardware.py +++ b/tests/components/raspberry_pi/test_hardware.py @@ -48,6 +48,7 @@ async def test_hardware_info(hass: HomeAssistant, hass_ws_client) -> None: "model": "1", "revision": None, }, + "config_entries": [config_entry.entry_id], "dongle": None, "name": "Raspberry Pi", "url": None, From ee9231363ffee6f61e4be9b4a7ff125798365fe4 Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Thu, 10 Nov 2022 14:25:41 +0100 Subject: [PATCH 0344/1033] Refactor KNX Config and Options flows (#80641) --- homeassistant/components/knx/config_flow.py | 510 +++++++++--------- homeassistant/components/knx/strings.json | 107 +++- .../components/knx/translations/en.json | 117 +++- tests/components/knx/test_config_flow.py | 484 +++++++---------- 4 files changed, 628 insertions(+), 590 deletions(-) diff --git a/homeassistant/components/knx/config_flow.py b/homeassistant/components/knx/config_flow.py index d6516d1d4ef..3d046ecaeec 100644 --- a/homeassistant/components/knx/config_flow.py +++ b/homeassistant/components/knx/config_flow.py @@ -1,6 +1,7 @@ """Config flow for KNX.""" from __future__ import annotations +from abc import ABC, abstractmethod from typing import Any, Final import voluptuous as vol @@ -10,13 +11,13 @@ from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT from xknx.io.gateway_scanner import GatewayDescriptor, GatewayScanner from xknx.secure import load_key_ring -from homeassistant import config_entries -from homeassistant.config_entries import ConfigEntry, OptionsFlow +from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import callback -from homeassistant.data_entry_flow import FlowResult +from homeassistant.data_entry_flow import FlowHandler, FlowResult from homeassistant.helpers import selector from homeassistant.helpers.storage import STORAGE_DIR +from homeassistant.helpers.typing import UNDEFINED from .const import ( CONF_KNX_AUTOMATIC, @@ -47,21 +48,25 @@ from .schema import ia_validator, ip_v4_validator CONF_KNX_GATEWAY: Final = "gateway" CONF_MAX_RATE_LIMIT: Final = 60 -CONF_DEFAULT_LOCAL_IP: Final = "0.0.0.0" DEFAULT_ENTRY_DATA = KNXConfigEntryData( individual_address=XKNX.DEFAULT_ADDRESS, + local_ip=None, multicast_group=DEFAULT_MCAST_GRP, multicast_port=DEFAULT_MCAST_PORT, - state_updater=CONF_KNX_DEFAULT_STATE_UPDATER, rate_limit=CONF_KNX_DEFAULT_RATE_LIMIT, + route_back=False, + state_updater=CONF_KNX_DEFAULT_STATE_UPDATER, ) CONF_KNX_TUNNELING_TYPE: Final = "tunneling_type" -CONF_KNX_LABEL_TUNNELING_TCP: Final = "TCP" -CONF_KNX_LABEL_TUNNELING_TCP_SECURE: Final = "TCP with IP Secure" -CONF_KNX_LABEL_TUNNELING_UDP: Final = "UDP" -CONF_KNX_LABEL_TUNNELING_UDP_ROUTE_BACK: Final = "UDP with route back / NAT mode" +CONF_KNX_TUNNELING_TYPE_LABELS: Final = { + CONF_KNX_TUNNELING: "UDP (Tunnelling v1)", + CONF_KNX_TUNNELING_TCP: "TCP (Tunnelling v2)", + CONF_KNX_TUNNELING_TCP_SECURE: "Secure Tunnelling (TCP)", +} + +OPTION_MANUAL_TUNNEL: Final = "Manual" _IA_SELECTOR = selector.TextSelector() _IP_SELECTOR = selector.TextSelector() @@ -75,88 +80,111 @@ _PORT_SELECTOR = vol.All( ) -class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): - """Handle a KNX config flow.""" +class KNXCommonFlow(ABC, FlowHandler): + """Base class for KNX flows.""" - VERSION = 1 + def __init__(self, initial_data: KNXConfigEntryData) -> None: + """Initialize KNXCommonFlow.""" + self.initial_data = initial_data + self._found_gateways: list[GatewayDescriptor] = [] + self._found_tunnels: list[GatewayDescriptor] = [] + self._selected_tunnel: GatewayDescriptor | None = None + self._tunneling_config: KNXConfigEntryData | None = None - _found_tunnels: list[GatewayDescriptor] - _selected_tunnel: GatewayDescriptor | None - _tunneling_config: KNXConfigEntryData | None + @abstractmethod + def finish_flow(self, new_entry_data: KNXConfigEntryData, title: str) -> FlowResult: + """Finish the flow.""" - @staticmethod - @callback - def async_get_options_flow(config_entry: ConfigEntry) -> KNXOptionsFlowHandler: - """Get the options flow for this handler.""" - return KNXOptionsFlowHandler(config_entry) - - async def async_step_user(self, user_input: dict | None = None) -> FlowResult: - """Handle a flow initialized by the user.""" - if self._async_current_entries(): - return self.async_abort(reason="single_instance_allowed") - - self._found_tunnels = [] - self._selected_tunnel = None - self._tunneling_config = None - return await self.async_step_type() - - async def async_step_type(self, user_input: dict | None = None) -> FlowResult: + async def async_step_connection_type( + self, user_input: dict | None = None + ) -> FlowResult: """Handle connection type configuration.""" if user_input is not None: connection_type = user_input[CONF_KNX_CONNECTION_TYPE] - if connection_type == CONF_KNX_AUTOMATIC: - entry_data = DEFAULT_ENTRY_DATA | KNXConfigEntryData( - connection_type=CONF_KNX_AUTOMATIC - ) - return self.async_create_entry( - title=CONF_KNX_AUTOMATIC.capitalize(), - data=entry_data, - ) - if connection_type == CONF_KNX_ROUTING: return await self.async_step_routing() - if connection_type == CONF_KNX_TUNNELING and self._found_tunnels: + if connection_type == CONF_KNX_TUNNELING: + self._found_tunnels = [ + gateway + for gateway in self._found_gateways + if gateway.supports_tunnelling + ] + self._found_tunnels.sort( + key=lambda tunnel: tunnel.individual_address.raw + if tunnel.individual_address + else 0 + ) return await self.async_step_tunnel() - return await self.async_step_manual_tunnel() + # Automatic connection type + entry_data = KNXConfigEntryData(connection_type=CONF_KNX_AUTOMATIC) + return self.finish_flow( + new_entry_data=entry_data, + title=CONF_KNX_AUTOMATIC.capitalize(), + ) supported_connection_types = { CONF_KNX_TUNNELING: CONF_KNX_TUNNELING.capitalize(), CONF_KNX_ROUTING: CONF_KNX_ROUTING.capitalize(), } - if gateways := await scan_for_gateways(): + self._found_gateways = await scan_for_gateways() + if self._found_gateways: # add automatic at first position only if a gateway responded supported_connection_types = { CONF_KNX_AUTOMATIC: CONF_KNX_AUTOMATIC.capitalize() } | supported_connection_types - self._found_tunnels = [ - gateway for gateway in gateways if gateway.supports_tunnelling - ] fields = { vol.Required(CONF_KNX_CONNECTION_TYPE): vol.In(supported_connection_types) } - return self.async_show_form(step_id="type", data_schema=vol.Schema(fields)) + return self.async_show_form( + step_id="connection_type", data_schema=vol.Schema(fields) + ) async def async_step_tunnel(self, user_input: dict | None = None) -> FlowResult: """Select a tunnel from a list. Will be skipped if the gateway scan was unsuccessful or if only one gateway was found.""" if user_input is not None: + if user_input[CONF_KNX_GATEWAY] == OPTION_MANUAL_TUNNEL: + if self._found_tunnels: + self._selected_tunnel = self._found_tunnels[0] + return await self.async_step_manual_tunnel() + self._selected_tunnel = next( tunnel for tunnel in self._found_tunnels if user_input[CONF_KNX_GATEWAY] == str(tunnel) ) - return await self.async_step_manual_tunnel() + connection_type = ( + CONF_KNX_TUNNELING_TCP_SECURE + if self._selected_tunnel.tunnelling_requires_secure + else CONF_KNX_TUNNELING_TCP + if self._selected_tunnel.supports_tunnelling_tcp + else CONF_KNX_TUNNELING + ) + self._tunneling_config = KNXConfigEntryData( + host=self._selected_tunnel.ip_addr, + port=self._selected_tunnel.port, + route_back=False, + connection_type=connection_type, + ) + if connection_type == CONF_KNX_TUNNELING_TCP_SECURE: + return self.async_show_menu( + step_id="secure_tunneling", + menu_options=["secure_knxkeys", "secure_tunnel_manual"], + ) + return self.finish_flow( + new_entry_data=self._tunneling_config, + title=f"Tunneling @ {self._selected_tunnel}", + ) - # skip this step if the user has only one unique gateway. - if len(self._found_tunnels) == 1: - self._selected_tunnel = self._found_tunnels[0] + if not self._found_tunnels: return await self.async_step_manual_tunnel() errors: dict = {} - tunnels_repr = {str(tunnel) for tunnel in self._found_tunnels} - fields = {vol.Required(CONF_KNX_GATEWAY): vol.In(tunnels_repr)} + tunnel_options = [str(tunnel) for tunnel in self._found_tunnels] + tunnel_options.append(OPTION_MANUAL_TUNNEL) + fields = {vol.Required(CONF_KNX_GATEWAY): vol.In(tunnel_options)} return self.async_show_form( step_id="tunnel", data_schema=vol.Schema(fields), errors=errors @@ -182,61 +210,83 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if not errors: connection_type = user_input[CONF_KNX_TUNNELING_TYPE] - entry_data = DEFAULT_ENTRY_DATA | KNXConfigEntryData( + self._tunneling_config = KNXConfigEntryData( host=_host, port=user_input[CONF_PORT], - route_back=( - connection_type == CONF_KNX_LABEL_TUNNELING_UDP_ROUTE_BACK - ), + route_back=user_input[CONF_KNX_ROUTE_BACK], local_ip=_local_ip, - connection_type=( - CONF_KNX_TUNNELING_TCP - if connection_type == CONF_KNX_LABEL_TUNNELING_TCP - else CONF_KNX_TUNNELING - ), + connection_type=connection_type, ) - if connection_type == CONF_KNX_LABEL_TUNNELING_TCP_SECURE: - self._tunneling_config = entry_data + if connection_type == CONF_KNX_TUNNELING_TCP_SECURE: return self.async_show_menu( step_id="secure_tunneling", - menu_options=["secure_knxkeys", "secure_manual"], + menu_options=["secure_knxkeys", "secure_tunnel_manual"], ) - - return self.async_create_entry( + return self.finish_flow( + new_entry_data=self._tunneling_config, title=f"Tunneling @ {_host}", - data=entry_data, ) - connection_methods: list[str] = [ - CONF_KNX_LABEL_TUNNELING_TCP, - CONF_KNX_LABEL_TUNNELING_UDP, - CONF_KNX_LABEL_TUNNELING_TCP_SECURE, - CONF_KNX_LABEL_TUNNELING_UDP_ROUTE_BACK, - ] - ip_address = "" - port = DEFAULT_MCAST_PORT - if self._selected_tunnel is not None: + _reconfiguring_existing_tunnel = ( + self.initial_data.get(CONF_KNX_CONNECTION_TYPE) + in CONF_KNX_TUNNELING_TYPE_LABELS + ) + if ( # initial attempt on ConfigFlow or coming from automatic / routing + (isinstance(self, ConfigFlow) or not _reconfiguring_existing_tunnel) + and not user_input + and self._selected_tunnel is not None + ): # default to first found tunnel ip_address = self._selected_tunnel.ip_addr port = self._selected_tunnel.port - if not self._selected_tunnel.supports_tunnelling_tcp: - connection_methods.remove(CONF_KNX_LABEL_TUNNELING_TCP) - connection_methods.remove(CONF_KNX_LABEL_TUNNELING_TCP_SECURE) + if self._selected_tunnel.tunnelling_requires_secure: + default_type = CONF_KNX_TUNNELING_TCP_SECURE + elif self._selected_tunnel.supports_tunnelling_tcp: + default_type = CONF_KNX_TUNNELING_TCP + else: + default_type = CONF_KNX_TUNNELING + else: # OptionFlow, no tunnel discovered or user input + ip_address = ( + user_input[CONF_HOST] + if user_input + else self.initial_data.get(CONF_HOST) + ) + port = ( + user_input[CONF_PORT] + if user_input + else self.initial_data.get(CONF_PORT, DEFAULT_MCAST_PORT) + ) + default_type = ( + user_input[CONF_KNX_TUNNELING_TYPE] + if user_input + else self.initial_data[CONF_KNX_CONNECTION_TYPE] + if _reconfiguring_existing_tunnel + else CONF_KNX_TUNNELING + ) + _route_back: bool = self.initial_data.get( + CONF_KNX_ROUTE_BACK, not bool(self._selected_tunnel) + ) fields = { - vol.Required(CONF_KNX_TUNNELING_TYPE): vol.In(connection_methods), + vol.Required(CONF_KNX_TUNNELING_TYPE, default=default_type): vol.In( + CONF_KNX_TUNNELING_TYPE_LABELS + ), vol.Required(CONF_HOST, default=ip_address): _IP_SELECTOR, vol.Required(CONF_PORT, default=port): _PORT_SELECTOR, + vol.Required( + CONF_KNX_ROUTE_BACK, default=_route_back + ): selector.BooleanSelector(), } - if self.show_advanced_options: fields[vol.Optional(CONF_KNX_LOCAL_IP)] = _IP_SELECTOR + if not self._found_tunnels: + errors["base"] = "no_tunnel_discovered" return self.async_show_form( step_id="manual_tunnel", data_schema=vol.Schema(fields), errors=errors ) - async def async_step_secure_manual( + async def async_step_secure_tunnel_manual( self, user_input: dict | None = None ) -> FlowResult: """Configure ip secure manually.""" @@ -250,14 +300,16 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): user_id=user_input[CONF_KNX_SECURE_USER_ID], user_password=user_input[CONF_KNX_SECURE_USER_PASSWORD], ) - - return self.async_create_entry( + return self.finish_flow( + new_entry_data=entry_data, title=f"Secure Tunneling @ {self._tunneling_config[CONF_HOST]}", - data=entry_data, ) fields = { - vol.Required(CONF_KNX_SECURE_USER_ID, default=2): vol.All( + vol.Required( + CONF_KNX_SECURE_USER_ID, + default=self.initial_data.get(CONF_KNX_SECURE_USER_ID, 2), + ): vol.All( selector.NumberSelector( selector.NumberSelectorConfig( min=1, max=127, mode=selector.NumberSelectorMode.BOX @@ -265,16 +317,24 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): ), vol.Coerce(int), ), - vol.Required(CONF_KNX_SECURE_USER_PASSWORD): selector.TextSelector( + vol.Required( + CONF_KNX_SECURE_USER_PASSWORD, + default=self.initial_data.get(CONF_KNX_SECURE_USER_PASSWORD), + ): selector.TextSelector( selector.TextSelectorConfig(type=selector.TextSelectorType.PASSWORD), ), - vol.Required(CONF_KNX_SECURE_DEVICE_AUTHENTICATION): selector.TextSelector( + vol.Required( + CONF_KNX_SECURE_DEVICE_AUTHENTICATION, + default=self.initial_data.get(CONF_KNX_SECURE_DEVICE_AUTHENTICATION), + ): selector.TextSelector( selector.TextSelectorConfig(type=selector.TextSelectorType.PASSWORD), ), } return self.async_show_form( - step_id="secure_manual", data_schema=vol.Schema(fields), errors=errors + step_id="secure_tunnel_manual", + data_schema=vol.Schema(fields), + errors=errors, ) async def async_step_secure_knxkeys( @@ -302,15 +362,20 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): knxkeys_filename=storage_key, knxkeys_password=user_input[CONF_KNX_KNXKEY_PASSWORD], ) - - return self.async_create_entry( + return self.finish_flow( + new_entry_data=entry_data, title=f"Secure Tunneling @ {self._tunneling_config[CONF_HOST]}", - data=entry_data, ) fields = { - vol.Required(CONF_KNX_KNXKEY_FILENAME): selector.TextSelector(), - vol.Required(CONF_KNX_KNXKEY_PASSWORD): selector.TextSelector(), + vol.Required( + CONF_KNX_KNXKEY_FILENAME, + default=self.initial_data.get(CONF_KNX_KNXKEY_FILENAME), + ): selector.TextSelector(), + vol.Required( + CONF_KNX_KNXKEY_PASSWORD, + default=self.initial_data.get(CONF_KNX_KNXKEY_PASSWORD), + ): selector.TextSelector(), } return self.async_show_form( @@ -323,10 +388,17 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): _individual_address = ( user_input[CONF_KNX_INDIVIDUAL_ADDRESS] if user_input - else XKNX.DEFAULT_ADDRESS + else self.initial_data[CONF_KNX_INDIVIDUAL_ADDRESS] ) _multicast_group = ( - user_input[CONF_KNX_MCAST_GRP] if user_input else DEFAULT_MCAST_GRP + user_input[CONF_KNX_MCAST_GRP] + if user_input + else self.initial_data[CONF_KNX_MCAST_GRP] + ) + _multicast_port = ( + user_input[CONF_KNX_MCAST_PORT] + if user_input + else self.initial_data[CONF_KNX_MCAST_PORT] ) if user_input is not None: @@ -345,15 +417,16 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): errors[CONF_KNX_LOCAL_IP] = "invalid_ip_address" if not errors: - entry_data = DEFAULT_ENTRY_DATA | KNXConfigEntryData( + entry_data = KNXConfigEntryData( connection_type=CONF_KNX_ROUTING, individual_address=_individual_address, multicast_group=_multicast_group, - multicast_port=user_input[CONF_KNX_MCAST_PORT], + multicast_port=_multicast_port, local_ip=_local_ip, ) - return self.async_create_entry( - title=CONF_KNX_ROUTING.capitalize(), data=entry_data + return self.finish_flow( + new_entry_data=entry_data, + title=f"Routing as {_individual_address}", ) fields = { @@ -361,101 +434,112 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): CONF_KNX_INDIVIDUAL_ADDRESS, default=_individual_address ): _IA_SELECTOR, vol.Required(CONF_KNX_MCAST_GRP, default=_multicast_group): _IP_SELECTOR, - vol.Required( - CONF_KNX_MCAST_PORT, default=DEFAULT_MCAST_PORT - ): _PORT_SELECTOR, + vol.Required(CONF_KNX_MCAST_PORT, default=_multicast_port): _PORT_SELECTOR, } if self.show_advanced_options: # Optional with default doesn't work properly in flow UI fields[vol.Optional(CONF_KNX_LOCAL_IP)] = _IP_SELECTOR + if not any( + router for router in self._found_gateways if router.supports_routing + ): + errors["base"] = "no_router_discovered" return self.async_show_form( step_id="routing", data_schema=vol.Schema(fields), errors=errors ) -class KNXOptionsFlowHandler(OptionsFlow): +class KNXConfigFlow(KNXCommonFlow, ConfigFlow, domain=DOMAIN): + """Handle a KNX config flow.""" + + VERSION = 1 + + def __init__(self) -> None: + """Initialize KNX options flow.""" + super().__init__(initial_data=DEFAULT_ENTRY_DATA) + + @staticmethod + @callback + def async_get_options_flow(config_entry: ConfigEntry) -> KNXOptionsFlow: + """Get the options flow for this handler.""" + return KNXOptionsFlow(config_entry) + + @callback + def finish_flow(self, new_entry_data: KNXConfigEntryData, title: str) -> FlowResult: + """Create the ConfigEntry.""" + return self.async_create_entry( + title=title, + data=DEFAULT_ENTRY_DATA | new_entry_data, + ) + + async def async_step_user(self, user_input: dict | None = None) -> FlowResult: + """Handle a flow initialized by the user.""" + if self._async_current_entries(): + return self.async_abort(reason="single_instance_allowed") + return await self.async_step_connection_type() + + +class KNXOptionsFlow(KNXCommonFlow, OptionsFlow): """Handle KNX options.""" general_settings: dict - current_config: dict def __init__(self, config_entry: ConfigEntry) -> None: """Initialize KNX options flow.""" self.config_entry = config_entry + super().__init__(initial_data=config_entry.data) # type: ignore[arg-type] + + @callback + def finish_flow( + self, new_entry_data: KNXConfigEntryData, title: str | None + ) -> FlowResult: + """Update the ConfigEntry and finish the flow.""" + new_data = DEFAULT_ENTRY_DATA | self.initial_data | new_entry_data + self.hass.config_entries.async_update_entry( + self.config_entry, + data=new_data, + title=title or UNDEFINED, + ) + return self.async_create_entry(title="", data={}) async def async_step_init( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Manage KNX options.""" - if user_input is not None: - self.general_settings = user_input - return await self.async_step_tunnel() + return self.async_show_menu( + step_id="options_init", + menu_options=["connection_type", "communication_settings"], + ) - supported_connection_types = [ - CONF_KNX_AUTOMATIC, - CONF_KNX_TUNNELING, - CONF_KNX_ROUTING, - ] - self.current_config = self.config_entry.data # type: ignore[assignment] + async def async_step_communication_settings( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Manage KNX communication settings.""" + if user_input is not None: + return self.finish_flow( + new_entry_data=KNXConfigEntryData( + state_updater=user_input[CONF_KNX_STATE_UPDATER], + rate_limit=user_input[CONF_KNX_RATE_LIMIT], + ), + title=None, + ) data_schema = { vol.Required( - CONF_KNX_CONNECTION_TYPE, - default=( - CONF_KNX_TUNNELING - if self.current_config.get(CONF_KNX_CONNECTION_TYPE) - == CONF_KNX_TUNNELING_TCP - else self.current_config.get(CONF_KNX_CONNECTION_TYPE) - ), - ): vol.In(supported_connection_types), - vol.Required( - CONF_KNX_INDIVIDUAL_ADDRESS, - default=self.current_config[CONF_KNX_INDIVIDUAL_ADDRESS], - ): selector.TextSelector(), - vol.Required( - CONF_KNX_MCAST_GRP, - default=self.current_config.get(CONF_KNX_MCAST_GRP, DEFAULT_MCAST_GRP), - ): _IP_SELECTOR, - vol.Required( - CONF_KNX_MCAST_PORT, - default=self.current_config.get( - CONF_KNX_MCAST_PORT, DEFAULT_MCAST_PORT - ), - ): _PORT_SELECTOR, - } - - if self.show_advanced_options: - local_ip = ( - self.current_config.get(CONF_KNX_LOCAL_IP) - if self.current_config.get(CONF_KNX_LOCAL_IP) is not None - else CONF_DEFAULT_LOCAL_IP - ) - data_schema[ - vol.Required( - CONF_KNX_LOCAL_IP, - default=local_ip, - ) - ] = _IP_SELECTOR - data_schema[ - vol.Required( + CONF_KNX_STATE_UPDATER, + default=self.initial_data.get( CONF_KNX_STATE_UPDATER, - default=self.current_config.get( - CONF_KNX_STATE_UPDATER, - CONF_KNX_DEFAULT_STATE_UPDATER, - ), - ) - ] = selector.BooleanSelector() - data_schema[ - vol.Required( + CONF_KNX_DEFAULT_STATE_UPDATER, + ), + ): selector.BooleanSelector(), + vol.Required( + CONF_KNX_RATE_LIMIT, + default=self.initial_data.get( CONF_KNX_RATE_LIMIT, - default=self.current_config.get( - CONF_KNX_RATE_LIMIT, - CONF_KNX_DEFAULT_RATE_LIMIT, - ), - ) - ] = vol.All( + CONF_KNX_DEFAULT_RATE_LIMIT, + ), + ): vol.All( selector.NumberSelector( selector.NumberSelectorConfig( min=0, @@ -464,96 +548,14 @@ class KNXOptionsFlowHandler(OptionsFlow): ), ), vol.Coerce(int), - ) - + ), + } return self.async_show_form( - step_id="init", + step_id="communication_settings", data_schema=vol.Schema(data_schema), - last_step=self.current_config.get(CONF_KNX_CONNECTION_TYPE) - != CONF_KNX_TUNNELING, + last_step=True, ) - async def async_step_tunnel( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Manage KNX tunneling options.""" - if ( - self.general_settings.get(CONF_KNX_CONNECTION_TYPE) == CONF_KNX_TUNNELING - and user_input is None - ): - connection_methods: list[str] = [ - CONF_KNX_LABEL_TUNNELING_TCP, - CONF_KNX_LABEL_TUNNELING_UDP, - CONF_KNX_LABEL_TUNNELING_UDP_ROUTE_BACK, - ] - return self.async_show_form( - step_id="tunnel", - data_schema=vol.Schema( - { - vol.Required( - CONF_KNX_TUNNELING_TYPE, - default=get_knx_tunneling_type(self.current_config), - ): vol.In(connection_methods), - vol.Required( - CONF_HOST, default=self.current_config.get(CONF_HOST) - ): _IP_SELECTOR, - vol.Required( - CONF_PORT, default=self.current_config.get(CONF_PORT, 3671) - ): _PORT_SELECTOR, - } - ), - last_step=True, - ) - - _local_ip = self.general_settings.get(CONF_KNX_LOCAL_IP) - entry_data = ( - DEFAULT_ENTRY_DATA - | self.general_settings - | KNXConfigEntryData( - host=self.current_config.get(CONF_HOST, ""), - local_ip=_local_ip if _local_ip != CONF_DEFAULT_LOCAL_IP else None, - ) - ) - - if user_input is not None: - connection_type = user_input[CONF_KNX_TUNNELING_TYPE] - entry_data = entry_data | KNXConfigEntryData( - host=user_input[CONF_HOST], - port=user_input[CONF_PORT], - route_back=(connection_type == CONF_KNX_LABEL_TUNNELING_UDP_ROUTE_BACK), - connection_type=( - CONF_KNX_TUNNELING_TCP - if connection_type == CONF_KNX_LABEL_TUNNELING_TCP - else CONF_KNX_TUNNELING - ), - ) - - entry_title = str(entry_data[CONF_KNX_CONNECTION_TYPE]).capitalize() - if entry_data[CONF_KNX_CONNECTION_TYPE] == CONF_KNX_TUNNELING: - entry_title = f"Tunneling @ {entry_data[CONF_HOST]}" - if entry_data[CONF_KNX_CONNECTION_TYPE] == CONF_KNX_TUNNELING_TCP: - entry_title = f"Tunneling @ {entry_data[CONF_HOST]} (TCP)" - - self.hass.config_entries.async_update_entry( - self.config_entry, - data=entry_data, - title=entry_title, - ) - - return self.async_create_entry(title="", data={}) - - -def get_knx_tunneling_type(config_entry_data: dict) -> str: - """Obtain the knx tunneling type based on the data in the config entry data.""" - connection_type = config_entry_data[CONF_KNX_CONNECTION_TYPE] - route_back = config_entry_data.get(CONF_KNX_ROUTE_BACK, False) - if route_back and connection_type == CONF_KNX_TUNNELING: - return CONF_KNX_LABEL_TUNNELING_UDP_ROUTE_BACK - if connection_type == CONF_KNX_TUNNELING_TCP: - return CONF_KNX_LABEL_TUNNELING_TCP - - return CONF_KNX_LABEL_TUNNELING_UDP - async def scan_for_gateways(stop_on_found: int = 0) -> list[GatewayDescriptor]: """Scan for gateways within the network.""" diff --git a/homeassistant/components/knx/strings.json b/homeassistant/components/knx/strings.json index c8161462d66..d87ad6ac177 100644 --- a/homeassistant/components/knx/strings.json +++ b/homeassistant/components/knx/strings.json @@ -1,7 +1,7 @@ { "config": { "step": { - "type": { + "connection_type": { "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing.", "data": { "connection_type": "KNX Connection Type" @@ -19,11 +19,13 @@ "tunneling_type": "KNX Tunneling Type", "port": "[%key:common::config_flow::data::port%]", "host": "[%key:common::config_flow::data::host%]", + "route_back": "Route back / NAT mode", "local_ip": "Local IP of Home Assistant" }, "data_description": { "port": "Port of the KNX/IP tunneling device.", "host": "IP address of the KNX/IP tunneling device.", + "route_back": "Enable if your KNXnet/IP tunneling server is behind NAT. Only applies for UDP connections.", "local_ip": "Leave blank to use auto-discovery." } }, @@ -31,7 +33,7 @@ "description": "Select how you want to configure KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", - "secure_manual": "Configure IP secure keys manually" + "secure_tunnel_manual": "Configure IP secure keys manually" } }, "secure_knxkeys": { @@ -45,7 +47,7 @@ "knxkeys_password": "This was set when exporting the file from ETS." } }, - "secure_manual": { + "secure_tunnel_manual": { "description": "Please enter your IP secure information.", "data": { "user_id": "User ID", @@ -81,41 +83,110 @@ "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", "invalid_ip_address": "Invalid IPv4 address.", "invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", - "file_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/" + "file_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/", + "no_router_discovered": "No KNXnet/IP router was discovered on the network.", + "no_tunnel_discovered": "Could not find a KNX tunneling server on your network." } }, "options": { "step": { - "init": { + "options_init": { + "menu_options": { + "connection_type": "Configure KNX interface", + "communication_settings": "Communication settings" + } + }, + "communication_settings": { "data": { - "connection_type": "KNX Connection Type", - "individual_address": "Default individual address", - "multicast_group": "[%key:component::knx::config::step::routing::data::multicast_group%]", - "multicast_port": "[%key:component::knx::config::step::routing::data::multicast_port%]", - "local_ip": "Local IP of Home Assistant", "state_updater": "State updater", "rate_limit": "Rate limit" }, "data_description": { - "individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`", - "multicast_group": "Used for routing and discovery. Default: `224.0.23.12`", - "multicast_port": "Used for routing and discovery. Default: `3671`", - "local_ip": "Use `0.0.0.0` for auto-discovery.", "state_updater": "Set default for reading states from the KNX Bus. When disabled, Home Assistant will not actively retrieve entity states from the KNX Bus. Can be overridden by `sync_state` entity options.", - "rate_limit": "Maximum outgoing telegrams per second.\nRecommended: 20 to 40" + "rate_limit": "Maximum outgoing telegrams per second.\n`0` to disable limit. Recommended: 0 or 20 to 40" + } + }, + "connection_type": { + "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing.", + "data": { + "connection_type": "KNX Connection Type" } }, "tunnel": { + "description": "[%key:component::knx::config::step::tunnel::description%]", "data": { - "tunneling_type": "KNX Tunneling Type", + "gateway": "[%key:component::knx::config::step::tunnel::data::gateway%]" + } + }, + "manual_tunnel": { + "description": "[%key:component::knx::config::step::manual_tunnel::description%]", + "data": { + "tunneling_type": "[%key:component::knx::config::step::manual_tunnel::data::tunneling_type%]", "port": "[%key:common::config_flow::data::port%]", - "host": "[%key:common::config_flow::data::host%]" + "host": "[%key:common::config_flow::data::host%]", + "route_back": "[%key:component::knx::config::step::manual_tunnel::data::route_back%]", + "local_ip": "[%key:component::knx::config::step::manual_tunnel::data::local_ip%]" }, "data_description": { "port": "[%key:component::knx::config::step::manual_tunnel::data_description::port%]", - "host": "[%key:component::knx::config::step::manual_tunnel::data_description::host%]" + "host": "[%key:component::knx::config::step::manual_tunnel::data_description::host%]", + "route_back": "[%key:component::knx::config::step::manual_tunnel::data_description::route_back%]", + "local_ip": "[%key:component::knx::config::step::manual_tunnel::data_description::local_ip%]" + } + }, + "secure_tunneling": { + "description": "[%key:component::knx::config::step::secure_tunneling::description%]", + "menu_options": { + "secure_knxkeys": "[%key:component::knx::config::step::secure_tunneling::menu_options::secure_knxkeys%]", + "secure_tunnel_manual": "[%key:component::knx::config::step::secure_tunneling::menu_options::secure_tunnel_manual%]" + } + }, + "secure_knxkeys": { + "description": "[%key:component::knx::config::step::secure_knxkeys::description%]", + "data": { + "knxkeys_filename": "[%key:component::knx::config::step::secure_knxkeys::data::knxkeys_filename%]", + "knxkeys_password": "[%key:component::knx::config::step::secure_knxkeys::data::knxkeys_password%]" + }, + "data_description": { + "knxkeys_filename": "[%key:component::knx::config::step::secure_knxkeys::data_description::knxkeys_filename%]", + "knxkeys_password": "[%key:component::knx::config::step::secure_knxkeys::data_description::knxkeys_password%]" + } + }, + "secure_tunnel_manual": { + "description": "[%key:component::knx::config::step::secure_tunnel_manual::description%]", + "data": { + "user_id": "[%key:component::knx::config::step::secure_tunnel_manual::data::user_id%]", + "user_password": "[%key:component::knx::config::step::secure_tunnel_manual::data::user_password%]", + "device_authentication": "[%key:component::knx::config::step::secure_tunnel_manual::data::device_authentication%]" + }, + "data_description": { + "user_id": "[%key:component::knx::config::step::secure_tunnel_manual::data_description::user_id%]", + "user_password": "[%key:component::knx::config::step::secure_tunnel_manual::data_description::user_password%]", + "device_authentication": "[%key:component::knx::config::step::secure_tunnel_manual::data_description::device_authentication%]" + } + }, + "routing": { + "description": "[%key:component::knx::config::step::routing::description%]", + "data": { + "individual_address": "[%key:component::knx::config::step::routing::data::individual_address%]", + "multicast_group": "[%key:component::knx::config::step::routing::data::multicast_group%]", + "multicast_port": "[%key:component::knx::config::step::routing::data::multicast_port%]", + "local_ip": "[%key:component::knx::config::step::routing::data::local_ip%]" + }, + "data_description": { + "individual_address": "[%key:component::knx::config::step::routing::data_description::individual_address%]", + "local_ip": "[%key:component::knx::config::step::routing::data_description::local_ip%]" } } + }, + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_individual_address": "[%key:component::knx::config::error::invalid_individual_address%]", + "invalid_ip_address": "[%key:component::knx::config::error::invalid_ip_address%]", + "invalid_signature": "[%key:component::knx::config::error::invalid_signature%]", + "file_not_found": "[%key:component::knx::config::error::file_not_found%]", + "no_router_discovered": "[%key:component::knx::config::error::no_router_discovered%]", + "no_tunnel_discovered": "[%key:component::knx::config::error::no_tunnel_discovered%]" } } } diff --git a/homeassistant/components/knx/translations/en.json b/homeassistant/components/knx/translations/en.json index 6dffe059b2a..c45c98b070a 100644 --- a/homeassistant/components/knx/translations/en.json +++ b/homeassistant/components/knx/translations/en.json @@ -9,20 +9,30 @@ "file_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/", "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", "invalid_ip_address": "Invalid IPv4 address.", - "invalid_signature": "The password to decrypt the `.knxkeys` file is wrong." + "invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", + "no_router_discovered": "No KNXnet/IP router was discovered on the network.", + "no_tunnel_discovered": "Could not find a KNX tunneling server on your network." }, "step": { + "connection_type": { + "data": { + "connection_type": "KNX Connection Type" + }, + "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing." + }, "manual_tunnel": { "data": { "host": "Host", "local_ip": "Local IP of Home Assistant", "port": "Port", + "route_back": "Route back / NAT mode", "tunneling_type": "KNX Tunneling Type" }, "data_description": { "host": "IP address of the KNX/IP tunneling device.", "local_ip": "Leave blank to use auto-discovery.", - "port": "Port of the KNX/IP tunneling device." + "port": "Port of the KNX/IP tunneling device.", + "route_back": "Enable if your KNXnet/IP tunneling server is behind NAT. Only applies for UDP connections." }, "description": "Please enter the connection information of your tunneling device." }, @@ -50,7 +60,7 @@ }, "description": "Please enter the information for your `.knxkeys` file." }, - "secure_manual": { + "secure_tunnel_manual": { "data": { "device_authentication": "Device authentication password", "user_id": "User ID", @@ -67,7 +77,7 @@ "description": "Select how you want to configure KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", - "secure_manual": "Configure IP secure keys manually" + "secure_tunnel_manual": "Configure IP secure keys manually" } }, "tunnel": { @@ -75,46 +85,107 @@ "gateway": "KNX Tunnel Connection" }, "description": "Please select a gateway from the list." - }, - "type": { - "data": { - "connection_type": "KNX Connection Type" - }, - "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing." } } }, "options": { + "error": { + "cannot_connect": "Failed to connect", + "file_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/", + "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", + "invalid_ip_address": "Invalid IPv4 address.", + "invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", + "no_router_discovered": "No KNXnet/IP router was discovered on the network.", + "no_tunnel_discovered": "Could not find a KNX tunneling server on your network." + }, "step": { - "init": { + "communication_settings": { "data": { - "connection_type": "KNX Connection Type", - "individual_address": "Default individual address", - "local_ip": "Local IP of Home Assistant", - "multicast_group": "Multicast group", - "multicast_port": "Multicast port", "rate_limit": "Rate limit", "state_updater": "State updater" }, "data_description": { - "individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`", - "local_ip": "Use `0.0.0.0` for auto-discovery.", - "multicast_group": "Used for routing and discovery. Default: `224.0.23.12`", - "multicast_port": "Used for routing and discovery. Default: `3671`", - "rate_limit": "Maximum outgoing telegrams per second.\nRecommended: 20 to 40", + "rate_limit": "Maximum outgoing telegrams per second.\n`0` to disable limit. Recommended: 0 or 20 to 40", "state_updater": "Set default for reading states from the KNX Bus. When disabled, Home Assistant will not actively retrieve entity states from the KNX Bus. Can be overridden by `sync_state` entity options." } }, - "tunnel": { + "connection_type": { + "data": { + "connection_type": "KNX Connection Type" + }, + "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing." + }, + "manual_tunnel": { "data": { "host": "Host", + "local_ip": "Local IP of Home Assistant", "port": "Port", + "route_back": "Route back / NAT mode", "tunneling_type": "KNX Tunneling Type" }, "data_description": { "host": "IP address of the KNX/IP tunneling device.", - "port": "Port of the KNX/IP tunneling device." + "local_ip": "Leave blank to use auto-discovery.", + "port": "Port of the KNX/IP tunneling device.", + "route_back": "Enable if your KNXnet/IP tunneling server is behind NAT. Only applies for UDP connections." + }, + "description": "Please enter the connection information of your tunneling device." + }, + "options_init": { + "menu_options": { + "communication_settings": "Communication settings", + "connection_type": "Configure KNX interface" } + }, + "routing": { + "data": { + "individual_address": "Individual address", + "local_ip": "Local IP of Home Assistant", + "multicast_group": "Multicast group", + "multicast_port": "Multicast port" + }, + "data_description": { + "individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`", + "local_ip": "Leave blank to use auto-discovery." + }, + "description": "Please configure the routing options." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "The filename of your `.knxkeys` file (including extension)", + "knxkeys_password": "The password to decrypt the `.knxkeys` file" + }, + "data_description": { + "knxkeys_filename": "The file is expected to be found in your config directory in `.storage/knx/`.\nIn Home Assistant OS this would be `/config/.storage/knx/`\nExample: `my_project.knxkeys`", + "knxkeys_password": "This was set when exporting the file from ETS." + }, + "description": "Please enter the information for your `.knxkeys` file." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Device authentication password", + "user_id": "User ID", + "user_password": "User password" + }, + "data_description": { + "device_authentication": "This is set in the 'IP' panel of the interface in ETS.", + "user_id": "This is often tunnel number +1. So 'Tunnel 2' would have User-ID '3'.", + "user_password": "Password for the specific tunnel connection set in the 'Properties' panel of the tunnel in ETS." + }, + "description": "Please enter your IP secure information." + }, + "secure_tunneling": { + "description": "Select how you want to configure KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", + "secure_tunnel_manual": "Configure IP secure keys manually" + } + }, + "tunnel": { + "data": { + "gateway": "KNX Tunnel Connection" + }, + "description": "Please select a gateway from the list." } } } diff --git a/tests/components/knx/test_config_flow.py b/tests/components/knx/test_config_flow.py index 30b8aa537a6..fdef15ed217 100644 --- a/tests/components/knx/test_config_flow.py +++ b/tests/components/knx/test_config_flow.py @@ -3,24 +3,20 @@ from unittest.mock import patch import pytest from xknx.exceptions.exception import InvalidSignature -from xknx.io import DEFAULT_MCAST_GRP +from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT from xknx.io.gateway_scanner import GatewayDescriptor from homeassistant import config_entries from homeassistant.components.knx.config_flow import ( - CONF_DEFAULT_LOCAL_IP, CONF_KNX_GATEWAY, - CONF_KNX_LABEL_TUNNELING_TCP, - CONF_KNX_LABEL_TUNNELING_TCP_SECURE, - CONF_KNX_LABEL_TUNNELING_UDP, - CONF_KNX_LABEL_TUNNELING_UDP_ROUTE_BACK, CONF_KNX_TUNNELING_TYPE, DEFAULT_ENTRY_DATA, - get_knx_tunneling_type, + OPTION_MANUAL_TUNNEL, ) from homeassistant.components.knx.const import ( CONF_KNX_AUTOMATIC, CONF_KNX_CONNECTION_TYPE, + CONF_KNX_DEFAULT_STATE_UPDATER, CONF_KNX_INDIVIDUAL_ADDRESS, CONF_KNX_KNXKEY_FILENAME, CONF_KNX_KNXKEY_PASSWORD, @@ -41,16 +37,19 @@ from homeassistant.components.knx.const import ( ) from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import HomeAssistant -from homeassistant.data_entry_flow import FlowResultType +from homeassistant.data_entry_flow import FlowResult, FlowResultType from tests.common import MockConfigEntry def _gateway_descriptor( - ip: str, port: int, supports_tunnelling_tcp: bool = False + ip: str, + port: int, + supports_tunnelling_tcp: bool = False, + requires_secure: bool = False, ) -> GatewayDescriptor: """Get mock gw descriptor.""" - return GatewayDescriptor( + descriptor = GatewayDescriptor( name="Test", ip_addr=ip, port=port, @@ -60,6 +59,9 @@ def _gateway_descriptor( supports_tunnelling=True, supports_tunnelling_tcp=supports_tunnelling_tcp, ) + descriptor.tunnelling_requires_secure = requires_secure + descriptor.routing_requires_secure = requires_secure + return descriptor async def test_user_single_instance(hass): @@ -92,7 +94,7 @@ async def test_routing_setup(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "routing" - assert not result2["errors"] + assert result2["errors"] == {"base": "no_router_discovered"} with patch( "homeassistant.components.knx.async_setup_entry", @@ -108,7 +110,7 @@ async def test_routing_setup(hass: HomeAssistant) -> None: ) await hass.async_block_till_done() assert result3["type"] == FlowResultType.CREATE_ENTRY - assert result3["title"] == CONF_KNX_ROUTING.capitalize() + assert result3["title"] == "Routing as 1.1.110" assert result3["data"] == { **DEFAULT_ENTRY_DATA, CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING, @@ -144,7 +146,7 @@ async def test_routing_setup_advanced(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "routing" - assert not result2["errors"] + assert result2["errors"] == {"base": "no_router_discovered"} # invalid user input result_invalid_input = await hass.config_entries.flow.async_configure( @@ -163,6 +165,7 @@ async def test_routing_setup_advanced(hass: HomeAssistant) -> None: CONF_KNX_MCAST_GRP: "invalid_ip_address", CONF_KNX_INDIVIDUAL_ADDRESS: "invalid_individual_address", CONF_KNX_LOCAL_IP: "invalid_ip_address", + "base": "no_router_discovered", } # valid user input @@ -181,7 +184,7 @@ async def test_routing_setup_advanced(hass: HomeAssistant) -> None: ) await hass.async_block_till_done() assert result3["type"] == FlowResultType.CREATE_ENTRY - assert result3["title"] == CONF_KNX_ROUTING.capitalize() + assert result3["title"] == "Routing as 1.1.110" assert result3["data"] == { **DEFAULT_ENTRY_DATA, CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING, @@ -199,9 +202,10 @@ async def test_routing_setup_advanced(hass: HomeAssistant) -> None: [ ( { - CONF_KNX_TUNNELING_TYPE: CONF_KNX_LABEL_TUNNELING_UDP, + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING, CONF_HOST: "192.168.0.1", CONF_PORT: 3675, + CONF_KNX_ROUTE_BACK: False, }, { **DEFAULT_ENTRY_DATA, @@ -215,9 +219,10 @@ async def test_routing_setup_advanced(hass: HomeAssistant) -> None: ), ( { - CONF_KNX_TUNNELING_TYPE: CONF_KNX_LABEL_TUNNELING_TCP, + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING_TCP, CONF_HOST: "192.168.0.1", CONF_PORT: 3675, + CONF_KNX_ROUTE_BACK: False, }, { **DEFAULT_ENTRY_DATA, @@ -231,9 +236,10 @@ async def test_routing_setup_advanced(hass: HomeAssistant) -> None: ), ( { - CONF_KNX_TUNNELING_TYPE: CONF_KNX_LABEL_TUNNELING_UDP_ROUTE_BACK, + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING, CONF_HOST: "192.168.0.1", CONF_PORT: 3675, + CONF_KNX_ROUTE_BACK: True, }, { **DEFAULT_ENTRY_DATA, @@ -247,13 +253,12 @@ async def test_routing_setup_advanced(hass: HomeAssistant) -> None: ), ], ) -async def test_tunneling_setup( +async def test_tunneling_setup_manual( hass: HomeAssistant, user_input, config_entry_data ) -> None: - """Test tunneling if only one gateway is found.""" - gateway = _gateway_descriptor("192.168.0.1", 3675, True) + """Test tunneling if no gateway was found found (or `manual` option was chosen).""" with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [gateway] + gateways.return_value = [] result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -269,7 +274,7 @@ async def test_tunneling_setup( await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "manual_tunnel" - assert not result2["errors"] + assert result2["errors"] == {"base": "no_tunnel_discovered"} with patch( "homeassistant.components.knx.async_setup_entry", @@ -289,9 +294,8 @@ async def test_tunneling_setup( async def test_tunneling_setup_for_local_ip(hass: HomeAssistant) -> None: """Test tunneling if only one gateway is found.""" - gateway = _gateway_descriptor("192.168.0.2", 3675) with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [gateway] + gateways.return_value = [] result = await hass.config_entries.flow.async_init( DOMAIN, context={ @@ -311,13 +315,13 @@ async def test_tunneling_setup_for_local_ip(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "manual_tunnel" - assert not result2["errors"] + assert result2["errors"] == {"base": "no_tunnel_discovered"} # invalid host ip address result_invalid_host = await hass.config_entries.flow.async_configure( result2["flow_id"], { - CONF_KNX_TUNNELING_TYPE: CONF_KNX_LABEL_TUNNELING_UDP, + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING, CONF_HOST: DEFAULT_MCAST_GRP, # multicast addresses are invalid CONF_PORT: 3675, CONF_KNX_LOCAL_IP: "192.168.1.112", @@ -326,12 +330,15 @@ async def test_tunneling_setup_for_local_ip(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert result_invalid_host["type"] == FlowResultType.FORM assert result_invalid_host["step_id"] == "manual_tunnel" - assert result_invalid_host["errors"] == {CONF_HOST: "invalid_ip_address"} + assert result_invalid_host["errors"] == { + CONF_HOST: "invalid_ip_address", + "base": "no_tunnel_discovered", + } # invalid local ip address result_invalid_local = await hass.config_entries.flow.async_configure( result2["flow_id"], { - CONF_KNX_TUNNELING_TYPE: CONF_KNX_LABEL_TUNNELING_UDP, + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING, CONF_HOST: "192.168.0.2", CONF_PORT: 3675, CONF_KNX_LOCAL_IP: "asdf", @@ -340,7 +347,10 @@ async def test_tunneling_setup_for_local_ip(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert result_invalid_local["type"] == FlowResultType.FORM assert result_invalid_local["step_id"] == "manual_tunnel" - assert result_invalid_local["errors"] == {CONF_KNX_LOCAL_IP: "invalid_ip_address"} + assert result_invalid_local["errors"] == { + CONF_KNX_LOCAL_IP: "invalid_ip_address", + "base": "no_tunnel_discovered", + } # valid user input with patch( @@ -350,7 +360,7 @@ async def test_tunneling_setup_for_local_ip(hass: HomeAssistant) -> None: result3 = await hass.config_entries.flow.async_configure( result2["flow_id"], { - CONF_KNX_TUNNELING_TYPE: CONF_KNX_LABEL_TUNNELING_UDP, + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING, CONF_HOST: "192.168.0.2", CONF_PORT: 3675, CONF_KNX_LOCAL_IP: "192.168.1.112", @@ -395,29 +405,17 @@ async def test_tunneling_setup_for_multiple_found_gateways(hass: HomeAssistant) assert tunnel_flow["step_id"] == "tunnel" assert not tunnel_flow["errors"] - manual_tunnel = await hass.config_entries.flow.async_configure( - tunnel_flow["flow_id"], - {CONF_KNX_GATEWAY: str(gateway)}, - ) - await hass.async_block_till_done() - assert manual_tunnel["type"] == FlowResultType.FORM - assert manual_tunnel["step_id"] == "manual_tunnel" - with patch( "homeassistant.components.knx.async_setup_entry", return_value=True, ) as mock_setup_entry: - manual_tunnel_flow = await hass.config_entries.flow.async_configure( - manual_tunnel["flow_id"], - { - CONF_KNX_TUNNELING_TYPE: CONF_KNX_LABEL_TUNNELING_UDP, - CONF_HOST: "192.168.0.1", - CONF_PORT: 3675, - }, + result = await hass.config_entries.flow.async_configure( + tunnel_flow["flow_id"], + {CONF_KNX_GATEWAY: str(gateway)}, ) await hass.async_block_till_done() - assert manual_tunnel_flow["type"] == FlowResultType.CREATE_ENTRY - assert manual_tunnel_flow["data"] == { + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["data"] == { **DEFAULT_ENTRY_DATA, CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, CONF_HOST: "192.168.0.1", @@ -430,10 +428,22 @@ async def test_tunneling_setup_for_multiple_found_gateways(hass: HomeAssistant) assert len(mock_setup_entry.mock_calls) == 1 -async def test_manual_tunnel_step_when_no_gateway(hass: HomeAssistant) -> None: - """Test manual tunnel if no gateway is found and tunneling is selected.""" +@pytest.mark.parametrize( + "gateway", + [ + _gateway_descriptor("192.168.0.1", 3675), + _gateway_descriptor("192.168.0.1", 3675, supports_tunnelling_tcp=True), + _gateway_descriptor( + "192.168.0.1", 3675, supports_tunnelling_tcp=True, requires_secure=True + ), + ], +) +async def test_manual_tunnel_step_with_found_gateway( + hass: HomeAssistant, gateway +) -> None: + """Test manual tunnel if gateway was found and tunneling is selected.""" with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [] + gateways.return_value = [gateway] result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -448,9 +458,20 @@ async def test_manual_tunnel_step_when_no_gateway(hass: HomeAssistant) -> None: ) await hass.async_block_till_done() assert tunnel_flow["type"] == FlowResultType.FORM - assert tunnel_flow["step_id"] == "manual_tunnel" + assert tunnel_flow["step_id"] == "tunnel" assert not tunnel_flow["errors"] + manual_tunnel_flow = await hass.config_entries.flow.async_configure( + tunnel_flow["flow_id"], + { + CONF_KNX_GATEWAY: OPTION_MANUAL_TUNNEL, + }, + ) + await hass.async_block_till_done() + assert manual_tunnel_flow["type"] == FlowResultType.FORM + assert manual_tunnel_flow["step_id"] == "manual_tunnel" + assert not manual_tunnel_flow["errors"] + async def test_form_with_automatic_connection_handling(hass: HomeAssistant) -> None: """Test we get the form.""" @@ -484,9 +505,14 @@ async def test_form_with_automatic_connection_handling(hass: HomeAssistant) -> N assert len(mock_setup_entry.mock_calls) == 1 -async def _get_menu_step(hass: HomeAssistant) -> None: - """Test ip secure manuel.""" - gateway = _gateway_descriptor("192.168.0.1", 3675, True) +async def _get_menu_step(hass: HomeAssistant) -> FlowResult: + """Return flow in secure_tunnellinn menu step.""" + gateway = _gateway_descriptor( + "192.168.0.1", + 3675, + supports_tunnelling_tcp=True, + requires_secure=True, + ) with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: gateways.return_value = [gateway] result = await hass.config_entries.flow.async_init( @@ -503,16 +529,12 @@ async def _get_menu_step(hass: HomeAssistant) -> None: ) await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM - assert result2["step_id"] == "manual_tunnel" + assert result2["step_id"] == "tunnel" assert not result2["errors"] result3 = await hass.config_entries.flow.async_configure( result2["flow_id"], - { - CONF_KNX_TUNNELING_TYPE: CONF_KNX_LABEL_TUNNELING_TCP_SECURE, - CONF_HOST: "192.168.0.1", - CONF_PORT: 3675, - }, + {CONF_KNX_GATEWAY: str(gateway)}, ) await hass.async_block_till_done() assert result3["type"] == FlowResultType.MENU @@ -520,23 +542,72 @@ async def _get_menu_step(hass: HomeAssistant) -> None: return result3 -async def test_configure_secure_manual(hass: HomeAssistant): - """Test configure secure manual.""" +async def test_get_secure_menu_step_manual_tunnelling( + hass: HomeAssistant, +): + """Test flow reaches secure_tunnellinn menu step from manual tunnelling configuration.""" + gateway = _gateway_descriptor( + "192.168.0.1", + 3675, + supports_tunnelling_tcp=True, + requires_secure=True, + ) + with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: + gateways.return_value = [gateway] + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert not result["errors"] + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, + }, + ) + await hass.async_block_till_done() + assert result2["type"] == FlowResultType.FORM + assert result2["step_id"] == "tunnel" + assert not result2["errors"] + + manual_tunnel_flow = await hass.config_entries.flow.async_configure( + result2["flow_id"], + { + CONF_KNX_GATEWAY: OPTION_MANUAL_TUNNEL, + }, + ) + + result3 = await hass.config_entries.flow.async_configure( + manual_tunnel_flow["flow_id"], + { + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, + CONF_HOST: "192.168.0.1", + CONF_PORT: 3675, + }, + ) + await hass.async_block_till_done() + assert result3["type"] == FlowResultType.MENU + assert result3["step_id"] == "secure_tunneling" + + +async def test_configure_secure_tunnel_manual(hass: HomeAssistant): + """Test configure tunnelling secure keys manually.""" menu_step = await _get_menu_step(hass) result = await hass.config_entries.flow.async_configure( menu_step["flow_id"], - {"next_step_id": "secure_manual"}, + {"next_step_id": "secure_tunnel_manual"}, ) assert result["type"] == FlowResultType.FORM - assert result["step_id"] == "secure_manual" + assert result["step_id"] == "secure_tunnel_manual" assert not result["errors"] with patch( "homeassistant.components.knx.async_setup_entry", return_value=True, ) as mock_setup_entry: - secure_manual = await hass.config_entries.flow.async_configure( + secure_tunnel_manual = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_KNX_SECURE_USER_ID: 2, @@ -545,8 +616,8 @@ async def test_configure_secure_manual(hass: HomeAssistant): }, ) await hass.async_block_till_done() - assert secure_manual["type"] == FlowResultType.CREATE_ENTRY - assert secure_manual["data"] == { + assert secure_tunnel_manual["type"] == FlowResultType.CREATE_ENTRY + assert secure_tunnel_manual["data"] == { **DEFAULT_ENTRY_DATA, CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, CONF_KNX_SECURE_USER_ID: 2, @@ -662,265 +733,88 @@ async def test_configure_secure_knxkeys_invalid_signature(hass: HomeAssistant): assert secure_knxkeys["errors"][CONF_KNX_KNXKEY_PASSWORD] == "invalid_signature" -async def test_options_flow( +async def test_options_flow_connection_type( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: - """Test options config flow.""" + """Test options flow changing interface.""" mock_config_entry.add_to_hass(hass) - gateway = _gateway_descriptor("192.168.0.1", 3675) + + menu_step = await hass.config_entries.options.async_init(mock_config_entry.entry_id) + with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: gateways.return_value = [gateway] - result = await hass.config_entries.options.async_init( - mock_config_entry.entry_id + result = await hass.config_entries.options.async_configure( + menu_step["flow_id"], + {"next_step_id": "connection_type"}, ) assert result.get("type") == FlowResultType.FORM - assert result.get("step_id") == "init" - assert "flow_id" in result - - result2 = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={ - CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.255", - CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - }, - ) - - await hass.async_block_till_done() - assert result2.get("type") == FlowResultType.CREATE_ENTRY - assert not result2.get("data") - - assert mock_config_entry.data == { - CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.255", - CONF_HOST: "", - CONF_KNX_LOCAL_IP: None, - CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_RATE_LIMIT: 20, - CONF_KNX_STATE_UPDATER: True, - } - - -@pytest.mark.parametrize( - "user_input,config_entry_data", - [ - ( - { - CONF_KNX_TUNNELING_TYPE: CONF_KNX_LABEL_TUNNELING_UDP_ROUTE_BACK, - CONF_HOST: "192.168.1.1", - CONF_PORT: 3675, - }, - { - CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.255", - CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_RATE_LIMIT: 20, - CONF_KNX_STATE_UPDATER: True, - CONF_KNX_LOCAL_IP: None, - CONF_HOST: "192.168.1.1", - CONF_PORT: 3675, - CONF_KNX_ROUTE_BACK: True, - }, - ), - ( - { - CONF_KNX_TUNNELING_TYPE: CONF_KNX_LABEL_TUNNELING_UDP, - CONF_HOST: "192.168.1.1", - CONF_PORT: 3675, - }, - { - CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.255", - CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_RATE_LIMIT: 20, - CONF_KNX_STATE_UPDATER: True, - CONF_KNX_LOCAL_IP: None, - CONF_HOST: "192.168.1.1", - CONF_PORT: 3675, - CONF_KNX_ROUTE_BACK: False, - }, - ), - ( - { - CONF_KNX_TUNNELING_TYPE: CONF_KNX_LABEL_TUNNELING_TCP, - CONF_HOST: "192.168.1.1", - CONF_PORT: 3675, - }, - { - CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.255", - CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_RATE_LIMIT: 20, - CONF_KNX_STATE_UPDATER: True, - CONF_KNX_LOCAL_IP: None, - CONF_HOST: "192.168.1.1", - CONF_PORT: 3675, - CONF_KNX_ROUTE_BACK: False, - }, - ), - ], -) -async def test_tunneling_options_flow( - hass: HomeAssistant, - mock_config_entry: MockConfigEntry, - user_input, - config_entry_data, -) -> None: - """Test options flow for tunneling.""" - mock_config_entry.add_to_hass(hass) - - gateway = _gateway_descriptor("192.168.0.1", 3675) - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [gateway] - result = await hass.config_entries.options.async_init( - mock_config_entry.entry_id - ) - - assert result.get("type") == FlowResultType.FORM - assert result.get("step_id") == "init" - assert "flow_id" in result + assert result.get("step_id") == "connection_type" result2 = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.255", - CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, }, ) - assert result2.get("type") == FlowResultType.FORM - assert not result2.get("data") - assert "flow_id" in result2 + assert result2.get("step_id") == "tunnel" result3 = await hass.config_entries.options.async_configure( result2["flow_id"], - user_input=user_input, + user_input={ + CONF_KNX_GATEWAY: str(gateway), + }, ) - await hass.async_block_till_done() assert result3.get("type") == FlowResultType.CREATE_ENTRY assert not result3.get("data") - assert mock_config_entry.data == config_entry_data + assert mock_config_entry.data == { + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, + CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", + CONF_HOST: "192.168.0.1", + CONF_PORT: 3675, + CONF_KNX_LOCAL_IP: None, + CONF_KNX_MCAST_PORT: DEFAULT_MCAST_PORT, + CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, + CONF_KNX_RATE_LIMIT: 20, + CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, + CONF_KNX_ROUTE_BACK: False, + } -@pytest.mark.parametrize( - "user_input,config_entry_data", - [ - ( - { - CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", - CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_RATE_LIMIT: 25, - CONF_KNX_STATE_UPDATER: False, - CONF_KNX_LOCAL_IP: "192.168.1.112", - }, - { - CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", - CONF_HOST: "", - CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_RATE_LIMIT: 25, - CONF_KNX_STATE_UPDATER: False, - CONF_KNX_LOCAL_IP: "192.168.1.112", - }, - ), - ( - { - CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", - CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_RATE_LIMIT: 25, - CONF_KNX_STATE_UPDATER: False, - CONF_KNX_LOCAL_IP: CONF_DEFAULT_LOCAL_IP, - }, - { - CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", - CONF_HOST: "", - CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_RATE_LIMIT: 25, - CONF_KNX_STATE_UPDATER: False, - CONF_KNX_LOCAL_IP: None, - }, - ), - ], -) -async def test_advanced_options( - hass: HomeAssistant, - mock_config_entry: MockConfigEntry, - user_input, - config_entry_data, +async def test_options_communication_settings( + hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: - """Test options config flow.""" + """Test options flow changing communication settings.""" mock_config_entry.add_to_hass(hass) - gateway = _gateway_descriptor("192.168.0.1", 3675) - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [gateway] - result = await hass.config_entries.options.async_init( - mock_config_entry.entry_id, context={"show_advanced_options": True} - ) + menu_step = await hass.config_entries.options.async_init(mock_config_entry.entry_id) - assert result.get("type") == FlowResultType.FORM - assert result.get("step_id") == "init" - assert "flow_id" in result + result = await hass.config_entries.options.async_configure( + menu_step["flow_id"], + {"next_step_id": "communication_settings"}, + ) + assert result.get("type") == FlowResultType.FORM + assert result.get("step_id") == "communication_settings" - result2 = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input=user_input, - ) + result2 = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_KNX_STATE_UPDATER: False, + CONF_KNX_RATE_LIMIT: 0, + }, + ) - await hass.async_block_till_done() - assert result2.get("type") == FlowResultType.CREATE_ENTRY - assert not result2.get("data") + await hass.async_block_till_done() + assert result2.get("type") == FlowResultType.CREATE_ENTRY + assert not result2.get("data") - assert mock_config_entry.data == config_entry_data - - -@pytest.mark.parametrize( - "config_entry_data,result", - [ - ( - { - CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, - CONF_KNX_ROUTE_BACK: False, - }, - CONF_KNX_LABEL_TUNNELING_UDP, - ), - ( - { - CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, - CONF_KNX_ROUTE_BACK: True, - }, - CONF_KNX_LABEL_TUNNELING_UDP_ROUTE_BACK, - ), - ( - { - CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP, - CONF_KNX_ROUTE_BACK: False, - }, - CONF_KNX_LABEL_TUNNELING_TCP, - ), - ], -) -async def test_get_knx_tunneling_type( - config_entry_data, - result, -) -> None: - """Test converting config entry data to tunneling type for config flow.""" - assert get_knx_tunneling_type(config_entry_data) == result + assert mock_config_entry.data == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC, + CONF_KNX_STATE_UPDATER: False, + CONF_KNX_RATE_LIMIT: 0, + } From 9bd676aff644aab1419458f80ace8bc11a806b8a Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 10 Nov 2022 14:44:55 +0100 Subject: [PATCH 0345/1033] Improve automation reload (#81854) * Improve automation reload * Small tweak * Improve --- .../components/automation/__init__.py | 21 ++++++++- tests/components/automation/test_init.py | 43 ++++++++++++++++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 234fcc97839..9581a6b1c40 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -817,9 +817,28 @@ async def _async_process_config( """ automation_matches: set[int] = set() config_matches: set[int] = set() + automation_configs_with_id: dict[str, tuple[int, AutomationEntityConfig]] = {} + automation_configs_without_id: list[tuple[int, AutomationEntityConfig]] = [] + + for config_idx, config in enumerate(automation_configs): + if automation_id := config.config_block.get(CONF_ID): + automation_configs_with_id[automation_id] = (config_idx, config) + continue + automation_configs_without_id.append((config_idx, config)) for automation_idx, automation in enumerate(automations): - for config_idx, config in enumerate(automation_configs): + if automation.unique_id: + if automation.unique_id not in automation_configs_with_id: + continue + config_idx, config = automation_configs_with_id.pop( + automation.unique_id + ) + if automation_matches_config(automation, config): + automation_matches.add(automation_idx) + config_matches.add(config_idx) + continue + + for config_idx, config in automation_configs_without_id: if config_idx in config_matches: # Only allow an automation config to match at most once continue diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index f40309bf7f6..742ae85ed68 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -738,7 +738,8 @@ async def test_automation_stops(hass, calls, service): assert len(calls) == (1 if service == "turn_off_no_stop" else 0) -async def test_reload_unchanged_does_not_stop(hass, calls): +@pytest.mark.parametrize("extra_config", ({}, {"id": "sun"})) +async def test_reload_unchanged_does_not_stop(hass, calls, extra_config): """Test that reloading stops any running actions as appropriate.""" test_entity = "test.entity" @@ -753,6 +754,7 @@ async def test_reload_unchanged_does_not_stop(hass, calls): ], } } + config[automation.DOMAIN].update(**extra_config) assert await async_setup_component(hass, automation.DOMAIN, config) running = asyncio.Event() @@ -970,6 +972,41 @@ async def test_reload_identical_automations_without_id(hass, calls): }, } }, + { + "id": "sun", + "trigger": {"platform": "event", "event_type": "test_event"}, + "action": [{"service": "test.automation"}], + }, + # An automation using templates + { + "id": "sun", + "trigger": {"platform": "event", "event_type": "test_event"}, + "action": [{"service": "{{ 'test.automation' }}"}], + }, + # An automation using blueprint + { + "id": "sun", + "use_blueprint": { + "path": "test_event_service.yaml", + "input": { + "trigger_event": "test_event", + "service_to_call": "test.automation", + "a_number": 5, + }, + }, + }, + # An automation using blueprint with templated input + { + "id": "sun", + "use_blueprint": { + "path": "test_event_service.yaml", + "input": { + "trigger_event": "{{ 'test_event' }}", + "service_to_call": "{{ 'test.automation' }}", + "a_number": 5, + }, + }, + }, ), ) async def test_reload_unchanged_automation(hass, calls, automation_config): @@ -1004,7 +1041,8 @@ async def test_reload_unchanged_automation(hass, calls, automation_config): assert len(calls) == 2 -async def test_reload_automation_when_blueprint_changes(hass, calls): +@pytest.mark.parametrize("extra_config", ({}, {"id": "sun"})) +async def test_reload_automation_when_blueprint_changes(hass, calls, extra_config): """Test an automation is updated at reload if the blueprint has changed.""" with patch( "homeassistant.components.automation.AutomationEntity", wraps=AutomationEntity @@ -1023,6 +1061,7 @@ async def test_reload_automation_when_blueprint_changes(hass, calls): } ] } + config[automation.DOMAIN][0].update(**extra_config) assert await async_setup_component(hass, automation.DOMAIN, config) assert automation_entity_init.call_count == 1 automation_entity_init.reset_mock() From 7500d0c61cfea3508bca9c53c144c0bc9c035142 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 10 Nov 2022 15:24:56 +0100 Subject: [PATCH 0346/1033] Refactor MQTT_WILL_BIRTH_SCHEMA (#81879) * Refactor MQTT_WILL_BIRTH_SCHEMA * Refactor and move birth/will validation to utils * Simplify birth will validation --- homeassistant/components/mqtt/__init__.py | 4 +-- homeassistant/components/mqtt/config.py | 4 +-- homeassistant/components/mqtt/config_flow.py | 6 ++--- .../components/mqtt/config_integration.py | 26 ++++--------------- homeassistant/components/mqtt/util.py | 21 +++++++++++---- 5 files changed, 28 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 06921105aae..f7c9e5fbe86 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -95,12 +95,12 @@ from .models import ( # noqa: F401 ReceivePayloadType, ) from .util import ( - _VALID_QOS_SCHEMA, async_create_certificate_temp_files, get_mqtt_data, migrate_certificate_file_to_content, mqtt_config_entry_enabled, valid_publish_topic, + valid_qos_schema, valid_subscribe_topic, ) @@ -172,7 +172,7 @@ MQTT_PUBLISH_SCHEMA = vol.All( vol.Exclusive(ATTR_TOPIC_TEMPLATE, CONF_TOPIC): cv.string, vol.Exclusive(ATTR_PAYLOAD, CONF_PAYLOAD): cv.string, vol.Exclusive(ATTR_PAYLOAD_TEMPLATE, CONF_PAYLOAD): cv.string, - vol.Optional(ATTR_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA, + vol.Optional(ATTR_QOS, default=DEFAULT_QOS): valid_qos_schema, vol.Optional(ATTR_RETAIN, default=DEFAULT_RETAIN): cv.boolean, }, required=True, diff --git a/homeassistant/components/mqtt/config.py b/homeassistant/components/mqtt/config.py index 8cfc3490f0c..88adcac7194 100644 --- a/homeassistant/components/mqtt/config.py +++ b/homeassistant/components/mqtt/config.py @@ -16,10 +16,10 @@ from .const import ( DEFAULT_QOS, DEFAULT_RETAIN, ) -from .util import _VALID_QOS_SCHEMA, valid_publish_topic, valid_subscribe_topic +from .util import valid_publish_topic, valid_qos_schema, valid_subscribe_topic SCHEMA_BASE = { - vol.Optional(CONF_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA, + vol.Optional(CONF_QOS, default=DEFAULT_QOS): valid_qos_schema, vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string, } diff --git a/homeassistant/components/mqtt/config_flow.py b/homeassistant/components/mqtt/config_flow.py index ec818348701..5d8b19ce31a 100644 --- a/homeassistant/components/mqtt/config_flow.py +++ b/homeassistant/components/mqtt/config_flow.py @@ -71,9 +71,9 @@ from .const import ( SUPPORTED_PROTOCOLS, ) from .util import ( - MQTT_WILL_BIRTH_SCHEMA, async_create_certificate_temp_files, get_file_path, + valid_birth_will, valid_publish_topic, ) @@ -326,7 +326,7 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow): CONF_BIRTH_MESSAGE, _birth_will("birth"), "bad_birth", - MQTT_WILL_BIRTH_SCHEMA, + valid_birth_will, ) if not user_input["birth_enable"]: options_config[CONF_BIRTH_MESSAGE] = {} @@ -336,7 +336,7 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow): CONF_WILL_MESSAGE, _birth_will("will"), "bad_will", - MQTT_WILL_BIRTH_SCHEMA, + valid_birth_will, ) if not user_input["will_enable"]: options_config[CONF_WILL_MESSAGE] = {} diff --git a/homeassistant/components/mqtt/config_integration.py b/homeassistant/components/mqtt/config_integration.py index 2be125c2c12..9319a48ac3d 100644 --- a/homeassistant/components/mqtt/config_integration.py +++ b/homeassistant/components/mqtt/config_integration.py @@ -36,10 +36,6 @@ from . import ( vacuum as vacuum_platform, ) from .const import ( - ATTR_PAYLOAD, - ATTR_QOS, - ATTR_RETAIN, - ATTR_TOPIC, CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CERTIFICATE, @@ -56,12 +52,10 @@ from .const import ( DEFAULT_PORT, DEFAULT_PREFIX, DEFAULT_PROTOCOL, - DEFAULT_QOS, - DEFAULT_RETAIN, DEFAULT_WILL, SUPPORTED_PROTOCOLS, ) -from .util import _VALID_QOS_SCHEMA, valid_publish_topic +from .util import valid_birth_will, valid_publish_topic DEFAULT_TLS_PROTOCOL = "auto" @@ -144,16 +138,6 @@ CLIENT_KEY_AUTH_MSG = ( "the MQTT broker configuration" ) -MQTT_WILL_BIRTH_SCHEMA = vol.Schema( - { - vol.Inclusive(ATTR_TOPIC, "topic_payload"): valid_publish_topic, - vol.Inclusive(ATTR_PAYLOAD, "topic_payload"): cv.string, - vol.Optional(ATTR_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA, - vol.Optional(ATTR_RETAIN, default=DEFAULT_RETAIN): cv.boolean, - }, - required=True, -) - CONFIG_SCHEMA_ENTRY = vol.Schema( { vol.Optional(CONF_CLIENT_ID): cv.string, @@ -170,8 +154,8 @@ CONFIG_SCHEMA_ENTRY = vol.Schema( vol.Optional(CONF_TLS_INSECURE): cv.boolean, vol.Optional(CONF_TLS_VERSION): vol.Any("auto", "1.0", "1.1", "1.2"), vol.Optional(CONF_PROTOCOL): vol.All(cv.string, vol.In(SUPPORTED_PROTOCOLS)), - vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, - vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, + vol.Optional(CONF_WILL_MESSAGE): valid_birth_will, + vol.Optional(CONF_BIRTH_MESSAGE): valid_birth_will, vol.Optional(CONF_DISCOVERY): cv.boolean, # discovery_prefix must be a valid publish topic because if no # state topic is specified, it will be created with the given prefix. @@ -197,8 +181,8 @@ CONFIG_SCHEMA_BASE = PLATFORM_CONFIG_SCHEMA_BASE.extend( vol.Optional(CONF_TLS_INSECURE): cv.boolean, vol.Optional(CONF_TLS_VERSION): vol.Any("auto", "1.0", "1.1", "1.2"), vol.Optional(CONF_PROTOCOL): vol.All(cv.string, vol.In(SUPPORTED_PROTOCOLS)), - vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, - vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, + vol.Optional(CONF_WILL_MESSAGE): valid_birth_will, + vol.Optional(CONF_BIRTH_MESSAGE): valid_birth_will, vol.Optional(CONF_DISCOVERY): cv.boolean, # discovery_prefix must be a valid publish topic because if no # state topic is specified, it will be created with the given prefix. diff --git a/homeassistant/components/mqtt/util.py b/homeassistant/components/mqtt/util.py index 0b2d10977aa..ab907854499 100644 --- a/homeassistant/components/mqtt/util.py +++ b/homeassistant/components/mqtt/util.py @@ -9,7 +9,6 @@ from typing import Any import voluptuous as vol -from homeassistant.const import CONF_PAYLOAD from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv, template from homeassistant.helpers.typing import ConfigType @@ -32,6 +31,8 @@ from .models import MqttData TEMP_DIR_NAME = f"home-assistant-{DOMAIN}" +_VALID_QOS_SCHEMA = vol.All(vol.Coerce(int), vol.In([0, 1, 2])) + def mqtt_config_entry_enabled(hass: HomeAssistant) -> bool | None: """Return true when the MQTT config entry is enabled.""" @@ -112,19 +113,29 @@ def valid_publish_topic(topic: Any) -> str: return validated_topic -_VALID_QOS_SCHEMA = vol.All(vol.Coerce(int), vol.In([0, 1, 2])) +def valid_qos_schema(qos: Any) -> int: + """Validate that QOS value is valid.""" + return _VALID_QOS_SCHEMA(qos) -MQTT_WILL_BIRTH_SCHEMA = vol.Schema( + +_MQTT_WILL_BIRTH_SCHEMA = vol.Schema( { vol.Required(ATTR_TOPIC): valid_publish_topic, - vol.Required(ATTR_PAYLOAD, CONF_PAYLOAD): cv.string, - vol.Optional(ATTR_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA, + vol.Required(ATTR_PAYLOAD): cv.string, + vol.Optional(ATTR_QOS, default=DEFAULT_QOS): valid_qos_schema, vol.Optional(ATTR_RETAIN, default=DEFAULT_RETAIN): cv.boolean, }, required=True, ) +def valid_birth_will(config: ConfigType) -> ConfigType: + """Validate a birth or will configuration and required topic/payload.""" + if config: + config = _MQTT_WILL_BIRTH_SCHEMA(config) + return config + + def get_mqtt_data(hass: HomeAssistant, ensure_exists: bool = False) -> MqttData: """Return typed MqttData from hass.data[DATA_MQTT].""" if ensure_exists: From ae2b2acab5049a6f30615fa7ae3d4ef0848125e1 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 10 Nov 2022 17:25:42 +0100 Subject: [PATCH 0347/1033] Fix grammar in tts service description (#81916) --- homeassistant/components/tts/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index 0e0c41e5e30..5914512a315 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -240,7 +240,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # Register the service description service_desc = { - CONF_NAME: f"Say an TTS message with {p_type}", + CONF_NAME: f"Say a TTS message with {p_type}", CONF_DESCRIPTION: f"Say something using text-to-speech on a media player with {p_type}.", CONF_FIELDS: services_dict[SERVICE_SAY][CONF_FIELDS], } From 2f9982d1c70f2d9fdd25de2c87a2565961fe027c Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 10 Nov 2022 17:27:14 +0100 Subject: [PATCH 0348/1033] Fix race when deleting a script (#81897) --- homeassistant/components/config/script.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/config/script.py b/homeassistant/components/config/script.py index 45a7a6dc227..73f89aaf509 100644 --- a/homeassistant/components/config/script.py +++ b/homeassistant/components/config/script.py @@ -16,9 +16,8 @@ async def async_setup(hass): async def hook(action, config_key): """post_write_hook for Config View that reloads scripts.""" - await hass.services.async_call(DOMAIN, SERVICE_RELOAD) - if action != ACTION_DELETE: + await hass.services.async_call(DOMAIN, SERVICE_RELOAD) return ent_reg = er.async_get(hass) From 25d54f407e65647c331e1269f2ed2acd624c9fbb Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 10 Nov 2022 17:27:26 +0100 Subject: [PATCH 0349/1033] Fix race when deleting a scene (#81896) --- homeassistant/components/config/scene.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/config/scene.py b/homeassistant/components/config/scene.py index 862c8c46f4d..befbfd052af 100644 --- a/homeassistant/components/config/scene.py +++ b/homeassistant/components/config/scene.py @@ -15,9 +15,8 @@ async def async_setup(hass): async def hook(action, config_key): """post_write_hook for Config View that reloads scenes.""" - await hass.services.async_call(DOMAIN, SERVICE_RELOAD) - if action != ACTION_DELETE: + await hass.services.async_call(DOMAIN, SERVICE_RELOAD) return ent_reg = entity_registry.async_get(hass) From a2da1c7db56747049d513e599bbf3be20bfa64c5 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 10 Nov 2022 17:28:19 +0100 Subject: [PATCH 0350/1033] Create repairs issue if an outdated currency code is configured in core store (#81772) * Create repairs issue if an outdated currency code is configured in core store * Update homeassistant/config.py Co-authored-by: Aarni Koskela Co-authored-by: Aarni Koskela --- homeassistant/config.py | 34 +++++++++++++++++----------------- tests/test_config.py | 27 +++++++++++++++++++++------ 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/homeassistant/config.py b/homeassistant/config.py index 8e06c2c47a2..c58f94ca197 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -48,14 +48,9 @@ from .const import ( LEGACY_CONF_WHITELIST_EXTERNAL_DIRS, __version__, ) -from .core import ( - DOMAIN as CONF_CORE, - ConfigSource, - HomeAssistant, - async_get_hass, - callback, -) +from .core import DOMAIN as CONF_CORE, ConfigSource, HomeAssistant, callback from .exceptions import HomeAssistantError +from .generated.currencies import HISTORIC_CURRENCIES from .helpers import ( config_per_platform, config_validation as cv, @@ -208,22 +203,25 @@ CUSTOMIZE_CONFIG_SCHEMA = vol.Schema( ) +def _raise_issue_if_historic_currency(hass: HomeAssistant, currency: str) -> None: + if currency in HISTORIC_CURRENCIES: + ir.async_create_issue( + hass, + "homeassistant", + "historic_currency", + is_fixable=False, + severity=ir.IssueSeverity.WARNING, + translation_key="historic_currency", + translation_placeholders={"currency": currency}, + ) + + def _validate_currency(data: Any) -> Any: - hass = async_get_hass() try: return cv.currency(data) except vol.InInvalid: with suppress(vol.InInvalid): currency = cv.historic_currency(data) - ir.async_create_issue( - hass, - "homeassistant", - "historic_currency", - is_fixable=False, - severity=ir.IssueSeverity.WARNING, - translation_key="historic_currency", - translation_placeholders={"currency": currency}, - ) return currency raise @@ -580,6 +578,8 @@ async def async_process_ha_core_config(hass: HomeAssistant, config: dict) -> Non if key in config: setattr(hac, attr, config[key]) + _raise_issue_if_historic_currency(hass, hass.config.currency) + if CONF_TIME_ZONE in config: hac.set_time_zone(config[CONF_TIME_ZONE]) diff --git a/tests/test_config.py b/tests/test_config.py index 75ad227a641..ef364638725 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1207,13 +1207,28 @@ def test_identify_config_schema(domain, schema, expected): ) -def test_core_config_schema_historic_currency(hass): +async def test_core_config_schema_historic_currency(hass): """Test core config schema.""" - config_util.CORE_CONFIG_SCHEMA( - { - "currency": "LTT", - } - ) + await config_util.async_process_ha_core_config(hass, {"currency": "LTT"}) + + issue_registry = ir.async_get(hass) + issue = issue_registry.async_get_issue("homeassistant", "historic_currency") + assert issue + assert issue.translation_placeholders == {"currency": "LTT"} + + +async def test_core_store_historic_currency(hass, hass_storage): + """Test core config store.""" + core_data = { + "data": { + "currency": "LTT", + }, + "key": "core.config", + "version": 1, + "minor_version": 1, + } + hass_storage["core.config"] = dict(core_data) + await config_util.async_process_ha_core_config(hass, {}) issue_registry = ir.async_get(hass) issue = issue_registry.async_get_issue("homeassistant", "historic_currency") From e6d1a4a4224c2c2332ccb5834aec474e7e126af8 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Thu, 10 Nov 2022 08:31:28 -0800 Subject: [PATCH 0351/1033] Revert google calendar back to old API for free/busy readers (#81894) * Revert google calendar back to old API for free/busy readers * Update homeassistant/components/google/calendar.py Co-authored-by: Martin Hjelmare Co-authored-by: Martin Hjelmare --- homeassistant/components/google/calendar.py | 10 ++++++++-- tests/components/google/conftest.py | 14 +++++++++++--- tests/components/google/test_calendar.py | 21 ++++++++++++++++----- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index 4eb57cff49c..eff26c2fbc4 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -10,7 +10,7 @@ from typing import Any from gcal_sync.api import GoogleCalendarService, ListEventsRequest, SyncEventsRequest from gcal_sync.exceptions import ApiException -from gcal_sync.model import DateOrDatetime, Event +from gcal_sync.model import AccessRole, DateOrDatetime, Event from gcal_sync.store import ScopedCalendarStore from gcal_sync.sync import CalendarEventSyncManager from gcal_sync.timeline import Timeline @@ -198,7 +198,13 @@ async def async_setup_entry( entity_entry.entity_id, ) coordinator: CalendarSyncUpdateCoordinator | CalendarQueryUpdateCoordinator - if search := data.get(CONF_SEARCH): + # Prefer calendar sync down of resources when possible. However, sync does not work + # for search. Also free-busy calendars denormalize recurring events as individual + # events which is not efficient for sync + if ( + search := data.get(CONF_SEARCH) + or calendar_item.access_role == AccessRole.FREE_BUSY_READER + ): coordinator = CalendarQueryUpdateCoordinator( hass, calendar_service, diff --git a/tests/components/google/conftest.py b/tests/components/google/conftest.py index 2f5efd829bf..ad27e971ece 100644 --- a/tests/components/google/conftest.py +++ b/tests/components/google/conftest.py @@ -47,7 +47,6 @@ TEST_API_CALENDAR = { "id": CALENDAR_ID, "etag": '"3584134138943410"', "timeZone": "UTC", - "accessRole": "reader", "foregroundColor": "#000000", "selected": True, "kind": "calendar#calendarListEntry", @@ -62,10 +61,19 @@ CLIENT_ID = "client-id" CLIENT_SECRET = "client-secret" +@pytest.fixture(name="calendar_access_role") +def test_calendar_access_role() -> str: + """Default access role to use for test_api_calendar in tests.""" + return "reader" + + @pytest.fixture -def test_api_calendar(): +def test_api_calendar(calendar_access_role: str): """Return a test calendar object used in API responses.""" - return TEST_API_CALENDAR + return { + **TEST_API_CALENDAR, + "accessRole": calendar_access_role, + } @pytest.fixture diff --git a/tests/components/google/test_calendar.py b/tests/components/google/test_calendar.py index 79eff393cc7..0e53642548d 100644 --- a/tests/components/google/test_calendar.py +++ b/tests/components/google/test_calendar.py @@ -60,6 +60,14 @@ TEST_EVENT = { } +@pytest.fixture( + autouse=True, scope="module", params=["reader", "owner", "freeBusyReader"] +) +def calendar_access_role(request) -> str: + """Fixture to exercise access roles in tests.""" + return request.param + + @pytest.fixture(autouse=True) def mock_test_setup( test_api_calendar, @@ -716,12 +724,15 @@ async def test_invalid_unique_id_cleanup( @pytest.mark.parametrize( - "time_zone,event_order", + "time_zone,event_order,calendar_access_role", + # This only tests the reader role to force testing against the local + # database filtering based on start/end time. (free busy reader would + # just use the API response which this test is not exercising) [ - ("America/Los_Angeles", ["One", "Two", "All Day Event"]), - ("America/Regina", ["One", "Two", "All Day Event"]), - ("UTC", ["One", "All Day Event", "Two"]), - ("Asia/Tokyo", ["All Day Event", "One", "Two"]), + ("America/Los_Angeles", ["One", "Two", "All Day Event"], "reader"), + ("America/Regina", ["One", "Two", "All Day Event"], "reader"), + ("UTC", ["One", "All Day Event", "Two"], "reader"), + ("Asia/Tokyo", ["All Day Event", "One", "Two"], "reader"), ], ) async def test_all_day_iter_order( From 97ebe59584b70c95d2aaf002607a0424239dffac Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Thu, 10 Nov 2022 19:33:10 +0100 Subject: [PATCH 0352/1033] Use UnitOfTemperature in devolo Home Control (#81923) --- homeassistant/components/devolo_home_control/climate.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/devolo_home_control/climate.py b/homeassistant/components/devolo_home_control/climate.py index 6c566aa45e3..227b4796883 100644 --- a/homeassistant/components/devolo_home_control/climate.py +++ b/homeassistant/components/devolo_home_control/climate.py @@ -8,13 +8,12 @@ from devolo_home_control_api.homecontrol import HomeControl from homeassistant.components.climate import ( ATTR_TEMPERATURE, - TEMP_CELSIUS, ClimateEntity, ClimateEntityFeature, HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PRECISION_HALVES, PRECISION_TENTHS +from homeassistant.const import PRECISION_HALVES, PRECISION_TENTHS, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -68,7 +67,7 @@ class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntit self._attr_precision = PRECISION_TENTHS self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE self._attr_target_temperature_step = PRECISION_HALVES - self._attr_temperature_unit = TEMP_CELSIUS + self._attr_temperature_unit = UnitOfTemperature.CELSIUS @property def current_temperature(self) -> float | None: From 281310141865eda58fda8ba81d489d843eda13f7 Mon Sep 17 00:00:00 2001 From: cdheiser <10488026+cdheiser@users.noreply.github.com> Date: Thu, 10 Nov 2022 10:35:47 -0800 Subject: [PATCH 0353/1033] Add cdheiser to Lutron codeowners (#81922) * Update manifest.json Add cdheiser as codeowner * Update CODEOWNERS Add @cdheiser as owner of Lutron. --- CODEOWNERS | 1 + homeassistant/components/lutron/manifest.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 1f45098e4f8..93715c3f93b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -655,6 +655,7 @@ build.json @home-assistant/supervisor /homeassistant/components/luftdaten/ @fabaff @frenck /tests/components/luftdaten/ @fabaff @frenck /homeassistant/components/lupusec/ @majuss +/homeassistant/components/lutron/ @cdheiser /homeassistant/components/lutron_caseta/ @swails @bdraco @danaues /tests/components/lutron_caseta/ @swails @bdraco @danaues /homeassistant/components/lyric/ @timmo001 diff --git a/homeassistant/components/lutron/manifest.json b/homeassistant/components/lutron/manifest.json index d46a47cf38d..cc002539d6b 100644 --- a/homeassistant/components/lutron/manifest.json +++ b/homeassistant/components/lutron/manifest.json @@ -3,7 +3,7 @@ "name": "Lutron", "documentation": "https://www.home-assistant.io/integrations/lutron", "requirements": ["pylutron==0.2.8"], - "codeowners": [], + "codeowners": ["@cdheiser"], "iot_class": "local_polling", "loggers": ["pylutron"] } From 78180b2ad869fd5f1add1e0738024e59f53d9570 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 10 Nov 2022 14:14:37 -0600 Subject: [PATCH 0354/1033] Fix bluetooth adapters with missing firmware patch files not being discovered (#81926) --- .../components/bluetooth/__init__.py | 25 +++++- homeassistant/components/bluetooth/const.py | 9 +++ tests/components/bluetooth/test_init.py | 77 +++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index 1d0b8824fb5..8590d1ad90a 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -3,6 +3,7 @@ from __future__ import annotations from asyncio import Future from collections.abc import Callable, Iterable +import datetime import logging import platform from typing import TYPE_CHECKING, cast @@ -21,6 +22,7 @@ from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_ca from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import device_registry as dr, discovery_flow from homeassistant.helpers.debounce import Debouncer +from homeassistant.helpers.event import async_call_later from homeassistant.helpers.issue_registry import ( IssueSeverity, async_create_issue, @@ -33,6 +35,7 @@ from .const import ( ADAPTER_ADDRESS, ADAPTER_HW_VERSION, ADAPTER_SW_VERSION, + BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS, CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, @@ -40,6 +43,7 @@ from .const import ( DEFAULT_ADDRESS, DOMAIN, FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, + LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS, SOURCE_LOCAL, AdapterDetails, ) @@ -298,9 +302,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: await async_discover_adapters(hass, discovered_adapters) discovery_debouncer = Debouncer( - hass, _LOGGER, cooldown=5, immediate=False, function=_async_rediscover_adapters + hass, + _LOGGER, + cooldown=BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS, + immediate=False, + function=_async_rediscover_adapters, ) + async def _async_call_debouncer(now: datetime.datetime) -> None: + """Call the debouncer at a later time.""" + await discovery_debouncer.async_call() + def _async_trigger_discovery() -> None: # There are so many bluetooth adapter models that # we check the bus whenever a usb device is plugged in @@ -310,6 +322,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # present. _LOGGER.debug("Triggering bluetooth usb discovery") hass.async_create_task(discovery_debouncer.async_call()) + # Because it can take 120s for the firmware loader + # fallback to timeout we need to wait that plus + # the debounce time to ensure we do not miss the + # adapter becoming available to DBus since otherwise + # we will never see the new adapter until + # Home Assistant is restarted + async_call_later( + hass, + BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS + LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS, + _async_call_debouncer, + ) cancel = usb.async_register_scan_request_callback(hass, _async_trigger_discovery) hass.bus.async_listen_once( diff --git a/homeassistant/components/bluetooth/const.py b/homeassistant/components/bluetooth/const.py index 6d6751f6ac4..038c2b1988f 100644 --- a/homeassistant/components/bluetooth/const.py +++ b/homeassistant/components/bluetooth/const.py @@ -59,6 +59,15 @@ SCANNER_WATCHDOG_TIMEOUT: Final = 90 SCANNER_WATCHDOG_INTERVAL: Final = timedelta(seconds=30) +# When the linux kernel is configured with +# CONFIG_FW_LOADER_USER_HELPER_FALLBACK it +# can take up to 120s before the USB device +# is available if the firmware files +# are not present +LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS = 120 +BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS = 5 + + class AdapterDetails(TypedDict, total=False): """Adapter details.""" diff --git a/tests/components/bluetooth/test_init.py b/tests/components/bluetooth/test_init.py index c9a5e6c78a7..5a5437af71a 100644 --- a/tests/components/bluetooth/test_init.py +++ b/tests/components/bluetooth/test_init.py @@ -20,9 +20,11 @@ from homeassistant.components.bluetooth import ( scanner, ) from homeassistant.components.bluetooth.const import ( + BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS, CONF_PASSIVE, DEFAULT_ADDRESS, DOMAIN, + LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS, SOURCE_LOCAL, UNAVAILABLE_TRACK_SECONDS, ) @@ -2737,6 +2739,81 @@ async def test_discover_new_usb_adapters(hass, mock_bleak_scanner_start, one_ada assert len(hass.config_entries.flow.async_progress(DOMAIN)) == 1 +async def test_discover_new_usb_adapters_with_firmware_fallback_delay( + hass, mock_bleak_scanner_start, one_adapter +): + """Test we can discover new usb adapters with a firmware fallback delay.""" + entry = MockConfigEntry( + domain=bluetooth.DOMAIN, data={}, unique_id="00:00:00:00:00:01" + ) + entry.add_to_hass(hass) + + saved_callback = None + + def _async_register_scan_request_callback(_hass, _callback): + nonlocal saved_callback + saved_callback = _callback + return lambda: None + + with patch( + "homeassistant.components.bluetooth.usb.async_register_scan_request_callback", + _async_register_scan_request_callback, + ): + assert await async_setup_component(hass, bluetooth.DOMAIN, {}) + await hass.async_block_till_done() + + assert not hass.config_entries.flow.async_progress(DOMAIN) + + saved_callback() + assert not hass.config_entries.flow.async_progress(DOMAIN) + + with patch( + "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" + ), patch( + "bluetooth_adapters.get_bluetooth_adapter_details", + return_value={}, + ): + async_fire_time_changed( + hass, dt_util.utcnow() + timedelta(BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS * 2) + ) + await hass.async_block_till_done() + + assert len(hass.config_entries.flow.async_progress(DOMAIN)) == 0 + + with patch( + "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" + ), patch( + "bluetooth_adapters.get_bluetooth_adapter_details", + return_value={ + "hci0": { + "org.bluez.Adapter1": { + "Address": "00:00:00:00:00:01", + "Name": "BlueZ 4.63", + "Modalias": "usbid:1234", + } + }, + "hci1": { + "org.bluez.Adapter1": { + "Address": "00:00:00:00:00:02", + "Name": "BlueZ 4.63", + "Modalias": "usbid:1234", + } + }, + }, + ): + async_fire_time_changed( + hass, + dt_util.utcnow() + + timedelta( + seconds=LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS + + (BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS * 2) + ), + ) + await hass.async_block_till_done() + + assert len(hass.config_entries.flow.async_progress(DOMAIN)) == 1 + + async def test_issue_outdated_haos( hass, mock_bleak_scanner_start, one_adapter, operating_system_85 ): From f67ecd8ef58de2327b58027c7ae8048919a3a7d2 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 10 Nov 2022 14:32:49 -0700 Subject: [PATCH 0355/1033] Bump aioridwell to 2022.11.0 (#81929) --- homeassistant/components/ridwell/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ridwell/manifest.json b/homeassistant/components/ridwell/manifest.json index aec0faf5dd3..785457a57e0 100644 --- a/homeassistant/components/ridwell/manifest.json +++ b/homeassistant/components/ridwell/manifest.json @@ -3,7 +3,7 @@ "name": "Ridwell", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/ridwell", - "requirements": ["aioridwell==2022.03.0"], + "requirements": ["aioridwell==2022.11.0"], "codeowners": ["@bachya"], "iot_class": "cloud_polling", "loggers": ["aioridwell"], diff --git a/requirements_all.txt b/requirements_all.txt index f789ab61f03..17351f986a2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -252,7 +252,7 @@ aioqsw==0.2.2 aiorecollect==1.0.8 # homeassistant.components.ridwell -aioridwell==2022.03.0 +aioridwell==2022.11.0 # homeassistant.components.senseme aiosenseme==0.6.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0f70d132de0..4a4276019e9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -227,7 +227,7 @@ aioqsw==0.2.2 aiorecollect==1.0.8 # homeassistant.components.ridwell -aioridwell==2022.03.0 +aioridwell==2022.11.0 # homeassistant.components.senseme aiosenseme==0.6.1 From 5621dfe4196990ad5591c51829f4b3eb8b3cf2e6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 10 Nov 2022 17:07:30 -0600 Subject: [PATCH 0356/1033] Small cleanups for HomeKit Controller (#81933) --- .../homekit_controller/connection.py | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/homekit_controller/connection.py b/homeassistant/components/homekit_controller/connection.py index 320df671144..3df5ab8aaed 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -2,7 +2,7 @@ from __future__ import annotations import asyncio -from collections.abc import Callable +from collections.abc import Callable, Iterable from datetime import timedelta import logging from types import MappingProxyType @@ -110,10 +110,6 @@ class HKDevice: self.signal_state_updated = "_".join((DOMAIN, self.unique_id, "state_updated")) - # Current values of all characteristics homekit_controller is tracking. - # Key is a (accessory_id, characteristic_id) tuple. - self.current_state: dict[tuple[int, int], Any] = {} - self.pollable_characteristics: list[tuple[int, int]] = [] # If this is set polling is active and can be disabled by calling @@ -685,28 +681,26 @@ class HKDevice: _LOGGER.debug("Finished HomeKit controller update: %s", self.unique_id) - def process_new_events(self, new_values_dict) -> None: + def process_new_events( + self, new_values_dict: dict[tuple[int, int], dict[str, Any]] + ) -> None: """Process events from accessory into HA state.""" self.async_set_available_state(True) # Process any stateless events (via device_triggers) async_fire_triggers(self, new_values_dict) - for (aid, cid), value in new_values_dict.items(): - accessory = self.current_state.setdefault(aid, {}) - accessory[cid] = value - - # self.current_state will be replaced by entity_map in a future PR - # For now we update both self.entity_map.process_changes(new_values_dict) async_dispatcher_send(self.hass, self.signal_state_updated) - async def get_characteristics(self, *args, **kwargs) -> dict[str, Any]: + async def get_characteristics(self, *args: Any, **kwargs: Any) -> dict[str, Any]: """Read latest state from homekit accessory.""" return await self.pairing.get_characteristics(*args, **kwargs) - async def put_characteristics(self, characteristics) -> None: + async def put_characteristics( + self, characteristics: Iterable[tuple[int, int, Any]] + ) -> None: """Control a HomeKit device state from Home Assistant.""" results = await self.pairing.put_characteristics(characteristics) From df0ba28b0552e0e29858ce6cd6756b5fc170bf40 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 11 Nov 2022 00:30:00 +0000 Subject: [PATCH 0357/1033] [ci skip] Translation update --- .../components/foscam/translations/bg.json | 1 + .../components/generic/translations/bg.json | 3 +- .../group/translations/zh-Hant.json | 8 +- .../components/knx/translations/ca.json | 125 ++++++++++++++++- .../components/knx/translations/de.json | 125 ++++++++++++++++- .../components/knx/translations/en.json | 48 ++++++- .../components/knx/translations/es.json | 131 +++++++++++++++++- .../components/knx/translations/et.json | 125 ++++++++++++++++- .../components/knx/translations/hu.json | 125 ++++++++++++++++- .../components/knx/translations/id.json | 125 ++++++++++++++++- .../components/knx/translations/it.json | 129 ++++++++++++++++- .../components/knx/translations/pt-BR.json | 125 ++++++++++++++++- .../components/knx/translations/zh-Hant.json | 127 ++++++++++++++++- .../nibe_heatpump/translations/ca.json | 16 +++ .../nibe_heatpump/translations/de.json | 10 +- .../nibe_heatpump/translations/en.json | 2 +- .../nibe_heatpump/translations/es.json | 12 +- .../nibe_heatpump/translations/et.json | 6 +- .../nibe_heatpump/translations/id.json | 17 ++- .../nibe_heatpump/translations/it.json | 12 +- .../nibe_heatpump/translations/pl.json | 8 +- .../nibe_heatpump/translations/pt-BR.json | 8 +- .../nibe_heatpump/translations/ru.json | 8 +- .../nibe_heatpump/translations/zh-Hant.json | 8 +- .../components/openuv/translations/bg.json | 1 + 25 files changed, 1215 insertions(+), 90 deletions(-) diff --git a/homeassistant/components/foscam/translations/bg.json b/homeassistant/components/foscam/translations/bg.json index e660bd80f53..8318b416845 100644 --- a/homeassistant/components/foscam/translations/bg.json +++ b/homeassistant/components/foscam/translations/bg.json @@ -10,6 +10,7 @@ "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "port": "\u041f\u043e\u0440\u0442", "rtsp_port": "RTSP \u043f\u043e\u0440\u0442", + "stream": "\u041f\u043e\u0442\u043e\u043a", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" } } diff --git a/homeassistant/components/generic/translations/bg.json b/homeassistant/components/generic/translations/bg.json index 8c6944af94a..b26b65e715e 100644 --- a/homeassistant/components/generic/translations/bg.json +++ b/homeassistant/components/generic/translations/bg.json @@ -23,7 +23,8 @@ "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "rtsp_transport": "RTSP \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0435\u043d \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" - } + }, + "description": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u0442\u0435 \u0437\u0430 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430." }, "user_confirm_still": { "data": { diff --git a/homeassistant/components/group/translations/zh-Hant.json b/homeassistant/components/group/translations/zh-Hant.json index 023c76ebbba..327aa3a0def 100644 --- a/homeassistant/components/group/translations/zh-Hant.json +++ b/homeassistant/components/group/translations/zh-Hant.json @@ -8,7 +8,7 @@ "hide_members": "\u96b1\u85cf\u6210\u54e1", "name": "\u540d\u7a31" }, - "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u88dd\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002", + "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u72c0\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002", "title": "\u65b0\u589e\u7fa4\u7d44" }, "cover": { @@ -82,7 +82,7 @@ "entities": "\u6210\u54e1", "hide_members": "\u96b1\u85cf\u6210\u54e1" }, - "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u88dd\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002" + "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u72c0\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002" }, "cover": { "data": { @@ -102,7 +102,7 @@ "entities": "\u6210\u54e1", "hide_members": "\u96b1\u85cf\u6210\u54e1" }, - "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u88dd\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002" + "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u72c0\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002" }, "lock": { "data": { @@ -122,7 +122,7 @@ "entities": "\u6210\u54e1", "hide_members": "\u96b1\u85cf\u6210\u54e1" }, - "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u88dd\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002" + "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u72c0\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002" } } }, diff --git a/homeassistant/components/knx/translations/ca.json b/homeassistant/components/knx/translations/ca.json index ff83ebec070..5b984ff4987 100644 --- a/homeassistant/components/knx/translations/ca.json +++ b/homeassistant/components/knx/translations/ca.json @@ -9,20 +9,30 @@ "file_not_found": "No s'ha trobat el fitxer `.knxkeys` especificat a la ruta config/.storage/knx/", "invalid_individual_address": "El valor no coincideix amb el patr\u00f3 d'adre\u00e7a KNX individual.\n'area.line.device'", "invalid_ip_address": "Adre\u00e7a IPv4 inv\u00e0lida.", - "invalid_signature": "La contrasenya per desxifrar el fitxer `.knxkeys` \u00e9s incorrecta." + "invalid_signature": "La contrasenya per desxifrar el fitxer `.knxkeys` \u00e9s incorrecta.", + "no_router_discovered": "No s'ha descobert cap encaminador ('router') KNXnet/IP a la xarxa.", + "no_tunnel_discovered": "No s'ha trobat cap servidor de tunelitzaci\u00f3 KNX a la xarxa." }, "step": { + "connection_type": { + "data": { + "connection_type": "Tipus de connexi\u00f3 KNX" + }, + "description": "Introdueix el tipus de connexi\u00f3 a utilitzar per a la connexi\u00f3 KNX.\n AUTOM\u00c0TICA: la integraci\u00f3 s'encarrega de la connectivitat al bus KNX realitzant una exploraci\u00f3 de la passarel\u00b7la.\n T\u00daNEL: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant un t\u00fanel.\n ENCAMINAMENT: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant l'encaminament." + }, "manual_tunnel": { "data": { "host": "Amfitri\u00f3", "local_ip": "IP local de Home Assistant", "port": "Port", + "route_back": "Encaminament de retorn / Mode NAT", "tunneling_type": "Tipus de t\u00fanel KNX" }, "data_description": { "host": "Adre\u00e7a IP del dispositiu de tunelitzaci\u00f3 KNX/IP.", "local_ip": "Deixa-ho en blanc per utilitzar el descobriment autom\u00e0tic.", - "port": "Port del dispositiu de tunelitzaci\u00f3 KNX/IP." + "port": "Port del dispositiu de tunelitzaci\u00f3 KNX/IP.", + "route_back": "Activa-ho si el teun servidor de tunelitzaci\u00f3 KNXnet/IP est\u00e0 darrere una NAT. Nom\u00e9s s'aplica a connexions UDP." }, "description": "Introdueix la informaci\u00f3 de connexi\u00f3 del dispositiu de t\u00fanel." }, @@ -63,11 +73,25 @@ }, "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure)." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Contrasenya d'autenticaci\u00f3 del dispositiu", + "user_id": "ID d'usuari", + "user_password": "Contrasenya d'usuari" + }, + "data_description": { + "device_authentication": "S'estableix al panell 'IP' de la interf\u00edcie d'ETS.", + "user_id": "Sovint \u00e9s el n\u00famero del t\u00fanel +1. Per tant, 'T\u00fanel 2' tindria l'ID d'usuari '3'.", + "user_password": "Contrasenya per a la connexi\u00f3 t\u00fanel espec\u00edfica configurada al panell 'Propietats' del t\u00fanel a ETS." + }, + "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure)." + }, "secure_tunneling": { "description": "Selecciona com vols configurar KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Utilitza un fitxer `.knxkeys` que contingui les claus de seguretat IP (IP Secure)", - "secure_manual": "Configura manualment les claus de seguretat IP (IP Secure)" + "secure_manual": "Configura manualment les claus de seguretat IP (IP Secure)", + "secure_tunnel_manual": "Configura manualment les claus de seguretat IP (IP Secure)" } }, "tunnel": { @@ -85,7 +109,32 @@ } }, "options": { + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "file_not_found": "No s'ha trobat el fitxer `.knxkeys` especificat a la ruta config/.storage/knx/", + "invalid_individual_address": "El valor no coincideix amb el patr\u00f3 d'adre\u00e7a KNX individual.\n'area.line.device'", + "invalid_ip_address": "Adre\u00e7a IPv4 inv\u00e0lida.", + "invalid_signature": "La contrasenya per desxifrar el fitxer `.knxkeys` \u00e9s incorrecta.", + "no_router_discovered": "No s'ha descobert cap encaminador ('router') KNXnet/IP a la xarxa.", + "no_tunnel_discovered": "No s'ha trobat cap servidor de tunelitzaci\u00f3 KNX a la xarxa." + }, "step": { + "communication_settings": { + "data": { + "rate_limit": "Freq\u00fc\u00e8ncia m\u00e0xima", + "state_updater": "Actualitzador d'estat" + }, + "data_description": { + "rate_limit": "Telegrames de sortida m\u00e0xims per segon.\nUtilitza `0` per desactivar la limitaci\u00f3. Recomanat: 0 o, de 20 a 40", + "state_updater": "Configuraci\u00f3 predeterminadament per llegir els estats del bus KNX. Si est\u00e0 desactivat, Home Assistant no obtindr\u00e0 activament els estats del bus KNX. Les opcions d'entitat `sync_state` poden substituir-ho." + } + }, + "connection_type": { + "data": { + "connection_type": "Tipus de connexi\u00f3 KNX" + }, + "description": "Introdueix el tipus de connexi\u00f3 a utilitzar per a la connexi\u00f3 KNX.\n AUTOM\u00c0TICA: la integraci\u00f3 s'encarrega de la connectivitat al bus KNX realitzant una exploraci\u00f3 de la passarel\u00b7la.\n T\u00daNEL: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant un t\u00fanel.\n ENCAMINAMENT: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant l'encaminament." + }, "init": { "data": { "connection_type": "Tipus de connexi\u00f3 KNX", @@ -105,8 +154,75 @@ "state_updater": "Configuraci\u00f3 predeterminadament per llegir els estats del bus KNX. Si est\u00e0 desactivat, Home Assistant no obtindr\u00e0 activament els estats del bus KNX. Les opcions d'entitat `sync_state` poden substituir-ho." } }, + "manual_tunnel": { + "data": { + "host": "Amfitri\u00f3", + "local_ip": "IP local de Home Assistant", + "port": "Port", + "route_back": "Encaminament de retorn / Mode NAT", + "tunneling_type": "Tipus de t\u00fanel KNX" + }, + "data_description": { + "host": "Adre\u00e7a IP del dispositiu de tunelitzaci\u00f3 KNX/IP.", + "local_ip": "Deixa-ho en blanc per utilitzar el descobriment autom\u00e0tic.", + "port": "Port del dispositiu de tunelitzaci\u00f3 KNX/IP.", + "route_back": "Activa-ho si el teun servidor de tunelitzaci\u00f3 KNXnet/IP est\u00e0 darrere una NAT. Nom\u00e9s s'aplica a connexions UDP." + }, + "description": "Introdueix la informaci\u00f3 de connexi\u00f3 del dispositiu de t\u00fanel." + }, + "options_init": { + "menu_options": { + "communication_settings": "Configuraci\u00f3 de la comunicaci\u00f3", + "connection_type": "Configura la interf\u00edcie KNX" + } + }, + "routing": { + "data": { + "individual_address": "Adre\u00e7a individual", + "local_ip": "IP local de Home Assistant", + "multicast_group": "Grup multidifusi\u00f3", + "multicast_port": "Port multidifusi\u00f3" + }, + "data_description": { + "individual_address": "Adre\u00e7a KNX per utilitzar amb Home Assistant, p. ex. `0.0.4`", + "local_ip": "Deixa-ho en blanc per utilitzar el descobriment autom\u00e0tic." + }, + "description": "Configura les opcions d'encaminament." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Nom del teu fitxer `.knxkeys` (inclosa l'extensi\u00f3)", + "knxkeys_password": "Contrasenya per desxifrar el fitxer `.knxkeys`." + }, + "data_description": { + "knxkeys_filename": "S'espera que el fitxer es trobi al teu directori de configuraci\u00f3 a `.storage/knx/`.\nA Home Assistant aix\u00f2 estaria a `/config/.storage/knx/`\nExemple: `el_meu_projecte.knxkeys`", + "knxkeys_password": "S'ha definit durant l'exportaci\u00f3 del fitxer des d'ETS." + }, + "description": "Introdueix la informaci\u00f3 del teu fitxer `.knxkeys`." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Contrasenya d'autenticaci\u00f3 del dispositiu", + "user_id": "ID d'usuari", + "user_password": "Contrasenya d'usuari" + }, + "data_description": { + "device_authentication": "S'estableix al panell 'IP' de la interf\u00edcie d'ETS.", + "user_id": "Sovint \u00e9s el n\u00famero del t\u00fanel +1. Per tant, 'T\u00fanel 2' tindria l'ID d'usuari '3'.", + "user_password": "Contrasenya per a la connexi\u00f3 t\u00fanel espec\u00edfica configurada al panell 'Propietats' del t\u00fanel a ETS." + }, + "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure)." + }, + "secure_tunneling": { + "description": "Selecciona com vols configurar KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Utilitza un fitxer `.knxkeys` que contingui les claus de seguretat IP (IP Secure)", + "secure_tunnel_manual": "Configura manualment les claus de seguretat IP (IP Secure)" + } + }, "tunnel": { "data": { + "gateway": "Connexi\u00f3 t\u00fanel KNX", "host": "Amfitri\u00f3", "port": "Port", "tunneling_type": "Tipus de t\u00fanel KNX" @@ -114,7 +230,8 @@ "data_description": { "host": "Adre\u00e7a IP del dispositiu de tunelitzaci\u00f3 KNX/IP.", "port": "Port del dispositiu de tunelitzaci\u00f3 KNX/IP." - } + }, + "description": "Selecciona una passarel\u00b7la d'enlla\u00e7 de la llista." } } } diff --git a/homeassistant/components/knx/translations/de.json b/homeassistant/components/knx/translations/de.json index 1daffa9c301..2d624346e00 100644 --- a/homeassistant/components/knx/translations/de.json +++ b/homeassistant/components/knx/translations/de.json @@ -9,20 +9,30 @@ "file_not_found": "Die angegebene `.knxkeys`-Datei wurde im Pfad config/.storage/knx/ nicht gefunden.", "invalid_individual_address": "Wert ist keine g\u00fcltige physikalische Adresse. 'Bereich.Linie.Teilnehmer'", "invalid_ip_address": "Ung\u00fcltige IPv4 Adresse.", - "invalid_signature": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys`-Datei ist ung\u00fcltig." + "invalid_signature": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys`-Datei ist ung\u00fcltig.", + "no_router_discovered": "Es wurde kein KNXnet/IP-Router im Netzwerk gefunden.", + "no_tunnel_discovered": "Es konnte kein KNX Tunneling Server in deinem Netzwerk gefunden werden." }, "step": { + "connection_type": { + "data": { + "connection_type": "KNX-Verbindungstyp" + }, + "description": "Bitte gib den Verbindungstyp ein, den wir f\u00fcr deine KNX-Verbindung verwenden sollen. \n AUTOMATISCH - Die Integration k\u00fcmmert sich um die Verbindung zu deinem KNX Bus, indem sie einen Gateway-Scan durchf\u00fchrt. \n TUNNELING - Die Integration stellt die Verbindung zu deinem KNX Bus \u00fcber Tunneling her. \n ROUTING - Die Integration stellt die Verbindung zu deinem KNX-Bus \u00fcber Routing her." + }, "manual_tunnel": { "data": { "host": "Host", "local_ip": "Lokale IP von Home Assistant", "port": "Port", + "route_back": "Zur\u00fcckrouten / NAT-Modus", "tunneling_type": "KNX Tunneling Typ" }, "data_description": { "host": "IP-Adresse der KNX/IP-Tunneling Schnittstelle.", "local_ip": "Lasse das Feld leer, um die automatische Erkennung zu verwenden.", - "port": "Port der KNX/IP-Tunneling Schnittstelle." + "port": "Port der KNX/IP-Tunneling Schnittstelle.", + "route_back": "Aktiviere diese Option, wenn sich Ihr KNXnet/IP-Tunnelserver hinter NAT befindet. Gilt nur f\u00fcr UDP-Verbindungen." }, "description": "Bitte gib die Verbindungsinformationen deiner Tunnel-Schnittstelle ein." }, @@ -63,11 +73,25 @@ }, "description": "Bitte gib deine IP-Secure Informationen ein." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Ger\u00e4te-Authentifizierungscode", + "user_id": "Benutzer-ID", + "user_password": "Benutzer-Passwort" + }, + "data_description": { + "device_authentication": "Dies wird im Feld \"IP\" der Schnittstelle in ETS eingestellt.", + "user_id": "Dies ist oft die Tunnelnummer +1. \u201eTunnel 2\u201c h\u00e4tte also die Benutzer-ID \u201e3\u201c.", + "user_password": "Passwort f\u00fcr die spezifische Tunnelverbindung, die im Bereich \u201eEigenschaften\u201c des Tunnels in ETS festgelegt wurde." + }, + "description": "Bitte gib deine IP-Secure Informationen ein." + }, "secure_tunneling": { "description": "W\u00e4hle aus, wie du KNX/IP-Secure konfigurieren m\u00f6chtest.", "menu_options": { "secure_knxkeys": "Verwende eine `.knxkeys`-Datei, die IP-Secure-Schl\u00fcssel enth\u00e4lt", - "secure_manual": "IP-Secure Schl\u00fcssel manuell konfigurieren" + "secure_manual": "IP-Secure Schl\u00fcssel manuell konfigurieren", + "secure_tunnel_manual": "IP-Secure Schl\u00fcssel manuell konfigurieren" } }, "tunnel": { @@ -85,7 +109,32 @@ } }, "options": { + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "file_not_found": "Die angegebene `.knxkeys`-Datei wurde im Pfad config/.storage/knx/ nicht gefunden.", + "invalid_individual_address": "Wert ist keine g\u00fcltige physikalische Adresse. 'Bereich.Linie.Teilnehmer'", + "invalid_ip_address": "Ung\u00fcltige IPv4 Adresse.", + "invalid_signature": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys`-Datei ist ung\u00fcltig.", + "no_router_discovered": "Es wurde kein KNXnet/IP-Router im Netzwerk gefunden.", + "no_tunnel_discovered": "Es konnte kein KNX Tunneling Server in deinem Netzwerk gefunden werden." + }, "step": { + "communication_settings": { + "data": { + "rate_limit": "Ratenlimit", + "state_updater": "Status-Updater" + }, + "data_description": { + "rate_limit": "Maximal ausgehende Telegramme pro Sekunde.\n `0`, um das Limit zu deaktivieren. Empfohlen: 0 oder 20 bis 40", + "state_updater": "Standardeinstellung f\u00fcr das Lesen von Zust\u00e4nden aus dem KNX-Bus. Wenn diese Option deaktiviert ist, wird der Home Assistant den Zustand der Entit\u00e4ten nicht aktiv vom KNX-Bus abrufen. Kann durch die Entity-Optionen `sync_state` au\u00dfer Kraft gesetzt werden." + } + }, + "connection_type": { + "data": { + "connection_type": "KNX-Verbindungstyp" + }, + "description": "Bitte gib den Verbindungstyp ein, den wir f\u00fcr deine KNX-Verbindung verwenden sollen. \n AUTOMATISCH - Die Integration k\u00fcmmert sich um die Verbindung zu deinem KNX Bus, indem sie einen Gateway-Scan durchf\u00fchrt. \n TUNNELING - Die Integration stellt die Verbindung zu deinem KNX Bus \u00fcber Tunneling her. \n ROUTING - Die Integration stellt die Verbindung zu deinem KNX-Bus \u00fcber Routing her." + }, "init": { "data": { "connection_type": "KNX-Verbindungstyp", @@ -105,8 +154,75 @@ "state_updater": "Standardeinstellung f\u00fcr das Lesen von Zust\u00e4nden aus dem KNX-Bus. Wenn diese Option deaktiviert ist, wird der Home Assistant den Zustand der Entit\u00e4ten nicht aktiv vom KNX-Bus abrufen. Kann durch die Entity-Optionen `sync_state` au\u00dfer Kraft gesetzt werden." } }, + "manual_tunnel": { + "data": { + "host": "Host", + "local_ip": "Lokale IP von Home Assistant", + "port": "Port", + "route_back": "Zur\u00fcckrouten / NAT-Modus", + "tunneling_type": "KNX Tunneling Typ" + }, + "data_description": { + "host": "IP-Adresse der KNX/IP-Tunneling Schnittstelle.", + "local_ip": "Lasse das Feld leer, um die automatische Erkennung zu verwenden.", + "port": "Port der KNX/IP-Tunneling Schnittstelle.", + "route_back": "Aktiviere diese Option, wenn sich Ihr KNXnet/IP-Tunnelserver hinter NAT befindet. Gilt nur f\u00fcr UDP-Verbindungen." + }, + "description": "Bitte gib die Verbindungsinformationen deiner Tunnel-Schnittstelle ein." + }, + "options_init": { + "menu_options": { + "communication_settings": "Kommunikationseinstellungen", + "connection_type": "KNX-Schnittstelle konfigurieren" + } + }, + "routing": { + "data": { + "individual_address": "Physikalische Adresse", + "local_ip": "Lokale IP von Home Assistant", + "multicast_group": "Multicast-Gruppe", + "multicast_port": "Multicast-Port" + }, + "data_description": { + "individual_address": "Physikalische Adresse, die von Home Assistant verwendet werden soll, z. B. \u201e0.0.4\u201c.", + "local_ip": "Lasse das Feld leer, um die automatische Erkennung zu verwenden." + }, + "description": "Bitte konfiguriere die Routing-Optionen." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Der Dateiname deiner `.knxkeys`-Datei (einschlie\u00dflich Erweiterung)", + "knxkeys_password": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys`-Datei" + }, + "data_description": { + "knxkeys_filename": "Die Datei wird in deinem Konfigurationsverzeichnis unter `.storage/knx/` erwartet.\nIm Home Assistant OS w\u00e4re dies `/config/.storage/knx/`\nBeispiel: `my_project.knxkeys`", + "knxkeys_password": "Dies wurde beim Exportieren der Datei aus ETS gesetzt." + }, + "description": "Bitte gib die Informationen f\u00fcr deine `.knxkeys`-Datei ein." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Ger\u00e4te-Authentifizierungscode", + "user_id": "Benutzer-ID", + "user_password": "Benutzer-Passwort" + }, + "data_description": { + "device_authentication": "Dies wird im Feld \"IP\" der Schnittstelle in ETS eingestellt.", + "user_id": "Dies ist oft die Tunnelnummer +1. \u201eTunnel 2\u201c h\u00e4tte also die Benutzer-ID \u201e3\u201c.", + "user_password": "Passwort f\u00fcr die spezifische Tunnelverbindung, die im Bereich \u201eEigenschaften\u201c des Tunnels in ETS festgelegt wurde." + }, + "description": "Bitte gib deine IP-Secure Informationen ein." + }, + "secure_tunneling": { + "description": "W\u00e4hle aus, wie du KNX/IP-Secure konfigurieren m\u00f6chtest.", + "menu_options": { + "secure_knxkeys": "Verwende eine `.knxkeys`-Datei, die IP-Secure-Schl\u00fcssel enth\u00e4lt", + "secure_tunnel_manual": "IP-Secure Schl\u00fcssel manuell konfigurieren" + } + }, "tunnel": { "data": { + "gateway": "KNX Tunnel Verbindung", "host": "Host", "port": "Port", "tunneling_type": "KNX Tunneling Typ" @@ -114,7 +230,8 @@ "data_description": { "host": "IP-Adresse der KNX/IP-Tunneling Schnittstelle.", "port": "Port der KNX/IP-Tunneling Schnittstelle." - } + }, + "description": "Bitte w\u00e4hle eine Schnittstelle aus der Liste aus." } } } diff --git a/homeassistant/components/knx/translations/en.json b/homeassistant/components/knx/translations/en.json index c45c98b070a..920cd21b1cc 100644 --- a/homeassistant/components/knx/translations/en.json +++ b/homeassistant/components/knx/translations/en.json @@ -60,6 +60,19 @@ }, "description": "Please enter the information for your `.knxkeys` file." }, + "secure_manual": { + "data": { + "device_authentication": "Device authentication password", + "user_id": "User ID", + "user_password": "User password" + }, + "data_description": { + "device_authentication": "This is set in the 'IP' panel of the interface in ETS.", + "user_id": "This is often tunnel number +1. So 'Tunnel 2' would have User-ID '3'.", + "user_password": "Password for the specific tunnel connection set in the 'Properties' panel of the tunnel in ETS." + }, + "description": "Please enter your IP secure information." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Device authentication password", @@ -77,6 +90,7 @@ "description": "Select how you want to configure KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", + "secure_manual": "Configure IP secure keys manually", "secure_tunnel_manual": "Configure IP secure keys manually" } }, @@ -85,6 +99,12 @@ "gateway": "KNX Tunnel Connection" }, "description": "Please select a gateway from the list." + }, + "type": { + "data": { + "connection_type": "KNX Connection Type" + }, + "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing." } } }, @@ -115,6 +135,25 @@ }, "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing." }, + "init": { + "data": { + "connection_type": "KNX Connection Type", + "individual_address": "Default individual address", + "local_ip": "Local IP of Home Assistant", + "multicast_group": "Multicast group", + "multicast_port": "Multicast port", + "rate_limit": "Rate limit", + "state_updater": "State updater" + }, + "data_description": { + "individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`", + "local_ip": "Use `0.0.0.0` for auto-discovery.", + "multicast_group": "Used for routing and discovery. Default: `224.0.23.12`", + "multicast_port": "Used for routing and discovery. Default: `3671`", + "rate_limit": "Maximum outgoing telegrams per second.\nRecommended: 20 to 40", + "state_updater": "Set default for reading states from the KNX Bus. When disabled, Home Assistant will not actively retrieve entity states from the KNX Bus. Can be overridden by `sync_state` entity options." + } + }, "manual_tunnel": { "data": { "host": "Host", @@ -183,7 +222,14 @@ }, "tunnel": { "data": { - "gateway": "KNX Tunnel Connection" + "gateway": "KNX Tunnel Connection", + "host": "Host", + "port": "Port", + "tunneling_type": "KNX Tunneling Type" + }, + "data_description": { + "host": "IP address of the KNX/IP tunneling device.", + "port": "Port of the KNX/IP tunneling device." }, "description": "Please select a gateway from the list." } diff --git a/homeassistant/components/knx/translations/es.json b/homeassistant/components/knx/translations/es.json index 19de37aaf56..df422c28208 100644 --- a/homeassistant/components/knx/translations/es.json +++ b/homeassistant/components/knx/translations/es.json @@ -9,20 +9,30 @@ "file_not_found": "El archivo `.knxkeys` especificado no se encontr\u00f3 en la ruta config/.storage/knx/", "invalid_individual_address": "El valor no coincide con el patr\u00f3n de la direcci\u00f3n KNX individual. 'area.line.device'", "invalid_ip_address": "Direcci\u00f3n IPv4 no v\u00e1lida.", - "invalid_signature": "La contrase\u00f1a para descifrar el archivo `.knxkeys` es incorrecta." + "invalid_signature": "La contrase\u00f1a para descifrar el archivo `.knxkeys` es incorrecta.", + "no_router_discovered": "No se ha descubierto ning\u00fan router KNXnet/IP en la red.", + "no_tunnel_discovered": "No se pudo encontrar un servidor de t\u00fanel KNX en tu red." }, "step": { + "connection_type": { + "data": { + "connection_type": "Tipo de conexi\u00f3n KNX" + }, + "description": "Por favor, introduce el tipo de conexi\u00f3n que debemos usar para tu conexi\u00f3n KNX.\n AUTOM\u00c1TICO: la integraci\u00f3n se encarga de la conectividad a tu bus KNX mediante la realizaci\u00f3n de un escaneo de la puerta de enlace.\n T\u00daNELES: la integraci\u00f3n se conectar\u00e1 a tu bus KNX a trav\u00e9s de t\u00faneles.\n ENRUTAMIENTO: la integraci\u00f3n se conectar\u00e1 a su tus KNX a trav\u00e9s del enrutamiento." + }, "manual_tunnel": { "data": { "host": "Host", "local_ip": "IP local de Home Assistant", "port": "Puerto", + "route_back": "Ruta de regreso / modo NAT", "tunneling_type": "Tipo de t\u00fanel KNX" }, "data_description": { - "host": "Direcci\u00f3n IP del dispositivo de tunelizaci\u00f3n KNX/IP.", + "host": "Direcci\u00f3n IP del dispositivo de t\u00fanel KNX/IP.", "local_ip": "D\u00e9jalo en blanco para usar el descubrimiento autom\u00e1tico.", - "port": "Puerto del dispositivo de tunelizaci\u00f3n KNX/IP." + "port": "Puerto del dispositivo de t\u00fanel KNX/IP.", + "route_back": "Habilitar si tu servidor de t\u00fanel IP/KNXnet est\u00e1 detr\u00e1s de NAT. Solo aplica para conexiones UDP." }, "description": "Por favor, introduce la informaci\u00f3n de conexi\u00f3n de tu dispositivo de t\u00fanel." }, @@ -63,11 +73,25 @@ }, "description": "Por favor, introduce tu informaci\u00f3n de IP segura." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Contrase\u00f1a de autenticaci\u00f3n del dispositivo", + "user_id": "ID de usuario", + "user_password": "Contrase\u00f1a de usuario" + }, + "data_description": { + "device_authentication": "Esto se configura en el panel 'IP' de la interfaz en ETS.", + "user_id": "Este suele ser el n\u00famero de t\u00fanel +1. Por tanto, 'T\u00fanel 2' tendr\u00eda ID de usuario '3'.", + "user_password": "Contrase\u00f1a para la conexi\u00f3n de t\u00fanel espec\u00edfica establecida en el panel 'Propiedades' del t\u00fanel en ETS." + }, + "description": "Por favor, introduce tu informaci\u00f3n de IP segura." + }, "secure_tunneling": { "description": "Selecciona c\u00f3mo quieres configurar KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Utilizar un archivo `.knxkeys` que contenga claves seguras de IP", - "secure_manual": "Configurar claves seguras de IP manualmente" + "secure_manual": "Configurar claves seguras de IP manualmente", + "secure_tunnel_manual": "Configurar claves seguras de IP manualmente" } }, "tunnel": { @@ -85,7 +109,32 @@ } }, "options": { + "error": { + "cannot_connect": "No se pudo conectar", + "file_not_found": "El archivo `.knxkeys` especificado no se encontr\u00f3 en la ruta config/.storage/knx/", + "invalid_individual_address": "El valor no coincide con el patr\u00f3n de la direcci\u00f3n KNX individual. 'area.line.device'", + "invalid_ip_address": "Direcci\u00f3n IPv4 no v\u00e1lida.", + "invalid_signature": "La contrase\u00f1a para descifrar el archivo `.knxkeys` es incorrecta.", + "no_router_discovered": "No se ha descubierto ning\u00fan router KNXnet/IP en la red.", + "no_tunnel_discovered": "No se pudo encontrar un servidor de t\u00fanel KNX en tu red." + }, "step": { + "communication_settings": { + "data": { + "rate_limit": "L\u00edmite de tasa", + "state_updater": "Actualizador de estado" + }, + "data_description": { + "rate_limit": "N\u00famero m\u00e1ximo de telegramas salientes por segundo.\n`0` para deshabilitar el l\u00edmite. Recomendado: 0 o 20 a 40", + "state_updater": "Establece los valores predeterminados para leer los estados del bus KNX. Cuando est\u00e1 deshabilitado, Home Assistant no recuperar\u00e1 activamente los estados de entidad del bus KNX. Puede ser anulado por las opciones de entidad `sync_state`." + } + }, + "connection_type": { + "data": { + "connection_type": "Tipo de conexi\u00f3n KNX" + }, + "description": "Por favor, introduce el tipo de conexi\u00f3n que debemos usar para tu conexi\u00f3n KNX.\n AUTOM\u00c1TICO: la integraci\u00f3n se encarga de la conectividad a tu bus KNX mediante la realizaci\u00f3n de un escaneo de la puerta de enlace.\n T\u00daNELES: la integraci\u00f3n se conectar\u00e1 a tu bus KNX a trav\u00e9s de t\u00faneles.\n ENRUTAMIENTO: la integraci\u00f3n se conectar\u00e1 a su tus KNX a trav\u00e9s del enrutamiento." + }, "init": { "data": { "connection_type": "Tipo de conexi\u00f3n KNX", @@ -105,16 +154,84 @@ "state_updater": "Establece los valores predeterminados para leer los estados del bus KNX. Cuando est\u00e1 deshabilitado, Home Assistant no recuperar\u00e1 activamente los estados de entidad del bus KNX. Puede ser anulado por las opciones de entidad `sync_state`." } }, + "manual_tunnel": { + "data": { + "host": "Host", + "local_ip": "IP local de Home Assistant", + "port": "Puerto", + "route_back": "Ruta de regreso / modo NAT", + "tunneling_type": "Tipo de t\u00fanel KNX" + }, + "data_description": { + "host": "Direcci\u00f3n IP del dispositivo de t\u00fanel KNX/IP.", + "local_ip": "D\u00e9jalo en blanco para usar el descubrimiento autom\u00e1tico.", + "port": "Puerto del dispositivo de t\u00fanel KNX/IP.", + "route_back": "Habilitar si tu servidor de t\u00fanel IP/KNXnet est\u00e1 detr\u00e1s de NAT. Solo aplica para conexiones UDP." + }, + "description": "Por favor, introduce la informaci\u00f3n de conexi\u00f3n de tu dispositivo de t\u00fanel." + }, + "options_init": { + "menu_options": { + "communication_settings": "Configuraci\u00f3n de comunicaci\u00f3n", + "connection_type": "Configurar interfaz KNX" + } + }, + "routing": { + "data": { + "individual_address": "Direcci\u00f3n individual", + "local_ip": "IP local de Home Assistant", + "multicast_group": "Grupo multicast", + "multicast_port": "Puerto multicast" + }, + "data_description": { + "individual_address": "Direcci\u00f3n KNX que usar\u00e1 Home Assistant, por ejemplo, `0.0.4`", + "local_ip": "D\u00e9jalo en blanco para usar el descubrimiento autom\u00e1tico." + }, + "description": "Por favor, configura las opciones de enrutamiento." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "El nombre de tu archivo `.knxkeys` (incluyendo la extensi\u00f3n)", + "knxkeys_password": "Contrase\u00f1a para descifrar el archivo `.knxkeys`." + }, + "data_description": { + "knxkeys_filename": "Se espera que el archivo se encuentre en tu directorio de configuraci\u00f3n en `.storage/knx/`.\nEn Home Assistant OS ser\u00eda `/config/.storage/knx/`\nEjemplo: `mi_proyecto.knxkeys`", + "knxkeys_password": "Esto se configur\u00f3 al exportar el archivo desde ETS." + }, + "description": "Por favor, introduce la informaci\u00f3n de tu archivo `.knxkeys`." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Contrase\u00f1a de autenticaci\u00f3n del dispositivo", + "user_id": "ID de usuario", + "user_password": "Contrase\u00f1a de usuario" + }, + "data_description": { + "device_authentication": "Esto se configura en el panel 'IP' de la interfaz en ETS.", + "user_id": "Este suele ser el n\u00famero de t\u00fanel +1. Por tanto, 'T\u00fanel 2' tendr\u00eda ID de usuario '3'.", + "user_password": "Contrase\u00f1a para la conexi\u00f3n de t\u00fanel espec\u00edfica establecida en el panel 'Propiedades' del t\u00fanel en ETS." + }, + "description": "Por favor, introduce tu informaci\u00f3n de IP segura." + }, + "secure_tunneling": { + "description": "Selecciona c\u00f3mo quieres configurar KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Utilizar un archivo `.knxkeys` que contenga claves seguras de IP", + "secure_tunnel_manual": "Configurar claves seguras de IP manualmente" + } + }, "tunnel": { "data": { + "gateway": "Conexi\u00f3n de t\u00fanel KNX", "host": "Host", "port": "Puerto", "tunneling_type": "Tipo de t\u00fanel KNX" }, "data_description": { - "host": "Direcci\u00f3n IP del dispositivo de tunelizaci\u00f3n KNX/IP.", - "port": "Puerto del dispositivo de tunelizaci\u00f3n KNX/IP." - } + "host": "Direcci\u00f3n IP del dispositivo de t\u00fanel KNX/IP.", + "port": "Puerto del dispositivo de t\u00fanel KNX/IP." + }, + "description": "Por favor, selecciona una puerta de enlace de la lista." } } } diff --git a/homeassistant/components/knx/translations/et.json b/homeassistant/components/knx/translations/et.json index fe60f5404de..3ed43438021 100644 --- a/homeassistant/components/knx/translations/et.json +++ b/homeassistant/components/knx/translations/et.json @@ -9,20 +9,30 @@ "file_not_found": "M\u00e4\u00e4ratud faili \".knxkeys\" ei leitud asukohas config/.storage/knx/", "invalid_individual_address": "V\u00e4\u00e4rtus ei \u00fchti KNX-i individuaalse aadressi mustriga.\n 'area.line.device'", "invalid_ip_address": "Kehtetu IPv4 aadress.", - "invalid_signature": "Parool faili `.knxkeys` dekr\u00fcpteerimiseks on vale." + "invalid_signature": "Parool faili `.knxkeys` dekr\u00fcpteerimiseks on vale.", + "no_router_discovered": "V\u00f5rgus ei leitud \u00fchtegi KNXnet/IP-ruuterit.", + "no_tunnel_discovered": "V\u00f5rgust ei leitud KNX tunneliserverit." }, "step": { + "connection_type": { + "data": { + "connection_type": "KNX \u00fchenduse t\u00fc\u00fcp" + }, + "description": "Sisesta \u00fchenduse t\u00fc\u00fcp, mida kasutada KNX-\u00fchenduse jaoks. \n AUTOMAATNE \u2013 sidumine hoolitseb KNX siini \u00fchenduvuse eest, tehes l\u00fc\u00fcsikontrolli. \n TUNNELING - sidumine \u00fchendub KNX siiniga tunneli kaudu. \n MARSRUUTIMINE \u2013 sidumine \u00fchendub marsruudi kaudu KNX siiniga." + }, "manual_tunnel": { "data": { "host": "Host", "local_ip": "Home Assistanti kohalik IP aadress", "port": "Port", + "route_back": "Marsruudi tagasitee / NAT-re\u017eiim", "tunneling_type": "KNX tunneli t\u00fc\u00fcp" }, "data_description": { "host": "KNX/IP tunneldusseadme IP-aadress.", "local_ip": "Automaatse avastamise kasutamiseks j\u00e4ta t\u00fchjaks.", - "port": "KNX/IP-tunneldusseadme port." + "port": "KNX/IP-tunneldusseadme port.", + "route_back": "Luba, kui KNXneti/IP tunneldusserver on NAT-i taga. Kehtib ainult UDP-\u00fchenduste puhul." }, "description": "Sisesta tunneldamisseadme \u00fchenduse teave." }, @@ -63,11 +73,25 @@ }, "description": "Sisesta IP Secure teave." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Seadme autentimise parool", + "user_id": "Kasutaja ID", + "user_password": "Kasutaja salas\u00f5na" + }, + "data_description": { + "device_authentication": "See m\u00e4\u00e4ratakse ETSi liidese IP-paneelil.", + "user_id": "See on sageli tunneli number +1. Nii et tunnel 2 oleks kasutaja ID-ga 3.", + "user_password": "Konkreetse tunneli\u00fchenduse parool, mis on m\u00e4\u00e4ratud ETS-i tunneli paneelil \u201eAtribuudid\u201d." + }, + "description": "Sisesta oma IP secure teave." + }, "secure_tunneling": { "description": "Vali kuidas soovid KNX/IP Secure'i seadistada.", "menu_options": { "secure_knxkeys": "Kasuta knxkeys fail, mis sisaldab IP Secure teavet.", - "secure_manual": "IP Secure v\u00f5tmete k\u00e4sitsi seadistamine" + "secure_manual": "IP Secure v\u00f5tmete k\u00e4sitsi seadistamine", + "secure_tunnel_manual": "IP Secure v\u00f5tmete k\u00e4sitsi seadistamine" } }, "tunnel": { @@ -85,7 +109,32 @@ } }, "options": { + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "file_not_found": "M\u00e4\u00e4ratud kirjet '.knxkeys' ei leitud asukohast config/.storage/knx/", + "invalid_individual_address": "V\u00e4\u00e4rtuse mall ei vasta KNX seadme \u00fcksuse aadressile.\n'area.line.device'", + "invalid_ip_address": "Vigane IPv4 aadress", + "invalid_signature": "'.knxkeys' kirje dekr\u00fcptimisv\u00f5ti on vale.", + "no_router_discovered": "V\u00f5rgus ei leitud \u00fchtegi KNXnet/IP-ruuterit.", + "no_tunnel_discovered": "V\u00f5rgust ei leitud KNX tunneliserverit." + }, "step": { + "communication_settings": { + "data": { + "rate_limit": "Teavituste m\u00e4\u00e4r", + "state_updater": "Oleku uuendaja" + }, + "data_description": { + "rate_limit": "Maksimaalne v\u00e4ljaminevate telegrammide arv sekundis. '0 piirangu eemaldamiseks. Soovitatav: 20 kuni 40", + "state_updater": "M\u00e4\u00e4ra KNX siini olekute lugemise vaikev\u00e4\u00e4rtused. Kui see on keelatud, ei too Home Assistant aktiivselt olemi olekuid KNX siinilt. Saab alistada olemivalikute s\u00fcnkroonimise_olekuga." + } + }, + "connection_type": { + "data": { + "connection_type": "KNX \u00fchenduse t\u00fc\u00fcp" + }, + "description": "Sisesta \u00fchenduse t\u00fc\u00fcp, mida kasutada KNX-\u00fchenduse jaoks. \n AUTOMAATNE \u2013 sidumine hoolitseb KNX siini \u00fchenduvuse eest, tehes l\u00fc\u00fcsikontrolli. \n TUNNELING - sidumine \u00fchendub KNX siiniga tunneli kaudu. \n MARSRUUTIMINE \u2013 sidumine \u00fchendub marsruudi kaudu KNX siiniga." + }, "init": { "data": { "connection_type": "KNX \u00fchenduse t\u00fc\u00fcp", @@ -105,8 +154,75 @@ "state_updater": "M\u00e4\u00e4ra KNX siini olekute lugemise vaikev\u00e4\u00e4rtused. Kui see on keelatud, ei too Home Assistant aktiivselt olemi olekuid KNX siinilt. Saab alistada olemivalikute s\u00fcnkroonimise_olekuga." } }, + "manual_tunnel": { + "data": { + "host": "Host", + "local_ip": "Home Assistanti kohtv\u00f5rgu IP", + "port": "Port", + "route_back": "Marsruudi tagasitee / NAT-re\u017eiim", + "tunneling_type": "KNX tunneli t\u00fc\u00fcp" + }, + "data_description": { + "host": "KNX/IP tunneldusseadme IP aadress.", + "local_ip": "Automaatseks tuvastamiseks j\u00e4ta t\u00fchjaks.", + "port": "KNX/IP tunneldusseadme port.", + "route_back": "Luba kui KNXnet/IP server on NAT-i taga. Kehtib ainult UDP \u00fchendustele." + }, + "description": "Sisesta tunnel\u00fchenduse parameetrid." + }, + "options_init": { + "menu_options": { + "communication_settings": "\u00dchenduse seaded", + "connection_type": "Seadista KNX liides" + } + }, + "routing": { + "data": { + "individual_address": "\u00dcksuse aadress", + "local_ip": "Home Assistati kohtv\u00f5rgu IP aadress", + "multicast_group": "Multicasti grupp", + "multicast_port": "Multicasti port" + }, + "data_description": { + "individual_address": "Home Assistantis kasutatav KNX aadress, n\u00e4iteks '0.0.4''", + "local_ip": "Automaatseks tuvastamiseks j\u00e4ta t\u00fchjaks." + }, + "description": "Seadista marsruutimine" + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "'.knxkeys' kirje nimi (koos laiendiga)", + "knxkeys_password": "Kirje '.knxkeys' dekr\u00fcptimise v\u00f5ti" + }, + "data_description": { + "knxkeys_filename": "See kirje peaks asuma seadete kaustas '.storage/knx/'.\nHome Assistant OS puhul oleks see 'config/.storage/knx/'\nN\u00e4iteks: 'my_project.knxkeys'", + "knxkeys_password": "See saadi kirje eksportisel ETS-ist." + }, + "description": "Sisesta oma '.knxkeys' kirje teave" + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Seadme tuvastamise salas\u00f5na", + "user_id": "Kasutaja ID", + "user_password": "Kasutaja salas\u00f5na" + }, + "data_description": { + "device_authentication": "Seda saab seada ETS liidese 'IP' paneelil", + "user_id": "See on tavaliselt tunneli number+1. Seega 'Tunnel 2' on kasutaja ID-ga '3'.", + "user_password": "Konkreetse tunneli\u00fchenduse parool, mis on m\u00e4\u00e4ratud ETS-i tunneli paneelil \u201eAtribuudid\u201d." + }, + "description": "Sisesta IP secure teave." + }, + "secure_tunneling": { + "description": "Vali kuidas seadistada KNX/IP Secure", + "menu_options": { + "secure_knxkeys": "Kasuta IP secure jaoks kirjet '.knxkeys'", + "secure_tunnel_manual": "Seadista IP secure v\u00f5tmed k\u00e4sitsi" + } + }, "tunnel": { "data": { + "gateway": "KNX tunnel\u00fchendus", "host": "Host", "port": "Port", "tunneling_type": "KNX tunneli t\u00fc\u00fcp" @@ -114,7 +230,8 @@ "data_description": { "host": "KNX/IP tunneldusseadme IP-aadress.", "port": "KNX/IP-tunneldusseadme port." - } + }, + "description": "Vali nimekirjast l\u00fc\u00fcs" } } } diff --git a/homeassistant/components/knx/translations/hu.json b/homeassistant/components/knx/translations/hu.json index 92411b58312..1aac9fb1b89 100644 --- a/homeassistant/components/knx/translations/hu.json +++ b/homeassistant/components/knx/translations/hu.json @@ -9,20 +9,30 @@ "file_not_found": "A megadott '.knxkeys' f\u00e1jl nem tal\u00e1lhat\u00f3 a config/.storage/knx/ el\u00e9r\u00e9si \u00fatvonalon.", "invalid_individual_address": "Az \u00e9rt\u00e9k nem felel meg a KNX egyedi c\u00edm mint\u00e1j\u00e1nak.\n'area.line.device'", "invalid_ip_address": "\u00c9rv\u00e9nytelen IPv4-c\u00edm.", - "invalid_signature": "A '.knxkeys' f\u00e1jl visszafejt\u00e9s\u00e9hez haszn\u00e1lt jelsz\u00f3 helytelen." + "invalid_signature": "A '.knxkeys' f\u00e1jl visszafejt\u00e9s\u00e9hez haszn\u00e1lt jelsz\u00f3 helytelen.", + "no_router_discovered": "Nem tal\u00e1lhat\u00f3 KNXnet/IP \u00fatv\u00e1laszt\u00f3 a h\u00e1l\u00f3zaton.", + "no_tunnel_discovered": "Nem tal\u00e1lhat\u00f3 KNX alag\u00fat-kiszolg\u00e1l\u00f3 a h\u00e1l\u00f3zaton." }, "step": { + "connection_type": { + "data": { + "connection_type": "KNX csatlakoz\u00e1s t\u00edpusa" + }, + "description": "K\u00e9rem, adja meg a KNX-kapcsolathoz haszn\u00e1land\u00f3 kapcsolatt\u00edpust. \n AUTOMATIKUS - Az integr\u00e1ci\u00f3 gondoskodik a KNX buszhoz val\u00f3 kapcsol\u00f3d\u00e1sr\u00f3l egy \u00e1tj\u00e1r\u00f3 keres\u00e9s elv\u00e9gz\u00e9s\u00e9vel. \n TUNNELING - Az integr\u00e1ci\u00f3 alag\u00faton kereszt\u00fcl csatlakozik a KNX buszhoz. \n ROUTING - Az integr\u00e1ci\u00f3 a KNX buszhoz \u00fatv\u00e1laszt\u00e1ssal csatlakozik." + }, "manual_tunnel": { "data": { "host": "C\u00edm", "local_ip": "Home Assistant lok\u00e1lis IP c\u00edme", "port": "Port", + "route_back": "Vissza\u00fat / NAT m\u00f3d", "tunneling_type": "KNX alag\u00fat t\u00edpusa" }, "data_description": { "host": "A KNX/IP tunnel eszk\u00f6z IP-c\u00edme.", "local_ip": "Az automatikus felder\u00edt\u00e9s haszn\u00e1lat\u00e1hoz hagyja \u00fcresen.", - "port": "A KNX/IP tunnel eszk\u00f6z portsz\u00e1ma." + "port": "A KNX/IP tunnel eszk\u00f6z portsz\u00e1ma.", + "route_back": "Enged\u00e9lyezze, ha a KNXnet/IP alag\u00fatkiszolg\u00e1l\u00f3 NAT m\u00f6g\u00f6tt van. Csak UDP-kapcsolatokra vonatkozik." }, "description": "Adja meg az alag\u00fatkezel\u0151 (tunneling) eszk\u00f6z csatlakoz\u00e1si adatait." }, @@ -63,11 +73,25 @@ }, "description": "K\u00e9rem, adja meg az IP secure adatokat." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Eszk\u00f6z hiteles\u00edt\u00e9si jelsz\u00f3", + "user_id": "Felhaszn\u00e1l\u00f3i azonos\u00edt\u00f3", + "user_password": "Felhaszn\u00e1l\u00f3i jelsz\u00f3" + }, + "data_description": { + "device_authentication": "Ezt az ETS-ben az interf\u00e9sz \"IP\" panelj\u00e9n kell be\u00e1ll\u00edtani.", + "user_id": "Ez gyakran a tunnel sz\u00e1ma +1. Teh\u00e1t a \"Tunnel 2\" felhaszn\u00e1l\u00f3i azonos\u00edt\u00f3ja \"3\".", + "user_password": "Jelsz\u00f3 az adott tunnelhez, amely a tunnel \u201eProperties\u201d panelj\u00e9n van be\u00e1ll\u00edtva az ETS-ben." + }, + "description": "K\u00e9rem, adja meg az IP secure adatokat." + }, "secure_tunneling": { "description": "V\u00e1lassza ki, hogyan szeretn\u00e9 konfigur\u00e1lni az KNX/IP secure-t.", "menu_options": { "secure_knxkeys": "IP secure kulcsokat tartalmaz\u00f3 '.knxkeys' f\u00e1jl haszn\u00e1lata", - "secure_manual": "IP secure kulcsok manu\u00e1lis be\u00e1ll\u00edt\u00e1sa" + "secure_manual": "IP secure kulcsok manu\u00e1lis be\u00e1ll\u00edt\u00e1sa", + "secure_tunnel_manual": "IP secure kulcsok manu\u00e1lis be\u00e1ll\u00edt\u00e1sa" } }, "tunnel": { @@ -85,7 +109,32 @@ } }, "options": { + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "file_not_found": "A megadott '.knxkeys' f\u00e1jl nem tal\u00e1lhat\u00f3 a config/.storage/knx/ el\u00e9r\u00e9si \u00fatvonalon.", + "invalid_individual_address": "Az \u00e9rt\u00e9k nem felel meg a KNX egyedi c\u00edm mint\u00e1j\u00e1nak.\n'area.line.device'", + "invalid_ip_address": "\u00c9rv\u00e9nytelen IPv4-c\u00edm.", + "invalid_signature": "A '.knxkeys' f\u00e1jl visszafejt\u00e9s\u00e9hez haszn\u00e1lt jelsz\u00f3 helytelen.", + "no_router_discovered": "Nem tal\u00e1lhat\u00f3 KNXnet/IP \u00fatv\u00e1laszt\u00f3 a h\u00e1l\u00f3zaton.", + "no_tunnel_discovered": "Nem tal\u00e1lhat\u00f3 KNX alag\u00fat-kiszolg\u00e1l\u00f3 a h\u00e1l\u00f3zaton." + }, "step": { + "communication_settings": { + "data": { + "rate_limit": "Lek\u00e9r\u00e9si korl\u00e1toz\u00e1s", + "state_updater": "\u00c1llapot friss\u00edt\u0151" + }, + "data_description": { + "rate_limit": "Maxim\u00e1lisan kimen\u0151 \u00fczenet m\u00e1sodpercenk\u00e9nt. 0 a kikapcsol\u00e1shoz.\nAj\u00e1nlott: 0, vagy 20 \u00e9s 40 k\u00f6z\u00f6tt", + "state_updater": "Alap\u00e9rtelmezett be\u00e1ll\u00edt\u00e1s a KNX busz \u00e1llapotainak olvas\u00e1s\u00e1hoz. Ha le va tiltva, Home Assistant nem fog akt\u00edvan lek\u00e9rdezni egys\u00e9g\u00e1llapotokat a KNX buszr\u00f3l. Fel\u00fclb\u00edr\u00e1lhat\u00f3 a `sync_state` entit\u00e1s opci\u00f3kkal." + } + }, + "connection_type": { + "data": { + "connection_type": "KNX csatlakoz\u00e1s t\u00edpusa" + }, + "description": "K\u00e9rem, adja meg a KNX-kapcsolathoz haszn\u00e1land\u00f3 kapcsolatt\u00edpust. \n AUTOMATIKUS - Az integr\u00e1ci\u00f3 gondoskodik a KNX buszhoz val\u00f3 kapcsol\u00f3d\u00e1sr\u00f3l egy \u00e1tj\u00e1r\u00f3 keres\u00e9s elv\u00e9gz\u00e9s\u00e9vel. \n TUNNELING - Az integr\u00e1ci\u00f3 alag\u00faton kereszt\u00fcl csatlakozik a KNX buszhoz. \n ROUTING - Az integr\u00e1ci\u00f3 a KNX buszhoz \u00fatv\u00e1laszt\u00e1ssal csatlakozik." + }, "init": { "data": { "connection_type": "KNX csatlakoz\u00e1s t\u00edpusa", @@ -105,8 +154,75 @@ "state_updater": "Alap\u00e9rtelmezett be\u00e1ll\u00edt\u00e1s a KNX busz \u00e1llapotainak olvas\u00e1s\u00e1hoz. Ha le va tiltva, Home Assistant nem fog akt\u00edvan lek\u00e9rdezni egys\u00e9g\u00e1llapotokat a KNX buszr\u00f3l. Fel\u00fclb\u00edr\u00e1lhat\u00f3 a `sync_state` entit\u00e1s opci\u00f3kkal." } }, + "manual_tunnel": { + "data": { + "host": "C\u00edm", + "local_ip": "Home Assistant lok\u00e1lis IP c\u00edme", + "port": "Port", + "route_back": "Vissza\u00fat / NAT m\u00f3d", + "tunneling_type": "KNX alag\u00fat t\u00edpusa" + }, + "data_description": { + "host": "A KNX/IP tunnel eszk\u00f6z IP-c\u00edme.", + "local_ip": "Az automatikus felder\u00edt\u00e9s haszn\u00e1lat\u00e1hoz hagyja \u00fcresen.", + "port": "A KNX/IP tunnel eszk\u00f6z portsz\u00e1ma.", + "route_back": "Enged\u00e9lyezze, ha a KNXnet/IP alag\u00fatkiszolg\u00e1l\u00f3 NAT m\u00f6g\u00f6tt van. Csak UDP-kapcsolatokra vonatkozik." + }, + "description": "Adja meg az alag\u00fatkezel\u0151 (tunneling) eszk\u00f6z csatlakoz\u00e1si adatait." + }, + "options_init": { + "menu_options": { + "communication_settings": "Kommunik\u00e1ci\u00f3s be\u00e1ll\u00edt\u00e1sok", + "connection_type": "KNX interf\u00e9sz konfigur\u00e1l\u00e1sa" + } + }, + "routing": { + "data": { + "individual_address": "Egy\u00e9ni c\u00edm", + "local_ip": "Home Assistant lok\u00e1lis IP c\u00edme", + "multicast_group": "Multicast csoport", + "multicast_port": "Multicast portsz\u00e1m" + }, + "data_description": { + "individual_address": "A Home Assistant \u00e1ltal haszn\u00e1land\u00f3 KNX-c\u00edm, pl. \"0.0.4\".", + "local_ip": "Az automatikus felder\u00edt\u00e9s haszn\u00e1lat\u00e1hoz hagyja \u00fcresen." + }, + "description": "K\u00e9rem, konfigur\u00e1lja az \u00fatv\u00e1laszt\u00e1si (routing) be\u00e1ll\u00edt\u00e1sokat." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "A '.knxkeys' f\u00e1jl teljes neve (kiterjeszt\u00e9ssel)", + "knxkeys_password": "A '.knxkeys' f\u00e1jl visszafejt\u00e9s\u00e9hez sz\u00fcks\u00e9ges jelsz\u00f3" + }, + "data_description": { + "knxkeys_filename": "A f\u00e1jl a `.storage/knx/` konfigur\u00e1ci\u00f3s k\u00f6nyvt\u00e1r\u00e1ban helyezend\u0151.\nHome Assistant oper\u00e1ci\u00f3s rendszer eset\u00e9n ez a k\u00f6vetkez\u0151 lenne: `/config/.storage/knx/`\nP\u00e9lda: \"my_project.knxkeys\".", + "knxkeys_password": "Ez a be\u00e1ll\u00edt\u00e1s a f\u00e1jl ETS-b\u0151l t\u00f6rt\u00e9n\u0151 export\u00e1l\u00e1sakor t\u00f6rt\u00e9nt." + }, + "description": "K\u00e9rem, adja meg a '.knxkeys' f\u00e1jl adatait." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Eszk\u00f6z hiteles\u00edt\u00e9si jelsz\u00f3", + "user_id": "Felhaszn\u00e1l\u00f3i azonos\u00edt\u00f3", + "user_password": "Felhaszn\u00e1l\u00f3i jelsz\u00f3" + }, + "data_description": { + "device_authentication": "Ezt az ETS-ben az interf\u00e9sz \"IP\" panelj\u00e9n kell be\u00e1ll\u00edtani.", + "user_id": "Ez gyakran a tunnel sz\u00e1ma +1. Teh\u00e1t a \"Tunnel 2\" felhaszn\u00e1l\u00f3i azonos\u00edt\u00f3ja \"3\".", + "user_password": "Jelsz\u00f3 az adott tunnelhez, amely a tunnel \u201eProperties\u201d panelj\u00e9n van be\u00e1ll\u00edtva az ETS-ben." + }, + "description": "K\u00e9rem, adja meg az IP secure adatokat." + }, + "secure_tunneling": { + "description": "V\u00e1lassza ki, hogyan szeretn\u00e9 konfigur\u00e1lni az KNX/IP secure-t.", + "menu_options": { + "secure_knxkeys": "IP secure kulcsokat tartalmaz\u00f3 '.knxkeys' f\u00e1jl haszn\u00e1lata", + "secure_tunnel_manual": "IP secure kulcsok manu\u00e1lis be\u00e1ll\u00edt\u00e1sa" + } + }, "tunnel": { "data": { + "gateway": "KNX alag\u00fat (tunnel) kapcsolat", "host": "C\u00edm", "port": "Port", "tunneling_type": "KNX alag\u00fat t\u00edpusa" @@ -114,7 +230,8 @@ "data_description": { "host": "A KNX/IP tunnel eszk\u00f6z IP-c\u00edme.", "port": "A KNX/IP tunnel eszk\u00f6z portsz\u00e1ma." - } + }, + "description": "V\u00e1lasszon egy \u00e1tj\u00e1r\u00f3t a list\u00e1b\u00f3l." } } } diff --git a/homeassistant/components/knx/translations/id.json b/homeassistant/components/knx/translations/id.json index bbf9a1b7862..6b4977e543a 100644 --- a/homeassistant/components/knx/translations/id.json +++ b/homeassistant/components/knx/translations/id.json @@ -9,20 +9,30 @@ "file_not_found": "File `.knxkeys` yang ditentukan tidak ditemukan di jalur config/.storage/knx/", "invalid_individual_address": "Nilai tidak cocok dengan pola untuk alamat individual KNX.\n'area.line.device'", "invalid_ip_address": "Alamat IPv4 tidak valid", - "invalid_signature": "Kata sandi untuk mendekripsi file `.knxkeys` salah." + "invalid_signature": "Kata sandi untuk mendekripsi file `.knxkeys` salah.", + "no_router_discovered": "Tidak ada router KNXnet/IP yang ditemukan di jaringan.", + "no_tunnel_discovered": "Tidak dapat menemukan server tunneling KNX di jaringan Anda." }, "step": { + "connection_type": { + "data": { + "connection_type": "Jenis Koneksi KNX" + }, + "description": "Masukkan jenis koneksi yang harus kami gunakan untuk koneksi KNX Anda. \nOTOMATIS - Integrasi melakukan konektivitas ke bus KNX Anda dengan melakukan pemindaian gateway. \nTUNNELING - Integrasi akan terhubung ke bus KNX Anda melalui tunneling. \nROUTING - Integrasi akan terhubung ke bus KNX Anda melalui routing." + }, "manual_tunnel": { "data": { "host": "Host", "local_ip": "IP lokal Home Assistant", "port": "Port", + "route_back": "Dirutekan kembali/Mode NAT", "tunneling_type": "Jenis Tunnel KNX" }, "data_description": { "host": "Alamat IP perangkat tunneling KNX/IP.", "local_ip": "Kosongkan untuk menggunakan penemuan otomatis.", - "port": "Port perangkat tunneling KNX/IP." + "port": "Port perangkat tunneling KNX/IP.", + "route_back": "Aktifkan jika server tunneling KNXnet/IP Anda berada di belakang NAT. Hanya berlaku untuk koneksi UDP." }, "description": "Masukkan informasi koneksi untuk perangkat tunneling Anda." }, @@ -63,11 +73,25 @@ }, "description": "Masukkan informasi IP aman Anda." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Kata sandi autentikasi perangkat", + "user_id": "ID pengguna", + "user_password": "Kata sandi pengguna" + }, + "data_description": { + "device_authentication": "Ini diatur dalam panel 'IP' dalam antarmuka di ETS.", + "user_id": "Ini sering kali merupakan tunnel nomor +1. Jadi 'Tunnel 2' akan memiliki User-ID '3'.", + "user_password": "Kata sandi untuk koneksi tunnel tertentu yang diatur di panel 'Properties' tunnel di ETS." + }, + "description": "Masukkan informasi IP aman Anda." + }, "secure_tunneling": { "description": "Pilih cara Anda ingin mengonfigurasi KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Gunakan file `.knxkeys` yang berisi kunci aman IP", - "secure_manual": "Konfigurasikan kunci aman IP secara manual" + "secure_manual": "Konfigurasikan kunci aman IP secara manual", + "secure_tunnel_manual": "Konfigurasikan kunci aman IP secara manual" } }, "tunnel": { @@ -85,7 +109,32 @@ } }, "options": { + "error": { + "cannot_connect": "Gagal terhubung", + "file_not_found": "File `.knxkeys` yang ditentukan tidak ditemukan di jalur config/.storage/knx/", + "invalid_individual_address": "Nilai tidak cocok dengan pola untuk alamat individual KNX.\n'area.line.device'", + "invalid_ip_address": "Alamat IPv4 tidak valid", + "invalid_signature": "Kata sandi untuk mendekripsi file `.knxkeys` salah.", + "no_router_discovered": "Tidak ada router KNXnet/IP yang ditemukan di jaringan.", + "no_tunnel_discovered": "Tidak dapat menemukan server tunneling KNX di jaringan Anda." + }, "step": { + "communication_settings": { + "data": { + "rate_limit": "Batas data", + "state_updater": "Pembaruan status" + }, + "data_description": { + "rate_limit": "Telegram keluar maksimum per detik. `0` untuk menonaktifkan batas. Direkomendasikan: 0 atau 20 hingga 40", + "state_updater": "Menyetel default untuk status pembacaan KNX Bus. Saat dinonaktifkan, Home Assistant tidak akan secara aktif mengambil status entitas dari KNX Bus. Hal ini bisa ditimpa dengan opsi entitas `sync_state`." + } + }, + "connection_type": { + "data": { + "connection_type": "Jenis Koneksi KNX" + }, + "description": "Masukkan jenis koneksi yang harus kami gunakan untuk koneksi KNX Anda. \nOTOMATIS - Integrasi melakukan konektivitas ke bus KNX Anda dengan melakukan pemindaian gateway. \nTUNNELING - Integrasi akan terhubung ke bus KNX Anda melalui tunneling. \nROUTING - Integrasi akan terhubung ke bus KNX Anda melalui routing." + }, "init": { "data": { "connection_type": "Jenis Koneksi KNX", @@ -105,8 +154,75 @@ "state_updater": "Menyetel default untuk status pembacaan KNX Bus. Saat dinonaktifkan, Home Assistant tidak akan secara aktif mengambil status entitas dari KNX Bus. Hal ini bisa ditimpa dengan opsi entitas `sync_state`." } }, + "manual_tunnel": { + "data": { + "host": "Host", + "local_ip": "IP lokal Home Assistant", + "port": "Port", + "route_back": "Dirutekan kembali/Mode NAT", + "tunneling_type": "Jenis Tunnel KNX" + }, + "data_description": { + "host": "Alamat IP perangkat tunneling KNX/IP.", + "local_ip": "Kosongkan untuk menggunakan penemuan otomatis.", + "port": "Port perangkat tunneling KNX/IP.", + "route_back": "Aktifkan jika server tunneling KNXnet/IP Anda berada di belakang NAT. Hanya berlaku untuk koneksi UDP." + }, + "description": "Masukkan informasi koneksi untuk perangkat tunneling Anda." + }, + "options_init": { + "menu_options": { + "communication_settings": "Pengaturan komunikasi", + "connection_type": "Konfigurasikan antarmuka KNX" + } + }, + "routing": { + "data": { + "individual_address": "Alamat individual", + "local_ip": "IP lokal Home Assistant", + "multicast_group": "Grup multicast", + "multicast_port": "Port multicast" + }, + "data_description": { + "individual_address": "Alamat KNX yang akan digunakan oleh Home Assistant, misalnya `0.0.4`", + "local_ip": "Kosongkan untuk menggunakan penemuan otomatis." + }, + "description": "Konfigurasikan opsi routing." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Nama file `.knxkeys` Anda (termasuk ekstensi)", + "knxkeys_password": "Kata sandi untuk mendekripsi file `.knxkeys`" + }, + "data_description": { + "knxkeys_filename": "File diharapkan dapat ditemukan di direktori konfigurasi Anda di `.storage/knx/`.\nDi Home Assistant OS ini akan menjadi `/config/.storage/knx/`\nContoh: `proyek_saya.knxkeys`", + "knxkeys_password": "Ini disetel saat mengekspor file dari ETS." + }, + "description": "Masukkan informasi untuk file `.knxkeys` Anda." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Kata sandi autentikasi perangkat", + "user_id": "ID pengguna", + "user_password": "Kata sandi pengguna" + }, + "data_description": { + "device_authentication": "Ini diatur dalam panel 'IP' dalam antarmuka di ETS.", + "user_id": "Ini sering kali merupakan tunnel nomor +1. Jadi 'Tunnel 2' akan memiliki User-ID '3'.", + "user_password": "Kata sandi untuk koneksi tunnel tertentu yang diatur di panel 'Properties' tunnel di ETS." + }, + "description": "Masukkan informasi IP aman Anda." + }, + "secure_tunneling": { + "description": "Pilih cara Anda ingin mengonfigurasi KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Gunakan file `.knxkeys` yang berisi kunci aman IP", + "secure_tunnel_manual": "Konfigurasikan kunci aman IP secara manual" + } + }, "tunnel": { "data": { + "gateway": "Koneksi Tunnel KNX", "host": "Host", "port": "Port", "tunneling_type": "Jenis Tunnel KNX" @@ -114,7 +230,8 @@ "data_description": { "host": "Alamat IP perangkat tunneling KNX/IP.", "port": "Port perangkat tunneling KNX/IP." - } + }, + "description": "Pilih gateway dari daftar." } } } diff --git a/homeassistant/components/knx/translations/it.json b/homeassistant/components/knx/translations/it.json index 8c2c58ad2d5..4a7bc92652d 100644 --- a/homeassistant/components/knx/translations/it.json +++ b/homeassistant/components/knx/translations/it.json @@ -9,20 +9,30 @@ "file_not_found": "Il file `.knxkeys` specificato non \u00e8 stato trovato nel percorso config/.storage/knx/", "invalid_individual_address": "Il valore non corrisponde al modello per l'indirizzo individuale KNX. 'area.line.device'", "invalid_ip_address": "Indirizzo IPv4 non valido.", - "invalid_signature": "La password per decifrare il file `.knxkeys` \u00e8 errata." + "invalid_signature": "La password per decifrare il file `.knxkeys` \u00e8 errata.", + "no_router_discovered": "Non \u00e8 stato rilevato alcun router KNXnet/IP nella rete.", + "no_tunnel_discovered": "Impossibile trovare un server di tunneling KNX sulla rete." }, "step": { + "connection_type": { + "data": { + "connection_type": "Tipo di connessione KNX" + }, + "description": "Inserisci il tipo di connessione che dovremmo usare per la tua connessione KNX. \n AUTOMATICO - L'integrazione si occupa della connettivit\u00e0 al tuo bus KNX eseguendo una scansione del gateway. \n TUNNELING - L'integrazione si collegher\u00e0 al tuo bus KNX tramite tunneling. \n ROUTING - L'integrazione si connetter\u00e0 al tuo bus KNX tramite instradamento." + }, "manual_tunnel": { "data": { "host": "Host", "local_ip": "IP locale di Home Assistant", "port": "Porta", + "route_back": "Modalit\u00e0 Route Back / NAT", "tunneling_type": "Tipo tunnel KNX" }, "data_description": { "host": "Indirizzo IP del dispositivo di tunneling KNX/IP.", "local_ip": "Lascia vuoto per usare il rilevamento automatico.", - "port": "Porta del dispositivo di tunneling KNX/IP." + "port": "Porta del dispositivo di tunneling KNX/IP.", + "route_back": "Abilitare se il server di tunneling KNXnet/IP \u00e8 protetto da NAT. Si applica solo alle connessioni UDP." }, "description": "Inserisci le informazioni di connessione del tuo dispositivo di tunneling." }, @@ -35,7 +45,7 @@ }, "data_description": { "individual_address": "Indirizzo KNX che deve essere utilizzato da Home Assistant, ad es. `0.0.4`", - "local_ip": "Lasciare vuoto per usare il rilevamento automatico." + "local_ip": "Lascia vuoto per usare il rilevamento automatico." }, "description": "Configura le opzioni di instradamento." }, @@ -45,7 +55,7 @@ "knxkeys_password": "La password per decifrare il file `.knxkeys`" }, "data_description": { - "knxkeys_filename": "Il file dovrebbe essere trovato nella tua cartella di configurazione in `.storage/knx/`.\n Nel Sistema Operativo di Home Assistant questo sarebbe `/config/.storage/knx/`\n Esempio: `mio_progetto.knxkeys`", + "knxkeys_filename": "Il file dovrebbe trovarsi nella directory di configurazione in '.storage/knx/'.\nNel sistema operativo Home Assistant questa sarebbe '/config/.storage/knx/'\nEsempio: 'my_project.knxkeys'", "knxkeys_password": "Questo \u00e8 stato impostato durante l'esportazione del file da ETS." }, "description": "Inserisci le informazioni per il tuo file `.knxkeys`." @@ -63,11 +73,25 @@ }, "description": "Inserisci le tue informazioni di sicurezza IP." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Password di autenticazione del dispositivo", + "user_id": "ID utente", + "user_password": "Password utente" + }, + "data_description": { + "device_authentication": "Questo \u00e8 impostato nel pannello 'IP' dell'interfaccia in ETS.", + "user_id": "Questo \u00e8 spesso il tunnel numero +1. Quindi \"Tunnel 2\" avrebbe l'ID utente \"3\".", + "user_password": "Password per la connessione specifica del tunnel impostata nel pannello 'Propriet\u00e0' del tunnel in ETS." + }, + "description": "Inserisci le tue informazioni di sicurezza IP." + }, "secure_tunneling": { "description": "Seleziona come vuoi configurare KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Utilizza un file `.knxkeys` contenente chiavi di sicurezza IP", - "secure_manual": "Configura manualmente le chiavi di sicurezza IP" + "secure_manual": "Configura manualmente le chiavi di sicurezza IP", + "secure_tunnel_manual": "Configura manualmente le chiavi di sicurezza IP" } }, "tunnel": { @@ -85,7 +109,32 @@ } }, "options": { + "error": { + "cannot_connect": "Impossibile connettersi", + "file_not_found": "Il file `.knxkeys` specificato non \u00e8 stato trovato nel percorso config/.storage/knx/", + "invalid_individual_address": "Il valore non corrisponde al modello per l'indirizzo individuale KNX. 'area.line.device'", + "invalid_ip_address": "Indirizzo IPv4 non valido.", + "invalid_signature": "La password per decifrare il file `.knxkeys` \u00e8 errata.", + "no_router_discovered": "Non \u00e8 stato rilevato alcun router KNXnet/IP nella rete.", + "no_tunnel_discovered": "Impossibile trovare un server di tunneling KNX sulla rete." + }, "step": { + "communication_settings": { + "data": { + "rate_limit": "Limite di velocit\u00e0", + "state_updater": "Aggiornatore di stato" + }, + "data_description": { + "rate_limit": "Numero massimo di telegrammi in uscita al secondo.\n'0' per disabilitare il limite. Consigliato: 0 o da 20 a 40", + "state_updater": "Impostazione predefinita per la lettura degli stati dal bus KNX. Quando disabilitato, Home Assistant non recuperer\u00e0 attivamente gli stati delle entit\u00e0 dal bus KNX. Pu\u00f2 essere sovrascritto dalle opzioni dell'entit\u00e0 `sync_state`." + } + }, + "connection_type": { + "data": { + "connection_type": "Tipo di connessione KNX" + }, + "description": "Inserisci il tipo di connessione che dovremmo usare per la tua connessione KNX. \n AUTOMATICO - L'integrazione si occupa della connettivit\u00e0 al tuo bus KNX eseguendo una scansione del gateway. \n TUNNELING - L'integrazione si collegher\u00e0 al tuo bus KNX tramite tunneling. \n ROUTING - L'integrazione si connetter\u00e0 al tuo bus KNX tramite instradamento." + }, "init": { "data": { "connection_type": "Tipo di connessione KNX", @@ -105,8 +154,75 @@ "state_updater": "Impostazione predefinita per la lettura degli stati dal bus KNX. Se disabilitata Home Assistant non recuperer\u00e0 attivamente gli stati delle entit\u00e0 dal bus KNX. Pu\u00f2 essere sovrascritta dalle opzioni dell'entit\u00e0 `sync_state`." } }, + "manual_tunnel": { + "data": { + "host": "Host", + "local_ip": "IP locale di Home Assistant", + "port": "Porta", + "route_back": "Modalit\u00e0 Route Back / NAT", + "tunneling_type": "Tipo tunnel KNX" + }, + "data_description": { + "host": "Indirizzo IP del dispositivo di tunneling KNX/IP.", + "local_ip": "Lascia vuoto per usare il rilevamento automatico.", + "port": "Porta del dispositivo di tunneling KNX/IP.", + "route_back": "Abilitare se il server di tunneling KNXnet/IP \u00e8 protetto da NAT. Si applica solo alle connessioni UDP." + }, + "description": "Inserisci le informazioni di connessione del tuo dispositivo di tunneling." + }, + "options_init": { + "menu_options": { + "communication_settings": "Impostazioni di comunicazione", + "connection_type": "Configura interfaccia KNX" + } + }, + "routing": { + "data": { + "individual_address": "Indirizzo individuale", + "local_ip": "IP locale di Home Assistant", + "multicast_group": "Gruppo multicast", + "multicast_port": "Porta multicast" + }, + "data_description": { + "individual_address": "Indirizzo KNX che deve essere utilizzato da Home Assistant, ad es. `0.0.4`", + "local_ip": "Lascia vuoto per usare il rilevamento automatico." + }, + "description": "Configura le opzioni di instradamento." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Il nome del file `.knxkeys` (inclusa l'estensione)", + "knxkeys_password": "La password per decifrare il file `.knxkeys`" + }, + "data_description": { + "knxkeys_filename": "Il file dovrebbe trovarsi nella directory di configurazione in '.storage/knx/'.\nNel sistema operativo Home Assistant questa sarebbe '/config/.storage/knx/'\nEsempio: 'my_project.knxkeys'", + "knxkeys_password": "Questo \u00e8 stato impostato durante l'esportazione del file da ETS." + }, + "description": "Inserisci le informazioni per il tuo file `.knxkeys`." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Password di autenticazione del dispositivo", + "user_id": "ID utente", + "user_password": "Password utente" + }, + "data_description": { + "device_authentication": "Questo \u00e8 impostato nel pannello 'IP' dell'interfaccia in ETS.", + "user_id": "Questo \u00e8 spesso il tunnel numero +1. Quindi \"Tunnel 2\" avrebbe l'ID utente \"3\".", + "user_password": "Password per la connessione specifica del tunnel impostata nel pannello 'Propriet\u00e0' del tunnel in ETS." + }, + "description": "Inserisci le tue informazioni di sicurezza IP." + }, + "secure_tunneling": { + "description": "Seleziona come vuoi configurare KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Utilizza un file `.knxkeys` contenente chiavi di sicurezza IP", + "secure_tunnel_manual": "Configura manualmente le chiavi di sicurezza IP" + } + }, "tunnel": { "data": { + "gateway": "Connessione tunnel KNX", "host": "Host", "port": "Porta", "tunneling_type": "Tipo tunnel KNX" @@ -114,7 +230,8 @@ "data_description": { "host": "Indirizzo IP del dispositivo di tunneling KNX/IP.", "port": "Porta del dispositivo di tunneling KNX/IP." - } + }, + "description": "Seleziona un gateway dall'elenco." } } } diff --git a/homeassistant/components/knx/translations/pt-BR.json b/homeassistant/components/knx/translations/pt-BR.json index dcdf057493b..bae7dd2f82c 100644 --- a/homeassistant/components/knx/translations/pt-BR.json +++ b/homeassistant/components/knx/translations/pt-BR.json @@ -9,20 +9,30 @@ "file_not_found": "O arquivo `.knxkeys` especificado n\u00e3o foi encontrado no caminho config/.storage/knx/", "invalid_individual_address": "O valor n\u00e3o corresponde ao padr\u00e3o do endere\u00e7o individual KNX.\n '\u00e1rea.linha.dispositivo'", "invalid_ip_address": "Endere\u00e7o IPv4 inv\u00e1lido.", - "invalid_signature": "A senha para descriptografar o arquivo `.knxkeys` est\u00e1 errada." + "invalid_signature": "A senha para descriptografar o arquivo `.knxkeys` est\u00e1 errada.", + "no_router_discovered": "Nenhum roteador KNXnet/IP foi descoberto na rede.", + "no_tunnel_discovered": "N\u00e3o foi poss\u00edvel encontrar um servidor de encapsulamento KNX em sua rede." }, "step": { + "connection_type": { + "data": { + "connection_type": "Tipo de conex\u00e3o KNX" + }, + "description": "Insira o tipo de conex\u00e3o que devemos usar para sua conex\u00e3o KNX.\n AUTOM\u00c1TICO - A integra\u00e7\u00e3o cuida da conectividade com o seu KNX Bus realizando uma varredura de gateway.\n TUNNELING - A integra\u00e7\u00e3o ser\u00e1 conectada ao seu barramento KNX via tunelamento.\n ROUTING - A integra\u00e7\u00e3o ligar-se-\u00e1 ao seu bus KNX atrav\u00e9s de encaminhamento." + }, "manual_tunnel": { "data": { "host": "Nome do host", "local_ip": "IP local do Home Assistant", "port": "Porta", + "route_back": "Rota de volta / modo NAT", "tunneling_type": "Tipo de t\u00fanel KNX" }, "data_description": { "host": "Endere\u00e7o IP do dispositivo de tunelamento KNX/IP.", "local_ip": "Deixe em branco para usar a descoberta autom\u00e1tica.", - "port": "Porta do dispositivo de tunelamento KNX/IP." + "port": "Porta do dispositivo de tunelamento KNX/IP.", + "route_back": "Ative se o servidor de encapsulamento KNXnet/IP estiver atr\u00e1s do NAT. Aplica-se apenas a conex\u00f5es UDP." }, "description": "Por favor, digite as informa\u00e7\u00f5es de conex\u00e3o do seu dispositivo de tunelamento." }, @@ -63,11 +73,25 @@ }, "description": "Por favor, insira suas informa\u00e7\u00f5es seguras de IP." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Senha de autentica\u00e7\u00e3o do dispositivo", + "user_id": "ID do usu\u00e1rio", + "user_password": "Senha do usu\u00e1rio" + }, + "data_description": { + "device_authentication": "Isso \u00e9 definido no painel 'IP' da interface no ETS.", + "user_id": "Isso geralmente \u00e9 o n\u00famero do t\u00fanel +1. Portanto, 'T\u00fanel 2' teria o ID de usu\u00e1rio '3'.", + "user_password": "Senha para a conex\u00e3o de t\u00fanel espec\u00edfica definida no painel 'Propriedades' do t\u00fanel no ETS." + }, + "description": "Por favor, insira suas informa\u00e7\u00f5es seguras de IP." + }, "secure_tunneling": { "description": "Selecione como deseja configurar o KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Use um arquivo `.knxkeys` contendo chaves seguras de IP", - "secure_manual": "Configurar manualmente as chaves de seguran\u00e7a IP" + "secure_manual": "Configurar manualmente as chaves de seguran\u00e7a IP", + "secure_tunnel_manual": "Configurar chaves seguras de IP manualmente" } }, "tunnel": { @@ -85,7 +109,32 @@ } }, "options": { + "error": { + "cannot_connect": "Falhou ao conectar", + "file_not_found": "O arquivo `.knxkeys` especificado n\u00e3o foi encontrado no caminho config/.storage/knx/", + "invalid_individual_address": "O valor n\u00e3o corresponde ao padr\u00e3o do endere\u00e7o individual KNX.\n'\u00e1rea.linha.dispositivo'", + "invalid_ip_address": "Endere\u00e7o IPv4 inv\u00e1lido.", + "invalid_signature": "A senha para descriptografar o arquivo `.knxkeys` est\u00e1 errada.", + "no_router_discovered": "Nenhum roteador KNXnet/IP foi descoberto na rede.", + "no_tunnel_discovered": "N\u00e3o foi poss\u00edvel encontrar um servidor de encapsulamento KNX em sua rede." + }, "step": { + "communication_settings": { + "data": { + "rate_limit": "Taxa limite", + "state_updater": "Atualizador de estado" + }, + "data_description": { + "rate_limit": "M\u00e1ximo de telegramas de sa\u00edda por segundo.\n `0` para desabilitar o limite. Recomendado: 0 ou 20 a 40", + "state_updater": "Defina o padr\u00e3o para estados de leitura do barramento KNX. Quando desativado, o Home Assistant n\u00e3o recuperar\u00e1 ativamente os estados de entidade do barramento KNX. Pode ser substitu\u00eddo pelas op\u00e7\u00f5es de entidade `sync_state`." + } + }, + "connection_type": { + "data": { + "connection_type": "Tipo de conex\u00e3o KNX" + }, + "description": "Insira o tipo de conex\u00e3o que devemos usar para sua conex\u00e3o KNX.\n AUTOM\u00c1TICO - A integra\u00e7\u00e3o cuida da conectividade com o seu KNX Bus realizando uma varredura de gateway.\n TUNNELING - A integra\u00e7\u00e3o ser\u00e1 conectada ao seu barramento KNX via tunelamento.\n ROUTING - A integra\u00e7\u00e3o ligar-se-\u00e1 ao seu bus KNX atrav\u00e9s de encaminhamento." + }, "init": { "data": { "connection_type": "Tipo de conex\u00e3o KNX", @@ -105,8 +154,75 @@ "state_updater": "Defina o padr\u00e3o para estados de leitura do barramento KNX. Quando desativado, o Home Assistant n\u00e3o recuperar\u00e1 ativamente os estados de entidade do barramento KNX. Pode ser substitu\u00eddo pelas op\u00e7\u00f5es de entidade `sync_state`." } }, + "manual_tunnel": { + "data": { + "host": "Host", + "local_ip": "IP local do Home Assistant", + "port": "Porta", + "route_back": "Rota de volta / modo NAT", + "tunneling_type": "Tipo de t\u00fanel KNX" + }, + "data_description": { + "host": "Endere\u00e7o IP do dispositivo de encapsulamento KNX/IP.", + "local_ip": "Deixe em branco para usar a descoberta autom\u00e1tica.", + "port": "Porta do dispositivo de encapsulamento KNX/IP.", + "route_back": "Ative se o servidor de encapsulamento KNXnet/IP estiver atr\u00e1s do NAT. Aplica-se apenas a conex\u00f5es UDP." + }, + "description": "Insira as informa\u00e7\u00f5es de conex\u00e3o do seu dispositivo de encapsulamento." + }, + "options_init": { + "menu_options": { + "communication_settings": "Configura\u00e7\u00f5es de comunica\u00e7\u00e3o", + "connection_type": "Configurar interface KNX" + } + }, + "routing": { + "data": { + "individual_address": "Endere\u00e7o individual", + "local_ip": "IP local do Home Assistant", + "multicast_group": "Grupo multicast", + "multicast_port": "Porta multicast" + }, + "data_description": { + "individual_address": "Endere\u00e7o KNX a ser utilizado pelo Home Assistant, por ex. `0.0.4`", + "local_ip": "Deixe em branco para usar a descoberta autom\u00e1tica." + }, + "description": "Por favor, configure as op\u00e7\u00f5es de roteamento." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "O nome do seu arquivo `.knxkeys` (incluindo extens\u00e3o)", + "knxkeys_password": "A senha para descriptografar o arquivo `.knxkeys`" + }, + "data_description": { + "knxkeys_filename": "Espera-se que o arquivo seja encontrado em seu diret\u00f3rio de configura\u00e7\u00e3o em `.storage/knx/`.\nNo sistema operacional Home Assistant seria `/config/.storage/knx/`\nExemplo: `my_project.knxkeys`", + "knxkeys_password": "Isso foi definido ao exportar o arquivo do ETS." + }, + "description": "Por favor, insira as informa\u00e7\u00f5es para o seu arquivo `.knxkeys`." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Senha de autentica\u00e7\u00e3o do dispositivo", + "user_id": "ID do usu\u00e1rio", + "user_password": "Senha do usu\u00e1rio" + }, + "data_description": { + "device_authentication": "Isso \u00e9 definido no painel 'IP' da interface no ETS.", + "user_id": "Isso geralmente \u00e9 o n\u00famero do t\u00fanel +1. Portanto, 'T\u00fanel 2' teria o ID de usu\u00e1rio '3'.", + "user_password": "Senha para a conex\u00e3o de t\u00fanel espec\u00edfica definida no painel 'Propriedades' do t\u00fanel no ETS." + }, + "description": "Por favor, insira suas informa\u00e7\u00f5es seguras de IP." + }, + "secure_tunneling": { + "description": "Selecione como deseja configurar o KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Use um arquivo `.knxkeys` contendo chaves seguras de IP", + "secure_tunnel_manual": "Configurar chaves seguras de IP manualmente" + } + }, "tunnel": { "data": { + "gateway": "Conex\u00e3o do KNX Tunnel", "host": "Nome do host", "port": "Porta", "tunneling_type": "Tipo de t\u00fanel KNX" @@ -114,7 +230,8 @@ "data_description": { "host": "Endere\u00e7o IP do dispositivo de tunelamento KNX/IP.", "port": "Porta do dispositivo de tunelamento KNX/IP." - } + }, + "description": "Selecione um gateway na lista." } } } diff --git a/homeassistant/components/knx/translations/zh-Hant.json b/homeassistant/components/knx/translations/zh-Hant.json index b348f38701d..90f98a31187 100644 --- a/homeassistant/components/knx/translations/zh-Hant.json +++ b/homeassistant/components/knx/translations/zh-Hant.json @@ -9,20 +9,30 @@ "file_not_found": "\u8def\u5f91 config/.storage/knx/ \u5167\u627e\u4e0d\u5230\u6307\u5b9a `.knxkeys` \u6a94\u6848", "invalid_individual_address": "\u6578\u503c\u8207 KNX \u500b\u5225\u4f4d\u5740\u4e0d\u76f8\u7b26\u3002\n'area.line.device'", "invalid_ip_address": "IPv4 \u4f4d\u5740\u7121\u6548\u3002", - "invalid_signature": "\u52a0\u5bc6 `.knxkeys` \u6a94\u6848\u5bc6\u78bc\u932f\u8aa4\u3002" + "invalid_signature": "\u52a0\u5bc6 `.knxkeys` \u6a94\u6848\u5bc6\u78bc\u932f\u8aa4\u3002", + "no_router_discovered": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230 KNXnet/IP \u8def\u7531\u5668\u3002", + "no_tunnel_discovered": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230 KNX \u901a\u9053\u4f3a\u670d\u5668\u3002" }, "step": { + "connection_type": { + "data": { + "connection_type": "KNX \u9023\u7dda\u985e\u578b" + }, + "description": "\u8acb\u8f38\u5165 KNX \u9023\u7dda\u6240\u4f7f\u7528\u4e4b\u9023\u7dda\u985e\u5225\u3002 \n \u81ea\u52d5\uff08AUTOMATIC\uff09 - \u6574\u5408\u81ea\u52d5\u85c9\u7531\u9598\u9053\u5668\u6383\u63cf\u5f8c\u8655\u7406\u9023\u7dda\u554f\u984c\u3002\n \u901a\u9053\uff08TUNNELING\uff09 - \u6574\u5408\u5c07\u6703\u900f\u904e\u901a\u9053\u65b9\u5f0f\u8207 KNX Bus \u9032\u884c\u9023\u7dda\u3002\n \u8def\u7531\uff08ROUTING\uff09 - \u6574\u5408\u5c07\u6703\u900f\u904e\u8def\u7531\u65b9\u5f0f\u8207 KNX Bus \u9032\u884c\u9023\u7dda\u3002" + }, "manual_tunnel": { "data": { "host": "\u4e3b\u6a5f\u7aef", "local_ip": "Home Assistant \u672c\u5730\u7aef IP", "port": "\u901a\u8a0a\u57e0", + "route_back": "\u8def\u7531\u8fd4\u56de / NAT \u6a21\u5f0f", "tunneling_type": "KNX \u901a\u9053\u985e\u5225" }, "data_description": { "host": "KNX/IP \u901a\u9053\u88dd\u7f6e IP \u4f4d\u5740\u3002", "local_ip": "\u4fdd\u6301\u7a7a\u767d\u4ee5\u4f7f\u7528\u81ea\u52d5\u641c\u7d22\u3002", - "port": "KNX/IP \u901a\u9053\u88dd\u7f6e\u901a\u8a0a\u57e0\u3002" + "port": "KNX/IP \u901a\u9053\u88dd\u7f6e\u901a\u8a0a\u57e0\u3002", + "route_back": "\u5047\u5982 KNXnet/IP \u901a\u9053\u4f3a\u670d\u5668\u4f4d\u65bc NAT \u6642\u555f\u7528\u3001\u50c5\u9069\u7528 UDP \u9023\u7dda\u3002" }, "description": "\u8acb\u8f38\u5165\u901a\u9053\u88dd\u7f6e\u7684\u9023\u7dda\u8cc7\u8a0a\u3002" }, @@ -63,11 +73,25 @@ }, "description": "\u8acb\u8f38\u5165 IP \u52a0\u5bc6\u8cc7\u8a0a\u3002" }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "\u88dd\u7f6e\u8a8d\u8b49\u5bc6\u78bc", + "user_id": "\u4f7f\u7528\u8005 ID", + "user_password": "\u4f7f\u7528\u8005\u5bc6\u78bc" + }, + "data_description": { + "device_authentication": "\u65bc EST \u4ecb\u9762\u4e2d 'IP' \u9762\u677f\u9032\u884c\u8a2d\u5b9a\u3002", + "user_id": "\u901a\u5e38\u70ba\u901a\u9053\u6578 +1\u3002\u56e0\u6b64 'Tunnel 2' \u5c07\u5177\u6709\u4f7f\u7528\u8005 ID '3'\u3002", + "user_password": "\u65bc ETS \u901a\u9053 'Properties' \u9762\u677f\u53ef\u8a2d\u5b9a\u6307\u5b9a\u901a\u9053\u9023\u7dda\u5bc6\u78bc\u3002" + }, + "description": "\u8acb\u8f38\u5165 IP \u52a0\u5bc6\u8cc7\u8a0a\u3002" + }, "secure_tunneling": { "description": "\u9078\u64c7\u5982\u4f55\u8a2d\u5b9a KNX/IP \u52a0\u5bc6\u3002", "menu_options": { "secure_knxkeys": "\u4f7f\u7528\u5305\u542b IP \u52a0\u5bc6\u91d1\u8000\u7684 knxkeys \u6a94\u6848", - "secure_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6\u91d1\u8000" + "secure_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6\u91d1\u8000", + "secure_tunnel_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6\u91d1\u8000" } }, "tunnel": { @@ -85,7 +109,32 @@ } }, "options": { + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "file_not_found": "\u8def\u5f91 config/.storage/knx/ \u5167\u627e\u4e0d\u5230\u6307\u5b9a `.knxkeys` \u6a94\u6848", + "invalid_individual_address": "\u6578\u503c\u8207 KNX \u500b\u5225\u4f4d\u5740\u4e0d\u76f8\u7b26\u3002\n'area.line.device'", + "invalid_ip_address": "IPv4 \u4f4d\u5740\u7121\u6548\u3002", + "invalid_signature": "\u52a0\u5bc6 `.knxkeys` \u6a94\u6848\u5bc6\u78bc\u932f\u8aa4\u3002", + "no_router_discovered": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230 KNXnet/IP \u8def\u7531\u5668\u3002", + "no_tunnel_discovered": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230 KNX \u901a\u9053\u4f3a\u670d\u5668\u3002" + }, "step": { + "communication_settings": { + "data": { + "rate_limit": "\u983b\u7387\u9650\u5236", + "state_updater": "\u72c0\u614b\u66f4\u65b0\u5668" + }, + "data_description": { + "rate_limit": "\u6bcf\u79d2\u6700\u5927 Telegram \u767c\u9001\u91cf\u3002\n`0` \u70ba\u95dc\u9589\u9650\u5236\u3001\u5efa\u8b70\uff1a0 \u6216 20 \u81f3 40", + "state_updater": "\u8a2d\u5b9a\u9810\u8a2d KNX Bus \u8b80\u53d6\u72c0\u614b\u3002\u7576\u95dc\u9589\u6642\u3001Home Assistant \u5c07\u4e0d\u6703\u4e3b\u52d5\u5f9e KNX Bus \u7372\u53d6\u5be6\u9ad4\u72c0\u614b\uff0c\u53ef\u88ab`sync_state` \u5be6\u9ad4\u9078\u9805\u8986\u84cb\u3002" + } + }, + "connection_type": { + "data": { + "connection_type": "KNX \u9023\u7dda\u985e\u578b" + }, + "description": "\u8acb\u8f38\u5165 KNX \u9023\u7dda\u6240\u4f7f\u7528\u4e4b\u9023\u7dda\u985e\u5225\u3002 \n \u81ea\u52d5\uff08AUTOMATIC\uff09 - \u6574\u5408\u81ea\u52d5\u85c9\u7531\u9598\u9053\u5668\u6383\u63cf\u5f8c\u8655\u7406\u9023\u7dda\u554f\u984c\u3002\n \u901a\u9053\uff08TUNNELING\uff09 - \u6574\u5408\u5c07\u6703\u900f\u904e\u901a\u9053\u65b9\u5f0f\u8207 KNX Bus \u9032\u884c\u9023\u7dda\u3002\n \u8def\u7531\uff08ROUTING\uff09 - \u6574\u5408\u5c07\u6703\u900f\u904e\u8def\u7531\u65b9\u5f0f\u8207 KNX Bus \u9032\u884c\u9023\u7dda\u3002" + }, "init": { "data": { "connection_type": "KNX \u9023\u7dda\u985e\u5225", @@ -94,7 +143,7 @@ "multicast_group": "Multicast \u7fa4\u7d44", "multicast_port": "Multicast \u901a\u8a0a\u57e0", "rate_limit": "\u983b\u7387\u9650\u5236", - "state_updater": "\u88dd\u614b\u66f4\u65b0\u5668" + "state_updater": "\u72c0\u614b\u66f4\u65b0\u5668" }, "data_description": { "individual_address": "Home Assistant \u6240\u4f7f\u7528\u4e4b KNX \u4f4d\u5740\u3002\u4f8b\u5982\uff1a`0.0.4`", @@ -105,8 +154,75 @@ "state_updater": "\u8a2d\u5b9a\u9810\u8a2d KNX Bus \u8b80\u53d6\u72c0\u614b\u3002\u7576\u95dc\u9589\u6642\u3001Home Assistant \u5c07\u4e0d\u6703\u4e3b\u52d5\u5f9e KNX Bus \u7372\u53d6\u5be6\u9ad4\u72c0\u614b\uff0c\u53ef\u88ab`sync_state` \u5be6\u9ad4\u9078\u9805\u8986\u84cb\u3002" } }, + "manual_tunnel": { + "data": { + "host": "\u4e3b\u6a5f\u7aef", + "local_ip": "Home Assistant \u672c\u5730\u7aef IP", + "port": "\u901a\u8a0a\u57e0", + "route_back": "\u8def\u7531\u8fd4\u56de / NAT \u6a21\u5f0f", + "tunneling_type": "KNX \u901a\u9053\u985e\u5225" + }, + "data_description": { + "host": "KNX/IP \u901a\u9053\u88dd\u7f6e IP \u4f4d\u5740\u3002", + "local_ip": "\u4fdd\u6301\u7a7a\u767d\u4ee5\u4f7f\u7528\u81ea\u52d5\u641c\u7d22\u3002", + "port": "KNX/IP \u901a\u9053\u88dd\u7f6e\u901a\u8a0a\u57e0\u3002", + "route_back": "\u5047\u5982 KNXnet/IP \u901a\u9053\u4f3a\u670d\u5668\u4f4d\u65bc NAT \u6642\u555f\u7528\u3001\u50c5\u9069\u7528 UDP \u9023\u7dda\u3002" + }, + "description": "\u8acb\u8f38\u5165\u901a\u9053\u88dd\u7f6e\u7684\u9023\u7dda\u8cc7\u8a0a\u3002" + }, + "options_init": { + "menu_options": { + "communication_settings": "\u901a\u8a0a\u8a2d\u5b9a", + "connection_type": "\u8a2d\u5b9a KNX \u4ecb\u9762" + } + }, + "routing": { + "data": { + "individual_address": "\u500b\u5225\u4f4d\u5740", + "local_ip": "Home Assistant \u672c\u5730\u7aef IP", + "multicast_group": "Multicast \u7fa4\u7d44", + "multicast_port": "Multicast \u901a\u8a0a\u57e0" + }, + "data_description": { + "individual_address": "Home Assistant \u6240\u4f7f\u7528\u4e4b KNX \u4f4d\u5740\u3002\u4f8b\u5982\uff1a`0.0.4`", + "local_ip": "\u4fdd\u6301\u7a7a\u767d\u4ee5\u4f7f\u7528\u81ea\u52d5\u641c\u7d22\u3002" + }, + "description": "\u8acb\u8a2d\u5b9a\u8def\u7531\u9078\u9805\u3002" + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "`.knxkeys` \u6a94\u6848\u5168\u540d\uff08\u5305\u542b\u526f\u6a94\u540d\uff09", + "knxkeys_password": "\u52a0\u5bc6 `.knxkeys` \u6a94\u6848\u5bc6\u78bc" + }, + "data_description": { + "knxkeys_filename": "\u6a94\u6848\u61c9\u8a72\u4f4d\u65bc\u8a2d\u5b9a\u8cc7\u6599\u593e `.storage/knx/` \u5167\u3002\n\u82e5\u70ba Home Assistant OS\u3001\u5247\u61c9\u8a72\u70ba `/config/.storage/knx/`\n\u4f8b\u5982\uff1a`my_project.knxkeys`", + "knxkeys_password": "\u81ea ETS \u532f\u51fa\u6a94\u6848\u4e2d\u9032\u884c\u8a2d\u5b9a\u3002" + }, + "description": "\u8acb\u8f38\u5165 `.knxkeys` \u6a94\u6848\u8cc7\u8a0a\u3002" + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "\u88dd\u7f6e\u8a8d\u8b49\u5bc6\u78bc", + "user_id": "\u4f7f\u7528\u8005 ID", + "user_password": "\u4f7f\u7528\u8005\u5bc6\u78bc" + }, + "data_description": { + "device_authentication": "\u65bc EST \u4ecb\u9762\u4e2d 'IP' \u9762\u677f\u9032\u884c\u8a2d\u5b9a\u3002", + "user_id": "\u901a\u5e38\u70ba\u901a\u9053\u6578 +1\u3002\u56e0\u6b64 'Tunnel 2' \u5c07\u5177\u6709\u4f7f\u7528\u8005 ID '3'\u3002", + "user_password": "\u65bc ETS \u901a\u9053 'Properties' \u9762\u677f\u53ef\u8a2d\u5b9a\u6307\u5b9a\u901a\u9053\u9023\u7dda\u5bc6\u78bc\u3002" + }, + "description": "\u8acb\u8f38\u5165 IP \u52a0\u5bc6\u8cc7\u8a0a\u3002" + }, + "secure_tunneling": { + "description": "\u9078\u64c7\u5982\u4f55\u8a2d\u5b9a KNX/IP \u52a0\u5bc6\u3002", + "menu_options": { + "secure_knxkeys": "\u4f7f\u7528\u5305\u542b IP \u52a0\u5bc6\u91d1\u8000\u7684 knxkeys \u6a94\u6848", + "secure_tunnel_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6\u91d1\u8000" + } + }, "tunnel": { "data": { + "gateway": "KNX \u901a\u9053\u9023\u7dda", "host": "\u4e3b\u6a5f\u7aef", "port": "\u901a\u8a0a\u57e0", "tunneling_type": "KNX \u901a\u9053\u985e\u5225" @@ -114,7 +230,8 @@ "data_description": { "host": "KNX/IP \u901a\u9053\u88dd\u7f6e IP \u4f4d\u5740\u3002", "port": "KNX/IP \u901a\u9053\u88dd\u7f6e\u901a\u8a0a\u57e0\u3002" - } + }, + "description": "\u8acb\u5f9e\u5217\u8868\u4e2d\u9078\u64c7\u4e00\u7d44\u9598\u9053\u5668\u3002" } } } diff --git a/homeassistant/components/nibe_heatpump/translations/ca.json b/homeassistant/components/nibe_heatpump/translations/ca.json index 7dcf70cc17d..de5814b4f38 100644 --- a/homeassistant/components/nibe_heatpump/translations/ca.json +++ b/homeassistant/components/nibe_heatpump/translations/ca.json @@ -18,6 +18,22 @@ "model": "Model de bomba de calor" } }, + "nibegw": { + "data": { + "ip_address": "Adre\u00e7a remota", + "listening_port": "Port local d'escolta", + "model": "Model de bomba de calor", + "remote_read_port": "Port remot de lectura", + "remote_write_port": "Port remot d'escriptura" + }, + "data_description": { + "ip_address": "Adre\u00e7a de la unitat NibeGW. El dispositiu hauria d'estar configurat amb una adre\u00e7a est\u00e0tica.", + "listening_port": "Port local d'aquest sistema al qual la unitat NibeGW est\u00e0 configurada per enviar-hi dades.", + "remote_read_port": "Port on la unitat NibeGW espera les sol\u00b7licituds de lectura.", + "remote_write_port": "Port on la unitat NibeGW espera les sol\u00b7licituds d'escriptura." + }, + "description": "Abans d'intentar configurar la integraci\u00f3, comprova que:\n - La unitat NibeGW est\u00e0 connectada a una bomba de calor.\n - S'ha activat l'accessori MODBUS40 a la configuraci\u00f3 de la bomba de calor.\n - La bomba no ha entrat en estat d'alarma per falta de l'accessori MODBUS40." + }, "user": { "data": { "ip_address": "Adre\u00e7a remota", diff --git a/homeassistant/components/nibe_heatpump/translations/de.json b/homeassistant/components/nibe_heatpump/translations/de.json index e5f98b60bdd..4e1fc0ce17b 100644 --- a/homeassistant/components/nibe_heatpump/translations/de.json +++ b/homeassistant/components/nibe_heatpump/translations/de.json @@ -6,11 +6,11 @@ "error": { "address": "Ung\u00fcltige Remote-Adresse angegeben. Die Adresse muss eine IP-Adresse oder ein aufl\u00f6sbarer Hostname sein.", "address_in_use": "Der ausgew\u00e4hlte Listening-Port wird auf diesem System bereits verwendet.", - "model": "Das ausgew\u00e4hlte Modell scheint modbus40 nicht zu unterst\u00fctzen", - "read": "Fehler bei Leseanforderung von Pumpe. \u00dcberpr\u00fcfe deinen \u201eRemote-Leseport\u201c oder \u201eRemote-IP-Adresse\u201c.", + "model": "Das ausgew\u00e4hlte Modell scheint MODBUS40 nicht zu unterst\u00fctzen", + "read": "Fehler bei Leseanforderung von Pumpe. \u00dcberpr\u00fcfe deinen \u201eRemote-Leseport\u201c oder \u201eRemote-Adresse\u201c.", "unknown": "Unerwarteter Fehler", - "url": "Die angegebene URL ist keine wohlgeformte und unterst\u00fctzte URL", - "write": "Fehler bei Schreibanforderung an Pumpe. \u00dcberpr\u00fcfe deinen \u201eRemote-Schreibport\u201c oder \u201eRemote-IP-Adresse\u201c." + "url": "Die angegebene URL ist weder wohlgeformt noch wird sie unterst\u00fctzt.", + "write": "Fehler bei Schreibanforderung an Pumpe. \u00dcberpr\u00fcfe deinen \u201eRemote-Schreibport\u201c oder \u201eRemote-Adresse\u201c." }, "step": { "modbus": { @@ -53,7 +53,7 @@ "remote_read_port": "Der Port, an dem das NibeGW-Ger\u00e4t auf Leseanfragen wartet.", "remote_write_port": "Der Port, an dem das NibeGW-Ger\u00e4t auf Schreibanfragen wartet." }, - "description": "W\u00e4hle die Verbindungsmethode zu deiner Pumpe. Im Allgemeinen erfordern Pumpen der F-Serie ein kundenspezifisches Nibe GW-Zubeh\u00f6r, w\u00e4hrend eine Pumpe der S-Serie \u00fcber eine integrierte Modbus-Unterst\u00fctzung verf\u00fcgt.", + "description": "W\u00e4hle die Verbindungsmethode zu deiner Pumpe. Im Allgemeinen erfordern Pumpen der F-Serie ein kundenspezifisches NibeGW-Zubeh\u00f6r, w\u00e4hrend eine Pumpe der S-Serie \u00fcber eine integrierte Modbus-Unterst\u00fctzung verf\u00fcgt.", "menu_options": { "modbus": "Modbus", "nibegw": "NibeGW" diff --git a/homeassistant/components/nibe_heatpump/translations/en.json b/homeassistant/components/nibe_heatpump/translations/en.json index afe50efb61a..3b50bb0986a 100644 --- a/homeassistant/components/nibe_heatpump/translations/en.json +++ b/homeassistant/components/nibe_heatpump/translations/en.json @@ -61,4 +61,4 @@ } } } -} +} \ No newline at end of file diff --git a/homeassistant/components/nibe_heatpump/translations/es.json b/homeassistant/components/nibe_heatpump/translations/es.json index 0365e60adea..c241724f14a 100644 --- a/homeassistant/components/nibe_heatpump/translations/es.json +++ b/homeassistant/components/nibe_heatpump/translations/es.json @@ -6,11 +6,11 @@ "error": { "address": "Se especific\u00f3 una direcci\u00f3n remota no v\u00e1lida. La direcci\u00f3n debe ser una direcci\u00f3n IP o un nombre de host resoluble.", "address_in_use": "El puerto de escucha seleccionado ya est\u00e1 en uso en este sistema.", - "model": "El modelo seleccionado no parece ser compatible con modbus40", - "read": "Error en la solicitud de lectura de la bomba. Verifica tu `Puerto de lectura remoto` o `Direcci\u00f3n IP remota`.", + "model": "El modelo seleccionado no parece ser compatible con MODBUS40", + "read": "Error en la solicitud de lectura de la bomba. Verifica tu `Puerto de lectura remoto` o `Direcci\u00f3n remota`.", "unknown": "Error inesperado", - "url": "La URL especificada no es una URL bien formada y compatible", - "write": "Error en la solicitud de escritura a la bomba. Verifica tu `Puerto de escritura remoto` o `Direcci\u00f3n IP remota`." + "url": "La URL especificada no est\u00e1 bien formada ni es compatible", + "write": "Error en la solicitud de escritura a la bomba. Verifica tu `Puerto de escritura remoto` o `Direcci\u00f3n remota`." }, "step": { "modbus": { @@ -20,7 +20,7 @@ "model": "Modelo de bomba de calor" }, "data_description": { - "modbus_unit": "Identificaci\u00f3n de la unidad para su bomba de calor. Por lo general, se puede dejar en 0.", + "modbus_unit": "Identificaci\u00f3n de la unidad para tu bomba de calor. Por lo general, se puede dejar en 0.", "modbus_url": "URL Modbus que describe la conexi\u00f3n a tu bomba de calor o unidad MODBUS40. Debe estar en el formato:\n - `tcp://[HOST]:[PUERTO]` para conexi\u00f3n Modbus TCP\n - `serial://[DISPOSITIVO LOCAL]` para una conexi\u00f3n Modbus RTU local\n - `rfc2217://[HOST]:[PUERTO]` para una conexi\u00f3n remota Modbus RTU basada en telnet." } }, @@ -53,7 +53,7 @@ "remote_read_port": "El puerto en el que la unidad NibeGW est\u00e1 escuchando las peticiones de lectura.", "remote_write_port": "El puerto en el que la unidad NibeGW est\u00e1 escuchando peticiones de escritura." }, - "description": "Elige el m\u00e9todo de conexi\u00f3n a tu bomba. En general, las bombas de la serie F requieren un accesorio personalizado Nibe GW, mientras que una bomba de la serie S tiene soporte Modbus incorporado.", + "description": "Elige el m\u00e9todo de conexi\u00f3n a tu bomba. En general, las bombas de la serie F requieren un accesorio personalizado NibeGW, mientras que una bomba de la serie S tiene soporte Modbus incorporado.", "menu_options": { "modbus": "Modbus", "nibegw": "NibeGW" diff --git a/homeassistant/components/nibe_heatpump/translations/et.json b/homeassistant/components/nibe_heatpump/translations/et.json index 8fff7663bb0..56ffec08aec 100644 --- a/homeassistant/components/nibe_heatpump/translations/et.json +++ b/homeassistant/components/nibe_heatpump/translations/et.json @@ -6,10 +6,10 @@ "error": { "address": "M\u00e4\u00e4ratud vale kaugaadress. Aadress peab olema IP-aadress v\u00f5i lahendatav hostinimi.", "address_in_use": "Valitud kuulamisport on selles s\u00fcsteemis juba kasutusel.", - "model": "Valitud mudel ei n\u00e4i toetavat modbus40.", - "read": "Viga pumba lugemistaotlusel. Kinnitage oma \"Kaugloetav port\" v\u00f5i \"Kaug-IP-aadress\".", + "model": "Valitud mudel ei n\u00e4i toetavat MODBUS40", + "read": "Viga pumba lugemistaotlusel. Kinnita oma \"Kaugloetav port\" v\u00f5i \"Kaug-IP-aadress\".", "unknown": "Ootamatu t\u00f5rge", - "url": "M\u00e4\u00e4ratud URL ei ole h\u00e4sti vormindatud ja toetatud URL", + "url": "M\u00e4\u00e4ratud URL ei ole h\u00e4sti vormindatud ja toetatud", "write": "Viga pumba kirjutamise taotlusel. Kontrollige oma `kaugkirjutusport` v\u00f5i `kaug-IP-aadress`." }, "step": { diff --git a/homeassistant/components/nibe_heatpump/translations/id.json b/homeassistant/components/nibe_heatpump/translations/id.json index 44e0a8c0247..7a5657508fc 100644 --- a/homeassistant/components/nibe_heatpump/translations/id.json +++ b/homeassistant/components/nibe_heatpump/translations/id.json @@ -6,10 +6,11 @@ "error": { "address": "Alamat IP jarak jauh yang ditentukan tidak valid. Alamat harus berupa alamat IP atau nama host yang dapat ditemukan.", "address_in_use": "Port mendengarkan yang dipilih sudah digunakan pada sistem ini.", - "model": "Model yang dipilih tampaknya tidak mendukung modbus40", - "read": "Kesalahan pada permintaan baca dari pompa. Verifikasi `Port baca jarak jauh` atau `Alamat IP jarak jauh` Anda.", + "model": "Model yang dipilih tampaknya tidak mendukung MODBUS40", + "read": "Kesalahan pada permintaan baca dari pompa. Verifikasi `Port baca jarak jauh` atau `Alamat jarak jauh` Anda.", "unknown": "Kesalahan yang tidak diharapkan", - "write": "Kesalahan pada permintaan tulis ke pompa. Verifikasi `Port tulis jarak jauh` atau `Alamat IP jarak jauh` Anda." + "url": "URL yang ditentukan tidak dalam format yang benar atau tidak didukung", + "write": "Kesalahan pada permintaan tulis ke pompa. Verifikasi `Port tulis jarak jauh` atau `Alamat jarak jauh` Anda." }, "step": { "modbus": { @@ -17,6 +18,10 @@ "modbus_unit": "Pengidentifikasi Unit Modbus", "modbus_url": "URL Modbus", "model": "Model Pompa Panas" + }, + "data_description": { + "modbus_unit": "Identifikasi unit untuk Pompa Panas Anda. Biasanya dapat dibiarkan pada nilai 0.", + "modbus_url": "URL Modbus yang menjelaskan koneksi ke unit Heat Pump atau MODBUS40 Anda. Ini harus dalam format:\n - `tcp://[HOST]:[PORT]` untuk koneksi Modbus TCP\n - `serial://[LOCAL DEVICE]` untuk koneksi Modbus RTU lokal\n - `rfc2217://[HOST]:[PORT]` untuk koneksi Modbus RTU berbasis telnet jarak jauh." } }, "nibegw": { @@ -29,7 +34,9 @@ }, "data_description": { "ip_address": "Alamat unit NibeGW. Perangkat seharusnya sudah dikonfigurasi dengan alamat statis.", - "listening_port": "Port lokal pada sistem ini, yang dikonfigurasi untuk mengirim data ke unit NibeGW." + "listening_port": "Port lokal pada sistem ini, yang dikonfigurasi untuk mengirim data ke unit NibeGW.", + "remote_read_port": "Port yang digunakan unit NibeGW untuk mendengarkan permintaan baca.", + "remote_write_port": "Port yang digunakan unit NibeGW untuk mendengarkan permintaan tulis." }, "description": "Sebelum mencoba mengonfigurasi integrasi, pastikan bahwa:\n - Unit NibeGW terhubung ke pompa panas.\n - Aksesori MODBUS40 telah diaktifkan dalam konfigurasi pompa panas.\n - Pompa tidak sedang dalam status alarm tentang aksesori MODBUS40 yang tidak tersedia." }, @@ -46,7 +53,7 @@ "remote_read_port": "Port yang digunakan unit NibeGW untuk mendengarkan permintaan baca.", "remote_write_port": "Port yang digunakan unit NibeGW untuk mendengarkan permintaan tulis." }, - "description": "Pilih metode koneksi ke pompa. Secara umum, pompa seri F memerlukan aksesori khusus Nibe GW, sementara pompa seri S memiliki dukungan Modbus bawaan.", + "description": "Pilih metode koneksi ke pompa. Secara umum, pompa seri F memerlukan aksesori khusus NibeGW, sementara pompa seri S memiliki dukungan Modbus bawaan.", "menu_options": { "modbus": "Modbus", "nibegw": "NibeGW" diff --git a/homeassistant/components/nibe_heatpump/translations/it.json b/homeassistant/components/nibe_heatpump/translations/it.json index 81da8c2503d..96b686f9818 100644 --- a/homeassistant/components/nibe_heatpump/translations/it.json +++ b/homeassistant/components/nibe_heatpump/translations/it.json @@ -6,11 +6,11 @@ "error": { "address": "Indirizzo remoto specificato non valido. L'indirizzo deve essere un indirizzo IP o un nome host risolvibile.", "address_in_use": "La porta di ascolto selezionata \u00e8 gi\u00e0 in uso su questo sistema.", - "model": "Il modello selezionato non sembra supportare il modbus40", - "read": "Errore su richiesta di lettura dalla pompa. Verifica la tua \"Porta di lettura remota\" o \"Indirizzo IP remoto\".", + "model": "Il modello selezionato non sembra supportare il MODBUS40.", + "read": "Errore nella richiesta di lettura da parte della pompa. Verifica la tua \"Porta di lettura remota\" o \"Indirizzo remoto\".", "unknown": "Errore imprevisto", - "url": "L'URL specificato non \u00e8 un URL ben formato e supportato", - "write": "Errore nella richiesta di scrittura alla pompa. Verifica la tua \"Porta di scrittura remota\" o \"Indirizzo IP remoto\"." + "url": "L'URL specificato non \u00e8 correttamente formato n\u00e9 supportato", + "write": "Errore nella richiesta di scrittura alla pompa. Verifica la tua \"Porta di scrittura remota\" o \"Indirizzo remoto\"." }, "step": { "modbus": { @@ -20,7 +20,7 @@ "model": "Modello di pompa di calore" }, "data_description": { - "modbus_unit": "Identificazione dell'unit\u00e0 per la tua pompa di calore. Di solito pu\u00f2 essere lasciato a 0.", + "modbus_unit": "Identificazione dell'unit\u00e0 per la pompa di calore. Di solito pu\u00f2 essere lasciato a 0.", "modbus_url": "Modbus URL che descrive la connessione alla pompa di calore o all'unit\u00e0 MODBUS40. Dovrebbe essere nella forma:\n - `tcp://[HOST]:[PORTA]` per la connessione Modbus TCP\n - `serial://[DISPOSITIVO LOCALE]` per una connessione Modbus RTU locale\n - `rfc2217://[HOST]:[PORTA]` per una connessione Modbus RTU remota basata su telnet." } }, @@ -53,7 +53,7 @@ "remote_read_port": "La porta su cui l'unit\u00e0 NibeGW \u00e8 in ascolto per le richieste di lettura.", "remote_write_port": "La porta su cui l'unit\u00e0 NibeGW \u00e8 in ascolto per le richieste di scrittura." }, - "description": "Scegli il metodo di connessione alla tua pompa. In generale, le pompe della serie F richiedono un accessorio personalizzato Nibe GW, mentre le pompe della serie S hanno il supporto Modbus integrato.", + "description": "Scegli il metodo di connessione alla tua pompa. In generale, le pompe della serie F richiedono un accessorio personalizzato NibeGW, mentre una pompa della serie S ha il supporto Modbus integrato.", "menu_options": { "modbus": "Modbus", "nibegw": "NibeGW" diff --git a/homeassistant/components/nibe_heatpump/translations/pl.json b/homeassistant/components/nibe_heatpump/translations/pl.json index a6cc0785e97..e97517d2a22 100644 --- a/homeassistant/components/nibe_heatpump/translations/pl.json +++ b/homeassistant/components/nibe_heatpump/translations/pl.json @@ -6,10 +6,10 @@ "error": { "address": "Podano nieprawid\u0142owy zdalny adres IP. Adres musi by\u0107 adresem IP lub rozpoznawaln\u0105 nazw\u0105 hosta.", "address_in_use": "Wybrany port nas\u0142uchiwania jest ju\u017c u\u017cywany w tym systemie.", - "model": "Wybrany model nie obs\u0142uguje modbus40", + "model": "Wybrany model nie obs\u0142uguje MODBUS40", "read": "B\u0142\u0105d przy \u017c\u0105daniu odczytu z pompy. Sprawd\u017a \u201eZdalny port odczytu\u201d lub \u201eZdalny adres IP\u201d.", "unknown": "Nieoczekiwany b\u0142\u0105d", - "url": "Podany adres URL nie jest poprawnie sformu\u0142owanym i obs\u0142ugiwanym adresem URL", + "url": "Podany adres URL nie jest poprawnie sformu\u0142owany lub obs\u0142ugiwany", "write": "B\u0142\u0105d przy \u017c\u0105daniu zapisu do pompy. Sprawd\u017a \u201eZdalny port zapisu\u201d lub \u201eZdalny adres IP\u201d." }, "step": { @@ -53,9 +53,9 @@ "remote_read_port": "Port, na kt\u00f3rym urz\u0105dzenie NibeGW nas\u0142uchuje \u017c\u0105da\u0144 odczytu.", "remote_write_port": "Port, na kt\u00f3rym urz\u0105dzenie NibeGW nas\u0142uchuje \u017c\u0105da\u0144 zapisu." }, - "description": "Wybierz metod\u0119 po\u0142\u0105czenia z pomp\u0105. Og\u00f3lnie rzecz bior\u0105c, pompy serii F wymagaj\u0105 niestandardowego akcesorium Nibe GW, podczas gdy pompy serii S maj\u0105 wbudowan\u0105 obs\u0142ug\u0119 protoko\u0142u Modbus.", + "description": "Wybierz metod\u0119 po\u0142\u0105czenia z pomp\u0105. Og\u00f3lnie rzecz bior\u0105c, pompy serii F wymagaj\u0105 niestandardowego akcesorium NibeGW, podczas gdy pompy serii S maj\u0105 wbudowan\u0105 obs\u0142ug\u0119 protoko\u0142u Modbus.", "menu_options": { - "modbus": "Modbus", + "modbus": "MODBUS", "nibegw": "NibeGW" } } diff --git a/homeassistant/components/nibe_heatpump/translations/pt-BR.json b/homeassistant/components/nibe_heatpump/translations/pt-BR.json index 1ae021ee11b..6059385b861 100644 --- a/homeassistant/components/nibe_heatpump/translations/pt-BR.json +++ b/homeassistant/components/nibe_heatpump/translations/pt-BR.json @@ -6,11 +6,11 @@ "error": { "address": "Endere\u00e7o remoto inv\u00e1lido especificado. O endere\u00e7o deve ser um endere\u00e7o IP ou um nome de host resolv\u00edvel.", "address_in_use": "A porta de escuta selecionada j\u00e1 est\u00e1 em uso neste sistema.", - "model": "O modelo selecionado parece n\u00e3o suportar modbus40", - "read": "Erro na solicita\u00e7\u00e3o de leitura da bomba. Verifique sua `Porta de leitura remota` ou `Endere\u00e7o IP remoto`.", + "model": "O modelo selecionado parece n\u00e3o suportar MODBUS40", + "read": "Erro na solicita\u00e7\u00e3o de leitura da bomba. Verifique sua `Porta de leitura remota` ou `Endere\u00e7o remoto`.", "unknown": "Erro inesperado", "url": "A URL especificada n\u00e3o \u00e9 uma URL bem formada e suportada", - "write": "Erro na solicita\u00e7\u00e3o de grava\u00e7\u00e3o para bombear. Verifique sua `Porta de grava\u00e7\u00e3o remota` ou `Endere\u00e7o IP remoto`." + "write": "Erro na solicita\u00e7\u00e3o de grava\u00e7\u00e3o para bombear. Verifique sua `Porta de grava\u00e7\u00e3o remota` ou `Endere\u00e7o remoto`." }, "step": { "modbus": { @@ -53,7 +53,7 @@ "remote_read_port": "A porta na qual a unidade NibeGW est\u00e1 escutando solicita\u00e7\u00f5es de leitura.", "remote_write_port": "A porta na qual a unidade NibeGW est\u00e1 escutando solicita\u00e7\u00f5es de grava\u00e7\u00e3o." }, - "description": "Escolha o m\u00e9todo de conex\u00e3o para sua bomba. Em geral, as bombas da s\u00e9rie F requerem um acess\u00f3rio personalizado Nibe GW, enquanto uma bomba da s\u00e9rie S possui suporte Modbus integrado.", + "description": "Escolha o m\u00e9todo de conex\u00e3o para sua bomba. Em geral, as bombas da s\u00e9rie F requerem um acess\u00f3rio personalizado NibeGW, enquanto uma bomba da s\u00e9rie S tem suporte Modbus integrado.", "menu_options": { "modbus": "Modbus", "nibegw": "NibeGW" diff --git a/homeassistant/components/nibe_heatpump/translations/ru.json b/homeassistant/components/nibe_heatpump/translations/ru.json index 42c7a853c94..de67991f5af 100644 --- a/homeassistant/components/nibe_heatpump/translations/ru.json +++ b/homeassistant/components/nibe_heatpump/translations/ru.json @@ -6,11 +6,11 @@ "error": { "address": "\u0423\u043a\u0430\u0437\u0430\u043d \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441. \u0421\u043b\u0435\u0434\u0443\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430.", "address_in_use": "\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u044d\u0442\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435.", - "model": "\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 modbus40.", - "read": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b `\u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0447\u0442\u0435\u043d\u0438\u044f` \u0438\u043b\u0438 `\u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441`.", + "model": "\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 MODBUS40.", + "read": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b `\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0447\u0442\u0435\u043d\u0438\u044f` \u0438\u043b\u0438 `\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441`.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430.", "url": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0439 URL-\u0430\u0434\u0440\u0435\u0441.", - "write": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b `\u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0437\u0430\u043f\u0438\u0441\u0438` \u0438\u043b\u0438 `\u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441`." + "write": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b `\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0437\u0430\u043f\u0438\u0441\u0438` \u0438\u043b\u0438 `\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441`." }, "step": { "modbus": { @@ -53,7 +53,7 @@ "remote_read_port": "\u041f\u043e\u0440\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435.", "remote_write_port": "\u041f\u043e\u0440\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c." }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0412\u0430\u0448\u0435\u043c\u0443 \u043d\u0430\u0441\u043e\u0441\u0443. \u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0434\u043b\u044f \u043d\u0430\u0441\u043e\u0441\u043e\u0432 \u0441\u0435\u0440\u0438\u0438 F \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440 Nibe GW, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u043d\u0430\u0441\u043e\u0441\u044b \u0441\u0435\u0440\u0438\u0438 S \u0438\u043c\u0435\u044e\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 Modbus.", + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0412\u0430\u0448\u0435\u043c\u0443 \u043d\u0430\u0441\u043e\u0441\u0443. \u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0434\u043b\u044f \u043d\u0430\u0441\u043e\u0441\u043e\u0432 \u0441\u0435\u0440\u0438\u0438 F \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440 NibeGW, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u043d\u0430\u0441\u043e\u0441\u044b \u0441\u0435\u0440\u0438\u0438 S \u0438\u043c\u0435\u044e\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 Modbus.", "menu_options": { "modbus": "Modbus", "nibegw": "NibeGW" diff --git a/homeassistant/components/nibe_heatpump/translations/zh-Hant.json b/homeassistant/components/nibe_heatpump/translations/zh-Hant.json index fe182397efa..ff9cb6fe24f 100644 --- a/homeassistant/components/nibe_heatpump/translations/zh-Hant.json +++ b/homeassistant/components/nibe_heatpump/translations/zh-Hant.json @@ -6,11 +6,11 @@ "error": { "address": "\u6307\u5b9a\u7684\u9060\u7aef\u4f4d\u5740\u7121\u6548\u3002\u4f4d\u5740\u5fc5\u9808\u70ba IP \u4f4d\u5740\u6216\u53ef\u89e3\u6790\u7684\u4e3b\u6a5f\u540d\u7a31\u3002", "address_in_use": "\u6240\u9078\u64c7\u7684\u76e3\u807d\u901a\u8a0a\u57e0\u5df2\u7d93\u88ab\u7cfb\u7d71\u6240\u4f7f\u7528\u3002", - "model": "\u6240\u9078\u64c7\u7684\u578b\u865f\u4f3c\u4e4e\u4e0d\u652f\u63f4 modbus40", - "read": "\u8b80\u53d6\u8acb\u6c42\u767c\u751f\u932f\u8aa4\uff0c\u8acb\u78ba\u8a8d `\u9060\u7aef\u8b80\u53d6\u57e0` \u6216 `\u9060\u7aef IP \u4f4d\u5740`\u3002", + "model": "\u6240\u9078\u64c7\u7684\u578b\u865f\u4f3c\u4e4e\u4e0d\u652f\u63f4 MODBUS40", + "read": "\u8b80\u53d6\u8acb\u6c42\u767c\u751f\u932f\u8aa4\uff0c\u8acb\u78ba\u8a8d `\u9060\u7aef\u8b80\u53d6\u57e0` \u6216 `\u9060\u7aef\u4f4d\u5740`\u3002", "unknown": "\u672a\u9810\u671f\u932f\u8aa4", - "url": "\u6307\u5b9a\u7684 URL \u4e0d\u662f\u6b63\u78ba\u683c\u5f0f\u6216\u652f\u63f4\u7684URL", - "write": "\u5beb\u5165\u8acb\u6c42\u767c\u751f\u932f\u8aa4\uff0c\u8acb\u78ba\u8a8d `\u9060\u7aef\u5beb\u5165\u57e0` \u6216 `\u9060\u7aef IP \u4f4d\u5740`\u3002" + "url": "\u6307\u5b9a\u7684 URL \u4e0d\u662f\u6b63\u78ba\u683c\u5f0f\u6216\u652f\u63f4\u7684 URL", + "write": "\u5beb\u5165\u8acb\u6c42\u767c\u751f\u932f\u8aa4\uff0c\u8acb\u78ba\u8a8d `\u9060\u7aef\u5beb\u5165\u57e0` \u6216 `\u9060\u7aef\u4f4d\u5740`\u3002" }, "step": { "modbus": { diff --git a/homeassistant/components/openuv/translations/bg.json b/homeassistant/components/openuv/translations/bg.json index 06c8d9cde09..64e6e35e737 100644 --- a/homeassistant/components/openuv/translations/bg.json +++ b/homeassistant/components/openuv/translations/bg.json @@ -12,6 +12,7 @@ "data": { "api_key": "API \u043a\u043b\u044e\u0447" }, + "description": "\u041c\u043e\u043b\u044f, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e API \u043a\u043b\u044e\u0447\u0430 \u0437\u0430 {latitude}, {longitude}.", "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" }, "user": { From 777bf27dda6a0a4d52db8205c5899532c3bf1217 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Nov 2022 01:51:58 -0600 Subject: [PATCH 0358/1033] Bump bleak-retry-connector to 2.8.4 (#81937) changelog: https://github.com/Bluetooth-Devices/bleak-retry-connector/compare/v2.8.3...v2.8.4 reduces the risk that the operation will fail because the adapter temporarily runs out of connection slots --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index cc7237d4585..59397f30bf9 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.2", - "bleak-retry-connector==2.8.3", + "bleak-retry-connector==2.8.4", "bluetooth-adapters==0.7.0", "bluetooth-auto-recovery==0.3.6", "bluetooth-data-tools==0.2.0", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 66ffb1eb85b..daeeda866a8 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.8.3 +bleak-retry-connector==2.8.4 bleak==0.19.2 bluetooth-adapters==0.7.0 bluetooth-auto-recovery==0.3.6 diff --git a/requirements_all.txt b/requirements_all.txt index 17351f986a2..e8b5e7083c9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -422,7 +422,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.3 +bleak-retry-connector==2.8.4 # homeassistant.components.bluetooth bleak==0.19.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4a4276019e9..6126fe55723 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -346,7 +346,7 @@ bellows==0.34.2 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.3 +bleak-retry-connector==2.8.4 # homeassistant.components.bluetooth bleak==0.19.2 From 8c9a8d72a8cdb178020b246c1d3511c9baeebd94 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Nov 2022 01:53:07 -0600 Subject: [PATCH 0359/1033] Bump PySwitchbot to 0.20.3 (#81938) changelog: https://github.com/Danielhiversen/pySwitchbot/compare/0.20.2...0.20.3 releases the connection sooner to reduce the risk of running out of connection slots on the ble adapter --- homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index 532edac7d43..2c95327beef 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.20.2"], + "requirements": ["PySwitchbot==0.20.3"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index e8b5e7083c9..232acb2c6d3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -37,7 +37,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.20.2 +PySwitchbot==0.20.3 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6126fe55723..9987c7a1e9b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -33,7 +33,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.20.2 +PySwitchbot==0.20.3 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From 6517e3e21e5828a317ea8b814d0345f2536b9170 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Nov 2022 02:09:28 -0600 Subject: [PATCH 0360/1033] Fix esphome bleak client seeing disconnects too late (#81932) * Fix esphome bleak client seeing disconnects too late Because allbacks are delivered asynchronously its possible that we find out during the operation before the callback is delivered telling us about the disconnected. We now watch for error code -1 which indicates the connection dropped out from under us * debug logging * cleanup comment * Fix comment grammar Co-authored-by: Martin Hjelmare --- .../components/esphome/bluetooth/client.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index c6b60831577..ceac4e5aaae 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -13,6 +13,7 @@ from aioesphomeapi import ( BLEConnectionError, ) from aioesphomeapi.connection import APIConnectionError, TimeoutAPIError +from aioesphomeapi.core import BluetoothGATTAPIError import async_timeout from bleak.backends.characteristic import BleakGATTCharacteristic from bleak.backends.client import BaseBleakClient, NotifyCallback @@ -83,6 +84,24 @@ def api_error_as_bleak_error(func: _WrapFuncType) -> _WrapFuncType: return await func(self, *args, **kwargs) except TimeoutAPIError as err: raise asyncio.TimeoutError(str(err)) from err + except BluetoothGATTAPIError as ex: + # If the device disconnects in the middle of an operation + # be sure to mark it as disconnected so any library using + # the proxy knows to reconnect. + # + # Because callbacks are delivered asynchronously it's possible + # that we find out about the disconnection during the operation + # before the callback is delivered. + if ex.error.error == -1: + _LOGGER.debug( + "%s: %s - %s: BLE device disconnected during %s operation", + self._source, # pylint: disable=protected-access + self._ble_device.name, # pylint: disable=protected-access + self._ble_device.address, # pylint: disable=protected-access + func.__name__, + ) + self._async_ble_device_disconnected() # pylint: disable=protected-access + raise BleakError(str(ex)) from ex except APIConnectionError as err: raise BleakError(str(err)) from err From 89959e7cda75400144ef1d3ec7c11ba651b8d085 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Nov 2022 07:59:06 -0600 Subject: [PATCH 0361/1033] Reduce complexity of bluetooth scanners for local adapters (#81940) This is a small refactor to align the bluetooth scanner class to the same design used in the esphome scanner to pass the callback for new service infos in the constructor. The original thinking was that multiple callbacks would be needed to send the service infos to different paths, however with the creation of a central manager, this designed turned out to not be needed and can be simplified --- .../components/bluetooth/__init__.py | 5 +- homeassistant/components/bluetooth/scanner.py | 47 +++++++------------ 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index 8590d1ad90a..23165e2bc09 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -425,15 +425,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: passive = entry.options.get(CONF_PASSIVE) mode = BluetoothScanningMode.PASSIVE if passive else BluetoothScanningMode.ACTIVE - scanner = HaScanner(hass, mode, adapter, address) + new_info_callback = async_get_advertisement_callback(hass) + scanner = HaScanner(hass, mode, adapter, address, new_info_callback) try: scanner.async_setup() except RuntimeError as err: raise ConfigEntryNotReady( f"{adapter_human_name(adapter, address)}: {err}" ) from err - info_callback = async_get_advertisement_callback(hass) - entry.async_on_unload(scanner.async_register_callback(info_callback)) try: await scanner.async_start() except ScannerStartError as err: diff --git a/homeassistant/components/bluetooth/scanner.py b/homeassistant/components/bluetooth/scanner.py index 43c4706474a..c05894d3b44 100644 --- a/homeassistant/components/bluetooth/scanner.py +++ b/homeassistant/components/bluetooth/scanner.py @@ -125,6 +125,7 @@ class HaScanner(BaseHaScanner): mode: BluetoothScanningMode, adapter: str, address: str, + new_info_callback: Callable[[BluetoothServiceInfoBleak], None], ) -> None: """Init bluetooth discovery.""" source = address if address != DEFAULT_ADDRESS else adapter or SOURCE_LOCAL @@ -135,7 +136,7 @@ class HaScanner(BaseHaScanner): self._cancel_watchdog: CALLBACK_TYPE | None = None self._last_detection = 0.0 self._start_time = 0.0 - self._callbacks: list[Callable[[BluetoothServiceInfoBleak], None]] = [] + self._new_info_callback = new_info_callback self.name = adapter_human_name(adapter, address) @property @@ -168,22 +169,6 @@ class HaScanner(BaseHaScanner): "start_time": self._start_time, } - @hass_callback - def async_register_callback( - self, callback: Callable[[BluetoothServiceInfoBleak], None] - ) -> CALLBACK_TYPE: - """Register a callback. - - Currently this is used to feed the callbacks into the - central manager. - """ - - def _remove() -> None: - self._callbacks.remove(callback) - - self._callbacks.append(callback) - return _remove - @hass_callback def _async_detection_callback( self, @@ -206,21 +191,21 @@ class HaScanner(BaseHaScanner): # as the adapter is in a failure # state if all the data is empty. self._last_detection = callback_time - service_info = BluetoothServiceInfoBleak( - name=advertisement_data.local_name or device.name or device.address, - address=device.address, - rssi=advertisement_data.rssi, - manufacturer_data=advertisement_data.manufacturer_data, - service_data=advertisement_data.service_data, - service_uuids=advertisement_data.service_uuids, - source=self.source, - device=device, - advertisement=advertisement_data, - connectable=True, - time=callback_time, + self._new_info_callback( + BluetoothServiceInfoBleak( + name=advertisement_data.local_name or device.name or device.address, + address=device.address, + rssi=advertisement_data.rssi, + manufacturer_data=advertisement_data.manufacturer_data, + service_data=advertisement_data.service_data, + service_uuids=advertisement_data.service_uuids, + source=self.source, + device=device, + advertisement=advertisement_data, + connectable=True, + time=callback_time, + ) ) - for callback in self._callbacks: - callback(service_info) async def async_start(self) -> None: """Start bluetooth scanner.""" From b5dfe8c6b978b0277151c17b46661811b0a06868 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Fri, 11 Nov 2022 15:37:57 +0000 Subject: [PATCH 0362/1033] Fix battery %, battery voltage and signal strength not being diagnostic entities in xiaomi_ble (#81960) --- homeassistant/components/xiaomi_ble/sensor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeassistant/components/xiaomi_ble/sensor.py b/homeassistant/components/xiaomi_ble/sensor.py index 1aa1b7f8f72..0e7e05a3a94 100644 --- a/homeassistant/components/xiaomi_ble/sensor.py +++ b/homeassistant/components/xiaomi_ble/sensor.py @@ -29,6 +29,7 @@ from homeassistant.const import ( TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN @@ -64,12 +65,14 @@ SENSOR_DESCRIPTIONS = { device_class=SensorDeviceClass.BATTERY, native_unit_of_measurement=PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, ), (DeviceClass.VOLTAGE, Units.ELECTRIC_POTENTIAL_VOLT): SensorEntityDescription( key=str(Units.ELECTRIC_POTENTIAL_VOLT), device_class=SensorDeviceClass.VOLTAGE, native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, ), ( DeviceClass.SIGNAL_STRENGTH, @@ -80,6 +83,7 @@ SENSOR_DESCRIPTIONS = { native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, + entity_category=EntityCategory.DIAGNOSTIC, ), # Used for e.g. moisture sensor on HHCCJCY01 (None, Units.PERCENTAGE): SensorEntityDescription( From 1baa5c12c0c4802edf01a64964b0ed94b7e2c4a7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 11 Nov 2022 17:08:26 +0100 Subject: [PATCH 0363/1033] Fix rest schema (#81857) --- homeassistant/components/rest/schema.py | 3 +-- tests/components/rest/test_init.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/rest/schema.py b/homeassistant/components/rest/schema.py index d124ce5789c..cfd8f8a3852 100644 --- a/homeassistant/components/rest/schema.py +++ b/homeassistant/components/rest/schema.py @@ -91,9 +91,8 @@ COMBINED_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.All( - # convert empty dict to empty list - lambda x: [] if x == {} else x, cv.ensure_list, + cv.remove_falsy, [COMBINED_SCHEMA], ) }, diff --git a/tests/components/rest/test_init.py b/tests/components/rest/test_init.py index 08d538fd163..fbee3c2051a 100644 --- a/tests/components/rest/test_init.py +++ b/tests/components/rest/test_init.py @@ -418,3 +418,19 @@ async def test_empty_config(hass: HomeAssistant) -> None: {DOMAIN: {}}, ) assert_setup_component(0, DOMAIN) + + +async def test_config_schema_via_packages(hass: HomeAssistant) -> None: + """Test configuration via packages.""" + packages = { + "pack_dict": {"rest": {}}, + "pack_11": {"rest": {"resource": "http://url1"}}, + "pack_list": {"rest": [{"resource": "http://url2"}]}, + } + config = {hass_config.CONF_CORE: {hass_config.CONF_PACKAGES: packages}} + await hass_config.merge_packages_config(hass, config, packages) + + assert len(config) == 2 + assert len(config["rest"]) == 2 + assert config["rest"][0]["resource"] == "http://url1" + assert config["rest"][1]["resource"] == "http://url2" From 380ae12997f91d2f63bb50f663677a0c51a35784 Mon Sep 17 00:00:00 2001 From: muppet3000 Date: Fri, 11 Nov 2022 22:01:15 +0000 Subject: [PATCH 0364/1033] Fix Growatt missing state class (#81980) Growatt - Fix #80003 - Add missing state class --- homeassistant/components/growatt_server/sensor_types/tlx.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/growatt_server/sensor_types/tlx.py b/homeassistant/components/growatt_server/sensor_types/tlx.py index 597ddd789cf..ba455747457 100644 --- a/homeassistant/components/growatt_server/sensor_types/tlx.py +++ b/homeassistant/components/growatt_server/sensor_types/tlx.py @@ -20,6 +20,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( api_key="eacToday", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL_INCREASING, precision=1, ), GrowattSensorEntityDescription( From a8aea6df5ea222f7cb960fb847f349a0cc771eb1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Nov 2022 16:01:54 -0600 Subject: [PATCH 0365/1033] Bump oralb-ble to 0.14.2 (#81969) fixes detection of the black 9000 model fixes #81967 changelog: https://github.com/Bluetooth-Devices/oralb-ble/compare/v0.14.1...v0.14.2 --- homeassistant/components/oralb/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/oralb/manifest.json b/homeassistant/components/oralb/manifest.json index eff6c999c30..8868330a7e7 100644 --- a/homeassistant/components/oralb/manifest.json +++ b/homeassistant/components/oralb/manifest.json @@ -8,7 +8,7 @@ "manufacturer_id": 220 } ], - "requirements": ["oralb-ble==0.14.1"], + "requirements": ["oralb-ble==0.14.2"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 232acb2c6d3..c80978cd464 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1255,7 +1255,7 @@ openwrt-luci-rpc==1.1.11 openwrt-ubus-rpc==0.0.2 # homeassistant.components.oralb -oralb-ble==0.14.1 +oralb-ble==0.14.2 # homeassistant.components.oru oru==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9987c7a1e9b..299050fbc7b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -900,7 +900,7 @@ open-meteo==0.2.1 openerz-api==0.1.0 # homeassistant.components.oralb -oralb-ble==0.14.1 +oralb-ble==0.14.2 # homeassistant.components.ovo_energy ovoenergy==1.2.0 From 8ee60c2e7f47e09d980fa2a5144482c8402a5857 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 11 Nov 2022 17:03:32 -0500 Subject: [PATCH 0366/1033] Bump ZHA quirks lib to 0.0.86 (#81966) --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index c8aebe3b0c0..312b93aff6f 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -7,7 +7,7 @@ "bellows==0.34.2", "pyserial==3.5", "pyserial-asyncio==0.6", - "zha-quirks==0.0.85", + "zha-quirks==0.0.86", "zigpy-deconz==0.19.0", "zigpy==0.51.5", "zigpy-xbee==0.16.2", diff --git a/requirements_all.txt b/requirements_all.txt index c80978cd464..3a1b515876e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2624,7 +2624,7 @@ zengge==0.2 zeroconf==0.39.4 # homeassistant.components.zha -zha-quirks==0.0.85 +zha-quirks==0.0.86 # homeassistant.components.zhong_hong zhong_hong_hvac==1.0.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 299050fbc7b..158501504e1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1825,7 +1825,7 @@ zamg==0.1.1 zeroconf==0.39.4 # homeassistant.components.zha -zha-quirks==0.0.85 +zha-quirks==0.0.86 # homeassistant.components.zha zigpy-deconz==0.19.0 From de48ae2e53f5b35df5892df1f96daf033198a5bd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Nov 2022 16:22:22 -0600 Subject: [PATCH 0367/1033] Bump dbus-fast to 1.73.0 (#81959) changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v1.72.0...v1.73.0 --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 59397f30bf9..e2daee3650d 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -11,7 +11,7 @@ "bluetooth-adapters==0.7.0", "bluetooth-auto-recovery==0.3.6", "bluetooth-data-tools==0.2.0", - "dbus-fast==1.72.0" + "dbus-fast==1.73.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index daeeda866a8..603d1bba36a 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -18,7 +18,7 @@ bluetooth-data-tools==0.2.0 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.3 -dbus-fast==1.72.0 +dbus-fast==1.73.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 3a1b515876e..ead4f05bf9d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -553,7 +553,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.72.0 +dbus-fast==1.73.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 158501504e1..d8661001022 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -433,7 +433,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.72.0 +dbus-fast==1.73.0 # homeassistant.components.debugpy debugpy==1.6.3 From a6a3bf261d89c7133841ae0fd8e1e24f2358a7c5 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 12 Nov 2022 00:28:24 +0000 Subject: [PATCH 0368/1033] [ci skip] Translation update --- .../components/braviatv/translations/ca.json | 3 + .../components/generic/translations/bg.json | 2 + .../homeassistant/translations/ca.json | 6 + .../components/knx/translations/bg.json | 14 +- .../components/knx/translations/no.json | 125 +++++++++++++++++- .../components/livisi/translations/ca.json | 4 +- .../components/mjpeg/translations/bg.json | 2 + .../components/mqtt/translations/ca.json | 16 +-- .../nibe_heatpump/translations/ca.json | 14 +- .../nibe_heatpump/translations/no.json | 10 +- .../transmission/translations/ca.json | 13 ++ 11 files changed, 186 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/braviatv/translations/ca.json b/homeassistant/components/braviatv/translations/ca.json index e6f8ebc8e92..2b15a496af0 100644 --- a/homeassistant/components/braviatv/translations/ca.json +++ b/homeassistant/components/braviatv/translations/ca.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "S'ha produ\u00eft un error en actualitzar la llista de fonts.\n\nAssegura't que el televisor est\u00e0 engegat abans de configurar-lo." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/generic/translations/bg.json b/homeassistant/components/generic/translations/bg.json index b26b65e715e..50cb1feb1b7 100644 --- a/homeassistant/components/generic/translations/bg.json +++ b/homeassistant/components/generic/translations/bg.json @@ -5,6 +5,7 @@ "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." }, "error": { + "no_still_image_or_stream_url": "\u0422\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u043f\u043e\u0441\u043e\u0447\u0438\u0442\u0435 \u043f\u043e\u043d\u0435 URL \u043d\u0430 \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0438\u043b\u0438 \u043f\u043e\u0442\u043e\u043a", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { @@ -22,6 +23,7 @@ "authentication": "\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "rtsp_transport": "RTSP \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0435\u043d \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b", + "still_image_url": "URL \u043d\u0430 \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 (\u043d\u0430\u043f\u0440. http://...)", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" }, "description": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u0442\u0435 \u0437\u0430 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430." diff --git a/homeassistant/components/homeassistant/translations/ca.json b/homeassistant/components/homeassistant/translations/ca.json index 8bcf5d96a86..e850412488a 100644 --- a/homeassistant/components/homeassistant/translations/ca.json +++ b/homeassistant/components/homeassistant/translations/ca.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "El valor de moneda {currency} ja no s'utilitza, modifica la configuraci\u00f3 del valor de moneda.", + "title": "El valor de moneda configurat ja no s'utilitza" + } + }, "system_health": { "info": { "arch": "Arquitectura de la CPU", diff --git a/homeassistant/components/knx/translations/bg.json b/homeassistant/components/knx/translations/bg.json index 331c85d4065..72bbfb4a80c 100644 --- a/homeassistant/components/knx/translations/bg.json +++ b/homeassistant/components/knx/translations/bg.json @@ -37,6 +37,10 @@ } }, "options": { + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_ip_address": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d IPv4 \u0430\u0434\u0440\u0435\u0441." + }, "step": { "init": { "data": { @@ -46,12 +50,20 @@ "local_ip": "\u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0439\u0442\u0435 `0.0.0.0` \u0437\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435." } }, + "manual_tunnel": { + "data": { + "host": "\u0425\u043e\u0441\u0442", + "local_ip": "\u041b\u043e\u043a\u0430\u043b\u0435\u043d IP \u043d\u0430 Home Assistant", + "port": "\u041f\u043e\u0440\u0442" + } + }, "tunnel": { "data": { "host": "\u0425\u043e\u0441\u0442", "port": "\u041f\u043e\u0440\u0442", "tunneling_type": "KNX \u0442\u0443\u043d\u0435\u043b\u0435\u043d \u0442\u0438\u043f" - } + }, + "description": "\u041c\u043e\u043b\u044f, \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0448\u043b\u044e\u0437 \u043e\u0442 \u0441\u043f\u0438\u0441\u044a\u043a\u0430." } } } diff --git a/homeassistant/components/knx/translations/no.json b/homeassistant/components/knx/translations/no.json index 596071695b4..f6aaca60d18 100644 --- a/homeassistant/components/knx/translations/no.json +++ b/homeassistant/components/knx/translations/no.json @@ -9,20 +9,30 @@ "file_not_found": "Den angitte `.knxkeys`-filen ble ikke funnet i banen config/.storage/knx/", "invalid_individual_address": "Verdien samsvarer ikke med m\u00f8nsteret for individuelle KNX-adresser.\n 'area.line.device'", "invalid_ip_address": "Ugyldig IPv4-adresse.", - "invalid_signature": "Passordet for \u00e5 dekryptere `.knxkeys`-filen er feil." + "invalid_signature": "Passordet for \u00e5 dekryptere `.knxkeys`-filen er feil.", + "no_router_discovered": "Ingen KNXnet/IP-ruter ble oppdaget p\u00e5 nettverket.", + "no_tunnel_discovered": "Kunne ikke finne en KNX-tunnelserver p\u00e5 nettverket ditt." }, "step": { + "connection_type": { + "data": { + "connection_type": "KNX tilkoblingstype" + }, + "description": "Vennligst skriv inn tilkoblingstypen vi skal bruke for din KNX-tilkobling.\n AUTOMATISK - Integrasjonen tar seg av tilkoblingen til KNX-bussen ved \u00e5 utf\u00f8re en gateway-skanning.\n TUNNELING - Integrasjonen vil kobles til din KNX-bussen via tunnelering.\n ROUTING - Integrasjonen vil koble til din KNX-bussen via ruting." + }, "manual_tunnel": { "data": { "host": "Vert", "local_ip": "Lokal IP for hjemmeassistent", "port": "Port", + "route_back": "Rute tilbake / NAT-modus", "tunneling_type": "KNX tunneltype" }, "data_description": { "host": "IP-adressen til KNX/IP-tunnelenheten.", "local_ip": "La st\u00e5 tomt for \u00e5 bruke automatisk oppdagelse.", - "port": "Port p\u00e5 KNX/IP-tunnelenheten." + "port": "Port p\u00e5 KNX/IP-tunnelenheten.", + "route_back": "Aktiver hvis KNXnet/IP-tunnelserveren din er bak NAT. Gjelder kun for UDP-tilkoblinger." }, "description": "Vennligst skriv inn tilkoblingsinformasjonen til tunnelenheten din." }, @@ -63,11 +73,25 @@ }, "description": "Vennligst skriv inn din sikre IP-informasjon." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Enhetsautentiseringspassord", + "user_id": "bruker-ID", + "user_password": "Bruker passord" + }, + "data_description": { + "device_authentication": "Dette settes i 'IP'-panelet til grensesnittet i ETS.", + "user_id": "Dette er ofte tunnelnummer +1. S\u00e5 'Tunnel 2' ville ha bruker-ID '3'.", + "user_password": "Passord for den spesifikke tunnelforbindelsen satt i 'Egenskaper'-panelet i tunnelen i ETS." + }, + "description": "Vennligst skriv inn din sikre IP-informasjon." + }, "secure_tunneling": { "description": "Velg hvordan du vil konfigurere KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Bruk en `.knxkeys`-fil som inneholder IP-sikre n\u00f8kler", - "secure_manual": "Konfigurer IP-sikre n\u00f8kler manuelt" + "secure_manual": "Konfigurer IP-sikre n\u00f8kler manuelt", + "secure_tunnel_manual": "Konfigurer IP-sikre n\u00f8kler manuelt" } }, "tunnel": { @@ -85,7 +109,32 @@ } }, "options": { + "error": { + "cannot_connect": "Tilkobling mislyktes", + "file_not_found": "Den angitte `.knxkeys`-filen ble ikke funnet i banen config/.storage/knx/", + "invalid_individual_address": "Verdien samsvarer ikke med m\u00f8nsteret for individuelle KNX-adresser.\n 'area.line.device'", + "invalid_ip_address": "Ugyldig IPv4-adresse.", + "invalid_signature": "Passordet for \u00e5 dekryptere `.knxkeys`-filen er feil.", + "no_router_discovered": "Ingen KNXnet/IP-ruter ble oppdaget p\u00e5 nettverket.", + "no_tunnel_discovered": "Kunne ikke finne en KNX-tunnelserver p\u00e5 nettverket ditt." + }, "step": { + "communication_settings": { + "data": { + "rate_limit": "Satsgrense", + "state_updater": "Statens oppdatering" + }, + "data_description": { + "rate_limit": "Maksimalt utg\u00e5ende telegrammer per sekund.\n `0` for \u00e5 deaktivere grensen. Anbefalt: 0 eller 20 til 40", + "state_updater": "Angi standard for lesing av tilstander fra KNX-bussen. N\u00e5r den er deaktivert, vil ikke Home Assistant aktivt hente enhetstilstander fra KNX-bussen. Kan overstyres av entitetsalternativer for \"sync_state\"." + } + }, + "connection_type": { + "data": { + "connection_type": "KNX tilkoblingstype" + }, + "description": "Vennligst skriv inn tilkoblingstypen vi skal bruke for din KNX-tilkobling.\n AUTOMATISK - Integrasjonen tar seg av tilkoblingen til KNX-bussen ved \u00e5 utf\u00f8re en gateway-skanning.\n TUNNELING - Integrasjonen vil kobles til din KNX-bussen via tunnelering.\n ROUTING - Integrasjonen vil koble til din KNX-bussen via ruting." + }, "init": { "data": { "connection_type": "KNX tilkoblingstype", @@ -105,8 +154,75 @@ "state_updater": "Sett standard for lesing av tilstander fra KNX-bussen. N\u00e5r den er deaktivert, vil ikke Home Assistant aktivt hente enhetstilstander fra KNX-bussen. Kan overstyres av entitetsalternativer for \"sync_state\"." } }, + "manual_tunnel": { + "data": { + "host": "Vert", + "local_ip": "Lokal IP for hjemmeassistent", + "port": "Port", + "route_back": "Rute tilbake / NAT-modus", + "tunneling_type": "KNX tunneltype" + }, + "data_description": { + "host": "IP-adressen til KNX/IP-tunnelenheten.", + "local_ip": "La st\u00e5 tomt for \u00e5 bruke automatisk oppdagelse.", + "port": "Port p\u00e5 KNX/IP-tunnelenheten.", + "route_back": "Aktiver hvis KNXnet/IP-tunnelserveren din er bak NAT. Gjelder kun for UDP-tilkoblinger." + }, + "description": "Vennligst skriv inn tilkoblingsinformasjonen til tunnelenheten din." + }, + "options_init": { + "menu_options": { + "communication_settings": "Kommunikasjonsinnstillinger", + "connection_type": "Konfigurer KNX-grensesnitt" + } + }, + "routing": { + "data": { + "individual_address": "Individuell adresse", + "local_ip": "Lokal IP for hjemmeassistent", + "multicast_group": "Multicast gruppe", + "multicast_port": "Multicast port" + }, + "data_description": { + "individual_address": "KNX-adresse som skal brukes av Home Assistant, f.eks. `0.0.4`", + "local_ip": "La st\u00e5 tomt for \u00e5 bruke automatisk oppdagelse." + }, + "description": "Vennligst konfigurer rutealternativene." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Filnavnet til `.knxkeys`-filen (inkludert utvidelse)", + "knxkeys_password": "Passordet for \u00e5 dekryptere `.knxkeys`-filen" + }, + "data_description": { + "knxkeys_filename": "Filen forventes \u00e5 bli funnet i konfigurasjonskatalogen din i `.storage/knx/`.\n I Home Assistant OS vil dette v\u00e6re `/config/.storage/knx/`\n Eksempel: `mitt_prosjekt.knxkeys`", + "knxkeys_password": "Dette ble satt ved eksport av filen fra ETS." + }, + "description": "Vennligst skriv inn informasjonen for `.knxkeys`-filen." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Enhetsautentiseringspassord", + "user_id": "bruker-ID", + "user_password": "Bruker passord" + }, + "data_description": { + "device_authentication": "Dette settes i 'IP'-panelet til grensesnittet i ETS.", + "user_id": "Dette er ofte tunnelnummer +1. S\u00e5 'Tunnel 2' ville ha bruker-ID '3'.", + "user_password": "Passord for den spesifikke tunnelforbindelsen satt i 'Egenskaper'-panelet i tunnelen i ETS." + }, + "description": "Vennligst skriv inn din sikre IP-informasjon." + }, + "secure_tunneling": { + "description": "Velg hvordan du vil konfigurere KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Bruk en `.knxkeys`-fil som inneholder IP-sikre n\u00f8kler", + "secure_tunnel_manual": "Konfigurer IP-sikre n\u00f8kler manuelt" + } + }, "tunnel": { "data": { + "gateway": "KNX Tunneltilkobling", "host": "Vert", "port": "Port", "tunneling_type": "KNX tunneltype" @@ -114,7 +230,8 @@ "data_description": { "host": "IP-adressen til KNX/IP-tunnelenheten.", "port": "Port p\u00e5 KNX/IP-tunnelenheten." - } + }, + "description": "Vennligst velg en gateway fra listen." } } } diff --git a/homeassistant/components/livisi/translations/ca.json b/homeassistant/components/livisi/translations/ca.json index 359b7eb01b1..e765c9199e0 100644 --- a/homeassistant/components/livisi/translations/ca.json +++ b/homeassistant/components/livisi/translations/ca.json @@ -2,6 +2,7 @@ "config": { "error": { "cannot_connect": "Ha fallat la connexi\u00f3", + "wrong_ip_address": "L'adre\u00e7a IP \u00e9s incorrecta o no es pot connectar amb l'SHC localment.", "wrong_password": "La contrasenya \u00e9s incorrecta." }, "step": { @@ -9,7 +10,8 @@ "data": { "host": "Adre\u00e7a IP", "password": "Contrasenya" - } + }, + "description": "Introdueix l'adre\u00e7a IP i la contrasenya (local) de l'SHC." } } } diff --git a/homeassistant/components/mjpeg/translations/bg.json b/homeassistant/components/mjpeg/translations/bg.json index 0e88f508191..19b62c42896 100644 --- a/homeassistant/components/mjpeg/translations/bg.json +++ b/homeassistant/components/mjpeg/translations/bg.json @@ -13,6 +13,7 @@ "mjpeg_url": "MJPEG URL", "name": "\u0418\u043c\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "still_image_url": "URL \u043d\u0430 \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" } } @@ -30,6 +31,7 @@ "mjpeg_url": "MJPEG URL", "name": "\u0418\u043c\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "still_image_url": "URL \u043d\u0430 \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" } } diff --git a/homeassistant/components/mqtt/translations/ca.json b/homeassistant/components/mqtt/translations/ca.json index 13c91abe856..7dc484cdcd4 100644 --- a/homeassistant/components/mqtt/translations/ca.json +++ b/homeassistant/components/mqtt/translations/ca.json @@ -20,10 +20,10 @@ "data": { "advanced_options": "Opcions avan\u00e7ades", "broker": "Broker", - "certificate": "Ruta a un fitxer de certificat CA personalitzat", - "client_cert": "Ruta a un fitxer de certificat de client", + "certificate": "Puja fitxer de certificat CA personalitzat", + "client_cert": "Puja fitxer de certificat client", "client_id": "ID de client (deixa-ho buit per generar-lo aleat\u00f2riament)", - "client_key": "Ruta a un fitxer de clau privada", + "client_key": "Puja fitxer de clau privada", "discovery": "Habilita el descobriment autom\u00e0tic", "keepalive": "Temps entre enviaments de missatges de manteniment viu ('keep alive')", "password": "Contrasenya", @@ -38,7 +38,7 @@ }, "hassio_confirm": { "data": { - "discovery": "Habilitar descobriment autom\u00e0tic" + "discovery": "Activa el descobriment" }, "description": "Vols configurar Home Assistant perqu\u00e8 es connecti amb el broker MQTT proporcionat pel complement {addon}?", "title": "Broker MQTT via complement de Home Assistant" @@ -94,10 +94,10 @@ "data": { "advanced_options": "Opcions avan\u00e7ades", "broker": "Broker", - "certificate": "Puja un fitxer de certificat CA personalitzat", - "client_cert": "Puja un fitxer de certificat de client", + "certificate": "Puja fitxer de certificat CA personalitzat", + "client_cert": "Puja fitxer de certificat client", "client_id": "ID de client (deixa-ho buit per generar-lo aleat\u00f2riament)", - "client_key": "Puja un fitxer de clau privada", + "client_key": "Puja fitxer de clau privada", "keepalive": "Temps entre enviaments de missatges de manteniment viu ('keep alive')", "password": "Contrasenya", "port": "Port", @@ -117,7 +117,7 @@ "birth_qos": "QoS del missatge de naixement", "birth_retain": "Retenci\u00f3 del missatge de naixement", "birth_topic": "Topic del missatge de naixement", - "discovery": "Activar descobriment", + "discovery": "Activa el descobriment", "discovery_prefix": "Prefix de descobriment", "will_enable": "Activa el missatge d'\u00faltima voluntat", "will_payload": "Dades (payload) del missatge d'\u00faltima voluntat", diff --git a/homeassistant/components/nibe_heatpump/translations/ca.json b/homeassistant/components/nibe_heatpump/translations/ca.json index de5814b4f38..a8a91740a7d 100644 --- a/homeassistant/components/nibe_heatpump/translations/ca.json +++ b/homeassistant/components/nibe_heatpump/translations/ca.json @@ -6,16 +6,22 @@ "error": { "address": "Adre\u00e7a remota inv\u00e0lida. L'adre\u00e7a ha de ser una adre\u00e7a IP o un nom d'amfitri\u00f3 resoluble.", "address_in_use": "El port d'escolta seleccionat ja est\u00e0 en \u00fas en aquest sistema.", - "model": "El model seleccionat no sembla admetre modbus40", - "read": "Error en la sol\u00b7licitud de lectura de la bomba. Verifica el port remot de lectura i/o l'adre\u00e7a IP remota.", + "model": "El model seleccionat no sembla admetre MODBUS40", + "read": "Error en la sol\u00b7licitud de lectura de la bomba. Verifica el `port remot de lectura` i/o `l'adre\u00e7a remota`.", "unknown": "Error inesperat", - "write": "Error en la sol\u00b7licitud d'escriptura a la bomba. Verifica el port remot d'escriptura i/o l'adre\u00e7a IP remota." + "url": "L'URL especificat no est\u00e0 ben format o no \u00e9s compatible", + "write": "Error en la sol\u00b7licitud d'escriptura a la bomba. Verifica el `port remot d'escriptura` i/o `l'adre\u00e7a remota`." }, "step": { "modbus": { "data": { + "modbus_unit": "Identificador d'unitat Modbus", "modbus_url": "URL de Modbus", "model": "Model de bomba de calor" + }, + "data_description": { + "modbus_unit": "Identificaci\u00f3 de la teva bomba de calor. Normalment es pot deixar a 0.", + "modbus_url": "URL de Modbus que descriu la connexi\u00f3 a la teva bomba de calor o unitat MODBUS40. Ha d'estar en el format:\n - `tcp://[HOST]:[PORT]` per a una connexi\u00f3 Modbus TCP\n - `serial://[DISPOSITIU LOCAL]` per a una connexi\u00f3 Modbus RTU local\n - `rfc2217://[HOST]:[PORT]` per a una connexi\u00f3 remota Modbus RTU basada en telnet." } }, "nibegw": { @@ -47,7 +53,7 @@ "remote_read_port": "Port on la unitat NibeGW espera les sol\u00b7licituds de lectura.", "remote_write_port": "Port on la unitat NibeGW espera les sol\u00b7licituds d'escriptura." }, - "description": "Abans d'intentar configurar la integraci\u00f3, comprova que:\n - La unitat NibeGW est\u00e0 connectada a una bomba de calor.\n - S'ha activat l'accessori MODBUS40 a la configuraci\u00f3 de la bomba de calor.\n - La bomba no ha entrat en estat d'alarma per falta de l'accessori MODBUS40.", + "description": "Tria el m\u00e8tode de connexi\u00f3 a la teva bomba. En general, les bombes de la s\u00e8rie F necessiten un accessori personalitzat NibeGW, mentre que les bombes de la s\u00e8rie S tenen Modbus integrat.", "menu_options": { "modbus": "Modbus", "nibegw": "NibeGW" diff --git a/homeassistant/components/nibe_heatpump/translations/no.json b/homeassistant/components/nibe_heatpump/translations/no.json index e2ffa8790f7..d7da2189116 100644 --- a/homeassistant/components/nibe_heatpump/translations/no.json +++ b/homeassistant/components/nibe_heatpump/translations/no.json @@ -6,11 +6,11 @@ "error": { "address": "Ugyldig ekstern adresse er angitt. Adressen m\u00e5 v\u00e6re en IP-adresse eller et vertsnavn som kan l\u00f8ses.", "address_in_use": "Den valgte lytteporten er allerede i bruk p\u00e5 dette systemet.", - "model": "Den valgte modellen ser ikke ut til \u00e5 st\u00f8tte modbus40", - "read": "Feil ved leseforesp\u00f8rsel fra pumpe. Bekreft din \"Ekstern leseport\" eller \"Ekstern IP-adresse\".", + "model": "Den valgte modellen ser ikke ut til \u00e5 st\u00f8tte MODBUS40", + "read": "Feil ved leseforesp\u00f8rsel fra pumpe. Bekreft din \"Ekstern leseport\" eller \"Ekstern adresse\".", "unknown": "Uventet feil", - "url": "Den spesifiserte nettadressen er ikke en godt utformet og st\u00f8ttet nettadresse", - "write": "Feil ved skriveforesp\u00f8rsel til pumpen. Bekreft din \"Ekstern skriveport\" eller \"Ekstern IP-adresse\"." + "url": "Den angitte URL-en er ikke godt utformet eller st\u00f8ttet", + "write": "Feil ved skriveforesp\u00f8rsel til pumpen. Bekreft din \"Ekstern skriveport\" eller \"Ekstern adresse\"." }, "step": { "modbus": { @@ -53,7 +53,7 @@ "remote_read_port": "Porten NibeGW-enheten lytter etter leseforesp\u00f8rsler p\u00e5.", "remote_write_port": "Porten NibeGW-enheten lytter etter skriveforesp\u00f8rsler p\u00e5." }, - "description": "Velg tilkoblingsmetoden til pumpen din. Generelt krever pumper i F-serien et Nibe GW-tilpasset tilbeh\u00f8r, mens en pumpe i S-serien har Modbus-st\u00f8tte innebygd.", + "description": "Velg tilkoblingsmetoden til pumpen din. Generelt krever pumper i F-serien et NibeGW-tilpasset tilbeh\u00f8r, mens en pumpe i S-serien har Modbus-st\u00f8tte innebygd.", "menu_options": { "modbus": "Modbus", "nibegw": "NibeGW" diff --git a/homeassistant/components/transmission/translations/ca.json b/homeassistant/components/transmission/translations/ca.json index 235e05bb78a..f982fcc9dd2 100644 --- a/homeassistant/components/transmission/translations/ca.json +++ b/homeassistant/components/transmission/translations/ca.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "Actualitza totes les automatitzacions o 'scripts' que utilitzin aquest servei. S'han de substituir totes les claus o entrades anomenades 'name' per 'entry_id'.", + "title": "S'est\u00e0 eliminant la clau 'name' del servei Transmission" + } + } + }, + "title": "S'est\u00e0 eliminant la clau 'name' del servei Transmission" + } + }, "options": { "step": { "init": { From 1fe85c9b1700f36d41374490701c9f8738137273 Mon Sep 17 00:00:00 2001 From: rappenze Date: Sat, 12 Nov 2022 10:43:11 +0100 Subject: [PATCH 0369/1033] Fix accelerator sensor in fibaro integration (#81237) * Fix accelerator sensor in fibaro integration * Implement suggestions from code review * Implement suggestions from code review * Changes as suggested in code review * Adjust as suggested in code review --- homeassistant/components/fibaro/__init__.py | 1 + .../components/fibaro/binary_sensor.py | 56 ++++++++++++++++--- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/fibaro/__init__.py b/homeassistant/components/fibaro/__init__.py index 3661721810b..19f36742740 100644 --- a/homeassistant/components/fibaro/__init__.py +++ b/homeassistant/components/fibaro/__init__.py @@ -84,6 +84,7 @@ FIBARO_TYPEMAP = { "com.fibaro.thermostatDanfoss": Platform.CLIMATE, "com.fibaro.doorLock": Platform.LOCK, "com.fibaro.binarySensor": Platform.BINARY_SENSOR, + "com.fibaro.accelerometer": Platform.BINARY_SENSOR, } DEVICE_CONFIG_SCHEMA_ENTRY = vol.Schema( diff --git a/homeassistant/components/fibaro/binary_sensor.py b/homeassistant/components/fibaro/binary_sensor.py index 359869efc25..f9baa33c41f 100644 --- a/homeassistant/components/fibaro/binary_sensor.py +++ b/homeassistant/components/fibaro/binary_sensor.py @@ -1,6 +1,8 @@ """Support for Fibaro binary sensors.""" from __future__ import annotations +from collections.abc import Mapping +import json from typing import Any from homeassistant.components.binary_sensor import ( @@ -28,6 +30,11 @@ SENSOR_TYPES = { "com.fibaro.smokeSensor": ["Smoke", "mdi:smoking", BinarySensorDeviceClass.SMOKE], "com.fibaro.FGMS001": ["Motion", "mdi:run", BinarySensorDeviceClass.MOTION], "com.fibaro.heatDetector": ["Heat", "mdi:fire", BinarySensorDeviceClass.HEAT], + "com.fibaro.accelerometer": [ + "Moving", + "mdi:axis-arrow", + BinarySensorDeviceClass.MOVING, + ], } @@ -55,15 +62,50 @@ class FibaroBinarySensor(FibaroDevice, BinarySensorEntity): """Initialize the binary_sensor.""" super().__init__(fibaro_device) self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id) - stype = None + self._own_extra_state_attributes: Mapping[str, Any] = {} + self._fibaro_sensor_type = None if fibaro_device.type in SENSOR_TYPES: - stype = fibaro_device.type + self._fibaro_sensor_type = fibaro_device.type elif fibaro_device.baseType in SENSOR_TYPES: - stype = fibaro_device.baseType - if stype: - self._attr_device_class = SENSOR_TYPES[stype][2] - self._attr_icon = SENSOR_TYPES[stype][1] + self._fibaro_sensor_type = fibaro_device.baseType + if self._fibaro_sensor_type: + self._attr_device_class = SENSOR_TYPES[self._fibaro_sensor_type][2] + self._attr_icon = SENSOR_TYPES[self._fibaro_sensor_type][1] + + @property + def extra_state_attributes(self) -> Mapping[str, Any] | None: + """Return the extra state attributes of the device.""" + return super().extra_state_attributes | self._own_extra_state_attributes def update(self) -> None: """Get the latest data and update the state.""" - self._attr_is_on = self.current_binary_state + if self._fibaro_sensor_type == "com.fibaro.accelerometer": + # Accelerator sensors have values for the three axis x, y and z + moving_values = self._get_moving_values() + self._attr_is_on = self._is_moving(moving_values) + self._own_extra_state_attributes = self._get_xyz_moving(moving_values) + else: + self._attr_is_on = self.current_binary_state + + def _get_xyz_moving(self, moving_values: Mapping[str, Any]) -> Mapping[str, Any]: + """Return x y z values of the accelerator sensor value.""" + attrs = {} + for axis_name in ("x", "y", "z"): + attrs[axis_name] = float(moving_values[axis_name]) + return attrs + + def _is_moving(self, moving_values: Mapping[str, Any]) -> bool: + """Return that a moving is detected when one axis reports a value.""" + for axis_name in ("x", "y", "z"): + if float(moving_values[axis_name]) != 0: + return True + return False + + def _get_moving_values(self) -> Mapping[str, Any]: + """Get the moving values of the accelerator sensor in a dict.""" + value = self.fibaro_device.properties.value + if isinstance(value, str): + # HC2 returns dict as str + return json.loads(value) + # HC3 returns a real dict + return value From ee910bd0e41391e00ccd521fe7d605e494d33046 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Sun, 13 Nov 2022 01:22:59 +0800 Subject: [PATCH 0370/1033] Refactor camera stream settings (#81663) --- homeassistant/components/camera/__init__.py | 23 ++--- homeassistant/components/camera/prefs.py | 84 +++++++++---------- .../components/generic/config_flow.py | 14 +++- homeassistant/components/onvif/camera.py | 2 +- homeassistant/components/stream/__init__.py | 41 ++++----- homeassistant/components/stream/core.py | 19 +++-- homeassistant/components/stream/hls.py | 13 ++- homeassistant/components/stream/recorder.py | 7 +- tests/components/camera/test_init.py | 18 ++-- tests/components/stream/common.py | 12 ++- tests/components/stream/test_hls.py | 31 +++---- tests/components/stream/test_ll_hls.py | 21 +++-- tests/components/stream/test_recorder.py | 27 +++--- tests/components/stream/test_worker.py | 66 +++++++++++---- 14 files changed, 226 insertions(+), 152 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index da88dc49a5b..82d981fc0cc 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -5,7 +5,7 @@ import asyncio import collections from collections.abc import Awaitable, Callable, Iterable from contextlib import suppress -from dataclasses import dataclass +from dataclasses import asdict, dataclass from datetime import datetime, timedelta from enum import IntEnum from functools import partial @@ -74,7 +74,7 @@ from .const import ( # noqa: F401 StreamType, ) from .img_util import scale_jpeg_camera_image -from .prefs import CameraPreferences +from .prefs import CameraPreferences, DynamicStreamSettings # noqa: F401 _LOGGER = logging.getLogger(__name__) @@ -346,7 +346,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: ) prefs = CameraPreferences(hass) - await prefs.async_initialize() hass.data[DATA_CAMERA_PREFS] = prefs hass.http.register_view(CameraImageView(component)) @@ -361,13 +360,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def preload_stream(_event: Event) -> None: for camera in component.entities: - camera_prefs = prefs.get(camera.entity_id) - if not camera_prefs.preload_stream: + stream_prefs = await prefs.get_dynamic_stream_settings(camera.entity_id) + if not stream_prefs.preload_stream: continue stream = await camera.async_create_stream() if not stream: continue - stream.keepalive = True stream.add_provider("hls") await stream.start() @@ -524,6 +522,9 @@ class Camera(Entity): self.hass, source, options=self.stream_options, + dynamic_stream_settings=await self.hass.data[ + DATA_CAMERA_PREFS + ].get_dynamic_stream_settings(self.entity_id), stream_label=self.entity_id, ) self.stream.set_update_callback(self.async_write_ha_state) @@ -861,8 +862,8 @@ async def websocket_get_prefs( ) -> None: """Handle request for account info.""" prefs: CameraPreferences = hass.data[DATA_CAMERA_PREFS] - camera_prefs = prefs.get(msg["entity_id"]) - connection.send_result(msg["id"], camera_prefs.as_dict()) + stream_prefs = await prefs.get_dynamic_stream_settings(msg["entity_id"]) + connection.send_result(msg["id"], asdict(stream_prefs)) @websocket_api.websocket_command( @@ -956,12 +957,6 @@ async def _async_stream_endpoint_url( f"{camera.entity_id} does not support play stream service" ) - # Update keepalive setting which manages idle shutdown - prefs: CameraPreferences = hass.data[DATA_CAMERA_PREFS] - camera_prefs = prefs.get(camera.entity_id) - stream.keepalive = camera_prefs.preload_stream - stream.orientation = camera_prefs.orientation - stream.add_provider(fmt) await stream.start() return stream.endpoint_url(fmt) diff --git a/homeassistant/components/camera/prefs.py b/homeassistant/components/camera/prefs.py index fac93df474e..0a8785457e8 100644 --- a/homeassistant/components/camera/prefs.py +++ b/homeassistant/components/camera/prefs.py @@ -1,6 +1,7 @@ """Preference management for camera component.""" from __future__ import annotations +from dataclasses import asdict, dataclass from typing import Final, Union, cast from homeassistant.components.stream import Orientation @@ -16,28 +17,12 @@ STORAGE_KEY: Final = DOMAIN STORAGE_VERSION: Final = 1 -class CameraEntityPreferences: - """Handle preferences for camera entity.""" +@dataclass +class DynamicStreamSettings: + """Stream settings which are managed and updated by the camera entity.""" - def __init__(self, prefs: dict[str, bool | Orientation]) -> None: - """Initialize prefs.""" - self._prefs = prefs - - def as_dict(self) -> dict[str, bool | Orientation]: - """Return dictionary version.""" - return self._prefs - - @property - def preload_stream(self) -> bool: - """Return if stream is loaded on hass start.""" - return cast(bool, self._prefs.get(PREF_PRELOAD_STREAM, False)) - - @property - def orientation(self) -> Orientation: - """Return the current stream orientation settings.""" - return cast( - Orientation, self._prefs.get(PREF_ORIENTATION, Orientation.NO_TRANSFORM) - ) + preload_stream: bool = False + orientation: Orientation = Orientation.NO_TRANSFORM class CameraPreferences: @@ -51,15 +36,9 @@ class CameraPreferences: self._store = Store[dict[str, dict[str, Union[bool, Orientation]]]]( hass, STORAGE_VERSION, STORAGE_KEY ) - # Local copy of the preload_stream prefs - self._prefs: dict[str, dict[str, bool | Orientation]] | None = None - - async def async_initialize(self) -> None: - """Finish initializing the preferences.""" - if (prefs := await self._store.async_load()) is None: - prefs = {} - - self._prefs = prefs + self._dynamic_stream_settings_by_entity_id: dict[ + str, DynamicStreamSettings + ] = {} async def async_update( self, @@ -67,20 +46,25 @@ class CameraPreferences: *, preload_stream: bool | UndefinedType = UNDEFINED, orientation: Orientation | UndefinedType = UNDEFINED, - stream_options: dict[str, str] | UndefinedType = UNDEFINED, ) -> dict[str, bool | Orientation]: """Update camera preferences. + Also update the DynamicStreamSettings if they exist. + preload_stream is stored in a Store + orientation is stored in the Entity Registry + Returns a dict with the preferences on success. Raises HomeAssistantError on failure. """ + dynamic_stream_settings = self._dynamic_stream_settings_by_entity_id.get( + entity_id + ) if preload_stream is not UNDEFINED: - # Prefs already initialized. - assert self._prefs is not None - if not self._prefs.get(entity_id): - self._prefs[entity_id] = {} - self._prefs[entity_id][PREF_PRELOAD_STREAM] = preload_stream - await self._store.async_save(self._prefs) + if dynamic_stream_settings: + dynamic_stream_settings.preload_stream = preload_stream + preload_prefs = await self._store.async_load() or {} + preload_prefs[entity_id] = {PREF_PRELOAD_STREAM: preload_stream} + await self._store.async_save(preload_prefs) if orientation is not UNDEFINED: if (registry := er.async_get(self._hass)).async_get(entity_id): @@ -91,12 +75,26 @@ class CameraPreferences: raise HomeAssistantError( "Orientation is only supported on entities set up through config flows" ) - return self.get(entity_id).as_dict() + if dynamic_stream_settings: + dynamic_stream_settings.orientation = orientation + return asdict(await self.get_dynamic_stream_settings(entity_id)) - def get(self, entity_id: str) -> CameraEntityPreferences: - """Get preferences for an entity.""" - # Prefs are already initialized. - assert self._prefs is not None + async def get_dynamic_stream_settings( + self, entity_id: str + ) -> DynamicStreamSettings: + """Get the DynamicStreamSettings for the entity.""" + if settings := self._dynamic_stream_settings_by_entity_id.get(entity_id): + return settings + # Get preload stream setting from prefs + # Get orientation setting from entity registry reg_entry = er.async_get(self._hass).async_get(entity_id) er_prefs = reg_entry.options.get(DOMAIN, {}) if reg_entry else {} - return CameraEntityPreferences(self._prefs.get(entity_id, {}) | er_prefs) + preload_prefs = await self._store.async_load() or {} + settings = DynamicStreamSettings( + preload_stream=cast( + bool, preload_prefs.get(entity_id, {}).get(PREF_PRELOAD_STREAM, False) + ), + orientation=er_prefs.get(PREF_ORIENTATION, Orientation.NO_TRANSFORM), + ) + self._dynamic_stream_settings_by_entity_id[entity_id] = settings + return settings diff --git a/homeassistant/components/generic/config_flow.py b/homeassistant/components/generic/config_flow.py index 09f52705734..6fa01ba369e 100644 --- a/homeassistant/components/generic/config_flow.py +++ b/homeassistant/components/generic/config_flow.py @@ -16,7 +16,11 @@ from httpx import HTTPStatusError, RequestError, TimeoutException import voluptuous as vol import yarl -from homeassistant.components.camera import CAMERA_IMAGE_TIMEOUT, _async_get_image +from homeassistant.components.camera import ( + CAMERA_IMAGE_TIMEOUT, + DynamicStreamSettings, + _async_get_image, +) from homeassistant.components.http.view import HomeAssistantView from homeassistant.components.stream import ( CONF_RTSP_TRANSPORT, @@ -246,7 +250,13 @@ async def async_test_stream( url = url.with_user(username).with_password(password) stream_source = str(url) try: - stream = create_stream(hass, stream_source, stream_options, "test_stream") + stream = create_stream( + hass, + stream_source, + stream_options, + DynamicStreamSettings(), + "test_stream", + ) hls_provider = stream.add_provider(HLS_PROVIDER) await stream.start() if not await hls_provider.part_recv(timeout=SOURCE_TIMEOUT): diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index 9a8535f2599..11699731b2f 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -137,7 +137,7 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera): ) -> bytes | None: """Return a still image response from the camera.""" - if self.stream and self.stream.keepalive: + if self.stream and self.stream.dynamic_stream_settings.preload_stream: return await self.stream.async_get_image(width, height) if self.device.capabilities.snapshot: diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index 01cd3c2962c..7e6f663fc28 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -25,7 +25,7 @@ import secrets import threading import time from types import MappingProxyType -from typing import Any, Final, cast +from typing import TYPE_CHECKING, Any, Final, cast import voluptuous as vol @@ -70,6 +70,9 @@ from .core import ( from .diagnostics import Diagnostics from .hls import HlsStreamOutput, async_setup_hls +if TYPE_CHECKING: + from homeassistant.components.camera import DynamicStreamSettings + __all__ = [ "ATTR_SETTINGS", "CONF_EXTRA_PART_WAIT_TIME", @@ -105,6 +108,7 @@ def create_stream( hass: HomeAssistant, stream_source: str, options: Mapping[str, str | bool | float], + dynamic_stream_settings: DynamicStreamSettings, stream_label: str | None = None, ) -> Stream: """Create a stream with the specified identfier based on the source url. @@ -156,6 +160,7 @@ def create_stream( stream_source, pyav_options=pyav_options, stream_settings=stream_settings, + dynamic_stream_settings=dynamic_stream_settings, stream_label=stream_label, ) hass.data[DOMAIN][ATTR_STREAMS].append(stream) @@ -231,7 +236,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: part_target_duration=conf[CONF_PART_DURATION], hls_advance_part_limit=max(int(3 / conf[CONF_PART_DURATION]), 3), hls_part_timeout=2 * conf[CONF_PART_DURATION], - orientation=Orientation.NO_TRANSFORM, ) else: hass.data[DOMAIN][ATTR_SETTINGS] = STREAM_SETTINGS_NON_LL_HLS @@ -246,7 +250,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def shutdown(event: Event) -> None: """Stop all stream workers.""" for stream in hass.data[DOMAIN][ATTR_STREAMS]: - stream.keepalive = False + stream.dynamic_stream_settings.preload_stream = False if awaitables := [ asyncio.create_task(stream.stop()) for stream in hass.data[DOMAIN][ATTR_STREAMS] @@ -268,6 +272,7 @@ class Stream: source: str, pyav_options: dict[str, str], stream_settings: StreamSettings, + dynamic_stream_settings: DynamicStreamSettings, stream_label: str | None = None, ) -> None: """Initialize a stream.""" @@ -276,14 +281,16 @@ class Stream: self.pyav_options = pyav_options self._stream_settings = stream_settings self._stream_label = stream_label - self.keepalive = False + self.dynamic_stream_settings = dynamic_stream_settings self.access_token: str | None = None self._start_stop_lock = asyncio.Lock() self._thread: threading.Thread | None = None self._thread_quit = threading.Event() self._outputs: dict[str, StreamOutput] = {} self._fast_restart_once = False - self._keyframe_converter = KeyFrameConverter(hass, stream_settings) + self._keyframe_converter = KeyFrameConverter( + hass, stream_settings, dynamic_stream_settings + ) self._available: bool = True self._update_callback: Callable[[], None] | None = None self._logger = ( @@ -293,16 +300,6 @@ class Stream: ) self._diagnostics = Diagnostics() - @property - def orientation(self) -> Orientation: - """Return the current orientation setting.""" - return self._stream_settings.orientation - - @orientation.setter - def orientation(self, value: Orientation) -> None: - """Set the stream orientation setting.""" - self._stream_settings.orientation = value - def endpoint_url(self, fmt: str) -> str: """Start the stream and returns a url for the output format.""" if fmt not in self._outputs: @@ -326,7 +323,8 @@ class Stream: async def idle_callback() -> None: if ( - not self.keepalive or fmt == RECORDER_PROVIDER + not self.dynamic_stream_settings.preload_stream + or fmt == RECORDER_PROVIDER ) and fmt in self._outputs: await self.remove_provider(self._outputs[fmt]) self.check_idle() @@ -335,6 +333,7 @@ class Stream: self.hass, IdleTimer(self.hass, timeout, idle_callback), self._stream_settings, + self.dynamic_stream_settings, ) self._outputs[fmt] = provider @@ -413,8 +412,12 @@ class Stream: while not self._thread_quit.wait(timeout=wait_timeout): start_time = time.time() self.hass.add_job(self._async_update_state, True) - self._diagnostics.set_value("keepalive", self.keepalive) - self._diagnostics.set_value("orientation", self.orientation) + self._diagnostics.set_value( + "keepalive", self.dynamic_stream_settings.preload_stream + ) + self._diagnostics.set_value( + "orientation", self.dynamic_stream_settings.orientation + ) self._diagnostics.increment("start_worker") try: stream_worker( @@ -473,7 +476,7 @@ class Stream: self._outputs = {} self.access_token = None - if not self.keepalive: + if not self.dynamic_stream_settings.preload_stream: await self._stop() async def _stop(self) -> None: diff --git a/homeassistant/components/stream/core.py b/homeassistant/components/stream/core.py index 31654f7d0db..a21a9f17d96 100644 --- a/homeassistant/components/stream/core.py +++ b/homeassistant/components/stream/core.py @@ -29,6 +29,8 @@ from .const import ( if TYPE_CHECKING: from av import CodecContext, Packet + from homeassistant.components.camera import DynamicStreamSettings + from . import Stream _LOGGER = logging.getLogger(__name__) @@ -58,7 +60,6 @@ class StreamSettings: part_target_duration: float = attr.ib() hls_advance_part_limit: int = attr.ib() hls_part_timeout: float = attr.ib() - orientation: Orientation = attr.ib() STREAM_SETTINGS_NON_LL_HLS = StreamSettings( @@ -67,7 +68,6 @@ STREAM_SETTINGS_NON_LL_HLS = StreamSettings( part_target_duration=TARGET_SEGMENT_DURATION_NON_LL_HLS, hls_advance_part_limit=3, hls_part_timeout=TARGET_SEGMENT_DURATION_NON_LL_HLS, - orientation=Orientation.NO_TRANSFORM, ) @@ -273,12 +273,14 @@ class StreamOutput: hass: HomeAssistant, idle_timer: IdleTimer, stream_settings: StreamSettings, + dynamic_stream_settings: DynamicStreamSettings, deque_maxlen: int | None = None, ) -> None: """Initialize a stream output.""" self._hass = hass self.idle_timer = idle_timer self.stream_settings = stream_settings + self.dynamic_stream_settings = dynamic_stream_settings self._event = asyncio.Event() self._part_event = asyncio.Event() self._segments: deque[Segment] = deque(maxlen=deque_maxlen) @@ -427,7 +429,12 @@ class KeyFrameConverter: If unsuccessful, get_image will return the previous image """ - def __init__(self, hass: HomeAssistant, stream_settings: StreamSettings) -> None: + def __init__( + self, + hass: HomeAssistant, + stream_settings: StreamSettings, + dynamic_stream_settings: DynamicStreamSettings, + ) -> None: """Initialize.""" # Keep import here so that we can import stream integration without installing reqs @@ -441,6 +448,7 @@ class KeyFrameConverter: self._lock = asyncio.Lock() self._codec_context: CodecContext | None = None self._stream_settings = stream_settings + self._dynamic_stream_settings = dynamic_stream_settings def create_codec_context(self, codec_context: CodecContext) -> None: """ @@ -498,12 +506,13 @@ class KeyFrameConverter: if frames: frame = frames[0] if width and height: - if self._stream_settings.orientation >= 5: + if self._dynamic_stream_settings.orientation >= 5: frame = frame.reformat(width=height, height=width) else: frame = frame.reformat(width=width, height=height) bgr_array = self.transform_image( - frame.to_ndarray(format="bgr24"), self._stream_settings.orientation + frame.to_ndarray(format="bgr24"), + self._dynamic_stream_settings.orientation, ) self._image = bytes(self._turbojpeg.encode(bgr_array)) diff --git a/homeassistant/components/stream/hls.py b/homeassistant/components/stream/hls.py index e8920abcaa6..cddb4413ed8 100644 --- a/homeassistant/components/stream/hls.py +++ b/homeassistant/components/stream/hls.py @@ -27,6 +27,8 @@ from .core import ( from .fmp4utils import get_codec_string, transform_init if TYPE_CHECKING: + from homeassistant.components.camera import DynamicStreamSettings + from . import Stream @@ -50,9 +52,16 @@ class HlsStreamOutput(StreamOutput): hass: HomeAssistant, idle_timer: IdleTimer, stream_settings: StreamSettings, + dynamic_stream_settings: DynamicStreamSettings, ) -> None: """Initialize HLS output.""" - super().__init__(hass, idle_timer, stream_settings, deque_maxlen=MAX_SEGMENTS) + super().__init__( + hass, + idle_timer, + stream_settings, + dynamic_stream_settings, + deque_maxlen=MAX_SEGMENTS, + ) self._target_duration = stream_settings.min_segment_duration @property @@ -339,7 +348,7 @@ class HlsInitView(StreamView): if not (segments := track.get_segments()) or not (body := segments[0].init): return web.HTTPNotFound() return web.Response( - body=transform_init(body, stream.orientation), + body=transform_init(body, stream.dynamic_stream_settings.orientation), headers={"Content-Type": "video/mp4"}, ) diff --git a/homeassistant/components/stream/recorder.py b/homeassistant/components/stream/recorder.py index e917292251a..fffbd489757 100644 --- a/homeassistant/components/stream/recorder.py +++ b/homeassistant/components/stream/recorder.py @@ -21,6 +21,8 @@ from .fmp4utils import read_init, transform_init if TYPE_CHECKING: import deque + from homeassistant.components.camera import DynamicStreamSettings + _LOGGER = logging.getLogger(__name__) @@ -38,9 +40,10 @@ class RecorderOutput(StreamOutput): hass: HomeAssistant, idle_timer: IdleTimer, stream_settings: StreamSettings, + dynamic_stream_settings: DynamicStreamSettings, ) -> None: """Initialize recorder output.""" - super().__init__(hass, idle_timer, stream_settings) + super().__init__(hass, idle_timer, stream_settings, dynamic_stream_settings) self.video_path: str @property @@ -154,7 +157,7 @@ class RecorderOutput(StreamOutput): video_path, mode="wb" ) as out_file: init = transform_init( - read_init(in_file), self.stream_settings.orientation + read_init(in_file), self.dynamic_stream_settings.orientation ) out_file.write(init) in_file.seek(len(init)) diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py index 9bf35ec55fa..3a78740684a 100644 --- a/tests/components/camera/test_init.py +++ b/tests/components/camera/test_init.py @@ -12,7 +12,6 @@ from homeassistant.components.camera.const import ( PREF_ORIENTATION, PREF_PRELOAD_STREAM, ) -from homeassistant.components.camera.prefs import CameraEntityPreferences from homeassistant.components.websocket_api.const import TYPE_RESULT from homeassistant.config import async_process_ha_core_config from homeassistant.const import ( @@ -302,8 +301,9 @@ async def test_websocket_update_preload_prefs(hass, hass_ws_client, mock_camera) ) msg = await client.receive_json() - # There should be no preferences - assert not msg["result"] + # The default prefs should be returned. Preload stream should be False + assert msg["success"] + assert msg["result"][PREF_PRELOAD_STREAM] is False # Update the preference await client.send_json( @@ -421,12 +421,12 @@ async def test_handle_play_stream_service(hass, mock_camera, mock_stream): async def test_no_preload_stream(hass, mock_stream): """Test camera preload preference.""" - demo_prefs = CameraEntityPreferences({PREF_PRELOAD_STREAM: False}) + demo_settings = camera.DynamicStreamSettings() with patch( "homeassistant.components.camera.Stream.endpoint_url", ) as mock_request_stream, patch( - "homeassistant.components.camera.prefs.CameraPreferences.get", - return_value=demo_prefs, + "homeassistant.components.camera.prefs.CameraPreferences.get_dynamic_stream_settings", + return_value=demo_settings, ), patch( "homeassistant.components.demo.camera.DemoCamera.stream_source", new_callable=PropertyMock, @@ -440,12 +440,12 @@ async def test_no_preload_stream(hass, mock_stream): async def test_preload_stream(hass, mock_stream): """Test camera preload preference.""" - demo_prefs = CameraEntityPreferences({PREF_PRELOAD_STREAM: True}) + demo_settings = camera.DynamicStreamSettings(preload_stream=True) with patch( "homeassistant.components.camera.create_stream" ) as mock_create_stream, patch( - "homeassistant.components.camera.prefs.CameraPreferences.get", - return_value=demo_prefs, + "homeassistant.components.camera.prefs.CameraPreferences.get_dynamic_stream_settings", + return_value=demo_settings, ), patch( "homeassistant.components.demo.camera.DemoCamera.stream_source", return_value="http://example.com", diff --git a/tests/components/stream/common.py b/tests/components/stream/common.py index de5b2c234eb..ff98d90ea8d 100644 --- a/tests/components/stream/common.py +++ b/tests/components/stream/common.py @@ -8,7 +8,8 @@ import io import av import numpy as np -from homeassistant.components.stream.core import Segment +from homeassistant.components.camera import DynamicStreamSettings +from homeassistant.components.stream.core import Orientation, Segment from homeassistant.components.stream.fmp4utils import ( TRANSFORM_MATRIX_TOP, XYW_ROW, @@ -16,8 +17,8 @@ from homeassistant.components.stream.fmp4utils import ( ) FAKE_TIME = datetime.utcnow() -# Segment with defaults filled in for use in tests +# Segment with defaults filled in for use in tests DefaultSegment = partial( Segment, init=None, @@ -157,7 +158,7 @@ def remux_with_audio(source, container_format, audio_codec): return output -def assert_mp4_has_transform_matrix(mp4: bytes, orientation: int): +def assert_mp4_has_transform_matrix(mp4: bytes, orientation: Orientation): """Assert that the mp4 (or init) has the proper transformation matrix.""" # Find moov moov_location = next(find_box(mp4, b"moov")) @@ -170,3 +171,8 @@ def assert_mp4_has_transform_matrix(mp4: bytes, orientation: int): mp4[tkhd_location + tkhd_length - 44 : tkhd_location + tkhd_length - 8] == TRANSFORM_MATRIX_TOP[orientation] + XYW_ROW ) + + +def dynamic_stream_settings(): + """Create new dynamic stream settings.""" + return DynamicStreamSettings() diff --git a/tests/components/stream/test_hls.py b/tests/components/stream/test_hls.py index e9da793369f..cd4f5796938 100644 --- a/tests/components/stream/test_hls.py +++ b/tests/components/stream/test_hls.py @@ -16,7 +16,7 @@ from homeassistant.components.stream.const import ( MAX_SEGMENTS, NUM_PLAYLIST_SEGMENTS, ) -from homeassistant.components.stream.core import Part +from homeassistant.components.stream.core import Orientation, Part from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -24,6 +24,7 @@ from .common import ( FAKE_TIME, DefaultSegment as Segment, assert_mp4_has_transform_matrix, + dynamic_stream_settings, ) from tests.common import async_fire_time_changed @@ -145,7 +146,7 @@ async def test_hls_stream( stream_worker_sync.pause() # Setup demo HLS track - stream = create_stream(hass, h264_video, {}) + stream = create_stream(hass, h264_video, {}, dynamic_stream_settings()) # Request stream stream.add_provider(HLS_PROVIDER) @@ -185,7 +186,7 @@ async def test_hls_stream( assert stream.get_diagnostics() == { "container_format": "mov,mp4,m4a,3gp,3g2,mj2", "keepalive": False, - "orientation": 1, + "orientation": Orientation.NO_TRANSFORM, "start_worker": 1, "video_codec": "h264", "worker_error": 1, @@ -199,7 +200,7 @@ async def test_stream_timeout( stream_worker_sync.pause() # Setup demo HLS track - stream = create_stream(hass, h264_video, {}) + stream = create_stream(hass, h264_video, {}, dynamic_stream_settings()) available_states = [] @@ -252,7 +253,7 @@ async def test_stream_timeout_after_stop( stream_worker_sync.pause() # Setup demo HLS track - stream = create_stream(hass, h264_video, {}) + stream = create_stream(hass, h264_video, {}, dynamic_stream_settings()) # Request stream stream.add_provider(HLS_PROVIDER) @@ -272,7 +273,7 @@ async def test_stream_retries(hass, setup_component, should_retry): """Test hls stream is retried on failure.""" # Setup demo HLS track source = "test_stream_keepalive_source" - stream = create_stream(hass, source, {}) + stream = create_stream(hass, source, {}, dynamic_stream_settings()) track = stream.add_provider(HLS_PROVIDER) track.num_segments = 2 @@ -320,7 +321,7 @@ async def test_stream_retries(hass, setup_component, should_retry): async def test_hls_playlist_view_no_output(hass, setup_component, hls_stream): """Test rendering the hls playlist with no output segments.""" - stream = create_stream(hass, STREAM_SOURCE, {}) + stream = create_stream(hass, STREAM_SOURCE, {}, dynamic_stream_settings()) stream.add_provider(HLS_PROVIDER) hls_client = await hls_stream(stream) @@ -332,7 +333,7 @@ async def test_hls_playlist_view_no_output(hass, setup_component, hls_stream): async def test_hls_playlist_view(hass, setup_component, hls_stream, stream_worker_sync): """Test rendering the hls playlist with 1 and 2 output segments.""" - stream = create_stream(hass, STREAM_SOURCE, {}) + stream = create_stream(hass, STREAM_SOURCE, {}, dynamic_stream_settings()) stream_worker_sync.pause() hls = stream.add_provider(HLS_PROVIDER) for i in range(2): @@ -363,7 +364,7 @@ async def test_hls_playlist_view(hass, setup_component, hls_stream, stream_worke async def test_hls_max_segments(hass, setup_component, hls_stream, stream_worker_sync): """Test rendering the hls playlist with more segments than the segment deque can hold.""" - stream = create_stream(hass, STREAM_SOURCE, {}) + stream = create_stream(hass, STREAM_SOURCE, {}, dynamic_stream_settings()) stream_worker_sync.pause() hls = stream.add_provider(HLS_PROVIDER) @@ -415,7 +416,7 @@ async def test_hls_playlist_view_discontinuity( ): """Test a discontinuity across segments in the stream with 3 segments.""" - stream = create_stream(hass, STREAM_SOURCE, {}) + stream = create_stream(hass, STREAM_SOURCE, {}, dynamic_stream_settings()) stream_worker_sync.pause() hls = stream.add_provider(HLS_PROVIDER) @@ -452,7 +453,7 @@ async def test_hls_max_segments_discontinuity( hass, setup_component, hls_stream, stream_worker_sync ): """Test a discontinuity with more segments than the segment deque can hold.""" - stream = create_stream(hass, STREAM_SOURCE, {}) + stream = create_stream(hass, STREAM_SOURCE, {}, dynamic_stream_settings()) stream_worker_sync.pause() hls = stream.add_provider(HLS_PROVIDER) @@ -495,7 +496,7 @@ async def test_remove_incomplete_segment_on_exit( hass, setup_component, stream_worker_sync ): """Test that the incomplete segment gets removed when the worker thread quits.""" - stream = create_stream(hass, STREAM_SOURCE, {}) + stream = create_stream(hass, STREAM_SOURCE, {}, dynamic_stream_settings()) stream_worker_sync.pause() await stream.start() hls = stream.add_provider(HLS_PROVIDER) @@ -536,7 +537,7 @@ async def test_hls_stream_rotate( stream_worker_sync.pause() # Setup demo HLS track - stream = create_stream(hass, h264_video, {}) + stream = create_stream(hass, h264_video, {}, dynamic_stream_settings()) # Request stream stream.add_provider(HLS_PROVIDER) @@ -549,14 +550,14 @@ async def test_hls_stream_rotate( assert master_playlist_response.status == HTTPStatus.OK # Fetch rotated init - stream.orientation = 6 + stream.dynamic_stream_settings.orientation = Orientation.ROTATE_LEFT init_response = await hls_client.get("/init.mp4") assert init_response.status == HTTPStatus.OK init = await init_response.read() stream_worker_sync.resume() - assert_mp4_has_transform_matrix(init, stream.orientation) + assert_mp4_has_transform_matrix(init, stream.dynamic_stream_settings.orientation) # Stop stream, if it hasn't quit already await stream.stop() diff --git a/tests/components/stream/test_ll_hls.py b/tests/components/stream/test_ll_hls.py index baad3043547..448c3593d68 100644 --- a/tests/components/stream/test_ll_hls.py +++ b/tests/components/stream/test_ll_hls.py @@ -22,7 +22,12 @@ from homeassistant.components.stream.const import ( from homeassistant.components.stream.core import Part from homeassistant.setup import async_setup_component -from .common import FAKE_TIME, DefaultSegment as Segment, generate_h264_video +from .common import ( + FAKE_TIME, + DefaultSegment as Segment, + dynamic_stream_settings, + generate_h264_video, +) from .test_hls import STREAM_SOURCE, HlsClient, make_playlist SEGMENT_DURATION = 6 @@ -135,7 +140,7 @@ async def test_ll_hls_stream(hass, hls_stream, stream_worker_sync): num_playlist_segments = 3 # Setup demo HLS track source = generate_h264_video(duration=num_playlist_segments * SEGMENT_DURATION + 2) - stream = create_stream(hass, source, {}) + stream = create_stream(hass, source, {}, dynamic_stream_settings()) # Request stream stream.add_provider(HLS_PROVIDER) @@ -259,7 +264,7 @@ async def test_ll_hls_playlist_view(hass, hls_stream, stream_worker_sync): }, ) - stream = create_stream(hass, STREAM_SOURCE, {}) + stream = create_stream(hass, STREAM_SOURCE, {}, dynamic_stream_settings()) stream_worker_sync.pause() hls = stream.add_provider(HLS_PROVIDER) @@ -328,7 +333,7 @@ async def test_ll_hls_msn(hass, hls_stream, stream_worker_sync, hls_sync): }, ) - stream = create_stream(hass, STREAM_SOURCE, {}) + stream = create_stream(hass, STREAM_SOURCE, {}, dynamic_stream_settings()) stream_worker_sync.pause() hls = stream.add_provider(HLS_PROVIDER) @@ -393,7 +398,7 @@ async def test_ll_hls_playlist_bad_msn_part(hass, hls_stream, stream_worker_sync }, ) - stream = create_stream(hass, STREAM_SOURCE, {}) + stream = create_stream(hass, STREAM_SOURCE, {}, dynamic_stream_settings()) stream_worker_sync.pause() hls = stream.add_provider(HLS_PROVIDER) @@ -462,7 +467,7 @@ async def test_ll_hls_playlist_rollover_part( }, ) - stream = create_stream(hass, STREAM_SOURCE, {}) + stream = create_stream(hass, STREAM_SOURCE, {}, dynamic_stream_settings()) stream_worker_sync.pause() hls = stream.add_provider(HLS_PROVIDER) @@ -541,7 +546,7 @@ async def test_ll_hls_playlist_msn_part(hass, hls_stream, stream_worker_sync, hl }, ) - stream = create_stream(hass, STREAM_SOURCE, {}) + stream = create_stream(hass, STREAM_SOURCE, {}, dynamic_stream_settings()) stream_worker_sync.pause() hls = stream.add_provider(HLS_PROVIDER) @@ -607,7 +612,7 @@ async def test_get_part_segments(hass, hls_stream, stream_worker_sync, hls_sync) }, ) - stream = create_stream(hass, STREAM_SOURCE, {}) + stream = create_stream(hass, STREAM_SOURCE, {}, dynamic_stream_settings()) stream_worker_sync.pause() hls = stream.add_provider(HLS_PROVIDER) diff --git a/tests/components/stream/test_recorder.py b/tests/components/stream/test_recorder.py index c07675c7712..1e941dd5d81 100644 --- a/tests/components/stream/test_recorder.py +++ b/tests/components/stream/test_recorder.py @@ -14,7 +14,7 @@ from homeassistant.components.stream.const import ( OUTPUT_IDLE_TIMEOUT, RECORDER_PROVIDER, ) -from homeassistant.components.stream.core import Part +from homeassistant.components.stream.core import Orientation, Part from homeassistant.components.stream.fmp4utils import find_box from homeassistant.exceptions import HomeAssistantError from homeassistant.setup import async_setup_component @@ -23,6 +23,7 @@ import homeassistant.util.dt as dt_util from .common import ( DefaultSegment as Segment, assert_mp4_has_transform_matrix, + dynamic_stream_settings, generate_h264_video, remux_with_audio, ) @@ -56,7 +57,7 @@ async def test_record_stream(hass, filename, h264_video): worker_finished.set() with patch("homeassistant.components.stream.Stream", wraps=MockStream): - stream = create_stream(hass, h264_video, {}) + stream = create_stream(hass, h264_video, {}, dynamic_stream_settings()) with patch.object(hass.config, "is_allowed_path", return_value=True): make_recording = hass.async_create_task(stream.async_record(filename)) @@ -79,7 +80,7 @@ async def test_record_stream(hass, filename, h264_video): async def test_record_lookback(hass, filename, h264_video): """Exercise record with lookback.""" - stream = create_stream(hass, h264_video, {}) + stream = create_stream(hass, h264_video, {}, dynamic_stream_settings()) # Start an HLS feed to enable lookback stream.add_provider(HLS_PROVIDER) @@ -96,7 +97,7 @@ async def test_record_lookback(hass, filename, h264_video): async def test_record_path_not_allowed(hass, h264_video): """Test where the output path is not allowed by home assistant configuration.""" - stream = create_stream(hass, h264_video, {}) + stream = create_stream(hass, h264_video, {}, dynamic_stream_settings()) with patch.object( hass.config, "is_allowed_path", return_value=False ), pytest.raises(HomeAssistantError): @@ -146,7 +147,7 @@ async def test_recorder_discontinuity(hass, filename, h264_video): with patch.object(hass.config, "is_allowed_path", return_value=True), patch( "homeassistant.components.stream.Stream", wraps=MockStream ), patch("homeassistant.components.stream.recorder.RecorderOutput.recv"): - stream = create_stream(hass, "blank", {}) + stream = create_stream(hass, "blank", {}, dynamic_stream_settings()) make_recording = hass.async_create_task(stream.async_record(filename)) await provider_ready.wait() @@ -166,7 +167,7 @@ async def test_recorder_discontinuity(hass, filename, h264_video): async def test_recorder_no_segments(hass, filename): """Test recorder behavior with a stream failure which causes no segments.""" - stream = create_stream(hass, BytesIO(), {}) + stream = create_stream(hass, BytesIO(), {}, dynamic_stream_settings()) # Run with patch.object(hass.config, "is_allowed_path", return_value=True): @@ -219,7 +220,7 @@ async def test_record_stream_audio( worker_finished.set() with patch("homeassistant.components.stream.Stream", wraps=MockStream): - stream = create_stream(hass, source, {}) + stream = create_stream(hass, source, {}, dynamic_stream_settings()) with patch.object(hass.config, "is_allowed_path", return_value=True): make_recording = hass.async_create_task(stream.async_record(filename)) @@ -252,7 +253,9 @@ async def test_record_stream_audio( async def test_recorder_log(hass, filename, caplog): """Test starting a stream to record logs the url without username and password.""" - stream = create_stream(hass, "https://abcd:efgh@foo.bar", {}) + stream = create_stream( + hass, "https://abcd:efgh@foo.bar", {}, dynamic_stream_settings() + ) with patch.object(hass.config, "is_allowed_path", return_value=True): await stream.async_record(filename) assert "https://abcd:efgh@foo.bar" not in caplog.text @@ -273,8 +276,8 @@ async def test_record_stream_rotate(hass, filename, h264_video): worker_finished.set() with patch("homeassistant.components.stream.Stream", wraps=MockStream): - stream = create_stream(hass, h264_video, {}) - stream.orientation = 8 + stream = create_stream(hass, h264_video, {}, dynamic_stream_settings()) + stream.dynamic_stream_settings.orientation = Orientation.ROTATE_RIGHT with patch.object(hass.config, "is_allowed_path", return_value=True): make_recording = hass.async_create_task(stream.async_record(filename)) @@ -293,4 +296,6 @@ async def test_record_stream_rotate(hass, filename, h264_video): # Assert assert os.path.exists(filename) with open(filename, "rb") as rotated_mp4: - assert_mp4_has_transform_matrix(rotated_mp4.read(), stream.orientation) + assert_mp4_has_transform_matrix( + rotated_mp4.read(), stream.dynamic_stream_settings.orientation + ) diff --git a/tests/components/stream/test_worker.py b/tests/components/stream/test_worker.py index e77b062fa9c..e9f5769ddf1 100644 --- a/tests/components/stream/test_worker.py +++ b/tests/components/stream/test_worker.py @@ -39,7 +39,7 @@ from homeassistant.components.stream.const import ( SEGMENT_DURATION_ADJUSTER, TARGET_SEGMENT_DURATION_NON_LL_HLS, ) -from homeassistant.components.stream.core import StreamSettings +from homeassistant.components.stream.core import Orientation, StreamSettings from homeassistant.components.stream.worker import ( StreamEndedError, StreamState, @@ -48,7 +48,7 @@ from homeassistant.components.stream.worker import ( ) from homeassistant.setup import async_setup_component -from .common import generate_h264_video, generate_h265_video +from .common import dynamic_stream_settings, generate_h264_video, generate_h265_video from .test_ll_hls import TEST_PART_DURATION from tests.components.camera.common import EMPTY_8_6_JPEG, mock_turbo_jpeg @@ -90,7 +90,6 @@ def mock_stream_settings(hass): part_target_duration=TARGET_SEGMENT_DURATION_NON_LL_HLS, hls_advance_part_limit=3, hls_part_timeout=TARGET_SEGMENT_DURATION_NON_LL_HLS, - orientation=1, ) } @@ -287,7 +286,7 @@ def run_worker(hass, stream, stream_source, stream_settings=None): {}, stream_settings or hass.data[DOMAIN][ATTR_SETTINGS], stream_state, - KeyFrameConverter(hass, 1), + KeyFrameConverter(hass, stream_settings, dynamic_stream_settings()), threading.Event(), ) @@ -295,7 +294,11 @@ def run_worker(hass, stream, stream_source, stream_settings=None): async def async_decode_stream(hass, packets, py_av=None, stream_settings=None): """Start a stream worker that decodes incoming stream packets into output segments.""" stream = Stream( - hass, STREAM_SOURCE, {}, stream_settings or hass.data[DOMAIN][ATTR_SETTINGS] + hass, + STREAM_SOURCE, + {}, + stream_settings or hass.data[DOMAIN][ATTR_SETTINGS], + dynamic_stream_settings(), ) stream.add_provider(HLS_PROVIDER) @@ -322,7 +325,13 @@ async def async_decode_stream(hass, packets, py_av=None, stream_settings=None): async def test_stream_open_fails(hass): """Test failure on stream open.""" - stream = Stream(hass, STREAM_SOURCE, {}, hass.data[DOMAIN][ATTR_SETTINGS]) + stream = Stream( + hass, + STREAM_SOURCE, + {}, + hass.data[DOMAIN][ATTR_SETTINGS], + dynamic_stream_settings(), + ) stream.add_provider(HLS_PROVIDER) with patch("av.open") as av_open, pytest.raises(StreamWorkerError): av_open.side_effect = av.error.InvalidDataError(-2, "error") @@ -636,7 +645,13 @@ async def test_stream_stopped_while_decoding(hass): worker_open = threading.Event() worker_wake = threading.Event() - stream = Stream(hass, STREAM_SOURCE, {}, hass.data[DOMAIN][ATTR_SETTINGS]) + stream = Stream( + hass, + STREAM_SOURCE, + {}, + hass.data[DOMAIN][ATTR_SETTINGS], + dynamic_stream_settings(), + ) stream.add_provider(HLS_PROVIDER) py_av = MockPyAv() @@ -666,7 +681,13 @@ async def test_update_stream_source(hass): worker_open = threading.Event() worker_wake = threading.Event() - stream = Stream(hass, STREAM_SOURCE, {}, hass.data[DOMAIN][ATTR_SETTINGS]) + stream = Stream( + hass, + STREAM_SOURCE, + {}, + hass.data[DOMAIN][ATTR_SETTINGS], + dynamic_stream_settings(), + ) stream.add_provider(HLS_PROVIDER) # Note that retries are disabled by default in tests, however the stream is "restarted" when # the stream source is updated. @@ -709,7 +730,11 @@ async def test_update_stream_source(hass): async def test_worker_log(hass, caplog): """Test that the worker logs the url without username and password.""" stream = Stream( - hass, "https://abcd:efgh@foo.bar", {}, hass.data[DOMAIN][ATTR_SETTINGS] + hass, + "https://abcd:efgh@foo.bar", + {}, + hass.data[DOMAIN][ATTR_SETTINGS], + dynamic_stream_settings(), ) stream.add_provider(HLS_PROVIDER) @@ -764,7 +789,9 @@ async def test_durations(hass, worker_finished_stream): worker_finished, mock_stream = worker_finished_stream with patch("homeassistant.components.stream.Stream", wraps=mock_stream): - stream = create_stream(hass, source, {}, stream_label="camera") + stream = create_stream( + hass, source, {}, dynamic_stream_settings(), stream_label="camera" + ) recorder_output = stream.add_provider(RECORDER_PROVIDER, timeout=30) await stream.start() @@ -839,7 +866,9 @@ async def test_has_keyframe(hass, h264_video, worker_finished_stream): worker_finished, mock_stream = worker_finished_stream with patch("homeassistant.components.stream.Stream", wraps=mock_stream): - stream = create_stream(hass, h264_video, {}, stream_label="camera") + stream = create_stream( + hass, h264_video, {}, dynamic_stream_settings(), stream_label="camera" + ) recorder_output = stream.add_provider(RECORDER_PROVIDER, timeout=30) await stream.start() @@ -880,7 +909,9 @@ async def test_h265_video_is_hvc1(hass, worker_finished_stream): worker_finished, mock_stream = worker_finished_stream with patch("homeassistant.components.stream.Stream", wraps=mock_stream): - stream = create_stream(hass, source, {}, stream_label="camera") + stream = create_stream( + hass, source, {}, dynamic_stream_settings(), stream_label="camera" + ) recorder_output = stream.add_provider(RECORDER_PROVIDER, timeout=30) await stream.start() @@ -900,7 +931,7 @@ async def test_h265_video_is_hvc1(hass, worker_finished_stream): assert stream.get_diagnostics() == { "container_format": "mov,mp4,m4a,3gp,3g2,mj2", "keepalive": False, - "orientation": 1, + "orientation": Orientation.NO_TRANSFORM, "start_worker": 1, "video_codec": "hevc", "worker_error": 1, @@ -916,7 +947,7 @@ async def test_get_image(hass, h264_video, filename): "homeassistant.components.camera.img_util.TurboJPEGSingleton" ) as mock_turbo_jpeg_singleton: mock_turbo_jpeg_singleton.instance.return_value = mock_turbo_jpeg() - stream = create_stream(hass, h264_video, {}) + stream = create_stream(hass, h264_video, {}, dynamic_stream_settings()) with patch.object(hass.config, "is_allowed_path", return_value=True): make_recording = hass.async_create_task(stream.async_record(filename)) @@ -937,7 +968,6 @@ async def test_worker_disable_ll_hls(hass): part_target_duration=TARGET_SEGMENT_DURATION_NON_LL_HLS, hls_advance_part_limit=3, hls_part_timeout=TARGET_SEGMENT_DURATION_NON_LL_HLS, - orientation=1, ) py_av = MockPyAv() py_av.container.format.name = "hls" @@ -959,9 +989,9 @@ async def test_get_image_rotated(hass, h264_video, filename): "homeassistant.components.camera.img_util.TurboJPEGSingleton" ) as mock_turbo_jpeg_singleton: mock_turbo_jpeg_singleton.instance.return_value = mock_turbo_jpeg() - for orientation in (1, 8): - stream = create_stream(hass, h264_video, {}) - stream._stream_settings.orientation = orientation + for orientation in (Orientation.NO_TRANSFORM, Orientation.ROTATE_RIGHT): + stream = create_stream(hass, h264_video, {}, dynamic_stream_settings()) + stream.dynamic_stream_settings.orientation = orientation with patch.object(hass.config, "is_allowed_path", return_value=True): make_recording = hass.async_create_task(stream.async_record(filename)) From 4bb1f4ec7909c25704a715c8798b3a1b05185ee6 Mon Sep 17 00:00:00 2001 From: On Freund Date: Sat, 12 Nov 2022 22:29:31 +0200 Subject: [PATCH 0371/1033] Add Armed binary sensor to local Risco (#81997) --- .../components/risco/binary_sensor.py | 25 +++++- homeassistant/components/risco/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/risco/conftest.py | 4 + tests/components/risco/test_binary_sensor.py | 86 +++++++++++-------- 6 files changed, 82 insertions(+), 39 deletions(-) diff --git a/homeassistant/components/risco/binary_sensor.py b/homeassistant/components/risco/binary_sensor.py index b1f55dd8693..7c9733e2ab9 100644 --- a/homeassistant/components/risco/binary_sensor.py +++ b/homeassistant/components/risco/binary_sensor.py @@ -35,6 +35,10 @@ async def async_setup_entry( RiscoLocalAlarmedBinarySensor(local_data.system.id, zone_id, zone) for zone_id, zone in local_data.system.zones.items() ) + async_add_entities( + RiscoLocalArmedBinarySensor(local_data.system.id, zone_id, zone) + for zone_id, zone in local_data.system.zones.items() + ) else: coordinator: RiscoDataUpdateCoordinator = hass.data[DOMAIN][ config_entry.entry_id @@ -92,8 +96,6 @@ class RiscoLocalBinarySensor(RiscoLocalZoneEntity, BinarySensorEntity): class RiscoLocalAlarmedBinarySensor(RiscoLocalZoneEntity, BinarySensorEntity): """Representation whether a zone in Risco local is currently triggering an alarm.""" - _attr_should_poll = False - def __init__(self, system_id: str, zone_id: int, zone: Zone) -> None: """Init the zone.""" super().__init__( @@ -108,3 +110,22 @@ class RiscoLocalAlarmedBinarySensor(RiscoLocalZoneEntity, BinarySensorEntity): def is_on(self) -> bool | None: """Return true if sensor is on.""" return self._zone.alarmed + + +class RiscoLocalArmedBinarySensor(RiscoLocalZoneEntity, BinarySensorEntity): + """Representation whether a zone in Risco local is currently armed.""" + + def __init__(self, system_id: str, zone_id: int, zone: Zone) -> None: + """Init the zone.""" + super().__init__( + system_id=system_id, + name="Armed", + suffix="_armed", + zone_id=zone_id, + zone=zone, + ) + + @property + def is_on(self) -> bool | None: + """Return true if sensor is on.""" + return self._zone.armed diff --git a/homeassistant/components/risco/manifest.json b/homeassistant/components/risco/manifest.json index bd8bbfd715f..d31d148f4da 100644 --- a/homeassistant/components/risco/manifest.json +++ b/homeassistant/components/risco/manifest.json @@ -3,7 +3,7 @@ "name": "Risco", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/risco", - "requirements": ["pyrisco==0.5.5"], + "requirements": ["pyrisco==0.5.6"], "codeowners": ["@OnFreund"], "quality_scale": "platinum", "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index ead4f05bf9d..6290759fe21 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1860,7 +1860,7 @@ pyrecswitch==1.0.2 pyrepetierng==0.1.0 # homeassistant.components.risco -pyrisco==0.5.5 +pyrisco==0.5.6 # homeassistant.components.rituals_perfume_genie pyrituals==0.0.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d8661001022..d07aeff4b7b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1316,7 +1316,7 @@ pyps4-2ndscreen==1.3.1 pyqwikswitch==0.93 # homeassistant.components.risco -pyrisco==0.5.5 +pyrisco==0.5.6 # homeassistant.components.rituals_perfume_genie pyrituals==0.0.6 diff --git a/tests/components/risco/conftest.py b/tests/components/risco/conftest.py index cc65efd9b55..9d14cfdcff0 100644 --- a/tests/components/risco/conftest.py +++ b/tests/components/risco/conftest.py @@ -70,6 +70,8 @@ def two_zone_local(): zone_mocks[0], "alarmed", new_callable=PropertyMock(return_value=False) ), patch.object( zone_mocks[0], "bypassed", new_callable=PropertyMock(return_value=False) + ), patch.object( + zone_mocks[0], "armed", new_callable=PropertyMock(return_value=False) ), patch.object( zone_mocks[1], "id", new_callable=PropertyMock(return_value=1) ), patch.object( @@ -78,6 +80,8 @@ def two_zone_local(): zone_mocks[1], "alarmed", new_callable=PropertyMock(return_value=False) ), patch.object( zone_mocks[1], "bypassed", new_callable=PropertyMock(return_value=False) + ), patch.object( + zone_mocks[1], "armed", new_callable=PropertyMock(return_value=False) ), patch( "homeassistant.components.risco.RiscoLocal.partitions", new_callable=PropertyMock(return_value={}), diff --git a/tests/components/risco/test_binary_sensor.py b/tests/components/risco/test_binary_sensor.py index 00d10f6059e..1c331adc145 100644 --- a/tests/components/risco/test_binary_sensor.py +++ b/tests/components/risco/test_binary_sensor.py @@ -15,6 +15,8 @@ FIRST_ENTITY_ID = "binary_sensor.zone_0" SECOND_ENTITY_ID = "binary_sensor.zone_1" FIRST_ALARMED_ENTITY_ID = FIRST_ENTITY_ID + "_alarmed" SECOND_ALARMED_ENTITY_ID = SECOND_ENTITY_ID + "_alarmed" +FIRST_ARMED_ENTITY_ID = FIRST_ENTITY_ID + "_armed" +SECOND_ARMED_ENTITY_ID = SECOND_ENTITY_ID + "_armed" @pytest.mark.parametrize("exception", [CannotConnectError, UnauthorizedError]) @@ -95,33 +97,19 @@ async def test_local_setup(hass, two_zone_local, setup_risco_local): assert device.manufacturer == "Risco" -async def _check_local_state(hass, zones, triggered, entity_id, zone_id, callback): - with patch.object( - zones[zone_id], - "triggered", - new_callable=PropertyMock(return_value=triggered), - ): - await callback(zone_id, zones[zone_id]) - await hass.async_block_till_done() - - expected_triggered = STATE_ON if triggered else STATE_OFF - assert hass.states.get(entity_id).state == expected_triggered - assert hass.states.get(entity_id).attributes["zone_id"] == zone_id - - -async def _check_alarmed_local_state( - hass, zones, alarmed, entity_id, zone_id, callback +async def _check_local_state( + hass, zones, property, value, entity_id, zone_id, callback ): with patch.object( zones[zone_id], - "alarmed", - new_callable=PropertyMock(return_value=alarmed), + property, + new_callable=PropertyMock(return_value=value), ): await callback(zone_id, zones[zone_id]) await hass.async_block_till_done() - expected_alarmed = STATE_ON if alarmed else STATE_OFF - assert hass.states.get(entity_id).state == expected_alarmed + expected_value = STATE_ON if value else STATE_OFF + assert hass.states.get(entity_id).state == expected_value assert hass.states.get(entity_id).attributes["zone_id"] == zone_id @@ -134,34 +122,64 @@ def _mock_zone_handler(): async def test_local_states( hass, two_zone_local, _mock_zone_handler, setup_risco_local ): - """Test the various alarm states.""" + """Test the various zone states.""" callback = _mock_zone_handler.call_args.args[0] assert callback is not None - await _check_local_state(hass, two_zone_local, True, FIRST_ENTITY_ID, 0, callback) - await _check_local_state(hass, two_zone_local, False, FIRST_ENTITY_ID, 0, callback) - await _check_local_state(hass, two_zone_local, True, SECOND_ENTITY_ID, 1, callback) - await _check_local_state(hass, two_zone_local, False, SECOND_ENTITY_ID, 1, callback) + await _check_local_state( + hass, two_zone_local, "triggered", True, FIRST_ENTITY_ID, 0, callback + ) + await _check_local_state( + hass, two_zone_local, "triggered", False, FIRST_ENTITY_ID, 0, callback + ) + await _check_local_state( + hass, two_zone_local, "triggered", True, SECOND_ENTITY_ID, 1, callback + ) + await _check_local_state( + hass, two_zone_local, "triggered", False, SECOND_ENTITY_ID, 1, callback + ) async def test_alarmed_local_states( hass, two_zone_local, _mock_zone_handler, setup_risco_local ): - """Test the various alarm states.""" + """Test the various zone alarmed states.""" callback = _mock_zone_handler.call_args.args[0] assert callback is not None - await _check_alarmed_local_state( - hass, two_zone_local, True, FIRST_ALARMED_ENTITY_ID, 0, callback + await _check_local_state( + hass, two_zone_local, "alarmed", True, FIRST_ALARMED_ENTITY_ID, 0, callback ) - await _check_alarmed_local_state( - hass, two_zone_local, False, FIRST_ALARMED_ENTITY_ID, 0, callback + await _check_local_state( + hass, two_zone_local, "alarmed", False, FIRST_ALARMED_ENTITY_ID, 0, callback ) - await _check_alarmed_local_state( - hass, two_zone_local, True, SECOND_ALARMED_ENTITY_ID, 1, callback + await _check_local_state( + hass, two_zone_local, "alarmed", True, SECOND_ALARMED_ENTITY_ID, 1, callback ) - await _check_alarmed_local_state( - hass, two_zone_local, False, SECOND_ALARMED_ENTITY_ID, 1, callback + await _check_local_state( + hass, two_zone_local, "alarmed", False, SECOND_ALARMED_ENTITY_ID, 1, callback + ) + + +async def test_armed_local_states( + hass, two_zone_local, _mock_zone_handler, setup_risco_local +): + """Test the various zone armed states.""" + callback = _mock_zone_handler.call_args.args[0] + + assert callback is not None + + await _check_local_state( + hass, two_zone_local, "armed", True, FIRST_ARMED_ENTITY_ID, 0, callback + ) + await _check_local_state( + hass, two_zone_local, "armed", False, FIRST_ARMED_ENTITY_ID, 0, callback + ) + await _check_local_state( + hass, two_zone_local, "armed", True, SECOND_ARMED_ENTITY_ID, 1, callback + ) + await _check_local_state( + hass, two_zone_local, "armed", False, SECOND_ARMED_ENTITY_ID, 1, callback ) From b6c27585c74294fd4cc4d3a2640cf98ef6b4c343 Mon Sep 17 00:00:00 2001 From: Ziv <16467659+ziv1234@users.noreply.github.com> Date: Sat, 12 Nov 2022 23:59:29 +0200 Subject: [PATCH 0372/1033] Implemented RestoreEntity for Dynalite (#73911) * Implemented RestoreEntity Merged commit conflict * removed accidental change * Update homeassistant/components/dynalite/dynalitebase.py Co-authored-by: Erik Montnemery * added tests for the state * added tests for switch state * moved to ATTR_x and STATE_x instead of strings some fixes to test_cover * moved blind to DEVICE_CLASS_BLIND * used correct constant instead of deprecated * Implemented RestoreEntity * removed accidental change * added tests for the state * added tests for switch state * moved to ATTR_x and STATE_x instead of strings some fixes to test_cover * fixed isort issue from merge Co-authored-by: Erik Montnemery --- homeassistant/components/dynalite/cover.py | 13 +++- .../components/dynalite/dynalitebase.py | 20 ++++- homeassistant/components/dynalite/light.py | 8 +- .../components/dynalite/manifest.json | 2 +- homeassistant/components/dynalite/switch.py | 6 ++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/dynalite/test_bridge.py | 4 + tests/components/dynalite/test_cover.py | 74 ++++++++++++++++--- tests/components/dynalite/test_light.py | 51 ++++++++++++- tests/components/dynalite/test_switch.py | 35 ++++++++- 11 files changed, 194 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/dynalite/cover.py b/homeassistant/components/dynalite/cover.py index e5c38996a89..bbca16d3db6 100644 --- a/homeassistant/components/dynalite/cover.py +++ b/homeassistant/components/dynalite/cover.py @@ -2,7 +2,12 @@ from typing import Any -from homeassistant.components.cover import DEVICE_CLASSES, CoverDeviceClass, CoverEntity +from homeassistant.components.cover import ( + ATTR_CURRENT_POSITION, + DEVICE_CLASSES, + CoverDeviceClass, + CoverEntity, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -78,6 +83,12 @@ class DynaliteCover(DynaliteBase, CoverEntity): """Stop the cover.""" await self._device.async_stop_cover(**kwargs) + def initialize_state(self, state): + """Initialize the state from cache.""" + target_level = state.attributes.get(ATTR_CURRENT_POSITION) + if target_level is not None: + self._device.init_level(target_level) + class DynaliteCoverWithTilt(DynaliteCover): """Representation of a Dynalite Channel as a Home Assistant Cover that uses up and down for tilt.""" diff --git a/homeassistant/components/dynalite/dynalitebase.py b/homeassistant/components/dynalite/dynalitebase.py index b4b8285cbb0..3ebf04ab219 100644 --- a/homeassistant/components/dynalite/dynalitebase.py +++ b/homeassistant/components/dynalite/dynalitebase.py @@ -1,14 +1,16 @@ """Support for the Dynalite devices as entities.""" from __future__ import annotations +from abc import ABC, abstractmethod from collections.abc import Callable from typing import Any from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.helpers.entity import DeviceInfo, Entity +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.restore_state import RestoreEntity from .bridge import DynaliteBridge from .const import DOMAIN, LOGGER @@ -36,7 +38,7 @@ def async_setup_entry_base( bridge.register_add_devices(platform, async_add_entities_platform) -class DynaliteBase(Entity): +class DynaliteBase(RestoreEntity, ABC): """Base class for the Dynalite entities.""" def __init__(self, device: Any, bridge: DynaliteBridge) -> None: @@ -70,8 +72,16 @@ class DynaliteBase(Entity): ) async def async_added_to_hass(self) -> None: - """Added to hass so need to register to dispatch.""" + """Added to hass so need to restore state and register to dispatch.""" # register for device specific update + await super().async_added_to_hass() + + cur_state = await self.async_get_last_state() + if cur_state: + self.initialize_state(cur_state) + else: + LOGGER.info("Restore state not available for %s", self.entity_id) + self._unsub_dispatchers.append( async_dispatcher_connect( self.hass, @@ -88,6 +98,10 @@ class DynaliteBase(Entity): ) ) + @abstractmethod + def initialize_state(self, state): + """Initialize the state from cache.""" + async def async_will_remove_from_hass(self) -> None: """Unregister signal dispatch listeners when being removed.""" for unsub in self._unsub_dispatchers: diff --git a/homeassistant/components/dynalite/light.py b/homeassistant/components/dynalite/light.py index 27cd6f8cae8..ffb97da49c1 100644 --- a/homeassistant/components/dynalite/light.py +++ b/homeassistant/components/dynalite/light.py @@ -2,7 +2,7 @@ from typing import Any -from homeassistant.components.light import ColorMode, LightEntity +from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -44,3 +44,9 @@ class DynaliteLight(DynaliteBase, LightEntity): async def async_turn_off(self, **kwargs: Any) -> None: """Turn the light off.""" await self._device.async_turn_off(**kwargs) + + def initialize_state(self, state): + """Initialize the state from cache.""" + target_level = state.attributes.get(ATTR_BRIGHTNESS) + if target_level is not None: + self._device.init_level(target_level) diff --git a/homeassistant/components/dynalite/manifest.json b/homeassistant/components/dynalite/manifest.json index d403291a081..57010666019 100644 --- a/homeassistant/components/dynalite/manifest.json +++ b/homeassistant/components/dynalite/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dynalite", "codeowners": ["@ziv1234"], - "requirements": ["dynalite_devices==0.1.46"], + "requirements": ["dynalite_devices==0.1.47"], "iot_class": "local_push", "loggers": ["dynalite_devices_lib"] } diff --git a/homeassistant/components/dynalite/switch.py b/homeassistant/components/dynalite/switch.py index 3e459e45847..54e9b919b89 100644 --- a/homeassistant/components/dynalite/switch.py +++ b/homeassistant/components/dynalite/switch.py @@ -4,6 +4,7 @@ from typing import Any from homeassistant.components.switch import SwitchEntity from homeassistant.config_entries import ConfigEntry +from homeassistant.const import STATE_ON from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -36,3 +37,8 @@ class DynaliteSwitch(DynaliteBase, SwitchEntity): async def async_turn_off(self, **kwargs: Any) -> None: """Turn the switch off.""" await self._device.async_turn_off() + + def initialize_state(self, state): + """Initialize the state from cache.""" + target_level = 1 if state.state == STATE_ON else 0 + self._device.init_level(target_level) diff --git a/requirements_all.txt b/requirements_all.txt index 6290759fe21..5f1a1150442 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -609,7 +609,7 @@ dwdwfsapi==1.0.5 dweepy==0.3.0 # homeassistant.components.dynalite -dynalite_devices==0.1.46 +dynalite_devices==0.1.47 # homeassistant.components.rainforest_eagle eagle100==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d07aeff4b7b..d43ecede70c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -471,7 +471,7 @@ doorbirdpy==2.1.0 dsmr_parser==0.33 # homeassistant.components.dynalite -dynalite_devices==0.1.46 +dynalite_devices==0.1.47 # homeassistant.components.rainforest_eagle eagle100==0.1.1 diff --git a/tests/components/dynalite/test_bridge.py b/tests/components/dynalite/test_bridge.py index 363a9671f59..f5cfaec7a97 100644 --- a/tests/components/dynalite/test_bridge.py +++ b/tests/components/dynalite/test_bridge.py @@ -70,10 +70,12 @@ async def test_add_devices_then_register(hass): device1.category = "light" device1.name = "NAME" device1.unique_id = "unique1" + device1.brightness = 1 device2 = Mock() device2.category = "switch" device2.name = "NAME2" device2.unique_id = "unique2" + device2.brightness = 1 new_device_func([device1, device2]) device3 = Mock() device3.category = "switch" @@ -103,10 +105,12 @@ async def test_register_then_add_devices(hass): device1.category = "light" device1.name = "NAME" device1.unique_id = "unique1" + device1.brightness = 1 device2 = Mock() device2.category = "switch" device2.name = "NAME2" device2.unique_id = "unique2" + device2.brightness = 1 new_device_func([device1, device2]) await hass.async_block_till_done() assert hass.states.get("light.name") diff --git a/tests/components/dynalite/test_cover.py b/tests/components/dynalite/test_cover.py index fd671365ba1..5fbb22b91a7 100644 --- a/tests/components/dynalite/test_cover.py +++ b/tests/components/dynalite/test_cover.py @@ -1,8 +1,25 @@ """Test Dynalite cover.""" +from unittest.mock import Mock + from dynalite_devices_lib.cover import DynaliteTimeCoverWithTiltDevice import pytest -from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_FRIENDLY_NAME +from homeassistant.components.cover import ( + ATTR_CURRENT_POSITION, + ATTR_CURRENT_TILT_POSITION, + ATTR_POSITION, + ATTR_TILT_POSITION, + CoverDeviceClass, +) +from homeassistant.const import ( + ATTR_DEVICE_CLASS, + ATTR_FRIENDLY_NAME, + STATE_CLOSED, + STATE_CLOSING, + STATE_OPEN, + STATE_OPENING, +) +from homeassistant.core import State from homeassistant.exceptions import HomeAssistantError from .common import ( @@ -14,12 +31,25 @@ from .common import ( run_service_tests, ) +from tests.common import mock_restore_cache + @pytest.fixture def mock_device(): """Mock a Dynalite device.""" mock_dev = create_mock_device("cover", DynaliteTimeCoverWithTiltDevice) - mock_dev.device_class = "blind" + mock_dev.device_class = CoverDeviceClass.BLIND.value + mock_dev.current_cover_position = 0 + mock_dev.current_cover_tilt_position = 0 + mock_dev.is_opening = False + mock_dev.is_closing = False + mock_dev.is_closed = True + + def mock_init_level(target): + mock_dev.is_closed = target == 0 + + type(mock_dev).init_level = Mock(side_effect=mock_init_level) + return mock_dev @@ -29,11 +59,11 @@ async def test_cover_setup(hass, mock_device): entity_state = hass.states.get("cover.name") assert entity_state.attributes[ATTR_FRIENDLY_NAME] == mock_device.name assert ( - entity_state.attributes["current_position"] + entity_state.attributes[ATTR_CURRENT_POSITION] == mock_device.current_cover_position ) assert ( - entity_state.attributes["current_tilt_position"] + entity_state.attributes[ATTR_CURRENT_TILT_POSITION] == mock_device.current_cover_tilt_position ) assert entity_state.attributes[ATTR_DEVICE_CLASS] == mock_device.device_class @@ -48,7 +78,7 @@ async def test_cover_setup(hass, mock_device): { ATTR_SERVICE: "set_cover_position", ATTR_METHOD: "async_set_cover_position", - ATTR_ARGS: {"position": 50}, + ATTR_ARGS: {ATTR_POSITION: 50}, }, {ATTR_SERVICE: "open_cover_tilt", ATTR_METHOD: "async_open_cover_tilt"}, {ATTR_SERVICE: "close_cover_tilt", ATTR_METHOD: "async_close_cover_tilt"}, @@ -56,7 +86,7 @@ async def test_cover_setup(hass, mock_device): { ATTR_SERVICE: "set_cover_tilt_position", ATTR_METHOD: "async_set_cover_tilt_position", - ATTR_ARGS: {"tilt_position": 50}, + ATTR_ARGS: {ATTR_TILT_POSITION: 50}, }, ], ) @@ -91,14 +121,38 @@ async def test_cover_positions(hass, mock_device): """Test that the state updates in the various positions.""" update_func = await create_entity_from_device(hass, mock_device) await check_cover_position( - hass, update_func, mock_device, True, False, False, "closing" + hass, update_func, mock_device, True, False, False, STATE_CLOSING ) await check_cover_position( - hass, update_func, mock_device, False, True, False, "opening" + hass, update_func, mock_device, False, True, False, STATE_OPENING ) await check_cover_position( - hass, update_func, mock_device, False, False, True, "closed" + hass, update_func, mock_device, False, False, True, STATE_CLOSED ) await check_cover_position( - hass, update_func, mock_device, False, False, False, "open" + hass, update_func, mock_device, False, False, False, STATE_OPEN ) + + +async def test_cover_restore_state(hass, mock_device): + """Test restore from cache.""" + mock_restore_cache( + hass, + [State("cover.name", STATE_OPEN, attributes={ATTR_CURRENT_POSITION: 77})], + ) + await create_entity_from_device(hass, mock_device) + mock_device.init_level.assert_called_once_with(77) + entity_state = hass.states.get("cover.name") + assert entity_state.state == STATE_OPEN + + +async def test_cover_restore_state_bad_cache(hass, mock_device): + """Test restore from a cache without the attribute.""" + mock_restore_cache( + hass, + [State("cover.name", STATE_OPEN, attributes={"bla bla": 77})], + ) + await create_entity_from_device(hass, mock_device) + mock_device.init_level.assert_not_called() + entity_state = hass.states.get("cover.name") + assert entity_state.state == STATE_CLOSED diff --git a/tests/components/dynalite/test_light.py b/tests/components/dynalite/test_light.py index b100cf8d3f6..337f0a415e6 100644 --- a/tests/components/dynalite/test_light.py +++ b/tests/components/dynalite/test_light.py @@ -1,8 +1,11 @@ """Test Dynalite light.""" +from unittest.mock import Mock, PropertyMock + from dynalite_devices_lib.light import DynaliteChannelLightDevice import pytest from homeassistant.components.light import ( + ATTR_BRIGHTNESS, ATTR_COLOR_MODE, ATTR_SUPPORTED_COLOR_MODES, ColorMode, @@ -10,8 +13,11 @@ from homeassistant.components.light import ( from homeassistant.const import ( ATTR_FRIENDLY_NAME, ATTR_SUPPORTED_FEATURES, + STATE_OFF, + STATE_ON, STATE_UNAVAILABLE, ) +from homeassistant.core import State from .common import ( ATTR_METHOD, @@ -22,11 +28,25 @@ from .common import ( run_service_tests, ) +from tests.common import mock_restore_cache + @pytest.fixture def mock_device(): """Mock a Dynalite device.""" - return create_mock_device("light", DynaliteChannelLightDevice) + mock_dev = create_mock_device("light", DynaliteChannelLightDevice) + mock_dev.brightness = 0 + + def mock_is_on(): + return mock_dev.brightness != 0 + + type(mock_dev).is_on = PropertyMock(side_effect=mock_is_on) + + def mock_init_level(target): + mock_dev.brightness = target + + type(mock_dev).init_level = Mock(side_effect=mock_init_level) + return mock_dev async def test_light_setup(hass, mock_device): @@ -34,10 +54,9 @@ async def test_light_setup(hass, mock_device): await create_entity_from_device(hass, mock_device) entity_state = hass.states.get("light.name") assert entity_state.attributes[ATTR_FRIENDLY_NAME] == mock_device.name - assert entity_state.attributes["brightness"] == mock_device.brightness - assert entity_state.attributes[ATTR_COLOR_MODE] == ColorMode.BRIGHTNESS assert entity_state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] assert entity_state.attributes[ATTR_SUPPORTED_FEATURES] == 0 + assert entity_state.state == STATE_OFF await run_service_tests( hass, mock_device, @@ -67,3 +86,29 @@ async def test_remove_config_entry(hass, mock_device): assert await hass.config_entries.async_remove(entry_id) await hass.async_block_till_done() assert not hass.states.get("light.name") + + +async def test_light_restore_state(hass, mock_device): + """Test restore from cache.""" + mock_restore_cache( + hass, + [State("light.name", STATE_ON, attributes={ATTR_BRIGHTNESS: 77})], + ) + await create_entity_from_device(hass, mock_device) + mock_device.init_level.assert_called_once_with(77) + entity_state = hass.states.get("light.name") + assert entity_state.state == STATE_ON + assert entity_state.attributes[ATTR_BRIGHTNESS] == 77 + assert entity_state.attributes[ATTR_COLOR_MODE] == ColorMode.BRIGHTNESS + + +async def test_light_restore_state_bad_cache(hass, mock_device): + """Test restore from a cache without the attribute.""" + mock_restore_cache( + hass, + [State("light.name", "abc", attributes={"blabla": 77})], + ) + await create_entity_from_device(hass, mock_device) + mock_device.init_level.assert_not_called() + entity_state = hass.states.get("light.name") + assert entity_state.state == STATE_OFF diff --git a/tests/components/dynalite/test_switch.py b/tests/components/dynalite/test_switch.py index de375e3b348..95ab64ef197 100644 --- a/tests/components/dynalite/test_switch.py +++ b/tests/components/dynalite/test_switch.py @@ -1,9 +1,12 @@ """Test Dynalite switch.""" +from unittest.mock import Mock + from dynalite_devices_lib.switch import DynalitePresetSwitchDevice import pytest -from homeassistant.const import ATTR_FRIENDLY_NAME +from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_OFF, STATE_ON +from homeassistant.core import State from .common import ( ATTR_METHOD, @@ -13,11 +16,20 @@ from .common import ( run_service_tests, ) +from tests.common import mock_restore_cache + @pytest.fixture def mock_device(): """Mock a Dynalite device.""" - return create_mock_device("switch", DynalitePresetSwitchDevice) + mock_dev = create_mock_device("switch", DynalitePresetSwitchDevice) + mock_dev.is_on = False + + def mock_init_level(level): + mock_dev.is_on = level + + type(mock_dev).init_level = Mock(side_effect=mock_init_level) + return mock_dev async def test_switch_setup(hass, mock_device): @@ -25,6 +37,7 @@ async def test_switch_setup(hass, mock_device): await create_entity_from_device(hass, mock_device) entity_state = hass.states.get("switch.name") assert entity_state.attributes[ATTR_FRIENDLY_NAME] == mock_device.name + assert entity_state.state == STATE_OFF await run_service_tests( hass, mock_device, @@ -34,3 +47,21 @@ async def test_switch_setup(hass, mock_device): {ATTR_SERVICE: "turn_off", ATTR_METHOD: "async_turn_off"}, ], ) + + +@pytest.mark.parametrize("saved_state, level", [(STATE_ON, 1), (STATE_OFF, 0)]) +async def test_switch_restore_state(hass, mock_device, saved_state, level): + """Test restore from cache.""" + mock_restore_cache( + hass, + [ + State( + "switch.name", + saved_state, + ) + ], + ) + await create_entity_from_device(hass, mock_device) + mock_device.init_level.assert_called_once_with(level) + entity_state = hass.states.get("switch.name") + assert entity_state.state == saved_state From 5e610cdfd2bac229cbc2a8253c7f102e792c5ff0 Mon Sep 17 00:00:00 2001 From: Cougar Date: Sun, 13 Nov 2022 00:24:33 +0200 Subject: [PATCH 0373/1033] Bump luftdaten to 0.7.4 (#82002) Bump luftdaten to 0.7.4 (#61687) changelog: https://github.com/home-assistant-ecosystem/python-luftdaten/compare/0.7.2...0.7.4 Fixes API connection error handling (#61687) --- homeassistant/components/luftdaten/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/luftdaten/manifest.json b/homeassistant/components/luftdaten/manifest.json index aed8d80f8b1..4c84c81af5e 100644 --- a/homeassistant/components/luftdaten/manifest.json +++ b/homeassistant/components/luftdaten/manifest.json @@ -3,7 +3,7 @@ "name": "Sensor.Community", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/luftdaten", - "requirements": ["luftdaten==0.7.2"], + "requirements": ["luftdaten==0.7.4"], "codeowners": ["@fabaff", "@frenck"], "quality_scale": "gold", "iot_class": "cloud_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 5f1a1150442..20ea4c7862e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1043,7 +1043,7 @@ logi_circle==0.2.3 london-tube-status==0.5 # homeassistant.components.luftdaten -luftdaten==0.7.2 +luftdaten==0.7.4 # homeassistant.components.lupusec lupupy==0.1.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d43ecede70c..f365317a7ad 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -763,7 +763,7 @@ life360==5.3.0 logi_circle==0.2.3 # homeassistant.components.luftdaten -luftdaten==0.7.2 +luftdaten==0.7.4 # homeassistant.components.scrape lxml==4.9.1 From 9be2704c11ab0a05b9995bae5d91a9d2b43b25ed Mon Sep 17 00:00:00 2001 From: Ian Byrne Date: Sun, 13 Nov 2022 00:11:06 +0000 Subject: [PATCH 0374/1033] Add option to include attachments from remote URL to Discord notifications (#74811) * Add option to include attachments from remote URL to Discord notifications * Use aiohttp instead of requests for Discord Notify URL retrieval * Refactor discord notifications code * Remove unecessary images check in discord notifier --- homeassistant/components/discord/notify.py | 72 ++++++++++++++-- tests/components/discord/conftest.py | 45 ++++++++++ tests/components/discord/test_notify.py | 96 ++++++++++++++++++++++ 3 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 tests/components/discord/conftest.py create mode 100644 tests/components/discord/test_notify.py diff --git a/homeassistant/components/discord/notify.py b/homeassistant/components/discord/notify.py index d97ce7042bc..8fcab7cefba 100644 --- a/homeassistant/components/discord/notify.py +++ b/homeassistant/components/discord/notify.py @@ -1,6 +1,7 @@ """Discord platform for notify component.""" from __future__ import annotations +from io import BytesIO import logging import os.path from typing import Any, cast @@ -15,6 +16,7 @@ from homeassistant.components.notify import ( ) from homeassistant.const import CONF_API_TOKEN from homeassistant.core import HomeAssistant +from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType _LOGGER = logging.getLogger(__name__) @@ -30,6 +32,10 @@ ATTR_EMBED_THUMBNAIL = "thumbnail" ATTR_EMBED_IMAGE = "image" ATTR_EMBED_URL = "url" ATTR_IMAGES = "images" +ATTR_URLS = "urls" +ATTR_VERIFY_SSL = "verify_ssl" + +MAX_ALLOWED_DOWNLOAD_SIZE_BYTES = 8000000 async def async_get_service( @@ -61,11 +67,54 @@ class DiscordNotificationService(BaseNotificationService): return False return True + async def async_get_file_from_url( + self, url: str, verify_ssl: bool, max_file_size: int + ) -> bytearray | None: + """Retrieve file bytes from URL.""" + if not self.hass.config.is_allowed_external_url(url): + _LOGGER.error("URL not allowed: %s", url) + return None + + session = async_get_clientsession(self.hass) + + async with session.get( + url, + ssl=verify_ssl, + timeout=30, + raise_for_status=True, + ) as resp: + content_length = resp.headers.get("Content-Length") + + if content_length is not None and int(content_length) > max_file_size: + _LOGGER.error( + "Attachment too large (Content-Length reports %s). Max size: %s bytes", + int(content_length), + max_file_size, + ) + return None + + file_size = 0 + byte_chunks = bytearray() + + async for byte_chunk, _ in resp.content.iter_chunks(): + file_size += len(byte_chunk) + if file_size > max_file_size: + _LOGGER.error( + "Attachment too large (Stream reports %s). Max size: %s bytes", + file_size, + max_file_size, + ) + return None + + byte_chunks.extend(byte_chunk) + + return byte_chunks + async def async_send_message(self, message: str, **kwargs: Any) -> None: """Login to Discord, send message to channel(s) and log out.""" nextcord.VoiceClient.warn_nacl = False discord_bot = nextcord.Client() - images = None + images = [] embedding = None if ATTR_TARGET not in kwargs: @@ -100,15 +149,28 @@ class DiscordNotificationService(BaseNotificationService): embeds.append(embed) if ATTR_IMAGES in data: - images = [] - for image in data.get(ATTR_IMAGES, []): image_exists = await self.hass.async_add_executor_job( self.file_exists, image ) + filename = os.path.basename(image) + if image_exists: - images.append(image) + images.append((image, filename)) + + if ATTR_URLS in data: + for url in data.get(ATTR_URLS, []): + file = await self.async_get_file_from_url( + url, + data.get(ATTR_VERIFY_SSL, True), + MAX_ALLOWED_DOWNLOAD_SIZE_BYTES, + ) + + if file is not None: + filename = os.path.basename(url) + + images.append((BytesIO(file), filename)) await discord_bot.login(self.token) @@ -116,7 +178,7 @@ class DiscordNotificationService(BaseNotificationService): for channelid in kwargs[ATTR_TARGET]: channelid = int(channelid) # Must create new instances of File for each channel. - files = [nextcord.File(image) for image in images] if images else [] + files = [nextcord.File(image, filename) for image, filename in images] try: channel = cast( Messageable, await discord_bot.fetch_channel(channelid) diff --git a/tests/components/discord/conftest.py b/tests/components/discord/conftest.py new file mode 100644 index 00000000000..c98944fdc85 --- /dev/null +++ b/tests/components/discord/conftest.py @@ -0,0 +1,45 @@ +"""Discord notification test helpers.""" +from http import HTTPStatus + +import pytest + +from homeassistant.components.discord.notify import DiscordNotificationService +from homeassistant.core import HomeAssistant + +from tests.test_util.aiohttp import AiohttpClientMocker + +MESSAGE = "Testing Discord Messenger platform" +CONTENT = b"TestContent" +URL_ATTACHMENT = "http://127.0.0.1:8080/image.jpg" +TARGET = "1234567890" + + +@pytest.fixture +def discord_notification_service(hass: HomeAssistant) -> DiscordNotificationService: + """Set up discord notification service.""" + hass.config.allowlist_external_urls.add(URL_ATTACHMENT) + return DiscordNotificationService(hass, "token") + + +@pytest.fixture +def discord_aiohttp_mock_factory( + aioclient_mock: AiohttpClientMocker, +) -> AiohttpClientMocker: + """Create Discord service mock from factory.""" + + def _discord_aiohttp_mock_factory( + headers: dict[str, str] = None, + ) -> AiohttpClientMocker: + if headers is not None: + aioclient_mock.get( + URL_ATTACHMENT, status=HTTPStatus.OK, content=CONTENT, headers=headers + ) + else: + aioclient_mock.get( + URL_ATTACHMENT, + status=HTTPStatus.OK, + content=CONTENT, + ) + return aioclient_mock + + return _discord_aiohttp_mock_factory diff --git a/tests/components/discord/test_notify.py b/tests/components/discord/test_notify.py new file mode 100644 index 00000000000..810898cdf73 --- /dev/null +++ b/tests/components/discord/test_notify.py @@ -0,0 +1,96 @@ +"""Test Discord notify.""" +import logging + +import pytest + +from homeassistant.components.discord.notify import DiscordNotificationService + +from .conftest import CONTENT, MESSAGE, URL_ATTACHMENT + +from tests.test_util.aiohttp import AiohttpClientMocker + + +async def test_send_message_without_target_logs_error( + discord_notification_service: DiscordNotificationService, + discord_aiohttp_mock_factory: AiohttpClientMocker, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test send message.""" + discord_aiohttp_mock = discord_aiohttp_mock_factory() + with caplog.at_level( + logging.ERROR, logger="homeassistant.components.discord.notify" + ): + await discord_notification_service.async_send_message(MESSAGE) + assert "No target specified" in caplog.text + assert discord_aiohttp_mock.call_count == 0 + + +async def test_get_file_from_url( + discord_notification_service: DiscordNotificationService, + discord_aiohttp_mock_factory: AiohttpClientMocker, +) -> None: + """Test getting a file from a URL.""" + headers = {"Content-Length": str(len(CONTENT))} + discord_aiohttp_mock = discord_aiohttp_mock_factory(headers) + result = await discord_notification_service.async_get_file_from_url( + URL_ATTACHMENT, True, len(CONTENT) + ) + + assert discord_aiohttp_mock.call_count == 1 + assert result == bytearray(CONTENT) + + +async def test_get_file_from_url_not_on_allowlist( + discord_notification_service: DiscordNotificationService, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test getting file from URL that isn't on the allowlist.""" + url = "http://dodgyurl.com" + with caplog.at_level( + logging.WARNING, logger="homeassistant.components.discord.notify" + ): + result = await discord_notification_service.async_get_file_from_url( + url, True, len(CONTENT) + ) + + assert f"URL not allowed: {url}" in caplog.text + assert result is None + + +async def test_get_file_from_url_with_large_attachment( + discord_notification_service: DiscordNotificationService, + discord_aiohttp_mock_factory: AiohttpClientMocker, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test getting file from URL with large attachment (per Content-Length header) throws error.""" + headers = {"Content-Length": str(len(CONTENT) + 1)} + discord_aiohttp_mock = discord_aiohttp_mock_factory(headers) + with caplog.at_level( + logging.WARNING, logger="homeassistant.components.discord.notify" + ): + result = await discord_notification_service.async_get_file_from_url( + URL_ATTACHMENT, True, len(CONTENT) + ) + + assert discord_aiohttp_mock.call_count == 1 + assert "Attachment too large (Content-Length reports" in caplog.text + assert result is None + + +async def test_get_file_from_url_with_large_attachment_no_header( + discord_notification_service: DiscordNotificationService, + discord_aiohttp_mock_factory: AiohttpClientMocker, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test getting file from URL with large attachment (per content length) throws error.""" + discord_aiohttp_mock = discord_aiohttp_mock_factory() + with caplog.at_level( + logging.WARNING, logger="homeassistant.components.discord.notify" + ): + result = await discord_notification_service.async_get_file_from_url( + URL_ATTACHMENT, True, len(CONTENT) - 1 + ) + + assert discord_aiohttp_mock.call_count == 1 + assert "Attachment too large (Stream reports" in caplog.text + assert result is None From 6df90291cf5bc01fb958078bc826c7bc141d9dec Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 13 Nov 2022 00:25:38 +0000 Subject: [PATCH 0375/1033] [ci skip] Translation update --- .../generic/translations/zh-Hant.json | 6 +- .../components/knx/translations/ru.json | 125 +++++++++++++++++- 2 files changed, 124 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/generic/translations/zh-Hant.json b/homeassistant/components/generic/translations/zh-Hant.json index 09203276fda..7ce9f004c37 100644 --- a/homeassistant/components/generic/translations/zh-Hant.json +++ b/homeassistant/components/generic/translations/zh-Hant.json @@ -48,7 +48,7 @@ }, "user_confirm_still": { "data": { - "confirmed_ok": "\u5f71\u50cf\u633a\u6e05\u6670\u3002" + "confirmed_ok": "\u5f71\u50cf\u770b\u8d77\u4f86\u5f88\u6e05\u6670" }, "description": "![\u651d\u5f71\u6a5f\u975c\u614b\u9810\u89bd]({preview_url})", "title": "\u9810\u89bd" @@ -77,7 +77,7 @@ "step": { "confirm_still": { "data": { - "confirmed_ok": "\u5f71\u50cf\u633a\u6e05\u6670\u3002" + "confirmed_ok": "\u5f71\u50cf\u770b\u8d77\u4f86\u5f88\u6e05\u6670" }, "description": "![\u651d\u5f71\u6a5f\u975c\u614b\u9810\u89bd]({preview_url})", "title": "\u9810\u89bd" @@ -97,7 +97,7 @@ "rtsp_transport": "RTSP \u50b3\u8f38\u5354\u5b9a", "still_image_url": "\u975c\u614b\u5f71\u50cf URL\uff08\u4f8b\u5982 http://...\uff09", "stream_source": "\u4e32\u6d41\u4f86\u6e90 URL\uff08\u4f8b\u5982 rtsp://...\uff09", - "use_wallclock_as_timestamps": "\u4f7f\u7528\u639b\u9418\u4f5c\u70ba\u6642\u9593\u6233", + "use_wallclock_as_timestamps": "\u4f7f\u7528\u6642\u9418\u4f5c\u70ba\u6642\u9593\u6233", "username": "\u4f7f\u7528\u8005\u540d\u7a31", "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" }, diff --git a/homeassistant/components/knx/translations/ru.json b/homeassistant/components/knx/translations/ru.json index d5c5bef24fa..d31d356aea0 100644 --- a/homeassistant/components/knx/translations/ru.json +++ b/homeassistant/components/knx/translations/ru.json @@ -9,20 +9,30 @@ "file_not_found": "\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b `.knxkeys` \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u0432 config/.storage/knx/", "invalid_individual_address": "\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d\u0443 \u0434\u043b\u044f \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0430\u0434\u0440\u0435\u0441\u0430 KNX 'area.line.device'.", "invalid_ip_address": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 IPv4.", - "invalid_signature": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 `.knxkeys`." + "invalid_signature": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 `.knxkeys`.", + "no_router_discovered": "\u0412 \u0441\u0435\u0442\u0438 \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440 KNXnet/IP.", + "no_tunnel_discovered": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX \u0432 \u0412\u0430\u0448\u0435\u0439 \u0441\u0435\u0442\u0438." }, "step": { + "connection_type": { + "data": { + "connection_type": "\u0422\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f KNX" + }, + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c.\nAUTOMATIC \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0448\u0438\u043d\u0435 KNX, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044f \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0448\u043b\u044e\u0437\u0430.\nTUNNELING \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a \u0448\u0438\u043d\u0435 KNX, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435.\nROUTING \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a \u0448\u0438\u043d\u0435 KNX, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u044e." + }, "manual_tunnel": { "data": { "host": "\u0425\u043e\u0441\u0442", "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant", "port": "\u041f\u043e\u0440\u0442", + "route_back": "\u041e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 / \u0440\u0435\u0436\u0438\u043c NAT", "tunneling_type": "\u0422\u0438\u043f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX" }, "data_description": { "host": "IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP.", "local_ip": "\u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435.", - "port": "\u041f\u043e\u0440\u0442 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP." + "port": "\u041f\u043e\u0440\u0442 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP.", + "route_back": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435, \u0435\u0441\u043b\u0438 \u0412\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNXnet/IP \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0437\u0430 NAT. \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 UDP." }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438." }, @@ -63,11 +73,25 @@ }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e IP Secure." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "\u041f\u0430\u0440\u043e\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", + "user_id": "ID \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", + "user_password": "\u041f\u0430\u0440\u043e\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + }, + "data_description": { + "device_authentication": "\u042d\u0442\u043e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 'IP' \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 ETS.", + "user_id": "\u0427\u0430\u0441\u0442\u043e \u043d\u043e\u043c\u0435\u0440 \u0442\u0443\u043d\u043d\u0435\u043b\u044f +1. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, 'Tunnel 2' \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c User-ID '3'.", + "user_password": "\u041f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0442\u0443\u043d\u043d\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f, \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 'Properties' \u0442\u0443\u043d\u043d\u0435\u043b\u044f \u0432 ETS." + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e IP Secure." + }, "secure_tunneling": { "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 KNX/IP Secure.", "menu_options": { "secure_knxkeys": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b `.knxkeys`, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043a\u043b\u044e\u0447\u0438 IP secure", - "secure_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0438 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e" + "secure_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0438 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e", + "secure_tunnel_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0438 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e" } }, "tunnel": { @@ -85,7 +109,32 @@ } }, "options": { + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "file_not_found": "\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b `.knxkeys` \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u0432 config/.storage/knx/", + "invalid_individual_address": "\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d\u0443 \u0434\u043b\u044f \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0430\u0434\u0440\u0435\u0441\u0430 KNX 'area.line.device'.", + "invalid_ip_address": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 IPv4.", + "invalid_signature": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 `.knxkeys`.", + "no_router_discovered": "\u0412 \u0441\u0435\u0442\u0438 \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440 KNXnet/IP.", + "no_tunnel_discovered": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX \u0432 \u0412\u0430\u0448\u0435\u0439 \u0441\u0435\u0442\u0438." + }, "step": { + "communication_settings": { + "data": { + "rate_limit": "\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438", + "state_updater": "\u0421\u0440\u0435\u0434\u0441\u0442\u0432\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f" + }, + "data_description": { + "rate_limit": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443.\n\u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: 0 \u0438\u043b\u0438 \u043e\u0442 20 \u0434\u043e 40 (`0` - \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435).", + "state_updater": "\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0438\u0437 \u0448\u0438\u043d\u044b KNX. \u0415\u0441\u043b\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e, Home Assistant \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441 \u0448\u0438\u043d\u044b KNX. \u041c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0442\u043c\u0435\u043d\u0435\u043d \u043e\u043f\u0446\u0438\u044f\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 `sync_state`." + } + }, + "connection_type": { + "data": { + "connection_type": "\u0422\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f KNX" + }, + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c.\nAUTOMATIC \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0448\u0438\u043d\u0435 KNX, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044f \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0448\u043b\u044e\u0437\u0430.\nTUNNELING \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a \u0448\u0438\u043d\u0435 KNX, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435.\nROUTING \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a \u0448\u0438\u043d\u0435 KNX, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u044e." + }, "init": { "data": { "connection_type": "\u0422\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f KNX", @@ -105,8 +154,75 @@ "state_updater": "\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0438\u0437 \u0448\u0438\u043d\u044b KNX. \u0415\u0441\u043b\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e, Home Assistant \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441 \u0448\u0438\u043d\u044b KNX. \u041c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0442\u043c\u0435\u043d\u0435\u043d \u043e\u043f\u0446\u0438\u044f\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 `sync_state`." } }, + "manual_tunnel": { + "data": { + "host": "\u0425\u043e\u0441\u0442", + "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant", + "port": "\u041f\u043e\u0440\u0442", + "route_back": "\u041e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 / \u0440\u0435\u0436\u0438\u043c NAT", + "tunneling_type": "\u0422\u0438\u043f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX" + }, + "data_description": { + "host": "IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP.", + "local_ip": "\u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435.", + "port": "\u041f\u043e\u0440\u0442 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP.", + "route_back": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435, \u0435\u0441\u043b\u0438 \u0412\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNXnet/IP \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0437\u0430 NAT. \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 UDP." + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438." + }, + "options_init": { + "menu_options": { + "communication_settings": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0432\u044f\u0437\u0438", + "connection_type": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 KNX" + } + }, + "routing": { + "data": { + "individual_address": "\u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441", + "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant", + "multicast_group": "\u0413\u0440\u0443\u043f\u043f\u0430 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438", + "multicast_port": "\u041f\u043e\u0440\u0442 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438" + }, + "data_description": { + "individual_address": "\u0410\u0434\u0440\u0435\u0441 KNX, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f Home Assistant, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, `0.0.4`", + "local_ip": "\u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435." + }, + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "\u0418\u043c\u044f \u0444\u0430\u0439\u043b\u0430 `.knxkeys` (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435)", + "knxkeys_password": "\u041f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 `.knxkeys`" + }, + "data_description": { + "knxkeys_filename": "\u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0439\u0434\u0435\u043d \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 `.storage/knx/`.\n\u0415\u0441\u043b\u0438 \u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 Home Assistant OS \u044d\u0442\u043e\u0442 \u043f\u0443\u0442\u044c \u0431\u0443\u0434\u0435\u0442 `/config/.storage/knx/`\n\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: `my_project.knxkeys`", + "knxkeys_password": "\u042d\u0442\u043e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0431\u044b\u043b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u043f\u0440\u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0437 ETS." + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0444\u0430\u0439\u043b\u0435 `.knxkeys`." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "\u041f\u0430\u0440\u043e\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", + "user_id": "ID \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", + "user_password": "\u041f\u0430\u0440\u043e\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + }, + "data_description": { + "device_authentication": "\u042d\u0442\u043e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 'IP' \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 ETS.", + "user_id": "\u0427\u0430\u0441\u0442\u043e \u043d\u043e\u043c\u0435\u0440 \u0442\u0443\u043d\u043d\u0435\u043b\u044f +1. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, 'Tunnel 2' \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c User-ID '3'.", + "user_password": "\u041f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0442\u0443\u043d\u043d\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f, \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 'Properties' \u0442\u0443\u043d\u043d\u0435\u043b\u044f \u0432 ETS." + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e IP Secure." + }, + "secure_tunneling": { + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b `.knxkeys`, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043a\u043b\u044e\u0447\u0438 IP secure", + "secure_tunnel_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0438 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e" + } + }, "tunnel": { "data": { + "gateway": "\u0422\u0443\u043d\u043d\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f KNX", "host": "\u0425\u043e\u0441\u0442", "port": "\u041f\u043e\u0440\u0442", "tunneling_type": "\u0422\u0438\u043f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX" @@ -114,7 +230,8 @@ "data_description": { "host": "IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP.", "port": "\u041f\u043e\u0440\u0442 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP." - } + }, + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0448\u043b\u044e\u0437 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430." } } } From 7ddf2e4ca4b5ed755a238ffb23e7a75414d72968 Mon Sep 17 00:00:00 2001 From: kingy444 Date: Sun, 13 Nov 2022 13:58:02 +1100 Subject: [PATCH 0376/1033] Bump Powerview aiopvapi to 2.0.4 (#82014) --- homeassistant/components/hunterdouglas_powerview/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/hunterdouglas_powerview/manifest.json b/homeassistant/components/hunterdouglas_powerview/manifest.json index 15b10dca0e0..568a6ede0b1 100644 --- a/homeassistant/components/hunterdouglas_powerview/manifest.json +++ b/homeassistant/components/hunterdouglas_powerview/manifest.json @@ -2,7 +2,7 @@ "domain": "hunterdouglas_powerview", "name": "Hunter Douglas PowerView", "documentation": "https://www.home-assistant.io/integrations/hunterdouglas_powerview", - "requirements": ["aiopvapi==2.0.3"], + "requirements": ["aiopvapi==2.0.4"], "codeowners": ["@bdraco", "@kingy444", "@trullock"], "config_flow": true, "homekit": { diff --git a/requirements_all.txt b/requirements_all.txt index 20ea4c7862e..90e83196ca2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -235,7 +235,7 @@ aioopenexchangerates==0.4.0 aiopulse==0.4.3 # homeassistant.components.hunterdouglas_powerview -aiopvapi==2.0.3 +aiopvapi==2.0.4 # homeassistant.components.pvpc_hourly_pricing aiopvpc==3.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f365317a7ad..505260d255d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -210,7 +210,7 @@ aioopenexchangerates==0.4.0 aiopulse==0.4.3 # homeassistant.components.hunterdouglas_powerview -aiopvapi==2.0.3 +aiopvapi==2.0.4 # homeassistant.components.pvpc_hourly_pricing aiopvpc==3.0.0 From a848dc11556624f8ebf2a09aff7192b84ab4f66e Mon Sep 17 00:00:00 2001 From: corneyl Date: Sun, 13 Nov 2022 05:15:45 +0100 Subject: [PATCH 0377/1033] Add service for adding products to a Picnic order (#67877) * Add Picnic services for searching products and adding products to the cart * Improve the Picnic services implementation and add unit tests * Fix pre-commit check issues * Fix comments and example product name * Remove search service, update add_product service schema * Fix pylint suggestion * Add more tests and removed unused code * Remove code needed for the removed service, clean tests from obvious comments and add type hints * Remove unused import * Remove unnecessary comments and simplify getting the config entry id Co-authored-by: Allen Porter * Don't use hass.data in tests, make device id mandatory for service * Rewrite all service tests so using lru.cache is not needed * Add test for uncovered line in _product_search() * Require a config entry id as service parameter instead of device id * Use explicit check in get_api_client() and raise HomeAssistantError * Fix HomeAssistantError import, fix services tests * Change HomeAssistantError to ValueError when config entry is not found Co-authored-by: Allen Porter --- homeassistant/components/picnic/__init__.py | 4 + homeassistant/components/picnic/const.py | 8 + homeassistant/components/picnic/services.py | 90 ++++++++ homeassistant/components/picnic/services.yaml | 37 +++ tests/components/picnic/test_services.py | 211 ++++++++++++++++++ 5 files changed, 350 insertions(+) create mode 100644 homeassistant/components/picnic/services.py create mode 100644 homeassistant/components/picnic/services.yaml create mode 100644 tests/components/picnic/test_services.py diff --git a/homeassistant/components/picnic/__init__.py b/homeassistant/components/picnic/__init__.py index a7d26ceb5c6..ec7f6e15425 100644 --- a/homeassistant/components/picnic/__init__.py +++ b/homeassistant/components/picnic/__init__.py @@ -8,6 +8,7 @@ from homeassistant.core import HomeAssistant from .const import CONF_API, CONF_COORDINATOR, CONF_COUNTRY_CODE, DOMAIN from .coordinator import PicnicUpdateCoordinator +from .services import async_register_services PLATFORMS = [Platform.SENSOR] @@ -36,6 +37,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + # Register the services + await async_register_services(hass) + return True diff --git a/homeassistant/components/picnic/const.py b/homeassistant/components/picnic/const.py index f33f58c0eb9..85a7acadaeb 100644 --- a/homeassistant/components/picnic/const.py +++ b/homeassistant/components/picnic/const.py @@ -17,6 +17,14 @@ CONF_API = "api" CONF_COORDINATOR = "coordinator" CONF_COUNTRY_CODE = "country_code" +SERVICE_ADD_PRODUCT_TO_CART = "add_product" + +ATTR_CONFIG_ENTRY_ID = "config_entry_id" +ATTR_PRODUCT_ID = "product_id" +ATTR_PRODUCT_NAME = "product_name" +ATTR_AMOUNT = "amount" +ATTR_PRODUCT_IDENTIFIERS = "product_identifiers" + COUNTRY_CODES = ["NL", "DE", "BE"] ATTRIBUTION = "Data provided by Picnic" ADDRESS = "address" diff --git a/homeassistant/components/picnic/services.py b/homeassistant/components/picnic/services.py new file mode 100644 index 00000000000..565905c76fe --- /dev/null +++ b/homeassistant/components/picnic/services.py @@ -0,0 +1,90 @@ +"""Services for the Picnic integration.""" +from __future__ import annotations + +from python_picnic_api import PicnicAPI +import voluptuous as vol + +from homeassistant.core import HomeAssistant, ServiceCall +import homeassistant.helpers.config_validation as cv + +from .const import ( + ATTR_AMOUNT, + ATTR_CONFIG_ENTRY_ID, + ATTR_PRODUCT_ID, + ATTR_PRODUCT_IDENTIFIERS, + ATTR_PRODUCT_NAME, + CONF_API, + DOMAIN, + SERVICE_ADD_PRODUCT_TO_CART, +) + + +class PicnicServiceException(Exception): + """Exception for Picnic services.""" + + +async def async_register_services(hass: HomeAssistant) -> None: + """Register services for the Picnic integration, if not registered yet.""" + + if hass.services.has_service(DOMAIN, SERVICE_ADD_PRODUCT_TO_CART): + return + + async def async_add_product_service(call: ServiceCall): + api_client = await get_api_client(hass, call.data[ATTR_CONFIG_ENTRY_ID]) + await handle_add_product(hass, api_client, call) + + hass.services.async_register( + DOMAIN, + SERVICE_ADD_PRODUCT_TO_CART, + async_add_product_service, + schema=vol.Schema( + { + vol.Required(ATTR_CONFIG_ENTRY_ID): cv.string, + vol.Exclusive( + ATTR_PRODUCT_ID, ATTR_PRODUCT_IDENTIFIERS + ): cv.positive_int, + vol.Exclusive(ATTR_PRODUCT_NAME, ATTR_PRODUCT_IDENTIFIERS): cv.string, + vol.Optional(ATTR_AMOUNT): vol.All(vol.Coerce(int), vol.Range(min=1)), + } + ), + ) + + +async def get_api_client(hass: HomeAssistant, config_entry_id: str) -> PicnicAPI: + """Get the right Picnic API client based on the device id, else get the default one.""" + if config_entry_id not in hass.data[DOMAIN]: + raise ValueError(f"Config entry with id {config_entry_id} not found!") + return hass.data[DOMAIN][config_entry_id][CONF_API] + + +async def handle_add_product( + hass: HomeAssistant, api_client: PicnicAPI, call: ServiceCall +) -> None: + """Handle the call for the add_product service.""" + product_id = call.data.get("product_id") + if not product_id: + product_id = await hass.async_add_executor_job( + _product_search, api_client, call.data.get("product_name") + ) + + if not product_id: + raise PicnicServiceException("No product found or no product ID given!") + + await hass.async_add_executor_job( + api_client.add_product, str(product_id), call.data.get("amount", 1) + ) + + +def _product_search(api_client: PicnicAPI, product_name: str) -> None | str: + """Query the api client for the product name.""" + search_result = api_client.search(product_name) + + if not search_result or "items" not in search_result[0]: + return None + + # Return the first valid result + for item in search_result[0]["items"]: + if "name" in item: + return str(item["id"]) + + return None diff --git a/homeassistant/components/picnic/services.yaml b/homeassistant/components/picnic/services.yaml new file mode 100644 index 00000000000..9af2cb48291 --- /dev/null +++ b/homeassistant/components/picnic/services.yaml @@ -0,0 +1,37 @@ +add_product: + name: Add a product to the cart + description: >- + Adds a product to the cart based on a search string or product ID. + The search string and product ID are exclusive. + + fields: + config_entry_id: + name: Picnic service + description: The product will be added to the selected service. + required: true + selector: + config_entry: + integration: picnic + product_id: + name: Product ID + description: The product ID of a Picnic product. + required: false + example: "10510201" + selector: + text: + product_name: + name: Product name + description: Search for a product and add the first result + required: false + example: "Yoghurt" + selector: + text: + amount: + name: Amount + description: Amount to add of the selected product + required: false + default: 1 + selector: + number: + min: 1 + max: 50 diff --git a/tests/components/picnic/test_services.py b/tests/components/picnic/test_services.py new file mode 100644 index 00000000000..ab7ff859d6e --- /dev/null +++ b/tests/components/picnic/test_services.py @@ -0,0 +1,211 @@ +"""Tests for the Picnic services.""" +from unittest.mock import MagicMock, patch + +import pytest + +from homeassistant.components.picnic import CONF_COUNTRY_CODE, DOMAIN +from homeassistant.components.picnic.const import SERVICE_ADD_PRODUCT_TO_CART +from homeassistant.components.picnic.services import PicnicServiceException +from homeassistant.const import CONF_ACCESS_TOKEN +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry + +UNIQUE_ID = "295-6y3-1nf4" + + +def create_picnic_api_client(unique_id): + """Create PicnicAPI mock with set response data.""" + auth_token = "af3wh738j3fa28l9fa23lhiufahu7l" + auth_data = { + "user_id": unique_id, + "address": { + "street": "Teststreet", + "house_number": 123, + "house_number_ext": "b", + }, + } + picnic_mock = MagicMock() + picnic_mock.session.auth_token = auth_token + picnic_mock.get_user.return_value = auth_data + + return picnic_mock + + +async def create_picnic_config_entry(hass: HomeAssistant, unique_id): + """Create a Picnic config entry.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_ACCESS_TOKEN: "x-original-picnic-auth-token", + CONF_COUNTRY_CODE: "NL", + }, + unique_id=unique_id, + ) + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + return config_entry + + +@pytest.fixture +def picnic_api_client(): + """Return the default picnic api client.""" + with patch( + "homeassistant.components.picnic.create_picnic_client" + ) as create_picnic_client_mock: + picnic_client_mock = create_picnic_api_client(UNIQUE_ID) + create_picnic_client_mock.return_value = picnic_client_mock + + yield picnic_client_mock + + +@pytest.fixture +async def picnic_config_entry(hass: HomeAssistant): + """Generate the default Picnic config entry.""" + return await create_picnic_config_entry(hass, UNIQUE_ID) + + +async def test_add_product_using_id( + hass: HomeAssistant, + picnic_api_client: MagicMock, + picnic_config_entry: MockConfigEntry, +): + """Test adding a product by id.""" + await hass.services.async_call( + DOMAIN, + SERVICE_ADD_PRODUCT_TO_CART, + { + "config_entry_id": picnic_config_entry.entry_id, + "product_id": "5109348572", + "amount": 3, + }, + blocking=True, + ) + + # Check that the right method is called on the api + picnic_api_client.add_product.assert_called_with("5109348572", 3) + + +async def test_add_product_using_name( + hass: HomeAssistant, + picnic_api_client: MagicMock, + picnic_config_entry: MockConfigEntry, +): + """Test adding a product by name.""" + + # Set the return value of the search api endpoint + picnic_api_client.search.return_value = [ + { + "items": [ + { + "id": "2525404", + "name": "Best tea", + "display_price": 321, + "unit_quantity": "big bags", + }, + { + "id": "2525500", + "name": "Cheap tea", + "display_price": 100, + "unit_quantity": "small bags", + }, + ] + } + ] + + await hass.services.async_call( + DOMAIN, + SERVICE_ADD_PRODUCT_TO_CART, + {"config_entry_id": picnic_config_entry.entry_id, "product_name": "Tea"}, + blocking=True, + ) + + # Check that the right method is called on the api + picnic_api_client.add_product.assert_called_with("2525404", 1) + + +async def test_add_product_using_name_no_results( + hass: HomeAssistant, + picnic_api_client: MagicMock, + picnic_config_entry: MockConfigEntry, +): + """Test adding a product by name that can't be found.""" + + # Set the search return value and check that the right exception is raised during the service call + picnic_api_client.search.return_value = [] + with pytest.raises(PicnicServiceException): + await hass.services.async_call( + DOMAIN, + SERVICE_ADD_PRODUCT_TO_CART, + { + "config_entry_id": picnic_config_entry.entry_id, + "product_name": "Random non existing product", + }, + blocking=True, + ) + + +async def test_add_product_using_name_no_named_results( + hass: HomeAssistant, + picnic_api_client: MagicMock, + picnic_config_entry: MockConfigEntry, +): + """Test adding a product by name for which no named results are returned.""" + + # Set the search return value and check that the right exception is raised during the service call + picnic_api_client.search.return_value = [{"items": [{"attr": "test"}]}] + with pytest.raises(PicnicServiceException): + await hass.services.async_call( + DOMAIN, + SERVICE_ADD_PRODUCT_TO_CART, + { + "config_entry_id": picnic_config_entry.entry_id, + "product_name": "Random product", + }, + blocking=True, + ) + + +async def test_add_product_multiple_config_entries( + hass: HomeAssistant, + picnic_api_client: MagicMock, + picnic_config_entry: MockConfigEntry, +): + """Test adding a product for a specific Picnic service while multiple are configured.""" + with patch( + "homeassistant.components.picnic.create_picnic_client" + ) as create_picnic_client_mock: + picnic_api_client_2 = create_picnic_api_client("3fj9-9gju-236") + create_picnic_client_mock.return_value = picnic_api_client_2 + picnic_config_entry_2 = await create_picnic_config_entry(hass, "3fj9-9gju-236") + + await hass.services.async_call( + DOMAIN, + SERVICE_ADD_PRODUCT_TO_CART, + {"product_id": "5109348572", "config_entry_id": picnic_config_entry_2.entry_id}, + blocking=True, + ) + + # Check that the right method is called on the api + picnic_api_client.add_product.assert_not_called() + picnic_api_client_2.add_product.assert_called_with("5109348572", 1) + + +async def test_add_product_device_doesnt_exist( + hass: HomeAssistant, + picnic_api_client: MagicMock, + picnic_config_entry: MockConfigEntry, +): + """Test adding a product for a specific Picnic service, which doesn't exist.""" + with pytest.raises(ValueError): + await hass.services.async_call( + DOMAIN, + SERVICE_ADD_PRODUCT_TO_CART, + {"product_id": "5109348572", "config_entry_id": 12345}, + blocking=True, + ) + + # Check that the right method is called on the api + picnic_api_client.add_product.assert_not_called() From 636bfe0f7b2ca568b0db0c6dfa6e3f9875246dbf Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 13 Nov 2022 00:33:26 -0800 Subject: [PATCH 0378/1033] Bump pyrainbird to 0.6.1 (#82009) * Bump pyrainbird to 0.6.0 * Bump pyrainbird to 0.6.1 --- homeassistant/components/rainbird/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/rainbird/manifest.json b/homeassistant/components/rainbird/manifest.json index 47bb7ce9bd9..7023283e66b 100644 --- a/homeassistant/components/rainbird/manifest.json +++ b/homeassistant/components/rainbird/manifest.json @@ -2,7 +2,7 @@ "domain": "rainbird", "name": "Rain Bird", "documentation": "https://www.home-assistant.io/integrations/rainbird", - "requirements": ["pyrainbird==0.4.3"], + "requirements": ["pyrainbird==0.6.1"], "codeowners": ["@konikvranik"], "iot_class": "local_polling", "loggers": ["pyrainbird"] diff --git a/requirements_all.txt b/requirements_all.txt index 90e83196ca2..64bc5a88e78 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1851,7 +1851,7 @@ pyqwikswitch==0.93 pyrail==0.0.3 # homeassistant.components.rainbird -pyrainbird==0.4.3 +pyrainbird==0.6.1 # homeassistant.components.recswitch pyrecswitch==1.0.2 From 62bd692a5833ecba901f8e84aa8e3d3ba611f104 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Nov 2022 02:47:24 -0600 Subject: [PATCH 0379/1033] Bump bluetooth-data-tools to 0.3.0 (#82016) prep work for #82007 changelog: https://github.com/Bluetooth-Devices/bluetooth-data-tools/compare/v0.2.0...v0.3.0 --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 4 +++- requirements_test_all.txt | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index e2daee3650d..b2b4c771e51 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -10,7 +10,7 @@ "bleak-retry-connector==2.8.4", "bluetooth-adapters==0.7.0", "bluetooth-auto-recovery==0.3.6", - "bluetooth-data-tools==0.2.0", + "bluetooth-data-tools==0.3.0", "dbus-fast==1.73.0" ], "codeowners": ["@bdraco"], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 603d1bba36a..d75ba977556 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -14,7 +14,7 @@ bleak-retry-connector==2.8.4 bleak==0.19.2 bluetooth-adapters==0.7.0 bluetooth-auto-recovery==0.3.6 -bluetooth-data-tools==0.2.0 +bluetooth-data-tools==0.3.0 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.3 diff --git a/requirements_all.txt b/requirements_all.txt index 64bc5a88e78..4e9d3aa8f1a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -452,10 +452,12 @@ bluetooth-adapters==0.7.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.3.6 -# homeassistant.components.bluetooth # homeassistant.components.led_ble bluetooth-data-tools==0.2.0 +# homeassistant.components.bluetooth +bluetooth-data-tools==0.3.0 + # homeassistant.components.bond bond-async==0.1.22 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 505260d255d..494309b5109 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -366,10 +366,12 @@ bluetooth-adapters==0.7.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.3.6 -# homeassistant.components.bluetooth # homeassistant.components.led_ble bluetooth-data-tools==0.2.0 +# homeassistant.components.bluetooth +bluetooth-data-tools==0.3.0 + # homeassistant.components.bond bond-async==0.1.22 From e4ecaa433a200585b747716a989c90d491d53e29 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Nov 2022 04:28:49 -0600 Subject: [PATCH 0380/1033] Update bluetooth util for upcoming bleak (#82020) Fixes ``` Nov 08 21:41:14 homeassistant homeassistant[474]: /usr/src/homeassistant/homeassistant/components/bluetooth/util.py:39: FutureWarning: BLEDevice.rssi is deprecated and will be removed in a future version of Bleak, use AdvertisementData.rssi instead Nov 08 21:41:14 homeassistant homeassistant[474]: rssi=history.device.rssi, ``` --- homeassistant/components/bluetooth/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/bluetooth/util.py b/homeassistant/components/bluetooth/util.py index 181796d3d2d..abfba6b22bc 100644 --- a/homeassistant/components/bluetooth/util.py +++ b/homeassistant/components/bluetooth/util.py @@ -36,7 +36,7 @@ async def async_load_history_from_system() -> dict[str, BluetoothServiceInfoBlea or history.device.name or history.device.address, address=history.device.address, - rssi=history.device.rssi, + rssi=history.advertisement_data.rssi, manufacturer_data=history.advertisement_data.manufacturer_data, service_data=history.advertisement_data.service_data, service_uuids=history.advertisement_data.service_uuids, From ebffe0f33b61e87c348bb7c99714c1d551623f9c Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Sun, 13 Nov 2022 08:30:16 -0500 Subject: [PATCH 0381/1033] Fix ZHA configuration APIs (#81874) * Fix ZHA configuration loading and saving issues * add tests --- homeassistant/components/zha/api.py | 10 +- homeassistant/components/zha/core/helpers.py | 4 +- tests/components/zha/data.py | 153 +++++++++++++++++++ tests/components/zha/test_api.py | 75 +++++++++ 4 files changed, 239 insertions(+), 3 deletions(-) create mode 100644 tests/components/zha/data.py diff --git a/homeassistant/components/zha/api.py b/homeassistant/components/zha/api.py index c68136c23da..eb5fc2e4343 100644 --- a/homeassistant/components/zha/api.py +++ b/homeassistant/components/zha/api.py @@ -1090,11 +1090,17 @@ async def websocket_update_zha_configuration( ): data_to_save[CUSTOM_CONFIGURATION][section].pop(entry) # remove entire section block if empty - if not data_to_save[CUSTOM_CONFIGURATION][section]: + if ( + not data_to_save[CUSTOM_CONFIGURATION].get(section) + and section in data_to_save[CUSTOM_CONFIGURATION] + ): data_to_save[CUSTOM_CONFIGURATION].pop(section) # remove entire custom_configuration block if empty - if not data_to_save[CUSTOM_CONFIGURATION]: + if ( + not data_to_save.get(CUSTOM_CONFIGURATION) + and CUSTOM_CONFIGURATION in data_to_save + ): data_to_save.pop(CUSTOM_CONFIGURATION) _LOGGER.info( diff --git a/homeassistant/components/zha/core/helpers.py b/homeassistant/components/zha/core/helpers.py index 1ea9a2a4c9b..2bc7d53fd79 100644 --- a/homeassistant/components/zha/core/helpers.py +++ b/homeassistant/components/zha/core/helpers.py @@ -221,11 +221,13 @@ def async_get_zha_config_value( ) -def async_cluster_exists(hass, cluster_id): +def async_cluster_exists(hass, cluster_id, skip_coordinator=True): """Determine if a device containing the specified in cluster is paired.""" zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] zha_devices = zha_gateway.devices.values() for zha_device in zha_devices: + if skip_coordinator and zha_device.is_coordinator: + continue clusters_by_endpoint = zha_device.async_get_clusters() for clusters in clusters_by_endpoint.values(): if ( diff --git a/tests/components/zha/data.py b/tests/components/zha/data.py new file mode 100644 index 00000000000..8b613ec2971 --- /dev/null +++ b/tests/components/zha/data.py @@ -0,0 +1,153 @@ +"""Test data for ZHA API tests.""" + +BASE_CUSTOM_CONFIGURATION = { + "schemas": { + "zha_options": [ + { + "type": "integer", + "valueMin": 0, + "name": "default_light_transition", + "optional": True, + "default": 0, + }, + { + "type": "boolean", + "name": "enhanced_light_transition", + "required": True, + "default": False, + }, + { + "type": "boolean", + "name": "light_transitioning_flag", + "required": True, + "default": True, + }, + { + "type": "boolean", + "name": "always_prefer_xy_color_mode", + "required": True, + "default": True, + }, + { + "type": "boolean", + "name": "enable_identify_on_join", + "required": True, + "default": True, + }, + { + "type": "integer", + "valueMin": 0, + "name": "consider_unavailable_mains", + "optional": True, + "default": 7200, + }, + { + "type": "integer", + "valueMin": 0, + "name": "consider_unavailable_battery", + "optional": True, + "default": 21600, + }, + ] + }, + "data": { + "zha_options": { + "enhanced_light_transition": True, + "default_light_transition": 0, + "light_transitioning_flag": True, + "always_prefer_xy_color_mode": True, + "enable_identify_on_join": True, + "consider_unavailable_mains": 7200, + "consider_unavailable_battery": 21600, + } + }, +} + +CONFIG_WITH_ALARM_OPTIONS = { + "schemas": { + "zha_options": [ + { + "type": "integer", + "valueMin": 0, + "name": "default_light_transition", + "optional": True, + "default": 0, + }, + { + "type": "boolean", + "name": "enhanced_light_transition", + "required": True, + "default": False, + }, + { + "type": "boolean", + "name": "light_transitioning_flag", + "required": True, + "default": True, + }, + { + "type": "boolean", + "name": "always_prefer_xy_color_mode", + "required": True, + "default": True, + }, + { + "type": "boolean", + "name": "enable_identify_on_join", + "required": True, + "default": True, + }, + { + "type": "integer", + "valueMin": 0, + "name": "consider_unavailable_mains", + "optional": True, + "default": 7200, + }, + { + "type": "integer", + "valueMin": 0, + "name": "consider_unavailable_battery", + "optional": True, + "default": 21600, + }, + ], + "zha_alarm_options": [ + { + "type": "string", + "name": "alarm_master_code", + "required": True, + "default": "1234", + }, + { + "type": "integer", + "valueMin": 0, + "name": "alarm_failed_tries", + "required": True, + "default": 3, + }, + { + "type": "boolean", + "name": "alarm_arm_requires_code", + "required": True, + "default": False, + }, + ], + }, + "data": { + "zha_options": { + "enhanced_light_transition": True, + "default_light_transition": 0, + "light_transitioning_flag": True, + "always_prefer_xy_color_mode": True, + "enable_identify_on_join": True, + "consider_unavailable_mains": 7200, + "consider_unavailable_battery": 21600, + }, + "zha_alarm_options": { + "alarm_arm_requires_code": False, + "alarm_master_code": "4321", + "alarm_failed_tries": 2, + }, + }, +} diff --git a/tests/components/zha/test_api.py b/tests/components/zha/test_api.py index e4daf7f365e..defc9842b01 100644 --- a/tests/components/zha/test_api.py +++ b/tests/components/zha/test_api.py @@ -1,5 +1,6 @@ """Test ZHA API.""" from binascii import unhexlify +from copy import deepcopy from unittest.mock import AsyncMock, patch import pytest @@ -8,6 +9,7 @@ import zigpy.backups import zigpy.profiles.zha import zigpy.types import zigpy.zcl.clusters.general as general +import zigpy.zcl.clusters.security as security from homeassistant.components.websocket_api import const from homeassistant.components.zha import DOMAIN @@ -50,6 +52,7 @@ from .conftest import ( SIG_EP_PROFILE, SIG_EP_TYPE, ) +from .data import BASE_CUSTOM_CONFIGURATION, CONFIG_WITH_ALARM_OPTIONS IEEE_SWITCH_DEVICE = "01:2d:6f:00:0a:90:69:e7" IEEE_GROUPABLE_DEVICE = "01:2d:6f:00:0a:90:69:e8" @@ -61,6 +64,7 @@ def required_platform_only(): with patch( "homeassistant.components.zha.PLATFORMS", ( + Platform.ALARM_CONTROL_PANEL, Platform.SELECT, Platform.SENSOR, Platform.SWITCH, @@ -89,6 +93,25 @@ async def device_switch(hass, zigpy_device_mock, zha_device_joined): return zha_device +@pytest.fixture +async def device_ias_ace(hass, zigpy_device_mock, zha_device_joined): + """Test alarm control panel device.""" + + zigpy_device = zigpy_device_mock( + { + 1: { + SIG_EP_INPUT: [security.IasAce.cluster_id], + SIG_EP_OUTPUT: [], + SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.IAS_ANCILLARY_CONTROL, + SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID, + } + }, + ) + zha_device = await zha_device_joined(zigpy_device) + zha_device.available = True + return zha_device + + @pytest.fixture async def device_groupable(hass, zigpy_device_mock, zha_device_joined): """Test zha light platform.""" @@ -225,6 +248,58 @@ async def test_list_devices(zha_client): assert device == device2 +async def test_get_zha_config(zha_client): + """Test getting zha custom configuration.""" + await zha_client.send_json({ID: 5, TYPE: "zha/configuration"}) + + msg = await zha_client.receive_json() + + configuration = msg["result"] + assert configuration == BASE_CUSTOM_CONFIGURATION + + +async def test_get_zha_config_with_alarm(hass, zha_client, device_ias_ace): + """Test getting zha custom configuration.""" + await zha_client.send_json({ID: 5, TYPE: "zha/configuration"}) + + msg = await zha_client.receive_json() + + configuration = msg["result"] + assert configuration == CONFIG_WITH_ALARM_OPTIONS + + # test that the alarm options are not in the config when we remove the device + device_ias_ace.gateway.device_removed(device_ias_ace.device) + await hass.async_block_till_done() + await zha_client.send_json({ID: 6, TYPE: "zha/configuration"}) + + msg = await zha_client.receive_json() + + configuration = msg["result"] + assert configuration == BASE_CUSTOM_CONFIGURATION + + +async def test_update_zha_config(zha_client, zigpy_app_controller): + """Test updating zha custom configuration.""" + + configuration = deepcopy(CONFIG_WITH_ALARM_OPTIONS) + configuration["data"]["zha_options"]["default_light_transition"] = 10 + + with patch( + "bellows.zigbee.application.ControllerApplication.new", + return_value=zigpy_app_controller, + ): + await zha_client.send_json( + {ID: 5, TYPE: "zha/configuration/update", "data": configuration["data"]} + ) + msg = await zha_client.receive_json() + assert msg["success"] + + await zha_client.send_json({ID: 6, TYPE: "zha/configuration"}) + msg = await zha_client.receive_json() + configuration = msg["result"] + assert configuration == configuration + + async def test_device_not_found(zha_client): """Test not found response from get device API.""" await zha_client.send_json( From edad5ece585ded5e701a2bca0b68a31db4cdd8ab Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 13 Nov 2022 06:58:59 -0800 Subject: [PATCH 0382/1033] Bump gcal_sync 4.0.2 (#82017) --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index 2bc84827cd6..7de3a735b96 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==4.0.1", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==4.0.2", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index 4e9d3aa8f1a..b62139ec52a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -740,7 +740,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==4.0.1 +gcal-sync==4.0.2 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 494309b5109..c32fa4fb91f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -556,7 +556,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==4.0.1 +gcal-sync==4.0.2 # homeassistant.components.geocaching geocachingapi==0.2.1 From 50c686343ef0e47046cb79290cbe81bfd4c80d21 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sun, 13 Nov 2022 16:05:54 +0100 Subject: [PATCH 0383/1033] Bump pysensibo to 1.0.21 (#82023) * pysensibo 1.0.21 * Add name to fixture --- homeassistant/components/sensibo/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/sensibo/fixtures/data.json | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensibo/manifest.json b/homeassistant/components/sensibo/manifest.json index 4f89148a21c..af0a4b00f23 100644 --- a/homeassistant/components/sensibo/manifest.json +++ b/homeassistant/components/sensibo/manifest.json @@ -2,7 +2,7 @@ "domain": "sensibo", "name": "Sensibo", "documentation": "https://www.home-assistant.io/integrations/sensibo", - "requirements": ["pysensibo==1.0.20"], + "requirements": ["pysensibo==1.0.21"], "config_flow": true, "codeowners": ["@andrey-git", "@gjohansson-ST"], "iot_class": "cloud_polling", diff --git a/requirements_all.txt b/requirements_all.txt index b62139ec52a..e0d1357197f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1880,7 +1880,7 @@ pysaj==0.0.16 pysdcp==1 # homeassistant.components.sensibo -pysensibo==1.0.20 +pysensibo==1.0.21 # homeassistant.components.serial # homeassistant.components.zha diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c32fa4fb91f..e4ad9368292 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1330,7 +1330,7 @@ pyruckus==0.16 pysabnzbd==1.1.1 # homeassistant.components.sensibo -pysensibo==1.0.20 +pysensibo==1.0.21 # homeassistant.components.serial # homeassistant.components.zha diff --git a/tests/components/sensibo/fixtures/data.json b/tests/components/sensibo/fixtures/data.json index 5837296d154..7db7b4a7c4a 100644 --- a/tests/components/sensibo/fixtures/data.json +++ b/tests/components/sensibo/fixtures/data.json @@ -110,6 +110,7 @@ { "id": "11", "isEnabled": false, + "name": null, "acState": { "on": false, "targetTemperature": 21, From 3658c5f67371057b7787b1e6b234e5a8a721f2eb Mon Sep 17 00:00:00 2001 From: Malte Franken Date: Mon, 14 Nov 2022 02:06:33 +1100 Subject: [PATCH 0384/1033] Add integration_type to nsw_rural_fire_service_feed (#82031) define integration type --- .../components/nsw_rural_fire_service_feed/manifest.json | 3 ++- homeassistant/generated/integrations.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/nsw_rural_fire_service_feed/manifest.json b/homeassistant/components/nsw_rural_fire_service_feed/manifest.json index 694089b1396..97e67f1f0a4 100644 --- a/homeassistant/components/nsw_rural_fire_service_feed/manifest.json +++ b/homeassistant/components/nsw_rural_fire_service_feed/manifest.json @@ -5,5 +5,6 @@ "requirements": ["aio_geojson_nsw_rfs_incidents==0.4"], "codeowners": ["@exxamalte"], "iot_class": "cloud_polling", - "loggers": ["aio_geojson_nsw_rfs_incidents"] + "loggers": ["aio_geojson_nsw_rfs_incidents"], + "integration_type": "service" } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index aa6221a1140..0ca0f01209c 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -3575,7 +3575,7 @@ }, "nsw_rural_fire_service_feed": { "name": "NSW Rural Fire Service Incidents", - "integration_type": "hub", + "integration_type": "service", "config_flow": false, "iot_class": "cloud_polling" }, From 8252d7f3b507b35d519f30647ed16bc37ecb2786 Mon Sep 17 00:00:00 2001 From: Vincent Giorgi Date: Sun, 13 Nov 2022 16:11:07 +0100 Subject: [PATCH 0385/1033] Bump airthings-ble to 0.5.3 (#82033) Bump airthings-ble --- homeassistant/components/airthings_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/airthings_ble/manifest.json b/homeassistant/components/airthings_ble/manifest.json index dca2dbbb562..422a51c7187 100644 --- a/homeassistant/components/airthings_ble/manifest.json +++ b/homeassistant/components/airthings_ble/manifest.json @@ -3,7 +3,7 @@ "name": "Airthings BLE", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/airthings_ble", - "requirements": ["airthings-ble==0.5.2"], + "requirements": ["airthings-ble==0.5.3"], "dependencies": ["bluetooth"], "codeowners": ["@vincegio"], "iot_class": "local_polling", diff --git a/requirements_all.txt b/requirements_all.txt index e0d1357197f..23251e23e47 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -300,7 +300,7 @@ aioymaps==1.2.2 airly==1.1.0 # homeassistant.components.airthings_ble -airthings-ble==0.5.2 +airthings-ble==0.5.3 # homeassistant.components.airthings airthings_cloud==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e4ad9368292..90b0509be1b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -275,7 +275,7 @@ aioymaps==1.2.2 airly==1.1.0 # homeassistant.components.airthings_ble -airthings-ble==0.5.2 +airthings-ble==0.5.3 # homeassistant.components.airthings airthings_cloud==0.1.0 From bfd2eb50b22f0c9176dabfd513b6e95e7c545f18 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Sun, 13 Nov 2022 10:34:29 -0500 Subject: [PATCH 0386/1033] Detect Early Access versions of UniFi Protect and Warn User (#81758) --- .../components/unifiprotect/__init__.py | 102 ++++++++------- .../components/unifiprotect/config_flow.py | 6 + .../components/unifiprotect/const.py | 1 + .../components/unifiprotect/manifest.json | 2 +- .../components/unifiprotect/repairs.py | 96 ++++++++++++++ .../components/unifiprotect/strings.json | 20 ++- .../unifiprotect/translations/en.json | 22 +++- .../components/unifiprotect/utils.py | 39 +++++- tests/components/unifiprotect/conftest.py | 2 +- .../unifiprotect/test_config_flow.py | 3 +- tests/components/unifiprotect/test_init.py | 6 +- .../unifiprotect/test_media_source.py | 8 +- tests/components/unifiprotect/test_repairs.py | 120 ++++++++++++++++++ 13 files changed, 368 insertions(+), 59 deletions(-) create mode 100644 homeassistant/components/unifiprotect/repairs.py create mode 100644 tests/components/unifiprotect/test_repairs.py diff --git a/homeassistant/components/unifiprotect/__init__.py b/homeassistant/components/unifiprotect/__init__.py index 174c5476cff..4e659d39cc5 100644 --- a/homeassistant/components/unifiprotect/__init__.py +++ b/homeassistant/components/unifiprotect/__init__.py @@ -5,31 +5,19 @@ import asyncio from datetime import timedelta import logging -from aiohttp import CookieJar from aiohttp.client_exceptions import ServerDisconnectedError -from pyunifiprotect import ProtectApiClient from pyunifiprotect.exceptions import ClientError, NotAuthorized from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - CONF_HOST, - CONF_PASSWORD, - CONF_PORT, - CONF_USERNAME, - CONF_VERIFY_SSL, - EVENT_HOMEASSISTANT_STOP, -) +from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import device_registry as dr, issue_registry as ir -from homeassistant.helpers.aiohttp_client import async_create_clientsession from homeassistant.helpers.issue_registry import IssueSeverity from .const import ( - CONF_ALL_UPDATES, - CONF_OVERRIDE_CHOST, + CONF_ALLOW_EA, DEFAULT_SCAN_INTERVAL, - DEVICES_FOR_SUBSCRIBE, DEVICES_THAT_ADOPT, DOMAIN, MIN_REQUIRED_PROTECT_V, @@ -40,7 +28,11 @@ from .data import ProtectData, async_ufp_instance_for_config_entry_ids from .discovery import async_start_discovery from .migrate import async_migrate_data from .services import async_cleanup_services, async_setup_services -from .utils import _async_unifi_mac_from_hass, async_get_devices +from .utils import ( + _async_unifi_mac_from_hass, + async_create_api_client, + async_get_devices, +) from .views import ThumbnailProxyView, VideoProxyView _LOGGER = logging.getLogger(__name__) @@ -52,19 +44,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the UniFi Protect config entries.""" async_start_discovery(hass) - session = async_create_clientsession(hass, cookie_jar=CookieJar(unsafe=True)) - protect = ProtectApiClient( - host=entry.data[CONF_HOST], - port=entry.data[CONF_PORT], - username=entry.data[CONF_USERNAME], - password=entry.data[CONF_PASSWORD], - verify_ssl=entry.data[CONF_VERIFY_SSL], - session=session, - subscribed_models=DEVICES_FOR_SUBSCRIBE, - override_connection_host=entry.options.get(CONF_OVERRIDE_CHOST, False), - ignore_stats=not entry.options.get(CONF_ALL_UPDATES, False), - ignore_unadopted=False, - ) + protect = async_create_api_client(hass, entry) _LOGGER.debug("Connect to UniFi Protect") data_service = ProtectData(hass, protect, SCAN_INTERVAL, entry) @@ -83,42 +63,75 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) return False - await async_migrate_data(hass, entry, protect) if entry.unique_id is None: hass.config_entries.async_update_entry(entry, unique_id=nvr_info.mac) - await data_service.async_setup() - if not data_service.last_update_success: - raise ConfigEntryNotReady - hass.data.setdefault(DOMAIN, {})[entry.entry_id] = data_service - await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - async_setup_services(hass) - hass.http.register_view(ThumbnailProxyView(hass)) - hass.http.register_view(VideoProxyView(hass)) - entry.async_on_unload(entry.add_update_listener(_async_options_updated)) entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, data_service.async_stop) ) - if await data_service.api.bootstrap.get_is_prerelease(): - protect_version = data_service.api.bootstrap.nvr.version + if ( + not entry.options.get(CONF_ALLOW_EA, False) + and await nvr_info.get_is_prerelease() + ): ir.async_create_issue( hass, DOMAIN, - f"ea_warning_{protect_version}", - is_fixable=False, - is_persistent=False, + "ea_warning", + is_fixable=True, + is_persistent=True, learn_more_url="https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access", severity=IssueSeverity.WARNING, translation_key="ea_warning", - translation_placeholders={"version": str(protect_version)}, + translation_placeholders={"version": str(nvr_info.version)}, + data={"entry_id": entry.entry_id}, ) + try: + await _async_setup_entry(hass, entry, data_service) + except Exception as err: + if await nvr_info.get_is_prerelease(): + # If they are running a pre-release, its quite common for setup + # to fail so we want to create a repair issue for them so its + # obvious what the problem is. + ir.async_create_issue( + hass, + DOMAIN, + f"ea_setup_failed_{nvr_info.version}", + is_fixable=False, + is_persistent=False, + learn_more_url="https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access", + severity=IssueSeverity.ERROR, + translation_key="ea_setup_failed", + translation_placeholders={ + "error": str(err), + "version": str(nvr_info.version), + }, + ) + ir.async_delete_issue(hass, DOMAIN, "ea_warning") + _LOGGER.exception("Error setting up UniFi Protect integration: %s", err) + raise + return True +async def _async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, data_service: ProtectData +) -> None: + await async_migrate_data(hass, entry, data_service.api) + + await data_service.async_setup() + if not data_service.last_update_success: + raise ConfigEntryNotReady + + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + async_setup_services(hass) + hass.http.register_view(ThumbnailProxyView(hass)) + hass.http.register_view(VideoProxyView(hass)) + + async def _async_options_updated(hass: HomeAssistant, entry: ConfigEntry) -> None: """Update options.""" await hass.config_entries.async_reload(entry.entry_id) @@ -126,6 +139,7 @@ async def _async_options_updated(hass: HomeAssistant, entry: ConfigEntry) -> Non async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload UniFi Protect config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): data: ProtectData = hass.data[DOMAIN][entry.entry_id] await data.async_stop() diff --git a/homeassistant/components/unifiprotect/config_flow.py b/homeassistant/components/unifiprotect/config_flow.py index f07ca923a53..571922d8651 100644 --- a/homeassistant/components/unifiprotect/config_flow.py +++ b/homeassistant/components/unifiprotect/config_flow.py @@ -34,6 +34,7 @@ from homeassistant.util.network import is_ip_address from .const import ( CONF_ALL_UPDATES, + CONF_ALLOW_EA, CONF_DISABLE_RTSP, CONF_MAX_MEDIA, CONF_OVERRIDE_CHOST, @@ -224,6 +225,7 @@ class ProtectFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): CONF_ALL_UPDATES: False, CONF_OVERRIDE_CHOST: False, CONF_MAX_MEDIA: DEFAULT_MAX_MEDIA, + CONF_ALLOW_EA: False, }, ) @@ -392,6 +394,10 @@ class OptionsFlowHandler(config_entries.OptionsFlow): CONF_MAX_MEDIA, DEFAULT_MAX_MEDIA ), ): vol.All(vol.Coerce(int), vol.Range(min=100, max=10000)), + vol.Optional( + CONF_ALLOW_EA, + default=self.config_entry.options.get(CONF_ALLOW_EA, False), + ): bool, } ), ) diff --git a/homeassistant/components/unifiprotect/const.py b/homeassistant/components/unifiprotect/const.py index 93a0fa5ff74..f751ed6a009 100644 --- a/homeassistant/components/unifiprotect/const.py +++ b/homeassistant/components/unifiprotect/const.py @@ -20,6 +20,7 @@ CONF_DISABLE_RTSP = "disable_rtsp" CONF_ALL_UPDATES = "all_updates" CONF_OVERRIDE_CHOST = "override_connection_host" CONF_MAX_MEDIA = "max_media" +CONF_ALLOW_EA = "allow_ea" CONFIG_OPTIONS = [ CONF_ALL_UPDATES, diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index 7a412fede1d..d9d7b76db41 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -5,7 +5,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/unifiprotect", "requirements": ["pyunifiprotect==4.4.1", "unifi-discovery==1.1.7"], - "dependencies": ["http"], + "dependencies": ["http", "repairs"], "codeowners": ["@briis", "@AngellusMortis", "@bdraco"], "quality_scale": "platinum", "iot_class": "local_push", diff --git a/homeassistant/components/unifiprotect/repairs.py b/homeassistant/components/unifiprotect/repairs.py new file mode 100644 index 00000000000..98846113e5e --- /dev/null +++ b/homeassistant/components/unifiprotect/repairs.py @@ -0,0 +1,96 @@ +"""unifiprotect.repairs.""" + +from __future__ import annotations + +from typing import cast + +from pyunifiprotect import ProtectApiClient +import voluptuous as vol + +from homeassistant import data_entry_flow +from homeassistant.components.repairs import ConfirmRepairFlow, RepairsFlow +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.issue_registry import async_get as async_get_issue_registry + +from .const import CONF_ALLOW_EA +from .utils import async_create_api_client + + +class EAConfirm(RepairsFlow): + """Handler for an issue fixing flow.""" + + _api: ProtectApiClient + _entry: ConfigEntry + + def __init__(self, api: ProtectApiClient, entry: ConfigEntry) -> None: + """Create flow.""" + + self._api = api + self._entry = entry + super().__init__() + + @callback + def _async_get_placeholders(self) -> dict[str, str] | None: + issue_registry = async_get_issue_registry(self.hass) + description_placeholders = None + if issue := issue_registry.async_get_issue(self.handler, self.issue_id): + description_placeholders = issue.translation_placeholders + + return description_placeholders + + async def async_step_init( + self, user_input: dict[str, str] | None = None + ) -> data_entry_flow.FlowResult: + """Handle the first step of a fix flow.""" + + return await self.async_step_start() + + async def async_step_start( + self, user_input: dict[str, str] | None = None + ) -> data_entry_flow.FlowResult: + """Handle the confirm step of a fix flow.""" + if user_input is None: + placeholders = self._async_get_placeholders() + return self.async_show_form( + step_id="start", + data_schema=vol.Schema({}), + description_placeholders=placeholders, + ) + + nvr = await self._api.get_nvr() + if await nvr.get_is_prerelease(): + return await self.async_step_confirm() + await self.hass.config_entries.async_reload(self._entry.entry_id) + return self.async_create_entry(title="", data={}) + + async def async_step_confirm( + self, user_input: dict[str, str] | None = None + ) -> data_entry_flow.FlowResult: + """Handle the confirm step of a fix flow.""" + if user_input is not None: + options = dict(self._entry.options) + options[CONF_ALLOW_EA] = True + self.hass.config_entries.async_update_entry(self._entry, options=options) + return self.async_create_entry(title="", data={}) + + placeholders = self._async_get_placeholders() + return self.async_show_form( + step_id="confirm", + data_schema=vol.Schema({}), + description_placeholders=placeholders, + ) + + +async def async_create_fix_flow( + hass: HomeAssistant, + issue_id: str, + data: dict[str, str | int | float | None] | None, +) -> RepairsFlow: + """Create flow.""" + if data is not None and issue_id == "ea_warning": + entry_id = cast(str, data["entry_id"]) + if (entry := hass.config_entries.async_get_entry(entry_id)) is not None: + api = async_create_api_client(hass, entry) + return EAConfirm(api, entry) + return ConfirmRepairFlow() diff --git a/homeassistant/components/unifiprotect/strings.json b/homeassistant/components/unifiprotect/strings.json index 5c9416440de..abac7701279 100644 --- a/homeassistant/components/unifiprotect/strings.json +++ b/homeassistant/components/unifiprotect/strings.json @@ -50,7 +50,8 @@ "disable_rtsp": "Disable the RTSP stream", "all_updates": "Realtime metrics (WARNING: Greatly increases CPU usage)", "override_connection_host": "Override Connection Host", - "max_media": "Max number of event to load for Media Browser (increases RAM usage)" + "max_media": "Max number of event to load for Media Browser (increases RAM usage)", + "allow_ea": "Allow Early Access versions of Protect (WARNING: Will mark your integration as unsupported)" } } } @@ -58,7 +59,22 @@ "issues": { "ea_warning": { "title": "UniFi Protect v{version} is an Early Access version", - "description": "You are using v{version} of UniFi Protect which is an Early Access version. Early Access versions are not supported by Home Assistant and may cause your UniFi Protect integration to break or not work as expected." + "fix_flow": { + "step": { + "start": { + "title": "v{version} is an Early Access version", + "description": "You are using v{version} of UniFi Protect which is an Early Access version. [Early Access versions are not supported by Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) and it is recommended to go back to a stable release as soon as possible.\n\nBy submitting this form you have either [downgraded UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) or you agree to run an unsupported version of UniFi Protect." + }, + "confirm": { + "title": "v{version} is an Early Access version", + "description": "Are you sure you want to run unsupported versions of UniFi Protect? This may cause your Home Assistant integration to break." + } + } + } + }, + "ea_setup_failed": { + "title": "Setup error using Early Access version", + "description": "You are using v{version} of UniFi Protect which is an Early Access version. An unrecoverable error occurred while trying to load the integration. Please [downgrade to a stable version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) of UniFi Protect to continue using the integration.\n\nError: {error}" } } } diff --git a/homeassistant/components/unifiprotect/translations/en.json b/homeassistant/components/unifiprotect/translations/en.json index 742a1b3a851..65a398375fe 100644 --- a/homeassistant/components/unifiprotect/translations/en.json +++ b/homeassistant/components/unifiprotect/translations/en.json @@ -42,21 +42,33 @@ } }, "issues": { + "ea_setup_failed": { + "description": "You are using v{version} of UniFi Protect which is an Early Access version. An unrecoverable error occurred while trying to load the integration. Please [downgrade to a stable version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) of UniFi Protect to continue using the integration.\n\nError: {error}", + "title": "Setup error using Early Access version" + }, "ea_warning": { - "description": "You are using v{version} of UniFi Protect which is an Early Access version. Early Access versions are not supported by Home Assistant and may cause your UniFi Protect integration to break or not work as expected.", + "fix_flow": { + "step": { + "confirm": { + "description": "Are you sure you want to run unsupported versions of UniFi Protect? This may cause your Home Assistant integration to break.", + "title": "v{version} is an Early Access version" + }, + "start": { + "description": "You are using v{version} of UniFi Protect which is an Early Access version. [Early Access versions are not supported by Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) and it is recommended to go back to a stable release as soon as possible.\n\nBy submitting this form you have either [downgraded UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) or you agree to run an unsupported version of UniFi Protect.", + "title": "v{version} is an Early Access version" + } + } + }, "title": "UniFi Protect v{version} is an Early Access version" } }, "options": { - "error": { - "invalid_mac_list": "Must be a list of MAC addresses seperated by commas" - }, "step": { "init": { "data": { "all_updates": "Realtime metrics (WARNING: Greatly increases CPU usage)", + "allow_ea": "Allow Early Access versions of Protect (WARNING: Will mark your integration as unsupported)", "disable_rtsp": "Disable the RTSP stream", - "ignored_devices": "Comma separated list of MAC addresses of devices to ignore", "max_media": "Max number of event to load for Media Browser (increases RAM usage)", "override_connection_host": "Override Connection Host" }, diff --git a/homeassistant/components/unifiprotect/utils.py b/homeassistant/components/unifiprotect/utils.py index 808117aac9e..f58bb14eb41 100644 --- a/homeassistant/components/unifiprotect/utils.py +++ b/homeassistant/components/unifiprotect/utils.py @@ -7,6 +7,8 @@ from enum import Enum import socket from typing import Any +from aiohttp import CookieJar +from pyunifiprotect import ProtectApiClient from pyunifiprotect.data import ( Bootstrap, Light, @@ -16,9 +18,23 @@ from pyunifiprotect.data import ( ) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + CONF_HOST, + CONF_PASSWORD, + CONF_PORT, + CONF_USERNAME, + CONF_VERIFY_SSL, +) from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.aiohttp_client import async_create_clientsession -from .const import DOMAIN, ModelType +from .const import ( + CONF_ALL_UPDATES, + CONF_OVERRIDE_CHOST, + DEVICES_FOR_SUBSCRIBE, + DOMAIN, + ModelType, +) def get_nested_attr(obj: Any, attr: str) -> Any: @@ -106,3 +122,24 @@ def async_dispatch_id(entry: ConfigEntry, dispatch: str) -> str: """Generate entry specific dispatch ID.""" return f"{DOMAIN}.{entry.entry_id}.{dispatch}" + + +@callback +def async_create_api_client( + hass: HomeAssistant, entry: ConfigEntry +) -> ProtectApiClient: + """Create ProtectApiClient from config entry.""" + + session = async_create_clientsession(hass, cookie_jar=CookieJar(unsafe=True)) + return ProtectApiClient( + host=entry.data[CONF_HOST], + port=entry.data[CONF_PORT], + username=entry.data[CONF_USERNAME], + password=entry.data[CONF_PASSWORD], + verify_ssl=entry.data[CONF_VERIFY_SSL], + session=session, + subscribed_models=DEVICES_FOR_SUBSCRIBE, + override_connection_host=entry.options.get(CONF_OVERRIDE_CHOST, False), + ignore_stats=not entry.options.get(CONF_ALL_UPDATES, False), + ignore_unadopted=False, + ) diff --git a/tests/components/unifiprotect/conftest.py b/tests/components/unifiprotect/conftest.py index b006dfbd004..77aa9622f9e 100644 --- a/tests/components/unifiprotect/conftest.py +++ b/tests/components/unifiprotect/conftest.py @@ -128,7 +128,7 @@ def mock_entry( """Mock ProtectApiClient for testing.""" with _patch_discovery(no_device=True), patch( - "homeassistant.components.unifiprotect.ProtectApiClient" + "homeassistant.components.unifiprotect.utils.ProtectApiClient" ) as mock_api: ufp_config_entry.add_to_hass(hass) diff --git a/tests/components/unifiprotect/test_config_flow.py b/tests/components/unifiprotect/test_config_flow.py index dc91b0f6dde..1ee1d40515b 100644 --- a/tests/components/unifiprotect/test_config_flow.py +++ b/tests/components/unifiprotect/test_config_flow.py @@ -246,7 +246,7 @@ async def test_form_options(hass: HomeAssistant, ufp_client: ProtectApiClient) - mock_config.add_to_hass(hass) with _patch_discovery(), patch( - "homeassistant.components.unifiprotect.ProtectApiClient" + "homeassistant.components.unifiprotect.utils.ProtectApiClient" ) as mock_api: mock_api.return_value = ufp_client @@ -274,6 +274,7 @@ async def test_form_options(hass: HomeAssistant, ufp_client: ProtectApiClient) - "disable_rtsp": True, "override_connection_host": True, "max_media": 1000, + "allow_ea": False, } await hass.config_entries.async_unload(mock_config.entry_id) diff --git a/tests/components/unifiprotect/test_init.py b/tests/components/unifiprotect/test_init.py index 9392caa30ac..04b4928aaec 100644 --- a/tests/components/unifiprotect/test_init.py +++ b/tests/components/unifiprotect/test_init.py @@ -72,7 +72,9 @@ async def test_setup_multiple( nvr.id ufp.api.get_nvr = AsyncMock(return_value=nvr) - with patch("homeassistant.components.unifiprotect.ProtectApiClient") as mock_api: + with patch( + "homeassistant.components.unifiprotect.utils.ProtectApiClient" + ) as mock_api: mock_config = MockConfigEntry( domain=DOMAIN, data={ @@ -194,7 +196,7 @@ async def test_setup_starts_discovery( ): """Test setting up will start discovery.""" with _patch_discovery(), patch( - "homeassistant.components.unifiprotect.ProtectApiClient" + "homeassistant.components.unifiprotect.utils.ProtectApiClient" ) as mock_api: ufp_config_entry.add_to_hass(hass) mock_api.return_value = ufp_client diff --git a/tests/components/unifiprotect/test_media_source.py b/tests/components/unifiprotect/test_media_source.py index 8200a1323ab..3447cdfc139 100644 --- a/tests/components/unifiprotect/test_media_source.py +++ b/tests/components/unifiprotect/test_media_source.py @@ -222,7 +222,9 @@ async def test_browse_media_root_multiple_consoles( api2.update = AsyncMock(return_value=bootstrap2) api2.async_disconnect_ws = AsyncMock() - with patch("homeassistant.components.unifiprotect.ProtectApiClient") as mock_api: + with patch( + "homeassistant.components.unifiprotect.utils.ProtectApiClient" + ) as mock_api: mock_config = MockConfigEntry( domain=DOMAIN, data={ @@ -285,7 +287,9 @@ async def test_browse_media_root_multiple_consoles_only_one_media( api2.update = AsyncMock(return_value=bootstrap2) api2.async_disconnect_ws = AsyncMock() - with patch("homeassistant.components.unifiprotect.ProtectApiClient") as mock_api: + with patch( + "homeassistant.components.unifiprotect.utils.ProtectApiClient" + ) as mock_api: mock_config = MockConfigEntry( domain=DOMAIN, data={ diff --git a/tests/components/unifiprotect/test_repairs.py b/tests/components/unifiprotect/test_repairs.py new file mode 100644 index 00000000000..f6f677a1976 --- /dev/null +++ b/tests/components/unifiprotect/test_repairs.py @@ -0,0 +1,120 @@ +"""Test repairs for unifiprotect.""" + +from __future__ import annotations + +from copy import copy +from http import HTTPStatus +from unittest.mock import Mock + +from pyunifiprotect.data import Version + +from homeassistant.components.repairs.issue_handler import ( + async_process_repairs_platforms, +) +from homeassistant.components.repairs.websocket_api import ( + RepairsFlowIndexView, + RepairsFlowResourceView, +) +from homeassistant.components.unifiprotect.const import DOMAIN +from homeassistant.core import HomeAssistant + +from .utils import MockUFPFixture, init_entry + + +async def test_ea_warning_ignore( + hass: HomeAssistant, + ufp: MockUFPFixture, + hass_client, + hass_ws_client, +): + """Test EA warning is created if using prerelease version of Protect.""" + + version = ufp.api.bootstrap.nvr.version + assert version.is_prerelease + await init_entry(hass, ufp, []) + await async_process_repairs_platforms(hass) + ws_client = await hass_ws_client(hass) + client = await hass_client() + + await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + assert len(msg["result"]["issues"]) == 1 + issue = msg["result"]["issues"][0] + assert issue["issue_id"] == "ea_warning" + + url = RepairsFlowIndexView.url + resp = await client.post(url, json={"handler": DOMAIN, "issue_id": "ea_warning"}) + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data["description_placeholders"] == {"version": str(version)} + assert data["step_id"] == "start" + + url = RepairsFlowResourceView.url.format(flow_id=flow_id) + resp = await client.post(url) + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data["description_placeholders"] == {"version": str(version)} + assert data["step_id"] == "confirm" + + url = RepairsFlowResourceView.url.format(flow_id=flow_id) + resp = await client.post(url) + assert resp.status == HTTPStatus.OK + data = await resp.json() + + assert data["type"] == "create_entry" + + +async def test_ea_warning_fix( + hass: HomeAssistant, + ufp: MockUFPFixture, + hass_client, + hass_ws_client, +): + """Test EA warning is created if using prerelease version of Protect.""" + + version = ufp.api.bootstrap.nvr.version + assert version.is_prerelease + await init_entry(hass, ufp, []) + await async_process_repairs_platforms(hass) + ws_client = await hass_ws_client(hass) + client = await hass_client() + + await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + assert len(msg["result"]["issues"]) == 1 + issue = msg["result"]["issues"][0] + assert issue["issue_id"] == "ea_warning" + + url = RepairsFlowIndexView.url + resp = await client.post(url, json={"handler": DOMAIN, "issue_id": "ea_warning"}) + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data["description_placeholders"] == {"version": str(version)} + assert data["step_id"] == "start" + + new_nvr = copy(ufp.api.bootstrap.nvr) + new_nvr.version = Version("2.2.6") + mock_msg = Mock() + mock_msg.changed_data = {"version": "2.2.6"} + mock_msg.new_obj = new_nvr + + ufp.api.bootstrap.nvr = new_nvr + ufp.ws_msg(mock_msg) + await hass.async_block_till_done() + + url = RepairsFlowResourceView.url.format(flow_id=flow_id) + resp = await client.post(url) + assert resp.status == HTTPStatus.OK + data = await resp.json() + + assert data["type"] == "create_entry" From f25ec15b07b5faaa98dc6523f58ddde526012060 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sun, 13 Nov 2022 16:40:11 +0100 Subject: [PATCH 0387/1033] Add element sensors to Sensibo (#82025) fixes undefined --- homeassistant/components/sensibo/sensor.py | 51 +++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sensibo/sensor.py b/homeassistant/components/sensibo/sensor.py index 22981ada51f..ad5286a02c2 100644 --- a/homeassistant/components/sensibo/sensor.py +++ b/homeassistant/components/sensibo/sensor.py @@ -204,7 +204,56 @@ AIRQ_SENSOR_TYPES: tuple[SensiboDeviceSensorEntityDescription, ...] = ( ), ) -DESCRIPTION_BY_MODELS = {"pure": PURE_SENSOR_TYPES, "airq": AIRQ_SENSOR_TYPES} +ELEMENT_SENSOR_TYPES: tuple[SensiboDeviceSensorEntityDescription, ...] = ( + SensiboDeviceSensorEntityDescription( + key="pm25", + device_class=SensorDeviceClass.PM25, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + name="PM 2.5", + value_fn=lambda data: data.pm25, + extra_fn=None, + ), + SensiboDeviceSensorEntityDescription( + key="tvoc", + native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION, + state_class=SensorStateClass.MEASUREMENT, + name="TVOC", + value_fn=lambda data: data.tvoc, + extra_fn=None, + ), + SensiboDeviceSensorEntityDescription( + key="co2", + device_class=SensorDeviceClass.CO2, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, + state_class=SensorStateClass.MEASUREMENT, + name="CO2", + value_fn=lambda data: data.co2, + extra_fn=None, + ), + SensiboDeviceSensorEntityDescription( + key="ethanol", + native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, + state_class=SensorStateClass.MEASUREMENT, + name="Ethanol", + value_fn=lambda data: data.etoh, + extra_fn=None, + ), + SensiboDeviceSensorEntityDescription( + key="iaq", + device_class=SensorDeviceClass.AQI, + state_class=SensorStateClass.MEASUREMENT, + name="Air quality", + value_fn=lambda data: data.iaq, + extra_fn=None, + ), +) + +DESCRIPTION_BY_MODELS = { + "pure": PURE_SENSOR_TYPES, + "airq": AIRQ_SENSOR_TYPES, + "elements": ELEMENT_SENSOR_TYPES, +} async def async_setup_entry( From 2ff45e198d14242802717fa4382132a2d458272d Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 13 Nov 2022 08:56:38 -0800 Subject: [PATCH 0388/1033] Bump pyrainbird 0.6.2 (#82040) --- homeassistant/components/rainbird/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/rainbird/manifest.json b/homeassistant/components/rainbird/manifest.json index 7023283e66b..2a49dc60843 100644 --- a/homeassistant/components/rainbird/manifest.json +++ b/homeassistant/components/rainbird/manifest.json @@ -2,7 +2,7 @@ "domain": "rainbird", "name": "Rain Bird", "documentation": "https://www.home-assistant.io/integrations/rainbird", - "requirements": ["pyrainbird==0.6.1"], + "requirements": ["pyrainbird==0.6.2"], "codeowners": ["@konikvranik"], "iot_class": "local_polling", "loggers": ["pyrainbird"] diff --git a/requirements_all.txt b/requirements_all.txt index 23251e23e47..49b4500c483 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1853,7 +1853,7 @@ pyqwikswitch==0.93 pyrail==0.0.3 # homeassistant.components.rainbird -pyrainbird==0.6.1 +pyrainbird==0.6.2 # homeassistant.components.recswitch pyrecswitch==1.0.2 From fe7ffe9519406b3490e9bccba2a56a634a5927d0 Mon Sep 17 00:00:00 2001 From: Avi Miller Date: Mon, 14 Nov 2022 03:59:47 +1100 Subject: [PATCH 0389/1033] Enable more customization of the LIFX pulse and color loop effects (#81699) --- homeassistant/components/lifx/manager.py | 41 +++++++++++++------ homeassistant/components/lifx/services.yaml | 44 ++++++++++++++++++--- tests/components/lifx/test_light.py | 17 +++++++- 3 files changed, 85 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/lifx/manager.py b/homeassistant/components/lifx/manager.py index f91ed761e44..1eb80c0c5a2 100644 --- a/homeassistant/components/lifx/manager.py +++ b/homeassistant/components/lifx/manager.py @@ -42,15 +42,17 @@ SERVICE_EFFECT_MOVE = "effect_move" SERVICE_EFFECT_PULSE = "effect_pulse" SERVICE_EFFECT_STOP = "effect_stop" +ATTR_CHANGE = "change" +ATTR_CYCLES = "cycles" +ATTR_DIRECTION = "direction" +ATTR_PALETTE = "palette" +ATTR_PERIOD = "period" ATTR_POWER_OFF = "power_off" ATTR_POWER_ON = "power_on" -ATTR_PERIOD = "period" -ATTR_CYCLES = "cycles" -ATTR_SPREAD = "spread" -ATTR_CHANGE = "change" -ATTR_DIRECTION = "direction" +ATTR_SATURATION_MAX = "saturation_max" +ATTR_SATURATION_MIN = "saturation_min" ATTR_SPEED = "speed" -ATTR_PALETTE = "palette" +ATTR_SPREAD = "spread" EFFECT_FLAME = "FLAME" EFFECT_MORPH = "MORPH" @@ -72,8 +74,8 @@ EFFECT_MOVE_DIRECTIONS = [EFFECT_MOVE_DIRECTION_LEFT, EFFECT_MOVE_DIRECTION_RIGH PULSE_MODE_BLINK = "blink" PULSE_MODE_BREATHE = "breathe" PULSE_MODE_PING = "ping" -PULSE_MODE_STROBE = "strobe" PULSE_MODE_SOLID = "solid" +PULSE_MODE_STROBE = "strobe" PULSE_MODES = [ PULSE_MODE_BLINK, @@ -90,8 +92,8 @@ LIFX_EFFECT_SCHEMA = { LIFX_EFFECT_PULSE_SCHEMA = cv.make_entity_service_schema( { **LIFX_EFFECT_SCHEMA, - ATTR_BRIGHTNESS: VALID_BRIGHTNESS, - ATTR_BRIGHTNESS_PCT: VALID_BRIGHTNESS_PCT, + vol.Exclusive(ATTR_BRIGHTNESS, ATTR_BRIGHTNESS): VALID_BRIGHTNESS, + vol.Exclusive(ATTR_BRIGHTNESS_PCT, ATTR_BRIGHTNESS): VALID_BRIGHTNESS_PCT, vol.Exclusive(ATTR_COLOR_NAME, COLOR_GROUP): cv.string, vol.Exclusive(ATTR_RGB_COLOR, COLOR_GROUP): vol.All( vol.Coerce(tuple), vol.ExactSequence((cv.byte, cv.byte, cv.byte)) @@ -121,8 +123,10 @@ LIFX_EFFECT_PULSE_SCHEMA = cv.make_entity_service_schema( LIFX_EFFECT_COLORLOOP_SCHEMA = cv.make_entity_service_schema( { **LIFX_EFFECT_SCHEMA, - ATTR_BRIGHTNESS: VALID_BRIGHTNESS, - ATTR_BRIGHTNESS_PCT: VALID_BRIGHTNESS_PCT, + vol.Exclusive(ATTR_BRIGHTNESS, ATTR_BRIGHTNESS): VALID_BRIGHTNESS, + vol.Exclusive(ATTR_BRIGHTNESS_PCT, ATTR_BRIGHTNESS): VALID_BRIGHTNESS_PCT, + ATTR_SATURATION_MAX: vol.All(vol.Coerce(int), vol.Clamp(min=0, max=100)), + ATTR_SATURATION_MIN: vol.All(vol.Coerce(int), vol.Clamp(min=0, max=100)), ATTR_PERIOD: vol.All(vol.Coerce(float), vol.Clamp(min=0.05)), ATTR_CHANGE: vol.All(vol.Coerce(float), vol.Clamp(min=0, max=360)), ATTR_SPREAD: vol.All(vol.Coerce(float), vol.Clamp(min=0, max=360)), @@ -345,8 +349,21 @@ class LIFXManager: elif service == SERVICE_EFFECT_COLORLOOP: brightness = None + saturation_max = None + saturation_min = None + if ATTR_BRIGHTNESS in kwargs: brightness = convert_8_to_16(kwargs[ATTR_BRIGHTNESS]) + elif ATTR_BRIGHTNESS_PCT in kwargs: + brightness = convert_8_to_16( + round(255 * kwargs[ATTR_BRIGHTNESS_PCT] / 100) + ) + + if ATTR_SATURATION_MAX in kwargs: + saturation_max = int(kwargs[ATTR_SATURATION_MAX] / 100 * 65535) + + if ATTR_SATURATION_MIN in kwargs: + saturation_min = int(kwargs[ATTR_SATURATION_MIN] / 100 * 65535) effect = aiolifx_effects.EffectColorloop( power_on=kwargs.get(ATTR_POWER_ON), @@ -355,6 +372,8 @@ class LIFXManager: spread=kwargs.get(ATTR_SPREAD), transition=kwargs.get(ATTR_TRANSITION), brightness=brightness, + saturation_max=saturation_max, + saturation_min=saturation_min, ) await self.effects_conductor.start(effect, bulbs) diff --git a/homeassistant/components/lifx/services.yaml b/homeassistant/components/lifx/services.yaml index 976d4ff5623..6613bb6a329 100644 --- a/homeassistant/components/lifx/services.yaml +++ b/homeassistant/components/lifx/services.yaml @@ -79,12 +79,20 @@ effect_pulse: - "strobe" - "solid" brightness: - name: Brightness - description: Number indicating brightness of the temporary color. + name: Brightness value + description: Number indicating brightness of the temporary color, where 1 is the minimum brightness and 255 is the maximum brightness supported by the light. selector: number: - min: 0 + min: 1 max: 255 + brightness_pct: + name: Brightness + description: Percentage indicating the brightness of the temporary color, where 1 is the minimum brightness and 100 is the maximum brightness supported by the light. + selector: + number: + min: 1 + max: 100 + unit_of_measurement: "%" color_name: name: Color name description: A human readable color name. @@ -131,12 +139,38 @@ effect_colorloop: domain: light fields: brightness: - name: Brightness - description: Number indicating brightness of the effect. Leave this out to maintain the current brightness of each participating light. + name: Brightness value + description: Number indicating brightness of the color loop, where 1 is the minimum brightness and 255 is the maximum brightness supported by the light. selector: number: min: 0 max: 255 + brightness_pct: + name: Brightness + description: Percentage indicating the brightness of the color loop, where 1 is the minimum brightness and 100 is the maximum brightness supported by the light. + selector: + number: + min: 0 + max: 100 + unit_of_measurement: "%" + saturation_min: + name: Minimum saturation + description: Percentage indicating the minimum saturation of the colors in the loop. + default: 80 + selector: + number: + min: 1 + max: 100 + unit_of_measurement: "%" + saturation_max: + name: Maximum saturation + description: Percentage indicating the maximum saturation of the colors in the loop. + default: 100 + selector: + number: + min: 1 + max: 100 + unit_of_measurement: "%" period: name: Period description: Duration between color changes. diff --git a/tests/components/lifx/test_light.py b/tests/components/lifx/test_light.py index 5a9b250034a..866095709ce 100644 --- a/tests/components/lifx/test_light.py +++ b/tests/components/lifx/test_light.py @@ -13,6 +13,8 @@ from homeassistant.components.lifx.light import ATTR_INFRARED, ATTR_ZONES from homeassistant.components.lifx.manager import ( ATTR_DIRECTION, ATTR_PALETTE, + ATTR_SATURATION_MAX, + ATTR_SATURATION_MIN, ATTR_SPEED, ATTR_THEME, SERVICE_EFFECT_COLORLOOP, @@ -1009,7 +1011,20 @@ async def test_color_light_with_temp( await hass.services.async_call( DOMAIN, SERVICE_EFFECT_COLORLOOP, - {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 128}, + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS_PCT: 50, ATTR_SATURATION_MAX: 90}, + blocking=True, + ) + start_call = mock_effect_conductor.start.mock_calls + first_call = start_call[0][1] + assert isinstance(first_call[0], aiolifx_effects.EffectColorloop) + assert first_call[1][0] == bulb + mock_effect_conductor.start.reset_mock() + mock_effect_conductor.stop.reset_mock() + + await hass.services.async_call( + DOMAIN, + SERVICE_EFFECT_COLORLOOP, + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 128, ATTR_SATURATION_MIN: 90}, blocking=True, ) start_call = mock_effect_conductor.start.mock_calls From 52a8f4df82d2a5fb0a0333c15d3a7b08346394df Mon Sep 17 00:00:00 2001 From: On Freund Date: Sun, 13 Nov 2022 19:53:54 +0200 Subject: [PATCH 0390/1033] Refactor binary sensor creation in Risco integration (#82043) --- homeassistant/components/risco/binary_sensor.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/risco/binary_sensor.py b/homeassistant/components/risco/binary_sensor.py index 7c9733e2ab9..423137d88b6 100644 --- a/homeassistant/components/risco/binary_sensor.py +++ b/homeassistant/components/risco/binary_sensor.py @@ -28,16 +28,13 @@ async def async_setup_entry( if is_local(config_entry): local_data: LocalData = hass.data[DOMAIN][config_entry.entry_id] async_add_entities( - RiscoLocalBinarySensor(local_data.system.id, zone_id, zone) - for zone_id, zone in local_data.system.zones.items() - ) - async_add_entities( - RiscoLocalAlarmedBinarySensor(local_data.system.id, zone_id, zone) - for zone_id, zone in local_data.system.zones.items() - ) - async_add_entities( - RiscoLocalArmedBinarySensor(local_data.system.id, zone_id, zone) + entity for zone_id, zone in local_data.system.zones.items() + for entity in ( + RiscoLocalBinarySensor(local_data.system.id, zone_id, zone), + RiscoLocalAlarmedBinarySensor(local_data.system.id, zone_id, zone), + RiscoLocalArmedBinarySensor(local_data.system.id, zone_id, zone), + ) ) else: coordinator: RiscoDataUpdateCoordinator = hass.data[DOMAIN][ From 50f960097f9077c503f5daed47233b667e0108fc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Nov 2022 13:30:50 -0600 Subject: [PATCH 0391/1033] Add support for thermobeacon WS 03 aka device 0x18 (#82045) * Add support for thermobeacon WS 03 aka device 0x18 changelog: https://github.com/Bluetooth-Devices/thermobeacon-ble/compare/v0.3.2...v0.4.0 * empty --- homeassistant/components/thermobeacon/manifest.json | 8 +++++++- homeassistant/generated/bluetooth.py | 9 +++++++++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/thermobeacon/manifest.json b/homeassistant/components/thermobeacon/manifest.json index 34321a66681..3e105eff138 100644 --- a/homeassistant/components/thermobeacon/manifest.json +++ b/homeassistant/components/thermobeacon/manifest.json @@ -22,9 +22,15 @@ "manufacturer_data_start": [0], "connectable": false }, + { + "service_uuid": "0000fff0-0000-1000-8000-00805f9b34fb", + "manufacturer_id": 24, + "manufacturer_data_start": [0], + "connectable": false + }, { "local_name": "ThermoBeacon", "connectable": false } ], - "requirements": ["thermobeacon-ble==0.3.2"], + "requirements": ["thermobeacon-ble==0.4.0"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/homeassistant/generated/bluetooth.py b/homeassistant/generated/bluetooth.py index 7fa3f363093..06ebbfbb515 100644 --- a/homeassistant/generated/bluetooth.py +++ b/homeassistant/generated/bluetooth.py @@ -349,6 +349,15 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ "manufacturer_id": 21, "service_uuid": "0000fff0-0000-1000-8000-00805f9b34fb", }, + { + "connectable": False, + "domain": "thermobeacon", + "manufacturer_data_start": [ + 0, + ], + "manufacturer_id": 24, + "service_uuid": "0000fff0-0000-1000-8000-00805f9b34fb", + }, { "connectable": False, "domain": "thermobeacon", diff --git a/requirements_all.txt b/requirements_all.txt index 49b4500c483..4719d3282fd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2417,7 +2417,7 @@ tesla-wall-connector==1.0.2 # tf-models-official==2.5.0 # homeassistant.components.thermobeacon -thermobeacon-ble==0.3.2 +thermobeacon-ble==0.4.0 # homeassistant.components.thermopro thermopro-ble==0.4.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 90b0509be1b..3e4222cec38 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1669,7 +1669,7 @@ tesla-powerwall==0.3.18 tesla-wall-connector==1.0.2 # homeassistant.components.thermobeacon -thermobeacon-ble==0.3.2 +thermobeacon-ble==0.4.0 # homeassistant.components.thermopro thermopro-ble==0.4.3 From bd28483b437e381abbbf013d1fa66f8f7c48f043 Mon Sep 17 00:00:00 2001 From: Oliver Dippel Date: Sun, 13 Nov 2022 21:17:59 +0100 Subject: [PATCH 0392/1033] Fix ibeacon source attribute not being updated (#81740) fixes undefined --- .../components/ibeacon/coordinator.py | 6 +- tests/components/ibeacon/test_coordinator.py | 69 ++++++++++++++++++- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/ibeacon/coordinator.py b/homeassistant/components/ibeacon/coordinator.py index 33b33c56ed0..ed62649de2d 100644 --- a/homeassistant/components/ibeacon/coordinator.py +++ b/homeassistant/components/ibeacon/coordinator.py @@ -396,7 +396,11 @@ class IBeaconCoordinator: ) continue - if service_info.rssi != ibeacon_advertisement.rssi: + if ( + service_info.rssi != ibeacon_advertisement.rssi + or service_info.source != ibeacon_advertisement.source + ): + ibeacon_advertisement.source = service_info.source ibeacon_advertisement.update_rssi(service_info.rssi) async_dispatcher_send( self.hass, diff --git a/tests/components/ibeacon/test_coordinator.py b/tests/components/ibeacon/test_coordinator.py index 25ce7154a37..6acbf5569f8 100644 --- a/tests/components/ibeacon/test_coordinator.py +++ b/tests/components/ibeacon/test_coordinator.py @@ -3,16 +3,19 @@ from dataclasses import replace from datetime import timedelta +import time +from bleak.backends.scanner import BLEDevice import pytest -from homeassistant.components.ibeacon.const import DOMAIN, UPDATE_INTERVAL +from homeassistant.components.ibeacon.const import ATTR_SOURCE, DOMAIN, UPDATE_INTERVAL from homeassistant.const import STATE_HOME from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo from homeassistant.util import dt as dt_util from . import ( BLUECHARM_BEACON_SERVICE_INFO, + BLUECHARM_BEACON_SERVICE_INFO_2, BLUECHARM_BEACON_SERVICE_INFO_DBUS, TESLA_TRANSIENT, TESLA_TRANSIENT_BLE_DEVICE, @@ -20,6 +23,8 @@ from . import ( from tests.common import MockConfigEntry, async_fire_time_changed from tests.components.bluetooth import ( + generate_advertisement_data, + inject_advertisement_with_time_and_source_connectable, inject_bluetooth_service_info, patch_all_discovered_devices, ) @@ -252,3 +257,65 @@ async def test_ignore_transient_devices_unless_we_see_them_a_few_times(hass): await hass.async_block_till_done() assert hass.states.get("device_tracker.s6da7c9389bd5452cc_cccc").state == STATE_HOME + + +async def test_changing_source_attribute(hass): + """Test update of the source attribute.""" + entry = MockConfigEntry( + domain=DOMAIN, + ) + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + now = time.monotonic() + info = BLUECHARM_BEACON_SERVICE_INFO_2 + device = BLEDevice( + address=info.address, + name=info.name, + details={}, + ) + advertisement_data = generate_advertisement_data( + local_name=info.name, + manufacturer_data=info.manufacturer_data, + service_data=info.service_data, + service_uuids=info.service_uuids, + rssi=info.rssi, + ) + + inject_advertisement_with_time_and_source_connectable( + hass, + device, + advertisement_data, + now, + "local", + True, + ) + await hass.async_block_till_done() + + attributes = hass.states.get( + "sensor.bluecharm_177999_8105_estimated_distance" + ).attributes + assert attributes[ATTR_SOURCE] == "local" + + inject_advertisement_with_time_and_source_connectable( + hass, + device, + advertisement_data, + now, + "proxy", + True, + ) + await hass.async_block_till_done() + with patch_all_discovered_devices([BLUECHARM_BEACON_SERVICE_INFO_2]): + async_fire_time_changed( + hass, + dt_util.utcnow() + timedelta(seconds=UPDATE_INTERVAL.total_seconds() * 2), + ) + await hass.async_block_till_done() + + attributes = hass.states.get( + "sensor.bluecharm_177999_8105_estimated_distance" + ).attributes + assert attributes[ATTR_SOURCE] == "proxy" From f584efa0c24df19ef1f805ecf95a95cecec5ff99 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Nov 2022 14:18:36 -0600 Subject: [PATCH 0393/1033] Move bluetooth remote scanner implementation into a base class (#82012) --- .../components/bluetooth/__init__.py | 2 + homeassistant/components/bluetooth/models.py | 153 +++++++++++ .../components/esphome/bluetooth/scanner.py | 140 +--------- tests/components/bluetooth/test_models.py | 255 ++++++++++++++++++ 4 files changed, 421 insertions(+), 129 deletions(-) diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index 23165e2bc09..e3f62280661 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -50,6 +50,7 @@ from .const import ( from .manager import BluetoothManager from .match import BluetoothCallbackMatcher, IntegrationMatcher from .models import ( + BaseHaRemoteScanner, BaseHaScanner, BluetoothCallback, BluetoothChange, @@ -80,6 +81,7 @@ __all__ = [ "async_track_unavailable", "async_scanner_count", "BaseHaScanner", + "BaseHaRemoteScanner", "BluetoothServiceInfo", "BluetoothServiceInfoBleak", "BluetoothScanningMode", diff --git a/homeassistant/components/bluetooth/models.py b/homeassistant/components/bluetooth/models.py index a63a704baf6..4e386ea3746 100644 --- a/homeassistant/components/bluetooth/models.py +++ b/homeassistant/components/bluetooth/models.py @@ -6,6 +6,8 @@ import asyncio from collections.abc import Callable import contextlib from dataclasses import dataclass +import datetime +from datetime import timedelta from enum import Enum import logging from typing import TYPE_CHECKING, Any, Final @@ -21,8 +23,21 @@ from bleak.backends.scanner import ( from bleak_retry_connector import NO_RSSI_VALUE, freshen_ble_device from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback +from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.frame import report from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo +from homeassistant.util.dt import monotonic_time_coarse + +from .const import FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + +# The maximum time between advertisements for a device to be considered +# stale when the advertisement tracker can determine the interval for +# connectable devices. +# +# BlueZ uses 180 seconds by default but we give it a bit more time +# to account for the esp32's bluetooth stack being a bit slower +# than BlueZ's. +CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS: Final = 195 if TYPE_CHECKING: @@ -35,6 +50,8 @@ FILTER_UUIDS: Final = "UUIDs" MANAGER: BluetoothManager | None = None +MONOTONIC_TIME: Final = monotonic_time_coarse + @dataclass class BluetoothServiceInfoBleak(BluetoothServiceInfo): @@ -134,6 +151,142 @@ class BaseHaScanner: } +class BaseHaRemoteScanner(BaseHaScanner): + """Base class for a Home Assistant remote BLE scanner.""" + + def __init__( + self, + hass: HomeAssistant, + scanner_id: str, + new_info_callback: Callable[[BluetoothServiceInfoBleak], None], + connector: HaBluetoothConnector, + connectable: bool, + ) -> None: + """Initialize the scanner.""" + super().__init__(hass, scanner_id) + self._new_info_callback = new_info_callback + self._discovered_device_advertisement_datas: dict[ + str, tuple[BLEDevice, AdvertisementData] + ] = {} + self._discovered_device_timestamps: dict[str, float] = {} + self._connector = connector + self._connectable = connectable + self._details: dict[str, str | HaBluetoothConnector] = {"source": scanner_id} + self._expire_seconds = FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + if connectable: + self._details["connector"] = connector + self._expire_seconds = ( + CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + ) + + @hass_callback + def async_setup(self) -> CALLBACK_TYPE: + """Set up the scanner.""" + return async_track_time_interval( + self.hass, self._async_expire_devices, timedelta(seconds=30) + ) + + def _async_expire_devices(self, _datetime: datetime.datetime) -> None: + """Expire old devices.""" + now = MONOTONIC_TIME() + expired = [ + address + for address, timestamp in self._discovered_device_timestamps.items() + if now - timestamp > self._expire_seconds + ] + for address in expired: + del self._discovered_device_advertisement_datas[address] + del self._discovered_device_timestamps[address] + + @property + def discovered_devices(self) -> list[BLEDevice]: + """Return a list of discovered devices.""" + return [ + device_advertisement_data[0] + for device_advertisement_data in self._discovered_device_advertisement_datas.values() + ] + + @property + def discovered_devices_and_advertisement_data( + self, + ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: + """Return a list of discovered devices and advertisement data.""" + return self._discovered_device_advertisement_datas + + @hass_callback + def _async_on_advertisement( + self, + address: str, + rssi: int, + local_name: str | None, + service_uuids: list[str], + service_data: dict[str, bytes], + manufacturer_data: dict[int, bytes], + tx_power: int | None, + ) -> None: + """Call the registered callback.""" + now = MONOTONIC_TIME() + if prev_discovery := self._discovered_device_advertisement_datas.get(address): + # Merge the new data with the old data + # to function the same as BlueZ which + # merges the dicts on PropertiesChanged + prev_device = prev_discovery[0] + prev_advertisement = prev_discovery[1] + if ( + local_name + and prev_device.name + and len(prev_device.name) > len(local_name) + ): + local_name = prev_device.name + if prev_advertisement.service_uuids: + service_uuids = list( + set(service_uuids + prev_advertisement.service_uuids) + ) + if prev_advertisement.service_data: + service_data = {**prev_advertisement.service_data, **service_data} + if prev_advertisement.manufacturer_data: + manufacturer_data = { + **prev_advertisement.manufacturer_data, + **manufacturer_data, + } + + advertisement_data = AdvertisementData( + local_name=None if local_name == "" else local_name, + manufacturer_data=manufacturer_data, + service_data=service_data, + service_uuids=service_uuids, + rssi=rssi, + tx_power=NO_RSSI_VALUE if tx_power is None else tx_power, + platform_data=(), + ) + device = BLEDevice( # type: ignore[no-untyped-call] + address=address, + name=local_name, + details=self._details, + rssi=rssi, # deprecated, will be removed in newer bleak + ) + self._discovered_device_advertisement_datas[address] = ( + device, + advertisement_data, + ) + self._discovered_device_timestamps[address] = now + self._new_info_callback( + BluetoothServiceInfoBleak( + name=advertisement_data.local_name or device.name or device.address, + address=device.address, + rssi=rssi, + manufacturer_data=advertisement_data.manufacturer_data, + service_data=advertisement_data.service_data, + service_uuids=advertisement_data.service_uuids, + source=self.source, + device=device, + advertisement=advertisement_data, + connectable=self._connectable, + time=now, + ) + ) + + class HaBleakScannerWrapper(BaseBleakScanner): """A wrapper that uses the single instance.""" diff --git a/homeassistant/components/esphome/bluetooth/scanner.py b/homeassistant/components/esphome/bluetooth/scanner.py index 4fbaf7cabb6..ab7572fd4ac 100644 --- a/homeassistant/components/esphome/bluetooth/scanner.py +++ b/homeassistant/components/esphome/bluetooth/scanner.py @@ -1,147 +1,29 @@ """Bluetooth scanner for esphome.""" from __future__ import annotations -from collections.abc import Callable -import datetime -from datetime import timedelta import re -import time -from typing import Final from aioesphomeapi import BluetoothLEAdvertisement -from bleak.backends.device import BLEDevice -from bleak.backends.scanner import AdvertisementData -from homeassistant.components.bluetooth import ( - FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, - BaseHaScanner, - BluetoothServiceInfoBleak, - HaBluetoothConnector, -) -from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback -from homeassistant.helpers.event import async_track_time_interval -from homeassistant.util.dt import monotonic_time_coarse +from homeassistant.components.bluetooth import BaseHaRemoteScanner +from homeassistant.core import callback TWO_CHAR = re.compile("..") -# The maximum time between advertisements for a device to be considered -# stale when the advertisement tracker can determine the interval for -# connectable devices. -# -# BlueZ uses 180 seconds by default but we give it a bit more time -# to account for the esp32's bluetooth stack being a bit slower -# than BlueZ's. -CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS: Final = 195 - -class ESPHomeScanner(BaseHaScanner): +class ESPHomeScanner(BaseHaRemoteScanner): """Scanner for esphome.""" - def __init__( - self, - hass: HomeAssistant, - scanner_id: str, - new_info_callback: Callable[[BluetoothServiceInfoBleak], None], - connector: HaBluetoothConnector, - connectable: bool, - ) -> None: - """Initialize the scanner.""" - super().__init__(hass, scanner_id) - self._new_info_callback = new_info_callback - self._discovered_device_advertisement_datas: dict[ - str, tuple[BLEDevice, AdvertisementData] - ] = {} - self._discovered_device_timestamps: dict[str, float] = {} - self._connector = connector - self._connectable = connectable - self._details: dict[str, str | HaBluetoothConnector] = {"source": scanner_id} - self._fallback_seconds = FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS - if connectable: - self._details["connector"] = connector - self._fallback_seconds = ( - CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS - ) - - @callback - def async_setup(self) -> CALLBACK_TYPE: - """Set up the scanner.""" - return async_track_time_interval( - self.hass, self._async_expire_devices, timedelta(seconds=30) - ) - - def _async_expire_devices(self, _datetime: datetime.datetime) -> None: - """Expire old devices.""" - now = time.monotonic() - expired = [ - address - for address, timestamp in self._discovered_device_timestamps.items() - if now - timestamp > self._fallback_seconds - ] - for address in expired: - del self._discovered_device_advertisement_datas[address] - del self._discovered_device_timestamps[address] - - @property - def discovered_devices(self) -> list[BLEDevice]: - """Return a list of discovered devices.""" - return [ - device_advertisement_data[0] - for device_advertisement_data in self._discovered_device_advertisement_datas.values() - ] - - @property - def discovered_devices_and_advertisement_data( - self, - ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: - """Return a list of discovered devices and advertisement data.""" - return self._discovered_device_advertisement_datas - @callback def async_on_advertisement(self, adv: BluetoothLEAdvertisement) -> None: """Call the registered callback.""" - now = monotonic_time_coarse() address = ":".join(TWO_CHAR.findall("%012X" % adv.address)) # must be upper - name = adv.name - if prev_discovery := self._discovered_device_advertisement_datas.get(address): - # If the last discovery had the full local name - # and this one doesn't, keep the old one as we - # always want the full local name over the short one - prev_device = prev_discovery[0] - if len(prev_device.name) > len(adv.name): - name = prev_device.name - - advertisement_data = AdvertisementData( - local_name=None if name == "" else name, - manufacturer_data=adv.manufacturer_data, - service_data=adv.service_data, - service_uuids=adv.service_uuids, - rssi=adv.rssi, - tx_power=-127, - platform_data=(), - ) - device = BLEDevice( # type: ignore[no-untyped-call] - address=address, - name=name, - details=self._details, - rssi=adv.rssi, # deprecated, will be removed in newer bleak - ) - self._discovered_device_advertisement_datas[address] = ( - device, - advertisement_data, - ) - self._discovered_device_timestamps[address] = now - self._new_info_callback( - BluetoothServiceInfoBleak( - name=advertisement_data.local_name or device.name or device.address, - address=device.address, - rssi=adv.rssi, - manufacturer_data=advertisement_data.manufacturer_data, - service_data=advertisement_data.service_data, - service_uuids=advertisement_data.service_uuids, - source=self.source, - device=device, - advertisement=advertisement_data, - connectable=self._connectable, - time=now, - ) + self._async_on_advertisement( + address, + adv.rssi, + adv.name, + adv.service_uuids, + adv.service_data, + adv.manufacturer_data, + None, ) diff --git a/tests/components/bluetooth/test_models.py b/tests/components/bluetooth/test_models.py index adb953b2af2..520c8d6d2f7 100644 --- a/tests/components/bluetooth/test_models.py +++ b/tests/components/bluetooth/test_models.py @@ -1,6 +1,8 @@ """Tests for the Bluetooth integration models.""" from __future__ import annotations +from datetime import timedelta +import time from unittest.mock import patch import bleak @@ -10,11 +12,15 @@ from bleak.backends.scanner import AdvertisementData import pytest from homeassistant.components.bluetooth.models import ( + CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, + FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, + BaseHaRemoteScanner, BaseHaScanner, HaBleakClientWrapper, HaBleakScannerWrapper, HaBluetoothConnector, ) +import homeassistant.util.dt as dt_util from . import ( _get_manager, @@ -23,6 +29,8 @@ from . import ( inject_advertisement_with_source, ) +from tests.common import async_fire_time_changed + class MockBleakClient(BleakClient): """Mock bleak client.""" @@ -345,3 +353,250 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab client.set_disconnected_callback(lambda client: None) await client.disconnect() cancel() + + +async def test_remote_scanner(hass): + """Test the remote scanner base class merges advertisement_data.""" + manager = _get_manager() + + switchbot_device = BLEDevice( + "44:44:33:11:23:45", + "wohand", + {}, + rssi=-100, + ) + switchbot_device_adv = generate_advertisement_data( + local_name="wohand", + service_uuids=["050a021a-0000-1000-8000-00805f9b34fb"], + service_data={"050a021a-0000-1000-8000-00805f9b34fb": b"\n\xff"}, + manufacturer_data={1: b"\x01"}, + rssi=-100, + ) + switchbot_device_2 = BLEDevice( + "44:44:33:11:23:45", + "w", + {}, + rssi=-100, + ) + switchbot_device_adv_2 = generate_advertisement_data( + local_name="wohand", + service_uuids=["00000001-0000-1000-8000-00805f9b34fb"], + service_data={"00000001-0000-1000-8000-00805f9b34fb": b"\n\xff"}, + manufacturer_data={1: b"\x01", 2: b"\x02"}, + rssi=-100, + ) + + class FakeScanner(BaseHaRemoteScanner): + def inject_advertisement( + self, device: BLEDevice, advertisement_data: AdvertisementData + ) -> None: + """Inject an advertisement.""" + self._async_on_advertisement( + device.address, + advertisement_data.rssi, + device.name, + advertisement_data.service_uuids, + advertisement_data.service_data, + advertisement_data.manufacturer_data, + advertisement_data.tx_power, + ) + + new_info_callback = manager.scanner_adv_received + connector = ( + HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), + ) + scanner = FakeScanner(hass, "esp32", new_info_callback, connector, True) + scanner.async_setup() + cancel = manager.async_register_scanner(scanner, True) + + scanner.inject_advertisement(switchbot_device, switchbot_device_adv) + + data = scanner.discovered_devices_and_advertisement_data + discovered_device, discovered_adv_data = data[switchbot_device.address] + assert discovered_device.address == switchbot_device.address + assert discovered_device.name == switchbot_device.name + assert ( + discovered_adv_data.manufacturer_data == switchbot_device_adv.manufacturer_data + ) + assert discovered_adv_data.service_data == switchbot_device_adv.service_data + assert discovered_adv_data.service_uuids == switchbot_device_adv.service_uuids + scanner.inject_advertisement(switchbot_device_2, switchbot_device_adv_2) + + data = scanner.discovered_devices_and_advertisement_data + discovered_device, discovered_adv_data = data[switchbot_device.address] + assert discovered_device.address == switchbot_device.address + assert discovered_device.name == switchbot_device.name + assert discovered_adv_data.manufacturer_data == {1: b"\x01", 2: b"\x02"} + assert discovered_adv_data.service_data == { + "050a021a-0000-1000-8000-00805f9b34fb": b"\n\xff", + "00000001-0000-1000-8000-00805f9b34fb": b"\n\xff", + } + assert set(discovered_adv_data.service_uuids) == { + "050a021a-0000-1000-8000-00805f9b34fb", + "00000001-0000-1000-8000-00805f9b34fb", + } + + cancel() + + +async def test_remote_scanner_expires_connectable(hass): + """Test the remote scanner expires stale connectable data.""" + manager = _get_manager() + + switchbot_device = BLEDevice( + "44:44:33:11:23:45", + "wohand", + {}, + rssi=-100, + ) + switchbot_device_adv = generate_advertisement_data( + local_name="wohand", + service_uuids=[], + manufacturer_data={1: b"\x01"}, + rssi=-100, + ) + + class FakeScanner(BaseHaRemoteScanner): + def inject_advertisement( + self, device: BLEDevice, advertisement_data: AdvertisementData + ) -> None: + """Inject an advertisement.""" + self._async_on_advertisement( + device.address, + advertisement_data.rssi, + device.name, + advertisement_data.service_uuids, + advertisement_data.service_data, + advertisement_data.manufacturer_data, + advertisement_data.tx_power, + ) + + new_info_callback = manager.scanner_adv_received + connector = ( + HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), + ) + scanner = FakeScanner(hass, "esp32", new_info_callback, connector, True) + scanner.async_setup() + cancel = manager.async_register_scanner(scanner, True) + + start_time_monotonic = time.monotonic() + scanner.inject_advertisement(switchbot_device, switchbot_device_adv) + + devices = scanner.discovered_devices + assert len(scanner.discovered_devices) == 1 + assert len(scanner.discovered_devices_and_advertisement_data) == 1 + assert devices[0].name == "wohand" + + expire_monotonic = ( + start_time_monotonic + + CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + + 1 + ) + expire_utc = dt_util.utcnow() + timedelta( + seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1 + ) + with patch( + "homeassistant.components.bluetooth.models.MONOTONIC_TIME", + return_value=expire_monotonic, + ): + async_fire_time_changed(hass, expire_utc) + await hass.async_block_till_done() + + devices = scanner.discovered_devices + assert len(scanner.discovered_devices) == 0 + assert len(scanner.discovered_devices_and_advertisement_data) == 0 + + cancel() + + +async def test_remote_scanner_expires_non_connectable(hass): + """Test the remote scanner expires stale non connectable data.""" + manager = _get_manager() + + switchbot_device = BLEDevice( + "44:44:33:11:23:45", + "wohand", + {}, + rssi=-100, + ) + switchbot_device_adv = generate_advertisement_data( + local_name="wohand", + service_uuids=[], + manufacturer_data={1: b"\x01"}, + rssi=-100, + ) + + class FakeScanner(BaseHaRemoteScanner): + def inject_advertisement( + self, device: BLEDevice, advertisement_data: AdvertisementData + ) -> None: + """Inject an advertisement.""" + self._async_on_advertisement( + device.address, + advertisement_data.rssi, + device.name, + advertisement_data.service_uuids, + advertisement_data.service_data, + advertisement_data.manufacturer_data, + advertisement_data.tx_power, + ) + + new_info_callback = manager.scanner_adv_received + connector = ( + HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), + ) + scanner = FakeScanner(hass, "esp32", new_info_callback, connector, False) + scanner.async_setup() + cancel = manager.async_register_scanner(scanner, True) + + start_time_monotonic = time.monotonic() + scanner.inject_advertisement(switchbot_device, switchbot_device_adv) + + devices = scanner.discovered_devices + assert len(scanner.discovered_devices) == 1 + assert len(scanner.discovered_devices_and_advertisement_data) == 1 + assert devices[0].name == "wohand" + + assert ( + FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + > CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + ) + + # The connectable timeout is not used for non connectable devices + expire_monotonic = ( + start_time_monotonic + + CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + + 1 + ) + expire_utc = dt_util.utcnow() + timedelta( + seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1 + ) + with patch( + "homeassistant.components.bluetooth.models.MONOTONIC_TIME", + return_value=expire_monotonic, + ): + async_fire_time_changed(hass, expire_utc) + await hass.async_block_till_done() + + assert len(scanner.discovered_devices) == 1 + assert len(scanner.discovered_devices_and_advertisement_data) == 1 + + # The non connectable timeout is used for non connectable devices + # which is always longer than the connectable timeout + expire_monotonic = ( + start_time_monotonic + FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1 + ) + expire_utc = dt_util.utcnow() + timedelta( + seconds=FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1 + ) + with patch( + "homeassistant.components.bluetooth.models.MONOTONIC_TIME", + return_value=expire_monotonic, + ): + async_fire_time_changed(hass, expire_utc) + await hass.async_block_till_done() + + assert len(scanner.discovered_devices) == 0 + assert len(scanner.discovered_devices_and_advertisement_data) == 0 + + cancel() From 298c95d9f0b0fdb5f93b3bf59215df88f54516af Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Nov 2022 17:12:52 -0600 Subject: [PATCH 0394/1033] Bump pySwitchbot to 0.20.4 (#82055) Fixes for curtain firmware v6 changelog: https://github.com/Danielhiversen/pySwitchbot/compare/0.20.3...0.20.4 --- homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index 2c95327beef..d586a328ed7 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.20.3"], + "requirements": ["PySwitchbot==0.20.4"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 4719d3282fd..a0bc24288fb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -37,7 +37,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.20.3 +PySwitchbot==0.20.4 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3e4222cec38..76a02996015 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -33,7 +33,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.20.3 +PySwitchbot==0.20.4 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From ae436d46fcad41f7941da60885602f6221946a59 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Sun, 13 Nov 2022 18:57:09 -0500 Subject: [PATCH 0395/1033] Add options to UniFi Protect diagnostics (#82062) --- .../components/unifiprotect/diagnostics.py | 3 ++- .../unifiprotect/test_diagnostics.py | 24 ++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/unifiprotect/diagnostics.py b/homeassistant/components/unifiprotect/diagnostics.py index b76c9eba1e7..6d4ebcd975d 100644 --- a/homeassistant/components/unifiprotect/diagnostics.py +++ b/homeassistant/components/unifiprotect/diagnostics.py @@ -18,4 +18,5 @@ async def async_get_config_entry_diagnostics( """Return diagnostics for a config entry.""" data: ProtectData = hass.data[DOMAIN][config_entry.entry_id] - return cast(dict[str, Any], anonymize_data(data.api.bootstrap.unifi_dict())) + bootstrap = cast(dict[str, Any], anonymize_data(data.api.bootstrap.unifi_dict())) + return {"bootstrap": bootstrap, "options": dict(config_entry.options)} diff --git a/tests/components/unifiprotect/test_diagnostics.py b/tests/components/unifiprotect/test_diagnostics.py index a0ed8f0d882..396cc13efa9 100644 --- a/tests/components/unifiprotect/test_diagnostics.py +++ b/tests/components/unifiprotect/test_diagnostics.py @@ -2,6 +2,7 @@ from pyunifiprotect.data import NVR, Light +from homeassistant.components.unifiprotect.const import CONF_ALLOW_EA from homeassistant.core import HomeAssistant from .utils import MockUFPFixture, init_entry @@ -16,12 +17,23 @@ async def test_diagnostics( await init_entry(hass, ufp, [light]) + options = dict(ufp.entry.options) + options[CONF_ALLOW_EA] = True + hass.config_entries.async_update_entry(ufp.entry, options=options) + await hass.async_block_till_done() + diag = await get_diagnostics_for_config_entry(hass, hass_client, ufp.entry) + assert "options" in diag and isinstance(diag["options"], dict) + options = diag["options"] + assert options[CONF_ALLOW_EA] is True + + assert "bootstrap" in diag and isinstance(diag["bootstrap"], dict) + bootstrap = diag["bootstrap"] nvr: NVR = ufp.api.bootstrap.nvr # validate some of the data - assert "nvr" in diag and isinstance(diag["nvr"], dict) - nvr_dict = diag["nvr"] + assert "nvr" in bootstrap and isinstance(bootstrap["nvr"], dict) + nvr_dict = bootstrap["nvr"] # should have been anonymized assert nvr_dict["id"] != nvr.id assert nvr_dict["mac"] != nvr.mac @@ -32,11 +44,11 @@ async def test_diagnostics( assert nvr_dict["type"] == nvr.type assert ( - "lights" in diag - and isinstance(diag["lights"], list) - and len(diag["lights"]) == 1 + "lights" in bootstrap + and isinstance(bootstrap["lights"], list) + and len(bootstrap["lights"]) == 1 ) - light_dict = diag["lights"][0] + light_dict = bootstrap["lights"][0] # should have been anonymized assert light_dict["id"] != light.id assert light_dict["name"] != light.mac From 48f6c9a4860489359f491231e2ac375934b6710b Mon Sep 17 00:00:00 2001 From: G Johansson Date: Mon, 14 Nov 2022 01:23:14 +0100 Subject: [PATCH 0396/1033] Add tests DNS IP (#82059) --- .coveragerc | 2 - tests/components/dnsip/__init__.py | 36 +++++++ tests/components/dnsip/test_config_flow.py | 25 ++--- tests/components/dnsip/test_init.py | 54 +++++++++++ tests/components/dnsip/test_sensor.py | 105 +++++++++++++++++++++ 5 files changed, 202 insertions(+), 20 deletions(-) create mode 100644 tests/components/dnsip/test_init.py create mode 100644 tests/components/dnsip/test_sensor.py diff --git a/.coveragerc b/.coveragerc index 1a8bfb462d8..e75460f742f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -234,8 +234,6 @@ omit = homeassistant/components/dlib_face_detect/image_processing.py homeassistant/components/dlib_face_identify/image_processing.py homeassistant/components/dlink/switch.py - homeassistant/components/dnsip/__init__.py - homeassistant/components/dnsip/sensor.py homeassistant/components/dominos/* homeassistant/components/doods/* homeassistant/components/doorbird/__init__.py diff --git a/tests/components/dnsip/__init__.py b/tests/components/dnsip/__init__.py index 9fb6f529c5e..1a465b59ab6 100644 --- a/tests/components/dnsip/__init__.py +++ b/tests/components/dnsip/__init__.py @@ -1 +1,37 @@ """Tests for the dnsip integration.""" +from __future__ import annotations + + +class QueryResult: + """Return Query results.""" + + host = "1.2.3.4" + ttl = 60 + + +class RetrieveDNS: + """Return list of test information.""" + + def __init__( + self, nameservers: list[str] | None = None, error: Exception | None = None + ) -> None: + """Initialize DNS class.""" + if nameservers: + self.nameservers = nameservers + self._nameservers = ["1.2.3.4"] + self.error = error + + async def query(self, hostname, qtype) -> dict[str, str]: + """Return information.""" + if self.error: + raise self.error + return [QueryResult] + + @property + def nameservers(self) -> list[str]: + """Return nameserver.""" + return self._nameservers + + @nameservers.setter + def nameservers(self, value: list[str]) -> None: + self._nameservers = value diff --git a/tests/components/dnsip/test_config_flow.py b/tests/components/dnsip/test_config_flow.py index fdec45be7f5..990fd4df159 100644 --- a/tests/components/dnsip/test_config_flow.py +++ b/tests/components/dnsip/test_config_flow.py @@ -20,23 +20,11 @@ from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from . import RetrieveDNS + from tests.common import MockConfigEntry -class RetrieveDNS: - """Return list of test information.""" - - @staticmethod - async def query(hostname, qtype) -> dict[str, str]: - """Return information.""" - return {"hostname": "1.2.3.4"} - - @property - def nameservers(self) -> list[str]: - """Return nameserver.""" - return ["1.2.3.4"] - - async def test_form(hass: HomeAssistant) -> None: """Test we get the form.""" @@ -164,12 +152,13 @@ async def test_flow_already_exist(hass: HomeAssistant) -> None: DOMAIN, context={"source": config_entries.SOURCE_USER} ) + dns_mock = RetrieveDNS() with patch( "homeassistant.components.dnsip.async_setup_entry", return_value=True, ), patch( "homeassistant.components.dnsip.config_flow.aiodns.DNSResolver", - return_value=RetrieveDNS, + return_value=dns_mock, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -204,9 +193,6 @@ async def test_options_flow(hass: HomeAssistant) -> None: with patch( "homeassistant.components.dnsip.config_flow.aiodns.DNSResolver", return_value=RetrieveDNS(), - ), patch( - "homeassistant.components.dnsip.async_setup_entry", - return_value=True, ): assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() @@ -223,6 +209,7 @@ async def test_options_flow(hass: HomeAssistant) -> None: CONF_RESOLVER_IPV6: "2001:4860:4860::8888", }, ) + await hass.async_block_till_done() assert result["type"] == FlowResultType.CREATE_ENTRY assert result["data"] == { @@ -230,6 +217,8 @@ async def test_options_flow(hass: HomeAssistant) -> None: "resolver_ipv6": "2001:4860:4860::8888", } + assert entry.state == config_entries.ConfigEntryState.LOADED + @pytest.mark.parametrize( "p_input", diff --git a/tests/components/dnsip/test_init.py b/tests/components/dnsip/test_init.py new file mode 100644 index 00000000000..1c8cf04c783 --- /dev/null +++ b/tests/components/dnsip/test_init.py @@ -0,0 +1,54 @@ +"""Test for DNS IP component Init.""" +from __future__ import annotations + +from unittest.mock import patch + +from homeassistant import config_entries +from homeassistant.components.dnsip.const import ( + CONF_HOSTNAME, + CONF_IPV4, + CONF_IPV6, + CONF_RESOLVER, + CONF_RESOLVER_IPV6, + DOMAIN, +) +from homeassistant.config_entries import SOURCE_USER +from homeassistant.const import CONF_NAME +from homeassistant.core import HomeAssistant + +from . import RetrieveDNS + +from tests.common import MockConfigEntry + + +async def test_load_unload_entry(hass: HomeAssistant) -> None: + """Test load and unload an entry.""" + entry = MockConfigEntry( + domain=DOMAIN, + source=SOURCE_USER, + data={ + CONF_HOSTNAME: "home-assistant.io", + CONF_NAME: "home-assistant.io", + CONF_IPV4: True, + CONF_IPV6: False, + }, + options={ + CONF_RESOLVER: "208.67.222.222", + CONF_RESOLVER_IPV6: "2620:0:ccc::2", + }, + entry_id="1", + unique_id="home-assistant.io", + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.dnsip.config_flow.aiodns.DNSResolver", + return_value=RetrieveDNS(), + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state == config_entries.ConfigEntryState.LOADED + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + assert entry.state is config_entries.ConfigEntryState.NOT_LOADED diff --git a/tests/components/dnsip/test_sensor.py b/tests/components/dnsip/test_sensor.py new file mode 100644 index 00000000000..f44d58d2125 --- /dev/null +++ b/tests/components/dnsip/test_sensor.py @@ -0,0 +1,105 @@ +"""The test for the DNS IP sensor platform.""" +from __future__ import annotations + +from datetime import timedelta +from unittest.mock import patch + +from aiodns.error import DNSError + +from homeassistant.components.dnsip.const import ( + CONF_HOSTNAME, + CONF_IPV4, + CONF_IPV6, + CONF_RESOLVER, + CONF_RESOLVER_IPV6, + DOMAIN, +) +from homeassistant.config_entries import SOURCE_USER +from homeassistant.const import CONF_NAME, STATE_UNAVAILABLE +from homeassistant.core import HomeAssistant +from homeassistant.util import dt + +from . import RetrieveDNS + +from tests.common import MockConfigEntry, async_fire_time_changed + + +async def test_sensor(hass: HomeAssistant) -> None: + """Test the DNS IP sensor.""" + entry = MockConfigEntry( + domain=DOMAIN, + source=SOURCE_USER, + data={ + CONF_HOSTNAME: "home-assistant.io", + CONF_NAME: "home-assistant.io", + CONF_IPV4: True, + CONF_IPV6: True, + }, + options={ + CONF_RESOLVER: "208.67.222.222", + CONF_RESOLVER_IPV6: "2620:0:ccc::2", + }, + entry_id="1", + unique_id="home-assistant.io", + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.dnsip.sensor.aiodns.DNSResolver", + return_value=RetrieveDNS(), + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state1 = hass.states.get("sensor.home_assistant_io") + state2 = hass.states.get("sensor.home_assistant_io_ipv6") + + assert state1.state == "1.2.3.4" + assert state2.state == "1.2.3.4" + + +async def test_sensor_no_response(hass: HomeAssistant) -> None: + """Test the DNS IP sensor with DNS error.""" + entry = MockConfigEntry( + domain=DOMAIN, + source=SOURCE_USER, + data={ + CONF_HOSTNAME: "home-assistant.io", + CONF_NAME: "home-assistant.io", + CONF_IPV4: True, + CONF_IPV6: False, + }, + options={ + CONF_RESOLVER: "208.67.222.222", + CONF_RESOLVER_IPV6: "2620:0:ccc::2", + }, + entry_id="1", + unique_id="home-assistant.io", + ) + entry.add_to_hass(hass) + + dns_mock = RetrieveDNS() + with patch( + "homeassistant.components.dnsip.sensor.aiodns.DNSResolver", + return_value=dns_mock, + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get("sensor.home_assistant_io") + + assert state.state == "1.2.3.4" + + dns_mock.error = DNSError() + with patch( + "homeassistant.components.dnsip.sensor.aiodns.DNSResolver", + return_value=dns_mock, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=10), + ) + await hass.async_block_till_done() + + state = hass.states.get("sensor.home_assistant_io") + assert state.state == STATE_UNAVAILABLE From 42652872f6f4de31a31a2fb79808e6a758b34461 Mon Sep 17 00:00:00 2001 From: Avi Miller Date: Mon, 14 Nov 2022 12:43:45 +1100 Subject: [PATCH 0397/1033] Restore color_temp handling for lifx.set_state (#82067) fixes undefined --- homeassistant/components/lifx/util.py | 7 +++++++ tests/components/lifx/test_light.py | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/homeassistant/components/lifx/util.py b/homeassistant/components/lifx/util.py index 6a9bff465ee..fde36d714d5 100644 --- a/homeassistant/components/lifx/util.py +++ b/homeassistant/components/lifx/util.py @@ -16,6 +16,7 @@ from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_PCT, ATTR_COLOR_NAME, + ATTR_COLOR_TEMP, ATTR_COLOR_TEMP_KELVIN, ATTR_HS_COLOR, ATTR_KELVIN, @@ -114,6 +115,12 @@ def find_hsbk(hass: HomeAssistant, **kwargs: Any) -> list[float | int | None] | kelvin = kwargs.pop(ATTR_KELVIN) saturation = 0 + if ATTR_COLOR_TEMP in kwargs: + kelvin = color_util.color_temperature_mired_to_kelvin( + kwargs.pop(ATTR_COLOR_TEMP) + ) + saturation = 0 + if ATTR_COLOR_TEMP_KELVIN in kwargs: kelvin = kwargs.pop(ATTR_COLOR_TEMP_KELVIN) saturation = 0 diff --git a/tests/components/lifx/test_light.py b/tests/components/lifx/test_light.py index 866095709ce..f1126d8ab7b 100644 --- a/tests/components/lifx/test_light.py +++ b/tests/components/lifx/test_light.py @@ -1539,6 +1539,15 @@ async def test_lifx_set_state_kelvin(hass: HomeAssistant) -> None: assert bulb.set_color.calls[0][0][0] == [32000, 0, 25700, 2700] bulb.set_color.reset_mock() + await hass.services.async_call( + DOMAIN, + "set_state", + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 255, ATTR_COLOR_TEMP: 400}, + blocking=True, + ) + assert bulb.set_color.calls[0][0][0] == [32000, 0, 65535, 2500] + bulb.set_color.reset_mock() + async def test_infrared_color_bulb(hass: HomeAssistant) -> None: """Test setting infrared with a color bulb.""" From 4bf233a4c134fc3acc0d276fc73d95ecc2e1de4b Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 13 Nov 2022 18:24:32 -0800 Subject: [PATCH 0398/1033] Bump python-google-nest-sdm to 2.1.0 (#82066) --- homeassistant/components/nest/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nest/manifest.json b/homeassistant/components/nest/manifest.json index 90fad5cf185..f0e86456fd7 100644 --- a/homeassistant/components/nest/manifest.json +++ b/homeassistant/components/nest/manifest.json @@ -5,7 +5,7 @@ "dependencies": ["ffmpeg", "http", "application_credentials"], "after_dependencies": ["media_source"], "documentation": "https://www.home-assistant.io/integrations/nest", - "requirements": ["python-nest==4.2.0", "google-nest-sdm==2.0.0"], + "requirements": ["python-nest==4.2.0", "google-nest-sdm==2.1.0"], "codeowners": ["@allenporter"], "quality_scale": "platinum", "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index a0bc24288fb..f264688c186 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -789,7 +789,7 @@ google-cloud-pubsub==2.13.10 google-cloud-texttospeech==2.12.3 # homeassistant.components.nest -google-nest-sdm==2.0.0 +google-nest-sdm==2.1.0 # homeassistant.components.google_travel_time googlemaps==2.5.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 76a02996015..f24c2bd782b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -596,7 +596,7 @@ goodwe==0.2.18 google-cloud-pubsub==2.13.10 # homeassistant.components.nest -google-nest-sdm==2.0.0 +google-nest-sdm==2.1.0 # homeassistant.components.google_travel_time googlemaps==2.5.1 From 727dcd6df6e240ec74084ffdb0baea22e3946745 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 14 Nov 2022 07:37:47 +0200 Subject: [PATCH 0399/1033] Keep mypy.ini and strict-typing files sorted with hassfest (#81331) hassfest: keep mypy.ini and .strict-typing sorted --- .strict-typing | 16 +++---- mypy.ini | 74 +++++++++++++++--------------- script/hassfest/mypy_config.py | 83 +++++++++++++++++++++++++--------- 3 files changed, 106 insertions(+), 67 deletions(-) diff --git a/.strict-typing b/.strict-typing index d45fe269638..4778893bfc2 100644 --- a/.strict-typing +++ b/.strict-typing @@ -5,14 +5,10 @@ # Strict typing is enabled by default for core files. # Add it here to add 'disallow_any_generics'. # --- Only for core file! --- -homeassistant.exceptions -homeassistant.core -homeassistant.loader -homeassistant.requirements -homeassistant.runner -homeassistant.setup homeassistant.auth.auth_store homeassistant.auth.providers.* +homeassistant.core +homeassistant.exceptions homeassistant.helpers.area_registry homeassistant.helpers.condition homeassistant.helpers.debounce @@ -29,6 +25,10 @@ homeassistant.helpers.script_variables homeassistant.helpers.singleton homeassistant.helpers.sun homeassistant.helpers.translation +homeassistant.loader +homeassistant.requirements +homeassistant.runner +homeassistant.setup homeassistant.util.async_ homeassistant.util.color homeassistant.util.decorator @@ -79,9 +79,9 @@ homeassistant.components.button.* homeassistant.components.calendar.* homeassistant.components.camera.* homeassistant.components.canary.* -homeassistant.components.cover.* homeassistant.components.clickatell.* homeassistant.components.clicksend.* +homeassistant.components.cover.* homeassistant.components.cpuspeed.* homeassistant.components.crownstone.* homeassistant.components.deconz.* @@ -215,9 +215,9 @@ homeassistant.components.prusalink.* homeassistant.components.pure_energie.* homeassistant.components.pvoutput.* homeassistant.components.qnap_qsw.* +homeassistant.components.radarr.* homeassistant.components.rainmachine.* homeassistant.components.rdw.* -homeassistant.components.radarr.* homeassistant.components.recollect_waste.* homeassistant.components.recorder.* homeassistant.components.remote.* diff --git a/mypy.ini b/mypy.ini index e30a78dab8c..326bbc5ed36 100644 --- a/mypy.ini +++ b/mypy.ini @@ -28,28 +28,16 @@ warn_unreachable = true [mypy-homeassistant.*] no_implicit_reexport = true -[mypy-homeassistant.exceptions] +[mypy-homeassistant.auth.auth_store] +disallow_any_generics = true + +[mypy-homeassistant.auth.providers.*] disallow_any_generics = true [mypy-homeassistant.core] disallow_any_generics = true -[mypy-homeassistant.loader] -disallow_any_generics = true - -[mypy-homeassistant.requirements] -disallow_any_generics = true - -[mypy-homeassistant.runner] -disallow_any_generics = true - -[mypy-homeassistant.setup] -disallow_any_generics = true - -[mypy-homeassistant.auth.auth_store] -disallow_any_generics = true - -[mypy-homeassistant.auth.providers.*] +[mypy-homeassistant.exceptions] disallow_any_generics = true [mypy-homeassistant.helpers.area_registry] @@ -100,6 +88,18 @@ disallow_any_generics = true [mypy-homeassistant.helpers.translation] disallow_any_generics = true +[mypy-homeassistant.loader] +disallow_any_generics = true + +[mypy-homeassistant.requirements] +disallow_any_generics = true + +[mypy-homeassistant.runner] +disallow_any_generics = true + +[mypy-homeassistant.setup] +disallow_any_generics = true + [mypy-homeassistant.util.async_] disallow_any_generics = true @@ -543,16 +543,6 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true -[mypy-homeassistant.components.cover.*] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -warn_return_any = true -warn_unreachable = true - [mypy-homeassistant.components.clickatell.*] check_untyped_defs = true disallow_incomplete_defs = true @@ -573,6 +563,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.cover.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.cpuspeed.*] check_untyped_defs = true disallow_incomplete_defs = true @@ -1903,6 +1903,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.radarr.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.rainmachine.*] check_untyped_defs = true disallow_incomplete_defs = true @@ -1923,16 +1933,6 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true -[mypy-homeassistant.components.radarr.*] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -warn_return_any = true -warn_unreachable = true - [mypy-homeassistant.components.recollect_waste.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index 99e07bcf118..e6886f2f79f 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -1,6 +1,7 @@ """Generate mypy config.""" from __future__ import annotations +from collections.abc import Iterable import configparser import io import os @@ -90,18 +91,48 @@ def _strict_module_in_ignore_list( return None -def generate_and_validate(config: Config) -> str: +def _sort_within_sections(line_iter: Iterable[str]) -> Iterable[str]: + """ + Sort lines within sections. + + Sections are defined as anything not delimited by a blank line + or an octothorpe-prefixed comment line. + """ + section: list[str] = [] + for line in line_iter: + if line.startswith("#") or not line.strip(): + yield from sorted(section) + section.clear() + yield line + continue + section.append(line) + yield from sorted(section) + + +def _get_strict_typing_path(config: Config) -> Path: + return config.root / ".strict-typing" + + +def _get_mypy_ini_path(config: Config) -> Path: + return config.root / "mypy.ini" + + +def _generate_and_validate_strict_typing(config: Config) -> str: + """Validate and generate strict_typing.""" + lines = [ + line.strip() + for line in _get_strict_typing_path(config).read_text().splitlines() + ] + return "\n".join(_sort_within_sections(lines)) + "\n" + + +def _generate_and_validate_mypy_config(config: Config) -> str: """Validate and generate mypy config.""" - config_path = config.root / ".strict-typing" - - with config_path.open() as fp: - lines = fp.readlines() - # Filter empty and commented lines. parsed_modules: list[str] = [ line.strip() - for line in lines + for line in config.cache["strict_typing"].splitlines() if line.strip() != "" and not line.startswith("#") ] @@ -210,28 +241,36 @@ def generate_and_validate(config: Config) -> str: with io.StringIO() as fp: mypy_config.write(fp) fp.seek(0) - return HEADER + fp.read().strip() + return f"{HEADER}{fp.read().strip()}\n" def validate(integrations: dict[str, Integration], config: Config) -> None: - """Validate mypy config.""" - config_path = config.root / "mypy.ini" - config.cache["mypy_config"] = content = generate_and_validate(config) + """Validate strict_typing and mypy config.""" + strict_typing_content = _generate_and_validate_strict_typing(config) + config.cache["strict_typing"] = strict_typing_content + + mypy_content = _generate_and_validate_mypy_config(config) + config.cache["mypy_config"] = mypy_content if any(err.plugin == "mypy_config" for err in config.errors): return - with open(str(config_path)) as fp: - if fp.read().strip() != content: - config.add_error( - "mypy_config", - "File mypy.ini is not up to date. Run python3 -m script.hassfest", - fixable=True, - ) + if _get_strict_typing_path(config).read_text() != strict_typing_content: + config.add_error( + "mypy_config", + "File .strict_typing is not up to date. Run python3 -m script.hassfest", + fixable=True, + ) + + if _get_mypy_ini_path(config).read_text() != mypy_content: + config.add_error( + "mypy_config", + "File mypy.ini is not up to date. Run python3 -m script.hassfest", + fixable=True, + ) def generate(integrations: dict[str, Integration], config: Config) -> None: - """Generate mypy config.""" - config_path = config.root / "mypy.ini" - with open(str(config_path), "w") as fp: - fp.write(f"{config.cache['mypy_config']}\n") + """Generate strict_typing and mypy config.""" + _get_mypy_ini_path(config).write_text(config.cache["mypy_config"]) + _get_strict_typing_path(config).write_text(config.cache["strict_typing"]) From a0b0e4088c42aef7613a7e62f8fcc6551bcc17cd Mon Sep 17 00:00:00 2001 From: Thomas Dietrich Date: Mon, 14 Nov 2022 09:23:02 +0100 Subject: [PATCH 0400/1033] Replace quantiles by percentile characteristic for statistics component (#81027) * Remove quantiles characteristic * Add percentile characteristic --- homeassistant/components/statistics/sensor.py | 43 ++++--------- tests/components/statistics/test_sensor.py | 64 +++++++++++++++++-- 2 files changed, 71 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/statistics/sensor.py b/homeassistant/components/statistics/sensor.py index cfc093c7762..55a225eebb8 100644 --- a/homeassistant/components/statistics/sensor.py +++ b/homeassistant/components/statistics/sensor.py @@ -78,7 +78,7 @@ STAT_DISTANCE_ABSOLUTE = "distance_absolute" STAT_MEAN = "mean" STAT_MEDIAN = "median" STAT_NOISINESS = "noisiness" -STAT_QUANTILES = "quantiles" +STAT_PERCENTILE = "percentile" STAT_STANDARD_DEVIATION = "standard_deviation" STAT_SUM = "sum" STAT_SUM_DIFFERENCES = "sum_differences" @@ -107,7 +107,7 @@ STATS_NUMERIC_SUPPORT = { STAT_MEAN, STAT_MEDIAN, STAT_NOISINESS, - STAT_QUANTILES, + STAT_PERCENTILE, STAT_STANDARD_DEVIATION, STAT_SUM, STAT_SUM_DIFFERENCES, @@ -135,7 +135,6 @@ STATS_NOT_A_NUMBER = { STAT_DATETIME_OLDEST, STAT_DATETIME_VALUE_MAX, STAT_DATETIME_VALUE_MIN, - STAT_QUANTILES, } STATS_DATETIME = { @@ -157,6 +156,7 @@ STAT_NUMERIC_RETAIN_UNIT = { STAT_MEAN, STAT_MEDIAN, STAT_NOISINESS, + STAT_PERCENTILE, STAT_STANDARD_DEVIATION, STAT_SUM, STAT_SUM_DIFFERENCES, @@ -177,13 +177,10 @@ CONF_STATE_CHARACTERISTIC = "state_characteristic" CONF_SAMPLES_MAX_BUFFER_SIZE = "sampling_size" CONF_MAX_AGE = "max_age" CONF_PRECISION = "precision" -CONF_QUANTILE_INTERVALS = "quantile_intervals" -CONF_QUANTILE_METHOD = "quantile_method" +CONF_PERCENTILE = "percentile" DEFAULT_NAME = "Stats" DEFAULT_PRECISION = 2 -DEFAULT_QUANTILE_INTERVALS = 4 -DEFAULT_QUANTILE_METHOD = "exclusive" ICON = "mdi:calculator" @@ -248,11 +245,8 @@ _PLATFORM_SCHEMA_BASE = PLATFORM_SCHEMA.extend( vol.Optional(CONF_SAMPLES_MAX_BUFFER_SIZE): vol.Coerce(int), vol.Optional(CONF_MAX_AGE): cv.time_period, vol.Optional(CONF_PRECISION, default=DEFAULT_PRECISION): vol.Coerce(int), - vol.Optional( - CONF_QUANTILE_INTERVALS, default=DEFAULT_QUANTILE_INTERVALS - ): vol.All(vol.Coerce(int), vol.Range(min=2)), - vol.Optional(CONF_QUANTILE_METHOD, default=DEFAULT_QUANTILE_METHOD): vol.In( - ["exclusive", "inclusive"] + vol.Optional(CONF_PERCENTILE, default=50): vol.All( + vol.Coerce(int), vol.Range(min=1, max=99) ), } ) @@ -283,8 +277,7 @@ async def async_setup_platform( samples_max_buffer_size=config[CONF_SAMPLES_MAX_BUFFER_SIZE], samples_max_age=config.get(CONF_MAX_AGE), precision=config[CONF_PRECISION], - quantile_intervals=config[CONF_QUANTILE_INTERVALS], - quantile_method=config[CONF_QUANTILE_METHOD], + percentile=config[CONF_PERCENTILE], ) ], update_before_add=True, @@ -303,8 +296,7 @@ class StatisticsSensor(SensorEntity): samples_max_buffer_size: int, samples_max_age: timedelta | None, precision: int, - quantile_intervals: int, - quantile_method: Literal["exclusive", "inclusive"], + percentile: int, ) -> None: """Initialize the Statistics sensor.""" self._attr_icon: str = ICON @@ -319,8 +311,7 @@ class StatisticsSensor(SensorEntity): self._samples_max_buffer_size: int = samples_max_buffer_size self._samples_max_age: timedelta | None = samples_max_age self._precision: int = precision - self._quantile_intervals: int = quantile_intervals - self._quantile_method: Literal["exclusive", "inclusive"] = quantile_method + self._percentile: int = percentile self._value: StateType | datetime = None self._unit_of_measurement: str | None = None self._available: bool = False @@ -700,18 +691,10 @@ class StatisticsSensor(SensorEntity): return cast(float, self._stat_sum_differences()) / (len(self.states) - 1) return None - def _stat_quantiles(self) -> StateType: - if len(self.states) > self._quantile_intervals: - return str( - [ - round(quantile, self._precision) - for quantile in statistics.quantiles( - self.states, - n=self._quantile_intervals, - method=self._quantile_method, - ) - ] - ) + def _stat_percentile(self) -> StateType: + if len(self.states) >= 2: + percentiles = statistics.quantiles(self.states, n=100, method="exclusive") + return percentiles[self._percentile - 1] return None def _stat_standard_deviation(self) -> StateType: diff --git a/tests/components/statistics/test_sensor.py b/tests/components/statistics/test_sensor.py index 691159fe2fc..be771d8be88 100644 --- a/tests/components/statistics/test_sensor.py +++ b/tests/components/statistics/test_sensor.py @@ -391,7 +391,7 @@ async def test_age_limit_expiry(hass: HomeAssistant): async def test_precision(hass: HomeAssistant): - """Test correct result with precision set.""" + """Test correct results with precision set.""" assert await async_setup_component( hass, "sensor", @@ -433,6 +433,60 @@ async def test_precision(hass: HomeAssistant): assert state.state == str(round(mean, 3)) +async def test_percentile(hass: HomeAssistant): + """Test correct results for percentile characteristic.""" + assert await async_setup_component( + hass, + "sensor", + { + "sensor": [ + { + "platform": "statistics", + "name": "test_percentile_omitted", + "entity_id": "sensor.test_monitored", + "state_characteristic": "percentile", + "sampling_size": 20, + }, + { + "platform": "statistics", + "name": "test_percentile_default", + "entity_id": "sensor.test_monitored", + "state_characteristic": "percentile", + "sampling_size": 20, + "percentile": 50, + }, + { + "platform": "statistics", + "name": "test_percentile_min", + "entity_id": "sensor.test_monitored", + "state_characteristic": "percentile", + "sampling_size": 20, + "percentile": 1, + }, + ] + }, + ) + await hass.async_block_till_done() + + for value in VALUES_NUMERIC: + hass.states.async_set( + "sensor.test_monitored", + str(value), + {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS}, + ) + await hass.async_block_till_done() + + state = hass.states.get("sensor.test_percentile_omitted") + assert state is not None + assert state.state == str(9.2) + state = hass.states.get("sensor.test_percentile_default") + assert state is not None + assert state.state == str(9.2) + state = hass.states.get("sensor.test_percentile_min") + assert state is not None + assert state.state == str(2.72) + + async def test_device_class(hass: HomeAssistant): """Test device class, which depends on the source entity.""" assert await async_setup_component( @@ -753,13 +807,11 @@ async def test_state_characteristics(hass: HomeAssistant): }, { "source_sensor_domain": "sensor", - "name": "quantiles", + "name": "percentile", "value_0": STATE_UNKNOWN, "value_1": STATE_UNKNOWN, - "value_9": [ - round(quantile, 2) for quantile in statistics.quantiles(VALUES_NUMERIC) - ], - "unit": None, + "value_9": 9.2, + "unit": "°C", }, { "source_sensor_domain": "sensor", From 096ef85c68b51eb68c8ef7829182e5be2c20d189 Mon Sep 17 00:00:00 2001 From: muppet3000 Date: Mon, 14 Nov 2022 09:41:25 +0000 Subject: [PATCH 0401/1033] Bump growattServer to 1.2.4 (#82071) Growatt - Library bump to workaround for #81951 --- homeassistant/components/growatt_server/manifest.json | 2 +- homeassistant/components/growatt_server/sensor.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/growatt_server/manifest.json b/homeassistant/components/growatt_server/manifest.json index f3f17804fc1..e3b63f7c8b3 100644 --- a/homeassistant/components/growatt_server/manifest.json +++ b/homeassistant/components/growatt_server/manifest.json @@ -3,7 +3,7 @@ "name": "Growatt", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/growatt_server/", - "requirements": ["growattServer==1.2.3"], + "requirements": ["growattServer==1.2.4"], "codeowners": ["@indykoning", "@muppet3000", "@JasperPlant"], "iot_class": "cloud_polling", "loggers": ["growattServer"] diff --git a/homeassistant/components/growatt_server/sensor.py b/homeassistant/components/growatt_server/sensor.py index eceba2f7bce..ac19d91b24d 100644 --- a/homeassistant/components/growatt_server/sensor.py +++ b/homeassistant/components/growatt_server/sensor.py @@ -32,7 +32,7 @@ from .sensor_types.total import TOTAL_SENSOR_TYPES _LOGGER = logging.getLogger(__name__) -SCAN_INTERVAL = datetime.timedelta(minutes=1) +SCAN_INTERVAL = datetime.timedelta(minutes=5) def get_device_list(api, config): diff --git a/requirements_all.txt b/requirements_all.txt index f264688c186..af6ec213546 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -819,7 +819,7 @@ greenwavereality==0.5.1 gridnet==4.0.0 # homeassistant.components.growatt_server -growattServer==1.2.3 +growattServer==1.2.4 # homeassistant.components.google_sheets gspread==5.5.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f24c2bd782b..9496175043d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -614,7 +614,7 @@ greeneye_monitor==3.0.3 gridnet==4.0.0 # homeassistant.components.growatt_server -growattServer==1.2.3 +growattServer==1.2.4 # homeassistant.components.google_sheets gspread==5.5.0 From de313dcc3fa6d796852169d3356b0bff9916dca8 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 14 Nov 2022 11:13:20 +0100 Subject: [PATCH 0402/1033] Improve config tests (#81898) * Improve config tests * Fix stale docstring * Use os.path.basename --- tests/components/config/conftest.py | 73 +++++++ tests/components/config/test_automation.py | 167 ++++++++-------- tests/components/config/test_scene.py | 216 +++++++++------------ tests/components/config/test_script.py | 29 ++- 4 files changed, 259 insertions(+), 226 deletions(-) create mode 100644 tests/components/config/conftest.py diff --git a/tests/components/config/conftest.py b/tests/components/config/conftest.py new file mode 100644 index 00000000000..e6f1532428e --- /dev/null +++ b/tests/components/config/conftest.py @@ -0,0 +1,73 @@ +"""Test fixtures for the config integration.""" +from contextlib import contextmanager +from copy import deepcopy +import json +import logging +from os.path import basename +from unittest.mock import patch + +import pytest + +from homeassistant.core import HomeAssistant + +from tests.common import raise_contains_mocks + +_LOGGER = logging.getLogger(__name__) + + +@contextmanager +def mock_config_store(data=None): + """Mock config yaml store. + + Data is a dict {'key': {'version': version, 'data': data}} + + Written data will be converted to JSON to ensure JSON parsing works. + """ + if data is None: + data = {} + + def mock_read(path): + """Mock version of load.""" + file_name = basename(path) + _LOGGER.info("Reading data from %s: %s", file_name, data.get(file_name)) + return deepcopy(data.get(file_name)) + + def mock_write(path, data_to_write): + """Mock version of write.""" + file_name = basename(path) + _LOGGER.info("Writing data to %s: %s", file_name, data_to_write) + raise_contains_mocks(data_to_write) + # To ensure that the data can be serialized + data[file_name] = json.loads(json.dumps(data_to_write)) + + async def mock_async_hass_config_yaml(hass: HomeAssistant) -> dict: + """Mock version of async_hass_config_yaml.""" + result = {} + # Return a configuration.yaml with "automation" mapped to the contents of + # automations.yaml and so on. + for key, value in data.items(): + result[key.partition(".")[0][0:-1]] = deepcopy(value) + _LOGGER.info("Reading data from configuration.yaml: %s", result) + return result + + with patch( + "homeassistant.components.config._read", + side_effect=mock_read, + autospec=True, + ), patch( + "homeassistant.components.config._write", + side_effect=mock_write, + autospec=True, + ), patch( + "homeassistant.config.async_hass_config_yaml", + side_effect=mock_async_hass_config_yaml, + autospec=True, + ): + yield data + + +@pytest.fixture +def hass_config_store(): + """Fixture to mock config yaml store.""" + with mock_config_store() as stored_data: + yield stored_data diff --git a/tests/components/config/test_automation.py b/tests/components/config/test_automation.py index 6f782fdbbff..0a5a79c7d15 100644 --- a/tests/components/config/test_automation.py +++ b/tests/components/config/test_automation.py @@ -7,6 +7,7 @@ import pytest from homeassistant.bootstrap import async_setup_component from homeassistant.components import config +from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from tests.components.blueprint.conftest import stub_blueprint_populate # noqa: F401 @@ -23,19 +24,18 @@ async def setup_automation( @pytest.mark.parametrize("automation_config", ({},)) -async def test_get_device_config(hass, hass_client, setup_automation): - """Test getting device config.""" +async def test_get_automation_config( + hass: HomeAssistant, hass_client, hass_config_store, setup_automation +): + """Test getting automation config.""" with patch.object(config, "SECTIONS", ["automation"]): await async_setup_component(hass, "config", {}) client = await hass_client() - def mock_read(path): - """Mock reading data.""" - return [{"id": "sun"}, {"id": "moon"}] + hass_config_store["automations.yaml"] = [{"id": "sun"}, {"id": "moon"}] - with patch("homeassistant.components.config._read", mock_read): - resp = await client.get("/api/config/automation/config/moon") + resp = await client.get("/api/config/automation/config/moon") assert resp.status == HTTPStatus.OK result = await resp.json() @@ -44,85 +44,81 @@ async def test_get_device_config(hass, hass_client, setup_automation): @pytest.mark.parametrize("automation_config", ({},)) -async def test_update_device_config(hass, hass_client, setup_automation): - """Test updating device config.""" +async def test_update_automation_config( + hass: HomeAssistant, hass_client, hass_config_store, setup_automation +): + """Test updating automation config.""" with patch.object(config, "SECTIONS", ["automation"]): await async_setup_component(hass, "config", {}) + assert sorted(hass.states.async_entity_ids("automation")) == [] + client = await hass_client() orig_data = [{"id": "sun"}, {"id": "moon"}] + hass_config_store["automations.yaml"] = orig_data - def mock_read(path): - """Mock reading data.""" - return orig_data - - written = [] - - def mock_write(path, data): - """Mock writing data.""" - written.append(data) - - with patch("homeassistant.components.config._read", mock_read), patch( - "homeassistant.components.config._write", mock_write - ), patch("homeassistant.config.async_hass_config_yaml", return_value={}): - resp = await client.post( - "/api/config/automation/config/moon", - data=json.dumps({"trigger": [], "action": [], "condition": []}), - ) + resp = await client.post( + "/api/config/automation/config/moon", + data=json.dumps({"trigger": [], "action": [], "condition": []}), + ) + await hass.async_block_till_done() + assert sorted(hass.states.async_entity_ids("automation")) == [ + "automation.automation_0" + ] assert resp.status == HTTPStatus.OK result = await resp.json() assert result == {"result": "ok"} - assert list(orig_data[1]) == ["id", "trigger", "condition", "action"] - assert orig_data[1] == {"id": "moon", "trigger": [], "condition": [], "action": []} - assert written[0] == orig_data + new_data = hass_config_store["automations.yaml"] + assert list(new_data[1]) == ["id", "trigger", "condition", "action"] + assert new_data[1] == {"id": "moon", "trigger": [], "condition": [], "action": []} @pytest.mark.parametrize("automation_config", ({},)) -async def test_update_remove_key_device_config(hass, hass_client, setup_automation): - """Test updating device config while removing a key.""" +async def test_update_remove_key_automation_config( + hass: HomeAssistant, hass_client, hass_config_store, setup_automation +): + """Test updating automation config while removing a key.""" with patch.object(config, "SECTIONS", ["automation"]): await async_setup_component(hass, "config", {}) + assert sorted(hass.states.async_entity_ids("automation")) == [] + client = await hass_client() orig_data = [{"id": "sun", "key": "value"}, {"id": "moon", "key": "value"}] + hass_config_store["automations.yaml"] = orig_data - def mock_read(path): - """Mock reading data.""" - return orig_data - - written = [] - - def mock_write(path, data): - """Mock writing data.""" - written.append(data) - - with patch("homeassistant.components.config._read", mock_read), patch( - "homeassistant.components.config._write", mock_write - ), patch("homeassistant.config.async_hass_config_yaml", return_value={}): - resp = await client.post( - "/api/config/automation/config/moon", - data=json.dumps({"trigger": [], "action": [], "condition": []}), - ) + resp = await client.post( + "/api/config/automation/config/moon", + data=json.dumps({"trigger": [], "action": [], "condition": []}), + ) + await hass.async_block_till_done() + assert sorted(hass.states.async_entity_ids("automation")) == [ + "automation.automation_0" + ] assert resp.status == HTTPStatus.OK result = await resp.json() assert result == {"result": "ok"} - assert list(orig_data[1]) == ["id", "trigger", "condition", "action"] - assert orig_data[1] == {"id": "moon", "trigger": [], "condition": [], "action": []} - assert written[0] == orig_data + new_data = hass_config_store["automations.yaml"] + assert list(new_data[1]) == ["id", "trigger", "condition", "action"] + assert new_data[1] == {"id": "moon", "trigger": [], "condition": [], "action": []} @pytest.mark.parametrize("automation_config", ({},)) -async def test_bad_formatted_automations(hass, hass_client, setup_automation): +async def test_bad_formatted_automations( + hass: HomeAssistant, hass_client, hass_config_store, setup_automation +): """Test that we handle automations without ID.""" with patch.object(config, "SECTIONS", ["automation"]): await async_setup_component(hass, "config", {}) + assert sorted(hass.states.async_entity_ids("automation")) == [] + client = await hass_client() orig_data = [ @@ -132,34 +128,25 @@ async def test_bad_formatted_automations(hass, hass_client, setup_automation): }, {"id": "moon"}, ] + hass_config_store["automations.yaml"] = orig_data - def mock_read(path): - """Mock reading data.""" - return orig_data - - written = [] - - def mock_write(path, data): - """Mock writing data.""" - written.append(data) - - with patch("homeassistant.components.config._read", mock_read), patch( - "homeassistant.components.config._write", mock_write - ), patch("homeassistant.config.async_hass_config_yaml", return_value={}): - resp = await client.post( - "/api/config/automation/config/moon", - data=json.dumps({"trigger": [], "action": [], "condition": []}), - ) - await hass.async_block_till_done() + resp = await client.post( + "/api/config/automation/config/moon", + data=json.dumps({"trigger": [], "action": [], "condition": []}), + ) + await hass.async_block_till_done() + assert sorted(hass.states.async_entity_ids("automation")) == [ + "automation.automation_0" + ] assert resp.status == HTTPStatus.OK result = await resp.json() assert result == {"result": "ok"} - # Verify ID added to orig_data - assert "id" in orig_data[0] - - assert orig_data[1] == {"id": "moon", "trigger": [], "condition": [], "action": []} + # Verify ID added + new_data = hass_config_store["automations.yaml"] + assert "id" in new_data[0] + assert new_data[1] == {"id": "moon", "trigger": [], "condition": [], "action": []} @pytest.mark.parametrize( @@ -179,7 +166,9 @@ async def test_bad_formatted_automations(hass, hass_client, setup_automation): ], ), ) -async def test_delete_automation(hass, hass_client, setup_automation): +async def test_delete_automation( + hass: HomeAssistant, hass_client, hass_config_store, setup_automation +): """Test deleting an automation.""" ent_reg = er.async_get(hass) @@ -188,31 +177,27 @@ async def test_delete_automation(hass, hass_client, setup_automation): with patch.object(config, "SECTIONS", ["automation"]): assert await async_setup_component(hass, "config", {}) + assert sorted(hass.states.async_entity_ids("automation")) == [ + "automation.automation_0", + "automation.automation_1", + ] + client = await hass_client() orig_data = [{"id": "sun"}, {"id": "moon"}] + hass_config_store["automations.yaml"] = orig_data - def mock_read(path): - """Mock reading data.""" - return orig_data + resp = await client.delete("/api/config/automation/config/sun") + await hass.async_block_till_done() - written = [] - - def mock_write(path, data): - """Mock writing data.""" - written.append(data) - - with patch("homeassistant.components.config._read", mock_read), patch( - "homeassistant.components.config._write", mock_write - ), patch("homeassistant.config.async_hass_config_yaml", return_value={}): - resp = await client.delete("/api/config/automation/config/sun") - await hass.async_block_till_done() + assert sorted(hass.states.async_entity_ids("automation")) == [ + "automation.automation_1", + ] assert resp.status == HTTPStatus.OK result = await resp.json() assert result == {"result": "ok"} - assert len(written) == 1 - assert written[0][0]["id"] == "moon" + assert hass_config_store["automations.yaml"] == [{"id": "moon"}] assert len(ent_reg.entities) == 1 diff --git a/tests/components/config/test_scene.py b/tests/components/config/test_scene.py index 69f75cc5895..781e4000c25 100644 --- a/tests/components/config/test_scene.py +++ b/tests/components/config/test_scene.py @@ -1,14 +1,13 @@ """Test Automation config panel.""" from http import HTTPStatus import json -from unittest.mock import patch +from unittest.mock import ANY, patch import pytest from homeassistant.bootstrap import async_setup_component from homeassistant.components import config from homeassistant.helpers import entity_registry as er -from homeassistant.util.yaml import dump @pytest.fixture @@ -18,114 +17,98 @@ async def setup_scene(hass, scene_config): @pytest.mark.parametrize("scene_config", ({},)) -async def test_create_scene(hass, hass_client, setup_scene): +async def test_create_scene(hass, hass_client, hass_config_store, setup_scene): """Test creating a scene.""" with patch.object(config, "SECTIONS", ["scene"]): await async_setup_component(hass, "config", {}) + assert sorted(hass.states.async_entity_ids("scene")) == [] + client = await hass_client() - def mock_read(path): - """Mock reading data.""" - return None + orig_data = {} + hass_config_store["scenes.yaml"] = orig_data - written = [] + resp = await client.post( + "/api/config/scene/config/light_off", + data=json.dumps( + { + # "id": "light_off", # The id should be added when writing + "name": "Lights off", + "entities": {"light.bedroom": {"state": "off"}}, + } + ), + ) + await hass.async_block_till_done() - def mock_write(path, data): - """Mock writing data.""" - data = dump(data) - written.append(data) - - with patch("homeassistant.components.config._read", mock_read), patch( - "homeassistant.components.config._write", mock_write - ), patch("homeassistant.config.async_hass_config_yaml", return_value={}): - resp = await client.post( - "/api/config/scene/config/light_off", - data=json.dumps( - { - # "id": "light_off", - "name": "Lights off", - "entities": {"light.bedroom": {"state": "off"}}, - } - ), - ) + assert sorted(hass.states.async_entity_ids("scene")) == [ + "scene.lights_off", + ] assert resp.status == HTTPStatus.OK result = await resp.json() assert result == {"result": "ok"} - assert len(written) == 1 - written_yaml = written[0] - assert ( - written_yaml - == """- id: light_off - name: Lights off - entities: - light.bedroom: - state: 'off' -""" - ) + assert hass_config_store["scenes.yaml"] == [ + { + "id": "light_off", + "name": "Lights off", + "entities": {"light.bedroom": {"state": "off"}}, + } + ] @pytest.mark.parametrize("scene_config", ({},)) -async def test_update_scene(hass, hass_client, setup_scene): +async def test_update_scene(hass, hass_client, hass_config_store, setup_scene): """Test updating a scene.""" with patch.object(config, "SECTIONS", ["scene"]): await async_setup_component(hass, "config", {}) + assert sorted(hass.states.async_entity_ids("scene")) == [] + client = await hass_client() orig_data = [{"id": "light_on"}, {"id": "light_off"}] + hass_config_store["scenes.yaml"] = orig_data - def mock_read(path): - """Mock reading data.""" - return orig_data + resp = await client.post( + "/api/config/scene/config/light_off", + data=json.dumps( + { + "id": "light_off", + "name": "Lights off", + "entities": {"light.bedroom": {"state": "off"}}, + } + ), + ) + await hass.async_block_till_done() - written = [] - - def mock_write(path, data): - """Mock writing data.""" - data = dump(data) - written.append(data) - - with patch("homeassistant.components.config._read", mock_read), patch( - "homeassistant.components.config._write", mock_write - ), patch("homeassistant.config.async_hass_config_yaml", return_value={}): - resp = await client.post( - "/api/config/scene/config/light_off", - data=json.dumps( - { - "id": "light_off", - "name": "Lights off", - "entities": {"light.bedroom": {"state": "off"}}, - } - ), - ) + assert sorted(hass.states.async_entity_ids("scene")) == [ + "scene.lights_off", + ] assert resp.status == HTTPStatus.OK result = await resp.json() assert result == {"result": "ok"} - assert len(written) == 1 - written_yaml = written[0] - assert ( - written_yaml - == """- id: light_on -- id: light_off - name: Lights off - entities: - light.bedroom: - state: 'off' -""" - ) + assert hass_config_store["scenes.yaml"] == [ + {"id": "light_on"}, + { + "id": "light_off", + "name": "Lights off", + "entities": {"light.bedroom": {"state": "off"}}, + }, + ] @pytest.mark.parametrize("scene_config", ({},)) -async def test_bad_formatted_scene(hass, hass_client, setup_scene): +async def test_bad_formatted_scene(hass, hass_client, hass_config_store, setup_scene): """Test that we handle scene without ID.""" with patch.object(config, "SECTIONS", ["scene"]): await async_setup_component(hass, "config", {}) + assert sorted(hass.states.async_entity_ids("scene")) == [] + client = await hass_client() orig_data = [ @@ -135,43 +118,40 @@ async def test_bad_formatted_scene(hass, hass_client, setup_scene): }, {"id": "light_off"}, ] + hass_config_store["scenes.yaml"] = orig_data - def mock_read(path): - """Mock reading data.""" - return orig_data + resp = await client.post( + "/api/config/scene/config/light_off", + data=json.dumps( + { + "id": "light_off", + "name": "Lights off", + "entities": {"light.bedroom": {"state": "off"}}, + } + ), + ) + await hass.async_block_till_done() - written = [] - - def mock_write(path, data): - """Mock writing data.""" - written.append(data) - - with patch("homeassistant.components.config._read", mock_read), patch( - "homeassistant.components.config._write", mock_write - ), patch("homeassistant.config.async_hass_config_yaml", return_value={}): - resp = await client.post( - "/api/config/scene/config/light_off", - data=json.dumps( - { - "id": "light_off", - "name": "Lights off", - "entities": {"light.bedroom": {"state": "off"}}, - } - ), - ) + assert sorted(hass.states.async_entity_ids("scene")) == [ + "scene.lights_off", + ] assert resp.status == HTTPStatus.OK result = await resp.json() assert result == {"result": "ok"} # Verify ID added to orig_data - assert "id" in orig_data[0] - - assert orig_data[1] == { - "id": "light_off", - "name": "Lights off", - "entities": {"light.bedroom": {"state": "off"}}, - } + assert hass_config_store["scenes.yaml"] == [ + { + "id": ANY, + "entities": {"light.bedroom": "on"}, + }, + { + "id": "light_off", + "name": "Lights off", + "entities": {"light.bedroom": {"state": "off"}}, + }, + ] @pytest.mark.parametrize( @@ -183,7 +163,7 @@ async def test_bad_formatted_scene(hass, hass_client, setup_scene): ], ), ) -async def test_delete_scene(hass, hass_client, setup_scene): +async def test_delete_scene(hass, hass_client, hass_config_store, setup_scene): """Test deleting a scene.""" ent_reg = er.async_get(hass) @@ -192,31 +172,29 @@ async def test_delete_scene(hass, hass_client, setup_scene): with patch.object(config, "SECTIONS", ["scene"]): assert await async_setup_component(hass, "config", {}) + assert sorted(hass.states.async_entity_ids("scene")) == [ + "scene.light_off", + "scene.light_on", + ] + client = await hass_client() orig_data = [{"id": "light_on"}, {"id": "light_off"}] + hass_config_store["scenes.yaml"] = orig_data - def mock_read(path): - """Mock reading data.""" - return orig_data + resp = await client.delete("/api/config/scene/config/light_on") + await hass.async_block_till_done() - written = [] - - def mock_write(path, data): - """Mock writing data.""" - written.append(data) - - with patch("homeassistant.components.config._read", mock_read), patch( - "homeassistant.components.config._write", mock_write - ), patch("homeassistant.config.async_hass_config_yaml", return_value={}): - resp = await client.delete("/api/config/scene/config/light_on") - await hass.async_block_till_done() + assert sorted(hass.states.async_entity_ids("scene")) == [ + "scene.light_off", + ] assert resp.status == HTTPStatus.OK result = await resp.json() assert result == {"result": "ok"} - assert len(written) == 1 - assert written[0][0]["id"] == "light_off" + assert hass_config_store["scenes.yaml"] == [ + {"id": "light_off"}, + ] assert len(ent_reg.entities) == 1 diff --git a/tests/components/config/test_script.py b/tests/components/config/test_script.py index 4b6ca1bdc8f..b8e980c29b9 100644 --- a/tests/components/config/test_script.py +++ b/tests/components/config/test_script.py @@ -26,38 +26,35 @@ async def setup_script(hass, script_config, stub_blueprint_populate): # noqa: F }, ), ) -async def test_delete_script(hass, hass_client): +async def test_delete_script(hass, hass_client, hass_config_store): """Test deleting a script.""" with patch.object(config, "SECTIONS", ["script"]): await async_setup_component(hass, "config", {}) + assert sorted(hass.states.async_entity_ids("script")) == [ + "script.one", + "script.two", + ] + ent_reg = er.async_get(hass) assert len(ent_reg.entities) == 2 client = await hass_client() orig_data = {"one": {}, "two": {}} + hass_config_store["scripts.yaml"] = orig_data - def mock_read(path): - """Mock reading data.""" - return orig_data + resp = await client.delete("/api/config/script/config/two") + await hass.async_block_till_done() - written = [] - - def mock_write(path, data): - """Mock writing data.""" - written.append(data) - - with patch("homeassistant.components.config._read", mock_read), patch( - "homeassistant.components.config._write", mock_write - ): - resp = await client.delete("/api/config/script/config/two") + assert sorted(hass.states.async_entity_ids("script")) == [ + "script.one", + ] assert resp.status == HTTPStatus.OK result = await resp.json() assert result == {"result": "ok"} - assert len(written) == 1 - assert written[0] == {"one": {}} + assert hass_config_store["scripts.yaml"] == {"one": {}} assert len(ent_reg.entities) == 1 From 55444b15a94304f6fd2f62a2c28f10679fe4380c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 14 Nov 2022 12:41:49 +0100 Subject: [PATCH 0403/1033] Add type hints to hassfest models (#82074) * Add type hints to hassfest models * Implement review --- script/hassfest/model.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/script/hassfest/model.py b/script/hassfest/model.py index 65d3b1144e8..175885a53ec 100644 --- a/script/hassfest/model.py +++ b/script/hassfest/model.py @@ -4,6 +4,7 @@ from __future__ import annotations import importlib import json import pathlib +from types import ModuleType from typing import Any import attr @@ -44,10 +45,10 @@ class Brand: """Represent a brand in our validator.""" @classmethod - def load_dir(cls, path: pathlib.Path, config: Config): + def load_dir(cls, path: pathlib.Path, config: Config) -> dict[str, Brand]: """Load all brands in a directory.""" assert path.is_dir() - brands = {} + brands: dict[str, Brand] = {} for fil in path.iterdir(): brand = cls(fil) brand.load_brand(config) @@ -66,16 +67,19 @@ class Brand: @property def name(self) -> str | None: """Return name of the integration.""" + assert self.brand is not None, "brand has not been loaded" return self.brand.get("name") @property def integrations(self) -> list[str]: """Return the sub integrations of this brand.""" - return self.brand.get("integrations") + assert self.brand is not None, "brand has not been loaded" + return self.brand.get("integrations", []) @property def iot_standards(self) -> list[str]: """Return list of supported IoT standards.""" + assert self.brand is not None, "brand has not been loaded" return self.brand.get("iot_standards", []) def load_brand(self, config: Config) -> None: @@ -85,7 +89,7 @@ class Brand: return try: - brand = json.loads(self.path.read_text()) + brand: dict[str, Any] = json.loads(self.path.read_text()) except ValueError as err: config.add_error( "model", f"Brand file {self.path.name} contains invalid JSON: {err}" @@ -100,10 +104,10 @@ class Integration: """Represent an integration in our validator.""" @classmethod - def load_dir(cls, path: pathlib.Path): + def load_dir(cls, path: pathlib.Path) -> dict[str, Integration]: """Load all integrations in a directory.""" assert path.is_dir() - integrations = {} + integrations: dict[str, Integration] = {} for fil in path.iterdir(): if fil.is_file() or fil.name == "__pycache__": continue @@ -143,51 +147,62 @@ class Integration: @property def disabled(self) -> str | None: """Return if integration is disabled.""" + assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("disabled") @property def name(self) -> str: """Return name of the integration.""" - return self.manifest["name"] + assert self.manifest is not None, "manifest has not been loaded" + name: str = self.manifest["name"] + return name @property - def quality_scale(self) -> str: + def quality_scale(self) -> str | None: """Return quality scale of the integration.""" + assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("quality_scale") @property def config_flow(self) -> bool: """Return if the integration has a config flow.""" + assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("config_flow", False) @property def requirements(self) -> list[str]: """List of requirements.""" + assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("requirements", []) @property def dependencies(self) -> list[str]: """List of dependencies.""" + assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("dependencies", []) @property def supported_by(self) -> str: """Return the integration supported by this virtual integration.""" + assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("supported_by", {}) @property def integration_type(self) -> str: """Get integration_type.""" + assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("integration_type", "hub") @property def iot_class(self) -> str | None: """Return the integration IoT Class.""" + assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("iot_class") @property def iot_standards(self) -> list[str]: """Return the IoT standard supported by this virtual integration.""" + assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("iot_standards", []) def add_error(self, *args: Any, **kwargs: Any) -> None: @@ -206,14 +221,14 @@ class Integration: return try: - manifest = json.loads(manifest_path.read_text()) + manifest: dict[str, Any] = json.loads(manifest_path.read_text()) except ValueError as err: self.add_error("model", f"Manifest contains invalid JSON: {err}") return self.manifest = manifest - def import_pkg(self, platform=None): + def import_pkg(self, platform: str | None = None) -> ModuleType: """Import the Python file.""" pkg = f"homeassistant.components.{self.domain}" if platform is not None: From e9117cd1db182bd19ec08eb6de4a55d315164474 Mon Sep 17 00:00:00 2001 From: Pascal Reeb Date: Mon, 14 Nov 2022 12:43:24 +0000 Subject: [PATCH 0404/1033] Update pynuki to 1.6.0 (#81208) --- homeassistant/components/nuki/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nuki/manifest.json b/homeassistant/components/nuki/manifest.json index 8a9b7c506b4..de8edd6af40 100644 --- a/homeassistant/components/nuki/manifest.json +++ b/homeassistant/components/nuki/manifest.json @@ -2,7 +2,7 @@ "domain": "nuki", "name": "Nuki", "documentation": "https://www.home-assistant.io/integrations/nuki", - "requirements": ["pynuki==1.5.2"], + "requirements": ["pynuki==1.6.0"], "codeowners": ["@pschmitt", "@pvizeli", "@pree"], "config_flow": true, "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index af6ec213546..1f26bef4f6a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1767,7 +1767,7 @@ pynina==0.1.8 pynobo==1.6.0 # homeassistant.components.nuki -pynuki==1.5.2 +pynuki==1.6.0 # homeassistant.components.nut pynut2==2.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9496175043d..672785b69a2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1253,7 +1253,7 @@ pynina==0.1.8 pynobo==1.6.0 # homeassistant.components.nuki -pynuki==1.5.2 +pynuki==1.6.0 # homeassistant.components.nut pynut2==2.1.2 From 1826795d3771f08c86d66e4effebe463e07179e3 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 14 Nov 2022 14:32:10 +0100 Subject: [PATCH 0405/1033] Add TagProtocol for type checking (#81086) * Add TagProtocol for type checking * Adjust type hints --- homeassistant/components/esphome/__init__.py | 7 +++++-- homeassistant/components/tag/__init__.py | 15 ++++++++++++++- homeassistant/components/tag/trigger.py | 8 ++++++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index 23b6a6550e4..ea909725b9f 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -5,7 +5,7 @@ from collections.abc import Callable import functools import logging import math -from typing import Any, Generic, NamedTuple, TypeVar, cast, overload +from typing import TYPE_CHECKING, Any, Generic, NamedTuple, TypeVar, cast, overload from aioesphomeapi import ( APIClient, @@ -55,6 +55,9 @@ from .domain_data import DOMAIN, DomainData # Import config flow so that it's added to the registry from .entry_data import RuntimeEntryData +if TYPE_CHECKING: + from homeassistant.components.tag import TagProtocol + CONF_NOISE_PSK = "noise_psk" _LOGGER = logging.getLogger(__name__) _R = TypeVar("_R") @@ -133,7 +136,7 @@ async def async_setup_entry( # noqa: C901 if service_name == "tag_scanned" and device_id is not None: # Importing tag via hass.components in case it is overridden # in a custom_components (custom_components.tag) - tag = hass.components.tag + tag: TagProtocol = hass.components.tag tag_id = service_data["tag_id"] hass.async_create_task(tag.async_scan_tag(tag_id, device_id)) return diff --git a/homeassistant/components/tag/__init__.py b/homeassistant/components/tag/__init__.py index c05b4416343..1b3ee9b646b 100644 --- a/homeassistant/components/tag/__init__.py +++ b/homeassistant/components/tag/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations import logging +from typing import Protocol import uuid import voluptuous as vol @@ -106,9 +107,21 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True +class TagProtocol(Protocol): + """Protocol for type checking.""" + + async def async_scan_tag( + self, tag_id: str, device_id: str | None, context: Context | None = None + ) -> None: + """Handle when a tag is scanned.""" + + @bind_hass async def async_scan_tag( - hass: HomeAssistant, tag_id: str, device_id: str, context: Context | None = None + hass: HomeAssistant, + tag_id: str, + device_id: str | None, + context: Context | None = None, ) -> None: """Handle when a tag is scanned.""" if DOMAIN not in hass.config.components: diff --git a/homeassistant/components/tag/trigger.py b/homeassistant/components/tag/trigger.py index 146521dfba9..b6d77737eab 100644 --- a/homeassistant/components/tag/trigger.py +++ b/homeassistant/components/tag/trigger.py @@ -1,4 +1,6 @@ """Support for tag triggers.""" +from __future__ import annotations + import voluptuous as vol from homeassistant.const import CONF_PLATFORM @@ -26,8 +28,10 @@ async def async_attach_trigger( ) -> CALLBACK_TYPE: """Listen for tag_scanned events based on configuration.""" trigger_data = trigger_info["trigger_data"] - tag_ids = set(config[TAG_ID]) - device_ids = set(config[DEVICE_ID]) if DEVICE_ID in config else None + tag_ids: set[str] = set(config[TAG_ID]) + device_ids: set[str] | None = ( + set(config[DEVICE_ID]) if DEVICE_ID in config else None + ) job = HassJob(action) From fa9a51e528125d118afbf74e58ec4e3eda88de7c Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 14 Nov 2022 16:21:35 +0200 Subject: [PATCH 0406/1033] Use os.path.dirname() for getting the directory name of a path (#81504) --- homeassistant/util/file.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/util/file.py b/homeassistant/util/file.py index cb5969b3079..06471eaca6a 100644 --- a/homeassistant/util/file.py +++ b/homeassistant/util/file.py @@ -54,11 +54,10 @@ def write_utf8_file( """ tmp_filename = "" - tmp_path = os.path.split(filename)[0] try: # Modern versions of Python tempfile create this file with mode 0o600 with tempfile.NamedTemporaryFile( - mode="w", encoding="utf-8", dir=tmp_path, delete=False + mode="w", encoding="utf-8", dir=os.path.dirname(filename), delete=False ) as fdesc: fdesc.write(utf8_data) tmp_filename = fdesc.name From 7d5794cfb1b1d39e95c9fcab53a6b1851a0fc7df Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 14 Nov 2022 16:18:11 +0100 Subject: [PATCH 0407/1033] Remove unused function from hassfest model (#82073) * Remove unused function from hassfest model * Update model.py Co-authored-by: J. Nick Koston --- script/hassfest/model.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/script/hassfest/model.py b/script/hassfest/model.py index 175885a53ec..6b500daa9b3 100644 --- a/script/hassfest/model.py +++ b/script/hassfest/model.py @@ -1,10 +1,8 @@ """Models for manifest validator.""" from __future__ import annotations -import importlib import json import pathlib -from types import ModuleType from typing import Any import attr @@ -227,10 +225,3 @@ class Integration: return self.manifest = manifest - - def import_pkg(self, platform: str | None = None) -> ModuleType: - """Import the Python file.""" - pkg = f"homeassistant.components.{self.domain}" - if platform is not None: - pkg += f".{platform}" - return importlib.import_module(pkg) From bbda122c9933885a3de7df98fe9c4c2cc72f5f12 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 14 Nov 2022 17:22:23 +0200 Subject: [PATCH 0408/1033] Fix mqtt typo (#82086) mqtt: fix "certiticate" typo --- homeassistant/components/mqtt/config_flow.py | 18 +++++++++--------- homeassistant/components/mqtt/strings.json | 2 +- .../components/mqtt/translations/en.json | 6 +++--- homeassistant/components/mqtt/util.py | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/mqtt/config_flow.py b/homeassistant/components/mqtt/config_flow.py index 5d8b19ce31a..5e5beb2f314 100644 --- a/homeassistant/components/mqtt/config_flow.py +++ b/homeassistant/components/mqtt/config_flow.py @@ -482,8 +482,8 @@ async def async_get_broker_settings( return False certificate_id: str | None = user_input.get(CONF_CERTIFICATE) if certificate_id: - with process_uploaded_file(hass, certificate_id) as certiticate_file: - certificate = certiticate_file.read_text(encoding=DEFAULT_ENCODING) + with process_uploaded_file(hass, certificate_id) as certificate_file: + certificate = certificate_file.read_text(encoding=DEFAULT_ENCODING) # Return to form for file upload CA cert or client cert and key if ( @@ -499,8 +499,8 @@ async def async_get_broker_settings( if client_certificate_id: with process_uploaded_file( hass, client_certificate_id - ) as client_certiticate_file: - client_certificate = client_certiticate_file.read_text( + ) as client_certificate_file: + client_certificate = client_certificate_file.read_text( encoding=DEFAULT_ENCODING ) if client_key_id: @@ -704,10 +704,10 @@ def try_connection( def check_certicate_chain() -> str | None: """Check the MQTT certificates.""" - if client_certiticate := get_file_path(CONF_CLIENT_CERT): + if client_certificate := get_file_path(CONF_CLIENT_CERT): try: - with open(client_certiticate, "rb") as client_certiticate_file: - load_pem_x509_certificate(client_certiticate_file.read()) + with open(client_certificate, "rb") as client_certificate_file: + load_pem_x509_certificate(client_certificate_file.read()) except ValueError: return "bad_client_cert" # Check we can serialize the private key file @@ -719,9 +719,9 @@ def check_certicate_chain() -> str | None: return "bad_client_key" # Check the certificate chain context = SSLContext(PROTOCOL_TLS) - if client_certiticate and private_key: + if client_certificate and private_key: try: - context.load_cert_chain(client_certiticate, private_key) + context.load_cert_chain(client_certificate, private_key) except SSLError: return "bad_client_cert_key" # try to load the custom CA file diff --git a/homeassistant/components/mqtt/strings.json b/homeassistant/components/mqtt/strings.json index 8d3a4ad3445..5788597e9a9 100644 --- a/homeassistant/components/mqtt/strings.json +++ b/homeassistant/components/mqtt/strings.json @@ -47,7 +47,7 @@ "bad_will": "Invalid will topic", "bad_discovery_prefix": "Invalid discovery prefix", "bad_certificate": "The CA certificate is invalid", - "bad_client_cert": "Invalid client certiticate, ensure a PEM coded file is supplied", + "bad_client_cert": "Invalid client certificate, ensure a PEM coded file is supplied", "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", "bad_client_cert_key": "Client certificate and private are no valid pair", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", diff --git a/homeassistant/components/mqtt/translations/en.json b/homeassistant/components/mqtt/translations/en.json index 74ecf3ccb11..00078b1082a 100644 --- a/homeassistant/components/mqtt/translations/en.json +++ b/homeassistant/components/mqtt/translations/en.json @@ -7,7 +7,7 @@ "error": { "bad_birth": "Invalid birth topic", "bad_certificate": "The CA certificate is invalid", - "bad_client_cert": "Invalid client certiticate, ensure a PEM coded file is supplied", + "bad_client_cert": "Invalid client certificate, ensure a PEM coded file is supplied", "bad_client_cert_key": "Client certificate and private are no valid pair", "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", "bad_discovery_prefix": "Invalid discovery prefix", @@ -81,7 +81,7 @@ "error": { "bad_birth": "Invalid birth topic", "bad_certificate": "The CA certificate is invalid", - "bad_client_cert": "Invalid client certiticate, ensure a PEM coded file is supplied", + "bad_client_cert": "Invalid client certificate, ensure a PEM coded file is supplied", "bad_client_cert_key": "Client certificate and private are no valid pair", "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", "bad_discovery_prefix": "Invalid discovery prefix", @@ -130,4 +130,4 @@ } } } -} \ No newline at end of file +} diff --git a/homeassistant/components/mqtt/util.py b/homeassistant/components/mqtt/util.py index ab907854499..bfd961871d3 100644 --- a/homeassistant/components/mqtt/util.py +++ b/homeassistant/components/mqtt/util.py @@ -191,7 +191,7 @@ def migrate_certificate_file_to_content(file_name_or_auto: str) -> str | None: if file_name_or_auto == "auto": return "auto" try: - with open(file_name_or_auto, encoding=DEFAULT_ENCODING) as certiticate_file: - return certiticate_file.read() + with open(file_name_or_auto, encoding=DEFAULT_ENCODING) as certificate_file: + return certificate_file.read() except OSError: return None From 1ded3ac51ebc1915a5026af1998eb119972f6117 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Nov 2022 10:54:28 -0600 Subject: [PATCH 0409/1033] Poll HomeKit Controller locks for state after lock operation (#82058) --- .../homekit_controller/connection.py | 23 +++++++++++++++++-- .../components/homekit_controller/const.py | 7 ++++++ .../components/homekit_controller/entity.py | 4 ++++ .../components/homekit_controller/lock.py | 4 ++++ tests/components/homekit_controller/common.py | 3 +++ 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/homekit_controller/connection.py b/homeassistant/components/homekit_controller/connection.py index 3df5ab8aaed..f158cd49e9c 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -3,7 +3,7 @@ from __future__ import annotations import asyncio from collections.abc import Callable, Iterable -from datetime import timedelta +from datetime import datetime, timedelta import logging from types import MappingProxyType from typing import Any @@ -22,6 +22,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_VIA_DEVICE, EVENT_HOMEASSISTANT_STARTED from homeassistant.core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.event import async_track_time_interval @@ -29,6 +30,7 @@ from homeassistant.helpers.event import async_track_time_interval from .const import ( CHARACTERISTIC_PLATFORMS, CONTROLLER, + DEBOUNCE_COOLDOWN, DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, IDENTIFIER_ACCESSORY_ID, @@ -41,6 +43,8 @@ from .device_trigger import async_fire_triggers, async_setup_triggers_for_entry RETRY_INTERVAL = 60 # seconds MAX_POLL_FAILURES_TO_DECLARE_UNAVAILABLE = 3 + + BLE_AVAILABILITY_CHECK_INTERVAL = 1800 # seconds _LOGGER = logging.getLogger(__name__) @@ -127,6 +131,14 @@ class HKDevice: self.watchable_characteristics: list[tuple[int, int]] = [] + self._debounced_update = Debouncer( + hass, + _LOGGER, + cooldown=DEBOUNCE_COOLDOWN, + immediate=False, + function=self.async_update, + ) + @property def entity_map(self) -> Accessories: """Return the accessories from the pairing.""" @@ -240,8 +252,11 @@ class HKDevice: self.async_set_available_state(self.pairing.is_available) + # We use async_request_update to avoid multiple updates + # at the same time which would generate a spurious warning + # in the log about concurrent polling. self._polling_interval_remover = async_track_time_interval( - self.hass, self.async_update, self.pairing.poll_interval + self.hass, self.async_request_update, self.pairing.poll_interval ) if transport == Transport.BLE: @@ -631,6 +646,10 @@ class HKDevice: """Update the available state of the device.""" self.async_set_available_state(self.pairing.is_available) + async def async_request_update(self, now: datetime | None = None) -> None: + """Request an debounced update from the accessory.""" + await self._debounced_update.async_call() + async def async_update(self, now=None): """Poll state of all entities attached to this bridge/accessory.""" if not self.pollable_characteristics: diff --git a/homeassistant/components/homekit_controller/const.py b/homeassistant/components/homekit_controller/const.py index 8c7db4dad00..7ed844ae9ae 100644 --- a/homeassistant/components/homekit_controller/const.py +++ b/homeassistant/components/homekit_controller/const.py @@ -107,3 +107,10 @@ STARTUP_EXCEPTIONS = ( EncryptionError, AccessoryDisconnectedError, ) + +# 10 seconds was chosen because its soon enough +# for most state changes to happen but not too +# long that the BLE connection is dropped. It +# also happens to be the same value used by +# the update coordinator. +DEBOUNCE_COOLDOWN = 10 # seconds diff --git a/homeassistant/components/homekit_controller/entity.py b/homeassistant/components/homekit_controller/entity.py index a4e1b2b41b3..1c492decf49 100644 --- a/homeassistant/components/homekit_controller/entity.py +++ b/homeassistant/components/homekit_controller/entity.py @@ -175,6 +175,10 @@ class HomeKitEntity(Entity): """Define the homekit characteristics the entity cares about.""" raise NotImplementedError + async def async_update(self) -> None: + """Update the entity.""" + await self._accessory.async_request_update() + class AccessoryEntity(HomeKitEntity): """A HomeKit entity that is related to an entire accessory rather than a specific service or characteristic.""" diff --git a/homeassistant/components/homekit_controller/lock.py b/homeassistant/components/homekit_controller/lock.py index a6c8a3672a3..df03a1fef34 100644 --- a/homeassistant/components/homekit_controller/lock.py +++ b/homeassistant/components/homekit_controller/lock.py @@ -124,6 +124,10 @@ class HomeKitLock(HomeKitEntity, LockEntity): await self.async_put_characteristics( {CharacteristicsTypes.LOCK_MECHANISM_TARGET_STATE: TARGET_STATE_MAP[state]} ) + # Some locks need to be polled to update the current state + # after a target state change. + # https://github.com/home-assistant/core/issues/81887 + await self._accessory.async_request_update() @property def extra_state_attributes(self) -> dict[str, Any]: diff --git a/tests/components/homekit_controller/common.py b/tests/components/homekit_controller/common.py index b30ba6236a9..ec30d541a93 100644 --- a/tests/components/homekit_controller/common.py +++ b/tests/components/homekit_controller/common.py @@ -21,6 +21,7 @@ from aiohomekit.zeroconf import HomeKitService from homeassistant.components.device_automation import DeviceAutomationType from homeassistant.components.homekit_controller.const import ( CONTROLLER, + DEBOUNCE_COOLDOWN, DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, IDENTIFIER_ACCESSORY_ID, @@ -146,6 +147,7 @@ class Helper: # If they are enabled, then HA will pick up the changes next time # we yield control await time_changed(self.hass, 60) + await time_changed(self.hass, DEBOUNCE_COOLDOWN) await self.hass.async_block_till_done() @@ -165,6 +167,7 @@ class Helper: async def poll_and_get_state(self) -> State: """Trigger a time based poll and return the current entity state.""" await time_changed(self.hass, 60) + await time_changed(self.hass, DEBOUNCE_COOLDOWN) state = self.hass.states.get(self.entity_id) assert state is not None From af73afa2e2965a8d8b20461503c8111b31719cee Mon Sep 17 00:00:00 2001 From: Ernst Klamer Date: Mon, 14 Nov 2022 18:18:51 +0100 Subject: [PATCH 0410/1033] Add support for HHCCJCY10 to xiaomi_ble (#82069) --- .../components/xiaomi_ble/manifest.json | 6 +- homeassistant/components/xiaomi_ble/sensor.py | 71 ++++++++++--------- homeassistant/generated/bluetooth.py | 5 ++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/xiaomi_ble/__init__.py | 14 ++++ tests/components/xiaomi_ble/test_sensor.py | 57 ++++++++++++++- 7 files changed, 121 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/xiaomi_ble/manifest.json b/homeassistant/components/xiaomi_ble/manifest.json index 56efd9e966a..3c66e8bc0ca 100644 --- a/homeassistant/components/xiaomi_ble/manifest.json +++ b/homeassistant/components/xiaomi_ble/manifest.json @@ -4,12 +4,16 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/xiaomi_ble", "bluetooth": [ + { + "connectable": false, + "service_data_uuid": "0000fd50-0000-1000-8000-00805f9b34fb" + }, { "connectable": false, "service_data_uuid": "0000fe95-0000-1000-8000-00805f9b34fb" } ], - "requirements": ["xiaomi-ble==0.10.0"], + "requirements": ["xiaomi-ble==0.12.1"], "dependencies": ["bluetooth"], "codeowners": ["@Jc2k", "@Ernst79"], "iot_class": "local_push" diff --git a/homeassistant/components/xiaomi_ble/sensor.py b/homeassistant/components/xiaomi_ble/sensor.py index 0e7e05a3a94..033d6b7daf0 100644 --- a/homeassistant/components/xiaomi_ble/sensor.py +++ b/homeassistant/components/xiaomi_ble/sensor.py @@ -36,10 +36,25 @@ from .const import DOMAIN from .device import device_key_to_bluetooth_entity_key, sensor_device_info_to_hass SENSOR_DESCRIPTIONS = { - (DeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( - key=f"{DeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", - device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + (DeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription( + key=f"{DeviceClass.BATTERY}_{Units.PERCENTAGE}", + device_class=SensorDeviceClass.BATTERY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + ), + (DeviceClass.CONDUCTIVITY, Units.CONDUCTIVITY): SensorEntityDescription( + key=str(Units.CONDUCTIVITY), + device_class=None, + native_unit_of_measurement=CONDUCTIVITY, + state_class=SensorStateClass.MEASUREMENT, + ), + ( + DeviceClass.FORMALDEHYDE, + Units.CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER, + ): SensorEntityDescription( + key=f"{DeviceClass.FORMALDEHYDE}_{Units.CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER}", + native_unit_of_measurement=CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER, state_class=SensorStateClass.MEASUREMENT, ), (DeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( @@ -54,26 +69,18 @@ SENSOR_DESCRIPTIONS = { native_unit_of_measurement=LIGHT_LUX, state_class=SensorStateClass.MEASUREMENT, ), + (DeviceClass.MOISTURE, Units.PERCENTAGE): SensorEntityDescription( + key=f"{DeviceClass.MOISTURE}_{Units.PERCENTAGE}", + device_class=SensorDeviceClass.MOISTURE, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), (DeviceClass.PRESSURE, Units.PRESSURE_MBAR): SensorEntityDescription( key=f"{DeviceClass.PRESSURE}_{Units.PRESSURE_MBAR}", device_class=SensorDeviceClass.PRESSURE, native_unit_of_measurement=PRESSURE_MBAR, state_class=SensorStateClass.MEASUREMENT, ), - (DeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription( - key=f"{DeviceClass.BATTERY}_{Units.PERCENTAGE}", - device_class=SensorDeviceClass.BATTERY, - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - ), - (DeviceClass.VOLTAGE, Units.ELECTRIC_POTENTIAL_VOLT): SensorEntityDescription( - key=str(Units.ELECTRIC_POTENTIAL_VOLT), - device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - ), ( DeviceClass.SIGNAL_STRENGTH, Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT, @@ -85,26 +92,26 @@ SENSOR_DESCRIPTIONS = { entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, ), - # Used for e.g. moisture sensor on HHCCJCY01 + (DeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( + key=f"{DeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=TEMP_CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + (DeviceClass.VOLTAGE, Units.ELECTRIC_POTENTIAL_VOLT): SensorEntityDescription( + key=f"{DeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_VOLT}", + device_class=SensorDeviceClass.VOLTAGE, + native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + ), + # Used for e.g. consumable sensor on WX08ZM (None, Units.PERCENTAGE): SensorEntityDescription( key=str(Units.PERCENTAGE), device_class=None, native_unit_of_measurement=PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, ), - # Used for e.g. conductivity sensor on HHCCJCY01 - (None, Units.CONDUCTIVITY): SensorEntityDescription( - key=str(Units.CONDUCTIVITY), - device_class=None, - native_unit_of_measurement=CONDUCTIVITY, - state_class=SensorStateClass.MEASUREMENT, - ), - # Used for e.g. formaldehyde - (None, Units.CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER): SensorEntityDescription( - key=str(Units.CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER), - native_unit_of_measurement=CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER, - state_class=SensorStateClass.MEASUREMENT, - ), } diff --git a/homeassistant/generated/bluetooth.py b/homeassistant/generated/bluetooth.py index 06ebbfbb515..017135e9d10 100644 --- a/homeassistant/generated/bluetooth.py +++ b/homeassistant/generated/bluetooth.py @@ -384,6 +384,11 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ ], "manufacturer_id": 76, }, + { + "connectable": False, + "domain": "xiaomi_ble", + "service_data_uuid": "0000fd50-0000-1000-8000-00805f9b34fb", + }, { "connectable": False, "domain": "xiaomi_ble", diff --git a/requirements_all.txt b/requirements_all.txt index 1f26bef4f6a..f2060fa568d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2576,7 +2576,7 @@ xbox-webapi==2.0.11 xboxapi==2.0.1 # homeassistant.components.xiaomi_ble -xiaomi-ble==0.10.0 +xiaomi-ble==0.12.1 # homeassistant.components.knx xknx==1.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 672785b69a2..367acc0eb86 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1789,7 +1789,7 @@ wolf_smartset==0.1.11 xbox-webapi==2.0.11 # homeassistant.components.xiaomi_ble -xiaomi-ble==0.10.0 +xiaomi-ble==0.12.1 # homeassistant.components.knx xknx==1.2.0 diff --git a/tests/components/xiaomi_ble/__init__.py b/tests/components/xiaomi_ble/__init__.py index ab88cc559b7..a6d66ecf6df 100644 --- a/tests/components/xiaomi_ble/__init__.py +++ b/tests/components/xiaomi_ble/__init__.py @@ -84,6 +84,20 @@ YLKG07YL_SERVICE_INFO = BluetoothServiceInfoBleak( connectable=False, ) +HHCCJCY10_SERVICE_INFO = BluetoothServiceInfoBleak( + name="HHCCJCY10", + address="DC:23:4D:E5:5B:FC", + device=BLEDevice("00:00:00:00:00:00", None), + rssi=-56, + manufacturer_data={}, + service_data={"0000fd50-0000-1000-8000-00805f9b34fb": b"\x0e\x00n\x014\xa4(\x00["}, + service_uuids=["0000fd50-0000-1000-8000-00805f9b34fb"], + source="local", + advertisement=generate_advertisement_data(local_name="Not it"), + time=0, + connectable=False, +) + MISSING_PAYLOAD_ENCRYPTED = BluetoothServiceInfoBleak( name="LYWSD02MMC", address="A4:C1:38:56:53:84", diff --git a/tests/components/xiaomi_ble/test_sensor.py b/tests/components/xiaomi_ble/test_sensor.py index 17f9254b5ff..25f15938c6c 100644 --- a/tests/components/xiaomi_ble/test_sensor.py +++ b/tests/components/xiaomi_ble/test_sensor.py @@ -5,7 +5,7 @@ from homeassistant.components.sensor import ATTR_STATE_CLASS from homeassistant.components.xiaomi_ble.const import DOMAIN from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT -from . import MMC_T201_1_SERVICE_INFO, make_advertisement +from . import HHCCJCY10_SERVICE_INFO, MMC_T201_1_SERVICE_INFO, make_advertisement from tests.common import MockConfigEntry from tests.components.bluetooth import inject_bluetooth_service_info_bleak @@ -433,3 +433,58 @@ async def test_xiaomi_CGDK2(hass): assert await hass.config_entries.async_unload(entry.entry_id) await hass.async_block_till_done() + + +async def test_hhcc_HHCCJCY10(hass): + """This device used a different UUID compared to the other Xiaomi sensors.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="DC:23:4D:E5:5B:FC", + ) + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + inject_bluetooth_service_info_bleak(hass, HHCCJCY10_SERVICE_INFO) + + await hass.async_block_till_done() + assert len(hass.states.async_all()) == 5 + + temp_sensor = hass.states.get("sensor.plant_sensor_5bfc_temperature") + temp_sensor_attr = temp_sensor.attributes + assert temp_sensor.state == "11.0" + assert temp_sensor_attr[ATTR_FRIENDLY_NAME] == "Plant Sensor 5BFC Temperature" + assert temp_sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == "°C" + assert temp_sensor_attr[ATTR_STATE_CLASS] == "measurement" + + illu_sensor = hass.states.get("sensor.plant_sensor_5bfc_illuminance") + illu_sensor_attr = illu_sensor.attributes + assert illu_sensor.state == "79012" + assert illu_sensor_attr[ATTR_FRIENDLY_NAME] == "Plant Sensor 5BFC Illuminance" + assert illu_sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == "lx" + assert illu_sensor_attr[ATTR_STATE_CLASS] == "measurement" + + cond_sensor = hass.states.get("sensor.plant_sensor_5bfc_conductivity") + cond_sensor_attr = cond_sensor.attributes + assert cond_sensor.state == "91" + assert cond_sensor_attr[ATTR_FRIENDLY_NAME] == "Plant Sensor 5BFC Conductivity" + assert cond_sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == "µS/cm" + assert cond_sensor_attr[ATTR_STATE_CLASS] == "measurement" + + moist_sensor = hass.states.get("sensor.plant_sensor_5bfc_moisture") + moist_sensor_attr = moist_sensor.attributes + assert moist_sensor.state == "14" + assert moist_sensor_attr[ATTR_FRIENDLY_NAME] == "Plant Sensor 5BFC Moisture" + assert moist_sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == "%" + assert moist_sensor_attr[ATTR_STATE_CLASS] == "measurement" + + bat_sensor = hass.states.get("sensor.plant_sensor_5bfc_battery") + bat_sensor_attr = bat_sensor.attributes + assert bat_sensor.state == "40" + assert bat_sensor_attr[ATTR_FRIENDLY_NAME] == "Plant Sensor 5BFC Battery" + assert bat_sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == "%" + assert bat_sensor_attr[ATTR_STATE_CLASS] == "measurement" + + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() From 3d29638804077662b87d4d295f4af8e0b4930b99 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 14 Nov 2022 20:12:53 +0200 Subject: [PATCH 0411/1033] Deduplicate `sensor_device_info_to_device_info` (#81905) Co-authored-by: J. Nick Koston --- .../components/bluemaestro/device.py | 18 +----------- .../components/bluemaestro/sensor.py | 5 ++-- .../components/bthome/binary_sensor.py | 5 ++-- homeassistant/components/bthome/device.py | 18 +----------- homeassistant/components/bthome/sensor.py | 5 ++-- homeassistant/components/govee_ble/sensor.py | 23 ++------------- homeassistant/components/inkbird/sensor.py | 23 ++------------- homeassistant/components/kegtron/device.py | 18 +----------- homeassistant/components/kegtron/sensor.py | 5 ++-- homeassistant/components/moat/sensor.py | 23 ++------------- homeassistant/components/oralb/device.py | 18 +----------- homeassistant/components/oralb/sensor.py | 5 ++-- .../components/qingping/binary_sensor.py | 5 ++-- homeassistant/components/qingping/device.py | 18 +----------- homeassistant/components/qingping/sensor.py | 5 ++-- .../components/ruuvitag_ble/sensor.py | 19 ++----------- homeassistant/components/sensorpro/device.py | 18 +----------- homeassistant/components/sensorpro/sensor.py | 5 ++-- homeassistant/components/sensorpush/sensor.py | 23 ++------------- .../components/thermobeacon/device.py | 18 +----------- .../components/thermobeacon/sensor.py | 5 ++-- homeassistant/components/thermopro/sensor.py | 22 ++------------- homeassistant/components/tilt_ble/sensor.py | 28 +++---------------- .../components/xiaomi_ble/binary_sensor.py | 5 ++-- homeassistant/components/xiaomi_ble/device.py | 18 +----------- homeassistant/components/xiaomi_ble/sensor.py | 5 ++-- homeassistant/helpers/sensor.py | 28 +++++++++++++++++++ 27 files changed, 89 insertions(+), 299 deletions(-) create mode 100644 homeassistant/helpers/sensor.py diff --git a/homeassistant/components/bluemaestro/device.py b/homeassistant/components/bluemaestro/device.py index 3d6e4546882..19d955dd945 100644 --- a/homeassistant/components/bluemaestro/device.py +++ b/homeassistant/components/bluemaestro/device.py @@ -1,13 +1,11 @@ """Support for BlueMaestro devices.""" from __future__ import annotations -from bluemaestro_ble import DeviceKey, SensorDeviceInfo +from bluemaestro_ble import DeviceKey from homeassistant.components.bluetooth.passive_update_processor import ( PassiveBluetoothEntityKey, ) -from homeassistant.const import ATTR_MANUFACTURER, ATTR_MODEL, ATTR_NAME -from homeassistant.helpers.entity import DeviceInfo def device_key_to_bluetooth_entity_key( @@ -15,17 +13,3 @@ def device_key_to_bluetooth_entity_key( ) -> PassiveBluetoothEntityKey: """Convert a device key to an entity key.""" return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) - - -def sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a bluemaestro device info to a sensor device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info diff --git a/homeassistant/components/bluemaestro/sensor.py b/homeassistant/components/bluemaestro/sensor.py index 8afdef48d51..7fff348d587 100644 --- a/homeassistant/components/bluemaestro/sensor.py +++ b/homeassistant/components/bluemaestro/sensor.py @@ -31,9 +31,10 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN -from .device import device_key_to_bluetooth_entity_key, sensor_device_info_to_hass +from .device import device_key_to_bluetooth_entity_key SENSOR_DESCRIPTIONS = { (BlueMaestroSensorDeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription( @@ -96,7 +97,7 @@ def sensor_update_to_bluetooth_data_update( """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/bthome/binary_sensor.py b/homeassistant/components/bthome/binary_sensor.py index a048f9202b6..bbd77f271a4 100644 --- a/homeassistant/components/bthome/binary_sensor.py +++ b/homeassistant/components/bthome/binary_sensor.py @@ -22,9 +22,10 @@ from homeassistant.components.bluetooth.passive_update_processor import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN -from .device import device_key_to_bluetooth_entity_key, sensor_device_info_to_hass +from .device import device_key_to_bluetooth_entity_key BINARY_SENSOR_DESCRIPTIONS = { BTHomeBinarySensorDeviceClass.BATTERY: BinarySensorEntityDescription( @@ -147,7 +148,7 @@ def sensor_update_to_bluetooth_data_update( """Convert a binary sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/bthome/device.py b/homeassistant/components/bthome/device.py index bd011752db1..eecd8161d6c 100644 --- a/homeassistant/components/bthome/device.py +++ b/homeassistant/components/bthome/device.py @@ -1,13 +1,11 @@ """Support for BTHome Bluetooth devices.""" from __future__ import annotations -from bthome_ble import DeviceKey, SensorDeviceInfo +from bthome_ble import DeviceKey from homeassistant.components.bluetooth.passive_update_processor import ( PassiveBluetoothEntityKey, ) -from homeassistant.const import ATTR_MANUFACTURER, ATTR_MODEL, ATTR_NAME -from homeassistant.helpers.entity import DeviceInfo def device_key_to_bluetooth_entity_key( @@ -15,17 +13,3 @@ def device_key_to_bluetooth_entity_key( ) -> PassiveBluetoothEntityKey: """Convert a device key to an entity key.""" return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) - - -def sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a sensor device info to a sensor device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info diff --git a/homeassistant/components/bthome/sensor.py b/homeassistant/components/bthome/sensor.py index 61f7603039e..6493b291085 100644 --- a/homeassistant/components/bthome/sensor.py +++ b/homeassistant/components/bthome/sensor.py @@ -39,9 +39,10 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN -from .device import device_key_to_bluetooth_entity_key, sensor_device_info_to_hass +from .device import device_key_to_bluetooth_entity_key SENSOR_DESCRIPTIONS = { (BTHomeSensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( @@ -243,7 +244,7 @@ def sensor_update_to_bluetooth_data_update( """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/govee_ble/sensor.py b/homeassistant/components/govee_ble/sensor.py index 4faa6befa06..1d193287419 100644 --- a/homeassistant/components/govee_ble/sensor.py +++ b/homeassistant/components/govee_ble/sensor.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Optional, Union -from govee_ble import DeviceClass, DeviceKey, SensorDeviceInfo, SensorUpdate, Units +from govee_ble import DeviceClass, DeviceKey, SensorUpdate, Units from homeassistant import config_entries from homeassistant.components.bluetooth.passive_update_processor import ( @@ -20,16 +20,13 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.const import ( - ATTR_MANUFACTURER, - ATTR_MODEL, - ATTR_NAME, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN @@ -72,27 +69,13 @@ def _device_key_to_bluetooth_entity_key( return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) -def _sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a sensor device info to hass device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info - - def sensor_update_to_bluetooth_data_update( sensor_update: SensorUpdate, ) -> PassiveBluetoothDataUpdate: """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: _sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/inkbird/sensor.py b/homeassistant/components/inkbird/sensor.py index 71d6f00ea40..d0e06e81647 100644 --- a/homeassistant/components/inkbird/sensor.py +++ b/homeassistant/components/inkbird/sensor.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Optional, Union -from inkbird_ble import DeviceClass, DeviceKey, SensorDeviceInfo, SensorUpdate, Units +from inkbird_ble import DeviceClass, DeviceKey, SensorUpdate, Units from homeassistant import config_entries from homeassistant.components.bluetooth.passive_update_processor import ( @@ -20,16 +20,13 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.const import ( - ATTR_MANUFACTURER, - ATTR_MODEL, - ATTR_NAME, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN @@ -72,27 +69,13 @@ def _device_key_to_bluetooth_entity_key( return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) -def _sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a sensor device info to a sensor device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info - - def sensor_update_to_bluetooth_data_update( sensor_update: SensorUpdate, ) -> PassiveBluetoothDataUpdate: """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: _sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/kegtron/device.py b/homeassistant/components/kegtron/device.py index b97aed76b7d..85516a3aea3 100644 --- a/homeassistant/components/kegtron/device.py +++ b/homeassistant/components/kegtron/device.py @@ -3,13 +3,11 @@ from __future__ import annotations import logging -from kegtron_ble import DeviceKey, SensorDeviceInfo +from kegtron_ble import DeviceKey from homeassistant.components.bluetooth.passive_update_processor import ( PassiveBluetoothEntityKey, ) -from homeassistant.const import ATTR_MANUFACTURER, ATTR_MODEL, ATTR_NAME -from homeassistant.helpers.entity import DeviceInfo _LOGGER = logging.getLogger(__name__) @@ -19,17 +17,3 @@ def device_key_to_bluetooth_entity_key( ) -> PassiveBluetoothEntityKey: """Convert a device key to an entity key.""" return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) - - -def sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a sensor device info to a sensor device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info diff --git a/homeassistant/components/kegtron/sensor.py b/homeassistant/components/kegtron/sensor.py index 892d8651185..b9386dd9bb4 100644 --- a/homeassistant/components/kegtron/sensor.py +++ b/homeassistant/components/kegtron/sensor.py @@ -26,9 +26,10 @@ from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, VOLUME_LITER from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN -from .device import device_key_to_bluetooth_entity_key, sensor_device_info_to_hass +from .device import device_key_to_bluetooth_entity_key SENSOR_DESCRIPTIONS = { KegtronSensorDeviceClass.PORT_COUNT: SensorEntityDescription( @@ -85,7 +86,7 @@ def sensor_update_to_bluetooth_data_update( """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/moat/sensor.py b/homeassistant/components/moat/sensor.py index c5e02a38dcd..29133e11283 100644 --- a/homeassistant/components/moat/sensor.py +++ b/homeassistant/components/moat/sensor.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Optional, Union -from moat_ble import DeviceClass, DeviceKey, SensorDeviceInfo, SensorUpdate, Units +from moat_ble import DeviceClass, DeviceKey, SensorUpdate, Units from homeassistant import config_entries from homeassistant.components.bluetooth.passive_update_processor import ( @@ -20,17 +20,14 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.const import ( - ATTR_MANUFACTURER, - ATTR_MODEL, - ATTR_NAME, ELECTRIC_POTENTIAL_VOLT, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN @@ -79,27 +76,13 @@ def _device_key_to_bluetooth_entity_key( return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) -def _sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a sensor device info to hass device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info - - def sensor_update_to_bluetooth_data_update( sensor_update: SensorUpdate, ) -> PassiveBluetoothDataUpdate: """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: _sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/oralb/device.py b/homeassistant/components/oralb/device.py index 0b9da5c3779..3cc46fd27c6 100644 --- a/homeassistant/components/oralb/device.py +++ b/homeassistant/components/oralb/device.py @@ -1,13 +1,11 @@ """Support for OralB devices.""" from __future__ import annotations -from oralb_ble import DeviceKey, SensorDeviceInfo +from oralb_ble import DeviceKey from homeassistant.components.bluetooth.passive_update_processor import ( PassiveBluetoothEntityKey, ) -from homeassistant.const import ATTR_MANUFACTURER, ATTR_MODEL, ATTR_NAME -from homeassistant.helpers.entity import DeviceInfo def device_key_to_bluetooth_entity_key( @@ -15,17 +13,3 @@ def device_key_to_bluetooth_entity_key( ) -> PassiveBluetoothEntityKey: """Convert a device key to an entity key.""" return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) - - -def sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a oralb device info to a sensor device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info diff --git a/homeassistant/components/oralb/sensor.py b/homeassistant/components/oralb/sensor.py index 6fbc19b092a..af1edd582f9 100644 --- a/homeassistant/components/oralb/sensor.py +++ b/homeassistant/components/oralb/sensor.py @@ -22,9 +22,10 @@ from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, TIME_SECONDS from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN -from .device import device_key_to_bluetooth_entity_key, sensor_device_info_to_hass +from .device import device_key_to_bluetooth_entity_key SENSOR_DESCRIPTIONS: dict[str, SensorEntityDescription] = { OralBSensor.TIME: SensorEntityDescription( @@ -67,7 +68,7 @@ def sensor_update_to_bluetooth_data_update( """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/qingping/binary_sensor.py b/homeassistant/components/qingping/binary_sensor.py index 046792a2ff2..b3cb80ad0f2 100644 --- a/homeassistant/components/qingping/binary_sensor.py +++ b/homeassistant/components/qingping/binary_sensor.py @@ -22,9 +22,10 @@ from homeassistant.components.bluetooth.passive_update_processor import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN -from .device import device_key_to_bluetooth_entity_key, sensor_device_info_to_hass +from .device import device_key_to_bluetooth_entity_key BINARY_SENSOR_DESCRIPTIONS = { QingpingBinarySensorDeviceClass.MOTION: BinarySensorEntityDescription( @@ -52,7 +53,7 @@ def sensor_update_to_bluetooth_data_update( """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/qingping/device.py b/homeassistant/components/qingping/device.py index 4e4f29b8db8..ec6bb23c2af 100644 --- a/homeassistant/components/qingping/device.py +++ b/homeassistant/components/qingping/device.py @@ -1,13 +1,11 @@ """Support for Qingping devices.""" from __future__ import annotations -from qingping_ble import DeviceKey, SensorDeviceInfo +from qingping_ble import DeviceKey from homeassistant.components.bluetooth.passive_update_processor import ( PassiveBluetoothEntityKey, ) -from homeassistant.const import ATTR_MANUFACTURER, ATTR_MODEL, ATTR_NAME -from homeassistant.helpers.entity import DeviceInfo def device_key_to_bluetooth_entity_key( @@ -15,17 +13,3 @@ def device_key_to_bluetooth_entity_key( ) -> PassiveBluetoothEntityKey: """Convert a device key to an entity key.""" return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) - - -def sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a qingping device info to a sensor device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info diff --git a/homeassistant/components/qingping/sensor.py b/homeassistant/components/qingping/sensor.py index 1affd320af2..6f1ad8118ab 100644 --- a/homeassistant/components/qingping/sensor.py +++ b/homeassistant/components/qingping/sensor.py @@ -34,9 +34,10 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN -from .device import device_key_to_bluetooth_entity_key, sensor_device_info_to_hass +from .device import device_key_to_bluetooth_entity_key SENSOR_DESCRIPTIONS = { (QingpingSensorDeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription( @@ -120,7 +121,7 @@ def sensor_update_to_bluetooth_data_update( """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/ruuvitag_ble/sensor.py b/homeassistant/components/ruuvitag_ble/sensor.py index 463d6da2de2..e02851b38d5 100644 --- a/homeassistant/components/ruuvitag_ble/sensor.py +++ b/homeassistant/components/ruuvitag_ble/sensor.py @@ -7,7 +7,6 @@ from sensor_state_data import ( DeviceKey, SensorDescription, SensorDeviceClass, - SensorDeviceInfo, SensorUpdate, Units, ) @@ -26,8 +25,8 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN @@ -85,20 +84,6 @@ def _device_key_to_bluetooth_entity_key( return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) -def _sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a sensor device info to a sensor device info.""" - hass_device_info = DeviceInfo() - if sensor_device_info.name is not None: - hass_device_info[const.ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[const.ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[const.ATTR_MODEL] = sensor_device_info.model - return hass_device_info - - def _to_sensor_key( description: SensorDescription, ) -> tuple[SensorDeviceClass, Units | None]: @@ -112,7 +97,7 @@ def sensor_update_to_bluetooth_data_update( """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: _sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/sensorpro/device.py b/homeassistant/components/sensorpro/device.py index b5b44eef50f..326eb8b8bbd 100644 --- a/homeassistant/components/sensorpro/device.py +++ b/homeassistant/components/sensorpro/device.py @@ -1,13 +1,11 @@ """Support for SensorPro devices.""" from __future__ import annotations -from sensorpro_ble import DeviceKey, SensorDeviceInfo +from sensorpro_ble import DeviceKey from homeassistant.components.bluetooth.passive_update_processor import ( PassiveBluetoothEntityKey, ) -from homeassistant.const import ATTR_MANUFACTURER, ATTR_MODEL, ATTR_NAME -from homeassistant.helpers.entity import DeviceInfo def device_key_to_bluetooth_entity_key( @@ -15,17 +13,3 @@ def device_key_to_bluetooth_entity_key( ) -> PassiveBluetoothEntityKey: """Convert a device key to an entity key.""" return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) - - -def sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a sensorpro device info to a sensor device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info diff --git a/homeassistant/components/sensorpro/sensor.py b/homeassistant/components/sensorpro/sensor.py index 8866ed44587..deb79e62bdc 100644 --- a/homeassistant/components/sensorpro/sensor.py +++ b/homeassistant/components/sensorpro/sensor.py @@ -31,9 +31,10 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN -from .device import device_key_to_bluetooth_entity_key, sensor_device_info_to_hass +from .device import device_key_to_bluetooth_entity_key SENSOR_DESCRIPTIONS = { (SensorProSensorDeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription( @@ -87,7 +88,7 @@ def sensor_update_to_bluetooth_data_update( """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/sensorpush/sensor.py b/homeassistant/components/sensorpush/sensor.py index 8a4db7aff14..58b569d5227 100644 --- a/homeassistant/components/sensorpush/sensor.py +++ b/homeassistant/components/sensorpush/sensor.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Optional, Union -from sensorpush_ble import DeviceClass, DeviceKey, SensorDeviceInfo, SensorUpdate, Units +from sensorpush_ble import DeviceClass, DeviceKey, SensorUpdate, Units from homeassistant import config_entries from homeassistant.components.bluetooth.passive_update_processor import ( @@ -20,17 +20,14 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.const import ( - ATTR_MANUFACTURER, - ATTR_MODEL, - ATTR_NAME, PERCENTAGE, PRESSURE_MBAR, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN @@ -73,27 +70,13 @@ def _device_key_to_bluetooth_entity_key( return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) -def _sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a sensor device info to a sensor device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info - - def sensor_update_to_bluetooth_data_update( sensor_update: SensorUpdate, ) -> PassiveBluetoothDataUpdate: """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: _sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/thermobeacon/device.py b/homeassistant/components/thermobeacon/device.py index 327a206042a..fe8a499d6ed 100644 --- a/homeassistant/components/thermobeacon/device.py +++ b/homeassistant/components/thermobeacon/device.py @@ -1,13 +1,11 @@ """Support for ThermoBeacon devices.""" from __future__ import annotations -from thermobeacon_ble import DeviceKey, SensorDeviceInfo +from thermobeacon_ble import DeviceKey from homeassistant.components.bluetooth.passive_update_processor import ( PassiveBluetoothEntityKey, ) -from homeassistant.const import ATTR_MANUFACTURER, ATTR_MODEL, ATTR_NAME -from homeassistant.helpers.entity import DeviceInfo def device_key_to_bluetooth_entity_key( @@ -15,17 +13,3 @@ def device_key_to_bluetooth_entity_key( ) -> PassiveBluetoothEntityKey: """Convert a device key to an entity key.""" return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) - - -def sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a thermobeacon device info to a sensor device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info diff --git a/homeassistant/components/thermobeacon/sensor.py b/homeassistant/components/thermobeacon/sensor.py index 83b616f8d84..900aacbf7a7 100644 --- a/homeassistant/components/thermobeacon/sensor.py +++ b/homeassistant/components/thermobeacon/sensor.py @@ -31,9 +31,10 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN -from .device import device_key_to_bluetooth_entity_key, sensor_device_info_to_hass +from .device import device_key_to_bluetooth_entity_key SENSOR_DESCRIPTIONS = { (ThermoBeaconSensorDeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription( @@ -87,7 +88,7 @@ def sensor_update_to_bluetooth_data_update( """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/thermopro/sensor.py b/homeassistant/components/thermopro/sensor.py index 505f620229c..22a6e2f086a 100644 --- a/homeassistant/components/thermopro/sensor.py +++ b/homeassistant/components/thermopro/sensor.py @@ -6,7 +6,6 @@ from typing import Optional, Union from thermopro_ble import ( DeviceKey, SensorDeviceClass as ThermoProSensorDeviceClass, - SensorDeviceInfo, SensorUpdate, Units, ) @@ -26,16 +25,13 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.const import ( - ATTR_MANUFACTURER, - ATTR_MODEL, - ATTR_NAME, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN @@ -75,27 +71,13 @@ def _device_key_to_bluetooth_entity_key( return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) -def _sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a sensor device info to a sensor device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info - - def sensor_update_to_bluetooth_data_update( sensor_update: SensorUpdate, ) -> PassiveBluetoothDataUpdate: """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: _sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/tilt_ble/sensor.py b/homeassistant/components/tilt_ble/sensor.py index 54d05b3c900..bddae2ca027 100644 --- a/homeassistant/components/tilt_ble/sensor.py +++ b/homeassistant/components/tilt_ble/sensor.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Optional, Union -from tilt_ble import DeviceClass, DeviceKey, SensorDeviceInfo, SensorUpdate, Units +from tilt_ble import DeviceClass, DeviceKey, SensorUpdate, Units from homeassistant import config_entries from homeassistant.components.bluetooth.passive_update_processor import ( @@ -19,16 +19,10 @@ from homeassistant.components.sensor import ( SensorEntityDescription, SensorStateClass, ) -from homeassistant.const import ( - ATTR_MANUFACTURER, - ATTR_MODEL, - ATTR_NAME, - SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_FAHRENHEIT, -) +from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, TEMP_FAHRENHEIT from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN @@ -63,27 +57,13 @@ def _device_key_to_bluetooth_entity_key( return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) -def _sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a sensor device info to a sensor device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info - - def sensor_update_to_bluetooth_data_update( sensor_update: SensorUpdate, ) -> PassiveBluetoothDataUpdate: """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: _sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/xiaomi_ble/binary_sensor.py b/homeassistant/components/xiaomi_ble/binary_sensor.py index 4de491ab9dd..6fc6c3c2761 100644 --- a/homeassistant/components/xiaomi_ble/binary_sensor.py +++ b/homeassistant/components/xiaomi_ble/binary_sensor.py @@ -22,9 +22,10 @@ from homeassistant.components.bluetooth.passive_update_processor import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN -from .device import device_key_to_bluetooth_entity_key, sensor_device_info_to_hass +from .device import device_key_to_bluetooth_entity_key BINARY_SENSOR_DESCRIPTIONS = { XiaomiBinarySensorDeviceClass.MOTION: BinarySensorEntityDescription( @@ -51,7 +52,7 @@ def sensor_update_to_bluetooth_data_update( """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/components/xiaomi_ble/device.py b/homeassistant/components/xiaomi_ble/device.py index 4ddfc31ae51..5714db4eadd 100644 --- a/homeassistant/components/xiaomi_ble/device.py +++ b/homeassistant/components/xiaomi_ble/device.py @@ -1,13 +1,11 @@ """Support for Xioami BLE devices.""" from __future__ import annotations -from xiaomi_ble import DeviceKey, SensorDeviceInfo +from xiaomi_ble import DeviceKey from homeassistant.components.bluetooth.passive_update_processor import ( PassiveBluetoothEntityKey, ) -from homeassistant.const import ATTR_MANUFACTURER, ATTR_MODEL, ATTR_NAME -from homeassistant.helpers.entity import DeviceInfo def device_key_to_bluetooth_entity_key( @@ -15,17 +13,3 @@ def device_key_to_bluetooth_entity_key( ) -> PassiveBluetoothEntityKey: """Convert a device key to an entity key.""" return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) - - -def sensor_device_info_to_hass( - sensor_device_info: SensorDeviceInfo, -) -> DeviceInfo: - """Convert a sensor device info to a sensor device info.""" - hass_device_info = DeviceInfo({}) - if sensor_device_info.name is not None: - hass_device_info[ATTR_NAME] = sensor_device_info.name - if sensor_device_info.manufacturer is not None: - hass_device_info[ATTR_MANUFACTURER] = sensor_device_info.manufacturer - if sensor_device_info.model is not None: - hass_device_info[ATTR_MODEL] = sensor_device_info.model - return hass_device_info diff --git a/homeassistant/components/xiaomi_ble/sensor.py b/homeassistant/components/xiaomi_ble/sensor.py index 033d6b7daf0..831b5d0910b 100644 --- a/homeassistant/components/xiaomi_ble/sensor.py +++ b/homeassistant/components/xiaomi_ble/sensor.py @@ -31,9 +31,10 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN -from .device import device_key_to_bluetooth_entity_key, sensor_device_info_to_hass +from .device import device_key_to_bluetooth_entity_key SENSOR_DESCRIPTIONS = { (DeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription( @@ -121,7 +122,7 @@ def sensor_update_to_bluetooth_data_update( """Convert a sensor update to a bluetooth data update.""" return PassiveBluetoothDataUpdate( devices={ - device_id: sensor_device_info_to_hass(device_info) + device_id: sensor_device_info_to_hass_device_info(device_info) for device_id, device_info in sensor_update.devices.items() }, entity_descriptions={ diff --git a/homeassistant/helpers/sensor.py b/homeassistant/helpers/sensor.py new file mode 100644 index 00000000000..f206ac55bdd --- /dev/null +++ b/homeassistant/helpers/sensor.py @@ -0,0 +1,28 @@ +"""Common functions related to sensor device management.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +from homeassistant import const + +from .entity import DeviceInfo + +if TYPE_CHECKING: + # `sensor_state_data` is a second-party library (i.e. maintained by Home Assistant + # core members) which is not strictly required by Home Assistant. + # Therefore, we import it as a type hint only. + from sensor_state_data import SensorDeviceInfo + + +def sensor_device_info_to_hass_device_info( + sensor_device_info: SensorDeviceInfo, +) -> DeviceInfo: + """Convert a sensor_state_data sensor device info to a Home Assistant device info.""" + device_info = DeviceInfo() + if sensor_device_info.name is not None: + device_info[const.ATTR_NAME] = sensor_device_info.name + if sensor_device_info.manufacturer is not None: + device_info[const.ATTR_MANUFACTURER] = sensor_device_info.manufacturer + if sensor_device_info.model is not None: + device_info[const.ATTR_MODEL] = sensor_device_info.model + return device_info From 13577981f9307c7b02ee910a12d354abee2c2ca6 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Mon, 14 Nov 2022 19:20:45 +0100 Subject: [PATCH 0412/1033] Local ip tests (#82051) --- tests/components/local_ip/test_config_flow.py | 7 +++++-- tests/components/local_ip/test_init.py | 11 ++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/components/local_ip/test_config_flow.py b/tests/components/local_ip/test_config_flow.py index 1b84e3e8552..4de150eaf7a 100644 --- a/tests/components/local_ip/test_config_flow.py +++ b/tests/components/local_ip/test_config_flow.py @@ -1,12 +1,15 @@ """Tests for the local_ip config_flow.""" +from __future__ import annotations + from homeassistant import data_entry_flow from homeassistant.components.local_ip.const import DOMAIN from homeassistant.config_entries import SOURCE_USER +from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry -async def test_config_flow(hass, mock_get_source_ip): +async def test_config_flow(hass: HomeAssistant, mock_get_source_ip) -> None: """Test we can finish a config flow.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} @@ -21,7 +24,7 @@ async def test_config_flow(hass, mock_get_source_ip): assert state -async def test_already_setup(hass, mock_get_source_ip): +async def test_already_setup(hass: HomeAssistant, mock_get_source_ip) -> None: """Test we abort if already setup.""" MockConfigEntry( domain=DOMAIN, diff --git a/tests/components/local_ip/test_init.py b/tests/components/local_ip/test_init.py index a3fba49b969..21becc39a94 100644 --- a/tests/components/local_ip/test_init.py +++ b/tests/components/local_ip/test_init.py @@ -1,20 +1,29 @@ """Tests for the local_ip component.""" +from __future__ import annotations + +from homeassistant import config_entries from homeassistant.components.local_ip import DOMAIN from homeassistant.components.network import async_get_source_ip from homeassistant.components.zeroconf import MDNS_TARGET_IP +from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry -async def test_basic_setup(hass, mock_get_source_ip): +async def test_basic_setup(hass: HomeAssistant, mock_get_source_ip) -> None: """Test component setup creates entry from config.""" entry = MockConfigEntry(domain=DOMAIN, data={}) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() + assert entry.state == config_entries.ConfigEntryState.LOADED local_ip = await async_get_source_ip(hass, target_ip=MDNS_TARGET_IP) state = hass.states.get(f"sensor.{DOMAIN}") assert state assert state.state == local_ip + + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + assert entry.state is config_entries.ConfigEntryState.NOT_LOADED From 956120662e58a4c114fbb4de2899018fbd3cad99 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Nov 2022 12:58:10 -0600 Subject: [PATCH 0413/1033] Add the ability to register for shelly event callbacks (#82052) --- .../components/shelly/coordinator.py | 57 +++++++++++++------ tests/components/shelly/__init__.py | 19 +++++++ tests/components/shelly/test_cover.py | 41 +++++++++---- tests/components/shelly/test_light.py | 4 +- 4 files changed, 91 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py index 23f905b0fd9..4a5b168e85c 100644 --- a/homeassistant/components/shelly/coordinator.py +++ b/homeassistant/components/shelly/coordinator.py @@ -1,7 +1,7 @@ """Coordinators for the Shelly integration.""" from __future__ import annotations -from collections.abc import Coroutine +from collections.abc import Callable, Coroutine from dataclasses import dataclass from datetime import timedelta from typing import Any, cast @@ -13,7 +13,7 @@ from aioshelly.rpc_device import RpcDevice from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_DEVICE_ID, CONF_HOST, EVENT_HOMEASSISTANT_STOP -from homeassistant.core import Event, HomeAssistant, callback +from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback from homeassistant.helpers import device_registry from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -337,6 +337,7 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): self.entry = entry self.device = device + self._event_listeners: list[Callable[[dict[str, Any]], None]] = [] self._debounced_reload: Debouncer[Coroutine[Any, Any, None]] = Debouncer( hass, LOGGER, @@ -346,10 +347,8 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): ) entry.async_on_unload(self._debounced_reload.async_cancel) - entry.async_on_unload( - self.async_add_listener(self._async_device_updates_handler) - ) self._last_event: dict[str, Any] | None = None + self._last_status: dict[str, Any] | None = None entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self._handle_ha_stop) @@ -379,24 +378,32 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): return True @callback - def _async_device_updates_handler(self) -> None: - """Handle device updates.""" - if ( - not self.device.initialized - or not self.device.event - or self.device.event == self._last_event - ): - return + def async_subscribe_events( + self, event_callback: Callable[[dict[str, Any]], None] + ) -> CALLBACK_TYPE: + """Subscribe to events.""" + def _unsubscribe() -> None: + self._event_listeners.remove(event_callback) + + self._event_listeners.append(event_callback) + + return _unsubscribe + + @callback + def _async_device_event_handler(self, event_data: dict[str, Any]) -> None: + """Handle device events.""" self.update_sleep_period() + events: list[dict[str, Any]] = event_data["events"] - self._last_event = self.device.event - - for event in self.device.event["events"]: + for event in events: event_type = event.get("event") if event_type is None: continue + for event_callback in self._event_listeners: + event_callback(event) + if event_type == "config_changed": LOGGER.info( "Config for %s changed, reloading entry in %s seconds", @@ -453,6 +460,22 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): """Firmware version of the device.""" return self.device.firmware_version if self.device.initialized else "" + @callback + def _async_handle_update(self, device_: RpcDevice) -> None: + """Handle device update.""" + device = self.device + if not device.initialized: + return + event = device.event + status = device.status + + if event and event != self._last_event: + self._last_event = event + self._async_device_event_handler(event) + if status and status != self._last_status: + self._last_status = status + self.async_set_updated_data(device) + def async_setup(self) -> None: """Set up the coordinator.""" dev_reg = device_registry.async_get(self.hass) @@ -467,7 +490,7 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): configuration_url=f"http://{self.entry.data[CONF_HOST]}", ) self.device_id = entry.id - self.device.subscribe_updates(self.async_set_updated_data) + self.device.subscribe_updates(self._async_handle_update) async def shutdown(self) -> None: """Shutdown the coordinator.""" diff --git a/tests/components/shelly/__init__.py b/tests/components/shelly/__init__.py index a3c571d7177..da98520332a 100644 --- a/tests/components/shelly/__init__.py +++ b/tests/components/shelly/__init__.py @@ -1,4 +1,10 @@ """Tests for the Shelly integration.""" +from copy import deepcopy +from typing import Any +from unittest.mock import Mock + +import pytest + from homeassistant.components.shelly.const import CONF_SLEEP_PERIOD, DOMAIN from homeassistant.const import CONF_HOST from homeassistant.core import HomeAssistant @@ -26,3 +32,16 @@ async def init_integration( await hass.async_block_till_done() return entry + + +def mutate_rpc_device_status( + monkeypatch: pytest.MonkeyPatch, + mock_rpc_device: Mock, + top_level_key: str, + key: str, + value: Any, +) -> None: + """Mutate status for rpc device.""" + new_status = deepcopy(mock_rpc_device.status) + new_status[top_level_key][key] = value + monkeypatch.setattr(mock_rpc_device, "status", new_status) diff --git a/tests/components/shelly/test_cover.py b/tests/components/shelly/test_cover.py index 51fef7dc030..34f63b7690c 100644 --- a/tests/components/shelly/test_cover.py +++ b/tests/components/shelly/test_cover.py @@ -1,4 +1,8 @@ """Tests for Shelly cover platform.""" +from unittest.mock import Mock + +import pytest + from homeassistant.components.cover import ( ATTR_CURRENT_POSITION, ATTR_POSITION, @@ -13,8 +17,9 @@ from homeassistant.components.cover import ( STATE_OPENING, ) from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant -from . import init_integration +from . import init_integration, mutate_rpc_device_status ROLLER_BLOCK_ID = 1 @@ -77,7 +82,9 @@ async def test_block_device_no_roller_blocks(hass, mock_block_device, monkeypatc assert hass.states.get("cover.test_name") is None -async def test_rpc_device_services(hass, mock_rpc_device, monkeypatch): +async def test_rpc_device_services( + hass: HomeAssistant, mock_rpc_device: Mock, monkeypatch: pytest.MonkeyPatch +) -> None: """Test RPC device cover services.""" await init_integration(hass, 2) @@ -90,7 +97,9 @@ async def test_rpc_device_services(hass, mock_rpc_device, monkeypatch): state = hass.states.get("cover.test_cover_0") assert state.attributes[ATTR_CURRENT_POSITION] == 50 - monkeypatch.setitem(mock_rpc_device.status["cover:0"], "state", "opening") + mutate_rpc_device_status( + monkeypatch, mock_rpc_device, "cover:0", "state", "opening" + ) await hass.services.async_call( COVER_DOMAIN, SERVICE_OPEN_COVER, @@ -100,7 +109,9 @@ async def test_rpc_device_services(hass, mock_rpc_device, monkeypatch): mock_rpc_device.mock_update() assert hass.states.get("cover.test_cover_0").state == STATE_OPENING - monkeypatch.setitem(mock_rpc_device.status["cover:0"], "state", "closing") + mutate_rpc_device_status( + monkeypatch, mock_rpc_device, "cover:0", "state", "closing" + ) await hass.services.async_call( COVER_DOMAIN, SERVICE_CLOSE_COVER, @@ -110,7 +121,7 @@ async def test_rpc_device_services(hass, mock_rpc_device, monkeypatch): mock_rpc_device.mock_update() assert hass.states.get("cover.test_cover_0").state == STATE_CLOSING - monkeypatch.setitem(mock_rpc_device.status["cover:0"], "state", "closed") + mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cover:0", "state", "closed") await hass.services.async_call( COVER_DOMAIN, SERVICE_STOP_COVER, @@ -121,26 +132,34 @@ async def test_rpc_device_services(hass, mock_rpc_device, monkeypatch): assert hass.states.get("cover.test_cover_0").state == STATE_CLOSED -async def test_rpc_device_no_cover_keys(hass, mock_rpc_device, monkeypatch): +async def test_rpc_device_no_cover_keys( + hass: HomeAssistant, mock_rpc_device: Mock, monkeypatch: pytest.MonkeyPatch +) -> None: """Test RPC device without cover keys.""" monkeypatch.delitem(mock_rpc_device.status, "cover:0") await init_integration(hass, 2) assert hass.states.get("cover.test_cover_0") is None -async def test_rpc_device_update(hass, mock_rpc_device, monkeypatch): +async def test_rpc_device_update( + hass: HomeAssistant, mock_rpc_device: Mock, monkeypatch: pytest.MonkeyPatch +) -> None: """Test RPC device update.""" - monkeypatch.setitem(mock_rpc_device.status["cover:0"], "state", "closed") + mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cover:0", "state", "closed") await init_integration(hass, 2) assert hass.states.get("cover.test_cover_0").state == STATE_CLOSED - monkeypatch.setitem(mock_rpc_device.status["cover:0"], "state", "open") + mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cover:0", "state", "open") mock_rpc_device.mock_update() assert hass.states.get("cover.test_cover_0").state == STATE_OPEN -async def test_rpc_device_no_position_control(hass, mock_rpc_device, monkeypatch): +async def test_rpc_device_no_position_control( + hass: HomeAssistant, mock_rpc_device: Mock, monkeypatch: pytest.MonkeyPatch +) -> None: """Test RPC device with no position control.""" - monkeypatch.setitem(mock_rpc_device.status["cover:0"], "pos_control", False) + mutate_rpc_device_status( + monkeypatch, mock_rpc_device, "cover:0", "pos_control", False + ) await init_integration(hass, 2) assert hass.states.get("cover.test_cover_0").state == STATE_OPEN diff --git a/tests/components/shelly/test_light.py b/tests/components/shelly/test_light.py index b0162f43e13..5f8d49fa8aa 100644 --- a/tests/components/shelly/test_light.py +++ b/tests/components/shelly/test_light.py @@ -25,7 +25,7 @@ from homeassistant.const import ( STATE_ON, ) -from . import init_integration +from . import init_integration, mutate_rpc_device_status RELAY_BLOCK_ID = 0 LIGHT_BLOCK_ID = 2 @@ -374,7 +374,7 @@ async def test_rpc_device_switch_type_lights_mode(hass, mock_rpc_device, monkeyp ) assert hass.states.get("light.test_switch_0").state == STATE_ON - monkeypatch.setitem(mock_rpc_device.status["switch:0"], "output", False) + mutate_rpc_device_status(monkeypatch, mock_rpc_device, "switch:0", "output", False) await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_OFF, From 2ac2a288cc4f6c46591e803fa0a5e391c52b411c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 14 Nov 2022 21:16:44 +0100 Subject: [PATCH 0414/1033] Update mypy to 0.991 (#82096) --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index e0cbafbd6d4..6aa8ed48236 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -12,7 +12,7 @@ codecov==2.1.12 coverage==6.4.4 freezegun==1.2.2 mock-open==1.4.0 -mypy==0.990 +mypy==0.991 pre-commit==2.20.0 pylint==2.15.5 pipdeptree==2.3.1 From 83a6012f8061ef8c5c8d5f60cb0ded7282d0dd1e Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 15 Nov 2022 00:26:42 +0000 Subject: [PATCH 0415/1033] [ci skip] Translation update --- .../components/airq/translations/fr.json | 20 ++++ .../components/aranet/translations/fr.json | 23 ++++ .../components/hassio/translations/fr.json | 77 +++++++++++++ .../homeassistant/translations/fr.json | 6 ++ .../components/knx/translations/fr.json | 101 +++++++++++++++++- .../components/livisi/translations/fr.json | 16 +++ .../components/mqtt/translations/en.json | 2 +- .../components/mqtt/translations/fr.json | 8 +- .../nibe_heatpump/translations/fr.json | 21 ++++ .../components/openuv/translations/fr.json | 10 +- .../pushbullet/translations/fr.json | 19 ++++ .../unifiprotect/translations/ca.json | 10 ++ .../unifiprotect/translations/de.json | 17 +++ .../unifiprotect/translations/en.json | 5 + .../unifiprotect/translations/es.json | 17 +++ .../unifiprotect/translations/et.json | 17 +++ .../unifiprotect/translations/hu.json | 17 +++ .../unifiprotect/translations/id.json | 13 +++ .../unifiprotect/translations/no.json | 17 +++ .../unifiprotect/translations/pt-BR.json | 17 +++ .../unifiprotect/translations/zh-Hant.json | 17 +++ .../utility_meter/translations/ca.json | 2 +- .../components/zamg/translations/fr.json | 7 +- 23 files changed, 451 insertions(+), 8 deletions(-) create mode 100644 homeassistant/components/airq/translations/fr.json create mode 100644 homeassistant/components/aranet/translations/fr.json create mode 100644 homeassistant/components/livisi/translations/fr.json create mode 100644 homeassistant/components/pushbullet/translations/fr.json diff --git a/homeassistant/components/airq/translations/fr.json b/homeassistant/components/airq/translations/fr.json new file mode 100644 index 00000000000..cbee413a351 --- /dev/null +++ b/homeassistant/components/airq/translations/fr.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_auth": "Authentification non valide", + "invalid_input": "Nom d'h\u00f4te ou adresse IP non valide" + }, + "step": { + "user": { + "data": { + "ip_address": "Adresse IP", + "password": "Mot de passe" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/fr.json b/homeassistant/components/aranet/translations/fr.json new file mode 100644 index 00000000000..a21fd193d8b --- /dev/null +++ b/homeassistant/components/aranet/translations/fr.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", + "no_devices_found": "Aucun appareil Aranet non configur\u00e9 n\u2019a \u00e9t\u00e9 trouv\u00e9." + }, + "error": { + "unknown": "Erreur inattendue" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Voulez-vous configurer {name}\u00a0?" + }, + "user": { + "data": { + "address": "Appareil" + }, + "description": "S\u00e9lectionnez l'appareil \u00e0 configurer" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hassio/translations/fr.json b/homeassistant/components/hassio/translations/fr.json index 9a042b97e57..6f072495c80 100644 --- a/homeassistant/components/hassio/translations/fr.json +++ b/homeassistant/components/hassio/translations/fr.json @@ -1,4 +1,81 @@ { + "issues": { + "unhealthy": { + "title": "Syst\u00e8me d\u00e9fectueux \u2013\u00a0{reason}" + }, + "unhealthy_docker": { + "title": "Syst\u00e8me d\u00e9fectueux \u2013\u00a0Docker mal configur\u00e9" + }, + "unhealthy_privileged": { + "title": "Syst\u00e8me d\u00e9fectueux \u2013\u00a0Non privil\u00e9gi\u00e9" + }, + "unhealthy_setup": { + "title": "Syst\u00e8me d\u00e9fectueux \u2013\u00a0\u00c9chec de l\u2019installation" + }, + "unhealthy_supervisor": { + "title": "Syst\u00e8me d\u00e9fectueux \u2013\u00a0\u00c9chec de la mise \u00e0 jour du superviseur" + }, + "unhealthy_untrusted": { + "title": "Syst\u00e8me d\u00e9fectueux \u2013\u00a0Code non approuv\u00e9" + }, + "unsupported": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0{reason}" + }, + "unsupported_apparmor": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Probl\u00e8mes li\u00e9s \u00e0 AppArmor" + }, + "unsupported_cgroup_version": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Version de CGroup" + }, + "unsupported_connectivity_check": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0V\u00e9rification de connectivit\u00e9 d\u00e9sactiv\u00e9e" + }, + "unsupported_dbus": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Probl\u00e8mes li\u00e9s \u00e0 D-Bus" + }, + "unsupported_dns_server": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Probl\u00e8mes de serveur DNS" + }, + "unsupported_docker_configuration": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Docker mal configur\u00e9" + }, + "unsupported_docker_version": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Version de Docker" + }, + "unsupported_job_conditions": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Protections d\u00e9sactiv\u00e9es" + }, + "unsupported_lxc": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0LXC d\u00e9tect\u00e9" + }, + "unsupported_network_manager": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Probl\u00e8mes li\u00e9s \u00e0 Network Manager" + }, + "unsupported_os": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Syst\u00e8me d\u2019exploitation" + }, + "unsupported_os_agent": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Probl\u00e8mes li\u00e9s \u00e0 OS-Agent" + }, + "unsupported_restart_policy": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0R\u00e8gle de red\u00e9marrage du conteneur" + }, + "unsupported_software": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Logiciel non pris en charge" + }, + "unsupported_source_mods": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Modifications du code source du superviseur" + }, + "unsupported_supervisor_version": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Version du superviseur" + }, + "unsupported_systemd": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Probl\u00e8mes li\u00e9s \u00e0 Systemd" + }, + "unsupported_systemd_resolved": { + "title": "Syst\u00e8me non pris en charge \u2013\u00a0Probl\u00e8mes li\u00e9s \u00e0 Systemd-Resolved" + } + }, "system_health": { "info": { "agent_version": "Version de l'agent", diff --git a/homeassistant/components/homeassistant/translations/fr.json b/homeassistant/components/homeassistant/translations/fr.json index 48e38978ba6..19835e38bf1 100644 --- a/homeassistant/components/homeassistant/translations/fr.json +++ b/homeassistant/components/homeassistant/translations/fr.json @@ -1,4 +1,10 @@ { + "issues": { + "historic_currency": { + "description": "La devise {currency} n\u2019est plus utilis\u00e9e, veuillez actualiser la configuration de la devise.", + "title": "La devise configur\u00e9e n\u2019est plus utilis\u00e9e" + } + }, "system_health": { "info": { "arch": "Architecture du processeur", diff --git a/homeassistant/components/knx/translations/fr.json b/homeassistant/components/knx/translations/fr.json index 7a1e8e88698..7ec7a06b7ad 100644 --- a/homeassistant/components/knx/translations/fr.json +++ b/homeassistant/components/knx/translations/fr.json @@ -12,6 +12,11 @@ "invalid_signature": "Le mot de passe pour d\u00e9chiffrer le fichier `.knxkeys` est erron\u00e9." }, "step": { + "connection_type": { + "data": { + "connection_type": "Type de connexion KNX" + } + }, "manual_tunnel": { "data": { "host": "H\u00f4te", @@ -63,11 +68,25 @@ }, "description": "Veuillez saisir vos informations de s\u00e9curit\u00e9 IP." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Mot de passe d\u2019authentification de l\u2019appareil", + "user_id": "ID de l\u2019utilisateur", + "user_password": "Mot de passe de l\u2019utilisateur" + }, + "data_description": { + "device_authentication": "D\u00e9fini dans le panneau \u00ab\u00a0IP\u00a0\u00bb de l'interface dans ETS.", + "user_id": "G\u00e9n\u00e9ralement le num\u00e9ro du tunnel +\u00a01. Par exemple, \u00ab\u00a0Tunnel 2\u00a0\u00bb aurait l'ID utilisateur \u00ab\u00a03\u00a0\u00bb.", + "user_password": "Mot de passe pour la connexion de tunnel sp\u00e9cifique, d\u00e9fini dans le panneau \u00ab\u00a0Propri\u00e9t\u00e9s\u00a0\u00bb du tunnel dans ETS." + }, + "description": "Veuillez saisir vos informations de s\u00e9curit\u00e9 IP." + }, "secure_tunneling": { "description": "S\u00e9lectionnez la mani\u00e8re dont vous souhaitez configurer la s\u00e9curit\u00e9 IP de KNX.", "menu_options": { "secure_knxkeys": "Utiliser un fichier `.knxkeys` contenant les cl\u00e9s de s\u00e9curit\u00e9 IP", - "secure_manual": "Configurer manuellement les cl\u00e9s de s\u00e9curit\u00e9 IP" + "secure_manual": "Configurer manuellement les cl\u00e9s de s\u00e9curit\u00e9 IP", + "secure_tunnel_manual": "Configurer manuellement les cl\u00e9s de s\u00e9curit\u00e9 IP" } }, "tunnel": { @@ -85,7 +104,19 @@ } }, "options": { + "error": { + "cannot_connect": "\u00c9chec de connexion", + "file_not_found": "Le fichier `.knxkeys` sp\u00e9cifi\u00e9 n'a pas \u00e9t\u00e9 trouv\u00e9 dans config/.storage/knx/", + "invalid_individual_address": "La valeur de l'adresse individuelle KNX ne correspond pas au mod\u00e8le.\n'area.line.device'", + "invalid_ip_address": "Adresse IPv4 non valide.", + "invalid_signature": "Le mot de passe pour d\u00e9chiffrer le fichier `.knxkeys` est erron\u00e9." + }, "step": { + "connection_type": { + "data": { + "connection_type": "Type de connexion KNX" + } + }, "init": { "data": { "connection_type": "Type de connexion KNX", @@ -105,8 +136,73 @@ "state_updater": "Active ou d\u00e9sactive globalement la lecture des \u00e9tats depuis le bus KNX. Lorsqu'elle est d\u00e9sactiv\u00e9e, Home Assistant ne r\u00e9cup\u00e8re pas activement les \u00e9tats depuis le bus KNX. Peut \u00eatre remplac\u00e9 par les options d'entit\u00e9 `sync_state`." } }, + "manual_tunnel": { + "data": { + "host": "H\u00f4te", + "local_ip": "IP locale de Home Assistant", + "port": "Port", + "tunneling_type": "Type de tunnel KNX" + }, + "data_description": { + "host": "Adresse IP de l'appareil de tunnel KNX/IP.", + "local_ip": "Laissez le champ vide pour utiliser la d\u00e9couverte automatique.", + "port": "Port de l'appareil de tunnel KNX/IP." + }, + "description": "Veuillez saisir les informations de connexion de votre appareil de cr\u00e9ation de tunnel." + }, + "options_init": { + "menu_options": { + "communication_settings": "Param\u00e8tres de communication", + "connection_type": "Configurer l\u2019interface KNX" + } + }, + "routing": { + "data": { + "individual_address": "Adresse individuelle", + "local_ip": "IP locale de Home Assistant", + "multicast_group": "Groupe multicast", + "multicast_port": "Port multicast" + }, + "data_description": { + "individual_address": "Adresse KNX que Home Assistant doit utiliser, par exemple `0.0.4`.", + "local_ip": "Laissez le champ vide pour utiliser la d\u00e9couverte automatique." + }, + "description": "Veuillez configurer les options de routage." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Le nom de votre fichier `.knxkeys` (extension incluse)", + "knxkeys_password": "Le mot de passe pour d\u00e9chiffrer le fichier `.knxkeys`" + }, + "data_description": { + "knxkeys_filename": "Le fichier devrait se trouver dans votre r\u00e9pertoire de configuration dans `.storage/knx/`.\nSous Home Assistant OS, il s'agirait de `/config/.storage/knx/`\nPar exemple\u00a0: `my_project.knxkeys`", + "knxkeys_password": "D\u00e9fini lors de l'exportation du fichier depuis ETS." + }, + "description": "Veuillez saisir les informations relatives \u00e0 votre fichier `.knxkeys`." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Mot de passe d\u2019authentification de l\u2019appareil", + "user_id": "ID de l\u2019utilisateur", + "user_password": "Mot de passe de l\u2019utilisateur" + }, + "data_description": { + "device_authentication": "D\u00e9fini dans le panneau \u00ab\u00a0IP\u00a0\u00bb de l'interface dans ETS.", + "user_id": "G\u00e9n\u00e9ralement le num\u00e9ro du tunnel +\u00a01. Par exemple, \u00ab\u00a0Tunnel 2\u00a0\u00bb aurait l'ID utilisateur \u00ab\u00a03\u00a0\u00bb.", + "user_password": "Mot de passe pour la connexion de tunnel sp\u00e9cifique, d\u00e9fini dans le panneau \u00ab\u00a0Propri\u00e9t\u00e9s\u00a0\u00bb du tunnel dans ETS." + }, + "description": "Veuillez saisir vos informations de s\u00e9curit\u00e9 IP." + }, + "secure_tunneling": { + "description": "S\u00e9lectionnez la mani\u00e8re dont vous souhaitez configurer la s\u00e9curit\u00e9 IP de KNX.", + "menu_options": { + "secure_knxkeys": "Utiliser un fichier `.knxkeys` contenant les cl\u00e9s de s\u00e9curit\u00e9 IP", + "secure_tunnel_manual": "Configurer manuellement les cl\u00e9s de s\u00e9curit\u00e9 IP" + } + }, "tunnel": { "data": { + "gateway": "Connexion tunnel KNX", "host": "H\u00f4te", "port": "Port", "tunneling_type": "Type de tunnel KNX" @@ -114,7 +210,8 @@ "data_description": { "host": "Adresse IP de l'appareil de tunnel KNX/IP.", "port": "Port de l'appareil de tunnel KNX/IP." - } + }, + "description": "Veuillez s\u00e9lectionner une passerelle dans la liste." } } } diff --git a/homeassistant/components/livisi/translations/fr.json b/homeassistant/components/livisi/translations/fr.json new file mode 100644 index 00000000000..7824796490a --- /dev/null +++ b/homeassistant/components/livisi/translations/fr.json @@ -0,0 +1,16 @@ +{ + "config": { + "error": { + "cannot_connect": "\u00c9chec de connexion", + "wrong_password": "Le mot de passe est erron\u00e9." + }, + "step": { + "user": { + "data": { + "host": "Adresse IP", + "password": "Mot de passe" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mqtt/translations/en.json b/homeassistant/components/mqtt/translations/en.json index 00078b1082a..6ab75c6a30d 100644 --- a/homeassistant/components/mqtt/translations/en.json +++ b/homeassistant/components/mqtt/translations/en.json @@ -130,4 +130,4 @@ } } } -} +} \ No newline at end of file diff --git a/homeassistant/components/mqtt/translations/fr.json b/homeassistant/components/mqtt/translations/fr.json index 49d041bc0f8..c005f4ad787 100644 --- a/homeassistant/components/mqtt/translations/fr.json +++ b/homeassistant/components/mqtt/translations/fr.json @@ -13,6 +13,9 @@ "data": { "advanced_options": "Options avanc\u00e9es", "broker": "Broker", + "certificate": "T\u00e9l\u00e9verser le certificat de l\u2019autorit\u00e9 de certification personnalis\u00e9", + "client_cert": "T\u00e9l\u00e9verser le certificat client", + "client_key": "T\u00e9l\u00e9verser la cl\u00e9 priv\u00e9e", "discovery": "Activer la d\u00e9couverte", "password": "Mot de passe", "port": "Port", @@ -60,9 +63,7 @@ }, "options": { "error": { - "bad_birth": "Sujet de la naissance non valide.", "bad_discovery_prefix": "Pr\u00e9fixe de d\u00e9couverte non valide", - "bad_will": "Sujet du testament non valide.", "cannot_connect": "\u00c9chec de connexion" }, "step": { @@ -70,6 +71,9 @@ "data": { "advanced_options": "Options avanc\u00e9es", "broker": "Broker", + "certificate": "T\u00e9l\u00e9verser le certificat de l\u2019autorit\u00e9 de certification personnalis\u00e9", + "client_cert": "T\u00e9l\u00e9verser le certificat client", + "client_key": "T\u00e9l\u00e9verser la cl\u00e9 priv\u00e9e", "password": "Mot de passe", "port": "Port", "protocol": "Protocole MQTT", diff --git a/homeassistant/components/nibe_heatpump/translations/fr.json b/homeassistant/components/nibe_heatpump/translations/fr.json index 6c12361adc5..9a0b8f0954a 100644 --- a/homeassistant/components/nibe_heatpump/translations/fr.json +++ b/homeassistant/components/nibe_heatpump/translations/fr.json @@ -4,15 +4,36 @@ "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" }, "error": { + "model": "Le mod\u00e8le s\u00e9lectionn\u00e9 ne semble pas prendre en charge MODBUS40", "unknown": "Erreur inattendue" }, "step": { + "modbus": { + "data": { + "modbus_unit": "Identifiant d\u2019unit\u00e9 Modbus", + "modbus_url": "URL Modbus", + "model": "Mod\u00e8le de la pompe \u00e0 chaleur" + } + }, + "nibegw": { + "data": { + "ip_address": "Adresse distante", + "listening_port": "Port d\u2019\u00e9coute local", + "model": "Mod\u00e8le de la pompe \u00e0 chaleur", + "remote_read_port": "Port de lecture distant", + "remote_write_port": "Port d\u2019\u00e9criture distant" + } + }, "user": { "data": { "ip_address": "Adresse distante", "listening_port": "Port d'\u00e9coute local", "remote_read_port": "Port de lecture distant", "remote_write_port": "Port d'\u00e9criture distant" + }, + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" } } } diff --git a/homeassistant/components/openuv/translations/fr.json b/homeassistant/components/openuv/translations/fr.json index 527d4d3348e..5485f0b4ac6 100644 --- a/homeassistant/components/openuv/translations/fr.json +++ b/homeassistant/components/openuv/translations/fr.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9" + "already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9", + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" }, "error": { "invalid_api_key": "Cl\u00e9 d'API non valide" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "Cl\u00e9 d'API" + }, + "description": "Veuillez de nouveau saisir la cl\u00e9 d\u2019API pour {latitude}, {longitude}.", + "title": "R\u00e9-authentifier l'int\u00e9gration" + }, "user": { "data": { "api_key": "Cl\u00e9 d'API", diff --git a/homeassistant/components/pushbullet/translations/fr.json b/homeassistant/components/pushbullet/translations/fr.json new file mode 100644 index 00000000000..f17886f699e --- /dev/null +++ b/homeassistant/components/pushbullet/translations/fr.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_api_key": "Cl\u00e9 d'API non valide" + }, + "step": { + "user": { + "data": { + "api_key": "Cl\u00e9 d'API", + "name": "Nom" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/ca.json b/homeassistant/components/unifiprotect/translations/ca.json index 39e57c2ed49..225b35c6225 100644 --- a/homeassistant/components/unifiprotect/translations/ca.json +++ b/homeassistant/components/unifiprotect/translations/ca.json @@ -44,6 +44,16 @@ "issues": { "ea_warning": { "description": "Est\u00e0s utilitzant UniFi Protect v{version} que \u00e9s una versi\u00f3 d'acc\u00e9s anticipat. Les versions d'acc\u00e9s anticipat no s\u00f3n compatibles amb Home Assistant i poden fer que la teva integraci\u00f3 d'UniFi Protect s'espatlli o no funcioni correctament.", + "fix_flow": { + "step": { + "confirm": { + "title": "v{version} \u00e9s una versi\u00f3 d'acc\u00e9s anticipat" + }, + "start": { + "title": "v{version} \u00e9s una versi\u00f3 d'acc\u00e9s anticipat" + } + } + }, "title": "UniFi Protect v{version} \u00e9s una versi\u00f3 d'acc\u00e9s anticipat" } }, diff --git a/homeassistant/components/unifiprotect/translations/de.json b/homeassistant/components/unifiprotect/translations/de.json index e283364599d..d7c3e127b63 100644 --- a/homeassistant/components/unifiprotect/translations/de.json +++ b/homeassistant/components/unifiprotect/translations/de.json @@ -42,8 +42,24 @@ } }, "issues": { + "ea_setup_failed": { + "description": "Du verwendest v{version} von UniFi Protect, eine Early-Access-Version. Beim Versuch, die Integration zu laden, ist ein nicht behebbarer Fehler aufgetreten. Bitte f\u00fchre ein [Downgrade auf eine stabile Version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) von UniFi Protect durch, um die Integration weiterhin zu verwenden. \n\nFehler: {error}", + "title": "Einrichtungsfehler bei Verwendung der Early-Access-Version" + }, "ea_warning": { "description": "Du verwendest v{version} von UniFi Protect, einer Early-Access-Version. Early-Access-Versionen werden von Home Assistant nicht unterst\u00fctzt und k\u00f6nnen dazu f\u00fchren, dass deine UniFi Protect-Integration nicht oder nicht wie erwartet funktioniert.", + "fix_flow": { + "step": { + "confirm": { + "description": "M\u00f6chtest du wirklich nicht unterst\u00fctzte Versionen von UniFi Protect ausf\u00fchren? Dies kann dazu f\u00fchren, dass deine Home Assistant-Integration nicht mehr funktioniert.", + "title": "v{version} ist eine Early-Access-Version" + }, + "start": { + "description": "Du verwendest v{version} von UniFi Protect, eine Early-Access-Version. [Early-Access-Versionen werden von Home Assistant nicht unterst\u00fctzt](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) und es wird empfohlen, sobald wie m\u00f6glich zu einer stabilen Version zur\u00fcckzukehren. \n\nDurch das Absenden dieses Formulars hast du entweder [UniFi Protect heruntergestuft](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) oder du stimmst zu, eine nicht unterst\u00fctzte Version von UniFi Protect auszuf\u00fchren.", + "title": "v{version} ist eine Early-Access-Version" + } + } + }, "title": "UniFi Protect v{version} ist eine Early-Access-Version" } }, @@ -55,6 +71,7 @@ "init": { "data": { "all_updates": "Echtzeitmetriken (WARNUNG: Erh\u00f6ht die CPU-Auslastung erheblich)", + "allow_ea": "Early-Access-Versionen von Protect zulassen (WARNUNG: Markiert deine Integration als nicht unterst\u00fctzt)", "disable_rtsp": "RTSP-Stream deaktivieren", "ignored_devices": "Kommagetrennte Liste von MAC-Adressen von Ger\u00e4ten, die ignoriert werden sollen", "max_media": "Maximale Anzahl von Ereignissen, die f\u00fcr den Medienbrowser geladen werden (erh\u00f6ht die RAM-Nutzung)", diff --git a/homeassistant/components/unifiprotect/translations/en.json b/homeassistant/components/unifiprotect/translations/en.json index 65a398375fe..31b06ac9a17 100644 --- a/homeassistant/components/unifiprotect/translations/en.json +++ b/homeassistant/components/unifiprotect/translations/en.json @@ -47,6 +47,7 @@ "title": "Setup error using Early Access version" }, "ea_warning": { + "description": "You are using v{version} of UniFi Protect which is an Early Access version. Early Access versions are not supported by Home Assistant and may cause your UniFi Protect integration to break or not work as expected.", "fix_flow": { "step": { "confirm": { @@ -63,12 +64,16 @@ } }, "options": { + "error": { + "invalid_mac_list": "Must be a list of MAC addresses seperated by commas" + }, "step": { "init": { "data": { "all_updates": "Realtime metrics (WARNING: Greatly increases CPU usage)", "allow_ea": "Allow Early Access versions of Protect (WARNING: Will mark your integration as unsupported)", "disable_rtsp": "Disable the RTSP stream", + "ignored_devices": "Comma separated list of MAC addresses of devices to ignore", "max_media": "Max number of event to load for Media Browser (increases RAM usage)", "override_connection_host": "Override Connection Host" }, diff --git a/homeassistant/components/unifiprotect/translations/es.json b/homeassistant/components/unifiprotect/translations/es.json index d3a3f9a2329..5168bd988e2 100644 --- a/homeassistant/components/unifiprotect/translations/es.json +++ b/homeassistant/components/unifiprotect/translations/es.json @@ -42,8 +42,24 @@ } }, "issues": { + "ea_setup_failed": { + "description": "Est\u00e1s utilizando v{version} de UniFi Protect, que es una versi\u00f3n de Early Access. Se produjo un error irrecuperable al intentar cargar la integraci\u00f3n. Por favor, [cambia a una versi\u00f3n estable](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) de UniFi Protect para continuar usando la integraci\u00f3n. \n\nError: {error}", + "title": "Error de configuraci\u00f3n al usar la versi\u00f3n Early Access" + }, "ea_warning": { "description": "Est\u00e1s utilizando v{version} de UniFi Protect, que es una versi\u00f3n Early Access. Las versiones Early Access no son compatibles con Home Assistant y pueden causar que tu integraci\u00f3n con UniFi Protect no funcione por completo o como se esperaba.", + "fix_flow": { + "step": { + "confirm": { + "description": "\u00bfEst\u00e1s seguro de que deseas ejecutar versiones no compatibles de UniFi Protect? Esto puede causar que la integraci\u00f3n de Home Assistant se rompa.", + "title": "v{version} es una versi\u00f3n de Early Access" + }, + "start": { + "description": "Est\u00e1s utilizando v{version} de UniFi Protect, que es una versi\u00f3n Early Access. [Las versiones Early Access no son compatibles con Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) y se recomienda volver a una versi\u00f3n estable tan pronto como sea posible. \n\nAl enviar este formulario, tienes [UniFi Protect degradado](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) o aceptas ejecutar una versi\u00f3n no compatible de UniFi Protect.", + "title": "v{version} es una versi\u00f3n Early Access" + } + } + }, "title": "UniFi Protect v{version} es una versi\u00f3n Early Access" } }, @@ -55,6 +71,7 @@ "init": { "data": { "all_updates": "M\u00e9tricas en tiempo real (ADVERTENCIA: aumenta considerablemente el uso de la CPU)", + "allow_ea": "Permitir versiones Early Access de Protect (ADVERTENCIA: marcar\u00e1 tu integraci\u00f3n como no admitida)", "disable_rtsp": "Deshabilitar la transmisi\u00f3n RTSP", "ignored_devices": "Lista separada por comas de direcciones MAC de dispositivos para ignorar", "max_media": "N\u00famero m\u00e1ximo de eventos a cargar para el Navegador de Medios (aumenta el uso de RAM)", diff --git a/homeassistant/components/unifiprotect/translations/et.json b/homeassistant/components/unifiprotect/translations/et.json index 54225737c77..f6ef320e38e 100644 --- a/homeassistant/components/unifiprotect/translations/et.json +++ b/homeassistant/components/unifiprotect/translations/et.json @@ -42,8 +42,24 @@ } }, "issues": { + "ea_setup_failed": { + "description": "Kasutad UniFi Protecti v {version} mis on varajase juurdep\u00e4\u00e4su versioon. Sidumise laadimisel ilmnes parandamatu viga. Sidumise kasutamise j\u00e4tkamiseks [alanda UniFi Protecti stabiilsele versioonile](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect). \n\n Viga: {error}", + "title": "Varajase juurdep\u00e4\u00e4su versiooni h\u00e4\u00e4lestamise t\u00f5rge" + }, "ea_warning": { "description": "Kasutad UniFi Protecti v{version}. Home Assistant ei toeta varajase juurdep\u00e4\u00e4su versioone ja see v\u00f5ib p\u00f5hjustada UniFi Protecti sidumise katkemist v\u00f5i ei t\u00f6\u00f6ta see ootusp\u00e4raselt.", + "fix_flow": { + "step": { + "confirm": { + "description": "Kas oled kindel, et soovid k\u00e4itada UniFi Protecti toetamata versioone? See v\u00f5ib p\u00f5hjustada Home Assistanti sidumise katkemise.", + "title": "v {version} on varajase juurdep\u00e4\u00e4su versioon" + }, + "start": { + "description": "Kasutad UniFi Protecti v {version} mis on varajase juurdep\u00e4\u00e4su versioon. [Home Assistant ei toeta varajase juurdep\u00e4\u00e4su versioone](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) ja soovitatav on naasta stabiilsele versioonile niipea kui v\u00f5imalik. \n\n Selle vormi esitades oled kas [UniFi Protecti madalamale versioonile \u00fcle l\u00e4inud](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) v\u00f5i n\u00f5ustud kasutama UniFi Protecti toetamata versiooni.", + "title": "v {version} on varajase juurdep\u00e4\u00e4su versioon" + } + } + }, "title": "v{version} on UniFi Protecti varajase juurdep\u00e4\u00e4su versioon" } }, @@ -55,6 +71,7 @@ "init": { "data": { "all_updates": "Reaalajas m\u00f5\u00f5dikud (HOIATUS: suurendab oluliselt CPU kasutust)", + "allow_ea": "Protecti varajase juurdep\u00e4\u00e4su versioonide lubamine (HOIATUS: m\u00e4rgib sidumise mitte toetatuks)", "disable_rtsp": "Keela RTSP voog", "ignored_devices": "Komaga eraldatud loend nende seadmete MAC-aadressidest mida eirata", "max_media": "Meediumibrauserisse laaditavate s\u00fcndmuste maksimaalne arv (suurendab RAM-i kasutamist)", diff --git a/homeassistant/components/unifiprotect/translations/hu.json b/homeassistant/components/unifiprotect/translations/hu.json index 477374a074f..cbc240cae13 100644 --- a/homeassistant/components/unifiprotect/translations/hu.json +++ b/homeassistant/components/unifiprotect/translations/hu.json @@ -42,8 +42,24 @@ } }, "issues": { + "ea_setup_failed": { + "description": "Az UniFi Protect {version}. verzi\u00f3j\u00e1t haszn\u00e1lja, amely egy korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3. Helyrehozhatatlan hiba t\u00f6rt\u00e9nt az integr\u00e1ci\u00f3 bet\u00f6lt\u00e9se k\u00f6zben. K\u00e9rem, [haszn\u00e1ljon stabil verzi\u00f3t](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) az integr\u00e1ci\u00f3 tov\u00e1bbi haszn\u00e1lat\u00e1hoz.\n\nHiba: {error}", + "title": "Be\u00e1ll\u00edt\u00e1si hiba a korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3 haszn\u00e1lat\u00e1val" + }, "ea_warning": { "description": "\u00d6n az UniFi Protect {version} verzi\u00f3j\u00e1t haszn\u00e1lja. A korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3kat Home Assistant nem t\u00e1mogatja, \u00e9s az UniFi Protect integr\u00e1ci\u00f3ja le\u00e1llhat, vagy nem az elv\u00e1rt m\u00f3don m\u0171k\u00f6dhet.", + "fix_flow": { + "step": { + "confirm": { + "description": "Biztos, hogy az UniFi Protect nem t\u00e1mogatott verzi\u00f3it szeretn\u00e9 futtatni? Ez a Home Assistant integr\u00e1ci\u00f3j\u00e1nak meghib\u00e1sod\u00e1s\u00e1t okozhatja.", + "title": "{version}. verzi\u00f3 egy korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3" + }, + "start": { + "description": "Az UniFi Protect {version}. verzi\u00f3j\u00e1t haszn\u00e1lja, amely egy korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3. [A korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3kat a Home Assistant nem t\u00e1mogatja] (https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) \u00e9s aj\u00e1nlott a lehet\u0151 leghamarabb visszat\u00e9rni a stabil verzi\u00f3j\u00e1hoz.", + "title": "{version}. verzi\u00f3 egy korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3" + } + } + }, "title": "{version} UniFi Protect korai hozz\u00e1f\u00e9r\u00e9si verzi\u00f3" } }, @@ -55,6 +71,7 @@ "init": { "data": { "all_updates": "Val\u00f3s idej\u0171 m\u00e9r\u0151sz\u00e1mok (FIGYELEM: nagym\u00e9rt\u00e9kben n\u00f6veli a CPU terhel\u00e9st)", + "allow_ea": "A Protect korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3inak enged\u00e9lyez\u00e9se (FIGYELMEZTET\u00c9S: Az integr\u00e1ci\u00f3t nem t\u00e1mogatottk\u00e9nt jel\u00f6li meg)", "disable_rtsp": "Az RTSP adatfolyam letilt\u00e1sa", "ignored_devices": "A figyelmen k\u00edv\u00fcl hagyand\u00f3 eszk\u00f6z\u00f6k MAC-c\u00edm\u00e9nek vessz\u0151vel elv\u00e1lasztott list\u00e1ja", "max_media": "A m\u00e9diab\u00f6ng\u00e9sz\u0151be bet\u00f6ltend\u0151 esem\u00e9nyek maxim\u00e1lis sz\u00e1ma (n\u00f6veli a RAM-haszn\u00e1latot)", diff --git a/homeassistant/components/unifiprotect/translations/id.json b/homeassistant/components/unifiprotect/translations/id.json index cae7dfd3253..620814f4247 100644 --- a/homeassistant/components/unifiprotect/translations/id.json +++ b/homeassistant/components/unifiprotect/translations/id.json @@ -42,8 +42,21 @@ } }, "issues": { + "ea_setup_failed": { + "title": "Kesalahan penyiapan menggunakan versi Early Access" + }, "ea_warning": { "description": "Anda menggunakan UniFi Protect v{version} yang merupakan versi Early Access. Versi Early Access tidak didukung oleh Home Assistant dan dapat menyebabkan integrasi UniFi Protect rusak atau tidak berfungsi seperti yang diharapkan.", + "fix_flow": { + "step": { + "confirm": { + "title": "v{version} adalah versi Early Access" + }, + "start": { + "title": "v{version} adalah versi Early Access" + } + } + }, "title": "UniFi Protect v{version} adalah versi Early Access" } }, diff --git a/homeassistant/components/unifiprotect/translations/no.json b/homeassistant/components/unifiprotect/translations/no.json index 1ac28d4221a..d6e453b243a 100644 --- a/homeassistant/components/unifiprotect/translations/no.json +++ b/homeassistant/components/unifiprotect/translations/no.json @@ -42,8 +42,24 @@ } }, "issues": { + "ea_setup_failed": { + "description": "Du bruker v {version} av UniFi Protect som er en tidlig tilgangsversjon. Det oppstod en uopprettelig feil under fors\u00f8k p\u00e5 \u00e5 laste integrasjonen. Vennligst [nedgrader til en stabil versjon](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) av UniFi Protect for \u00e5 fortsette \u00e5 bruke integrasjonen. \n\n Feil: {error}", + "title": "Konfigurasjonsfeil ved bruk av tidlig tilgangsversjon" + }, "ea_warning": { "description": "Du bruker v {version} av UniFi Protect som er en tidlig tilgangsversjon. Early Access-versjoner st\u00f8ttes ikke av Home Assistant og kan f\u00f8re til at UniFi Protect-integrasjonen din g\u00e5r i stykker eller ikke fungerer som forventet.", + "fix_flow": { + "step": { + "confirm": { + "description": "Er du sikker p\u00e5 at du vil kj\u00f8re versjoner av UniFi Protect som ikke st\u00f8ttes? Dette kan f\u00f8re til at Home Assistant-integrasjonen din g\u00e5r i stykker.", + "title": "v {version} er en tidlig tilgangsversjon" + }, + "start": { + "description": "Du bruker v {version} av UniFi Protect som er en tidlig tilgangsversjon. [Early Access-versjoner st\u00f8ttes ikke av Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access), og det anbefales \u00e5 g\u00e5 tilbake til en stabil utgivelse s\u00e5 snart som mulig. \n\n Ved \u00e5 sende inn dette skjemaet har du enten [nedgradert UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) eller du godtar \u00e5 kj\u00f8re en ust\u00f8ttet versjon av UniFi Protect.", + "title": "v {version} er en tidlig tilgangsversjon" + } + } + }, "title": "UniFi Protect v {version} er en versjon med tidlig tilgang" } }, @@ -55,6 +71,7 @@ "init": { "data": { "all_updates": "Sanntidsm\u00e5linger (ADVARSEL: \u00d8ker CPU-bruken betraktelig)", + "allow_ea": "Tillat tidlig tilgangsversjoner av Protect (ADVARSEL: Vil merke integrasjonen din som ikke st\u00f8ttet)", "disable_rtsp": "Deaktiver RTSP-str\u00f8mmen", "ignored_devices": "Kommadelt liste over MAC-adresser til enheter som skal ignoreres", "max_media": "Maks antall hendelser som skal lastes for medienettleseren (\u00f8ker RAM-bruken)", diff --git a/homeassistant/components/unifiprotect/translations/pt-BR.json b/homeassistant/components/unifiprotect/translations/pt-BR.json index 441ef2e7ab6..b0e58459815 100644 --- a/homeassistant/components/unifiprotect/translations/pt-BR.json +++ b/homeassistant/components/unifiprotect/translations/pt-BR.json @@ -42,8 +42,24 @@ } }, "issues": { + "ea_setup_failed": { + "description": "Voc\u00ea est\u00e1 usando v {version} do UniFi Protect, que \u00e9 uma vers\u00e3o de acesso antecipado. Ocorreu um erro irrecuper\u00e1vel ao tentar carregar a integra\u00e7\u00e3o. Fa\u00e7a [downgrade para uma vers\u00e3o est\u00e1vel](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) do UniFi Protect para continuar usando a integra\u00e7\u00e3o. \n\n Erro: {error}", + "title": "Erro de configura\u00e7\u00e3o usando a vers\u00e3o de acesso antecipado" + }, "ea_warning": { "description": "Voc\u00ea est\u00e1 usando v {version} do UniFi Protect, que \u00e9 uma vers\u00e3o de acesso antecipado. As vers\u00f5es de acesso antecipado n\u00e3o s\u00e3o suportadas pelo Home Assistant e podem fazer com que a integra\u00e7\u00e3o do UniFi Protect seja interrompida ou n\u00e3o funcione conforme o esperado.", + "fix_flow": { + "step": { + "confirm": { + "description": "Tem certeza de que deseja executar vers\u00f5es n\u00e3o suportadas do UniFi Protect? Isso pode fazer com que a integra\u00e7\u00e3o do Home Assistant seja interrompida.", + "title": "v {version} \u00e9 uma vers\u00e3o de acesso antecipado" + }, + "start": { + "description": "Voc\u00ea est\u00e1 usando v {version} do UniFi Protect, que \u00e9 uma vers\u00e3o de acesso antecipado. [Vers\u00f5es de acesso antecipado n\u00e3o s\u00e3o suportadas pelo Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) e \u00e9 recomend\u00e1vel voltar para uma vers\u00e3o est\u00e1vel assim que poss\u00edvel. \n\n Ao enviar este formul\u00e1rio, voc\u00ea fez o [downgrade do UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) ou concorda em executar uma vers\u00e3o n\u00e3o suportada do UniFi Protect.", + "title": "v {version} \u00e9 uma vers\u00e3o de acesso antecipado" + } + } + }, "title": "UniFi Protect v {version} \u00e9 uma vers\u00e3o de acesso antecipado" } }, @@ -55,6 +71,7 @@ "init": { "data": { "all_updates": "M\u00e9tricas em tempo real (AVISO: aumenta muito o uso da CPU)", + "allow_ea": "Permitir vers\u00f5es de acesso antecipado do Protect (AVISO: marcar\u00e1 sua integra\u00e7\u00e3o como n\u00e3o suportada)", "disable_rtsp": "Desativar o fluxo RTSP", "ignored_devices": "Lista separada por v\u00edrgulas de endere\u00e7os MAC de dispositivos a serem ignorados", "max_media": "N\u00famero m\u00e1ximo de eventos a serem carregados para o Media Browser (aumenta o uso de RAM)", diff --git a/homeassistant/components/unifiprotect/translations/zh-Hant.json b/homeassistant/components/unifiprotect/translations/zh-Hant.json index ce1498044f3..c358ea9dec2 100644 --- a/homeassistant/components/unifiprotect/translations/zh-Hant.json +++ b/homeassistant/components/unifiprotect/translations/zh-Hant.json @@ -42,8 +42,24 @@ } }, "issues": { + "ea_setup_failed": { + "description": "\u6b63\u5728\u4f7f\u7528\u7684 UniFi Protect v{version} \u7248\u6436\u5148\u9ad4\u9a57\u7248\u3002\u5617\u8a66\u8f09\u5165\u6574\u5408\u6642\u767c\u751f\u4e0d\u53ef\u6062\u5fa9\u7684\u932f\u8aa4\u3002\u8acb[\u964d\u7d1a\u81f3\u7a69\u5b9a\u7248\u672c](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) \u4e4b UniFi Protect \u4ee5\u7e7c\u7e8c\u4f7f\u7528\u6b64\u6574\u5408\u3002\n\n\u932f\u8aa4\uff1a{error}", + "title": "\u4f7f\u7528\u6436\u5148\u9ad4\u9a57\u7248\u8a2d\u5b9a\u932f\u8aa4" + }, "ea_warning": { "description": "\u6b63\u5728\u4f7f\u7528\u7684 UniFi Protect {version} \u7248\u3001\u70ba Home Assistant \u4e0d\u652f\u63f4\u7684\u6436\u5148\u9ad4\u9a57\u7248\u672c\uff0c\u53ef\u80fd\u6703\u5c0e\u81f4 UniFi Protect \u6574\u5408\u51fa\u73fe\u554f\u984c\u3001\u6216\u7121\u6cd5\u6b63\u5e38\u5de5\u4f5c\u3002", + "fix_flow": { + "step": { + "confirm": { + "description": "\u78ba\u5b9a\u8981\u4f7f\u7528\u4e0d\u652f\u63f4\u7684 UniFi Protect \u7248\u672c\uff1f\u53ef\u80fd\u6703\u5c0e\u81f4 Home Assistant \u6574\u5408\u4e0d\u7a69\u5b9a\u3002", + "title": "v{version} \u7248\u70ba\u6436\u5148\u9ad4\u9a57\u7248" + }, + "start": { + "description": "\u6b63\u5728\u4f7f\u7528\u7684 UniFi Protect v{version} \u7248\u6436\u5148\u9ad4\u9a57\u7248\u3002 [Home Assistant \u4e0d\u652f\u63f4\u6436\u5148\u9ad4\u9a57\u7248] (https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access)\u3001\u4e26\u5efa\u8b70\u76e1\u53ef\u80fd\u9000\u56de\u81f3\u7a69\u5b9a\u7248\u672c\u3002\n\n\u50b3\u9001\u6b64\u8868\u683c\u8868\u793a [\u5df2\u964d\u7d1a UniFi Protect \u7248\u672c] (https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) \u6216\u540c\u610f\u4f7f\u7528\u4e0d\u652f\u63f4\u7684 UniFi Protect \u7248\u672c\u3002", + "title": "v{version} \u7248\u70ba\u6436\u5148\u9ad4\u9a57\u7248" + } + } + }, "title": "UniFi Protect {version} \u7248\u70ba\u6436\u5148\u9ad4\u9a57\u7248" } }, @@ -55,6 +71,7 @@ "init": { "data": { "all_updates": "\u5373\u6642\u6307\u6a19\uff08\u8b66\u544a\uff1a\u5927\u91cf\u63d0\u5347 CPU \u4f7f\u7528\u7387\uff09", + "allow_ea": "\u5141\u8a31\u4f7f\u7528\u6436\u5148\u9ad4\u9a57\u7248 Protect\uff08\u8b66\u544a\uff1a\u53ef\u80fd\u6703\u5c0e\u81f4\u6574\u5408\u8b8a\u70ba\u4e0d\u652f\u63f4\uff09)", "disable_rtsp": "\u95dc\u9589 RTSP \u4e32\u6d41", "ignored_devices": "\u4ee5\u9017\u865f\u5206\u9694\u7684\u5ffd\u7565 MAC \u4f4d\u5740\u5217\u8868", "max_media": "\u5a92\u9ad4\u700f\u89bd\u5668\u6700\u9ad8\u8f09\u5165\u4e8b\u4ef6\u6578\uff08\u589e\u52a0\u8a18\u61b6\u9ad4\u4f7f\u7528\uff09", diff --git a/homeassistant/components/utility_meter/translations/ca.json b/homeassistant/components/utility_meter/translations/ca.json index 6781fc9533c..d48052eaf33 100644 --- a/homeassistant/components/utility_meter/translations/ca.json +++ b/homeassistant/components/utility_meter/translations/ca.json @@ -12,7 +12,7 @@ "tariffs": "Tarifes suportades" }, "data_description": { - "delta_values": "Activa-ho si els les lectures s\u00f3n valors delta (actual - anterior) en lloc de valors absoluts.", + "delta_values": "Activa-ho si les lectures s\u00f3n valors delta des de l'\u00faltima lectura en lloc de valors absoluts.", "net_consumption": "Activa-ho si \u00e9s un comptador net, \u00e9s a dir, pot augmentar i disminuir.", "offset": "Despla\u00e7a el dia de restabliment mensual del comptador.", "tariffs": "Llista de tarifes admeses, deixa-la en blanc si utilitzes una \u00fanica tarifa." diff --git a/homeassistant/components/zamg/translations/fr.json b/homeassistant/components/zamg/translations/fr.json index 4e3c83100cd..f0ce4b1170b 100644 --- a/homeassistant/components/zamg/translations/fr.json +++ b/homeassistant/components/zamg/translations/fr.json @@ -7,6 +7,11 @@ "error": { "cannot_connect": "\u00c9chec de connexion" }, - "flow_title": "{name}" + "flow_title": "{name}", + "step": { + "user": { + "description": "Configurez ZAMG pour l\u2019int\u00e9grer \u00e0 Home Assistant." + } + } } } \ No newline at end of file From 9c0a3cdae0c38a70bf924f2e410df70652ad69f4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Nov 2022 21:18:10 -0600 Subject: [PATCH 0416/1033] Fix homekit_controller comment (#82111) --- homeassistant/components/homekit_controller/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/homekit_controller/const.py b/homeassistant/components/homekit_controller/const.py index 7ed844ae9ae..8b6a3e5fd30 100644 --- a/homeassistant/components/homekit_controller/const.py +++ b/homeassistant/components/homekit_controller/const.py @@ -108,7 +108,7 @@ STARTUP_EXCEPTIONS = ( AccessoryDisconnectedError, ) -# 10 seconds was chosen because its soon enough +# 10 seconds was chosen because it is soon enough # for most state changes to happen but not too # long that the BLE connection is dropped. It # also happens to be the same value used by From f96a4e2d0f2e943377fc0a43bf8b6571617f0602 Mon Sep 17 00:00:00 2001 From: Andrew Berry Date: Mon, 14 Nov 2022 22:35:10 -0500 Subject: [PATCH 0417/1033] Fix HomeKit logging None instead of the linked battery sensor (#81860) --- homeassistant/components/homekit/accessories.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/homekit/accessories.py b/homeassistant/components/homekit/accessories.py index 3832d9d31c2..75b122dfb94 100644 --- a/homeassistant/components/homekit/accessories.py +++ b/homeassistant/components/homekit/accessories.py @@ -358,12 +358,12 @@ class HomeAccessory(Accessory): # type: ignore[misc] if state is not None: battery_found = state.state else: - self.linked_battery_sensor = None _LOGGER.warning( "%s: Battery sensor state missing: %s", self.entity_id, self.linked_battery_sensor, ) + self.linked_battery_sensor = None if not battery_found: return From 6bab63fb0a11e087ef294518137c9a32f2506f46 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Tue, 15 Nov 2022 12:42:40 +0800 Subject: [PATCH 0418/1033] Redact more credentials in stream URL query params (#82089) Co-authored-by: Allen Porter --- homeassistant/components/stream/__init__.py | 21 ++++++++------- tests/components/stream/test_worker.py | 29 ++++++++++++++++----- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index 7e6f663fc28..acc242006ca 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -20,7 +20,6 @@ import asyncio from collections.abc import Callable, Mapping import copy import logging -import re import secrets import threading import time @@ -28,6 +27,7 @@ from types import MappingProxyType from typing import TYPE_CHECKING, Any, Final, cast import voluptuous as vol +from yarl import URL from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.core import Event, HomeAssistant, callback @@ -91,17 +91,18 @@ __all__ = [ _LOGGER = logging.getLogger(__name__) -STREAM_SOURCE_REDACT_PATTERN = [ - (re.compile(r"//.*:.*@"), "//****:****@"), - (re.compile(r"\?auth=.*"), "?auth=****"), -] - -def redact_credentials(data: str) -> str: +def redact_credentials(url: str) -> str: """Redact credentials from string data.""" - for (pattern, repl) in STREAM_SOURCE_REDACT_PATTERN: - data = pattern.sub(repl, data) - return data + yurl = URL(url) + if yurl.user is not None: + yurl = yurl.with_user("****") + if yurl.password is not None: + yurl = yurl.with_password("****") + redacted_query_params = dict.fromkeys( + {"auth", "user", "password"} & yurl.query.keys(), "****" + ) + return str(yurl.update_query(redacted_query_params)) def create_stream( diff --git a/tests/components/stream/test_worker.py b/tests/components/stream/test_worker.py index e9f5769ddf1..a5a1f00d90a 100644 --- a/tests/components/stream/test_worker.py +++ b/tests/components/stream/test_worker.py @@ -727,11 +727,29 @@ async def test_update_stream_source(hass): await stream.stop() -async def test_worker_log(hass, caplog): +test_worker_log_cases = ( + ("https://abcd:efgh@foo.bar", "https://****:****@foo.bar"), + ( + "https://foo.bar/baz?user=abcd&password=efgh", + "https://foo.bar/baz?user=****&password=****", + ), + ( + "https://foo.bar/baz?param1=abcd¶m2=efgh", + "https://foo.bar/baz?param1=abcd¶m2=efgh", + ), + ( + "https://foo.bar/baz?param1=abcd&password=efgh", + "https://foo.bar/baz?param1=abcd&password=****", + ), +) + + +@pytest.mark.parametrize("stream_url, redacted_url", test_worker_log_cases) +async def test_worker_log(hass, caplog, stream_url, redacted_url): """Test that the worker logs the url without username and password.""" stream = Stream( hass, - "https://abcd:efgh@foo.bar", + stream_url, {}, hass.data[DOMAIN][ATTR_SETTINGS], dynamic_stream_settings(), @@ -740,13 +758,12 @@ async def test_worker_log(hass, caplog): with patch("av.open") as av_open, pytest.raises(StreamWorkerError) as err: av_open.side_effect = av.error.InvalidDataError(-2, "error") - run_worker(hass, stream, "https://abcd:efgh@foo.bar") + run_worker(hass, stream, stream_url) await hass.async_block_till_done() assert ( - str(err.value) - == "Error opening stream (ERRORTYPE_-2, error) https://****:****@foo.bar" + str(err.value) == f"Error opening stream (ERRORTYPE_-2, error) {redacted_url}" ) - assert "https://abcd:efgh@foo.bar" not in caplog.text + assert stream_url not in caplog.text @pytest.fixture From 6975186f2ffb02160413c7068c090522aaab2922 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 15 Nov 2022 12:48:02 +0100 Subject: [PATCH 0419/1033] Improve MQTT type hints / refactor part 10 - vacuum (#81253) * Improve type hints vacuum __init__ and schema * Improve type hints and refactor templates legacy * Improve type hints state vacuum * Add hint for template parameters * Apply suggestions from code review Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> * Some corrections * Remove stale constant * Use bitwise and * Follow-up comment * Remove incorrect type hint * Remove asserts * Cleanup asserts and reduce code Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- homeassistant/components/mqtt/mixins.py | 2 +- .../components/mqtt/vacuum/__init__.py | 22 +- .../components/mqtt/vacuum/schema.py | 25 +- .../components/mqtt/vacuum/schema_legacy.py | 278 +++++++++--------- .../components/mqtt/vacuum/schema_state.py | 199 +++++++------ 5 files changed, 275 insertions(+), 251 deletions(-) diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 6b181f2e4b5..400413c73d7 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -1107,7 +1107,7 @@ class MqttEntity( payload: PublishPayloadType, qos: int = 0, retain: bool = False, - encoding: str = DEFAULT_ENCODING, + encoding: str | None = DEFAULT_ENCODING, ) -> None: """Publish message to an MQTT topic.""" log_message(self.hass, self.entity_id, topic, payload, qos, retain) diff --git a/homeassistant/components/mqtt/vacuum/__init__.py b/homeassistant/components/mqtt/vacuum/__init__.py index abab55c632c..98183a92911 100644 --- a/homeassistant/components/mqtt/vacuum/__init__.py +++ b/homeassistant/components/mqtt/vacuum/__init__.py @@ -11,8 +11,9 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from ..const import CONF_SCHEMA from ..mixins import async_setup_entry_helper, async_setup_platform_helper -from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE +from .schema import LEGACY, MQTT_VACUUM_SCHEMA, STATE from .schema_legacy import ( DISCOVERY_SCHEMA_LEGACY, PLATFORM_SCHEMA_LEGACY, @@ -27,26 +28,29 @@ from .schema_state import ( ) -def validate_mqtt_vacuum_discovery(value): +def validate_mqtt_vacuum_discovery(config_value: ConfigType) -> ConfigType: """Validate MQTT vacuum schema.""" schemas = {LEGACY: DISCOVERY_SCHEMA_LEGACY, STATE: DISCOVERY_SCHEMA_STATE} - return schemas[value[CONF_SCHEMA]](value) + config: ConfigType = schemas[config_value[CONF_SCHEMA]](config_value) + return config # Configuring MQTT Vacuums under the vacuum platform key is deprecated in HA Core 2022.6 -def validate_mqtt_vacuum(value): +def validate_mqtt_vacuum(config_value: ConfigType) -> ConfigType: """Validate MQTT vacuum schema (deprecated).""" schemas = {LEGACY: PLATFORM_SCHEMA_LEGACY, STATE: PLATFORM_SCHEMA_STATE} - return schemas[value[CONF_SCHEMA]](value) + config: ConfigType = schemas[config_value[CONF_SCHEMA]](config_value) + return config -def validate_mqtt_vacuum_modern(value): +def validate_mqtt_vacuum_modern(config_value: ConfigType) -> ConfigType: """Validate MQTT vacuum modern schema.""" schemas = { LEGACY: PLATFORM_SCHEMA_LEGACY_MODERN, STATE: PLATFORM_SCHEMA_STATE_MODERN, } - return schemas[value[CONF_SCHEMA]](value) + config: ConfigType = schemas[config_value[CONF_SCHEMA]](config_value) + return config DISCOVERY_SCHEMA = vol.All( @@ -96,8 +100,8 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, - discovery_data: dict | None = None, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, ) -> None: """Set up the MQTT vacuum.""" setup_entity = { diff --git a/homeassistant/components/mqtt/vacuum/schema.py b/homeassistant/components/mqtt/vacuum/schema.py index d2ed91fa631..20b7bced99d 100644 --- a/homeassistant/components/mqtt/vacuum/schema.py +++ b/homeassistant/components/mqtt/vacuum/schema.py @@ -1,6 +1,10 @@ """Shared schema code.""" +from __future__ import annotations + import voluptuous as vol +from homeassistant.components.vacuum import VacuumEntityFeature + from ..const import CONF_SCHEMA LEGACY = "legacy" @@ -15,18 +19,23 @@ MQTT_VACUUM_SCHEMA = vol.Schema( ) -def services_to_strings(services, service_to_string): +def services_to_strings( + services: VacuumEntityFeature | int, + service_to_string: dict[VacuumEntityFeature, str], +) -> list[str]: """Convert SUPPORT_* service bitmask to list of service strings.""" - strings = [] - for service in service_to_string: - if service & services: - strings.append(service_to_string[service]) - return strings + return [ + service_to_string[service] + for service in service_to_string + if service & services + ] -def strings_to_services(strings, string_to_service): +def strings_to_services( + strings: list[str], string_to_service: dict[str, VacuumEntityFeature] +) -> VacuumEntityFeature | int: """Convert service strings to SUPPORT_* service bitmask.""" - services = 0 + services: VacuumEntityFeature | int = 0 for string in strings: services |= string_to_service[string] return services diff --git a/homeassistant/components/mqtt/vacuum/schema_legacy.py b/homeassistant/components/mqtt/vacuum/schema_legacy.py index 4425a3775d9..68618aba20c 100644 --- a/homeassistant/components/mqtt/vacuum/schema_legacy.py +++ b/homeassistant/components/mqtt/vacuum/schema_legacy.py @@ -1,4 +1,9 @@ """Support for Legacy MQTT vacuum.""" +from __future__ import annotations + +from collections.abc import Callable +from typing import Any + import voluptuous as vol from homeassistant.components.vacuum import ( @@ -8,18 +13,26 @@ from homeassistant.components.vacuum import ( VacuumEntity, VacuumEntityFeature, ) +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_SUPPORTED_FEATURES, CONF_NAME -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.icon import icon_for_battery_level from homeassistant.helpers.json import json_dumps +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from .. import subscription from ..config import MQTT_BASE_SCHEMA from ..const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema -from ..models import MqttValueTemplate, PayloadSentinel, ReceiveMessage +from ..models import ( + MqttValueTemplate, + PayloadSentinel, + ReceiveMessage, + ReceivePayloadType, +) from ..util import get_mqtt_data, valid_publish_topic from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services @@ -158,9 +171,45 @@ DISCOVERY_SCHEMA_LEGACY = PLATFORM_SCHEMA_LEGACY_MODERN.extend( ) +_COMMANDS = { + VacuumEntityFeature.TURN_ON: { + "payload": CONF_PAYLOAD_TURN_ON, + "status": "Cleaning", + }, + VacuumEntityFeature.TURN_OFF: { + "payload": CONF_PAYLOAD_TURN_OFF, + "status": "Turning Off", + }, + VacuumEntityFeature.STOP: { + "payload": CONF_PAYLOAD_STOP, + "status": "Stopping the current task", + }, + VacuumEntityFeature.CLEAN_SPOT: { + "payload": CONF_PAYLOAD_CLEAN_SPOT, + "status": "Cleaning spot", + }, + VacuumEntityFeature.LOCATE: { + "payload": CONF_PAYLOAD_LOCATE, + "status": "Hi, I'm over here!", + }, + VacuumEntityFeature.PAUSE: { + "payload": CONF_PAYLOAD_START_PAUSE, + "status": "Pausing/Resuming cleaning...", + }, + VacuumEntityFeature.RETURN_HOME: { + "payload": CONF_PAYLOAD_RETURN_TO_BASE, + "status": "Returning home...", + }, +} + + async def async_setup_entity_legacy( - hass, config, async_add_entities, config_entry, discovery_data -): + hass: HomeAssistant, + config: ConfigType, + async_add_entities: AddEntitiesCallback, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, +) -> None: """Set up a MQTT Vacuum Legacy.""" async_add_entities([MqttVacuum(hass, config, config_entry, discovery_data)]) @@ -171,24 +220,42 @@ class MqttVacuum(MqttEntity, VacuumEntity): _entity_id_format = ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_LEGACY_VACUUM_ATTRIBUTES_BLOCKED - def __init__(self, hass, config, config_entry, discovery_data): + _encoding: str | None + _qos: bool + _retain: bool + _payloads: dict[str, str] + _send_command_topic: str | None + _set_fan_speed_topic: str | None + _state_topics: dict[str, str | None] + _templates: dict[ + str, Callable[[ReceivePayloadType, PayloadSentinel], ReceivePayloadType] + ] + + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the vacuum.""" self._attr_battery_level = 0 self._attr_is_on = False self._attr_fan_speed = "unknown" self._charging = False + self._cleaning = False self._docked = False - self._error = None + self._error: str | None = None MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA_LEGACY - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" supported_feature_strings = config[CONF_SUPPORTED_FEATURES] self._attr_supported_features = strings_to_services( @@ -204,7 +271,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): self._send_command_topic = config.get(CONF_SEND_COMMAND_TOPIC) self._payloads = { - key: config.get(key) + key: config[key] for key in ( CONF_PAYLOAD_TURN_ON, CONF_PAYLOAD_TURN_OFF, @@ -227,7 +294,9 @@ class MqttVacuum(MqttEntity, VacuumEntity): ) } self._templates = { - key: config.get(key) + key: MqttValueTemplate( + config[key], entity=self + ).async_render_with_possible_json_value for key in ( CONF_BATTERY_LEVEL_TEMPLATE, CONF_CHARGING_TEMPLATE, @@ -236,13 +305,11 @@ class MqttVacuum(MqttEntity, VacuumEntity): CONF_ERROR_TEMPLATE, CONF_FAN_SPEED_TEMPLATE, ) + if key in config } - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" - for tpl in self._templates.values(): - if tpl is not None: - tpl = MqttValueTemplate(tpl, entity=self) @callback @log_messages(self.hass, self.entity_id) @@ -250,11 +317,9 @@ class MqttVacuum(MqttEntity, VacuumEntity): """Handle new MQTT message.""" if ( msg.topic == self._state_topics[CONF_BATTERY_LEVEL_TOPIC] - and self._templates[CONF_BATTERY_LEVEL_TEMPLATE] + and CONF_BATTERY_LEVEL_TEMPLATE in self._config ): - battery_level = self._templates[ - CONF_BATTERY_LEVEL_TEMPLATE - ].async_render_with_possible_json_value( + battery_level = self._templates[CONF_BATTERY_LEVEL_TEMPLATE]( msg.payload, PayloadSentinel.DEFAULT ) if battery_level and battery_level is not PayloadSentinel.DEFAULT: @@ -262,11 +327,9 @@ class MqttVacuum(MqttEntity, VacuumEntity): if ( msg.topic == self._state_topics[CONF_CHARGING_TOPIC] - and self._templates[CONF_CHARGING_TEMPLATE] + and CONF_CHARGING_TEMPLATE in self._templates ): - charging = self._templates[ - CONF_CHARGING_TEMPLATE - ].async_render_with_possible_json_value( + charging = self._templates[CONF_CHARGING_TEMPLATE]( msg.payload, PayloadSentinel.DEFAULT ) if charging and charging is not PayloadSentinel.DEFAULT: @@ -274,11 +337,9 @@ class MqttVacuum(MqttEntity, VacuumEntity): if ( msg.topic == self._state_topics[CONF_CLEANING_TOPIC] - and self._templates[CONF_CLEANING_TEMPLATE] + and CONF_CLEANING_TEMPLATE in self._config ): - cleaning = self._templates[ - CONF_CLEANING_TEMPLATE - ].async_render_with_possible_json_value( + cleaning = self._templates[CONF_CLEANING_TEMPLATE]( msg.payload, PayloadSentinel.DEFAULT ) if cleaning and cleaning is not PayloadSentinel.DEFAULT: @@ -286,11 +347,9 @@ class MqttVacuum(MqttEntity, VacuumEntity): if ( msg.topic == self._state_topics[CONF_DOCKED_TOPIC] - and self._templates[CONF_DOCKED_TEMPLATE] + and CONF_DOCKED_TEMPLATE in self._config ): - docked = self._templates[ - CONF_DOCKED_TEMPLATE - ].async_render_with_possible_json_value( + docked = self._templates[CONF_DOCKED_TEMPLATE]( msg.payload, PayloadSentinel.DEFAULT ) if docked and docked is not PayloadSentinel.DEFAULT: @@ -298,11 +357,9 @@ class MqttVacuum(MqttEntity, VacuumEntity): if ( msg.topic == self._state_topics[CONF_ERROR_TOPIC] - and self._templates[CONF_ERROR_TEMPLATE] + and CONF_ERROR_TEMPLATE in self._config ): - error = self._templates[ - CONF_ERROR_TEMPLATE - ].async_render_with_possible_json_value( + error = self._templates[CONF_ERROR_TEMPLATE]( msg.payload, PayloadSentinel.DEFAULT ) if error is not PayloadSentinel.DEFAULT: @@ -322,15 +379,13 @@ class MqttVacuum(MqttEntity, VacuumEntity): if ( msg.topic == self._state_topics[CONF_FAN_SPEED_TOPIC] - and self._templates[CONF_FAN_SPEED_TEMPLATE] + and CONF_FAN_SPEED_TEMPLATE in self._config ): - fan_speed = self._templates[ - CONF_FAN_SPEED_TEMPLATE - ].async_render_with_possible_json_value( + fan_speed = self._templates[CONF_FAN_SPEED_TEMPLATE]( msg.payload, PayloadSentinel.DEFAULT ) if fan_speed and fan_speed is not PayloadSentinel.DEFAULT: - self._attr_fan_speed = fan_speed + self._attr_fan_speed = str(fan_speed) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) @@ -349,12 +404,12 @@ class MqttVacuum(MqttEntity, VacuumEntity): }, ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) @property - def battery_icon(self): + def battery_icon(self) -> str: """Return the battery icon for the vacuum cleaner. No need to check VacuumEntityFeature.BATTERY, this won't be called if battery_level is None. @@ -363,116 +418,57 @@ class MqttVacuum(MqttEntity, VacuumEntity): battery_level=self.battery_level, charging=self._charging ) - async def async_turn_on(self, **kwargs): - """Turn the vacuum on.""" - if self.supported_features & VacuumEntityFeature.TURN_ON == 0: + async def _async_publish_command(self, feature: VacuumEntityFeature) -> None: + """Check for a missing feature or command topic.""" + + if self._command_topic is None or self.supported_features & feature == 0: return await self.async_publish( self._command_topic, - self._payloads[CONF_PAYLOAD_TURN_ON], - self._qos, - self._retain, - self._encoding, + self._payloads[_COMMANDS[feature]["payload"]], + qos=self._qos, + retain=self._retain, + encoding=self._encoding, ) - self._attr_status = "Cleaning" + self._attr_status = _COMMANDS[feature]["status"] self.async_write_ha_state() - async def async_turn_off(self, **kwargs): + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the vacuum on.""" + await self._async_publish_command(VacuumEntityFeature.TURN_ON) + + async def async_turn_off(self, **kwargs: Any) -> None: """Turn the vacuum off.""" - if self.supported_features & VacuumEntityFeature.TURN_OFF == 0: - return None + await self._async_publish_command(VacuumEntityFeature.TURN_OFF) - await self.async_publish( - self._command_topic, - self._payloads[CONF_PAYLOAD_TURN_OFF], - self._qos, - self._retain, - self._encoding, - ) - self._attr_status = "Turning Off" - self.async_write_ha_state() - - async def async_stop(self, **kwargs): + async def async_stop(self, **kwargs: Any) -> None: """Stop the vacuum.""" - if self.supported_features & VacuumEntityFeature.STOP == 0: - return None + await self._async_publish_command(VacuumEntityFeature.STOP) - await self.async_publish( - self._command_topic, - self._payloads[CONF_PAYLOAD_STOP], - self._qos, - self._retain, - self._encoding, - ) - self._attr_status = "Stopping the current task" - self.async_write_ha_state() - - async def async_clean_spot(self, **kwargs): + async def async_clean_spot(self, **kwargs: Any) -> None: """Perform a spot clean-up.""" - if self.supported_features & VacuumEntityFeature.CLEAN_SPOT == 0: - return None + await self._async_publish_command(VacuumEntityFeature.CLEAN_SPOT) - await self.async_publish( - self._command_topic, - self._payloads[CONF_PAYLOAD_CLEAN_SPOT], - self._qos, - self._retain, - self._encoding, - ) - self._attr_status = "Cleaning spot" - self.async_write_ha_state() - - async def async_locate(self, **kwargs): + async def async_locate(self, **kwargs: Any) -> None: """Locate the vacuum (usually by playing a song).""" - if self.supported_features & VacuumEntityFeature.LOCATE == 0: - return None + await self._async_publish_command(VacuumEntityFeature.LOCATE) - await self.async_publish( - self._command_topic, - self._payloads[CONF_PAYLOAD_LOCATE], - self._qos, - self._retain, - self._encoding, - ) - self._attr_status = "Hi, I'm over here!" - self.async_write_ha_state() - - async def async_start_pause(self, **kwargs): + async def async_start_pause(self, **kwargs: Any) -> None: """Start, pause or resume the cleaning task.""" - if self.supported_features & VacuumEntityFeature.PAUSE == 0: - return None + await self._async_publish_command(VacuumEntityFeature.PAUSE) - await self.async_publish( - self._command_topic, - self._payloads[CONF_PAYLOAD_START_PAUSE], - self._qos, - self._retain, - self._encoding, - ) - self._attr_status = "Pausing/Resuming cleaning..." - self.async_write_ha_state() - - async def async_return_to_base(self, **kwargs): + async def async_return_to_base(self, **kwargs: Any) -> None: """Tell the vacuum to return to its dock.""" - if self.supported_features & VacuumEntityFeature.RETURN_HOME == 0: - return None + await self._async_publish_command(VacuumEntityFeature.RETURN_HOME) - await self.async_publish( - self._command_topic, - self._payloads[CONF_PAYLOAD_RETURN_TO_BASE], - self._qos, - self._retain, - self._encoding, - ) - self._attr_status = "Returning home..." - self.async_write_ha_state() - - async def async_set_fan_speed(self, fan_speed, **kwargs): + async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None: """Set fan speed.""" if ( - self.supported_features & VacuumEntityFeature.FAN_SPEED == 0 - ) or fan_speed not in self.fan_speed_list: + self._set_fan_speed_topic is None + or (self.supported_features & VacuumEntityFeature.FAN_SPEED == 0) + or fan_speed not in self.fan_speed_list + ): return None await self.async_publish( @@ -485,22 +481,30 @@ class MqttVacuum(MqttEntity, VacuumEntity): self._attr_status = f"Setting fan to {fan_speed}..." self.async_write_ha_state() - async def async_send_command(self, command, params=None, **kwargs): + async def async_send_command( + self, + command: str, + params: dict[str, Any] | list[Any] | None = None, + **kwargs: Any, + ) -> None: """Send a command to a vacuum cleaner.""" - if self.supported_features & VacuumEntityFeature.SEND_COMMAND == 0: + if ( + self._send_command_topic is None + or self.supported_features & VacuumEntityFeature.SEND_COMMAND == 0 + ): return if params: - message = {"command": command} + message: dict[str, Any] = {"command": command} message.update(params) - message = json_dumps(message) + message_payload = json_dumps(message) else: - message = command + message_payload = command await self.async_publish( self._send_command_topic, - message, + message_payload, self._qos, self._retain, self._encoding, ) - self._attr_status = f"Sending command {message}..." + self._attr_status = f"Sending command {message_payload}..." self.async_write_ha_state() diff --git a/homeassistant/components/mqtt/vacuum/schema_state.py b/homeassistant/components/mqtt/vacuum/schema_state.py index ede258102d7..2bacf4f36de 100644 --- a/homeassistant/components/mqtt/vacuum/schema_state.py +++ b/homeassistant/components/mqtt/vacuum/schema_state.py @@ -1,4 +1,8 @@ """Support for a State MQTT vacuum.""" +from __future__ import annotations + +from typing import Any + import voluptuous as vol from homeassistant.components.vacuum import ( @@ -11,15 +15,18 @@ from homeassistant.components.vacuum import ( StateVacuumEntity, VacuumEntityFeature, ) +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_SUPPORTED_FEATURES, CONF_NAME, STATE_IDLE, STATE_PAUSED, ) -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.json import json_dumps, json_loads +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from .. import subscription from ..config import MQTT_BASE_SCHEMA @@ -32,11 +39,12 @@ from ..const import ( ) from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema +from ..models import ReceiveMessage from ..util import get_mqtt_data, valid_publish_topic from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services -SERVICE_TO_STRING = { +SERVICE_TO_STRING: dict[VacuumEntityFeature, str] = { VacuumEntityFeature.START: "start", VacuumEntityFeature.PAUSE: "pause", VacuumEntityFeature.STOP: "stop", @@ -52,7 +60,7 @@ SERVICE_TO_STRING = { STRING_TO_SERVICE = {v: k for k, v in SERVICE_TO_STRING.items()} -DEFAULT_SERVICES = ( +DEFAULT_SERVICES: VacuumEntityFeature | int = ( VacuumEntityFeature.START | VacuumEntityFeature.STOP | VacuumEntityFeature.RETURN_HOME @@ -60,7 +68,7 @@ DEFAULT_SERVICES = ( | VacuumEntityFeature.BATTERY | VacuumEntityFeature.CLEAN_SPOT ) -ALL_SERVICES = ( +ALL_SERVICES: VacuumEntityFeature | int = ( DEFAULT_SERVICES | VacuumEntityFeature.PAUSE | VacuumEntityFeature.LOCATE @@ -72,7 +80,7 @@ BATTERY = "battery_level" FAN_SPEED = "fan_speed" STATE = "state" -POSSIBLE_STATES = { +POSSIBLE_STATES: dict[str, str] = { STATE_IDLE: STATE_IDLE, STATE_DOCKED: STATE_DOCKED, STATE_ERROR: STATE_ERROR, @@ -104,6 +112,15 @@ DEFAULT_PAYLOAD_LOCATE = "locate" DEFAULT_PAYLOAD_START = "start" DEFAULT_PAYLOAD_PAUSE = "pause" +_FEATURE_PAYLOADS = { + VacuumEntityFeature.START: CONF_PAYLOAD_START, + VacuumEntityFeature.STOP: CONF_PAYLOAD_STOP, + VacuumEntityFeature.PAUSE: CONF_PAYLOAD_PAUSE, + VacuumEntityFeature.CLEAN_SPOT: CONF_PAYLOAD_CLEAN_SPOT, + VacuumEntityFeature.LOCATE: CONF_PAYLOAD_LOCATE, + VacuumEntityFeature.RETURN_HOME: CONF_PAYLOAD_RETURN_TO_BASE, +} + PLATFORM_SCHEMA_STATE_MODERN = ( MQTT_BASE_SCHEMA.extend( { @@ -147,8 +164,12 @@ DISCOVERY_SCHEMA_STATE = PLATFORM_SCHEMA_STATE_MODERN.extend({}, extra=vol.REMOV async def async_setup_entity_state( - hass, config, async_add_entities, config_entry, discovery_data -): + hass: HomeAssistant, + config: ConfigType, + async_add_entities: AddEntitiesCallback, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, +) -> None: """Set up a State MQTT Vacuum.""" async_add_entities([MqttStateVacuum(hass, config, config_entry, discovery_data)]) @@ -159,20 +180,30 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): _entity_id_format = ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_VACUUM_ATTRIBUTES_BLOCKED - def __init__(self, hass, config, config_entry, discovery_data): + _set_fan_speed_topic: str | None + _send_command_topic: str | None + _payloads: dict[str, str | None] + + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize the vacuum.""" - self._state_attrs = {} + self._state_attrs: dict[str, Any] = {} MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA_STATE - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" - supported_feature_strings = config[CONF_SUPPORTED_FEATURES] + supported_feature_strings: list[str] = config[CONF_SUPPORTED_FEATURES] self._attr_supported_features = strings_to_services( supported_feature_strings, STRING_TO_SERVICE ) @@ -193,21 +224,21 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): ) } - def _update_state_attributes(self, payload): + def _update_state_attributes(self, payload: dict[str, Any]) -> None: """Update the entity state attributes.""" self._state_attrs.update(payload) self._attr_fan_speed = self._state_attrs.get(FAN_SPEED, 0) self._attr_battery_level = max(0, min(100, self._state_attrs.get(BATTERY, 0))) - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" - topics = {} + topics: dict[str, Any] = {} @callback @log_messages(self.hass, self.entity_id) - def state_message_received(msg): + def state_message_received(msg: ReceiveMessage) -> None: """Handle state MQTT message.""" - payload = json_loads(msg.payload) + payload: dict[str, Any] = json_loads(msg.payload) if STATE in payload and ( payload[STATE] in POSSIBLE_STATES or payload[STATE] is None ): @@ -218,9 +249,9 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): self._update_state_attributes(payload) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) - if self._config.get(CONF_STATE_TOPIC): + if state_topic := self._config.get(CONF_STATE_TOPIC): topics["state_position_topic"] = { - "topic": self._config.get(CONF_STATE_TOPIC), + "topic": state_topic, "msg_callback": state_message_received, "qos": self._config[CONF_QOS], "encoding": self._config[CONF_ENCODING] or None, @@ -229,50 +260,54 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): self.hass, self._sub_state, topics ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) - async def async_start(self): + async def _async_publish_command(self, feature: VacuumEntityFeature) -> None: + """Check for a missing feature or command topic.""" + if self._command_topic is None or self.supported_features & feature == 0: + return + + await self.async_publish( + self._command_topic, + self._payloads[_FEATURE_PAYLOADS[feature]], + qos=self._config[CONF_QOS], + retain=self._config[CONF_RETAIN], + encoding=self._config[CONF_ENCODING], + ) + self.async_write_ha_state() + + async def async_start(self) -> None: """Start the vacuum.""" - if self.supported_features & VacuumEntityFeature.START == 0: - return None - await self.async_publish( - self._command_topic, - self._config[CONF_PAYLOAD_START], - self._config[CONF_QOS], - self._config[CONF_RETAIN], - self._config[CONF_ENCODING], - ) + await self._async_publish_command(VacuumEntityFeature.START) - async def async_pause(self): + async def async_pause(self) -> None: """Pause the vacuum.""" - if self.supported_features & VacuumEntityFeature.PAUSE == 0: - return - await self.async_publish( - self._command_topic, - self._config[CONF_PAYLOAD_PAUSE], - self._config[CONF_QOS], - self._config[CONF_RETAIN], - self._config[CONF_ENCODING], - ) + await self._async_publish_command(VacuumEntityFeature.PAUSE) - async def async_stop(self, **kwargs): + async def async_stop(self, **kwargs: Any) -> None: """Stop the vacuum.""" - if self.supported_features & VacuumEntityFeature.STOP == 0: - return - await self.async_publish( - self._command_topic, - self._config[CONF_PAYLOAD_STOP], - self._config[CONF_QOS], - self._config[CONF_RETAIN], - self._config[CONF_ENCODING], - ) + await self._async_publish_command(VacuumEntityFeature.STOP) - async def async_set_fan_speed(self, fan_speed, **kwargs): + async def async_return_to_base(self, **kwargs: Any) -> None: + """Tell the vacuum to return to its dock.""" + await self._async_publish_command(VacuumEntityFeature.RETURN_HOME) + + async def async_clean_spot(self, **kwargs: Any) -> None: + """Perform a spot clean-up.""" + await self._async_publish_command(VacuumEntityFeature.CLEAN_SPOT) + + async def async_locate(self, **kwargs: Any) -> None: + """Locate the vacuum (usually by playing a song).""" + await self._async_publish_command(VacuumEntityFeature.LOCATE) + + async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None: """Set fan speed.""" - if (self.supported_features & VacuumEntityFeature.FAN_SPEED == 0) or ( - fan_speed not in self.fan_speed_list + if ( + self._set_fan_speed_topic is None + or (self.supported_features & VacuumEntityFeature.FAN_SPEED == 0) + or (fan_speed not in self.fan_speed_list) ): return await self.async_publish( @@ -283,55 +318,27 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): self._config[CONF_ENCODING], ) - async def async_return_to_base(self, **kwargs): - """Tell the vacuum to return to its dock.""" - if self.supported_features & VacuumEntityFeature.RETURN_HOME == 0: - return - await self.async_publish( - self._command_topic, - self._config[CONF_PAYLOAD_RETURN_TO_BASE], - self._config[CONF_QOS], - self._config[CONF_RETAIN], - self._config[CONF_ENCODING], - ) - - async def async_clean_spot(self, **kwargs): - """Perform a spot clean-up.""" - if self.supported_features & VacuumEntityFeature.CLEAN_SPOT == 0: - return - await self.async_publish( - self._command_topic, - self._config[CONF_PAYLOAD_CLEAN_SPOT], - self._config[CONF_QOS], - self._config[CONF_RETAIN], - self._config[CONF_ENCODING], - ) - - async def async_locate(self, **kwargs): - """Locate the vacuum (usually by playing a song).""" - if self.supported_features & VacuumEntityFeature.LOCATE == 0: - return - await self.async_publish( - self._command_topic, - self._config[CONF_PAYLOAD_LOCATE], - self._config[CONF_QOS], - self._config[CONF_RETAIN], - self._config[CONF_ENCODING], - ) - - async def async_send_command(self, command, params=None, **kwargs): + async def async_send_command( + self, + command: str, + params: dict[str, Any] | list[Any] | None = None, + **kwargs: Any, + ) -> None: """Send a command to a vacuum cleaner.""" - if self.supported_features & VacuumEntityFeature.SEND_COMMAND == 0: + if ( + self._send_command_topic is None + or self.supported_features & VacuumEntityFeature.SEND_COMMAND == 0 + ): return - if params: - message = {"command": command} + if isinstance(params, dict): + message: dict[str, Any] = {"command": command} message.update(params) - message = json_dumps(message) + payload = json_dumps(message) else: - message = command + payload = command await self.async_publish( self._send_command_topic, - message, + payload, self._config[CONF_QOS], self._config[CONF_RETAIN], self._config[CONF_ENCODING], From 4cce359960ae16585aa1ec8a9df51298fb547e83 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 15 Nov 2022 13:39:30 +0100 Subject: [PATCH 0420/1033] Fix legacy scrape scan interval (#81764) * Fix legacy scrape scan interval * Adjust init * Adjust tests * Clearer default --- homeassistant/components/scrape/__init__.py | 9 ++------- homeassistant/components/scrape/const.py | 4 +++- homeassistant/components/scrape/sensor.py | 15 +++++++++++---- tests/components/scrape/test_init.py | 5 ++--- tests/components/scrape/test_sensor.py | 4 ++-- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/scrape/__init__.py b/homeassistant/components/scrape/__init__.py index 25a734dbd24..5bc865dbb6c 100644 --- a/homeassistant/components/scrape/__init__.py +++ b/homeassistant/components/scrape/__init__.py @@ -60,13 +60,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: load_coroutines: list[Coroutine[Any, Any, None]] = [] for resource_config in scrape_config: rest = create_rest_data_from_config(hass, resource_config) - coordinator = ScrapeCoordinator( - hass, - rest, - timedelta( - seconds=resource_config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) - ), - ) + scan_interval: timedelta = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) + coordinator = ScrapeCoordinator(hass, rest, scan_interval) sensors: list[ConfigType] = resource_config.get(SENSOR_DOMAIN, []) if sensors: diff --git a/homeassistant/components/scrape/const.py b/homeassistant/components/scrape/const.py index 26926729309..fc433ebb6f0 100644 --- a/homeassistant/components/scrape/const.py +++ b/homeassistant/components/scrape/const.py @@ -1,12 +1,14 @@ """Constants for Scrape integration.""" from __future__ import annotations +from datetime import timedelta + from homeassistant.const import Platform DOMAIN = "scrape" DEFAULT_NAME = "Web scrape" DEFAULT_VERIFY_SSL = True -DEFAULT_SCAN_INTERVAL = 60 * 10 +DEFAULT_SCAN_INTERVAL = timedelta(minutes=10) PLATFORMS = [Platform.SENSOR] diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index b606f00aaa7..61f0b9711b1 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -22,6 +22,7 @@ from homeassistant.const import ( CONF_NAME, CONF_PASSWORD, CONF_RESOURCE, + CONF_SCAN_INTERVAL, CONF_UNIQUE_ID, CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME, @@ -43,13 +44,18 @@ from homeassistant.helpers.template_entity import ( from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import CONF_INDEX, CONF_SELECT, DEFAULT_NAME, DEFAULT_VERIFY_SSL, DOMAIN +from .const import ( + CONF_INDEX, + CONF_SELECT, + DEFAULT_NAME, + DEFAULT_SCAN_INTERVAL, + DEFAULT_VERIFY_SSL, + DOMAIN, +) from .coordinator import ScrapeCoordinator _LOGGER = logging.getLogger(__name__) -SCAN_INTERVAL = timedelta(minutes=10) - PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend( { # Linked to the loading of the page (can be linked to RestData) @@ -98,7 +104,8 @@ async def async_setup_platform( resource_config = vol.Schema(RESOURCE_SCHEMA, extra=vol.REMOVE_EXTRA)(config) rest = create_rest_data_from_config(hass, resource_config) - coordinator = ScrapeCoordinator(hass, rest, SCAN_INTERVAL) + scan_interval: timedelta = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) + coordinator = ScrapeCoordinator(hass, rest, scan_interval) sensors_config = [ vol.Schema(TEMPLATE_SENSOR_BASE_SCHEMA.schema, extra=vol.ALLOW_EXTRA)( diff --git a/tests/components/scrape/test_init.py b/tests/components/scrape/test_init.py index e66b9a65fd4..17803cc30c2 100644 --- a/tests/components/scrape/test_init.py +++ b/tests/components/scrape/test_init.py @@ -6,8 +6,7 @@ from unittest.mock import patch import pytest -from homeassistant.components.scrape.const import DOMAIN -from homeassistant.components.scrape.sensor import SCAN_INTERVAL +from homeassistant.components.scrape.const import DEFAULT_SCAN_INTERVAL, DOMAIN from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component @@ -67,7 +66,7 @@ async def test_setup_no_data_fails_with_recovery( assert "Platform scrape not ready yet" in caplog.text mocker.payload = "test_scrape_sensor" - async_fire_time_changed(hass, datetime.utcnow() + SCAN_INTERVAL) + async_fire_time_changed(hass, datetime.utcnow() + DEFAULT_SCAN_INTERVAL) await hass.async_block_till_done() state = hass.states.get("sensor.ha_version") diff --git a/tests/components/scrape/test_sensor.py b/tests/components/scrape/test_sensor.py index 2d0c7b4c61d..23c558a72af 100644 --- a/tests/components/scrape/test_sensor.py +++ b/tests/components/scrape/test_sensor.py @@ -6,7 +6,7 @@ from unittest.mock import patch import pytest -from homeassistant.components.scrape.sensor import SCAN_INTERVAL +from homeassistant.components.scrape.const import DEFAULT_SCAN_INTERVAL from homeassistant.components.sensor import ( CONF_STATE_CLASS, DOMAIN as SENSOR_DOMAIN, @@ -298,7 +298,7 @@ async def test_scrape_sensor_no_data_refresh(hass: HomeAssistant) -> None: assert state.state == "Current Version: 2021.12.10" mocker.payload = "test_scrape_sensor_no_data" - async_fire_time_changed(hass, datetime.utcnow() + SCAN_INTERVAL) + async_fire_time_changed(hass, datetime.utcnow() + DEFAULT_SCAN_INTERVAL) await hass.async_block_till_done() state = hass.states.get("sensor.ha_version") From e7dd31f37b7e6491a75f63ca0770df39fd8730b3 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Tue, 15 Nov 2022 22:02:35 +0800 Subject: [PATCH 0421/1033] Iterate over entities safely in camera setup (#82080) fixes undefined --- homeassistant/components/camera/__init__.py | 2 +- homeassistant/helpers/entity_component.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 82d981fc0cc..a8c6b1ea517 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -359,7 +359,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: await component.async_setup(config) async def preload_stream(_event: Event) -> None: - for camera in component.entities: + for camera in list(component.entities): stream_prefs = await prefs.get_dynamic_stream_settings(camera.entity_id) if not stream_prefs.preload_stream: continue diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index eea0a9943a3..f7f2b7e3dd3 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -90,7 +90,12 @@ class EntityComponent(Generic[_EntityT]): @property def entities(self) -> Iterable[_EntityT]: - """Return an iterable that returns all entities.""" + """ + Return an iterable that returns all entities. + + As the underlying dicts may change when async context is lost, callers that + iterate over this asynchronously should make a copy using list() before iterating. + """ return chain.from_iterable( platform.entities.values() # type: ignore[misc] for platform in self._platforms.values() From f497b989b91f6fcc30f23da08c294f5901a59d35 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Nov 2022 08:27:54 -0600 Subject: [PATCH 0422/1033] Bump dbus-fast to 1.74.0 (#82107) changelog: https://github.com/Bluetooth-Devices/dbus-fast/compare/v1.73.0...v1.74.0 --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index b2b4c771e51..3d596d817ee 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -11,7 +11,7 @@ "bluetooth-adapters==0.7.0", "bluetooth-auto-recovery==0.3.6", "bluetooth-data-tools==0.3.0", - "dbus-fast==1.73.0" + "dbus-fast==1.74.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index d75ba977556..b973e5bc0e9 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -18,7 +18,7 @@ bluetooth-data-tools==0.3.0 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.3 -dbus-fast==1.73.0 +dbus-fast==1.74.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index f2060fa568d..eaa6047807b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -555,7 +555,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.73.0 +dbus-fast==1.74.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 367acc0eb86..92ae4da89fd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -435,7 +435,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.73.0 +dbus-fast==1.74.0 # homeassistant.components.debugpy debugpy==1.6.3 From 19e5e671a25994d766c0d1ae0fe07a6b872743d3 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 15 Nov 2022 15:51:13 +0100 Subject: [PATCH 0423/1033] Update sqlalchemy to 1.4.44 (#82129) --- homeassistant/components/recorder/manifest.json | 2 +- homeassistant/components/sql/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/recorder/manifest.json b/homeassistant/components/recorder/manifest.json index afdabfd6d01..3fb873bfc90 100644 --- a/homeassistant/components/recorder/manifest.json +++ b/homeassistant/components/recorder/manifest.json @@ -2,7 +2,7 @@ "domain": "recorder", "name": "Recorder", "documentation": "https://www.home-assistant.io/integrations/recorder", - "requirements": ["sqlalchemy==1.4.42", "fnvhash==0.1.0"], + "requirements": ["sqlalchemy==1.4.44", "fnvhash==0.1.0"], "codeowners": ["@home-assistant/core"], "quality_scale": "internal", "iot_class": "local_push", diff --git a/homeassistant/components/sql/manifest.json b/homeassistant/components/sql/manifest.json index 7484ca0feb7..4ee1683a357 100644 --- a/homeassistant/components/sql/manifest.json +++ b/homeassistant/components/sql/manifest.json @@ -2,7 +2,7 @@ "domain": "sql", "name": "SQL", "documentation": "https://www.home-assistant.io/integrations/sql", - "requirements": ["sqlalchemy==1.4.42"], + "requirements": ["sqlalchemy==1.4.44"], "codeowners": ["@dgomes", "@gjohansson-ST"], "config_flow": true, "iot_class": "local_polling" diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index b973e5bc0e9..346b61fd1a8 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -38,7 +38,7 @@ pyudev==0.23.2 pyyaml==6.0 requests==2.28.1 scapy==2.4.5 -sqlalchemy==1.4.42 +sqlalchemy==1.4.44 typing-extensions>=4.4.0,<5.0 voluptuous-serialize==2.5.0 voluptuous==0.13.1 diff --git a/requirements_all.txt b/requirements_all.txt index eaa6047807b..e86d899d9f7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2333,7 +2333,7 @@ spotipy==2.21.0 # homeassistant.components.recorder # homeassistant.components.sql -sqlalchemy==1.4.42 +sqlalchemy==1.4.44 # homeassistant.components.srp_energy srpenergy==1.3.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 92ae4da89fd..066b33877da 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1618,7 +1618,7 @@ spotipy==2.21.0 # homeassistant.components.recorder # homeassistant.components.sql -sqlalchemy==1.4.42 +sqlalchemy==1.4.44 # homeassistant.components.srp_energy srpenergy==1.3.6 From 9eef5129f07f2595f94903ce596a3213ce2e7901 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Tue, 15 Nov 2022 16:04:22 +0100 Subject: [PATCH 0424/1033] Full test coverage Min Max (#82053) --- tests/components/min_max/test_sensor.py | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/components/min_max/test_sensor.py b/tests/components/min_max/test_sensor.py index 4819cc31a9b..417bcda2d91 100644 --- a/tests/components/min_max/test_sensor.py +++ b/tests/components/min_max/test_sensor.py @@ -2,6 +2,8 @@ import statistics from unittest.mock import patch +from pytest import LogCaptureFixture + from homeassistant import config as hass_config from homeassistant.components.min_max.const import DOMAIN from homeassistant.components.sensor import ATTR_STATE_CLASS, SensorStateClass @@ -14,12 +16,14 @@ from homeassistant.const import ( TEMP_CELSIUS, TEMP_FAHRENHEIT, ) +from homeassistant.core import HomeAssistant import homeassistant.helpers.entity_registry as er from homeassistant.setup import async_setup_component from tests.common import get_fixture_path VALUES = [17, 20, 15.3] +VALUES_ERROR = [17, "string", 15.3] COUNT = len(VALUES) MIN_VALUE = min(VALUES) MAX_VALUE = max(VALUES) @@ -433,3 +437,32 @@ async def test_reload(hass): assert hass.states.get("sensor.test") is None assert hass.states.get("sensor.second_test") + + +async def test_sensor_incorrect_state( + hass: HomeAssistant, caplog: LogCaptureFixture +) -> None: + """Test the min sensor.""" + config = { + "sensor": { + "platform": "min_max", + "name": "test_failure", + "type": "min", + "entity_ids": ["sensor.test_1", "sensor.test_2", "sensor.test_3"], + "unique_id": "very_unique_id", + } + } + + assert await async_setup_component(hass, "sensor", config) + await hass.async_block_till_done() + + entity_ids = config["sensor"]["entity_ids"] + + for entity_id, value in dict(zip(entity_ids, VALUES_ERROR)).items(): + hass.states.async_set(entity_id, value) + await hass.async_block_till_done() + + state = hass.states.get("sensor.test_failure") + + assert state.state == "15.3" + assert "Unable to store state. Only numerical states are supported" in caplog.text From 285c61c0d70d2cb7ec5332fa1267998224db51d2 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 15 Nov 2022 16:54:39 +0100 Subject: [PATCH 0425/1033] Add TagProtocol as type hint in MQTT tag (#82134) --- homeassistant/components/mqtt/tag.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/tag.py b/homeassistant/components/mqtt/tag.py index 26cb66e9f2f..223718554b0 100644 --- a/homeassistant/components/mqtt/tag.py +++ b/homeassistant/components/mqtt/tag.py @@ -3,6 +3,7 @@ from __future__ import annotations from collections.abc import Callable import functools +from typing import TYPE_CHECKING import voluptuous as vol @@ -27,6 +28,9 @@ from .models import MqttValueTemplate, ReceiveMessage, ReceivePayloadType from .subscription import EntitySubscription from .util import get_mqtt_data, valid_subscribe_topic +if TYPE_CHECKING: + from homeassistant.components.tag import TagProtocol + LOG_NAME = "Tag" TAG = "tag" @@ -137,7 +141,7 @@ class MQTTTagScanner(MqttDiscoveryDeviceUpdate): # Importing tag via hass.components in case it is overridden # in a custom_components (custom_components.tag) - tag = self.hass.components.tag + tag: TagProtocol = self.hass.components.tag await tag.async_scan_tag(tag_id, self.device_id) self._sub_state = subscription.async_prepare_subscribe_topics( From ab5c99fe22c32eb2aedb3b2df6c78950776d83c4 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 15 Nov 2022 17:59:02 +0100 Subject: [PATCH 0426/1033] Update sentry-sdk to 1.11.0 (#82122) --- homeassistant/components/sentry/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sentry/manifest.json b/homeassistant/components/sentry/manifest.json index 567a0350c4e..e86ff697896 100644 --- a/homeassistant/components/sentry/manifest.json +++ b/homeassistant/components/sentry/manifest.json @@ -3,7 +3,7 @@ "name": "Sentry", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/sentry", - "requirements": ["sentry-sdk==1.10.1"], + "requirements": ["sentry-sdk==1.11.0"], "codeowners": ["@dcramer", "@frenck"], "integration_type": "service", "iot_class": "cloud_polling" diff --git a/requirements_all.txt b/requirements_all.txt index e86d899d9f7..3a6aad58d8b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2263,7 +2263,7 @@ sensorpro-ble==0.5.0 sensorpush-ble==1.5.2 # homeassistant.components.sentry -sentry-sdk==1.10.1 +sentry-sdk==1.11.0 # homeassistant.components.sharkiq sharkiq==0.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 066b33877da..d50eae721f8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1566,7 +1566,7 @@ sensorpro-ble==0.5.0 sensorpush-ble==1.5.2 # homeassistant.components.sentry -sentry-sdk==1.10.1 +sentry-sdk==1.11.0 # homeassistant.components.sharkiq sharkiq==0.0.1 From af9ac9022bc78b86adf306f74dcfd3afcfeb6fd3 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 15 Nov 2022 18:27:37 +0100 Subject: [PATCH 0427/1033] Import tag via hass.components in mobile_app (#82138) * Import tag via hass.components in mobile_app * Update webhook.py --- homeassistant/components/mobile_app/webhook.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 7c6bcc58db9..514d8820399 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -1,17 +1,20 @@ """Webhook handlers for mobile_app.""" +from __future__ import annotations + import asyncio from contextlib import suppress from functools import wraps from http import HTTPStatus import logging import secrets +from typing import TYPE_CHECKING from aiohttp.web import HTTPBadRequest, Request, Response, json_response from nacl.exceptions import CryptoError from nacl.secret import SecretBox import voluptuous as vol -from homeassistant.components import camera, cloud, notify as hass_notify, tag +from homeassistant.components import camera, cloud, notify as hass_notify from homeassistant.components.binary_sensor import ( DEVICE_CLASSES as BINARY_SENSOR_CLASSES, ) @@ -111,6 +114,10 @@ from .helpers import ( webhook_response, ) +if TYPE_CHECKING: + from homeassistant.components.tag import TagProtocol + + _LOGGER = logging.getLogger(__name__) DELAY_SAVE = 10 @@ -681,8 +688,10 @@ async def webhook_get_config(hass, config_entry, data): @validate_schema({vol.Required("tag_id"): cv.string}) async def webhook_scan_tag(hass, config_entry, data): """Handle a fire event webhook.""" + # Importing tag via hass.components in case it is overridden + # in a custom_components (custom_components.tag) + tag: TagProtocol = hass.components.tag await tag.async_scan_tag( - hass, data["tag_id"], config_entry.data[ATTR_DEVICE_ID], registration_context(config_entry.data), From f035223599824e817b6db2289f3daf5c17d7e0ac Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Nov 2022 11:27:59 -0600 Subject: [PATCH 0428/1033] Make sure the config_flow key is set for brands (#82038) Fixes https://github.com/home-assistant/frontend/issues/14376 --- homeassistant/generated/integrations.json | 85 +++++++++++++++++++++++ script/hassfest/config_flow.py | 5 +- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 0ca0f01209c..3e969c3e6f5 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -165,21 +165,25 @@ "integrations": { "alexa": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Amazon Alexa" }, "amazon_polly": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Amazon Polly" }, "aws": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Amazon Web Services (AWS)" }, "route53": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "AWS Route53" } @@ -290,6 +294,7 @@ }, "itunes": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Apple iTunes" } @@ -348,11 +353,13 @@ "integrations": { "aruba": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Aruba" }, "cppm_tracker": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Aruba ClearPass" } @@ -375,11 +382,13 @@ "integrations": { "asterisk_cdr": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Asterisk Call Detail Records" }, "asterisk_mbox": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_push", "name": "Asterisk Voicemail" } @@ -727,16 +736,19 @@ "integrations": { "cisco_ios": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Cisco IOS" }, "cisco_mobility_express": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Cisco Mobility Express" }, "cisco_webex_teams": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Cisco Webex Teams" } @@ -765,11 +777,13 @@ "integrations": { "clicksend": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "ClickSend SMS" }, "clicksend_tts": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "ClickSend TTS" } @@ -961,6 +975,7 @@ "integrations": { "denon": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Denon Network Receivers" }, @@ -1262,6 +1277,7 @@ "integrations": { "avea": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Elgato Avea" }, @@ -1308,11 +1324,13 @@ "integrations": { "emoncms": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Emoncms" }, "emoncms_history": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Emoncms History" } @@ -1394,6 +1412,7 @@ }, "epsonworkforce": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Epson Workforce" } @@ -1404,11 +1423,13 @@ "integrations": { "eq3btsmart": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "eQ-3 Bluetooth Smart Thermostats" }, "maxcube": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "eQ-3 MAX!" } @@ -1497,15 +1518,18 @@ "integrations": { "ffmpeg": { "integration_type": "hub", + "config_flow": false, "name": "FFmpeg" }, "ffmpeg_motion": { "integration_type": "hub", + "config_flow": false, "iot_class": "calculated", "name": "FFmpeg Motion" }, "ffmpeg_noise": { "integration_type": "hub", + "config_flow": false, "iot_class": "calculated", "name": "FFmpeg Noise" } @@ -1888,11 +1912,13 @@ "integrations": { "gc100": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Global Cach\u00e9 GC-100" }, "itach": { "integration_type": "hub", + "config_flow": false, "iot_class": "assumed_state", "name": "Global Cach\u00e9 iTach TCP/IP to IR" } @@ -1927,26 +1953,31 @@ "integrations": { "google_assistant": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Google Assistant" }, "google_cloud": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Google Cloud Platform" }, "google_domains": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_polling", "name": "Google Domains" }, "google_maps": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_polling", "name": "Google Maps" }, "google_pubsub": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Google Pub/Sub" }, @@ -1958,6 +1989,7 @@ }, "google_translate": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Google Translate Text-to-Speech" }, @@ -1968,6 +2000,7 @@ }, "google_wifi": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Google Wifi" }, @@ -2141,11 +2174,13 @@ "integrations": { "hikvision": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_push", "name": "Hikvision" }, "hikvisioncam": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Hikvision" } @@ -2198,6 +2233,7 @@ "integrations": { "homematic": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_push", "name": "Homematic" }, @@ -2226,6 +2262,7 @@ }, "evohome": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_polling", "name": "Honeywell Total Connect Comfort (Europe)" }, @@ -2319,11 +2356,13 @@ "integrations": { "watson_iot": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "IBM Watson IoT Platform" }, "watson_tts": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "IBM Watson TTS" } @@ -2364,6 +2403,7 @@ "integrations": { "symfonisk": { "integration_type": "virtual", + "config_flow": false, "supported_by": "sonos", "name": "IKEA SYMFONISK" }, @@ -2742,6 +2782,7 @@ "integrations": { "lg_netcast": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "LG Netcast" }, @@ -2883,6 +2924,7 @@ }, "ue_smart_radio": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_polling", "name": "Logitech UE Smart Radio" }, @@ -2929,6 +2971,7 @@ "integrations": { "lutron": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Lutron" }, @@ -2940,6 +2983,7 @@ }, "homeworks": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_push", "name": "Lutron Homeworks" } @@ -3049,6 +3093,7 @@ }, "raincloud": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_polling", "name": "Melnor RainCloud" } @@ -3125,31 +3170,37 @@ }, "azure_service_bus": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Azure Service Bus" }, "microsoft_face_detect": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Microsoft Face Detect" }, "microsoft_face_identify": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Microsoft Face Identify" }, "microsoft_face": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Microsoft Face" }, "microsoft": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Microsoft Text-to-Speech (TTS)" }, "msteams": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Microsoft Teams" }, @@ -3161,6 +3212,7 @@ }, "xbox_live": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_polling", "name": "Xbox Live" } @@ -3288,6 +3340,7 @@ "integrations": { "manual_mqtt": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_push", "name": "Manual MQTT Alarm Control Panel" }, @@ -3299,21 +3352,25 @@ }, "mqtt_eventstream": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "MQTT Eventstream" }, "mqtt_json": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_push", "name": "MQTT JSON" }, "mqtt_room": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_push", "name": "MQTT Room Presence" }, "mqtt_statestream": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_push", "name": "MQTT Statestream" } @@ -3432,6 +3489,7 @@ }, "netgear_lte": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "NETGEAR LTE" } @@ -3793,11 +3851,13 @@ "integrations": { "luci": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "OpenWrt (luci)" }, "ubus": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "OpenWrt (ubus)" } @@ -3874,6 +3934,7 @@ "integrations": { "panasonic_bluray": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Panasonic Blu-Ray Player" }, @@ -4168,6 +4229,7 @@ "integrations": { "qnap": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "QNAP" }, @@ -4256,6 +4318,7 @@ "integrations": { "rpi_camera": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Raspberry Pi Camera" }, @@ -4266,6 +4329,7 @@ }, "remote_rpi_gpio": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_push", "name": "Raspberry Pi Remote GPIO" } @@ -4465,11 +4529,13 @@ "integrations": { "russound_rio": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_push", "name": "Russound RIO" }, "russound_rnet": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Russound RNET" } @@ -4498,6 +4564,7 @@ "integrations": { "familyhub": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Samsung Family Hub" }, @@ -4884,6 +4951,7 @@ }, "solaredge_local": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "SolarEdge Local" } @@ -4947,6 +5015,7 @@ }, "sony_projector": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Sony Projector" }, @@ -5160,6 +5229,7 @@ "integrations": { "synology_chat": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Synology Chat" }, @@ -5171,6 +5241,7 @@ }, "synology_srm": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Synology SRM" } @@ -5257,11 +5328,13 @@ "integrations": { "telegram": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_polling", "name": "Telegram" }, "telegram_bot": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Telegram bot" } @@ -5278,6 +5351,7 @@ }, "tellstick": { "integration_type": "hub", + "config_flow": false, "iot_class": "assumed_state", "name": "TellStick" } @@ -5561,11 +5635,13 @@ }, "twilio_call": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Twilio Call" }, "twilio_sms": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Twilio SMS" } @@ -5594,6 +5670,7 @@ "integrations": { "ultraloq": { "integration_type": "virtual", + "config_flow": false, "iot_standards": [ "zwave" ], @@ -5612,11 +5689,13 @@ }, "unifi_direct": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "UniFi AP" }, "unifiled": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "UniFi LED" }, @@ -5798,6 +5877,7 @@ "integrations": { "vlc": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "VLC media player" }, @@ -6022,11 +6102,13 @@ }, "xiaomi_tv": { "integration_type": "hub", + "config_flow": false, "iot_class": "assumed_state", "name": "Xiaomi TV" }, "xiaomi": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Xiaomi" } @@ -6084,11 +6166,13 @@ "integrations": { "yandex_transport": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_polling", "name": "Yandex Transport" }, "yandextts": { "integration_type": "hub", + "config_flow": false, "iot_class": "cloud_push", "name": "Yandex TTS" } @@ -6105,6 +6189,7 @@ }, "yeelightsunflower": { "integration_type": "hub", + "config_flow": false, "iot_class": "local_polling", "name": "Yeelight Sunflower" } diff --git a/script/hassfest/config_flow.py b/script/hassfest/config_flow.py index cf782a413b8..e668846505f 100644 --- a/script/hassfest/config_flow.py +++ b/script/hassfest/config_flow.py @@ -102,8 +102,9 @@ def _populate_brand_integrations( metadata = { "integration_type": integration.integration_type, } - if integration.config_flow: - metadata["config_flow"] = integration.config_flow + # Always set the config_flow key to avoid breaking the frontend + # https://github.com/home-assistant/frontend/issues/14376 + metadata["config_flow"] = bool(integration.config_flow) if integration.iot_class: metadata["iot_class"] = integration.iot_class if integration.supported_by: From dd38d2ff05ce8bad4e1be145fdd27c4e4929055c Mon Sep 17 00:00:00 2001 From: Jeef Date: Tue, 15 Nov 2022 09:29:32 -0800 Subject: [PATCH 0429/1033] Increasing device usage update interval for Flume (#81968) --- homeassistant/components/flume/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/flume/const.py b/homeassistant/components/flume/const.py index 2d53db4c486..b9192207e75 100644 --- a/homeassistant/components/flume/const.py +++ b/homeassistant/components/flume/const.py @@ -17,7 +17,7 @@ DEFAULT_NAME = "Flume Sensor" # Flume API limits individual endpoints to 120 queries per hour NOTIFICATION_SCAN_INTERVAL = timedelta(minutes=1) -DEVICE_SCAN_INTERVAL = timedelta(minutes=1) +DEVICE_SCAN_INTERVAL = timedelta(minutes=5) DEVICE_CONNECTION_SCAN_INTERVAL = timedelta(minutes=1) _LOGGER = logging.getLogger(__package__) From c65f894fbe9a77c8bfc90cf691b2560f0628a613 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Nov 2022 11:30:03 -0600 Subject: [PATCH 0430/1033] Bump PySwitchbot to 0.20.5 (#82099) --- homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index d586a328ed7..274c5784b2f 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.20.4"], + "requirements": ["PySwitchbot==0.20.5"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 3a6aad58d8b..7803f34f498 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -37,7 +37,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.20.4 +PySwitchbot==0.20.5 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d50eae721f8..4268e45c12d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -33,7 +33,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.20.4 +PySwitchbot==0.20.5 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From 1331a3771a19358957e30341a1c25dd0b7f552e8 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 15 Nov 2022 18:31:30 +0100 Subject: [PATCH 0431/1033] Block client switches should rely only on events for state changes (#81883) fixes undefined --- homeassistant/components/unifi/switch.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/unifi/switch.py b/homeassistant/components/unifi/switch.py index 5a88caca807..3e98f41188a 100644 --- a/homeassistant/components/unifi/switch.py +++ b/homeassistant/components/unifi/switch.py @@ -179,6 +179,7 @@ class UnifiEntityDescription(SwitchEntityDescription, UnifiEntityLoader[Handler, """Class describing UniFi switch entity.""" custom_subscribe: Callable[[aiounifi.Controller], Subscription] | None = None + only_event_for_state_change: bool = False ENTITY_DESCRIPTIONS: tuple[UnifiEntityDescription, ...] = ( @@ -198,6 +199,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiEntityDescription, ...] = ( is_on_fn=lambda api, client: not client.blocked, name_fn=lambda client: None, object_fn=lambda api, obj_id: api.clients[obj_id], + only_event_for_state_change=True, supported_fn=lambda api, obj_id: True, unique_id_fn=lambda obj_id: f"block-{obj_id}", ), @@ -400,8 +402,9 @@ class UnifiSwitchEntity(SwitchEntity): self.hass.async_create_task(self.remove_item({self._obj_id})) return - obj = description.object_fn(self.controller.api, self._obj_id) - self._attr_is_on = description.is_on_fn(self.controller.api, obj) + if not description.only_event_for_state_change: + obj = description.object_fn(self.controller.api, self._obj_id) + self._attr_is_on = description.is_on_fn(self.controller.api, obj) self._attr_available = description.available_fn(self.controller, self._obj_id) self.async_write_ha_state() From ade4b62aec9cf35494a9ed1585483982ae8a7234 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Nov 2022 11:41:55 -0600 Subject: [PATCH 0432/1033] Fix instability with HomeKit trigger accessories (#80703) fixes https://github.com/home-assistant/core/issues/78774 fixes https://github.com/home-assistant/core/issues/81685 --- homeassistant/components/homekit/__init__.py | 61 ++-- .../components/homekit/accessories.py | 2 +- .../components/homekit/diagnostics.py | 11 +- .../components/homekit/type_triggers.py | 22 +- tests/components/homekit/test_diagnostics.py | 322 +++++++++++++++++- tests/components/homekit/test_init.py | 64 +++- 6 files changed, 450 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index ca73c7dc242..1129e8c3f66 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -23,6 +23,9 @@ from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, ) from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN +from homeassistant.components.device_automation.trigger import ( + async_validate_trigger_config, +) from homeassistant.components.http import HomeAssistantView from homeassistant.components.humidifier import DOMAIN as HUMIDIFIER_DOMAIN from homeassistant.components.network import MDNS_TARGET_IP @@ -906,29 +909,47 @@ class HomeKit: self.bridge = HomeBridge(self.hass, self.driver, self._name) for state in entity_states: self.add_bridge_accessory(state) - dev_reg = device_registry.async_get(self.hass) if self._devices: - valid_device_ids = [] - for device_id in self._devices: - if not dev_reg.async_get(device_id): - _LOGGER.warning( - "HomeKit %s cannot add device %s because it is missing from the device registry", - self._name, - device_id, - ) - else: - valid_device_ids.append(device_id) - for device_id, device_triggers in ( - await device_automation.async_get_device_automations( - self.hass, - device_automation.DeviceAutomationType.TRIGGER, - valid_device_ids, - ) - ).items(): - if device := dev_reg.async_get(device_id): - self.add_bridge_triggers_accessory(device, device_triggers) + await self._async_add_trigger_accessories() return self.bridge + async def _async_add_trigger_accessories(self) -> None: + """Add devices with triggers to the bridge.""" + dev_reg = device_registry.async_get(self.hass) + valid_device_ids = [] + for device_id in self._devices: + if not dev_reg.async_get(device_id): + _LOGGER.warning( + "HomeKit %s cannot add device %s because it is missing from the device registry", + self._name, + device_id, + ) + else: + valid_device_ids.append(device_id) + for device_id, device_triggers in ( + await device_automation.async_get_device_automations( + self.hass, + device_automation.DeviceAutomationType.TRIGGER, + valid_device_ids, + ) + ).items(): + device = dev_reg.async_get(device_id) + assert device is not None + valid_device_triggers: list[dict[str, Any]] = [] + for trigger in device_triggers: + try: + await async_validate_trigger_config(self.hass, trigger) + except vol.Invalid as ex: + _LOGGER.debug( + "%s: cannot add unsupported trigger %s because it requires additional inputs which are not supported by HomeKit: %s", + self._name, + trigger, + ex, + ) + continue + valid_device_triggers.append(trigger) + self.add_bridge_triggers_accessory(device, valid_device_triggers) + async def _async_create_accessories(self) -> bool: """Create the accessories.""" assert self.driver is not None diff --git a/homeassistant/components/homekit/accessories.py b/homeassistant/components/homekit/accessories.py index 75b122dfb94..eef43892677 100644 --- a/homeassistant/components/homekit/accessories.py +++ b/homeassistant/components/homekit/accessories.py @@ -657,7 +657,7 @@ class HomeIIDManager(IIDManager): # type: ignore[misc] """Get IID for object.""" aid = obj.broker.aid if isinstance(obj, Characteristic): - service = obj.service + service: Service = obj.service iid = self._iid_storage.get_or_allocate_iid( aid, service.type_id, service.unique_id, obj.type_id, obj.unique_id ) diff --git a/homeassistant/components/homekit/diagnostics.py b/homeassistant/components/homekit/diagnostics.py index 1d0bfb92fcc..f27171e6eae 100644 --- a/homeassistant/components/homekit/diagnostics.py +++ b/homeassistant/components/homekit/diagnostics.py @@ -67,13 +67,16 @@ def _get_accessory_diagnostics( hass: HomeAssistant, accessory: HomeAccessory ) -> dict[str, Any]: """Return diagnostics for an accessory.""" - return { + entity_state = None + if accessory.entity_id: + entity_state = hass.states.get(accessory.entity_id) + data = { "aid": accessory.aid, "config": accessory.config, "category": accessory.category, "name": accessory.display_name, "entity_id": accessory.entity_id, - "entity_state": async_redact_data( - hass.states.get(accessory.entity_id), TO_REDACT - ), } + if entity_state: + data["entity_state"] = async_redact_data(entity_state, TO_REDACT) + return data diff --git a/homeassistant/components/homekit/type_triggers.py b/homeassistant/components/homekit/type_triggers.py index b9b2ad6ce8f..b239d67877c 100644 --- a/homeassistant/components/homekit/type_triggers.py +++ b/homeassistant/components/homekit/type_triggers.py @@ -7,9 +7,11 @@ from typing import Any from pyhap.const import CATEGORY_SENSOR from homeassistant.core import CALLBACK_TYPE, Context +from homeassistant.helpers import entity_registry from homeassistant.helpers.trigger import async_initialize_triggers from .accessories import TYPES, HomeAccessory +from .aidmanager import get_system_unique_id from .const import ( CHAR_NAME, CHAR_PROGRAMMABLE_SWITCH_EVENT, @@ -18,6 +20,7 @@ from .const import ( SERV_SERVICE_LABEL, SERV_STATELESS_PROGRAMMABLE_SWITCH, ) +from .util import cleanup_name_for_homekit _LOGGER = logging.getLogger(__name__) @@ -39,13 +42,22 @@ class DeviceTriggerAccessory(HomeAccessory): self._remove_triggers: CALLBACK_TYPE | None = None self.triggers = [] assert device_triggers is not None + ent_reg = entity_registry.async_get(self.hass) for idx, trigger in enumerate(device_triggers): - type_ = trigger["type"] - subtype = trigger.get("subtype") + type_: str = trigger["type"] + subtype: str | None = trigger.get("subtype") unique_id = f'{type_}-{subtype or ""}' - trigger_name = ( - f"{type_.title()} {subtype.title()}" if subtype else type_.title() - ) + if (entity_id := trigger.get("entity_id")) and ( + entry := ent_reg.async_get(entity_id) + ): + unique_id += f"-entity_unique_id:{get_system_unique_id(entry)}" + trigger_name_parts = [] + if entity_id and (state := self.hass.states.get(entity_id)): + trigger_name_parts.append(state.name) + trigger_name_parts.append(type_.replace("_", " ").title()) + if subtype: + trigger_name_parts.append(subtype.replace("_", " ").title()) + trigger_name = cleanup_name_for_homekit(" ".join(trigger_name_parts)) serv_stateless_switch = self.add_preload_service( SERV_STATELESS_PROGRAMMABLE_SWITCH, [CHAR_NAME, CHAR_SERVICE_LABEL_INDEX], diff --git a/tests/components/homekit/test_diagnostics.py b/tests/components/homekit/test_diagnostics.py index d114c462e2f..30fe5f2d8fc 100644 --- a/tests/components/homekit/test_diagnostics.py +++ b/tests/components/homekit/test_diagnostics.py @@ -1,12 +1,14 @@ """Test homekit diagnostics.""" -from unittest.mock import ANY, patch +from unittest.mock import ANY, MagicMock, patch from homeassistant.components.homekit.const import ( + CONF_DEVICES, CONF_HOMEKIT_MODE, DOMAIN, HOMEKIT_MODE_ACCESSORY, ) from homeassistant.const import CONF_NAME, CONF_PORT, EVENT_HOMEASSISTANT_STARTED +from homeassistant.setup import async_setup_component from .util import async_init_integration @@ -290,3 +292,321 @@ async def test_config_entry_accessory( ), patch("homeassistant.components.homekit.async_port_is_available"): assert await hass.config_entries.async_unload(entry.entry_id) await hass.async_block_till_done() + + +async def test_config_entry_with_trigger_accessory( + hass, + hass_client, + hk_driver, + mock_async_zeroconf, + events, + demo_cleanup, + device_reg, + entity_reg, +): + """Test generating diagnostics for a bridge config entry with a trigger accessory.""" + assert await async_setup_component(hass, "demo", {"demo": {}}) + hk_driver.publish = MagicMock() + + demo_config_entry = MockConfigEntry(domain="domain") + demo_config_entry.add_to_hass(hass) + assert await async_setup_component(hass, "demo", {"demo": {}}) + await hass.async_block_till_done() + + entry = entity_reg.async_get("light.ceiling_lights") + assert entry is not None + device_id = entry.device_id + + entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_NAME: "mock_name", + CONF_PORT: 12345, + CONF_DEVICES: [device_id], + "filter": { + "exclude_domains": [], + "exclude_entities": [], + "include_domains": [], + "include_entities": ["light.none"], + }, + }, + ) + entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(entry.entry_id) + hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) + await hass.async_block_till_done() + diag = await get_diagnostics_for_config_entry(hass, hass_client, entry) + diag.pop("iid_storage") + diag.pop("bridge") + assert diag == { + "accessories": [ + { + "aid": 1, + "services": [ + { + "characteristics": [ + {"format": "bool", "iid": 2, "perms": ["pw"], "type": "14"}, + { + "format": "string", + "iid": 3, + "perms": ["pr"], + "type": "20", + "value": "Home Assistant", + }, + { + "format": "string", + "iid": 4, + "perms": ["pr"], + "type": "21", + "value": "Bridge", + }, + { + "format": "string", + "iid": 5, + "perms": ["pr"], + "type": "23", + "value": "mock_name", + }, + { + "format": "string", + "iid": 6, + "perms": ["pr"], + "type": "30", + "value": "homekit.bridge", + }, + { + "format": "string", + "iid": 7, + "perms": ["pr"], + "type": "52", + "value": "2022.12.0", + }, + ], + "iid": 1, + "type": "3E", + }, + { + "characteristics": [ + { + "format": "string", + "iid": 9, + "perms": ["pr", "ev"], + "type": "37", + "value": "01.01.00", + } + ], + "iid": 8, + "type": "A2", + }, + ], + }, + { + "aid": ANY, + "services": [ + { + "characteristics": [ + {"format": "bool", "iid": 2, "perms": ["pw"], "type": "14"}, + { + "format": "string", + "iid": 3, + "perms": ["pr"], + "type": "20", + "value": "Demo", + }, + { + "format": "string", + "iid": 4, + "perms": ["pr"], + "type": "21", + "value": "Home Assistant", + }, + { + "format": "string", + "iid": 5, + "perms": ["pr"], + "type": "23", + "value": "Ceiling Lights", + }, + { + "format": "string", + "iid": 6, + "perms": ["pr"], + "type": "30", + "value": ANY, + }, + { + "format": "string", + "iid": 7, + "perms": ["pr"], + "type": "52", + "value": ANY, + }, + ], + "iid": 1, + "type": "3E", + }, + { + "characteristics": [ + { + "format": "uint8", + "iid": 9, + "perms": ["pr", "ev"], + "type": "73", + "valid-values": [0], + "value": None, + }, + { + "format": "string", + "iid": 10, + "perms": ["pr"], + "type": "23", + "value": "Ceiling Lights " "Changed States", + }, + { + "format": "uint8", + "iid": 11, + "maxValue": 255, + "minStep": 1, + "minValue": 1, + "perms": ["pr"], + "type": "CB", + "value": 1, + }, + ], + "iid": 8, + "linked": [12], + "type": "89", + }, + { + "characteristics": [ + { + "format": "uint8", + "iid": 13, + "perms": ["pr"], + "type": "CD", + "valid-values": [0, 1], + "value": 1, + } + ], + "iid": 12, + "type": "CC", + }, + { + "characteristics": [ + { + "format": "uint8", + "iid": 15, + "perms": ["pr", "ev"], + "type": "73", + "valid-values": [0], + "value": None, + }, + { + "format": "string", + "iid": 16, + "perms": ["pr"], + "type": "23", + "value": "Ceiling Lights " "Turned Off", + }, + { + "format": "uint8", + "iid": 17, + "maxValue": 255, + "minStep": 1, + "minValue": 1, + "perms": ["pr"], + "type": "CB", + "value": 2, + }, + ], + "iid": 14, + "linked": [18], + "type": "89", + }, + { + "characteristics": [ + { + "format": "uint8", + "iid": 19, + "perms": ["pr"], + "type": "CD", + "valid-values": [0, 1], + "value": 1, + } + ], + "iid": 18, + "type": "CC", + }, + { + "characteristics": [ + { + "format": "uint8", + "iid": 21, + "perms": ["pr", "ev"], + "type": "73", + "valid-values": [0], + "value": None, + }, + { + "format": "string", + "iid": 22, + "perms": ["pr"], + "type": "23", + "value": "Ceiling Lights " "Turned On", + }, + { + "format": "uint8", + "iid": 23, + "maxValue": 255, + "minStep": 1, + "minValue": 1, + "perms": ["pr"], + "type": "CB", + "value": 3, + }, + ], + "iid": 20, + "linked": [24], + "type": "89", + }, + { + "characteristics": [ + { + "format": "uint8", + "iid": 25, + "perms": ["pr"], + "type": "CD", + "valid-values": [0, 1], + "value": 1, + } + ], + "iid": 24, + "type": "CC", + }, + ], + }, + ], + "client_properties": {}, + "config-entry": { + "data": {"name": "mock_name", "port": 12345}, + "options": { + "devices": [device_id], + "filter": { + "exclude_domains": [], + "exclude_entities": [], + "include_domains": [], + "include_entities": ["light.none"], + }, + }, + "title": "Mock Title", + "version": 1, + }, + "config_version": 2, + "pairing_id": ANY, + "status": 1, + } + with patch("pyhap.accessory_driver.AccessoryDriver.async_start"), patch( + "homeassistant.components.homekit.HomeKit.async_stop" + ), patch("homeassistant.components.homekit.async_port_is_available"): + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() diff --git a/tests/components/homekit/test_init.py b/tests/components/homekit/test_init.py index 17933616fc4..f91855b31b5 100644 --- a/tests/components/homekit/test_init.py +++ b/tests/components/homekit/test_init.py @@ -7,9 +7,17 @@ from homeassistant.components.homekit.const import ( DOMAIN as DOMAIN_HOMEKIT, EVENT_HOMEKIT_CHANGED, ) -from homeassistant.const import ATTR_ENTITY_ID, ATTR_SERVICE +from homeassistant.config_entries import SOURCE_ZEROCONF, ConfigEntryState +from homeassistant.const import ( + ATTR_ENTITY_ID, + ATTR_SERVICE, + EVENT_HOMEASSISTANT_STARTED, +) from homeassistant.setup import async_setup_component +from .util import PATH_HOMEKIT + +from tests.common import MockConfigEntry from tests.components.logbook.common import MockRow, mock_humanify @@ -52,3 +60,57 @@ async def test_humanify_homekit_changed_event(hass, hk_driver, mock_get_source_i assert event2["domain"] == DOMAIN_HOMEKIT assert event2["message"] == "send command set_cover_position to 75 for Window" assert event2["entity_id"] == "cover.window" + + +async def test_bridge_with_triggers( + hass, hk_driver, mock_async_zeroconf, entity_reg, caplog +): + """Test we can setup a bridge with triggers and we ignore numeric states. + + Since numeric states are not supported by HomeKit as they require + an above or below additional configuration which we have no way + to input, we ignore them. + """ + assert await async_setup_component(hass, "demo", {"demo": {}}) + await hass.async_block_till_done() + + entry = entity_reg.async_get("cover.living_room_window") + assert entry is not None + device_id = entry.device_id + + entry = MockConfigEntry( + domain=DOMAIN_HOMEKIT, + source=SOURCE_ZEROCONF, + data={ + "name": "HASS Bridge", + "port": 12345, + }, + options={ + "filter": { + "exclude_domains": [], + "exclude_entities": [], + "include_domains": [], + "include_entities": ["cover.living_room_window"], + }, + "exclude_accessory_mode": True, + "mode": "bridge", + "devices": [device_id], + }, + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.network.async_get_source_ip", return_value="1.2.3.4" + ), patch(f"{PATH_HOMEKIT}.async_port_is_available", return_value=True): + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) + await hass.async_block_till_done() + + assert entry.state == ConfigEntryState.LOADED + await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + + assert ( + "requires additional inputs which are not supported by HomeKit" in caplog.text + ) From 7932864e00039d3c35c1887463b527979ddf8083 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 15 Nov 2022 18:43:01 +0100 Subject: [PATCH 0433/1033] Always update attributes on an update for MQTT update (#82139) --- homeassistant/components/mqtt/update.py | 8 ++++---- tests/components/mqtt/test_update.py | 16 ++++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/mqtt/update.py b/homeassistant/components/mqtt/update.py index 5536d16d1c7..abad1cdb2ff 100644 --- a/homeassistant/components/mqtt/update.py +++ b/homeassistant/components/mqtt/update.py @@ -196,19 +196,19 @@ class MqttUpdate(MqttEntity, UpdateEntity, RestoreEntity): self._attr_latest_version = json_payload["latest_version"] get_mqtt_data(self.hass).state_write_requests.write_state_request(self) - if CONF_TITLE in json_payload and not self._attr_title: + if CONF_TITLE in json_payload: self._attr_title = json_payload[CONF_TITLE] get_mqtt_data(self.hass).state_write_requests.write_state_request(self) - if CONF_RELEASE_SUMMARY in json_payload and not self._attr_release_summary: + if CONF_RELEASE_SUMMARY in json_payload: self._attr_release_summary = json_payload[CONF_RELEASE_SUMMARY] get_mqtt_data(self.hass).state_write_requests.write_state_request(self) - if CONF_RELEASE_URL in json_payload and not self._attr_release_url: + if CONF_RELEASE_URL in json_payload: self._attr_release_url = json_payload[CONF_RELEASE_URL] get_mqtt_data(self.hass).state_write_requests.write_state_request(self) - if CONF_ENTITY_PICTURE in json_payload and not self._entity_picture: + if CONF_ENTITY_PICTURE in json_payload: self._entity_picture = json_payload[CONF_ENTITY_PICTURE] get_mqtt_data(self.hass).state_write_requests.write_state_request(self) diff --git a/tests/components/mqtt/test_update.py b/tests/components/mqtt/test_update.py index e7d75ee7cc8..a8f925bf4a6 100644 --- a/tests/components/mqtt/test_update.py +++ b/tests/components/mqtt/test_update.py @@ -203,8 +203,9 @@ async def test_json_state_message(hass, mqtt_mock_entry_with_yaml_config): hass, state_topic, '{"installed_version":"1.9.0","latest_version":"1.9.0",' - '"title":"Test Update Title","release_url":"https://example.com/release",' - '"release_summary":"Test release summary"}', + '"title":"Test Update 1 Title","release_url":"https://example.com/release1",' + '"release_summary":"Test release summary 1",' + '"entity_picture": "https://example.com/icon1.png"}', ) await hass.async_block_till_done() @@ -213,14 +214,16 @@ async def test_json_state_message(hass, mqtt_mock_entry_with_yaml_config): assert state.state == STATE_OFF assert state.attributes.get("installed_version") == "1.9.0" assert state.attributes.get("latest_version") == "1.9.0" - assert state.attributes.get("release_summary") == "Test release summary" - assert state.attributes.get("release_url") == "https://example.com/release" - assert state.attributes.get("title") == "Test Update Title" + assert state.attributes.get("release_summary") == "Test release summary 1" + assert state.attributes.get("release_url") == "https://example.com/release1" + assert state.attributes.get("title") == "Test Update 1 Title" + assert state.attributes.get("entity_picture") == "https://example.com/icon1.png" async_fire_mqtt_message( hass, state_topic, - '{"installed_version":"1.9.0","latest_version":"2.0.0","title":"Test Update Title"}', + '{"installed_version":"1.9.0","latest_version":"2.0.0",' + '"title":"Test Update 2 Title","entity_picture":"https://example.com/icon2.png"}', ) await hass.async_block_till_done() @@ -229,6 +232,7 @@ async def test_json_state_message(hass, mqtt_mock_entry_with_yaml_config): assert state.state == STATE_ON assert state.attributes.get("installed_version") == "1.9.0" assert state.attributes.get("latest_version") == "2.0.0" + assert state.attributes.get("entity_picture") == "https://example.com/icon2.png" async def test_json_state_message_with_template(hass, mqtt_mock_entry_with_yaml_config): From 435fc237375b86a5d6d8498ba5216c208b665ecc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Nov 2022 12:34:45 -0600 Subject: [PATCH 0434/1033] Add shelly ble scanner support (#82007) --- CODEOWNERS | 4 +- homeassistant/components/shelly/__init__.py | 4 +- .../components/shelly/bluetooth/__init__.py | 66 ++++++++ .../components/shelly/bluetooth/scanner.py | 47 ++++++ .../components/shelly/config_flow.py | 78 ++++++++- homeassistant/components/shelly/const.py | 16 ++ .../components/shelly/coordinator.py | 109 +++++++++--- homeassistant/components/shelly/manifest.json | 6 +- homeassistant/components/shelly/strings.json | 13 ++ .../components/shelly/translations/en.json | 13 ++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/shelly/__init__.py | 22 ++- tests/components/shelly/bluetooth/__init__.py | 2 + .../shelly/bluetooth/test_scanner.py | 133 +++++++++++++++ tests/components/shelly/conftest.py | 68 ++++++-- tests/components/shelly/test_button.py | 2 + tests/components/shelly/test_config_flow.py | 155 +++++++++++++++++- tests/components/shelly/test_init.py | 1 + 19 files changed, 694 insertions(+), 49 deletions(-) create mode 100644 homeassistant/components/shelly/bluetooth/__init__.py create mode 100644 homeassistant/components/shelly/bluetooth/scanner.py create mode 100644 tests/components/shelly/bluetooth/__init__.py create mode 100644 tests/components/shelly/bluetooth/test_scanner.py diff --git a/CODEOWNERS b/CODEOWNERS index 93715c3f93b..6ff584963dc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1013,8 +1013,8 @@ build.json @home-assistant/supervisor /tests/components/sharkiq/ @JeffResc @funkybunch @AritroSaha10 /homeassistant/components/shell_command/ @home-assistant/core /tests/components/shell_command/ @home-assistant/core -/homeassistant/components/shelly/ @balloob @bieniu @thecode @chemelli74 -/tests/components/shelly/ @balloob @bieniu @thecode @chemelli74 +/homeassistant/components/shelly/ @balloob @bieniu @thecode @chemelli74 @bdraco +/tests/components/shelly/ @balloob @bieniu @thecode @chemelli74 @bdraco /homeassistant/components/shodan/ @fabaff /homeassistant/components/sia/ @eavanvalkenburg /tests/components/sia/ @eavanvalkenburg diff --git a/homeassistant/components/shelly/__init__.py b/homeassistant/components/shelly/__init__.py index b65c314789a..1e77fae439e 100644 --- a/homeassistant/components/shelly/__init__.py +++ b/homeassistant/components/shelly/__init__.py @@ -6,7 +6,7 @@ from typing import Any, Final import aioshelly from aioshelly.block_device import BlockDevice from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError -from aioshelly.rpc_device import RpcDevice +from aioshelly.rpc_device import RpcDevice, UpdateType import voluptuous as vol from homeassistant.config_entries import ConfigEntry @@ -252,7 +252,7 @@ async def _async_setup_rpc_entry(hass: HomeAssistant, entry: ConfigEntry) -> boo hass.config_entries.async_setup_platforms(entry, platforms) @callback - def _async_device_online(_: Any) -> None: + def _async_device_online(_: Any, update_type: UpdateType) -> None: LOGGER.debug("Device %s is online, resuming setup", entry.title) shelly_entry_data.device = None diff --git a/homeassistant/components/shelly/bluetooth/__init__.py b/homeassistant/components/shelly/bluetooth/__init__.py new file mode 100644 index 00000000000..5e7f1c0ad4f --- /dev/null +++ b/homeassistant/components/shelly/bluetooth/__init__.py @@ -0,0 +1,66 @@ +"""Bluetooth support for shelly.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +from aioshelly.ble import async_start_scanner +from aioshelly.ble.const import ( + BLE_SCAN_RESULT_EVENT, + BLE_SCAN_RESULT_VERSION, + DEFAULT_DURATION_MS, + DEFAULT_INTERVAL_MS, + DEFAULT_WINDOW_MS, +) + +from homeassistant.components.bluetooth import ( + HaBluetoothConnector, + async_get_advertisement_callback, + async_register_scanner, +) +from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback +from homeassistant.helpers.device_registry import format_mac + +from ..const import BLEScannerMode +from .scanner import ShellyBLEScanner + +if TYPE_CHECKING: + from ..coordinator import ShellyRpcCoordinator + + +async def async_connect_scanner( + hass: HomeAssistant, + coordinator: ShellyRpcCoordinator, + scanner_mode: BLEScannerMode, +) -> CALLBACK_TYPE: + """Connect scanner.""" + device = coordinator.device + source = format_mac(coordinator.mac).upper() + new_info_callback = async_get_advertisement_callback(hass) + connector = HaBluetoothConnector( + # no active connections to shelly yet + client=None, # type: ignore[arg-type] + source=source, + can_connect=lambda: False, + ) + scanner = ShellyBLEScanner(hass, source, new_info_callback, connector, False) + unload_callbacks = [ + async_register_scanner(hass, scanner, False), + scanner.async_setup(), + coordinator.async_subscribe_events(scanner.async_on_event), + ] + await async_start_scanner( + device=device, + active=scanner_mode == BLEScannerMode.ACTIVE, + event_type=BLE_SCAN_RESULT_EVENT, + data_version=BLE_SCAN_RESULT_VERSION, + interval_ms=DEFAULT_INTERVAL_MS, + window_ms=DEFAULT_WINDOW_MS, + duration_ms=DEFAULT_DURATION_MS, + ) + + @hass_callback + def _async_unload() -> None: + for callback in unload_callbacks: + callback() + + return _async_unload diff --git a/homeassistant/components/shelly/bluetooth/scanner.py b/homeassistant/components/shelly/bluetooth/scanner.py new file mode 100644 index 00000000000..a6fb72ecc98 --- /dev/null +++ b/homeassistant/components/shelly/bluetooth/scanner.py @@ -0,0 +1,47 @@ +"""Bluetooth scanner for shelly.""" +from __future__ import annotations + +import logging +from typing import Any + +from aioshelly.ble import parse_ble_scan_result_event +from aioshelly.ble.const import BLE_SCAN_RESULT_EVENT, BLE_SCAN_RESULT_VERSION + +from homeassistant.components.bluetooth import BaseHaRemoteScanner +from homeassistant.core import callback + +_LOGGER = logging.getLogger(__name__) + + +class ShellyBLEScanner(BaseHaRemoteScanner): + """Scanner for shelly.""" + + @callback + def async_on_event(self, event: dict[str, Any]) -> None: + """Process an event from the shelly and ignore if its not a ble.scan_result.""" + if event.get("event") != BLE_SCAN_RESULT_EVENT: + return + + data = event["data"] + + if data[0] != BLE_SCAN_RESULT_VERSION: + _LOGGER.warning("Unsupported BLE scan result version: %s", data[0]) + return + + try: + address, rssi, parsed = parse_ble_scan_result_event(data) + except Exception as err: # pylint: disable=broad-except + # Broad exception catch because we have no + # control over the data that is coming in. + _LOGGER.error("Failed to parse BLE event: %s", err, exc_info=True) + return + + self._async_on_advertisement( + address, + rssi, + parsed.local_name, + parsed.service_uuids, + parsed.service_data, + parsed.manufacturer_data, + parsed.tx_power, + ) diff --git a/homeassistant/components/shelly/config_flow.py b/homeassistant/components/shelly/config_flow.py index 0f6ae9c9da6..d02fe9556bf 100644 --- a/homeassistant/components/shelly/config_flow.py +++ b/homeassistant/components/shelly/config_flow.py @@ -12,16 +12,25 @@ from aioshelly.exceptions import ( InvalidAuthError, ) from aioshelly.rpc_device import RpcDevice +from awesomeversion import AwesomeVersion import voluptuous as vol from homeassistant import config_entries from homeassistant.components import zeroconf from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult -from homeassistant.helpers import aiohttp_client +from homeassistant.helpers import aiohttp_client, selector -from .const import CONF_SLEEP_PERIOD, DOMAIN, LOGGER +from .const import ( + BLE_MIN_VERSION, + CONF_BLE_SCANNER_MODE, + CONF_SLEEP_PERIOD, + DOMAIN, + LOGGER, + BLEScannerMode, +) +from .coordinator import get_entry_data from .utils import ( get_block_device_name, get_block_device_sleep_period, @@ -37,6 +46,13 @@ from .utils import ( HOST_SCHEMA: Final = vol.Schema({vol.Required(CONF_HOST): str}) +BLE_SCANNER_OPTIONS = [ + selector.SelectOptionDict(value=BLEScannerMode.DISABLED, label="Disabled"), + selector.SelectOptionDict(value=BLEScannerMode.ACTIVE, label="Active"), + selector.SelectOptionDict(value=BLEScannerMode.PASSIVE, label="Passive"), +] + + async def validate_input( hass: HomeAssistant, host: str, @@ -310,3 +326,59 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return await aioshelly.common.get_info( aiohttp_client.async_get_clientsession(self.hass), host ) + + @staticmethod + @callback + def async_get_options_flow( + config_entry: config_entries.ConfigEntry, + ) -> OptionsFlowHandler: + """Get the options flow for this handler.""" + return OptionsFlowHandler(config_entry) + + @classmethod + @callback + def async_supports_options_flow( + cls, config_entry: config_entries.ConfigEntry + ) -> bool: + """Return options flow support for this handler.""" + return config_entry.data.get("gen") == 2 + + +class OptionsFlowHandler(config_entries.OptionsFlow): + """Handle the option flow for shelly.""" + + def __init__(self, config_entry: config_entries.ConfigEntry) -> None: + """Initialize options flow.""" + self.config_entry = config_entry + + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle options flow.""" + if user_input is not None: + entry_data = get_entry_data(self.hass)[self.config_entry.entry_id] + if user_input[CONF_BLE_SCANNER_MODE] != BLEScannerMode.DISABLED and ( + not entry_data.rpc + or AwesomeVersion(entry_data.rpc.device.version) < BLE_MIN_VERSION + ): + return self.async_abort( + reason="ble_unsupported", + description_placeholders={"ble_min_version": BLE_MIN_VERSION}, + ) + return self.async_create_entry(title="", data=user_input) + + return self.async_show_form( + step_id="init", + data_schema=vol.Schema( + { + vol.Required( + CONF_BLE_SCANNER_MODE, + default=self.config_entry.options.get( + CONF_BLE_SCANNER_MODE, BLEScannerMode.DISABLED + ), + ): selector.SelectSelector( + selector.SelectSelectorConfig(options=BLE_SCANNER_OPTIONS), + ), + } + ), + ) diff --git a/homeassistant/components/shelly/const.py b/homeassistant/components/shelly/const.py index 39ca515e5ed..81d4bfd7b14 100644 --- a/homeassistant/components/shelly/const.py +++ b/homeassistant/components/shelly/const.py @@ -5,6 +5,10 @@ from logging import Logger, getLogger import re from typing import Final +from awesomeversion import AwesomeVersion + +from homeassistant.backports.enum import StrEnum + DOMAIN: Final = "shelly" LOGGER: Logger = getLogger(__package__) @@ -156,3 +160,15 @@ UPTIME_DEVIATION: Final = 5 ENTRY_RELOAD_COOLDOWN = 60 SHELLY_GAS_MODELS = ["SHGS-1"] + +BLE_MIN_VERSION = AwesomeVersion("0.12.0-beta2") + +CONF_BLE_SCANNER_MODE = "ble_scanner_mode" + + +class BLEScannerMode(StrEnum): + """BLE scanner mode.""" + + DISABLED = "disabled" + ACTIVE = "active" + PASSIVE = "passive" diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py index 4a5b168e85c..9177d390c91 100644 --- a/homeassistant/components/shelly/coordinator.py +++ b/homeassistant/components/shelly/coordinator.py @@ -1,6 +1,7 @@ """Coordinators for the Shelly integration.""" from __future__ import annotations +import asyncio from collections.abc import Callable, Coroutine from dataclasses import dataclass from datetime import timedelta @@ -9,7 +10,8 @@ from typing import Any, cast import aioshelly from aioshelly.block_device import BlockDevice from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError -from aioshelly.rpc_device import RpcDevice +from aioshelly.rpc_device import RpcDevice, UpdateType +from awesomeversion import AwesomeVersion from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_DEVICE_ID, CONF_HOST, EVENT_HOMEASSISTANT_STOP @@ -18,12 +20,15 @@ from homeassistant.helpers import device_registry from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed +from .bluetooth import async_connect_scanner from .const import ( ATTR_CHANNEL, ATTR_CLICK_TYPE, ATTR_DEVICE, ATTR_GENERATION, BATTERY_DEVICES_WITH_PERMANENT_CONNECTION, + BLE_MIN_VERSION, + CONF_BLE_SCANNER_MODE, CONF_SLEEP_PERIOD, DATA_CONFIG_ENTRY, DOMAIN, @@ -40,6 +45,7 @@ from .const import ( SHBTN_MODELS, SLEEP_PERIOD_MULTIPLIER, UPDATE_PERIOD_MULTIPLIER, + BLEScannerMode, ) from .utils import ( device_update_info, @@ -336,7 +342,10 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): ) self.entry = entry self.device = device + self.connected = False + self._disconnected_callbacks: list[CALLBACK_TYPE] = [] + self._connection_lock = asyncio.Lock() self._event_listeners: list[Callable[[dict[str, Any]], None]] = [] self._debounced_reload: Debouncer[Coroutine[Any, Any, None]] = Debouncer( hass, @@ -346,16 +355,14 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): function=self._async_reload_entry, ) entry.async_on_unload(self._debounced_reload.async_cancel) - - self._last_event: dict[str, Any] | None = None - self._last_status: dict[str, Any] | None = None - entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self._handle_ha_stop) ) + entry.async_on_unload(entry.add_update_listener(self._async_update_listener)) async def _async_reload_entry(self) -> None: """Reload entry.""" + self._debounced_reload.async_cancel() LOGGER.debug("Reloading entry %s", self.name) await self.hass.config_entries.async_reload(self.entry.entry_id) @@ -390,12 +397,19 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): return _unsubscribe + async def _async_update_listener( + self, hass: HomeAssistant, entry: ConfigEntry + ) -> None: + """Reconfigure on update.""" + async with self._connection_lock: + if self.connected: + self._async_run_disconnected_events() + await self._async_run_connected_events() + @callback def _async_device_event_handler(self, event_data: dict[str, Any]) -> None: """Handle device events.""" - self.update_sleep_period() events: list[dict[str, Any]] = event_data["events"] - for event in events: event_type = event.get("event") if event_type is None: @@ -405,6 +419,7 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): event_callback(event) if event_type == "config_changed": + self.update_sleep_period() LOGGER.info( "Config for %s changed, reloading entry in %s seconds", self.name, @@ -460,21 +475,71 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): """Firmware version of the device.""" return self.device.firmware_version if self.device.initialized else "" - @callback - def _async_handle_update(self, device_: RpcDevice) -> None: - """Handle device update.""" - device = self.device - if not device.initialized: - return - event = device.event - status = device.status + async def _async_disconnected(self) -> None: + """Handle device disconnected.""" + async with self._connection_lock: + if not self.connected: # Already disconnected + return + self.connected = False + self._async_run_disconnected_events() - if event and event != self._last_event: - self._last_event = event + @callback + def _async_run_disconnected_events(self) -> None: + """Run disconnected events. + + This will be executed on disconnect or when the config entry + is updated. + """ + for disconnected_callback in self._disconnected_callbacks: + disconnected_callback() + self._disconnected_callbacks.clear() + + async def _async_connected(self) -> None: + """Handle device connected.""" + async with self._connection_lock: + if self.connected: # Already connected + return + self.connected = True + await self._async_run_connected_events() + + async def _async_run_connected_events(self) -> None: + """Run connected events. + + This will be executed on connect or when the config entry + is updated. + """ + await self._async_connect_ble_scanner() + + async def _async_connect_ble_scanner(self) -> None: + """Connect BLE scanner.""" + ble_scanner_mode = self.entry.options.get( + CONF_BLE_SCANNER_MODE, BLEScannerMode.DISABLED + ) + if ble_scanner_mode == BLEScannerMode.DISABLED: + return + if AwesomeVersion(self.device.version) < BLE_MIN_VERSION: + LOGGER.error( + "BLE not supported on device %s with firmware %s; upgrade to %s", + self.name, + self.device.version, + BLE_MIN_VERSION, + ) + return + self._disconnected_callbacks.append( + await async_connect_scanner(self.hass, self, ble_scanner_mode) + ) + + @callback + def _async_handle_update(self, device_: RpcDevice, update_type: UpdateType) -> None: + """Handle device update.""" + if update_type is UpdateType.INITIALIZED: + self.hass.async_create_task(self._async_connected()) + elif update_type is UpdateType.DISCONNECTED: + self.hass.async_create_task(self._async_disconnected()) + elif update_type is UpdateType.STATUS: + self.async_set_updated_data(self.device) + elif update_type is UpdateType.EVENT and (event := self.device.event): self._async_device_event_handler(event) - if status and status != self._last_status: - self._last_status = status - self.async_set_updated_data(device) def async_setup(self) -> None: """Set up the coordinator.""" @@ -491,10 +556,14 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): ) self.device_id = entry.id self.device.subscribe_updates(self._async_handle_update) + if self.device.initialized: + # If we are already initialized, we are connected + self.hass.async_create_task(self._async_connected()) async def shutdown(self) -> None: """Shutdown the coordinator.""" await self.device.shutdown() + await self._async_disconnected() async def _handle_ha_stop(self, _event: Event) -> None: """Handle Home Assistant stopping.""" diff --git a/homeassistant/components/shelly/manifest.json b/homeassistant/components/shelly/manifest.json index 70970e73e30..ce32dbcc41a 100644 --- a/homeassistant/components/shelly/manifest.json +++ b/homeassistant/components/shelly/manifest.json @@ -3,15 +3,15 @@ "name": "Shelly", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/shelly", - "requirements": ["aioshelly==4.1.2"], - "dependencies": ["http"], + "requirements": ["aioshelly==5.0.0"], + "dependencies": ["bluetooth", "http"], "zeroconf": [ { "type": "_http._tcp.local.", "name": "shelly*" } ], - "codeowners": ["@balloob", "@bieniu", "@thecode", "@chemelli74"], + "codeowners": ["@balloob", "@bieniu", "@thecode", "@chemelli74", "@bdraco"], "iot_class": "local_push", "loggers": ["aioshelly"], "integration_type": "device" diff --git a/homeassistant/components/shelly/strings.json b/homeassistant/components/shelly/strings.json index d3684f85be2..15f3be4d1e5 100644 --- a/homeassistant/components/shelly/strings.json +++ b/homeassistant/components/shelly/strings.json @@ -58,5 +58,18 @@ "double_push": "{subtype} double push", "long_push": "{subtype} long push" } + }, + "options": { + "step": { + "init": { + "description": "Bluetooth scanning can be active or passive. With active, the Shelly requests data from nearby devices; with passive, the Shelly receives unsolicited data from nearby devices.", + "data": { + "ble_scanner_mode": "Bluetooth scanner mode" + } + } + }, + "abort": { + "ble_unsupported": "Bluetooth support requires firmware version {ble_min_version} or newer." + } } } diff --git a/homeassistant/components/shelly/translations/en.json b/homeassistant/components/shelly/translations/en.json index dfc2e8aebec..4ca783b6fe4 100644 --- a/homeassistant/components/shelly/translations/en.json +++ b/homeassistant/components/shelly/translations/en.json @@ -58,5 +58,18 @@ "single_push": "{subtype} single push", "triple": "{subtype} triple clicked" } + }, + "options": { + "abort": { + "ble_unsupported": "Bluetooth support requires firmware version {ble_min_version} or newer." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Bluetooth scanner mode" + }, + "description": "Bluetooth scanning can be active or passive. With active, the Shelly requests data from nearby devices; with passive, the Shelly receives unsolicited data from nearby devices." + } + } } } \ No newline at end of file diff --git a/requirements_all.txt b/requirements_all.txt index 7803f34f498..3be11491e42 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -261,7 +261,7 @@ aiosenseme==0.6.1 aiosenz==1.0.0 # homeassistant.components.shelly -aioshelly==4.1.2 +aioshelly==5.0.0 # homeassistant.components.skybell aioskybell==22.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4268e45c12d..a7b8fdd9a5c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -236,7 +236,7 @@ aiosenseme==0.6.1 aiosenz==1.0.0 # homeassistant.components.shelly -aioshelly==4.1.2 +aioshelly==5.0.0 # homeassistant.components.skybell aioskybell==22.7.0 diff --git a/tests/components/shelly/__init__.py b/tests/components/shelly/__init__.py index da98520332a..a844367bced 100644 --- a/tests/components/shelly/__init__.py +++ b/tests/components/shelly/__init__.py @@ -1,4 +1,6 @@ """Tests for the Shelly integration.""" +from __future__ import annotations + from copy import deepcopy from typing import Any from unittest.mock import Mock @@ -15,7 +17,11 @@ MOCK_MAC = "123456789ABC" async def init_integration( - hass: HomeAssistant, gen: int, model="SHSW-25", sleep_period=0 + hass: HomeAssistant, + gen: int, + model="SHSW-25", + sleep_period=0, + options: dict[str, Any] | None = None, ) -> MockConfigEntry: """Set up the Shelly integration in Home Assistant.""" data = { @@ -25,7 +31,9 @@ async def init_integration( "gen": gen, } - entry = MockConfigEntry(domain=DOMAIN, data=data, unique_id=MOCK_MAC) + entry = MockConfigEntry( + domain=DOMAIN, data=data, unique_id=MOCK_MAC, options=options + ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -45,3 +53,13 @@ def mutate_rpc_device_status( new_status = deepcopy(mock_rpc_device.status) new_status[top_level_key][key] = value monkeypatch.setattr(mock_rpc_device, "status", new_status) + + +def inject_rpc_device_event( + monkeypatch: pytest.MonkeyPatch, + mock_rpc_device: Mock, + event: dict[str, dict[str, Any]], +) -> None: + """Inject event for rpc device.""" + monkeypatch.setattr(mock_rpc_device, "event", event) + mock_rpc_device.mock_event() diff --git a/tests/components/shelly/bluetooth/__init__.py b/tests/components/shelly/bluetooth/__init__.py new file mode 100644 index 00000000000..a4b1f4cdb7e --- /dev/null +++ b/tests/components/shelly/bluetooth/__init__.py @@ -0,0 +1,2 @@ +"""Bluetooth tests for Shelly integration.""" +from __future__ import annotations diff --git a/tests/components/shelly/bluetooth/test_scanner.py b/tests/components/shelly/bluetooth/test_scanner.py new file mode 100644 index 00000000000..8a429e20532 --- /dev/null +++ b/tests/components/shelly/bluetooth/test_scanner.py @@ -0,0 +1,133 @@ +"""Test the shelly bluetooth scanner.""" +from __future__ import annotations + +from aioshelly.ble.const import BLE_SCAN_RESULT_EVENT + +from homeassistant.components import bluetooth +from homeassistant.components.shelly.const import CONF_BLE_SCANNER_MODE, BLEScannerMode + +from .. import init_integration, inject_rpc_device_event + + +async def test_scanner(hass, mock_rpc_device, monkeypatch): + """Test injecting data into the scanner.""" + await init_integration( + hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} + ) + assert mock_rpc_device.initialized is True + inject_rpc_device_event( + monkeypatch, + mock_rpc_device, + { + "events": [ + { + "component": "script:1", + "data": [ + 1, + "aa:bb:cc:dd:ee:ff", + -62, + "AgEGCf9ZANH7O3TIkA==", + "EQcbxdWlAgC4n+YRTSIADaLLBhYADUgQYQ==", + ], + "event": BLE_SCAN_RESULT_EVENT, + "id": 1, + "ts": 1668522399.2, + } + ], + "ts": 1668522399.2, + }, + ) + ble_device = bluetooth.async_ble_device_from_address( + hass, "AA:BB:CC:DD:EE:FF", connectable=False + ) + assert ble_device is not None + ble_device = bluetooth.async_ble_device_from_address( + hass, "AA:BB:CC:DD:EE:FF", connectable=True + ) + assert ble_device is None + + +async def test_scanner_ignores_non_ble_events(hass, mock_rpc_device, monkeypatch): + """Test injecting non ble data into the scanner.""" + await init_integration( + hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} + ) + assert mock_rpc_device.initialized is True + inject_rpc_device_event( + monkeypatch, + mock_rpc_device, + { + "events": [ + { + "component": "script:1", + "data": [], + "event": "not_ble_scan_result", + "id": 1, + "ts": 1668522399.2, + } + ], + "ts": 1668522399.2, + }, + ) + + +async def test_scanner_ignores_wrong_version_and_logs( + hass, mock_rpc_device, monkeypatch, caplog +): + """Test injecting wrong version of ble data into the scanner.""" + await init_integration( + hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} + ) + assert mock_rpc_device.initialized is True + inject_rpc_device_event( + monkeypatch, + mock_rpc_device, + { + "events": [ + { + "component": "script:1", + "data": [ + 0, + "aa:bb:cc:dd:ee:ff", + -62, + "AgEGCf9ZANH7O3TIkA==", + "EQcbxdWlAgC4n+YRTSIADaLLBhYADUgQYQ==", + ], + "event": BLE_SCAN_RESULT_EVENT, + "id": 1, + "ts": 1668522399.2, + } + ], + "ts": 1668522399.2, + }, + ) + assert "Unsupported BLE scan result version: 0" in caplog.text + + +async def test_scanner_warns_on_corrupt_event( + hass, mock_rpc_device, monkeypatch, caplog +): + """Test injecting garbage ble data into the scanner.""" + await init_integration( + hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} + ) + assert mock_rpc_device.initialized is True + inject_rpc_device_event( + monkeypatch, + mock_rpc_device, + { + "events": [ + { + "component": "script:1", + "data": [ + 1, + ], + "event": BLE_SCAN_RESULT_EVENT, + "id": 1, + "ts": 1668522399.2, + } + ], + "ts": 1668522399.2, + }, + ) + assert "Failed to parse BLE event" in caplog.text diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py index cd23cc240c5..f27f91cbfe7 100644 --- a/tests/components/shelly/conftest.py +++ b/tests/components/shelly/conftest.py @@ -1,8 +1,10 @@ """Test configuration for Shelly.""" +from __future__ import annotations + from unittest.mock import AsyncMock, Mock, patch from aioshelly.block_device import BlockDevice -from aioshelly.rpc_device import RpcDevice +from aioshelly.rpc_device import RpcDevice, UpdateType import pytest from homeassistant.components.shelly.const import ( @@ -194,6 +196,7 @@ async def mock_block_device(): blocks=MOCK_BLOCKS, settings=MOCK_SETTINGS, shelly=MOCK_SHELLY_COAP, + version="0.10.0", status=MOCK_STATUS_COAP, firmware_version="some fw string", initialized=True, @@ -204,25 +207,62 @@ async def mock_block_device(): yield block_device_mock.return_value -@pytest.fixture -async def mock_rpc_device(): +def _mock_rpc_device(version: str | None = None): """Mock rpc (Gen2, Websocket) device.""" + return Mock( + spec=RpcDevice, + config=MOCK_CONFIG, + event={}, + shelly=MOCK_SHELLY_RPC, + version=version or "0.12.0", + status=MOCK_STATUS_RPC, + firmware_version="some fw string", + initialized=True, + ) + + +@pytest.fixture +async def mock_pre_ble_rpc_device(): + """Mock rpc (Gen2, Websocket) device pre BLE.""" with patch("aioshelly.rpc_device.RpcDevice.create") as rpc_device_mock: def update(): - rpc_device_mock.return_value.subscribe_updates.call_args[0][0]({}) - - device = Mock( - spec=RpcDevice, - config=MOCK_CONFIG, - event={}, - shelly=MOCK_SHELLY_RPC, - status=MOCK_STATUS_RPC, - firmware_version="some fw string", - initialized=True, - ) + rpc_device_mock.return_value.subscribe_updates.call_args[0][0]( + {}, UpdateType.STATUS + ) + device = _mock_rpc_device("0.11.0") rpc_device_mock.return_value = device rpc_device_mock.return_value.mock_update = Mock(side_effect=update) yield rpc_device_mock.return_value + + +@pytest.fixture +async def mock_rpc_device(): + """Mock rpc (Gen2, Websocket) device with BLE support.""" + with patch("aioshelly.rpc_device.RpcDevice.create") as rpc_device_mock, patch( + "homeassistant.components.shelly.bluetooth.async_start_scanner" + ): + + def update(): + rpc_device_mock.return_value.subscribe_updates.call_args[0][0]( + {}, UpdateType.STATUS + ) + + def event(): + rpc_device_mock.return_value.subscribe_updates.call_args[0][0]( + {}, UpdateType.EVENT + ) + + device = _mock_rpc_device("0.12.0") + rpc_device_mock.return_value = device + rpc_device_mock.return_value.mock_update = Mock(side_effect=update) + rpc_device_mock.return_value.mock_event = Mock(side_effect=event) + + yield rpc_device_mock.return_value + + +@pytest.fixture(autouse=True) +def mock_bluetooth(enable_bluetooth): + """Auto mock bluetooth.""" diff --git a/tests/components/shelly/test_button.py b/tests/components/shelly/test_button.py index bd20be7c645..2661f55d178 100644 --- a/tests/components/shelly/test_button.py +++ b/tests/components/shelly/test_button.py @@ -1,4 +1,6 @@ """Tests for Shelly button platform.""" +from __future__ import annotations + from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN from homeassistant.core import HomeAssistant diff --git a/tests/components/shelly/test_config_flow.py b/tests/components/shelly/test_config_flow.py index ad28ffbd4f0..fa53f8d3467 100644 --- a/tests/components/shelly/test_config_flow.py +++ b/tests/components/shelly/test_config_flow.py @@ -10,8 +10,15 @@ import pytest from homeassistant import config_entries, data_entry_flow from homeassistant.components import zeroconf -from homeassistant.components.shelly.const import DOMAIN +from homeassistant.components.shelly.const import ( + CONF_BLE_SCANNER_MODE, + DOMAIN, + BLEScannerMode, +) from homeassistant.config_entries import SOURCE_REAUTH +from homeassistant.setup import async_setup_component + +from . import init_integration from tests.common import MockConfigEntry @@ -880,3 +887,149 @@ async def test_reauth_get_info_error(hass, error): assert result["type"] == data_entry_flow.FlowResultType.ABORT assert result["reason"] == "reauth_unsuccessful" + + +async def test_options_flow_disabled_gen_1(hass, mock_block_device, hass_ws_client): + """Test options are disabled for gen1 devices.""" + await async_setup_component(hass, "config", {}) + entry = await init_integration(hass, 1) + + ws_client = await hass_ws_client(hass) + + await ws_client.send_json( + { + "id": 5, + "type": "config_entries/get", + "domain": "shelly", + } + ) + response = await ws_client.receive_json() + assert response["result"][0]["supports_options"] is False + await hass.config_entries.async_unload(entry.entry_id) + + +async def test_options_flow_enabled_gen_2(hass, mock_rpc_device, hass_ws_client): + """Test options are enabled for gen2 devices.""" + await async_setup_component(hass, "config", {}) + entry = await init_integration(hass, 2) + + ws_client = await hass_ws_client(hass) + + await ws_client.send_json( + { + "id": 5, + "type": "config_entries/get", + "domain": "shelly", + } + ) + response = await ws_client.receive_json() + assert response["result"][0]["supports_options"] is True + await hass.config_entries.async_unload(entry.entry_id) + + +async def test_options_flow_ble(hass, mock_rpc_device): + """Test setting ble options for gen2 devices.""" + entry = await init_integration(hass, 2) + result = await hass.config_entries.options.async_init(entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "init" + assert result["errors"] is None + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_BLE_SCANNER_MODE: BLEScannerMode.DISABLED, + }, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"][CONF_BLE_SCANNER_MODE] == BLEScannerMode.DISABLED + + result = await hass.config_entries.options.async_init(entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "init" + assert result["errors"] is None + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE, + }, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"][CONF_BLE_SCANNER_MODE] == BLEScannerMode.ACTIVE + + result = await hass.config_entries.options.async_init(entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "init" + assert result["errors"] is None + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_BLE_SCANNER_MODE: BLEScannerMode.PASSIVE, + }, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"][CONF_BLE_SCANNER_MODE] == BLEScannerMode.PASSIVE + + await hass.config_entries.async_unload(entry.entry_id) + + +async def test_options_flow_pre_ble_device(hass, mock_pre_ble_rpc_device): + """Test setting ble options for gen2 devices with pre ble firmware.""" + entry = await init_integration(hass, 2) + result = await hass.config_entries.options.async_init(entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "init" + assert result["errors"] is None + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_BLE_SCANNER_MODE: BLEScannerMode.DISABLED, + }, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"][CONF_BLE_SCANNER_MODE] == BLEScannerMode.DISABLED + + result = await hass.config_entries.options.async_init(entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "init" + assert result["errors"] is None + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE, + }, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "ble_unsupported" + + result = await hass.config_entries.options.async_init(entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "init" + assert result["errors"] is None + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_BLE_SCANNER_MODE: BLEScannerMode.PASSIVE, + }, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "ble_unsupported" + + await hass.config_entries.async_unload(entry.entry_id) diff --git a/tests/components/shelly/test_init.py b/tests/components/shelly/test_init.py index f795b79132f..251dc8a8da7 100644 --- a/tests/components/shelly/test_init.py +++ b/tests/components/shelly/test_init.py @@ -1,4 +1,5 @@ """Test cases for the Shelly component.""" +from __future__ import annotations from unittest.mock import AsyncMock From c940ad992046f9cd30d451df2d92a0a737ecf7c8 Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Tue, 15 Nov 2022 13:58:59 -0500 Subject: [PATCH 0435/1033] Add autocomplete to text selector (#81060) --- homeassistant/helpers/selector.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/helpers/selector.py b/homeassistant/helpers/selector.py index bd6dff03858..01f46d7e119 100644 --- a/homeassistant/helpers/selector.py +++ b/homeassistant/helpers/selector.py @@ -869,6 +869,7 @@ class TextSelectorConfig(TypedDict, total=False): multiline: bool suffix: str type: TextSelectorType + autocomplete: str class TextSelectorType(StrEnum): @@ -904,6 +905,7 @@ class TextSelector(Selector): vol.Optional("type"): vol.All( vol.Coerce(TextSelectorType), lambda val: val.value ), + vol.Optional("autocomplete"): str, } ) From 682187541f556d1714592b8e509ba8a8207319a9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Nov 2022 14:00:52 -0600 Subject: [PATCH 0436/1033] Move BluetoothServiceInfoBleak to home_assistant_bluetooth (#82064) --- homeassistant/components/bluetooth/manager.py | 15 ++++++- homeassistant/components/bluetooth/models.py | 40 ++----------------- .../bluetooth/update_coordinator.py | 3 +- .../components/ibeacon/coordinator.py | 2 +- homeassistant/package_constraints.txt | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- .../components/bluetooth/test_diagnostics.py | 8 ++++ tests/components/ibeacon/__init__.py | 17 ++++++++ tests/components/ibeacon/test_coordinator.py | 10 +++-- .../components/ibeacon/test_device_tracker.py | 2 +- tests/components/ibeacon/test_sensor.py | 2 +- 12 files changed, 56 insertions(+), 49 deletions(-) diff --git a/homeassistant/components/bluetooth/manager.py b/homeassistant/components/bluetooth/manager.py index d29023acef7..c216abde4ea 100644 --- a/homeassistant/components/bluetooth/manager.py +++ b/homeassistant/components/bluetooth/manager.py @@ -3,7 +3,6 @@ from __future__ import annotations import asyncio from collections.abc import Callable, Iterable -from dataclasses import replace from datetime import datetime, timedelta import itertools import logging @@ -442,7 +441,19 @@ class BluetoothManager: # route any connection attempts to the connectable path, we # mark the service_info as connectable so that the callbacks # will be called and the device can be discovered. - service_info = replace(service_info, connectable=True) + service_info = BluetoothServiceInfoBleak( + name=service_info.name, + address=service_info.address, + rssi=service_info.rssi, + manufacturer_data=service_info.manufacturer_data, + service_data=service_info.service_data, + service_uuids=service_info.service_uuids, + source=service_info.source, + device=service_info.device, + advertisement=service_info.advertisement, + connectable=True, + time=service_info.time, + ) matched_domains = self._integration_matcher.match_domains(service_info) _LOGGER.debug( diff --git a/homeassistant/components/bluetooth/models.py b/homeassistant/components/bluetooth/models.py index 4e386ea3746..f324d6086f7 100644 --- a/homeassistant/components/bluetooth/models.py +++ b/homeassistant/components/bluetooth/models.py @@ -21,11 +21,14 @@ from bleak.backends.scanner import ( BaseBleakScanner, ) from bleak_retry_connector import NO_RSSI_VALUE, freshen_ble_device +from home_assistant_bluetooth import BluetoothServiceInfoBleak from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.frame import report -from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo +from homeassistant.helpers.service_info.bluetooth import ( # noqa: F401 # pylint: disable=unused-import + BluetoothServiceInfo, +) from homeassistant.util.dt import monotonic_time_coarse from .const import FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS @@ -53,41 +56,6 @@ MANAGER: BluetoothManager | None = None MONOTONIC_TIME: Final = monotonic_time_coarse -@dataclass -class BluetoothServiceInfoBleak(BluetoothServiceInfo): - """BluetoothServiceInfo with bleak data. - - Integrations may need BLEDevice and AdvertisementData - to connect to the device without having bleak trigger - another scan to translate the address to the system's - internal details. - """ - - device: BLEDevice - advertisement: AdvertisementData - connectable: bool - time: float - - def as_dict(self) -> dict[str, Any]: - """Return as dict. - - The dataclass asdict method is not used because - it will try to deepcopy pyobjc data which will fail. - """ - return { - "name": self.name, - "address": self.address, - "rssi": self.rssi, - "manufacturer_data": self.manufacturer_data, - "service_data": self.service_data, - "service_uuids": self.service_uuids, - "source": self.source, - "advertisement": self.advertisement, - "connectable": self.connectable, - "time": self.time, - } - - class BluetoothScanningMode(Enum): """The mode of scanning for bluetooth devices.""" diff --git a/homeassistant/components/bluetooth/update_coordinator.py b/homeassistant/components/bluetooth/update_coordinator.py index 2c99f189852..a02e601a878 100644 --- a/homeassistant/components/bluetooth/update_coordinator.py +++ b/homeassistant/components/bluetooth/update_coordinator.py @@ -3,6 +3,7 @@ from __future__ import annotations from abc import abstractmethod import logging +from typing import cast from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback @@ -69,7 +70,7 @@ class BasePassiveBluetoothCoordinator: if service_info := async_last_service_info( self.hass, self.address, self.connectable ): - return service_info.name + return cast(str, service_info.name) # for compat this can be a pyobjc return self._last_name @property diff --git a/homeassistant/components/ibeacon/coordinator.py b/homeassistant/components/ibeacon/coordinator.py index ed62649de2d..bab0a5b5655 100644 --- a/homeassistant/components/ibeacon/coordinator.py +++ b/homeassistant/components/ibeacon/coordinator.py @@ -62,7 +62,7 @@ def async_name( """Return a name for the device.""" if service_info.address in ( service_info.name, - service_info.name.replace("_", ":"), + service_info.name.replace("-", ":"), ): base_name = f"{ibeacon_advertisement.uuid}_{ibeacon_advertisement.major}_{ibeacon_advertisement.minor}" else: diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 346b61fd1a8..0a862e040b6 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -21,7 +21,7 @@ cryptography==38.0.3 dbus-fast==1.74.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 -home-assistant-bluetooth==1.6.0 +home-assistant-bluetooth==1.8.0 home-assistant-frontend==20221108.0 httpx==0.23.0 ifaddr==0.1.7 diff --git a/pyproject.toml b/pyproject.toml index 3a74b2c5994..6aa8c9d3f0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ dependencies = [ # When bumping httpx, please check the version pins of # httpcore, anyio, and h11 in gen_requirements_all "httpx==0.23.0", - "home-assistant-bluetooth==1.6.0", + "home-assistant-bluetooth==1.8.0", "ifaddr==0.1.7", "jinja2==3.1.2", "lru-dict==1.1.8", diff --git a/requirements.txt b/requirements.txt index 96a9f801df9..e748db517b5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ bcrypt==3.1.7 certifi>=2021.5.30 ciso8601==2.2.0 httpx==0.23.0 -home-assistant-bluetooth==1.6.0 +home-assistant-bluetooth==1.8.0 ifaddr==0.1.7 jinja2==3.1.2 lru-dict==1.1.8 diff --git a/tests/components/bluetooth/test_diagnostics.py b/tests/components/bluetooth/test_diagnostics.py index a8d4d7aa142..56321175241 100644 --- a/tests/components/bluetooth/test_diagnostics.py +++ b/tests/components/bluetooth/test_diagnostics.py @@ -227,6 +227,10 @@ async def test_diagnostics_macos( -127, [[]], ], + "device": { + "__type": "", + "repr": "BLEDevice(44:44:33:11:23:45, " "wohand)", + }, "connectable": True, "manufacturer_data": { "1": {"__type": "", "repr": "b'\\x01'"} @@ -251,6 +255,10 @@ async def test_diagnostics_macos( -127, [[]], ], + "device": { + "__type": "", + "repr": "BLEDevice(44:44:33:11:23:45, " "wohand)", + }, "connectable": True, "manufacturer_data": { "1": {"__type": "", "repr": "b'\\x01'"} diff --git a/tests/components/ibeacon/__init__.py b/tests/components/ibeacon/__init__.py index 56d5eb78467..50636ee9d48 100644 --- a/tests/components/ibeacon/__init__.py +++ b/tests/components/ibeacon/__init__.py @@ -1,4 +1,6 @@ """Tests for the ibeacon integration.""" +from typing import Any + from bleak.backends.device import BLEDevice from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo @@ -115,3 +117,18 @@ FEASY_BEACON_SERVICE_INFO_2 = BluetoothServiceInfo( ], source="local", ) + + +def bluetooth_service_info_replace( + info: BluetoothServiceInfo, **kwargs: Any +) -> BluetoothServiceInfo: + """Replace attributes of a BluetoothServiceInfoBleak.""" + return BluetoothServiceInfo( + address=kwargs.get("address", info.address), + name=kwargs.get("name", info.name), + rssi=kwargs.get("rssi", info.rssi), + manufacturer_data=kwargs.get("manufacturer_data", info.manufacturer_data), + service_data=kwargs.get("service_data", info.service_data), + service_uuids=kwargs.get("service_uuids", info.service_uuids), + source=kwargs.get("source", info.source), + ) diff --git a/tests/components/ibeacon/test_coordinator.py b/tests/components/ibeacon/test_coordinator.py index 6acbf5569f8..89bc932f7a0 100644 --- a/tests/components/ibeacon/test_coordinator.py +++ b/tests/components/ibeacon/test_coordinator.py @@ -1,7 +1,6 @@ """Test the ibeacon sensors.""" -from dataclasses import replace from datetime import timedelta import time @@ -19,6 +18,7 @@ from . import ( BLUECHARM_BEACON_SERVICE_INFO_DBUS, TESLA_TRANSIENT, TESLA_TRANSIENT_BLE_DEVICE, + bluetooth_service_info_replace as replace, ) from tests.common import MockConfigEntry, async_fire_time_changed @@ -145,16 +145,17 @@ async def test_ignore_default_name(hass): assert len(hass.states.async_entity_ids()) == before_entity_count -async def test_rotating_major_minor_and_mac(hass): +async def test_rotating_major_minor_and_mac_with_name(hass): """Test the different uuid, major, minor from many addresses removes all associated entities.""" entry = MockConfigEntry( domain=DOMAIN, ) entry.add_to_hass(hass) - before_entity_count = len(hass.states.async_entity_ids("device_tracker")) assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() + before_entity_count = len(hass.states.async_entity_ids("device_tracker")) + for i in range(100): service_info = BluetoothServiceInfo( name="BlueCharm_177999", @@ -186,9 +187,10 @@ async def test_rotating_major_minor_and_mac_no_name(hass): ) entry.add_to_hass(hass) - before_entity_count = len(hass.states.async_entity_ids("device_tracker")) assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() + before_entity_count = len(hass.states.async_entity_ids("device_tracker")) + for i in range(51): service_info = BluetoothServiceInfo( name=f"AA:BB:CC:DD:EE:{i:02X}", diff --git a/tests/components/ibeacon/test_device_tracker.py b/tests/components/ibeacon/test_device_tracker.py index f3520f835ca..b16144d7d90 100644 --- a/tests/components/ibeacon/test_device_tracker.py +++ b/tests/components/ibeacon/test_device_tracker.py @@ -1,7 +1,6 @@ """Test the ibeacon device trackers.""" -from dataclasses import replace from datetime import timedelta import time from unittest.mock import patch @@ -22,6 +21,7 @@ from . import ( BEACON_RANDOM_ADDRESS_SERVICE_INFO, BLUECHARM_BEACON_SERVICE_INFO, BLUECHARM_BLE_DEVICE, + bluetooth_service_info_replace as replace, ) from tests.common import MockConfigEntry, async_fire_time_changed diff --git a/tests/components/ibeacon/test_sensor.py b/tests/components/ibeacon/test_sensor.py index 671172efe93..f6a2ab51430 100644 --- a/tests/components/ibeacon/test_sensor.py +++ b/tests/components/ibeacon/test_sensor.py @@ -1,7 +1,6 @@ """Test the ibeacon sensors.""" -from dataclasses import replace from datetime import timedelta import pytest @@ -24,6 +23,7 @@ from . import ( FEASY_BEACON_SERVICE_INFO_1, FEASY_BEACON_SERVICE_INFO_2, NO_NAME_BEACON_SERVICE_INFO, + bluetooth_service_info_replace as replace, ) from tests.common import MockConfigEntry, async_fire_time_changed From 35c1604ea753c447c3d5380212c4f557723799e2 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 15 Nov 2022 22:26:34 +0200 Subject: [PATCH 0437/1033] Remove unused hassfest helper (#82123) --- script/hassfest/manifest_helper.py | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 script/hassfest/manifest_helper.py diff --git a/script/hassfest/manifest_helper.py b/script/hassfest/manifest_helper.py deleted file mode 100644 index 0c2a1456ec6..00000000000 --- a/script/hassfest/manifest_helper.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Helpers to deal with manifests.""" -import json -import pathlib - -component_dir = pathlib.Path("homeassistant/components") - - -def iter_manifests(): - """Iterate over all available manifests.""" - manifests = [ - json.loads(fil.read_text()) for fil in component_dir.glob("*/manifest.json") - ] - return sorted(manifests, key=lambda man: man["domain"]) From 8038485ca47ec5ff57e877e73d5de6d5c16ab11c Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 15 Nov 2022 22:45:48 +0200 Subject: [PATCH 0438/1033] Use partition instead of split where possible in core (#81806) --- homeassistant/auth/permissions/entities.py | 2 +- homeassistant/auth/providers/command_line.py | 6 +++--- homeassistant/bootstrap.py | 2 +- homeassistant/config.py | 2 +- homeassistant/helpers/check_config.py | 2 +- homeassistant/helpers/entity_component.py | 2 +- homeassistant/helpers/service.py | 2 +- homeassistant/helpers/trace.py | 2 +- homeassistant/helpers/translation.py | 7 ++----- homeassistant/setup.py | 7 ++----- homeassistant/util/__init__.py | 2 +- 11 files changed, 15 insertions(+), 21 deletions(-) diff --git a/homeassistant/auth/permissions/entities.py b/homeassistant/auth/permissions/entities.py index 3f2a0c14f19..4dc221a9ff4 100644 --- a/homeassistant/auth/permissions/entities.py +++ b/homeassistant/auth/permissions/entities.py @@ -47,7 +47,7 @@ def _lookup_domain( perm_lookup: PermissionLookup, domains_dict: SubCategoryDict, entity_id: str ) -> ValueType | None: """Look up entity permissions by domain.""" - return domains_dict.get(entity_id.split(".", 1)[0]) + return domains_dict.get(entity_id.partition(".")[0]) def _lookup_area( diff --git a/homeassistant/auth/providers/command_line.py b/homeassistant/auth/providers/command_line.py index af9a01f5d9b..92d8d617481 100644 --- a/homeassistant/auth/providers/command_line.py +++ b/homeassistant/auth/providers/command_line.py @@ -88,12 +88,12 @@ class CommandLineAuthProvider(AuthProvider): for _line in stdout.splitlines(): try: line = _line.decode().lstrip() - if line.startswith("#"): - continue - key, value = line.split("=", 1) except ValueError: # malformed line continue + if line.startswith("#") or "=" not in line: + continue + key, _, value = line.partition("=") key = key.strip() value = value.strip() if key in self.ALLOWED_META_KEYS: diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 31834c7b7a3..9ac8b2ef6e6 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -404,7 +404,7 @@ async def async_mount_local_lib_path(config_dir: str) -> str: def _get_domains(hass: core.HomeAssistant, config: dict[str, Any]) -> set[str]: """Get domains of components to set up.""" # Filter out the repeating and common config section [homeassistant] - domains = {key.split(" ")[0] for key in config if key != core.DOMAIN} + domains = {key.partition(" ")[0] for key in config if key != core.DOMAIN} # Add config entry domains if not hass.config.safe_mode: diff --git a/homeassistant/config.py b/homeassistant/config.py index c58f94ca197..a422cbea1d9 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -734,7 +734,7 @@ async def merge_packages_config( continue # If component name is given with a trailing description, remove it # when looking for component - domain = comp_name.split(" ")[0] + domain = comp_name.partition(" ")[0] try: integration = await async_get_integration_with_requirements( diff --git a/homeassistant/helpers/check_config.py b/homeassistant/helpers/check_config.py index 3bda89c9a73..bc17330f656 100644 --- a/homeassistant/helpers/check_config.py +++ b/homeassistant/helpers/check_config.py @@ -122,7 +122,7 @@ async def async_check_ha_config_file( # noqa: C901 core_config.pop(CONF_PACKAGES, None) # Filter out repeating config sections - components = {key.split(" ")[0] for key in config.keys()} + components = {key.partition(" ")[0] for key in config.keys()} # Process and validate config for domain in components: diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index f7f2b7e3dd3..1932c0397a1 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -36,7 +36,7 @@ _EntityT = TypeVar("_EntityT", bound=entity.Entity) @bind_hass async def async_update_entity(hass: HomeAssistant, entity_id: str) -> None: """Trigger an update for an entity.""" - domain = entity_id.split(".", 1)[0] + domain = entity_id.partition(".")[0] entity_comp: EntityComponent[entity.Entity] | None entity_comp = hass.data.get(DATA_INSTANCES, {}).get(domain) diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 138fa739794..ad90998cd7d 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -202,7 +202,7 @@ def async_prepare_call_from_config( f"Template rendered invalid service: {domain_service}" ) from ex - domain, service = domain_service.split(".", 1) + domain, _, service = domain_service.partition(".") target = {} if CONF_TARGET in config: diff --git a/homeassistant/helpers/trace.py b/homeassistant/helpers/trace.py index bc3e7ff3565..3a5fb83395a 100644 --- a/homeassistant/helpers/trace.py +++ b/homeassistant/helpers/trace.py @@ -63,7 +63,7 @@ class TraceElement: """Return dictionary version of this TraceElement.""" result: dict[str, Any] = {"path": self.path, "timestamp": self._timestamp} if self._child_key is not None: - domain, item_id = self._child_key.split(".", 1) + domain, _, item_id = self._child_key.partition(".") result["child_id"] = { "domain": domain, "item_id": item_id, diff --git a/homeassistant/helpers/translation.py b/homeassistant/helpers/translation.py index 616baeeea92..d1953b2fd00 100644 --- a/homeassistant/helpers/translation.py +++ b/homeassistant/helpers/translation.py @@ -97,10 +97,7 @@ def _merge_resources( # Build response resources: dict[str, dict[str, Any]] = {} for component in components: - if "." not in component: - domain = component - else: - domain = component.split(".", 1)[0] + domain = component.partition(".")[0] domain_resources = resources.setdefault(domain, {}) @@ -148,7 +145,7 @@ async def async_get_component_strings( hass: HomeAssistant, language: str, components: set[str] ) -> dict[str, Any]: """Load translations.""" - domains = list({loaded.split(".")[-1] for loaded in components}) + domains = list({loaded.rpartition(".")[-1] for loaded in components}) integrations: dict[str, Integration] = {} ints_or_excs = await async_get_integrations(hass, domains) diff --git a/homeassistant/setup.py b/homeassistant/setup.py index d85e4043505..2be22910a08 100644 --- a/homeassistant/setup.py +++ b/homeassistant/setup.py @@ -433,7 +433,7 @@ def async_get_loaded_integrations(hass: core.HomeAssistant) -> set[str]: if "." not in component: integrations.add(component) continue - domain, platform = component.split(".", 1) + domain, _, platform = component.partition(".") if domain in BASE_PLATFORMS: integrations.add(platform) return integrations @@ -458,10 +458,7 @@ def async_start_setup( time_taken = dt_util.utcnow() - started for unique, domain in unique_components.items(): del setup_started[unique] - if "." in domain: - _, integration = domain.split(".", 1) - else: - integration = domain + integration = domain.rpartition(".")[-1] if integration in setup_time: setup_time[integration] += time_taken else: diff --git a/homeassistant/util/__init__.py b/homeassistant/util/__init__.py index 6562ecedb4f..315c8ebda74 100644 --- a/homeassistant/util/__init__.py +++ b/homeassistant/util/__init__.py @@ -156,7 +156,7 @@ class Throttle: # be prefixed by '..' so we strip that out. is_func = ( not hasattr(method, "__self__") - and "." not in method.__qualname__.split("..")[-1] + and "." not in method.__qualname__.rpartition("..")[-1] ) @wraps(method) From a2e638329e1e75dafb2a3b7572799ea7efc0d13d Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 15 Nov 2022 21:52:50 +0100 Subject: [PATCH 0439/1033] Update geopy to 2.3.0 (#82145) --- homeassistant/components/aprs/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/aprs/manifest.json b/homeassistant/components/aprs/manifest.json index 6979eab4516..d12af3e6c7e 100644 --- a/homeassistant/components/aprs/manifest.json +++ b/homeassistant/components/aprs/manifest.json @@ -3,7 +3,7 @@ "name": "APRS", "documentation": "https://www.home-assistant.io/integrations/aprs", "codeowners": ["@PhilRW"], - "requirements": ["aprslib==0.7.0", "geopy==2.1.0"], + "requirements": ["aprslib==0.7.0", "geopy==2.3.0"], "iot_class": "cloud_push", "loggers": ["aprslib", "geographiclib", "geopy"] } diff --git a/requirements_all.txt b/requirements_all.txt index 3be11491e42..fb185bead9b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -749,7 +749,7 @@ geniushub-client==0.6.30 geocachingapi==0.2.1 # homeassistant.components.aprs -geopy==2.1.0 +geopy==2.3.0 # homeassistant.components.geo_rss_events georss_generic_client==0.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a7b8fdd9a5c..c3654ae0d94 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -562,7 +562,7 @@ gcal-sync==4.0.2 geocachingapi==0.2.1 # homeassistant.components.aprs -geopy==2.1.0 +geopy==2.3.0 # homeassistant.components.geo_rss_events georss_generic_client==0.6 From 7614aba401ffd4041d9dac09344d37b7525c4685 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Nov 2022 15:22:33 -0600 Subject: [PATCH 0440/1033] Stop shelly BLE scanner on unload (#82151) * Stop shelly BLE scanner on unload needs https://github.com/home-assistant-libs/aioshelly/pull/285 * bump --- homeassistant/components/shelly/coordinator.py | 3 +++ homeassistant/components/shelly/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py index 9177d390c91..1de02f56a07 100644 --- a/homeassistant/components/shelly/coordinator.py +++ b/homeassistant/components/shelly/coordinator.py @@ -8,6 +8,7 @@ from datetime import timedelta from typing import Any, cast import aioshelly +from aioshelly.ble import async_stop_scanner from aioshelly.block_device import BlockDevice from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError from aioshelly.rpc_device import RpcDevice, UpdateType @@ -516,6 +517,7 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): CONF_BLE_SCANNER_MODE, BLEScannerMode.DISABLED ) if ble_scanner_mode == BLEScannerMode.DISABLED: + await async_stop_scanner(self.device) return if AwesomeVersion(self.device.version) < BLE_MIN_VERSION: LOGGER.error( @@ -562,6 +564,7 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): async def shutdown(self) -> None: """Shutdown the coordinator.""" + await async_stop_scanner(self.device) await self.device.shutdown() await self._async_disconnected() diff --git a/homeassistant/components/shelly/manifest.json b/homeassistant/components/shelly/manifest.json index ce32dbcc41a..aea453127fd 100644 --- a/homeassistant/components/shelly/manifest.json +++ b/homeassistant/components/shelly/manifest.json @@ -3,7 +3,7 @@ "name": "Shelly", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/shelly", - "requirements": ["aioshelly==5.0.0"], + "requirements": ["aioshelly==5.1.0"], "dependencies": ["bluetooth", "http"], "zeroconf": [ { diff --git a/requirements_all.txt b/requirements_all.txt index fb185bead9b..75181ecaf4e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -261,7 +261,7 @@ aiosenseme==0.6.1 aiosenz==1.0.0 # homeassistant.components.shelly -aioshelly==5.0.0 +aioshelly==5.1.0 # homeassistant.components.skybell aioskybell==22.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c3654ae0d94..70ed2b28bc5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -236,7 +236,7 @@ aiosenseme==0.6.1 aiosenz==1.0.0 # homeassistant.components.shelly -aioshelly==5.0.0 +aioshelly==5.1.0 # homeassistant.components.skybell aioskybell==22.7.0 From ae7272575a30e73fc9d6cecb0217cd15a4ba0d59 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Nov 2022 15:37:45 -0600 Subject: [PATCH 0441/1033] Enable shelly BLE when the scanner is enabled if it is disabled (#82153) Co-authored-by: Shay Levy --- homeassistant/components/shelly/coordinator.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py index 1de02f56a07..11bdf933037 100644 --- a/homeassistant/components/shelly/coordinator.py +++ b/homeassistant/components/shelly/coordinator.py @@ -8,7 +8,7 @@ from datetime import timedelta from typing import Any, cast import aioshelly -from aioshelly.ble import async_stop_scanner +from aioshelly.ble import async_ensure_ble_enabled, async_stop_scanner from aioshelly.block_device import BlockDevice from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError from aioshelly.rpc_device import RpcDevice, UpdateType @@ -527,6 +527,10 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): BLE_MIN_VERSION, ) return + if await async_ensure_ble_enabled(self.device): + # BLE enable required a reboot, don't bother connecting + # the scanner since it will be disconnected anyway + return self._disconnected_callbacks.append( await async_connect_scanner(self.hass, self, ble_scanner_mode) ) From 21b32804d17881a0faf78457eba0bc43fb432867 Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Tue, 15 Nov 2022 17:07:43 -0500 Subject: [PATCH 0442/1033] Fix Google Sheets formula input (#82157) --- homeassistant/components/google_sheets/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/google_sheets/__init__.py b/homeassistant/components/google_sheets/__init__.py index e211693bf21..19f5ce81f5c 100644 --- a/homeassistant/components/google_sheets/__init__.py +++ b/homeassistant/components/google_sheets/__init__.py @@ -7,6 +7,7 @@ import aiohttp from google.auth.exceptions import RefreshError from google.oauth2.credentials import Credentials from gspread import Client +from gspread.utils import ValueInputOption import voluptuous as vol from homeassistant.config_entries import ConfigEntry, ConfigEntryState @@ -100,7 +101,7 @@ async def async_setup_service(hass: HomeAssistant) -> None: columns.append(key) worksheet.update_cell(1, len(columns), key) row.append(value) - worksheet.append_row(row) + worksheet.append_row(row, value_input_option=ValueInputOption.user_entered) async def append_to_sheet(call: ServiceCall) -> None: """Append new line of data to a Google Sheets document.""" From 361bd6bb818a99fb7bbb612dc5ea3a83b64ff68c Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 16 Nov 2022 00:27:00 +0000 Subject: [PATCH 0443/1033] [ci skip] Translation update --- .../airthings_ble/translations/hy.json | 18 +++ .../components/knx/translations/bg.json | 12 ++ .../components/knx/translations/pl.json | 125 +++++++++++++++++- .../components/mikrotik/translations/hy.json | 11 ++ .../components/shelly/translations/ca.json | 9 ++ .../components/shelly/translations/de.json | 13 ++ .../components/shelly/translations/et.json | 13 ++ .../unifiprotect/translations/bg.json | 15 +++ .../unifiprotect/translations/pl.json | 17 +++ .../unifiprotect/translations/ru.json | 4 + 10 files changed, 233 insertions(+), 4 deletions(-) create mode 100644 homeassistant/components/airthings_ble/translations/hy.json create mode 100644 homeassistant/components/mikrotik/translations/hy.json diff --git a/homeassistant/components/airthings_ble/translations/hy.json b/homeassistant/components/airthings_ble/translations/hy.json new file mode 100644 index 00000000000..6284ab97571 --- /dev/null +++ b/homeassistant/components/airthings_ble/translations/hy.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "cannot_connect": "\u0549\u0570\u0561\u057b\u0578\u0572\u057e\u0565\u0581 \u0574\u056b\u0561\u0576\u0561\u056c", + "no_devices_found": "\u0551\u0561\u0576\u0581\u0578\u0582\u0574 \u057d\u0561\u0580\u0584\u0565\u0580 \u0579\u0565\u0576 \u0563\u057f\u0576\u057e\u0565\u056c", + "unknown": "\u0531\u0576\u057d\u057a\u0561\u057d\u0565\u056c\u056b \u057d\u056d\u0561\u056c" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "\u0551\u0561\u0576\u056f\u0561\u0576\u0578\u0582\u055e\u0574 \u0565\u0584 \u056f\u0561\u0580\u0563\u0561\u057e\u0578\u0580\u0565\u056c {name}-\u0568:" + }, + "user": { + "description": "\u0538\u0576\u057f\u0580\u0565\u0584 \u057d\u0561\u0580\u0584\u0568 \u056f\u0561\u0580\u0563\u0561\u057e\u0578\u0580\u0565\u056c\u0578\u0582 \u0570\u0561\u0574\u0561\u0580" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/knx/translations/bg.json b/homeassistant/components/knx/translations/bg.json index 72bbfb4a80c..022fd141b76 100644 --- a/homeassistant/components/knx/translations/bg.json +++ b/homeassistant/components/knx/translations/bg.json @@ -55,6 +55,18 @@ "host": "\u0425\u043e\u0441\u0442", "local_ip": "\u041b\u043e\u043a\u0430\u043b\u0435\u043d IP \u043d\u0430 Home Assistant", "port": "\u041f\u043e\u0440\u0442" + }, + "data_description": { + "local_ip": "\u041e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0437\u0430 \u0434\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435." + } + }, + "routing": { + "data": { + "individual_address": "\u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u0435\u043d \u0430\u0434\u0440\u0435\u0441", + "local_ip": "\u041b\u043e\u043a\u0430\u043b\u0435\u043d IP \u043d\u0430 Home Assistant" + }, + "data_description": { + "local_ip": "\u041e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0437\u0430 \u0434\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435." } }, "tunnel": { diff --git a/homeassistant/components/knx/translations/pl.json b/homeassistant/components/knx/translations/pl.json index 23185a0ae49..0bb9bbdfb1a 100644 --- a/homeassistant/components/knx/translations/pl.json +++ b/homeassistant/components/knx/translations/pl.json @@ -9,20 +9,30 @@ "file_not_found": "Podany plik '.knxkeys' nie zosta\u0142 znaleziony w \u015bcie\u017cce config/.storage/knx/", "invalid_individual_address": "Warto\u015b\u0107 nie pasuje do wzorca dla indywidualnego adresu KNX.\n 'obszar.linia.urz\u0105dzenie'", "invalid_ip_address": "Nieprawid\u0142owy adres IPv4.", - "invalid_signature": "Has\u0142o do odszyfrowania pliku '.knxkeys' jest nieprawid\u0142owe." + "invalid_signature": "Has\u0142o do odszyfrowania pliku '.knxkeys' jest nieprawid\u0142owe.", + "no_router_discovered": "Nie wykryto w sieci routera KNXnet/IP.", + "no_tunnel_discovered": "Nie mo\u017cna znale\u017a\u0107 serwera tuneluj\u0105cego KNX w Twojej sieci." }, "step": { + "connection_type": { + "data": { + "connection_type": "Typ po\u0142\u0105czenia KNX" + }, + "description": "Prosz\u0119 wprowadzi\u0107 typ po\u0142\u0105czenia, kt\u00f3rego powinni\u015bmy u\u017cy\u0107 dla po\u0142\u0105czenia KNX. \nAUTOMATIC - Integracja sama zadba o po\u0142\u0105czenie z magistral\u0105 KNX poprzez skanowanie bramki. \nTUNNELING - Integracja po\u0142\u0105czy si\u0119 z magistral\u0105 KNX poprzez tunelowanie. \nROUTING - Integracja po\u0142\u0105czy si\u0119 z magistral\u0105 KNX poprzez routing." + }, "manual_tunnel": { "data": { "host": "Nazwa hosta lub adres IP", "local_ip": "Lokalny adres IP Home Assistanta", "port": "Port", + "route_back": "Tryb Route Back / NAT", "tunneling_type": "Typ tunelowania KNX" }, "data_description": { "host": "Adres IP urz\u0105dzenia tuneluj\u0105cego KNX/IP.", "local_ip": "Pozostaw puste, aby u\u017cy\u0107 automatycznego wykrywania.", - "port": "Port urz\u0105dzenia tuneluj\u0105cego KNX/IP." + "port": "Port urz\u0105dzenia tuneluj\u0105cego KNX/IP.", + "route_back": "W\u0142\u0105cz, je\u015bli serwer tuneluj\u0105cy KNXnet/IP znajduje si\u0119 za NAT. Dotyczy tylko po\u0142\u0105cze\u0144 UDP." }, "description": "Prosz\u0119 wprowadzi\u0107 informacje o po\u0142\u0105czeniu urz\u0105dzenia tuneluj\u0105cego." }, @@ -63,11 +73,25 @@ }, "description": "Wprowad\u017a informacje o IP secure." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Has\u0142o uwierzytelniania urz\u0105dzenia", + "user_id": "Identyfikator u\u017cytkownika", + "user_password": "Has\u0142o u\u017cytkownika" + }, + "data_description": { + "device_authentication": "Jest to ustawiane w panelu \u201eIP\u201d interfejsu w ETS.", + "user_id": "Cz\u0119sto jest to numer tunelu plus 1. Tak wi\u0119c \u201eTunnel 2\u201d mia\u0142by identyfikator u\u017cytkownika \u201e3\u201d.", + "user_password": "Has\u0142o dla konkretnego po\u0142\u0105czenia tunelowego ustawione w panelu \u201eW\u0142a\u015bciwo\u015bci\u201d tunelu w ETS." + }, + "description": "Wprowad\u017a informacje o IP secure." + }, "secure_tunneling": { "description": "Wybierz, jak chcesz skonfigurowa\u0107 KNX/IP secure.", "menu_options": { "secure_knxkeys": "U\u017cyj pliku `.knxkeys` zawieraj\u0105cego klucze IP secure", - "secure_manual": "R\u0119czna konfiguracja kluczy IP secure" + "secure_manual": "R\u0119czna konfiguracja kluczy IP secure", + "secure_tunnel_manual": "R\u0119czna konfiguracja kluczy IP secure" } }, "tunnel": { @@ -85,7 +109,32 @@ } }, "options": { + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "file_not_found": "Podany plik '.knxkeys' nie zosta\u0142 znaleziony w \u015bcie\u017cce config/.storage/knx/", + "invalid_individual_address": "Warto\u015b\u0107 nie pasuje do wzorca dla indywidualnego adresu KNX.\n 'obszar.linia.urz\u0105dzenie'", + "invalid_ip_address": "Nieprawid\u0142owy adres IPv4.", + "invalid_signature": "Has\u0142o do odszyfrowania pliku '.knxkeys' jest nieprawid\u0142owe.", + "no_router_discovered": "Nie wykryto w sieci routera KNXnet/IP.", + "no_tunnel_discovered": "Nie mo\u017cna znale\u017a\u0107 serwera tuneluj\u0105cego KNX w Twojej sieci." + }, "step": { + "communication_settings": { + "data": { + "rate_limit": "Limit", + "state_updater": "Aktualizator stanu" + }, + "data_description": { + "rate_limit": "Maksymalna liczba wychodz\u0105cych wiadomo\u015bci na sekund\u0119.\n \u201e0\u201d, aby wy\u0142\u0105czy\u0107 limit. Zalecane: 0 lub 20 do 40", + "state_updater": "Ustaw domy\u015blne odczytywanie stan\u00f3w z magistrali KNX. Po wy\u0142\u0105czeniu, Home Assistant nie b\u0119dzie aktywnie pobiera\u0107 stan\u00f3w encji z magistrali KNX. Mo\u017cna to zast\u0105pi\u0107 przez opcj\u0119 encji `sync_state`." + } + }, + "connection_type": { + "data": { + "connection_type": "Typ po\u0142\u0105czenia KNX" + }, + "description": "Prosz\u0119 wprowadzi\u0107 typ po\u0142\u0105czenia, kt\u00f3rego powinni\u015bmy u\u017cy\u0107 dla po\u0142\u0105czenia KNX. \nAUTOMATIC - Integracja sama zadba o po\u0142\u0105czenie z magistral\u0105 KNX poprzez skanowanie bramki. \nTUNNELING - Integracja po\u0142\u0105czy si\u0119 z magistral\u0105 KNX poprzez tunelowanie. \nROUTING - Integracja po\u0142\u0105czy si\u0119 z magistral\u0105 KNX poprzez routing." + }, "init": { "data": { "connection_type": "Typ po\u0142\u0105czenia KNX", @@ -105,8 +154,75 @@ "state_updater": "Ustaw domy\u015blne odczytywanie stan\u00f3w z magistrali KNX. Po wy\u0142\u0105czeniu, Home Assistant nie b\u0119dzie aktywnie pobiera\u0107 stan\u00f3w encji z magistrali KNX. Mo\u017cna to zast\u0105pi\u0107 przez opcj\u0119 encji `sync_state`." } }, + "manual_tunnel": { + "data": { + "host": "Nazwa hosta lub adres IP", + "local_ip": "Lokalny adres IP Home Assistanta", + "port": "Port", + "route_back": "Tryb Route Back / NAT", + "tunneling_type": "Typ tunelowania KNX" + }, + "data_description": { + "host": "Adres IP urz\u0105dzenia tuneluj\u0105cego KNX/IP.", + "local_ip": "Pozostaw puste, aby u\u017cy\u0107 automatycznego wykrywania.", + "port": "Port urz\u0105dzenia tuneluj\u0105cego KNX/IP.", + "route_back": "W\u0142\u0105cz, je\u015bli serwer tuneluj\u0105cy KNXnet/IP znajduje si\u0119 za NAT. Dotyczy tylko po\u0142\u0105cze\u0144 UDP." + }, + "description": "Prosz\u0119 wprowadzi\u0107 informacje o po\u0142\u0105czeniu urz\u0105dzenia tuneluj\u0105cego." + }, + "options_init": { + "menu_options": { + "communication_settings": "Ustawienia komunikacji", + "connection_type": "Konfiguracja interfejsu KNX" + } + }, + "routing": { + "data": { + "individual_address": "Adres indywidualny", + "local_ip": "Lokalny adres IP Home Assistanta", + "multicast_group": "Grupa multicast", + "multicast_port": "Port multicast" + }, + "data_description": { + "individual_address": "Adres KNX u\u017cywany przez Home Assistanta, np. `0.0.4`", + "local_ip": "Pozostaw puste, aby u\u017cy\u0107 automatycznego wykrywania." + }, + "description": "Prosz\u0119 skonfigurowa\u0107 opcje routingu." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Nazwa pliku `.knxkeys` (wraz z rozszerzeniem)", + "knxkeys_password": "Has\u0142o do odszyfrowania pliku `.knxkeys`" + }, + "data_description": { + "knxkeys_filename": "Plik powinien znajdowa\u0107 si\u0119 w katalogu konfiguracyjnym w `.storage/knx/`.\nW systemie Home Assistant OS b\u0119dzie to `/config/.storage/knx/`\nPrzyk\u0142ad: `m\u00f3j_projekt.knxkeys`", + "knxkeys_password": "Zosta\u0142o to ustawione podczas eksportowania pliku z ETS." + }, + "description": "Wprowad\u017a informacje dotycz\u0105ce pliku `.knxkeys`." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Has\u0142o uwierzytelniania urz\u0105dzenia", + "user_id": "Identyfikator u\u017cytkownika", + "user_password": "Has\u0142o u\u017cytkownika" + }, + "data_description": { + "device_authentication": "Jest to ustawiane w panelu \u201eIP\u201d interfejsu w ETS.", + "user_id": "Cz\u0119sto jest to numer tunelu plus 1. Tak wi\u0119c \u201eTunnel 2\u201d mia\u0142by identyfikator u\u017cytkownika \u201e3\u201d.", + "user_password": "Has\u0142o dla konkretnego po\u0142\u0105czenia tunelowego ustawione w panelu \u201eW\u0142a\u015bciwo\u015bci\u201d tunelu w ETS." + }, + "description": "Wprowad\u017a informacje o IP secure." + }, + "secure_tunneling": { + "description": "Wybierz, jak chcesz skonfigurowa\u0107 KNX/IP secure.", + "menu_options": { + "secure_knxkeys": "U\u017cyj pliku `.knxkeys` zawieraj\u0105cego klucze IP secure", + "secure_tunnel_manual": "R\u0119czna konfiguracja kluczy IP secure" + } + }, "tunnel": { "data": { + "gateway": "Po\u0142\u0105czenie tunelowe KNX", "host": "Nazwa hosta lub adres IP", "port": "Port", "tunneling_type": "Typ tunelowania KNX" @@ -114,7 +230,8 @@ "data_description": { "host": "Adres IP urz\u0105dzenia tuneluj\u0105cego KNX/IP.", "port": "Port urz\u0105dzenia tuneluj\u0105cego KNX/IP." - } + }, + "description": "Prosz\u0119 wybra\u0107 bramk\u0119 z listy." } } } diff --git a/homeassistant/components/mikrotik/translations/hy.json b/homeassistant/components/mikrotik/translations/hy.json new file mode 100644 index 00000000000..a3a5d6d7c2f --- /dev/null +++ b/homeassistant/components/mikrotik/translations/hy.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "data": { + "password": "\u0533\u0561\u0572\u057f\u0576\u0561\u0562\u0561\u057c" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/ca.json b/homeassistant/components/shelly/translations/ca.json index 4dd4626b6dc..4406edce9f7 100644 --- a/homeassistant/components/shelly/translations/ca.json +++ b/homeassistant/components/shelly/translations/ca.json @@ -58,5 +58,14 @@ "single_push": "{subtype} clicat una vegada", "triple": "{subtype} clicat tres vegades" } + }, + "options": { + "step": { + "init": { + "data": { + "ble_scanner_mode": "Mode d'escaneig Bluetooth" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/de.json b/homeassistant/components/shelly/translations/de.json index 4990f40a93a..cf030e13a11 100644 --- a/homeassistant/components/shelly/translations/de.json +++ b/homeassistant/components/shelly/translations/de.json @@ -58,5 +58,18 @@ "single_push": "{subtype} einfacher Druck", "triple": "{subtype} dreifach bet\u00e4tigt" } + }, + "options": { + "abort": { + "ble_unsupported": "Bluetooth-Unterst\u00fctzung erfordert Firmware-Version {ble_min_version} oder neuer." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Bluetooth-Scannermodus" + }, + "description": "Bluetooth-Scannen kann aktiv oder passiv sein. Bei aktiv fordert Shelly Daten von Ger\u00e4ten in der N\u00e4he an; Mit Passiv empf\u00e4ngt Shelly unaufgefordert Daten von Ger\u00e4ten in der N\u00e4he." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/et.json b/homeassistant/components/shelly/translations/et.json index bc68caeb9bb..69889526619 100644 --- a/homeassistant/components/shelly/translations/et.json +++ b/homeassistant/components/shelly/translations/et.json @@ -58,5 +58,18 @@ "single_push": "{subtype} l\u00fchike vajutus", "triple": "Nuppu {subtype} kl\u00f5psati kolm korda" } + }, + "options": { + "abort": { + "ble_unsupported": "Bluetoothi tugi n\u00f5uab p\u00fcsivara versiooni {ble_min_version} v\u00f5i uuemat." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Bluetooth-sk\u00e4nneri re\u017eiim" + }, + "description": "Bluetoothi skannimine v\u00f5ib olla aktiivne v\u00f5i passiivne. Aktiivse oleku korral k\u00fcsib Shelly andmeid l\u00e4hedalasuvatest seadmetest, passiivse puhul saab Shelly l\u00e4hedalasuvatest seadmetest soovimatuid andmeid." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/bg.json b/homeassistant/components/unifiprotect/translations/bg.json index c5b9cada9e0..ca8d83e0111 100644 --- a/homeassistant/components/unifiprotect/translations/bg.json +++ b/homeassistant/components/unifiprotect/translations/bg.json @@ -34,6 +34,21 @@ } } }, + "issues": { + "ea_warning": { + "fix_flow": { + "step": { + "confirm": { + "title": "v{version} \u0435 \u0432\u0435\u0440\u0441\u0438\u044f \u0441 \u0440\u0430\u043d\u0435\u043d \u0434\u043e\u0441\u0442\u044a\u043f" + }, + "start": { + "title": "v{version} \u0435 \u0432\u0435\u0440\u0441\u0438\u044f \u0441 \u0440\u0430\u043d\u0435\u043d \u0434\u043e\u0441\u0442\u044a\u043f" + } + } + }, + "title": "UniFi Protect v{version} \u0435 \u0432\u0435\u0440\u0441\u0438\u044f \u0441 \u0440\u0430\u043d\u0435\u043d \u0434\u043e\u0441\u0442\u044a\u043f" + } + }, "options": { "error": { "invalid_mac_list": "\u0422\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0431\u044a\u0434\u0435 \u0441\u043f\u0438\u0441\u044a\u043a \u0441 MAC \u0430\u0434\u0440\u0435\u0441\u0438, \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438 \u0441\u044a\u0441 \u0437\u0430\u043f\u0435\u0442\u0430\u0438" diff --git a/homeassistant/components/unifiprotect/translations/pl.json b/homeassistant/components/unifiprotect/translations/pl.json index 22ccc97ca07..7b74d044bcb 100644 --- a/homeassistant/components/unifiprotect/translations/pl.json +++ b/homeassistant/components/unifiprotect/translations/pl.json @@ -42,8 +42,24 @@ } }, "issues": { + "ea_setup_failed": { + "description": "U\u017cywasz wersji {version} UniFi Protect, kt\u00f3ra jest wersj\u0105 Early Access. Wyst\u0105pi\u0142 nienaprawialny b\u0142\u0105d podczas pr\u00f3by za\u0142adowania integracji. Aby kontynuowa\u0107 korzystanie z integracji, [zmie\u0144 wersj\u0119 na stabiln\u0105](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) UniFi Protect. \n\nB\u0142\u0105d: {error}", + "title": "B\u0142\u0105d konfiguracji w wersji Early Access" + }, "ea_warning": { "description": "U\u017cywasz UniFi Protect v{version}, kt\u00f3ra jest wersj\u0105 Early Access. Wersje Early Access nie s\u0105 obs\u0142ugiwane przez Home Assistanta i mog\u0105 spowodowa\u0107 uszkodzenie integracji UniFi Protect lub niedzia\u0142anie zgodnie z oczekiwaniami.", + "fix_flow": { + "step": { + "confirm": { + "description": "Czy na pewno chcesz uruchomi\u0107 nieobs\u0142ugiwane wersje UniFi Protect? Mo\u017ce to popsu\u0107 integracj\u0119 Home Assistant.", + "title": "Wersja {version} jest wersj\u0105 Early Access" + }, + "start": { + "description": "U\u017cywasz wersji {version} UniFi Protect, kt\u00f3ra jest wersj\u0105 Early Access. [Wersje Early Access nie s\u0105 obs\u0142ugiwane przez Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) i zaleca si\u0119 jak najszybszy powr\u00f3t do stabilnej wersji.\n\nPrzesy\u0142aj\u0105c ten formularz, [obni\u017cy\u0142e\u015b wersj\u0119 UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) albo zgadzasz si\u0119 na uruchomienie nieobs\u0142ugiwanej wersji UniFi Protect.", + "title": "Wersja {version} jest wersj\u0105 Early Access" + } + } + }, "title": "UniFi Protect v{version} to wersja Early Access" } }, @@ -55,6 +71,7 @@ "init": { "data": { "all_updates": "Metryki w czasie rzeczywistym (UWAGA: Znacznie zwi\u0119ksza u\u017cycie CPU)", + "allow_ea": "Zezw\u00f3l na wersje UniFi Protect Early Access (OSTRZE\u017bENIE: Twoja integracja zostanie oznaczona jako nieobs\u0142ugiwana)", "disable_rtsp": "Wy\u0142\u0105cz strumie\u0144 RTSP", "ignored_devices": "Oddzielona przecinkami lista adres\u00f3w MAC urz\u0105dze\u0144 do zignorowania", "max_media": "Maksymalna liczba zdarze\u0144 do za\u0142adowania dla przegl\u0105darki medi\u00f3w (zwi\u0119ksza u\u017cycie pami\u0119ci RAM)", diff --git a/homeassistant/components/unifiprotect/translations/ru.json b/homeassistant/components/unifiprotect/translations/ru.json index c90b4eec0f4..0789e3f29ea 100644 --- a/homeassistant/components/unifiprotect/translations/ru.json +++ b/homeassistant/components/unifiprotect/translations/ru.json @@ -42,6 +42,10 @@ } }, "issues": { + "ea_setup_failed": { + "description": "\u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 UniFi Protect v{version}, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0435\u0440\u0441\u0438\u0435\u0439 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430. \u041f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u0443\u0441\u0442\u0440\u0430\u043d\u0438\u043c\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, [\u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect), \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.\n\n\u041e\u0448\u0438\u0431\u043a\u0430: {error}", + "title": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0432\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430" + }, "ea_warning": { "description": "\u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 UniFi Protect v{version}. \u0412\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f Home Assistant \u0438 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.", "title": "UniFi Protect v{version} \u2014 \u044d\u0442\u043e \u0432\u0435\u0440\u0441\u0438\u044f \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430" From 248b93f112e0734af1375fcfa442431ee765f6fd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Nov 2022 20:38:31 -0600 Subject: [PATCH 0444/1033] Update aiohomekit to 2.3.0 (#82164) --- .../homekit_controller/connection.py | 22 +------------------ .../homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/homekit_controller/connection.py b/homeassistant/components/homekit_controller/connection.py index f158cd49e9c..dbcd8b28fc2 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -721,27 +721,7 @@ class HKDevice: self, characteristics: Iterable[tuple[int, int, Any]] ) -> None: """Control a HomeKit device state from Home Assistant.""" - results = await self.pairing.put_characteristics(characteristics) - - # Feed characteristics back into HA and update the current state - # results will only contain failures, so anythin in characteristics - # but not in results was applied successfully - we can just have HA - # reflect the change immediately. - - new_entity_state = {} - for aid, iid, value in characteristics: - key = (aid, iid) - - # If the key was returned by put_characteristics() then the - # change didn't work - if key in results: - continue - - # Otherwise it was accepted and we can apply the change to - # our state - new_entity_state[key] = {"value": value} - - self.process_new_events(new_entity_state) + await self.pairing.put_characteristics(characteristics) @property def unique_id(self) -> str: diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index f0438a7b841..864ec1e9bd0 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.2.19"], + "requirements": ["aiohomekit==2.3.0"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index 75181ecaf4e..38a8c4bc40a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.19 +aiohomekit==2.3.0 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 70ed2b28bc5..3893f747920 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.2.19 +aiohomekit==2.3.0 # homeassistant.components.emulated_hue # homeassistant.components.http From 495ca67e8b83e9a3026a1b0434cd5e10bf91326e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Nov 2022 20:38:46 -0600 Subject: [PATCH 0445/1033] Bump xiaomi_ble to 0.12.2 (#82167) --- homeassistant/components/xiaomi_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/xiaomi_ble/manifest.json b/homeassistant/components/xiaomi_ble/manifest.json index 3c66e8bc0ca..3f02c4e8767 100644 --- a/homeassistant/components/xiaomi_ble/manifest.json +++ b/homeassistant/components/xiaomi_ble/manifest.json @@ -13,7 +13,7 @@ "service_data_uuid": "0000fe95-0000-1000-8000-00805f9b34fb" } ], - "requirements": ["xiaomi-ble==0.12.1"], + "requirements": ["xiaomi-ble==0.12.2"], "dependencies": ["bluetooth"], "codeowners": ["@Jc2k", "@Ernst79"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 38a8c4bc40a..03778c2609e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2576,7 +2576,7 @@ xbox-webapi==2.0.11 xboxapi==2.0.1 # homeassistant.components.xiaomi_ble -xiaomi-ble==0.12.1 +xiaomi-ble==0.12.2 # homeassistant.components.knx xknx==1.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3893f747920..983c60b3f74 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1789,7 +1789,7 @@ wolf_smartset==0.1.11 xbox-webapi==2.0.11 # homeassistant.components.xiaomi_ble -xiaomi-ble==0.12.1 +xiaomi-ble==0.12.2 # homeassistant.components.knx xknx==1.2.0 From aedbfdabee67e3e7b915b842d28f151a08cb3d7f Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Wed, 16 Nov 2022 04:04:41 +0100 Subject: [PATCH 0446/1033] Use HERE API v8 (#80892) * Use HERE API v8 Signed-off-by: Kevin Stillhammer * Add migration Signed-off-by: Kevin Stillhammer * Catch correct voluptuous error Signed-off-by: Kevin Stillhammer * Use list comprehension for transit values * Add migration alternative Signed-off-by: Kevin Stillhammer --- .../components/here_travel_time/__init__.py | 232 +------- .../here_travel_time/config_flow.py | 61 +-- .../components/here_travel_time/const.py | 11 - .../here_travel_time/coordinator.py | 290 ++++++++++ .../components/here_travel_time/manifest.json | 4 +- .../components/here_travel_time/model.py | 4 +- .../components/here_travel_time/sensor.py | 10 +- requirements_all.txt | 5 +- requirements_test_all.txt | 5 +- tests/components/here_travel_time/conftest.py | 30 +- tests/components/here_travel_time/const.py | 27 +- .../fixtures/car_response.json | 500 +++++++----------- .../fixtures/empty_attribution_response.json | 131 ----- ...no_attribution_transit_route_response.json | 145 +++++ .../fixtures/transit_route_response.json | 153 ++++++ .../here_travel_time/test_config_flow.py | 74 +-- .../components/here_travel_time/test_init.py | 28 +- .../here_travel_time/test_sensor.py | 211 ++++---- 18 files changed, 1044 insertions(+), 877 deletions(-) create mode 100644 homeassistant/components/here_travel_time/coordinator.py delete mode 100644 tests/components/here_travel_time/fixtures/empty_attribution_response.json create mode 100644 tests/components/here_travel_time/fixtures/no_attribution_transit_route_response.json create mode 100644 tests/components/here_travel_time/fixtures/transit_route_response.json diff --git a/homeassistant/components/here_travel_time/__init__.py b/homeassistant/components/here_travel_time/__init__.py index b9ffa3e4baa..28ecf3f3d8d 100644 --- a/homeassistant/components/here_travel_time/__init__.py +++ b/homeassistant/components/here_travel_time/__init__.py @@ -1,38 +1,14 @@ """The HERE Travel Time integration.""" from __future__ import annotations -from datetime import datetime, time, timedelta import logging -import async_timeout -from herepy import NoRouteFoundError, RouteMode, RoutingApi, RoutingResponse -import voluptuous as vol - from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_ATTRIBUTION, - CONF_API_KEY, - CONF_MODE, - CONF_UNIT_SYSTEM, - LENGTH_METERS, - LENGTH_MILES, - Platform, -) +from homeassistant.const import CONF_API_KEY, CONF_MODE, CONF_UNIT_SYSTEM, Platform from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.location import find_coordinates -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.util import dt -from homeassistant.util.unit_conversion import DistanceConverter from .const import ( - ATTR_DESTINATION, - ATTR_DESTINATION_NAME, - ATTR_DISTANCE, - ATTR_DURATION, - ATTR_DURATION_IN_TRAFFIC, - ATTR_ORIGIN, - ATTR_ORIGIN_NAME, CONF_ARRIVAL_TIME, CONF_DEPARTURE_TIME, CONF_DESTINATION_ENTITY_ID, @@ -42,24 +18,22 @@ from .const import ( CONF_ORIGIN_LATITUDE, CONF_ORIGIN_LONGITUDE, CONF_ROUTE_MODE, - DEFAULT_SCAN_INTERVAL, DOMAIN, - IMPERIAL_UNITS, - NO_ROUTE_ERROR_MESSAGE, - TRAFFIC_MODE_ENABLED, - TRAVEL_MODES_VEHICLE, + TRAVEL_MODE_PUBLIC, ) -from .model import HERERoutingData, HERETravelTimeConfig +from .coordinator import ( + HERERoutingDataUpdateCoordinator, + HERETransitDataUpdateCoordinator, +) +from .model import HERETravelTimeConfig PLATFORMS = [Platform.SENSOR] - _LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Set up HERE Travel Time from a config entry.""" api_key = config_entry.data[CONF_API_KEY] - here_client = RoutingApi(api_key) arrival = ( dt.parse_time(config_entry.options[CONF_ARRIVAL_TIME]) @@ -86,12 +60,22 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b departure=departure, ) - coordinator = HereTravelTimeDataUpdateCoordinator( - hass, - here_client, - here_travel_time_config, - ) - hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = coordinator + if config_entry.data[CONF_MODE] in {TRAVEL_MODE_PUBLIC, "publicTransportTimeTable"}: + hass.data.setdefault(DOMAIN, {})[ + config_entry.entry_id + ] = HERETransitDataUpdateCoordinator( + hass, + api_key, + here_travel_time_config, + ) + else: + hass.data.setdefault(DOMAIN, {})[ + config_entry.entry_id + ] = HERERoutingDataUpdateCoordinator( + hass, + api_key, + here_travel_time_config, + ) await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) return True @@ -106,173 +90,3 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> hass.data[DOMAIN].pop(config_entry.entry_id) return unload_ok - - -class HereTravelTimeDataUpdateCoordinator(DataUpdateCoordinator): - """HERETravelTime DataUpdateCoordinator.""" - - def __init__( - self, - hass: HomeAssistant, - api: RoutingApi, - config: HERETravelTimeConfig, - ) -> None: - """Initialize.""" - super().__init__( - hass, - _LOGGER, - name=DOMAIN, - update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL), - ) - self._api = api - self.config = config - - async def _async_update_data(self) -> HERERoutingData | None: - """Get the latest data from the HERE Routing API.""" - try: - async with async_timeout.timeout(10): - return await self.hass.async_add_executor_job(self._update) - except NoRouteFoundError as error: - raise UpdateFailed(NO_ROUTE_ERROR_MESSAGE) from error - - def _update(self) -> HERERoutingData | None: - """Get the latest data from the HERE Routing API.""" - try: - origin, destination, arrival, departure = self._prepare_parameters() - - _LOGGER.debug( - "Requesting route for origin: %s, destination: %s, route_mode: %s, mode: %s, traffic_mode: %s, arrival: %s, departure: %s", - origin, - destination, - RouteMode[self.config.route_mode], - RouteMode[self.config.travel_mode], - RouteMode[TRAFFIC_MODE_ENABLED], - arrival, - departure, - ) - - response: RoutingResponse = self._api.public_transport_timetable( - origin, - destination, - True, - [ - RouteMode[self.config.route_mode], - RouteMode[self.config.travel_mode], - RouteMode[TRAFFIC_MODE_ENABLED], - ], - arrival=arrival, - departure=departure, - ) - - _LOGGER.debug("Raw response is: %s", response.response) - - attribution: str | None = None - if "sourceAttribution" in response.response: - attribution = build_hass_attribution( - response.response.get("sourceAttribution") - ) - route: list = response.response["route"] - summary: dict = route[0]["summary"] - waypoint: list = route[0]["waypoint"] - distance: float = summary["distance"] - traffic_time: float = summary["baseTime"] - if self.config.travel_mode in TRAVEL_MODES_VEHICLE: - traffic_time = summary["trafficTime"] - if self.config.units == IMPERIAL_UNITS: - # Convert to miles. - distance = DistanceConverter.convert( - distance, LENGTH_METERS, LENGTH_MILES - ) - else: - # Convert to kilometers - distance = distance / 1000 - return HERERoutingData( - { - ATTR_ATTRIBUTION: attribution, - ATTR_DURATION: round(summary["baseTime"] / 60), # type: ignore[misc] - ATTR_DURATION_IN_TRAFFIC: round(traffic_time / 60), - ATTR_DISTANCE: distance, - ATTR_ORIGIN: ",".join(origin), - ATTR_DESTINATION: ",".join(destination), - ATTR_ORIGIN_NAME: waypoint[0]["mappedRoadName"], - ATTR_DESTINATION_NAME: waypoint[1]["mappedRoadName"], - } - ) - except InvalidCoordinatesException as ex: - _LOGGER.error("Could not call HERE api: %s", ex) - return None - - def _prepare_parameters( - self, - ) -> tuple[list[str], list[str], str | None, str | None]: - """Prepare parameters for the HERE api.""" - - def _from_entity_id(entity_id: str) -> list[str]: - coordinates = find_coordinates(self.hass, entity_id) - if coordinates is None: - raise InvalidCoordinatesException( - f"No coordinatnes found for {entity_id}" - ) - try: - here_formatted_coordinates = coordinates.split(",") - vol.Schema(cv.gps(here_formatted_coordinates)) - except (AttributeError, vol.Invalid) as ex: - raise InvalidCoordinatesException( - f"{coordinates} are not valid coordinates" - ) from ex - return here_formatted_coordinates - - # Destination - if self.config.destination_entity_id is not None: - destination = _from_entity_id(self.config.destination_entity_id) - else: - destination = [ - str(self.config.destination_latitude), - str(self.config.destination_longitude), - ] - - # Origin - if self.config.origin_entity_id is not None: - origin = _from_entity_id(self.config.origin_entity_id) - else: - origin = [ - str(self.config.origin_latitude), - str(self.config.origin_longitude), - ] - - # Arrival/Departure - arrival: str | None = None - departure: str | None = None - if self.config.arrival is not None: - arrival = convert_time_to_isodate(self.config.arrival) - if self.config.departure is not None: - departure = convert_time_to_isodate(self.config.departure) - - if arrival is None and departure is None: - departure = "now" - - return (origin, destination, arrival, departure) - - -def build_hass_attribution(source_attribution: dict) -> str | None: - """Build a hass frontend ready string out of the sourceAttribution.""" - if (suppliers := source_attribution.get("supplier")) is not None: - supplier_titles = [] - for supplier in suppliers: - if (title := supplier.get("title")) is not None: - supplier_titles.append(title) - joined_supplier_titles = ",".join(supplier_titles) - return f"With the support of {joined_supplier_titles}. All information is provided without warranty of any kind." - return None - - -def convert_time_to_isodate(simple_time: time) -> str: - """Take a time like 08:00:00 and combine it with the current date.""" - combined = datetime.combine(dt.start_of_local_day(), simple_time) - if combined < datetime.now(): - combined = combined + timedelta(days=1) - return combined.isoformat() - - -class InvalidCoordinatesException(Exception): - """Coordinates for origin or destination are malformed.""" diff --git a/homeassistant/components/here_travel_time/config_flow.py b/homeassistant/components/here_travel_time/config_flow.py index 38bd1742c91..c0b00ffc876 100644 --- a/homeassistant/components/here_travel_time/config_flow.py +++ b/homeassistant/components/here_travel_time/config_flow.py @@ -4,7 +4,14 @@ from __future__ import annotations import logging from typing import Any -from herepy import HEREError, InvalidCredentialsError, RouteMode, RoutingApi +from here_routing import ( + HERERoutingApi, + HERERoutingError, + HERERoutingUnauthorizedError, + Place, + TransportMode, +) +from here_transit import HERETransitError import voluptuous as vol from homeassistant import config_entries @@ -38,17 +45,14 @@ from .const import ( CONF_ORIGIN_LATITUDE, CONF_ORIGIN_LONGITUDE, CONF_ROUTE_MODE, - CONF_TRAFFIC_MODE, DEFAULT_NAME, DOMAIN, IMPERIAL_UNITS, METRIC_UNITS, ROUTE_MODE_FASTEST, ROUTE_MODES, - TRAFFIC_MODE_ENABLED, - TRAFFIC_MODES, TRAVEL_MODE_CAR, - TRAVEL_MODE_PUBLIC_TIME_TABLE, + TRAVEL_MODE_PUBLIC, TRAVEL_MODES, UNITS, ) @@ -56,26 +60,23 @@ from .const import ( _LOGGER = logging.getLogger(__name__) -def validate_api_key(api_key: str) -> None: +async def async_validate_api_key(api_key: str) -> None: """Validate the user input allows us to connect.""" - known_working_origin = [38.9, -77.04833] - known_working_destination = [39.0, -77.1] - RoutingApi(api_key).public_transport_timetable( - known_working_origin, - known_working_destination, - True, - [ - RouteMode[ROUTE_MODE_FASTEST], - RouteMode[TRAVEL_MODE_CAR], - RouteMode[TRAFFIC_MODE_ENABLED], - ], - arrival=None, - departure="now", + known_working_origin = Place(latitude=38.9, longitude=-77.04833) + known_working_destination = Place(latitude=39.0, longitude=-77.1) + + await HERERoutingApi(api_key).route( + origin=known_working_origin, + destination=known_working_destination, + transport_mode=TransportMode.CAR, ) def get_user_step_schema(data: dict[str, Any]) -> vol.Schema: """Get a populated schema or default.""" + travel_mode = data.get(CONF_MODE, TRAVEL_MODE_CAR) + if travel_mode == "publicTransportTimeTable": + travel_mode = TRAVEL_MODE_PUBLIC return vol.Schema( { vol.Optional( @@ -92,7 +93,6 @@ def get_user_step_schema(data: dict[str, Any]) -> vol.Schema: def default_options(hass: HomeAssistant) -> dict[str, str | None]: """Get the default options.""" default = { - CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, CONF_ARRIVAL_TIME: None, CONF_DEPARTURE_TIME: None, @@ -128,12 +128,10 @@ class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): user_input = user_input or {} if user_input: try: - await self.hass.async_add_executor_job( - validate_api_key, user_input[CONF_API_KEY] - ) - except InvalidCredentialsError: + await async_validate_api_key(user_input[CONF_API_KEY]) + except HERERoutingUnauthorizedError: errors["base"] = "invalid_auth" - except HEREError as error: + except (HERERoutingError, HERETransitError) as error: _LOGGER.exception("Unexpected exception: %s", error) errors["base"] = "unknown" if not errors: @@ -251,25 +249,14 @@ class HERETravelTimeOptionsFlow(config_entries.OptionsFlow): """Manage the HERE Travel Time options.""" if user_input is not None: self._config = user_input - if self.config_entry.data[CONF_MODE] == TRAVEL_MODE_PUBLIC_TIME_TABLE: - return self.async_show_menu( - step_id="time_menu", - menu_options=["departure_time", "arrival_time", "no_time"], - ) return self.async_show_menu( step_id="time_menu", - menu_options=["departure_time", "no_time"], + menu_options=["departure_time", "arrival_time", "no_time"], ) defaults = default_options(self.hass) schema = vol.Schema( { - vol.Optional( - CONF_TRAFFIC_MODE, - default=self.config_entry.options.get( - CONF_TRAFFIC_MODE, defaults[CONF_TRAFFIC_MODE] - ), - ): vol.In(TRAFFIC_MODES), vol.Optional( CONF_ROUTE_MODE, default=self.config_entry.options.get( diff --git a/homeassistant/components/here_travel_time/const.py b/homeassistant/components/here_travel_time/const.py index ea0dc5c136e..4fce053f768 100644 --- a/homeassistant/components/here_travel_time/const.py +++ b/homeassistant/components/here_travel_time/const.py @@ -11,7 +11,6 @@ CONF_ORIGIN = "origin" CONF_ORIGIN_LATITUDE = "origin_latitude" CONF_ORIGIN_LONGITUDE = "origin_longitude" CONF_ORIGIN_ENTITY_ID = "origin_entity_id" -CONF_TRAFFIC_MODE = "traffic_mode" CONF_ROUTE_MODE = "route_mode" CONF_ARRIVAL = "arrival" CONF_DEPARTURE = "departure" @@ -24,23 +23,17 @@ TRAVEL_MODE_BICYCLE = "bicycle" TRAVEL_MODE_CAR = "car" TRAVEL_MODE_PEDESTRIAN = "pedestrian" TRAVEL_MODE_PUBLIC = "publicTransport" -TRAVEL_MODE_PUBLIC_TIME_TABLE = "publicTransportTimeTable" TRAVEL_MODE_TRUCK = "truck" TRAVEL_MODES = [ TRAVEL_MODE_BICYCLE, TRAVEL_MODE_CAR, TRAVEL_MODE_PEDESTRIAN, TRAVEL_MODE_PUBLIC, - TRAVEL_MODE_PUBLIC_TIME_TABLE, TRAVEL_MODE_TRUCK, ] TRAVEL_MODES_VEHICLE = [TRAVEL_MODE_CAR, TRAVEL_MODE_TRUCK] -TRAFFIC_MODE_ENABLED = "traffic_enabled" -TRAFFIC_MODE_DISABLED = "traffic_disabled" -TRAFFIC_MODES = [TRAFFIC_MODE_ENABLED, TRAFFIC_MODE_DISABLED] - ROUTE_MODE_FASTEST = "fastest" ROUTE_MODE_SHORTEST = "shortest" ROUTE_MODES = [ROUTE_MODE_FASTEST, ROUTE_MODE_SHORTEST] @@ -55,7 +48,6 @@ ICONS = { TRAVEL_MODE_BICYCLE: ICON_BICYCLE, TRAVEL_MODE_PEDESTRIAN: ICON_PEDESTRIAN, TRAVEL_MODE_PUBLIC: ICON_PUBLIC, - TRAVEL_MODE_PUBLIC_TIME_TABLE: ICON_PUBLIC, TRAVEL_MODE_TRUCK: ICON_TRUCK, } @@ -69,10 +61,7 @@ ATTR_ORIGIN = "origin" ATTR_DESTINATION = "destination" ATTR_UNIT_SYSTEM = "unit_system" -ATTR_TRAFFIC_MODE = CONF_TRAFFIC_MODE ATTR_DURATION_IN_TRAFFIC = "duration_in_traffic" ATTR_ORIGIN_NAME = "origin_name" ATTR_DESTINATION_NAME = "destination_name" - -NO_ROUTE_ERROR_MESSAGE = "HERE could not find a route based on the input" diff --git a/homeassistant/components/here_travel_time/coordinator.py b/homeassistant/components/here_travel_time/coordinator.py new file mode 100644 index 00000000000..de9bb9f1c60 --- /dev/null +++ b/homeassistant/components/here_travel_time/coordinator.py @@ -0,0 +1,290 @@ +"""The HERE Travel Time integration.""" +from __future__ import annotations + +from datetime import datetime, time, timedelta +import logging + +import here_routing +from here_routing import HERERoutingApi, Return, RoutingMode, Spans, TransportMode +import here_transit +from here_transit import HERETransitApi +import voluptuous as vol + +from homeassistant.const import ATTR_ATTRIBUTION, LENGTH_METERS, LENGTH_MILES +from homeassistant.core import HomeAssistant +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.location import find_coordinates +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +from homeassistant.util import dt +from homeassistant.util.unit_conversion import DistanceConverter + +from .const import ( + ATTR_DESTINATION, + ATTR_DESTINATION_NAME, + ATTR_DISTANCE, + ATTR_DURATION, + ATTR_DURATION_IN_TRAFFIC, + ATTR_ORIGIN, + ATTR_ORIGIN_NAME, + DEFAULT_SCAN_INTERVAL, + DOMAIN, + IMPERIAL_UNITS, + ROUTE_MODE_FASTEST, +) +from .model import HERETravelTimeConfig, HERETravelTimeData + +_LOGGER = logging.getLogger(__name__) + + +class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator): + """here_routing DataUpdateCoordinator.""" + + def __init__( + self, + hass: HomeAssistant, + api_key: str, + config: HERETravelTimeConfig, + ) -> None: + """Initialize.""" + super().__init__( + hass, + _LOGGER, + name=DOMAIN, + update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL), + ) + self._api = HERERoutingApi(api_key) + self.config = config + + async def _async_update_data(self) -> HERETravelTimeData | None: + """Get the latest data from the HERE Routing API.""" + origin, destination, arrival, departure = prepare_parameters( + self.hass, self.config + ) + + route_mode = ( + RoutingMode.FAST + if self.config.route_mode == ROUTE_MODE_FASTEST + else RoutingMode.SHORT + ) + + _LOGGER.debug( + "Requesting route for origin: %s, destination: %s, route_mode: %s, mode: %s, arrival: %s, departure: %s", + origin, + destination, + route_mode, + TransportMode(self.config.travel_mode), + arrival, + departure, + ) + + response = await self._api.route( + transport_mode=TransportMode(self.config.travel_mode), + origin=here_routing.Place(origin[0], origin[1]), + destination=here_routing.Place(destination[0], destination[1]), + routing_mode=route_mode, + arrival_time=arrival, + departure_time=departure, + return_values=[Return.POLYINE, Return.SUMMARY], + spans=[Spans.NAMES], + ) + + _LOGGER.debug("Raw response is: %s", response) + + return self._parse_routing_response(response) + + def _parse_routing_response(self, response) -> HERETravelTimeData: + """Parse the routing response dict to a HERETravelTimeData.""" + section: dict = response["routes"][0]["sections"][0] + summary: dict = section["summary"] + mapped_origin_lat: float = section["departure"]["place"]["location"]["lat"] + mapped_origin_lon: float = section["departure"]["place"]["location"]["lng"] + mapped_destination_lat: float = section["arrival"]["place"]["location"]["lat"] + mapped_destination_lon: float = section["arrival"]["place"]["location"]["lng"] + distance: float = summary["length"] + if self.config.units == IMPERIAL_UNITS: + # Convert to miles. + distance = DistanceConverter.convert(distance, LENGTH_METERS, LENGTH_MILES) + else: + # Convert to kilometers + distance = distance / 1000 + origin_name: str | None = None + if (names := section["spans"][0].get("names")) is not None: + origin_name = names[0]["value"] + destination_name: str | None = None + if (names := section["spans"][-1].get("names")) is not None: + destination_name = names[0]["value"] + return HERETravelTimeData( + { + ATTR_ATTRIBUTION: None, + ATTR_DURATION: round(summary["baseDuration"] / 60), # type: ignore[misc] + ATTR_DURATION_IN_TRAFFIC: round(summary["duration"] / 60), + ATTR_DISTANCE: distance, + ATTR_ORIGIN: f"{mapped_origin_lat},{mapped_origin_lon}", + ATTR_DESTINATION: f"{mapped_destination_lat},{mapped_destination_lon}", + ATTR_ORIGIN_NAME: origin_name, + ATTR_DESTINATION_NAME: destination_name, + } + ) + + +class HERETransitDataUpdateCoordinator(DataUpdateCoordinator): + """HERETravelTime DataUpdateCoordinator.""" + + def __init__( + self, + hass: HomeAssistant, + api_key: str, + config: HERETravelTimeConfig, + ) -> None: + """Initialize.""" + super().__init__( + hass, + _LOGGER, + name=DOMAIN, + update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL), + ) + self._api = HERETransitApi(api_key) + self.config = config + + async def _async_update_data(self) -> HERETravelTimeData | None: + """Get the latest data from the HERE Routing API.""" + origin, destination, arrival, departure = prepare_parameters( + self.hass, self.config + ) + + _LOGGER.debug( + "Requesting transit route for origin: %s, destination: %s, arrival: %s, departure: %s", + origin, + destination, + arrival, + departure, + ) + + response = await self._api.route( + origin=here_transit.Place(latitude=origin[0], longitude=origin[1]), + destination=here_transit.Place( + latitude=destination[0], longitude=destination[1] + ), + arrival_time=arrival, + departure_time=departure, + return_values=[ + here_transit.Return.POLYLINE, + here_transit.Return.TRAVEL_SUMMARY, + ], + ) + + _LOGGER.debug("Raw response is: %s", response) + + return self._parse_transit_response(response) + + def _parse_transit_response(self, response) -> HERETravelTimeData: + """Parse the transit response dict to a HERETravelTimeData.""" + sections: dict = response["routes"][0]["sections"] + attribution: str | None = build_hass_attribution(sections) + mapped_origin_lat: float = sections[0]["departure"]["place"]["location"]["lat"] + mapped_origin_lon: float = sections[0]["departure"]["place"]["location"]["lng"] + mapped_destination_lat: float = sections[-1]["arrival"]["place"]["location"][ + "lat" + ] + mapped_destination_lon: float = sections[-1]["arrival"]["place"]["location"][ + "lng" + ] + distance: float = sum( + section["travelSummary"]["length"] for section in sections + ) + duration: float = sum( + section["travelSummary"]["duration"] for section in sections + ) + if self.config.units == IMPERIAL_UNITS: + # Convert to miles. + distance = DistanceConverter.convert(distance, LENGTH_METERS, LENGTH_MILES) + else: + # Convert to kilometers + distance = distance / 1000 + return HERETravelTimeData( + { + ATTR_ATTRIBUTION: attribution, + ATTR_DURATION: round(duration / 60), # type: ignore[misc] + ATTR_DURATION_IN_TRAFFIC: round(duration / 60), + ATTR_DISTANCE: distance, + ATTR_ORIGIN: f"{mapped_origin_lat},{mapped_origin_lon}", + ATTR_DESTINATION: f"{mapped_destination_lat},{mapped_destination_lon}", + ATTR_ORIGIN_NAME: sections[0]["departure"]["place"].get("name"), + ATTR_DESTINATION_NAME: sections[-1]["arrival"]["place"].get("name"), + } + ) + + +def prepare_parameters( + hass: HomeAssistant, + config: HERETravelTimeConfig, +) -> tuple[list[str], list[str], str | None, str | None]: + """Prepare parameters for the HERE api.""" + + def _from_entity_id(entity_id: str) -> list[str]: + coordinates = find_coordinates(hass, entity_id) + if coordinates is None: + raise InvalidCoordinatesException(f"No coordinates found for {entity_id}") + try: + formatted_coordinates = coordinates.split(",") + vol.Schema(cv.gps(formatted_coordinates)) + except (AttributeError, vol.ExactSequenceInvalid) as ex: + raise InvalidCoordinatesException( + f"{coordinates} are not valid coordinates" + ) from ex + return formatted_coordinates + + # Destination + if config.destination_entity_id is not None: + destination = _from_entity_id(config.destination_entity_id) + else: + destination = [ + str(config.destination_latitude), + str(config.destination_longitude), + ] + + # Origin + if config.origin_entity_id is not None: + origin = _from_entity_id(config.origin_entity_id) + else: + origin = [ + str(config.origin_latitude), + str(config.origin_longitude), + ] + + # Arrival/Departure + arrival: str | None = None + departure: str | None = None + if config.arrival is not None: + arrival = convert_time_to_isodate(config.arrival) + if config.departure is not None: + departure = convert_time_to_isodate(config.departure) + + return (origin, destination, arrival, departure) + + +def build_hass_attribution(sections: dict) -> str | None: + """Build a hass frontend ready string out of the attributions.""" + relevant_attributions = [] + for section in sections: + if (attributions := section.get("attributions")) is not None: + for attribution in attributions: + if (href := attribution.get("href")) is not None: + relevant_attributions.append(f"{href}") + if (text := attribution.get("text")) is not None: + relevant_attributions.append(text) + if len(relevant_attributions) > 0: + return ",".join(relevant_attributions) + return None + + +def convert_time_to_isodate(simple_time: time) -> str: + """Take a time like 08:00:00 and combine it with the current date.""" + combined = datetime.combine(dt.start_of_local_day(), simple_time) + if combined < datetime.now(): + combined = combined + timedelta(days=1) + return combined.isoformat() + + +class InvalidCoordinatesException(Exception): + """Coordinates for origin or destination are malformed.""" diff --git a/homeassistant/components/here_travel_time/manifest.json b/homeassistant/components/here_travel_time/manifest.json index 68370311254..8efcf29b6b0 100644 --- a/homeassistant/components/here_travel_time/manifest.json +++ b/homeassistant/components/here_travel_time/manifest.json @@ -3,8 +3,8 @@ "name": "HERE Travel Time", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/here_travel_time", - "requirements": ["herepy==2.0.0"], + "requirements": ["here_routing==0.1.1", "here_transit==1.0.0"], "codeowners": ["@eifinger"], "iot_class": "cloud_polling", - "loggers": ["herepy"] + "loggers": ["here_routing", "here_transit"] } diff --git a/homeassistant/components/here_travel_time/model.py b/homeassistant/components/here_travel_time/model.py index 7310ac24e77..737aae78a03 100644 --- a/homeassistant/components/here_travel_time/model.py +++ b/homeassistant/components/here_travel_time/model.py @@ -6,8 +6,8 @@ from datetime import time from typing import TypedDict -class HERERoutingData(TypedDict): - """Routing information calculated from a herepy.RoutingResponse.""" +class HERETravelTimeData(TypedDict): + """Routing information.""" ATTR_ATTRIBUTION: str | None ATTR_DURATION: float diff --git a/homeassistant/components/here_travel_time/sensor.py b/homeassistant/components/here_travel_time/sensor.py index 1ee0087eab7..1fec002377e 100644 --- a/homeassistant/components/here_travel_time/sensor.py +++ b/homeassistant/components/here_travel_time/sensor.py @@ -28,7 +28,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.start import async_at_start from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import HereTravelTimeDataUpdateCoordinator from .const import ( ATTR_DESTINATION, ATTR_DESTINATION_NAME, @@ -42,6 +41,7 @@ from .const import ( ICONS, IMPERIAL_UNITS, ) +from .coordinator import HERERoutingDataUpdateCoordinator SCAN_INTERVAL = timedelta(minutes=5) @@ -101,7 +101,7 @@ class HERETravelTimeSensor(SensorEntity, CoordinatorEntity): unique_id_prefix: str, name: str, sensor_description: SensorEntityDescription, - coordinator: HereTravelTimeDataUpdateCoordinator, + coordinator: HERERoutingDataUpdateCoordinator, ) -> None: """Initialize the sensor.""" super().__init__(coordinator) @@ -146,7 +146,7 @@ class OriginSensor(HERETravelTimeSensor): self, unique_id_prefix: str, name: str, - coordinator: HereTravelTimeDataUpdateCoordinator, + coordinator: HERERoutingDataUpdateCoordinator, ) -> None: """Initialize the sensor.""" sensor_description = SensorEntityDescription( @@ -174,7 +174,7 @@ class DestinationSensor(HERETravelTimeSensor): self, unique_id_prefix: str, name: str, - coordinator: HereTravelTimeDataUpdateCoordinator, + coordinator: HERERoutingDataUpdateCoordinator, ) -> None: """Initialize the sensor.""" sensor_description = SensorEntityDescription( @@ -202,7 +202,7 @@ class DistanceSensor(HERETravelTimeSensor): self, unique_id_prefix: str, name: str, - coordinator: HereTravelTimeDataUpdateCoordinator, + coordinator: HERERoutingDataUpdateCoordinator, ) -> None: """Initialize the sensor.""" sensor_description = SensorEntityDescription( diff --git a/requirements_all.txt b/requirements_all.txt index 03778c2609e..9252d244a53 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -868,7 +868,10 @@ hdate==0.10.4 heatmiserV3==1.1.18 # homeassistant.components.here_travel_time -herepy==2.0.0 +here_routing==0.1.1 + +# homeassistant.components.here_travel_time +here_transit==1.0.0 # homeassistant.components.hikvisioncam hikvision==0.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 983c60b3f74..3a8681f3d9a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -654,7 +654,10 @@ hatasmota==0.6.1 hdate==0.10.4 # homeassistant.components.here_travel_time -herepy==2.0.0 +here_routing==0.1.1 + +# homeassistant.components.here_travel_time +here_transit==1.0.0 # homeassistant.components.hlk_sw16 hlk-sw16==0.0.9 diff --git a/tests/components/here_travel_time/conftest.py b/tests/components/here_travel_time/conftest.py index 368b070428e..8069583df76 100644 --- a/tests/components/here_travel_time/conftest.py +++ b/tests/components/here_travel_time/conftest.py @@ -2,37 +2,39 @@ import json from unittest.mock import patch -from herepy.models import RoutingResponse import pytest from tests.common import load_fixture -RESPONSE = RoutingResponse.new_from_jsondict( - json.loads(load_fixture("here_travel_time/car_response.json")) +RESPONSE = json.loads(load_fixture("here_travel_time/car_response.json")) +TRANSIT_RESPONSE = json.loads( + load_fixture("here_travel_time/transit_route_response.json") ) -RESPONSE.route_short = "US-29 - K St NW; US-29 - Whitehurst Fwy; I-495 N - Capital Beltway; MD-187 S - Old Georgetown Rd" - -EMPTY_ATTRIBUTION_RESPONSE = RoutingResponse.new_from_jsondict( - json.loads(load_fixture("here_travel_time/empty_attribution_response.json")) +NO_ATTRIBUTION_TRANSIT_RESPONSE = json.loads( + load_fixture("here_travel_time/no_attribution_transit_route_response.json") ) -EMPTY_ATTRIBUTION_RESPONSE.route_short = "US-29 - K St NW; US-29 - Whitehurst Fwy; I-495 N - Capital Beltway; MD-187 S - Old Georgetown Rd" @pytest.fixture(name="valid_response") def valid_response_fixture(): """Return valid api response.""" with patch( - "herepy.RoutingApi.public_transport_timetable", + "here_transit.HERETransitApi.route", return_value=TRANSIT_RESPONSE + ), patch( + "here_routing.HERERoutingApi.route", return_value=RESPONSE, ) as mock: yield mock -@pytest.fixture(name="empty_attribution_response") -def empty_attribution_response_fixture(): - """Return valid api response with an empty attribution.""" +@pytest.fixture(name="no_attribution_response") +def no_attribution_response_fixture(): + """Return valid api response without attribution.""" with patch( - "herepy.RoutingApi.public_transport_timetable", - return_value=EMPTY_ATTRIBUTION_RESPONSE, + "here_transit.HERETransitApi.route", + return_value=NO_ATTRIBUTION_TRANSIT_RESPONSE, + ), patch( + "here_routing.HERERoutingApi.route", + return_value=RESPONSE, ) as mock: yield mock diff --git a/tests/components/here_travel_time/const.py b/tests/components/here_travel_time/const.py index 0cc3143bc0b..167fd51dc5b 100644 --- a/tests/components/here_travel_time/const.py +++ b/tests/components/here_travel_time/const.py @@ -1,8 +1,27 @@ """Constants for HERE Travel Time tests.""" +from homeassistant.components.here_travel_time.const import ( + CONF_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE, + CONF_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE, + TRAVEL_MODE_CAR, +) +from homeassistant.const import CONF_API_KEY, CONF_MODE, CONF_NAME + API_KEY = "test" -CAR_ORIGIN_LATITUDE = "38.9" -CAR_ORIGIN_LONGITUDE = "-77.04833" -CAR_DESTINATION_LATITUDE = "39.0" -CAR_DESTINATION_LONGITUDE = "-77.1" +ORIGIN_LATITUDE = "38.9" +ORIGIN_LONGITUDE = "-77.04833" +DESTINATION_LATITUDE = "39.0" +DESTINATION_LONGITUDE = "-77.1" + +DEFAULT_CONFIG = { + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_CAR, + CONF_NAME: "test", +} diff --git a/tests/components/here_travel_time/fixtures/car_response.json b/tests/components/here_travel_time/fixtures/car_response.json index cd479b2c947..99a2d9ef051 100644 --- a/tests/components/here_travel_time/fixtures/car_response.json +++ b/tests/components/here_travel_time/fixtures/car_response.json @@ -1,304 +1,208 @@ { - "response": { - "metaInfo": { - "timestamp": "2019-07-19T07:38:39Z", - "mapVersion": "8.30.98.154", - "moduleVersion": "7.2.201928-4446", - "interfaceVersion": "2.6.64", - "availableMapVersion": ["8.30.98.154"] - }, - "route": [ - { - "waypoint": [ - { - "linkId": "+732182239", - "mappedPosition": { - "latitude": 38.9, - "longitude": -77.0488358 - }, - "originalPosition": { - "latitude": 38.9, - "longitude": -77.0483301 - }, - "type": "stopOver", - "spot": 0.4946237, - "sideOfStreet": "right", - "mappedRoadName": "22nd St NW", - "label": "22nd St NW", - "shapeIndex": 0, - "source": "user" - }, - { - "linkId": "+942865877", - "mappedPosition": { - "latitude": 38.9999735, - "longitude": -77.100141 - }, - "originalPosition": { - "latitude": 38.9999999, - "longitude": -77.1000001 - }, - "type": "stopOver", - "spot": 1, - "sideOfStreet": "left", - "mappedRoadName": "Service Rd S", - "label": "Service Rd S", - "shapeIndex": 279, - "source": "user" - } - ], - "mode": { - "type": "fastest", - "transportModes": ["car"], - "trafficMode": "enabled", - "feature": [] - }, - "leg": [ - { - "start": { - "linkId": "+732182239", - "mappedPosition": { - "latitude": 38.9, - "longitude": -77.0488358 - }, - "originalPosition": { - "latitude": 38.9, - "longitude": -77.0483301 - }, - "type": "stopOver", - "spot": 0.4946237, - "sideOfStreet": "right", - "mappedRoadName": "22nd St NW", - "label": "22nd St NW", - "shapeIndex": 0, - "source": "user" - }, - "end": { - "linkId": "+942865877", - "mappedPosition": { - "latitude": 38.9999735, - "longitude": -77.100141 - }, - "originalPosition": { - "latitude": 38.9999999, - "longitude": -77.1000001 - }, - "type": "stopOver", - "spot": 1, - "sideOfStreet": "left", - "mappedRoadName": "Service Rd S", - "label": "Service Rd S", - "shapeIndex": 279, - "source": "user" - }, - "length": 23903, - "travelTime": 1884, - "maneuver": [ - { - "position": { - "latitude": 38.9, - "longitude": -77.0488358 - }, - "instruction": "Head toward I St NW on 22nd St NW. Go for 279 m.", - "travelTime": 95, - "length": 279, - "id": "M1", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9021051, - "longitude": -77.048825 - }, - "instruction": "Turn left toward Pennsylvania Ave NW. Go for 71 m.", - "travelTime": 21, - "length": 71, - "id": "M2", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.902545, - "longitude": -77.0494151 - }, - "instruction": "Take the 3rd exit from Washington Cir NW roundabout onto K St NW. Go for 352 m.", - "travelTime": 90, - "length": 352, - "id": "M3", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9026523, - "longitude": -77.0529449 - }, - "instruction": "Keep left onto K St NW (US-29). Go for 201 m.", - "travelTime": 30, - "length": 201, - "id": "M4", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9025235, - "longitude": -77.0552516 - }, - "instruction": "Keep right onto Whitehurst Fwy (US-29). Go for 1.4 km.", - "travelTime": 131, - "length": 1381, - "id": "M5", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9050448, - "longitude": -77.0701969 - }, - "instruction": "Turn left onto M St NW. Go for 784 m.", - "travelTime": 78, - "length": 784, - "id": "M6", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9060318, - "longitude": -77.0790696 - }, - "instruction": "Turn slightly left onto Canal Rd NW. Go for 4.2 km.", - "travelTime": 277, - "length": 4230, - "id": "M7", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9303219, - "longitude": -77.1117926 - }, - "instruction": "Continue on Clara Barton Pkwy. Go for 844 m.", - "travelTime": 55, - "length": 844, - "id": "M8", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9368558, - "longitude": -77.1166742 - }, - "instruction": "Continue on Clara Barton Pkwy. Go for 4.7 km.", - "travelTime": 298, - "length": 4652, - "id": "M9", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9706838, - "longitude": -77.1461463 - }, - "instruction": "Keep right onto Cabin John Pkwy N toward I-495 N. Go for 2.1 km.", - "travelTime": 91, - "length": 2069, - "id": "M10", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9858222, - "longitude": -77.1571326 - }, - "instruction": "Take left ramp onto I-495 N (Capital Beltway). Go for 5.5 km.", - "travelTime": 238, - "length": 5538, - "id": "M11", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 39.0153587, - "longitude": -77.1221781 - }, - "instruction": "Take exit 36 toward Bethesda onto MD-187 S (Old Georgetown Rd). Go for 2.4 km.", - "travelTime": 211, - "length": 2365, - "id": "M12", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9981818, - "longitude": -77.1093571 - }, - "instruction": "Turn left onto Lincoln Dr. Go for 506 m.", - "travelTime": 127, - "length": 506, - "id": "M13", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9987397, - "longitude": -77.1037138 - }, - "instruction": "Turn right onto Service Rd W. Go for 121 m.", - "travelTime": 36, - "length": 121, - "id": "M14", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9976454, - "longitude": -77.1036172 - }, - "instruction": "Turn left onto Service Rd S. Go for 510 m.", - "travelTime": 106, - "length": 510, - "id": "M15", - "_type": "PrivateTransportManeuverType" - }, - { - "position": { - "latitude": 38.9999735, - "longitude": -77.100141 - }, - "instruction": "Arrive at Service Rd S. Your destination is on the left.", - "travelTime": 0, - "length": 0, - "id": "M16", - "_type": "PrivateTransportManeuverType" - } - ] - } - ], - "summary": { - "distance": 23903, - "trafficTime": 1861, - "baseTime": 1803, - "flags": [ - "noThroughRoad", - "motorway", - "builtUpArea", - "park", - "privateRoad" - ], - "text": "The trip takes 23.9 km and 31 mins.", - "travelTime": 1861, - "_type": "RouteSummaryType" - } - } - ], - "language": "en-us", - "sourceAttribution": { - "attribution": "With the support of HERE Technologies. All information is provided without warranty of any kind.", - "supplier": [ + "routes": [ + { + "id": "bace556c-2a16-4d93-a6c0-f595bbc89aa6", + "sections": [ { - "title": "HERE Technologies", - "href": "https://transit.api.here.com/r?appId=Mt1bOYh3m9uxE7r3wuUx&u=https://wego.here.com" + "id": "3ef521f3-7f88-480b-9b84-567a352c323c", + "type": "vehicle", + "departure": { + "time": "2022-10-24T05:53:00-04:00", + "place": { + "type": "place", + "location": { + "lat": 38.8999937, + "lng": -77.0479682 + }, + "originalLocation": { + "lat": 38.9, + "lng": -77.0483301 + } + } + }, + "arrival": { + "time": "2022-10-24T06:22:36-04:00", + "place": { + "type": "place", + "location": { + "lat": 38.99997, + "lng": -77.10014 + }, + "originalLocation": { + "lat": 38.9999999, + "lng": -77.1000001 + } + } + }, + "summary": { + "duration": 1776, + "length": 13682, + "baseDuration": 1571 + }, + "polyline": "BG0xomqC_p0-yE7ZXA_dA3XArnBTnpBkXAsYUkXAo4BTgZAA8GoB0F8BoG4DwHkDsEgFsEsEwC4D8BwCoB8BUwCU4DU0FT4DTsE7BkD7BwC7BgFzFwCrEkNwMkNwMwW8VgKsJoLAgPA4cUgeAwqBA0PAwHA8GA0yBAwbU4NA0FAgPAsTAoBAsnBAsEAkXAwvBU8VoBwMoBwMjD4NUkIUkD3IsEvMsEnLkI7V8GzUsE7LkD3IsEUoBAwCAoBAwCT8BToBTwC7BoBnB8BvCoB7BoBvCoBjDUvCA_EwCnLwC3IwCvHwC3I4D_E4DrE4D_EwCjD8BvCwCjDwCjDwC3DgFvHgFvH0KnQwRrY4XvgBkInLkDrEwCjDwHzK8GrJ0FjI0KrOgKjNwHzKsJjN8QjXkNjSwlBnzBoazjBwbnkB4D_EgenpBwCjDgFnGgF7G4cjmBoB7B4DnGwRzZkSrY4S_YgK3NoG3I0K_O4DzFoBnB4XvgB0K_OopBz3BoQrT4IjIoGrEkN_E8ajIkNrEoV_J0PrJsJvHwH7G4N3N0K3NgKjNkI7LsE7GwCrEoG_O4D_JsEvM0FnV0FjcoBvW8B3NwC_JwHvWUvCsJ7QokBnzBsTnasOrTwM7QsJjNoB7BoB7BsYvgB4I7LkNjSsTnawRrYgPnV4hB_sBgK3NoGrJwCjD0ZkDgZsEsEUkNwC4DU0UwCwlB0F0K8Bk_B0K4hBoG4I8B0FUgZkD0UwC8GUsYAkSAkSnB0KvC4rBjSkNzF0PvHwR3IoBToQjI0UnLwRrJwWvMwCnBozB3c4rB3XsT_JoVzKgjB_ToQrJsJzF8G3D0Z_OoL7GkrB3XoV7L0jBrTkIrE0P3IwCnBwWvM8VvMoBTwW7LkIrE4I_E4NvHoGjDsTnLwHrEkI_E4D7BoBT0FjDwMvHoBT4IrE4D7BsdzP0FjDgKzFoL7G4I_E8BnBoa_O4X3NoQ3IsTzK8BnBsJ_EwCnBgKrEwHjDsJvCwH7B8anG8QjD0PrE0ZnGoajI4D7B8zBvbgKrE8BnBwWnLwR3I0K_EoQ3I4I_EoLnGoBT4IrEgK_EofzP4X7L0UzKsxBzZkIrE4IrEoVzKsTrJ8QjIoGjDwH3DkcrOwWnLwR3I0UzKsYvMoajN08BnfwW7LwRjD0PnG0PnGwHjD0P7GgjBvRoVnLoVnL0PjI8VnLkmB3SoVzK8BnB4S3IoajN0ejN4SjIwHvCkS7GoBTwR_E8QrEoV_Ek6B7L8GnBoa3DwgB3D84B_J8zB7G03B_JsnBnGoQvCsYrEwHnB08B_Jo4BrJoVrE8L3D8GvC8QnG4NnGoQ7GkSjIsT_JkSrJ0UzK0Z3NgFvCwMnGoLzF0U_J4D7BwWnL4SrJ4D7BkmB3SwMnGoLzFkDnBkIrE0FvCof_O8ajN8V_JgK_EgZnL4N7G8QjI0Z3IkNrE8BToGjD0F3DwHzFsJvCwgBjIgU_EwMjD4DT4DTgFnBkhB3IoLjDoBAwqBrJ4DTwqB3IwHnBsTrEwH7BkS3DkI7BwCT4X_E8BToBA4I7BgKvCoQjDsY_E8azF0PjD8LjDwMvCgUzF0enGwR3DkI7B0UrEgUrEwR3D0PjDoQjD8L7BoVjD0P7B8GTsO7BgejD8Q7BoQ7BwMnBsJnBwHTgejDkSnBgPnBkNT0UnBoBAwHTwHT0UvC4N7B0KT4DT8BAT3DA_ET_d8BrOwC_J8GnQgPjXsEzFsJ7QsJ3XwHvWU_OoBvWAnGTzP_iBjSzK3D_JnB7LAvW8B7LUv5BsEoGgPoGsJ0FoGsEwC4D8BkDU8VToGA4SoGwHnBsJ3DoQA4DT8L7BgFkD8BwCUsE", + "spans": [ + { + "offset": 0, + "names": [ + { + "value": "22nd St NW", + "language": "en" + } + ] + }, + { + "offset": 1, + "names": [ + { + "value": "H St NW", + "language": "en" + } + ] + }, + { + "offset": 5, + "names": [ + { + "value": "23rd St NW", + "language": "en" + } + ] + }, + { + "offset": 10, + "names": [ + { + "value": "Washington Cir NW", + "language": "en" + } + ] + }, + { + "offset": 29, + "names": [ + { + "value": "New Hampshire Ave NW", + "language": "en" + } + ] + }, + { + "offset": 33, + "names": [ + { + "value": "22nd St NW", + "language": "en" + } + ] + }, + { + "offset": 57, + "names": [ + { + "value": "Massachusetts Ave NW", + "language": "en" + } + ] + }, + { + "offset": 64, + "names": [ + { + "value": "Massachusetts Ave NW", + "language": "en" + }, + { + "value": "Sheridan Cir NW", + "language": "en" + } + ] + }, + { + "offset": 78, + "names": [ + { + "value": "Sheridan Cir NW", + "language": "en" + }, + { + "value": "Massachusetts Ave NW", + "language": "en" + } + ] + }, + { + "offset": 79, + "names": [ + { + "value": "Massachusetts Ave NW", + "language": "en" + } + ] + }, + { + "offset": 174, + "names": [ + { + "value": "Wisconsin Ave NW", + "language": "en" + } + ] + }, + { + "offset": 291, + "names": [ + { + "value": "Wisconsin Ave", + "language": "en" + } + ] + }, + { + "offset": 408, + "names": [ + { + "value": "Rockville Pike", + "language": "en" + }, + { + "value": "Wisconsin Ave", + "language": "en" + } + ] + }, + { + "offset": 428, + "names": [ + { + "value": "South Dr", + "language": "en" + } + ] + }, + { + "offset": 443, + "names": [ + { + "value": "Center Dr", + "language": "en" + } + ] + }, + { + "offset": 450, + "names": [ + { + "value": "Service Rd S", + "language": "en" + } + ] + } + ], + "transport": { + "mode": "car" + } } ] } - } + ] } diff --git a/tests/components/here_travel_time/fixtures/empty_attribution_response.json b/tests/components/here_travel_time/fixtures/empty_attribution_response.json deleted file mode 100644 index cc1bb20a373..00000000000 --- a/tests/components/here_travel_time/fixtures/empty_attribution_response.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "response": { - "metaInfo": { - "timestamp": "2019-07-19T07:38:39Z", - "mapVersion": "8.30.98.154", - "moduleVersion": "7.2.201928-4446", - "interfaceVersion": "2.6.64", - "availableMapVersion": ["8.30.98.154"] - }, - "route": [ - { - "waypoint": [ - { - "linkId": "+732182239", - "mappedPosition": { - "latitude": 38.9, - "longitude": -77.0488358 - }, - "originalPosition": { - "latitude": 38.9, - "longitude": -77.0483301 - }, - "type": "stopOver", - "spot": 0.4946237, - "sideOfStreet": "right", - "mappedRoadName": "22nd St NW", - "label": "22nd St NW", - "shapeIndex": 0, - "source": "user" - }, - { - "linkId": "+942865877", - "mappedPosition": { - "latitude": 38.9999735, - "longitude": -77.100141 - }, - "originalPosition": { - "latitude": 38.9999999, - "longitude": -77.1000001 - }, - "type": "stopOver", - "spot": 1, - "sideOfStreet": "left", - "mappedRoadName": "Service Rd S", - "label": "Service Rd S", - "shapeIndex": 279, - "source": "user" - } - ], - "mode": { - "type": "fastest", - "transportModes": ["car"], - "trafficMode": "enabled", - "feature": [] - }, - "leg": [ - { - "start": { - "linkId": "+732182239", - "mappedPosition": { - "latitude": 38.9, - "longitude": -77.0488358 - }, - "originalPosition": { - "latitude": 38.9, - "longitude": -77.0483301 - }, - "type": "stopOver", - "spot": 0.4946237, - "sideOfStreet": "right", - "mappedRoadName": "22nd St NW", - "label": "22nd St NW", - "shapeIndex": 0, - "source": "user" - }, - "end": { - "linkId": "+942865877", - "mappedPosition": { - "latitude": 38.9999735, - "longitude": -77.100141 - }, - "originalPosition": { - "latitude": 38.9999999, - "longitude": -77.1000001 - }, - "type": "stopOver", - "spot": 1, - "sideOfStreet": "left", - "mappedRoadName": "Service Rd S", - "label": "Service Rd S", - "shapeIndex": 279, - "source": "user" - }, - "length": 23903, - "travelTime": 1884, - "maneuver": [ - { - "position": { - "latitude": 38.9999735, - "longitude": -77.100141 - }, - "instruction": "Arrive at Service Rd S. Your destination is on the left.", - "travelTime": 0, - "length": 0, - "id": "M16", - "_type": "PrivateTransportManeuverType" - } - ] - } - ], - "summary": { - "distance": 23903, - "trafficTime": 1861, - "baseTime": 1803, - "flags": [ - "noThroughRoad", - "motorway", - "builtUpArea", - "park", - "privateRoad" - ], - "text": "The trip takes 23.9 km and 31 mins.", - "travelTime": 1861, - "_type": "RouteSummaryType" - } - } - ], - "language": "en-us", - "sourceAttribution": {} - } -} diff --git a/tests/components/here_travel_time/fixtures/no_attribution_transit_route_response.json b/tests/components/here_travel_time/fixtures/no_attribution_transit_route_response.json new file mode 100644 index 00000000000..1057a4a66b1 --- /dev/null +++ b/tests/components/here_travel_time/fixtures/no_attribution_transit_route_response.json @@ -0,0 +1,145 @@ +{ + "routes": [ + { + "id": "C0", + "sections": [ + { + "id": "C0-S0", + "type": "pedestrian", + "actions": [ + { + "action": "depart", + "duration": 1111, + "instruction": "Head west on Wilhelm-Fay-Straße. Go for 1.1 km.", + "length": 1099, + "offset": 0 + }, + { + "action": "roundaboutExit", + "duration": 73, + "instruction": "Walk right around the roundabout and turn at the 1st street Frankfurter Straße. Go for 63 m.", + "length": 63, + "offset": 40, + "exit": 1, + "direction": "right" + }, + { + "action": "arrive", + "duration": 0, + "instruction": "Arrive at Frankfurter Straße. Your destination is on the left.", + "length": 0, + "offset": 47 + } + ], + "travelSummary": { + "duration": 1140, + "length": 1162 + }, + "departure": { + "time": "2022-07-19T15:39:00+02:00", + "place": { + "type": "place", + "location": { + "lat": 50.127787, + "lng": 8.582082 + } + } + }, + "arrival": { + "time": "2022-07-19T15:58:00+02:00", + "place": { + "name": "Eschborn Alfred-Herrhausen-Allee", + "type": "station", + "location": { + "lat": 50.135176, + "lng": 8.572745 + }, + "id": "110439568" + } + }, + "polyline": "BGwhxz_Cgv5rQwDrQ8BvHkNr2BsE7LkI7V0FrO4I7QgF3IoGzKkSjXsTzU0FzFkXnV0tB7pBkN3N0FzF4DjD0KrJwW3S0UrJwWnG4cjI0FjDkDvC8BnB0F_EsErEkDrEkIrJ8G_J4IrOsJ_O8VzjB8Q7asJnQwbztB8G7L0FrJsE7GkNvW8BwC4D8BsEnB8BjDgFkIkD0FqHwL", + "transport": { + "mode": "pedestrian" + } + }, + { + "id": "C0-S3", + "type": "pedestrian", + "actions": [ + { + "action": "depart", + "duration": 166, + "instruction": "Head southwest on Hunsrückstraße. Go for 155 m.", + "length": 155, + "offset": 0 + }, + { + "action": "turn", + "duration": 91, + "instruction": "Turn right onto Stolberger Straße. Go for 82 m.", + "length": 82, + "offset": 4, + "direction": "right", + "severity": "quite" + }, + { + "action": "turn", + "duration": 476, + "instruction": "Turn left onto Horchheimer Straße. Go for 466 m.", + "length": 466, + "offset": 9, + "direction": "left", + "severity": "quite" + }, + { + "action": "turn", + "duration": 18, + "instruction": "Turn left onto Hessenring. Go for 18 m.", + "length": 18, + "offset": 21, + "direction": "left", + "severity": "quite" + }, + { + "action": "arrive", + "duration": 0, + "instruction": "Arrive at Hessenring. Your destination is on the left.", + "length": 0, + "offset": 22 + } + ], + "travelSummary": { + "duration": 720, + "length": 721 + }, + "departure": { + "time": "2022-07-19T17:15:00+02:00", + "place": { + "name": "Wiesbaden-Nordenstadt Stolberger Straße", + "type": "station", + "location": { + "lat": 50.060615, + "lng": 8.344163 + }, + "id": "110812533" + } + }, + "arrival": { + "time": "2022-07-19T17:27:00+02:00", + "place": { + "type": "place", + "location": { + "lat": 50.060941, + "lng": 8.336477 + } + } + }, + "polyline": "BG20uv_C4lp9P9nBn-B3DzFvC3DnQjX8GvHkDvC4DnBoGoBkX8L0F3cwCnLkN7iCnBzhCAnG8B3wBsEzZkD_OwCzP8GnkBwHjmB8GvlBhJ1G", + "transport": { + "mode": "pedestrian" + } + } + ] + } + ] +} diff --git a/tests/components/here_travel_time/fixtures/transit_route_response.json b/tests/components/here_travel_time/fixtures/transit_route_response.json new file mode 100644 index 00000000000..72b04a2d10e --- /dev/null +++ b/tests/components/here_travel_time/fixtures/transit_route_response.json @@ -0,0 +1,153 @@ +{ + "routes": [ + { + "id": "C0", + "sections": [ + { + "id": "C0-S0", + "type": "pedestrian", + "actions": [ + { + "action": "depart", + "duration": 1111, + "instruction": "Head west on Wilhelm-Fay-Straße. Go for 1.1 km.", + "length": 1099, + "offset": 0 + }, + { + "action": "roundaboutExit", + "duration": 73, + "instruction": "Walk right around the roundabout and turn at the 1st street Frankfurter Straße. Go for 63 m.", + "length": 63, + "offset": 40, + "exit": 1, + "direction": "right" + }, + { + "action": "arrive", + "duration": 0, + "instruction": "Arrive at Frankfurter Straße. Your destination is on the left.", + "length": 0, + "offset": 47 + } + ], + "travelSummary": { + "duration": 1140, + "length": 1162 + }, + "departure": { + "time": "2022-07-19T15:39:00+02:00", + "place": { + "type": "place", + "location": { + "lat": 50.127787, + "lng": 8.582082 + } + } + }, + "arrival": { + "time": "2022-07-19T15:58:00+02:00", + "place": { + "name": "Eschborn Alfred-Herrhausen-Allee", + "type": "station", + "location": { + "lat": 50.135176, + "lng": 8.572745 + }, + "id": "110439568" + } + }, + "polyline": "BGwhxz_Cgv5rQwDrQ8BvHkNr2BsE7LkI7V0FrO4I7QgF3IoGzKkSjXsTzU0FzFkXnV0tB7pBkN3N0FzF4DjD0KrJwW3S0UrJwWnG4cjI0FjDkDvC8BnB0F_EsErEkDrEkIrJ8G_J4IrOsJ_O8VzjB8Q7asJnQwbztB8G7L0FrJsE7GkNvW8BwC4D8BsEnB8BjDgFkIkD0FqHwL", + "transport": { + "mode": "pedestrian" + } + }, + { + "id": "C0-S3", + "type": "pedestrian", + "actions": [ + { + "action": "depart", + "duration": 166, + "instruction": "Head southwest on Hunsrückstraße. Go for 155 m.", + "length": 155, + "offset": 0 + }, + { + "action": "turn", + "duration": 91, + "instruction": "Turn right onto Stolberger Straße. Go for 82 m.", + "length": 82, + "offset": 4, + "direction": "right", + "severity": "quite" + }, + { + "action": "turn", + "duration": 476, + "instruction": "Turn left onto Horchheimer Straße. Go for 466 m.", + "length": 466, + "offset": 9, + "direction": "left", + "severity": "quite" + }, + { + "action": "turn", + "duration": 18, + "instruction": "Turn left onto Hessenring. Go for 18 m.", + "length": 18, + "offset": 21, + "direction": "left", + "severity": "quite" + }, + { + "action": "arrive", + "duration": 0, + "instruction": "Arrive at Hessenring. Your destination is on the left.", + "length": 0, + "offset": 22 + } + ], + "travelSummary": { + "duration": 720, + "length": 721 + }, + "departure": { + "time": "2022-07-19T17:15:00+02:00", + "place": { + "name": "Wiesbaden-Nordenstadt Stolberger Straße", + "type": "station", + "location": { + "lat": 50.060615, + "lng": 8.344163 + }, + "id": "110812533" + } + }, + "arrival": { + "time": "2022-07-19T17:27:00+02:00", + "place": { + "type": "place", + "location": { + "lat": 50.060941, + "lng": 8.336477 + } + } + }, + "polyline": "BG20uv_C4lp9P9nBn-B3DzFvC3DnQjX8GvHkDvC4DnBoGoBkX8L0F3cwCnLkN7iCnBzhCAnG8B3wBsEzZkD_OwCzP8GnkBwHjmB8GvlBhJ1G", + "transport": { + "mode": "pedestrian" + }, + "attributions": [ + { + "id": "R00370b-C0-S2-link-0", + "href": "http://creativecommons.org/licenses/by/3.0/it/", + "text": "Some line names used in this product or service were edited to align with official transportation maps.", + "type": "disclaimer" + } + ] + } + ] + } + ] +} diff --git a/tests/components/here_travel_time/test_config_flow.py b/tests/components/here_travel_time/test_config_flow.py index 120ffd828bc..d22d36a4d66 100644 --- a/tests/components/here_travel_time/test_config_flow.py +++ b/tests/components/here_travel_time/test_config_flow.py @@ -1,8 +1,7 @@ """Test the HERE Travel Time config flow.""" from unittest.mock import patch -from herepy import HEREError -from herepy.routing_api import InvalidCredentialsError +from here_routing import HERERoutingError, HERERoutingUnauthorizedError import pytest from homeassistant import config_entries, data_entry_flow @@ -15,12 +14,10 @@ from homeassistant.components.here_travel_time.const import ( CONF_ORIGIN_LATITUDE, CONF_ORIGIN_LONGITUDE, CONF_ROUTE_MODE, - CONF_TRAFFIC_MODE, DOMAIN, ROUTE_MODE_FASTEST, - TRAFFIC_MODE_ENABLED, TRAVEL_MODE_CAR, - TRAVEL_MODE_PUBLIC_TIME_TABLE, + TRAVEL_MODE_PUBLIC, ) from homeassistant.const import ( CONF_API_KEY, @@ -39,10 +36,11 @@ from homeassistant.util.unit_system import ( from .const import ( API_KEY, - CAR_DESTINATION_LATITUDE, - CAR_DESTINATION_LONGITUDE, - CAR_ORIGIN_LATITUDE, - CAR_ORIGIN_LONGITUDE, + DEFAULT_CONFIG, + DESTINATION_LATITUDE, + DESTINATION_LONGITUDE, + ORIGIN_LATITUDE, + ORIGIN_LONGITUDE, ) from tests.common import MockConfigEntry @@ -83,12 +81,12 @@ async def option_init_result_fixture(hass: HomeAssistant) -> data_entry_flow.Flo domain=DOMAIN, unique_id="0123456789", data={ - CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), - CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), - CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), - CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), CONF_API_KEY: API_KEY, - CONF_MODE: TRAVEL_MODE_PUBLIC_TIME_TABLE, + CONF_MODE: TRAVEL_MODE_PUBLIC, CONF_NAME: "test", }, ) @@ -99,7 +97,6 @@ async def option_init_result_fixture(hass: HomeAssistant) -> data_entry_flow.Flo result = await hass.config_entries.options.async_configure( flow["flow_id"], user_input={ - CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, }, @@ -120,8 +117,8 @@ async def origin_step_result_fixture( origin_menu_result["flow_id"], { "origin": { - "latitude": float(CAR_ORIGIN_LATITUDE), - "longitude": float(CAR_ORIGIN_LONGITUDE), + "latitude": float(ORIGIN_LATITUDE), + "longitude": float(ORIGIN_LONGITUDE), "radius": 3.0, } }, @@ -170,8 +167,8 @@ async def test_step_origin_coordinates( menu_result["flow_id"], { "origin": { - "latitude": float(CAR_ORIGIN_LATITUDE), - "longitude": float(CAR_ORIGIN_LONGITUDE), + "latitude": float(ORIGIN_LATITUDE), + "longitude": float(ORIGIN_LONGITUDE), "radius": 3.0, } }, @@ -210,8 +207,8 @@ async def test_step_destination_coordinates( menu_result["flow_id"], { "destination": { - "latitude": float(CAR_DESTINATION_LATITUDE), - "longitude": float(CAR_DESTINATION_LONGITUDE), + "latitude": float(DESTINATION_LATITUDE), + "longitude": float(DESTINATION_LONGITUDE), "radius": 3.0, } }, @@ -223,10 +220,10 @@ async def test_step_destination_coordinates( assert entry.data == { CONF_NAME: "test", CONF_API_KEY: API_KEY, - CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), - CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), - CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), - CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), CONF_MODE: TRAVEL_MODE_CAR, } @@ -261,15 +258,14 @@ async def test_step_destination_entity( assert entry.data == { CONF_NAME: "test", CONF_API_KEY: API_KEY, - CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), - CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), CONF_DESTINATION_ENTITY_ID: "zone.home", CONF_MODE: TRAVEL_MODE_CAR, } assert entry.options == { CONF_UNIT_SYSTEM: expected_unit_option, CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, - CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, CONF_ARRIVAL_TIME: None, CONF_DEPARTURE_TIME: None, } @@ -282,8 +278,8 @@ async def test_form_invalid_auth(hass: HomeAssistant) -> None: ) with patch( - "herepy.RoutingApi.public_transport_timetable", - side_effect=InvalidCredentialsError, + "here_routing.HERERoutingApi.route", + side_effect=HERERoutingUnauthorizedError, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -305,8 +301,8 @@ async def test_form_unknown_error(hass: HomeAssistant) -> None: ) with patch( - "herepy.RoutingApi.public_transport_timetable", - side_effect=HEREError, + "here_routing.HERERoutingApi.route", + side_effect=HERERoutingError, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -327,15 +323,7 @@ async def test_options_flow(hass: HomeAssistant) -> None: entry = MockConfigEntry( domain=DOMAIN, unique_id="0123456789", - data={ - CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), - CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), - CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), - CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), - CONF_API_KEY: API_KEY, - CONF_MODE: TRAVEL_MODE_CAR, - CONF_NAME: "test", - }, + data=DEFAULT_CONFIG, ) entry.add_to_hass(hass) @@ -351,7 +339,6 @@ async def test_options_flow(hass: HomeAssistant) -> None: result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ - CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, }, @@ -381,7 +368,6 @@ async def test_options_flow_arrival_time_step( assert entry.options == { CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, - CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, CONF_ARRIVAL_TIME: "08:00:00", } @@ -407,7 +393,6 @@ async def test_options_flow_departure_time_step( assert entry.options == { CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, - CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, CONF_DEPARTURE_TIME: "08:00:00", } @@ -426,5 +411,4 @@ async def test_options_flow_no_time_step( assert entry.options == { CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, - CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, } diff --git a/tests/components/here_travel_time/test_init.py b/tests/components/here_travel_time/test_init.py index 05b7f6983db..b8bbdec241a 100644 --- a/tests/components/here_travel_time/test_init.py +++ b/tests/components/here_travel_time/test_init.py @@ -3,24 +3,10 @@ import pytest from homeassistant.components.here_travel_time.config_flow import default_options -from homeassistant.components.here_travel_time.const import ( - CONF_DESTINATION_LATITUDE, - CONF_DESTINATION_LONGITUDE, - CONF_ORIGIN_LATITUDE, - CONF_ORIGIN_LONGITUDE, - DOMAIN, - TRAVEL_MODE_CAR, -) -from homeassistant.const import CONF_API_KEY, CONF_MODE, CONF_NAME +from homeassistant.components.here_travel_time.const import DOMAIN from homeassistant.core import HomeAssistant -from .const import ( - API_KEY, - CAR_DESTINATION_LATITUDE, - CAR_DESTINATION_LONGITUDE, - CAR_ORIGIN_LATITUDE, - CAR_ORIGIN_LONGITUDE, -) +from .const import DEFAULT_CONFIG from tests.common import MockConfigEntry @@ -31,15 +17,7 @@ async def test_unload_entry(hass: HomeAssistant) -> None: entry = MockConfigEntry( domain=DOMAIN, unique_id="0123456789", - data={ - CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), - CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), - CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), - CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), - CONF_API_KEY: API_KEY, - CONF_MODE: TRAVEL_MODE_CAR, - CONF_NAME: "test", - }, + data=DEFAULT_CONFIG, options=default_options(hass), ) entry.add_to_hass(hass) diff --git a/tests/components/here_travel_time/test_sensor.py b/tests/components/here_travel_time/test_sensor.py index 5cc4802d253..8a6f7f3f5d3 100644 --- a/tests/components/here_travel_time/test_sensor.py +++ b/tests/components/here_travel_time/test_sensor.py @@ -1,8 +1,14 @@ """The test for the HERE Travel Time sensor platform.""" from unittest.mock import MagicMock, patch -from herepy.here_enum import RouteMode -from herepy.routing_api import NoRouteFoundError +from here_routing import ( + HERERoutingError, + Place, + Return, + RoutingMode, + Spans, + TransportMode, +) import pytest from homeassistant.components.here_travel_time.config_flow import default_options @@ -20,15 +26,14 @@ from homeassistant.components.here_travel_time.const import ( ICON_BICYCLE, ICON_CAR, ICON_PEDESTRIAN, - ICON_PUBLIC, ICON_TRUCK, - NO_ROUTE_ERROR_MESSAGE, + IMPERIAL_UNITS, + METRIC_UNITS, ROUTE_MODE_FASTEST, - TRAFFIC_MODE_ENABLED, TRAVEL_MODE_BICYCLE, TRAVEL_MODE_CAR, TRAVEL_MODE_PEDESTRIAN, - TRAVEL_MODE_PUBLIC_TIME_TABLE, + TRAVEL_MODE_PUBLIC, TRAVEL_MODE_TRUCK, ) from homeassistant.const import ( @@ -51,10 +56,10 @@ from homeassistant.setup import async_setup_component from .const import ( API_KEY, - CAR_DESTINATION_LATITUDE, - CAR_DESTINATION_LONGITUDE, - CAR_ORIGIN_LATITUDE, - CAR_ORIGIN_LONGITUDE, + DESTINATION_LATITUDE, + DESTINATION_LONGITUDE, + ORIGIN_LATITUDE, + ORIGIN_LONGITUDE, ) from tests.common import MockConfigEntry @@ -69,9 +74,9 @@ from tests.common import MockConfigEntry "metric", None, None, + "26", + 13.682, "30", - 23.903, - "31", LENGTH_KILOMETERS, ), ( @@ -80,8 +85,8 @@ from tests.common import MockConfigEntry "metric", None, None, - "30", - 23.903, + "26", + 13.682, "30", LENGTH_KILOMETERS, ), @@ -91,19 +96,8 @@ from tests.common import MockConfigEntry "imperial", None, None, - "30", - 14.85263, - "30", - LENGTH_MILES, - ), - ( - TRAVEL_MODE_PUBLIC_TIME_TABLE, - ICON_PUBLIC, - "imperial", - "08:00:00", - None, - "30", - 14.85263, + "26", + 8.5016, "30", LENGTH_MILES, ), @@ -113,9 +107,9 @@ from tests.common import MockConfigEntry "metric", None, "08:00:00", + "26", + 13.682, "30", - 23.903, - "31", LENGTH_KILOMETERS, ), ], @@ -138,10 +132,10 @@ async def test_sensor( domain=DOMAIN, unique_id="0123456789", data={ - CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), - CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), - CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), - CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), CONF_API_KEY: API_KEY, CONF_MODE: mode, CONF_NAME: "test", @@ -161,10 +155,6 @@ async def test_sensor( duration = hass.states.get("sensor.test_duration") assert duration.attributes.get("unit_of_measurement") == TIME_MINUTES - assert ( - duration.attributes.get(ATTR_ATTRIBUTION) - == "With the support of HERE Technologies. All information is provided without warranty of any kind." - ) assert duration.attributes.get(ATTR_ICON) == icon assert duration.state == expected_duration @@ -186,31 +176,21 @@ async def test_sensor( assert hass.states.get("sensor.test_origin").state == "22nd St NW" assert ( hass.states.get("sensor.test_origin").attributes.get(ATTR_LATITUDE) - == CAR_ORIGIN_LATITUDE + == "38.8999937" ) assert ( hass.states.get("sensor.test_origin").attributes.get(ATTR_LONGITUDE) - == CAR_ORIGIN_LONGITUDE - ) - - assert hass.states.get("sensor.test_origin").state == "22nd St NW" - assert ( - hass.states.get("sensor.test_origin").attributes.get(ATTR_LATITUDE) - == CAR_ORIGIN_LATITUDE - ) - assert ( - hass.states.get("sensor.test_origin").attributes.get(ATTR_LONGITUDE) - == CAR_ORIGIN_LONGITUDE + == "-77.0479682" ) assert hass.states.get("sensor.test_destination").state == "Service Rd S" assert ( hass.states.get("sensor.test_destination").attributes.get(ATTR_LATITUDE) - == CAR_DESTINATION_LATITUDE + == "38.99997" ) assert ( hass.states.get("sensor.test_destination").attributes.get(ATTR_LONGITUDE) - == CAR_DESTINATION_LONGITUDE + == "-77.10014" ) @@ -227,8 +207,8 @@ async def test_circular_ref(hass: HomeAssistant, caplog): unique_id="0123456789", data={ CONF_ORIGIN_ENTITY_ID: "test.first", - CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), - CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), CONF_API_KEY: API_KEY, CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", @@ -242,22 +222,69 @@ async def test_circular_ref(hass: HomeAssistant, caplog): hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() - assert "No coordinatnes found for test.first" in caplog.text + assert "No coordinates found for test.first" in caplog.text -@pytest.mark.usefixtures("empty_attribution_response") -async def test_no_attribution(hass: HomeAssistant): - """Test that an empty attribution is handled.""" +@pytest.mark.usefixtures("valid_response") +@pytest.mark.parametrize( + "unit_system,expected_distance", + [ + (METRIC_UNITS, "1.883"), + (IMPERIAL_UNITS, "1.1700419549829"), + ], +) +async def test_public_transport( + hass: HomeAssistant, unit_system: str, expected_distance: str +): + """Test that public transport mode is handled.""" entry = MockConfigEntry( domain=DOMAIN, unique_id="0123456789", data={ - CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), - CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), - CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), - CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), CONF_API_KEY: API_KEY, - CONF_MODE: TRAVEL_MODE_TRUCK, + CONF_MODE: TRAVEL_MODE_PUBLIC, + CONF_NAME: "test", + }, + options={ + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_ARRIVAL_TIME: "08:00:00", + CONF_DEPARTURE_TIME: None, + CONF_UNIT_SYSTEM: unit_system, + }, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + + assert ( + hass.states.get("sensor.test_duration").attributes.get(ATTR_ATTRIBUTION) + == "http://creativecommons.org/licenses/by/3.0/it/,Some line names used in this product or service were edited to align with official transportation maps." + ) + assert hass.states.get("sensor.test_distance").state == pytest.approx( + expected_distance + ) + + +@pytest.mark.usefixtures("no_attribution_response") +async def test_no_attribution_response(hass: HomeAssistant): + """Test that no_attribution is handled.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_PUBLIC, CONF_NAME: "test", }, options=default_options(hass), @@ -280,8 +307,8 @@ async def test_entity_ids(hass: HomeAssistant, valid_response: MagicMock): "zone": [ { "name": "Origin", - "latitude": CAR_ORIGIN_LATITUDE, - "longitude": CAR_ORIGIN_LONGITUDE, + "latitude": ORIGIN_LATITUDE, + "longitude": ORIGIN_LONGITUDE, "radius": 250, "passive": False, }, @@ -292,8 +319,8 @@ async def test_entity_ids(hass: HomeAssistant, valid_response: MagicMock): "device_tracker.test", "not_home", { - "latitude": float(CAR_DESTINATION_LATITUDE), - "longitude": float(CAR_DESTINATION_LONGITUDE), + "latitude": float(DESTINATION_LATITUDE), + "longitude": float(DESTINATION_LONGITUDE), }, ) entry = MockConfigEntry( @@ -315,19 +342,17 @@ async def test_entity_ids(hass: HomeAssistant, valid_response: MagicMock): hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() - assert hass.states.get("sensor.test_distance").state == "23.903" + assert hass.states.get("sensor.test_distance").state == "13.682" valid_response.assert_called_with( - [CAR_ORIGIN_LATITUDE, CAR_ORIGIN_LONGITUDE], - [CAR_DESTINATION_LATITUDE, CAR_DESTINATION_LONGITUDE], - True, - [ - RouteMode[ROUTE_MODE_FASTEST], - RouteMode[TRAVEL_MODE_TRUCK], - RouteMode[TRAFFIC_MODE_ENABLED], - ], - arrival=None, - departure="now", + transport_mode=TransportMode.TRUCK, + origin=Place(ORIGIN_LATITUDE, ORIGIN_LONGITUDE), + destination=Place(DESTINATION_LATITUDE, DESTINATION_LONGITUDE), + routing_mode=RoutingMode.FAST, + arrival_time=None, + departure_time=None, + return_values=[Return.POLYINE, Return.SUMMARY], + spans=[Spans.NAMES], ) @@ -338,8 +363,8 @@ async def test_destination_entity_not_found(hass: HomeAssistant, caplog): domain=DOMAIN, unique_id="0123456789", data={ - CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), - CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), CONF_DESTINATION_ENTITY_ID: "device_tracker.test", CONF_API_KEY: API_KEY, CONF_MODE: TRAVEL_MODE_TRUCK, @@ -365,8 +390,8 @@ async def test_origin_entity_not_found(hass: HomeAssistant, caplog): unique_id="0123456789", data={ CONF_ORIGIN_ENTITY_ID: "device_tracker.test", - CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), - CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), CONF_API_KEY: API_KEY, CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", @@ -394,8 +419,8 @@ async def test_invalid_destination_entity_state(hass: HomeAssistant, caplog): domain=DOMAIN, unique_id="0123456789", data={ - CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), - CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), CONF_DESTINATION_ENTITY_ID: "device_tracker.test", CONF_API_KEY: API_KEY, CONF_MODE: TRAVEL_MODE_TRUCK, @@ -425,8 +450,8 @@ async def test_invalid_origin_entity_state(hass: HomeAssistant, caplog): unique_id="0123456789", data={ CONF_ORIGIN_ENTITY_ID: "device_tracker.test", - CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), - CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), CONF_API_KEY: API_KEY, CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", @@ -446,17 +471,19 @@ async def test_invalid_origin_entity_state(hass: HomeAssistant, caplog): async def test_route_not_found(hass: HomeAssistant, caplog): """Test that route not found error is correctly handled.""" with patch( - "herepy.RoutingApi.public_transport_timetable", - side_effect=NoRouteFoundError, + "here_routing.HERERoutingApi.route", + side_effect=HERERoutingError( + "Route calculation failed: Couldn't find a route." + ), ): entry = MockConfigEntry( domain=DOMAIN, unique_id="0123456789", data={ - CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), - CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), - CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), - CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), CONF_API_KEY: API_KEY, CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", @@ -469,4 +496,4 @@ async def test_route_not_found(hass: HomeAssistant, caplog): hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() - assert NO_ROUTE_ERROR_MESSAGE in caplog.text + assert "Route calculation failed: Couldn't find a route." in caplog.text From 52298a251ba3d5630ed381a0a3090ed5e3606efb Mon Sep 17 00:00:00 2001 From: G Johansson Date: Wed, 16 Nov 2022 06:23:48 +0100 Subject: [PATCH 0447/1033] Wake on LAN yaml configuration move to integration key (#81249) * Wake on Lan integration yaml * clean code * Add test * Add test fixture * Address loading --- .../components/wake_on_lan/__init__.py | 47 +++++++++++++- homeassistant/components/wake_on_lan/const.py | 4 ++ .../components/wake_on_lan/strings.json | 8 +++ .../components/wake_on_lan/switch.py | 38 ++++++----- .../wake_on_lan/translations/en.json | 8 +++ tests/components/wake_on_lan/conftest.py | 13 ++++ tests/components/wake_on_lan/test_init.py | 64 ++++++++++++++++++- 7 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 homeassistant/components/wake_on_lan/strings.json create mode 100644 homeassistant/components/wake_on_lan/translations/en.json create mode 100644 tests/components/wake_on_lan/conftest.py diff --git a/homeassistant/components/wake_on_lan/__init__.py b/homeassistant/components/wake_on_lan/__init__.py index aae640381a2..a8ad7637c6e 100644 --- a/homeassistant/components/wake_on_lan/__init__.py +++ b/homeassistant/components/wake_on_lan/__init__.py @@ -1,16 +1,27 @@ """Support for sending Wake-On-LAN magic packets.""" +import asyncio +from collections.abc import Coroutine from functools import partial import logging +from typing import Any import voluptuous as vol import wakeonlan -from homeassistant.const import CONF_BROADCAST_ADDRESS, CONF_BROADCAST_PORT, CONF_MAC +from homeassistant.const import ( + CONF_BROADCAST_ADDRESS, + CONF_BROADCAST_PORT, + CONF_HOST, + CONF_MAC, + CONF_NAME, + Platform, +) from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType -from .const import DOMAIN +from .const import CONF_OFF_ACTION, DEFAULT_NAME, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -24,9 +35,41 @@ WAKE_ON_LAN_SEND_MAGIC_PACKET_SCHEMA = vol.Schema( } ) +SWITCH_SCHEMA = vol.Schema( + { + vol.Required(CONF_MAC): cv.string, + vol.Optional(CONF_BROADCAST_ADDRESS): cv.string, + vol.Optional(CONF_BROADCAST_PORT): cv.port, + vol.Optional(CONF_HOST): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_OFF_ACTION): cv.SCRIPT_SCHEMA, + } +) + + +CONFIG_SCHEMA = vol.Schema( + {vol.Optional(DOMAIN): vol.All(cv.ensure_list, [vol.Schema(SWITCH_SCHEMA)])}, + extra=vol.ALLOW_EXTRA, +) + async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the wake on LAN component.""" + load_coroutines: list[Coroutine[Any, Any, None]] = [] + if wol_config := config.get(DOMAIN): + for switch_config in wol_config: + load_coroutines.append( + discovery.async_load_platform( + hass, + Platform.SWITCH, + DOMAIN, + switch_config, + config, + ) + ) + + if load_coroutines: + await asyncio.gather(*load_coroutines) async def send_magic_packet(call: ServiceCall) -> None: """Send magic packet to wake up a device.""" diff --git a/homeassistant/components/wake_on_lan/const.py b/homeassistant/components/wake_on_lan/const.py index 14f2bd0263f..a954be33943 100644 --- a/homeassistant/components/wake_on_lan/const.py +++ b/homeassistant/components/wake_on_lan/const.py @@ -1,2 +1,6 @@ """Constants for the Wake-On-LAN component.""" DOMAIN = "wake_on_lan" +CONF_OFF_ACTION = "turn_off" + +DEFAULT_NAME = "Wake on LAN" +DEFAULT_PING_TIMEOUT = 1 diff --git a/homeassistant/components/wake_on_lan/strings.json b/homeassistant/components/wake_on_lan/strings.json new file mode 100644 index 00000000000..275f2e30fff --- /dev/null +++ b/homeassistant/components/wake_on_lan/strings.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "title": "The Wake on Lan YAML configuration has been moved", + "description": "Configuring Wake on Lan using YAML has been moved to integration key.\n\nYour existing YAML configuration will be working for 2 more versions.\n\nMigrate your YAML configuration to the integration key according to the documentation." + } + } +} diff --git a/homeassistant/components/wake_on_lan/switch.py b/homeassistant/components/wake_on_lan/switch.py index 446df402c87..af4f15b79ba 100644 --- a/homeassistant/components/wake_on_lan/switch.py +++ b/homeassistant/components/wake_on_lan/switch.py @@ -23,18 +23,14 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.script import Script from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from .const import DOMAIN +from .const import CONF_OFF_ACTION, DEFAULT_NAME, DEFAULT_PING_TIMEOUT, DOMAIN _LOGGER = logging.getLogger(__name__) -CONF_OFF_ACTION = "turn_off" - -DEFAULT_NAME = "Wake on LAN" -DEFAULT_PING_TIMEOUT = 1 - PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend( { vol.Required(CONF_MAC): cv.string, @@ -47,21 +43,33 @@ PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend( ) -def setup_platform( +async def async_setup_platform( hass: HomeAssistant, config: ConfigType, - add_entities: AddEntitiesCallback, + async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up a wake on lan switch.""" - broadcast_address: str | None = config.get(CONF_BROADCAST_ADDRESS) - broadcast_port: int | None = config.get(CONF_BROADCAST_PORT) - host: str | None = config.get(CONF_HOST) - mac_address: str = config[CONF_MAC] - name: str = config[CONF_NAME] - off_action: list[Any] | None = config.get(CONF_OFF_ACTION) + if (switch_config := discovery_info) is None: + async_create_issue( + hass, + DOMAIN, + "moved_yaml", + breaks_in_ha_version="2022.12.0", + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key="moved_yaml", + ) + switch_config = config - add_entities( + broadcast_address: str | None = switch_config.get(CONF_BROADCAST_ADDRESS) + broadcast_port: int | None = switch_config.get(CONF_BROADCAST_PORT) + host: str | None = switch_config.get(CONF_HOST) + mac_address: str = switch_config[CONF_MAC] + name: str = switch_config[CONF_NAME] + off_action: list[Any] | None = switch_config.get(CONF_OFF_ACTION) + + async_add_entities( [ WolSwitch( hass, diff --git a/homeassistant/components/wake_on_lan/translations/en.json b/homeassistant/components/wake_on_lan/translations/en.json new file mode 100644 index 00000000000..3d37fa4b9ba --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/en.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "Configuring Wake on Lan using YAML has been moved to integration key.\n\nYour existing YAML configuration will be working for 2 more versions.\n\nMigrate your YAML configuration to the integration key according to the documentation.", + "title": "The Wake on Lan YAML configuration has been moved" + } + } +} \ No newline at end of file diff --git a/tests/components/wake_on_lan/conftest.py b/tests/components/wake_on_lan/conftest.py new file mode 100644 index 00000000000..5f92cf34e37 --- /dev/null +++ b/tests/components/wake_on_lan/conftest.py @@ -0,0 +1,13 @@ +"""Test fixtures for Wake on Lan.""" +from __future__ import annotations + +from unittest.mock import patch + +import pytest + + +@pytest.fixture(autouse=True) +def mock_send_magic_packet(): + """Mock magic packet.""" + with patch("wakeonlan.send_magic_packet") as mock_send: + yield mock_send diff --git a/tests/components/wake_on_lan/test_init.py b/tests/components/wake_on_lan/test_init.py index 3ec7a53a436..0c61538a835 100644 --- a/tests/components/wake_on_lan/test_init.py +++ b/tests/components/wake_on_lan/test_init.py @@ -1,14 +1,26 @@ """Tests for Wake On LAN component.""" +from __future__ import annotations + +import subprocess from unittest.mock import patch import pytest import voluptuous as vol +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.components.wake_on_lan import DOMAIN, SERVICE_SEND_MAGIC_PACKET +from homeassistant.const import ( + ATTR_ENTITY_ID, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, + STATE_OFF, + STATE_ON, +) +from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component -async def test_send_magic_packet(hass): +async def test_send_magic_packet(hass: HomeAssistant) -> None: """Test of send magic packet service call.""" with patch("homeassistant.components.wake_on_lan.wakeonlan") as mocked_wakeonlan: mac = "aa:bb:cc:dd:ee:ff" @@ -65,3 +77,53 @@ async def test_send_magic_packet(hass): assert len(mocked_wakeonlan.mock_calls) == 4 assert mocked_wakeonlan.mock_calls[-1][1][0] == mac assert not mocked_wakeonlan.mock_calls[-1][2] + + +async def test_setup_from_integration_yaml(hass: HomeAssistant) -> None: + """Test setup from integration yaml.""" + assert await async_setup_component( + hass, + DOMAIN, + { + DOMAIN: [ + { + "mac": "00-01-02-03-04-05", + "host": "validhostname", + }, + { + "mac": "06-07-08-09-0A-0B", + "host": "validhostname2", + "name": "Friendly Name", + }, + ], + }, + ) + await hass.async_block_till_done() + + state = hass.states.get("switch.wake_on_lan") + state2 = hass.states.get("switch.friendly_name") + + assert state.state == STATE_OFF + assert state2.state == STATE_OFF + + with patch.object(subprocess, "call", return_value=0): + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.wake_on_lan"}, + blocking=True, + ) + + state = hass.states.get("switch.wake_on_lan") + assert state.state == STATE_ON + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.wake_on_lan"}, + blocking=True, + ) + + state = hass.states.get("switch.wake_on_lan") + assert state.state == STATE_ON From 00f4933e726fa432fc9e3caf644d1ad0978f41fa Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 16 Nov 2022 07:09:46 +0100 Subject: [PATCH 0448/1033] Import tag from homeassistant.components (#82161) --- homeassistant/components/esphome/__init__.py | 12 +++--------- homeassistant/components/mobile_app/webhook.py | 11 ++--------- homeassistant/components/mqtt/tag.py | 10 ++-------- homeassistant/components/tag/__init__.py | 10 ---------- 4 files changed, 7 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index ea909725b9f..acbcc0f57dc 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -5,7 +5,7 @@ from collections.abc import Callable import functools import logging import math -from typing import TYPE_CHECKING, Any, Generic, NamedTuple, TypeVar, cast, overload +from typing import Any, Generic, NamedTuple, TypeVar, cast, overload from aioesphomeapi import ( APIClient, @@ -27,7 +27,7 @@ from aioesphomeapi import ( import voluptuous as vol from homeassistant import const -from homeassistant.components import zeroconf +from homeassistant.components import tag, zeroconf from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_DEVICE_ID, @@ -55,9 +55,6 @@ from .domain_data import DOMAIN, DomainData # Import config flow so that it's added to the registry from .entry_data import RuntimeEntryData -if TYPE_CHECKING: - from homeassistant.components.tag import TagProtocol - CONF_NOISE_PSK = "noise_psk" _LOGGER = logging.getLogger(__name__) _R = TypeVar("_R") @@ -134,11 +131,8 @@ async def async_setup_entry( # noqa: C901 # Call native tag scan if service_name == "tag_scanned" and device_id is not None: - # Importing tag via hass.components in case it is overridden - # in a custom_components (custom_components.tag) - tag: TagProtocol = hass.components.tag tag_id = service_data["tag_id"] - hass.async_create_task(tag.async_scan_tag(tag_id, device_id)) + hass.async_create_task(tag.async_scan_tag(hass, tag_id, device_id)) return hass.bus.async_fire( diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 514d8820399..8dfadc6645d 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -7,14 +7,13 @@ from functools import wraps from http import HTTPStatus import logging import secrets -from typing import TYPE_CHECKING from aiohttp.web import HTTPBadRequest, Request, Response, json_response from nacl.exceptions import CryptoError from nacl.secret import SecretBox import voluptuous as vol -from homeassistant.components import camera, cloud, notify as hass_notify +from homeassistant.components import camera, cloud, notify as hass_notify, tag from homeassistant.components.binary_sensor import ( DEVICE_CLASSES as BINARY_SENSOR_CLASSES, ) @@ -114,10 +113,6 @@ from .helpers import ( webhook_response, ) -if TYPE_CHECKING: - from homeassistant.components.tag import TagProtocol - - _LOGGER = logging.getLogger(__name__) DELAY_SAVE = 10 @@ -688,10 +683,8 @@ async def webhook_get_config(hass, config_entry, data): @validate_schema({vol.Required("tag_id"): cv.string}) async def webhook_scan_tag(hass, config_entry, data): """Handle a fire event webhook.""" - # Importing tag via hass.components in case it is overridden - # in a custom_components (custom_components.tag) - tag: TagProtocol = hass.components.tag await tag.async_scan_tag( + hass, data["tag_id"], config_entry.data[ATTR_DEVICE_ID], registration_context(config_entry.data), diff --git a/homeassistant/components/mqtt/tag.py b/homeassistant/components/mqtt/tag.py index 223718554b0..132ab3200a1 100644 --- a/homeassistant/components/mqtt/tag.py +++ b/homeassistant/components/mqtt/tag.py @@ -3,10 +3,10 @@ from __future__ import annotations from collections.abc import Callable import functools -from typing import TYPE_CHECKING import voluptuous as vol +from homeassistant.components import tag from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_DEVICE, CONF_PLATFORM, CONF_VALUE_TEMPLATE from homeassistant.core import HomeAssistant @@ -28,9 +28,6 @@ from .models import MqttValueTemplate, ReceiveMessage, ReceivePayloadType from .subscription import EntitySubscription from .util import get_mqtt_data, valid_subscribe_topic -if TYPE_CHECKING: - from homeassistant.components.tag import TagProtocol - LOG_NAME = "Tag" TAG = "tag" @@ -139,10 +136,7 @@ class MQTTTagScanner(MqttDiscoveryDeviceUpdate): if not tag_id: # No output from template, ignore return - # Importing tag via hass.components in case it is overridden - # in a custom_components (custom_components.tag) - tag: TagProtocol = self.hass.components.tag - await tag.async_scan_tag(tag_id, self.device_id) + await tag.async_scan_tag(self.hass, tag_id, self.device_id) self._sub_state = subscription.async_prepare_subscribe_topics( self.hass, diff --git a/homeassistant/components/tag/__init__.py b/homeassistant/components/tag/__init__.py index 1b3ee9b646b..090835103f9 100644 --- a/homeassistant/components/tag/__init__.py +++ b/homeassistant/components/tag/__init__.py @@ -2,7 +2,6 @@ from __future__ import annotations import logging -from typing import Protocol import uuid import voluptuous as vol @@ -107,15 +106,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True -class TagProtocol(Protocol): - """Protocol for type checking.""" - - async def async_scan_tag( - self, tag_id: str, device_id: str | None, context: Context | None = None - ) -> None: - """Handle when a tag is scanned.""" - - @bind_hass async def async_scan_tag( hass: HomeAssistant, From 3bfb91d15903b147b283ce99a00a51c3c709b320 Mon Sep 17 00:00:00 2001 From: Brynley McDonald Date: Wed, 16 Nov 2022 19:27:09 +1300 Subject: [PATCH 0449/1033] Add integration_type for flick_electric (#81065) * Add integration_type for flick_electric * Updates from hassfest --- homeassistant/components/flick_electric/manifest.json | 1 + homeassistant/generated/integrations.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/flick_electric/manifest.json b/homeassistant/components/flick_electric/manifest.json index 0a79bff792a..849a0270576 100644 --- a/homeassistant/components/flick_electric/manifest.json +++ b/homeassistant/components/flick_electric/manifest.json @@ -6,5 +6,6 @@ "requirements": ["PyFlick==0.0.2"], "codeowners": ["@ZephireNZ"], "iot_class": "cloud_polling", + "integration_type": "service", "loggers": ["pyflick"] } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 3e969c3e6f5..f20e3f18b07 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -1631,7 +1631,7 @@ }, "flick_electric": { "name": "Flick Electric", - "integration_type": "hub", + "integration_type": "service", "config_flow": true, "iot_class": "cloud_polling" }, From 532c6b74d4f2397bce400ecabf3e056cbf346839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Paris?= Date: Wed, 16 Nov 2022 08:27:51 +0000 Subject: [PATCH 0450/1033] Enable long term stats for fitbit entities (#81934) --- homeassistant/components/fitbit/const.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/fitbit/const.py b/homeassistant/components/fitbit/const.py index 1da3058c790..26db1f91659 100644 --- a/homeassistant/components/fitbit/const.py +++ b/homeassistant/components/fitbit/const.py @@ -4,7 +4,7 @@ from __future__ import annotations from dataclasses import dataclass from typing import Final -from homeassistant.components.sensor import SensorEntityDescription +from homeassistant.components.sensor import SensorEntityDescription, SensorStateClass from homeassistant.const import ( CONF_CLIENT_ID, CONF_CLIENT_SECRET, @@ -196,18 +196,21 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( name="BMI", unit_type="BMI", icon="mdi:human", + state_class=SensorStateClass.MEASUREMENT, ), FitbitSensorEntityDescription( key="body/fat", name="Body Fat", unit_type=PERCENTAGE, icon="mdi:human", + state_class=SensorStateClass.MEASUREMENT, ), FitbitSensorEntityDescription( key="body/weight", name="Weight", unit_type="", icon="mdi:human", + state_class=SensorStateClass.MEASUREMENT, ), FitbitSensorEntityDescription( key="sleep/awakeningsCount", @@ -220,6 +223,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( name="Sleep Efficiency", unit_type=PERCENTAGE, icon="mdi:sleep", + state_class=SensorStateClass.MEASUREMENT, ), FitbitSensorEntityDescription( key="sleep/minutesAfterWakeup", From c7dfd6b15a3fc9fa81d260b3dfa8a3d836f9afa8 Mon Sep 17 00:00:00 2001 From: cnico Date: Wed, 16 Nov 2022 09:42:31 +0100 Subject: [PATCH 0451/1033] Add flipr battery level sensor (#81389) * Addition of battery level sensor. Correction of pylint errors * Review improvement for typing * Review improvement for typing * Correction following review --- homeassistant/components/flipr/binary_sensor.py | 2 +- homeassistant/components/flipr/config_flow.py | 17 +++++++++++------ homeassistant/components/flipr/manifest.json | 2 +- homeassistant/components/flipr/sensor.py | 11 +++++++++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/flipr/test_sensor.py | 9 +++++++++ 7 files changed, 33 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/flipr/binary_sensor.py b/homeassistant/components/flipr/binary_sensor.py index 64ccaf7553f..646e260bd60 100644 --- a/homeassistant/components/flipr/binary_sensor.py +++ b/homeassistant/components/flipr/binary_sensor.py @@ -45,7 +45,7 @@ class FliprBinarySensor(FliprEntity, BinarySensorEntity): """Representation of Flipr binary sensors.""" @property - def is_on(self): + def is_on(self) -> bool: """Return true if the binary sensor is on in case of a Problem is detected.""" return ( self.coordinator.data[self.entity_description.key] == "TooLow" diff --git a/homeassistant/components/flipr/config_flow.py b/homeassistant/components/flipr/config_flow.py index b1e4f31d044..5c8cc6f76fb 100644 --- a/homeassistant/components/flipr/config_flow.py +++ b/homeassistant/components/flipr/config_flow.py @@ -9,6 +9,7 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_EMAIL, CONF_PASSWORD +from homeassistant.data_entry_flow import FlowResult from .const import CONF_FLIPR_ID, DOMAIN @@ -20,12 +21,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 - _username: str | None = None - _password: str | None = None - _flipr_id: str | None = None - _possible_flipr_ids: list[str] | None = None + _username: str + _password: str + _flipr_id: str = "" + _possible_flipr_ids: list[str] - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict[str, str] | None = None + ) -> FlowResult: """Handle the initial step.""" if user_input is None: return self._show_setup_form() @@ -92,7 +95,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return flipr_ids - async def async_step_flipr_id(self, user_input=None): + async def async_step_flipr_id( + self, user_input: dict[str, str] | None = None + ) -> FlowResult: """Handle the initial step.""" if not user_input: # Creation of a select with the proposal of flipr ids values found by API. diff --git a/homeassistant/components/flipr/manifest.json b/homeassistant/components/flipr/manifest.json index 35fbe8259a2..f88ea64dc3a 100644 --- a/homeassistant/components/flipr/manifest.json +++ b/homeassistant/components/flipr/manifest.json @@ -3,7 +3,7 @@ "name": "Flipr", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/flipr", - "requirements": ["flipr-api==1.4.2"], + "requirements": ["flipr-api==1.4.4"], "codeowners": ["@cnico"], "iot_class": "cloud_polling", "loggers": ["flipr_api"] diff --git a/homeassistant/components/flipr/sensor.py b/homeassistant/components/flipr/sensor.py index 9cf788d7170..99b44cc95a0 100644 --- a/homeassistant/components/flipr/sensor.py +++ b/homeassistant/components/flipr/sensor.py @@ -8,7 +8,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ELECTRIC_POTENTIAL_MILLIVOLT, TEMP_CELSIUS +from homeassistant.const import ELECTRIC_POTENTIAL_MILLIVOLT, PERCENTAGE, TEMP_CELSIUS from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -48,6 +48,13 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( icon="mdi:pool", state_class=SensorStateClass.MEASUREMENT, ), + SensorEntityDescription( + key="battery", + name="Battery Level", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.BATTERY, + ), ) @@ -67,6 +74,6 @@ class FliprSensor(FliprEntity, SensorEntity): """Sensor representing FliprSensor data.""" @property - def native_value(self): + def native_value(self) -> str: """State of the sensor.""" return self.coordinator.data[self.entity_description.key] diff --git a/requirements_all.txt b/requirements_all.txt index 9252d244a53..2b0c55828a9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -705,7 +705,7 @@ fixerio==1.0.0a0 fjaraskupan==2.2.0 # homeassistant.components.flipr -flipr-api==1.4.2 +flipr-api==1.4.4 # homeassistant.components.flux_led flux_led==0.28.32 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3a8681f3d9a..99f7cdfd899 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -527,7 +527,7 @@ fivem-api==0.1.2 fjaraskupan==2.2.0 # homeassistant.components.flipr -flipr-api==1.4.2 +flipr-api==1.4.4 # homeassistant.components.flux_led flux_led==0.28.32 diff --git a/tests/components/flipr/test_sensor.py b/tests/components/flipr/test_sensor.py index 1b8a1928b1f..51fbf2941f8 100644 --- a/tests/components/flipr/test_sensor.py +++ b/tests/components/flipr/test_sensor.py @@ -11,6 +11,7 @@ from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, CONF_EMAIL, CONF_PASSWORD, + PERCENTAGE, TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant @@ -29,6 +30,7 @@ MOCK_FLIPR_MEASURE = { "date_time": MOCK_DATE_TIME, "ph_status": "TooLow", "chlorine_status": "Medium", + "battery": 95.0, } @@ -94,6 +96,13 @@ async def test_sensors(hass: HomeAssistant) -> None: assert state.attributes.get(ATTR_STATE_CLASS) is SensorStateClass.MEASUREMENT assert state.state == "0.23654886" + state = hass.states.get("sensor.flipr_myfliprid_battery_level") + assert state + assert state.attributes.get(ATTR_ICON) is None + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE + assert state.attributes.get(ATTR_STATE_CLASS) is SensorStateClass.MEASUREMENT + assert state.state == "95.0" + async def test_error_flipr_api_sensors(hass: HomeAssistant) -> None: """Test the Flipr sensors error.""" From 5b29fe650b7a9eac7826461d35c92ceec18262ef Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 16 Nov 2022 10:22:50 +0100 Subject: [PATCH 0452/1033] Revert "Wake on LAN yaml configuration move to integration key" (#82184) --- .../components/wake_on_lan/__init__.py | 47 +------------- homeassistant/components/wake_on_lan/const.py | 4 -- .../components/wake_on_lan/strings.json | 8 --- .../components/wake_on_lan/switch.py | 38 +++++------ .../wake_on_lan/translations/en.json | 8 --- tests/components/wake_on_lan/conftest.py | 13 ---- tests/components/wake_on_lan/test_init.py | 64 +------------------ 7 files changed, 18 insertions(+), 164 deletions(-) delete mode 100644 homeassistant/components/wake_on_lan/strings.json delete mode 100644 homeassistant/components/wake_on_lan/translations/en.json delete mode 100644 tests/components/wake_on_lan/conftest.py diff --git a/homeassistant/components/wake_on_lan/__init__.py b/homeassistant/components/wake_on_lan/__init__.py index a8ad7637c6e..aae640381a2 100644 --- a/homeassistant/components/wake_on_lan/__init__.py +++ b/homeassistant/components/wake_on_lan/__init__.py @@ -1,27 +1,16 @@ """Support for sending Wake-On-LAN magic packets.""" -import asyncio -from collections.abc import Coroutine from functools import partial import logging -from typing import Any import voluptuous as vol import wakeonlan -from homeassistant.const import ( - CONF_BROADCAST_ADDRESS, - CONF_BROADCAST_PORT, - CONF_HOST, - CONF_MAC, - CONF_NAME, - Platform, -) +from homeassistant.const import CONF_BROADCAST_ADDRESS, CONF_BROADCAST_PORT, CONF_MAC from homeassistant.core import HomeAssistant, ServiceCall -from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType -from .const import CONF_OFF_ACTION, DEFAULT_NAME, DOMAIN +from .const import DOMAIN _LOGGER = logging.getLogger(__name__) @@ -35,41 +24,9 @@ WAKE_ON_LAN_SEND_MAGIC_PACKET_SCHEMA = vol.Schema( } ) -SWITCH_SCHEMA = vol.Schema( - { - vol.Required(CONF_MAC): cv.string, - vol.Optional(CONF_BROADCAST_ADDRESS): cv.string, - vol.Optional(CONF_BROADCAST_PORT): cv.port, - vol.Optional(CONF_HOST): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_OFF_ACTION): cv.SCRIPT_SCHEMA, - } -) - - -CONFIG_SCHEMA = vol.Schema( - {vol.Optional(DOMAIN): vol.All(cv.ensure_list, [vol.Schema(SWITCH_SCHEMA)])}, - extra=vol.ALLOW_EXTRA, -) - async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the wake on LAN component.""" - load_coroutines: list[Coroutine[Any, Any, None]] = [] - if wol_config := config.get(DOMAIN): - for switch_config in wol_config: - load_coroutines.append( - discovery.async_load_platform( - hass, - Platform.SWITCH, - DOMAIN, - switch_config, - config, - ) - ) - - if load_coroutines: - await asyncio.gather(*load_coroutines) async def send_magic_packet(call: ServiceCall) -> None: """Send magic packet to wake up a device.""" diff --git a/homeassistant/components/wake_on_lan/const.py b/homeassistant/components/wake_on_lan/const.py index a954be33943..14f2bd0263f 100644 --- a/homeassistant/components/wake_on_lan/const.py +++ b/homeassistant/components/wake_on_lan/const.py @@ -1,6 +1,2 @@ """Constants for the Wake-On-LAN component.""" DOMAIN = "wake_on_lan" -CONF_OFF_ACTION = "turn_off" - -DEFAULT_NAME = "Wake on LAN" -DEFAULT_PING_TIMEOUT = 1 diff --git a/homeassistant/components/wake_on_lan/strings.json b/homeassistant/components/wake_on_lan/strings.json deleted file mode 100644 index 275f2e30fff..00000000000 --- a/homeassistant/components/wake_on_lan/strings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "moved_yaml": { - "title": "The Wake on Lan YAML configuration has been moved", - "description": "Configuring Wake on Lan using YAML has been moved to integration key.\n\nYour existing YAML configuration will be working for 2 more versions.\n\nMigrate your YAML configuration to the integration key according to the documentation." - } - } -} diff --git a/homeassistant/components/wake_on_lan/switch.py b/homeassistant/components/wake_on_lan/switch.py index af4f15b79ba..446df402c87 100644 --- a/homeassistant/components/wake_on_lan/switch.py +++ b/homeassistant/components/wake_on_lan/switch.py @@ -23,14 +23,18 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.script import Script from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from .const import CONF_OFF_ACTION, DEFAULT_NAME, DEFAULT_PING_TIMEOUT, DOMAIN +from .const import DOMAIN _LOGGER = logging.getLogger(__name__) +CONF_OFF_ACTION = "turn_off" + +DEFAULT_NAME = "Wake on LAN" +DEFAULT_PING_TIMEOUT = 1 + PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend( { vol.Required(CONF_MAC): cv.string, @@ -43,33 +47,21 @@ PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend( ) -async def async_setup_platform( +def setup_platform( hass: HomeAssistant, config: ConfigType, - async_add_entities: AddEntitiesCallback, + add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up a wake on lan switch.""" - if (switch_config := discovery_info) is None: - async_create_issue( - hass, - DOMAIN, - "moved_yaml", - breaks_in_ha_version="2022.12.0", - is_fixable=False, - severity=IssueSeverity.WARNING, - translation_key="moved_yaml", - ) - switch_config = config + broadcast_address: str | None = config.get(CONF_BROADCAST_ADDRESS) + broadcast_port: int | None = config.get(CONF_BROADCAST_PORT) + host: str | None = config.get(CONF_HOST) + mac_address: str = config[CONF_MAC] + name: str = config[CONF_NAME] + off_action: list[Any] | None = config.get(CONF_OFF_ACTION) - broadcast_address: str | None = switch_config.get(CONF_BROADCAST_ADDRESS) - broadcast_port: int | None = switch_config.get(CONF_BROADCAST_PORT) - host: str | None = switch_config.get(CONF_HOST) - mac_address: str = switch_config[CONF_MAC] - name: str = switch_config[CONF_NAME] - off_action: list[Any] | None = switch_config.get(CONF_OFF_ACTION) - - async_add_entities( + add_entities( [ WolSwitch( hass, diff --git a/homeassistant/components/wake_on_lan/translations/en.json b/homeassistant/components/wake_on_lan/translations/en.json deleted file mode 100644 index 3d37fa4b9ba..00000000000 --- a/homeassistant/components/wake_on_lan/translations/en.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "moved_yaml": { - "description": "Configuring Wake on Lan using YAML has been moved to integration key.\n\nYour existing YAML configuration will be working for 2 more versions.\n\nMigrate your YAML configuration to the integration key according to the documentation.", - "title": "The Wake on Lan YAML configuration has been moved" - } - } -} \ No newline at end of file diff --git a/tests/components/wake_on_lan/conftest.py b/tests/components/wake_on_lan/conftest.py deleted file mode 100644 index 5f92cf34e37..00000000000 --- a/tests/components/wake_on_lan/conftest.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Test fixtures for Wake on Lan.""" -from __future__ import annotations - -from unittest.mock import patch - -import pytest - - -@pytest.fixture(autouse=True) -def mock_send_magic_packet(): - """Mock magic packet.""" - with patch("wakeonlan.send_magic_packet") as mock_send: - yield mock_send diff --git a/tests/components/wake_on_lan/test_init.py b/tests/components/wake_on_lan/test_init.py index 0c61538a835..3ec7a53a436 100644 --- a/tests/components/wake_on_lan/test_init.py +++ b/tests/components/wake_on_lan/test_init.py @@ -1,26 +1,14 @@ """Tests for Wake On LAN component.""" -from __future__ import annotations - -import subprocess from unittest.mock import patch import pytest import voluptuous as vol -from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.components.wake_on_lan import DOMAIN, SERVICE_SEND_MAGIC_PACKET -from homeassistant.const import ( - ATTR_ENTITY_ID, - SERVICE_TURN_OFF, - SERVICE_TURN_ON, - STATE_OFF, - STATE_ON, -) -from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component -async def test_send_magic_packet(hass: HomeAssistant) -> None: +async def test_send_magic_packet(hass): """Test of send magic packet service call.""" with patch("homeassistant.components.wake_on_lan.wakeonlan") as mocked_wakeonlan: mac = "aa:bb:cc:dd:ee:ff" @@ -77,53 +65,3 @@ async def test_send_magic_packet(hass: HomeAssistant) -> None: assert len(mocked_wakeonlan.mock_calls) == 4 assert mocked_wakeonlan.mock_calls[-1][1][0] == mac assert not mocked_wakeonlan.mock_calls[-1][2] - - -async def test_setup_from_integration_yaml(hass: HomeAssistant) -> None: - """Test setup from integration yaml.""" - assert await async_setup_component( - hass, - DOMAIN, - { - DOMAIN: [ - { - "mac": "00-01-02-03-04-05", - "host": "validhostname", - }, - { - "mac": "06-07-08-09-0A-0B", - "host": "validhostname2", - "name": "Friendly Name", - }, - ], - }, - ) - await hass.async_block_till_done() - - state = hass.states.get("switch.wake_on_lan") - state2 = hass.states.get("switch.friendly_name") - - assert state.state == STATE_OFF - assert state2.state == STATE_OFF - - with patch.object(subprocess, "call", return_value=0): - - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_ON, - {ATTR_ENTITY_ID: "switch.wake_on_lan"}, - blocking=True, - ) - - state = hass.states.get("switch.wake_on_lan") - assert state.state == STATE_ON - - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: "switch.wake_on_lan"}, - blocking=True, - ) - - state = hass.states.get("switch.wake_on_lan") - assert state.state == STATE_ON From 93401df73fc688d7c8395128f5484d59155a31cc Mon Sep 17 00:00:00 2001 From: muppet3000 Date: Wed, 16 Nov 2022 09:41:14 +0000 Subject: [PATCH 0453/1033] Fix Growatt incorrect energy dashboard values for grid import (#82163) * Fix Growatt incorrect energy dashboard values for grid import (#80905) * Growatt - addressing review comments (#80905) * Growatt - addressing more review comments (#80905) --- .../components/growatt_server/sensor.py | 61 +++++++++++++++++-- .../growatt_server/sensor_types/mix.py | 1 + .../sensor_types/sensor_entity_description.py | 1 + 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/growatt_server/sensor.py b/homeassistant/components/growatt_server/sensor.py index ac19d91b24d..d6b74b78475 100644 --- a/homeassistant/components/growatt_server/sensor.py +++ b/homeassistant/components/growatt_server/sensor.py @@ -159,7 +159,7 @@ class GrowattInverter(SensorEntity): @property def native_value(self): """Return the state of the sensor.""" - result = self.probe.get_data(self.entity_description.api_key) + result = self.probe.get_data(self.entity_description) if self.entity_description.precision is not None: result = round(result, self.entity_description.precision) return result @@ -168,7 +168,7 @@ class GrowattInverter(SensorEntity): def native_unit_of_measurement(self) -> str | None: """Return the unit of measurement of the sensor, if any.""" if self.entity_description.currency: - return self.probe.get_data("currency") + return self.probe.get_currency() return super().native_unit_of_measurement def update(self) -> None: @@ -187,6 +187,7 @@ class GrowattData: self.device_id = device_id self.plant_id = None self.data = {} + self.previous_values = {} self.username = username self.password = password @@ -254,9 +255,61 @@ class GrowattData: **mix_detail, **dashboard_values_for_mix, } + _LOGGER.debug( + "Finished updating data for %s (%s)", + self.device_id, + self.growatt_type, + ) except json.decoder.JSONDecodeError: _LOGGER.error("Unable to fetch data from Growatt server") - def get_data(self, variable): + def get_currency(self): + """Get the currency.""" + return self.data.get("currency") + + def get_data(self, entity_description): """Get the data.""" - return self.data.get(variable) + _LOGGER.debug( + "Data request for: %s", + entity_description.name, + ) + variable = entity_description.api_key + api_value = self.data.get(variable) + previous_value = self.previous_values.get(variable) + return_value = api_value + + # If we have a 'drop threshold' specified, then check it and correct if needed + if ( + entity_description.previous_value_drop_threshold is not None + and previous_value is not None + and api_value is not None + ): + _LOGGER.debug( + "%s - Drop threshold specified (%s), checking for drop... API Value: %s, Previous Value: %s", + entity_description.name, + entity_description.previous_value_drop_threshold, + api_value, + previous_value, + ) + diff = float(api_value) - float(previous_value) + + # Check if the value has dropped (negative value i.e. < 0) and it has only dropped by a + # small amount, if so, use the previous value. + # Note - The energy dashboard takes care of drops within 10% of the current value, + # however if the value is low e.g. 0.2 and drops by 0.1 it classes as a reset. + if -(entity_description.previous_value_drop_threshold) <= diff < 0: + _LOGGER.debug( + "Diff is negative, but only by a small amount therefore not a nightly reset, " + "using previous value (%s) instead of api value (%s)", + previous_value, + api_value, + ) + return_value = previous_value + else: + _LOGGER.debug( + "%s - No drop detected, using API value", entity_description.name + ) + + self.previous_values[variable] = return_value + + return return_value diff --git a/homeassistant/components/growatt_server/sensor_types/mix.py b/homeassistant/components/growatt_server/sensor_types/mix.py index ce29760b317..cfb47e81519 100644 --- a/homeassistant/components/growatt_server/sensor_types/mix.py +++ b/homeassistant/components/growatt_server/sensor_types/mix.py @@ -240,5 +240,6 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, + previous_value_drop_threshold=0.2, ), ) diff --git a/homeassistant/components/growatt_server/sensor_types/sensor_entity_description.py b/homeassistant/components/growatt_server/sensor_types/sensor_entity_description.py index 04822fca35b..08a20209098 100644 --- a/homeassistant/components/growatt_server/sensor_types/sensor_entity_description.py +++ b/homeassistant/components/growatt_server/sensor_types/sensor_entity_description.py @@ -19,3 +19,4 @@ class GrowattSensorEntityDescription(SensorEntityDescription, GrowattRequiredKey precision: int | None = None currency: bool = False + previous_value_drop_threshold: float | None = None From ff1ec7a028f747de1f96521eb3df6f98d7426434 Mon Sep 17 00:00:00 2001 From: rappenze Date: Wed, 16 Nov 2022 10:44:34 +0100 Subject: [PATCH 0454/1033] Normalize url entered in fibaro integration setup dialog (#81996) * Normalize url entered in fibaro integration setup dialog * Improvements as suggested in code review * Fix spelling in comments --- .../components/fibaro/config_flow.py | 13 ++++++++++++ tests/components/fibaro/test_config_flow.py | 21 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/homeassistant/components/fibaro/config_flow.py b/homeassistant/components/fibaro/config_flow.py index 7a6d7422520..e5cb75890be 100644 --- a/homeassistant/components/fibaro/config_flow.py +++ b/homeassistant/components/fibaro/config_flow.py @@ -54,6 +54,18 @@ async def _validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str } +def _normalize_url(url: str) -> str: + """Try to fix errors in the entered url. + + We know that the url should be in the format http:///api/ + """ + if url.endswith("/api"): + return f"{url}/" + if not url.endswith("/api/"): + return f"{url}api/" if url.endswith("/") else f"{url}/api/" + return url + + class FibaroConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for Fibaro.""" @@ -71,6 +83,7 @@ class FibaroConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if user_input is not None: try: + user_input[CONF_URL] = _normalize_url(user_input[CONF_URL]) info = await _validate_input(self.hass, user_input) except FibaroConnectFailed: errors["base"] = "cannot_connect" diff --git a/tests/components/fibaro/test_config_flow.py b/tests/components/fibaro/test_config_flow.py index f68bb5fe4ca..2a0936588cd 100644 --- a/tests/components/fibaro/test_config_flow.py +++ b/tests/components/fibaro/test_config_flow.py @@ -6,6 +6,7 @@ import pytest from homeassistant import config_entries from homeassistant.components.fibaro import DOMAIN +from homeassistant.components.fibaro.config_flow import _normalize_url from homeassistant.components.fibaro.const import CONF_IMPORT_PLUGINS from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME @@ -362,3 +363,23 @@ async def test_reauth_auth_failure(hass): assert result["type"] == "form" assert result["step_id"] == "reauth_confirm" assert result["errors"] == {"base": "invalid_auth"} + + +async def test_normalize_url_does_not_touch_valid_url(): + """Test that a correctly entered url is not touched.""" + assert _normalize_url(TEST_URL) == TEST_URL + + +async def test_normalize_url_add_missing_slash_at_the_end(): + """Test that a / is added at the end.""" + assert _normalize_url("http://192.168.1.1/api") == "http://192.168.1.1/api/" + + +async def test_normalize_url_add_api(): + """Test that api/ is added.""" + assert _normalize_url("http://192.168.1.1/") == "http://192.168.1.1/api/" + + +async def test_normalize_url_add_api_with_leading_slash(): + """Test that /api/ is added.""" + assert _normalize_url("http://192.168.1.1") == "http://192.168.1.1/api/" From e01115fd1fa990c691717ded69ea2adf2410d56a Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 16 Nov 2022 12:39:39 +0100 Subject: [PATCH 0455/1033] Update apprise to 1.2.0 (#82182) --- homeassistant/components/apprise/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/apprise/manifest.json b/homeassistant/components/apprise/manifest.json index c0dc9ab4497..984ecea50d7 100644 --- a/homeassistant/components/apprise/manifest.json +++ b/homeassistant/components/apprise/manifest.json @@ -2,7 +2,7 @@ "domain": "apprise", "name": "Apprise", "documentation": "https://www.home-assistant.io/integrations/apprise", - "requirements": ["apprise==1.1.0"], + "requirements": ["apprise==1.2.0"], "codeowners": ["@caronc"], "iot_class": "cloud_push", "loggers": ["apprise"] diff --git a/requirements_all.txt b/requirements_all.txt index 2b0c55828a9..9661cca9c58 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -333,7 +333,7 @@ anthemav==1.4.1 apcaccess==0.0.13 # homeassistant.components.apprise -apprise==1.1.0 +apprise==1.2.0 # homeassistant.components.aprs aprslib==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 99f7cdfd899..d23e8208081 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -299,7 +299,7 @@ anthemav==1.4.1 apcaccess==0.0.13 # homeassistant.components.apprise -apprise==1.1.0 +apprise==1.2.0 # homeassistant.components.aprs aprslib==0.7.0 From a91abebea8ce354ea108e3def1e2ef3782599332 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 16 Nov 2022 12:43:17 +0100 Subject: [PATCH 0456/1033] Use IntFlag for EntityFeature (#81903) --- homeassistant/components/alarm_control_panel/const.py | 4 ++-- homeassistant/components/camera/__init__.py | 4 ++-- homeassistant/components/climate/const.py | 4 ++-- homeassistant/components/cover/__init__.py | 4 ++-- homeassistant/components/fan/__init__.py | 4 ++-- homeassistant/components/humidifier/const.py | 4 ++-- homeassistant/components/light/__init__.py | 4 ++-- homeassistant/components/lock/__init__.py | 4 ++-- homeassistant/components/media_player/const.py | 4 ++-- homeassistant/components/remote/__init__.py | 4 ++-- homeassistant/components/siren/const.py | 4 ++-- homeassistant/components/update/const.py | 4 ++-- homeassistant/components/vacuum/__init__.py | 4 ++-- homeassistant/components/water_heater/__init__.py | 4 ++-- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/const.py b/homeassistant/components/alarm_control_panel/const.py index 3a2f2c51551..e6e628f834d 100644 --- a/homeassistant/components/alarm_control_panel/const.py +++ b/homeassistant/components/alarm_control_panel/const.py @@ -1,5 +1,5 @@ """Provides the constants needed for component.""" -from enum import IntEnum +from enum import IntFlag from typing import Final from homeassistant.backports.enum import StrEnum @@ -23,7 +23,7 @@ FORMAT_TEXT: Final = "text" FORMAT_NUMBER: Final = "number" -class AlarmControlPanelEntityFeature(IntEnum): +class AlarmControlPanelEntityFeature(IntFlag): """Supported features of the alarm control panel entity.""" ARM_HOME = 1 diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index a8c6b1ea517..227b1e0c9b7 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -7,7 +7,7 @@ from collections.abc import Awaitable, Callable, Iterable from contextlib import suppress from dataclasses import asdict, dataclass from datetime import datetime, timedelta -from enum import IntEnum +from enum import IntFlag from functools import partial import logging import os @@ -95,7 +95,7 @@ STATE_STREAMING: Final = "streaming" STATE_IDLE: Final = "idle" -class CameraEntityFeature(IntEnum): +class CameraEntityFeature(IntFlag): """Supported features of the camera entity.""" ON_OFF = 1 diff --git a/homeassistant/components/climate/const.py b/homeassistant/components/climate/const.py index 202da1af597..9ee561b9c1b 100644 --- a/homeassistant/components/climate/const.py +++ b/homeassistant/components/climate/const.py @@ -1,6 +1,6 @@ """Provides the constants needed for component.""" -from enum import IntEnum +from enum import IntFlag from homeassistant.backports.enum import StrEnum @@ -146,7 +146,7 @@ SERVICE_SET_SWING_MODE = "set_swing_mode" SERVICE_SET_TEMPERATURE = "set_temperature" -class ClimateEntityFeature(IntEnum): +class ClimateEntityFeature(IntFlag): """Supported features of the climate entity.""" TARGET_TEMPERATURE = 1 diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py index 57187c56819..8b4aa79659d 100644 --- a/homeassistant/components/cover/__init__.py +++ b/homeassistant/components/cover/__init__.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass from datetime import timedelta -from enum import IntEnum +from enum import IntFlag import functools as ft import logging from typing import Any, TypeVar, final @@ -86,7 +86,7 @@ DEVICE_CLASS_WINDOW = CoverDeviceClass.WINDOW.value # mypy: disallow-any-generics -class CoverEntityFeature(IntEnum): +class CoverEntityFeature(IntFlag): """Supported features of the cover entity.""" OPEN = 1 diff --git a/homeassistant/components/fan/__init__.py b/homeassistant/components/fan/__init__.py index d44335fad07..b65e6f75b3e 100644 --- a/homeassistant/components/fan/__init__.py +++ b/homeassistant/components/fan/__init__.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass from datetime import timedelta -from enum import IntEnum +from enum import IntFlag import functools as ft import logging import math @@ -41,7 +41,7 @@ SCAN_INTERVAL = timedelta(seconds=30) ENTITY_ID_FORMAT = DOMAIN + ".{}" -class FanEntityFeature(IntEnum): +class FanEntityFeature(IntFlag): """Supported features of the fan entity.""" SET_SPEED = 1 diff --git a/homeassistant/components/humidifier/const.py b/homeassistant/components/humidifier/const.py index 03f89f5489a..1f802f7fa36 100644 --- a/homeassistant/components/humidifier/const.py +++ b/homeassistant/components/humidifier/const.py @@ -1,5 +1,5 @@ """Provides the constants needed for component.""" -from enum import IntEnum +from enum import IntFlag MODE_NORMAL = "normal" MODE_ECO = "eco" @@ -30,7 +30,7 @@ SERVICE_SET_MODE = "set_mode" SERVICE_SET_HUMIDITY = "set_humidity" -class HumidifierEntityFeature(IntEnum): +class HumidifierEntityFeature(IntFlag): """Supported features of the alarm control panel entity.""" MODES = 1 diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 5bf72b7267b..098b627cf48 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -5,7 +5,7 @@ from collections.abc import Iterable import csv import dataclasses from datetime import timedelta -from enum import IntEnum +from enum import IntFlag import logging import os from typing import Any, cast, final @@ -41,7 +41,7 @@ DATA_PROFILES = "light_profiles" ENTITY_ID_FORMAT = DOMAIN + ".{}" -class LightEntityFeature(IntEnum): +class LightEntityFeature(IntFlag): """Supported features of the light entity.""" EFFECT = 4 diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index d241d57e128..a8166e9e3bd 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass from datetime import timedelta -from enum import IntEnum +from enum import IntFlag import functools as ft import logging from typing import Any, final @@ -48,7 +48,7 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) LOCK_SERVICE_SCHEMA = make_entity_service_schema({vol.Optional(ATTR_CODE): cv.string}) -class LockEntityFeature(IntEnum): +class LockEntityFeature(IntFlag): """Supported features of the lock entity.""" OPEN = 1 diff --git a/homeassistant/components/media_player/const.py b/homeassistant/components/media_player/const.py index ea8069cc7e4..1cc90aa4904 100644 --- a/homeassistant/components/media_player/const.py +++ b/homeassistant/components/media_player/const.py @@ -1,5 +1,5 @@ """Provides the constants needed for component.""" -from enum import IntEnum +from enum import IntFlag from homeassistant.backports.enum import StrEnum @@ -176,7 +176,7 @@ REPEAT_MODE_ONE = "one" REPEAT_MODES = [REPEAT_MODE_OFF, REPEAT_MODE_ALL, REPEAT_MODE_ONE] -class MediaPlayerEntityFeature(IntEnum): +class MediaPlayerEntityFeature(IntFlag): """Supported features of the media player entity.""" PAUSE = 1 diff --git a/homeassistant/components/remote/__init__.py b/homeassistant/components/remote/__init__.py index 6ba5ca89d2d..74535c44228 100644 --- a/homeassistant/components/remote/__init__.py +++ b/homeassistant/components/remote/__init__.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Iterable from dataclasses import dataclass from datetime import timedelta -from enum import IntEnum +from enum import IntFlag import functools as ft import logging from typing import Any, final @@ -61,7 +61,7 @@ DEFAULT_DELAY_SECS = 0.4 DEFAULT_HOLD_SECS = 0 -class RemoteEntityFeature(IntEnum): +class RemoteEntityFeature(IntFlag): """Supported features of the remote entity.""" LEARN_COMMAND = 1 diff --git a/homeassistant/components/siren/const.py b/homeassistant/components/siren/const.py index 6ef7c9e6955..374b1d59e2a 100644 --- a/homeassistant/components/siren/const.py +++ b/homeassistant/components/siren/const.py @@ -1,6 +1,6 @@ """Constants for the siren component.""" -from enum import IntEnum +from enum import IntFlag from typing import Final DOMAIN: Final = "siren" @@ -12,7 +12,7 @@ ATTR_DURATION: Final = "duration" ATTR_VOLUME_LEVEL: Final = "volume_level" -class SirenEntityFeature(IntEnum): +class SirenEntityFeature(IntFlag): """Supported features of the siren entity.""" TURN_ON = 1 diff --git a/homeassistant/components/update/const.py b/homeassistant/components/update/const.py index aec3c183a63..c9340473298 100644 --- a/homeassistant/components/update/const.py +++ b/homeassistant/components/update/const.py @@ -1,13 +1,13 @@ """Constants for the update component.""" from __future__ import annotations -from enum import IntEnum +from enum import IntFlag from typing import Final DOMAIN: Final = "update" -class UpdateEntityFeature(IntEnum): +class UpdateEntityFeature(IntFlag): """Supported features of the update entity.""" INSTALL = 1 diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py index 4488f8062d6..6a7efe4fa56 100644 --- a/homeassistant/components/vacuum/__init__.py +++ b/homeassistant/components/vacuum/__init__.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Mapping from dataclasses import dataclass from datetime import timedelta -from enum import IntEnum +from enum import IntFlag from functools import partial import logging from typing import Any, final @@ -74,7 +74,7 @@ STATES = [STATE_CLEANING, STATE_DOCKED, STATE_RETURNING, STATE_ERROR] DEFAULT_NAME = "Vacuum cleaner robot" -class VacuumEntityFeature(IntEnum): +class VacuumEntityFeature(IntFlag): """Supported features of the vacuum entity.""" TURN_ON = 1 diff --git a/homeassistant/components/water_heater/__init__.py b/homeassistant/components/water_heater/__init__.py index 4712d4a2701..ed12a1a6cf9 100644 --- a/homeassistant/components/water_heater/__init__.py +++ b/homeassistant/components/water_heater/__init__.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Mapping from dataclasses import dataclass from datetime import timedelta -from enum import IntEnum +from enum import IntFlag import functools as ft import logging from typing import Any, final @@ -56,7 +56,7 @@ STATE_HEAT_PUMP = "heat_pump" STATE_GAS = "gas" -class WaterHeaterEntityFeature(IntEnum): +class WaterHeaterEntityFeature(IntFlag): """Supported features of the fan entity.""" TARGET_TEMPERATURE = 1 From 9b8f94363c0b4ecd1434ac1ac3bb82febd3889d0 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 16 Nov 2022 12:46:29 +0100 Subject: [PATCH 0457/1033] Fix statistic_during_period for data with holes (#81847) --- .../components/recorder/statistics.py | 201 +++++++----- .../components/recorder/test_websocket_api.py | 289 +++++++++++++++--- 2 files changed, 375 insertions(+), 115 deletions(-) diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 8a744fd4daa..e361249580f 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -1216,11 +1216,29 @@ def _get_max_mean_min_statistic( return result +def _first_statistic( + session: Session, + table: type[Statistics | StatisticsShortTerm], + metadata_id: int, +) -> datetime | None: + """Return the data of the oldest statistic row for a given metadata id.""" + stmt = lambda_stmt( + lambda: select(table.start) + .filter(table.metadata_id == metadata_id) + .order_by(table.start.asc()) + .limit(1) + ) + if stats := execute_stmt_lambda_element(session, stmt): + return process_timestamp(stats[0].start) # type: ignore[no-any-return] + return None + + def _get_oldest_sum_statistic( session: Session, head_start_time: datetime | None, main_start_time: datetime | None, tail_start_time: datetime | None, + oldest_stat: datetime | None, tail_only: bool, metadata_id: int, ) -> float | None: @@ -1231,10 +1249,10 @@ def _get_oldest_sum_statistic( start_time: datetime | None, table: type[Statistics | StatisticsShortTerm], metadata_id: int, - ) -> tuple[float | None, datetime | None]: + ) -> float | None: """Return the oldest non-NULL sum during the period.""" stmt = lambda_stmt( - lambda: select(table.sum, table.start) + lambda: select(table.sum) .filter(table.metadata_id == metadata_id) .filter(table.sum.is_not(None)) .order_by(table.start.asc()) @@ -1248,49 +1266,49 @@ def _get_oldest_sum_statistic( else: period = start_time.replace(minute=0, second=0, microsecond=0) prev_period = period - table.duration - stmt += lambda q: q.filter(table.start == prev_period) + stmt += lambda q: q.filter(table.start >= prev_period) stats = execute_stmt_lambda_element(session, stmt) - return ( - (stats[0].sum, process_timestamp(stats[0].start)) if stats else (None, None) - ) + return stats[0].sum if stats else None - oldest_start: datetime | None oldest_sum: float | None = None - if head_start_time is not None: - oldest_sum, oldest_start = _get_oldest_sum_statistic_in_sub_period( - session, head_start_time, StatisticsShortTerm, metadata_id + # This function won't be called if tail_only is False and main_start_time is None + # the extra checks are added to satisfy MyPy + if not tail_only and main_start_time is not None and oldest_stat is not None: + period = main_start_time.replace(minute=0, second=0, microsecond=0) + prev_period = period - Statistics.duration + if prev_period < oldest_stat: + return 0 + + if ( + head_start_time is not None + and ( + oldest_sum := _get_oldest_sum_statistic_in_sub_period( + session, head_start_time, StatisticsShortTerm, metadata_id + ) ) - if ( - oldest_start is not None - and oldest_start < head_start_time - and oldest_sum is not None - ): - return oldest_sum + is not None + ): + return oldest_sum if not tail_only: - assert main_start_time is not None - oldest_sum, oldest_start = _get_oldest_sum_statistic_in_sub_period( - session, main_start_time, Statistics, metadata_id - ) if ( - oldest_start is not None - and oldest_start < main_start_time - and oldest_sum is not None - ): + oldest_sum := _get_oldest_sum_statistic_in_sub_period( + session, main_start_time, Statistics, metadata_id + ) + ) is not None: return oldest_sum return 0 - if tail_start_time is not None: - oldest_sum, oldest_start = _get_oldest_sum_statistic_in_sub_period( - session, tail_start_time, StatisticsShortTerm, metadata_id + if ( + tail_start_time is not None + and ( + oldest_sum := _get_oldest_sum_statistic_in_sub_period( + session, tail_start_time, StatisticsShortTerm, metadata_id + ) ) - if ( - oldest_start is not None - and oldest_start < tail_start_time - and oldest_sum is not None - ): - return oldest_sum + ) is not None: + return oldest_sum return 0 @@ -1373,51 +1391,79 @@ def statistic_during_period( result: dict[str, Any] = {} - # To calculate the summary, data from the statistics (hourly) and short_term_statistics - # (5 minute) tables is combined - # - The short term statistics table is used for the head and tail of the period, - # if the period it doesn't start or end on a full hour - # - The statistics table is used for the remainder of the time - now = dt_util.utcnow() - if end_time is not None and end_time > now: - end_time = now - - tail_only = ( - start_time is not None - and end_time is not None - and end_time - start_time < timedelta(hours=1) - ) - - # Calculate the head period - head_start_time: datetime | None = None - head_end_time: datetime | None = None - if not tail_only and start_time is not None and start_time.minute: - head_start_time = start_time - head_end_time = start_time.replace( - minute=0, second=0, microsecond=0 - ) + timedelta(hours=1) - - # Calculate the tail period - tail_start_time: datetime | None = None - tail_end_time: datetime | None = None - if end_time is None: - tail_start_time = now.replace(minute=0, second=0, microsecond=0) - elif end_time.minute: - tail_start_time = ( - start_time - if tail_only - else end_time.replace(minute=0, second=0, microsecond=0) - ) - tail_end_time = end_time - - # Calculate the main period - main_start_time: datetime | None = None - main_end_time: datetime | None = None - if not tail_only: - main_start_time = start_time if head_end_time is None else head_end_time - main_end_time = end_time if tail_start_time is None else tail_start_time - with session_scope(hass=hass) as session: + # Fetch metadata for the given statistic_id + if not ( + metadata := get_metadata_with_session(session, statistic_ids=[statistic_id]) + ): + return result + + metadata_id = metadata[statistic_id][0] + + oldest_stat = _first_statistic(session, Statistics, metadata_id) + oldest_5_min_stat = None + if not valid_statistic_id(statistic_id): + oldest_5_min_stat = _first_statistic( + session, StatisticsShortTerm, metadata_id + ) + + # To calculate the summary, data from the statistics (hourly) and + # short_term_statistics (5 minute) tables is combined + # - The short term statistics table is used for the head and tail of the period, + # if the period it doesn't start or end on a full hour + # - The statistics table is used for the remainder of the time + now = dt_util.utcnow() + if end_time is not None and end_time > now: + end_time = now + + tail_only = ( + start_time is not None + and end_time is not None + and end_time - start_time < timedelta(hours=1) + ) + + # Calculate the head period + head_start_time: datetime | None = None + head_end_time: datetime | None = None + if ( + not tail_only + and oldest_stat is not None + and oldest_5_min_stat is not None + and oldest_5_min_stat - oldest_stat < timedelta(hours=1) + and (start_time is None or start_time < oldest_5_min_stat) + ): + # To improve accuracy of averaged for statistics which were added within + # recorder's retention period. + head_start_time = oldest_5_min_stat + head_end_time = oldest_5_min_stat.replace( + minute=0, second=0, microsecond=0 + ) + timedelta(hours=1) + elif not tail_only and start_time is not None and start_time.minute: + head_start_time = start_time + head_end_time = start_time.replace( + minute=0, second=0, microsecond=0 + ) + timedelta(hours=1) + + # Calculate the tail period + tail_start_time: datetime | None = None + tail_end_time: datetime | None = None + if end_time is None: + tail_start_time = now.replace(minute=0, second=0, microsecond=0) + elif end_time.minute: + tail_start_time = ( + start_time + if tail_only + else end_time.replace(minute=0, second=0, microsecond=0) + ) + tail_end_time = end_time + + # Calculate the main period + main_start_time: datetime | None = None + main_end_time: datetime | None = None + if not tail_only: + main_start_time = start_time if head_end_time is None else head_end_time + main_end_time = end_time if tail_start_time is None else tail_start_time + # Fetch metadata for the given statistic_id metadata = get_metadata_with_session(session, statistic_ids=[statistic_id]) if not metadata: @@ -1449,6 +1495,7 @@ def statistic_during_period( head_start_time, main_start_time, tail_start_time, + oldest_stat, tail_only, metadata_id, ) diff --git a/tests/components/recorder/test_websocket_api.py b/tests/components/recorder/test_websocket_api.py index 00e9d0d35b4..423ab4bbc52 100644 --- a/tests/components/recorder/test_websocket_api.py +++ b/tests/components/recorder/test_websocket_api.py @@ -182,7 +182,8 @@ async def test_statistics_during_period(recorder_mock, hass, hass_ws_client): @freeze_time(datetime.datetime(2022, 10, 21, 7, 25, tzinfo=datetime.timezone.utc)) -async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): +@pytest.mark.parametrize("offset", (0, 1, 2)) +async def test_statistic_during_period(recorder_mock, hass, hass_ws_client, offset): """Test statistic_during_period.""" id = 1 @@ -197,7 +198,9 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): client = await hass_ws_client() zero = now - start = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=-3) + start = zero.replace(minute=offset * 5, second=0, microsecond=0) + timedelta( + hours=-3 + ) imported_stats_5min = [ { @@ -209,22 +212,37 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): } for i in range(0, 39) ] - imported_stats = [ + imported_stats = [] + slice_end = 12 - offset + imported_stats.append( { - "start": imported_stats_5min[i * 12]["start"], - "max": max( - stat["max"] for stat in imported_stats_5min[i * 12 : (i + 1) * 12] - ), - "mean": fmean( - stat["mean"] for stat in imported_stats_5min[i * 12 : (i + 1) * 12] - ), - "min": min( - stat["min"] for stat in imported_stats_5min[i * 12 : (i + 1) * 12] - ), - "sum": imported_stats_5min[i * 12 + 11]["sum"], + "start": imported_stats_5min[0]["start"].replace(minute=0), + "max": max(stat["max"] for stat in imported_stats_5min[0:slice_end]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[0:slice_end]), + "min": min(stat["min"] for stat in imported_stats_5min[0:slice_end]), + "sum": imported_stats_5min[slice_end - 1]["sum"], } - for i in range(0, 3) - ] + ) + for i in range(0, 2): + slice_start = i * 12 + (12 - offset) + slice_end = (i + 1) * 12 + (12 - offset) + assert imported_stats_5min[slice_start]["start"].minute == 0 + imported_stats.append( + { + "start": imported_stats_5min[slice_start]["start"], + "max": max( + stat["max"] for stat in imported_stats_5min[slice_start:slice_end] + ), + "mean": fmean( + stat["mean"] for stat in imported_stats_5min[slice_start:slice_end] + ), + "min": min( + stat["min"] for stat in imported_stats_5min[slice_start:slice_end] + ), + "sum": imported_stats_5min[slice_end - 1]["sum"], + } + ) + imported_metadata = { "has_mean": False, "has_sum": True, @@ -285,8 +303,14 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): } # This should also include imported_statistics_5min[:] - start_time = "2022-10-21T04:00:00+00:00" - end_time = "2022-10-21T07:15:00+00:00" + start_time = ( + dt_util.parse_datetime("2022-10-21T04:00:00+00:00") + + timedelta(minutes=5 * offset) + ).isoformat() + end_time = ( + dt_util.parse_datetime("2022-10-21T07:15:00+00:00") + + timedelta(minutes=5 * offset) + ).isoformat() await client.send_json( { "id": next_id(), @@ -308,8 +332,14 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): } # This should also include imported_statistics_5min[:] - start_time = "2022-10-20T04:00:00+00:00" - end_time = "2022-10-21T08:20:00+00:00" + start_time = ( + dt_util.parse_datetime("2022-10-21T04:00:00+00:00") + + timedelta(minutes=5 * offset) + ).isoformat() + end_time = ( + dt_util.parse_datetime("2022-10-21T08:20:00+00:00") + + timedelta(minutes=5 * offset) + ).isoformat() await client.send_json( { "id": next_id(), @@ -331,7 +361,10 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): } # This should include imported_statistics_5min[26:] - start_time = "2022-10-21T06:10:00+00:00" + start_time = ( + dt_util.parse_datetime("2022-10-21T06:10:00+00:00") + + timedelta(minutes=5 * offset) + ).isoformat() assert imported_stats_5min[26]["start"].isoformat() == start_time await client.send_json( { @@ -353,7 +386,10 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): } # This should also include imported_statistics_5min[26:] - start_time = "2022-10-21T06:09:00+00:00" + start_time = ( + dt_util.parse_datetime("2022-10-21T06:09:00+00:00") + + timedelta(minutes=5 * offset) + ).isoformat() await client.send_json( { "id": next_id(), @@ -374,7 +410,10 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): } # This should include imported_statistics_5min[:26] - end_time = "2022-10-21T06:10:00+00:00" + end_time = ( + dt_util.parse_datetime("2022-10-21T06:10:00+00:00") + + timedelta(minutes=5 * offset) + ).isoformat() assert imported_stats_5min[26]["start"].isoformat() == end_time await client.send_json( { @@ -396,9 +435,15 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): } # This should include imported_statistics_5min[26:32] (less than a full hour) - start_time = "2022-10-21T06:10:00+00:00" + start_time = ( + dt_util.parse_datetime("2022-10-21T06:10:00+00:00") + + timedelta(minutes=5 * offset) + ).isoformat() assert imported_stats_5min[26]["start"].isoformat() == start_time - end_time = "2022-10-21T06:40:00+00:00" + end_time = ( + dt_util.parse_datetime("2022-10-21T06:40:00+00:00") + + timedelta(minutes=5 * offset) + ).isoformat() assert imported_stats_5min[32]["start"].isoformat() == end_time await client.send_json( { @@ -422,7 +467,7 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): # This should include imported_statistics[2:] + imported_statistics_5min[36:] start_time = "2022-10-21T06:00:00+00:00" - assert imported_stats_5min[24]["start"].isoformat() == start_time + assert imported_stats_5min[24 - offset]["start"].isoformat() == start_time assert imported_stats[2]["start"].isoformat() == start_time await client.send_json( { @@ -437,10 +482,11 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): response = await client.receive_json() assert response["success"] assert response["result"] == { - "max": max(stat["max"] for stat in imported_stats_5min[24:]), - "mean": fmean(stat["mean"] for stat in imported_stats_5min[24:]), - "min": min(stat["min"] for stat in imported_stats_5min[24:]), - "change": imported_stats_5min[-1]["sum"] - imported_stats_5min[23]["sum"], + "max": max(stat["max"] for stat in imported_stats_5min[24 - offset :]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[24 - offset :]), + "min": min(stat["min"] for stat in imported_stats_5min[24 - offset :]), + "change": imported_stats_5min[-1]["sum"] + - imported_stats_5min[23 - offset]["sum"], } # This should also include imported_statistics[2:] + imported_statistics_5min[36:] @@ -457,10 +503,11 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): response = await client.receive_json() assert response["success"] assert response["result"] == { - "max": max(stat["max"] for stat in imported_stats_5min[24:]), - "mean": fmean(stat["mean"] for stat in imported_stats_5min[24:]), - "min": min(stat["min"] for stat in imported_stats_5min[24:]), - "change": imported_stats_5min[-1]["sum"] - imported_stats_5min[23]["sum"], + "max": max(stat["max"] for stat in imported_stats_5min[24 - offset :]), + "mean": fmean(stat["mean"] for stat in imported_stats_5min[24 - offset :]), + "min": min(stat["min"] for stat in imported_stats_5min[24 - offset :]), + "change": imported_stats_5min[-1]["sum"] + - imported_stats_5min[23 - offset]["sum"], } # This should include imported_statistics[2:3] @@ -477,11 +524,16 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): ) response = await client.receive_json() assert response["success"] + slice_start = 24 - offset + slice_end = 36 - offset assert response["result"] == { - "max": max(stat["max"] for stat in imported_stats_5min[24:36]), - "mean": fmean(stat["mean"] for stat in imported_stats_5min[24:36]), - "min": min(stat["min"] for stat in imported_stats_5min[24:36]), - "change": imported_stats_5min[35]["sum"] - imported_stats_5min[23]["sum"], + "max": max(stat["max"] for stat in imported_stats_5min[slice_start:slice_end]), + "mean": fmean( + stat["mean"] for stat in imported_stats_5min[slice_start:slice_end] + ), + "min": min(stat["min"] for stat in imported_stats_5min[slice_start:slice_end]), + "change": imported_stats_5min[slice_end - 1]["sum"] + - imported_stats_5min[slice_start - 1]["sum"], } # Test we can get only selected types @@ -539,6 +591,167 @@ async def test_statistic_during_period(recorder_mock, hass, hass_ws_client): } +@freeze_time(datetime.datetime(2022, 10, 21, 7, 25, tzinfo=datetime.timezone.utc)) +async def test_statistic_during_period_hole(recorder_mock, hass, hass_ws_client): + """Test statistic_during_period when there are holes in the data.""" + id = 1 + + def next_id(): + nonlocal id + id += 1 + return id + + now = dt_util.utcnow() + + await async_recorder_block_till_done(hass) + client = await hass_ws_client() + + zero = now + start = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=-18) + + imported_stats = [ + { + "start": (start + timedelta(hours=3 * i)), + "max": i * 2, + "mean": i, + "min": -76 + i * 2, + "sum": i, + } + for i in range(0, 6) + ] + + imported_metadata = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "recorder", + "statistic_id": "sensor.test", + "unit_of_measurement": "kWh", + } + + recorder.get_instance(hass).async_import_statistics( + imported_metadata, + imported_stats, + Statistics, + ) + await async_wait_recording_done(hass) + + # This should include imported_stats[:] + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "statistic_id": "sensor.test", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats[:]), + "mean": fmean(stat["mean"] for stat in imported_stats[:]), + "min": min(stat["min"] for stat in imported_stats[:]), + "change": imported_stats[-1]["sum"] - imported_stats[0]["sum"], + } + + # This should also include imported_stats[:] + start_time = "2022-10-20T13:00:00+00:00" + end_time = "2022-10-21T05:00:00+00:00" + assert imported_stats[0]["start"].isoformat() == start_time + assert imported_stats[-1]["start"].isoformat() < end_time + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "statistic_id": "sensor.test", + "fixed_period": { + "start_time": start_time, + "end_time": end_time, + }, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats[:]), + "mean": fmean(stat["mean"] for stat in imported_stats[:]), + "min": min(stat["min"] for stat in imported_stats[:]), + "change": imported_stats[-1]["sum"] - imported_stats[0]["sum"], + } + + # This should also include imported_stats[:] + start_time = "2022-10-20T13:00:00+00:00" + end_time = "2022-10-21T08:20:00+00:00" + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "statistic_id": "sensor.test", + "fixed_period": { + "start_time": start_time, + "end_time": end_time, + }, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats[:]), + "mean": fmean(stat["mean"] for stat in imported_stats[:]), + "min": min(stat["min"] for stat in imported_stats[:]), + "change": imported_stats[-1]["sum"] - imported_stats[0]["sum"], + } + + # This should include imported_stats[1:4] + start_time = "2022-10-20T16:00:00+00:00" + end_time = "2022-10-20T23:00:00+00:00" + assert imported_stats[1]["start"].isoformat() == start_time + assert imported_stats[3]["start"].isoformat() < end_time + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "statistic_id": "sensor.test", + "fixed_period": { + "start_time": start_time, + "end_time": end_time, + }, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats[1:4]), + "mean": fmean(stat["mean"] for stat in imported_stats[1:4]), + "min": min(stat["min"] for stat in imported_stats[1:4]), + "change": imported_stats[3]["sum"] - imported_stats[1]["sum"], + } + + # This should also include imported_stats[1:4] + start_time = "2022-10-20T15:00:00+00:00" + end_time = "2022-10-21T00:00:00+00:00" + assert imported_stats[1]["start"].isoformat() > start_time + assert imported_stats[3]["start"].isoformat() < end_time + await client.send_json( + { + "id": next_id(), + "type": "recorder/statistic_during_period", + "statistic_id": "sensor.test", + "fixed_period": { + "start_time": start_time, + "end_time": end_time, + }, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "max": max(stat["max"] for stat in imported_stats[1:4]), + "mean": fmean(stat["mean"] for stat in imported_stats[1:4]), + "min": min(stat["min"] for stat in imported_stats[1:4]), + "change": imported_stats[3]["sum"] - imported_stats[1]["sum"], + } + + @freeze_time(datetime.datetime(2022, 10, 21, 7, 25, tzinfo=datetime.timezone.utc)) @pytest.mark.parametrize( "calendar_period, start_time, end_time", From af6338343e9a87bb51121e49e5a8206a058fd852 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 16 Nov 2022 12:49:10 +0100 Subject: [PATCH 0458/1033] Remove (2021) backwards supporting functionality from UniFi (#81981) --- homeassistant/components/unifi/__init__.py | 48 ++++------------------ tests/components/unifi/test_init.py | 23 ++--------- 2 files changed, 11 insertions(+), 60 deletions(-) diff --git a/homeassistant/components/unifi/__init__.py b/homeassistant/components/unifi/__init__.py index e37e89b3da5..adaa7c977f7 100644 --- a/homeassistant/components/unifi/__init__.py +++ b/homeassistant/components/unifi/__init__.py @@ -1,6 +1,4 @@ """Integration to UniFi Network and its various features.""" -from collections.abc import Mapping -from typing import Any from homeassistant.config_entries import ConfigEntry from homeassistant.const import EVENT_HOMEASSISTANT_STOP @@ -10,12 +8,7 @@ from homeassistant.helpers import entity_registry as er from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType -from .const import ( - CONF_CONTROLLER, - DOMAIN as UNIFI_DOMAIN, - PLATFORMS, - UNIFI_WIRELESS_CLIENTS, -) +from .const import DOMAIN as UNIFI_DOMAIN, PLATFORMS, UNIFI_WIRELESS_CLIENTS from .controller import UniFiController, get_unifi_controller from .errors import AuthenticationRequired, CannotConnect from .services import async_setup_services, async_unload_services @@ -40,9 +33,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b # Removal of legacy PoE control was introduced with 2022.12 async_remove_poe_client_entities(hass, config_entry) - # Flat configuration was introduced with 2021.3 - await async_flatten_entry_data(hass, config_entry) - try: api = await get_unifi_controller(hass, config_entry.data) controller = UniFiController(hass, config_entry, api) @@ -54,12 +44,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b except AuthenticationRequired as err: raise ConfigEntryAuthFailed from err - # Unique ID was introduced with 2021.3 - if config_entry.unique_id is None: - hass.config_entries.async_update_entry( - config_entry, unique_id=controller.site_id - ) - hass.data[UNIFI_DOMAIN][config_entry.entry_id] = controller await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) await controller.async_update_device_registry() @@ -104,52 +88,36 @@ def async_remove_poe_client_entities( ent_reg.async_remove(entity_id) -async def async_flatten_entry_data( - hass: HomeAssistant, config_entry: ConfigEntry -) -> None: - """Simpler configuration structure for entry data. - - Keep controller key layer in case user rollbacks. - """ - - data: Mapping[str, Any] = { - **config_entry.data, - **config_entry.data[CONF_CONTROLLER], - } - if config_entry.data != data: - hass.config_entries.async_update_entry(config_entry, data=data) - - class UnifiWirelessClients: """Class to store clients known to be wireless. This is needed since wireless devices going offline might get marked as wired by UniFi. """ - def __init__(self, hass): + def __init__(self, hass: HomeAssistant) -> None: """Set up client storage.""" self.hass = hass - self.data = {} - self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY) + self.data: dict[str, dict[str, list[str]]] = {} + self._store: Store = Store(hass, STORAGE_VERSION, STORAGE_KEY) - async def async_load(self): + async def async_load(self) -> None: """Load data from file.""" if (data := await self._store.async_load()) is not None: self.data = data @callback - def get_data(self, config_entry): + def get_data(self, config_entry: ConfigEntry) -> set[str]: """Get data related to a specific controller.""" data = self.data.get(config_entry.entry_id, {"wireless_devices": []}) return set(data["wireless_devices"]) @callback - def update_data(self, data, config_entry): + def update_data(self, data: set[str], config_entry: ConfigEntry) -> None: """Update data and schedule to save to file.""" self.data[config_entry.entry_id] = {"wireless_devices": list(data)} self._store.async_delay_save(self._data_to_save, SAVE_DELAY) @callback - def _data_to_save(self): + def _data_to_save(self) -> dict[str, dict[str, list[str]]]: """Return data of UniFi wireless clients to store in a file.""" return self.data diff --git a/tests/components/unifi/test_init.py b/tests/components/unifi/test_init.py index 03ea89097c5..1362f1dc0d5 100644 --- a/tests/components/unifi/test_init.py +++ b/tests/components/unifi/test_init.py @@ -2,19 +2,13 @@ from unittest.mock import patch from homeassistant.components import unifi -from homeassistant.components.unifi import async_flatten_entry_data -from homeassistant.components.unifi.const import CONF_CONTROLLER, DOMAIN as UNIFI_DOMAIN +from homeassistant.components.unifi.const import DOMAIN as UNIFI_DOMAIN from homeassistant.components.unifi.errors import AuthenticationRequired, CannotConnect from homeassistant.setup import async_setup_component -from .test_controller import ( - CONTROLLER_DATA, - DEFAULT_CONFIG_ENTRY_ID, - ENTRY_CONFIG, - setup_unifi_integration, -) +from .test_controller import DEFAULT_CONFIG_ENTRY_ID, setup_unifi_integration -from tests.common import MockConfigEntry, flush_store +from tests.common import flush_store async def test_setup_with_no_config(hass): @@ -52,17 +46,6 @@ async def test_setup_entry_fails_trigger_reauth_flow(hass): assert hass.data[UNIFI_DOMAIN] == {} -async def test_flatten_entry_data(hass): - """Verify entry data can be flattened.""" - entry = MockConfigEntry( - domain=UNIFI_DOMAIN, - data={CONF_CONTROLLER: CONTROLLER_DATA}, - ) - await async_flatten_entry_data(hass, entry) - - assert entry.data == ENTRY_CONFIG - - async def test_unload_entry(hass, aioclient_mock): """Test being able to unload an entry.""" config_entry = await setup_unifi_integration(hass, aioclient_mock) From 7999f109d180664ea2f415b0db879a101bb21b11 Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Wed, 16 Nov 2022 11:51:14 +0000 Subject: [PATCH 0459/1033] Fix ONVIF subscription errors (#81965) fixes undefined --- homeassistant/components/onvif/event.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/onvif/event.py b/homeassistant/components/onvif/event.py index 3801d8081db..2dd5d226e37 100644 --- a/homeassistant/components/onvif/event.py +++ b/homeassistant/components/onvif/event.py @@ -8,7 +8,7 @@ import datetime as dt from httpx import RemoteProtocolError, TransportError from onvif import ONVIFCamera, ONVIFService -from zeep.exceptions import Fault +from zeep.exceptions import Fault, XMLParseError from homeassistant.core import CALLBACK_TYPE, CoreState, HomeAssistant, callback from homeassistant.helpers.event import async_call_later @@ -20,6 +20,7 @@ from .parsers import PARSERS UNHANDLED_TOPICS = set() SUBSCRIPTION_ERRORS = ( + XMLParseError, Fault, asyncio.TimeoutError, TransportError, @@ -153,7 +154,8 @@ class EventManager: .isoformat(timespec="seconds") .replace("+00:00", "Z") ) - await self._subscription.Renew(termination_time) + with suppress(*SUBSCRIPTION_ERRORS): + await self._subscription.Renew(termination_time) def async_schedule_pull(self) -> None: """Schedule async_pull_messages to run.""" From 1582d8895731e8d3f064936e666095243aac581b Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 16 Nov 2022 12:54:03 +0100 Subject: [PATCH 0460/1033] Remove deprecated history WS API (#82136) --- homeassistant/components/history/__init__.py | 47 +------------ tests/components/history/test_init.py | 74 -------------------- 2 files changed, 1 insertion(+), 120 deletions(-) diff --git a/homeassistant/components/history/__init__.py b/homeassistant/components/history/__init__.py index 165ef72d525..f07fe82b50d 100644 --- a/homeassistant/components/history/__init__.py +++ b/homeassistant/components/history/__init__.py @@ -13,11 +13,7 @@ import voluptuous as vol from homeassistant.components import frontend, websocket_api from homeassistant.components.http import HomeAssistantView -from homeassistant.components.recorder import ( - get_instance, - history, - websocket_api as recorder_ws, -) +from homeassistant.components.recorder import get_instance, history from homeassistant.components.recorder.filters import ( Filters, sqlalchemy_filter_from_include_exclude_conf, @@ -61,52 +57,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass.http.register_view(HistoryPeriodView(filters, use_include_order)) frontend.async_register_built_in_panel(hass, "history", "history", "hass:chart-box") - websocket_api.async_register_command(hass, ws_get_statistics_during_period) - websocket_api.async_register_command(hass, ws_get_list_statistic_ids) websocket_api.async_register_command(hass, ws_get_history_during_period) return True -@websocket_api.websocket_command( - { - vol.Required("type"): "history/statistics_during_period", - vol.Required("start_time"): str, - vol.Optional("end_time"): str, - vol.Optional("statistic_ids"): [str], - vol.Required("period"): vol.Any("5minute", "hour", "day", "month"), - } -) -@websocket_api.async_response -async def ws_get_statistics_during_period( - hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any] -) -> None: - """Handle statistics websocket command.""" - _LOGGER.warning( - "WS API 'history/statistics_during_period' is deprecated and will be removed in " - "Home Assistant Core 2022.12. Use 'recorder/statistics_during_period' instead" - ) - await recorder_ws.ws_handle_get_statistics_during_period(hass, connection, msg) - - -@websocket_api.websocket_command( - { - vol.Required("type"): "history/list_statistic_ids", - vol.Optional("statistic_type"): vol.Any("sum", "mean"), - } -) -@websocket_api.async_response -async def ws_get_list_statistic_ids( - hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any] -) -> None: - """Fetch a list of available statistic_id.""" - _LOGGER.warning( - "WS API 'history/list_statistic_ids' is deprecated and will be removed in " - "Home Assistant Core 2022.12. Use 'recorder/list_statistic_ids' instead" - ) - await recorder_ws.ws_handle_list_statistic_ids(hass, connection, msg) - - def _ws_get_significant_states( hass: HomeAssistant, msg_id: int, diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index 981ff3bc08d..092c589035d 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -10,10 +10,6 @@ import pytest from homeassistant.components import history from homeassistant.components.recorder.history import get_significant_states from homeassistant.components.recorder.models import process_timestamp -from homeassistant.components.recorder.websocket_api import ( - ws_handle_get_statistics_during_period, - ws_handle_list_statistic_ids, -) from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE import homeassistant.core as ha from homeassistant.helpers.json import JSONEncoder @@ -844,76 +840,6 @@ async def test_entity_ids_limit_via_api_with_skip_initial_state( assert response_json[1][0]["entity_id"] == "light.cow" -async def test_statistics_during_period(recorder_mock, hass, hass_ws_client, caplog): - """Test history/statistics_during_period forwards to recorder.""" - now = dt_util.utcnow() - await async_setup_component(hass, "history", {}) - client = await hass_ws_client() - - # Test the WS API works and issues a warning - await client.send_json( - { - "id": 1, - "type": "history/statistics_during_period", - "start_time": now.isoformat(), - "end_time": now.isoformat(), - "statistic_ids": ["sensor.test"], - "period": "hour", - } - ) - response = await client.receive_json() - assert response["success"] - assert response["result"] == {} - - assert ( - "WS API 'history/statistics_during_period' is deprecated and will be removed in " - "Home Assistant Core 2022.12. Use 'recorder/statistics_during_period' instead" - ) in caplog.text - - # Test the WS API forwards to recorder - with patch( - "homeassistant.components.history.recorder_ws.ws_handle_get_statistics_during_period", - wraps=ws_handle_get_statistics_during_period, - ) as ws_mock: - await client.send_json( - { - "id": 2, - "type": "history/statistics_during_period", - "start_time": now.isoformat(), - "end_time": now.isoformat(), - "statistic_ids": ["sensor.test"], - "period": "hour", - } - ) - await client.receive_json() - ws_mock.assert_awaited_once() - - -async def test_list_statistic_ids(recorder_mock, hass, hass_ws_client, caplog): - """Test history/list_statistic_ids forwards to recorder.""" - await async_setup_component(hass, "history", {}) - client = await hass_ws_client() - - # Test the WS API works and issues a warning - await client.send_json({"id": 1, "type": "history/list_statistic_ids"}) - response = await client.receive_json() - assert response["success"] - assert response["result"] == [] - - assert ( - "WS API 'history/list_statistic_ids' is deprecated and will be removed in " - "Home Assistant Core 2022.12. Use 'recorder/list_statistic_ids' instead" - ) in caplog.text - - with patch( - "homeassistant.components.history.recorder_ws.ws_handle_list_statistic_ids", - wraps=ws_handle_list_statistic_ids, - ) as ws_mock: - await client.send_json({"id": 2, "type": "history/list_statistic_ids"}) - await client.receive_json() - ws_mock.assert_called_once() - - async def test_history_during_period(recorder_mock, hass, hass_ws_client): """Test history_during_period.""" now = dt_util.utcnow() From 053815476767c196c83c0652e79f7d5f9716f573 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 16 Nov 2022 13:00:35 +0100 Subject: [PATCH 0461/1033] Add type hints to requirements script (#82075) --- script/gen_requirements_all.py | 70 +++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index bbc970f9178..264d9ff9f8a 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -1,5 +1,7 @@ #!/usr/bin/env python3 -"""Generate an updated requirements_all.txt.""" +"""Generate updated constraint and requirements files.""" +from __future__ import annotations + import difflib import importlib import os @@ -7,6 +9,7 @@ from pathlib import Path import pkgutil import re import sys +from typing import Any from homeassistant.util.yaml.loader import load_yaml from script.hassfest.model import Integration @@ -157,7 +160,7 @@ IGNORE_PRE_COMMIT_HOOK_ID = ( PACKAGE_REGEX = re.compile(r"^(?:--.+\s)?([-_\.\w\d]+).*==.+$") -def has_tests(module: str): +def has_tests(module: str) -> bool: """Test if a module has tests. Module format: homeassistant.components.hue @@ -169,11 +172,11 @@ def has_tests(module: str): return path.exists() -def explore_module(package, explore_children): +def explore_module(package: str, explore_children: bool) -> list[str]: """Explore the modules.""" module = importlib.import_module(package) - found = [] + found: list[str] = [] if not hasattr(module, "__path__"): return found @@ -187,14 +190,17 @@ def explore_module(package, explore_children): return found -def core_requirements(): +def core_requirements() -> list[str]: """Gather core requirements out of pyproject.toml.""" with open("pyproject.toml", "rb") as fp: data = tomllib.load(fp) - return data["project"]["dependencies"] + dependencies: list[str] = data["project"]["dependencies"] + return dependencies -def gather_recursive_requirements(domain, seen=None): +def gather_recursive_requirements( + domain: str, seen: set[str] | None = None +) -> set[str]: """Recursively gather requirements from a module.""" if seen is None: seen = set() @@ -221,18 +227,18 @@ def normalize_package_name(requirement: str) -> str: return package -def comment_requirement(req): +def comment_requirement(req: str) -> bool: """Comment out requirement. Some don't install on all systems.""" return any( normalize_package_name(req) == ign for ign in COMMENT_REQUIREMENTS_NORMALIZED ) -def gather_modules(): +def gather_modules() -> dict[str, list[str]] | None: """Collect the information.""" - reqs = {} + reqs: dict[str, list[str]] = {} - errors = [] + errors: list[str] = [] gather_requirements_from_manifests(errors, reqs) gather_requirements_from_modules(errors, reqs) @@ -248,7 +254,9 @@ def gather_modules(): return reqs -def gather_requirements_from_manifests(errors, reqs): +def gather_requirements_from_manifests( + errors: list[str], reqs: dict[str, list[str]] +) -> None: """Gather all of the requirements from manifests.""" integrations = Integration.load_dir(Path("homeassistant/components")) for domain in sorted(integrations): @@ -266,7 +274,9 @@ def gather_requirements_from_manifests(errors, reqs): ) -def gather_requirements_from_modules(errors, reqs): +def gather_requirements_from_modules( + errors: list[str], reqs: dict[str, list[str]] +) -> None: """Collect the requirements from the modules directly.""" for package in sorted( explore_module("homeassistant.scripts", True) @@ -283,7 +293,12 @@ def gather_requirements_from_modules(errors, reqs): process_requirements(errors, module.REQUIREMENTS, package, reqs) -def process_requirements(errors, module_requirements, package, reqs): +def process_requirements( + errors: list[str], + module_requirements: list[str], + package: str, + reqs: dict[str, list[str]], +) -> None: """Process all of the requirements.""" for req in module_requirements: if "://" in req: @@ -293,7 +308,7 @@ def process_requirements(errors, module_requirements, package, reqs): reqs.setdefault(req, []).append(package) -def generate_requirements_list(reqs): +def generate_requirements_list(reqs: dict[str, list[str]]) -> str: """Generate a pip file based on requirements.""" output = [] for pkg, requirements in sorted(reqs.items(), key=lambda item: item[0]): @@ -307,7 +322,7 @@ def generate_requirements_list(reqs): return "".join(output) -def requirements_output(reqs): +def requirements_output() -> str: """Generate output for requirements.""" output = [ "-c homeassistant/package_constraints.txt\n", @@ -320,7 +335,7 @@ def requirements_output(reqs): return "".join(output) -def requirements_all_output(reqs): +def requirements_all_output(reqs: dict[str, list[str]]) -> str: """Generate output for requirements_all.""" output = [ "# Home Assistant Core, full dependency set\n", @@ -331,7 +346,7 @@ def requirements_all_output(reqs): return "".join(output) -def requirements_test_all_output(reqs): +def requirements_test_all_output(reqs: dict[str, list[str]]) -> str: """Generate output for test_requirements.""" output = [ "# Home Assistant tests, full dependency set\n", @@ -356,15 +371,18 @@ def requirements_test_all_output(reqs): return "".join(output) -def requirements_pre_commit_output(): +def requirements_pre_commit_output() -> str: """Generate output for pre-commit dependencies.""" source = ".pre-commit-config.yaml" - pre_commit_conf = load_yaml(source) - reqs = [] + pre_commit_conf: dict[str, list[dict[str, Any]]] + pre_commit_conf = load_yaml(source) # type: ignore[assignment] + reqs: list[str] = [] + hook: dict[str, Any] for repo in (x for x in pre_commit_conf["repos"] if x.get("rev")): + rev: str = repo["rev"] for hook in repo["hooks"]: if hook["id"] not in IGNORE_PRE_COMMIT_HOOK_ID: - reqs.append(f"{hook['id']}=={repo['rev'].lstrip('v')}") + reqs.append(f"{hook['id']}=={rev.lstrip('v')}") reqs.extend(x for x in hook.get("additional_dependencies", ())) output = [ f"# Automatically generated " @@ -375,7 +393,7 @@ def requirements_pre_commit_output(): return "\n".join(output) + "\n" -def gather_constraints(): +def gather_constraints() -> str: """Construct output for constraint file.""" return ( "\n".join( @@ -392,7 +410,7 @@ def gather_constraints(): ) -def diff_file(filename, content): +def diff_file(filename: str, content: str) -> list[str]: """Diff a file.""" return list( difflib.context_diff( @@ -404,7 +422,7 @@ def diff_file(filename, content): ) -def main(validate): +def main(validate: bool) -> int: """Run the script.""" if not os.path.isfile("requirements_all.txt"): print("Run this from HA root dir") @@ -415,7 +433,7 @@ def main(validate): if data is None: return 1 - reqs_file = requirements_output(data) + reqs_file = requirements_output() reqs_all_file = requirements_all_output(data) reqs_test_all_file = requirements_test_all_output(data) reqs_pre_commit_file = requirements_pre_commit_output() From 3695f39515a078bf06c14b528b77cb476093ed2c Mon Sep 17 00:00:00 2001 From: Owen Johnson Date: Wed, 16 Nov 2022 06:02:26 -0600 Subject: [PATCH 0462/1033] Correctly Map Heavy Rain and Thunderstorms (#80926) --- homeassistant/components/tomorrowio/const.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/tomorrowio/const.py b/homeassistant/components/tomorrowio/const.py index a6af4ef5819..4b1e2487da8 100644 --- a/homeassistant/components/tomorrowio/const.py +++ b/homeassistant/components/tomorrowio/const.py @@ -10,7 +10,7 @@ from homeassistant.components.weather import ( ATTR_CONDITION_CLOUDY, ATTR_CONDITION_FOG, ATTR_CONDITION_HAIL, - ATTR_CONDITION_LIGHTNING, + ATTR_CONDITION_LIGHTNING_RAINY, ATTR_CONDITION_PARTLYCLOUDY, ATTR_CONDITION_POURING, ATTR_CONDITION_RAINY, @@ -63,9 +63,9 @@ CONDITIONS = { WeatherCode.HEAVY_SNOW: ATTR_CONDITION_SNOWY, WeatherCode.LIGHT_SNOW: ATTR_CONDITION_SNOWY, WeatherCode.FLURRIES: ATTR_CONDITION_SNOWY, - WeatherCode.THUNDERSTORM: ATTR_CONDITION_LIGHTNING, - WeatherCode.RAIN: ATTR_CONDITION_POURING, - WeatherCode.HEAVY_RAIN: ATTR_CONDITION_RAINY, + WeatherCode.THUNDERSTORM: ATTR_CONDITION_LIGHTNING_RAINY, + WeatherCode.HEAVY_RAIN: ATTR_CONDITION_POURING, + WeatherCode.RAIN: ATTR_CONDITION_RAINY, WeatherCode.LIGHT_RAIN: ATTR_CONDITION_RAINY, WeatherCode.DRIZZLE: ATTR_CONDITION_RAINY, WeatherCode.FOG: ATTR_CONDITION_FOG, From d38bae76bec543c2bf71a59341e7b6a39b12240a Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Wed, 16 Nov 2022 07:04:34 -0500 Subject: [PATCH 0463/1033] Add missing strings in Onvif (#82141) --- homeassistant/components/onvif/strings.json | 3 ++- homeassistant/components/onvif/translations/en.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/onvif/strings.json b/homeassistant/components/onvif/strings.json index 4cf1bd4bad0..210027e96e5 100644 --- a/homeassistant/components/onvif/strings.json +++ b/homeassistant/components/onvif/strings.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "Extra FFMPEG arguments", - "rtsp_transport": "RTSP transport mechanism" + "rtsp_transport": "RTSP transport mechanism", + "use_wallclock_as_timestamps": "Use wall clock as timestamps" }, "title": "ONVIF Device Options" } diff --git a/homeassistant/components/onvif/translations/en.json b/homeassistant/components/onvif/translations/en.json index c3b328646ee..473e2af9ba8 100644 --- a/homeassistant/components/onvif/translations/en.json +++ b/homeassistant/components/onvif/translations/en.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "Extra FFMPEG arguments", - "rtsp_transport": "RTSP transport mechanism" + "rtsp_transport": "RTSP transport mechanism", + "use_wallclock_as_timestamps": "Use wall clock as timestamps" }, "title": "ONVIF Device Options" } From 322cb352ac21dd5de9d14786dbdca282e7ae80ac Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 16 Nov 2022 14:13:45 +0200 Subject: [PATCH 0464/1033] Use functools.cache instead of global in hassfest requirements (#82124) --- script/hassfest/requirements.py | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/script/hassfest/requirements.py b/script/hassfest/requirements.py index 510a70f30ce..b8fb22fb786 100644 --- a/script/hassfest/requirements.py +++ b/script/hassfest/requirements.py @@ -2,6 +2,7 @@ from __future__ import annotations from collections import deque +from functools import cache import json import os import re @@ -36,7 +37,6 @@ SUPPORTED_PYTHON_VERSIONS = [ ".".join(map(str, version_tuple)) for version_tuple in SUPPORTED_PYTHON_TUPLES ] STD_LIBS = {version: set(stdlib_list(version)) for version in SUPPORTED_PYTHON_VERSIONS} -PIPDEPTREE_CACHE = None IGNORE_VIOLATIONS = { # Still has standard library requirements. @@ -60,8 +60,6 @@ def validate(integrations: dict[str, Integration], config: Config): validate_requirements_format(integration) return - ensure_cache() - # check for incompatible requirements disable_tqdm = config.specific_integrations or os.environ.get("CI", False) @@ -167,8 +165,9 @@ def validate_requirements(integration: Integration): ) -def ensure_cache(): - """Ensure we have a cache of pipdeptree. +@cache +def get_pipdeptree(): + """Get pipdeptree output. Cached on first invocation. { "flake8-docstring": { @@ -179,12 +178,7 @@ def ensure_cache(): } } """ - global PIPDEPTREE_CACHE - - if PIPDEPTREE_CACHE is not None: - return - - cache = {} + deptree = {} for item in json.loads( subprocess.run( @@ -194,17 +188,16 @@ def ensure_cache(): text=True, ).stdout ): - cache[item["package"]["key"]] = { + deptree[item["package"]["key"]] = { **item["package"], "dependencies": {dep["key"] for dep in item["dependencies"]}, } - - PIPDEPTREE_CACHE = cache + return deptree def get_requirements(integration: Integration, packages: set[str]) -> set[str]: """Return all (recursively) requirements for an integration.""" - ensure_cache() + deptree = get_pipdeptree() all_requirements = set() @@ -218,7 +211,7 @@ def get_requirements(integration: Integration, packages: set[str]) -> set[str]: all_requirements.add(package) - item = PIPDEPTREE_CACHE.get(package) + item = deptree.get(package) if item is None: # Only warn if direct dependencies could not be resolved @@ -238,9 +231,7 @@ def install_requirements(integration: Integration, requirements: set[str]) -> bo Return True if successful. """ - global PIPDEPTREE_CACHE - - ensure_cache() + deptree = get_pipdeptree() for req in requirements: match = PIP_REGEX.search(req) @@ -261,7 +252,7 @@ def install_requirements(integration: Integration, requirements: set[str]) -> bo if normalized and "==" in requirement_arg: ver = requirement_arg.split("==")[-1] - item = PIPDEPTREE_CACHE.get(normalized) + item = deptree.get(normalized) is_installed = item and item["installed_version"] == ver if not is_installed: @@ -287,7 +278,7 @@ def install_requirements(integration: Integration, requirements: set[str]) -> bo else: # Clear the pipdeptree cache if something got installed if "Successfully installed" in result.stdout: - PIPDEPTREE_CACHE = None + get_pipdeptree.cache_clear() if integration.errors: return False From 90985922570972d263ebfb5bdb6302d0f28aa98f Mon Sep 17 00:00:00 2001 From: Yukon Vinecki Date: Wed, 16 Nov 2022 04:14:14 -0800 Subject: [PATCH 0465/1033] Fix Z-Wave JS cover stop support (#78723) Co-authored-by: Franck Nijhof --- homeassistant/components/zwave_js/cover.py | 20 ++------- tests/components/zwave_js/test_cover.py | 48 ++-------------------- 2 files changed, 8 insertions(+), 60 deletions(-) diff --git a/homeassistant/components/zwave_js/cover.py b/homeassistant/components/zwave_js/cover.py index b3f3aeaf1c0..43b51048de4 100644 --- a/homeassistant/components/zwave_js/cover.py +++ b/homeassistant/components/zwave_js/cover.py @@ -7,9 +7,6 @@ from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.const import TARGET_STATE_PROPERTY, TARGET_VALUE_PROPERTY from zwave_js_server.const.command_class.barrier_operator import BarrierState from zwave_js_server.const.command_class.multilevel_switch import ( - COVER_CLOSE_PROPERTY, - COVER_DOWN_PROPERTY, - COVER_OFF_PROPERTY, COVER_ON_PROPERTY, COVER_OPEN_PROPERTY, COVER_UP_PROPERTY, @@ -156,23 +153,14 @@ class ZWaveCover(ZWaveBaseEntity, CoverEntity): async def async_stop_cover(self, **kwargs: Any) -> None: """Stop cover.""" - open_value = ( + cover_property = ( self.get_zwave_value(COVER_OPEN_PROPERTY) or self.get_zwave_value(COVER_UP_PROPERTY) or self.get_zwave_value(COVER_ON_PROPERTY) ) - if open_value: - # Stop the cover if it's opening - await self.info.node.async_set_value(open_value, False) - - close_value = ( - self.get_zwave_value(COVER_CLOSE_PROPERTY) - or self.get_zwave_value(COVER_DOWN_PROPERTY) - or self.get_zwave_value(COVER_OFF_PROPERTY) - ) - if close_value: - # Stop the cover if it's closing - await self.info.node.async_set_value(close_value, False) + if cover_property: + # Stop the cover, will stop regardless of the actual direction of travel. + await self.info.node.async_set_value(cover_property, False) class ZWaveTiltCover(ZWaveCover): diff --git a/tests/components/zwave_js/test_cover.py b/tests/components/zwave_js/test_cover.py index f26b0d29069..0ca2e36d853 100644 --- a/tests/components/zwave_js/test_cover.py +++ b/tests/components/zwave_js/test_cover.py @@ -116,7 +116,7 @@ async def test_window_cover(hass, client, chain_actuator_zws12, integration): blocking=True, ) - assert len(client.async_send_command.call_args_list) == 2 + assert len(client.async_send_command.call_args_list) == 1 open_args = client.async_send_command.call_args_list[0][0][0] assert open_args["command"] == "node.set_value" assert open_args["nodeId"] == 6 @@ -127,16 +127,6 @@ async def test_window_cover(hass, client, chain_actuator_zws12, integration): } assert not open_args["value"] - close_args = client.async_send_command.call_args_list[1][0][0] - assert close_args["command"] == "node.set_value" - assert close_args["nodeId"] == 6 - assert close_args["valueId"] == { - "commandClass": 38, - "endpoint": 0, - "property": "Close", - } - assert not close_args["value"] - # Test position update from value updated event event = Event( type="value updated", @@ -189,7 +179,7 @@ async def test_window_cover(hass, client, chain_actuator_zws12, integration): blocking=True, ) - assert len(client.async_send_command.call_args_list) == 2 + assert len(client.async_send_command.call_args_list) == 1 open_args = client.async_send_command.call_args_list[0][0][0] assert open_args["command"] == "node.set_value" assert open_args["nodeId"] == 6 @@ -200,16 +190,6 @@ async def test_window_cover(hass, client, chain_actuator_zws12, integration): } assert not open_args["value"] - close_args = client.async_send_command.call_args_list[1][0][0] - assert close_args["command"] == "node.set_value" - assert close_args["nodeId"] == 6 - assert close_args["valueId"] == { - "commandClass": 38, - "endpoint": 0, - "property": "Close", - } - assert not close_args["value"] - client.async_send_command.reset_mock() event = Event( @@ -329,7 +309,7 @@ async def test_aeotec_nano_shutter_cover( blocking=True, ) - assert len(client.async_send_command.call_args_list) == 2 + assert len(client.async_send_command.call_args_list) == 1 open_args = client.async_send_command.call_args_list[0][0][0] assert open_args["command"] == "node.set_value" assert open_args["nodeId"] == 3 @@ -340,16 +320,6 @@ async def test_aeotec_nano_shutter_cover( } assert not open_args["value"] - close_args = client.async_send_command.call_args_list[1][0][0] - assert close_args["command"] == "node.set_value" - assert close_args["nodeId"] == 3 - assert close_args["valueId"] == { - "commandClass": 38, - "endpoint": 0, - "property": "Off", - } - assert not close_args["value"] - # Test position update from value updated event event = Event( type="value updated", @@ -403,7 +373,7 @@ async def test_aeotec_nano_shutter_cover( blocking=True, ) - assert len(client.async_send_command.call_args_list) == 2 + assert len(client.async_send_command.call_args_list) == 1 open_args = client.async_send_command.call_args_list[0][0][0] assert open_args["command"] == "node.set_value" assert open_args["nodeId"] == 3 @@ -414,16 +384,6 @@ async def test_aeotec_nano_shutter_cover( } assert not open_args["value"] - close_args = client.async_send_command.call_args_list[1][0][0] - assert close_args["command"] == "node.set_value" - assert close_args["nodeId"] == 3 - assert close_args["valueId"] == { - "commandClass": 38, - "endpoint": 0, - "property": "Off", - } - assert not close_args["value"] - async def test_blind_cover(hass, client, iblinds_v2, integration): """Test a blind cover entity.""" From 7dfb8dda0ac5c23cf453ce927f9edda76ddcc099 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 16 Nov 2022 13:30:05 +0100 Subject: [PATCH 0466/1033] Keep rest_command interface consistent (#82116) rest_command: Keep interface consistent This is a tiny cosmetic patch, where we keep the variable names of the interface more consistent, to avoid having to wonder "is it a name or a command?". Signed-off-by: Olliver Schinagl Signed-off-by: Olliver Schinagl --- homeassistant/components/rest_command/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/rest_command/__init__.py b/homeassistant/components/rest_command/__init__.py index 70f887cf896..c26f9711e1f 100644 --- a/homeassistant/components/rest_command/__init__.py +++ b/homeassistant/components/rest_command/__init__.py @@ -154,7 +154,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # register services hass.services.async_register(DOMAIN, name, async_service_handler) - for command, command_config in config[DOMAIN].items(): - async_register_rest_command(command, command_config) + for name, command_config in config[DOMAIN].items(): + async_register_rest_command(name, command_config) return True From 987b8406652434785fe505e29bd723bb17998fc2 Mon Sep 17 00:00:00 2001 From: shou72 Date: Wed, 16 Nov 2022 13:51:33 +0100 Subject: [PATCH 0467/1033] Add Tuya wkcz thermostat sensors and switches (#81667) fixes undefined --- homeassistant/components/tuya/sensor.py | 17 +++++++++++++++++ homeassistant/components/tuya/switch.py | 15 +++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/homeassistant/components/tuya/sensor.py b/homeassistant/components/tuya/sensor.py index e19bcd20ba5..4ad0c8837cf 100644 --- a/homeassistant/components/tuya/sensor.py +++ b/homeassistant/components/tuya/sensor.py @@ -205,6 +205,23 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { ), *BATTERY_SENSORS, ), + # Two-way temperature and humidity switch + # "MOES Temperature and Humidity Smart Switch Module MS-103" + # Documentation not found + "wkcz": ( + TuyaSensorEntityDescription( + key=DPCode.HUMIDITY_VALUE, + name="Humidity", + device_class=SensorDeviceClass.HUMIDITY, + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.TEMP_CURRENT, + name="Temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), + ), # CO Detector # https://developer.tuya.com/en/docs/iot/categorycobj?id=Kaiuz3u1j6q1v "cobj": ( diff --git a/homeassistant/components/tuya/switch.py b/homeassistant/components/tuya/switch.py index 3ec24f4daab..329b170c53f 100644 --- a/homeassistant/components/tuya/switch.py +++ b/homeassistant/components/tuya/switch.py @@ -142,6 +142,21 @@ SWITCHES: dict[str, tuple[SwitchEntityDescription, ...]] = { icon="mdi:power-sleep", ), ), + # Two-way temperature and humidity switch + # "MOES Temperature and Humidity Smart Switch Module MS-103" + # Documentation not found + "wkcz": ( + SwitchEntityDescription( + key=DPCode.SWITCH_1, + name="Switch 1", + device_class=SwitchDeviceClass.OUTLET, + ), + SwitchEntityDescription( + key=DPCode.SWITCH_2, + name="Switch 2", + device_class=SwitchDeviceClass.OUTLET, + ), + ), # Switch # https://developer.tuya.com/en/docs/iot/s?id=K9gf7o5prgf7s "kg": ( From 4f7b583f197898da3dd59589872602650ca77c8b Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 16 Nov 2022 15:04:57 +0200 Subject: [PATCH 0468/1033] Fix shadowing of outer name in config_validation (#82088) Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- homeassistant/helpers/config_validation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 48f26c2b768..6963b2cfb05 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -914,9 +914,9 @@ def key_value_schemas( with contextlib.suppress(vol.Invalid): return cast(dict[Hashable, Any], default_schema(value)) - alternatives = ", ".join(str(key) for key in value_schemas) + alternatives = ", ".join(str(alternative) for alternative in value_schemas) if default_description: - alternatives += ", " + default_description + alternatives = f"{alternatives}, {default_description}" raise vol.Invalid( f"Unexpected value for {key}: '{key_value}'. Expected {alternatives}" ) From 5d78632d045a9e8cd31e3a100fb958996172b752 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 16 Nov 2022 14:44:08 +0100 Subject: [PATCH 0469/1033] Adjust type hints for CameraEntityFeature (#82191) --- homeassistant/components/camera/__init__.py | 4 ++-- homeassistant/components/netatmo/camera.py | 4 ++-- homeassistant/components/unifiprotect/camera.py | 2 +- homeassistant/components/uvc/camera.py | 2 +- pylint/plugins/hass_enforce_type_hints.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 227b1e0c9b7..0b103c6a4ff 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -429,7 +429,7 @@ class Camera(Entity): _attr_motion_detection_enabled: bool = False _attr_should_poll: bool = False # No need to poll cameras _attr_state: None = None # State is determined by is_on - _attr_supported_features: int = 0 + _attr_supported_features: CameraEntityFeature | int = 0 def __init__(self) -> None: """Initialize a camera.""" @@ -450,7 +450,7 @@ class Camera(Entity): return ENTITY_IMAGE_URL.format(self.entity_id, self.access_tokens[-1]) @property - def supported_features(self) -> int: + def supported_features(self) -> CameraEntityFeature | int: """Flag supported features.""" return self._attr_supported_features diff --git a/homeassistant/components/netatmo/camera.py b/homeassistant/components/netatmo/camera.py index 9254ff6e284..01c459acaea 100644 --- a/homeassistant/components/netatmo/camera.py +++ b/homeassistant/components/netatmo/camera.py @@ -179,9 +179,9 @@ class NetatmoCamera(NetatmoBase, Camera): return None @property - def supported_features(self) -> int: + def supported_features(self) -> CameraEntityFeature: """Return supported features.""" - supported_features: int = CameraEntityFeature.ON_OFF + supported_features = CameraEntityFeature.ON_OFF if self._model != "NDB": supported_features |= CameraEntityFeature.STREAM return supported_features diff --git a/homeassistant/components/unifiprotect/camera.py b/homeassistant/components/unifiprotect/camera.py index 8f561e5556f..5f858614ace 100644 --- a/homeassistant/components/unifiprotect/camera.py +++ b/homeassistant/components/unifiprotect/camera.py @@ -175,7 +175,7 @@ class ProtectCamera(ProtectDeviceEntity, Camera): self._stream_source = ( # pylint: disable=attribute-defined-outside-init None if disable_stream else rtsp_url ) - self._attr_supported_features: int = ( + self._attr_supported_features = ( CameraEntityFeature.STREAM if self._stream_source else 0 ) diff --git a/homeassistant/components/uvc/camera.py b/homeassistant/components/uvc/camera.py index c64642077da..28a221fd131 100644 --- a/homeassistant/components/uvc/camera.py +++ b/homeassistant/components/uvc/camera.py @@ -107,7 +107,7 @@ class UnifiVideoCamera(Camera): return self._name @property - def supported_features(self) -> int: + def supported_features(self) -> CameraEntityFeature | int: """Return supported features.""" channels = self._caminfo["channels"] for channel in channels: diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 6562785180d..2774fc54b02 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -803,7 +803,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type="int", + return_type=["CameraEntityFeature", "int"], ), TypeHintMatch( function_name="is_recording", From ecb7509333d02817ae552cb8fb3a7ca32c310790 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 16 Nov 2022 15:12:08 +0100 Subject: [PATCH 0470/1033] Add tibber price sensor state class (#82189) --- homeassistant/components/tibber/sensor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/tibber/sensor.py b/homeassistant/components/tibber/sensor.py index 4dcc4a8a777..73e9db08c03 100644 --- a/homeassistant/components/tibber/sensor.py +++ b/homeassistant/components/tibber/sensor.py @@ -350,6 +350,7 @@ class TibberSensorElPrice(TibberSensor): } self._attr_icon = ICON self._attr_name = f"Electricity price {self._home_name}" + self._attr_state_class = SensorStateClass.MEASUREMENT self._attr_unique_id = self._tibber_home.home_id self._model = "Price Sensor" From 871b4135ee6153f836c29b40a3ec7e3f6e204265 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 16 Nov 2022 15:26:37 +0100 Subject: [PATCH 0471/1033] Remove unused variable in qvr_pro (#82190) --- homeassistant/components/qvr_pro/camera.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/homeassistant/components/qvr_pro/camera.py b/homeassistant/components/qvr_pro/camera.py index 67e36dab203..6ab918f26fe 100644 --- a/homeassistant/components/qvr_pro/camera.py +++ b/homeassistant/components/qvr_pro/camera.py @@ -71,8 +71,6 @@ class QVRProCamera(Camera): self._client = client self._stream_source = stream_source - self._supported_features = 0 - super().__init__() @property @@ -113,8 +111,3 @@ class QVRProCamera(Camera): async def stream_source(self): """Get stream source.""" return self._stream_source - - @property - def supported_features(self): - """Get supported features.""" - return self._supported_features From f87ef742e8c2037fe77d031b48bd2b9e1ac0c995 Mon Sep 17 00:00:00 2001 From: rappenze Date: Wed, 16 Nov 2022 15:27:20 +0100 Subject: [PATCH 0472/1033] Use parameterized test for better code readability (#82194) * Use parameterized test for better code readability * Use parameterized test for better code readability * Update tests/components/fibaro/test_config_flow.py Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- tests/components/fibaro/test_config_flow.py | 22 ++++----------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/tests/components/fibaro/test_config_flow.py b/tests/components/fibaro/test_config_flow.py index 2a0936588cd..080cc3f4458 100644 --- a/tests/components/fibaro/test_config_flow.py +++ b/tests/components/fibaro/test_config_flow.py @@ -365,21 +365,7 @@ async def test_reauth_auth_failure(hass): assert result["errors"] == {"base": "invalid_auth"} -async def test_normalize_url_does_not_touch_valid_url(): - """Test that a correctly entered url is not touched.""" - assert _normalize_url(TEST_URL) == TEST_URL - - -async def test_normalize_url_add_missing_slash_at_the_end(): - """Test that a / is added at the end.""" - assert _normalize_url("http://192.168.1.1/api") == "http://192.168.1.1/api/" - - -async def test_normalize_url_add_api(): - """Test that api/ is added.""" - assert _normalize_url("http://192.168.1.1/") == "http://192.168.1.1/api/" - - -async def test_normalize_url_add_api_with_leading_slash(): - """Test that /api/ is added.""" - assert _normalize_url("http://192.168.1.1") == "http://192.168.1.1/api/" +@pytest.mark.parametrize("url_path", ["/api/", "/api", "/", ""]) +async def test_normalize_url(url_path: str) -> None: + """Test that the url is normalized for different entered values.""" + assert _normalize_url(f"http://192.168.1.1{url_path}") == "http://192.168.1.1/api/" From 6a1bb8c421e967955efac0f1802f3daceea31ac0 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 16 Nov 2022 15:38:10 +0100 Subject: [PATCH 0473/1033] Deprecate Python 3.9 (#82193) --- homeassistant/bootstrap.py | 35 +++++++++++++------ .../components/homeassistant/strings.json | 4 +++ .../homeassistant/translations/en.json | 4 +++ homeassistant/const.py | 4 +-- script/hassfest/requirements.py | 3 +- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 9ac8b2ef6e6..8771fb41247 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -17,7 +17,7 @@ import voluptuous as vol import yarl from . import config as conf_util, config_entries, core, loader -from .components import http, persistent_notification +from .components import http from .const import ( REQUIRED_NEXT_PYTHON_HA_RELEASE, REQUIRED_NEXT_PYTHON_VER, @@ -268,16 +268,31 @@ async def async_from_config_dict( REQUIRED_NEXT_PYTHON_HA_RELEASE and sys.version_info[:3] < REQUIRED_NEXT_PYTHON_VER ): - msg = ( - "Support for the running Python version " - f"{'.'.join(str(x) for x in sys.version_info[:3])} is deprecated and will " - f"be removed in Home Assistant {REQUIRED_NEXT_PYTHON_HA_RELEASE}. " - "Please upgrade Python to " - f"{'.'.join(str(x) for x in REQUIRED_NEXT_PYTHON_VER[:2])}." + current_python_version = ".".join(str(x) for x in sys.version_info[:3]) + required_python_version = ".".join(str(x) for x in REQUIRED_NEXT_PYTHON_VER[:2]) + _LOGGER.warning( + ( + "Support for the running Python version %s is deprecated and " + "will be removed in Home Assistant %s; " + "Please upgrade Python to %s" + ), + current_python_version, + REQUIRED_NEXT_PYTHON_HA_RELEASE, + required_python_version, ) - _LOGGER.warning(msg) - persistent_notification.async_create( - hass, msg, "Python version", "python_version" + issue_registry.async_create_issue( + hass, + core.DOMAIN, + "python_version", + is_fixable=False, + severity=issue_registry.IssueSeverity.WARNING, + breaks_in_ha_version=REQUIRED_NEXT_PYTHON_HA_RELEASE, + translation_key="python_version", + translation_placeholders={ + "current_python_version": current_python_version, + "required_python_version": required_python_version, + "breaks_in_ha_version": REQUIRED_NEXT_PYTHON_HA_RELEASE, + }, ) return hass diff --git a/homeassistant/components/homeassistant/strings.json b/homeassistant/components/homeassistant/strings.json index 2db00081eaa..d7c5216111c 100644 --- a/homeassistant/components/homeassistant/strings.json +++ b/homeassistant/components/homeassistant/strings.json @@ -3,6 +3,10 @@ "historic_currency": { "title": "The configured currency is no longer in use", "description": "The currency {currency} is no longer in use, please reconfigure the currency configuration." + }, + "python_version": { + "title": "Support for Python {current_python_version} is being removed", + "description": "Support for running Home Assistant in the current used Python version {current_python_version} is deprecated and will be removed in Home Assistant {breaks_in_ha_version}. Please upgrade Python to {required_python_version} to prevent your Home Assistant instance from breaking." } }, "system_health": { diff --git a/homeassistant/components/homeassistant/translations/en.json b/homeassistant/components/homeassistant/translations/en.json index 8008aa813aa..b41e5753995 100644 --- a/homeassistant/components/homeassistant/translations/en.json +++ b/homeassistant/components/homeassistant/translations/en.json @@ -3,6 +3,10 @@ "historic_currency": { "description": "The currency {currency} is no longer in use, please reconfigure the currency configuration.", "title": "The configured currency is no longer in use" + }, + "python_version": { + "description": "Support for running Home Assistant in the current used Python version {current_python_version} is deprecated and will be removed in Home Assistant {breaks_in_ha_version}. Please upgrade Python to {required_python_version} to prevent your Home Assistant instance from breaking.", + "title": "Support for Python {current_python_version} is being removed" } }, "system_health": { diff --git a/homeassistant/const.py b/homeassistant/const.py index 6fe8f3730ff..4201c7f2de4 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -12,9 +12,9 @@ PATCH_VERSION: Final = "0.dev0" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) -REQUIRED_NEXT_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) +REQUIRED_NEXT_PYTHON_VER: Final[tuple[int, int, int]] = (3, 10, 0) # Truthy date string triggers showing related deprecation warning messages. -REQUIRED_NEXT_PYTHON_HA_RELEASE: Final = "" +REQUIRED_NEXT_PYTHON_HA_RELEASE: Final = "2023.2" # Format for platform files PLATFORM_FORMAT: Final = "{platform}.{domain}" diff --git a/script/hassfest/requirements.py b/script/hassfest/requirements.py index b8fb22fb786..0e72e2eabd6 100644 --- a/script/hassfest/requirements.py +++ b/script/hassfest/requirements.py @@ -32,7 +32,8 @@ SUPPORTED_PYTHON_TUPLES = [ ] if REQUIRED_PYTHON_VER[0] == REQUIRED_NEXT_PYTHON_VER[0]: for minor in range(REQUIRED_PYTHON_VER[1] + 1, REQUIRED_NEXT_PYTHON_VER[1] + 1): - SUPPORTED_PYTHON_TUPLES.append((REQUIRED_PYTHON_VER[0], minor)) + if minor < 10: # stdlib list does not support 3.10+ + SUPPORTED_PYTHON_TUPLES.append((REQUIRED_PYTHON_VER[0], minor)) SUPPORTED_PYTHON_VERSIONS = [ ".".join(map(str, version_tuple)) for version_tuple in SUPPORTED_PYTHON_TUPLES ] From 615f7204cb1097218e374f6fdc77225b52e729d2 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 16 Nov 2022 16:07:43 +0100 Subject: [PATCH 0474/1033] Add type hints to mobile app webhooks (#82177) --- .../components/mobile_app/helpers.py | 11 +- .../components/mobile_app/webhook.py | 102 ++++++++++++------ 2 files changed, 75 insertions(+), 38 deletions(-) diff --git a/homeassistant/components/mobile_app/helpers.py b/homeassistant/components/mobile_app/helpers.py index a116e6196f4..25217962bc8 100644 --- a/homeassistant/components/mobile_app/helpers.py +++ b/homeassistant/components/mobile_app/helpers.py @@ -1,10 +1,11 @@ """Helpers for mobile_app.""" from __future__ import annotations -from collections.abc import Callable +from collections.abc import Callable, Mapping from http import HTTPStatus import json import logging +from typing import Any from aiohttp.web import Response, json_response from nacl.encoding import Base64Encoder, HexEncoder, RawEncoder @@ -111,7 +112,7 @@ def _decrypt_payload_legacy(key: str | None, ciphertext: str) -> dict[str, str] return _decrypt_payload_helper(key, ciphertext, get_key_bytes, RawEncoder) -def registration_context(registration: dict) -> Context: +def registration_context(registration: Mapping[str, Any]) -> Context: """Generate a context from a request.""" return Context(user_id=registration[CONF_USER_ID]) @@ -173,11 +174,11 @@ def savable_state(hass: HomeAssistant) -> dict: def webhook_response( - data, + data: Any, *, - registration: dict, + registration: Mapping[str, Any], status: HTTPStatus = HTTPStatus.OK, - headers: dict | None = None, + headers: Mapping[str, str] | None = None, ) -> Response: """Return a encrypted response if registration supports it.""" data = json.dumps(data, cls=JSONEncoder) diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 8dfadc6645d..7fecf461b13 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -2,11 +2,13 @@ from __future__ import annotations import asyncio +from collections.abc import Callable, Coroutine from contextlib import suppress from functools import wraps from http import HTTPStatus import logging import secrets +from typing import Any from aiohttp.web import HTTPBadRequest, Request, Response, json_response from nacl.exceptions import CryptoError @@ -30,6 +32,7 @@ from homeassistant.components.sensor import ( STATE_CLASSES as SENSOSR_STATE_CLASSES, ) from homeassistant.components.zone import DOMAIN as ZONE_DOMAIN +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_DEVICE_ID, ATTR_DOMAIN, @@ -41,7 +44,7 @@ from homeassistant.const import ( CONF_WEBHOOK_ID, ) from homeassistant.core import EventOrigin, HomeAssistant -from homeassistant.exceptions import HomeAssistantError, ServiceNotFound +from homeassistant.exceptions import HomeAssistantError, ServiceNotFound, TemplateError from homeassistant.helpers import ( config_validation as cv, device_registry as dr, @@ -117,7 +120,9 @@ _LOGGER = logging.getLogger(__name__) DELAY_SAVE = 10 -WEBHOOK_COMMANDS = Registry() # type: ignore[var-annotated] +WEBHOOK_COMMANDS: Registry[ + str, Callable[[HomeAssistant, ConfigEntry, Any], Coroutine[Any, Any, Response]] +] = Registry() COMBINED_CLASSES = set(BINARY_SENSOR_CLASSES + SENSOR_CLASSES) SENSOR_TYPES = [ATTR_SENSOR_TYPE_BINARY_SENSOR, ATTR_SENSOR_TYPE_SENSOR] @@ -164,9 +169,9 @@ async def handle_webhook( if webhook_id in hass.data[DOMAIN][DATA_DELETED_IDS]: return Response(status=410) - config_entry = hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id] + config_entry: ConfigEntry = hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id] - device_name = config_entry.data[ATTR_DEVICE_NAME] + device_name: str = config_entry.data[ATTR_DEVICE_NAME] try: req_data = await request.json() @@ -248,7 +253,9 @@ async def handle_webhook( vol.Optional(ATTR_SERVICE_DATA, default={}): dict, } ) -async def webhook_call_service(hass, config_entry, data): +async def webhook_call_service( + hass: HomeAssistant, config_entry: ConfigEntry, data: dict[str, Any] +) -> Response: """Handle a call service webhook.""" try: await hass.services.async_call( @@ -277,9 +284,11 @@ async def webhook_call_service(hass, config_entry, data): vol.Optional(ATTR_EVENT_DATA, default={}): dict, } ) -async def webhook_fire_event(hass, config_entry, data): +async def webhook_fire_event( + hass: HomeAssistant, config_entry: ConfigEntry, data: dict[str, Any] +) -> Response: """Handle a fire event webhook.""" - event_type = data[ATTR_EVENT_TYPE] + event_type: str = data[ATTR_EVENT_TYPE] hass.bus.async_fire( event_type, data[ATTR_EVENT_DATA], @@ -291,7 +300,9 @@ async def webhook_fire_event(hass, config_entry, data): @WEBHOOK_COMMANDS.register("stream_camera") @validate_schema({vol.Required(ATTR_CAMERA_ENTITY_ID): cv.string}) -async def webhook_stream_camera(hass, config_entry, data): +async def webhook_stream_camera( + hass: HomeAssistant, config_entry: ConfigEntry, data: dict[str, str] +) -> Response: """Handle a request to HLS-stream a camera.""" if (camera_state := hass.states.get(data[ATTR_CAMERA_ENTITY_ID])) is None: return webhook_response( @@ -300,7 +311,9 @@ async def webhook_stream_camera(hass, config_entry, data): status=HTTPStatus.BAD_REQUEST, ) - resp = {"mjpeg_path": f"/api/camera_proxy_stream/{camera_state.entity_id}"} + resp: dict[str, Any] = { + "mjpeg_path": f"/api/camera_proxy_stream/{camera_state.entity_id}" + } if camera_state.attributes[ATTR_SUPPORTED_FEATURES] & CameraEntityFeature.STREAM: try: @@ -324,14 +337,16 @@ async def webhook_stream_camera(hass, config_entry, data): } } ) -async def webhook_render_template(hass, config_entry, data): +async def webhook_render_template( + hass: HomeAssistant, config_entry: ConfigEntry, data: dict[str, Any] +) -> Response: """Handle a render template webhook.""" resp = {} for key, item in data.items(): try: tpl = template.Template(item[ATTR_TEMPLATE], hass) resp[key] = tpl.async_render(item.get(ATTR_TEMPLATE_VARIABLES)) - except template.TemplateError as ex: + except TemplateError as ex: resp[key] = {"error": str(ex)} return webhook_response(resp, registration=config_entry.data) @@ -353,7 +368,9 @@ async def webhook_render_template(hass, config_entry, data): }, ) ) -async def webhook_update_location(hass, config_entry, data): +async def webhook_update_location( + hass: HomeAssistant, config_entry: ConfigEntry, data: dict[str, Any] +) -> Response: """Handle an update location webhook.""" async_dispatcher_send( hass, SIGNAL_LOCATION_UPDATE.format(config_entry.entry_id), data @@ -372,7 +389,9 @@ async def webhook_update_location(hass, config_entry, data): vol.Optional(ATTR_OS_VERSION): cv.string, } ) -async def webhook_update_registration(hass, config_entry, data): +async def webhook_update_registration( + hass: HomeAssistant, config_entry: ConfigEntry, data: dict[str, Any] +) -> Response: """Handle an update registration webhook.""" new_registration = {**config_entry.data, **data} @@ -398,7 +417,9 @@ async def webhook_update_registration(hass, config_entry, data): @WEBHOOK_COMMANDS.register("enable_encryption") -async def webhook_enable_encryption(hass, config_entry, data): +async def webhook_enable_encryption( + hass: HomeAssistant, config_entry: ConfigEntry, data: Any +) -> Response: """Handle a encryption enable webhook.""" if config_entry.data[ATTR_SUPPORTS_ENCRYPTION]: _LOGGER.warning( @@ -418,14 +439,18 @@ async def webhook_enable_encryption(hass, config_entry, data): secret = secrets.token_hex(SecretBox.KEY_SIZE) - data = {**config_entry.data, ATTR_SUPPORTS_ENCRYPTION: True, CONF_SECRET: secret} + update_data = { + **config_entry.data, + ATTR_SUPPORTS_ENCRYPTION: True, + CONF_SECRET: secret, + } - hass.config_entries.async_update_entry(config_entry, data=data) + hass.config_entries.async_update_entry(config_entry, data=update_data) return json_response({"secret": secret}) -def _validate_state_class_sensor(value: dict): +def _validate_state_class_sensor(value: dict[str, Any]) -> dict[str, Any]: """Validate we only set state class for sensors.""" if ( ATTR_SENSOR_STATE_CLASS in value @@ -436,12 +461,12 @@ def _validate_state_class_sensor(value: dict): return value -def _gen_unique_id(webhook_id, sensor_unique_id): +def _gen_unique_id(webhook_id: str, sensor_unique_id: str) -> str: """Return a unique sensor ID.""" return f"{webhook_id}_{sensor_unique_id}" -def _extract_sensor_unique_id(webhook_id, unique_id): +def _extract_sensor_unique_id(webhook_id: str, unique_id: str) -> str: """Return a unique sensor ID.""" return unique_id[len(webhook_id) + 1 :] @@ -469,11 +494,13 @@ def _extract_sensor_unique_id(webhook_id, unique_id): _validate_state_class_sensor, ) ) -async def webhook_register_sensor(hass, config_entry, data): +async def webhook_register_sensor( + hass: HomeAssistant, config_entry: ConfigEntry, data: dict[str, Any] +) -> Response: """Handle a register sensor webhook.""" - entity_type = data[ATTR_SENSOR_TYPE] - unique_id = data[ATTR_SENSOR_UNIQUE_ID] - device_name = config_entry.data[ATTR_DEVICE_NAME] + entity_type: str = data[ATTR_SENSOR_TYPE] + unique_id: str = data[ATTR_SENSOR_UNIQUE_ID] + device_name: str = config_entry.data[ATTR_DEVICE_NAME] unique_store_key = _gen_unique_id(config_entry.data[CONF_WEBHOOK_ID], unique_id) entity_registry = er.async_get(hass) @@ -490,7 +517,8 @@ async def webhook_register_sensor(hass, config_entry, data): ) entry = entity_registry.async_get(existing_sensor) - changes = {} + assert entry is not None + changes: dict[str, Any] = {} if ( new_name := f"{device_name} {data[ATTR_SENSOR_NAME]}" @@ -553,7 +581,9 @@ async def webhook_register_sensor(hass, config_entry, data): ], ) ) -async def webhook_update_sensor_states(hass, config_entry, data): +async def webhook_update_sensor_states( + hass: HomeAssistant, config_entry: ConfigEntry, data: list[dict[str, Any]] +) -> Response: """Handle an update sensor states webhook.""" sensor_schema_full = vol.Schema( { @@ -565,14 +595,14 @@ async def webhook_update_sensor_states(hass, config_entry, data): } ) - device_name = config_entry.data[ATTR_DEVICE_NAME] - resp = {} + device_name: str = config_entry.data[ATTR_DEVICE_NAME] + resp: dict[str, Any] = {} entity_registry = er.async_get(hass) for sensor in data: - entity_type = sensor[ATTR_SENSOR_TYPE] + entity_type: str = sensor[ATTR_SENSOR_TYPE] - unique_id = sensor[ATTR_SENSOR_UNIQUE_ID] + unique_id: str = sensor[ATTR_SENSOR_UNIQUE_ID] unique_store_key = _gen_unique_id(config_entry.data[CONF_WEBHOOK_ID], unique_id) @@ -622,14 +652,16 @@ async def webhook_update_sensor_states(hass, config_entry, data): # Check if disabled entry = entity_registry.async_get(entity_id) - if entry.disabled_by: + if entry and entry.disabled_by: resp[unique_id]["is_disabled"] = True return webhook_response(resp, registration=config_entry.data) @WEBHOOK_COMMANDS.register("get_zones") -async def webhook_get_zones(hass, config_entry, data): +async def webhook_get_zones( + hass: HomeAssistant, config_entry: ConfigEntry, data: Any +) -> Response: """Handle a get zones webhook.""" zones = [ hass.states.get(entity_id) @@ -639,7 +671,9 @@ async def webhook_get_zones(hass, config_entry, data): @WEBHOOK_COMMANDS.register("get_config") -async def webhook_get_config(hass, config_entry, data): +async def webhook_get_config( + hass: HomeAssistant, config_entry: ConfigEntry, data: Any +) -> Response: """Handle a get config webhook.""" hass_config = hass.config.as_dict() @@ -681,7 +715,9 @@ async def webhook_get_config(hass, config_entry, data): @WEBHOOK_COMMANDS.register("scan_tag") @validate_schema({vol.Required("tag_id"): cv.string}) -async def webhook_scan_tag(hass, config_entry, data): +async def webhook_scan_tag( + hass: HomeAssistant, config_entry: ConfigEntry, data: dict[str, str] +) -> Response: """Handle a fire event webhook.""" await tag.async_scan_tag( hass, From 03e6132d98346864116f3babc3aa5477b04326c9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 16 Nov 2022 09:37:00 -0600 Subject: [PATCH 0475/1033] Fix static version in homekit tests (#82201) --- tests/components/homekit/test_diagnostics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/homekit/test_diagnostics.py b/tests/components/homekit/test_diagnostics.py index 30fe5f2d8fc..48b1b84580e 100644 --- a/tests/components/homekit/test_diagnostics.py +++ b/tests/components/homekit/test_diagnostics.py @@ -379,7 +379,7 @@ async def test_config_entry_with_trigger_accessory( "iid": 7, "perms": ["pr"], "type": "52", - "value": "2022.12.0", + "value": ANY, }, ], "iid": 1, From 1f0691bf18874a42a8b124eda512ffe28e7c57d3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 16 Nov 2022 10:07:01 -0600 Subject: [PATCH 0476/1033] Bump bluetooth-auto-recovery to 0.4.0 (#82195) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 3d596d817ee..4d4075bc5a9 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -9,7 +9,7 @@ "bleak==0.19.2", "bleak-retry-connector==2.8.4", "bluetooth-adapters==0.7.0", - "bluetooth-auto-recovery==0.3.6", + "bluetooth-auto-recovery==0.4.0", "bluetooth-data-tools==0.3.0", "dbus-fast==1.74.0" ], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 0a862e040b6..51b38623c81 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -13,7 +13,7 @@ bcrypt==3.1.7 bleak-retry-connector==2.8.4 bleak==0.19.2 bluetooth-adapters==0.7.0 -bluetooth-auto-recovery==0.3.6 +bluetooth-auto-recovery==0.4.0 bluetooth-data-tools==0.3.0 certifi>=2021.5.30 ciso8601==2.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index 9661cca9c58..d577053f253 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -450,7 +450,7 @@ bluemaestro-ble==0.2.0 bluetooth-adapters==0.7.0 # homeassistant.components.bluetooth -bluetooth-auto-recovery==0.3.6 +bluetooth-auto-recovery==0.4.0 # homeassistant.components.led_ble bluetooth-data-tools==0.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d23e8208081..9dbcb71ac2e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -364,7 +364,7 @@ bluemaestro-ble==0.2.0 bluetooth-adapters==0.7.0 # homeassistant.components.bluetooth -bluetooth-auto-recovery==0.3.6 +bluetooth-auto-recovery==0.4.0 # homeassistant.components.led_ble bluetooth-data-tools==0.2.0 From f952b74b74443d20c2ed200990e3040fee38aa9d Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 16 Nov 2022 17:08:42 +0100 Subject: [PATCH 0477/1033] Adjust type hints for AlarmControlPanelEntityFeature (#82186) --- homeassistant/components/alarm_control_panel/__init__.py | 4 ++-- homeassistant/components/mqtt/alarm_control_panel.py | 2 +- homeassistant/components/overkiz/alarm_control_panel.py | 2 +- pylint/plugins/hass_enforce_type_hints.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index 4d74a39d977..8f523d5af7c 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -133,7 +133,7 @@ class AlarmControlPanelEntity(Entity): _attr_changed_by: str | None = None _attr_code_arm_required: bool = True _attr_code_format: CodeFormat | None = None - _attr_supported_features: int + _attr_supported_features: AlarmControlPanelEntityFeature | int @property def code_format(self) -> CodeFormat | None: @@ -207,7 +207,7 @@ class AlarmControlPanelEntity(Entity): await self.hass.async_add_executor_job(self.alarm_arm_custom_bypass, code) @property - def supported_features(self) -> int: + def supported_features(self) -> AlarmControlPanelEntityFeature | int: """Return the list of supported features.""" return self._attr_supported_features diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index 8a15c2587f1..136fe79752e 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -243,7 +243,7 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): return self._state @property - def supported_features(self) -> int: + def supported_features(self) -> AlarmControlPanelEntityFeature: """Return the list of supported features.""" return ( AlarmControlPanelEntityFeature.ARM_HOME diff --git a/homeassistant/components/overkiz/alarm_control_panel.py b/homeassistant/components/overkiz/alarm_control_panel.py index 2229c583297..ae7d16aee9c 100644 --- a/homeassistant/components/overkiz/alarm_control_panel.py +++ b/homeassistant/components/overkiz/alarm_control_panel.py @@ -39,7 +39,7 @@ from .entity import OverkizDescriptiveEntity class OverkizAlarmDescriptionMixin: """Define an entity description mixin for switch entities.""" - supported_features: int + supported_features: AlarmControlPanelEntityFeature fn_state: Callable[[Callable[[str], OverkizStateType]], str] diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 2774fc54b02..ddf3a3a5155 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -661,7 +661,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type="int", + return_type=["AlarmControlPanelEntityFeature", "int"], ), TypeHintMatch( function_name="alarm_disarm", From bb64b39d0e6d41f531af9c63b69d1ce243a2751b Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 16 Nov 2022 17:13:23 +0100 Subject: [PATCH 0478/1033] Minor refactor of zha config flow (#82200) * Minor refactor of zha config flow * Move ZhaRadioManager to a separate module --- homeassistant/components/zha/config_flow.py | 300 ++++++------------ homeassistant/components/zha/radio_manager.py | 158 +++++++++ tests/components/zha/test_config_flow.py | 40 ++- 3 files changed, 283 insertions(+), 215 deletions(-) create mode 100644 homeassistant/components/zha/radio_manager.py diff --git a/homeassistant/components/zha/config_flow.py b/homeassistant/components/zha/config_flow.py index be422778e2d..e8745684275 100644 --- a/homeassistant/components/zha/config_flow.py +++ b/homeassistant/components/zha/config_flow.py @@ -1,44 +1,34 @@ """Config flow for ZHA.""" from __future__ import annotations -import asyncio import collections -import contextlib import copy import json -import logging -import os from typing import Any import serial.tools.list_ports import voluptuous as vol -from zigpy.application import ControllerApplication import zigpy.backups from zigpy.config import CONF_DEVICE, CONF_DEVICE_PATH -from zigpy.exceptions import NetworkNotFormed from homeassistant import config_entries from homeassistant.components import onboarding, usb, zeroconf from homeassistant.components.file_upload import process_uploaded_file from homeassistant.const import CONF_NAME -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowHandler, FlowResult from homeassistant.helpers.selector import FileSelector, FileSelectorConfig from homeassistant.util import dt from .core.const import ( CONF_BAUDRATE, - CONF_DATABASE, CONF_FLOWCONTROL, CONF_RADIO_TYPE, - CONF_ZIGPY, - DATA_ZHA, - DATA_ZHA_CONFIG, - DEFAULT_DATABASE_NAME, DOMAIN, EZSP_OVERWRITE_EUI64, RadioType, ) +from .radio_manager import ZhaRadioManager CONF_MANUAL_PATH = "Enter Manually" SUPPORTED_PORT_SETTINGS = ( @@ -47,16 +37,6 @@ SUPPORTED_PORT_SETTINGS = ( ) DECONZ_DOMAIN = "deconz" -# Only the common radio types will be autoprobed, ordered by new device popularity. -# XBee takes too long to probe since it scans through all possible bauds and likely has -# very few users to begin with. -AUTOPROBE_RADIOS = ( - RadioType.ezsp, - RadioType.znp, - RadioType.deconz, - RadioType.zigate, -) - FORMATION_STRATEGY = "formation_strategy" FORMATION_FORM_NEW_NETWORK = "form_new_network" FORMATION_REUSE_SETTINGS = "reuse_settings" @@ -74,8 +54,6 @@ UPLOADED_BACKUP_FILE = "uploaded_backup_file" DEFAULT_ZHA_ZEROCONF_PORT = 6638 ESPHOME_API_PORT = 6053 -CONNECT_DELAY_S = 1.0 - HARDWARE_DISCOVERY_SCHEMA = vol.Schema( { vol.Required("name"): str, @@ -84,8 +62,6 @@ HARDWARE_DISCOVERY_SCHEMA = vol.Schema( } ) -_LOGGER = logging.getLogger(__name__) - def _format_backup_choice( backup: zigpy.backups.NetworkBackup, *, pan_ids: bool = True @@ -134,110 +110,44 @@ def _prevent_overwrite_ezsp_ieee( class BaseZhaFlow(FlowHandler): """Mixin for common ZHA flow steps and forms.""" + _hass: HomeAssistant + def __init__(self) -> None: """Initialize flow instance.""" super().__init__() - self._device_path: str | None = None - self._device_settings: dict[str, Any] | None = None - self._radio_type: RadioType | None = None + self._hass = None # type: ignore[assignment] + self._radio_mgr = ZhaRadioManager() self._title: str | None = None - self._current_settings: zigpy.backups.NetworkBackup | None = None - self._backups: list[zigpy.backups.NetworkBackup] = [] - self._chosen_backup: zigpy.backups.NetworkBackup | None = None - @contextlib.asynccontextmanager - async def _connect_zigpy_app(self) -> ControllerApplication: - """Connect to the radio with the current config and then clean up.""" - assert self._radio_type is not None + @property + def hass(self): + """Return hass.""" + return self._hass - config = self.hass.data.get(DATA_ZHA, {}).get(DATA_ZHA_CONFIG, {}) - app_config = config.get(CONF_ZIGPY, {}).copy() + @hass.setter + def hass(self, hass): + """Set hass.""" + self._hass = hass + self._radio_mgr.hass = hass - database_path = config.get( - CONF_DATABASE, - self.hass.config.path(DEFAULT_DATABASE_NAME), - ) - - # Don't create `zigbee.db` if it doesn't already exist - if not await self.hass.async_add_executor_job(os.path.exists, database_path): - database_path = None - - app_config[CONF_DATABASE] = database_path - app_config[CONF_DEVICE] = self._device_settings - app_config = self._radio_type.controller.SCHEMA(app_config) - - app = await self._radio_type.controller.new( - app_config, auto_form=False, start_radio=False - ) - - try: - await app.connect() - yield app - finally: - await app.disconnect() - await asyncio.sleep(CONNECT_DELAY_S) - - async def _restore_backup( - self, backup: zigpy.backups.NetworkBackup, **kwargs: Any - ) -> None: - """Restore the provided network backup, passing through kwargs.""" - if self._current_settings is not None and self._current_settings.supersedes( - self._chosen_backup - ): - return - - async with self._connect_zigpy_app() as app: - await app.backups.restore_backup(backup, **kwargs) - - def _parse_radio_type(self, radio_type: str) -> RadioType: - """Parse a radio type name, accounting for past aliases.""" - if radio_type == "efr32": - return RadioType.ezsp - - return RadioType[radio_type] - - async def _detect_radio_type(self) -> bool: - """Probe all radio types on the current port.""" - for radio in AUTOPROBE_RADIOS: - _LOGGER.debug("Attempting to probe radio type %s", radio) - - dev_config = radio.controller.SCHEMA_DEVICE( - {CONF_DEVICE_PATH: self._device_path} - ) - probe_result = await radio.controller.probe(dev_config) - - if not probe_result: - continue - - # Radio library probing can succeed and return new device settings - if isinstance(probe_result, dict): - dev_config = probe_result - - self._radio_type = radio - self._device_settings = dev_config - - return True - - return False - - async def _async_create_radio_entity(self) -> FlowResult: - """Create a config entity with the current flow state.""" + async def _async_create_radio_entry(self) -> FlowResult: + """Create a config entry with the current flow state.""" assert self._title is not None - assert self._radio_type is not None - assert self._device_path is not None - assert self._device_settings is not None + assert self._radio_mgr.radio_type is not None + assert self._radio_mgr.device_path is not None + assert self._radio_mgr.device_settings is not None - device_settings = self._device_settings.copy() + device_settings = self._radio_mgr.device_settings.copy() device_settings[CONF_DEVICE_PATH] = await self.hass.async_add_executor_job( - usb.get_serial_by_id, self._device_path + usb.get_serial_by_id, self._radio_mgr.device_path ) return self.async_create_entry( title=self._title, data={ CONF_DEVICE: device_settings, - CONF_RADIO_TYPE: self._radio_type.name, + CONF_RADIO_TYPE: self._radio_mgr.radio_type.name, }, ) @@ -264,9 +174,9 @@ class BaseZhaFlow(FlowHandler): return await self.async_step_manual_pick_radio_type() port = ports[list_of_ports.index(user_selection)] - self._device_path = port.device + self._radio_mgr.device_path = port.device - if not await self._detect_radio_type(): + if not await self._radio_mgr.detect_radio_type(): # Did not autodetect anything, proceed to manual selection return await self.async_step_manual_pick_radio_type() @@ -282,9 +192,9 @@ class BaseZhaFlow(FlowHandler): # Pre-select the currently configured port default_port = vol.UNDEFINED - if self._device_path is not None: + if self._radio_mgr.device_path is not None: for description, port in zip(list_of_ports, ports): - if port.device == self._device_path: + if port.device == self._radio_mgr.device_path: default_port = description break else: @@ -304,14 +214,16 @@ class BaseZhaFlow(FlowHandler): ) -> FlowResult: """Manually select the radio type.""" if user_input is not None: - self._radio_type = RadioType.get_by_description(user_input[CONF_RADIO_TYPE]) + self._radio_mgr.radio_type = RadioType.get_by_description( + user_input[CONF_RADIO_TYPE] + ) return await self.async_step_manual_port_config() # Pre-select the current radio type default = vol.UNDEFINED - if self._radio_type is not None: - default = self._radio_type.description + if self._radio_mgr.radio_type is not None: + default = self._radio_mgr.radio_type.description schema = { vol.Required(CONF_RADIO_TYPE, default=default): vol.In(RadioType.list()) @@ -326,35 +238,43 @@ class BaseZhaFlow(FlowHandler): self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Enter port settings specific for this type of radio.""" - assert self._radio_type is not None + assert self._radio_mgr.radio_type is not None errors = {} if user_input is not None: self._title = user_input[CONF_DEVICE_PATH] - self._device_path = user_input[CONF_DEVICE_PATH] - self._device_settings = user_input.copy() + self._radio_mgr.device_path = user_input[CONF_DEVICE_PATH] + self._radio_mgr.device_settings = user_input.copy() - if await self._radio_type.controller.probe(user_input): + if await self._radio_mgr.radio_type.controller.probe(user_input): return await self.async_step_choose_formation_strategy() errors["base"] = "cannot_connect" schema = { vol.Required( - CONF_DEVICE_PATH, default=self._device_path or vol.UNDEFINED + CONF_DEVICE_PATH, default=self._radio_mgr.device_path or vol.UNDEFINED ): str } source = self.context.get("source") - for param, value in self._radio_type.controller.SCHEMA_DEVICE.schema.items(): + for ( + param, + value, + ) in self._radio_mgr.radio_type.controller.SCHEMA_DEVICE.schema.items(): if param not in SUPPORTED_PORT_SETTINGS: continue if source == config_entries.SOURCE_ZEROCONF and param == CONF_BAUDRATE: value = 115200 param = vol.Required(CONF_BAUDRATE, default=value) - elif self._device_settings is not None and param in self._device_settings: - param = vol.Required(str(param), default=self._device_settings[param]) + elif ( + self._radio_mgr.device_settings is not None + and param in self._radio_mgr.device_settings + ): + param = vol.Required( + str(param), default=self._radio_mgr.device_settings[param] + ) schema[param] = value @@ -364,43 +284,26 @@ class BaseZhaFlow(FlowHandler): errors=errors, ) - async def _async_load_network_settings(self) -> None: - """Connect to the radio and load its current network settings.""" - async with self._connect_zigpy_app() as app: - # Check if the stick has any settings and load them - try: - await app.load_network_info() - except NetworkNotFormed: - pass - else: - self._current_settings = zigpy.backups.NetworkBackup( - network_info=app.state.network_info, - node_info=app.state.node_info, - ) - - # The list of backups will always exist - self._backups = app.backups.backups.copy() - async def async_step_choose_formation_strategy( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Choose how to deal with the current radio's settings.""" - await self._async_load_network_settings() + await self._radio_mgr.async_load_network_settings() strategies = [] # Check if we have any automatic backups *and* if the backups differ from # the current radio settings, if they exist (since restoring would be redundant) - if self._backups and ( - self._current_settings is None + if self._radio_mgr.backups and ( + self._radio_mgr.current_settings is None or any( - not backup.is_compatible_with(self._current_settings) - for backup in self._backups + not backup.is_compatible_with(self._radio_mgr.current_settings) + for backup in self._radio_mgr.backups ) ): strategies.append(CHOOSE_AUTOMATIC_BACKUP) - if self._current_settings is not None: + if self._radio_mgr.current_settings is not None: strategies.append(FORMATION_REUSE_SETTINGS) strategies.append(FORMATION_UPLOAD_MANUAL_BACKUP) @@ -415,16 +318,14 @@ class BaseZhaFlow(FlowHandler): self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Reuse the existing network settings on the stick.""" - return await self._async_create_radio_entity() + return await self._async_create_radio_entry() async def async_step_form_new_network( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Form a brand new network.""" - async with self._connect_zigpy_app() as app: - await app.form_network() - - return await self._async_create_radio_entity() + await self._radio_mgr.async_form_network() + return await self._async_create_radio_entry() def _parse_uploaded_backup( self, uploaded_file_id: str @@ -443,7 +344,7 @@ class BaseZhaFlow(FlowHandler): if user_input is not None: try: - self._chosen_backup = await self.hass.async_add_executor_job( + self._radio_mgr.chosen_backup = await self.hass.async_add_executor_job( self._parse_uploaded_backup, user_input[UPLOADED_BACKUP_FILE] ) except ValueError: @@ -470,23 +371,24 @@ class BaseZhaFlow(FlowHandler): if self.show_advanced_options: # Always show the PAN IDs when in advanced mode choices = [ - _format_backup_choice(backup, pan_ids=True) for backup in self._backups + _format_backup_choice(backup, pan_ids=True) + for backup in self._radio_mgr.backups ] else: # Only show the PAN IDs for multiple backups taken on the same day num_backups_on_date = collections.Counter( - backup.backup_time.date() for backup in self._backups + backup.backup_time.date() for backup in self._radio_mgr.backups ) choices = [ _format_backup_choice( backup, pan_ids=(num_backups_on_date[backup.backup_time.date()] > 1) ) - for backup in self._backups + for backup in self._radio_mgr.backups ] if user_input is not None: index = choices.index(user_input[CHOOSE_AUTOMATIC_BACKUP]) - self._chosen_backup = self._backups[index] + self._radio_mgr.chosen_backup = self._radio_mgr.backups[index] return await self.async_step_maybe_confirm_ezsp_restore() @@ -505,46 +407,47 @@ class BaseZhaFlow(FlowHandler): self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Confirm restore for EZSP radios that require permanent IEEE writes.""" - assert self._chosen_backup is not None + assert self._radio_mgr.chosen_backup is not None - if self._radio_type != RadioType.ezsp: - await self._restore_backup(self._chosen_backup) - return await self._async_create_radio_entity() + if self._radio_mgr.radio_type != RadioType.ezsp: + await self._radio_mgr.restore_backup(self._radio_mgr.chosen_backup) + return await self._async_create_radio_entry() # We have no way to partially load network settings if no network is formed - if self._current_settings is None: + if self._radio_mgr.current_settings is None: # Since we are going to be restoring the backup anyways, write it to the # radio without overwriting the IEEE but don't take a backup with these # temporary settings - temp_backup = _prevent_overwrite_ezsp_ieee(self._chosen_backup) - await self._restore_backup(temp_backup, create_new=False) - await self._async_load_network_settings() + temp_backup = _prevent_overwrite_ezsp_ieee(self._radio_mgr.chosen_backup) + await self._radio_mgr.restore_backup(temp_backup, create_new=False) + await self._radio_mgr.async_load_network_settings() - assert self._current_settings is not None + assert self._radio_mgr.current_settings is not None if ( - self._current_settings.node_info.ieee == self._chosen_backup.node_info.ieee - or not self._current_settings.network_info.metadata["ezsp"][ + self._radio_mgr.current_settings.node_info.ieee + == self._radio_mgr.chosen_backup.node_info.ieee + or not self._radio_mgr.current_settings.network_info.metadata["ezsp"][ "can_write_custom_eui64" ] ): # No point in prompting the user if the backup doesn't have a new IEEE # address or if there is no way to overwrite the IEEE address a second time - await self._restore_backup(self._chosen_backup) + await self._radio_mgr.restore_backup(self._radio_mgr.chosen_backup) - return await self._async_create_radio_entity() + return await self._async_create_radio_entry() if user_input is not None: - backup = self._chosen_backup + backup = self._radio_mgr.chosen_backup if user_input[OVERWRITE_COORDINATOR_IEEE]: backup = _allow_overwrite_ezsp_ieee(backup) # If the user declined to overwrite the IEEE *and* we wrote the backup to # their empty radio above, restoring it again would be redundant. - await self._restore_backup(backup) + await self._radio_mgr.restore_backup(backup) - return await self._async_create_radio_entity() + return await self._async_create_radio_entry() return self.async_show_form( step_id="maybe_confirm_ezsp_restore", @@ -608,13 +511,16 @@ class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN # config flow logic that interacts with hardware! if user_input is not None or not onboarding.async_is_onboarded(self.hass): # Probe the radio type if we don't have one yet - if self._radio_type is None and not await self._detect_radio_type(): + if ( + self._radio_mgr.radio_type is None + and not await self._radio_mgr.detect_radio_type() + ): # This path probably will not happen now that we have # more precise USB matching unless there is a problem # with the device return self.async_abort(reason="usb_probe_failed") - if self._device_settings is None: + if self._radio_mgr.device_settings is None: return await self.async_step_manual_port_config() return await self.async_step_choose_formation_strategy() @@ -647,7 +553,7 @@ class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN if entry.source != config_entries.SOURCE_IGNORE: return self.async_abort(reason="not_zha_device") - self._device_path = dev_path + self._radio_mgr.device_path = dev_path self._title = description or usb.human_readable_device_name( dev_path, serial_number, @@ -673,13 +579,13 @@ class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN port = DEFAULT_ZHA_ZEROCONF_PORT if "radio_type" in discovery_info.properties: - self._radio_type = self._parse_radio_type( + self._radio_mgr.radio_type = self._radio_mgr.parse_radio_type( discovery_info.properties["radio_type"] ) elif "efr32" in local_name: - self._radio_type = RadioType.ezsp + self._radio_mgr.radio_type = RadioType.ezsp else: - self._radio_type = RadioType.znp + self._radio_mgr.radio_type = RadioType.znp node_name = local_name[: -len(".local")] device_path = f"socket://{discovery_info.host}:{port}" @@ -691,7 +597,7 @@ class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN self.context["title_placeholders"] = {CONF_NAME: node_name} self._title = device_path - self._device_path = device_path + self._radio_mgr.device_path = device_path return await self.async_step_confirm() @@ -705,7 +611,7 @@ class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN return self.async_abort(reason="invalid_hardware_data") name = discovery_data["name"] - radio_type = self._parse_radio_type(discovery_data["radio_type"]) + radio_type = self._radio_mgr.parse_radio_type(discovery_data["radio_type"]) try: device_settings = radio_type.controller.SCHEMA_DEVICE( @@ -720,9 +626,9 @@ class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN ) self._title = name - self._radio_type = radio_type - self._device_path = device_settings[CONF_DEVICE_PATH] - self._device_settings = device_settings + self._radio_mgr.radio_type = radio_type + self._radio_mgr.device_path = device_settings[CONF_DEVICE_PATH] + self._radio_mgr.device_settings = device_settings self.context["title_placeholders"] = {CONF_NAME: name} return await self.async_step_confirm() @@ -736,9 +642,9 @@ class ZhaOptionsFlowHandler(BaseZhaFlow, config_entries.OptionsFlow): super().__init__() self.config_entry = config_entry - self._device_path = config_entry.data[CONF_DEVICE][CONF_DEVICE_PATH] - self._device_settings = config_entry.data[CONF_DEVICE] - self._radio_type = RadioType[config_entry.data[CONF_RADIO_TYPE]] + self._radio_mgr.device_path = config_entry.data[CONF_DEVICE][CONF_DEVICE_PATH] + self._radio_mgr.device_settings = config_entry.data[CONF_DEVICE] + self._radio_mgr.radio_type = RadioType[config_entry.data[CONF_RADIO_TYPE]] self._title = config_entry.title async def async_step_init( @@ -781,9 +687,7 @@ class ZhaOptionsFlowHandler(BaseZhaFlow, config_entries.OptionsFlow): """Confirm the user wants to reset their current radio.""" if user_input is not None: - # Reset the current adapter - async with self._connect_zigpy_app() as app: - await app.reset_network_info() + await self._radio_mgr.async_reset_adapter() return await self.async_step_instruct_unplug() @@ -800,11 +704,11 @@ class ZhaOptionsFlowHandler(BaseZhaFlow, config_entries.OptionsFlow): return self.async_show_form(step_id="instruct_unplug") - async def _async_create_radio_entity(self): + async def _async_create_radio_entry(self): """Re-implementation of the base flow's final step to update the config.""" - device_settings = self._device_settings.copy() + device_settings = self._radio_mgr.device_settings.copy() device_settings[CONF_DEVICE_PATH] = await self.hass.async_add_executor_job( - usb.get_serial_by_id, self._device_path + usb.get_serial_by_id, self._radio_mgr.device_path ) # Avoid creating both `.options` and `.data` by directly writing `data` here @@ -812,7 +716,7 @@ class ZhaOptionsFlowHandler(BaseZhaFlow, config_entries.OptionsFlow): entry=self.config_entry, data={ CONF_DEVICE: device_settings, - CONF_RADIO_TYPE: self._radio_type.name, + CONF_RADIO_TYPE: self._radio_mgr.radio_type.name, }, options=self.config_entry.options, ) diff --git a/homeassistant/components/zha/radio_manager.py b/homeassistant/components/zha/radio_manager.py new file mode 100644 index 00000000000..05629ec8249 --- /dev/null +++ b/homeassistant/components/zha/radio_manager.py @@ -0,0 +1,158 @@ +"""Config flow for ZHA.""" +from __future__ import annotations + +import asyncio +import contextlib +import logging +import os +from typing import Any + +from zigpy.application import ControllerApplication +import zigpy.backups +from zigpy.config import CONF_DEVICE, CONF_DEVICE_PATH +from zigpy.exceptions import NetworkNotFormed + +from homeassistant.core import HomeAssistant + +from .core.const import ( + CONF_DATABASE, + CONF_ZIGPY, + DATA_ZHA, + DATA_ZHA_CONFIG, + DEFAULT_DATABASE_NAME, + RadioType, +) + +# Only the common radio types will be autoprobed, ordered by new device popularity. +# XBee takes too long to probe since it scans through all possible bauds and likely has +# very few users to begin with. +AUTOPROBE_RADIOS = ( + RadioType.ezsp, + RadioType.znp, + RadioType.deconz, + RadioType.zigate, +) + +CONNECT_DELAY_S = 1.0 + +_LOGGER = logging.getLogger(__name__) + + +class ZhaRadioManager: + """Helper class with radio related functionality.""" + + hass: HomeAssistant + + def __init__(self) -> None: + """Initialize ZhaRadioManager instance.""" + self.device_path: str | None = None + self.device_settings: dict[str, Any] | None = None + self.radio_type: RadioType | None = None + self.current_settings: zigpy.backups.NetworkBackup | None = None + self.backups: list[zigpy.backups.NetworkBackup] = [] + self.chosen_backup: zigpy.backups.NetworkBackup | None = None + + @contextlib.asynccontextmanager + async def _connect_zigpy_app(self) -> ControllerApplication: + """Connect to the radio with the current config and then clean up.""" + assert self.radio_type is not None + + config = self.hass.data.get(DATA_ZHA, {}).get(DATA_ZHA_CONFIG, {}) + app_config = config.get(CONF_ZIGPY, {}).copy() + + database_path = config.get( + CONF_DATABASE, + self.hass.config.path(DEFAULT_DATABASE_NAME), + ) + + # Don't create `zigbee.db` if it doesn't already exist + if not await self.hass.async_add_executor_job(os.path.exists, database_path): + database_path = None + + app_config[CONF_DATABASE] = database_path + app_config[CONF_DEVICE] = self.device_settings + app_config = self.radio_type.controller.SCHEMA(app_config) + + app = await self.radio_type.controller.new( + app_config, auto_form=False, start_radio=False + ) + + try: + await app.connect() + yield app + finally: + await app.disconnect() + await asyncio.sleep(CONNECT_DELAY_S) + + async def restore_backup( + self, backup: zigpy.backups.NetworkBackup, **kwargs: Any + ) -> None: + """Restore the provided network backup, passing through kwargs.""" + if self.current_settings is not None and self.current_settings.supersedes( + self.chosen_backup + ): + return + + async with self._connect_zigpy_app() as app: + await app.backups.restore_backup(backup, **kwargs) + + def parse_radio_type(self, radio_type: str) -> RadioType: + """Parse a radio type name, accounting for past aliases.""" + if radio_type == "efr32": + return RadioType.ezsp + + return RadioType[radio_type] + + async def detect_radio_type(self) -> bool: + """Probe all radio types on the current port.""" + for radio in AUTOPROBE_RADIOS: + _LOGGER.debug("Attempting to probe radio type %s", radio) + + dev_config = radio.controller.SCHEMA_DEVICE( + {CONF_DEVICE_PATH: self.device_path} + ) + probe_result = await radio.controller.probe(dev_config) + + if not probe_result: + continue + + # Radio library probing can succeed and return new device settings + if isinstance(probe_result, dict): + dev_config = probe_result + + self.radio_type = radio + self.device_settings = dev_config + + return True + + return False + + async def async_load_network_settings(self, create_backup: bool = False) -> None: + """Connect to the radio and load its current network settings.""" + async with self._connect_zigpy_app() as app: + # Check if the stick has any settings and load them + try: + await app.load_network_info() + except NetworkNotFormed: + pass + else: + self.current_settings = zigpy.backups.NetworkBackup( + network_info=app.state.network_info, + node_info=app.state.node_info, + ) + + if create_backup: + await app.backups.create_backup() + + # The list of backups will always exist + self.backups = app.backups.backups.copy() + + async def async_form_network(self) -> None: + """Form a brand new network.""" + async with self._connect_zigpy_app() as app: + await app.form_network() + + async def async_reset_adapter(self) -> None: + """Reset the current adapter.""" + async with self._connect_zigpy_app() as app: + await app.reset_network_info() diff --git a/tests/components/zha/test_config_flow.py b/tests/components/zha/test_config_flow.py index dd6498fde43..d2001dfda1b 100644 --- a/tests/components/zha/test_config_flow.py +++ b/tests/components/zha/test_config_flow.py @@ -49,7 +49,7 @@ def disable_platform_only(): @pytest.fixture(autouse=True) def reduce_reconnect_timeout(): """Reduces reconnect timeout to speed up tests.""" - with patch("homeassistant.components.zha.config_flow.CONNECT_DELAY_S", 0.01): + with patch("homeassistant.components.zha.radio_manager.CONNECT_DELAY_S", 0.01): yield @@ -76,12 +76,12 @@ def backup(): def mock_detect_radio_type(radio_type=RadioType.ezsp, ret=True): - """Mock `_detect_radio_type` that just sets the appropriate attributes.""" + """Mock `detect_radio_type` that just sets the appropriate attributes.""" async def detect(self): - self._radio_type = radio_type - self._device_settings = radio_type.controller.SCHEMA_DEVICE( - {CONF_DEVICE_PATH: self._device_path} + self.radio_type = radio_type + self.device_settings = radio_type.controller.SCHEMA_DEVICE( + {CONF_DEVICE_PATH: self.device_path} ) return ret @@ -669,7 +669,7 @@ async def test_discovery_already_setup(hass): @patch( - "homeassistant.components.zha.config_flow.ZhaConfigFlowHandler._detect_radio_type", + "homeassistant.components.zha.radio_manager.ZhaRadioManager.detect_radio_type", mock_detect_radio_type(radio_type=RadioType.deconz), ) @patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()])) @@ -707,7 +707,7 @@ async def test_user_flow(hass): @patch( - "homeassistant.components.zha.config_flow.ZhaConfigFlowHandler._detect_radio_type", + "homeassistant.components.zha.radio_manager.ZhaRadioManager.detect_radio_type", mock_detect_radio_type(ret=False), ) @patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()])) @@ -799,12 +799,14 @@ async def test_detect_radio_type_success( """Test detect radios successfully.""" handler = config_flow.ZhaConfigFlowHandler() - handler._device_path = "/dev/null" + handler._radio_mgr.device_path = "/dev/null" - await handler._detect_radio_type() + await handler._radio_mgr.detect_radio_type() - assert handler._radio_type == RadioType.znp - assert handler._device_settings[zigpy.config.CONF_DEVICE_PATH] == "/dev/null" + assert handler._radio_mgr.radio_type == RadioType.znp + assert ( + handler._radio_mgr.device_settings[zigpy.config.CONF_DEVICE_PATH] == "/dev/null" + ) assert bellows_probe.await_count == 1 assert znp_probe.await_count == 1 @@ -825,12 +827,14 @@ async def test_detect_radio_type_success_with_settings( """Test detect radios successfully but probing returns new settings.""" handler = config_flow.ZhaConfigFlowHandler() - handler._device_path = "/dev/null" - await handler._detect_radio_type() + handler._radio_mgr.device_path = "/dev/null" + await handler._radio_mgr.detect_radio_type() - assert handler._radio_type == RadioType.ezsp - assert handler._device_settings["new_setting"] == 123 - assert handler._device_settings[zigpy.config.CONF_DEVICE_PATH] == "/dev/null" + assert handler._radio_mgr.radio_type == RadioType.ezsp + assert handler._radio_mgr.device_settings["new_setting"] == 123 + assert ( + handler._radio_mgr.device_settings[zigpy.config.CONF_DEVICE_PATH] == "/dev/null" + ) assert bellows_probe.await_count == 1 assert znp_probe.await_count == 0 @@ -1047,7 +1051,7 @@ def pick_radio(hass): port_select = f"{port}, s/n: {port.serial_number} - {port.manufacturer}" with patch( - "homeassistant.components.zha.config_flow.ZhaConfigFlowHandler._detect_radio_type", + "homeassistant.components.zha.radio_manager.ZhaRadioManager.detect_radio_type", mock_detect_radio_type(radio_type=radio_type), ): result = await hass.config_entries.flow.async_init( @@ -1631,6 +1635,7 @@ async def test_options_flow_defaults_socket(hass): assert result5["step_id"] == "choose_formation_strategy" +@patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()])) @patch("homeassistant.components.zha.async_setup_entry", return_value=True) async def test_options_flow_restarts_running_zha_if_cancelled(async_setup_entry, hass): """Test options flow restarts a previously-running ZHA if it's cancelled.""" @@ -1683,6 +1688,7 @@ async def test_options_flow_restarts_running_zha_if_cancelled(async_setup_entry, async_setup_entry.assert_called_once_with(hass, entry) +@patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()])) @patch("homeassistant.components.zha.async_setup_entry", AsyncMock(return_value=True)) async def test_options_flow_migration_reset_old_adapter(hass, mock_app): """Test options flow for migrating from an old radio.""" From 607a0e7697a640e524405f5560868125781bdf0c Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 16 Nov 2022 17:36:30 +0100 Subject: [PATCH 0479/1033] Reduce size of get_statistics_during_period WS API response (#82131) --- homeassistant/components/demo/__init__.py | 2 +- .../components/energy/websocket_api.py | 2 +- .../components/recorder/statistics.py | 242 ++++++------ .../components/recorder/websocket_api.py | 38 +- homeassistant/components/sensor/recorder.py | 4 +- homeassistant/components/tibber/sensor.py | 7 +- tests/components/demo/test_init.py | 2 +- tests/components/recorder/common.py | 20 +- tests/components/recorder/test_statistics.py | 284 +++++++------- .../components/recorder/test_websocket_api.py | 207 +++++----- tests/components/sensor/test_recorder.py | 368 +++++++----------- tests/components/tibber/test_statistics.py | 3 +- 12 files changed, 588 insertions(+), 591 deletions(-) diff --git a/homeassistant/components/demo/__init__.py b/homeassistant/components/demo/__init__.py index 7ed989903e5..3bbafefa6ae 100644 --- a/homeassistant/components/demo/__init__.py +++ b/homeassistant/components/demo/__init__.py @@ -267,7 +267,7 @@ async def _insert_sum_statistics( statistic_id = metadata["statistic_id"] last_stats = await get_instance(hass).async_add_executor_job( - get_last_statistics, hass, 1, statistic_id, False + get_last_statistics, hass, 1, statistic_id, False, {"sum"} ) if statistic_id in last_stats: sum_ = last_stats[statistic_id][0]["sum"] or 0 diff --git a/homeassistant/components/energy/websocket_api.py b/homeassistant/components/energy/websocket_api.py index c2b693c0809..293d34ae8d8 100644 --- a/homeassistant/components/energy/websocket_api.py +++ b/homeassistant/components/energy/websocket_api.py @@ -272,8 +272,8 @@ async def ws_get_fossil_energy_consumption( end_time, statistic_ids, "hour", - True, {"energy": UnitOfEnergy.KILO_WATT_HOUR}, + {"mean", "sum"}, ) def _combine_sum_statistics( diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index e361249580f..e0ef46856c9 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -53,13 +53,7 @@ from .db_schema import ( StatisticsRuns, StatisticsShortTerm, ) -from .models import ( - StatisticData, - StatisticMetaData, - StatisticResult, - process_timestamp, - process_timestamp_to_utc_isoformat, -) +from .models import StatisticData, StatisticMetaData, StatisticResult, process_timestamp from .util import ( execute, execute_stmt_lambda_element, @@ -947,6 +941,7 @@ def _reduce_statistics( same_period: Callable[[datetime, datetime], bool], period_start_end: Callable[[datetime], tuple[datetime, datetime]], period: timedelta, + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], ) -> dict[str, list[dict[str, Any]]]: """Reduce hourly statistics to daily or monthly statistics.""" result: dict[str, list[dict[str, Any]]] = defaultdict(list) @@ -963,19 +958,24 @@ def _reduce_statistics( if not same_period(prev_stat["start"], statistic["start"]): start, end = period_start_end(prev_stat["start"]) # The previous statistic was the last entry of the period - result[statistic_id].append( - { - "statistic_id": statistic_id, - "start": start.isoformat(), - "end": end.isoformat(), - "mean": mean(mean_values) if mean_values else None, - "min": min(min_values) if min_values else None, - "max": max(max_values) if max_values else None, - "last_reset": prev_stat.get("last_reset"), - "state": prev_stat.get("state"), - "sum": prev_stat["sum"], - } - ) + row: dict[str, Any] = { + "start": start, + "end": end, + } + if "mean" in types: + row["mean"] = mean(mean_values) if mean_values else None + if "min" in types: + row["min"] = min(min_values) if min_values else None + if "max" in types: + row["max"] = max(max_values) if max_values else None + if "last_reset" in types: + row["last_reset"] = prev_stat.get("last_reset") + if "state" in types: + row["state"] = prev_stat.get("state") + if "sum" in types: + row["sum"] = prev_stat["sum"] + result[statistic_id].append(row) + max_values = [] mean_values = [] min_values = [] @@ -1007,11 +1007,12 @@ def day_start_end(time: datetime) -> tuple[datetime, datetime]: def _reduce_statistics_per_day( - stats: dict[str, list[dict[str, Any]]] + stats: dict[str, list[dict[str, Any]]], + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], ) -> dict[str, list[dict[str, Any]]]: """Reduce hourly statistics to daily statistics.""" - return _reduce_statistics(stats, same_day, day_start_end, timedelta(days=1)) + return _reduce_statistics(stats, same_day, day_start_end, timedelta(days=1), types) def same_week(time1: datetime, time2: datetime) -> bool: @@ -1037,10 +1038,13 @@ def week_start_end(time: datetime) -> tuple[datetime, datetime]: def _reduce_statistics_per_week( stats: dict[str, list[dict[str, Any]]], + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], ) -> dict[str, list[dict[str, Any]]]: """Reduce hourly statistics to weekly statistics.""" - return _reduce_statistics(stats, same_week, week_start_end, timedelta(days=7)) + return _reduce_statistics( + stats, same_week, week_start_end, timedelta(days=7), types + ) def same_month(time1: datetime, time2: datetime) -> bool: @@ -1063,53 +1067,47 @@ def month_start_end(time: datetime) -> tuple[datetime, datetime]: def _reduce_statistics_per_month( stats: dict[str, list[dict[str, Any]]], + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], ) -> dict[str, list[dict[str, Any]]]: """Reduce hourly statistics to monthly statistics.""" - return _reduce_statistics(stats, same_month, month_start_end, timedelta(days=31)) + return _reduce_statistics( + stats, same_month, month_start_end, timedelta(days=31), types + ) def _statistics_during_period_stmt( start_time: datetime, end_time: datetime | None, metadata_ids: list[int] | None, + table: type[Statistics | StatisticsShortTerm], + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], ) -> StatementLambdaElement: """Prepare a database query for statistics during a given period. This prepares a lambda_stmt query, so we don't insert the parameters yet. """ - stmt = lambda_stmt( - lambda: select(*QUERY_STATISTICS).filter(Statistics.start >= start_time) - ) + + columns = [table.metadata_id, table.start] + if "last_reset" in types: + columns.append(table.last_reset) + if "max" in types: + columns.append(table.max) + if "mean" in types: + columns.append(table.mean) + if "min" in types: + columns.append(table.min) + if "state" in types: + columns.append(table.state) + if "sum" in types: + columns.append(table.sum) + + stmt = lambda_stmt(lambda: select(columns).filter(table.start >= start_time)) if end_time is not None: - stmt += lambda q: q.filter(Statistics.start < end_time) + stmt += lambda q: q.filter(table.start < end_time) if metadata_ids: - stmt += lambda q: q.filter(Statistics.metadata_id.in_(metadata_ids)) - stmt += lambda q: q.order_by(Statistics.metadata_id, Statistics.start) - return stmt - - -def _statistics_during_period_stmt_short_term( - start_time: datetime, - end_time: datetime | None, - metadata_ids: list[int] | None, -) -> StatementLambdaElement: - """Prepare a database query for short term statistics during a given period. - - This prepares a lambda_stmt query, so we don't insert the parameters yet. - """ - stmt = lambda_stmt( - lambda: select(*QUERY_STATISTICS_SHORT_TERM).filter( - StatisticsShortTerm.start >= start_time - ) - ) - if end_time is not None: - stmt += lambda q: q.filter(StatisticsShortTerm.start < end_time) - if metadata_ids: - stmt += lambda q: q.filter(StatisticsShortTerm.metadata_id.in_(metadata_ids)) - stmt += lambda q: q.order_by( - StatisticsShortTerm.metadata_id, StatisticsShortTerm.start - ) + stmt += lambda q: q.filter(table.metadata_id.in_(metadata_ids)) + stmt += lambda q: q.order_by(table.metadata_id, table.start) return stmt @@ -1119,7 +1117,7 @@ def _get_max_mean_min_statistic_in_sub_period( start_time: datetime | None, end_time: datetime | None, table: type[Statistics | StatisticsShortTerm], - types: set[str], + types: set[Literal["max", "mean", "min", "change"]], metadata_id: int, ) -> None: """Return max, mean and min during the period.""" @@ -1160,7 +1158,7 @@ def _get_max_mean_min_statistic( tail_end_time: datetime | None, tail_only: bool, metadata_id: int, - types: set[str], + types: set[Literal["max", "mean", "min", "change"]], ) -> dict[str, float | None]: """Return max, mean and min during the period. @@ -1380,7 +1378,7 @@ def statistic_during_period( start_time: datetime | None, end_time: datetime | None, statistic_id: str, - types: set[str] | None, + types: set[Literal["max", "mean", "min", "change"]] | None, units: dict[str, str] | None, ) -> dict[str, Any]: """Return a statistic data point for the UTC period start_time - end_time.""" @@ -1534,11 +1532,11 @@ def statistic_during_period( def statistics_during_period( hass: HomeAssistant, start_time: datetime, - end_time: datetime | None = None, - statistic_ids: list[str] | None = None, - period: Literal["5minute", "day", "hour", "week", "month"] = "hour", - start_time_as_datetime: bool = False, - units: dict[str, str] | None = None, + end_time: datetime | None, + statistic_ids: list[str] | None, + period: Literal["5minute", "day", "hour", "week", "month"], + units: dict[str, str] | None, + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], ) -> dict[str, list[dict[str, Any]]]: """Return statistic data points during UTC period start_time - end_time. @@ -1556,14 +1554,12 @@ def statistics_during_period( if statistic_ids is not None: metadata_ids = [metadata_id for metadata_id, _ in metadata.values()] - if period == "5minute": - table = StatisticsShortTerm - stmt = _statistics_during_period_stmt_short_term( - start_time, end_time, metadata_ids - ) - else: - table = Statistics - stmt = _statistics_during_period_stmt(start_time, end_time, metadata_ids) + table: type[Statistics | StatisticsShortTerm] = ( + Statistics if period != "5minute" else StatisticsShortTerm + ) + stmt = _statistics_during_period_stmt( + start_time, end_time, metadata_ids, table, types + ) stats = execute_stmt_lambda_element(session, stmt) if not stats: @@ -1579,8 +1575,8 @@ def statistics_during_period( True, table, start_time, - start_time_as_datetime, units, + types, ) result = _sorted_statistics_to_dict( @@ -1592,17 +1588,17 @@ def statistics_during_period( True, table, start_time, - True, units, + types, ) if period == "day": - return _reduce_statistics_per_day(result) + return _reduce_statistics_per_day(result, types) if period == "week": - return _reduce_statistics_per_week(result) + return _reduce_statistics_per_week(result, types) - return _reduce_statistics_per_month(result) + return _reduce_statistics_per_month(result, types) def _get_last_statistics_stmt( @@ -1637,6 +1633,7 @@ def _get_last_statistics( statistic_id: str, convert_units: bool, table: type[Statistics | StatisticsShortTerm], + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], ) -> dict[str, list[dict]]: """Return the last number_of_stats statistics for a given statistic_id.""" statistic_ids = [statistic_id] @@ -1665,26 +1662,34 @@ def _get_last_statistics( convert_units, table, None, - False, None, + types, ) def get_last_statistics( - hass: HomeAssistant, number_of_stats: int, statistic_id: str, convert_units: bool + hass: HomeAssistant, + number_of_stats: int, + statistic_id: str, + convert_units: bool, + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], ) -> dict[str, list[dict]]: """Return the last number_of_stats statistics for a statistic_id.""" return _get_last_statistics( - hass, number_of_stats, statistic_id, convert_units, Statistics + hass, number_of_stats, statistic_id, convert_units, Statistics, types ) def get_last_short_term_statistics( - hass: HomeAssistant, number_of_stats: int, statistic_id: str, convert_units: bool + hass: HomeAssistant, + number_of_stats: int, + statistic_id: str, + convert_units: bool, + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], ) -> dict[str, list[dict]]: """Return the last number_of_stats short term statistics for a statistic_id.""" return _get_last_statistics( - hass, number_of_stats, statistic_id, convert_units, StatisticsShortTerm + hass, number_of_stats, statistic_id, convert_units, StatisticsShortTerm, types ) @@ -1720,6 +1725,7 @@ def _latest_short_term_statistics_stmt( def get_latest_short_term_statistics( hass: HomeAssistant, statistic_ids: list[str], + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], metadata: dict[str, tuple[int, StatisticMetaData]] | None = None, ) -> dict[str, list[dict]]: """Return the latest short term statistics for a list of statistic_ids.""" @@ -1749,8 +1755,8 @@ def get_latest_short_term_statistics( False, StatisticsShortTerm, None, - False, None, + types, ) @@ -1759,31 +1765,38 @@ def _statistics_at_time( metadata_ids: set[int], table: type[Statistics | StatisticsShortTerm], start_time: datetime, + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], ) -> list | None: """Return last known statistics, earlier than start_time, for the metadata_ids.""" - # Fetch metadata for the given (or all) statistic_ids - if table == StatisticsShortTerm: - base_query = QUERY_STATISTICS_SHORT_TERM - else: - base_query = QUERY_STATISTICS + columns = [table.metadata_id, table.start] + if "last_reset" in types: + columns.append(table.last_reset) + if "max" in types: + columns.append(table.max) + if "mean" in types: + columns.append(table.mean) + if "min" in types: + columns.append(table.min) + if "state" in types: + columns.append(table.state) + if "sum" in types: + columns.append(table.sum) - query = session.query(*base_query) + stmt = lambda_stmt(lambda: select(columns)) most_recent_statistic_ids = ( - session.query( - func.max(table.id).label("max_id"), - ) + lambda_stmt(lambda: select(func.max(table.id).label("max_id"))) .filter(table.start < start_time) .filter(table.metadata_id.in_(metadata_ids)) + .group_by(table.metadata_id) + .subquery() ) - most_recent_statistic_ids = most_recent_statistic_ids.group_by(table.metadata_id) - most_recent_statistic_ids = most_recent_statistic_ids.subquery() - query = query.join( + + stmt += lambda q: q.join( most_recent_statistic_ids, table.id == most_recent_statistic_ids.c.max_id, ) - - return execute(query) + return execute_stmt_lambda_element(session, stmt) def _sorted_statistics_to_dict( @@ -1795,8 +1808,8 @@ def _sorted_statistics_to_dict( convert_units: bool, table: type[Statistics | StatisticsShortTerm], start_time: datetime | None, - start_time_as_datetime: bool, units: dict[str, str] | None, + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], ) -> dict[str, list[dict]]: """Convert SQL results into JSON friendly data structure.""" result: dict = defaultdict(list) @@ -1822,7 +1835,9 @@ def _sorted_statistics_to_dict( # Fetch last known statistics for the needed metadata IDs if need_stat_at_start_time: assert start_time # Can not be None if need_stat_at_start_time is not empty - tmp = _statistics_at_time(session, need_stat_at_start_time, table, start_time) + tmp = _statistics_at_time( + session, need_stat_at_start_time, table, start_time, types + ) if tmp: for stat in tmp: stats_at_start_time[stat.metadata_id] = (stat,) @@ -1841,21 +1856,24 @@ def _sorted_statistics_to_dict( for db_state in chain(stats_at_start_time.get(meta_id, ()), group): start = process_timestamp(db_state.start) end = start + table.duration - ent_results.append( - { - "statistic_id": statistic_id, - "start": start if start_time_as_datetime else start.isoformat(), - "end": end.isoformat(), - "mean": convert(db_state.mean), - "min": convert(db_state.min), - "max": convert(db_state.max), - "last_reset": process_timestamp_to_utc_isoformat( - db_state.last_reset - ), - "state": convert(db_state.state), - "sum": convert(db_state.sum), - } - ) + row = { + "start": start, + "end": end, + } + if "mean" in types: + row["mean"] = convert(db_state.mean) + if "min" in types: + row["min"] = convert(db_state.min) + if "max" in types: + row["max"] = convert(db_state.max) + if "last_reset" in types: + row["last_reset"] = process_timestamp(db_state.last_reset) + if "state" in types: + row["state"] = convert(db_state.state) + if "sum" in types: + row["sum"] = convert(db_state.sum) + + ent_results.append(row) # Filter out the empty lists if some states had 0 results. return {metadata[key]["statistic_id"]: val for key, val in result.items() if val} diff --git a/homeassistant/components/recorder/websocket_api.py b/homeassistant/components/recorder/websocket_api.py index 9b2ef417755..a9fe1589654 100644 --- a/homeassistant/components/recorder/websocket_api.py +++ b/homeassistant/components/recorder/websocket_api.py @@ -65,7 +65,7 @@ def _ws_get_statistic_during_period( start_time: dt | None, end_time: dt | None, statistic_id: str, - types: set[str] | None, + types: set[Literal["max", "mean", "min", "change"]] | None, units: dict[str, str], ) -> str: """Fetch statistics and convert them to json in the executor.""" @@ -101,7 +101,9 @@ def _ws_get_statistic_during_period( } ), vol.Optional("statistic_id"): str, - vol.Optional("types"): vol.All([str], vol.Coerce(set)), + vol.Optional("types"): vol.All( + [vol.Any("max", "mean", "min", "change")], vol.Coerce(set) + ), vol.Optional("units"): vol.Schema( { vol.Optional("distance"): vol.In(DistanceConverter.VALID_UNITS), @@ -210,16 +212,27 @@ def _ws_get_statistics_during_period( statistic_ids: list[str] | None, period: Literal["5minute", "day", "hour", "week", "month"], units: dict[str, str], + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], ) -> str: """Fetch statistics and convert them to json in the executor.""" - return JSON_DUMP( - messages.result_message( - msg_id, - statistics_during_period( - hass, start_time, end_time, statistic_ids, period, units=units - ), - ) + result = statistics_during_period( + hass, + start_time, + end_time, + statistic_ids, + period, + units, + types, ) + for statistic_id in result: + for item in result[statistic_id]: + if (start := item.get("start")) is not None: + item["start"] = int(start.timestamp() * 1000) + if (end := item.get("end")) is not None: + item["end"] = int(end.timestamp() * 1000) + if (last_reset := item.get("last_reset")) is not None: + item["last_reset"] = int(last_reset.timestamp() * 1000) + return JSON_DUMP(messages.result_message(msg_id, result)) async def ws_handle_get_statistics_during_period( @@ -244,6 +257,8 @@ async def ws_handle_get_statistics_during_period( else: end_time = None + if (types := msg.get("types")) is None: + types = {"last_reset", "max", "mean", "min", "state", "sum"} connection.send_message( await get_instance(hass).async_add_executor_job( _ws_get_statistics_during_period, @@ -254,6 +269,7 @@ async def ws_handle_get_statistics_during_period( msg.get("statistic_ids"), msg.get("period"), msg.get("units"), + types, ) ) @@ -277,6 +293,10 @@ async def ws_handle_get_statistics_during_period( vol.Optional("volume"): vol.In(VolumeConverter.VALID_UNITS), } ), + vol.Optional("types"): vol.All( + [vol.Any("last_reset", "max", "mean", "min", "state", "sum")], + vol.Coerce(set), + ), } ) @websocket_api.async_response diff --git a/homeassistant/components/sensor/recorder.py b/homeassistant/components/sensor/recorder.py index 5e853ec3c74..f4b3897492d 100644 --- a/homeassistant/components/sensor/recorder.py +++ b/homeassistant/components/sensor/recorder.py @@ -450,7 +450,7 @@ def _compile_statistics( # noqa: C901 to_query.append(entity_id) last_stats = statistics.get_latest_short_term_statistics( - hass, to_query, metadata=old_metadatas + hass, to_query, {"last_reset", "state", "sum"}, metadata=old_metadatas ) for ( # pylint: disable=too-many-nested-blocks entity_id, @@ -508,6 +508,8 @@ def _compile_statistics( # noqa: C901 if entity_id in last_stats: # We have compiled history for this sensor before, use that as a starting point last_reset = old_last_reset = last_stats[entity_id][0]["last_reset"] + if old_last_reset is not None: + last_reset = old_last_reset = old_last_reset.isoformat() new_state = old_state = last_stats[entity_id][0]["state"] _sum = last_stats[entity_id][0]["sum"] or 0.0 diff --git a/homeassistant/components/tibber/sensor.py b/homeassistant/components/tibber/sensor.py index 73e9db08c03..066e1f921c5 100644 --- a/homeassistant/components/tibber/sensor.py +++ b/homeassistant/components/tibber/sensor.py @@ -591,7 +591,7 @@ class TibberDataCoordinator(DataUpdateCoordinator): ) last_stats = await get_instance(self.hass).async_add_executor_job( - get_last_statistics, self.hass, 1, statistic_id, True + get_last_statistics, self.hass, 1, statistic_id, True, {} ) if not last_stats: @@ -613,7 +613,7 @@ class TibberDataCoordinator(DataUpdateCoordinator): else home.hourly_consumption_data ) - from_time = dt_util.parse_datetime(hourly_data[0]["from"]) + from_time = hourly_data[0]["from"] if from_time is None: continue start = from_time - timedelta(hours=1) @@ -624,7 +624,8 @@ class TibberDataCoordinator(DataUpdateCoordinator): None, [statistic_id], "hour", - True, + None, + {"sum"}, ) _sum = stat[statistic_id][0]["sum"] last_stats_time = stat[statistic_id][0]["start"] diff --git a/tests/components/demo/test_init.py b/tests/components/demo/test_init.py index c1b9d4c436e..1dd049f8ab4 100644 --- a/tests/components/demo/test_init.py +++ b/tests/components/demo/test_init.py @@ -114,7 +114,7 @@ async def test_demo_statistics_growth(recorder_mock, mock_history, hass): await async_wait_recording_done(hass) statistics = await get_instance(hass).async_add_executor_job( - get_last_statistics, hass, 1, statistic_id, False + get_last_statistics, hass, 1, statistic_id, False, {"sum"} ) assert statistics[statistic_id][0]["sum"] > 2**20 assert statistics[statistic_id][0]["sum"] <= (2**20 + 24) diff --git a/tests/components/recorder/common.py b/tests/components/recorder/common.py index 0ddc76e4423..701d05175a7 100644 --- a/tests/components/recorder/common.py +++ b/tests/components/recorder/common.py @@ -5,7 +5,7 @@ import asyncio from dataclasses import dataclass from datetime import datetime import time -from typing import Any, cast +from typing import Any, Literal, cast from sqlalchemy import create_engine from sqlalchemy.orm.session import Session @@ -137,3 +137,21 @@ def run_information_with_session( session.expunge(res) return cast(RecorderRuns, res) return res + + +def statistics_during_period( + hass: HomeAssistant, + start_time: datetime, + end_time: datetime | None = None, + statistic_ids: list[str] | None = None, + period: Literal["5minute", "day", "hour", "week", "month"] = "hour", + units: dict[str, str] | None = None, + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]] + | None = None, +) -> dict[str, list[dict[str, Any]]]: + """Call statistics_during_period with defaults for simpler tests.""" + if types is None: + types = {"last_reset", "max", "mean", "min", "state", "sum"} + return statistics.statistics_during_period( + hass, start_time, end_time, statistic_ids, period, units, types + ) diff --git a/tests/components/recorder/test_statistics.py b/tests/components/recorder/test_statistics.py index aae6fcf91cb..26c6734178c 100644 --- a/tests/components/recorder/test_statistics.py +++ b/tests/components/recorder/test_statistics.py @@ -14,7 +14,7 @@ from homeassistant.components import recorder from homeassistant.components.recorder import history, statistics from homeassistant.components.recorder.const import SQLITE_URL_PREFIX from homeassistant.components.recorder.db_schema import StatisticsShortTerm -from homeassistant.components.recorder.models import process_timestamp_to_utc_isoformat +from homeassistant.components.recorder.models import process_timestamp from homeassistant.components.recorder.statistics import ( async_add_external_statistics, async_import_statistics, @@ -25,7 +25,6 @@ from homeassistant.components.recorder.statistics import ( get_latest_short_term_statistics, get_metadata, list_statistic_ids, - statistics_during_period, ) from homeassistant.components.recorder.util import session_scope from homeassistant.const import TEMP_CELSIUS @@ -35,7 +34,12 @@ from homeassistant.helpers import recorder as recorder_helper from homeassistant.setup import setup_component import homeassistant.util.dt as dt_util -from .common import async_wait_recording_done, do_adhoc_statistics, wait_recording_done +from .common import ( + async_wait_recording_done, + do_adhoc_statistics, + statistics_during_period, + wait_recording_done, +) from tests.common import get_test_home_assistant, mock_registry @@ -52,22 +56,29 @@ def test_compile_hourly_statistics(hass_recorder): assert dict(states) == dict(hist) # Should not fail if there is nothing there yet - stats = get_latest_short_term_statistics(hass, ["sensor.test1"]) + stats = get_latest_short_term_statistics( + hass, ["sensor.test1"], {"last_reset", "max", "mean", "min", "state", "sum"} + ) assert stats == {} for kwargs in ({}, {"statistic_ids": ["sensor.test1"]}): stats = statistics_during_period(hass, zero, period="5minute", **kwargs) assert stats == {} - stats = get_last_short_term_statistics(hass, 0, "sensor.test1", True) + stats = get_last_short_term_statistics( + hass, + 0, + "sensor.test1", + True, + {"last_reset", "max", "mean", "min", "state", "sum"}, + ) assert stats == {} do_adhoc_statistics(hass, start=zero) do_adhoc_statistics(hass, start=four) wait_recording_done(hass) expected_1 = { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(14.915254237288135), "min": approx(10.0), "max": approx(20.0), @@ -76,9 +87,8 @@ def test_compile_hourly_statistics(hass_recorder): "sum": None, } expected_2 = { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(four), - "end": process_timestamp_to_utc_isoformat(four + timedelta(minutes=5)), + "start": process_timestamp(four), + "end": process_timestamp(four + timedelta(minutes=5)), "mean": approx(20.0), "min": approx(20.0), "max": approx(20.0), @@ -86,14 +96,8 @@ def test_compile_hourly_statistics(hass_recorder): "state": None, "sum": None, } - expected_stats1 = [ - {**expected_1, "statistic_id": "sensor.test1"}, - {**expected_2, "statistic_id": "sensor.test1"}, - ] - expected_stats2 = [ - {**expected_1, "statistic_id": "sensor.test2"}, - {**expected_2, "statistic_id": "sensor.test2"}, - ] + expected_stats1 = [expected_1, expected_2] + expected_stats2 = [expected_1, expected_2] # Test statistics_during_period stats = statistics_during_period(hass, zero, period="5minute") @@ -119,32 +123,71 @@ def test_compile_hourly_statistics(hass_recorder): assert stats == {} # Test get_last_short_term_statistics and get_latest_short_term_statistics - stats = get_last_short_term_statistics(hass, 0, "sensor.test1", True) + stats = get_last_short_term_statistics( + hass, + 0, + "sensor.test1", + True, + {"last_reset", "max", "mean", "min", "state", "sum"}, + ) assert stats == {} - stats = get_last_short_term_statistics(hass, 1, "sensor.test1", True) - assert stats == {"sensor.test1": [{**expected_2, "statistic_id": "sensor.test1"}]} + stats = get_last_short_term_statistics( + hass, + 1, + "sensor.test1", + True, + {"last_reset", "max", "mean", "min", "state", "sum"}, + ) + assert stats == {"sensor.test1": [expected_2]} - stats = get_latest_short_term_statistics(hass, ["sensor.test1"]) - assert stats == {"sensor.test1": [{**expected_2, "statistic_id": "sensor.test1"}]} + stats = get_latest_short_term_statistics( + hass, ["sensor.test1"], {"last_reset", "max", "mean", "min", "state", "sum"} + ) + assert stats == {"sensor.test1": [expected_2]} metadata = get_metadata(hass, statistic_ids=['sensor.test1"']) - stats = get_latest_short_term_statistics(hass, ["sensor.test1"], metadata=metadata) - assert stats == {"sensor.test1": [{**expected_2, "statistic_id": "sensor.test1"}]} + stats = get_latest_short_term_statistics( + hass, + ["sensor.test1"], + {"last_reset", "max", "mean", "min", "state", "sum"}, + metadata=metadata, + ) + assert stats == {"sensor.test1": [expected_2]} - stats = get_last_short_term_statistics(hass, 2, "sensor.test1", True) + stats = get_last_short_term_statistics( + hass, + 2, + "sensor.test1", + True, + {"last_reset", "max", "mean", "min", "state", "sum"}, + ) assert stats == {"sensor.test1": expected_stats1[::-1]} - stats = get_last_short_term_statistics(hass, 3, "sensor.test1", True) + stats = get_last_short_term_statistics( + hass, + 3, + "sensor.test1", + True, + {"last_reset", "max", "mean", "min", "state", "sum"}, + ) assert stats == {"sensor.test1": expected_stats1[::-1]} - stats = get_last_short_term_statistics(hass, 1, "sensor.test3", True) + stats = get_last_short_term_statistics( + hass, + 1, + "sensor.test3", + True, + {"last_reset", "max", "mean", "min", "state", "sum"}, + ) assert stats == {} instance.get_session().query(StatisticsShortTerm).delete() # Should not fail there is nothing in the table - stats = get_latest_short_term_statistics(hass, ["sensor.test1"]) + stats = get_latest_short_term_statistics( + hass, ["sensor.test1"], {"last_reset", "max", "mean", "min", "state", "sum"} + ) assert stats == {} @@ -218,9 +261,8 @@ def test_compile_periodic_statistics_exception( do_adhoc_statistics(hass, start=now + timedelta(minutes=5)) wait_recording_done(hass) expected_1 = { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(now), - "end": process_timestamp_to_utc_isoformat(now + timedelta(minutes=5)), + "start": process_timestamp(now), + "end": process_timestamp(now + timedelta(minutes=5)), "mean": None, "min": None, "max": None, @@ -229,9 +271,8 @@ def test_compile_periodic_statistics_exception( "sum": None, } expected_2 = { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(now + timedelta(minutes=5)), - "end": process_timestamp_to_utc_isoformat(now + timedelta(minutes=10)), + "start": process_timestamp(now + timedelta(minutes=5)), + "end": process_timestamp(now + timedelta(minutes=10)), "mean": None, "min": None, "max": None, @@ -239,17 +280,9 @@ def test_compile_periodic_statistics_exception( "state": None, "sum": None, } - expected_stats1 = [ - {**expected_1, "statistic_id": "sensor.test1"}, - {**expected_2, "statistic_id": "sensor.test1"}, - ] - expected_stats2 = [ - {**expected_2, "statistic_id": "sensor.test2"}, - ] - expected_stats3 = [ - {**expected_1, "statistic_id": "sensor.test3"}, - {**expected_2, "statistic_id": "sensor.test3"}, - ] + expected_stats1 = [expected_1, expected_2] + expected_stats2 = [expected_2] + expected_stats3 = [expected_1, expected_2] stats = statistics_during_period(hass, now, period="5minute") assert stats == { @@ -286,15 +319,20 @@ def test_rename_entity(hass_recorder): for kwargs in ({}, {"statistic_ids": ["sensor.test1"]}): stats = statistics_during_period(hass, zero, period="5minute", **kwargs) assert stats == {} - stats = get_last_short_term_statistics(hass, 0, "sensor.test1", True) + stats = get_last_short_term_statistics( + hass, + 0, + "sensor.test1", + True, + {"last_reset", "max", "mean", "min", "state", "sum"}, + ) assert stats == {} do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) expected_1 = { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(14.915254237288135), "min": approx(10.0), "max": approx(20.0), @@ -302,15 +340,9 @@ def test_rename_entity(hass_recorder): "state": None, "sum": None, } - expected_stats1 = [ - {**expected_1, "statistic_id": "sensor.test1"}, - ] - expected_stats2 = [ - {**expected_1, "statistic_id": "sensor.test2"}, - ] - expected_stats99 = [ - {**expected_1, "statistic_id": "sensor.test99"}, - ] + expected_stats1 = [expected_1] + expected_stats2 = [expected_1] + expected_stats99 = [expected_1] stats = statistics_during_period(hass, zero, period="5minute") assert stats == {"sensor.test1": expected_stats1, "sensor.test2": expected_stats2} @@ -353,15 +385,20 @@ def test_rename_entity_collision(hass_recorder, caplog): for kwargs in ({}, {"statistic_ids": ["sensor.test1"]}): stats = statistics_during_period(hass, zero, period="5minute", **kwargs) assert stats == {} - stats = get_last_short_term_statistics(hass, 0, "sensor.test1", True) + stats = get_last_short_term_statistics( + hass, + 0, + "sensor.test1", + True, + {"last_reset", "max", "mean", "min", "state", "sum"}, + ) assert stats == {} do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) expected_1 = { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(14.915254237288135), "min": approx(10.0), "max": approx(20.0), @@ -369,12 +406,8 @@ def test_rename_entity_collision(hass_recorder, caplog): "state": None, "sum": None, } - expected_stats1 = [ - {**expected_1, "statistic_id": "sensor.test1"}, - ] - expected_stats2 = [ - {**expected_1, "statistic_id": "sensor.test2"}, - ] + expected_stats1 = [expected_1] + expected_stats2 = [expected_1] stats = statistics_during_period(hass, zero, period="5minute") assert stats == {"sensor.test1": expected_stats1, "sensor.test2": expected_stats2} @@ -465,7 +498,7 @@ async def test_import_statistics( zero = dt_util.utcnow() last_reset = dt_util.parse_datetime(last_reset_str) if last_reset_str else None - last_reset_utc_str = dt_util.as_utc(last_reset).isoformat() if last_reset else None + last_reset_utc = dt_util.as_utc(last_reset) if last_reset else None period1 = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1) period2 = zero.replace(minute=0, second=0, microsecond=0) + timedelta(hours=2) @@ -497,24 +530,22 @@ async def test_import_statistics( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": process_timestamp(period1), + "end": process_timestamp(period1 + timedelta(hours=1)), "max": None, "mean": None, "min": None, - "last_reset": last_reset_utc_str, + "last_reset": last_reset_utc, "state": approx(0.0), "sum": approx(2.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": process_timestamp(period2), + "end": process_timestamp(period2 + timedelta(hours=1)), "max": None, "mean": None, "min": None, - "last_reset": last_reset_utc_str, + "last_reset": last_reset_utc, "state": approx(1.0), "sum": approx(3.0), }, @@ -546,17 +577,22 @@ async def test_import_statistics( }, ) } - last_stats = get_last_statistics(hass, 1, statistic_id, True) + last_stats = get_last_statistics( + hass, + 1, + statistic_id, + True, + {"last_reset", "max", "mean", "min", "state", "sum"}, + ) assert last_stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": process_timestamp(period2), + "end": process_timestamp(period2 + timedelta(hours=1)), "max": None, "mean": None, "min": None, - "last_reset": last_reset_utc_str, + "last_reset": last_reset_utc, "state": approx(1.0), "sum": approx(3.0), }, @@ -576,9 +612,8 @@ async def test_import_statistics( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": process_timestamp(period1), + "end": process_timestamp(period1 + timedelta(hours=1)), "max": None, "mean": None, "min": None, @@ -587,13 +622,12 @@ async def test_import_statistics( "sum": approx(6.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": process_timestamp(period2), + "end": process_timestamp(period2 + timedelta(hours=1)), "max": None, "mean": None, "min": None, - "last_reset": last_reset_utc_str, + "last_reset": last_reset_utc, "state": approx(1.0), "sum": approx(3.0), }, @@ -643,24 +677,22 @@ async def test_import_statistics( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": process_timestamp(period1), + "end": process_timestamp(period1 + timedelta(hours=1)), "max": approx(1.0), "mean": approx(2.0), "min": approx(3.0), - "last_reset": last_reset_utc_str, + "last_reset": last_reset_utc, "state": approx(4.0), "sum": approx(5.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": process_timestamp(period2), + "end": process_timestamp(period2 + timedelta(hours=1)), "max": None, "mean": None, "min": None, - "last_reset": last_reset_utc_str, + "last_reset": last_reset_utc, "state": approx(1.0), "sum": approx(3.0), }, @@ -686,24 +718,22 @@ async def test_import_statistics( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": process_timestamp(period1), + "end": process_timestamp(period1 + timedelta(hours=1)), "max": approx(1.0), "mean": approx(2.0), "min": approx(3.0), - "last_reset": last_reset_utc_str, + "last_reset": last_reset_utc, "state": approx(4.0), "sum": approx(5.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": process_timestamp(period2), + "end": process_timestamp(period2 + timedelta(hours=1)), "max": None, "mean": None, "min": None, - "last_reset": last_reset_utc_str, + "last_reset": last_reset_utc, "state": approx(1.0), "sum": approx(1000 * 1000 + 3.0), }, @@ -947,9 +977,8 @@ def test_weekly_statistics(hass_recorder, caplog, timezone): assert stats == { "test:total_energy_import": [ { - "statistic_id": "test:total_energy_import", - "start": week1_start.isoformat(), - "end": week1_end.isoformat(), + "start": week1_start, + "end": week1_end, "max": None, "mean": None, "min": None, @@ -958,9 +987,8 @@ def test_weekly_statistics(hass_recorder, caplog, timezone): "sum": 3.0, }, { - "statistic_id": "test:total_energy_import", - "start": week2_start.isoformat(), - "end": week2_end.isoformat(), + "start": week2_start, + "end": week2_end, "max": None, "mean": None, "min": None, @@ -980,9 +1008,8 @@ def test_weekly_statistics(hass_recorder, caplog, timezone): assert stats == { "test:total_energy_import": [ { - "statistic_id": "test:total_energy_import", - "start": week1_start.isoformat(), - "end": week1_end.isoformat(), + "start": week1_start, + "end": week1_end, "max": None, "mean": None, "min": None, @@ -991,9 +1018,8 @@ def test_weekly_statistics(hass_recorder, caplog, timezone): "sum": 3.0, }, { - "statistic_id": "test:total_energy_import", - "start": week2_start.isoformat(), - "end": week2_end.isoformat(), + "start": week2_start, + "end": week2_end, "max": None, "mean": None, "min": None, @@ -1085,9 +1111,8 @@ def test_monthly_statistics(hass_recorder, caplog, timezone): assert stats == { "test:total_energy_import": [ { - "statistic_id": "test:total_energy_import", - "start": sep_start.isoformat(), - "end": sep_end.isoformat(), + "start": sep_start, + "end": sep_end, "max": None, "mean": None, "min": None, @@ -1096,9 +1121,8 @@ def test_monthly_statistics(hass_recorder, caplog, timezone): "sum": approx(3.0), }, { - "statistic_id": "test:total_energy_import", - "start": oct_start.isoformat(), - "end": oct_end.isoformat(), + "start": oct_start, + "end": oct_end, "max": None, "mean": None, "min": None, @@ -1122,9 +1146,8 @@ def test_monthly_statistics(hass_recorder, caplog, timezone): assert stats == { "test:total_energy_import": [ { - "statistic_id": "test:total_energy_import", - "start": sep_start.isoformat(), - "end": sep_end.isoformat(), + "start": sep_start, + "end": sep_end, "max": None, "mean": None, "min": None, @@ -1133,9 +1156,8 @@ def test_monthly_statistics(hass_recorder, caplog, timezone): "sum": approx(3.0), }, { - "statistic_id": "test:total_energy_import", - "start": oct_start.isoformat(), - "end": oct_end.isoformat(), + "start": oct_start, + "end": oct_end, "max": None, "mean": None, "min": None, diff --git a/tests/components/recorder/test_websocket_api.py b/tests/components/recorder/test_websocket_api.py index 423ab4bbc52..855737a4d7d 100644 --- a/tests/components/recorder/test_websocket_api.py +++ b/tests/components/recorder/test_websocket_api.py @@ -17,7 +17,6 @@ from homeassistant.components.recorder.statistics import ( get_last_statistics, get_metadata, list_statistic_ids, - statistics_during_period, ) from homeassistant.helpers import recorder as recorder_helper from homeassistant.setup import async_setup_component @@ -29,6 +28,7 @@ from .common import ( async_wait_recording_done, create_engine_test, do_adhoc_statistics, + statistics_during_period, ) from tests.common import async_fire_time_changed @@ -167,9 +167,8 @@ async def test_statistics_during_period(recorder_mock, hass, hass_ws_client): assert response["result"] == { "sensor.test": [ { - "statistic_id": "sensor.test", - "start": now.isoformat(), - "end": (now + timedelta(minutes=5)).isoformat(), + "start": int(now.timestamp() * 1000), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), "mean": approx(10), "min": approx(10), "max": approx(10), @@ -180,6 +179,28 @@ async def test_statistics_during_period(recorder_mock, hass, hass_ws_client): ] } + await client.send_json( + { + "id": 3, + "type": "recorder/statistics_during_period", + "start_time": now.isoformat(), + "statistic_ids": ["sensor.test"], + "period": "5minute", + "types": ["mean"], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == { + "sensor.test": [ + { + "start": int(now.timestamp() * 1000), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), + "mean": approx(10), + } + ] + } + @freeze_time(datetime.datetime(2022, 10, 21, 7, 25, tzinfo=datetime.timezone.utc)) @pytest.mark.parametrize("offset", (0, 1, 2)) @@ -895,9 +916,8 @@ async def test_statistics_during_period_unit_conversion( assert response["result"] == { "sensor.test": [ { - "statistic_id": "sensor.test", - "start": now.isoformat(), - "end": (now + timedelta(minutes=5)).isoformat(), + "start": int(now.timestamp() * 1000), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), "mean": approx(value), "min": approx(value), "max": approx(value), @@ -924,9 +944,8 @@ async def test_statistics_during_period_unit_conversion( assert response["result"] == { "sensor.test": [ { - "statistic_id": "sensor.test", - "start": now.isoformat(), - "end": (now + timedelta(minutes=5)).isoformat(), + "start": int(now.timestamp() * 1000), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), "mean": approx(converted_value), "min": approx(converted_value), "max": approx(converted_value), @@ -989,9 +1008,8 @@ async def test_sum_statistics_during_period_unit_conversion( assert response["result"] == { "sensor.test": [ { - "statistic_id": "sensor.test", - "start": now.isoformat(), - "end": (now + timedelta(minutes=5)).isoformat(), + "start": int(now.timestamp() * 1000), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), "mean": None, "min": None, "max": None, @@ -1018,9 +1036,8 @@ async def test_sum_statistics_during_period_unit_conversion( assert response["result"] == { "sensor.test": [ { - "statistic_id": "sensor.test", - "start": now.isoformat(), - "end": (now + timedelta(minutes=5)).isoformat(), + "start": int(now.timestamp() * 1000), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), "mean": None, "min": None, "max": None, @@ -1150,9 +1167,8 @@ async def test_statistics_during_period_in_the_past( assert response["result"] == { "sensor.test": [ { - "statistic_id": "sensor.test", - "start": stats_start.isoformat(), - "end": (stats_start + timedelta(minutes=5)).isoformat(), + "start": int(stats_start.timestamp() * 1000), + "end": int((stats_start + timedelta(minutes=5)).timestamp() * 1000), "mean": approx(10), "min": approx(10), "max": approx(10), @@ -1178,9 +1194,8 @@ async def test_statistics_during_period_in_the_past( assert response["result"] == { "sensor.test": [ { - "statistic_id": "sensor.test", - "start": start_of_day.isoformat(), - "end": (start_of_day + timedelta(days=1)).isoformat(), + "start": int(start_of_day.timestamp() * 1000), + "end": int((start_of_day + timedelta(days=1)).timestamp() * 1000), "mean": approx(10), "min": approx(10), "max": approx(10), @@ -1468,9 +1483,8 @@ async def test_clear_statistics(recorder_mock, hass, hass_ws_client): expected_response = { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": now.isoformat(), - "end": (now + timedelta(minutes=5)).isoformat(), + "start": int(now.timestamp() * 1000), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), "mean": approx(value), "min": approx(value), "max": approx(value), @@ -1481,9 +1495,8 @@ async def test_clear_statistics(recorder_mock, hass, hass_ws_client): ], "sensor.test2": [ { - "statistic_id": "sensor.test2", - "start": now.isoformat(), - "end": (now + timedelta(minutes=5)).isoformat(), + "start": int(now.timestamp() * 1000), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), "mean": approx(value * 2), "min": approx(value * 2), "max": approx(value * 2), @@ -1494,9 +1507,8 @@ async def test_clear_statistics(recorder_mock, hass, hass_ws_client): ], "sensor.test3": [ { - "statistic_id": "sensor.test3", - "start": now.isoformat(), - "end": (now + timedelta(minutes=5)).isoformat(), + "start": int(now.timestamp() * 1000), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), "mean": approx(value * 3), "min": approx(value * 3), "max": approx(value * 3), @@ -1638,14 +1650,13 @@ async def test_update_statistics_metadata( assert response["result"] == { "sensor.test": [ { - "end": (now + timedelta(minutes=5)).isoformat(), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), "last_reset": None, "max": 10.0, "mean": 10.0, "min": 10.0, - "start": now.isoformat(), + "start": int(now.timestamp() * 1000), "state": None, - "statistic_id": "sensor.test", "sum": None, } ], @@ -1700,14 +1711,13 @@ async def test_change_statistics_unit(recorder_mock, hass, hass_ws_client): assert response["result"] == { "sensor.test": [ { - "end": (now + timedelta(minutes=5)).isoformat(), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), "last_reset": None, "max": 10.0, "mean": 10.0, "min": 10.0, - "start": now.isoformat(), + "start": int(now.timestamp() * 1000), "state": None, - "statistic_id": "sensor.test", "sum": None, } ], @@ -1756,14 +1766,13 @@ async def test_change_statistics_unit(recorder_mock, hass, hass_ws_client): assert response["result"] == { "sensor.test": [ { - "end": (now + timedelta(minutes=5)).isoformat(), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), "last_reset": None, "max": 10000.0, "mean": 10000.0, "min": 10000.0, - "start": now.isoformat(), + "start": int(now.timestamp() * 1000), "state": None, - "statistic_id": "sensor.test", "sum": None, } ], @@ -1796,14 +1805,13 @@ async def test_change_statistics_unit_errors( expected_statistics = { "sensor.test": [ { - "end": (now + timedelta(minutes=5)).isoformat(), + "end": int((now + timedelta(minutes=5)).timestamp() * 1000), "last_reset": None, "max": 10.0, "mean": 10.0, "min": 10.0, - "start": now.isoformat(), + "start": int(now.timestamp() * 1000), "state": None, - "statistic_id": "sensor.test", "sum": None, } ], @@ -2292,9 +2300,8 @@ async def test_import_statistics( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": period1, + "end": (period1 + timedelta(hours=1)), "max": None, "mean": None, "min": None, @@ -2303,9 +2310,8 @@ async def test_import_statistics( "sum": approx(2.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": period2, + "end": period2 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2341,13 +2347,18 @@ async def test_import_statistics( }, ) } - last_stats = get_last_statistics(hass, 1, statistic_id, True) + last_stats = get_last_statistics( + hass, + 1, + statistic_id, + True, + {"last_reset", "max", "mean", "min", "state", "sum"}, + ) assert last_stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": period2, + "end": period2 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2383,9 +2394,8 @@ async def test_import_statistics( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": period1, + "end": period1 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2394,9 +2404,8 @@ async def test_import_statistics( "sum": approx(6.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": period2, + "end": period2 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2435,9 +2444,8 @@ async def test_import_statistics( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": period1, + "end": period1 + timedelta(hours=1), "max": approx(1.0), "mean": approx(2.0), "min": approx(3.0), @@ -2446,9 +2454,8 @@ async def test_import_statistics( "sum": approx(5.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": period2, + "end": period2 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2519,9 +2526,8 @@ async def test_adjust_sum_statistics_energy( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": period1, + "end": period1 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2530,9 +2536,8 @@ async def test_adjust_sum_statistics_energy( "sum": approx(2.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": period2, + "end": period2 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2588,9 +2593,8 @@ async def test_adjust_sum_statistics_energy( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": period1, + "end": period1 + timedelta(hours=1), "max": approx(None), "mean": approx(None), "min": approx(None), @@ -2599,9 +2603,8 @@ async def test_adjust_sum_statistics_energy( "sum": approx(2.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": period2, + "end": period2 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2631,9 +2634,8 @@ async def test_adjust_sum_statistics_energy( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": period1, + "end": period1 + timedelta(hours=1), "max": approx(None), "mean": approx(None), "min": approx(None), @@ -2642,9 +2644,8 @@ async def test_adjust_sum_statistics_energy( "sum": approx(2.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": period2, + "end": period2 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2715,9 +2716,8 @@ async def test_adjust_sum_statistics_gas( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": period1, + "end": period1 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2726,9 +2726,8 @@ async def test_adjust_sum_statistics_gas( "sum": approx(2.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": period2, + "end": period2 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2784,9 +2783,8 @@ async def test_adjust_sum_statistics_gas( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": period1, + "end": period1 + timedelta(hours=1), "max": approx(None), "mean": approx(None), "min": approx(None), @@ -2795,9 +2793,8 @@ async def test_adjust_sum_statistics_gas( "sum": approx(2.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": period2, + "end": period2 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2827,9 +2824,8 @@ async def test_adjust_sum_statistics_gas( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": period1, + "end": period1 + timedelta(hours=1), "max": approx(None), "mean": approx(None), "min": approx(None), @@ -2838,9 +2834,8 @@ async def test_adjust_sum_statistics_gas( "sum": approx(2.0), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": period2, + "end": period2 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2926,9 +2921,8 @@ async def test_adjust_sum_statistics_errors( assert stats == { statistic_id: [ { - "statistic_id": statistic_id, - "start": period1.isoformat(), - "end": (period1 + timedelta(hours=1)).isoformat(), + "start": period1, + "end": period1 + timedelta(hours=1), "max": None, "mean": None, "min": None, @@ -2937,9 +2931,8 @@ async def test_adjust_sum_statistics_errors( "sum": approx(2.0 * factor), }, { - "statistic_id": statistic_id, - "start": period2.isoformat(), - "end": (period2 + timedelta(hours=1)).isoformat(), + "start": period2, + "end": period2 + timedelta(hours=1), "max": None, "mean": None, "min": None, diff --git a/tests/components/sensor/test_recorder.py b/tests/components/sensor/test_recorder.py index f8bdf7acf87..83d61d0a7fc 100644 --- a/tests/components/sensor/test_recorder.py +++ b/tests/components/sensor/test_recorder.py @@ -14,13 +14,12 @@ from homeassistant.components.recorder.db_schema import StatisticsMeta from homeassistant.components.recorder.models import ( StatisticData, StatisticMetaData, - process_timestamp_to_utc_isoformat, + process_timestamp, ) from homeassistant.components.recorder.statistics import ( async_import_statistics, get_metadata, list_statistic_ids, - statistics_during_period, ) from homeassistant.components.recorder.util import get_instance, session_scope from homeassistant.const import STATE_UNAVAILABLE @@ -32,6 +31,7 @@ from tests.components.recorder.common import ( async_recorder_block_till_done, async_wait_recording_done, do_adhoc_statistics, + statistics_during_period, wait_recording_done, ) @@ -153,9 +153,8 @@ def test_compile_hourly_statistics( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean), "min": approx(min), "max": approx(max), @@ -227,9 +226,8 @@ def test_compile_hourly_statistics_purged_state_changes( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean), "min": approx(min), "max": approx(max), @@ -332,9 +330,8 @@ def test_compile_hourly_statistics_wrong_unit(hass_recorder, caplog, attributes) assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(13.050847), "min": approx(-10.0), "max": approx(30.0), @@ -345,9 +342,8 @@ def test_compile_hourly_statistics_wrong_unit(hass_recorder, caplog, attributes) ], "sensor.test2": [ { - "statistic_id": "sensor.test2", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": 13.05084745762712, "min": -10.0, "max": 30.0, @@ -358,9 +354,8 @@ def test_compile_hourly_statistics_wrong_unit(hass_recorder, caplog, attributes) ], "sensor.test3": [ { - "statistic_id": "sensor.test3", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": 13.05084745762712, "min": -10.0, "max": 30.0, @@ -371,9 +366,8 @@ def test_compile_hourly_statistics_wrong_unit(hass_recorder, caplog, attributes) ], "sensor.test6": [ { - "statistic_id": "sensor.test6", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(13.050847), "min": approx(-10.0), "max": approx(30.0), @@ -384,9 +378,8 @@ def test_compile_hourly_statistics_wrong_unit(hass_recorder, caplog, attributes) ], "sensor.test7": [ { - "statistic_id": "sensor.test7", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(13.050847), "min": approx(-10.0), "max": approx(30.0), @@ -492,35 +485,32 @@ async def test_compile_hourly_sum_statistics_amount( expected_stats = { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period0), - "end": process_timestamp_to_utc_isoformat(period0_end), + "start": process_timestamp(period0), + "end": process_timestamp(period0_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(period0), + "last_reset": process_timestamp(period0), "state": approx(factor * seq[2]), "sum": approx(factor * 10.0), }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period1), - "end": process_timestamp_to_utc_isoformat(period1_end), + "start": process_timestamp(period1), + "end": process_timestamp(period1_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(four), + "last_reset": process_timestamp(four), "state": approx(factor * seq[5]), "sum": approx(factor * 40.0), }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period2), - "end": process_timestamp_to_utc_isoformat(period2_end), + "start": process_timestamp(period2), + "end": process_timestamp(period2_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(four), + "last_reset": process_timestamp(four), "state": approx(factor * seq[8]), "sum": approx(factor * 70.0), }, @@ -684,26 +674,22 @@ def test_compile_hourly_sum_statistics_amount_reset_every_state_change( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(dt_util.as_local(one)), + "last_reset": process_timestamp(dt_util.as_local(one)), "state": approx(factor * seq[7]), "sum": approx(factor * (sum(seq) - seq[0])), }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat( - zero + timedelta(minutes=5) - ), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=10)), + "start": process_timestamp(zero + timedelta(minutes=5)), + "end": process_timestamp(zero + timedelta(minutes=10)), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(dt_util.as_local(two)), + "last_reset": process_timestamp(dt_util.as_local(two)), "state": approx(factor * seq[7]), "sum": approx(factor * (2 * sum(seq) - seq[0])), }, @@ -784,13 +770,12 @@ def test_compile_hourly_sum_statistics_amount_invalid_last_reset( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(dt_util.as_local(one)), + "last_reset": process_timestamp(dt_util.as_local(one)), "state": approx(factor * seq[7]), "sum": approx(factor * (sum(seq) - seq[0] - seq[3])), }, @@ -868,13 +853,12 @@ def test_compile_hourly_sum_statistics_nan_inf_state( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(one), + "last_reset": process_timestamp(one), "state": approx(factor * seq[7]), "sum": approx(factor * (seq[2] + seq[3] + seq[4] + seq[6] + seq[7])), }, @@ -992,9 +976,8 @@ def test_compile_hourly_sum_statistics_negative_state( stats = statistics_during_period(hass, zero, period="5minute") assert stats[entity_id] == [ { - "statistic_id": entity_id, - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "max": None, "mean": None, "min": None, @@ -1081,9 +1064,8 @@ def test_compile_hourly_sum_statistics_total_no_reset( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period0), - "end": process_timestamp_to_utc_isoformat(period0_end), + "start": process_timestamp(period0), + "end": process_timestamp(period0_end), "max": None, "mean": None, "min": None, @@ -1092,9 +1074,8 @@ def test_compile_hourly_sum_statistics_total_no_reset( "sum": approx(factor * 10.0), }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period1), - "end": process_timestamp_to_utc_isoformat(period1_end), + "start": process_timestamp(period1), + "end": process_timestamp(period1_end), "max": None, "mean": None, "min": None, @@ -1103,9 +1084,8 @@ def test_compile_hourly_sum_statistics_total_no_reset( "sum": approx(factor * 30.0), }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period2), - "end": process_timestamp_to_utc_isoformat(period2_end), + "start": process_timestamp(period2), + "end": process_timestamp(period2_end), "max": None, "mean": None, "min": None, @@ -1183,9 +1163,8 @@ def test_compile_hourly_sum_statistics_total_increasing( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period0), - "end": process_timestamp_to_utc_isoformat(period0_end), + "start": process_timestamp(period0), + "end": process_timestamp(period0_end), "max": None, "mean": None, "min": None, @@ -1194,9 +1173,8 @@ def test_compile_hourly_sum_statistics_total_increasing( "sum": approx(factor * 10.0), }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period1), - "end": process_timestamp_to_utc_isoformat(period1_end), + "start": process_timestamp(period1), + "end": process_timestamp(period1_end), "max": None, "mean": None, "min": None, @@ -1205,9 +1183,8 @@ def test_compile_hourly_sum_statistics_total_increasing( "sum": approx(factor * 50.0), }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period2), - "end": process_timestamp_to_utc_isoformat(period2_end), + "start": process_timestamp(period2), + "end": process_timestamp(period2_end), "max": None, "mean": None, "min": None, @@ -1297,9 +1274,8 @@ def test_compile_hourly_sum_statistics_total_increasing_small_dip( "sensor.test1": [ { "last_reset": None, - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period0), - "end": process_timestamp_to_utc_isoformat(period0_end), + "start": process_timestamp(period0), + "end": process_timestamp(period0_end), "max": None, "mean": None, "min": None, @@ -1308,9 +1284,8 @@ def test_compile_hourly_sum_statistics_total_increasing_small_dip( }, { "last_reset": None, - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period1), - "end": process_timestamp_to_utc_isoformat(period1_end), + "start": process_timestamp(period1), + "end": process_timestamp(period1_end), "max": None, "mean": None, "min": None, @@ -1319,9 +1294,8 @@ def test_compile_hourly_sum_statistics_total_increasing_small_dip( }, { "last_reset": None, - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period2), - "end": process_timestamp_to_utc_isoformat(period2_end), + "start": process_timestamp(period2), + "end": process_timestamp(period2_end), "max": None, "mean": None, "min": None, @@ -1390,35 +1364,32 @@ def test_compile_hourly_energy_statistics_unsupported(hass_recorder, caplog): assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period0), - "end": process_timestamp_to_utc_isoformat(period0_end), + "start": process_timestamp(period0), + "end": process_timestamp(period0_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(period0), + "last_reset": process_timestamp(period0), "state": approx(20.0), "sum": approx(10.0), }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period1), - "end": process_timestamp_to_utc_isoformat(period1_end), + "start": process_timestamp(period1), + "end": process_timestamp(period1_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(four), + "last_reset": process_timestamp(four), "state": approx(40.0), "sum": approx(40.0), }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period2), - "end": process_timestamp_to_utc_isoformat(period2_end), + "start": process_timestamp(period2), + "end": process_timestamp(period2_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(four), + "last_reset": process_timestamp(four), "state": approx(70.0), "sum": approx(70.0), }, @@ -1500,105 +1471,96 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog): assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period0), - "end": process_timestamp_to_utc_isoformat(period0_end), + "start": process_timestamp(period0), + "end": process_timestamp(period0_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(period0), + "last_reset": process_timestamp(period0), "state": approx(20.0), "sum": approx(10.0), }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period1), - "end": process_timestamp_to_utc_isoformat(period1_end), + "start": process_timestamp(period1), + "end": process_timestamp(period1_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(four), + "last_reset": process_timestamp(four), "state": approx(40.0), "sum": approx(40.0), }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period2), - "end": process_timestamp_to_utc_isoformat(period2_end), + "start": process_timestamp(period2), + "end": process_timestamp(period2_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(four), + "last_reset": process_timestamp(four), "state": approx(70.0), "sum": approx(70.0), }, ], "sensor.test2": [ { - "statistic_id": "sensor.test2", - "start": process_timestamp_to_utc_isoformat(period0), - "end": process_timestamp_to_utc_isoformat(period0_end), + "start": process_timestamp(period0), + "end": process_timestamp(period0_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(period0), + "last_reset": process_timestamp(period0), "state": approx(130.0), "sum": approx(20.0), }, { - "statistic_id": "sensor.test2", - "start": process_timestamp_to_utc_isoformat(period1), - "end": process_timestamp_to_utc_isoformat(period1_end), + "start": process_timestamp(period1), + "end": process_timestamp(period1_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(four), + "last_reset": process_timestamp(four), "state": approx(45.0), "sum": approx(-65.0), }, { - "statistic_id": "sensor.test2", - "start": process_timestamp_to_utc_isoformat(period2), - "end": process_timestamp_to_utc_isoformat(period2_end), + "start": process_timestamp(period2), + "end": process_timestamp(period2_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(four), + "last_reset": process_timestamp(four), "state": approx(75.0), "sum": approx(-35.0), }, ], "sensor.test3": [ { - "statistic_id": "sensor.test3", - "start": process_timestamp_to_utc_isoformat(period0), - "end": process_timestamp_to_utc_isoformat(period0_end), + "start": process_timestamp(period0), + "end": process_timestamp(period0_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(period0), + "last_reset": process_timestamp(period0), "state": approx(5.0), "sum": approx(5.0), }, { - "statistic_id": "sensor.test3", - "start": process_timestamp_to_utc_isoformat(period1), - "end": process_timestamp_to_utc_isoformat(period1_end), + "start": process_timestamp(period1), + "end": process_timestamp(period1_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(four), + "last_reset": process_timestamp(four), "state": approx(50.0), "sum": approx(60.0), }, { - "statistic_id": "sensor.test3", - "start": process_timestamp_to_utc_isoformat(period2), - "end": process_timestamp_to_utc_isoformat(period2_end), + "start": process_timestamp(period2), + "end": process_timestamp(period2_end), "max": None, "mean": None, "min": None, - "last_reset": process_timestamp_to_utc_isoformat(four), + "last_reset": process_timestamp(four), "state": approx(90.0), "sum": approx(100.0), }, @@ -1654,9 +1616,8 @@ def test_compile_hourly_statistics_unchanged( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(four), - "end": process_timestamp_to_utc_isoformat(four + timedelta(minutes=5)), + "start": process_timestamp(four), + "end": process_timestamp(four + timedelta(minutes=5)), "mean": approx(value), "min": approx(value), "max": approx(value), @@ -1687,9 +1648,8 @@ def test_compile_hourly_statistics_partially_unavailable(hass_recorder, caplog): assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(21.1864406779661), "min": approx(10.0), "max": approx(25.0), @@ -1757,9 +1717,8 @@ def test_compile_hourly_statistics_unavailable( assert stats == { "sensor.test2": [ { - "statistic_id": "sensor.test2", - "start": process_timestamp_to_utc_isoformat(four), - "end": process_timestamp_to_utc_isoformat(four + timedelta(minutes=5)), + "start": process_timestamp(four), + "end": process_timestamp(four + timedelta(minutes=5)), "mean": approx(value), "min": approx(value), "max": approx(value), @@ -1974,9 +1933,8 @@ def test_compile_hourly_statistics_changing_units_1( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean), "min": approx(min), "max": approx(max), @@ -2009,9 +1967,8 @@ def test_compile_hourly_statistics_changing_units_1( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean), "min": approx(min), "max": approx(max), @@ -2149,9 +2106,8 @@ def test_compile_hourly_statistics_changing_units_3( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean), "min": approx(min), "max": approx(max), @@ -2184,9 +2140,8 @@ def test_compile_hourly_statistics_changing_units_3( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean), "min": approx(min), "max": approx(max), @@ -2263,9 +2218,8 @@ def test_compile_hourly_statistics_equivalent_units_1( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean), "min": approx(min), "max": approx(max), @@ -2294,9 +2248,8 @@ def test_compile_hourly_statistics_equivalent_units_1( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean), "min": approx(min), "max": approx(max), @@ -2305,11 +2258,8 @@ def test_compile_hourly_statistics_equivalent_units_1( "sum": None, }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat( - zero + timedelta(minutes=10) - ), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=15)), + "start": process_timestamp(zero + timedelta(minutes=10)), + "end": process_timestamp(zero + timedelta(minutes=15)), "mean": approx(mean2), "min": approx(min), "max": approx(max), @@ -2381,13 +2331,8 @@ def test_compile_hourly_statistics_equivalent_units_2( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat( - zero + timedelta(seconds=30 * 5) - ), - "end": process_timestamp_to_utc_isoformat( - zero + timedelta(seconds=30 * 15) - ), + "start": process_timestamp(zero + timedelta(seconds=30 * 5)), + "end": process_timestamp(zero + timedelta(seconds=30 * 15)), "mean": approx(mean), "min": approx(min), "max": approx(max), @@ -2451,9 +2396,8 @@ def test_compile_hourly_statistics_changing_device_class_1( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean1), "min": approx(min), "max": approx(max), @@ -2496,9 +2440,8 @@ def test_compile_hourly_statistics_changing_device_class_1( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean1), "min": approx(min), "max": approx(max), @@ -2507,11 +2450,8 @@ def test_compile_hourly_statistics_changing_device_class_1( "sum": None, }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat( - zero + timedelta(minutes=10) - ), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=15)), + "start": process_timestamp(zero + timedelta(minutes=10)), + "end": process_timestamp(zero + timedelta(minutes=15)), "mean": approx(mean2), "min": approx(min), "max": approx(max), @@ -2554,9 +2494,8 @@ def test_compile_hourly_statistics_changing_device_class_1( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean1), "min": approx(min), "max": approx(max), @@ -2565,11 +2504,8 @@ def test_compile_hourly_statistics_changing_device_class_1( "sum": None, }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat( - zero + timedelta(minutes=10) - ), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=15)), + "start": process_timestamp(zero + timedelta(minutes=10)), + "end": process_timestamp(zero + timedelta(minutes=15)), "mean": approx(mean2), "min": approx(min), "max": approx(max), @@ -2578,11 +2514,8 @@ def test_compile_hourly_statistics_changing_device_class_1( "sum": None, }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat( - zero + timedelta(minutes=20) - ), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=25)), + "start": process_timestamp(zero + timedelta(minutes=20)), + "end": process_timestamp(zero + timedelta(minutes=25)), "mean": approx(mean2), "min": approx(min), "max": approx(max), @@ -2647,9 +2580,8 @@ def test_compile_hourly_statistics_changing_device_class_2( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean), "min": approx(min), "max": approx(max), @@ -2692,9 +2624,8 @@ def test_compile_hourly_statistics_changing_device_class_2( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(zero), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "start": process_timestamp(zero), + "end": process_timestamp(zero + timedelta(minutes=5)), "mean": approx(mean), "min": approx(min), "max": approx(max), @@ -2703,11 +2634,8 @@ def test_compile_hourly_statistics_changing_device_class_2( "sum": None, }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat( - zero + timedelta(minutes=10) - ), - "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=15)), + "start": process_timestamp(zero + timedelta(minutes=10)), + "end": process_timestamp(zero + timedelta(minutes=15)), "mean": approx(mean2), "min": approx(min), "max": approx(max), @@ -2823,9 +2751,8 @@ def test_compile_hourly_statistics_changing_statistics( assert stats == { "sensor.test1": [ { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period0), - "end": process_timestamp_to_utc_isoformat(period0_end), + "start": process_timestamp(period0), + "end": process_timestamp(period0_end), "mean": approx(mean), "min": approx(min), "max": approx(max), @@ -2834,9 +2761,8 @@ def test_compile_hourly_statistics_changing_statistics( "sum": None, }, { - "statistic_id": "sensor.test1", - "start": process_timestamp_to_utc_isoformat(period1), - "end": process_timestamp_to_utc_isoformat(period1_end), + "start": process_timestamp(period1), + "end": process_timestamp(period1_end), "mean": None, "min": None, "max": None, @@ -3071,9 +2997,8 @@ def test_compile_statistics_hourly_daily_monthly_summary(hass_recorder, caplog): ) expected_stats[entity_id].append( { - "statistic_id": entity_id, - "start": process_timestamp_to_utc_isoformat(start), - "end": process_timestamp_to_utc_isoformat(end), + "start": process_timestamp(start), + "end": process_timestamp(end), "mean": approx(expected_average), "min": approx(expected_minimum), "max": approx(expected_maximum), @@ -3129,9 +3054,8 @@ def test_compile_statistics_hourly_daily_monthly_summary(hass_recorder, caplog): ) expected_stats[entity_id].append( { - "statistic_id": entity_id, - "start": process_timestamp_to_utc_isoformat(start), - "end": process_timestamp_to_utc_isoformat(end), + "start": process_timestamp(start), + "end": process_timestamp(end), "mean": approx(expected_average), "min": approx(expected_minimum), "max": approx(expected_maximum), @@ -3187,9 +3111,8 @@ def test_compile_statistics_hourly_daily_monthly_summary(hass_recorder, caplog): ) expected_stats[entity_id].append( { - "statistic_id": entity_id, - "start": process_timestamp_to_utc_isoformat(start), - "end": process_timestamp_to_utc_isoformat(end), + "start": process_timestamp(start), + "end": process_timestamp(end), "mean": approx(expected_average), "min": approx(expected_minimum), "max": approx(expected_maximum), @@ -3245,9 +3168,8 @@ def test_compile_statistics_hourly_daily_monthly_summary(hass_recorder, caplog): ) expected_stats[entity_id].append( { - "statistic_id": entity_id, - "start": process_timestamp_to_utc_isoformat(start), - "end": process_timestamp_to_utc_isoformat(end), + "start": process_timestamp(start), + "end": process_timestamp(end), "mean": approx(expected_average), "min": approx(expected_minimum), "max": approx(expected_maximum), diff --git a/tests/components/tibber/test_statistics.py b/tests/components/tibber/test_statistics.py index 745c434237b..661297f9a37 100644 --- a/tests/components/tibber/test_statistics.py +++ b/tests/components/tibber/test_statistics.py @@ -35,7 +35,8 @@ async def test_async_setup_entry(recorder_mock, hass): None, [statistic_id], "hour", - True, + None, + {"start", "state", "mean", "min", "max", "last_reset", "sum"}, ) assert len(stats) == 1 From aaec4646273d3d072c4b18d72f47379163949633 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 16 Nov 2022 17:38:07 +0100 Subject: [PATCH 0480/1033] Add support for managing the silabs multiprotocol add-on (#82170) * Add support for managing the silabs multiprotocol add-on * Fix passing context when starting option flow * Allow unloading a ha yellow config entry * Fix tests * Log data passed to ZHA option flow * Improve ZHA migration logic * Move tests * Improve test coverage * Remove dead code * Drop automatic ZHA migration --- CODEOWNERS | 2 + .../components/hassio/addon_manager.py | 3 + .../homeassistant_hardware/__init__.py | 10 + .../homeassistant_hardware/const.py | 7 + .../homeassistant_hardware/manifest.json | 7 + .../silabs_multiprotocol_addon.py | 307 ++++++++++++ .../homeassistant_yellow/__init__.py | 46 +- .../homeassistant_yellow/config_flow.py | 26 +- .../homeassistant_yellow/manifest.json | 2 +- .../homeassistant_yellow/strings.json | 40 ++ .../homeassistant_yellow/translations/en.json | 40 ++ script/hassfest/manifest.py | 1 + tests/components/hassio/test_addon_manager.py | 6 + .../homeassistant_hardware/__init__.py | 1 + .../homeassistant_hardware/conftest.py | 108 +++++ .../test_silabs_multiprotocol_addon.py | 441 ++++++++++++++++++ .../homeassistant_yellow/conftest.py | 104 +++++ .../homeassistant_yellow/test_config_flow.py | 70 ++- .../homeassistant_yellow/test_hardware.py | 8 +- .../homeassistant_yellow/test_init.py | 108 ++++- tests/components/zwave_js/conftest.py | 1 + 21 files changed, 1329 insertions(+), 9 deletions(-) create mode 100644 homeassistant/components/homeassistant_hardware/__init__.py create mode 100644 homeassistant/components/homeassistant_hardware/const.py create mode 100644 homeassistant/components/homeassistant_hardware/manifest.json create mode 100644 homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py create mode 100644 homeassistant/components/homeassistant_yellow/strings.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/en.json create mode 100644 tests/components/homeassistant_hardware/__init__.py create mode 100644 tests/components/homeassistant_hardware/conftest.py create mode 100644 tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py diff --git a/CODEOWNERS b/CODEOWNERS index 6ff584963dc..374a3d37a81 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -481,6 +481,8 @@ build.json @home-assistant/supervisor /tests/components/homeassistant/ @home-assistant/core /homeassistant/components/homeassistant_alerts/ @home-assistant/core /tests/components/homeassistant_alerts/ @home-assistant/core +/homeassistant/components/homeassistant_hardware/ @home-assistant/core +/tests/components/homeassistant_hardware/ @home-assistant/core /homeassistant/components/homeassistant_sky_connect/ @home-assistant/core /tests/components/homeassistant_sky_connect/ @home-assistant/core /homeassistant/components/homeassistant_yellow/ @home-assistant/core diff --git a/homeassistant/components/hassio/addon_manager.py b/homeassistant/components/hassio/addon_manager.py index ff3e9036018..f240937c7f5 100644 --- a/homeassistant/components/hassio/addon_manager.py +++ b/homeassistant/components/hassio/addon_manager.py @@ -70,6 +70,7 @@ def api_error( class AddonInfo: """Represent the current add-on info state.""" + hostname: str | None options: dict[str, Any] state: AddonState update_available: bool @@ -143,6 +144,7 @@ class AddonManager: self._logger.debug("Add-on store info: %s", addon_store_info) if not addon_store_info["installed"]: return AddonInfo( + hostname=None, options={}, state=AddonState.NOT_INSTALLED, update_available=False, @@ -152,6 +154,7 @@ class AddonManager: addon_info = await async_get_addon_info(self._hass, self.addon_slug) addon_state = self.async_get_addon_state(addon_info) return AddonInfo( + hostname=addon_info["hostname"], options=addon_info["options"], state=addon_state, update_available=addon_info["update_available"], diff --git a/homeassistant/components/homeassistant_hardware/__init__.py b/homeassistant/components/homeassistant_hardware/__init__.py new file mode 100644 index 00000000000..f3a63a7f767 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/__init__.py @@ -0,0 +1,10 @@ +"""The Home Assistant Hardware integration.""" +from __future__ import annotations + +from homeassistant.core import HomeAssistant +from homeassistant.helpers.typing import ConfigType + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up the component.""" + return True diff --git a/homeassistant/components/homeassistant_hardware/const.py b/homeassistant/components/homeassistant_hardware/const.py new file mode 100644 index 00000000000..dd3a254d097 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/const.py @@ -0,0 +1,7 @@ +"""Constants for the Homeassistant Hardware integration.""" + +import logging + +LOGGER = logging.getLogger(__package__) + +SILABS_MULTIPROTOCOL_ADDON_SLUG = "core_silabs_multiprotocol" diff --git a/homeassistant/components/homeassistant_hardware/manifest.json b/homeassistant/components/homeassistant_hardware/manifest.json new file mode 100644 index 00000000000..32472bcdded --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/manifest.json @@ -0,0 +1,7 @@ +{ + "domain": "homeassistant_hardware", + "name": "Home Assistant Hardware", + "documentation": "https://www.home-assistant.io/integrations/homeassistant_hardware", + "codeowners": ["@home-assistant/core"], + "integration_type": "system" +} diff --git a/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py b/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py new file mode 100644 index 00000000000..b4dcca0e329 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py @@ -0,0 +1,307 @@ +"""Manage the Silicon Labs Multiprotocol add-on.""" +from __future__ import annotations + +from abc import abstractmethod +import asyncio +import dataclasses +import logging +from typing import Any + +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.components.hassio import ( + AddonError, + AddonInfo, + AddonManager, + AddonState, + is_hassio, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.data_entry_flow import ( + AbortFlow, + FlowHandler, + FlowManager, + FlowResult, +) +from homeassistant.helpers.singleton import singleton + +from .const import LOGGER, SILABS_MULTIPROTOCOL_ADDON_SLUG + +_LOGGER = logging.getLogger(__name__) + +DATA_ADDON_MANAGER = "silabs_multiprotocol_addon_manager" + +ADDON_SETUP_TIMEOUT = 5 +ADDON_SETUP_TIMEOUT_ROUNDS = 40 + +CONF_ADDON_AUTOFLASH_FW = "autoflash_firmware" +CONF_ADDON_DEVICE = "device" +CONF_ENABLE_MULTI_PAN = "enable_multi_pan" + + +@singleton(DATA_ADDON_MANAGER) +@callback +def get_addon_manager(hass: HomeAssistant) -> AddonManager: + """Get the add-on manager.""" + return AddonManager( + hass, + LOGGER, + "Silicon Labs Multiprotocol", + SILABS_MULTIPROTOCOL_ADDON_SLUG, + ) + + +@dataclasses.dataclass +class SerialPortSettings: + """Serial port settings.""" + + device: str + baudrate: str + flow_control: bool + + +def get_zigbee_socket(hass, addon_info: AddonInfo) -> str: + """Return the zigbee socket. + + Raises AddonError on error + """ + return f"socket://{addon_info.hostname}:9999" + + +class BaseMultiPanFlow(FlowHandler): + """Support configuring the Silicon Labs Multiprotocol add-on.""" + + def __init__(self) -> None: + """Set up flow instance.""" + # If we install the add-on we should uninstall it on entry remove. + self.install_task: asyncio.Task | None = None + self.start_task: asyncio.Task | None = None + + @property + @abstractmethod + def flow_manager(self) -> FlowManager: + """Return the flow manager of the flow.""" + + @abstractmethod + async def _async_serial_port_settings(self) -> SerialPortSettings: + """Return the radio serial port settings.""" + + async def async_step_install_addon( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Install Silicon Labs Multiprotocol add-on.""" + if not self.install_task: + self.install_task = self.hass.async_create_task(self._async_install_addon()) + return self.async_show_progress( + step_id="install_addon", progress_action="install_addon" + ) + + try: + await self.install_task + except AddonError as err: + self.install_task = None + _LOGGER.error(err) + return self.async_show_progress_done(next_step_id="install_failed") + + self.install_task = None + + return self.async_show_progress_done(next_step_id="configure_addon") + + async def async_step_install_failed( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Add-on installation failed.""" + return self.async_abort(reason="addon_install_failed") + + async def async_step_start_addon( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Start Silicon Labs Multiprotocol add-on.""" + if not self.start_task: + self.start_task = self.hass.async_create_task(self._async_start_addon()) + return self.async_show_progress( + step_id="start_addon", progress_action="start_addon" + ) + + try: + await self.start_task + except (AddonError, AbortFlow) as err: + self.start_task = None + _LOGGER.error(err) + return self.async_show_progress_done(next_step_id="start_failed") + + self.start_task = None + return self.async_show_progress_done(next_step_id="finish_addon_setup") + + async def async_step_start_failed( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Add-on start failed.""" + return self.async_abort(reason="addon_start_failed") + + async def _async_start_addon(self) -> None: + """Start Silicon Labs Multiprotocol add-on.""" + addon_manager: AddonManager = get_addon_manager(self.hass) + try: + await addon_manager.async_schedule_start_addon() + finally: + # Continue the flow after show progress when the task is done. + self.hass.async_create_task( + self.flow_manager.async_configure(flow_id=self.flow_id) + ) + + @abstractmethod + async def async_step_configure_addon( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Configure the Silicon Labs Multiprotocol add-on.""" + + @abstractmethod + async def async_step_finish_addon_setup( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Finish setup of the Silicon Labs Multiprotocol add-on.""" + + async def _async_get_addon_info(self) -> AddonInfo: + """Return and cache Silicon Labs Multiprotocol add-on info.""" + addon_manager: AddonManager = get_addon_manager(self.hass) + try: + addon_info: AddonInfo = await addon_manager.async_get_addon_info() + except AddonError as err: + _LOGGER.error(err) + raise AbortFlow("addon_info_failed") from err + + return addon_info + + async def _async_set_addon_config(self, config: dict) -> None: + """Set Silicon Labs Multiprotocol add-on config.""" + addon_manager: AddonManager = get_addon_manager(self.hass) + try: + await addon_manager.async_set_addon_options(config) + except AddonError as err: + _LOGGER.error(err) + raise AbortFlow("addon_set_config_failed") from err + + async def _async_install_addon(self) -> None: + """Install the Silicon Labs Multiprotocol add-on.""" + addon_manager: AddonManager = get_addon_manager(self.hass) + try: + await addon_manager.async_schedule_install_addon() + finally: + # Continue the flow after show progress when the task is done. + self.hass.async_create_task( + self.flow_manager.async_configure(flow_id=self.flow_id) + ) + + +class OptionsFlowHandler(BaseMultiPanFlow, config_entries.OptionsFlow): + """Handle an options flow for the Silicon Labs Multiprotocol add-on.""" + + def __init__(self, config_entry: config_entries.ConfigEntry) -> None: + """Set up the options flow.""" + super().__init__() + self.config_entry = config_entry + self.original_addon_config: dict[str, Any] | None = None + self.revert_reason: str | None = None + + @property + def flow_manager(self) -> config_entries.OptionsFlowManager: + """Return the correct flow manager.""" + return self.hass.config_entries.options + + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Manage the options.""" + if not is_hassio(self.hass): + return self.async_abort(reason="not_hassio") + + return await self.async_step_on_supervisor() + + async def async_step_on_supervisor( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle logic when on Supervisor host.""" + addon_info = await self._async_get_addon_info() + + if addon_info.state == AddonState.NOT_INSTALLED: + return await self.async_step_addon_not_installed() + return await self.async_step_addon_installed() + + async def async_step_addon_not_installed( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle logic when the addon is not yet installed.""" + if user_input is None: + return self.async_show_form( + step_id="addon_not_installed", + data_schema=vol.Schema( + {vol.Required(CONF_ENABLE_MULTI_PAN, default=False): bool} + ), + ) + if not user_input[CONF_ENABLE_MULTI_PAN]: + return self.async_create_entry(title="", data={}) + + return await self.async_step_install_addon() + + async def async_step_configure_addon( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Configure the Silicon Labs Multiprotocol add-on.""" + addon_info = await self._async_get_addon_info() + + addon_config = addon_info.options + + serial_port_settings = await self._async_serial_port_settings() + new_addon_config = { + **addon_config, + CONF_ADDON_AUTOFLASH_FW: True, + **dataclasses.asdict(serial_port_settings), + } + + if new_addon_config != addon_config: + # Copy the add-on config to keep the objects separate. + self.original_addon_config = dict(addon_config) + _LOGGER.debug("Reconfiguring addon with %s", new_addon_config) + await self._async_set_addon_config(new_addon_config) + + return await self.async_step_start_addon() + + async def async_step_finish_addon_setup( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Prepare info needed to complete the config entry update.""" + # Always reload entry after installing the addon. + self.hass.async_create_task( + self.hass.config_entries.async_reload(self.config_entry.entry_id) + ) + + return self.async_create_entry(title="", data={}) + + async def async_step_addon_installed( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle logic when the addon is already installed.""" + addon_info = await self._async_get_addon_info() + + serial_device = (await self._async_serial_port_settings()).device + if addon_info.options.get(CONF_ADDON_DEVICE) == serial_device: + return await self.async_step_show_revert_guide() + return await self.async_step_addon_installed_other_device() + + async def async_step_show_revert_guide( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Link to a guide for reverting to Zigbee firmware.""" + if user_input is None: + return self.async_show_form(step_id="show_revert_guide") + return self.async_create_entry(title="", data={}) + + async def async_step_addon_installed_other_device( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Show dialog explaining the addon is in use by another device.""" + if user_input is None: + return self.async_show_form(step_id="addon_installed_other_device") + return self.async_create_entry(title="", data={}) diff --git a/homeassistant/components/homeassistant_yellow/__init__.py b/homeassistant/components/homeassistant_yellow/__init__.py index e6eaa2f7fce..83a3d9dad32 100644 --- a/homeassistant/components/homeassistant_yellow/__init__.py +++ b/homeassistant/components/homeassistant_yellow/__init__.py @@ -1,11 +1,25 @@ """The Home Assistant Yellow integration.""" from __future__ import annotations -from homeassistant.components.hassio import get_os_info +import logging + +from homeassistant.components.hassio import ( + AddonError, + AddonInfo, + AddonManager, + AddonState, + get_os_info, +) +from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon import ( + get_addon_manager, + get_zigbee_socket, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady +_LOGGER = logging.getLogger(__name__) + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up a Home Assistant Yellow config entry.""" @@ -19,13 +33,36 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.async_create_task(hass.config_entries.async_remove(entry.entry_id)) return False + addon_manager: AddonManager = get_addon_manager(hass) + try: + addon_info: AddonInfo = await addon_manager.async_get_addon_info() + except AddonError as err: + _LOGGER.error(err) + raise ConfigEntryNotReady from err + + # Start the addon if it's not started + if addon_info.state == AddonState.NOT_RUNNING: + await addon_manager.async_start_addon() + + if addon_info.state not in (AddonState.NOT_INSTALLED, AddonState.RUNNING): + _LOGGER.debug( + "Multi pan addon in state %s, delaying yellow config entry setup", + addon_info.state, + ) + raise ConfigEntryNotReady + + if addon_info.state == AddonState.NOT_INSTALLED: + path = "/dev/ttyAMA1" + else: + path = get_zigbee_socket(hass, addon_info) + await hass.config_entries.flow.async_init( "zha", context={"source": "hardware"}, data={ "name": "Yellow", "port": { - "path": "/dev/ttyAMA1", + "path": path, "baudrate": 115200, "flow_control": "hardware", }, @@ -34,3 +71,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + return True diff --git a/homeassistant/components/homeassistant_yellow/config_flow.py b/homeassistant/components/homeassistant_yellow/config_flow.py index 191a28f47a4..48a797a2aab 100644 --- a/homeassistant/components/homeassistant_yellow/config_flow.py +++ b/homeassistant/components/homeassistant_yellow/config_flow.py @@ -3,7 +3,9 @@ from __future__ import annotations from typing import Any -from homeassistant.config_entries import ConfigFlow +from homeassistant.components.homeassistant_hardware import silabs_multiprotocol_addon +from homeassistant.config_entries import ConfigEntry, ConfigFlow +from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from .const import DOMAIN @@ -14,9 +16,31 @@ class HomeAssistantYellowConfigFlow(ConfigFlow, domain=DOMAIN): VERSION = 1 + @staticmethod + @callback + def async_get_options_flow( + config_entry: ConfigEntry, + ) -> HomeAssistantYellowOptionsFlow: + """Return the options flow.""" + return HomeAssistantYellowOptionsFlow(config_entry) + async def async_step_system(self, data: dict[str, Any] | None = None) -> FlowResult: """Handle the initial step.""" if self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") return self.async_create_entry(title="Home Assistant Yellow", data={}) + + +class HomeAssistantYellowOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandler): + """Handle an option flow for Home Assistant Yellow.""" + + async def _async_serial_port_settings( + self, + ) -> silabs_multiprotocol_addon.SerialPortSettings: + """Return the radio serial port settings.""" + return silabs_multiprotocol_addon.SerialPortSettings( + device="/dev/ttyAMA1", + baudrate="115200", + flow_control=True, + ) diff --git a/homeassistant/components/homeassistant_yellow/manifest.json b/homeassistant/components/homeassistant_yellow/manifest.json index 47e6c8e2cd8..ef708e9429a 100644 --- a/homeassistant/components/homeassistant_yellow/manifest.json +++ b/homeassistant/components/homeassistant_yellow/manifest.json @@ -3,7 +3,7 @@ "name": "Home Assistant Yellow", "config_flow": false, "documentation": "https://www.home-assistant.io/integrations/homeassistant_yellow", - "dependencies": ["hardware", "hassio"], + "dependencies": ["hardware", "hassio", "homeassistant_hardware"], "codeowners": ["@home-assistant/core"], "integration_type": "hardware" } diff --git a/homeassistant/components/homeassistant_yellow/strings.json b/homeassistant/components/homeassistant_yellow/strings.json new file mode 100644 index 00000000000..df1eb780a96 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/strings.json @@ -0,0 +1,40 @@ +{ + "options": { + "step": { + "addon_not_installed": { + "title": "Enable multiprotocol support on the IEEE 802.15.4 radio", + "description": "When multiprotocol support is enabled, the Home Assistant Yellow's IEEE 802.15.4 radio can be used for both Zigbee and Thread (used by Matter) at the same time. Note: This is an experimental feature.", + "data": { + "enable_multi_pan": "Enable multiprotocol support" + } + }, + "addon_installed_other_device": { + "title": "Multiprotocol support is already enabled for another device" + }, + "install_addon": { + "title": "The Silicon Labs Multiprotocol add-on installation has started" + }, + "show_revert_guide": { + "title": "Multiprotocol support is enabled for this device", + "description": "If you want to change to Zigbee only firmware, please complete the following manual steps:\n\n * Remove the Silicon Labs Multiprotocol addon\n\n * Flash the Zigbee only firmware, follow the guide at https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Reconfigure ZHA to migrate settings to the reflashed radio" + }, + "start_addon": { + "title": "The Silicon Labs Multiprotocol add-on is starting." + } + }, + "error": { + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "addon_info_failed": "Failed to get Silicon Labs Multiprotocol add-on info.", + "addon_install_failed": "Failed to install the Silicon Labs Multiprotocol add-on.", + "addon_set_config_failed": "Failed to set Silicon Labs Multiprotocol configuration.", + "addon_start_failed": "Failed to start the Silicon Labs Multiprotocol add-on.", + "not_hassio": "The hardware options can only be configured on HassOS installations." + }, + "progress": { + "install_addon": "Please wait while the Silicon Labs Multiprotocol add-on installation finishes. This can take several minutes.", + "start_addon": "Please wait while the Silicon Labs Multiprotocol add-on start completes. This may take some seconds." + } + } +} diff --git a/homeassistant/components/homeassistant_yellow/translations/en.json b/homeassistant/components/homeassistant_yellow/translations/en.json new file mode 100644 index 00000000000..252303ea428 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/en.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Failed to get Silicon Labs Multiprotocol add-on info.", + "addon_install_failed": "Failed to install the Silicon Labs Multiprotocol add-on.", + "addon_set_config_failed": "Failed to set Silicon Labs Multiprotocol configuration.", + "addon_start_failed": "Failed to start the Silicon Labs Multiprotocol add-on.", + "not_hassio": "The hardware options can only be configured on HassOS installations." + }, + "error": { + "unknown": "Unexpected error" + }, + "progress": { + "install_addon": "Please wait while the Silicon Labs Multiprotocol add-on installation finishes. This can take several minutes.", + "start_addon": "Please wait while the Silicon Labs Multiprotocol add-on start completes. This may take some seconds." + }, + "step": { + "addon_installed_other_device": { + "title": "Multiprotocol support is already enabled for another device" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Enable multiprotocol support" + }, + "description": "When multiprotocol support is enabled, the Home Assistant Yellow's IEEE 802.15.4 radio can be used for both Zigbee and Thread (used by Matter) at the same time. Note: This is an experimental feature.", + "title": "Enable multiprotocol support on the IEEE 802.15.4 radio" + }, + "install_addon": { + "title": "The Silicon Labs Multiprotocol add-on installation has started" + }, + "show_revert_guide": { + "description": "If you want to change to Zigbee only firmware, please complete the following manual steps:\n\n * Remove the Silicon Labs Multiprotocol addon\n\n * Flash the Zigbee only firmware, follow the guide at https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Reconfigure ZHA to migrate settings to the reflashed radio", + "title": "Multiprotocol support is enabled for this device" + }, + "start_addon": { + "title": "The Silicon Labs Multiprotocol add-on is starting." + } + } + } +} \ No newline at end of file diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py index 874fb069818..b4130de1dee 100644 --- a/script/hassfest/manifest.py +++ b/script/hassfest/manifest.py @@ -59,6 +59,7 @@ NO_IOT_CLASS = [ "history", "homeassistant", "homeassistant_alerts", + "homeassistant_hardware", "homeassistant_sky_connect", "homeassistant_yellow", "image", diff --git a/tests/components/hassio/test_addon_manager.py b/tests/components/hassio/test_addon_manager.py index ffeecd167e6..7135f1ea646 100644 --- a/tests/components/hassio/test_addon_manager.py +++ b/tests/components/hassio/test_addon_manager.py @@ -45,6 +45,7 @@ def mock_addon_installed( "state": "stopped", "version": "1.0.0", } + addon_info.return_value["hostname"] = "core-test-addon" addon_info.return_value["state"] = "stopped" addon_info.return_value["version"] = "1.0.0" return addon_info @@ -80,6 +81,7 @@ def addon_info_fixture() -> Generator[AsyncMock, None, None]: "homeassistant.components.hassio.addon_manager.async_get_addon_info", ) as addon_info: addon_info.return_value = { + "hostname": None, "options": {}, "state": None, "update_available": False, @@ -220,6 +222,7 @@ async def test_get_addon_info_not_installed( ) -> None: """Test get addon info when addon is not installed..""" assert await addon_manager.async_get_addon_info() == AddonInfo( + hostname=None, options={}, state=AddonState.NOT_INSTALLED, update_available=False, @@ -240,6 +243,7 @@ async def test_get_addon_info( """Test get addon info when addon is installed.""" addon_installed.return_value["state"] = addon_info_state assert await addon_manager.async_get_addon_info() == AddonInfo( + hostname="core-test-addon", options={}, state=addon_state, update_available=False, @@ -337,6 +341,7 @@ async def test_schedule_install_addon( assert addon_manager.task_in_progress() is True assert await addon_manager.async_get_addon_info() == AddonInfo( + hostname="core-test-addon", options={}, state=AddonState.INSTALLING, update_available=False, @@ -671,6 +676,7 @@ async def test_schedule_update_addon( assert addon_manager.task_in_progress() is True assert await addon_manager.async_get_addon_info() == AddonInfo( + hostname="core-test-addon", options={}, state=AddonState.UPDATING, update_available=True, diff --git a/tests/components/homeassistant_hardware/__init__.py b/tests/components/homeassistant_hardware/__init__.py new file mode 100644 index 00000000000..8f2ab13e5d1 --- /dev/null +++ b/tests/components/homeassistant_hardware/__init__.py @@ -0,0 +1 @@ +"""Tests for the Home Assistant Hardware integration.""" diff --git a/tests/components/homeassistant_hardware/conftest.py b/tests/components/homeassistant_hardware/conftest.py new file mode 100644 index 00000000000..d46241b46e2 --- /dev/null +++ b/tests/components/homeassistant_hardware/conftest.py @@ -0,0 +1,108 @@ +"""Test fixtures for the Home Assistant Hardware integration.""" +from unittest.mock import patch + +import pytest + + +@pytest.fixture(name="addon_running") +def mock_addon_running(addon_store_info, addon_info): + """Mock add-on already running.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "started", + "version": "1.0.0", + } + addon_info.return_value["hostname"] = "core-silabs-multiprotocol" + addon_info.return_value["state"] = "started" + addon_info.return_value["version"] = "1.0.0" + return addon_info + + +@pytest.fixture(name="addon_installed") +def mock_addon_installed(addon_store_info, addon_info): + """Mock add-on already installed but not running.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "stopped", + "version": "1.0.0", + } + addon_info.return_value["hostname"] = "core-silabs-multiprotocol" + addon_info.return_value["state"] = "stopped" + addon_info.return_value["version"] = "1.0.0" + return addon_info + + +@pytest.fixture(name="addon_store_info") +def addon_store_info_fixture(): + """Mock Supervisor add-on store info.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_get_addon_store_info" + ) as addon_store_info: + addon_store_info.return_value = { + "installed": None, + "state": None, + "version": "1.0.0", + } + yield addon_store_info + + +@pytest.fixture(name="addon_info") +def addon_info_fixture(): + """Mock Supervisor add-on info.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_get_addon_info", + ) as addon_info: + addon_info.return_value = { + "hostname": None, + "options": {}, + "state": None, + "update_available": False, + "version": None, + } + yield addon_info + + +@pytest.fixture(name="set_addon_options") +def set_addon_options_fixture(): + """Mock set add-on options.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_set_addon_options" + ) as set_options: + yield set_options + + +@pytest.fixture(name="install_addon_side_effect") +def install_addon_side_effect_fixture(addon_store_info, addon_info): + """Return the install add-on side effect.""" + + async def install_addon(hass, slug): + """Mock install add-on.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "stopped", + "version": "1.0.0", + } + addon_info.return_value["hostname"] = "core-silabs-multiprotocol" + addon_info.return_value["state"] = "stopped" + addon_info.return_value["version"] = "1.0.0" + + return install_addon + + +@pytest.fixture(name="install_addon") +def mock_install_addon(install_addon_side_effect): + """Mock install add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_install_addon", + side_effect=install_addon_side_effect, + ) as install_addon: + yield install_addon + + +@pytest.fixture(name="start_addon") +def start_addon_fixture(): + """Mock start add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_start_addon" + ) as start_addon: + yield start_addon diff --git a/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py b/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py new file mode 100644 index 00000000000..4b966033857 --- /dev/null +++ b/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py @@ -0,0 +1,441 @@ +"""Test the Home Assistant Yellow config flow.""" +from __future__ import annotations + +from collections.abc import Generator +from typing import Any +from unittest.mock import Mock, patch + +import pytest + +from homeassistant import config_entries +from homeassistant.components.hassio.handler import HassioAPIError +from homeassistant.components.homeassistant_hardware import silabs_multiprotocol_addon +from homeassistant.config_entries import ConfigEntry, ConfigFlow +from homeassistant.core import HomeAssistant, callback +from homeassistant.data_entry_flow import FlowResult, FlowResultType + +from tests.common import MockConfigEntry, MockModule, mock_integration, mock_platform + +TEST_DOMAIN = "test" + + +class TestConfigFlow(ConfigFlow, domain=TEST_DOMAIN): + """Handle a config flow for Home Assistant Yellow.""" + + VERSION = 1 + + @staticmethod + @callback + def async_get_options_flow( + config_entry: ConfigEntry, + ) -> TestOptionsFlow: + """Return the options flow.""" + return TestOptionsFlow(config_entry) + + async def async_step_system(self, data: dict[str, Any] | None = None) -> FlowResult: + """Handle the initial step.""" + if self._async_current_entries(): + return self.async_abort(reason="single_instance_allowed") + + return self.async_create_entry(title="Home Assistant Yellow", data={}) + + +class TestOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandler): + """Handle an option flow for Home Assistant Yellow.""" + + async def _async_serial_port_settings( + self, + ) -> silabs_multiprotocol_addon.SerialPortSettings: + """Return the radio serial port settings.""" + return silabs_multiprotocol_addon.SerialPortSettings( + device="/dev/ttyTEST123", + baudrate="115200", + flow_control=True, + ) + + +@pytest.fixture(autouse=True) +def config_flow_handler( + hass: HomeAssistant, current_request_with_host: Any +) -> Generator[TestConfigFlow, None, None]: + """Fixture for a test config flow.""" + mock_platform(hass, f"{TEST_DOMAIN}.config_flow") + with patch.dict(config_entries.HANDLERS, {TEST_DOMAIN: TestConfigFlow}): + yield TestConfigFlow + + +async def test_option_flow_install_multi_pan_addon( + hass: HomeAssistant, + addon_store_info, + addon_info, + install_addon, + set_addon_options, + start_addon, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": True, + }, + ) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + assert result["progress_action"] == "install_addon" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "configure_addon" + install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + set_addon_options.assert_called_once_with( + hass, + "core_silabs_multiprotocol", + { + "options": { + "autoflash_firmware": True, + "device": "/dev/ttyTEST123", + "baudrate": "115200", + "flow_control": True, + } + }, + ) + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "finish_addon_setup" + start_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.CREATE_ENTRY + + +async def test_option_flow_non_hassio( + hass: HomeAssistant, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "not_hassio" + + +async def test_option_flow_addon_installed_other_device( + hass: HomeAssistant, + addon_store_info, + addon_installed, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_installed_other_device" + + result = await hass.config_entries.options.async_configure(result["flow_id"], {}) + assert result["type"] == FlowResultType.CREATE_ENTRY + + +async def test_option_flow_addon_installed_same_device( + hass: HomeAssistant, + addon_info, + addon_store_info, + addon_installed, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + addon_info.return_value["options"]["device"] = "/dev/ttyTEST123" + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "show_revert_guide" + + result = await hass.config_entries.options.async_configure(result["flow_id"], {}) + assert result["type"] == FlowResultType.CREATE_ENTRY + + +async def test_option_flow_do_not_install_multi_pan_addon( + hass: HomeAssistant, + addon_info, + addon_store_info, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": False, + }, + ) + assert result["type"] == FlowResultType.CREATE_ENTRY + + +async def test_option_flow_install_multi_pan_addon_install_fails( + hass: HomeAssistant, + addon_store_info, + addon_info, + install_addon, + set_addon_options, + start_addon, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + install_addon.side_effect = HassioAPIError("Boom") + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": True, + }, + ) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + assert result["progress_action"] == "install_addon" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "install_failed" + install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "addon_install_failed" + + +async def test_option_flow_install_multi_pan_addon_start_fails( + hass: HomeAssistant, + addon_store_info, + addon_info, + install_addon, + set_addon_options, + start_addon, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + start_addon.side_effect = HassioAPIError("Boom") + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": True, + }, + ) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + assert result["progress_action"] == "install_addon" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "configure_addon" + install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + set_addon_options.assert_called_once_with( + hass, + "core_silabs_multiprotocol", + { + "options": { + "autoflash_firmware": True, + "device": "/dev/ttyTEST123", + "baudrate": "115200", + "flow_control": True, + } + }, + ) + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "start_failed" + start_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "addon_start_failed" + + +async def test_option_flow_install_multi_pan_addon_set_options_fails( + hass: HomeAssistant, + addon_store_info, + addon_info, + install_addon, + set_addon_options, + start_addon, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + set_addon_options.side_effect = HassioAPIError("Boom") + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": True, + }, + ) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + assert result["progress_action"] == "install_addon" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "configure_addon" + install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "addon_set_config_failed" + + +async def test_option_flow_addon_info_fails( + hass: HomeAssistant, + addon_store_info, + addon_info, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + addon_store_info.side_effect = HassioAPIError("Boom") + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "addon_info_failed" diff --git a/tests/components/homeassistant_yellow/conftest.py b/tests/components/homeassistant_yellow/conftest.py index 52759ba6d89..e5224aea05d 100644 --- a/tests/components/homeassistant_yellow/conftest.py +++ b/tests/components/homeassistant_yellow/conftest.py @@ -27,3 +27,107 @@ def mock_zha_config_flow_setup() -> Generator[None, None, None]: return_value=True, ): yield + + +@pytest.fixture(name="addon_running") +def mock_addon_running(addon_store_info, addon_info): + """Mock add-on already running.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "started", + "version": "1.0.0", + } + addon_info.return_value["hostname"] = "core-silabs-multiprotocol" + addon_info.return_value["state"] = "started" + addon_info.return_value["version"] = "1.0.0" + return addon_info + + +@pytest.fixture(name="addon_installed") +def mock_addon_installed(addon_store_info, addon_info): + """Mock add-on already installed but not running.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "stopped", + "version": "1.0.0", + } + addon_info.return_value["hostname"] = "core-silabs-multiprotocol" + addon_info.return_value["state"] = "stopped" + addon_info.return_value["version"] = "1.0.0" + return addon_info + + +@pytest.fixture(name="addon_store_info") +def addon_store_info_fixture(): + """Mock Supervisor add-on store info.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_get_addon_store_info" + ) as addon_store_info: + addon_store_info.return_value = { + "installed": None, + "state": None, + "version": "1.0.0", + } + yield addon_store_info + + +@pytest.fixture(name="addon_info") +def addon_info_fixture(): + """Mock Supervisor add-on info.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_get_addon_info", + ) as addon_info: + addon_info.return_value = { + "hostname": None, + "options": {}, + "state": None, + "update_available": False, + "version": None, + } + yield addon_info + + +@pytest.fixture(name="set_addon_options") +def set_addon_options_fixture(): + """Mock set add-on options.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_set_addon_options" + ) as set_options: + yield set_options + + +@pytest.fixture(name="install_addon_side_effect") +def install_addon_side_effect_fixture(addon_store_info, addon_info): + """Return the install add-on side effect.""" + + async def install_addon(hass, slug): + """Mock install add-on.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "stopped", + "version": "1.0.0", + } + addon_info.return_value["hostname"] = "core-silabs-multiprotocol" + addon_info.return_value["state"] = "stopped" + addon_info.return_value["version"] = "1.0.0" + + return install_addon + + +@pytest.fixture(name="install_addon") +def mock_install_addon(install_addon_side_effect): + """Mock install add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_install_addon", + side_effect=install_addon_side_effect, + ) as install_addon: + yield install_addon + + +@pytest.fixture(name="start_addon") +def start_addon_fixture(): + """Mock start add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_start_addon" + ) as start_addon: + yield start_addon diff --git a/tests/components/homeassistant_yellow/test_config_flow.py b/tests/components/homeassistant_yellow/test_config_flow.py index e6d5da11806..91f15f792d5 100644 --- a/tests/components/homeassistant_yellow/test_config_flow.py +++ b/tests/components/homeassistant_yellow/test_config_flow.py @@ -1,5 +1,5 @@ """Test the Home Assistant Yellow config flow.""" -from unittest.mock import patch +from unittest.mock import Mock, patch from homeassistant.components.homeassistant_yellow.const import DOMAIN from homeassistant.core import HomeAssistant @@ -56,3 +56,71 @@ async def test_config_flow_single_entry(hass: HomeAssistant) -> None: assert result["type"] == FlowResultType.ABORT assert result["reason"] == "single_instance_allowed" mock_setup_entry.assert_not_called() + + +async def test_option_flow_install_multi_pan_addon( + hass: HomeAssistant, + addon_store_info, + addon_info, + install_addon, + set_addon_options, + start_addon, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": True, + }, + ) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + assert result["progress_action"] == "install_addon" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "configure_addon" + install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + set_addon_options.assert_called_once_with( + hass, + "core_silabs_multiprotocol", + { + "options": { + "autoflash_firmware": True, + "device": "/dev/ttyAMA1", + "baudrate": "115200", + "flow_control": True, + } + }, + ) + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "finish_addon_setup" + start_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.CREATE_ENTRY diff --git a/tests/components/homeassistant_yellow/test_hardware.py b/tests/components/homeassistant_yellow/test_hardware.py index 681c031b2e1..add83fde7c1 100644 --- a/tests/components/homeassistant_yellow/test_hardware.py +++ b/tests/components/homeassistant_yellow/test_hardware.py @@ -9,7 +9,9 @@ from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry, MockModule, mock_integration -async def test_hardware_info(hass: HomeAssistant, hass_ws_client) -> None: +async def test_hardware_info( + hass: HomeAssistant, hass_ws_client, addon_store_info +) -> None: """Test we can get the board info.""" mock_integration(hass, MockModule("hassio")) @@ -58,7 +60,9 @@ async def test_hardware_info(hass: HomeAssistant, hass_ws_client) -> None: @pytest.mark.parametrize("os_info", [None, {"board": None}, {"board": "other"}]) -async def test_hardware_info_fail(hass: HomeAssistant, hass_ws_client, os_info) -> None: +async def test_hardware_info_fail( + hass: HomeAssistant, hass_ws_client, os_info, addon_store_info +) -> None: """Test async_info raises if os_info is not as expected.""" mock_integration(hass, MockModule("hassio")) diff --git a/tests/components/homeassistant_yellow/test_init.py b/tests/components/homeassistant_yellow/test_init.py index bc36ae3cec2..06fe9465d2d 100644 --- a/tests/components/homeassistant_yellow/test_init.py +++ b/tests/components/homeassistant_yellow/test_init.py @@ -4,6 +4,7 @@ from unittest.mock import patch import pytest from homeassistant.components import zha +from homeassistant.components.hassio.handler import HassioAPIError from homeassistant.components.homeassistant_yellow.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant @@ -15,7 +16,7 @@ from tests.common import MockConfigEntry, MockModule, mock_integration "onboarded, num_entries, num_flows", ((False, 1, 0), (True, 0, 1)) ) async def test_setup_entry( - hass: HomeAssistant, onboarded, num_entries, num_flows + hass: HomeAssistant, onboarded, num_entries, num_flows, addon_store_info ) -> None: """Test setup of a config entry, including setup of zha.""" mock_integration(hass, MockModule("hassio")) @@ -53,8 +54,11 @@ async def test_setup_entry( assert len(hass.config_entries.flow.async_progress_by_handler("zha")) == num_flows assert len(hass.config_entries.async_entries("zha")) == num_entries + # Test unloading the config entry + assert await hass.config_entries.async_unload(config_entry.entry_id) -async def test_setup_zha(hass: HomeAssistant) -> None: + +async def test_setup_zha(hass: HomeAssistant, addon_store_info) -> None: """Test zha gets the right config.""" mock_integration(hass, MockModule("hassio")) @@ -100,6 +104,54 @@ async def test_setup_zha(hass: HomeAssistant) -> None: assert config_entry.title == "Yellow" +async def test_setup_zha_multipan( + hass: HomeAssistant, addon_info, addon_running +) -> None: + """Test zha gets the right config.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.homeassistant_yellow.get_os_info", + return_value={"board": "yellow"}, + ) as mock_get_os_info, patch( + "homeassistant.components.onboarding.async_is_onboarded", return_value=False + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert len(mock_get_os_info.mock_calls) == 1 + + # Finish setting up ZHA + zha_flows = hass.config_entries.flow.async_progress_by_handler("zha") + assert len(zha_flows) == 1 + assert zha_flows[0]["step_id"] == "choose_formation_strategy" + + await hass.config_entries.flow.async_configure( + zha_flows[0]["flow_id"], + user_input={"next_step_id": zha.config_flow.FORMATION_REUSE_SETTINGS}, + ) + await hass.async_block_till_done() + + config_entry = hass.config_entries.async_entries("zha")[0] + assert config_entry.data == { + "device": { + "baudrate": 115200, + "flow_control": "hardware", + "path": "socket://core-silabs-multiprotocol:9999", + }, + "radio_type": "ezsp", + } + assert config_entry.options == {} + assert config_entry.title == "Yellow" + + async def test_setup_entry_wrong_board(hass: HomeAssistant) -> None: """Test setup of a config entry with wrong board type.""" mock_integration(hass, MockModule("hassio")) @@ -141,3 +193,55 @@ async def test_setup_entry_wait_hassio(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert len(mock_get_os_info.mock_calls) == 1 assert config_entry.state == ConfigEntryState.SETUP_RETRY + + +async def test_setup_entry_addon_info_fails( + hass: HomeAssistant, addon_store_info +) -> None: + """Test setup of a config entry when fetching addon info fails.""" + mock_integration(hass, MockModule("hassio")) + addon_store_info.side_effect = HassioAPIError("Boom") + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.homeassistant_yellow.get_os_info", + return_value={"board": "yellow"}, + ), patch( + "homeassistant.components.onboarding.async_is_onboarded", return_value=False + ): + assert not await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert config_entry.state == ConfigEntryState.SETUP_RETRY + + +async def test_setup_entry_addon_not_running( + hass: HomeAssistant, addon_installed, start_addon +) -> None: + """Test the addon is started if it is not running.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.homeassistant_yellow.get_os_info", + return_value={"board": "yellow"}, + ), patch( + "homeassistant.components.onboarding.async_is_onboarded", return_value=False + ): + assert not await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert config_entry.state == ConfigEntryState.SETUP_RETRY + start_addon.assert_called_once() diff --git a/tests/components/zwave_js/conftest.py b/tests/components/zwave_js/conftest.py index 74b43e8394a..e5a759ba0a0 100644 --- a/tests/components/zwave_js/conftest.py +++ b/tests/components/zwave_js/conftest.py @@ -30,6 +30,7 @@ def mock_addon_info(addon_info_side_effect): side_effect=addon_info_side_effect, ) as addon_info: addon_info.return_value = { + "hostname": None, "options": {}, "state": None, "update_available": False, From 08398370c5765a70e7dd9407a14f870c85619a17 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 16 Nov 2022 11:26:47 -0600 Subject: [PATCH 0481/1033] Bump dbus-fast to 1.74.1 (#82202) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 4d4075bc5a9..739f9c96404 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -11,7 +11,7 @@ "bluetooth-adapters==0.7.0", "bluetooth-auto-recovery==0.4.0", "bluetooth-data-tools==0.3.0", - "dbus-fast==1.74.0" + "dbus-fast==1.74.1" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 51b38623c81..72267ae7f14 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -18,7 +18,7 @@ bluetooth-data-tools==0.3.0 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.3 -dbus-fast==1.74.0 +dbus-fast==1.74.1 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.8.0 diff --git a/requirements_all.txt b/requirements_all.txt index d577053f253..aec80b757c9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -555,7 +555,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.74.0 +dbus-fast==1.74.1 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9dbcb71ac2e..07540d1bc6c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -435,7 +435,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.74.0 +dbus-fast==1.74.1 # homeassistant.components.debugpy debugpy==1.6.3 From 7ce6600f69d20fec4f1ee97ef6fd0e83715a82fd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 16 Nov 2022 12:02:09 -0600 Subject: [PATCH 0482/1033] Fix missing await in nexia emergency heat (#82207) fixes undefined --- homeassistant/components/nexia/climate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/nexia/climate.py b/homeassistant/components/nexia/climate.py index 66c325d2fc3..f8c08b4efd6 100644 --- a/homeassistant/components/nexia/climate.py +++ b/homeassistant/components/nexia/climate.py @@ -378,7 +378,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): async def async_turn_aux_heat_on(self) -> None: """Turn Aux Heat on.""" - self._thermostat.set_emergency_heat(True) + await self._thermostat.set_emergency_heat(True) self._signal_thermostat_update() async def async_turn_off(self) -> None: From a6ff8c5fb345e585777bee557e0a07881641a56e Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 16 Nov 2022 20:01:53 +0100 Subject: [PATCH 0483/1033] Fix Home Assistant Yellow tests (#82212) --- tests/components/homeassistant_sky_connect/test_init.py | 2 +- tests/components/homeassistant_yellow/conftest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/components/homeassistant_sky_connect/test_init.py b/tests/components/homeassistant_sky_connect/test_init.py index 179a0eff8da..5a9d2064a24 100644 --- a/tests/components/homeassistant_sky_connect/test_init.py +++ b/tests/components/homeassistant_sky_connect/test_init.py @@ -36,7 +36,7 @@ def mock_zha_config_flow_setup() -> Generator[None, None, None]: with patch( "bellows.zigbee.application.ControllerApplication.probe", side_effect=mock_probe ), patch( - "homeassistant.components.zha.config_flow.BaseZhaFlow._connect_zigpy_app", + "homeassistant.components.zha.radio_manager.ZhaRadioManager._connect_zigpy_app", return_value=mock_connect_app, ): yield diff --git a/tests/components/homeassistant_yellow/conftest.py b/tests/components/homeassistant_yellow/conftest.py index e5224aea05d..23ef93ee8c6 100644 --- a/tests/components/homeassistant_yellow/conftest.py +++ b/tests/components/homeassistant_yellow/conftest.py @@ -20,7 +20,7 @@ def mock_zha_config_flow_setup() -> Generator[None, None, None]: with patch( "bellows.zigbee.application.ControllerApplication.probe", side_effect=mock_probe ), patch( - "homeassistant.components.zha.config_flow.BaseZhaFlow._connect_zigpy_app", + "homeassistant.components.zha.radio_manager.ZhaRadioManager._connect_zigpy_app", return_value=mock_connect_app, ), patch( "homeassistant.components.zha.async_setup_entry", From ba546e107239c0bbb46858cdc548addd704f2444 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 16 Nov 2022 20:47:58 +0100 Subject: [PATCH 0484/1033] Use _attr_supported_features in nexia climate (#82218) --- homeassistant/components/nexia/climate.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/nexia/climate.py b/homeassistant/components/nexia/climate.py index f8c08b4efd6..aa7c55df33f 100644 --- a/homeassistant/components/nexia/climate.py +++ b/homeassistant/components/nexia/climate.py @@ -166,12 +166,11 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): self._has_emergency_heat = self._thermostat.has_emergency_heat() self._has_humidify_support = self._thermostat.has_humidify_support() self._has_dehumidify_support = self._thermostat.has_dehumidify_support() - supported = NEXIA_SUPPORTED + self._attr_supported_features = NEXIA_SUPPORTED if self._has_humidify_support or self._has_dehumidify_support: - supported |= ClimateEntityFeature.TARGET_HUMIDITY + self._attr_supported_features |= ClimateEntityFeature.TARGET_HUMIDITY if self._has_emergency_heat: - supported |= ClimateEntityFeature.AUX_HEAT - self._attr_supported_features = supported + self._attr_supported_features |= ClimateEntityFeature.AUX_HEAT self._attr_preset_modes = self._zone.get_presets() self._attr_fan_modes = self._thermostat.get_fan_modes() self._attr_hvac_modes = HVAC_MODES From c62c19e58b46a4b591e5b65867b2c851ac8e44c8 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 16 Nov 2022 20:52:36 +0100 Subject: [PATCH 0485/1033] Use _attr_supported_features in tado climate (#82219) --- homeassistant/components/tado/climate.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/tado/climate.py b/homeassistant/components/tado/climate.py index 5f58203e9e8..0e8b968ca7c 100644 --- a/homeassistant/components/tado/climate.py +++ b/homeassistant/components/tado/climate.py @@ -245,7 +245,7 @@ class TadoClimate(TadoZoneEntity, ClimateEntity): self._ac_device = zone_type == TYPE_AIR_CONDITIONING self._supported_hvac_modes = supported_hvac_modes self._supported_fan_modes = supported_fan_modes - self._support_flags = support_flags + self._attr_supported_features = support_flags self._available = False @@ -286,11 +286,6 @@ class TadoClimate(TadoZoneEntity, ClimateEntity): ) ) - @property - def supported_features(self): - """Return the list of supported features.""" - return self._support_flags - @property def name(self): """Return the name of the entity.""" @@ -466,7 +461,7 @@ class TadoClimate(TadoZoneEntity, ClimateEntity): @property def swing_modes(self): """Swing modes for the device.""" - if self._support_flags & ClimateEntityFeature.SWING_MODE: + if self.supported_features & ClimateEntityFeature.SWING_MODE: return [TADO_SWING_ON, TADO_SWING_OFF] return None @@ -618,10 +613,10 @@ class TadoClimate(TadoZoneEntity, ClimateEntity): temperature_to_send = None fan_speed = None - if self._support_flags & ClimateEntityFeature.FAN_MODE: + if self.supported_features & ClimateEntityFeature.FAN_MODE: fan_speed = self._current_tado_fan_speed swing = None - if self._support_flags & ClimateEntityFeature.SWING_MODE: + if self.supported_features & ClimateEntityFeature.SWING_MODE: swing = self._current_tado_swing_mode self._tado.set_zone_overlay( From 4e11246ba5a6b392f0697a9bc8d8c64cf6a658eb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 16 Nov 2022 14:16:51 -0600 Subject: [PATCH 0486/1033] Create an issue when using older esphome firmwares when bluetooth support is enabled (#82162) * Create an issue when using older esphome firmwares when bluetooth support is enabled 2022.11.0 has some significant stability imporvements with BLE. Its is non-obvious if the user is running an older esphome version that does not have the fixes * name * skip repair issue if a project name is set * drop reflashing term * Update homeassistant/components/esphome/strings.json * Update homeassistant/components/esphome/translations/en.json * Update homeassistant/components/esphome/translations/en.json Co-authored-by: Franck Nijhof * Update homeassistant/components/esphome/translations/en.json Co-authored-by: Franck Nijhof * Update homeassistant/components/esphome/strings.json Co-authored-by: Franck Nijhof * Update homeassistant/components/esphome/strings.json Co-authored-by: Franck Nijhof * Update homeassistant/components/esphome/strings.json Co-authored-by: Franck Nijhof --- homeassistant/components/esphome/__init__.py | 42 ++++++++++++++++++- homeassistant/components/esphome/strings.json | 6 +++ .../components/esphome/translations/en.json | 6 +++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index acbcc0f57dc..894928e597a 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -24,6 +24,7 @@ from aioesphomeapi import ( UserService, UserServiceArgType, ) +from awesomeversion import AwesomeVersion import voluptuous as vol from homeassistant import const @@ -46,6 +47,11 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo, Entity, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_track_state_change_event +from homeassistant.helpers.issue_registry import ( + IssueSeverity, + async_create_issue, + async_delete_issue, +) from homeassistant.helpers.service import async_set_service_schema from homeassistant.helpers.template import Template @@ -59,6 +65,37 @@ CONF_NOISE_PSK = "noise_psk" _LOGGER = logging.getLogger(__name__) _R = TypeVar("_R") +STABLE_BLE_VERSION_STR = "2022.11.0" +STABLE_BLE_VERSION = AwesomeVersion(STABLE_BLE_VERSION_STR) + + +@callback +def _async_check_firmware_version( + hass: HomeAssistant, device_info: EsphomeDeviceInfo +) -> None: + """Create or delete an the ble_firmware_outdated issue.""" + # ESPHome device_info.name is the unique_id + issue = f"ble_firmware_outdated-{device_info.name}" + if ( + not device_info.bluetooth_proxy_version + # If the device has a project name its up to that project + # to tell them about the firmware version update so we don't notify here + or device_info.project_name + or AwesomeVersion(device_info.esphome_version) >= STABLE_BLE_VERSION + ): + async_delete_issue(hass, DOMAIN, issue) + return + async_create_issue( + hass, + DOMAIN, + issue, + is_fixable=False, + severity=IssueSeverity.WARNING, + learn_more_url=f"https://esphome.io/changelog/{STABLE_BLE_VERSION_STR}.html", + translation_key="ble_firmware_outdated", + translation_placeholders={"name": device_info.name}, + ) + async def async_setup_entry( # noqa: C901 hass: HomeAssistant, entry: ConfigEntry @@ -215,7 +252,8 @@ async def async_setup_entry( # noqa: C901 """Subscribe to states and list entities on successful API login.""" nonlocal device_id try: - entry_data.device_info = await cli.device_info() + device_info = await cli.device_info() + entry_data.device_info = device_info assert cli.api_version is not None entry_data.api_version = cli.api_version entry_data.available = True @@ -243,6 +281,8 @@ async def async_setup_entry( # noqa: C901 _LOGGER.warning("Error getting initial data for %s: %s", host, err) # Re-connection logic will trigger after this await cli.disconnect() + else: + _async_check_firmware_version(hass, device_info) async def on_disconnect() -> None: """Run disconnect callbacks on API disconnect.""" diff --git a/homeassistant/components/esphome/strings.json b/homeassistant/components/esphome/strings.json index b1b1ba94e3f..0ec4d93b405 100644 --- a/homeassistant/components/esphome/strings.json +++ b/homeassistant/components/esphome/strings.json @@ -43,5 +43,11 @@ } }, "flow_title": "{name}" + }, + "issues": { + "ble_firmware_outdated": { + "title": "Update {name} with ESPHome 2022.11.0 or later", + "description": "To improve Bluetooth reliability and performance, we highly recommend updating {name} with ESPHome 2022.11.0 or later." + } } } diff --git a/homeassistant/components/esphome/translations/en.json b/homeassistant/components/esphome/translations/en.json index b0b502631df..173113f64cd 100644 --- a/homeassistant/components/esphome/translations/en.json +++ b/homeassistant/components/esphome/translations/en.json @@ -43,5 +43,11 @@ "description": "Please enter connection settings of your [ESPHome]({esphome_url}) node." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "To improve Bluetooth reliability and performance, we highly recommend updating {name} with ESPHome 2022.11.0 or later.", + "title": "Update {name} with ESPHome 2022.11.0 or later" + } } } \ No newline at end of file From 39ac2c11017f84276cb23d15843dcccae5b104f4 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 16 Nov 2022 21:17:50 +0100 Subject: [PATCH 0487/1033] Adjust type hints for ClimateEntityFeature (#82206) --- homeassistant/components/climate/__init__.py | 4 ++-- homeassistant/components/ecobee/climate.py | 2 +- homeassistant/components/econet/climate.py | 2 +- homeassistant/components/lookin/climate.py | 2 +- homeassistant/components/lyric/climate.py | 8 +++----- homeassistant/components/shelly/climate.py | 2 +- homeassistant/components/spider/climate.py | 2 +- homeassistant/components/venstar/climate.py | 2 +- homeassistant/components/zha/climate.py | 2 +- pylint/plugins/hass_enforce_type_hints.py | 2 +- 10 files changed, 13 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/climate/__init__.py b/homeassistant/components/climate/__init__.py index be5f33f8cd5..d19637a9be0 100644 --- a/homeassistant/components/climate/__init__.py +++ b/homeassistant/components/climate/__init__.py @@ -225,7 +225,7 @@ class ClimateEntity(Entity): _attr_precision: float _attr_preset_mode: str | None _attr_preset_modes: list[str] | None - _attr_supported_features: int + _attr_supported_features: ClimateEntityFeature | int _attr_swing_mode: str | None _attr_swing_modes: list[str] | None _attr_target_humidity: int | None = None @@ -552,7 +552,7 @@ class ClimateEntity(Entity): await self.async_set_hvac_mode(HVACMode.OFF) @property - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature | int: """Return the list of supported features.""" return self._attr_supported_features diff --git a/homeassistant/components/ecobee/climate.py b/homeassistant/components/ecobee/climate.py index 16870d76902..028979b2a85 100644 --- a/homeassistant/components/ecobee/climate.py +++ b/homeassistant/components/ecobee/climate.py @@ -351,7 +351,7 @@ class Thermostat(ClimateEntity): return self.thermostat["runtime"]["connected"] @property - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" if self.has_humidifier_control: return SUPPORT_FLAGS | ClimateEntityFeature.TARGET_HUMIDITY diff --git a/homeassistant/components/econet/climate.py b/homeassistant/components/econet/climate.py index bda462285fc..cf950a3c38c 100644 --- a/homeassistant/components/econet/climate.py +++ b/homeassistant/components/econet/climate.py @@ -81,7 +81,7 @@ class EcoNetThermostat(EcoNetEntity, ClimateEntity): self.op_list.append(ha_mode) @property - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" if self._econet.supports_humidifier: return SUPPORT_FLAGS_THERMOSTAT | ClimateEntityFeature.TARGET_HUMIDITY diff --git a/homeassistant/components/lookin/climate.py b/homeassistant/components/lookin/climate.py index 5b3ecefa4ff..aa3ba0c3614 100644 --- a/homeassistant/components/lookin/climate.py +++ b/homeassistant/components/lookin/climate.py @@ -93,7 +93,7 @@ class ConditionerEntity(LookinCoordinatorEntity, ClimateEntity): _attr_current_humidity: float | None = None # type: ignore[assignment] _attr_temperature_unit = TEMP_CELSIUS - _attr_supported_features: int = ( + _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.SWING_MODE diff --git a/homeassistant/components/lyric/climate.py b/homeassistant/components/lyric/climate.py index ae4afa0b0c6..8339c4dad45 100644 --- a/homeassistant/components/lyric/climate.py +++ b/homeassistant/components/lyric/climate.py @@ -169,13 +169,11 @@ class LyricClimate(LyricDeviceEntity, ClimateEntity): self.entity_description = description @property - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" if self.device.changeableValues.thermostatSetpointStatus: - support_flags = SUPPORT_FLAGS_LCC - else: - support_flags = SUPPORT_FLAGS_TCC - return support_flags + return SUPPORT_FLAGS_LCC + return SUPPORT_FLAGS_TCC @property def temperature_unit(self) -> str: diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index 38ba4a51c9f..c55c0261839 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -109,7 +109,7 @@ class BlockSleepingClimate( _attr_icon = "mdi:thermostat" _attr_max_temp = SHTRV_01_TEMPERATURE_SETTINGS["max"] _attr_min_temp = SHTRV_01_TEMPERATURE_SETTINGS["min"] - _attr_supported_features: int = ( + _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) _attr_target_temperature_step = SHTRV_01_TEMPERATURE_SETTINGS["step"] diff --git a/homeassistant/components/spider/climate.py b/homeassistant/components/spider/climate.py index 6c02e8485c4..111c1e377cf 100644 --- a/homeassistant/components/spider/climate.py +++ b/homeassistant/components/spider/climate.py @@ -64,7 +64,7 @@ class SpiderThermostat(ClimateEntity): ) @property - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" if self.thermostat.has_fan_mode: return ( diff --git a/homeassistant/components/venstar/climate.py b/homeassistant/components/venstar/climate.py index 2fb0595788f..2a921fe3731 100644 --- a/homeassistant/components/venstar/climate.py +++ b/homeassistant/components/venstar/climate.py @@ -125,7 +125,7 @@ class VenstarThermostat(VenstarEntity, ClimateEntity): self._attr_name = self._client.name @property - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" features = ( ClimateEntityFeature.TARGET_TEMPERATURE diff --git a/homeassistant/components/zha/climate.py b/homeassistant/components/zha/climate.py index 155a254217e..4de07bf0d74 100644 --- a/homeassistant/components/zha/climate.py +++ b/homeassistant/components/zha/climate.py @@ -277,7 +277,7 @@ class Thermostat(ZhaEntity, ClimateEntity): return self._presets @property - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" features = self._supported_flags if HVACMode.HEAT_COOL in self.hvac_modes: diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index ddf3a3a5155..873a783c1ca 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1055,7 +1055,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type="int", + return_type=["ClimateEntityFeature", "int"], ), TypeHintMatch( function_name="min_temp", From e37211ad420cd7edf4b389f60354751921531253 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 16 Nov 2022 21:28:23 +0100 Subject: [PATCH 0488/1033] Use _attr_supported_features in demo climate (#82216) --- homeassistant/components/demo/climate.py | 27 ++++++++---------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/demo/climate.py b/homeassistant/components/demo/climate.py index 91594423744..52616d4cf47 100644 --- a/homeassistant/components/demo/climate.py +++ b/homeassistant/components/demo/climate.py @@ -128,26 +128,22 @@ class DemoClimate(ClimateEntity): """Initialize the climate device.""" self._unique_id = unique_id self._attr_name = name - self._support_flags = SUPPORT_FLAGS + self._attr_supported_features = SUPPORT_FLAGS if target_temperature is not None: - self._support_flags = ( - self._support_flags | ClimateEntityFeature.TARGET_TEMPERATURE - ) + self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE if preset is not None: - self._support_flags = self._support_flags | ClimateEntityFeature.PRESET_MODE + self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE if fan_mode is not None: - self._support_flags = self._support_flags | ClimateEntityFeature.FAN_MODE + self._attr_supported_features |= ClimateEntityFeature.FAN_MODE if target_humidity is not None: - self._support_flags = ( - self._support_flags | ClimateEntityFeature.TARGET_HUMIDITY - ) + self._attr_supported_features |= ClimateEntityFeature.TARGET_HUMIDITY if swing_mode is not None: - self._support_flags = self._support_flags | ClimateEntityFeature.SWING_MODE + self._attr_supported_features |= ClimateEntityFeature.SWING_MODE if aux is not None: - self._support_flags = self._support_flags | ClimateEntityFeature.AUX_HEAT + self._attr_supported_features |= ClimateEntityFeature.AUX_HEAT if HVACMode.HEAT_COOL in hvac_modes or HVACMode.AUTO in hvac_modes: - self._support_flags = ( - self._support_flags | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE + self._attr_supported_features |= ( + ClimateEntityFeature.TARGET_TEMPERATURE_RANGE ) self._target_temperature = target_temperature self._target_humidity = target_humidity @@ -183,11 +179,6 @@ class DemoClimate(ClimateEntity): """Return the unique id.""" return self._unique_id - @property - def supported_features(self) -> int: - """Return the list of supported features.""" - return self._support_flags - @property def temperature_unit(self) -> str: """Return the unit of measurement.""" From 9d8dfc2d716437f2f4b660cb1cab01eb804d832b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 16 Nov 2022 15:07:02 -0600 Subject: [PATCH 0489/1033] Add effects support to led-ble (#82224) --- homeassistant/components/led_ble/const.py | 4 ++++ homeassistant/components/led_ble/light.py | 20 +++++++++++++++++-- .../components/led_ble/manifest.json | 2 +- requirements_all.txt | 4 +--- requirements_test_all.txt | 4 +--- 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/led_ble/const.py b/homeassistant/components/led_ble/const.py index ad3ea8f6707..64c28d1ada5 100644 --- a/homeassistant/components/led_ble/const.py +++ b/homeassistant/components/led_ble/const.py @@ -1,5 +1,7 @@ """Constants for the LED BLE integration.""" +from typing import Final + DOMAIN = "led_ble" DEVICE_TIMEOUT = 30 @@ -8,3 +10,5 @@ LOCAL_NAMES = {"LEDnet", "BLE-LED", "LEDBLE", "Triones", "LEDBlue"} UNSUPPORTED_SUB_MODEL = "LEDnetWF" UPDATE_SECONDS = 15 + +DEFAULT_EFFECT_SPEED: Final = 50 diff --git a/homeassistant/components/led_ble/light.py b/homeassistant/components/led_ble/light.py index 4a8ff3f01af..22a52e61b63 100644 --- a/homeassistant/components/led_ble/light.py +++ b/homeassistant/components/led_ble/light.py @@ -7,10 +7,12 @@ from led_ble import LEDBLE from homeassistant.components.light import ( ATTR_BRIGHTNESS, + ATTR_EFFECT, ATTR_RGB_COLOR, ATTR_WHITE, ColorMode, LightEntity, + LightEntityFeature, ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback @@ -22,7 +24,7 @@ from homeassistant.helpers.update_coordinator import ( DataUpdateCoordinator, ) -from .const import DOMAIN +from .const import DEFAULT_EFFECT_SPEED, DOMAIN from .models import LEDBLEData @@ -41,6 +43,7 @@ class LEDBLEEntity(CoordinatorEntity, LightEntity): _attr_supported_color_modes = {ColorMode.RGB, ColorMode.WHITE} _attr_has_entity_name = True + _attr_supported_features = LightEntityFeature.EFFECT def __init__( self, coordinator: DataUpdateCoordinator, device: LEDBLE, name: str @@ -51,7 +54,7 @@ class LEDBLEEntity(CoordinatorEntity, LightEntity): self._attr_unique_id = device.address self._attr_device_info = DeviceInfo( name=name, - model=hex(device.model_num), + model=f"{device.model_data.description} {hex(device.model_num)}", sw_version=hex(device.version_num), connections={(dr.CONNECTION_BLUETOOTH, device.address)}, ) @@ -65,10 +68,23 @@ class LEDBLEEntity(CoordinatorEntity, LightEntity): self._attr_brightness = device.brightness self._attr_rgb_color = device.rgb_unscaled self._attr_is_on = device.on + self._attr_effect = device.effect + self._attr_effect_list = device.effect_list + + async def _async_set_effect(self, effect: str, brightness: int) -> None: + """Set an effect.""" + await self._device.async_set_effect( + effect, + self._device.speed or DEFAULT_EFFECT_SPEED, + round(brightness / 255 * 100), + ) async def async_turn_on(self, **kwargs: Any) -> None: """Instruct the light to turn on.""" brightness = kwargs.get(ATTR_BRIGHTNESS, self.brightness) + if effect := kwargs.get(ATTR_EFFECT): + await self._async_set_effect(effect, brightness) + return if ATTR_RGB_COLOR in kwargs: rgb = kwargs[ATTR_RGB_COLOR] await self._device.set_rgb(rgb, brightness) diff --git a/homeassistant/components/led_ble/manifest.json b/homeassistant/components/led_ble/manifest.json index d59570c6257..ea272dcacc0 100644 --- a/homeassistant/components/led_ble/manifest.json +++ b/homeassistant/components/led_ble/manifest.json @@ -3,7 +3,7 @@ "name": "LED BLE", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/led_ble/", - "requirements": ["bluetooth-data-tools==0.2.0", "led-ble==1.0.0"], + "requirements": ["bluetooth-data-tools==0.3.0", "led-ble==1.0.0"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [ diff --git a/requirements_all.txt b/requirements_all.txt index aec80b757c9..10c5aa6b375 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -452,10 +452,8 @@ bluetooth-adapters==0.7.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.4.0 -# homeassistant.components.led_ble -bluetooth-data-tools==0.2.0 - # homeassistant.components.bluetooth +# homeassistant.components.led_ble bluetooth-data-tools==0.3.0 # homeassistant.components.bond diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 07540d1bc6c..259c607c031 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -366,10 +366,8 @@ bluetooth-adapters==0.7.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.4.0 -# homeassistant.components.led_ble -bluetooth-data-tools==0.2.0 - # homeassistant.components.bluetooth +# homeassistant.components.led_ble bluetooth-data-tools==0.3.0 # homeassistant.components.bond From e85e79052b4395877a328e36d469d861d6acfacc Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 16 Nov 2022 22:07:48 +0100 Subject: [PATCH 0490/1033] Improve climate type hints for supported_features (#82223) --- homeassistant/components/esphome/climate.py | 2 +- homeassistant/components/homekit_controller/climate.py | 6 +++--- homeassistant/components/izone/climate.py | 2 +- homeassistant/components/mqtt/climate.py | 2 +- homeassistant/components/mysensors/climate.py | 2 +- homeassistant/components/sensibo/climate.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/esphome/climate.py b/homeassistant/components/esphome/climate.py index 4f38d1caa24..e27d1d47bca 100644 --- a/homeassistant/components/esphome/climate.py +++ b/homeassistant/components/esphome/climate.py @@ -197,7 +197,7 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti return self._static_info.visual_max_temperature @property - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature | int: """Return the list of supported features.""" features = 0 if self._static_info.supports_two_point_target_temperature: diff --git a/homeassistant/components/homekit_controller/climate.py b/homeassistant/components/homekit_controller/climate.py index 41e88725121..3838c3a862e 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -148,7 +148,7 @@ class HomeKitBaseClimateEntity(HomeKitEntity, ClimateEntity): ) @property - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature | int: """Return the list of supported features.""" features = 0 @@ -368,7 +368,7 @@ class HomeKitHeaterCoolerEntity(HomeKitBaseClimateEntity): ) @property - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature | int: """Return the list of supported features.""" features = super().supported_features @@ -602,7 +602,7 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity): return [MODE_HOMEKIT_TO_HASS[mode] for mode in valid_values] @property - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature | int: """Return the list of supported features.""" features = super().supported_features diff --git a/homeassistant/components/izone/climate.py b/homeassistant/components/izone/climate.py index 31ad5f7860d..d65ebae24d4 100644 --- a/homeassistant/components/izone/climate.py +++ b/homeassistant/components/izone/climate.py @@ -518,7 +518,7 @@ class ZoneDevice(ClimateEntity): @property @_return_on_connection_error(0) - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature | int: """Return the list of supported features.""" if self._zone.mode == Zone.Mode.AUTO: return self._attr_supported_features diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index ef5a25d959b..8ce43cac752 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -475,7 +475,7 @@ class MqttClimate(MqttEntity, ClimateEntity): config.get(key), entity=self ).async_render - support: int = 0 + support: ClimateEntityFeature | int = 0 if (self._topic[CONF_TEMP_STATE_TOPIC] is not None) or ( self._topic[CONF_TEMP_COMMAND_TOPIC] is not None ): diff --git a/homeassistant/components/mysensors/climate.py b/homeassistant/components/mysensors/climate.py index 44468d8db4c..abaa74ab2fa 100644 --- a/homeassistant/components/mysensors/climate.py +++ b/homeassistant/components/mysensors/climate.py @@ -77,7 +77,7 @@ class MySensorsHVAC(mysensors.device.MySensorsEntity, ClimateEntity): _attr_hvac_modes = OPERATION_LIST @property - def supported_features(self) -> int: + def supported_features(self) -> ClimateEntityFeature | int: """Return the list of supported features.""" features = 0 set_req = self.gateway.const.SetReq diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index 70d4a12406a..4c11d5e781b 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -176,7 +176,7 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): self._attr_supported_features = self.get_features() self._attr_precision = PRECISION_TENTHS - def get_features(self) -> int: + def get_features(self) -> ClimateEntityFeature | int: """Get supported features.""" features = 0 for key in self.device_data.full_features: From 4b89d087bb62dd1da8f5a3728619c670df7c9b90 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 16 Nov 2022 15:08:17 -0600 Subject: [PATCH 0491/1033] Bump home-assistant-bluetooth to 1.8.1 (#82222) changelog: https://github.com/home-assistant-libs/home-assistant-bluetooth/compare/v1.8.0...v1.8.1 --- homeassistant/package_constraints.txt | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 72267ae7f14..394de1abc83 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -21,7 +21,7 @@ cryptography==38.0.3 dbus-fast==1.74.1 fnvhash==0.1.0 hass-nabucasa==0.56.0 -home-assistant-bluetooth==1.8.0 +home-assistant-bluetooth==1.8.1 home-assistant-frontend==20221108.0 httpx==0.23.0 ifaddr==0.1.7 diff --git a/pyproject.toml b/pyproject.toml index 6aa8c9d3f0b..a32414da464 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ dependencies = [ # When bumping httpx, please check the version pins of # httpcore, anyio, and h11 in gen_requirements_all "httpx==0.23.0", - "home-assistant-bluetooth==1.8.0", + "home-assistant-bluetooth==1.8.1", "ifaddr==0.1.7", "jinja2==3.1.2", "lru-dict==1.1.8", diff --git a/requirements.txt b/requirements.txt index e748db517b5..ba4d9fad807 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ bcrypt==3.1.7 certifi>=2021.5.30 ciso8601==2.2.0 httpx==0.23.0 -home-assistant-bluetooth==1.8.0 +home-assistant-bluetooth==1.8.1 ifaddr==0.1.7 jinja2==3.1.2 lru-dict==1.1.8 From ba8f69a5ce0d2862c509cba06468a0373a680427 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 16 Nov 2022 23:51:38 +0100 Subject: [PATCH 0492/1033] Cache improvements [ci] (#80898) * Cleanup old pip caches * Add workflow to delete caches for closed PRs --- .github/workflows/cache.yml | 56 ++++++++++++++++++++++++++++++++ .github/workflows/ci.yaml | 64 +++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 .github/workflows/cache.yml diff --git a/.github/workflows/cache.yml b/.github/workflows/cache.yml new file mode 100644 index 00000000000..8a1bcad6570 --- /dev/null +++ b/.github/workflows/cache.yml @@ -0,0 +1,56 @@ +name: Delete Caches + +# yamllint disable-line rule:truthy +on: + pull_request: + types: [closed] + workflow_dispatch: + inputs: + pr-number: + description: "Closed PR number" + type: string + required: true + +jobs: + delete: + name: "Delete unused caches (PR #${{ github.event.inputs.pr-number || github.event.number }})" + runs-on: ubuntu-latest + permissions: + actions: write + steps: + - name: Delete caches + run: | + ref="refs/pull/${{ github.event.inputs.pr-number || github.event.number }}/merge" + + page=1 + per_page=100 + del_count=0 + + while true; do + res=$(curl -fsS \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: token ${{ github.token }}" \ + "https://api.github.com/repos/${{ github.repository }}/actions/caches?per_page=$per_page&page=$page") + + res_count=$(echo $res | jq '.actions_caches | length') + if [ $res_count -eq 0 ]; then break; fi + + targets=$(echo $res | jq \ + --arg ref "$ref" \ + '.actions_caches[] | select(.ref == $ref) | del(.created_at, .size_in_bytes, .version)') + echo "$targets" + + for id in $(echo $targets | jq '.id'); do + curl -fsS \ + -X DELETE \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: token ${{ github.token }}" \ + "https://api.github.com/repos/${{ github.repository }}/actions/caches/$id" + ((del_count+=1)) + done + + if [ $res_count -lt $per_page ]; then break; fi + ((page+=1)) + done + + echo "Delete $del_count caches." diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 262229e6ade..cd21717142a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -551,6 +551,70 @@ jobs: pip install --cache-dir=$PIP_CACHE -r requirements_test.txt --use-deprecated=legacy-resolver pip install -e . + cleanup-cache: + name: Cleanup old pip caches + runs-on: ubuntu-20.04 + needs: + - base + permissions: + actions: write + steps: + - name: Cleanup + run: | + sort="last_accessed_at" + page=1 + per_page=100 + del_count=0 + index=0 + + all_versions=($(echo $ALL_PYTHON_VERSIONS | tr -d \',[])) + + if [[ "${{ github.ref }}" == "refs/heads/dev" ]] \ + || [[ "${{ contains(github.event.pull_request.labels.*.name, 'ci-keep-cache') }}" == "true" ]] + then + index=1 + echo "Keep one entry per Python version" + fi + + while true; do + res=$(curl -fsS \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: token ${{ github.token }}" \ + "https://api.github.com/repos/${{ github.repository }}/actions/caches?sort=$sort&per_page=$per_page&page=$page") + + res_count=$(echo $res | jq '.actions_caches | length') + if [ $res_count -eq 0 ]; then break; fi + + echo "Check ref: ${{ github.ref }}" + for version in ${all_versions[@]}; do + key="${{ runner.os }}-$version.+-pip" + echo "Check key regex: $key" + + # Select all cache keys which match the ref and key regex + # Keep the first entry for 'ref/heads/dev' + targets=$(echo $res | jq \ + --arg ref "${{ github.ref }}" --arg key "$key" --arg index $index \ + '[.actions_caches[] | select(.ref == $ref) | select(.key | test($key))][$index | fromjson:][] | del(.created_at, .size_in_bytes, .version)') + + if [ $(echo $targets | jq -s 'length') -eq 0 ]; then continue; fi + echo "$targets" + + for id in $(echo $targets | jq '.id'); do + curl -fsS \ + -X DELETE \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: token ${{ github.token }}" \ + "https://api.github.com/repos/${{ github.repository }}/actions/caches/$id" + ((del_count += 1)) + done + done + + if [ $res_count -lt $per_page ]; then break; fi + ((page += 1)) + done + + echo "Deleted $del_count caches." + hassfest: name: Check hassfest runs-on: ubuntu-20.04 From c999953514c7ebf44356fdd229e558b44c7b3d06 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 17 Nov 2022 00:26:24 +0000 Subject: [PATCH 0493/1033] [ci skip] Translation update --- .../components/aranet/translations/nl.json | 22 ++++++ .../components/esphome/translations/el.json | 6 ++ .../components/esphome/translations/id.json | 6 ++ .../components/hassio/translations/el.json | 74 +++++++++++++++++++ .../homeassistant/translations/bg.json | 3 + .../homeassistant/translations/de.json | 4 + .../homeassistant/translations/el.json | 6 ++ .../homeassistant/translations/es.json | 4 + .../homeassistant/translations/hu.json | 4 + .../homeassistant/translations/id.json | 4 + .../homeassistant/translations/pl.json | 4 + .../homeassistant_yellow/translations/de.json | 40 ++++++++++ .../homeassistant_yellow/translations/el.json | 40 ++++++++++ .../homeassistant_yellow/translations/id.json | 40 ++++++++++ .../homeassistant_yellow/translations/nl.json | 7 ++ .../homeassistant_yellow/translations/pl.json | 40 ++++++++++ .../components/knx/translations/el.json | 36 ++++++++- .../components/knx/translations/nl.json | 46 +++++++++++- .../components/mqtt/translations/el.json | 17 ++++- .../nibe_heatpump/translations/el.json | 5 ++ .../components/onvif/translations/de.json | 3 +- .../components/onvif/translations/el.json | 3 +- .../components/onvif/translations/es.json | 3 +- .../components/onvif/translations/et.json | 3 +- .../components/onvif/translations/hu.json | 3 +- .../components/onvif/translations/id.json | 3 +- .../components/onvif/translations/no.json | 3 +- .../components/onvif/translations/pl.json | 3 +- .../components/onvif/translations/pt-BR.json | 3 +- .../components/openuv/translations/nl.json | 9 ++- .../pushbullet/translations/nl.json | 19 +++++ .../sensibo/translations/sensor.nl.json | 4 + .../components/shelly/translations/el.json | 13 ++++ .../components/shelly/translations/es.json | 13 ++++ .../components/shelly/translations/hu.json | 13 ++++ .../components/shelly/translations/id.json | 13 ++++ .../components/shelly/translations/no.json | 13 ++++ .../components/shelly/translations/pt-BR.json | 13 ++++ .../unifiprotect/translations/el.json | 21 ++++++ .../unifiprotect/translations/id.json | 4 + .../wake_on_lan/translations/de.json | 8 ++ .../wake_on_lan/translations/el.json | 8 ++ .../wake_on_lan/translations/en.json | 8 ++ .../wake_on_lan/translations/es.json | 8 ++ .../wake_on_lan/translations/et.json | 8 ++ .../wake_on_lan/translations/hu.json | 8 ++ .../wake_on_lan/translations/id.json | 8 ++ .../wake_on_lan/translations/no.json | 8 ++ .../wake_on_lan/translations/pl.json | 8 ++ .../wake_on_lan/translations/pt-BR.json | 8 ++ .../components/zamg/translations/nl.json | 3 +- 51 files changed, 639 insertions(+), 14 deletions(-) create mode 100644 homeassistant/components/aranet/translations/nl.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/de.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/el.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/id.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/nl.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/pl.json create mode 100644 homeassistant/components/pushbullet/translations/nl.json create mode 100644 homeassistant/components/wake_on_lan/translations/de.json create mode 100644 homeassistant/components/wake_on_lan/translations/el.json create mode 100644 homeassistant/components/wake_on_lan/translations/en.json create mode 100644 homeassistant/components/wake_on_lan/translations/es.json create mode 100644 homeassistant/components/wake_on_lan/translations/et.json create mode 100644 homeassistant/components/wake_on_lan/translations/hu.json create mode 100644 homeassistant/components/wake_on_lan/translations/id.json create mode 100644 homeassistant/components/wake_on_lan/translations/no.json create mode 100644 homeassistant/components/wake_on_lan/translations/pl.json create mode 100644 homeassistant/components/wake_on_lan/translations/pt-BR.json diff --git a/homeassistant/components/aranet/translations/nl.json b/homeassistant/components/aranet/translations/nl.json new file mode 100644 index 00000000000..64f3286888c --- /dev/null +++ b/homeassistant/components/aranet/translations/nl.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Apparaat is al geconfigureerd" + }, + "error": { + "unknown": "Onverwachte fout" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Wilt u {name} instellen?" + }, + "user": { + "data": { + "address": "Apparaat" + }, + "description": "Kies een apparaat om in te stellen" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/el.json b/homeassistant/components/esphome/translations/el.json index 405b1a55a89..041628a3673 100644 --- a/homeassistant/components/esphome/translations/el.json +++ b/homeassistant/components/esphome/translations/el.json @@ -43,5 +43,11 @@ "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03ba\u03cc\u03bc\u03b2\u03bf\u03c5 [ESPHome](https://esphomelib.com/)." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03b2\u03b5\u03bb\u03c4\u03b9\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03be\u03b9\u03bf\u03c0\u03b9\u03c3\u03c4\u03af\u03b1 \u03ba\u03b1\u03b9 \u03c4\u03b7\u03bd \u03b1\u03c0\u03cc\u03b4\u03bf\u03c3\u03b7 \u03c4\u03bf\u03c5 Bluetooth, \u03c3\u03c5\u03bd\u03b9\u03c3\u03c4\u03bf\u03cd\u03bc\u03b5 \u03b1\u03bd\u03b5\u03c0\u03b9\u03c6\u03cd\u03bb\u03b1\u03ba\u03c4\u03b1 \u03c4\u03b7\u03bd \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 {name} \u03bc\u03b5 ESPHome 2022.11.0 \u03ae \u03bd\u03b5\u03cc\u03c4\u03b5\u03c1\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7.", + "title": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 {name} \u03bc\u03b5 ESPHome 2022.11.0 \u03ae \u03bd\u03b5\u03cc\u03c4\u03b5\u03c1\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/id.json b/homeassistant/components/esphome/translations/id.json index 155bf33d986..8a82425a924 100644 --- a/homeassistant/components/esphome/translations/id.json +++ b/homeassistant/components/esphome/translations/id.json @@ -43,5 +43,11 @@ "description": "Masukkan pengaturan koneksi node [ESPHome]({esphome_url})." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "Untuk meningkatkan keandalan dan performa Bluetooth, kami sangat menyarankan untuk memperbarui {name} dengan ESPHome 2022.11.0 atau yang lebih baru.", + "title": "Perbarui {name} dengan ESPHome 2022.11.0 atau yang lebih baru" + } } } \ No newline at end of file diff --git a/homeassistant/components/hassio/translations/el.json b/homeassistant/components/hassio/translations/el.json index 9e9b32d7ce3..cbe923f2888 100644 --- a/homeassistant/components/hassio/translations/el.json +++ b/homeassistant/components/hassio/translations/el.json @@ -1,4 +1,78 @@ { + "issues": { + "unsupported_connectivity_check": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c4\u03bf Home Assistant \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b4\u03b9\u03bf\u03c1\u03af\u03c3\u03b5\u03b9 \u03c0\u03cc\u03c4\u03b5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03bc\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03bf \u0394\u03b9\u03b1\u03b4\u03af\u03ba\u03c4\u03c5\u03bf. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u039f \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c3\u03c5\u03bd\u03b4\u03b5\u03c3\u03b9\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2" + }, + "unsupported_content_trust": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03bf \u0392\u03bf\u03b7\u03b8\u03cc\u03c2 \u039f\u03b9\u03ba\u03af\u03b1\u03c2 \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b5\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03b5\u03b9 \u03cc\u03c4\u03b9 \u03c4\u03bf \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf \u03c0\u03bf\u03c5 \u03b5\u03ba\u03c4\u03b5\u03bb\u03b5\u03af\u03c4\u03b1\u03b9 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03be\u03b9\u03cc\u03c0\u03b9\u03c3\u03c4\u03bf \u03ba\u03b1\u03b9 \u03cc\u03c4\u03b9 \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c4\u03c1\u03bf\u03c0\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03b5\u03b9\u03c3\u03b2\u03bf\u03bb\u03b5\u03af\u03c2. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u039f \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03b5\u03bc\u03c0\u03b9\u03c3\u03c4\u03bf\u03c3\u03cd\u03bd\u03b7\u03c2 \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2" + }, + "unsupported_dbus": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c4\u03bf D-Bus \u03b4\u03b5\u03bd \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03b5\u03af \u03c3\u03c9\u03c3\u03c4\u03ac. \u03a0\u03bf\u03bb\u03bb\u03ac \u03c0\u03c1\u03ac\u03b3\u03bc\u03b1\u03c4\u03b1 \u03b1\u03c0\u03bf\u03c4\u03c5\u03b3\u03c7\u03ac\u03bd\u03bf\u03c5\u03bd \u03c7\u03c9\u03c1\u03af\u03c2 \u03b1\u03c5\u03c4\u03cc, \u03ba\u03b1\u03b8\u03ce\u03c2 \u03bf \u0395\u03c0\u03cc\u03c0\u03c4\u03b7\u03c2 \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b5\u03c0\u03b9\u03ba\u03bf\u03b9\u03bd\u03c9\u03bd\u03ae\u03c3\u03b5\u03b9 \u03bc\u03b5 \u03c4\u03bf\u03bd \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0396\u03b7\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 D-Bus" + }, + "unsupported_dns_server": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03bf \u03c0\u03b1\u03c1\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf\u03c2 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae\u03c2 DNS \u03b4\u03b5\u03bd \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03b5\u03af \u03c3\u03c9\u03c3\u03c4\u03ac \u03ba\u03b1\u03b9 \u03b7 \u03b5\u03bd\u03b1\u03bb\u03bb\u03b1\u03ba\u03c4\u03b9\u03ba\u03ae \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae DNS \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0396\u03b7\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae DNS" + }, + "unsupported_docker_configuration": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03bf \u03b4\u03b1\u03af\u03bc\u03bf\u03bd\u03b1\u03c2 Docker \u03b5\u03ba\u03c4\u03b5\u03bb\u03b5\u03af\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03b1\u03c0\u03c1\u03bf\u03c3\u03b4\u03cc\u03ba\u03b7\u03c4\u03bf \u03c4\u03c1\u03cc\u03c0\u03bf. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u03a4\u03bf Docker \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af \u03c3\u03c9\u03c3\u03c4\u03ac" + }, + "unsupported_docker_version": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03bb\u03ac\u03b8\u03bf\u03c2 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03c4\u03bf\u03c5 Docker. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c9\u03c3\u03c4\u03ae \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0388\u03ba\u03b4\u03bf\u03c3\u03b7 Docker" + }, + "unsupported_job_conditions": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03bc\u03af\u03b1 \u03ae \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c3\u03c5\u03bd\u03b8\u03ae\u03ba\u03b5\u03c2 \u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1\u03c2 \u03ad\u03c7\u03bf\u03c5\u03bd \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03bf\u03b9 \u03bf\u03c0\u03bf\u03af\u03b5\u03c2 \u03c0\u03c1\u03bf\u03c3\u03c4\u03b1\u03c4\u03b5\u03cd\u03bf\u03c5\u03bd \u03b1\u03c0\u03cc \u03b1\u03c0\u03c1\u03bf\u03c3\u03b4\u03cc\u03ba\u03b7\u03c4\u03b5\u03c2 \u03b2\u03bb\u03ac\u03b2\u03b5\u03c2 \u03ba\u03b1\u03b9 \u03c3\u03c0\u03b1\u03c3\u03af\u03bc\u03b1\u03c4\u03b1. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u039f\u03b9 \u03c0\u03c1\u03bf\u03c3\u03c4\u03b1\u03c3\u03af\u03b5\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b5\u03c2" + }, + "unsupported_lxc": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03b5\u03ba\u03c4\u03b5\u03bb\u03b5\u03af\u03c4\u03b1\u03b9 \u03c3\u03b5 \u03b5\u03b9\u03ba\u03bf\u03bd\u03b9\u03ba\u03ae \u03bc\u03b7\u03c7\u03b1\u03bd\u03ae LXC. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0395\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5 LXC" + }, + "unsupported_network_manager": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c4\u03bf Network Manager \u03bb\u03b5\u03af\u03c0\u03b5\u03b9, \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc \u03ae \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af \u03b5\u03c3\u03c6\u03b1\u03bb\u03bc\u03ad\u03bd\u03b1. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0396\u03b7\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b4\u03b9\u03b1\u03c7\u03b5\u03af\u03c1\u03b9\u03c3\u03b7\u03c2 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5" + }, + "unsupported_os": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c4\u03bf \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03b9\u03ba\u03cc \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bb\u03b5\u03b3\u03c7\u03b8\u03b5\u03af \u03ae \u03c3\u03c5\u03bd\u03c4\u03b7\u03c1\u03b7\u03b8\u03b5\u03af \u03b3\u03b9\u03b1 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03bc\u03b5 \u03c4\u03bf Supervisor. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03c0\u03c1\u03bf\u03c2 \u03c4\u03bf \u03bf\u03c0\u03bf\u03af\u03bf \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03b9\u03ba\u03ac \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03b9\u03ba\u03cc \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1" + }, + "unsupported_os_agent": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c4\u03bf OS-Agent \u03bb\u03b5\u03af\u03c0\u03b5\u03b9, \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc \u03ae \u03ad\u03c7\u03b5\u03b9 \u03b5\u03c3\u03c6\u03b1\u03bb\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0396\u03b7\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 OS-Agent" + }, + "unsupported_restart_policy": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03ad\u03bd\u03b1 \u03ba\u03bf\u03bd\u03c4\u03ad\u03b9\u03bd\u03b5\u03c1 Docker \u03ad\u03c7\u03b5\u03b9 \u03ad\u03bd\u03b1 \u03c3\u03cd\u03bd\u03bf\u03bb\u03bf \u03c0\u03bf\u03bb\u03b9\u03c4\u03b9\u03ba\u03ae\u03c2 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7\u03c2 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03bc\u03c0\u03bf\u03c1\u03bf\u03cd\u03c3\u03b5 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03ba\u03b1\u03bb\u03ad\u03c3\u03b5\u03b9 \u03c0\u03c1\u03bf\u03b2\u03bb\u03ae\u03bc\u03b1\u03c4\u03b1 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u03a0\u03bf\u03bb\u03b9\u03c4\u03b9\u03ba\u03ae \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7\u03c2 \u03ba\u03bf\u03bd\u03c4\u03ad\u03b9\u03bd\u03b5\u03c1" + }, + "unsupported_software": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03c4\u03b5\u03af \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf \u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc \u03b5\u03ba\u03c4\u03cc\u03c2 \u03c4\u03bf\u03c5 \u03bf\u03b9\u03ba\u03bf\u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2 \u03c4\u03bf\u03c5 Home Assistant. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc" + }, + "unsupported_source_mods": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03bf \u03c0\u03b7\u03b3\u03b1\u03af\u03bf\u03c2 \u03ba\u03ce\u03b4\u03b9\u03ba\u03b1\u03c2 \u03c4\u03bf\u03c5 \u03b5\u03c0\u03cc\u03c0\u03c4\u03b7 \u03ad\u03c7\u03b5\u03b9 \u03c4\u03c1\u03bf\u03c0\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u03a4\u03c1\u03bf\u03c0\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03b9\u03c2 \u03c0\u03b7\u03b3\u03ae\u03c2 \u03b5\u03c0\u03cc\u03c0\u03c4\u03b7" + }, + "unsupported_supervisor_version": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03bc\u03b9\u03b1 \u03c0\u03b1\u03bb\u03b9\u03ac \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03c4\u03bf\u03c5 Supervisor \u03ba\u03b1\u03b9 \u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0388\u03ba\u03b4\u03bf\u03c3\u03b7 \u03b5\u03c0\u03cc\u03c0\u03c4\u03b7" + }, + "unsupported_systemd": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c4\u03bf Systemd \u03bb\u03b5\u03af\u03c0\u03b5\u03b9, \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc \u03ae \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af \u03b5\u03c3\u03c6\u03b1\u03bb\u03bc\u03ad\u03bd\u03b1. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0396\u03b7\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 Systemd" + }, + "unsupported_systemd_journal": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c4\u03bf Systemd Journal \u03ae/\u03ba\u03b1\u03b9 \u03b7 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03c0\u03cd\u03bb\u03b7\u03c2 \u03bb\u03b5\u03af\u03c0\u03bf\u03c5\u03bd, \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03ac \u03ae \u03ad\u03c7\u03bf\u03c5\u03bd \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af \u03b5\u03c3\u03c6\u03b1\u03bb\u03bc\u03ad\u03bd\u03b1. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0396\u03b7\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 Systemd Journal" + }, + "unsupported_systemd_resolved": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c4\u03bf Systemd Resolved \u03bb\u03b5\u03af\u03c0\u03b5\u03b9, \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc \u03ae \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af \u03b5\u03c3\u03c6\u03b1\u03bb\u03bc\u03ad\u03bd\u03b1. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0395\u03c0\u03b9\u03bb\u03cd\u03b8\u03b7\u03ba\u03b1\u03bd \u03c0\u03c1\u03bf\u03b2\u03bb\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b1\u03c0\u03cc \u03c4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1" + } + }, "system_health": { "info": { "agent_version": "\u0388\u03ba\u03b4\u03bf\u03c3\u03b7 Agent", diff --git a/homeassistant/components/homeassistant/translations/bg.json b/homeassistant/components/homeassistant/translations/bg.json index 2c33a7925c2..32a3f445483 100644 --- a/homeassistant/components/homeassistant/translations/bg.json +++ b/homeassistant/components/homeassistant/translations/bg.json @@ -3,6 +3,9 @@ "historic_currency": { "description": "\u0412\u0430\u043b\u0443\u0442\u0430\u0442\u0430 {currency} \u0432\u0435\u0447\u0435 \u043d\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430, \u043c\u043e\u043b\u044f, \u043f\u0440\u0435\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0439\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 \u0432\u0430\u043b\u0443\u0442\u0430\u0442\u0430.", "title": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430\u0442\u0430 \u0432\u0430\u043b\u0443\u0442\u0430 \u0432\u0435\u0447\u0435 \u043d\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430" + }, + "python_version": { + "title": "\u041f\u043e\u0434\u0434\u0440\u044a\u0436\u043a\u0430\u0442\u0430 \u0437\u0430 Python {current_python_version} \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430" } }, "system_health": { diff --git a/homeassistant/components/homeassistant/translations/de.json b/homeassistant/components/homeassistant/translations/de.json index 61ca66f49a9..85737129f53 100644 --- a/homeassistant/components/homeassistant/translations/de.json +++ b/homeassistant/components/homeassistant/translations/de.json @@ -3,6 +3,10 @@ "historic_currency": { "description": "Die W\u00e4hrung {currency} wird nicht mehr verwendet, bitte konfiguriere die W\u00e4hrungskonfiguration neu.", "title": "Die konfigurierte W\u00e4hrung ist nicht mehr in Gebrauch" + }, + "python_version": { + "description": "Die Unterst\u00fctzung f\u00fcr die Ausf\u00fchrung von Home Assistant in der aktuell verwendeten Python-Version {current_python_version} ist veraltet und wird in Home Assistant {breaks_in_ha_version} entfernt. Bitte aktualisiere Python auf {required_python_version}, um zu verhindern, dass deine Home Assistant-Instanz besch\u00e4digt wird.", + "title": "Die Unterst\u00fctzung f\u00fcr Python {current_python_version} wird entfernt" } }, "system_health": { diff --git a/homeassistant/components/homeassistant/translations/el.json b/homeassistant/components/homeassistant/translations/el.json index 9c345d511e1..ac8aedc8297 100644 --- a/homeassistant/components/homeassistant/translations/el.json +++ b/homeassistant/components/homeassistant/translations/el.json @@ -1,4 +1,10 @@ { + "issues": { + "python_version": { + "description": "\u0397 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b5\u03ba\u03c4\u03ad\u03bb\u03b5\u03c3\u03b7 \u03c4\u03bf\u03c5 Home Assistant \u03c3\u03c4\u03b7\u03bd \u03c4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bc\u03b5\u03bd\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 Python {current_python_version} \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af \u03ba\u03b1\u03b9 \u03b8\u03b1 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af \u03c3\u03c4\u03bf Home Assistant {breaks_in_ha_version} . \u0391\u03bd\u03b1\u03b2\u03b1\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd Python \u03c3\u03b5 {required_python_version} \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b1\u03c0\u03bf\u03c4\u03c1\u03ad\u03c8\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03c1\u03bf\u03c6\u03ae \u03c4\u03b7\u03c2 \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03af\u03b1\u03c2 \u03c4\u03bf\u03c5 Home Assistant.", + "title": "\u0397 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd Python {current_python_version} \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" + } + }, "system_health": { "info": { "arch": "\u0391\u03c1\u03c7\u03b9\u03c4\u03b5\u03ba\u03c4\u03bf\u03bd\u03b9\u03ba\u03ae CPU", diff --git a/homeassistant/components/homeassistant/translations/es.json b/homeassistant/components/homeassistant/translations/es.json index e0cb62bee3f..6d64ab4747b 100644 --- a/homeassistant/components/homeassistant/translations/es.json +++ b/homeassistant/components/homeassistant/translations/es.json @@ -3,6 +3,10 @@ "historic_currency": { "description": "La moneda {currency} ya no est\u00e1 en uso, por favor, vuelve a configurar la moneda.", "title": "La moneda configurada ya no est\u00e1 en uso" + }, + "python_version": { + "description": "La compatibilidad con la ejecuci\u00f3n de Home Assistant en la versi\u00f3n de Python utilizada actualmente {current_python_version} est\u00e1 obsoleta y se eliminar\u00e1 en Home Assistant {breaks_in_ha_version}. Actualiza Python a {required_python_version} para evitar que tu instancia de Home Assistant se rompa.", + "title": "Se va a eliminar la compatibilidad con Python {current_python_version}" } }, "system_health": { diff --git a/homeassistant/components/homeassistant/translations/hu.json b/homeassistant/components/homeassistant/translations/hu.json index c7fdde1ca44..1e290293009 100644 --- a/homeassistant/components/homeassistant/translations/hu.json +++ b/homeassistant/components/homeassistant/translations/hu.json @@ -3,6 +3,10 @@ "historic_currency": { "description": "{currency} p\u00e9nznem m\u00e1r nincs haszn\u00e1latban, k\u00e9rj\u00fck, konfigur\u00e1lja \u00fajra a p\u00e9nznemet.", "title": "A be\u00e1ll\u00edtott p\u00e9nznem m\u00e1r nincs haszn\u00e1latban" + }, + "python_version": { + "description": "A Home Assistant futtat\u00e1s\u00e1nak t\u00e1mogat\u00e1sa az aktu\u00e1lisan haszn\u00e1lt Python-verzi\u00f3ban {current_python_version} elavult, \u00e9s a Home Assistant {breaks_in_ha_version} verzi\u00f3j\u00e1ban elt\u00e1vol\u00edt\u00e1sra ker\u00fcl. K\u00e9rj\u00fck, friss\u00edtse a Pythont a {required_python_version} verzi\u00f3ra, hogy megakad\u00e1lyozza a Home Assistant j\u00f6v\u0151beni m\u0171k\u00f6d\u00e9sk\u00e9ptelens\u00e9g\u00e9t.", + "title": "A Python {current_python_version} t\u00e1mogat\u00e1sa megsz\u0171nik" } }, "system_health": { diff --git a/homeassistant/components/homeassistant/translations/id.json b/homeassistant/components/homeassistant/translations/id.json index 0be9cd9f286..a5b079caf8f 100644 --- a/homeassistant/components/homeassistant/translations/id.json +++ b/homeassistant/components/homeassistant/translations/id.json @@ -3,6 +3,10 @@ "historic_currency": { "description": "Mata uang {currency} tidak lagi digunakan, konfigurasikan ulang konfigurasi mata uang.", "title": "Mata uang yang dikonfigurasi tidak lagi digunakan" + }, + "python_version": { + "description": "Dukungan untuk menjalankan Home Assistant dalam versi Python yang digunakan saat ini {current_python_version} sudah usang dan akan dihapus di Home Assistant {breaks_in_ha_version}. Tingkatkan Python ke {required_python_version} untuk mencegah instans Home Assistant Anda rusak.", + "title": "Dukungan untuk Python {current_python_version} dalam proses penghapusan" } }, "system_health": { diff --git a/homeassistant/components/homeassistant/translations/pl.json b/homeassistant/components/homeassistant/translations/pl.json index b40abbe44e7..02383146b53 100644 --- a/homeassistant/components/homeassistant/translations/pl.json +++ b/homeassistant/components/homeassistant/translations/pl.json @@ -3,6 +3,10 @@ "historic_currency": { "description": "Waluta {currency} nie jest ju\u017c u\u017cywana. Zmie\u0144 konfiguracj\u0119 waluty.", "title": "Skonfigurowana waluta nie jest ju\u017c u\u017cywana" + }, + "python_version": { + "description": "Obs\u0142uga uruchamiania Home Assistanta w obecnie u\u017cywanej wersji Pythona {current_python_version} jest przestarza\u0142a i zostanie usuni\u0119ta w Home Assistant {breaks_in_ha_version}. Zaktualizuj Pythona do wersji {required_python_version}, aby zapobiec awarii Home Assistanta.", + "title": "Obs\u0142uga Pythona w wersji {current_python_version} zostanie usuni\u0119ta" } }, "system_health": { diff --git a/homeassistant/components/homeassistant_yellow/translations/de.json b/homeassistant/components/homeassistant_yellow/translations/de.json new file mode 100644 index 00000000000..aae6bed9fa5 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/de.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Silicon Labs Multiprotokoll-Zusatzinformationen konnten nicht abgerufen werden.", + "addon_install_failed": "Die Installation des Silicon Labs Multiprotokoll-Add-Ons ist fehlgeschlagen.", + "addon_set_config_failed": "Die Silicon Labs Multiprotokoll-Konfiguration konnte nicht eingestellt werden.", + "addon_start_failed": "Das Silicon Labs Multiprotokoll-Add-On konnte nicht gestartet werden.", + "not_hassio": "Die Hardwareoptionen k\u00f6nnen nur auf HassOS-Installationen konfiguriert werden." + }, + "error": { + "unknown": "Unerwarteter Fehler" + }, + "progress": { + "install_addon": "Bitte warte, bis die Installation des Silicon Labs Multiprotokoll-Add-ons abgeschlossen ist. Dies kann einige Minuten dauern.", + "start_addon": "Bitte warte, bis der Start des Silicon Labs Multiprotokoll-Add-Ons abgeschlossen ist. Dies kann einige Sekunden dauern." + }, + "step": { + "addon_installed_other_device": { + "title": "Die Multiprotokollunterst\u00fctzung ist bereits f\u00fcr ein anderes Ger\u00e4t aktiviert" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Multiprotokollunterst\u00fctzung aktivieren" + }, + "description": "Wenn die Multiprotokoll-Unterst\u00fctzung aktiviert ist, kann das IEEE 802.15.4-Funkger\u00e4t des Home Assistant Yellow sowohl f\u00fcr Zigbee als auch f\u00fcr Thread (von Matter verwendet) gleichzeitig verwendet werden. Hinweis: Dies ist eine experimentelle Funktion.", + "title": "Aktiviere die Multiprotokollunterst\u00fctzung auf dem IEEE 802.15.4-Funkger\u00e4t" + }, + "install_addon": { + "title": "Die Installation des Silicon Labs Multiprotokoll-Add-Ons hat begonnen" + }, + "show_revert_guide": { + "description": "Wenn du zu einer reinen Zigbee-Firmware wechseln m\u00f6chtest, f\u00fchre bitte die folgenden manuellen Schritte aus:\n\n * Entferne das Silicon Labs Multiprotokoll-Add-On\n\n * Flashe die reine Zigbee-Firmware, folge der Anleitung unter https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Rekonfiguriere ZHA, um die Einstellungen auf das neu geflashte Funkger\u00e4t zu migrieren.", + "title": "Multiprotokoll-Unterst\u00fctzung ist f\u00fcr dieses Ger\u00e4t aktiviert" + }, + "start_addon": { + "title": "Das Silicon Labs Multiprotokoll-Add-on wird gestartet." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/el.json b/homeassistant/components/homeassistant_yellow/translations/el.json new file mode 100644 index 00000000000..71f89914d28 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/el.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03bb\u03ae\u03c8\u03b7\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03b9\u03ce\u03bd \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", + "addon_install_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", + "addon_set_config_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd Silicon Labs.", + "addon_start_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", + "not_hassio": "\u039f\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c5\u03bb\u03b9\u03ba\u03bf\u03cd \u03bc\u03c0\u03bf\u03c1\u03bf\u03cd\u03bd \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03bf\u03cd\u03bd \u03bc\u03cc\u03bd\u03bf \u03c3\u03b5 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 HassOS." + }, + "error": { + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "progress": { + "install_addon": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03c0\u03b5\u03c1\u03b9\u03bc\u03ad\u03bd\u03b5\u03c4\u03b5 \u03bc\u03ad\u03c7\u03c1\u03b9 \u03bd\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03c9\u03b8\u03b5\u03af \u03b7 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b4\u03b9\u03b1\u03c1\u03ba\u03ad\u03c3\u03b5\u03b9 \u03b1\u03c1\u03ba\u03b5\u03c4\u03ac \u03bb\u03b5\u03c0\u03c4\u03ac.", + "start_addon": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03c0\u03b5\u03c1\u03b9\u03bc\u03ad\u03bd\u03b5\u03c4\u03b5 \u03bc\u03ad\u03c7\u03c1\u03b9 \u03bd\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03c9\u03b8\u03b5\u03af \u03b7 \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 \u03c0\u03c1\u03bf\u03b3\u03c1\u03ac\u03bc\u03bc\u03b1\u03c4\u03bf\u03c2 Silicon Labs Multiprotocol. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b4\u03b9\u03b1\u03c1\u03ba\u03ad\u03c3\u03b5\u03b9 \u03bc\u03b5\u03c1\u03b9\u03ba\u03ac \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1." + }, + "step": { + "addon_installed_other_device": { + "title": "\u0397 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b3\u03b9\u03b1 \u03bc\u03b9\u03b1 \u03ac\u03bb\u03bb\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7\u03c2 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd" + }, + "description": "\u038c\u03c4\u03b1\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd, \u03b7 \u03c1\u03b1\u03b4\u03b9\u03bf\u03c3\u03c5\u03c7\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 IEEE 802.15.4 \u03c4\u03bf\u03c5 Home Assistant Yellow \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03c4\u03b1\u03c5\u03c4\u03cc\u03c7\u03c1\u03bf\u03bd\u03b1 \u03c4\u03cc\u03c3\u03bf \u03b3\u03b9\u03b1 \u03c4\u03bf Zigbee \u03cc\u03c3\u03bf \u03ba\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03bf Thread (\u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Matter). \u03a3\u03b7\u03bc\u03b5\u03af\u03c9\u03c3\u03b7: \u03a0\u03c1\u03cc\u03ba\u03b5\u03b9\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03bc\u03b9\u03b1 \u03c0\u03b5\u03b9\u03c1\u03b1\u03bc\u03b1\u03c4\u03b9\u03ba\u03ae \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1.", + "title": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7\u03c2 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd \u03c3\u03c4\u03bf\u03bd \u03b1\u03c3\u03cd\u03c1\u03bc\u03b1\u03c4\u03bf IEEE 802.15.4" + }, + "install_addon": { + "title": "\u0397 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol \u03ad\u03c7\u03b5\u03b9 \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03b9" + }, + "show_revert_guide": { + "description": "\u0395\u03ac\u03bd \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03c4\u03b5 \u03c3\u03b5 \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc \u03bc\u03cc\u03bd\u03bf Zigbee, \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03b2\u03ae\u03bc\u03b1\u03c4\u03b1: \n\n * \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Silicon Labs Multiprotocol \n\n * \u0391\u03bd\u03b1\u03b2\u03bf\u03c3\u03b2\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03bc\u03cc\u03bd\u03bf \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc Zigbee, \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03bf\u03b4\u03b7\u03b3\u03cc \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manual. \n\n * \u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03bf ZHA \u03b3\u03b9\u03b1 \u03bc\u03b5\u03c4\u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03c9\u03bd \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c9\u03bd \u03c3\u03c4\u03bf \u03b1\u03bd\u03b1\u03bd\u03b5\u03c9\u03bc\u03ad\u03bd\u03bf \u03c1\u03b1\u03b4\u03b9\u03cc\u03c6\u03c9\u03bd\u03bf", + "title": "\u0397 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" + }, + "start_addon": { + "title": "\u039e\u03b5\u03ba\u03b9\u03bd\u03ac \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Silicon Labs Multiprotocol." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/id.json b/homeassistant/components/homeassistant_yellow/translations/id.json new file mode 100644 index 00000000000..6c7dd05faec --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/id.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Gagal mendapatkan info add-on Silicon Labs Multiprotocol.", + "addon_install_failed": "Gagal menginstal add-on Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Gagal mengatur konfigurasi Silicon Labs Multiprotocol.", + "addon_start_failed": "Gagal memulai add-on Silicon Labs Multiprotocol.", + "not_hassio": "Opsi perangkat keras hanya bisa dikonfigurasi pada instalasi HassOS." + }, + "error": { + "unknown": "Kesalahan yang tidak diharapkan" + }, + "progress": { + "install_addon": "Harap tunggu hingga penginstalan add-on Silicon Labs Multiprotocol selesai. Ini bisa memakan waktu beberapa saat.", + "start_addon": "Harap tunggu hingga add-on Silicon Labs Multiprotocol selesai. Ini mungkin perlu waktu beberapa saat." + }, + "step": { + "addon_installed_other_device": { + "title": "Dukungan multiprotokol sudah diaktifkan untuk perangkat lain" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Aktifkan dukungan multiprotokol" + }, + "description": "Jika dukungan multiprotocol diaktifkan, radio IEEE 802.15.4 Home Assistant Yellow dapat digunakan untuk Zigbee dan Thread (digunakan oleh Matter) secara bersamaan. Catatan: Ini adalah fitur eksperimental.", + "title": "Aktifkan dukungan multiprotokol pada radio IEEE 802.15.4" + }, + "install_addon": { + "title": "Penginstalan add-on Multiprotocol Silicon Labs telah dimulai" + }, + "show_revert_guide": { + "description": "Jika Anda ingin mengubah ke firmware Zigbee saja, selesaikan langkah-langkah manual berikut ini:\n\n * Hapus add-on Multiprotocol Silicon Labs\n\n * Flash firmware khusus Zigbee, ikuti panduan di https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Konfigurasikan ulang ZHA untuk memigrasikan pengaturan ke radio yang diflash ulang", + "title": "Dukungan multiprotokol diaktifkan untuk perangkat ini" + }, + "start_addon": { + "title": "Add-on Multiprotokol Silicon Labs sedang dimulai." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/nl.json b/homeassistant/components/homeassistant_yellow/translations/nl.json new file mode 100644 index 00000000000..879fd7717f6 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/nl.json @@ -0,0 +1,7 @@ +{ + "options": { + "error": { + "unknown": "Onverwachte fout" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/pl.json b/homeassistant/components/homeassistant_yellow/translations/pl.json new file mode 100644 index 00000000000..a52ba1df93f --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/pl.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Nie uda\u0142o si\u0119 pobra\u0107 informacji o dodatku Silicon Labs Multiprotocol.", + "addon_install_failed": "Nie uda\u0142o si\u0119 zainstalowa\u0107 dodatku Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Nie uda\u0142o si\u0119 ustawi\u0107 konfiguracji Silicon Labs Multiprotocol.", + "addon_start_failed": "Nie uda\u0142o si\u0119 uruchomi\u0107 dodatku Silicon Labs Multiprotocol.", + "not_hassio": "Opcje sprz\u0119towe mo\u017cna skonfigurowa\u0107 tylko w instalacjach HassOS." + }, + "error": { + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "progress": { + "install_addon": "Poczekaj, a\u017c zako\u0144czy si\u0119 instalacja dodatku Silicon Labs Multiprotocol. Mo\u017ce to potrwa\u0107 kilka minut.", + "start_addon": "Poczekaj, a\u017c zako\u0144czy si\u0119 uruchamianie dodatku Silicon Labs Multiprotocol. Mo\u017ce to potrwa\u0107 kilka sekund." + }, + "step": { + "addon_installed_other_device": { + "title": "Obs\u0142uga Multiprotocol jest ju\u017c w\u0142\u0105czona dla innego urz\u0105dzenia" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "W\u0142\u0105cz obs\u0142ug\u0119 Multiprotocol" + }, + "description": "Gdy w\u0142\u0105czona jest obs\u0142uga Multiprotocol, radio IEEE 802.15.4 Home Assistant Yellow mo\u017ce by\u0107 u\u017cywane jednocze\u015bnie dla Zigbee i Thread (u\u017cywane przez Matter). Uwaga: jest to funkcja eksperymentalna.", + "title": "W\u0142\u0105cz obs\u0142ug\u0119 multiprotocol w radiu IEEE 802.15.4" + }, + "install_addon": { + "title": "Rozpocz\u0119\u0142a si\u0119 instalacja dodatku Silicon Labs Multiprotocol" + }, + "show_revert_guide": { + "description": "Je\u015bli chcesz zmieni\u0107 oprogramowanie obs\u0142uguj\u0105ce tylko Zigbee, wykonaj nast\u0119puj\u0105ce czynno\u015bci r\u0119czne: \n\n* Usu\u0144 dodatek Silicon Labs Multiprotocol \n\n* Wgraj oprogramowanie tylko dla Zigbee, post\u0119puj zgodnie z instrukcjami na stronie https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n* Ponownie skonfiguruj ZHA, aby przeprowadzi\u0107 migracj\u0119 ustawie\u0144 do przeprogramowanego radia", + "title": "Obs\u0142uga multiprotocol jest w\u0142\u0105czona dla tego urz\u0105dzenia" + }, + "start_addon": { + "title": "Uruchamianie dodatku Silicon Labs Multiprotocol." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/knx/translations/el.json b/homeassistant/components/knx/translations/el.json index 30047781c26..20e4a2fe140 100644 --- a/homeassistant/components/knx/translations/el.json +++ b/homeassistant/components/knx/translations/el.json @@ -41,7 +41,7 @@ }, "secure_knxkeys": { "data": { - "knxkeys_filename": "\u03a4\u03bf \u03c0\u03bb\u03ae\u03c1\u03b5\u03c2 \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 knxkeys \u03c3\u03b1\u03c2", + "knxkeys_filename": "\u03a4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03c3\u03b1\u03c2 `.knxkeys` (\u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b1\u03bc\u03b2\u03b1\u03bd\u03bf\u03bc\u03ad\u03bd\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03b5\u03c0\u03ad\u03ba\u03c4\u03b1\u03c3\u03b7\u03c2)", "knxkeys_password": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03ac\u03c6\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 knxkeys" }, "data_description": { @@ -86,6 +86,22 @@ }, "options": { "step": { + "communication_settings": { + "data": { + "rate_limit": "\u038c\u03c1\u03b9\u03bf \u03c0\u03bf\u03c3\u03bf\u03c3\u03c4\u03bf\u03cd", + "state_updater": "\u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03c9\u03c4\u03ae\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2" + }, + "data_description": { + "rate_limit": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03b1 \u03b5\u03be\u03b5\u03c1\u03c7\u03cc\u03bc\u03b5\u03bd\u03b1 \u03c4\u03b7\u03bb\u03b5\u03b3\u03c1\u03b1\u03c6\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b1\u03bd\u03ac \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03bf.\n \"0\" \u03b3\u03b9\u03b1 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03bf\u03c1\u03af\u03bf\u03c5. \u03a3\u03c5\u03bd\u03b9\u03c3\u03c4\u03ac\u03c4\u03b1\u03b9: 0 \u03ae 20 \u03ad\u03c9\u03c2 40", + "state_updater": "\u039f\u03c1\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03c9\u03bd \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b4\u03af\u03b1\u03c5\u03bb\u03bf KNX. \u038c\u03c4\u03b1\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf, \u03c4\u03bf Home Assistant \u03b4\u03b5\u03bd \u03b8\u03b1 \u03b1\u03bd\u03b1\u03ba\u03c4\u03ac \u03b5\u03bd\u03b5\u03c1\u03b3\u03ac \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 \u03bf\u03bd\u03c4\u03bf\u03c4\u03ae\u03c4\u03c9\u03bd \u03b1\u03c0\u03cc \u03c4\u03bf KNX Bus. \u039c\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bc\u03c6\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03c4\u03b9\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bd\u03c4\u03bf\u03c4\u03ae\u03c4\u03c9\u03bd \u00absync_state\u00bb." + } + }, + "connection_type": { + "data": { + "connection_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 KNX" + }, + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03cd\u03c0\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c0\u03bf\u03c5 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03bf\u03c5\u03bc\u03b5 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03ae \u03c3\u03b1\u03c2 KNX.\n AUTOMATIC - \u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c6\u03c1\u03bf\u03bd\u03c4\u03af\u03b6\u03b5\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03bd\u03b4\u03b5\u03c3\u03b9\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1 \u03bc\u03b5 \u03c4\u03bf KNX Bus \u03c3\u03b1\u03c2 \u03b5\u03ba\u03c4\u03b5\u03bb\u03ce\u03bd\u03c4\u03b1\u03c2 \u03bc\u03b9\u03b1 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7 \u03c0\u03cd\u03bb\u03b7\u03c2.\n TUNNELING - \u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b8\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03bc\u03b5 \u03c4\u03bf \u03b4\u03af\u03b1\u03c5\u03bb\u03bf KNX \u03bc\u03ad\u03c3\u03c9 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2.\n \u0394\u03a1\u039f\u039c\u039f\u039b\u039f\u0393\u0397\u03a3\u0397 - \u0397 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b8\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03bc\u03b5 \u03c4\u03bf \u03b4\u03af\u03b1\u03c5\u03bb\u03bf KNX \u03bc\u03ad\u03c3\u03c9 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7\u03c2." + }, "init": { "data": { "connection_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 KNX", @@ -105,6 +121,24 @@ "state_updater": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2 \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03c9\u03bd \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b4\u03af\u03b1\u03c5\u03bb\u03bf KNX. \u038c\u03c4\u03b1\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf, \u03c4\u03bf Home Assistant \u03b4\u03b5\u03bd \u03b8\u03b1 \u03b1\u03bd\u03b1\u03ba\u03c4\u03ac \u03b5\u03bd\u03b5\u03c1\u03b3\u03ac \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 \u03b1\u03c0\u03cc \u03c4\u03bf KNX Bus, \u03bf\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 `sync_state` \u03b4\u03b5\u03bd \u03b8\u03b1 \u03ad\u03c7\u03bf\u03c5\u03bd \u03ba\u03b1\u03bc\u03af\u03b1 \u03b5\u03c0\u03af\u03b4\u03c1\u03b1\u03c3\u03b7." } }, + "options_init": { + "menu_options": { + "communication_settings": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03b5\u03c0\u03b9\u03ba\u03bf\u03b9\u03bd\u03c9\u03bd\u03af\u03b1\u03c2", + "connection_type": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b4\u03b9\u03b1\u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 KNX" + } + }, + "routing": { + "data_description": { + "individual_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant, \u03c0.\u03c7. `0.0.4`.", + "local_ip": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7." + }, + "description": "\u0394\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7\u03c2." + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "\u03a4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03c3\u03b1\u03c2 `.knxkeys` (\u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b1\u03bc\u03b2\u03b1\u03bd\u03bf\u03bc\u03ad\u03bd\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03b5\u03c0\u03ad\u03ba\u03c4\u03b1\u03c3\u03b7\u03c2)" + } + }, "tunnel": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", diff --git a/homeassistant/components/knx/translations/nl.json b/homeassistant/components/knx/translations/nl.json index bc8f03eb06c..a9c52b68e75 100644 --- a/homeassistant/components/knx/translations/nl.json +++ b/homeassistant/components/knx/translations/nl.json @@ -85,6 +85,11 @@ } }, "options": { + "error": { + "cannot_connect": "Kan geen verbinding maken", + "file_not_found": "Het opgegeven `.knxkeys`-bestand is niet gevonden in het pad config/.storage/knx/", + "invalid_individual_address": "Waarde komt niet overeen met patroon voor KNX individueel adres.\n\"area.line.device" + }, "step": { "init": { "data": { @@ -105,8 +110,46 @@ "state_updater": "Globaal in- of uitschakelen van het lezen van de status van de KNX bus. Indien uitgeschakeld, zal Home Assistant niet actief de status van de KNX Bus ophalen, `sync_state` entiteitsopties zullen geen effect hebben." } }, + "manual_tunnel": { + "data_description": { + "local_ip": "Leeg laten om auto-discovery te gebruiken.", + "port": "Poort van het KNX/IP-tunnelapparaat." + }, + "description": "Voer de verbindingsinformatie van uw tunneling-apparaat in." + }, + "routing": { + "data": { + "individual_address": "Individueel adres", + "local_ip": "Lokale IP van Home Assistant", + "multicast_group": "Multicast-groep", + "multicast_port": "Multicast-poort" + }, + "data_description": { + "individual_address": "KNX-adres te gebruiken door Home Assistant, bijv. `0.0.4`", + "local_ip": "Leeg laten om auto-discovery te gebruiken." + }, + "description": "Configureer de routing opties" + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "De bestandsnaam van uw `.knxkeys` bestand (inclusief extensie)", + "knxkeys_password": "Het wachtwoord om het bestand `.knxkeys` te ontcijferen" + }, + "data_description": { + "knxkeys_filename": "Het bestand zal naar verwachting worden gevonden in uw configuratiemap in '.storage/knx/'.\nIn Home Assistant OS zou dit '/config/.storage/knx/' zijn.\nVoorbeeld: 'my_project.knxkeys'", + "knxkeys_password": "Dit werd ingesteld bij het exporteren van het bestand van ETS." + }, + "description": "Voer de informatie voor uw `.knxkeys` bestand in." + }, + "secure_tunneling": { + "description": "Kies hoe u KNX/IP Secure wilt configureren.", + "menu_options": { + "secure_knxkeys": "Gebruik een `.knxkeys` bestand met IP beveiligde sleutels" + } + }, "tunnel": { "data": { + "gateway": "KNX Tunnel Connection", "host": "Host", "port": "Poort", "tunneling_type": "KNX Tunneling Type" @@ -114,7 +157,8 @@ "data_description": { "host": "IP adres van het KNX/IP tunneling apparaat.", "port": "Poort van het KNX/IP-tunnelapparaat." - } + }, + "description": "Selecteer een gateway uit de lijst." } } } diff --git a/homeassistant/components/mqtt/translations/el.json b/homeassistant/components/mqtt/translations/el.json index 129b2de45f8..834d2045b77 100644 --- a/homeassistant/components/mqtt/translations/el.json +++ b/homeassistant/components/mqtt/translations/el.json @@ -5,15 +5,26 @@ "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." }, "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_inclusion": "\u03a4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7 \u03ba\u03b1\u03b9 \u03c4\u03bf \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03cc \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03bf\u03cd\u03bd \u03bc\u03b1\u03b6\u03af" }, "step": { "broker": { "data": { + "advanced_options": "\u03a0\u03c1\u03bf\u03b7\u03b3\u03bc\u03ad\u03bd\u03b5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2", "broker": "\u039c\u03b5\u03c3\u03af\u03c4\u03b7\u03c2", + "certificate": "\u0391\u03bd\u03ad\u03b2\u03b1\u03c3\u03bc\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03c3\u03bc\u03ad\u03bd\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd CA", + "client_cert": "\u0391\u03bd\u03ad\u03b2\u03b1\u03c3\u03bc\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7", + "client_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7 (\u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b5\u03bd\u03cc \u03c3\u03b5 \u03ad\u03bd\u03b1 \u03c0\u03bf\u03c5 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03b8\u03b7\u03ba\u03b5 \u03c4\u03c5\u03c7\u03b1\u03af\u03b1)", + "client_key": "\u039c\u03b5\u03c4\u03b1\u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03bf\u03cd \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd", "discovery": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7\u03c2", + "keepalive": "\u039f \u03c7\u03c1\u03cc\u03bd\u03bf\u03c2 \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03c4\u03b7\u03c2 \u03b1\u03c0\u03bf\u03c3\u03c4\u03bf\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03c4\u03b7\u03c1\u03b5\u03af \u03b6\u03c9\u03bd\u03c4\u03b1\u03bd\u03ac \u03c4\u03b1 \u03bc\u03b7\u03bd\u03cd\u03bc\u03b1\u03c4\u03b1", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "port": "\u0398\u03cd\u03c1\u03b1", + "protocol": "\u03a0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf MQTT", + "set_ca_cert": "\u0395\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03b5\u03c3\u03af\u03c4\u03b7", + "set_client_cert": "\u03a7\u03c1\u03ae\u03c3\u03b7 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7", + "tls_insecure": "\u0391\u03b3\u03bd\u03bf\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03b5\u03c3\u03af\u03c4\u03b7", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" }, "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 MQTT broker." @@ -53,6 +64,10 @@ "deprecated_yaml": { "description": "\u0392\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b1 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03b5\u03c2 {\u03c0\u03bb\u03b1\u03c4\u03c6\u03cc\u03c1\u03bc\u03b1}(\u03b5\u03c2) MQTT \u03ba\u03ac\u03c4\u03c9 \u03b1\u03c0\u03cc \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c0\u03bb\u03b1\u03c4\u03c6\u03cc\u03c1\u03bc\u03b1\u03c2 `{platform}`.\n\n\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03bc\u03b5\u03c4\u03b1\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c3\u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 `mqtt` \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1. \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd [\u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7]({more_info_url}), \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2.", "title": "\u039f\u03b9 \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b5\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c3\u03b1\u03c2 MQTT {platform}(s) \u03c7\u03c1\u03b5\u03b9\u03ac\u03b6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03c0\u03c1\u03bf\u03c3\u03bf\u03c7\u03ae" + }, + "deprecated_yaml_broker_settings": { + "description": "\u039f\u03b9 \u03b1\u03ba\u03cc\u03bb\u03bf\u03c5\u03b8\u03b5\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c0\u03bf\u03c5 \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c4\u03bf \"configuration.yaml\" \u03bc\u03b5\u03c4\u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c4\u03b7\u03bd \u03ba\u03b1\u03c4\u03b1\u03c7\u03ce\u03c1\u03b7\u03c3\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 MQTT \u03ba\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd \u03b8\u03b1 \u03b1\u03bd\u03c4\u03b9\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ae\u03c3\u03bf\u03c5\u03bd \u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c3\u03c4\u03bf \"configuration.yaml\":\n ` {deprecated_settings} ` \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ad\u03c2 \u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03b1\u03c0\u03cc \u03c4\u03bf \"configuration.yaml\" \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1. \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd [\u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7]( {more_info_url} ), \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2.", + "title": "\u039f\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03bc\u03ad\u03bd\u03b5\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 MQTT \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c4\u03bf \"configuration.yaml\"." } }, "options": { diff --git a/homeassistant/components/nibe_heatpump/translations/el.json b/homeassistant/components/nibe_heatpump/translations/el.json index a740bc43742..cdb5c77e15d 100644 --- a/homeassistant/components/nibe_heatpump/translations/el.json +++ b/homeassistant/components/nibe_heatpump/translations/el.json @@ -12,6 +12,11 @@ "write": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03c3\u03c4\u03bf \u03b1\u03af\u03c4\u03b7\u03bc\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03c3\u03c4\u03b7\u03bd \u03b1\u03bd\u03c4\u03bb\u03af\u03b1. \u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \"\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b8\u03cd\u03c1\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2\" \u03ae \u03c4\u03b7\u03bd \"\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP\"." }, "step": { + "nibegw": { + "data_description": { + "listening_port": "\u0397 \u03c4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2, \u03c3\u03c4\u03b7\u03bd \u03bf\u03c0\u03bf\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af \u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 NibeGW \u03b3\u03b9\u03b1 \u03b1\u03c0\u03bf\u03c3\u03c4\u03bf\u03bb\u03ae \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd." + } + }, "user": { "data": { "ip_address": "\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP", diff --git a/homeassistant/components/onvif/translations/de.json b/homeassistant/components/onvif/translations/de.json index 10a7ec2ed53..2eaf20b2b3a 100644 --- a/homeassistant/components/onvif/translations/de.json +++ b/homeassistant/components/onvif/translations/de.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "Zus\u00e4tzliche FFMPEG-Argumente", - "rtsp_transport": "RTSP-Transportmechanismus" + "rtsp_transport": "RTSP-Transportmechanismus", + "use_wallclock_as_timestamps": "Wanduhr als Zeitstempel verwenden" }, "title": "ONVIF-Ger\u00e4teoptionen" } diff --git a/homeassistant/components/onvif/translations/el.json b/homeassistant/components/onvif/translations/el.json index 911994c7344..84d03d2a97e 100644 --- a/homeassistant/components/onvif/translations/el.json +++ b/homeassistant/components/onvif/translations/el.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "\u0395\u03c0\u03b9\u03c0\u03bb\u03ad\u03bf\u03bd \u03bf\u03c1\u03af\u03c3\u03bc\u03b1\u03c4\u03b1 FFMPEG", - "rtsp_transport": "\u039c\u03b7\u03c7\u03b1\u03bd\u03b9\u03c3\u03bc\u03cc\u03c2 \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP" + "rtsp_transport": "\u039c\u03b7\u03c7\u03b1\u03bd\u03b9\u03c3\u03bc\u03cc\u03c2 \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP", + "use_wallclock_as_timestamps": "\u03a7\u03c1\u03ae\u03c3\u03b7 \u03c1\u03bf\u03bb\u03bf\u03b3\u03b9\u03bf\u03cd \u03c4\u03bf\u03af\u03c7\u03bf\u03c5 \u03c9\u03c2 \u03c7\u03c1\u03bf\u03bd\u03bf\u03c3\u03c6\u03c1\u03b1\u03b3\u03af\u03b4\u03b5\u03c2" }, "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 ONVIF" } diff --git a/homeassistant/components/onvif/translations/es.json b/homeassistant/components/onvif/translations/es.json index ce828b53cd5..b20c53988c2 100644 --- a/homeassistant/components/onvif/translations/es.json +++ b/homeassistant/components/onvif/translations/es.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "Argumentos extra de FFMPEG", - "rtsp_transport": "Mecanismo de transporte RTSP" + "rtsp_transport": "Mecanismo de transporte RTSP", + "use_wallclock_as_timestamps": "Usar reloj de pared como marca de tiempo" }, "title": "Opciones del dispositivo ONVIF" } diff --git a/homeassistant/components/onvif/translations/et.json b/homeassistant/components/onvif/translations/et.json index 1d7bede6657..22389a11756 100644 --- a/homeassistant/components/onvif/translations/et.json +++ b/homeassistant/components/onvif/translations/et.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "T\u00e4iendavad FFMPEG argumendid", - "rtsp_transport": "RTSP edastusviis" + "rtsp_transport": "RTSP edastusviis", + "use_wallclock_as_timestamps": "Seinakella kasutamine ajatemplitena" }, "title": "ONVIF-seadme suvandid" } diff --git a/homeassistant/components/onvif/translations/hu.json b/homeassistant/components/onvif/translations/hu.json index 4fcb62c26fc..fa1b455a1a4 100644 --- a/homeassistant/components/onvif/translations/hu.json +++ b/homeassistant/components/onvif/translations/hu.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "Extra FFMPEG opci\u00f3k", - "rtsp_transport": "RTSP sz\u00e1ll\u00edt\u00e1si mechanizmus" + "rtsp_transport": "RTSP sz\u00e1ll\u00edt\u00e1si mechanizmus", + "use_wallclock_as_timestamps": "\u00d3ra haszn\u00e1lata id\u0151b\u00e9lyegk\u00e9nt" }, "title": "ONVIF eszk\u00f6z opci\u00f3i" } diff --git a/homeassistant/components/onvif/translations/id.json b/homeassistant/components/onvif/translations/id.json index 383287db875..8f4439fe0ab 100644 --- a/homeassistant/components/onvif/translations/id.json +++ b/homeassistant/components/onvif/translations/id.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "Argumen FFMPEG ekstra", - "rtsp_transport": "Mekanisme transport RTSP" + "rtsp_transport": "Mekanisme transport RTSP", + "use_wallclock_as_timestamps": "Gunakan jam dinding sebagai stempel waktu" }, "title": "Opsi Perangkat ONVIF" } diff --git a/homeassistant/components/onvif/translations/no.json b/homeassistant/components/onvif/translations/no.json index a9087ba6be4..7219501f079 100644 --- a/homeassistant/components/onvif/translations/no.json +++ b/homeassistant/components/onvif/translations/no.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "Ekstra FFMPEG-argumenter", - "rtsp_transport": "RTSP transportmekanisme" + "rtsp_transport": "RTSP transportmekanisme", + "use_wallclock_as_timestamps": "Bruk veggklokke som tidsstempler" }, "title": "ONVIF enhetsalternativer" } diff --git a/homeassistant/components/onvif/translations/pl.json b/homeassistant/components/onvif/translations/pl.json index 2300f6d6041..ae2aa3019bb 100644 --- a/homeassistant/components/onvif/translations/pl.json +++ b/homeassistant/components/onvif/translations/pl.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "Dodatkowe argumenty FFMPEG", - "rtsp_transport": "Mechanizm transportu RTSP" + "rtsp_transport": "Mechanizm transportu RTSP", + "use_wallclock_as_timestamps": "U\u017cyj wallclock jako znacznika czasu" }, "title": "Opcje urz\u0105dzenia ONVIF" } diff --git a/homeassistant/components/onvif/translations/pt-BR.json b/homeassistant/components/onvif/translations/pt-BR.json index f491f21d56b..41ab5180ddc 100644 --- a/homeassistant/components/onvif/translations/pt-BR.json +++ b/homeassistant/components/onvif/translations/pt-BR.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "Argumentos FFMPEG extras", - "rtsp_transport": "Mecanismo de transporte RTSP" + "rtsp_transport": "Mecanismo de transporte RTSP", + "use_wallclock_as_timestamps": "Use o rel\u00f3gio de parede como carimbo de data/hora" }, "title": "Op\u00e7\u00f5es do dispositivo ONVIF" } diff --git a/homeassistant/components/openuv/translations/nl.json b/homeassistant/components/openuv/translations/nl.json index a85799658e3..8a1a5e0489d 100644 --- a/homeassistant/components/openuv/translations/nl.json +++ b/homeassistant/components/openuv/translations/nl.json @@ -1,12 +1,19 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd" + "already_configured": "Locatie is al geconfigureerd", + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_api_key": "Ongeldige API-sleutel" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API-sleutel" + }, + "title": "Integratie herauthenticeren" + }, "user": { "data": { "api_key": "API-sleutel", diff --git a/homeassistant/components/pushbullet/translations/nl.json b/homeassistant/components/pushbullet/translations/nl.json new file mode 100644 index 00000000000..f50fd20c509 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/nl.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Dienst is al geconfigureerd" + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_api_key": "Ongeldige API-sleutel" + }, + "step": { + "user": { + "data": { + "api_key": "API-sleutel", + "name": "Naam" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/sensor.nl.json b/homeassistant/components/sensibo/translations/sensor.nl.json index bd7b06dc940..e2ad9aebd5d 100644 --- a/homeassistant/components/sensibo/translations/sensor.nl.json +++ b/homeassistant/components/sensibo/translations/sensor.nl.json @@ -3,6 +3,10 @@ "sensibo__sensitivity": { "n": "Normaal", "s": "Gevoelig" + }, + "sensibo__smart_type": { + "humidity": "Vochtigheid", + "temperature": "Temperatuur" } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/el.json b/homeassistant/components/shelly/translations/el.json index 01c7af19be0..f1cb15a3805 100644 --- a/homeassistant/components/shelly/translations/el.json +++ b/homeassistant/components/shelly/translations/el.json @@ -58,5 +58,18 @@ "single_push": "{subtype} \u03bc\u03bf\u03bd\u03ae \u03ce\u03b8\u03b7\u03c3\u03b7", "triple": "\u03a4\u03c1\u03b9\u03c0\u03bb\u03cc \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03bf {subtype}" } + }, + "options": { + "abort": { + "ble_unsupported": "\u0397 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 Bluetooth \u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03bf\u03cd {ble_min_version} \u03ae \u03bd\u03b5\u03cc\u03c4\u03b5\u03c1\u03b7." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03b1\u03c1\u03c9\u03c4\u03ae Bluetooth" + }, + "description": "\u0397 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7 Bluetooth \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03ae \u03ae \u03c0\u03b1\u03b8\u03b7\u03c4\u03b9\u03ba\u03ae. \u039c\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03b5\u03c1\u03b3\u03ae, \u03c4\u03bf Shelly \u03b6\u03b7\u03c4\u03ac \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 \u03b1\u03c0\u03cc \u03ba\u03bf\u03bd\u03c4\u03b9\u03bd\u03ad\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2- \u03bc\u03b5 \u03c4\u03b7\u03bd \u03c0\u03b1\u03b8\u03b7\u03c4\u03b9\u03ba\u03ae, \u03c4\u03bf Shelly \u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03b9 \u03bc\u03b7 \u03b6\u03b7\u03c4\u03b7\u03b8\u03ad\u03bd\u03c4\u03b1 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 \u03b1\u03c0\u03cc \u03ba\u03bf\u03bd\u03c4\u03b9\u03bd\u03ad\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/es.json b/homeassistant/components/shelly/translations/es.json index 7acd73471b7..2bbf32ebe86 100644 --- a/homeassistant/components/shelly/translations/es.json +++ b/homeassistant/components/shelly/translations/es.json @@ -58,5 +58,18 @@ "single_push": "Pulsaci\u00f3n simple de {subtype}", "triple": "Pulsaci\u00f3n triple de {subtype}" } + }, + "options": { + "abort": { + "ble_unsupported": "La compatibilidad con Bluetooth requiere la versi\u00f3n de firmware {ble_min_version} o m\u00e1s reciente." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Modo de esc\u00e1ner Bluetooth" + }, + "description": "El escaneo de Bluetooth puede ser activo o pasivo. Con activo, Shelly solicita datos de dispositivos cercanos; con pasivo, Shelly recibe datos no solicitados de dispositivos cercanos." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/hu.json b/homeassistant/components/shelly/translations/hu.json index 7878e7d28f8..8b7ebcac15e 100644 --- a/homeassistant/components/shelly/translations/hu.json +++ b/homeassistant/components/shelly/translations/hu.json @@ -58,5 +58,18 @@ "single_push": "{subtype} egy lenyom\u00e1s", "triple": "{subtype} tripla kattint\u00e1s" } + }, + "options": { + "abort": { + "ble_unsupported": "A Bluetooth-t\u00e1mogat\u00e1shoz legal\u00e1bb {ble_min_version} firmware verzi\u00f3 sz\u00fcks\u00e9ges." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Bluetooth szkenner m\u00f3d" + }, + "description": "A Bluetooth-keres\u00e9s lehet akt\u00edv vagy passz\u00edv. Ha akt\u00edv, a Shelly adatokat k\u00e9r a k\u00f6zeli eszk\u00f6z\u00f6kr\u0151l; a passz\u00edv funkci\u00f3val a Shelly k\u00e9retlen adatokat is fogad a k\u00f6zeli eszk\u00f6z\u00f6kr\u0151l." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/id.json b/homeassistant/components/shelly/translations/id.json index 104494e01c6..ca945eff968 100644 --- a/homeassistant/components/shelly/translations/id.json +++ b/homeassistant/components/shelly/translations/id.json @@ -58,5 +58,18 @@ "single_push": "Push tunggal {subtype}", "triple": "{subtype} diklik tiga kali" } + }, + "options": { + "abort": { + "ble_unsupported": "Dukungan Bluetooth memerlukan versi firmware {ble_min_version} atau yang lebih baru." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Mode pemindai Bluetooth" + }, + "description": "Mode pemindaian Bluetooth bisa berupa mode aktif atau pasif. Pada mode aktif, Shelly akan meminta data dari perangkat di dekatnya; pada mode pasif, Shelly akan menerima data yang tidak diminta dari perangkat di dekatnya." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/no.json b/homeassistant/components/shelly/translations/no.json index 2f483843e52..4b1390b4dfe 100644 --- a/homeassistant/components/shelly/translations/no.json +++ b/homeassistant/components/shelly/translations/no.json @@ -58,5 +58,18 @@ "single_push": "{subtype} enkelt trykk", "triple": "{subtype} trippelklikket" } + }, + "options": { + "abort": { + "ble_unsupported": "Bluetooth-st\u00f8tte krever fastvareversjon {ble_min_version} eller nyere." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Bluetooth-skannermodus" + }, + "description": "Bluetooth-skanning kan v\u00e6re aktiv eller passiv. Med aktiv ber Shelly om data fra enheter i n\u00e6rheten; med passiv, mottar Shelly u\u00f8nsket data fra enheter i n\u00e6rheten." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/pt-BR.json b/homeassistant/components/shelly/translations/pt-BR.json index 0a546c9807d..508552a92f4 100644 --- a/homeassistant/components/shelly/translations/pt-BR.json +++ b/homeassistant/components/shelly/translations/pt-BR.json @@ -58,5 +58,18 @@ "single_push": "{subtype} \u00fanico empurr\u00e3o", "triple": "{subtype} triplo clicado" } + }, + "options": { + "abort": { + "ble_unsupported": "O suporte a Bluetooth requer a vers\u00e3o de firmware {ble_min_version} ou mais recente." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Modo de varredura Bluetooth" + }, + "description": "A varredura Bluetooth pode ser ativa ou passiva. Com ativo, o Shelly solicita dados de dispositivos pr\u00f3ximos; com passivo, o Shelly recebe dados n\u00e3o solicitados de dispositivos pr\u00f3ximos." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/el.json b/homeassistant/components/unifiprotect/translations/el.json index 58da67d9383..d3771a72409 100644 --- a/homeassistant/components/unifiprotect/translations/el.json +++ b/homeassistant/components/unifiprotect/translations/el.json @@ -41,6 +41,26 @@ } } }, + "issues": { + "ea_setup_failed": { + "description": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 v{version} \u03c4\u03bf\u03c5 UniFi Protect \u03c0\u03bf\u03c5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 Early Access. \u03a0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ac\u03c3\u03c4\u03b7\u03ba\u03b5 \u03ad\u03bd\u03b1 \u03bc\u03b7 \u03b1\u03bd\u03b1\u03ba\u03c4\u03ae\u03c3\u03b9\u03bc\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2. \u039a\u03ac\u03bd\u03c4\u03b5 [\u03c5\u03c0\u03bf\u03b2\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c3\u03b5 \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) \u03c4\u03bf\u03c5 UniFi Protect \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7. \n\n \u03a3\u03c6\u03ac\u03bb\u03bc\u03b1: {error}", + "title": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7\u03c2 Early Access" + }, + "ea_warning": { + "fix_flow": { + "step": { + "confirm": { + "description": "\u0395\u03af\u03c3\u03c4\u03b5 \u03b2\u03ad\u03b2\u03b1\u03b9\u03bf\u03b9 \u03cc\u03c4\u03b9 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03ba\u03c4\u03b5\u03bb\u03ad\u03c3\u03b5\u03c4\u03b5 \u03bc\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03b5\u03c2 \u03b5\u03ba\u03b4\u03cc\u03c3\u03b5\u03b9\u03c2 \u03c4\u03bf\u03c5 UniFi Protect; \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c0\u03c1\u03bf\u03ba\u03b1\u03bb\u03ad\u03c3\u03b5\u03b9 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03ba\u03bf\u03c0\u03ae \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Home Assistant.", + "title": "\u03a4\u03bf v{version} \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03b9\u03b1 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 Early Access" + }, + "start": { + "description": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd v{version} \u03c4\u03bf\u03c5 UniFi Protect \u03c0\u03bf\u03c5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 Early Access. [\u039f\u03b9 \u03b5\u03ba\u03b4\u03cc\u03c3\u03b5\u03b9\u03c2 Early Access \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) \u03ba\u03b1\u03b9 \u03c3\u03c5\u03bd\u03b9\u03c3\u03c4\u03ac\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c8\u03b5\u03c4\u03b5 \u03c3\u03b5 \u03bc\u03b9\u03b1 \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03c4\u03bf \u03c3\u03c5\u03bd\u03c4\u03bf\u03bc\u03cc\u03c4\u03b5\u03c1\u03bf \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03bd. \n\n \u039c\u03b5 \u03c4\u03b7\u03bd \u03c5\u03c0\u03bf\u03b2\u03bf\u03bb\u03ae \u03b1\u03c5\u03c4\u03ae\u03c2 \u03c4\u03b7\u03c2 \u03c6\u03cc\u03c1\u03bc\u03b1\u03c2 \u03ad\u03c7\u03b5\u03c4\u03b5 \u03b5\u03af\u03c4\u03b5 [\u03c5\u03c0\u03bf\u03b2\u03b1\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf \u03c4\u03bf UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) \u03b5\u03af\u03c4\u03b5 \u03c3\u03c5\u03bc\u03c6\u03c9\u03bd\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03ba\u03c4\u03b5\u03bb\u03ad\u03c3\u03b5\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03bc\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03c4\u03bf\u03c5 UniFi Protect.", + "title": "\u03a4\u03bf v{version} \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03b9\u03b1 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 Early Access" + } + } + } + } + }, "options": { "error": { "invalid_mac_list": "\u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1\u03c2 \u03ba\u03b1\u03c4\u03ac\u03bb\u03bf\u03b3\u03bf\u03c2 \u03b4\u03b9\u03b5\u03c5\u03b8\u03cd\u03bd\u03c3\u03b5\u03c9\u03bd MAC \u03c0\u03bf\u03c5 \u03c7\u03c9\u03c1\u03af\u03b6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03ba\u03cc\u03bc\u03bc\u03b1." @@ -49,6 +69,7 @@ "init": { "data": { "all_updates": "\u039c\u03b5\u03c4\u03c1\u03ae\u03c3\u03b5\u03b9\u03c2 \u03c3\u03b5 \u03c0\u03c1\u03b1\u03b3\u03bc\u03b1\u03c4\u03b9\u03ba\u03cc \u03c7\u03c1\u03cc\u03bd\u03bf (\u03a0\u03a1\u039f\u0395\u0399\u0394\u039f\u03a0\u039f\u0399\u0397\u03a3\u0397: \u0391\u03c5\u03be\u03ac\u03bd\u03b5\u03b9 \u03c3\u03b7\u03bc\u03b1\u03bd\u03c4\u03b9\u03ba\u03ac \u03c4\u03b7 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c4\u03b7\u03c2 CPU)", + "allow_ea": "\u039d\u03b1 \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03bf\u03bd\u03c4\u03b1\u03b9 \u03bf\u03b9 \u03b5\u03ba\u03b4\u03cc\u03c3\u03b5\u03b9\u03c2 Early Access \u03c4\u03bf\u03c5 Protect (\u03a0\u03a1\u039f\u0395\u0399\u0394\u039f\u03a0\u039f\u0399\u0397\u03a3\u0397: \u0398\u03b1 \u03b5\u03c0\u03b9\u03c3\u03b7\u03bc\u03b1\u03bd\u03b8\u03b5\u03af \u03b7 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 \u03c9\u03c2 \u03bc\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03b7)", "disable_rtsp": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03bf\u03ae RTSP", "ignored_devices": "\u039a\u03b1\u03c4\u03ac\u03bb\u03bf\u03b3\u03bf\u03c2 \u03b4\u03b9\u03b5\u03c5\u03b8\u03cd\u03bd\u03c3\u03b5\u03c9\u03bd MAC \u03c4\u03c9\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ce\u03bd \u03c0\u03bf\u03c5 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b1\u03b3\u03bd\u03bf\u03b7\u03b8\u03bf\u03cd\u03bd \u03bc\u03b5 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03cc \u03ba\u03cc\u03bc\u03bc\u03b1\u03c4\u03bf\u03c2", "max_media": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03bf\u03c2 \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd\u03c4\u03c9\u03bd \u03c0\u03c1\u03bf\u03c2 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03c0\u03c1\u03cc\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1 \u03c0\u03b5\u03c1\u03b9\u03ae\u03b3\u03b7\u03c3\u03b7\u03c2 \u03c0\u03bf\u03bb\u03c5\u03bc\u03ad\u03c3\u03c9\u03bd (\u03b1\u03c5\u03be\u03ac\u03bd\u03b5\u03b9 \u03c4\u03b7 \u03c7\u03c1\u03ae\u03c3\u03b7 RAM)", diff --git a/homeassistant/components/unifiprotect/translations/id.json b/homeassistant/components/unifiprotect/translations/id.json index 620814f4247..075fb120e70 100644 --- a/homeassistant/components/unifiprotect/translations/id.json +++ b/homeassistant/components/unifiprotect/translations/id.json @@ -43,6 +43,7 @@ }, "issues": { "ea_setup_failed": { + "description": "Anda menggunakan v{version} dari UniFi Protect yang merupakan versi Early Access. Terjadi kesalahan yang tidak dapat dipulihkan saat mencoba memuat integrasi. Silakan [turunkan ke versi stabil](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) dari UniFi Protect untuk terus menggunakan integrasi.\n\nKesalahan: {error}", "title": "Kesalahan penyiapan menggunakan versi Early Access" }, "ea_warning": { @@ -50,9 +51,11 @@ "fix_flow": { "step": { "confirm": { + "description": "Yakin ingin menjalankan versi UniFi Protect yang tidak didukung? Ini dapat menyebabkan integrasi Home Assistant Anda rusak.", "title": "v{version} adalah versi Early Access" }, "start": { + "description": "Anda menggunakan v{version} UniFi Protect yang merupakan versi Early Access. [Versi Early Access tidak didukung oleh Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) dan disarankan untuk kembali ke rilis stabil sesegera mungkin.\n\nDengan mengirimkan formulir ini, Anda seharusnya telah [menurunkan versi UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) atau Anda setuju untuk menjalankan versi UniFi Protect yang tidak didukung.", "title": "v{version} adalah versi Early Access" } } @@ -68,6 +71,7 @@ "init": { "data": { "all_updates": "Metrik waktu nyata (PERINGATAN: Meningkatkan penggunaan CPU)", + "allow_ea": "Izinkan versi Early Access UniFi Protect (PERINGATAN: Akan menandai integrasi Anda sebagai tidak didukung)", "disable_rtsp": "Nonaktifkan aliran RTSP", "ignored_devices": "Daftar alamat MAC perangkat yang dipisahkan koma untuk diabaikan", "max_media": "Jumlah maksimum peristiwa yang akan dimuat untuk Browser Media (meningkatkan penggunaan RAM)", diff --git a/homeassistant/components/wake_on_lan/translations/de.json b/homeassistant/components/wake_on_lan/translations/de.json new file mode 100644 index 00000000000..16105601211 --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/de.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "Die Konfiguration von Wake-on-LAN mit YAML wurde in den Integrationsschl\u00fcssel verschoben. \n\nDie vorhandene YAML-Konfiguration funktioniert f\u00fcr zwei weitere Versionen. \n\nMigriere deine YAML-Konfiguration gem\u00e4\u00df der Dokumentation zum Integrationsschl\u00fcssel.", + "title": "Die Wake-on-LAN YAML-Konfiguration wurde verschoben" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/el.json b/homeassistant/components/wake_on_lan/translations/el.json new file mode 100644 index 00000000000..a971c35c922 --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/el.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Wake on Lan \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ad\u03c7\u03b5\u03b9 \u03bc\u03b5\u03c4\u03b1\u03c6\u03b5\u03c1\u03b8\u03b5\u03af \u03c3\u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2.\n\n\u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b8\u03b1 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03b5\u03af \u03b3\u03b9\u03b1 2 \u03b1\u03ba\u03cc\u03bc\u03b1 \u03b5\u03ba\u03b4\u03cc\u03c3\u03b5\u03b9\u03c2.\n\n\u039c\u03b5\u03c4\u03b1\u03c6\u03ad\u03c1\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03c3\u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7.", + "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Wake on Lan YAML \u03ad\u03c7\u03b5\u03b9 \u03bc\u03b5\u03c4\u03b1\u03ba\u03b9\u03bd\u03b7\u03b8\u03b5\u03af" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/en.json b/homeassistant/components/wake_on_lan/translations/en.json new file mode 100644 index 00000000000..3d37fa4b9ba --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/en.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "Configuring Wake on Lan using YAML has been moved to integration key.\n\nYour existing YAML configuration will be working for 2 more versions.\n\nMigrate your YAML configuration to the integration key according to the documentation.", + "title": "The Wake on Lan YAML configuration has been moved" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/es.json b/homeassistant/components/wake_on_lan/translations/es.json new file mode 100644 index 00000000000..e8500317ade --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/es.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "La configuraci\u00f3n de Wake on Lan usando YAML se ha movido a clave de integraci\u00f3n. \n\nTu configuraci\u00f3n YAML existente funcionar\u00e1 durante 2 versiones m\u00e1s. \n\nMigra tu configuraci\u00f3n YAML a clave de integraci\u00f3n de acuerdo con la documentaci\u00f3n.", + "title": "La configuraci\u00f3n YAML de Wake on Lan se ha movido" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/et.json b/homeassistant/components/wake_on_lan/translations/et.json new file mode 100644 index 00000000000..e9f60abfede --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/et.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "Wake on Lan'i seadistamine YAML-i abil on viidud integratsiooniv\u00f5tmesse.\n\nOlemasolev YAML-konfiguratsioon t\u00f6\u00f6tab veel 2 versiooni.\n\nMigreeri oma YAML-konfiguratsioon integratsiooniv\u00f5tmesse vastavalt dokumentatsioonile.", + "title": "Wake on Lan YAML-i konfiguratsioon on teisaldatud" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/hu.json b/homeassistant/components/wake_on_lan/translations/hu.json new file mode 100644 index 00000000000..83dcde2cdf5 --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/hu.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "A Wake-on-LAN yaml haszn\u00e1lat\u00e1val t\u00f6rt\u00e9n\u0151 konfigur\u00e1l\u00e1sa megsz\u0171nt.\n\nA megl\u00e9v\u0151 YAML-konfigur\u00e1ci\u00f3 a k\u00f6vetkez\u0151 k\u00e9t verzi\u00f3ban fog m\u0171k\u00f6dni.\n\nHelyezze \u00e1t a YAML-konfigur\u00e1ci\u00f3t az integr\u00e1ci\u00f3ba a dokument\u00e1ci\u00f3nak megfelel\u0151en.", + "title": "A Wake on Lan YAML konfigur\u00e1ci\u00f3 meg fog sz\u0171nni" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/id.json b/homeassistant/components/wake_on_lan/translations/id.json new file mode 100644 index 00000000000..96b2cc2a2b2 --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/id.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "Konfigurasi Integrasi Wake-on-LAN menggunakan YAML telah dipindahkan ke kunci integrasi.\n\nKonfigurasi YAML Anda yang ada saat ini akan berfungsi hingga 2 versi berikutnya.\n\nMigrasikan konfigurasi YAML Anda ke kunci integrasi sesuai dengan dokumentasi.", + "title": "Konfigurasi YAML Integrasi Wake-on-LAN telah dipindahkan" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/no.json b/homeassistant/components/wake_on_lan/translations/no.json new file mode 100644 index 00000000000..37148c404d8 --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/no.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "Konfigurering av Wake on Lan ved hjelp av YAML har blitt flyttet til integrasjonsn\u00f8kkel. \n\n Din eksisterende YAML-konfigurasjon vil fungere for 2 flere versjoner. \n\n Migrer YAML-konfigurasjonen til integrasjonsn\u00f8kkelen i henhold til dokumentasjonen.", + "title": "Wake on Lan YAML-konfigurasjonen er flyttet" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/pl.json b/homeassistant/components/wake_on_lan/translations/pl.json new file mode 100644 index 00000000000..3d230c99282 --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/pl.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "Konfiguracja Wake on LAN za pomoc\u0105 YAML zosta\u0142a przeniesiona do klucza integracji. \n\nTwoja istniej\u0105ca konfiguracja YAML b\u0119dzie dzia\u0142a\u0107 przez kolejne 2 wersje. \n\nPrzenie\u015b swoj\u0105 konfiguracj\u0119 YAML do klucza integracji zgodnie z dokumentacj\u0105.", + "title": "Konfiguracja YAML dla Wake on LAN zostaje przeniesiona" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/pt-BR.json b/homeassistant/components/wake_on_lan/translations/pt-BR.json new file mode 100644 index 00000000000..b685bc04ad5 --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/pt-BR.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "A configura\u00e7\u00e3o do Wake on Lan usando YAML foi movida para a chave de integra\u00e7\u00e3o. \n\n Sua configura\u00e7\u00e3o YAML existente funcionar\u00e1 para mais 2 vers\u00f5es. \n\n Migre sua configura\u00e7\u00e3o YAML para a chave de integra\u00e7\u00e3o de acordo com a documenta\u00e7\u00e3o.", + "title": "A configura\u00e7\u00e3o YAML de Wake on Lan foi movida" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/nl.json b/homeassistant/components/zamg/translations/nl.json index f6a0344cbc4..2bdc82453e8 100644 --- a/homeassistant/components/zamg/translations/nl.json +++ b/homeassistant/components/zamg/translations/nl.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Kan geen verbinding maken" - } + }, + "flow_title": "{name}" } } \ No newline at end of file From 6856374f24e75f2790a16828b3f063595283fbcb Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 08:32:42 +0100 Subject: [PATCH 0494/1033] Cleanup supported_features in smartthings cover (#82240) --- homeassistant/components/smartthings/cover.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/smartthings/cover.py b/homeassistant/components/smartthings/cover.py index 0ff3a82d788..d2d0dba6773 100644 --- a/homeassistant/components/smartthings/cover.py +++ b/homeassistant/components/smartthings/cover.py @@ -70,8 +70,6 @@ def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None: class SmartThingsCover(SmartThingsEntity, CoverEntity): """Define a SmartThings cover.""" - _attr_supported_features: int - def __init__(self, device): """Initialize the cover class.""" super().__init__(device) @@ -102,7 +100,7 @@ class SmartThingsCover(SmartThingsEntity, CoverEntity): async def async_set_cover_position(self, **kwargs: Any) -> None: """Move the cover to a specific position.""" - if not self._attr_supported_features & CoverEntityFeature.SET_POSITION: + if not self.supported_features & CoverEntityFeature.SET_POSITION: return # Do not set_status=True as device will report progress. await self._device.set_level(kwargs[ATTR_POSITION], 0) @@ -144,7 +142,7 @@ class SmartThingsCover(SmartThingsEntity, CoverEntity): @property def current_cover_position(self) -> int | None: """Return current position of cover.""" - if not self._attr_supported_features & CoverEntityFeature.SET_POSITION: + if not self.supported_features & CoverEntityFeature.SET_POSITION: return None return self._device.status.level From 17573196c883a84f9831ab591eba4cdd2e4a827a Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 08:39:44 +0100 Subject: [PATCH 0495/1033] Adjust type hints for CoverEntityFeature (#82238) --- homeassistant/components/acmeda/cover.py | 2 +- homeassistant/components/cover/__init__.py | 3 ++- homeassistant/components/esphome/cover.py | 2 +- homeassistant/components/homekit_controller/cover.py | 2 +- homeassistant/components/motion_blinds/cover.py | 2 +- homeassistant/components/mqtt/cover.py | 2 +- homeassistant/components/shelly/cover.py | 4 ++-- homeassistant/components/template/cover.py | 2 +- homeassistant/components/velux/cover.py | 2 +- pylint/plugins/hass_enforce_type_hints.py | 4 ++++ 10 files changed, 15 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/acmeda/cover.py b/homeassistant/components/acmeda/cover.py index 3c3a5ba825a..fba538a7a70 100644 --- a/homeassistant/components/acmeda/cover.py +++ b/homeassistant/components/acmeda/cover.py @@ -70,7 +70,7 @@ class AcmedaCover(AcmedaBase, CoverEntity): return position @property - def supported_features(self) -> int: + def supported_features(self) -> CoverEntityFeature | int: """Flag supported features.""" supported_features = 0 if self.current_cover_position is not None: diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py index 8b4aa79659d..37cdc6422ad 100644 --- a/homeassistant/components/cover/__init__.py +++ b/homeassistant/components/cover/__init__.py @@ -232,6 +232,7 @@ class CoverEntity(Entity): _attr_is_closing: bool | None = None _attr_is_opening: bool | None = None _attr_state: None = None + _attr_supported_features: CoverEntityFeature | int | None _cover_is_last_toggle_direction_open = True @@ -291,7 +292,7 @@ class CoverEntity(Entity): return data @property - def supported_features(self) -> int: + def supported_features(self) -> CoverEntityFeature | int: """Flag supported features.""" if self._attr_supported_features is not None: return self._attr_supported_features diff --git a/homeassistant/components/esphome/cover.py b/homeassistant/components/esphome/cover.py index 10662977307..1b3e42d5a17 100644 --- a/homeassistant/components/esphome/cover.py +++ b/homeassistant/components/esphome/cover.py @@ -38,7 +38,7 @@ class EsphomeCover(EsphomeEntity[CoverInfo, CoverState], CoverEntity): """A cover implementation for ESPHome.""" @property - def supported_features(self) -> int: + def supported_features(self) -> CoverEntityFeature: """Flag supported features.""" flags = ( CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | CoverEntityFeature.STOP diff --git a/homeassistant/components/homekit_controller/cover.py b/homeassistant/components/homekit_controller/cover.py index d4feeccc77a..fbe6f08bc75 100644 --- a/homeassistant/components/homekit_controller/cover.py +++ b/homeassistant/components/homekit_controller/cover.py @@ -143,7 +143,7 @@ class HomeKitWindowCover(HomeKitEntity, CoverEntity): ] @property - def supported_features(self) -> int: + def supported_features(self) -> CoverEntityFeature: """Flag supported features.""" features = ( CoverEntityFeature.OPEN diff --git a/homeassistant/components/motion_blinds/cover.py b/homeassistant/components/motion_blinds/cover.py index a73166912f4..e0d02750d6d 100644 --- a/homeassistant/components/motion_blinds/cover.py +++ b/homeassistant/components/motion_blinds/cover.py @@ -380,7 +380,7 @@ class MotionTiltOnlyDevice(MotionTiltDevice): _restore_tilt = False @property - def supported_features(self) -> int: + def supported_features(self) -> CoverEntityFeature: """Flag supported features.""" supported_features = ( CoverEntityFeature.OPEN_TILT diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index ef8b0090015..6016f4cf96e 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -535,7 +535,7 @@ class MqttCover(MqttEntity, CoverEntity): return self._config.get(CONF_DEVICE_CLASS) @property - def supported_features(self) -> int: + def supported_features(self) -> CoverEntityFeature | int: """Flag supported features.""" supported_features = 0 if self._config.get(CONF_COMMAND_TOPIC) is not None: diff --git a/homeassistant/components/shelly/cover.py b/homeassistant/components/shelly/cover.py index 66b95a7a7fd..f2020597277 100644 --- a/homeassistant/components/shelly/cover.py +++ b/homeassistant/components/shelly/cover.py @@ -75,7 +75,7 @@ class BlockShellyCover(ShellyBlockEntity, CoverEntity): """Initialize block cover.""" super().__init__(coordinator, block) self.control_result: dict[str, Any] | None = None - self._attr_supported_features: int = ( + self._attr_supported_features = ( CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | CoverEntityFeature.STOP ) if self.coordinator.device.settings["rollers"][0]["positioning"]: @@ -151,7 +151,7 @@ class RpcShellyCover(ShellyRpcEntity, CoverEntity): """Initialize rpc cover.""" super().__init__(coordinator, f"cover:{id_}") self._id = id_ - self._attr_supported_features: int = ( + self._attr_supported_features = ( CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | CoverEntityFeature.STOP ) if self.status["pos_control"]: diff --git a/homeassistant/components/template/cover.py b/homeassistant/components/template/cover.py index aad0270e434..4c234e21875 100644 --- a/homeassistant/components/template/cover.py +++ b/homeassistant/components/template/cover.py @@ -309,7 +309,7 @@ class CoverTemplate(TemplateEntity, CoverEntity): return self._device_class @property - def supported_features(self) -> int: + def supported_features(self) -> CoverEntityFeature: """Flag supported features.""" supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE diff --git a/homeassistant/components/velux/cover.py b/homeassistant/components/velux/cover.py index f721a628ef8..c924fe5c10b 100644 --- a/homeassistant/components/velux/cover.py +++ b/homeassistant/components/velux/cover.py @@ -40,7 +40,7 @@ class VeluxCover(VeluxEntity, CoverEntity): """Representation of a Velux cover.""" @property - def supported_features(self) -> int: + def supported_features(self) -> CoverEntityFeature: """Flag supported features.""" supported_features = ( CoverEntityFeature.OPEN diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 873a783c1ca..2bb80cac2d7 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1108,6 +1108,10 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { function_name="is_closed", return_type=["bool", None], ), + TypeHintMatch( + function_name="supported_features", + return_type=["CoverEntityFeature", "int"], + ), TypeHintMatch( function_name="open_cover", kwargs_type="Any", From 146fe8f15600c3a545109a7a9fcccfe12a856786 Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Thu, 17 Nov 2022 09:06:56 +0100 Subject: [PATCH 0496/1033] Rename convert_time_to_isodate to next_datetime (#82214) --- homeassistant/components/here_travel_time/coordinator.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/here_travel_time/coordinator.py b/homeassistant/components/here_travel_time/coordinator.py index de9bb9f1c60..0d30df51f67 100644 --- a/homeassistant/components/here_travel_time/coordinator.py +++ b/homeassistant/components/here_travel_time/coordinator.py @@ -256,9 +256,9 @@ def prepare_parameters( arrival: str | None = None departure: str | None = None if config.arrival is not None: - arrival = convert_time_to_isodate(config.arrival) + arrival = next_datetime(config.arrival).isoformat() if config.departure is not None: - departure = convert_time_to_isodate(config.departure) + departure = next_datetime(config.departure).isoformat() return (origin, destination, arrival, departure) @@ -278,12 +278,12 @@ def build_hass_attribution(sections: dict) -> str | None: return None -def convert_time_to_isodate(simple_time: time) -> str: +def next_datetime(simple_time: time) -> datetime: """Take a time like 08:00:00 and combine it with the current date.""" combined = datetime.combine(dt.start_of_local_day(), simple_time) if combined < datetime.now(): combined = combined + timedelta(days=1) - return combined.isoformat() + return combined class InvalidCoordinatesException(Exception): From 89c756851562d232d43bc51e967e11f3bac44c82 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 09:10:37 +0100 Subject: [PATCH 0497/1033] Remove duplicate code in hassfest model (#82203) --- script/hassfest/model.py | 33 ++++++++++++++++----------------- tests/hassfest/test_version.py | 2 +- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/script/hassfest/model.py b/script/hassfest/model.py index 6b500daa9b3..fa1580b0317 100644 --- a/script/hassfest/model.py +++ b/script/hassfest/model.py @@ -55,7 +55,13 @@ class Brand: return brands path: pathlib.Path = attr.ib() - brand: dict[str, Any] | None = attr.ib(default=None) + _brand: dict[str, Any] | None = attr.ib(default=None) + + @property + def brand(self) -> dict[str, Any]: + """Guarded access to brand.""" + assert self._brand is not None, "brand has not been loaded" + return self._brand @property def domain(self) -> str: @@ -65,19 +71,16 @@ class Brand: @property def name(self) -> str | None: """Return name of the integration.""" - assert self.brand is not None, "brand has not been loaded" return self.brand.get("name") @property def integrations(self) -> list[str]: """Return the sub integrations of this brand.""" - assert self.brand is not None, "brand has not been loaded" return self.brand.get("integrations", []) @property def iot_standards(self) -> list[str]: """Return list of supported IoT standards.""" - assert self.brand is not None, "brand has not been loaded" return self.brand.get("iot_standards", []) def load_brand(self, config: Config) -> None: @@ -94,7 +97,7 @@ class Brand: ) return - self.brand = brand + self._brand = brand @attr.s @@ -127,11 +130,17 @@ class Integration: return integrations path: pathlib.Path = attr.ib() - manifest: dict[str, Any] | None = attr.ib(default=None) + _manifest: dict[str, Any] | None = attr.ib(default=None) errors: list[Error] = attr.ib(factory=list) warnings: list[Error] = attr.ib(factory=list) translated_name: bool = attr.ib(default=False) + @property + def manifest(self) -> dict[str, Any]: + """Guarded access to manifest.""" + assert self._manifest is not None, "manifest has not been loaded" + return self._manifest + @property def domain(self) -> str: """Integration domain.""" @@ -145,62 +154,52 @@ class Integration: @property def disabled(self) -> str | None: """Return if integration is disabled.""" - assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("disabled") @property def name(self) -> str: """Return name of the integration.""" - assert self.manifest is not None, "manifest has not been loaded" name: str = self.manifest["name"] return name @property def quality_scale(self) -> str | None: """Return quality scale of the integration.""" - assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("quality_scale") @property def config_flow(self) -> bool: """Return if the integration has a config flow.""" - assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("config_flow", False) @property def requirements(self) -> list[str]: """List of requirements.""" - assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("requirements", []) @property def dependencies(self) -> list[str]: """List of dependencies.""" - assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("dependencies", []) @property def supported_by(self) -> str: """Return the integration supported by this virtual integration.""" - assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("supported_by", {}) @property def integration_type(self) -> str: """Get integration_type.""" - assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("integration_type", "hub") @property def iot_class(self) -> str | None: """Return the integration IoT Class.""" - assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("iot_class") @property def iot_standards(self) -> list[str]: """Return the IoT standard supported by this virtual integration.""" - assert self.manifest is not None, "manifest has not been loaded" return self.manifest.get("iot_standards", []) def add_error(self, *args: Any, **kwargs: Any) -> None: @@ -224,4 +223,4 @@ class Integration: self.add_error("model", f"Manifest contains invalid JSON: {err}") return - self.manifest = manifest + self._manifest = manifest diff --git a/tests/hassfest/test_version.py b/tests/hassfest/test_version.py index 7f12fb83fd7..eee184404b8 100644 --- a/tests/hassfest/test_version.py +++ b/tests/hassfest/test_version.py @@ -13,7 +13,7 @@ from script.hassfest.model import Integration def integration(): """Fixture for hassfest integration model.""" integration = Integration("") - integration.manifest = { + integration._manifest = { "domain": "test", "documentation": "https://example.com", "name": "test", From 6424c71daaba644c8f1c4c3edba5544c5c2b6510 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 09:39:46 +0100 Subject: [PATCH 0498/1033] Cleanup supported_features in demo (#82239) * Cleanup supported_features in demo cover * Improve other platforms * Adjust media_player patch --- homeassistant/components/demo/cover.py | 11 ++--------- homeassistant/components/demo/fan.py | 9 ++------- homeassistant/components/demo/humidifier.py | 4 +--- homeassistant/components/demo/light.py | 9 ++------- homeassistant/components/demo/lock.py | 8 +------- homeassistant/components/demo/media_player.py | 18 +++--------------- homeassistant/components/demo/vacuum.py | 18 +++++------------- homeassistant/components/demo/water_heater.py | 13 +++---------- tests/components/media_player/test_init.py | 6 +++--- 9 files changed, 22 insertions(+), 74 deletions(-) diff --git a/homeassistant/components/demo/cover.py b/homeassistant/components/demo/cover.py index 845bd9976a3..6f443329661 100644 --- a/homeassistant/components/demo/cover.py +++ b/homeassistant/components/demo/cover.py @@ -78,7 +78,7 @@ class DemoCover(CoverEntity): position: int | None = None, tilt_position: int | None = None, device_class: CoverDeviceClass | None = None, - supported_features: int | None = None, + supported_features: CoverEntityFeature | None = None, ) -> None: """Initialize the cover.""" self.hass = hass @@ -86,7 +86,7 @@ class DemoCover(CoverEntity): self._attr_name = name self._position = position self._attr_device_class = device_class - self._supported_features = supported_features + self._attr_supported_features = supported_features self._set_position: int | None = None self._set_tilt_position: int | None = None self._tilt_position = tilt_position @@ -142,13 +142,6 @@ class DemoCover(CoverEntity): """Return if the cover is opening.""" return self._is_opening - @property - def supported_features(self) -> int: - """Flag supported features.""" - if self._supported_features is not None: - return self._supported_features - return super().supported_features - async def async_close_cover(self, **kwargs: Any) -> None: """Close the cover.""" if self._position == 0: diff --git a/homeassistant/components/demo/fan.py b/homeassistant/components/demo/fan.py index 8dcffa6e141..5c8cc849285 100644 --- a/homeassistant/components/demo/fan.py +++ b/homeassistant/components/demo/fan.py @@ -107,13 +107,13 @@ class BaseDemoFan(FanEntity): hass: HomeAssistant, unique_id: str, name: str, - supported_features: int, + supported_features: FanEntityFeature, preset_modes: list[str] | None, ) -> None: """Initialize the entity.""" self.hass = hass self._unique_id = unique_id - self._supported_features = supported_features + self._attr_supported_features = supported_features self._percentage: int | None = None self._preset_modes = preset_modes self._preset_mode: str | None = None @@ -140,11 +140,6 @@ class BaseDemoFan(FanEntity): """Oscillating.""" return self._oscillating - @property - def supported_features(self) -> int: - """Flag supported features.""" - return self._supported_features - class DemoPercentageFan(BaseDemoFan, FanEntity): """A demonstration fan component that uses percentages.""" diff --git a/homeassistant/components/demo/humidifier.py b/homeassistant/components/demo/humidifier.py index 571cfbe8db9..9b55583d12b 100644 --- a/homeassistant/components/demo/humidifier.py +++ b/homeassistant/components/demo/humidifier.py @@ -75,9 +75,7 @@ class DemoHumidifier(HumidifierEntity): self._attr_is_on = is_on self._attr_supported_features = SUPPORT_FLAGS if mode is not None: - self._attr_supported_features = ( - self._attr_supported_features | HumidifierEntityFeature.MODES - ) + self._attr_supported_features |= HumidifierEntityFeature.MODES self._attr_target_humidity = target_humidity self._attr_mode = mode self._attr_available_modes = available_modes diff --git a/homeassistant/components/demo/light.py b/homeassistant/components/demo/light.py index af8afe2c15d..c990e4ff8e0 100644 --- a/homeassistant/components/demo/light.py +++ b/homeassistant/components/demo/light.py @@ -129,7 +129,7 @@ class DemoLight(LightEntity): self._ct = ct or random.choice(LIGHT_TEMPS) self._effect = effect self._effect_list = effect_list - self._features = 0 + self._attr_supported_features = 0 self._hs_color = hs_color self._attr_name = name self._rgbw_color = rgbw_color @@ -148,7 +148,7 @@ class DemoLight(LightEntity): supported_color_modes = SUPPORT_DEMO self._color_modes = supported_color_modes if self._effect_list is not None: - self._features |= LightEntityFeature.EFFECT + self._attr_supported_features |= LightEntityFeature.EFFECT @property def device_info(self) -> DeviceInfo: @@ -218,11 +218,6 @@ class DemoLight(LightEntity): """Return true if light is on.""" return self._state - @property - def supported_features(self) -> int: - """Flag supported features.""" - return self._features - @property def supported_color_modes(self) -> set[ColorMode]: """Flag supported color modes.""" diff --git a/homeassistant/components/demo/lock.py b/homeassistant/components/demo/lock.py index d21d89f238b..546161b2d6f 100644 --- a/homeassistant/components/demo/lock.py +++ b/homeassistant/components/demo/lock.py @@ -60,6 +60,7 @@ class DemoLock(LockEntity): ) -> None: """Initialize the lock.""" self._attr_name = name + self._attr_supported_features = 0 if openable: self._attr_supported_features = LockEntityFeature.OPEN self._state = state @@ -109,10 +110,3 @@ class DemoLock(LockEntity): """Open the door latch.""" self._state = STATE_UNLOCKED self.async_write_ha_state() - - @property - def supported_features(self) -> int: - """Flag supported features.""" - if self._openable: - return LockEntityFeature.OPEN - return 0 diff --git a/homeassistant/components/demo/media_player.py b/homeassistant/components/demo/media_player.py index 8bbe380d9ec..646485e8f42 100644 --- a/homeassistant/components/demo/media_player.py +++ b/homeassistant/components/demo/media_player.py @@ -186,6 +186,7 @@ class DemoYoutubePlayer(AbstractDemoPlayer): # We only implement the methods that we support _attr_media_content_type = MediaType.MOVIE + _attr_supported_features = YOUTUBE_PLAYER_SUPPORT def __init__( self, name: str, youtube_id: str, media_title: str, duration: int @@ -223,11 +224,6 @@ class DemoYoutubePlayer(AbstractDemoPlayer): """Return the current running application.""" return "YouTube" - @property - def supported_features(self) -> int: - """Flag media player features that are supported.""" - return YOUTUBE_PLAYER_SUPPORT - @property def media_position(self) -> int | None: """Position of current playing media in seconds.""" @@ -271,6 +267,7 @@ class DemoMusicPlayer(AbstractDemoPlayer): # We only implement the methods that we support _attr_media_content_type = MediaType.MUSIC + _attr_supported_features = MUSIC_PLAYER_SUPPORT tracks = [ ("Technohead", "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)"), @@ -347,11 +344,6 @@ class DemoMusicPlayer(AbstractDemoPlayer): """Return current repeat mode.""" return self._repeat - @property - def supported_features(self) -> int: - """Flag media player features that are supported.""" - return MUSIC_PLAYER_SUPPORT - def media_previous_track(self) -> None: """Send previous track command.""" if self._cur_track > 0: @@ -395,6 +387,7 @@ class DemoTVShowPlayer(AbstractDemoPlayer): # We only implement the methods that we support _attr_media_content_type = MediaType.TVSHOW + _attr_supported_features = NETFLIX_PLAYER_SUPPORT def __init__(self) -> None: """Initialize the demo device.""" @@ -454,11 +447,6 @@ class DemoTVShowPlayer(AbstractDemoPlayer): """List of available sources.""" return self._source_list - @property - def supported_features(self) -> int: - """Flag media player features that are supported.""" - return NETFLIX_PLAYER_SUPPORT - def media_previous_track(self) -> None: """Send previous track command.""" if self._cur_episode > 1: diff --git a/homeassistant/components/demo/vacuum.py b/homeassistant/components/demo/vacuum.py index 015e6b8ca6f..5426c61fa52 100644 --- a/homeassistant/components/demo/vacuum.py +++ b/homeassistant/components/demo/vacuum.py @@ -106,10 +106,12 @@ class DemoVacuum(VacuumEntity): _attr_should_poll = False - def __init__(self, name: str, supported_features: int) -> None: + def __init__( + self, name: str, supported_features: VacuumEntityFeature | int + ) -> None: """Initialize the vacuum.""" self._attr_name = name - self._supported_features = supported_features + self._attr_supported_features = supported_features self._state = False self._status = "Charging" self._fan_speed = FAN_SPEEDS[1] @@ -146,11 +148,6 @@ class DemoVacuum(VacuumEntity): """Return device state attributes.""" return {ATTR_CLEANED_AREA: round(self._cleaned_area, 2)} - @property - def supported_features(self) -> int: - """Flag supported features.""" - return self._supported_features - def turn_on(self, **kwargs: Any) -> None: """Turn the vacuum on.""" if self.supported_features & VacuumEntityFeature.TURN_ON == 0: @@ -251,21 +248,16 @@ class StateDemoVacuum(StateVacuumEntity): """Representation of a demo vacuum supporting states.""" _attr_should_poll = False + _attr_supported_features = SUPPORT_STATE_SERVICES def __init__(self, name: str) -> None: """Initialize the vacuum.""" self._attr_name = name - self._supported_features = SUPPORT_STATE_SERVICES self._state = STATE_DOCKED self._fan_speed = FAN_SPEEDS[1] self._cleaned_area: float = 0 self._battery_level = 100 - @property - def supported_features(self) -> int: - """Flag supported features.""" - return self._supported_features - @property def state(self) -> str: """Return the current state of the vacuum.""" diff --git a/homeassistant/components/demo/water_heater.py b/homeassistant/components/demo/water_heater.py index 322abb0038b..1fc164d5046 100644 --- a/homeassistant/components/demo/water_heater.py +++ b/homeassistant/components/demo/water_heater.py @@ -61,18 +61,11 @@ class DemoWaterHeater(WaterHeaterEntity): """Initialize the water_heater device.""" self._attr_name = name if target_temperature is not None: - self._attr_supported_features = ( - self._attr_supported_features - | WaterHeaterEntityFeature.TARGET_TEMPERATURE - ) + self._attr_supported_features |= WaterHeaterEntityFeature.TARGET_TEMPERATURE if away is not None: - self._attr_supported_features = ( - self._attr_supported_features | WaterHeaterEntityFeature.AWAY_MODE - ) + self._attr_supported_features |= WaterHeaterEntityFeature.AWAY_MODE if current_operation is not None: - self._attr_supported_features = ( - self._attr_supported_features | WaterHeaterEntityFeature.OPERATION_MODE - ) + self._attr_supported_features |= WaterHeaterEntityFeature.OPERATION_MODE self._attr_target_temperature = target_temperature self._attr_temperature_unit = unit_of_measurement self._attr_is_away_mode_on = away diff --git a/tests/components/media_player/test_init.py b/tests/components/media_player/test_init.py index 22206133fca..6c2ebda023a 100644 --- a/tests/components/media_player/test_init.py +++ b/tests/components/media_player/test_init.py @@ -136,7 +136,7 @@ async def test_media_browse(hass, hass_ws_client): client = await hass_ws_client(hass) with patch( - "homeassistant.components.demo.media_player.YOUTUBE_PLAYER_SUPPORT", + "homeassistant.components.demo.media_player.MediaPlayerEntity.supported_features", MediaPlayerEntityFeature.BROWSE_MEDIA, ), patch( "homeassistant.components.media_player.MediaPlayerEntity.async_browse_media", @@ -179,7 +179,7 @@ async def test_media_browse(hass, hass_ws_client): assert mock_browse_media.mock_calls[0][1] == ("album", "abcd") with patch( - "homeassistant.components.demo.media_player.YOUTUBE_PLAYER_SUPPORT", + "homeassistant.components.demo.media_player.MediaPlayerEntity.supported_features", MediaPlayerEntityFeature.BROWSE_MEDIA, ), patch( "homeassistant.components.media_player.MediaPlayerEntity.async_browse_media", @@ -210,7 +210,7 @@ async def test_group_members_available_when_off(hass): # Fake group support for DemoYoutubePlayer with patch( - "homeassistant.components.demo.media_player.YOUTUBE_PLAYER_SUPPORT", + "homeassistant.components.demo.media_player.MediaPlayerEntity.supported_features", MediaPlayerEntityFeature.GROUPING | MediaPlayerEntityFeature.TURN_OFF, ): await hass.services.async_call( From 4c8850ec7b2c65c16e2f3349c6108c57d3dc5ad5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 17 Nov 2022 03:10:55 -0600 Subject: [PATCH 0499/1033] Add dhcp support for newer powerwall gateways (#82236) --- homeassistant/components/powerwall/manifest.json | 3 +++ homeassistant/generated/dhcp.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/homeassistant/components/powerwall/manifest.json b/homeassistant/components/powerwall/manifest.json index 3d1eb07f3fa..d5baae17df6 100644 --- a/homeassistant/components/powerwall/manifest.json +++ b/homeassistant/components/powerwall/manifest.json @@ -8,6 +8,9 @@ "dhcp": [ { "hostname": "1118431-*" + }, + { + "hostname": "1232100-*" } ], "iot_class": "local_polling", diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index 08ea9bfe64c..7aaa183c114 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -326,6 +326,10 @@ DHCP: list[dict[str, str | bool]] = [ "domain": "powerwall", "hostname": "1118431-*", }, + { + "domain": "powerwall", + "hostname": "1232100-*", + }, { "domain": "prusalink", "macaddress": "109C70*", From f60850586e05e486bd32c44bbfeaa9d23ed0816b Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 17 Nov 2022 10:11:25 +0100 Subject: [PATCH 0500/1033] Remove `b64` encoding work-a-round for MQTT camera (#82244) --- homeassistant/components/mqtt/camera.py | 18 +---------- tests/components/mqtt/test_camera.py | 40 +------------------------ 2 files changed, 2 insertions(+), 56 deletions(-) diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index 5044f5abfaf..8f52212172d 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -18,7 +18,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import subscription from .config import MQTT_BASE_SCHEMA -from .const import CONF_ENCODING, CONF_QOS, CONF_TOPIC, DEFAULT_ENCODING +from .const import CONF_QOS, CONF_TOPIC from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, @@ -45,20 +45,6 @@ MQTT_CAMERA_ATTRIBUTES_BLOCKED = frozenset( } ) - -# Using CONF_ENCODING to set b64 encoding for images is deprecated as of Home Assistant 2022.9 -# use CONF_IMAGE_ENCODING instead, support for the work-a-round will be removed with Home Assistant 2022.11 -def repair_legacy_encoding(config: ConfigType) -> ConfigType: - """Check incorrect deprecated config of image encoding.""" - if config[CONF_ENCODING] == "b64": - config[CONF_IMAGE_ENCODING] = "b64" - config[CONF_ENCODING] = DEFAULT_ENCODING - _LOGGER.warning( - "Using the `encoding` parameter to set image encoding has been deprecated, use `image_encoding` instead" - ) - return config - - PLATFORM_SCHEMA_BASE = MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, @@ -69,14 +55,12 @@ PLATFORM_SCHEMA_BASE = MQTT_BASE_SCHEMA.extend( PLATFORM_SCHEMA_MODERN = vol.All( PLATFORM_SCHEMA_BASE.schema, - repair_legacy_encoding, ) # Configuring MQTT Camera under the camera platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_BASE.schema), warn_for_legacy_schema(camera.DOMAIN), - repair_legacy_encoding, ) DISCOVERY_SCHEMA = PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA) diff --git a/tests/components/mqtt/test_camera.py b/tests/components/mqtt/test_camera.py index 4cb6afb6495..5b8ad747244 100644 --- a/tests/components/mqtt/test_camera.py +++ b/tests/components/mqtt/test_camera.py @@ -95,7 +95,7 @@ async def test_run_camera_b64_encoded( camera.DOMAIN: { "topic": topic, "name": "Test Camera", - "encoding": "b64", + "image_encoding": "b64", } } }, @@ -114,44 +114,6 @@ async def test_run_camera_b64_encoded( assert body == "grass" -# Using CONF_ENCODING to set b64 encoding for images is deprecated in Home Assistant 2022.9, use CONF_IMAGE_ENCODING instead -async def test_legacy_camera_b64_encoded_with_availability( - hass, hass_client_no_auth, mqtt_mock_entry_with_yaml_config -): - """Test availability works if b64 encoding (legacy mode) is turned on.""" - topic = "test/camera" - topic_availability = "test/camera_availability" - await async_setup_component( - hass, - mqtt.DOMAIN, - { - mqtt.DOMAIN: { - camera.DOMAIN: { - "topic": topic, - "name": "Test Camera", - "encoding": "b64", - "availability": {"topic": topic_availability}, - } - } - }, - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - - # Make sure we are available - async_fire_mqtt_message(hass, topic_availability, "online") - - url = hass.states.get("camera.test_camera").attributes["entity_picture"] - - async_fire_mqtt_message(hass, topic, b64encode(b"grass")) - - client = await hass_client_no_auth() - resp = await client.get(url) - assert resp.status == HTTPStatus.OK - body = await resp.text() - assert body == "grass" - - async def test_camera_b64_encoded_with_availability( hass, hass_client_no_auth, mqtt_mock_entry_with_yaml_config ): From da2efde35439360b431c9b11ad85a6a8cf98a04c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 10:21:55 +0100 Subject: [PATCH 0501/1033] Set alarm and climate supported features default (#82247) --- homeassistant/components/alarm_control_panel/__init__.py | 2 +- homeassistant/components/climate/__init__.py | 2 +- homeassistant/components/fibaro/climate.py | 1 - homeassistant/components/intesishome/climate.py | 1 - homeassistant/components/izone/climate.py | 1 - homeassistant/components/risco/alarm_control_panel.py | 1 - homeassistant/components/sia/alarm_control_panel.py | 1 - homeassistant/components/switcher_kis/climate.py | 1 - homeassistant/components/tuya/alarm_control_panel.py | 1 - homeassistant/components/tuya/climate.py | 1 - homeassistant/components/zwave_js/climate.py | 1 - 11 files changed, 2 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index 8f523d5af7c..f210af3d670 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -133,7 +133,7 @@ class AlarmControlPanelEntity(Entity): _attr_changed_by: str | None = None _attr_code_arm_required: bool = True _attr_code_format: CodeFormat | None = None - _attr_supported_features: AlarmControlPanelEntityFeature | int + _attr_supported_features: AlarmControlPanelEntityFeature | int = 0 @property def code_format(self) -> CodeFormat | None: diff --git a/homeassistant/components/climate/__init__.py b/homeassistant/components/climate/__init__.py index d19637a9be0..1cea229a306 100644 --- a/homeassistant/components/climate/__init__.py +++ b/homeassistant/components/climate/__init__.py @@ -225,7 +225,7 @@ class ClimateEntity(Entity): _attr_precision: float _attr_preset_mode: str | None _attr_preset_modes: list[str] | None - _attr_supported_features: ClimateEntityFeature | int + _attr_supported_features: ClimateEntityFeature | int = 0 _attr_swing_mode: str | None _attr_swing_modes: list[str] | None _attr_target_humidity: int | None = None diff --git a/homeassistant/components/fibaro/climate.py b/homeassistant/components/fibaro/climate.py index 90a13fe8988..be2b8c6a527 100644 --- a/homeassistant/components/fibaro/climate.py +++ b/homeassistant/components/fibaro/climate.py @@ -126,7 +126,6 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): self._target_temp_device: FibaroDevice | None = None self._op_mode_device: FibaroDevice | None = None self._fan_mode_device: FibaroDevice | None = None - self._attr_supported_features = 0 self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id) siblings = fibaro_device.fibaro_controller.get_siblings(fibaro_device) diff --git a/homeassistant/components/intesishome/climate.py b/homeassistant/components/intesishome/climate.py index 29cb30b81a2..bfdf406ee1a 100644 --- a/homeassistant/components/intesishome/climate.py +++ b/homeassistant/components/intesishome/climate.py @@ -172,7 +172,6 @@ class IntesisAC(ClimateEntity): self._hvane = None self._power = False self._fan_speed = None - self._attr_supported_features = 0 self._power_consumption_heat = None self._power_consumption_cool = None diff --git a/homeassistant/components/izone/climate.py b/homeassistant/components/izone/climate.py index d65ebae24d4..a8dc90ad88c 100644 --- a/homeassistant/components/izone/climate.py +++ b/homeassistant/components/izone/climate.py @@ -445,7 +445,6 @@ class ZoneDevice(ClimateEntity): self._zone = zone self._name = zone.name.title() - self._attr_supported_features = 0 if zone.type != Zone.Type.AUTO: self._state_to_pizone = { HVACMode.OFF: Zone.Mode.CLOSE, diff --git a/homeassistant/components/risco/alarm_control_panel.py b/homeassistant/components/risco/alarm_control_panel.py index 7f4241048d7..116e022d216 100644 --- a/homeassistant/components/risco/alarm_control_panel.py +++ b/homeassistant/components/risco/alarm_control_panel.py @@ -107,7 +107,6 @@ class RiscoAlarm(AlarmControlPanelEntity): self._code_disarm_required = options[CONF_CODE_DISARM_REQUIRED] self._risco_to_ha = options[CONF_RISCO_STATES_TO_HA] self._ha_to_risco = options[CONF_HA_STATES_TO_RISCO] - self._attr_supported_features = 0 self._attr_has_entity_name = True self._attr_name = None for state in self._ha_to_risco: diff --git a/homeassistant/components/sia/alarm_control_panel.py b/homeassistant/components/sia/alarm_control_panel.py index 0a2a17db200..25d3f447a09 100644 --- a/homeassistant/components/sia/alarm_control_panel.py +++ b/homeassistant/components/sia/alarm_control_panel.py @@ -90,7 +90,6 @@ class SIAAlarmControlPanel(SIABaseEntity, AlarmControlPanelEntity): """Class for SIA Alarm Control Panels.""" entity_description: SIAAlarmControlPanelEntityDescription - _attr_supported_features = 0 def __init__( self, diff --git a/homeassistant/components/switcher_kis/climate.py b/homeassistant/components/switcher_kis/climate.py index 99b9208e4ad..8462c8f02f8 100644 --- a/homeassistant/components/switcher_kis/climate.py +++ b/homeassistant/components/switcher_kis/climate.py @@ -104,7 +104,6 @@ class SwitcherClimateEntity( self._attr_target_temperature_step = 1 self._attr_temperature_unit = TEMP_CELSIUS - self._attr_supported_features = 0 self._attr_hvac_modes = [HVACMode.OFF] for mode in remote.modes_features: self._attr_hvac_modes.append(DEVICE_MODE_TO_HA[mode]) diff --git a/homeassistant/components/tuya/alarm_control_panel.py b/homeassistant/components/tuya/alarm_control_panel.py index aae50902d03..d5122862e2c 100644 --- a/homeassistant/components/tuya/alarm_control_panel.py +++ b/homeassistant/components/tuya/alarm_control_panel.py @@ -97,7 +97,6 @@ class TuyaAlarmEntity(TuyaEntity, AlarmControlPanelEntity): description: AlarmControlPanelEntityDescription, ) -> None: """Init Tuya Alarm.""" - self._attr_supported_features = 0 super().__init__(device, device_manager) self.entity_description = description self._attr_unique_id = f"{super().unique_id}{description.key}" diff --git a/homeassistant/components/tuya/climate.py b/homeassistant/components/tuya/climate.py index 757701d5382..1b393dd5872 100644 --- a/homeassistant/components/tuya/climate.py +++ b/homeassistant/components/tuya/climate.py @@ -134,7 +134,6 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity): ) -> None: """Determine which values to use.""" self._attr_target_temperature_step = 1.0 - self._attr_supported_features = 0 self.entity_description = description super().__init__(device, device_manager) diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py index 06f2d225742..03f7fa94642 100644 --- a/homeassistant/components/zwave_js/climate.py +++ b/homeassistant/components/zwave_js/climate.py @@ -182,7 +182,6 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): check_all_endpoints=True, ) self._set_modes_and_presets() - self._attr_supported_features = 0 if self._current_mode and len(self._hvac_presets) > 1: self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE # If any setpoint value exists, we can assume temperature From 3d009236657d3a496966709aafa10b7fa8ddc89c Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 17 Nov 2022 10:22:25 +0100 Subject: [PATCH 0502/1033] Improve MQTT type hints / refactor part 9 - light (#81151) * Improve type hints light base * improve type hints light schema basic * Improve type hints light schema json * Use MQTT Template models with light template * Addtional type hints for template light * Improve template hints for schema template * Use strict type hints for templates * MyPy corrections * Follow up comments * Follow up comments * Revert changes schema_json * Revert changes schema template * Hints setup_entity_json and setup_entity_template * Remove type hint on setup_entity JSON --- .../components/mqtt/light/__init__.py | 21 +- .../components/mqtt/light/schema_basic.py | 262 ++++++++++-------- .../components/mqtt/light/schema_json.py | 16 +- .../components/mqtt/light/schema_template.py | 15 +- 4 files changed, 182 insertions(+), 132 deletions(-) diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index b7d52919d5e..cb43789c6ec 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -1,8 +1,8 @@ """Support for MQTT lights.""" from __future__ import annotations -from collections.abc import Callable import functools +from typing import Any import voluptuous as vol @@ -39,34 +39,37 @@ from .schema_template import ( ) -def validate_mqtt_light_discovery(value): +def validate_mqtt_light_discovery(config_value: dict[str, Any]) -> ConfigType: """Validate MQTT light schema for.""" schemas = { "basic": DISCOVERY_SCHEMA_BASIC, "json": DISCOVERY_SCHEMA_JSON, "template": DISCOVERY_SCHEMA_TEMPLATE, } - return schemas[value[CONF_SCHEMA]](value) + config: ConfigType = schemas[config_value[CONF_SCHEMA]](config_value) + return config -def validate_mqtt_light(value): +def validate_mqtt_light(config_value: dict[str, Any]) -> ConfigType: """Validate MQTT light schema.""" schemas = { "basic": PLATFORM_SCHEMA_BASIC, "json": PLATFORM_SCHEMA_JSON, "template": PLATFORM_SCHEMA_TEMPLATE, } - return schemas[value[CONF_SCHEMA]](value) + config: ConfigType = schemas[config_value[CONF_SCHEMA]](config_value) + return config -def validate_mqtt_light_modern(value): +def validate_mqtt_light_modern(config_value: dict[str, Any]) -> ConfigType: """Validate MQTT light schema.""" schemas = { "basic": PLATFORM_SCHEMA_MODERN_BASIC, "json": PLATFORM_SCHEMA_MODERN_JSON, "template": PLATFORM_SCHEMA_MODERN_TEMPLATE, } - return schemas[value[CONF_SCHEMA]](value) + config: ConfigType = schemas[config_value[CONF_SCHEMA]](config_value) + return config DISCOVERY_SCHEMA = vol.All( @@ -120,11 +123,11 @@ async def _async_setup_entity( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, config: ConfigType, - config_entry: ConfigEntry | None = None, + config_entry: ConfigEntry, discovery_data: dict | None = None, ) -> None: """Set up a MQTT Light.""" - setup_entity: dict[str, Callable] = { + setup_entity = { "basic": async_setup_entity_basic, "json": async_setup_entity_json, "template": async_setup_entity_template, diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index 19d059ae8cc..ea0c3b545da 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -1,6 +1,9 @@ """Support for MQTT lights.""" +from __future__ import annotations + +from collections.abc import Callable import logging -from typing import cast +from typing import Any, cast import voluptuous as vol @@ -25,6 +28,7 @@ from homeassistant.components.light import ( LightEntityFeature, valid_supported_color_modes, ) +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_NAME, CONF_OPTIMISTIC, @@ -33,9 +37,11 @@ from homeassistant.const import ( CONF_VALUE_TEMPLATE, STATE_ON, ) -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType import homeassistant.util.color as color_util from .. import subscription @@ -52,10 +58,14 @@ from ..const import ( from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity from ..models import ( + MessageCallbackType, MqttCommandTemplate, MqttValueTemplate, PayloadSentinel, + PublishPayloadType, ReceiveMessage, + ReceivePayloadType, + TemplateVarsType, ) from ..util import get_mqtt_data, valid_publish_topic, valid_subscribe_topic from .schema import MQTT_LIGHT_SCHEMA_SCHEMA @@ -243,8 +253,12 @@ PLATFORM_SCHEMA_MODERN_BASIC = vol.All( async def async_setup_entity_basic( - hass, config, async_add_entities, config_entry, discovery_data=None -): + hass: HomeAssistant, + config: ConfigType, + async_add_entities: AddEntitiesCallback, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, +) -> None: """Set up a MQTT Light.""" async_add_entities([MqttLight(hass, config, config_entry, discovery_data)]) @@ -254,32 +268,41 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): _entity_id_format = ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED + _topic: dict[str, str | None] + _payload: dict[str, str] + _command_templates: dict[ + str, Callable[[PublishPayloadType, TemplateVarsType], PublishPayloadType] + ] + _value_templates: dict[ + str, Callable[[ReceivePayloadType, ReceivePayloadType], ReceivePayloadType] + ] + _optimistic: bool + _optimistic_brightness: bool + _optimistic_color_mode: bool + _optimistic_color_temp: bool + _optimistic_effect: bool + _optimistic_hs_color: bool + _optimistic_rgb_color: bool + _optimistic_rgbw_color: bool + _optimistic_rgbww_color: bool + _optimistic_xy_color: bool - def __init__(self, hass, config, config_entry, discovery_data): + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize MQTT light.""" - self._topic = None - self._payload = None - self._command_templates = None - self._value_templates = None - self._optimistic = False - self._optimistic_brightness = False - self._optimistic_color_mode = False - self._optimistic_color_temp = False - self._optimistic_effect = False - self._optimistic_hs_color = False - self._optimistic_rgb_color = False - self._optimistic_rgbw_color = False - self._optimistic_rgbww_color = False - self._optimistic_xy_color = False - MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA_BASIC - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" self._attr_min_mireds = config.get(CONF_MIN_MIREDS, super().min_mireds) self._attr_max_mireds = config.get(CONF_MAX_MIREDS, super().max_mireds) @@ -288,7 +311,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): if CONF_STATE_VALUE_TEMPLATE not in config and CONF_VALUE_TEMPLATE in config: config[CONF_STATE_VALUE_TEMPLATE] = config[CONF_VALUE_TEMPLATE] - topic = { + topic: dict[str, str | None] = { key: config.get(key) for key in ( CONF_BRIGHTNESS_COMMAND_TOPIC, @@ -316,32 +339,19 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): self._topic = topic self._payload = {"on": config[CONF_PAYLOAD_ON], "off": config[CONF_PAYLOAD_OFF]} - value_templates = {} - for key in VALUE_TEMPLATE_KEYS: - value_templates[key] = None - if CONF_VALUE_TEMPLATE in config: - value_templates = { - key: config.get(CONF_VALUE_TEMPLATE) for key in VALUE_TEMPLATE_KEYS - } - for key in VALUE_TEMPLATE_KEYS & config.keys(): - value_templates[key] = config[key] self._value_templates = { key: MqttValueTemplate( - template, entity=self + config.get(key), entity=self ).async_render_with_possible_json_value - for key, template in value_templates.items() + for key in VALUE_TEMPLATE_KEYS } - command_templates = {} - for key in COMMAND_TEMPLATE_KEYS: - command_templates[key] = None - for key in COMMAND_TEMPLATE_KEYS & config.keys(): - command_templates[key] = MqttCommandTemplate( - config[key], entity=self - ).async_render - self._command_templates = command_templates + self._command_templates = { + key: MqttCommandTemplate(config.get(key), entity=self).async_render + for key in COMMAND_TEMPLATE_KEYS + } - optimistic = config[CONF_OPTIMISTIC] + optimistic: bool = config[CONF_OPTIMISTIC] self._optimistic_color_mode = ( optimistic or topic[CONF_COLOR_MODE_STATE_TOPIC] is None ) @@ -368,7 +378,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): self._optimistic_effect = optimistic or topic[CONF_EFFECT_STATE_TOPIC] is None self._optimistic_hs_color = optimistic or topic[CONF_HS_STATE_TOPIC] is None self._optimistic_xy_color = optimistic or topic[CONF_XY_STATE_TOPIC] is None - supported_color_modes = set() + supported_color_modes: set[ColorMode] = set() if topic[CONF_COLOR_TEMP_COMMAND_TOPIC] is not None: supported_color_modes.add(ColorMode.COLOR_TEMP) self._attr_color_mode = ColorMode.COLOR_TEMP @@ -413,13 +423,14 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): def _is_optimistic(self, attribute: str) -> bool: """Return True if the attribute is optimistically updated.""" - return getattr(self, f"_optimistic_{attribute}") + attr: bool = getattr(self, f"_optimistic_{attribute}") + return attr - def _prepare_subscribe_topics(self): # noqa: C901 + def _prepare_subscribe_topics(self) -> None: # noqa: C901 """(Re)Subscribe to topics.""" - topics = {} + topics: dict[str, dict[str, Any]] = {} - def add_topic(topic, msg_callback): + def add_topic(topic: str, msg_callback: MessageCallbackType) -> None: """Add a topic.""" if self._topic[topic] is not None: topics[topic] = { @@ -431,9 +442,11 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): @callback @log_messages(self.hass, self.entity_id) - def state_received(msg): + def state_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages.""" - payload = self._value_templates[CONF_STATE_VALUE_TEMPLATE](msg.payload) + payload = self._value_templates[CONF_STATE_VALUE_TEMPLATE]( + msg.payload, PayloadSentinel.NONE + ) if not payload: _LOGGER.debug("Ignoring empty state message from '%s'", msg.topic) return @@ -473,7 +486,12 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): add_topic(CONF_BRIGHTNESS_STATE_TOPIC, brightness_received) - def _rgbx_received(msg, template, color_mode, convert_color): + def _rgbx_received( + msg: ReceiveMessage, + template: str, + color_mode: ColorMode, + convert_color: Callable[..., tuple[int, ...]], + ) -> tuple[int, ...] | None: """Handle new MQTT messages for RGBW and RGBWW.""" payload = self._value_templates[template]( msg.payload, PayloadSentinel.DEFAULT @@ -483,7 +501,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): "Ignoring empty %s message from '%s'", color_mode, msg.topic ) return None - color = tuple(int(val) for val in payload.split(",")) + color = tuple(int(val) for val in str(payload).split(",")) if self._optimistic_color_mode: self._attr_color_mode = color_mode if self._topic[CONF_BRIGHTNESS_STATE_TOPIC] is None: @@ -494,21 +512,21 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): @callback @log_messages(self.hass, self.entity_id) - def rgb_received(msg): + def rgb_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages for RGB.""" rgb = _rgbx_received( msg, CONF_RGB_VALUE_TEMPLATE, ColorMode.RGB, lambda *x: x ) - if not rgb: + if rgb is None: return - self._attr_rgb_color = rgb + self._attr_rgb_color = cast(tuple[int, int, int], rgb) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_RGB_STATE_TOPIC, rgb_received) @callback @log_messages(self.hass, self.entity_id) - def rgbw_received(msg): + def rgbw_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages for RGBW.""" rgbw = _rgbx_received( msg, @@ -516,16 +534,16 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): ColorMode.RGBW, color_util.color_rgbw_to_rgb, ) - if not rgbw: + if rgbw is None: return - self._attr_rgbw_color = rgbw + self._attr_rgbw_color = cast(tuple[int, int, int, int], rgbw) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_RGBW_STATE_TOPIC, rgbw_received) @callback @log_messages(self.hass, self.entity_id) - def rgbww_received(msg): + def rgbww_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages for RGBWW.""" rgbww = _rgbx_received( msg, @@ -533,9 +551,9 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): ColorMode.RGBWW, color_util.color_rgbww_to_rgb, ) - if not rgbww: + if rgbww is None: return - self._attr_rgbww_color = rgbww + self._attr_rgbww_color = cast(tuple[int, int, int, int, int], rgbww) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_RGBWW_STATE_TOPIC, rgbww_received) @@ -551,7 +569,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): _LOGGER.debug("Ignoring empty color mode message from '%s'", msg.topic) return - self._attr_color_mode = payload + self._attr_color_mode = str(payload) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_COLOR_MODE_STATE_TOPIC, color_mode_received) @@ -585,7 +603,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): _LOGGER.debug("Ignoring empty effect message from '%s'", msg.topic) return - self._attr_effect = payload + self._attr_effect = str(payload) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_EFFECT_STATE_TOPIC, effect_received) @@ -601,13 +619,10 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): _LOGGER.debug("Ignoring empty hs message from '%s'", msg.topic) return try: - hs_color = cast( - tuple[float, float], - tuple(float(val) for val in payload.split(",", 2)), - ) + hs_color = tuple(float(val) for val in str(payload).split(",", 2)) if self._optimistic_color_mode: self._attr_color_mode = ColorMode.HS - self._attr_hs_color = hs_color + self._attr_hs_color = cast(tuple[float, float], hs_color) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) except ValueError: _LOGGER.debug("Failed to parse hs state update: '%s'", payload) @@ -625,12 +640,10 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): _LOGGER.debug("Ignoring empty xy-color message from '%s'", msg.topic) return - xy_color = cast( - tuple[float, float], tuple(float(val) for val in payload.split(",", 2)) - ) + xy_color = tuple(float(val) for val in str(payload).split(",", 2)) if self._optimistic_color_mode: self._attr_color_mode = ColorMode.XY - self._attr_xy_color = xy_color + self._attr_xy_color = cast(tuple[float, float], xy_color) get_mqtt_data(self.hass).state_write_requests.write_state_request(self) add_topic(CONF_XY_STATE_TOPIC, xy_received) @@ -639,12 +652,14 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): self.hass, self._sub_state, topics ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) last_state = await self.async_get_last_state() - def restore_state(attribute, condition_attribute=None): + def restore_state( + attribute: str, condition_attribute: str | None = None + ) -> None: """Restore a state attribute.""" if condition_attribute is None: condition_attribute = attribute @@ -671,25 +686,28 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): """Return true if we do optimistic updates.""" return self._optimistic - async def async_turn_on(self, **kwargs): # noqa: C901 + async def async_turn_on(self, **kwargs: Any) -> None: # noqa: C901 """Turn the device on. This method is a coroutine. """ should_update = False - on_command_type = self._config[CONF_ON_COMMAND_TYPE] + on_command_type: str = self._config[CONF_ON_COMMAND_TYPE] - async def publish(topic, payload): + async def publish(topic: str, payload: PublishPayloadType) -> None: """Publish an MQTT message.""" await self.async_publish( - self._topic[topic], + str(self._topic[topic]), payload, self._config[CONF_QOS], self._config[CONF_RETAIN], self._config[CONF_ENCODING], ) - def scale_rgbx(color, brightness=None): + def scale_rgbx( + color: tuple[int, ...], + brightness: int | None = None, + ) -> tuple[int, ...]: """Scale RGBx for brightness.""" if brightness is None: # If there's a brightness topic set, we don't want to scale the RGBx @@ -697,23 +715,30 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): if self._topic[CONF_BRIGHTNESS_COMMAND_TOPIC] is not None: brightness = 255 else: - brightness = kwargs.get(ATTR_BRIGHTNESS, self.brightness or 255) + brightness = kwargs.get(ATTR_BRIGHTNESS) or self.brightness or 255 return tuple(int(channel * brightness / 255) for channel in color) - def render_rgbx(color, template, color_mode): + def render_rgbx( + color: tuple[int, ...], + template: str, + color_mode: ColorMode, + ) -> PublishPayloadType: """Render RGBx payload.""" - if tpl := self._command_templates[template]: - keys = ["red", "green", "blue"] - if color_mode == ColorMode.RGBW: - keys.append("white") - elif color_mode == ColorMode.RGBWW: - keys.extend(["cold_white", "warm_white"]) - rgb_color_str = tpl(variables=zip(keys, color)) - else: - rgb_color_str = ",".join(str(channel) for channel in color) - return rgb_color_str + rgb_color_str = ",".join(str(channel) for channel in color) + keys = ["red", "green", "blue"] + if color_mode == ColorMode.RGBW: + keys.append("white") + elif color_mode == ColorMode.RGBWW: + keys.extend(["cold_white", "warm_white"]) + variables = dict(zip(keys, color)) + return self._command_templates[template](rgb_color_str, variables) - def set_optimistic(attribute, value, color_mode=None, condition_attribute=None): + def set_optimistic( + attribute: str, + value: Any, + color_mode: ColorMode | None = None, + condition_attribute: str | None = None, + ) -> bool: """Optimistically update a state attribute.""" if condition_attribute is None: condition_attribute = attribute @@ -739,12 +764,13 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): ): kwargs[ATTR_BRIGHTNESS] = self.brightness or 255 - hs_color = kwargs.get(ATTR_HS_COLOR) + hs_color: str | None = kwargs.get(ATTR_HS_COLOR) if hs_color and self._topic[CONF_HS_COMMAND_TOPIC] is not None: await publish(CONF_HS_COMMAND_TOPIC, f"{hs_color[0]},{hs_color[1]}") should_update |= set_optimistic(ATTR_HS_COLOR, hs_color, ColorMode.HS) + rgb: tuple[int, int, int] | None if (rgb := kwargs.get(ATTR_RGB_COLOR)) and self._topic[ CONF_RGB_COMMAND_TOPIC ] is not None: @@ -753,6 +779,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): await publish(CONF_RGB_COMMAND_TOPIC, rgb_s) should_update |= set_optimistic(ATTR_RGB_COLOR, rgb, ColorMode.RGB) + rgbw: tuple[int, int, int, int] | None if (rgbw := kwargs.get(ATTR_RGBW_COLOR)) and self._topic[ CONF_RGBW_COMMAND_TOPIC ] is not None: @@ -761,6 +788,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): await publish(CONF_RGBW_COMMAND_TOPIC, rgbw_s) should_update |= set_optimistic(ATTR_RGBW_COLOR, rgbw, ColorMode.RGBW) + rgbww: tuple[int, int, int, int, int] | None if (rgbww := kwargs.get(ATTR_RGBWW_COLOR)) and self._topic[ CONF_RGBWW_COMMAND_TOPIC ] is not None: @@ -769,6 +797,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): await publish(CONF_RGBWW_COMMAND_TOPIC, rgbww_s) should_update |= set_optimistic(ATTR_RGBWW_COLOR, rgbww, ColorMode.RGBWW) + xy_color: tuple[float, float] | None if (xy_color := kwargs.get(ATTR_XY_COLOR)) and self._topic[ CONF_XY_COMMAND_TOPIC ] is not None: @@ -779,16 +808,16 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): ATTR_BRIGHTNESS in kwargs and self._topic[CONF_BRIGHTNESS_COMMAND_TOPIC] is not None ): - brightness_normalized = kwargs[ATTR_BRIGHTNESS] / 255 - brightness_scale = self._config[CONF_BRIGHTNESS_SCALE] + brightness_normalized: float = kwargs[ATTR_BRIGHTNESS] / 255 + brightness_scale: int = self._config[CONF_BRIGHTNESS_SCALE] device_brightness = min( round(brightness_normalized * brightness_scale), brightness_scale ) # Make sure the brightness is not rounded down to 0 device_brightness = max(device_brightness, 1) - if tpl := self._command_templates[CONF_BRIGHTNESS_COMMAND_TEMPLATE]: - device_brightness = tpl(variables={"value": device_brightness}) - await publish(CONF_BRIGHTNESS_COMMAND_TOPIC, device_brightness) + command_tpl = self._command_templates[CONF_BRIGHTNESS_COMMAND_TEMPLATE] + device_brightness_payload = command_tpl(device_brightness, None) + await publish(CONF_BRIGHTNESS_COMMAND_TOPIC, device_brightness_payload) should_update |= set_optimistic(ATTR_BRIGHTNESS, kwargs[ATTR_BRIGHTNESS]) elif ( ATTR_BRIGHTNESS in kwargs @@ -796,8 +825,8 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): and self._topic[CONF_RGB_COMMAND_TOPIC] is not None ): rgb_color = self.rgb_color or (255,) * 3 - rgb = scale_rgbx(rgb_color, kwargs[ATTR_BRIGHTNESS]) - rgb_s = render_rgbx(rgb, CONF_RGB_COMMAND_TEMPLATE, ColorMode.RGB) + rgb_scaled = scale_rgbx(rgb_color, kwargs[ATTR_BRIGHTNESS]) + rgb_s = render_rgbx(rgb_scaled, CONF_RGB_COMMAND_TEMPLATE, ColorMode.RGB) await publish(CONF_RGB_COMMAND_TOPIC, rgb_s) should_update |= set_optimistic(ATTR_BRIGHTNESS, kwargs[ATTR_BRIGHTNESS]) elif ( @@ -806,8 +835,8 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): and self._topic[CONF_RGBW_COMMAND_TOPIC] is not None ): rgbw_color = self.rgbw_color or (255,) * 4 - rgbw = scale_rgbx(rgbw_color, kwargs[ATTR_BRIGHTNESS]) - rgbw_s = render_rgbx(rgbw, CONF_RGBW_COMMAND_TEMPLATE, ColorMode.RGBW) + rgbw_b = scale_rgbx(rgbw_color, kwargs[ATTR_BRIGHTNESS]) + rgbw_s = render_rgbx(rgbw_b, CONF_RGBW_COMMAND_TEMPLATE, ColorMode.RGBW) await publish(CONF_RGBW_COMMAND_TOPIC, rgbw_s) should_update |= set_optimistic(ATTR_BRIGHTNESS, kwargs[ATTR_BRIGHTNESS]) elif ( @@ -816,34 +845,35 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): and self._topic[CONF_RGBWW_COMMAND_TOPIC] is not None ): rgbww_color = self.rgbww_color or (255,) * 5 - rgbww = scale_rgbx(rgbww_color, kwargs[ATTR_BRIGHTNESS]) - rgbww_s = render_rgbx(rgbww, CONF_RGBWW_COMMAND_TEMPLATE, ColorMode.RGBWW) + rgbww_b = scale_rgbx(rgbww_color, kwargs[ATTR_BRIGHTNESS]) + rgbww_s = render_rgbx(rgbww_b, CONF_RGBWW_COMMAND_TEMPLATE, ColorMode.RGBWW) await publish(CONF_RGBWW_COMMAND_TOPIC, rgbww_s) should_update |= set_optimistic(ATTR_BRIGHTNESS, kwargs[ATTR_BRIGHTNESS]) if ( ATTR_COLOR_TEMP in kwargs and self._topic[CONF_COLOR_TEMP_COMMAND_TOPIC] is not None ): - color_temp = int(kwargs[ATTR_COLOR_TEMP]) - if tpl := self._command_templates[CONF_COLOR_TEMP_COMMAND_TEMPLATE]: - color_temp = tpl(variables={"value": color_temp}) - + ct_command_tpl = self._command_templates[CONF_COLOR_TEMP_COMMAND_TEMPLATE] + color_temp = ct_command_tpl(int(kwargs[ATTR_COLOR_TEMP]), None) await publish(CONF_COLOR_TEMP_COMMAND_TOPIC, color_temp) should_update |= set_optimistic( ATTR_COLOR_TEMP, kwargs[ATTR_COLOR_TEMP], ColorMode.COLOR_TEMP ) - if ATTR_EFFECT in kwargs and self._topic[CONF_EFFECT_COMMAND_TOPIC] is not None: - effect = kwargs[ATTR_EFFECT] - if effect in self._config.get(CONF_EFFECT_LIST): - if tpl := self._command_templates[CONF_EFFECT_COMMAND_TEMPLATE]: - effect = tpl(variables={"value": effect}) + if ( + ATTR_EFFECT in kwargs + and self._topic[CONF_EFFECT_COMMAND_TOPIC] is not None + and CONF_EFFECT_LIST in self._config + ): + if kwargs[ATTR_EFFECT] in self._config[CONF_EFFECT_LIST]: + eff_command_tpl = self._command_templates[CONF_EFFECT_COMMAND_TEMPLATE] + effect = eff_command_tpl(kwargs[ATTR_EFFECT], None) await publish(CONF_EFFECT_COMMAND_TOPIC, effect) should_update |= set_optimistic(ATTR_EFFECT, kwargs[ATTR_EFFECT]) if ATTR_WHITE in kwargs and self._topic[CONF_WHITE_COMMAND_TOPIC] is not None: percent_white = float(kwargs[ATTR_WHITE]) / 255 - white_scale = self._config[CONF_WHITE_SCALE] + white_scale: int = self._config[CONF_WHITE_SCALE] device_white_value = min(round(percent_white * white_scale), white_scale) await publish(CONF_WHITE_COMMAND_TOPIC, device_white_value) should_update |= set_optimistic( @@ -864,13 +894,13 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): if should_update: self.async_write_ha_state() - async def async_turn_off(self, **kwargs): + async def async_turn_off(self, **kwargs: Any) -> None: """Turn the device off. This method is a coroutine. """ await self.async_publish( - self._topic[CONF_COMMAND_TOPIC], + str(self._topic[CONF_COMMAND_TOPIC]), self._payload["off"], self._config[CONF_QOS], self._config[CONF_RETAIN], diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index b5824e5e456..76307b61e24 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -1,4 +1,6 @@ """Support for MQTT JSON lights.""" +from __future__ import annotations + from contextlib import suppress import logging @@ -29,6 +31,7 @@ from homeassistant.components.light import ( filter_supported_color_modes, valid_supported_color_modes, ) +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_BRIGHTNESS, CONF_COLOR_TEMP, @@ -40,11 +43,12 @@ from homeassistant.const import ( CONF_XY, STATE_ON, ) -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.json import json_dumps, json_loads from homeassistant.helpers.restore_state import RestoreEntity -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType import homeassistant.util.color as color_util from .. import subscription @@ -174,8 +178,12 @@ PLATFORM_SCHEMA_MODERN_JSON = vol.All( async def async_setup_entity_json( - hass, config: ConfigType, async_add_entities, config_entry, discovery_data -): + hass: HomeAssistant, + config: ConfigType, + async_add_entities: AddEntitiesCallback, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, +) -> None: """Set up a MQTT JSON Light.""" async_add_entities([MqttLightJson(hass, config, config_entry, discovery_data)]) diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index b57e09e0c1f..1085bcb5ef1 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -1,4 +1,6 @@ """Support for MQTT Template lights.""" +from __future__ import annotations + import logging import voluptuous as vol @@ -16,6 +18,7 @@ from homeassistant.components.light import ( LightEntityFeature, filter_supported_color_modes, ) +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_NAME, CONF_OPTIMISTIC, @@ -23,9 +26,11 @@ from homeassistant.const import ( STATE_OFF, STATE_ON, ) -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType import homeassistant.util.color as color_util from .. import subscription @@ -107,8 +112,12 @@ PLATFORM_SCHEMA_MODERN_TEMPLATE = vol.All( async def async_setup_entity_template( - hass, config, async_add_entities, config_entry, discovery_data -): + hass: HomeAssistant, + config: ConfigType, + async_add_entities: AddEntitiesCallback, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, +) -> None: """Set up a MQTT Template light.""" async_add_entities([MqttLightTemplate(hass, config, config_entry, discovery_data)]) From 1b80c661953bdf347a2091275f6dbb8115a299c7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 10:30:39 +0100 Subject: [PATCH 0503/1033] Adjust type hints for FanEntityFeature (#82241) * Adjust type hints for FanEntityFeature * Adjust template default --- homeassistant/components/bond/fan.py | 2 +- homeassistant/components/esphome/fan.py | 2 +- homeassistant/components/fan/__init__.py | 4 ++-- homeassistant/components/homekit_controller/fan.py | 2 +- homeassistant/components/knx/fan.py | 4 ++-- homeassistant/components/template/fan.py | 14 ++++---------- homeassistant/components/zwave_js/fan.py | 6 +++--- pylint/plugins/hass_enforce_type_hints.py | 4 ++++ 8 files changed, 18 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/bond/fan.py b/homeassistant/components/bond/fan.py index d1121e4a3a8..18ce3777b4e 100644 --- a/homeassistant/components/bond/fan.py +++ b/homeassistant/components/bond/fan.py @@ -82,7 +82,7 @@ class BondFan(BondEntity, FanEntity): self._attr_preset_mode = PRESET_MODE_BREEZE if breeze[0] else None @property - def supported_features(self) -> int: + def supported_features(self) -> FanEntityFeature | int: """Flag supported features.""" features = 0 if self._device.supports_speed(): diff --git a/homeassistant/components/esphome/fan.py b/homeassistant/components/esphome/fan.py index 772a1b8befa..fc8a3e600f3 100644 --- a/homeassistant/components/esphome/fan.py +++ b/homeassistant/components/esphome/fan.py @@ -158,7 +158,7 @@ class EsphomeFan(EsphomeEntity[FanInfo, FanState], FanEntity): return _FAN_DIRECTIONS.from_esphome(self._state.direction) @property - def supported_features(self) -> int: + def supported_features(self) -> FanEntityFeature | int: """Flag supported features.""" flags = 0 if self._static_info.supports_oscillation: diff --git a/homeassistant/components/fan/__init__.py b/homeassistant/components/fan/__init__.py index b65e6f75b3e..b5cd653502f 100644 --- a/homeassistant/components/fan/__init__.py +++ b/homeassistant/components/fan/__init__.py @@ -190,7 +190,7 @@ class FanEntity(ToggleEntity): _attr_preset_mode: str | None _attr_preset_modes: list[str] | None _attr_speed_count: int - _attr_supported_features: int = 0 + _attr_supported_features: FanEntityFeature | int = 0 def set_percentage(self, percentage: int) -> None: """Set the speed of the fan, as a percentage.""" @@ -363,7 +363,7 @@ class FanEntity(ToggleEntity): return data @property - def supported_features(self) -> int: + def supported_features(self) -> FanEntityFeature | int: """Flag supported features.""" return self._attr_supported_features diff --git a/homeassistant/components/homekit_controller/fan.py b/homeassistant/components/homekit_controller/fan.py index cdd9c3e803c..dffa323f8ad 100644 --- a/homeassistant/components/homekit_controller/fan.py +++ b/homeassistant/components/homekit_controller/fan.py @@ -95,7 +95,7 @@ class BaseHomeKitFan(HomeKitEntity, FanEntity): return oscillating == 1 @property - def supported_features(self) -> int: + def supported_features(self) -> FanEntityFeature | int: """Flag supported features.""" features = 0 diff --git a/homeassistant/components/knx/fan.py b/homeassistant/components/knx/fan.py index 27f9fb963f7..60db7e95a65 100644 --- a/homeassistant/components/knx/fan.py +++ b/homeassistant/components/knx/fan.py @@ -76,9 +76,9 @@ class KNXFan(KnxEntity, FanEntity): await self._device.set_speed(percentage) @property - def supported_features(self) -> int: + def supported_features(self) -> FanEntityFeature: """Flag supported features.""" - flags: int = FanEntityFeature.SET_SPEED + flags = FanEntityFeature.SET_SPEED if self._device.supports_oscillation: flags |= FanEntityFeature.OSCILLATE diff --git a/homeassistant/components/template/fan.py b/homeassistant/components/template/fan.py index b27a6ee3e51..3d3c17551ca 100644 --- a/homeassistant/components/template/fan.py +++ b/homeassistant/components/template/fan.py @@ -147,7 +147,6 @@ class TemplateFan(TemplateEntity, FanEntity): self._preset_mode_template = config.get(CONF_PRESET_MODE_TEMPLATE) self._oscillating_template = config.get(CONF_OSCILLATING_TEMPLATE) self._direction_template = config.get(CONF_DIRECTION_TEMPLATE) - self._supported_features = 0 self._on_script = Script(hass, config[CONF_ON_ACTION], friendly_name, DOMAIN) self._off_script = Script(hass, config[CONF_OFF_ACTION], friendly_name, DOMAIN) @@ -189,18 +188,13 @@ class TemplateFan(TemplateEntity, FanEntity): self._preset_modes = config.get(CONF_PRESET_MODES) if self._percentage_template: - self._supported_features |= FanEntityFeature.SET_SPEED + self._attr_supported_features |= FanEntityFeature.SET_SPEED if self._preset_mode_template and self._preset_modes: - self._supported_features |= FanEntityFeature.PRESET_MODE + self._attr_supported_features |= FanEntityFeature.PRESET_MODE if self._oscillating_template: - self._supported_features |= FanEntityFeature.OSCILLATE + self._attr_supported_features |= FanEntityFeature.OSCILLATE if self._direction_template: - self._supported_features |= FanEntityFeature.DIRECTION - - @property - def supported_features(self) -> int: - """Flag supported features.""" - return self._supported_features + self._attr_supported_features |= FanEntityFeature.DIRECTION @property def speed_count(self) -> int: diff --git a/homeassistant/components/zwave_js/fan.py b/homeassistant/components/zwave_js/fan.py index 27f73353ca8..bf4942d76cc 100644 --- a/homeassistant/components/zwave_js/fan.py +++ b/homeassistant/components/zwave_js/fan.py @@ -250,9 +250,9 @@ class ValueMappingZwaveFan(ZwaveFan): return len(self.fan_value_mapping.speeds) @property - def supported_features(self) -> int: + def supported_features(self) -> FanEntityFeature: """Flag supported features.""" - flags: int = FanEntityFeature.SET_SPEED + flags = FanEntityFeature.SET_SPEED if self.has_fan_value_mapping and self.fan_value_mapping.presets: flags |= FanEntityFeature.PRESET_MODE @@ -387,7 +387,7 @@ class ZwaveThermostatFan(ZWaveBaseEntity, FanEntity): return list(self._fan_mode.metadata.states.values()) @property - def supported_features(self) -> int: + def supported_features(self) -> FanEntityFeature: """Flag supported features.""" return FanEntityFeature.PRESET_MODE diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 2bb80cac2d7..5da1c563c0f 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1288,6 +1288,10 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { function_name="preset_modes", return_type=["list[str]", None], ), + TypeHintMatch( + function_name="supported_features", + return_type=["FanEntityFeature", "int"], + ), TypeHintMatch( function_name="set_percentage", arg_types={1: "int"}, From ad8b882cb623f8b42971cdf2f406af3a9f87f4b1 Mon Sep 17 00:00:00 2001 From: Thomas Dietrich Date: Thu, 17 Nov 2022 10:31:06 +0100 Subject: [PATCH 0504/1033] Switch statistics config to require either/both 'max_age' and 'sampling_size' (#80999) * Remove default characteristic * Remove default sampling_size * Fix typo * Fix typo --- homeassistant/components/statistics/sensor.py | 95 +++++++------------ .../components/statistics/strings.json | 12 --- .../statistics/fixtures/configuration.yaml | 1 + tests/components/statistics/test_sensor.py | 36 +++++-- 4 files changed, 65 insertions(+), 79 deletions(-) delete mode 100644 homeassistant/components/statistics/strings.json diff --git a/homeassistant/components/statistics/sensor.py b/homeassistant/components/statistics/sensor.py index 55a225eebb8..64e967604c1 100644 --- a/homeassistant/components/statistics/sensor.py +++ b/homeassistant/components/statistics/sensor.py @@ -34,11 +34,10 @@ from homeassistant.core import ( Event, HomeAssistant, State, - async_get_hass, callback, split_entity_id, ) -from homeassistant.helpers import config_validation as cv, issue_registry +from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import ( async_track_point_in_utc_time, @@ -179,7 +178,7 @@ CONF_MAX_AGE = "max_age" CONF_PRECISION = "precision" CONF_PERCENTILE = "percentile" -DEFAULT_NAME = "Stats" +DEFAULT_NAME = "Statistical characteristic" DEFAULT_PRECISION = 2 ICON = "mdi:calculator" @@ -187,24 +186,6 @@ ICON = "mdi:calculator" def valid_state_characteristic_configuration(config: dict[str, Any]) -> dict[str, Any]: """Validate that the characteristic selected is valid for the source sensor type, throw if it isn't.""" is_binary = split_entity_id(config[CONF_ENTITY_ID])[0] == BINARY_SENSOR_DOMAIN - - if config.get(CONF_STATE_CHARACTERISTIC) is None: - config[CONF_STATE_CHARACTERISTIC] = STAT_COUNT if is_binary else STAT_MEAN - issue_registry.async_create_issue( - hass=async_get_hass(), - domain=DOMAIN, - issue_id=f"{config[CONF_ENTITY_ID]}_default_characteristic", - breaks_in_ha_version="2022.12.0", - is_fixable=False, - severity=issue_registry.IssueSeverity.WARNING, - translation_key="deprecation_warning_characteristic", - translation_placeholders={ - "entity": config[CONF_NAME], - "characteristic": config[CONF_STATE_CHARACTERISTIC], - }, - learn_more_url="https://github.com/home-assistant/core/pull/60402", - ) - characteristic = cast(str, config[CONF_STATE_CHARACTERISTIC]) if (is_binary and characteristic not in STATS_BINARY_SUPPORT) or ( not is_binary and characteristic not in STATS_NUMERIC_SUPPORT @@ -218,20 +199,14 @@ def valid_state_characteristic_configuration(config: dict[str, Any]) -> dict[str def valid_boundary_configuration(config: dict[str, Any]) -> dict[str, Any]: - """Validate that sampling_size, max_age, or both are provided.""" + """Validate that max_age, sampling_size, or both are provided.""" - if config.get(CONF_SAMPLES_MAX_BUFFER_SIZE) is None: - config[CONF_SAMPLES_MAX_BUFFER_SIZE] = 20 - issue_registry.async_create_issue( - hass=async_get_hass(), - domain=DOMAIN, - issue_id=f"{config[CONF_ENTITY_ID]}_invalid_boundary_config", - breaks_in_ha_version="2022.12.0", - is_fixable=False, - severity=issue_registry.IssueSeverity.WARNING, - translation_key="deprecation_warning_size", - translation_placeholders={"entity": config[CONF_NAME]}, - learn_more_url="https://github.com/home-assistant/core/pull/69700", + if ( + config.get(CONF_SAMPLES_MAX_BUFFER_SIZE) is None + and config.get(CONF_MAX_AGE) is None + ): + raise vol.RequiredFieldInvalid( + "The sensor configuration must provide 'max_age' and/or 'sampling_size'" ) return config @@ -241,8 +216,10 @@ _PLATFORM_SCHEMA_BASE = PLATFORM_SCHEMA.extend( vol.Required(CONF_ENTITY_ID): cv.entity_id, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_STATE_CHARACTERISTIC): cv.string, - vol.Optional(CONF_SAMPLES_MAX_BUFFER_SIZE): vol.Coerce(int), + vol.Required(CONF_STATE_CHARACTERISTIC): cv.string, + vol.Optional(CONF_SAMPLES_MAX_BUFFER_SIZE): vol.All( + vol.Coerce(int), vol.Range(min=1) + ), vol.Optional(CONF_MAX_AGE): cv.time_period, vol.Optional(CONF_PRECISION, default=DEFAULT_PRECISION): vol.Coerce(int), vol.Optional(CONF_PERCENTILE, default=50): vol.All( @@ -274,7 +251,7 @@ async def async_setup_platform( name=config[CONF_NAME], unique_id=config.get(CONF_UNIQUE_ID), state_characteristic=config[CONF_STATE_CHARACTERISTIC], - samples_max_buffer_size=config[CONF_SAMPLES_MAX_BUFFER_SIZE], + samples_max_buffer_size=config.get(CONF_SAMPLES_MAX_BUFFER_SIZE), samples_max_age=config.get(CONF_MAX_AGE), precision=config[CONF_PRECISION], percentile=config[CONF_PERCENTILE], @@ -293,7 +270,7 @@ class StatisticsSensor(SensorEntity): name: str, unique_id: str | None, state_characteristic: str, - samples_max_buffer_size: int, + samples_max_buffer_size: int | None, samples_max_age: timedelta | None, precision: int, percentile: int, @@ -308,20 +285,17 @@ class StatisticsSensor(SensorEntity): split_entity_id(self._source_entity_id)[0] == BINARY_SENSOR_DOMAIN ) self._state_characteristic: str = state_characteristic - self._samples_max_buffer_size: int = samples_max_buffer_size + self._samples_max_buffer_size: int | None = samples_max_buffer_size self._samples_max_age: timedelta | None = samples_max_age self._precision: int = precision self._percentile: int = percentile self._value: StateType | datetime = None self._unit_of_measurement: str | None = None self._available: bool = False + self.states: deque[float | bool] = deque(maxlen=self._samples_max_buffer_size) self.ages: deque[datetime] = deque(maxlen=self._samples_max_buffer_size) - self.attributes: dict[str, StateType] = { - STAT_AGE_COVERAGE_RATIO: None, - STAT_BUFFER_USAGE_RATIO: None, - STAT_SOURCE_VALUE_VALID: None, - } + self.attributes: dict[str, StateType] = {} self._state_characteristic_fn: Callable[[], StateType | datetime] if self.is_binary: @@ -496,11 +470,8 @@ class StatisticsSensor(SensorEntity): self._update_value() # If max_age is set, ensure to update again after the defined interval. - next_to_purge_timestamp = self._next_to_purge_timestamp() - if next_to_purge_timestamp: - _LOGGER.debug( - "%s: scheduling update at %s", self.entity_id, next_to_purge_timestamp - ) + if timestamp := self._next_to_purge_timestamp(): + _LOGGER.debug("%s: scheduling update at %s", self.entity_id, timestamp) if self._update_listener: self._update_listener() self._update_listener = None @@ -513,7 +484,7 @@ class StatisticsSensor(SensorEntity): self._update_listener = None self._update_listener = async_track_point_in_utc_time( - self.hass, _scheduled_update, next_to_purge_timestamp + self.hass, _scheduled_update, timestamp ) def _fetch_states_from_database(self) -> list[State]: @@ -563,18 +534,20 @@ class StatisticsSensor(SensorEntity): def _update_attributes(self) -> None: """Calculate and update the various attributes.""" - self.attributes[STAT_BUFFER_USAGE_RATIO] = round( - len(self.states) / self._samples_max_buffer_size, 2 - ) - - if len(self.states) >= 1 and self._samples_max_age is not None: - self.attributes[STAT_AGE_COVERAGE_RATIO] = round( - (self.ages[-1] - self.ages[0]).total_seconds() - / self._samples_max_age.total_seconds(), - 2, + if self._samples_max_buffer_size is not None: + self.attributes[STAT_BUFFER_USAGE_RATIO] = round( + len(self.states) / self._samples_max_buffer_size, 2 ) - else: - self.attributes[STAT_AGE_COVERAGE_RATIO] = None + + if self._samples_max_age is not None: + if len(self.states) >= 1: + self.attributes[STAT_AGE_COVERAGE_RATIO] = round( + (self.ages[-1] - self.ages[0]).total_seconds() + / self._samples_max_age.total_seconds(), + 2, + ) + else: + self.attributes[STAT_AGE_COVERAGE_RATIO] = None def _update_value(self) -> None: """Front to call the right statistical characteristics functions. diff --git a/homeassistant/components/statistics/strings.json b/homeassistant/components/statistics/strings.json deleted file mode 100644 index 0cca71f172f..00000000000 --- a/homeassistant/components/statistics/strings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "issues": { - "deprecation_warning_characteristic": { - "description": "The configuration parameter `state_characteristic` of the statistics integration will become mandatory.\n\nPlease add `state_characteristic: {characteristic}` to the configuration of sensor `{entity}` to keep the current behavior.\n\nRead the documentation of the statistics integration for further details: https://www.home-assistant.io/integrations/statistics/", - "title": "Mandatory 'state_characteristic' assumed for a Statistics entity" - }, - "deprecation_warning_size": { - "description": "The configuration parameter `sampling_size` of the statistics integration defaulted to the value 20 so far, which will change.\n\nPlease check the configuration for sensor `{entity}` and add suited boundaries, e.g., `sampling_size: 20` to keep the current behavior. The configuration of the statistics integration will become more flexible with version 2022.12.0 and accept either `sampling_size` or `max_age`, or both settings. The request above prepares your configuration for this otherwise breaking change.\n\nRead the documentation of the statistics integration for further details: https://www.home-assistant.io/integrations/statistics/", - "title": "Implicit 'sampling_size' assumed for a Statistics entity" - } - } -} diff --git a/tests/components/statistics/fixtures/configuration.yaml b/tests/components/statistics/fixtures/configuration.yaml index 4708910b53e..b7a10756281 100644 --- a/tests/components/statistics/fixtures/configuration.yaml +++ b/tests/components/statistics/fixtures/configuration.yaml @@ -3,3 +3,4 @@ sensor: entity_id: sensor.cpu name: cputest state_characteristic: mean + sampling_size: 20 diff --git a/tests/components/statistics/test_sensor.py b/tests/components/statistics/test_sensor.py index be771d8be88..bd73216d69e 100644 --- a/tests/components/statistics/test_sensor.py +++ b/tests/components/statistics/test_sensor.py @@ -45,8 +45,10 @@ async def test_unique_id(hass: HomeAssistant): { "platform": "statistics", "name": "test", - "entity_id": "sensor.test_monitored", "unique_id": "uniqueid_sensor_test", + "entity_id": "sensor.test_monitored", + "state_characteristic": "mean", + "sampling_size": 20, }, ] }, @@ -71,6 +73,8 @@ async def test_sensor_defaults_numeric(hass: HomeAssistant): "platform": "statistics", "name": "test", "entity_id": "sensor.test_monitored", + "state_characteristic": "mean", + "sampling_size": 20, }, ] }, @@ -162,6 +166,8 @@ async def test_sensor_defaults_binary(hass: HomeAssistant): "platform": "statistics", "name": "test", "entity_id": "binary_sensor.test_monitored", + "state_characteristic": "count", + "sampling_size": 20, }, ] }, @@ -199,12 +205,14 @@ async def test_sensor_source_with_force_update(hass: HomeAssistant): "name": "test_normal", "entity_id": "sensor.test_monitored_normal", "state_characteristic": "mean", + "sampling_size": 20, }, { "platform": "statistics", "name": "test_force", "entity_id": "sensor.test_monitored_force", "state_characteristic": "mean", + "sampling_size": 20, }, ] }, @@ -234,8 +242,8 @@ async def test_sensor_source_with_force_update(hass: HomeAssistant): assert state_force.attributes.get("buffer_usage_ratio") == round(9 / 20, 2) -async def test_sampling_size_non_default(hass: HomeAssistant): - """Test rotation.""" +async def test_sampling_size_reduced(hass: HomeAssistant): + """Test limited buffer size.""" assert await async_setup_component( hass, "sensor", @@ -287,7 +295,7 @@ async def test_sampling_size_1(hass: HomeAssistant): ) await hass.async_block_till_done() - for value in VALUES_NUMERIC[-3:]: # just the last 3 will do + for value in VALUES_NUMERIC: hass.states.async_set( "sensor.test_monitored", str(value), @@ -303,7 +311,7 @@ async def test_sampling_size_1(hass: HomeAssistant): async def test_age_limit_expiry(hass: HomeAssistant): - """Test that values are removed after certain age.""" + """Test that values are removed with given max age.""" now = dt_util.utcnow() mock_data = { "return_time": datetime(now.year + 1, 8, 2, 12, 23, tzinfo=dt_util.UTC) @@ -325,6 +333,7 @@ async def test_age_limit_expiry(hass: HomeAssistant): "name": "test", "entity_id": "sensor.test_monitored", "state_characteristic": "mean", + "sampling_size": 20, "max_age": {"minutes": 4}, }, ] @@ -402,6 +411,7 @@ async def test_precision(hass: HomeAssistant): "name": "test_precision_0", "entity_id": "sensor.test_monitored", "state_characteristic": "mean", + "sampling_size": 20, "precision": 0, }, { @@ -409,6 +419,7 @@ async def test_precision(hass: HomeAssistant): "name": "test_precision_3", "entity_id": "sensor.test_monitored", "state_characteristic": "mean", + "sampling_size": 20, "precision": 3, }, ] @@ -500,6 +511,7 @@ async def test_device_class(hass: HomeAssistant): "name": "test_source_class", "entity_id": "sensor.test_monitored", "state_characteristic": "mean", + "sampling_size": 20, }, { # Device class is set to None for characteristics with special meaning @@ -507,6 +519,7 @@ async def test_device_class(hass: HomeAssistant): "name": "test_none", "entity_id": "sensor.test_monitored", "state_characteristic": "count", + "sampling_size": 20, }, { # Device class is set to timestamp for datetime characteristics @@ -514,6 +527,7 @@ async def test_device_class(hass: HomeAssistant): "name": "test_timestamp", "entity_id": "sensor.test_monitored", "state_characteristic": "datetime_oldest", + "sampling_size": 20, }, ] }, @@ -554,12 +568,14 @@ async def test_state_class(hass: HomeAssistant): "name": "test_normal", "entity_id": "sensor.test_monitored", "state_characteristic": "count", + "sampling_size": 20, }, { "platform": "statistics", "name": "test_nan", "entity_id": "sensor.test_monitored", "state_characteristic": "datetime_oldest", + "sampling_size": 20, }, ] }, @@ -594,29 +610,35 @@ async def test_unitless_source_sensor(hass: HomeAssistant): "name": "test_unitless_1", "entity_id": "sensor.test_monitored_unitless", "state_characteristic": "count", + "sampling_size": 20, }, { "platform": "statistics", "name": "test_unitless_2", "entity_id": "sensor.test_monitored_unitless", "state_characteristic": "mean", + "sampling_size": 20, }, { "platform": "statistics", "name": "test_unitless_3", "entity_id": "sensor.test_monitored_unitless", "state_characteristic": "change_second", + "sampling_size": 20, }, { "platform": "statistics", "name": "test_unitless_4", "entity_id": "binary_sensor.test_monitored_unitless", + "state_characteristic": "count", + "sampling_size": 20, }, { "platform": "statistics", "name": "test_unitless_5", "entity_id": "binary_sensor.test_monitored_unitless", "state_characteristic": "mean", + "sampling_size": 20, }, ] }, @@ -1087,12 +1109,14 @@ async def test_invalid_state_characteristic(hass: HomeAssistant): "name": "test_numeric", "entity_id": "sensor.test_monitored", "state_characteristic": "invalid", + "sampling_size": 20, }, { "platform": "statistics", "name": "test_binary", "entity_id": "binary_sensor.test_monitored", "state_characteristic": "variance", + "sampling_size": 20, }, ] }, @@ -1192,8 +1216,8 @@ async def test_initialize_from_database_with_maxage(recorder_mock, hass: HomeAss "platform": "statistics", "name": "test", "entity_id": "sensor.test_monitored", - "sampling_size": 100, "state_characteristic": "datetime_newest", + "sampling_size": 100, "max_age": {"hours": 3}, }, ] From 38a8e86ddeb65ee8c731b90a7063a3b3702dc1ef Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 10:45:32 +0100 Subject: [PATCH 0505/1033] Cleanup supported_features in group (#82242) * Cleanup supported_features in group * Remove defaults (already set to 0 in fan and media_player) --- homeassistant/components/group/fan.py | 8 +------- homeassistant/components/group/media_player.py | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/group/fan.py b/homeassistant/components/group/fan.py index 7d09c9573b5..09b59a2d123 100644 --- a/homeassistant/components/group/fan.py +++ b/homeassistant/components/group/fan.py @@ -110,18 +110,12 @@ class FanGroup(GroupEntity, FanEntity): self._percentage = None self._oscillating = None self._direction = None - self._supported_features = 0 self._speed_count = 100 self._is_on: bool | None = False self._attr_name = name self._attr_extra_state_attributes = {ATTR_ENTITY_ID: entities} self._attr_unique_id = unique_id - @property - def supported_features(self) -> int: - """Flag supported features.""" - return self._supported_features - @property def speed_count(self) -> int: """Return the number of speeds the fan supports.""" @@ -319,7 +313,7 @@ class FanGroup(GroupEntity, FanEntity): "_direction", FanEntityFeature.DIRECTION, ATTR_DIRECTION ) - self._supported_features = reduce( + self._attr_supported_features = reduce( ior, [feature for feature in SUPPORTED_FLAGS if self._fans[feature]], 0 ) self._attr_assumed_state |= any( diff --git a/homeassistant/components/group/media_player.py b/homeassistant/components/group/media_player.py index ddb44072080..2e46599aba1 100644 --- a/homeassistant/components/group/media_player.py +++ b/homeassistant/components/group/media_player.py @@ -109,7 +109,6 @@ class MediaPlayerGroup(MediaPlayerEntity): """Initialize a Media Group entity.""" self._name = name self._state: str | None = None - self._supported_features: int = 0 self._attr_unique_id = unique_id self._entities = entities @@ -212,11 +211,6 @@ class MediaPlayerGroup(MediaPlayerEntity): """Return the state of the media group.""" return self._state - @property - def supported_features(self) -> int: - """Flag supported features.""" - return self._supported_features - @property def extra_state_attributes(self) -> dict: """Return the state attributes for the media group.""" @@ -442,5 +436,5 @@ class MediaPlayerGroup(MediaPlayerEntity): | MediaPlayerEntityFeature.VOLUME_STEP ) - self._supported_features = supported_features + self._attr_supported_features = supported_features self.async_write_ha_state() From 2453f95b2442036200a07d862d98bfd3a401e726 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 10:58:46 +0100 Subject: [PATCH 0506/1033] Adjust HumidifierEntity type hints (#82248) --- .../generic_hygrostat/humidifier.py | 1 - .../components/humidifier/__init__.py | 6 ++ homeassistant/components/mqtt/humidifier.py | 2 - homeassistant/components/tuya/humidifier.py | 1 - .../components/xiaomi_miio/humidifier.py | 1 - pylint/plugins/hass_enforce_type_hints.py | 55 +++++++++++++++++++ 6 files changed, 61 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/generic_hygrostat/humidifier.py b/homeassistant/components/generic_hygrostat/humidifier.py index 720c76e766d..e6caf2ca097 100644 --- a/homeassistant/components/generic_hygrostat/humidifier.py +++ b/homeassistant/components/generic_hygrostat/humidifier.py @@ -147,7 +147,6 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity): self._min_humidity = min_humidity self._max_humidity = max_humidity self._target_humidity = target_humidity - self._attr_supported_features = 0 if away_humidity: self._attr_supported_features |= HumidifierEntityFeature.MODES self._away_humidity = away_humidity diff --git a/homeassistant/components/humidifier/__init__.py b/homeassistant/components/humidifier/__init__.py index 1077e133b3a..ef1620b3922 100644 --- a/homeassistant/components/humidifier/__init__.py +++ b/homeassistant/components/humidifier/__init__.py @@ -137,6 +137,7 @@ class HumidifierEntity(ToggleEntity): _attr_max_humidity: int = DEFAULT_MAX_HUMIDITY _attr_min_humidity: int = DEFAULT_MIN_HUMIDITY _attr_mode: str | None + _attr_supported_features: HumidifierEntityFeature | int = 0 _attr_target_humidity: int | None = None @property @@ -223,3 +224,8 @@ class HumidifierEntity(ToggleEntity): def max_humidity(self) -> int: """Return the maximum humidity.""" return self._attr_max_humidity + + @property + def supported_features(self) -> HumidifierEntityFeature | int: + """Return the list of supported features.""" + return self._attr_supported_features diff --git a/homeassistant/components/mqtt/humidifier.py b/homeassistant/components/mqtt/humidifier.py index 69b6d3e3e89..c8bb469ed2e 100644 --- a/homeassistant/components/mqtt/humidifier.py +++ b/homeassistant/components/mqtt/humidifier.py @@ -271,8 +271,6 @@ class MqttHumidifier(MqttEntity, HumidifierEntity): self._attr_available_modes = [] if self._attr_available_modes: self._attr_supported_features = HumidifierEntityFeature.MODES - else: - self._attr_supported_features = 0 optimistic: bool = config[CONF_OPTIMISTIC] self._optimistic = optimistic or self._topic[CONF_STATE_TOPIC] is None diff --git a/homeassistant/components/tuya/humidifier.py b/homeassistant/components/tuya/humidifier.py index 9891c81a456..765de4d860a 100644 --- a/homeassistant/components/tuya/humidifier.py +++ b/homeassistant/components/tuya/humidifier.py @@ -93,7 +93,6 @@ class TuyaHumidifierEntity(TuyaEntity, HumidifierEntity): super().__init__(device, device_manager) self.entity_description = description self._attr_unique_id = f"{super().unique_id}{description.key}" - self._attr_supported_features = 0 # Determine main switch DPCode self._switch_dpcode = self.find_dpcode( diff --git a/homeassistant/components/xiaomi_miio/humidifier.py b/homeassistant/components/xiaomi_miio/humidifier.py index b5a5e738ea0..2cf3944bb91 100644 --- a/homeassistant/components/xiaomi_miio/humidifier.py +++ b/homeassistant/components/xiaomi_miio/humidifier.py @@ -115,7 +115,6 @@ class XiaomiGenericHumidifier(XiaomiCoordinatedMiioEntity, HumidifierEntity): _attr_device_class = HumidifierDeviceClass.HUMIDIFIER _attr_supported_features = HumidifierEntityFeature.MODES - supported_features: int def __init__(self, device, entry, unique_id, coordinator): """Initialize the generic Xiaomi device.""" diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 5da1c563c0f..3da5a537435 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1395,6 +1395,61 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ], ), ], + "humidifier": [ + ClassTypeHintMatch( + base_class="Entity", + matches=_ENTITY_MATCH, + ), + ClassTypeHintMatch( + base_class="ToggleEntity", + matches=_TOGGLE_ENTITY_MATCH, + ), + ClassTypeHintMatch( + base_class="HumidifierEntity", + matches=[ + TypeHintMatch( + function_name="available_modes", + return_type=["list[str]", None], + ), + TypeHintMatch( + function_name="device_class", + return_type=["HumidifierDeviceClass", "str", None], + ), + TypeHintMatch( + function_name="min_humidity", + return_type=["int"], + ), + TypeHintMatch( + function_name="max_humidity", + return_type=["int"], + ), + TypeHintMatch( + function_name="mode", + return_type=["str", None], + ), + TypeHintMatch( + function_name="supported_features", + return_type=["HumidifierEntityFeature", "int"], + ), + TypeHintMatch( + function_name="target_humidity", + return_type=["int", None], + ), + TypeHintMatch( + function_name="set_humidity", + arg_types={1: "str"}, + return_type=None, + has_async_counterpart=True, + ), + TypeHintMatch( + function_name="set_mode", + arg_types={1: "str"}, + return_type=None, + has_async_counterpart=True, + ), + ], + ), + ], "light": [ ClassTypeHintMatch( base_class="Entity", From 8a084cf94d4f2ed760994573254dc968cd17e799 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:45:59 +0100 Subject: [PATCH 0507/1033] Use _attr_supported_features in osram light (#82255) --- .../components/osramlightify/light.py | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/osramlightify/light.py b/homeassistant/components/osramlightify/light.py index e1b204115e6..d3c529c738e 100644 --- a/homeassistant/components/osramlightify/light.py +++ b/homeassistant/components/osramlightify/light.py @@ -187,7 +187,6 @@ class Luminary(LightEntity): self._changed = changed self._unique_id = None - self._supported_features = [] self._effect_list = [] self._is_on = False self._available = True @@ -205,9 +204,9 @@ class Luminary(LightEntity): """Get a unique ID (not implemented).""" raise NotImplementedError - def _get_supported_color_modes(self): + def _get_supported_color_modes(self) -> set[ColorMode]: """Get supported color modes.""" - color_modes = set() + color_modes: set[ColorMode] = set() if "temp" in self._luminary.supported_features(): color_modes.add(ColorMode.COLOR_TEMP) @@ -222,7 +221,7 @@ class Luminary(LightEntity): return color_modes - def _get_supported_features(self): + def _get_supported_features(self) -> LightEntityFeature | int: """Get list of supported features.""" features = 0 if "lum" in self._luminary.supported_features(): @@ -271,11 +270,6 @@ class Luminary(LightEntity): """Return True if the device is on.""" return self._is_on - @property - def supported_features(self): - """List of supported features.""" - return self._supported_features - @property def effect_list(self): """List of supported effects.""" @@ -360,11 +354,11 @@ class Luminary(LightEntity): self._luminary = luminary self.update_static_attributes() - def update_static_attributes(self): + def update_static_attributes(self) -> None: """Update static attributes of the luminary.""" self._unique_id = self._get_unique_id() self._attr_supported_color_modes = self._get_supported_color_modes() - self._supported_features = self._get_supported_features() + self._attr_supported_features = self._get_supported_features() self._effect_list = self._get_effect_list() if ColorMode.COLOR_TEMP in self._attr_supported_color_modes: self._min_mireds = color_util.color_temperature_kelvin_to_mired( @@ -441,11 +435,11 @@ class OsramLightifyGroup(Luminary): # users. return f"{self._luminary.lights()}" - def _get_supported_features(self): + def _get_supported_features(self) -> LightEntityFeature | int: """Get list of supported features.""" features = super()._get_supported_features() if self._luminary.scenes(): - features = features | LightEntityFeature.EFFECT + features |= LightEntityFeature.EFFECT return features From dd7bc7971fb5a9ae7a4dbf624fe9ad515ec95720 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:46:42 +0100 Subject: [PATCH 0508/1033] Adjust type hints for LightEntityFeature (#82251) --- homeassistant/components/control4/light.py | 2 +- homeassistant/components/decora_wifi/light.py | 2 +- homeassistant/components/esphome/light.py | 4 ++-- homeassistant/components/homematic/light.py | 4 ++-- homeassistant/components/iaqualink/light.py | 2 +- homeassistant/components/light/__init__.py | 4 ++-- homeassistant/components/smartthings/light.py | 2 +- homeassistant/components/template/light.py | 2 +- homeassistant/components/tplink/light.py | 2 +- homeassistant/components/upb/light.py | 2 +- pylint/plugins/hass_enforce_type_hints.py | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/control4/light.py b/homeassistant/components/control4/light.py index 539547a79f1..51da8113710 100644 --- a/homeassistant/components/control4/light.py +++ b/homeassistant/components/control4/light.py @@ -192,7 +192,7 @@ class Control4Light(Control4Entity, LightEntity): return None @property - def supported_features(self) -> int: + def supported_features(self) -> LightEntityFeature | int: """Flag supported features.""" if self._is_dimmer: return LightEntityFeature.TRANSITION diff --git a/homeassistant/components/decora_wifi/light.py b/homeassistant/components/decora_wifi/light.py index 3c43e816097..820b5c8f53a 100644 --- a/homeassistant/components/decora_wifi/light.py +++ b/homeassistant/components/decora_wifi/light.py @@ -112,7 +112,7 @@ class DecoraWifiLight(LightEntity): return {self.color_mode} @property - def supported_features(self) -> int: + def supported_features(self) -> LightEntityFeature | int: """Return supported features.""" if self._switch.canSetLevel: return LightEntityFeature.TRANSITION diff --git a/homeassistant/components/esphome/light.py b/homeassistant/components/esphome/light.py index 624dfc8950f..76de857a863 100644 --- a/homeassistant/components/esphome/light.py +++ b/homeassistant/components/esphome/light.py @@ -347,9 +347,9 @@ class EsphomeLight(EsphomeEntity[LightInfo, LightState], LightEntity): return self._static_info.supported_color_modes_compat(self._api_version) @property - def supported_features(self) -> int: + def supported_features(self) -> LightEntityFeature: """Flag supported features.""" - flags: int = LightEntityFeature.FLASH + flags = LightEntityFeature.FLASH # All color modes except UNKNOWN,ON_OFF support transition modes = self._native_supported_color_modes diff --git a/homeassistant/components/homematic/light.py b/homeassistant/components/homematic/light.py index 38a75266a17..87f3dfb314a 100644 --- a/homeassistant/components/homematic/light.py +++ b/homeassistant/components/homematic/light.py @@ -82,9 +82,9 @@ class HMLight(HMDevice, LightEntity): return color_modes @property - def supported_features(self) -> int: + def supported_features(self) -> LightEntityFeature: """Flag supported features.""" - features: int = LightEntityFeature.TRANSITION + features = LightEntityFeature.TRANSITION if "PROGRAM" in self._hmdevice.WRITENODE: features |= LightEntityFeature.EFFECT return features diff --git a/homeassistant/components/iaqualink/light.py b/homeassistant/components/iaqualink/light.py index 91ca64a87e6..15800419ed5 100644 --- a/homeassistant/components/iaqualink/light.py +++ b/homeassistant/components/iaqualink/light.py @@ -100,7 +100,7 @@ class HassAqualinkLight(AqualinkEntity, LightEntity): return {self.color_mode} @property - def supported_features(self) -> int: + def supported_features(self) -> LightEntityFeature | int: """Return the list of features supported by the light.""" if self.dev.supports_effect: return LightEntityFeature.EFFECT diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 098b627cf48..80110dbd006 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -793,7 +793,7 @@ class LightEntity(ToggleEntity): _attr_rgbw_color: tuple[int, int, int, int] | None = None _attr_rgbww_color: tuple[int, int, int, int, int] | None = None _attr_supported_color_modes: set[ColorMode] | set[str] | None = None - _attr_supported_features: int = 0 + _attr_supported_features: LightEntityFeature | int = 0 _attr_xy_color: tuple[float, float] | None = None @property @@ -1060,6 +1060,6 @@ class LightEntity(ToggleEntity): return self._attr_supported_color_modes @property - def supported_features(self) -> int: + def supported_features(self) -> LightEntityFeature | int: """Flag supported features.""" return self._attr_supported_features diff --git a/homeassistant/components/smartthings/light.py b/homeassistant/components/smartthings/light.py index ccf63582e86..514296d156a 100644 --- a/homeassistant/components/smartthings/light.py +++ b/homeassistant/components/smartthings/light.py @@ -101,7 +101,7 @@ class SmartThingsLight(SmartThingsEntity, LightEntity): return color_modes - def _determine_features(self): + def _determine_features(self) -> LightEntityFeature | int: """Get features supported by the device.""" features = 0 # Transition diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index db1c89921d1..8e0027b323f 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -254,7 +254,7 @@ class LightTemplate(TemplateEntity, LightEntity): return self._supported_color_modes @property - def supported_features(self) -> int: + def supported_features(self) -> LightEntityFeature | int: """Flag supported features.""" supported_features = 0 if self._effect_script is not None: diff --git a/homeassistant/components/tplink/light.py b/homeassistant/components/tplink/light.py index f946629813e..65967eb6bea 100644 --- a/homeassistant/components/tplink/light.py +++ b/homeassistant/components/tplink/light.py @@ -312,7 +312,7 @@ class TPLinkSmartLightStrip(TPLinkSmartBulb): device: SmartLightStrip @property - def supported_features(self) -> int: + def supported_features(self) -> LightEntityFeature | int: """Flag supported features.""" return super().supported_features | LightEntityFeature.EFFECT diff --git a/homeassistant/components/upb/light.py b/homeassistant/components/upb/light.py index 85cda1f0061..47680714d19 100644 --- a/homeassistant/components/upb/light.py +++ b/homeassistant/components/upb/light.py @@ -71,7 +71,7 @@ class UpbLight(UpbAttachedEntity, LightEntity): return {self.color_mode} @property - def supported_features(self) -> int: + def supported_features(self) -> LightEntityFeature: """Flag supported features.""" if self._element.dimmable: return LightEntityFeature.TRANSITION | LightEntityFeature.FLASH diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 3da5a537435..2d29ea2fb1a 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1520,7 +1520,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type="int", + return_type=["LightEntityFeature", "int"], ), TypeHintMatch( function_name="turn_on", From 4986f65dca980622866242e81f6a637cbcc0123b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:47:08 +0100 Subject: [PATCH 0509/1033] Adjust supported_features defaults in lights (#82252) --- homeassistant/components/abode/light.py | 5 ----- homeassistant/components/demo/light.py | 1 - homeassistant/components/zwave_js/light.py | 1 - 3 files changed, 7 deletions(-) diff --git a/homeassistant/components/abode/light.py b/homeassistant/components/abode/light.py index 030e5744ce8..b930c3d654b 100644 --- a/homeassistant/components/abode/light.py +++ b/homeassistant/components/abode/light.py @@ -116,8 +116,3 @@ class AbodeLight(AbodeDevice, LightEntity): if self._device.is_dimmable: return {ColorMode.BRIGHTNESS} return {ColorMode.ONOFF} - - @property - def supported_features(self) -> int: - """Flag supported features.""" - return 0 diff --git a/homeassistant/components/demo/light.py b/homeassistant/components/demo/light.py index c990e4ff8e0..2e5291b8a13 100644 --- a/homeassistant/components/demo/light.py +++ b/homeassistant/components/demo/light.py @@ -129,7 +129,6 @@ class DemoLight(LightEntity): self._ct = ct or random.choice(LIGHT_TEMPS) self._effect = effect self._effect_list = effect_list - self._attr_supported_features = 0 self._hs_color = hs_color self._attr_name = name self._rgbw_color = rgbw_color diff --git a/homeassistant/components/zwave_js/light.py b/homeassistant/components/zwave_js/light.py index 4c8fe2a3986..6b2fd2c8dbb 100644 --- a/homeassistant/components/zwave_js/light.py +++ b/homeassistant/components/zwave_js/light.py @@ -150,7 +150,6 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity): self._supported_color_modes.add(ColorMode.BRIGHTNESS) # Entity class attributes - self._attr_supported_features = 0 self.supports_brightness_transition = bool( self._target_brightness is not None and TRANSITION_DURATION_OPTION From 8570d3aabeb0bde1345659087849acd9de946ce5 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:47:48 +0100 Subject: [PATCH 0510/1033] Adjust type hints for LockEntityFeature (#82256) --- homeassistant/components/demo/lock.py | 1 - homeassistant/components/esphome/lock.py | 2 +- homeassistant/components/lock/__init__.py | 6 ++++++ pylint/plugins/hass_enforce_type_hints.py | 4 ++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/demo/lock.py b/homeassistant/components/demo/lock.py index 546161b2d6f..47187d5ffc6 100644 --- a/homeassistant/components/demo/lock.py +++ b/homeassistant/components/demo/lock.py @@ -60,7 +60,6 @@ class DemoLock(LockEntity): ) -> None: """Initialize the lock.""" self._attr_name = name - self._attr_supported_features = 0 if openable: self._attr_supported_features = LockEntityFeature.OPEN self._state = state diff --git a/homeassistant/components/esphome/lock.py b/homeassistant/components/esphome/lock.py index bcfa0131518..12666971d71 100644 --- a/homeassistant/components/esphome/lock.py +++ b/homeassistant/components/esphome/lock.py @@ -38,7 +38,7 @@ class EsphomeLock(EsphomeEntity[LockInfo, LockEntityState], LockEntity): return self._static_info.assumed_state @property - def supported_features(self) -> int: + def supported_features(self) -> LockEntityFeature | int: """Flag supported features.""" return LockEntityFeature.OPEN if self._static_info.supports_open else 0 diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index a8166e9e3bd..412e314da0f 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -112,6 +112,7 @@ class LockEntity(Entity): _attr_is_unlocking: bool | None = None _attr_is_jammed: bool | None = None _attr_state: None = None + _attr_supported_features: LockEntityFeature | int = 0 @property def changed_by(self) -> str | None: @@ -190,3 +191,8 @@ class LockEntity(Entity): if (locked := self.is_locked) is None: return None return STATE_LOCKED if locked else STATE_UNLOCKED + + @property + def supported_features(self) -> LockEntityFeature | int: + """Return the list of supported features.""" + return self._attr_supported_features diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 2d29ea2fb1a..52d14562dec 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1581,6 +1581,10 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { function_name="is_jammed", return_type=["bool", None], ), + TypeHintMatch( + function_name="supported_features", + return_type=["LockEntityFeature", "int"], + ), TypeHintMatch( function_name="lock", kwargs_type="Any", From ae181a47fd284095c64251970ba717b56cd6a9fe Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:52:17 +0100 Subject: [PATCH 0511/1033] Show full error on cache delete failure [ci] (#82259) --- .github/workflows/cache.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cache.yml b/.github/workflows/cache.yml index 8a1bcad6570..077908c7b42 100644 --- a/.github/workflows/cache.yml +++ b/.github/workflows/cache.yml @@ -41,7 +41,7 @@ jobs: echo "$targets" for id in $(echo $targets | jq '.id'); do - curl -fsS \ + curl -sS --fail-with-body \ -X DELETE \ -H "Accept: application/vnd.github+json" \ -H "Authorization: token ${{ github.token }}" \ From 44b28d67f1a21c17f3724af25adcfa687815db24 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 13:03:50 +0100 Subject: [PATCH 0512/1033] Use _attr_supported_features in nest climate (#82217) --- homeassistant/components/nest/legacy/climate.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/nest/legacy/climate.py b/homeassistant/components/nest/legacy/climate.py index 13728585e39..de6bd0e3b26 100644 --- a/homeassistant/components/nest/legacy/climate.py +++ b/homeassistant/components/nest/legacy/climate.py @@ -97,7 +97,7 @@ class NestThermostat(ClimateEntity): self._fan_modes = [FAN_ON, FAN_AUTO] # Set the default supported features - self._support_flags = ( + self._attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) @@ -106,7 +106,9 @@ class NestThermostat(ClimateEntity): if self.device.can_heat and self.device.can_cool: self._operation_list.append(HVACMode.AUTO) - self._support_flags |= ClimateEntityFeature.TARGET_TEMPERATURE_RANGE + self._attr_supported_features |= ( + ClimateEntityFeature.TARGET_TEMPERATURE_RANGE + ) # Add supported nest thermostat features if self.device.can_heat: @@ -120,7 +122,7 @@ class NestThermostat(ClimateEntity): # feature of device self._has_fan = self.device.has_fan if self._has_fan: - self._support_flags |= ClimateEntityFeature.FAN_MODE + self._attr_supported_features |= ClimateEntityFeature.FAN_MODE # data attributes self._away = None @@ -150,11 +152,6 @@ class NestThermostat(ClimateEntity): async_dispatcher_connect(self.hass, SIGNAL_NEST_UPDATE, async_update_state) ) - @property - def supported_features(self): - """Return the list of supported features.""" - return self._support_flags - @property def unique_id(self): """Return unique ID for this device.""" From 569e52c9acf984be507b4d422c677a626d9eb200 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 17 Nov 2022 13:23:42 +0100 Subject: [PATCH 0513/1033] Show full error on cache delete failure (2) [ci] (#82263) --- .github/workflows/cache.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cache.yml b/.github/workflows/cache.yml index 077908c7b42..ca80d54ad6a 100644 --- a/.github/workflows/cache.yml +++ b/.github/workflows/cache.yml @@ -41,7 +41,7 @@ jobs: echo "$targets" for id in $(echo $targets | jq '.id'); do - curl -sS --fail-with-body \ + curl -sS \ -X DELETE \ -H "Accept: application/vnd.github+json" \ -H "Authorization: token ${{ github.token }}" \ From 18e30e7c06cb1d658c61b324ad80f232f36b7b6f Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 13:58:34 +0100 Subject: [PATCH 0514/1033] Adjust type hints for MediaPlayerEntityFeature (#82258) --- homeassistant/components/bluesound/media_player.py | 2 +- homeassistant/components/cast/media_player.py | 2 +- homeassistant/components/denon/media_player.py | 2 +- homeassistant/components/denonavr/media_player.py | 2 +- homeassistant/components/directv/media_player.py | 2 +- homeassistant/components/dlna_dmr/media_player.py | 2 +- homeassistant/components/dunehd/media_player.py | 4 ++-- homeassistant/components/emby/media_player.py | 2 +- homeassistant/components/esphome/media_player.py | 2 +- homeassistant/components/forked_daapd/media_player.py | 5 +++-- homeassistant/components/hdmi_cec/media_player.py | 2 +- homeassistant/components/homekit_controller/media_player.py | 2 +- homeassistant/components/jellyfin/media_player.py | 2 +- homeassistant/components/lg_netcast/media_player.py | 2 +- homeassistant/components/lookin/media_player.py | 1 - homeassistant/components/media_player/__init__.py | 4 ++-- homeassistant/components/mpd/media_player.py | 2 +- homeassistant/components/onkyo/media_player.py | 2 +- homeassistant/components/philips_js/media_player.py | 2 +- homeassistant/components/plex/media_player.py | 2 +- homeassistant/components/universal/media_player.py | 6 ++++-- homeassistant/components/webostv/media_player.py | 4 ++-- homeassistant/components/xbox/media_player.py | 2 +- homeassistant/components/yamaha/media_player.py | 2 +- homeassistant/components/yamaha_musiccast/media_player.py | 2 +- pylint/plugins/hass_enforce_type_hints.py | 4 ++++ 26 files changed, 36 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/bluesound/media_player.py b/homeassistant/components/bluesound/media_player.py index 833050ba089..f21276abbbe 100644 --- a/homeassistant/components/bluesound/media_player.py +++ b/homeassistant/components/bluesound/media_player.py @@ -775,7 +775,7 @@ class BluesoundPlayer(MediaPlayerEntity): return None @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature | int: """Flag of media commands that are supported.""" if self._status is None: return 0 diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index edd8e0331d9..786a530e36c 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -899,7 +899,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity): return self._chromecast.app_display_name if self._chromecast else None @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" support = ( MediaPlayerEntityFeature.PLAY_MEDIA diff --git a/homeassistant/components/denon/media_player.py b/homeassistant/components/denon/media_player.py index 8c71dd46c3e..c1f864c8c2f 100644 --- a/homeassistant/components/denon/media_player.py +++ b/homeassistant/components/denon/media_player.py @@ -245,7 +245,7 @@ class DenonDevice(MediaPlayerEntity): return self._mediainfo @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" if self._mediasource in MEDIA_MODES.values(): return SUPPORT_DENON | SUPPORT_MEDIA_MODES diff --git a/homeassistant/components/denonavr/media_player.py b/homeassistant/components/denonavr/media_player.py index cc0e0c06656..32f233d5413 100644 --- a/homeassistant/components/denonavr/media_player.py +++ b/homeassistant/components/denonavr/media_player.py @@ -277,7 +277,7 @@ class DenonDevice(MediaPlayerEntity): return self._receiver.sound_mode @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" if self._receiver.input_func in self._receiver.netaudio_func_list: return self._supported_features_base | SUPPORT_MEDIA_MODES diff --git a/homeassistant/components/directv/media_player.py b/homeassistant/components/directv/media_player.py index bc838757854..21b25962fce 100644 --- a/homeassistant/components/directv/media_player.py +++ b/homeassistant/components/directv/media_player.py @@ -237,7 +237,7 @@ class DIRECTVMediaPlayer(DIRECTVEntity, MediaPlayerEntity): return self._program.channel @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" return SUPPORT_DTV_CLIENT if self._is_client else SUPPORT_DTV diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index ff09f018639..00444cd8f1c 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -478,7 +478,7 @@ class DlnaDmrEntity(MediaPlayerEntity): return MediaPlayerState.IDLE @property - def supported_features(self) -> int: + def supported_features(self) -> MediaPlayerEntityFeature | int: """Flag media player features that are supported at this moment. Supported features may change as the device enters different states. diff --git a/homeassistant/components/dunehd/media_player.py b/homeassistant/components/dunehd/media_player.py index 4770492876d..a184b91c05e 100644 --- a/homeassistant/components/dunehd/media_player.py +++ b/homeassistant/components/dunehd/media_player.py @@ -19,7 +19,7 @@ from .const import ATTR_MANUFACTURER, DEFAULT_NAME, DOMAIN CONF_SOURCES: Final = "sources" -DUNEHD_PLAYER_SUPPORT: Final[int] = ( +DUNEHD_PLAYER_SUPPORT: Final[MediaPlayerEntityFeature] = ( MediaPlayerEntityFeature.PAUSE | MediaPlayerEntityFeature.TURN_ON | MediaPlayerEntityFeature.TURN_OFF @@ -105,7 +105,7 @@ class DuneHDPlayerEntity(MediaPlayerEntity): return int(self._state.get("playback_mute", 0)) == 1 @property - def supported_features(self) -> int: + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" return DUNEHD_PLAYER_SUPPORT diff --git a/homeassistant/components/emby/media_player.py b/homeassistant/components/emby/media_player.py index b573aef65ea..c03ae088a83 100644 --- a/homeassistant/components/emby/media_player.py +++ b/homeassistant/components/emby/media_player.py @@ -284,7 +284,7 @@ class EmbyDevice(MediaPlayerEntity): return self.device.media_album_artist @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature | int: """Flag media player features that are supported.""" if self.supports_remote_control: return SUPPORT_EMBY diff --git a/homeassistant/components/esphome/media_player.py b/homeassistant/components/esphome/media_player.py index d7a70737690..7f90f4e27d8 100644 --- a/homeassistant/components/esphome/media_player.py +++ b/homeassistant/components/esphome/media_player.py @@ -83,7 +83,7 @@ class EsphomeMediaPlayer( return self._state.volume @property - def supported_features(self) -> int: + def supported_features(self) -> MediaPlayerEntityFeature: """Flag supported features.""" flags = ( MediaPlayerEntityFeature.PLAY_MEDIA diff --git a/homeassistant/components/forked_daapd/media_player.py b/homeassistant/components/forked_daapd/media_player.py index 9da1c1a1168..1562a68b696 100644 --- a/homeassistant/components/forked_daapd/media_player.py +++ b/homeassistant/components/forked_daapd/media_player.py @@ -17,6 +17,7 @@ from homeassistant.components.media_player import ( BrowseMedia, MediaPlayerEnqueue, MediaPlayerEntity, + MediaPlayerEntityFeature, MediaPlayerState, MediaType, async_process_play_media_url, @@ -236,7 +237,7 @@ class ForkedDaapdZone(MediaPlayerEntity): await self._api.set_volume(volume=volume * 100, output_id=self._output_id) @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" return SUPPORTED_FEATURES_ZONE @@ -558,7 +559,7 @@ class ForkedDaapdMaster(MediaPlayerEntity): return self._player["shuffle"] @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" return SUPPORTED_FEATURES diff --git a/homeassistant/components/hdmi_cec/media_player.py b/homeassistant/components/hdmi_cec/media_player.py index cfe73ff7c40..25019ec6933 100644 --- a/homeassistant/components/hdmi_cec/media_player.py +++ b/homeassistant/components/hdmi_cec/media_player.py @@ -163,7 +163,7 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity): _LOGGER.warning("Unknown state: %s", device.status) @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" if self.type_id == TYPE_RECORDER or self.type == TYPE_PLAYBACK: return ( diff --git a/homeassistant/components/homekit_controller/media_player.py b/homeassistant/components/homekit_controller/media_player.py index 4efa7dbce1c..04fad045cb5 100644 --- a/homeassistant/components/homekit_controller/media_player.py +++ b/homeassistant/components/homekit_controller/media_player.py @@ -80,7 +80,7 @@ class HomeKitTelevision(HomeKitEntity, MediaPlayerEntity): ] @property - def supported_features(self) -> int: + def supported_features(self) -> MediaPlayerEntityFeature | int: """Flag media player features that are supported.""" features = 0 diff --git a/homeassistant/components/jellyfin/media_player.py b/homeassistant/components/jellyfin/media_player.py index 36fb65916d2..a05aada2152 100644 --- a/homeassistant/components/jellyfin/media_player.py +++ b/homeassistant/components/jellyfin/media_player.py @@ -198,7 +198,7 @@ class JellyfinMediaPlayer(JellyfinEntity, MediaPlayerEntity): return get_artwork_url(self.coordinator.api_client, self.now_playing, 150) @property - def supported_features(self) -> int: + def supported_features(self) -> MediaPlayerEntityFeature | int: """Flag media player features that are supported.""" commands: list[str] = self.capabilities.get("SupportedCommands", []) controllable = self.capabilities.get("SupportsMediaControl", False) diff --git a/homeassistant/components/lg_netcast/media_player.py b/homeassistant/components/lg_netcast/media_player.py index 6f3508e22eb..47cbebf939b 100644 --- a/homeassistant/components/lg_netcast/media_player.py +++ b/homeassistant/components/lg_netcast/media_player.py @@ -197,7 +197,7 @@ class LgTVDevice(MediaPlayerEntity): return self._program_name @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" if self._on_action_script: return SUPPORT_LGTV | MediaPlayerEntityFeature.TURN_ON diff --git a/homeassistant/components/lookin/media_player.py b/homeassistant/components/lookin/media_player.py index f0e9c7e5928..9e925836e11 100644 --- a/homeassistant/components/lookin/media_player.py +++ b/homeassistant/components/lookin/media_player.py @@ -82,7 +82,6 @@ class LookinMedia(LookinPowerPushRemoteEntity, MediaPlayerEntity): ) -> None: """Init the lookin media player.""" self._attr_device_class = device_class - self._attr_supported_features: int = 0 super().__init__(coordinator, uuid, device, lookin_data) for function_name, feature in _FUNCTION_NAME_TO_FEATURE.items(): if function_name in self._function_names: diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index c43c9980c19..0e17d5c5339 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -492,7 +492,7 @@ class MediaPlayerEntity(Entity): _attr_source_list: list[str] | None = None _attr_source: str | None = None _attr_state: MediaPlayerState | str | None = None - _attr_supported_features: int = 0 + _attr_supported_features: MediaPlayerEntityFeature | int = 0 _attr_volume_level: float | None = None # Implement these for your media player @@ -692,7 +692,7 @@ class MediaPlayerEntity(Entity): return self._attr_group_members @property - def supported_features(self) -> int: + def supported_features(self) -> MediaPlayerEntityFeature | int: """Flag media player features that are supported.""" return self._attr_supported_features diff --git a/homeassistant/components/mpd/media_player.py b/homeassistant/components/mpd/media_player.py index 44d94bc649f..d54b9702686 100644 --- a/homeassistant/components/mpd/media_player.py +++ b/homeassistant/components/mpd/media_player.py @@ -338,7 +338,7 @@ class MpdDevice(MediaPlayerEntity): return None @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature | int: """Flag media player features that are supported.""" if self._status is None: return 0 diff --git a/homeassistant/components/onkyo/media_player.py b/homeassistant/components/onkyo/media_player.py index c1d242c840c..11af0ea4013 100644 --- a/homeassistant/components/onkyo/media_player.py +++ b/homeassistant/components/onkyo/media_player.py @@ -553,7 +553,7 @@ class OnkyoDeviceZone(OnkyoDevice): ) @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Return media player features that are supported.""" if self._supports_volume: return SUPPORT_ONKYO diff --git a/homeassistant/components/philips_js/media_player.py b/homeassistant/components/philips_js/media_player.py index 116833d8a97..e1ceddd4bda 100644 --- a/homeassistant/components/philips_js/media_player.py +++ b/homeassistant/components/philips_js/media_player.py @@ -104,7 +104,7 @@ class PhilipsTVMediaPlayer( await self.coordinator.async_request_refresh() @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" supports = self._supports if self.coordinator.turn_on or ( diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index 84e0f084210..b43c4dc0e21 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -389,7 +389,7 @@ class PlexMediaPlayer(MediaPlayerEntity): return self.session.media_episode @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" if self.device and "playback" in self._device_protocol_capabilities: return ( diff --git a/homeassistant/components/universal/media_player.py b/homeassistant/components/universal/media_player.py index d26b062de40..450a0f6f3bf 100644 --- a/homeassistant/components/universal/media_player.py +++ b/homeassistant/components/universal/media_player.py @@ -453,9 +453,11 @@ class UniversalMediaPlayer(MediaPlayerEntity): return self._override_or_child_attr(ATTR_MEDIA_SHUFFLE) @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature | int: """Flag media player features that are supported.""" - flags = self._child_attr(ATTR_SUPPORTED_FEATURES) or 0 + flags: MediaPlayerEntityFeature | int = ( + self._child_attr(ATTR_SUPPORTED_FEATURES) or 0 + ) if SERVICE_TURN_ON in self._cmds: flags |= MediaPlayerEntityFeature.TURN_ON diff --git a/homeassistant/components/webostv/media_player.py b/homeassistant/components/webostv/media_player.py index 10fed607ee8..41432d65489 100644 --- a/homeassistant/components/webostv/media_player.py +++ b/homeassistant/components/webostv/media_player.py @@ -137,7 +137,7 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity): self._current_source = None self._source_list: dict = {} - self._supported_features: int = 0 + self._supported_features: MediaPlayerEntityFeature | int = 0 self._update_states() async def async_added_to_hass(self) -> None: @@ -314,7 +314,7 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity): await self._client.connect() @property - def supported_features(self) -> int: + def supported_features(self) -> MediaPlayerEntityFeature | int: """Flag media player features that are supported.""" if self._wrapper.turn_on: return self._supported_features | MediaPlayerEntityFeature.TURN_ON diff --git a/homeassistant/components/xbox/media_player.py b/homeassistant/components/xbox/media_player.py index 5b9dcd77f2b..71ec382f04a 100644 --- a/homeassistant/components/xbox/media_player.py +++ b/homeassistant/components/xbox/media_player.py @@ -107,7 +107,7 @@ class XboxMediaPlayer(CoordinatorEntity[XboxUpdateCoordinator], MediaPlayerEntit return XBOX_STATE_MAP[status.power_state] @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" if self.state not in [MediaPlayerState.PLAYING, MediaPlayerState.PAUSED]: return ( diff --git a/homeassistant/components/yamaha/media_player.py b/homeassistant/components/yamaha/media_player.py index 7d98ae5d62a..3f0b331c648 100644 --- a/homeassistant/components/yamaha/media_player.py +++ b/homeassistant/components/yamaha/media_player.py @@ -311,7 +311,7 @@ class YamahaDevice(MediaPlayerEntity): return f"{self.receiver.ctrl_url}:{self._zone}" @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" supported_features = SUPPORT_YAMAHA diff --git a/homeassistant/components/yamaha_musiccast/media_player.py b/homeassistant/components/yamaha_musiccast/media_player.py index 504c56d73ec..9eb1be3c0fe 100644 --- a/homeassistant/components/yamaha_musiccast/media_player.py +++ b/homeassistant/components/yamaha_musiccast/media_player.py @@ -424,7 +424,7 @@ class MusicCastMediaPlayer(MusicCastDeviceEntity, MediaPlayerEntity): ) @property - def supported_features(self): + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" supported_features = MUSIC_PLAYER_BASE_SUPPORT zone = self.coordinator.data.zones[self._zone_id] diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 52d14562dec..e5cbcca09b2 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1788,6 +1788,10 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { function_name="group_members", return_type=["list[str]", None], ), + TypeHintMatch( + function_name="supported_features", + return_type=["MediaPlayerEntityFeature", "int"], + ), TypeHintMatch( function_name="turn_on", return_type=None, From b6586d5c34bf7ea5c30fbb1b62c438078ea14f39 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 17 Nov 2022 14:00:28 +0100 Subject: [PATCH 0515/1033] Align number and sensor device classes (#81909) * Align number and sensor device classes * Add tests * Tweak tests --- homeassistant/components/number/__init__.py | 243 +++++++++++++++++++- homeassistant/components/sensor/__init__.py | 46 ++-- tests/components/number/test_init.py | 18 ++ tests/components/sensor/test_init.py | 9 + 4 files changed, 293 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/number/__init__.py b/homeassistant/components/number/__init__.py index 0012a4b77ff..9c828d1a32f 100644 --- a/homeassistant/components/number/__init__.py +++ b/homeassistant/components/number/__init__.py @@ -55,8 +55,249 @@ _LOGGER = logging.getLogger(__name__) class NumberDeviceClass(StrEnum): """Device class for numbers.""" - # temperature (C/F) + # NumberDeviceClass should be aligned with SensorDeviceClass + + APPARENT_POWER = "apparent_power" + """Apparent power. + + Unit of measurement: `VA` + """ + + AQI = "aqi" + """Air Quality Index. + + Unit of measurement: `None` + """ + + BATTERY = "battery" + """Percentage of battery that is left. + + Unit of measurement: `%` + """ + + CO = "carbon_monoxide" + """Carbon Monoxide gas concentration. + + Unit of measurement: `ppm` (parts per million) + """ + + CO2 = "carbon_dioxide" + """Carbon Dioxide gas concentration. + + Unit of measurement: `ppm` (parts per million) + """ + + CURRENT = "current" + """Current. + + Unit of measurement: `A` + """ + + DISTANCE = "distance" + """Generic distance. + + Unit of measurement: `LENGTH_*` units + - SI /metric: `mm`, `cm`, `m`, `km` + - USCS / imperial: `in`, `ft`, `yd`, `mi` + """ + + ENERGY = "energy" + """Energy. + + Unit of measurement: `Wh`, `kWh`, `MWh`, `GJ` + """ + + FREQUENCY = "frequency" + """Frequency. + + Unit of measurement: `Hz`, `kHz`, `MHz`, `GHz` + """ + + GAS = "gas" + """Gas. + + Unit of measurement: `m³`, `ft³` + """ + + HUMIDITY = "humidity" + """Relative humidity. + + Unit of measurement: `%` + """ + + ILLUMINANCE = "illuminance" + """Illuminance. + + Unit of measurement: `lx`, `lm` + """ + + MOISTURE = "moisture" + """Moisture. + + Unit of measurement: `%` + """ + + MONETARY = "monetary" + """Amount of money. + + Unit of measurement: ISO4217 currency code + + See https://en.wikipedia.org/wiki/ISO_4217#Active_codes for active codes + """ + + NITROGEN_DIOXIDE = "nitrogen_dioxide" + """Amount of NO2. + + Unit of measurement: `µg/m³` + """ + + NITROGEN_MONOXIDE = "nitrogen_monoxide" + """Amount of NO. + + Unit of measurement: `µg/m³` + """ + + NITROUS_OXIDE = "nitrous_oxide" + """Amount of N2O. + + Unit of measurement: `µg/m³` + """ + + OZONE = "ozone" + """Amount of O3. + + Unit of measurement: `µg/m³` + """ + + PM1 = "pm1" + """Particulate matter <= 0.1 μm. + + Unit of measurement: `µg/m³` + """ + + PM10 = "pm10" + """Particulate matter <= 10 μm. + + Unit of measurement: `µg/m³` + """ + + PM25 = "pm25" + """Particulate matter <= 2.5 μm. + + Unit of measurement: `µg/m³` + """ + + POWER_FACTOR = "power_factor" + """Power factor. + + Unit of measurement: `%` + """ + + POWER = "power" + """Power. + + Unit of measurement: `W`, `kW` + """ + + PRECIPITATION_INTENSITY = "precipitation_intensity" + """Precipitation intensity. + + Unit of measurement: UnitOfVolumetricFlux + - SI /metric: `mm/d`, `mm/h` + - USCS / imperial: `in/d`, `in/h` + """ + + PRESSURE = "pressure" + """Pressure. + + Unit of measurement: + - `mbar`, `cbar`, `bar` + - `Pa`, `hPa`, `kPa` + - `inHg` + - `psi` + """ + + REACTIVE_POWER = "reactive_power" + """Reactive power. + + Unit of measurement: `var` + """ + + SIGNAL_STRENGTH = "signal_strength" + """Signal strength. + + Unit of measurement: `dB`, `dBm` + """ + + SPEED = "speed" + """Generic speed. + + Unit of measurement: `SPEED_*` units or `UnitOfVolumetricFlux` + - SI /metric: `mm/d`, `mm/h`, `m/s`, `km/h` + - USCS / imperial: `in/d`, `in/h`, `ft/s`, `mph` + - Nautical: `kn` + """ + + SULPHUR_DIOXIDE = "sulphur_dioxide" + """Amount of SO2. + + Unit of measurement: `µg/m³` + """ + TEMPERATURE = "temperature" + """Temperature. + + Unit of measurement: `°C`, `°F` + """ + + VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds" + """Amount of VOC. + + Unit of measurement: `µg/m³` + """ + + VOLTAGE = "voltage" + """Voltage. + + Unit of measurement: `V` + """ + + VOLUME = "volume" + """Generic volume. + + Unit of measurement: `VOLUME_*` units + - SI / metric: `mL`, `L`, `m³` + - USCS / imperial: `fl. oz.`, `ft³`, `gal` (warning: volumes expressed in + USCS/imperial units are currently assumed to be US volumes) + """ + + WATER = "water" + """Water. + + Unit of measurement: + - SI / metric: `m³`, `L` + - USCS / imperial: `ft³`, `gal` (warning: volumes expressed in + USCS/imperial units are currently assumed to be US volumes) + """ + + WEIGHT = "weight" + """Generic weight, represents a measurement of an object's mass. + + Weight is used instead of mass to fit with every day language. + + Unit of measurement: `MASS_*` units + - SI / metric: `µg`, `mg`, `g`, `kg` + - USCS / imperial: `oz`, `lb` + """ + + WIND_SPEED = "wind_speed" + """Wind speed. + + Unit of measurement: `SPEED_*` units + - SI /metric: `m/s`, `km/h` + - USCS / imperial: `ft/s`, `mph` + - Nautical: `kn` + """ DEVICE_CLASSES_SCHEMA: Final = vol.All(vol.Lower, vol.Coerce(NumberDeviceClass)) diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index 6ba88defc83..0382af7f9f8 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -88,6 +88,30 @@ SCAN_INTERVAL: Final = timedelta(seconds=30) class SensorDeviceClass(StrEnum): """Device class for sensors.""" + # Non-numerical device classes + DATE = "date" + """Date. + + Unit of measurement: `None` + + ISO8601 format: https://en.wikipedia.org/wiki/ISO_8601 + """ + + DURATION = "duration" + """Fixed duration. + + Unit of measurement: `d`, `h`, `min`, `s` + """ + + TIMESTAMP = "timestamp" + """Timestamp. + + Unit of measurement: `None` + + ISO8601 format: https://en.wikipedia.org/wiki/ISO_8601 + """ + + # Numerical device classes, these should be aligned with NumberDeviceClass APPARENT_POWER = "apparent_power" """Apparent power. @@ -124,14 +148,6 @@ class SensorDeviceClass(StrEnum): Unit of measurement: `A` """ - DATE = "date" - """Date. - - Unit of measurement: `None` - - ISO8601 format: https://en.wikipedia.org/wiki/ISO_8601 - """ - DISTANCE = "distance" """Generic distance. @@ -140,12 +156,6 @@ class SensorDeviceClass(StrEnum): - USCS / imperial: `in`, `ft`, `yd`, `mi` """ - DURATION = "duration" - """Fixed duration. - - Unit of measurement: `d`, `h`, `min`, `s` - """ - ENERGY = "energy" """Energy. @@ -295,14 +305,6 @@ class SensorDeviceClass(StrEnum): Unit of measurement: `°C`, `°F` """ - TIMESTAMP = "timestamp" - """Timestamp. - - Unit of measurement: `None` - - ISO8601 format: https://en.wikipedia.org/wiki/ISO_8601 - """ - VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds" """Amount of VOC. diff --git a/tests/components/number/test_init.py b/tests/components/number/test_init.py index 98b30616952..630506623de 100644 --- a/tests/components/number/test_init.py +++ b/tests/components/number/test_init.py @@ -14,6 +14,7 @@ from homeassistant.components.number import ( NumberEntity, NumberEntityDescription, ) +from homeassistant.components.sensor import SensorDeviceClass from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_UNIT_OF_MEASUREMENT, @@ -848,3 +849,20 @@ async def test_custom_unit_change( state = hass.states.get(entity0.entity_id) assert float(state.state) == pytest.approx(float(default_value)) assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == default_unit + + +def test_device_classes_aligned(): + """Make sure all sensor device classes are also available in NumberDeviceClass.""" + + non_numeric_device_classes = { + SensorDeviceClass.DATE, + SensorDeviceClass.DURATION, + SensorDeviceClass.TIMESTAMP, + } + + for device_class in SensorDeviceClass: + if device_class in non_numeric_device_classes: + continue + + assert hasattr(NumberDeviceClass, device_class.name) + assert getattr(NumberDeviceClass, device_class.name).value == device_class.value diff --git a/tests/components/sensor/test_init.py b/tests/components/sensor/test_init.py index e168a1c2271..c9c29d6c99a 100644 --- a/tests/components/sensor/test_init.py +++ b/tests/components/sensor/test_init.py @@ -5,6 +5,7 @@ from decimal import Decimal import pytest from pytest import approx +from homeassistant.components.number import NumberDeviceClass from homeassistant.components.sensor import SensorDeviceClass from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, @@ -927,3 +928,11 @@ async def test_unit_conversion_priority_legacy_conversion_removed( state = hass.states.get(entity0.entity_id) assert float(state.state) == approx(float(original_value)) assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == original_unit + + +def test_device_classes_aligned(): + """Make sure all number device classes are also available in SensorDeviceClass.""" + + for device_class in NumberDeviceClass: + assert hasattr(SensorDeviceClass, device_class.name) + assert getattr(SensorDeviceClass, device_class.name).value == device_class.value From 9b9bdc725f3b670875f5479c0ffcb61f50a9a9cb Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:06:16 +0100 Subject: [PATCH 0516/1033] Adjust type hints for RemoteEntityFeature (#82265) --- homeassistant/components/remote/__init__.py | 4 ++-- pylint/plugins/hass_enforce_type_hints.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/remote/__init__.py b/homeassistant/components/remote/__init__.py index 74535c44228..bc0711daeb5 100644 --- a/homeassistant/components/remote/__init__.py +++ b/homeassistant/components/remote/__init__.py @@ -166,10 +166,10 @@ class RemoteEntity(ToggleEntity): entity_description: RemoteEntityDescription _attr_activity_list: list[str] | None = None _attr_current_activity: str | None = None - _attr_supported_features: int = 0 + _attr_supported_features: RemoteEntityFeature | int = 0 @property - def supported_features(self) -> int: + def supported_features(self) -> RemoteEntityFeature | int: """Flag supported features.""" return self._attr_supported_features diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index e5cbcca09b2..1f372c6550d 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -2052,16 +2052,16 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { base_class="RemoteEntity", matches=[ TypeHintMatch( - function_name="supported_features", - return_type="int", + function_name="activity_list", + return_type=["list[str]", None], ), TypeHintMatch( function_name="current_activity", return_type=["str", None], ), TypeHintMatch( - function_name="activity_list", - return_type=["list[str]", None], + function_name="supported_features", + return_type=["RemoteEntityFeature", "int"], ), TypeHintMatch( function_name="send_command", From 295cd0143c6870fd19033960619e7812b81a28b4 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:06:26 +0100 Subject: [PATCH 0517/1033] Adjust type hints for SirenEntityFeature (#82266) --- homeassistant/components/mqtt/siren.py | 3 +-- homeassistant/components/siren/__init__.py | 6 ++++++ pylint/plugins/hass_enforce_type_hints.py | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/siren.py b/homeassistant/components/mqtt/siren.py index 4a69977df45..8b622d207fa 100644 --- a/homeassistant/components/mqtt/siren.py +++ b/homeassistant/components/mqtt/siren.py @@ -171,7 +171,6 @@ class MqttSiren(MqttEntity, SirenEntity): _entity_id_format = ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_SIREN_ATTRIBUTES_BLOCKED - _attr_supported_features: int _command_templates: dict[ str, Callable[[PublishPayloadType, TemplateVarsType], PublishPayloadType] | None @@ -207,7 +206,7 @@ class MqttSiren(MqttEntity, SirenEntity): self._attr_extra_state_attributes = {} - _supported_features: int = SUPPORTED_BASE + _supported_features = SUPPORTED_BASE if config[CONF_SUPPORT_DURATION]: _supported_features |= SirenEntityFeature.DURATION self._attr_extra_state_attributes[ATTR_DURATION] = None diff --git a/homeassistant/components/siren/__init__.py b/homeassistant/components/siren/__init__.py index 920865c41f3..faf8caf8ed0 100644 --- a/homeassistant/components/siren/__init__.py +++ b/homeassistant/components/siren/__init__.py @@ -163,6 +163,7 @@ class SirenEntity(ToggleEntity): entity_description: SirenEntityDescription _attr_available_tones: list[int | str] | dict[int, str] | None + _attr_supported_features: SirenEntityFeature | int = 0 @final @property @@ -190,3 +191,8 @@ class SirenEntity(ToggleEntity): if hasattr(self, "entity_description"): return self.entity_description.available_tones return None + + @property + def supported_features(self) -> SirenEntityFeature | int: + """Return the list of supported features.""" + return self._attr_supported_features diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 1f372c6550d..b9247bbca27 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -2212,6 +2212,10 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { function_name="available_tones", return_type=["dict[int, str]", "list[int | str]", None], ), + TypeHintMatch( + function_name="supported_features", + return_type=["SirenEntityFeature", "int"], + ), ], ), ], From 3be750812daddf717c8edb9e4c90412047533cb1 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:17:50 +0100 Subject: [PATCH 0518/1033] Adjust type hints for UpdateEntityFeature (#82268) --- homeassistant/components/mqtt/update.py | 2 +- homeassistant/components/update/__init__.py | 4 ++-- pylint/plugins/hass_enforce_type_hints.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/update.py b/homeassistant/components/mqtt/update.py index abad1cdb2ff..9e08f765be0 100644 --- a/homeassistant/components/mqtt/update.py +++ b/homeassistant/components/mqtt/update.py @@ -253,7 +253,7 @@ class MqttUpdate(MqttEntity, UpdateEntity, RestoreEntity): get_mqtt_data(self.hass).state_write_requests.write_state_request(self) @property - def supported_features(self) -> int: + def supported_features(self) -> UpdateEntityFeature | int: """Return the list of supported features.""" support = 0 diff --git a/homeassistant/components/update/__init__.py b/homeassistant/components/update/__init__.py index 9ec748c1ed5..92272d56c4b 100644 --- a/homeassistant/components/update/__init__.py +++ b/homeassistant/components/update/__init__.py @@ -190,7 +190,7 @@ class UpdateEntity(RestoreEntity): _attr_release_summary: str | None = None _attr_release_url: str | None = None _attr_state: None = None - _attr_supported_features: int = 0 + _attr_supported_features: UpdateEntityFeature | int = 0 _attr_title: str | None = None __skipped_version: str | None = None __in_progress: bool = False @@ -270,7 +270,7 @@ class UpdateEntity(RestoreEntity): return self._attr_release_url @property - def supported_features(self) -> int: + def supported_features(self) -> UpdateEntityFeature | int: """Flag supported features.""" return self._attr_supported_features diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index b9247bbca27..ade51cbe97d 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -2345,7 +2345,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type="int", + return_type=["UpdateEntityFeature", "int"], ), TypeHintMatch( function_name="title", From ced16da5769f2e98652d739c9ce81f5399d3d36b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:31:09 +0100 Subject: [PATCH 0519/1033] Adjust type hints for VacuumEntityFeature (#82269) --- homeassistant/components/tuya/vacuum.py | 1 - homeassistant/components/vacuum/__init__.py | 4 ++-- pylint/plugins/hass_enforce_type_hints.py | 4 ++++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tuya/vacuum.py b/homeassistant/components/tuya/vacuum.py index d17452b11c6..27fe764b1e3 100644 --- a/homeassistant/components/tuya/vacuum.py +++ b/homeassistant/components/tuya/vacuum.py @@ -83,7 +83,6 @@ class TuyaVacuumEntity(TuyaEntity, StateVacuumEntity): """Init Tuya vacuum.""" super().__init__(device, device_manager) - self._attr_supported_features = 0 self._attr_fan_speed_list = [] self._attr_supported_features |= VacuumEntityFeature.SEND_COMMAND diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py index 6a7efe4fa56..708c511a85c 100644 --- a/homeassistant/components/vacuum/__init__.py +++ b/homeassistant/components/vacuum/__init__.py @@ -180,10 +180,10 @@ class _BaseVacuum(Entity): _attr_battery_level: int | None = None _attr_fan_speed: str | None = None _attr_fan_speed_list: list[str] - _attr_supported_features: int + _attr_supported_features: VacuumEntityFeature | int = 0 @property - def supported_features(self) -> int: + def supported_features(self) -> VacuumEntityFeature | int: """Flag vacuum cleaner features that are supported.""" return self._attr_supported_features diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index ade51cbe97d..e454be1ee22 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -2394,6 +2394,10 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { function_name="fan_speed_list", return_type="list[str]", ), + TypeHintMatch( + function_name="supported_features", + return_type=["VacuumEntityFeature", "int"], + ), TypeHintMatch( function_name="stop", kwargs_type="Any", From 9d607c8bd5b40e555814cae198fa4db98339017a Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:51:55 +0100 Subject: [PATCH 0520/1033] Adjust type hints for WaterHeaterEntityFeature (#82270) --- homeassistant/components/atag/water_heater.py | 1 - .../components/econet/water_heater.py | 2 +- .../components/incomfort/water_heater.py | 5 --- .../components/water_heater/__init__.py | 7 +++- pylint/plugins/hass_enforce_type_hints.py | 42 ++++++++++--------- 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/atag/water_heater.py b/homeassistant/components/atag/water_heater.py index 009f84a72ef..0b41373356d 100644 --- a/homeassistant/components/atag/water_heater.py +++ b/homeassistant/components/atag/water_heater.py @@ -30,7 +30,6 @@ class AtagWaterHeater(AtagEntity, WaterHeaterEntity): """Representation of an ATAG water heater.""" _attr_operation_list = OPERATION_LIST - _attr_supported_features = 0 _attr_temperature_unit = TEMP_CELSIUS @property diff --git a/homeassistant/components/econet/water_heater.py b/homeassistant/components/econet/water_heater.py index 165dc49e205..c94afd8b5d7 100644 --- a/homeassistant/components/econet/water_heater.py +++ b/homeassistant/components/econet/water_heater.py @@ -106,7 +106,7 @@ class EcoNetWaterHeater(EcoNetEntity, WaterHeaterEntity): return op_list @property - def supported_features(self): + def supported_features(self) -> WaterHeaterEntityFeature: """Return the list of supported features.""" if self.water_heater.modes: if self.water_heater.supports_away: diff --git a/homeassistant/components/incomfort/water_heater.py b/homeassistant/components/incomfort/water_heater.py index 733e7d8adc2..fb16cc48f8b 100644 --- a/homeassistant/components/incomfort/water_heater.py +++ b/homeassistant/components/incomfort/water_heater.py @@ -88,11 +88,6 @@ class IncomfortWaterHeater(IncomfortEntity, WaterHeaterEntity): """Return the unit of measurement.""" return TEMP_CELSIUS - @property - def supported_features(self) -> int: - """Return the list of supported features.""" - return 0 - @property def current_operation(self) -> str: """Return the current operation mode.""" diff --git a/homeassistant/components/water_heater/__init__.py b/homeassistant/components/water_heater/__init__.py index ed12a1a6cf9..187c643638b 100644 --- a/homeassistant/components/water_heater/__init__.py +++ b/homeassistant/components/water_heater/__init__.py @@ -167,7 +167,7 @@ class WaterHeaterEntity(Entity): _attr_operation_list: list[str] | None = None _attr_precision: float _attr_state: None = None - _attr_supported_features: int + _attr_supported_features: WaterHeaterEntityFeature | int = 0 _attr_target_temperature_high: float | None = None _attr_target_temperature_low: float | None = None _attr_target_temperature: float | None = None @@ -341,6 +341,11 @@ class WaterHeaterEntity(Entity): DEFAULT_MAX_TEMP, TEMP_FAHRENHEIT, self.temperature_unit ) + @property + def supported_features(self) -> WaterHeaterEntityFeature | int: + """Return the list of supported features.""" + return self._attr_supported_features + async def async_service_away_mode( entity: WaterHeaterEntity, service: ServiceCall diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index e454be1ee22..bbae733a536 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -2510,24 +2510,36 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { base_class="WaterHeaterEntity", matches=[ TypeHintMatch( - function_name="precision", + function_name="current_operation", + return_type=["str", None], + ), + TypeHintMatch( + function_name="current_temperature", + return_type=["float", None], + ), + TypeHintMatch( + function_name="is_away_mode_on", + return_type=["bool", None], + ), + TypeHintMatch( + function_name="max_temp", return_type="float", ), TypeHintMatch( - function_name="temperature_unit", - return_type="str", - ), - TypeHintMatch( - function_name="current_operation", - return_type=["str", None], + function_name="min_temp", + return_type="float", ), TypeHintMatch( function_name="operation_list", return_type=["list[str]", None], ), TypeHintMatch( - function_name="current_temperature", - return_type=["float", None], + function_name="precision", + return_type="float", + ), + TypeHintMatch( + function_name="supported_features", + return_type=["WaterHeaterEntityFeature", "int"], ), TypeHintMatch( function_name="target_temperature", @@ -2542,8 +2554,8 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { return_type=["float", None], ), TypeHintMatch( - function_name="is_away_mode_on", - return_type=["bool", None], + function_name="temperature_unit", + return_type="str", ), TypeHintMatch( function_name="set_temperature", @@ -2567,14 +2579,6 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { return_type=None, has_async_counterpart=True, ), - TypeHintMatch( - function_name="min_temp", - return_type="float", - ), - TypeHintMatch( - function_name="max_temp", - return_type="float", - ), ], ), ], From 8792d664e7b8e7fe6ddf65e1430f8cab17f22cca Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 17 Nov 2022 08:57:43 -0600 Subject: [PATCH 0521/1033] Add websocket endpoints to control integration logging (#65158) Co-authored-by: Erik Montnemery Co-authored-by: Erik --- .strict-typing | 1 + homeassistant/components/logger/__init__.py | 106 ++++----- homeassistant/components/logger/const.py | 42 ++++ homeassistant/components/logger/helpers.py | 217 ++++++++++++++++++ .../components/logger/websocket_api.py | 104 +++++++++ mypy.ini | 10 + tests/components/logger/conftest.py | 12 + tests/components/logger/test_init.py | 166 +++++++++++++- tests/components/logger/test_websocket_api.py | 195 ++++++++++++++++ 9 files changed, 784 insertions(+), 69 deletions(-) create mode 100644 homeassistant/components/logger/const.py create mode 100644 homeassistant/components/logger/helpers.py create mode 100644 homeassistant/components/logger/websocket_api.py create mode 100644 tests/components/logger/conftest.py create mode 100644 tests/components/logger/test_websocket_api.py diff --git a/.strict-typing b/.strict-typing index 4778893bfc2..08a167855d1 100644 --- a/.strict-typing +++ b/.strict-typing @@ -174,6 +174,7 @@ homeassistant.components.litterrobot.* homeassistant.components.local_ip.* homeassistant.components.lock.* homeassistant.components.logbook.* +homeassistant.components.logger.* homeassistant.components.lookin.* homeassistant.components.luftdaten.* homeassistant.components.mailbox.* diff --git a/homeassistant/components/logger/__init__.py b/homeassistant/components/logger/__init__.py index 5fc999d7d11..0d087ef23b7 100644 --- a/homeassistant/components/logger/__init__.py +++ b/homeassistant/components/logger/__init__.py @@ -1,5 +1,8 @@ """Support for setting the level of logging for components.""" +from __future__ import annotations + import logging +import re import voluptuous as vol @@ -7,29 +10,26 @@ from homeassistant.core import HomeAssistant, ServiceCall, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType -DOMAIN = "logger" +from . import websocket_api +from .const import ( + ATTR_LEVEL, + DEFAULT_LOGSEVERITY, + DOMAIN, + LOGGER_DEFAULT, + LOGGER_FILTERS, + LOGGER_LOGS, + LOGSEVERITY, + SERVICE_SET_DEFAULT_LEVEL, + SERVICE_SET_LEVEL, +) +from .helpers import ( + LoggerDomainConfig, + LoggerSettings, + set_default_log_level, + set_log_levels, +) -SERVICE_SET_DEFAULT_LEVEL = "set_default_level" -SERVICE_SET_LEVEL = "set_level" - -LOGSEVERITY = { - "CRITICAL": 50, - "FATAL": 50, - "ERROR": 40, - "WARNING": 30, - "WARN": 30, - "INFO": 20, - "DEBUG": 10, - "NOTSET": 0, -} - -LOGGER_DEFAULT = "default" -LOGGER_LOGS = "logs" -LOGGER_FILTERS = "filters" - -ATTR_LEVEL = "level" - -_VALID_LOG_LEVEL = vol.All(vol.Upper, vol.In(LOGSEVERITY)) +_VALID_LOG_LEVEL = vol.All(vol.Upper, vol.In(LOGSEVERITY), LOGSEVERITY.__getitem__) SERVICE_SET_DEFAULT_LEVEL_SCHEMA = vol.Schema({ATTR_LEVEL: _VALID_LOG_LEVEL}) SERVICE_SET_LEVEL_SCHEMA = vol.Schema({cv.string: _VALID_LOG_LEVEL}) @@ -38,7 +38,9 @@ CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.Schema( { - vol.Optional(LOGGER_DEFAULT): _VALID_LOG_LEVEL, + vol.Optional( + LOGGER_DEFAULT, default=DEFAULT_LOGSEVERITY + ): _VALID_LOG_LEVEL, vol.Optional(LOGGER_LOGS): vol.Schema({cv.string: _VALID_LOG_LEVEL}), vol.Optional(LOGGER_FILTERS): vol.Schema({cv.string: [cv.is_regex]}), } @@ -50,42 +52,38 @@ CONFIG_SCHEMA = vol.Schema( async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the logger component.""" - hass.data[DOMAIN] = {} - logging.setLoggerClass(_get_logger_class(hass.data[DOMAIN])) - @callback - def set_default_log_level(level): - """Set the default log level for components.""" - _set_log_level(logging.getLogger(""), level) + settings = LoggerSettings(hass, config) - @callback - def set_log_levels(logpoints): - """Set the specified log levels.""" - hass.data[DOMAIN].update(logpoints) - for key, value in logpoints.items(): - _set_log_level(logging.getLogger(key), value) + domain_config = hass.data[DOMAIN] = LoggerDomainConfig({}, settings) + logging.setLoggerClass(_get_logger_class(domain_config.overrides)) - # Set default log severity + websocket_api.async_load_websocket_api(hass) + + await settings.async_load() + + # Set default log severity and filter logger_config = config.get(DOMAIN, {}) if LOGGER_DEFAULT in logger_config: - set_default_log_level(logger_config[LOGGER_DEFAULT]) - - if LOGGER_LOGS in logger_config: - set_log_levels(config[DOMAIN][LOGGER_LOGS]) + set_default_log_level(hass, logger_config[LOGGER_DEFAULT]) if LOGGER_FILTERS in logger_config: - for key, value in logger_config[LOGGER_FILTERS].items(): - logger = logging.getLogger(key) - _add_log_filter(logger, value) + log_filters: dict[str, list[re.Pattern]] = logger_config[LOGGER_FILTERS] + for key, value in log_filters.items(): + _add_log_filter(logging.getLogger(key), value) + + # Combine log levels configured in configuration.yaml with log levels set by frontend + combined_logs = await settings.async_get_levels(hass) + set_log_levels(hass, combined_logs) @callback def async_service_handler(service: ServiceCall) -> None: """Handle logger services.""" if service.service == SERVICE_SET_DEFAULT_LEVEL: - set_default_log_level(service.data.get(ATTR_LEVEL)) + set_default_log_level(hass, service.data[ATTR_LEVEL]) else: - set_log_levels(service.data) + set_log_levels(hass, service.data) hass.services.async_register( DOMAIN, @@ -104,24 +102,16 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True -def _set_log_level(logger, level): - """Set the log level. - - Any logger fetched before this integration is loaded will use old class. - """ - getattr(logger, "orig_setLevel", logger.setLevel)(LOGSEVERITY[level]) - - -def _add_log_filter(logger, patterns): +def _add_log_filter(logger: logging.Logger, patterns: list[re.Pattern]) -> None: """Add a Filter to the logger based on a regexp of the filter_str.""" - def filter_func(logrecord): + def filter_func(logrecord: logging.LogRecord) -> bool: return not any(p.search(logrecord.getMessage()) for p in patterns) logger.addFilter(filter_func) -def _get_logger_class(hass_overrides): +def _get_logger_class(hass_overrides: dict[str, int]) -> type[logging.Logger]: """Create a logger subclass. logging.setLoggerClass checks if it is a subclass of Logger and @@ -131,7 +121,7 @@ def _get_logger_class(hass_overrides): class HassLogger(logging.Logger): """Home Assistant aware logger class.""" - def setLevel(self, level) -> None: + def setLevel(self, level: int | str) -> None: """Set the log level unless overridden.""" if self.name in hass_overrides: return @@ -139,7 +129,7 @@ def _get_logger_class(hass_overrides): super().setLevel(level) # pylint: disable=invalid-name - def orig_setLevel(self, level) -> None: + def orig_setLevel(self, level: int | str) -> None: """Set the log level.""" super().setLevel(level) diff --git a/homeassistant/components/logger/const.py b/homeassistant/components/logger/const.py new file mode 100644 index 00000000000..06f2af4f3f5 --- /dev/null +++ b/homeassistant/components/logger/const.py @@ -0,0 +1,42 @@ +"""Constants for the Logger integration.""" +import logging + +DOMAIN = "logger" + +SERVICE_SET_DEFAULT_LEVEL = "set_default_level" +SERVICE_SET_LEVEL = "set_level" + +LOGSEVERITY_NOTSET = "NOTSET" +LOGSEVERITY_DEBUG = "DEBUG" +LOGSEVERITY_INFO = "INFO" +LOGSEVERITY_WARNING = "WARNING" +LOGSEVERITY_ERROR = "ERROR" +LOGSEVERITY_CRITICAL = "CRITICAL" +LOGSEVERITY_WARN = "WARN" +LOGSEVERITY_FATAL = "FATAL" + +LOGSEVERITY = { + LOGSEVERITY_CRITICAL: logging.CRITICAL, + LOGSEVERITY_FATAL: logging.FATAL, + LOGSEVERITY_ERROR: logging.ERROR, + LOGSEVERITY_WARNING: logging.WARNING, + LOGSEVERITY_WARN: logging.WARN, + LOGSEVERITY_INFO: logging.INFO, + LOGSEVERITY_DEBUG: logging.DEBUG, + LOGSEVERITY_NOTSET: logging.NOTSET, +} + + +DEFAULT_LOGSEVERITY = "DEBUG" + +LOGGER_DEFAULT = "default" +LOGGER_LOGS = "logs" +LOGGER_FILTERS = "filters" + +ATTR_LEVEL = "level" + +EVENT_LOGGING_CHANGED = "logging_changed" + +STORAGE_KEY = "core.logger" +STORAGE_LOG_KEY = "logs" +STORAGE_VERSION = 1 diff --git a/homeassistant/components/logger/helpers.py b/homeassistant/components/logger/helpers.py new file mode 100644 index 00000000000..d85486a41e0 --- /dev/null +++ b/homeassistant/components/logger/helpers.py @@ -0,0 +1,217 @@ +"""Helpers for the logger integration.""" +from __future__ import annotations + +from collections import defaultdict +from collections.abc import Mapping +import contextlib +from dataclasses import asdict, dataclass +import logging +from typing import Any, cast + +from homeassistant.backports.enum import StrEnum +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.storage import Store +from homeassistant.helpers.typing import ConfigType +from homeassistant.loader import IntegrationNotFound, async_get_integration + +from .const import ( + DOMAIN, + EVENT_LOGGING_CHANGED, + LOGGER_DEFAULT, + LOGGER_LOGS, + LOGSEVERITY, + LOGSEVERITY_NOTSET, + STORAGE_KEY, + STORAGE_LOG_KEY, + STORAGE_VERSION, +) + + +@callback +def async_get_domain_config(hass: HomeAssistant) -> LoggerDomainConfig: + """Return the domain config.""" + return cast(LoggerDomainConfig, hass.data[DOMAIN]) + + +@callback +def set_default_log_level(hass: HomeAssistant, level: int) -> None: + """Set the default log level for components.""" + _set_log_level(logging.getLogger(""), level) + hass.bus.async_fire(EVENT_LOGGING_CHANGED) + + +@callback +def set_log_levels(hass: HomeAssistant, logpoints: Mapping[str, int]) -> None: + """Set the specified log levels.""" + async_get_domain_config(hass).overrides.update(logpoints) + for key, value in logpoints.items(): + _set_log_level(logging.getLogger(key), value) + hass.bus.async_fire(EVENT_LOGGING_CHANGED) + + +def _set_log_level(logger: logging.Logger, level: int) -> None: + """Set the log level. + + Any logger fetched before this integration is loaded will use old class. + """ + getattr(logger, "orig_setLevel", logger.setLevel)(level) + + +def _chattiest_log_level(level1: int, level2: int) -> int: + """Return the chattiest log level.""" + if level1 == logging.NOTSET: + return level2 + if level2 == logging.NOTSET: + return level1 + return min(level1, level2) + + +async def get_integration_loggers(hass: HomeAssistant, domain: str) -> list[str]: + """Get loggers for an integration.""" + loggers = [f"homeassistant.components.{domain}"] + with contextlib.suppress(IntegrationNotFound): + integration = await async_get_integration(hass, domain) + if integration.loggers: + loggers.extend(integration.loggers) + return loggers + + +@dataclass +class LoggerSetting: + """Settings for a single module or integration.""" + + level: str + persistence: str + type: str + + +@dataclass +class LoggerDomainConfig: + """Logger domain config.""" + + overrides: dict[str, Any] + settings: LoggerSettings + + +class LogPersistance(StrEnum): + """Log persistence.""" + + NONE = "none" + ONCE = "once" + PERMANENT = "permanent" + + +class LogSettingsType(StrEnum): + """Log settings type.""" + + INTEGRATION = "integration" + MODULE = "module" + + +class LoggerSettings: + """Manage log settings.""" + + _stored_config: dict[str, dict[str, LoggerSetting]] + + def __init__(self, hass: HomeAssistant, yaml_config: ConfigType) -> None: + """Initialize log settings.""" + + self._yaml_config = yaml_config + self._default_level = logging.INFO + if DOMAIN in yaml_config: + self._default_level = yaml_config[DOMAIN][LOGGER_DEFAULT] + self._store: Store[dict[str, dict[str, dict[str, Any]]]] = Store( + hass, STORAGE_VERSION, STORAGE_KEY + ) + + async def async_load(self) -> None: + """Load stored settings.""" + stored_config = await self._store.async_load() + if not stored_config: + self._stored_config = {STORAGE_LOG_KEY: {}} + return + + def reset_persistence(settings: LoggerSetting) -> LoggerSetting: + """Reset persistence.""" + if settings.persistence == LogPersistance.ONCE: + settings.persistence = LogPersistance.NONE + return settings + + stored_log_config = stored_config[STORAGE_LOG_KEY] + # Reset domains for which the overrides should only be applied once + self._stored_config = { + STORAGE_LOG_KEY: { + domain: reset_persistence(LoggerSetting(**settings)) + for domain, settings in stored_log_config.items() + } + } + await self._store.async_save(self._async_data_to_save()) + + @callback + def _async_data_to_save(self) -> dict[str, dict[str, dict[str, str]]]: + """Generate data to be saved.""" + stored_log_config = self._stored_config[STORAGE_LOG_KEY] + return { + STORAGE_LOG_KEY: { + domain: asdict(settings) + for domain, settings in stored_log_config.items() + if settings.persistence + in (LogPersistance.ONCE, LogPersistance.PERMANENT) + } + } + + @callback + def async_save(self) -> None: + """Save settings.""" + self._store.async_delay_save(self._async_data_to_save, 15) + + @callback + def _async_get_logger_logs(self) -> dict[str, int]: + """Get the logger logs.""" + logger_logs: dict[str, int] = self._yaml_config.get(DOMAIN, {}).get( + LOGGER_LOGS, {} + ) + return logger_logs + + async def async_update( + self, hass: HomeAssistant, domain: str, settings: LoggerSetting + ) -> None: + """Update settings.""" + stored_log_config = self._stored_config[STORAGE_LOG_KEY] + if settings.level == LOGSEVERITY_NOTSET: + stored_log_config.pop(domain, None) + else: + stored_log_config[domain] = settings + + self.async_save() + + if settings.type == LogSettingsType.INTEGRATION: + loggers = await get_integration_loggers(hass, domain) + else: + loggers = [domain] + + combined_logs = {logger: LOGSEVERITY[settings.level] for logger in loggers} + # Don't override the log levels with the ones from YAML + # since we want whatever the user is asking for to be honored. + + set_log_levels(hass, combined_logs) + + async def async_get_levels(self, hass: HomeAssistant) -> dict[str, int]: + """Get combination of levels from yaml and storage.""" + combined_logs = defaultdict(lambda: logging.CRITICAL) + for domain, settings in self._stored_config[STORAGE_LOG_KEY].items(): + if settings.type == LogSettingsType.INTEGRATION: + loggers = await get_integration_loggers(hass, domain) + else: + loggers = [domain] + + for logger in loggers: + combined_logs[logger] = LOGSEVERITY[settings.level] + + if yaml_log_settings := self._async_get_logger_logs(): + for domain, level in yaml_log_settings.items(): + combined_logs[domain] = _chattiest_log_level( + combined_logs[domain], level + ) + + return dict(combined_logs) diff --git a/homeassistant/components/logger/websocket_api.py b/homeassistant/components/logger/websocket_api.py new file mode 100644 index 00000000000..1b4e5cb36a6 --- /dev/null +++ b/homeassistant/components/logger/websocket_api.py @@ -0,0 +1,104 @@ +"""Websocket API handlers for the logger integration.""" +import logging +from typing import Any + +import voluptuous as vol + +from homeassistant.components import websocket_api +from homeassistant.components.websocket_api.connection import ActiveConnection +from homeassistant.core import HomeAssistant, callback +from homeassistant.loader import IntegrationNotFound, async_get_integration +from homeassistant.setup import async_get_loaded_integrations + +from .const import LOGSEVERITY +from .helpers import ( + LoggerSetting, + LogPersistance, + LogSettingsType, + async_get_domain_config, +) + + +@callback +def async_load_websocket_api(hass: HomeAssistant) -> None: + """Set up the websocket API.""" + websocket_api.async_register_command(hass, handle_integration_log_info) + websocket_api.async_register_command(hass, handle_integration_log_level) + websocket_api.async_register_command(hass, handle_module_log_level) + + +@websocket_api.websocket_command({vol.Required("type"): "logger/log_info"}) +@websocket_api.async_response +async def handle_integration_log_info( + hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] +) -> None: + """Handle integrations logger info.""" + connection.send_result( + msg["id"], + [ + { + "domain": integration, + "level": logging.getLogger( + f"homeassistant.components.{integration}" + ).getEffectiveLevel(), + } + for integration in async_get_loaded_integrations(hass) + ], + ) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "logger/integration_log_level", + vol.Required("integration"): str, + vol.Required("level"): vol.In(LOGSEVERITY), + vol.Required("persistence"): vol.Coerce(LogPersistance), + } +) +@websocket_api.async_response +async def handle_integration_log_level( + hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] +) -> None: + """Handle setting integration log level.""" + try: + await async_get_integration(hass, msg["integration"]) + except IntegrationNotFound: + connection.send_error( + msg["id"], websocket_api.const.ERR_NOT_FOUND, "Integration not found" + ) + return + await async_get_domain_config(hass).settings.async_update( + hass, + msg["integration"], + LoggerSetting( + level=msg["level"], + persistence=msg["persistence"], + type=LogSettingsType.INTEGRATION, + ), + ) + connection.send_message(websocket_api.messages.result_message(msg["id"])) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "logger/log_level", + vol.Required("module"): str, + vol.Required("level"): vol.In(LOGSEVERITY), + vol.Required("persistence"): vol.Coerce(LogPersistance), + } +) +@websocket_api.async_response +async def handle_module_log_level( + hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] +) -> None: + """Handle setting integration log level.""" + await async_get_domain_config(hass).settings.async_update( + hass, + msg["module"], + LoggerSetting( + level=msg["level"], + persistence=msg["persistence"], + type=LogSettingsType.MODULE, + ), + ) + connection.send_message(websocket_api.messages.result_message(msg["id"])) diff --git a/mypy.ini b/mypy.ini index 326bbc5ed36..fa245821ede 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1493,6 +1493,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.logger.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.lookin.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/tests/components/logger/conftest.py b/tests/components/logger/conftest.py new file mode 100644 index 00000000000..00d27753a61 --- /dev/null +++ b/tests/components/logger/conftest.py @@ -0,0 +1,12 @@ +"""Test fixtures for the Logger component.""" +import logging + +import pytest + + +@pytest.fixture(autouse=True) +def restore_logging_class(): + """Restore logging class.""" + klass = logging.getLoggerClass() + yield + logging.setLoggerClass(klass) diff --git a/tests/components/logger/test_init.py b/tests/components/logger/test_init.py index 6435ef95394..6b6269c4099 100644 --- a/tests/components/logger/test_init.py +++ b/tests/components/logger/test_init.py @@ -3,8 +3,6 @@ from collections import defaultdict import logging from unittest.mock import Mock, patch -import pytest - from homeassistant.components import logger from homeassistant.components.logger import LOGSEVERITY from homeassistant.setup import async_setup_component @@ -15,14 +13,8 @@ ZONE_NS = f"{COMPONENTS_NS}.zone" GROUP_NS = f"{COMPONENTS_NS}.group" CONFIGED_NS = "otherlibx" UNCONFIG_NS = "unconfigurednamespace" - - -@pytest.fixture(autouse=True) -def restore_logging_class(): - """Restore logging class.""" - klass = logging.getLoggerClass() - yield - logging.setLoggerClass(klass) +INTEGRATION = "test_component" +INTEGRATION_NS = f"homeassistant.components.{INTEGRATION}" async def test_log_filtering(hass, caplog): @@ -158,7 +150,7 @@ async def test_setting_level(hass): ) -async def test_can_set_level(hass): +async def test_can_set_level_from_yaml(hass): """Test logger propagation.""" assert await async_setup_component( @@ -178,7 +170,49 @@ async def test_can_set_level(hass): } }, ) + await _assert_log_levels(hass) + _reset_logging() + +async def test_can_set_level_from_store(hass, hass_storage): + """Test setting up logs from store.""" + hass_storage["core.logger"] = { + "data": { + "logs": { + CONFIGED_NS: { + "level": "WARNING", + "persistence": "once", + "type": "module", + }, + f"{CONFIGED_NS}.info": { + "level": "INFO", + "persistence": "once", + "type": "module", + }, + f"{CONFIGED_NS}.debug": { + "level": "DEBUG", + "persistence": "once", + "type": "module", + }, + HASS_NS: {"level": "WARNING", "persistence": "once", "type": "module"}, + COMPONENTS_NS: { + "level": "INFO", + "persistence": "once", + "type": "module", + }, + ZONE_NS: {"level": "DEBUG", "persistence": "once", "type": "module"}, + GROUP_NS: {"level": "INFO", "persistence": "once", "type": "module"}, + } + }, + "key": "core.logger", + "version": 1, + } + assert await async_setup_component(hass, "logger", {}) + await _assert_log_levels(hass) + _reset_logging() + + +async def _assert_log_levels(hass): assert logging.getLogger(UNCONFIG_NS).level == logging.NOTSET assert logging.getLogger(UNCONFIG_NS).isEnabledFor(logging.CRITICAL) is True assert ( @@ -255,3 +289,113 @@ async def test_can_set_level(hass): assert logging.getLogger(CONFIGED_NS).level == logging.WARNING logging.getLogger("").setLevel(logging.NOTSET) + + +def _reset_logging(): + """Reset loggers.""" + logging.getLogger(CONFIGED_NS).orig_setLevel(logging.NOTSET) + logging.getLogger(f"{CONFIGED_NS}.info").orig_setLevel(logging.NOTSET) + logging.getLogger(f"{CONFIGED_NS}.debug").orig_setLevel(logging.NOTSET) + logging.getLogger(HASS_NS).orig_setLevel(logging.NOTSET) + logging.getLogger(COMPONENTS_NS).orig_setLevel(logging.NOTSET) + logging.getLogger(ZONE_NS).orig_setLevel(logging.NOTSET) + logging.getLogger(GROUP_NS).orig_setLevel(logging.NOTSET) + logging.getLogger(INTEGRATION_NS).orig_setLevel(logging.NOTSET) + + +async def test_can_set_integration_level_from_store(hass, hass_storage): + """Test setting up integration logs from store.""" + hass_storage["core.logger"] = { + "data": { + "logs": { + INTEGRATION: { + "level": "WARNING", + "persistence": "once", + "type": "integration", + }, + } + }, + "key": "core.logger", + "version": 1, + } + assert await async_setup_component(hass, "logger", {}) + + assert logging.getLogger(INTEGRATION_NS).isEnabledFor(logging.DEBUG) is False + assert logging.getLogger(INTEGRATION_NS).isEnabledFor(logging.WARNING) is True + + _reset_logging() + + +async def test_chattier_log_level_wins_1(hass, hass_storage): + """Test chattier log level in store takes precedence.""" + hass_storage["core.logger"] = { + "data": { + "logs": { + INTEGRATION_NS: { + "level": "DEBUG", + "persistence": "once", + "type": "module", + }, + } + }, + "key": "core.logger", + "version": 1, + } + assert await async_setup_component( + hass, + "logger", + { + "logger": { + "logs": { + INTEGRATION_NS: "warning", + } + } + }, + ) + + assert logging.getLogger(INTEGRATION_NS).isEnabledFor(logging.DEBUG) is True + assert logging.getLogger(INTEGRATION_NS).isEnabledFor(logging.WARNING) is True + + _reset_logging() + + +async def test_chattier_log_level_wins_2(hass, hass_storage): + """Test chattier log level in yaml takes precedence.""" + hass_storage["core.logger"] = { + "data": { + "logs": { + INTEGRATION_NS: { + "level": "WARNING", + "persistence": "once", + "type": "module", + }, + } + }, + "key": "core.logger", + "version": 1, + } + assert await async_setup_component( + hass, "logger", {"logger": {"logs": {INTEGRATION_NS: "debug"}}} + ) + + assert logging.getLogger(INTEGRATION_NS).isEnabledFor(logging.DEBUG) is True + assert logging.getLogger(INTEGRATION_NS).isEnabledFor(logging.WARNING) is True + + _reset_logging() + + +async def test_log_once_removed_from_store(hass, hass_storage): + """Test logs with persistence "once" are removed from the store at startup.""" + hass_storage["core.logger"] = { + "data": { + "logs": { + ZONE_NS: {"type": "module", "level": "DEBUG", "persistence": "once"} + } + }, + "key": "core.logger", + "version": 1, + } + + assert await async_setup_component(hass, "logger", {}) + + assert hass_storage["core.logger"]["data"] == {"logs": {}} diff --git a/tests/components/logger/test_websocket_api.py b/tests/components/logger/test_websocket_api.py new file mode 100644 index 00000000000..1448196f1a3 --- /dev/null +++ b/tests/components/logger/test_websocket_api.py @@ -0,0 +1,195 @@ +"""Tests for Logger Websocket API commands.""" +import logging + +from homeassistant.components.logger.helpers import async_get_domain_config +from homeassistant.components.websocket_api import const +from homeassistant.setup import async_setup_component + + +async def test_integration_log_info(hass, hass_ws_client, hass_admin_user): + """Test fetching integration log info.""" + + assert await async_setup_component(hass, "logger", {}) + + logging.getLogger("homeassistant.components.http").setLevel(logging.DEBUG) + logging.getLogger("homeassistant.components.websocket_api").setLevel(logging.DEBUG) + + websocket_client = await hass_ws_client() + await websocket_client.send_json({"id": 7, "type": "logger/log_info"}) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == const.TYPE_RESULT + assert msg["success"] + assert {"domain": "http", "level": logging.DEBUG} in msg["result"] + assert {"domain": "websocket_api", "level": logging.DEBUG} in msg["result"] + + +async def test_integration_log_level_logger_not_loaded( + hass, hass_ws_client, hass_admin_user +): + """Test setting integration log level.""" + websocket_client = await hass_ws_client() + await websocket_client.send_json( + { + "id": 7, + "type": "logger/log_level", + "integration": "websocket_api", + "level": logging.DEBUG, + "persistence": "none", + } + ) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == const.TYPE_RESULT + assert not msg["success"] + + +async def test_integration_log_level(hass, hass_ws_client, hass_admin_user): + """Test setting integration log level.""" + websocket_client = await hass_ws_client() + assert await async_setup_component(hass, "logger", {}) + + await websocket_client.send_json( + { + "id": 7, + "type": "logger/integration_log_level", + "integration": "websocket_api", + "level": "DEBUG", + "persistence": "none", + } + ) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == const.TYPE_RESULT + assert msg["success"] + + assert async_get_domain_config(hass).overrides == { + "homeassistant.components.websocket_api": logging.DEBUG + } + + +async def test_integration_log_level_unknown_integration( + hass, hass_ws_client, hass_admin_user +): + """Test setting integration log level for an unknown integration.""" + websocket_client = await hass_ws_client() + assert await async_setup_component(hass, "logger", {}) + + await websocket_client.send_json( + { + "id": 7, + "type": "logger/integration_log_level", + "integration": "websocket_api_123", + "level": "DEBUG", + "persistence": "none", + } + ) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == const.TYPE_RESULT + assert not msg["success"] + + +async def test_module_log_level(hass, hass_ws_client, hass_admin_user): + """Test setting integration log level.""" + websocket_client = await hass_ws_client() + assert await async_setup_component( + hass, + "logger", + {"logger": {"logs": {"homeassistant.components.other_component": "warning"}}}, + ) + + await websocket_client.send_json( + { + "id": 7, + "type": "logger/log_level", + "module": "homeassistant.components.websocket_api", + "level": "DEBUG", + "persistence": "none", + } + ) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == const.TYPE_RESULT + assert msg["success"] + + assert async_get_domain_config(hass).overrides == { + "homeassistant.components.websocket_api": logging.DEBUG, + "homeassistant.components.other_component": logging.WARNING, + } + + +async def test_module_log_level_override(hass, hass_ws_client, hass_admin_user): + """Test override yaml integration log level.""" + websocket_client = await hass_ws_client() + assert await async_setup_component( + hass, + "logger", + {"logger": {"logs": {"homeassistant.components.websocket_api": "warning"}}}, + ) + + assert async_get_domain_config(hass).overrides == { + "homeassistant.components.websocket_api": logging.WARNING + } + + await websocket_client.send_json( + { + "id": 6, + "type": "logger/log_level", + "module": "homeassistant.components.websocket_api", + "level": "ERROR", + "persistence": "none", + } + ) + + msg = await websocket_client.receive_json() + assert msg["id"] == 6 + assert msg["type"] == const.TYPE_RESULT + assert msg["success"] + + assert async_get_domain_config(hass).overrides == { + "homeassistant.components.websocket_api": logging.ERROR + } + + await websocket_client.send_json( + { + "id": 7, + "type": "logger/log_level", + "module": "homeassistant.components.websocket_api", + "level": "DEBUG", + "persistence": "none", + } + ) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == const.TYPE_RESULT + assert msg["success"] + + assert async_get_domain_config(hass).overrides == { + "homeassistant.components.websocket_api": logging.DEBUG + } + + await websocket_client.send_json( + { + "id": 8, + "type": "logger/log_level", + "module": "homeassistant.components.websocket_api", + "level": "NOTSET", + "persistence": "none", + } + ) + + msg = await websocket_client.receive_json() + assert msg["id"] == 8 + assert msg["type"] == const.TYPE_RESULT + assert msg["success"] + + assert async_get_domain_config(hass).overrides == { + "homeassistant.components.websocket_api": logging.NOTSET + } From 0aa5610d884ea291d45a14a2c27bd97f5e03d306 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 17 Nov 2022 11:06:21 -0600 Subject: [PATCH 0522/1033] Add bluetooth loggers to manifest.json (#82276) --- homeassistant/components/bluetooth/manifest.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 739f9c96404..255c4ce0e82 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -15,5 +15,11 @@ ], "codeowners": ["@bdraco"], "config_flow": true, - "iot_class": "local_push" + "iot_class": "local_push", + "loggers": [ + "btsocket", + "bleak_retry_connector", + "bluetooth_adapters", + "bluetooth_auto_recovery" + ] } From 8da969b767b26de02d7b958285f9fbe3a8c77b38 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 17 Nov 2022 12:39:51 -0600 Subject: [PATCH 0523/1033] Disable BLE options flow for sleepy shelly devices (#82283) --- .../components/shelly/config_flow.py | 4 +++- tests/components/shelly/test_config_flow.py | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/shelly/config_flow.py b/homeassistant/components/shelly/config_flow.py index d02fe9556bf..9bf4a6126b0 100644 --- a/homeassistant/components/shelly/config_flow.py +++ b/homeassistant/components/shelly/config_flow.py @@ -341,7 +341,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): cls, config_entry: config_entries.ConfigEntry ) -> bool: """Return options flow support for this handler.""" - return config_entry.data.get("gen") == 2 + return config_entry.data.get("gen") == 2 and not config_entry.data.get( + CONF_SLEEP_PERIOD + ) class OptionsFlowHandler(config_entries.OptionsFlow): diff --git a/tests/components/shelly/test_config_flow.py b/tests/components/shelly/test_config_flow.py index fa53f8d3467..29f2804054f 100644 --- a/tests/components/shelly/test_config_flow.py +++ b/tests/components/shelly/test_config_flow.py @@ -1,4 +1,6 @@ """Test the Shelly config flow.""" +from __future__ import annotations + from unittest.mock import AsyncMock, Mock, patch from aioshelly.exceptions import ( @@ -927,6 +929,27 @@ async def test_options_flow_enabled_gen_2(hass, mock_rpc_device, hass_ws_client) await hass.config_entries.async_unload(entry.entry_id) +async def test_options_flow_disabled_sleepy_gen_2( + hass, mock_rpc_device, hass_ws_client +): + """Test options are disabled for sleepy gen2 devices.""" + await async_setup_component(hass, "config", {}) + entry = await init_integration(hass, 2, sleep_period=10) + + ws_client = await hass_ws_client(hass) + + await ws_client.send_json( + { + "id": 5, + "type": "config_entries/get", + "domain": "shelly", + } + ) + response = await ws_client.receive_json() + assert response["result"][0]["supports_options"] is False + await hass.config_entries.async_unload(entry.entry_id) + + async def test_options_flow_ble(hass, mock_rpc_device): """Test setting ble options for gen2 devices.""" entry = await init_integration(hass, 2) From e8ff3d10f717847b149a753f34e6c8c4c7b9f4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Thu, 17 Nov 2022 19:40:27 +0100 Subject: [PATCH 0524/1033] Update aioairzone to v0.5.0 (#82278) --- homeassistant/components/airzone/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/airzone/manifest.json b/homeassistant/components/airzone/manifest.json index bd8ed6b9920..b3885fae278 100644 --- a/homeassistant/components/airzone/manifest.json +++ b/homeassistant/components/airzone/manifest.json @@ -3,7 +3,7 @@ "name": "Airzone", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/airzone", - "requirements": ["aioairzone==0.4.8"], + "requirements": ["aioairzone==0.5.0"], "codeowners": ["@Noltari"], "iot_class": "local_polling", "loggers": ["aioairzone"] diff --git a/requirements_all.txt b/requirements_all.txt index 10c5aa6b375..679be04f0e7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -116,7 +116,7 @@ aio_georss_gdacs==0.7 aioairq==0.2.4 # homeassistant.components.airzone -aioairzone==0.4.8 +aioairzone==0.5.0 # homeassistant.components.ambient_station aioambient==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 259c607c031..a7c25fb3cea 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -103,7 +103,7 @@ aio_georss_gdacs==0.7 aioairq==0.2.4 # homeassistant.components.airzone -aioairzone==0.4.8 +aioairzone==0.5.0 # homeassistant.components.ambient_station aioambient==2021.11.0 From 3dba9c469584223cd693d01b3231453d2ab422da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Thu, 17 Nov 2022 19:41:33 +0100 Subject: [PATCH 0525/1033] Add QNAP QSW accumulated port sensors (#76514) Co-authored-by: J. Nick Koston --- homeassistant/components/qnap_qsw/sensor.py | 76 ++++++++++++++++++++- tests/components/qnap_qsw/test_sensor.py | 26 ++++++- 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/qnap_qsw/sensor.py b/homeassistant/components/qnap_qsw/sensor.py index 618c20b4cc2..5fecf28c9f2 100644 --- a/homeassistant/components/qnap_qsw/sensor.py +++ b/homeassistant/components/qnap_qsw/sensor.py @@ -7,10 +7,20 @@ from typing import Final from aioqsw.const import ( QSD_FAN1_SPEED, QSD_FAN2_SPEED, + QSD_LINK, + QSD_PORT_NUM, + QSD_PORTS_STATISTICS, + QSD_PORTS_STATUS, + QSD_RX_ERRORS, + QSD_RX_OCTETS, + QSD_RX_SPEED, + QSD_SYSTEM_BOARD, QSD_SYSTEM_SENSOR, QSD_SYSTEM_TIME, QSD_TEMP, QSD_TEMP_MAX, + QSD_TX_OCTETS, + QSD_TX_SPEED, QSD_UPTIME, ) @@ -21,7 +31,12 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS, TIME_SECONDS +from homeassistant.const import ( + DATA_BYTES, + DATA_RATE_BYTES_PER_SECOND, + TEMP_CELSIUS, + TIME_SECONDS, +) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -55,6 +70,44 @@ SENSOR_TYPES: Final[tuple[QswSensorEntityDescription, ...]] = ( state_class=SensorStateClass.MEASUREMENT, subkey=QSD_FAN2_SPEED, ), + QswSensorEntityDescription( + attributes={ + ATTR_MAX: [QSD_SYSTEM_BOARD, QSD_PORT_NUM], + }, + entity_registry_enabled_default=False, + icon="mdi:ethernet", + key=QSD_PORTS_STATUS, + name="Ports", + state_class=SensorStateClass.MEASUREMENT, + subkey=QSD_LINK, + ), + QswSensorEntityDescription( + entity_registry_enabled_default=False, + icon="mdi:download-network", + key=QSD_PORTS_STATISTICS, + name="RX", + native_unit_of_measurement=DATA_BYTES, + state_class=SensorStateClass.TOTAL_INCREASING, + subkey=QSD_RX_OCTETS, + ), + QswSensorEntityDescription( + entity_registry_enabled_default=False, + icon="mdi:close-network", + key=QSD_PORTS_STATISTICS, + entity_category=EntityCategory.DIAGNOSTIC, + name="RX Errors", + state_class=SensorStateClass.TOTAL_INCREASING, + subkey=QSD_RX_ERRORS, + ), + QswSensorEntityDescription( + entity_registry_enabled_default=False, + icon="mdi:download-network", + key=QSD_PORTS_STATISTICS, + name="RX Speed", + native_unit_of_measurement=DATA_RATE_BYTES_PER_SECOND, + state_class=SensorStateClass.MEASUREMENT, + subkey=QSD_RX_SPEED, + ), QswSensorEntityDescription( attributes={ ATTR_MAX: [QSD_SYSTEM_SENSOR, QSD_TEMP_MAX], @@ -66,6 +119,24 @@ SENSOR_TYPES: Final[tuple[QswSensorEntityDescription, ...]] = ( state_class=SensorStateClass.MEASUREMENT, subkey=QSD_TEMP, ), + QswSensorEntityDescription( + entity_registry_enabled_default=False, + icon="mdi:upload-network", + key=QSD_PORTS_STATISTICS, + name="TX", + native_unit_of_measurement=DATA_BYTES, + state_class=SensorStateClass.TOTAL_INCREASING, + subkey=QSD_TX_OCTETS, + ), + QswSensorEntityDescription( + entity_registry_enabled_default=False, + icon="mdi:upload-network", + key=QSD_PORTS_STATISTICS, + name="TX Speed", + native_unit_of_measurement=DATA_RATE_BYTES_PER_SECOND, + state_class=SensorStateClass.MEASUREMENT, + subkey=QSD_TX_SPEED, + ), QswSensorEntityDescription( icon="mdi:timer-outline", key=QSD_SYSTEM_TIME, @@ -116,7 +187,8 @@ class QswSensor(QswSensorEntity, SensorEntity): @callback def _async_update_attrs(self) -> None: """Update sensor attributes.""" - self._attr_native_value = self.get_device_value( + value = self.get_device_value( self.entity_description.key, self.entity_description.subkey ) + self._attr_native_value = value super()._async_update_attrs() diff --git a/tests/components/qnap_qsw/test_sensor.py b/tests/components/qnap_qsw/test_sensor.py index 3caf223c808..b37a45e441e 100644 --- a/tests/components/qnap_qsw/test_sensor.py +++ b/tests/components/qnap_qsw/test_sensor.py @@ -1,12 +1,17 @@ """The sensor tests for the QNAP QSW platform.""" +from unittest.mock import AsyncMock + from homeassistant.components.qnap_qsw.const import ATTR_MAX from homeassistant.core import HomeAssistant from .util import async_init_integration -async def test_qnap_qsw_create_sensors(hass: HomeAssistant) -> None: +async def test_qnap_qsw_create_sensors( + hass: HomeAssistant, + entity_registry_enabled_by_default: AsyncMock, +) -> None: """Test creation of sensors.""" await async_init_integration(hass) @@ -17,9 +22,28 @@ async def test_qnap_qsw_create_sensors(hass: HomeAssistant) -> None: state = hass.states.get("sensor.qsw_m408_4c_fan_2_speed") assert state is None + state = hass.states.get("sensor.qsw_m408_4c_ports") + assert state.state == "3" + assert state.attributes.get(ATTR_MAX) == 12 + + state = hass.states.get("sensor.qsw_m408_4c_rx_errors") + assert state.state == "22" + + state = hass.states.get("sensor.qsw_m408_4c_rx") + assert state.state == "22200" + + state = hass.states.get("sensor.qsw_m408_4c_rx_speed") + assert state.state == "0" + state = hass.states.get("sensor.qsw_m408_4c_temperature") assert state.state == "31" assert state.attributes.get(ATTR_MAX) == 85 + state = hass.states.get("sensor.qsw_m408_4c_tx") + assert state.state == "11100" + + state = hass.states.get("sensor.qsw_m408_4c_tx_speed") + assert state.state == "0" + state = hass.states.get("sensor.qsw_m408_4c_uptime") assert state.state == "91" From 0c887eab877c1ed8622c654c033bae3478e867fd Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 17 Nov 2022 20:00:52 +0100 Subject: [PATCH 0526/1033] Cleanup access to supported features (#82274) --- homeassistant/components/humidifier/__init__.py | 6 ++---- homeassistant/components/media_player/__init__.py | 5 ++--- homeassistant/components/siren/__init__.py | 11 ++++------- homeassistant/components/water_heater/__init__.py | 10 +++------- 4 files changed, 11 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/humidifier/__init__.py b/homeassistant/components/humidifier/__init__.py index ef1620b3922..976dad03a45 100644 --- a/homeassistant/components/humidifier/__init__.py +++ b/homeassistant/components/humidifier/__init__.py @@ -143,13 +143,12 @@ class HumidifierEntity(ToggleEntity): @property def capability_attributes(self) -> dict[str, Any]: """Return capability attributes.""" - supported_features = self.supported_features or 0 data: dict[str, int | list[str] | None] = { ATTR_MIN_HUMIDITY: self.min_humidity, ATTR_MAX_HUMIDITY: self.max_humidity, } - if supported_features & HumidifierEntityFeature.MODES: + if self.supported_features & HumidifierEntityFeature.MODES: data[ATTR_AVAILABLE_MODES] = self.available_modes return data @@ -167,13 +166,12 @@ class HumidifierEntity(ToggleEntity): @property def state_attributes(self) -> dict[str, Any]: """Return the optional state attributes.""" - supported_features = self.supported_features or 0 data: dict[str, int | str | None] = {} if self.target_humidity is not None: data[ATTR_HUMIDITY] = self.target_humidity - if supported_features & HumidifierEntityFeature.MODES: + if self.supported_features & HumidifierEntityFeature.MODES: data[ATTR_MODE] = self.mode return data diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 0e17d5c5339..120a1128597 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -1000,15 +1000,14 @@ class MediaPlayerEntity(Entity): @property def capability_attributes(self) -> dict[str, Any]: """Return capability attributes.""" - supported_features = self.supported_features or 0 data: dict[str, Any] = {} - if supported_features & MediaPlayerEntityFeature.SELECT_SOURCE and ( + if self.supported_features & MediaPlayerEntityFeature.SELECT_SOURCE and ( source_list := self.source_list ): data[ATTR_INPUT_SOURCE_LIST] = source_list - if supported_features & MediaPlayerEntityFeature.SELECT_SOUND_MODE and ( + if self.supported_features & MediaPlayerEntityFeature.SELECT_SOUND_MODE and ( sound_mode_list := self.sound_mode_list ): data[ATTR_SOUND_MODE_LIST] = sound_mode_list diff --git a/homeassistant/components/siren/__init__.py b/homeassistant/components/siren/__init__.py index faf8caf8ed0..0bc16d03770 100644 --- a/homeassistant/components/siren/__init__.py +++ b/homeassistant/components/siren/__init__.py @@ -64,9 +64,8 @@ def process_turn_on_params( Filters out unsupported params and validates the rest. """ - supported_features = siren.supported_features or 0 - if not supported_features & SirenEntityFeature.TONES: + if not siren.supported_features & SirenEntityFeature.TONES: params.pop(ATTR_TONE, None) elif (tone := params.get(ATTR_TONE)) is not None: # Raise an exception if the specified tone isn't available @@ -92,9 +91,9 @@ def process_turn_on_params( key for key, value in siren.available_tones.items() if value == tone ) - if not supported_features & SirenEntityFeature.DURATION: + if not siren.supported_features & SirenEntityFeature.DURATION: params.pop(ATTR_DURATION, None) - if not supported_features & SirenEntityFeature.VOLUME_SET: + if not siren.supported_features & SirenEntityFeature.VOLUME_SET: params.pop(ATTR_VOLUME_LEVEL, None) return params @@ -169,10 +168,8 @@ class SirenEntity(ToggleEntity): @property def capability_attributes(self) -> dict[str, Any] | None: """Return capability attributes.""" - supported_features = self.supported_features or 0 - if ( - supported_features & SirenEntityFeature.TONES + self.supported_features & SirenEntityFeature.TONES and self.available_tones is not None ): return {ATTR_AVAILABLE_TONES: self.available_tones} diff --git a/homeassistant/components/water_heater/__init__.py b/homeassistant/components/water_heater/__init__.py index 187c643638b..4396c66cf22 100644 --- a/homeassistant/components/water_heater/__init__.py +++ b/homeassistant/components/water_heater/__init__.py @@ -191,8 +191,6 @@ class WaterHeaterEntity(Entity): @property def capability_attributes(self) -> Mapping[str, Any]: """Return capability attributes.""" - supported_features = self.supported_features or 0 - data: dict[str, Any] = { ATTR_MIN_TEMP: show_temp( self.hass, self.min_temp, self.temperature_unit, self.precision @@ -202,7 +200,7 @@ class WaterHeaterEntity(Entity): ), } - if supported_features & WaterHeaterEntityFeature.OPERATION_MODE: + if self.supported_features & WaterHeaterEntityFeature.OPERATION_MODE: data[ATTR_OPERATION_LIST] = self.operation_list return data @@ -238,12 +236,10 @@ class WaterHeaterEntity(Entity): ), } - supported_features = self.supported_features or 0 - - if supported_features & WaterHeaterEntityFeature.OPERATION_MODE: + if self.supported_features & WaterHeaterEntityFeature.OPERATION_MODE: data[ATTR_OPERATION_MODE] = self.current_operation - if supported_features & WaterHeaterEntityFeature.AWAY_MODE: + if self.supported_features & WaterHeaterEntityFeature.AWAY_MODE: is_away = self.is_away_mode_on data[ATTR_AWAY_MODE] = STATE_ON if is_away else STATE_OFF From d0efdd750f40982356782f2c23e7cda8a2303434 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 17 Nov 2022 13:22:06 -0600 Subject: [PATCH 0527/1033] Fix high latency from thundering heard at 0 microseconds (#82233) * Fix high latency at 0 microseconds fixes #82231 * fix async_track_utc_time_change alignment * use replace to preserve fold * naming * tweak * make async_fire_time_changed aware of the thundering heard issue --- homeassistant/helpers/event.py | 10 +++++++++- homeassistant/helpers/update_coordinator.py | 17 +++++++++++++++-- tests/common.py | 8 ++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index 613b6fb3227..b553b855be1 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -8,6 +8,7 @@ from dataclasses import dataclass from datetime import datetime, timedelta import functools as ft import logging +from random import randint import time from typing import Any, Union, cast @@ -60,6 +61,9 @@ _ENTITIES_LISTENER = "entities" _LOGGER = logging.getLogger(__name__) +RANDOM_MICROSECOND_MIN = 50000 +RANDOM_MICROSECOND_MAX = 500000 + _P = ParamSpec("_P") @@ -1506,13 +1510,17 @@ def async_track_utc_time_change( matching_seconds = dt_util.parse_time_expression(second, 0, 59) matching_minutes = dt_util.parse_time_expression(minute, 0, 59) matching_hours = dt_util.parse_time_expression(hour, 0, 23) + # Avoid aligning all time trackers to the same second + # since it can create a thundering herd problem + # https://github.com/home-assistant/core/issues/82231 + microsecond = randint(RANDOM_MICROSECOND_MIN, RANDOM_MICROSECOND_MAX) def calculate_next(now: datetime) -> datetime: """Calculate and set the next time the trigger should fire.""" localized_now = dt_util.as_local(now) if local else now return dt_util.find_next_time_expression_time( localized_now, matching_seconds, matching_minutes, matching_hours - ) + ).replace(microsecond=microsecond) time_listener: CALLBACK_TYPE | None = None diff --git a/homeassistant/helpers/update_coordinator.py b/homeassistant/helpers/update_coordinator.py index 768b8040729..f25691ae504 100644 --- a/homeassistant/helpers/update_coordinator.py +++ b/homeassistant/helpers/update_coordinator.py @@ -5,6 +5,7 @@ import asyncio from collections.abc import Awaitable, Callable, Coroutine, Generator from datetime import datetime, timedelta import logging +from random import randint from time import monotonic from typing import Any, Generic, TypeVar import urllib.error @@ -61,6 +62,12 @@ class DataUpdateCoordinator(Generic[_T]): # when it was already checked during setup. self.data: _T = None # type: ignore[assignment] + # Pick a random microsecond to stagger the refreshes + # and avoid a thundering herd. + self._microsecond = randint( + event.RANDOM_MICROSECOND_MIN, event.RANDOM_MICROSECOND_MAX + ) + self._listeners: dict[CALLBACK_TYPE, tuple[CALLBACK_TYPE, object | None]] = {} self._job = HassJob(self._handle_refresh_interval) self._unsub_refresh: CALLBACK_TYPE | None = None @@ -138,11 +145,17 @@ class DataUpdateCoordinator(Generic[_T]): # We _floor_ utcnow to create a schedule on a rounded second, # minimizing the time between the point and the real activation. # That way we obtain a constant update frequency, - # as long as the update process takes less than a second + # as long as the update process takes less than 500ms + # + # We do not align everything to happen at microsecond 0 + # since it increases the risk of a thundering herd + # when multiple coordinators are scheduled to update at the same time. + # + # https://github.com/home-assistant/core/issues/82231 self._unsub_refresh = event.async_track_point_in_utc_time( self.hass, self._job, - utcnow().replace(microsecond=0) + self.update_interval, + utcnow().replace(microsecond=self._microsecond) + self.update_interval, ) async def _handle_refresh_interval(self, _now: datetime) -> None: diff --git a/tests/common.py b/tests/common.py index 14f3cdd47c2..140f4060d00 100644 --- a/tests/common.py +++ b/tests/common.py @@ -388,6 +388,14 @@ def async_fire_time_changed( utc_datetime = date_util.utcnow() else: utc_datetime = date_util.as_utc(datetime_) + + if utc_datetime.microsecond < 500000: + # Allow up to 500000 microseconds to be added to the time + # to handle update_coordinator's and + # async_track_time_interval's + # staggering to avoid thundering herd. + utc_datetime = utc_datetime.replace(microsecond=500000) + timestamp = date_util.utc_to_timestamp(utc_datetime) for task in list(hass.loop._scheduled): From 47c66dbed4b11b5dbea25e91ec027f6bb940b66b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 17 Nov 2022 13:34:19 -0600 Subject: [PATCH 0528/1033] Offload platform specific bluetooth code to bluetooth-adapters (#82196) * Offload platform specific bluetooth code to bluetooth-adapters * adjust * fix some more patch targets * more test fixes * almost there * may not be setup yet * more fixes * fixes * fix test * fix merge --- .../components/bluetooth/__init__.py | 25 ++--- .../components/bluetooth/config_flow.py | 21 ++-- homeassistant/components/bluetooth/const.py | 29 +----- homeassistant/components/bluetooth/manager.py | 25 +++-- .../components/bluetooth/manifest.json | 2 +- homeassistant/components/bluetooth/scanner.py | 4 +- homeassistant/components/bluetooth/util.py | 81 ++------------- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/bluetooth/__init__.py | 2 +- tests/components/bluetooth/conftest.py | 99 ++++++++----------- .../components/bluetooth/test_config_flow.py | 4 +- .../components/bluetooth/test_diagnostics.py | 18 ++-- tests/components/bluetooth/test_init.py | 79 +++++++-------- tests/components/bluetooth/test_manager.py | 6 +- tests/conftest.py | 19 ++-- 17 files changed, 158 insertions(+), 262 deletions(-) diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index e3f62280661..9a8cf115a8e 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -10,6 +10,16 @@ from typing import TYPE_CHECKING, cast import async_timeout from awesomeversion import AwesomeVersion +from bluetooth_adapters import ( + ADAPTER_ADDRESS, + ADAPTER_HW_VERSION, + ADAPTER_SW_VERSION, + DEFAULT_ADDRESS, + AdapterDetails, + adapter_human_name, + adapter_unique_name, + get_adapters, +) from homeassistant.components import usb from homeassistant.config_entries import ( @@ -32,20 +42,15 @@ from homeassistant.loader import async_get_bluetooth from . import models from .const import ( - ADAPTER_ADDRESS, - ADAPTER_HW_VERSION, - ADAPTER_SW_VERSION, BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS, CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, DATA_MANAGER, - DEFAULT_ADDRESS, DOMAIN, FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS, SOURCE_LOCAL, - AdapterDetails, ) from .manager import BluetoothManager from .match import BluetoothCallbackMatcher, IntegrationMatcher @@ -62,7 +67,6 @@ from .models import ( ProcessAdvertisementCallback, ) from .scanner import HaScanner, ScannerStartError -from .util import adapter_human_name, adapter_unique_name, async_default_adapter if TYPE_CHECKING: from bleak.backends.device import BLEDevice @@ -288,13 +292,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the bluetooth integration.""" integration_matcher = IntegrationMatcher(await async_get_bluetooth(hass)) integration_matcher.async_setup() - manager = BluetoothManager(hass, integration_matcher) + bluetooth_adapters = get_adapters() + manager = BluetoothManager(hass, integration_matcher, bluetooth_adapters) await manager.async_setup() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, manager.async_stop) hass.data[DATA_MANAGER] = models.MANAGER = manager adapters = await manager.async_get_bluetooth_adapters() - async_migrate_entries(hass, adapters) + async_migrate_entries(hass, adapters, bluetooth_adapters.default_adapter) await async_discover_adapters(hass, adapters) async def _async_rediscover_adapters() -> None: @@ -347,17 +352,15 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: EVENT_HOMEASSISTANT_STARTED, hass_callback(lambda event: _async_check_haos(hass)), ) - return True @hass_callback def async_migrate_entries( - hass: HomeAssistant, adapters: dict[str, AdapterDetails] + hass: HomeAssistant, adapters: dict[str, AdapterDetails], default_adapter: str ) -> None: """Migrate config entries to support multiple.""" current_entries = hass.config_entries.async_entries(DOMAIN) - default_adapter = async_default_adapter() for entry in current_entries: if entry.unique_id: diff --git a/homeassistant/components/bluetooth/config_flow.py b/homeassistant/components/bluetooth/config_flow.py index 324520a8b5b..ffebce2e521 100644 --- a/homeassistant/components/bluetooth/config_flow.py +++ b/homeassistant/components/bluetooth/config_flow.py @@ -3,6 +3,13 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, cast +from bluetooth_adapters import ( + ADAPTER_ADDRESS, + AdapterDetails, + adapter_human_name, + adapter_unique_name, + get_adapters, +) import voluptuous as vol from homeassistant.components import onboarding @@ -11,15 +18,7 @@ from homeassistant.core import callback from homeassistant.helpers.typing import DiscoveryInfoType from . import models -from .const import ( - ADAPTER_ADDRESS, - CONF_ADAPTER, - CONF_DETAILS, - CONF_PASSIVE, - DOMAIN, - AdapterDetails, -) -from .util import adapter_human_name, adapter_unique_name, async_get_bluetooth_adapters +from .const import CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, DOMAIN if TYPE_CHECKING: from homeassistant.data_entry_flow import FlowResult @@ -87,7 +86,9 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN): ) configured_addresses = self._async_current_ids() - self._adapters = await async_get_bluetooth_adapters() + bluetooth_adapters = get_adapters() + await bluetooth_adapters.refresh() + self._adapters = bluetooth_adapters.adapters unconfigured_adapters = [ adapter for adapter, details in self._adapters.items() diff --git a/homeassistant/components/bluetooth/const.py b/homeassistant/components/bluetooth/const.py index 038c2b1988f..d44858107ab 100644 --- a/homeassistant/components/bluetooth/const.py +++ b/homeassistant/components/bluetooth/const.py @@ -2,7 +2,7 @@ from __future__ import annotations from datetime import timedelta -from typing import Final, TypedDict +from typing import Final DOMAIN = "bluetooth" @@ -10,18 +10,6 @@ CONF_ADAPTER = "adapter" CONF_DETAILS = "details" CONF_PASSIVE = "passive" -WINDOWS_DEFAULT_BLUETOOTH_ADAPTER = "bluetooth" -MACOS_DEFAULT_BLUETOOTH_ADAPTER = "Core Bluetooth" -UNIX_DEFAULT_BLUETOOTH_ADAPTER = "hci0" - -DEFAULT_ADAPTER_BY_PLATFORM = { - "Windows": WINDOWS_DEFAULT_BLUETOOTH_ADAPTER, - "Darwin": MACOS_DEFAULT_BLUETOOTH_ADAPTER, -} - - -# Some operating systems hide the adapter address for privacy reasons (ex MacOS) -DEFAULT_ADDRESS: Final = "00:00:00:00:00:00" SOURCE_LOCAL: Final = "local" @@ -66,18 +54,3 @@ SCANNER_WATCHDOG_INTERVAL: Final = timedelta(seconds=30) # are not present LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS = 120 BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS = 5 - - -class AdapterDetails(TypedDict, total=False): - """Adapter details.""" - - address: str - sw_version: str - hw_version: str | None - passive_scan: bool - - -ADAPTER_ADDRESS: Final = "address" -ADAPTER_SW_VERSION: Final = "sw_version" -ADAPTER_HW_VERSION: Final = "hw_version" -ADAPTER_PASSIVE_SCAN: Final = "passive_scan" diff --git a/homeassistant/components/bluetooth/manager.py b/homeassistant/components/bluetooth/manager.py index c216abde4ea..535ee5e3716 100644 --- a/homeassistant/components/bluetooth/manager.py +++ b/homeassistant/components/bluetooth/manager.py @@ -10,6 +10,12 @@ from typing import TYPE_CHECKING, Any, Final from bleak.backends.scanner import AdvertisementDataCallback from bleak_retry_connector import NO_RSSI_VALUE, RSSI_SWITCH_THRESHOLD +from bluetooth_adapters import ( + ADAPTER_ADDRESS, + ADAPTER_PASSIVE_SCAN, + AdapterDetails, + BluetoothAdapters, +) from homeassistant import config_entries from homeassistant.core import ( @@ -24,11 +30,8 @@ from homeassistant.util.dt import monotonic_time_coarse from .advertisement_tracker import AdvertisementTracker from .const import ( - ADAPTER_ADDRESS, - ADAPTER_PASSIVE_SCAN, FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, UNAVAILABLE_TRACK_SECONDS, - AdapterDetails, ) from .match import ( ADDRESS, @@ -47,7 +50,7 @@ from .models import ( BluetoothServiceInfoBleak, ) from .usage import install_multiple_bleak_catcher, uninstall_multiple_bleak_catcher -from .util import async_get_bluetooth_adapters, async_load_history_from_system +from .util import async_load_history_from_system if TYPE_CHECKING: from bleak.backends.device import BLEDevice @@ -102,6 +105,7 @@ class BluetoothManager: self, hass: HomeAssistant, integration_matcher: IntegrationMatcher, + bluetooth_adapters: BluetoothAdapters, ) -> None: """Init bluetooth manager.""" self.hass = hass @@ -127,6 +131,7 @@ class BluetoothManager: self._connectable_scanners: list[BaseHaScanner] = [] self._adapters: dict[str, AdapterDetails] = {} self._sources: set[str] = set() + self._bluetooth_adapters = bluetooth_adapters @property def supports_passive_scan(self) -> bool: @@ -172,21 +177,25 @@ class BluetoothManager: self, cached: bool = True ) -> dict[str, AdapterDetails]: """Get bluetooth adapters.""" - if not cached or not self._adapters: - self._adapters = await async_get_bluetooth_adapters() + if not self._adapters or not cached: + if not cached: + await self._bluetooth_adapters.refresh() + self._adapters = self._bluetooth_adapters.adapters return self._adapters async def async_get_adapter_from_address(self, address: str) -> str | None: """Get adapter from address.""" if adapter := self._find_adapter_by_address(address): return adapter - self._adapters = await async_get_bluetooth_adapters() + await self._bluetooth_adapters.refresh() + self._adapters = self._bluetooth_adapters.adapters return self._find_adapter_by_address(address) async def async_setup(self) -> None: """Set up the bluetooth manager.""" + await self._bluetooth_adapters.refresh() install_multiple_bleak_catcher() - history = await async_load_history_from_system() + history = async_load_history_from_system(self._bluetooth_adapters) # Everything is connectable so it fall into both # buckets since the host system can only provide # connectable devices diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 255c4ce0e82..ca0df4d2042 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -8,7 +8,7 @@ "requirements": [ "bleak==0.19.2", "bleak-retry-connector==2.8.4", - "bluetooth-adapters==0.7.0", + "bluetooth-adapters==0.8.0", "bluetooth-auto-recovery==0.4.0", "bluetooth-data-tools==0.3.0", "dbus-fast==1.74.1" diff --git a/homeassistant/components/bluetooth/scanner.py b/homeassistant/components/bluetooth/scanner.py index c05894d3b44..797a0551552 100644 --- a/homeassistant/components/bluetooth/scanner.py +++ b/homeassistant/components/bluetooth/scanner.py @@ -16,6 +16,7 @@ from bleak.backends.bluezdbus.advertisement_monitor import OrPattern from bleak.backends.bluezdbus.scanner import BlueZScannerArgs from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData, AdvertisementDataCallback +from bluetooth_adapters import DEFAULT_ADDRESS, adapter_human_name from dbus_fast import InvalidMessageError from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback @@ -25,14 +26,13 @@ from homeassistant.util.dt import monotonic_time_coarse from homeassistant.util.package import is_docker_env from .const import ( - DEFAULT_ADDRESS, SCANNER_WATCHDOG_INTERVAL, SCANNER_WATCHDOG_TIMEOUT, SOURCE_LOCAL, START_TIMEOUT, ) from .models import BaseHaScanner, BluetoothScanningMode, BluetoothServiceInfoBleak -from .util import adapter_human_name, async_reset_adapter +from .util import async_reset_adapter OriginalBleakScanner = bleak.BleakScanner MONOTONIC_TIME = monotonic_time_coarse diff --git a/homeassistant/components/bluetooth/util.py b/homeassistant/components/bluetooth/util.py index abfba6b22bc..c2336dd7af0 100644 --- a/homeassistant/components/bluetooth/util.py +++ b/homeassistant/components/bluetooth/util.py @@ -1,34 +1,20 @@ """The bluetooth integration utilities.""" from __future__ import annotations -import platform - +from bluetooth_adapters import BluetoothAdapters from bluetooth_auto_recovery import recover_adapter from homeassistant.core import callback from homeassistant.util.dt import monotonic_time_coarse -from .const import ( - DEFAULT_ADAPTER_BY_PLATFORM, - DEFAULT_ADDRESS, - MACOS_DEFAULT_BLUETOOTH_ADAPTER, - UNIX_DEFAULT_BLUETOOTH_ADAPTER, - WINDOWS_DEFAULT_BLUETOOTH_ADAPTER, - AdapterDetails, -) from .models import BluetoothServiceInfoBleak -async def async_load_history_from_system() -> dict[str, BluetoothServiceInfoBleak]: +@callback +def async_load_history_from_system( + adapters: BluetoothAdapters, +) -> dict[str, BluetoothServiceInfoBleak]: """Load the device and advertisement_data history if available on the current system.""" - if platform.system() != "Linux": - return {} - from bluetooth_adapters import ( # pylint: disable=import-outside-toplevel - BlueZDBusObjects, - ) - - bluez_dbus = BlueZDBusObjects() - await bluez_dbus.load() now = monotonic_time_coarse() return { address: BluetoothServiceInfoBleak( @@ -46,65 +32,10 @@ async def async_load_history_from_system() -> dict[str, BluetoothServiceInfoBlea connectable=False, time=now, ) - for address, history in bluez_dbus.history.items() + for address, history in adapters.history.items() } -async def async_get_bluetooth_adapters() -> dict[str, AdapterDetails]: - """Return a list of bluetooth adapters.""" - if platform.system() == "Windows": - return { - WINDOWS_DEFAULT_BLUETOOTH_ADAPTER: AdapterDetails( - address=DEFAULT_ADDRESS, - sw_version=platform.release(), - passive_scan=False, - ) - } - if platform.system() == "Darwin": - return { - MACOS_DEFAULT_BLUETOOTH_ADAPTER: AdapterDetails( - address=DEFAULT_ADDRESS, - sw_version=platform.release(), - passive_scan=False, - ) - } - from bluetooth_adapters import ( # pylint: disable=import-outside-toplevel - get_bluetooth_adapter_details, - ) - - adapters: dict[str, AdapterDetails] = {} - adapter_details = await get_bluetooth_adapter_details() - for adapter, details in adapter_details.items(): - adapter1 = details["org.bluez.Adapter1"] - adapters[adapter] = AdapterDetails( - address=adapter1["Address"], - sw_version=adapter1["Name"], # This is actually the BlueZ version - hw_version=adapter1.get("Modalias"), - passive_scan="org.bluez.AdvertisementMonitorManager1" in details, - ) - return adapters - - -@callback -def async_default_adapter() -> str: - """Return the default adapter for the platform.""" - return DEFAULT_ADAPTER_BY_PLATFORM.get( - platform.system(), UNIX_DEFAULT_BLUETOOTH_ADAPTER - ) - - -@callback -def adapter_human_name(adapter: str, address: str) -> str: - """Return a human readable name for the adapter.""" - return adapter if address == DEFAULT_ADDRESS else f"{adapter} ({address})" - - -@callback -def adapter_unique_name(adapter: str, address: str) -> str: - """Return a unique name for the adapter.""" - return adapter if address == DEFAULT_ADDRESS else address - - async def async_reset_adapter(adapter: str | None) -> bool | None: """Reset the adapter.""" if adapter and adapter.startswith("hci"): diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 394de1abc83..833421d3bf2 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ awesomeversion==22.9.0 bcrypt==3.1.7 bleak-retry-connector==2.8.4 bleak==0.19.2 -bluetooth-adapters==0.7.0 +bluetooth-adapters==0.8.0 bluetooth-auto-recovery==0.4.0 bluetooth-data-tools==0.3.0 certifi>=2021.5.30 diff --git a/requirements_all.txt b/requirements_all.txt index 679be04f0e7..0fd0942c23d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -447,7 +447,7 @@ bluemaestro-ble==0.2.0 # bluepy==1.3.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.7.0 +bluetooth-adapters==0.8.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a7c25fb3cea..418b0aa48a9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -361,7 +361,7 @@ blinkpy==0.19.2 bluemaestro-ble==0.2.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.7.0 +bluetooth-adapters==0.8.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.4.0 diff --git a/tests/components/bluetooth/__init__.py b/tests/components/bluetooth/__init__.py index e695f18c42f..0ffa347c6d2 100644 --- a/tests/components/bluetooth/__init__.py +++ b/tests/components/bluetooth/__init__.py @@ -6,6 +6,7 @@ from typing import Any from unittest.mock import patch from bleak.backends.scanner import AdvertisementData, BLEDevice +from bluetooth_adapters import DEFAULT_ADDRESS from homeassistant.components.bluetooth import ( DOMAIN, @@ -13,7 +14,6 @@ from homeassistant.components.bluetooth import ( async_get_advertisement_callback, models, ) -from homeassistant.components.bluetooth.const import DEFAULT_ADDRESS from homeassistant.components.bluetooth.manager import BluetoothManager from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component diff --git a/tests/components/bluetooth/conftest.py b/tests/components/bluetooth/conftest.py index 3d29b4cbbe1..c650a84df85 100644 --- a/tests/components/bluetooth/conftest.py +++ b/tests/components/bluetooth/conftest.py @@ -1,6 +1,6 @@ """Tests for the bluetooth component.""" -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import patch import pytest @@ -43,16 +43,6 @@ def mock_operating_system_90(): yield -@pytest.fixture(name="bluez_dbus_mock") -def bluez_dbus_mock(): - """Fixture that mocks out the bluez dbus calls.""" - # Must patch directly since this is loaded on demand only - with patch( - "bluetooth_adapters.BlueZDBusObjects", return_value=MagicMock(load=AsyncMock()) - ): - yield - - @pytest.fixture(name="macos_adapter") def macos_adapter(): """Fixture that mocks the macos adapter.""" @@ -62,7 +52,7 @@ def macos_adapter(): "homeassistant.components.bluetooth.scanner.platform.system", return_value="Darwin", ), patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Darwin" + "bluetooth_adapters.systems.platform.system", return_value="Darwin" ): yield @@ -71,14 +61,14 @@ def macos_adapter(): def windows_adapter(): """Fixture that mocks the windows adapter.""" with patch( - "homeassistant.components.bluetooth.util.platform.system", + "bluetooth_adapters.systems.platform.system", return_value="Windows", ): yield @pytest.fixture(name="no_adapters") -def no_adapter_fixture(bluez_dbus_mock): +def no_adapter_fixture(): """Fixture that mocks no adapters on Linux.""" with patch( "homeassistant.components.bluetooth.platform.system", return_value="Linux" @@ -86,16 +76,18 @@ def no_adapter_fixture(bluez_dbus_mock): "homeassistant.components.bluetooth.scanner.platform.system", return_value="Linux", ), patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" + "bluetooth_adapters.systems.platform.system", return_value="Linux" ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={}, + "bluetooth_adapters.systems.linux.LinuxAdapters.refresh" + ), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + {}, ): yield @pytest.fixture(name="one_adapter") -def one_adapter_fixture(bluez_dbus_mock): +def one_adapter_fixture(): """Fixture that mocks one adapter on Linux.""" with patch( "homeassistant.components.bluetooth.platform.system", return_value="Linux" @@ -103,20 +95,17 @@ def one_adapter_fixture(bluez_dbus_mock): "homeassistant.components.bluetooth.scanner.platform.system", return_value="Linux", ), patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" + "bluetooth_adapters.systems.platform.system", return_value="Linux" ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={ + "bluetooth_adapters.systems.linux.LinuxAdapters.refresh" + ), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + { "hci0": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:01", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - }, - "org.bluez.AdvertisementMonitorManager1": { - "SupportedMonitorTypes": ["or_patterns"], - "SupportedFeatures": [], - }, + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": True, + "sw_version": "homeassistant", }, }, ): @@ -124,7 +113,7 @@ def one_adapter_fixture(bluez_dbus_mock): @pytest.fixture(name="two_adapters") -def two_adapters_fixture(bluez_dbus_mock): +def two_adapters_fixture(): """Fixture that mocks two adapters on Linux.""" with patch( "homeassistant.components.bluetooth.platform.system", return_value="Linux" @@ -132,27 +121,23 @@ def two_adapters_fixture(bluez_dbus_mock): "homeassistant.components.bluetooth.scanner.platform.system", return_value="Linux", ), patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" + "bluetooth_adapters.systems.platform.system", return_value="Linux" ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={ + "bluetooth_adapters.systems.linux.LinuxAdapters.refresh" + ), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + { "hci0": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:01", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - } + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, "hci1": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:02", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - }, - "org.bluez.AdvertisementMonitorManager1": { - "SupportedMonitorTypes": ["or_patterns"], - "SupportedFeatures": [], - }, + "address": "00:00:00:00:00:02", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": True, + "sw_version": "homeassistant", }, }, ): @@ -160,7 +145,7 @@ def two_adapters_fixture(bluez_dbus_mock): @pytest.fixture(name="one_adapter_old_bluez") -def one_adapter_old_bluez(bluez_dbus_mock): +def one_adapter_old_bluez(): """Fixture that mocks two adapters on Linux.""" with patch( "homeassistant.components.bluetooth.platform.system", return_value="Linux" @@ -168,15 +153,17 @@ def one_adapter_old_bluez(bluez_dbus_mock): "homeassistant.components.bluetooth.scanner.platform.system", return_value="Linux", ), patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" + "bluetooth_adapters.systems.platform.system", return_value="Linux" ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={ + "bluetooth_adapters.systems.linux.LinuxAdapters.refresh" + ), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + { "hci0": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:01", - "Name": "BlueZ 4.43", - } + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, }, ): diff --git a/tests/components/bluetooth/test_config_flow.py b/tests/components/bluetooth/test_config_flow.py index 69619ba76a7..76c0346026b 100644 --- a/tests/components/bluetooth/test_config_flow.py +++ b/tests/components/bluetooth/test_config_flow.py @@ -2,14 +2,14 @@ from unittest.mock import patch +from bluetooth_adapters import DEFAULT_ADDRESS, AdapterDetails + from homeassistant import config_entries from homeassistant.components.bluetooth.const import ( CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, - DEFAULT_ADDRESS, DOMAIN, - AdapterDetails, ) from homeassistant.data_entry_flow import FlowResultType from homeassistant.setup import async_setup_component diff --git a/tests/components/bluetooth/test_diagnostics.py b/tests/components/bluetooth/test_diagnostics.py index 56321175241..bfbefe74151 100644 --- a/tests/components/bluetooth/test_diagnostics.py +++ b/tests/components/bluetooth/test_diagnostics.py @@ -4,9 +4,9 @@ from unittest.mock import ANY, patch from bleak.backends.scanner import BLEDevice +from bluetooth_adapters import DEFAULT_ADDRESS from homeassistant.components import bluetooth -from homeassistant.components.bluetooth.const import DEFAULT_ADDRESS from . import generate_advertisement_data, inject_advertisement @@ -68,15 +68,15 @@ async def test_diagnostics( "adapters": { "hci0": { "address": "00:00:00:00:00:01", - "hw_version": "usbid:1234", + "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": False, - "sw_version": "BlueZ 4.63", + "sw_version": "homeassistant", }, "hci1": { "address": "00:00:00:00:00:02", - "hw_version": "usbid:1234", + "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": True, - "sw_version": "BlueZ 4.63", + "sw_version": "homeassistant", }, }, "dbus": { @@ -99,15 +99,15 @@ async def test_diagnostics( "adapters": { "hci0": { "address": "00:00:00:00:00:01", - "hw_version": "usbid:1234", + "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": False, - "sw_version": "BlueZ 4.63", + "sw_version": "homeassistant", }, "hci1": { "address": "00:00:00:00:00:02", - "hw_version": "usbid:1234", + "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": True, - "sw_version": "BlueZ 4.63", + "sw_version": "homeassistant", }, }, "advertisement_tracker": { diff --git a/tests/components/bluetooth/test_init.py b/tests/components/bluetooth/test_init.py index 5a5437af71a..da315984f3c 100644 --- a/tests/components/bluetooth/test_init.py +++ b/tests/components/bluetooth/test_init.py @@ -6,6 +6,7 @@ from unittest.mock import ANY, MagicMock, Mock, patch from bleak import BleakError from bleak.backends.scanner import AdvertisementData, BLEDevice +from bluetooth_adapters import DEFAULT_ADDRESS import pytest from homeassistant.components import bluetooth @@ -22,7 +23,6 @@ from homeassistant.components.bluetooth import ( from homeassistant.components.bluetooth.const import ( BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS, CONF_PASSIVE, - DEFAULT_ADDRESS, DOMAIN, LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS, SOURCE_LOCAL, @@ -2522,7 +2522,7 @@ async def test_async_ble_device_from_address( async def test_can_unsetup_bluetooth_single_adapter_macos( - hass, mock_bleak_scanner_start, enable_bluetooth, macos_adapter + hass, mock_bleak_scanner_start, macos_adapter ): """Test we can setup and unsetup bluetooth.""" entry = MockConfigEntry(domain=bluetooth.DOMAIN, data={}, unique_id=DEFAULT_ADDRESS) @@ -2605,12 +2605,13 @@ async def test_auto_detect_bluetooth_adapters_linux_multiple(hass, two_adapters) assert len(hass.config_entries.flow.async_progress(bluetooth.DOMAIN)) == 2 -async def test_auto_detect_bluetooth_adapters_linux_none_found(hass, bluez_dbus_mock): +async def test_auto_detect_bluetooth_adapters_linux_none_found(hass): """Test we auto detect bluetooth adapters on linux with no adapters found.""" with patch( - "bluetooth_adapters.get_bluetooth_adapter_details", return_value={} - ), patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" + "bluetooth_adapters.systems.platform.system", return_value="Linux" + ), patch("bluetooth_adapters.systems.linux.LinuxAdapters.refresh"), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + {}, ): assert await async_setup_component(hass, bluetooth.DOMAIN, {}) await hass.async_block_till_done() @@ -2620,9 +2621,7 @@ async def test_auto_detect_bluetooth_adapters_linux_none_found(hass, bluez_dbus_ async def test_auto_detect_bluetooth_adapters_macos(hass): """Test we auto detect bluetooth adapters on macos.""" - with patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Darwin" - ): + with patch("bluetooth_adapters.systems.platform.system", return_value="Darwin"): assert await async_setup_component(hass, bluetooth.DOMAIN, {}) await hass.async_block_till_done() assert not hass.config_entries.async_entries(bluetooth.DOMAIN) @@ -2632,7 +2631,7 @@ async def test_auto_detect_bluetooth_adapters_macos(hass): async def test_no_auto_detect_bluetooth_adapters_windows(hass): """Test we auto detect bluetooth adapters on windows.""" with patch( - "homeassistant.components.bluetooth.util.platform.system", + "bluetooth_adapters.systems.platform.system", return_value="Windows", ): assert await async_setup_component(hass, bluetooth.DOMAIN, {}) @@ -2710,23 +2709,21 @@ async def test_discover_new_usb_adapters(hass, mock_bleak_scanner_start, one_ada assert not hass.config_entries.flow.async_progress(DOMAIN) with patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" - ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={ + "bluetooth_adapters.systems.platform.system", return_value="Linux" + ), patch("bluetooth_adapters.systems.linux.LinuxAdapters.refresh"), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + { "hci0": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:01", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - } + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, "hci1": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:02", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - } + "address": "00:00:00:00:00:02", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, }, ): @@ -2768,10 +2765,10 @@ async def test_discover_new_usb_adapters_with_firmware_fallback_delay( assert not hass.config_entries.flow.async_progress(DOMAIN) with patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" - ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={}, + "bluetooth_adapters.systems.platform.system", return_value="Linux" + ), patch("bluetooth_adapters.systems.linux.LinuxAdapters.refresh"), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + {}, ): async_fire_time_changed( hass, dt_util.utcnow() + timedelta(BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS * 2) @@ -2781,23 +2778,21 @@ async def test_discover_new_usb_adapters_with_firmware_fallback_delay( assert len(hass.config_entries.flow.async_progress(DOMAIN)) == 0 with patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" - ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={ + "bluetooth_adapters.systems.platform.system", return_value="Linux" + ), patch("bluetooth_adapters.systems.linux.LinuxAdapters.refresh"), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + { "hci0": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:01", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - } + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, "hci1": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:02", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - } + "address": "00:00:00:00:00:02", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, }, ): diff --git a/tests/components/bluetooth/test_manager.py b/tests/components/bluetooth/test_manager.py index 0375f68309f..c61d8f4c9f1 100644 --- a/tests/components/bluetooth/test_manager.py +++ b/tests/components/bluetooth/test_manager.py @@ -1,7 +1,7 @@ """Tests for the Bluetooth integration manager.""" import time -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import patch from bleak.backends.scanner import BLEDevice from bluetooth_adapters import AdvertisementHistory @@ -275,8 +275,8 @@ async def test_restore_history_from_dbus(hass, one_adapter): } with patch( - "bluetooth_adapters.BlueZDBusObjects", - return_value=MagicMock(load=AsyncMock(), history=history), + "bluetooth_adapters.systems.linux.LinuxAdapters.history", + history, ): assert await async_setup_component(hass, bluetooth.DOMAIN, {}) await hass.async_block_till_done() diff --git a/tests/conftest.py b/tests/conftest.py index 1047293ee16..ca005625655 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1041,18 +1041,15 @@ async def mock_enable_bluetooth( def mock_bluetooth_adapters(): """Fixture to mock bluetooth adapters.""" with patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" - ), patch( - "bluetooth_adapters.BlueZDBusObjects", return_value=MagicMock(load=AsyncMock()) - ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={ + "bluetooth_adapters.systems.platform.system", return_value="Linux" + ), patch("bluetooth_adapters.systems.linux.LinuxAdapters.refresh"), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + { "hci0": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:01", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - } + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, }, ): From b5390f55ceea36f0d7f73cb878b757e82ad832a7 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Thu, 17 Nov 2022 20:59:13 +0100 Subject: [PATCH 0529/1033] Add intent_script debug logging (#82060) --- homeassistant/components/intent_script/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/homeassistant/components/intent_script/__init__.py b/homeassistant/components/intent_script/__init__.py index e8c5c580708..0090b1def9b 100644 --- a/homeassistant/components/intent_script/__init__.py +++ b/homeassistant/components/intent_script/__init__.py @@ -1,5 +1,6 @@ """Handle intents with scripts.""" import copy +import logging import voluptuous as vol @@ -8,6 +9,8 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv, intent, script, template from homeassistant.helpers.typing import ConfigType +_LOGGER = logging.getLogger(__name__) + DOMAIN = "intent_script" CONF_INTENTS = "intents" @@ -83,6 +86,16 @@ class ScriptIntentHandler(intent.IntentHandler): is_async_action = self.config.get(CONF_ASYNC_ACTION) slots = {key: value["value"] for key, value in intent_obj.slots.items()} + _LOGGER.debug( + "Intent named %s received with slots: %s", + intent_obj.intent_type, + { + key: value + for key, value in slots.items() + if not key.startswith("_") and not key.endswith("_raw_value") + }, + ) + if action is not None: if is_async_action: intent_obj.hass.async_create_task( From b717da879feba6746360962c0adc58b5463204e5 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 17 Nov 2022 21:12:27 +0100 Subject: [PATCH 0530/1033] Add support for Mqtt protocol version 5 (#82260) --- homeassistant/components/mqtt/client.py | 20 ++++++++-- homeassistant/components/mqtt/config_flow.py | 6 ++- homeassistant/components/mqtt/const.py | 3 +- tests/components/mqtt/test_config_flow.py | 39 ++++++++++++++++++++ 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py index 34384614c20..c94b5d5ec51 100644 --- a/homeassistant/components/mqtt/client.py +++ b/homeassistant/components/mqtt/client.py @@ -57,6 +57,7 @@ from .const import ( DEFAULT_QOS, MQTT_CONNECTED, MQTT_DISCONNECTED, + PROTOCOL_5, PROTOCOL_31, ) from .models import ( @@ -272,8 +273,10 @@ class MqttClientSetup: # should be able to optionally rely on MQTT. import paho.mqtt.client as mqtt # pylint: disable=import-outside-toplevel - if config.get(CONF_PROTOCOL, DEFAULT_PROTOCOL) == PROTOCOL_31: + if (protocol := config.get(CONF_PROTOCOL, DEFAULT_PROTOCOL)) == PROTOCOL_31: proto = mqtt.MQTTv31 + elif protocol == PROTOCOL_5: + proto = mqtt.MQTTv5 else: proto = mqtt.MQTTv311 @@ -558,8 +561,9 @@ class MQTT: self, _mqttc: mqtt.Client, _userdata: None, - _flags: dict[str, Any], + _flags: dict[str, int], result_code: int, + properties: mqtt.Properties | None = None, ) -> None: """On connect callback. @@ -677,9 +681,13 @@ class MQTT: _mqttc: mqtt.Client, _userdata: None, mid: int, - _granted_qos: tuple[Any, ...] | None = None, + _granted_qos_reason: tuple[int, ...] | mqtt.ReasonCodes | None = None, + _properties_reason: mqtt.ReasonCodes | None = None, ) -> None: """Publish / Subscribe / Unsubscribe callback.""" + # The callback signature for on_unsubscribe is different from on_subscribe + # see https://github.com/eclipse/paho.mqtt.python/issues/687 + # properties and reasoncodes are not used in Home Assistant self.hass.add_job(self._mqtt_handle_mid, mid) async def _mqtt_handle_mid(self, mid: int) -> None: @@ -695,7 +703,11 @@ class MQTT: self._pending_operations[mid] = asyncio.Event() def _mqtt_on_disconnect( - self, _mqttc: mqtt.Client, _userdata: None, result_code: int + self, + _mqttc: mqtt.Client, + _userdata: None, + result_code: int, + properties: mqtt.Properties | None = None, ) -> None: """Disconnected callback.""" self.connected = False diff --git a/homeassistant/components/mqtt/config_flow.py b/homeassistant/components/mqtt/config_flow.py index 5e5beb2f314..3d5ab99f340 100644 --- a/homeassistant/components/mqtt/config_flow.py +++ b/homeassistant/components/mqtt/config_flow.py @@ -683,7 +683,11 @@ def try_connection( result: queue.Queue[bool] = queue.Queue(maxsize=1) def on_connect( - client_: mqtt.Client, userdata: None, flags: dict[str, Any], result_code: int + client_: mqtt.Client, + userdata: None, + flags: dict[str, Any], + result_code: int, + properties: mqtt.Properties | None = None, ) -> None: """Handle connection result.""" result.put(result_code == mqtt.CONNACK_ACCEPTED) diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index dc8aa2f2c74..87da9ab7979 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -46,7 +46,8 @@ DEFAULT_RETAIN = False PROTOCOL_31 = "3.1" PROTOCOL_311 = "3.1.1" -SUPPORTED_PROTOCOLS = [PROTOCOL_31, PROTOCOL_311] +PROTOCOL_5 = "5" +SUPPORTED_PROTOCOLS = [PROTOCOL_31, PROTOCOL_311, PROTOCOL_5] DEFAULT_PORT = 1883 DEFAULT_KEEPALIVE = 60 diff --git a/tests/components/mqtt/test_config_flow.py b/tests/components/mqtt/test_config_flow.py index eef728664aa..b9fdf0b9c26 100644 --- a/tests/components/mqtt/test_config_flow.py +++ b/tests/components/mqtt/test_config_flow.py @@ -194,6 +194,45 @@ async def test_user_connection_works( assert len(mock_finish_setup.mock_calls) == 1 +async def test_user_v5_connection_works( + hass, mock_try_connection, mock_finish_setup, mqtt_client_mock +): + """Test we can finish a config flow.""" + mock_try_connection.return_value = True + + result = await hass.config_entries.flow.async_init( + "mqtt", context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"broker": "127.0.0.1", "advanced_options": True} + ) + + assert result["step_id"] == "broker" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + mqtt.CONF_BROKER: "another-broker", + mqtt.CONF_PORT: 2345, + mqtt.CONF_PROTOCOL: "5", + }, + ) + assert result["type"] == "create_entry" + assert result["result"].data == { + "broker": "another-broker", + "discovery": True, + "discovery_prefix": "homeassistant", + "port": 2345, + "protocol": "5", + } + # Check we tried the connection + assert len(mock_try_connection.mock_calls) == 1 + # Check config entry got setup + assert len(mock_finish_setup.mock_calls) == 1 + + async def test_user_connection_fails( hass, mock_try_connection_time_out, mock_finish_setup ): From a07117470e9a16c5f5bcefd12d109afd3bcf3f69 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 17 Nov 2022 21:12:45 +0100 Subject: [PATCH 0531/1033] Add number device class support to ESPHome (#82277) --- homeassistant/components/esphome/manifest.json | 2 +- homeassistant/components/esphome/number.py | 10 +++++++++- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 2070b8ae362..12b2bc98d2c 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==11.4.3"], + "requirements": ["aioesphomeapi==11.5.0"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/homeassistant/components/esphome/number.py b/homeassistant/components/esphome/number.py index a00d4456227..4111a616439 100644 --- a/homeassistant/components/esphome/number.py +++ b/homeassistant/components/esphome/number.py @@ -1,11 +1,12 @@ """Support for esphome numbers.""" from __future__ import annotations +from contextlib import suppress import math from aioesphomeapi import NumberInfo, NumberMode as EsphomeNumberMode, NumberState -from homeassistant.components.number import NumberEntity, NumberMode +from homeassistant.components.number import NumberDeviceClass, NumberEntity, NumberMode from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -47,6 +48,13 @@ NUMBER_MODES: EsphomeEnumMapper[EsphomeNumberMode, NumberMode] = EsphomeEnumMapp class EsphomeNumber(EsphomeEntity[NumberInfo, NumberState], NumberEntity): """A number implementation for esphome.""" + @property + def device_class(self) -> NumberDeviceClass | None: + """Return the class of this entity.""" + with suppress(ValueError): + return NumberDeviceClass(self._static_info.device_class) + return None + @property def native_min_value(self) -> float: """Return the minimum value.""" diff --git a/requirements_all.txt b/requirements_all.txt index 0fd0942c23d..c18fd4d3661 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -156,7 +156,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.4.3 +aioesphomeapi==11.5.0 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 418b0aa48a9..17baae684ad 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -143,7 +143,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.4.3 +aioesphomeapi==11.5.0 # homeassistant.components.flo aioflo==2021.11.0 From e3749e0f76d3e6b0c3d23de6e6e64b5530ea8e31 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 17 Nov 2022 21:14:54 +0100 Subject: [PATCH 0532/1033] Cleanup pip cache workaround [ci] (#82272) --- .github/workflows/cache.yml | 56 ------------------------------------- .github/workflows/ci.yaml | 4 +++ 2 files changed, 4 insertions(+), 56 deletions(-) delete mode 100644 .github/workflows/cache.yml diff --git a/.github/workflows/cache.yml b/.github/workflows/cache.yml deleted file mode 100644 index ca80d54ad6a..00000000000 --- a/.github/workflows/cache.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Delete Caches - -# yamllint disable-line rule:truthy -on: - pull_request: - types: [closed] - workflow_dispatch: - inputs: - pr-number: - description: "Closed PR number" - type: string - required: true - -jobs: - delete: - name: "Delete unused caches (PR #${{ github.event.inputs.pr-number || github.event.number }})" - runs-on: ubuntu-latest - permissions: - actions: write - steps: - - name: Delete caches - run: | - ref="refs/pull/${{ github.event.inputs.pr-number || github.event.number }}/merge" - - page=1 - per_page=100 - del_count=0 - - while true; do - res=$(curl -fsS \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: token ${{ github.token }}" \ - "https://api.github.com/repos/${{ github.repository }}/actions/caches?per_page=$per_page&page=$page") - - res_count=$(echo $res | jq '.actions_caches | length') - if [ $res_count -eq 0 ]; then break; fi - - targets=$(echo $res | jq \ - --arg ref "$ref" \ - '.actions_caches[] | select(.ref == $ref) | del(.created_at, .size_in_bytes, .version)') - echo "$targets" - - for id in $(echo $targets | jq '.id'); do - curl -sS \ - -X DELETE \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: token ${{ github.token }}" \ - "https://api.github.com/repos/${{ github.repository }}/actions/caches/$id" - ((del_count+=1)) - done - - if [ $res_count -lt $per_page ]; then break; fi - ((page+=1)) - done - - echo "Delete $del_count caches." diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index cd21717142a..eadcc082732 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -558,6 +558,10 @@ jobs: - base permissions: actions: write + # Limit to 'dev' branch until permissions issue is resolved + # https://github.com/home-assistant/core/pull/80898 + # https://github.com/home-assistant/core/pull/82254 + if: github.ref == 'refs/heads/dev' steps: - name: Cleanup run: | From 1a274adc28a9e69fe06d3ebaf1a6e168e844ecda Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 17 Nov 2022 21:52:57 +0100 Subject: [PATCH 0533/1033] Add config_entries.async_wait_component (#76980) Co-authored-by: thecode --- .../components/device_automation/trigger.py | 24 ++++++- .../components/webostv/device_trigger.py | 7 -- homeassistant/components/webostv/helpers.py | 14 ---- homeassistant/components/zha/__init__.py | 5 +- homeassistant/components/zha/core/const.py | 1 - .../components/zha/device_trigger.py | 24 ++++--- homeassistant/config_entries.py | 18 ++++- homeassistant/setup.py | 24 ++++++- .../components/shelly/test_device_trigger.py | 26 ++++++-- .../components/webostv/test_device_trigger.py | 39 ----------- tests/test_config_entries.py | 66 ++++++++++++++++++- 11 files changed, 157 insertions(+), 91 deletions(-) diff --git a/homeassistant/components/device_automation/trigger.py b/homeassistant/components/device_automation/trigger.py index bd72b24d844..05f2f79ff28 100644 --- a/homeassistant/components/device_automation/trigger.py +++ b/homeassistant/components/device_automation/trigger.py @@ -5,8 +5,9 @@ from typing import Any, Protocol, cast import voluptuous as vol -from homeassistant.const import CONF_DOMAIN +from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN from homeassistant.core import CALLBACK_TYPE, HomeAssistant +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo from homeassistant.helpers.typing import ConfigType @@ -63,6 +64,27 @@ async def async_validate_trigger_config( ) if not hasattr(platform, "async_validate_trigger_config"): return cast(ConfigType, platform.TRIGGER_SCHEMA(config)) + + # Only call the dynamic validator if the relevant config entry is loaded + registry = dr.async_get(hass) + if not (device := registry.async_get(config[CONF_DEVICE_ID])): + raise InvalidDeviceAutomationConfig + + device_config_entry = None + for entry_id in device.config_entries: + if not (entry := hass.config_entries.async_get_entry(entry_id)): + continue + if entry.domain != config[CONF_DOMAIN]: + continue + device_config_entry = entry + break + + if not device_config_entry: + raise InvalidDeviceAutomationConfig + + if not await hass.config_entries.async_wait_component(device_config_entry): + return config + return await platform.async_validate_trigger_config(hass, config) except InvalidDeviceAutomationConfig as err: raise vol.Invalid(str(err) or "Invalid trigger configuration") from err diff --git a/homeassistant/components/webostv/device_trigger.py b/homeassistant/components/webostv/device_trigger.py index ef3e74a7daa..590cbc19de8 100644 --- a/homeassistant/components/webostv/device_trigger.py +++ b/homeassistant/components/webostv/device_trigger.py @@ -18,7 +18,6 @@ from .const import DOMAIN from .helpers import ( async_get_client_wrapper_by_device_entry, async_get_device_entry_by_device_id, - async_is_device_config_entry_not_loaded, ) from .triggers.turn_on import PLATFORM_TYPE as TURN_ON_PLATFORM_TYPE @@ -36,12 +35,6 @@ async def async_validate_trigger_config( """Validate config.""" config = TRIGGER_SCHEMA(config) - try: - if async_is_device_config_entry_not_loaded(hass, config[CONF_DEVICE_ID]): - return config - except ValueError as err: - raise InvalidDeviceAutomationConfig(err) from err - if config[CONF_TYPE] == TURN_ON_PLATFORM_TYPE: device_id = config[CONF_DEVICE_ID] try: diff --git a/homeassistant/components/webostv/helpers.py b/homeassistant/components/webostv/helpers.py index 0ee3805f42f..4f1ab9dfebe 100644 --- a/homeassistant/components/webostv/helpers.py +++ b/homeassistant/components/webostv/helpers.py @@ -1,7 +1,6 @@ """Helper functions for webOS Smart TV.""" from __future__ import annotations -from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.device_registry import DeviceEntry @@ -26,19 +25,6 @@ def async_get_device_entry_by_device_id( return device -@callback -def async_is_device_config_entry_not_loaded( - hass: HomeAssistant, device_id: str -) -> bool: - """Return whether device's config entries are not loaded.""" - device = async_get_device_entry_by_device_id(hass, device_id) - return any( - (entry := hass.config_entries.async_get_entry(entry_id)) - and entry.state != ConfigEntryState.LOADED - for entry_id in device.config_entries - ) - - @callback def async_get_device_id_from_entity_id(hass: HomeAssistant, entity_id: str) -> str: """ diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index 0a7d43120f7..70b9dfd9b46 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -35,7 +35,6 @@ from .core.const import ( DOMAIN, PLATFORMS, SIGNAL_ADD_ENTITIES, - ZHA_DEVICES_LOADED_EVENT, RadioType, ) from .core.discovery import GROUP_PROBE @@ -76,7 +75,7 @@ _LOGGER = logging.getLogger(__name__) async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up ZHA from config.""" - hass.data[DATA_ZHA] = {ZHA_DEVICES_LOADED_EVENT: asyncio.Event()} + hass.data[DATA_ZHA] = {} if DOMAIN in config: conf = config[DOMAIN] @@ -110,7 +109,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b zha_gateway = ZHAGateway(hass, config, config_entry) await zha_gateway.async_initialize() - hass.data[DATA_ZHA][ZHA_DEVICES_LOADED_EVENT].set() device_registry = dr.async_get(hass) device_registry.async_get_or_create( @@ -143,7 +141,6 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> """Unload ZHA config entry.""" zha_gateway: ZHAGateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] await zha_gateway.shutdown() - hass.data[DATA_ZHA][ZHA_DEVICES_LOADED_EVENT].clear() GROUP_PROBE.cleanup() api.async_unload_api(hass) diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index b9871a1f2ab..180be2d8830 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -395,7 +395,6 @@ ZHA_GW_MSG_GROUP_REMOVED = "group_removed" ZHA_GW_MSG_LOG_ENTRY = "log_entry" ZHA_GW_MSG_LOG_OUTPUT = "log_output" ZHA_GW_MSG_RAW_INIT = "raw_device_initialized" -ZHA_DEVICES_LOADED_EVENT = "zha_devices_loaded_event" class Strobe(t.enum8): diff --git a/homeassistant/components/zha/device_trigger.py b/homeassistant/components/zha/device_trigger.py index 94b94b89e40..6f78aa6f858 100644 --- a/homeassistant/components/zha/device_trigger.py +++ b/homeassistant/components/zha/device_trigger.py @@ -14,7 +14,7 @@ from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo from homeassistant.helpers.typing import ConfigType from . import DOMAIN as ZHA_DOMAIN -from .core.const import DATA_ZHA, ZHA_DEVICES_LOADED_EVENT, ZHA_EVENT +from .core.const import ZHA_EVENT from .core.helpers import async_get_zha_device CONF_SUBTYPE = "subtype" @@ -32,18 +32,16 @@ async def async_validate_trigger_config( """Validate config.""" config = TRIGGER_SCHEMA(config) - if ZHA_DOMAIN in hass.config.components: - await hass.data[DATA_ZHA][ZHA_DEVICES_LOADED_EVENT].wait() - trigger = (config[CONF_TYPE], config[CONF_SUBTYPE]) - try: - zha_device = async_get_zha_device(hass, config[CONF_DEVICE_ID]) - except (KeyError, AttributeError, IntegrationError) as err: - raise InvalidDeviceAutomationConfig from err - if ( - zha_device.device_automation_triggers is None - or trigger not in zha_device.device_automation_triggers - ): - raise InvalidDeviceAutomationConfig + trigger = (config[CONF_TYPE], config[CONF_SUBTYPE]) + try: + zha_device = async_get_zha_device(hass, config[CONF_DEVICE_ID]) + except (KeyError, AttributeError, IntegrationError) as err: + raise InvalidDeviceAutomationConfig from err + if ( + zha_device.device_automation_triggers is None + or trigger not in zha_device.device_automation_triggers + ): + raise InvalidDeviceAutomationConfig return config diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index ddef5d7f226..eb6fcf855a3 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -26,7 +26,7 @@ from .helpers.dispatcher import async_dispatcher_connect, async_dispatcher_send from .helpers.event import async_call_later from .helpers.frame import report from .helpers.typing import UNDEFINED, ConfigType, DiscoveryInfoType, UndefinedType -from .setup import async_process_deps_reqs, async_setup_component +from .setup import DATA_SETUP_DONE, async_process_deps_reqs, async_setup_component from .util import uuid as uuid_util from .util.decorator import Registry @@ -1314,6 +1314,22 @@ class ConfigEntries: """Return data to save.""" return {"entries": [entry.as_dict() for entry in self._entries.values()]} + async def async_wait_component(self, entry: ConfigEntry) -> bool: + """Wait for an entry's component to load and return if the entry is loaded. + + This is primarily intended for existing config entries which are loaded at + startup, awaiting this function will block until the component and all its + config entries are loaded. + Config entries which are created after Home Assistant is started can't be waited + for, the function will just return if the config entry is loaded or not. + """ + if setup_event := self.hass.data.get(DATA_SETUP_DONE, {}).get(entry.domain): + await setup_event.wait() + # The component was not loaded. + if entry.domain not in self.hass.config.components: + return False + return entry.state == ConfigEntryState.LOADED + async def _old_conf_migrator(old_config: dict[str, Any]) -> dict[str, Any]: """Migrate the pre-0.73 config format to the latest version.""" diff --git a/homeassistant/setup.py b/homeassistant/setup.py index 2be22910a08..1172ab00bd7 100644 --- a/homeassistant/setup.py +++ b/homeassistant/setup.py @@ -29,11 +29,27 @@ ATTR_COMPONENT = "component" BASE_PLATFORMS = {platform.value for platform in Platform} +# DATA_SETUP is a dict[str, asyncio.Task[bool]], indicating domains which are currently +# being setup or which failed to setup +# - Tasks are added to DATA_SETUP by `async_setup_component`, the key is the domain being setup +# and the Task is the `_async_setup_component` helper. +# - Tasks are removed from DATA_SETUP if setup was successful, that is, the task returned True +DATA_SETUP = "setup_tasks" + +# DATA_SETUP_DONE is a dict [str, asyncio.Event], indicating components which will be setup +# - Events are added to DATA_SETUP_DONE during bootstrap by async_set_domains_to_be_loaded, +# the key is the domain which will be loaded +# - Events are set and removed from DATA_SETUP_DONE when async_setup_component is finished, +# regardless of if the setup was successful or not. DATA_SETUP_DONE = "setup_done" + +# DATA_SETUP_DONE is a dict [str, datetime], indicating when an attempt to setup a component +# started DATA_SETUP_STARTED = "setup_started" + +# DATA_SETUP_TIME is a dict [str, timedelta], indicating how time was spent setting up a component DATA_SETUP_TIME = "setup_time" -DATA_SETUP = "setup_tasks" DATA_DEPS_REQS = "deps_reqs_processed" SLOW_SETUP_WARNING = 10 @@ -44,7 +60,9 @@ SLOW_SETUP_MAX_WAIT = 300 def async_set_domains_to_be_loaded(hass: core.HomeAssistant, domains: set[str]) -> None: """Set domains that are going to be loaded from the config. - This will allow us to properly handle after_dependencies. + This allow us to: + - Properly handle after_dependencies. + - Keep track of domains which will load but have not yet finished loading """ hass.data[DATA_SETUP_DONE] = {domain: asyncio.Event() for domain in domains} @@ -265,7 +283,7 @@ async def _async_setup_component( await asyncio.sleep(0) await hass.config_entries.flow.async_wait_init_flow_finish(domain) - # Add to components before the async_setup + # Add to components before the entry.async_setup # call to avoid a deadlock when forwarding platforms hass.config.components.add(domain) diff --git a/tests/components/shelly/test_device_trigger.py b/tests/components/shelly/test_device_trigger.py index d5881696bf6..ec74745da15 100644 --- a/tests/components/shelly/test_device_trigger.py +++ b/tests/components/shelly/test_device_trigger.py @@ -240,9 +240,14 @@ async def test_if_fires_on_click_event_rpc_device(hass, calls, mock_rpc_device): assert calls[0].data["some"] == "test_trigger_single_push" -async def test_validate_trigger_block_device_not_ready(hass, calls, mock_block_device): +async def test_validate_trigger_block_device_not_ready( + hass, calls, mock_block_device, monkeypatch +): """Test validate trigger config when block device is not ready.""" - await init_integration(hass, 1) + monkeypatch.setattr(mock_block_device, "initialized", False) + entry = await init_integration(hass, 1) + dev_reg = async_get_dev_reg(hass) + device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0] assert await async_setup_component( hass, @@ -253,7 +258,7 @@ async def test_validate_trigger_block_device_not_ready(hass, calls, mock_block_d "trigger": { CONF_PLATFORM: "device", CONF_DOMAIN: DOMAIN, - CONF_DEVICE_ID: "device_not_ready", + CONF_DEVICE_ID: device.id, CONF_TYPE: "single", CONF_SUBTYPE: "button1", }, @@ -266,7 +271,7 @@ async def test_validate_trigger_block_device_not_ready(hass, calls, mock_block_d }, ) message = { - CONF_DEVICE_ID: "device_not_ready", + CONF_DEVICE_ID: device.id, ATTR_CLICK_TYPE: "single", ATTR_CHANNEL: 1, } @@ -277,8 +282,15 @@ async def test_validate_trigger_block_device_not_ready(hass, calls, mock_block_d assert calls[0].data["some"] == "test_trigger_single_click" -async def test_validate_trigger_rpc_device_not_ready(hass, calls, mock_rpc_device): +async def test_validate_trigger_rpc_device_not_ready( + hass, calls, mock_rpc_device, monkeypatch +): """Test validate trigger config when RPC device is not ready.""" + monkeypatch.setattr(mock_rpc_device, "initialized", False) + entry = await init_integration(hass, 2) + dev_reg = async_get_dev_reg(hass) + device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0] + assert await async_setup_component( hass, automation.DOMAIN, @@ -288,7 +300,7 @@ async def test_validate_trigger_rpc_device_not_ready(hass, calls, mock_rpc_devic "trigger": { CONF_PLATFORM: "device", CONF_DOMAIN: DOMAIN, - CONF_DEVICE_ID: "device_not_ready", + CONF_DEVICE_ID: device.id, CONF_TYPE: "single_push", CONF_SUBTYPE: "button1", }, @@ -301,7 +313,7 @@ async def test_validate_trigger_rpc_device_not_ready(hass, calls, mock_rpc_devic }, ) message = { - CONF_DEVICE_ID: "device_not_ready", + CONF_DEVICE_ID: device.id, ATTR_CLICK_TYPE: "single_push", ATTR_CHANNEL: 1, } diff --git a/tests/components/webostv/test_device_trigger.py b/tests/components/webostv/test_device_trigger.py index db15ce3a592..befa62340a5 100644 --- a/tests/components/webostv/test_device_trigger.py +++ b/tests/components/webostv/test_device_trigger.py @@ -98,41 +98,6 @@ async def test_if_fires_on_turn_on_request(hass, calls, client): assert calls[1].data["id"] == 0 -async def test_get_triggers_for_invalid_device_id(hass, caplog): - """Test error raised for invalid shelly device_id.""" - await async_setup_component(hass, "persistent_notification", {}) - - assert await async_setup_component( - hass, - automation.DOMAIN, - { - automation.DOMAIN: [ - { - "trigger": { - "platform": "device", - "domain": DOMAIN, - "device_id": "invalid_device_id", - "type": "webostv.turn_on", - }, - "action": { - "service": "test.automation", - "data_template": { - "some": "{{ trigger.invalid_device }}", - "id": "{{ trigger.id }}", - }, - }, - } - ] - }, - ) - await hass.async_block_till_done() - - assert ( - "Invalid config for [automation]: Device invalid_device_id is not a valid webostv device" - in caplog.text - ) - - async def test_failure_scenarios(hass, client): """Test failure scenarios.""" await setup_webostv(hass) @@ -173,7 +138,3 @@ async def test_failure_scenarios(hass, client): # Test that device id from non webostv domain raises exception with pytest.raises(InvalidDeviceAutomationConfig): await device_trigger.async_validate_trigger_config(hass, config) - - # Test no exception if device is not loaded - await hass.config_entries.async_unload(entry.entry_id) - assert await device_trigger.async_validate_trigger_config(hass, config) == config diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 28c3f9c2803..bdcd5a37651 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -25,7 +25,7 @@ from homeassistant.exceptions import ( ) from homeassistant.helpers import entity_registry as er from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from homeassistant.setup import async_setup_component +from homeassistant.setup import async_set_domains_to_be_loaded, async_setup_component from homeassistant.util import dt from tests.common import ( @@ -3461,3 +3461,67 @@ async def test_get_active_flows(hass): iter(entry.async_get_active_flows(hass, {config_entries.SOURCE_USER})), None ) assert active_user_flow is None + + +async def test_async_wait_component_dynamic(hass: HomeAssistant): + """Test async_wait_component for a config entry which is dynamically loaded.""" + entry = MockConfigEntry(title="test_title", domain="test") + + mock_setup_entry = AsyncMock(return_value=True) + mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry)) + mock_entity_platform(hass, "config_flow.test", None) + + entry.add_to_hass(hass) + + # The config entry is not loaded, and is also not scheduled to load + assert await hass.config_entries.async_wait_component(entry) is False + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + # The config entry is loaded + assert await hass.config_entries.async_wait_component(entry) is True + + +async def test_async_wait_component_startup(hass: HomeAssistant): + """Test async_wait_component for a config entry which is loaded at startup.""" + entry = MockConfigEntry(title="test_title", domain="test") + + setup_stall = asyncio.Event() + setup_started = asyncio.Event() + + async def mock_setup(hass: HomeAssistant, _) -> bool: + setup_started.set() + await setup_stall.wait() + return True + + mock_setup_entry = AsyncMock(return_value=True) + mock_integration( + hass, + MockModule("test", async_setup=mock_setup, async_setup_entry=mock_setup_entry), + ) + mock_entity_platform(hass, "config_flow.test", None) + + entry.add_to_hass(hass) + + # The config entry is not loaded, and is also not scheduled to load + assert await hass.config_entries.async_wait_component(entry) is False + + # Mark the component as scheduled to be loaded + async_set_domains_to_be_loaded(hass, {"test"}) + + # Start loading the component, including its config entries + hass.async_create_task(async_setup_component(hass, "test", {})) + await setup_started.wait() + + # The component is not yet loaded + assert "test" not in hass.config.components + + # Allow setup to proceed + setup_stall.set() + + # The component is scheduled to load, this will block until the config entry is loaded + assert await hass.config_entries.async_wait_component(entry) is True + + # The component has been loaded + assert "test" in hass.config.components From f82c4c763395ad832a76e227e93d3b94ff2bcda4 Mon Sep 17 00:00:00 2001 From: Daan Beverdam Date: Thu, 17 Nov 2022 21:57:26 +0100 Subject: [PATCH 0534/1033] Fix moving average for 0 values (#80476) --- homeassistant/components/filter/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/filter/sensor.py b/homeassistant/components/filter/sensor.py index 4c9c83a8787..ce70ed14d19 100644 --- a/homeassistant/components/filter/sensor.py +++ b/homeassistant/components/filter/sensor.py @@ -612,7 +612,7 @@ class TimeSMAFilter(Filter, SensorEntity): moving_sum = 0 start = new_state.timestamp - self._time_window - prev_state = self.last_leak or self.queue[0] + prev_state = self.last_leak if self.last_leak is not None else self.queue[0] for state in self.queue: moving_sum += (state.timestamp - start).total_seconds() * prev_state.state start = state.timestamp From 7c2e7863d24e0a110737bd0a1aab033f71a63f38 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 17 Nov 2022 22:55:31 +0100 Subject: [PATCH 0535/1033] Remove legacy YAML support from MQTT (#82102) --- homeassistant/components/mqtt/__init__.py | 5 +- .../components/mqtt/alarm_control_panel.py | 20 +- .../components/mqtt/binary_sensor.py | 20 +- homeassistant/components/mqtt/button.py | 20 +- homeassistant/components/mqtt/camera.py | 20 +- homeassistant/components/mqtt/climate.py | 21 +- homeassistant/components/mqtt/const.py | 1 - homeassistant/components/mqtt/cover.py | 21 +- .../mqtt/device_tracker/__init__.py | 28 +- .../mqtt/device_tracker/schema_yaml.py | 111 ----- homeassistant/components/mqtt/fan.py | 22 +- homeassistant/components/mqtt/humidifier.py | 22 +- .../components/mqtt/light/__init__.py | 29 +- homeassistant/components/mqtt/lock.py | 20 +- homeassistant/components/mqtt/mixins.py | 32 +- homeassistant/components/mqtt/number.py | 21 +- homeassistant/components/mqtt/scene.py | 20 +- homeassistant/components/mqtt/select.py | 19 - homeassistant/components/mqtt/sensor.py | 21 - homeassistant/components/mqtt/siren.py | 20 +- homeassistant/components/mqtt/switch.py | 20 +- .../components/mqtt/vacuum/__init__.py | 25 +- .../mqtt/test_alarm_control_panel.py | 28 -- tests/components/mqtt/test_binary_sensor.py | 43 +- tests/components/mqtt/test_button.py | 28 -- tests/components/mqtt/test_camera.py | 29 -- tests/components/mqtt/test_climate.py | 28 -- tests/components/mqtt/test_common.py | 95 +--- tests/components/mqtt/test_cover.py | 29 -- tests/components/mqtt/test_device_tracker.py | 405 ------------------ tests/components/mqtt/test_fan.py | 28 -- tests/components/mqtt/test_humidifier.py | 28 -- tests/components/mqtt/test_init.py | 28 +- tests/components/mqtt/test_legacy_vacuum.py | 30 +- tests/components/mqtt/test_light.py | 28 -- tests/components/mqtt/test_light_json.py | 28 -- tests/components/mqtt/test_light_template.py | 28 -- tests/components/mqtt/test_lock.py | 29 -- tests/components/mqtt/test_number.py | 29 -- tests/components/mqtt/test_scene.py | 28 -- tests/components/mqtt/test_select.py | 28 -- tests/components/mqtt/test_sensor.py | 28 -- tests/components/mqtt/test_siren.py | 28 -- tests/components/mqtt/test_state_vacuum.py | 38 +- tests/components/mqtt/test_switch.py | 28 -- tests/components/mqtt/test_update.py | 10 + 46 files changed, 54 insertions(+), 1613 deletions(-) delete mode 100644 homeassistant/components/mqtt/device_tracker/schema_yaml.py delete mode 100644 tests/components/mqtt/test_device_tracker.py diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index f7c9e5fbe86..0a2ee1eea9d 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -192,7 +192,7 @@ async def _async_setup_discovery( async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Start the MQTT protocol service.""" + """Set up the MQTT protocol service.""" mqtt_data = get_mqtt_data(hass, True) conf: ConfigType | None = config.get(DOMAIN) @@ -464,9 +464,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def _reload_config(call: ServiceCall) -> None: """Reload the platforms.""" - # Reload the legacy yaml platform - await async_reload_integration_platforms(hass, DOMAIN, RELOADABLE_PLATFORMS) - # Reload the modern yaml platforms mqtt_platforms = async_get_platforms(hass, DOMAIN) tasks = [ diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index 136fe79752e..41021fe0d37 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -45,7 +45,6 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import MqttCommandTemplate, MqttValueTemplate, ReceiveMessage @@ -114,31 +113,14 @@ PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend( ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) # Configuring MQTT alarm control panels under the alarm_control_panel platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), warn_for_legacy_schema(alarm.DOMAIN), ) DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT alarm control panel configured under the alarm_control_panel key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - alarm.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 82818c5b706..12b402065ce 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -43,7 +43,6 @@ from .mixins import ( MqttAvailability, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import MqttValueTemplate, ReceiveMessage @@ -71,31 +70,14 @@ PLATFORM_SCHEMA_MODERN = MQTT_RO_SCHEMA.extend( ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) # Configuring MQTT Binary sensors under the binary_sensor platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), warn_for_legacy_schema(binary_sensor.DOMAIN), ) DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT binary sensor configured under the fan platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - binary_sensor.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/button.py b/homeassistant/components/mqtt/button.py index d2743560411..71f59b8cda7 100644 --- a/homeassistant/components/mqtt/button.py +++ b/homeassistant/components/mqtt/button.py @@ -26,7 +26,6 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import MqttCommandTemplate @@ -48,8 +47,8 @@ PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend( ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) # Configuring MQTT Buttons under the button platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), warn_for_legacy_schema(button.DOMAIN), ) @@ -57,23 +56,6 @@ PLATFORM_SCHEMA = vol.All( DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT button configured under the fan platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - button.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index 8f52212172d..26688756aa4 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -24,7 +24,6 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import ReceiveMessage @@ -58,31 +57,14 @@ PLATFORM_SCHEMA_MODERN = vol.All( ) # Configuring MQTT Camera under the camera platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_BASE.schema), warn_for_legacy_schema(camera.DOMAIN), ) DISCOVERY_SCHEMA = PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT camera configured under the camera platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - camera.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 8ce43cac752..1c3e48efd74 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -53,7 +53,6 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import ( @@ -309,9 +308,8 @@ PLATFORM_SCHEMA_MODERN = vol.All( ) # Configuring MQTT Climate under the climate platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), - valid_preset_mode_configuration, warn_for_legacy_schema(climate.DOMAIN), ) @@ -334,23 +332,6 @@ DISCOVERY_SCHEMA = vol.All( ) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT climate configured under the fan platform key (deprecated).""" - # The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - climate.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index 87da9ab7979..cbcc23bb1a9 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -32,7 +32,6 @@ CONF_TLS_INSECURE = "tls_insecure" CONF_TLS_VERSION = "tls_version" DATA_MQTT = "mqtt" -MQTT_DATA_DEVICE_TRACKER_LEGACY = "mqtt_device_tracker_legacy" DEFAULT_PREFIX = "homeassistant" DEFAULT_BIRTH_WILL_TOPIC = DEFAULT_PREFIX + "/status" diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 6016f4cf96e..c0bc92c42f5 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -48,7 +48,6 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import MqttCommandTemplate, MqttValueTemplate, ReceiveMessage @@ -206,9 +205,8 @@ PLATFORM_SCHEMA_MODERN = vol.All( ) # Configuring MQTT Covers under the cover platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), - validate_options, warn_for_legacy_schema(cover.DOMAIN), ) @@ -219,23 +217,6 @@ DISCOVERY_SCHEMA = vol.All( ) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT covers configured under the fan platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - cover.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/device_tracker/__init__.py b/homeassistant/components/mqtt/device_tracker/__init__.py index 342817e38cc..6be4908403b 100644 --- a/homeassistant/components/mqtt/device_tracker/__init__.py +++ b/homeassistant/components/mqtt/device_tracker/__init__.py @@ -6,23 +6,15 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from ..const import MQTT_DATA_DEVICE_TRACKER_LEGACY from ..mixins import warn_for_legacy_schema -from .schema_discovery import PLATFORM_SCHEMA_MODERN # noqa: F401 -from .schema_discovery import async_setup_entry_from_discovery -from .schema_yaml import ( - PLATFORM_SCHEMA_YAML, - MQTTLegacyDeviceTrackerData, - async_setup_scanner_from_yaml, +from .schema_discovery import ( # noqa: F401 + PLATFORM_SCHEMA_MODERN, + async_setup_entry_from_discovery, ) # Configuring MQTT Device Trackers under the device_tracker platform key is deprecated in HA Core 2022.6 -PLATFORM_SCHEMA = vol.All( - PLATFORM_SCHEMA_YAML, warn_for_legacy_schema(device_tracker.DOMAIN) -) - -# Legacy setup -async_setup_scanner = async_setup_scanner_from_yaml +# Setup for the legacy YAML format was removed in HA Core 2022.12 +PLATFORM_SCHEMA = vol.All(warn_for_legacy_schema(device_tracker.DOMAIN)) async def async_setup_entry( @@ -32,13 +24,3 @@ async def async_setup_entry( ) -> None: """Set up MQTT device_tracker through configuration.yaml and dynamically through MQTT discovery.""" await async_setup_entry_from_discovery(hass, config_entry, async_add_entities) - # (re)load legacy service - if MQTT_DATA_DEVICE_TRACKER_LEGACY in hass.data: - yaml_device_tracker_data: MQTTLegacyDeviceTrackerData = hass.data[ - MQTT_DATA_DEVICE_TRACKER_LEGACY - ] - await async_setup_scanner_from_yaml( - hass, - config=yaml_device_tracker_data.config, - async_see=yaml_device_tracker_data.async_see, - ) diff --git a/homeassistant/components/mqtt/device_tracker/schema_yaml.py b/homeassistant/components/mqtt/device_tracker/schema_yaml.py deleted file mode 100644 index d88a82e3002..00000000000 --- a/homeassistant/components/mqtt/device_tracker/schema_yaml.py +++ /dev/null @@ -1,111 +0,0 @@ -"""Support for tracking MQTT enabled devices defined in YAML.""" -from __future__ import annotations - -from collections.abc import Callable, Coroutine -import dataclasses -import logging -from typing import Any - -import voluptuous as vol - -from homeassistant.components.device_tracker import ( - PLATFORM_SCHEMA, - SOURCE_TYPES, - AsyncSeeCallback, - SourceType, -) -from homeassistant.const import CONF_DEVICES, STATE_HOME, STATE_NOT_HOME -from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -from ... import mqtt -from ..client import async_subscribe -from ..config import SCHEMA_BASE -from ..const import CONF_QOS, MQTT_DATA_DEVICE_TRACKER_LEGACY -from ..models import ReceiveMessage -from ..util import mqtt_config_entry_enabled, valid_subscribe_topic - -_LOGGER = logging.getLogger(__name__) - -CONF_PAYLOAD_HOME = "payload_home" -CONF_PAYLOAD_NOT_HOME = "payload_not_home" -CONF_SOURCE_TYPE = "source_type" - -PLATFORM_SCHEMA_YAML = PLATFORM_SCHEMA.extend(SCHEMA_BASE).extend( - { - vol.Required(CONF_DEVICES): {cv.string: valid_subscribe_topic}, - vol.Optional(CONF_PAYLOAD_HOME, default=STATE_HOME): cv.string, - vol.Optional(CONF_PAYLOAD_NOT_HOME, default=STATE_NOT_HOME): cv.string, - vol.Optional(CONF_SOURCE_TYPE): vol.In(SOURCE_TYPES), - } -) - - -@dataclasses.dataclass -class MQTTLegacyDeviceTrackerData: - """Class to hold device tracker data.""" - - async_see: Callable[..., Coroutine[Any, Any, None]] - config: ConfigType - - -async def async_setup_scanner_from_yaml( - hass: HomeAssistant, - config: ConfigType, - async_see: AsyncSeeCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> bool: - """Set up the MQTT tracker.""" - devices: dict[str, str] = config[CONF_DEVICES] - qos: int = config[CONF_QOS] - payload_home: str = config[CONF_PAYLOAD_HOME] - payload_not_home: str = config[CONF_PAYLOAD_NOT_HOME] - source_type: SourceType | str | None = config.get(CONF_SOURCE_TYPE) - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] - subscriptions: list[Callable] = [] - - hass.data[MQTT_DATA_DEVICE_TRACKER_LEGACY] = MQTTLegacyDeviceTrackerData( - async_see, config - ) - if not mqtt_config_entry_enabled(hass): - _LOGGER.info( - "MQTT device trackers will be not available until the config entry is enabled", - ) - return False - - @callback - def _entry_unload(*_: Any) -> None: - """Handle the unload of the config entry.""" - # Unsubscribe from mqtt - for unsubscribe in subscriptions: - unsubscribe() - - for dev_id, topic in devices.items(): - - @callback - def async_message_received(msg: ReceiveMessage, dev_id: str = dev_id) -> None: - """Handle received MQTT message.""" - if msg.payload == payload_home: - location_name = STATE_HOME - elif msg.payload == payload_not_home: - location_name = STATE_NOT_HOME - else: - location_name = str(msg.payload) - - see_args: dict[str, Any] = { - "dev_id": dev_id, - "location_name": location_name, - } - if source_type: - see_args["source_type"] = source_type - - hass.async_create_task(async_see(**see_args)) - - subscriptions.append( - await async_subscribe(hass, topic, async_message_received, qos) - ) - - config_entry.async_on_unload(_entry_unload) - - return True diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 8a65b909eb8..ef5386664f5 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -53,7 +53,6 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import ( @@ -185,10 +184,8 @@ _PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend( ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) # Configuring MQTT Fans under the fan platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), - valid_speed_range_configuration, - valid_preset_mode_configuration, warn_for_legacy_schema(fan.DOMAIN), ) @@ -225,23 +222,6 @@ DISCOVERY_SCHEMA = vol.All( ) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT fans configured under the fan platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - fan.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/humidifier.py b/homeassistant/components/mqtt/humidifier.py index c8bb469ed2e..d386aade32a 100644 --- a/homeassistant/components/mqtt/humidifier.py +++ b/homeassistant/components/mqtt/humidifier.py @@ -49,7 +49,6 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import ( @@ -151,10 +150,8 @@ _PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend( ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) # Configuring MQTT Humidifiers under the humidifier platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), - valid_humidity_range_configuration, - valid_mode_configuration, warn_for_legacy_schema(humidifier.DOMAIN), ) @@ -171,23 +168,6 @@ DISCOVERY_SCHEMA = vol.All( ) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT humidifier configured under the fan platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - humidifier.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index cb43789c6ec..5860a011ab5 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -9,15 +9,10 @@ import voluptuous as vol from homeassistant.components import light from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.helpers.typing import ConfigType -from ..mixins import ( - async_setup_entry_helper, - async_setup_platform_helper, - warn_for_legacy_schema, -) +from ..mixins import async_setup_entry_helper, warn_for_legacy_schema from .schema import CONF_SCHEMA, MQTT_LIGHT_SCHEMA_SCHEMA from .schema_basic import ( DISCOVERY_SCHEMA_BASIC, @@ -78,9 +73,8 @@ DISCOVERY_SCHEMA = vol.All( ) # Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema, extra=vol.ALLOW_EXTRA), - validate_mqtt_light, warn_for_legacy_schema(light.DOMAIN), ) @@ -90,23 +84,6 @@ PLATFORM_SCHEMA_MODERN = vol.All( ) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT light through configuration.yaml (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - light.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index e141fcbd693..ec1a3f2a897 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -30,7 +30,6 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import MqttValueTemplate, ReceiveMessage, ReceivePayloadType @@ -72,31 +71,14 @@ PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) # Configuring MQTT Locks under the lock platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), warn_for_legacy_schema(lock.DOMAIN), ) DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT locks configured under the lock platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - lock.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 400413c73d7..3cb208f3adc 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -240,10 +240,11 @@ def warn_for_legacy_schema(domain: str) -> Callable[[ConfigType], ConfigType]: """Return a validator.""" nonlocal warned + # Logged error and repair can be removed from HA 2023.6 if domain in warned: return config - _LOGGER.warning( + _LOGGER.error( "Manually configured MQTT %s(s) found under platform key '%s', " "please move to the mqtt integration key, see " "https://www.home-assistant.io/integrations/%s.mqtt/#new_format", @@ -259,7 +260,7 @@ def warn_for_legacy_schema(domain: str) -> Callable[[ConfigType], ConfigType]: f"deprecated_yaml_{domain}", breaks_in_ha_version="2022.12.0", # Warning first added in 2022.6.0 is_fixable=False, - severity=IssueSeverity.WARNING, + severity=IssueSeverity.ERROR, translation_key="deprecated_yaml", translation_placeholders={ "more_info_url": f"https://www.home-assistant.io/integrations/{domain}.mqtt/#new_format", @@ -366,33 +367,6 @@ async def async_setup_entry_helper( await _async_setup_entities() -async def async_setup_platform_helper( - hass: HomeAssistant, - platform_domain: str, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - async_setup_entities: SetupEntity, -) -> None: - """Help to set up the platform for manual configured MQTT entities.""" - mqtt_data = get_mqtt_data(hass) - if mqtt_data.reload_entry: - _LOGGER.debug( - "MQTT integration is %s, skipping setup of manually configured MQTT items while unloading the config entry", - platform_domain, - ) - return - if not (entry_status := mqtt_config_entry_enabled(hass)): - _LOGGER.warning( - "MQTT integration is %s, skipping setup of manually configured MQTT %s", - "not setup" if entry_status is None else "disabled", - platform_domain, - ) - return - # Ensure we set config_entry when entries are set up to enable clean up - config_entry: ConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] - await async_setup_entities(hass, async_add_entities, config, config_entry) - - def init_entity_id_from_config( hass: HomeAssistant, entity: Entity, config: ConfigType, entity_id_format: str ) -> None: diff --git a/homeassistant/components/mqtt/number.py b/homeassistant/components/mqtt/number.py index 51b480c036e..b81ce113a88 100644 --- a/homeassistant/components/mqtt/number.py +++ b/homeassistant/components/mqtt/number.py @@ -45,7 +45,6 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import ( @@ -109,9 +108,8 @@ PLATFORM_SCHEMA_MODERN = vol.All( ) # Configuring MQTT Number under the number platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), - validate_config, warn_for_legacy_schema(number.DOMAIN), ) @@ -121,23 +119,6 @@ DISCOVERY_SCHEMA = vol.All( ) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT number configured under the number platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - number.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/scene.py b/homeassistant/components/mqtt/scene.py index 9eafd0cdd99..f671daea2b2 100644 --- a/homeassistant/components/mqtt/scene.py +++ b/homeassistant/components/mqtt/scene.py @@ -24,7 +24,6 @@ from .mixins import ( MQTT_AVAILABILITY_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .util import valid_publish_topic @@ -47,31 +46,14 @@ PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend( ).extend(MQTT_AVAILABILITY_SCHEMA.schema) # Configuring MQTT Scenes under the scene platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), warn_for_legacy_schema(scene.DOMAIN), ) DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT scene configured under the scene platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - scene.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/select.py b/homeassistant/components/mqtt/select.py index 6dfe5081e74..c7e00420cbc 100644 --- a/homeassistant/components/mqtt/select.py +++ b/homeassistant/components/mqtt/select.py @@ -32,7 +32,6 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import ( @@ -70,30 +69,12 @@ PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( # Configuring MQTT Select under the select platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), warn_for_legacy_schema(select.DOMAIN), ) DISCOVERY_SCHEMA = vol.All(PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA)) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT select configured under the select platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - select.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index ed65b5a42fe..fd42d5539ec 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -45,7 +45,6 @@ from .mixins import ( MqttAvailability, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import ( @@ -117,9 +116,6 @@ PLATFORM_SCHEMA_MODERN = vol.All( # Configuring MQTT Sensors under the sensor platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( - cv.deprecated(CONF_LAST_RESET_TOPIC), - cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), - validate_options, warn_for_legacy_schema(sensor.DOMAIN), ) @@ -130,23 +126,6 @@ DISCOVERY_SCHEMA = vol.All( ) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT sensors configured under the fan platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - sensor.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/siren.py b/homeassistant/components/mqtt/siren.py index 8b622d207fa..0e6c3c3dec1 100644 --- a/homeassistant/components/mqtt/siren.py +++ b/homeassistant/components/mqtt/siren.py @@ -53,7 +53,6 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import ( @@ -99,8 +98,8 @@ PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) # Configuring MQTT Sirens under the siren platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), warn_for_legacy_schema(siren.DOMAIN), ) @@ -126,23 +125,6 @@ SUPPORTED_ATTRIBUTES = { _LOGGER = logging.getLogger(__name__) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT sirens configured under the fan platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - siren.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index a20603e2399..ffcb829e3a7 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -41,7 +41,6 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper, - async_setup_platform_helper, warn_for_legacy_schema, ) from .models import MqttValueTemplate, ReceiveMessage @@ -68,31 +67,14 @@ PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) # Configuring MQTT Switches under the switch platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), warn_for_legacy_schema(switch.DOMAIN), ) DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT switch configured under the fan platform key (deprecated).""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - switch.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/homeassistant/components/mqtt/vacuum/__init__.py b/homeassistant/components/mqtt/vacuum/__init__.py index 98183a92911..87b76930312 100644 --- a/homeassistant/components/mqtt/vacuum/__init__.py +++ b/homeassistant/components/mqtt/vacuum/__init__.py @@ -11,9 +11,8 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from ..const import CONF_SCHEMA -from ..mixins import async_setup_entry_helper, async_setup_platform_helper -from .schema import LEGACY, MQTT_VACUUM_SCHEMA, STATE +from ..mixins import async_setup_entry_helper, warn_for_legacy_schema +from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE from .schema_legacy import ( DISCOVERY_SCHEMA_LEGACY, PLATFORM_SCHEMA_LEGACY, @@ -58,8 +57,9 @@ DISCOVERY_SCHEMA = vol.All( ) # Configuring MQTT Vacuums under the vacuum platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( - MQTT_VACUUM_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_vacuum + warn_for_legacy_schema(vacuum.DOMAIN), ) PLATFORM_SCHEMA_MODERN = vol.All( @@ -67,23 +67,6 @@ PLATFORM_SCHEMA_MODERN = vol.All( ) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up MQTT vacuum through configuration.yaml.""" - # Deprecated in HA Core 2022.6 - await async_setup_platform_helper( - hass, - vacuum.DOMAIN, - discovery_info or config, - async_add_entities, - _async_setup_entity, - ) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, diff --git a/tests/components/mqtt/test_alarm_control_panel.py b/tests/components/mqtt/test_alarm_control_panel.py index d305d2ae7aa..5d9ed872beb 100644 --- a/tests/components/mqtt/test_alarm_control_panel.py +++ b/tests/components/mqtt/test_alarm_control_panel.py @@ -54,7 +54,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -117,11 +116,6 @@ DEFAULT_CONFIG_REMOTE_CODE_TEXT = { } } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[alarm_control_panel.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def alarm_control_panel_platform_only(): @@ -991,15 +985,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = alarm_control_panel.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - async def test_setup_manual_entity_from_yaml(hass): """Test setup manual configured MQTT entity.""" platform = alarm_control_panel.DOMAIN @@ -1014,16 +999,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = alarm_control_panel.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_binary_sensor.py b/tests/components/mqtt/test_binary_sensor.py index 48acde5c6c9..91607de9343 100644 --- a/tests/components/mqtt/test_binary_sensor.py +++ b/tests/components/mqtt/test_binary_sensor.py @@ -39,7 +39,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_reload_with_config, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setup_manual_entity_from_yaml, @@ -64,11 +63,6 @@ DEFAULT_CONFIG = { } } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[binary_sensor.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def binary_sensor_platform_only(): @@ -117,14 +111,15 @@ async def test_setting_sensor_value_expires( """Test the expiration of the value.""" assert await async_setup_component( hass, - binary_sensor.DOMAIN, + mqtt.DOMAIN, { - binary_sensor.DOMAIN: { - "platform": "mqtt", - "name": "test", - "state_topic": "test-topic", - "expire_after": 4, - "force_update": True, + mqtt.DOMAIN: { + binary_sensor.DOMAIN: { + "name": "test", + "state_topic": "test-topic", + "expire_after": 4, + "force_update": True, + } } }, ) @@ -1016,15 +1011,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = binary_sensor.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - @pytest.mark.parametrize( "payload1, state1, payload2, state2", [("ON", "on", "OFF", "off"), ("OFF", "off", "ON", "on")], @@ -1138,16 +1124,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = binary_sensor.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_button.py b/tests/components/mqtt/test_button.py index 80e5ce60a47..e9e209358da 100644 --- a/tests/components/mqtt/test_button.py +++ b/tests/components/mqtt/test_button.py @@ -31,7 +31,6 @@ from .test_common import ( help_test_entity_id_update_discovery_update, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -46,11 +45,6 @@ DEFAULT_CONFIG = { mqtt.DOMAIN: {button.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[button.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def button_platform_only(): @@ -484,15 +478,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = button.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - async def test_setup_manual_entity_from_yaml(hass): """Test setup manual configured MQTT entity.""" platform = button.DOMAIN @@ -507,16 +492,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = button.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_camera.py b/tests/components/mqtt/test_camera.py index 5b8ad747244..20060c196b4 100644 --- a/tests/components/mqtt/test_camera.py +++ b/tests/components/mqtt/test_camera.py @@ -1,6 +1,5 @@ """The tests for mqtt camera component.""" from base64 import b64encode -import copy from http import HTTPStatus import json from unittest.mock import patch @@ -30,7 +29,6 @@ from .test_common import ( help_test_entity_id_update_discovery_update, help_test_entity_id_update_subscriptions, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -45,11 +43,6 @@ from tests.common import async_fire_mqtt_message DEFAULT_CONFIG = {mqtt.DOMAIN: {camera.DOMAIN: {"name": "test", "topic": "test_topic"}}} -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[camera.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def camera_platform_only(): @@ -386,15 +379,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = camera.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - async def test_setup_manual_entity_from_yaml(hass): """Test setup manual configured MQTT entity.""" platform = camera.DOMAIN @@ -409,16 +393,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = camera.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_climate.py b/tests/components/mqtt/test_climate.py index d7d278be160..eb4c71dd254 100644 --- a/tests/components/mqtt/test_climate.py +++ b/tests/components/mqtt/test_climate.py @@ -46,7 +46,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -88,11 +87,6 @@ DEFAULT_CONFIG = { } } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[climate.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def climate_platform_only(): @@ -1406,15 +1400,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = climate.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - async def test_setup_manual_entity_from_yaml(hass): """Test setup manual configured MQTT entity.""" platform = climate.DOMAIN @@ -1429,16 +1414,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = climate.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_common.py b/tests/components/mqtt/test_common.py index a9cfb88bfb6..e5880b981a2 100644 --- a/tests/components/mqtt/test_common.py +++ b/tests/components/mqtt/test_common.py @@ -1705,103 +1705,16 @@ async def help_test_reloadable( old_config_2 = copy.deepcopy(config) old_config_2["name"] = "test_old_2" - # Test deprecated YAML configuration under the platform key - # Scheduled to be removed in HA core 2022.12 - old_config_3 = copy.deepcopy(config) - old_config_3["name"] = "test_old_3" - old_config_3["platform"] = mqtt.DOMAIN - old_config_4 = copy.deepcopy(config) - old_config_4["name"] = "test_old_4" - old_config_4["platform"] = mqtt.DOMAIN - old_config = { mqtt.DOMAIN: {domain: [old_config_1, old_config_2]}, - domain: [old_config_3, old_config_4], } - assert await async_setup_component(hass, domain, old_config) assert await async_setup_component(hass, mqtt.DOMAIN, old_config) await hass.async_block_till_done() await mqtt_mock_entry_with_yaml_config() assert hass.states.get(f"{domain}.test_old_1") assert hass.states.get(f"{domain}.test_old_2") - assert hass.states.get(f"{domain}.test_old_3") - assert hass.states.get(f"{domain}.test_old_4") - assert len(hass.states.async_all(domain)) == 4 - - # Create temporary fixture for configuration.yaml based on the supplied config and - # test a reload with this new config - new_config_1 = copy.deepcopy(config) - new_config_1["name"] = "test_new_1" - new_config_2 = copy.deepcopy(config) - new_config_2["name"] = "test_new_2" - new_config_extra = copy.deepcopy(config) - new_config_extra["name"] = "test_new_5" - - # Test deprecated YAML configuration under the platform key - # Scheduled to be removed in HA core 2022.12 - new_config_3 = copy.deepcopy(config) - new_config_3["name"] = "test_new_3" - new_config_3["platform"] = mqtt.DOMAIN - new_config_4 = copy.deepcopy(config) - new_config_4["name"] = "test_new_4" - new_config_4["platform"] = mqtt.DOMAIN - new_config_extra_legacy = copy.deepcopy(config) - new_config_extra_legacy["name"] = "test_new_6" - new_config_extra_legacy["platform"] = mqtt.DOMAIN - - new_config = { - mqtt.DOMAIN: {domain: [new_config_1, new_config_2, new_config_extra]}, - domain: [new_config_3, new_config_4, new_config_extra_legacy], - } - - await help_test_reload_with_config(hass, caplog, tmp_path, new_config) - - assert len(hass.states.async_all(domain)) == 6 - - assert hass.states.get(f"{domain}.test_new_1") - assert hass.states.get(f"{domain}.test_new_2") - assert hass.states.get(f"{domain}.test_new_3") - assert hass.states.get(f"{domain}.test_new_4") - assert hass.states.get(f"{domain}.test_new_5") - assert hass.states.get(f"{domain}.test_new_6") - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def help_test_reloadable_late(hass, caplog, tmp_path, domain, config): - """Test reloading an MQTT platform when config entry is setup is late.""" - # Create and test an old config of 2 entities based on the config supplied - # using the deprecated platform schema - old_config_1 = copy.deepcopy(config) - old_config_1["name"] = "test_old_1" - old_config_2 = copy.deepcopy(config) - old_config_2["name"] = "test_old_2" - - old_yaml_config_file = tmp_path / "configuration.yaml" - old_yaml_config = yaml.dump({domain: [old_config_1, old_config_2]}) - old_yaml_config_file.write_text(old_yaml_config) - assert old_yaml_config_file.read_text() == old_yaml_config - - assert await async_setup_component( - hass, domain, {domain: [old_config_1, old_config_2]} - ) - await hass.async_block_till_done() - - # No MQTT config entry, there should be a warning and no entities - assert ( - "MQTT integration is not setup, skipping setup of manually " - f"configured MQTT {domain}" - ) in caplog.text - assert len(hass.states.async_all(domain)) == 0 - - # User sets up a config entry, should succeed and entities will setup - entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}) - entry.add_to_hass(hass) - with patch.object(hass_config, "YAML_CONFIG_FILE", old_yaml_config_file): - assert await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() assert len(hass.states.async_all(domain)) == 2 # Create temporary fixture for configuration.yaml based on the supplied config and @@ -1810,14 +1723,14 @@ async def help_test_reloadable_late(hass, caplog, tmp_path, domain, config): new_config_1["name"] = "test_new_1" new_config_2 = copy.deepcopy(config) new_config_2["name"] = "test_new_2" - new_config_3 = copy.deepcopy(config) - new_config_3["name"] = "test_new_3" + new_config_extra = copy.deepcopy(config) + new_config_extra["name"] = "test_new_3" new_config = { - domain: [new_config_1, new_config_2, new_config_3], + mqtt.DOMAIN: {domain: [new_config_1, new_config_2, new_config_extra]}, } + await help_test_reload_with_config(hass, caplog, tmp_path, new_config) - await hass.async_block_till_done() assert len(hass.states.async_all(domain)) == 3 diff --git a/tests/components/mqtt/test_cover.py b/tests/components/mqtt/test_cover.py index fb7df111d96..93656c6aae3 100644 --- a/tests/components/mqtt/test_cover.py +++ b/tests/components/mqtt/test_cover.py @@ -1,6 +1,5 @@ """The tests for the MQTT cover platform.""" -import copy from unittest.mock import patch import pytest @@ -67,7 +66,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -84,11 +82,6 @@ DEFAULT_CONFIG = { mqtt.DOMAIN: {cover.DOMAIN: {"name": "test", "state_topic": "test-topic"}} } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[cover.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def cover_platform_only(): @@ -3368,15 +3361,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = cover.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ @@ -3424,16 +3408,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = cover.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_device_tracker.py b/tests/components/mqtt/test_device_tracker.py deleted file mode 100644 index db6e0a292d7..00000000000 --- a/tests/components/mqtt/test_device_tracker.py +++ /dev/null @@ -1,405 +0,0 @@ -"""The tests for the MQTT device tracker platform using configuration.yaml with legacy schema.""" -import json -from unittest.mock import patch - -import pytest - -from homeassistant.components import device_tracker -from homeassistant.components.device_tracker import SourceType -from homeassistant.config_entries import ConfigEntryDisabler -from homeassistant.const import CONF_PLATFORM, STATE_HOME, STATE_NOT_HOME, Platform -from homeassistant.setup import async_setup_component - -from .test_common import ( - MockConfigEntry, - help_test_entry_reload_with_new_config, - help_test_unload_config_entry, -) - -from tests.common import async_fire_mqtt_message - - -@pytest.fixture(autouse=True) -def device_tracker_platform_only(): - """Only setup the device_tracker platform to speed up tests.""" - with patch("homeassistant.components.mqtt.PLATFORMS", [Platform.DEVICE_TRACKER]): - yield - - -# Deprecated in HA Core 2022.6 -async def test_legacy_ensure_device_tracker_platform_validation( - hass, mqtt_mock_entry_with_yaml_config -): - """Test if platform validation was done.""" - - async def mock_setup_scanner(hass, config, see, discovery_info=None): - """Check that Qos was added by validation.""" - assert "qos" in config - - with patch( - "homeassistant.components.mqtt.device_tracker.async_setup_scanner", - autospec=True, - side_effect=mock_setup_scanner, - ) as mock_sp: - - dev_id = "paulus" - topic = "/location/paulus" - assert await async_setup_component( - hass, - device_tracker.DOMAIN, - { - device_tracker.DOMAIN: { - CONF_PLATFORM: "mqtt", - "devices": {dev_id: topic}, - } - }, - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert mock_sp.call_count == 1 - - -# Deprecated in HA Core 2022.6 -async def test_legacy_new_message( - hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config -): - """Test new message.""" - await mqtt_mock_entry_no_yaml_config() - dev_id = "paulus" - entity_id = f"{device_tracker.DOMAIN}.{dev_id}" - topic = "/location/paulus" - location = "work" - - hass.config.components = {"mqtt", "zone"} - assert await async_setup_component( - hass, - device_tracker.DOMAIN, - {device_tracker.DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: topic}}}, - ) - async_fire_mqtt_message(hass, topic, location) - await hass.async_block_till_done() - assert hass.states.get(entity_id).state == location - - -# Deprecated in HA Core 2022.6 -async def test_legacy_single_level_wildcard_topic( - hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config -): - """Test single level wildcard topic.""" - await mqtt_mock_entry_no_yaml_config() - dev_id = "paulus" - entity_id = f"{device_tracker.DOMAIN}.{dev_id}" - subscription = "/location/+/paulus" - topic = "/location/room/paulus" - location = "work" - - hass.config.components = {"mqtt", "zone"} - assert await async_setup_component( - hass, - device_tracker.DOMAIN, - { - device_tracker.DOMAIN: { - CONF_PLATFORM: "mqtt", - "devices": {dev_id: subscription}, - } - }, - ) - async_fire_mqtt_message(hass, topic, location) - await hass.async_block_till_done() - assert hass.states.get(entity_id).state == location - - -# Deprecated in HA Core 2022.6 -async def test_legacy_multi_level_wildcard_topic( - hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config -): - """Test multi level wildcard topic.""" - await mqtt_mock_entry_no_yaml_config() - dev_id = "paulus" - entity_id = f"{device_tracker.DOMAIN}.{dev_id}" - subscription = "/location/#" - topic = "/location/room/paulus" - location = "work" - - hass.config.components = {"mqtt", "zone"} - assert await async_setup_component( - hass, - device_tracker.DOMAIN, - { - device_tracker.DOMAIN: { - CONF_PLATFORM: "mqtt", - "devices": {dev_id: subscription}, - } - }, - ) - async_fire_mqtt_message(hass, topic, location) - await hass.async_block_till_done() - assert hass.states.get(entity_id).state == location - - -# Deprecated in HA Core 2022.6 -async def test_legacy_single_level_wildcard_topic_not_matching( - hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config -): - """Test not matching single level wildcard topic.""" - await mqtt_mock_entry_no_yaml_config() - dev_id = "paulus" - entity_id = f"{device_tracker.DOMAIN}.{dev_id}" - subscription = "/location/+/paulus" - topic = "/location/paulus" - location = "work" - - hass.config.components = {"mqtt", "zone"} - assert await async_setup_component( - hass, - device_tracker.DOMAIN, - { - device_tracker.DOMAIN: { - CONF_PLATFORM: "mqtt", - "devices": {dev_id: subscription}, - } - }, - ) - async_fire_mqtt_message(hass, topic, location) - await hass.async_block_till_done() - assert hass.states.get(entity_id) is None - - -# Deprecated in HA Core 2022.6 -async def test_legacy_multi_level_wildcard_topic_not_matching( - hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config -): - """Test not matching multi level wildcard topic.""" - await mqtt_mock_entry_no_yaml_config() - dev_id = "paulus" - entity_id = f"{device_tracker.DOMAIN}.{dev_id}" - subscription = "/location/#" - topic = "/somewhere/room/paulus" - location = "work" - - hass.config.components = {"mqtt", "zone"} - assert await async_setup_component( - hass, - device_tracker.DOMAIN, - { - device_tracker.DOMAIN: { - CONF_PLATFORM: "mqtt", - "devices": {dev_id: subscription}, - } - }, - ) - async_fire_mqtt_message(hass, topic, location) - await hass.async_block_till_done() - assert hass.states.get(entity_id) is None - - -# Deprecated in HA Core 2022.6 -async def test_legacy_matching_custom_payload_for_home_and_not_home( - hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config -): - """Test custom payload_home sets state to home and custom payload_not_home sets state to not_home.""" - await mqtt_mock_entry_no_yaml_config() - dev_id = "paulus" - entity_id = f"{device_tracker.DOMAIN}.{dev_id}" - topic = "/location/paulus" - payload_home = "present" - payload_not_home = "not present" - - hass.config.components = {"mqtt", "zone"} - assert await async_setup_component( - hass, - device_tracker.DOMAIN, - { - device_tracker.DOMAIN: { - CONF_PLATFORM: "mqtt", - "devices": {dev_id: topic}, - "payload_home": payload_home, - "payload_not_home": payload_not_home, - } - }, - ) - async_fire_mqtt_message(hass, topic, payload_home) - await hass.async_block_till_done() - assert hass.states.get(entity_id).state == STATE_HOME - - async_fire_mqtt_message(hass, topic, payload_not_home) - await hass.async_block_till_done() - assert hass.states.get(entity_id).state == STATE_NOT_HOME - - -# Deprecated in HA Core 2022.6 -async def test_legacy_not_matching_custom_payload_for_home_and_not_home( - hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config -): - """Test not matching payload does not set state to home or not_home.""" - await mqtt_mock_entry_no_yaml_config() - dev_id = "paulus" - entity_id = f"{device_tracker.DOMAIN}.{dev_id}" - topic = "/location/paulus" - payload_home = "present" - payload_not_home = "not present" - payload_not_matching = "test" - - hass.config.components = {"mqtt", "zone"} - assert await async_setup_component( - hass, - device_tracker.DOMAIN, - { - device_tracker.DOMAIN: { - CONF_PLATFORM: "mqtt", - "devices": {dev_id: topic}, - "payload_home": payload_home, - "payload_not_home": payload_not_home, - } - }, - ) - async_fire_mqtt_message(hass, topic, payload_not_matching) - await hass.async_block_till_done() - assert hass.states.get(entity_id).state != STATE_HOME - assert hass.states.get(entity_id).state != STATE_NOT_HOME - - -# Deprecated in HA Core 2022.6 -async def test_legacy_matching_source_type( - hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config -): - """Test setting source type.""" - await mqtt_mock_entry_no_yaml_config() - dev_id = "paulus" - entity_id = f"{device_tracker.DOMAIN}.{dev_id}" - topic = "/location/paulus" - source_type = SourceType.BLUETOOTH - location = "work" - - hass.config.components = {"mqtt", "zone"} - assert await async_setup_component( - hass, - device_tracker.DOMAIN, - { - device_tracker.DOMAIN: { - CONF_PLATFORM: "mqtt", - "devices": {dev_id: topic}, - "source_type": source_type, - } - }, - ) - - async_fire_mqtt_message(hass, topic, location) - await hass.async_block_till_done() - assert hass.states.get(entity_id).attributes["source_type"] == SourceType.BLUETOOTH - - -# Deprecated in HA Core 2022.6 -async def test_unload_entry( - hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config, tmp_path -): - """Test unloading the config entry.""" - # setup through configuration.yaml - await mqtt_mock_entry_no_yaml_config() - dev_id = "jan" - entity_id = f"{device_tracker.DOMAIN}.{dev_id}" - topic = "/location/jan" - location = "home" - - hass.config.components = {"mqtt", "zone"} - assert await async_setup_component( - hass, - device_tracker.DOMAIN, - {device_tracker.DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: topic}}}, - ) - async_fire_mqtt_message(hass, topic, location) - await hass.async_block_till_done() - assert hass.states.get(entity_id).state == location - - # setup through discovery - dev_id = "piet" - subscription = "/location/#" - domain = device_tracker.DOMAIN - discovery_config = { - "devices": {dev_id: subscription}, - "state_topic": "some-state", - "name": "piet", - } - async_fire_mqtt_message( - hass, f"homeassistant/{domain}/bla/config", json.dumps(discovery_config) - ) - await hass.async_block_till_done() - - # check that both entities were created - config_setup_entity = hass.states.get(f"{domain}.jan") - assert config_setup_entity - - discovery_setup_entity = hass.states.get(f"{domain}.piet") - assert discovery_setup_entity - - await help_test_unload_config_entry(hass, tmp_path, {}) - await hass.async_block_till_done() - - # check that both entities were unsubscribed and that the location was not processed - async_fire_mqtt_message(hass, "some-state", "not_home") - async_fire_mqtt_message(hass, "location/jan", "not_home") - await hass.async_block_till_done() - - config_setup_entity = hass.states.get(f"{domain}.jan") - assert config_setup_entity.state == location - - # the discovered tracker is an entity which state is removed at unload - discovery_setup_entity = hass.states.get(f"{domain}.piet") - assert discovery_setup_entity is None - - -# Deprecated in HA Core 2022.6 -async def test_reload_entry_legacy( - hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config, tmp_path -): - """Test reloading the config entry with manual MQTT items.""" - # setup through configuration.yaml - await mqtt_mock_entry_no_yaml_config() - entity_id = f"{device_tracker.DOMAIN}.jan" - topic = "location/jan" - location = "home" - - config = { - device_tracker.DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {"jan": topic}}, - } - hass.config.components = {"mqtt", "zone"} - assert await async_setup_component(hass, device_tracker.DOMAIN, config) - await hass.async_block_till_done() - - async_fire_mqtt_message(hass, topic, location) - await hass.async_block_till_done() - assert hass.states.get(entity_id).state == location - - await help_test_entry_reload_with_new_config(hass, tmp_path, config) - await hass.async_block_till_done() - - location = "not_home" - async_fire_mqtt_message(hass, topic, location) - await hass.async_block_till_done() - assert hass.states.get(entity_id).state == location - - -# Deprecated in HA Core 2022.6 -async def test_setup_with_disabled_entry( - hass, mock_device_tracker_conf, caplog -) -> None: - """Test setting up the platform with a disabled config entry.""" - # Try to setup the platform with a disabled config entry - config_entry = MockConfigEntry( - domain="mqtt", data={}, disabled_by=ConfigEntryDisabler.USER - ) - config_entry.add_to_hass(hass) - topic = "location/jan" - - config = { - device_tracker.DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {"jan": topic}}, - } - hass.config.components = {"mqtt", "zone"} - - await async_setup_component(hass, device_tracker.DOMAIN, config) - await hass.async_block_till_done() - - assert ( - "MQTT device trackers will be not available until the config entry is enabled" - in caplog.text - ) diff --git a/tests/components/mqtt/test_fan.py b/tests/components/mqtt/test_fan.py index 1666ccee6ce..f4e89aa1ceb 100644 --- a/tests/components/mqtt/test_fan.py +++ b/tests/components/mqtt/test_fan.py @@ -52,7 +52,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -76,11 +75,6 @@ DEFAULT_CONFIG = { } } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[fan.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def fan_platform_only(): @@ -1941,15 +1935,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = fan.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - async def test_setup_manual_entity_from_yaml(hass): """Test setup manual configured MQTT entity.""" platform = fan.DOMAIN @@ -1964,16 +1949,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = fan.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_humidifier.py b/tests/components/mqtt/test_humidifier.py index 1e2d64b66cf..1b8eac397cf 100644 --- a/tests/components/mqtt/test_humidifier.py +++ b/tests/components/mqtt/test_humidifier.py @@ -54,7 +54,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -78,11 +77,6 @@ DEFAULT_CONFIG = { } } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[humidifier.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def humidifer_platform_only(): @@ -1313,15 +1307,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = humidifier.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - async def test_setup_manual_entity_from_yaml(hass): """Test setup manual configured MQTT entity.""" platform = humidifier.DOMAIN @@ -1347,16 +1332,3 @@ async def test_unload_config_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_p await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = humidifier.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 426ccb5806f..2e4a44b9525 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -2735,7 +2735,7 @@ async def test_one_deprecation_warning_per_platform( await mqtt_mock_entry_with_yaml_config() count = 0 for record in caplog.records: - if record.levelname == "WARNING" and ( + if record.levelname == "ERROR" and ( f"Manually configured MQTT {platform}(s) found under platform key '{platform}'" in record.message ): @@ -2817,15 +2817,6 @@ async def test_reload_entry_with_new_config(hass, tmp_path): "mqtt": { "light": [{"name": "test_new_modern", "command_topic": "test-topic_new"}] }, - # Test deprecated YAML configuration under the platform key - # Scheduled to be removed in HA core 2022.12 - "light": [ - { - "platform": "mqtt", - "name": "test_new_legacy", - "command_topic": "test-topic_new", - } - ], } await help_test_setup_manual_entity_from_yaml(hass, config_old) assert hass.states.get("light.test_old1") is not None @@ -2833,7 +2824,6 @@ async def test_reload_entry_with_new_config(hass, tmp_path): await help_test_entry_reload_with_new_config(hass, tmp_path, config_yaml_new) assert hass.states.get("light.test_old1") is None assert hass.states.get("light.test_new_modern") is not None - assert hass.states.get("light.test_new_legacy") is not None @patch("homeassistant.components.mqtt.PLATFORMS", [Platform.LIGHT]) @@ -2846,15 +2836,6 @@ async def test_disabling_and_enabling_entry(hass, tmp_path, caplog): "mqtt": { "light": [{"name": "test_new_modern", "command_topic": "test-topic_new"}] }, - # Test deprecated YAML configuration under the platform key - # Scheduled to be removed in HA core 2022.12 - "light": [ - { - "platform": "mqtt", - "name": "test_new_legacy", - "command_topic": "test-topic_new", - } - ], } await help_test_setup_manual_entity_from_yaml(hass, config_old) assert hass.states.get("light.test_old1") is not None @@ -2883,12 +2864,6 @@ async def test_disabling_and_enabling_entry(hass, tmp_path, caplog): await hass.async_block_till_done() await hass.async_block_till_done() - # Assert that the discovery was still received - # but kipped the setup - assert ( - "MQTT integration is disabled, skipping setup of manually configured MQTT light" - in caplog.text - ) assert mqtt_config_entry.state is ConfigEntryState.NOT_LOADED assert hass.states.get("light.test_old1") is None @@ -2903,7 +2878,6 @@ async def test_disabling_and_enabling_entry(hass, tmp_path, caplog): assert hass.states.get("light.test_old1") is None assert hass.states.get("light.test_new_modern") is not None - assert hass.states.get("light.test_new_legacy") is not None @patch("homeassistant.components.mqtt.PLATFORMS", [Platform.LIGHT]) diff --git a/tests/components/mqtt/test_legacy_vacuum.py b/tests/components/mqtt/test_legacy_vacuum.py index 4b44807b7c9..c9842b09eb2 100644 --- a/tests/components/mqtt/test_legacy_vacuum.py +++ b/tests/components/mqtt/test_legacy_vacuum.py @@ -29,7 +29,7 @@ from homeassistant.components.vacuum import ( ATTR_STATUS, VacuumEntityFeature, ) -from homeassistant.const import CONF_NAME, CONF_PLATFORM, STATE_OFF, STATE_ON, Platform +from homeassistant.const import CONF_NAME, STATE_OFF, STATE_ON, Platform from homeassistant.setup import async_setup_component from .test_common import ( @@ -52,7 +52,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -91,11 +90,6 @@ DEFAULT_CONFIG = { DEFAULT_CONFIG_2 = {mqtt.DOMAIN: {vacuum.DOMAIN: {"name": "test"}}} -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[vacuum.DOMAIN][CONF_PLATFORM] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def vacuum_platform_only(): @@ -936,15 +930,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = vacuum.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ @@ -1011,16 +996,3 @@ async def test_setup_manual_entity_from_yaml(hass): platform = vacuum.DOMAIN await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG) assert hass.states.get(f"{platform}.mqtttest") - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = vacuum.DOMAIN - config = deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index 05f9d0e72f0..9e64d1c45a7 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -216,7 +216,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -234,11 +233,6 @@ DEFAULT_CONFIG = { mqtt.DOMAIN: {light.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[light.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def light_platform_only(): @@ -2945,15 +2939,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = light.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - @pytest.mark.parametrize( "topic,value,attribute,attribute_value,init_payload", [ @@ -3151,16 +3136,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = light.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_light_json.py b/tests/components/mqtt/test_light_json.py index f2835121b86..8af7b244f61 100644 --- a/tests/components/mqtt/test_light_json.py +++ b/tests/components/mqtt/test_light_json.py @@ -119,7 +119,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -142,11 +141,6 @@ DEFAULT_CONFIG = { } } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[light.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def light_platform_only(): @@ -2205,15 +2199,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = light.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - @pytest.mark.parametrize( "topic,value,attribute,attribute_value,init_payload", [ @@ -2267,16 +2252,3 @@ async def test_setup_manual_entity_from_yaml(hass): platform = light.DOMAIN await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG) assert hass.states.get(f"{platform}.test") - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = light.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_light_template.py b/tests/components/mqtt/test_light_template.py index dd3f267b0d0..727949eba4b 100644 --- a/tests/components/mqtt/test_light_template.py +++ b/tests/components/mqtt/test_light_template.py @@ -64,7 +64,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -90,11 +89,6 @@ DEFAULT_CONFIG = { } } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[light.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def light_platform_only(): @@ -1201,15 +1195,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = light.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - @pytest.mark.parametrize( "topic,value,attribute,attribute_value,init_payload", [ @@ -1257,16 +1242,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = light.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_lock.py b/tests/components/mqtt/test_lock.py index 0784065ddbe..ef1690221aa 100644 --- a/tests/components/mqtt/test_lock.py +++ b/tests/components/mqtt/test_lock.py @@ -1,5 +1,4 @@ """The tests for the MQTT lock platform.""" -import copy from unittest.mock import patch import pytest @@ -42,7 +41,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -59,11 +57,6 @@ DEFAULT_CONFIG = { mqtt.DOMAIN: {lock.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[lock.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def lock_platform_only(): @@ -726,15 +719,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = lock.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ @@ -778,16 +762,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = lock.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_number.py b/tests/components/mqtt/test_number.py index 65c8655472c..e547e8207a7 100644 --- a/tests/components/mqtt/test_number.py +++ b/tests/components/mqtt/test_number.py @@ -1,5 +1,4 @@ """The tests for mqtt number component.""" -import copy import json from unittest.mock import patch @@ -52,7 +51,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -69,11 +67,6 @@ DEFAULT_CONFIG = { mqtt.DOMAIN: {number.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[number.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def number_platform_only(): @@ -907,15 +900,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = number.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ @@ -960,16 +944,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = number.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_scene.py b/tests/components/mqtt/test_scene.py index 122c32caa03..7f83ccb5cc2 100644 --- a/tests/components/mqtt/test_scene.py +++ b/tests/components/mqtt/test_scene.py @@ -19,7 +19,6 @@ from .test_common import ( help_test_discovery_update, help_test_discovery_update_unchanged, help_test_reloadable, - help_test_reloadable_late, help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_unload_config_entry_with_platform, @@ -37,11 +36,6 @@ DEFAULT_CONFIG = { } } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[scene.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def scene_platform_only(): @@ -231,15 +225,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = scene.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - async def test_setup_manual_entity_from_yaml(hass): """Test setup manual configured MQTT entity.""" platform = scene.DOMAIN @@ -254,16 +239,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = scene.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_select.py b/tests/components/mqtt/test_select.py index 5b588304061..e82bd20aa2b 100644 --- a/tests/components/mqtt/test_select.py +++ b/tests/components/mqtt/test_select.py @@ -42,7 +42,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -65,11 +64,6 @@ DEFAULT_CONFIG = { } } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[select.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def select_platform_only(): @@ -661,15 +655,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = select.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ @@ -716,16 +701,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = select.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_sensor.py b/tests/components/mqtt/test_sensor.py index 1884d04efc3..750a6d79edd 100644 --- a/tests/components/mqtt/test_sensor.py +++ b/tests/components/mqtt/test_sensor.py @@ -52,7 +52,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_reload_with_config, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -73,11 +72,6 @@ DEFAULT_CONFIG = { mqtt.DOMAIN: {sensor.DOMAIN: {"name": "test", "state_topic": "test-topic"}} } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[sensor.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def sensor_platform_only(): @@ -1110,15 +1104,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = sensor.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - async def test_cleanup_triggers_and_restoring_state( hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, freezer ): @@ -1258,16 +1243,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = sensor.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_siren.py b/tests/components/mqtt/test_siren.py index 47e52cca643..361a043ed4b 100644 --- a/tests/components/mqtt/test_siren.py +++ b/tests/components/mqtt/test_siren.py @@ -39,7 +39,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -56,11 +55,6 @@ DEFAULT_CONFIG = { mqtt.DOMAIN: {siren.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[siren.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def siren_platform_only(): @@ -954,15 +948,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = siren.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ @@ -1006,16 +991,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = siren.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_state_vacuum.py b/tests/components/mqtt/test_state_vacuum.py index 917c5fa43d8..89c3db9b5d9 100644 --- a/tests/components/mqtt/test_state_vacuum.py +++ b/tests/components/mqtt/test_state_vacuum.py @@ -26,13 +26,7 @@ from homeassistant.components.vacuum import ( STATE_CLEANING, STATE_DOCKED, ) -from homeassistant.const import ( - CONF_NAME, - CONF_PLATFORM, - ENTITY_MATCH_ALL, - STATE_UNKNOWN, - Platform, -) +from homeassistant.const import CONF_NAME, ENTITY_MATCH_ALL, STATE_UNKNOWN, Platform from homeassistant.setup import async_setup_component from .test_common import ( @@ -55,7 +49,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -88,13 +81,6 @@ DEFAULT_CONFIG = { DEFAULT_CONFIG_2 = {mqtt.DOMAIN: {vacuum.DOMAIN: {"schema": "state", "name": "test"}}} -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[vacuum.DOMAIN][CONF_PLATFORM] = mqtt.DOMAIN -DEFAULT_CONFIG_2_LEGACY = deepcopy(DEFAULT_CONFIG_2[mqtt.DOMAIN]) -DEFAULT_CONFIG_2_LEGACY[vacuum.DOMAIN][CONF_PLATFORM] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def vacuum_platform_only(): @@ -680,15 +666,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = vacuum.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ @@ -735,16 +712,3 @@ async def test_setup_manual_entity_from_yaml(hass): platform = vacuum.DOMAIN await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG) assert hass.states.get(f"{platform}.mqtttest") - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = vacuum.DOMAIN - config = deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_switch.py b/tests/components/mqtt/test_switch.py index 72a09529242..6248bb129aa 100644 --- a/tests/components/mqtt/test_switch.py +++ b/tests/components/mqtt/test_switch.py @@ -36,7 +36,6 @@ from .test_common import ( help_test_entity_id_update_subscriptions, help_test_publishing_with_custom_encoding, help_test_reloadable, - help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, @@ -54,11 +53,6 @@ DEFAULT_CONFIG = { mqtt.DOMAIN: {switch.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN]) -DEFAULT_CONFIG_LEGACY[switch.DOMAIN]["platform"] = mqtt.DOMAIN - @pytest.fixture(autouse=True) def switch_platform_only(): @@ -642,15 +636,6 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): - """Test reloading the MQTT platform with late entry setup.""" - domain = switch.DOMAIN - config = DEFAULT_CONFIG_LEGACY[domain] - await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) - - @pytest.mark.parametrize( "topic,value,attribute,attribute_value", [ @@ -694,16 +679,3 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) - - -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config): - """Test a setup with deprecated yaml platform schema.""" - domain = switch.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain]) - config["name"] = "test" - assert await async_setup_component(hass, domain, {domain: config}) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() - assert hass.states.get(f"{domain}.test") is not None diff --git a/tests/components/mqtt/test_update.py b/tests/components/mqtt/test_update.py index a8f925bf4a6..5746f2ac471 100644 --- a/tests/components/mqtt/test_update.py +++ b/tests/components/mqtt/test_update.py @@ -30,6 +30,7 @@ from .test_common import ( help_test_entity_device_info_with_connection, help_test_entity_device_info_with_identifier, help_test_entity_id_update_discovery_update, + help_test_reloadable, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setup_manual_entity_from_yaml, @@ -527,3 +528,12 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) + + +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): + """Test reloading the MQTT platform.""" + domain = update.DOMAIN + config = DEFAULT_CONFIG + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) From f28d9285bf00a1e0b0c7847ff8da8f6ea92aaa24 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 17 Nov 2022 16:02:35 -0600 Subject: [PATCH 0536/1033] Bump dbus-fast to 1.75.0 (#82289) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index ca0df4d2042..8e3dfb4ac80 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -11,7 +11,7 @@ "bluetooth-adapters==0.8.0", "bluetooth-auto-recovery==0.4.0", "bluetooth-data-tools==0.3.0", - "dbus-fast==1.74.1" + "dbus-fast==1.75.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 833421d3bf2..bbc88934a70 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -18,7 +18,7 @@ bluetooth-data-tools==0.3.0 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==38.0.3 -dbus-fast==1.74.1 +dbus-fast==1.75.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.8.1 diff --git a/requirements_all.txt b/requirements_all.txt index c18fd4d3661..5c2d17f9022 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -553,7 +553,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.74.1 +dbus-fast==1.75.0 # homeassistant.components.debugpy debugpy==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 17baae684ad..ae7acf7cce1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -433,7 +433,7 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.74.1 +dbus-fast==1.75.0 # homeassistant.components.debugpy debugpy==1.6.3 From 5119005a8d53d68f91e8da379fb46b0d16d49138 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 18 Nov 2022 00:29:16 +0000 Subject: [PATCH 0537/1033] [ci skip] Translation update --- .../components/airq/translations/pt-BR.json | 6 +-- .../components/aranet/translations/pt-BR.json | 2 +- .../components/bluetooth/translations/fr.json | 6 +++ .../components/bluetooth/translations/hu.json | 2 +- .../components/braviatv/translations/bg.json | 5 +++ .../components/braviatv/translations/fr.json | 3 +- .../components/esphome/translations/bg.json | 6 +++ .../components/esphome/translations/de.json | 6 +++ .../components/esphome/translations/es.json | 6 +++ .../components/esphome/translations/et.json | 6 +++ .../components/esphome/translations/fr.json | 5 +++ .../components/esphome/translations/hu.json | 6 +++ .../components/esphome/translations/no.json | 6 +++ .../components/esphome/translations/pl.json | 6 +++ .../esphome/translations/pt-BR.json | 6 +++ .../components/esphome/translations/ru.json | 6 +++ .../components/generic/translations/bg.json | 1 + .../components/generic/translations/fr.json | 6 +++ .../growatt_server/translations/hu.json | 6 +-- .../components/hassio/translations/pt-BR.json | 6 +-- .../homeassistant/translations/et.json | 4 ++ .../homeassistant/translations/no.json | 4 ++ .../homeassistant/translations/pt-BR.json | 4 ++ .../homeassistant/translations/ru.json | 4 ++ .../homeassistant_yellow/translations/bg.json | 38 ++++++++++++++++++ .../homeassistant_yellow/translations/ca.json | 7 ++++ .../homeassistant_yellow/translations/es.json | 40 +++++++++++++++++++ .../homeassistant_yellow/translations/et.json | 40 +++++++++++++++++++ .../homeassistant_yellow/translations/fr.json | 16 ++++++++ .../homeassistant_yellow/translations/hu.json | 40 +++++++++++++++++++ .../homeassistant_yellow/translations/no.json | 40 +++++++++++++++++++ .../translations/pt-BR.json | 40 +++++++++++++++++++ .../homeassistant_yellow/translations/ru.json | 40 +++++++++++++++++++ .../components/knx/translations/bg.json | 6 +++ .../components/knx/translations/pt-BR.json | 18 ++++----- .../components/livisi/translations/pt-BR.json | 2 +- .../components/mqtt/translations/pt-BR.json | 4 +- .../components/onvif/translations/ru.json | 3 +- .../components/plant/translations/hu.json | 2 +- .../pushbullet/translations/pt-BR.json | 4 +- .../components/shelly/translations/ru.json | 13 ++++++ .../components/tasmota/translations/fr.json | 1 + .../unifiprotect/translations/ru.json | 13 ++++++ .../wake_on_lan/translations/bg.json | 7 ++++ .../wake_on_lan/translations/ru.json | 7 ++++ .../components/zamg/translations/pt-BR.json | 6 +-- 46 files changed, 474 insertions(+), 31 deletions(-) create mode 100644 homeassistant/components/homeassistant_yellow/translations/bg.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/ca.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/es.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/et.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/fr.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/hu.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/no.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/pt-BR.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/ru.json create mode 100644 homeassistant/components/wake_on_lan/translations/bg.json create mode 100644 homeassistant/components/wake_on_lan/translations/ru.json diff --git a/homeassistant/components/airq/translations/pt-BR.json b/homeassistant/components/airq/translations/pt-BR.json index bf556a86b0d..614b80c4b0e 100644 --- a/homeassistant/components/airq/translations/pt-BR.json +++ b/homeassistant/components/airq/translations/pt-BR.json @@ -1,12 +1,12 @@ { "config": { "abort": { - "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falhou ao conectar", + "cannot_connect": "Falha ao conectar", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", - "invalid_input": "Nome do host ou endere\u00e7o IP inv\u00e1lido" + "invalid_input": "Nome de host ou endere\u00e7o IP inv\u00e1lido" }, "step": { "user": { diff --git a/homeassistant/components/aranet/translations/pt-BR.json b/homeassistant/components/aranet/translations/pt-BR.json index f98a79dabaa..d286add568d 100644 --- a/homeassistant/components/aranet/translations/pt-BR.json +++ b/homeassistant/components/aranet/translations/pt-BR.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", "integrations_diabled": "Este dispositivo n\u00e3o tem integra\u00e7\u00f5es ativadas. Ative as integra\u00e7\u00f5es de casa inteligente usando o aplicativo e tente novamente.", "no_devices_found": "Nenhum dispositivo Aranet n\u00e3o configurado encontrado.", "outdated_version": "Este dispositivo est\u00e1 usando um firmware desatualizado. Atualize-o para pelo menos a v1.2.0 e tente novamente." diff --git a/homeassistant/components/bluetooth/translations/fr.json b/homeassistant/components/bluetooth/translations/fr.json index 025b00bb0cf..c53c306b987 100644 --- a/homeassistant/components/bluetooth/translations/fr.json +++ b/homeassistant/components/bluetooth/translations/fr.json @@ -28,6 +28,12 @@ } } }, + "issues": { + "haos_outdated": { + "description": "Pour am\u00e9liorer la fiabilit\u00e9 et les performances du Bluetooth, nous vous recommandons vivement de passer \u00e0 la version 9.0 ou ult\u00e9rieure du syst\u00e8me d'exploitation d'Home Assistant.", + "title": "Mettez \u00e0 jour le syst\u00e8me d'exploitation Home Assistant (HAOS) vers la version 9.0 ou sup\u00e9rieure" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/bluetooth/translations/hu.json b/homeassistant/components/bluetooth/translations/hu.json index e5b94f070ea..d063d089ded 100644 --- a/homeassistant/components/bluetooth/translations/hu.json +++ b/homeassistant/components/bluetooth/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van", - "no_adapters": "Nem tal\u00e1ltak konfigur\u00e1latlan Bluetooth-adaptert" + "no_adapters": "Nem tal\u00e1lhat\u00f3 konfigur\u00e1latlan Bluetooth-adapter" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/braviatv/translations/bg.json b/homeassistant/components/braviatv/translations/bg.json index 8df7ac6e1c3..ed2a2759529 100644 --- a/homeassistant/components/braviatv/translations/bg.json +++ b/homeassistant/components/braviatv/translations/bg.json @@ -34,5 +34,10 @@ } } } + }, + "options": { + "abort": { + "failed_update": "\u0412\u044a\u0437\u043d\u0438\u043a\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u0430\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0441\u043f\u0438\u0441\u044a\u043a\u0430 \u0441 \u0438\u0437\u0442\u043e\u0447\u043d\u0438\u0446\u0438.\n\n\u0423\u0432\u0435\u0440\u0435\u0442\u0435 \u0441\u0435, \u0447\u0435 \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440\u044a\u0442 \u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d, \u043f\u0440\u0435\u0434\u0438 \u0434\u0430 \u0441\u0435 \u043e\u043f\u0438\u0442\u0430\u0442\u0435 \u0434\u0430 \u0433\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435." + } } } \ No newline at end of file diff --git a/homeassistant/components/braviatv/translations/fr.json b/homeassistant/components/braviatv/translations/fr.json index 40445ec8062..d71bbd0e8ac 100644 --- a/homeassistant/components/braviatv/translations/fr.json +++ b/homeassistant/components/braviatv/translations/fr.json @@ -29,7 +29,8 @@ "data": { "pin": "Code PIN", "use_psk": "Utiliser l'authentification PSK" - } + }, + "description": "Saisissez le code PIN affich\u00e9 sur le t\u00e9l\u00e9viseur Sony Bravia. \n\nSi le code PIN n'est pas affich\u00e9, vous devez supprimer Home Assistant du t\u00e9l\u00e9viseur. Pour cela, allez dans : Param\u00e8tres -> R\u00e9seau -> Param\u00e8tres du p\u00e9riph\u00e9rique distant ->Supprimer le p\u00e9riph\u00e9rique distant. \n\nVous pouvez utiliser PSK (Pre-Shared-Key) au lieu du code PIN. PSK est une cl\u00e9 secr\u00e8te d\u00e9finie par l'utilisateur utilis\u00e9e pour le contr\u00f4le d'acc\u00e8s. Cette m\u00e9thode d'authentification est recommand\u00e9e car elle est plus stable. Pour activer PSK sur votre t\u00e9l\u00e9viseur, allez dans : Param\u00e8tres -> R\u00e9seau -> Configuration du r\u00e9seau domestique -> Contr\u00f4le IP. Cochez ensuite la case \"Utiliser l'authentification PSK\" et entrez votre PSK au lieu du code PIN." }, "user": { "data": { diff --git a/homeassistant/components/esphome/translations/bg.json b/homeassistant/components/esphome/translations/bg.json index 699a993403f..061fcc768a0 100644 --- a/homeassistant/components/esphome/translations/bg.json +++ b/homeassistant/components/esphome/translations/bg.json @@ -39,5 +39,11 @@ "description": "\u041c\u043e\u043b\u044f, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u0442\u0435 \u0437\u0430 \u0432\u0440\u044a\u0437\u043a\u0430 \u0441 [ESPHome](https://esphomelib.com/)." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "\u0417\u0430 \u0434\u0430 \u043f\u043e\u0434\u043e\u0431\u0440\u0438\u0442\u0435 \u043d\u0430\u0434\u0435\u0436\u0434\u043d\u043e\u0441\u0442\u0442\u0430 \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u043d\u043e\u0441\u0442\u0442\u0430 \u043d\u0430 Bluetooth, \u0441\u0438\u043b\u043d\u043e \u043f\u0440\u0435\u043f\u043e\u0440\u044a\u0447\u0432\u0430\u043c\u0435 \u0434\u0430 \u0430\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u0442\u0435 {name} \u0441 ESPHome 2022.11.0 \u0438\u043b\u0438 \u043f\u043e-\u043d\u043e\u0432\u0430 \u0432\u0435\u0440\u0441\u0438\u044f.", + "title": "\u0410\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u0439\u0442\u0435 {name} \u0441 ESPHome 2022.11.0 \u0438\u043b\u0438 \u043f\u043e-\u043d\u043e\u0432\u0430 \u0432\u0435\u0440\u0441\u0438\u044f" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/de.json b/homeassistant/components/esphome/translations/de.json index 7556739ce93..bf7bcd386e5 100644 --- a/homeassistant/components/esphome/translations/de.json +++ b/homeassistant/components/esphome/translations/de.json @@ -43,5 +43,11 @@ "description": "Bitte gib die Verbindungseinstellungen deines [ESPHome]( {esphome_url} )-Knotens ein." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "Um die Zuverl\u00e4ssigkeit und Leistung von Bluetooth zu verbessern, empfehlen wir dringend, {name} mit ESPHome 2022.11.0 oder h\u00f6her zu aktualisieren.", + "title": "Aktualisieren von {name} mit ESPHome 2022.11.0 oder h\u00f6her" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/es.json b/homeassistant/components/esphome/translations/es.json index 82066953472..2b10be769e4 100644 --- a/homeassistant/components/esphome/translations/es.json +++ b/homeassistant/components/esphome/translations/es.json @@ -43,5 +43,11 @@ "description": "Por favor, introduce la configuraci\u00f3n de conexi\u00f3n de tu nodo [ESPHome]({esphome_url})." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "Para mejorar la confiabilidad y el rendimiento de Bluetooth, recomendamos actualizar {name} con ESPHome 2022.11.0 o posterior.", + "title": "Actualizar {name} con ESPHome 2022.11.0 o posterior" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/et.json b/homeassistant/components/esphome/translations/et.json index 29fa98bafb0..07ec62741f9 100644 --- a/homeassistant/components/esphome/translations/et.json +++ b/homeassistant/components/esphome/translations/et.json @@ -43,5 +43,11 @@ "description": "Sisesta oma [ESPHome]({esphome_url}) s\u00f5lme \u00fchenduse s\u00e4tted." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "Bluetoothi t\u00f6\u00f6kindluse ja j\u00f5udluse parandamiseks soovitame tungivalt v\u00e4rskendada {name} versiooniga ESPHome 2022.11.0 v\u00f5i uuema versiooniga.", + "title": "Uuenda {nimi} ESPHome 2022.11.0 v\u00f5i uuema versiooniga" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/fr.json b/homeassistant/components/esphome/translations/fr.json index 8ac9feb8a93..ab7e9545931 100644 --- a/homeassistant/components/esphome/translations/fr.json +++ b/homeassistant/components/esphome/translations/fr.json @@ -43,5 +43,10 @@ "description": "Veuillez saisir les param\u00e8tres de connexion de votre n\u0153ud [ESPHome]({esphome_url})." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "Pour am\u00e9liorer la fiabilit\u00e9 et les performances du Bluetooth, nous recommandons vivement de mettre \u00e0 jour {nom} avec ESPHome 2022.11.0 ou ult\u00e9rieure." + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/hu.json b/homeassistant/components/esphome/translations/hu.json index 41550d02a43..5fd2dfc2565 100644 --- a/homeassistant/components/esphome/translations/hu.json +++ b/homeassistant/components/esphome/translations/hu.json @@ -43,5 +43,11 @@ "description": "K\u00e9rem, adja meg az [ESPHome]({esphome_url}) v\u00e9gpontj\u00e1nak kapcsol\u00f3d\u00e1si be\u00e1ll\u00edt\u00e1sait." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "A Bluetooth megb\u00edzhat\u00f3s\u00e1g\u00e1nak \u00e9s teljes\u00edtm\u00e9ny\u00e9nek jav\u00edt\u00e1sa \u00e9rdek\u00e9ben javasoljuk {name} v\u00e9gpont friss\u00edt\u00e9s\u00e9t az ESPHome 2022.11.0 vagy \u00fajabb verzi\u00f3ra.", + "title": "{name} v\u00e9gpont friss\u00edt\u00e9se ESPHome 2022.11.0 vagy \u00fajabb verzi\u00f3j\u00e1val" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/no.json b/homeassistant/components/esphome/translations/no.json index e17b9dad815..d24d557f49e 100644 --- a/homeassistant/components/esphome/translations/no.json +++ b/homeassistant/components/esphome/translations/no.json @@ -43,5 +43,11 @@ "description": "Vennligst skriv inn tilkoblingsinnstillingene for [ESPHome]( {esphome_url} )-noden." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "For \u00e5 forbedre Bluetooth-p\u00e5litelighet og ytelse anbefaler vi p\u00e5 det sterkeste \u00e5 oppdatere {name} med ESPHome 2022.11.0 eller nyere.", + "title": "Oppdater {name} med ESPHome 2022.11.0 eller nyere" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/pl.json b/homeassistant/components/esphome/translations/pl.json index dd495f0f097..ae9ae8c7b18 100644 --- a/homeassistant/components/esphome/translations/pl.json +++ b/homeassistant/components/esphome/translations/pl.json @@ -43,5 +43,11 @@ "description": "Wprowad\u017a ustawienia po\u0142\u0105czenia w\u0119z\u0142a [ESPHome]({esphome_url})." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "Aby poprawi\u0107 niezawodno\u015b\u0107 i wydajno\u015b\u0107 Bluetooth, zdecydowanie zalecamy zaktualizowanie {name} do wersji ESPHome 2022.11.0 lub nowszej.", + "title": "Zaktualizuj {name} do wersji ESPHome 2022.11.0 lub nowszej" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/pt-BR.json b/homeassistant/components/esphome/translations/pt-BR.json index 21fd9067be1..ae78ce6b88c 100644 --- a/homeassistant/components/esphome/translations/pt-BR.json +++ b/homeassistant/components/esphome/translations/pt-BR.json @@ -43,5 +43,11 @@ "description": "Insira as configura\u00e7\u00f5es de conex\u00e3o do seu n\u00f3 [ESPHome]( {esphome_url} )." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "Para melhorar a confiabilidade e o desempenho do Bluetooth, recomendamos atualizar {name} com ESPHome 2022.11.0 ou posterior.", + "title": "Atualize {name} com ESPHome 2022.11.0 ou posterior" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/ru.json b/homeassistant/components/esphome/translations/ru.json index ea0a3105226..7aa09cf7ff4 100644 --- a/homeassistant/components/esphome/translations/ru.json +++ b/homeassistant/components/esphome/translations/ru.json @@ -43,5 +43,11 @@ "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0412\u0430\u0448\u0435\u043c\u0443 [ESPHome]({esphome_url})." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "\u0414\u043b\u044f \u043f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 Bluetooth \u043c\u044b \u043d\u0430\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c {name} \u0434\u043e \u0432\u0435\u0440\u0441\u0438\u0438 ESPHome 2022.11.0 \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0437\u0434\u043d\u0435\u0439.", + "title": "\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e ESPHome {name} \u0434\u043e \u0432\u0435\u0440\u0441\u0438\u0438 2022.11.0 \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0437\u0434\u043d\u0435\u0439" + } } } \ No newline at end of file diff --git a/homeassistant/components/generic/translations/bg.json b/homeassistant/components/generic/translations/bg.json index 50cb1feb1b7..07c7f4e8734 100644 --- a/homeassistant/components/generic/translations/bg.json +++ b/homeassistant/components/generic/translations/bg.json @@ -45,6 +45,7 @@ "data": { "confirmed_ok": "\u0422\u043e\u0432\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0438\u0437\u0433\u043b\u0435\u0436\u0434\u0430 \u0434\u043e\u0431\u0440\u0435." }, + "description": "![\u041f\u0440\u0435\u0433\u043b\u0435\u0434 \u043d\u0430 \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043e\u0442 \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430]({preview_url})", "title": "\u041f\u0440\u0435\u0433\u043b\u0435\u0434" }, "content_type": { diff --git a/homeassistant/components/generic/translations/fr.json b/homeassistant/components/generic/translations/fr.json index d6c057d4da1..1592208480c 100644 --- a/homeassistant/components/generic/translations/fr.json +++ b/homeassistant/components/generic/translations/fr.json @@ -47,6 +47,9 @@ "description": "Saisissez les param\u00e8tres de connexion \u00e0 la cam\u00e9ra." }, "user_confirm_still": { + "data": { + "confirmed_ok": "Cette image semble conforme." + }, "title": "Aper\u00e7u" } } @@ -72,6 +75,9 @@ }, "step": { "confirm_still": { + "data": { + "confirmed_ok": "Cette image semble conforme." + }, "title": "Aper\u00e7u" }, "content_type": { diff --git a/homeassistant/components/growatt_server/translations/hu.json b/homeassistant/components/growatt_server/translations/hu.json index 44d87bf753e..4e94af79900 100644 --- a/homeassistant/components/growatt_server/translations/hu.json +++ b/homeassistant/components/growatt_server/translations/hu.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_plants": "Ezen a sz\u00e1ml\u00e1n nem tal\u00e1ltak n\u00f6v\u00e9nyeket" + "no_plants": "Ebben a fi\u00f3kban nem tal\u00e1lhat\u00f3 egy er\u0151m\u0171 sem" }, "error": { "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s" @@ -9,9 +9,9 @@ "step": { "plant": { "data": { - "plant_id": "N\u00f6v\u00e9ny" + "plant_id": "Er\u0151m\u0171" }, - "title": "V\u00e1lassza ki a n\u00f6v\u00e9ny\u00e9t" + "title": "V\u00e1lassza ki az er\u0151m\u0171vet" }, "user": { "data": { diff --git a/homeassistant/components/hassio/translations/pt-BR.json b/homeassistant/components/hassio/translations/pt-BR.json index 9696b9ca2bb..d185ebd2f5c 100644 --- a/homeassistant/components/hassio/translations/pt-BR.json +++ b/homeassistant/components/hassio/translations/pt-BR.json @@ -112,9 +112,9 @@ "system_health": { "info": { "agent_version": "Vers\u00e3o do Agent", - "board": "Borda", + "board": "Placa", "disk_total": "Total do disco", - "disk_used": "Disco usado", + "disk_used": "Uso do disco", "docker_version": "Vers\u00e3o do Docker", "healthy": "Saud\u00e1vel", "host_os": "Sistema Operacional Host", @@ -122,7 +122,7 @@ "supervisor_api": "API do supervisor", "supervisor_version": "Vers\u00e3o do Supervisor", "supported": "Suportado", - "update_channel": "Atualizar canal", + "update_channel": "Canal de atualiza\u00e7\u00e3o", "version_api": "API de vers\u00e3o" } } diff --git a/homeassistant/components/homeassistant/translations/et.json b/homeassistant/components/homeassistant/translations/et.json index e1c765556cf..82dcb4948a3 100644 --- a/homeassistant/components/homeassistant/translations/et.json +++ b/homeassistant/components/homeassistant/translations/et.json @@ -3,6 +3,10 @@ "historic_currency": { "description": "Valuuta {currency} ei ole enam kasutusel, seadista valuuta uuesti.", "title": "Seadistatud valuutat enam ei kasutata" + }, + "python_version": { + "description": "Home Assistanti k\u00e4itamise tugi praeguses kasutatavas Pythoni versioonis {current_python_version} on aegunud ja see eemaldatakse rakendusest Home Assistant {breaks_in_ha_version} . T\u00e4ienda Python versioonile {required_python_version} , et v\u00e4ltida Home Assistanti eksemplari t\u00f6\u00f6 l\u00f5ppemist.", + "title": "Pythoni {current_python_version} tugi eemaldatakse" } }, "system_health": { diff --git a/homeassistant/components/homeassistant/translations/no.json b/homeassistant/components/homeassistant/translations/no.json index e98f298b5f9..aadefaca085 100644 --- a/homeassistant/components/homeassistant/translations/no.json +++ b/homeassistant/components/homeassistant/translations/no.json @@ -3,6 +3,10 @@ "historic_currency": { "description": "Valutaen {currency} er ikke lenger i bruk, vennligst konfigurer valutakonfigurasjonen p\u00e5 nytt.", "title": "Tollet er ugyldig eller ikke lenger autorisert." + }, + "python_version": { + "description": "St\u00f8tte for \u00e5 kj\u00f8re Home Assistant i den gjeldende brukte Python-versjonen {current_python_version} er avviklet og vil bli fjernet i Home Assistant {breaks_in_ha_version} . Oppgrader Python til {required_python_version} for \u00e5 forhindre at Home Assistant-forekomsten din g\u00e5r i stykker.", + "title": "St\u00f8tte for Python {current_python_version} blir fjernet" } }, "system_health": { diff --git a/homeassistant/components/homeassistant/translations/pt-BR.json b/homeassistant/components/homeassistant/translations/pt-BR.json index 3fb4e740548..35e1bf1838b 100644 --- a/homeassistant/components/homeassistant/translations/pt-BR.json +++ b/homeassistant/components/homeassistant/translations/pt-BR.json @@ -3,6 +3,10 @@ "historic_currency": { "description": "A moeda {currency} n\u00e3o est\u00e1 mais em uso, reconfigure a configura\u00e7\u00e3o da moeda.", "title": "A moeda configurada n\u00e3o est\u00e1 mais em uso" + }, + "python_version": { + "description": "O suporte para executar o Home Assistant na vers\u00e3o atual do Python usada {current_python_version} est\u00e1 obsoleto e ser\u00e1 removido no Home Assistant {breaks_in_ha_version} . Atualize o Python para {required_python_version} para evitar que sua inst\u00e2ncia do Home Assistant seja interrompida.", + "title": "O suporte para Python {current_python_version} est\u00e1 sendo removido" } }, "system_health": { diff --git a/homeassistant/components/homeassistant/translations/ru.json b/homeassistant/components/homeassistant/translations/ru.json index d6326763387..cbeaa3023a8 100644 --- a/homeassistant/components/homeassistant/translations/ru.json +++ b/homeassistant/components/homeassistant/translations/ru.json @@ -3,6 +3,10 @@ "historic_currency": { "description": "\u0412\u0430\u043b\u044e\u0442\u0430 {currency} \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f. \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0432\u0430\u043b\u044e\u0442\u044b.", "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0432\u0430\u043b\u044e\u0442\u0430 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f" + }, + "python_version": { + "description": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 Home Assistant \u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0439 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 Python {current_python_version} \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u043a\u0440\u0430\u0449\u0435\u043d\u0430 \u0432 Home Assistant \u0432\u0435\u0440\u0441\u0438\u0438 {breaks_in_ha_version}. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 Python \u0434\u043e \u0432\u0435\u0440\u0441\u0438\u0438 {required_python_version}.", + "title": "\u041f\u0440\u0435\u043a\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 Python \u0432\u0435\u0440\u0441\u0438\u0438 {current_python_version}" } }, "system_health": { diff --git a/homeassistant/components/homeassistant_yellow/translations/bg.json b/homeassistant/components/homeassistant_yellow/translations/bg.json new file mode 100644 index 00000000000..7bd4bbd2aa1 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/bg.json @@ -0,0 +1,38 @@ +{ + "options": { + "abort": { + "addon_info_failed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0437\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Silicon Labs Multiprotocol.", + "addon_install_failed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Silicon Labs Multiprotocol.", + "addon_set_config_failed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0437\u0430\u0434\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Silicon Labs Multiprotocol.", + "addon_start_failed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Silicon Labs Multiprotocol.", + "not_hassio": "\u0425\u0430\u0440\u0434\u0443\u0435\u0440\u043d\u0438\u0442\u0435 \u043e\u043f\u0446\u0438\u0438 \u043c\u043e\u0433\u0430\u0442 \u0434\u0430 \u0441\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0442 \u0441\u0430\u043c\u043e \u0437\u0430 \u0438\u043d\u0441\u0442\u0430\u043b\u0430\u0446\u0438\u0438 \u043d\u0430 HassOS." + }, + "error": { + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "progress": { + "install_addon": "\u041c\u043e\u043b\u044f, \u0438\u0437\u0447\u0430\u043a\u0430\u0439\u0442\u0435, \u0434\u043e\u043a\u0430\u0442\u043e \u0437\u0430\u0432\u044a\u0440\u0448\u0438 \u0438\u043d\u0441\u0442\u0430\u043b\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Silicon Labs Multiprotocol. \u0422\u043e\u0432\u0430 \u043c\u043e\u0436\u0435 \u0434\u0430 \u043e\u0442\u043d\u0435\u043c\u0435 \u043d\u044f\u043a\u043e\u043b\u043a\u043e \u043c\u0438\u043d\u0443\u0442\u0438.", + "start_addon": "\u041c\u043e\u043b\u044f, \u0438\u0437\u0447\u0430\u043a\u0430\u0439\u0442\u0435, \u0434\u043e\u043a\u0430\u0442\u043e \u0437\u0430\u0432\u044a\u0440\u0448\u0438 \u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Silicon Labs Multiprotocol. \u0422\u043e\u0432\u0430 \u043c\u043e\u0436\u0435 \u0434\u0430 \u043e\u0442\u043d\u0435\u043c\u0435 \u043d\u044f\u043a\u043e\u043b\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434\u0438." + }, + "step": { + "addon_installed_other_device": { + "title": "\u041c\u043d\u043e\u0433\u043e\u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043d\u0430\u0442\u0430 \u043f\u043e\u0434\u0434\u0440\u044a\u0436\u043a\u0430 \u0432\u0435\u0447\u0435 \u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0430 \u0437\u0430 \u0434\u0440\u0443\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043c\u043d\u043e\u0433\u043e\u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043d\u0430 \u043f\u043e\u0434\u0434\u0440\u044a\u0436\u043a\u0430" + }, + "title": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043c\u0443\u043b\u0442\u0438\u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043d\u0430 \u043f\u043e\u0434\u0434\u0440\u044a\u0436\u043a\u0430 \u043d\u0430 IEEE 802.15.4 \u0440\u0430\u0434\u0438\u043e\u0442\u043e" + }, + "install_addon": { + "title": "\u0418\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Silicon Labs Multiprotocol \u0437\u0430\u043f\u043e\u0447\u043d\u0430" + }, + "show_revert_guide": { + "title": "\u041c\u043d\u043e\u0433\u043e\u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043d\u0430\u0442\u0430 \u043f\u043e\u0434\u0434\u0440\u044a\u0436\u043a\u0430 \u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0430 \u0437\u0430 \u0442\u043e\u0432\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + }, + "start_addon": { + "title": "\u0414\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Silicon Labs Multiprotocol \u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/ca.json b/homeassistant/components/homeassistant_yellow/translations/ca.json new file mode 100644 index 00000000000..ebff40e34e8 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/ca.json @@ -0,0 +1,7 @@ +{ + "options": { + "error": { + "unknown": "Error inesperat" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/es.json b/homeassistant/components/homeassistant_yellow/translations/es.json new file mode 100644 index 00000000000..c745e933c0e --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/es.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "No se pudo obtener la informaci\u00f3n del complemento Silicon Labs Multiprotocol.", + "addon_install_failed": "No se pudo instalar el complemento Silicon Labs Multiprotocol.", + "addon_set_config_failed": "No se pudo establecer la configuraci\u00f3n de Silicon Labs Multiprotocol.", + "addon_start_failed": "No se pudo iniciar el complemento Silicon Labs Multiprotocol.", + "not_hassio": "Las opciones de hardware solo se pueden configurar en instalaciones de HassOS." + }, + "error": { + "unknown": "Error inesperado" + }, + "progress": { + "install_addon": "Por favor, espera mientras finaliza la instalaci\u00f3n del complemento Silicon Labs Multiprotocol. Esto puede tardar varios minutos.", + "start_addon": "Por favor, espera mientras se completa el inicio del complemento Silicon Labs Multiprotocol. Esto puede tardar unos segundos." + }, + "step": { + "addon_installed_other_device": { + "title": "El soporte multiprotocolo ya est\u00e1 habilitado para otro dispositivo" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Habilitar soporte multiprotocolo" + }, + "description": "Cuando el soporte multiprotocolo est\u00e1 habilitado, la radio IEEE 802.15.4 de Home Assistant Yellow se puede usar tanto para Zigbee como para Thread (usado por Matter) al mismo tiempo. Nota: Esta es una caracter\u00edstica experimental.", + "title": "Habilitar el soporte multiprotocolo en la radio IEEE 802.15.4" + }, + "install_addon": { + "title": "La instalaci\u00f3n del complemento Silicon Labs Multiprotocol ha comenzado" + }, + "show_revert_guide": { + "description": "Si quieres cambiar el firmware a solo Zigbee, completa los siguientes pasos manuales: \n\n* Eliminar el complemento Silicon Labs Multiprotocol \n\n* Actualiza el firmware a solo Zigbee, sigue la gu\u00eda en https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n* Vuelve a configurar ZHA para migrar la configuraci\u00f3n a la radio actualizada", + "title": "El soporte multiprotocolo est\u00e1 habilitado para este dispositivo" + }, + "start_addon": { + "title": "El complemento Silicon Labs Multiprotocol est\u00e1 iniciando." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/et.json b/homeassistant/components/homeassistant_yellow/translations/et.json new file mode 100644 index 00000000000..d0d1709b99e --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/et.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Silicon Labs Multiprotocol add-on info saamine eba\u00f5nnestus.", + "addon_install_failed": "Silicon Labs Multiprotocol add-on'i paigaldamine eba\u00f5nnestus.", + "addon_set_config_failed": "Silicon Labs Multiprotocol konfiguratsiooni seadistamine eba\u00f5nnestus.", + "addon_start_failed": "Silicon Labsi mitmeprotokolli lisandmooduli k\u00e4ivitamine nurjus.", + "not_hassio": "Riistvaravalikuid saab konfigureerida ainult HassOS-i paigaldustes." + }, + "error": { + "unknown": "Ootamatu t\u00f5rge" + }, + "progress": { + "install_addon": "Oota kuni Silicon Labsi mitmeprotokolli lisandmooduli installimine l\u00f5peb. Selleks v\u00f5ib kuluda mitu minutit.", + "start_addon": "Oota kuni Silicon Labsi mitmeprotokolli lisandmooduli k\u00e4ivitamine on l\u00f5pule viidud. See v\u00f5ib v\u00f5tta m\u00f5ne sekundi." + }, + "step": { + "addon_installed_other_device": { + "title": "Mitmeprotokolli tugi on m\u00f5ne teise seadme jaoks juba lubatud" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Luba multiprotokolli tugi" + }, + "description": "Kui multiprotokollide tugi on lubatud, saab Home Assistant Yellow'i IEEE 802.15.4 raadiot kasutada samaaegselt nii Zigbee kui ka Threadi (mida kasutab Matter) jaoks. M\u00e4rkus: See on eksperimentaalne funktsioon.", + "title": "Multiprotokollide toe lubamine IEEE 802.15.4 raadiosides" + }, + "install_addon": { + "title": "Silicon Labs Multiprotocol add-on paigaldus on alanud" + }, + "show_revert_guide": { + "description": "Kui soovid muuta ainult Zigbee p\u00fcsivara, tee j\u00e4rgmised sammud:\n\n * Eemalda Silicon Labsi mitmeprotokolli lisand \n\n * V\u00e4rskenda ainult Zigbee p\u00fcsivara, j\u00e4rgi juhendit aadressil https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Seadistuste \u00fcleviimiseks v\u00e4rskendatud raadiosse seadista ZHA uuesti", + "title": "Multiprotokollide tugi on selle seadme puhul lubatud." + }, + "start_addon": { + "title": "Silicon Labs Multiprotocol lisandmoodul k\u00e4ivitub." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/fr.json b/homeassistant/components/homeassistant_yellow/translations/fr.json new file mode 100644 index 00000000000..6297713a99a --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/fr.json @@ -0,0 +1,16 @@ +{ + "options": { + "step": { + "install_addon": { + "title": "L'installation du module compl\u00e9mentaire multi-protocoles de Silicon Labs a commenc\u00e9." + }, + "show_revert_guide": { + "description": "Si vous voulez utiliser uniquement le firmware Zigbee, suivez les \u00e9tapes manuelles suivantes :\n\n * Supprimez le module compl\u00e9mentaire multi-protocoles de Silicon Labs.\n\n * Flashez le firmware qui supporte uniquement Zigbee en suivant le guide https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Reconfigurez ZHA pour migrer les param\u00e8tres vers le nouveau canal", + "title": "Le support multi-protocoles est activ\u00e9 pour cet appareil." + }, + "start_addon": { + "title": "Le module compl\u00e9mentaire multi-protocoles de Silicon Labs d\u00e9marre." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/hu.json b/homeassistant/components/homeassistant_yellow/translations/hu.json new file mode 100644 index 00000000000..b2fdffb1450 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/hu.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Nem siker\u00fclt lek\u00e9rni a Silicon Labs Multiprotocol kieg\u00e9sz\u00edt\u0151 adatait.", + "addon_install_failed": "Nem siker\u00fclt telep\u00edteni a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9nyt.", + "addon_set_config_failed": "A Silicon Labs Multiprotokoll konfigur\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1sa sikertelen volt.", + "addon_start_failed": "Nem siker\u00fclt elind\u00edtani a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9nyt.", + "not_hassio": "A hardverbe\u00e1ll\u00edt\u00e1sok csak HassOS telep\u00edt\u00e9sekn\u00e9l konfigur\u00e1lhat\u00f3k." + }, + "error": { + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "progress": { + "install_addon": "K\u00e9rj\u00fck, v\u00e1rjon, am\u00edg a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9ny telep\u00edt\u00e9se befejez\u0151dik. Ez t\u00f6bb percig is eltarthat.", + "start_addon": "K\u00e9rj\u00fck, v\u00e1rjon, am\u00edg a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9ny elindul. Ez eltarthat n\u00e9h\u00e1ny m\u00e1sodpercig." + }, + "step": { + "addon_installed_other_device": { + "title": "A multiprotokoll-t\u00e1mogat\u00e1s m\u00e1r enged\u00e9lyezve van egy m\u00e1sik eszk\u00f6z sz\u00e1m\u00e1ra" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Multiprotokoll-t\u00e1mogat\u00e1s enged\u00e9lyez\u00e9se" + }, + "description": "Ha a multiprotokoll-t\u00e1mogat\u00e1s enged\u00e9lyezve van, a Home Assistant Yellow IEEE 802.15.4 r\u00e1di\u00f3ja egyszerre haszn\u00e1lhat\u00f3 a Zigbee \u00e9s a Thread (a Matter \u00e1ltal haszn\u00e1lt) r\u00e1di\u00f3hoz. Megjegyz\u00e9s: Ez egy k\u00eds\u00e9rleti funkci\u00f3.", + "title": "Multiprotokoll t\u00e1mogat\u00e1s\u00e1nak enged\u00e9lyez\u00e9se az IEEE 802.15.4 r\u00e1di\u00f3ban" + }, + "install_addon": { + "title": "A Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9ny telep\u00edt\u00e9se folyamatban van" + }, + "show_revert_guide": { + "description": "Ha csak Zigbee firmware-re szeretne v\u00e1ltani, k\u00e9rj\u00fck, hajtsa v\u00e9gre a k\u00f6vetkez\u0151 manu\u00e1lis l\u00e9p\u00e9seket:\n\n * T\u00e1vol\u00edtsa el a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9nyt.\n\n * Flashelje a csak Zigbee firmware-t, k\u00f6vesse a https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually oldalon tal\u00e1lhat\u00f3 \u00fatmutat\u00f3t.\n\n * Konfigur\u00e1lja \u00fajra a ZHA-t, hogy a be\u00e1ll\u00edt\u00e1sokat \u00e1tvigye az \u00fajraflashelt r\u00e1di\u00f3ra.", + "title": "A multiprotokoll-t\u00e1mogat\u00e1s enged\u00e9lyezve van az eszk\u00f6z\u00f6n." + }, + "start_addon": { + "title": "A Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9ny elind\u00edt\u00e1sa folyamatban van" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/no.json b/homeassistant/components/homeassistant_yellow/translations/no.json new file mode 100644 index 00000000000..d53c6b0fbcd --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/no.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Kunne ikke hente informasjon om tilleggsprogrammet for Silicon Labs Multiprotocol.", + "addon_install_failed": "Kunne ikke installere Silicon Labs Multiprotocol-tillegget.", + "addon_set_config_failed": "Kunne ikke angi Silicon Labs Multiprotocol-konfigurasjon.", + "addon_start_failed": "Kunne ikke starte Silicon Labs Multiprotocol-tillegget.", + "not_hassio": "Maskinvarealternativene kan bare konfigureres p\u00e5 HassOS-installasjoner." + }, + "error": { + "unknown": "Uventet feil" + }, + "progress": { + "install_addon": "Vennligst vent mens installasjonen av Silicon Labs Multiprotocol-tillegget fullf\u00f8res. Dette kan ta flere minutter.", + "start_addon": "Vennligst vent mens oppstarten av Silicon Labs Multiprotocol-tillegget fullf\u00f8res. Dette kan ta noen sekunder." + }, + "step": { + "addon_installed_other_device": { + "title": "Multiprotokollst\u00f8tte er allerede aktivert for en annen enhet" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Aktiver st\u00f8tte for multiprotokoll" + }, + "description": "N\u00e5r multiprotokollst\u00f8tte er aktivert, kan Home Assistant Yellows IEEE 802.15.4-radio brukes for b\u00e5de Zigbee og Thread (brukt av Matter) samtidig. Merk: Dette er en eksperimentell funksjon.", + "title": "Aktiver st\u00f8tte for multiprotokoll p\u00e5 IEEE 802.15.4-radioen" + }, + "install_addon": { + "title": "Silicon Labs Multiprotocol-tilleggsinstallasjonen har startet" + }, + "show_revert_guide": { + "description": "Hvis du vil bytte til kun Zigbee-firmware, m\u00e5 du fullf\u00f8re f\u00f8lgende manuelle trinn: \n\n * Fjern Silicon Labs Multiprotocol-tillegget \n\n * Flash den eneste Zigbee-fastvaren, f\u00f8lg veiledningen p\u00e5 https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Konfigurer ZHA p\u00e5 nytt for \u00e5 migrere innstillinger til radioen med oppdatering", + "title": "Multiprotokollst\u00f8tte er aktivert for denne enheten" + }, + "start_addon": { + "title": "Silicon Labs Multiprotocol-tillegget starter." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/pt-BR.json b/homeassistant/components/homeassistant_yellow/translations/pt-BR.json new file mode 100644 index 00000000000..ecbc1a36d62 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/pt-BR.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Falha ao obter informa\u00e7\u00f5es do add-on Silicon Labs Multiprotocol.", + "addon_install_failed": "Falha ao instalar o add-on Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Falha ao definir a configura\u00e7\u00e3o multiprotocolo da Silicon Labs.", + "addon_start_failed": "Falha ao iniciar o add-on Silicon Labs Multiprotocol.", + "not_hassio": "As op\u00e7\u00f5es de hardware s\u00f3 podem ser configuradas em instala\u00e7\u00f5es HassOS." + }, + "error": { + "unknown": "Erro inesperado" + }, + "progress": { + "install_addon": "Aguarde enquanto a instala\u00e7\u00e3o do add-on Silicon Labs Multiprotocol \u00e9 conclu\u00edda. Isso pode levar v\u00e1rios minutos.", + "start_addon": "Aguarde enquanto a inicializa\u00e7\u00e3o do add-on Silicon Labs Multiprotocol \u00e9 conclu\u00edda. Isso pode levar alguns segundos." + }, + "step": { + "addon_installed_other_device": { + "title": "O suporte multiprotocolo j\u00e1 est\u00e1 ativado para outro dispositivo" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Habilitar suporte multiprotocolo" + }, + "description": "Quando o suporte multiprotocolo est\u00e1 ativado, o r\u00e1dio IEEE 802.15.4 do Home Assistant Yellow pode ser usado para Zigbee e Thread (usado por Matter) ao mesmo tempo. Nota: Este \u00e9 um recurso experimental.", + "title": "Habilitar o suporte multiprotocolo no r\u00e1dio IEEE 802.15.4" + }, + "install_addon": { + "title": "A instala\u00e7\u00e3o do add-on Silicon Labs Multiprotocol foi iniciada" + }, + "show_revert_guide": { + "description": "Se voc\u00ea deseja alterar para o firmware somente Zigbee, conclua as seguintes etapas manuais: \n\n * Remova o add-on Silicon Labs Multiprotocol \n\n * Atualize apenas o firmware Zigbee, siga o guia em https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Reconfigure o ZHA para migrar as configura\u00e7\u00f5es para o r\u00e1dio reflashed", + "title": "O suporte multiprotocolo est\u00e1 ativado para este dispositivo" + }, + "start_addon": { + "title": "O add-on Silicon Labs Multiprotocol est\u00e1 iniciando." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/ru.json b/homeassistant/components/homeassistant_yellow/translations/ru.json new file mode 100644 index 00000000000..375c16ec566 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/ru.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \"Silicon Labs Multiprotocol\".", + "addon_install_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".", + "addon_set_config_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\".", + "addon_start_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".", + "not_hassio": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445 HassOS." + }, + "error": { + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "progress": { + "install_addon": "\u041f\u043e\u0434\u043e\u0436\u0434\u0438\u0442\u0435, \u043f\u043e\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\". \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442.", + "start_addon": "\u041f\u043e\u0434\u043e\u0436\u0434\u0438\u0442\u0435, \u043f\u043e\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0441\u044f \u0437\u0430\u043f\u0443\u0441\u043a \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\". \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434." + }, + "step": { + "addon_installed_other_device": { + "title": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432 \u0443\u0436\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u0434\u043b\u044f \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432" + }, + "description": "\u041a\u043e\u0433\u0434\u0430 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432, \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c Home Assistant Yellow \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 IEEE 802.15.4 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043a\u0430\u043a \u0434\u043b\u044f Zigbee, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f Thread (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 Matter). \u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435: \u044d\u0442\u043e \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f.", + "title": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432 \u043d\u0430 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u0435 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 IEEE 802.15.4." + }, + "install_addon": { + "title": "\u041d\u0430\u0447\u0430\u043b\u0430\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\"" + }, + "show_revert_guide": { + "description": "\u0415\u0441\u043b\u0438 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043d\u0430 \u043f\u0440\u043e\u0448\u0438\u0432\u043a\u0443, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0449\u0443\u044e \u0442\u043e\u043b\u044c\u043a\u043e Zigbee, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f:\n\n* \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".\n\n* \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u043f\u0440\u043e\u0448\u0438\u0432\u043a\u0443 Zigbee, \u0441\u043b\u0435\u0434\u0443\u044f \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0443 \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n* \u041f\u0435\u0440\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 ZHA \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043d\u0430 \u0437\u0430\u043d\u043e\u0432\u043e \u043f\u0440\u043e\u0448\u0438\u0442\u044b\u0439 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c.", + "title": "\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432" + }, + "start_addon": { + "title": "\u0414\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 Silicon Labs Multiprotocol \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/knx/translations/bg.json b/homeassistant/components/knx/translations/bg.json index 022fd141b76..b4dc6afc1b8 100644 --- a/homeassistant/components/knx/translations/bg.json +++ b/homeassistant/components/knx/translations/bg.json @@ -33,6 +33,12 @@ "user_id": "\u0418\u0414 \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f", "user_password": "\u041f\u0430\u0440\u043e\u043b\u0430 \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f" } + }, + "secure_tunnel_manual": { + "data": { + "user_id": "ID \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f", + "user_password": "\u041f\u0430\u0440\u043e\u043b\u0430 \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f" + } } } }, diff --git a/homeassistant/components/knx/translations/pt-BR.json b/homeassistant/components/knx/translations/pt-BR.json index bae7dd2f82c..52c23d388d7 100644 --- a/homeassistant/components/knx/translations/pt-BR.json +++ b/homeassistant/components/knx/translations/pt-BR.json @@ -110,9 +110,9 @@ }, "options": { "error": { - "cannot_connect": "Falhou ao conectar", + "cannot_connect": "Falha ao conectar", "file_not_found": "O arquivo `.knxkeys` especificado n\u00e3o foi encontrado no caminho config/.storage/knx/", - "invalid_individual_address": "O valor n\u00e3o corresponde ao padr\u00e3o do endere\u00e7o individual KNX.\n'\u00e1rea.linha.dispositivo'", + "invalid_individual_address": "O valor n\u00e3o corresponde ao padr\u00e3o do endere\u00e7o individual KNX.\n '\u00e1rea.linha.dispositivo'", "invalid_ip_address": "Endere\u00e7o IPv4 inv\u00e1lido.", "invalid_signature": "A senha para descriptografar o arquivo `.knxkeys` est\u00e1 errada.", "no_router_discovered": "Nenhum roteador KNXnet/IP foi descoberto na rede.", @@ -156,19 +156,19 @@ }, "manual_tunnel": { "data": { - "host": "Host", + "host": "Nome do host", "local_ip": "IP local do Home Assistant", "port": "Porta", "route_back": "Rota de volta / modo NAT", "tunneling_type": "Tipo de t\u00fanel KNX" }, "data_description": { - "host": "Endere\u00e7o IP do dispositivo de encapsulamento KNX/IP.", + "host": "Endere\u00e7o IP do dispositivo de tunelamento KNX/IP.", "local_ip": "Deixe em branco para usar a descoberta autom\u00e1tica.", - "port": "Porta do dispositivo de encapsulamento KNX/IP.", + "port": "Porta do dispositivo de tunelamento KNX/IP.", "route_back": "Ative se o servidor de encapsulamento KNXnet/IP estiver atr\u00e1s do NAT. Aplica-se apenas a conex\u00f5es UDP." }, - "description": "Insira as informa\u00e7\u00f5es de conex\u00e3o do seu dispositivo de encapsulamento." + "description": "Por favor, digite as informa\u00e7\u00f5es de conex\u00e3o do seu dispositivo de tunelamento." }, "options_init": { "menu_options": { @@ -184,7 +184,7 @@ "multicast_port": "Porta multicast" }, "data_description": { - "individual_address": "Endere\u00e7o KNX a ser utilizado pelo Home Assistant, por ex. `0.0.4`", + "individual_address": "Endere\u00e7o KNX a ser usado pelo Home Assistant, por exemplo, `0.0.4`", "local_ip": "Deixe em branco para usar a descoberta autom\u00e1tica." }, "description": "Por favor, configure as op\u00e7\u00f5es de roteamento." @@ -195,7 +195,7 @@ "knxkeys_password": "A senha para descriptografar o arquivo `.knxkeys`" }, "data_description": { - "knxkeys_filename": "Espera-se que o arquivo seja encontrado em seu diret\u00f3rio de configura\u00e7\u00e3o em `.storage/knx/`.\nNo sistema operacional Home Assistant seria `/config/.storage/knx/`\nExemplo: `my_project.knxkeys`", + "knxkeys_filename": "Espera-se que o arquivo seja encontrado em seu diret\u00f3rio de configura\u00e7\u00e3o em `.storage/knx/`.\n No sistema operacional Home Assistant seria `/config/.storage/knx/`\n Exemplo: `my_project.knxkeys`", "knxkeys_password": "Isso foi definido ao exportar o arquivo do ETS." }, "description": "Por favor, insira as informa\u00e7\u00f5es para o seu arquivo `.knxkeys`." @@ -222,7 +222,7 @@ }, "tunnel": { "data": { - "gateway": "Conex\u00e3o do KNX Tunnel", + "gateway": "Conex\u00e3o do t\u00fanel KNX", "host": "Nome do host", "port": "Porta", "tunneling_type": "Tipo de t\u00fanel KNX" diff --git a/homeassistant/components/livisi/translations/pt-BR.json b/homeassistant/components/livisi/translations/pt-BR.json index ff56d2c5aca..94145dafc78 100644 --- a/homeassistant/components/livisi/translations/pt-BR.json +++ b/homeassistant/components/livisi/translations/pt-BR.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falhou ao conectar", + "cannot_connect": "Falha ao conectar", "wrong_ip_address": "O endere\u00e7o IP est\u00e1 incorreto ou o SHC n\u00e3o pode ser alcan\u00e7ado localmente.", "wrong_password": "A senha est\u00e1 incorreta." }, diff --git a/homeassistant/components/mqtt/translations/pt-BR.json b/homeassistant/components/mqtt/translations/pt-BR.json index d0fb407331a..a1676396477 100644 --- a/homeassistant/components/mqtt/translations/pt-BR.json +++ b/homeassistant/components/mqtt/translations/pt-BR.json @@ -93,7 +93,7 @@ "broker": { "data": { "advanced_options": "Op\u00e7\u00f5es avan\u00e7adas", - "broker": "Broker", + "broker": "Endere\u00e7o do Broker", "certificate": "Carregar arquivo de certificado de CA personalizado", "client_cert": "Carregar arquivo de certificado do cliente", "client_id": "ID do cliente (deixe em branco para um gerado aleatoriamente)", @@ -107,7 +107,7 @@ "tls_insecure": "Ignorar a valida\u00e7\u00e3o do certificado do corretor", "username": "Usu\u00e1rio" }, - "description": "Insira as informa\u00e7\u00f5es de conex\u00e3o do seu broker MQTT.", + "description": "Por favor, insira as informa\u00e7\u00f5es de conex\u00e3o do seu agente MQTT.", "title": "Op\u00e7\u00f5es do broker" }, "options": { diff --git a/homeassistant/components/onvif/translations/ru.json b/homeassistant/components/onvif/translations/ru.json index d9b09be4d42..1b30aefb519 100644 --- a/homeassistant/components/onvif/translations/ru.json +++ b/homeassistant/components/onvif/translations/ru.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b FFMPEG", - "rtsp_transport": "\u0422\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c RTSP" + "rtsp_transport": "\u0422\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c RTSP", + "use_wallclock_as_timestamps": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0435\u043d\u043d\u044b\u0435 \u0447\u0430\u0441\u044b \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u043a" }, "title": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 ONVIF" } diff --git a/homeassistant/components/plant/translations/hu.json b/homeassistant/components/plant/translations/hu.json index ad2061411f5..dbfa2b73fe1 100644 --- a/homeassistant/components/plant/translations/hu.json +++ b/homeassistant/components/plant/translations/hu.json @@ -5,5 +5,5 @@ "problem": "Probl\u00e9ma" } }, - "title": "N\u00f6v\u00e9nyfigyel\u0151" + "title": "Er\u0151m\u0171 monitoroz\u00e1s" } \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/pt-BR.json b/homeassistant/components/pushbullet/translations/pt-BR.json index 16a928353cd..7638ab3ca44 100644 --- a/homeassistant/components/pushbullet/translations/pt-BR.json +++ b/homeassistant/components/pushbullet/translations/pt-BR.json @@ -4,13 +4,13 @@ "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falhou ao conectar", + "cannot_connect": "Falha ao conectar", "invalid_api_key": "Chave de API inv\u00e1lida" }, "step": { "user": { "data": { - "api_key": "Chave de API", + "api_key": "Chave da API", "name": "Nome" } } diff --git a/homeassistant/components/shelly/translations/ru.json b/homeassistant/components/shelly/translations/ru.json index 31b2a8ca9a1..30f28100fa9 100644 --- a/homeassistant/components/shelly/translations/ru.json +++ b/homeassistant/components/shelly/translations/ru.json @@ -58,5 +58,18 @@ "single_push": "{subtype} \u043d\u0430\u0436\u0430\u0442\u0430 \u043e\u0434\u0438\u043d \u0440\u0430\u0437", "triple": "{subtype} \u043d\u0430\u0436\u0430\u0442\u0430 \u0442\u0440\u0438 \u0440\u0430\u0437\u0430" } + }, + "options": { + "abort": { + "ble_unsupported": "\u0414\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 Bluetooth \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0448\u0438\u0432\u043a\u0430 \u0432\u0435\u0440\u0441\u0438\u0438 {ble_min_version} \u0438\u043b\u0438 \u043d\u043e\u0432\u0435\u0435." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "\u0420\u0435\u0436\u0438\u043c \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f Bluetooth" + }, + "description": "\u0421\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 Bluetooth \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u043c \u0438\u043b\u0438 \u043f\u0430\u0441\u0441\u0438\u0432\u043d\u044b\u043c. \u041f\u0440\u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 Shelly \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0443 \u0431\u043b\u0438\u0437\u043b\u0435\u0436\u0430\u0449\u0438\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432; \u043f\u0440\u0438 \u043f\u0430\u0441\u0441\u0438\u0432\u043d\u043e\u043c Shelly \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043d\u0435\u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0442 \u0431\u043b\u0438\u0437\u043b\u0435\u0436\u0430\u0449\u0438\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tasmota/translations/fr.json b/homeassistant/components/tasmota/translations/fr.json index 9ead135ce72..d24dbdf53b9 100644 --- a/homeassistant/components/tasmota/translations/fr.json +++ b/homeassistant/components/tasmota/translations/fr.json @@ -19,6 +19,7 @@ }, "issues": { "topic_duplicated": { + "description": "Plusieurs appareils Tasmota partagent le sujet {topic}.\n\nAppareils Tasmota avec ce probl\u00e8me : {offenders}.", "title": "Plusieurs appareils Tasmota partagent le m\u00eame sujet" }, "topic_no_prefix": { diff --git a/homeassistant/components/unifiprotect/translations/ru.json b/homeassistant/components/unifiprotect/translations/ru.json index 0789e3f29ea..bcb45a539db 100644 --- a/homeassistant/components/unifiprotect/translations/ru.json +++ b/homeassistant/components/unifiprotect/translations/ru.json @@ -48,6 +48,18 @@ }, "ea_warning": { "description": "\u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 UniFi Protect v{version}. \u0412\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f Home Assistant \u0438 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.", + "fix_flow": { + "step": { + "confirm": { + "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 UniFi Protect? \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.", + "title": "v{version} - \u0432\u0435\u0440\u0441\u0438\u044f \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430" + }, + "start": { + "description": "\u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 UniFi Protect v{version}, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0435\u0440\u0441\u0438\u0435\u0439 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430. [\u0412\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access), \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438.\n\n\u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044f \u044d\u0442\u0443 \u0444\u043e\u0440\u043c\u0443, \u0412\u044b \u043b\u0438\u0431\u043e [\u043e\u0442\u043a\u0430\u0442\u0438\u043b\u0438\u0441\u044c \u043d\u0430 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect), \u043b\u0438\u0431\u043e \u0441\u043e\u0433\u043b\u0430\u0448\u0430\u0435\u0442\u0435\u0441\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e UniFi Protect.", + "title": "v{version} - \u0432\u0435\u0440\u0441\u0438\u044f \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430" + } + } + }, "title": "UniFi Protect v{version} \u2014 \u044d\u0442\u043e \u0432\u0435\u0440\u0441\u0438\u044f \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430" } }, @@ -59,6 +71,7 @@ "init": { "data": { "all_updates": "\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 (\u0412\u041d\u0418\u041c\u0410\u041d\u0418\u0415: \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u0426\u041f)", + "allow_ea": "\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 UniFi Protect (\u0412\u041d\u0418\u041c\u0410\u041d\u0418\u0415: \u0412\u0430\u0448\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043c\u0435\u0447\u0435\u043d\u0430 \u043a\u0430\u043a \u043d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f)", "disable_rtsp": "\u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0442\u043e\u043a RTSP", "ignored_devices": "\u0421\u043f\u0438\u0441\u043e\u043a MAC-\u0430\u0434\u0440\u0435\u0441\u043e\u0432 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043f\u044f\u0442\u0443\u044e", "max_media": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0434\u043b\u044f \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u043c\u0443\u043b\u044c\u0442\u0438\u043c\u0435\u0434\u0438\u0430 (\u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438)", diff --git a/homeassistant/components/wake_on_lan/translations/bg.json b/homeassistant/components/wake_on_lan/translations/bg.json new file mode 100644 index 00000000000..daa173607c0 --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/bg.json @@ -0,0 +1,7 @@ +{ + "issues": { + "moved_yaml": { + "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Wake on Lan \u0435 \u043f\u0440\u0435\u043c\u0435\u0441\u0442\u0435\u043d\u0430" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/ru.json b/homeassistant/components/wake_on_lan/translations/ru.json new file mode 100644 index 00000000000..cc9445ad551 --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/ru.json @@ -0,0 +1,7 @@ +{ + "issues": { + "moved_yaml": { + "title": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f YAML Wake on Lan \u0431\u044b\u043b\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0430" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/pt-BR.json b/homeassistant/components/zamg/translations/pt-BR.json index a57030ce56d..e35fbd0e49e 100644 --- a/homeassistant/components/zamg/translations/pt-BR.json +++ b/homeassistant/components/zamg/translations/pt-BR.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falhou ao conectar" + "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", + "cannot_connect": "Falha ao conectar" }, "error": { - "cannot_connect": "Falhou ao conectar" + "cannot_connect": "Falha ao conectar" }, "flow_title": "{name}", "step": { From 6bd70fdf15bbafd337a5fc0e88f1e10d87dcbb25 Mon Sep 17 00:00:00 2001 From: jan iversen Date: Fri, 18 Nov 2022 09:32:51 +0100 Subject: [PATCH 0538/1033] Solve Modbus reload issue (#82253) fixes undefined --- homeassistant/components/modbus/__init__.py | 1 - homeassistant/components/modbus/modbus.py | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/modbus/__init__.py b/homeassistant/components/modbus/__init__.py index d905a5f9423..a62d8537a33 100644 --- a/homeassistant/components/modbus/__init__.py +++ b/homeassistant/components/modbus/__init__.py @@ -386,4 +386,3 @@ async def async_reset_platform(hass: HomeAssistant, integration_name: str) -> No hubs = hass.data[DOMAIN] for name in hubs: await hubs[name].async_close() - del hass.data[DOMAIN] diff --git a/homeassistant/components/modbus/modbus.py b/homeassistant/components/modbus/modbus.py index 5e15f65035a..e2240f530c6 100644 --- a/homeassistant/components/modbus/modbus.py +++ b/homeassistant/components/modbus/modbus.py @@ -132,6 +132,12 @@ async def async_modbus_setup( await async_setup_reload_service(hass, DOMAIN, [DOMAIN]) + if DOMAIN in hass.data and config[DOMAIN] == []: + hubs = hass.data[DOMAIN] + for name in hubs: + if not await hubs[name].async_setup(): + return False + hass.data[DOMAIN] = hub_collect = {} for conf_hub in config[DOMAIN]: my_hub = ModbusHub(hass, conf_hub) From 547b36d09a21751937321ab4792fbe9fdf3c0655 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Fri, 18 Nov 2022 09:56:34 +0100 Subject: [PATCH 0539/1033] Add kilo watts unit mapping for nibe_heatpump (#82284) --- homeassistant/components/nibe_heatpump/sensor.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/homeassistant/components/nibe_heatpump/sensor.py b/homeassistant/components/nibe_heatpump/sensor.py index 66c66aaabe1..0b12afd9e05 100644 --- a/homeassistant/components/nibe_heatpump/sensor.py +++ b/homeassistant/components/nibe_heatpump/sensor.py @@ -19,6 +19,7 @@ from homeassistant.const import ( ENERGY_KILO_WATT_HOUR, ENERGY_MEGA_WATT_HOUR, ENERGY_WATT_HOUR, + POWER_KILO_WATT, POWER_WATT, TEMP_CELSIUS, TEMP_FAHRENHEIT, @@ -80,6 +81,13 @@ UNIT_DESCRIPTIONS = { state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=POWER_WATT, ), + "kW": SensorEntityDescription( + key="kW", + entity_category=EntityCategory.DIAGNOSTIC, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=POWER_KILO_WATT, + ), "Wh": SensorEntityDescription( key="Wh", entity_category=EntityCategory.DIAGNOSTIC, From 7b4d4d11ac56b0b3406a2570032cc31e242f6438 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 18 Nov 2022 10:09:18 +0100 Subject: [PATCH 0540/1033] Remove unused variable from homekit_controller (#82307) --- homeassistant/components/homekit_controller/entity.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/homekit_controller/entity.py b/homeassistant/components/homekit_controller/entity.py index 1c492decf49..eecf8b9c080 100644 --- a/homeassistant/components/homekit_controller/entity.py +++ b/homeassistant/components/homekit_controller/entity.py @@ -30,7 +30,6 @@ class HomeKitEntity(Entity): self._aid = devinfo["aid"] self._iid = devinfo["iid"] self._char_name: str | None = None - self._features = 0 self.setup() super().__init__() From b166a6d88b94c223fce03dde8d3543d9f601c4fc Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 18 Nov 2022 10:09:40 +0100 Subject: [PATCH 0541/1033] Cleanup supported_features default values (#82306) --- homeassistant/components/doorbird/camera.py | 5 ++--- homeassistant/components/evohome/climate.py | 5 ++--- homeassistant/components/generic/camera.py | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/doorbird/camera.py b/homeassistant/components/doorbird/camera.py index fce76a65ff5..5983e639851 100644 --- a/homeassistant/components/doorbird/camera.py +++ b/homeassistant/components/doorbird/camera.py @@ -96,9 +96,8 @@ class DoorBirdCamera(DoorBirdEntity, Camera): self._stream_url = stream_url self._attr_name = name self._last_image: bytes | None = None - self._attr_supported_features = ( - CameraEntityFeature.STREAM if self._stream_url else 0 - ) + if self._stream_url: + self._attr_supported_features = CameraEntityFeature.STREAM self._interval = interval self._last_update = datetime.datetime.min self._attr_unique_id = f"{self._mac_addr}_{camera_id}" diff --git a/homeassistant/components/evohome/climate.py b/homeassistant/components/evohome/climate.py index 0ec64c6b2b1..4594268623c 100644 --- a/homeassistant/components/evohome/climate.py +++ b/homeassistant/components/evohome/climate.py @@ -321,9 +321,8 @@ class EvoController(EvoClimateEntity): self._attr_preset_modes = [ TCS_PRESET_TO_HA[m] for m in modes if m in list(TCS_PRESET_TO_HA) ] - self._attr_supported_features = ( - ClimateEntityFeature.PRESET_MODE if self._attr_preset_modes else 0 - ) + if self._attr_preset_modes: + self._attr_supported_features = ClimateEntityFeature.PRESET_MODE async def async_tcs_svc_request(self, service: str, data: dict[str, Any]) -> None: """Process a service request (system mode) for a controller. diff --git a/homeassistant/components/generic/camera.py b/homeassistant/components/generic/camera.py index 961d3cecfb7..b039b32d73d 100644 --- a/homeassistant/components/generic/camera.py +++ b/homeassistant/components/generic/camera.py @@ -165,9 +165,8 @@ class GenericCamera(Camera): self._stream_source.hass = hass self._limit_refetch = device_info[CONF_LIMIT_REFETCH_TO_URL_CHANGE] self._attr_frame_interval = 1 / device_info[CONF_FRAMERATE] - self._attr_supported_features = ( - CameraEntityFeature.STREAM if self._stream_source else 0 - ) + if self._stream_source: + self._attr_supported_features = CameraEntityFeature.STREAM self.content_type = device_info[CONF_CONTENT_TYPE] self.verify_ssl = device_info[CONF_VERIFY_SSL] if device_info.get(CONF_RTSP_TRANSPORT): From 5453b346dd54741be0edce1a78476657f6197c2c Mon Sep 17 00:00:00 2001 From: Jevgeni Kiski Date: Fri, 18 Nov 2022 11:16:54 +0200 Subject: [PATCH 0542/1033] Add Vallox bypass locking switch entity (#75857) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Switch entity * adding missing function for tests * Apply suggestions from code review Co-authored-by: Sebastian Lövdahl * Review * fix * Update homeassistant/components/vallox/switch.py Co-authored-by: Andre Richter * Apply suggestions from code review Co-authored-by: Martin Hjelmare * Adding a separate parameter for the expected state Co-authored-by: Sebastian Lövdahl Co-authored-by: Andre Richter Co-authored-by: Martin Hjelmare --- homeassistant/components/vallox/__init__.py | 1 + homeassistant/components/vallox/switch.py | 105 ++++++++++++++++++++ tests/components/vallox/conftest.py | 5 + tests/components/vallox/test_switch.py | 68 +++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 homeassistant/components/vallox/switch.py create mode 100644 tests/components/vallox/test_switch.py diff --git a/homeassistant/components/vallox/__init__.py b/homeassistant/components/vallox/__init__.py index 6a2234af9e6..1a1788aeeed 100644 --- a/homeassistant/components/vallox/__init__.py +++ b/homeassistant/components/vallox/__init__.py @@ -63,6 +63,7 @@ PLATFORMS: list[str] = [ Platform.SENSOR, Platform.FAN, Platform.BINARY_SENSOR, + Platform.SWITCH, ] ATTR_PROFILE_FAN_SPEED = "fan_speed" diff --git a/homeassistant/components/vallox/switch.py b/homeassistant/components/vallox/switch.py new file mode 100644 index 00000000000..4c8116ebd27 --- /dev/null +++ b/homeassistant/components/vallox/switch.py @@ -0,0 +1,105 @@ +"""Support for Vallox ventilation unit switches.""" +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +from vallox_websocket_api import Vallox + +from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import ValloxDataUpdateCoordinator, ValloxEntity +from .const import DOMAIN + + +class ValloxSwitchEntity(ValloxEntity, SwitchEntity): + """Representation of a Vallox switch.""" + + entity_description: ValloxSwitchEntityDescription + _attr_entity_category = EntityCategory.CONFIG + _attr_has_entity_name = True + + def __init__( + self, + name: str, + coordinator: ValloxDataUpdateCoordinator, + description: ValloxSwitchEntityDescription, + client: Vallox, + ) -> None: + """Initialize the Vallox switch.""" + super().__init__(name, coordinator) + + self.entity_description = description + + self._attr_unique_id = f"{self._device_uuid}-{description.key}" + self._client = client + + @property + def is_on(self) -> bool | None: + """Return true if the switch is on.""" + if ( + value := self.coordinator.data.get_metric( + self.entity_description.metric_key + ) + ) is None: + return None + return value == 1 + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn on.""" + await self._set_value(True) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn off.""" + await self._set_value(False) + + async def _set_value(self, value: bool) -> None: + """Update the current value.""" + metric_key = self.entity_description.metric_key + await self._client.set_values({metric_key: 1 if value else 0}) + await self.coordinator.async_request_refresh() + + +@dataclass +class ValloxMetricKeyMixin: + """Dataclass to allow defining metric_key without a default value.""" + + metric_key: str + + +@dataclass +class ValloxSwitchEntityDescription(SwitchEntityDescription, ValloxMetricKeyMixin): + """Describes Vallox switch entity.""" + + +SWITCH_ENTITIES: tuple[ValloxSwitchEntityDescription, ...] = ( + ValloxSwitchEntityDescription( + key="bypass_locked", + name="Bypass locked", + icon="mdi:arrow-horizontal-lock", + metric_key="A_CYC_BYPASS_LOCKED", + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the switches.""" + + data = hass.data[DOMAIN][entry.entry_id] + client = data["client"] + client.set_settable_address("A_CYC_BYPASS_LOCKED", int) + + async_add_entities( + [ + ValloxSwitchEntity(data["name"], data["coordinator"], description, client) + for description in SWITCH_ENTITIES + ] + ) diff --git a/tests/components/vallox/conftest.py b/tests/components/vallox/conftest.py index ef9fd2a0e4b..0c14f359b5f 100644 --- a/tests/components/vallox/conftest.py +++ b/tests/components/vallox/conftest.py @@ -39,6 +39,11 @@ def patch_metrics(metrics: dict[str, Any]): ) +def patch_metrics_set(): + """Patch the Vallox metrics set values.""" + return patch("homeassistant.components.vallox.Vallox.set_values") + + @pytest.fixture(autouse=True) def patch_profile_home(): """Patch the Vallox profile response.""" diff --git a/tests/components/vallox/test_switch.py b/tests/components/vallox/test_switch.py new file mode 100644 index 00000000000..5a3f0ebd648 --- /dev/null +++ b/tests/components/vallox/test_switch.py @@ -0,0 +1,68 @@ +"""Tests for Vallox switch platform.""" +import pytest + +from homeassistant.components.switch.const import DOMAIN as SWITCH_DOMAIN +from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON +from homeassistant.core import HomeAssistant + +from .conftest import patch_metrics, patch_metrics_set + +from tests.common import MockConfigEntry + + +@pytest.mark.parametrize( + "entity_id, metric_key, value, expected_state", + [ + ("switch.vallox_bypass_locked", "A_CYC_BYPASS_LOCKED", 1, "on"), + ("switch.vallox_bypass_locked", "A_CYC_BYPASS_LOCKED", 0, "off"), + ], +) +async def test_switch_entities( + entity_id: str, + metric_key: str, + value: int, + expected_state: str, + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test switch entities.""" + # Arrange + metrics = {metric_key: value} + + # Act + with patch_metrics(metrics=metrics): + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + + # Assert + sensor = hass.states.get(entity_id) + assert sensor + assert sensor.state == expected_state + + +@pytest.mark.parametrize( + "service, metric_key, value", + [ + (SERVICE_TURN_ON, "A_CYC_BYPASS_LOCKED", 1), + (SERVICE_TURN_OFF, "A_CYC_BYPASS_LOCKED", 0), + ], +) +async def test_bypass_lock_switch_entitity_set( + service: str, + metric_key: str, + value: int, + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test bypass lock switch set.""" + # Act + with patch_metrics(metrics={}), patch_metrics_set() as metrics_set: + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + await hass.services.async_call( + SWITCH_DOMAIN, + service, + service_data={ATTR_ENTITY_ID: "switch.vallox_bypass_locked"}, + ) + await hass.async_block_till_done() + metrics_set.assert_called_once_with({metric_key: value}) From fb909d694f6b4ac4574beb207c754ec3472ae033 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 18 Nov 2022 12:12:27 +0100 Subject: [PATCH 0543/1033] Improve type hints MQTT light schema json (#82208) * Improve type hints schema json * Add hint for brightness * Follow up comments * Follow up missed comments * Correct hint on flash arg * hints on one line --- .../components/mqtt/light/schema_json.py | 111 ++++++++++-------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index 76307b61e24..9547b6d31a8 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -3,6 +3,7 @@ from __future__ import annotations from contextlib import suppress import logging +from typing import Any, cast import voluptuous as vol @@ -62,6 +63,7 @@ from ..const import ( ) from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity +from ..models import ReceiveMessage from ..util import get_mqtt_data, valid_subscribe_topic from .schema import MQTT_LIGHT_SCHEMA_SCHEMA from .schema_basic import ( @@ -102,7 +104,7 @@ CONF_MIN_MIREDS = "min_mireds" CONF_WHITE_VALUE = "white_value" -def valid_color_configuration(config): +def valid_color_configuration(config: ConfigType) -> ConfigType: """Test color_mode is not combined with deprecated config.""" deprecated = {CONF_COLOR_TEMP, CONF_HS, CONF_RGB, CONF_XY} if config[CONF_COLOR_MODE] and any(config.get(key) for key in deprecated): @@ -194,21 +196,27 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): _entity_id_format = ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED - def __init__(self, hass, config, config_entry, discovery_data): - """Initialize MQTT JSON light.""" - self._topic = None - self._optimistic = False - self._fixed_color_mode = None - self._flash_times = None + _flash_times: dict[str, int | None] + _topic: dict[str, str | None] + _optimistic: bool + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: + """Initialize MQTT JSON light.""" + self._fixed_color_mode: ColorMode | str | None = None MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA_JSON - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" self._attr_max_mireds = config.get(CONF_MAX_MIREDS, super().max_mireds) self._attr_min_mireds = config.get(CONF_MIN_MIREDS, super().min_mireds) @@ -217,7 +225,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): self._topic = { key: config.get(key) for key in (CONF_STATE_TOPIC, CONF_COMMAND_TOPIC) } - optimistic = config[CONF_OPTIMISTIC] + optimistic: bool = config[CONF_OPTIMISTIC] self._optimistic = optimistic or self._topic[CONF_STATE_TOPIC] is None self._flash_times = { @@ -240,14 +248,14 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): if config[CONF_HS] or config[CONF_RGB] or config[CONF_XY]: color_modes.add(ColorMode.HS) self._attr_supported_color_modes = filter_supported_color_modes(color_modes) - if len(self.supported_color_modes) == 1: + if self.supported_color_modes and len(self.supported_color_modes) == 1: self._fixed_color_mode = next(iter(self.supported_color_modes)) else: self._attr_supported_color_modes = self._config[CONF_SUPPORTED_COLOR_MODES] - if len(self.supported_color_modes) == 1: + if self.supported_color_modes and len(self.supported_color_modes) == 1: self._attr_color_mode = next(iter(self.supported_color_modes)) - def _update_color(self, values): + def _update_color(self, values: dict[str, Any]) -> None: if not self._config[CONF_COLOR_MODE]: # Deprecated color handling try: @@ -287,7 +295,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): ) return else: - color_mode = values["color_mode"] + color_mode: str = values["color_mode"] if not self._supports_color_mode(color_mode): _LOGGER.warning( "Invalid color mode received for entity %s", self.entity_id @@ -336,14 +344,14 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): self.entity_id, ) - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" @callback @log_messages(self.hass, self.entity_id) - def state_received(msg): + def state_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages.""" - values = json_loads(msg.payload) + values: dict[str, Any] = json_loads(msg.payload) if values["state"] == "ON": self._attr_is_on = True @@ -382,7 +390,8 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): ) if ( - ColorMode.COLOR_TEMP in self.supported_color_modes + self.supported_color_modes + and ColorMode.COLOR_TEMP in self.supported_color_modes and not self._config[CONF_COLOR_MODE] ): # Deprecated color handling @@ -419,7 +428,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): }, ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) @@ -448,12 +457,12 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): self._attr_xy_color = last_attributes.get(ATTR_XY_COLOR, self.xy_color) @property - def assumed_state(self): + def assumed_state(self) -> bool: """Return true if we do optimistic updates.""" return self._optimistic @property - def color_mode(self): + def color_mode(self) -> ColorMode | str | None: """Return current color mode.""" if self._config[CONF_COLOR_MODE]: return self._attr_color_mode @@ -465,41 +474,49 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): return ColorMode.HS return ColorMode.COLOR_TEMP - def _set_flash_and_transition(self, message, **kwargs): + def _set_flash_and_transition(self, message: dict[str, Any], **kwargs: Any) -> None: if ATTR_TRANSITION in kwargs: message["transition"] = kwargs[ATTR_TRANSITION] if ATTR_FLASH in kwargs: - flash = kwargs.get(ATTR_FLASH) + flash: str = kwargs[ATTR_FLASH] if flash == FLASH_LONG: message["flash"] = self._flash_times[CONF_FLASH_TIME_LONG] elif flash == FLASH_SHORT: message["flash"] = self._flash_times[CONF_FLASH_TIME_SHORT] - def _scale_rgbxx(self, rgbxx, kwargs): + def _scale_rgbxx(self, rgbxx: tuple[int, ...], kwargs: Any) -> tuple[int, ...]: # If there's a brightness topic set, we don't want to scale the # RGBxx values given using the brightness. + brightness: int if self._config[CONF_BRIGHTNESS]: brightness = 255 else: brightness = kwargs.get(ATTR_BRIGHTNESS, 255) return tuple(round(i / 255 * brightness) for i in rgbxx) - def _supports_color_mode(self, color_mode): + def _supports_color_mode(self, color_mode: ColorMode | str) -> bool: """Return True if the light natively supports a color mode.""" return ( - self._config[CONF_COLOR_MODE] and color_mode in self.supported_color_modes + self._config[CONF_COLOR_MODE] + and self.supported_color_modes is not None + and color_mode in self.supported_color_modes ) - async def async_turn_on(self, **kwargs): # noqa: C901 + async def async_turn_on(self, **kwargs: Any) -> None: # noqa: C901 """Turn the device on. This method is a coroutine. """ + brightness: int should_update = False - - message = {"state": "ON"} + hs_color: tuple[float, float] + message: dict[str, Any] = {"state": "ON"} + rgb: tuple[int, ...] + rgbw: tuple[int, ...] + rgbcw: tuple[int, ...] + xy_color: tuple[float, float] if ATTR_HS_COLOR in kwargs and ( self._config[CONF_HS] or self._config[CONF_RGB] or self._config[CONF_XY] @@ -546,37 +563,37 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): message["color"] = {"r": rgb[0], "g": rgb[1], "b": rgb[2]} if self._optimistic: self._attr_color_mode = ColorMode.RGB - self._attr_rgb_color = rgb + self._attr_rgb_color = cast(tuple[int, int, int], rgb) should_update = True if ATTR_RGBW_COLOR in kwargs and self._supports_color_mode(ColorMode.RGBW): - rgb = self._scale_rgbxx(kwargs[ATTR_RGBW_COLOR], kwargs) - message["color"] = {"r": rgb[0], "g": rgb[1], "b": rgb[2], "w": rgb[3]} + rgbw = self._scale_rgbxx(kwargs[ATTR_RGBW_COLOR], kwargs) + message["color"] = {"r": rgbw[0], "g": rgbw[1], "b": rgbw[2], "w": rgbw[3]} if self._optimistic: self._attr_color_mode = ColorMode.RGBW - self._attr_rgbw_color = rgb + self._attr_rgbw_color = cast(tuple[int, int, int, int], rgbw) should_update = True if ATTR_RGBWW_COLOR in kwargs and self._supports_color_mode(ColorMode.RGBWW): - rgb = self._scale_rgbxx(kwargs[ATTR_RGBWW_COLOR], kwargs) + rgbcw = self._scale_rgbxx(kwargs[ATTR_RGBWW_COLOR], kwargs) message["color"] = { - "r": rgb[0], - "g": rgb[1], - "b": rgb[2], - "c": rgb[3], - "w": rgb[4], + "r": rgbcw[0], + "g": rgbcw[1], + "b": rgbcw[2], + "c": rgbcw[3], + "w": rgbcw[4], } if self._optimistic: self._attr_color_mode = ColorMode.RGBWW - self._attr_rgbww_color = rgb + self._attr_rgbww_color = cast(tuple[int, int, int, int, int], rgbcw) should_update = True if ATTR_XY_COLOR in kwargs and self._supports_color_mode(ColorMode.XY): - xy = kwargs[ATTR_XY_COLOR] # pylint: disable=invalid-name - message["color"] = {"x": xy[0], "y": xy[1]} + xy_color = kwargs[ATTR_XY_COLOR] + message["color"] = {"x": xy_color[0], "y": xy_color[1]} if self._optimistic: self._attr_color_mode = ColorMode.XY - self._attr_xy_color = xy + self._attr_xy_color = xy_color should_update = True self._set_flash_and_transition(message, **kwargs) @@ -625,7 +642,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): should_update = True await self.async_publish( - self._topic[CONF_COMMAND_TOPIC], + str(self._topic[CONF_COMMAND_TOPIC]), json_dumps(message), self._config[CONF_QOS], self._config[CONF_RETAIN], @@ -640,17 +657,17 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): if should_update: self.async_write_ha_state() - async def async_turn_off(self, **kwargs): + async def async_turn_off(self, **kwargs: Any) -> None: """Turn the device off. This method is a coroutine. """ - message = {"state": "OFF"} + message: dict[str, Any] = {"state": "OFF"} self._set_flash_and_transition(message, **kwargs) await self.async_publish( - self._topic[CONF_COMMAND_TOPIC], + str(self._topic[CONF_COMMAND_TOPIC]), json_dumps(message), self._config[CONF_QOS], self._config[CONF_RETAIN], From dd960c4e625f792c1f9818eddad7dab1e68c56c1 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 18 Nov 2022 12:41:33 +0100 Subject: [PATCH 0544/1033] Add precipitation device class (#81145) --- homeassistant/components/number/__init__.py | 8 ++++++++ homeassistant/components/sensor/__init__.py | 9 +++++++++ homeassistant/components/sensor/device_condition.py | 2 ++ homeassistant/components/sensor/device_trigger.py | 2 ++ homeassistant/util/unit_system.py | 4 ++++ 5 files changed, 25 insertions(+) diff --git a/homeassistant/components/number/__init__.py b/homeassistant/components/number/__init__.py index 9c828d1a32f..dfd14f5257b 100644 --- a/homeassistant/components/number/__init__.py +++ b/homeassistant/components/number/__init__.py @@ -199,6 +199,14 @@ class NumberDeviceClass(StrEnum): Unit of measurement: `W`, `kW` """ + PRECIPITATION = "precipitation" + """Precipitation. + + Unit of measurement: + - SI / metric: `mm` + - USCS / imperial: `in` + """ + PRECIPITATION_INTENSITY = "precipitation_intensity" """Precipitation intensity. diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index 0382af7f9f8..a1a7e9ce1a7 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -254,6 +254,14 @@ class SensorDeviceClass(StrEnum): Unit of measurement: `W`, `kW` """ + PRECIPITATION = "precipitation" + """Precipitation. + + Unit of measurement: + - SI / metric: `mm` + - USCS / imperial: `in` + """ + PRECIPITATION_INTENSITY = "precipitation_intensity" """Precipitation intensity. @@ -394,6 +402,7 @@ STATE_CLASSES: Final[list[str]] = [cls.value for cls in SensorStateClass] UNIT_CONVERTERS: dict[SensorDeviceClass | str | None, type[BaseUnitConverter]] = { SensorDeviceClass.DISTANCE: DistanceConverter, SensorDeviceClass.GAS: VolumeConverter, + SensorDeviceClass.PRECIPITATION: DistanceConverter, SensorDeviceClass.PRESSURE: PressureConverter, SensorDeviceClass.SPEED: SpeedConverter, SensorDeviceClass.TEMPERATURE: TemperatureConverter, diff --git a/homeassistant/components/sensor/device_condition.py b/homeassistant/components/sensor/device_condition.py index e06355dda8c..55e993f46ba 100644 --- a/homeassistant/components/sensor/device_condition.py +++ b/homeassistant/components/sensor/device_condition.py @@ -52,6 +52,7 @@ CONF_IS_PM10 = "is_pm10" CONF_IS_PM25 = "is_pm25" CONF_IS_POWER = "is_power" CONF_IS_POWER_FACTOR = "is_power_factor" +CONF_IS_PRECIPITATION = "is_precipitation" CONF_IS_PRECIPITATION_INTENSITY = "is_precipitation_intensity" CONF_IS_PRESSURE = "is_pressure" CONF_IS_SPEED = "is_speed" @@ -89,6 +90,7 @@ ENTITY_CONDITIONS = { SensorDeviceClass.PM1: [{CONF_TYPE: CONF_IS_PM1}], SensorDeviceClass.PM10: [{CONF_TYPE: CONF_IS_PM10}], SensorDeviceClass.PM25: [{CONF_TYPE: CONF_IS_PM25}], + SensorDeviceClass.PRECIPITATION: [{CONF_TYPE: CONF_IS_PRECIPITATION}], SensorDeviceClass.PRECIPITATION_INTENSITY: [ {CONF_TYPE: CONF_IS_PRECIPITATION_INTENSITY} ], diff --git a/homeassistant/components/sensor/device_trigger.py b/homeassistant/components/sensor/device_trigger.py index 4f36c8b78cb..ef724881c52 100644 --- a/homeassistant/components/sensor/device_trigger.py +++ b/homeassistant/components/sensor/device_trigger.py @@ -51,6 +51,7 @@ CONF_PM10 = "pm10" CONF_PM25 = "pm25" CONF_POWER = "power" CONF_POWER_FACTOR = "power_factor" +CONF_PRECIPITATION = "precipitation" CONF_PRECIPITATION_INTENSITY = "precipitation_intensity" CONF_PRESSURE = "pressure" CONF_REACTIVE_POWER = "reactive_power" @@ -88,6 +89,7 @@ ENTITY_TRIGGERS = { SensorDeviceClass.PM25: [{CONF_TYPE: CONF_PM25}], SensorDeviceClass.POWER: [{CONF_TYPE: CONF_POWER}], SensorDeviceClass.POWER_FACTOR: [{CONF_TYPE: CONF_POWER_FACTOR}], + SensorDeviceClass.PRECIPITATION: [{CONF_TYPE: CONF_PRECIPITATION}], SensorDeviceClass.PRECIPITATION_INTENSITY: [ {CONF_TYPE: CONF_PRECIPITATION_INTENSITY} ], diff --git a/homeassistant/util/unit_system.py b/homeassistant/util/unit_system.py index 7e338f8f313..6350d80f3d7 100644 --- a/homeassistant/util/unit_system.py +++ b/homeassistant/util/unit_system.py @@ -256,6 +256,8 @@ METRIC_SYSTEM = UnitSystem( ("distance", UnitOfLength.YARDS): UnitOfLength.METERS, # Convert non-metric volumes of gas meters ("gas", UnitOfVolume.CUBIC_FEET): UnitOfVolume.CUBIC_METERS, + # Convert non-metric precipitation + ("precipitation", UnitOfLength.INCHES): UnitOfLength.MILLIMETERS, # Convert non-metric speeds except knots to km/h ("speed", UnitOfSpeed.FEET_PER_SECOND): UnitOfSpeed.KILOMETERS_PER_HOUR, ("speed", UnitOfSpeed.MILES_PER_HOUR): UnitOfSpeed.KILOMETERS_PER_HOUR, @@ -286,6 +288,8 @@ US_CUSTOMARY_SYSTEM = UnitSystem( ("distance", UnitOfLength.MILLIMETERS): UnitOfLength.INCHES, # Convert non-USCS volumes of gas meters ("gas", UnitOfVolume.CUBIC_METERS): UnitOfVolume.CUBIC_FEET, + # Convert non-USCS precipitation + ("precipitation", UnitOfLength.MILLIMETERS): UnitOfLength.INCHES, # Convert non-USCS speeds except knots to mph ("speed", UnitOfSpeed.METERS_PER_SECOND): UnitOfSpeed.MILES_PER_HOUR, ("speed", UnitOfSpeed.KILOMETERS_PER_HOUR): UnitOfSpeed.MILES_PER_HOUR, From 077bd594eb4294d3726ed24badf8bf3a638a06ad Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 18 Nov 2022 07:23:42 -0600 Subject: [PATCH 0545/1033] Fix modbus test assuming repeat listeners always fire at 0 microseconds (#82320) --- tests/components/modbus/test_init.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/components/modbus/test_init.py b/tests/components/modbus/test_init.py index 11ddf4bc426..e238ecc6170 100644 --- a/tests/components/modbus/test_init.py +++ b/tests/components/modbus/test_init.py @@ -733,7 +733,10 @@ async def test_delay(hass, mock_pymodbus): time_stop = time_after_scan + timedelta(seconds=10) now = start_time while now < time_stop: - now += timedelta(seconds=1) + # This test assumed listeners are always fired at 0 + # microseconds which is impossible in production so + # we use 999999 microseconds to simulate the real world. + now += timedelta(seconds=1, microseconds=999999) with mock.patch( "homeassistant.helpers.event.dt_util.utcnow", return_value=now, From 1c6b4967cfec84447ecbcd556901a2e50e4d86b3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 18 Nov 2022 08:19:38 -0600 Subject: [PATCH 0546/1033] Fix litejet tests (#82324) Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- tests/common.py | 39 +++++++++++++++++++++--- tests/components/litejet/test_trigger.py | 4 +-- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/tests/common.py b/tests/common.py index 140f4060d00..b1eec13a851 100644 --- a/tests/common.py +++ b/tests/common.py @@ -380,10 +380,35 @@ fire_mqtt_message = threadsafe_callback_factory(async_fire_mqtt_message) @ha.callback -def async_fire_time_changed( - hass: HomeAssistant, datetime_: datetime = None, fire_all: bool = False +def async_fire_time_changed_exact( + hass: HomeAssistant, datetime_: datetime | None = None, fire_all: bool = False ) -> None: - """Fire a time changed event.""" + """Fire a time changed event at an exact microsecond. + + Consider that its not possible to actually achieve an exact microsecond + in production as the event loop is not precise enough. If your code + relies on this level of precision, consider a different approach + as this is only for testing. + """ + if datetime_ is None: + utc_datetime = date_util.utcnow() + else: + utc_datetime = date_util.as_utc(datetime_) + + _async_fire_time_changed(hass, utc_datetime, fire_all) + + +@ha.callback +def async_fire_time_changed( + hass: HomeAssistant, datetime_: datetime | None = None, fire_all: bool = False +) -> None: + """Fire a time changed event. + + This function will ensure microseconds at at least 500000 + to account for the synchronization repeating listeners. + + If you need to fire an exact microsecond, use async_fire_time_changed_exact. + """ if datetime_ is None: utc_datetime = date_util.utcnow() else: @@ -396,8 +421,14 @@ def async_fire_time_changed( # staggering to avoid thundering herd. utc_datetime = utc_datetime.replace(microsecond=500000) - timestamp = date_util.utc_to_timestamp(utc_datetime) + _async_fire_time_changed(hass, utc_datetime, fire_all) + +@ha.callback +def _async_fire_time_changed( + hass: HomeAssistant, utc_datetime: datetime | None, fire_all: bool +) -> None: + timestamp = date_util.utc_to_timestamp(utc_datetime) for task in list(hass.loop._scheduled): if not isinstance(task, asyncio.TimerHandle): continue diff --git a/tests/components/litejet/test_trigger.py b/tests/components/litejet/test_trigger.py index a06d490cea6..152d11b0c23 100644 --- a/tests/components/litejet/test_trigger.py +++ b/tests/components/litejet/test_trigger.py @@ -12,7 +12,7 @@ import homeassistant.util.dt as dt_util from . import async_init_integration -from tests.common import async_fire_time_changed, async_mock_service +from tests.common import async_fire_time_changed_exact, async_mock_service from tests.components.blueprint.conftest import stub_blueprint_populate # noqa: F401 _LOGGER = logging.getLogger(__name__) @@ -66,7 +66,7 @@ async def simulate_time(hass, mock_litejet, delta): return_value=mock_litejet.start_time + delta, ): _LOGGER.info("now=%s", dt_util.utcnow()) - async_fire_time_changed(hass, mock_litejet.start_time + delta) + async_fire_time_changed_exact(hass, mock_litejet.start_time + delta) await hass.async_block_till_done() _LOGGER.info("done with now=%s", dt_util.utcnow()) From 2ca61eef79318a540afa944cd9010c15d9e306e5 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Fri, 18 Nov 2022 16:27:20 +0100 Subject: [PATCH 0547/1033] Code quality tests Wake on Lan (#82048) --- tests/components/wake_on_lan/conftest.py | 13 +++++++ tests/components/wake_on_lan/test_init.py | 5 ++- tests/components/wake_on_lan/test_switch.py | 40 ++++++++++++--------- 3 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 tests/components/wake_on_lan/conftest.py diff --git a/tests/components/wake_on_lan/conftest.py b/tests/components/wake_on_lan/conftest.py new file mode 100644 index 00000000000..582698e39d5 --- /dev/null +++ b/tests/components/wake_on_lan/conftest.py @@ -0,0 +1,13 @@ +"""Test fixtures for Wake on Lan.""" +from __future__ import annotations + +from unittest.mock import AsyncMock, patch + +import pytest + + +@pytest.fixture +def mock_send_magic_packet() -> AsyncMock: + """Mock magic packet.""" + with patch("wakeonlan.send_magic_packet") as mock_send: + yield mock_send diff --git a/tests/components/wake_on_lan/test_init.py b/tests/components/wake_on_lan/test_init.py index 3ec7a53a436..1cfe2fa7436 100644 --- a/tests/components/wake_on_lan/test_init.py +++ b/tests/components/wake_on_lan/test_init.py @@ -1,14 +1,17 @@ """Tests for Wake On LAN component.""" +from __future__ import annotations + from unittest.mock import patch import pytest import voluptuous as vol from homeassistant.components.wake_on_lan import DOMAIN, SERVICE_SEND_MAGIC_PACKET +from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component -async def test_send_magic_packet(hass): +async def test_send_magic_packet(hass: HomeAssistant) -> None: """Test of send magic packet service call.""" with patch("homeassistant.components.wake_on_lan.wakeonlan") as mocked_wakeonlan: mac = "aa:bb:cc:dd:ee:ff" diff --git a/tests/components/wake_on_lan/test_switch.py b/tests/components/wake_on_lan/test_switch.py index cac287a87a1..b73f5223576 100644 --- a/tests/components/wake_on_lan/test_switch.py +++ b/tests/components/wake_on_lan/test_switch.py @@ -1,10 +1,10 @@ """The tests for the wake on lan switch platform.""" +from __future__ import annotations + import subprocess -from unittest.mock import patch +from unittest.mock import AsyncMock, patch -import pytest - -import homeassistant.components.switch as switch +from homeassistant.components import switch from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_TURN_OFF, @@ -12,19 +12,15 @@ from homeassistant.const import ( STATE_OFF, STATE_ON, ) +from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component from tests.common import async_mock_service -@pytest.fixture(autouse=True) -def mock_send_magic_packet(): - """Mock magic packet.""" - with patch("wakeonlan.send_magic_packet") as mock_send: - yield mock_send - - -async def test_valid_hostname(hass): +async def test_valid_hostname( + hass: HomeAssistant, mock_send_magic_packet: AsyncMock +) -> None: """Test with valid hostname.""" assert await async_setup_component( hass, @@ -65,7 +61,9 @@ async def test_valid_hostname(hass): assert state.state == STATE_ON -async def test_broadcast_config_ip_and_port(hass, mock_send_magic_packet): +async def test_broadcast_config_ip_and_port( + hass: HomeAssistant, mock_send_magic_packet: AsyncMock +) -> None: """Test with broadcast address and broadcast port config.""" mac = "00-01-02-03-04-05" broadcast_address = "255.255.255.255" @@ -102,7 +100,9 @@ async def test_broadcast_config_ip_and_port(hass, mock_send_magic_packet): ) -async def test_broadcast_config_ip(hass, mock_send_magic_packet): +async def test_broadcast_config_ip( + hass: HomeAssistant, mock_send_magic_packet: AsyncMock +) -> None: """Test with only broadcast address.""" mac = "00-01-02-03-04-05" @@ -136,7 +136,9 @@ async def test_broadcast_config_ip(hass, mock_send_magic_packet): mock_send_magic_packet.assert_called_with(mac, ip_address=broadcast_address) -async def test_broadcast_config_port(hass, mock_send_magic_packet): +async def test_broadcast_config_port( + hass: HomeAssistant, mock_send_magic_packet: AsyncMock +) -> None: """Test with only broadcast port config.""" mac = "00-01-02-03-04-05" @@ -164,7 +166,9 @@ async def test_broadcast_config_port(hass, mock_send_magic_packet): mock_send_magic_packet.assert_called_with(mac, port=port) -async def test_off_script(hass): +async def test_off_script( + hass: HomeAssistant, mock_send_magic_packet: AsyncMock +) -> None: """Test with turn off script.""" assert await async_setup_component( @@ -212,7 +216,9 @@ async def test_off_script(hass): assert len(calls) == 1 -async def test_no_hostname_state(hass): +async def test_no_hostname_state( + hass: HomeAssistant, mock_send_magic_packet: AsyncMock +) -> None: """Test that the state updates if we do not pass in a hostname.""" assert await async_setup_component( From c5c23cbc95d0a252f02ed803f9ca47fbd3b9c02a Mon Sep 17 00:00:00 2001 From: G Johansson Date: Fri, 18 Nov 2022 16:53:32 +0100 Subject: [PATCH 0548/1033] Add unique id to Times of Day (#81196) --- homeassistant/components/tod/binary_sensor.py | 5 +++- tests/components/tod/test_binary_sensor.py | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/tod/binary_sensor.py b/homeassistant/components/tod/binary_sensor.py index 1c909388b60..e3a40be16c0 100644 --- a/homeassistant/components/tod/binary_sensor.py +++ b/homeassistant/components/tod/binary_sensor.py @@ -13,6 +13,7 @@ from homeassistant.const import ( CONF_AFTER, CONF_BEFORE, CONF_NAME, + CONF_UNIQUE_ID, SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET, ) @@ -43,6 +44,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Required(CONF_NAME): cv.string, vol.Optional(CONF_AFTER_OFFSET, default=timedelta(0)): cv.time_period, vol.Optional(CONF_BEFORE_OFFSET, default=timedelta(0)): cv.time_period, + vol.Optional(CONF_UNIQUE_ID): cv.string, } ) @@ -85,7 +87,8 @@ async def async_setup_platform( before = config[CONF_BEFORE] before_offset = config[CONF_BEFORE_OFFSET] name = config[CONF_NAME] - sensor = TodSensor(name, after, after_offset, before, before_offset, None) + unique_id = config.get(CONF_UNIQUE_ID) + sensor = TodSensor(name, after, after_offset, before, before_offset, unique_id) async_add_entities([sensor]) diff --git a/tests/components/tod/test_binary_sensor.py b/tests/components/tod/test_binary_sensor.py index b0e5fc1f028..c0444b02eb8 100644 --- a/tests/components/tod/test_binary_sensor.py +++ b/tests/components/tod/test_binary_sensor.py @@ -5,6 +5,8 @@ from freezegun import freeze_time import pytest from homeassistant.const import STATE_OFF, STATE_ON +from homeassistant.core import HomeAssistant +import homeassistant.helpers.entity_registry as er from homeassistant.helpers.sun import get_astral_event_date, get_astral_event_next from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -786,3 +788,25 @@ async def test_simple_before_after_does_not_loop_berlin_in_range(hass): assert state.attributes["after"] == "2019-01-11T00:00:00+01:00" assert state.attributes["before"] == "2019-01-11T06:00:00+01:00" assert state.attributes["next_update"] == "2019-01-11T06:00:00+01:00" + + +async def test_unique_id(hass: HomeAssistant) -> None: + """Test unique id.""" + config = { + "binary_sensor": [ + { + "platform": "tod", + "name": "Evening", + "after": "18:00", + "before": "22:00", + "unique_id": "very_unique_id", + } + ] + } + await async_setup_component(hass, "binary_sensor", config) + await hass.async_block_till_done() + + entity_reg = er.async_get(hass) + entity = entity_reg.async_get("binary_sensor.evening") + + assert entity.unique_id == "very_unique_id" From c720742ec9efd97501faa84a8dada22d89db9a62 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 18 Nov 2022 17:16:03 +0100 Subject: [PATCH 0549/1033] Revert #81771 (wait_for_states) and #81801 (late review) (#82085) * Revert "Address late review of config entry wait for states tests (#81801)" This reverts commit 12d76a8a4f1cbc9403798a32dd335d928dd61373. * Revert "Implement ConfigEntry async_wait_for_states (#81771)" This reverts commit 3cc9ecf1dc0dc5fdace19770c076eb47d2f1c746. --- homeassistant/config_entries.py | 34 +---------- tests/test_config_entries.py | 99 --------------------------------- 2 files changed, 1 insertion(+), 132 deletions(-) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index eb6fcf855a3..5679eeabcc7 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -12,8 +12,6 @@ from types import MappingProxyType, MethodType from typing import TYPE_CHECKING, Any, Optional, TypeVar, cast import weakref -import async_timeout - from . import data_entry_flow, loader from .backports.enum import StrEnum from .components import persistent_notification @@ -22,7 +20,7 @@ from .core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback from .data_entry_flow import FlowResult from .exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady, HomeAssistantError from .helpers import device_registry, entity_registry, storage -from .helpers.dispatcher import async_dispatcher_connect, async_dispatcher_send +from .helpers.dispatcher import async_dispatcher_send from .helpers.event import async_call_later from .helpers.frame import report from .helpers.typing import UNDEFINED, ConfigType, DiscoveryInfoType, UndefinedType @@ -1249,36 +1247,6 @@ class ConfigEntries: await entry.async_setup(self.hass, integration=integration) return True - async def async_wait_for_states( - self, entry: ConfigEntry, states: set[ConfigEntryState], timeout: float = 60.0 - ) -> ConfigEntryState: - """Wait for the setup of an entry to reach one of the supplied states state. - - Returns the state the entry reached or raises asyncio.TimeoutError if the - entry did not reach one of the supplied states within the timeout. - """ - state_reached_future: asyncio.Future[ConfigEntryState] = asyncio.Future() - - @callback - def _async_entry_changed( - change: ConfigEntryChange, event_entry: ConfigEntry - ) -> None: - if ( - event_entry is entry - and change is ConfigEntryChange.UPDATED - and entry.state in states - ): - state_reached_future.set_result(entry.state) - - unsub = async_dispatcher_connect( - self.hass, SIGNAL_CONFIG_ENTRY_CHANGED, _async_entry_changed - ) - try: - async with async_timeout.timeout(timeout): - return await state_reached_future - finally: - unsub() - async def async_unload_platforms( self, entry: ConfigEntry, platforms: Iterable[Platform | str] ) -> bool: diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index bdcd5a37651..272b3d321b8 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -3332,105 +3332,6 @@ async def test_reauth(hass): assert len(hass.config_entries.flow.async_progress()) == 2 -async def test_wait_for_loading_entry(hass: HomeAssistant) -> None: - """Test waiting for entry to be set up.""" - - entry = MockConfigEntry(title="test_title", domain="test") - - mock_setup_entry = AsyncMock(return_value=True) - mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry)) - mock_entity_platform(hass, "config_flow.test", None) - - await entry.async_setup(hass) - await hass.async_block_till_done() - - flow = hass.config_entries.flow - - async def _load_entry() -> None: - assert await async_setup_component(hass, "test", {}) - - entry.add_to_hass(hass) - flow = hass.config_entries.flow - with patch.object(flow, "async_init", wraps=flow.async_init): - hass.async_create_task(_load_entry()) - new_state = await hass.config_entries.async_wait_for_states( - entry, - { - config_entries.ConfigEntryState.LOADED, - config_entries.ConfigEntryState.SETUP_ERROR, - }, - timeout=1.0, - ) - assert new_state is config_entries.ConfigEntryState.LOADED - assert entry.state is config_entries.ConfigEntryState.LOADED - - -async def test_wait_for_loading_failed_entry(hass: HomeAssistant) -> None: - """Test waiting for entry to be set up that fails loading.""" - - entry = MockConfigEntry(title="test_title", domain="test") - - mock_setup_entry = AsyncMock(side_effect=HomeAssistantError) - mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry)) - mock_entity_platform(hass, "config_flow.test", None) - - await hass.async_block_till_done() - - flow = hass.config_entries.flow - - async def _load_entry() -> None: - assert await async_setup_component(hass, "test", {}) - - entry.add_to_hass(hass) - flow = hass.config_entries.flow - with patch.object(flow, "async_init", wraps=flow.async_init): - hass.async_create_task(_load_entry()) - new_state = await hass.config_entries.async_wait_for_states( - entry, - { - config_entries.ConfigEntryState.LOADED, - config_entries.ConfigEntryState.SETUP_ERROR, - }, - timeout=1.0, - ) - assert new_state is config_entries.ConfigEntryState.SETUP_ERROR - assert entry.state is config_entries.ConfigEntryState.SETUP_ERROR - - -async def test_wait_for_loading_timeout(hass: HomeAssistant) -> None: - """Test waiting for entry to be set up that fails with a timeout.""" - - async def _async_setup_entry(hass, entry): - await asyncio.sleep(1) - return True - - entry = MockConfigEntry(title="test_title", domain="test") - - mock_integration(hass, MockModule("test", async_setup_entry=_async_setup_entry)) - mock_entity_platform(hass, "config_flow.test", None) - - await hass.async_block_till_done() - - flow = hass.config_entries.flow - - async def _load_entry() -> None: - assert await async_setup_component(hass, "test", {}) - - entry.add_to_hass(hass) - flow = hass.config_entries.flow - with patch.object(flow, "async_init", wraps=flow.async_init): - hass.async_create_task(_load_entry()) - with pytest.raises(asyncio.exceptions.TimeoutError): - await hass.config_entries.async_wait_for_states( - entry, - { - config_entries.ConfigEntryState.LOADED, - config_entries.ConfigEntryState.SETUP_ERROR, - }, - timeout=0.1, - ) - - async def test_get_active_flows(hass): """Test the async_get_active_flows helper.""" entry = MockConfigEntry(title="test_title", domain="test") From ca6376488d4431d05f5da10a55d695b9ab8654de Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 18 Nov 2022 17:35:24 +0100 Subject: [PATCH 0550/1033] Remove CI cache cleanup (#82330) --- .github/workflows/ci.yaml | 68 --------------------------------------- 1 file changed, 68 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index eadcc082732..262229e6ade 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -551,74 +551,6 @@ jobs: pip install --cache-dir=$PIP_CACHE -r requirements_test.txt --use-deprecated=legacy-resolver pip install -e . - cleanup-cache: - name: Cleanup old pip caches - runs-on: ubuntu-20.04 - needs: - - base - permissions: - actions: write - # Limit to 'dev' branch until permissions issue is resolved - # https://github.com/home-assistant/core/pull/80898 - # https://github.com/home-assistant/core/pull/82254 - if: github.ref == 'refs/heads/dev' - steps: - - name: Cleanup - run: | - sort="last_accessed_at" - page=1 - per_page=100 - del_count=0 - index=0 - - all_versions=($(echo $ALL_PYTHON_VERSIONS | tr -d \',[])) - - if [[ "${{ github.ref }}" == "refs/heads/dev" ]] \ - || [[ "${{ contains(github.event.pull_request.labels.*.name, 'ci-keep-cache') }}" == "true" ]] - then - index=1 - echo "Keep one entry per Python version" - fi - - while true; do - res=$(curl -fsS \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: token ${{ github.token }}" \ - "https://api.github.com/repos/${{ github.repository }}/actions/caches?sort=$sort&per_page=$per_page&page=$page") - - res_count=$(echo $res | jq '.actions_caches | length') - if [ $res_count -eq 0 ]; then break; fi - - echo "Check ref: ${{ github.ref }}" - for version in ${all_versions[@]}; do - key="${{ runner.os }}-$version.+-pip" - echo "Check key regex: $key" - - # Select all cache keys which match the ref and key regex - # Keep the first entry for 'ref/heads/dev' - targets=$(echo $res | jq \ - --arg ref "${{ github.ref }}" --arg key "$key" --arg index $index \ - '[.actions_caches[] | select(.ref == $ref) | select(.key | test($key))][$index | fromjson:][] | del(.created_at, .size_in_bytes, .version)') - - if [ $(echo $targets | jq -s 'length') -eq 0 ]; then continue; fi - echo "$targets" - - for id in $(echo $targets | jq '.id'); do - curl -fsS \ - -X DELETE \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: token ${{ github.token }}" \ - "https://api.github.com/repos/${{ github.repository }}/actions/caches/$id" - ((del_count += 1)) - done - done - - if [ $res_count -lt $per_page ]; then break; fi - ((page += 1)) - done - - echo "Deleted $del_count caches." - hassfest: name: Check hassfest runs-on: ubuntu-20.04 From f7badfe441a9e5bb3d02113b908e5c54d95e06bd Mon Sep 17 00:00:00 2001 From: Hessel Date: Fri, 18 Nov 2022 17:36:38 +0100 Subject: [PATCH 0551/1033] Add wallbox Energy Price (#82039) fixes undefined --- homeassistant/components/wallbox/__init__.py | 8 ++++++++ homeassistant/components/wallbox/const.py | 2 ++ homeassistant/components/wallbox/sensor.py | 19 +++++++++++++++++++ tests/components/wallbox/__init__.py | 4 ++++ 4 files changed, 33 insertions(+) diff --git a/homeassistant/components/wallbox/__init__.py b/homeassistant/components/wallbox/__init__.py index d927e7282ed..9a133b1794a 100644 --- a/homeassistant/components/wallbox/__init__.py +++ b/homeassistant/components/wallbox/__init__.py @@ -21,8 +21,10 @@ from homeassistant.helpers.update_coordinator import ( ) from .const import ( + CHARGER_CURRENCY_KEY, CHARGER_CURRENT_VERSION_KEY, CHARGER_DATA_KEY, + CHARGER_ENERGY_PRICE_KEY, CHARGER_LOCKED_UNLOCKED_KEY, CHARGER_MAX_CHARGING_CURRENT_KEY, CHARGER_NAME_KEY, @@ -124,6 +126,12 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]): data[CHARGER_LOCKED_UNLOCKED_KEY] = data[CHARGER_DATA_KEY][ CHARGER_LOCKED_UNLOCKED_KEY ] + data[CHARGER_ENERGY_PRICE_KEY] = data[CHARGER_DATA_KEY][ + CHARGER_ENERGY_PRICE_KEY + ] + data[CHARGER_CURRENCY_KEY] = data[CHARGER_DATA_KEY][CHARGER_CURRENCY_KEY][ + "code" + ] data[CHARGER_STATUS_DESCRIPTION_KEY] = CHARGER_STATUS.get( data[CHARGER_STATUS_ID_KEY], ChargerStatus.UNKNOWN ) diff --git a/homeassistant/components/wallbox/const.py b/homeassistant/components/wallbox/const.py index 9d1db637879..d7ee2dbb388 100644 --- a/homeassistant/components/wallbox/const.py +++ b/homeassistant/components/wallbox/const.py @@ -15,8 +15,10 @@ CHARGER_CHARGING_TIME_KEY = "charging_time" CHARGER_COST_KEY = "cost" CHARGER_CURRENT_MODE_KEY = "current_mode" CHARGER_CURRENT_VERSION_KEY = "currentVersion" +CHARGER_CURRENCY_KEY = "currency" CHARGER_DATA_KEY = "config_data" CHARGER_DEPOT_PRICE_KEY = "depot_price" +CHARGER_ENERGY_PRICE_KEY = "energy_price" CHARGER_SERIAL_NUMBER_KEY = "serial_number" CHARGER_PART_NUMBER_KEY = "part_number" CHARGER_SOFTWARE_KEY = "software" diff --git a/homeassistant/components/wallbox/sensor.py b/homeassistant/components/wallbox/sensor.py index 1fae68da2e4..f44f5ea6aaa 100644 --- a/homeassistant/components/wallbox/sensor.py +++ b/homeassistant/components/wallbox/sensor.py @@ -31,9 +31,11 @@ from .const import ( CHARGER_CHARGING_POWER_KEY, CHARGER_CHARGING_SPEED_KEY, CHARGER_COST_KEY, + CHARGER_CURRENCY_KEY, CHARGER_CURRENT_MODE_KEY, CHARGER_DATA_KEY, CHARGER_DEPOT_PRICE_KEY, + CHARGER_ENERGY_PRICE_KEY, CHARGER_MAX_AVAILABLE_POWER_KEY, CHARGER_MAX_CHARGING_CURRENT_KEY, CHARGER_SERIAL_NUMBER_KEY, @@ -127,6 +129,16 @@ SENSOR_TYPES: dict[str, WallboxSensorEntityDescription] = { icon="mdi:ev-station", name="Depot Price", precision=2, + device_class=SensorDeviceClass.MONETARY, + state_class=SensorStateClass.MEASUREMENT, + ), + CHARGER_ENERGY_PRICE_KEY: WallboxSensorEntityDescription( + key=CHARGER_ENERGY_PRICE_KEY, + icon="mdi:ev-station", + name="Energy Price", + precision=2, + device_class=SensorDeviceClass.MONETARY, + state_class=SensorStateClass.MEASUREMENT, ), CHARGER_STATUS_DESCRIPTION_KEY: WallboxSensorEntityDescription( key=CHARGER_STATUS_DESCRIPTION_KEY, @@ -188,3 +200,10 @@ class WallboxSensor(WallboxEntity, SensorEntity): round(self.coordinator.data[self.entity_description.key], sensor_round), ) return cast(StateType, self.coordinator.data[self.entity_description.key]) + + @property + def native_unit_of_measurement(self) -> str | None: + """Return the unit of measurement of the sensor. When monetary, get the value from the api.""" + if self.entity_description.device_class == SensorDeviceClass.MONETARY: + return cast(str, self.coordinator.data[CHARGER_CURRENCY_KEY]) + return cast(str, self.entity_description.native_unit_of_measurement) diff --git a/tests/components/wallbox/__init__.py b/tests/components/wallbox/__init__.py index 53af3b6383d..ef72f556a8c 100644 --- a/tests/components/wallbox/__init__.py +++ b/tests/components/wallbox/__init__.py @@ -10,8 +10,10 @@ from homeassistant.components.wallbox.const import ( CHARGER_ADDED_RANGE_KEY, CHARGER_CHARGING_POWER_KEY, CHARGER_CHARGING_SPEED_KEY, + CHARGER_CURRENCY_KEY, CHARGER_CURRENT_VERSION_KEY, CHARGER_DATA_KEY, + CHARGER_ENERGY_PRICE_KEY, CHARGER_LOCKED_UNLOCKED_KEY, CHARGER_MAX_AVAILABLE_POWER_KEY, CHARGER_MAX_CHARGING_CURRENT_KEY, @@ -42,10 +44,12 @@ test_response = json.loads( CHARGER_NAME_KEY: "WallboxName", CHARGER_DATA_KEY: { CHARGER_MAX_CHARGING_CURRENT_KEY: 24, + CHARGER_ENERGY_PRICE_KEY: 0.4, CHARGER_LOCKED_UNLOCKED_KEY: False, CHARGER_SERIAL_NUMBER_KEY: "20000", CHARGER_PART_NUMBER_KEY: "PLP1-0-2-4-9-002-E", CHARGER_SOFTWARE_KEY: {CHARGER_CURRENT_VERSION_KEY: "5.5.10"}, + CHARGER_CURRENCY_KEY: {"code": "€"}, }, } ) From 3b783a85c30001d7a8cf60c54932cb2b6616de4d Mon Sep 17 00:00:00 2001 From: Ayk Borstelmann Date: Fri, 18 Nov 2022 17:37:56 +0100 Subject: [PATCH 0552/1033] AVM Fritz!Box SmartHome: Integrate Templates (#81885) --- homeassistant/components/fritzbox/__init__.py | 32 +++++++---- .../components/fritzbox/binary_sensor.py | 10 ++-- homeassistant/components/fritzbox/button.py | 56 +++++++++++++++++++ homeassistant/components/fritzbox/climate.py | 50 ++++++++--------- homeassistant/components/fritzbox/const.py | 1 + .../components/fritzbox/coordinator.py | 30 ++++++++-- homeassistant/components/fritzbox/cover.py | 22 ++++---- .../components/fritzbox/diagnostics.py | 10 ++-- homeassistant/components/fritzbox/light.py | 34 +++++------ homeassistant/components/fritzbox/sensor.py | 8 +-- homeassistant/components/fritzbox/switch.py | 12 ++-- tests/components/fritzbox/__init__.py | 19 ++++--- tests/components/fritzbox/test_button.py | 43 ++++++++++++++ tests/components/fritzbox/test_init.py | 3 + 14 files changed, 235 insertions(+), 95 deletions(-) create mode 100644 homeassistant/components/fritzbox/button.py create mode 100644 tests/components/fritzbox/test_button.py diff --git a/homeassistant/components/fritzbox/__init__.py b/homeassistant/components/fritzbox/__init__.py index 4bb2eee45b1..3351372421b 100644 --- a/homeassistant/components/fritzbox/__init__.py +++ b/homeassistant/components/fritzbox/__init__.py @@ -1,7 +1,10 @@ """Support for AVM FRITZ!SmartHome devices.""" from __future__ import annotations +from abc import ABC, abstractmethod + from pyfritzhome import Fritzhome, FritzhomeDevice, LoginError +from pyfritzhome.devicetypes.fritzhomeentitybase import FritzhomeEntityBase from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.config_entries import ConfigEntry @@ -93,7 +96,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class FritzBoxEntity(CoordinatorEntity[FritzboxDataUpdateCoordinator]): +class FritzBoxEntity(CoordinatorEntity[FritzboxDataUpdateCoordinator], ABC): """Basis FritzBox entity.""" def __init__( @@ -108,30 +111,39 @@ class FritzBoxEntity(CoordinatorEntity[FritzboxDataUpdateCoordinator]): self.ain = ain if entity_description is not None: self.entity_description = entity_description - self._attr_name = f"{self.device.name} {entity_description.name}" + self._attr_name = f"{self.entity.name} {entity_description.name}" self._attr_unique_id = f"{ain}_{entity_description.key}" else: - self._attr_name = self.device.name + self._attr_name = self.entity.name self._attr_unique_id = ain + @property + @abstractmethod + def entity(self) -> FritzhomeEntityBase: + """Return entity object from coordinator.""" + + +class FritzBoxDeviceEntity(FritzBoxEntity): + """Reflects FritzhomeDevice and uses its attributes to construct FritzBoxDeviceEntity.""" + @property def available(self) -> bool: """Return if entity is available.""" - return super().available and self.device.present + return super().available and self.entity.present @property - def device(self) -> FritzhomeDevice: + def entity(self) -> FritzhomeDevice: """Return device object from coordinator.""" - return self.coordinator.data[self.ain] + return self.coordinator.data.devices[self.ain] @property def device_info(self) -> DeviceInfo: """Return device specific attributes.""" return DeviceInfo( - name=self.device.name, + name=self.entity.name, identifiers={(DOMAIN, self.ain)}, - manufacturer=self.device.manufacturer, - model=self.device.productname, - sw_version=self.device.fw_version, + manufacturer=self.entity.manufacturer, + model=self.entity.productname, + sw_version=self.entity.fw_version, configuration_url=self.coordinator.configuration_url, ) diff --git a/homeassistant/components/fritzbox/binary_sensor.py b/homeassistant/components/fritzbox/binary_sensor.py index b80d853e562..10ffd0b9104 100644 --- a/homeassistant/components/fritzbox/binary_sensor.py +++ b/homeassistant/components/fritzbox/binary_sensor.py @@ -17,7 +17,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import FritzBoxEntity +from . import FritzBoxDeviceEntity from .const import CONF_COORDINATOR, DOMAIN as FRITZBOX_DOMAIN from .coordinator import FritzboxDataUpdateCoordinator from .model import FritzEntityDescriptionMixinBase @@ -73,14 +73,14 @@ async def async_setup_entry( async_add_entities( [ FritzboxBinarySensor(coordinator, ain, description) - for ain, device in coordinator.data.items() + for ain, device in coordinator.data.devices.items() for description in BINARY_SENSOR_TYPES if description.suitable(device) ] ) -class FritzboxBinarySensor(FritzBoxEntity, BinarySensorEntity): +class FritzboxBinarySensor(FritzBoxDeviceEntity, BinarySensorEntity): """Representation of a binary FRITZ!SmartHome device.""" entity_description: FritzBinarySensorEntityDescription @@ -93,10 +93,10 @@ class FritzboxBinarySensor(FritzBoxEntity, BinarySensorEntity): ) -> None: """Initialize the FritzBox entity.""" super().__init__(coordinator, ain, entity_description) - self._attr_name = f"{self.device.name} {entity_description.name}" + self._attr_name = f"{self.entity.name} {entity_description.name}" self._attr_unique_id = f"{ain}_{entity_description.key}" @property def is_on(self) -> bool | None: """Return true if sensor is on.""" - return self.entity_description.is_on(self.device) + return self.entity_description.is_on(self.entity) diff --git a/homeassistant/components/fritzbox/button.py b/homeassistant/components/fritzbox/button.py new file mode 100644 index 00000000000..f8e5db9fcef --- /dev/null +++ b/homeassistant/components/fritzbox/button.py @@ -0,0 +1,56 @@ +"""Support for AVM FRITZ!SmartHome templates.""" +from pyfritzhome.devicetypes import FritzhomeTemplate + +from homeassistant.components.button import ButtonEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import FritzboxDataUpdateCoordinator, FritzBoxEntity +from .const import CONF_COORDINATOR, DOMAIN as FRITZBOX_DOMAIN + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up the FRITZ!SmartHome template from ConfigEntry.""" + coordinator: FritzboxDataUpdateCoordinator = hass.data[FRITZBOX_DOMAIN][ + entry.entry_id + ][CONF_COORDINATOR] + + async_add_entities( + [ + FritzBoxTemplate(coordinator, ain) + for ain in coordinator.data.templates.keys() + ] + ) + + +class FritzBoxTemplate(FritzBoxEntity, ButtonEntity): + """Interface between FritzhomeTemplate and hass.""" + + @property + def entity(self) -> FritzhomeTemplate: + """Return the template entity.""" + return self.coordinator.data.templates[self.ain] + + @property + def device_info(self) -> DeviceInfo: + """Return device specific attributes.""" + return DeviceInfo( + name=self.entity.name, + identifiers={(FRITZBOX_DOMAIN, self.ain)}, + configuration_url=self.coordinator.configuration_url, + manufacturer="AVM", + model="SmartHome Template", + ) + + async def async_press(self) -> None: + """Apply template and refresh.""" + await self.hass.async_add_executor_job(self.apply_template) + await self.coordinator.async_refresh() + + def apply_template(self) -> None: + """Use Fritzhome to apply the template via ain.""" + self.coordinator.fritz.apply_template(self.ain) diff --git a/homeassistant/components/fritzbox/climate.py b/homeassistant/components/fritzbox/climate.py index 806f8b2303e..364a96e8d2f 100644 --- a/homeassistant/components/fritzbox/climate.py +++ b/homeassistant/components/fritzbox/climate.py @@ -21,7 +21,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import FritzBoxEntity +from . import FritzBoxDeviceEntity from .const import ( ATTR_STATE_BATTERY_LOW, ATTR_STATE_HOLIDAY_MODE, @@ -55,13 +55,13 @@ async def async_setup_entry( async_add_entities( [ FritzboxThermostat(coordinator, ain) - for ain, device in coordinator.data.items() + for ain, device in coordinator.data.devices.items() if device.has_thermostat ] ) -class FritzboxThermostat(FritzBoxEntity, ClimateEntity): +class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity): """The thermostat class for FRITZ!SmartHome thermostats.""" _attr_precision = PRECISION_HALVES @@ -73,18 +73,18 @@ class FritzboxThermostat(FritzBoxEntity, ClimateEntity): @property def current_temperature(self) -> float: """Return the current temperature.""" - if self.device.has_temperature_sensor and self.device.temperature is not None: - return self.device.temperature # type: ignore [no-any-return] - return self.device.actual_temperature # type: ignore [no-any-return] + if self.entity.has_temperature_sensor and self.entity.temperature is not None: + return self.entity.temperature # type: ignore [no-any-return] + return self.entity.actual_temperature # type: ignore [no-any-return] @property def target_temperature(self) -> float: """Return the temperature we try to reach.""" - if self.device.target_temperature == ON_API_TEMPERATURE: + if self.entity.target_temperature == ON_API_TEMPERATURE: return ON_REPORT_SET_TEMPERATURE - if self.device.target_temperature == OFF_API_TEMPERATURE: + if self.entity.target_temperature == OFF_API_TEMPERATURE: return OFF_REPORT_SET_TEMPERATURE - return self.device.target_temperature # type: ignore [no-any-return] + return self.entity.target_temperature # type: ignore [no-any-return] async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature.""" @@ -94,14 +94,14 @@ class FritzboxThermostat(FritzBoxEntity, ClimateEntity): elif kwargs.get(ATTR_TEMPERATURE) is not None: temperature = kwargs[ATTR_TEMPERATURE] await self.hass.async_add_executor_job( - self.device.set_target_temperature, temperature + self.entity.set_target_temperature, temperature ) await self.coordinator.async_refresh() @property def hvac_mode(self) -> str: """Return the current operation mode.""" - if self.device.target_temperature in ( + if self.entity.target_temperature in ( OFF_REPORT_SET_TEMPERATURE, OFF_API_TEMPERATURE, ): @@ -120,15 +120,15 @@ class FritzboxThermostat(FritzBoxEntity, ClimateEntity): await self.async_set_temperature(temperature=OFF_REPORT_SET_TEMPERATURE) else: await self.async_set_temperature( - temperature=self.device.comfort_temperature + temperature=self.entity.comfort_temperature ) @property def preset_mode(self) -> str | None: """Return current preset mode.""" - if self.device.target_temperature == self.device.comfort_temperature: + if self.entity.target_temperature == self.entity.comfort_temperature: return PRESET_COMFORT - if self.device.target_temperature == self.device.eco_temperature: + if self.entity.target_temperature == self.entity.eco_temperature: return PRESET_ECO return None @@ -141,10 +141,10 @@ class FritzboxThermostat(FritzBoxEntity, ClimateEntity): """Set preset mode.""" if preset_mode == PRESET_COMFORT: await self.async_set_temperature( - temperature=self.device.comfort_temperature + temperature=self.entity.comfort_temperature ) elif preset_mode == PRESET_ECO: - await self.async_set_temperature(temperature=self.device.eco_temperature) + await self.async_set_temperature(temperature=self.entity.eco_temperature) @property def min_temp(self) -> int: @@ -160,17 +160,17 @@ class FritzboxThermostat(FritzBoxEntity, ClimateEntity): def extra_state_attributes(self) -> ClimateExtraAttributes: """Return the device specific state attributes.""" attrs: ClimateExtraAttributes = { - ATTR_STATE_BATTERY_LOW: self.device.battery_low, + ATTR_STATE_BATTERY_LOW: self.entity.battery_low, } # the following attributes are available since fritzos 7 - if self.device.battery_level is not None: - attrs[ATTR_BATTERY_LEVEL] = self.device.battery_level - if self.device.holiday_active is not None: - attrs[ATTR_STATE_HOLIDAY_MODE] = self.device.holiday_active - if self.device.summer_active is not None: - attrs[ATTR_STATE_SUMMER_MODE] = self.device.summer_active - if self.device.window_open is not None: - attrs[ATTR_STATE_WINDOW_OPEN] = self.device.window_open + if self.entity.battery_level is not None: + attrs[ATTR_BATTERY_LEVEL] = self.entity.battery_level + if self.entity.holiday_active is not None: + attrs[ATTR_STATE_HOLIDAY_MODE] = self.entity.holiday_active + if self.entity.summer_active is not None: + attrs[ATTR_STATE_SUMMER_MODE] = self.entity.summer_active + if self.entity.window_open is not None: + attrs[ATTR_STATE_WINDOW_OPEN] = self.entity.window_open return attrs diff --git a/homeassistant/components/fritzbox/const.py b/homeassistant/components/fritzbox/const.py index 4831b0f6ab2..791da4540a4 100644 --- a/homeassistant/components/fritzbox/const.py +++ b/homeassistant/components/fritzbox/const.py @@ -26,6 +26,7 @@ LOGGER: Final[logging.Logger] = logging.getLogger(__package__) PLATFORMS: Final[list[Platform]] = [ Platform.BINARY_SENSOR, + Platform.BUTTON, Platform.CLIMATE, Platform.COVER, Platform.LIGHT, diff --git a/homeassistant/components/fritzbox/coordinator.py b/homeassistant/components/fritzbox/coordinator.py index 47d2cdca005..c16751c8c9f 100644 --- a/homeassistant/components/fritzbox/coordinator.py +++ b/homeassistant/components/fritzbox/coordinator.py @@ -1,9 +1,11 @@ """Data update coordinator for AVM FRITZ!SmartHome devices.""" from __future__ import annotations +from dataclasses import dataclass from datetime import timedelta from pyfritzhome import Fritzhome, FritzhomeDevice, LoginError +from pyfritzhome.devicetypes import FritzhomeTemplate import requests from homeassistant.config_entries import ConfigEntry @@ -14,7 +16,15 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from .const import CONF_CONNECTIONS, DOMAIN, LOGGER -class FritzboxDataUpdateCoordinator(DataUpdateCoordinator): +@dataclass +class FritzboxCoordinatorData: + """Data Type of FritzboxDataUpdateCoordinator's data.""" + + devices: dict[str, FritzhomeDevice] + templates: dict[str, FritzhomeTemplate] + + +class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorData]): """Fritzbox Smarthome device data update coordinator.""" configuration_url: str @@ -31,10 +41,11 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator): update_interval=timedelta(seconds=30), ) - def _update_fritz_devices(self) -> dict[str, FritzhomeDevice]: + def _update_fritz_devices(self) -> FritzboxCoordinatorData: """Update all fritzbox device data.""" try: self.fritz.update_devices() + self.fritz.update_templates() except requests.exceptions.ConnectionError as ex: raise UpdateFailed from ex except requests.exceptions.HTTPError: @@ -44,9 +55,10 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator): except LoginError as ex: raise ConfigEntryAuthFailed from ex self.fritz.update_devices() + self.fritz.update_templates() devices = self.fritz.get_devices() - data = {} + device_data = {} for device in devices: # assume device as unavailable, see #55799 if ( @@ -61,9 +73,15 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator): LOGGER.debug("Assume device %s as unavailable", device.name) device.present = False - data[device.ain] = device - return data + device_data[device.ain] = device - async def _async_update_data(self) -> dict[str, FritzhomeDevice]: + templates = self.fritz.get_templates() + template_data = {} + for template in templates: + template_data[template.ain] = template + + return FritzboxCoordinatorData(devices=device_data, templates=template_data) + + async def _async_update_data(self) -> FritzboxCoordinatorData: """Fetch all device data.""" return await self.hass.async_add_executor_job(self._update_fritz_devices) diff --git a/homeassistant/components/fritzbox/cover.py b/homeassistant/components/fritzbox/cover.py index d8fc8d4f3c3..78389fe3c99 100644 --- a/homeassistant/components/fritzbox/cover.py +++ b/homeassistant/components/fritzbox/cover.py @@ -13,7 +13,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import FritzBoxEntity +from . import FritzBoxDeviceEntity from .const import CONF_COORDINATOR, DOMAIN as FRITZBOX_DOMAIN @@ -25,12 +25,12 @@ async def async_setup_entry( async_add_entities( FritzboxCover(coordinator, ain) - for ain, device in coordinator.data.items() + for ain, device in coordinator.data.devices.items() if device.has_blind ) -class FritzboxCover(FritzBoxEntity, CoverEntity): +class FritzboxCover(FritzBoxDeviceEntity, CoverEntity): """The cover class for FRITZ!SmartHome covers.""" _attr_device_class = CoverDeviceClass.BLIND @@ -45,34 +45,34 @@ class FritzboxCover(FritzBoxEntity, CoverEntity): def current_cover_position(self) -> int | None: """Return the current position.""" position = None - if self.device.levelpercentage is not None: - position = 100 - self.device.levelpercentage + if self.entity.levelpercentage is not None: + position = 100 - self.entity.levelpercentage return position @property def is_closed(self) -> bool | None: """Return if the cover is closed.""" - if self.device.levelpercentage is None: + if self.entity.levelpercentage is None: return None - return self.device.levelpercentage == 100 # type: ignore [no-any-return] + return self.entity.levelpercentage == 100 # type: ignore [no-any-return] async def async_open_cover(self, **kwargs: Any) -> None: """Open the cover.""" - await self.hass.async_add_executor_job(self.device.set_blind_open) + await self.hass.async_add_executor_job(self.entity.set_blind_open) await self.coordinator.async_refresh() async def async_close_cover(self, **kwargs: Any) -> None: """Close the cover.""" - await self.hass.async_add_executor_job(self.device.set_blind_close) + await self.hass.async_add_executor_job(self.entity.set_blind_close) await self.coordinator.async_refresh() async def async_set_cover_position(self, **kwargs: Any) -> None: """Move the cover to a specific position.""" await self.hass.async_add_executor_job( - self.device.set_level_percentage, 100 - kwargs[ATTR_POSITION] + self.entity.set_level_percentage, 100 - kwargs[ATTR_POSITION] ) async def async_stop_cover(self, **kwargs: Any) -> None: """Stop the cover.""" - await self.hass.async_add_executor_job(self.device.set_blind_stop) + await self.hass.async_add_executor_job(self.entity.set_blind_stop) await self.coordinator.async_refresh() diff --git a/homeassistant/components/fritzbox/diagnostics.py b/homeassistant/components/fritzbox/diagnostics.py index 581fe4c6f01..403082c4f90 100644 --- a/homeassistant/components/fritzbox/diagnostics.py +++ b/homeassistant/components/fritzbox/diagnostics.py @@ -23,11 +23,13 @@ async def async_get_config_entry_diagnostics( "entry": async_redact_data(entry.as_dict(), TO_REDACT), "data": {}, } - if not isinstance(coordinator.data, dict): - return diag_data + entities: dict[str, dict] = { + **coordinator.data.devices, + **coordinator.data.templates, + } diag_data["data"] = { - ain: {k: v for k, v in vars(dev).items() if not k.startswith("_")} - for ain, dev in coordinator.data.items() + ain: {k: v for k, v in vars(entity).items() if not k.startswith("_")} + for ain, entity in entities.items() } return diag_data diff --git a/homeassistant/components/fritzbox/light.py b/homeassistant/components/fritzbox/light.py index 0f7037843d8..a63d3e06008 100644 --- a/homeassistant/components/fritzbox/light.py +++ b/homeassistant/components/fritzbox/light.py @@ -16,7 +16,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import FritzBoxEntity +from . import FritzBoxDeviceEntity from .const import ( COLOR_MODE, COLOR_TEMP_MODE, @@ -36,7 +36,7 @@ async def async_setup_entry( entities: list[FritzboxLight] = [] coordinator = hass.data[FRITZBOX_DOMAIN][entry.entry_id][CONF_COORDINATOR] - for ain, device in coordinator.data.items(): + for ain, device in coordinator.data.devices.items(): if not device.has_lightbulb: continue @@ -58,7 +58,7 @@ async def async_setup_entry( async_add_entities(entities) -class FritzboxLight(FritzBoxEntity, LightEntity): +class FritzboxLight(FritzBoxDeviceEntity, LightEntity): """The light class for FRITZ!SmartHome lightbulbs.""" def __init__( @@ -88,36 +88,36 @@ class FritzboxLight(FritzBoxEntity, LightEntity): @property def is_on(self) -> bool: """If the light is currently on or off.""" - return self.device.state # type: ignore [no-any-return] + return self.entity.state # type: ignore [no-any-return] @property def brightness(self) -> int: """Return the current Brightness.""" - return self.device.level # type: ignore [no-any-return] + return self.entity.level # type: ignore [no-any-return] @property def hs_color(self) -> tuple[float, float] | None: """Return the hs color value.""" - if self.device.color_mode != COLOR_MODE: + if self.entity.color_mode != COLOR_MODE: return None - hue = self.device.hue - saturation = self.device.saturation + hue = self.entity.hue + saturation = self.entity.saturation return (hue, float(saturation) * 100.0 / 255.0) @property def color_temp_kelvin(self) -> int | None: """Return the CT color value.""" - if self.device.color_mode != COLOR_TEMP_MODE: + if self.entity.color_mode != COLOR_TEMP_MODE: return None - return self.device.color_temp # type: ignore [no-any-return] + return self.entity.color_temp # type: ignore [no-any-return] @property def color_mode(self) -> ColorMode: """Return the color mode of the light.""" - if self.device.color_mode == COLOR_MODE: + if self.entity.color_mode == COLOR_MODE: return ColorMode.HS return ColorMode.COLOR_TEMP @@ -130,7 +130,7 @@ class FritzboxLight(FritzBoxEntity, LightEntity): """Turn the light on.""" if kwargs.get(ATTR_BRIGHTNESS) is not None: level = kwargs[ATTR_BRIGHTNESS] - await self.hass.async_add_executor_job(self.device.set_level, level) + await self.hass.async_add_executor_job(self.entity.set_level, level) if kwargs.get(ATTR_HS_COLOR) is not None: # Try setunmappedcolor first. This allows free color selection, # but we don't know if its supported by all devices. @@ -139,7 +139,7 @@ class FritzboxLight(FritzBoxEntity, LightEntity): unmapped_hue = int(kwargs[ATTR_HS_COLOR][0] % 360) unmapped_saturation = round(kwargs[ATTR_HS_COLOR][1] * 255.0 / 100.0) await self.hass.async_add_executor_job( - self.device.set_unmapped_color, (unmapped_hue, unmapped_saturation) + self.entity.set_unmapped_color, (unmapped_hue, unmapped_saturation) ) # This will raise 400 BAD REQUEST if the setunmappedcolor is not available except HTTPError as err: @@ -157,18 +157,18 @@ class FritzboxLight(FritzBoxEntity, LightEntity): key=lambda x: abs(x - unmapped_saturation), ) await self.hass.async_add_executor_job( - self.device.set_color, (hue, saturation) + self.entity.set_color, (hue, saturation) ) if kwargs.get(ATTR_COLOR_TEMP_KELVIN) is not None: await self.hass.async_add_executor_job( - self.device.set_color_temp, kwargs[ATTR_COLOR_TEMP_KELVIN] + self.entity.set_color_temp, kwargs[ATTR_COLOR_TEMP_KELVIN] ) - await self.hass.async_add_executor_job(self.device.set_state_on) + await self.hass.async_add_executor_job(self.entity.set_state_on) await self.coordinator.async_refresh() async def async_turn_off(self, **kwargs: Any) -> None: """Turn the light off.""" - await self.hass.async_add_executor_job(self.device.set_state_off) + await self.hass.async_add_executor_job(self.entity.set_state_off) await self.coordinator.async_refresh() diff --git a/homeassistant/components/fritzbox/sensor.py b/homeassistant/components/fritzbox/sensor.py index ab341fb1520..9a86ffae5ed 100644 --- a/homeassistant/components/fritzbox/sensor.py +++ b/homeassistant/components/fritzbox/sensor.py @@ -30,7 +30,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from homeassistant.util.dt import utc_from_timestamp -from . import FritzBoxEntity +from . import FritzBoxDeviceEntity from .const import CONF_COORDINATOR, DOMAIN as FRITZBOX_DOMAIN from .model import FritzEntityDescriptionMixinBase @@ -220,14 +220,14 @@ async def async_setup_entry( async_add_entities( [ FritzBoxSensor(coordinator, ain, description) - for ain, device in coordinator.data.items() + for ain, device in coordinator.data.devices.items() for description in SENSOR_TYPES if description.suitable(device) ] ) -class FritzBoxSensor(FritzBoxEntity, SensorEntity): +class FritzBoxSensor(FritzBoxDeviceEntity, SensorEntity): """The entity class for FRITZ!SmartHome sensors.""" entity_description: FritzSensorEntityDescription @@ -235,4 +235,4 @@ class FritzBoxSensor(FritzBoxEntity, SensorEntity): @property def native_value(self) -> StateType | datetime: """Return the state of the sensor.""" - return self.entity_description.native_value(self.device) + return self.entity_description.native_value(self.entity) diff --git a/homeassistant/components/fritzbox/switch.py b/homeassistant/components/fritzbox/switch.py index 79f256bded0..029627e191c 100644 --- a/homeassistant/components/fritzbox/switch.py +++ b/homeassistant/components/fritzbox/switch.py @@ -8,7 +8,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import FritzBoxEntity +from . import FritzBoxDeviceEntity from .const import CONF_COORDINATOR, DOMAIN as FRITZBOX_DOMAIN @@ -21,26 +21,26 @@ async def async_setup_entry( async_add_entities( [ FritzboxSwitch(coordinator, ain) - for ain, device in coordinator.data.items() + for ain, device in coordinator.data.devices.items() if device.has_switch ] ) -class FritzboxSwitch(FritzBoxEntity, SwitchEntity): +class FritzboxSwitch(FritzBoxDeviceEntity, SwitchEntity): """The switch class for FRITZ!SmartHome switches.""" @property def is_on(self) -> bool: """Return true if the switch is on.""" - return self.device.switch_state # type: ignore [no-any-return] + return self.entity.switch_state # type: ignore [no-any-return] async def async_turn_on(self, **kwargs: Any) -> None: """Turn the switch on.""" - await self.hass.async_add_executor_job(self.device.set_switch_state_on) + await self.hass.async_add_executor_job(self.entity.set_switch_state_on) await self.coordinator.async_refresh() async def async_turn_off(self, **kwargs: Any) -> None: """Turn the switch off.""" - await self.hass.async_add_executor_job(self.device.set_switch_state_off) + await self.hass.async_add_executor_job(self.entity.set_switch_state_off) await self.coordinator.async_refresh() diff --git a/tests/components/fritzbox/__init__.py b/tests/components/fritzbox/__init__.py index acafd19c924..fe7d11068fd 100644 --- a/tests/components/fritzbox/__init__.py +++ b/tests/components/fritzbox/__init__.py @@ -24,6 +24,7 @@ async def setup_config_entry( unique_id: str = "any", device: Mock = None, fritz: Mock = None, + template: Mock = None, ) -> bool: """Do setup of a MockConfigEntry.""" entry = MockConfigEntry( @@ -34,13 +35,17 @@ async def setup_config_entry( entry.add_to_hass(hass) if device is not None and fritz is not None: fritz().get_devices.return_value = [device] + + if template is not None and fritz is not None: + fritz().get_templates.return_value = [template] + result = await hass.config_entries.async_setup(entry.entry_id) if device is not None: await hass.async_block_till_done() return result -class FritzDeviceBaseMock(Mock): +class FritzEntityBaseMock(Mock): """base mock of a AVM Fritz!Box binary sensor device.""" ain = CONF_FAKE_AIN @@ -49,7 +54,7 @@ class FritzDeviceBaseMock(Mock): productname = CONF_FAKE_PRODUCTNAME -class FritzDeviceBinarySensorMock(FritzDeviceBaseMock): +class FritzDeviceBinarySensorMock(FritzEntityBaseMock): """Mock of a AVM Fritz!Box binary sensor device.""" alert_state = "fake_state" @@ -65,7 +70,7 @@ class FritzDeviceBinarySensorMock(FritzDeviceBaseMock): present = True -class FritzDeviceClimateMock(FritzDeviceBaseMock): +class FritzDeviceClimateMock(FritzEntityBaseMock): """Mock of a AVM Fritz!Box climate device.""" actual_temperature = 18.0 @@ -96,7 +101,7 @@ class FritzDeviceClimateMock(FritzDeviceBaseMock): scheduled_preset = PRESET_ECO -class FritzDeviceSensorMock(FritzDeviceBaseMock): +class FritzDeviceSensorMock(FritzEntityBaseMock): """Mock of a AVM Fritz!Box sensor device.""" battery_level = 23 @@ -115,7 +120,7 @@ class FritzDeviceSensorMock(FritzDeviceBaseMock): rel_humidity = 42 -class FritzDeviceSwitchMock(FritzDeviceBaseMock): +class FritzDeviceSwitchMock(FritzEntityBaseMock): """Mock of a AVM Fritz!Box switch device.""" battery_level = None @@ -137,7 +142,7 @@ class FritzDeviceSwitchMock(FritzDeviceBaseMock): temperature = 1.23 -class FritzDeviceLightMock(FritzDeviceBaseMock): +class FritzDeviceLightMock(FritzEntityBaseMock): """Mock of a AVM Fritz!Box light device.""" fw_version = "1.2.3" @@ -153,7 +158,7 @@ class FritzDeviceLightMock(FritzDeviceBaseMock): state = True -class FritzDeviceCoverMock(FritzDeviceBaseMock): +class FritzDeviceCoverMock(FritzEntityBaseMock): """Mock of a AVM Fritz!Box cover device.""" fw_version = "1.2.3" diff --git a/tests/components/fritzbox/test_button.py b/tests/components/fritzbox/test_button.py new file mode 100644 index 00000000000..b362e7dcfb1 --- /dev/null +++ b/tests/components/fritzbox/test_button.py @@ -0,0 +1,43 @@ +"""Tests for AVM Fritz!Box templates.""" +from unittest.mock import Mock + +from homeassistant.components.button import DOMAIN, SERVICE_PRESS +from homeassistant.components.fritzbox.const import DOMAIN as FB_DOMAIN +from homeassistant.const import ( + ATTR_ENTITY_ID, + ATTR_FRIENDLY_NAME, + CONF_DEVICES, + STATE_UNKNOWN, +) +from homeassistant.core import HomeAssistant + +from . import FritzEntityBaseMock, setup_config_entry +from .const import CONF_FAKE_NAME, MOCK_CONFIG + +ENTITY_ID = f"{DOMAIN}.{CONF_FAKE_NAME}" + + +async def test_setup(hass: HomeAssistant, fritz: Mock): + """Test if is initialized correctly.""" + template = FritzEntityBaseMock() + assert await setup_config_entry( + hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], fritz=fritz, template=template + ) + + state = hass.states.get(ENTITY_ID) + assert state + assert state.attributes[ATTR_FRIENDLY_NAME] == CONF_FAKE_NAME + assert state.state == STATE_UNKNOWN + + +async def test_apply_template(hass: HomeAssistant, fritz: Mock): + """Test if applies works.""" + template = FritzEntityBaseMock() + assert await setup_config_entry( + hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], fritz=fritz, template=template + ) + + assert await hass.services.async_call( + DOMAIN, SERVICE_PRESS, {ATTR_ENTITY_ID: ENTITY_ID}, True + ) + assert fritz().apply_template.call_count == 1 diff --git a/tests/components/fritzbox/test_init.py b/tests/components/fritzbox/test_init.py index fdf787d4cf2..d68d9e1679c 100644 --- a/tests/components/fritzbox/test_init.py +++ b/tests/components/fritzbox/test_init.py @@ -167,7 +167,9 @@ async def test_coordinator_update_after_reboot(hass: HomeAssistant, fritz: Mock) assert await hass.config_entries.async_setup(entry.entry_id) assert fritz().update_devices.call_count == 2 + assert fritz().update_templates.call_count == 1 assert fritz().get_devices.call_count == 1 + assert fritz().get_templates.call_count == 1 assert fritz().login.call_count == 2 @@ -187,6 +189,7 @@ async def test_coordinator_update_after_password_change( assert not await hass.config_entries.async_setup(entry.entry_id) assert fritz().update_devices.call_count == 1 assert fritz().get_devices.call_count == 0 + assert fritz().get_templates.call_count == 0 assert fritz().login.call_count == 2 From fff967ce17e7b8360d136537367eb530928345ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Fri, 18 Nov 2022 18:01:41 +0100 Subject: [PATCH 0553/1033] Update aioairzone to v0.5.1 (#82311) Co-authored-by: J. Nick Koston --- homeassistant/components/airzone/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/airzone/manifest.json b/homeassistant/components/airzone/manifest.json index b3885fae278..25b853db290 100644 --- a/homeassistant/components/airzone/manifest.json +++ b/homeassistant/components/airzone/manifest.json @@ -3,7 +3,7 @@ "name": "Airzone", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/airzone", - "requirements": ["aioairzone==0.5.0"], + "requirements": ["aioairzone==0.5.1"], "codeowners": ["@Noltari"], "iot_class": "local_polling", "loggers": ["aioairzone"] diff --git a/requirements_all.txt b/requirements_all.txt index 5c2d17f9022..f84582832ac 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -116,7 +116,7 @@ aio_georss_gdacs==0.7 aioairq==0.2.4 # homeassistant.components.airzone -aioairzone==0.5.0 +aioairzone==0.5.1 # homeassistant.components.ambient_station aioambient==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ae7acf7cce1..957a3f10450 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -103,7 +103,7 @@ aio_georss_gdacs==0.7 aioairq==0.2.4 # homeassistant.components.airzone -aioairzone==0.5.0 +aioairzone==0.5.1 # homeassistant.components.ambient_station aioambient==2021.11.0 From fe5246fb6fdf33222680623aac811ad83dd1ea26 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 19 Nov 2022 00:26:11 +0000 Subject: [PATCH 0554/1033] [ci skip] Translation update --- .../components/abode/translations/sk.json | 18 +++++++-- .../accuweather/translations/sensor.sk.json | 8 ++++ .../accuweather/translations/sk.json | 13 ++++++ .../components/acmeda/translations/sk.json | 11 +++++ .../components/adguard/translations/sk.json | 13 +++++- .../advantage_air/translations/sk.json | 10 ++++- .../components/aemet/translations/sk.json | 3 ++ .../components/agent_dvr/translations/sk.json | 7 +++- .../components/airly/translations/sk.json | 3 ++ .../components/airq/translations/el.json | 10 +++++ .../components/airq/translations/sk.json | 7 ++++ .../components/airthings/translations/sk.json | 11 ++++- .../airthings_ble/translations/sk.json | 11 +++++ .../components/airtouch4/translations/sk.json | 17 ++++++++ .../airvisual/translations/sensor.sk.json | 12 +++++- .../components/airvisual/translations/sk.json | 15 ++++++- .../components/airzone/translations/sk.json | 18 +++++++++ .../aladdin_connect/translations/sk.json | 15 +++++++ .../alarm_control_panel/translations/is.json | 3 +- .../alarmdecoder/translations/sk.json | 22 ++++++++++ .../components/almond/translations/sk.json | 14 +++++++ .../amberelectric/translations/sk.json | 8 ++++ .../ambiclimate/translations/sk.json | 4 ++ .../ambient_station/translations/sk.json | 9 ++++- .../android_ip_webcam/translations/sk.json | 19 +++++++++ .../components/androidtv/translations/sk.json | 4 ++ .../components/anthemav/translations/sk.json | 11 +++++ .../components/apcupsd/translations/sk.json | 11 +++++ .../components/aranet/translations/el.json | 9 +++++ .../components/arcam_fmj/translations/sk.json | 2 + .../components/asuswrt/translations/sk.json | 4 ++ .../components/atag/translations/sk.json | 1 + .../components/awair/translations/sk.json | 10 +++++ .../components/axis/translations/sk.json | 2 + .../components/balboa/translations/sk.json | 11 +++++ .../binary_sensor/translations/is.json | 35 ++++++++++++++++ .../components/blebox/translations/sk.json | 2 + .../components/bond/translations/sk.json | 4 +- .../components/bosch_shc/translations/sk.json | 7 ++++ .../components/braviatv/translations/el.json | 3 ++ .../components/braviatv/translations/sk.json | 14 +++++++ .../components/broadlink/translations/sk.json | 12 +++++- .../components/brother/translations/sk.json | 14 +++++++ .../components/bsblan/translations/sk.json | 1 + .../components/cast/translations/sk.json | 14 +++++++ .../cert_expiry/translations/sk.json | 1 + .../components/coinbase/translations/el.json | 6 +++ .../components/control4/translations/sk.json | 7 ++++ .../coolmaster/translations/sk.json | 11 +++++ .../components/cover/translations/is.json | 5 ++- .../components/daikin/translations/sk.json | 3 +- .../components/deconz/translations/sk.json | 1 + .../components/deluge/translations/sk.json | 1 + .../components/denonavr/translations/sk.json | 12 ++++++ .../device_tracker/translations/is.json | 6 +++ .../components/directv/translations/sk.json | 11 +++++ .../components/dlna_dmr/translations/sk.json | 11 +++++ .../components/dlna_dms/translations/sk.json | 11 +++++ .../components/dnsip/translations/sk.json | 7 ++++ .../components/doorbird/translations/sk.json | 8 ++++ .../components/dsmr/translations/sk.json | 1 + .../components/dunehd/translations/sk.json | 14 +++++++ .../components/elgato/translations/sk.json | 1 + .../components/elkm1/translations/sk.json | 3 +- .../components/emonitor/translations/sk.json | 11 +++++ .../emulated_roku/translations/sk.json | 1 + .../enphase_envoy/translations/sk.json | 8 ++++ .../components/epson/translations/sk.json | 1 + .../components/esphome/translations/sk.json | 6 +++ .../esphome/translations/zh-Hant.json | 6 +++ .../evil_genius_labs/translations/sk.json | 11 +++++ .../components/ezviz/translations/sk.json | 3 +- .../components/fan/translations/is.json | 6 +++ .../fireservicerota/translations/el.json | 3 ++ .../components/fivem/translations/sk.json | 1 + .../components/flo/translations/sk.json | 7 ++++ .../components/flux_led/translations/sk.json | 7 ++++ .../forked_daapd/translations/sk.json | 12 ++++++ .../components/foscam/translations/sk.json | 1 + .../components/freebox/translations/sk.json | 1 + .../components/fritz/translations/sk.json | 1 + .../components/fritzbox/translations/sk.json | 7 ++++ .../fritzbox_callmonitor/translations/sk.json | 1 + .../components/fronius/translations/sk.json | 14 +++++++ .../fully_kiosk/translations/sk.json | 11 +++++ .../components/glances/translations/sk.json | 1 + .../components/goalzero/translations/sk.json | 7 ++++ .../components/goodwe/translations/sk.json | 7 ++++ .../google_travel_time/translations/el.json | 1 + .../components/harmony/translations/sk.json | 11 +++++ .../components/hassio/translations/el.json | 35 ++++++++++++++++ .../components/heos/translations/sk.json | 11 +++++ .../components/hlk_sw16/translations/sk.json | 7 ++++ .../homeassistant/translations/el.json | 4 ++ .../homeassistant/translations/zh-Hant.json | 4 ++ .../translations/zh-Hant.json | 40 +++++++++++++++++++ .../components/hue/translations/sk.json | 8 ++++ .../humidifier/translations/is.json | 8 ++++ .../translations/sk.json | 12 ++++++ .../hvv_departures/translations/sk.json | 7 ++++ .../components/hyperion/translations/sk.json | 1 + .../components/ialarm/translations/sk.json | 1 + .../components/insteon/translations/sk.json | 3 ++ .../intellifire/translations/sk.json | 12 ++++++ .../components/iotawatt/translations/sk.json | 7 ++++ .../components/ipp/translations/sk.json | 1 + .../components/isy994/translations/sk.json | 8 ++++ .../kaleidescape/translations/sk.json | 11 +++++ .../keenetic_ndms2/translations/sk.json | 2 + .../components/kmtronic/translations/sk.json | 7 ++++ .../components/knx/translations/el.json | 30 ++++++++++++-- .../components/knx/translations/sk.json | 7 ++++ .../components/kodi/translations/sk.json | 1 + .../components/konnected/translations/sk.json | 1 + .../kostal_plenticore/translations/sk.json | 7 ++++ .../components/lametric/translations/el.json | 1 + .../lametric/translations/select.el.json | 8 ++++ .../lg_soundbar/translations/sk.json | 11 +++++ .../components/life360/translations/bg.json | 2 +- .../components/light/translations/is.json | 6 +++ .../components/livisi/translations/el.json | 13 ++++++ .../components/livisi/translations/sk.json | 11 +++++ .../components/lock/translations/is.json | 6 +++ .../lutron_caseta/translations/el.json | 3 ++ .../lutron_caseta/translations/sk.json | 11 +++++ .../media_player/translations/is.json | 10 +++++ .../components/mikrotik/translations/sk.json | 1 + .../minecraft_server/translations/sk.json | 1 + .../modern_forms/translations/sk.json | 11 +++++ .../moehlenhoff_alpha2/translations/sk.json | 11 +++++ .../motion_blinds/translations/sk.json | 5 +++ .../components/mqtt/translations/el.json | 8 ++++ .../components/mutesync/translations/sk.json | 11 +++++ .../components/nam/translations/sk.json | 8 ++++ .../components/nanoleaf/translations/sk.json | 7 ++++ .../components/netgear/translations/sk.json | 12 ++++++ .../nfandroidtv/translations/sk.json | 1 + .../nibe_heatpump/translations/el.json | 38 +++++++++++++++++- .../nmap_tracker/translations/sk.json | 12 ++++++ .../components/nuki/translations/sk.json | 1 + .../components/nut/translations/sk.json | 1 + .../components/nzbget/translations/sk.json | 1 + .../components/octoprint/translations/sk.json | 11 +++++ .../components/onewire/translations/sk.json | 3 ++ .../components/onvif/translations/sk.json | 1 + .../onvif/translations/zh-Hant.json | 3 +- .../opengarage/translations/sk.json | 1 + .../components/openuv/translations/el.json | 3 ++ .../components/oralb/translations/el.json | 7 ++++ .../components/overkiz/translations/sk.json | 1 + .../ovo_energy/translations/el.json | 1 + .../p1_monitor/translations/sk.json | 1 + .../panasonic_viera/translations/sk.json | 1 + .../components/pi_hole/translations/sk.json | 1 + .../components/plex/translations/sk.json | 1 + .../plugwise/translations/select.el.json | 6 +++ .../components/plugwise/translations/sk.json | 6 +++ .../progettihwsw/translations/sk.json | 1 + .../components/prusalink/translations/sk.json | 11 +++++ .../pure_energie/translations/sk.json | 11 +++++ .../pushbullet/translations/el.json | 8 ++++ .../components/pushover/translations/el.json | 4 ++ .../radiotherm/translations/sk.json | 12 ++++++ .../rainforest_eagle/translations/sk.json | 7 ++++ .../rainmachine/translations/el.json | 1 + .../rainmachine/translations/sk.json | 1 + .../components/remote/translations/is.json | 6 +++ .../components/rfxtrx/translations/sk.json | 1 + .../components/risco/translations/sk.json | 7 ++++ .../components/roku/translations/sk.json | 7 ++++ .../components/roomba/translations/sk.json | 17 ++++++++ .../components/roon/translations/sk.json | 7 ++++ .../ruckus_unleashed/translations/sk.json | 1 + .../components/samsungtv/translations/sk.json | 1 + .../components/scrape/translations/el.json | 6 +++ .../components/senseme/translations/sk.json | 11 ++++- .../sensibo/translations/sensor.el.json | 5 +++ .../components/sensor/translations/el.json | 1 + .../components/sensor/translations/is.json | 5 +++ .../components/shelly/translations/sk.json | 3 ++ .../shelly/translations/zh-Hant.json | 13 ++++++ .../components/sma/translations/sk.json | 7 ++++ .../components/smappee/translations/sk.json | 11 +++++ .../components/snooz/translations/el.json | 12 ++++++ .../components/solarlog/translations/sk.json | 11 +++++ .../components/soma/translations/sk.json | 1 + .../somfy_mylink/translations/sk.json | 1 + .../components/songpal/translations/sk.json | 5 +++ .../soundtouch/translations/sk.json | 11 +++++ .../squeezebox/translations/sk.json | 7 ++++ .../statistics/translations/el.json | 12 ++++++ .../components/steamist/translations/sk.json | 7 ++++ .../components/switch/translations/is.json | 6 +++ .../components/switchbee/translations/sk.json | 11 +++++ .../synology_dsm/translations/sk.json | 2 + .../tesla_wall_connector/translations/sk.json | 11 +++++ .../components/tolo/translations/sk.json | 11 +++++ .../components/tplink/translations/sk.json | 11 +++++ .../components/tradfri/translations/sk.json | 7 ++++ .../transmission/translations/el.json | 13 ++++++ .../transmission/translations/sk.json | 1 + .../components/twinkly/translations/sk.json | 11 +++++ .../components/unifi/translations/sk.json | 2 + .../unifiprotect/translations/el.json | 4 +- .../unifiprotect/translations/sk.json | 1 + .../components/vacuum/translations/is.json | 5 +++ .../components/vallox/translations/sk.json | 17 ++++++++ .../components/venstar/translations/sk.json | 11 +++++ .../components/vicare/translations/sk.json | 1 + .../components/vilfo/translations/sk.json | 3 +- .../components/vizio/translations/sk.json | 1 + .../vlc_telnet/translations/sk.json | 2 + .../components/volumio/translations/sk.json | 1 + .../wake_on_lan/translations/zh-Hant.json | 8 ++++ .../components/webostv/translations/sk.json | 1 + .../components/wiz/translations/sk.json | 8 ++++ .../components/wled/translations/sk.json | 11 +++++ .../xiaomi_aqara/translations/sk.json | 7 ++++ .../xiaomi_miio/translations/sk.json | 1 + .../yamaha_musiccast/translations/sk.json | 7 ++++ .../components/yeelight/translations/sk.json | 8 ++++ .../components/youless/translations/sk.json | 1 + .../components/zamg/translations/el.json | 19 +++++++++ .../components/zwave_js/translations/el.json | 3 +- .../components/zwave_js/translations/is.json | 7 ++++ 225 files changed, 1626 insertions(+), 30 deletions(-) create mode 100644 homeassistant/components/accuweather/translations/sensor.sk.json create mode 100644 homeassistant/components/acmeda/translations/sk.json create mode 100644 homeassistant/components/airq/translations/el.json create mode 100644 homeassistant/components/airq/translations/sk.json create mode 100644 homeassistant/components/airthings_ble/translations/sk.json create mode 100644 homeassistant/components/airtouch4/translations/sk.json create mode 100644 homeassistant/components/airzone/translations/sk.json create mode 100644 homeassistant/components/almond/translations/sk.json create mode 100644 homeassistant/components/amberelectric/translations/sk.json create mode 100644 homeassistant/components/android_ip_webcam/translations/sk.json create mode 100644 homeassistant/components/anthemav/translations/sk.json create mode 100644 homeassistant/components/apcupsd/translations/sk.json create mode 100644 homeassistant/components/aranet/translations/el.json create mode 100644 homeassistant/components/balboa/translations/sk.json create mode 100644 homeassistant/components/braviatv/translations/sk.json create mode 100644 homeassistant/components/brother/translations/sk.json create mode 100644 homeassistant/components/coolmaster/translations/sk.json create mode 100644 homeassistant/components/directv/translations/sk.json create mode 100644 homeassistant/components/dlna_dmr/translations/sk.json create mode 100644 homeassistant/components/dlna_dms/translations/sk.json create mode 100644 homeassistant/components/dnsip/translations/sk.json create mode 100644 homeassistant/components/dunehd/translations/sk.json create mode 100644 homeassistant/components/emonitor/translations/sk.json create mode 100644 homeassistant/components/evil_genius_labs/translations/sk.json create mode 100644 homeassistant/components/forked_daapd/translations/sk.json create mode 100644 homeassistant/components/fronius/translations/sk.json create mode 100644 homeassistant/components/fully_kiosk/translations/sk.json create mode 100644 homeassistant/components/harmony/translations/sk.json create mode 100644 homeassistant/components/heos/translations/sk.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/zh-Hant.json create mode 100644 homeassistant/components/humidifier/translations/is.json create mode 100644 homeassistant/components/hunterdouglas_powerview/translations/sk.json create mode 100644 homeassistant/components/intellifire/translations/sk.json create mode 100644 homeassistant/components/kaleidescape/translations/sk.json create mode 100644 homeassistant/components/lametric/translations/select.el.json create mode 100644 homeassistant/components/lg_soundbar/translations/sk.json create mode 100644 homeassistant/components/livisi/translations/el.json create mode 100644 homeassistant/components/livisi/translations/sk.json create mode 100644 homeassistant/components/lutron_caseta/translations/sk.json create mode 100644 homeassistant/components/modern_forms/translations/sk.json create mode 100644 homeassistant/components/moehlenhoff_alpha2/translations/sk.json create mode 100644 homeassistant/components/mutesync/translations/sk.json create mode 100644 homeassistant/components/netgear/translations/sk.json create mode 100644 homeassistant/components/nmap_tracker/translations/sk.json create mode 100644 homeassistant/components/octoprint/translations/sk.json create mode 100644 homeassistant/components/oralb/translations/el.json create mode 100644 homeassistant/components/prusalink/translations/sk.json create mode 100644 homeassistant/components/pure_energie/translations/sk.json create mode 100644 homeassistant/components/pushbullet/translations/el.json create mode 100644 homeassistant/components/radiotherm/translations/sk.json create mode 100644 homeassistant/components/roomba/translations/sk.json create mode 100644 homeassistant/components/smappee/translations/sk.json create mode 100644 homeassistant/components/snooz/translations/el.json create mode 100644 homeassistant/components/solarlog/translations/sk.json create mode 100644 homeassistant/components/songpal/translations/sk.json create mode 100644 homeassistant/components/soundtouch/translations/sk.json create mode 100644 homeassistant/components/statistics/translations/el.json create mode 100644 homeassistant/components/switchbee/translations/sk.json create mode 100644 homeassistant/components/tesla_wall_connector/translations/sk.json create mode 100644 homeassistant/components/tolo/translations/sk.json create mode 100644 homeassistant/components/tplink/translations/sk.json create mode 100644 homeassistant/components/twinkly/translations/sk.json create mode 100644 homeassistant/components/vallox/translations/sk.json create mode 100644 homeassistant/components/venstar/translations/sk.json create mode 100644 homeassistant/components/wake_on_lan/translations/zh-Hant.json create mode 100644 homeassistant/components/wled/translations/sk.json create mode 100644 homeassistant/components/zamg/translations/el.json create mode 100644 homeassistant/components/zwave_js/translations/is.json diff --git a/homeassistant/components/abode/translations/sk.json b/homeassistant/components/abode/translations/sk.json index 2230fa979b4..5dd46ab3755 100644 --- a/homeassistant/components/abode/translations/sk.json +++ b/homeassistant/components/abode/translations/sk.json @@ -1,21 +1,33 @@ { "config": { "abort": { - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_mfa_code": "Neplatn\u00fd k\u00f3d MFA" }, "step": { + "mfa": { + "data": { + "mfa_code": "K\u00f3d MFA (6-miestny)" + }, + "title": "Zadajte svoj k\u00f3d MFA pre Abode" + }, "reauth_confirm": { "data": { + "password": "Heslo", "username": "Email" } }, "user": { "data": { + "password": "Heslo", "username": "Email" - } + }, + "title": "Vypl\u0148te svoje prihlasovacie \u00fadaje Abode" } } } diff --git a/homeassistant/components/accuweather/translations/sensor.sk.json b/homeassistant/components/accuweather/translations/sensor.sk.json new file mode 100644 index 00000000000..4152daff605 --- /dev/null +++ b/homeassistant/components/accuweather/translations/sensor.sk.json @@ -0,0 +1,8 @@ +{ + "state": { + "accuweather__pressure_tendency": { + "falling": "Klesaj\u00faci", + "rising": "Padaj\u00faci" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/accuweather/translations/sk.json b/homeassistant/components/accuweather/translations/sk.json index 8e0bc629a13..cc5fe25ca87 100644 --- a/homeassistant/components/accuweather/translations/sk.json +++ b/homeassistant/components/accuweather/translations/sk.json @@ -1,6 +1,10 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" }, "step": { @@ -13,5 +17,14 @@ } } } + }, + "options": { + "step": { + "user": { + "data": { + "forecast": "Predpove\u010f po\u010dasia" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/acmeda/translations/sk.json b/homeassistant/components/acmeda/translations/sk.json new file mode 100644 index 00000000000..7733c32fc87 --- /dev/null +++ b/homeassistant/components/acmeda/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "id": "ID hostite\u013ea" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/adguard/translations/sk.json b/homeassistant/components/adguard/translations/sk.json index 892b8b2cd91..0a425483090 100644 --- a/homeassistant/components/adguard/translations/sk.json +++ b/homeassistant/components/adguard/translations/sk.json @@ -1,9 +1,20 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { - "port": "Port" + "host": "Hostite\u013e", + "password": "Heslo", + "port": "Port", + "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/advantage_air/translations/sk.json b/homeassistant/components/advantage_air/translations/sk.json index 892b8b2cd91..7abc08b46d9 100644 --- a/homeassistant/components/advantage_air/translations/sk.json +++ b/homeassistant/components/advantage_air/translations/sk.json @@ -1,10 +1,18 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { + "ip_address": "IP adresa", "port": "Port" - } + }, + "title": "Pripoji\u0165" } } } diff --git a/homeassistant/components/aemet/translations/sk.json b/homeassistant/components/aemet/translations/sk.json index 3c287c2d9d2..f14039f810f 100644 --- a/homeassistant/components/aemet/translations/sk.json +++ b/homeassistant/components/aemet/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" }, diff --git a/homeassistant/components/agent_dvr/translations/sk.json b/homeassistant/components/agent_dvr/translations/sk.json index ba2680ac75e..b6699663a03 100644 --- a/homeassistant/components/agent_dvr/translations/sk.json +++ b/homeassistant/components/agent_dvr/translations/sk.json @@ -1,11 +1,16 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/airly/translations/sk.json b/homeassistant/components/airly/translations/sk.json index 8e0bc629a13..6f941ead750 100644 --- a/homeassistant/components/airly/translations/sk.json +++ b/homeassistant/components/airly/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" }, diff --git a/homeassistant/components/airq/translations/el.json b/homeassistant/components/airq/translations/el.json new file mode 100644 index 00000000000..3bfc85e23b9 --- /dev/null +++ b/homeassistant/components/airq/translations/el.json @@ -0,0 +1,10 @@ +{ + "config": { + "step": { + "user": { + "description": "\u0394\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03ae \u03c4\u03bf mDNS \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03ba\u03b1\u03b9 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03ae\u03c2 \u03c4\u03b7\u03c2", + "title": "\u03a0\u03c1\u03bf\u03c3\u03b4\u03b9\u03bf\u03c1\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/sk.json b/homeassistant/components/airq/translations/sk.json new file mode 100644 index 00000000000..3d0490e59bd --- /dev/null +++ b/homeassistant/components/airq/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "invalid_input": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airthings/translations/sk.json b/homeassistant/components/airthings/translations/sk.json index 5ada995aa6e..a404aeedf8e 100644 --- a/homeassistant/components/airthings/translations/sk.json +++ b/homeassistant/components/airthings/translations/sk.json @@ -1,7 +1,16 @@ { "config": { "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "id": "ID" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/airthings_ble/translations/sk.json b/homeassistant/components/airthings_ble/translations/sk.json new file mode 100644 index 00000000000..5f8e521919f --- /dev/null +++ b/homeassistant/components/airthings_ble/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airtouch4/translations/sk.json b/homeassistant/components/airtouch4/translations/sk.json new file mode 100644 index 00000000000..c8e33ffce5e --- /dev/null +++ b/homeassistant/components/airtouch4/translations/sk.json @@ -0,0 +1,17 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual/translations/sensor.sk.json b/homeassistant/components/airvisual/translations/sensor.sk.json index 6d640d965b9..e54107c433a 100644 --- a/homeassistant/components/airvisual/translations/sensor.sk.json +++ b/homeassistant/components/airvisual/translations/sensor.sk.json @@ -1,7 +1,17 @@ { "state": { + "airvisual__pollutant_label": { + "p1": "PM10", + "p2": "PM2,5", + "s2": "Oxid siri\u010dit\u00fd" + }, "airvisual__pollutant_level": { - "good": "Dobr\u00e9" + "good": "Dobr\u00e9", + "hazardous": "Nebezpe\u010dn\u00e9", + "moderate": "Mierne", + "unhealthy": "Nezdrav\u00e9", + "unhealthy_sensitive": "Nezdrav\u00e9 pre citliv\u00e9 skupiny", + "very_unhealthy": "Ve\u013emi nezdrav\u00e9" } } } \ No newline at end of file diff --git a/homeassistant/components/airvisual/translations/sk.json b/homeassistant/components/airvisual/translations/sk.json index 22c02bbfec3..156f165a5ab 100644 --- a/homeassistant/components/airvisual/translations/sk.json +++ b/homeassistant/components/airvisual/translations/sk.json @@ -4,7 +4,10 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "general_error": "Neo\u010dak\u00e1van\u00e1 chyba", + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", + "location_not_found": "Poloha sa nena\u0161la" }, "step": { "geography_by_coords": { @@ -16,7 +19,15 @@ }, "geography_by_name": { "data": { - "api_key": "API k\u013e\u00fa\u010d" + "api_key": "API k\u013e\u00fa\u010d", + "city": "Mesto", + "country": "Krajina", + "state": "stav" + } + }, + "node_pro": { + "data": { + "ip_address": "Hostite\u013e" } }, "reauth_confirm": { diff --git a/homeassistant/components/airzone/translations/sk.json b/homeassistant/components/airzone/translations/sk.json new file mode 100644 index 00000000000..04558fa44e4 --- /dev/null +++ b/homeassistant/components/airzone/translations/sk.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e", + "port": "Port" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/sk.json b/homeassistant/components/aladdin_connect/translations/sk.json index c9a7b4c204a..bfab81adff8 100644 --- a/homeassistant/components/aladdin_connect/translations/sk.json +++ b/homeassistant/components/aladdin_connect/translations/sk.json @@ -1,9 +1,24 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie" + }, "step": { "reauth_confirm": { "data": { "password": "Heslo" + }, + "title": "Znova overi\u0165 integr\u00e1ciu" + }, + "user": { + "data": { + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/alarm_control_panel/translations/is.json b/homeassistant/components/alarm_control_panel/translations/is.json index 16c2aeec21d..eb962bc6626 100644 --- a/homeassistant/components/alarm_control_panel/translations/is.json +++ b/homeassistant/components/alarm_control_panel/translations/is.json @@ -4,7 +4,8 @@ "is_armed_away": "{entity_name} er \u00e1 ver\u00f0i \u00fati", "is_armed_home": "{entity_name} er \u00e1 ver\u00f0i heima", "is_armed_night": "{entity_name} er \u00e1 ver\u00f0i n\u00f3tt", - "is_disarmed": "{entity_name} er ekki \u00e1 ver\u00f0i" + "is_disarmed": "{entity_name} er ekki \u00e1 ver\u00f0i", + "is_triggered": "{entity_name} er r\u00e6st" }, "trigger_type": { "armed_away": "{entity_name} \u00e1 ver\u00f0i \u00fati", diff --git a/homeassistant/components/alarmdecoder/translations/sk.json b/homeassistant/components/alarmdecoder/translations/sk.json index 9b801344831..15235e55f54 100644 --- a/homeassistant/components/alarmdecoder/translations/sk.json +++ b/homeassistant/components/alarmdecoder/translations/sk.json @@ -3,9 +3,31 @@ "step": { "protocol": { "data": { + "host": "Hostite\u013e", "port": "Port" } } } + }, + "options": { + "step": { + "init": { + "data": { + "edit_select": "Upravi\u0165" + } + }, + "zone_details": { + "data": { + "zone_loop": "RF slu\u010dka", + "zone_name": "N\u00e1zov z\u00f3ny", + "zone_rfid": "RF Serial" + } + }, + "zone_select": { + "data": { + "zone_number": "\u010c\u00edslo z\u00f3ny" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/almond/translations/sk.json b/homeassistant/components/almond/translations/sk.json new file mode 100644 index 00000000000..f5f8cd88968 --- /dev/null +++ b/homeassistant/components/almond/translations/sk.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/amberelectric/translations/sk.json b/homeassistant/components/amberelectric/translations/sk.json new file mode 100644 index 00000000000..8b5990b5086 --- /dev/null +++ b/homeassistant/components/amberelectric/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "error": { + "invalid_api_token": "Neplatn\u00fd API k\u013e\u00fa\u010d", + "unknown_error": "Neo\u010dak\u00e1van\u00e1 chyba" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ambiclimate/translations/sk.json b/homeassistant/components/ambiclimate/translations/sk.json index c19b1a0b70c..86647261f75 100644 --- a/homeassistant/components/ambiclimate/translations/sk.json +++ b/homeassistant/components/ambiclimate/translations/sk.json @@ -1,5 +1,9 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie." + }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" } diff --git a/homeassistant/components/ambient_station/translations/sk.json b/homeassistant/components/ambient_station/translations/sk.json index 01c13a4f11e..0524846d2bf 100644 --- a/homeassistant/components/ambient_station/translations/sk.json +++ b/homeassistant/components/ambient_station/translations/sk.json @@ -1,13 +1,18 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, "error": { "invalid_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" }, "step": { "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" - } + "api_key": "API k\u013e\u00fa\u010d", + "app_key": "Aplika\u010dn\u00fd k\u013e\u00fa\u010d" + }, + "title": "Vypl\u0148te svoje \u00fadaje" } } } diff --git a/homeassistant/components/android_ip_webcam/translations/sk.json b/homeassistant/components/android_ip_webcam/translations/sk.json new file mode 100644 index 00000000000..6455e273fb1 --- /dev/null +++ b/homeassistant/components/android_ip_webcam/translations/sk.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e", + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/androidtv/translations/sk.json b/homeassistant/components/androidtv/translations/sk.json index 86bba63ac49..6820d55bf23 100644 --- a/homeassistant/components/androidtv/translations/sk.json +++ b/homeassistant/components/androidtv/translations/sk.json @@ -3,9 +3,13 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/anthemav/translations/sk.json b/homeassistant/components/anthemav/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/anthemav/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/apcupsd/translations/sk.json b/homeassistant/components/apcupsd/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/apcupsd/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/el.json b/homeassistant/components/aranet/translations/el.json new file mode 100644 index 00000000000..a22b2c2813e --- /dev/null +++ b/homeassistant/components/aranet/translations/el.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "integrations_diabled": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b5\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03b1\u03c4\u03ce\u03c3\u03b5\u03b9\u03c2. \u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03b1\u03c4\u03ce\u03c3\u03b5\u03b9\u03c2 \u03ad\u03be\u03c5\u03c0\u03bd\u03bf\u03c5 \u03c3\u03c0\u03b9\u03c4\u03b9\u03bf\u03cd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae \u03ba\u03b1\u03b9 \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac.", + "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03bc\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03bc\u03ad\u03bd\u03b5\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 Aranet.", + "outdated_version": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c0\u03b1\u03bb\u03b9\u03cc \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc. \u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c4\u03bf\u03c5\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf\u03bd \u03c3\u03b5 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 1.2.0 \u03ba\u03b1\u03b9 \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/arcam_fmj/translations/sk.json b/homeassistant/components/arcam_fmj/translations/sk.json index b41d6edbd4b..0f258ae8682 100644 --- a/homeassistant/components/arcam_fmj/translations/sk.json +++ b/homeassistant/components/arcam_fmj/translations/sk.json @@ -3,9 +3,11 @@ "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "flow_title": "{host}", "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/asuswrt/translations/sk.json b/homeassistant/components/asuswrt/translations/sk.json index 39d2e182c40..0cdd25de0a5 100644 --- a/homeassistant/components/asuswrt/translations/sk.json +++ b/homeassistant/components/asuswrt/translations/sk.json @@ -1,8 +1,12 @@ { "config": { + "error": { + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov", "port": "Port" } diff --git a/homeassistant/components/atag/translations/sk.json b/homeassistant/components/atag/translations/sk.json index 5b7f75998d7..f92b79dc3e3 100644 --- a/homeassistant/components/atag/translations/sk.json +++ b/homeassistant/components/atag/translations/sk.json @@ -6,6 +6,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/awair/translations/sk.json b/homeassistant/components/awair/translations/sk.json index dabf3e7e933..79d8b98712e 100644 --- a/homeassistant/components/awair/translations/sk.json +++ b/homeassistant/components/awair/translations/sk.json @@ -4,6 +4,16 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "step": { + "local": { + "data": { + "host": "IP adresa" + } + }, + "local_pick": { + "data": { + "host": "IP adresa" + } + }, "reauth": { "data": { "access_token": "Pr\u00edstupov\u00fd token", diff --git a/homeassistant/components/axis/translations/sk.json b/homeassistant/components/axis/translations/sk.json index 53eb88bf838..add545fae6e 100644 --- a/homeassistant/components/axis/translations/sk.json +++ b/homeassistant/components/axis/translations/sk.json @@ -4,9 +4,11 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{name} ({host})", "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/balboa/translations/sk.json b/homeassistant/components/balboa/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/balboa/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/binary_sensor/translations/is.json b/homeassistant/components/binary_sensor/translations/is.json index ac2c9901cad..72bc49a04c6 100644 --- a/homeassistant/components/binary_sensor/translations/is.json +++ b/homeassistant/components/binary_sensor/translations/is.json @@ -1,17 +1,52 @@ { "device_automation": { "condition_type": { + "is_bat_low": "{entity_name} rafhla\u00f0an er l\u00edtil", "is_co": "{entity_name} skynja\u00f0i kolm\u00f3noxi\u00f0", + "is_cold": "{entity_name} er kalt", + "is_connected": "{entity_name} er tengdur", "is_gas": "{entity_name} skynja\u00f0i gas", + "is_hot": "{entity_name} er heitt", "is_light": "{entity_name} skynja\u00f0i lj\u00f3s", + "is_locked": "{entity_name} er l\u00e6st", + "is_moist": "{entity_name} er rakt", "is_motion": "{entity_name} skynja\u00f0i hreyfingu", + "is_moving": "{entity_name} er \u00e1 hreyfingu", + "is_no_co": "{entity_name} er ekki a\u00f0 skynja kolm\u00f3nox\u00ed\u00f0", + "is_no_gas": "{entity_name} greinir ekki gas", + "is_no_light": "{entity_name} greinir ekki lj\u00f3s", "is_no_motion": "{entity_name} er ekki a\u00f0 skynja hreyfingu", + "is_no_problem": "{entity_name} greinir ekki vandam\u00e1l", "is_no_smoke": "{entity_name} er ekki a\u00f0 skynja reyk", + "is_no_sound": "{entity_name} greinir ekki hlj\u00f3\u00f0", + "is_no_update": "{entity_name} er uppf\u00e6rt", + "is_no_vibration": "{entity_name} greinir ekki titring", + "is_not_bat_low": "{entity_name} rafhla\u00f0a er e\u00f0lileg", + "is_not_cold": "{entity_name} er ekki kalt", + "is_not_connected": "{entity_name} er aftengt", + "is_not_locked": "{entity_name} er \u00f3l\u00e6st", + "is_not_moist": "{entity_name} er \u00feurr", + "is_not_moving": "{entity_name} hreyfist ekki", "is_not_open": "{entity_name} er loku\u00f0", + "is_not_plugged_in": "{entity_name} er aftengt", + "is_not_powered": "{entity_name} er ekki \u00ed gangi", + "is_not_present": "{entity_name} er ekki til sta\u00f0ar", + "is_not_running": "{entity_name} er ekki keyrandi", + "is_not_tampered": "{entity_name} skynja\u00f0i ekki fikt", + "is_not_unsafe": "{entity_name} er \u00f6ruggt", + "is_occupied": "{entity_name} er uppteki\u00f0", + "is_off": "{entity_name} er sl\u00f6kkt", + "is_on": "{entity_name} er \u00ed gangi", + "is_open": "{entity_name} er opin", + "is_plugged_in": "{entity_name} er tengdur", + "is_powered": "{entity_name} er \u00ed gangi", + "is_present": "{entity_name} er til sta\u00f0ar", "is_problem": "{entity_name} skynja\u00f0i vandam\u00e1l", + "is_running": "{entity_name} er keyrandi", "is_smoke": "{entity_name} skynja\u00f0i reyk", "is_sound": "{entity_name} skynja\u00f0i hlj\u00f3\u00f0", "is_tampered": "{entity_name} skynja\u00f0i fikt", + "is_unsafe": "{entity_name} er \u00f3\u00f6ruggt", "is_vibration": "{entity_name} skynja\u00f0i titring" }, "trigger_type": { diff --git a/homeassistant/components/blebox/translations/sk.json b/homeassistant/components/blebox/translations/sk.json index 892b8b2cd91..27bd1caec0c 100644 --- a/homeassistant/components/blebox/translations/sk.json +++ b/homeassistant/components/blebox/translations/sk.json @@ -1,8 +1,10 @@ { "config": { + "flow_title": "{name} ({host})", "step": { "user": { "data": { + "host": "IP adresa", "port": "Port" } } diff --git a/homeassistant/components/bond/translations/sk.json b/homeassistant/components/bond/translations/sk.json index e237bd34b0a..9d41265051b 100644 --- a/homeassistant/components/bond/translations/sk.json +++ b/homeassistant/components/bond/translations/sk.json @@ -3,6 +3,7 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{name} ({host})", "step": { "confirm": { "data": { @@ -11,7 +12,8 @@ }, "user": { "data": { - "access_token": "Pr\u00edstupov\u00fd token" + "access_token": "Pr\u00edstupov\u00fd token", + "host": "Hostite\u013e" } } } diff --git a/homeassistant/components/bosch_shc/translations/sk.json b/homeassistant/components/bosch_shc/translations/sk.json index 71a7aea5018..81a4dcb4a93 100644 --- a/homeassistant/components/bosch_shc/translations/sk.json +++ b/homeassistant/components/bosch_shc/translations/sk.json @@ -5,6 +5,13 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/braviatv/translations/el.json b/homeassistant/components/braviatv/translations/el.json index fc3ba88c57e..98ea6682eec 100644 --- a/homeassistant/components/braviatv/translations/el.json +++ b/homeassistant/components/braviatv/translations/el.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "\u03a0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ac\u03c3\u03c4\u03b7\u03ba\u03b5 \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bb\u03af\u03c3\u03c4\u03b1\u03c2 \u03c0\u03b7\u03b3\u03ce\u03bd. \n\n \u0392\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03b7 \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03ae \u03c3\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03c0\u03c1\u03b9\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/sk.json b/homeassistant/components/braviatv/translations/sk.json new file mode 100644 index 00000000000..2fe193920d4 --- /dev/null +++ b/homeassistant/components/braviatv/translations/sk.json @@ -0,0 +1,14 @@ +{ + "config": { + "error": { + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/broadlink/translations/sk.json b/homeassistant/components/broadlink/translations/sk.json index 358fdc848ff..cebc1d218d4 100644 --- a/homeassistant/components/broadlink/translations/sk.json +++ b/homeassistant/components/broadlink/translations/sk.json @@ -1,13 +1,23 @@ { "config": { "abort": { - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, + "error": { + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, + "flow_title": "{name} ({model} na {host})", "step": { "finish": { "data": { "name": "N\u00e1zov" } + }, + "user": { + "data": { + "host": "Hostite\u013e" + } } } } diff --git a/homeassistant/components/brother/translations/sk.json b/homeassistant/components/brother/translations/sk.json new file mode 100644 index 00000000000..39a130777c5 --- /dev/null +++ b/homeassistant/components/brother/translations/sk.json @@ -0,0 +1,14 @@ +{ + "config": { + "error": { + "wrong_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa." + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/bsblan/translations/sk.json b/homeassistant/components/bsblan/translations/sk.json index 892b8b2cd91..33d93b62475 100644 --- a/homeassistant/components/bsblan/translations/sk.json +++ b/homeassistant/components/bsblan/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/cast/translations/sk.json b/homeassistant/components/cast/translations/sk.json index e227301685b..f2782828716 100644 --- a/homeassistant/components/cast/translations/sk.json +++ b/homeassistant/components/cast/translations/sk.json @@ -1,9 +1,23 @@ { "config": { "step": { + "config": { + "data": { + "known_hosts": "Zn\u00e1mi hostitelia" + } + }, "confirm": { "description": "Chcete za\u010da\u0165 nastavova\u0165?" } } + }, + "options": { + "step": { + "basic_options": { + "data": { + "known_hosts": "Zn\u00e1mi hostitelia" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/cert_expiry/translations/sk.json b/homeassistant/components/cert_expiry/translations/sk.json index 892b8b2cd91..33d93b62475 100644 --- a/homeassistant/components/cert_expiry/translations/sk.json +++ b/homeassistant/components/cert_expiry/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/coinbase/translations/el.json b/homeassistant/components/coinbase/translations/el.json index 6b2141f091f..b4c1108b19a 100644 --- a/homeassistant/components/coinbase/translations/el.json +++ b/homeassistant/components/coinbase/translations/el.json @@ -21,6 +21,12 @@ } } }, + "issues": { + "removed_yaml": { + "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Coinbase \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", + "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03c4\u03bf\u03c5 Coinbase \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af" + } + }, "options": { "error": { "currency_unavailable": "\u0388\u03bd\u03b1 \u03ae \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03b1\u03c0\u03cc \u03c4\u03b1 \u03b6\u03b7\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03b1 \u03c5\u03c0\u03cc\u03bb\u03bf\u03b9\u03c0\u03b1 \u03bd\u03bf\u03bc\u03b9\u03c3\u03bc\u03ac\u03c4\u03c9\u03bd \u03b4\u03b5\u03bd \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf API \u03c4\u03b7\u03c2 Coinbase.", diff --git a/homeassistant/components/control4/translations/sk.json b/homeassistant/components/control4/translations/sk.json index 5ada995aa6e..b8437f98a1e 100644 --- a/homeassistant/components/control4/translations/sk.json +++ b/homeassistant/components/control4/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "IP adresa" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/coolmaster/translations/sk.json b/homeassistant/components/coolmaster/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/coolmaster/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cover/translations/is.json b/homeassistant/components/cover/translations/is.json index ce8052eb02b..f3844b33c61 100644 --- a/homeassistant/components/cover/translations/is.json +++ b/homeassistant/components/cover/translations/is.json @@ -1,7 +1,10 @@ { "device_automation": { "condition_type": { - "is_closed": "{entity_name} er loku\u00f0" + "is_closed": "{entity_name} er loku\u00f0", + "is_closing": "{entity_name} er a\u00f0 loka", + "is_open": "{entity_name} er opin", + "is_opening": "{entity_name} er a\u00f0 opnast" }, "trigger_type": { "closed": "{entity_name} loku\u00f0", diff --git a/homeassistant/components/daikin/translations/sk.json b/homeassistant/components/daikin/translations/sk.json index a1222826481..53957647798 100644 --- a/homeassistant/components/daikin/translations/sk.json +++ b/homeassistant/components/daikin/translations/sk.json @@ -7,7 +7,8 @@ "step": { "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" + "api_key": "API k\u013e\u00fa\u010d", + "host": "Hostite\u013e" } } } diff --git a/homeassistant/components/deconz/translations/sk.json b/homeassistant/components/deconz/translations/sk.json index 81684739474..8031205063b 100644 --- a/homeassistant/components/deconz/translations/sk.json +++ b/homeassistant/components/deconz/translations/sk.json @@ -6,6 +6,7 @@ "step": { "manual_input": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/deluge/translations/sk.json b/homeassistant/components/deluge/translations/sk.json index 0fbba9ccd6b..bf47f225eb7 100644 --- a/homeassistant/components/deluge/translations/sk.json +++ b/homeassistant/components/deluge/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port", "username": "U\u017e\u00edvate\u013esk\u00e9 meno" } diff --git a/homeassistant/components/denonavr/translations/sk.json b/homeassistant/components/denonavr/translations/sk.json index bee0999420f..426e2fb9f72 100644 --- a/homeassistant/components/denonavr/translations/sk.json +++ b/homeassistant/components/denonavr/translations/sk.json @@ -2,6 +2,18 @@ "config": { "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + }, + "step": { + "select": { + "data": { + "select_host": "IP adresa prij\u00edma\u010da" + } + }, + "user": { + "data": { + "host": "IP adresa" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/device_tracker/translations/is.json b/homeassistant/components/device_tracker/translations/is.json index 433d2a6afb8..3696d2a25a4 100644 --- a/homeassistant/components/device_tracker/translations/is.json +++ b/homeassistant/components/device_tracker/translations/is.json @@ -1,4 +1,10 @@ { + "device_automation": { + "condition_type": { + "is_home": "{entity_name} er heima", + "is_not_home": "{entity_name} er ekki heima" + } + }, "state": { "_": { "home": "Heima", diff --git a/homeassistant/components/directv/translations/sk.json b/homeassistant/components/directv/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/directv/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dlna_dmr/translations/sk.json b/homeassistant/components/dlna_dmr/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/dlna_dmr/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dlna_dms/translations/sk.json b/homeassistant/components/dlna_dms/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/dlna_dms/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dnsip/translations/sk.json b/homeassistant/components/dnsip/translations/sk.json new file mode 100644 index 00000000000..69a87c21915 --- /dev/null +++ b/homeassistant/components/dnsip/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "invalid_hostname": "Neplatn\u00fd n\u00e1zov hostite\u013ea" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/doorbird/translations/sk.json b/homeassistant/components/doorbird/translations/sk.json index 5ada995aa6e..429cf4e7a80 100644 --- a/homeassistant/components/doorbird/translations/sk.json +++ b/homeassistant/components/doorbird/translations/sk.json @@ -2,6 +2,14 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "flow_title": "{name} ({host})", + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/dsmr/translations/sk.json b/homeassistant/components/dsmr/translations/sk.json index e343d2e8b31..d74ce79a3d7 100644 --- a/homeassistant/components/dsmr/translations/sk.json +++ b/homeassistant/components/dsmr/translations/sk.json @@ -3,6 +3,7 @@ "step": { "setup_network": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/dunehd/translations/sk.json b/homeassistant/components/dunehd/translations/sk.json new file mode 100644 index 00000000000..2fe193920d4 --- /dev/null +++ b/homeassistant/components/dunehd/translations/sk.json @@ -0,0 +1,14 @@ +{ + "config": { + "error": { + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/elgato/translations/sk.json b/homeassistant/components/elgato/translations/sk.json index 892b8b2cd91..33d93b62475 100644 --- a/homeassistant/components/elgato/translations/sk.json +++ b/homeassistant/components/elgato/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/elkm1/translations/sk.json b/homeassistant/components/elkm1/translations/sk.json index 0b7bf878ea9..96183ba3069 100644 --- a/homeassistant/components/elkm1/translations/sk.json +++ b/homeassistant/components/elkm1/translations/sk.json @@ -5,6 +5,7 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" - } + }, + "flow_title": "{mac_address} ({host})" } } \ No newline at end of file diff --git a/homeassistant/components/emonitor/translations/sk.json b/homeassistant/components/emonitor/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/emonitor/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/emulated_roku/translations/sk.json b/homeassistant/components/emulated_roku/translations/sk.json index af15f92c2f2..26dc85eff39 100644 --- a/homeassistant/components/emulated_roku/translations/sk.json +++ b/homeassistant/components/emulated_roku/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host_ip": "IP adresa hostite\u013ea", "name": "N\u00e1zov" } } diff --git a/homeassistant/components/enphase_envoy/translations/sk.json b/homeassistant/components/enphase_envoy/translations/sk.json index 71a7aea5018..1072f39428f 100644 --- a/homeassistant/components/enphase_envoy/translations/sk.json +++ b/homeassistant/components/enphase_envoy/translations/sk.json @@ -5,6 +5,14 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "flow_title": "{serial} ( {host} )", + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/epson/translations/sk.json b/homeassistant/components/epson/translations/sk.json index af15f92c2f2..965b3bb8919 100644 --- a/homeassistant/components/epson/translations/sk.json +++ b/homeassistant/components/epson/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov" } } diff --git a/homeassistant/components/esphome/translations/sk.json b/homeassistant/components/esphome/translations/sk.json index ca12462f388..86bf0aa5397 100644 --- a/homeassistant/components/esphome/translations/sk.json +++ b/homeassistant/components/esphome/translations/sk.json @@ -37,10 +37,16 @@ }, "user": { "data": { + "host": "Hostite\u013e", "port": "Port" }, "description": "Pros\u00edm, zadajte nastavenia pripojenia v\u00e1\u0161ho uzla [ESPHome](https://esphomelib.com/)." } } + }, + "issues": { + "ble_firmware_outdated": { + "title": "Aktualizujte {name} pomocou ESPHome 2022.11.0 alebo nov\u0161ieho" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/zh-Hant.json b/homeassistant/components/esphome/translations/zh-Hant.json index 44f50a433e3..b08d5d97c34 100644 --- a/homeassistant/components/esphome/translations/zh-Hant.json +++ b/homeassistant/components/esphome/translations/zh-Hant.json @@ -43,5 +43,11 @@ "description": "\u8acb\u8f38\u5165 [ESPHome]({esphome_url}) \u7bc0\u9ede\u9023\u7dda\u8cc7\u8a0a\u3002" } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "\u6b32\u6539\u5584\u85cd\u82bd\u53ef\u9760\u6027\u8207\u6548\u80fd\uff0c\u5f37\u70c8\u5efa\u8b70\u60a8\u66f4\u65b0\u81f3 2022.11.0 \u7248\u6216\u66f4\u65b0\u7248\u672c ESPHome \u4e4b {name}\u3002", + "title": "\u66f4\u65b0\u81f3 2022.11.0 \u7248\u6216\u66f4\u65b0\u7248\u672c ESPHome \u4e4b {name} " + } } } \ No newline at end of file diff --git a/homeassistant/components/evil_genius_labs/translations/sk.json b/homeassistant/components/evil_genius_labs/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/evil_genius_labs/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ezviz/translations/sk.json b/homeassistant/components/ezviz/translations/sk.json index 5ada995aa6e..c6b3dbdc5e0 100644 --- a/homeassistant/components/ezviz/translations/sk.json +++ b/homeassistant/components/ezviz/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" } } } \ No newline at end of file diff --git a/homeassistant/components/fan/translations/is.json b/homeassistant/components/fan/translations/is.json index f06d44366b0..840591cb4e3 100644 --- a/homeassistant/components/fan/translations/is.json +++ b/homeassistant/components/fan/translations/is.json @@ -1,4 +1,10 @@ { + "device_automation": { + "condition_type": { + "is_off": "{entity_name} er sl\u00f6kkt", + "is_on": "{entity_name} er \u00ed gangi" + } + }, "state": { "_": { "off": "Sl\u00f6kkt", diff --git a/homeassistant/components/fireservicerota/translations/el.json b/homeassistant/components/fireservicerota/translations/el.json index 467a98a49ad..68cc2556581 100644 --- a/homeassistant/components/fireservicerota/translations/el.json +++ b/homeassistant/components/fireservicerota/translations/el.json @@ -17,6 +17,9 @@ }, "description": "\u03a4\u03b1 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03ac \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ad\u03b3\u03b9\u03bd\u03b1\u03bd \u03ac\u03ba\u03c5\u03c1\u03b1, \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c4\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac." }, + "reauth_confirm": { + "description": "\u03a4\u03b1 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03ac \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ad\u03b3\u03b9\u03bd\u03b1\u03bd \u03ac\u03ba\u03c5\u03c1\u03b1, \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c4\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac." + }, "user": { "data": { "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", diff --git a/homeassistant/components/fivem/translations/sk.json b/homeassistant/components/fivem/translations/sk.json index 39d2e182c40..1c96c63ac91 100644 --- a/homeassistant/components/fivem/translations/sk.json +++ b/homeassistant/components/fivem/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov", "port": "Port" } diff --git a/homeassistant/components/flo/translations/sk.json b/homeassistant/components/flo/translations/sk.json index 5ada995aa6e..368553a4887 100644 --- a/homeassistant/components/flo/translations/sk.json +++ b/homeassistant/components/flo/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/flux_led/translations/sk.json b/homeassistant/components/flux_led/translations/sk.json index bee0999420f..cefae299404 100644 --- a/homeassistant/components/flux_led/translations/sk.json +++ b/homeassistant/components/flux_led/translations/sk.json @@ -2,6 +2,13 @@ "config": { "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/forked_daapd/translations/sk.json b/homeassistant/components/forked_daapd/translations/sk.json new file mode 100644 index 00000000000..d7dee4be88b --- /dev/null +++ b/homeassistant/components/forked_daapd/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "flow_title": "{name} ({host})", + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/sk.json b/homeassistant/components/foscam/translations/sk.json index 8bbcb516b56..cdd79c25392 100644 --- a/homeassistant/components/foscam/translations/sk.json +++ b/homeassistant/components/foscam/translations/sk.json @@ -9,6 +9,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } diff --git a/homeassistant/components/freebox/translations/sk.json b/homeassistant/components/freebox/translations/sk.json index 892b8b2cd91..33d93b62475 100644 --- a/homeassistant/components/freebox/translations/sk.json +++ b/homeassistant/components/freebox/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/fritz/translations/sk.json b/homeassistant/components/fritz/translations/sk.json index 17cb7bfc78b..0ee0cae7a3d 100644 --- a/homeassistant/components/fritz/translations/sk.json +++ b/homeassistant/components/fritz/translations/sk.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/fritzbox/translations/sk.json b/homeassistant/components/fritzbox/translations/sk.json index e0e6b1c5bda..3c7866e85a8 100644 --- a/homeassistant/components/fritzbox/translations/sk.json +++ b/homeassistant/components/fritzbox/translations/sk.json @@ -6,6 +6,13 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/fritzbox_callmonitor/translations/sk.json b/homeassistant/components/fritzbox_callmonitor/translations/sk.json index 1145b3bb9f8..6f2d6c9c475 100644 --- a/homeassistant/components/fritzbox_callmonitor/translations/sk.json +++ b/homeassistant/components/fritzbox_callmonitor/translations/sk.json @@ -6,6 +6,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/fronius/translations/sk.json b/homeassistant/components/fronius/translations/sk.json new file mode 100644 index 00000000000..e583414047d --- /dev/null +++ b/homeassistant/components/fronius/translations/sk.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fully_kiosk/translations/sk.json b/homeassistant/components/fully_kiosk/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/fully_kiosk/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/glances/translations/sk.json b/homeassistant/components/glances/translations/sk.json index 39d2e182c40..1c96c63ac91 100644 --- a/homeassistant/components/glances/translations/sk.json +++ b/homeassistant/components/glances/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov", "port": "Port" } diff --git a/homeassistant/components/goalzero/translations/sk.json b/homeassistant/components/goalzero/translations/sk.json index af15f92c2f2..a7ffce559f2 100644 --- a/homeassistant/components/goalzero/translations/sk.json +++ b/homeassistant/components/goalzero/translations/sk.json @@ -1,8 +1,15 @@ { "config": { + "abort": { + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, + "error": { + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov" } } diff --git a/homeassistant/components/goodwe/translations/sk.json b/homeassistant/components/goodwe/translations/sk.json index bee0999420f..a245098293d 100644 --- a/homeassistant/components/goodwe/translations/sk.json +++ b/homeassistant/components/goodwe/translations/sk.json @@ -2,6 +2,13 @@ "config": { "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + }, + "step": { + "user": { + "data": { + "host": "IP adresa" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/google_travel_time/translations/el.json b/homeassistant/components/google_travel_time/translations/el.json index 8180ef61c03..3385eb26b48 100644 --- a/homeassistant/components/google_travel_time/translations/el.json +++ b/homeassistant/components/google_travel_time/translations/el.json @@ -27,6 +27,7 @@ "mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c4\u03b1\u03be\u03b9\u03b4\u03b9\u03bf\u03cd", "time": "\u038f\u03c1\u03b1", "time_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03ce\u03c1\u03b1\u03c2", + "traffic_mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03ba\u03c5\u03ba\u03bb\u03bf\u03c6\u03bf\u03c1\u03af\u03b1\u03c2", "transit_mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03c5\u03b3\u03ba\u03bf\u03b9\u03bd\u03c9\u03bd\u03af\u03b1\u03c2", "transit_routing_preference": "\u03a0\u03c1\u03bf\u03c4\u03af\u03bc\u03b7\u03c3\u03b7 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7\u03c2 \u03c3\u03c5\u03b3\u03ba\u03bf\u03b9\u03bd\u03c9\u03bd\u03af\u03b1\u03c2", "units": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b5\u03c2" diff --git a/homeassistant/components/harmony/translations/sk.json b/homeassistant/components/harmony/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/harmony/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hassio/translations/el.json b/homeassistant/components/hassio/translations/el.json index cbe923f2888..91448c065c8 100644 --- a/homeassistant/components/hassio/translations/el.json +++ b/homeassistant/components/hassio/translations/el.json @@ -1,5 +1,40 @@ { "issues": { + "unhealthy": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03c0\u03af \u03c4\u03bf\u03c5 \u03c0\u03b1\u03c1\u03cc\u03bd\u03c4\u03bf\u03c2 \u03bc\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03bb\u03cc\u03b3\u03c9 {reason}. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - {reason}" + }, + "unhealthy_docker": { + "title": "\u039c\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u039a\u03b1\u03ba\u03ae \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03bf\u03c5 Docker" + }, + "unhealthy_privileged": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03c1\u03bf\u03c2 \u03c4\u03bf \u03c0\u03b1\u03c1\u03cc\u03bd \u03bc\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c0\u03c1\u03bf\u03bd\u03bf\u03bc\u03b9\u03b1\u03ba\u03ae \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7 \u03c3\u03c4\u03bf docker runtime. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u039c\u03b7 \u03c0\u03c1\u03bf\u03bd\u03bf\u03bc\u03b9\u03b1\u03ba\u03cc" + }, + "unhealthy_setup": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03c1\u03bf\u03c2 \u03c4\u03bf \u03c0\u03b1\u03c1\u03cc\u03bd \u03bc\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03b7 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03b4\u03b5\u03bd \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5. \u03a5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03bd \u03b4\u03b9\u03ac\u03c6\u03bf\u03c1\u03bf\u03b9 \u03bb\u03cc\u03b3\u03bf\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03bf\u03c5\u03c2 \u03bf\u03c0\u03bf\u03af\u03bf\u03c5\u03c2 \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c3\u03c5\u03bc\u03b2\u03b5\u03af \u03b1\u03c5\u03c4\u03cc, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0397 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5" + }, + "unhealthy_supervisor": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03c1\u03bf\u03c2 \u03c4\u03bf \u03c0\u03b1\u03c1\u03cc\u03bd \u03bc\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03b7 \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Supervisor \u03c3\u03c4\u03b7\u03bd \u03c4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03b1 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0397 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03b5\u03c0\u03cc\u03c0\u03c4\u03b7 \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5" + }, + "unhealthy_untrusted": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03c1\u03bf\u03c2 \u03c4\u03bf \u03c0\u03b1\u03c1\u03cc\u03bd \u03bc\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03b5\u03b9 \u03bc\u03b7 \u03b1\u03be\u03b9\u03cc\u03c0\u03b9\u03c3\u03c4\u03bf \u03ba\u03ce\u03b4\u03b9\u03ba\u03b1 \u03ae \u03b5\u03b9\u03ba\u03cc\u03bd\u03b5\u03c2 \u03c3\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u039c\u03b7 \u03b1\u03be\u03b9\u03cc\u03c0\u03b9\u03c3\u03c4\u03bf\u03c2 \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2" + }, + "unsupported": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03bb\u03cc\u03b3\u03c9 {reason}. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - {reason}" + }, + "unsupported_apparmor": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c4\u03bf AppArmor \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03b5\u03af \u03bb\u03b1\u03bd\u03b8\u03b1\u03c3\u03bc\u03ad\u03bd\u03b1 \u03ba\u03b1\u03b9 \u03c4\u03b1 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03b1 \u03b5\u03ba\u03c4\u03b5\u03bb\u03bf\u03cd\u03bd\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03bc\u03b7 \u03c0\u03c1\u03bf\u03c3\u03c4\u03b1\u03c4\u03b5\u03c5\u03bc\u03ad\u03bd\u03bf \u03ba\u03b1\u03b9 \u03b1\u03bd\u03b1\u03c3\u03c6\u03b1\u03bb\u03ae \u03c4\u03c1\u03cc\u03c0\u03bf. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0396\u03b7\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 AppArmor" + }, + "unsupported_cgroup_version": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03bb\u03ac\u03b8\u03bf\u03c2 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03c4\u03bf\u03c5 Docker CGroup. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c9\u03c3\u03c4\u03ae \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u0388\u03ba\u03b4\u03bf\u03c3\u03b7 CGroup" + }, "unsupported_connectivity_check": { "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c4\u03bf Home Assistant \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b4\u03b9\u03bf\u03c1\u03af\u03c3\u03b5\u03b9 \u03c0\u03cc\u03c4\u03b5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03bc\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03bf \u0394\u03b9\u03b1\u03b4\u03af\u03ba\u03c4\u03c5\u03bf. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", "title": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u039f \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c3\u03c5\u03bd\u03b4\u03b5\u03c3\u03b9\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2" diff --git a/homeassistant/components/heos/translations/sk.json b/homeassistant/components/heos/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/heos/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hlk_sw16/translations/sk.json b/homeassistant/components/hlk_sw16/translations/sk.json index 5ada995aa6e..368553a4887 100644 --- a/homeassistant/components/hlk_sw16/translations/sk.json +++ b/homeassistant/components/hlk_sw16/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/homeassistant/translations/el.json b/homeassistant/components/homeassistant/translations/el.json index ac8aedc8297..7fa0d674233 100644 --- a/homeassistant/components/homeassistant/translations/el.json +++ b/homeassistant/components/homeassistant/translations/el.json @@ -1,5 +1,9 @@ { "issues": { + "historic_currency": { + "description": "\u03a4\u03bf \u03bd\u03cc\u03bc\u03b9\u03c3\u03bc\u03b1 {currency} \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd, \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03bd\u03bf\u03bc\u03af\u03c3\u03bc\u03b1\u03c4\u03bf\u03c2.", + "title": "\u03a4\u03bf \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03bc\u03ad\u03bd\u03bf \u03bd\u03cc\u03bc\u03b9\u03c3\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd" + }, "python_version": { "description": "\u0397 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b5\u03ba\u03c4\u03ad\u03bb\u03b5\u03c3\u03b7 \u03c4\u03bf\u03c5 Home Assistant \u03c3\u03c4\u03b7\u03bd \u03c4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bc\u03b5\u03bd\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 Python {current_python_version} \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af \u03ba\u03b1\u03b9 \u03b8\u03b1 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af \u03c3\u03c4\u03bf Home Assistant {breaks_in_ha_version} . \u0391\u03bd\u03b1\u03b2\u03b1\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd Python \u03c3\u03b5 {required_python_version} \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b1\u03c0\u03bf\u03c4\u03c1\u03ad\u03c8\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03c1\u03bf\u03c6\u03ae \u03c4\u03b7\u03c2 \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03af\u03b1\u03c2 \u03c4\u03bf\u03c5 Home Assistant.", "title": "\u0397 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd Python {current_python_version} \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" diff --git a/homeassistant/components/homeassistant/translations/zh-Hant.json b/homeassistant/components/homeassistant/translations/zh-Hant.json index dc43dcaf7d3..e3227e6d440 100644 --- a/homeassistant/components/homeassistant/translations/zh-Hant.json +++ b/homeassistant/components/homeassistant/translations/zh-Hant.json @@ -3,6 +3,10 @@ "historic_currency": { "description": "\u8ca8\u5e63 {currency} \u4e0d\u518d\u4f7f\u7528\u3001\u8acb\u91cd\u65b0\u8a2d\u5b9a\u5176\u4ed6\u8ca8\u5e63\u3002", "title": "\u8a2d\u5b9a\u8ca8\u5e63\u4e0d\u518d\u4f7f\u7528" + }, + "python_version": { + "description": "Support for \u57f7\u884c Home Assistant \u76ee\u524d\u652f\u63f4\u4f7f\u7528\u4e4b Python v{current_python_version} \u7248\u5df2\u7d93\u505c\u6b62\u652f\u63f4\u3001\u5373\u5c07\u65bc Home Assistant {breaks_in_ha_version} \u7248\u9032\u884c\u79fb\u9664\u3002\u8acb\u66f4\u65b0 Python \u81f3 v{required_python_version} \u7248\u4ee5\u907f\u514d Home Assistant \u7121\u6cd5\u57f7\u884c\u3002", + "title": "Python {current_python_version} \u7248\u652f\u63f4\u5373\u5c07\u79fb\u9664" } }, "system_health": { diff --git a/homeassistant/components/homeassistant_yellow/translations/zh-Hant.json b/homeassistant/components/homeassistant_yellow/translations/zh-Hant.json new file mode 100644 index 00000000000..9b0fffd7464 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/zh-Hant.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "\u53d6\u5f97 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u8cc7\u8a0a\u5931\u6557\u3002", + "addon_install_failed": "\u5b89\u88dd Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", + "addon_set_config_failed": "\u8a2d\u5b9a Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", + "addon_start_failed": "\u555f\u52d5 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", + "not_hassio": "\u786c\u9ad4\u9078\u9805\u50c5\u80fd\u65bc HassOS \u5b89\u88dd\u6a21\u5f0f\u9032\u884c\u8a2d\u5b9a\u3002" + }, + "error": { + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "progress": { + "install_addon": "\u8acb\u7a0d\u7b49 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5b89\u88dd\u5b8c\u6210\u3002\u53ef\u80fd\u9700\u8981\u5e7e\u5206\u9418\u3002", + "start_addon": "\u8acb\u7a0d\u7b49 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u555f\u59cb\u5b8c\u6210\uff0c\u53ef\u80fd\u6703\u9700\u8981\u5e7e\u5206\u9418\u3002" + }, + "step": { + "addon_installed_other_device": { + "title": "Multiprotocol \u652f\u63f4\u5df2\u7d93\u65bc\u5176\u4ed6\u88dd\u7f6e\u958b\u555f" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u555f\u7528 Multiprotocol \u652f\u63f4" + }, + "description": "\u7576\u555f\u7528 Multiprotocol \u652f\u63f4\u6642\u3001Home Assistant Yellow \u7684 IEEE 802.15.4 radio \u53ef\u540c\u6642\u4f5c\u70ba Zigbee \u8207 Thread \uff08\u7528\u65bc Matter\uff09\u4f7f\u7528\u3002\u6ce8\u610f\uff1a\u76ee\u524d\u70ba\u5be6\u9a57\u6027\u529f\u80fd\u3002", + "title": "\u65bc IEEE 802.15.4 radio \u4e0a\u555f\u7528 Multiprotocol \u652f\u63f4" + }, + "install_addon": { + "title": "Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5df2\u555f\u7528\u3002" + }, + "show_revert_guide": { + "description": "\u5047\u5982\u60f3\u8b8a\u66f4\u70ba\u50c5 Zigbee \u97cc\u9ad4\u3001\u8acb\u5b8c\u6210\u4ee5\u4e0b\u624b\u52d5\u6b65\u9a5f\uff1a\n\n * \u79fb\u9664 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\n\n * \u5237\u5165\u50c5 Zigbee \u97cc\u9ad4\uff0c\u8acb\u53c3\u95b1\u64cd\u4f5c\u6307\u5f15 https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually\u3002\n\n * \u91cd\u65b0\u8a2d\u5b9a ZHA \u4ee5\u9077\u79fb\u8a2d\u5b9a\u81f3\u91cd\u65b0\u5237\u5165\u7684 Radio", + "title": "\u88dd\u7f6e\u4e4b Multiprotocol \u652f\u63f4\u5df2\u958b\u555f" + }, + "start_addon": { + "title": "Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u555f\u7528\u4e2d\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hue/translations/sk.json b/homeassistant/components/hue/translations/sk.json index 10605f27ce1..496de2d99b2 100644 --- a/homeassistant/components/hue/translations/sk.json +++ b/homeassistant/components/hue/translations/sk.json @@ -14,10 +14,18 @@ }, "step": { "init": { + "data": { + "host": "Hostite\u013e" + }, "title": "Vyberte Hue bridge" }, "link": { "description": "Pre registr\u00e1ciu Philips Hue s Home Assistant stla\u010dte tla\u010didlo na Philips Hue bridge.\n\n![Location of button on bridge](/static/images/config_philips_hue.jpg)" + }, + "manual": { + "data": { + "host": "Hostite\u013e" + } } } }, diff --git a/homeassistant/components/humidifier/translations/is.json b/homeassistant/components/humidifier/translations/is.json new file mode 100644 index 00000000000..5009de10c9b --- /dev/null +++ b/homeassistant/components/humidifier/translations/is.json @@ -0,0 +1,8 @@ +{ + "device_automation": { + "condition_type": { + "is_off": "{entity_name} er sl\u00f6kkt", + "is_on": "{entity_name} er \u00ed gangi" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hunterdouglas_powerview/translations/sk.json b/homeassistant/components/hunterdouglas_powerview/translations/sk.json new file mode 100644 index 00000000000..8784ee07ad5 --- /dev/null +++ b/homeassistant/components/hunterdouglas_powerview/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "flow_title": "{name} ({host})", + "step": { + "user": { + "data": { + "host": "IP adresa" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hvv_departures/translations/sk.json b/homeassistant/components/hvv_departures/translations/sk.json index 5ada995aa6e..368553a4887 100644 --- a/homeassistant/components/hvv_departures/translations/sk.json +++ b/homeassistant/components/hvv_departures/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/hyperion/translations/sk.json b/homeassistant/components/hyperion/translations/sk.json index a8223b5b2e3..41c1548a85d 100644 --- a/homeassistant/components/hyperion/translations/sk.json +++ b/homeassistant/components/hyperion/translations/sk.json @@ -7,6 +7,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/ialarm/translations/sk.json b/homeassistant/components/ialarm/translations/sk.json index 892b8b2cd91..33d93b62475 100644 --- a/homeassistant/components/ialarm/translations/sk.json +++ b/homeassistant/components/ialarm/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/insteon/translations/sk.json b/homeassistant/components/insteon/translations/sk.json index b3711644c03..704c6b2d86c 100644 --- a/homeassistant/components/insteon/translations/sk.json +++ b/homeassistant/components/insteon/translations/sk.json @@ -6,11 +6,13 @@ "step": { "hubv1": { "data": { + "host": "IP adresa", "port": "Port" } }, "hubv2": { "data": { + "host": "IP adresa", "port": "Port" } } @@ -20,6 +22,7 @@ "step": { "change_hub_config": { "data": { + "host": "IP adresa", "port": "Port" } } diff --git a/homeassistant/components/intellifire/translations/sk.json b/homeassistant/components/intellifire/translations/sk.json new file mode 100644 index 00000000000..f70e03e3c83 --- /dev/null +++ b/homeassistant/components/intellifire/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "flow_title": "{serial} ({host})", + "step": { + "pick_device": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/iotawatt/translations/sk.json b/homeassistant/components/iotawatt/translations/sk.json index 5ada995aa6e..368553a4887 100644 --- a/homeassistant/components/iotawatt/translations/sk.json +++ b/homeassistant/components/iotawatt/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/ipp/translations/sk.json b/homeassistant/components/ipp/translations/sk.json index 892b8b2cd91..33d93b62475 100644 --- a/homeassistant/components/ipp/translations/sk.json +++ b/homeassistant/components/ipp/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/isy994/translations/sk.json b/homeassistant/components/isy994/translations/sk.json index 5ada995aa6e..1e1ce1bb39f 100644 --- a/homeassistant/components/isy994/translations/sk.json +++ b/homeassistant/components/isy994/translations/sk.json @@ -2,6 +2,14 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "flow_title": "{name} ({host})", + "step": { + "user": { + "data": { + "host": "URL" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/kaleidescape/translations/sk.json b/homeassistant/components/kaleidescape/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/kaleidescape/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/keenetic_ndms2/translations/sk.json b/homeassistant/components/keenetic_ndms2/translations/sk.json index 892b8b2cd91..430bfe3ea0d 100644 --- a/homeassistant/components/keenetic_ndms2/translations/sk.json +++ b/homeassistant/components/keenetic_ndms2/translations/sk.json @@ -1,8 +1,10 @@ { "config": { + "flow_title": "{name} ({host})", "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/kmtronic/translations/sk.json b/homeassistant/components/kmtronic/translations/sk.json index 5ada995aa6e..368553a4887 100644 --- a/homeassistant/components/kmtronic/translations/sk.json +++ b/homeassistant/components/kmtronic/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/knx/translations/el.json b/homeassistant/components/knx/translations/el.json index 20e4a2fe140..fc3af662be2 100644 --- a/homeassistant/components/knx/translations/el.json +++ b/homeassistant/components/knx/translations/el.json @@ -9,20 +9,30 @@ "file_not_found": "\u03a4\u03bf \u03ba\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae config/.storage/knx/", "invalid_individual_address": "\u0397 \u03c4\u03b9\u03bc\u03ae \u03b4\u03b5\u03bd \u03c4\u03b1\u03b9\u03c1\u03b9\u03ac\u03b6\u03b5\u03b9 \u03bc\u03b5 \u03c4\u03bf \u03bc\u03bf\u03c4\u03af\u03b2\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03bc\u03b5\u03bc\u03bf\u03bd\u03c9\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX.\n \"area.line.device\"", "invalid_ip_address": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IPv4.", - "invalid_signature": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03ac\u03c6\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 knxkeys \u03b5\u03af\u03bd\u03b1\u03b9 \u03bb\u03ac\u03b8\u03bf\u03c2." + "invalid_signature": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03ac\u03c6\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 knxkeys \u03b5\u03af\u03bd\u03b1\u03b9 \u03bb\u03ac\u03b8\u03bf\u03c2.", + "no_router_discovered": "\u0394\u03b5\u03bd \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b5 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae\u03c2 KNXnet/IP \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf.", + "no_tunnel_discovered": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03c2 \u03bf \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03cc \u03c3\u03b1\u03c2." }, "step": { + "connection_type": { + "data": { + "connection_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 KNX" + }, + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03cd\u03c0\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c0\u03bf\u03c5 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03bf\u03c5\u03bc\u03b5 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03ae \u03c3\u03b1\u03c2 KNX.\n AUTOMATIC - \u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c6\u03c1\u03bf\u03bd\u03c4\u03af\u03b6\u03b5\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03bd\u03b4\u03b5\u03c3\u03b9\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1 \u03bc\u03b5 \u03c4\u03bf KNX Bus \u03c3\u03b1\u03c2 \u03b5\u03ba\u03c4\u03b5\u03bb\u03ce\u03bd\u03c4\u03b1\u03c2 \u03bc\u03b9\u03b1 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7 \u03c0\u03cd\u03bb\u03b7\u03c2.\n TUNNELING - \u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b8\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03bc\u03b5 \u03c4\u03bf \u03b4\u03af\u03b1\u03c5\u03bb\u03bf KNX \u03bc\u03ad\u03c3\u03c9 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2.\n \u0394\u03a1\u039f\u039c\u039f\u039b\u039f\u0393\u0397\u03a3\u0397 - \u0397 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b8\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03bc\u03b5 \u03c4\u03bf \u03b4\u03af\u03b1\u03c5\u03bb\u03bf KNX \u03bc\u03ad\u03c3\u03c9 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7\u03c2." + }, "manual_tunnel": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", "local_ip": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae IP \u03c4\u03bf\u03c5 Home Assistant (\u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03b5\u03bd\u03ae \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7)", "port": "\u0398\u03cd\u03c1\u03b1", + "route_back": "\u03a0\u03af\u03c3\u03c9 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae / \u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 NAT", "tunneling_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" }, "data_description": { "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP.", "local_ip": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7.", - "port": "\u0398\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP." + "port": "\u0398\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP.", + "route_back": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03b5\u03ac\u03bd \u03bf \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae\u03c2 \u03c3\u03b1\u03c2 KNXnet/IP tunneling \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c0\u03af\u03c3\u03c9 \u03b1\u03c0\u03cc \u03c4\u03bf NAT. \u0399\u03c3\u03c7\u03cd\u03b5\u03b9 \u03bc\u03cc\u03bd\u03bf \u03b3\u03b9\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03b9\u03c2 UDP." }, "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03ac\u03c2 \u03c3\u03b1\u03c2." }, @@ -63,11 +73,25 @@ }, "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 IP secure." }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", + "user_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", + "user_password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03a7\u03c1\u03ae\u03c3\u03c4\u03b7" + }, + "data_description": { + "device_authentication": "\u0391\u03c5\u03c4\u03cc \u03bf\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf\u03bd \u03c0\u03af\u03bd\u03b1\u03ba\u03b1 \u00abIP\u00bb \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae\u03c2 \u03c3\u03c4\u03bf ETS.", + "user_id": "\u0391\u03c5\u03c4\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03c7\u03bd\u03ac \u03bf \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 +1. \u0386\u03c1\u03b1 \u03c4\u03bf \"Tunnel 2\" \u03b8\u03b1 \u03ad\u03c7\u03b5\u03b9 User-ID \"3\".", + "user_password": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af \u03c3\u03c4\u03bf\u03bd \u03c0\u03af\u03bd\u03b1\u03ba\u03b1 \u00ab\u0399\u03b4\u03b9\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\u00bb \u03c4\u03b7\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 \u03c3\u03c4\u03bf ETS." + }, + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 IP \u03c3\u03b1\u03c2." + }, "secure_tunneling": { "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03ce\u03c2 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf IP Secure.", "menu_options": { "secure_knxkeys": "\u0394\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 IP secure", - "secure_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 IP secure \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b1" + "secure_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 IP secure \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b1", + "secure_tunnel_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ce\u03bd \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP \u03bc\u03b5 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf \u03c4\u03c1\u03cc\u03c0\u03bf" } }, "tunnel": { diff --git a/homeassistant/components/knx/translations/sk.json b/homeassistant/components/knx/translations/sk.json index 347dbdfbcef..efcd2371649 100644 --- a/homeassistant/components/knx/translations/sk.json +++ b/homeassistant/components/knx/translations/sk.json @@ -6,6 +6,7 @@ "step": { "manual_tunnel": { "data": { + "host": "Hostite\u013e", "port": "Port" } }, @@ -23,8 +24,14 @@ "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" } }, + "manual_tunnel": { + "data": { + "host": "Hostite\u013e" + } + }, "tunnel": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/kodi/translations/sk.json b/homeassistant/components/kodi/translations/sk.json index ab39cbe9c5e..7f66f32dd4e 100644 --- a/homeassistant/components/kodi/translations/sk.json +++ b/homeassistant/components/kodi/translations/sk.json @@ -15,6 +15,7 @@ }, "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } }, diff --git a/homeassistant/components/konnected/translations/sk.json b/homeassistant/components/konnected/translations/sk.json index 51eaba460d8..288d7536945 100644 --- a/homeassistant/components/konnected/translations/sk.json +++ b/homeassistant/components/konnected/translations/sk.json @@ -6,6 +6,7 @@ "step": { "user": { "data": { + "host": "IP adresa", "port": "Port" } } diff --git a/homeassistant/components/kostal_plenticore/translations/sk.json b/homeassistant/components/kostal_plenticore/translations/sk.json index 5ada995aa6e..368553a4887 100644 --- a/homeassistant/components/kostal_plenticore/translations/sk.json +++ b/homeassistant/components/kostal_plenticore/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/lametric/translations/el.json b/homeassistant/components/lametric/translations/el.json index 8733a19e690..f653a0b6af4 100644 --- a/homeassistant/components/lametric/translations/el.json +++ b/homeassistant/components/lametric/translations/el.json @@ -8,6 +8,7 @@ "missing_configuration": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 LaMetric \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7.", "no_devices": "\u039f \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 LaMetric", "no_url_available": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL. \u0393\u03b9\u03b1 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1, [\u03b5\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b2\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1\u03c2] ( {docs_url} )", + "reauth_device_not_found": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c0\u03bf\u03c5 \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03bb\u03ad\u03b3\u03be\u03b5\u03c4\u03b5 \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03c4\u03b7\u03bd \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b4\u03b5\u03bd \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c3\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc LaMetric", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "error": { diff --git a/homeassistant/components/lametric/translations/select.el.json b/homeassistant/components/lametric/translations/select.el.json new file mode 100644 index 00000000000..f51845f4c45 --- /dev/null +++ b/homeassistant/components/lametric/translations/select.el.json @@ -0,0 +1,8 @@ +{ + "state": { + "lametric__brightness_mode": { + "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "manual": "\u03a7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03bf" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lg_soundbar/translations/sk.json b/homeassistant/components/lg_soundbar/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/lg_soundbar/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/life360/translations/bg.json b/homeassistant/components/life360/translations/bg.json index 22fad245c5d..f2f11f3d4be 100644 --- a/homeassistant/components/life360/translations/bg.json +++ b/homeassistant/components/life360/translations/bg.json @@ -36,7 +36,7 @@ "init": { "data": { "driving_speed": "\u0421\u043a\u043e\u0440\u043e\u0441\u0442 \u043d\u0430 \u0448\u043e\u0444\u0438\u0440\u0430\u043d\u0435", - "max_gps_accuracy": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u043d\u0430 GPS \u0442\u043e\u0447\u043d\u043e\u0441\u0442 (\u043c\u0435\u0442\u0440\u0438)" + "max_gps_accuracy": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u043d\u0430 \u0442\u043e\u0447\u043d\u043e\u0441\u0442 \u043d\u0430 GPS (\u043c\u0435\u0442\u0440\u0438)" }, "title": "\u041e\u043f\u0446\u0438\u0438 \u043d\u0430 \u0430\u043a\u0430\u0443\u043d\u0442\u0430" } diff --git a/homeassistant/components/light/translations/is.json b/homeassistant/components/light/translations/is.json index 365502c2032..9e4ab1e453f 100644 --- a/homeassistant/components/light/translations/is.json +++ b/homeassistant/components/light/translations/is.json @@ -1,4 +1,10 @@ { + "device_automation": { + "condition_type": { + "is_off": "{entity_name} er sl\u00f6kkt", + "is_on": "{entity_name} er kveikt" + } + }, "state": { "_": { "off": "Sl\u00f6kkt", diff --git a/homeassistant/components/livisi/translations/el.json b/homeassistant/components/livisi/translations/el.json new file mode 100644 index 00000000000..1260983be3c --- /dev/null +++ b/homeassistant/components/livisi/translations/el.json @@ -0,0 +1,13 @@ +{ + "config": { + "error": { + "wrong_ip_address": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03b5\u03af\u03bd\u03b1\u03b9 \u03bb\u03b1\u03bd\u03b8\u03b1\u03c3\u03bc\u03ad\u03bd\u03b7 \u03ae \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03c4\u03bf\u03c0\u03b9\u03ba\u03ae \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7 \u03c3\u03c4\u03bf SHC.", + "wrong_password": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03bb\u03b1\u03bd\u03b8\u03b1\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2." + }, + "step": { + "user": { + "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03ba\u03b1\u03b9 \u03c4\u03bf\u03bd (\u03c4\u03bf\u03c0\u03b9\u03ba\u03cc) \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 SHC." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/sk.json b/homeassistant/components/livisi/translations/sk.json new file mode 100644 index 00000000000..5656d8635a0 --- /dev/null +++ b/homeassistant/components/livisi/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "IP adresa" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lock/translations/is.json b/homeassistant/components/lock/translations/is.json index e1960ee5888..4de60d6af72 100644 --- a/homeassistant/components/lock/translations/is.json +++ b/homeassistant/components/lock/translations/is.json @@ -1,4 +1,10 @@ { + "device_automation": { + "condition_type": { + "is_locked": "{entity_name} er l\u00e6st", + "is_unlocked": "{entity_name} er \u00f3l\u00e6st" + } + }, "state": { "_": { "locked": "L\u00e6st", diff --git a/homeassistant/components/lutron_caseta/translations/el.json b/homeassistant/components/lutron_caseta/translations/el.json index f0c1ec35450..87e52ca850f 100644 --- a/homeassistant/components/lutron_caseta/translations/el.json +++ b/homeassistant/components/lutron_caseta/translations/el.json @@ -33,6 +33,9 @@ "button_2": "\u0394\u03b5\u03cd\u03c4\u03b5\u03c1\u03bf \u03ba\u03bf\u03c5\u03bc\u03c0\u03af", "button_3": "\u03a4\u03c1\u03af\u03c4\u03bf \u03ba\u03bf\u03c5\u03bc\u03c0\u03af", "button_4": "\u03a4\u03ad\u03c4\u03b1\u03c1\u03c4\u03bf \u03ba\u03bf\u03c5\u03bc\u03c0\u03af", + "button_5": "\u03a0\u03ad\u03bc\u03c0\u03c4\u03bf \u03ba\u03bf\u03c5\u03bc\u03c0\u03af", + "button_6": "\u0388\u03ba\u03c4\u03bf \u03ba\u03bf\u03c5\u03bc\u03c0\u03af", + "button_7": "\u0388\u03b2\u03b4\u03bf\u03bc\u03bf \u03ba\u03bf\u03c5\u03bc\u03c0\u03af", "close_1": "\u039a\u03bb\u03b5\u03af\u03c3\u03b9\u03bc\u03bf 1", "close_2": "\u039a\u03bb\u03b5\u03af\u03c3\u03b9\u03bc\u03bf 2", "close_3": "\u039a\u03bb\u03b5\u03af\u03c3\u03b9\u03bc\u03bf 3", diff --git a/homeassistant/components/lutron_caseta/translations/sk.json b/homeassistant/components/lutron_caseta/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/lutron_caseta/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/media_player/translations/is.json b/homeassistant/components/media_player/translations/is.json index 276e3428171..98148d50a0a 100644 --- a/homeassistant/components/media_player/translations/is.json +++ b/homeassistant/components/media_player/translations/is.json @@ -1,4 +1,14 @@ { + "device_automation": { + "condition_type": { + "is_buffering": "{entity_name} er a\u00f0 hla\u00f0a bi\u00f0minni", + "is_idle": "{entity_name} er a\u00f0ger\u00f0alaus", + "is_off": "{entity_name} er sl\u00f6kkt", + "is_on": "{entity_name} er \u00ed gangi", + "is_paused": "{entity_name} er \u00ed bi\u00f0", + "is_playing": "{entity_name} er a\u00f0 spila" + } + }, "state": { "_": { "idle": "A\u00f0ger\u00f0alaus", diff --git a/homeassistant/components/mikrotik/translations/sk.json b/homeassistant/components/mikrotik/translations/sk.json index 6f753f20966..e7eb9363f01 100644 --- a/homeassistant/components/mikrotik/translations/sk.json +++ b/homeassistant/components/mikrotik/translations/sk.json @@ -6,6 +6,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov", "port": "Port" } diff --git a/homeassistant/components/minecraft_server/translations/sk.json b/homeassistant/components/minecraft_server/translations/sk.json index af15f92c2f2..965b3bb8919 100644 --- a/homeassistant/components/minecraft_server/translations/sk.json +++ b/homeassistant/components/minecraft_server/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov" } } diff --git a/homeassistant/components/modern_forms/translations/sk.json b/homeassistant/components/modern_forms/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/modern_forms/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/moehlenhoff_alpha2/translations/sk.json b/homeassistant/components/moehlenhoff_alpha2/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/moehlenhoff_alpha2/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/motion_blinds/translations/sk.json b/homeassistant/components/motion_blinds/translations/sk.json index 6ea9c064f16..302a4868415 100644 --- a/homeassistant/components/motion_blinds/translations/sk.json +++ b/homeassistant/components/motion_blinds/translations/sk.json @@ -8,6 +8,11 @@ "data": { "api_key": "API k\u013e\u00fa\u010d" } + }, + "user": { + "data": { + "host": "IP adresa" + } } } } diff --git a/homeassistant/components/mqtt/translations/el.json b/homeassistant/components/mqtt/translations/el.json index 834d2045b77..2f17db20882 100644 --- a/homeassistant/components/mqtt/translations/el.json +++ b/homeassistant/components/mqtt/translations/el.json @@ -5,6 +5,13 @@ "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." }, "error": { + "bad_birth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b8\u03ad\u03bc\u03b1 \u03b3\u03ad\u03bd\u03bd\u03b7\u03c3\u03b7\u03c2", + "bad_certificate": "\u03a4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc CA \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf", + "bad_client_cert": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7, \u03b2\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03c0\u03b1\u03c1\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03bc\u03b5 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 PEM", + "bad_client_cert_key": "\u03a4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7 \u03ba\u03b1\u03b9 \u03c4\u03bf \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03cc \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b6\u03b5\u03cd\u03b3\u03bf\u03c2", + "bad_client_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03cc \u03ba\u03bb\u03b5\u03b9\u03b4\u03af, \u03b2\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03bc\u03b5 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 PEM \u03c0\u03b1\u03c1\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03c7\u03c9\u03c1\u03af\u03c2 \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "bad_discovery_prefix": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7\u03c2", + "bad_will": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b8\u03ad\u03bc\u03b1", "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "invalid_inclusion": "\u03a4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7 \u03ba\u03b1\u03b9 \u03c4\u03bf \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03cc \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03bf\u03cd\u03bd \u03bc\u03b1\u03b6\u03af" }, @@ -95,6 +102,7 @@ "birth_retain": "\u0394\u03b9\u03b1\u03c4\u03ae\u03c1\u03b7\u03c3\u03b7 \u03bc\u03b7\u03bd\u03cd\u03bc\u03b1\u03c4\u03bf\u03c2 birth", "birth_topic": "\u0398\u03ad\u03bc\u03b1 \u03bc\u03b7\u03bd\u03cd\u03bc\u03b1\u03c4\u03bf\u03c2 birth", "discovery": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7\u03c2", + "discovery_prefix": "\u03a0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7\u03c2", "will_enable": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03bc\u03b7\u03bd\u03cd\u03bc\u03b1\u03c4\u03bf\u03c2 will", "will_payload": "\u03a9\u03c6\u03ad\u03bb\u03b9\u03bc\u03bf \u03c6\u03bf\u03c1\u03c4\u03af\u03bf \u03bc\u03b7\u03bd\u03cd\u03bc\u03b1\u03c4\u03bf\u03c2 will", "will_qos": "QoS \u03bc\u03b7\u03bd\u03cd\u03bc\u03b1\u03c4\u03bf\u03c2 will", diff --git a/homeassistant/components/mutesync/translations/sk.json b/homeassistant/components/mutesync/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/mutesync/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nam/translations/sk.json b/homeassistant/components/nam/translations/sk.json index 71a7aea5018..c037dc135ae 100644 --- a/homeassistant/components/nam/translations/sk.json +++ b/homeassistant/components/nam/translations/sk.json @@ -5,6 +5,14 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "flow_title": "{host}", + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/nanoleaf/translations/sk.json b/homeassistant/components/nanoleaf/translations/sk.json index c34ad96714a..8717df47273 100644 --- a/homeassistant/components/nanoleaf/translations/sk.json +++ b/homeassistant/components/nanoleaf/translations/sk.json @@ -2,6 +2,13 @@ "config": { "abort": { "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } }, "device_automation": { diff --git a/homeassistant/components/netgear/translations/sk.json b/homeassistant/components/netgear/translations/sk.json new file mode 100644 index 00000000000..5d61ddd2e3b --- /dev/null +++ b/homeassistant/components/netgear/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e (Volite\u013en\u00e9)" + }, + "description": "Predvolen\u00fd hostite\u013e: {host}\nPredvolen\u00e9 pou\u017e\u00edvate\u013esk\u00e9 meno: {username}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nfandroidtv/translations/sk.json b/homeassistant/components/nfandroidtv/translations/sk.json index af15f92c2f2..965b3bb8919 100644 --- a/homeassistant/components/nfandroidtv/translations/sk.json +++ b/homeassistant/components/nfandroidtv/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov" } } diff --git a/homeassistant/components/nibe_heatpump/translations/el.json b/homeassistant/components/nibe_heatpump/translations/el.json index cdb5c77e15d..aa8a2008cbc 100644 --- a/homeassistant/components/nibe_heatpump/translations/el.json +++ b/homeassistant/components/nibe_heatpump/translations/el.json @@ -9,20 +9,54 @@ "model": "\u03a4\u03bf \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bc\u03bf\u03bd\u03c4\u03ad\u03bb\u03bf \u03b4\u03b5\u03bd \u03c6\u03b1\u03af\u03bd\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03b9 modbus40", "read": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03c3\u03c4\u03bf \u03b1\u03af\u03c4\u03b7\u03bc\u03b1 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03b1\u03bd\u03c4\u03bb\u03af\u03b1. \u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \"\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b8\u03cd\u03c1\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2\" \u03ae \u03c4\u03b7\u03bd \"\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP\".", "unknown": "\u0391\u03c0\u03c1\u03bf\u03c3\u03b4\u03cc\u03ba\u03b7\u03c4\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1", + "url": "\u0397 \u03ba\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ba\u03b1\u03bb\u03ac \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03bc\u03ad\u03bd\u03b7 \u03bf\u03cd\u03c4\u03b5 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9", "write": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03c3\u03c4\u03bf \u03b1\u03af\u03c4\u03b7\u03bc\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03c3\u03c4\u03b7\u03bd \u03b1\u03bd\u03c4\u03bb\u03af\u03b1. \u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \"\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b8\u03cd\u03c1\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2\" \u03ae \u03c4\u03b7\u03bd \"\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP\"." }, "step": { - "nibegw": { + "modbus": { + "data": { + "modbus_unit": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1\u03c2 Modbus", + "modbus_url": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL Modbus", + "model": "\u039c\u03bf\u03bd\u03c4\u03ad\u03bb\u03bf \u03b1\u03bd\u03c4\u03bb\u03af\u03b1\u03c2 \u03b8\u03b5\u03c1\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, "data_description": { - "listening_port": "\u0397 \u03c4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2, \u03c3\u03c4\u03b7\u03bd \u03bf\u03c0\u03bf\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af \u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 NibeGW \u03b3\u03b9\u03b1 \u03b1\u03c0\u03bf\u03c3\u03c4\u03bf\u03bb\u03ae \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd." + "modbus_unit": "\u0391\u03bd\u03b1\u03b3\u03bd\u03ce\u03c1\u03b9\u03c3\u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03bd\u03c4\u03bb\u03af\u03b1 \u03b8\u03b5\u03c1\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2. \u03a3\u03c5\u03bd\u03ae\u03b8\u03c9\u03c2 \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03bc\u03b5\u03af\u03bd\u03b5\u03b9 \u03c3\u03c4\u03bf 0.", + "modbus_url": "URL Modbus \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03b3\u03c1\u03ac\u03c6\u03b5\u03b9 \u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03bc\u03b5 \u03c4\u03b7\u03bd \u03b1\u03bd\u03c4\u03bb\u03af\u03b1 \u03b8\u03b5\u03c1\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae \u03c4\u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 MODBUS40. \u0398\u03b1 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c4\u03b7 \u03c6\u03cc\u03c1\u03bc\u03b1:\n - `tcp://[HOST]:[PORT]` \u03b3\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 Modbus TCP\n - `serial://[LOCAL DEVICE]` \u03b3\u03b9\u03b1 \u03c4\u03bf\u03c0\u03b9\u03ba\u03ae \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 Modbus RTU\n - `rfc2217://[HOST]:[PORT]` \u03b3\u03b9\u03b1 \u03b1\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 Modbus RTU \u03c0\u03bf\u03c5 \u03b2\u03b1\u03c3\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03b5 telnet." } }, + "nibegw": { + "data": { + "ip_address": "\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7", + "listening_port": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03b1\u03ba\u03c1\u03cc\u03b1\u03c3\u03b7\u03c2", + "model": "\u039c\u03bf\u03bd\u03c4\u03ad\u03bb\u03bf \u03b1\u03bd\u03c4\u03bb\u03af\u03b1\u03c2 \u03b8\u03b5\u03c1\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "remote_read_port": "\u0398\u03cd\u03c1\u03b1 \u03b1\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7\u03c2 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2", + "remote_write_port": "\u0398\u03cd\u03c1\u03b1 \u03b1\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7\u03c2 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2" + }, + "data_description": { + "ip_address": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1\u03c2 NibeGW. \u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b8\u03b1 \u03ad\u03c0\u03c1\u03b5\u03c0\u03b5 \u03bd\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af \u03bc\u03b5 \u03c3\u03c4\u03b1\u03c4\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7.", + "listening_port": "\u0397 \u03c4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2, \u03c3\u03c4\u03b7\u03bd \u03bf\u03c0\u03bf\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af \u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 NibeGW \u03b3\u03b9\u03b1 \u03b1\u03c0\u03bf\u03c3\u03c4\u03bf\u03bb\u03ae \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd.", + "remote_read_port": "\u0397 \u03b8\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1\u03c2 NibeGW \u03b1\u03ba\u03bf\u03cd\u03b5\u03b9 \u03b3\u03b9\u03b1 \u03b1\u03b9\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2.", + "remote_write_port": "\u0397 \u03b8\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1\u03c2 NibeGW \u03b1\u03ba\u03bf\u03cd\u03b5\u03b9 \u03b3\u03b9\u03b1 \u03b1\u03b9\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2." + }, + "description": "\u03a0\u03c1\u03b9\u03bd \u03b5\u03c0\u03b9\u03c7\u03b5\u03b9\u03c1\u03ae\u03c3\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7, \u03b2\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9:\n - \u0397 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 NibeGW \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03b7 \u03bc\u03b5 \u03b1\u03bd\u03c4\u03bb\u03af\u03b1 \u03b8\u03b5\u03c1\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2.\n - \u03a4\u03bf \u03b5\u03be\u03ac\u03c1\u03c4\u03b7\u03bc\u03b1 MODBUS40 \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b1\u03bd\u03c4\u03bb\u03af\u03b1\u03c2 \u03b8\u03b5\u03c1\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2.\n - \u0397 \u03b1\u03bd\u03c4\u03bb\u03af\u03b1 \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c4\u03b5\u03b8\u03b5\u03af \u03c3\u03b5 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c3\u03c5\u03bd\u03b1\u03b3\u03b5\u03c1\u03bc\u03bf\u03cd \u03b3\u03b9\u03b1 \u03ad\u03bb\u03bb\u03b5\u03b9\u03c8\u03b7 \u03b5\u03be\u03b1\u03c1\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2 MODBUS40." + }, "user": { "data": { "ip_address": "\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP", "listening_port": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03b1\u03ba\u03c1\u03cc\u03b1\u03c3\u03b7\u03c2", "remote_read_port": "\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b8\u03cd\u03c1\u03b1 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2", "remote_write_port": "\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b8\u03cd\u03c1\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2" + }, + "data_description": { + "ip_address": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1\u03c2 NibeGW. \u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b8\u03b1 \u03ad\u03c0\u03c1\u03b5\u03c0\u03b5 \u03bd\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af \u03bc\u03b5 \u03c3\u03c4\u03b1\u03c4\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7.", + "listening_port": "\u0397 \u03c4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2, \u03c3\u03c4\u03b7\u03bd \u03bf\u03c0\u03bf\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af \u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 NibeGW \u03b3\u03b9\u03b1 \u03b1\u03c0\u03bf\u03c3\u03c4\u03bf\u03bb\u03ae \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd.", + "remote_read_port": "\u0397 \u03b8\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1\u03c2 NibeGW \u03b1\u03ba\u03bf\u03cd\u03b5\u03b9 \u03b3\u03b9\u03b1 \u03b1\u03b9\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2.", + "remote_write_port": "\u0397 \u03b8\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1\u03c2 NibeGW \u03b1\u03ba\u03bf\u03cd\u03b5\u03b9 \u03b3\u03b9\u03b1 \u03b1\u03b9\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2." + }, + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03bc\u03ad\u03b8\u03bf\u03b4\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03bc\u03b5 \u03c4\u03b7\u03bd \u03b1\u03bd\u03c4\u03bb\u03af\u03b1 \u03c3\u03b1\u03c2. \u0393\u03b5\u03bd\u03b9\u03ba\u03ac, \u03bf\u03b9 \u03b1\u03bd\u03c4\u03bb\u03af\u03b5\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03b5\u03b9\u03c1\u03ac\u03c2 F \u03b1\u03c0\u03b1\u03b9\u03c4\u03bf\u03cd\u03bd \u03c0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03c3\u03bc\u03ad\u03bd\u03bf \u03b5\u03be\u03ac\u03c1\u03c4\u03b7\u03bc\u03b1 NibeGW, \u03b5\u03bd\u03ce \u03bf\u03b9 \u03b1\u03bd\u03c4\u03bb\u03af\u03b5\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03b5\u03b9\u03c1\u03ac\u03c2 S \u03ad\u03c7\u03bf\u03c5\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03b1\u03c4\u03c9\u03bc\u03ad\u03bd\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 Modbus.", + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" } } } diff --git a/homeassistant/components/nmap_tracker/translations/sk.json b/homeassistant/components/nmap_tracker/translations/sk.json new file mode 100644 index 00000000000..c255a357016 --- /dev/null +++ b/homeassistant/components/nmap_tracker/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "error": { + "invalid_hosts": "Neplatn\u00ed hostitelia" + } + }, + "options": { + "error": { + "invalid_hosts": "Neplatn\u00ed hostitelia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nuki/translations/sk.json b/homeassistant/components/nuki/translations/sk.json index 16e76236805..9b0a7b1efd2 100644 --- a/homeassistant/components/nuki/translations/sk.json +++ b/homeassistant/components/nuki/translations/sk.json @@ -14,6 +14,7 @@ }, "user": { "data": { + "host": "Hostite\u013e", "port": "Port", "token": "Pr\u00edstupov\u00fd token" } diff --git a/homeassistant/components/nut/translations/sk.json b/homeassistant/components/nut/translations/sk.json index d00d818716f..974b12f60d5 100644 --- a/homeassistant/components/nut/translations/sk.json +++ b/homeassistant/components/nut/translations/sk.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "host": "Hostite\u013e", "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" diff --git a/homeassistant/components/nzbget/translations/sk.json b/homeassistant/components/nzbget/translations/sk.json index 39d2e182c40..1c96c63ac91 100644 --- a/homeassistant/components/nzbget/translations/sk.json +++ b/homeassistant/components/nzbget/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov", "port": "Port" } diff --git a/homeassistant/components/octoprint/translations/sk.json b/homeassistant/components/octoprint/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/octoprint/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/onewire/translations/sk.json b/homeassistant/components/onewire/translations/sk.json index 8b2a2f7343b..fc635f2c709 100644 --- a/homeassistant/components/onewire/translations/sk.json +++ b/homeassistant/components/onewire/translations/sk.json @@ -8,6 +8,9 @@ }, "step": { "user": { + "data": { + "host": "Hostite\u013e" + }, "title": "Nastavenie 1-Wire" } } diff --git a/homeassistant/components/onvif/translations/sk.json b/homeassistant/components/onvif/translations/sk.json index f51c7e32bc8..1ec67861352 100644 --- a/homeassistant/components/onvif/translations/sk.json +++ b/homeassistant/components/onvif/translations/sk.json @@ -7,6 +7,7 @@ "step": { "configure": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" diff --git a/homeassistant/components/onvif/translations/zh-Hant.json b/homeassistant/components/onvif/translations/zh-Hant.json index f4d2ddf036d..c16195007f4 100644 --- a/homeassistant/components/onvif/translations/zh-Hant.json +++ b/homeassistant/components/onvif/translations/zh-Hant.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "\u984d\u5916 FFMPEG \u53c3\u6578", - "rtsp_transport": "RTSP \u50b3\u8f38\u5354\u5b9a" + "rtsp_transport": "RTSP \u50b3\u8f38\u5354\u5b9a", + "use_wallclock_as_timestamps": "\u4f7f\u7528\u6642\u9418\u4f5c\u70ba\u6642\u9593\u6233" }, "title": "ONVIF \u88dd\u7f6e\u9078\u9805" } diff --git a/homeassistant/components/opengarage/translations/sk.json b/homeassistant/components/opengarage/translations/sk.json index 1145b3bb9f8..6f2d6c9c475 100644 --- a/homeassistant/components/opengarage/translations/sk.json +++ b/homeassistant/components/opengarage/translations/sk.json @@ -6,6 +6,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/openuv/translations/el.json b/homeassistant/components/openuv/translations/el.json index 0b81ff25fd5..533f09924ed 100644 --- a/homeassistant/components/openuv/translations/el.json +++ b/homeassistant/components/openuv/translations/el.json @@ -7,6 +7,9 @@ "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API" }, "step": { + "reauth_confirm": { + "description": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b5\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03b3\u03b9\u03b1 \u03c4\u03b1 {latitude}, {longitude}." + }, "user": { "data": { "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", diff --git a/homeassistant/components/oralb/translations/el.json b/homeassistant/components/oralb/translations/el.json new file mode 100644 index 00000000000..d3346614257 --- /dev/null +++ b/homeassistant/components/oralb/translations/el.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "not_supported": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/sk.json b/homeassistant/components/overkiz/translations/sk.json index 93eb8dcc1b5..c9c2a93b5a9 100644 --- a/homeassistant/components/overkiz/translations/sk.json +++ b/homeassistant/components/overkiz/translations/sk.json @@ -9,6 +9,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "password": "Heslo" } } diff --git a/homeassistant/components/ovo_energy/translations/el.json b/homeassistant/components/ovo_energy/translations/el.json index b8b9d35a238..0a0949fd703 100644 --- a/homeassistant/components/ovo_energy/translations/el.json +++ b/homeassistant/components/ovo_energy/translations/el.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "account": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd OVO (\u03c0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03bc\u03cc\u03bd\u03bf \u03b5\u03ac\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c0\u03bf\u03bb\u03bb\u03bf\u03cd\u03c2 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd\u03c2)", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" }, diff --git a/homeassistant/components/p1_monitor/translations/sk.json b/homeassistant/components/p1_monitor/translations/sk.json index af15f92c2f2..965b3bb8919 100644 --- a/homeassistant/components/p1_monitor/translations/sk.json +++ b/homeassistant/components/p1_monitor/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov" } } diff --git a/homeassistant/components/panasonic_viera/translations/sk.json b/homeassistant/components/panasonic_viera/translations/sk.json index af15f92c2f2..0bf52ff8f0f 100644 --- a/homeassistant/components/panasonic_viera/translations/sk.json +++ b/homeassistant/components/panasonic_viera/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "IP adresa", "name": "N\u00e1zov" } } diff --git a/homeassistant/components/pi_hole/translations/sk.json b/homeassistant/components/pi_hole/translations/sk.json index 4d37397c800..f3fc592906a 100644 --- a/homeassistant/components/pi_hole/translations/sk.json +++ b/homeassistant/components/pi_hole/translations/sk.json @@ -12,6 +12,7 @@ "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", + "host": "Hostite\u013e", "location": "Umiestnenie", "name": "N\u00e1zov", "port": "Port" diff --git a/homeassistant/components/plex/translations/sk.json b/homeassistant/components/plex/translations/sk.json index 68438cbdfb0..d82a02b7bbf 100644 --- a/homeassistant/components/plex/translations/sk.json +++ b/homeassistant/components/plex/translations/sk.json @@ -9,6 +9,7 @@ "step": { "manual_setup": { "data": { + "host": "Hostite\u013e", "port": "Port", "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" diff --git a/homeassistant/components/plugwise/translations/select.el.json b/homeassistant/components/plugwise/translations/select.el.json index 88f8e117b48..10dc2853ef4 100644 --- a/homeassistant/components/plugwise/translations/select.el.json +++ b/homeassistant/components/plugwise/translations/select.el.json @@ -1,5 +1,11 @@ { "state": { + "plugwise__dhw_mode": { + "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "boost": "\u0395\u03bd\u03af\u03c3\u03c7\u03c5\u03c3\u03b7", + "comfort": "\u0386\u03bd\u03b5\u03c3\u03b7", + "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc" + }, "plugwise__regulation_mode": { "bleeding_cold": "\u0391\u03b9\u03bc\u03bf\u03c1\u03c1\u03b1\u03b3\u03af\u03b1 \u03ba\u03c1\u03cd\u03b1", "bleeding_hot": "\u0391\u03b9\u03bc\u03bf\u03c1\u03c1\u03b1\u03b3\u03af\u03b1 \u03ba\u03b1\u03c5\u03c4\u03ae", diff --git a/homeassistant/components/plugwise/translations/sk.json b/homeassistant/components/plugwise/translations/sk.json index 7124f1e5e28..ac5c3a01c03 100644 --- a/homeassistant/components/plugwise/translations/sk.json +++ b/homeassistant/components/plugwise/translations/sk.json @@ -4,8 +4,14 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "user": { + "data": { + "host": "IP adresa" + } + }, "user_gateway": { "data": { + "host": "IP adresa", "port": "Port" } } diff --git a/homeassistant/components/progettihwsw/translations/sk.json b/homeassistant/components/progettihwsw/translations/sk.json index 892b8b2cd91..33d93b62475 100644 --- a/homeassistant/components/progettihwsw/translations/sk.json +++ b/homeassistant/components/progettihwsw/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/prusalink/translations/sk.json b/homeassistant/components/prusalink/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/prusalink/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pure_energie/translations/sk.json b/homeassistant/components/pure_energie/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/pure_energie/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/el.json b/homeassistant/components/pushbullet/translations/el.json new file mode 100644 index 00000000000..ca4a9135c92 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/el.json @@ -0,0 +1,8 @@ +{ + "issues": { + "deprecated_yaml": { + "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Pushbullet \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03b5\u03b9\u03c3\u03b1\u03c7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Pushbullet YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", + "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Pushbullet YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/el.json b/homeassistant/components/pushover/translations/el.json index d79e3edd8f4..433ceaaddb6 100644 --- a/homeassistant/components/pushover/translations/el.json +++ b/homeassistant/components/pushover/translations/el.json @@ -29,6 +29,10 @@ "deprecated_yaml": { "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Pushover \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03b5\u03b9\u03c3\u03b1\u03c7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Pushover YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Pushover YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" + }, + "removed_yaml": { + "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Pushover \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Pushover YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", + "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Pushover YAML \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c6\u03b1\u03b9\u03c1\u03b5\u03b8\u03b5\u03af" } } } \ No newline at end of file diff --git a/homeassistant/components/radiotherm/translations/sk.json b/homeassistant/components/radiotherm/translations/sk.json new file mode 100644 index 00000000000..d12d05c0033 --- /dev/null +++ b/homeassistant/components/radiotherm/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "flow_title": "{name} {model} ({host})", + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/rainforest_eagle/translations/sk.json b/homeassistant/components/rainforest_eagle/translations/sk.json index 5ada995aa6e..368553a4887 100644 --- a/homeassistant/components/rainforest_eagle/translations/sk.json +++ b/homeassistant/components/rainforest_eagle/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/rainmachine/translations/el.json b/homeassistant/components/rainmachine/translations/el.json index 313f2bfc1fb..f64454dbbbc 100644 --- a/homeassistant/components/rainmachine/translations/el.json +++ b/homeassistant/components/rainmachine/translations/el.json @@ -35,6 +35,7 @@ "step": { "init": { "data": { + "use_app_run_times": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5\u03c2 \u03b5\u03ba\u03c4\u03ad\u03bb\u03b5\u03c3\u03b7\u03c2 \u03b6\u03ce\u03bd\u03b7\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae RainMachine", "zone_run_time": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c2 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03b6\u03ce\u03bd\u03b7\u03c2 (\u03c3\u03b5 \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1)" }, "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 RainMachine" diff --git a/homeassistant/components/rainmachine/translations/sk.json b/homeassistant/components/rainmachine/translations/sk.json index 7fd0d4942e8..c02d61fe9e3 100644 --- a/homeassistant/components/rainmachine/translations/sk.json +++ b/homeassistant/components/rainmachine/translations/sk.json @@ -6,6 +6,7 @@ "step": { "user": { "data": { + "ip_address": "N\u00e1zov hostite\u013ea alebo IP adresa", "password": "Heslo", "port": "Port" } diff --git a/homeassistant/components/remote/translations/is.json b/homeassistant/components/remote/translations/is.json index 3908be15d36..e36cffa6bee 100644 --- a/homeassistant/components/remote/translations/is.json +++ b/homeassistant/components/remote/translations/is.json @@ -1,4 +1,10 @@ { + "device_automation": { + "condition_type": { + "is_off": "{entity_name} er sl\u00f6kkt", + "is_on": "{entity_name} er kveikt" + } + }, "state": { "_": { "off": "\u00d3virk", diff --git a/homeassistant/components/rfxtrx/translations/sk.json b/homeassistant/components/rfxtrx/translations/sk.json index e343d2e8b31..d74ce79a3d7 100644 --- a/homeassistant/components/rfxtrx/translations/sk.json +++ b/homeassistant/components/rfxtrx/translations/sk.json @@ -3,6 +3,7 @@ "step": { "setup_network": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/risco/translations/sk.json b/homeassistant/components/risco/translations/sk.json index 3f464b4046d..d84b4133668 100644 --- a/homeassistant/components/risco/translations/sk.json +++ b/homeassistant/components/risco/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "local": { + "data": { + "host": "Hostite\u013e" + } + } } }, "options": { diff --git a/homeassistant/components/roku/translations/sk.json b/homeassistant/components/roku/translations/sk.json index bee0999420f..cefae299404 100644 --- a/homeassistant/components/roku/translations/sk.json +++ b/homeassistant/components/roku/translations/sk.json @@ -2,6 +2,13 @@ "config": { "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/roomba/translations/sk.json b/homeassistant/components/roomba/translations/sk.json new file mode 100644 index 00000000000..76d3f7b111f --- /dev/null +++ b/homeassistant/components/roomba/translations/sk.json @@ -0,0 +1,17 @@ +{ + "config": { + "flow_title": "{name} ({host})", + "step": { + "manual": { + "data": { + "host": "Hostite\u013e" + } + }, + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/roon/translations/sk.json b/homeassistant/components/roon/translations/sk.json index 5ada995aa6e..1c19d1e71e6 100644 --- a/homeassistant/components/roon/translations/sk.json +++ b/homeassistant/components/roon/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "fallback": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/ruckus_unleashed/translations/sk.json b/homeassistant/components/ruckus_unleashed/translations/sk.json index dbe0480911b..3c3fd224ad3 100644 --- a/homeassistant/components/ruckus_unleashed/translations/sk.json +++ b/homeassistant/components/ruckus_unleashed/translations/sk.json @@ -6,6 +6,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "username": "U\u017e\u00edvate\u013esk\u00e9 meno" } } diff --git a/homeassistant/components/samsungtv/translations/sk.json b/homeassistant/components/samsungtv/translations/sk.json index d4a3e2e9fbb..6058e81bb73 100644 --- a/homeassistant/components/samsungtv/translations/sk.json +++ b/homeassistant/components/samsungtv/translations/sk.json @@ -7,6 +7,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov" } } diff --git a/homeassistant/components/scrape/translations/el.json b/homeassistant/components/scrape/translations/el.json index 8782b2b9f6d..2e2ce016816 100644 --- a/homeassistant/components/scrape/translations/el.json +++ b/homeassistant/components/scrape/translations/el.json @@ -36,6 +36,12 @@ } } }, + "issues": { + "moved_yaml": { + "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Scrape \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03bf YAML \u03ad\u03c7\u03b5\u03b9 \u03bc\u03b5\u03c4\u03b1\u03ba\u03b9\u03bd\u03b7\u03b8\u03b5\u03af \u03c3\u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 YAML \u03b8\u03b1 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03b5\u03af \u03b3\u03b9\u03b1 2 \u03b1\u03ba\u03cc\u03bc\u03b7 \u03b5\u03ba\u03b4\u03cc\u03c3\u03b5\u03b9\u03c2. \n\n \u039c\u03b5\u03c4\u03b1\u03c6\u03ad\u03c1\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03c3\u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7.", + "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Scrape YAML \u03ad\u03c7\u03b5\u03b9 \u03bc\u03b5\u03c4\u03b1\u03ba\u03b9\u03bd\u03b7\u03b8\u03b5\u03af" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/senseme/translations/sk.json b/homeassistant/components/senseme/translations/sk.json index ffc3e13321e..7bf403ddc3b 100644 --- a/homeassistant/components/senseme/translations/sk.json +++ b/homeassistant/components/senseme/translations/sk.json @@ -1,7 +1,16 @@ { "config": { "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165." + "cannot_connect": "Nepodarilo sa pripoji\u0165.", + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, + "flow_title": "{name} - {model} ({host})", + "step": { + "manual": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/sensor.el.json b/homeassistant/components/sensibo/translations/sensor.el.json index b4e595db882..87adea95b4a 100644 --- a/homeassistant/components/sensibo/translations/sensor.el.json +++ b/homeassistant/components/sensibo/translations/sensor.el.json @@ -3,6 +3,11 @@ "sensibo__sensitivity": { "n": "\u039a\u03b1\u03bd\u03bf\u03bd\u03b9\u03ba\u03cc", "s": "\u0395\u03c5\u03b1\u03af\u03c3\u03b8\u03b7\u03c4\u03bf" + }, + "sensibo__smart_type": { + "feelslike": "\u0391\u03af\u03c3\u03b8\u03b7\u03c3\u03b7 \u03c3\u03b1\u03bd", + "humidity": "\u03a5\u03b3\u03c1\u03b1\u03c3\u03af\u03b1", + "temperature": "\u0398\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1" } } } \ No newline at end of file diff --git a/homeassistant/components/sensor/translations/el.json b/homeassistant/components/sensor/translations/el.json index 6acf181aad3..10011a93c2f 100644 --- a/homeassistant/components/sensor/translations/el.json +++ b/homeassistant/components/sensor/translations/el.json @@ -32,6 +32,7 @@ "is_volatile_organic_compounds": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03b5\u03c0\u03af\u03c0\u03b5\u03b4\u03bf \u03c3\u03c5\u03b3\u03ba\u03ad\u03bd\u03c4\u03c1\u03c9\u03c3\u03b7\u03c2 \u03c0\u03c4\u03b7\u03c4\u03b9\u03ba\u03ce\u03bd \u03bf\u03c1\u03b3\u03b1\u03bd\u03b9\u03ba\u03ce\u03bd \u03b5\u03bd\u03ce\u03c3\u03b5\u03c9\u03bd {entity_name}", "is_voltage": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c4\u03ac\u03c3\u03b7 {entity_name}", "is_volume": "\u03a4\u03c1\u03ad\u03c7\u03c9\u03bd \u03cc\u03b3\u03ba\u03bf\u03c2 {entity_name}", + "is_water": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03bd\u03b5\u03c1\u03cc {entity_name}", "is_weight": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03b2\u03ac\u03c1\u03bf\u03c2 {entity_name}" }, "trigger_type": { diff --git a/homeassistant/components/sensor/translations/is.json b/homeassistant/components/sensor/translations/is.json index 0444c7b2866..1739ee8a3c3 100644 --- a/homeassistant/components/sensor/translations/is.json +++ b/homeassistant/components/sensor/translations/is.json @@ -1,4 +1,9 @@ { + "device_automation": { + "condition_type": { + "is_water": "N\u00faverandi {entity_name} vatn" + } + }, "state": { "_": { "off": "Af", diff --git a/homeassistant/components/shelly/translations/sk.json b/homeassistant/components/shelly/translations/sk.json index a019d22d264..5b66d1e57e4 100644 --- a/homeassistant/components/shelly/translations/sk.json +++ b/homeassistant/components/shelly/translations/sk.json @@ -21,6 +21,9 @@ } }, "user": { + "data": { + "host": "Hostite\u013e" + }, "description": "Pred nastaven\u00edm musia by\u0165 zariadenia nap\u00e1jan\u00e9 z bat\u00e9rie zobuden\u00e9. Zobu\u010fte zariadenie pomocou tla\u010didla na \u0148om." } } diff --git a/homeassistant/components/shelly/translations/zh-Hant.json b/homeassistant/components/shelly/translations/zh-Hant.json index 728fcdccbfe..e3165f04a09 100644 --- a/homeassistant/components/shelly/translations/zh-Hant.json +++ b/homeassistant/components/shelly/translations/zh-Hant.json @@ -58,5 +58,18 @@ "single_push": "{subtype} \u55ae\u6309", "triple": "{subtype} \u4e09\u9023\u64ca" } + }, + "options": { + "abort": { + "ble_unsupported": "\u85cd\u82bd\u652f\u63f4\u9700\u8981\u97cc\u9ad4 {ble_min_version} \u7248\u6216\u66f4\u65b0\u7248\u672c\u3002" + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "\u85cd\u82bd\u6383\u7784\u5668\u6a21\u5f0f" + }, + "description": "\u85cd\u82bd\u6383\u63cf\u53ef\u4ee5\u70ba\u4e3b\u52d5\u6216\u88ab\u52d5\u6a21\u5f0f\u3002\u4e3b\u52d5\u6a21\u5f0f\u4e0b\u3001Shelly \u6703\u5411\u5468\u570d\u7684\u88dd\u7f6e\u8acb\u6c42\u8cc7\u6599\uff1b\u88ab\u52d5\u6a21\u5f0f\u4e0b\u3001Shelly \u6703\u7531\u5468\u570d\u7684\u88dd\u7f6e\u63a5\u6536\u5ee3\u64ad\u8cc7\u6599\u3002" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sma/translations/sk.json b/homeassistant/components/sma/translations/sk.json index 0b7bf878ea9..c8c897bc0f5 100644 --- a/homeassistant/components/sma/translations/sk.json +++ b/homeassistant/components/sma/translations/sk.json @@ -5,6 +5,13 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/smappee/translations/sk.json b/homeassistant/components/smappee/translations/sk.json new file mode 100644 index 00000000000..995bc004013 --- /dev/null +++ b/homeassistant/components/smappee/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "local": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/snooz/translations/el.json b/homeassistant/components/snooz/translations/el.json new file mode 100644 index 00000000000..4024c6fc6ba --- /dev/null +++ b/homeassistant/components/snooz/translations/el.json @@ -0,0 +1,12 @@ +{ + "config": { + "progress": { + "wait_for_pairing_mode": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7, \u03b2\u03ac\u03bb\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c3\u03b5 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03cd\u03b6\u03b5\u03c5\u03be\u03b7\u03c2. \n\n ### \u03a0\u03ce\u03c2 \u03bd\u03b1 \u03b5\u03b9\u03c3\u03ad\u03bb\u03b8\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03cd\u03b6\u03b5\u03c5\u03be\u03b7\u03c2\n 1. \u0391\u03bd\u03b1\u03b3\u03ba\u03b1\u03c3\u03c4\u03b9\u03ba\u03ae \u03ad\u03be\u03bf\u03b4\u03bf\u03c2 \u03b1\u03c0\u03cc \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ad\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac SNOOZ.\n 2. \u03a0\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b1\u03b9 \u03ba\u03c1\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 \u03c0\u03b1\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf \u03c4\u03bf \u03ba\u03bf\u03c5\u03bc\u03c0\u03af \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae. \u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03cc\u03c4\u03b1\u03bd \u03b1\u03c1\u03c7\u03af\u03c3\u03bf\u03c5\u03bd \u03bd\u03b1 \u03b1\u03bd\u03b1\u03b2\u03bf\u03c3\u03b2\u03ae\u03bd\u03bf\u03c5\u03bd \u03c4\u03b1 \u03c6\u03ce\u03c4\u03b1 (\u03c0\u03b5\u03c1\u03af\u03c0\u03bf\u03c5 5 \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1)." + }, + "step": { + "pairing_timeout": { + "description": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b5\u03b9\u03c3\u03ae\u03bb\u03b8\u03b5 \u03c3\u03b5 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03cd\u03b6\u03b5\u03c5\u03be\u03b7\u03c2. \u039a\u03ac\u03bd\u03c4\u03b5 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03b7\u03bd \u03a5\u03c0\u03bf\u03b2\u03bf\u03bb\u03ae \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac. \n\n ### \u0391\u03bd\u03c4\u03b9\u03bc\u03b5\u03c4\u03ce\u03c0\u03b9\u03c3\u03b7 \u03c0\u03c1\u03bf\u03b2\u03bb\u03b7\u03bc\u03ac\u03c4\u03c9\u03bd\n 1. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03b7 \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac.\n 2. \u0391\u03c0\u03bf\u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b3\u03b9\u03b1 5 \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1 \u03ba\u03b1\u03b9 \u03bc\u03b5\u03c4\u03ac \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03be\u03b1\u03bd\u03ac." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/solarlog/translations/sk.json b/homeassistant/components/solarlog/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/solarlog/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/soma/translations/sk.json b/homeassistant/components/soma/translations/sk.json index 91a46a2787f..c808fb0e695 100644 --- a/homeassistant/components/soma/translations/sk.json +++ b/homeassistant/components/soma/translations/sk.json @@ -6,6 +6,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/somfy_mylink/translations/sk.json b/homeassistant/components/somfy_mylink/translations/sk.json index 1145b3bb9f8..6f2d6c9c475 100644 --- a/homeassistant/components/somfy_mylink/translations/sk.json +++ b/homeassistant/components/somfy_mylink/translations/sk.json @@ -6,6 +6,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/songpal/translations/sk.json b/homeassistant/components/songpal/translations/sk.json new file mode 100644 index 00000000000..eaec481025c --- /dev/null +++ b/homeassistant/components/songpal/translations/sk.json @@ -0,0 +1,5 @@ +{ + "config": { + "flow_title": "{name} ({host})" + } +} \ No newline at end of file diff --git a/homeassistant/components/soundtouch/translations/sk.json b/homeassistant/components/soundtouch/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/soundtouch/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/squeezebox/translations/sk.json b/homeassistant/components/squeezebox/translations/sk.json index 85b770fe2ed..8f3f3bcd7a1 100644 --- a/homeassistant/components/squeezebox/translations/sk.json +++ b/homeassistant/components/squeezebox/translations/sk.json @@ -3,11 +3,18 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{host}", "step": { "edit": { "data": { + "host": "Hostite\u013e", "port": "Port" } + }, + "user": { + "data": { + "host": "Hostite\u013e" + } } } } diff --git a/homeassistant/components/statistics/translations/el.json b/homeassistant/components/statistics/translations/el.json new file mode 100644 index 00000000000..f0402fc2039 --- /dev/null +++ b/homeassistant/components/statistics/translations/el.json @@ -0,0 +1,12 @@ +{ + "issues": { + "deprecation_warning_characteristic": { + "description": "\u0397 \u03c0\u03b1\u03c1\u03ac\u03bc\u03b5\u03c4\u03c1\u03bf\u03c2 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u00abstate_characteristic\u00bb \u03c4\u03b7\u03c2 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03ce\u03bd \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03c9\u03bd \u03b8\u03b1 \u03b3\u03af\u03bd\u03b5\u03b9 \u03c5\u03c0\u03bf\u03c7\u03c1\u03b5\u03c9\u03c4\u03b9\u03ba\u03ae. \n\n \u03a0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03c4\u03b5 \u03c4\u03bf \u00abstate_characteristic: {characteristic} \u00bb \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u00ab {entity} \u00bb \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03c4\u03b7\u03c1\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03c6\u03bf\u03c1\u03ac. \n\n \u0394\u03b9\u03b1\u03b2\u03ac\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03ce\u03bd \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2: https://www.home-assistant.io/integrations/statistics/", + "title": "\u03a5\u03c0\u03bf\u03c7\u03c1\u03b5\u03c9\u03c4\u03b9\u03ba\u03cc 'state_characteristic' \u03b8\u03b5\u03c9\u03c1\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03bc\u03b9\u03b1 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03a3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03ae\u03c2" + }, + "deprecation_warning_size": { + "description": "\u0397 \u03c0\u03b1\u03c1\u03ac\u03bc\u03b5\u03c4\u03c1\u03bf\u03c2 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u00absampling_size\u00bb \u03c4\u03b7\u03c2 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03ce\u03bd \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03c9\u03bd \u03ae\u03c4\u03b1\u03bd \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03c3\u03c4\u03b7\u03bd \u03c4\u03b9\u03bc\u03ae 20 \u03bc\u03ad\u03c7\u03c1\u03b9 \u03c3\u03c4\u03b9\u03b3\u03bc\u03ae\u03c2, \u03b7 \u03bf\u03c0\u03bf\u03af\u03b1 \u03b8\u03b1 \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03b9. \n\n \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u00ab {entity} \u00bb \u03ba\u03b1\u03b9 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03c4\u03b5 \u03c4\u03b1 \u03ba\u03b1\u03c4\u03ac\u03bb\u03bb\u03b7\u03bb\u03b1 \u03cc\u03c1\u03b9\u03b1, \u03c0.\u03c7. \u00absampling_size: 20\u00bb \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03c4\u03b7\u03c1\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03c6\u03bf\u03c1\u03ac. \u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03ce\u03bd \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03c9\u03bd \u03b8\u03b1 \u03b3\u03af\u03bd\u03b5\u03b9 \u03c0\u03b9\u03bf \u03b5\u03c5\u03ad\u03bb\u03b9\u03ba\u03c4\u03b7 \u03bc\u03b5 \u03c4\u03b7\u03bd \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 2022.12.0 \u03ba\u03b1\u03b9 \u03b8\u03b1 \u03b1\u03c0\u03bf\u03b4\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03b5\u03af\u03c4\u03b5 \u03c4\u03bf \"sampling_size\" \u03b5\u03af\u03c4\u03b5 \u03c4\u03bf \"max_age\" \u03ae \u03ba\u03b1\u03b9 \u03c4\u03b9\u03c2 \u03b4\u03cd\u03bf \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2. \u03a4\u03bf \u03c0\u03b1\u03c1\u03b1\u03c0\u03ac\u03bd\u03c9 \u03b1\u03af\u03c4\u03b7\u03bc\u03b1 \u03c0\u03c1\u03bf\u03b5\u03c4\u03bf\u03b9\u03bc\u03ac\u03b6\u03b5\u03b9 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7\u03bd \u03ba\u03b1\u03c4\u03ac \u03c4\u03b1 \u03ac\u03bb\u03bb\u03b1 \u03c3\u03b7\u03bc\u03b1\u03bd\u03c4\u03b9\u03ba\u03ae \u03b1\u03bb\u03bb\u03b1\u03b3\u03ae. \n\n \u0394\u03b9\u03b1\u03b2\u03ac\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03ce\u03bd \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2: https://www.home-assistant.io/integrations/statistics/", + "title": "\u03a4\u03bf \u03c3\u03b9\u03c9\u03c0\u03b7\u03c1\u03cc 'sampling_size' \u03b8\u03b5\u03c9\u03c1\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03bc\u03b9\u03b1 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03a3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03ae\u03c2" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steamist/translations/sk.json b/homeassistant/components/steamist/translations/sk.json index bee0999420f..cefae299404 100644 --- a/homeassistant/components/steamist/translations/sk.json +++ b/homeassistant/components/steamist/translations/sk.json @@ -2,6 +2,13 @@ "config": { "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/switch/translations/is.json b/homeassistant/components/switch/translations/is.json index 35751f3f4f6..2f6c55fb1b6 100644 --- a/homeassistant/components/switch/translations/is.json +++ b/homeassistant/components/switch/translations/is.json @@ -1,4 +1,10 @@ { + "device_automation": { + "condition_type": { + "is_off": "{entity_name} er sl\u00f6kkt", + "is_on": "{entity_name} er kveikt" + } + }, "state": { "_": { "off": "Sl\u00f6kkt", diff --git a/homeassistant/components/switchbee/translations/sk.json b/homeassistant/components/switchbee/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/switchbee/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/synology_dsm/translations/sk.json b/homeassistant/components/synology_dsm/translations/sk.json index 83965998f0e..dda01d0550d 100644 --- a/homeassistant/components/synology_dsm/translations/sk.json +++ b/homeassistant/components/synology_dsm/translations/sk.json @@ -7,6 +7,7 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{name} ({host})", "step": { "link": { "data": { @@ -15,6 +16,7 @@ }, "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/tesla_wall_connector/translations/sk.json b/homeassistant/components/tesla_wall_connector/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/tesla_wall_connector/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/sk.json b/homeassistant/components/tolo/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/tolo/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tplink/translations/sk.json b/homeassistant/components/tplink/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/tplink/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tradfri/translations/sk.json b/homeassistant/components/tradfri/translations/sk.json index 299acb612fb..1b796109322 100644 --- a/homeassistant/components/tradfri/translations/sk.json +++ b/homeassistant/components/tradfri/translations/sk.json @@ -3,6 +3,13 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + }, + "step": { + "auth": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/transmission/translations/el.json b/homeassistant/components/transmission/translations/el.json index 6790e6e351d..e18065b34bf 100644 --- a/homeassistant/components/transmission/translations/el.json +++ b/homeassistant/components/transmission/translations/el.json @@ -29,6 +29,19 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "\u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03c5\u03c7\u03cc\u03bd \u03b1\u03c5\u03c4\u03bf\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03bf\u03cd\u03c2 \u03ae \u03c3\u03b5\u03bd\u03ac\u03c1\u03b9\u03b1 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7\u03bd \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ba\u03b1\u03b9 \u03b1\u03bd\u03c4\u03b9\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03bf\u03bd\u03cc\u03bc\u03b1\u03c4\u03bf\u03c2 \u03bc\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af entry_id.", + "title": "\u03a4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03bf\u03bd\u03cc\u03bc\u03b1\u03c4\u03bf\u03c2 \u03c3\u03c4\u03bf Transmission \u03bc\u03b5\u03c4\u03ac\u03b4\u03bf\u03c3\u03b7\u03c2 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" + } + } + }, + "title": "\u03a4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03bf\u03bd\u03cc\u03bc\u03b1\u03c4\u03bf\u03c2 \u03c3\u03c4\u03bf Transmission \u03bc\u03b5\u03c4\u03ac\u03b4\u03bf\u03c3\u03b7\u03c2 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/transmission/translations/sk.json b/homeassistant/components/transmission/translations/sk.json index 731004b0ebc..c3ed214e566 100644 --- a/homeassistant/components/transmission/translations/sk.json +++ b/homeassistant/components/transmission/translations/sk.json @@ -7,6 +7,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov", "port": "Port" } diff --git a/homeassistant/components/twinkly/translations/sk.json b/homeassistant/components/twinkly/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/twinkly/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifi/translations/sk.json b/homeassistant/components/unifi/translations/sk.json index da71ce60d66..96cb696c161 100644 --- a/homeassistant/components/unifi/translations/sk.json +++ b/homeassistant/components/unifi/translations/sk.json @@ -6,9 +6,11 @@ "error": { "faulty_credentials": "Neplatn\u00e9 overenie" }, + "flow_title": "{site} ({host})", "step": { "user": { "data": { + "host": "Hostite\u013e", "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" diff --git a/homeassistant/components/unifiprotect/translations/el.json b/homeassistant/components/unifiprotect/translations/el.json index d3771a72409..2872232eb76 100644 --- a/homeassistant/components/unifiprotect/translations/el.json +++ b/homeassistant/components/unifiprotect/translations/el.json @@ -47,6 +47,7 @@ "title": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7\u03c2 Early Access" }, "ea_warning": { + "description": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 v{version} \u03c4\u03bf\u03c5 UniFi Protect, \u03b7 \u03bf\u03c0\u03bf\u03af\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03c0\u03c1\u03ce\u03b9\u03bc\u03b7\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2. \u039f\u03b9 \u03b5\u03ba\u03b4\u03cc\u03c3\u03b5\u03b9\u03c2 Early Access \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant \u03ba\u03b1\u03b9 \u03b5\u03bd\u03b4\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03ba\u03b1\u03bb\u03ad\u03c3\u03bf\u03c5\u03bd \u03b4\u03b9\u03b1\u03ba\u03bf\u03c0\u03ae \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 UniFi Protect \u03ae \u03bd\u03b1 \u03bc\u03b7\u03bd \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03bf\u03cd\u03bd \u03cc\u03c0\u03c9\u03c2 \u03b1\u03bd\u03b1\u03bc\u03ad\u03bd\u03b5\u03c4\u03b1\u03b9.", "fix_flow": { "step": { "confirm": { @@ -58,7 +59,8 @@ "title": "\u03a4\u03bf v{version} \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03b9\u03b1 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 Early Access" } } - } + }, + "title": "\u03a4\u03bf UniFi Protect v{version} \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03b9\u03b1 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03c0\u03c1\u03ce\u03b9\u03bc\u03b7\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" } }, "options": { diff --git a/homeassistant/components/unifiprotect/translations/sk.json b/homeassistant/components/unifiprotect/translations/sk.json index 3b59b1ff213..7e4857f7ec5 100644 --- a/homeassistant/components/unifiprotect/translations/sk.json +++ b/homeassistant/components/unifiprotect/translations/sk.json @@ -11,6 +11,7 @@ }, "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/vacuum/translations/is.json b/homeassistant/components/vacuum/translations/is.json index 759191ae67e..da930604165 100644 --- a/homeassistant/components/vacuum/translations/is.json +++ b/homeassistant/components/vacuum/translations/is.json @@ -1,4 +1,9 @@ { + "device_automation": { + "condition_type": { + "is_cleaning": "{entity_name} er a\u00f0 \u00fer\u00edfa" + } + }, "state": { "_": { "cleaning": "A\u00f0 ryksuga", diff --git a/homeassistant/components/vallox/translations/sk.json b/homeassistant/components/vallox/translations/sk.json new file mode 100644 index 00000000000..4116024575b --- /dev/null +++ b/homeassistant/components/vallox/translations/sk.json @@ -0,0 +1,17 @@ +{ + "config": { + "abort": { + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, + "error": { + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/venstar/translations/sk.json b/homeassistant/components/venstar/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/venstar/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vicare/translations/sk.json b/homeassistant/components/vicare/translations/sk.json index 1a2c2a47260..dfca287492d 100644 --- a/homeassistant/components/vicare/translations/sk.json +++ b/homeassistant/components/vicare/translations/sk.json @@ -3,6 +3,7 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{name} ({host})", "step": { "user": { "data": { diff --git a/homeassistant/components/vilfo/translations/sk.json b/homeassistant/components/vilfo/translations/sk.json index 7afa1eaea6e..519a32c6735 100644 --- a/homeassistant/components/vilfo/translations/sk.json +++ b/homeassistant/components/vilfo/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "access_token": "Pr\u00edstupov\u00fd token" + "access_token": "Pr\u00edstupov\u00fd token", + "host": "Hostite\u013e" } } } diff --git a/homeassistant/components/vizio/translations/sk.json b/homeassistant/components/vizio/translations/sk.json index 171a5a2c708..6c864220193 100644 --- a/homeassistant/components/vizio/translations/sk.json +++ b/homeassistant/components/vizio/translations/sk.json @@ -4,6 +4,7 @@ "user": { "data": { "access_token": "Pr\u00edstupov\u00fd token", + "host": "Hostite\u013e", "name": "N\u00e1zov" } } diff --git a/homeassistant/components/vlc_telnet/translations/sk.json b/homeassistant/components/vlc_telnet/translations/sk.json index d3bc93c4168..9b3bf644a30 100644 --- a/homeassistant/components/vlc_telnet/translations/sk.json +++ b/homeassistant/components/vlc_telnet/translations/sk.json @@ -7,9 +7,11 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{host}", "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov", "port": "Port" } diff --git a/homeassistant/components/volumio/translations/sk.json b/homeassistant/components/volumio/translations/sk.json index 892b8b2cd91..33d93b62475 100644 --- a/homeassistant/components/volumio/translations/sk.json +++ b/homeassistant/components/volumio/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/wake_on_lan/translations/zh-Hant.json b/homeassistant/components/wake_on_lan/translations/zh-Hant.json new file mode 100644 index 00000000000..f62b98371a2 --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/zh-Hant.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a\u7684 Wake on Lan \u5373\u5c07\u8f49\u79fb\u81f3\u6574\u5408\u3002\n\n\u73fe\u6709\u7684 YAML \u8a2d\u5b9a\u53ea\u80fd\u518d\u4f7f\u7528\u5169\u500b\u66f4\u65b0\u7248\u672c\u3002\n\n\u8ddf\u96a8\u6587\u4ef6\u8aaa\u660e\u9077\u79fb YAML \u8a2d\u5b9a\u81f3\u6574\u5408\u3002", + "title": "Wake on Lan YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/webostv/translations/sk.json b/homeassistant/components/webostv/translations/sk.json index 57685686065..eb20925e0a4 100644 --- a/homeassistant/components/webostv/translations/sk.json +++ b/homeassistant/components/webostv/translations/sk.json @@ -16,6 +16,7 @@ }, "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov" }, "description": "Zapnite TV, vypl\u0148te nasleduj\u00face polia, kliknite na Odosla\u0165", diff --git a/homeassistant/components/wiz/translations/sk.json b/homeassistant/components/wiz/translations/sk.json index 641bd9c13ee..73be7f26f22 100644 --- a/homeassistant/components/wiz/translations/sk.json +++ b/homeassistant/components/wiz/translations/sk.json @@ -2,6 +2,14 @@ "config": { "abort": { "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "flow_title": "{name} ({host})", + "step": { + "user": { + "data": { + "host": "IP adresa" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/wled/translations/sk.json b/homeassistant/components/wled/translations/sk.json new file mode 100644 index 00000000000..842ff61bd79 --- /dev/null +++ b/homeassistant/components/wled/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xiaomi_aqara/translations/sk.json b/homeassistant/components/xiaomi_aqara/translations/sk.json index 299acb612fb..2c8dce2a9d8 100644 --- a/homeassistant/components/xiaomi_aqara/translations/sk.json +++ b/homeassistant/components/xiaomi_aqara/translations/sk.json @@ -3,6 +3,13 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + }, + "step": { + "user": { + "data": { + "host": "IP adresa (volite\u013en\u00e1)" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/sk.json b/homeassistant/components/xiaomi_miio/translations/sk.json index fca4223f4ac..41d70853d59 100644 --- a/homeassistant/components/xiaomi_miio/translations/sk.json +++ b/homeassistant/components/xiaomi_miio/translations/sk.json @@ -8,6 +8,7 @@ "step": { "manual": { "data": { + "host": "IP adresa", "token": "API token" } } diff --git a/homeassistant/components/yamaha_musiccast/translations/sk.json b/homeassistant/components/yamaha_musiccast/translations/sk.json index f74ba4b46d2..704da465e48 100644 --- a/homeassistant/components/yamaha_musiccast/translations/sk.json +++ b/homeassistant/components/yamaha_musiccast/translations/sk.json @@ -2,6 +2,13 @@ "config": { "abort": { "already_configured": "Zariadenie je u\u017e nakonfigurovan\u00e9" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/yeelight/translations/sk.json b/homeassistant/components/yeelight/translations/sk.json index 793f8eff278..2cf44c3ea29 100644 --- a/homeassistant/components/yeelight/translations/sk.json +++ b/homeassistant/components/yeelight/translations/sk.json @@ -2,6 +2,14 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "flow_title": "{model} {id} ({host})", + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/youless/translations/sk.json b/homeassistant/components/youless/translations/sk.json index af15f92c2f2..965b3bb8919 100644 --- a/homeassistant/components/youless/translations/sk.json +++ b/homeassistant/components/youless/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "host": "Hostite\u013e", "name": "N\u00e1zov" } } diff --git a/homeassistant/components/zamg/translations/el.json b/homeassistant/components/zamg/translations/el.json new file mode 100644 index 00000000000..4e7f31120be --- /dev/null +++ b/homeassistant/components/zamg/translations/el.json @@ -0,0 +1,19 @@ +{ + "config": { + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd (\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03c0\u03bb\u03b7\u03c3\u03b9\u03ad\u03c3\u03c4\u03b5\u03c1\u03bf \u03c3\u03c4\u03b1\u03b8\u03bc\u03cc)" + }, + "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf ZAMG \u03b3\u03b9\u03b1 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03bc\u03b5 \u03c4\u03bf Home Assistant." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 ZAMG \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03b5\u03b9\u03c3\u03b1\u03c7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 ZAMG YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", + "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 ZAMG YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/el.json b/homeassistant/components/zwave_js/translations/el.json index 9e671ee1aaa..d34b6762200 100644 --- a/homeassistant/components/zwave_js/translations/el.json +++ b/homeassistant/components/zwave_js/translations/el.json @@ -10,7 +10,8 @@ "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "discovery_requires_supervisor": "\u0397 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7 \u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af \u03c4\u03bf\u03bd \u03b5\u03c0\u03cc\u03c0\u03c4\u03b7.", - "not_zwave_device": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c0\u03bf\u03c5 \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b5 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Z-Wave." + "not_zwave_device": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c0\u03bf\u03c5 \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b5 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Z-Wave.", + "not_zwave_js_addon": "\u03a4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf \u03c0\u03bf\u03c5 \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b5 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c4\u03bf \u03b5\u03c0\u03af\u03c3\u03b7\u03bc\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Z-Wave JS." }, "error": { "addon_start_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Z-Wave JS. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7.", diff --git a/homeassistant/components/zwave_js/translations/is.json b/homeassistant/components/zwave_js/translations/is.json new file mode 100644 index 00000000000..8558e65ea2c --- /dev/null +++ b/homeassistant/components/zwave_js/translations/is.json @@ -0,0 +1,7 @@ +{ + "device_automation": { + "condition_type": { + "node_status": "Sta\u00f0a hn\u00fats" + } + } +} \ No newline at end of file From 9fefa2e1395a06f12ffdcd704fc45a529f13331f Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Fri, 18 Nov 2022 17:37:12 -0700 Subject: [PATCH 0555/1033] Bump `regenmaschine` to 2022.11.0 (#82337) fixes undefined --- homeassistant/components/rainmachine/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/rainmachine/manifest.json b/homeassistant/components/rainmachine/manifest.json index a41db1d18f9..a8f6f316d3a 100644 --- a/homeassistant/components/rainmachine/manifest.json +++ b/homeassistant/components/rainmachine/manifest.json @@ -3,7 +3,7 @@ "name": "RainMachine", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/rainmachine", - "requirements": ["regenmaschine==2022.10.0"], + "requirements": ["regenmaschine==2022.11.0"], "codeowners": ["@bachya"], "iot_class": "local_polling", "homekit": { diff --git a/requirements_all.txt b/requirements_all.txt index f84582832ac..5313d46134c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2170,7 +2170,7 @@ raincloudy==0.0.7 raspyrfm-client==1.2.8 # homeassistant.components.rainmachine -regenmaschine==2022.10.0 +regenmaschine==2022.11.0 # homeassistant.components.renault renault-api==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 957a3f10450..defe1e0bcf6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1506,7 +1506,7 @@ radios==0.1.1 radiotherm==2.1.0 # homeassistant.components.rainmachine -regenmaschine==2022.10.0 +regenmaschine==2022.11.0 # homeassistant.components.renault renault-api==0.1.11 From e4666206ec1f83c29ed42169e7d5015c5d659254 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 18 Nov 2022 21:04:19 -0600 Subject: [PATCH 0556/1033] Improve time change helper comments (#82349) * Improve time change helper comments https://github.com/home-assistant/core/pull/82324#discussion_r1026962806 * Improve time change helper comments https://github.com/home-assistant/core/pull/82324#discussion_r1026962806 --- tests/common.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/common.py b/tests/common.py index b1eec13a851..48e51303a0c 100644 --- a/tests/common.py +++ b/tests/common.py @@ -385,10 +385,10 @@ def async_fire_time_changed_exact( ) -> None: """Fire a time changed event at an exact microsecond. - Consider that its not possible to actually achieve an exact microsecond - in production as the event loop is not precise enough. If your code - relies on this level of precision, consider a different approach - as this is only for testing. + Consider that it is not possible to actually achieve an exact + microsecond in production as the event loop is not precise enough. + If your code relies on this level of precision, consider a different + approach, as this is only for testing. """ if datetime_ is None: utc_datetime = date_util.utcnow() @@ -404,10 +404,13 @@ def async_fire_time_changed( ) -> None: """Fire a time changed event. - This function will ensure microseconds at at least 500000 - to account for the synchronization repeating listeners. + This function will add up to 0.5 seconds to the time to ensure that + it accounts for the accidental synchronization avoidance code in repeating + listeners. - If you need to fire an exact microsecond, use async_fire_time_changed_exact. + As asyncio is cooperative, we can't guarantee that the event loop will + run an event at the exact time we want. If you need to fire time changed + for an exact microsecond, use async_fire_time_changed_exact. """ if datetime_ is None: utc_datetime = date_util.utcnow() From c8d4febe13a5c800c20f2aaee6c145b503580c35 Mon Sep 17 00:00:00 2001 From: Benjamin <46243805+bbr111@users.noreply.github.com> Date: Sat, 19 Nov 2022 13:09:57 +0100 Subject: [PATCH 0557/1033] Add Homematic sensor descriptions (#82156) Update sensor.py Add Sensor Descriptions. --- homeassistant/components/homematic/sensor.py | 30 ++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/homeassistant/components/homematic/sensor.py b/homeassistant/components/homematic/sensor.py index 806d56b2c1d..4c18afd0458 100644 --- a/homeassistant/components/homematic/sensor.py +++ b/homeassistant/components/homematic/sensor.py @@ -253,6 +253,36 @@ SENSOR_DESCRIPTIONS: dict[str, SensorEntityDescription] = { device_class=SensorDeviceClass.PM10, state_class=SensorStateClass.MEASUREMENT, ), + "STATE": SensorEntityDescription( + key="STATE", + ), + "SMOKE_DETECTOR_ALARM_STATUS": SensorEntityDescription( + key="SMOKE_DETECTOR_ALARM_STATUS", + ), + "WIND_DIR": SensorEntityDescription( + key="WIND_DIR", + ), + "WIND_DIR_RANGE": SensorEntityDescription( + key="WIND_DIR_RANGE", + ), + "CONCENTRATION_STATUS": SensorEntityDescription( + key="CONCENTRATION_STATUS", + ), + "PASSAGE_COUNTER_VALUE": SensorEntityDescription( + key="PASSAGE_COUNTER_VALUE", + ), + "LEVEL": SensorEntityDescription( + key="LEVEL", + ), + "LEVEL_2": SensorEntityDescription( + key="LEVEL_2", + ), + "DOOR_STATE": SensorEntityDescription( + key="DOOR_STATE", + ), + "FILLING_LEVEL": SensorEntityDescription( + key="FILLING_LEVEL", + ), } DEFAULT_SENSOR_DESCRIPTION = SensorEntityDescription( From 672acf30a42ffbc6350d1a7c7b65abaa2e0bae90 Mon Sep 17 00:00:00 2001 From: Ernst Klamer Date: Sat, 19 Nov 2022 13:48:33 +0100 Subject: [PATCH 0558/1033] Bump bthome-ble to 2.3.1 (#82342) * Bump bthome-ble * Bump bthome-ble --- homeassistant/components/bthome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/bthome/test_sensor.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bthome/manifest.json b/homeassistant/components/bthome/manifest.json index 0111f765014..bf447e6a485 100644 --- a/homeassistant/components/bthome/manifest.json +++ b/homeassistant/components/bthome/manifest.json @@ -17,7 +17,7 @@ "service_data_uuid": "0000fcd2-0000-1000-8000-00805f9b34fb" } ], - "requirements": ["bthome-ble==2.2.1"], + "requirements": ["bthome-ble==2.3.1"], "dependencies": ["bluetooth"], "codeowners": ["@Ernst79"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 5313d46134c..ca51893947f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -482,7 +482,7 @@ brunt==1.2.0 bt_proximity==0.2.1 # homeassistant.components.bthome -bthome-ble==2.2.1 +bthome-ble==2.3.1 # homeassistant.components.bt_home_hub_5 bthomehub5-devicelist==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index defe1e0bcf6..0952803fb54 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -386,7 +386,7 @@ brother==2.0.0 brunt==1.2.0 # homeassistant.components.bthome -bthome-ble==2.2.1 +bthome-ble==2.3.1 # homeassistant.components.buienradar buienradar==1.0.5 diff --git a/tests/components/bthome/test_sensor.py b/tests/components/bthome/test_sensor.py index 989fff1a25f..dce82ab454f 100644 --- a/tests/components/bthome/test_sensor.py +++ b/tests/components/bthome/test_sensor.py @@ -870,7 +870,7 @@ async def test_v1_sensors( "54:48:E6:8F:80:A5", make_bthome_v2_adv( "54:48:E6:8F:80:A5", - b"\x41\xa4\x72\x66\xc9\x5f\x73\x00\x11\x22\x33\xb7\xce\xd8\xe5", + b"\x41\xa4\x72\x66\xc9\x5f\x73\x00\x11\x22\x33\x78\x23\x72\x14", ), "231d39c1d7cc1ab1aee224cd096db932", [ From df959c364c8b65d470e035fcefc9976986d07dfb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 19 Nov 2022 06:49:54 -0600 Subject: [PATCH 0559/1033] Bump flux_led to 0.28.34 (#82347) * Bump flux_led to 0.28.33 fixes #75832 changelog: https://github.com/Danielhiversen/flux_led/compare/0.28.32...0.28.33 * more more bump for legacy turn on --- homeassistant/components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index 632ef04e456..66aa9fe0b92 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["network"], "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.28.32"], + "requirements": ["flux_led==0.28.34"], "quality_scale": "platinum", "codeowners": ["@icemanch", "@bdraco"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index ca51893947f..735259d3c95 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -706,7 +706,7 @@ fjaraskupan==2.2.0 flipr-api==1.4.4 # homeassistant.components.flux_led -flux_led==0.28.32 +flux_led==0.28.34 # homeassistant.components.homekit # homeassistant.components.recorder diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0952803fb54..6279f5f267d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -528,7 +528,7 @@ fjaraskupan==2.2.0 flipr-api==1.4.4 # homeassistant.components.flux_led -flux_led==0.28.32 +flux_led==0.28.34 # homeassistant.components.homekit # homeassistant.components.recorder From 93897016f0ea400fe9f3e90c7faa1bba39321a27 Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Sat, 19 Nov 2022 15:02:07 +0100 Subject: [PATCH 0560/1033] Fix invalid configuration_url in Netatmo (#82372) --- homeassistant/components/netatmo/netatmo_entity_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/netatmo/netatmo_entity_base.py b/homeassistant/components/netatmo/netatmo_entity_base.py index c434d370e27..78352e486e0 100644 --- a/homeassistant/components/netatmo/netatmo_entity_base.py +++ b/homeassistant/components/netatmo/netatmo_entity_base.py @@ -29,7 +29,7 @@ class NetatmoBase(Entity): self._device_name: str = "" self._id: str = "" self._model: str = "" - self._config_url: str = "" + self._config_url: str | None = None self._attr_name = None self._attr_unique_id = None self._attr_extra_state_attributes = {} From 217d8eb9c5d7cfd8ab2b868fbff0a54b84f16637 Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Sat, 19 Nov 2022 15:12:24 +0100 Subject: [PATCH 0561/1033] Address late review of fritzbox (#82369) --- homeassistant/components/fritzbox/__init__.py | 22 ++++---- .../components/fritzbox/binary_sensor.py | 8 +-- homeassistant/components/fritzbox/button.py | 6 +-- homeassistant/components/fritzbox/climate.py | 54 +++++++++---------- homeassistant/components/fritzbox/cover.py | 22 ++++---- homeassistant/components/fritzbox/light.py | 35 ++++++------ homeassistant/components/fritzbox/sensor.py | 2 +- homeassistant/components/fritzbox/switch.py | 12 +++-- 8 files changed, 83 insertions(+), 78 deletions(-) diff --git a/homeassistant/components/fritzbox/__init__.py b/homeassistant/components/fritzbox/__init__.py index 3351372421b..40d170db3b3 100644 --- a/homeassistant/components/fritzbox/__init__.py +++ b/homeassistant/components/fritzbox/__init__.py @@ -111,16 +111,16 @@ class FritzBoxEntity(CoordinatorEntity[FritzboxDataUpdateCoordinator], ABC): self.ain = ain if entity_description is not None: self.entity_description = entity_description - self._attr_name = f"{self.entity.name} {entity_description.name}" + self._attr_name = f"{self.data.name} {entity_description.name}" self._attr_unique_id = f"{ain}_{entity_description.key}" else: - self._attr_name = self.entity.name + self._attr_name = self.data.name self._attr_unique_id = ain @property @abstractmethod - def entity(self) -> FritzhomeEntityBase: - """Return entity object from coordinator.""" + def data(self) -> FritzhomeEntityBase: + """Return data object from coordinator.""" class FritzBoxDeviceEntity(FritzBoxEntity): @@ -129,21 +129,21 @@ class FritzBoxDeviceEntity(FritzBoxEntity): @property def available(self) -> bool: """Return if entity is available.""" - return super().available and self.entity.present + return super().available and self.data.present @property - def entity(self) -> FritzhomeDevice: - """Return device object from coordinator.""" + def data(self) -> FritzhomeDevice: + """Return device data object from coordinator.""" return self.coordinator.data.devices[self.ain] @property def device_info(self) -> DeviceInfo: """Return device specific attributes.""" return DeviceInfo( - name=self.entity.name, + name=self.data.name, identifiers={(DOMAIN, self.ain)}, - manufacturer=self.entity.manufacturer, - model=self.entity.productname, - sw_version=self.entity.fw_version, + manufacturer=self.data.manufacturer, + model=self.data.productname, + sw_version=self.data.fw_version, configuration_url=self.coordinator.configuration_url, ) diff --git a/homeassistant/components/fritzbox/binary_sensor.py b/homeassistant/components/fritzbox/binary_sensor.py index 10ffd0b9104..62f0786af53 100644 --- a/homeassistant/components/fritzbox/binary_sensor.py +++ b/homeassistant/components/fritzbox/binary_sensor.py @@ -68,7 +68,9 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the FRITZ!SmartHome binary sensor from ConfigEntry.""" - coordinator = hass.data[FRITZBOX_DOMAIN][entry.entry_id][CONF_COORDINATOR] + coordinator: FritzboxDataUpdateCoordinator = hass.data[FRITZBOX_DOMAIN][ + entry.entry_id + ][CONF_COORDINATOR] async_add_entities( [ @@ -93,10 +95,10 @@ class FritzboxBinarySensor(FritzBoxDeviceEntity, BinarySensorEntity): ) -> None: """Initialize the FritzBox entity.""" super().__init__(coordinator, ain, entity_description) - self._attr_name = f"{self.entity.name} {entity_description.name}" + self._attr_name = f"{self.data.name} {entity_description.name}" self._attr_unique_id = f"{ain}_{entity_description.key}" @property def is_on(self) -> bool | None: """Return true if sensor is on.""" - return self.entity_description.is_on(self.entity) + return self.entity_description.is_on(self.data) diff --git a/homeassistant/components/fritzbox/button.py b/homeassistant/components/fritzbox/button.py index f8e5db9fcef..00e8ad5e4d6 100644 --- a/homeassistant/components/fritzbox/button.py +++ b/homeassistant/components/fritzbox/button.py @@ -31,15 +31,15 @@ class FritzBoxTemplate(FritzBoxEntity, ButtonEntity): """Interface between FritzhomeTemplate and hass.""" @property - def entity(self) -> FritzhomeTemplate: - """Return the template entity.""" + def data(self) -> FritzhomeTemplate: + """Return the template data entity.""" return self.coordinator.data.templates[self.ain] @property def device_info(self) -> DeviceInfo: """Return device specific attributes.""" return DeviceInfo( - name=self.entity.name, + name=self.data.name, identifiers={(FRITZBOX_DOMAIN, self.ain)}, configuration_url=self.coordinator.configuration_url, manufacturer="AVM", diff --git a/homeassistant/components/fritzbox/climate.py b/homeassistant/components/fritzbox/climate.py index 364a96e8d2f..b1898c41cc7 100644 --- a/homeassistant/components/fritzbox/climate.py +++ b/homeassistant/components/fritzbox/climate.py @@ -21,7 +21,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import FritzBoxDeviceEntity +from . import FritzboxDataUpdateCoordinator, FritzBoxDeviceEntity from .const import ( ATTR_STATE_BATTERY_LOW, ATTR_STATE_HOLIDAY_MODE, @@ -50,7 +50,9 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the FRITZ!SmartHome thermostat from ConfigEntry.""" - coordinator = hass.data[FRITZBOX_DOMAIN][entry.entry_id][CONF_COORDINATOR] + coordinator: FritzboxDataUpdateCoordinator = hass.data[FRITZBOX_DOMAIN][ + entry.entry_id + ][CONF_COORDINATOR] async_add_entities( [ @@ -73,18 +75,18 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity): @property def current_temperature(self) -> float: """Return the current temperature.""" - if self.entity.has_temperature_sensor and self.entity.temperature is not None: - return self.entity.temperature # type: ignore [no-any-return] - return self.entity.actual_temperature # type: ignore [no-any-return] + if self.data.has_temperature_sensor and self.data.temperature is not None: + return self.data.temperature # type: ignore [no-any-return] + return self.data.actual_temperature # type: ignore [no-any-return] @property def target_temperature(self) -> float: """Return the temperature we try to reach.""" - if self.entity.target_temperature == ON_API_TEMPERATURE: + if self.data.target_temperature == ON_API_TEMPERATURE: return ON_REPORT_SET_TEMPERATURE - if self.entity.target_temperature == OFF_API_TEMPERATURE: + if self.data.target_temperature == OFF_API_TEMPERATURE: return OFF_REPORT_SET_TEMPERATURE - return self.entity.target_temperature # type: ignore [no-any-return] + return self.data.target_temperature # type: ignore [no-any-return] async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature.""" @@ -94,14 +96,14 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity): elif kwargs.get(ATTR_TEMPERATURE) is not None: temperature = kwargs[ATTR_TEMPERATURE] await self.hass.async_add_executor_job( - self.entity.set_target_temperature, temperature + self.data.set_target_temperature, temperature ) await self.coordinator.async_refresh() @property def hvac_mode(self) -> str: """Return the current operation mode.""" - if self.entity.target_temperature in ( + if self.data.target_temperature in ( OFF_REPORT_SET_TEMPERATURE, OFF_API_TEMPERATURE, ): @@ -119,16 +121,14 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity): if hvac_mode == HVACMode.OFF: await self.async_set_temperature(temperature=OFF_REPORT_SET_TEMPERATURE) else: - await self.async_set_temperature( - temperature=self.entity.comfort_temperature - ) + await self.async_set_temperature(temperature=self.data.comfort_temperature) @property def preset_mode(self) -> str | None: """Return current preset mode.""" - if self.entity.target_temperature == self.entity.comfort_temperature: + if self.data.target_temperature == self.data.comfort_temperature: return PRESET_COMFORT - if self.entity.target_temperature == self.entity.eco_temperature: + if self.data.target_temperature == self.data.eco_temperature: return PRESET_ECO return None @@ -140,11 +140,9 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity): async def async_set_preset_mode(self, preset_mode: str) -> None: """Set preset mode.""" if preset_mode == PRESET_COMFORT: - await self.async_set_temperature( - temperature=self.entity.comfort_temperature - ) + await self.async_set_temperature(temperature=self.data.comfort_temperature) elif preset_mode == PRESET_ECO: - await self.async_set_temperature(temperature=self.entity.eco_temperature) + await self.async_set_temperature(temperature=self.data.eco_temperature) @property def min_temp(self) -> int: @@ -160,17 +158,17 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity): def extra_state_attributes(self) -> ClimateExtraAttributes: """Return the device specific state attributes.""" attrs: ClimateExtraAttributes = { - ATTR_STATE_BATTERY_LOW: self.entity.battery_low, + ATTR_STATE_BATTERY_LOW: self.data.battery_low, } # the following attributes are available since fritzos 7 - if self.entity.battery_level is not None: - attrs[ATTR_BATTERY_LEVEL] = self.entity.battery_level - if self.entity.holiday_active is not None: - attrs[ATTR_STATE_HOLIDAY_MODE] = self.entity.holiday_active - if self.entity.summer_active is not None: - attrs[ATTR_STATE_SUMMER_MODE] = self.entity.summer_active - if self.entity.window_open is not None: - attrs[ATTR_STATE_WINDOW_OPEN] = self.entity.window_open + if self.data.battery_level is not None: + attrs[ATTR_BATTERY_LEVEL] = self.data.battery_level + if self.data.holiday_active is not None: + attrs[ATTR_STATE_HOLIDAY_MODE] = self.data.holiday_active + if self.data.summer_active is not None: + attrs[ATTR_STATE_SUMMER_MODE] = self.data.summer_active + if self.data.window_open is not None: + attrs[ATTR_STATE_WINDOW_OPEN] = self.data.window_open return attrs diff --git a/homeassistant/components/fritzbox/cover.py b/homeassistant/components/fritzbox/cover.py index 78389fe3c99..df3b1562f9b 100644 --- a/homeassistant/components/fritzbox/cover.py +++ b/homeassistant/components/fritzbox/cover.py @@ -13,7 +13,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import FritzBoxDeviceEntity +from . import FritzboxDataUpdateCoordinator, FritzBoxDeviceEntity from .const import CONF_COORDINATOR, DOMAIN as FRITZBOX_DOMAIN @@ -21,7 +21,9 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the FRITZ!SmartHome cover from ConfigEntry.""" - coordinator = hass.data[FRITZBOX_DOMAIN][entry.entry_id][CONF_COORDINATOR] + coordinator: FritzboxDataUpdateCoordinator = hass.data[FRITZBOX_DOMAIN][ + entry.entry_id + ][CONF_COORDINATOR] async_add_entities( FritzboxCover(coordinator, ain) @@ -45,34 +47,34 @@ class FritzboxCover(FritzBoxDeviceEntity, CoverEntity): def current_cover_position(self) -> int | None: """Return the current position.""" position = None - if self.entity.levelpercentage is not None: - position = 100 - self.entity.levelpercentage + if self.data.levelpercentage is not None: + position = 100 - self.data.levelpercentage return position @property def is_closed(self) -> bool | None: """Return if the cover is closed.""" - if self.entity.levelpercentage is None: + if self.data.levelpercentage is None: return None - return self.entity.levelpercentage == 100 # type: ignore [no-any-return] + return self.data.levelpercentage == 100 # type: ignore [no-any-return] async def async_open_cover(self, **kwargs: Any) -> None: """Open the cover.""" - await self.hass.async_add_executor_job(self.entity.set_blind_open) + await self.hass.async_add_executor_job(self.data.set_blind_open) await self.coordinator.async_refresh() async def async_close_cover(self, **kwargs: Any) -> None: """Close the cover.""" - await self.hass.async_add_executor_job(self.entity.set_blind_close) + await self.hass.async_add_executor_job(self.data.set_blind_close) await self.coordinator.async_refresh() async def async_set_cover_position(self, **kwargs: Any) -> None: """Move the cover to a specific position.""" await self.hass.async_add_executor_job( - self.entity.set_level_percentage, 100 - kwargs[ATTR_POSITION] + self.data.set_level_percentage, 100 - kwargs[ATTR_POSITION] ) async def async_stop_cover(self, **kwargs: Any) -> None: """Stop the cover.""" - await self.hass.async_add_executor_job(self.entity.set_blind_stop) + await self.hass.async_add_executor_job(self.data.set_blind_stop) await self.coordinator.async_refresh() diff --git a/homeassistant/components/fritzbox/light.py b/homeassistant/components/fritzbox/light.py index a63d3e06008..5053eda69d7 100644 --- a/homeassistant/components/fritzbox/light.py +++ b/homeassistant/components/fritzbox/light.py @@ -16,7 +16,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import FritzBoxDeviceEntity +from . import FritzboxDataUpdateCoordinator, FritzBoxDeviceEntity from .const import ( COLOR_MODE, COLOR_TEMP_MODE, @@ -24,7 +24,6 @@ from .const import ( DOMAIN as FRITZBOX_DOMAIN, LOGGER, ) -from .coordinator import FritzboxDataUpdateCoordinator SUPPORTED_COLOR_MODES = {ColorMode.COLOR_TEMP, ColorMode.HS} @@ -34,7 +33,9 @@ async def async_setup_entry( ) -> None: """Set up the FRITZ!SmartHome light from ConfigEntry.""" entities: list[FritzboxLight] = [] - coordinator = hass.data[FRITZBOX_DOMAIN][entry.entry_id][CONF_COORDINATOR] + coordinator: FritzboxDataUpdateCoordinator = hass.data[FRITZBOX_DOMAIN][ + entry.entry_id + ][CONF_COORDINATOR] for ain, device in coordinator.data.devices.items(): if not device.has_lightbulb: @@ -88,36 +89,36 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity): @property def is_on(self) -> bool: """If the light is currently on or off.""" - return self.entity.state # type: ignore [no-any-return] + return self.data.state # type: ignore [no-any-return] @property def brightness(self) -> int: """Return the current Brightness.""" - return self.entity.level # type: ignore [no-any-return] + return self.data.level # type: ignore [no-any-return] @property def hs_color(self) -> tuple[float, float] | None: """Return the hs color value.""" - if self.entity.color_mode != COLOR_MODE: + if self.data.color_mode != COLOR_MODE: return None - hue = self.entity.hue - saturation = self.entity.saturation + hue = self.data.hue + saturation = self.data.saturation return (hue, float(saturation) * 100.0 / 255.0) @property def color_temp_kelvin(self) -> int | None: """Return the CT color value.""" - if self.entity.color_mode != COLOR_TEMP_MODE: + if self.data.color_mode != COLOR_TEMP_MODE: return None - return self.entity.color_temp # type: ignore [no-any-return] + return self.data.color_temp # type: ignore [no-any-return] @property def color_mode(self) -> ColorMode: """Return the color mode of the light.""" - if self.entity.color_mode == COLOR_MODE: + if self.data.color_mode == COLOR_MODE: return ColorMode.HS return ColorMode.COLOR_TEMP @@ -130,7 +131,7 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity): """Turn the light on.""" if kwargs.get(ATTR_BRIGHTNESS) is not None: level = kwargs[ATTR_BRIGHTNESS] - await self.hass.async_add_executor_job(self.entity.set_level, level) + await self.hass.async_add_executor_job(self.data.set_level, level) if kwargs.get(ATTR_HS_COLOR) is not None: # Try setunmappedcolor first. This allows free color selection, # but we don't know if its supported by all devices. @@ -139,7 +140,7 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity): unmapped_hue = int(kwargs[ATTR_HS_COLOR][0] % 360) unmapped_saturation = round(kwargs[ATTR_HS_COLOR][1] * 255.0 / 100.0) await self.hass.async_add_executor_job( - self.entity.set_unmapped_color, (unmapped_hue, unmapped_saturation) + self.data.set_unmapped_color, (unmapped_hue, unmapped_saturation) ) # This will raise 400 BAD REQUEST if the setunmappedcolor is not available except HTTPError as err: @@ -157,18 +158,18 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity): key=lambda x: abs(x - unmapped_saturation), ) await self.hass.async_add_executor_job( - self.entity.set_color, (hue, saturation) + self.data.set_color, (hue, saturation) ) if kwargs.get(ATTR_COLOR_TEMP_KELVIN) is not None: await self.hass.async_add_executor_job( - self.entity.set_color_temp, kwargs[ATTR_COLOR_TEMP_KELVIN] + self.data.set_color_temp, kwargs[ATTR_COLOR_TEMP_KELVIN] ) - await self.hass.async_add_executor_job(self.entity.set_state_on) + await self.hass.async_add_executor_job(self.data.set_state_on) await self.coordinator.async_refresh() async def async_turn_off(self, **kwargs: Any) -> None: """Turn the light off.""" - await self.hass.async_add_executor_job(self.entity.set_state_off) + await self.hass.async_add_executor_job(self.data.set_state_off) await self.coordinator.async_refresh() diff --git a/homeassistant/components/fritzbox/sensor.py b/homeassistant/components/fritzbox/sensor.py index 9a86ffae5ed..9d68821fdca 100644 --- a/homeassistant/components/fritzbox/sensor.py +++ b/homeassistant/components/fritzbox/sensor.py @@ -235,4 +235,4 @@ class FritzBoxSensor(FritzBoxDeviceEntity, SensorEntity): @property def native_value(self) -> StateType | datetime: """Return the state of the sensor.""" - return self.entity_description.native_value(self.entity) + return self.entity_description.native_value(self.data) diff --git a/homeassistant/components/fritzbox/switch.py b/homeassistant/components/fritzbox/switch.py index 029627e191c..5eee3019633 100644 --- a/homeassistant/components/fritzbox/switch.py +++ b/homeassistant/components/fritzbox/switch.py @@ -8,7 +8,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import FritzBoxDeviceEntity +from . import FritzboxDataUpdateCoordinator, FritzBoxDeviceEntity from .const import CONF_COORDINATOR, DOMAIN as FRITZBOX_DOMAIN @@ -16,7 +16,9 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the FRITZ!SmartHome switch from ConfigEntry.""" - coordinator = hass.data[FRITZBOX_DOMAIN][entry.entry_id][CONF_COORDINATOR] + coordinator: FritzboxDataUpdateCoordinator = hass.data[FRITZBOX_DOMAIN][ + entry.entry_id + ][CONF_COORDINATOR] async_add_entities( [ @@ -33,14 +35,14 @@ class FritzboxSwitch(FritzBoxDeviceEntity, SwitchEntity): @property def is_on(self) -> bool: """Return true if the switch is on.""" - return self.entity.switch_state # type: ignore [no-any-return] + return self.data.switch_state # type: ignore [no-any-return] async def async_turn_on(self, **kwargs: Any) -> None: """Turn the switch on.""" - await self.hass.async_add_executor_job(self.entity.set_switch_state_on) + await self.hass.async_add_executor_job(self.data.set_switch_state_on) await self.coordinator.async_refresh() async def async_turn_off(self, **kwargs: Any) -> None: """Turn the switch off.""" - await self.hass.async_add_executor_job(self.entity.set_switch_state_off) + await self.hass.async_add_executor_job(self.data.set_switch_state_off) await self.coordinator.async_refresh() From 14f2649ae21cc1349afc761879d84f0096df441a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Klomp?= Date: Sat, 19 Nov 2022 16:05:38 +0100 Subject: [PATCH 0562/1033] Bump pysma to version 0.7.3 (#82343) fixes undefined --- homeassistant/components/sma/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sma/manifest.json b/homeassistant/components/sma/manifest.json index 83bf4258a95..f4e82a550e3 100644 --- a/homeassistant/components/sma/manifest.json +++ b/homeassistant/components/sma/manifest.json @@ -3,7 +3,7 @@ "name": "SMA Solar", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/sma", - "requirements": ["pysma==0.7.2"], + "requirements": ["pysma==0.7.3"], "codeowners": ["@kellerza", "@rklomp"], "iot_class": "local_polling", "loggers": ["pysma"] diff --git a/requirements_all.txt b/requirements_all.txt index 735259d3c95..17a9550b1c5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1910,7 +1910,7 @@ pysignalclirestapi==0.3.18 pyskyqhub==0.1.4 # homeassistant.components.sma -pysma==0.7.2 +pysma==0.7.3 # homeassistant.components.smappee pysmappee==0.2.29 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6279f5f267d..31c780304bd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1351,7 +1351,7 @@ pysiaalarm==3.0.2 pysignalclirestapi==0.3.18 # homeassistant.components.sma -pysma==0.7.2 +pysma==0.7.3 # homeassistant.components.smappee pysmappee==0.2.29 From e0285169396e7187b16f83d7f9d199268ce0fe1c Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Sat, 19 Nov 2022 16:57:18 +0100 Subject: [PATCH 0563/1033] Use unit enums in nibe heatpump (#82370) Use unit enums in nibe --- .../components/nibe_heatpump/sensor.py | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/nibe_heatpump/sensor.py b/homeassistant/components/nibe_heatpump/sensor.py index 0b12afd9e05..c092464b1bf 100644 --- a/homeassistant/components/nibe_heatpump/sensor.py +++ b/homeassistant/components/nibe_heatpump/sensor.py @@ -16,14 +16,10 @@ from homeassistant.const import ( ELECTRIC_CURRENT_MILLIAMPERE, ELECTRIC_POTENTIAL_MILLIVOLT, ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, - ENERGY_WATT_HOUR, - POWER_KILO_WATT, - POWER_WATT, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, TIME_HOURS, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -37,14 +33,14 @@ UNIT_DESCRIPTIONS = { entity_category=EntityCategory.DIAGNOSTIC, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), "°F": SensorEntityDescription( key="°F", entity_category=EntityCategory.DIAGNOSTIC, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, ), "A": SensorEntityDescription( key="A", @@ -79,35 +75,35 @@ UNIT_DESCRIPTIONS = { entity_category=EntityCategory.DIAGNOSTIC, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), "kW": SensorEntityDescription( key="kW", entity_category=EntityCategory.DIAGNOSTIC, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, ), "Wh": SensorEntityDescription( key="Wh", entity_category=EntityCategory.DIAGNOSTIC, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, ), "kWh": SensorEntityDescription( key="kWh", entity_category=EntityCategory.DIAGNOSTIC, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), "MWh": SensorEntityDescription( key="MWh", entity_category=EntityCategory.DIAGNOSTIC, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_MEGA_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR, ), "h": SensorEntityDescription( key="h", From ea98f0e9e8d5c66a73a539d5930c045478e41320 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Sat, 19 Nov 2022 18:07:02 +0200 Subject: [PATCH 0564/1033] Add tests for Shelly binary_sensor platform (#82367) * Add tests for Shelly binary_sensor platform * Rename create to register for --- .coveragerc | 1 - .../components/shelly/binary_sensor.py | 10 +- tests/components/shelly/__init__.py | 60 ++++++- tests/components/shelly/conftest.py | 18 +- tests/components/shelly/test_binary_sensor.py | 155 ++++++++++++++++++ tests/components/shelly/test_update.py | 38 +---- 6 files changed, 238 insertions(+), 44 deletions(-) create mode 100644 tests/components/shelly/test_binary_sensor.py diff --git a/.coveragerc b/.coveragerc index e75460f742f..0bfd1fbbbeb 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1109,7 +1109,6 @@ omit = homeassistant/components/sesame/lock.py homeassistant/components/seven_segments/image_processing.py homeassistant/components/seventeentrack/sensor.py - homeassistant/components/shelly/binary_sensor.py homeassistant/components/shelly/climate.py homeassistant/components/shelly/coordinator.py homeassistant/components/shelly/entity.py diff --git a/homeassistant/components/shelly/binary_sensor.py b/homeassistant/components/shelly/binary_sensor.py index cfacdf85cfd..a5265241da3 100644 --- a/homeassistant/components/shelly/binary_sensor.py +++ b/homeassistant/components/shelly/binary_sensor.py @@ -268,10 +268,8 @@ class RestBinarySensor(ShellyRestAttributeEntity, BinarySensorEntity): entity_description: RestBinarySensorDescription @property - def is_on(self) -> bool | None: + def is_on(self) -> bool: """Return true if REST sensor state is on.""" - if self.attribute_value is None: - return None return bool(self.attribute_value) @@ -281,10 +279,8 @@ class RpcBinarySensor(ShellyRpcAttributeEntity, BinarySensorEntity): entity_description: RpcBinarySensorDescription @property - def is_on(self) -> bool | None: + def is_on(self) -> bool: """Return true if RPC sensor state is on.""" - if self.attribute_value is None: - return None return bool(self.attribute_value) @@ -308,7 +304,7 @@ class RpcSleepingBinarySensor(ShellySleepingRpcAttributeEntity, BinarySensorEnti entity_description: RpcBinarySensorDescription @property - def is_on(self) -> bool | None: + def is_on(self) -> bool: """Return true if RPC sensor state is on.""" if self.coordinator.device.initialized: return bool(self.attribute_value) diff --git a/tests/components/shelly/__init__.py b/tests/components/shelly/__init__.py index a844367bced..36165afe72d 100644 --- a/tests/components/shelly/__init__.py +++ b/tests/components/shelly/__init__.py @@ -2,16 +2,25 @@ from __future__ import annotations from copy import deepcopy +from datetime import timedelta from typing import Any from unittest.mock import Mock import pytest -from homeassistant.components.shelly.const import CONF_SLEEP_PERIOD, DOMAIN +from homeassistant.components.shelly.const import ( + CONF_SLEEP_PERIOD, + DOMAIN, + REST_SENSORS_UPDATE_INTERVAL, +) +from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry +from homeassistant.helpers.entity_registry import async_get +from homeassistant.util import dt -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, async_fire_time_changed MOCK_MAC = "123456789ABC" @@ -22,6 +31,7 @@ async def init_integration( model="SHSW-25", sleep_period=0, options: dict[str, Any] | None = None, + skip_setup: bool = False, ) -> MockConfigEntry: """Set up the Shelly integration in Home Assistant.""" data = { @@ -36,8 +46,9 @@ async def init_integration( ) entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() + if not skip_setup: + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() return entry @@ -63,3 +74,44 @@ def inject_rpc_device_event( """Inject event for rpc device.""" monkeypatch.setattr(mock_rpc_device, "event", event) mock_rpc_device.mock_event() + + +async def mock_rest_update(hass: HomeAssistant): + """Move time to create REST sensors update event.""" + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL) + ) + await hass.async_block_till_done() + + +def register_entity( + hass: HomeAssistant, + domain: str, + object_id: str, + unique_id: str, + config_entry: ConfigEntry | None = None, +) -> str: + """Register enabled entity, return entity_id.""" + entity_registry = async_get(hass) + entity_registry.async_get_or_create( + domain, + DOMAIN, + f"{MOCK_MAC}-{unique_id}", + suggested_object_id=object_id, + disabled_by=None, + config_entry=config_entry, + ) + return f"{domain}.{object_id}" + + +def register_device(device_reg, config_entry: ConfigEntry): + """Register Shelly device.""" + device_reg.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections={ + ( + device_registry.CONNECTION_NETWORK_MAC, + device_registry.format_mac(MOCK_MAC), + ) + }, + ) diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py index f27f91cbfe7..5e1655c7d46 100644 --- a/tests/components/shelly/conftest.py +++ b/tests/components/shelly/conftest.py @@ -63,9 +63,11 @@ def mock_light_set_state( MOCK_BLOCKS = [ Mock( - sensor_ids={"inputEvent": "S", "inputEventCnt": 2}, + sensor_ids={"inputEvent": "S", "inputEventCnt": 2, "overpower": 0}, channel="0", type="relay", + overpower=0, + description="relay_0", set_state=AsyncMock(side_effect=lambda turn: {"ison": turn == "on"}), ), Mock( @@ -88,6 +90,12 @@ MOCK_BLOCKS = [ type="light", set_state=AsyncMock(side_effect=mock_light_set_state), ), + Mock( + sensor_ids={"motion": 0}, + motion=0, + description="sensor_0", + type="sensor", + ), ] MOCK_CONFIG = { @@ -135,7 +143,13 @@ MOCK_STATUS_COAP = { MOCK_STATUS_RPC = { "switch:0": {"output": True}, - "cover:0": {"state": "stopped", "pos_control": True, "current_pos": 50}, + "cloud": {"connected": False}, + "cover:0": { + "state": "stopped", + "pos_control": True, + "current_pos": 50, + "apower": 85.3, + }, "sys": { "available_updates": { "beta": {"version": "some_beta_version"}, diff --git a/tests/components/shelly/test_binary_sensor.py b/tests/components/shelly/test_binary_sensor.py new file mode 100644 index 00000000000..c7e2faaf47c --- /dev/null +++ b/tests/components/shelly/test_binary_sensor.py @@ -0,0 +1,155 @@ +"""Tests for Shelly binary sensor platform.""" + + +from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN +from homeassistant.const import STATE_OFF, STATE_ON +from homeassistant.core import State + +from . import ( + init_integration, + mock_rest_update, + mutate_rpc_device_status, + register_device, + register_entity, +) + +from tests.common import mock_restore_cache + +RELAY_BLOCK_ID = 0 +SENSOR_BLOCK_ID = 3 + + +async def test_block_binary_sensor(hass, mock_block_device, monkeypatch): + """Test block binary sensor.""" + entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_channel_1_overpowering" + await init_integration(hass, 1) + + assert hass.states.get(entity_id).state == STATE_OFF + + monkeypatch.setattr(mock_block_device.blocks[RELAY_BLOCK_ID], "overpower", 1) + mock_block_device.mock_update() + + assert hass.states.get(entity_id).state == STATE_ON + + +async def test_block_rest_binary_sensor(hass, mock_block_device, monkeypatch): + """Test block REST binary sensor.""" + entity_id = register_entity(hass, BINARY_SENSOR_DOMAIN, "test_name_cloud", "cloud") + monkeypatch.setitem(mock_block_device.status, "cloud", {"connected": False}) + await init_integration(hass, 1) + + assert hass.states.get(entity_id).state == STATE_OFF + + monkeypatch.setitem(mock_block_device.status["cloud"], "connected", True) + await mock_rest_update(hass) + + assert hass.states.get(entity_id).state == STATE_ON + + +async def test_block_sleeping_binary_sensor(hass, mock_block_device, monkeypatch): + """Test block sleeping binary sensor.""" + entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_motion" + await init_integration(hass, 1, sleep_period=1000) + + # Sensor should be created when device is online + assert hass.states.get(entity_id) is None + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == STATE_OFF + + monkeypatch.setattr(mock_block_device.blocks[SENSOR_BLOCK_ID], "motion", 1) + mock_block_device.mock_update() + + assert hass.states.get(entity_id).state == STATE_ON + + +async def test_block_restored_sleeping_binary_sensor( + hass, mock_block_device, device_reg, monkeypatch +): + """Test block restored sleeping binary sensor.""" + entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True) + register_device(device_reg, entry) + entity_id = register_entity( + hass, BINARY_SENSOR_DOMAIN, "test_name_motion", "sensor_0-motion", entry + ) + mock_restore_cache(hass, [State(entity_id, STATE_ON)]) + monkeypatch.setattr(mock_block_device, "initialized", False) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == STATE_ON + + # Make device online + monkeypatch.setattr(mock_block_device, "initialized", True) + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == STATE_OFF + + +async def test_rpc_binary_sensor(hass, mock_rpc_device, monkeypatch) -> None: + """Test RPC binary sensor.""" + entity_id = f"{BINARY_SENSOR_DOMAIN}.test_cover_0_overpowering" + await init_integration(hass, 2) + + assert hass.states.get(entity_id).state == STATE_OFF + + mutate_rpc_device_status( + monkeypatch, mock_rpc_device, "cover:0", "errors", "overpower" + ) + mock_rpc_device.mock_update() + + assert hass.states.get(entity_id).state == STATE_ON + + +async def test_rpc_sleeping_binary_sensor( + hass, mock_rpc_device, device_reg, monkeypatch +) -> None: + """Test RPC online sleeping binary sensor.""" + entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_cloud" + entry = await init_integration(hass, 2, sleep_period=1000) + + # Sensor should be created when device is online + assert hass.states.get(entity_id) is None + + register_entity(hass, BINARY_SENSOR_DOMAIN, "test_name_cloud", "cloud-cloud", entry) + + # Make device online + mock_rpc_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == STATE_OFF + + mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cloud", "connected", True) + mock_rpc_device.mock_update() + + assert hass.states.get(entity_id).state == STATE_ON + + +async def test_rpc_restored_sleeping_binary_sensor( + hass, mock_rpc_device, device_reg, monkeypatch +): + """Test RPC restored binary sensor.""" + entry = await init_integration(hass, 2, sleep_period=1000, skip_setup=True) + register_device(device_reg, entry) + entity_id = register_entity( + hass, BINARY_SENSOR_DOMAIN, "test_name_cloud", "cloud-cloud", entry + ) + + mock_restore_cache(hass, [State(entity_id, STATE_ON)]) + monkeypatch.setattr(mock_rpc_device, "initialized", False) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == STATE_ON + + # Make device online + monkeypatch.setattr(mock_rpc_device, "initialized", True) + mock_rpc_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == STATE_OFF diff --git a/tests/components/shelly/test_update.py b/tests/components/shelly/test_update.py index 4da81e076ae..fe7d979e797 100644 --- a/tests/components/shelly/test_update.py +++ b/tests/components/shelly/test_update.py @@ -1,5 +1,4 @@ """Tests for Shelly update platform.""" -from datetime import timedelta from unittest.mock import AsyncMock from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError @@ -7,7 +6,7 @@ import pytest from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN -from homeassistant.components.shelly.const import DOMAIN, REST_SENSORS_UPDATE_INTERVAL +from homeassistant.components.shelly.const import DOMAIN from homeassistant.components.update import ( ATTR_IN_PROGRESS, ATTR_INSTALLED_VERSION, @@ -20,11 +19,8 @@ from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_registry import async_get -from homeassistant.util import dt -from . import MOCK_MAC, init_integration - -from tests.common import async_fire_time_changed +from . import MOCK_MAC, init_integration, mock_rest_update @pytest.mark.parametrize( @@ -100,10 +96,7 @@ async def test_block_update(hass: HomeAssistant, mock_block_device, monkeypatch) assert state.attributes[ATTR_IN_PROGRESS] is True monkeypatch.setitem(mock_block_device.status["update"], "old_version", "2") - async_fire_time_changed( - hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL) - ) - await hass.async_block_till_done() + await mock_rest_update(hass) state = hass.states.get("update.test_name_firmware_update") assert state.state == STATE_OFF @@ -134,10 +127,7 @@ async def test_block_beta_update(hass: HomeAssistant, mock_block_device, monkeyp assert state.attributes[ATTR_IN_PROGRESS] is False monkeypatch.setitem(mock_block_device.status["update"], "beta_version", "2b") - async_fire_time_changed( - hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL) - ) - await hass.async_block_till_done() + await mock_rest_update(hass) state = hass.states.get("update.test_name_beta_firmware_update") assert state.state == STATE_ON @@ -160,10 +150,7 @@ async def test_block_beta_update(hass: HomeAssistant, mock_block_device, monkeyp assert state.attributes[ATTR_IN_PROGRESS] is True monkeypatch.setitem(mock_block_device.status["update"], "old_version", "2b") - async_fire_time_changed( - hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL) - ) - await hass.async_block_till_done() + await mock_rest_update(hass) state = hass.states.get("update.test_name_beta_firmware_update") assert state.state == STATE_OFF @@ -288,10 +275,7 @@ async def test_rpc_update(hass: HomeAssistant, mock_rpc_device, monkeypatch): assert state.attributes[ATTR_IN_PROGRESS] is True monkeypatch.setitem(mock_rpc_device.shelly, "ver", "2") - async_fire_time_changed( - hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL) - ) - await hass.async_block_till_done() + await mock_rest_update(hass) state = hass.states.get("update.test_name_firmware_update") assert state.state == STATE_OFF @@ -335,10 +319,7 @@ async def test_rpc_beta_update(hass: HomeAssistant, mock_rpc_device, monkeypatch "beta": {"version": "2b"}, }, ) - async_fire_time_changed( - hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL) - ) - await hass.async_block_till_done() + await mock_rest_update(hass) state = hass.states.get("update.test_name_beta_firmware_update") assert state.state == STATE_ON @@ -361,10 +342,7 @@ async def test_rpc_beta_update(hass: HomeAssistant, mock_rpc_device, monkeypatch assert state.attributes[ATTR_IN_PROGRESS] is True monkeypatch.setitem(mock_rpc_device.shelly, "ver", "2b") - async_fire_time_changed( - hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL) - ) - await hass.async_block_till_done() + await mock_rest_update(hass) state = hass.states.get("update.test_name_beta_firmware_update") assert state.state == STATE_OFF From 6de887b9b912d69b06b943bfa8115a901986d036 Mon Sep 17 00:00:00 2001 From: Florent Thoumie Date: Sat, 19 Nov 2022 11:48:45 -0800 Subject: [PATCH 0565/1033] iaqualink: fix supported_effects reference after function was renamed (#82383) Fix supported_effects reference --- homeassistant/components/iaqualink/light.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/iaqualink/light.py b/homeassistant/components/iaqualink/light.py index 15800419ed5..853660c10bf 100644 --- a/homeassistant/components/iaqualink/light.py +++ b/homeassistant/components/iaqualink/light.py @@ -85,7 +85,7 @@ class HassAqualinkLight(AqualinkEntity, LightEntity): @property def effect_list(self) -> list: """Return supported light effects.""" - return list(self.dev.supported_light_effects) + return list(self.dev.supported_effects) @property def color_mode(self) -> ColorMode: From 8bd7f59b93aa5cf1780d1c3e9a0c7547ca55e938 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Sat, 19 Nov 2022 22:01:30 +0200 Subject: [PATCH 0566/1033] Add tests for Shelly sensor platform (#82385) --- .coveragerc | 1 - tests/components/shelly/conftest.py | 13 +- tests/components/shelly/test_sensor.py | 161 +++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 tests/components/shelly/test_sensor.py diff --git a/.coveragerc b/.coveragerc index 0bfd1fbbbeb..426fd3a6634 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1113,7 +1113,6 @@ omit = homeassistant/components/shelly/coordinator.py homeassistant/components/shelly/entity.py homeassistant/components/shelly/number.py - homeassistant/components/shelly/sensor.py homeassistant/components/shelly/utils.py homeassistant/components/shiftr/* homeassistant/components/shodan/sensor.py diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py index 5e1655c7d46..0264e675674 100644 --- a/tests/components/shelly/conftest.py +++ b/tests/components/shelly/conftest.py @@ -63,10 +63,16 @@ def mock_light_set_state( MOCK_BLOCKS = [ Mock( - sensor_ids={"inputEvent": "S", "inputEventCnt": 2, "overpower": 0}, + sensor_ids={ + "inputEvent": "S", + "inputEventCnt": 2, + "overpower": 0, + "power": 53.4, + }, channel="0", type="relay", overpower=0, + power=53.4, description="relay_0", set_state=AsyncMock(side_effect=lambda turn: {"ison": turn == "on"}), ), @@ -91,8 +97,9 @@ MOCK_BLOCKS = [ set_state=AsyncMock(side_effect=mock_light_set_state), ), Mock( - sensor_ids={"motion": 0}, + sensor_ids={"motion": 0, "temp": 22.1}, motion=0, + temp=22.1, description="sensor_0", type="sensor", ), @@ -138,6 +145,7 @@ MOCK_STATUS_COAP = { "old_version": "some_old_version", }, "uptime": 5 * REST_SENSORS_UPDATE_INTERVAL, + "wifi_sta": {"rssi": -64}, } @@ -150,6 +158,7 @@ MOCK_STATUS_RPC = { "current_pos": 50, "apower": 85.3, }, + "temperature:0": {"tC": 22.9}, "sys": { "available_updates": { "beta": {"version": "some_beta_version"}, diff --git a/tests/components/shelly/test_sensor.py b/tests/components/shelly/test_sensor.py new file mode 100644 index 00000000000..96c7b0e9cc3 --- /dev/null +++ b/tests/components/shelly/test_sensor.py @@ -0,0 +1,161 @@ +"""Tests for Shelly sensor platform.""" + + +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.core import State + +from . import ( + init_integration, + mock_rest_update, + mutate_rpc_device_status, + register_device, + register_entity, +) + +from tests.common import mock_restore_cache + +RELAY_BLOCK_ID = 0 +SENSOR_BLOCK_ID = 3 + + +async def test_block_sensor(hass, mock_block_device, monkeypatch): + """Test block sensor.""" + entity_id = f"{SENSOR_DOMAIN}.test_name_channel_1_power" + await init_integration(hass, 1) + + assert hass.states.get(entity_id).state == "53.4" + + monkeypatch.setattr(mock_block_device.blocks[RELAY_BLOCK_ID], "power", 60.1) + mock_block_device.mock_update() + + assert hass.states.get(entity_id).state == "60.1" + + +async def test_block_rest_sensor(hass, mock_block_device, monkeypatch): + """Test block REST sensor.""" + entity_id = register_entity(hass, SENSOR_DOMAIN, "test_name_rssi", "rssi") + await init_integration(hass, 1) + + assert hass.states.get(entity_id).state == "-64" + + monkeypatch.setitem(mock_block_device.status["wifi_sta"], "rssi", -71) + await mock_rest_update(hass) + + assert hass.states.get(entity_id).state == "-71" + + +async def test_block_sleeping_sensor(hass, mock_block_device, monkeypatch): + """Test block sleeping sensor.""" + entity_id = f"{SENSOR_DOMAIN}.test_name_temperature" + await init_integration(hass, 1, sleep_period=1000) + + # Sensor should be created when device is online + assert hass.states.get(entity_id) is None + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == "22.1" + + monkeypatch.setattr(mock_block_device.blocks[SENSOR_BLOCK_ID], "temp", 23.4) + mock_block_device.mock_update() + + assert hass.states.get(entity_id).state == "23.4" + + +async def test_block_restored_sleeping_sensor( + hass, mock_block_device, device_reg, monkeypatch +): + """Test block restored sleeping sensor.""" + entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True) + register_device(device_reg, entry) + entity_id = register_entity( + hass, SENSOR_DOMAIN, "test_name_temperature", "sensor_0-temp", entry + ) + mock_restore_cache(hass, [State(entity_id, "20.4")]) + monkeypatch.setattr(mock_block_device, "initialized", False) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == "20.4" + + # Make device online + monkeypatch.setattr(mock_block_device, "initialized", True) + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == "22.1" + + +async def test_rpc_sensor(hass, mock_rpc_device, monkeypatch) -> None: + """Test RPC sensor.""" + entity_id = f"{SENSOR_DOMAIN}.test_cover_0_power" + await init_integration(hass, 2) + + assert hass.states.get(entity_id).state == "85.3" + + mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cover:0", "apower", "88.2") + mock_rpc_device.mock_update() + + assert hass.states.get(entity_id).state == "88.2" + + +async def test_rpc_sleeping_sensor( + hass, mock_rpc_device, device_reg, monkeypatch +) -> None: + """Test RPC online sleeping sensor.""" + entity_id = f"{SENSOR_DOMAIN}.test_name_temperature" + entry = await init_integration(hass, 2, sleep_period=1000) + + # Sensor should be created when device is online + assert hass.states.get(entity_id) is None + + register_entity( + hass, + SENSOR_DOMAIN, + "test_name_temperature", + "temperature:0-temperature_0", + entry, + ) + + # Make device online + mock_rpc_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == "22.9" + + mutate_rpc_device_status(monkeypatch, mock_rpc_device, "temperature:0", "tC", 23.4) + mock_rpc_device.mock_update() + + assert hass.states.get(entity_id).state == "23.4" + + +async def test_rpc_restored_sleeping_sensor( + hass, mock_rpc_device, device_reg, monkeypatch +): + """Test RPC restored sensor.""" + entry = await init_integration(hass, 2, sleep_period=1000, skip_setup=True) + register_device(device_reg, entry) + entity_id = register_entity( + hass, + SENSOR_DOMAIN, + "test_name_temperature", + "temperature:0-temperature_0", + entry, + ) + + mock_restore_cache(hass, [State(entity_id, "21.0")]) + monkeypatch.setattr(mock_rpc_device, "initialized", False) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == "21.0" + + # Make device online + monkeypatch.setattr(mock_rpc_device, "initialized", True) + mock_rpc_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == "22.9" From 38b84620bdbf241a6b246b386dbc1cee21e95472 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 19 Nov 2022 22:26:54 +0200 Subject: [PATCH 0567/1033] Add support for Sensirion BLE sensors (#82382) --- .strict-typing | 1 + CODEOWNERS | 2 + .../components/sensirion_ble/__init__.py | 49 +++++ .../components/sensirion_ble/config_flow.py | 94 ++++++++ .../components/sensirion_ble/const.py | 3 + .../components/sensirion_ble/manifest.json | 18 ++ .../components/sensirion_ble/sensor.py | 131 +++++++++++ homeassistant/generated/bluetooth.py | 8 + homeassistant/generated/config_flows.py | 1 + homeassistant/generated/integrations.json | 6 + mypy.ini | 10 + requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/sensirion_ble/__init__.py | 1 + tests/components/sensirion_ble/fixtures.py | 26 +++ .../sensirion_ble/test_config_flow.py | 206 ++++++++++++++++++ tests/components/sensirion_ble/test_sensor.py | 45 ++++ 17 files changed, 607 insertions(+) create mode 100644 homeassistant/components/sensirion_ble/__init__.py create mode 100644 homeassistant/components/sensirion_ble/config_flow.py create mode 100644 homeassistant/components/sensirion_ble/const.py create mode 100644 homeassistant/components/sensirion_ble/manifest.json create mode 100644 homeassistant/components/sensirion_ble/sensor.py create mode 100644 tests/components/sensirion_ble/__init__.py create mode 100644 tests/components/sensirion_ble/fixtures.py create mode 100644 tests/components/sensirion_ble/test_config_flow.py create mode 100644 tests/components/sensirion_ble/test_sensor.py diff --git a/.strict-typing b/.strict-typing index 08a167855d1..44e303152ce 100644 --- a/.strict-typing +++ b/.strict-typing @@ -238,6 +238,7 @@ homeassistant.components.schedule.* homeassistant.components.select.* homeassistant.components.senseme.* homeassistant.components.sensibo.* +homeassistant.components.sensirion_ble.* homeassistant.components.sensor.* homeassistant.components.senz.* homeassistant.components.shelly.* diff --git a/CODEOWNERS b/CODEOWNERS index 374a3d37a81..3646c36422d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -999,6 +999,8 @@ build.json @home-assistant/supervisor /tests/components/senseme/ @mikelawrence @bdraco /homeassistant/components/sensibo/ @andrey-git @gjohansson-ST /tests/components/sensibo/ @andrey-git @gjohansson-ST +/homeassistant/components/sensirion_ble/ @akx +/tests/components/sensirion_ble/ @akx /homeassistant/components/sensor/ @home-assistant/core /tests/components/sensor/ @home-assistant/core /homeassistant/components/sensorpro/ @bdraco diff --git a/homeassistant/components/sensirion_ble/__init__.py b/homeassistant/components/sensirion_ble/__init__.py new file mode 100644 index 00000000000..66e6f7c250b --- /dev/null +++ b/homeassistant/components/sensirion_ble/__init__.py @@ -0,0 +1,49 @@ +"""The sensirion_ble integration.""" +from __future__ import annotations + +import logging + +from sensirion_ble import SensirionBluetoothDeviceData + +from homeassistant.components.bluetooth import BluetoothScanningMode +from homeassistant.components.bluetooth.passive_update_processor import ( + PassiveBluetoothProcessorCoordinator, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant + +from .const import DOMAIN + +PLATFORMS: list[Platform] = [Platform.SENSOR] + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Sensirion BLE device from a config entry.""" + address = entry.unique_id + assert address is not None + data = SensirionBluetoothDeviceData() + coordinator = hass.data.setdefault(DOMAIN, {})[ + entry.entry_id + ] = PassiveBluetoothProcessorCoordinator( + hass, + _LOGGER, + address=address, + mode=BluetoothScanningMode.ACTIVE, + update_method=data.update, + ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + entry.async_on_unload( + coordinator.async_start() + ) # only start after all platforms have had a chance to subscribe + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_ok diff --git a/homeassistant/components/sensirion_ble/config_flow.py b/homeassistant/components/sensirion_ble/config_flow.py new file mode 100644 index 00000000000..0442b6d16c3 --- /dev/null +++ b/homeassistant/components/sensirion_ble/config_flow.py @@ -0,0 +1,94 @@ +"""Config flow for sensirion_ble.""" +from __future__ import annotations + +from typing import Any + +from sensirion_ble import SensirionBluetoothDeviceData +import voluptuous as vol + +from homeassistant.components.bluetooth import ( + BluetoothServiceInfoBleak, + async_discovered_service_info, +) +from homeassistant.config_entries import ConfigFlow +from homeassistant.const import CONF_ADDRESS +from homeassistant.data_entry_flow import FlowResult + +from .const import DOMAIN + + +class SensirionConfigFlow(ConfigFlow, domain=DOMAIN): + """Handle a config flow for sensirion_ble.""" + + VERSION = 1 + + def __init__(self) -> None: + """Initialize the config flow.""" + self._discovery_info: BluetoothServiceInfoBleak | None = None + self._discovered_device: SensirionBluetoothDeviceData | None = None + self._discovered_devices: dict[str, str] = {} + + async def async_step_bluetooth( + self, discovery_info: BluetoothServiceInfoBleak + ) -> FlowResult: + """Handle the bluetooth discovery step.""" + await self.async_set_unique_id(discovery_info.address) + self._abort_if_unique_id_configured() + device = SensirionBluetoothDeviceData() + if not device.supported(discovery_info): + return self.async_abort(reason="not_supported") + self._discovery_info = discovery_info + self._discovered_device = device + return await self.async_step_bluetooth_confirm() + + async def async_step_bluetooth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Confirm discovery.""" + assert self._discovered_device is not None + device = self._discovered_device + assert self._discovery_info is not None + discovery_info = self._discovery_info + title = device.title or device.get_device_name() or discovery_info.name + if user_input is not None: + return self.async_create_entry(title=title, data={}) + + self._set_confirm_only() + placeholders = {"name": title} + self.context["title_placeholders"] = placeholders + return self.async_show_form( + step_id="bluetooth_confirm", description_placeholders=placeholders + ) + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the user step to pick discovered device.""" + if user_input is not None: + address = user_input[CONF_ADDRESS] + await self.async_set_unique_id(address, raise_on_progress=False) + self._abort_if_unique_id_configured() + return self.async_create_entry( + title=self._discovered_devices[address], data={} + ) + + current_addresses = self._async_current_ids() + for discovery_info in async_discovered_service_info(self.hass, False): + address = discovery_info.address + if address in current_addresses or address in self._discovered_devices: + continue + device = SensirionBluetoothDeviceData() + if device.supported(discovery_info): + self._discovered_devices[address] = ( + device.title or device.get_device_name() or discovery_info.name + ) + + if not self._discovered_devices: + return self.async_abort(reason="no_devices_found") + + return self.async_show_form( + step_id="user", + data_schema=vol.Schema( + {vol.Required(CONF_ADDRESS): vol.In(self._discovered_devices)} + ), + ) diff --git a/homeassistant/components/sensirion_ble/const.py b/homeassistant/components/sensirion_ble/const.py new file mode 100644 index 00000000000..58403948762 --- /dev/null +++ b/homeassistant/components/sensirion_ble/const.py @@ -0,0 +1,3 @@ +"""Constants for the sensirion_ble integration.""" + +DOMAIN = "sensirion_ble" diff --git a/homeassistant/components/sensirion_ble/manifest.json b/homeassistant/components/sensirion_ble/manifest.json new file mode 100644 index 00000000000..f13f393a844 --- /dev/null +++ b/homeassistant/components/sensirion_ble/manifest.json @@ -0,0 +1,18 @@ +{ + "domain": "sensirion_ble", + "name": "Sensirion BLE", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/sensirion_ble", + "bluetooth": [ + { + "manufacturer_id": 1749 + }, + { + "local_name": "MyCO2*" + } + ], + "requirements": ["sensirion-ble==0.0.1"], + "dependencies": ["bluetooth"], + "codeowners": ["@akx"], + "iot_class": "local_push" +} diff --git a/homeassistant/components/sensirion_ble/sensor.py b/homeassistant/components/sensirion_ble/sensor.py new file mode 100644 index 00000000000..cd78d2e059b --- /dev/null +++ b/homeassistant/components/sensirion_ble/sensor.py @@ -0,0 +1,131 @@ +"""Support for Sensirion sensors.""" +from __future__ import annotations + +from typing import Optional, Union + +from sensor_state_data import ( + DeviceKey, + SensorDescription, + SensorDeviceClass as SSDSensorDeviceClass, + SensorUpdate, + Units, +) + +from homeassistant import config_entries, const +from homeassistant.components.bluetooth.passive_update_processor import ( + PassiveBluetoothDataProcessor, + PassiveBluetoothDataUpdate, + PassiveBluetoothEntityKey, + PassiveBluetoothProcessorCoordinator, + PassiveBluetoothProcessorEntity, +) +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info + +from .const import DOMAIN + +SENSOR_DESCRIPTIONS: dict[ + tuple[SSDSensorDeviceClass, Units | None], SensorEntityDescription +] = { + ( + SSDSensorDeviceClass.CO2, + Units.CONCENTRATION_PARTS_PER_MILLION, + ): SensorEntityDescription( + key=f"{SensorDeviceClass.CO2}_{Units.CONCENTRATION_PARTS_PER_MILLION}", + device_class=SensorDeviceClass.CO2, + native_unit_of_measurement=const.CONCENTRATION_PARTS_PER_MILLION, + state_class=SensorStateClass.MEASUREMENT, + ), + (SSDSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( + key=f"{SensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}", + device_class=SensorDeviceClass.HUMIDITY, + native_unit_of_measurement=const.PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + (SSDSensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( + key=f"{SensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=const.TEMP_CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), +} + + +def _device_key_to_bluetooth_entity_key( + device_key: DeviceKey, +) -> PassiveBluetoothEntityKey: + """Convert a device key to an entity key.""" + return PassiveBluetoothEntityKey(device_key.key, device_key.device_id) + + +def _to_sensor_key( + description: SensorDescription, +) -> tuple[SSDSensorDeviceClass, Units | None]: + assert description.device_class is not None + return (description.device_class, description.native_unit_of_measurement) + + +def sensor_update_to_bluetooth_data_update( + sensor_update: SensorUpdate, +) -> PassiveBluetoothDataUpdate: + """Convert a sensor update to a bluetooth data update.""" + return PassiveBluetoothDataUpdate( + devices={ + device_id: sensor_device_info_to_hass_device_info(device_info) + for device_id, device_info in sensor_update.devices.items() + }, + entity_descriptions={ + _device_key_to_bluetooth_entity_key(device_key): SENSOR_DESCRIPTIONS[ + _to_sensor_key(description) + ] + for device_key, description in sensor_update.entity_descriptions.items() + if _to_sensor_key(description) in SENSOR_DESCRIPTIONS + }, + entity_data={ + _device_key_to_bluetooth_entity_key(device_key): sensor_values.native_value + for device_key, sensor_values in sensor_update.entity_values.items() + }, + entity_names={ + _device_key_to_bluetooth_entity_key(device_key): sensor_values.name + for device_key, sensor_values in sensor_update.entity_values.items() + }, + ) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: config_entries.ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Sensirion BLE sensors.""" + coordinator: PassiveBluetoothProcessorCoordinator = hass.data[DOMAIN][ + entry.entry_id + ] + processor = PassiveBluetoothDataProcessor(sensor_update_to_bluetooth_data_update) + entry.async_on_unload( + processor.async_add_entities_listener( + SensirionBluetoothSensorEntity, async_add_entities + ) + ) + entry.async_on_unload(coordinator.async_register_processor(processor)) + + +class SensirionBluetoothSensorEntity( + PassiveBluetoothProcessorEntity[ + PassiveBluetoothDataProcessor[Optional[Union[float, int]]] + ], + SensorEntity, +): + """Representation of a Sensirion BLE sensor.""" + + @property + def native_value(self) -> int | float | None: + """Return the native value.""" + return self.processor.entity_data.get(self.entity_key) diff --git a/homeassistant/generated/bluetooth.py b/homeassistant/generated/bluetooth.py index 017135e9d10..922e754e84a 100644 --- a/homeassistant/generated/bluetooth.py +++ b/homeassistant/generated/bluetooth.py @@ -272,6 +272,14 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ "domain": "ruuvitag_ble", "local_name": "Ruuvi *", }, + { + "domain": "sensirion_ble", + "manufacturer_id": 1749, + }, + { + "domain": "sensirion_ble", + "local_name": "MyCO2*", + }, { "connectable": False, "domain": "sensorpro", diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index cdcea6cf9b3..e3519ac8773 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -352,6 +352,7 @@ FLOWS = { "sense", "senseme", "sensibo", + "sensirion_ble", "sensorpro", "sensorpush", "sentry", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index f20e3f18b07..69911965eb4 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -4647,6 +4647,12 @@ "config_flow": true, "iot_class": "cloud_polling" }, + "sensirion_ble": { + "name": "Sensirion BLE", + "integration_type": "hub", + "config_flow": true, + "iot_class": "local_push" + }, "sensorblue": { "name": "SensorBlue", "integration_type": "virtual", diff --git a/mypy.ini b/mypy.ini index fa245821ede..c0628a2fc71 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2133,6 +2133,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.sensirion_ble.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.sensor.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/requirements_all.txt b/requirements_all.txt index 17a9550b1c5..0341bb700ef 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2257,6 +2257,9 @@ sendgrid==6.8.2 # homeassistant.components.sense sense_energy==0.10.4 +# homeassistant.components.sensirion_ble +sensirion-ble==0.0.1 + # homeassistant.components.sensorpro sensorpro-ble==0.5.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 31c780304bd..b7cf3f9a2a9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1560,6 +1560,9 @@ securetar==2022.2.0 # homeassistant.components.sense sense_energy==0.10.4 +# homeassistant.components.sensirion_ble +sensirion-ble==0.0.1 + # homeassistant.components.sensorpro sensorpro-ble==0.5.0 diff --git a/tests/components/sensirion_ble/__init__.py b/tests/components/sensirion_ble/__init__.py new file mode 100644 index 00000000000..30f89f8934b --- /dev/null +++ b/tests/components/sensirion_ble/__init__.py @@ -0,0 +1 @@ +"""Test package for Sensirion BLE sensor integration.""" diff --git a/tests/components/sensirion_ble/fixtures.py b/tests/components/sensirion_ble/fixtures.py new file mode 100644 index 00000000000..c49ea3c1da2 --- /dev/null +++ b/tests/components/sensirion_ble/fixtures.py @@ -0,0 +1,26 @@ +"""Fixtures for testing Sensirion BLE.""" +from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo + +NOT_SENSIRION_SERVICE_INFO = BluetoothServiceInfo( + name="Not it", + address="61DE521B-F0BF-9F44-64D4-75BBE1738105", + rssi=-63, + manufacturer_data={3234: b"\x00\x01"}, + service_data={}, + service_uuids=[], + source="local", +) + +SENSIRION_SERVICE_INFO = BluetoothServiceInfo( + name="MyCO2", + address="01:03:05:07:09:11", # Ignored (the payload encodes a device ID) + rssi=-60, + manufacturer_data={ + 0x06D5: b"\x00\x08\x84\xe3>_3G\xd4\x02", + }, + service_data={}, + service_uuids=[], + source="local", +) +CONFIGURED_NAME = "MyCO2 84E3" +CONFIGURED_PREFIX = "myco2_84e3" diff --git a/tests/components/sensirion_ble/test_config_flow.py b/tests/components/sensirion_ble/test_config_flow.py new file mode 100644 index 00000000000..9c23a42c90c --- /dev/null +++ b/tests/components/sensirion_ble/test_config_flow.py @@ -0,0 +1,206 @@ +"""Test the Sensirion config flow.""" + +from unittest.mock import patch + +import pytest + +from homeassistant import config_entries +from homeassistant.components.sensirion_ble.const import DOMAIN +from homeassistant.data_entry_flow import FlowResultType + +from .fixtures import ( + CONFIGURED_NAME, + NOT_SENSIRION_SERVICE_INFO, + SENSIRION_SERVICE_INFO, +) + +from tests.common import MockConfigEntry + + +@pytest.fixture(autouse=True) +def mock_bluetooth(enable_bluetooth): + """Mock bluetooth for all tests in this module.""" + + +async def test_async_step_bluetooth_valid_device(hass): + """Test discovery via bluetooth with a valid device.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=SENSIRION_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "bluetooth_confirm" + with patch( + "homeassistant.components.sensirion_ble.async_setup_entry", return_value=True + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={} + ) + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == CONFIGURED_NAME + assert result2["result"].unique_id == SENSIRION_SERVICE_INFO.address + + +async def test_async_step_bluetooth_not_sensirion(hass): + """Test discovery via bluetooth not sensirion.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=NOT_SENSIRION_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "not_supported" + + +async def test_async_step_user_no_devices_found(hass): + """Test setup from service info cache with no devices found.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "no_devices_found" + + +async def test_async_step_user_with_found_devices(hass): + """Test setup from service info cache with devices found.""" + with patch( + "homeassistant.components.sensirion_ble.config_flow.async_discovered_service_info", + return_value=[SENSIRION_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + with patch( + "homeassistant.components.sensirion_ble.async_setup_entry", return_value=True + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"address": SENSIRION_SERVICE_INFO.address}, + ) + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == CONFIGURED_NAME + assert result2["result"].unique_id == SENSIRION_SERVICE_INFO.address + + +async def test_async_step_user_device_added_between_steps(hass): + """Test the device gets added via another flow between steps.""" + with patch( + "homeassistant.components.sensirion_ble.config_flow.async_discovered_service_info", + return_value=[SENSIRION_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=SENSIRION_SERVICE_INFO.address, + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.sensirion_ble.async_setup_entry", return_value=True + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"address": SENSIRION_SERVICE_INFO.address}, + ) + assert result2["type"] == FlowResultType.ABORT + assert result2["reason"] == "already_configured" + + +async def test_async_step_user_with_found_devices_already_setup(hass): + """Test setup from service info cache with devices found.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=SENSIRION_SERVICE_INFO.address, + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.sensirion_ble.config_flow.async_discovered_service_info", + return_value=[SENSIRION_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "no_devices_found" + + +async def test_async_step_bluetooth_devices_already_setup(hass): + """Test we can't start a flow if there is already a config entry.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=SENSIRION_SERVICE_INFO.address, + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=SENSIRION_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_configured" + + +async def test_async_step_bluetooth_already_in_progress(hass): + """Test we can't start a flow for the same device twice.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=SENSIRION_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "bluetooth_confirm" + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=SENSIRION_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_in_progress" + + +async def test_async_step_user_takes_precedence_over_discovery(hass): + """Test manual setup takes precedence over discovery.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_BLUETOOTH}, + data=SENSIRION_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "bluetooth_confirm" + + with patch( + "homeassistant.components.sensirion_ble.config_flow.async_discovered_service_info", + return_value=[SENSIRION_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.FORM + + with patch( + "homeassistant.components.sensirion_ble.async_setup_entry", return_value=True + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"address": SENSIRION_SERVICE_INFO.address}, + ) + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == CONFIGURED_NAME + assert result2["data"] == {} + assert result2["result"].unique_id == SENSIRION_SERVICE_INFO.address diff --git a/tests/components/sensirion_ble/test_sensor.py b/tests/components/sensirion_ble/test_sensor.py new file mode 100644 index 00000000000..cdec4a0944e --- /dev/null +++ b/tests/components/sensirion_ble/test_sensor.py @@ -0,0 +1,45 @@ +"""Test the Sensirion BLE sensors.""" + +from __future__ import annotations + +from homeassistant.components.sensirion_ble.const import DOMAIN +from homeassistant.components.sensor import ATTR_STATE_CLASS +from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT +from homeassistant.core import HomeAssistant + +from .fixtures import CONFIGURED_NAME, CONFIGURED_PREFIX, SENSIRION_SERVICE_INFO + +from tests.common import MockConfigEntry +from tests.components.bluetooth import inject_bluetooth_service_info + + +async def test_sensors(enable_bluetooth, hass: HomeAssistant): + """Test the Sensirion BLE sensors.""" + entry = MockConfigEntry(domain=DOMAIN, unique_id=SENSIRION_SERVICE_INFO.address) + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 0 + inject_bluetooth_service_info( + hass, + SENSIRION_SERVICE_INFO, + ) + await hass.async_block_till_done() + assert len(hass.states.async_all()) >= 3 + + for sensor, value, unit, state_class in [ + ("carbon_dioxide", "724", "ppm", "measurement"), + ("humidity", "27.8", "%", "measurement"), + ("temperature", "20.1", "°C", "measurement"), + ]: + state = hass.states.get(f"sensor.{CONFIGURED_PREFIX}_{sensor}") + assert state is not None + assert state.state == value + name_lower = state.attributes[ATTR_FRIENDLY_NAME].lower() + assert name_lower == f"{CONFIGURED_NAME} {sensor}".lower().replace("_", " ") + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == unit + assert state.attributes[ATTR_STATE_CLASS] == state_class + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() From 9add474c457b75929f52eb0cc5446c432c84ece9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 19 Nov 2022 18:46:06 -0600 Subject: [PATCH 0568/1033] Bump bleak-retry-connector to 2.8.5 (#82387) changelog: https://github.com/Bluetooth-Devices/bleak-retry-connector/compare/v2.8.4...v2.8.5 from: https://github.com/esphome/esphome/pull/4049 --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 8e3dfb4ac80..c6611704dc4 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.2", - "bleak-retry-connector==2.8.4", + "bleak-retry-connector==2.8.5", "bluetooth-adapters==0.8.0", "bluetooth-auto-recovery==0.4.0", "bluetooth-data-tools==0.3.0", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index bbc88934a70..2c46489598c 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.8.4 +bleak-retry-connector==2.8.5 bleak==0.19.2 bluetooth-adapters==0.8.0 bluetooth-auto-recovery==0.4.0 diff --git a/requirements_all.txt b/requirements_all.txt index 0341bb700ef..aaec97b2b38 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -422,7 +422,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.4 +bleak-retry-connector==2.8.5 # homeassistant.components.bluetooth bleak==0.19.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b7cf3f9a2a9..24d62ce4bf0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -346,7 +346,7 @@ bellows==0.34.2 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.4 +bleak-retry-connector==2.8.5 # homeassistant.components.bluetooth bleak==0.19.2 From 3618160768f5cca3f4599b8288d365684c031268 Mon Sep 17 00:00:00 2001 From: Marvin Wichmann Date: Sun, 20 Nov 2022 15:12:38 +0100 Subject: [PATCH 0569/1033] Update xknx to 1.2.1 (#82404) --- homeassistant/components/knx/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json index b29e44490e9..a7436ef1ae3 100644 --- a/homeassistant/components/knx/manifest.json +++ b/homeassistant/components/knx/manifest.json @@ -3,7 +3,7 @@ "name": "KNX", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/knx", - "requirements": ["xknx==1.2.0"], + "requirements": ["xknx==1.2.1"], "codeowners": ["@Julius2342", "@farmio", "@marvin-w"], "quality_scale": "platinum", "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index aaec97b2b38..c4fef1ef6ca 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2583,7 +2583,7 @@ xboxapi==2.0.1 xiaomi-ble==0.12.2 # homeassistant.components.knx -xknx==1.2.0 +xknx==1.2.1 # homeassistant.components.bluesound # homeassistant.components.fritz diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 24d62ce4bf0..1182d9423cc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1796,7 +1796,7 @@ xbox-webapi==2.0.11 xiaomi-ble==0.12.2 # homeassistant.components.knx -xknx==1.2.0 +xknx==1.2.1 # homeassistant.components.bluesound # homeassistant.components.fritz From ea0c3e806de7c5a7cdcef9fd257bedd43ab470a3 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Sun, 20 Nov 2022 15:13:19 +0100 Subject: [PATCH 0570/1033] Bump PyViCare to 2.19.0 (#82381) fixes undefined --- homeassistant/components/vicare/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/vicare/manifest.json b/homeassistant/components/vicare/manifest.json index c770f1b2382..0294022137c 100644 --- a/homeassistant/components/vicare/manifest.json +++ b/homeassistant/components/vicare/manifest.json @@ -3,7 +3,7 @@ "name": "Viessmann ViCare", "documentation": "https://www.home-assistant.io/integrations/vicare", "codeowners": ["@oischinger"], - "requirements": ["PyViCare==2.17.0"], + "requirements": ["PyViCare==2.19.0"], "iot_class": "cloud_polling", "config_flow": true, "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index c4fef1ef6ca..214d7b7dd13 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -47,7 +47,7 @@ PyTransportNSW==0.1.1 PyTurboJPEG==1.6.7 # homeassistant.components.vicare -PyViCare==2.17.0 +PyViCare==2.19.0 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.14.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1182d9423cc..bcf562814a2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -43,7 +43,7 @@ PyTransportNSW==0.1.1 PyTurboJPEG==1.6.7 # homeassistant.components.vicare -PyViCare==2.17.0 +PyViCare==2.19.0 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.14.3 From 2a817a2874028c6c31e3e0f5b2f1b2b0e1f29c89 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Sun, 20 Nov 2022 09:19:15 -0500 Subject: [PATCH 0571/1033] Remove duplicate device class on Flo water sensor (#82407) Fixes https://github.com/home-assistant/core/issues/82390 fixes undefined --- homeassistant/components/flo/sensor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/flo/sensor.py b/homeassistant/components/flo/sensor.py index 9cb070a1c62..58e8417de09 100644 --- a/homeassistant/components/flo/sensor.py +++ b/homeassistant/components/flo/sensor.py @@ -67,7 +67,6 @@ async def async_setup_entry( class FloDailyUsageSensor(FloEntity, SensorEntity): """Monitors the daily water usage.""" - _attr_device_class = SensorDeviceClass.VOLUME _attr_icon = WATER_ICON _attr_native_unit_of_measurement = VOLUME_GALLONS _attr_state_class: SensorStateClass = SensorStateClass.TOTAL_INCREASING From 372c3cd0f0ff18f46bbfe6abc5f90ca8bc9971ab Mon Sep 17 00:00:00 2001 From: Paarth Shah Date: Sun, 20 Nov 2022 06:26:00 -0800 Subject: [PATCH 0572/1033] Bump plexapi version to 4.13.1 (#82396) --- homeassistant/components/plex/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plex/manifest.json b/homeassistant/components/plex/manifest.json index 171648042c1..8eb1e3d6903 100644 --- a/homeassistant/components/plex/manifest.json +++ b/homeassistant/components/plex/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/plex", "requirements": [ - "plexapi==4.13.0", + "plexapi==4.13.1", "plexauth==0.0.6", "plexwebsocket==0.0.13" ], diff --git a/requirements_all.txt b/requirements_all.txt index 214d7b7dd13..a1960e096b4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1323,7 +1323,7 @@ pillow==9.3.0 pizzapi==0.0.3 # homeassistant.components.plex -plexapi==4.13.0 +plexapi==4.13.1 # homeassistant.components.plex plexauth==0.0.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bcf562814a2..fb77432ea45 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -950,7 +950,7 @@ pilight==0.1.1 pillow==9.3.0 # homeassistant.components.plex -plexapi==4.13.0 +plexapi==4.13.1 # homeassistant.components.plex plexauth==0.0.6 From 76cb1c4978e8e5ca880d656bb3406f0d5316b4b0 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sun, 20 Nov 2022 16:27:36 +0200 Subject: [PATCH 0573/1033] Correct SensorDeviceClass for sensirion_ble (#82413) --- homeassistant/components/sensirion_ble/sensor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensirion_ble/sensor.py b/homeassistant/components/sensirion_ble/sensor.py index cd78d2e059b..3612b25f341 100644 --- a/homeassistant/components/sensirion_ble/sensor.py +++ b/homeassistant/components/sensirion_ble/sensor.py @@ -38,19 +38,19 @@ SENSOR_DESCRIPTIONS: dict[ SSDSensorDeviceClass.CO2, Units.CONCENTRATION_PARTS_PER_MILLION, ): SensorEntityDescription( - key=f"{SensorDeviceClass.CO2}_{Units.CONCENTRATION_PARTS_PER_MILLION}", + key=f"{SSDSensorDeviceClass.CO2}_{Units.CONCENTRATION_PARTS_PER_MILLION}", device_class=SensorDeviceClass.CO2, native_unit_of_measurement=const.CONCENTRATION_PARTS_PER_MILLION, state_class=SensorStateClass.MEASUREMENT, ), (SSDSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( - key=f"{SensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}", + key=f"{SSDSensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}", device_class=SensorDeviceClass.HUMIDITY, native_unit_of_measurement=const.PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, ), (SSDSensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( - key=f"{SensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", + key=f"{SSDSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=const.TEMP_CELSIUS, state_class=SensorStateClass.MEASUREMENT, From 7801cd96de9a9dcd4ba5c562ca766552067e9625 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 20 Nov 2022 08:44:28 -0600 Subject: [PATCH 0574/1033] Split bluetooth models into base_scanner and wrappers (#82291) --- .../components/bluetooth/__init__.py | 9 +- .../components/bluetooth/base_scanner.py | 195 +++++++ homeassistant/components/bluetooth/const.py | 9 + homeassistant/components/bluetooth/manager.py | 8 +- homeassistant/components/bluetooth/models.py | 479 +----------------- homeassistant/components/bluetooth/scanner.py | 3 +- homeassistant/components/bluetooth/usage.py | 2 +- .../components/bluetooth/wrappers.py | 274 ++++++++++ tests/components/bluetooth/__init__.py | 36 +- .../bluetooth/test_advertisement_tracker.py | 2 +- .../components/bluetooth/test_base_scanner.py | 267 ++++++++++ tests/components/bluetooth/test_init.py | 21 +- tests/components/bluetooth/test_manager.py | 12 +- tests/components/bluetooth/test_models.py | 288 +---------- tests/components/bluetooth/test_usage.py | 8 +- 15 files changed, 821 insertions(+), 792 deletions(-) create mode 100644 homeassistant/components/bluetooth/base_scanner.py create mode 100644 homeassistant/components/bluetooth/wrappers.py create mode 100644 tests/components/bluetooth/test_base_scanner.py diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index 9a8cf115a8e..fb816d4bfc6 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -20,6 +20,7 @@ from bluetooth_adapters import ( adapter_unique_name, get_adapters, ) +from home_assistant_bluetooth import BluetoothServiceInfo, BluetoothServiceInfoBleak from homeassistant.components import usb from homeassistant.config_entries import ( @@ -41,6 +42,7 @@ from homeassistant.helpers.issue_registry import ( from homeassistant.loader import async_get_bluetooth from . import models +from .base_scanner import BaseHaRemoteScanner, BaseHaScanner from .const import ( BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS, CONF_ADAPTER, @@ -55,18 +57,13 @@ from .const import ( from .manager import BluetoothManager from .match import BluetoothCallbackMatcher, IntegrationMatcher from .models import ( - BaseHaRemoteScanner, - BaseHaScanner, BluetoothCallback, BluetoothChange, BluetoothScanningMode, - BluetoothServiceInfo, - BluetoothServiceInfoBleak, - HaBleakScannerWrapper, - HaBluetoothConnector, ProcessAdvertisementCallback, ) from .scanner import HaScanner, ScannerStartError +from .wrappers import HaBleakScannerWrapper, HaBluetoothConnector if TYPE_CHECKING: from bleak.backends.device import BLEDevice diff --git a/homeassistant/components/bluetooth/base_scanner.py b/homeassistant/components/bluetooth/base_scanner.py new file mode 100644 index 00000000000..e92725994be --- /dev/null +++ b/homeassistant/components/bluetooth/base_scanner.py @@ -0,0 +1,195 @@ +"""Base classes for HA Bluetooth scanners for bluetooth.""" +from __future__ import annotations + +from abc import abstractmethod +from collections.abc import Callable +import datetime +from datetime import timedelta +from typing import Any, Final + +from bleak.backends.device import BLEDevice +from bleak.backends.scanner import AdvertisementData +from bleak_retry_connector import NO_RSSI_VALUE +from home_assistant_bluetooth import BluetoothServiceInfoBleak + +from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback +from homeassistant.helpers.event import async_track_time_interval +from homeassistant.util.dt import monotonic_time_coarse + +from .const import ( + CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, + FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, +) +from .models import HaBluetoothConnector + +MONOTONIC_TIME: Final = monotonic_time_coarse + + +class BaseHaScanner: + """Base class for Ha Scanners.""" + + def __init__(self, hass: HomeAssistant, source: str) -> None: + """Initialize the scanner.""" + self.hass = hass + self.source = source + + @property + @abstractmethod + def discovered_devices(self) -> list[BLEDevice]: + """Return a list of discovered devices.""" + + @property + @abstractmethod + def discovered_devices_and_advertisement_data( + self, + ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: + """Return a list of discovered devices and their advertisement data.""" + + async def async_diagnostics(self) -> dict[str, Any]: + """Return diagnostic information about the scanner.""" + return { + "type": self.__class__.__name__, + "discovered_devices": [ + { + "name": device.name, + "address": device.address, + } + for device in self.discovered_devices + ], + } + + +class BaseHaRemoteScanner(BaseHaScanner): + """Base class for a Home Assistant remote BLE scanner.""" + + def __init__( + self, + hass: HomeAssistant, + scanner_id: str, + new_info_callback: Callable[[BluetoothServiceInfoBleak], None], + connector: HaBluetoothConnector, + connectable: bool, + ) -> None: + """Initialize the scanner.""" + super().__init__(hass, scanner_id) + self._new_info_callback = new_info_callback + self._discovered_device_advertisement_datas: dict[ + str, tuple[BLEDevice, AdvertisementData] + ] = {} + self._discovered_device_timestamps: dict[str, float] = {} + self._connector = connector + self._connectable = connectable + self._details: dict[str, str | HaBluetoothConnector] = {"source": scanner_id} + self._expire_seconds = FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + if connectable: + self._details["connector"] = connector + self._expire_seconds = ( + CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + ) + + @hass_callback + def async_setup(self) -> CALLBACK_TYPE: + """Set up the scanner.""" + return async_track_time_interval( + self.hass, self._async_expire_devices, timedelta(seconds=30) + ) + + def _async_expire_devices(self, _datetime: datetime.datetime) -> None: + """Expire old devices.""" + now = MONOTONIC_TIME() + expired = [ + address + for address, timestamp in self._discovered_device_timestamps.items() + if now - timestamp > self._expire_seconds + ] + for address in expired: + del self._discovered_device_advertisement_datas[address] + del self._discovered_device_timestamps[address] + + @property + def discovered_devices(self) -> list[BLEDevice]: + """Return a list of discovered devices.""" + return [ + device_advertisement_data[0] + for device_advertisement_data in self._discovered_device_advertisement_datas.values() + ] + + @property + def discovered_devices_and_advertisement_data( + self, + ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: + """Return a list of discovered devices and advertisement data.""" + return self._discovered_device_advertisement_datas + + @hass_callback + def _async_on_advertisement( + self, + address: str, + rssi: int, + local_name: str | None, + service_uuids: list[str], + service_data: dict[str, bytes], + manufacturer_data: dict[int, bytes], + tx_power: int | None, + ) -> None: + """Call the registered callback.""" + now = MONOTONIC_TIME() + if prev_discovery := self._discovered_device_advertisement_datas.get(address): + # Merge the new data with the old data + # to function the same as BlueZ which + # merges the dicts on PropertiesChanged + prev_device = prev_discovery[0] + prev_advertisement = prev_discovery[1] + if ( + local_name + and prev_device.name + and len(prev_device.name) > len(local_name) + ): + local_name = prev_device.name + if prev_advertisement.service_uuids: + service_uuids = list( + set(service_uuids + prev_advertisement.service_uuids) + ) + if prev_advertisement.service_data: + service_data = {**prev_advertisement.service_data, **service_data} + if prev_advertisement.manufacturer_data: + manufacturer_data = { + **prev_advertisement.manufacturer_data, + **manufacturer_data, + } + + advertisement_data = AdvertisementData( + local_name=None if local_name == "" else local_name, + manufacturer_data=manufacturer_data, + service_data=service_data, + service_uuids=service_uuids, + rssi=rssi, + tx_power=NO_RSSI_VALUE if tx_power is None else tx_power, + platform_data=(), + ) + device = BLEDevice( # type: ignore[no-untyped-call] + address=address, + name=local_name, + details=self._details, + rssi=rssi, # deprecated, will be removed in newer bleak + ) + self._discovered_device_advertisement_datas[address] = ( + device, + advertisement_data, + ) + self._discovered_device_timestamps[address] = now + self._new_info_callback( + BluetoothServiceInfoBleak( + name=advertisement_data.local_name or device.name or device.address, + address=device.address, + rssi=rssi, + manufacturer_data=advertisement_data.manufacturer_data, + service_data=advertisement_data.service_data, + service_uuids=advertisement_data.service_uuids, + source=self.source, + device=device, + advertisement=advertisement_data, + connectable=self._connectable, + time=now, + ) + ) diff --git a/homeassistant/components/bluetooth/const.py b/homeassistant/components/bluetooth/const.py index d44858107ab..150239eec02 100644 --- a/homeassistant/components/bluetooth/const.py +++ b/homeassistant/components/bluetooth/const.py @@ -31,6 +31,15 @@ START_TIMEOUT = 15 # FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS: Final = 60 * 15 +# The maximum time between advertisements for a device to be considered +# stale when the advertisement tracker can determine the interval for +# connectable devices. +# +# BlueZ uses 180 seconds by default but we give it a bit more time +# to account for the esp32's bluetooth stack being a bit slower +# than BlueZ's. +CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS: Final = 195 + # We must recover before we hit the 180s mark # where the device is removed from the stack diff --git a/homeassistant/components/bluetooth/manager.py b/homeassistant/components/bluetooth/manager.py index 535ee5e3716..65f8333304b 100644 --- a/homeassistant/components/bluetooth/manager.py +++ b/homeassistant/components/bluetooth/manager.py @@ -29,6 +29,7 @@ from homeassistant.helpers.event import async_track_time_interval from homeassistant.util.dt import monotonic_time_coarse from .advertisement_tracker import AdvertisementTracker +from .base_scanner import BaseHaScanner from .const import ( FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, UNAVAILABLE_TRACK_SECONDS, @@ -43,12 +44,7 @@ from .match import ( IntegrationMatcher, ble_device_matches, ) -from .models import ( - BaseHaScanner, - BluetoothCallback, - BluetoothChange, - BluetoothServiceInfoBleak, -) +from .models import BluetoothCallback, BluetoothChange, BluetoothServiceInfoBleak from .usage import install_multiple_bleak_catcher, uninstall_multiple_bleak_catcher from .util import async_load_history_from_system diff --git a/homeassistant/components/bluetooth/models.py b/homeassistant/components/bluetooth/models.py index f324d6086f7..58067a467ce 100644 --- a/homeassistant/components/bluetooth/models.py +++ b/homeassistant/components/bluetooth/models.py @@ -1,61 +1,35 @@ """Models for bluetooth.""" from __future__ import annotations -from abc import abstractmethod -import asyncio from collections.abc import Callable -import contextlib from dataclasses import dataclass -import datetime -from datetime import timedelta from enum import Enum -import logging -from typing import TYPE_CHECKING, Any, Final +from typing import TYPE_CHECKING, Final -from bleak import BleakClient, BleakError -from bleak.backends.client import BaseBleakClient, get_platform_client_backend_type -from bleak.backends.device import BLEDevice -from bleak.backends.scanner import ( - AdvertisementData, - AdvertisementDataCallback, - BaseBleakScanner, -) -from bleak_retry_connector import NO_RSSI_VALUE, freshen_ble_device +from bleak import BaseBleakClient from home_assistant_bluetooth import BluetoothServiceInfoBleak -from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback -from homeassistant.helpers.event import async_track_time_interval -from homeassistant.helpers.frame import report -from homeassistant.helpers.service_info.bluetooth import ( # noqa: F401 # pylint: disable=unused-import - BluetoothServiceInfo, -) from homeassistant.util.dt import monotonic_time_coarse -from .const import FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS - -# The maximum time between advertisements for a device to be considered -# stale when the advertisement tracker can determine the interval for -# connectable devices. -# -# BlueZ uses 180 seconds by default but we give it a bit more time -# to account for the esp32's bluetooth stack being a bit slower -# than BlueZ's. -CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS: Final = 195 - if TYPE_CHECKING: from .manager import BluetoothManager -_LOGGER = logging.getLogger(__name__) - -FILTER_UUIDS: Final = "UUIDs" - MANAGER: BluetoothManager | None = None MONOTONIC_TIME: Final = monotonic_time_coarse +@dataclass +class HaBluetoothConnector: + """Data for how to connect a BLEDevice from a given scanner.""" + + client: type[BaseBleakClient] + source: str + can_connect: Callable[[], bool] + + class BluetoothScanningMode(Enum): """The mode of scanning for bluetooth devices.""" @@ -66,434 +40,3 @@ class BluetoothScanningMode(Enum): BluetoothChange = Enum("BluetoothChange", "ADVERTISEMENT") BluetoothCallback = Callable[[BluetoothServiceInfoBleak, BluetoothChange], None] ProcessAdvertisementCallback = Callable[[BluetoothServiceInfoBleak], bool] - - -@dataclass -class HaBluetoothConnector: - """Data for how to connect a BLEDevice from a given scanner.""" - - client: type[BaseBleakClient] - source: str - can_connect: Callable[[], bool] - - -@dataclass -class _HaWrappedBleakBackend: - """Wrap bleak backend to make it usable by Home Assistant.""" - - device: BLEDevice - client: type[BaseBleakClient] - - -class BaseHaScanner: - """Base class for Ha Scanners.""" - - def __init__(self, hass: HomeAssistant, source: str) -> None: - """Initialize the scanner.""" - self.hass = hass - self.source = source - - @property - @abstractmethod - def discovered_devices(self) -> list[BLEDevice]: - """Return a list of discovered devices.""" - - @property - @abstractmethod - def discovered_devices_and_advertisement_data( - self, - ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: - """Return a list of discovered devices and their advertisement data.""" - - async def async_diagnostics(self) -> dict[str, Any]: - """Return diagnostic information about the scanner.""" - return { - "type": self.__class__.__name__, - "discovered_devices": [ - { - "name": device.name, - "address": device.address, - } - for device in self.discovered_devices - ], - } - - -class BaseHaRemoteScanner(BaseHaScanner): - """Base class for a Home Assistant remote BLE scanner.""" - - def __init__( - self, - hass: HomeAssistant, - scanner_id: str, - new_info_callback: Callable[[BluetoothServiceInfoBleak], None], - connector: HaBluetoothConnector, - connectable: bool, - ) -> None: - """Initialize the scanner.""" - super().__init__(hass, scanner_id) - self._new_info_callback = new_info_callback - self._discovered_device_advertisement_datas: dict[ - str, tuple[BLEDevice, AdvertisementData] - ] = {} - self._discovered_device_timestamps: dict[str, float] = {} - self._connector = connector - self._connectable = connectable - self._details: dict[str, str | HaBluetoothConnector] = {"source": scanner_id} - self._expire_seconds = FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS - if connectable: - self._details["connector"] = connector - self._expire_seconds = ( - CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS - ) - - @hass_callback - def async_setup(self) -> CALLBACK_TYPE: - """Set up the scanner.""" - return async_track_time_interval( - self.hass, self._async_expire_devices, timedelta(seconds=30) - ) - - def _async_expire_devices(self, _datetime: datetime.datetime) -> None: - """Expire old devices.""" - now = MONOTONIC_TIME() - expired = [ - address - for address, timestamp in self._discovered_device_timestamps.items() - if now - timestamp > self._expire_seconds - ] - for address in expired: - del self._discovered_device_advertisement_datas[address] - del self._discovered_device_timestamps[address] - - @property - def discovered_devices(self) -> list[BLEDevice]: - """Return a list of discovered devices.""" - return [ - device_advertisement_data[0] - for device_advertisement_data in self._discovered_device_advertisement_datas.values() - ] - - @property - def discovered_devices_and_advertisement_data( - self, - ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: - """Return a list of discovered devices and advertisement data.""" - return self._discovered_device_advertisement_datas - - @hass_callback - def _async_on_advertisement( - self, - address: str, - rssi: int, - local_name: str | None, - service_uuids: list[str], - service_data: dict[str, bytes], - manufacturer_data: dict[int, bytes], - tx_power: int | None, - ) -> None: - """Call the registered callback.""" - now = MONOTONIC_TIME() - if prev_discovery := self._discovered_device_advertisement_datas.get(address): - # Merge the new data with the old data - # to function the same as BlueZ which - # merges the dicts on PropertiesChanged - prev_device = prev_discovery[0] - prev_advertisement = prev_discovery[1] - if ( - local_name - and prev_device.name - and len(prev_device.name) > len(local_name) - ): - local_name = prev_device.name - if prev_advertisement.service_uuids: - service_uuids = list( - set(service_uuids + prev_advertisement.service_uuids) - ) - if prev_advertisement.service_data: - service_data = {**prev_advertisement.service_data, **service_data} - if prev_advertisement.manufacturer_data: - manufacturer_data = { - **prev_advertisement.manufacturer_data, - **manufacturer_data, - } - - advertisement_data = AdvertisementData( - local_name=None if local_name == "" else local_name, - manufacturer_data=manufacturer_data, - service_data=service_data, - service_uuids=service_uuids, - rssi=rssi, - tx_power=NO_RSSI_VALUE if tx_power is None else tx_power, - platform_data=(), - ) - device = BLEDevice( # type: ignore[no-untyped-call] - address=address, - name=local_name, - details=self._details, - rssi=rssi, # deprecated, will be removed in newer bleak - ) - self._discovered_device_advertisement_datas[address] = ( - device, - advertisement_data, - ) - self._discovered_device_timestamps[address] = now - self._new_info_callback( - BluetoothServiceInfoBleak( - name=advertisement_data.local_name or device.name or device.address, - address=device.address, - rssi=rssi, - manufacturer_data=advertisement_data.manufacturer_data, - service_data=advertisement_data.service_data, - service_uuids=advertisement_data.service_uuids, - source=self.source, - device=device, - advertisement=advertisement_data, - connectable=self._connectable, - time=now, - ) - ) - - -class HaBleakScannerWrapper(BaseBleakScanner): - """A wrapper that uses the single instance.""" - - def __init__( - self, - *args: Any, - detection_callback: AdvertisementDataCallback | None = None, - service_uuids: list[str] | None = None, - **kwargs: Any, - ) -> None: - """Initialize the BleakScanner.""" - self._detection_cancel: CALLBACK_TYPE | None = None - self._mapped_filters: dict[str, set[str]] = {} - self._advertisement_data_callback: AdvertisementDataCallback | None = None - remapped_kwargs = { - "detection_callback": detection_callback, - "service_uuids": service_uuids or [], - **kwargs, - } - self._map_filters(*args, **remapped_kwargs) - super().__init__( - detection_callback=detection_callback, service_uuids=service_uuids or [] - ) - - @classmethod - async def discover(cls, timeout: float = 5.0, **kwargs: Any) -> list[BLEDevice]: - """Discover devices.""" - assert MANAGER is not None - return list(MANAGER.async_discovered_devices(True)) - - async def stop(self, *args: Any, **kwargs: Any) -> None: - """Stop scanning for devices.""" - - async def start(self, *args: Any, **kwargs: Any) -> None: - """Start scanning for devices.""" - - def _map_filters(self, *args: Any, **kwargs: Any) -> bool: - """Map the filters.""" - mapped_filters = {} - if filters := kwargs.get("filters"): - if filter_uuids := filters.get(FILTER_UUIDS): - mapped_filters[FILTER_UUIDS] = set(filter_uuids) - else: - _LOGGER.warning("Only %s filters are supported", FILTER_UUIDS) - if service_uuids := kwargs.get("service_uuids"): - mapped_filters[FILTER_UUIDS] = set(service_uuids) - if mapped_filters == self._mapped_filters: - return False - self._mapped_filters = mapped_filters - return True - - def set_scanning_filter(self, *args: Any, **kwargs: Any) -> None: - """Set the filters to use.""" - if self._map_filters(*args, **kwargs): - self._setup_detection_callback() - - def _cancel_callback(self) -> None: - """Cancel callback.""" - if self._detection_cancel: - self._detection_cancel() - self._detection_cancel = None - - @property - def discovered_devices(self) -> list[BLEDevice]: - """Return a list of discovered devices.""" - assert MANAGER is not None - return list(MANAGER.async_discovered_devices(True)) - - def register_detection_callback( - self, callback: AdvertisementDataCallback | None - ) -> None: - """Register a callback that is called when a device is discovered or has a property changed. - - This method takes the callback and registers it with the long running - scanner. - """ - self._advertisement_data_callback = callback - self._setup_detection_callback() - - def _setup_detection_callback(self) -> None: - """Set up the detection callback.""" - if self._advertisement_data_callback is None: - return - self._cancel_callback() - super().register_detection_callback(self._advertisement_data_callback) - assert MANAGER is not None - assert self._callback is not None - self._detection_cancel = MANAGER.async_register_bleak_callback( - self._callback, self._mapped_filters - ) - - def __del__(self) -> None: - """Delete the BleakScanner.""" - if self._detection_cancel: - # Nothing to do if event loop is already closed - with contextlib.suppress(RuntimeError): - asyncio.get_running_loop().call_soon_threadsafe(self._detection_cancel) - - -class HaBleakClientWrapper(BleakClient): - """Wrap the BleakClient to ensure it does not shutdown our scanner. - - If an address is passed into BleakClient instead of a BLEDevice, - bleak will quietly start a new scanner under the hood to resolve - the address. This can cause a conflict with our scanner. We need - to handle translating the address to the BLEDevice in this case - to avoid the whole stack from getting stuck in an in progress state - when an integration does this. - """ - - def __init__( # pylint: disable=super-init-not-called, keyword-arg-before-vararg - self, - address_or_ble_device: str | BLEDevice, - disconnected_callback: Callable[[BleakClient], None] | None = None, - *args: Any, - timeout: float = 10.0, - **kwargs: Any, - ) -> None: - """Initialize the BleakClient.""" - if isinstance(address_or_ble_device, BLEDevice): - self.__address = address_or_ble_device.address - else: - report( - "attempted to call BleakClient with an address instead of a BLEDevice", - exclude_integrations={"bluetooth"}, - error_if_core=False, - ) - self.__address = address_or_ble_device - self.__disconnected_callback = disconnected_callback - self.__timeout = timeout - self.__ble_device: BLEDevice | None = None - self._backend: BaseBleakClient | None = None # type: ignore[assignment] - - @property - def is_connected(self) -> bool: - """Return True if the client is connected to a device.""" - return self._backend is not None and self._backend.is_connected - - def set_disconnected_callback( - self, - callback: Callable[[BleakClient], None] | None, - **kwargs: Any, - ) -> None: - """Set the disconnect callback.""" - self.__disconnected_callback = callback - if self._backend: - self._backend.set_disconnected_callback(callback, **kwargs) # type: ignore[arg-type] - - async def connect(self, **kwargs: Any) -> bool: - """Connect to the specified GATT server.""" - if ( - not self._backend - or not self.__ble_device - or not self._async_get_backend_for_ble_device(self.__ble_device) - ): - assert MANAGER is not None - wrapped_backend = ( - self._async_get_backend() or self._async_get_fallback_backend() - ) - self.__ble_device = ( - await freshen_ble_device(wrapped_backend.device) - or wrapped_backend.device - ) - self._backend = wrapped_backend.client( - self.__ble_device, - disconnected_callback=self.__disconnected_callback, - timeout=self.__timeout, - hass=MANAGER.hass, - ) - return await super().connect(**kwargs) - - @hass_callback - def _async_get_backend_for_ble_device( - self, ble_device: BLEDevice - ) -> _HaWrappedBleakBackend | None: - """Get the backend for a BLEDevice.""" - details = ble_device.details - if not isinstance(details, dict) or "connector" not in details: - # If client is not defined in details - # its the client for this platform - cls = get_platform_client_backend_type() - return _HaWrappedBleakBackend(ble_device, cls) - - connector: HaBluetoothConnector = details["connector"] - # Make sure the backend can connect to the device - # as some backends have connection limits - if not connector.can_connect(): - return None - - return _HaWrappedBleakBackend(ble_device, connector.client) - - @hass_callback - def _async_get_backend(self) -> _HaWrappedBleakBackend | None: - """Get the bleak backend for the given address.""" - assert MANAGER is not None - address = self.__address - ble_device = MANAGER.async_ble_device_from_address(address, True) - if ble_device is None: - raise BleakError(f"No device found for address {address}") - - if backend := self._async_get_backend_for_ble_device(ble_device): - return backend - - return None - - @hass_callback - def _async_get_fallback_backend(self) -> _HaWrappedBleakBackend: - """Get a fallback backend for the given address.""" - # - # The preferred backend cannot currently connect the device - # because it is likely out of connection slots. - # - # We need to try all backends to find one that can - # connect to the device. - # - assert MANAGER is not None - address = self.__address - device_advertisement_datas = ( - MANAGER.async_get_discovered_devices_and_advertisement_data_by_address( - address, True - ) - ) - for device_advertisement_data in sorted( - device_advertisement_datas, - key=lambda device_advertisement_data: device_advertisement_data[1].rssi - or NO_RSSI_VALUE, - reverse=True, - ): - if backend := self._async_get_backend_for_ble_device( - device_advertisement_data[0] - ): - return backend - - raise BleakError( - f"No backend with an available connection slot that can reach address {address} was found" - ) - - async def disconnect(self) -> bool: - """Disconnect from the device.""" - if self._backend is None: - return True - return await self._backend.disconnect() diff --git a/homeassistant/components/bluetooth/scanner.py b/homeassistant/components/bluetooth/scanner.py index 797a0551552..3ea7b1b8622 100644 --- a/homeassistant/components/bluetooth/scanner.py +++ b/homeassistant/components/bluetooth/scanner.py @@ -25,13 +25,14 @@ from homeassistant.helpers.event import async_track_time_interval from homeassistant.util.dt import monotonic_time_coarse from homeassistant.util.package import is_docker_env +from .base_scanner import BaseHaScanner from .const import ( SCANNER_WATCHDOG_INTERVAL, SCANNER_WATCHDOG_TIMEOUT, SOURCE_LOCAL, START_TIMEOUT, ) -from .models import BaseHaScanner, BluetoothScanningMode, BluetoothServiceInfoBleak +from .models import BluetoothScanningMode, BluetoothServiceInfoBleak from .util import async_reset_adapter OriginalBleakScanner = bleak.BleakScanner diff --git a/homeassistant/components/bluetooth/usage.py b/homeassistant/components/bluetooth/usage.py index ba174f0306a..0b1e615ddda 100644 --- a/homeassistant/components/bluetooth/usage.py +++ b/homeassistant/components/bluetooth/usage.py @@ -6,7 +6,7 @@ import bleak from bleak.backends.service import BleakGATTServiceCollection import bleak_retry_connector -from .models import HaBleakClientWrapper, HaBleakScannerWrapper +from .wrappers import HaBleakClientWrapper, HaBleakScannerWrapper ORIGINAL_BLEAK_SCANNER = bleak.BleakScanner ORIGINAL_BLEAK_CLIENT = bleak.BleakClient diff --git a/homeassistant/components/bluetooth/wrappers.py b/homeassistant/components/bluetooth/wrappers.py new file mode 100644 index 00000000000..9565886c9a3 --- /dev/null +++ b/homeassistant/components/bluetooth/wrappers.py @@ -0,0 +1,274 @@ +"""Bleak wrappers for bluetooth.""" +from __future__ import annotations + +import asyncio +from collections.abc import Callable +import contextlib +from dataclasses import dataclass +import logging +from typing import Any, Final + +from bleak import BleakClient, BleakError +from bleak.backends.client import BaseBleakClient, get_platform_client_backend_type +from bleak.backends.device import BLEDevice +from bleak.backends.scanner import AdvertisementDataCallback, BaseBleakScanner +from bleak_retry_connector import NO_RSSI_VALUE, freshen_ble_device + +from homeassistant.core import CALLBACK_TYPE, callback as hass_callback +from homeassistant.helpers.frame import report + +from . import models +from .models import HaBluetoothConnector + +FILTER_UUIDS: Final = "UUIDs" +_LOGGER = logging.getLogger(__name__) + + +@dataclass +class _HaWrappedBleakBackend: + """Wrap bleak backend to make it usable by Home Assistant.""" + + device: BLEDevice + client: type[BaseBleakClient] + + +class HaBleakScannerWrapper(BaseBleakScanner): + """A wrapper that uses the single instance.""" + + def __init__( + self, + *args: Any, + detection_callback: AdvertisementDataCallback | None = None, + service_uuids: list[str] | None = None, + **kwargs: Any, + ) -> None: + """Initialize the BleakScanner.""" + self._detection_cancel: CALLBACK_TYPE | None = None + self._mapped_filters: dict[str, set[str]] = {} + self._advertisement_data_callback: AdvertisementDataCallback | None = None + remapped_kwargs = { + "detection_callback": detection_callback, + "service_uuids": service_uuids or [], + **kwargs, + } + self._map_filters(*args, **remapped_kwargs) + super().__init__( + detection_callback=detection_callback, service_uuids=service_uuids or [] + ) + + @classmethod + async def discover(cls, timeout: float = 5.0, **kwargs: Any) -> list[BLEDevice]: + """Discover devices.""" + assert models.MANAGER is not None + return list(models.MANAGER.async_discovered_devices(True)) + + async def stop(self, *args: Any, **kwargs: Any) -> None: + """Stop scanning for devices.""" + + async def start(self, *args: Any, **kwargs: Any) -> None: + """Start scanning for devices.""" + + def _map_filters(self, *args: Any, **kwargs: Any) -> bool: + """Map the filters.""" + mapped_filters = {} + if filters := kwargs.get("filters"): + if filter_uuids := filters.get(FILTER_UUIDS): + mapped_filters[FILTER_UUIDS] = set(filter_uuids) + else: + _LOGGER.warning("Only %s filters are supported", FILTER_UUIDS) + if service_uuids := kwargs.get("service_uuids"): + mapped_filters[FILTER_UUIDS] = set(service_uuids) + if mapped_filters == self._mapped_filters: + return False + self._mapped_filters = mapped_filters + return True + + def set_scanning_filter(self, *args: Any, **kwargs: Any) -> None: + """Set the filters to use.""" + if self._map_filters(*args, **kwargs): + self._setup_detection_callback() + + def _cancel_callback(self) -> None: + """Cancel callback.""" + if self._detection_cancel: + self._detection_cancel() + self._detection_cancel = None + + @property + def discovered_devices(self) -> list[BLEDevice]: + """Return a list of discovered devices.""" + assert models.MANAGER is not None + return list(models.MANAGER.async_discovered_devices(True)) + + def register_detection_callback( + self, callback: AdvertisementDataCallback | None + ) -> None: + """Register a callback that is called when a device is discovered or has a property changed. + + This method takes the callback and registers it with the long running + scanner. + """ + self._advertisement_data_callback = callback + self._setup_detection_callback() + + def _setup_detection_callback(self) -> None: + """Set up the detection callback.""" + if self._advertisement_data_callback is None: + return + self._cancel_callback() + super().register_detection_callback(self._advertisement_data_callback) + assert models.MANAGER is not None + assert self._callback is not None + self._detection_cancel = models.MANAGER.async_register_bleak_callback( + self._callback, self._mapped_filters + ) + + def __del__(self) -> None: + """Delete the BleakScanner.""" + if self._detection_cancel: + # Nothing to do if event loop is already closed + with contextlib.suppress(RuntimeError): + asyncio.get_running_loop().call_soon_threadsafe(self._detection_cancel) + + +class HaBleakClientWrapper(BleakClient): + """Wrap the BleakClient to ensure it does not shutdown our scanner. + + If an address is passed into BleakClient instead of a BLEDevice, + bleak will quietly start a new scanner under the hood to resolve + the address. This can cause a conflict with our scanner. We need + to handle translating the address to the BLEDevice in this case + to avoid the whole stack from getting stuck in an in progress state + when an integration does this. + """ + + def __init__( # pylint: disable=super-init-not-called, keyword-arg-before-vararg + self, + address_or_ble_device: str | BLEDevice, + disconnected_callback: Callable[[BleakClient], None] | None = None, + *args: Any, + timeout: float = 10.0, + **kwargs: Any, + ) -> None: + """Initialize the BleakClient.""" + if isinstance(address_or_ble_device, BLEDevice): + self.__address = address_or_ble_device.address + else: + report( + "attempted to call BleakClient with an address instead of a BLEDevice", + exclude_integrations={"bluetooth"}, + error_if_core=False, + ) + self.__address = address_or_ble_device + self.__disconnected_callback = disconnected_callback + self.__timeout = timeout + self.__ble_device: BLEDevice | None = None + self._backend: BaseBleakClient | None = None # type: ignore[assignment] + + @property + def is_connected(self) -> bool: + """Return True if the client is connected to a device.""" + return self._backend is not None and self._backend.is_connected + + def set_disconnected_callback( + self, + callback: Callable[[BleakClient], None] | None, + **kwargs: Any, + ) -> None: + """Set the disconnect callback.""" + self.__disconnected_callback = callback + if self._backend: + self._backend.set_disconnected_callback(callback, **kwargs) # type: ignore[arg-type] + + async def connect(self, **kwargs: Any) -> bool: + """Connect to the specified GATT server.""" + if ( + not self._backend + or not self.__ble_device + or not self._async_get_backend_for_ble_device(self.__ble_device) + ): + assert models.MANAGER is not None + wrapped_backend = ( + self._async_get_backend() or self._async_get_fallback_backend() + ) + self.__ble_device = ( + await freshen_ble_device(wrapped_backend.device) + or wrapped_backend.device + ) + self._backend = wrapped_backend.client( + self.__ble_device, + disconnected_callback=self.__disconnected_callback, + timeout=self.__timeout, + hass=models.MANAGER.hass, + ) + return await super().connect(**kwargs) + + @hass_callback + def _async_get_backend_for_ble_device( + self, ble_device: BLEDevice + ) -> _HaWrappedBleakBackend | None: + """Get the backend for a BLEDevice.""" + details = ble_device.details + if not isinstance(details, dict) or "connector" not in details: + # If client is not defined in details + # its the client for this platform + cls = get_platform_client_backend_type() + return _HaWrappedBleakBackend(ble_device, cls) + + connector: HaBluetoothConnector = details["connector"] + # Make sure the backend can connect to the device + # as some backends have connection limits + if not connector.can_connect(): + return None + + return _HaWrappedBleakBackend(ble_device, connector.client) + + @hass_callback + def _async_get_backend(self) -> _HaWrappedBleakBackend | None: + """Get the bleak backend for the given address.""" + assert models.MANAGER is not None + address = self.__address + ble_device = models.MANAGER.async_ble_device_from_address(address, True) + if ble_device is None: + raise BleakError(f"No device found for address {address}") + + if backend := self._async_get_backend_for_ble_device(ble_device): + return backend + + return None + + @hass_callback + def _async_get_fallback_backend(self) -> _HaWrappedBleakBackend: + """Get a fallback backend for the given address.""" + # + # The preferred backend cannot currently connect the device + # because it is likely out of connection slots. + # + # We need to try all backends to find one that can + # connect to the device. + # + assert models.MANAGER is not None + address = self.__address + device_advertisement_datas = models.MANAGER.async_get_discovered_devices_and_advertisement_data_by_address( + address, True + ) + for device_advertisement_data in sorted( + device_advertisement_datas, + key=lambda device_advertisement_data: device_advertisement_data[1].rssi + or NO_RSSI_VALUE, + reverse=True, + ): + if backend := self._async_get_backend_for_ble_device( + device_advertisement_data[0] + ): + return backend + + raise BleakError( + f"No backend with an available connection slot that can reach address {address} was found" + ) + + async def disconnect(self) -> bool: + """Disconnect from the device.""" + if self._backend is None: + return True + return await self._backend.disconnect() diff --git a/tests/components/bluetooth/__init__.py b/tests/components/bluetooth/__init__.py index 0ffa347c6d2..fbe5be2e34d 100644 --- a/tests/components/bluetooth/__init__.py +++ b/tests/components/bluetooth/__init__.py @@ -5,12 +5,15 @@ import time from typing import Any from unittest.mock import patch +from bleak import BleakClient from bleak.backends.scanner import AdvertisementData, BLEDevice from bluetooth_adapters import DEFAULT_ADDRESS from homeassistant.components.bluetooth import ( DOMAIN, SOURCE_LOCAL, + BluetoothServiceInfo, + BluetoothServiceInfoBleak, async_get_advertisement_callback, models, ) @@ -29,6 +32,7 @@ __all__ = ( "patch_all_discovered_devices", "patch_discovered_devices", "generate_advertisement_data", + "MockBleakClient", ) ADVERTISEMENT_DATA_DEFAULTS = { @@ -94,7 +98,7 @@ def inject_advertisement_with_time_and_source_connectable( ) -> None: """Inject an advertisement into the manager from a specific source at a time and connectable status.""" async_get_advertisement_callback(hass)( - models.BluetoothServiceInfoBleak( + BluetoothServiceInfoBleak( name=adv.local_name or device.name or device.address, address=device.address, rssi=adv.rssi, @@ -111,7 +115,7 @@ def inject_advertisement_with_time_and_source_connectable( def inject_bluetooth_service_info_bleak( - hass: HomeAssistant, info: models.BluetoothServiceInfoBleak + hass: HomeAssistant, info: BluetoothServiceInfoBleak ) -> None: """Inject an advertisement into the manager with connectable status.""" advertisement_data = generate_advertisement_data( @@ -137,7 +141,7 @@ def inject_bluetooth_service_info_bleak( def inject_bluetooth_service_info( - hass: HomeAssistant, info: models.BluetoothServiceInfo + hass: HomeAssistant, info: BluetoothServiceInfo ) -> None: """Inject a BluetoothServiceInfo into the manager.""" advertisement_data = generate_advertisement_data( # type: ignore[no-untyped-call] @@ -190,3 +194,29 @@ async def _async_setup_with_adapter( assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) await hass.async_block_till_done() return entry + + +class MockBleakClient(BleakClient): + """Mock bleak client.""" + + def __init__(self, *args, **kwargs): + """Mock init.""" + super().__init__(*args, **kwargs) + self._device_path = "/dev/test" + + @property + def is_connected(self) -> bool: + """Mock connected.""" + return True + + async def connect(self, *args, **kwargs): + """Mock connect.""" + return True + + async def disconnect(self, *args, **kwargs): + """Mock disconnect.""" + pass + + async def get_services(self, *args, **kwargs): + """Mock get_services.""" + return [] diff --git a/tests/components/bluetooth/test_advertisement_tracker.py b/tests/components/bluetooth/test_advertisement_tracker.py index 6eb2b5a968e..dd52ba38a47 100644 --- a/tests/components/bluetooth/test_advertisement_tracker.py +++ b/tests/components/bluetooth/test_advertisement_tracker.py @@ -7,6 +7,7 @@ from unittest.mock import patch from bleak.backends.scanner import AdvertisementData, BLEDevice from homeassistant.components.bluetooth import ( + BaseHaScanner, async_register_scanner, async_track_unavailable, ) @@ -17,7 +18,6 @@ from homeassistant.components.bluetooth.const import ( SOURCE_LOCAL, UNAVAILABLE_TRACK_SECONDS, ) -from homeassistant.components.bluetooth.models import BaseHaScanner from homeassistant.core import callback from homeassistant.util import dt as dt_util diff --git a/tests/components/bluetooth/test_base_scanner.py b/tests/components/bluetooth/test_base_scanner.py new file mode 100644 index 00000000000..9808bb74bc6 --- /dev/null +++ b/tests/components/bluetooth/test_base_scanner.py @@ -0,0 +1,267 @@ +"""Tests for the Bluetooth base scanner models.""" +from __future__ import annotations + +from datetime import timedelta +import time +from unittest.mock import patch + +from bleak.backends.device import BLEDevice +from bleak.backends.scanner import AdvertisementData + +from homeassistant.components.bluetooth import BaseHaRemoteScanner, HaBluetoothConnector +from homeassistant.components.bluetooth.const import ( + CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, + FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, +) +import homeassistant.util.dt as dt_util + +from . import MockBleakClient, _get_manager, generate_advertisement_data + +from tests.common import async_fire_time_changed + + +async def test_remote_scanner(hass): + """Test the remote scanner base class merges advertisement_data.""" + manager = _get_manager() + + switchbot_device = BLEDevice( + "44:44:33:11:23:45", + "wohand", + {}, + rssi=-100, + ) + switchbot_device_adv = generate_advertisement_data( + local_name="wohand", + service_uuids=["050a021a-0000-1000-8000-00805f9b34fb"], + service_data={"050a021a-0000-1000-8000-00805f9b34fb": b"\n\xff"}, + manufacturer_data={1: b"\x01"}, + rssi=-100, + ) + switchbot_device_2 = BLEDevice( + "44:44:33:11:23:45", + "w", + {}, + rssi=-100, + ) + switchbot_device_adv_2 = generate_advertisement_data( + local_name="wohand", + service_uuids=["00000001-0000-1000-8000-00805f9b34fb"], + service_data={"00000001-0000-1000-8000-00805f9b34fb": b"\n\xff"}, + manufacturer_data={1: b"\x01", 2: b"\x02"}, + rssi=-100, + ) + + class FakeScanner(BaseHaRemoteScanner): + def inject_advertisement( + self, device: BLEDevice, advertisement_data: AdvertisementData + ) -> None: + """Inject an advertisement.""" + self._async_on_advertisement( + device.address, + advertisement_data.rssi, + device.name, + advertisement_data.service_uuids, + advertisement_data.service_data, + advertisement_data.manufacturer_data, + advertisement_data.tx_power, + ) + + new_info_callback = manager.scanner_adv_received + connector = ( + HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), + ) + scanner = FakeScanner(hass, "esp32", new_info_callback, connector, True) + scanner.async_setup() + cancel = manager.async_register_scanner(scanner, True) + + scanner.inject_advertisement(switchbot_device, switchbot_device_adv) + + data = scanner.discovered_devices_and_advertisement_data + discovered_device, discovered_adv_data = data[switchbot_device.address] + assert discovered_device.address == switchbot_device.address + assert discovered_device.name == switchbot_device.name + assert ( + discovered_adv_data.manufacturer_data == switchbot_device_adv.manufacturer_data + ) + assert discovered_adv_data.service_data == switchbot_device_adv.service_data + assert discovered_adv_data.service_uuids == switchbot_device_adv.service_uuids + scanner.inject_advertisement(switchbot_device_2, switchbot_device_adv_2) + + data = scanner.discovered_devices_and_advertisement_data + discovered_device, discovered_adv_data = data[switchbot_device.address] + assert discovered_device.address == switchbot_device.address + assert discovered_device.name == switchbot_device.name + assert discovered_adv_data.manufacturer_data == {1: b"\x01", 2: b"\x02"} + assert discovered_adv_data.service_data == { + "050a021a-0000-1000-8000-00805f9b34fb": b"\n\xff", + "00000001-0000-1000-8000-00805f9b34fb": b"\n\xff", + } + assert set(discovered_adv_data.service_uuids) == { + "050a021a-0000-1000-8000-00805f9b34fb", + "00000001-0000-1000-8000-00805f9b34fb", + } + + cancel() + + +async def test_remote_scanner_expires_connectable(hass): + """Test the remote scanner expires stale connectable data.""" + manager = _get_manager() + + switchbot_device = BLEDevice( + "44:44:33:11:23:45", + "wohand", + {}, + rssi=-100, + ) + switchbot_device_adv = generate_advertisement_data( + local_name="wohand", + service_uuids=[], + manufacturer_data={1: b"\x01"}, + rssi=-100, + ) + + class FakeScanner(BaseHaRemoteScanner): + def inject_advertisement( + self, device: BLEDevice, advertisement_data: AdvertisementData + ) -> None: + """Inject an advertisement.""" + self._async_on_advertisement( + device.address, + advertisement_data.rssi, + device.name, + advertisement_data.service_uuids, + advertisement_data.service_data, + advertisement_data.manufacturer_data, + advertisement_data.tx_power, + ) + + new_info_callback = manager.scanner_adv_received + connector = ( + HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), + ) + scanner = FakeScanner(hass, "esp32", new_info_callback, connector, True) + scanner.async_setup() + cancel = manager.async_register_scanner(scanner, True) + + start_time_monotonic = time.monotonic() + scanner.inject_advertisement(switchbot_device, switchbot_device_adv) + + devices = scanner.discovered_devices + assert len(scanner.discovered_devices) == 1 + assert len(scanner.discovered_devices_and_advertisement_data) == 1 + assert devices[0].name == "wohand" + + expire_monotonic = ( + start_time_monotonic + + CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + + 1 + ) + expire_utc = dt_util.utcnow() + timedelta( + seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1 + ) + with patch( + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", + return_value=expire_monotonic, + ): + async_fire_time_changed(hass, expire_utc) + await hass.async_block_till_done() + + devices = scanner.discovered_devices + assert len(scanner.discovered_devices) == 0 + assert len(scanner.discovered_devices_and_advertisement_data) == 0 + + cancel() + + +async def test_remote_scanner_expires_non_connectable(hass): + """Test the remote scanner expires stale non connectable data.""" + manager = _get_manager() + + switchbot_device = BLEDevice( + "44:44:33:11:23:45", + "wohand", + {}, + rssi=-100, + ) + switchbot_device_adv = generate_advertisement_data( + local_name="wohand", + service_uuids=[], + manufacturer_data={1: b"\x01"}, + rssi=-100, + ) + + class FakeScanner(BaseHaRemoteScanner): + def inject_advertisement( + self, device: BLEDevice, advertisement_data: AdvertisementData + ) -> None: + """Inject an advertisement.""" + self._async_on_advertisement( + device.address, + advertisement_data.rssi, + device.name, + advertisement_data.service_uuids, + advertisement_data.service_data, + advertisement_data.manufacturer_data, + advertisement_data.tx_power, + ) + + new_info_callback = manager.scanner_adv_received + connector = ( + HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), + ) + scanner = FakeScanner(hass, "esp32", new_info_callback, connector, False) + scanner.async_setup() + cancel = manager.async_register_scanner(scanner, True) + + start_time_monotonic = time.monotonic() + scanner.inject_advertisement(switchbot_device, switchbot_device_adv) + + devices = scanner.discovered_devices + assert len(scanner.discovered_devices) == 1 + assert len(scanner.discovered_devices_and_advertisement_data) == 1 + assert devices[0].name == "wohand" + + assert ( + FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + > CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + ) + + # The connectable timeout is not used for non connectable devices + expire_monotonic = ( + start_time_monotonic + + CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + + 1 + ) + expire_utc = dt_util.utcnow() + timedelta( + seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1 + ) + with patch( + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", + return_value=expire_monotonic, + ): + async_fire_time_changed(hass, expire_utc) + await hass.async_block_till_done() + + assert len(scanner.discovered_devices) == 1 + assert len(scanner.discovered_devices_and_advertisement_data) == 1 + + # The non connectable timeout is used for non connectable devices + # which is always longer than the connectable timeout + expire_monotonic = ( + start_time_monotonic + FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1 + ) + expire_utc = dt_util.utcnow() + timedelta( + seconds=FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1 + ) + with patch( + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", + return_value=expire_monotonic, + ): + async_fire_time_changed(hass, expire_utc) + await hass.async_block_till_done() + + assert len(scanner.discovered_devices) == 0 + assert len(scanner.discovered_devices_and_advertisement_data) == 0 + + cancel() diff --git a/tests/components/bluetooth/test_init.py b/tests/components/bluetooth/test_init.py index da315984f3c..a6f594669ca 100644 --- a/tests/components/bluetooth/test_init.py +++ b/tests/components/bluetooth/test_init.py @@ -11,13 +11,13 @@ import pytest from homeassistant.components import bluetooth from homeassistant.components.bluetooth import ( + BaseHaScanner, BluetoothChange, BluetoothScanningMode, BluetoothServiceInfo, async_process_advertisements, async_rediscover_address, async_track_unavailable, - models, scanner, ) from homeassistant.components.bluetooth.const import ( @@ -36,6 +36,7 @@ from homeassistant.components.bluetooth.match import ( SERVICE_DATA_UUID, SERVICE_UUID, ) +from homeassistant.components.bluetooth.wrappers import HaBleakScannerWrapper from homeassistant.config_entries import ConfigEntryState from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant, callback @@ -2210,7 +2211,7 @@ async def test_wrapped_instance_with_filter( empty_adv = generate_advertisement_data(local_name="empty") assert _get_manager() is not None - scanner = models.HaBleakScannerWrapper( + scanner = HaBleakScannerWrapper( filters={"UUIDs": ["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]} ) scanner.register_detection_callback(_device_detected) @@ -2282,7 +2283,7 @@ async def test_wrapped_instance_with_service_uuids( empty_adv = generate_advertisement_data(local_name="empty") assert _get_manager() is not None - scanner = models.HaBleakScannerWrapper( + scanner = HaBleakScannerWrapper( service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] ) scanner.register_detection_callback(_device_detected) @@ -2332,7 +2333,7 @@ async def test_wrapped_instance_with_broken_callbacks( ) assert _get_manager() is not None - scanner = models.HaBleakScannerWrapper( + scanner = HaBleakScannerWrapper( service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] ) scanner.register_detection_callback(_device_detected) @@ -2381,7 +2382,7 @@ async def test_wrapped_instance_changes_uuids( empty_adv = generate_advertisement_data(local_name="empty") assert _get_manager() is not None - scanner = models.HaBleakScannerWrapper() + scanner = HaBleakScannerWrapper() scanner.set_scanning_filter( service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] ) @@ -2436,7 +2437,7 @@ async def test_wrapped_instance_changes_filters( empty_adv = generate_advertisement_data(local_name="empty") assert _get_manager() is not None - scanner = models.HaBleakScannerWrapper() + scanner = HaBleakScannerWrapper() scanner.set_scanning_filter( filters={"UUIDs": ["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]} ) @@ -2468,7 +2469,7 @@ async def test_wrapped_instance_unsupported_filter( hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) await hass.async_block_till_done() assert _get_manager() is not None - scanner = models.HaBleakScannerWrapper() + scanner = HaBleakScannerWrapper() scanner.set_scanning_filter( filters={ "unsupported": ["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], @@ -2643,12 +2644,12 @@ async def test_no_auto_detect_bluetooth_adapters_windows(hass): async def test_getting_the_scanner_returns_the_wrapped_instance(hass, enable_bluetooth): """Test getting the scanner returns the wrapped instance.""" scanner = bluetooth.async_get_scanner(hass) - assert isinstance(scanner, models.HaBleakScannerWrapper) + assert isinstance(scanner, HaBleakScannerWrapper) async def test_scanner_count_connectable(hass, enable_bluetooth): """Test getting the connectable scanner count.""" - scanner = models.BaseHaScanner(hass, "any") + scanner = BaseHaScanner(hass, "any") cancel = bluetooth.async_register_scanner(hass, scanner, False) assert bluetooth.async_scanner_count(hass, connectable=True) == 1 cancel() @@ -2656,7 +2657,7 @@ async def test_scanner_count_connectable(hass, enable_bluetooth): async def test_scanner_count(hass, enable_bluetooth): """Test getting the connectable and non-connectable scanner count.""" - scanner = models.BaseHaScanner(hass, "any") + scanner = BaseHaScanner(hass, "any") cancel = bluetooth.async_register_scanner(hass, scanner, False) assert bluetooth.async_scanner_count(hass, connectable=False) == 2 cancel() diff --git a/tests/components/bluetooth/test_manager.py b/tests/components/bluetooth/test_manager.py index c61d8f4c9f1..a7ea4fefe6b 100644 --- a/tests/components/bluetooth/test_manager.py +++ b/tests/components/bluetooth/test_manager.py @@ -8,7 +8,7 @@ from bluetooth_adapters import AdvertisementHistory import pytest from homeassistant.components import bluetooth -from homeassistant.components.bluetooth import models +from homeassistant.components.bluetooth import BaseHaScanner from homeassistant.components.bluetooth.manager import ( FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, ) @@ -26,9 +26,7 @@ from . import ( @pytest.fixture def register_hci0_scanner(hass: HomeAssistant) -> None: """Register an hci0 scanner.""" - cancel = bluetooth.async_register_scanner( - hass, models.BaseHaScanner(hass, "hci0"), True - ) + cancel = bluetooth.async_register_scanner(hass, BaseHaScanner(hass, "hci0"), True) yield cancel() @@ -36,9 +34,7 @@ def register_hci0_scanner(hass: HomeAssistant) -> None: @pytest.fixture def register_hci1_scanner(hass: HomeAssistant) -> None: """Register an hci1 scanner.""" - cancel = bluetooth.async_register_scanner( - hass, models.BaseHaScanner(hass, "hci1"), True - ) + cancel = bluetooth.async_register_scanner(hass, BaseHaScanner(hass, "hci1"), True) yield cancel() @@ -420,7 +416,7 @@ async def test_switching_adapters_when_one_goes_away( ): """Test switching adapters when one goes away.""" cancel_hci2 = bluetooth.async_register_scanner( - hass, models.BaseHaScanner(hass, "hci2"), True + hass, BaseHaScanner(hass, "hci2"), True ) address = "44:44:33:11:23:45" diff --git a/tests/components/bluetooth/test_models.py b/tests/components/bluetooth/test_models.py index 520c8d6d2f7..0ad510b97ff 100644 --- a/tests/components/bluetooth/test_models.py +++ b/tests/components/bluetooth/test_models.py @@ -1,62 +1,29 @@ """Tests for the Bluetooth integration models.""" from __future__ import annotations -from datetime import timedelta -import time from unittest.mock import patch import bleak -from bleak import BleakClient, BleakError +from bleak import BleakError from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData import pytest -from homeassistant.components.bluetooth.models import ( - CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, - FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, - BaseHaRemoteScanner, +from homeassistant.components.bluetooth import ( BaseHaScanner, - HaBleakClientWrapper, HaBleakScannerWrapper, HaBluetoothConnector, ) -import homeassistant.util.dt as dt_util +from homeassistant.components.bluetooth.wrappers import HaBleakClientWrapper from . import ( + MockBleakClient, _get_manager, generate_advertisement_data, inject_advertisement, inject_advertisement_with_source, ) -from tests.common import async_fire_time_changed - - -class MockBleakClient(BleakClient): - """Mock bleak client.""" - - def __init__(self, *args, **kwargs): - """Mock init.""" - super().__init__(*args, **kwargs) - self._device_path = "/dev/test" - - @property - def is_connected(self) -> bool: - """Mock connected.""" - return True - - async def connect(self, *args, **kwargs): - """Mock connect.""" - return True - - async def disconnect(self, *args, **kwargs): - """Mock disconnect.""" - pass - - async def get_services(self, *args, **kwargs): - """Mock get_services.""" - return [] - async def test_wrapped_bleak_scanner(hass, enable_bluetooth): """Test wrapped bleak scanner dispatches calls as expected.""" @@ -353,250 +320,3 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab client.set_disconnected_callback(lambda client: None) await client.disconnect() cancel() - - -async def test_remote_scanner(hass): - """Test the remote scanner base class merges advertisement_data.""" - manager = _get_manager() - - switchbot_device = BLEDevice( - "44:44:33:11:23:45", - "wohand", - {}, - rssi=-100, - ) - switchbot_device_adv = generate_advertisement_data( - local_name="wohand", - service_uuids=["050a021a-0000-1000-8000-00805f9b34fb"], - service_data={"050a021a-0000-1000-8000-00805f9b34fb": b"\n\xff"}, - manufacturer_data={1: b"\x01"}, - rssi=-100, - ) - switchbot_device_2 = BLEDevice( - "44:44:33:11:23:45", - "w", - {}, - rssi=-100, - ) - switchbot_device_adv_2 = generate_advertisement_data( - local_name="wohand", - service_uuids=["00000001-0000-1000-8000-00805f9b34fb"], - service_data={"00000001-0000-1000-8000-00805f9b34fb": b"\n\xff"}, - manufacturer_data={1: b"\x01", 2: b"\x02"}, - rssi=-100, - ) - - class FakeScanner(BaseHaRemoteScanner): - def inject_advertisement( - self, device: BLEDevice, advertisement_data: AdvertisementData - ) -> None: - """Inject an advertisement.""" - self._async_on_advertisement( - device.address, - advertisement_data.rssi, - device.name, - advertisement_data.service_uuids, - advertisement_data.service_data, - advertisement_data.manufacturer_data, - advertisement_data.tx_power, - ) - - new_info_callback = manager.scanner_adv_received - connector = ( - HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), - ) - scanner = FakeScanner(hass, "esp32", new_info_callback, connector, True) - scanner.async_setup() - cancel = manager.async_register_scanner(scanner, True) - - scanner.inject_advertisement(switchbot_device, switchbot_device_adv) - - data = scanner.discovered_devices_and_advertisement_data - discovered_device, discovered_adv_data = data[switchbot_device.address] - assert discovered_device.address == switchbot_device.address - assert discovered_device.name == switchbot_device.name - assert ( - discovered_adv_data.manufacturer_data == switchbot_device_adv.manufacturer_data - ) - assert discovered_adv_data.service_data == switchbot_device_adv.service_data - assert discovered_adv_data.service_uuids == switchbot_device_adv.service_uuids - scanner.inject_advertisement(switchbot_device_2, switchbot_device_adv_2) - - data = scanner.discovered_devices_and_advertisement_data - discovered_device, discovered_adv_data = data[switchbot_device.address] - assert discovered_device.address == switchbot_device.address - assert discovered_device.name == switchbot_device.name - assert discovered_adv_data.manufacturer_data == {1: b"\x01", 2: b"\x02"} - assert discovered_adv_data.service_data == { - "050a021a-0000-1000-8000-00805f9b34fb": b"\n\xff", - "00000001-0000-1000-8000-00805f9b34fb": b"\n\xff", - } - assert set(discovered_adv_data.service_uuids) == { - "050a021a-0000-1000-8000-00805f9b34fb", - "00000001-0000-1000-8000-00805f9b34fb", - } - - cancel() - - -async def test_remote_scanner_expires_connectable(hass): - """Test the remote scanner expires stale connectable data.""" - manager = _get_manager() - - switchbot_device = BLEDevice( - "44:44:33:11:23:45", - "wohand", - {}, - rssi=-100, - ) - switchbot_device_adv = generate_advertisement_data( - local_name="wohand", - service_uuids=[], - manufacturer_data={1: b"\x01"}, - rssi=-100, - ) - - class FakeScanner(BaseHaRemoteScanner): - def inject_advertisement( - self, device: BLEDevice, advertisement_data: AdvertisementData - ) -> None: - """Inject an advertisement.""" - self._async_on_advertisement( - device.address, - advertisement_data.rssi, - device.name, - advertisement_data.service_uuids, - advertisement_data.service_data, - advertisement_data.manufacturer_data, - advertisement_data.tx_power, - ) - - new_info_callback = manager.scanner_adv_received - connector = ( - HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), - ) - scanner = FakeScanner(hass, "esp32", new_info_callback, connector, True) - scanner.async_setup() - cancel = manager.async_register_scanner(scanner, True) - - start_time_monotonic = time.monotonic() - scanner.inject_advertisement(switchbot_device, switchbot_device_adv) - - devices = scanner.discovered_devices - assert len(scanner.discovered_devices) == 1 - assert len(scanner.discovered_devices_and_advertisement_data) == 1 - assert devices[0].name == "wohand" - - expire_monotonic = ( - start_time_monotonic - + CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS - + 1 - ) - expire_utc = dt_util.utcnow() + timedelta( - seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1 - ) - with patch( - "homeassistant.components.bluetooth.models.MONOTONIC_TIME", - return_value=expire_monotonic, - ): - async_fire_time_changed(hass, expire_utc) - await hass.async_block_till_done() - - devices = scanner.discovered_devices - assert len(scanner.discovered_devices) == 0 - assert len(scanner.discovered_devices_and_advertisement_data) == 0 - - cancel() - - -async def test_remote_scanner_expires_non_connectable(hass): - """Test the remote scanner expires stale non connectable data.""" - manager = _get_manager() - - switchbot_device = BLEDevice( - "44:44:33:11:23:45", - "wohand", - {}, - rssi=-100, - ) - switchbot_device_adv = generate_advertisement_data( - local_name="wohand", - service_uuids=[], - manufacturer_data={1: b"\x01"}, - rssi=-100, - ) - - class FakeScanner(BaseHaRemoteScanner): - def inject_advertisement( - self, device: BLEDevice, advertisement_data: AdvertisementData - ) -> None: - """Inject an advertisement.""" - self._async_on_advertisement( - device.address, - advertisement_data.rssi, - device.name, - advertisement_data.service_uuids, - advertisement_data.service_data, - advertisement_data.manufacturer_data, - advertisement_data.tx_power, - ) - - new_info_callback = manager.scanner_adv_received - connector = ( - HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), - ) - scanner = FakeScanner(hass, "esp32", new_info_callback, connector, False) - scanner.async_setup() - cancel = manager.async_register_scanner(scanner, True) - - start_time_monotonic = time.monotonic() - scanner.inject_advertisement(switchbot_device, switchbot_device_adv) - - devices = scanner.discovered_devices - assert len(scanner.discovered_devices) == 1 - assert len(scanner.discovered_devices_and_advertisement_data) == 1 - assert devices[0].name == "wohand" - - assert ( - FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS - > CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS - ) - - # The connectable timeout is not used for non connectable devices - expire_monotonic = ( - start_time_monotonic - + CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS - + 1 - ) - expire_utc = dt_util.utcnow() + timedelta( - seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1 - ) - with patch( - "homeassistant.components.bluetooth.models.MONOTONIC_TIME", - return_value=expire_monotonic, - ): - async_fire_time_changed(hass, expire_utc) - await hass.async_block_till_done() - - assert len(scanner.discovered_devices) == 1 - assert len(scanner.discovered_devices_and_advertisement_data) == 1 - - # The non connectable timeout is used for non connectable devices - # which is always longer than the connectable timeout - expire_monotonic = ( - start_time_monotonic + FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1 - ) - expire_utc = dt_util.utcnow() + timedelta( - seconds=FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1 - ) - with patch( - "homeassistant.components.bluetooth.models.MONOTONIC_TIME", - return_value=expire_monotonic, - ): - async_fire_time_changed(hass, expire_utc) - await hass.async_block_till_done() - - assert len(scanner.discovered_devices) == 0 - assert len(scanner.discovered_devices_and_advertisement_data) == 0 - - cancel() diff --git a/tests/components/bluetooth/test_usage.py b/tests/components/bluetooth/test_usage.py index 7e0d97d3d91..dc6d88ca5d1 100644 --- a/tests/components/bluetooth/test_usage.py +++ b/tests/components/bluetooth/test_usage.py @@ -7,14 +7,14 @@ import bleak from bleak.backends.device import BLEDevice import bleak_retry_connector -from homeassistant.components.bluetooth.models import ( - HaBleakClientWrapper, - HaBleakScannerWrapper, -) from homeassistant.components.bluetooth.usage import ( install_multiple_bleak_catcher, uninstall_multiple_bleak_catcher, ) +from homeassistant.components.bluetooth.wrappers import ( + HaBleakClientWrapper, + HaBleakScannerWrapper, +) from . import _get_manager From 5c00b8b823438f6d7739577577247c7dc2e13ca3 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sun, 20 Nov 2022 16:17:45 +0100 Subject: [PATCH 0575/1033] Wait for MQTT entry on snips integration startup (#81733) Wait for MQTT integration to load --- homeassistant/components/snips/__init__.py | 8 ++++++++ tests/components/snips/test_init.py | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/homeassistant/components/snips/__init__.py b/homeassistant/components/snips/__init__.py index 01471b13bc7..d4619fa3b3a 100644 --- a/homeassistant/components/snips/__init__.py +++ b/homeassistant/components/snips/__init__.py @@ -90,6 +90,14 @@ SERVICE_SCHEMA_FEEDBACK = vol.Schema( async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Activate Snips component.""" + # Make sure MQTT is available and the entry is loaded + if not hass.config_entries.async_entries( + mqtt.DOMAIN + ) or not await hass.config_entries.async_wait_component( + hass.config_entries.async_entries(mqtt.DOMAIN)[0] + ): + _LOGGER.error("MQTT integration is not available") + return False async def async_set_feedback(site_ids, state): """Set Feedback sound state.""" diff --git a/tests/components/snips/test_init.py b/tests/components/snips/test_init.py index 14e58d54ebe..9582c83b267 100644 --- a/tests/components/snips/test_init.py +++ b/tests/components/snips/test_init.py @@ -28,6 +28,23 @@ async def test_snips_config(hass, mqtt_mock): assert result +async def test_snips_no_mqtt(hass, caplog): + """Test Snips Config.""" + result = await async_setup_component( + hass, + "snips", + { + "snips": { + "feedback_sounds": True, + "probability_threshold": 0.5, + "site_ids": ["default", "remote"], + } + }, + ) + assert not result + assert "MQTT integration is not available" in caplog.text + + async def test_snips_bad_config(hass, mqtt_mock): """Test Snips bad config.""" result = await async_setup_component( From a1d9d7116c8815c94d046f307c72a2793340849c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 20 Nov 2022 09:38:30 -0600 Subject: [PATCH 0576/1033] Prevent powerwall from switching addresses if its online (#82410) * Prevent powerwall from switching addresses if its online If the wifi interface was discovered we would switch the ip address in the entry to the wifi ip even if it was connected via ethernet * cover * more cover --- .../components/powerwall/__init__.py | 13 +- .../components/powerwall/config_flow.py | 49 ++++++- .../components/powerwall/test_config_flow.py | 136 +++++++++++++++++- 3 files changed, 194 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/powerwall/__init__.py b/homeassistant/components/powerwall/__init__.py index 2fdf3d61d20..d8550e6f46b 100644 --- a/homeassistant/components/powerwall/__init__.py +++ b/homeassistant/components/powerwall/__init__.py @@ -18,7 +18,7 @@ from tesla_powerwall import ( from homeassistant.components import persistent_notification from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, Platform -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady import homeassistant.helpers.config_validation as cv from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -221,6 +221,17 @@ def _fetch_powerwall_data(power_wall: Powerwall) -> PowerwallData: ) +@callback +def async_last_update_was_successful(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Return True if the last update was successful.""" + return bool( + (domain_data := hass.data.get(DOMAIN)) + and (entry_data := domain_data.get(entry.entry_id)) + and (coordinator := entry_data.get(POWERWALL_COORDINATOR)) + and coordinator.last_update_success + ) + + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/powerwall/config_flow.py b/homeassistant/components/powerwall/config_flow.py index b9f6f3969fd..6e4f40bf01b 100644 --- a/homeassistant/components/powerwall/config_flow.py +++ b/homeassistant/components/powerwall/config_flow.py @@ -20,11 +20,18 @@ from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD from homeassistant.data_entry_flow import FlowResult from homeassistant.util.network import is_ip_address +from . import async_last_update_was_successful from .const import DOMAIN _LOGGER = logging.getLogger(__name__) +ENTRY_FAILURE_STATES = { + config_entries.ConfigEntryState.SETUP_ERROR, + config_entries.ConfigEntryState.SETUP_RETRY, +} + + def _login_and_fetch_site_info( power_wall: Powerwall, password: str ) -> tuple[SiteInfo, str]: @@ -34,6 +41,17 @@ def _login_and_fetch_site_info( return power_wall.get_site_info(), power_wall.get_gateway_din() +def _powerwall_is_reachable(ip_address: str, password: str) -> bool: + """Check if the powerwall is reachable.""" + try: + Powerwall(ip_address).login(password) + except AccessDeniedError: + return True + except PowerwallUnreachableError: + return False + return True + + async def validate_input( hass: core.HomeAssistant, data: dict[str, str] ) -> dict[str, str]: @@ -69,13 +87,31 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self.title: str | None = None self.reauth_entry: config_entries.ConfigEntry | None = None + async def _async_powerwall_is_offline( + self, entry: config_entries.ConfigEntry + ) -> bool: + """Check if the power wall is offline. + + We define offline by the config entry + is in a failure/retry state or the updates + are failing and the powerwall is unreachable + since device may be updating. + """ + ip_address = entry.data[CONF_IP_ADDRESS] + password = entry.data[CONF_PASSWORD] + return bool( + entry.state in ENTRY_FAILURE_STATES + or not async_last_update_was_successful(self.hass, entry) + ) and not await self.hass.async_add_executor_job( + _powerwall_is_reachable, ip_address, password + ) + async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult: """Handle dhcp discovery.""" self.ip_address = discovery_info.ip gateway_din = discovery_info.hostname.upper() # The hostname is the gateway_din (unique_id) await self.async_set_unique_id(gateway_din) - self._abort_if_unique_id_configured(updates={CONF_IP_ADDRESS: self.ip_address}) for entry in self._async_current_entries(include_ignore=False): if entry.data[CONF_IP_ADDRESS] == discovery_info.ip: if entry.unique_id is not None and is_ip_address(entry.unique_id): @@ -86,6 +122,17 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self.hass.config_entries.async_reload(entry.entry_id) ) return self.async_abort(reason="already_configured") + if entry.unique_id == gateway_din: + if await self._async_powerwall_is_offline(entry): + if self.hass.config_entries.async_update_entry( + entry, data={**entry.data, CONF_IP_ADDRESS: self.ip_address} + ): + self.hass.async_create_task( + self.hass.config_entries.async_reload(entry.entry_id) + ) + return self.async_abort(reason="already_configured") + # Still need to abort for ignored entries + self._abort_if_unique_id_configured() self.context["title_placeholders"] = { "name": gateway_din, "ip_address": self.ip_address, diff --git a/tests/components/powerwall/test_config_flow.py b/tests/components/powerwall/test_config_flow.py index f4dcfd87b8b..11861a8238c 100644 --- a/tests/components/powerwall/test_config_flow.py +++ b/tests/components/powerwall/test_config_flow.py @@ -1,6 +1,6 @@ """Test the Powerwall config flow.""" -from unittest.mock import patch +from unittest.mock import MagicMock, patch from tesla_powerwall import ( AccessDeniedError, @@ -18,6 +18,7 @@ from .mocks import ( MOCK_GATEWAY_DIN, _mock_powerwall_side_effect, _mock_powerwall_site_name, + _mock_powerwall_with_fixtures, ) from tests.common import MockConfigEntry @@ -351,7 +352,7 @@ async def test_dhcp_discovery_update_ip_address(hass): unique_id=MOCK_GATEWAY_DIN, ) entry.add_to_hass(hass) - mock_powerwall = await _mock_powerwall_site_name(hass, "Some site") + mock_powerwall = MagicMock(login=MagicMock(side_effect=PowerwallUnreachableError)) with patch( "homeassistant.components.powerwall.config_flow.Powerwall", @@ -375,6 +376,70 @@ async def test_dhcp_discovery_update_ip_address(hass): assert entry.data[CONF_IP_ADDRESS] == "1.1.1.1" +async def test_dhcp_discovery_does_not_update_ip_when_auth_fails(hass): + """Test we do not switch to another interface when auth is failing.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=VALID_CONFIG, + unique_id=MOCK_GATEWAY_DIN, + ) + entry.add_to_hass(hass) + mock_powerwall = MagicMock(login=MagicMock(side_effect=AccessDeniedError("any"))) + + with patch( + "homeassistant.components.powerwall.config_flow.Powerwall", + return_value=mock_powerwall, + ), patch( + "homeassistant.components.powerwall.async_setup_entry", + return_value=True, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_DHCP}, + data=dhcp.DhcpServiceInfo( + ip="1.1.1.1", + macaddress="AA:BB:CC:DD:EE:FF", + hostname=MOCK_GATEWAY_DIN.lower(), + ), + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_configured" + assert entry.data[CONF_IP_ADDRESS] == "1.2.3.4" + + +async def test_dhcp_discovery_does_not_update_ip_when_auth_successful(hass): + """Test we do not switch to another interface when auth is successful.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=VALID_CONFIG, + unique_id=MOCK_GATEWAY_DIN, + ) + entry.add_to_hass(hass) + mock_powerwall = MagicMock(login=MagicMock(return_value=True)) + + with patch( + "homeassistant.components.powerwall.config_flow.Powerwall", + return_value=mock_powerwall, + ), patch( + "homeassistant.components.powerwall.async_setup_entry", + return_value=True, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_DHCP}, + data=dhcp.DhcpServiceInfo( + ip="1.1.1.1", + macaddress="AA:BB:CC:DD:EE:FF", + hostname=MOCK_GATEWAY_DIN.lower(), + ), + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_configured" + assert entry.data[CONF_IP_ADDRESS] == "1.2.3.4" + + async def test_dhcp_discovery_updates_unique_id(hass): """Test we can update the unique id from dhcp.""" entry = MockConfigEntry( @@ -406,3 +471,70 @@ async def test_dhcp_discovery_updates_unique_id(hass): assert result["reason"] == "already_configured" assert entry.data[CONF_IP_ADDRESS] == "1.2.3.4" assert entry.unique_id == MOCK_GATEWAY_DIN + + +async def test_dhcp_discovery_updates_unique_id_when_entry_is_failed(hass): + """Test we can update the unique id from dhcp in a failed state.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=VALID_CONFIG, + unique_id="1.2.3.4", + ) + entry.add_to_hass(hass) + entry.state = config_entries.ConfigEntryState.SETUP_ERROR + mock_powerwall = await _mock_powerwall_site_name(hass, "Some site") + + with patch( + "homeassistant.components.powerwall.config_flow.Powerwall", + return_value=mock_powerwall, + ), patch( + "homeassistant.components.powerwall.async_setup_entry", + return_value=True, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_DHCP}, + data=dhcp.DhcpServiceInfo( + ip="1.2.3.4", + macaddress="AA:BB:CC:DD:EE:FF", + hostname=MOCK_GATEWAY_DIN.lower(), + ), + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_configured" + assert entry.data[CONF_IP_ADDRESS] == "1.2.3.4" + assert entry.unique_id == MOCK_GATEWAY_DIN + + +async def test_discovered_wifi_does_not_update_ip_if_is_still_online(hass) -> None: + """Test a discovery does not update the ip unless the powerwall at the old ip is offline.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=VALID_CONFIG, + unique_id=MOCK_GATEWAY_DIN, + ) + entry.add_to_hass(hass) + mock_powerwall = await _mock_powerwall_with_fixtures(hass) + + with patch( + "homeassistant.components.powerwall.config_flow.Powerwall", + return_value=mock_powerwall, + ), patch( + "homeassistant.components.powerwall.Powerwall", return_value=mock_powerwall + ): + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_DHCP}, + data=dhcp.DhcpServiceInfo( + ip="1.2.3.5", + macaddress="AA:BB:CC:DD:EE:FF", + hostname=MOCK_GATEWAY_DIN.lower(), + ), + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_configured" + assert entry.data[CONF_IP_ADDRESS] == "1.2.3.4" From adff01876a0c807c873cd4777ed26ec95df94720 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Sun, 20 Nov 2022 10:39:47 -0500 Subject: [PATCH 0577/1033] Attempt to fix occasional Flo timeouts (#82408) * Attempt to fix occasional Flo timeouts * remove gather --- homeassistant/components/flo/device.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/flo/device.py b/homeassistant/components/flo/device.py index ca4b6aa6234..d6e05c17136 100644 --- a/homeassistant/components/flo/device.py +++ b/homeassistant/components/flo/device.py @@ -1,7 +1,6 @@ """Flo device object.""" from __future__ import annotations -import asyncio from datetime import datetime, timedelta from typing import Any @@ -40,14 +39,10 @@ class FloDeviceDataUpdateCoordinator(DataUpdateCoordinator): async def _async_update_data(self): """Update data via library.""" try: - async with timeout(10): - await asyncio.gather( - *[ - self.send_presence_ping(), - self._update_device(), - self._update_consumption_data(), - ] - ) + async with timeout(20): + await self.send_presence_ping() + await self._update_device() + await self._update_consumption_data() except (RequestError) as error: raise UpdateFailed(error) from error From 6ec8c63b5ccb49bfa190de22725325c181028bd1 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sun, 20 Nov 2022 17:58:37 +0200 Subject: [PATCH 0578/1033] Use HA SensorDeviceClass in ruuvitag-ble (#82321) * Use HA SensorDeviceClass in ruuvitag-ble Refs https://github.com/home-assistant/core/pull/81327#discussion_r1024565439 * Use HA SensorDeviceClass even more --- .../components/ruuvitag_ble/sensor.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/ruuvitag_ble/sensor.py b/homeassistant/components/ruuvitag_ble/sensor.py index e02851b38d5..f99e78f65a6 100644 --- a/homeassistant/components/ruuvitag_ble/sensor.py +++ b/homeassistant/components/ruuvitag_ble/sensor.py @@ -6,7 +6,7 @@ from typing import Optional, Union from sensor_state_data import ( DeviceKey, SensorDescription, - SensorDeviceClass, + SensorDeviceClass as SSDSensorDeviceClass, SensorUpdate, Units, ) @@ -20,6 +20,7 @@ from homeassistant.components.bluetooth.passive_update_processor import ( PassiveBluetoothProcessorEntity, ) from homeassistant.components.sensor import ( + SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, @@ -31,46 +32,45 @@ from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info from .const import DOMAIN SENSOR_DESCRIPTIONS = { - (SensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( - key=f"{SensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", + (SSDSensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( + key=f"{SSDSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=const.TEMP_CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), - (SensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( - key=f"{SensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}", + (SSDSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( + key=f"{SSDSensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}", device_class=SensorDeviceClass.HUMIDITY, native_unit_of_measurement=const.PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, ), - (SensorDeviceClass.PRESSURE, Units.PRESSURE_HPA): SensorEntityDescription( - key=f"{SensorDeviceClass.PRESSURE}_{Units.PRESSURE_HPA}", + (SSDSensorDeviceClass.PRESSURE, Units.PRESSURE_HPA): SensorEntityDescription( + key=f"{SSDSensorDeviceClass.PRESSURE}_{Units.PRESSURE_HPA}", device_class=SensorDeviceClass.PRESSURE, native_unit_of_measurement=const.PRESSURE_HPA, state_class=SensorStateClass.MEASUREMENT, ), ( - SensorDeviceClass.VOLTAGE, + SSDSensorDeviceClass.VOLTAGE, Units.ELECTRIC_POTENTIAL_MILLIVOLT, ): SensorEntityDescription( - key=f"{SensorDeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_MILLIVOLT}", + key=f"{SSDSensorDeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_MILLIVOLT}", device_class=SensorDeviceClass.VOLTAGE, native_unit_of_measurement=const.ELECTRIC_POTENTIAL_MILLIVOLT, state_class=SensorStateClass.MEASUREMENT, ), ( - SensorDeviceClass.SIGNAL_STRENGTH, + SSDSensorDeviceClass.SIGNAL_STRENGTH, Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT, ): SensorEntityDescription( - key=f"{SensorDeviceClass.SIGNAL_STRENGTH}_{Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT}", + key=f"{SSDSensorDeviceClass.SIGNAL_STRENGTH}_{Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT}", device_class=SensorDeviceClass.SIGNAL_STRENGTH, native_unit_of_measurement=const.SIGNAL_STRENGTH_DECIBELS_MILLIWATT, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, ), - (SensorDeviceClass.COUNT, None): SensorEntityDescription( + (SSDSensorDeviceClass.COUNT, None): SensorEntityDescription( key="movement_counter", - device_class=SensorDeviceClass.COUNT, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, ), @@ -86,7 +86,7 @@ def _device_key_to_bluetooth_entity_key( def _to_sensor_key( description: SensorDescription, -) -> tuple[SensorDeviceClass, Units | None]: +) -> tuple[SSDSensorDeviceClass, Units | None]: assert description.device_class is not None return (description.device_class, description.native_unit_of_measurement) From 3f5649092ec88e31317d2545c6ec0e260af4dd36 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 20 Nov 2022 11:33:03 -0600 Subject: [PATCH 0579/1033] Break out bluetooth apis into api.py (#82416) * Break out bluetooth apis into api.py Like #82291 this is not a functional change. * cleanups --- .../components/bluetooth/__init__.py | 184 +++--------------- homeassistant/components/bluetooth/api.py | 173 ++++++++++++++++ tests/components/bluetooth/test_models.py | 7 +- 3 files changed, 200 insertions(+), 164 deletions(-) create mode 100644 homeassistant/components/bluetooth/api.py diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index fb816d4bfc6..278b88364f1 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -1,14 +1,11 @@ """The bluetooth integration.""" from __future__ import annotations -from asyncio import Future -from collections.abc import Callable, Iterable import datetime import logging import platform -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING -import async_timeout from awesomeversion import AwesomeVersion from bluetooth_adapters import ( ADAPTER_ADDRESS, @@ -29,7 +26,7 @@ from homeassistant.config_entries import ( ConfigEntry, ) from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP -from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback +from homeassistant.core import HomeAssistant, callback as hass_callback from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import device_registry as dr, discovery_flow from homeassistant.helpers.debounce import Debouncer @@ -42,6 +39,21 @@ from homeassistant.helpers.issue_registry import ( from homeassistant.loader import async_get_bluetooth from . import models +from .api import ( + _get_manager, + async_address_present, + async_ble_device_from_address, + async_discovered_service_info, + async_get_advertisement_callback, + async_get_scanner, + async_last_service_info, + async_process_advertisements, + async_rediscover_address, + async_register_callback, + async_register_scanner, + async_scanner_count, + async_track_unavailable, +) from .base_scanner import BaseHaRemoteScanner, BaseHaScanner from .const import ( BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS, @@ -56,21 +68,15 @@ from .const import ( ) from .manager import BluetoothManager from .match import BluetoothCallbackMatcher, IntegrationMatcher -from .models import ( - BluetoothCallback, - BluetoothChange, - BluetoothScanningMode, - ProcessAdvertisementCallback, -) +from .models import BluetoothCallback, BluetoothChange, BluetoothScanningMode from .scanner import HaScanner, ScannerStartError -from .wrappers import HaBleakScannerWrapper, HaBluetoothConnector +from .wrappers import HaBluetoothConnector if TYPE_CHECKING: - from bleak.backends.device import BLEDevice - from homeassistant.helpers.typing import ConfigType __all__ = [ + "async_address_present", "async_ble_device_from_address", "async_discovered_service_info", "async_get_scanner", @@ -83,6 +89,8 @@ __all__ = [ "async_scanner_count", "BaseHaScanner", "BaseHaRemoteScanner", + "BluetoothCallbackMatcher", + "BluetoothChange", "BluetoothServiceInfo", "BluetoothServiceInfoBleak", "BluetoothScanningMode", @@ -97,151 +105,7 @@ _LOGGER = logging.getLogger(__name__) RECOMMENDED_MIN_HAOS_VERSION = AwesomeVersion("9.0.dev0") -def _get_manager(hass: HomeAssistant) -> BluetoothManager: - """Get the bluetooth manager.""" - return cast(BluetoothManager, hass.data[DATA_MANAGER]) - - -@hass_callback -def async_get_scanner(hass: HomeAssistant) -> HaBleakScannerWrapper: - """Return a HaBleakScannerWrapper. - - This is a wrapper around our BleakScanner singleton that allows - multiple integrations to share the same BleakScanner. - """ - return HaBleakScannerWrapper() - - -@hass_callback -def async_scanner_count(hass: HomeAssistant, connectable: bool = True) -> int: - """Return the number of scanners currently in use.""" - return _get_manager(hass).async_scanner_count(connectable) - - -@hass_callback -def async_discovered_service_info( - hass: HomeAssistant, connectable: bool = True -) -> Iterable[BluetoothServiceInfoBleak]: - """Return the discovered devices list.""" - if DATA_MANAGER not in hass.data: - return [] - return _get_manager(hass).async_discovered_service_info(connectable) - - -@hass_callback -def async_last_service_info( - hass: HomeAssistant, address: str, connectable: bool = True -) -> BluetoothServiceInfoBleak | None: - """Return the last service info for an address.""" - if DATA_MANAGER not in hass.data: - return None - return _get_manager(hass).async_last_service_info(address, connectable) - - -@hass_callback -def async_ble_device_from_address( - hass: HomeAssistant, address: str, connectable: bool = True -) -> BLEDevice | None: - """Return BLEDevice for an address if its present.""" - if DATA_MANAGER not in hass.data: - return None - return _get_manager(hass).async_ble_device_from_address(address, connectable) - - -@hass_callback -def async_address_present( - hass: HomeAssistant, address: str, connectable: bool = True -) -> bool: - """Check if an address is present in the bluetooth device list.""" - if DATA_MANAGER not in hass.data: - return False - return _get_manager(hass).async_address_present(address, connectable) - - -@hass_callback -def async_register_callback( - hass: HomeAssistant, - callback: BluetoothCallback, - match_dict: BluetoothCallbackMatcher | None, - mode: BluetoothScanningMode, -) -> Callable[[], None]: - """Register to receive a callback on bluetooth change. - - mode is currently not used as we only support active scanning. - Passive scanning will be available in the future. The flag - is required to be present to avoid a future breaking change - when we support passive scanning. - - Returns a callback that can be used to cancel the registration. - """ - return _get_manager(hass).async_register_callback(callback, match_dict) - - -async def async_process_advertisements( - hass: HomeAssistant, - callback: ProcessAdvertisementCallback, - match_dict: BluetoothCallbackMatcher, - mode: BluetoothScanningMode, - timeout: int, -) -> BluetoothServiceInfoBleak: - """Process advertisements until callback returns true or timeout expires.""" - done: Future[BluetoothServiceInfoBleak] = Future() - - @hass_callback - def _async_discovered_device( - service_info: BluetoothServiceInfoBleak, change: BluetoothChange - ) -> None: - if not done.done() and callback(service_info): - done.set_result(service_info) - - unload = _get_manager(hass).async_register_callback( - _async_discovered_device, match_dict - ) - - try: - async with async_timeout.timeout(timeout): - return await done - finally: - unload() - - -@hass_callback -def async_track_unavailable( - hass: HomeAssistant, - callback: Callable[[BluetoothServiceInfoBleak], None], - address: str, - connectable: bool = True, -) -> Callable[[], None]: - """Register to receive a callback when an address is unavailable. - - Returns a callback that can be used to cancel the registration. - """ - return _get_manager(hass).async_track_unavailable(callback, address, connectable) - - -@hass_callback -def async_rediscover_address(hass: HomeAssistant, address: str) -> None: - """Trigger discovery of devices which have already been seen.""" - _get_manager(hass).async_rediscover_address(address) - - -@hass_callback -def async_register_scanner( - hass: HomeAssistant, scanner: BaseHaScanner, connectable: bool -) -> CALLBACK_TYPE: - """Register a BleakScanner.""" - return _get_manager(hass).async_register_scanner(scanner, connectable) - - -@hass_callback -def async_get_advertisement_callback( - hass: HomeAssistant, -) -> Callable[[BluetoothServiceInfoBleak], None]: - """Get the advertisement callback.""" - return _get_manager(hass).scanner_adv_received - - -async def async_get_adapter_from_address( +async def _async_get_adapter_from_address( hass: HomeAssistant, address: str ) -> str | None: """Get an adapter by the address.""" @@ -419,7 +283,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up a config entry for a bluetooth scanner.""" address = entry.unique_id assert address is not None - adapter = await async_get_adapter_from_address(hass, address) + adapter = await _async_get_adapter_from_address(hass, address) if adapter is None: raise ConfigEntryNotReady( f"Bluetooth adapter {adapter} with address {address} not found" diff --git a/homeassistant/components/bluetooth/api.py b/homeassistant/components/bluetooth/api.py new file mode 100644 index 00000000000..1399dd84dc6 --- /dev/null +++ b/homeassistant/components/bluetooth/api.py @@ -0,0 +1,173 @@ +"""The bluetooth integration apis. + +These APIs are the only documented way to interact with the bluetooth integration. +""" +from __future__ import annotations + +from asyncio import Future +from collections.abc import Callable, Iterable +from typing import TYPE_CHECKING, cast + +import async_timeout +from home_assistant_bluetooth import BluetoothServiceInfoBleak + +from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback + +from .base_scanner import BaseHaScanner +from .const import DATA_MANAGER +from .manager import BluetoothManager +from .match import BluetoothCallbackMatcher +from .models import ( + BluetoothCallback, + BluetoothChange, + BluetoothScanningMode, + ProcessAdvertisementCallback, +) +from .wrappers import HaBleakScannerWrapper + +if TYPE_CHECKING: + from bleak.backends.device import BLEDevice + + +def _get_manager(hass: HomeAssistant) -> BluetoothManager: + """Get the bluetooth manager.""" + return cast(BluetoothManager, hass.data[DATA_MANAGER]) + + +@hass_callback +def async_get_scanner(hass: HomeAssistant) -> HaBleakScannerWrapper: + """Return a HaBleakScannerWrapper. + + This is a wrapper around our BleakScanner singleton that allows + multiple integrations to share the same BleakScanner. + """ + return HaBleakScannerWrapper() + + +@hass_callback +def async_scanner_count(hass: HomeAssistant, connectable: bool = True) -> int: + """Return the number of scanners currently in use.""" + return _get_manager(hass).async_scanner_count(connectable) + + +@hass_callback +def async_discovered_service_info( + hass: HomeAssistant, connectable: bool = True +) -> Iterable[BluetoothServiceInfoBleak]: + """Return the discovered devices list.""" + if DATA_MANAGER not in hass.data: + return [] + return _get_manager(hass).async_discovered_service_info(connectable) + + +@hass_callback +def async_last_service_info( + hass: HomeAssistant, address: str, connectable: bool = True +) -> BluetoothServiceInfoBleak | None: + """Return the last service info for an address.""" + if DATA_MANAGER not in hass.data: + return None + return _get_manager(hass).async_last_service_info(address, connectable) + + +@hass_callback +def async_ble_device_from_address( + hass: HomeAssistant, address: str, connectable: bool = True +) -> BLEDevice | None: + """Return BLEDevice for an address if its present.""" + if DATA_MANAGER not in hass.data: + return None + return _get_manager(hass).async_ble_device_from_address(address, connectable) + + +@hass_callback +def async_address_present( + hass: HomeAssistant, address: str, connectable: bool = True +) -> bool: + """Check if an address is present in the bluetooth device list.""" + if DATA_MANAGER not in hass.data: + return False + return _get_manager(hass).async_address_present(address, connectable) + + +@hass_callback +def async_register_callback( + hass: HomeAssistant, + callback: BluetoothCallback, + match_dict: BluetoothCallbackMatcher | None, + mode: BluetoothScanningMode, +) -> Callable[[], None]: + """Register to receive a callback on bluetooth change. + + mode is currently not used as we only support active scanning. + Passive scanning will be available in the future. The flag + is required to be present to avoid a future breaking change + when we support passive scanning. + + Returns a callback that can be used to cancel the registration. + """ + return _get_manager(hass).async_register_callback(callback, match_dict) + + +async def async_process_advertisements( + hass: HomeAssistant, + callback: ProcessAdvertisementCallback, + match_dict: BluetoothCallbackMatcher, + mode: BluetoothScanningMode, + timeout: int, +) -> BluetoothServiceInfoBleak: + """Process advertisements until callback returns true or timeout expires.""" + done: Future[BluetoothServiceInfoBleak] = Future() + + @hass_callback + def _async_discovered_device( + service_info: BluetoothServiceInfoBleak, change: BluetoothChange + ) -> None: + if not done.done() and callback(service_info): + done.set_result(service_info) + + unload = _get_manager(hass).async_register_callback( + _async_discovered_device, match_dict + ) + + try: + async with async_timeout.timeout(timeout): + return await done + finally: + unload() + + +@hass_callback +def async_track_unavailable( + hass: HomeAssistant, + callback: Callable[[BluetoothServiceInfoBleak], None], + address: str, + connectable: bool = True, +) -> Callable[[], None]: + """Register to receive a callback when an address is unavailable. + + Returns a callback that can be used to cancel the registration. + """ + return _get_manager(hass).async_track_unavailable(callback, address, connectable) + + +@hass_callback +def async_rediscover_address(hass: HomeAssistant, address: str) -> None: + """Trigger discovery of devices which have already been seen.""" + _get_manager(hass).async_rediscover_address(address) + + +@hass_callback +def async_register_scanner( + hass: HomeAssistant, scanner: BaseHaScanner, connectable: bool +) -> CALLBACK_TYPE: + """Register a BleakScanner.""" + return _get_manager(hass).async_register_scanner(scanner, connectable) + + +@hass_callback +def async_get_advertisement_callback( + hass: HomeAssistant, +) -> Callable[[BluetoothServiceInfoBleak], None]: + """Get the advertisement callback.""" + return _get_manager(hass).scanner_adv_received diff --git a/tests/components/bluetooth/test_models.py b/tests/components/bluetooth/test_models.py index 0ad510b97ff..01245d93184 100644 --- a/tests/components/bluetooth/test_models.py +++ b/tests/components/bluetooth/test_models.py @@ -9,12 +9,11 @@ from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData import pytest -from homeassistant.components.bluetooth import ( - BaseHaScanner, +from homeassistant.components.bluetooth import BaseHaScanner, HaBluetoothConnector +from homeassistant.components.bluetooth.wrappers import ( + HaBleakClientWrapper, HaBleakScannerWrapper, - HaBluetoothConnector, ) -from homeassistant.components.bluetooth.wrappers import HaBleakClientWrapper from . import ( MockBleakClient, From 574f6f3fd1f6d7b5e07ed51c74e50399ed89e854 Mon Sep 17 00:00:00 2001 From: Felipe Martins Diel <41558831+felipediel@users.noreply.github.com> Date: Sun, 20 Nov 2022 18:13:18 -0300 Subject: [PATCH 0580/1033] Bump broadlink to 0.18.3 (#82427) fixes undefined --- homeassistant/components/broadlink/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/broadlink/manifest.json b/homeassistant/components/broadlink/manifest.json index 4ae0e39cf04..04a4d284161 100644 --- a/homeassistant/components/broadlink/manifest.json +++ b/homeassistant/components/broadlink/manifest.json @@ -2,7 +2,7 @@ "domain": "broadlink", "name": "Broadlink", "documentation": "https://www.home-assistant.io/integrations/broadlink", - "requirements": ["broadlink==0.18.2"], + "requirements": ["broadlink==0.18.3"], "codeowners": ["@danielhiversen", "@felipediel", "@L-I-Am"], "config_flow": true, "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index a1960e096b4..c4a4140f9d1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -467,7 +467,7 @@ boschshcpy==0.2.35 boto3==1.20.24 # homeassistant.components.broadlink -broadlink==0.18.2 +broadlink==0.18.3 # homeassistant.components.brother brother==2.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fb77432ea45..06c21a6c504 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -377,7 +377,7 @@ bond-async==0.1.22 boschshcpy==0.2.35 # homeassistant.components.broadlink -broadlink==0.18.2 +broadlink==0.18.3 # homeassistant.components.brother brother==2.0.0 From e897c8c47ca2f25ceee6de01fe03f8b4df104f48 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 20 Nov 2022 22:45:34 +0100 Subject: [PATCH 0581/1033] Fix round typing [shelly] (#82436) --- homeassistant/components/shelly/light.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/shelly/light.py b/homeassistant/components/shelly/light.py index dda9a41bb89..805b8147ba5 100644 --- a/homeassistant/components/shelly/light.py +++ b/homeassistant/components/shelly/light.py @@ -184,16 +184,17 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): @property def brightness(self) -> int: """Return the brightness of this light between 0..255.""" + brightness_pct: int if self.mode == "color": if self.control_result: brightness_pct = self.control_result["gain"] else: - brightness_pct = self.block.gain + brightness_pct = cast(int, self.block.gain) else: if self.control_result: brightness_pct = self.control_result["brightness"] else: - brightness_pct = self.block.brightness + brightness_pct = cast(int, self.block.brightness) return round(255 * brightness_pct / 100) From 4bb69fee230d8f48b669e861c4dd4675f33be348 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 20 Nov 2022 22:52:43 +0100 Subject: [PATCH 0582/1033] Fix round typing [accuweather] (#82433) --- homeassistant/components/accuweather/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/accuweather/sensor.py b/homeassistant/components/accuweather/sensor.py index 78041c5309c..2dbefe19965 100644 --- a/homeassistant/components/accuweather/sensor.py +++ b/homeassistant/components/accuweather/sensor.py @@ -253,7 +253,7 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( name="Cloud ceiling", state_class=SensorStateClass.MEASUREMENT, unit_fn=lambda metric: LENGTH_METERS if metric else LENGTH_FEET, - value_fn=lambda data, unit: round(data[unit][ATTR_VALUE]), + value_fn=lambda data, unit: round(cast(float, data[unit][ATTR_VALUE])), ), AccuWeatherSensorDescription( key="CloudCover", From 32f3eb722e78eadc2ac3bdee4218f15c0fea097e Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Sun, 20 Nov 2022 23:59:25 +0200 Subject: [PATCH 0583/1033] Add tests coverage for Shelly entity (#82432) * Add tests coverage for Shelly entity * Make it generator expression --- .coveragerc | 1 - homeassistant/components/shelly/entity.py | 63 +---------- tests/components/shelly/__init__.py | 9 ++ tests/components/shelly/conftest.py | 13 ++- tests/components/shelly/test_binary_sensor.py | 35 ++++++ tests/components/shelly/test_diagnostics.py | 3 +- tests/components/shelly/test_sensor.py | 103 +++++++++++++++++ tests/components/shelly/test_switch.py | 105 ++++++++++++++++++ 8 files changed, 272 insertions(+), 60 deletions(-) diff --git a/.coveragerc b/.coveragerc index 426fd3a6634..33e0763a5dd 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1111,7 +1111,6 @@ omit = homeassistant/components/seventeentrack/sensor.py homeassistant/components/shelly/climate.py homeassistant/components/shelly/coordinator.py - homeassistant/components/shelly/entity.py homeassistant/components/shelly/number.py homeassistant/components/shelly/utils.py homeassistant/components/shiftr/* diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index 96f566f6a2e..3c57bee8f02 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -21,12 +21,7 @@ from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import CONF_SLEEP_PERIOD, LOGGER -from .coordinator import ( - ShellyBlockCoordinator, - ShellyRpcCoordinator, - ShellyRpcPollingCoordinator, - get_entry_data, -) +from .coordinator import ShellyBlockCoordinator, ShellyRpcCoordinator, get_entry_data from .utils import ( async_remove_shelly_entity, get_block_entity_name, @@ -269,21 +264,10 @@ def async_setup_entry_rest( """Set up entities for REST sensors.""" coordinator = get_entry_data(hass)[config_entry.entry_id].rest assert coordinator - entities = [] - for sensor_id in sensors: - description = sensors.get(sensor_id) - - if not coordinator.device.settings.get("sleep_mode"): - entities.append((sensor_id, description)) - - if not entities: - return async_add_entities( - [ - sensor_class(coordinator, sensor_id, description) - for sensor_id, description in entities - ] + sensor_class(coordinator, sensor_id, sensors[sensor_id]) + for sensor_id in sensors ) @@ -350,10 +334,6 @@ class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]): """When entity is added to HASS.""" self.async_on_remove(self.coordinator.async_add_listener(self._update_callback)) - async def async_update(self) -> None: - """Update entity with latest info.""" - await self.coordinator.async_request_refresh() - @callback def _update_callback(self) -> None: """Handle device update.""" @@ -373,16 +353,12 @@ class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]): self.coordinator.entry.async_start_reauth(self.hass) -class ShellyRpcEntity(entity.Entity): +class ShellyRpcEntity(CoordinatorEntity[ShellyRpcCoordinator]): """Helper class to represent a rpc entity.""" - def __init__( - self, - coordinator: ShellyRpcCoordinator | ShellyRpcPollingCoordinator, - key: str, - ) -> None: + def __init__(self, coordinator: ShellyRpcCoordinator, key: str) -> None: """Initialize Shelly entity.""" - self.coordinator = coordinator + super().__init__(coordinator) self.key = key self._attr_should_poll = False self._attr_device_info = { @@ -405,10 +381,6 @@ class ShellyRpcEntity(entity.Entity): """When entity is added to HASS.""" self.async_on_remove(self.coordinator.async_add_listener(self._update_callback)) - async def async_update(self) -> None: - """Update entity with latest info.""" - await self.coordinator.async_request_refresh() - @callback def _update_callback(self) -> None: """Handle device update.""" @@ -525,16 +497,6 @@ class ShellyRestAttributeEntity(CoordinatorEntity[ShellyBlockCoordinator]): ) return self._last_value - @property - def extra_state_attributes(self) -> dict[str, Any] | None: - """Return the state attributes.""" - if self.entity_description.extra_state_attributes is None: - return None - - return self.entity_description.extra_state_attributes( - self.block_coordinator.device.status - ) - class ShellyRpcAttributeEntity(ShellyRpcEntity, entity.Entity): """Helper class to represent a rpc attribute.""" @@ -586,19 +548,6 @@ class ShellyRpcAttributeEntity(ShellyRpcEntity, entity.Entity): self.coordinator.device.status[self.key][self.entity_description.sub_key] ) - @property - def extra_state_attributes(self) -> dict[str, Any] | None: - """Return the state attributes.""" - if self.entity_description.extra_state_attributes is None: - return None - - assert self.coordinator.device.shelly - - return self.entity_description.extra_state_attributes( - self.coordinator.device.status[self.key][self.entity_description.sub_key], - self.coordinator.device.shelly, - ) - class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEntity): """Represent a shelly sleeping block attribute entity.""" diff --git a/tests/components/shelly/__init__.py b/tests/components/shelly/__init__.py index 36165afe72d..3adc1eaf49a 100644 --- a/tests/components/shelly/__init__.py +++ b/tests/components/shelly/__init__.py @@ -12,6 +12,7 @@ from homeassistant.components.shelly.const import ( CONF_SLEEP_PERIOD, DOMAIN, REST_SENSORS_UPDATE_INTERVAL, + RPC_SENSORS_POLLING_INTERVAL, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST @@ -84,6 +85,14 @@ async def mock_rest_update(hass: HomeAssistant): await hass.async_block_till_done() +async def mock_polling_rpc_update(hass: HomeAssistant): + """Move time to create polling RPC sensors update event.""" + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=RPC_SENSORS_POLLING_INTERVAL) + ) + await hass.async_block_till_done() + + def register_entity( hass: HomeAssistant, domain: str, diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py index 0264e675674..2f940530319 100644 --- a/tests/components/shelly/conftest.py +++ b/tests/components/shelly/conftest.py @@ -29,6 +29,7 @@ MOCK_SETTINGS = { "fw": "20201124-092159/v1.9.0@57ac4ad8", "relays": [{"btn_type": "momentary"}, {"btn_type": "toggle"}], "rollers": [{"positioning": True}], + "external_power": 0, } @@ -97,12 +98,20 @@ MOCK_BLOCKS = [ set_state=AsyncMock(side_effect=mock_light_set_state), ), Mock( - sensor_ids={"motion": 0, "temp": 22.1}, + sensor_ids={"motion": 0, "temp": 22.1, "gas": "mild"}, motion=0, temp=22.1, + gas="mild", description="sensor_0", type="sensor", ), + Mock( + sensor_ids={"battery": 98}, + battery=98, + cfgChanged=0, + description="device_0", + type="device", + ), ] MOCK_CONFIG = { @@ -165,6 +174,8 @@ MOCK_STATUS_RPC = { "stable": {"version": "some_beta_version"}, } }, + "voltmeter": {"voltage": 4.3}, + "wifi": {"rssi": -63}, } diff --git a/tests/components/shelly/test_binary_sensor.py b/tests/components/shelly/test_binary_sensor.py index c7e2faaf47c..6257bd191e6 100644 --- a/tests/components/shelly/test_binary_sensor.py +++ b/tests/components/shelly/test_binary_sensor.py @@ -4,6 +4,7 @@ from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.core import State +from homeassistant.helpers.entity_registry import async_get from . import ( init_integration, @@ -32,6 +33,25 @@ async def test_block_binary_sensor(hass, mock_block_device, monkeypatch): assert hass.states.get(entity_id).state == STATE_ON +async def test_block_binary_sensor_extra_state_attr( + hass, mock_block_device, monkeypatch +): + """Test block binary sensor extra state attributes.""" + entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_gas" + await init_integration(hass, 1) + + state = hass.states.get(entity_id) + assert state.state == STATE_ON + assert state.attributes.get("detected") == "mild" + + monkeypatch.setattr(mock_block_device.blocks[SENSOR_BLOCK_ID], "gas", "none") + mock_block_device.mock_update() + + state = hass.states.get(entity_id) + assert state.state == STATE_OFF + assert state.attributes.get("detected") == "none" + + async def test_block_rest_binary_sensor(hass, mock_block_device, monkeypatch): """Test block REST binary sensor.""" entity_id = register_entity(hass, BINARY_SENSOR_DOMAIN, "test_name_cloud", "cloud") @@ -105,6 +125,21 @@ async def test_rpc_binary_sensor(hass, mock_rpc_device, monkeypatch) -> None: assert hass.states.get(entity_id).state == STATE_ON +async def test_rpc_binary_sensor_removal(hass, mock_rpc_device, monkeypatch): + """Test RPC binary sensor is removed due to removal_condition.""" + entity_registry = async_get(hass) + entity_id = register_entity( + hass, BINARY_SENSOR_DOMAIN, "test_cover_0_input", "input:0-input" + ) + + assert entity_registry.async_get(entity_id) is not None + + monkeypatch.setattr(mock_rpc_device, "status", {"input:0": {"state": False}}) + await init_integration(hass, 2) + + assert entity_registry.async_get(entity_id) is None + + async def test_rpc_sleeping_binary_sensor( hass, mock_rpc_device, device_reg, monkeypatch ) -> None: diff --git a/tests/components/shelly/test_diagnostics.py b/tests/components/shelly/test_diagnostics.py index a99b28d48e0..ccac9bcc1b0 100644 --- a/tests/components/shelly/test_diagnostics.py +++ b/tests/components/shelly/test_diagnostics.py @@ -70,6 +70,7 @@ async def test_rpc_config_entry_diagnostics( "beta": {"version": "some_beta_version"}, "stable": {"version": "some_beta_version"}, } - } + }, + "wifi": {"rssi": -63}, }, } diff --git a/tests/components/shelly/test_sensor.py b/tests/components/shelly/test_sensor.py index 96c7b0e9cc3..89fa138349f 100644 --- a/tests/components/shelly/test_sensor.py +++ b/tests/components/shelly/test_sensor.py @@ -2,10 +2,13 @@ from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN from homeassistant.core import State +from homeassistant.helpers.entity_registry import async_get from . import ( init_integration, + mock_polling_rpc_update, mock_rest_update, mutate_rpc_device_status, register_device, @@ -16,6 +19,7 @@ from tests.common import mock_restore_cache RELAY_BLOCK_ID = 0 SENSOR_BLOCK_ID = 3 +DEVICE_BLOCK_ID = 4 async def test_block_sensor(hass, mock_block_device, monkeypatch): @@ -88,6 +92,79 @@ async def test_block_restored_sleeping_sensor( assert hass.states.get(entity_id).state == "22.1" +async def test_block_sensor_error(hass, mock_block_device, monkeypatch): + """Test block sensor unavailable on sensor error.""" + entity_id = f"{SENSOR_DOMAIN}.test_name_battery" + await init_integration(hass, 1) + + assert hass.states.get(entity_id).state == "98" + + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "battery", -1) + mock_block_device.mock_update() + + assert hass.states.get(entity_id).state == STATE_UNAVAILABLE + + +async def test_block_sensor_removal(hass, mock_block_device, monkeypatch): + """Test block sensor is removed due to removal_condition.""" + entity_registry = async_get(hass) + entity_id = register_entity( + hass, SENSOR_DOMAIN, "test_name_battery", "device_0-battery" + ) + + assert entity_registry.async_get(entity_id) is not None + + monkeypatch.setitem(mock_block_device.settings, "external_power", 1) + await init_integration(hass, 1) + + assert entity_registry.async_get(entity_id) is None + + +async def test_block_not_matched_restored_sleeping_sensor( + hass, mock_block_device, device_reg, monkeypatch +): + """Test block not matched to restored sleeping sensor.""" + entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True) + register_device(device_reg, entry) + entity_id = register_entity( + hass, SENSOR_DOMAIN, "test_name_temperature", "sensor_0-temp", entry + ) + mock_restore_cache(hass, [State(entity_id, "20.4")]) + monkeypatch.setattr(mock_block_device, "initialized", False) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == "20.4" + + # Make device online + monkeypatch.setattr(mock_block_device.blocks[SENSOR_BLOCK_ID], "type", "other_type") + monkeypatch.setattr(mock_block_device, "initialized", True) + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == "20.4" + + +async def test_block_sensor_without_value(hass, mock_block_device, monkeypatch): + """Test block sensor without value is not created.""" + entity_id = f"{SENSOR_DOMAIN}.test_name_battery" + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "battery", None) + await init_integration(hass, 1) + + assert hass.states.get(entity_id) is None + + +async def test_block_sensor_unknown_value(hass, mock_block_device, monkeypatch): + """Test block sensor unknown value.""" + entity_id = f"{SENSOR_DOMAIN}.test_name_battery" + await init_integration(hass, 1) + + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "battery", None) + mock_block_device.mock_update() + + assert hass.states.get(entity_id).state == STATE_UNKNOWN + + async def test_rpc_sensor(hass, mock_rpc_device, monkeypatch) -> None: """Test RPC sensor.""" entity_id = f"{SENSOR_DOMAIN}.test_cover_0_power" @@ -101,6 +178,32 @@ async def test_rpc_sensor(hass, mock_rpc_device, monkeypatch) -> None: assert hass.states.get(entity_id).state == "88.2" +async def test_rpc_sensor_error(hass, mock_rpc_device, monkeypatch): + """Test RPC sensor unavailable on sensor error.""" + entity_id = f"{SENSOR_DOMAIN}.test_name_voltmeter" + await init_integration(hass, 2) + + assert hass.states.get(entity_id).state == "4.3" + + mutate_rpc_device_status(monkeypatch, mock_rpc_device, "voltmeter", "voltage", None) + mock_rpc_device.mock_update() + + assert hass.states.get(entity_id).state == STATE_UNAVAILABLE + + +async def test_rpc_polling_sensor(hass, mock_rpc_device, monkeypatch) -> None: + """Test RPC polling sensor.""" + entity_id = register_entity(hass, SENSOR_DOMAIN, "test_name_rssi", "wifi-rssi") + await init_integration(hass, 2) + + assert hass.states.get(entity_id).state == "-63" + + mutate_rpc_device_status(monkeypatch, mock_rpc_device, "wifi", "rssi", "-70") + await mock_polling_rpc_update(hass) + + assert hass.states.get(entity_id).state == "-70" + + async def test_rpc_sleeping_sensor( hass, mock_rpc_device, device_reg, monkeypatch ) -> None: diff --git a/tests/components/shelly/test_switch.py b/tests/components/shelly/test_switch.py index 458de9c655b..a5e7a56065d 100644 --- a/tests/components/shelly/test_switch.py +++ b/tests/components/shelly/test_switch.py @@ -1,5 +1,12 @@ """Tests for Shelly switch platform.""" +from unittest.mock import AsyncMock + +from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError +import pytest + +from homeassistant.components.shelly.const import DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN +from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_TURN_OFF, @@ -7,6 +14,7 @@ from homeassistant.const import ( STATE_OFF, STATE_ON, ) +from homeassistant.exceptions import HomeAssistantError from . import init_integration @@ -34,6 +42,56 @@ async def test_block_device_services(hass, mock_block_device): assert hass.states.get("switch.test_name_channel_1").state == STATE_OFF +async def test_block_set_state_connection_error(hass, mock_block_device, monkeypatch): + """Test block device set state connection error.""" + monkeypatch.setattr( + mock_block_device.blocks[RELAY_BLOCK_ID], + "set_state", + AsyncMock(side_effect=DeviceConnectionError), + ) + await init_integration(hass, 1) + + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.test_name_channel_1"}, + blocking=True, + ) + + +async def test_block_set_state_auth_error(hass, mock_block_device, monkeypatch): + """Test block device set state authentication error.""" + monkeypatch.setattr( + mock_block_device.blocks[RELAY_BLOCK_ID], + "set_state", + AsyncMock(side_effect=InvalidAuthError), + ) + entry = await init_integration(hass, 1) + + assert entry.state == ConfigEntryState.LOADED + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.test_name_channel_1"}, + blocking=True, + ) + + assert entry.state == ConfigEntryState.LOADED + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + + flow = flows[0] + assert flow.get("step_id") == "reauth_confirm" + assert flow.get("handler") == DOMAIN + + assert "context" in flow + assert flow["context"].get("source") == SOURCE_REAUTH + assert flow["context"].get("entry_id") == entry.entry_id + + async def test_block_device_update(hass, mock_block_device, monkeypatch): """Test block device update.""" monkeypatch.setattr(mock_block_device.blocks[RELAY_BLOCK_ID], "output", False) @@ -98,3 +156,50 @@ async def test_rpc_device_switch_type_lights_mode(hass, mock_rpc_device, monkeyp ) await init_integration(hass, 2) assert hass.states.get("switch.test_switch_0") is None + + +@pytest.mark.parametrize("exc", [DeviceConnectionError, RpcCallError(-1, "error")]) +async def test_rpc_set_state_errors(hass, exc, mock_rpc_device, monkeypatch): + """Test RPC device set state connection/call errors.""" + monkeypatch.setattr(mock_rpc_device, "call_rpc", AsyncMock(side_effect=exc)) + await init_integration(hass, 2) + + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.test_switch_0"}, + blocking=True, + ) + + +async def test_rpc_auth_error(hass, mock_rpc_device, monkeypatch): + """Test RPC device set state authentication error.""" + monkeypatch.setattr( + mock_rpc_device, + "call_rpc", + AsyncMock(side_effect=InvalidAuthError), + ) + entry = await init_integration(hass, 2) + + assert entry.state == ConfigEntryState.LOADED + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.test_switch_0"}, + blocking=True, + ) + + assert entry.state == ConfigEntryState.LOADED + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + + flow = flows[0] + assert flow.get("step_id") == "reauth_confirm" + assert flow.get("handler") == DOMAIN + + assert "context" in flow + assert flow["context"].get("source") == SOURCE_REAUTH + assert flow["context"].get("entry_id") == entry.entry_id From 3bea04e3872029ebc5a5157771488d76a7c70466 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 21 Nov 2022 00:26:41 +0000 Subject: [PATCH 0584/1033] [ci skip] Translation update --- .../components/adax/translations/sk.json | 12 ++++ .../components/airq/translations/el.json | 6 ++ .../components/airq/translations/sk.json | 7 +++ .../airthings_ble/translations/sk.json | 3 +- .../components/airvisual/translations/sk.json | 3 +- .../components/apple_tv/translations/sk.json | 5 ++ .../components/aranet/translations/el.json | 9 +++ .../aseko_pool_live/translations/sk.json | 3 +- .../components/asuswrt/translations/sk.json | 1 + .../components/august/translations/sk.json | 12 ++++ .../aussie_broadband/translations/sk.json | 10 +++ .../components/awair/translations/sk.json | 1 + .../components/axis/translations/sk.json | 1 + .../components/baf/translations/sk.json | 1 + .../components/blink/translations/sk.json | 7 +++ .../components/bluetooth/translations/sk.json | 5 ++ .../bmw_connected_drive/translations/sk.json | 7 +++ .../components/brother/translations/sk.json | 1 + .../components/brunt/translations/sk.json | 6 ++ .../components/bsblan/translations/sk.json | 2 + .../components/canary/translations/sk.json | 11 ++++ .../components/control4/translations/sk.json | 3 +- .../crownstone/translations/sk.json | 3 +- .../components/daikin/translations/sk.json | 3 +- .../components/deconz/translations/sk.json | 1 + .../components/deluge/translations/sk.json | 1 + .../components/denonavr/translations/sk.json | 1 + .../devolo_home_control/translations/sk.json | 2 + .../devolo_home_network/translations/el.json | 5 ++ .../devolo_home_network/translations/sk.json | 12 ++++ .../components/dexcom/translations/sk.json | 7 +++ .../components/directv/translations/sk.json | 1 + .../components/dlna_dmr/translations/sk.json | 1 + .../components/dlna_dms/translations/sk.json | 1 + .../components/doorbird/translations/sk.json | 3 +- .../components/econet/translations/sk.json | 3 +- .../eight_sleep/translations/sk.json | 11 ++++ .../components/elkm1/translations/sk.json | 14 ++++- .../components/elmax/translations/sk.json | 7 +++ .../components/emonitor/translations/sk.json | 1 + .../enphase_envoy/translations/sk.json | 3 +- .../components/esphome/translations/ca.json | 6 ++ .../components/esphome/translations/it.json | 6 ++ .../components/ezviz/translations/sk.json | 17 +++++ .../components/fibaro/translations/sk.json | 6 ++ .../fireservicerota/translations/el.json | 3 + .../fireservicerota/translations/sk.json | 17 +++++ .../flick_electric/translations/sk.json | 7 +++ .../components/flipr/translations/sk.json | 3 +- .../components/flo/translations/sk.json | 3 +- .../components/flume/translations/sk.json | 12 ++++ .../components/flux_led/translations/sk.json | 1 + .../forked_daapd/translations/sk.json | 3 + .../components/foscam/translations/sk.json | 1 + .../components/fritz/translations/sk.json | 12 ++++ .../components/fritzbox/translations/sk.json | 14 ++++- .../fritzbox_callmonitor/translations/sk.json | 2 + .../fully_kiosk/translations/sk.json | 3 +- .../components/generic/translations/sk.json | 20 ++++++ .../components/glances/translations/sk.json | 1 + .../components/gogogate2/translations/sk.json | 7 +++ .../growatt_server/translations/sk.json | 3 +- .../components/harmony/translations/sk.json | 1 + .../components/hassio/translations/el.json | 1 + .../components/hive/translations/sk.json | 12 ++++ .../components/hlk_sw16/translations/sk.json | 3 +- .../homeassistant/translations/ca.json | 3 + .../homeassistant/translations/it.json | 4 ++ .../homeassistant_yellow/translations/ca.json | 12 ++++ .../homeassistant_yellow/translations/it.json | 40 ++++++++++++ .../homekit_controller/translations/sk.json | 1 + .../huawei_lte/translations/sk.json | 14 +++++ .../huisbaasje/translations/sk.json | 7 +++ .../hvv_departures/translations/sk.json | 3 +- .../components/iaqualink/translations/sk.json | 7 +++ .../components/icloud/translations/sk.json | 11 ++++ .../components/insteon/translations/sk.json | 3 + .../intellifire/translations/sk.json | 5 ++ .../components/iotawatt/translations/sk.json | 5 ++ .../components/ipp/translations/sk.json | 1 + .../components/isy994/translations/sk.json | 8 ++- .../kaleidescape/translations/sk.json | 1 + .../keenetic_ndms2/translations/sk.json | 1 + .../keymitt_ble/translations/sk.json | 5 ++ .../components/kmtronic/translations/sk.json | 3 +- .../components/knx/translations/el.json | 63 ++++++++++++++++--- .../components/knx/translations/sk.json | 13 +++- .../components/kodi/translations/sk.json | 1 + .../components/konnected/translations/sk.json | 4 +- .../kostal_plenticore/translations/sk.json | 3 +- .../lacrosse_view/translations/sk.json | 11 ++++ .../components/led_ble/translations/sk.json | 5 ++ .../components/life360/translations/sk.json | 12 ++++ .../components/lifx/translations/sk.json | 5 ++ .../litterrobot/translations/sk.json | 12 ++++ .../components/livisi/translations/el.json | 3 + .../components/livisi/translations/sk.json | 6 +- .../components/lookin/translations/sk.json | 1 + .../lutron_caseta/translations/sk.json | 1 + .../components/mazda/translations/sk.json | 3 +- .../components/meater/translations/sk.json | 12 ++++ .../components/melcloud/translations/sk.json | 1 + .../components/mikrotik/translations/sk.json | 6 ++ .../components/mill/translations/sk.json | 11 ++++ .../components/mjpeg/translations/sk.json | 6 +- .../modern_forms/translations/sk.json | 1 + .../components/mqtt/translations/sk.json | 1 + .../components/myq/translations/sk.json | 12 ++++ .../components/nam/translations/sk.json | 10 +++ .../components/nanoleaf/translations/sk.json | 1 + .../components/netgear/translations/sk.json | 3 +- .../components/nexia/translations/sk.json | 7 +++ .../components/notion/translations/sk.json | 12 ++++ .../components/nuheat/translations/sk.json | 7 +++ .../components/nzbget/translations/sk.json | 1 + .../components/omnilogic/translations/sk.json | 7 +++ .../components/oncue/translations/sk.json | 7 +++ .../components/onvif/translations/it.json | 3 +- .../components/onvif/translations/sk.json | 1 + .../components/oralb/translations/el.json | 8 +++ .../ovo_energy/translations/sk.json | 12 ++++ .../components/picnic/translations/sk.json | 3 +- .../components/plex/translations/sk.json | 1 + .../components/plugwise/translations/sk.json | 1 + .../plum_lightpad/translations/sk.json | 1 + .../components/poolsense/translations/sk.json | 3 +- .../components/powerwall/translations/sk.json | 13 ++++ .../components/prosegur/translations/sk.json | 12 ++++ .../pure_energie/translations/sk.json | 1 + .../components/qnap_qsw/translations/sk.json | 16 +++++ .../components/renault/translations/sk.json | 6 ++ .../components/ridwell/translations/sk.json | 12 ++++ .../components/ring/translations/sk.json | 7 +++ .../components/risco/translations/sk.json | 10 +++ .../translations/sk.json | 3 +- .../components/roku/translations/sk.json | 1 + .../components/roomba/translations/sk.json | 6 ++ .../ruckus_unleashed/translations/sk.json | 1 + .../components/scrape/translations/sk.json | 20 ++++++ .../screenlogic/translations/sk.json | 1 + .../components/sense/translations/sk.json | 8 ++- .../components/sensor/translations/el.json | 1 + .../components/sharkiq/translations/sk.json | 12 ++++ .../components/shelly/translations/it.json | 13 ++++ .../components/shelly/translations/sk.json | 5 ++ .../simplisafe/translations/sk.json | 5 ++ .../components/skybell/translations/sk.json | 16 +++++ .../components/sleepiq/translations/sk.json | 12 ++++ .../components/sma/translations/sk.json | 3 +- .../smart_meter_texas/translations/sk.json | 7 +++ .../components/smarttub/translations/sk.json | 3 +- .../components/snooz/translations/el.json | 9 +++ .../components/solax/translations/sk.json | 1 + .../components/sonarr/translations/sk.json | 1 + .../components/spider/translations/sk.json | 7 +++ .../squeezebox/translations/sk.json | 1 + .../srp_energy/translations/sk.json | 7 +++ .../components/starline/translations/sk.json | 14 +++++ .../components/subaru/translations/sk.json | 7 +++ .../components/switch/translations/sk.json | 10 +++ .../switch_as_x/translations/bg.json | 3 +- .../components/switchbee/translations/sk.json | 12 +++- .../components/switchbot/translations/sk.json | 13 +++- .../switcher_kis/translations/sk.json | 13 ++++ .../components/syncthru/translations/sk.json | 1 + .../synology_dsm/translations/sk.json | 7 +++ .../components/tado/translations/sk.json | 7 +++ .../tesla_wall_connector/translations/sk.json | 1 + .../components/tile/translations/sk.json | 6 ++ .../components/tolo/translations/sk.json | 1 + .../totalconnect/translations/sk.json | 7 +++ .../components/tplink/translations/sk.json | 1 + .../components/tractive/translations/sk.json | 3 +- .../transmission/translations/sk.json | 6 ++ .../tuya/translations/select.sk.json | 3 + .../components/tuya/translations/sk.json | 3 +- .../unifiprotect/translations/it.json | 17 +++++ .../unifiprotect/translations/sk.json | 7 +++ .../components/upcloud/translations/sk.json | 7 +++ .../components/upnp/translations/sk.json | 3 +- .../components/venstar/translations/sk.json | 3 +- .../components/verisure/translations/sk.json | 6 +- .../components/vesync/translations/sk.json | 4 +- .../components/vicare/translations/sk.json | 1 + .../vlc_telnet/translations/sk.json | 6 ++ .../volvooncall/translations/sk.json | 11 ++++ .../wake_on_lan/translations/it.json | 8 +++ .../components/wallbox/translations/sk.json | 12 ++++ .../components/watttime/translations/sk.json | 10 +++ .../components/whirlpool/translations/sk.json | 7 +++ .../components/wled/translations/sk.json | 1 + .../components/wolflink/translations/sk.json | 7 +++ .../xiaomi_aqara/translations/sk.json | 1 + .../xiaomi_miio/translations/sk.json | 1 + .../yale_smart_alarm/translations/sk.json | 3 +- .../yalexs_ble/translations/sk.json | 5 ++ .../components/zamg/translations/el.json | 3 + .../components/zamg/translations/sk.json | 5 ++ .../components/zha/translations/sk.json | 5 ++ .../zoneminder/translations/sk.json | 7 +++ .../components/zwave_js/translations/sk.json | 3 +- 201 files changed, 1201 insertions(+), 55 deletions(-) create mode 100644 homeassistant/components/bluetooth/translations/sk.json create mode 100644 homeassistant/components/canary/translations/sk.json create mode 100644 homeassistant/components/devolo_home_network/translations/sk.json create mode 100644 homeassistant/components/eight_sleep/translations/sk.json create mode 100644 homeassistant/components/generic/translations/sk.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/it.json create mode 100644 homeassistant/components/keymitt_ble/translations/sk.json create mode 100644 homeassistant/components/lacrosse_view/translations/sk.json create mode 100644 homeassistant/components/led_ble/translations/sk.json create mode 100644 homeassistant/components/lifx/translations/sk.json create mode 100644 homeassistant/components/mill/translations/sk.json create mode 100644 homeassistant/components/qnap_qsw/translations/sk.json create mode 100644 homeassistant/components/scrape/translations/sk.json create mode 100644 homeassistant/components/skybell/translations/sk.json create mode 100644 homeassistant/components/starline/translations/sk.json create mode 100644 homeassistant/components/switcher_kis/translations/sk.json create mode 100644 homeassistant/components/volvooncall/translations/sk.json create mode 100644 homeassistant/components/wake_on_lan/translations/it.json create mode 100644 homeassistant/components/yalexs_ble/translations/sk.json create mode 100644 homeassistant/components/zamg/translations/sk.json create mode 100644 homeassistant/components/zha/translations/sk.json diff --git a/homeassistant/components/adax/translations/sk.json b/homeassistant/components/adax/translations/sk.json index af9d83e3703..a1c6f397beb 100644 --- a/homeassistant/components/adax/translations/sk.json +++ b/homeassistant/components/adax/translations/sk.json @@ -2,6 +2,18 @@ "config": { "abort": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "cloud": { + "data": { + "password": "Heslo" + } + }, + "local": { + "data": { + "wifi_pswd": "Heslo Wi-Fi" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/airq/translations/el.json b/homeassistant/components/airq/translations/el.json index 3bfc85e23b9..79004a86cb9 100644 --- a/homeassistant/components/airq/translations/el.json +++ b/homeassistant/components/airq/translations/el.json @@ -1,7 +1,13 @@ { "config": { + "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + }, "step": { "user": { + "data": { + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" + }, "description": "\u0394\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03ae \u03c4\u03bf mDNS \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03ba\u03b1\u03b9 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03ae\u03c2 \u03c4\u03b7\u03c2", "title": "\u03a0\u03c1\u03bf\u03c3\u03b4\u03b9\u03bf\u03c1\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" } diff --git a/homeassistant/components/airq/translations/sk.json b/homeassistant/components/airq/translations/sk.json index 3d0490e59bd..7c93ea593b6 100644 --- a/homeassistant/components/airq/translations/sk.json +++ b/homeassistant/components/airq/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_input": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/airthings_ble/translations/sk.json b/homeassistant/components/airthings_ble/translations/sk.json index 5f8e521919f..5667845902b 100644 --- a/homeassistant/components/airthings_ble/translations/sk.json +++ b/homeassistant/components/airthings_ble/translations/sk.json @@ -6,6 +6,7 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" - } + }, + "flow_title": "{name}" } } \ No newline at end of file diff --git a/homeassistant/components/airvisual/translations/sk.json b/homeassistant/components/airvisual/translations/sk.json index 156f165a5ab..f54803951c5 100644 --- a/homeassistant/components/airvisual/translations/sk.json +++ b/homeassistant/components/airvisual/translations/sk.json @@ -27,7 +27,8 @@ }, "node_pro": { "data": { - "ip_address": "Hostite\u013e" + "ip_address": "Hostite\u013e", + "password": "Heslo" } }, "reauth_confirm": { diff --git a/homeassistant/components/apple_tv/translations/sk.json b/homeassistant/components/apple_tv/translations/sk.json index e0e6b1c5bda..1dab0308776 100644 --- a/homeassistant/components/apple_tv/translations/sk.json +++ b/homeassistant/components/apple_tv/translations/sk.json @@ -6,6 +6,11 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "password": { + "title": "Vy\u017eaduje sa heslo" + } } } } \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/el.json b/homeassistant/components/aranet/translations/el.json index a22b2c2813e..1f5383f1c07 100644 --- a/homeassistant/components/aranet/translations/el.json +++ b/homeassistant/components/aranet/translations/el.json @@ -1,9 +1,18 @@ { "config": { "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "integrations_diabled": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b5\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03b1\u03c4\u03ce\u03c3\u03b5\u03b9\u03c2. \u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03b1\u03c4\u03ce\u03c3\u03b5\u03b9\u03c2 \u03ad\u03be\u03c5\u03c0\u03bd\u03bf\u03c5 \u03c3\u03c0\u03b9\u03c4\u03b9\u03bf\u03cd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae \u03ba\u03b1\u03b9 \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac.", "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03bc\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03bc\u03ad\u03bd\u03b5\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 Aranet.", "outdated_version": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c0\u03b1\u03bb\u03b9\u03cc \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc. \u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c4\u03bf\u03c5\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf\u03bd \u03c3\u03b5 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 1.2.0 \u03ba\u03b1\u03b9 \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac." + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "address": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/aseko_pool_live/translations/sk.json b/homeassistant/components/aseko_pool_live/translations/sk.json index 72b0304f1c3..87352b48fad 100644 --- a/homeassistant/components/aseko_pool_live/translations/sk.json +++ b/homeassistant/components/aseko_pool_live/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "email": "Email" + "email": "Email", + "password": "Heslo" } } } diff --git a/homeassistant/components/asuswrt/translations/sk.json b/homeassistant/components/asuswrt/translations/sk.json index 0cdd25de0a5..b808989525e 100644 --- a/homeassistant/components/asuswrt/translations/sk.json +++ b/homeassistant/components/asuswrt/translations/sk.json @@ -8,6 +8,7 @@ "data": { "host": "Hostite\u013e", "name": "N\u00e1zov", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/august/translations/sk.json b/homeassistant/components/august/translations/sk.json index 71a7aea5018..bbdf6a7d2e6 100644 --- a/homeassistant/components/august/translations/sk.json +++ b/homeassistant/components/august/translations/sk.json @@ -5,6 +5,18 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth_validate": { + "data": { + "password": "Heslo" + } + }, + "user_validate": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/aussie_broadband/translations/sk.json b/homeassistant/components/aussie_broadband/translations/sk.json index 2d028b5f36c..ee107e64a8f 100644 --- a/homeassistant/components/aussie_broadband/translations/sk.json +++ b/homeassistant/components/aussie_broadband/translations/sk.json @@ -7,8 +7,18 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "service": { "title": "Vyberte slu\u017eby" + }, + "user": { + "data": { + "password": "Heslo" + } } } }, diff --git a/homeassistant/components/awair/translations/sk.json b/homeassistant/components/awair/translations/sk.json index 79d8b98712e..50f9d9020f7 100644 --- a/homeassistant/components/awair/translations/sk.json +++ b/homeassistant/components/awair/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, + "flow_title": "{model} ({device_id})", "step": { "local": { "data": { diff --git a/homeassistant/components/axis/translations/sk.json b/homeassistant/components/axis/translations/sk.json index add545fae6e..5aa81a95c18 100644 --- a/homeassistant/components/axis/translations/sk.json +++ b/homeassistant/components/axis/translations/sk.json @@ -9,6 +9,7 @@ "user": { "data": { "host": "Hostite\u013e", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/baf/translations/sk.json b/homeassistant/components/baf/translations/sk.json index 5ca1b9820a9..a0c6a2aa853 100644 --- a/homeassistant/components/baf/translations/sk.json +++ b/homeassistant/components/baf/translations/sk.json @@ -8,6 +8,7 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, + "flow_title": "{name} - {model} ({ip_address})", "step": { "user": { "data": { diff --git a/homeassistant/components/blink/translations/sk.json b/homeassistant/components/blink/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/blink/translations/sk.json +++ b/homeassistant/components/blink/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/bluetooth/translations/sk.json b/homeassistant/components/bluetooth/translations/sk.json new file mode 100644 index 00000000000..e8940bef26a --- /dev/null +++ b/homeassistant/components/bluetooth/translations/sk.json @@ -0,0 +1,5 @@ +{ + "config": { + "flow_title": "{name}" + } +} \ No newline at end of file diff --git a/homeassistant/components/bmw_connected_drive/translations/sk.json b/homeassistant/components/bmw_connected_drive/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/bmw_connected_drive/translations/sk.json +++ b/homeassistant/components/bmw_connected_drive/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/brother/translations/sk.json b/homeassistant/components/brother/translations/sk.json index 39a130777c5..2c530043b17 100644 --- a/homeassistant/components/brother/translations/sk.json +++ b/homeassistant/components/brother/translations/sk.json @@ -3,6 +3,7 @@ "error": { "wrong_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa." }, + "flow_title": "{model} {serial_number}", "step": { "user": { "data": { diff --git a/homeassistant/components/brunt/translations/sk.json b/homeassistant/components/brunt/translations/sk.json index 3f00701d0ba..b86eef0e0df 100644 --- a/homeassistant/components/brunt/translations/sk.json +++ b/homeassistant/components/brunt/translations/sk.json @@ -7,8 +7,14 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { + "password": "Heslo", "username": "U\u017e\u00edvate\u013esk\u00e9 meno" } } diff --git a/homeassistant/components/bsblan/translations/sk.json b/homeassistant/components/bsblan/translations/sk.json index 33d93b62475..dae42543c95 100644 --- a/homeassistant/components/bsblan/translations/sk.json +++ b/homeassistant/components/bsblan/translations/sk.json @@ -1,9 +1,11 @@ { "config": { + "flow_title": "{name}", "step": { "user": { "data": { "host": "Hostite\u013e", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/canary/translations/sk.json b/homeassistant/components/canary/translations/sk.json new file mode 100644 index 00000000000..66e8cd431a4 --- /dev/null +++ b/homeassistant/components/canary/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/control4/translations/sk.json b/homeassistant/components/control4/translations/sk.json index b8437f98a1e..788f778e13a 100644 --- a/homeassistant/components/control4/translations/sk.json +++ b/homeassistant/components/control4/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "host": "IP adresa" + "host": "IP adresa", + "password": "Heslo" } } } diff --git a/homeassistant/components/crownstone/translations/sk.json b/homeassistant/components/crownstone/translations/sk.json index 72b0304f1c3..87352b48fad 100644 --- a/homeassistant/components/crownstone/translations/sk.json +++ b/homeassistant/components/crownstone/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "email": "Email" + "email": "Email", + "password": "Heslo" } } } diff --git a/homeassistant/components/daikin/translations/sk.json b/homeassistant/components/daikin/translations/sk.json index 53957647798..17cde55b9f2 100644 --- a/homeassistant/components/daikin/translations/sk.json +++ b/homeassistant/components/daikin/translations/sk.json @@ -8,7 +8,8 @@ "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo" } } } diff --git a/homeassistant/components/deconz/translations/sk.json b/homeassistant/components/deconz/translations/sk.json index 8031205063b..6cbd7bd3752 100644 --- a/homeassistant/components/deconz/translations/sk.json +++ b/homeassistant/components/deconz/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "flow_title": "{host}", "step": { "manual_input": { "data": { diff --git a/homeassistant/components/deluge/translations/sk.json b/homeassistant/components/deluge/translations/sk.json index bf47f225eb7..bc2d39d1a3c 100644 --- a/homeassistant/components/deluge/translations/sk.json +++ b/homeassistant/components/deluge/translations/sk.json @@ -4,6 +4,7 @@ "user": { "data": { "host": "Hostite\u013e", + "password": "Heslo", "port": "Port", "username": "U\u017e\u00edvate\u013esk\u00e9 meno" } diff --git a/homeassistant/components/denonavr/translations/sk.json b/homeassistant/components/denonavr/translations/sk.json index 426e2fb9f72..0136b344d18 100644 --- a/homeassistant/components/denonavr/translations/sk.json +++ b/homeassistant/components/denonavr/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "flow_title": "{name}", "step": { "select": { "data": { diff --git a/homeassistant/components/devolo_home_control/translations/sk.json b/homeassistant/components/devolo_home_control/translations/sk.json index 9273954369f..001d94eb0a8 100644 --- a/homeassistant/components/devolo_home_control/translations/sk.json +++ b/homeassistant/components/devolo_home_control/translations/sk.json @@ -9,11 +9,13 @@ "step": { "user": { "data": { + "password": "Heslo", "username": "Email / devolo ID" } }, "zeroconf_confirm": { "data": { + "password": "Heslo", "username": "Email / devolo ID" } } diff --git a/homeassistant/components/devolo_home_network/translations/el.json b/homeassistant/components/devolo_home_network/translations/el.json index 45a54b59b00..9fee61cd430 100644 --- a/homeassistant/components/devolo_home_network/translations/el.json +++ b/homeassistant/components/devolo_home_network/translations/el.json @@ -10,6 +10,11 @@ }, "flow_title": "{product} ({name})", "step": { + "reauth_confirm": { + "data": { + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" + } + }, "user": { "data": { "ip_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP" diff --git a/homeassistant/components/devolo_home_network/translations/sk.json b/homeassistant/components/devolo_home_network/translations/sk.json new file mode 100644 index 00000000000..ffc0103d598 --- /dev/null +++ b/homeassistant/components/devolo_home_network/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "flow_title": "{product} ({name})", + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dexcom/translations/sk.json b/homeassistant/components/dexcom/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/dexcom/translations/sk.json +++ b/homeassistant/components/dexcom/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/directv/translations/sk.json b/homeassistant/components/directv/translations/sk.json index 842ff61bd79..aaad66e6729 100644 --- a/homeassistant/components/directv/translations/sk.json +++ b/homeassistant/components/directv/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/dlna_dmr/translations/sk.json b/homeassistant/components/dlna_dmr/translations/sk.json index 842ff61bd79..aaad66e6729 100644 --- a/homeassistant/components/dlna_dmr/translations/sk.json +++ b/homeassistant/components/dlna_dmr/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/dlna_dms/translations/sk.json b/homeassistant/components/dlna_dms/translations/sk.json index 842ff61bd79..aaad66e6729 100644 --- a/homeassistant/components/dlna_dms/translations/sk.json +++ b/homeassistant/components/dlna_dms/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/doorbird/translations/sk.json b/homeassistant/components/doorbird/translations/sk.json index 429cf4e7a80..0390bd497a9 100644 --- a/homeassistant/components/doorbird/translations/sk.json +++ b/homeassistant/components/doorbird/translations/sk.json @@ -7,7 +7,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo" } } } diff --git a/homeassistant/components/econet/translations/sk.json b/homeassistant/components/econet/translations/sk.json index 1a3e5d67caa..ccc424ecb47 100644 --- a/homeassistant/components/econet/translations/sk.json +++ b/homeassistant/components/econet/translations/sk.json @@ -9,7 +9,8 @@ "step": { "user": { "data": { - "email": "Email" + "email": "Email", + "password": "Heslo" } } } diff --git a/homeassistant/components/eight_sleep/translations/sk.json b/homeassistant/components/eight_sleep/translations/sk.json new file mode 100644 index 00000000000..66e8cd431a4 --- /dev/null +++ b/homeassistant/components/eight_sleep/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/elkm1/translations/sk.json b/homeassistant/components/elkm1/translations/sk.json index 96183ba3069..76d5e5fdfea 100644 --- a/homeassistant/components/elkm1/translations/sk.json +++ b/homeassistant/components/elkm1/translations/sk.json @@ -6,6 +6,18 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, - "flow_title": "{mac_address} ({host})" + "flow_title": "{mac_address} ({host})", + "step": { + "discovered_connection": { + "data": { + "password": "Heslo" + } + }, + "manual_connection": { + "data": { + "password": "Heslo" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/sk.json b/homeassistant/components/elmax/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/elmax/translations/sk.json +++ b/homeassistant/components/elmax/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/emonitor/translations/sk.json b/homeassistant/components/emonitor/translations/sk.json index 842ff61bd79..aaad66e6729 100644 --- a/homeassistant/components/emonitor/translations/sk.json +++ b/homeassistant/components/emonitor/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/enphase_envoy/translations/sk.json b/homeassistant/components/enphase_envoy/translations/sk.json index 1072f39428f..2c5e58fb5ea 100644 --- a/homeassistant/components/enphase_envoy/translations/sk.json +++ b/homeassistant/components/enphase_envoy/translations/sk.json @@ -10,7 +10,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo" } } } diff --git a/homeassistant/components/esphome/translations/ca.json b/homeassistant/components/esphome/translations/ca.json index ef9814d3ac8..ce4b9ce5da2 100644 --- a/homeassistant/components/esphome/translations/ca.json +++ b/homeassistant/components/esphome/translations/ca.json @@ -43,5 +43,11 @@ "description": "Introdueix la configuraci\u00f3 de connexi\u00f3 del node [ESPHome]({esphome_url})." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "Per millorar la fiabilitat i el rendiment de Bluetooth, et recomanem que actualitzis {name} a ESPHome 2022.11.0 o posterior.", + "title": "Actualitza {name} amb ESPHome 2022.11.0 o posterior" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/it.json b/homeassistant/components/esphome/translations/it.json index 23727d1510f..0f0b31f7094 100644 --- a/homeassistant/components/esphome/translations/it.json +++ b/homeassistant/components/esphome/translations/it.json @@ -43,5 +43,11 @@ "description": "Inserisci le impostazioni di connessione del tuo nodo [ESPHome]({esphome_url})." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "Per migliorare l'affidabilit\u00e0 e le prestazioni Bluetooth, si consiglia vivamente di aggiornare {name} con ESPHome 2022.11.0 o versioni successive.", + "title": "Aggiorna {name} con ESPHome 2022.11.0 o versioni successive" + } } } \ No newline at end of file diff --git a/homeassistant/components/ezviz/translations/sk.json b/homeassistant/components/ezviz/translations/sk.json index c6b3dbdc5e0..d10b6dbf6be 100644 --- a/homeassistant/components/ezviz/translations/sk.json +++ b/homeassistant/components/ezviz/translations/sk.json @@ -3,6 +3,23 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + }, + "step": { + "confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + }, + "user_custom_url": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/sk.json b/homeassistant/components/fibaro/translations/sk.json index 649a1e186ee..b374deb03c9 100644 --- a/homeassistant/components/fibaro/translations/sk.json +++ b/homeassistant/components/fibaro/translations/sk.json @@ -1,8 +1,14 @@ { "config": { "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { + "password": "Heslo", "username": "U\u017e\u00edvate\u013esk\u00e9 meno" } } diff --git a/homeassistant/components/fireservicerota/translations/el.json b/homeassistant/components/fireservicerota/translations/el.json index 68cc2556581..bb6b01c97c8 100644 --- a/homeassistant/components/fireservicerota/translations/el.json +++ b/homeassistant/components/fireservicerota/translations/el.json @@ -18,6 +18,9 @@ "description": "\u03a4\u03b1 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03ac \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ad\u03b3\u03b9\u03bd\u03b1\u03bd \u03ac\u03ba\u03c5\u03c1\u03b1, \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c4\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac." }, "reauth_confirm": { + "data": { + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" + }, "description": "\u03a4\u03b1 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03ac \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ad\u03b3\u03b9\u03bd\u03b1\u03bd \u03ac\u03ba\u03c5\u03c1\u03b1, \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c4\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac." }, "user": { diff --git a/homeassistant/components/fireservicerota/translations/sk.json b/homeassistant/components/fireservicerota/translations/sk.json index 879d148fd13..9b831d2d901 100644 --- a/homeassistant/components/fireservicerota/translations/sk.json +++ b/homeassistant/components/fireservicerota/translations/sk.json @@ -8,6 +8,23 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth": { + "data": { + "password": "Heslo" + } + }, + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/flick_electric/translations/sk.json b/homeassistant/components/flick_electric/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/flick_electric/translations/sk.json +++ b/homeassistant/components/flick_electric/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/flipr/translations/sk.json b/homeassistant/components/flipr/translations/sk.json index 72b0304f1c3..87352b48fad 100644 --- a/homeassistant/components/flipr/translations/sk.json +++ b/homeassistant/components/flipr/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "email": "Email" + "email": "Email", + "password": "Heslo" } } } diff --git a/homeassistant/components/flo/translations/sk.json b/homeassistant/components/flo/translations/sk.json index 368553a4887..08d8e8f6516 100644 --- a/homeassistant/components/flo/translations/sk.json +++ b/homeassistant/components/flo/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo" } } } diff --git a/homeassistant/components/flume/translations/sk.json b/homeassistant/components/flume/translations/sk.json index 71a7aea5018..bd3a9e24cf1 100644 --- a/homeassistant/components/flume/translations/sk.json +++ b/homeassistant/components/flume/translations/sk.json @@ -5,6 +5,18 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/flux_led/translations/sk.json b/homeassistant/components/flux_led/translations/sk.json index cefae299404..9b4cff3cb69 100644 --- a/homeassistant/components/flux_led/translations/sk.json +++ b/homeassistant/components/flux_led/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "flow_title": "{model} {id} ({ipaddr})", "step": { "user": { "data": { diff --git a/homeassistant/components/forked_daapd/translations/sk.json b/homeassistant/components/forked_daapd/translations/sk.json index d7dee4be88b..3ac40922981 100644 --- a/homeassistant/components/forked_daapd/translations/sk.json +++ b/homeassistant/components/forked_daapd/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "wrong_password": "Nespr\u00e1vne heslo." + }, "flow_title": "{name} ({host})", "step": { "user": { diff --git a/homeassistant/components/foscam/translations/sk.json b/homeassistant/components/foscam/translations/sk.json index cdd79c25392..db9ec47e7c7 100644 --- a/homeassistant/components/foscam/translations/sk.json +++ b/homeassistant/components/foscam/translations/sk.json @@ -10,6 +10,7 @@ "user": { "data": { "host": "Hostite\u013e", + "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } diff --git a/homeassistant/components/fritz/translations/sk.json b/homeassistant/components/fritz/translations/sk.json index 0ee0cae7a3d..a379d26084b 100644 --- a/homeassistant/components/fritz/translations/sk.json +++ b/homeassistant/components/fritz/translations/sk.json @@ -8,10 +8,22 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{name}", "step": { + "confirm": { + "data": { + "password": "Heslo" + } + }, + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { "host": "Hostite\u013e", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/fritzbox/translations/sk.json b/homeassistant/components/fritzbox/translations/sk.json index 3c7866e85a8..926cf579a43 100644 --- a/homeassistant/components/fritzbox/translations/sk.json +++ b/homeassistant/components/fritzbox/translations/sk.json @@ -7,10 +7,22 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{name}", "step": { + "confirm": { + "data": { + "password": "Heslo" + } + }, + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo" } } } diff --git a/homeassistant/components/fritzbox_callmonitor/translations/sk.json b/homeassistant/components/fritzbox_callmonitor/translations/sk.json index 6f2d6c9c475..3ae2562c19e 100644 --- a/homeassistant/components/fritzbox_callmonitor/translations/sk.json +++ b/homeassistant/components/fritzbox_callmonitor/translations/sk.json @@ -3,10 +3,12 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{name}", "step": { "user": { "data": { "host": "Hostite\u013e", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/fully_kiosk/translations/sk.json b/homeassistant/components/fully_kiosk/translations/sk.json index 842ff61bd79..9efe27e4e38 100644 --- a/homeassistant/components/fully_kiosk/translations/sk.json +++ b/homeassistant/components/fully_kiosk/translations/sk.json @@ -3,7 +3,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo" } } } diff --git a/homeassistant/components/generic/translations/sk.json b/homeassistant/components/generic/translations/sk.json new file mode 100644 index 00000000000..9ff8487afaa --- /dev/null +++ b/homeassistant/components/generic/translations/sk.json @@ -0,0 +1,20 @@ +{ + "config": { + "step": { + "user": { + "data": { + "password": "Heslo" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/glances/translations/sk.json b/homeassistant/components/glances/translations/sk.json index 1c96c63ac91..e6a451b5ba5 100644 --- a/homeassistant/components/glances/translations/sk.json +++ b/homeassistant/components/glances/translations/sk.json @@ -5,6 +5,7 @@ "data": { "host": "Hostite\u013e", "name": "N\u00e1zov", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/gogogate2/translations/sk.json b/homeassistant/components/gogogate2/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/gogogate2/translations/sk.json +++ b/homeassistant/components/gogogate2/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/growatt_server/translations/sk.json b/homeassistant/components/growatt_server/translations/sk.json index d2e4793a68b..fc428c8f4b9 100644 --- a/homeassistant/components/growatt_server/translations/sk.json +++ b/homeassistant/components/growatt_server/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "name": "N\u00e1zov" + "name": "N\u00e1zov", + "password": "Heslo" } } } diff --git a/homeassistant/components/harmony/translations/sk.json b/homeassistant/components/harmony/translations/sk.json index 842ff61bd79..aaad66e6729 100644 --- a/homeassistant/components/harmony/translations/sk.json +++ b/homeassistant/components/harmony/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/hassio/translations/el.json b/homeassistant/components/hassio/translations/el.json index 91448c065c8..a5d6ef68293 100644 --- a/homeassistant/components/hassio/translations/el.json +++ b/homeassistant/components/hassio/translations/el.json @@ -5,6 +5,7 @@ "title": "\u039c\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - {reason}" }, "unhealthy_docker": { + "description": "\u03a4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03c1\u03bf\u03c2 \u03c4\u03bf \u03c0\u03b1\u03c1\u03cc\u03bd \u03bc\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03c4\u03bf Docker \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af \u03bb\u03b1\u03bd\u03b8\u03b1\u03c3\u03bc\u03ad\u03bd\u03b1. \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03c4\u03bf \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5.", "title": "\u039c\u03b7 \u03c5\u03b3\u03b9\u03ad\u03c2 \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 - \u039a\u03b1\u03ba\u03ae \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03bf\u03c5 Docker" }, "unhealthy_privileged": { diff --git a/homeassistant/components/hive/translations/sk.json b/homeassistant/components/hive/translations/sk.json index c2f015fe339..5e1d3b6f394 100644 --- a/homeassistant/components/hive/translations/sk.json +++ b/homeassistant/components/hive/translations/sk.json @@ -2,6 +2,18 @@ "config": { "abort": { "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + }, + "step": { + "reauth": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/hlk_sw16/translations/sk.json b/homeassistant/components/hlk_sw16/translations/sk.json index 368553a4887..08d8e8f6516 100644 --- a/homeassistant/components/hlk_sw16/translations/sk.json +++ b/homeassistant/components/hlk_sw16/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo" } } } diff --git a/homeassistant/components/homeassistant/translations/ca.json b/homeassistant/components/homeassistant/translations/ca.json index e850412488a..01261ca3ea5 100644 --- a/homeassistant/components/homeassistant/translations/ca.json +++ b/homeassistant/components/homeassistant/translations/ca.json @@ -3,6 +3,9 @@ "historic_currency": { "description": "El valor de moneda {currency} ja no s'utilitza, modifica la configuraci\u00f3 del valor de moneda.", "title": "El valor de moneda configurat ja no s'utilitza" + }, + "python_version": { + "title": "S'est\u00e0 eliminant la compatibilitat amb Python {current_python_version}" } }, "system_health": { diff --git a/homeassistant/components/homeassistant/translations/it.json b/homeassistant/components/homeassistant/translations/it.json index abc7c056692..164f7e90c95 100644 --- a/homeassistant/components/homeassistant/translations/it.json +++ b/homeassistant/components/homeassistant/translations/it.json @@ -3,6 +3,10 @@ "historic_currency": { "description": "La valuta {currency} non \u00e8 pi\u00f9 in uso, riconfigura la configurazione della valuta.", "title": "La valuta configurata non \u00e8 pi\u00f9 in uso" + }, + "python_version": { + "description": "Il supporto per l'esecuzione di Home Assistant nell'attuale versione di Python utilizzata {current_python_version} \u00e8 deprecato e verr\u00e0 rimosso in Home Assistant {breaks_in_ha_version}. Aggiorna Python a {required_python_version} per evitare che la tua istanza di Home Assistant non funzioni pi\u00f9.", + "title": "Il supporto per Python {current_python_version} \u00e8 stato rimosso" } }, "system_health": { diff --git a/homeassistant/components/homeassistant_yellow/translations/ca.json b/homeassistant/components/homeassistant_yellow/translations/ca.json index ebff40e34e8..e3f318dca31 100644 --- a/homeassistant/components/homeassistant_yellow/translations/ca.json +++ b/homeassistant/components/homeassistant_yellow/translations/ca.json @@ -1,7 +1,19 @@ { "options": { + "abort": { + "addon_info_failed": "No s'ha pogut obtenir la informaci\u00f3 del complement Silicon Labs Multiprotocol.", + "addon_install_failed": "No s'ha pogut instal\u00b7lar el complement Silicon Labs Multiprotocol-", + "addon_set_config_failed": "No s'ha pogut establir la configuraci\u00f3 de Silicon Labs Multiprotocol.", + "addon_start_failed": "No s'ha pogut iniciar el complement Silicon Labs Multiprotocol.", + "not_hassio": "Les opcions de maquinari nom\u00e9s es poden configurar a les instal\u00b7lacions HassOS." + }, "error": { "unknown": "Error inesperat" + }, + "step": { + "start_addon": { + "title": "El complement Silicon Labs Multiprotocol s'est\u00e0 iniciant." + } } } } \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/it.json b/homeassistant/components/homeassistant_yellow/translations/it.json new file mode 100644 index 00000000000..353d65ea7a5 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/it.json @@ -0,0 +1,40 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Impossibile ottenere informazioni sul componente aggiuntivo Silicon Labs Multiprotocol.", + "addon_install_failed": "Impossibile installare il componente aggiuntivo Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Impossibile impostare la configurazione di Silicon Labs Multiprotocol.", + "addon_start_failed": "Impossibile avviare il componente aggiuntivo Silicon Labs Multiprotocol.", + "not_hassio": "Le opzioni hardware possono essere configurate solo su installazioni HassOS." + }, + "error": { + "unknown": "Errore imprevisto" + }, + "progress": { + "install_addon": "Attendere il completamento dell'installazione del componente aggiuntivo Silicon Labs Multiprotocol. Questo pu\u00f2 richiedere diversi minuti.", + "start_addon": "Attendi il completamento dell'avvio del componente aggiuntivo Silicon Labs Multiprotocol. Questo potrebbe richiedere alcuni secondi." + }, + "step": { + "addon_installed_other_device": { + "title": "Il supporto multiprotocollo \u00e8 gi\u00e0 abilitato per un altro dispositivo" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Abilita il supporto multiprotocollo" + }, + "description": "Quando il supporto multiprotocollo \u00e8 abilitato, la radio IEEE 802.15.4 di Home Assistant Yellow pu\u00f2 essere utilizzata contemporaneamente sia per Zigbee che per Thread (utilizzato da Matter). Nota: questa \u00e8 una funzione sperimentale.", + "title": "Abilita il supporto multiprotocollo sulla radio IEEE 802.15.4" + }, + "install_addon": { + "title": "L'installazione del componente aggiuntivo Silicon Labs Multiprotocol \u00e8 iniziata" + }, + "show_revert_guide": { + "description": "Se desideri passare al solo firmware Zigbee, completa i seguenti passaggi manuali: \n\n * Rimuovi il componente aggiuntivo Silicon Labs Multiprotocol \n\n * Caricare solo il firmware Zigbee, segui la guida su https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Riconfigurare ZHA per migrare le impostazioni alla radio ricaricata", + "title": "Il supporto multiprotocollo \u00e8 abilitato per questo dispositivo" + }, + "start_addon": { + "title": "Il componente aggiuntivo Silicon Labs Multiprotocol sta per essere avviato." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/sk.json b/homeassistant/components/homekit_controller/translations/sk.json index 8ebd0e1e08e..9afe02731ca 100644 --- a/homeassistant/components/homekit_controller/translations/sk.json +++ b/homeassistant/components/homekit_controller/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "flow_title": "{name} ({category})", "step": { "user": { "title": "V\u00fdber zariadenia" diff --git a/homeassistant/components/huawei_lte/translations/sk.json b/homeassistant/components/huawei_lte/translations/sk.json index 5ada995aa6e..959a2866eba 100644 --- a/homeassistant/components/huawei_lte/translations/sk.json +++ b/homeassistant/components/huawei_lte/translations/sk.json @@ -1,7 +1,21 @@ { "config": { "error": { + "incorrect_password": "Nespr\u00e1vne heslo", "invalid_auth": "Neplatn\u00e9 overenie" + }, + "flow_title": "{name}", + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/huisbaasje/translations/sk.json b/homeassistant/components/huisbaasje/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/huisbaasje/translations/sk.json +++ b/homeassistant/components/huisbaasje/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/hvv_departures/translations/sk.json b/homeassistant/components/hvv_departures/translations/sk.json index 368553a4887..08d8e8f6516 100644 --- a/homeassistant/components/hvv_departures/translations/sk.json +++ b/homeassistant/components/hvv_departures/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo" } } } diff --git a/homeassistant/components/iaqualink/translations/sk.json b/homeassistant/components/iaqualink/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/iaqualink/translations/sk.json +++ b/homeassistant/components/iaqualink/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/icloud/translations/sk.json b/homeassistant/components/icloud/translations/sk.json index d30ed436a4f..9c21cacd361 100644 --- a/homeassistant/components/icloud/translations/sk.json +++ b/homeassistant/components/icloud/translations/sk.json @@ -7,8 +7,19 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "reauth": { + "data": { + "password": "Heslo" + } + }, + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { + "password": "Heslo", "username": "Email" } } diff --git a/homeassistant/components/insteon/translations/sk.json b/homeassistant/components/insteon/translations/sk.json index 704c6b2d86c..bad6f4f3cd3 100644 --- a/homeassistant/components/insteon/translations/sk.json +++ b/homeassistant/components/insteon/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "cannot_connect": "Nepodarilo sa pripoji\u0165" }, + "flow_title": "{name}", "step": { "hubv1": { "data": { @@ -13,6 +14,7 @@ "hubv2": { "data": { "host": "IP adresa", + "password": "Heslo", "port": "Port" } } @@ -23,6 +25,7 @@ "change_hub_config": { "data": { "host": "IP adresa", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/intellifire/translations/sk.json b/homeassistant/components/intellifire/translations/sk.json index f70e03e3c83..c049c1433b3 100644 --- a/homeassistant/components/intellifire/translations/sk.json +++ b/homeassistant/components/intellifire/translations/sk.json @@ -2,6 +2,11 @@ "config": { "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "Heslo" + } + }, "pick_device": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/iotawatt/translations/sk.json b/homeassistant/components/iotawatt/translations/sk.json index 368553a4887..0d9f93851ce 100644 --- a/homeassistant/components/iotawatt/translations/sk.json +++ b/homeassistant/components/iotawatt/translations/sk.json @@ -4,6 +4,11 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "auth": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/ipp/translations/sk.json b/homeassistant/components/ipp/translations/sk.json index 33d93b62475..a28e43a198f 100644 --- a/homeassistant/components/ipp/translations/sk.json +++ b/homeassistant/components/ipp/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/isy994/translations/sk.json b/homeassistant/components/isy994/translations/sk.json index 1e1ce1bb39f..80987bdbc31 100644 --- a/homeassistant/components/isy994/translations/sk.json +++ b/homeassistant/components/isy994/translations/sk.json @@ -5,9 +5,15 @@ }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { - "host": "URL" + "host": "URL", + "password": "Heslo" } } } diff --git a/homeassistant/components/kaleidescape/translations/sk.json b/homeassistant/components/kaleidescape/translations/sk.json index 842ff61bd79..f9049e5041a 100644 --- a/homeassistant/components/kaleidescape/translations/sk.json +++ b/homeassistant/components/kaleidescape/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{model} ({name})", "step": { "user": { "data": { diff --git a/homeassistant/components/keenetic_ndms2/translations/sk.json b/homeassistant/components/keenetic_ndms2/translations/sk.json index 430bfe3ea0d..2f376202caa 100644 --- a/homeassistant/components/keenetic_ndms2/translations/sk.json +++ b/homeassistant/components/keenetic_ndms2/translations/sk.json @@ -5,6 +5,7 @@ "user": { "data": { "host": "Hostite\u013e", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/keymitt_ble/translations/sk.json b/homeassistant/components/keymitt_ble/translations/sk.json new file mode 100644 index 00000000000..e8940bef26a --- /dev/null +++ b/homeassistant/components/keymitt_ble/translations/sk.json @@ -0,0 +1,5 @@ +{ + "config": { + "flow_title": "{name}" + } +} \ No newline at end of file diff --git a/homeassistant/components/kmtronic/translations/sk.json b/homeassistant/components/kmtronic/translations/sk.json index 368553a4887..08d8e8f6516 100644 --- a/homeassistant/components/kmtronic/translations/sk.json +++ b/homeassistant/components/kmtronic/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo" } } } diff --git a/homeassistant/components/knx/translations/el.json b/homeassistant/components/knx/translations/el.json index fc3af662be2..2e27c415cd9 100644 --- a/homeassistant/components/knx/translations/el.json +++ b/homeassistant/components/knx/translations/el.json @@ -31,17 +31,17 @@ "data_description": { "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP.", "local_ip": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7.", - "port": "\u0398\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP.", + "port": "\u0398\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX/IP.", "route_back": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03b5\u03ac\u03bd \u03bf \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae\u03c2 \u03c3\u03b1\u03c2 KNXnet/IP tunneling \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c0\u03af\u03c3\u03c9 \u03b1\u03c0\u03cc \u03c4\u03bf NAT. \u0399\u03c3\u03c7\u03cd\u03b5\u03b9 \u03bc\u03cc\u03bd\u03bf \u03b3\u03b9\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03b9\u03c2 UDP." }, - "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03ac\u03c2 \u03c3\u03b1\u03c2." + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03ac\u03bd\u03bf\u03b9\u03be\u03b7\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2." }, "routing": { "data": { - "individual_address": "\u0391\u03c4\u03bf\u03bc\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7\u03c2", - "local_ip": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae IP \u03c4\u03bf\u03c5 Home Assistant (\u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03b5\u03bd\u03ae \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7)", - "multicast_group": "\u0397 \u03bf\u03bc\u03ac\u03b4\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b5\u03ba\u03c0\u03bf\u03bc\u03c0\u03ae\u03c2 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7", - "multicast_port": "\u0397 \u03b8\u03cd\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03bd\u03bf\u03bc\u03ae\u03c2 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7" + "individual_address": "\u0391\u03c4\u03bf\u03bc\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7", + "local_ip": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae IP \u03c4\u03bf\u03c5 Home Assistant", + "multicast_group": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03bd\u03bf\u03bc\u03ae\u03c2", + "multicast_port": "\u0398\u03cd\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b5\u03ba\u03c0\u03bf\u03bc\u03c0\u03ae\u03c2" }, "data_description": { "individual_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant, \u03c0.\u03c7. `0.0.4`.", @@ -89,7 +89,7 @@ "secure_tunneling": { "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03ce\u03c2 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf IP Secure.", "menu_options": { - "secure_knxkeys": "\u0394\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 IP secure", + "secure_knxkeys": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf `.knxkeys` \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ac \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP", "secure_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 IP secure \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b1", "secure_tunnel_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ce\u03bd \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP \u03bc\u03b5 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf \u03c4\u03c1\u03cc\u03c0\u03bf" } @@ -145,6 +145,17 @@ "state_updater": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2 \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03c9\u03bd \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b4\u03af\u03b1\u03c5\u03bb\u03bf KNX. \u038c\u03c4\u03b1\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf, \u03c4\u03bf Home Assistant \u03b4\u03b5\u03bd \u03b8\u03b1 \u03b1\u03bd\u03b1\u03ba\u03c4\u03ac \u03b5\u03bd\u03b5\u03c1\u03b3\u03ac \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 \u03b1\u03c0\u03cc \u03c4\u03bf KNX Bus, \u03bf\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 `sync_state` \u03b4\u03b5\u03bd \u03b8\u03b1 \u03ad\u03c7\u03bf\u03c5\u03bd \u03ba\u03b1\u03bc\u03af\u03b1 \u03b5\u03c0\u03af\u03b4\u03c1\u03b1\u03c3\u03b7." } }, + "manual_tunnel": { + "data": { + "port": "\u0398\u03cd\u03c1\u03b1" + }, + "data_description": { + "local_ip": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7.", + "port": "\u0398\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX/IP.", + "route_back": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03b5\u03ac\u03bd \u03bf \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae\u03c2 \u03c3\u03b1\u03c2 KNXnet/IP tunneling \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c0\u03af\u03c3\u03c9 \u03b1\u03c0\u03cc \u03c4\u03bf NAT. \u0399\u03c3\u03c7\u03cd\u03b5\u03b9 \u03bc\u03cc\u03bd\u03bf \u03b3\u03b9\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03b9\u03c2 UDP." + }, + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03ac\u03bd\u03bf\u03b9\u03be\u03b7\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2." + }, "options_init": { "menu_options": { "communication_settings": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03b5\u03c0\u03b9\u03ba\u03bf\u03b9\u03bd\u03c9\u03bd\u03af\u03b1\u03c2", @@ -152,6 +163,12 @@ } }, "routing": { + "data": { + "individual_address": "\u0391\u03c4\u03bf\u03bc\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7", + "local_ip": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae IP \u03c4\u03bf\u03c5 Home Assistant", + "multicast_group": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03bd\u03bf\u03bc\u03ae\u03c2", + "multicast_port": "\u0398\u03cd\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b5\u03ba\u03c0\u03bf\u03bc\u03c0\u03ae\u03c2" + }, "data_description": { "individual_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant, \u03c0.\u03c7. `0.0.4`.", "local_ip": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7." @@ -160,11 +177,38 @@ }, "secure_knxkeys": { "data": { - "knxkeys_filename": "\u03a4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03c3\u03b1\u03c2 `.knxkeys` (\u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b1\u03bc\u03b2\u03b1\u03bd\u03bf\u03bc\u03ad\u03bd\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03b5\u03c0\u03ad\u03ba\u03c4\u03b1\u03c3\u03b7\u03c2)" + "knxkeys_filename": "\u03a4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03c3\u03b1\u03c2 `.knxkeys` (\u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b1\u03bc\u03b2\u03b1\u03bd\u03bf\u03bc\u03ad\u03bd\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03b5\u03c0\u03ad\u03ba\u03c4\u03b1\u03c3\u03b7\u03c2)", + "knxkeys_password": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03ac\u03c6\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 knxkeys" + }, + "data_description": { + "knxkeys_filename": "\u03a4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03b1\u03bd\u03b1\u03bc\u03ad\u03bd\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03b2\u03c1\u03b5\u03b8\u03b5\u03af \u03c3\u03c4\u03bf\u03bd \u03ba\u03b1\u03c4\u03ac\u03bb\u03bf\u03b3\u03bf \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c9\u03bd \u03c3\u03c4\u03bf `.storage/knx/`.\n \u03a3\u03c4\u03bf Home Assistant OS \u03b1\u03c5\u03c4\u03cc \u03b8\u03b1 \u03ae\u03c4\u03b1\u03bd `/config/.storage/knx/`\n \u03a0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1: `my_project.knxkeys`", + "knxkeys_password": "\u0391\u03c5\u03c4\u03cc \u03bf\u03c1\u03af\u03c3\u03c4\u03b7\u03ba\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03be\u03b1\u03b3\u03c9\u03b3\u03ae \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03b1\u03c0\u03cc \u03c4\u03bf ETS." + }, + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03c3\u03b1\u03c2." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", + "user_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", + "user_password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03a7\u03c1\u03ae\u03c3\u03c4\u03b7" + }, + "data_description": { + "device_authentication": "\u0391\u03c5\u03c4\u03cc \u03bf\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf\u03bd \u03c0\u03af\u03bd\u03b1\u03ba\u03b1 \u00abIP\u00bb \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae\u03c2 \u03c3\u03c4\u03bf ETS.", + "user_id": "\u0391\u03c5\u03c4\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03c7\u03bd\u03ac \u03bf \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 +1. \u0386\u03c1\u03b1 \u03c4\u03bf \"Tunnel 2\" \u03b8\u03b1 \u03ad\u03c7\u03b5\u03b9 User-ID \"3\".", + "user_password": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af \u03c3\u03c4\u03bf\u03bd \u03c0\u03af\u03bd\u03b1\u03ba\u03b1 \u00ab\u0399\u03b4\u03b9\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\u00bb \u03c4\u03b7\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 \u03c3\u03c4\u03bf ETS." + }, + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 IP \u03c3\u03b1\u03c2." + }, + "secure_tunneling": { + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03ce\u03c2 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf IP Secure.", + "menu_options": { + "secure_knxkeys": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf `.knxkeys` \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ac \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP", + "secure_tunnel_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ce\u03bd \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP \u03bc\u03b5 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf \u03c4\u03c1\u03cc\u03c0\u03bf" } }, "tunnel": { "data": { + "gateway": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX", "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", "port": "\u0398\u03cd\u03c1\u03b1", "tunneling_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" @@ -172,7 +216,8 @@ "data_description": { "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP.", "port": "\u0398\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP." - } + }, + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c0\u03cd\u03bb\u03b7 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03bb\u03af\u03c3\u03c4\u03b1." } } } diff --git a/homeassistant/components/knx/translations/sk.json b/homeassistant/components/knx/translations/sk.json index efcd2371649..a973f77e114 100644 --- a/homeassistant/components/knx/translations/sk.json +++ b/homeassistant/components/knx/translations/sk.json @@ -12,7 +12,13 @@ }, "secure_manual": { "data": { - "device_authentication": "Heslo na overenie zariadenia" + "device_authentication": "Heslo na overenie zariadenia", + "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" + } + }, + "secure_tunnel_manual": { + "data": { + "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" } } } @@ -29,6 +35,11 @@ "host": "Hostite\u013e" } }, + "secure_tunnel_manual": { + "data": { + "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" + } + }, "tunnel": { "data": { "host": "Hostite\u013e", diff --git a/homeassistant/components/kodi/translations/sk.json b/homeassistant/components/kodi/translations/sk.json index 7f66f32dd4e..8a9f91ad3ee 100644 --- a/homeassistant/components/kodi/translations/sk.json +++ b/homeassistant/components/kodi/translations/sk.json @@ -10,6 +10,7 @@ "step": { "credentials": { "data": { + "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, diff --git a/homeassistant/components/konnected/translations/sk.json b/homeassistant/components/konnected/translations/sk.json index 288d7536945..3a0f5b6f90f 100644 --- a/homeassistant/components/konnected/translations/sk.json +++ b/homeassistant/components/konnected/translations/sk.json @@ -26,7 +26,9 @@ }, "options_switch": { "data": { - "name": "N\u00e1zov (volite\u013en\u00fd)" + "momentary": "Trvanie impulzu (ms)", + "name": "N\u00e1zov (volite\u013en\u00fd)", + "pause": "Pauza medzi impulzmi (ms)" } } } diff --git a/homeassistant/components/kostal_plenticore/translations/sk.json b/homeassistant/components/kostal_plenticore/translations/sk.json index 368553a4887..08d8e8f6516 100644 --- a/homeassistant/components/kostal_plenticore/translations/sk.json +++ b/homeassistant/components/kostal_plenticore/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo" } } } diff --git a/homeassistant/components/lacrosse_view/translations/sk.json b/homeassistant/components/lacrosse_view/translations/sk.json new file mode 100644 index 00000000000..66e8cd431a4 --- /dev/null +++ b/homeassistant/components/lacrosse_view/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/led_ble/translations/sk.json b/homeassistant/components/led_ble/translations/sk.json new file mode 100644 index 00000000000..e8940bef26a --- /dev/null +++ b/homeassistant/components/led_ble/translations/sk.json @@ -0,0 +1,5 @@ +{ + "config": { + "flow_title": "{name}" + } +} \ No newline at end of file diff --git a/homeassistant/components/life360/translations/sk.json b/homeassistant/components/life360/translations/sk.json index 2c3ed1dd930..b4d12b85362 100644 --- a/homeassistant/components/life360/translations/sk.json +++ b/homeassistant/components/life360/translations/sk.json @@ -5,6 +5,18 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/sk.json b/homeassistant/components/lifx/translations/sk.json new file mode 100644 index 00000000000..38ce072cd6e --- /dev/null +++ b/homeassistant/components/lifx/translations/sk.json @@ -0,0 +1,5 @@ +{ + "config": { + "flow_title": "{label} ({host}) {serial}" + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sk.json b/homeassistant/components/litterrobot/translations/sk.json index 5ada995aa6e..4b1e8bf1a77 100644 --- a/homeassistant/components/litterrobot/translations/sk.json +++ b/homeassistant/components/litterrobot/translations/sk.json @@ -2,6 +2,18 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/el.json b/homeassistant/components/livisi/translations/el.json index 1260983be3c..9d33b5aaa1e 100644 --- a/homeassistant/components/livisi/translations/el.json +++ b/homeassistant/components/livisi/translations/el.json @@ -6,6 +6,9 @@ }, "step": { "user": { + "data": { + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" + }, "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03ba\u03b1\u03b9 \u03c4\u03bf\u03bd (\u03c4\u03bf\u03c0\u03b9\u03ba\u03cc) \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 SHC." } } diff --git a/homeassistant/components/livisi/translations/sk.json b/homeassistant/components/livisi/translations/sk.json index 5656d8635a0..5a658de5246 100644 --- a/homeassistant/components/livisi/translations/sk.json +++ b/homeassistant/components/livisi/translations/sk.json @@ -1,9 +1,13 @@ { "config": { + "error": { + "wrong_password": "Heslo je nespr\u00e1vne." + }, "step": { "user": { "data": { - "host": "IP adresa" + "host": "IP adresa", + "password": "Heslo" } } } diff --git a/homeassistant/components/lookin/translations/sk.json b/homeassistant/components/lookin/translations/sk.json index 561644de2dd..79b9923e1cf 100644 --- a/homeassistant/components/lookin/translations/sk.json +++ b/homeassistant/components/lookin/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "flow_title": "{name} ({host})", "step": { "device_name": { "data": { diff --git a/homeassistant/components/lutron_caseta/translations/sk.json b/homeassistant/components/lutron_caseta/translations/sk.json index 842ff61bd79..d7dee4be88b 100644 --- a/homeassistant/components/lutron_caseta/translations/sk.json +++ b/homeassistant/components/lutron_caseta/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name} ({host})", "step": { "user": { "data": { diff --git a/homeassistant/components/mazda/translations/sk.json b/homeassistant/components/mazda/translations/sk.json index f8b6dfeea81..bf7ebd86a62 100644 --- a/homeassistant/components/mazda/translations/sk.json +++ b/homeassistant/components/mazda/translations/sk.json @@ -9,7 +9,8 @@ "step": { "user": { "data": { - "email": "Email" + "email": "Email", + "password": "Heslo" } } } diff --git a/homeassistant/components/meater/translations/sk.json b/homeassistant/components/meater/translations/sk.json index a52d3b46e7c..1dc09dd1da0 100644 --- a/homeassistant/components/meater/translations/sk.json +++ b/homeassistant/components/meater/translations/sk.json @@ -2,6 +2,18 @@ "config": { "error": { "service_unavailable_error": "Rozhranie API je moment\u00e1lne nedostupn\u00e9, sk\u00faste to pros\u00edm nesk\u00f4r." + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/melcloud/translations/sk.json b/homeassistant/components/melcloud/translations/sk.json index c043ef9ff19..7f2f6229304 100644 --- a/homeassistant/components/melcloud/translations/sk.json +++ b/homeassistant/components/melcloud/translations/sk.json @@ -6,6 +6,7 @@ "step": { "user": { "data": { + "password": "Heslo", "username": "Email" } } diff --git a/homeassistant/components/mikrotik/translations/sk.json b/homeassistant/components/mikrotik/translations/sk.json index e7eb9363f01..384e693a043 100644 --- a/homeassistant/components/mikrotik/translations/sk.json +++ b/homeassistant/components/mikrotik/translations/sk.json @@ -4,10 +4,16 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { "host": "Hostite\u013e", "name": "N\u00e1zov", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/mill/translations/sk.json b/homeassistant/components/mill/translations/sk.json new file mode 100644 index 00000000000..a221d039da3 --- /dev/null +++ b/homeassistant/components/mill/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "cloud": { + "data": { + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mjpeg/translations/sk.json b/homeassistant/components/mjpeg/translations/sk.json index 4a2050c2353..55f23ea3d80 100644 --- a/homeassistant/components/mjpeg/translations/sk.json +++ b/homeassistant/components/mjpeg/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "name": "N\u00e1zov" + "name": "N\u00e1zov", + "password": "Heslo" } } } @@ -18,7 +19,8 @@ "step": { "init": { "data": { - "name": "N\u00e1zov" + "name": "N\u00e1zov", + "password": "Heslo" } } } diff --git a/homeassistant/components/modern_forms/translations/sk.json b/homeassistant/components/modern_forms/translations/sk.json index 842ff61bd79..aaad66e6729 100644 --- a/homeassistant/components/modern_forms/translations/sk.json +++ b/homeassistant/components/modern_forms/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/mqtt/translations/sk.json b/homeassistant/components/mqtt/translations/sk.json index e01295844ec..4793e54fab6 100644 --- a/homeassistant/components/mqtt/translations/sk.json +++ b/homeassistant/components/mqtt/translations/sk.json @@ -17,6 +17,7 @@ "step": { "broker": { "data": { + "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } diff --git a/homeassistant/components/myq/translations/sk.json b/homeassistant/components/myq/translations/sk.json index 71a7aea5018..bd3a9e24cf1 100644 --- a/homeassistant/components/myq/translations/sk.json +++ b/homeassistant/components/myq/translations/sk.json @@ -5,6 +5,18 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/sk.json b/homeassistant/components/nam/translations/sk.json index c037dc135ae..65c34bd9d4b 100644 --- a/homeassistant/components/nam/translations/sk.json +++ b/homeassistant/components/nam/translations/sk.json @@ -8,6 +8,16 @@ }, "flow_title": "{host}", "step": { + "credentials": { + "data": { + "password": "Heslo" + } + }, + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/nanoleaf/translations/sk.json b/homeassistant/components/nanoleaf/translations/sk.json index 8717df47273..b41366e3fde 100644 --- a/homeassistant/components/nanoleaf/translations/sk.json +++ b/homeassistant/components/nanoleaf/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/netgear/translations/sk.json b/homeassistant/components/netgear/translations/sk.json index 5d61ddd2e3b..06265159290 100644 --- a/homeassistant/components/netgear/translations/sk.json +++ b/homeassistant/components/netgear/translations/sk.json @@ -3,7 +3,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e (Volite\u013en\u00e9)" + "host": "Hostite\u013e (Volite\u013en\u00e9)", + "password": "Heslo" }, "description": "Predvolen\u00fd hostite\u013e: {host}\nPredvolen\u00e9 pou\u017e\u00edvate\u013esk\u00e9 meno: {username}" } diff --git a/homeassistant/components/nexia/translations/sk.json b/homeassistant/components/nexia/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/nexia/translations/sk.json +++ b/homeassistant/components/nexia/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/notion/translations/sk.json b/homeassistant/components/notion/translations/sk.json index 71a7aea5018..bd3a9e24cf1 100644 --- a/homeassistant/components/notion/translations/sk.json +++ b/homeassistant/components/notion/translations/sk.json @@ -5,6 +5,18 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/nuheat/translations/sk.json b/homeassistant/components/nuheat/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/nuheat/translations/sk.json +++ b/homeassistant/components/nuheat/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/nzbget/translations/sk.json b/homeassistant/components/nzbget/translations/sk.json index 1c96c63ac91..e6a451b5ba5 100644 --- a/homeassistant/components/nzbget/translations/sk.json +++ b/homeassistant/components/nzbget/translations/sk.json @@ -5,6 +5,7 @@ "data": { "host": "Hostite\u013e", "name": "N\u00e1zov", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/omnilogic/translations/sk.json b/homeassistant/components/omnilogic/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/omnilogic/translations/sk.json +++ b/homeassistant/components/omnilogic/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/oncue/translations/sk.json b/homeassistant/components/oncue/translations/sk.json index 82a91905e7c..a74f3ca1a9f 100644 --- a/homeassistant/components/oncue/translations/sk.json +++ b/homeassistant/components/oncue/translations/sk.json @@ -3,6 +3,13 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/onvif/translations/it.json b/homeassistant/components/onvif/translations/it.json index 34d23e2f3fe..5a8e395174b 100644 --- a/homeassistant/components/onvif/translations/it.json +++ b/homeassistant/components/onvif/translations/it.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "Argomenti FFMPEG aggiuntivi", - "rtsp_transport": "Meccanismo di trasporto RTSP" + "rtsp_transport": "Meccanismo di trasporto RTSP", + "use_wallclock_as_timestamps": "Usa l'orologio come marca temporale" }, "title": "Opzioni dispositivo ONVIF" } diff --git a/homeassistant/components/onvif/translations/sk.json b/homeassistant/components/onvif/translations/sk.json index 1ec67861352..1188a84382e 100644 --- a/homeassistant/components/onvif/translations/sk.json +++ b/homeassistant/components/onvif/translations/sk.json @@ -9,6 +9,7 @@ "data": { "host": "Hostite\u013e", "name": "N\u00e1zov", + "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } diff --git a/homeassistant/components/oralb/translations/el.json b/homeassistant/components/oralb/translations/el.json index d3346614257..b1306bd0bce 100644 --- a/homeassistant/components/oralb/translations/el.json +++ b/homeassistant/components/oralb/translations/el.json @@ -1,7 +1,15 @@ { "config": { "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "not_supported": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9" + }, + "step": { + "user": { + "data": { + "address": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/ovo_energy/translations/sk.json b/homeassistant/components/ovo_energy/translations/sk.json index 5ada995aa6e..053375ea711 100644 --- a/homeassistant/components/ovo_energy/translations/sk.json +++ b/homeassistant/components/ovo_energy/translations/sk.json @@ -2,6 +2,18 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/sk.json b/homeassistant/components/picnic/translations/sk.json index 1c63a1923bd..083db09d558 100644 --- a/homeassistant/components/picnic/translations/sk.json +++ b/homeassistant/components/picnic/translations/sk.json @@ -9,7 +9,8 @@ "step": { "user": { "data": { - "country_code": "K\u00f3d krajiny" + "country_code": "K\u00f3d krajiny", + "password": "Heslo" } } } diff --git a/homeassistant/components/plex/translations/sk.json b/homeassistant/components/plex/translations/sk.json index d82a02b7bbf..b28db64fe23 100644 --- a/homeassistant/components/plex/translations/sk.json +++ b/homeassistant/components/plex/translations/sk.json @@ -6,6 +6,7 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, + "flow_title": "{name} ({host})", "step": { "manual_setup": { "data": { diff --git a/homeassistant/components/plugwise/translations/sk.json b/homeassistant/components/plugwise/translations/sk.json index ac5c3a01c03..b700b57b22c 100644 --- a/homeassistant/components/plugwise/translations/sk.json +++ b/homeassistant/components/plugwise/translations/sk.json @@ -3,6 +3,7 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/plum_lightpad/translations/sk.json b/homeassistant/components/plum_lightpad/translations/sk.json index ee5407aae19..cc9161d1644 100644 --- a/homeassistant/components/plum_lightpad/translations/sk.json +++ b/homeassistant/components/plum_lightpad/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "password": "Heslo", "username": "Email" } } diff --git a/homeassistant/components/poolsense/translations/sk.json b/homeassistant/components/poolsense/translations/sk.json index 72b0304f1c3..87352b48fad 100644 --- a/homeassistant/components/poolsense/translations/sk.json +++ b/homeassistant/components/poolsense/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "email": "Email" + "email": "Email", + "password": "Heslo" } } } diff --git a/homeassistant/components/powerwall/translations/sk.json b/homeassistant/components/powerwall/translations/sk.json index 71a7aea5018..680b72cf9a9 100644 --- a/homeassistant/components/powerwall/translations/sk.json +++ b/homeassistant/components/powerwall/translations/sk.json @@ -5,6 +5,19 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "flow_title": "{name} ({ip_address})", + "step": { + "reauth_confim": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/prosegur/translations/sk.json b/homeassistant/components/prosegur/translations/sk.json index 71a7aea5018..bd3a9e24cf1 100644 --- a/homeassistant/components/prosegur/translations/sk.json +++ b/homeassistant/components/prosegur/translations/sk.json @@ -5,6 +5,18 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/pure_energie/translations/sk.json b/homeassistant/components/pure_energie/translations/sk.json index 842ff61bd79..a73a7a961f5 100644 --- a/homeassistant/components/pure_energie/translations/sk.json +++ b/homeassistant/components/pure_energie/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{model} ({host})", "step": { "user": { "data": { diff --git a/homeassistant/components/qnap_qsw/translations/sk.json b/homeassistant/components/qnap_qsw/translations/sk.json new file mode 100644 index 00000000000..a4249f0efe6 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/sk.json @@ -0,0 +1,16 @@ +{ + "config": { + "step": { + "discovered_connection": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/renault/translations/sk.json b/homeassistant/components/renault/translations/sk.json index d1d6ad72898..a6e63951200 100644 --- a/homeassistant/components/renault/translations/sk.json +++ b/homeassistant/components/renault/translations/sk.json @@ -7,8 +7,14 @@ "invalid_credentials": "Neplatn\u00e9 overenie" }, "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { + "password": "Heslo", "username": "Email" } } diff --git a/homeassistant/components/ridwell/translations/sk.json b/homeassistant/components/ridwell/translations/sk.json index 71a7aea5018..bd3a9e24cf1 100644 --- a/homeassistant/components/ridwell/translations/sk.json +++ b/homeassistant/components/ridwell/translations/sk.json @@ -5,6 +5,18 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/ring/translations/sk.json b/homeassistant/components/ring/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/ring/translations/sk.json +++ b/homeassistant/components/ring/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/risco/translations/sk.json b/homeassistant/components/risco/translations/sk.json index d84b4133668..a0c200a4364 100644 --- a/homeassistant/components/risco/translations/sk.json +++ b/homeassistant/components/risco/translations/sk.json @@ -4,10 +4,20 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "cloud": { + "data": { + "password": "Heslo" + } + }, "local": { "data": { "host": "Hostite\u013e" } + }, + "user": { + "data": { + "password": "Heslo" + } } } }, diff --git a/homeassistant/components/rituals_perfume_genie/translations/sk.json b/homeassistant/components/rituals_perfume_genie/translations/sk.json index 72b0304f1c3..87352b48fad 100644 --- a/homeassistant/components/rituals_perfume_genie/translations/sk.json +++ b/homeassistant/components/rituals_perfume_genie/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "email": "Email" + "email": "Email", + "password": "Heslo" } } } diff --git a/homeassistant/components/roku/translations/sk.json b/homeassistant/components/roku/translations/sk.json index cefae299404..95a5e4fa51e 100644 --- a/homeassistant/components/roku/translations/sk.json +++ b/homeassistant/components/roku/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/roomba/translations/sk.json b/homeassistant/components/roomba/translations/sk.json index 76d3f7b111f..b3e6f50eaef 100644 --- a/homeassistant/components/roomba/translations/sk.json +++ b/homeassistant/components/roomba/translations/sk.json @@ -2,6 +2,12 @@ "config": { "flow_title": "{name} ({host})", "step": { + "link_manual": { + "data": { + "password": "Heslo" + }, + "title": "Zadajte heslo" + }, "manual": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/ruckus_unleashed/translations/sk.json b/homeassistant/components/ruckus_unleashed/translations/sk.json index 3c3fd224ad3..d2927b101d0 100644 --- a/homeassistant/components/ruckus_unleashed/translations/sk.json +++ b/homeassistant/components/ruckus_unleashed/translations/sk.json @@ -7,6 +7,7 @@ "user": { "data": { "host": "Hostite\u013e", + "password": "Heslo", "username": "U\u017e\u00edvate\u013esk\u00e9 meno" } } diff --git a/homeassistant/components/scrape/translations/sk.json b/homeassistant/components/scrape/translations/sk.json new file mode 100644 index 00000000000..9ff8487afaa --- /dev/null +++ b/homeassistant/components/scrape/translations/sk.json @@ -0,0 +1,20 @@ +{ + "config": { + "step": { + "user": { + "data": { + "password": "Heslo" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/screenlogic/translations/sk.json b/homeassistant/components/screenlogic/translations/sk.json index f547d5e3a90..5619ebff744 100644 --- a/homeassistant/components/screenlogic/translations/sk.json +++ b/homeassistant/components/screenlogic/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "gateway_entry": { "data": { diff --git a/homeassistant/components/sense/translations/sk.json b/homeassistant/components/sense/translations/sk.json index 72b0304f1c3..950756c502b 100644 --- a/homeassistant/components/sense/translations/sk.json +++ b/homeassistant/components/sense/translations/sk.json @@ -4,9 +4,15 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "reauth_validate": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { - "email": "Email" + "email": "Email", + "password": "Heslo" } } } diff --git a/homeassistant/components/sensor/translations/el.json b/homeassistant/components/sensor/translations/el.json index 10011a93c2f..6e31e95bf6b 100644 --- a/homeassistant/components/sensor/translations/el.json +++ b/homeassistant/components/sensor/translations/el.json @@ -67,6 +67,7 @@ "volatile_organic_compounds": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03b3\u03ba\u03ad\u03bd\u03c4\u03c1\u03c9\u03c3\u03b7 \u03c4\u03c9\u03bd \u03c0\u03c4\u03b7\u03c4\u03b9\u03ba\u03ce\u03bd \u03bf\u03c1\u03b3\u03b1\u03bd\u03b9\u03ba\u03ce\u03bd \u03b5\u03bd\u03ce\u03c3\u03b5\u03c9\u03bd {entity_name}", "voltage": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ae \u03c4\u03ac\u03c3\u03b7\u03c2 {entity_name}", "volume": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ae \u03cc\u03b3\u03ba\u03bf\u03c5 {entity_name}", + "water": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ae \u03bd\u03b5\u03c1\u03bf\u03cd {entity_name}", "weight": "\u03a4\u03bf \u03b2\u03ac\u03c1\u03bf\u03c2 {entity_name} \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9" } }, diff --git a/homeassistant/components/sharkiq/translations/sk.json b/homeassistant/components/sharkiq/translations/sk.json index 71a7aea5018..82277c439cf 100644 --- a/homeassistant/components/sharkiq/translations/sk.json +++ b/homeassistant/components/sharkiq/translations/sk.json @@ -5,6 +5,18 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/it.json b/homeassistant/components/shelly/translations/it.json index 7b882246e66..04bf99179b4 100644 --- a/homeassistant/components/shelly/translations/it.json +++ b/homeassistant/components/shelly/translations/it.json @@ -58,5 +58,18 @@ "single_push": "{subtype} singola pressione", "triple": "{subtype} premuto tre volte" } + }, + "options": { + "abort": { + "ble_unsupported": "Il supporto Bluetooth richiede la versione del firmware {ble_min_version} o successiva." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Modalit\u00e0 scansione Bluetooth" + }, + "description": "La scansione Bluetooth pu\u00f2 essere attiva o passiva. In caso di scansione attiva, lo Shelly richiede dati ai dispositivi vicini; in caso di scansione passiva, lo Shelly riceve dati non richiesti dai dispositivi vicini." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/sk.json b/homeassistant/components/shelly/translations/sk.json index 5b66d1e57e4..b40b3c0b06a 100644 --- a/homeassistant/components/shelly/translations/sk.json +++ b/homeassistant/components/shelly/translations/sk.json @@ -20,6 +20,11 @@ "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/simplisafe/translations/sk.json b/homeassistant/components/simplisafe/translations/sk.json index f59069125d0..d1d3d1408a9 100644 --- a/homeassistant/components/simplisafe/translations/sk.json +++ b/homeassistant/components/simplisafe/translations/sk.json @@ -7,6 +7,11 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { "password": "Heslo", diff --git a/homeassistant/components/skybell/translations/sk.json b/homeassistant/components/skybell/translations/sk.json new file mode 100644 index 00000000000..abc99d4bf43 --- /dev/null +++ b/homeassistant/components/skybell/translations/sk.json @@ -0,0 +1,16 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sleepiq/translations/sk.json b/homeassistant/components/sleepiq/translations/sk.json index 5ada995aa6e..4b1e8bf1a77 100644 --- a/homeassistant/components/sleepiq/translations/sk.json +++ b/homeassistant/components/sleepiq/translations/sk.json @@ -2,6 +2,18 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/sma/translations/sk.json b/homeassistant/components/sma/translations/sk.json index c8c897bc0f5..4b759c272ce 100644 --- a/homeassistant/components/sma/translations/sk.json +++ b/homeassistant/components/sma/translations/sk.json @@ -9,7 +9,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo" } } } diff --git a/homeassistant/components/smart_meter_texas/translations/sk.json b/homeassistant/components/smart_meter_texas/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/smart_meter_texas/translations/sk.json +++ b/homeassistant/components/smart_meter_texas/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/smarttub/translations/sk.json b/homeassistant/components/smarttub/translations/sk.json index f8b6dfeea81..bf7ebd86a62 100644 --- a/homeassistant/components/smarttub/translations/sk.json +++ b/homeassistant/components/smarttub/translations/sk.json @@ -9,7 +9,8 @@ "step": { "user": { "data": { - "email": "Email" + "email": "Email", + "password": "Heslo" } } } diff --git a/homeassistant/components/snooz/translations/el.json b/homeassistant/components/snooz/translations/el.json index 4024c6fc6ba..ca4174b137a 100644 --- a/homeassistant/components/snooz/translations/el.json +++ b/homeassistant/components/snooz/translations/el.json @@ -1,11 +1,20 @@ { "config": { + "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + }, + "flow_title": "{name}", "progress": { "wait_for_pairing_mode": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7, \u03b2\u03ac\u03bb\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c3\u03b5 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03cd\u03b6\u03b5\u03c5\u03be\u03b7\u03c2. \n\n ### \u03a0\u03ce\u03c2 \u03bd\u03b1 \u03b5\u03b9\u03c3\u03ad\u03bb\u03b8\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03cd\u03b6\u03b5\u03c5\u03be\u03b7\u03c2\n 1. \u0391\u03bd\u03b1\u03b3\u03ba\u03b1\u03c3\u03c4\u03b9\u03ba\u03ae \u03ad\u03be\u03bf\u03b4\u03bf\u03c2 \u03b1\u03c0\u03cc \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ad\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac SNOOZ.\n 2. \u03a0\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b1\u03b9 \u03ba\u03c1\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 \u03c0\u03b1\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf \u03c4\u03bf \u03ba\u03bf\u03c5\u03bc\u03c0\u03af \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae. \u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03cc\u03c4\u03b1\u03bd \u03b1\u03c1\u03c7\u03af\u03c3\u03bf\u03c5\u03bd \u03bd\u03b1 \u03b1\u03bd\u03b1\u03b2\u03bf\u03c3\u03b2\u03ae\u03bd\u03bf\u03c5\u03bd \u03c4\u03b1 \u03c6\u03ce\u03c4\u03b1 (\u03c0\u03b5\u03c1\u03af\u03c0\u03bf\u03c5 5 \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1)." }, "step": { "pairing_timeout": { "description": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b5\u03b9\u03c3\u03ae\u03bb\u03b8\u03b5 \u03c3\u03b5 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03cd\u03b6\u03b5\u03c5\u03be\u03b7\u03c2. \u039a\u03ac\u03bd\u03c4\u03b5 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03b7\u03bd \u03a5\u03c0\u03bf\u03b2\u03bf\u03bb\u03ae \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac. \n\n ### \u0391\u03bd\u03c4\u03b9\u03bc\u03b5\u03c4\u03ce\u03c0\u03b9\u03c3\u03b7 \u03c0\u03c1\u03bf\u03b2\u03bb\u03b7\u03bc\u03ac\u03c4\u03c9\u03bd\n 1. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03b7 \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac.\n 2. \u0391\u03c0\u03bf\u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b3\u03b9\u03b1 5 \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1 \u03ba\u03b1\u03b9 \u03bc\u03b5\u03c4\u03ac \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03be\u03b1\u03bd\u03ac." + }, + "user": { + "data": { + "address": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" + } } } } diff --git a/homeassistant/components/solax/translations/sk.json b/homeassistant/components/solax/translations/sk.json index 892b8b2cd91..599a1b7a304 100644 --- a/homeassistant/components/solax/translations/sk.json +++ b/homeassistant/components/solax/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/sonarr/translations/sk.json b/homeassistant/components/sonarr/translations/sk.json index 64731388e98..e6cc7a1767a 100644 --- a/homeassistant/components/sonarr/translations/sk.json +++ b/homeassistant/components/sonarr/translations/sk.json @@ -6,6 +6,7 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/spider/translations/sk.json b/homeassistant/components/spider/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/spider/translations/sk.json +++ b/homeassistant/components/spider/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/squeezebox/translations/sk.json b/homeassistant/components/squeezebox/translations/sk.json index 8f3f3bcd7a1..a9eaff35fa5 100644 --- a/homeassistant/components/squeezebox/translations/sk.json +++ b/homeassistant/components/squeezebox/translations/sk.json @@ -8,6 +8,7 @@ "edit": { "data": { "host": "Hostite\u013e", + "password": "Heslo", "port": "Port" } }, diff --git a/homeassistant/components/srp_energy/translations/sk.json b/homeassistant/components/srp_energy/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/srp_energy/translations/sk.json +++ b/homeassistant/components/srp_energy/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/starline/translations/sk.json b/homeassistant/components/starline/translations/sk.json new file mode 100644 index 00000000000..fef79e7eebe --- /dev/null +++ b/homeassistant/components/starline/translations/sk.json @@ -0,0 +1,14 @@ +{ + "config": { + "error": { + "error_auth_user": "Nespr\u00e1vne u\u017e\u00edvate\u013esk\u00e9 meno alebo heslo" + }, + "step": { + "auth_user": { + "data": { + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/subaru/translations/sk.json b/homeassistant/components/subaru/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/subaru/translations/sk.json +++ b/homeassistant/components/subaru/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/switch/translations/sk.json b/homeassistant/components/switch/translations/sk.json index 3f871c8a4a1..a1095c6469f 100644 --- a/homeassistant/components/switch/translations/sk.json +++ b/homeassistant/components/switch/translations/sk.json @@ -1,4 +1,14 @@ { + "device_automation": { + "action_type": { + "turn_off": "Vypn\u00fa\u0165 {entity_name}", + "turn_on": "Zapn\u00fa\u0165 {entity_name}" + }, + "trigger_type": { + "turned_off": "{entity_name} vypnut\u00e1", + "turned_on": "{entity_name} zapnut\u00e1" + } + }, "state": { "_": { "off": "Vypnut\u00fd", diff --git a/homeassistant/components/switch_as_x/translations/bg.json b/homeassistant/components/switch_as_x/translations/bg.json index f5c2a6e0433..e7491f78010 100644 --- a/homeassistant/components/switch_as_x/translations/bg.json +++ b/homeassistant/components/switch_as_x/translations/bg.json @@ -7,5 +7,6 @@ } } } - } + }, + "title": "\u041f\u0440\u043e\u043c\u044f\u043d\u0430 \u043d\u0430 \u0442\u0438\u043f\u0430 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u043d\u0430 \u043f\u0440\u0435\u0432\u043a\u043b\u044e\u0447\u0432\u0430\u0442\u0435\u043b" } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/sk.json b/homeassistant/components/switchbee/translations/sk.json index 842ff61bd79..666f6e28840 100644 --- a/homeassistant/components/switchbee/translations/sk.json +++ b/homeassistant/components/switchbee/translations/sk.json @@ -1,9 +1,19 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/switchbot/translations/sk.json b/homeassistant/components/switchbot/translations/sk.json index af15f92c2f2..c9f52d5e1b6 100644 --- a/homeassistant/components/switchbot/translations/sk.json +++ b/homeassistant/components/switchbot/translations/sk.json @@ -1,9 +1,20 @@ { "config": { + "abort": { + "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "flow_title": "{name} ({address})", "step": { + "password": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { - "name": "N\u00e1zov" + "name": "N\u00e1zov", + "password": "Heslo" } } } diff --git a/homeassistant/components/switcher_kis/translations/sk.json b/homeassistant/components/switcher_kis/translations/sk.json new file mode 100644 index 00000000000..d4bb209c34c --- /dev/null +++ b/homeassistant/components/switcher_kis/translations/sk.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "confirm": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/syncthru/translations/sk.json b/homeassistant/components/syncthru/translations/sk.json index 3d28cc36f74..bd95d061070 100644 --- a/homeassistant/components/syncthru/translations/sk.json +++ b/homeassistant/components/syncthru/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "confirm": { "data": { diff --git a/homeassistant/components/synology_dsm/translations/sk.json b/homeassistant/components/synology_dsm/translations/sk.json index dda01d0550d..59fbd6c1f5d 100644 --- a/homeassistant/components/synology_dsm/translations/sk.json +++ b/homeassistant/components/synology_dsm/translations/sk.json @@ -11,12 +11,19 @@ "step": { "link": { "data": { + "password": "Heslo", "port": "Port" } }, + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { "host": "Hostite\u013e", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/tado/translations/sk.json b/homeassistant/components/tado/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/tado/translations/sk.json +++ b/homeassistant/components/tado/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/sk.json b/homeassistant/components/tesla_wall_connector/translations/sk.json index 842ff61bd79..e909344d91b 100644 --- a/homeassistant/components/tesla_wall_connector/translations/sk.json +++ b/homeassistant/components/tesla_wall_connector/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{serial_number} ({host})", "step": { "user": { "data": { diff --git a/homeassistant/components/tile/translations/sk.json b/homeassistant/components/tile/translations/sk.json index d30ed436a4f..d1d3d1408a9 100644 --- a/homeassistant/components/tile/translations/sk.json +++ b/homeassistant/components/tile/translations/sk.json @@ -7,8 +7,14 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { + "password": "Heslo", "username": "Email" } } diff --git a/homeassistant/components/tolo/translations/sk.json b/homeassistant/components/tolo/translations/sk.json index 842ff61bd79..aaad66e6729 100644 --- a/homeassistant/components/tolo/translations/sk.json +++ b/homeassistant/components/tolo/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/totalconnect/translations/sk.json b/homeassistant/components/totalconnect/translations/sk.json index 71a7aea5018..93eb8dcc1b5 100644 --- a/homeassistant/components/totalconnect/translations/sk.json +++ b/homeassistant/components/totalconnect/translations/sk.json @@ -5,6 +5,13 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/tplink/translations/sk.json b/homeassistant/components/tplink/translations/sk.json index 842ff61bd79..d12d05c0033 100644 --- a/homeassistant/components/tplink/translations/sk.json +++ b/homeassistant/components/tplink/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name} {model} ({host})", "step": { "user": { "data": { diff --git a/homeassistant/components/tractive/translations/sk.json b/homeassistant/components/tractive/translations/sk.json index f8b6dfeea81..bf7ebd86a62 100644 --- a/homeassistant/components/tractive/translations/sk.json +++ b/homeassistant/components/tractive/translations/sk.json @@ -9,7 +9,8 @@ "step": { "user": { "data": { - "email": "Email" + "email": "Email", + "password": "Heslo" } } } diff --git a/homeassistant/components/transmission/translations/sk.json b/homeassistant/components/transmission/translations/sk.json index c3ed214e566..f01e6c74867 100644 --- a/homeassistant/components/transmission/translations/sk.json +++ b/homeassistant/components/transmission/translations/sk.json @@ -5,10 +5,16 @@ "name_exists": "N\u00e1zov u\u017e existuje" }, "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { "host": "Hostite\u013e", "name": "N\u00e1zov", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/tuya/translations/select.sk.json b/homeassistant/components/tuya/translations/select.sk.json index 493576472b9..bcd41ff83e0 100644 --- a/homeassistant/components/tuya/translations/select.sk.json +++ b/homeassistant/components/tuya/translations/select.sk.json @@ -6,6 +6,9 @@ "tuya__decibel_sensitivity": { "1": "Vysok\u00e1 citlivos\u0165" }, + "tuya__fingerbot_mode": { + "switch": "Prep\u00edna\u010d" + }, "tuya__motion_sensitivity": { "0": "N\u00edzka citlivos\u0165", "1": "Stredn\u00e1 citlivos\u0165", diff --git a/homeassistant/components/tuya/translations/sk.json b/homeassistant/components/tuya/translations/sk.json index 93fa886f796..90cb016cf9a 100644 --- a/homeassistant/components/tuya/translations/sk.json +++ b/homeassistant/components/tuya/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "country_code": "Krajina" + "country_code": "Krajina", + "password": "Heslo" } } } diff --git a/homeassistant/components/unifiprotect/translations/it.json b/homeassistant/components/unifiprotect/translations/it.json index fedbef4bdfc..609eb432fa2 100644 --- a/homeassistant/components/unifiprotect/translations/it.json +++ b/homeassistant/components/unifiprotect/translations/it.json @@ -42,8 +42,24 @@ } }, "issues": { + "ea_setup_failed": { + "description": "Stai utilizzando v{version} di UniFi Protect che \u00e8 una versione ad accesso anticipato. Si \u00e8 verificato un errore irreversibile durante il tentativo di caricare l'integrazione. [Esegui la retrocessione ad una versione stabile](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) di UniFi Protect per continuare ad utilizzare l'integrazione. \n\nErrore: {error}", + "title": "Errore di configurazione durante l'utilizzo della versione ad accesso anticipato" + }, "ea_warning": { "description": "Si sta utilizzando v{version} di UniFi Protect che \u00e8 una versione in accesso anticipato. Le versioni in accesso anticipato non sono supportate da Home Assistant e potrebbero causare l'interruzione o il mancato funzionamento dell'integrazione di UniFi Protect.", + "fix_flow": { + "step": { + "confirm": { + "description": "Sei sicuro di voler eseguire versioni non supportate di UniFi Protect? Ci\u00f2 potrebbe causare l'interruzione dell'integrazione di Home Assistant.", + "title": "v{version} \u00e8 una versione in accesso anticipato" + }, + "start": { + "description": "Stai utilizzando la v{version} di UniFi Protect che \u00e8 una versione ad accesso anticipato. [Le versioni ad accesso anticipato non sono supportate da Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) e si consiglia di tornare a una versione stabile non appena possibile. \n\nInviando questo modulo hai [retrocesso UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) oppure accetti di eseguire una versione non supportata di UniFi Protect.", + "title": "v{version} \u00e8 una versione in accesso anticipato" + } + } + }, "title": "UniFi Protect v{version} \u00e8 una versione in accesso anticipato" } }, @@ -55,6 +71,7 @@ "init": { "data": { "all_updates": "Metriche in tempo reale (ATTENZIONE: aumenta notevolmente l'utilizzo della CPU)", + "allow_ea": "Consenti versioni ad accesso anticipato di Protect (ATTENZIONE: l'integrazione verr\u00e0 contrassegnata come non supportata)", "disable_rtsp": "Disabilita il flusso RTSP", "ignored_devices": "Elenco separato da virgole di indirizzi MAC di dispositivi da ignorare", "max_media": "Numero massimo di eventi da caricare per Media Browser (aumenta l'utilizzo della RAM)", diff --git a/homeassistant/components/unifiprotect/translations/sk.json b/homeassistant/components/unifiprotect/translations/sk.json index 7e4857f7ec5..c36677b0ed8 100644 --- a/homeassistant/components/unifiprotect/translations/sk.json +++ b/homeassistant/components/unifiprotect/translations/sk.json @@ -4,14 +4,21 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "discovery_confirm": { + "data": { + "password": "Heslo" + } + }, "reauth_confirm": { "data": { + "password": "Heslo", "port": "Port" } }, "user": { "data": { "host": "Hostite\u013e", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/upcloud/translations/sk.json b/homeassistant/components/upcloud/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/upcloud/translations/sk.json +++ b/homeassistant/components/upcloud/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/upnp/translations/sk.json b/homeassistant/components/upnp/translations/sk.json index 793f8eff278..c9177d7b5d2 100644 --- a/homeassistant/components/upnp/translations/sk.json +++ b/homeassistant/components/upnp/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" - } + }, + "flow_title": "{name}" } } \ No newline at end of file diff --git a/homeassistant/components/venstar/translations/sk.json b/homeassistant/components/venstar/translations/sk.json index 842ff61bd79..9efe27e4e38 100644 --- a/homeassistant/components/venstar/translations/sk.json +++ b/homeassistant/components/venstar/translations/sk.json @@ -3,7 +3,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo" } } } diff --git a/homeassistant/components/verisure/translations/sk.json b/homeassistant/components/verisure/translations/sk.json index 0f898b977ee..4ceb4d02151 100644 --- a/homeassistant/components/verisure/translations/sk.json +++ b/homeassistant/components/verisure/translations/sk.json @@ -9,12 +9,14 @@ "step": { "reauth_confirm": { "data": { - "email": "Email" + "email": "Email", + "password": "Heslo" } }, "user": { "data": { - "email": "Email" + "email": "Email", + "password": "Heslo" } } } diff --git a/homeassistant/components/vesync/translations/sk.json b/homeassistant/components/vesync/translations/sk.json index c043ef9ff19..98944b83429 100644 --- a/homeassistant/components/vesync/translations/sk.json +++ b/homeassistant/components/vesync/translations/sk.json @@ -6,8 +6,10 @@ "step": { "user": { "data": { + "password": "Heslo", "username": "Email" - } + }, + "title": "Zadajte pou\u017e\u00edvate\u013esk\u00e9 meno a heslo" } } } diff --git a/homeassistant/components/vicare/translations/sk.json b/homeassistant/components/vicare/translations/sk.json index dfca287492d..00b93b9ac57 100644 --- a/homeassistant/components/vicare/translations/sk.json +++ b/homeassistant/components/vicare/translations/sk.json @@ -8,6 +8,7 @@ "user": { "data": { "client_id": "API k\u013e\u00fa\u010d", + "password": "Heslo", "username": "Email" } } diff --git a/homeassistant/components/vlc_telnet/translations/sk.json b/homeassistant/components/vlc_telnet/translations/sk.json index 9b3bf644a30..6c6301cd13a 100644 --- a/homeassistant/components/vlc_telnet/translations/sk.json +++ b/homeassistant/components/vlc_telnet/translations/sk.json @@ -9,10 +9,16 @@ }, "flow_title": "{host}", "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { "host": "Hostite\u013e", "name": "N\u00e1zov", + "password": "Heslo", "port": "Port" } } diff --git a/homeassistant/components/volvooncall/translations/sk.json b/homeassistant/components/volvooncall/translations/sk.json new file mode 100644 index 00000000000..66e8cd431a4 --- /dev/null +++ b/homeassistant/components/volvooncall/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/it.json b/homeassistant/components/wake_on_lan/translations/it.json new file mode 100644 index 00000000000..758ef17ae09 --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/it.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "La configurazione di Wake on Lan tramite YAML \u00e8 stata spostata nella chiave di integrazione. \n\nLa tua configurazione YAML esistente funzioner\u00e0 per altre 2 versioni. \n\nMigra la tua configurazione YAML alla chiave di integrazione seguendo la documentazione.", + "title": "La configurazione YAML di Wake on Lan \u00e8 stata spostata" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/sk.json b/homeassistant/components/wallbox/translations/sk.json index 71a7aea5018..bd3a9e24cf1 100644 --- a/homeassistant/components/wallbox/translations/sk.json +++ b/homeassistant/components/wallbox/translations/sk.json @@ -5,6 +5,18 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/watttime/translations/sk.json b/homeassistant/components/watttime/translations/sk.json index 1d9ecbee3fc..9becbdda335 100644 --- a/homeassistant/components/watttime/translations/sk.json +++ b/homeassistant/components/watttime/translations/sk.json @@ -17,6 +17,16 @@ "data": { "location_type": "Umiestnenie" } + }, + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, + "user": { + "data": { + "password": "Heslo" + } } } } diff --git a/homeassistant/components/whirlpool/translations/sk.json b/homeassistant/components/whirlpool/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/whirlpool/translations/sk.json +++ b/homeassistant/components/whirlpool/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/wled/translations/sk.json b/homeassistant/components/wled/translations/sk.json index 842ff61bd79..aaad66e6729 100644 --- a/homeassistant/components/wled/translations/sk.json +++ b/homeassistant/components/wled/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/wolflink/translations/sk.json b/homeassistant/components/wolflink/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/wolflink/translations/sk.json +++ b/homeassistant/components/wolflink/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_aqara/translations/sk.json b/homeassistant/components/xiaomi_aqara/translations/sk.json index 2c8dce2a9d8..2fbc4f458b4 100644 --- a/homeassistant/components/xiaomi_aqara/translations/sk.json +++ b/homeassistant/components/xiaomi_aqara/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/xiaomi_miio/translations/sk.json b/homeassistant/components/xiaomi_miio/translations/sk.json index 41d70853d59..c61a54e6d8d 100644 --- a/homeassistant/components/xiaomi_miio/translations/sk.json +++ b/homeassistant/components/xiaomi_miio/translations/sk.json @@ -5,6 +5,7 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, + "flow_title": "{name}", "step": { "manual": { "data": { diff --git a/homeassistant/components/yale_smart_alarm/translations/sk.json b/homeassistant/components/yale_smart_alarm/translations/sk.json index 6130380de31..0e707dc558b 100644 --- a/homeassistant/components/yale_smart_alarm/translations/sk.json +++ b/homeassistant/components/yale_smart_alarm/translations/sk.json @@ -15,7 +15,8 @@ }, "user": { "data": { - "name": "N\u00e1zov" + "name": "N\u00e1zov", + "password": "Heslo" } } } diff --git a/homeassistant/components/yalexs_ble/translations/sk.json b/homeassistant/components/yalexs_ble/translations/sk.json new file mode 100644 index 00000000000..e8940bef26a --- /dev/null +++ b/homeassistant/components/yalexs_ble/translations/sk.json @@ -0,0 +1,5 @@ +{ + "config": { + "flow_title": "{name}" + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/el.json b/homeassistant/components/zamg/translations/el.json index 4e7f31120be..5e5408a2b5a 100644 --- a/homeassistant/components/zamg/translations/el.json +++ b/homeassistant/components/zamg/translations/el.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/zamg/translations/sk.json b/homeassistant/components/zamg/translations/sk.json new file mode 100644 index 00000000000..e8940bef26a --- /dev/null +++ b/homeassistant/components/zamg/translations/sk.json @@ -0,0 +1,5 @@ +{ + "config": { + "flow_title": "{name}" + } +} \ No newline at end of file diff --git a/homeassistant/components/zha/translations/sk.json b/homeassistant/components/zha/translations/sk.json new file mode 100644 index 00000000000..e8940bef26a --- /dev/null +++ b/homeassistant/components/zha/translations/sk.json @@ -0,0 +1,5 @@ +{ + "config": { + "flow_title": "{name}" + } +} \ No newline at end of file diff --git a/homeassistant/components/zoneminder/translations/sk.json b/homeassistant/components/zoneminder/translations/sk.json index 2c3ed1dd930..8bacfc145e2 100644 --- a/homeassistant/components/zoneminder/translations/sk.json +++ b/homeassistant/components/zoneminder/translations/sk.json @@ -5,6 +5,13 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/sk.json b/homeassistant/components/zwave_js/translations/sk.json index 833d18faafb..675a2a95d55 100644 --- a/homeassistant/components/zwave_js/translations/sk.json +++ b/homeassistant/components/zwave_js/translations/sk.json @@ -3,7 +3,8 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" - } + }, + "flow_title": "{name}" }, "options": { "abort": { From d2000f0c7a4bd516965b15d2d6ec9ef1eaf966db Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 21 Nov 2022 01:29:55 +0100 Subject: [PATCH 0585/1033] Fix round typing [zwave_js] (#82439) --- homeassistant/components/zwave_js/cover.py | 2 +- homeassistant/components/zwave_js/light.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zwave_js/cover.py b/homeassistant/components/zwave_js/cover.py index 43b51048de4..4704718c804 100644 --- a/homeassistant/components/zwave_js/cover.py +++ b/homeassistant/components/zwave_js/cover.py @@ -129,7 +129,7 @@ class ZWaveCover(ZWaveBaseEntity, CoverEntity): if self.info.primary_value.value is None: # guard missing value return None - return round((self.info.primary_value.value / 99) * 100) + return round((cast(int, self.info.primary_value.value) / 99) * 100) async def async_set_cover_position(self, **kwargs: Any) -> None: """Move the cover to a specific position.""" diff --git a/homeassistant/components/zwave_js/light.py b/homeassistant/components/zwave_js/light.py index 6b2fd2c8dbb..94d55dc1a2f 100644 --- a/homeassistant/components/zwave_js/light.py +++ b/homeassistant/components/zwave_js/light.py @@ -177,7 +177,7 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity): """ if self.info.primary_value.value is None: return None - return round((self.info.primary_value.value / 99) * 255) + return round((cast(int, self.info.primary_value.value) / 99) * 255) @property def color_mode(self) -> str | None: From c068024abe3b63fd995cce1a7ba2b84c19872e86 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 21 Nov 2022 08:18:37 +0100 Subject: [PATCH 0586/1033] Allow `None` return for notify `get_service` (#82444) --- homeassistant/components/notify/legacy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/notify/legacy.py b/homeassistant/components/notify/legacy.py index 0172fbc98d0..35c1bbe65bf 100644 --- a/homeassistant/components/notify/legacy.py +++ b/homeassistant/components/notify/legacy.py @@ -41,7 +41,7 @@ class LegacyNotifyPlatform(Protocol): hass: HomeAssistant, config: ConfigType, discovery_info: DiscoveryInfoType | None = ..., - ) -> BaseNotificationService: + ) -> BaseNotificationService | None: """Set up notification service.""" def get_service( @@ -49,7 +49,7 @@ class LegacyNotifyPlatform(Protocol): hass: HomeAssistant, config: ConfigType, discovery_info: DiscoveryInfoType | None = ..., - ) -> BaseNotificationService: + ) -> BaseNotificationService | None: """Set up notification service.""" @@ -82,7 +82,7 @@ def async_setup_legacy( full_name = f"{DOMAIN}.{integration_name}" LOGGER.info("Setting up %s", full_name) with async_start_setup(hass, [full_name]): - notify_service = None + notify_service: BaseNotificationService | None = None try: if hasattr(platform, "async_get_service"): notify_service = await platform.async_get_service( From 71604f544b06f22b0661c04f9c137fca8ed99191 Mon Sep 17 00:00:00 2001 From: mvn23 Date: Mon, 21 Nov 2022 08:47:38 +0100 Subject: [PATCH 0587/1033] Bump pyotgw to 2.1.3 (#82430) --- homeassistant/components/opentherm_gw/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/opentherm_gw/manifest.json b/homeassistant/components/opentherm_gw/manifest.json index 99a10bc1539..488f6cb4f1e 100644 --- a/homeassistant/components/opentherm_gw/manifest.json +++ b/homeassistant/components/opentherm_gw/manifest.json @@ -2,7 +2,7 @@ "domain": "opentherm_gw", "name": "OpenTherm Gateway", "documentation": "https://www.home-assistant.io/integrations/opentherm_gw", - "requirements": ["pyotgw==2.1.1"], + "requirements": ["pyotgw==2.1.3"], "codeowners": ["@mvn23"], "config_flow": true, "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index c4a4140f9d1..776721854b6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1801,7 +1801,7 @@ pyopnsense==0.2.0 pyoppleio==1.0.5 # homeassistant.components.opentherm_gw -pyotgw==2.1.1 +pyotgw==2.1.3 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 06c21a6c504..31d0c68dbc0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1278,7 +1278,7 @@ pyopenuv==2022.04.0 pyopnsense==0.2.0 # homeassistant.components.opentherm_gw -pyotgw==2.1.1 +pyotgw==2.1.3 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp From 33e57c2e24b9d91ee3bce2a73850e4a9686fa0ff Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 21 Nov 2022 08:51:04 +0100 Subject: [PATCH 0588/1033] Fix round typing [tradfri] (#82438) --- homeassistant/components/tradfri/sensor.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/tradfri/sensor.py b/homeassistant/components/tradfri/sensor.py index ebd90333292..acda0bec06d 100644 --- a/homeassistant/components/tradfri/sensor.py +++ b/homeassistant/components/tradfri/sensor.py @@ -54,6 +54,7 @@ class TradfriSensorEntityDescription( def _get_air_quality(device: Device) -> int | None: """Fetch the air quality value.""" + assert device.air_purifier_control is not None if ( device.air_purifier_control.air_purifiers[0].air_quality == 65535 ): # The sensor returns 65535 if the fan is turned off @@ -64,8 +65,12 @@ def _get_air_quality(device: Device) -> int | None: def _get_filter_time_left(device: Device) -> int: """Fetch the filter's remaining life (in hours).""" + assert device.air_purifier_control is not None return round( - device.air_purifier_control.air_purifiers[0].filter_lifetime_remaining / 60 + cast( + int, device.air_purifier_control.air_purifiers[0].filter_lifetime_remaining + ) + / 60 ) From 914384578e25acb8cd5f5e1da7d2b4b3185d116f Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 21 Nov 2022 08:53:52 +0100 Subject: [PATCH 0589/1033] Fix overkiz bitwise operation (#82456) --- .../overkiz/climate_entities/atlantic_electrical_towel_dryer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/overkiz/climate_entities/atlantic_electrical_towel_dryer.py b/homeassistant/components/overkiz/climate_entities/atlantic_electrical_towel_dryer.py index 7ab59a47a34..374d7e1164d 100644 --- a/homeassistant/components/overkiz/climate_entities/atlantic_electrical_towel_dryer.py +++ b/homeassistant/components/overkiz/climate_entities/atlantic_electrical_towel_dryer.py @@ -57,7 +57,7 @@ class AtlanticElectricalTowelDryer(OverkizEntity, ClimateEntity): # Not all AtlanticElectricalTowelDryer models support presets, thus we need to check if the command is available if self.executor.has_command(OverkizCommand.SET_TOWEL_DRYER_TEMPORARY_STATE): - self._attr_supported_features += ClimateEntityFeature.PRESET_MODE + self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE @property def hvac_mode(self) -> str: From 8f33ad38cf443fedeef7f32b48f179c60415f583 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 21 Nov 2022 08:58:45 +0100 Subject: [PATCH 0590/1033] Update pylint to 2.15.6 (#82440) * Update pylint to 2.15.6 * Use single pylint disable * Use implicit dict constructor --- .../components/esphome/bluetooth/client.py | 10 ++++++---- homeassistant/components/kef/media_player.py | 16 ++++++++-------- requirements_test.txt | 4 ++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index ceac4e5aaae..6672b17d8ba 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -92,15 +92,17 @@ def api_error_as_bleak_error(func: _WrapFuncType) -> _WrapFuncType: # Because callbacks are delivered asynchronously it's possible # that we find out about the disconnection during the operation # before the callback is delivered. + + # pylint: disable=protected-access if ex.error.error == -1: _LOGGER.debug( "%s: %s - %s: BLE device disconnected during %s operation", - self._source, # pylint: disable=protected-access - self._ble_device.name, # pylint: disable=protected-access - self._ble_device.address, # pylint: disable=protected-access + self._source, + self._ble_device.name, + self._ble_device.address, func.__name__, ) - self._async_ble_device_disconnected() # pylint: disable=protected-access + self._async_ble_device_disconnected() raise BleakError(str(ex)) from ex except APIConnectionError as err: raise BleakError(str(err)) from err diff --git a/homeassistant/components/kef/media_player.py b/homeassistant/components/kef/media_player.py index 078354c705e..96f52ef7e03 100644 --- a/homeassistant/components/kef/media_player.py +++ b/homeassistant/components/kef/media_player.py @@ -321,15 +321,15 @@ class KefMediaPlayer(MediaPlayerEntity): return mode = await self._speaker.get_mode() - self._dsp = dict( - desk_db=await self._speaker.get_desk_db(), - wall_db=await self._speaker.get_wall_db(), - treble_db=await self._speaker.get_treble_db(), - high_hz=await self._speaker.get_high_hz(), - low_hz=await self._speaker.get_low_hz(), - sub_db=await self._speaker.get_sub_db(), + self._dsp = { + "desk_db": await self._speaker.get_desk_db(), + "wall_db": await self._speaker.get_wall_db(), + "treble_db": await self._speaker.get_treble_db(), + "high_hz": await self._speaker.get_high_hz(), + "low_hz": await self._speaker.get_low_hz(), + "sub_db": await self._speaker.get_sub_db(), **mode._asdict(), - ) + } async def async_added_to_hass(self) -> None: """Subscribe to DSP updates.""" diff --git a/requirements_test.txt b/requirements_test.txt index 6aa8ed48236..b4bfaa3038d 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -7,14 +7,14 @@ -c homeassistant/package_constraints.txt -r requirements_test_pre_commit.txt -astroid==2.12.12 +astroid==2.12.13 codecov==2.1.12 coverage==6.4.4 freezegun==1.2.2 mock-open==1.4.0 mypy==0.991 pre-commit==2.20.0 -pylint==2.15.5 +pylint==2.15.6 pipdeptree==2.3.1 pytest-aiohttp==0.3.0 pytest-cov==3.0.0 From 36e38841a4140053a01bd72be91e9567d9556a7f Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Mon, 21 Nov 2022 09:02:11 +0100 Subject: [PATCH 0591/1033] Bump nibe to 1.3.0 (#82445) --- homeassistant/components/nibe_heatpump/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nibe_heatpump/manifest.json b/homeassistant/components/nibe_heatpump/manifest.json index 511970832ed..f9276570885 100644 --- a/homeassistant/components/nibe_heatpump/manifest.json +++ b/homeassistant/components/nibe_heatpump/manifest.json @@ -3,7 +3,7 @@ "name": "Nibe Heat Pump", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/nibe_heatpump", - "requirements": ["nibe==1.2.1"], + "requirements": ["nibe==1.3.0"], "codeowners": ["@elupus"], "iot_class": "local_polling" } diff --git a/requirements_all.txt b/requirements_all.txt index 776721854b6..0ac94b2f24a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1166,7 +1166,7 @@ nextcord==2.0.0a8 nextdns==1.1.1 # homeassistant.components.nibe_heatpump -nibe==1.2.1 +nibe==1.3.0 # homeassistant.components.niko_home_control niko-home-control==0.2.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 31d0c68dbc0..d7ac04e5f16 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -853,7 +853,7 @@ nextcord==2.0.0a8 nextdns==1.1.1 # homeassistant.components.nibe_heatpump -nibe==1.2.1 +nibe==1.3.0 # homeassistant.components.nfandroidtv notifications-android-tv==0.1.5 From aa3bd78f7e3eaa4d9f5cf77342cc49be2be6aea3 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 21 Nov 2022 09:06:30 +0100 Subject: [PATCH 0592/1033] Code quality improvements for `nissan_leaf` (#82442) Code quality improvements for nissan_leaf --- .../components/nissan_leaf/__init__.py | 2 +- .../components/nissan_leaf/binary_sensor.py | 8 ++--- .../components/nissan_leaf/sensor.py | 30 ++++++------------- .../components/nissan_leaf/switch.py | 6 ++-- 4 files changed, 15 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/nissan_leaf/__init__.py b/homeassistant/components/nissan_leaf/__init__.py index 75757ae4a25..a32ba2e6329 100644 --- a/homeassistant/components/nissan_leaf/__init__.py +++ b/homeassistant/components/nissan_leaf/__init__.py @@ -500,7 +500,7 @@ class LeafDataStore: class LeafEntity(Entity): """Base class for Nissan Leaf entity.""" - def __init__(self, car: Leaf) -> None: + def __init__(self, car: LeafDataStore) -> None: """Store LeafDataStore upon init.""" self.car = car diff --git a/homeassistant/components/nissan_leaf/binary_sensor.py b/homeassistant/components/nissan_leaf/binary_sensor.py index 7b8173cd31b..40880714c2a 100644 --- a/homeassistant/components/nissan_leaf/binary_sensor.py +++ b/homeassistant/components/nissan_leaf/binary_sensor.py @@ -3,8 +3,6 @@ from __future__ import annotations import logging -from pycarwings2.pycarwings2 import Leaf - from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, BinarySensorEntity, @@ -13,7 +11,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import LeafEntity +from . import LeafDataStore, LeafEntity from .const import DATA_CHARGING, DATA_LEAF, DATA_PLUGGED_IN _LOGGER = logging.getLogger(__name__) @@ -43,7 +41,7 @@ class LeafPluggedInSensor(LeafEntity, BinarySensorEntity): _attr_device_class = BinarySensorDeviceClass.PLUG - def __init__(self, car: Leaf) -> None: + def __init__(self, car: LeafDataStore) -> None: """Set up plug status sensor.""" super().__init__(car) self._attr_unique_id = f"{self.car.leaf.vin.lower()}_plugstatus" @@ -69,7 +67,7 @@ class LeafChargingSensor(LeafEntity, BinarySensorEntity): _attr_device_class = BinarySensorDeviceClass.BATTERY_CHARGING - def __init__(self, car: Leaf) -> None: + def __init__(self, car: LeafDataStore) -> None: """Set up charging status sensor.""" super().__init__(car) self._attr_unique_id = f"{self.car.leaf.vin.lower()}_chargingstatus" diff --git a/homeassistant/components/nissan_leaf/sensor.py b/homeassistant/components/nissan_leaf/sensor.py index c92ed2300a4..b0da5757db9 100644 --- a/homeassistant/components/nissan_leaf/sensor.py +++ b/homeassistant/components/nissan_leaf/sensor.py @@ -3,7 +3,6 @@ from __future__ import annotations import logging -from pycarwings2.pycarwings2 import Leaf from voluptuous.validators import Number from homeassistant.components.sensor import SensorDeviceClass, SensorEntity @@ -15,7 +14,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util.unit_conversion import DistanceConverter from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM -from . import LeafEntity +from . import LeafDataStore, LeafEntity from .const import ( DATA_BATTERY, DATA_CHARGING, @@ -26,8 +25,6 @@ from .const import ( _LOGGER = logging.getLogger(__name__) -ICON_RANGE = "mdi:speedometer" - def setup_platform( hass: HomeAssistant, @@ -52,7 +49,10 @@ def setup_platform( class LeafBatterySensor(LeafEntity, SensorEntity): """Nissan Leaf Battery Sensor.""" - def __init__(self, car: Leaf) -> None: + _attr_device_class = SensorDeviceClass.BATTERY + _attr_native_unit_of_measurement = PERCENTAGE + + def __init__(self, car: LeafDataStore) -> None: """Set up battery sensor.""" super().__init__(car) self._attr_unique_id = f"{self.car.leaf.vin.lower()}_soc" @@ -62,11 +62,6 @@ class LeafBatterySensor(LeafEntity, SensorEntity): """Sensor Name.""" return f"{self.car.leaf.nickname} Charge" - @property - def device_class(self) -> str: - """Return the device class of the sensor.""" - return SensorDeviceClass.BATTERY - @property def native_value(self) -> Number | None: """Battery state percentage.""" @@ -74,11 +69,6 @@ class LeafBatterySensor(LeafEntity, SensorEntity): return None return round(self.car.data[DATA_BATTERY]) - @property - def native_unit_of_measurement(self) -> str: - """Battery state measured in percentage.""" - return PERCENTAGE - @property def icon(self) -> str: """Battery state icon handling.""" @@ -89,7 +79,9 @@ class LeafBatterySensor(LeafEntity, SensorEntity): class LeafRangeSensor(LeafEntity, SensorEntity): """Nissan Leaf Range Sensor.""" - def __init__(self, car: Leaf, ac_on: bool) -> None: + _attr_icon = "mdi:speedometer" + + def __init__(self, car: LeafDataStore, ac_on: bool) -> None: """Set up range sensor. Store if AC on.""" self._ac_on = ac_on super().__init__(car) @@ -115,6 +107,7 @@ class LeafRangeSensor(LeafEntity, SensorEntity): @property def native_value(self) -> float | None: """Battery range in miles or kms.""" + ret: float | None if self._ac_on: ret = self.car.data[DATA_RANGE_AC] else: @@ -134,8 +127,3 @@ class LeafRangeSensor(LeafEntity, SensorEntity): if self.car.hass.config.units is US_CUSTOMARY_SYSTEM or self.car.force_miles: return LENGTH_MILES return LENGTH_KILOMETERS - - @property - def icon(self) -> str: - """Nice icon for range.""" - return ICON_RANGE diff --git a/homeassistant/components/nissan_leaf/switch.py b/homeassistant/components/nissan_leaf/switch.py index 0d655622517..97f02a9e0be 100644 --- a/homeassistant/components/nissan_leaf/switch.py +++ b/homeassistant/components/nissan_leaf/switch.py @@ -4,14 +4,12 @@ from __future__ import annotations import logging from typing import Any -from pycarwings2.pycarwings2 import Leaf - from homeassistant.components.switch import SwitchEntity from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import LeafEntity +from . import LeafDataStore, LeafEntity from .const import DATA_CLIMATE, DATA_LEAF _LOGGER = logging.getLogger(__name__) @@ -38,7 +36,7 @@ def setup_platform( class LeafClimateSwitch(LeafEntity, SwitchEntity): """Nissan Leaf Climate Control switch.""" - def __init__(self, car: Leaf) -> None: + def __init__(self, car: LeafDataStore) -> None: """Set up climate control switch.""" super().__init__(car) self._attr_unique_id = f"{self.car.leaf.vin.lower()}_climatecontrol" From 982e75a15fa5ce33b272fde35906801933893996 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 21 Nov 2022 09:20:11 +0100 Subject: [PATCH 0593/1033] Improve type hints MQTT light schema template (#82211) * Improve type hints MQTT light schema template * A few improvements * Follow up comments --- .../components/mqtt/light/schema_template.py | 169 +++++++++--------- 1 file changed, 86 insertions(+), 83 deletions(-) diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 1085bcb5ef1..7e3c8a0a751 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -1,7 +1,9 @@ """Support for MQTT Template lights.""" from __future__ import annotations +from collections.abc import Callable import logging +from typing import Any import voluptuous as vol @@ -30,7 +32,7 @@ from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, TemplateVarsType import homeassistant.util.color as color_util from .. import subscription @@ -45,7 +47,13 @@ from ..const import ( ) from ..debug_info import log_messages from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity -from ..models import MqttValueTemplate +from ..models import ( + MqttCommandTemplate, + MqttValueTemplate, + PublishPayloadType, + ReceiveMessage, + ReceivePayloadType, +) from ..util import get_mqtt_data from .schema import MQTT_LIGHT_SCHEMA_SCHEMA from .schema_basic import MQTT_LIGHT_ATTRIBUTES_BLOCKED @@ -70,6 +78,17 @@ CONF_MIN_MIREDS = "min_mireds" CONF_RED_TEMPLATE = "red_template" CONF_WHITE_VALUE_TEMPLATE = "white_value_template" +COMMAND_TEMPLATES = (CONF_COMMAND_ON_TEMPLATE, CONF_COMMAND_OFF_TEMPLATE) +VALUE_TEMPLATES = ( + CONF_BLUE_TEMPLATE, + CONF_BRIGHTNESS_TEMPLATE, + CONF_COLOR_TEMP_TEMPLATE, + CONF_EFFECT_TEMPLATE, + CONF_GREEN_TEMPLATE, + CONF_RED_TEMPLATE, + CONF_STATE_TEMPLATE, +) + _PLATFORM_SCHEMA_BASE = ( MQTT_RW_SCHEMA.extend( { @@ -127,24 +146,30 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): _entity_id_format = ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED + _optimistic: bool + _command_templates: dict[ + str, Callable[[PublishPayloadType, TemplateVarsType], PublishPayloadType] + ] + _value_templates: dict[str, Callable[[ReceivePayloadType], ReceivePayloadType]] + _fixed_color_mode: ColorMode | str | None + _topics: dict[str, str | None] - def __init__(self, hass, config, config_entry, discovery_data): + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None, + ) -> None: """Initialize a MQTT Template light.""" - self._topics = None - self._templates = None - self._optimistic = False - - # features - self._fixed_color_mode = None - MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @staticmethod - def config_schema(): + def config_schema() -> vol.Schema: """Return the config schema.""" return DISCOVERY_SCHEMA_TEMPLATE - def _setup_from_config(self, config): + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" self._attr_max_mireds = config.get(CONF_MAX_MIREDS, super().max_mireds) self._attr_min_mireds = config.get(CONF_MIN_MIREDS, super().min_mireds) @@ -153,41 +178,37 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): self._topics = { key: config.get(key) for key in (CONF_STATE_TOPIC, CONF_COMMAND_TOPIC) } - self._templates = { - key: config.get(key) - for key in ( - CONF_BLUE_TEMPLATE, - CONF_BRIGHTNESS_TEMPLATE, - CONF_COLOR_TEMP_TEMPLATE, - CONF_COMMAND_OFF_TEMPLATE, - CONF_COMMAND_ON_TEMPLATE, - CONF_EFFECT_TEMPLATE, - CONF_GREEN_TEMPLATE, - CONF_RED_TEMPLATE, - CONF_STATE_TEMPLATE, - ) + self._command_templates = { + key: MqttCommandTemplate(config[key], entity=self).async_render + for key in COMMAND_TEMPLATES } - optimistic = config[CONF_OPTIMISTIC] + self._value_templates = { + key: MqttValueTemplate( + config.get(key), entity=self + ).async_render_with_possible_json_value + for key in VALUE_TEMPLATES + } + optimistic: bool = config[CONF_OPTIMISTIC] self._optimistic = ( optimistic or self._topics[CONF_STATE_TOPIC] is None - or self._templates[CONF_STATE_TEMPLATE] is None + or CONF_STATE_TEMPLATE not in self._config ) color_modes = {ColorMode.ONOFF} - if self._templates[CONF_BRIGHTNESS_TEMPLATE] is not None: + if CONF_BRIGHTNESS_TEMPLATE in config: color_modes.add(ColorMode.BRIGHTNESS) - if self._templates[CONF_COLOR_TEMP_TEMPLATE] is not None: + if CONF_COLOR_TEMP_TEMPLATE in config: color_modes.add(ColorMode.COLOR_TEMP) if ( - self._templates[CONF_RED_TEMPLATE] is not None - and self._templates[CONF_GREEN_TEMPLATE] is not None - and self._templates[CONF_BLUE_TEMPLATE] is not None + CONF_RED_TEMPLATE in config + and CONF_GREEN_TEMPLATE in config + and CONF_BLUE_TEMPLATE in config ): color_modes.add(ColorMode.HS) self._attr_supported_color_modes = filter_supported_color_modes(color_modes) self._fixed_color_mode = None - if len(self.supported_color_modes) == 1: + if self.supported_color_modes and len(self.supported_color_modes) == 1: self._fixed_color_mode = next(iter(self.supported_color_modes)) self._attr_color_mode = self._fixed_color_mode @@ -196,26 +217,21 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): features = features | LightEntityFeature.EFFECT self._attr_supported_features = features - def _update_color_mode(self): + def _update_color_mode(self) -> None: """Update the color_mode attribute.""" if self._fixed_color_mode: return # Support for ct + hs, prioritize hs self._attr_color_mode = ColorMode.HS if self.hs_color else ColorMode.COLOR_TEMP - def _prepare_subscribe_topics(self): + def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" - for tpl in self._templates.values(): - if tpl is not None: - tpl = MqttValueTemplate(tpl, entity=self) @callback @log_messages(self.hass, self.entity_id) - def state_received(msg): + def state_received(msg: ReceiveMessage) -> None: """Handle new MQTT messages.""" - state = self._templates[ - CONF_STATE_TEMPLATE - ].async_render_with_possible_json_value(msg.payload) + state = self._value_templates[CONF_STATE_TEMPLATE](msg.payload) if state == STATE_ON: self._attr_is_on = True elif state == STATE_OFF: @@ -225,21 +241,19 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): else: _LOGGER.warning("Invalid state value received") - if self._templates[CONF_BRIGHTNESS_TEMPLATE] is not None: + if CONF_BRIGHTNESS_TEMPLATE in self._config: try: self._attr_brightness = int( - self._templates[ - CONF_BRIGHTNESS_TEMPLATE - ].async_render_with_possible_json_value(msg.payload) + self._value_templates[CONF_BRIGHTNESS_TEMPLATE](msg.payload) ) except ValueError: _LOGGER.warning("Invalid brightness value received") - if self._templates[CONF_COLOR_TEMP_TEMPLATE] is not None: + if CONF_COLOR_TEMP_TEMPLATE in self._config: try: - color_temp = self._templates[ - CONF_COLOR_TEMP_TEMPLATE - ].async_render_with_possible_json_value(msg.payload) + color_temp = self._value_templates[CONF_COLOR_TEMP_TEMPLATE]( + msg.payload + ) self._attr_color_temp = ( int(color_temp) if color_temp != "None" else None ) @@ -247,20 +261,14 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): _LOGGER.warning("Invalid color temperature value received") if ( - self._templates[CONF_RED_TEMPLATE] is not None - and self._templates[CONF_GREEN_TEMPLATE] is not None - and self._templates[CONF_BLUE_TEMPLATE] is not None + CONF_RED_TEMPLATE in self._config + and CONF_GREEN_TEMPLATE in self._config + and CONF_BLUE_TEMPLATE in self._config ): try: - red = self._templates[ - CONF_RED_TEMPLATE - ].async_render_with_possible_json_value(msg.payload) - green = self._templates[ - CONF_GREEN_TEMPLATE - ].async_render_with_possible_json_value(msg.payload) - blue = self._templates[ - CONF_BLUE_TEMPLATE - ].async_render_with_possible_json_value(msg.payload) + red = self._value_templates[CONF_RED_TEMPLATE](msg.payload) + green = self._value_templates[CONF_GREEN_TEMPLATE](msg.payload) + blue = self._value_templates[CONF_BLUE_TEMPLATE](msg.payload) if red == "None" and green == "None" and blue == "None": self._attr_hs_color = None else: @@ -271,12 +279,11 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): except ValueError: _LOGGER.warning("Invalid color value received") - if self._templates[CONF_EFFECT_TEMPLATE] is not None: - effect = self._templates[ - CONF_EFFECT_TEMPLATE - ].async_render_with_possible_json_value(msg.payload) - - if effect in self._config.get(CONF_EFFECT_LIST): + if CONF_EFFECT_TEMPLATE in self._config: + effect = str(self._value_templates[CONF_EFFECT_TEMPLATE](msg.payload)) + if ( + effect_list := self._config[CONF_EFFECT_LIST] + ) and effect in effect_list: self._attr_effect = effect else: _LOGGER.warning("Unsupported effect value received") @@ -297,7 +304,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): }, ) - async def _subscribe_topics(self): + async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" await subscription.async_subscribe_topics(self.hass, self._sub_state) @@ -315,16 +322,16 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): self._attr_effect = last_state.attributes.get(ATTR_EFFECT) @property - def assumed_state(self): + def assumed_state(self) -> bool: """Return True if unable to access real state of the entity.""" return self._optimistic - async def async_turn_on(self, **kwargs): + async def async_turn_on(self, **kwargs: Any) -> None: """Turn the entity on. This method is a coroutine. """ - values = {"state": True} + values: dict[str, Any] = {"state": True} if self._optimistic: self._attr_is_on = True @@ -347,7 +354,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): # If there's a brightness topic set, we don't want to scale the RGB # values given using the brightness. - if self._templates[CONF_BRIGHTNESS_TEMPLATE] is not None: + if CONF_BRIGHTNESS_TEMPLATE in self._config: brightness = 255 else: brightness = kwargs.get( @@ -381,10 +388,8 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): values["transition"] = kwargs[ATTR_TRANSITION] await self.async_publish( - self._topics[CONF_COMMAND_TOPIC], - self._templates[CONF_COMMAND_ON_TEMPLATE].async_render( - parse_result=False, **values - ), + str(self._topics[CONF_COMMAND_TOPIC]), + self._command_templates[CONF_COMMAND_ON_TEMPLATE](None, values), self._config[CONF_QOS], self._config[CONF_RETAIN], self._config[CONF_ENCODING], @@ -393,12 +398,12 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): if self._optimistic: self.async_write_ha_state() - async def async_turn_off(self, **kwargs): + async def async_turn_off(self, **kwargs: Any) -> None: """Turn the entity off. This method is a coroutine. """ - values = {"state": False} + values: dict[str, Any] = {"state": False} if self._optimistic: self._attr_is_on = False @@ -406,10 +411,8 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): values["transition"] = kwargs[ATTR_TRANSITION] await self.async_publish( - self._topics[CONF_COMMAND_TOPIC], - self._templates[CONF_COMMAND_OFF_TEMPLATE].async_render( - parse_result=False, **values - ), + str(self._topics[CONF_COMMAND_TOPIC]), + self._command_templates[CONF_COMMAND_OFF_TEMPLATE](None, values), self._config[CONF_QOS], self._config[CONF_RETAIN], self._config[CONF_ENCODING], From c899e72029af78aa768a912c2d7ad87a9ab8ef3f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 21 Nov 2022 10:13:22 +0100 Subject: [PATCH 0594/1033] Code quality improvements for `dsmr` (#82443) --- homeassistant/components/dsmr/sensor.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/dsmr/sensor.py b/homeassistant/components/dsmr/sensor.py index aa01c798072..02a14601cc3 100644 --- a/homeassistant/components/dsmr/sensor.py +++ b/homeassistant/components/dsmr/sensor.py @@ -559,6 +559,7 @@ class DSMREntity(SensorEntity): @property def native_value(self) -> StateType: """Return the state of sensor, if available, translate if needed.""" + value: StateType if (value := self.get_dsmr_object_attr("value")) is None: return None @@ -573,10 +574,7 @@ class DSMREntity(SensorEntity): float(value), self._entry.data.get(CONF_PRECISION, DEFAULT_PRECISION) ) - if value is not None: - return value - - return None + return value @property def native_unit_of_measurement(self) -> str | None: From caac3d03c4862a779147614a659f7fc451b135d2 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 21 Nov 2022 11:46:18 +0100 Subject: [PATCH 0595/1033] Fix round typing [fritzbox] (#82434) --- homeassistant/components/fritzbox/light.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/fritzbox/light.py b/homeassistant/components/fritzbox/light.py index 5053eda69d7..03eb9eb9d78 100644 --- a/homeassistant/components/fritzbox/light.py +++ b/homeassistant/components/fritzbox/light.py @@ -1,7 +1,7 @@ """Support for AVM FRITZ!SmartHome lightbulbs.""" from __future__ import annotations -from typing import Any +from typing import Any, cast from requests.exceptions import HTTPError @@ -77,7 +77,7 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity): # Fritz!DECT 500 only supports 12 values for hue, with 3 saturations each. # Map supported colors to dict {hue: [sat1, sat2, sat3]} for easier lookup - self._supported_hs = {} + self._supported_hs: dict[int, list[int]] = {} for values in supported_colors.values(): hue = int(values[0][0]) self._supported_hs[hue] = [ @@ -138,7 +138,9 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity): try: # HA gives 0..360 for hue, fritz light only supports 0..359 unmapped_hue = int(kwargs[ATTR_HS_COLOR][0] % 360) - unmapped_saturation = round(kwargs[ATTR_HS_COLOR][1] * 255.0 / 100.0) + unmapped_saturation = round( + cast(float, kwargs[ATTR_HS_COLOR][1]) * 255.0 / 100.0 + ) await self.hass.async_add_executor_job( self.data.set_unmapped_color, (unmapped_hue, unmapped_saturation) ) From 4b0b2ecc0e2325e6f3cea140bc69f4b6f9d3ad3f Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 21 Nov 2022 14:55:32 +0100 Subject: [PATCH 0596/1033] Bump pychromecast to 13.0.1 (#82472) --- homeassistant/components/cast/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cast/manifest.json b/homeassistant/components/cast/manifest.json index d7cddaaa293..c5dc56a7da0 100644 --- a/homeassistant/components/cast/manifest.json +++ b/homeassistant/components/cast/manifest.json @@ -3,7 +3,7 @@ "name": "Google Cast", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/cast", - "requirements": ["pychromecast==12.1.4"], + "requirements": ["pychromecast==13.0.1"], "after_dependencies": [ "cloud", "http", diff --git a/requirements_all.txt b/requirements_all.txt index 0ac94b2f24a..340a1ddf790 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1501,7 +1501,7 @@ pycfdns==2.0.0 pychannels==1.2.3 # homeassistant.components.cast -pychromecast==12.1.4 +pychromecast==13.0.1 # homeassistant.components.pocketcasts pycketcasts==1.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d7ac04e5f16..ef6a7527f1a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1074,7 +1074,7 @@ pybravia==0.2.3 pycfdns==2.0.0 # homeassistant.components.cast -pychromecast==12.1.4 +pychromecast==13.0.1 # homeassistant.components.comfoconnect pycomfoconnect==0.4 From 13c03d022bf68349037c3b1fc15ec41c0a217ad2 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 21 Nov 2022 17:12:28 +0100 Subject: [PATCH 0597/1033] Fix suggested values in openuv config flow (#82479) * Fix suggested values in openuv config flow * Don't use new helper --- .../components/openuv/config_flow.py | 2 +- tests/components/openuv/test_config_flow.py | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/openuv/config_flow.py b/homeassistant/components/openuv/config_flow.py index 2e96ce7c292..8852ed677cd 100644 --- a/homeassistant/components/openuv/config_flow.py +++ b/homeassistant/components/openuv/config_flow.py @@ -200,7 +200,7 @@ class OpenUvOptionsFlowHandler(config_entries.OptionsFlow): CONF_TO_WINDOW, description={ "suggested_value": self.entry.options.get( - CONF_FROM_WINDOW, DEFAULT_TO_WINDOW + CONF_TO_WINDOW, DEFAULT_TO_WINDOW ) }, ): vol.Coerce(float), diff --git a/tests/components/openuv/test_config_flow.py b/tests/components/openuv/test_config_flow.py index eeafd82a20f..555d01e2624 100644 --- a/tests/components/openuv/test_config_flow.py +++ b/tests/components/openuv/test_config_flow.py @@ -2,6 +2,7 @@ from unittest.mock import patch from pyopenuv.errors import InvalidApiKeyError +import voluptuous as vol from homeassistant import data_entry_flow from homeassistant.components.openuv import CONF_FROM_WINDOW, CONF_TO_WINDOW, DOMAIN @@ -36,6 +37,13 @@ async def test_invalid_api_key(hass, config): assert result["errors"] == {CONF_API_KEY: "invalid_api_key"} +def _get_schema_marker(data_schema: vol.Schema, key: str) -> vol.Marker: + for k in data_schema.schema: + if k == key and isinstance(k, vol.Marker): + return k + return None + + async def test_options_flow(hass, config_entry): """Test config flow options.""" with patch("homeassistant.components.openuv.async_setup_entry", return_value=True): @@ -43,6 +51,13 @@ async def test_options_flow(hass, config_entry): result = await hass.config_entries.options.async_init(config_entry.entry_id) assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "init" + # Original schema uses defaults for suggested values + assert _get_schema_marker( + result["data_schema"], CONF_FROM_WINDOW + ).description == {"suggested_value": 3.5} + assert _get_schema_marker( + result["data_schema"], CONF_TO_WINDOW + ).description == {"suggested_value": 3.5} result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={CONF_FROM_WINDOW: 3.5, CONF_TO_WINDOW: 2.0} @@ -50,6 +65,17 @@ async def test_options_flow(hass, config_entry): assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert config_entry.options == {CONF_FROM_WINDOW: 3.5, CONF_TO_WINDOW: 2.0} + # Subsequent schema uses previous input for suggested values + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "init" + assert _get_schema_marker( + result["data_schema"], CONF_FROM_WINDOW + ).description == {"suggested_value": 3.5} + assert _get_schema_marker( + result["data_schema"], CONF_TO_WINDOW + ).description == {"suggested_value": 2.0} + async def test_step_reauth(hass, config, config_entry, setup_openuv): """Test that the reauth step works.""" From 91a44b697bec8e5bb69e0bbd3559ba0b7e4f4e5a Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Mon, 21 Nov 2022 18:55:06 +0200 Subject: [PATCH 0598/1033] Add tests coverage for Shelly number platform (#82480) --- .coveragerc | 1 - homeassistant/components/shelly/number.py | 4 - tests/components/shelly/__init__.py | 3 + tests/components/shelly/conftest.py | 5 +- tests/components/shelly/test_number.py | 152 ++++++++++++++++++++++ tests/components/shelly/test_sensor.py | 3 + 6 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 tests/components/shelly/test_number.py diff --git a/.coveragerc b/.coveragerc index 33e0763a5dd..413294573a0 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1111,7 +1111,6 @@ omit = homeassistant/components/seventeentrack/sensor.py homeassistant/components/shelly/climate.py homeassistant/components/shelly/coordinator.py - homeassistant/components/shelly/number.py homeassistant/components/shelly/utils.py homeassistant/components/shiftr/* homeassistant/components/shodan/sensor.py diff --git a/homeassistant/components/shelly/number.py b/homeassistant/components/shelly/number.py index bb7f17ea18d..7066f386355 100644 --- a/homeassistant/components/shelly/number.py +++ b/homeassistant/components/shelly/number.py @@ -25,7 +25,6 @@ from .entity import ( ShellySleepingBlockAttributeEntity, async_setup_entry_attribute_entities, ) -from .utils import get_device_entry_gen @dataclass @@ -77,9 +76,6 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up numbers for device.""" - if get_device_entry_gen(config_entry) == 2: - return - if config_entry.data[CONF_SLEEP_PERIOD]: async_setup_entry_attribute_entities( hass, diff --git a/tests/components/shelly/__init__.py b/tests/components/shelly/__init__.py index 3adc1eaf49a..36a43984c4a 100644 --- a/tests/components/shelly/__init__.py +++ b/tests/components/shelly/__init__.py @@ -1,6 +1,7 @@ """Tests for the Shelly integration.""" from __future__ import annotations +from collections.abc import Mapping from copy import deepcopy from datetime import timedelta from typing import Any @@ -99,6 +100,7 @@ def register_entity( object_id: str, unique_id: str, config_entry: ConfigEntry | None = None, + capabilities: Mapping[str, Any] | None = None, ) -> str: """Register enabled entity, return entity_id.""" entity_registry = async_get(hass) @@ -109,6 +111,7 @@ def register_entity( suggested_object_id=object_id, disabled_by=None, config_entry=config_entry, + capabilities=capabilities, ) return f"{domain}.{object_id}" diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py index 2f940530319..26931ea804e 100644 --- a/tests/components/shelly/conftest.py +++ b/tests/components/shelly/conftest.py @@ -30,6 +30,7 @@ MOCK_SETTINGS = { "relays": [{"btn_type": "momentary"}, {"btn_type": "toggle"}], "rollers": [{"positioning": True}], "external_power": 0, + "thermostats": [{"schedule_profile_names": {}}], } @@ -106,9 +107,11 @@ MOCK_BLOCKS = [ type="sensor", ), Mock( - sensor_ids={"battery": 98}, + sensor_ids={"battery": 98, "valvePos": 50}, + channel="0", battery=98, cfgChanged=0, + valvePos=50, description="device_0", type="device", ), diff --git a/tests/components/shelly/test_number.py b/tests/components/shelly/test_number.py new file mode 100644 index 00000000000..69b1105fef5 --- /dev/null +++ b/tests/components/shelly/test_number.py @@ -0,0 +1,152 @@ +"""Tests for Shelly number platform.""" +from unittest.mock import AsyncMock + +from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError +import pytest + +from homeassistant.components.number import ( + ATTR_VALUE, + DOMAIN as NUMBER_DOMAIN, + SERVICE_SET_VALUE, +) +from homeassistant.components.shelly.const import DOMAIN +from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import State +from homeassistant.exceptions import HomeAssistantError + +from . import init_integration, register_device, register_entity + +from tests.common import mock_restore_cache + +DEVICE_BLOCK_ID = 4 + + +async def test_block_number_update(hass, mock_block_device, monkeypatch): + """Test block device number update.""" + await init_integration(hass, 1, sleep_period=1000) + + assert hass.states.get("number.test_name_valve_position") is None + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get("number.test_name_valve_position").state == "50" + + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valvePos", 30) + mock_block_device.mock_update() + + assert hass.states.get("number.test_name_valve_position").state == "30" + + +async def test_block_restored_number(hass, mock_block_device, device_reg, monkeypatch): + """Test block restored number.""" + entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True) + register_device(device_reg, entry) + capabilities = { + "min": 0, + "max": 100, + "step": 1, + "mode": "slider", + } + entity_id = register_entity( + hass, + NUMBER_DOMAIN, + "test_name_valve_position", + "device_0-valvePos", + entry, + capabilities, + ) + mock_restore_cache(hass, [State(entity_id, "40")]) + + monkeypatch.setattr(mock_block_device, "initialized", False) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == "40" + + # Make device online + monkeypatch.setattr(mock_block_device, "initialized", True) + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == "50" + + +async def test_block_number_set_value(hass, mock_block_device): + """Test block device number set value.""" + await init_integration(hass, 1, sleep_period=1000) + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + mock_block_device.reset_mock() + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: "number.test_name_valve_position", ATTR_VALUE: 30}, + blocking=True, + ) + mock_block_device.http_request.assert_called_once_with( + "get", "thermostat/0", {"pos": 30.0} + ) + + +async def test_block_set_value_connection_error(hass, mock_block_device, monkeypatch): + """Test block device set value connection error.""" + monkeypatch.setattr( + mock_block_device, + "http_request", + AsyncMock(side_effect=DeviceConnectionError), + ) + await init_integration(hass, 1, sleep_period=1000) + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: "number.test_name_valve_position", ATTR_VALUE: 30}, + blocking=True, + ) + + +async def test_block_set_value_auth_error(hass, mock_block_device, monkeypatch): + """Test block device set value authentication error.""" + monkeypatch.setattr( + mock_block_device, + "http_request", + AsyncMock(side_effect=InvalidAuthError), + ) + entry = await init_integration(hass, 1, sleep_period=1000) + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert entry.state == ConfigEntryState.LOADED + + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: "number.test_name_valve_position", ATTR_VALUE: 30}, + blocking=True, + ) + + assert entry.state == ConfigEntryState.LOADED + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + + flow = flows[0] + assert flow.get("step_id") == "reauth_confirm" + assert flow.get("handler") == DOMAIN + + assert "context" in flow + assert flow["context"].get("source") == SOURCE_REAUTH + assert flow["context"].get("entry_id") == entry.entry_id diff --git a/tests/components/shelly/test_sensor.py b/tests/components/shelly/test_sensor.py index 89fa138349f..6dcd903219f 100644 --- a/tests/components/shelly/test_sensor.py +++ b/tests/components/shelly/test_sensor.py @@ -50,6 +50,9 @@ async def test_block_rest_sensor(hass, mock_block_device, monkeypatch): async def test_block_sleeping_sensor(hass, mock_block_device, monkeypatch): """Test block sleeping sensor.""" + monkeypatch.setattr( + mock_block_device.blocks[DEVICE_BLOCK_ID], "sensor_ids", {"battery": 98} + ) entity_id = f"{SENSOR_DOMAIN}.test_name_temperature" await init_integration(hass, 1, sleep_period=1000) From 0337b5d9752fca8d3e3e736106b0ce52e881188c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 21 Nov 2022 18:07:30 +0100 Subject: [PATCH 0599/1033] Fix round typing [isy994] (#82435) --- homeassistant/components/isy994/cover.py | 4 ++-- homeassistant/components/isy994/light.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/isy994/cover.py b/homeassistant/components/isy994/cover.py index 7c82ea2459a..60027a31c89 100644 --- a/homeassistant/components/isy994/cover.py +++ b/homeassistant/components/isy994/cover.py @@ -1,7 +1,7 @@ """Support for ISY994 covers.""" from __future__ import annotations -from typing import Any +from typing import Any, cast from pyisy.constants import ISY_VALUE_UNKNOWN @@ -58,7 +58,7 @@ class ISYCoverEntity(ISYNodeEntity, CoverEntity): if self._node.status == ISY_VALUE_UNKNOWN: return None if self._node.uom == UOM_8_BIT_RANGE: - return round(self._node.status * 100.0 / 255.0) + return round(cast(float, self._node.status) * 100.0 / 255.0) return int(sorted((0, self._node.status, 100))[1]) @property diff --git a/homeassistant/components/isy994/light.py b/homeassistant/components/isy994/light.py index 4606ef7e8de..6e67ed32938 100644 --- a/homeassistant/components/isy994/light.py +++ b/homeassistant/components/isy994/light.py @@ -1,7 +1,7 @@ """Support for ISY994 lights.""" from __future__ import annotations -from typing import Any +from typing import Any, cast from pyisy.constants import ISY_VALUE_UNKNOWN from pyisy.helpers import NodeProperty @@ -70,7 +70,7 @@ class ISYLightEntity(ISYNodeEntity, LightEntity, RestoreEntity): return None # Special Case for ISY Z-Wave Devices using % instead of 0-255: if self._node.uom == UOM_PERCENTAGE: - return round(self._node.status * 255.0 / 100.0) + return round(cast(float, self._node.status) * 255.0 / 100.0) return int(self._node.status) async def async_turn_off(self, **kwargs: Any) -> None: From a88e865dffa611d04cd4672b7c09ab523f03b46d Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 21 Nov 2022 18:59:08 +0100 Subject: [PATCH 0600/1033] Fix round typing [tplink] (#82437) --- homeassistant/components/tplink/light.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/tplink/light.py b/homeassistant/components/tplink/light.py index 65967eb6bea..d0c056d7c17 100644 --- a/homeassistant/components/tplink/light.py +++ b/homeassistant/components/tplink/light.py @@ -269,7 +269,7 @@ class TPLinkSmartBulb(CoordinatedTPLinkEntity, LightEntity): @property def brightness(self) -> int | None: """Return the brightness of this light between 0..255.""" - return round((self.device.brightness * 255.0) / 100.0) + return round((cast(int, self.device.brightness) * 255.0) / 100.0) @property def hs_color(self) -> tuple[int, int] | None: From 848821139d5ba4146dd2e41d21d36529ba8d36bd Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 21 Nov 2022 19:00:34 +0100 Subject: [PATCH 0601/1033] Fix picnic typing (#82476) --- homeassistant/components/picnic/services.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/picnic/services.py b/homeassistant/components/picnic/services.py index 565905c76fe..3af2a521f8a 100644 --- a/homeassistant/components/picnic/services.py +++ b/homeassistant/components/picnic/services.py @@ -1,6 +1,8 @@ """Services for the Picnic integration.""" from __future__ import annotations +from typing import cast + from python_picnic_api import PicnicAPI import voluptuous as vol @@ -64,7 +66,7 @@ async def handle_add_product( product_id = call.data.get("product_id") if not product_id: product_id = await hass.async_add_executor_job( - _product_search, api_client, call.data.get("product_name") + _product_search, api_client, cast(str, call.data["product_name"]) ) if not product_id: From b3dd59f20219f058053a82504948e80040eca731 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Mon, 21 Nov 2022 21:39:39 +0100 Subject: [PATCH 0602/1033] Add config flow to Scrape (#81193) * Scrape take 2 * cleanup * new entity name * Fix name, add tests * Use FlowResultType * Add test abort * hassfest * Remove not needed test * clean * Remove config entry and implement datacoordinator * fix codeowners * fix codeowners * codeowners reset * Fix coordinator * Remove test config_flow * Fix tests * hassfest * reset config flow * reset strings * reset sensor * Reconfig * Fix tests * coverage * Remove coverage * Remove print * Add config flow * Fix config flow * Add back init * Add entry to sensor * float to int * Fix SelectSelector * Add tests for config entry to existing * Test config flow * Fix test reload * Fix rebase * Fix strings * clean init * Clean test_sensor * Align sensor setup entry * Add error to strings * review changes * clean tests * Add back options flow * review changes * update_listener * Add tests * Remove duplicate abort * strings * sensors to sensor * review changes * more review changes * clarify test payload * fixture name --- homeassistant/components/scrape/__init__.py | 37 +++- .../components/scrape/config_flow.py | 90 +++++--- homeassistant/components/scrape/manifest.json | 3 +- homeassistant/components/scrape/sensor.py | 40 ++++ homeassistant/components/scrape/strings.json | 62 +++--- .../components/scrape/translations/en.json | 56 ++--- homeassistant/generated/config_flows.py | 1 + homeassistant/generated/integrations.json | 2 +- tests/components/scrape/__init__.py | 9 + tests/components/scrape/conftest.py | 80 +++++++ tests/components/scrape/test_config_flow.py | 206 ++++++++++++++++++ tests/components/scrape/test_init.py | 18 +- tests/components/scrape/test_sensor.py | 11 +- 13 files changed, 528 insertions(+), 87 deletions(-) create mode 100644 tests/components/scrape/conftest.py create mode 100644 tests/components/scrape/test_config_flow.py diff --git a/homeassistant/components/scrape/__init__.py b/homeassistant/components/scrape/__init__.py index 5bc865dbb6c..cb5657a9649 100644 --- a/homeassistant/components/scrape/__init__.py +++ b/homeassistant/components/scrape/__init__.py @@ -10,6 +10,7 @@ import voluptuous as vol from homeassistant.components.rest import RESOURCE_SCHEMA, create_rest_data_from_config from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_ATTRIBUTE, CONF_SCAN_INTERVAL, @@ -22,7 +23,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.template_entity import TEMPLATE_SENSOR_BASE_SCHEMA from homeassistant.helpers.typing import ConfigType -from .const import CONF_INDEX, CONF_SELECT, DEFAULT_SCAN_INTERVAL, DOMAIN +from .const import CONF_INDEX, CONF_SELECT, DEFAULT_SCAN_INTERVAL, DOMAIN, PLATFORMS from .coordinator import ScrapeCoordinator SENSOR_SCHEMA = vol.Schema( @@ -79,3 +80,37 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: await asyncio.gather(*load_coroutines) return True + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Scrape from a config entry.""" + + rest_config: dict[str, Any] = COMBINED_SCHEMA(dict(entry.options)) + rest = create_rest_data_from_config(hass, rest_config) + + coordinator = ScrapeCoordinator( + hass, + rest, + DEFAULT_SCAN_INTERVAL, + ) + await coordinator.async_config_entry_first_refresh() + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator + + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + entry.async_on_unload(entry.add_update_listener(update_listener)) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload Scrape config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + del hass.data[DOMAIN][entry.entry_id] + if not hass.data[DOMAIN]: + del hass.data[DOMAIN] + return unload_ok + + +async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Handle options update.""" + await hass.config_entries.async_reload(entry.entry_id) diff --git a/homeassistant/components/scrape/config_flow.py b/homeassistant/components/scrape/config_flow.py index a32e371a487..034667a4a76 100644 --- a/homeassistant/components/scrape/config_flow.py +++ b/homeassistant/components/scrape/config_flow.py @@ -6,6 +6,9 @@ from typing import Any import voluptuous as vol +from homeassistant.components.rest import create_rest_data_from_config +from homeassistant.components.rest.data import DEFAULT_TIMEOUT +from homeassistant.components.rest.schema import DEFAULT_METHOD, METHODS from homeassistant.components.sensor import ( CONF_STATE_CLASS, SensorDeviceClass, @@ -16,18 +19,23 @@ from homeassistant.const import ( CONF_AUTHENTICATION, CONF_DEVICE_CLASS, CONF_HEADERS, + CONF_METHOD, CONF_NAME, CONF_PASSWORD, CONF_RESOURCE, + CONF_TIMEOUT, CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME, CONF_VALUE_TEMPLATE, CONF_VERIFY_SSL, HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION, + UnitOfTemperature, ) +from homeassistant.core import async_get_hass from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, + SchemaFlowError, SchemaFlowFormStep, SchemaFlowMenuStep, SchemaOptionsFlowHandler, @@ -47,20 +55,15 @@ from homeassistant.helpers.selector import ( TextSelectorType, ) +from . import COMBINED_SCHEMA from .const import CONF_INDEX, CONF_SELECT, DEFAULT_NAME, DEFAULT_VERIFY_SSL, DOMAIN -SCHEMA_SETUP = { - vol.Optional(CONF_NAME, default=DEFAULT_NAME): TextSelector(), +RESOURCE_SETUP = { vol.Required(CONF_RESOURCE): TextSelector( TextSelectorConfig(type=TextSelectorType.URL) ), - vol.Required(CONF_SELECT): TextSelector(), -} - -SCHEMA_OPT = { - vol.Optional(CONF_ATTRIBUTE): TextSelector(), - vol.Optional(CONF_INDEX, default=0): NumberSelector( - NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX) + vol.Optional(CONF_METHOD, default=DEFAULT_METHOD): SelectSelector( + SelectSelectorConfig(options=METHODS, mode=SelectSelectorMode.DROPDOWN) ), vol.Optional(CONF_AUTHENTICATION): SelectSelector( SelectSelectorConfig( @@ -73,32 +76,74 @@ SCHEMA_OPT = { TextSelectorConfig(type=TextSelectorType.PASSWORD) ), vol.Optional(CONF_HEADERS): ObjectSelector(), - vol.Optional(CONF_UNIT_OF_MEASUREMENT): TextSelector(), + vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): BooleanSelector(), + vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): NumberSelector( + NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX) + ), +} + +SENSOR_SETUP = { + vol.Optional(CONF_NAME, default=DEFAULT_NAME): TextSelector(), + vol.Required(CONF_SELECT): TextSelector(), + vol.Optional(CONF_INDEX, default=0): NumberSelector( + NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX) + ), + vol.Optional(CONF_ATTRIBUTE): TextSelector(), + vol.Optional(CONF_VALUE_TEMPLATE): TemplateSelector(), vol.Optional(CONF_DEVICE_CLASS): SelectSelector( SelectSelectorConfig( - options=[e.value for e in SensorDeviceClass], + options=[cls.value for cls in SensorDeviceClass], mode=SelectSelectorMode.DROPDOWN, ) ), vol.Optional(CONF_STATE_CLASS): SelectSelector( SelectSelectorConfig( - options=[e.value for e in SensorStateClass], + options=[cls.value for cls in SensorStateClass], + mode=SelectSelectorMode.DROPDOWN, + ) + ), + vol.Optional(CONF_UNIT_OF_MEASUREMENT): SelectSelector( + SelectSelectorConfig( + options=[cls.value for cls in UnitOfTemperature], + custom_value=True, mode=SelectSelectorMode.DROPDOWN, ) ), - vol.Optional(CONF_VALUE_TEMPLATE): TemplateSelector(), - vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): BooleanSelector(), } -DATA_SCHEMA = vol.Schema({**SCHEMA_SETUP, **SCHEMA_OPT}) -DATA_SCHEMA_OPT = vol.Schema({**SCHEMA_OPT}) + +def validate_rest_setup(user_input: dict[str, Any]) -> dict[str, Any]: + """Validate rest setup.""" + hass = async_get_hass() + rest_config: dict[str, Any] = COMBINED_SCHEMA(user_input) + try: + create_rest_data_from_config(hass, rest_config) + except Exception as err: + raise SchemaFlowError("resource_error") from err + return user_input + + +def validate_sensor_setup(user_input: dict[str, Any]) -> dict[str, Any]: + """Validate sensor setup.""" + return {"sensor": [{**user_input, CONF_INDEX: int(user_input[CONF_INDEX])}]} + + +DATA_SCHEMA_RESOURCE = vol.Schema(RESOURCE_SETUP) +DATA_SCHEMA_SENSOR = vol.Schema(SENSOR_SETUP) CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "user": SchemaFlowFormStep(DATA_SCHEMA), - "import": SchemaFlowFormStep(DATA_SCHEMA), + "user": SchemaFlowFormStep( + schema=DATA_SCHEMA_RESOURCE, + next_step=lambda _: "sensor", + validate_user_input=validate_rest_setup, + ), + "sensor": SchemaFlowFormStep( + schema=DATA_SCHEMA_SENSOR, + validate_user_input=validate_sensor_setup, + ), } OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "init": SchemaFlowFormStep(DATA_SCHEMA_OPT), + "init": SchemaFlowFormStep(DATA_SCHEMA_RESOURCE), } @@ -110,12 +155,7 @@ class ScrapeConfigFlowHandler(SchemaConfigFlowHandler, domain=DOMAIN): def async_config_entry_title(self, options: Mapping[str, Any]) -> str: """Return config entry title.""" - return options[CONF_NAME] - - def async_config_flow_finished(self, options: Mapping[str, Any]) -> None: - """Check for duplicate records.""" - data: dict[str, Any] = dict(options) - self._async_abort_entries_match(data) + return options[CONF_RESOURCE] class ScrapeOptionsFlowHandler(SchemaOptionsFlowHandler): diff --git a/homeassistant/components/scrape/manifest.json b/homeassistant/components/scrape/manifest.json index b7e5660e381..86319ce3744 100644 --- a/homeassistant/components/scrape/manifest.json +++ b/homeassistant/components/scrape/manifest.json @@ -5,5 +5,6 @@ "requirements": ["beautifulsoup4==4.11.1", "lxml==4.9.1"], "after_dependencies": ["rest"], "codeowners": ["@fabaff", "@gjohansson-ST", "@epenet"], - "iot_class": "cloud_polling" + "iot_class": "cloud_polling", + "config_flow": true } diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index 61f0b9711b1..10b96716929 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -14,6 +14,7 @@ from homeassistant.components.sensor import ( PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA, STATE_CLASSES_SCHEMA, ) +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_ATTRIBUTE, CONF_AUTHENTICATION, @@ -144,6 +145,45 @@ async def async_setup_platform( async_add_entities(entities) +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up the Scrape sensor entry.""" + entities: list = [] + + coordinator: ScrapeCoordinator = hass.data[DOMAIN][entry.entry_id] + config = dict(entry.options) + for sensor in config["sensor"]: + sensor_config: ConfigType = vol.Schema( + TEMPLATE_SENSOR_BASE_SCHEMA.schema, extra=vol.ALLOW_EXTRA + )(sensor) + + name: str = sensor_config[CONF_NAME] + select: str = sensor_config[CONF_SELECT] + attr: str | None = sensor_config.get(CONF_ATTRIBUTE) + index: int = int(sensor_config[CONF_INDEX]) + value_string: str | None = sensor_config.get(CONF_VALUE_TEMPLATE) + + value_template: Template | None = ( + Template(value_string, hass) if value_string is not None else None + ) + entities.append( + ScrapeSensor( + hass, + coordinator, + sensor_config, + name, + None, + select, + attr, + index, + value_template, + ) + ) + + async_add_entities(entities) + + class ScrapeSensor(CoordinatorEntity[ScrapeCoordinator], TemplateSensor): """Representation of a web scrape sensor.""" diff --git a/homeassistant/components/scrape/strings.json b/homeassistant/components/scrape/strings.json index 11c17a176a3..391bfe5ad9f 100644 --- a/homeassistant/components/scrape/strings.json +++ b/homeassistant/components/scrape/strings.json @@ -3,35 +3,48 @@ "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_account%]" }, + "error": { + "resource_error": "Could not update rest data. Verify your configuration" + }, "step": { "user": { "data": { - "name": "[%key:common::config_flow::data::name%]", "resource": "Resource", - "select": "Select", - "attribute": "Attribute", - "index": "Index", - "value_template": "Value Template", - "unit_of_measurement": "Unit of Measurement", - "device_class": "Device Class", - "state_class": "State Class", - "authentication": "Authentication", + "authentication": "Select authentication method", "verify_ssl": "[%key:common::config_flow::data::verify_ssl%]", "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]", - "headers": "Headers" + "headers": "Headers", + "method": "Method", + "timeout": "Timeout" }, "data_description": { "resource": "The URL to the website that contains the value", + "authentication": "Type of the HTTP authentication. Either basic or digest", + "verify_ssl": "Enables/disables verification of SSL/TLS certificate, for example if it is self-signed", + "headers": "Headers to use for the web request", + "timeout": "Timeout for connection to website" + } + }, + "sensor": { + "data": { + "name": "[%key:common::config_flow::data::name%]", + "attribute": "Attribute", + "index": "Index", + "select": "Select", + "value_template": "Value Template", + "device_class": "Device Class", + "state_class": "State Class", + "unit_of_measurement": "Unit of Measurement" + }, + "data_description": { "select": "Defines what tag to search for. Check Beautifulsoup CSS selectors for details", "attribute": "Get value of an attribute on the selected tag", "index": "Defines which of the elements returned by the CSS selector to use", "value_template": "Defines a template to get the state of the sensor", "device_class": "The type/class of the sensor to set the icon in the frontend", "state_class": "The state_class of the sensor", - "authentication": "Type of the HTTP authentication. Either basic or digest", - "verify_ssl": "Enables/disables verification of SSL/TLS certificate, for example if it is self-signed", - "headers": "Headers to use for the web request" + "unit_of_measurement": "Choose temperature measurement or create your own" } } } @@ -40,32 +53,21 @@ "step": { "init": { "data": { - "name": "[%key:component::scrape::config::step::user::data::name%]", "resource": "[%key:component::scrape::config::step::user::data::resource%]", - "select": "[%key:component::scrape::config::step::user::data::select%]", - "attribute": "[%key:component::scrape::config::step::user::data::attribute%]", - "index": "[%key:component::scrape::config::step::user::data::index%]", - "value_template": "[%key:component::scrape::config::step::user::data::value_template%]", - "unit_of_measurement": "[%key:component::scrape::config::step::user::data::unit_of_measurement%]", - "device_class": "[%key:component::scrape::config::step::user::data::device_class%]", - "state_class": "[%key:component::scrape::config::step::user::data::state_class%]", + "method": "[%key:component::scrape::config::step::user::data::method%]", "authentication": "[%key:component::scrape::config::step::user::data::authentication%]", - "verify_ssl": "[%key:component::scrape::config::step::user::data::verify_ssl%]", "username": "[%key:component::scrape::config::step::user::data::username%]", "password": "[%key:component::scrape::config::step::user::data::password%]", - "headers": "[%key:component::scrape::config::step::user::data::headers%]" + "headers": "[%key:component::scrape::config::step::user::data::headers%]", + "verify_ssl": "[%key:component::scrape::config::step::user::data::verify_ssl%]", + "timeout": "[%key:component::scrape::config::step::user::data::timeout%]" }, "data_description": { "resource": "[%key:component::scrape::config::step::user::data_description::resource%]", - "select": "[%key:component::scrape::config::step::user::data_description::select%]", - "attribute": "[%key:component::scrape::config::step::user::data_description::attribute%]", - "index": "[%key:component::scrape::config::step::user::data_description::index%]", - "value_template": "[%key:component::scrape::config::step::user::data_description::value_template%]", - "device_class": "[%key:component::scrape::config::step::user::data_description::device_class%]", - "state_class": "[%key:component::scrape::config::step::user::data_description::state_class%]", "authentication": "[%key:component::scrape::config::step::user::data_description::authentication%]", + "headers": "[%key:component::scrape::config::step::user::data_description::headers%]", "verify_ssl": "[%key:component::scrape::config::step::user::data_description::verify_ssl%]", - "headers": "[%key:component::scrape::config::step::user::data_description::headers%]" + "timeout": "[%key:component::scrape::config::step::user::data_description::timeout%]" } } } diff --git a/homeassistant/components/scrape/translations/en.json b/homeassistant/components/scrape/translations/en.json index b64310f0251..8cc642c12a2 100644 --- a/homeassistant/components/scrape/translations/en.json +++ b/homeassistant/components/scrape/translations/en.json @@ -3,34 +3,47 @@ "abort": { "already_configured": "Account is already configured" }, + "error": { + "resource_error": "Could not update rest data. Verify your configuration" + }, "step": { - "user": { + "sensor": { "data": { "attribute": "Attribute", - "authentication": "Authentication", "device_class": "Device Class", - "headers": "Headers", "index": "Index", "name": "Name", - "password": "Password", - "resource": "Resource", "select": "Select", "state_class": "State Class", "unit_of_measurement": "Unit of Measurement", - "username": "Username", - "value_template": "Value Template", - "verify_ssl": "Verify SSL certificate" + "value_template": "Value Template" }, "data_description": { "attribute": "Get value of an attribute on the selected tag", - "authentication": "Type of the HTTP authentication. Either basic or digest", "device_class": "The type/class of the sensor to set the icon in the frontend", - "headers": "Headers to use for the web request", "index": "Defines which of the elements returned by the CSS selector to use", - "resource": "The URL to the website that contains the value", "select": "Defines what tag to search for. Check Beautifulsoup CSS selectors for details", "state_class": "The state_class of the sensor", - "value_template": "Defines a template to get the state of the sensor", + "unit_of_measurement": "Choose temperature measurement or create your own", + "value_template": "Defines a template to get the state of the sensor" + } + }, + "user": { + "data": { + "authentication": "Select authentication method", + "headers": "Headers", + "method": "Method", + "password": "Password", + "resource": "Resource", + "timeout": "Timeout", + "username": "Username", + "verify_ssl": "Verify SSL certificate" + }, + "data_description": { + "authentication": "Type of the HTTP authentication. Either basic or digest", + "headers": "Headers to use for the web request", + "resource": "The URL to the website that contains the value", + "timeout": "Timeout for connection to website", "verify_ssl": "Enables/disables verification of SSL/TLS certificate, for example if it is self-signed" } } @@ -46,31 +59,20 @@ "step": { "init": { "data": { - "attribute": "Attribute", - "authentication": "Authentication", - "device_class": "Device Class", + "authentication": "Select authentication method", "headers": "Headers", - "index": "Index", - "name": "Name", + "method": "Method", "password": "Password", "resource": "Resource", - "select": "Select", - "state_class": "State Class", - "unit_of_measurement": "Unit of Measurement", + "timeout": "Timeout", "username": "Username", - "value_template": "Value Template", "verify_ssl": "Verify SSL certificate" }, "data_description": { - "attribute": "Get value of an attribute on the selected tag", "authentication": "Type of the HTTP authentication. Either basic or digest", - "device_class": "The type/class of the sensor to set the icon in the frontend", "headers": "Headers to use for the web request", - "index": "Defines which of the elements returned by the CSS selector to use", "resource": "The URL to the website that contains the value", - "select": "Defines what tag to search for. Check Beautifulsoup CSS selectors for details", - "state_class": "The state_class of the sensor", - "value_template": "Defines a template to get the state of the sensor", + "timeout": "Timeout for connection to website", "verify_ssl": "Enables/disables verification of SSL/TLS certificate, for example if it is self-signed" } } diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index e3519ac8773..33579c6b5ee 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -347,6 +347,7 @@ FLOWS = { "ruuvitag_ble", "sabnzbd", "samsungtv", + "scrape", "screenlogic", "season", "sense", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 69911965eb4..0eb5ed5fc6f 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -4597,7 +4597,7 @@ "scrape": { "name": "Scrape", "integration_type": "hub", - "config_flow": false, + "config_flow": true, "iot_class": "cloud_polling" }, "screenaway": { diff --git a/tests/components/scrape/__init__.py b/tests/components/scrape/__init__.py index d4eac856d7d..b13cc4a7326 100644 --- a/tests/components/scrape/__init__.py +++ b/tests/components/scrape/__init__.py @@ -98,11 +98,20 @@ class MockRestData: self.count += 1 if self.payload == "test_scrape_sensor": self.data = ( + # Default "
" "

Current Version: 2021.12.10

Released: January 17, 2022" "
" "" ) + if self.payload == "test_scrape_sensor2": + self.data = ( + # Hidden version + "
" + "

Hidden Version: 2021.12.10

Released: January 17, 2022" + "
" + "" + ) if self.payload == "test_scrape_uom_and_classes": self.data = ( "
" diff --git a/tests/components/scrape/conftest.py b/tests/components/scrape/conftest.py new file mode 100644 index 00000000000..c3b63cbb6c1 --- /dev/null +++ b/tests/components/scrape/conftest.py @@ -0,0 +1,80 @@ +"""Fixtures for the Scrape integration.""" +from __future__ import annotations + +from typing import Any +from unittest.mock import patch + +import pytest + +from homeassistant.components.rest.data import DEFAULT_TIMEOUT +from homeassistant.components.rest.schema import DEFAULT_METHOD, DEFAULT_VERIFY_SSL +from homeassistant.components.scrape.const import CONF_INDEX, CONF_SELECT, DOMAIN +from homeassistant.config_entries import SOURCE_USER +from homeassistant.const import ( + CONF_METHOD, + CONF_NAME, + CONF_RESOURCE, + CONF_TIMEOUT, + CONF_VERIFY_SSL, +) +from homeassistant.core import HomeAssistant + +from . import MockRestData + +from tests.common import MockConfigEntry + + +@pytest.fixture(name="get_config") +async def get_config_to_integration_load() -> dict[str, Any]: + """Return default minimal configuration. + + To override the config, tests can be marked with: + @pytest.mark.parametrize("get_config", [{...}]) + """ + return { + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: DEFAULT_METHOD, + CONF_VERIFY_SSL: DEFAULT_VERIFY_SSL, + CONF_TIMEOUT: DEFAULT_TIMEOUT, + "sensor": [ + { + CONF_NAME: "Current version", + CONF_SELECT: ".current-version h1", + CONF_INDEX: 0, + } + ], + } + + +@pytest.fixture(name="get_data") +async def get_data_to_integration_load() -> MockRestData: + """Return RestData. + + To override the config, tests can be marked with: + @pytest.mark.parametrize("get_data", [{...}]) + """ + return MockRestData("test_scrape_sensor") + + +@pytest.fixture(name="loaded_entry") +async def load_integration( + hass: HomeAssistant, get_config: dict[str, Any], get_data: MockRestData +) -> MockConfigEntry: + """Set up the Scrape integration in Home Assistant.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + source=SOURCE_USER, + options=get_config, + entry_id="1", + ) + + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.rest.RestData", + return_value=get_data, + ): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + return config_entry diff --git a/tests/components/scrape/test_config_flow.py b/tests/components/scrape/test_config_flow.py new file mode 100644 index 00000000000..b8a84fd73e9 --- /dev/null +++ b/tests/components/scrape/test_config_flow.py @@ -0,0 +1,206 @@ +"""Test the Scrape config flow.""" +from __future__ import annotations + +from unittest.mock import patch + +from homeassistant import config_entries +from homeassistant.components.rest.data import DEFAULT_TIMEOUT +from homeassistant.components.rest.schema import DEFAULT_METHOD +from homeassistant.components.scrape import DOMAIN +from homeassistant.components.scrape.const import ( + CONF_INDEX, + CONF_SELECT, + DEFAULT_VERIFY_SSL, +) +from homeassistant.const import ( + CONF_METHOD, + CONF_NAME, + CONF_PASSWORD, + CONF_RESOURCE, + CONF_TIMEOUT, + CONF_USERNAME, + CONF_VERIFY_SSL, +) +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType +from homeassistant.exceptions import HomeAssistantError + +from . import MockRestData + +from tests.common import MockConfigEntry + + +async def test_form(hass: HomeAssistant, get_data: MockRestData) -> None: + """Test we get the form.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["step_id"] == "user" + assert result["type"] == FlowResultType.FORM + + with patch( + "homeassistant.components.rest.RestData", + return_value=get_data, + ) as mock_data, patch( + "homeassistant.components.scrape.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: "GET", + CONF_VERIFY_SSL: True, + CONF_TIMEOUT: 10.0, + }, + ) + await hass.async_block_till_done() + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + { + CONF_NAME: "Current version", + CONF_SELECT: ".current-version h1", + CONF_INDEX: 0.0, + }, + ) + await hass.async_block_till_done() + + assert result3["type"] == FlowResultType.CREATE_ENTRY + assert result3["version"] == 1 + assert result3["options"] == { + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: "GET", + CONF_VERIFY_SSL: True, + CONF_TIMEOUT: 10.0, + "sensor": [ + { + CONF_NAME: "Current version", + CONF_SELECT: ".current-version h1", + CONF_INDEX: 0.0, + } + ], + } + + assert len(mock_data.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_flow_fails(hass: HomeAssistant, get_data: MockRestData) -> None: + """Test config flow error.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == config_entries.SOURCE_USER + + with patch( + "homeassistant.components.rest.RestData", + side_effect=HomeAssistantError, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: "GET", + CONF_VERIFY_SSL: True, + CONF_TIMEOUT: 10.0, + }, + ) + + assert result2["errors"] == {"base": "resource_error"} + + with patch("homeassistant.components.rest.RestData", return_value=get_data,), patch( + "homeassistant.components.scrape.async_setup_entry", + return_value=True, + ): + result3 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: "GET", + CONF_VERIFY_SSL: True, + CONF_TIMEOUT: 10.0, + }, + ) + await hass.async_block_till_done() + result4 = await hass.config_entries.flow.async_configure( + result3["flow_id"], + { + CONF_NAME: "Current version", + CONF_SELECT: ".current-version h1", + CONF_INDEX: 0.0, + }, + ) + await hass.async_block_till_done() + + assert result4["type"] == FlowResultType.CREATE_ENTRY + assert result4["title"] == "https://www.home-assistant.io" + assert result4["options"] == { + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: "GET", + CONF_VERIFY_SSL: True, + CONF_TIMEOUT: 10.0, + "sensor": [ + { + CONF_NAME: "Current version", + CONF_SELECT: ".current-version h1", + CONF_INDEX: 0.0, + } + ], + } + + +async def test_options_flow(hass: HomeAssistant, loaded_entry: MockConfigEntry) -> None: + """Test options config flow.""" + + state = hass.states.get("sensor.current_version") + assert state.state == "Current Version: 2021.12.10" + + result = await hass.config_entries.options.async_init(loaded_entry.entry_id) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "init" + + mocker = MockRestData("test_scrape_sensor2") + with patch("homeassistant.components.rest.RestData", return_value=mocker): + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: DEFAULT_METHOD, + CONF_VERIFY_SSL: DEFAULT_VERIFY_SSL, + CONF_TIMEOUT: DEFAULT_TIMEOUT, + CONF_USERNAME: "secret_username", + CONF_PASSWORD: "secret_password", + }, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["data"] == { + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: "GET", + CONF_VERIFY_SSL: True, + CONF_TIMEOUT: 10.0, + CONF_USERNAME: "secret_username", + CONF_PASSWORD: "secret_password", + "sensor": [ + { + CONF_NAME: "Current version", + CONF_SELECT: ".current-version h1", + CONF_INDEX: 0.0, + } + ], + } + + await hass.async_block_till_done() + + # Check the entity was updated, no new entity was created + assert len(hass.states.async_all()) == 1 + + # Check the state of the entity has changed as expected + state = hass.states.get("sensor.current_version") + assert state.state == "Hidden Version: 2021.12.10" diff --git a/tests/components/scrape/test_init.py b/tests/components/scrape/test_init.py index 17803cc30c2..9b6122d6010 100644 --- a/tests/components/scrape/test_init.py +++ b/tests/components/scrape/test_init.py @@ -6,6 +6,7 @@ from unittest.mock import patch import pytest +from homeassistant import config_entries from homeassistant.components.scrape.const import DEFAULT_SCAN_INTERVAL, DOMAIN from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er @@ -13,7 +14,7 @@ from homeassistant.setup import async_setup_component from . import MockRestData, return_integration_config -from tests.common import async_fire_time_changed +from tests.common import MockConfigEntry, async_fire_time_changed async def test_setup_config(hass: HomeAssistant) -> None: @@ -109,3 +110,18 @@ async def test_setup_config_no_sensors( ): assert await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() + + +async def test_setup_entry(hass: HomeAssistant, loaded_entry: MockConfigEntry) -> None: + """Test setup entry.""" + + assert loaded_entry.state == config_entries.ConfigEntryState.LOADED + + +async def test_unload_entry(hass: HomeAssistant, loaded_entry: MockConfigEntry) -> None: + """Test unload an entry.""" + + assert loaded_entry.state == config_entries.ConfigEntryState.LOADED + assert await hass.config_entries.async_unload(loaded_entry.entry_id) + await hass.async_block_till_done() + assert loaded_entry.state is config_entries.ConfigEntryState.NOT_LOADED diff --git a/tests/components/scrape/test_sensor.py b/tests/components/scrape/test_sensor.py index 23c558a72af..2f83cdef239 100644 --- a/tests/components/scrape/test_sensor.py +++ b/tests/components/scrape/test_sensor.py @@ -26,7 +26,7 @@ from homeassistant.setup import async_setup_component from . import MockRestData, return_config, return_integration_config -from tests.common import async_fire_time_changed +from tests.common import MockConfigEntry, async_fire_time_changed DOMAIN = "scrape" @@ -405,3 +405,12 @@ async def test_scrape_sensor_unique_id(hass: HomeAssistant) -> None: entity = entity_reg.async_get("sensor.ha_version") assert entity.unique_id == "ha_version_unique_id" + + +async def test_setup_config_entry( + hass: HomeAssistant, loaded_entry: MockConfigEntry +) -> None: + """Test setup from config entry.""" + + state = hass.states.get("sensor.current_version") + assert state.state == "Current Version: 2021.12.10" From cec81e137c93674ee63304ee24f55d7b37cbad3c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 21 Nov 2022 22:58:36 +0100 Subject: [PATCH 0603/1033] Enforce AlarmControlPanelEntityFeature (#82313) --- homeassistant/components/alarm_control_panel/__init__.py | 6 ++++-- homeassistant/components/template/alarm_control_panel.py | 4 ++-- pylint/plugins/hass_enforce_type_hints.py | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index f210af3d670..f3e02465c13 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -133,7 +133,9 @@ class AlarmControlPanelEntity(Entity): _attr_changed_by: str | None = None _attr_code_arm_required: bool = True _attr_code_format: CodeFormat | None = None - _attr_supported_features: AlarmControlPanelEntityFeature | int = 0 + _attr_supported_features: AlarmControlPanelEntityFeature = ( + AlarmControlPanelEntityFeature(0) + ) @property def code_format(self) -> CodeFormat | None: @@ -207,7 +209,7 @@ class AlarmControlPanelEntity(Entity): await self.hass.async_add_executor_job(self.alarm_arm_custom_bypass, code) @property - def supported_features(self) -> AlarmControlPanelEntityFeature | int: + def supported_features(self) -> AlarmControlPanelEntityFeature: """Return the list of supported features.""" return self._attr_supported_features diff --git a/homeassistant/components/template/alarm_control_panel.py b/homeassistant/components/template/alarm_control_panel.py index d7c117c9be7..8f164142212 100644 --- a/homeassistant/components/template/alarm_control_panel.py +++ b/homeassistant/components/template/alarm_control_panel.py @@ -189,9 +189,9 @@ class AlarmControlPanelTemplate(TemplateEntity, AlarmControlPanelEntity): return self._state @property - def supported_features(self) -> int: + def supported_features(self) -> AlarmControlPanelEntityFeature: """Return the list of supported features.""" - supported_features = 0 + supported_features = AlarmControlPanelEntityFeature(0) if self._arm_night_script is not None: supported_features = ( supported_features | AlarmControlPanelEntityFeature.ARM_NIGHT diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index bbae733a536..9c7a7798893 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -661,7 +661,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["AlarmControlPanelEntityFeature", "int"], + return_type="AlarmControlPanelEntityFeature", ), TypeHintMatch( function_name="alarm_disarm", From aa443842b7c2e69462c605511a893ece4edc0fd2 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 21 Nov 2022 23:38:23 +0100 Subject: [PATCH 0604/1033] Fix incorrect type hints in scrape (#82502) --- homeassistant/components/scrape/sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index 10b96716929..943cda9564d 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -135,7 +135,7 @@ async def async_setup_platform( sensor_config, sensor_config[CONF_NAME], sensor_config.get(CONF_UNIQUE_ID), - sensor_config.get(CONF_SELECT), + sensor_config[CONF_SELECT], sensor_config.get(CONF_ATTRIBUTE), sensor_config[CONF_INDEX], value_template, @@ -194,7 +194,7 @@ class ScrapeSensor(CoordinatorEntity[ScrapeCoordinator], TemplateSensor): config: ConfigType, name: str, unique_id: str | None, - select: str | None, + select: str, attr: str | None, index: int, value_template: Template | None, From d47fe35a881f90b4bf35f17af018a6a118d8f24d Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Mon, 21 Nov 2022 18:02:54 -0500 Subject: [PATCH 0605/1033] Add config entities for lumi.motion.agl04 (#82087) --- .../components/zha/core/channels/manufacturerspecific.py | 7 ++++++- homeassistant/components/zha/number.py | 4 +++- homeassistant/components/zha/select.py | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/core/channels/manufacturerspecific.py b/homeassistant/components/zha/core/channels/manufacturerspecific.py index c4baccf4ae6..b60df5d3706 100644 --- a/homeassistant/components/zha/core/channels/manufacturerspecific.py +++ b/homeassistant/components/zha/core/channels/manufacturerspecific.py @@ -110,6 +110,11 @@ class OppleRemote(ZigbeeChannel): "motion_sensitivity": True, "trigger_indicator": True, } + elif self.cluster.endpoint.model == "lumi.motion.agl04": + self.ZCL_INIT_ATTRS = { + "detection_interval": True, + "motion_sensitivity": True, + } elif self.cluster.endpoint.model == "lumi.motion.ac01": self.ZCL_INIT_ATTRS = { "presence": True, @@ -124,7 +129,7 @@ class OppleRemote(ZigbeeChannel): async def async_initialize_channel_specific(self, from_cache: bool) -> None: """Initialize channel specific.""" - if self.cluster.endpoint.model == "lumi.motion.ac02": + if self.cluster.endpoint.model in ("lumi.motion.ac02", "lumi.motion.agl04"): interval = self.cluster.get("detection_interval", self.cluster.get(0x0102)) if interval is not None: self.debug("Loaded detection interval at startup: %s", interval) diff --git a/homeassistant/components/zha/number.py b/homeassistant/components/zha/number.py index 853ab189bbf..8a71321c208 100644 --- a/homeassistant/components/zha/number.py +++ b/homeassistant/components/zha/number.py @@ -448,7 +448,9 @@ class ZHANumberConfigurationEntity(ZhaEntity, NumberEntity): _LOGGER.debug("read value=%s", value) -@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.motion.ac02"}) +@CONFIG_DIAGNOSTIC_MATCH( + channel_names="opple_cluster", models={"lumi.motion.ac02", "lumi.motion.agl04"} +) class AqaraMotionDetectionInterval( ZHANumberConfigurationEntity, id_suffix="detection_interval" ): diff --git a/homeassistant/components/zha/select.py b/homeassistant/components/zha/select.py index 334040a2b4d..96925550676 100644 --- a/homeassistant/components/zha/select.py +++ b/homeassistant/components/zha/select.py @@ -336,7 +336,8 @@ class AqaraMotionSensitivities(types.enum8): @CONFIG_DIAGNOSTIC_MATCH( - channel_names="opple_cluster", models={"lumi.motion.ac01", "lumi.motion.ac02"} + channel_names="opple_cluster", + models={"lumi.motion.ac01", "lumi.motion.ac02", "lumi.motion.agl04"}, ) class AqaraMotionSensitivity(ZCLEnumSelectEntity, id_suffix="motion_sensitivity"): """Representation of a ZHA motion sensitivity configuration entity.""" From 5329a679bbddfdbcb351121614c412992a92d032 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Mon, 21 Nov 2022 18:03:17 -0500 Subject: [PATCH 0606/1033] Add Aqara c1 pet feeder support to ZHA (#82340) * Add Aqara c1 pet feeder support to ZHA * clean up * cleanup * state classes for daily measurements * cleanups * cleanups * restore the refreshing of the inverted value cache * cleanup --- homeassistant/components/zha/binary_sensor.py | 9 ++++ homeassistant/components/zha/button.py | 9 ++++ .../zha/core/channels/manufacturerspecific.py | 11 ++++ homeassistant/components/zha/number.py | 33 +++++++++++- homeassistant/components/zha/select.py | 17 ++++++ homeassistant/components/zha/sensor.py | 53 +++++++++++++++++++ homeassistant/components/zha/switch.py | 43 ++++++++++++--- 7 files changed, 165 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/zha/binary_sensor.py b/homeassistant/components/zha/binary_sensor.py index abddfda3358..1250e3c92a7 100644 --- a/homeassistant/components/zha/binary_sensor.py +++ b/homeassistant/components/zha/binary_sensor.py @@ -194,3 +194,12 @@ class ReplaceFilter(BinarySensor, id_suffix="replace_filter"): SENSOR_ATTR = "replace_filter" _attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.PROBLEM + + +@MULTI_MATCH(channel_names="opple_cluster", models={"aqara.feeder.acn001"}) +class AqaraPetFeederErrorDetected(BinarySensor, id_suffix="error_detected"): + """ZHA aqara pet feeder error detected binary sensor.""" + + SENSOR_ATTR = "error_detected" + _attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.PROBLEM + _attr_name: str = "Error detected" diff --git a/homeassistant/components/zha/button.py b/homeassistant/components/zha/button.py index 41f3846e97f..e41e7a81e12 100644 --- a/homeassistant/components/zha/button.py +++ b/homeassistant/components/zha/button.py @@ -177,3 +177,12 @@ class NoPresenceStatusResetButton( _attribute_value = 1 _attr_device_class = ButtonDeviceClass.RESTART _attr_entity_category = EntityCategory.CONFIG + + +@MULTI_MATCH(channel_names="opple_cluster", models={"aqara.feeder.acn001"}) +class AqaraPetFeederFeedButton(ZHAAttributeButton, id_suffix="feeding"): + """Defines a feed button for the aqara c1 pet feeder.""" + + _attribute_name = "feeding" + _attr_name = "Feed" + _attribute_value = 1 diff --git a/homeassistant/components/zha/core/channels/manufacturerspecific.py b/homeassistant/components/zha/core/channels/manufacturerspecific.py index b60df5d3706..427579cfb59 100644 --- a/homeassistant/components/zha/core/channels/manufacturerspecific.py +++ b/homeassistant/components/zha/core/channels/manufacturerspecific.py @@ -126,6 +126,17 @@ class OppleRemote(ZigbeeChannel): self.ZCL_INIT_ATTRS = { "power_outage_memory": True, } + elif self.cluster.endpoint.model == "aqara.feeder.acn001": + self.ZCL_INIT_ATTRS = { + "portions_dispensed": True, + "weight_dispensed": True, + "error_detected": True, + "disable_led_indicator": True, + "child_lock": True, + "feeding_mode": True, + "serving_size": True, + "portion_weight": True, + } async def async_initialize_channel_specific(self, from_cache: bool) -> None: """Initialize channel specific.""" diff --git a/homeassistant/components/zha/number.py b/homeassistant/components/zha/number.py index 8a71321c208..41e90899894 100644 --- a/homeassistant/components/zha/number.py +++ b/homeassistant/components/zha/number.py @@ -8,9 +8,9 @@ from typing import TYPE_CHECKING, Any, TypeVar import zigpy.exceptions from zigpy.zcl.foundation import Status -from homeassistant.components.number import NumberEntity +from homeassistant.components.number import NumberEntity, NumberMode from homeassistant.config_entries import ConfigEntry -from homeassistant.const import Platform +from homeassistant.const import Platform, UnitOfMass from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory @@ -836,3 +836,32 @@ class InovelliDefaultAllLEDOffIntensity( _attr_native_max_value: float = 100 _zcl_attribute: str = "led_intensity_when_off" _attr_name: str = "Default all LED off intensity" + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"aqara.feeder.acn001"}) +class AqaraPetFeederServingSize(ZHANumberConfigurationEntity, id_suffix="serving_size"): + """Aqara pet feeder serving size configuration entity.""" + + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 1 + _attr_native_max_value: float = 10 + _zcl_attribute: str = "serving_size" + _attr_name: str = "Serving to dispense" + _attr_mode: NumberMode = NumberMode.BOX + _attr_icon: str = "mdi:counter" + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"aqara.feeder.acn001"}) +class AqaraPetFeederPortionWeight( + ZHANumberConfigurationEntity, id_suffix="portion_weight" +): + """Aqara pet feeder portion weight configuration entity.""" + + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 1 + _attr_native_max_value: float = 100 + _zcl_attribute: str = "portion_weight" + _attr_name: str = "Portion weight" + _attr_mode: NumberMode = NumberMode.BOX + _attr_native_unit_of_measurement: str = UnitOfMass.GRAMS + _attr_icon: str = "mdi:weight-gram" diff --git a/homeassistant/components/zha/select.py b/homeassistant/components/zha/select.py index 96925550676..295c61314c7 100644 --- a/homeassistant/components/zha/select.py +++ b/homeassistant/components/zha/select.py @@ -477,3 +477,20 @@ class InovelliSwitchTypeEntity(ZCLEnumSelectEntity, id_suffix="switch_type"): _select_attr = "switch_type" _enum = InovelliSwitchType _attr_name: str = "Switch type" + + +class AqaraFeedingMode(types.enum8): + """Feeding mode.""" + + Manual = 0x00 + Schedule = 0x01 + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"aqara.feeder.acn001"}) +class AqaraPetFeederMode(ZCLEnumSelectEntity, id_suffix="feeding_mode"): + """Representation of an Aqara pet feeder mode configuration entity.""" + + _select_attr = "feeding_mode" + _enum = AqaraFeedingMode + _attr_name = "Mode" + _attr_icon: str = "mdi:wrench-clock" diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index ba4aec66f35..003f5771b93 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -5,6 +5,8 @@ import functools import numbers from typing import TYPE_CHECKING, Any, TypeVar +from zigpy import types + from homeassistant.components.climate import HVACAction from homeassistant.components.sensor import ( SensorDeviceClass, @@ -36,6 +38,7 @@ from homeassistant.const import ( VOLUME_GALLONS, VOLUME_LITERS, Platform, + UnitOfMass, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -837,3 +840,53 @@ class IkeaFilterRunTime(Sensor, id_suffix="filter_run_time"): _attr_icon = "mdi:timer" _attr_name: str = "Filter run time" _unit = TIME_MINUTES + + +class AqaraFeedingSource(types.enum8): + """Aqara pet feeder feeding source.""" + + Feeder = 0x01 + HomeAssistant = 0x02 + + +@MULTI_MATCH(channel_names="opple_cluster", models={"aqara.feeder.acn001"}) +class AqaraPetFeederLastFeedingSource(Sensor, id_suffix="last_feeding_source"): + """Sensor that displays the last feeding source of pet feeder.""" + + SENSOR_ATTR = "last_feeding_source" + _attr_name: str = "Last feeding source" + _attr_icon = "mdi:devices" + + def formatter(self, value: int) -> int | float | None: + """Numeric pass-through formatter.""" + return AqaraFeedingSource(value).name + + +@MULTI_MATCH(channel_names="opple_cluster", models={"aqara.feeder.acn001"}) +class AqaraPetFeederLastFeedingSize(Sensor, id_suffix="last_feeding_size"): + """Sensor that displays the last feeding size of the pet feeder.""" + + SENSOR_ATTR = "last_feeding_size" + _attr_name: str = "Last feeding size" + _attr_icon: str = "mdi:counter" + + +@MULTI_MATCH(channel_names="opple_cluster", models={"aqara.feeder.acn001"}) +class AqaraPetFeederPortionsDispensed(Sensor, id_suffix="portions_dispensed"): + """Sensor that displays the number of portions dispensed by the pet feeder.""" + + SENSOR_ATTR = "portions_dispensed" + _attr_name: str = "Portions dispensed today" + _attr_state_class: SensorStateClass = SensorStateClass.TOTAL_INCREASING + _attr_icon: str = "mdi:counter" + + +@MULTI_MATCH(channel_names="opple_cluster", models={"aqara.feeder.acn001"}) +class AqaraPetFeederWeightDispensed(Sensor, id_suffix="weight_dispensed"): + """Sensor that displays the weight weight dispensed by the pet feeder.""" + + SENSOR_ATTR = "weight_dispensed" + _attr_name: str = "Weight dispensed today" + _unit = UnitOfMass.GRAMS + _attr_state_class: SensorStateClass = SensorStateClass.TOTAL_INCREASING + _attr_icon: str = "mdi:weight-gram" diff --git a/homeassistant/components/zha/switch.py b/homeassistant/components/zha/switch.py index 0c2e5e7ebe2..ac285700a27 100644 --- a/homeassistant/components/zha/switch.py +++ b/homeassistant/components/zha/switch.py @@ -174,7 +174,8 @@ class ZHASwitchConfigurationEntity(ZhaEntity, SwitchEntity): _attr_entity_category = EntityCategory.CONFIG _zcl_attribute: str - _zcl_inverter_attribute: str = "" + _zcl_inverter_attribute: str | None = None + _force_inverted: bool = False @classmethod def create_entity( @@ -225,19 +226,24 @@ class ZHASwitchConfigurationEntity(ZhaEntity, SwitchEntity): """Handle state update from channel.""" self.async_write_ha_state() + @property + def inverted(self) -> bool: + """Return True if the switch is inverted.""" + if self._zcl_inverter_attribute: + return bool(self._channel.cluster.get(self._zcl_inverter_attribute)) + return self._force_inverted + @property def is_on(self) -> bool: """Return if the switch is on based on the statemachine.""" val = bool(self._channel.cluster.get(self._zcl_attribute)) - invert = bool(self._channel.cluster.get(self._zcl_inverter_attribute)) - return (not val) if invert else val + return (not val) if self.inverted else val async def async_turn_on_off(self, state: bool) -> None: """Turn the entity on or off.""" try: - invert = bool(self._channel.cluster.get(self._zcl_inverter_attribute)) result = await self._channel.cluster.write_attributes( - {self._zcl_attribute: not state if invert else state} + {self._zcl_attribute: not state if self.inverted else state} ) except zigpy.exceptions.ZigbeeException as ex: self.error("Could not set value: %s", ex) @@ -258,15 +264,15 @@ class ZHASwitchConfigurationEntity(ZhaEntity, SwitchEntity): async def async_update(self) -> None: """Attempt to retrieve the state of the entity.""" await super().async_update() - _LOGGER.error("Polling current state") + self.error("Polling current state") if self._channel: value = await self._channel.get_attribute_value( self._zcl_attribute, from_cache=False ) - invert = await self._channel.get_attribute_value( + await self._channel.get_attribute_value( self._zcl_inverter_attribute, from_cache=False ) - _LOGGER.debug("read value=%s, inverter=%s", value, bool(invert)) + self.debug("read value=%s, inverted=%s", value, self.inverted) @CONFIG_DIAGNOSTIC_MATCH( @@ -430,3 +436,24 @@ class InovelliDisableDoubleTapClearNotificationsMode( _zcl_attribute: str = "disable_clear_notifications_double_tap" _attr_name: str = "Disable config 2x tap to clear notifications" + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"aqara.feeder.acn001"}) +class AqaraPetFeederLEDIndicator( + ZHASwitchConfigurationEntity, id_suffix="disable_led_indicator" +): + """Representation of a LED indicator configuration entity.""" + + _zcl_attribute: str = "disable_led_indicator" + _attr_name = "LED indicator" + _force_inverted = True + _attr_icon: str = "mdi:led-on" + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"aqara.feeder.acn001"}) +class AqaraPetFeederChildLock(ZHASwitchConfigurationEntity, id_suffix="child_lock"): + """Representation of a child lock configuration entity.""" + + _zcl_attribute: str = "child_lock" + _attr_name = "Child lock" + _attr_icon: str = "mdi:account-lock" From f9e80fead38162c0bd5fe81d1c1c8f438b5d7ced Mon Sep 17 00:00:00 2001 From: wildekek Date: Tue, 22 Nov 2022 01:17:37 +0100 Subject: [PATCH 0607/1033] Add new Amazon Polly voices (#82501) --- homeassistant/components/amazon_polly/const.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/homeassistant/components/amazon_polly/const.py b/homeassistant/components/amazon_polly/const.py index 3892204c47a..a0250938fb4 100644 --- a/homeassistant/components/amazon_polly/const.py +++ b/homeassistant/components/amazon_polly/const.py @@ -36,6 +36,7 @@ SUPPORTED_VOICES: Final[list[str]] = [ "Aditi", # Hindi "Amy", "Aria", + "Arlet", # Catalan, Neural "Arthur", # English, Neural "Astrid", # Swedish "Ayanda", @@ -50,6 +51,7 @@ SUPPORTED_VOICES: Final[list[str]] = [ "Cristiano", "Daniel", # German, Neural "Dora", # Icelandic + "Elin", # Swedish, Neural "Emma", # English "Enrique", "Ewa", @@ -58,7 +60,11 @@ SUPPORTED_VOICES: Final[list[str]] = [ "Geraint", # English Welsh "Giorgio", "Gwyneth", # Welsh + "Hala", # Arabic (Gulf), Neural + "Hannah", # German (Austrian), Neural "Hans", + "Hiujin", # Chinese (Cantonese), Neural + "Ida", # Norwegian, Neural "Ines", # Portuguese, European "Ivy", "Jacek", @@ -66,10 +72,12 @@ SUPPORTED_VOICES: Final[list[str]] = [ "Joanna", "Joey", "Justin", + "Kajal", # English (Indian)/Hindi (Bilingual ), Neural "Karl", "Kendra", "Kevin", "Kimberly", + "Laura", # Dutch, Neural "Lea", # French "Liam", # Canadian French, Neural "Liv", # Norwegian @@ -87,6 +95,7 @@ SUPPORTED_VOICES: Final[list[str]] = [ "Mizuki", # Japanese "Naja", # Danish "Nicole", # English Australian + "Ola", # Polish, Neural "Olivia", # Female, Australian, Neural "Penelope", # Spanish US "Pedro", # Spanish US, Neural @@ -96,6 +105,7 @@ SUPPORTED_VOICES: Final[list[str]] = [ "Russell", "Salli", # English "Seoyeon", # Korean + "Suvi", # Finnish "Takumi", "Tatyana", # Russian "Vicki", # German From 0be2a3f1827c7fd3b5c200d28ae0593d0bb3ff07 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 22 Nov 2022 00:26:32 +0000 Subject: [PATCH 0608/1033] [ci skip] Translation update --- .../components/abode/translations/bg.json | 2 +- .../components/airq/translations/sk.json | 6 ++++ .../airthings_ble/translations/sk.json | 9 ++++- .../components/airvisual/translations/bg.json | 2 +- .../aladdin_connect/translations/bg.json | 2 +- .../components/apcupsd/translations/sk.json | 3 ++ .../components/apple_tv/translations/bg.json | 2 +- .../components/apple_tv/translations/sk.json | 5 +++ .../components/aranet/translations/sk.json | 18 ++++++++++ .../aussie_broadband/translations/bg.json | 2 +- .../components/awair/translations/bg.json | 2 +- .../components/awair/translations/sk.json | 1 + .../azure_devops/translations/bg.json | 2 +- .../bluemaestro/translations/sk.json | 9 +++++ .../components/bluetooth/translations/sk.json | 9 ++++- .../components/bosch_shc/translations/bg.json | 2 +- .../components/braviatv/translations/bg.json | 2 +- .../components/broadlink/translations/sk.json | 3 +- .../components/brunt/translations/bg.json | 2 +- .../components/bthome/translations/bg.json | 2 +- .../components/bthome/translations/sk.json | 8 +++++ .../cloudflare/translations/bg.json | 2 +- .../devolo_home_control/translations/bg.json | 2 +- .../components/discord/translations/bg.json | 2 +- .../components/doorbird/translations/sk.json | 1 + .../components/dsmr/translations/sk.json | 3 ++ .../components/efergy/translations/bg.json | 2 +- .../components/elkm1/translations/sk.json | 5 +++ .../enphase_envoy/translations/bg.json | 2 +- .../components/esphome/translations/bg.json | 2 +- .../components/fibaro/translations/bg.json | 2 +- .../fireservicerota/translations/bg.json | 2 +- .../components/flume/translations/bg.json | 2 +- .../components/fritz/translations/bg.json | 2 +- .../geocaching/translations/bg.json | 2 +- .../components/google/translations/bg.json | 2 +- .../google_sheets/translations/bg.json | 2 +- .../components/hive/translations/bg.json | 2 +- .../components/hive/translations/sk.json | 5 +++ .../homeassistant_yellow/translations/sk.json | 7 ++++ .../homekit_controller/translations/sk.json | 3 ++ .../components/hyperion/translations/bg.json | 2 +- .../components/insteon/translations/sk.json | 7 ++++ .../intellifire/translations/bg.json | 2 +- .../components/isy994/translations/bg.json | 2 +- .../components/kegtron/translations/sk.json | 9 +++++ .../keymitt_ble/translations/sk.json | 12 ++++++- .../components/knx/translations/sk.json | 11 +++++- .../lacrosse_view/translations/bg.json | 2 +- .../components/lametric/translations/sk.json | 7 ++++ .../landisgyr_heat_meter/translations/sk.json | 7 ++++ .../components/led_ble/translations/sk.json | 4 +++ .../components/lidarr/translations/bg.json | 2 +- .../components/life360/translations/bg.json | 2 +- .../components/lifx/translations/sk.json | 9 ++++- .../litterrobot/translations/bg.json | 2 +- .../components/livisi/translations/sk.json | 1 + .../components/lyric/translations/bg.json | 2 +- .../components/mikrotik/translations/bg.json | 2 +- .../components/motioneye/translations/bg.json | 2 +- .../components/myq/translations/bg.json | 2 +- .../components/nam/translations/bg.json | 2 +- .../components/nanoleaf/translations/bg.json | 2 +- .../components/neato/translations/bg.json | 2 +- .../components/netatmo/translations/bg.json | 2 +- .../nibe_heatpump/translations/sk.json | 7 ++++ .../components/nobo_hub/translations/sk.json | 7 ++++ .../components/notion/translations/bg.json | 2 +- .../components/nuki/translations/bg.json | 2 +- .../components/octoprint/translations/bg.json | 2 +- .../components/oralb/translations/sk.json | 7 ++++ .../components/overkiz/translations/bg.json | 2 +- .../components/picnic/translations/bg.json | 2 +- .../components/plex/translations/bg.json | 2 +- .../components/prosegur/translations/bg.json | 2 +- .../pushbullet/translations/sk.json | 19 +++++++++++ .../components/pushover/translations/bg.json | 2 +- .../components/pvoutput/translations/bg.json | 2 +- .../components/qingping/translations/sk.json | 9 +++++ .../components/radarr/translations/bg.json | 2 +- .../components/renault/translations/bg.json | 2 +- .../components/rfxtrx/translations/sk.json | 3 ++ .../components/ridwell/translations/bg.json | 2 +- .../components/samsungtv/translations/bg.json | 2 +- .../components/scrape/translations/de.json | 34 +++++++++++++++++-- .../components/scrape/translations/en.json | 28 +++++++++++++++ .../components/sense/translations/bg.json | 2 +- .../components/senseme/translations/sk.json | 5 +++ .../components/sensibo/translations/bg.json | 2 +- .../components/sensorpro/translations/sk.json | 7 ++++ .../components/sharkiq/translations/bg.json | 2 +- .../components/shelly/translations/bg.json | 2 +- .../simplisafe/translations/bg.json | 2 +- .../components/skybell/translations/bg.json | 2 +- .../components/sleepiq/translations/bg.json | 2 +- .../components/sms/translations/sk.json | 11 ++++++ .../components/snooz/translations/sk.json | 8 +++++ .../components/sonarr/translations/bg.json | 2 +- .../steam_online/translations/bg.json | 2 +- .../components/steamist/translations/sk.json | 5 +++ .../components/switchbot/translations/sk.json | 2 ++ .../synology_dsm/translations/bg.json | 2 +- .../system_bridge/translations/bg.json | 2 +- .../components/tailscale/translations/bg.json | 2 +- .../tankerkoenig/translations/bg.json | 2 +- .../components/tautulli/translations/bg.json | 2 +- .../thermobeacon/translations/sk.json | 9 +++++ .../components/thermopro/translations/sk.json | 8 +++++ .../components/tile/translations/bg.json | 2 +- .../components/tilt_ble/translations/sk.json | 8 +++++ .../totalconnect/translations/bg.json | 2 +- .../components/tplink/translations/sk.json | 5 +++ .../components/tractive/translations/bg.json | 2 +- .../trafikverket_ferry/translations/bg.json | 2 +- .../trafikverket_train/translations/bg.json | 2 +- .../transmission/translations/bg.json | 2 +- .../components/unifi/translations/bg.json | 2 +- .../components/upnp/translations/sk.json | 9 ++++- .../uptimerobot/translations/bg.json | 2 +- .../components/verisure/translations/bg.json | 2 +- .../components/vizio/translations/sk.json | 1 + .../vlc_telnet/translations/bg.json | 2 +- .../volvooncall/translations/bg.json | 2 +- .../components/wallbox/translations/bg.json | 2 +- .../components/watttime/translations/bg.json | 2 +- .../components/wiz/translations/sk.json | 5 +++ .../components/wolflink/translations/sk.json | 5 +++ .../xiaomi_miio/translations/bg.json | 2 +- .../yale_smart_alarm/translations/bg.json | 2 +- .../components/yeelight/translations/sk.json | 5 +++ .../components/yolink/translations/bg.json | 2 +- .../components/zamg/translations/sk.json | 7 ++++ 132 files changed, 450 insertions(+), 92 deletions(-) create mode 100644 homeassistant/components/aranet/translations/sk.json create mode 100644 homeassistant/components/bluemaestro/translations/sk.json create mode 100644 homeassistant/components/bthome/translations/sk.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/sk.json create mode 100644 homeassistant/components/kegtron/translations/sk.json create mode 100644 homeassistant/components/lametric/translations/sk.json create mode 100644 homeassistant/components/landisgyr_heat_meter/translations/sk.json create mode 100644 homeassistant/components/nibe_heatpump/translations/sk.json create mode 100644 homeassistant/components/nobo_hub/translations/sk.json create mode 100644 homeassistant/components/oralb/translations/sk.json create mode 100644 homeassistant/components/pushbullet/translations/sk.json create mode 100644 homeassistant/components/qingping/translations/sk.json create mode 100644 homeassistant/components/sensorpro/translations/sk.json create mode 100644 homeassistant/components/sms/translations/sk.json create mode 100644 homeassistant/components/snooz/translations/sk.json create mode 100644 homeassistant/components/thermobeacon/translations/sk.json create mode 100644 homeassistant/components/thermopro/translations/sk.json create mode 100644 homeassistant/components/tilt_ble/translations/sk.json diff --git a/homeassistant/components/abode/translations/bg.json b/homeassistant/components/abode/translations/bg.json index a451dd3516a..6a58c61203d 100644 --- a/homeassistant/components/abode/translations/bg.json +++ b/homeassistant/components/abode/translations/bg.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "single_instance_allowed": "\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0430 Abode." }, "error": { diff --git a/homeassistant/components/airq/translations/sk.json b/homeassistant/components/airq/translations/sk.json index 7c93ea593b6..16f47ef1360 100644 --- a/homeassistant/components/airq/translations/sk.json +++ b/homeassistant/components/airq/translations/sk.json @@ -1,11 +1,17 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", "invalid_input": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, "step": { "user": { "data": { + "ip_address": "IP adresa", "password": "Heslo" } } diff --git a/homeassistant/components/airthings_ble/translations/sk.json b/homeassistant/components/airthings_ble/translations/sk.json index 5667845902b..8da39055837 100644 --- a/homeassistant/components/airthings_ble/translations/sk.json +++ b/homeassistant/components/airthings_ble/translations/sk.json @@ -7,6 +7,13 @@ "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, - "flow_title": "{name}" + "flow_title": "{name}", + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/airvisual/translations/bg.json b/homeassistant/components/airvisual/translations/bg.json index 807b9556240..d97a78ab191 100644 --- a/homeassistant/components/airvisual/translations/bg.json +++ b/homeassistant/components/airvisual/translations/bg.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/aladdin_connect/translations/bg.json b/homeassistant/components/aladdin_connect/translations/bg.json index d5babb02c1c..0a5cd10febe 100644 --- a/homeassistant/components/aladdin_connect/translations/bg.json +++ b/homeassistant/components/aladdin_connect/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/apcupsd/translations/sk.json b/homeassistant/components/apcupsd/translations/sk.json index 842ff61bd79..3c3d27a6689 100644 --- a/homeassistant/components/apcupsd/translations/sk.json +++ b/homeassistant/components/apcupsd/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/apple_tv/translations/bg.json b/homeassistant/components/apple_tv/translations/bg.json index cd0488141c4..0417c28d219 100644 --- a/homeassistant/components/apple_tv/translations/bg.json +++ b/homeassistant/components/apple_tv/translations/bg.json @@ -4,7 +4,7 @@ "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "ipv6_not_supported": "IPv6 \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430.", "no_devices_found": "\u041d\u0435 \u0441\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { diff --git a/homeassistant/components/apple_tv/translations/sk.json b/homeassistant/components/apple_tv/translations/sk.json index 1dab0308776..7614703adcc 100644 --- a/homeassistant/components/apple_tv/translations/sk.json +++ b/homeassistant/components/apple_tv/translations/sk.json @@ -10,6 +10,11 @@ "step": { "password": { "title": "Vy\u017eaduje sa heslo" + }, + "user": { + "data": { + "device_input": "Zariadenie" + } } } } diff --git a/homeassistant/components/aranet/translations/sk.json b/homeassistant/components/aranet/translations/sk.json new file mode 100644 index 00000000000..6879ec0155c --- /dev/null +++ b/homeassistant/components/aranet/translations/sk.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aussie_broadband/translations/bg.json b/homeassistant/components/aussie_broadband/translations/bg.json index 8098935e86c..6771b472a59 100644 --- a/homeassistant/components/aussie_broadband/translations/bg.json +++ b/homeassistant/components/aussie_broadband/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "no_services_found": "\u041d\u0435 \u0431\u044f\u0445\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u043b\u0443\u0433\u0438 \u0437\u0430 \u0442\u043e\u0437\u0438 \u0430\u043a\u0430\u0443\u043d\u0442", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/awair/translations/bg.json b/homeassistant/components/awair/translations/bg.json index 0f9884bf058..3f1e4734e29 100644 --- a/homeassistant/components/awair/translations/bg.json +++ b/homeassistant/components/awair/translations/bg.json @@ -5,7 +5,7 @@ "already_configured_account": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "already_configured_device": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e", "no_devices_found": "\u041d\u044f\u043c\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unreachable": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "error": { diff --git a/homeassistant/components/awair/translations/sk.json b/homeassistant/components/awair/translations/sk.json index 50f9d9020f7..26d16542bc8 100644 --- a/homeassistant/components/awair/translations/sk.json +++ b/homeassistant/components/awair/translations/sk.json @@ -12,6 +12,7 @@ }, "local_pick": { "data": { + "device": "Zaradenie", "host": "IP adresa" } }, diff --git a/homeassistant/components/azure_devops/translations/bg.json b/homeassistant/components/azure_devops/translations/bg.json index 28af7ef6e00..1b4e526e1b3 100644 --- a/homeassistant/components/azure_devops/translations/bg.json +++ b/homeassistant/components/azure_devops/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/bluemaestro/translations/sk.json b/homeassistant/components/bluemaestro/translations/sk.json new file mode 100644 index 00000000000..25ca828afe4 --- /dev/null +++ b/homeassistant/components/bluemaestro/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "not_supported": "Zariadenie nie je podporovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/bluetooth/translations/sk.json b/homeassistant/components/bluetooth/translations/sk.json index e8940bef26a..efe0c9c2168 100644 --- a/homeassistant/components/bluetooth/translations/sk.json +++ b/homeassistant/components/bluetooth/translations/sk.json @@ -1,5 +1,12 @@ { "config": { - "flow_title": "{name}" + "flow_title": "{name}", + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/bg.json b/homeassistant/components/bosch_shc/translations/bg.json index a0b0548b51b..55de74f88a6 100644 --- a/homeassistant/components/bosch_shc/translations/bg.json +++ b/homeassistant/components/bosch_shc/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/braviatv/translations/bg.json b/homeassistant/components/braviatv/translations/bg.json index ed2a2759529..3eedf02caab 100644 --- a/homeassistant/components/braviatv/translations/bg.json +++ b/homeassistant/components/braviatv/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "not_bravia_device": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u043d\u0435 \u0435 \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440 Bravia.", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "reauth_unsuccessful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430, \u043c\u043e\u043b\u044f, \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0438 \u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e." }, "error": { diff --git a/homeassistant/components/broadlink/translations/sk.json b/homeassistant/components/broadlink/translations/sk.json index cebc1d218d4..217d7cc83a4 100644 --- a/homeassistant/components/broadlink/translations/sk.json +++ b/homeassistant/components/broadlink/translations/sk.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", - "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", + "not_supported": "Zariadenie nie je podporovan\u00e9" }, "error": { "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" diff --git a/homeassistant/components/brunt/translations/bg.json b/homeassistant/components/brunt/translations/bg.json index 71737c4fb26..57d5f6be9bf 100644 --- a/homeassistant/components/brunt/translations/bg.json +++ b/homeassistant/components/brunt/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/bthome/translations/bg.json b/homeassistant/components/bthome/translations/bg.json index 895fcac7c4f..22080c02972 100644 --- a/homeassistant/components/bthome/translations/bg.json +++ b/homeassistant/components/bthome/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "no_devices_found": "\u041d\u044f\u043c\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/bthome/translations/sk.json b/homeassistant/components/bthome/translations/sk.json new file mode 100644 index 00000000000..8444b9fbeae --- /dev/null +++ b/homeassistant/components/bthome/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cloudflare/translations/bg.json b/homeassistant/components/cloudflare/translations/bg.json index 84593a40a00..7690840a21a 100644 --- a/homeassistant/components/cloudflare/translations/bg.json +++ b/homeassistant/components/cloudflare/translations/bg.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { diff --git a/homeassistant/components/devolo_home_control/translations/bg.json b/homeassistant/components/devolo_home_control/translations/bg.json index 47ab5f03cbc..3741bfd4438 100644 --- a/homeassistant/components/devolo_home_control/translations/bg.json +++ b/homeassistant/components/devolo_home_control/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u041f\u0440\u043e\u0444\u0438\u043b\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "step": { "user": { diff --git a/homeassistant/components/discord/translations/bg.json b/homeassistant/components/discord/translations/bg.json index 00faba1155f..2d2d1253e4a 100644 --- a/homeassistant/components/discord/translations/bg.json +++ b/homeassistant/components/discord/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/doorbird/translations/sk.json b/homeassistant/components/doorbird/translations/sk.json index 0390bd497a9..8969a058800 100644 --- a/homeassistant/components/doorbird/translations/sk.json +++ b/homeassistant/components/doorbird/translations/sk.json @@ -8,6 +8,7 @@ "user": { "data": { "host": "Hostite\u013e", + "name": "N\u00e1zov zariadenia", "password": "Heslo" } } diff --git a/homeassistant/components/dsmr/translations/sk.json b/homeassistant/components/dsmr/translations/sk.json index d74ce79a3d7..35f2d7628c7 100644 --- a/homeassistant/components/dsmr/translations/sk.json +++ b/homeassistant/components/dsmr/translations/sk.json @@ -6,6 +6,9 @@ "host": "Hostite\u013e", "port": "Port" } + }, + "setup_serial": { + "title": "Zariadenie" } } } diff --git a/homeassistant/components/efergy/translations/bg.json b/homeassistant/components/efergy/translations/bg.json index 14d4c77c8f9..2abb443e903 100644 --- a/homeassistant/components/efergy/translations/bg.json +++ b/homeassistant/components/efergy/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/elkm1/translations/sk.json b/homeassistant/components/elkm1/translations/sk.json index 76d5e5fdfea..6b5aee05996 100644 --- a/homeassistant/components/elkm1/translations/sk.json +++ b/homeassistant/components/elkm1/translations/sk.json @@ -17,6 +17,11 @@ "data": { "password": "Heslo" } + }, + "user": { + "data": { + "device": "Zariadenie" + } } } } diff --git a/homeassistant/components/enphase_envoy/translations/bg.json b/homeassistant/components/enphase_envoy/translations/bg.json index 7d794942093..53a947fb298 100644 --- a/homeassistant/components/enphase_envoy/translations/bg.json +++ b/homeassistant/components/enphase_envoy/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/esphome/translations/bg.json b/homeassistant/components/esphome/translations/bg.json index 061fcc768a0..282d3503aec 100644 --- a/homeassistant/components/esphome/translations/bg.json +++ b/homeassistant/components/esphome/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "ESP \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "connection_error": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 ESP. \u041c\u043e\u043b\u044f, \u0443\u0432\u0435\u0440\u0435\u0442\u0435 \u0441\u0435, \u0447\u0435 \u0432\u0430\u0448\u0438\u044f\u0442 YAML \u0444\u0430\u0439\u043b \u0441\u044a\u0434\u044a\u0440\u0436\u0430 \u0440\u0435\u0434 \"api:\".", diff --git a/homeassistant/components/fibaro/translations/bg.json b/homeassistant/components/fibaro/translations/bg.json index 3eab800c93f..b03b7cff057 100644 --- a/homeassistant/components/fibaro/translations/bg.json +++ b/homeassistant/components/fibaro/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/fireservicerota/translations/bg.json b/homeassistant/components/fireservicerota/translations/bg.json index 422988d551b..ae6e31fb3e0 100644 --- a/homeassistant/components/fireservicerota/translations/bg.json +++ b/homeassistant/components/fireservicerota/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "create_entry": { "default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u0435\u043d" diff --git a/homeassistant/components/flume/translations/bg.json b/homeassistant/components/flume/translations/bg.json index 1ceb53d2be7..56718239f65 100644 --- a/homeassistant/components/flume/translations/bg.json +++ b/homeassistant/components/flume/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/fritz/translations/bg.json b/homeassistant/components/fritz/translations/bg.json index 7341a275d46..5b2be26d0d4 100644 --- a/homeassistant/components/fritz/translations/bg.json +++ b/homeassistant/components/fritz/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", diff --git a/homeassistant/components/geocaching/translations/bg.json b/homeassistant/components/geocaching/translations/bg.json index d3ca579dff7..3004464ff5b 100644 --- a/homeassistant/components/geocaching/translations/bg.json +++ b/homeassistant/components/geocaching/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430.", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "create_entry": { "default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u0435\u043d\u043e" diff --git a/homeassistant/components/google/translations/bg.json b/homeassistant/components/google/translations/bg.json index 2fa49447827..2cf37955e1e 100644 --- a/homeassistant/components/google/translations/bg.json +++ b/homeassistant/components/google/translations/bg.json @@ -4,7 +4,7 @@ "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430.", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "create_entry": { "default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u0435\u043d\u043e" diff --git a/homeassistant/components/google_sheets/translations/bg.json b/homeassistant/components/google_sheets/translations/bg.json index 80ba164940b..ec743b0b8fd 100644 --- a/homeassistant/components/google_sheets/translations/bg.json +++ b/homeassistant/components/google_sheets/translations/bg.json @@ -5,7 +5,7 @@ "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "create_spreadsheet_failure": "\u0413\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u0441\u044a\u0437\u0434\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u0430, \u0432\u0438\u0436\u0442\u0435 \u0436\u0443\u0440\u043d\u0430\u043b\u0430 \u0437\u0430 \u0433\u0440\u0435\u0448\u043a\u0438 \u0437\u0430 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438", "missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430.", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "create_entry": { diff --git a/homeassistant/components/hive/translations/bg.json b/homeassistant/components/hive/translations/bg.json index c027da47da5..0484d63a5b5 100644 --- a/homeassistant/components/hive/translations/bg.json +++ b/homeassistant/components/hive/translations/bg.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "no_internet_available": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u0441\u0432\u044a\u0440\u0437\u0430\u043d\u043e\u0441\u0442 \u0437\u0430 \u0434\u0430 \u0441\u0435 \u0441\u0432\u044a\u0440\u0436\u0435\u0442\u0435 \u0441 Hive." diff --git a/homeassistant/components/hive/translations/sk.json b/homeassistant/components/hive/translations/sk.json index 5e1d3b6f394..29946cdbfba 100644 --- a/homeassistant/components/hive/translations/sk.json +++ b/homeassistant/components/hive/translations/sk.json @@ -4,6 +4,11 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "step": { + "configuration": { + "data": { + "device_name": "N\u00e1zov zariadenia" + } + }, "reauth": { "data": { "password": "Heslo" diff --git a/homeassistant/components/homeassistant_yellow/translations/sk.json b/homeassistant/components/homeassistant_yellow/translations/sk.json new file mode 100644 index 00000000000..03a9c39219d --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/sk.json @@ -0,0 +1,7 @@ +{ + "options": { + "error": { + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/sk.json b/homeassistant/components/homekit_controller/translations/sk.json index 9afe02731ca..ccf13684b9a 100644 --- a/homeassistant/components/homekit_controller/translations/sk.json +++ b/homeassistant/components/homekit_controller/translations/sk.json @@ -6,6 +6,9 @@ "flow_title": "{name} ({category})", "step": { "user": { + "data": { + "device": "Zariadenie" + }, "title": "V\u00fdber zariadenia" } } diff --git a/homeassistant/components/hyperion/translations/bg.json b/homeassistant/components/hyperion/translations/bg.json index 38499f1fefa..8a0677c63ab 100644 --- a/homeassistant/components/hyperion/translations/bg.json +++ b/homeassistant/components/hyperion/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" diff --git a/homeassistant/components/insteon/translations/sk.json b/homeassistant/components/insteon/translations/sk.json index bad6f4f3cd3..1c992e2f792 100644 --- a/homeassistant/components/insteon/translations/sk.json +++ b/homeassistant/components/insteon/translations/sk.json @@ -22,6 +22,13 @@ }, "options": { "step": { + "add_override": { + "data": { + "address": "Adresa zariadenia (t. j. 1a2b3c)", + "cat": "Kateg\u00f3ria zariadenia (t. j. 0x10)", + "subcat": "Podkateg\u00f3ria zariadenia (napr. 0x0a)" + } + }, "change_hub_config": { "data": { "host": "IP adresa", diff --git a/homeassistant/components/intellifire/translations/bg.json b/homeassistant/components/intellifire/translations/bg.json index 0a0b8c64e8e..af24819f4ac 100644 --- a/homeassistant/components/intellifire/translations/bg.json +++ b/homeassistant/components/intellifire/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "api_error": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u043b\u0438\u0437\u0430\u043d\u0435", diff --git a/homeassistant/components/isy994/translations/bg.json b/homeassistant/components/isy994/translations/bg.json index bba26a6cf89..75bd10bbca9 100644 --- a/homeassistant/components/isy994/translations/bg.json +++ b/homeassistant/components/isy994/translations/bg.json @@ -6,7 +6,7 @@ "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "flow_title": "{name} ({host})", diff --git a/homeassistant/components/kegtron/translations/sk.json b/homeassistant/components/kegtron/translations/sk.json new file mode 100644 index 00000000000..25ca828afe4 --- /dev/null +++ b/homeassistant/components/kegtron/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "not_supported": "Zariadenie nie je podporovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/keymitt_ble/translations/sk.json b/homeassistant/components/keymitt_ble/translations/sk.json index e8940bef26a..edfb613ff7d 100644 --- a/homeassistant/components/keymitt_ble/translations/sk.json +++ b/homeassistant/components/keymitt_ble/translations/sk.json @@ -1,5 +1,15 @@ { "config": { - "flow_title": "{name}" + "abort": { + "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "flow_title": "{name}", + "step": { + "init": { + "data": { + "address": "Adresa zariadenia" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/knx/translations/sk.json b/homeassistant/components/knx/translations/sk.json index a973f77e114..c96c541a3bf 100644 --- a/homeassistant/components/knx/translations/sk.json +++ b/homeassistant/components/knx/translations/sk.json @@ -18,6 +18,8 @@ }, "secure_tunnel_manual": { "data": { + "device_authentication": "Heslo na overenie zariadenia", + "user_id": "ID pou\u017e\u00edvate\u013ea", "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" } } @@ -25,6 +27,11 @@ }, "options": { "step": { + "connection_type": { + "data": { + "connection_type": "Typ pripojenia KNX" + } + }, "init": { "data": { "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" @@ -32,11 +39,13 @@ }, "manual_tunnel": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "port": "Port" } }, "secure_tunnel_manual": { "data": { + "user_id": "ID pou\u017e\u00edvate\u013ea", "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" } }, diff --git a/homeassistant/components/lacrosse_view/translations/bg.json b/homeassistant/components/lacrosse_view/translations/bg.json index 652dea38fcc..057f117c82a 100644 --- a/homeassistant/components/lacrosse_view/translations/bg.json +++ b/homeassistant/components/lacrosse_view/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/lametric/translations/sk.json b/homeassistant/components/lametric/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/lametric/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/landisgyr_heat_meter/translations/sk.json b/homeassistant/components/landisgyr_heat_meter/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/landisgyr_heat_meter/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/led_ble/translations/sk.json b/homeassistant/components/led_ble/translations/sk.json index e8940bef26a..6a0b9f15898 100644 --- a/homeassistant/components/led_ble/translations/sk.json +++ b/homeassistant/components/led_ble/translations/sk.json @@ -1,5 +1,9 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "not_supported": "Zariadenie nie je podporovan\u00e9" + }, "flow_title": "{name}" } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/bg.json b/homeassistant/components/lidarr/translations/bg.json index 040b54c06e1..ee48b108f6f 100644 --- a/homeassistant/components/lidarr/translations/bg.json +++ b/homeassistant/components/lidarr/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/life360/translations/bg.json b/homeassistant/components/life360/translations/bg.json index f2f11f3d4be..cf5cca16dd3 100644 --- a/homeassistant/components/life360/translations/bg.json +++ b/homeassistant/components/life360/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "create_entry": { "default": "\u0417\u0430 \u0434\u0430 \u0437\u0430\u0434\u0430\u0434\u0435\u0442\u0435 \u0440\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438 \u043e\u043f\u0446\u0438\u0438, \u0432\u0438\u0436\u0442\u0435 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043d\u0430 Life360]({docs_url})." diff --git a/homeassistant/components/lifx/translations/sk.json b/homeassistant/components/lifx/translations/sk.json index 38ce072cd6e..b0535f56098 100644 --- a/homeassistant/components/lifx/translations/sk.json +++ b/homeassistant/components/lifx/translations/sk.json @@ -1,5 +1,12 @@ { "config": { - "flow_title": "{label} ({host}) {serial}" + "flow_title": "{label} ({host}) {serial}", + "step": { + "pick_device": { + "data": { + "device": "Zariadenie" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/bg.json b/homeassistant/components/litterrobot/translations/bg.json index 7664989fae5..71ce9e1b4af 100644 --- a/homeassistant/components/litterrobot/translations/bg.json +++ b/homeassistant/components/litterrobot/translations/bg.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" diff --git a/homeassistant/components/livisi/translations/sk.json b/homeassistant/components/livisi/translations/sk.json index 5a658de5246..6ca95b4f1f6 100644 --- a/homeassistant/components/livisi/translations/sk.json +++ b/homeassistant/components/livisi/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "wrong_password": "Heslo je nespr\u00e1vne." }, "step": { diff --git a/homeassistant/components/lyric/translations/bg.json b/homeassistant/components/lyric/translations/bg.json index 5d9459cac2c..0cca3507e7f 100644 --- a/homeassistant/components/lyric/translations/bg.json +++ b/homeassistant/components/lyric/translations/bg.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "create_entry": { "default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" diff --git a/homeassistant/components/mikrotik/translations/bg.json b/homeassistant/components/mikrotik/translations/bg.json index 3316c8f5a6c..d9459868bdb 100644 --- a/homeassistant/components/mikrotik/translations/bg.json +++ b/homeassistant/components/mikrotik/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/motioneye/translations/bg.json b/homeassistant/components/motioneye/translations/bg.json index e3acc778fd3..670f394fe6c 100644 --- a/homeassistant/components/motioneye/translations/bg.json +++ b/homeassistant/components/motioneye/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/myq/translations/bg.json b/homeassistant/components/myq/translations/bg.json index f175754fdca..df3fd63febb 100644 --- a/homeassistant/components/myq/translations/bg.json +++ b/homeassistant/components/myq/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/nam/translations/bg.json b/homeassistant/components/nam/translations/bg.json index 57ae4ea05d0..69f6ab5783d 100644 --- a/homeassistant/components/nam/translations/bg.json +++ b/homeassistant/components/nam/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "device_unsupported": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430.", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "reauth_unsuccessful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430, \u043c\u043e\u043b\u044f, \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0438 \u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e." }, "error": { diff --git a/homeassistant/components/nanoleaf/translations/bg.json b/homeassistant/components/nanoleaf/translations/bg.json index 467fcb0d9bb..36fbff17d7c 100644 --- a/homeassistant/components/nanoleaf/translations/bg.json +++ b/homeassistant/components/nanoleaf/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { diff --git a/homeassistant/components/neato/translations/bg.json b/homeassistant/components/neato/translations/bg.json index e0e4b9a410d..4a31f71b97e 100644 --- a/homeassistant/components/neato/translations/bg.json +++ b/homeassistant/components/neato/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430.", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "create_entry": { "default": "\u0412\u0438\u0436\u0442\u0435 [Neato \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f]({docs_url})." diff --git a/homeassistant/components/netatmo/translations/bg.json b/homeassistant/components/netatmo/translations/bg.json index 5e771b9224b..d458bae9e8e 100644 --- a/homeassistant/components/netatmo/translations/bg.json +++ b/homeassistant/components/netatmo/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430.", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." }, "create_entry": { diff --git a/homeassistant/components/nibe_heatpump/translations/sk.json b/homeassistant/components/nibe_heatpump/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/nibe_heatpump/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nobo_hub/translations/sk.json b/homeassistant/components/nobo_hub/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/nobo_hub/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/notion/translations/bg.json b/homeassistant/components/notion/translations/bg.json index 1df2ad13a33..ddf339824c9 100644 --- a/homeassistant/components/notion/translations/bg.json +++ b/homeassistant/components/notion/translations/bg.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" diff --git a/homeassistant/components/nuki/translations/bg.json b/homeassistant/components/nuki/translations/bg.json index fcb95a304ab..d97503db4ad 100644 --- a/homeassistant/components/nuki/translations/bg.json +++ b/homeassistant/components/nuki/translations/bg.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/octoprint/translations/bg.json b/homeassistant/components/octoprint/translations/bg.json index 0635640be7d..e5c0f273c28 100644 --- a/homeassistant/components/octoprint/translations/bg.json +++ b/homeassistant/components/octoprint/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { diff --git a/homeassistant/components/oralb/translations/sk.json b/homeassistant/components/oralb/translations/sk.json new file mode 100644 index 00000000000..ba3b7cee831 --- /dev/null +++ b/homeassistant/components/oralb/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "not_supported": "Zariadenie nie je podporovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/bg.json b/homeassistant/components/overkiz/translations/bg.json index 99fe944f9cb..4f427272738 100644 --- a/homeassistant/components/overkiz/translations/bg.json +++ b/homeassistant/components/overkiz/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "reauth_wrong_account": "\u041c\u043e\u0436\u0435\u0442\u0435 \u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u0435 \u0437\u0430\u043f\u0438\u0441\u0430 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0441\u044a\u0441 \u0441\u044a\u0449\u0438\u044f Overkiz \u0430\u043a\u0430\u0443\u043d\u0442 \u0438 \u0445\u044a\u0431 " }, "error": { diff --git a/homeassistant/components/picnic/translations/bg.json b/homeassistant/components/picnic/translations/bg.json index 24fa035f619..016286c9d2c 100644 --- a/homeassistant/components/picnic/translations/bg.json +++ b/homeassistant/components/picnic/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/plex/translations/bg.json b/homeassistant/components/plex/translations/bg.json index 0e39e4b8b04..b0927813fa6 100644 --- a/homeassistant/components/plex/translations/bg.json +++ b/homeassistant/components/plex/translations/bg.json @@ -4,7 +4,7 @@ "all_configured": "\u0412\u0441\u0438\u0447\u043a\u0438 \u0441\u0432\u044a\u0440\u0437\u0430\u043d\u0438 \u0441\u044a\u0440\u0432\u044a\u0440\u0438 \u0432\u0435\u0447\u0435 \u0441\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0438", "already_configured": "\u0422\u043e\u0437\u0438 Plex \u0441\u044a\u0440\u0432\u044a\u0440 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "already_in_progress": "Plex \u0441\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "token_request_timeout": "\u0418\u0437\u0442\u0435\u0447\u0435 \u0432\u0440\u0435\u043c\u0435\u0442\u043e \u0437\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u0434 \u0437\u0430 \u0434\u043e\u0441\u0442\u044a\u043f", "unknown": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0440\u0430\u0434\u0438 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, diff --git a/homeassistant/components/prosegur/translations/bg.json b/homeassistant/components/prosegur/translations/bg.json index c00de2c8049..48c3f434084 100644 --- a/homeassistant/components/prosegur/translations/bg.json +++ b/homeassistant/components/prosegur/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/pushbullet/translations/sk.json b/homeassistant/components/pushbullet/translations/sk.json new file mode 100644 index 00000000000..65dd8989032 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/sk.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" + }, + "step": { + "user": { + "data": { + "api_key": "API k\u013e\u00fa\u010d", + "name": "N\u00e1zov" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/bg.json b/homeassistant/components/pushover/translations/bg.json index d8148026d9b..49896debf18 100644 --- a/homeassistant/components/pushover/translations/bg.json +++ b/homeassistant/components/pushover/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/pvoutput/translations/bg.json b/homeassistant/components/pvoutput/translations/bg.json index a580355fe12..8ec410d2f18 100644 --- a/homeassistant/components/pvoutput/translations/bg.json +++ b/homeassistant/components/pvoutput/translations/bg.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/qingping/translations/sk.json b/homeassistant/components/qingping/translations/sk.json new file mode 100644 index 00000000000..25ca828afe4 --- /dev/null +++ b/homeassistant/components/qingping/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "not_supported": "Zariadenie nie je podporovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/radarr/translations/bg.json b/homeassistant/components/radarr/translations/bg.json index 562883a2f23..449bb233d01 100644 --- a/homeassistant/components/radarr/translations/bg.json +++ b/homeassistant/components/radarr/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/renault/translations/bg.json b/homeassistant/components/renault/translations/bg.json index 364d397cb57..388b2dc6f8e 100644 --- a/homeassistant/components/renault/translations/bg.json +++ b/homeassistant/components/renault/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "invalid_credentials": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" diff --git a/homeassistant/components/rfxtrx/translations/sk.json b/homeassistant/components/rfxtrx/translations/sk.json index d74ce79a3d7..35f2d7628c7 100644 --- a/homeassistant/components/rfxtrx/translations/sk.json +++ b/homeassistant/components/rfxtrx/translations/sk.json @@ -6,6 +6,9 @@ "host": "Hostite\u013e", "port": "Port" } + }, + "setup_serial": { + "title": "Zariadenie" } } } diff --git a/homeassistant/components/ridwell/translations/bg.json b/homeassistant/components/ridwell/translations/bg.json index a0418dd4af0..fd738789a07 100644 --- a/homeassistant/components/ridwell/translations/bg.json +++ b/homeassistant/components/ridwell/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/samsungtv/translations/bg.json b/homeassistant/components/samsungtv/translations/bg.json index 56d85b8ff5d..f9fa03680a4 100644 --- a/homeassistant/components/samsungtv/translations/bg.json +++ b/homeassistant/components/samsungtv/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { diff --git a/homeassistant/components/scrape/translations/de.json b/homeassistant/components/scrape/translations/de.json index 17aeb0816a1..b44ee29f3d1 100644 --- a/homeassistant/components/scrape/translations/de.json +++ b/homeassistant/components/scrape/translations/de.json @@ -3,19 +3,45 @@ "abort": { "already_configured": "Konto wurde bereits konfiguriert" }, + "error": { + "resource_error": "Restdaten konnten nicht aktualisiert werden. \u00dcberpr\u00fcfe deine Konfiguration" + }, "step": { + "sensor": { + "data": { + "attribute": "Attribut", + "device_class": "Ger\u00e4teklasse", + "index": "Index", + "name": "Name", + "select": "Ausw\u00e4hlen", + "state_class": "Zustandsklasse", + "unit_of_measurement": "Ma\u00dfeinheit", + "value_template": "Wertvorlage" + }, + "data_description": { + "attribute": "Wert eines Attributs auf dem ausgew\u00e4hlten Tag abrufen", + "device_class": "Der Typ/die Klasse des Sensors, um das Symbol im Frontend festzulegen", + "index": "Definiert, welche der vom CSS-Selektor zur\u00fcckgegebenen Elemente verwendet werden sollen", + "select": "Legt fest, nach welchem Tag gesucht werden soll. Siehe Beautifulsoup CSS-Selektoren f\u00fcr Details", + "state_class": "Die state_class des Sensors", + "unit_of_measurement": "W\u00e4hle die Temperaturmessung oder erstelle deine eigene", + "value_template": "Definiert eine Vorlage, um den Zustand des Sensors zu ermitteln" + } + }, "user": { "data": { "attribute": "Attribut", - "authentication": "Authentifizierung", + "authentication": "Authentifizierungsmethode ausw\u00e4hlen", "device_class": "Ger\u00e4teklasse", "headers": "Header", "index": "Index", + "method": "Methode", "name": "Name", "password": "Passwort", "resource": "Ressource", "select": "Ausw\u00e4hlen", "state_class": "Zustandsklasse", + "timeout": "Zeit\u00fcberschreitung", "unit_of_measurement": "Ma\u00dfeinheit", "username": "Benutzername", "value_template": "Wertvorlage", @@ -30,6 +56,7 @@ "resource": "Die URL der Website, die den Wert enth\u00e4lt", "select": "Legt fest, nach welchem Tag gesucht werden soll. Siehe Beautifulsoup CSS-Selektoren f\u00fcr Details", "state_class": "Die state_class des Sensors", + "timeout": "Zeit\u00fcberschreitung f\u00fcr die Verbindung zur Website", "value_template": "Definiert eine Vorlage, um den Zustand des Sensors zu ermitteln", "verify_ssl": "Aktiviert/deaktiviert die \u00dcberpr\u00fcfung des SSL/TLS-Zertifikats, z.B. wenn es selbst signiert ist" } @@ -47,15 +74,17 @@ "init": { "data": { "attribute": "Attribut", - "authentication": "Authentifizierung", + "authentication": "Authentifizierungsmethode ausw\u00e4hlen", "device_class": "Ger\u00e4teklasse", "headers": "Header", "index": "Index", + "method": "Methode", "name": "Name", "password": "Passwort", "resource": "Ressource", "select": "Ausw\u00e4hlen", "state_class": "Zustandsklasse", + "timeout": "Zeit\u00fcberschreitung", "unit_of_measurement": "Ma\u00dfeinheit", "username": "Benutzername", "value_template": "Wertvorlage", @@ -70,6 +99,7 @@ "resource": "Die URL der Website, die den Wert enth\u00e4lt", "select": "Legt fest, nach welchem Tag gesucht werden soll. Siehe Beautifulsoup CSS-Selektoren f\u00fcr Details", "state_class": "Die state_class des Sensors", + "timeout": "Zeit\u00fcberschreitung f\u00fcr die Verbindung zur Website", "value_template": "Definiert eine Vorlage, um den Zustand des Sensors zu ermitteln", "verify_ssl": "Aktiviert/deaktiviert die \u00dcberpr\u00fcfung des SSL/TLS-Zertifikats, z.B. wenn es selbst signiert ist" } diff --git a/homeassistant/components/scrape/translations/en.json b/homeassistant/components/scrape/translations/en.json index 8cc642c12a2..042e9d913b0 100644 --- a/homeassistant/components/scrape/translations/en.json +++ b/homeassistant/components/scrape/translations/en.json @@ -30,20 +30,34 @@ }, "user": { "data": { + "attribute": "Attribute", "authentication": "Select authentication method", + "device_class": "Device Class", "headers": "Headers", + "index": "Index", "method": "Method", + "name": "Name", "password": "Password", "resource": "Resource", + "select": "Select", + "state_class": "State Class", "timeout": "Timeout", + "unit_of_measurement": "Unit of Measurement", "username": "Username", + "value_template": "Value Template", "verify_ssl": "Verify SSL certificate" }, "data_description": { + "attribute": "Get value of an attribute on the selected tag", "authentication": "Type of the HTTP authentication. Either basic or digest", + "device_class": "The type/class of the sensor to set the icon in the frontend", "headers": "Headers to use for the web request", + "index": "Defines which of the elements returned by the CSS selector to use", "resource": "The URL to the website that contains the value", + "select": "Defines what tag to search for. Check Beautifulsoup CSS selectors for details", + "state_class": "The state_class of the sensor", "timeout": "Timeout for connection to website", + "value_template": "Defines a template to get the state of the sensor", "verify_ssl": "Enables/disables verification of SSL/TLS certificate, for example if it is self-signed" } } @@ -59,20 +73,34 @@ "step": { "init": { "data": { + "attribute": "Attribute", "authentication": "Select authentication method", + "device_class": "Device Class", "headers": "Headers", + "index": "Index", "method": "Method", + "name": "Name", "password": "Password", "resource": "Resource", + "select": "Select", + "state_class": "State Class", "timeout": "Timeout", + "unit_of_measurement": "Unit of Measurement", "username": "Username", + "value_template": "Value Template", "verify_ssl": "Verify SSL certificate" }, "data_description": { + "attribute": "Get value of an attribute on the selected tag", "authentication": "Type of the HTTP authentication. Either basic or digest", + "device_class": "The type/class of the sensor to set the icon in the frontend", "headers": "Headers to use for the web request", + "index": "Defines which of the elements returned by the CSS selector to use", "resource": "The URL to the website that contains the value", + "select": "Defines what tag to search for. Check Beautifulsoup CSS selectors for details", + "state_class": "The state_class of the sensor", "timeout": "Timeout for connection to website", + "value_template": "Defines a template to get the state of the sensor", "verify_ssl": "Enables/disables verification of SSL/TLS certificate, for example if it is self-signed" } } diff --git a/homeassistant/components/sense/translations/bg.json b/homeassistant/components/sense/translations/bg.json index 91bf013c047..f93c252a662 100644 --- a/homeassistant/components/sense/translations/bg.json +++ b/homeassistant/components/sense/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" diff --git a/homeassistant/components/senseme/translations/sk.json b/homeassistant/components/senseme/translations/sk.json index 7bf403ddc3b..9dae060b4cc 100644 --- a/homeassistant/components/senseme/translations/sk.json +++ b/homeassistant/components/senseme/translations/sk.json @@ -10,6 +10,11 @@ "data": { "host": "Hostite\u013e" } + }, + "user": { + "data": { + "device": "Zariadenie" + } } } } diff --git a/homeassistant/components/sensibo/translations/bg.json b/homeassistant/components/sensibo/translations/bg.json index 6bdc0050748..3e64be8cd23 100644 --- a/homeassistant/components/sensibo/translations/bg.json +++ b/homeassistant/components/sensibo/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/sensorpro/translations/sk.json b/homeassistant/components/sensorpro/translations/sk.json new file mode 100644 index 00000000000..ba3b7cee831 --- /dev/null +++ b/homeassistant/components/sensorpro/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "not_supported": "Zariadenie nie je podporovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sharkiq/translations/bg.json b/homeassistant/components/sharkiq/translations/bg.json index 339d2106051..065933b2fb9 100644 --- a/homeassistant/components/sharkiq/translations/bg.json +++ b/homeassistant/components/sharkiq/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { diff --git a/homeassistant/components/shelly/translations/bg.json b/homeassistant/components/shelly/translations/bg.json index 36889af941c..8e3487284ef 100644 --- a/homeassistant/components/shelly/translations/bg.json +++ b/homeassistant/components/shelly/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "reauth_unsuccessful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430, \u043c\u043e\u043b\u044f, \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0438 \u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.", "unsupported_firmware": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u043d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u043d\u0430 \u0444\u044a\u0440\u043c\u0443\u0435\u0440\u0430." }, diff --git a/homeassistant/components/simplisafe/translations/bg.json b/homeassistant/components/simplisafe/translations/bg.json index fa71285d6c9..f5c6862820c 100644 --- a/homeassistant/components/simplisafe/translations/bg.json +++ b/homeassistant/components/simplisafe/translations/bg.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "identifier_exists": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0430\u043d", diff --git a/homeassistant/components/skybell/translations/bg.json b/homeassistant/components/skybell/translations/bg.json index 556fba19234..b6c96b5290e 100644 --- a/homeassistant/components/skybell/translations/bg.json +++ b/homeassistant/components/skybell/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/sleepiq/translations/bg.json b/homeassistant/components/sleepiq/translations/bg.json index e1494bd66a1..a0a6aa3235c 100644 --- a/homeassistant/components/sleepiq/translations/bg.json +++ b/homeassistant/components/sleepiq/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/sms/translations/sk.json b/homeassistant/components/sms/translations/sk.json new file mode 100644 index 00000000000..c1f2297ec7d --- /dev/null +++ b/homeassistant/components/sms/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "device": "Zariadenie" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/snooz/translations/sk.json b/homeassistant/components/snooz/translations/sk.json new file mode 100644 index 00000000000..8444b9fbeae --- /dev/null +++ b/homeassistant/components/snooz/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sonarr/translations/bg.json b/homeassistant/components/sonarr/translations/bg.json index 05d96cf621c..ac4dc5ea4bf 100644 --- a/homeassistant/components/sonarr/translations/bg.json +++ b/homeassistant/components/sonarr/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { diff --git a/homeassistant/components/steam_online/translations/bg.json b/homeassistant/components/steam_online/translations/bg.json index 5cfc98b4803..0f91d0affaa 100644 --- a/homeassistant/components/steam_online/translations/bg.json +++ b/homeassistant/components/steam_online/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/steamist/translations/sk.json b/homeassistant/components/steamist/translations/sk.json index cefae299404..c72bfbf48f2 100644 --- a/homeassistant/components/steamist/translations/sk.json +++ b/homeassistant/components/steamist/translations/sk.json @@ -4,6 +4,11 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "step": { + "pick_device": { + "data": { + "device": "Zariadenie" + } + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/switchbot/translations/sk.json b/homeassistant/components/switchbot/translations/sk.json index c9f52d5e1b6..b9e9b852d8a 100644 --- a/homeassistant/components/switchbot/translations/sk.json +++ b/homeassistant/components/switchbot/translations/sk.json @@ -13,6 +13,8 @@ }, "user": { "data": { + "address": "Adresa zariadenia", + "mac": "MAC adresa zariadenia", "name": "N\u00e1zov", "password": "Heslo" } diff --git a/homeassistant/components/synology_dsm/translations/bg.json b/homeassistant/components/synology_dsm/translations/bg.json index d0f8abc4062..038e32254e1 100644 --- a/homeassistant/components/synology_dsm/translations/bg.json +++ b/homeassistant/components/synology_dsm/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "reconfigure_successful": "\u041f\u0440\u0435\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" }, "error": { diff --git a/homeassistant/components/system_bridge/translations/bg.json b/homeassistant/components/system_bridge/translations/bg.json index 25a1b280f57..42b204c86cc 100644 --- a/homeassistant/components/system_bridge/translations/bg.json +++ b/homeassistant/components/system_bridge/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { diff --git a/homeassistant/components/tailscale/translations/bg.json b/homeassistant/components/tailscale/translations/bg.json index a580355fe12..8ec410d2f18 100644 --- a/homeassistant/components/tailscale/translations/bg.json +++ b/homeassistant/components/tailscale/translations/bg.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/tankerkoenig/translations/bg.json b/homeassistant/components/tankerkoenig/translations/bg.json index 8631e4a1daa..b33f9039b6e 100644 --- a/homeassistant/components/tankerkoenig/translations/bg.json +++ b/homeassistant/components/tankerkoenig/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" diff --git a/homeassistant/components/tautulli/translations/bg.json b/homeassistant/components/tautulli/translations/bg.json index 8f8e92fc429..9603d8e9169 100644 --- a/homeassistant/components/tautulli/translations/bg.json +++ b/homeassistant/components/tautulli/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." }, "error": { diff --git a/homeassistant/components/thermobeacon/translations/sk.json b/homeassistant/components/thermobeacon/translations/sk.json new file mode 100644 index 00000000000..25ca828afe4 --- /dev/null +++ b/homeassistant/components/thermobeacon/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "not_supported": "Zariadenie nie je podporovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/thermopro/translations/sk.json b/homeassistant/components/thermopro/translations/sk.json new file mode 100644 index 00000000000..8444b9fbeae --- /dev/null +++ b/homeassistant/components/thermopro/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tile/translations/bg.json b/homeassistant/components/tile/translations/bg.json index 08a4edb4db8..b1b1faed2fe 100644 --- a/homeassistant/components/tile/translations/bg.json +++ b/homeassistant/components/tile/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" diff --git a/homeassistant/components/tilt_ble/translations/sk.json b/homeassistant/components/tilt_ble/translations/sk.json new file mode 100644 index 00000000000..8444b9fbeae --- /dev/null +++ b/homeassistant/components/tilt_ble/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/totalconnect/translations/bg.json b/homeassistant/components/totalconnect/translations/bg.json index 8213742fc09..83bf8d48400 100644 --- a/homeassistant/components/totalconnect/translations/bg.json +++ b/homeassistant/components/totalconnect/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" diff --git a/homeassistant/components/tplink/translations/sk.json b/homeassistant/components/tplink/translations/sk.json index d12d05c0033..e4aaec5b474 100644 --- a/homeassistant/components/tplink/translations/sk.json +++ b/homeassistant/components/tplink/translations/sk.json @@ -2,6 +2,11 @@ "config": { "flow_title": "{name} {model} ({host})", "step": { + "pick_device": { + "data": { + "device": "Zariadenie" + } + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/tractive/translations/bg.json b/homeassistant/components/tractive/translations/bg.json index 0276f3b11cd..7f4cf185d8c 100644 --- a/homeassistant/components/tractive/translations/bg.json +++ b/homeassistant/components/tractive/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/trafikverket_ferry/translations/bg.json b/homeassistant/components/trafikverket_ferry/translations/bg.json index 72b2aa2cf7b..05da54a248e 100644 --- a/homeassistant/components/trafikverket_ferry/translations/bg.json +++ b/homeassistant/components/trafikverket_ferry/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/trafikverket_train/translations/bg.json b/homeassistant/components/trafikverket_train/translations/bg.json index 91bb9c04e35..86ad46927ae 100644 --- a/homeassistant/components/trafikverket_train/translations/bg.json +++ b/homeassistant/components/trafikverket_train/translations/bg.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/transmission/translations/bg.json b/homeassistant/components/transmission/translations/bg.json index 23c75c464b7..9461cd6e6a1 100644 --- a/homeassistant/components/transmission/translations/bg.json +++ b/homeassistant/components/transmission/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u0434\u0440\u0435\u0441\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d.", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u0430\u0434\u0440\u0435\u0441\u0430", diff --git a/homeassistant/components/unifi/translations/bg.json b/homeassistant/components/unifi/translations/bg.json index ebe6a494d6c..b9688a023f2 100644 --- a/homeassistant/components/unifi/translations/bg.json +++ b/homeassistant/components/unifi/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0421\u0430\u0439\u0442\u044a\u0442 \u0435 \u0432\u0435\u0447\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "faulty_credentials": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0438 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u0438 \u0434\u0430\u043d\u043d\u0438", diff --git a/homeassistant/components/upnp/translations/sk.json b/homeassistant/components/upnp/translations/sk.json index c9177d7b5d2..ebedbbac1b5 100644 --- a/homeassistant/components/upnp/translations/sk.json +++ b/homeassistant/components/upnp/translations/sk.json @@ -3,6 +3,13 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, - "flow_title": "{name}" + "flow_title": "{name}", + "step": { + "user": { + "data": { + "unique_id": "Zariadenie" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/bg.json b/homeassistant/components/uptimerobot/translations/bg.json index d75409d8ee1..d729a88dbc0 100644 --- a/homeassistant/components/uptimerobot/translations/bg.json +++ b/homeassistant/components/uptimerobot/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { diff --git a/homeassistant/components/verisure/translations/bg.json b/homeassistant/components/verisure/translations/bg.json index 927c79f2674..6411c970f11 100644 --- a/homeassistant/components/verisure/translations/bg.json +++ b/homeassistant/components/verisure/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" diff --git a/homeassistant/components/vizio/translations/sk.json b/homeassistant/components/vizio/translations/sk.json index 6c864220193..4839b5767ba 100644 --- a/homeassistant/components/vizio/translations/sk.json +++ b/homeassistant/components/vizio/translations/sk.json @@ -4,6 +4,7 @@ "user": { "data": { "access_token": "Pr\u00edstupov\u00fd token", + "device_class": "Typ zariadenia", "host": "Hostite\u013e", "name": "N\u00e1zov" } diff --git a/homeassistant/components/vlc_telnet/translations/bg.json b/homeassistant/components/vlc_telnet/translations/bg.json index 41e4a0484f6..5d7713be732 100644 --- a/homeassistant/components/vlc_telnet/translations/bg.json +++ b/homeassistant/components/vlc_telnet/translations/bg.json @@ -4,7 +4,7 @@ "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { diff --git a/homeassistant/components/volvooncall/translations/bg.json b/homeassistant/components/volvooncall/translations/bg.json index 62a0a14568d..aaa9b046b2d 100644 --- a/homeassistant/components/volvooncall/translations/bg.json +++ b/homeassistant/components/volvooncall/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/wallbox/translations/bg.json b/homeassistant/components/wallbox/translations/bg.json index cc0ea3ece2d..b221ff4ddb8 100644 --- a/homeassistant/components/wallbox/translations/bg.json +++ b/homeassistant/components/wallbox/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/watttime/translations/bg.json b/homeassistant/components/watttime/translations/bg.json index 0ce2b541513..62b9a11a068 100644 --- a/homeassistant/components/watttime/translations/bg.json +++ b/homeassistant/components/watttime/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/wiz/translations/sk.json b/homeassistant/components/wiz/translations/sk.json index 73be7f26f22..27fdc258e0a 100644 --- a/homeassistant/components/wiz/translations/sk.json +++ b/homeassistant/components/wiz/translations/sk.json @@ -5,6 +5,11 @@ }, "flow_title": "{name} ({host})", "step": { + "pick_device": { + "data": { + "device": "Zariadenie" + } + }, "user": { "data": { "host": "IP adresa" diff --git a/homeassistant/components/wolflink/translations/sk.json b/homeassistant/components/wolflink/translations/sk.json index 1b1e671c054..23825cbb7ba 100644 --- a/homeassistant/components/wolflink/translations/sk.json +++ b/homeassistant/components/wolflink/translations/sk.json @@ -4,6 +4,11 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "device": { + "data": { + "device_name": "Zariadenie" + } + }, "user": { "data": { "password": "Heslo" diff --git a/homeassistant/components/xiaomi_miio/translations/bg.json b/homeassistant/components/xiaomi_miio/translations/bg.json index 2ad6a9dda26..606f160f7e9 100644 --- a/homeassistant/components/xiaomi_miio/translations/bg.json +++ b/homeassistant/components/xiaomi_miio/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { diff --git a/homeassistant/components/yale_smart_alarm/translations/bg.json b/homeassistant/components/yale_smart_alarm/translations/bg.json index ecab0c9bc29..8237e72bb8d 100644 --- a/homeassistant/components/yale_smart_alarm/translations/bg.json +++ b/homeassistant/components/yale_smart_alarm/translations/bg.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "\u041f\u0440\u043e\u0444\u0438\u043b\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/yeelight/translations/sk.json b/homeassistant/components/yeelight/translations/sk.json index 2cf44c3ea29..96cd70f97aa 100644 --- a/homeassistant/components/yeelight/translations/sk.json +++ b/homeassistant/components/yeelight/translations/sk.json @@ -5,6 +5,11 @@ }, "flow_title": "{model} {id} ({host})", "step": { + "pick_device": { + "data": { + "device": "Zariadenie" + } + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/yolink/translations/bg.json b/homeassistant/components/yolink/translations/bg.json index 5f7e924f493..85093bfd348 100644 --- a/homeassistant/components/yolink/translations/bg.json +++ b/homeassistant/components/yolink/translations/bg.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430.", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "create_entry": { "default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" diff --git a/homeassistant/components/zamg/translations/sk.json b/homeassistant/components/zamg/translations/sk.json index e8940bef26a..6408ace0719 100644 --- a/homeassistant/components/zamg/translations/sk.json +++ b/homeassistant/components/zamg/translations/sk.json @@ -1,5 +1,12 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{name}" } } \ No newline at end of file From dfed57ed4da04817358f47b8eea95ebdd8f8fb36 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Mon, 21 Nov 2022 19:39:34 -0500 Subject: [PATCH 0609/1033] Bump ZHA dependencies (#82509) * Bump ZHA dependencies * Use the corrected `TypeValue` keyword argument name in unit tests --- homeassistant/components/zha/manifest.json | 8 ++++---- requirements_all.txt | 8 ++++---- requirements_test_all.txt | 8 ++++---- tests/components/zha/common.py | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 312b93aff6f..d2b19fe8893 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -4,12 +4,12 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zha", "requirements": [ - "bellows==0.34.2", + "bellows==0.34.4", "pyserial==3.5", "pyserial-asyncio==0.6", - "zha-quirks==0.0.86", - "zigpy-deconz==0.19.0", - "zigpy==0.51.5", + "zha-quirks==0.0.87", + "zigpy-deconz==0.19.1", + "zigpy==0.51.6", "zigpy-xbee==0.16.2", "zigpy-zigate==0.10.3", "zigpy-znp==0.9.1" diff --git a/requirements_all.txt b/requirements_all.txt index 340a1ddf790..ebc20fec753 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -413,7 +413,7 @@ beautifulsoup4==4.11.1 # beewi_smartclim==0.0.10 # homeassistant.components.zha -bellows==0.34.2 +bellows==0.34.4 # homeassistant.components.bmw_connected_drive bimmer_connected==0.10.4 @@ -2630,7 +2630,7 @@ zengge==0.2 zeroconf==0.39.4 # homeassistant.components.zha -zha-quirks==0.0.86 +zha-quirks==0.0.87 # homeassistant.components.zhong_hong zhong_hong_hvac==1.0.9 @@ -2639,7 +2639,7 @@ zhong_hong_hvac==1.0.9 ziggo-mediabox-xl==1.1.0 # homeassistant.components.zha -zigpy-deconz==0.19.0 +zigpy-deconz==0.19.1 # homeassistant.components.zha zigpy-xbee==0.16.2 @@ -2651,7 +2651,7 @@ zigpy-zigate==0.10.3 zigpy-znp==0.9.1 # homeassistant.components.zha -zigpy==0.51.5 +zigpy==0.51.6 # homeassistant.components.zoneminder zm-py==0.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ef6a7527f1a..d34101dc73b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -340,7 +340,7 @@ base36==0.1.1 beautifulsoup4==4.11.1 # homeassistant.components.zha -bellows==0.34.2 +bellows==0.34.4 # homeassistant.components.bmw_connected_drive bimmer_connected==0.10.4 @@ -1831,10 +1831,10 @@ zamg==0.1.1 zeroconf==0.39.4 # homeassistant.components.zha -zha-quirks==0.0.86 +zha-quirks==0.0.87 # homeassistant.components.zha -zigpy-deconz==0.19.0 +zigpy-deconz==0.19.1 # homeassistant.components.zha zigpy-xbee==0.16.2 @@ -1846,7 +1846,7 @@ zigpy-zigate==0.10.3 zigpy-znp==0.9.1 # homeassistant.components.zha -zigpy==0.51.5 +zigpy==0.51.6 # homeassistant.components.zwave_js zwave-js-server-python==0.43.0 diff --git a/tests/components/zha/common.py b/tests/components/zha/common.py index 56197fa39ec..a6a533acc71 100644 --- a/tests/components/zha/common.py +++ b/tests/components/zha/common.py @@ -35,7 +35,7 @@ def patch_cluster(cluster): zcl_f.ReadAttributeRecord( attr_id, zcl_f.Status.SUCCESS, - zcl_f.TypeValue(python_type=None, value=value), + zcl_f.TypeValue(type=None, value=value), ) ) else: From a7caa038be2c0b05b53756ba6c9563854b2ca1ea Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 21 Nov 2022 20:23:07 -0600 Subject: [PATCH 0610/1033] Accept advertisements from alternate scanners when a scanner stops scanning (#82448) --- .../components/bluetooth/__init__.py | 2 + homeassistant/components/bluetooth/api.py | 13 +++ .../components/bluetooth/base_scanner.py | 35 +++++- homeassistant/components/bluetooth/manager.py | 101 +++++++++++------- homeassistant/components/bluetooth/scanner.py | 8 +- .../components/esphome/bluetooth/__init__.py | 4 +- .../components/esphome/bluetooth/client.py | 19 ++-- .../components/shelly/bluetooth/__init__.py | 5 +- .../bluetooth/test_advertisement_tracker.py | 2 +- tests/components/bluetooth/test_api.py | 16 +++ .../components/bluetooth/test_base_scanner.py | 70 ++++++++++-- tests/components/bluetooth/test_init.py | 4 +- tests/components/bluetooth/test_manager.py | 60 ++++++++++- tests/components/bluetooth/test_models.py | 4 +- 14 files changed, 275 insertions(+), 68 deletions(-) create mode 100644 tests/components/bluetooth/test_api.py diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index 278b88364f1..be2d0d36fd7 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -51,6 +51,7 @@ from .api import ( async_rediscover_address, async_register_callback, async_register_scanner, + async_scanner_by_source, async_scanner_count, async_track_unavailable, ) @@ -86,6 +87,7 @@ __all__ = [ "async_register_callback", "async_register_scanner", "async_track_unavailable", + "async_scanner_by_source", "async_scanner_count", "BaseHaScanner", "BaseHaRemoteScanner", diff --git a/homeassistant/components/bluetooth/api.py b/homeassistant/components/bluetooth/api.py index 1399dd84dc6..582370ffbda 100644 --- a/homeassistant/components/bluetooth/api.py +++ b/homeassistant/components/bluetooth/api.py @@ -44,6 +44,19 @@ def async_get_scanner(hass: HomeAssistant) -> HaBleakScannerWrapper: return HaBleakScannerWrapper() +@hass_callback +def async_scanner_by_source(hass: HomeAssistant, source: str) -> BaseHaScanner | None: + """Return a scanner for a given source. + + This method is only intended to be used by integrations that implement + a bluetooth client and need to interact with a scanner directly. + + It is not intended to be used by integrations that need to interact + with a device. + """ + return _get_manager(hass).async_scanner_by_source(source) + + @hass_callback def async_scanner_count(hass: HomeAssistant, connectable: bool = True) -> int: """Return the number of scanners currently in use.""" diff --git a/homeassistant/components/bluetooth/base_scanner.py b/homeassistant/components/bluetooth/base_scanner.py index e92725994be..88332c79667 100644 --- a/homeassistant/components/bluetooth/base_scanner.py +++ b/homeassistant/components/bluetooth/base_scanner.py @@ -2,7 +2,8 @@ from __future__ import annotations from abc import abstractmethod -from collections.abc import Callable +from collections.abc import Callable, Generator +from contextlib import contextmanager import datetime from datetime import timedelta from typing import Any, Final @@ -10,6 +11,7 @@ from typing import Any, Final from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData from bleak_retry_connector import NO_RSSI_VALUE +from bluetooth_adapters import adapter_human_name from home_assistant_bluetooth import BluetoothServiceInfoBleak from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback @@ -28,10 +30,26 @@ MONOTONIC_TIME: Final = monotonic_time_coarse class BaseHaScanner: """Base class for Ha Scanners.""" - def __init__(self, hass: HomeAssistant, source: str) -> None: + __slots__ = ("hass", "source", "_connecting", "name", "scanning") + + def __init__(self, hass: HomeAssistant, source: str, adapter: str) -> None: """Initialize the scanner.""" self.hass = hass self.source = source + self._connecting = 0 + self.name = adapter_human_name(adapter, source) if adapter != source else source + self.scanning = True + + @contextmanager + def connecting(self) -> Generator[None, None, None]: + """Context manager to track connecting state.""" + self._connecting += 1 + self.scanning = not self._connecting + try: + yield + finally: + self._connecting -= 1 + self.scanning = not self._connecting @property @abstractmethod @@ -62,16 +80,27 @@ class BaseHaScanner: class BaseHaRemoteScanner(BaseHaScanner): """Base class for a Home Assistant remote BLE scanner.""" + __slots__ = ( + "_new_info_callback", + "_discovered_device_advertisement_datas", + "_discovered_device_timestamps", + "_connector", + "_connectable", + "_details", + "_expire_seconds", + ) + def __init__( self, hass: HomeAssistant, scanner_id: str, + name: str, new_info_callback: Callable[[BluetoothServiceInfoBleak], None], connector: HaBluetoothConnector, connectable: bool, ) -> None: """Initialize the scanner.""" - super().__init__(hass, scanner_id) + super().__init__(hass, scanner_id, name) self._new_info_callback = new_info_callback self._discovered_device_advertisement_datas: dict[ str, tuple[BLEDevice, AdvertisementData] diff --git a/homeassistant/components/bluetooth/manager.py b/homeassistant/components/bluetooth/manager.py index 65f8333304b..534bd636355 100644 --- a/homeassistant/components/bluetooth/manager.py +++ b/homeassistant/components/bluetooth/manager.py @@ -126,7 +126,7 @@ class BluetoothManager: self._non_connectable_scanners: list[BaseHaScanner] = [] self._connectable_scanners: list[BaseHaScanner] = [] self._adapters: dict[str, AdapterDetails] = {} - self._sources: set[str] = set() + self._sources: dict[str, BaseHaScanner] = {} self._bluetooth_adapters = bluetooth_adapters @property @@ -169,6 +169,11 @@ class BluetoothManager: return adapter return None + @hass_callback + def async_scanner_by_source(self, source: str) -> BaseHaScanner | None: + """Return the scanner for a source.""" + return self._sources.get(source) + async def async_get_bluetooth_adapters( self, cached: bool = True ) -> dict[str, AdapterDetails]: @@ -302,6 +307,7 @@ class BluetoothManager: self, old: BluetoothServiceInfoBleak, new: BluetoothServiceInfoBleak, + debug: bool, ) -> bool: """Prefer previous advertisement from a different source if it is better.""" if new.time - old.time > ( @@ -310,34 +316,32 @@ class BluetoothManager: ) ): # If the old advertisement is stale, any new advertisement is preferred - _LOGGER.debug( - "%s (%s): Switching from %s[%s] to %s[%s] (time elapsed:%s > stale seconds:%s)", - new.name, - new.address, - old.source, - old.connectable, - new.source, - new.connectable, - new.time - old.time, - stale_seconds, - ) + if debug: + _LOGGER.debug( + "%s (%s): Switching from %s to %s (time elapsed:%s > stale seconds:%s)", + new.name, + new.address, + self._async_describe_source(old), + self._async_describe_source(new), + new.time - old.time, + stale_seconds, + ) return False if (new.rssi or NO_RSSI_VALUE) - RSSI_SWITCH_THRESHOLD > ( old.rssi or NO_RSSI_VALUE ): # If new advertisement is RSSI_SWITCH_THRESHOLD more, the new one is preferred - _LOGGER.debug( - "%s (%s): Switching from %s[%s] to %s[%s] (new rssi:%s - threshold:%s > old rssi:%s)", - new.name, - new.address, - old.source, - old.connectable, - new.source, - new.connectable, - new.rssi, - RSSI_SWITCH_THRESHOLD, - old.rssi, - ) + if debug: + _LOGGER.debug( + "%s (%s): Switching from %s to %s (new rssi:%s - threshold:%s > old rssi:%s)", + new.name, + new.address, + self._async_describe_source(old), + self._async_describe_source(new), + new.rssi, + RSSI_SWITCH_THRESHOLD, + old.rssi, + ) return False return True @@ -367,6 +371,7 @@ class BluetoothManager: connectable_history = self._connectable_history source = service_info.source + debug = _LOGGER.isEnabledFor(logging.DEBUG) # This logic is complex due to the many combinations of scanners that are supported. # # We need to handle multiple connectable and non-connectable scanners @@ -384,9 +389,10 @@ class BluetoothManager: if ( (old_service_info := all_history.get(address)) and source != old_service_info.source - and old_service_info.source in self._sources + and (scanner := self._sources.get(old_service_info.source)) + and scanner.scanning and self._prefer_previous_adv_from_different_source( - old_service_info, service_info + old_service_info, service_info, debug ) ): # If we are rejecting the new advertisement and the device is connectable @@ -404,9 +410,14 @@ class BluetoothManager: # the old connectable advertisement or ( source != old_connectable_service_info.source - and old_connectable_service_info.source in self._sources + and ( + connectable_scanner := self._sources.get( + old_connectable_service_info.source + ) + ) + and connectable_scanner.scanning and self._prefer_previous_adv_from_different_source( - old_connectable_service_info, service_info + old_connectable_service_info, service_info, debug ) ) ): @@ -461,15 +472,14 @@ class BluetoothManager: ) matched_domains = self._integration_matcher.match_domains(service_info) - _LOGGER.debug( - "%s: %s %s connectable: %s match: %s rssi: %s", - source, - address, - advertisement_data, - connectable, - matched_domains, - advertisement_data.rssi, - ) + if debug: + _LOGGER.debug( + "%s: %s %s match: %s", + self._async_describe_source(service_info), + address, + advertisement_data, + matched_domains, + ) if is_connectable_by_any_source: # Bleak callbacks must get a connectable device @@ -491,6 +501,17 @@ class BluetoothManager: service_info, ) + @hass_callback + def _async_describe_source(self, service_info: BluetoothServiceInfoBleak) -> str: + """Describe a source.""" + if scanner := self._sources.get(service_info.source): + description = scanner.name + else: + description = service_info.source + if service_info.connectable: + description += " [connectable]" + return description + @hass_callback def async_track_unavailable( self, @@ -611,15 +632,17 @@ class BluetoothManager: self, scanner: BaseHaScanner, connectable: bool ) -> CALLBACK_TYPE: """Register a new scanner.""" + _LOGGER.debug("Registering scanner %s", scanner.name) scanners = self._get_scanners_by_type(connectable) def _unregister_scanner() -> None: + _LOGGER.debug("Unregistering scanner %s", scanner.name) self._advertisement_tracker.async_remove_source(scanner.source) scanners.remove(scanner) - self._sources.remove(scanner.source) + del self._sources[scanner.source] scanners.append(scanner) - self._sources.add(scanner.source) + self._sources[scanner.source] = scanner return _unregister_scanner @hass_callback diff --git a/homeassistant/components/bluetooth/scanner.py b/homeassistant/components/bluetooth/scanner.py index 3ea7b1b8622..6f61e16c583 100644 --- a/homeassistant/components/bluetooth/scanner.py +++ b/homeassistant/components/bluetooth/scanner.py @@ -16,7 +16,7 @@ from bleak.backends.bluezdbus.advertisement_monitor import OrPattern from bleak.backends.bluezdbus.scanner import BlueZScannerArgs from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData, AdvertisementDataCallback -from bluetooth_adapters import DEFAULT_ADDRESS, adapter_human_name +from bluetooth_adapters import DEFAULT_ADDRESS from dbus_fast import InvalidMessageError from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback @@ -130,7 +130,7 @@ class HaScanner(BaseHaScanner): ) -> None: """Init bluetooth discovery.""" source = address if address != DEFAULT_ADDRESS else adapter or SOURCE_LOCAL - super().__init__(hass, source) + super().__init__(hass, source, adapter) self.mode = mode self.adapter = adapter self._start_stop_lock = asyncio.Lock() @@ -138,7 +138,7 @@ class HaScanner(BaseHaScanner): self._last_detection = 0.0 self._start_time = 0.0 self._new_info_callback = new_info_callback - self.name = adapter_human_name(adapter, address) + self.scanning = False @property def discovered_devices(self) -> list[BLEDevice]: @@ -312,6 +312,7 @@ class HaScanner(BaseHaScanner): # Everything is fine, break out of the loop break + self.scanning = True self._async_setup_scanner_watchdog() @hass_callback @@ -385,6 +386,7 @@ class HaScanner(BaseHaScanner): async def _async_stop_scanner(self) -> None: """Stop bluetooth discovery under the lock.""" + self.scanning = False _LOGGER.debug("%s: Stopping bluetooth discovery", self.name) try: await self.scanner.stop() # type: ignore[no-untyped-call] diff --git a/homeassistant/components/esphome/bluetooth/__init__.py b/homeassistant/components/esphome/bluetooth/__init__.py index b5be5362474..4b6281c7c5a 100644 --- a/homeassistant/components/esphome/bluetooth/__init__.py +++ b/homeassistant/components/esphome/bluetooth/__init__.py @@ -67,7 +67,9 @@ async def async_connect_scanner( source=source, can_connect=_async_can_connect_factory(entry_data, source), ) - scanner = ESPHomeScanner(hass, source, new_info_callback, connector, connectable) + scanner = ESPHomeScanner( + hass, source, entry.title, new_info_callback, connector, connectable + ) unload_callbacks = [ async_register_scanner(hass, scanner, connectable), scanner.async_setup(), diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index 6672b17d8ba..666700bfc86 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -21,6 +21,7 @@ from bleak.backends.device import BLEDevice from bleak.backends.service import BleakGATTServiceCollection from bleak.exc import BleakError +from homeassistant.components.bluetooth import async_scanner_by_source from homeassistant.core import CALLBACK_TYPE from ..domain_data import DomainData @@ -119,11 +120,12 @@ class ESPHomeClient(BaseBleakClient): """Initialize the ESPHomeClient.""" assert isinstance(address_or_ble_device, BLEDevice) super().__init__(address_or_ble_device, *args, **kwargs) + self._hass = kwargs["hass"] self._ble_device = address_or_ble_device self._address_as_int = mac_to_int(self._ble_device.address) assert self._ble_device.details is not None self._source = self._ble_device.details["source"] - self.domain_data = DomainData.get(kwargs["hass"]) + self.domain_data = DomainData.get(self._hass) config_entry = self.domain_data.get_by_unique_id(self._source) self.entry_data = self.domain_data.get_entry_data(config_entry) self._client = self.entry_data.client @@ -257,12 +259,15 @@ class ESPHomeClient(BaseBleakClient): connected_future.set_result(connected) timeout = kwargs.get("timeout", self._timeout) - self._cancel_connection_state = await self._client.bluetooth_device_connect( - self._address_as_int, - _on_bluetooth_connection_state, - timeout=timeout, - ) - await connected_future + if not (scanner := async_scanner_by_source(self._hass, self._source)): + raise BleakError("Scanner disappeared for {self._source}") + with scanner.connecting(): + self._cancel_connection_state = await self._client.bluetooth_device_connect( + self._address_as_int, + _on_bluetooth_connection_state, + timeout=timeout, + ) + await connected_future await self.get_services(dangerous_use_bleak_cache=dangerous_use_bleak_cache) self._disconnected_event = asyncio.Event() return True diff --git a/homeassistant/components/shelly/bluetooth/__init__.py b/homeassistant/components/shelly/bluetooth/__init__.py index 5e7f1c0ad4f..429fae1a9a1 100644 --- a/homeassistant/components/shelly/bluetooth/__init__.py +++ b/homeassistant/components/shelly/bluetooth/__init__.py @@ -34,6 +34,7 @@ async def async_connect_scanner( ) -> CALLBACK_TYPE: """Connect scanner.""" device = coordinator.device + entry = coordinator.entry source = format_mac(coordinator.mac).upper() new_info_callback = async_get_advertisement_callback(hass) connector = HaBluetoothConnector( @@ -42,7 +43,9 @@ async def async_connect_scanner( source=source, can_connect=lambda: False, ) - scanner = ShellyBLEScanner(hass, source, new_info_callback, connector, False) + scanner = ShellyBLEScanner( + hass, source, entry.title, new_info_callback, connector, False + ) unload_callbacks = [ async_register_scanner(hass, scanner, False), scanner.async_setup(), diff --git a/tests/components/bluetooth/test_advertisement_tracker.py b/tests/components/bluetooth/test_advertisement_tracker.py index dd52ba38a47..29787f92910 100644 --- a/tests/components/bluetooth/test_advertisement_tracker.py +++ b/tests/components/bluetooth/test_advertisement_tracker.py @@ -314,7 +314,7 @@ async def test_advertisment_interval_longer_than_adapter_stack_timeout_adapter_c """Return a list of discovered devices.""" return {} - scanner = FakeScanner(hass, "new") + scanner = FakeScanner(hass, "new", "fake_adapter") cancel_scanner = async_register_scanner(hass, scanner, False) @callback diff --git a/tests/components/bluetooth/test_api.py b/tests/components/bluetooth/test_api.py new file mode 100644 index 00000000000..b1d7e68972b --- /dev/null +++ b/tests/components/bluetooth/test_api.py @@ -0,0 +1,16 @@ +"""Tests for the Bluetooth integration API.""" + + +from homeassistant.components import bluetooth +from homeassistant.components.bluetooth import BaseHaScanner, async_scanner_by_source + + +async def test_scanner_by_source(hass, enable_bluetooth): + """Test we can get a scanner by source.""" + + hci2_scanner = BaseHaScanner(hass, "hci2", "hci2") + cancel_hci2 = bluetooth.async_register_scanner(hass, hci2_scanner, True) + + assert async_scanner_by_source(hass, "hci2") is hci2_scanner + cancel_hci2() + assert async_scanner_by_source(hass, "hci2") is None diff --git a/tests/components/bluetooth/test_base_scanner.py b/tests/components/bluetooth/test_base_scanner.py index 9808bb74bc6..a4042ada005 100644 --- a/tests/components/bluetooth/test_base_scanner.py +++ b/tests/components/bluetooth/test_base_scanner.py @@ -20,7 +20,7 @@ from . import MockBleakClient, _get_manager, generate_advertisement_data from tests.common import async_fire_time_changed -async def test_remote_scanner(hass): +async def test_remote_scanner(hass, enable_bluetooth): """Test the remote scanner base class merges advertisement_data.""" manager = _get_manager() @@ -70,7 +70,7 @@ async def test_remote_scanner(hass): connector = ( HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), ) - scanner = FakeScanner(hass, "esp32", new_info_callback, connector, True) + scanner = FakeScanner(hass, "esp32", "esp32", new_info_callback, connector, True) scanner.async_setup() cancel = manager.async_register_scanner(scanner, True) @@ -104,7 +104,7 @@ async def test_remote_scanner(hass): cancel() -async def test_remote_scanner_expires_connectable(hass): +async def test_remote_scanner_expires_connectable(hass, enable_bluetooth): """Test the remote scanner expires stale connectable data.""" manager = _get_manager() @@ -140,7 +140,7 @@ async def test_remote_scanner_expires_connectable(hass): connector = ( HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), ) - scanner = FakeScanner(hass, "esp32", new_info_callback, connector, True) + scanner = FakeScanner(hass, "esp32", "esp32", new_info_callback, connector, True) scanner.async_setup() cancel = manager.async_register_scanner(scanner, True) @@ -174,7 +174,7 @@ async def test_remote_scanner_expires_connectable(hass): cancel() -async def test_remote_scanner_expires_non_connectable(hass): +async def test_remote_scanner_expires_non_connectable(hass, enable_bluetooth): """Test the remote scanner expires stale non connectable data.""" manager = _get_manager() @@ -210,7 +210,7 @@ async def test_remote_scanner_expires_non_connectable(hass): connector = ( HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), ) - scanner = FakeScanner(hass, "esp32", new_info_callback, connector, False) + scanner = FakeScanner(hass, "esp32", "esp32", new_info_callback, connector, False) scanner.async_setup() cancel = manager.async_register_scanner(scanner, True) @@ -265,3 +265,61 @@ async def test_remote_scanner_expires_non_connectable(hass): assert len(scanner.discovered_devices_and_advertisement_data) == 0 cancel() + + +async def test_base_scanner_connecting_behavior(hass, enable_bluetooth): + """Test that the default behavior is to mark the scanner as not scanning when connecting.""" + manager = _get_manager() + + switchbot_device = BLEDevice( + "44:44:33:11:23:45", + "wohand", + {}, + rssi=-100, + ) + switchbot_device_adv = generate_advertisement_data( + local_name="wohand", + service_uuids=[], + manufacturer_data={1: b"\x01"}, + rssi=-100, + ) + + class FakeScanner(BaseHaRemoteScanner): + def inject_advertisement( + self, device: BLEDevice, advertisement_data: AdvertisementData + ) -> None: + """Inject an advertisement.""" + self._async_on_advertisement( + device.address, + advertisement_data.rssi, + device.name, + advertisement_data.service_uuids, + advertisement_data.service_data, + advertisement_data.manufacturer_data, + advertisement_data.tx_power, + ) + + new_info_callback = manager.scanner_adv_received + connector = ( + HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), + ) + scanner = FakeScanner(hass, "esp32", "esp32", new_info_callback, connector, False) + scanner.async_setup() + cancel = manager.async_register_scanner(scanner, True) + + with scanner.connecting(): + assert scanner.scanning is False + + # We should still accept new advertisements while connecting + # since advertisements are delivered asynchronously and + # we don't want to miss any even when we are willing to + # accept advertisements from another scanner in the brief window + # between when we start connecting and when we stop scanning + scanner.inject_advertisement(switchbot_device, switchbot_device_adv) + + devices = scanner.discovered_devices + assert len(scanner.discovered_devices) == 1 + assert len(scanner.discovered_devices_and_advertisement_data) == 1 + assert devices[0].name == "wohand" + + cancel() diff --git a/tests/components/bluetooth/test_init.py b/tests/components/bluetooth/test_init.py index a6f594669ca..9af1c0f313b 100644 --- a/tests/components/bluetooth/test_init.py +++ b/tests/components/bluetooth/test_init.py @@ -2649,7 +2649,7 @@ async def test_getting_the_scanner_returns_the_wrapped_instance(hass, enable_blu async def test_scanner_count_connectable(hass, enable_bluetooth): """Test getting the connectable scanner count.""" - scanner = BaseHaScanner(hass, "any") + scanner = BaseHaScanner(hass, "any", "any") cancel = bluetooth.async_register_scanner(hass, scanner, False) assert bluetooth.async_scanner_count(hass, connectable=True) == 1 cancel() @@ -2657,7 +2657,7 @@ async def test_scanner_count_connectable(hass, enable_bluetooth): async def test_scanner_count(hass, enable_bluetooth): """Test getting the connectable and non-connectable scanner count.""" - scanner = BaseHaScanner(hass, "any") + scanner = BaseHaScanner(hass, "any", "any") cancel = bluetooth.async_register_scanner(hass, scanner, False) assert bluetooth.async_scanner_count(hass, connectable=False) == 2 cancel() diff --git a/tests/components/bluetooth/test_manager.py b/tests/components/bluetooth/test_manager.py index a7ea4fefe6b..e295291068a 100644 --- a/tests/components/bluetooth/test_manager.py +++ b/tests/components/bluetooth/test_manager.py @@ -26,7 +26,8 @@ from . import ( @pytest.fixture def register_hci0_scanner(hass: HomeAssistant) -> None: """Register an hci0 scanner.""" - cancel = bluetooth.async_register_scanner(hass, BaseHaScanner(hass, "hci0"), True) + hci0_scanner = BaseHaScanner(hass, "hci0", "hci0") + cancel = bluetooth.async_register_scanner(hass, hci0_scanner, True) yield cancel() @@ -34,7 +35,8 @@ def register_hci0_scanner(hass: HomeAssistant) -> None: @pytest.fixture def register_hci1_scanner(hass: HomeAssistant) -> None: """Register an hci1 scanner.""" - cancel = bluetooth.async_register_scanner(hass, BaseHaScanner(hass, "hci1"), True) + hci1_scanner = BaseHaScanner(hass, "hci1", "hci1") + cancel = bluetooth.async_register_scanner(hass, hci1_scanner, True) yield cancel() @@ -416,7 +418,7 @@ async def test_switching_adapters_when_one_goes_away( ): """Test switching adapters when one goes away.""" cancel_hci2 = bluetooth.async_register_scanner( - hass, BaseHaScanner(hass, "hci2"), True + hass, BaseHaScanner(hass, "hci2", "hci2"), True ) address = "44:44:33:11:23:45" @@ -460,3 +462,55 @@ async def test_switching_adapters_when_one_goes_away( bluetooth.async_ble_device_from_address(hass, address) is switchbot_device_poor_signal ) + + +async def test_switching_adapters_when_one_stop_scanning( + hass, enable_bluetooth, register_hci0_scanner +): + """Test switching adapters when stops scanning.""" + hci2_scanner = BaseHaScanner(hass, "hci2", "hci2") + cancel_hci2 = bluetooth.async_register_scanner(hass, hci2_scanner, True) + + address = "44:44:33:11:23:45" + + switchbot_device_good_signal = BLEDevice(address, "wohand_good_signal") + switchbot_adv_good_signal = generate_advertisement_data( + local_name="wohand_good_signal", service_uuids=[], rssi=-60 + ) + inject_advertisement_with_source( + hass, switchbot_device_good_signal, switchbot_adv_good_signal, "hci2" + ) + + assert ( + bluetooth.async_ble_device_from_address(hass, address) + is switchbot_device_good_signal + ) + + switchbot_device_poor_signal = BLEDevice(address, "wohand_poor_signal") + switchbot_adv_poor_signal = generate_advertisement_data( + local_name="wohand_poor_signal", service_uuids=[], rssi=-100 + ) + inject_advertisement_with_source( + hass, switchbot_device_poor_signal, switchbot_adv_poor_signal, "hci0" + ) + + # We want to prefer the good signal when we have options + assert ( + bluetooth.async_ble_device_from_address(hass, address) + is switchbot_device_good_signal + ) + + hci2_scanner.scanning = False + + inject_advertisement_with_source( + hass, switchbot_device_poor_signal, switchbot_adv_poor_signal, "hci0" + ) + + # Now that hci2 has stopped scanning, we should prefer the poor signal + # since poor signal is better than no signal + assert ( + bluetooth.async_ble_device_from_address(hass, address) + is switchbot_device_poor_signal + ) + + cancel_hci2() diff --git a/tests/components/bluetooth/test_models.py b/tests/components/bluetooth/test_models.py index 01245d93184..05f67b97daa 100644 --- a/tests/components/bluetooth/test_models.py +++ b/tests/components/bluetooth/test_models.py @@ -200,7 +200,7 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab return switchbot_proxy_device_has_connection_slot return None - scanner = FakeScanner(hass, "esp32") + scanner = FakeScanner(hass, "esp32", "esp32") cancel = manager.async_register_scanner(scanner, True) assert manager.async_discovered_devices(True) == [ switchbot_proxy_device_no_connection_slot @@ -306,7 +306,7 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab return switchbot_proxy_device_has_connection_slot return None - scanner = FakeScanner(hass, "esp32") + scanner = FakeScanner(hass, "esp32", "esp32") cancel = manager.async_register_scanner(scanner, True) assert manager.async_discovered_devices(True) == [ switchbot_proxy_device_no_connection_slot From 7df711f1f3208476ee604125e475fba7f95dd653 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 21 Nov 2022 20:24:35 -0600 Subject: [PATCH 0611/1033] Show HomeKit Controller unhandled pairing error reason in the UI (#82505) --- .../homekit_controller/config_flow.py | 15 ++++-- .../homekit_controller/strings.json | 2 +- .../homekit_controller/translations/en.json | 2 +- .../homekit_controller/test_config_flow.py | 52 +++++++++++++++++++ 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/homekit_controller/config_flow.py b/homeassistant/components/homekit_controller/config_flow.py index da4ccfe9f9a..10d3ed0cfa8 100644 --- a/homeassistant/components/homekit_controller/config_flow.py +++ b/homeassistant/components/homekit_controller/config_flow.py @@ -420,6 +420,7 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): # Should never call this step without setting self.hkid assert self.hkid + description_placeholders = {} errors = {} @@ -465,10 +466,11 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return self.async_abort(reason="accessory_not_found_error") except InsecureSetupCode: errors["pairing_code"] = "insecure_setup_code" - except Exception: # pylint: disable=broad-except + except Exception as err: # pylint: disable=broad-except _LOGGER.exception("Pairing attempt failed with an unhandled exception") self.finish_pairing = None errors["pairing_code"] = "pairing_failed" + description_placeholders["error"] = str(err) if not self.finish_pairing: # Its possible that the first try may have been busy so @@ -496,11 +498,12 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): # TLV error, usually not in pairing mode _LOGGER.exception("Pairing communication failed") return await self.async_step_protocol_error() - except Exception: # pylint: disable=broad-except + except Exception as err: # pylint: disable=broad-except _LOGGER.exception("Pairing attempt failed with an unhandled exception") errors["pairing_code"] = "pairing_failed" + description_placeholders["error"] = str(err) - return self._async_step_pair_show_form(errors) + return self._async_step_pair_show_form(errors, description_placeholders) async def async_step_busy_error( self, user_input: dict[str, Any] | None = None @@ -531,7 +534,9 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): @callback def _async_step_pair_show_form( - self, errors: dict[str, str] | None = None + self, + errors: dict[str, str] | None = None, + description_placeholders: dict[str, str] | None = None, ) -> FlowResult: assert self.category @@ -547,7 +552,7 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return self.async_show_form( step_id="pair", errors=errors or {}, - description_placeholders=placeholders, + description_placeholders=placeholders | (description_placeholders or {}), data_schema=vol.Schema(schema), ) diff --git a/homeassistant/components/homekit_controller/strings.json b/homeassistant/components/homekit_controller/strings.json index 2831dabc38d..201e0a9b3c2 100644 --- a/homeassistant/components/homekit_controller/strings.json +++ b/homeassistant/components/homekit_controller/strings.json @@ -37,7 +37,7 @@ "unknown_error": "Device reported an unknown error. Pairing failed.", "authentication_error": "Incorrect HomeKit code. Please check it and try again.", "max_peers_error": "Device refused to add pairing as it has no free pairing storage.", - "pairing_failed": "An unhandled error occurred while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently." + "pairing_failed": "An unhandled error occurred while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently: {error}" }, "abort": { "no_devices": "No unpaired devices could be found", diff --git a/homeassistant/components/homekit_controller/translations/en.json b/homeassistant/components/homekit_controller/translations/en.json index 2686e71d252..c5b5178a58b 100644 --- a/homeassistant/components/homekit_controller/translations/en.json +++ b/homeassistant/components/homekit_controller/translations/en.json @@ -14,7 +14,7 @@ "authentication_error": "Incorrect HomeKit code. Please check it and try again.", "insecure_setup_code": "The requested setup code is insecure because of its trivial nature. This accessory fails to meet basic security requirements.", "max_peers_error": "Device refused to add pairing as it has no free pairing storage.", - "pairing_failed": "An unhandled error occurred while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently.", + "pairing_failed": "An unhandled error occurred while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently: {error}", "unable_to_pair": "Unable to pair, please try again.", "unknown_error": "Device reported an unknown error. Pairing failed." }, diff --git a/tests/components/homekit_controller/test_config_flow.py b/tests/components/homekit_controller/test_config_flow.py index 5e2c8249560..86af49a96c4 100644 --- a/tests/components/homekit_controller/test_config_flow.py +++ b/tests/components/homekit_controller/test_config_flow.py @@ -8,6 +8,7 @@ from aiohomekit.exceptions import AuthenticationError from aiohomekit.model import Accessories, Accessory from aiohomekit.model.characteristics import CharacteristicsTypes from aiohomekit.model.services import ServicesTypes +from bleak.exc import BleakError import pytest from homeassistant import config_entries @@ -743,6 +744,57 @@ async def test_pair_form_errors_on_finish(hass, controller, exception, expected) } +async def test_pair_unknown_errors(hass, controller): + """Test describing unknown errors.""" + device = setup_mock_accessory(controller) + discovery_info = get_device_discovery_info(device) + + # Device is discovered + result = await hass.config_entries.flow.async_init( + "homekit_controller", + context={"source": config_entries.SOURCE_ZEROCONF}, + data=discovery_info, + ) + + assert get_flow_context(hass, result) == { + "title_placeholders": {"name": "TestDevice", "category": "Outlet"}, + "unique_id": "00:00:00:00:00:00", + "source": config_entries.SOURCE_ZEROCONF, + } + + # User initiates pairing - this triggers the device to show a pairing code + # and then HA to show a pairing form + finish_pairing = unittest.mock.AsyncMock( + side_effect=BleakError("The bluetooth connection failed") + ) + with patch.object(device, "async_start_pairing", return_value=finish_pairing): + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert result["type"] == "form" + assert get_flow_context(hass, result) == { + "title_placeholders": {"name": "TestDevice", "category": "Outlet"}, + "unique_id": "00:00:00:00:00:00", + "source": config_entries.SOURCE_ZEROCONF, + } + + # User enters pairing code + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"pairing_code": "111-22-333"} + ) + assert result["type"] == "form" + assert result["errors"]["pairing_code"] == "pairing_failed" + assert ( + result["description_placeholders"]["error"] == "The bluetooth connection failed" + ) + + assert get_flow_context(hass, result) == { + "title_placeholders": {"name": "TestDevice", "category": "Outlet"}, + "unique_id": "00:00:00:00:00:00", + "source": config_entries.SOURCE_ZEROCONF, + "pairing": True, + } + + async def test_user_works(hass, controller): """Test user initiated disovers devices.""" setup_mock_accessory(controller) From 8b54a0679f7555cf3c3829738de164c23668aef3 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:08:28 +0100 Subject: [PATCH 0612/1033] Enforce CameraEntityFeature (#82325) --- homeassistant/components/camera/__init__.py | 4 ++-- homeassistant/components/nest/camera_sdm.py | 4 ++-- homeassistant/components/unifiprotect/camera.py | 7 ++++--- homeassistant/components/uvc/camera.py | 4 ++-- pylint/plugins/hass_enforce_type_hints.py | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 0b103c6a4ff..51e874ef3b7 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -429,7 +429,7 @@ class Camera(Entity): _attr_motion_detection_enabled: bool = False _attr_should_poll: bool = False # No need to poll cameras _attr_state: None = None # State is determined by is_on - _attr_supported_features: CameraEntityFeature | int = 0 + _attr_supported_features: CameraEntityFeature = CameraEntityFeature(0) def __init__(self) -> None: """Initialize a camera.""" @@ -450,7 +450,7 @@ class Camera(Entity): return ENTITY_IMAGE_URL.format(self.entity_id, self.access_tokens[-1]) @property - def supported_features(self) -> CameraEntityFeature | int: + def supported_features(self) -> CameraEntityFeature: """Flag supported features.""" return self._attr_supported_features diff --git a/homeassistant/components/nest/camera_sdm.py b/homeassistant/components/nest/camera_sdm.py index f83f914385e..f0b9b4930e1 100644 --- a/homeassistant/components/nest/camera_sdm.py +++ b/homeassistant/components/nest/camera_sdm.py @@ -95,9 +95,9 @@ class NestCamera(Camera): return self._device_info.device_model @property - def supported_features(self) -> int: + def supported_features(self) -> CameraEntityFeature: """Flag supported features.""" - supported_features = 0 + supported_features = CameraEntityFeature(0) if CameraLiveStreamTrait.NAME in self._device.traits: supported_features |= CameraEntityFeature.STREAM return supported_features diff --git a/homeassistant/components/unifiprotect/camera.py b/homeassistant/components/unifiprotect/camera.py index 5f858614ace..00dbbe77e77 100644 --- a/homeassistant/components/unifiprotect/camera.py +++ b/homeassistant/components/unifiprotect/camera.py @@ -175,9 +175,10 @@ class ProtectCamera(ProtectDeviceEntity, Camera): self._stream_source = ( # pylint: disable=attribute-defined-outside-init None if disable_stream else rtsp_url ) - self._attr_supported_features = ( - CameraEntityFeature.STREAM if self._stream_source else 0 - ) + if self._stream_source: + self._attr_supported_features = CameraEntityFeature.STREAM + else: + self._attr_supported_features = CameraEntityFeature(0) @callback def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None: diff --git a/homeassistant/components/uvc/camera.py b/homeassistant/components/uvc/camera.py index 28a221fd131..2417908ecec 100644 --- a/homeassistant/components/uvc/camera.py +++ b/homeassistant/components/uvc/camera.py @@ -107,14 +107,14 @@ class UnifiVideoCamera(Camera): return self._name @property - def supported_features(self) -> CameraEntityFeature | int: + def supported_features(self) -> CameraEntityFeature: """Return supported features.""" channels = self._caminfo["channels"] for channel in channels: if channel["isRtspEnabled"]: return CameraEntityFeature.STREAM - return 0 + return CameraEntityFeature(0) @property def extra_state_attributes(self): diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 9c7a7798893..f03f9f11186 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -803,7 +803,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["CameraEntityFeature", "int"], + return_type="CameraEntityFeature", ), TypeHintMatch( function_name="is_recording", From 48cc3071bb6dae97f8ae976fe2157a55d4d83127 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:08:53 +0100 Subject: [PATCH 0613/1033] Enforce ClimateEntityFeature (#82329) --- homeassistant/components/climate/__init__.py | 4 ++-- homeassistant/components/demo/climate.py | 2 +- homeassistant/components/esphome/climate.py | 4 ++-- homeassistant/components/homekit_controller/climate.py | 8 ++++---- homeassistant/components/izone/climate.py | 2 +- homeassistant/components/mqtt/climate.py | 2 +- homeassistant/components/mysensors/climate.py | 4 ++-- homeassistant/components/nest/climate_sdm.py | 5 ++--- homeassistant/components/sensibo/climate.py | 4 ++-- pylint/plugins/hass_enforce_type_hints.py | 2 +- 10 files changed, 18 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/climate/__init__.py b/homeassistant/components/climate/__init__.py index 1cea229a306..68338819421 100644 --- a/homeassistant/components/climate/__init__.py +++ b/homeassistant/components/climate/__init__.py @@ -225,7 +225,7 @@ class ClimateEntity(Entity): _attr_precision: float _attr_preset_mode: str | None _attr_preset_modes: list[str] | None - _attr_supported_features: ClimateEntityFeature | int = 0 + _attr_supported_features: ClimateEntityFeature = ClimateEntityFeature(0) _attr_swing_mode: str | None _attr_swing_modes: list[str] | None _attr_target_humidity: int | None = None @@ -552,7 +552,7 @@ class ClimateEntity(Entity): await self.async_set_hvac_mode(HVACMode.OFF) @property - def supported_features(self) -> ClimateEntityFeature | int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" return self._attr_supported_features diff --git a/homeassistant/components/demo/climate.py b/homeassistant/components/demo/climate.py index 52616d4cf47..00e049c6034 100644 --- a/homeassistant/components/demo/climate.py +++ b/homeassistant/components/demo/climate.py @@ -20,7 +20,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import DOMAIN -SUPPORT_FLAGS = 0 +SUPPORT_FLAGS = ClimateEntityFeature(0) async def async_setup_platform( diff --git a/homeassistant/components/esphome/climate.py b/homeassistant/components/esphome/climate.py index e27d1d47bca..352068aaa57 100644 --- a/homeassistant/components/esphome/climate.py +++ b/homeassistant/components/esphome/climate.py @@ -197,9 +197,9 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti return self._static_info.visual_max_temperature @property - def supported_features(self) -> ClimateEntityFeature | int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" - features = 0 + features = ClimateEntityFeature(0) if self._static_info.supports_two_point_target_temperature: features |= ClimateEntityFeature.TARGET_TEMPERATURE_RANGE else: diff --git a/homeassistant/components/homekit_controller/climate.py b/homeassistant/components/homekit_controller/climate.py index 3838c3a862e..2dc998200a4 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -148,9 +148,9 @@ class HomeKitBaseClimateEntity(HomeKitEntity, ClimateEntity): ) @property - def supported_features(self) -> ClimateEntityFeature | int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" - features = 0 + features = ClimateEntityFeature(0) if self.service.has(CharacteristicsTypes.FAN_STATE_TARGET): features |= ClimateEntityFeature.FAN_MODE @@ -368,7 +368,7 @@ class HomeKitHeaterCoolerEntity(HomeKitBaseClimateEntity): ) @property - def supported_features(self) -> ClimateEntityFeature | int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" features = super().supported_features @@ -602,7 +602,7 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity): return [MODE_HOMEKIT_TO_HASS[mode] for mode in valid_values] @property - def supported_features(self) -> ClimateEntityFeature | int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" features = super().supported_features diff --git a/homeassistant/components/izone/climate.py b/homeassistant/components/izone/climate.py index a8dc90ad88c..c94ccada5b3 100644 --- a/homeassistant/components/izone/climate.py +++ b/homeassistant/components/izone/climate.py @@ -517,7 +517,7 @@ class ZoneDevice(ClimateEntity): @property @_return_on_connection_error(0) - def supported_features(self) -> ClimateEntityFeature | int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" if self._zone.mode == Zone.Mode.AUTO: return self._attr_supported_features diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 1c3e48efd74..0d3313a4475 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -456,7 +456,7 @@ class MqttClimate(MqttEntity, ClimateEntity): config.get(key), entity=self ).async_render - support: ClimateEntityFeature | int = 0 + support = ClimateEntityFeature(0) if (self._topic[CONF_TEMP_STATE_TOPIC] is not None) or ( self._topic[CONF_TEMP_COMMAND_TOPIC] is not None ): diff --git a/homeassistant/components/mysensors/climate.py b/homeassistant/components/mysensors/climate.py index abaa74ab2fa..38077768f2e 100644 --- a/homeassistant/components/mysensors/climate.py +++ b/homeassistant/components/mysensors/climate.py @@ -77,9 +77,9 @@ class MySensorsHVAC(mysensors.device.MySensorsEntity, ClimateEntity): _attr_hvac_modes = OPERATION_LIST @property - def supported_features(self) -> ClimateEntityFeature | int: + def supported_features(self) -> ClimateEntityFeature: """Return the list of supported features.""" - features = 0 + features = ClimateEntityFeature(0) set_req = self.gateway.const.SetReq if set_req.V_HVAC_SPEED in self._values: features = features | ClimateEntityFeature.FAN_MODE diff --git a/homeassistant/components/nest/climate_sdm.py b/homeassistant/components/nest/climate_sdm.py index bed44045c11..4a453c5fd38 100644 --- a/homeassistant/components/nest/climate_sdm.py +++ b/homeassistant/components/nest/climate_sdm.py @@ -104,7 +104,6 @@ class ThermostatEntity(ClimateEntity): """Initialize ThermostatEntity.""" self._device = device self._device_info = NestDeviceInfo(device) - self._attr_supported_features = 0 @property def unique_id(self) -> str | None: @@ -266,9 +265,9 @@ class ThermostatEntity(ClimateEntity): return FAN_INV_MODES return [] - def _get_supported_features(self) -> int: + def _get_supported_features(self) -> ClimateEntityFeature: """Compute the bitmap of supported features from the current state.""" - features = 0 + features = ClimateEntityFeature(0) if HVACMode.HEAT_COOL in self.hvac_modes: features |= ClimateEntityFeature.TARGET_TEMPERATURE_RANGE if HVACMode.HEAT in self.hvac_modes or HVACMode.COOL in self.hvac_modes: diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index 4c11d5e781b..b1980b5e595 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -176,9 +176,9 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): self._attr_supported_features = self.get_features() self._attr_precision = PRECISION_TENTHS - def get_features(self) -> ClimateEntityFeature | int: + def get_features(self) -> ClimateEntityFeature: """Get supported features.""" - features = 0 + features = ClimateEntityFeature(0) for key in self.device_data.full_features: if key in FIELD_TO_FLAG: features |= FIELD_TO_FLAG[key] diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index f03f9f11186..78c21747948 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1055,7 +1055,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["ClimateEntityFeature", "int"], + return_type="ClimateEntityFeature", ), TypeHintMatch( function_name="min_temp", From 34607d4410a78fc6e41337e2b51600d1fdf39580 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:09:19 +0100 Subject: [PATCH 0614/1033] Enforce CoverEntityFeature (#82457) * Enforce CoverEntityFeature * Adjust pylint --- homeassistant/components/acmeda/cover.py | 4 ++-- homeassistant/components/bond/cover.py | 2 +- homeassistant/components/cover/__init__.py | 4 ++-- homeassistant/components/group/cover.py | 2 +- homeassistant/components/hunterdouglas_powerview/cover.py | 2 +- homeassistant/components/mqtt/cover.py | 4 ++-- homeassistant/components/overkiz/cover_entities/awning.py | 4 ++-- .../components/overkiz/cover_entities/generic_cover.py | 4 ++-- .../components/overkiz/cover_entities/vertical_cover.py | 4 ++-- homeassistant/components/tuya/cover.py | 2 +- pylint/plugins/hass_enforce_type_hints.py | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/acmeda/cover.py b/homeassistant/components/acmeda/cover.py index fba538a7a70..15a20cf6932 100644 --- a/homeassistant/components/acmeda/cover.py +++ b/homeassistant/components/acmeda/cover.py @@ -70,9 +70,9 @@ class AcmedaCover(AcmedaBase, CoverEntity): return position @property - def supported_features(self) -> CoverEntityFeature | int: + def supported_features(self) -> CoverEntityFeature: """Flag supported features.""" - supported_features = 0 + supported_features = CoverEntityFeature(0) if self.current_cover_position is not None: supported_features |= ( CoverEntityFeature.OPEN diff --git a/homeassistant/components/bond/cover.py b/homeassistant/components/bond/cover.py index 0a3e9048451..a41e188ed9d 100644 --- a/homeassistant/components/bond/cover.py +++ b/homeassistant/components/bond/cover.py @@ -58,7 +58,7 @@ class BondCover(BondEntity, CoverEntity): ) -> None: """Create HA entity representing Bond cover.""" super().__init__(hub, device, bpup_subs) - supported_features = 0 + supported_features = CoverEntityFeature(0) if self._device.supports_set_position(): supported_features |= CoverEntityFeature.SET_POSITION if self._device.supports_open(): diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py index 37cdc6422ad..072710aa947 100644 --- a/homeassistant/components/cover/__init__.py +++ b/homeassistant/components/cover/__init__.py @@ -232,7 +232,7 @@ class CoverEntity(Entity): _attr_is_closing: bool | None = None _attr_is_opening: bool | None = None _attr_state: None = None - _attr_supported_features: CoverEntityFeature | int | None + _attr_supported_features: CoverEntityFeature | None _cover_is_last_toggle_direction_open = True @@ -292,7 +292,7 @@ class CoverEntity(Entity): return data @property - def supported_features(self) -> CoverEntityFeature | int: + def supported_features(self) -> CoverEntityFeature: """Flag supported features.""" if self._attr_supported_features is not None: return self._attr_supported_features diff --git a/homeassistant/components/group/cover.py b/homeassistant/components/group/cover.py index a867c92d956..2ecfbeaca42 100644 --- a/homeassistant/components/group/cover.py +++ b/homeassistant/components/group/cover.py @@ -324,7 +324,7 @@ class CoverGroup(GroupEntity, CoverEntity): tilt_states, ATTR_CURRENT_TILT_POSITION ) - supported_features = 0 + supported_features = CoverEntityFeature(0) if self._covers[KEY_OPEN_CLOSE]: supported_features |= CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE supported_features |= CoverEntityFeature.STOP if self._covers[KEY_STOP] else 0 diff --git a/homeassistant/components/hunterdouglas_powerview/cover.py b/homeassistant/components/hunterdouglas_powerview/cover.py index 347b2c3af03..7e0206de322 100644 --- a/homeassistant/components/hunterdouglas_powerview/cover.py +++ b/homeassistant/components/hunterdouglas_powerview/cover.py @@ -118,7 +118,7 @@ class PowerViewShadeBase(ShadeEntity, CoverEntity): """Representation of a powerview shade.""" _attr_device_class = CoverDeviceClass.SHADE - _attr_supported_features = 0 + _attr_supported_features = CoverEntityFeature(0) def __init__( self, diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index c0bc92c42f5..533698d91b3 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -516,9 +516,9 @@ class MqttCover(MqttEntity, CoverEntity): return self._config.get(CONF_DEVICE_CLASS) @property - def supported_features(self) -> CoverEntityFeature | int: + def supported_features(self) -> CoverEntityFeature: """Flag supported features.""" - supported_features = 0 + supported_features = CoverEntityFeature(0) if self._config.get(CONF_COMMAND_TOPIC) is not None: if self._config.get(CONF_PAYLOAD_OPEN) is not None: supported_features |= CoverEntityFeature.OPEN diff --git a/homeassistant/components/overkiz/cover_entities/awning.py b/homeassistant/components/overkiz/cover_entities/awning.py index 07665c2a5c8..a5aee73bf5b 100644 --- a/homeassistant/components/overkiz/cover_entities/awning.py +++ b/homeassistant/components/overkiz/cover_entities/awning.py @@ -25,9 +25,9 @@ class Awning(OverkizGenericCover): _attr_device_class = CoverDeviceClass.AWNING @property - def supported_features(self) -> int: + def supported_features(self) -> CoverEntityFeature: """Flag supported features.""" - supported_features: int = super().supported_features + supported_features = super().supported_features if self.executor.has_command(OverkizCommand.SET_DEPLOYMENT): supported_features |= CoverEntityFeature.SET_POSITION diff --git a/homeassistant/components/overkiz/cover_entities/generic_cover.py b/homeassistant/components/overkiz/cover_entities/generic_cover.py index e36d7a680b1..1bc108b531d 100644 --- a/homeassistant/components/overkiz/cover_entities/generic_cover.py +++ b/homeassistant/components/overkiz/cover_entities/generic_cover.py @@ -129,9 +129,9 @@ class OverkizGenericCover(OverkizEntity, CoverEntity): return attr @property - def supported_features(self) -> int: + def supported_features(self) -> CoverEntityFeature: """Flag supported features.""" - supported_features = 0 + supported_features = CoverEntityFeature(0) if self.executor.has_command(*COMMANDS_OPEN_TILT): supported_features |= CoverEntityFeature.OPEN_TILT diff --git a/homeassistant/components/overkiz/cover_entities/vertical_cover.py b/homeassistant/components/overkiz/cover_entities/vertical_cover.py index 90ac6428960..1459786c23f 100644 --- a/homeassistant/components/overkiz/cover_entities/vertical_cover.py +++ b/homeassistant/components/overkiz/cover_entities/vertical_cover.py @@ -46,9 +46,9 @@ class VerticalCover(OverkizGenericCover): """Representation of an Overkiz vertical cover.""" @property - def supported_features(self) -> int: + def supported_features(self) -> CoverEntityFeature: """Flag supported features.""" - supported_features: int = super().supported_features + supported_features = super().supported_features if self.executor.has_command(OverkizCommand.SET_CLOSURE): supported_features |= CoverEntityFeature.SET_POSITION diff --git a/homeassistant/components/tuya/cover.py b/homeassistant/components/tuya/cover.py index d8b0a97480e..5bb9c794ca4 100644 --- a/homeassistant/components/tuya/cover.py +++ b/homeassistant/components/tuya/cover.py @@ -191,7 +191,7 @@ class TuyaCoverEntity(TuyaEntity, CoverEntity): super().__init__(device, device_manager) self.entity_description = description self._attr_unique_id = f"{super().unique_id}{description.key}" - self._attr_supported_features = 0 + self._attr_supported_features = CoverEntityFeature(0) # Check if this cover is based on a switch or has controls if self.find_dpcode(description.key, prefer_function=True): diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 78c21747948..a8d1eb16110 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1110,7 +1110,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["CoverEntityFeature", "int"], + return_type="CoverEntityFeature", ), TypeHintMatch( function_name="open_cover", From 12cb17620e11ce5dfa0c673094748b5dc948e505 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:13:54 +0100 Subject: [PATCH 0615/1033] Enforce FanEntityFeature (#82458) * Enforce FanEntityFeature * Adjust pylint --- homeassistant/components/bond/fan.py | 4 ++-- homeassistant/components/esphome/fan.py | 4 ++-- homeassistant/components/fan/__init__.py | 4 ++-- homeassistant/components/group/fan.py | 6 ++++-- homeassistant/components/homekit_controller/fan.py | 4 ++-- homeassistant/components/mqtt/fan.py | 2 +- pylint/plugins/hass_enforce_type_hints.py | 2 +- 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/bond/fan.py b/homeassistant/components/bond/fan.py index 18ce3777b4e..bd4f01bce52 100644 --- a/homeassistant/components/bond/fan.py +++ b/homeassistant/components/bond/fan.py @@ -82,9 +82,9 @@ class BondFan(BondEntity, FanEntity): self._attr_preset_mode = PRESET_MODE_BREEZE if breeze[0] else None @property - def supported_features(self) -> FanEntityFeature | int: + def supported_features(self) -> FanEntityFeature: """Flag supported features.""" - features = 0 + features = FanEntityFeature(0) if self._device.supports_speed(): features |= FanEntityFeature.SET_SPEED if self._device.supports_direction(): diff --git a/homeassistant/components/esphome/fan.py b/homeassistant/components/esphome/fan.py index fc8a3e600f3..27952d36c60 100644 --- a/homeassistant/components/esphome/fan.py +++ b/homeassistant/components/esphome/fan.py @@ -158,9 +158,9 @@ class EsphomeFan(EsphomeEntity[FanInfo, FanState], FanEntity): return _FAN_DIRECTIONS.from_esphome(self._state.direction) @property - def supported_features(self) -> FanEntityFeature | int: + def supported_features(self) -> FanEntityFeature: """Flag supported features.""" - flags = 0 + flags = FanEntityFeature(0) if self._static_info.supports_oscillation: flags |= FanEntityFeature.OSCILLATE if self._static_info.supports_speed: diff --git a/homeassistant/components/fan/__init__.py b/homeassistant/components/fan/__init__.py index b5cd653502f..e143b93258e 100644 --- a/homeassistant/components/fan/__init__.py +++ b/homeassistant/components/fan/__init__.py @@ -190,7 +190,7 @@ class FanEntity(ToggleEntity): _attr_preset_mode: str | None _attr_preset_modes: list[str] | None _attr_speed_count: int - _attr_supported_features: FanEntityFeature | int = 0 + _attr_supported_features: FanEntityFeature = FanEntityFeature(0) def set_percentage(self, percentage: int) -> None: """Set the speed of the fan, as a percentage.""" @@ -363,7 +363,7 @@ class FanEntity(ToggleEntity): return data @property - def supported_features(self) -> FanEntityFeature | int: + def supported_features(self) -> FanEntityFeature: """Flag supported features.""" return self._attr_supported_features diff --git a/homeassistant/components/group/fan.py b/homeassistant/components/group/fan.py index 09b59a2d123..682890dddd6 100644 --- a/homeassistant/components/group/fan.py +++ b/homeassistant/components/group/fan.py @@ -313,8 +313,10 @@ class FanGroup(GroupEntity, FanEntity): "_direction", FanEntityFeature.DIRECTION, ATTR_DIRECTION ) - self._attr_supported_features = reduce( - ior, [feature for feature in SUPPORTED_FLAGS if self._fans[feature]], 0 + self._attr_supported_features = FanEntityFeature( + reduce( + ior, [feature for feature in SUPPORTED_FLAGS if self._fans[feature]], 0 + ) ) self._attr_assumed_state |= any( state.attributes.get(ATTR_ASSUMED_STATE) for state in states diff --git a/homeassistant/components/homekit_controller/fan.py b/homeassistant/components/homekit_controller/fan.py index dffa323f8ad..550f86ddbe4 100644 --- a/homeassistant/components/homekit_controller/fan.py +++ b/homeassistant/components/homekit_controller/fan.py @@ -95,9 +95,9 @@ class BaseHomeKitFan(HomeKitEntity, FanEntity): return oscillating == 1 @property - def supported_features(self) -> FanEntityFeature | int: + def supported_features(self) -> FanEntityFeature: """Flag supported features.""" - features = 0 + features = FanEntityFeature(0) if self.service.has(CharacteristicsTypes.ROTATION_DIRECTION): features |= FanEntityFeature.DIRECTION diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index ef5386664f5..9da7721a7dc 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -334,7 +334,7 @@ class MqttFan(MqttEntity, FanEntity): optimistic or self._topic[CONF_PRESET_MODE_STATE_TOPIC] is None ) - self._attr_supported_features = 0 + self._attr_supported_features = FanEntityFeature(0) self._attr_supported_features |= ( self._topic[CONF_OSCILLATION_COMMAND_TOPIC] is not None and FanEntityFeature.OSCILLATE diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index a8d1eb16110..0ae157a694f 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1290,7 +1290,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["FanEntityFeature", "int"], + return_type="FanEntityFeature", ), TypeHintMatch( function_name="set_percentage", From d4bd9a0f7eb714a291aa082b734d869b8e100712 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:14:19 +0100 Subject: [PATCH 0616/1033] Enforce HumidifierEntityFeature (#82459) --- homeassistant/components/demo/humidifier.py | 2 +- homeassistant/components/humidifier/__init__.py | 4 ++-- pylint/plugins/hass_enforce_type_hints.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/demo/humidifier.py b/homeassistant/components/demo/humidifier.py index 9b55583d12b..772726ac1d5 100644 --- a/homeassistant/components/demo/humidifier.py +++ b/homeassistant/components/demo/humidifier.py @@ -13,7 +13,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -SUPPORT_FLAGS = 0 +SUPPORT_FLAGS = HumidifierEntityFeature(0) async def async_setup_platform( diff --git a/homeassistant/components/humidifier/__init__.py b/homeassistant/components/humidifier/__init__.py index 976dad03a45..61d7b6c3944 100644 --- a/homeassistant/components/humidifier/__init__.py +++ b/homeassistant/components/humidifier/__init__.py @@ -137,7 +137,7 @@ class HumidifierEntity(ToggleEntity): _attr_max_humidity: int = DEFAULT_MAX_HUMIDITY _attr_min_humidity: int = DEFAULT_MIN_HUMIDITY _attr_mode: str | None - _attr_supported_features: HumidifierEntityFeature | int = 0 + _attr_supported_features: HumidifierEntityFeature = HumidifierEntityFeature(0) _attr_target_humidity: int | None = None @property @@ -224,6 +224,6 @@ class HumidifierEntity(ToggleEntity): return self._attr_max_humidity @property - def supported_features(self) -> HumidifierEntityFeature | int: + def supported_features(self) -> HumidifierEntityFeature: """Return the list of supported features.""" return self._attr_supported_features diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 0ae157a694f..4c15de25c4d 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1429,7 +1429,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["HumidifierEntityFeature", "int"], + return_type="HumidifierEntityFeature", ), TypeHintMatch( function_name="target_humidity", From 7f1e1ed1d84700fc1517f6e2c10260a9698f4eb6 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:14:47 +0100 Subject: [PATCH 0617/1033] Enforce LightEntityFeature (#82460) --- homeassistant/components/control4/light.py | 4 ++-- homeassistant/components/decora_wifi/light.py | 4 ++-- homeassistant/components/group/light.py | 2 +- homeassistant/components/hue/v1/light.py | 2 +- homeassistant/components/iaqualink/light.py | 4 ++-- homeassistant/components/light/__init__.py | 4 ++-- homeassistant/components/mqtt/light/schema_basic.py | 8 +++----- homeassistant/components/osramlightify/light.py | 6 +++--- homeassistant/components/smartthings/light.py | 4 ++-- homeassistant/components/tasmota/light.py | 2 +- homeassistant/components/template/light.py | 4 ++-- homeassistant/components/tplink/light.py | 2 +- homeassistant/components/yeelight/light.py | 4 ++-- homeassistant/components/zha/light.py | 3 ++- pylint/plugins/hass_enforce_type_hints.py | 2 +- 15 files changed, 27 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/control4/light.py b/homeassistant/components/control4/light.py index 51da8113710..1f738803c2e 100644 --- a/homeassistant/components/control4/light.py +++ b/homeassistant/components/control4/light.py @@ -192,11 +192,11 @@ class Control4Light(Control4Entity, LightEntity): return None @property - def supported_features(self) -> LightEntityFeature | int: + def supported_features(self) -> LightEntityFeature: """Flag supported features.""" if self._is_dimmer: return LightEntityFeature.TRANSITION - return 0 + return LightEntityFeature(0) async def async_turn_on(self, **kwargs: Any) -> None: """Turn the entity on.""" diff --git a/homeassistant/components/decora_wifi/light.py b/homeassistant/components/decora_wifi/light.py index 820b5c8f53a..c103636563c 100644 --- a/homeassistant/components/decora_wifi/light.py +++ b/homeassistant/components/decora_wifi/light.py @@ -112,11 +112,11 @@ class DecoraWifiLight(LightEntity): return {self.color_mode} @property - def supported_features(self) -> LightEntityFeature | int: + def supported_features(self) -> LightEntityFeature: """Return supported features.""" if self._switch.canSetLevel: return LightEntityFeature.TRANSITION - return 0 + return LightEntityFeature(0) @property def name(self): diff --git a/homeassistant/components/group/light.py b/homeassistant/components/group/light.py index 71afa5d104b..6315e79d61a 100644 --- a/homeassistant/components/group/light.py +++ b/homeassistant/components/group/light.py @@ -287,7 +287,7 @@ class LightGroup(GroupEntity, LightEntity): set[str], set().union(*all_supported_color_modes) ) - self._attr_supported_features = 0 + self._attr_supported_features = LightEntityFeature(0) for support in find_state_attributes(states, ATTR_SUPPORTED_FEATURES): # Merge supported features by emulating support for every feature # we find. diff --git a/homeassistant/components/hue/v1/light.py b/homeassistant/components/hue/v1/light.py index 74fe25dafcf..e840835764b 100644 --- a/homeassistant/components/hue/v1/light.py +++ b/homeassistant/components/hue/v1/light.py @@ -108,7 +108,7 @@ def create_light(item_class, coordinator, bridge, is_group, rooms, api, item_id) if is_group: supported_color_modes = set() - supported_features = 0 + supported_features = LightEntityFeature(0) for light_id in api_item.lights: if light_id not in bridge.api.lights: continue diff --git a/homeassistant/components/iaqualink/light.py b/homeassistant/components/iaqualink/light.py index 853660c10bf..00c9445a3b5 100644 --- a/homeassistant/components/iaqualink/light.py +++ b/homeassistant/components/iaqualink/light.py @@ -100,9 +100,9 @@ class HassAqualinkLight(AqualinkEntity, LightEntity): return {self.color_mode} @property - def supported_features(self) -> LightEntityFeature | int: + def supported_features(self) -> LightEntityFeature: """Return the list of features supported by the light.""" if self.dev.supports_effect: return LightEntityFeature.EFFECT - return 0 + return LightEntityFeature(0) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 80110dbd006..acc6252d3b3 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -793,7 +793,7 @@ class LightEntity(ToggleEntity): _attr_rgbw_color: tuple[int, int, int, int] | None = None _attr_rgbww_color: tuple[int, int, int, int, int] | None = None _attr_supported_color_modes: set[ColorMode] | set[str] | None = None - _attr_supported_features: LightEntityFeature | int = 0 + _attr_supported_features: LightEntityFeature = LightEntityFeature(0) _attr_xy_color: tuple[float, float] | None = None @property @@ -1060,6 +1060,6 @@ class LightEntity(ToggleEntity): return self._attr_supported_color_modes @property - def supported_features(self) -> LightEntityFeature | int: + def supported_features(self) -> LightEntityFeature: """Flag supported features.""" return self._attr_supported_features diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index ea0c3b545da..dfa8af0097d 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -415,11 +415,9 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): supported_color_modes ) - supported_features: int = 0 - supported_features |= ( - topic[CONF_EFFECT_COMMAND_TOPIC] is not None and LightEntityFeature.EFFECT - ) - self._attr_supported_features = supported_features + self._attr_supported_features = LightEntityFeature(0) + if topic[CONF_EFFECT_COMMAND_TOPIC] is not None: + self._attr_supported_features |= LightEntityFeature.EFFECT def _is_optimistic(self, attribute: str) -> bool: """Return True if the attribute is optimistically updated.""" diff --git a/homeassistant/components/osramlightify/light.py b/homeassistant/components/osramlightify/light.py index d3c529c738e..fa4aca357c1 100644 --- a/homeassistant/components/osramlightify/light.py +++ b/homeassistant/components/osramlightify/light.py @@ -221,9 +221,9 @@ class Luminary(LightEntity): return color_modes - def _get_supported_features(self) -> LightEntityFeature | int: + def _get_supported_features(self) -> LightEntityFeature: """Get list of supported features.""" - features = 0 + features = LightEntityFeature(0) if "lum" in self._luminary.supported_features(): features = features | LightEntityFeature.TRANSITION @@ -435,7 +435,7 @@ class OsramLightifyGroup(Luminary): # users. return f"{self._luminary.lights()}" - def _get_supported_features(self) -> LightEntityFeature | int: + def _get_supported_features(self) -> LightEntityFeature: """Get list of supported features.""" features = super()._get_supported_features() if self._luminary.scenes(): diff --git a/homeassistant/components/smartthings/light.py b/homeassistant/components/smartthings/light.py index 514296d156a..37237323d1c 100644 --- a/homeassistant/components/smartthings/light.py +++ b/homeassistant/components/smartthings/light.py @@ -101,9 +101,9 @@ class SmartThingsLight(SmartThingsEntity, LightEntity): return color_modes - def _determine_features(self) -> LightEntityFeature | int: + def _determine_features(self) -> LightEntityFeature: """Get features supported by the device.""" - features = 0 + features = LightEntityFeature(0) # Transition if Capability.switch_level in self._device.capabilities: features |= LightEntityFeature.TRANSITION diff --git a/homeassistant/components/tasmota/light.py b/homeassistant/components/tasmota/light.py index 2ff51752ea5..b7b695c260f 100644 --- a/homeassistant/components/tasmota/light.py +++ b/homeassistant/components/tasmota/light.py @@ -120,7 +120,7 @@ class TasmotaLight( def _setup_from_entity(self) -> None: """(Re)Setup the entity.""" self._supported_color_modes = set() - supported_features = 0 + supported_features = LightEntityFeature(0) light_type = self._tasmota_entity.light_type if light_type in [LIGHT_TYPE_RGB, LIGHT_TYPE_RGBW, LIGHT_TYPE_RGBCW]: diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index 8e0027b323f..27dcbf0e014 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -254,9 +254,9 @@ class LightTemplate(TemplateEntity, LightEntity): return self._supported_color_modes @property - def supported_features(self) -> LightEntityFeature | int: + def supported_features(self) -> LightEntityFeature: """Flag supported features.""" - supported_features = 0 + supported_features = LightEntityFeature(0) if self._effect_script is not None: supported_features |= LightEntityFeature.EFFECT if self._supports_transition is True: diff --git a/homeassistant/components/tplink/light.py b/homeassistant/components/tplink/light.py index d0c056d7c17..c8944b7add2 100644 --- a/homeassistant/components/tplink/light.py +++ b/homeassistant/components/tplink/light.py @@ -312,7 +312,7 @@ class TPLinkSmartLightStrip(TPLinkSmartBulb): device: SmartLightStrip @property - def supported_features(self) -> LightEntityFeature | int: + def supported_features(self) -> LightEntityFeature: """Flag supported features.""" return super().supported_features | LightEntityFeature.EFFECT diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index af1c3de74e7..da79a1eb2d6 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -1002,9 +1002,9 @@ class YeelightNightLightMode(YeelightGenericLight): return PowerMode.MOONLIGHT @property - def supported_features(self) -> int: + def supported_features(self) -> LightEntityFeature: """Flag no supported features.""" - return 0 + return LightEntityFeature(0) class YeelightNightLightModeWithAmbientSupport(YeelightNightLightMode): diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index 2953ab3b99a..b4c760b27ec 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -18,6 +18,7 @@ from zigpy.zcl.foundation import Status from homeassistant.components import light from homeassistant.components.light import ( ColorMode, + LightEntityFeature, brightness_supported, filter_supported_color_modes, ) @@ -1078,7 +1079,7 @@ class LightGroup(BaseLight, ZhaGroupEntity): set[str], set().union(*all_supported_color_modes) ) - self._attr_supported_features = 0 + self._attr_supported_features = LightEntityFeature(0) for support in helpers.find_state_attributes(states, ATTR_SUPPORTED_FEATURES): # Merge supported features by emulating support for every feature # we find. diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 4c15de25c4d..a7c8782197f 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1520,7 +1520,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["LightEntityFeature", "int"], + return_type="LightEntityFeature", ), TypeHintMatch( function_name="turn_on", From a225fc456fb11901ccc628fbb45a3a2c87efe085 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:15:11 +0100 Subject: [PATCH 0618/1033] Enforce LockEntityFeature (#82461) --- homeassistant/components/esphome/lock.py | 6 ++++-- homeassistant/components/lock/__init__.py | 4 ++-- homeassistant/components/mqtt/lock.py | 6 +++--- pylint/plugins/hass_enforce_type_hints.py | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/esphome/lock.py b/homeassistant/components/esphome/lock.py index 12666971d71..947ea4729bb 100644 --- a/homeassistant/components/esphome/lock.py +++ b/homeassistant/components/esphome/lock.py @@ -38,9 +38,11 @@ class EsphomeLock(EsphomeEntity[LockInfo, LockEntityState], LockEntity): return self._static_info.assumed_state @property - def supported_features(self) -> LockEntityFeature | int: + def supported_features(self) -> LockEntityFeature: """Flag supported features.""" - return LockEntityFeature.OPEN if self._static_info.supports_open else 0 + if self._static_info.supports_open: + return LockEntityFeature.OPEN + return LockEntityFeature(0) @property def code_format(self) -> str | None: diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index 412e314da0f..5008fa0ca2b 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -112,7 +112,7 @@ class LockEntity(Entity): _attr_is_unlocking: bool | None = None _attr_is_jammed: bool | None = None _attr_state: None = None - _attr_supported_features: LockEntityFeature | int = 0 + _attr_supported_features: LockEntityFeature = LockEntityFeature(0) @property def changed_by(self) -> str | None: @@ -193,6 +193,6 @@ class LockEntity(Entity): return STATE_LOCKED if locked else STATE_UNLOCKED @property - def supported_features(self) -> LockEntityFeature | int: + def supported_features(self) -> LockEntityFeature: """Return the list of supported features.""" return self._attr_supported_features diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index ec1a3f2a897..525d85b5a6c 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -136,9 +136,9 @@ class MqttLock(MqttEntity, LockEntity): entity=self, ).async_render_with_possible_json_value - self._attr_supported_features = ( - LockEntityFeature.OPEN if CONF_PAYLOAD_OPEN in config else 0 - ) + self._attr_supported_features = LockEntityFeature(0) + if CONF_PAYLOAD_OPEN in config: + self._attr_supported_features |= LockEntityFeature.OPEN def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index a7c8782197f..e2e889e71b3 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1583,7 +1583,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["LockEntityFeature", "int"], + return_type="LockEntityFeature", ), TypeHintMatch( function_name="lock", From 6f05a7468638ca21502bd926052abee22d6114cd Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:15:37 +0100 Subject: [PATCH 0619/1033] Enforce MediaPlayerEntityFeature (#82462) --- homeassistant/components/bluesound/media_player.py | 4 ++-- homeassistant/components/dlna_dmr/media_player.py | 6 +++--- homeassistant/components/emby/media_player.py | 4 ++-- homeassistant/components/group/media_player.py | 2 +- .../components/homekit_controller/media_player.py | 4 ++-- homeassistant/components/jellyfin/media_player.py | 4 ++-- homeassistant/components/media_player/__init__.py | 4 ++-- homeassistant/components/mpd/media_player.py | 4 ++-- homeassistant/components/universal/media_player.py | 8 ++++---- homeassistant/components/webostv/media_player.py | 8 +++++--- pylint/plugins/hass_enforce_type_hints.py | 2 +- 11 files changed, 26 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/bluesound/media_player.py b/homeassistant/components/bluesound/media_player.py index f21276abbbe..7c23e76385a 100644 --- a/homeassistant/components/bluesound/media_player.py +++ b/homeassistant/components/bluesound/media_player.py @@ -775,10 +775,10 @@ class BluesoundPlayer(MediaPlayerEntity): return None @property - def supported_features(self) -> MediaPlayerEntityFeature | int: + def supported_features(self) -> MediaPlayerEntityFeature: """Flag of media commands that are supported.""" if self._status is None: - return 0 + return MediaPlayerEntityFeature(0) if self.is_grouped and not self.is_master: return ( diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index 00444cd8f1c..d2c61e9a318 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -478,15 +478,15 @@ class DlnaDmrEntity(MediaPlayerEntity): return MediaPlayerState.IDLE @property - def supported_features(self) -> MediaPlayerEntityFeature | int: + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported at this moment. Supported features may change as the device enters different states. """ if not self._device: - return 0 + return MediaPlayerEntityFeature(0) - supported_features = 0 + supported_features = MediaPlayerEntityFeature(0) if self._device.has_volume_level: supported_features |= MediaPlayerEntityFeature.VOLUME_SET diff --git a/homeassistant/components/emby/media_player.py b/homeassistant/components/emby/media_player.py index c03ae088a83..bc85b983d9d 100644 --- a/homeassistant/components/emby/media_player.py +++ b/homeassistant/components/emby/media_player.py @@ -284,11 +284,11 @@ class EmbyDevice(MediaPlayerEntity): return self.device.media_album_artist @property - def supported_features(self) -> MediaPlayerEntityFeature | int: + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" if self.supports_remote_control: return SUPPORT_EMBY - return 0 + return MediaPlayerEntityFeature(0) async def async_media_play(self) -> None: """Play media.""" diff --git a/homeassistant/components/group/media_player.py b/homeassistant/components/group/media_player.py index 2e46599aba1..e966016d63d 100644 --- a/homeassistant/components/group/media_player.py +++ b/homeassistant/components/group/media_player.py @@ -405,7 +405,7 @@ class MediaPlayerGroup(MediaPlayerEntity): else: self._state = MediaPlayerState.OFF - supported_features = 0 + supported_features = MediaPlayerEntityFeature(0) if self._features[KEY_CLEAR_PLAYLIST]: supported_features |= MediaPlayerEntityFeature.CLEAR_PLAYLIST if self._features[KEY_TRACKS]: diff --git a/homeassistant/components/homekit_controller/media_player.py b/homeassistant/components/homekit_controller/media_player.py index 04fad045cb5..1efa33429b1 100644 --- a/homeassistant/components/homekit_controller/media_player.py +++ b/homeassistant/components/homekit_controller/media_player.py @@ -80,9 +80,9 @@ class HomeKitTelevision(HomeKitEntity, MediaPlayerEntity): ] @property - def supported_features(self) -> MediaPlayerEntityFeature | int: + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" - features = 0 + features = MediaPlayerEntityFeature(0) if self.service.has(CharacteristicsTypes.ACTIVE_IDENTIFIER): features |= MediaPlayerEntityFeature.SELECT_SOURCE diff --git a/homeassistant/components/jellyfin/media_player.py b/homeassistant/components/jellyfin/media_player.py index a05aada2152..60fae2caac7 100644 --- a/homeassistant/components/jellyfin/media_player.py +++ b/homeassistant/components/jellyfin/media_player.py @@ -198,11 +198,11 @@ class JellyfinMediaPlayer(JellyfinEntity, MediaPlayerEntity): return get_artwork_url(self.coordinator.api_client, self.now_playing, 150) @property - def supported_features(self) -> MediaPlayerEntityFeature | int: + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" commands: list[str] = self.capabilities.get("SupportedCommands", []) controllable = self.capabilities.get("SupportsMediaControl", False) - features = 0 + features = MediaPlayerEntityFeature(0) if controllable: features |= ( diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 120a1128597..1f3204c3df1 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -492,7 +492,7 @@ class MediaPlayerEntity(Entity): _attr_source_list: list[str] | None = None _attr_source: str | None = None _attr_state: MediaPlayerState | str | None = None - _attr_supported_features: MediaPlayerEntityFeature | int = 0 + _attr_supported_features: MediaPlayerEntityFeature = MediaPlayerEntityFeature(0) _attr_volume_level: float | None = None # Implement these for your media player @@ -692,7 +692,7 @@ class MediaPlayerEntity(Entity): return self._attr_group_members @property - def supported_features(self) -> MediaPlayerEntityFeature | int: + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" return self._attr_supported_features diff --git a/homeassistant/components/mpd/media_player.py b/homeassistant/components/mpd/media_player.py index d54b9702686..fd783e0975b 100644 --- a/homeassistant/components/mpd/media_player.py +++ b/homeassistant/components/mpd/media_player.py @@ -338,10 +338,10 @@ class MpdDevice(MediaPlayerEntity): return None @property - def supported_features(self) -> MediaPlayerEntityFeature | int: + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" if self._status is None: - return 0 + return MediaPlayerEntityFeature(0) supported = SUPPORT_MPD if "volume" in self._status: diff --git a/homeassistant/components/universal/media_player.py b/homeassistant/components/universal/media_player.py index 450a0f6f3bf..3c6ba701f7d 100644 --- a/homeassistant/components/universal/media_player.py +++ b/homeassistant/components/universal/media_player.py @@ -453,11 +453,11 @@ class UniversalMediaPlayer(MediaPlayerEntity): return self._override_or_child_attr(ATTR_MEDIA_SHUFFLE) @property - def supported_features(self) -> MediaPlayerEntityFeature | int: + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" - flags: MediaPlayerEntityFeature | int = ( - self._child_attr(ATTR_SUPPORTED_FEATURES) or 0 - ) + flags: MediaPlayerEntityFeature = self._child_attr( + ATTR_SUPPORTED_FEATURES + ) or MediaPlayerEntityFeature(0) if SERVICE_TURN_ON in self._cmds: flags |= MediaPlayerEntityFeature.TURN_ON diff --git a/homeassistant/components/webostv/media_player.py b/homeassistant/components/webostv/media_player.py index 41432d65489..07d49f703f7 100644 --- a/homeassistant/components/webostv/media_player.py +++ b/homeassistant/components/webostv/media_player.py @@ -137,7 +137,7 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity): self._current_source = None self._source_list: dict = {} - self._supported_features: MediaPlayerEntityFeature | int = 0 + self._supported_features = MediaPlayerEntityFeature(0) self._update_states() async def async_added_to_hass(self) -> None: @@ -157,7 +157,9 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity): and (state := await self.async_get_last_state()) is not None ): self._supported_features = ( - state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) + state.attributes.get( + ATTR_SUPPORTED_FEATURES, MediaPlayerEntityFeature(0) + ) & ~MediaPlayerEntityFeature.TURN_ON ) @@ -314,7 +316,7 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity): await self._client.connect() @property - def supported_features(self) -> MediaPlayerEntityFeature | int: + def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" if self._wrapper.turn_on: return self._supported_features | MediaPlayerEntityFeature.TURN_ON diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index e2e889e71b3..75a34d4e3b2 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1790,7 +1790,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["MediaPlayerEntityFeature", "int"], + return_type="MediaPlayerEntityFeature", ), TypeHintMatch( function_name="turn_on", From 0cb3ec142eee8beb488b575facc0f88369f43f85 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:16:03 +0100 Subject: [PATCH 0620/1033] Enforce RemoteEntityFeature (#82463) --- homeassistant/components/remote/__init__.py | 4 ++-- pylint/plugins/hass_enforce_type_hints.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/remote/__init__.py b/homeassistant/components/remote/__init__.py index bc0711daeb5..17915e1be19 100644 --- a/homeassistant/components/remote/__init__.py +++ b/homeassistant/components/remote/__init__.py @@ -166,10 +166,10 @@ class RemoteEntity(ToggleEntity): entity_description: RemoteEntityDescription _attr_activity_list: list[str] | None = None _attr_current_activity: str | None = None - _attr_supported_features: RemoteEntityFeature | int = 0 + _attr_supported_features: RemoteEntityFeature = RemoteEntityFeature(0) @property - def supported_features(self) -> RemoteEntityFeature | int: + def supported_features(self) -> RemoteEntityFeature: """Flag supported features.""" return self._attr_supported_features diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 75a34d4e3b2..1974b789a83 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -2061,7 +2061,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["RemoteEntityFeature", "int"], + return_type="RemoteEntityFeature", ), TypeHintMatch( function_name="send_command", From ff5f1aee5372e859143ecbe9bee736d02aebe1d8 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:16:29 +0100 Subject: [PATCH 0621/1033] Enforce SirenEntityFeature (#82464) --- homeassistant/components/siren/__init__.py | 4 ++-- pylint/plugins/hass_enforce_type_hints.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/siren/__init__.py b/homeassistant/components/siren/__init__.py index 0bc16d03770..92d547d5760 100644 --- a/homeassistant/components/siren/__init__.py +++ b/homeassistant/components/siren/__init__.py @@ -162,7 +162,7 @@ class SirenEntity(ToggleEntity): entity_description: SirenEntityDescription _attr_available_tones: list[int | str] | dict[int, str] | None - _attr_supported_features: SirenEntityFeature | int = 0 + _attr_supported_features: SirenEntityFeature = SirenEntityFeature(0) @final @property @@ -190,6 +190,6 @@ class SirenEntity(ToggleEntity): return None @property - def supported_features(self) -> SirenEntityFeature | int: + def supported_features(self) -> SirenEntityFeature: """Return the list of supported features.""" return self._attr_supported_features diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 1974b789a83..ac8c6e674e0 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -2214,7 +2214,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["SirenEntityFeature", "int"], + return_type="SirenEntityFeature", ), ], ), From ab26ea06b90010485d88c442940e468d47133377 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:16:53 +0100 Subject: [PATCH 0622/1033] Enforce UpdateEntityFeature (#82465) --- homeassistant/components/mqtt/update.py | 4 ++-- homeassistant/components/update/__init__.py | 4 ++-- pylint/plugins/hass_enforce_type_hints.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mqtt/update.py b/homeassistant/components/mqtt/update.py index 9e08f765be0..0875778059f 100644 --- a/homeassistant/components/mqtt/update.py +++ b/homeassistant/components/mqtt/update.py @@ -253,9 +253,9 @@ class MqttUpdate(MqttEntity, UpdateEntity, RestoreEntity): get_mqtt_data(self.hass).state_write_requests.write_state_request(self) @property - def supported_features(self) -> UpdateEntityFeature | int: + def supported_features(self) -> UpdateEntityFeature: """Return the list of supported features.""" - support = 0 + support = UpdateEntityFeature(0) if self._config.get(CONF_COMMAND_TOPIC) is not None: support |= UpdateEntityFeature.INSTALL diff --git a/homeassistant/components/update/__init__.py b/homeassistant/components/update/__init__.py index 92272d56c4b..57e20a764ba 100644 --- a/homeassistant/components/update/__init__.py +++ b/homeassistant/components/update/__init__.py @@ -190,7 +190,7 @@ class UpdateEntity(RestoreEntity): _attr_release_summary: str | None = None _attr_release_url: str | None = None _attr_state: None = None - _attr_supported_features: UpdateEntityFeature | int = 0 + _attr_supported_features: UpdateEntityFeature = UpdateEntityFeature(0) _attr_title: str | None = None __skipped_version: str | None = None __in_progress: bool = False @@ -270,7 +270,7 @@ class UpdateEntity(RestoreEntity): return self._attr_release_url @property - def supported_features(self) -> UpdateEntityFeature | int: + def supported_features(self) -> UpdateEntityFeature: """Flag supported features.""" return self._attr_supported_features diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index ac8c6e674e0..53c3aa4b8b3 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -2345,7 +2345,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["UpdateEntityFeature", "int"], + return_type="UpdateEntityFeature", ), TypeHintMatch( function_name="title", From 4134d722da93c02278c601368d2e4ec554927e67 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:27:09 +0100 Subject: [PATCH 0623/1033] Enforce WaterHeaterEntityFeature (#82467) --- homeassistant/components/water_heater/__init__.py | 4 ++-- pylint/plugins/hass_enforce_type_hints.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/water_heater/__init__.py b/homeassistant/components/water_heater/__init__.py index 4396c66cf22..c821824d279 100644 --- a/homeassistant/components/water_heater/__init__.py +++ b/homeassistant/components/water_heater/__init__.py @@ -167,7 +167,7 @@ class WaterHeaterEntity(Entity): _attr_operation_list: list[str] | None = None _attr_precision: float _attr_state: None = None - _attr_supported_features: WaterHeaterEntityFeature | int = 0 + _attr_supported_features: WaterHeaterEntityFeature = WaterHeaterEntityFeature(0) _attr_target_temperature_high: float | None = None _attr_target_temperature_low: float | None = None _attr_target_temperature: float | None = None @@ -338,7 +338,7 @@ class WaterHeaterEntity(Entity): ) @property - def supported_features(self) -> WaterHeaterEntityFeature | int: + def supported_features(self) -> WaterHeaterEntityFeature: """Return the list of supported features.""" return self._attr_supported_features diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 53c3aa4b8b3..b7219bbf0e4 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -2539,7 +2539,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["WaterHeaterEntityFeature", "int"], + return_type="WaterHeaterEntityFeature", ), TypeHintMatch( function_name="target_temperature", From bf3c6e5f58b7a42d8e4c9784b41c664f2f047f84 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 07:27:27 +0100 Subject: [PATCH 0624/1033] Enforce VacuumEntityFeature (#82466) --- homeassistant/components/demo/vacuum.py | 6 ++---- homeassistant/components/mqtt/vacuum/schema.py | 6 +++--- homeassistant/components/mqtt/vacuum/schema_state.py | 4 ++-- homeassistant/components/vacuum/__init__.py | 4 ++-- pylint/plugins/hass_enforce_type_hints.py | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/demo/vacuum.py b/homeassistant/components/demo/vacuum.py index 5426c61fa52..c283ab5456f 100644 --- a/homeassistant/components/demo/vacuum.py +++ b/homeassistant/components/demo/vacuum.py @@ -95,7 +95,7 @@ async def async_setup_platform( DemoVacuum(DEMO_VACUUM_MOST, SUPPORT_MOST_SERVICES), DemoVacuum(DEMO_VACUUM_BASIC, SUPPORT_BASIC_SERVICES), DemoVacuum(DEMO_VACUUM_MINIMAL, SUPPORT_MINIMAL_SERVICES), - DemoVacuum(DEMO_VACUUM_NONE, 0), + DemoVacuum(DEMO_VACUUM_NONE, VacuumEntityFeature(0)), StateDemoVacuum(DEMO_VACUUM_STATE), ] ) @@ -106,9 +106,7 @@ class DemoVacuum(VacuumEntity): _attr_should_poll = False - def __init__( - self, name: str, supported_features: VacuumEntityFeature | int - ) -> None: + def __init__(self, name: str, supported_features: VacuumEntityFeature) -> None: """Initialize the vacuum.""" self._attr_name = name self._attr_supported_features = supported_features diff --git a/homeassistant/components/mqtt/vacuum/schema.py b/homeassistant/components/mqtt/vacuum/schema.py index 20b7bced99d..78175f61255 100644 --- a/homeassistant/components/mqtt/vacuum/schema.py +++ b/homeassistant/components/mqtt/vacuum/schema.py @@ -20,7 +20,7 @@ MQTT_VACUUM_SCHEMA = vol.Schema( def services_to_strings( - services: VacuumEntityFeature | int, + services: VacuumEntityFeature, service_to_string: dict[VacuumEntityFeature, str], ) -> list[str]: """Convert SUPPORT_* service bitmask to list of service strings.""" @@ -33,9 +33,9 @@ def services_to_strings( def strings_to_services( strings: list[str], string_to_service: dict[str, VacuumEntityFeature] -) -> VacuumEntityFeature | int: +) -> VacuumEntityFeature: """Convert service strings to SUPPORT_* service bitmask.""" - services: VacuumEntityFeature | int = 0 + services = VacuumEntityFeature(0) for string in strings: services |= string_to_service[string] return services diff --git a/homeassistant/components/mqtt/vacuum/schema_state.py b/homeassistant/components/mqtt/vacuum/schema_state.py index 2bacf4f36de..a854d3b0620 100644 --- a/homeassistant/components/mqtt/vacuum/schema_state.py +++ b/homeassistant/components/mqtt/vacuum/schema_state.py @@ -60,7 +60,7 @@ SERVICE_TO_STRING: dict[VacuumEntityFeature, str] = { STRING_TO_SERVICE = {v: k for k, v in SERVICE_TO_STRING.items()} -DEFAULT_SERVICES: VacuumEntityFeature | int = ( +DEFAULT_SERVICES = ( VacuumEntityFeature.START | VacuumEntityFeature.STOP | VacuumEntityFeature.RETURN_HOME @@ -68,7 +68,7 @@ DEFAULT_SERVICES: VacuumEntityFeature | int = ( | VacuumEntityFeature.BATTERY | VacuumEntityFeature.CLEAN_SPOT ) -ALL_SERVICES: VacuumEntityFeature | int = ( +ALL_SERVICES = ( DEFAULT_SERVICES | VacuumEntityFeature.PAUSE | VacuumEntityFeature.LOCATE diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py index 708c511a85c..cf82836cbec 100644 --- a/homeassistant/components/vacuum/__init__.py +++ b/homeassistant/components/vacuum/__init__.py @@ -180,10 +180,10 @@ class _BaseVacuum(Entity): _attr_battery_level: int | None = None _attr_fan_speed: str | None = None _attr_fan_speed_list: list[str] - _attr_supported_features: VacuumEntityFeature | int = 0 + _attr_supported_features: VacuumEntityFeature = VacuumEntityFeature(0) @property - def supported_features(self) -> VacuumEntityFeature | int: + def supported_features(self) -> VacuumEntityFeature: """Flag vacuum cleaner features that are supported.""" return self._attr_supported_features diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index b7219bbf0e4..a60b1c7de85 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -2396,7 +2396,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="supported_features", - return_type=["VacuumEntityFeature", "int"], + return_type="VacuumEntityFeature", ), TypeHintMatch( function_name="stop", From e4c73259f7ba1902854b97e58188e530df4983cc Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 22 Nov 2022 09:10:36 +0200 Subject: [PATCH 0625/1033] Configure autoflake for pre-commit (#82414) --- .pre-commit-config.yaml | 7 +++++++ homeassistant/components/zha/core/__init__.py | 3 ++- requirements_test_pre_commit.txt | 1 + tests/components/bluetooth/__init__.py | 1 - .../bluetooth_le_tracker/test_device_tracker.py | 2 -- tests/components/config/test_config_entries.py | 1 - tests/components/fido/test_sensor.py | 2 -- tests/components/history/test_init.py | 1 - tests/components/kodi/util.py | 4 ---- tests/components/lastfm/test_sensor.py | 1 - tests/components/locative/test_init.py | 1 - tests/components/mqtt/test_subscription.py | 2 -- tests/components/ness_alarm/test_init.py | 10 ---------- tests/components/owntracks/test_init.py | 1 - tests/components/tellduslive/test_config_flow.py | 2 +- tests/components/xiaomi_ble/conftest.py | 4 ---- .../custom_components/test_embedded/switch.py | 1 - 17 files changed, 11 insertions(+), 33 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1676b60a802..583776421e7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,6 +4,13 @@ repos: hooks: - id: pyupgrade args: [--py39-plus] + - repo: https://github.com/PyCQA/autoflake + rev: v1.7.7 + hooks: + - id: autoflake + args: + - --in-place + - --remove-all-unused-imports - repo: https://github.com/psf/black rev: 22.10.0 hooks: diff --git a/homeassistant/components/zha/core/__init__.py b/homeassistant/components/zha/core/__init__.py index a416ff2eebe..755eac3c4ce 100644 --- a/homeassistant/components/zha/core/__init__.py +++ b/homeassistant/components/zha/core/__init__.py @@ -1,5 +1,6 @@ """Core module for Zigbee Home Automation.""" -# flake8: noqa from .device import ZHADevice from .gateway import ZHAGateway + +__all__ = ["ZHADevice", "ZHAGateway"] diff --git a/requirements_test_pre_commit.txt b/requirements_test_pre_commit.txt index ec6edeeea66..2258b8c055c 100644 --- a/requirements_test_pre_commit.txt +++ b/requirements_test_pre_commit.txt @@ -1,5 +1,6 @@ # Automatically generated from .pre-commit-config.yaml by gen_requirements_all.py, do not edit +autoflake==1.7.7 bandit==1.7.4 black==22.10.0 codespell==2.1.0 diff --git a/tests/components/bluetooth/__init__.py b/tests/components/bluetooth/__init__.py index fbe5be2e34d..e4dd2a8bb57 100644 --- a/tests/components/bluetooth/__init__.py +++ b/tests/components/bluetooth/__init__.py @@ -215,7 +215,6 @@ class MockBleakClient(BleakClient): async def disconnect(self, *args, **kwargs): """Mock disconnect.""" - pass async def get_services(self, *args, **kwargs): """Mock get_services.""" diff --git a/tests/components/bluetooth_le_tracker/test_device_tracker.py b/tests/components/bluetooth_le_tracker/test_device_tracker.py index 585c83f20a7..04f8c534cf2 100644 --- a/tests/components/bluetooth_le_tracker/test_device_tracker.py +++ b/tests/components/bluetooth_le_tracker/test_device_tracker.py @@ -31,7 +31,6 @@ class MockBleakClient: def __init__(self, *args, **kwargs): """Mock BleakClient.""" - pass async def __aenter__(self, *args, **kwargs): """Mock BleakClient.__aenter__.""" @@ -39,7 +38,6 @@ class MockBleakClient: async def __aexit__(self, *args, **kwargs): """Mock BleakClient.__aexit__.""" - pass class MockBleakClientTimesOut(MockBleakClient): diff --git a/tests/components/config/test_config_entries.py b/tests/components/config/test_config_entries.py index 29a2395a926..8ebf59323c8 100644 --- a/tests/components/config/test_config_entries.py +++ b/tests/components/config/test_config_entries.py @@ -69,7 +69,6 @@ async def test_get_entries(hass, client, clear_handlers): @callback def async_get_options_flow(config_entry): """Get options flow.""" - pass @classmethod @callback diff --git a/tests/components/fido/test_sensor.py b/tests/components/fido/test_sensor.py index bcece50f6e4..0ab1de94117 100644 --- a/tests/components/fido/test_sensor.py +++ b/tests/components/fido/test_sensor.py @@ -17,7 +17,6 @@ class FidoClientMock: def __init__(self, username, password, timeout=None, httpsession=None): """Fake Fido client init.""" - pass def get_phone_numbers(self): """Return Phone numbers.""" @@ -29,7 +28,6 @@ class FidoClientMock: async def fetch_data(self): """Return fake fetching data.""" - pass class FidoClientMockError(FidoClientMock): diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index 092c589035d..f0c4da26231 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -27,7 +27,6 @@ from tests.components.recorder.common import ( def test_setup(): """Test setup method of history.""" # Verification occurs in the fixture - pass def test_get_significant_states(hass_history): diff --git a/tests/components/kodi/util.py b/tests/components/kodi/util.py index 5b8b07583c5..9fb215e2d8a 100644 --- a/tests/components/kodi/util.py +++ b/tests/components/kodi/util.py @@ -68,7 +68,6 @@ class MockConnection: async def connect(self): """Mock connect.""" - pass @property def connected(self): @@ -82,7 +81,6 @@ class MockConnection: async def close(self): """Mock close.""" - pass @property def server(self): @@ -99,7 +97,6 @@ class MockWSConnection: async def connect(self): """Mock connect.""" - pass @property def connected(self): @@ -113,7 +110,6 @@ class MockWSConnection: async def close(self): """Mock close.""" - pass @property def server(self): diff --git a/tests/components/lastfm/test_sensor.py b/tests/components/lastfm/test_sensor.py index cbb37f94dc4..d20b54e738f 100644 --- a/tests/components/lastfm/test_sensor.py +++ b/tests/components/lastfm/test_sensor.py @@ -30,7 +30,6 @@ class MockUser: def get_image(self): """Get mock image.""" - pass def get_recent_tracks(self, limit): """Get mock recent tracks.""" diff --git a/tests/components/locative/test_init.py b/tests/components/locative/test_init.py index 94b94c2ec93..5f608803e4a 100644 --- a/tests/components/locative/test_init.py +++ b/tests/components/locative/test_init.py @@ -18,7 +18,6 @@ from homeassistant.setup import async_setup_component @pytest.fixture(autouse=True) def mock_dev_track(mock_device_tracker_conf): """Mock device tracker config loading.""" - pass @pytest.fixture diff --git a/tests/components/mqtt/test_subscription.py b/tests/components/mqtt/test_subscription.py index 3be66f0aa90..6487e5cb826 100644 --- a/tests/components/mqtt/test_subscription.py +++ b/tests/components/mqtt/test_subscription.py @@ -139,7 +139,6 @@ async def test_qos_encoding_default(hass, mqtt_mock_entry_no_yaml_config, caplog @callback def msg_callback(*args): """Do nothing.""" - pass sub_state = None sub_state = async_prepare_subscribe_topics( @@ -158,7 +157,6 @@ async def test_qos_encoding_custom(hass, mqtt_mock_entry_no_yaml_config, caplog) @callback def msg_callback(*args): """Do nothing.""" - pass sub_state = None sub_state = async_prepare_subscribe_topics( diff --git a/tests/components/ness_alarm/test_init.py b/tests/components/ness_alarm/test_init.py index 507a786c3d1..6a0158674e3 100644 --- a/tests/components/ness_alarm/test_init.py +++ b/tests/components/ness_alarm/test_init.py @@ -210,43 +210,33 @@ class MockClient: async def panic(self, code): """Handle panic.""" - pass async def disarm(self, code): """Handle disarm.""" - pass async def arm_away(self, code): """Handle arm_away.""" - pass async def arm_home(self, code): """Handle arm_home.""" - pass async def aux(self, output_id, state): """Handle auxiliary control.""" - pass async def keepalive(self): """Handle keepalive.""" - pass async def update(self): """Handle update.""" - pass def on_zone_change(self): """Handle on_zone_change.""" - pass def on_state_change(self): """Handle on_state_change.""" - pass async def close(self): """Handle close.""" - pass @pytest.fixture diff --git a/tests/components/owntracks/test_init.py b/tests/components/owntracks/test_init.py index 856d3ece298..f7f978f381e 100644 --- a/tests/components/owntracks/test_init.py +++ b/tests/components/owntracks/test_init.py @@ -35,7 +35,6 @@ LOCATION_MESSAGE = { @pytest.fixture(autouse=True) def mock_dev_track(mock_device_tracker_conf): """Mock device tracker config loading.""" - pass @pytest.fixture diff --git a/tests/components/tellduslive/test_config_flow.py b/tests/components/tellduslive/test_config_flow.py index ba233d04a78..0872060712e 100644 --- a/tests/components/tellduslive/test_config_flow.py +++ b/tests/components/tellduslive/test_config_flow.py @@ -16,7 +16,7 @@ from homeassistant.components.tellduslive import ( from homeassistant.config_entries import SOURCE_DISCOVERY from homeassistant.const import CONF_HOST -from tests.common import MockConfigEntry, mock_coro +from tests.common import MockConfigEntry def init_config_flow(hass, side_effect=None): diff --git a/tests/components/xiaomi_ble/conftest.py b/tests/components/xiaomi_ble/conftest.py index 2997943468f..3d68d78e27e 100644 --- a/tests/components/xiaomi_ble/conftest.py +++ b/tests/components/xiaomi_ble/conftest.py @@ -20,7 +20,6 @@ class MockBleakClient: def __init__(self, *args, **kwargs): """Mock BleakClient.""" - pass async def __aenter__(self, *args, **kwargs): """Mock BleakClient.__aenter__.""" @@ -28,15 +27,12 @@ class MockBleakClient: async def __aexit__(self, *args, **kwargs): """Mock BleakClient.__aexit__.""" - pass async def connect(self, *args, **kwargs): """Mock BleakClient.connect.""" - pass async def disconnect(self, *args, **kwargs): """Mock BleakClient.disconnect.""" - pass class MockBleakClientBattery5(MockBleakClient): diff --git a/tests/testing_config/custom_components/test_embedded/switch.py b/tests/testing_config/custom_components/test_embedded/switch.py index 3abd5b79085..46dac4419a6 100644 --- a/tests/testing_config/custom_components/test_embedded/switch.py +++ b/tests/testing_config/custom_components/test_embedded/switch.py @@ -5,4 +5,3 @@ async def async_setup_platform( hass, config, async_add_entities_callback, discovery_info=None ): """Find and return test switches.""" - pass From 1e64d830ac5dfb9a1e23121a269cf9fa73e9d226 Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Tue, 22 Nov 2022 09:54:05 +0100 Subject: [PATCH 0626/1033] Migrate devolo Home Control to new entity naming (#74969) --- .../devolo_home_control/binary_sensor.py | 16 +++++---- .../devolo_home_control/devolo_device.py | 14 +++++--- .../components/devolo_home_control/sensor.py | 9 ++--- .../devolo_home_control/test_binary_sensor.py | 35 ++++++++++++------- .../devolo_home_control/test_climate.py | 8 ++++- .../devolo_home_control/test_cover.py | 2 ++ .../devolo_home_control/test_light.py | 2 ++ .../devolo_home_control/test_siren.py | 8 ++++- 8 files changed, 62 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/devolo_home_control/binary_sensor.py b/homeassistant/components/devolo_home_control/binary_sensor.py index a6b18c2b312..e8395062e8c 100644 --- a/homeassistant/components/devolo_home_control/binary_sensor.py +++ b/homeassistant/components/devolo_home_control/binary_sensor.py @@ -81,13 +81,14 @@ class DevoloBinaryDeviceEntity(DevoloDeviceEntity, BinarySensorEntity): or self._binary_sensor_property.sensor_type ) - if self._attr_device_class is None: - if device_instance.binary_sensor_property[element_uid].sub_type != "": - self._attr_name += ( - f" {device_instance.binary_sensor_property[element_uid].sub_type}" - ) - else: - self._attr_name += f" {device_instance.binary_sensor_property[element_uid].sensor_type}" + if device_instance.binary_sensor_property[element_uid].sub_type != "": + self._attr_name = device_instance.binary_sensor_property[ + element_uid + ].sub_type.capitalize() + else: + self._attr_name = device_instance.binary_sensor_property[ + element_uid + ].sensor_type.capitalize() self._value = self._binary_sensor_property.state @@ -128,6 +129,7 @@ class DevoloRemoteControl(DevoloDeviceEntity, BinarySensorEntity): self._key = key self._attr_is_on = False + self._attr_name = f"Button {key}" def _sync(self, message: tuple) -> None: """Update the binary sensor state.""" diff --git a/homeassistant/components/devolo_home_control/devolo_device.py b/homeassistant/components/devolo_home_control/devolo_device.py index 6087e07799d..5848f682626 100644 --- a/homeassistant/components/devolo_home_control/devolo_device.py +++ b/homeassistant/components/devolo_home_control/devolo_device.py @@ -19,6 +19,8 @@ _LOGGER = logging.getLogger(__name__) class DevoloDeviceEntity(Entity): """Abstract representation of a device within devolo Home Control.""" + _attr_has_entity_name = True + def __init__( self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str ) -> None: @@ -29,9 +31,6 @@ class DevoloDeviceEntity(Entity): self._attr_available = ( device_instance.is_online() ) # This is not doing I/O. It fetches an internal state of the API - self._attr_name: str = device_instance.settings_property[ - "general_device_settings" - ].name self._attr_should_poll = False self._attr_unique_id = element_uid self._attr_device_info = DeviceInfo( @@ -39,7 +38,7 @@ class DevoloDeviceEntity(Entity): identifiers={(DOMAIN, self._device_instance.uid)}, manufacturer=device_instance.brand, model=device_instance.name, - name=self._attr_name, + name=device_instance.settings_property["general_device_settings"].name, suggested_area=device_instance.settings_property[ "general_device_settings" ].zone, @@ -47,11 +46,16 @@ class DevoloDeviceEntity(Entity): self.subscriber: Subscriber | None = None self.sync_callback = self._sync + self._value: float async def async_added_to_hass(self) -> None: """Call when entity is added to hass.""" - self.subscriber = Subscriber(self._attr_name, callback=self.sync_callback) + assert self.device_info + assert self.device_info["name"] # The name was set on entity creation + self.subscriber = Subscriber( + self.device_info["name"], callback=self.sync_callback + ) self._homecontrol.publisher.register( self._device_instance.uid, self.subscriber, self.sync_callback ) diff --git a/homeassistant/components/devolo_home_control/sensor.py b/homeassistant/components/devolo_home_control/sensor.py index 2d023d23e2d..33a17929f71 100644 --- a/homeassistant/components/devolo_home_control/sensor.py +++ b/homeassistant/components/devolo_home_control/sensor.py @@ -115,12 +115,9 @@ class DevoloGenericMultiLevelDeviceEntity(DevoloMultiLevelDeviceEntity): self._multi_level_sensor_property.sensor_type ) self._attr_native_unit_of_measurement = self._multi_level_sensor_property.unit - + self._attr_name = self._multi_level_sensor_property.sensor_type.capitalize() self._value = self._multi_level_sensor_property.value - if self._attr_device_class is None: - self._attr_name += f" {self._multi_level_sensor_property.sensor_type}" - if element_uid.startswith("devolo.VoltageMultiLevelSensor:"): self._attr_entity_registry_enabled_default = False @@ -143,7 +140,7 @@ class DevoloBatteryEntity(DevoloMultiLevelDeviceEntity): self._attr_state_class = STATE_CLASS_MAPPING.get("battery") self._attr_entity_category = EntityCategory.DIAGNOSTIC self._attr_native_unit_of_measurement = PERCENTAGE - + self._attr_name = "Battery level" self._value = device_instance.battery_level @@ -179,7 +176,7 @@ class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity): device_instance.consumption_property[element_uid], consumption ) - self._attr_name += f" {consumption}" + self._attr_name = f"{consumption.capitalize()} consumption" @property def unique_id(self) -> str: diff --git a/tests/components/devolo_home_control/test_binary_sensor.py b/tests/components/devolo_home_control/test_binary_sensor.py index 4bce2ebd19e..ec8a7d288ab 100644 --- a/tests/components/devolo_home_control/test_binary_sensor.py +++ b/tests/components/devolo_home_control/test_binary_sensor.py @@ -4,7 +4,12 @@ from unittest.mock import patch import pytest from homeassistant.components.binary_sensor import DOMAIN -from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE +from homeassistant.const import ( + ATTR_FRIENDLY_NAME, + STATE_OFF, + STATE_ON, + STATE_UNAVAILABLE, +) from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry from homeassistant.helpers.entity import EntityCategory @@ -31,25 +36,30 @@ async def test_binary_sensor(hass: HomeAssistant): await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - state = hass.states.get(f"{DOMAIN}.test") + state = hass.states.get(f"{DOMAIN}.test_door") assert state is not None assert state.state == STATE_OFF + assert state.attributes[ATTR_FRIENDLY_NAME] == "Test Door" - state = hass.states.get(f"{DOMAIN}.test_2") + state = hass.states.get(f"{DOMAIN}.test_overload") assert state is not None + assert state.attributes[ATTR_FRIENDLY_NAME] == "Test Overload" er = entity_registry.async_get(hass) - assert er.async_get(f"{DOMAIN}.test_2").entity_category == EntityCategory.DIAGNOSTIC + assert ( + er.async_get(f"{DOMAIN}.test_overload").entity_category + == EntityCategory.DIAGNOSTIC + ) # Emulate websocket message: sensor turned on test_gateway.publisher.dispatch("Test", ("Test", True)) await hass.async_block_till_done() - assert hass.states.get(f"{DOMAIN}.test").state == STATE_ON + assert hass.states.get(f"{DOMAIN}.test_door").state == STATE_ON # Emulate websocket message: device went offline test_gateway.devices["Test"].status = 1 test_gateway.publisher.dispatch("Test", ("Status", False, "status")) await hass.async_block_till_done() - assert hass.states.get(f"{DOMAIN}.test").state == STATE_UNAVAILABLE + assert hass.states.get(f"{DOMAIN}.test_door").state == STATE_UNAVAILABLE @pytest.mark.usefixtures("mock_zeroconf") @@ -65,25 +75,26 @@ async def test_remote_control(hass: HomeAssistant): await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - state = hass.states.get(f"{DOMAIN}.test") + state = hass.states.get(f"{DOMAIN}.test_button_1") assert state is not None assert state.state == STATE_OFF + assert state.attributes[ATTR_FRIENDLY_NAME] == "Test Button 1" # Emulate websocket message: button pressed test_gateway.publisher.dispatch("Test", ("Test", 1)) await hass.async_block_till_done() - assert hass.states.get(f"{DOMAIN}.test").state == STATE_ON + assert hass.states.get(f"{DOMAIN}.test_button_1").state == STATE_ON # Emulate websocket message: button released test_gateway.publisher.dispatch("Test", ("Test", 0)) await hass.async_block_till_done() - assert hass.states.get(f"{DOMAIN}.test").state == STATE_OFF + assert hass.states.get(f"{DOMAIN}.test_button_1").state == STATE_OFF # Emulate websocket message: device went offline test_gateway.devices["Test"].status = 1 test_gateway.publisher.dispatch("Test", ("Status", False, "status")) await hass.async_block_till_done() - assert hass.states.get(f"{DOMAIN}.test").state == STATE_UNAVAILABLE + assert hass.states.get(f"{DOMAIN}.test_button_1").state == STATE_UNAVAILABLE @pytest.mark.usefixtures("mock_zeroconf") @@ -97,7 +108,7 @@ async def test_disabled(hass: HomeAssistant): await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - assert hass.states.get(f"{DOMAIN}.devolo.WarningBinaryFI:Test") is None + assert hass.states.get(f"{DOMAIN}.test_door") is None @pytest.mark.usefixtures("mock_zeroconf") @@ -112,7 +123,7 @@ async def test_remove_from_hass(hass: HomeAssistant): await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - state = hass.states.get(f"{DOMAIN}.test") + state = hass.states.get(f"{DOMAIN}.test_door") assert state is not None await hass.config_entries.async_remove(entry.entry_id) await hass.async_block_till_done() diff --git a/tests/components/devolo_home_control/test_climate.py b/tests/components/devolo_home_control/test_climate.py index 98200b66476..c1ecc7ecc29 100644 --- a/tests/components/devolo_home_control/test_climate.py +++ b/tests/components/devolo_home_control/test_climate.py @@ -7,7 +7,12 @@ from homeassistant.components.climate import ( SERVICE_SET_TEMPERATURE, HVACMode, ) -from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_UNAVAILABLE +from homeassistant.const import ( + ATTR_ENTITY_ID, + ATTR_FRIENDLY_NAME, + ATTR_TEMPERATURE, + STATE_UNAVAILABLE, +) from homeassistant.core import HomeAssistant from . import configure_integration @@ -30,6 +35,7 @@ async def test_climate(hass: HomeAssistant): assert state is not None assert state.state == HVACMode.HEAT assert state.attributes[ATTR_TEMPERATURE] == test_gateway.devices["Test"].value + assert state.attributes[ATTR_FRIENDLY_NAME] == "Test" # Emulate websocket message: temperature changed test_gateway.publisher.dispatch("Test", ("Test", 21.0)) diff --git a/tests/components/devolo_home_control/test_cover.py b/tests/components/devolo_home_control/test_cover.py index 1c05c00370b..a783b635001 100644 --- a/tests/components/devolo_home_control/test_cover.py +++ b/tests/components/devolo_home_control/test_cover.py @@ -4,6 +4,7 @@ from unittest.mock import patch from homeassistant.components.cover import ATTR_CURRENT_POSITION, ATTR_POSITION, DOMAIN from homeassistant.const import ( ATTR_ENTITY_ID, + ATTR_FRIENDLY_NAME, SERVICE_CLOSE_COVER, SERVICE_OPEN_COVER, SERVICE_SET_COVER_POSITION, @@ -32,6 +33,7 @@ async def test_cover(hass: HomeAssistant): state = hass.states.get(f"{DOMAIN}.test") assert state is not None assert state.state == STATE_OPEN + assert state.attributes[ATTR_FRIENDLY_NAME] == "Test" assert ( state.attributes[ATTR_CURRENT_POSITION] == test_gateway.devices["Test"] diff --git a/tests/components/devolo_home_control/test_light.py b/tests/components/devolo_home_control/test_light.py index 7b18b28a493..ce4a42f5226 100644 --- a/tests/components/devolo_home_control/test_light.py +++ b/tests/components/devolo_home_control/test_light.py @@ -10,6 +10,7 @@ from homeassistant.components.light import ( ) from homeassistant.const import ( ATTR_ENTITY_ID, + ATTR_FRIENDLY_NAME, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_OFF, @@ -36,6 +37,7 @@ async def test_light_without_binary_sensor(hass: HomeAssistant): state = hass.states.get(f"{DOMAIN}.test") assert state is not None assert state.state == STATE_ON + assert state.attributes[ATTR_FRIENDLY_NAME] == "Test" assert state.attributes[ATTR_COLOR_MODE] == ColorMode.BRIGHTNESS assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] assert state.attributes[ATTR_BRIGHTNESS] == round( diff --git a/tests/components/devolo_home_control/test_siren.py b/tests/components/devolo_home_control/test_siren.py index 97e044738a5..bca54a5b5b8 100644 --- a/tests/components/devolo_home_control/test_siren.py +++ b/tests/components/devolo_home_control/test_siren.py @@ -4,7 +4,12 @@ from unittest.mock import patch import pytest from homeassistant.components.siren import DOMAIN -from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE +from homeassistant.const import ( + ATTR_FRIENDLY_NAME, + STATE_OFF, + STATE_ON, + STATE_UNAVAILABLE, +) from homeassistant.core import HomeAssistant from . import configure_integration @@ -27,6 +32,7 @@ async def test_siren(hass: HomeAssistant): state = hass.states.get(f"{DOMAIN}.test") assert state is not None assert state.state == STATE_OFF + assert state.attributes[ATTR_FRIENDLY_NAME] == "Test" # Emulate websocket message: sensor turned on test_gateway.publisher.dispatch("Test", ("devolo.SirenMultiLevelSwitch:Test", 1)) From 15176300e209a9570bc0fd7dfcfc6edabcdf781d Mon Sep 17 00:00:00 2001 From: jan iversen Date: Tue, 22 Nov 2022 09:57:04 +0100 Subject: [PATCH 0627/1033] Add full test of wrap option (#82308) fixes undefined --- tests/components/modbus/test_sensor.py | 79 ++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/tests/components/modbus/test_sensor.py b/tests/components/modbus/test_sensor.py index b1432876a97..bb9e4285c42 100644 --- a/tests/components/modbus/test_sensor.py +++ b/tests/components/modbus/test_sensor.py @@ -717,6 +717,7 @@ async def test_lazy_error_sensor(hass, mock_do_cycle, start_expect, end_expect): { CONF_NAME: TEST_ENTITY_NAME, CONF_ADDRESS: 51, + CONF_SCAN_INTERVAL: 1, }, ], }, @@ -762,6 +763,84 @@ async def test_struct_sensor(hass, mock_do_cycle, expected): assert hass.states.get(ENTITY_ID).state == expected +@pytest.mark.parametrize( + "do_config", + [ + { + CONF_SENSORS: [ + { + CONF_NAME: TEST_ENTITY_NAME, + CONF_ADDRESS: 201, + CONF_SCAN_INTERVAL: 1, + }, + ], + }, + ], +) +@pytest.mark.parametrize( + "config_addon,register_words,expected", + [ + ( + { + CONF_COUNT: 1, + CONF_SWAP: CONF_SWAP_NONE, + CONF_DATA_TYPE: DataType.UINT16, + }, + [0x0102], + "258", + ), + ( + { + CONF_COUNT: 1, + CONF_SWAP: CONF_SWAP_BYTE, + CONF_DATA_TYPE: DataType.UINT16, + }, + [0x0102], + "513", + ), + ( + { + CONF_COUNT: 2, + CONF_SWAP: CONF_SWAP_NONE, + CONF_DATA_TYPE: DataType.UINT32, + }, + [0x0102, 0x0304], + "16909060", + ), + ( + { + CONF_COUNT: 2, + CONF_SWAP: CONF_SWAP_BYTE, + CONF_DATA_TYPE: DataType.UINT32, + }, + [0x0102, 0x0304], + "33620995", + ), + ( + { + CONF_COUNT: 2, + CONF_SWAP: CONF_SWAP_WORD, + CONF_DATA_TYPE: DataType.UINT32, + }, + [0x0102, 0x0304], + "50594050", + ), + ( + { + CONF_COUNT: 2, + CONF_SWAP: CONF_SWAP_WORD_BYTE, + CONF_DATA_TYPE: DataType.UINT32, + }, + [0x0102, 0x0304], + "67305985", + ), + ], +) +async def test_wrap_sensor(hass, mock_do_cycle, expected): + """Run test for sensor struct.""" + assert hass.states.get(ENTITY_ID).state == expected + + @pytest.mark.parametrize( "mock_test_state", [(State(ENTITY_ID, "117"), State(f"{ENTITY_ID}_1", "119"))], From be7e76f302f670da61f24b96332a061a5032f479 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 22 Nov 2022 11:17:23 +0100 Subject: [PATCH 0628/1033] Migrate ZHA when enabling multi-PAN support on HA Yellow (#82213) * Migrate ZHA when enabling multi-PAN support on HA Yellow * Refactor BaseZhaFlow.async_step_maybe_confirm_ezsp_restore * Change data passed to ZHA to initiate migration * Catch errors during ZHA migration * Fix ZhaMigrationHelper.async_prepare_yellow_migration return value * Improve test coverage * Improve test coverage * Fix spelling * Rename some none HA yellow specifics * Rename again * Increase number of migration retries + refactor * Suppress OperationNotAllowed when reloading * Adjust tests --- .../homeassistant_hardware/manifest.json | 3 +- .../silabs_multiprotocol_addon.py | 48 +++ .../homeassistant_yellow/__init__.py | 22 +- .../homeassistant_yellow/config_flow.py | 14 +- .../components/homeassistant_yellow/const.py | 10 + .../homeassistant_yellow/strings.json | 5 +- .../homeassistant_yellow/translations/en.json | 5 +- homeassistant/components/zha/config_flow.py | 81 +--- homeassistant/components/zha/radio_manager.py | 230 ++++++++++- .../homeassistant_hardware/conftest.py | 30 +- .../test_silabs_multiprotocol_addon.py | 371 +++++++++++++++++- .../homeassistant_yellow/conftest.py | 5 +- .../homeassistant_yellow/test_config_flow.py | 86 ++++ .../homeassistant_yellow/test_init.py | 2 +- tests/components/zha/test_config_flow.py | 14 +- tests/components/zha/test_radio_manager.py | 311 +++++++++++++++ 16 files changed, 1120 insertions(+), 117 deletions(-) create mode 100644 tests/components/zha/test_radio_manager.py diff --git a/homeassistant/components/homeassistant_hardware/manifest.json b/homeassistant/components/homeassistant_hardware/manifest.json index 32472bcdded..722306720a5 100644 --- a/homeassistant/components/homeassistant_hardware/manifest.json +++ b/homeassistant/components/homeassistant_hardware/manifest.json @@ -3,5 +3,6 @@ "name": "Home Assistant Hardware", "documentation": "https://www.home-assistant.io/integrations/homeassistant_hardware", "codeowners": ["@home-assistant/core"], - "integration_type": "system" + "integration_type": "system", + "after_dependencies": ["zha"] } diff --git a/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py b/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py index b4dcca0e329..0724eee9ed5 100644 --- a/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py +++ b/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py @@ -17,6 +17,8 @@ from homeassistant.components.hassio import ( AddonState, is_hassio, ) +from homeassistant.components.zha import DOMAIN as ZHA_DOMAIN +from homeassistant.components.zha.radio_manager import ZhaMultiPANMigrationHelper from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import ( AbortFlow, @@ -77,6 +79,7 @@ class BaseMultiPanFlow(FlowHandler): # If we install the add-on we should uninstall it on entry remove. self.install_task: asyncio.Task | None = None self.start_task: asyncio.Task | None = None + self._zha_migration_mgr: ZhaMultiPANMigrationHelper | None = None @property @abstractmethod @@ -87,6 +90,18 @@ class BaseMultiPanFlow(FlowHandler): async def _async_serial_port_settings(self) -> SerialPortSettings: """Return the radio serial port settings.""" + @abstractmethod + async def _async_zha_physical_discovery(self) -> dict[str, Any]: + """Return ZHA discovery data when multiprotocol FW is not used. + + Passed to ZHA do determine if the ZHA config entry is connected to the radio + being migrated. + """ + + @abstractmethod + def _zha_name(self) -> str: + """Return the ZHA name.""" + async def async_step_install_addon( self, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -260,6 +275,31 @@ class OptionsFlowHandler(BaseMultiPanFlow, config_entries.OptionsFlow): **dataclasses.asdict(serial_port_settings), } + # Initiate ZHA migration + zha_entries = self.hass.config_entries.async_entries(ZHA_DOMAIN) + + if zha_entries: + zha_migration_mgr = ZhaMultiPANMigrationHelper(self.hass, zha_entries[0]) + migration_data = { + "new_discovery_info": { + "name": self._zha_name(), + "port": { + "path": get_zigbee_socket(self.hass, addon_info), + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + }, + "old_discovery_info": await self._async_zha_physical_discovery(), + } + _LOGGER.debug("Starting ZHA migration with: %s", migration_data) + try: + if await zha_migration_mgr.async_initiate_migration(migration_data): + self._zha_migration_mgr = zha_migration_mgr + except Exception as err: + _LOGGER.exception("Unexpected exception during ZHA migration") + raise AbortFlow("zha_migration_failed") from err + if new_addon_config != addon_config: # Copy the add-on config to keep the objects separate. self.original_addon_config = dict(addon_config) @@ -277,6 +317,14 @@ class OptionsFlowHandler(BaseMultiPanFlow, config_entries.OptionsFlow): self.hass.config_entries.async_reload(self.config_entry.entry_id) ) + # Finish ZHA migration if needed + if self._zha_migration_mgr: + try: + await self._zha_migration_mgr.async_finish_migration() + except Exception as err: + _LOGGER.exception("Unexpected exception during ZHA migration") + raise AbortFlow("zha_migration_failed") from err + return self.async_create_entry(title="", data={}) async def async_step_addon_installed( diff --git a/homeassistant/components/homeassistant_yellow/__init__.py b/homeassistant/components/homeassistant_yellow/__init__.py index 83a3d9dad32..a7e351535aa 100644 --- a/homeassistant/components/homeassistant_yellow/__init__.py +++ b/homeassistant/components/homeassistant_yellow/__init__.py @@ -18,6 +18,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady +from .const import ZHA_HW_DISCOVERY_DATA + _LOGGER = logging.getLogger(__name__) @@ -52,22 +54,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryNotReady if addon_info.state == AddonState.NOT_INSTALLED: - path = "/dev/ttyAMA1" + hw_discovery_data = ZHA_HW_DISCOVERY_DATA else: - path = get_zigbee_socket(hass, addon_info) - - await hass.config_entries.flow.async_init( - "zha", - context={"source": "hardware"}, - data={ - "name": "Yellow", + hw_discovery_data = { + "name": "Yellow Multi-PAN", "port": { - "path": path, + "path": get_zigbee_socket(hass, addon_info), "baudrate": 115200, "flow_control": "hardware", }, "radio_type": "efr32", - }, + } + + await hass.config_entries.flow.async_init( + "zha", + context={"source": "hardware"}, + data=hw_discovery_data, ) return True diff --git a/homeassistant/components/homeassistant_yellow/config_flow.py b/homeassistant/components/homeassistant_yellow/config_flow.py index 48a797a2aab..e5e6bb3b2c6 100644 --- a/homeassistant/components/homeassistant_yellow/config_flow.py +++ b/homeassistant/components/homeassistant_yellow/config_flow.py @@ -8,7 +8,7 @@ from homeassistant.config_entries import ConfigEntry, ConfigFlow from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult -from .const import DOMAIN +from .const import DOMAIN, ZHA_HW_DISCOVERY_DATA class HomeAssistantYellowConfigFlow(ConfigFlow, domain=DOMAIN): @@ -44,3 +44,15 @@ class HomeAssistantYellowOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandl baudrate="115200", flow_control=True, ) + + async def _async_zha_physical_discovery(self) -> dict[str, Any]: + """Return ZHA discovery data when multiprotocol FW is not used. + + Passed to ZHA do determine if the ZHA config entry is connected to the radio + being migrated. + """ + return ZHA_HW_DISCOVERY_DATA + + def _zha_name(self) -> str: + """Return the ZHA name.""" + return "Yellow Multi-PAN" diff --git a/homeassistant/components/homeassistant_yellow/const.py b/homeassistant/components/homeassistant_yellow/const.py index 41eae70b3f2..5d693d819b6 100644 --- a/homeassistant/components/homeassistant_yellow/const.py +++ b/homeassistant/components/homeassistant_yellow/const.py @@ -1,3 +1,13 @@ """Constants for the Home Assistant Yellow integration.""" DOMAIN = "homeassistant_yellow" + +ZHA_HW_DISCOVERY_DATA = { + "name": "Yellow", + "port": { + "path": "/dev/ttyAMA1", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", +} diff --git a/homeassistant/components/homeassistant_yellow/strings.json b/homeassistant/components/homeassistant_yellow/strings.json index df1eb780a96..1810597b242 100644 --- a/homeassistant/components/homeassistant_yellow/strings.json +++ b/homeassistant/components/homeassistant_yellow/strings.json @@ -3,7 +3,7 @@ "step": { "addon_not_installed": { "title": "Enable multiprotocol support on the IEEE 802.15.4 radio", - "description": "When multiprotocol support is enabled, the Home Assistant Yellow's IEEE 802.15.4 radio can be used for both Zigbee and Thread (used by Matter) at the same time. Note: This is an experimental feature.", + "description": "When multiprotocol support is enabled, the Home Assistant Yellow's IEEE 802.15.4 radio can be used for both Zigbee and Thread (used by Matter) at the same time. If the radio is already used by the ZHA Zigbee integration, ZHA will be reconfigured to use the multiprotocol firmware.\n\nNote: This is an experimental feature.", "data": { "enable_multi_pan": "Enable multiprotocol support" } @@ -30,7 +30,8 @@ "addon_install_failed": "Failed to install the Silicon Labs Multiprotocol add-on.", "addon_set_config_failed": "Failed to set Silicon Labs Multiprotocol configuration.", "addon_start_failed": "Failed to start the Silicon Labs Multiprotocol add-on.", - "not_hassio": "The hardware options can only be configured on HassOS installations." + "not_hassio": "The hardware options can only be configured on HassOS installations.", + "zha_migration_failed": "The ZHA migration did not succeed." }, "progress": { "install_addon": "Please wait while the Silicon Labs Multiprotocol add-on installation finishes. This can take several minutes.", diff --git a/homeassistant/components/homeassistant_yellow/translations/en.json b/homeassistant/components/homeassistant_yellow/translations/en.json index 252303ea428..0b074452652 100644 --- a/homeassistant/components/homeassistant_yellow/translations/en.json +++ b/homeassistant/components/homeassistant_yellow/translations/en.json @@ -5,7 +5,8 @@ "addon_install_failed": "Failed to install the Silicon Labs Multiprotocol add-on.", "addon_set_config_failed": "Failed to set Silicon Labs Multiprotocol configuration.", "addon_start_failed": "Failed to start the Silicon Labs Multiprotocol add-on.", - "not_hassio": "The hardware options can only be configured on HassOS installations." + "not_hassio": "The hardware options can only be configured on HassOS installations.", + "zha_migration_failed": "The ZHA migration did not succeed." }, "error": { "unknown": "Unexpected error" @@ -22,7 +23,7 @@ "data": { "enable_multi_pan": "Enable multiprotocol support" }, - "description": "When multiprotocol support is enabled, the Home Assistant Yellow's IEEE 802.15.4 radio can be used for both Zigbee and Thread (used by Matter) at the same time. Note: This is an experimental feature.", + "description": "When multiprotocol support is enabled, the Home Assistant Yellow's IEEE 802.15.4 radio can be used for both Zigbee and Thread (used by Matter) at the same time. If the radio is already used by the ZHA Zigbee integration, ZHA will be reconfigured to use the multiprotocol firmware.\n\nNote: This is an experimental feature.", "title": "Enable multiprotocol support on the IEEE 802.15.4 radio" }, "install_addon": { diff --git a/homeassistant/components/zha/config_flow.py b/homeassistant/components/zha/config_flow.py index e8745684275..df5fa047c99 100644 --- a/homeassistant/components/zha/config_flow.py +++ b/homeassistant/components/zha/config_flow.py @@ -2,7 +2,6 @@ from __future__ import annotations import collections -import copy import json from typing import Any @@ -25,10 +24,9 @@ from .core.const import ( CONF_FLOWCONTROL, CONF_RADIO_TYPE, DOMAIN, - EZSP_OVERWRITE_EUI64, RadioType, ) -from .radio_manager import ZhaRadioManager +from .radio_manager import HARDWARE_DISCOVERY_SCHEMA, ZhaRadioManager CONF_MANUAL_PATH = "Enter Manually" SUPPORTED_PORT_SETTINGS = ( @@ -54,14 +52,6 @@ UPLOADED_BACKUP_FILE = "uploaded_backup_file" DEFAULT_ZHA_ZEROCONF_PORT = 6638 ESPHOME_API_PORT = 6053 -HARDWARE_DISCOVERY_SCHEMA = vol.Schema( - { - vol.Required("name"): str, - vol.Required("port"): dict, - vol.Required("radio_type"): str, - } -) - def _format_backup_choice( backup: zigpy.backups.NetworkBackup, *, pan_ids: bool = True @@ -80,33 +70,6 @@ def _format_backup_choice( return f"{dt.as_local(backup.backup_time).strftime('%c')} ({identifier})" -def _allow_overwrite_ezsp_ieee( - backup: zigpy.backups.NetworkBackup, -) -> zigpy.backups.NetworkBackup: - """Return a new backup with the flag to allow overwriting the EZSP EUI64.""" - new_stack_specific = copy.deepcopy(backup.network_info.stack_specific) - new_stack_specific.setdefault("ezsp", {})[EZSP_OVERWRITE_EUI64] = True - - return backup.replace( - network_info=backup.network_info.replace(stack_specific=new_stack_specific) - ) - - -def _prevent_overwrite_ezsp_ieee( - backup: zigpy.backups.NetworkBackup, -) -> zigpy.backups.NetworkBackup: - """Return a new backup without the flag to allow overwriting the EZSP EUI64.""" - if "ezsp" not in backup.network_info.stack_specific: - return backup - - new_stack_specific = copy.deepcopy(backup.network_info.stack_specific) - new_stack_specific.setdefault("ezsp", {}).pop(EZSP_OVERWRITE_EUI64, None) - - return backup.replace( - network_info=backup.network_info.replace(stack_specific=new_stack_specific) - ) - - class BaseZhaFlow(FlowHandler): """Mixin for common ZHA flow steps and forms.""" @@ -407,46 +370,14 @@ class BaseZhaFlow(FlowHandler): self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Confirm restore for EZSP radios that require permanent IEEE writes.""" - assert self._radio_mgr.chosen_backup is not None - - if self._radio_mgr.radio_type != RadioType.ezsp: - await self._radio_mgr.restore_backup(self._radio_mgr.chosen_backup) - return await self._async_create_radio_entry() - - # We have no way to partially load network settings if no network is formed - if self._radio_mgr.current_settings is None: - # Since we are going to be restoring the backup anyways, write it to the - # radio without overwriting the IEEE but don't take a backup with these - # temporary settings - temp_backup = _prevent_overwrite_ezsp_ieee(self._radio_mgr.chosen_backup) - await self._radio_mgr.restore_backup(temp_backup, create_new=False) - await self._radio_mgr.async_load_network_settings() - - assert self._radio_mgr.current_settings is not None - - if ( - self._radio_mgr.current_settings.node_info.ieee - == self._radio_mgr.chosen_backup.node_info.ieee - or not self._radio_mgr.current_settings.network_info.metadata["ezsp"][ - "can_write_custom_eui64" - ] - ): - # No point in prompting the user if the backup doesn't have a new IEEE - # address or if there is no way to overwrite the IEEE address a second time - await self._radio_mgr.restore_backup(self._radio_mgr.chosen_backup) - + call_step_2 = await self._radio_mgr.async_restore_backup_step_1() + if not call_step_2: return await self._async_create_radio_entry() if user_input is not None: - backup = self._radio_mgr.chosen_backup - - if user_input[OVERWRITE_COORDINATOR_IEEE]: - backup = _allow_overwrite_ezsp_ieee(backup) - - # If the user declined to overwrite the IEEE *and* we wrote the backup to - # their empty radio above, restoring it again would be redundant. - await self._radio_mgr.restore_backup(backup) - + await self._radio_mgr.async_restore_backup_step_2( + user_input[OVERWRITE_COORDINATOR_IEEE] + ) return await self._async_create_radio_entry() return self.async_show_form( diff --git a/homeassistant/components/zha/radio_manager.py b/homeassistant/components/zha/radio_manager.py index 05629ec8249..512f26a139b 100644 --- a/homeassistant/components/zha/radio_manager.py +++ b/homeassistant/components/zha/radio_manager.py @@ -3,23 +3,28 @@ from __future__ import annotations import asyncio import contextlib +import copy import logging import os from typing import Any +import voluptuous as vol from zigpy.application import ControllerApplication import zigpy.backups from zigpy.config import CONF_DEVICE, CONF_DEVICE_PATH from zigpy.exceptions import NetworkNotFormed +from homeassistant import config_entries from homeassistant.core import HomeAssistant from .core.const import ( CONF_DATABASE, + CONF_RADIO_TYPE, CONF_ZIGPY, DATA_ZHA, DATA_ZHA_CONFIG, DEFAULT_DATABASE_NAME, + EZSP_OVERWRITE_EUI64, RadioType, ) @@ -35,9 +40,53 @@ AUTOPROBE_RADIOS = ( CONNECT_DELAY_S = 1.0 +MIGRATION_RETRIES = 100 + +HARDWARE_DISCOVERY_SCHEMA = vol.Schema( + { + vol.Required("name"): str, + vol.Required("port"): dict, + vol.Required("radio_type"): str, + } +) + +HARDWARE_MIGRATION_SCHEMA = vol.Schema( + { + vol.Required("new_discovery_info"): HARDWARE_DISCOVERY_SCHEMA, + vol.Required("old_discovery_info"): HARDWARE_DISCOVERY_SCHEMA, + } +) + _LOGGER = logging.getLogger(__name__) +def _allow_overwrite_ezsp_ieee( + backup: zigpy.backups.NetworkBackup, +) -> zigpy.backups.NetworkBackup: + """Return a new backup with the flag to allow overwriting the EZSP EUI64.""" + new_stack_specific = copy.deepcopy(backup.network_info.stack_specific) + new_stack_specific.setdefault("ezsp", {})[EZSP_OVERWRITE_EUI64] = True + + return backup.replace( + network_info=backup.network_info.replace(stack_specific=new_stack_specific) + ) + + +def _prevent_overwrite_ezsp_ieee( + backup: zigpy.backups.NetworkBackup, +) -> zigpy.backups.NetworkBackup: + """Return a new backup without the flag to allow overwriting the EZSP EUI64.""" + if "ezsp" not in backup.network_info.stack_specific: + return backup + + new_stack_specific = copy.deepcopy(backup.network_info.stack_specific) + new_stack_specific.setdefault("ezsp", {}).pop(EZSP_OVERWRITE_EUI64, None) + + return backup.replace( + network_info=backup.network_info.replace(stack_specific=new_stack_specific) + ) + + class ZhaRadioManager: """Helper class with radio related functionality.""" @@ -96,7 +145,8 @@ class ZhaRadioManager: async with self._connect_zigpy_app() as app: await app.backups.restore_backup(backup, **kwargs) - def parse_radio_type(self, radio_type: str) -> RadioType: + @staticmethod + def parse_radio_type(radio_type: str) -> RadioType: """Parse a radio type name, accounting for past aliases.""" if radio_type == "efr32": return RadioType.ezsp @@ -127,8 +177,12 @@ class ZhaRadioManager: return False - async def async_load_network_settings(self, create_backup: bool = False) -> None: + async def async_load_network_settings( + self, *, create_backup: bool = False + ) -> zigpy.backups.NetworkBackup | None: """Connect to the radio and load its current network settings.""" + backup = None + async with self._connect_zigpy_app() as app: # Check if the stick has any settings and load them try: @@ -142,11 +196,13 @@ class ZhaRadioManager: ) if create_backup: - await app.backups.create_backup() + backup = await app.backups.create_backup() # The list of backups will always exist self.backups = app.backups.backups.copy() + return backup + async def async_form_network(self) -> None: """Form a brand new network.""" async with self._connect_zigpy_app() as app: @@ -156,3 +212,171 @@ class ZhaRadioManager: """Reset the current adapter.""" async with self._connect_zigpy_app() as app: await app.reset_network_info() + + async def async_restore_backup_step_1(self) -> bool: + """Prepare restoring backup. + + Returns True if async_restore_backup_step_2 should be called. + """ + assert self.chosen_backup is not None + + if self.radio_type != RadioType.ezsp: + await self.restore_backup(self.chosen_backup) + return False + + # We have no way to partially load network settings if no network is formed + if self.current_settings is None: + # Since we are going to be restoring the backup anyways, write it to the + # radio without overwriting the IEEE but don't take a backup with these + # temporary settings + temp_backup = _prevent_overwrite_ezsp_ieee(self.chosen_backup) + await self.restore_backup(temp_backup, create_new=False) + await self.async_load_network_settings() + + assert self.current_settings is not None + + if ( + self.current_settings.node_info.ieee == self.chosen_backup.node_info.ieee + or not self.current_settings.network_info.metadata["ezsp"][ + "can_write_custom_eui64" + ] + ): + # No point in prompting the user if the backup doesn't have a new IEEE + # address or if there is no way to overwrite the IEEE address a second time + await self.restore_backup(self.chosen_backup) + + return False + + return True + + async def async_restore_backup_step_2(self, overwrite_ieee: bool) -> None: + """Restore backup and optionally overwrite IEEE.""" + assert self.chosen_backup is not None + + backup = self.chosen_backup + + if overwrite_ieee: + backup = _allow_overwrite_ezsp_ieee(backup) + + # If the user declined to overwrite the IEEE *and* we wrote the backup to + # their empty radio above, restoring it again would be redundant. + await self.restore_backup(backup) + + +class ZhaMultiPANMigrationHelper: + """Helper class for automatic migration when upgrading the firmware of a radio. + + This class is currently only intended to be used when changing the firmware on the + radio used in the Home Assistant Sky Connect USB stick and the Home Asssistant Yellow + from Zigbee only firmware to firmware supporting both Zigbee and Thread. + """ + + def __init__( + self, hass: HomeAssistant, config_entry: config_entries.ConfigEntry + ) -> None: + """Initialize MigrationHelper instance.""" + self._config_entry = config_entry + self._hass = hass + self._radio_mgr = ZhaRadioManager() + self._radio_mgr.hass = hass + + async def async_initiate_migration(self, data: dict[str, Any]) -> bool: + """Initiate ZHA migration. + + The passed data should contain: + - Discovery data identifying the device being firmware updated + - Discovery data for connecting to the device after the firmware update is + completed. + + Returns True if async_finish_migration should be called after the firmware + update is completed. + """ + migration_data = HARDWARE_MIGRATION_SCHEMA(data) + + name = migration_data["new_discovery_info"]["name"] + new_radio_type = ZhaRadioManager.parse_radio_type( + migration_data["new_discovery_info"]["radio_type"] + ) + old_radio_type = ZhaRadioManager.parse_radio_type( + migration_data["old_discovery_info"]["radio_type"] + ) + + new_device_settings = new_radio_type.controller.SCHEMA_DEVICE( + migration_data["new_discovery_info"]["port"] + ) + old_device_settings = old_radio_type.controller.SCHEMA_DEVICE( + migration_data["old_discovery_info"]["port"] + ) + + if ( + self._config_entry.data[CONF_DEVICE][CONF_DEVICE_PATH] + != old_device_settings[CONF_DEVICE_PATH] + ): + # ZHA is using another radio, do nothing + return False + + try: + await self._hass.config_entries.async_unload(self._config_entry.entry_id) + except config_entries.OperationNotAllowed: + # ZHA is not running + pass + + # Temporarily connect to the old radio to read its settings + old_radio_mgr = ZhaRadioManager() + old_radio_mgr.hass = self._hass + old_radio_mgr.radio_type = old_radio_type + old_radio_mgr.device_path = old_device_settings[CONF_DEVICE_PATH] + old_radio_mgr.device_settings = old_device_settings + backup = await old_radio_mgr.async_load_network_settings(create_backup=True) + + # Then configure the radio manager for the new radio to use the new settings + self._radio_mgr.chosen_backup = backup + self._radio_mgr.radio_type = new_radio_type + self._radio_mgr.device_path = new_device_settings[CONF_DEVICE_PATH] + self._radio_mgr.device_settings = new_device_settings + device_settings = self._radio_mgr.device_settings.copy() # type: ignore[union-attr] + + # Update the config entry settings + self._hass.config_entries.async_update_entry( + entry=self._config_entry, + data={ + CONF_DEVICE: device_settings, + CONF_RADIO_TYPE: self._radio_mgr.radio_type.name, + }, + options=self._config_entry.options, + title=name, + ) + return True + + async def async_finish_migration(self) -> None: + """Finish ZHA migration. + + Throws an exception if the migration did not succeed. + """ + # Restore the backup, permanently overwriting the device IEEE address + for retry in range(MIGRATION_RETRIES): + try: + if await self._radio_mgr.async_restore_backup_step_1(): + await self._radio_mgr.async_restore_backup_step_2(True) + + break + except OSError as err: + if retry >= MIGRATION_RETRIES - 1: + raise + + _LOGGER.debug( + "Failed to restore backup %r, retrying in %s seconds", + err, + CONNECT_DELAY_S, + ) + + await asyncio.sleep(CONNECT_DELAY_S) + + _LOGGER.debug("Restored backup after %s retries", retry) + + # Launch ZHA again + try: + await self._hass.config_entries.async_setup(self._config_entry.entry_id) + except config_entries.OperationNotAllowed: + # ZHA is not unloaded + pass diff --git a/tests/components/homeassistant_hardware/conftest.py b/tests/components/homeassistant_hardware/conftest.py index d46241b46e2..fd0ce2e761b 100644 --- a/tests/components/homeassistant_hardware/conftest.py +++ b/tests/components/homeassistant_hardware/conftest.py @@ -1,9 +1,37 @@ """Test fixtures for the Home Assistant Hardware integration.""" -from unittest.mock import patch +from collections.abc import Generator +from typing import Any +from unittest.mock import MagicMock, patch import pytest +@pytest.fixture(autouse=True) +def mock_zha_config_flow_setup() -> Generator[None, None, None]: + """Mock the radio connection and probing of the ZHA config flow.""" + + def mock_probe(config: dict[str, Any]) -> None: + # The radio probing will return the correct baudrate + return {**config, "baudrate": 115200} + + mock_connect_app = MagicMock() + mock_connect_app.__aenter__.return_value.backups.backups = [MagicMock()] + mock_connect_app.__aenter__.return_value.backups.create_backup.return_value = ( + MagicMock() + ) + + with patch( + "bellows.zigbee.application.ControllerApplication.probe", side_effect=mock_probe + ), patch( + "homeassistant.components.zha.radio_manager.ZhaRadioManager._connect_zigpy_app", + return_value=mock_connect_app, + ), patch( + "homeassistant.components.zha.async_setup_entry", + return_value=True, + ): + yield + + @pytest.fixture(name="addon_running") def mock_addon_running(addon_store_info, addon_info): """Mock add-on already running.""" diff --git a/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py b/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py index 4b966033857..f61fd5e8d76 100644 --- a/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py +++ b/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py @@ -1,4 +1,4 @@ -"""Test the Home Assistant Yellow config flow.""" +"""Test the Home Assistant Hardware silabs multiprotocol addon manager.""" from __future__ import annotations from collections.abc import Generator @@ -10,6 +10,7 @@ import pytest from homeassistant import config_entries from homeassistant.components.hassio.handler import HassioAPIError from homeassistant.components.homeassistant_hardware import silabs_multiprotocol_addon +from homeassistant.components.zha.core.const import DOMAIN as ZHA_DOMAIN from homeassistant.config_entries import ConfigEntry, ConfigFlow from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult, FlowResultType @@ -20,7 +21,7 @@ TEST_DOMAIN = "test" class TestConfigFlow(ConfigFlow, domain=TEST_DOMAIN): - """Handle a config flow for Home Assistant Yellow.""" + """Handle a config flow for the silabs multiprotocol add-on.""" VERSION = 1 @@ -37,11 +38,11 @@ class TestConfigFlow(ConfigFlow, domain=TEST_DOMAIN): if self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") - return self.async_create_entry(title="Home Assistant Yellow", data={}) + return self.async_create_entry(title="Test HW", data={}) class TestOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandler): - """Handle an option flow for Home Assistant Yellow.""" + """Handle an option flow for the silabs multiprotocol add-on.""" async def _async_serial_port_settings( self, @@ -53,6 +54,26 @@ class TestOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandler): flow_control=True, ) + async def _async_zha_physical_discovery(self) -> dict[str, Any]: + """Return ZHA discovery data when multiprotocol FW is not used. + + Passed to ZHA do determine if the ZHA config entry is connected to the radio + being migrated. + """ + return { + "name": "Test", + "port": { + "path": "/dev/ttyTEST123", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + } + + def _zha_name(self) -> str: + """Return the ZHA name.""" + return "Test Multi-PAN" + @pytest.fixture(autouse=True) def config_flow_handler( @@ -80,7 +101,7 @@ async def test_option_flow_install_multi_pan_addon( data={}, domain=TEST_DOMAIN, options={}, - title="Home Assistant Yellow", + title="Test HW", ) config_entry.add_to_hass(hass) @@ -132,6 +153,186 @@ async def test_option_flow_install_multi_pan_addon( assert result["type"] == FlowResultType.CREATE_ENTRY +async def test_option_flow_install_multi_pan_addon_zha( + hass: HomeAssistant, + addon_store_info, + addon_info, + install_addon, + set_addon_options, + start_addon, +) -> None: + """Test installing the multi pan addon when a zha config entry exists.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Test HW", + ) + config_entry.add_to_hass(hass) + + zha_config_entry = MockConfigEntry( + data={"device": {"path": "/dev/ttyTEST123"}, "radio_type": "ezsp"}, + domain=ZHA_DOMAIN, + options={}, + title="Test", + ) + zha_config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": True, + }, + ) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + assert result["progress_action"] == "install_addon" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "configure_addon" + install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + set_addon_options.assert_called_once_with( + hass, + "core_silabs_multiprotocol", + { + "options": { + "autoflash_firmware": True, + "device": "/dev/ttyTEST123", + "baudrate": "115200", + "flow_control": True, + } + }, + ) + # Check the ZHA config entry data is updated + assert zha_config_entry.data == { + "device": { + "path": "socket://core-silabs-multiprotocol:9999", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "ezsp", + } + assert zha_config_entry.title == "Test Multi-PAN" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "finish_addon_setup" + start_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.CREATE_ENTRY + + +async def test_option_flow_install_multi_pan_addon_zha_other_radio( + hass: HomeAssistant, + addon_store_info, + addon_info, + install_addon, + set_addon_options, + start_addon, +) -> None: + """Test installing the multi pan addon when a zha config entry exists.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Test HW", + ) + config_entry.add_to_hass(hass) + + zha_config_entry = MockConfigEntry( + data={ + "device": { + "path": "/dev/other_radio", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "ezsp", + }, + domain=ZHA_DOMAIN, + options={}, + title="Test HW", + ) + zha_config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": True, + }, + ) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + assert result["progress_action"] == "install_addon" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "configure_addon" + install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + addon_info.return_value["hostname"] = "core-silabs-multiprotocol" + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + set_addon_options.assert_called_once_with( + hass, + "core_silabs_multiprotocol", + { + "options": { + "autoflash_firmware": True, + "device": "/dev/ttyTEST123", + "baudrate": "115200", + "flow_control": True, + } + }, + ) + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "finish_addon_setup" + start_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.CREATE_ENTRY + + # Check the ZHA entry data is not changed + assert zha_config_entry.data == { + "device": { + "path": "/dev/other_radio", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "ezsp", + } + + async def test_option_flow_non_hassio( hass: HomeAssistant, ) -> None: @@ -143,7 +344,7 @@ async def test_option_flow_non_hassio( data={}, domain=TEST_DOMAIN, options={}, - title="Home Assistant Yellow", + title="Test HW", ) config_entry.add_to_hass(hass) @@ -165,7 +366,7 @@ async def test_option_flow_addon_installed_other_device( data={}, domain=TEST_DOMAIN, options={}, - title="Home Assistant Yellow", + title="Test HW", ) config_entry.add_to_hass(hass) @@ -196,7 +397,7 @@ async def test_option_flow_addon_installed_same_device( data={}, domain=TEST_DOMAIN, options={}, - title="Home Assistant Yellow", + title="Test HW", ) config_entry.add_to_hass(hass) @@ -225,7 +426,7 @@ async def test_option_flow_do_not_install_multi_pan_addon( data={}, domain=TEST_DOMAIN, options={}, - title="Home Assistant Yellow", + title="Test HW", ) config_entry.add_to_hass(hass) @@ -263,7 +464,7 @@ async def test_option_flow_install_multi_pan_addon_install_fails( data={}, domain=TEST_DOMAIN, options={}, - title="Home Assistant Yellow", + title="Test HW", ) config_entry.add_to_hass(hass) @@ -312,7 +513,7 @@ async def test_option_flow_install_multi_pan_addon_start_fails( data={}, domain=TEST_DOMAIN, options={}, - title="Home Assistant Yellow", + title="Test HW", ) config_entry.add_to_hass(hass) @@ -382,7 +583,7 @@ async def test_option_flow_install_multi_pan_addon_set_options_fails( data={}, domain=TEST_DOMAIN, options={}, - title="Home Assistant Yellow", + title="Test HW", ) config_entry.add_to_hass(hass) @@ -428,7 +629,7 @@ async def test_option_flow_addon_info_fails( data={}, domain=TEST_DOMAIN, options={}, - title="Home Assistant Yellow", + title="Test HW", ) config_entry.add_to_hass(hass) @@ -439,3 +640,147 @@ async def test_option_flow_addon_info_fails( result = await hass.config_entries.options.async_init(config_entry.entry_id) assert result["type"] == FlowResultType.ABORT assert result["reason"] == "addon_info_failed" + + +@patch( + "homeassistant.components.zha.radio_manager.ZhaMultiPANMigrationHelper.async_initiate_migration", + side_effect=Exception("Boom!"), +) +async def test_option_flow_install_multi_pan_addon_zha_migration_fails_step_1( + mock_initiate_migration, + hass: HomeAssistant, + addon_store_info, + addon_info, + install_addon, + set_addon_options, + start_addon, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Test HW", + ) + config_entry.add_to_hass(hass) + + zha_config_entry = MockConfigEntry( + data={"device": {"path": "/dev/ttyTEST123"}, "radio_type": "ezsp"}, + domain=ZHA_DOMAIN, + options={}, + title="Test", + ) + zha_config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": True, + }, + ) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + assert result["progress_action"] == "install_addon" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "configure_addon" + install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "zha_migration_failed" + set_addon_options.assert_not_called() + + +@patch( + "homeassistant.components.zha.radio_manager.ZhaMultiPANMigrationHelper.async_finish_migration", + side_effect=Exception("Boom!"), +) +async def test_option_flow_install_multi_pan_addon_zha_migration_fails_step_2( + mock_finish_migration, + hass: HomeAssistant, + addon_store_info, + addon_info, + install_addon, + set_addon_options, + start_addon, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=TEST_DOMAIN, + options={}, + title="Test HW", + ) + config_entry.add_to_hass(hass) + + zha_config_entry = MockConfigEntry( + data={"device": {"path": "/dev/ttyTEST123"}, "radio_type": "ezsp"}, + domain=ZHA_DOMAIN, + options={}, + title="Test", + ) + zha_config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": True, + }, + ) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + assert result["progress_action"] == "install_addon" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "configure_addon" + install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + set_addon_options.assert_called_once_with( + hass, + "core_silabs_multiprotocol", + { + "options": { + "autoflash_firmware": True, + "device": "/dev/ttyTEST123", + "baudrate": "115200", + "flow_control": True, + } + }, + ) + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "finish_addon_setup" + start_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "zha_migration_failed" diff --git a/tests/components/homeassistant_yellow/conftest.py b/tests/components/homeassistant_yellow/conftest.py index 23ef93ee8c6..62595c11fe1 100644 --- a/tests/components/homeassistant_yellow/conftest.py +++ b/tests/components/homeassistant_yellow/conftest.py @@ -15,7 +15,10 @@ def mock_zha_config_flow_setup() -> Generator[None, None, None]: return {**config, "baudrate": 115200} mock_connect_app = MagicMock() - mock_connect_app.__aenter__.return_value.backups.backups = [] + mock_connect_app.__aenter__.return_value.backups.backups = [MagicMock()] + mock_connect_app.__aenter__.return_value.backups.create_backup.return_value = ( + MagicMock() + ) with patch( "bellows.zigbee.application.ControllerApplication.probe", side_effect=mock_probe diff --git a/tests/components/homeassistant_yellow/test_config_flow.py b/tests/components/homeassistant_yellow/test_config_flow.py index 91f15f792d5..a956f812d8a 100644 --- a/tests/components/homeassistant_yellow/test_config_flow.py +++ b/tests/components/homeassistant_yellow/test_config_flow.py @@ -2,6 +2,7 @@ from unittest.mock import Mock, patch from homeassistant.components.homeassistant_yellow.const import DOMAIN +from homeassistant.components.zha.core.const import DOMAIN as ZHA_DOMAIN from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType @@ -124,3 +125,88 @@ async def test_option_flow_install_multi_pan_addon( result = await hass.config_entries.options.async_configure(result["flow_id"]) assert result["type"] == FlowResultType.CREATE_ENTRY + + +async def test_option_flow_install_multi_pan_addon_zha( + hass: HomeAssistant, + addon_store_info, + addon_info, + install_addon, + set_addon_options, + start_addon, +) -> None: + """Test installing the multi pan addon when a zha config entry exists.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + + zha_config_entry = MockConfigEntry( + data={"device": {"path": "/dev/ttyAMA1"}, "radio_type": "ezsp"}, + domain=ZHA_DOMAIN, + options={}, + title="Yellow", + ) + zha_config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": True, + }, + ) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + assert result["progress_action"] == "install_addon" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "configure_addon" + install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + set_addon_options.assert_called_once_with( + hass, + "core_silabs_multiprotocol", + { + "options": { + "autoflash_firmware": True, + "device": "/dev/ttyAMA1", + "baudrate": "115200", + "flow_control": True, + } + }, + ) + # Check the ZHA config entry data is updated + assert zha_config_entry.data == { + "device": { + "path": "socket://core-silabs-multiprotocol:9999", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "ezsp", + } + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "finish_addon_setup" + start_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.CREATE_ENTRY diff --git a/tests/components/homeassistant_yellow/test_init.py b/tests/components/homeassistant_yellow/test_init.py index 06fe9465d2d..6df8977b35d 100644 --- a/tests/components/homeassistant_yellow/test_init.py +++ b/tests/components/homeassistant_yellow/test_init.py @@ -149,7 +149,7 @@ async def test_setup_zha_multipan( "radio_type": "ezsp", } assert config_entry.options == {} - assert config_entry.title == "Yellow" + assert config_entry.title == "Yellow Multi-PAN" async def test_setup_entry_wrong_board(hass: HomeAssistant) -> None: diff --git a/tests/components/zha/test_config_flow.py b/tests/components/zha/test_config_flow.py index d2001dfda1b..d457e0b6b8c 100644 --- a/tests/components/zha/test_config_flow.py +++ b/tests/components/zha/test_config_flow.py @@ -16,7 +16,7 @@ import zigpy.types from homeassistant import config_entries from homeassistant.components import ssdp, usb, zeroconf from homeassistant.components.ssdp import ATTR_UPNP_MANUFACTURER_URL, ATTR_UPNP_SERIAL -from homeassistant.components.zha import config_flow +from homeassistant.components.zha import config_flow, radio_manager from homeassistant.components.zha.core.const import ( CONF_BAUDRATE, CONF_FLOWCONTROL, @@ -1024,7 +1024,7 @@ async def test_hardware_invalid_data(hass, data): def test_allow_overwrite_ezsp_ieee(): """Test modifying the backup to allow bellows to override the IEEE address.""" backup = zigpy.backups.NetworkBackup() - new_backup = config_flow._allow_overwrite_ezsp_ieee(backup) + new_backup = radio_manager._allow_overwrite_ezsp_ieee(backup) assert backup != new_backup assert new_backup.network_info.stack_specific["ezsp"][EZSP_OVERWRITE_EUI64] is True @@ -1034,7 +1034,7 @@ def test_prevent_overwrite_ezsp_ieee(): """Test modifying the backup to prevent bellows from overriding the IEEE address.""" backup = zigpy.backups.NetworkBackup() backup.network_info.stack_specific["ezsp"] = {EZSP_OVERWRITE_EUI64: True} - new_backup = config_flow._prevent_overwrite_ezsp_ieee(backup) + new_backup = radio_manager._prevent_overwrite_ezsp_ieee(backup) assert backup != new_backup assert not new_backup.network_info.stack_specific.get("ezsp", {}).get( @@ -1131,7 +1131,7 @@ def test_parse_uploaded_backup(process_mock): assert backup == parsed_backup -@patch("homeassistant.components.zha.config_flow._allow_overwrite_ezsp_ieee") +@patch("homeassistant.components.zha.radio_manager._allow_overwrite_ezsp_ieee") async def test_formation_strategy_restore_manual_backup_non_ezsp( allow_overwrite_ieee_mock, pick_radio, mock_app, hass ): @@ -1163,7 +1163,7 @@ async def test_formation_strategy_restore_manual_backup_non_ezsp( assert result3["data"][CONF_RADIO_TYPE] == "znp" -@patch("homeassistant.components.zha.config_flow._allow_overwrite_ezsp_ieee") +@patch("homeassistant.components.zha.radio_manager._allow_overwrite_ezsp_ieee") async def test_formation_strategy_restore_manual_backup_overwrite_ieee_ezsp( allow_overwrite_ieee_mock, pick_radio, mock_app, backup, hass ): @@ -1203,7 +1203,7 @@ async def test_formation_strategy_restore_manual_backup_overwrite_ieee_ezsp( assert result4["data"][CONF_RADIO_TYPE] == "ezsp" -@patch("homeassistant.components.zha.config_flow._allow_overwrite_ezsp_ieee") +@patch("homeassistant.components.zha.radio_manager._allow_overwrite_ezsp_ieee") async def test_formation_strategy_restore_manual_backup_ezsp( allow_overwrite_ieee_mock, pick_radio, mock_app, hass ): @@ -1391,7 +1391,7 @@ async def test_formation_strategy_restore_automatic_backup_non_ezsp( assert result3["data"][CONF_RADIO_TYPE] == "znp" -@patch("homeassistant.components.zha.config_flow._allow_overwrite_ezsp_ieee") +@patch("homeassistant.components.zha.radio_manager._allow_overwrite_ezsp_ieee") async def test_ezsp_restore_without_settings_change_ieee( allow_overwrite_ieee_mock, pick_radio, mock_app, backup, hass ): diff --git a/tests/components/zha/test_radio_manager.py b/tests/components/zha/test_radio_manager.py new file mode 100644 index 00000000000..505118524b6 --- /dev/null +++ b/tests/components/zha/test_radio_manager.py @@ -0,0 +1,311 @@ +"""Tests for ZHA config flow.""" + +from collections.abc import Generator +from unittest.mock import AsyncMock, MagicMock, create_autospec, patch + +import pytest +import serial.tools.list_ports +from zigpy.backups import BackupManager +import zigpy.config +from zigpy.config import CONF_DEVICE_PATH +import zigpy.types + +from homeassistant import config_entries +from homeassistant.components.zha import radio_manager +from homeassistant.components.zha.core.const import DOMAIN, RadioType +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry + +PROBE_FUNCTION_PATH = "zigbee.application.ControllerApplication.probe" + + +@pytest.fixture(autouse=True) +def disable_platform_only(): + """Disable platforms to speed up tests.""" + with patch("homeassistant.components.zha.PLATFORMS", []): + yield + + +@pytest.fixture(autouse=True) +def reduce_reconnect_timeout(): + """Reduces reconnect timeout to speed up tests.""" + with patch("homeassistant.components.zha.radio_manager.CONNECT_DELAY_S", 0.0001): + yield + + +@pytest.fixture(autouse=True) +def mock_app(): + """Mock zigpy app interface.""" + mock_app = AsyncMock() + mock_app.backups = create_autospec(BackupManager, instance=True) + mock_app.backups.backups = [] + + with patch( + "zigpy.application.ControllerApplication.new", AsyncMock(return_value=mock_app) + ): + yield mock_app + + +@pytest.fixture +def backup(): + """Zigpy network backup with non-default settings.""" + backup = zigpy.backups.NetworkBackup() + backup.node_info.ieee = zigpy.types.EUI64.convert("AA:BB:CC:DD:11:22:33:44") + + return backup + + +def mock_detect_radio_type(radio_type=RadioType.ezsp, ret=True): + """Mock `detect_radio_type` that just sets the appropriate attributes.""" + + async def detect(self): + self.radio_type = radio_type + self.device_settings = radio_type.controller.SCHEMA_DEVICE( + {CONF_DEVICE_PATH: self.device_path} + ) + + return ret + + return detect + + +def com_port(device="/dev/ttyUSB1234"): + """Mock of a serial port.""" + port = serial.tools.list_ports_common.ListPortInfo("/dev/ttyUSB1234") + port.serial_number = "1234" + port.manufacturer = "Virtual serial port" + port.device = device + port.description = "Some serial port" + + return port + + +@pytest.fixture() +def mock_connect_zigpy_app() -> Generator[None, None, None]: + """Mock the radio connection.""" + + mock_connect_app = MagicMock() + mock_connect_app.__aenter__.return_value.backups.backups = [MagicMock()] + mock_connect_app.__aenter__.return_value.backups.create_backup.return_value = ( + MagicMock() + ) + + with patch( + "homeassistant.components.zha.radio_manager.ZhaRadioManager._connect_zigpy_app", + return_value=mock_connect_app, + ): + yield + + +@patch("homeassistant.components.zha.async_setup_entry", AsyncMock(return_value=True)) +async def test_migrate_matching_port( + hass: HomeAssistant, + mock_connect_zigpy_app, +) -> None: + """Test automatic migration.""" + # Setup the config entry + config_entry = MockConfigEntry( + data={"device": {"path": "/dev/ttyTEST123"}, "radio_type": "ezsp"}, + domain=DOMAIN, + options={}, + title="Test", + version=3, + ) + config_entry.add_to_hass(hass) + + migration_data = { + "new_discovery_info": { + "name": "Test Updated", + "port": { + "path": "socket://some/virtual_port", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + }, + "old_discovery_info": { + "name": "Test", + "port": { + "path": "/dev/ttyTEST123", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + }, + } + + migration_helper = radio_manager.ZhaMultiPANMigrationHelper(hass, config_entry) + assert await migration_helper.async_initiate_migration(migration_data) + + # Check the ZHA config entry data is updated + assert config_entry.data == { + "device": { + "path": "socket://some/virtual_port", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "ezsp", + } + assert config_entry.title == "Test Updated" + + await migration_helper.async_finish_migration() + + +async def test_migrate_matching_port_config_entry_not_loaded( + hass: HomeAssistant, + mock_connect_zigpy_app, +) -> None: + """Test automatic migration.""" + # Setup the config entry + config_entry = MockConfigEntry( + data={"device": {"path": "/dev/ttyTEST123"}, "radio_type": "ezsp"}, + domain=DOMAIN, + options={}, + title="Test", + ) + config_entry.add_to_hass(hass) + config_entry.state = config_entries.ConfigEntryState.SETUP_IN_PROGRESS + + migration_data = { + "new_discovery_info": { + "name": "Test Updated", + "port": { + "path": "socket://some/virtual_port", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + }, + "old_discovery_info": { + "name": "Test", + "port": { + "path": "/dev/ttyTEST123", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + }, + } + + migration_helper = radio_manager.ZhaMultiPANMigrationHelper(hass, config_entry) + assert await migration_helper.async_initiate_migration(migration_data) + + # Check the ZHA config entry data is updated + assert config_entry.data == { + "device": { + "path": "socket://some/virtual_port", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "ezsp", + } + assert config_entry.title == "Test Updated" + + await migration_helper.async_finish_migration() + + +@patch( + "homeassistant.components.zha.radio_manager.ZhaRadioManager.async_restore_backup_step_1", + side_effect=OSError, +) +async def test_migrate_matching_port_retry( + mock_restore_backup_step_1, + hass: HomeAssistant, + mock_connect_zigpy_app, +) -> None: + """Test automatic migration.""" + # Setup the config entry + config_entry = MockConfigEntry( + data={"device": {"path": "/dev/ttyTEST123"}, "radio_type": "ezsp"}, + domain=DOMAIN, + options={}, + title="Test", + ) + config_entry.add_to_hass(hass) + config_entry.state = config_entries.ConfigEntryState.SETUP_IN_PROGRESS + + migration_data = { + "new_discovery_info": { + "name": "Test Updated", + "port": { + "path": "socket://some/virtual_port", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + }, + "old_discovery_info": { + "name": "Test", + "port": { + "path": "/dev/ttyTEST123", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + }, + } + + migration_helper = radio_manager.ZhaMultiPANMigrationHelper(hass, config_entry) + assert await migration_helper.async_initiate_migration(migration_data) + + # Check the ZHA config entry data is updated + assert config_entry.data == { + "device": { + "path": "socket://some/virtual_port", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "ezsp", + } + assert config_entry.title == "Test Updated" + + with pytest.raises(OSError): + await migration_helper.async_finish_migration() + assert mock_restore_backup_step_1.call_count == 100 + + +async def test_migrate_non_matching_port( + hass: HomeAssistant, + mock_connect_zigpy_app, +) -> None: + """Test automatic migration.""" + # Setup the config entry + config_entry = MockConfigEntry( + data={"device": {"path": "/dev/ttyTEST123"}, "radio_type": "ezsp"}, + domain=DOMAIN, + options={}, + title="Test", + ) + config_entry.add_to_hass(hass) + + migration_data = { + "new_discovery_info": { + "name": "Test Updated", + "port": { + "path": "socket://some/virtual_port", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + }, + "old_discovery_info": { + "name": "Test", + "port": { + "path": "/dev/ttyTEST456", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + }, + } + + migration_helper = radio_manager.ZhaMultiPANMigrationHelper(hass, config_entry) + assert not await migration_helper.async_initiate_migration(migration_data) + + # Check the ZHA config entry data is not updated + assert config_entry.data == { + "device": {"path": "/dev/ttyTEST123"}, + "radio_type": "ezsp", + } + assert config_entry.title == "Test" From b566d55998112f8062e0fb58ae2e47fc844619fe Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 22 Nov 2022 11:26:53 +0100 Subject: [PATCH 0629/1033] Sort ignore-words-list (#82520) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 583776421e7..823c13a4d55 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: codespell args: - - --ignore-words-list=hass,alot,bre,datas,dof,dur,ether,farenheit,hist,iff,iif,ines,ist,lightsensor,mut,nd,pres,referer,rime,ser,serie,sur,te,technik,ue,uint,visability,wan,wanna,withing,iam,incomfort,ba,haa,pullrequests + - --ignore-words-list=alot,ba,bre,datas,dof,dur,ether,farenheit,haa,hass,hist,iam,iff,iif,incomfort,ines,ist,lightsensor,mut,nd,pres,pullrequests,referer,rime,ser,serie,sur,te,technik,ue,uint,visability,wan,wanna,withing - --skip="./.*,*.csv,*.json" - --quiet-level=2 exclude_types: [csv, json] From 9d192643eef3af69c6d8dd6c615d83196898bb58 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 11:35:18 +0100 Subject: [PATCH 0630/1033] Move PluggableAction to trigger helpers (#81900) Co-authored-by: Martin Hjelmare Co-authored-by: Joakim Plate Co-authored-by: Shay Levy --- .coveragerc | 1 + .../components/philips_js/__init__.py | 44 +------ homeassistant/components/philips_js/const.py | 2 + .../components/philips_js/device_trigger.py | 40 +++---- .../components/philips_js/helpers.py | 16 +++ .../components/philips_js/media_player.py | 20 +++- homeassistant/components/philips_js/remote.py | 14 ++- homeassistant/components/webostv/__init__.py | 50 +------- .../components/webostv/device_trigger.py | 17 +-- .../components/webostv/media_player.py | 15 ++- .../components/webostv/triggers/turn_on.py | 33 +++++- homeassistant/helpers/trigger.py | 109 ++++++++++++++++++ tests/components/philips_js/conftest.py | 6 +- .../philips_js/test_device_trigger.py | 1 + tests/helpers/test_trigger.py | 83 ++++++++++++- 15 files changed, 304 insertions(+), 147 deletions(-) create mode 100644 homeassistant/components/philips_js/helpers.py diff --git a/.coveragerc b/.coveragerc index 413294573a0..98a9ee9c6ff 100644 --- a/.coveragerc +++ b/.coveragerc @@ -961,6 +961,7 @@ omit = homeassistant/components/pencom/switch.py homeassistant/components/philips_js/__init__.py homeassistant/components/philips_js/diagnostics.py + homeassistant/components/philips_js/helpers.py homeassistant/components/philips_js/light.py homeassistant/components/philips_js/media_player.py homeassistant/components/philips_js/remote.py diff --git a/homeassistant/components/philips_js/__init__.py b/homeassistant/components/philips_js/__init__.py index a31212be3f7..3287d907578 100644 --- a/homeassistant/components/philips_js/__init__.py +++ b/homeassistant/components/philips_js/__init__.py @@ -2,10 +2,9 @@ from __future__ import annotations import asyncio -from collections.abc import Callable, Coroutine, Mapping +from collections.abc import Mapping from datetime import timedelta import logging -from typing import Any from haphilipsjs import AutenticationFailure, ConnectionFailure, PhilipsTV from haphilipsjs.typing import SystemType @@ -18,9 +17,8 @@ from homeassistant.const import ( CONF_USERNAME, Platform, ) -from homeassistant.core import Context, HassJob, HomeAssistant, callback +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.debounce import Debouncer -from homeassistant.helpers.trigger import TriggerActionType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import CONF_ALLOW_NOTIFY, CONF_SYSTEM, DOMAIN @@ -78,42 +76,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class PluggableAction: - """A pluggable action handler.""" - - def __init__(self, update: Callable[[], None]) -> None: - """Initialize.""" - self._update = update - self._actions: dict[ - Any, tuple[HassJob[..., Coroutine[Any, Any, None]], dict[str, Any]] - ] = {} - - def __bool__(self): - """Return if we have something attached.""" - return bool(self._actions) - - @callback - def async_attach(self, action: TriggerActionType, variables: dict[str, Any]): - """Attach a device trigger for turn on.""" - - @callback - def _remove(): - del self._actions[_remove] - self._update() - - job = HassJob(action) - - self._actions[_remove] = (job, variables) - self._update() - - return _remove - - async def async_run(self, hass: HomeAssistant, context: Context | None = None): - """Run all turn on triggers.""" - for job, variables in self._actions.values(): - hass.async_run_hass_job(job, variables, context) - - class PhilipsTVDataUpdateCoordinator(DataUpdateCoordinator[None]): """Coordinator to update data.""" @@ -125,8 +87,6 @@ class PhilipsTVDataUpdateCoordinator(DataUpdateCoordinator[None]): self.options = options self._notify_future: asyncio.Task | None = None - self.turn_on = PluggableAction(self.async_update_listeners) - super().__init__( hass, LOGGER, diff --git a/homeassistant/components/philips_js/const.py b/homeassistant/components/philips_js/const.py index 5d1141a8fb9..7788634ebc0 100644 --- a/homeassistant/components/philips_js/const.py +++ b/homeassistant/components/philips_js/const.py @@ -6,3 +6,5 @@ CONF_ALLOW_NOTIFY = "allow_notify" CONST_APP_ID = "homeassistant.io" CONST_APP_NAME = "Home Assistant" + +TRIGGER_TYPE_TURN_ON = "turn_on" diff --git a/homeassistant/components/philips_js/device_trigger.py b/homeassistant/components/philips_js/device_trigger.py index d7ce9807d64..bdf47674bc8 100644 --- a/homeassistant/components/philips_js/device_trigger.py +++ b/homeassistant/components/philips_js/device_trigger.py @@ -4,17 +4,18 @@ from __future__ import annotations import voluptuous as vol from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA -from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE +from homeassistant.const import CONF_DEVICE_ID, CONF_TYPE from homeassistant.core import CALLBACK_TYPE, HomeAssistant from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import device_registry as dr -from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo +from homeassistant.helpers.trigger import ( + PluggableAction, + TriggerActionType, + TriggerInfo, +) from homeassistant.helpers.typing import ConfigType -from . import PhilipsTVDataUpdateCoordinator -from .const import DOMAIN - -TRIGGER_TYPE_TURN_ON = "turn_on" +from .const import DOMAIN, TRIGGER_TYPE_TURN_ON +from .helpers import async_get_turn_on_trigger TRIGGER_TYPES = {TRIGGER_TYPE_TURN_ON} TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( @@ -29,14 +30,7 @@ async def async_get_triggers( ) -> list[dict[str, str]]: """List device triggers for device.""" triggers = [] - triggers.append( - { - CONF_PLATFORM: "device", - CONF_DEVICE_ID: device_id, - CONF_DOMAIN: DOMAIN, - CONF_TYPE: TRIGGER_TYPE_TURN_ON, - } - ) + triggers.append(async_get_turn_on_trigger(device_id)) return triggers @@ -49,7 +43,6 @@ async def async_attach_trigger( ) -> CALLBACK_TYPE: """Attach a trigger.""" trigger_data = trigger_info["trigger_data"] - registry: dr.DeviceRegistry = dr.async_get(hass) if (trigger_type := config[CONF_TYPE]) == TRIGGER_TYPE_TURN_ON: variables = { "trigger": { @@ -61,16 +54,9 @@ async def async_attach_trigger( } } - device = registry.async_get(config[CONF_DEVICE_ID]) - if device is None: - raise HomeAssistantError( - f"Device id {config[CONF_DEVICE_ID]} not found in registry" - ) - for config_entry_id in device.config_entries: - coordinator: PhilipsTVDataUpdateCoordinator = hass.data[DOMAIN].get( - config_entry_id - ) - if coordinator: - return coordinator.turn_on.async_attach(action, variables) + turn_on_trigger = async_get_turn_on_trigger(config[CONF_DEVICE_ID]) + return PluggableAction.async_attach_trigger( + hass, turn_on_trigger, action, variables + ) raise HomeAssistantError(f"Unhandled trigger type {trigger_type}") diff --git a/homeassistant/components/philips_js/helpers.py b/homeassistant/components/philips_js/helpers.py new file mode 100644 index 00000000000..010ca7b9a19 --- /dev/null +++ b/homeassistant/components/philips_js/helpers.py @@ -0,0 +1,16 @@ +"""Helpers for philips_js.""" + +from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE + +from .const import DOMAIN, TRIGGER_TYPE_TURN_ON + + +def async_get_turn_on_trigger(device_id: str) -> dict[str, str]: + """Return trigger description for a turn on trigger.""" + + return { + CONF_PLATFORM: "device", + CONF_DEVICE_ID: device_id, + CONF_DOMAIN: DOMAIN, + CONF_TYPE: TRIGGER_TYPE_TURN_ON, + } diff --git a/homeassistant/components/philips_js/media_player.py b/homeassistant/components/philips_js/media_player.py index e1ceddd4bda..04e63008e7b 100644 --- a/homeassistant/components/philips_js/media_player.py +++ b/homeassistant/components/philips_js/media_player.py @@ -19,10 +19,12 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.trigger import PluggableAction from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import LOGGER as _LOGGER, PhilipsTVDataUpdateCoordinator from .const import DOMAIN +from .helpers import async_get_turn_on_trigger SUPPORT_PHILIPS_JS = ( MediaPlayerEntityFeature.TURN_OFF @@ -39,8 +41,6 @@ SUPPORT_PHILIPS_JS = ( | MediaPlayerEntityFeature.STOP ) -CONF_ON_ACTION = "turn_on_action" - def _inverted(data): return {v: k for k, v in data.items()} @@ -95,9 +95,19 @@ class PhilipsTVMediaPlayer( self._media_title: str | None = None self._media_channel: str | None = None + self._turn_on = PluggableAction(self.async_write_ha_state) super().__init__(coordinator) self._update_from_coordinator() + async def async_added_to_hass(self) -> None: + """Handle being added to hass.""" + if (entry := self.registry_entry) and entry.device_id: + self.async_on_remove( + self._turn_on.async_register( + self.hass, async_get_turn_on_trigger(entry.device_id) + ) + ) + async def _async_update_soon(self): """Reschedule update task.""" self.async_write_ha_state() @@ -107,9 +117,7 @@ class PhilipsTVMediaPlayer( def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" supports = self._supports - if self.coordinator.turn_on or ( - self._tv.on and self._tv.powerstate is not None - ): + if self._turn_on or (self._tv.on and self._tv.powerstate is not None): supports |= MediaPlayerEntityFeature.TURN_ON return supports @@ -152,7 +160,7 @@ class PhilipsTVMediaPlayer( await self._tv.setPowerState("On") self._state = MediaPlayerState.ON else: - await self.coordinator.turn_on.async_run(self.hass, self._context) + await self._turn_on.async_run(self.hass, self._context) await self._async_update_soon() async def async_turn_off(self) -> None: diff --git a/homeassistant/components/philips_js/remote.py b/homeassistant/components/philips_js/remote.py index 02d5e512a33..3496ec5f576 100644 --- a/homeassistant/components/philips_js/remote.py +++ b/homeassistant/components/philips_js/remote.py @@ -13,10 +13,12 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.trigger import PluggableAction from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import LOGGER, PhilipsTVDataUpdateCoordinator from .const import DOMAIN +from .helpers import async_get_turn_on_trigger async def async_setup_entry( @@ -52,6 +54,16 @@ class PhilipsTVRemote(CoordinatorEntity[PhilipsTVDataUpdateCoordinator], RemoteE name=coordinator.system["name"], sw_version=coordinator.system.get("softwareversion"), ) + self._turn_on = PluggableAction(self.async_write_ha_state) + + async def async_added_to_hass(self) -> None: + """Handle being added to hass.""" + if (entry := self.registry_entry) and entry.device_id: + self.async_on_remove( + self._turn_on.async_register( + self.hass, async_get_turn_on_trigger(entry.device_id) + ) + ) @property def is_on(self): @@ -65,7 +77,7 @@ class PhilipsTVRemote(CoordinatorEntity[PhilipsTVDataUpdateCoordinator], RemoteE if self._tv.on and self._tv.powerstate: await self._tv.setPowerState("On") else: - await self.coordinator.turn_on.async_run(self.hass, self._context) + await self._turn_on.async_run(self.hass, self._context) self.async_write_ha_state() async def async_turn_off(self, **kwargs: Any) -> None: diff --git a/homeassistant/components/webostv/__init__.py b/homeassistant/components/webostv/__init__.py index 8b023990590..cd5485d4fd2 100644 --- a/homeassistant/components/webostv/__init__.py +++ b/homeassistant/components/webostv/__init__.py @@ -1,10 +1,8 @@ """Support for LG webOS Smart TV.""" from __future__ import annotations -from collections.abc import Callable, Coroutine from contextlib import suppress import logging -from typing import Any from aiowebostv import WebOsClient, WebOsTvPairError import voluptuous as vol @@ -19,17 +17,9 @@ from homeassistant.const import ( CONF_NAME, EVENT_HOMEASSISTANT_STOP, ) -from homeassistant.core import ( - Context, - Event, - HassJob, - HomeAssistant, - ServiceCall, - callback, -) +from homeassistant.core import Event, HomeAssistant, ServiceCall from homeassistant.helpers import config_validation as cv, discovery from homeassistant.helpers.dispatcher import async_dispatcher_send -from homeassistant.helpers.trigger import TriggerActionType from homeassistant.helpers.typing import ConfigType from .const import ( @@ -165,43 +155,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class PluggableAction: - """A pluggable action handler.""" - - def __init__(self) -> None: - """Initialize.""" - self._actions: dict[ - Callable[[], None], - tuple[HassJob[..., Coroutine[Any, Any, None]], dict[str, Any]], - ] = {} - - def __bool__(self) -> bool: - """Return if we have something attached.""" - return bool(self._actions) - - @callback - def async_attach( - self, action: TriggerActionType, variables: dict[str, Any] - ) -> Callable[[], None]: - """Attach a device trigger for turn on.""" - - @callback - def _remove() -> None: - del self._actions[_remove] - - job = HassJob(action) - - self._actions[_remove] = (job, variables) - - return _remove - - @callback - def async_run(self, hass: HomeAssistant, context: Context | None = None) -> None: - """Run all turn on triggers.""" - for job, variables in self._actions.values(): - hass.async_run_hass_job(job, variables, context) - - class WebOsClientWrapper: """Wrapper for a WebOS TV client with Home Assistant specific functions.""" @@ -209,7 +162,6 @@ class WebOsClientWrapper: """Set up the client.""" self.host = host self.client_key = client_key - self.turn_on = PluggableAction() self.client: WebOsClient | None = None async def connect(self) -> None: diff --git a/homeassistant/components/webostv/device_trigger.py b/homeassistant/components/webostv/device_trigger.py index 590cbc19de8..14854383ec8 100644 --- a/homeassistant/components/webostv/device_trigger.py +++ b/homeassistant/components/webostv/device_trigger.py @@ -7,7 +7,7 @@ from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEM from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) -from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE +from homeassistant.const import CONF_DEVICE_ID, CONF_PLATFORM, CONF_TYPE from homeassistant.core import CALLBACK_TYPE, HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo @@ -19,7 +19,10 @@ from .helpers import ( async_get_client_wrapper_by_device_entry, async_get_device_entry_by_device_id, ) -from .triggers.turn_on import PLATFORM_TYPE as TURN_ON_PLATFORM_TYPE +from .triggers.turn_on import ( + PLATFORM_TYPE as TURN_ON_PLATFORM_TYPE, + async_get_turn_on_trigger, +) TRIGGER_TYPES = {TURN_ON_PLATFORM_TYPE} TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( @@ -51,15 +54,7 @@ async def async_get_triggers( _hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device triggers for device.""" - triggers = [] - base_trigger = { - CONF_PLATFORM: "device", - CONF_DEVICE_ID: device_id, - CONF_DOMAIN: DOMAIN, - } - - triggers.append({**base_trigger, CONF_TYPE: TURN_ON_PLATFORM_TYPE}) - + triggers = [async_get_turn_on_trigger(device_id)] return triggers diff --git a/homeassistant/components/webostv/media_player.py b/homeassistant/components/webostv/media_player.py index 07d49f703f7..dcbec24c665 100644 --- a/homeassistant/components/webostv/media_player.py +++ b/homeassistant/components/webostv/media_player.py @@ -32,6 +32,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity +from homeassistant.helpers.trigger import PluggableAction from . import WebOsClientWrapper from .const import ( @@ -43,6 +44,7 @@ from .const import ( LIVE_TV_APP_ID, WEBOSTV_EXCEPTIONS, ) +from .triggers.turn_on import async_get_turn_on_trigger _LOGGER = logging.getLogger(__name__) @@ -133,7 +135,7 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity): # Assume that the TV is not paused self._paused = False - + self._turn_on = PluggableAction(self.async_write_ha_state) self._current_source = None self._source_list: dict = {} @@ -144,6 +146,13 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity): """Connect and subscribe to dispatcher signals and state updates.""" await super().async_added_to_hass() + if (entry := self.registry_entry) and entry.device_id: + self.async_on_remove( + self._turn_on.async_register( + self.hass, async_get_turn_on_trigger(entry.device_id) + ) + ) + self.async_on_remove( async_dispatcher_connect(self.hass, DOMAIN, self.async_signal_handler) ) @@ -318,7 +327,7 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity): @property def supported_features(self) -> MediaPlayerEntityFeature: """Flag media player features that are supported.""" - if self._wrapper.turn_on: + if self._turn_on: return self._supported_features | MediaPlayerEntityFeature.TURN_ON return self._supported_features @@ -330,7 +339,7 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity): async def async_turn_on(self) -> None: """Turn on media player.""" - self._wrapper.turn_on.async_run(self.hass, self._context) + await self._turn_on.async_run(self.hass, self._context) @cmd async def async_volume_up(self) -> None: diff --git a/homeassistant/components/webostv/triggers/turn_on.py b/homeassistant/components/webostv/triggers/turn_on.py index 806b0b4b964..403219f1372 100644 --- a/homeassistant/components/webostv/triggers/turn_on.py +++ b/homeassistant/components/webostv/triggers/turn_on.py @@ -3,15 +3,25 @@ from __future__ import annotations import voluptuous as vol -from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID, CONF_PLATFORM +from homeassistant.const import ( + ATTR_DEVICE_ID, + ATTR_ENTITY_ID, + CONF_DEVICE_ID, + CONF_DOMAIN, + CONF_PLATFORM, + CONF_TYPE, +) from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo +from homeassistant.helpers.trigger import ( + PluggableAction, + TriggerActionType, + TriggerInfo, +) from homeassistant.helpers.typing import ConfigType from ..const import DOMAIN from ..helpers import ( - async_get_client_wrapper_by_device_entry, async_get_device_entry_by_device_id, async_get_device_id_from_entity_id, ) @@ -33,6 +43,17 @@ TRIGGER_SCHEMA = vol.All( ) +def async_get_turn_on_trigger(device_id: str) -> dict[str, str]: + """Return data for a turn on trigger.""" + + return { + CONF_PLATFORM: "device", + CONF_DEVICE_ID: device_id, + CONF_DOMAIN: DOMAIN, + CONF_TYPE: PLATFORM_TYPE, + } + + async def async_attach_trigger( hass: HomeAssistant, config: ConfigType, @@ -69,10 +90,12 @@ async def async_attach_trigger( "description": f"webostv turn on trigger for {device_name}", } - client_wrapper = async_get_client_wrapper_by_device_entry(hass, device) + turn_on_trigger = async_get_turn_on_trigger(device_id) unsubs.append( - client_wrapper.turn_on.async_attach(action, {"trigger": variables}) + PluggableAction.async_attach_trigger( + hass, turn_on_trigger, action, {"trigger": variables} + ) ) @callback diff --git a/homeassistant/helpers/trigger.py b/homeassistant/helpers/trigger.py index 4cb724a6435..1054521ee51 100644 --- a/homeassistant/helpers/trigger.py +++ b/homeassistant/helpers/trigger.py @@ -2,7 +2,9 @@ from __future__ import annotations import asyncio +from collections import defaultdict from collections.abc import Callable, Coroutine +from dataclasses import dataclass, field import functools import logging from typing import TYPE_CHECKING, Any, Protocol, TypedDict, cast @@ -19,6 +21,7 @@ from homeassistant.const import ( from homeassistant.core import ( CALLBACK_TYPE, Context, + HassJob, HomeAssistant, callback, is_callback, @@ -38,6 +41,8 @@ _PLATFORM_ALIASES = { "homeassistant": ("event", "numeric_state", "state", "time_pattern", "time"), } +DATA_PLUGGABLE_ACTIONS = "pluggable_actions" + class TriggerActionType(Protocol): """Protocol type for trigger action callback.""" @@ -68,6 +73,110 @@ class TriggerInfo(TypedDict): trigger_data: TriggerData +@dataclass +class PluggableActionsEntry: + """Holder to keep track of all plugs and actions for a given trigger.""" + + plugs: set[PluggableAction] = field(default_factory=set) + actions: dict[object, tuple[HassJob, dict[str, Any]]] = field(default_factory=dict) + + +class PluggableAction: + """A pluggable action handler.""" + + _entry: PluggableActionsEntry | None = None + + def __init__(self, update: CALLBACK_TYPE | None = None) -> None: + """Initialize a pluggable action. + + :param update: callback triggered whenever triggers are attached or removed. + """ + self._update = update + + def __bool__(self) -> bool: + """Return if we have something attached.""" + return bool(self._entry and self._entry.actions) + + @callback + def async_run_update(self) -> None: + """Run update function if one exists.""" + if self._update: + self._update() + + @staticmethod + @callback + def async_get_registry(hass: HomeAssistant) -> dict[tuple, PluggableActionsEntry]: + """Return the pluggable actions registry.""" + if data := hass.data.get(DATA_PLUGGABLE_ACTIONS): + return data # type: ignore[no-any-return] + data = defaultdict(PluggableActionsEntry) + hass.data[DATA_PLUGGABLE_ACTIONS] = data + return data + + @staticmethod + @callback + def async_attach_trigger( + hass: HomeAssistant, + trigger: dict[str, str], + action: TriggerActionType, + variables: dict[str, Any], + ) -> CALLBACK_TYPE: + """Attach an action to a trigger entry. Existing or future plugs registered will be attached.""" + reg = PluggableAction.async_get_registry(hass) + key = tuple(sorted(trigger.items())) + entry = reg[key] + + def _update() -> None: + for plug in entry.plugs: + plug.async_run_update() + + @callback + def _remove() -> None: + """Remove this action attachment, and disconnect all plugs.""" + del entry.actions[_remove] + _update() + if not entry.actions and not entry.plugs: + del reg[key] + + job = HassJob(action) + entry.actions[_remove] = (job, variables) + _update() + + return _remove + + @callback + def async_register( + self, hass: HomeAssistant, trigger: dict[str, str] + ) -> CALLBACK_TYPE: + """Register plug in the global plugs dictionary.""" + + reg = PluggableAction.async_get_registry(hass) + key = tuple(sorted(trigger.items())) + self._entry = reg[key] + self._entry.plugs.add(self) + + @callback + def _remove() -> None: + """Remove plug from registration, and clean up entry if there are no actions or plugs registered.""" + assert self._entry + self._entry.plugs.remove(self) + if not self._entry.actions and not self._entry.plugs: + del reg[key] + self._entry = None + + return _remove + + async def async_run( + self, hass: HomeAssistant, context: Context | None = None + ) -> None: + """Run all actions.""" + assert self._entry + for job, variables in self._entry.actions.values(): + task = hass.async_run_hass_job(job, variables, context) + if task: + await task + + async def _async_get_trigger_platform( hass: HomeAssistant, config: ConfigType ) -> DeviceAutomationTriggerProtocol: diff --git a/tests/components/philips_js/conftest.py b/tests/components/philips_js/conftest.py index e0069cf9b75..dfda844c7a2 100644 --- a/tests/components/philips_js/conftest.py +++ b/tests/components/philips_js/conftest.py @@ -31,6 +31,10 @@ def mock_tv(): tv.notify_change_supported = False tv.pairing_type = None tv.powerstate = None + tv.source_id = None + tv.ambilight_current_configuration = None + tv.ambilight_styles = {} + tv.ambilight_cached = {} with patch( "homeassistant.components.philips_js.config_flow.PhilipsTV", return_value=tv @@ -42,7 +46,7 @@ def mock_tv(): async def mock_config_entry(hass): """Get standard player.""" config_entry = MockConfigEntry( - domain=DOMAIN, data=MOCK_CONFIG, title=MOCK_NAME, unique_id="ABCDEFGHIJKLF" + domain=DOMAIN, data=MOCK_CONFIG, title=MOCK_NAME, unique_id=MOCK_SERIAL_NO ) config_entry.add_to_hass(hass) return config_entry diff --git a/tests/components/philips_js/test_device_trigger.py b/tests/components/philips_js/test_device_trigger.py index dd06ee25d49..2c5b21b1e34 100644 --- a/tests/components/philips_js/test_device_trigger.py +++ b/tests/components/philips_js/test_device_trigger.py @@ -34,6 +34,7 @@ async def test_get_triggers(hass, mock_device): triggers = await async_get_device_automations( hass, DeviceAutomationType.TRIGGER, mock_device.id ) + triggers = [trigger for trigger in triggers if trigger["domain"] == DOMAIN] assert_lists_same(triggers, expected_triggers) diff --git a/tests/helpers/test_trigger.py b/tests/helpers/test_trigger.py index 9cd3b0956ce..4718e3130d5 100644 --- a/tests/helpers/test_trigger.py +++ b/tests/helpers/test_trigger.py @@ -1,11 +1,13 @@ """The tests for the trigger helper.""" -from unittest.mock import ANY, MagicMock, call, patch +from unittest.mock import ANY, AsyncMock, MagicMock, call, patch import pytest import voluptuous as vol -from homeassistant.core import HomeAssistant, ServiceCall, callback +from homeassistant.core import Context, HomeAssistant, ServiceCall, callback from homeassistant.helpers.trigger import ( + DATA_PLUGGABLE_ACTIONS, + PluggableAction, _async_get_trigger_platform, async_initialize_triggers, async_validate_trigger_config, @@ -197,3 +199,80 @@ async def test_async_initialize_triggers( log_cb.reset_mock() unsub() + + +async def test_pluggable_action(hass: HomeAssistant, calls: list[ServiceCall]): + """Test normal behavior of pluggable actions.""" + update_1 = MagicMock() + update_2 = MagicMock() + action_1 = AsyncMock() + action_2 = AsyncMock() + trigger_1 = {"domain": "test", "device": "1"} + trigger_2 = {"domain": "test", "device": "2"} + variables_1 = {"source": "test 1"} + variables_2 = {"source": "test 2"} + context_1 = Context() + context_2 = Context() + + plug_1 = PluggableAction(update_1) + plug_2 = PluggableAction(update_2) + + # Verify plug is inactive without triggers + remove_plug_1 = plug_1.async_register(hass, trigger_1) + assert not plug_1 + assert not plug_2 + + # Verify plug remain inactive with non matching trigger + remove_attach_2 = PluggableAction.async_attach_trigger( + hass, trigger_2, action_2, variables_2 + ) + assert not plug_1 + assert not plug_2 + update_1.assert_not_called() + update_2.assert_not_called() + + # Verify plug is active, and update when matching trigger attaches + remove_attach_1 = PluggableAction.async_attach_trigger( + hass, trigger_1, action_1, variables_1 + ) + assert plug_1 + assert not plug_2 + update_1.assert_called() + update_1.reset_mock() + update_2.assert_not_called() + + # Verify a non registered plug is inactive + remove_plug_1() + assert not plug_1 + assert not plug_2 + + # Verify a plug registered to existing trigger is true + remove_plug_1 = plug_1.async_register(hass, trigger_1) + assert plug_1 + assert not plug_2 + + remove_plug_2 = plug_2.async_register(hass, trigger_2) + assert plug_1 + assert plug_2 + + # Verify no actions should have been triggered so far + action_1.assert_not_called() + action_2.assert_not_called() + + # Verify action is triggered with correct data + await plug_1.async_run(hass, context_1) + await plug_2.async_run(hass, context_2) + action_1.assert_called_with(variables_1, context_1) + action_2.assert_called_with(variables_2, context_2) + + # Verify plug goes inactive if trigger is removed + remove_attach_1() + assert not plug_1 + + # Verify registry is cleaned when no plugs nor triggers are attached + assert hass.data[DATA_PLUGGABLE_ACTIONS] + remove_plug_1() + remove_plug_2() + remove_attach_2() + assert not hass.data[DATA_PLUGGABLE_ACTIONS] + assert not plug_2 From 687d162a941938bda9839d1a9bcacfbc02afbeae Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 12:45:35 +0100 Subject: [PATCH 0631/1033] Fix incorrect type hint in SchemaOptionsFlow (#82516) --- homeassistant/helpers/schema_config_entry_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 413161eb150..405d9333776 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -306,7 +306,7 @@ class SchemaOptionsFlowHandler(config_entries.OptionsFlow): def __init__( self, config_entry: config_entries.ConfigEntry, - options_flow: dict[str, vol.Schema], + options_flow: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep], async_options_flow_finished: Callable[[HomeAssistant, Mapping[str, Any]], None], ) -> None: """Initialize options flow.""" From 157222126ea25819fc34370c00e993ca77b18073 Mon Sep 17 00:00:00 2001 From: Jevgeni Kiski Date: Tue, 22 Nov 2022 13:46:57 +0200 Subject: [PATCH 0632/1033] Add Vallox temperature control entities (#75858) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastian Lövdahl Co-authored-by: Andre Richter Co-authored-by: Martin Hjelmare --- homeassistant/components/vallox/__init__.py | 1 + homeassistant/components/vallox/number.py | 127 ++++++++++++++++++++ tests/components/vallox/test_number.py | 80 ++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 homeassistant/components/vallox/number.py create mode 100644 tests/components/vallox/test_number.py diff --git a/homeassistant/components/vallox/__init__.py b/homeassistant/components/vallox/__init__.py index 1a1788aeeed..f393342dfd5 100644 --- a/homeassistant/components/vallox/__init__.py +++ b/homeassistant/components/vallox/__init__.py @@ -63,6 +63,7 @@ PLATFORMS: list[str] = [ Platform.SENSOR, Platform.FAN, Platform.BINARY_SENSOR, + Platform.NUMBER, Platform.SWITCH, ] diff --git a/homeassistant/components/vallox/number.py b/homeassistant/components/vallox/number.py new file mode 100644 index 00000000000..5be91fe66e6 --- /dev/null +++ b/homeassistant/components/vallox/number.py @@ -0,0 +1,127 @@ +"""Support for Vallox ventilation unit numbers.""" +from __future__ import annotations + +from dataclasses import dataclass + +from vallox_websocket_api import Vallox + +from homeassistant.components.number import ( + NumberDeviceClass, + NumberEntity, + NumberEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import TEMP_CELSIUS +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import ValloxDataUpdateCoordinator, ValloxEntity +from .const import DOMAIN + + +class ValloxNumberEntity(ValloxEntity, NumberEntity): + """Representation of a Vallox number entity.""" + + entity_description: ValloxNumberEntityDescription + _attr_has_entity_name = True + _attr_entity_category = EntityCategory.CONFIG + + def __init__( + self, + name: str, + coordinator: ValloxDataUpdateCoordinator, + description: ValloxNumberEntityDescription, + client: Vallox, + ) -> None: + """Initialize the Vallox number entity.""" + super().__init__(name, coordinator) + + self.entity_description = description + + self._attr_unique_id = f"{self._device_uuid}-{description.key}" + self._client = client + + @property + def native_value(self) -> float | None: + """Return the value reported by the sensor.""" + if ( + value := self.coordinator.data.get_metric( + self.entity_description.metric_key + ) + ) is None: + return None + + return float(value) + + async def async_set_native_value(self, value: float) -> None: + """Update the current value.""" + await self._client.set_values( + {self.entity_description.metric_key: float(value)} + ) + await self.coordinator.async_request_refresh() + + +@dataclass +class ValloxMetricMixin: + """Holds Vallox metric key.""" + + metric_key: str + + +@dataclass +class ValloxNumberEntityDescription(NumberEntityDescription, ValloxMetricMixin): + """Describes Vallox number entity.""" + + +NUMBER_ENTITIES: tuple[ValloxNumberEntityDescription, ...] = ( + ValloxNumberEntityDescription( + key="supply_air_target_home", + name="Supply air temperature (Home)", + metric_key="A_CYC_HOME_AIR_TEMP_TARGET", + device_class=NumberDeviceClass.TEMPERATURE, + native_unit_of_measurement=TEMP_CELSIUS, + icon="mdi:thermometer", + native_min_value=5.0, + native_max_value=25.0, + native_step=1.0, + ), + ValloxNumberEntityDescription( + key="supply_air_target_away", + name="Supply air temperature (Away)", + metric_key="A_CYC_AWAY_AIR_TEMP_TARGET", + device_class=NumberDeviceClass.TEMPERATURE, + native_unit_of_measurement=TEMP_CELSIUS, + icon="mdi:thermometer", + native_min_value=5.0, + native_max_value=25.0, + native_step=1.0, + ), + ValloxNumberEntityDescription( + key="supply_air_target_boost", + name="Supply air temperature (Boost)", + metric_key="A_CYC_BOOST_AIR_TEMP_TARGET", + device_class=NumberDeviceClass.TEMPERATURE, + native_unit_of_measurement=TEMP_CELSIUS, + icon="mdi:thermometer", + native_min_value=5.0, + native_max_value=25.0, + native_step=1.0, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up the sensors.""" + data = hass.data[DOMAIN][entry.entry_id] + + async_add_entities( + [ + ValloxNumberEntity( + data["name"], data["coordinator"], description, data["client"] + ) + for description in NUMBER_ENTITIES + ] + ) diff --git a/tests/components/vallox/test_number.py b/tests/components/vallox/test_number.py new file mode 100644 index 00000000000..3d05cafaef1 --- /dev/null +++ b/tests/components/vallox/test_number.py @@ -0,0 +1,80 @@ +"""Tests for Vallox number platform.""" +import pytest + +from homeassistant.components.number.const import ( + ATTR_VALUE, + DOMAIN as NUMBER_DOMAIN, + SERVICE_SET_VALUE, +) +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant + +from .conftest import patch_metrics, patch_metrics_set + +from tests.common import MockConfigEntry + +TEST_TEMPERATURE_ENTITIES_DATA = [ + ( + "number.vallox_supply_air_temperature_home", + "A_CYC_HOME_AIR_TEMP_TARGET", + 19.0, + ), + ( + "number.vallox_supply_air_temperature_away", + "A_CYC_AWAY_AIR_TEMP_TARGET", + 18.0, + ), + ( + "number.vallox_supply_air_temperature_boost", + "A_CYC_BOOST_AIR_TEMP_TARGET", + 17.0, + ), +] + + +@pytest.mark.parametrize("entity_id, metric_key, value", TEST_TEMPERATURE_ENTITIES_DATA) +async def test_temperature_number_entities( + entity_id: str, + metric_key: str, + value: float, + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test temperature entities.""" + # Arrange + metrics = {metric_key: value} + + # Act + with patch_metrics(metrics=metrics): + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + + # Assert + sensor = hass.states.get(entity_id) + assert sensor.state == str(value) + assert sensor.attributes["unit_of_measurement"] == "°C" + + +@pytest.mark.parametrize("entity_id, metric_key, value", TEST_TEMPERATURE_ENTITIES_DATA) +async def test_temperature_number_entity_set( + entity_id: str, + metric_key: str, + value: float, + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test temperature set.""" + # Act + with patch_metrics(metrics={}), patch_metrics_set() as metrics_set: + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + service_data={ + ATTR_ENTITY_ID: entity_id, + ATTR_VALUE: value, + }, + ) + await hass.async_block_till_done() + metrics_set.assert_called_once_with({metric_key: value}) From 9c1e8486c54508468963604b0d35dc9ef22e532b Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 22 Nov 2022 15:56:31 +0100 Subject: [PATCH 0633/1033] Add unique id option for `mqtt_room` sensors (#82521) Add unique_id setting for mqtt_room --- homeassistant/components/mqtt_room/sensor.py | 23 +++++++++--- tests/components/mqtt_room/test_sensor.py | 39 +++++++++++++++++++- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mqtt_room/sensor.py b/homeassistant/components/mqtt_room/sensor.py index a6927048051..b1b52e42fce 100644 --- a/homeassistant/components/mqtt_room/sensor.py +++ b/homeassistant/components/mqtt_room/sensor.py @@ -16,6 +16,7 @@ from homeassistant.const import ( CONF_DEVICE_ID, CONF_NAME, CONF_TIMEOUT, + CONF_UNIQUE_ID, STATE_NOT_HOME, ) from homeassistant.core import HomeAssistant, callback @@ -42,6 +43,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Required(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, vol.Optional(CONF_AWAY_TIMEOUT, default=DEFAULT_AWAY_TIMEOUT): cv.positive_int, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_UNIQUE_ID): cv.string, } ).extend(mqtt.config.MQTT_RO_SCHEMA.schema) @@ -70,10 +72,11 @@ async def async_setup_platform( [ MQTTRoomSensor( config.get(CONF_NAME), - config.get(CONF_STATE_TOPIC), - config.get(CONF_DEVICE_ID), - config.get(CONF_TIMEOUT), - config.get(CONF_AWAY_TIMEOUT), + config[CONF_STATE_TOPIC], + config[CONF_DEVICE_ID], + config[CONF_TIMEOUT], + config[CONF_AWAY_TIMEOUT], + config.get(CONF_UNIQUE_ID), ) ] ) @@ -82,8 +85,18 @@ async def async_setup_platform( class MQTTRoomSensor(SensorEntity): """Representation of a room sensor that is updated via MQTT.""" - def __init__(self, name, state_topic, device_id, timeout, consider_home): + def __init__( + self, + name: str | None, + state_topic: str, + device_id: str, + timeout: int, + consider_home: int, + unique_id: str | None, + ) -> None: """Initialize the sensor.""" + self._attr_unique_id = unique_id + self._state = STATE_NOT_HOME self._name = name self._state_topic = f"{state_topic}/+" diff --git a/tests/components/mqtt_room/test_sensor.py b/tests/components/mqtt_room/test_sensor.py index b17a2bed457..562b8c95fd0 100644 --- a/tests/components/mqtt_room/test_sensor.py +++ b/tests/components/mqtt_room/test_sensor.py @@ -5,7 +5,15 @@ from unittest.mock import patch from homeassistant.components.mqtt import CONF_QOS, CONF_STATE_TOPIC, DEFAULT_QOS import homeassistant.components.sensor as sensor -from homeassistant.const import CONF_DEVICE_ID, CONF_NAME, CONF_PLATFORM, CONF_TIMEOUT +from homeassistant.const import ( + CONF_DEVICE_ID, + CONF_NAME, + CONF_PLATFORM, + CONF_TIMEOUT, + CONF_UNIQUE_ID, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component from homeassistant.util import dt @@ -82,3 +90,32 @@ async def test_room_update(hass, mqtt_mock_entry_with_yaml_config): await send_message(hass, BEDROOM_TOPIC, FAR_MESSAGE) await assert_state(hass, BEDROOM) await assert_distance(hass, 10) + + +async def test_unique_id_is_set( + hass: HomeAssistant, mqtt_mock_entry_with_yaml_config +) -> None: + """Test the updating between rooms.""" + unique_name = "my_unique_name_0123456789" + assert await async_setup_component( + hass, + sensor.DOMAIN, + { + sensor.DOMAIN: { + CONF_PLATFORM: "mqtt_room", + CONF_NAME: NAME, + CONF_DEVICE_ID: DEVICE_ID, + CONF_STATE_TOPIC: "room_presence", + CONF_QOS: DEFAULT_QOS, + CONF_TIMEOUT: 5, + CONF_UNIQUE_ID: unique_name, + } + }, + ) + await hass.async_block_till_done() + state = hass.states.get(SENSOR_STATE) + assert state.state is not None + + entity_registry = er.async_get(hass) + entry = entity_registry.async_get(SENSOR_STATE) + assert entry.unique_id == unique_name From 4a089b5c2810e3d18d49ea97cbe149393ac15b90 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Tue, 22 Nov 2022 17:04:55 +0200 Subject: [PATCH 0634/1033] Add tests coverage for Shelly climate platform (#82529) --- .coveragerc | 1 - homeassistant/components/shelly/climate.py | 5 - tests/components/shelly/conftest.py | 5 +- tests/components/shelly/test_climate.py | 374 +++++++++++++++++++++ 4 files changed, 378 insertions(+), 7 deletions(-) create mode 100644 tests/components/shelly/test_climate.py diff --git a/.coveragerc b/.coveragerc index 98a9ee9c6ff..f3931e47155 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1110,7 +1110,6 @@ omit = homeassistant/components/sesame/lock.py homeassistant/components/seven_segments/image_processing.py homeassistant/components/seventeentrack/sensor.py - homeassistant/components/shelly/climate.py homeassistant/components/shelly/coordinator.py homeassistant/components/shelly/utils.py homeassistant/components/shiftr/* diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index c55c0261839..cc831c10ee0 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -27,7 +27,6 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import LOGGER, SHTRV_01_TEMPERATURE_SETTINGS from .coordinator import ShellyBlockCoordinator, get_entry_data -from .utils import get_device_entry_gen async def async_setup_entry( @@ -36,10 +35,6 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up climate device.""" - - if get_device_entry_gen(config_entry) == 2: - return - coordinator = get_entry_data(hass)[config_entry.entry_id].block assert coordinator if coordinator.device.initialized: diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py index 26931ea804e..bfa8e903155 100644 --- a/tests/components/shelly/conftest.py +++ b/tests/components/shelly/conftest.py @@ -30,7 +30,7 @@ MOCK_SETTINGS = { "relays": [{"btn_type": "momentary"}, {"btn_type": "toggle"}], "rollers": [{"positioning": True}], "external_power": 0, - "thermostats": [{"schedule_profile_names": {}}], + "thermostats": [{"schedule_profile_names": ["Profile1", "Profile2"]}], } @@ -100,9 +100,11 @@ MOCK_BLOCKS = [ ), Mock( sensor_ids={"motion": 0, "temp": 22.1, "gas": "mild"}, + channel="0", motion=0, temp=22.1, gas="mild", + targetTemp=4, description="sensor_0", type="sensor", ), @@ -111,6 +113,7 @@ MOCK_BLOCKS = [ channel="0", battery=98, cfgChanged=0, + mode=0, valvePos=50, description="device_0", type="device", diff --git a/tests/components/shelly/test_climate.py b/tests/components/shelly/test_climate.py new file mode 100644 index 00000000000..56effa156e6 --- /dev/null +++ b/tests/components/shelly/test_climate.py @@ -0,0 +1,374 @@ +"""Tests for Shelly climate platform.""" +from unittest.mock import AsyncMock, PropertyMock + +from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError +import pytest + +from homeassistant.components.climate import ( + ATTR_HVAC_MODE, + ATTR_PRESET_MODE, + ATTR_TARGET_TEMP_HIGH, + ATTR_TARGET_TEMP_LOW, + DOMAIN as CLIMATE_DOMAIN, + PRESET_NONE, + SERVICE_SET_HVAC_MODE, + SERVICE_SET_PRESET_MODE, + SERVICE_SET_TEMPERATURE, + HVACMode, +) +from homeassistant.components.shelly.const import DOMAIN +from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState +from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_UNAVAILABLE +from homeassistant.core import State +from homeassistant.exceptions import HomeAssistantError + +from . import init_integration, register_device, register_entity + +from tests.common import mock_restore_cache + +SENSOR_BLOCK_ID = 3 +DEVICE_BLOCK_ID = 4 +ENTITY_ID = f"{CLIMATE_DOMAIN}.test_name" + + +async def test_climate_hvac_mode(hass, mock_block_device, monkeypatch): + """Test climate hvac mode service.""" + monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp") + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0) + await init_integration(hass, 1, sleep_period=1000) + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + # Test initial hvac mode - off + state = hass.states.get(ENTITY_ID) + assert state.state == HVACMode.OFF + + # Test set hvac mode heat + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_HVAC_MODE: HVACMode.HEAT}, + blocking=True, + ) + mock_block_device.http_request.assert_called_once_with( + "get", "thermostat/0", {"target_t_enabled": 1, "target_t": 20.0} + ) + + monkeypatch.setattr(mock_block_device.blocks[SENSOR_BLOCK_ID], "targetTemp", 20.0) + mock_block_device.mock_update() + state = hass.states.get(ENTITY_ID) + assert state.state == HVACMode.HEAT + + # Test set hvac mode off + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_HVAC_MODE: HVACMode.OFF}, + blocking=True, + ) + + mock_block_device.http_request.assert_called_with( + "get", "thermostat/0", {"target_t_enabled": 1, "target_t": "4"} + ) + + monkeypatch.setattr(mock_block_device.blocks[SENSOR_BLOCK_ID], "targetTemp", 4.0) + mock_block_device.mock_update() + state = hass.states.get(ENTITY_ID) + assert state.state == HVACMode.OFF + + # Test unavailable on error + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 1) + mock_block_device.mock_update() + state = hass.states.get(ENTITY_ID) + assert state.state == STATE_UNAVAILABLE + + +async def test_climate_set_temperature(hass, mock_block_device, monkeypatch): + """Test climate set temperature service.""" + monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp") + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0) + await init_integration(hass, 1, sleep_period=1000) + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + state = hass.states.get(ENTITY_ID) + assert state.state == HVACMode.OFF + assert state.attributes[ATTR_TEMPERATURE] == 4 + + # Test set temperature without target temperature + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + { + ATTR_ENTITY_ID: ENTITY_ID, + ATTR_TARGET_TEMP_LOW: 20, + ATTR_TARGET_TEMP_HIGH: 30, + }, + blocking=True, + ) + mock_block_device.http_request.assert_not_called() + + # Test set temperature + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_TEMPERATURE: 23}, + blocking=True, + ) + + mock_block_device.http_request.assert_called_once_with( + "get", "thermostat/0", {"target_t_enabled": 1, "target_t": "23.0"} + ) + + +async def test_climate_set_preset_mode(hass, mock_block_device, monkeypatch): + """Test climate set preset mode service.""" + monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp") + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0) + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "mode", None) + await init_integration(hass, 1, sleep_period=1000) + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + state = hass.states.get(ENTITY_ID) + assert state.attributes[ATTR_PRESET_MODE] == PRESET_NONE + + # Test set Profile2 + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_PRESET_MODE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: "Profile2"}, + blocking=True, + ) + + mock_block_device.http_request.assert_called_once_with( + "get", "thermostat/0", {"schedule": 1, "schedule_profile": "2"} + ) + + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "mode", 2) + mock_block_device.mock_update() + + state = hass.states.get(ENTITY_ID) + assert state.attributes[ATTR_PRESET_MODE] == "Profile2" + + # Set preset to none + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_PRESET_MODE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_NONE}, + blocking=True, + ) + + assert len(mock_block_device.http_request.mock_calls) == 2 + mock_block_device.http_request.assert_called_with( + "get", "thermostat/0", {"schedule": 0} + ) + + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "mode", 0) + mock_block_device.mock_update() + + state = hass.states.get(ENTITY_ID) + assert state.attributes[ATTR_PRESET_MODE] == PRESET_NONE + + +async def test_block_restored_climate(hass, mock_block_device, device_reg, monkeypatch): + """Test block restored climate.""" + monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp") + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0) + entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True) + register_device(device_reg, entry) + entity_id = register_entity( + hass, + CLIMATE_DOMAIN, + "test_name", + "sensor_0", + entry, + ) + mock_restore_cache(hass, [State(entity_id, HVACMode.HEAT)]) + + monkeypatch.setattr(mock_block_device, "initialized", False) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == HVACMode.HEAT + + # Partial update, should not change state + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == HVACMode.HEAT + + # Make device online + monkeypatch.setattr(mock_block_device, "initialized", True) + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == HVACMode.OFF + + +async def test_block_restored_climate_unavailable( + hass, mock_block_device, device_reg, monkeypatch +): + """Test block restored climate unavailable state.""" + monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp") + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0) + entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True) + register_device(device_reg, entry) + entity_id = register_entity( + hass, + CLIMATE_DOMAIN, + "test_name", + "sensor_0", + entry, + ) + mock_restore_cache(hass, [State(entity_id, STATE_UNAVAILABLE)]) + + monkeypatch.setattr(mock_block_device, "initialized", False) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == HVACMode.OFF + + +async def test_block_restored_climate_set_preset_before_online( + hass, mock_block_device, device_reg, monkeypatch +): + """Test block restored climate set preset before device is online.""" + monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp") + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0) + entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True) + register_device(device_reg, entry) + entity_id = register_entity( + hass, + CLIMATE_DOMAIN, + "test_name", + "sensor_0", + entry, + ) + mock_restore_cache(hass, [State(entity_id, HVACMode.HEAT)]) + + monkeypatch.setattr(mock_block_device, "initialized", False) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == HVACMode.HEAT + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_PRESET_MODE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: "Profile1"}, + blocking=True, + ) + + mock_block_device.http_request.assert_not_called() + + +async def test_block_set_mode_connection_error(hass, mock_block_device, monkeypatch): + """Test block device set mode connection error.""" + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0) + monkeypatch.setattr( + mock_block_device, + "http_request", + AsyncMock(side_effect=DeviceConnectionError), + ) + await init_integration(hass, 1, sleep_period=1000) + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_HVAC_MODE: HVACMode.HEAT}, + blocking=True, + ) + + +async def test_block_set_mode_auth_error(hass, mock_block_device, monkeypatch): + """Test block device set mode authentication error.""" + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0) + monkeypatch.setattr( + mock_block_device, + "http_request", + AsyncMock(side_effect=InvalidAuthError), + ) + entry = await init_integration(hass, 1, sleep_period=1000) + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert entry.state == ConfigEntryState.LOADED + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_HVAC_MODE: HVACMode.HEAT}, + blocking=True, + ) + + assert entry.state == ConfigEntryState.LOADED + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + + flow = flows[0] + assert flow.get("step_id") == "reauth_confirm" + assert flow.get("handler") == DOMAIN + + assert "context" in flow + assert flow["context"].get("source") == SOURCE_REAUTH + assert flow["context"].get("entry_id") == entry.entry_id + + +async def test_block_restored_climate_auth_error( + hass, mock_block_device, device_reg, monkeypatch +): + """Test block restored climate with authentication error during init.""" + monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp") + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0) + entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True) + register_device(device_reg, entry) + entity_id = register_entity( + hass, + CLIMATE_DOMAIN, + "test_name", + "sensor_0", + entry, + ) + mock_restore_cache(hass, [State(entity_id, HVACMode.HEAT)]) + + monkeypatch.setattr(mock_block_device, "initialized", False) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state == ConfigEntryState.LOADED + + # Make device online with auth error + monkeypatch.setattr(mock_block_device, "initialized", True) + type(mock_block_device).settings = PropertyMock( + return_value={}, side_effect=InvalidAuthError + ) + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert entry.state == ConfigEntryState.LOADED + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + + flow = flows[0] + assert flow.get("step_id") == "reauth_confirm" + assert flow.get("handler") == DOMAIN + + assert "context" in flow + assert flow["context"].get("source") == SOURCE_REAUTH + assert flow["context"].get("entry_id") == entry.entry_id From 00afcffbf92fda61d56f571822b3132fab950b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Tue, 22 Nov 2022 16:12:33 +0100 Subject: [PATCH 0635/1033] Abort before trying to connect in QNAP QSW DHCP discovery when already configured (#82362) --- homeassistant/components/qnap_qsw/config_flow.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/qnap_qsw/config_flow.py b/homeassistant/components/qnap_qsw/config_flow.py index 794e8c67baa..e0d4a1f78cd 100644 --- a/homeassistant/components/qnap_qsw/config_flow.py +++ b/homeassistant/components/qnap_qsw/config_flow.py @@ -78,6 +78,13 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): _LOGGER.debug("DHCP discovery detected QSW: %s", self._discovered_mac) + await self.async_set_unique_id(format_mac(self._discovered_mac)) + self._abort_if_unique_id_configured( + updates={ + CONF_URL: self._discovered_url, + } + ) + options = ConnectionOptions(self._discovered_url, "", "") qsw = QnapQswApi(aiohttp_client.async_get_clientsession(self.hass), options) @@ -86,9 +93,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): except QswError as err: raise AbortFlow("cannot_connect") from err - await self.async_set_unique_id(format_mac(self._discovered_mac)) - self._abort_if_unique_id_configured() - return await self.async_step_discovered_connection() async def async_step_discovered_connection( From 22f6a72694cd08172bb82ee2394d5e77301eb055 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 22 Nov 2022 16:14:08 +0100 Subject: [PATCH 0636/1033] Improve type hints in acmedia config flow (#82534) --- homeassistant/components/acmeda/config_flow.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/acmeda/config_flow.py b/homeassistant/components/acmeda/config_flow.py index 1db629e506a..f1bd0613f1e 100644 --- a/homeassistant/components/acmeda/config_flow.py +++ b/homeassistant/components/acmeda/config_flow.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio from contextlib import suppress +from typing import Any import aiopulse import async_timeout @@ -10,6 +11,7 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_HOST, CONF_ID +from homeassistant.data_entry_flow import FlowResult from .const import DOMAIN @@ -19,11 +21,13 @@ class AcmedaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 - def __init__(self): + def __init__(self) -> None: """Initialize the config flow.""" self.discovered_hubs: dict[str, aiopulse.Hub] | None = None - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle a flow initialized by the user.""" if ( user_input is not None @@ -37,7 +41,7 @@ class AcmedaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): entry.unique_id for entry in self._async_current_entries() } - hubs = [] + hubs: list[aiopulse.Hub] = [] with suppress(asyncio.TimeoutError): async with async_timeout.timeout(5): async for hub in aiopulse.Hub.discover(): @@ -63,7 +67,7 @@ class AcmedaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): ), ) - async def async_create(self, hub): + async def async_create(self, hub: aiopulse.Hub) -> FlowResult: """Create the Acmeda Hub entry.""" await self.async_set_unique_id(hub.id, raise_on_progress=False) return self.async_create_entry(title=hub.id, data={CONF_HOST: hub.host}) From 7c36f1409daf334d12fe119eb1f3489aa0abf80a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 23 Nov 2022 08:45:40 +1300 Subject: [PATCH 0637/1033] Bump aioesphomeapi to 12.0.0 (#82543) --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 12b2bc98d2c..b38a4c1f4d5 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==11.5.0"], + "requirements": ["aioesphomeapi==12.0.0"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/requirements_all.txt b/requirements_all.txt index ebc20fec753..f584f049445 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -156,7 +156,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.5.0 +aioesphomeapi==12.0.0 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d34101dc73b..272072cc6d8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -143,7 +143,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==11.5.0 +aioesphomeapi==12.0.0 # homeassistant.components.flo aioflo==2021.11.0 From f20c8c657aee3ee9068f6bd96abe409e4e4910f9 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Wed, 23 Nov 2022 00:05:40 +0000 Subject: [PATCH 0638/1033] Fix HomeKit CoAP connection getting RST incorrectly (#82553) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 864ec1e9bd0..54d5c5b854d 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.3.0"], + "requirements": ["aiohomekit==2.3.1"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index f584f049445..a5ff67ef687 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.0 +aiohomekit==2.3.1 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 272072cc6d8..dccaea89705 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.0 +aiohomekit==2.3.1 # homeassistant.components.emulated_hue # homeassistant.components.http From eaf6d43508c015bda740ea6e6cb496e11d9f71fd Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 23 Nov 2022 00:26:26 +0000 Subject: [PATCH 0639/1033] [ci skip] Translation update --- .../components/acmeda/translations/sk.json | 3 + .../components/adax/translations/sk.json | 1 + .../components/airnow/translations/sk.json | 3 + .../components/airq/translations/el.json | 6 ++ .../components/airq/translations/sk.json | 3 +- .../airthings_ble/translations/sk.json | 6 +- .../airvisual/translations/sensor.sk.json | 1 + .../alarm_control_panel/translations/sk.json | 5 ++ .../alarmdecoder/translations/sk.json | 5 ++ .../ambient_station/translations/sk.json | 3 +- .../components/androidtv/translations/sk.json | 1 + .../components/anthemav/translations/sk.json | 3 + .../components/apple_tv/translations/sk.json | 22 ++++++- .../components/aranet/translations/el.json | 9 ++- .../components/arcam_fmj/translations/sk.json | 9 ++- .../components/asuswrt/translations/sk.json | 9 +++ .../components/atag/translations/sk.json | 3 +- .../aurora_abb_powerone/translations/sk.json | 7 ++ .../aussie_broadband/translations/sk.json | 3 +- .../automation/translations/sk.json | 12 ++++ .../components/awair/translations/sk.json | 5 ++ .../components/axis/translations/sk.json | 3 + .../components/baf/translations/sk.json | 3 + .../components/balboa/translations/sk.json | 3 + .../binary_sensor/translations/sk.json | 48 ++++++++++++++ .../components/blebox/translations/sk.json | 3 + .../components/blink/translations/sk.json | 3 + .../components/bluetooth/translations/sk.json | 9 ++- .../components/bond/translations/sk.json | 3 +- .../components/bosch_shc/translations/sk.json | 2 + .../components/braviatv/translations/sk.json | 3 + .../components/broadlink/translations/sk.json | 10 ++- .../components/brother/translations/sk.json | 3 + .../components/bsblan/translations/sk.json | 3 + .../components/canary/translations/sk.json | 1 + .../climacell/translations/sensor.sk.json | 11 ++++ .../components/coinbase/translations/sk.json | 3 + .../components/control4/translations/sk.json | 12 ++++ .../components/cover/translations/sk.json | 21 ++++++ .../components/deconz/translations/sk.json | 22 +++++++ .../derivative/translations/sk.json | 3 +- .../devolo_home_network/translations/el.json | 3 +- .../devolo_home_network/translations/sk.json | 3 + .../components/dexcom/translations/sk.json | 9 +++ .../components/directv/translations/sk.json | 6 ++ .../components/dlna_dmr/translations/sk.json | 5 ++ .../components/dlna_dms/translations/sk.json | 7 +- .../components/doorbird/translations/sk.json | 3 + .../components/dsmr/translations/sk.json | 9 +++ .../components/dunehd/translations/sk.json | 4 ++ .../components/eafm/translations/sk.json | 7 ++ .../components/econet/translations/sk.json | 1 + .../components/efergy/translations/sk.json | 1 + .../eight_sleep/translations/sk.json | 3 + .../components/elgato/translations/sk.json | 3 + .../components/elmax/translations/sk.json | 3 + .../components/emonitor/translations/sk.json | 3 + .../components/escea/translations/sk.json | 7 ++ .../components/fan/translations/sk.json | 9 +++ .../components/fibaro/translations/sk.json | 3 + .../fjaraskupan/translations/sk.json | 7 ++ .../components/flipr/translations/sk.json | 3 + .../components/flo/translations/sk.json | 3 + .../components/flux_led/translations/sk.json | 4 +- .../forked_daapd/translations/sk.json | 3 + .../components/foscam/translations/sk.json | 3 +- .../components/freebox/translations/sk.json | 3 + .../freedompro/translations/sk.json | 3 + .../components/fritz/translations/sk.json | 2 + .../components/fritzbox/translations/sk.json | 8 ++- .../fritzbox_callmonitor/translations/sk.json | 4 ++ .../components/fronius/translations/sk.json | 5 ++ .../garages_amsterdam/translations/sk.json | 7 ++ .../components/generic/translations/el.json | 7 ++ .../components/generic/translations/sk.json | 3 + .../components/glances/translations/sk.json | 12 ++++ .../components/goalzero/translations/sk.json | 1 + .../components/gogogate2/translations/sk.json | 1 + .../components/goodwe/translations/sk.json | 1 + .../components/google/translations/sk.json | 11 ++++ .../google_travel_time/translations/el.json | 3 +- .../components/govee_ble/translations/sk.json | 8 +++ .../components/gree/translations/sk.json | 7 ++ .../components/group/translations/sk.json | 66 +++++++++++++++++++ .../components/guardian/translations/sk.json | 1 + .../components/harmony/translations/sk.json | 3 + .../here_travel_time/translations/sk.json | 7 ++ .../homeassistant_yellow/translations/de.json | 5 +- .../homeassistant_yellow/translations/el.json | 3 +- .../homeassistant_yellow/translations/es.json | 5 +- .../homeassistant_yellow/translations/et.json | 5 +- .../homeassistant_yellow/translations/fr.json | 3 + .../homeassistant_yellow/translations/id.json | 3 +- .../homeassistant_yellow/translations/ru.json | 5 +- .../homekit_controller/translations/de.json | 2 +- .../homekit_controller/translations/es.json | 2 +- .../homekit_controller/translations/et.json | 2 +- .../homekit_controller/translations/ru.json | 2 +- .../translations/sensor.sk.json | 7 ++ .../homekit_controller/translations/sk.json | 20 ++++++ .../homematicip_cloud/translations/sk.json | 3 + .../homewizard/translations/sk.json | 16 +++++ .../huawei_lte/translations/sk.json | 3 +- .../components/hue/translations/sk.json | 5 ++ .../huisbaasje/translations/sk.json | 3 + .../humidifier/translations/sk.json | 10 +++ .../translations/sk.json | 6 ++ .../hvv_departures/translations/sk.json | 3 + .../components/ialarm/translations/sk.json | 3 + .../components/icloud/translations/sk.json | 7 ++ .../components/inkbird/translations/sk.json | 8 +++ .../components/insteon/translations/sk.json | 3 + .../integration/translations/sk.json | 3 +- .../intellifire/translations/sk.json | 15 ++++- .../components/ipp/translations/sk.json | 6 ++ .../components/isy994/translations/sk.json | 6 +- .../components/izone/translations/sk.json | 7 ++ .../justnimbus/translations/sk.json | 7 ++ .../kaleidescape/translations/sk.json | 7 ++ .../keymitt_ble/translations/sk.json | 3 +- .../components/kmtronic/translations/sk.json | 3 + .../components/knx/translations/el.json | 16 ++++- .../components/knx/translations/sk.json | 13 ++++ .../components/kodi/translations/sk.json | 1 + .../components/konnected/translations/sk.json | 10 ++- .../lacrosse_view/translations/sk.json | 3 + .../components/lametric/translations/el.json | 1 + .../landisgyr_heat_meter/translations/sk.json | 7 ++ .../components/led_ble/translations/sk.json | 2 + .../lg_soundbar/translations/sk.json | 4 ++ .../components/lifx/translations/sk.json | 6 ++ .../components/light/translations/sk.json | 17 +++++ .../litterrobot/translations/sensor.sk.json | 2 + .../components/livisi/translations/el.json | 2 + .../components/lock/translations/sk.json | 15 +++++ .../components/lookin/translations/sk.json | 10 ++- .../components/luftdaten/translations/sk.json | 14 ++++ .../lutron_caseta/translations/sk.json | 17 ++++- .../media_player/translations/sk.json | 5 ++ .../meteoclimatic/translations/sk.json | 10 +++ .../components/mill/translations/sk.json | 3 + .../components/mjpeg/translations/sk.json | 4 ++ .../components/moat/translations/sk.json | 8 +++ .../modem_callerid/translations/sk.json | 1 + .../modern_forms/translations/sk.json | 3 + .../moehlenhoff_alpha2/translations/sk.json | 3 + .../components/monoprice/translations/sk.json | 6 +- .../moon/translations/sensor.sk.json | 10 +++ .../motion_blinds/translations/sk.json | 2 + .../components/mqtt/translations/el.json | 18 ++++- .../components/mqtt/translations/sk.json | 13 ++++ .../components/mullvad/translations/sk.json | 7 ++ .../components/mysensors/translations/sk.json | 32 ++++++++- .../nam/translations/sensor.sk.json | 11 ++++ .../components/nam/translations/sk.json | 5 +- .../components/nanoleaf/translations/sk.json | 6 +- .../components/neato/translations/sk.json | 1 + .../components/nest/translations/sk.json | 8 +++ .../components/netatmo/translations/sk.json | 14 ++++ .../components/netgear/translations/sk.json | 3 + .../components/nexia/translations/sk.json | 3 + .../nfandroidtv/translations/sk.json | 3 + .../nightscout/translations/sk.json | 3 + .../components/nuheat/translations/sk.json | 3 + .../components/nzbget/translations/sk.json | 1 + .../components/octoprint/translations/sk.json | 3 + .../components/onewire/translations/sk.json | 12 ++++ .../opengarage/translations/sk.json | 4 ++ .../components/openuv/translations/el.json | 9 ++- .../components/oralb/translations/el.json | 9 ++- .../components/oralb/translations/sk.json | 2 + .../overkiz/translations/sensor.sk.json | 19 +++++- .../components/picnic/translations/sk.json | 1 + .../components/plaato/translations/sk.json | 11 ++++ .../components/poolsense/translations/sk.json | 3 + .../components/powerwall/translations/sk.json | 4 ++ .../progettihwsw/translations/sk.json | 3 + .../components/prosegur/translations/sk.json | 1 + .../prusalink/translations/sensor.sk.json | 7 ++ .../components/ps4/translations/sk.json | 3 +- .../pure_energie/translations/sk.json | 3 + .../pushbullet/translations/el.json | 17 +++++ .../pvpc_hourly_pricing/translations/sk.json | 11 ++++ .../components/qnap_qsw/translations/sk.json | 3 + .../components/rachio/translations/sk.json | 3 + .../radiotherm/translations/sk.json | 6 ++ .../rainforest_eagle/translations/sk.json | 3 + .../rainmachine/translations/sk.json | 3 + .../recollect_waste/translations/sk.json | 7 ++ .../components/remote/translations/sk.json | 5 ++ .../components/rfxtrx/translations/sk.json | 25 +++++++ .../components/risco/translations/sk.json | 3 + .../translations/sk.json | 3 + .../components/roku/translations/sk.json | 4 ++ .../components/roomba/translations/sk.json | 9 ++- .../components/roon/translations/sk.json | 3 + .../ruckus_unleashed/translations/sk.json | 3 + .../components/samsungtv/translations/sk.json | 6 ++ .../components/scrape/translations/bg.json | 14 ++++ .../components/scrape/translations/el.json | 30 +++++++++ .../components/scrape/translations/es.json | 34 +++++++++- .../components/scrape/translations/et.json | 32 ++++++++- .../components/scrape/translations/fr.json | 22 ++++++- .../components/scrape/translations/id.json | 31 ++++++++- .../components/scrape/translations/ru.json | 34 +++++++++- .../components/scrape/translations/sk.json | 19 +++++- .../screenlogic/translations/sk.json | 3 + .../season/translations/sensor.sk.json | 10 ++- .../components/sense/translations/sk.json | 3 + .../components/senseme/translations/sk.json | 6 ++ .../sensibo/translations/sensor.sk.json | 13 ++++ .../components/sensibo/translations/sk.json | 3 +- .../components/sensor/translations/sk.json | 2 + .../components/sensorpro/translations/sk.json | 15 +++++ .../sensorpush/translations/sk.json | 21 ++++++ .../simplepush/translations/sk.json | 7 ++ .../components/skybell/translations/sk.json | 3 +- .../components/sma/translations/sk.json | 1 + .../components/smappee/translations/sk.json | 4 ++ .../smart_meter_texas/translations/sk.json | 3 + .../components/sms/translations/sk.json | 3 + .../components/snooz/translations/el.json | 10 ++- .../components/solaredge/translations/sk.json | 4 ++ .../components/solarlog/translations/sk.json | 6 ++ .../components/songpal/translations/sk.json | 7 +- .../components/sonos/translations/sk.json | 7 ++ .../soundtouch/translations/sk.json | 3 + .../speedtestdotnet/translations/sk.json | 12 ++++ .../components/sql/translations/sk.json | 26 ++++++++ .../squeezebox/translations/sk.json | 3 + .../components/steamist/translations/sk.json | 8 ++- .../components/switch/translations/sk.json | 1 + .../components/switchbot/translations/sk.json | 16 ++++- .../synology_dsm/translations/sk.json | 3 +- .../system_bridge/translations/sk.json | 2 + .../components/tado/translations/sk.json | 3 + .../tankerkoenig/translations/sk.json | 9 +++ .../tesla_wall_connector/translations/sk.json | 3 + .../components/tolo/translations/sk.json | 3 + .../tomorrowio/translations/sensor.sk.json | 4 +- .../components/tplink/translations/sk.json | 7 ++ .../tractive/translations/sensor.sk.json | 7 ++ .../components/tractive/translations/sk.json | 1 + .../transmission/translations/sk.json | 12 ++++ .../tuya/translations/sensor.sk.json | 8 ++- .../components/twinkly/translations/sk.json | 6 ++ .../unifiprotect/translations/sk.json | 1 + .../components/update/translations/sk.json | 9 +++ .../components/upnp/translations/sk.json | 3 +- .../utility_meter/translations/sk.json | 11 ++++ .../components/velbus/translations/sk.json | 10 +++ .../components/venstar/translations/sk.json | 3 + .../components/version/translations/sk.json | 7 ++ .../components/vilfo/translations/sk.json | 3 + .../components/vizio/translations/sk.json | 3 + .../vlc_telnet/translations/sk.json | 3 +- .../components/volumio/translations/sk.json | 3 + .../components/wallbox/translations/sk.json | 1 + .../components/watttime/translations/sk.json | 1 + .../components/wemo/translations/sk.json | 7 ++ .../components/wilight/translations/sk.json | 8 +++ .../components/wiz/translations/sk.json | 7 +- .../wled/translations/select.sk.json | 7 ++ .../components/wled/translations/sk.json | 3 + .../wolflink/translations/sensor.sk.json | 15 +++++ .../components/ws66i/translations/sk.json | 12 ++++ .../xiaomi_ble/translations/sk.json | 8 +++ .../xiaomi_miio/translations/el.json | 3 +- .../xiaomi_miio/translations/sk.json | 5 ++ .../yalexs_ble/translations/sk.json | 12 +++- .../components/yeelight/translations/sk.json | 6 +- .../components/zamg/translations/el.json | 6 +- .../components/zerproc/translations/sk.json | 7 ++ .../components/zha/translations/sk.json | 21 +++++- .../zodiac/translations/sensor.sk.json | 18 +++++ .../components/zwave_js/translations/sk.json | 9 +++ .../components/zwave_me/translations/sk.json | 7 ++ 277 files changed, 1903 insertions(+), 88 deletions(-) create mode 100644 homeassistant/components/aurora_abb_powerone/translations/sk.json create mode 100644 homeassistant/components/eafm/translations/sk.json create mode 100644 homeassistant/components/escea/translations/sk.json create mode 100644 homeassistant/components/fjaraskupan/translations/sk.json create mode 100644 homeassistant/components/garages_amsterdam/translations/sk.json create mode 100644 homeassistant/components/google/translations/sk.json create mode 100644 homeassistant/components/govee_ble/translations/sk.json create mode 100644 homeassistant/components/gree/translations/sk.json create mode 100644 homeassistant/components/here_travel_time/translations/sk.json create mode 100644 homeassistant/components/homekit_controller/translations/sensor.sk.json create mode 100644 homeassistant/components/homewizard/translations/sk.json create mode 100644 homeassistant/components/humidifier/translations/sk.json create mode 100644 homeassistant/components/inkbird/translations/sk.json create mode 100644 homeassistant/components/izone/translations/sk.json create mode 100644 homeassistant/components/justnimbus/translations/sk.json create mode 100644 homeassistant/components/luftdaten/translations/sk.json create mode 100644 homeassistant/components/meteoclimatic/translations/sk.json create mode 100644 homeassistant/components/moat/translations/sk.json create mode 100644 homeassistant/components/moon/translations/sensor.sk.json create mode 100644 homeassistant/components/mullvad/translations/sk.json create mode 100644 homeassistant/components/nam/translations/sensor.sk.json create mode 100644 homeassistant/components/plaato/translations/sk.json create mode 100644 homeassistant/components/prusalink/translations/sensor.sk.json create mode 100644 homeassistant/components/pvpc_hourly_pricing/translations/sk.json create mode 100644 homeassistant/components/recollect_waste/translations/sk.json create mode 100644 homeassistant/components/sensibo/translations/sensor.sk.json create mode 100644 homeassistant/components/sensorpush/translations/sk.json create mode 100644 homeassistant/components/simplepush/translations/sk.json create mode 100644 homeassistant/components/sonos/translations/sk.json create mode 100644 homeassistant/components/speedtestdotnet/translations/sk.json create mode 100644 homeassistant/components/sql/translations/sk.json create mode 100644 homeassistant/components/tractive/translations/sensor.sk.json create mode 100644 homeassistant/components/update/translations/sk.json create mode 100644 homeassistant/components/utility_meter/translations/sk.json create mode 100644 homeassistant/components/velbus/translations/sk.json create mode 100644 homeassistant/components/version/translations/sk.json create mode 100644 homeassistant/components/wemo/translations/sk.json create mode 100644 homeassistant/components/wilight/translations/sk.json create mode 100644 homeassistant/components/wled/translations/select.sk.json create mode 100644 homeassistant/components/wolflink/translations/sensor.sk.json create mode 100644 homeassistant/components/ws66i/translations/sk.json create mode 100644 homeassistant/components/xiaomi_ble/translations/sk.json create mode 100644 homeassistant/components/zerproc/translations/sk.json create mode 100644 homeassistant/components/zodiac/translations/sensor.sk.json create mode 100644 homeassistant/components/zwave_me/translations/sk.json diff --git a/homeassistant/components/acmeda/translations/sk.json b/homeassistant/components/acmeda/translations/sk.json index 7733c32fc87..f1ebfe35ab4 100644 --- a/homeassistant/components/acmeda/translations/sk.json +++ b/homeassistant/components/acmeda/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/adax/translations/sk.json b/homeassistant/components/adax/translations/sk.json index a1c6f397beb..7f23f337f53 100644 --- a/homeassistant/components/adax/translations/sk.json +++ b/homeassistant/components/adax/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { diff --git a/homeassistant/components/airnow/translations/sk.json b/homeassistant/components/airnow/translations/sk.json index df686b2a565..c0aa708a993 100644 --- a/homeassistant/components/airnow/translations/sk.json +++ b/homeassistant/components/airnow/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/airq/translations/el.json b/homeassistant/components/airq/translations/el.json index 79004a86cb9..594e0aaf188 100644 --- a/homeassistant/components/airq/translations/el.json +++ b/homeassistant/components/airq/translations/el.json @@ -3,9 +3,15 @@ "abort": { "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "invalid_input": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae \u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP" + }, "step": { "user": { "data": { + "ip_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" }, "description": "\u0394\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03ae \u03c4\u03bf mDNS \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03ba\u03b1\u03b9 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03ae\u03c2 \u03c4\u03b7\u03c2", diff --git a/homeassistant/components/airq/translations/sk.json b/homeassistant/components/airq/translations/sk.json index 16f47ef1360..ddf1a8dc9b3 100644 --- a/homeassistant/components/airq/translations/sk.json +++ b/homeassistant/components/airq/translations/sk.json @@ -13,7 +13,8 @@ "data": { "ip_address": "IP adresa", "password": "Heslo" - } + }, + "title": "Identifikujte zariadenie" } } } diff --git a/homeassistant/components/airthings_ble/translations/sk.json b/homeassistant/components/airthings_ble/translations/sk.json index 8da39055837..8643ee69fa7 100644 --- a/homeassistant/components/airthings_ble/translations/sk.json +++ b/homeassistant/components/airthings_ble/translations/sk.json @@ -9,10 +9,14 @@ }, "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/airvisual/translations/sensor.sk.json b/homeassistant/components/airvisual/translations/sensor.sk.json index e54107c433a..2af833ce1f2 100644 --- a/homeassistant/components/airvisual/translations/sensor.sk.json +++ b/homeassistant/components/airvisual/translations/sensor.sk.json @@ -1,6 +1,7 @@ { "state": { "airvisual__pollutant_label": { + "o3": "Oz\u00f3n", "p1": "PM10", "p2": "PM2,5", "s2": "Oxid siri\u010dit\u00fd" diff --git a/homeassistant/components/alarm_control_panel/translations/sk.json b/homeassistant/components/alarm_control_panel/translations/sk.json index ceff70c00a6..f60448e3d31 100644 --- a/homeassistant/components/alarm_control_panel/translations/sk.json +++ b/homeassistant/components/alarm_control_panel/translations/sk.json @@ -1,4 +1,9 @@ { + "device_automation": { + "action_type": { + "trigger": "Sp\u00fa\u0161\u0165a\u010d {entity_name}" + } + }, "state": { "_": { "armed": "Akt\u00edvny", diff --git a/homeassistant/components/alarmdecoder/translations/sk.json b/homeassistant/components/alarmdecoder/translations/sk.json index 15235e55f54..c5139d40774 100644 --- a/homeassistant/components/alarmdecoder/translations/sk.json +++ b/homeassistant/components/alarmdecoder/translations/sk.json @@ -1,8 +1,13 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "protocol": { "data": { + "device_baudrate": "Prenosov\u00e1 r\u00fdchlos\u0165 zariadenia", + "device_path": "Cesta k zariadeniu", "host": "Hostite\u013e", "port": "Port" } diff --git a/homeassistant/components/ambient_station/translations/sk.json b/homeassistant/components/ambient_station/translations/sk.json index 0524846d2bf..285f13630cc 100644 --- a/homeassistant/components/ambient_station/translations/sk.json +++ b/homeassistant/components/ambient_station/translations/sk.json @@ -4,7 +4,8 @@ "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" }, "error": { - "invalid_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" + "invalid_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", + "no_devices": "V \u00fa\u010dte sa nena\u0161li \u017eiadne zariadenia" }, "step": { "user": { diff --git a/homeassistant/components/androidtv/translations/sk.json b/homeassistant/components/androidtv/translations/sk.json index 6820d55bf23..8e18f3c18cc 100644 --- a/homeassistant/components/androidtv/translations/sk.json +++ b/homeassistant/components/androidtv/translations/sk.json @@ -9,6 +9,7 @@ "step": { "user": { "data": { + "device_class": "Typ zariadenia", "host": "Hostite\u013e", "port": "Port" } diff --git a/homeassistant/components/anthemav/translations/sk.json b/homeassistant/components/anthemav/translations/sk.json index 842ff61bd79..3c3d27a6689 100644 --- a/homeassistant/components/anthemav/translations/sk.json +++ b/homeassistant/components/anthemav/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/apple_tv/translations/sk.json b/homeassistant/components/apple_tv/translations/sk.json index 7614703adcc..63c0bc85d40 100644 --- a/homeassistant/components/apple_tv/translations/sk.json +++ b/homeassistant/components/apple_tv/translations/sk.json @@ -1,21 +1,39 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "setup_failed": "Zariadenie sa nepodarilo nastavi\u0165." }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "invalid_auth": "Neplatn\u00e9 overenie", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "flow_title": "{name} ({type})", "step": { "password": { "title": "Vy\u017eaduje sa heslo" }, + "reconfigure": { + "title": "Rekonfigur\u00e1cia zariadenia" + }, "user": { "data": { "device_input": "Zariadenie" } } } + }, + "options": { + "step": { + "init": { + "data": { + "start_off": "Nezap\u00ednajte zariadenie pri sp\u00fa\u0161\u0165an\u00ed Home Assistant" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/el.json b/homeassistant/components/aranet/translations/el.json index 1f5383f1c07..9a667f53fbf 100644 --- a/homeassistant/components/aranet/translations/el.json +++ b/homeassistant/components/aranet/translations/el.json @@ -6,12 +6,19 @@ "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03bc\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03bc\u03ad\u03bd\u03b5\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 Aranet.", "outdated_version": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c0\u03b1\u03bb\u03b9\u03cc \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc. \u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c4\u03bf\u03c5\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf\u03bd \u03c3\u03b5 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 1.2.0 \u03ba\u03b1\u03b9 \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac." }, + "error": { + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name};" + }, "user": { "data": { "address": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" - } + }, + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b3\u03b9\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7" } } } diff --git a/homeassistant/components/arcam_fmj/translations/sk.json b/homeassistant/components/arcam_fmj/translations/sk.json index 0f258ae8682..087244c9027 100644 --- a/homeassistant/components/arcam_fmj/translations/sk.json +++ b/homeassistant/components/arcam_fmj/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "flow_title": "{host}", @@ -9,8 +10,14 @@ "data": { "host": "Hostite\u013e", "port": "Port" - } + }, + "description": "Zadajte n\u00e1zov hostite\u013ea alebo IP adresu zariadenia." } } + }, + "device_automation": { + "trigger_type": { + "turn_on": "{entity_name} bola po\u017eiadan\u00e1 o zapnutie" + } } } \ No newline at end of file diff --git a/homeassistant/components/asuswrt/translations/sk.json b/homeassistant/components/asuswrt/translations/sk.json index b808989525e..d61369ad97b 100644 --- a/homeassistant/components/asuswrt/translations/sk.json +++ b/homeassistant/components/asuswrt/translations/sk.json @@ -13,5 +13,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "require_ip": "Zariadenia musia ma\u0165 IP (pre re\u017eim pr\u00edstupov\u00e9ho bodu)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/atag/translations/sk.json b/homeassistant/components/atag/translations/sk.json index f92b79dc3e3..92621fc08c4 100644 --- a/homeassistant/components/atag/translations/sk.json +++ b/homeassistant/components/atag/translations/sk.json @@ -8,7 +8,8 @@ "data": { "host": "Hostite\u013e", "port": "Port" - } + }, + "title": "Pripojte sa k zariadeniu" } } } diff --git a/homeassistant/components/aurora_abb_powerone/translations/sk.json b/homeassistant/components/aurora_abb_powerone/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/aurora_abb_powerone/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aussie_broadband/translations/sk.json b/homeassistant/components/aussie_broadband/translations/sk.json index ee107e64a8f..6008bee5113 100644 --- a/homeassistant/components/aussie_broadband/translations/sk.json +++ b/homeassistant/components/aussie_broadband/translations/sk.json @@ -10,7 +10,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Aktualizova\u0165 heslo pre {username}" }, "service": { "title": "Vyberte slu\u017eby" diff --git a/homeassistant/components/automation/translations/sk.json b/homeassistant/components/automation/translations/sk.json index a300acd23da..d75fc6b3157 100644 --- a/homeassistant/components/automation/translations/sk.json +++ b/homeassistant/components/automation/translations/sk.json @@ -1,4 +1,16 @@ { + "issues": { + "service_not_found": { + "fix_flow": { + "step": { + "confirm": { + "title": "{name} pou\u017e\u00edva nezn\u00e1mu slu\u017ebu" + } + } + }, + "title": "{name} pou\u017e\u00edva nezn\u00e1mu slu\u017ebu" + } + }, "state": { "_": { "off": "Neakt\u00edvny", diff --git a/homeassistant/components/awair/translations/sk.json b/homeassistant/components/awair/translations/sk.json index 26d16542bc8..38e668addf9 100644 --- a/homeassistant/components/awair/translations/sk.json +++ b/homeassistant/components/awair/translations/sk.json @@ -1,10 +1,15 @@ { "config": { "abort": { + "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "flow_title": "{model} ({device_id})", "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {model} ({device_id})?" + }, "local": { "data": { "host": "IP adresa" diff --git a/homeassistant/components/axis/translations/sk.json b/homeassistant/components/axis/translations/sk.json index 5aa81a95c18..085b5538318 100644 --- a/homeassistant/components/axis/translations/sk.json +++ b/homeassistant/components/axis/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "invalid_auth": "Neplatn\u00e9 overenie" diff --git a/homeassistant/components/baf/translations/sk.json b/homeassistant/components/baf/translations/sk.json index a0c6a2aa853..95d21efd120 100644 --- a/homeassistant/components/baf/translations/sk.json +++ b/homeassistant/components/baf/translations/sk.json @@ -10,6 +10,9 @@ }, "flow_title": "{name} - {model} ({ip_address})", "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {name} - {model} ({ip_address})?" + }, "user": { "data": { "ip_address": "IP adresa" diff --git a/homeassistant/components/balboa/translations/sk.json b/homeassistant/components/balboa/translations/sk.json index 842ff61bd79..3c3d27a6689 100644 --- a/homeassistant/components/balboa/translations/sk.json +++ b/homeassistant/components/balboa/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/binary_sensor/translations/sk.json b/homeassistant/components/binary_sensor/translations/sk.json index 89e6288a979..018a7494aeb 100644 --- a/homeassistant/components/binary_sensor/translations/sk.json +++ b/homeassistant/components/binary_sensor/translations/sk.json @@ -1,6 +1,38 @@ { + "device_automation": { + "condition_type": { + "is_motion": "{entity_name} zaznamen\u00e1va pohyb", + "is_moving": "{entity_name} sa h\u00fdbe", + "is_no_gas": "{entity_name} nedetekuje plyn", + "is_no_light": "{entity_name} nezaznamen\u00e1va svetlo", + "is_no_motion": "{entity_name} nezaznamen\u00e1va pohyb", + "is_no_problem": "{entity_name} nedetekuje probl\u00e9m", + "is_no_smoke": "{entity_name} nezis\u0165uje dym", + "is_no_sound": "{entity_name} nerozpozn\u00e1va zvuk", + "is_no_update": "{entity_name} je aktu\u00e1lne", + "is_no_vibration": "{entity_name} nezaznamen\u00e1va vibr\u00e1cie", + "is_not_cold": "{entity_name} nie je studen\u00e9", + "is_not_connected": "{entity_name} je odpojen\u00fd", + "is_not_hot": "{entity_name} nie je hor\u00face", + "is_not_locked": "{entity_name} je odomknut\u00e9", + "is_not_moist": "{entity_name} je such\u00e9", + "is_not_moving": "{entity_name} sa neh\u00fdbe" + }, + "trigger_type": { + "connected": "{entity_name} je pripojen\u00fd", + "not_plugged_in": "{entity_name} odpojen\u00fd", + "not_powered": "{entity_name} nie je nap\u00e1jan\u00e9" + } + }, "device_class": { + "cold": "chlad", + "gas": "plyn", + "heat": "teplo", + "moisture": "vlhkos\u0165", "motion": "pohyb", + "power": "nap\u00e1janie", + "smoke": "dym", + "sound": "zvuk", "vibration": "vibr\u00e1cia" }, "state": { @@ -12,6 +44,10 @@ "off": "Norm\u00e1lna", "on": "Slab\u00e1" }, + "battery_charging": { + "off": "Nenab\u00edja sa", + "on": "Nab\u00edja sa" + }, "cold": { "off": "Norm\u00e1lny", "on": "Studen\u00fd" @@ -36,6 +72,10 @@ "off": "Norm\u00e1lny", "on": "Hor\u00faci" }, + "light": { + "off": "Bez svetla", + "on": "Detekovan\u00e9 svetlo" + }, "lock": { "off": "Zamknut\u00fd", "on": "Odomknut\u00fd" @@ -64,6 +104,10 @@ "off": "OK", "on": "Probl\u00e9m" }, + "running": { + "off": "Nie je spusten\u00e9", + "on": "Spusten\u00e9" + }, "safety": { "off": "Zabezpe\u010den\u00e9", "on": "Nezabezpe\u010den\u00e9" @@ -76,6 +120,10 @@ "off": "Ticho", "on": "Zachyten\u00fd zvuk" }, + "update": { + "off": "Aktu\u00e1lne", + "on": "Aktualiz\u00e1cia k dispoz\u00edcii" + }, "vibration": { "off": "K\u013eud", "on": "Zachyten\u00e9 vibr\u00e1cie" diff --git a/homeassistant/components/blebox/translations/sk.json b/homeassistant/components/blebox/translations/sk.json index 27bd1caec0c..da190d975bf 100644 --- a/homeassistant/components/blebox/translations/sk.json +++ b/homeassistant/components/blebox/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name} ({host})", "step": { "user": { diff --git a/homeassistant/components/blink/translations/sk.json b/homeassistant/components/blink/translations/sk.json index 1b1e671c054..8f06d99549b 100644 --- a/homeassistant/components/blink/translations/sk.json +++ b/homeassistant/components/blink/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/bluetooth/translations/sk.json b/homeassistant/components/bluetooth/translations/sk.json index efe0c9c2168..1d375717246 100644 --- a/homeassistant/components/bluetooth/translations/sk.json +++ b/homeassistant/components/bluetooth/translations/sk.json @@ -2,10 +2,17 @@ "config": { "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, + "single_adapter": { + "description": "Chcete nastavi\u0165 adapt\u00e9r Bluetooth {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/bond/translations/sk.json b/homeassistant/components/bond/translations/sk.json index 9d41265051b..f98254c3683 100644 --- a/homeassistant/components/bond/translations/sk.json +++ b/homeassistant/components/bond/translations/sk.json @@ -8,7 +8,8 @@ "confirm": { "data": { "access_token": "Pr\u00edstupov\u00fd token" - } + }, + "description": "Chcete nastavi\u0165 {name}?" }, "user": { "data": { diff --git a/homeassistant/components/bosch_shc/translations/sk.json b/homeassistant/components/bosch_shc/translations/sk.json index 81a4dcb4a93..ff58ed1c18e 100644 --- a/homeassistant/components/bosch_shc/translations/sk.json +++ b/homeassistant/components/bosch_shc/translations/sk.json @@ -1,11 +1,13 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "Bosch SHC: {name}", "step": { "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/sk.json b/homeassistant/components/braviatv/translations/sk.json index 2fe193920d4..70375f14897 100644 --- a/homeassistant/components/braviatv/translations/sk.json +++ b/homeassistant/components/braviatv/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, diff --git a/homeassistant/components/broadlink/translations/sk.json b/homeassistant/components/broadlink/translations/sk.json index 217d7cc83a4..2ea0040fd3a 100644 --- a/homeassistant/components/broadlink/translations/sk.json +++ b/homeassistant/components/broadlink/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", "not_supported": "Zariadenie nie je podporovan\u00e9" @@ -15,10 +16,17 @@ "name": "N\u00e1zov" } }, + "reset": { + "title": "Odomknite zariadenie" + }, + "unlock": { + "title": "Odomknite zariadenie (volite\u013en\u00e9)" + }, "user": { "data": { "host": "Hostite\u013e" - } + }, + "title": "Pripojte sa k zariadeniu" } } } diff --git a/homeassistant/components/brother/translations/sk.json b/homeassistant/components/brother/translations/sk.json index 2c530043b17..ce151b96477 100644 --- a/homeassistant/components/brother/translations/sk.json +++ b/homeassistant/components/brother/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "wrong_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa." }, diff --git a/homeassistant/components/bsblan/translations/sk.json b/homeassistant/components/bsblan/translations/sk.json index dae42543c95..5266d9beff4 100644 --- a/homeassistant/components/bsblan/translations/sk.json +++ b/homeassistant/components/bsblan/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/canary/translations/sk.json b/homeassistant/components/canary/translations/sk.json index 66e8cd431a4..6a802cc7398 100644 --- a/homeassistant/components/canary/translations/sk.json +++ b/homeassistant/components/canary/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/climacell/translations/sensor.sk.json b/homeassistant/components/climacell/translations/sensor.sk.json index 843169b2f3b..dfe997964cc 100644 --- a/homeassistant/components/climacell/translations/sensor.sk.json +++ b/homeassistant/components/climacell/translations/sensor.sk.json @@ -2,6 +2,17 @@ "state": { "climacell__health_concern": { "unhealthy": "Nezdrav\u00e9" + }, + "climacell__pollen_index": { + "low": "N\u00edzke", + "medium": "Stredn\u00e9", + "very_high": "Ve\u013emi vysok\u00e9", + "very_low": "Ve\u013emi n\u00edzke" + }, + "climacell__precipitation_type": { + "freezing_rain": "Mrzn\u00faci d\u00e1\u017e\u010f", + "rain": "D\u00e1\u017e\u010f", + "snow": "Sneh" } } } \ No newline at end of file diff --git a/homeassistant/components/coinbase/translations/sk.json b/homeassistant/components/coinbase/translations/sk.json index f161003be52..f2ffd76bc74 100644 --- a/homeassistant/components/coinbase/translations/sk.json +++ b/homeassistant/components/coinbase/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/control4/translations/sk.json b/homeassistant/components/control4/translations/sk.json index 788f778e13a..f6b92d16c96 100644 --- a/homeassistant/components/control4/translations/sk.json +++ b/homeassistant/components/control4/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, @@ -11,5 +14,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Sekundy medzi aktualiz\u00e1ciami" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/cover/translations/sk.json b/homeassistant/components/cover/translations/sk.json index 57379849b32..8962c6b8db3 100644 --- a/homeassistant/components/cover/translations/sk.json +++ b/homeassistant/components/cover/translations/sk.json @@ -1,4 +1,25 @@ { + "device_automation": { + "action_type": { + "close": "Zavrie\u0165 {entity_name}", + "open": "Otvori\u0165 {entity_name}", + "set_position": "Nastavi\u0165 poz\u00edciu {entity_name}", + "stop": "Zastavi\u0165 {entity_name}" + }, + "condition_type": { + "is_closed": "{entity_name} je zatvoren\u00e9", + "is_closing": "{entity_name} sa zatv\u00e1ra", + "is_open": "{entity_name} je otvoren\u00e1", + "is_opening": "{entity_name} sa otv\u00e1ra", + "is_position": "Aktu\u00e1lna poz\u00edcia {entity_name} je" + }, + "trigger_type": { + "closed": "{entity_name} zatvoren\u00e9", + "closing": "zatv\u00e1renie {entity_name}", + "opened": "{entity_name} otvoren\u00e9", + "position": "zmeny poz\u00edcie {entity_name}" + } + }, "state": { "_": { "closed": "Zatvoren\u00e9", diff --git a/homeassistant/components/deconz/translations/sk.json b/homeassistant/components/deconz/translations/sk.json index 6cbd7bd3752..8fb170e8bc7 100644 --- a/homeassistant/components/deconz/translations/sk.json +++ b/homeassistant/components/deconz/translations/sk.json @@ -12,5 +12,27 @@ } } } + }, + "device_automation": { + "trigger_subtype": { + "close": "Zavrie\u0165", + "dim_down": "Stlmi\u0165", + "dim_up": "Zv\u00fd\u0161i\u0165", + "left": "V\u013eavo", + "open": "Otvori\u0165", + "right": "Vpravo", + "side_1": "Strana 1", + "side_2": "Strana 2", + "side_3": "Strana 3", + "side_4": "Strana 4", + "side_5": "Strana 5", + "side_6": "Strana 6", + "turn_off": "Vypn\u00fa\u0165", + "turn_on": "Zapn\u00fa\u0165" + }, + "trigger_type": { + "remote_awakened": "Zariadenie sa prebudilo", + "remote_button_double_press": "dvojklik na tla\u010didlo \u201e{subtype}\u201c" + } } } \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/sk.json b/homeassistant/components/derivative/translations/sk.json index cc4a1cc7cb9..ac28b231df7 100644 --- a/homeassistant/components/derivative/translations/sk.json +++ b/homeassistant/components/derivative/translations/sk.json @@ -3,7 +3,8 @@ "step": { "user": { "data": { - "name": "Meno" + "name": "Meno", + "source": "Vstupn\u00fd sn\u00edma\u010d" } } } diff --git a/homeassistant/components/devolo_home_network/translations/el.json b/homeassistant/components/devolo_home_network/translations/el.json index 9fee61cd430..e78c38ea13e 100644 --- a/homeassistant/components/devolo_home_network/translations/el.json +++ b/homeassistant/components/devolo_home_network/translations/el.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", - "home_control": "\u0397 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03ae \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 Home \u03c4\u03b7\u03c2 devolo \u03b4\u03b5\u03bd \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03b5\u03af \u03bc\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7." + "home_control": "\u0397 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03ae \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 Home \u03c4\u03b7\u03c2 devolo \u03b4\u03b5\u03bd \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03b5\u03af \u03bc\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7.", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", diff --git a/homeassistant/components/devolo_home_network/translations/sk.json b/homeassistant/components/devolo_home_network/translations/sk.json index ffc0103d598..16c033491b2 100644 --- a/homeassistant/components/devolo_home_network/translations/sk.json +++ b/homeassistant/components/devolo_home_network/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{product} ({name})", "step": { "reauth_confirm": { diff --git a/homeassistant/components/dexcom/translations/sk.json b/homeassistant/components/dexcom/translations/sk.json index 1b1e671c054..5ac7d0818ba 100644 --- a/homeassistant/components/dexcom/translations/sk.json +++ b/homeassistant/components/dexcom/translations/sk.json @@ -10,5 +10,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "unit_of_measurement": "Jednotka merania" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/directv/translations/sk.json b/homeassistant/components/directv/translations/sk.json index aaad66e6729..59a11c7e4e2 100644 --- a/homeassistant/components/directv/translations/sk.json +++ b/homeassistant/components/directv/translations/sk.json @@ -1,7 +1,13 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name}", "step": { + "ssdp_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/dlna_dmr/translations/sk.json b/homeassistant/components/dlna_dmr/translations/sk.json index aaad66e6729..8ac7aa5d949 100644 --- a/homeassistant/components/dlna_dmr/translations/sk.json +++ b/homeassistant/components/dlna_dmr/translations/sk.json @@ -1,5 +1,10 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "alternative_integration": "Zariadenie je lep\u0161ie podporovan\u00e9 inou integr\u00e1ciou", + "non_unique_id": "Viacer\u00e9 zariadenia n\u00e1jden\u00e9 s rovnak\u00fdm jedine\u010dn\u00fdm identifik\u00e1torom" + }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/dlna_dms/translations/sk.json b/homeassistant/components/dlna_dms/translations/sk.json index aaad66e6729..8715062cf81 100644 --- a/homeassistant/components/dlna_dms/translations/sk.json +++ b/homeassistant/components/dlna_dms/translations/sk.json @@ -1,11 +1,16 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, "flow_title": "{name}", "step": { "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nakonfigurova\u0165" } } } diff --git a/homeassistant/components/doorbird/translations/sk.json b/homeassistant/components/doorbird/translations/sk.json index 8969a058800..4a4df7037bc 100644 --- a/homeassistant/components/doorbird/translations/sk.json +++ b/homeassistant/components/doorbird/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/dsmr/translations/sk.json b/homeassistant/components/dsmr/translations/sk.json index 35f2d7628c7..b43117f7056 100644 --- a/homeassistant/components/dsmr/translations/sk.json +++ b/homeassistant/components/dsmr/translations/sk.json @@ -1,5 +1,11 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "setup_network": { "data": { @@ -8,6 +14,9 @@ } }, "setup_serial": { + "data": { + "port": "Vyberte zariadenie" + }, "title": "Zariadenie" } } diff --git a/homeassistant/components/dunehd/translations/sk.json b/homeassistant/components/dunehd/translations/sk.json index 2fe193920d4..7d2ebc79c3c 100644 --- a/homeassistant/components/dunehd/translations/sk.json +++ b/homeassistant/components/dunehd/translations/sk.json @@ -1,6 +1,10 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, "step": { diff --git a/homeassistant/components/eafm/translations/sk.json b/homeassistant/components/eafm/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/eafm/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/econet/translations/sk.json b/homeassistant/components/econet/translations/sk.json index ccc424ecb47..f4894a1aed6 100644 --- a/homeassistant/components/econet/translations/sk.json +++ b/homeassistant/components/econet/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "invalid_auth": "Neplatn\u00e9 overenie" }, "error": { diff --git a/homeassistant/components/efergy/translations/sk.json b/homeassistant/components/efergy/translations/sk.json index 64731388e98..890673b969f 100644 --- a/homeassistant/components/efergy/translations/sk.json +++ b/homeassistant/components/efergy/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/eight_sleep/translations/sk.json b/homeassistant/components/eight_sleep/translations/sk.json index 66e8cd431a4..d1c130a108b 100644 --- a/homeassistant/components/eight_sleep/translations/sk.json +++ b/homeassistant/components/eight_sleep/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/elgato/translations/sk.json b/homeassistant/components/elgato/translations/sk.json index 33d93b62475..142f47cf1fd 100644 --- a/homeassistant/components/elgato/translations/sk.json +++ b/homeassistant/components/elgato/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/elmax/translations/sk.json b/homeassistant/components/elmax/translations/sk.json index 1b1e671c054..8f06d99549b 100644 --- a/homeassistant/components/elmax/translations/sk.json +++ b/homeassistant/components/elmax/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/emonitor/translations/sk.json b/homeassistant/components/emonitor/translations/sk.json index aaad66e6729..2f510ec91da 100644 --- a/homeassistant/components/emonitor/translations/sk.json +++ b/homeassistant/components/emonitor/translations/sk.json @@ -2,6 +2,9 @@ "config": { "flow_title": "{name}", "step": { + "confirm": { + "description": "Chcete nastavi\u0165 {name} ({host})?" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/escea/translations/sk.json b/homeassistant/components/escea/translations/sk.json new file mode 100644 index 00000000000..69d8d942494 --- /dev/null +++ b/homeassistant/components/escea/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fan/translations/sk.json b/homeassistant/components/fan/translations/sk.json index 1dc17560e34..e5343697ccb 100644 --- a/homeassistant/components/fan/translations/sk.json +++ b/homeassistant/components/fan/translations/sk.json @@ -1,4 +1,13 @@ { + "device_automation": { + "condition_type": { + "is_off": "{entity_name} je vypnut\u00e9", + "is_on": "{entity_name} je zapnut\u00e9" + }, + "trigger_type": { + "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9" + } + }, "state": { "_": { "off": "Neakt\u00edvny", diff --git a/homeassistant/components/fibaro/translations/sk.json b/homeassistant/components/fibaro/translations/sk.json index b374deb03c9..1a831c812f7 100644 --- a/homeassistant/components/fibaro/translations/sk.json +++ b/homeassistant/components/fibaro/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "reauth_confirm": { "data": { diff --git a/homeassistant/components/fjaraskupan/translations/sk.json b/homeassistant/components/fjaraskupan/translations/sk.json new file mode 100644 index 00000000000..69d8d942494 --- /dev/null +++ b/homeassistant/components/fjaraskupan/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/flipr/translations/sk.json b/homeassistant/components/flipr/translations/sk.json index 87352b48fad..bfca61034ec 100644 --- a/homeassistant/components/flipr/translations/sk.json +++ b/homeassistant/components/flipr/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/flo/translations/sk.json b/homeassistant/components/flo/translations/sk.json index 08d8e8f6516..254174d26ab 100644 --- a/homeassistant/components/flo/translations/sk.json +++ b/homeassistant/components/flo/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/flux_led/translations/sk.json b/homeassistant/components/flux_led/translations/sk.json index 9b4cff3cb69..23d4e4927f4 100644 --- a/homeassistant/components/flux_led/translations/sk.json +++ b/homeassistant/components/flux_led/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "abort": { - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, "flow_title": "{model} {id} ({ipaddr})", "step": { diff --git a/homeassistant/components/forked_daapd/translations/sk.json b/homeassistant/components/forked_daapd/translations/sk.json index 3ac40922981..ac802e99cb9 100644 --- a/homeassistant/components/forked_daapd/translations/sk.json +++ b/homeassistant/components/forked_daapd/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "wrong_password": "Nespr\u00e1vne heslo." }, diff --git a/homeassistant/components/foscam/translations/sk.json b/homeassistant/components/foscam/translations/sk.json index db9ec47e7c7..e97579ab1d0 100644 --- a/homeassistant/components/foscam/translations/sk.json +++ b/homeassistant/components/foscam/translations/sk.json @@ -4,7 +4,8 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_response": "Neplatn\u00e1 odpove\u010f zo zariadenia" }, "step": { "user": { diff --git a/homeassistant/components/freebox/translations/sk.json b/homeassistant/components/freebox/translations/sk.json index 33d93b62475..142f47cf1fd 100644 --- a/homeassistant/components/freebox/translations/sk.json +++ b/homeassistant/components/freebox/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/freedompro/translations/sk.json b/homeassistant/components/freedompro/translations/sk.json index ff853127803..f5f4789c7fe 100644 --- a/homeassistant/components/freedompro/translations/sk.json +++ b/homeassistant/components/freedompro/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/fritz/translations/sk.json b/homeassistant/components/fritz/translations/sk.json index a379d26084b..75d1a5bf763 100644 --- a/homeassistant/components/fritz/translations/sk.json +++ b/homeassistant/components/fritz/translations/sk.json @@ -1,10 +1,12 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/fritzbox/translations/sk.json b/homeassistant/components/fritzbox/translations/sk.json index 926cf579a43..bc1a0e4b469 100644 --- a/homeassistant/components/fritzbox/translations/sk.json +++ b/homeassistant/components/fritzbox/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -12,12 +14,14 @@ "confirm": { "data": { "password": "Heslo" - } + }, + "description": "Chcete nastavi\u0165 {name}?" }, "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Aktualizujte svoje prihlasovacie \u00fadaje pre {name}." }, "user": { "data": { diff --git a/homeassistant/components/fritzbox_callmonitor/translations/sk.json b/homeassistant/components/fritzbox_callmonitor/translations/sk.json index 3ae2562c19e..db18895a108 100644 --- a/homeassistant/components/fritzbox_callmonitor/translations/sk.json +++ b/homeassistant/components/fritzbox_callmonitor/translations/sk.json @@ -1,5 +1,9 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/fronius/translations/sk.json b/homeassistant/components/fronius/translations/sk.json index e583414047d..4a0d1d21e7d 100644 --- a/homeassistant/components/fronius/translations/sk.json +++ b/homeassistant/components/fronius/translations/sk.json @@ -1,9 +1,14 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, + "flow_title": "{device}", "step": { + "confirm_discovery": { + "description": "Chcete do Home Assistant-a prida\u0165 {device}?" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/garages_amsterdam/translations/sk.json b/homeassistant/components/garages_amsterdam/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/garages_amsterdam/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/el.json b/homeassistant/components/generic/translations/el.json index eda806cc137..b3480bb85be 100644 --- a/homeassistant/components/generic/translations/el.json +++ b/homeassistant/components/generic/translations/el.json @@ -75,6 +75,13 @@ "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "step": { + "confirm_still": { + "data": { + "confirmed_ok": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1 \u03c6\u03b1\u03af\u03bd\u03b5\u03c4\u03b1\u03b9 \u03ba\u03b1\u03bb\u03ae." + }, + "description": "! [\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03c3\u03ba\u03cc\u03c0\u03b7\u03c3\u03b7 \u03c3\u03c4\u03b1\u03c4\u03b9\u03ba\u03ae\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 \u03ba\u03ac\u03bc\u03b5\u03c1\u03b1\u03c2] ({preview_url})", + "title": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03c3\u03ba\u03cc\u03c0\u03b7\u03c3\u03b7" + }, "content_type": { "data": { "content_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5" diff --git a/homeassistant/components/generic/translations/sk.json b/homeassistant/components/generic/translations/sk.json index 9ff8487afaa..dcb28c2222e 100644 --- a/homeassistant/components/generic/translations/sk.json +++ b/homeassistant/components/generic/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/glances/translations/sk.json b/homeassistant/components/glances/translations/sk.json index e6a451b5ba5..fcb9ef6d04f 100644 --- a/homeassistant/components/glances/translations/sk.json +++ b/homeassistant/components/glances/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { @@ -10,5 +13,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Frekvencia aktualiz\u00e1cie" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/goalzero/translations/sk.json b/homeassistant/components/goalzero/translations/sk.json index a7ffce559f2..27a7d78c377 100644 --- a/homeassistant/components/goalzero/translations/sk.json +++ b/homeassistant/components/goalzero/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, "error": { diff --git a/homeassistant/components/gogogate2/translations/sk.json b/homeassistant/components/gogogate2/translations/sk.json index 1b1e671c054..9a6b8022c06 100644 --- a/homeassistant/components/gogogate2/translations/sk.json +++ b/homeassistant/components/gogogate2/translations/sk.json @@ -3,6 +3,7 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{device} ({ip_address})", "step": { "user": { "data": { diff --git a/homeassistant/components/goodwe/translations/sk.json b/homeassistant/components/goodwe/translations/sk.json index a245098293d..ca49bb2fe7b 100644 --- a/homeassistant/components/goodwe/translations/sk.json +++ b/homeassistant/components/goodwe/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "step": { diff --git a/homeassistant/components/google/translations/sk.json b/homeassistant/components/google/translations/sk.json new file mode 100644 index 00000000000..c0763b92aef --- /dev/null +++ b/homeassistant/components/google/translations/sk.json @@ -0,0 +1,11 @@ +{ + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Pr\u00edstup Home Assistant ku kalend\u00e1ru Google" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_travel_time/translations/el.json b/homeassistant/components/google_travel_time/translations/el.json index 3385eb26b48..c7e821b6b39 100644 --- a/homeassistant/components/google_travel_time/translations/el.json +++ b/homeassistant/components/google_travel_time/translations/el.json @@ -4,7 +4,8 @@ "already_configured": "\u0397 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" }, "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" }, "step": { "user": { diff --git a/homeassistant/components/govee_ble/translations/sk.json b/homeassistant/components/govee_ble/translations/sk.json new file mode 100644 index 00000000000..8444b9fbeae --- /dev/null +++ b/homeassistant/components/govee_ble/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/gree/translations/sk.json b/homeassistant/components/gree/translations/sk.json new file mode 100644 index 00000000000..69d8d942494 --- /dev/null +++ b/homeassistant/components/gree/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/group/translations/sk.json b/homeassistant/components/group/translations/sk.json index 51759a9dc86..8668e59847d 100644 --- a/homeassistant/components/group/translations/sk.json +++ b/homeassistant/components/group/translations/sk.json @@ -1,17 +1,83 @@ { "config": { "step": { + "binary_sensor": { + "data": { + "all": "V\u0161etky entity", + "entities": "\u010clenovia", + "name": "N\u00e1zov" + } + }, + "cover": { + "data": { + "entities": "\u010clenovia", + "name": "N\u00e1zov" + } + }, + "fan": { + "data": { + "entities": "\u010clenovia", + "name": "N\u00e1zov" + } + }, + "light": { + "data": { + "entities": "\u010clenovia", + "name": "N\u00e1zov" + } + }, + "lock": { + "data": { + "entities": "\u010clenovia", + "name": "N\u00e1zov" + } + }, "media_player": { "data": { + "entities": "\u010clenovia", "name": "Meno" } + }, + "switch": { + "data": { + "entities": "\u010clenovia", + "name": "N\u00e1zov" + } } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "V\u0161etky entity", + "entities": "\u010clenovia" + } + }, + "cover": { + "data": { + "entities": "\u010clenovia" + } + }, + "fan": { + "data": { + "entities": "\u010clenovia" + } + }, "light": { "data": { + "all": "V\u0161etky entity", + "entities": "\u010clenovia" + } + }, + "media_player": { + "data": { + "entities": "\u010clenovia" + } + }, + "switch": { + "data": { + "all": "V\u0161etky entity", "entities": "\u010clenovia" } } diff --git a/homeassistant/components/guardian/translations/sk.json b/homeassistant/components/guardian/translations/sk.json index b41d6edbd4b..99d87189f7b 100644 --- a/homeassistant/components/guardian/translations/sk.json +++ b/homeassistant/components/guardian/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "step": { diff --git a/homeassistant/components/harmony/translations/sk.json b/homeassistant/components/harmony/translations/sk.json index aaad66e6729..427503789ce 100644 --- a/homeassistant/components/harmony/translations/sk.json +++ b/homeassistant/components/harmony/translations/sk.json @@ -2,6 +2,9 @@ "config": { "flow_title": "{name}", "step": { + "link": { + "description": "Chcete nastavi\u0165 {name} ({host})?" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/here_travel_time/translations/sk.json b/homeassistant/components/here_travel_time/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/de.json b/homeassistant/components/homeassistant_yellow/translations/de.json index aae6bed9fa5..92f3438e2dc 100644 --- a/homeassistant/components/homeassistant_yellow/translations/de.json +++ b/homeassistant/components/homeassistant_yellow/translations/de.json @@ -5,7 +5,8 @@ "addon_install_failed": "Die Installation des Silicon Labs Multiprotokoll-Add-Ons ist fehlgeschlagen.", "addon_set_config_failed": "Die Silicon Labs Multiprotokoll-Konfiguration konnte nicht eingestellt werden.", "addon_start_failed": "Das Silicon Labs Multiprotokoll-Add-On konnte nicht gestartet werden.", - "not_hassio": "Die Hardwareoptionen k\u00f6nnen nur auf HassOS-Installationen konfiguriert werden." + "not_hassio": "Die Hardwareoptionen k\u00f6nnen nur auf HassOS-Installationen konfiguriert werden.", + "zha_migration_failed": "Die ZHA Migration war nicht erfolgreich." }, "error": { "unknown": "Unerwarteter Fehler" @@ -22,7 +23,7 @@ "data": { "enable_multi_pan": "Multiprotokollunterst\u00fctzung aktivieren" }, - "description": "Wenn die Multiprotokoll-Unterst\u00fctzung aktiviert ist, kann das IEEE 802.15.4-Funkger\u00e4t des Home Assistant Yellow sowohl f\u00fcr Zigbee als auch f\u00fcr Thread (von Matter verwendet) gleichzeitig verwendet werden. Hinweis: Dies ist eine experimentelle Funktion.", + "description": "Wenn die Multiprotokoll-Unterst\u00fctzung aktiviert ist, kann das IEEE 802.15.4-Radio des Home Assistant Yellow gleichzeitig f\u00fcr Zigbee und Thread (von Matter verwendet) verwendet werden. Wenn das Funkger\u00e4t bereits von der ZHA-Zigbee-Integration verwendet wird, wird ZHA neu konfiguriert, um die Multiprotokoll-Firmware zu verwenden. \n\nHinweis: Dies ist eine experimentelle Funktion.", "title": "Aktiviere die Multiprotokollunterst\u00fctzung auf dem IEEE 802.15.4-Funkger\u00e4t" }, "install_addon": { diff --git a/homeassistant/components/homeassistant_yellow/translations/el.json b/homeassistant/components/homeassistant_yellow/translations/el.json index 71f89914d28..8652b4e8a98 100644 --- a/homeassistant/components/homeassistant_yellow/translations/el.json +++ b/homeassistant/components/homeassistant_yellow/translations/el.json @@ -5,7 +5,8 @@ "addon_install_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", "addon_set_config_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd Silicon Labs.", "addon_start_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", - "not_hassio": "\u039f\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c5\u03bb\u03b9\u03ba\u03bf\u03cd \u03bc\u03c0\u03bf\u03c1\u03bf\u03cd\u03bd \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03bf\u03cd\u03bd \u03bc\u03cc\u03bd\u03bf \u03c3\u03b5 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 HassOS." + "not_hassio": "\u039f\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c5\u03bb\u03b9\u03ba\u03bf\u03cd \u03bc\u03c0\u03bf\u03c1\u03bf\u03cd\u03bd \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03bf\u03cd\u03bd \u03bc\u03cc\u03bd\u03bf \u03c3\u03b5 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 HassOS.", + "zha_migration_failed": "\u0397 \u03bc\u03b5\u03c4\u03ac\u03b2\u03b1\u03c3\u03b7 ZHA \u03b4\u03b5\u03bd \u03c0\u03ad\u03c4\u03c5\u03c7\u03b5." }, "error": { "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" diff --git a/homeassistant/components/homeassistant_yellow/translations/es.json b/homeassistant/components/homeassistant_yellow/translations/es.json index c745e933c0e..d93a329efd6 100644 --- a/homeassistant/components/homeassistant_yellow/translations/es.json +++ b/homeassistant/components/homeassistant_yellow/translations/es.json @@ -5,7 +5,8 @@ "addon_install_failed": "No se pudo instalar el complemento Silicon Labs Multiprotocol.", "addon_set_config_failed": "No se pudo establecer la configuraci\u00f3n de Silicon Labs Multiprotocol.", "addon_start_failed": "No se pudo iniciar el complemento Silicon Labs Multiprotocol.", - "not_hassio": "Las opciones de hardware solo se pueden configurar en instalaciones de HassOS." + "not_hassio": "Las opciones de hardware solo se pueden configurar en instalaciones de HassOS.", + "zha_migration_failed": "La migraci\u00f3n de ZHA no tuvo \u00e9xito." }, "error": { "unknown": "Error inesperado" @@ -22,7 +23,7 @@ "data": { "enable_multi_pan": "Habilitar soporte multiprotocolo" }, - "description": "Cuando el soporte multiprotocolo est\u00e1 habilitado, la radio IEEE 802.15.4 de Home Assistant Yellow se puede usar tanto para Zigbee como para Thread (usado por Matter) al mismo tiempo. Nota: Esta es una caracter\u00edstica experimental.", + "description": "Cuando el soporte multiprotocolo est\u00e1 habilitado, la radio IEEE 802.15.4 de Home Assistant Yellow se puede usar tanto para Zigbee como para Thread (usado por Matter) al mismo tiempo. Si la integraci\u00f3n ZHA Zigbee ya usa la radio, ZHA se volver\u00e1 a configurar para usar el firmware multiprotocolo. \n\nNota: Esta es una caracter\u00edstica experimental.", "title": "Habilitar el soporte multiprotocolo en la radio IEEE 802.15.4" }, "install_addon": { diff --git a/homeassistant/components/homeassistant_yellow/translations/et.json b/homeassistant/components/homeassistant_yellow/translations/et.json index d0d1709b99e..b1ecacaf12e 100644 --- a/homeassistant/components/homeassistant_yellow/translations/et.json +++ b/homeassistant/components/homeassistant_yellow/translations/et.json @@ -5,7 +5,8 @@ "addon_install_failed": "Silicon Labs Multiprotocol add-on'i paigaldamine eba\u00f5nnestus.", "addon_set_config_failed": "Silicon Labs Multiprotocol konfiguratsiooni seadistamine eba\u00f5nnestus.", "addon_start_failed": "Silicon Labsi mitmeprotokolli lisandmooduli k\u00e4ivitamine nurjus.", - "not_hassio": "Riistvaravalikuid saab konfigureerida ainult HassOS-i paigaldustes." + "not_hassio": "Riistvaravalikuid saab konfigureerida ainult HassOS-i paigaldustes.", + "zha_migration_failed": "ZHA r\u00e4nne nurjus." }, "error": { "unknown": "Ootamatu t\u00f5rge" @@ -22,7 +23,7 @@ "data": { "enable_multi_pan": "Luba multiprotokolli tugi" }, - "description": "Kui multiprotokollide tugi on lubatud, saab Home Assistant Yellow'i IEEE 802.15.4 raadiot kasutada samaaegselt nii Zigbee kui ka Threadi (mida kasutab Matter) jaoks. M\u00e4rkus: See on eksperimentaalne funktsioon.", + "description": "Kui mitme protokolli tugi on lubatud, saab Home Assistant Yellowi IEEE 802.15.4 raadiot kasutada samaaegselt nii Zigbee kui ka Threadi jaoks (kasutab Matter). Kui ZHA Zigbee integratsioon juba kasutab raadiot, konfigureeritakse ZHA \u00fcmber mitmeprotokollilise p\u00fcsivara kasutamiseks. \n\n M\u00e4rkus. See on eksperimentaalne funktsioon.", "title": "Multiprotokollide toe lubamine IEEE 802.15.4 raadiosides" }, "install_addon": { diff --git a/homeassistant/components/homeassistant_yellow/translations/fr.json b/homeassistant/components/homeassistant_yellow/translations/fr.json index 6297713a99a..8e0da06078f 100644 --- a/homeassistant/components/homeassistant_yellow/translations/fr.json +++ b/homeassistant/components/homeassistant_yellow/translations/fr.json @@ -1,5 +1,8 @@ { "options": { + "error": { + "unknown": "Erreur inattendue" + }, "step": { "install_addon": { "title": "L'installation du module compl\u00e9mentaire multi-protocoles de Silicon Labs a commenc\u00e9." diff --git a/homeassistant/components/homeassistant_yellow/translations/id.json b/homeassistant/components/homeassistant_yellow/translations/id.json index 6c7dd05faec..cf100f4f07c 100644 --- a/homeassistant/components/homeassistant_yellow/translations/id.json +++ b/homeassistant/components/homeassistant_yellow/translations/id.json @@ -5,7 +5,8 @@ "addon_install_failed": "Gagal menginstal add-on Silicon Labs Multiprotocol.", "addon_set_config_failed": "Gagal mengatur konfigurasi Silicon Labs Multiprotocol.", "addon_start_failed": "Gagal memulai add-on Silicon Labs Multiprotocol.", - "not_hassio": "Opsi perangkat keras hanya bisa dikonfigurasi pada instalasi HassOS." + "not_hassio": "Opsi perangkat keras hanya bisa dikonfigurasi pada instalasi HassOS.", + "zha_migration_failed": "Migrasi ZHA tidak berhasil." }, "error": { "unknown": "Kesalahan yang tidak diharapkan" diff --git a/homeassistant/components/homeassistant_yellow/translations/ru.json b/homeassistant/components/homeassistant_yellow/translations/ru.json index 375c16ec566..f1ae0759e15 100644 --- a/homeassistant/components/homeassistant_yellow/translations/ru.json +++ b/homeassistant/components/homeassistant_yellow/translations/ru.json @@ -5,7 +5,8 @@ "addon_install_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".", "addon_set_config_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\".", "addon_start_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".", - "not_hassio": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445 HassOS." + "not_hassio": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445 HassOS.", + "zha_migration_failed": "\u041c\u0438\u0433\u0440\u0430\u0446\u0438\u044f ZHA \u043d\u0435 \u0443\u0434\u0430\u043b\u0430\u0441\u044c." }, "error": { "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." @@ -22,7 +23,7 @@ "data": { "enable_multi_pan": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432" }, - "description": "\u041a\u043e\u0433\u0434\u0430 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432, \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c Home Assistant Yellow \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 IEEE 802.15.4 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043a\u0430\u043a \u0434\u043b\u044f Zigbee, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f Thread (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 Matter). \u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435: \u044d\u0442\u043e \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f.", + "description": "\u041a\u043e\u0433\u0434\u0430 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432, \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c Home Assistant Yellow \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 IEEE 802.15.4 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043a\u0430\u043a \u0434\u043b\u044f Zigbee, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f Thread (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 Matter). \u0415\u0441\u043b\u0438 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0432 ZHA \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 Zigbee, ZHA \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432.\n\n\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435: \u044d\u0442\u043e \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f.", "title": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432 \u043d\u0430 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u0435 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 IEEE 802.15.4." }, "install_addon": { diff --git a/homeassistant/components/homekit_controller/translations/de.json b/homeassistant/components/homekit_controller/translations/de.json index 2b61efe6892..687d6702715 100644 --- a/homeassistant/components/homekit_controller/translations/de.json +++ b/homeassistant/components/homekit_controller/translations/de.json @@ -14,7 +14,7 @@ "authentication_error": "Ung\u00fcltiger HomeKit Code, \u00fcberpr\u00fcfe bitte den Code und versuche es erneut.", "insecure_setup_code": "Der angeforderte Setup-Code ist unsicher, da er zu trivial ist. Dieses Zubeh\u00f6r erf\u00fcllt nicht die grundlegenden Sicherheitsanforderungen.", "max_peers_error": "Das Ger\u00e4t weigerte sich, die Kopplung durchzuf\u00fchren, da es keinen freien Kopplungs-Speicher hat.", - "pairing_failed": "Beim Versuch dieses Ger\u00e4t zu koppeln ist ein Fehler aufgetreten. Dies kann ein vor\u00fcbergehender Fehler sein oder das Ger\u00e4t wird derzeit m\u00f6glicherweise nicht unterst\u00fctzt.", + "pairing_failed": "Beim Koppeln mit diesem Ger\u00e4t ist ein nicht behandelter Fehler aufgetreten. Dies kann ein vor\u00fcbergehender Fehler sein oder das oder Ihr Ger\u00e4t wird derzeit nicht unterst\u00fctzt: {error}", "unable_to_pair": "Koppeln fehltgeschlagen, bitte versuche es erneut", "unknown_error": "Das Ger\u00e4t meldete einen unbekannten Fehler. Die Kopplung ist fehlgeschlagen." }, diff --git a/homeassistant/components/homekit_controller/translations/es.json b/homeassistant/components/homekit_controller/translations/es.json index a43373cd399..8e3eb44475b 100644 --- a/homeassistant/components/homekit_controller/translations/es.json +++ b/homeassistant/components/homekit_controller/translations/es.json @@ -14,7 +14,7 @@ "authentication_error": "C\u00f3digo de HomeKit incorrecto. Por favor, compru\u00e9balo e int\u00e9ntalo de nuevo.", "insecure_setup_code": "El c\u00f3digo de configuraci\u00f3n solicitado no es seguro debido a su naturaleza trivial. Este accesorio no cumple con los requisitos b\u00e1sicos de seguridad.", "max_peers_error": "El dispositivo se neg\u00f3 a a\u00f1adir el emparejamiento porque no tiene almacenamiento disponible para emparejamientos.", - "pairing_failed": "Se produjo un error no controlado al intentar emparejar con este dispositivo. Esto puede ser un fallo temporal o que tu dispositivo no sea compatible por el momento.", + "pairing_failed": "Se produjo un error no controlado al intentar emparejar con este dispositivo. Esto puede ser un fallo temporal o es posible que tu dispositivo no sea compatible actualmente: {error}", "unable_to_pair": "No se puede emparejar, por favor, int\u00e9ntalo de nuevo.", "unknown_error": "El dispositivo report\u00f3 un error desconocido. El emparejamiento ha fallado." }, diff --git a/homeassistant/components/homekit_controller/translations/et.json b/homeassistant/components/homekit_controller/translations/et.json index 8db1df29803..1cefd89db57 100644 --- a/homeassistant/components/homekit_controller/translations/et.json +++ b/homeassistant/components/homekit_controller/translations/et.json @@ -14,7 +14,7 @@ "authentication_error": "Vale HomeKiti kood. Kontrolli seda ja proovi uuesti.", "insecure_setup_code": "Taotletud salas\u00f5na on ebaturvaline, sest see on liiga lihtne ning ei vasta p\u00f5hilistele turvan\u00f5uetele.", "max_peers_error": "Seade keeldus sidumist lisamast kuna puudub piisav salvestusruum.", - "pairing_failed": "Selle seadmega sidumise katsel ilmnes tundmatu t\u00f5rge. See v\u00f5ib olla ajutine t\u00f5rge v\u00f5i seadet ei toetata praegu.", + "pairing_failed": "Selle seadmega sidumise katsel ilmnes ootamatu t\u00f5rge. See v\u00f5ib olla ajutine rike v\u00f5i seadet ei toetata praegu: {error}", "unable_to_pair": "Ei saa siduda, proovi uuesti.", "unknown_error": "Seade teatas tundmatust t\u00f5rkest. Sidumine nurjus." }, diff --git a/homeassistant/components/homekit_controller/translations/ru.json b/homeassistant/components/homekit_controller/translations/ru.json index 00a4d9fcb7c..0e7c4f94628 100644 --- a/homeassistant/components/homekit_controller/translations/ru.json +++ b/homeassistant/components/homekit_controller/translations/ru.json @@ -14,7 +14,7 @@ "authentication_error": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 HomeKit. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043a\u043e\u0434 \u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437.", "insecure_setup_code": "\u0417\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0435\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u0435\u043d \u0438\u0437-\u0437\u0430 \u0441\u0432\u043e\u0435\u0439 \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438. \u042d\u0442\u043e\u0442 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440 \u043d\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f\u043c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438.", "max_peers_error": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043e\u0442\u043a\u043b\u043e\u043d\u0438\u043b\u043e \u0441\u043e\u043f\u0440\u044f\u0436\u0435\u043d\u0438\u0435 \u0438\u0437-\u0437\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044f \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u0430.", - "pairing_failed": "\u0412\u043e \u0432\u0440\u0435\u043c\u044f \u0441\u043e\u043f\u0440\u044f\u0436\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0441\u0431\u043e\u0439 \u0438\u043b\u0438 \u0412\u0430\u0448\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0435\u0449\u0435 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f.", + "pairing_failed": "\u0412\u043e \u0432\u0440\u0435\u043c\u044f \u0441\u043e\u043f\u0440\u044f\u0436\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0441\u0431\u043e\u0439 \u0438\u043b\u0438 \u0412\u0430\u0448\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0435\u0449\u0435 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f: {error}", "unable_to_pair": "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0441\u043e\u043f\u0440\u044f\u0436\u0435\u043d\u0438\u0435. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437.", "unknown_error": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0441\u043e\u043e\u0431\u0449\u0438\u043b\u043e \u043e \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0435. \u0421\u043e\u043f\u0440\u044f\u0436\u0435\u043d\u0438\u0435 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c." }, diff --git a/homeassistant/components/homekit_controller/translations/sensor.sk.json b/homeassistant/components/homekit_controller/translations/sensor.sk.json new file mode 100644 index 00000000000..145119e835b --- /dev/null +++ b/homeassistant/components/homekit_controller/translations/sensor.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "homekit_controller__thread_status": { + "router": "Router" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/sk.json b/homeassistant/components/homekit_controller/translations/sk.json index ccf13684b9a..84988df072f 100644 --- a/homeassistant/components/homekit_controller/translations/sk.json +++ b/homeassistant/components/homekit_controller/translations/sk.json @@ -12,5 +12,25 @@ "title": "V\u00fdber zariadenia" } } + }, + "device_automation": { + "trigger_subtype": { + "button1": "Tla\u010didlo 1", + "button10": "Tla\u010didlo 10", + "button2": "Tla\u010didlo 2", + "button3": "Tla\u010didlo 3", + "button4": "Tla\u010didlo 4", + "button5": "Tla\u010didlo 5", + "button6": "Tla\u010didlo 6", + "button7": "Tla\u010didlo 7", + "button8": "Tla\u010didlo 8", + "button9": "Tla\u010didlo 9", + "doorbell": "Domov\u00fd zvon\u010dek" + }, + "trigger_type": { + "double_press": "\"{subtype}\" stla\u010den\u00e9 dvakr\u00e1t", + "long_press": "\"{subtype}\" stla\u010den\u00e9 a podr\u017ean\u00e9", + "single_press": "\"{subtype}\" stla\u010den\u00e9" + } } } \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/translations/sk.json b/homeassistant/components/homematicip_cloud/translations/sk.json index 48638e08787..47aa296310a 100644 --- a/homeassistant/components/homematicip_cloud/translations/sk.json +++ b/homeassistant/components/homematicip_cloud/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/homewizard/translations/sk.json b/homeassistant/components/homewizard/translations/sk.json new file mode 100644 index 00000000000..0a8865e3b27 --- /dev/null +++ b/homeassistant/components/homewizard/translations/sk.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "device_not_supported": "Toto zariadenie nie je podporovan\u00e9" + }, + "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {product_type} ({serial}) na {ip_address}?" + }, + "user": { + "title": "Konfigur\u00e1cia zariadenia" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/huawei_lte/translations/sk.json b/homeassistant/components/huawei_lte/translations/sk.json index 959a2866eba..27394040ea5 100644 --- a/homeassistant/components/huawei_lte/translations/sk.json +++ b/homeassistant/components/huawei_lte/translations/sk.json @@ -2,7 +2,8 @@ "config": { "error": { "incorrect_password": "Nespr\u00e1vne heslo", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "response_error": "Nezn\u00e1ma chyba zo zariadenia" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/hue/translations/sk.json b/homeassistant/components/hue/translations/sk.json index 496de2d99b2..cc98dc0c8e8 100644 --- a/homeassistant/components/hue/translations/sk.json +++ b/homeassistant/components/hue/translations/sk.json @@ -32,6 +32,11 @@ "device_automation": { "trigger_subtype": { "1": "Prv\u00e9 tla\u010didlo" + }, + "trigger_type": { + "remote_button_long_release": "\"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed", + "remote_button_short_press": "\"{subtype}\" stla\u010den\u00e9", + "remote_button_short_release": "\u201c{subtype}\u201c uvo\u013enen\u00e9" } } } \ No newline at end of file diff --git a/homeassistant/components/huisbaasje/translations/sk.json b/homeassistant/components/huisbaasje/translations/sk.json index 1b1e671c054..8f06d99549b 100644 --- a/homeassistant/components/huisbaasje/translations/sk.json +++ b/homeassistant/components/huisbaasje/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/humidifier/translations/sk.json b/homeassistant/components/humidifier/translations/sk.json new file mode 100644 index 00000000000..3734130c308 --- /dev/null +++ b/homeassistant/components/humidifier/translations/sk.json @@ -0,0 +1,10 @@ +{ + "device_automation": { + "action_type": { + "set_humidity": "Nastavi\u0165 vlhkos\u0165 pre {entity_name}" + }, + "trigger_type": { + "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hunterdouglas_powerview/translations/sk.json b/homeassistant/components/hunterdouglas_powerview/translations/sk.json index 8784ee07ad5..c1f2a447006 100644 --- a/homeassistant/components/hunterdouglas_powerview/translations/sk.json +++ b/homeassistant/components/hunterdouglas_powerview/translations/sk.json @@ -1,7 +1,13 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name} ({host})", "step": { + "link": { + "description": "Chcete nastavi\u0165 {name} ({host})?" + }, "user": { "data": { "host": "IP adresa" diff --git a/homeassistant/components/hvv_departures/translations/sk.json b/homeassistant/components/hvv_departures/translations/sk.json index 08d8e8f6516..254174d26ab 100644 --- a/homeassistant/components/hvv_departures/translations/sk.json +++ b/homeassistant/components/hvv_departures/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/ialarm/translations/sk.json b/homeassistant/components/ialarm/translations/sk.json index 33d93b62475..142f47cf1fd 100644 --- a/homeassistant/components/ialarm/translations/sk.json +++ b/homeassistant/components/ialarm/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/icloud/translations/sk.json b/homeassistant/components/icloud/translations/sk.json index 9c21cacd361..af66dbba08d 100644 --- a/homeassistant/components/icloud/translations/sk.json +++ b/homeassistant/components/icloud/translations/sk.json @@ -17,6 +17,13 @@ "password": "Heslo" } }, + "trusted_device": { + "data": { + "trusted_device": "D\u00f4veryhodn\u00e9 zariadenie" + }, + "description": "Vyberte svoje d\u00f4veryhodn\u00e9 zariadenie", + "title": "d\u00f4veryhodn\u00e9 zariadenie iCloud" + }, "user": { "data": { "password": "Heslo", diff --git a/homeassistant/components/inkbird/translations/sk.json b/homeassistant/components/inkbird/translations/sk.json new file mode 100644 index 00000000000..8444b9fbeae --- /dev/null +++ b/homeassistant/components/inkbird/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/insteon/translations/sk.json b/homeassistant/components/insteon/translations/sk.json index 1c992e2f792..a7f1d45f5d4 100644 --- a/homeassistant/components/insteon/translations/sk.json +++ b/homeassistant/components/insteon/translations/sk.json @@ -5,6 +5,9 @@ }, "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "Chcete nastavi\u0165 {name}?" + }, "hubv1": { "data": { "host": "IP adresa", diff --git a/homeassistant/components/integration/translations/sk.json b/homeassistant/components/integration/translations/sk.json index c1372b00a8a..c051b1c5d0d 100644 --- a/homeassistant/components/integration/translations/sk.json +++ b/homeassistant/components/integration/translations/sk.json @@ -3,7 +3,8 @@ "step": { "user": { "data": { - "round": "Presnos\u0165" + "round": "Presnos\u0165", + "source": "Vstupn\u00fd sn\u00edma\u010d" } } } diff --git a/homeassistant/components/intellifire/translations/sk.json b/homeassistant/components/intellifire/translations/sk.json index c049c1433b3..c1e58930394 100644 --- a/homeassistant/components/intellifire/translations/sk.json +++ b/homeassistant/components/intellifire/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{serial} ({host})", "step": { "api_config": { @@ -7,10 +10,20 @@ "password": "Heslo" } }, + "dhcp_confirm": { + "description": "Chcete nastavi\u0165 {host}\nSerial: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "Hostite\u013e (IP adresa)" + }, + "description": "Lok\u00e1lna konfigur\u00e1cia" + }, "pick_device": { "data": { "host": "Hostite\u013e" - } + }, + "title": "V\u00fdber zariadenia" } } } diff --git a/homeassistant/components/ipp/translations/sk.json b/homeassistant/components/ipp/translations/sk.json index a28e43a198f..10e176c9797 100644 --- a/homeassistant/components/ipp/translations/sk.json +++ b/homeassistant/components/ipp/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name}", "step": { "user": { @@ -7,6 +10,9 @@ "host": "Hostite\u013e", "port": "Port" } + }, + "zeroconf_confirm": { + "description": "Chcete nastavi\u0165 {name}?" } } } diff --git a/homeassistant/components/isy994/translations/sk.json b/homeassistant/components/isy994/translations/sk.json index 80987bdbc31..cdd7aaba085 100644 --- a/homeassistant/components/isy994/translations/sk.json +++ b/homeassistant/components/isy994/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, @@ -8,7 +11,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Poverenia pre {host} u\u017e nie s\u00fa platn\u00e9." }, "user": { "data": { diff --git a/homeassistant/components/izone/translations/sk.json b/homeassistant/components/izone/translations/sk.json new file mode 100644 index 00000000000..69d8d942494 --- /dev/null +++ b/homeassistant/components/izone/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/justnimbus/translations/sk.json b/homeassistant/components/justnimbus/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/justnimbus/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/kaleidescape/translations/sk.json b/homeassistant/components/kaleidescape/translations/sk.json index f9049e5041a..5799d863b27 100644 --- a/homeassistant/components/kaleidescape/translations/sk.json +++ b/homeassistant/components/kaleidescape/translations/sk.json @@ -1,5 +1,12 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "unsupported": "Nepodporovan\u00e9 zariadenie" + }, + "error": { + "unsupported": "Nepodporovan\u00e9 zariadenie" + }, "flow_title": "{model} ({name})", "step": { "user": { diff --git a/homeassistant/components/keymitt_ble/translations/sk.json b/homeassistant/components/keymitt_ble/translations/sk.json index edfb613ff7d..2db5c07a5ea 100644 --- a/homeassistant/components/keymitt_ble/translations/sk.json +++ b/homeassistant/components/keymitt_ble/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_unconfigured_devices": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia." }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/kmtronic/translations/sk.json b/homeassistant/components/kmtronic/translations/sk.json index 08d8e8f6516..254174d26ab 100644 --- a/homeassistant/components/kmtronic/translations/sk.json +++ b/homeassistant/components/kmtronic/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/knx/translations/el.json b/homeassistant/components/knx/translations/el.json index 2e27c415cd9..a0753789ca9 100644 --- a/homeassistant/components/knx/translations/el.json +++ b/homeassistant/components/knx/translations/el.json @@ -109,6 +109,15 @@ } }, "options": { + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "file_not_found": "\u03a4\u03bf \u03ba\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae config/.storage/knx/", + "invalid_individual_address": "\u0397 \u03c4\u03b9\u03bc\u03ae \u03b4\u03b5\u03bd \u03c4\u03b1\u03b9\u03c1\u03b9\u03ac\u03b6\u03b5\u03b9 \u03bc\u03b5 \u03c4\u03bf \u03bc\u03bf\u03c4\u03af\u03b2\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03bc\u03b5\u03bc\u03bf\u03bd\u03c9\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX.\n \"area.line.device\"", + "invalid_ip_address": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IPv4.", + "invalid_signature": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03ac\u03c6\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 knxkeys \u03b5\u03af\u03bd\u03b1\u03b9 \u03bb\u03ac\u03b8\u03bf\u03c2.", + "no_router_discovered": "\u0394\u03b5\u03bd \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b5 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae\u03c2 KNXnet/IP \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf.", + "no_tunnel_discovered": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03c2 \u03bf \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03cc \u03c3\u03b1\u03c2." + }, "step": { "communication_settings": { "data": { @@ -147,9 +156,14 @@ }, "manual_tunnel": { "data": { - "port": "\u0398\u03cd\u03c1\u03b1" + "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", + "local_ip": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae IP \u03c4\u03bf\u03c5 Home Assistant (\u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03b5\u03bd\u03ae \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7)", + "port": "\u0398\u03cd\u03c1\u03b1", + "route_back": "\u03a0\u03af\u03c3\u03c9 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae / \u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 NAT", + "tunneling_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" }, "data_description": { + "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP.", "local_ip": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7.", "port": "\u0398\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX/IP.", "route_back": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03b5\u03ac\u03bd \u03bf \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae\u03c2 \u03c3\u03b1\u03c2 KNXnet/IP tunneling \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c0\u03af\u03c3\u03c9 \u03b1\u03c0\u03cc \u03c4\u03bf NAT. \u0399\u03c3\u03c7\u03cd\u03b5\u03b9 \u03bc\u03cc\u03bd\u03bf \u03b3\u03b9\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03b9\u03c2 UDP." diff --git a/homeassistant/components/knx/translations/sk.json b/homeassistant/components/knx/translations/sk.json index c96c541a3bf..82e96300c36 100644 --- a/homeassistant/components/knx/translations/sk.json +++ b/homeassistant/components/knx/translations/sk.json @@ -7,9 +7,15 @@ "manual_tunnel": { "data": { "host": "Hostite\u013e", + "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a", "port": "Port" } }, + "routing": { + "data": { + "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" + } + }, "secure_manual": { "data": { "device_authentication": "Heslo na overenie zariadenia", @@ -40,11 +46,18 @@ "manual_tunnel": { "data": { "host": "Hostite\u013e", + "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a", "port": "Port" } }, + "routing": { + "data": { + "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" + } + }, "secure_tunnel_manual": { "data": { + "device_authentication": "Heslo na overenie zariadenia", "user_id": "ID pou\u017e\u00edvate\u013ea", "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" } diff --git a/homeassistant/components/kodi/translations/sk.json b/homeassistant/components/kodi/translations/sk.json index 8a9f91ad3ee..9a6a4bcb484 100644 --- a/homeassistant/components/kodi/translations/sk.json +++ b/homeassistant/components/kodi/translations/sk.json @@ -7,6 +7,7 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{name}", "step": { "credentials": { "data": { diff --git a/homeassistant/components/konnected/translations/sk.json b/homeassistant/components/konnected/translations/sk.json index 3a0f5b6f90f..11a47f35157 100644 --- a/homeassistant/components/konnected/translations/sk.json +++ b/homeassistant/components/konnected/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "step": { @@ -16,12 +17,15 @@ "step": { "options_binary": { "data": { - "name": "N\u00e1zov (volite\u013en\u00fd)" - } + "name": "N\u00e1zov (volite\u013en\u00fd)", + "type": "Typ sn\u00edma\u010da" + }, + "title": "Konfigur\u00e1cia sn\u00edma\u010da" }, "options_digital": { "data": { - "name": "N\u00e1zov (volite\u013en\u00fd)" + "name": "N\u00e1zov (volite\u013en\u00fd)", + "type": "Typ sn\u00edma\u010da" } }, "options_switch": { diff --git a/homeassistant/components/lacrosse_view/translations/sk.json b/homeassistant/components/lacrosse_view/translations/sk.json index 66e8cd431a4..d1c130a108b 100644 --- a/homeassistant/components/lacrosse_view/translations/sk.json +++ b/homeassistant/components/lacrosse_view/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/lametric/translations/el.json b/homeassistant/components/lametric/translations/el.json index f653a0b6af4..dfd0423ce15 100644 --- a/homeassistant/components/lametric/translations/el.json +++ b/homeassistant/components/lametric/translations/el.json @@ -9,6 +9,7 @@ "no_devices": "\u039f \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 LaMetric", "no_url_available": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL. \u0393\u03b9\u03b1 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1, [\u03b5\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b2\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1\u03c2] ( {docs_url} )", "reauth_device_not_found": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c0\u03bf\u03c5 \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03bb\u03ad\u03b3\u03be\u03b5\u03c4\u03b5 \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03c4\u03b7\u03bd \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b4\u03b5\u03bd \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c3\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc LaMetric", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "error": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/sk.json b/homeassistant/components/landisgyr_heat_meter/translations/sk.json index 793f8eff278..019769fcac4 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/sk.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/sk.json @@ -2,6 +2,13 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "step": { + "user": { + "data": { + "device": "Vyberte zariadenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/led_ble/translations/sk.json b/homeassistant/components/led_ble/translations/sk.json index 6a0b9f15898..fe721216387 100644 --- a/homeassistant/components/led_ble/translations/sk.json +++ b/homeassistant/components/led_ble/translations/sk.json @@ -2,6 +2,8 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "no_unconfigured_devices": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia.", "not_supported": "Zariadenie nie je podporovan\u00e9" }, "flow_title": "{name}" diff --git a/homeassistant/components/lg_soundbar/translations/sk.json b/homeassistant/components/lg_soundbar/translations/sk.json index 842ff61bd79..92d25505bbc 100644 --- a/homeassistant/components/lg_soundbar/translations/sk.json +++ b/homeassistant/components/lg_soundbar/translations/sk.json @@ -1,5 +1,9 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "existing_instance_updated": "Aktualizovan\u00e1 existuj\u00faca konfigur\u00e1cia." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/lifx/translations/sk.json b/homeassistant/components/lifx/translations/sk.json index b0535f56098..af15d63baa2 100644 --- a/homeassistant/components/lifx/translations/sk.json +++ b/homeassistant/components/lifx/translations/sk.json @@ -1,7 +1,13 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{label} ({host}) {serial}", "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {label} ({host}) {serial}?" + }, "pick_device": { "data": { "device": "Zariadenie" diff --git a/homeassistant/components/light/translations/sk.json b/homeassistant/components/light/translations/sk.json index 5294df79ce7..73f424dd74a 100644 --- a/homeassistant/components/light/translations/sk.json +++ b/homeassistant/components/light/translations/sk.json @@ -1,4 +1,21 @@ { + "device_automation": { + "action_type": { + "brightness_decrease": "Zn\u00ed\u017ete jas {entity_name}", + "brightness_increase": "Zv\u00fd\u0161te jas {entity_name}", + "flash": "Flash {entity_name}", + "toggle": "Prepn\u00fa\u0165 {entity_name}", + "turn_off": "Vypn\u00fa\u0165 {entity_name}", + "turn_on": "Zapn\u00fa\u0165 {entity_name}" + }, + "condition_type": { + "is_off": "{entity_name} je vypnut\u00e9", + "is_on": "{entity_name} je zapnut\u00e9" + }, + "trigger_type": { + "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9" + } + }, "state": { "_": { "off": "Vypnut\u00e9", diff --git a/homeassistant/components/litterrobot/translations/sensor.sk.json b/homeassistant/components/litterrobot/translations/sensor.sk.json index b4c5c43292d..1fc7f2128f0 100644 --- a/homeassistant/components/litterrobot/translations/sensor.sk.json +++ b/homeassistant/components/litterrobot/translations/sensor.sk.json @@ -2,6 +2,8 @@ "state": { "litterrobot__status_code": { "off": "Vypnut\u00fd", + "offline": "Offline", + "p": "Pozastaven\u00fd", "rdy": "Pripraven\u00fd" } } diff --git a/homeassistant/components/livisi/translations/el.json b/homeassistant/components/livisi/translations/el.json index 9d33b5aaa1e..f192cdf285a 100644 --- a/homeassistant/components/livisi/translations/el.json +++ b/homeassistant/components/livisi/translations/el.json @@ -1,12 +1,14 @@ { "config": { "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "wrong_ip_address": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03b5\u03af\u03bd\u03b1\u03b9 \u03bb\u03b1\u03bd\u03b8\u03b1\u03c3\u03bc\u03ad\u03bd\u03b7 \u03ae \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03c4\u03bf\u03c0\u03b9\u03ba\u03ae \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7 \u03c3\u03c4\u03bf SHC.", "wrong_password": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03bb\u03b1\u03bd\u03b8\u03b1\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2." }, "step": { "user": { "data": { + "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" }, "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03ba\u03b1\u03b9 \u03c4\u03bf\u03bd (\u03c4\u03bf\u03c0\u03b9\u03ba\u03cc) \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 SHC." diff --git a/homeassistant/components/lock/translations/sk.json b/homeassistant/components/lock/translations/sk.json index c01a1106cd5..17c0e221f97 100644 --- a/homeassistant/components/lock/translations/sk.json +++ b/homeassistant/components/lock/translations/sk.json @@ -1,4 +1,19 @@ { + "device_automation": { + "action_type": { + "lock": "Uzamkn\u00fa\u0165 {entity_name}", + "open": "Otvori\u0165 {entity_name}", + "unlock": "Odomkn\u00fa\u0165 {entity_name}" + }, + "condition_type": { + "is_locked": "{entity_name} je uzamknut\u00fd", + "is_unlocked": "{entity_name} je odomknut\u00fd" + }, + "trigger_type": { + "locked": "{entity_name} uzamknut\u00fd", + "unlocked": "{entity_name} odomknut\u00fd" + } + }, "state": { "_": { "locked": "Zamknut\u00fd", diff --git a/homeassistant/components/lookin/translations/sk.json b/homeassistant/components/lookin/translations/sk.json index 79b9923e1cf..3474144425e 100644 --- a/homeassistant/components/lookin/translations/sk.json +++ b/homeassistant/components/lookin/translations/sk.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "error": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, "flow_title": "{name} ({host})", "step": { @@ -9,6 +14,9 @@ "data": { "name": "N\u00e1zov" } + }, + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {name} ({host})?" } } } diff --git a/homeassistant/components/luftdaten/translations/sk.json b/homeassistant/components/luftdaten/translations/sk.json new file mode 100644 index 00000000000..4aa3ebe30a9 --- /dev/null +++ b/homeassistant/components/luftdaten/translations/sk.json @@ -0,0 +1,14 @@ +{ + "config": { + "error": { + "invalid_sensor": "Sn\u00edma\u010d nie je dostupn\u00fd alebo je neplatn\u00fd" + }, + "step": { + "user": { + "data": { + "station_id": "ID sn\u00edma\u010da" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lutron_caseta/translations/sk.json b/homeassistant/components/lutron_caseta/translations/sk.json index d7dee4be88b..7b3c7b86ff7 100644 --- a/homeassistant/components/lutron_caseta/translations/sk.json +++ b/homeassistant/components/lutron_caseta/translations/sk.json @@ -1,12 +1,27 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name} ({host})", "step": { "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Zadajte IP adresu zariadenia." } } + }, + "device_automation": { + "trigger_subtype": { + "button_1": "Prv\u00e9 tla\u010didlo", + "button_2": "Druh\u00e9 tla\u010didlo", + "button_3": "Tretie tla\u010didlo", + "button_4": "\u0160tvrt\u00e9 tla\u010didlo", + "button_5": "Piate tla\u010didlo", + "button_6": "\u0160ieste tla\u010didlo", + "button_7": "Siedme tla\u010didlo" + } } } \ No newline at end of file diff --git a/homeassistant/components/media_player/translations/sk.json b/homeassistant/components/media_player/translations/sk.json index 16818a4b593..af39abe21d4 100644 --- a/homeassistant/components/media_player/translations/sk.json +++ b/homeassistant/components/media_player/translations/sk.json @@ -1,4 +1,9 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} zmenil stav" + } + }, "state": { "_": { "idle": "Ne\u010dinn\u00fd", diff --git a/homeassistant/components/meteoclimatic/translations/sk.json b/homeassistant/components/meteoclimatic/translations/sk.json new file mode 100644 index 00000000000..fa6643bfcab --- /dev/null +++ b/homeassistant/components/meteoclimatic/translations/sk.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "not_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mill/translations/sk.json b/homeassistant/components/mill/translations/sk.json index a221d039da3..f14b98631b1 100644 --- a/homeassistant/components/mill/translations/sk.json +++ b/homeassistant/components/mill/translations/sk.json @@ -5,6 +5,9 @@ "data": { "password": "Heslo" } + }, + "local": { + "description": "Lok\u00e1lna IP adresa zariadenia." } } } diff --git a/homeassistant/components/mjpeg/translations/sk.json b/homeassistant/components/mjpeg/translations/sk.json index 55f23ea3d80..1b1ae53738b 100644 --- a/homeassistant/components/mjpeg/translations/sk.json +++ b/homeassistant/components/mjpeg/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, @@ -14,6 +17,7 @@ }, "options": { "error": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { diff --git a/homeassistant/components/moat/translations/sk.json b/homeassistant/components/moat/translations/sk.json new file mode 100644 index 00000000000..8444b9fbeae --- /dev/null +++ b/homeassistant/components/moat/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/modem_callerid/translations/sk.json b/homeassistant/components/modem_callerid/translations/sk.json index f7ef4cd289d..3a991747995 100644 --- a/homeassistant/components/modem_callerid/translations/sk.json +++ b/homeassistant/components/modem_callerid/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "step": { diff --git a/homeassistant/components/modern_forms/translations/sk.json b/homeassistant/components/modern_forms/translations/sk.json index aaad66e6729..835f107f7af 100644 --- a/homeassistant/components/modern_forms/translations/sk.json +++ b/homeassistant/components/modern_forms/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/moehlenhoff_alpha2/translations/sk.json b/homeassistant/components/moehlenhoff_alpha2/translations/sk.json index 842ff61bd79..3c3d27a6689 100644 --- a/homeassistant/components/moehlenhoff_alpha2/translations/sk.json +++ b/homeassistant/components/moehlenhoff_alpha2/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/monoprice/translations/sk.json b/homeassistant/components/monoprice/translations/sk.json index 892b8b2cd91..cb264915fdd 100644 --- a/homeassistant/components/monoprice/translations/sk.json +++ b/homeassistant/components/monoprice/translations/sk.json @@ -1,10 +1,14 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { "port": "Port" - } + }, + "title": "Pripojte sa k zariadeniu" } } } diff --git a/homeassistant/components/moon/translations/sensor.sk.json b/homeassistant/components/moon/translations/sensor.sk.json new file mode 100644 index 00000000000..4adfde603cf --- /dev/null +++ b/homeassistant/components/moon/translations/sensor.sk.json @@ -0,0 +1,10 @@ +{ + "state": { + "moon__phase": { + "first_quarter": "Prv\u00e1 \u0161tvrtina", + "full_moon": "Spln", + "last_quarter": "Posledn\u00e1 \u0161tvrtina", + "new_moon": "Nov mesiac" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/motion_blinds/translations/sk.json b/homeassistant/components/motion_blinds/translations/sk.json index 302a4868415..aa889b4cd07 100644 --- a/homeassistant/components/motion_blinds/translations/sk.json +++ b/homeassistant/components/motion_blinds/translations/sk.json @@ -1,8 +1,10 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/mqtt/translations/el.json b/homeassistant/components/mqtt/translations/el.json index 2f17db20882..a822cd2a927 100644 --- a/homeassistant/components/mqtt/translations/el.json +++ b/homeassistant/components/mqtt/translations/el.json @@ -80,15 +80,31 @@ "options": { "error": { "bad_birth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b8\u03ad\u03bc\u03b1 birth.", + "bad_certificate": "\u03a4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc CA \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf", + "bad_client_cert": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7, \u03b2\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03c0\u03b1\u03c1\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03bc\u03b5 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 PEM", + "bad_client_cert_key": "\u03a4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7 \u03ba\u03b1\u03b9 \u03c4\u03bf \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03cc \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b6\u03b5\u03cd\u03b3\u03bf\u03c2", + "bad_client_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03cc \u03ba\u03bb\u03b5\u03b9\u03b4\u03af, \u03b2\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03bc\u03b5 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 PEM \u03c0\u03b1\u03c1\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03c7\u03c9\u03c1\u03af\u03c2 \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "bad_discovery_prefix": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7\u03c2", "bad_will": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b8\u03ad\u03bc\u03b1 will.", - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_inclusion": "\u03a4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7 \u03ba\u03b1\u03b9 \u03c4\u03bf \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03cc \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03bf\u03cd\u03bd \u03bc\u03b1\u03b6\u03af" }, "step": { "broker": { "data": { + "advanced_options": "\u03a0\u03c1\u03bf\u03b7\u03b3\u03bc\u03ad\u03bd\u03b5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2", "broker": "\u039c\u03b5\u03c3\u03af\u03c4\u03b7\u03c2", + "certificate": "\u0391\u03bd\u03ad\u03b2\u03b1\u03c3\u03bc\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03c3\u03bc\u03ad\u03bd\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd CA", + "client_cert": "\u0391\u03bd\u03ad\u03b2\u03b1\u03c3\u03bc\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7", + "client_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7 (\u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b5\u03bd\u03cc \u03c3\u03b5 \u03ad\u03bd\u03b1 \u03c0\u03bf\u03c5 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03b8\u03b7\u03ba\u03b5 \u03c4\u03c5\u03c7\u03b1\u03af\u03b1)", + "client_key": "\u039c\u03b5\u03c4\u03b1\u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03bf\u03cd \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd", + "keepalive": "\u039f \u03c7\u03c1\u03cc\u03bd\u03bf\u03c2 \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03c4\u03b7\u03c2 \u03b1\u03c0\u03bf\u03c3\u03c4\u03bf\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03c4\u03b7\u03c1\u03b5\u03af \u03b6\u03c9\u03bd\u03c4\u03b1\u03bd\u03ac \u03c4\u03b1 \u03bc\u03b7\u03bd\u03cd\u03bc\u03b1\u03c4\u03b1", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "port": "\u0398\u03cd\u03c1\u03b1", + "protocol": "\u03a0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf MQTT", + "set_ca_cert": "\u0395\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03b5\u03c3\u03af\u03c4\u03b7", + "set_client_cert": "\u03a7\u03c1\u03ae\u03c3\u03b7 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7", + "tls_insecure": "\u0391\u03b3\u03bd\u03bf\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03b5\u03c3\u03af\u03c4\u03b7", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" }, "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03bc\u03b5\u03c3\u03af\u03c4\u03b7 MQTT.", diff --git a/homeassistant/components/mqtt/translations/sk.json b/homeassistant/components/mqtt/translations/sk.json index 4793e54fab6..2bb5c5b89cf 100644 --- a/homeassistant/components/mqtt/translations/sk.json +++ b/homeassistant/components/mqtt/translations/sk.json @@ -10,9 +10,22 @@ "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } + }, + "hassio_confirm": { + "title": "MQTT Broker cez doplnok Home Assistant" } } }, + "device_automation": { + "trigger_subtype": { + "button_1": "Prv\u00e9 tla\u010didlo", + "button_2": "Druh\u00e9 tla\u010didlo", + "button_3": "Tretie tla\u010didlo", + "button_4": "\u0160tvrt\u00e9 tla\u010didlo", + "button_5": "Piate tla\u010didlo", + "button_6": "\u0160ieste tla\u010didlo" + } + }, "options": { "step": { "broker": { diff --git a/homeassistant/components/mullvad/translations/sk.json b/homeassistant/components/mullvad/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/mullvad/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/sk.json b/homeassistant/components/mysensors/translations/sk.json index 2c3ed1dd930..c5206b30284 100644 --- a/homeassistant/components/mysensors/translations/sk.json +++ b/homeassistant/components/mysensors/translations/sk.json @@ -1,10 +1,38 @@ { "config": { "abort": { - "invalid_auth": "Neplatn\u00e9 overenie" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_device": "Neplatn\u00e9 zariadenie", + "invalid_ip": "Neplatn\u00e1 IP adresa", + "invalid_port": "Neplatn\u00e9 \u010d\u00edslo portu", + "invalid_serial": "Neplatn\u00fd s\u00e9riov\u00fd port", + "not_a_number": "Zadajte \u010d\u00edslo", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_device": "Neplatn\u00e9 zariadenie", + "invalid_ip": "Neplatn\u00e1 IP adresa", + "invalid_port": "Neplatn\u00e9 \u010d\u00edslo portu", + "invalid_serial": "Neplatn\u00fd s\u00e9riov\u00fd port", + "not_a_number": "Zadajte \u010d\u00edslo", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "gw_serial": { + "data": { + "device": "S\u00e9riov\u00fd port" + } + }, + "gw_tcp": { + "data": { + "device": "IP adresa br\u00e1ny", + "tcp_port": "port" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/sensor.sk.json b/homeassistant/components/nam/translations/sensor.sk.json new file mode 100644 index 00000000000..c0ac0fd36db --- /dev/null +++ b/homeassistant/components/nam/translations/sensor.sk.json @@ -0,0 +1,11 @@ +{ + "state": { + "nam__caqi_level": { + "high": "Vysok\u00e9", + "low": "N\u00edzke", + "medium": "Stredn\u00e9", + "very high": "Ve\u013emi vysok\u00e9", + "very low": "Ve\u013emi n\u00edzke" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nam/translations/sk.json b/homeassistant/components/nam/translations/sk.json index 65c34bd9d4b..116df3a12bd 100644 --- a/homeassistant/components/nam/translations/sk.json +++ b/homeassistant/components/nam/translations/sk.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "device_unsupported": "Zariadenie nie je podporovan\u00e9.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -16,7 +18,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Zadajte spr\u00e1vne pou\u017e\u00edvate\u013esk\u00e9 meno a heslo pre hostite\u013ea: {host}" }, "user": { "data": { diff --git a/homeassistant/components/nanoleaf/translations/sk.json b/homeassistant/components/nanoleaf/translations/sk.json index b41366e3fde..164d511360f 100644 --- a/homeassistant/components/nanoleaf/translations/sk.json +++ b/homeassistant/components/nanoleaf/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "flow_title": "{name}", @@ -14,7 +15,10 @@ }, "device_automation": { "trigger_type": { - "swipe_right": "Potiahnite prstom doprava" + "swipe_down": "Potiahnite prstom nadol", + "swipe_left": "Potiahnite prstom do\u013eava", + "swipe_right": "Potiahnite prstom doprava", + "swipe_up": "Potiahnite prstom nahor" } } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/sk.json b/homeassistant/components/neato/translations/sk.json index 520a3afd6d9..67cfbafa4f8 100644 --- a/homeassistant/components/neato/translations/sk.json +++ b/homeassistant/components/neato/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "create_entry": { diff --git a/homeassistant/components/nest/translations/sk.json b/homeassistant/components/nest/translations/sk.json index 43b57c320d4..73c43e25db2 100644 --- a/homeassistant/components/nest/translations/sk.json +++ b/homeassistant/components/nest/translations/sk.json @@ -16,5 +16,13 @@ } } } + }, + "device_automation": { + "trigger_type": { + "camera_motion": "Zisten\u00fd pohyb", + "camera_person": "Zisten\u00e1 osoba", + "camera_sound": "Rozpoznan\u00fd zvuk", + "doorbell_chime": "Zvon\u010dek stla\u010den\u00fd" + } } } \ No newline at end of file diff --git a/homeassistant/components/netatmo/translations/sk.json b/homeassistant/components/netatmo/translations/sk.json index 0f73e9340d9..fcadc9ac0c9 100644 --- a/homeassistant/components/netatmo/translations/sk.json +++ b/homeassistant/components/netatmo/translations/sk.json @@ -12,6 +12,20 @@ } } }, + "device_automation": { + "trigger_type": { + "alarm_started": "{entity_name} rozpoznala alarm", + "animal": "{entity_name} rozpoznala zviera", + "human": "{entity_name} rozpoznala \u010dloveka", + "movement": "{entity_name} rozpoznala pohyb", + "outdoor": "{n\u00e1zov_objektu} zistila vonkaj\u0161iu udalos\u0165", + "person": "{entity_name} rozpoznala osobu", + "person_away": "{entity_name} zistila, \u017ee osoba odi\u0161la", + "therm_mode": "{entity_name} prepnut\u00e9 na \u201e{subtype}\u201c", + "turned_off": "{entity_name} vypnut\u00e1", + "turned_on": "{entity_name} zapnut\u00e1" + } + }, "options": { "step": { "public_weather": { diff --git a/homeassistant/components/netgear/translations/sk.json b/homeassistant/components/netgear/translations/sk.json index 06265159290..15c74aa2ccd 100644 --- a/homeassistant/components/netgear/translations/sk.json +++ b/homeassistant/components/netgear/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/nexia/translations/sk.json b/homeassistant/components/nexia/translations/sk.json index 1b1e671c054..8f06d99549b 100644 --- a/homeassistant/components/nexia/translations/sk.json +++ b/homeassistant/components/nexia/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/nfandroidtv/translations/sk.json b/homeassistant/components/nfandroidtv/translations/sk.json index 965b3bb8919..9c5640da7d0 100644 --- a/homeassistant/components/nfandroidtv/translations/sk.json +++ b/homeassistant/components/nfandroidtv/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/sk.json b/homeassistant/components/nightscout/translations/sk.json index ff853127803..f5f4789c7fe 100644 --- a/homeassistant/components/nightscout/translations/sk.json +++ b/homeassistant/components/nightscout/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/nuheat/translations/sk.json b/homeassistant/components/nuheat/translations/sk.json index 1b1e671c054..8f06d99549b 100644 --- a/homeassistant/components/nuheat/translations/sk.json +++ b/homeassistant/components/nuheat/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/nzbget/translations/sk.json b/homeassistant/components/nzbget/translations/sk.json index e6a451b5ba5..dbffe2972bc 100644 --- a/homeassistant/components/nzbget/translations/sk.json +++ b/homeassistant/components/nzbget/translations/sk.json @@ -1,5 +1,6 @@ { "config": { + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/octoprint/translations/sk.json b/homeassistant/components/octoprint/translations/sk.json index 842ff61bd79..3c3d27a6689 100644 --- a/homeassistant/components/octoprint/translations/sk.json +++ b/homeassistant/components/octoprint/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/onewire/translations/sk.json b/homeassistant/components/onewire/translations/sk.json index fc635f2c709..b20cd8f3ca6 100644 --- a/homeassistant/components/onewire/translations/sk.json +++ b/homeassistant/components/onewire/translations/sk.json @@ -14,5 +14,17 @@ "title": "Nastavenie 1-Wire" } } + }, + "options": { + "error": { + "device_not_selected": "Vyberte zariadenia, ktor\u00e9 chcete nakonfigurova\u0165" + }, + "step": { + "configure_device": { + "data": { + "precision": "Presnos\u0165 sn\u00edma\u010da" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/opengarage/translations/sk.json b/homeassistant/components/opengarage/translations/sk.json index 6f2d6c9c475..b343b9c45d2 100644 --- a/homeassistant/components/opengarage/translations/sk.json +++ b/homeassistant/components/opengarage/translations/sk.json @@ -1,11 +1,15 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { "user": { "data": { + "device_key": "K\u013e\u00fa\u010d zariadenia", "host": "Hostite\u013e", "port": "Port" } diff --git a/homeassistant/components/openuv/translations/el.json b/homeassistant/components/openuv/translations/el.json index 533f09924ed..7a88486863b 100644 --- a/homeassistant/components/openuv/translations/el.json +++ b/homeassistant/components/openuv/translations/el.json @@ -1,14 +1,19 @@ { "config": { "abort": { - "already_configured": "\u0397 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + "already_configured": "\u0397 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" }, "error": { "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API" }, "step": { "reauth_confirm": { - "description": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b5\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03b3\u03b9\u03b1 \u03c4\u03b1 {latitude}, {longitude}." + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API" + }, + "description": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b5\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03b3\u03b9\u03b1 \u03c4\u03b1 {latitude}, {longitude}.", + "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" }, "user": { "data": { diff --git a/homeassistant/components/oralb/translations/el.json b/homeassistant/components/oralb/translations/el.json index b1306bd0bce..cdb57c8ac1b 100644 --- a/homeassistant/components/oralb/translations/el.json +++ b/homeassistant/components/oralb/translations/el.json @@ -2,13 +2,20 @@ "config": { "abort": { "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf", "not_supported": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9" }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name};" + }, "user": { "data": { "address": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" - } + }, + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b3\u03b9\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7" } } } diff --git a/homeassistant/components/oralb/translations/sk.json b/homeassistant/components/oralb/translations/sk.json index ba3b7cee831..25ca828afe4 100644 --- a/homeassistant/components/oralb/translations/sk.json +++ b/homeassistant/components/oralb/translations/sk.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "not_supported": "Zariadenie nie je podporovan\u00e9" } } diff --git a/homeassistant/components/overkiz/translations/sensor.sk.json b/homeassistant/components/overkiz/translations/sensor.sk.json index cfa849319eb..ba996535149 100644 --- a/homeassistant/components/overkiz/translations/sensor.sk.json +++ b/homeassistant/components/overkiz/translations/sensor.sk.json @@ -1,10 +1,27 @@ { "state": { + "overkiz__battery": { + "low": "N\u00edzke", + "normal": "Norm\u00e1lne", + "verylow": "Ve\u013emi n\u00edzke" + }, "overkiz__discrete_rssi_level": { - "good": "Dobr\u00e1" + "good": "Dobr\u00e1", + "low": "N\u00edzke", + "normal": "Norm\u00e1lne", + "verylow": "Ve\u013emi n\u00edzke" }, "overkiz__priority_lock_originator": { + "local_user": "Miestny pou\u017e\u00edvate\u013e", + "temperature": "Teplota", + "timer": "\u010casova\u010d", + "ups": "UPS", + "user": "Pou\u017e\u00edvate\u013e", "wind": "Vietor" + }, + "overkiz__three_way_handle_direction": { + "closed": "Zatvoren\u00e9", + "open": "Otvori\u0165" } } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/sk.json b/homeassistant/components/picnic/translations/sk.json index 083db09d558..7b3c6e79436 100644 --- a/homeassistant/components/picnic/translations/sk.json +++ b/homeassistant/components/picnic/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/plaato/translations/sk.json b/homeassistant/components/plaato/translations/sk.json new file mode 100644 index 00000000000..a81e0400e19 --- /dev/null +++ b/homeassistant/components/plaato/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "device_name": "Pomenujte svoje zariadenie" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/poolsense/translations/sk.json b/homeassistant/components/poolsense/translations/sk.json index 87352b48fad..bfca61034ec 100644 --- a/homeassistant/components/poolsense/translations/sk.json +++ b/homeassistant/components/poolsense/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/powerwall/translations/sk.json b/homeassistant/components/powerwall/translations/sk.json index 680b72cf9a9..0ac8e2c85cf 100644 --- a/homeassistant/components/powerwall/translations/sk.json +++ b/homeassistant/components/powerwall/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -8,6 +9,9 @@ }, "flow_title": "{name} ({ip_address})", "step": { + "confirm_discovery": { + "description": "Chcete nastavi\u0165 {name} ({ip_address})?" + }, "reauth_confim": { "data": { "password": "Heslo" diff --git a/homeassistant/components/progettihwsw/translations/sk.json b/homeassistant/components/progettihwsw/translations/sk.json index 33d93b62475..142f47cf1fd 100644 --- a/homeassistant/components/progettihwsw/translations/sk.json +++ b/homeassistant/components/progettihwsw/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/prosegur/translations/sk.json b/homeassistant/components/prosegur/translations/sk.json index bd3a9e24cf1..b557456a8fe 100644 --- a/homeassistant/components/prosegur/translations/sk.json +++ b/homeassistant/components/prosegur/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/prusalink/translations/sensor.sk.json b/homeassistant/components/prusalink/translations/sensor.sk.json new file mode 100644 index 00000000000..054a2d156d3 --- /dev/null +++ b/homeassistant/components/prusalink/translations/sensor.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "prusalink__printer_state": { + "printing": "Tla\u010d" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ps4/translations/sk.json b/homeassistant/components/ps4/translations/sk.json index fa12207330b..4a31453350b 100644 --- a/homeassistant/components/ps4/translations/sk.json +++ b/homeassistant/components/ps4/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, "step": { "link": { diff --git a/homeassistant/components/pure_energie/translations/sk.json b/homeassistant/components/pure_energie/translations/sk.json index a73a7a961f5..c1e7b583554 100644 --- a/homeassistant/components/pure_energie/translations/sk.json +++ b/homeassistant/components/pure_energie/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{model} ({host})", "step": { "user": { diff --git a/homeassistant/components/pushbullet/translations/el.json b/homeassistant/components/pushbullet/translations/el.json index ca4a9135c92..0f47d7be366 100644 --- a/homeassistant/components/pushbullet/translations/el.json +++ b/homeassistant/components/pushbullet/translations/el.json @@ -1,4 +1,21 @@ { + "config": { + "abort": { + "already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API" + }, + "step": { + "user": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1" + } + } + } + }, "issues": { "deprecated_yaml": { "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Pushbullet \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03b5\u03b9\u03c3\u03b1\u03c7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Pushbullet YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/sk.json b/homeassistant/components/pvpc_hourly_pricing/translations/sk.json new file mode 100644 index 00000000000..bf9a293e4f8 --- /dev/null +++ b/homeassistant/components/pvpc_hourly_pricing/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "N\u00e1zov sn\u00edma\u010da" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/sk.json b/homeassistant/components/qnap_qsw/translations/sk.json index a4249f0efe6..3671c1f6d2b 100644 --- a/homeassistant/components/qnap_qsw/translations/sk.json +++ b/homeassistant/components/qnap_qsw/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "discovered_connection": { "data": { diff --git a/homeassistant/components/rachio/translations/sk.json b/homeassistant/components/rachio/translations/sk.json index ff853127803..f5f4789c7fe 100644 --- a/homeassistant/components/rachio/translations/sk.json +++ b/homeassistant/components/rachio/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/radiotherm/translations/sk.json b/homeassistant/components/radiotherm/translations/sk.json index d12d05c0033..060571686c4 100644 --- a/homeassistant/components/radiotherm/translations/sk.json +++ b/homeassistant/components/radiotherm/translations/sk.json @@ -1,7 +1,13 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name} {model} ({host})", "step": { + "confirm": { + "description": "Chcete nastavi\u0165 {name} {model} ({host})?" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/rainforest_eagle/translations/sk.json b/homeassistant/components/rainforest_eagle/translations/sk.json index 368553a4887..c3d4ba60dd8 100644 --- a/homeassistant/components/rainforest_eagle/translations/sk.json +++ b/homeassistant/components/rainforest_eagle/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/rainmachine/translations/sk.json b/homeassistant/components/rainmachine/translations/sk.json index c02d61fe9e3..c9c84dd69a7 100644 --- a/homeassistant/components/rainmachine/translations/sk.json +++ b/homeassistant/components/rainmachine/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/recollect_waste/translations/sk.json b/homeassistant/components/recollect_waste/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/recollect_waste/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/remote/translations/sk.json b/homeassistant/components/remote/translations/sk.json index 381668927df..4c8b090aea1 100644 --- a/homeassistant/components/remote/translations/sk.json +++ b/homeassistant/components/remote/translations/sk.json @@ -1,4 +1,9 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9" + } + }, "state": { "_": { "off": "Vypnut\u00fd", diff --git a/homeassistant/components/rfxtrx/translations/sk.json b/homeassistant/components/rfxtrx/translations/sk.json index 35f2d7628c7..0335755c3a5 100644 --- a/homeassistant/components/rfxtrx/translations/sk.json +++ b/homeassistant/components/rfxtrx/translations/sk.json @@ -8,8 +8,33 @@ } }, "setup_serial": { + "data": { + "device": "Vyberte zariadenie" + }, "title": "Zariadenie" } } + }, + "device_automation": { + "action_type": { + "send_command": "Odosla\u0165 pr\u00edkaz: {subtype}", + "send_status": "Odosla\u0165 aktualiz\u00e1ciu stavu: {subtype}" + }, + "trigger_type": { + "command": "Prijat\u00fd pr\u00edkaz: {subtype}", + "status": "Prijat\u00fd stav: {subtype}" + } + }, + "options": { + "error": { + "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "step": { + "prompt_options": { + "data": { + "device": "Vyberte zariadenie, ktor\u00e9 chcete nakonfigurova\u0165" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/risco/translations/sk.json b/homeassistant/components/risco/translations/sk.json index a0c200a4364..8311c3f50c2 100644 --- a/homeassistant/components/risco/translations/sk.json +++ b/homeassistant/components/risco/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/rituals_perfume_genie/translations/sk.json b/homeassistant/components/rituals_perfume_genie/translations/sk.json index 87352b48fad..bfca61034ec 100644 --- a/homeassistant/components/rituals_perfume_genie/translations/sk.json +++ b/homeassistant/components/rituals_perfume_genie/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/roku/translations/sk.json b/homeassistant/components/roku/translations/sk.json index 95a5e4fa51e..5d0572a9984 100644 --- a/homeassistant/components/roku/translations/sk.json +++ b/homeassistant/components/roku/translations/sk.json @@ -1,10 +1,14 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "flow_title": "{name}", "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/roomba/translations/sk.json b/homeassistant/components/roomba/translations/sk.json index b3e6f50eaef..b941f41c75b 100644 --- a/homeassistant/components/roomba/translations/sk.json +++ b/homeassistant/components/roomba/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name} ({host})", "step": { "link_manual": { @@ -11,12 +14,14 @@ "manual": { "data": { "host": "Hostite\u013e" - } + }, + "title": "Manu\u00e1lne pripojenie k zariadeniu" }, "user": { "data": { "host": "Hostite\u013e" - } + }, + "title": "Automaticky sa pripoji\u0165 k zariadeniu" } } } diff --git a/homeassistant/components/roon/translations/sk.json b/homeassistant/components/roon/translations/sk.json index 1c19d1e71e6..8d62051fd1d 100644 --- a/homeassistant/components/roon/translations/sk.json +++ b/homeassistant/components/roon/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/ruckus_unleashed/translations/sk.json b/homeassistant/components/ruckus_unleashed/translations/sk.json index d2927b101d0..e86b8339ba6 100644 --- a/homeassistant/components/ruckus_unleashed/translations/sk.json +++ b/homeassistant/components/ruckus_unleashed/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/samsungtv/translations/sk.json b/homeassistant/components/samsungtv/translations/sk.json index 6058e81bb73..3fb8c75cdae 100644 --- a/homeassistant/components/samsungtv/translations/sk.json +++ b/homeassistant/components/samsungtv/translations/sk.json @@ -1,10 +1,16 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "not_supported": "Toto zariadenie Samsung moment\u00e1lne nie je podporovan\u00e9.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, + "flow_title": "{device}", "step": { + "encrypted_pairing": { + "description": "Zadajte k\u00f3d PIN zobrazen\u00fd na {device}." + }, "user": { "data": { "host": "Hostite\u013e", diff --git a/homeassistant/components/scrape/translations/bg.json b/homeassistant/components/scrape/translations/bg.json index 095249f5317..b75cbbb4e6e 100644 --- a/homeassistant/components/scrape/translations/bg.json +++ b/homeassistant/components/scrape/translations/bg.json @@ -4,11 +4,24 @@ "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d" }, "step": { + "sensor": { + "data": { + "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", + "index": "\u0418\u043d\u0434\u0435\u043a\u0441", + "name": "\u0418\u043c\u0435", + "select": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435", + "unit_of_measurement": "\u041c\u0435\u0440\u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0430" + }, + "data_description": { + "state_class": "state_class \u043d\u0430 \u0441\u0435\u043d\u0437\u043e\u0440\u0430" + } + }, "user": { "data": { "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", "authentication": "\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", "index": "\u0418\u043d\u0434\u0435\u043a\u0441", + "method": "\u041c\u0435\u0442\u043e\u0434", "name": "\u0418\u043c\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "select": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435", @@ -30,6 +43,7 @@ "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", "authentication": "\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", "index": "\u0418\u043d\u0434\u0435\u043a\u0441", + "method": "\u041c\u0435\u0442\u043e\u0434", "name": "\u0418\u043c\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "select": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435", diff --git a/homeassistant/components/scrape/translations/el.json b/homeassistant/components/scrape/translations/el.json index 2e2ce016816..4dddb51b27a 100644 --- a/homeassistant/components/scrape/translations/el.json +++ b/homeassistant/components/scrape/translations/el.json @@ -3,7 +3,31 @@ "abort": { "already_configured": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" }, + "error": { + "resource_error": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03c4\u03c9\u03bd \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd \u03b1\u03bd\u03ac\u03c0\u03b1\u03c5\u03c3\u03b7\u03c2. \u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2" + }, "step": { + "sensor": { + "data": { + "attribute": "\u03a7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc", + "device_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", + "index": "\u0394\u03b5\u03af\u03ba\u03c4\u03b7\u03c2", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1", + "select": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae", + "state_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", + "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2", + "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2 " + }, + "data_description": { + "attribute": "\u039b\u03ae\u03c8\u03b7 \u03c4\u03b7\u03c2 \u03c4\u03b9\u03bc\u03ae\u03c2 \u03b5\u03bd\u03cc\u03c2 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03bf\u03cd \u03c3\u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1", + "device_class": "\u039f \u03c4\u03cd\u03c0\u03bf\u03c2/\u03ba\u03bb\u03ac\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03bf\u03c1\u03b9\u03c3\u03bc\u03cc \u03c4\u03bf\u03c5 \u03b5\u03b9\u03ba\u03bf\u03bd\u03b9\u03b4\u03af\u03bf\u03c5 \u03c3\u03c4\u03bf frontend", + "index": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03c0\u03bf\u03b9\u03b1 \u03b1\u03c0\u03cc \u03c4\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03b1 \u03c0\u03bf\u03c5 \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03b1 CSS \u03b3\u03b9\u03b1 \u03c7\u03c1\u03ae\u03c3\u03b7", + "select": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03c0\u03bf\u03b9\u03b1 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1 \u03b8\u03b1 \u03b1\u03bd\u03b1\u03b6\u03b7\u03c4\u03b7\u03b8\u03b5\u03af. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03bf\u03c5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03b5\u03af\u03c2 CSS Beautifulsoup \u03b3\u03b9\u03b1 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2", + "state_class": "\u0397 state_class \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", + "unit_of_measurement": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1\u03c2 \u03ae \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03ba\u03ae \u03c3\u03b1\u03c2", + "value_template": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03ad\u03bd\u03b1 \u03c0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03bb\u03ae\u03c8\u03b7 \u03c4\u03b7\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1" + } + }, "user": { "data": { "attribute": "\u03a7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc", @@ -11,11 +35,13 @@ "device_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", "headers": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b5\u03c2", "index": "\u0394\u03b5\u03af\u03ba\u03c4\u03b7\u03c2", + "method": "\u039c\u03ad\u03b8\u03bf\u03b4\u03bf\u03c2", "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "resource": "\u03a0\u03cc\u03c1\u03bf\u03c2", "select": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae", "state_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", + "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf", "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2", @@ -30,6 +56,7 @@ "resource": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c3\u03c4\u03bf\u03bd \u03b9\u03c3\u03c4\u03cc\u03c4\u03bf\u03c0\u03bf \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03c4\u03b7\u03bd \u03c4\u03b9\u03bc\u03ae", "select": "\u039f\u03c1\u03af\u03b6\u03b5\u03b9 \u03c0\u03bf\u03b9\u03b1 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1 \u03b8\u03b1 \u03b1\u03bd\u03b1\u03b6\u03b7\u03c4\u03b7\u03b8\u03b5\u03af. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03bf\u03c5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03b5\u03af\u03c2 CSS \u03c4\u03bf\u03c5 Beautifulsoup \u03b3\u03b9\u03b1 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2", "state_class": "\u0397 state_class \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2", + "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03bf\u03bd \u03b9\u03c3\u03c4\u03cc\u03c4\u03bf\u03c0\u03bf", "value_template": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03ad\u03bd\u03b1 \u03c0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03c0\u03b1\u03c1\u03b1\u03bb\u03b1\u03b2\u03ae \u03c4\u03b7\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", "verify_ssl": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af/\u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c4\u03b7\u03bd \u03b5\u03c0\u03b1\u03bb\u03ae\u03b8\u03b5\u03c5\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd SSL/TLS, \u03c0.\u03c7. \u03b1\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c5\u03c4\u03bf-\u03c5\u03c0\u03bf\u03b3\u03b5\u03b3\u03c1\u03b1\u03bc\u03bc\u03ad\u03bd\u03bf." } @@ -51,11 +78,13 @@ "device_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", "headers": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b5\u03c2", "index": "\u0394\u03b5\u03af\u03ba\u03c4\u03b7\u03c2", + "method": "\u039c\u03ad\u03b8\u03bf\u03b4\u03bf\u03c2", "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "password": "\u039a\u03b5\u03bd\u03cc", "resource": "\u03a0\u03cc\u03c1\u03bf\u03c2", "select": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae", "state_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", + "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf", "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2", "username": "\u039a\u03b5\u03bd\u03cc", "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2", @@ -70,6 +99,7 @@ "resource": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c3\u03c4\u03bf\u03bd \u03b9\u03c3\u03c4\u03cc\u03c4\u03bf\u03c0\u03bf \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03c4\u03b7\u03bd \u03c4\u03b9\u03bc\u03ae", "select": "\u039f\u03c1\u03af\u03b6\u03b5\u03b9 \u03c0\u03bf\u03b9\u03b1 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1 \u03b8\u03b1 \u03b1\u03bd\u03b1\u03b6\u03b7\u03c4\u03b7\u03b8\u03b5\u03af. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03bf\u03c5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03b5\u03af\u03c2 CSS \u03c4\u03bf\u03c5 Beautifulsoup \u03b3\u03b9\u03b1 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2", "state_class": "\u0397 state_class \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", + "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03bf\u03bd \u03b9\u03c3\u03c4\u03cc\u03c4\u03bf\u03c0\u03bf", "value_template": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03ad\u03bd\u03b1 \u03c0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03c0\u03b1\u03c1\u03b1\u03bb\u03b1\u03b2\u03ae \u03c4\u03b7\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", "verify_ssl": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af/\u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c4\u03b7\u03bd \u03b5\u03c0\u03b1\u03bb\u03ae\u03b8\u03b5\u03c5\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd SSL/TLS, \u03c0.\u03c7. \u03b1\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c5\u03c4\u03bf-\u03c5\u03c0\u03bf\u03b3\u03b5\u03b3\u03c1\u03b1\u03bc\u03bc\u03ad\u03bd\u03bf." } diff --git a/homeassistant/components/scrape/translations/es.json b/homeassistant/components/scrape/translations/es.json index a001498c7ef..9f893f0ee67 100644 --- a/homeassistant/components/scrape/translations/es.json +++ b/homeassistant/components/scrape/translations/es.json @@ -3,19 +3,45 @@ "abort": { "already_configured": "La cuenta ya est\u00e1 configurada" }, + "error": { + "resource_error": "No se pudieron actualizar los datos rest. Verifica tu configuraci\u00f3n" + }, "step": { + "sensor": { + "data": { + "attribute": "Atributo", + "device_class": "Clase de dispositivo", + "index": "\u00cdndice", + "name": "Nombre", + "select": "Selecciona", + "state_class": "Clase de estado", + "unit_of_measurement": "Unidad de medida", + "value_template": "Plantilla de valor" + }, + "data_description": { + "attribute": "Obtener el valor de un atributo en la etiqueta seleccionada", + "device_class": "El tipo/clase del sensor para establecer el icono en la interfaz", + "index": "Define cu\u00e1l de los elementos devueltos por el selector CSS usar", + "select": "Define qu\u00e9 etiqueta buscar. Revisa los selectores CSS de Beautifulsoup para obtener m\u00e1s informaci\u00f3n.", + "state_class": "El state_class del sensor", + "unit_of_measurement": "Elige la medici\u00f3n de temperatura o crea la suya propia", + "value_template": "Define una plantilla para obtener el estado del sensor" + } + }, "user": { "data": { "attribute": "Atributo", - "authentication": "Autenticaci\u00f3n", + "authentication": "Selecciona el m\u00e9todo de autenticaci\u00f3n", "device_class": "Clase de dispositivo", "headers": "Cabeceras", "index": "\u00cdndice", + "method": "M\u00e9todo", "name": "Nombre", "password": "Contrase\u00f1a", "resource": "Recurso", "select": "Seleccionar", "state_class": "Clase de estado", + "timeout": "Tiempo de espera", "unit_of_measurement": "Unidad de medida", "username": "Nombre de usuario", "value_template": "Plantilla de valor", @@ -30,6 +56,7 @@ "resource": "La URL del sitio web que contiene el valor.", "select": "Define qu\u00e9 etiqueta buscar. Revisa los selectores CSS de Beautifulsoup para obtener m\u00e1s informaci\u00f3n.", "state_class": "El state_class del sensor", + "timeout": "Tiempo de espera para la conexi\u00f3n al sitio web", "value_template": "Define una plantilla para obtener el estado del sensor", "verify_ssl": "Habilita/deshabilita la verificaci\u00f3n del certificado SSL/TLS, por ejemplo, si est\u00e1 autofirmado" } @@ -47,15 +74,17 @@ "init": { "data": { "attribute": "Atributo", - "authentication": "Autenticaci\u00f3n", + "authentication": "Selecciona el m\u00e9todo de autenticaci\u00f3n", "device_class": "Clase de dispositivo", "headers": "Cabeceras", "index": "\u00cdndice", + "method": "M\u00e9todo", "name": "Nombre", "password": "Contrase\u00f1a", "resource": "Recurso", "select": "Seleccionar", "state_class": "Clase de estado", + "timeout": "Tiempo de espera", "unit_of_measurement": "Unidad de medida", "username": "Nombre de usuario", "value_template": "Plantilla de valor", @@ -70,6 +99,7 @@ "resource": "La URL del sitio web que contiene el valor.", "select": "Define qu\u00e9 etiqueta buscar. Revisa los selectores CSS de Beautifulsoup para obtener m\u00e1s informaci\u00f3n.", "state_class": "El state_class del sensor", + "timeout": "Tiempo de espera para la conexi\u00f3n al sitio web", "value_template": "Define una plantilla para obtener el estado del sensor", "verify_ssl": "Habilita/deshabilita la verificaci\u00f3n del certificado SSL/TLS, por ejemplo, si est\u00e1 autofirmado" } diff --git a/homeassistant/components/scrape/translations/et.json b/homeassistant/components/scrape/translations/et.json index 7168041b566..9bfd788e86d 100644 --- a/homeassistant/components/scrape/translations/et.json +++ b/homeassistant/components/scrape/translations/et.json @@ -3,19 +3,45 @@ "abort": { "already_configured": "Kasutaja on juba seadistatud" }, + "error": { + "resource_error": "Rest p\u00e4ringu andmeid ei saanud v\u00e4rskendada. Kontrolli oma s\u00e4tteid" + }, "step": { + "sensor": { + "data": { + "attribute": "Atribuut", + "device_class": "Seadme klass", + "index": "Indeks", + "name": "Nimi", + "select": "Vali", + "state_class": "Oleku klass", + "unit_of_measurement": "M\u00f5\u00f5t\u00fchik", + "value_template": "V\u00e4\u00e4rtuse mall" + }, + "data_description": { + "attribute": "Hangi valitud sildi atribuudi v\u00e4\u00e4rtus", + "device_class": "Anduri t\u00fc\u00fcp/klass ikooni seadmiseks kasutajaliideses", + "index": "M\u00e4\u00e4rab, milliseid CSS selektoriga tagastatud elemente kasutada.", + "select": "M\u00e4\u00e4rab, millist silti otsida. Lisateavet leiad Beautifulsoup CSS-i valijatest", + "state_class": "Anduri oleku klass", + "unit_of_measurement": "Vali temperatuuri m\u00f5\u00f5tmine v\u00f5i loo oma", + "value_template": "M\u00e4\u00e4rab malli anduri oleku saamiseks" + } + }, "user": { "data": { "attribute": "Atribuut", - "authentication": "Tuvastamine", + "authentication": "Vali tuvastusmeetod", "device_class": "Seadme klass", "headers": "P\u00e4ised", "index": "Indeks", + "method": "Meetod", "name": "Nimi", "password": "Salas\u00f5na", "resource": "Resurss", "select": "Vali", "state_class": "Oleku klass", + "timeout": "Ajal\u00f5pp", "unit_of_measurement": "M\u00f5\u00f5t\u00fchik", "username": "Kasutajanimi", "value_template": "V\u00e4\u00e4rtuse mall", @@ -30,6 +56,7 @@ "resource": "V\u00e4\u00e4rtust sisaldava veebisaidi URL", "select": "M\u00e4\u00e4rab, millist silti otsida. Lisateavet leiad Beautifulsoup CSS-i valijatest", "state_class": "Anduri oleku klass", + "timeout": "Veebilehe ajal\u00f5pp", "value_template": "M\u00e4\u00e4rab malli anduri oleku saamiseks", "verify_ssl": "Lubab/keelab SSL/TLS-sertifikaadi kontrollimise, n\u00e4iteks kui see on ise allkirjastatud" } @@ -51,11 +78,13 @@ "device_class": "Seadme klss", "headers": "P\u00e4ised", "index": "Indeks", + "method": "Meetod", "name": "", "password": "Salas\u00f5na", "resource": "Resurss", "select": "Vali", "state_class": "Oleku klass", + "timeout": "Ajal\u00f5pp", "unit_of_measurement": "M\u00f5\u00f5t\u00fchik", "username": "", "value_template": "V\u00e4\u00e4rtuse mall", @@ -70,6 +99,7 @@ "resource": "Veebilehe URL ei sisalda soovitud v\u00e4\u00e4rtusi", "select": "M\u00e4\u00e4rab otsitava v\u00f5tmes\u00f5na. Vaata Beatifulsoup CSS valimeid", "state_class": "Anduri olekuklass", + "timeout": "Veebilehe ajal\u00f5pp", "value_template": "M\u00e4\u00e4rab anduri oleku saamiseks vajaliku malli", "verify_ssl": "Lubab v\u00f5i keelab SSL/TLS serdi tuvastamise n\u00e4iteks juhul kui sert on ise allkirjastatud" } diff --git a/homeassistant/components/scrape/translations/fr.json b/homeassistant/components/scrape/translations/fr.json index f68ce5808b7..86257268448 100644 --- a/homeassistant/components/scrape/translations/fr.json +++ b/homeassistant/components/scrape/translations/fr.json @@ -4,18 +4,32 @@ "already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9" }, "step": { + "sensor": { + "data": { + "attribute": "Attribut", + "index": "Index", + "name": "Nom", + "state_class": "Classe d'\u00e9tat", + "unit_of_measurement": "Unit\u00e9 de mesure" + }, + "data_description": { + "state_class": "La state_class du capteur" + } + }, "user": { "data": { "attribute": "Attribut", - "authentication": "Authentification", + "authentication": "S\u00e9lectionner la m\u00e9thode d\u2019authentification", "device_class": "Classe d'appareil", "headers": "En-t\u00eates", "index": "Index", + "method": "M\u00e9thode", "name": "Nom", "password": "Mot de passe", "resource": "Ressource", "select": "S\u00e9lectionner", "state_class": "Classe d'\u00e9tat", + "timeout": "D\u00e9lai d\u2019expiration", "unit_of_measurement": "Unit\u00e9 de mesure", "username": "Nom d'utilisateur", "value_template": "Mod\u00e8le de valeur", @@ -30,6 +44,7 @@ "resource": "L'URL du site web qui contient la valeur", "select": "D\u00e9finit la balise \u00e0 rechercher. Consultez les s\u00e9lecteurs CSS de Beautifulsoup pour plus de d\u00e9tails", "state_class": "La state_class du capteur", + "timeout": "D\u00e9lai d\u2019expiration pour la connexion au site Web", "value_template": "D\u00e9finit un mod\u00e8le pour obtenir l'\u00e9tat du capteur", "verify_ssl": "Active ou d\u00e9sactive la v\u00e9rification du certificat SSL/TLS, par exemple s'il est auto-sign\u00e9" } @@ -41,15 +56,17 @@ "init": { "data": { "attribute": "Attribut", - "authentication": "Authentification", + "authentication": "S\u00e9lectionner la m\u00e9thode d\u2019authentification", "device_class": "Classe d'appareil", "headers": "En-t\u00eates", "index": "Index", + "method": "M\u00e9thode", "name": "Nom", "password": "Mot de passe", "resource": "Ressource", "select": "S\u00e9lectionner", "state_class": "Classe d'\u00e9tat", + "timeout": "D\u00e9lai d\u2019expiration", "unit_of_measurement": "Unit\u00e9 de mesure", "username": "Nom d'utilisateur", "value_template": "Mod\u00e8le de valeur", @@ -64,6 +81,7 @@ "resource": "L'URL du site web qui contient la valeur", "select": "D\u00e9finit la balise \u00e0 rechercher. Consultez les s\u00e9lecteurs CSS de Beautifulsoup pour plus de d\u00e9tails", "state_class": "La state_class du capteur", + "timeout": "D\u00e9lai d\u2019expiration pour la connexion au site Web", "value_template": "D\u00e9finit un mod\u00e8le pour obtenir l'\u00e9tat du capteur", "verify_ssl": "Active ou d\u00e9sactive la v\u00e9rification du certificat SSL/TLS, par exemple s'il est auto-sign\u00e9" } diff --git a/homeassistant/components/scrape/translations/id.json b/homeassistant/components/scrape/translations/id.json index 0ee76592dfe..b4c03197ecb 100644 --- a/homeassistant/components/scrape/translations/id.json +++ b/homeassistant/components/scrape/translations/id.json @@ -4,18 +4,41 @@ "already_configured": "Akun sudah dikonfigurasi" }, "step": { + "sensor": { + "data": { + "attribute": "Atribut", + "device_class": "Kelas Perangkat", + "index": "Indeks", + "name": "Nama", + "select": "Pilih", + "state_class": "Kelas Status", + "unit_of_measurement": "Satuan Pengukuran", + "value_template": "Nilai Templat" + }, + "data_description": { + "attribute": "Dapatkan nilai atribut pada tag yang dipilih", + "device_class": "Jenis/kelas sensor untuk mengatur ikon di antarmuka", + "index": "Menentukan elemen mana yang dikembalikan oleh selektor CSS untuk digunakan", + "select": "Menentukan tag yang harus dicari. Periksa selektor CSS Beautifulsoup untuk melihat detailnya", + "state_class": "Nilai state_class dari sensor", + "unit_of_measurement": "Pilih pengukuran suhu atau buat sendiri", + "value_template": "Mendefinisikan templat untuk mendapatkan status sensor" + } + }, "user": { "data": { "attribute": "Atribut", - "authentication": "Autentikasi", + "authentication": "Pilih metode autentikasi", "device_class": "Kelas Perangkat", "headers": "Header", "index": "Indeks", + "method": "Metode", "name": "Nama", "password": "Kata Sandi", "resource": "Sumber Daya", "select": "Pilihan", "state_class": "Kelas Status", + "timeout": "Tenggang waktu", "unit_of_measurement": "Satuan Pengukuran", "username": "Nama Pengguna", "value_template": "Nilai Templat", @@ -30,6 +53,7 @@ "resource": "URL ke situs web yang mengandung nilai", "select": "Menentukan tag yang harus dicari. Periksa selektor CSS Beautifulsoup untuk melihat detailnya", "state_class": "Nilai state_class dari sensor", + "timeout": "Tenggang waktu untuk koneksi ke situs web", "value_template": "Mendefinisikan templat untuk mendapatkan status sensor", "verify_ssl": "Mengaktifkan/menonaktifkan verifikasi sertifikat SSL/TLS, misalnya jika sertifikat ditandatangani sendiri" } @@ -47,15 +71,17 @@ "init": { "data": { "attribute": "Atribut", - "authentication": "Autentikasi", + "authentication": "Pilih metode autentikasi", "device_class": "Kelas Perangkat", "headers": "Header", "index": "Indeks", + "method": "Metode", "name": "Nama", "password": "Kata Sandi", "resource": "Sumber Daya", "select": "Pilihan", "state_class": "Kelas Status", + "timeout": "Tenggang waktu", "unit_of_measurement": "Satuan Pengukuran", "username": "Nama Pengguna", "value_template": "Nilai Templat", @@ -70,6 +96,7 @@ "resource": "URL ke situs web yang mengandung nilai", "select": "Menentukan tag yang harus dicari. Periksa selektor CSS Beautifulsoup untuk melihat detailnya", "state_class": "Nilai state_class dari sensor", + "timeout": "Tenggang waktu untuk koneksi ke situs web", "value_template": "Mendefinisikan templat untuk mendapatkan status sensor", "verify_ssl": "Mengaktifkan/menonaktifkan verifikasi sertifikat SSL/TLS, misalnya jika sertifikat ditandatangani sendiri" } diff --git a/homeassistant/components/scrape/translations/ru.json b/homeassistant/components/scrape/translations/ru.json index 2d014592e85..139b006ce69 100644 --- a/homeassistant/components/scrape/translations/ru.json +++ b/homeassistant/components/scrape/translations/ru.json @@ -3,19 +3,45 @@ "abort": { "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant." }, + "error": { + "resource_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." + }, "step": { + "sensor": { + "data": { + "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", + "device_class": "\u041a\u043b\u0430\u0441\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", + "index": "\u0418\u043d\u0434\u0435\u043a\u0441", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "select": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c", + "state_class": "\u041a\u043b\u0430\u0441\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f", + "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f", + "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f" + }, + "data_description": { + "attribute": "\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u0442\u0435\u0433\u0430.", + "device_class": "\u0422\u0438\u043f/\u043a\u043b\u0430\u0441\u0441 \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435.", + "index": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0438\u0437 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u043c CSS \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c.", + "select": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0442\u0435\u0433 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 CSS Beautifulsoup.", + "state_class": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 state_class \u0434\u043b\u044f \u0441\u0435\u043d\u0441\u043e\u0440\u0430.", + "unit_of_measurement": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u0435 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b \u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0441\u0432\u043e\u0435 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435", + "value_template": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u0430." + } + }, "user": { "data": { "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", - "authentication": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", + "authentication": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438", "device_class": "\u041a\u043b\u0430\u0441\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", "headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", "index": "\u0418\u043d\u0434\u0435\u043a\u0441", + "method": "\u041c\u0435\u0442\u043e\u0434", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "resource": "\u0420\u0435\u0441\u0443\u0440\u0441", "select": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c", "state_class": "\u041a\u043b\u0430\u0441\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f", + "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442", "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f", @@ -30,6 +56,7 @@ "resource": "URL-\u0430\u0434\u0440\u0435\u0441 \u0432\u0435\u0431-\u0441\u0430\u0439\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.", "select": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0442\u0435\u0433 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 CSS Beautifulsoup.", "state_class": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 state_class \u0434\u043b\u044f \u0441\u0435\u043d\u0441\u043e\u0440\u0430.", + "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0441\u0430\u0439\u0442\u0443", "value_template": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u0430.", "verify_ssl": "\u0412\u043a\u043b\u044e\u0447\u0430\u0435\u0442/\u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 SSL/TLS. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0435\u0441\u043b\u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u0441\u0430\u043c\u043e\u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439." } @@ -41,15 +68,17 @@ "init": { "data": { "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", - "authentication": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", + "authentication": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438", "device_class": "\u041a\u043b\u0430\u0441\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", "headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", "index": "\u0418\u043d\u0434\u0435\u043a\u0441", + "method": "\u041c\u0435\u0442\u043e\u0434", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "resource": "\u0420\u0435\u0441\u0443\u0440\u0441", "select": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c", "state_class": "\u041a\u043b\u0430\u0441\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f", + "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442", "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f", @@ -64,6 +93,7 @@ "resource": "URL-\u0430\u0434\u0440\u0435\u0441 \u0432\u0435\u0431-\u0441\u0430\u0439\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.", "select": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0442\u0435\u0433 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 CSS Beautifulsoup.", "state_class": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 state_class \u0434\u043b\u044f \u0441\u0435\u043d\u0441\u043e\u0440\u0430.", + "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0441\u0430\u0439\u0442\u0443", "value_template": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u0430.", "verify_ssl": "\u0412\u043a\u043b\u044e\u0447\u0430\u0435\u0442/\u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 SSL/TLS. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0435\u0441\u043b\u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u0441\u0430\u043c\u043e\u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439." } diff --git a/homeassistant/components/scrape/translations/sk.json b/homeassistant/components/scrape/translations/sk.json index 9ff8487afaa..f24cd357ee8 100644 --- a/homeassistant/components/scrape/translations/sk.json +++ b/homeassistant/components/scrape/translations/sk.json @@ -1,9 +1,22 @@ { "config": { "step": { + "sensor": { + "data": { + "attribute": "Atrib\u00fat", + "name": "N\u00e1zov", + "unit_of_measurement": "Jednotka merania", + "value_template": "\u0160abl\u00f3na hodnoty" + }, + "data_description": { + "unit_of_measurement": "Vyberte si meranie teploty alebo si vytvorte vlastn\u00e9" + } + }, "user": { "data": { - "password": "Heslo" + "device_class": "Trieda zariadenia", + "password": "Heslo", + "unit_of_measurement": "Jednotka merania" } } } @@ -12,7 +25,9 @@ "step": { "init": { "data": { - "password": "Heslo" + "device_class": "Trieda zariadenia", + "password": "Heslo", + "unit_of_measurement": "Jednotka merania" } } } diff --git a/homeassistant/components/screenlogic/translations/sk.json b/homeassistant/components/screenlogic/translations/sk.json index 5619ebff744..492389f5677 100644 --- a/homeassistant/components/screenlogic/translations/sk.json +++ b/homeassistant/components/screenlogic/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name}", "step": { "gateway_entry": { diff --git a/homeassistant/components/season/translations/sensor.sk.json b/homeassistant/components/season/translations/sensor.sk.json index 6a1f5dd293b..548b77defd0 100644 --- a/homeassistant/components/season/translations/sensor.sk.json +++ b/homeassistant/components/season/translations/sensor.sk.json @@ -1,7 +1,15 @@ { "state": { + "season__season": { + "autumn": "Jese\u0148", + "spring": "Jar", + "winter": "Zima" + }, "season__season__": { - "spring": "Jar" + "autumn": "Jese\u0148", + "spring": "Jar", + "summer": "Leto", + "winter": "Zima" } } } \ No newline at end of file diff --git a/homeassistant/components/sense/translations/sk.json b/homeassistant/components/sense/translations/sk.json index 950756c502b..d6121453b25 100644 --- a/homeassistant/components/sense/translations/sk.json +++ b/homeassistant/components/sense/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/senseme/translations/sk.json b/homeassistant/components/senseme/translations/sk.json index 9dae060b4cc..2f000bd7e8d 100644 --- a/homeassistant/components/senseme/translations/sk.json +++ b/homeassistant/components/senseme/translations/sk.json @@ -1,11 +1,17 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165.", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, "flow_title": "{name} - {model} ({host})", "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {name} - {model} ({host})?" + }, "manual": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/sensibo/translations/sensor.sk.json b/homeassistant/components/sensibo/translations/sensor.sk.json new file mode 100644 index 00000000000..badb719530c --- /dev/null +++ b/homeassistant/components/sensibo/translations/sensor.sk.json @@ -0,0 +1,13 @@ +{ + "state": { + "sensibo__sensitivity": { + "n": "Norm\u00e1lne", + "s": "Citliv\u00e9" + }, + "sensibo__smart_type": { + "feelslike": "Pocitovo", + "humidity": "Vlhkos\u0165", + "temperature": "Teplota" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/sk.json b/homeassistant/components/sensibo/translations/sk.json index d2401d8b7a3..630d49c3423 100644 --- a/homeassistant/components/sensibo/translations/sk.json +++ b/homeassistant/components/sensibo/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "error": { - "incorrect_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d pre vybran\u00fd \u00fa\u010det" + "incorrect_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d pre vybran\u00fd \u00fa\u010det", + "no_devices": "Nena\u0161li sa \u017eiadne zariadenia" }, "step": { "user": { diff --git a/homeassistant/components/sensor/translations/sk.json b/homeassistant/components/sensor/translations/sk.json index f86600559a4..133573ea24e 100644 --- a/homeassistant/components/sensor/translations/sk.json +++ b/homeassistant/components/sensor/translations/sk.json @@ -5,6 +5,8 @@ }, "trigger_type": { "current": "{entity_name} pr\u00fad sa zmen\u00ed", + "temperature": "Zmena teploty {entity_name}", + "value": "Zmena hodnoty {entity_name}", "voltage": "{entity_name} zmen\u00ed nap\u00e4tie" } }, diff --git a/homeassistant/components/sensorpro/translations/sk.json b/homeassistant/components/sensorpro/translations/sk.json index ba3b7cee831..8273d877c92 100644 --- a/homeassistant/components/sensorpro/translations/sk.json +++ b/homeassistant/components/sensorpro/translations/sk.json @@ -1,7 +1,22 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "not_supported": "Zariadenie nie je podporovan\u00e9" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, + "user": { + "data": { + "address": "Zaradenie" + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" + } } } } \ No newline at end of file diff --git a/homeassistant/components/sensorpush/translations/sk.json b/homeassistant/components/sensorpush/translations/sk.json new file mode 100644 index 00000000000..b121bbc35a3 --- /dev/null +++ b/homeassistant/components/sensorpush/translations/sk.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, + "user": { + "data": { + "address": "Zaradenie" + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/simplepush/translations/sk.json b/homeassistant/components/simplepush/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/simplepush/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/skybell/translations/sk.json b/homeassistant/components/skybell/translations/sk.json index abc99d4bf43..a262779e103 100644 --- a/homeassistant/components/skybell/translations/sk.json +++ b/homeassistant/components/skybell/translations/sk.json @@ -4,7 +4,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Aktualizujte svoje heslo pre {email}" }, "user": { "data": { diff --git a/homeassistant/components/sma/translations/sk.json b/homeassistant/components/sma/translations/sk.json index 4b759c272ce..1e05cf728e0 100644 --- a/homeassistant/components/sma/translations/sk.json +++ b/homeassistant/components/sma/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "error": { diff --git a/homeassistant/components/smappee/translations/sk.json b/homeassistant/components/smappee/translations/sk.json index 995bc004013..f91e0502cc3 100644 --- a/homeassistant/components/smappee/translations/sk.json +++ b/homeassistant/components/smappee/translations/sk.json @@ -1,5 +1,9 @@ { "config": { + "abort": { + "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "flow_title": "{name}", "step": { "local": { "data": { diff --git a/homeassistant/components/smart_meter_texas/translations/sk.json b/homeassistant/components/smart_meter_texas/translations/sk.json index 1b1e671c054..8f06d99549b 100644 --- a/homeassistant/components/smart_meter_texas/translations/sk.json +++ b/homeassistant/components/smart_meter_texas/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/sms/translations/sk.json b/homeassistant/components/sms/translations/sk.json index c1f2297ec7d..2a411e71b3f 100644 --- a/homeassistant/components/sms/translations/sk.json +++ b/homeassistant/components/sms/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/snooz/translations/el.json b/homeassistant/components/snooz/translations/el.json index ca4174b137a..034770f7ce0 100644 --- a/homeassistant/components/snooz/translations/el.json +++ b/homeassistant/components/snooz/translations/el.json @@ -1,20 +1,26 @@ { "config": { "abort": { - "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf" }, "flow_title": "{name}", "progress": { "wait_for_pairing_mode": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7, \u03b2\u03ac\u03bb\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c3\u03b5 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03cd\u03b6\u03b5\u03c5\u03be\u03b7\u03c2. \n\n ### \u03a0\u03ce\u03c2 \u03bd\u03b1 \u03b5\u03b9\u03c3\u03ad\u03bb\u03b8\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03cd\u03b6\u03b5\u03c5\u03be\u03b7\u03c2\n 1. \u0391\u03bd\u03b1\u03b3\u03ba\u03b1\u03c3\u03c4\u03b9\u03ba\u03ae \u03ad\u03be\u03bf\u03b4\u03bf\u03c2 \u03b1\u03c0\u03cc \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ad\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac SNOOZ.\n 2. \u03a0\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b1\u03b9 \u03ba\u03c1\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 \u03c0\u03b1\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf \u03c4\u03bf \u03ba\u03bf\u03c5\u03bc\u03c0\u03af \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae. \u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03cc\u03c4\u03b1\u03bd \u03b1\u03c1\u03c7\u03af\u03c3\u03bf\u03c5\u03bd \u03bd\u03b1 \u03b1\u03bd\u03b1\u03b2\u03bf\u03c3\u03b2\u03ae\u03bd\u03bf\u03c5\u03bd \u03c4\u03b1 \u03c6\u03ce\u03c4\u03b1 (\u03c0\u03b5\u03c1\u03af\u03c0\u03bf\u03c5 5 \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1)." }, "step": { + "bluetooth_confirm": { + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name};" + }, "pairing_timeout": { "description": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b5\u03b9\u03c3\u03ae\u03bb\u03b8\u03b5 \u03c3\u03b5 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03cd\u03b6\u03b5\u03c5\u03be\u03b7\u03c2. \u039a\u03ac\u03bd\u03c4\u03b5 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03b7\u03bd \u03a5\u03c0\u03bf\u03b2\u03bf\u03bb\u03ae \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac. \n\n ### \u0391\u03bd\u03c4\u03b9\u03bc\u03b5\u03c4\u03ce\u03c0\u03b9\u03c3\u03b7 \u03c0\u03c1\u03bf\u03b2\u03bb\u03b7\u03bc\u03ac\u03c4\u03c9\u03bd\n 1. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03b7 \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac.\n 2. \u0391\u03c0\u03bf\u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b3\u03b9\u03b1 5 \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1 \u03ba\u03b1\u03b9 \u03bc\u03b5\u03c4\u03ac \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03be\u03b1\u03bd\u03ac." }, "user": { "data": { "address": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" - } + }, + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b3\u03b9\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7" } } } diff --git a/homeassistant/components/solaredge/translations/sk.json b/homeassistant/components/solaredge/translations/sk.json index 7c6659c2e0b..8d2e3cff838 100644 --- a/homeassistant/components/solaredge/translations/sk.json +++ b/homeassistant/components/solaredge/translations/sk.json @@ -1,6 +1,10 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" }, "step": { diff --git a/homeassistant/components/solarlog/translations/sk.json b/homeassistant/components/solarlog/translations/sk.json index 842ff61bd79..cafa14fff99 100644 --- a/homeassistant/components/solarlog/translations/sk.json +++ b/homeassistant/components/solarlog/translations/sk.json @@ -1,5 +1,11 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/songpal/translations/sk.json b/homeassistant/components/songpal/translations/sk.json index eaec481025c..fb1495d284c 100644 --- a/homeassistant/components/songpal/translations/sk.json +++ b/homeassistant/components/songpal/translations/sk.json @@ -1,5 +1,10 @@ { "config": { - "flow_title": "{name} ({host})" + "flow_title": "{name} ({host})", + "step": { + "init": { + "description": "Chcete nastavi\u0165 {name} ({host})?" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sonos/translations/sk.json b/homeassistant/components/sonos/translations/sk.json new file mode 100644 index 00000000000..69d8d942494 --- /dev/null +++ b/homeassistant/components/sonos/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/soundtouch/translations/sk.json b/homeassistant/components/soundtouch/translations/sk.json index 842ff61bd79..3c3d27a6689 100644 --- a/homeassistant/components/soundtouch/translations/sk.json +++ b/homeassistant/components/soundtouch/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/speedtestdotnet/translations/sk.json b/homeassistant/components/speedtestdotnet/translations/sk.json new file mode 100644 index 00000000000..838b602b6df --- /dev/null +++ b/homeassistant/components/speedtestdotnet/translations/sk.json @@ -0,0 +1,12 @@ +{ + "options": { + "step": { + "init": { + "data": { + "manual": "Zak\u00e1za\u0165 automatick\u00fa aktualiz\u00e1ciu", + "scan_interval": "Frekvencia aktualiz\u00e1cie (min\u00faty)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/sk.json b/homeassistant/components/sql/translations/sk.json new file mode 100644 index 00000000000..474e7ffebe9 --- /dev/null +++ b/homeassistant/components/sql/translations/sk.json @@ -0,0 +1,26 @@ +{ + "config": { + "step": { + "user": { + "data": { + "unit_of_measurement": "Mern\u00e1 jednotka" + }, + "data_description": { + "unit_of_measurement": "Jednotka miery (volite\u013en\u00e9)" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "unit_of_measurement": "Mern\u00e1 jednotka" + }, + "data_description": { + "unit_of_measurement": "Jednotka miery (volite\u013en\u00e9)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/squeezebox/translations/sk.json b/homeassistant/components/squeezebox/translations/sk.json index a9eaff35fa5..b3bce3f6d95 100644 --- a/homeassistant/components/squeezebox/translations/sk.json +++ b/homeassistant/components/squeezebox/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/steamist/translations/sk.json b/homeassistant/components/steamist/translations/sk.json index c72bfbf48f2..88164bb680c 100644 --- a/homeassistant/components/steamist/translations/sk.json +++ b/homeassistant/components/steamist/translations/sk.json @@ -1,9 +1,15 @@ { "config": { "abort": { - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "flow_title": "{name} ({ipaddress})", "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {name} ({ip_address})?" + }, "pick_device": { "data": { "device": "Zariadenie" diff --git a/homeassistant/components/switch/translations/sk.json b/homeassistant/components/switch/translations/sk.json index a1095c6469f..297ea3159d0 100644 --- a/homeassistant/components/switch/translations/sk.json +++ b/homeassistant/components/switch/translations/sk.json @@ -5,6 +5,7 @@ "turn_on": "Zapn\u00fa\u0165 {entity_name}" }, "trigger_type": { + "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9", "turned_off": "{entity_name} vypnut\u00e1", "turned_on": "{entity_name} zapnut\u00e1" } diff --git a/homeassistant/components/switchbot/translations/sk.json b/homeassistant/components/switchbot/translations/sk.json index b9e9b852d8a..75880ae9770 100644 --- a/homeassistant/components/switchbot/translations/sk.json +++ b/homeassistant/components/switchbot/translations/sk.json @@ -2,14 +2,19 @@ "config": { "abort": { "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_unconfigured_devices": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({address})", "step": { + "confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "password": { "data": { "password": "Heslo" - } + }, + "description": "Zariadenie {name} vy\u017eaduje heslo" }, "user": { "data": { @@ -20,5 +25,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "update_time": "\u010cas medzi aktualiz\u00e1ciami (sekundy)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/synology_dsm/translations/sk.json b/homeassistant/components/synology_dsm/translations/sk.json index 59fbd6c1f5d..e021f42bd88 100644 --- a/homeassistant/components/synology_dsm/translations/sk.json +++ b/homeassistant/components/synology_dsm/translations/sk.json @@ -13,7 +13,8 @@ "data": { "password": "Heslo", "port": "Port" - } + }, + "description": "Chcete nastavi\u0165 {name} ({host})?" }, "reauth_confirm": { "data": { diff --git a/homeassistant/components/system_bridge/translations/sk.json b/homeassistant/components/system_bridge/translations/sk.json index 4a6f823bbe6..b099280098f 100644 --- a/homeassistant/components/system_bridge/translations/sk.json +++ b/homeassistant/components/system_bridge/translations/sk.json @@ -1,12 +1,14 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, + "flow_title": "{name}", "step": { "authenticate": { "data": { diff --git a/homeassistant/components/tado/translations/sk.json b/homeassistant/components/tado/translations/sk.json index 1b1e671c054..8f06d99549b 100644 --- a/homeassistant/components/tado/translations/sk.json +++ b/homeassistant/components/tado/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/tankerkoenig/translations/sk.json b/homeassistant/components/tankerkoenig/translations/sk.json index 06c74b52725..82a41b014ca 100644 --- a/homeassistant/components/tankerkoenig/translations/sk.json +++ b/homeassistant/components/tankerkoenig/translations/sk.json @@ -7,5 +7,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Interval aktualiz\u00e1cie" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/sk.json b/homeassistant/components/tesla_wall_connector/translations/sk.json index e909344d91b..951b68ac4c7 100644 --- a/homeassistant/components/tesla_wall_connector/translations/sk.json +++ b/homeassistant/components/tesla_wall_connector/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{serial_number} ({host})", "step": { "user": { diff --git a/homeassistant/components/tolo/translations/sk.json b/homeassistant/components/tolo/translations/sk.json index aaad66e6729..835f107f7af 100644 --- a/homeassistant/components/tolo/translations/sk.json +++ b/homeassistant/components/tolo/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/tomorrowio/translations/sensor.sk.json b/homeassistant/components/tomorrowio/translations/sensor.sk.json index 3dd3dede27b..b4772750e4e 100644 --- a/homeassistant/components/tomorrowio/translations/sensor.sk.json +++ b/homeassistant/components/tomorrowio/translations/sensor.sk.json @@ -1,7 +1,9 @@ { "state": { "tomorrowio__pollen_index": { - "low": "N\u00edzka" + "high": "Vysok\u00fd", + "low": "N\u00edzka", + "medium": "Stredn\u00fd" } } } \ No newline at end of file diff --git a/homeassistant/components/tplink/translations/sk.json b/homeassistant/components/tplink/translations/sk.json index e4aaec5b474..a68579f2bf8 100644 --- a/homeassistant/components/tplink/translations/sk.json +++ b/homeassistant/components/tplink/translations/sk.json @@ -1,7 +1,14 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, "flow_title": "{name} {model} ({host})", "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {name} {model} ({host})?" + }, "pick_device": { "data": { "device": "Zariadenie" diff --git a/homeassistant/components/tractive/translations/sensor.sk.json b/homeassistant/components/tractive/translations/sensor.sk.json new file mode 100644 index 00000000000..547d8fcc7c9 --- /dev/null +++ b/homeassistant/components/tractive/translations/sensor.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "tractive__tracker_state": { + "system_startup": "Spustenie syst\u00e9mu" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/sk.json b/homeassistant/components/tractive/translations/sk.json index bf7ebd86a62..7159735b925 100644 --- a/homeassistant/components/tractive/translations/sk.json +++ b/homeassistant/components/tractive/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/transmission/translations/sk.json b/homeassistant/components/transmission/translations/sk.json index f01e6c74867..586008f54cb 100644 --- a/homeassistant/components/transmission/translations/sk.json +++ b/homeassistant/components/transmission/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie", "name_exists": "N\u00e1zov u\u017e existuje" @@ -19,5 +22,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Frekvencia aktualiz\u00e1cie" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/sensor.sk.json b/homeassistant/components/tuya/translations/sensor.sk.json index 4f80ab106ad..c1b9162966d 100644 --- a/homeassistant/components/tuya/translations/sensor.sk.json +++ b/homeassistant/components/tuya/translations/sensor.sk.json @@ -1,7 +1,13 @@ { "state": { "tuya__status": { - "boiling_temp": "Teplota varu" + "boiling_temp": "Teplota varu", + "cooling": "Chladenie", + "heating": "Vykurovanie", + "heating_temp": "Teplota vykurovania", + "reserve_1": "Rezerva 1", + "reserve_2": "Rezerva 2", + "reserve_3": "Rezerva 3" } } } \ No newline at end of file diff --git a/homeassistant/components/twinkly/translations/sk.json b/homeassistant/components/twinkly/translations/sk.json index 842ff61bd79..e29eb15ea62 100644 --- a/homeassistant/components/twinkly/translations/sk.json +++ b/homeassistant/components/twinkly/translations/sk.json @@ -1,6 +1,12 @@ { "config": { + "abort": { + "device_exists": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {name} - {model} ({host})?" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/unifiprotect/translations/sk.json b/homeassistant/components/unifiprotect/translations/sk.json index c36677b0ed8..4668f4f8d07 100644 --- a/homeassistant/components/unifiprotect/translations/sk.json +++ b/homeassistant/components/unifiprotect/translations/sk.json @@ -3,6 +3,7 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { diff --git a/homeassistant/components/update/translations/sk.json b/homeassistant/components/update/translations/sk.json new file mode 100644 index 00000000000..76ac84624ef --- /dev/null +++ b/homeassistant/components/update/translations/sk.json @@ -0,0 +1,9 @@ +{ + "device_automation": { + "trigger_type": { + "changed_states": "Dostupnos\u0165 aktualiz\u00e1cie {entity_name} sa zmenila", + "turned_off": "{entity_name} sa stalo aktu\u00e1lnym" + } + }, + "title": "Aktualizova\u0165" +} \ No newline at end of file diff --git a/homeassistant/components/upnp/translations/sk.json b/homeassistant/components/upnp/translations/sk.json index ebedbbac1b5..ae6b0093d7f 100644 --- a/homeassistant/components/upnp/translations/sk.json +++ b/homeassistant/components/upnp/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/utility_meter/translations/sk.json b/homeassistant/components/utility_meter/translations/sk.json new file mode 100644 index 00000000000..33f4006cee6 --- /dev/null +++ b/homeassistant/components/utility_meter/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "source": "Vstupn\u00fd sn\u00edma\u010d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/velbus/translations/sk.json b/homeassistant/components/velbus/translations/sk.json new file mode 100644 index 00000000000..bc7287c7640 --- /dev/null +++ b/homeassistant/components/velbus/translations/sk.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/venstar/translations/sk.json b/homeassistant/components/venstar/translations/sk.json index 9efe27e4e38..c59db6e13d4 100644 --- a/homeassistant/components/venstar/translations/sk.json +++ b/homeassistant/components/venstar/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/version/translations/sk.json b/homeassistant/components/version/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/version/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vilfo/translations/sk.json b/homeassistant/components/vilfo/translations/sk.json index 519a32c6735..566ed69ba48 100644 --- a/homeassistant/components/vilfo/translations/sk.json +++ b/homeassistant/components/vilfo/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/vizio/translations/sk.json b/homeassistant/components/vizio/translations/sk.json index 4839b5767ba..2b36ca0550c 100644 --- a/homeassistant/components/vizio/translations/sk.json +++ b/homeassistant/components/vizio/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/vlc_telnet/translations/sk.json b/homeassistant/components/vlc_telnet/translations/sk.json index 6c6301cd13a..0a5cf6b52a8 100644 --- a/homeassistant/components/vlc_telnet/translations/sk.json +++ b/homeassistant/components/vlc_telnet/translations/sk.json @@ -12,7 +12,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Zadajte spr\u00e1vne heslo pre hostite\u013ea: {host}" }, "user": { "data": { diff --git a/homeassistant/components/volumio/translations/sk.json b/homeassistant/components/volumio/translations/sk.json index 33d93b62475..142f47cf1fd 100644 --- a/homeassistant/components/volumio/translations/sk.json +++ b/homeassistant/components/volumio/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/wallbox/translations/sk.json b/homeassistant/components/wallbox/translations/sk.json index bd3a9e24cf1..b557456a8fe 100644 --- a/homeassistant/components/wallbox/translations/sk.json +++ b/homeassistant/components/wallbox/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/watttime/translations/sk.json b/homeassistant/components/watttime/translations/sk.json index 9becbdda335..546d563046d 100644 --- a/homeassistant/components/watttime/translations/sk.json +++ b/homeassistant/components/watttime/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/wemo/translations/sk.json b/homeassistant/components/wemo/translations/sk.json new file mode 100644 index 00000000000..69d8d942494 --- /dev/null +++ b/homeassistant/components/wemo/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wilight/translations/sk.json b/homeassistant/components/wilight/translations/sk.json new file mode 100644 index 00000000000..c9177d7b5d2 --- /dev/null +++ b/homeassistant/components/wilight/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "flow_title": "{name}" + } +} \ No newline at end of file diff --git a/homeassistant/components/wiz/translations/sk.json b/homeassistant/components/wiz/translations/sk.json index 27fdc258e0a..a0cedfaf1bb 100644 --- a/homeassistant/components/wiz/translations/sk.json +++ b/homeassistant/components/wiz/translations/sk.json @@ -1,10 +1,15 @@ { "config": { "abort": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, "flow_title": "{name} ({host})", "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {name} ({host})?" + }, "pick_device": { "data": { "device": "Zariadenie" diff --git a/homeassistant/components/wled/translations/select.sk.json b/homeassistant/components/wled/translations/select.sk.json new file mode 100644 index 00000000000..fa75e69abe9 --- /dev/null +++ b/homeassistant/components/wled/translations/select.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "wled__live_override": { + "2": "K\u00fdm sa zariadenie nere\u0161tartuje" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wled/translations/sk.json b/homeassistant/components/wled/translations/sk.json index aaad66e6729..835f107f7af 100644 --- a/homeassistant/components/wled/translations/sk.json +++ b/homeassistant/components/wled/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/wolflink/translations/sensor.sk.json b/homeassistant/components/wolflink/translations/sensor.sk.json new file mode 100644 index 00000000000..dae86d4ec26 --- /dev/null +++ b/homeassistant/components/wolflink/translations/sensor.sk.json @@ -0,0 +1,15 @@ +{ + "state": { + "wolflink__state": { + "cooling": "Chladenie", + "eco": "Eco", + "externe_deaktivierung": "Extern\u00e1 deaktiv\u00e1cia", + "fernschalter_ein": "Dia\u013ekov\u00e9 ovl\u00e1danie je povolen\u00e9", + "frostschutz": "Ochrana pred mrazom", + "gasdruck": "Tlak plynu", + "heizung": "Vykurovanie", + "initialisierung": "Inicializ\u00e1cia", + "kalibration": "Kalibr\u00e1cia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/sk.json b/homeassistant/components/ws66i/translations/sk.json new file mode 100644 index 00000000000..70e5f25b908 --- /dev/null +++ b/homeassistant/components/ws66i/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "step": { + "user": { + "title": "Pripojte sa k zariadeniu" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xiaomi_ble/translations/sk.json b/homeassistant/components/xiaomi_ble/translations/sk.json new file mode 100644 index 00000000000..8444b9fbeae --- /dev/null +++ b/homeassistant/components/xiaomi_ble/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/el.json b/homeassistant/components/xiaomi_miio/translations/el.json index 48f55af9246..779d71d14ff 100644 --- a/homeassistant/components/xiaomi_miio/translations/el.json +++ b/homeassistant/components/xiaomi_miio/translations/el.json @@ -5,7 +5,8 @@ "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", "incomplete_info": "\u0395\u03bb\u03bb\u03b9\u03c0\u03b5\u03af\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2, \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c0\u03b1\u03c1\u03b1\u03c3\u03c7\u03b5\u03b8\u03b5\u03af \u03c5\u03c0\u03bf\u03b4\u03bf\u03c7\u03ad\u03b1\u03c2 \u03ae \u03ba\u03bf\u03c5\u03c0\u03cc\u03bd\u03b9.", "not_xiaomi_miio": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 (\u03b1\u03ba\u03cc\u03bc\u03b1) \u03b1\u03c0\u03cc \u03c4\u03bf Xiaomi Miio.", - "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", diff --git a/homeassistant/components/xiaomi_miio/translations/sk.json b/homeassistant/components/xiaomi_miio/translations/sk.json index c61a54e6d8d..4b66c25fa57 100644 --- a/homeassistant/components/xiaomi_miio/translations/sk.json +++ b/homeassistant/components/xiaomi_miio/translations/sk.json @@ -7,6 +7,11 @@ }, "flow_title": "{name}", "step": { + "connect": { + "data": { + "model": "Model zariadenia" + } + }, "manual": { "data": { "host": "IP adresa", diff --git a/homeassistant/components/yalexs_ble/translations/sk.json b/homeassistant/components/yalexs_ble/translations/sk.json index e8940bef26a..c4cd811a778 100644 --- a/homeassistant/components/yalexs_ble/translations/sk.json +++ b/homeassistant/components/yalexs_ble/translations/sk.json @@ -1,5 +1,15 @@ { "config": { - "flow_title": "{name}" + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "no_unconfigured_devices": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia." + }, + "flow_title": "{name}", + "step": { + "integration_discovery_confirm": { + "description": "Chcete nastavi\u0165 {name} cez Bluetooth s adresou {address}?" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yeelight/translations/sk.json b/homeassistant/components/yeelight/translations/sk.json index 96cd70f97aa..2216797f253 100644 --- a/homeassistant/components/yeelight/translations/sk.json +++ b/homeassistant/components/yeelight/translations/sk.json @@ -1,10 +1,14 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, "flow_title": "{model} {id} ({host})", "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {model} ({host})?" + }, "pick_device": { "data": { "device": "Zariadenie" diff --git a/homeassistant/components/zamg/translations/el.json b/homeassistant/components/zamg/translations/el.json index 5e5408a2b5a..9ed237562b4 100644 --- a/homeassistant/components/zamg/translations/el.json +++ b/homeassistant/components/zamg/translations/el.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zerproc/translations/sk.json b/homeassistant/components/zerproc/translations/sk.json new file mode 100644 index 00000000000..69d8d942494 --- /dev/null +++ b/homeassistant/components/zerproc/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zha/translations/sk.json b/homeassistant/components/zha/translations/sk.json index e8940bef26a..d0bd56a816a 100644 --- a/homeassistant/components/zha/translations/sk.json +++ b/homeassistant/components/zha/translations/sk.json @@ -1,5 +1,24 @@ { "config": { - "flow_title": "{name}" + "flow_title": "{name}", + "step": { + "confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, + "confirm_hardware": { + "description": "Chcete nastavi\u0165 {name}?" + } + } + }, + "device_automation": { + "trigger_subtype": { + "both_buttons": "Obe tla\u010didl\u00e1", + "button_1": "Prv\u00e9 tla\u010didlo", + "button_2": "Druh\u00e9 tla\u010didlo", + "button_3": "Tretie tla\u010didlo", + "button_4": "\u0160tvrt\u00e9 tla\u010didlo", + "button_5": "Piate tla\u010didlo", + "button_6": "\u0160ieste tla\u010didlo" + } } } \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/sensor.sk.json b/homeassistant/components/zodiac/translations/sensor.sk.json new file mode 100644 index 00000000000..1d89a22507c --- /dev/null +++ b/homeassistant/components/zodiac/translations/sensor.sk.json @@ -0,0 +1,18 @@ +{ + "state": { + "zodiac__sign": { + "aquarius": "Vodn\u00e1r", + "aries": "Baran", + "cancer": "Rak", + "capricorn": "Kozoro\u017eec", + "gemini": "Bl\u00ed\u017eenci", + "leo": "Lev", + "libra": "V\u00e1hy", + "pisces": "Ryby", + "sagittarius": "Strelec", + "scorpio": "\u0160korpi\u00f3n", + "taurus": "B\u00fdk", + "virgo": "Panna" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/sk.json b/homeassistant/components/zwave_js/translations/sk.json index 675a2a95d55..7db446dc6c3 100644 --- a/homeassistant/components/zwave_js/translations/sk.json +++ b/homeassistant/components/zwave_js/translations/sk.json @@ -6,6 +6,15 @@ }, "flow_title": "{name}" }, + "device_automation": { + "action_type": { + "ping": "Ping zariadenia" + }, + "trigger_type": { + "event.notification.notification": "Odosla\u0165 ozn\u00e1menie", + "event.value_notification.scene_activation": "Aktiv\u00e1cia sc\u00e9ny na {subtype}" + } + }, "options": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" diff --git a/homeassistant/components/zwave_me/translations/sk.json b/homeassistant/components/zwave_me/translations/sk.json new file mode 100644 index 00000000000..793f8eff278 --- /dev/null +++ b/homeassistant/components/zwave_me/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file From 188cdd1bacd7ef2393cf29f6ce8dd0036d3fea20 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 23 Nov 2022 03:07:05 -0600 Subject: [PATCH 0640/1033] Ensure esphome client tasks await cancelation (#82547) --- homeassistant/components/esphome/bluetooth/client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index 666700bfc86..a5075da0bf9 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio from collections.abc import Callable, Coroutine +import contextlib import logging from typing import Any, TypeVar, cast import uuid @@ -66,6 +67,8 @@ def verify_connected(func: _WrapFuncType) -> _WrapFuncType: ) if disconnected_event.is_set(): task.cancel() + with contextlib.suppress(asyncio.CancelledError): + await task raise BleakError( f"{self._source}: {self._ble_device.name} - {self._ble_device.address}: " # pylint: disable=protected-access "Disconnected during operation" From c68149c75bceb5cc75c60416fda6a424463133e4 Mon Sep 17 00:00:00 2001 From: Hessel Date: Wed, 23 Nov 2022 11:25:21 +0100 Subject: [PATCH 0641/1033] Fix Wallbox price unit (#82420) * Change Symbol to Currency code in test * change unit to currency/kWh * f string --- homeassistant/components/wallbox/__init__.py | 8 +++++--- homeassistant/components/wallbox/const.py | 1 + homeassistant/components/wallbox/sensor.py | 7 ++++--- tests/components/wallbox/__init__.py | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/wallbox/__init__.py b/homeassistant/components/wallbox/__init__.py index 9a133b1794a..b5e935c27f1 100644 --- a/homeassistant/components/wallbox/__init__.py +++ b/homeassistant/components/wallbox/__init__.py @@ -33,6 +33,7 @@ from .const import ( CHARGER_SOFTWARE_KEY, CHARGER_STATUS_DESCRIPTION_KEY, CHARGER_STATUS_ID_KEY, + CODE_KEY, CONF_STATION, DOMAIN, ChargerStatus, @@ -129,9 +130,10 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]): data[CHARGER_ENERGY_PRICE_KEY] = data[CHARGER_DATA_KEY][ CHARGER_ENERGY_PRICE_KEY ] - data[CHARGER_CURRENCY_KEY] = data[CHARGER_DATA_KEY][CHARGER_CURRENCY_KEY][ - "code" - ] + data[ + CHARGER_CURRENCY_KEY + ] = f"{data[CHARGER_DATA_KEY][CHARGER_CURRENCY_KEY][CODE_KEY]}/kWh" + data[CHARGER_STATUS_DESCRIPTION_KEY] = CHARGER_STATUS.get( data[CHARGER_STATUS_ID_KEY], ChargerStatus.UNKNOWN ) diff --git a/homeassistant/components/wallbox/const.py b/homeassistant/components/wallbox/const.py index d7ee2dbb388..a6f92541a10 100644 --- a/homeassistant/components/wallbox/const.py +++ b/homeassistant/components/wallbox/const.py @@ -5,6 +5,7 @@ DOMAIN = "wallbox" BIDIRECTIONAL_MODEL_PREFIXES = ["QSX"] +CODE_KEY = "code" CONF_STATION = "station" CHARGER_ADDED_DISCHARGED_ENERGY_KEY = "added_discharged_energy" CHARGER_ADDED_ENERGY_KEY = "added_energy" diff --git a/homeassistant/components/wallbox/sensor.py b/homeassistant/components/wallbox/sensor.py index f44f5ea6aaa..9ce76d59608 100644 --- a/homeassistant/components/wallbox/sensor.py +++ b/homeassistant/components/wallbox/sensor.py @@ -129,7 +129,6 @@ SENSOR_TYPES: dict[str, WallboxSensorEntityDescription] = { icon="mdi:ev-station", name="Depot Price", precision=2, - device_class=SensorDeviceClass.MONETARY, state_class=SensorStateClass.MEASUREMENT, ), CHARGER_ENERGY_PRICE_KEY: WallboxSensorEntityDescription( @@ -137,7 +136,6 @@ SENSOR_TYPES: dict[str, WallboxSensorEntityDescription] = { icon="mdi:ev-station", name="Energy Price", precision=2, - device_class=SensorDeviceClass.MONETARY, state_class=SensorStateClass.MEASUREMENT, ), CHARGER_STATUS_DESCRIPTION_KEY: WallboxSensorEntityDescription( @@ -204,6 +202,9 @@ class WallboxSensor(WallboxEntity, SensorEntity): @property def native_unit_of_measurement(self) -> str | None: """Return the unit of measurement of the sensor. When monetary, get the value from the api.""" - if self.entity_description.device_class == SensorDeviceClass.MONETARY: + if self.entity_description.key in ( + CHARGER_ENERGY_PRICE_KEY, + CHARGER_DEPOT_PRICE_KEY, + ): return cast(str, self.coordinator.data[CHARGER_CURRENCY_KEY]) return cast(str, self.entity_description.native_unit_of_measurement) diff --git a/tests/components/wallbox/__init__.py b/tests/components/wallbox/__init__.py index ef72f556a8c..ed20e01cb2c 100644 --- a/tests/components/wallbox/__init__.py +++ b/tests/components/wallbox/__init__.py @@ -49,7 +49,7 @@ test_response = json.loads( CHARGER_SERIAL_NUMBER_KEY: "20000", CHARGER_PART_NUMBER_KEY: "PLP1-0-2-4-9-002-E", CHARGER_SOFTWARE_KEY: {CHARGER_CURRENT_VERSION_KEY: "5.5.10"}, - CHARGER_CURRENCY_KEY: {"code": "€"}, + CHARGER_CURRENCY_KEY: {"code": "EUR/kWh"}, }, } ) From 3738135e0b8132a81da045115bc28c51111c4cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 23 Nov 2022 12:00:38 +0100 Subject: [PATCH 0642/1033] Add helper to access RepairsFlowManager (#82567) async_external_step --- homeassistant/components/repairs/__init__.py | 15 +++++++++++++-- tests/components/repairs/test_init.py | 13 +++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/repairs/__init__.py b/homeassistant/components/repairs/__init__.py index 9c26fe01a69..aa578c098d5 100644 --- a/homeassistant/components/repairs/__init__.py +++ b/homeassistant/components/repairs/__init__.py @@ -6,16 +6,27 @@ from homeassistant.helpers.typing import ConfigType from . import issue_handler, websocket_api from .const import DOMAIN -from .issue_handler import ConfirmRepairFlow +from .issue_handler import ConfirmRepairFlow, RepairsFlowManager from .models import RepairsFlow __all__ = [ - "DOMAIN", "ConfirmRepairFlow", + "DOMAIN", + "repairs_flow_manager", "RepairsFlow", + "RepairsFlowManager", ] +def repairs_flow_manager(hass: HomeAssistant) -> RepairsFlowManager | None: + """Return the repairs flow manager.""" + if (domain_data := hass.data.get(DOMAIN)) is None: + return None + + flow_manager: RepairsFlowManager | None = domain_data.get("flow_manager") + return flow_manager + + async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up Repairs.""" hass.data[DOMAIN] = {} diff --git a/tests/components/repairs/test_init.py b/tests/components/repairs/test_init.py index 9311b1cf024..513eb4071bc 100644 --- a/tests/components/repairs/test_init.py +++ b/tests/components/repairs/test_init.py @@ -6,8 +6,10 @@ from aiohttp import ClientWebSocketResponse from freezegun import freeze_time import pytest +from homeassistant.components.repairs import repairs_flow_manager from homeassistant.components.repairs.const import DOMAIN from homeassistant.components.repairs.issue_handler import ( + RepairsFlowManager, async_process_repairs_platforms, ) from homeassistant.const import __version__ as ha_version @@ -538,3 +540,14 @@ async def test_sync_methods( assert msg["success"] assert msg["result"] == {"issues": []} + + +async def test_flow_manager_helper(hass: HomeAssistant) -> None: + """Test accessing the repairs flow manager with the helper.""" + assert repairs_flow_manager(hass) is None + + assert await async_setup_component(hass, DOMAIN, {}) + + flow_manager = repairs_flow_manager(hass) + assert flow_manager is not None + assert isinstance(flow_manager, RepairsFlowManager) From 4ea992649709bef7654447bf7eace7e91245fe48 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 23 Nov 2022 14:40:37 +0100 Subject: [PATCH 0643/1033] Not to Tox (#76582) --- .dockerignore | 1 - .gitignore | 1 - script/bootstrap | 2 +- script/lint | 2 +- script/{lazytox.py => lint_and_test.py} | 8 ++--- setup.cfg | 2 +- tox.ini | 48 ------------------------- 7 files changed, 7 insertions(+), 57 deletions(-) rename script/{lazytox.py => lint_and_test.py} (96%) delete mode 100644 tox.ini diff --git a/.dockerignore b/.dockerignore index 8144367ede1..7fde7f33fa5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,7 +9,6 @@ docs .vscode # Test related files -.tox tests # Other virtualization methods diff --git a/.gitignore b/.gitignore index d6f7198fcd4..2e3df400c76 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,6 @@ pip-log.txt # Unit test / coverage reports .coverage -.tox coverage.xml nosetests.xml htmlcov/ diff --git a/script/bootstrap b/script/bootstrap index 5040a322b62..68f02961d27 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -8,4 +8,4 @@ cd "$(dirname "$0")/.." echo "Installing development dependencies..." python3 -m pip install wheel --constraint homeassistant/package_constraints.txt -python3 -m pip install tox tox-pip-version colorlog pre-commit $(grep mypy requirements_test.txt) $(grep stdlib-list requirements_test.txt) $(grep tqdm requirements_test.txt) $(grep pipdeptree requirements_test.txt) $(grep awesomeversion requirements.txt) --constraint homeassistant/package_constraints.txt --use-deprecated=legacy-resolver +python3 -m pip install colorlog pre-commit $(grep mypy requirements_test.txt) $(grep stdlib-list requirements_test.txt) $(grep tqdm requirements_test.txt) $(grep pipdeptree requirements_test.txt) $(grep awesomeversion requirements.txt) --constraint homeassistant/package_constraints.txt --use-deprecated=legacy-resolver diff --git a/script/lint b/script/lint index e4bf74cf602..378c8c68d39 100755 --- a/script/lint +++ b/script/lint @@ -8,7 +8,7 @@ echo '=================================================' echo '= FILES CHANGED =' echo '=================================================' if [ -z "$files" ] ; then - echo "No python file changed. Rather use: tox -e lint\n" + echo "No python file changed.\n" exit fi printf "%s\n" $files diff --git a/script/lazytox.py b/script/lint_and_test.py similarity index 96% rename from script/lazytox.py rename to script/lint_and_test.py index 1f2f4cf02b0..97108e1c630 100755 --- a/script/lazytox.py +++ b/script/lint_and_test.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 """ -Lazy 'tox' to quickly check if branch is up to PR standards. +Quickly check if branch is up to PR standards. -This is NOT a tox replacement, only a quick check during development. +This is NOT a full CI/linting replacement, only a quick check during development. """ import asyncio from collections import namedtuple @@ -214,7 +214,7 @@ async def main(): print("=============================") if not test_files: - print("No test files identified, ideally you should run tox") + print("No test files identified") return code, _ = await async_exec( @@ -223,7 +223,7 @@ async def main(): print("=============================") if code == 0: - printc(PASS, "Yay! This will most likely pass tox") + printc(PASS, "Yay! This will most likely pass CI") else: printc(FAIL, "Tests not passing") diff --git a/setup.cfg b/setup.cfg index b1a2172f8f1..709b9e4286a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,7 +5,7 @@ url = https://www.home-assistant.io/ [flake8] -exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build +exclude = .venv,.git,docs,venv,bin,lib,deps,build max-complexity = 25 doctests = True # To work with Black diff --git a/tox.ini b/tox.ini deleted file mode 100644 index cbc98968177..00000000000 --- a/tox.ini +++ /dev/null @@ -1,48 +0,0 @@ -[tox] -envlist = py39, lint, pylint, typing, cov -skip_missing_interpreters = True -ignore_basepython_conflict = True -isolated_build = True - -[testenv] -basepython = {env:PYTHON3_PATH:python3} -# pip version duplicated in homeassistant/package_constraints.txt -pip_version = pip>=21.0,<22.4 -install_command = python -m pip install --use-deprecated legacy-resolver {opts} {packages} -commands = - {envpython} -X dev -m pytest --timeout=9 --durations=10 -n auto --dist=loadfile -qq -o console_output_style=count -p no:sugar {posargs} - {toxinidir}/script/check_dirty -deps = - -r{toxinidir}/requirements_test_all.txt - -[testenv:cov] -commands = - {envpython} -X dev -m pytest --timeout=9 --durations=10 -n auto --dist=loadfile -qq -o console_output_style=count -p no:sugar --cov --cov-report= {posargs} - {toxinidir}/script/check_dirty -deps = - -r{toxinidir}/requirements_test_all.txt - -[testenv:pylint] -skip_install = True -ignore_errors = True -deps = - -r{toxinidir}/requirements_all.txt - -r{toxinidir}/requirements_test.txt -commands = - pylint {env:PYLINT_ARGS:} {posargs} homeassistant - -[testenv:lint] -deps = - -r{toxinidir}/requirements_test.txt -commands = - python -m script.gen_requirements_all validate - python -m script.hassfest --action validate - pre-commit run codespell {posargs: --all-files} - pre-commit run flake8 {posargs: --all-files} - pre-commit run bandit {posargs: --all-files} - -[testenv:typing] -deps = - -r{toxinidir}/requirements_test_all.txt -commands = - mypy homeassistant From 32d68f375b34f17a07f662095c5c250e32acc438 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 23 Nov 2022 15:03:31 +0100 Subject: [PATCH 0644/1033] Add Mqtt WebSockets support (#82078) * Add Mqtt WebSockets support * Fix tests * Add testing websockets options * Add tests transport settings * Do not use templates for ws_headers * Use json helper - small corrections --- homeassistant/components/mqtt/__init__.py | 6 + homeassistant/components/mqtt/client.py | 12 +- homeassistant/components/mqtt/config_flow.py | 61 ++++++++ .../components/mqtt/config_integration.py | 12 ++ homeassistant/components/mqtt/const.py | 9 ++ homeassistant/components/mqtt/strings.json | 12 +- .../components/mqtt/translations/en.json | 12 +- tests/components/mqtt/test_config_flow.py | 131 +++++++++++++++++- tests/components/mqtt/test_diagnostics.py | 3 + tests/components/mqtt/test_discovery.py | 3 + 10 files changed, 253 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 0a2ee1eea9d..1c0df640c08 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -76,7 +76,10 @@ from .const import ( # noqa: F401 CONF_TLS_INSECURE, CONF_TLS_VERSION, CONF_TOPIC, + CONF_TRANSPORT, CONF_WILL_MESSAGE, + CONF_WS_HEADERS, + CONF_WS_PATH, DATA_MQTT, DEFAULT_ENCODING, DEFAULT_QOS, @@ -134,6 +137,9 @@ CONFIG_ENTRY_CONFIG_KEYS = [ CONF_PORT, CONF_PROTOCOL, CONF_TLS_INSECURE, + CONF_TRANSPORT, + CONF_WS_PATH, + CONF_WS_HEADERS, CONF_USERNAME, CONF_WILL_MESSAGE, ] diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py index c94b5d5ec51..66f1e130ff7 100644 --- a/homeassistant/components/mqtt/client.py +++ b/homeassistant/components/mqtt/client.py @@ -51,14 +51,19 @@ from .const import ( CONF_CLIENT_KEY, CONF_KEEPALIVE, CONF_TLS_INSECURE, + CONF_TRANSPORT, CONF_WILL_MESSAGE, + CONF_WS_HEADERS, + CONF_WS_PATH, DEFAULT_ENCODING, DEFAULT_PROTOCOL, DEFAULT_QOS, + DEFAULT_TRANSPORT, MQTT_CONNECTED, MQTT_DISCONNECTED, PROTOCOL_5, PROTOCOL_31, + TRANSPORT_WEBSOCKETS, ) from .models import ( AsyncMessageCallbackType, @@ -284,7 +289,8 @@ class MqttClientSetup: # PAHO MQTT relies on the MQTT server to generate random client IDs. # However, that feature is not mandatory so we generate our own. client_id = mqtt.base62(uuid.uuid4().int, padding=22) - self._client = mqtt.Client(client_id, protocol=proto) + transport = config.get(CONF_TRANSPORT, DEFAULT_TRANSPORT) + self._client = mqtt.Client(client_id, protocol=proto, transport=transport) # Enable logging self._client.enable_logger() @@ -302,6 +308,10 @@ class MqttClientSetup: client_key = get_file_path(CONF_CLIENT_KEY, config.get(CONF_CLIENT_KEY)) client_cert = get_file_path(CONF_CLIENT_CERT, config.get(CONF_CLIENT_CERT)) tls_insecure = config.get(CONF_TLS_INSECURE) + if transport == TRANSPORT_WEBSOCKETS: + ws_path = config.get(CONF_WS_PATH) + ws_headers = config.get(CONF_WS_HEADERS) + self._client.ws_set_options(ws_path, ws_headers) if certificate is not None: self._client.tls_set( certificate, diff --git a/homeassistant/components/mqtt/config_flow.py b/homeassistant/components/mqtt/config_flow.py index 3d5ab99f340..b79ff30f111 100644 --- a/homeassistant/components/mqtt/config_flow.py +++ b/homeassistant/components/mqtt/config_flow.py @@ -27,6 +27,8 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.json import JSON_DECODE_EXCEPTIONS, json_dumps, json_loads from homeassistant.helpers.selector import ( BooleanSelector, FileSelector, @@ -58,7 +60,10 @@ from .const import ( CONF_DISCOVERY_PREFIX, CONF_KEEPALIVE, CONF_TLS_INSECURE, + CONF_TRANSPORT, CONF_WILL_MESSAGE, + CONF_WS_HEADERS, + CONF_WS_PATH, DEFAULT_BIRTH, DEFAULT_DISCOVERY, DEFAULT_ENCODING, @@ -66,9 +71,14 @@ from .const import ( DEFAULT_PORT, DEFAULT_PREFIX, DEFAULT_PROTOCOL, + DEFAULT_TRANSPORT, DEFAULT_WILL, + DEFAULT_WS_PATH, DOMAIN, SUPPORTED_PROTOCOLS, + SUPPORTED_TRANSPORTS, + TRANSPORT_TCP, + TRANSPORT_WEBSOCKETS, ) from .util import ( async_create_certificate_temp_files, @@ -109,6 +119,15 @@ PROTOCOL_SELECTOR = SelectSelector( mode=SelectSelectorMode.DROPDOWN, ) ) +TRANSPORT_SELECTOR = SelectSelector( + SelectSelectorConfig( + options=SUPPORTED_TRANSPORTS, + mode=SelectSelectorMode.DROPDOWN, + ) +) +WS_HEADERS_SELECTOR = TextSelector( + TextSelectorConfig(type=TextSelectorType.TEXT, multiline=True) +) CA_VERIFICATION_MODES = [ SelectOptionDict(value="off", label="Off"), SelectOptionDict(value="auto", label="Auto"), @@ -493,6 +512,8 @@ async def async_get_broker_settings( or not certificate and user_input.get(SET_CA_CERT, "off") == "custom" and not certificate_id + or user_input.get(CONF_TRANSPORT) == TRANSPORT_WEBSOCKETS + and CONF_WS_PATH not in user_input ): return False @@ -526,6 +547,23 @@ async def async_get_broker_settings( del validated_user_input[SET_CA_CERT] if SET_CLIENT_CERT in validated_user_input: del validated_user_input[SET_CLIENT_CERT] + if validated_user_input.get(CONF_TRANSPORT, TRANSPORT_TCP) == TRANSPORT_TCP: + if CONF_WS_PATH in validated_user_input: + del validated_user_input[CONF_WS_PATH] + if CONF_WS_HEADERS in validated_user_input: + del validated_user_input[CONF_WS_HEADERS] + return True + try: + validated_user_input[CONF_WS_HEADERS] = json_loads( + validated_user_input.get(CONF_WS_HEADERS, "{}") + ) + schema = vol.Schema({cv.string: cv.template}) + schema(validated_user_input[CONF_WS_HEADERS]) + except JSON_DECODE_EXCEPTIONS + ( # pylint: disable=wrong-exception-operation + vol.MultipleInvalid, + ): + errors["base"] = "bad_ws_headers" + return False return True if user_input: @@ -562,6 +600,13 @@ async def async_get_broker_settings( current_client_key = current_config.get(CONF_CLIENT_KEY) current_tls_insecure = current_config.get(CONF_TLS_INSECURE, False) current_protocol = current_config.get(CONF_PROTOCOL, DEFAULT_PROTOCOL) + current_transport = current_config.get(CONF_TRANSPORT, DEFAULT_TRANSPORT) + current_ws_path = current_config.get(CONF_WS_PATH, DEFAULT_WS_PATH) + current_ws_headers = ( + json_dumps(current_config.get(CONF_WS_HEADERS)) + if CONF_WS_HEADERS in current_config + else None + ) advanced_broker_options |= bool( current_client_id or current_keepalive != DEFAULT_KEEPALIVE @@ -572,6 +617,7 @@ async def async_get_broker_settings( or current_protocol != DEFAULT_PROTOCOL or current_config.get(SET_CA_CERT, "off") != "off" or current_config.get(SET_CLIENT_CERT) + or current_transport == TRANSPORT_WEBSOCKETS ) # Build form @@ -665,6 +711,21 @@ async def async_get_broker_settings( description={"suggested_value": current_protocol}, ) ] = PROTOCOL_SELECTOR + fields[ + vol.Optional( + CONF_TRANSPORT, + description={"suggested_value": current_transport}, + ) + ] = TRANSPORT_SELECTOR + if current_transport == TRANSPORT_WEBSOCKETS: + fields[ + vol.Optional(CONF_WS_PATH, description={"suggested_value": current_ws_path}) + ] = TEXT_SELECTOR + fields[ + vol.Optional( + CONF_WS_HEADERS, description={"suggested_value": current_ws_headers} + ) + ] = WS_HEADERS_SELECTOR # Show form return False diff --git a/homeassistant/components/mqtt/config_integration.py b/homeassistant/components/mqtt/config_integration.py index 9319a48ac3d..d487f1b382f 100644 --- a/homeassistant/components/mqtt/config_integration.py +++ b/homeassistant/components/mqtt/config_integration.py @@ -45,15 +45,21 @@ from .const import ( CONF_KEEPALIVE, CONF_TLS_INSECURE, CONF_TLS_VERSION, + CONF_TRANSPORT, CONF_WILL_MESSAGE, + CONF_WS_HEADERS, + CONF_WS_PATH, DEFAULT_BIRTH, DEFAULT_DISCOVERY, DEFAULT_KEEPALIVE, DEFAULT_PORT, DEFAULT_PREFIX, DEFAULT_PROTOCOL, + DEFAULT_TRANSPORT, DEFAULT_WILL, SUPPORTED_PROTOCOLS, + TRANSPORT_TCP, + TRANSPORT_WEBSOCKETS, ) from .util import valid_birth_will, valid_publish_topic @@ -66,6 +72,7 @@ DEFAULT_VALUES = { CONF_PORT: DEFAULT_PORT, CONF_PROTOCOL: DEFAULT_PROTOCOL, CONF_TLS_VERSION: DEFAULT_TLS_PROTOCOL, + CONF_TRANSPORT: DEFAULT_TRANSPORT, CONF_WILL_MESSAGE: DEFAULT_WILL, CONF_KEEPALIVE: DEFAULT_KEEPALIVE, } @@ -160,6 +167,11 @@ CONFIG_SCHEMA_ENTRY = vol.Schema( # discovery_prefix must be a valid publish topic because if no # state topic is specified, it will be created with the given prefix. vol.Optional(CONF_DISCOVERY_PREFIX): valid_publish_topic, + vol.Optional(CONF_TRANSPORT, default=DEFAULT_TRANSPORT): vol.All( + cv.string, vol.In([TRANSPORT_TCP, TRANSPORT_WEBSOCKETS]) + ), + vol.Optional(CONF_WS_PATH, default="/"): cv.string, + vol.Optional(CONF_WS_HEADERS, default={}): {cv.string: cv.string}, } ) diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index cbcc23bb1a9..7b4c7378b9e 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -23,6 +23,9 @@ CONF_SCHEMA = "schema" CONF_STATE_TOPIC = "state_topic" CONF_STATE_VALUE_TEMPLATE = "state_value_template" CONF_TOPIC = "topic" +CONF_TRANSPORT = "transport" +CONF_WS_PATH = "ws_path" +CONF_WS_HEADERS = "ws_headers" CONF_WILL_MESSAGE = "will_message" CONF_CERTIFICATE = "certificate" @@ -42,15 +45,21 @@ DEFAULT_PAYLOAD_AVAILABLE = "online" DEFAULT_PAYLOAD_NOT_AVAILABLE = "offline" DEFAULT_PORT = 1883 DEFAULT_RETAIN = False +DEFAULT_WS_PATH = "/" PROTOCOL_31 = "3.1" PROTOCOL_311 = "3.1.1" PROTOCOL_5 = "5" SUPPORTED_PROTOCOLS = [PROTOCOL_31, PROTOCOL_311, PROTOCOL_5] +TRANSPORT_TCP = "tcp" +TRANSPORT_WEBSOCKETS = "websockets" +SUPPORTED_TRANSPORTS = [TRANSPORT_TCP, TRANSPORT_WEBSOCKETS] + DEFAULT_PORT = 1883 DEFAULT_KEEPALIVE = 60 DEFAULT_PROTOCOL = PROTOCOL_311 +DEFAULT_TRANSPORT = TRANSPORT_TCP DEFAULT_BIRTH = { ATTR_TOPIC: DEFAULT_BIRTH_WILL_TOPIC, diff --git a/homeassistant/components/mqtt/strings.json b/homeassistant/components/mqtt/strings.json index 5788597e9a9..30cb57bb85a 100644 --- a/homeassistant/components/mqtt/strings.json +++ b/homeassistant/components/mqtt/strings.json @@ -27,7 +27,10 @@ "tls_insecure": "Ignore broker certificate validation", "protocol": "MQTT protocol", "set_ca_cert": "Broker certificate validation", - "set_client_cert": "Use a client certificate" + "set_client_cert": "Use a client certificate", + "transport": "MQTT transport", + "ws_headers": "WebSocket headers in JSON format", + "ws_path": "WebSocket path" } }, "hassio_confirm": { @@ -50,6 +53,7 @@ "bad_client_cert": "Invalid client certificate, ensure a PEM coded file is supplied", "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", "bad_client_cert_key": "Client certificate and private are no valid pair", + "bad_ws_headers": "Supply valid HTTP headers as a JSON object", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_inclusion": "The client certificate and private key must be configurered together" } @@ -95,7 +99,10 @@ "tls_insecure": "[%key:component::mqtt::config::step::broker::data::tls_insecure%]", "protocol": "[%key:component::mqtt::config::step::broker::data::protocol%]", "set_ca_cert": "[%key:component::mqtt::config::step::broker::data::set_ca_cert%]", - "set_client_cert": "[%key:component::mqtt::config::step::broker::data::set_client_cert%]" + "set_client_cert": "[%key:component::mqtt::config::step::broker::data::set_client_cert%]", + "transport": "[%key:component::mqtt::config::step::broker::data::transport%]", + "ws_headers": "[%key:component::mqtt::config::step::broker::data::ws_headers%]", + "ws_path": "[%key:component::mqtt::config::step::broker::data::ws_path%]" } }, "options": { @@ -125,6 +132,7 @@ "bad_client_cert": "[%key:component::mqtt::config::error::bad_client_cert%]", "bad_client_key": "[%key:component::mqtt::config::error::bad_client_key%]", "bad_client_cert_key": "[%key:component::mqtt::config::error::bad_client_cert_key%]", + "bad_ws_headers": "[%key:component::mqtt::config::error::bad_ws_headers%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_inclusion": "[%key:component::mqtt::config::error::invalid_inclusion%]" } diff --git a/homeassistant/components/mqtt/translations/en.json b/homeassistant/components/mqtt/translations/en.json index 6ab75c6a30d..51e7918b5cc 100644 --- a/homeassistant/components/mqtt/translations/en.json +++ b/homeassistant/components/mqtt/translations/en.json @@ -12,6 +12,7 @@ "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", "bad_discovery_prefix": "Invalid discovery prefix", "bad_will": "Invalid will topic", + "bad_ws_headers": "Supply valid HTTP headers as a JSON object", "cannot_connect": "Failed to connect", "invalid_inclusion": "The client certificate and private key must be configurered together" }, @@ -32,7 +33,10 @@ "set_ca_cert": "Broker certificate validation", "set_client_cert": "Use a client certificate", "tls_insecure": "Ignore broker certificate validation", - "username": "Username" + "transport": "MQTT transport", + "username": "Username", + "ws_path": "WebSocket path", + "ws_headers": "WebSocket headers" }, "description": "Please enter the connection information of your MQTT broker." }, @@ -86,6 +90,7 @@ "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", "bad_discovery_prefix": "Invalid discovery prefix", "bad_will": "Invalid will topic", + "bad_ws_headers": "Supply valid HTTP headers as a JSON object", "cannot_connect": "Failed to connect", "invalid_inclusion": "The client certificate and private key must be configurered together" }, @@ -105,7 +110,10 @@ "set_ca_cert": "Broker certificate validation", "set_client_cert": "Use a client certificate", "tls_insecure": "Ignore broker certificate validation", - "username": "Username" + "transport": "MQTT transport", + "username": "Username", + "ws_path": "WebSocket path", + "ws_headers": "WebSocket headers" }, "description": "Please enter the connection information of your MQTT broker.", "title": "Broker options" diff --git a/tests/components/mqtt/test_config_flow.py b/tests/components/mqtt/test_config_flow.py index b9fdf0b9c26..818cdcf33a6 100644 --- a/tests/components/mqtt/test_config_flow.py +++ b/tests/components/mqtt/test_config_flow.py @@ -1140,7 +1140,6 @@ async def test_options_bad_will_message_fails(hass, mock_try_connection): async def test_try_connection_with_advanced_parameters( hass, - mqtt_mock_entry_with_yaml_config, mock_try_connection_success, tmp_path, mock_ssl_context, @@ -1171,6 +1170,9 @@ async def test_try_connection_with_advanced_parameters( mqtt.CONF_PORT: 1234, mqtt.CONF_USERNAME: "user", mqtt.CONF_PASSWORD: "pass", + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_PATH: "/path/", + mqtt.CONF_WS_HEADERS: {"h1": "v1", "h2": "v2"}, mqtt.CONF_KEEPALIVE: 30, mqtt.CONF_DISCOVERY: True, mqtt.CONF_BIRTH_MESSAGE: { @@ -1205,6 +1207,9 @@ async def test_try_connection_with_advanced_parameters( mqtt.CONF_PASSWORD: "pass", mqtt.CONF_TLS_INSECURE: True, mqtt.CONF_PROTOCOL: "3.1.1", + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_PATH: "/path/", + mqtt.CONF_WS_HEADERS: '{"h1":"v1","h2":"v2"}', } for k, v in defaults.items(): assert get_default(result["data_schema"].schema, k) == v @@ -1220,7 +1225,7 @@ async def test_try_connection_with_advanced_parameters( ) assert config_entry.data[mqtt.CONF_CERTIFICATE] == "auto" - # test we can chante username and password + # test we can change username and password # as it was configured as auto in configuration.yaml is is migrated now mock_try_connection_success.reset_mock() result = await hass.config_entries.options.async_configure( @@ -1233,6 +1238,9 @@ async def test_try_connection_with_advanced_parameters( "set_ca_cert": "auto", "set_client_cert": True, mqtt.CONF_TLS_INSECURE: True, + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_PATH: "/new/path", + mqtt.CONF_WS_HEADERS: '{"h3": "v3"}', }, ) assert result["type"] == data_entry_flow.FlowResultType.FORM @@ -1256,6 +1264,12 @@ async def test_try_connection_with_advanced_parameters( "keyfile" ] == mqtt.util.get_file_path(mqtt.CONF_CLIENT_KEY) + # check if websockets options are set + assert mock_try_connection_success.ws_set_options.mock_calls[0][1] == ( + "/new/path", + {"h3": "v3"}, + ) + # Accept default option result = await hass.config_entries.options.async_configure( result["flow_id"], @@ -1305,6 +1319,7 @@ async def test_setup_with_advanced_settings( assert result["data_schema"].schema["set_ca_cert"] assert result["data_schema"].schema[mqtt.CONF_TLS_INSECURE] assert result["data_schema"].schema[mqtt.CONF_PROTOCOL] + assert result["data_schema"].schema[mqtt.CONF_TRANSPORT] assert mqtt.CONF_CLIENT_CERT not in result["data_schema"].schema assert mqtt.CONF_CLIENT_KEY not in result["data_schema"].schema @@ -1320,6 +1335,8 @@ async def test_setup_with_advanced_settings( "set_ca_cert": "auto", "set_client_cert": True, mqtt.CONF_TLS_INSECURE: True, + mqtt.CONF_PROTOCOL: "3.1.1", + mqtt.CONF_TRANSPORT: "websockets", }, ) assert result["type"] == "form" @@ -1333,8 +1350,11 @@ async def test_setup_with_advanced_settings( assert result["data_schema"].schema[mqtt.CONF_PROTOCOL] assert result["data_schema"].schema[mqtt.CONF_CLIENT_CERT] assert result["data_schema"].schema[mqtt.CONF_CLIENT_KEY] + assert result["data_schema"].schema[mqtt.CONF_TRANSPORT] + assert result["data_schema"].schema[mqtt.CONF_WS_PATH] + assert result["data_schema"].schema[mqtt.CONF_WS_HEADERS] - # third iteration, advanced settings with client cert and key set + # third iteration, advanced settings with client cert and key set and bad json payload result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ @@ -1348,6 +1368,34 @@ async def test_setup_with_advanced_settings( mqtt.CONF_CLIENT_CERT: file_id[mqtt.CONF_CLIENT_CERT], mqtt.CONF_CLIENT_KEY: file_id[mqtt.CONF_CLIENT_KEY], mqtt.CONF_TLS_INSECURE: True, + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_PATH: "/custom_path/", + mqtt.CONF_WS_HEADERS: '{"header_1": "content_header_1", "header_2": "content_header_2"', + }, + ) + + assert result["type"] == "form" + assert result["step_id"] == "broker" + assert result["errors"]["base"] == "bad_ws_headers" + + # fourth iteration, advanced settings with client cert and key set + # and correct json payload for ws_headers + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + mqtt.CONF_BROKER: "test-broker", + mqtt.CONF_PORT: 2345, + mqtt.CONF_USERNAME: "user", + mqtt.CONF_PASSWORD: "secret", + mqtt.CONF_KEEPALIVE: 30, + "set_ca_cert": "auto", + "set_client_cert": True, + mqtt.CONF_CLIENT_CERT: file_id[mqtt.CONF_CLIENT_CERT], + mqtt.CONF_CLIENT_KEY: file_id[mqtt.CONF_CLIENT_KEY], + mqtt.CONF_TLS_INSECURE: True, + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_PATH: "/custom_path/", + mqtt.CONF_WS_HEADERS: '{"header_1": "content_header_1", "header_2": "content_header_2"}', }, ) @@ -1362,3 +1410,80 @@ async def test_setup_with_advanced_settings( }, ) assert result["type"] == "create_entry" + + # Check config entry result + assert config_entry.data == { + mqtt.CONF_BROKER: "test-broker", + mqtt.CONF_PORT: 2345, + mqtt.CONF_USERNAME: "user", + mqtt.CONF_PASSWORD: "secret", + mqtt.CONF_KEEPALIVE: 30, + mqtt.CONF_CLIENT_CERT: "## mock client certificate file ##", + mqtt.CONF_CLIENT_KEY: "## mock key file ##", + "tls_insecure": True, + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_PATH: "/custom_path/", + mqtt.CONF_WS_HEADERS: { + "header_1": "content_header_1", + "header_2": "content_header_2", + }, + mqtt.CONF_CERTIFICATE: "auto", + mqtt.CONF_DISCOVERY: True, + mqtt.CONF_DISCOVERY_PREFIX: "homeassistant_test", + } + + +async def test_change_websockets_transport_to_tcp( + hass, mock_try_connection, tmp_path, mock_ssl_context, mock_process_uploaded_file +): + """Test option flow setup with websockets transport settings.""" + config_entry = MockConfigEntry(domain=mqtt.DOMAIN) + config_entry.add_to_hass(hass) + config_entry.data = { + mqtt.CONF_BROKER: "test-broker", + mqtt.CONF_PORT: 1234, + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_HEADERS: {"header_1": "custom_header1"}, + mqtt.CONF_WS_PATH: "/some_path", + } + + mock_try_connection.return_value = True + + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == "form" + assert result["step_id"] == "broker" + assert result["data_schema"].schema["transport"] + assert result["data_schema"].schema["ws_path"] + assert result["data_schema"].schema["ws_headers"] + + # Change transport to tcp + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + mqtt.CONF_BROKER: "test-broker", + mqtt.CONF_PORT: 1234, + mqtt.CONF_TRANSPORT: "tcp", + mqtt.CONF_WS_HEADERS: '{"header_1": "custom_header1"}', + mqtt.CONF_WS_PATH: "/some_path", + }, + ) + assert result["type"] == "form" + assert result["step_id"] == "options" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + mqtt.CONF_DISCOVERY: True, + mqtt.CONF_DISCOVERY_PREFIX: "homeassistant_test", + }, + ) + assert result["type"] == "create_entry" + + # Check config entry result + assert config_entry.data == { + mqtt.CONF_BROKER: "test-broker", + mqtt.CONF_PORT: 1234, + mqtt.CONF_TRANSPORT: "tcp", + mqtt.CONF_DISCOVERY: True, + mqtt.CONF_DISCOVERY_PREFIX: "homeassistant_test", + } diff --git a/tests/components/mqtt/test_diagnostics.py b/tests/components/mqtt/test_diagnostics.py index 49d3fe0b4b6..7512a46802f 100644 --- a/tests/components/mqtt/test_diagnostics.py +++ b/tests/components/mqtt/test_diagnostics.py @@ -23,6 +23,9 @@ default_config = { "port": 1883, "protocol": "3.1.1", "tls_version": "auto", + "transport": "tcp", + "ws_headers": {}, + "ws_path": "/", "will_message": { "payload": "offline", "qos": 0, diff --git a/tests/components/mqtt/test_discovery.py b/tests/components/mqtt/test_discovery.py index 77d7093830e..48c1d88a9a8 100644 --- a/tests/components/mqtt/test_discovery.py +++ b/tests/components/mqtt/test_discovery.py @@ -1179,7 +1179,10 @@ ABBREVIATIONS_WHITE_LIST = [ "CONF_KEEPALIVE", "CONF_TLS_INSECURE", "CONF_TLS_VERSION", + "CONF_TRANSPORT", "CONF_WILL_MESSAGE", + "CONF_WS_PATH", + "CONF_WS_HEADERS", # Undocumented device configuration "CONF_DEPRECATED_VIA_HUB", "CONF_VIA_DEVICE", From a55fb445b0ed4efd625227b4f13a01a0f469c358 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Wed, 23 Nov 2022 16:23:25 +0100 Subject: [PATCH 0645/1033] Bump to Arcam 1.0.1 and make strictly typed (#82487) * Make arcam_fmj strictly typed * Add test for invalid UDN --- .../components/arcam_fmj/__init__.py | 9 ++--- .../components/arcam_fmj/config_flow.py | 29 +++++++++++----- .../components/arcam_fmj/device_trigger.py | 2 +- .../components/arcam_fmj/manifest.json | 2 +- .../components/arcam_fmj/media_player.py | 34 +++++++++---------- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../components/arcam_fmj/test_config_flow.py | 16 +++++++++ 8 files changed, 63 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/arcam_fmj/__init__.py b/homeassistant/components/arcam_fmj/__init__.py index c2c6be0db30..2796a607ac0 100644 --- a/homeassistant/components/arcam_fmj/__init__.py +++ b/homeassistant/components/arcam_fmj/__init__.py @@ -2,6 +2,7 @@ import asyncio from contextlib import suppress import logging +from typing import Any from arcam.fmj import ConnectionFailed from arcam.fmj.client import Client @@ -31,7 +32,7 @@ CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) PLATFORMS = [Platform.MEDIA_PLAYER] -async def _await_cancel(task): +async def _await_cancel(task: asyncio.Task) -> None: task.cancel() with suppress(asyncio.CancelledError): await task @@ -42,7 +43,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass.data[DOMAIN_DATA_ENTRIES] = {} hass.data[DOMAIN_DATA_TASKS] = {} - async def _stop(_): + async def _stop(_: Any) -> None: asyncio.gather( *(_await_cancel(task) for task in hass.data[DOMAIN_DATA_TASKS].values()) ) @@ -80,8 +81,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -async def _run_client(hass, client, interval): - def _listen(_): +async def _run_client(hass: HomeAssistant, client: Client, interval: float) -> None: + def _listen(_: Any) -> None: async_dispatcher_send(hass, SIGNAL_CLIENT_DATA, client.host) while True: diff --git a/homeassistant/components/arcam_fmj/config_flow.py b/homeassistant/components/arcam_fmj/config_flow.py index 2570fd1aea5..09944328c4a 100644 --- a/homeassistant/components/arcam_fmj/config_flow.py +++ b/homeassistant/components/arcam_fmj/config_flow.py @@ -1,4 +1,7 @@ """Config flow to configure the Arcam FMJ component.""" +from __future__ import annotations + +from typing import Any from urllib.parse import urlparse from arcam.fmj.client import Client, ConnectionFailed @@ -8,15 +11,17 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.components import ssdp from homeassistant.const import CONF_HOST, CONF_PORT +from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import DEFAULT_NAME, DEFAULT_PORT, DOMAIN, DOMAIN_DATA_ENTRIES -def get_entry_client(hass, entry): +def get_entry_client(hass: HomeAssistant, entry: config_entries.ConfigEntry) -> Client: """Retrieve client associated with a config entry.""" - return hass.data[DOMAIN_DATA_ENTRIES][entry.entry_id] + client: Client = hass.data[DOMAIN_DATA_ENTRIES][entry.entry_id] + return client class ArcamFmjFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): @@ -24,11 +29,13 @@ class ArcamFmjFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 - async def _async_set_unique_id_and_update(self, host, port, uuid): + async def _async_set_unique_id_and_update( + self, host: str, port: int, uuid: str + ) -> None: await self.async_set_unique_id(uuid) self._abort_if_unique_id_configured({CONF_HOST: host, CONF_PORT: port}) - async def _async_check_and_create(self, host, port): + async def _async_check_and_create(self, host: str, port: int) -> FlowResult: client = Client(host, port) try: await client.start() @@ -42,9 +49,11 @@ class ArcamFmjFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): data={CONF_HOST: host, CONF_PORT: port}, ) - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle a discovered device.""" - errors = {} + errors: dict[str, str] = {} if user_input is not None: uuid = await get_uniqueid_from_host( @@ -68,7 +77,9 @@ class ArcamFmjFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): step_id="user", data_schema=vol.Schema(fields), errors=errors ) - async def async_step_confirm(self, user_input=None): + async def async_step_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle user-confirmation of discovered node.""" context = self.context placeholders = { @@ -87,9 +98,11 @@ class ArcamFmjFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): async def async_step_ssdp(self, discovery_info: ssdp.SsdpServiceInfo) -> FlowResult: """Handle a discovered device.""" - host = urlparse(discovery_info.ssdp_location).hostname + host = str(urlparse(discovery_info.ssdp_location).hostname) port = DEFAULT_PORT uuid = get_uniqueid_from_udn(discovery_info.upnp[ssdp.ATTR_UPNP_UDN]) + if not uuid: + return self.async_abort(reason="cannot_connect") await self._async_set_unique_id_and_update(host, port, uuid) diff --git a/homeassistant/components/arcam_fmj/device_trigger.py b/homeassistant/components/arcam_fmj/device_trigger.py index 13f1acc7244..f3722c81ec5 100644 --- a/homeassistant/components/arcam_fmj/device_trigger.py +++ b/homeassistant/components/arcam_fmj/device_trigger.py @@ -65,7 +65,7 @@ async def async_attach_trigger( entity_id = config[CONF_ENTITY_ID] @callback - def _handle_event(event: Event): + def _handle_event(event: Event) -> None: if event.data[ATTR_ENTITY_ID] == entity_id: hass.async_run_hass_job( job, diff --git a/homeassistant/components/arcam_fmj/manifest.json b/homeassistant/components/arcam_fmj/manifest.json index c91c92922b4..3fcbaabc7e8 100644 --- a/homeassistant/components/arcam_fmj/manifest.json +++ b/homeassistant/components/arcam_fmj/manifest.json @@ -3,7 +3,7 @@ "name": "Arcam FMJ Receivers", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/arcam_fmj", - "requirements": ["arcam-fmj==0.12.0"], + "requirements": ["arcam-fmj==1.0.1"], "ssdp": [ { "deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1", diff --git a/homeassistant/components/arcam_fmj/media_player.py b/homeassistant/components/arcam_fmj/media_player.py index 65a5d8c3580..7a15a50e375 100644 --- a/homeassistant/components/arcam_fmj/media_player.py +++ b/homeassistant/components/arcam_fmj/media_player.py @@ -64,10 +64,10 @@ class ArcamFmj(MediaPlayerEntity): def __init__( self, - device_name, + device_name: str, state: State, uuid: str, - ): + ) -> None: """Initialize device.""" self._state = state self._device_name = device_name @@ -96,12 +96,12 @@ class ArcamFmj(MediaPlayerEntity): return MediaPlayerState.OFF @property - def device_info(self): + def device_info(self) -> DeviceInfo: """Return a device description for device registry.""" return DeviceInfo( identifiers={ (DOMAIN, self._uuid), - (DOMAIN, self._state.client.host, self._state.client.port), + (DOMAIN, self._state.client.host, self._state.client.port), # type: ignore[arg-type] }, manufacturer="Arcam", model="Arcam FMJ AVR", @@ -114,17 +114,17 @@ class ArcamFmj(MediaPlayerEntity): await self._state.update() @callback - def _data(host): + def _data(host: str) -> None: if host == self._state.client.host: self.async_write_ha_state() @callback - def _started(host): + def _started(host: str) -> None: if host == self._state.client.host: self.async_schedule_update_ha_state(force_refresh=True) @callback - def _stopped(host): + def _stopped(host: str) -> None: if host == self._state.client.host: self.async_schedule_update_ha_state(force_refresh=True) @@ -249,40 +249,40 @@ class ArcamFmj(MediaPlayerEntity): return @property - def source(self): + def source(self) -> str | None: """Return the current input source.""" if (value := self._state.get_source()) is None: return None return value.name @property - def source_list(self): + def source_list(self) -> list[str]: """List of available input sources.""" return [x.name for x in self._state.get_source_list()] @property - def sound_mode(self): + def sound_mode(self) -> str | None: """Name of the current sound mode.""" if (value := self._state.get_decode_mode()) is None: return None return value.name @property - def sound_mode_list(self): + def sound_mode_list(self) -> list[str] | None: """List of available sound modes.""" if (values := self._state.get_decode_modes()) is None: return None return [x.name for x in values] @property - def is_volume_muted(self): + def is_volume_muted(self) -> bool | None: """Boolean if volume is currently muted.""" if (value := self._state.get_mute()) is None: return None return value @property - def volume_level(self): + def volume_level(self) -> float | None: """Volume level of device.""" if (value := self._state.get_volume()) is None: return None @@ -301,7 +301,7 @@ class ArcamFmj(MediaPlayerEntity): return value @property - def media_content_id(self): + def media_content_id(self) -> str | None: """Content type of current playing media.""" source = self._state.get_source() if source in (SourceCodes.DAB, SourceCodes.FM): @@ -315,7 +315,7 @@ class ArcamFmj(MediaPlayerEntity): return value @property - def media_channel(self): + def media_channel(self) -> str | None: """Channel currently playing.""" source = self._state.get_source() if source == SourceCodes.DAB: @@ -327,7 +327,7 @@ class ArcamFmj(MediaPlayerEntity): return value @property - def media_artist(self): + def media_artist(self) -> str | None: """Artist of current playing media, music track only.""" if self._state.get_source() == SourceCodes.DAB: value = self._state.get_dls_pdt() @@ -336,7 +336,7 @@ class ArcamFmj(MediaPlayerEntity): return value @property - def media_title(self): + def media_title(self) -> str | None: """Title of current playing media.""" if (source := self._state.get_source()) is None: return None diff --git a/requirements_all.txt b/requirements_all.txt index a5ff67ef687..d715e53c1c8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -345,7 +345,7 @@ aqualogic==2.6 aranet4==2.1.3 # homeassistant.components.arcam_fmj -arcam-fmj==0.12.0 +arcam-fmj==1.0.1 # homeassistant.components.arris_tg2492lg arris-tg2492lg==1.2.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index dccaea89705..fc73125f821 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -308,7 +308,7 @@ aprslib==0.7.0 aranet4==2.1.3 # homeassistant.components.arcam_fmj -arcam-fmj==0.12.0 +arcam-fmj==1.0.1 # homeassistant.components.dlna_dmr # homeassistant.components.dlna_dms diff --git a/tests/components/arcam_fmj/test_config_flow.py b/tests/components/arcam_fmj/test_config_flow.py index eeb5adbc7e3..e49775f00b5 100644 --- a/tests/components/arcam_fmj/test_config_flow.py +++ b/tests/components/arcam_fmj/test_config_flow.py @@ -1,5 +1,6 @@ """Tests for the Arcam FMJ config flow module.""" +from dataclasses import replace from unittest.mock import AsyncMock, patch from arcam.fmj.client import ConnectionFailed @@ -107,6 +108,21 @@ async def test_ssdp_unable_to_connect(hass, dummy_client): assert result["reason"] == "cannot_connect" +async def test_ssdp_invalid_id(hass, dummy_client): + """Test a ssdp with invalid UDN.""" + discover = replace( + MOCK_DISCOVER, upnp=MOCK_DISCOVER.upnp | {ssdp.ATTR_UPNP_UDN: "invalid"} + ) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={CONF_SOURCE: SOURCE_SSDP}, + data=discover, + ) + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "cannot_connect" + + async def test_ssdp_update(hass): """Test a ssdp import flow.""" entry = MockConfigEntry( From 4f3919cb95261b28e53472bd301c753c0d3fbbc8 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 23 Nov 2022 16:24:56 +0100 Subject: [PATCH 0646/1033] Move mqtt device_tracker files (#82581) Move mqtt device_tracker files to single file --- .../schema_discovery.py => device_tracker.py} | 29 ++++++++++++------- .../mqtt/device_tracker/__init__.py | 26 ----------------- ...er_discovery.py => test_device_tracker.py} | 2 +- 3 files changed, 20 insertions(+), 37 deletions(-) rename homeassistant/components/mqtt/{device_tracker/schema_discovery.py => device_tracker.py} (87%) delete mode 100644 homeassistant/components/mqtt/device_tracker/__init__.py rename tests/components/mqtt/{test_device_tracker_discovery.py => test_device_tracker.py} (99%) diff --git a/homeassistant/components/mqtt/device_tracker/schema_discovery.py b/homeassistant/components/mqtt/device_tracker.py similarity index 87% rename from homeassistant/components/mqtt/device_tracker/schema_discovery.py rename to homeassistant/components/mqtt/device_tracker.py index ba088a59c44..41902349de4 100644 --- a/homeassistant/components/mqtt/device_tracker/schema_discovery.py +++ b/homeassistant/components/mqtt/device_tracker.py @@ -1,4 +1,4 @@ -"""Support for tracking MQTT enabled devices identified through discovery.""" +"""Support for tracking MQTT enabled devices identified.""" from __future__ import annotations from collections.abc import Callable @@ -27,13 +27,18 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from .. import subscription -from ..config import MQTT_RO_SCHEMA -from ..const import CONF_QOS, CONF_STATE_TOPIC -from ..debug_info import log_messages -from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper -from ..models import MqttValueTemplate, ReceiveMessage, ReceivePayloadType -from ..util import get_mqtt_data +from . import subscription +from .config import MQTT_RO_SCHEMA +from .const import CONF_QOS, CONF_STATE_TOPIC +from .debug_info import log_messages +from .mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_setup_entry_helper, + warn_for_legacy_schema, +) +from .models import MqttValueTemplate, ReceiveMessage, ReceivePayloadType +from .util import get_mqtt_data CONF_PAYLOAD_HOME = "payload_home" CONF_PAYLOAD_NOT_HOME = "payload_not_home" @@ -54,13 +59,17 @@ PLATFORM_SCHEMA_MODERN = MQTT_RO_SCHEMA.extend( DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) +# Configuring MQTT Device Trackers under the device_tracker platform key is deprecated in HA Core 2022.6 +# Setup for the legacy YAML format was removed in HA Core 2022.12 +PLATFORM_SCHEMA = vol.All(warn_for_legacy_schema(device_tracker.DOMAIN)) -async def async_setup_entry_from_discovery( + +async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT device tracker configuration.yaml and dynamically through MQTT discovery.""" + """Set up MQTT device_tracker through configuration.yaml and dynamically through MQTT discovery.""" setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/homeassistant/components/mqtt/device_tracker/__init__.py b/homeassistant/components/mqtt/device_tracker/__init__.py deleted file mode 100644 index 6be4908403b..00000000000 --- a/homeassistant/components/mqtt/device_tracker/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Support for tracking MQTT enabled devices.""" -import voluptuous as vol - -from homeassistant.components import device_tracker -from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_platform import AddEntitiesCallback - -from ..mixins import warn_for_legacy_schema -from .schema_discovery import ( # noqa: F401 - PLATFORM_SCHEMA_MODERN, - async_setup_entry_from_discovery, -) - -# Configuring MQTT Device Trackers under the device_tracker platform key is deprecated in HA Core 2022.6 -# Setup for the legacy YAML format was removed in HA Core 2022.12 -PLATFORM_SCHEMA = vol.All(warn_for_legacy_schema(device_tracker.DOMAIN)) - - -async def async_setup_entry( - hass: HomeAssistant, - config_entry: ConfigEntry, - async_add_entities: AddEntitiesCallback, -) -> None: - """Set up MQTT device_tracker through configuration.yaml and dynamically through MQTT discovery.""" - await async_setup_entry_from_discovery(hass, config_entry, async_add_entities) diff --git a/tests/components/mqtt/test_device_tracker_discovery.py b/tests/components/mqtt/test_device_tracker.py similarity index 99% rename from tests/components/mqtt/test_device_tracker_discovery.py rename to tests/components/mqtt/test_device_tracker.py index 873ca1ed9a1..6db5811afd4 100644 --- a/tests/components/mqtt/test_device_tracker_discovery.py +++ b/tests/components/mqtt/test_device_tracker.py @@ -1,4 +1,4 @@ -"""The tests for the MQTT device_tracker platform.""" +"""The tests for the MQTT device_tracker platform.""" from unittest.mock import patch From 95cbf7cca7c9b02f32a9b5a617e90266274149ff Mon Sep 17 00:00:00 2001 From: Matrix Date: Wed, 23 Nov 2022 23:27:51 +0800 Subject: [PATCH 0647/1033] Add yolink Dimmer support (#81970) * Add yolink Dimmer support * suggest change * fix suggest * fix suggest * fix suggest --- .coveragerc | 1 + homeassistant/components/yolink/__init__.py | 1 + homeassistant/components/yolink/const.py | 1 + homeassistant/components/yolink/light.py | 75 +++++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 homeassistant/components/yolink/light.py diff --git a/.coveragerc b/.coveragerc index f3931e47155..bf493a6a0a8 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1572,6 +1572,7 @@ omit = homeassistant/components/yolink/coordinator.py homeassistant/components/yolink/cover.py homeassistant/components/yolink/entity.py + homeassistant/components/yolink/light.py homeassistant/components/yolink/lock.py homeassistant/components/yolink/sensor.py homeassistant/components/yolink/siren.py diff --git a/homeassistant/components/yolink/__init__.py b/homeassistant/components/yolink/__init__.py index 06e6fd6472a..58407df38a3 100644 --- a/homeassistant/components/yolink/__init__.py +++ b/homeassistant/components/yolink/__init__.py @@ -28,6 +28,7 @@ PLATFORMS = [ Platform.BINARY_SENSOR, Platform.CLIMATE, Platform.COVER, + Platform.LIGHT, Platform.LOCK, Platform.SENSOR, Platform.SIREN, diff --git a/homeassistant/components/yolink/const.py b/homeassistant/components/yolink/const.py index f6add984dc2..14279ebeae0 100644 --- a/homeassistant/components/yolink/const.py +++ b/homeassistant/components/yolink/const.py @@ -25,3 +25,4 @@ ATTR_DEVICE_MANIPULATOR = "Manipulator" ATTR_DEVICE_CO_SMOKE_SENSOR = "COSmokeSensor" ATTR_DEVICE_SWITCH = "Switch" ATTR_DEVICE_THERMOSTAT = "Thermostat" +ATTR_DEVICE_DIMMER = "Dimmer" diff --git a/homeassistant/components/yolink/light.py b/homeassistant/components/yolink/light.py new file mode 100644 index 00000000000..fbb8c8d50c6 --- /dev/null +++ b/homeassistant/components/yolink/light.py @@ -0,0 +1,75 @@ +"""YoLink Dimmer.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import ATTR_COORDINATORS, ATTR_DEVICE_DIMMER, DOMAIN +from .coordinator import YoLinkCoordinator +from .entity import YoLinkEntity + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up YoLink Dimmer from a config entry.""" + device_coordinators = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATORS] + entities = [ + YoLinkDimmerEntity(config_entry, device_coordinator) + for device_coordinator in device_coordinators.values() + if device_coordinator.device.device_type == ATTR_DEVICE_DIMMER + ] + async_add_entities(entities) + + +class YoLinkDimmerEntity(YoLinkEntity, LightEntity): + """YoLink Dimmer Entity.""" + + _attr_color_mode = ColorMode.BRIGHTNESS + _attr_has_entity_name = True + _attr_name = None + _attr_supported_color_modes: set[ColorMode] = {ColorMode.BRIGHTNESS} + + def __init__( + self, + config_entry: ConfigEntry, + coordinator: YoLinkCoordinator, + ) -> None: + """Init YoLink Dimmer entity.""" + super().__init__(config_entry, coordinator) + self._attr_unique_id = f"{coordinator.device.device_id}" + + @callback + def update_entity_state(self, state: dict[str, Any]) -> None: + """Update HA Entity State.""" + if (dimmer_is_on := state.get("state")) is not None: + # update _attr_is_on when device report it's state + self._attr_is_on = dimmer_is_on + if (brightness := state.get("brightness")) is not None: + self._attr_brightness = round(255 * brightness / 100) + self.async_write_ha_state() + + async def toggle_light_state(self, state: str, brightness: int | None) -> None: + """Toggle light state.""" + params: dict[str, Any] = {"state": state} + if brightness is not None: + self._attr_brightness = brightness + params["brightness"] = round(brightness / 255, 2) * 100 + await self.call_device_api("setState", params) + self._attr_is_on = state == "open" + self.async_write_ha_state() + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn on light.""" + brightness = kwargs.get(ATTR_BRIGHTNESS) + await self.toggle_light_state("open", brightness) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn off light.""" + await self.toggle_light_state("close", None) From aa02a53ac667d08c66a536baf139993bcfe4d7d6 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 23 Nov 2022 17:46:51 +0100 Subject: [PATCH 0648/1033] Add type hints to template states (#82582) * Add type hints to template states * Undo rename * Remove invalid mypy issue link --- homeassistant/helpers/template.py | 35 +++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 24852ab5cf3..69986570a69 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -40,6 +40,7 @@ from homeassistant.const import ( STATE_UNKNOWN, ) from homeassistant.core import ( + Context, HomeAssistant, State, callback, @@ -683,7 +684,7 @@ class AllStates: if render_info is not None: render_info.all_states_lifecycle = True - def __iter__(self): + def __iter__(self) -> Generator[TemplateState, None, None]: """Return all states.""" self._collect_all() return _state_generator(self._hass, None) @@ -693,7 +694,7 @@ class AllStates: self._collect_all_lifecycle() return self._hass.states.async_entity_ids_count() - def __call__(self, entity_id): + def __call__(self, entity_id: str) -> str: """Return the states.""" state = _get_state(self._hass, entity_id) return STATE_UNKNOWN if state is None else state.state @@ -716,7 +717,7 @@ class DomainStates: self._hass = hass self._domain = domain - def __getattr__(self, name): + def __getattr__(self, name: str) -> TemplateState | None: """Return the states.""" return _get_state_if_valid(self._hass, f"{self._domain}.{name}") @@ -734,7 +735,7 @@ class DomainStates: if entity_collect is not None: entity_collect.domains_lifecycle.add(self._domain) - def __iter__(self): + def __iter__(self) -> Generator[TemplateState, None, None]: """Return the iteration over all the states.""" self._collect_domain() return _state_generator(self._hass, self._domain) @@ -774,7 +775,7 @@ class TemplateStateBase(State): # Jinja will try __getitem__ first and it avoids the need # to call is_safe_attribute - def __getitem__(self, item): + def __getitem__(self, item: str) -> Any: """Return a property as an attribute for jinja.""" if item in _COLLECTABLE_STATE_ATTRIBUTES: # _collect_state inlined here for performance @@ -788,7 +789,7 @@ class TemplateStateBase(State): raise KeyError @property - def entity_id(self): + def entity_id(self) -> str: # type: ignore[override] """Wrap State.entity_id. Intentionally does not collect state @@ -796,49 +797,49 @@ class TemplateStateBase(State): return self._entity_id @property - def state(self): + def state(self) -> str: # type: ignore[override] """Wrap State.state.""" self._collect_state() return self._state.state @property - def attributes(self): + def attributes(self) -> ReadOnlyDict[str, Any]: # type: ignore[override] """Wrap State.attributes.""" self._collect_state() return self._state.attributes @property - def last_changed(self): + def last_changed(self) -> datetime: # type: ignore[override] """Wrap State.last_changed.""" self._collect_state() return self._state.last_changed @property - def last_updated(self): + def last_updated(self) -> datetime: # type: ignore[override] """Wrap State.last_updated.""" self._collect_state() return self._state.last_updated @property - def context(self): + def context(self) -> Context: # type: ignore[override] """Wrap State.context.""" self._collect_state() return self._state.context @property - def domain(self): + def domain(self) -> str: # type: ignore[override] """Wrap State.domain.""" self._collect_state() return self._state.domain @property - def object_id(self): + def object_id(self) -> str: # type: ignore[override] """Wrap State.object_id.""" self._collect_state() return self._state.object_id @property - def name(self): + def name(self) -> str: """Wrap State.name.""" self._collect_state() return self._state.name @@ -882,7 +883,7 @@ class TemplateStateFromEntityId(TemplateStateBase): super().__init__(hass, collect, entity_id) @property - def _state(self) -> State: # type: ignore[override] # mypy issue 4125 + def _state(self) -> State: # type: ignore[override] state = self._hass.states.get(self._entity_id) if not state: state = State(self._entity_id, STATE_UNKNOWN) @@ -903,7 +904,9 @@ def _template_state_no_collect(hass: HomeAssistant, state: State) -> TemplateSta return TemplateState(hass, state, collect=False) -def _state_generator(hass: HomeAssistant, domain: str | None) -> Generator: +def _state_generator( + hass: HomeAssistant, domain: str | None +) -> Generator[TemplateState, None, None]: """State generator for a domain or all states.""" for state in sorted(hass.states.async_all(domain), key=attrgetter("entity_id")): yield _template_state_no_collect(hass, state) From a969f69fd57acc13b2c249069802473fccd5e91f Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Wed, 23 Nov 2022 17:47:32 +0100 Subject: [PATCH 0649/1033] Add support for controlling HomeWizard Energy Socket status light level (#82378) * Add control option for Energy Socket status LED * Add tests for number * Fix failing tests in switch * Fix comments * Adjust name * Simplify device_info --- homeassistant/components/homewizard/const.py | 2 +- homeassistant/components/homewizard/number.py | 65 ++++++++ tests/components/homewizard/test_number.py | 141 ++++++++++++++++++ 3 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/homewizard/number.py create mode 100644 tests/components/homewizard/test_number.py diff --git a/homeassistant/components/homewizard/const.py b/homeassistant/components/homewizard/const.py index c1c788d371b..677f7ec46d9 100644 --- a/homeassistant/components/homewizard/const.py +++ b/homeassistant/components/homewizard/const.py @@ -10,7 +10,7 @@ from homewizard_energy.models import Data, Device, State from homeassistant.const import Platform DOMAIN = "homewizard" -PLATFORMS = [Platform.SENSOR, Platform.SWITCH] +PLATFORMS = [Platform.SENSOR, Platform.SWITCH, Platform.NUMBER] # Platform config. CONF_API_ENABLED = "api_enabled" diff --git a/homeassistant/components/homewizard/number.py b/homeassistant/components/homewizard/number.py new file mode 100644 index 00000000000..43dccc364bd --- /dev/null +++ b/homeassistant/components/homewizard/number.py @@ -0,0 +1,65 @@ +"""Creates HomeWizard Number entities.""" +from __future__ import annotations + +from homeassistant.components.number import NumberEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import PERCENTAGE +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN +from .coordinator import HWEnergyDeviceUpdateCoordinator + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up numbers for device.""" + coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + + if coordinator.data["state"]: + async_add_entities( + [ + HWEnergyNumberEntity(coordinator, entry), + ] + ) + + +class HWEnergyNumberEntity( + CoordinatorEntity[HWEnergyDeviceUpdateCoordinator], NumberEntity +): + """Representation of status light number.""" + + _attr_entity_category = EntityCategory.CONFIG + _attr_has_entity_name = True + + def __init__( + self, + coordinator: HWEnergyDeviceUpdateCoordinator, + entry: ConfigEntry, + ) -> None: + """Initialize the control number.""" + super().__init__(coordinator) + self._attr_unique_id = f"{entry.unique_id}_status_light_brightness" + self._attr_name = "Status light brightness" + self._attr_native_unit_of_measurement = PERCENTAGE + self._attr_icon = "mdi:lightbulb-on" + self._attr_device_info = { + "identifiers": {(DOMAIN, coordinator.data["device"].serial)}, + } + + async def async_set_native_value(self, value: float) -> None: + """Set a new value.""" + await self.coordinator.api.state_set(brightness=value * (255 / 100)) + await self.coordinator.async_refresh() + + @property + def native_value(self) -> float | None: + """Return the current value.""" + if self.coordinator.data["state"].brightness is None: + return None + return round(self.coordinator.data["state"].brightness * (100 / 255)) diff --git a/tests/components/homewizard/test_number.py b/tests/components/homewizard/test_number.py new file mode 100644 index 00000000000..9538fd3cef9 --- /dev/null +++ b/tests/components/homewizard/test_number.py @@ -0,0 +1,141 @@ +"""Test the update coordinator for HomeWizard.""" + +from unittest.mock import AsyncMock, patch + +from homewizard_energy.models import State + +from homeassistant.components import number +from homeassistant.components.number import ATTR_VALUE, SERVICE_SET_VALUE +from homeassistant.const import ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME +from homeassistant.helpers import entity_registry as er + +from .generator import get_mock_device + + +async def test_number_entity_not_loaded_when_not_available( + hass, mock_config_entry_data, mock_config_entry +): + """Test entity does not load number when brightness is not available.""" + + api = get_mock_device() + + with patch( + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", + return_value=api, + ): + entry = mock_config_entry + entry.data = mock_config_entry_data + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert ( + hass.states.get("number.product_name_aabbccddeeff_status_light_brightness") + is None + ) + + +async def test_number_loads_entities(hass, mock_config_entry_data, mock_config_entry): + """Test entity does load number when brightness is available.""" + + api = get_mock_device() + api.state = AsyncMock(return_value=State.from_dict({"brightness": 255})) + + with patch( + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", + return_value=api, + ): + entry = mock_config_entry + entry.data = mock_config_entry_data + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + entity_registry = er.async_get(hass) + + state = hass.states.get("number.product_name_aabbccddeeff_status_light_brightness") + assert state + assert state.state == "100" + assert ( + state.attributes.get(ATTR_FRIENDLY_NAME) + == "Product Name (aabbccddeeff) Status light brightness" + ) + + entry = entity_registry.async_get( + "number.product_name_aabbccddeeff_status_light_brightness" + ) + assert entry + assert entry.unique_id == "aabbccddeeff_status_light_brightness" + assert not entry.disabled + + +async def test_brightness_level_set(hass, mock_config_entry_data, mock_config_entry): + """Test entity turns sets light level.""" + + api = get_mock_device() + api.state = AsyncMock(return_value=State.from_dict({"brightness": 255})) + + def state_set(brightness): + api.state = AsyncMock(return_value=State.from_dict({"brightness": brightness})) + + api.state_set = AsyncMock(side_effect=state_set) + + with patch( + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", + return_value=api, + ): + entry = mock_config_entry + entry.data = mock_config_entry_data + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert ( + hass.states.get( + "number.product_name_aabbccddeeff_status_light_brightness" + ).state + == "100" + ) + + # Set level halfway + await hass.services.async_call( + number.DOMAIN, + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: "number.product_name_aabbccddeeff_status_light_brightness", + ATTR_VALUE: 50, + }, + blocking=True, + ) + + await hass.async_block_till_done() + assert ( + hass.states.get( + "number.product_name_aabbccddeeff_status_light_brightness" + ).state + == "50" + ) + assert len(api.state_set.mock_calls) == 1 + + # Turn off level + await hass.services.async_call( + number.DOMAIN, + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: "number.product_name_aabbccddeeff_status_light_brightness", + ATTR_VALUE: 0, + }, + blocking=True, + ) + + await hass.async_block_till_done() + assert ( + hass.states.get( + "number.product_name_aabbccddeeff_status_light_brightness" + ).state + == "0" + ) + assert len(api.state_set.mock_calls) == 2 From 0b5357de44263f201caab37a284b86705ce3a0bb Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 23 Nov 2022 17:49:59 +0100 Subject: [PATCH 0650/1033] Cleanup Climacell translations (#82584) --- .../components/climacell/translations/af.json | 9 ------- .../components/climacell/translations/ca.json | 13 --------- .../components/climacell/translations/de.json | 13 --------- .../components/climacell/translations/el.json | 13 --------- .../components/climacell/translations/en.json | 13 --------- .../climacell/translations/es-419.json | 13 --------- .../components/climacell/translations/es.json | 13 --------- .../components/climacell/translations/et.json | 13 --------- .../components/climacell/translations/fr.json | 13 --------- .../components/climacell/translations/hu.json | 13 --------- .../components/climacell/translations/id.json | 13 --------- .../components/climacell/translations/it.json | 13 --------- .../components/climacell/translations/ja.json | 13 --------- .../components/climacell/translations/ko.json | 13 --------- .../components/climacell/translations/nl.json | 13 --------- .../components/climacell/translations/no.json | 13 --------- .../components/climacell/translations/pl.json | 13 --------- .../climacell/translations/pt-BR.json | 13 --------- .../components/climacell/translations/ru.json | 13 --------- .../climacell/translations/sensor.bg.json | 8 ------ .../climacell/translations/sensor.ca.json | 27 ------------------- .../climacell/translations/sensor.de.json | 27 ------------------- .../climacell/translations/sensor.el.json | 27 ------------------- .../climacell/translations/sensor.en.json | 27 ------------------- .../climacell/translations/sensor.es-419.json | 27 ------------------- .../climacell/translations/sensor.es.json | 27 ------------------- .../climacell/translations/sensor.et.json | 27 ------------------- .../climacell/translations/sensor.fr.json | 27 ------------------- .../climacell/translations/sensor.he.json | 7 ----- .../climacell/translations/sensor.hu.json | 27 ------------------- .../climacell/translations/sensor.id.json | 27 ------------------- .../climacell/translations/sensor.is.json | 12 --------- .../climacell/translations/sensor.it.json | 27 ------------------- .../climacell/translations/sensor.ja.json | 27 ------------------- .../climacell/translations/sensor.ko.json | 7 ----- .../climacell/translations/sensor.lv.json | 27 ------------------- .../climacell/translations/sensor.nl.json | 27 ------------------- .../climacell/translations/sensor.no.json | 27 ------------------- .../climacell/translations/sensor.pl.json | 27 ------------------- .../climacell/translations/sensor.pt-BR.json | 27 ------------------- .../climacell/translations/sensor.pt.json | 7 ----- .../climacell/translations/sensor.ru.json | 27 ------------------- .../climacell/translations/sensor.sk.json | 18 ------------- .../climacell/translations/sensor.sv.json | 27 ------------------- .../climacell/translations/sensor.tr.json | 27 ------------------- .../translations/sensor.zh-Hant.json | 27 ------------------- .../components/climacell/translations/sv.json | 13 --------- .../components/climacell/translations/tr.json | 13 --------- .../climacell/translations/zh-Hant.json | 13 --------- 49 files changed, 908 deletions(-) delete mode 100644 homeassistant/components/climacell/translations/af.json delete mode 100644 homeassistant/components/climacell/translations/ca.json delete mode 100644 homeassistant/components/climacell/translations/de.json delete mode 100644 homeassistant/components/climacell/translations/el.json delete mode 100644 homeassistant/components/climacell/translations/en.json delete mode 100644 homeassistant/components/climacell/translations/es-419.json delete mode 100644 homeassistant/components/climacell/translations/es.json delete mode 100644 homeassistant/components/climacell/translations/et.json delete mode 100644 homeassistant/components/climacell/translations/fr.json delete mode 100644 homeassistant/components/climacell/translations/hu.json delete mode 100644 homeassistant/components/climacell/translations/id.json delete mode 100644 homeassistant/components/climacell/translations/it.json delete mode 100644 homeassistant/components/climacell/translations/ja.json delete mode 100644 homeassistant/components/climacell/translations/ko.json delete mode 100644 homeassistant/components/climacell/translations/nl.json delete mode 100644 homeassistant/components/climacell/translations/no.json delete mode 100644 homeassistant/components/climacell/translations/pl.json delete mode 100644 homeassistant/components/climacell/translations/pt-BR.json delete mode 100644 homeassistant/components/climacell/translations/ru.json delete mode 100644 homeassistant/components/climacell/translations/sensor.bg.json delete mode 100644 homeassistant/components/climacell/translations/sensor.ca.json delete mode 100644 homeassistant/components/climacell/translations/sensor.de.json delete mode 100644 homeassistant/components/climacell/translations/sensor.el.json delete mode 100644 homeassistant/components/climacell/translations/sensor.en.json delete mode 100644 homeassistant/components/climacell/translations/sensor.es-419.json delete mode 100644 homeassistant/components/climacell/translations/sensor.es.json delete mode 100644 homeassistant/components/climacell/translations/sensor.et.json delete mode 100644 homeassistant/components/climacell/translations/sensor.fr.json delete mode 100644 homeassistant/components/climacell/translations/sensor.he.json delete mode 100644 homeassistant/components/climacell/translations/sensor.hu.json delete mode 100644 homeassistant/components/climacell/translations/sensor.id.json delete mode 100644 homeassistant/components/climacell/translations/sensor.is.json delete mode 100644 homeassistant/components/climacell/translations/sensor.it.json delete mode 100644 homeassistant/components/climacell/translations/sensor.ja.json delete mode 100644 homeassistant/components/climacell/translations/sensor.ko.json delete mode 100644 homeassistant/components/climacell/translations/sensor.lv.json delete mode 100644 homeassistant/components/climacell/translations/sensor.nl.json delete mode 100644 homeassistant/components/climacell/translations/sensor.no.json delete mode 100644 homeassistant/components/climacell/translations/sensor.pl.json delete mode 100644 homeassistant/components/climacell/translations/sensor.pt-BR.json delete mode 100644 homeassistant/components/climacell/translations/sensor.pt.json delete mode 100644 homeassistant/components/climacell/translations/sensor.ru.json delete mode 100644 homeassistant/components/climacell/translations/sensor.sk.json delete mode 100644 homeassistant/components/climacell/translations/sensor.sv.json delete mode 100644 homeassistant/components/climacell/translations/sensor.tr.json delete mode 100644 homeassistant/components/climacell/translations/sensor.zh-Hant.json delete mode 100644 homeassistant/components/climacell/translations/sv.json delete mode 100644 homeassistant/components/climacell/translations/tr.json delete mode 100644 homeassistant/components/climacell/translations/zh-Hant.json diff --git a/homeassistant/components/climacell/translations/af.json b/homeassistant/components/climacell/translations/af.json deleted file mode 100644 index d05e07e4eff..00000000000 --- a/homeassistant/components/climacell/translations/af.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "options": { - "step": { - "init": { - "title": "Update [%key:component::climacell::title%] opties" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ca.json b/homeassistant/components/climacell/translations/ca.json deleted file mode 100644 index 2b6abb46737..00000000000 --- a/homeassistant/components/climacell/translations/ca.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Minuts entre previsions NowCast" - }, - "description": "Si decideixes activar l'entitat de previsi\u00f3 `nowcast`, podr\u00e0s configurar l'interval en minuts entre cada previsi\u00f3. El nombre de previsions proporcionades dep\u00e8n d'aquest interval de minuts.", - "title": "Actualitzaci\u00f3 d'opcions de ClimaCell" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/de.json b/homeassistant/components/climacell/translations/de.json deleted file mode 100644 index 7c3e929dde2..00000000000 --- a/homeassistant/components/climacell/translations/de.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Minuten zwischen den NowCast Kurzvorhersagen" - }, - "description": "Wenn du die Vorhersage-Entitit\u00e4t \"Kurzvorhersage\" aktivierst, kannst du die Anzahl der Minuten zwischen den einzelnen Vorhersagen konfigurieren. Die Anzahl der bereitgestellten Vorhersagen h\u00e4ngt von der Anzahl der zwischen den Vorhersagen gew\u00e4hlten Minuten ab.", - "title": "ClimaCell-Optionen aktualisieren" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/el.json b/homeassistant/components/climacell/translations/el.json deleted file mode 100644 index 392573f693c..00000000000 --- a/homeassistant/components/climacell/translations/el.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "\u039b\u03b5\u03c0\u03c4\u03ac \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03c4\u03c9\u03bd \u03b4\u03b5\u03bb\u03c4\u03af\u03c9\u03bd NowCast" - }, - "description": "\u0395\u03ac\u03bd \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b4\u03b5\u03bb\u03c4\u03af\u03c9\u03bd 'nowcast', \u03bc\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03bb\u03b5\u03c0\u03c4\u03ce\u03bd \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03ba\u03ac\u03b8\u03b5 \u03b4\u03b5\u03bb\u03c4\u03af\u03bf\u03c5. \u039f \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03c4\u03c9\u03bd \u03b4\u03b5\u03bb\u03c4\u03af\u03c9\u03bd \u03c0\u03bf\u03c5 \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b5\u03be\u03b1\u03c1\u03c4\u03ac\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03bb\u03b5\u03c0\u03c4\u03ce\u03bd \u03c0\u03bf\u03c5 \u03b5\u03c0\u03b9\u03bb\u03ad\u03b3\u03bf\u03bd\u03c4\u03b1\u03b9 \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03c4\u03c9\u03bd \u03b4\u03b5\u03bb\u03c4\u03af\u03c9\u03bd.", - "title": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd ClimaCell" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/en.json b/homeassistant/components/climacell/translations/en.json deleted file mode 100644 index a35be85d5b2..00000000000 --- a/homeassistant/components/climacell/translations/en.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Min. Between NowCast Forecasts" - }, - "description": "If you choose to enable the `nowcast` forecast entity, you can configure the number of minutes between each forecast. The number of forecasts provided depends on the number of minutes chosen between forecasts.", - "title": "Update ClimaCell Options" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/es-419.json b/homeassistant/components/climacell/translations/es-419.json deleted file mode 100644 index 449ad1ba367..00000000000 --- a/homeassistant/components/climacell/translations/es-419.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Min. entre pron\u00f3sticos de NowCast" - }, - "description": "Si elige habilitar la entidad de pron\u00f3stico \"nowcast\", puede configurar el n\u00famero de minutos entre cada pron\u00f3stico. El n\u00famero de pron\u00f3sticos proporcionados depende del n\u00famero de minutos elegidos entre los pron\u00f3sticos.", - "title": "Actualizar opciones de ClimaCell" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/es.json b/homeassistant/components/climacell/translations/es.json deleted file mode 100644 index 270d72bd58c..00000000000 --- a/homeassistant/components/climacell/translations/es.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Min. entre pron\u00f3sticos de NowCast" - }, - "description": "Si eliges habilitar la entidad de pron\u00f3stico `nowcast`, puedes configurar la cantidad de minutos entre cada pron\u00f3stico. La cantidad de pron\u00f3sticos proporcionados depende de la cantidad de minutos elegidos entre los pron\u00f3sticos.", - "title": "Actualizar opciones de ClimaCell" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/et.json b/homeassistant/components/climacell/translations/et.json deleted file mode 100644 index 5d915a87d80..00000000000 --- a/homeassistant/components/climacell/translations/et.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Minuteid NowCasti prognooside vahel" - }, - "description": "Kui otsustad lubada \"nowcast\" prognoosi\u00fcksuse, saad seadistada minutite arvu iga prognoosi vahel. Esitatavate prognooside arv s\u00f5ltub prognooside vahel valitud minutite arvust.", - "title": "V\u00e4rskenda [%key:component::climacell::title%] suvandeid" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/fr.json b/homeassistant/components/climacell/translations/fr.json deleted file mode 100644 index b2c1285ecc9..00000000000 --- a/homeassistant/components/climacell/translations/fr.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Min. entre les pr\u00e9visions NowCast" - }, - "description": "Si vous choisissez d'activer l'entit\u00e9 de pr\u00e9vision \u00ab\u00a0nowcast\u00a0\u00bb, vous pouvez configurer le nombre de minutes entre chaque pr\u00e9vision. Le nombre de pr\u00e9visions fournies d\u00e9pend du nombre de minutes choisies entre les pr\u00e9visions.", - "title": "Mettre \u00e0 jour les options ClimaCell" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/hu.json b/homeassistant/components/climacell/translations/hu.json deleted file mode 100644 index 4cad1eaaa0f..00000000000 --- a/homeassistant/components/climacell/translations/hu.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Min. A NowCast el\u0151rejelz\u00e9sek k\u00f6z\u00f6tt" - }, - "description": "Ha a `nowcast` el\u0151rejelz\u00e9si entit\u00e1s enged\u00e9lyez\u00e9s\u00e9t v\u00e1lasztja, be\u00e1ll\u00edthatja az egyes el\u0151rejelz\u00e9sek k\u00f6z\u00f6tti percek sz\u00e1m\u00e1t. A megadott el\u0151rejelz\u00e9sek sz\u00e1ma az el\u0151rejelz\u00e9sek k\u00f6z\u00f6tt kiv\u00e1lasztott percek sz\u00e1m\u00e1t\u00f3l f\u00fcgg.", - "title": "ClimaCell be\u00e1ll\u00edt\u00e1sok friss\u00edt\u00e9se" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/id.json b/homeassistant/components/climacell/translations/id.json deleted file mode 100644 index 4d020351665..00000000000 --- a/homeassistant/components/climacell/translations/id.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Jarak Interval Prakiraan NowCast dalam Menit" - }, - "description": "Jika Anda memilih untuk mengaktifkan entitas prakiraan `nowcast`, Anda dapat mengonfigurasi jarak interval prakiraan dalam menit. Jumlah prakiraan yang diberikan tergantung pada nilai interval yang dipilih.", - "title": "Perbarui Opsi ClimaCell" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/it.json b/homeassistant/components/climacell/translations/it.json deleted file mode 100644 index b9667d6bfb1..00000000000 --- a/homeassistant/components/climacell/translations/it.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Minuti tra le previsioni di NowCast" - }, - "description": "Se scegli di abilitare l'entit\u00e0 di previsione `nowcast`, puoi configurare il numero di minuti tra ogni previsione. Il numero di previsioni fornite dipende dal numero di minuti scelti tra le previsioni.", - "title": "Aggiorna le opzioni di ClimaCell" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ja.json b/homeassistant/components/climacell/translations/ja.json deleted file mode 100644 index e2742d11435..00000000000 --- a/homeassistant/components/climacell/translations/ja.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "\u6700\u5c0f\u3002 NowCast \u4e88\u6e2c\u306e\u9593" - }, - "description": "`nowcast` forecast(\u4e88\u6e2c) \u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u6709\u52b9\u306b\u3059\u308b\u3053\u3068\u3092\u9078\u629e\u3057\u305f\u5834\u5408\u3001\u5404\u4e88\u6e2c\u9593\u306e\u5206\u6570\u3092\u8a2d\u5b9a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u63d0\u4f9b\u3055\u308c\u308bforecast(\u4e88\u6e2c)\u306e\u6570\u306f\u3001forecast(\u4e88\u6e2c)\u306e\u9593\u306b\u9078\u629e\u3057\u305f\u5206\u6570\u306b\u4f9d\u5b58\u3057\u307e\u3059\u3002", - "title": "ClimaCell \u30aa\u30d7\u30b7\u30e7\u30f3\u306e\u66f4\u65b0" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ko.json b/homeassistant/components/climacell/translations/ko.json deleted file mode 100644 index 8accc07410d..00000000000 --- a/homeassistant/components/climacell/translations/ko.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "\ub2e8\uae30\uc608\uce21 \uc77c\uae30\uc608\ubcf4 \uac04 \ucd5c\uc18c \uc2dc\uac04" - }, - "description": "`nowcast` \uc77c\uae30\uc608\ubcf4 \uad6c\uc131\uc694\uc18c\ub97c \uc0ac\uc6a9\ud558\ub3c4\ub85d \uc120\ud0dd\ud55c \uacbd\uc6b0 \uac01 \uc77c\uae30\uc608\ubcf4 \uc0ac\uc774\uc758 \uc2dc\uac04(\ubd84)\uc744 \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \uc81c\uacf5\ub41c \uc77c\uae30\uc608\ubcf4 \ud69f\uc218\ub294 \uc608\uce21 \uac04 \uc120\ud0dd\ud55c \uc2dc\uac04(\ubd84)\uc5d0 \ub530\ub77c \ub2ec\ub77c\uc9d1\ub2c8\ub2e4.", - "title": "[%key:component::climacell::title%] \uc635\uc158 \uc5c5\ub370\uc774\ud2b8\ud558\uae30" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/nl.json b/homeassistant/components/climacell/translations/nl.json deleted file mode 100644 index a895fa8234d..00000000000 --- a/homeassistant/components/climacell/translations/nl.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Min. Tussen NowCast-voorspellingen" - }, - "description": "Als u ervoor kiest om de `nowcast` voorspellingsentiteit in te schakelen, kan u het aantal minuten tussen elke voorspelling configureren. Het aantal voorspellingen hangt af van het aantal gekozen minuten tussen de voorspellingen.", - "title": "Update ClimaCell Opties" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/no.json b/homeassistant/components/climacell/translations/no.json deleted file mode 100644 index 9f050624967..00000000000 --- a/homeassistant/components/climacell/translations/no.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Min. mellom NowCast prognoser" - }, - "description": "Hvis du velger \u00e5 aktivere \u00abnowcast\u00bb -varselentiteten, kan du konfigurere antall minutter mellom hver prognose. Antall angitte prognoser avhenger av antall minutter som er valgt mellom prognosene.", - "title": "Oppdater ClimaCell-alternativer" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/pl.json b/homeassistant/components/climacell/translations/pl.json deleted file mode 100644 index 5f69764ffab..00000000000 --- a/homeassistant/components/climacell/translations/pl.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Czas (min) mi\u0119dzy prognozami NowCast" - }, - "description": "Je\u015bli zdecydujesz si\u0119 w\u0142\u0105czy\u0107 encj\u0119 prognozy \u201enowcast\u201d, mo\u017cesz skonfigurowa\u0107 liczb\u0119 minut mi\u0119dzy ka\u017cd\u0105 prognoz\u0105. Liczba dostarczonych prognoz zale\u017cy od liczby minut wybranych mi\u0119dzy prognozami.", - "title": "Opcje aktualizacji ClimaCell" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/pt-BR.json b/homeassistant/components/climacell/translations/pt-BR.json deleted file mode 100644 index b7e71d45971..00000000000 --- a/homeassistant/components/climacell/translations/pt-BR.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "M\u00ednimo entre previs\u00f5es NowCast" - }, - "description": "Se voc\u00ea optar por ativar a entidade de previs\u00e3o `nowcast`, poder\u00e1 configurar o n\u00famero de minutos entre cada previs\u00e3o. O n\u00famero de previs\u00f5es fornecidas depende do n\u00famero de minutos escolhidos entre as previs\u00f5es.", - "title": "Atualizar as op\u00e7\u00f5es do ClimaCell" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ru.json b/homeassistant/components/climacell/translations/ru.json deleted file mode 100644 index 9f3219ce4d6..00000000000 --- a/homeassistant/components/climacell/translations/ru.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f (\u0432 \u043c\u0438\u043d\u0443\u0442\u0430\u0445)" - }, - "description": "\u0415\u0441\u043b\u0438 \u0412\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u0442\u0435 \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 'nowcast', \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430.", - "title": "\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 ClimaCell" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.bg.json b/homeassistant/components/climacell/translations/sensor.bg.json deleted file mode 100644 index 04f393f1d99..00000000000 --- a/homeassistant/components/climacell/translations/sensor.bg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "state": { - "climacell__precipitation_type": { - "rain": "\u0414\u044a\u0436\u0434", - "snow": "\u0421\u043d\u044f\u0433" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.ca.json b/homeassistant/components/climacell/translations/sensor.ca.json deleted file mode 100644 index 359857925da..00000000000 --- a/homeassistant/components/climacell/translations/sensor.ca.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Bo", - "hazardous": "Perill\u00f3s", - "moderate": "Moderat", - "unhealthy": "Poc saludable", - "unhealthy_for_sensitive_groups": "No saludable per a grups sensibles", - "very_unhealthy": "Gens saludable" - }, - "climacell__pollen_index": { - "high": "Alt", - "low": "Baix", - "medium": "Mitj\u00e0", - "none": "Cap", - "very_high": "Molt alt", - "very_low": "Molt baix" - }, - "climacell__precipitation_type": { - "freezing_rain": "Pluja congelada", - "ice_pellets": "Gran\u00eds", - "none": "Cap", - "rain": "Pluja", - "snow": "Neu" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.de.json b/homeassistant/components/climacell/translations/sensor.de.json deleted file mode 100644 index 93a1e5e8e98..00000000000 --- a/homeassistant/components/climacell/translations/sensor.de.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Gut", - "hazardous": "Gef\u00e4hrlich", - "moderate": "M\u00e4\u00dfig", - "unhealthy": "Ungesund", - "unhealthy_for_sensitive_groups": "Ungesund f\u00fcr sensible Gruppen", - "very_unhealthy": "Sehr ungesund" - }, - "climacell__pollen_index": { - "high": "Hoch", - "low": "Niedrig", - "medium": "Mittel", - "none": "Keine", - "very_high": "Sehr hoch", - "very_low": "Sehr niedrig" - }, - "climacell__precipitation_type": { - "freezing_rain": "Gefrierender Regen", - "ice_pellets": "Graupel", - "none": "Keine", - "rain": "Regen", - "snow": "Schnee" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.el.json b/homeassistant/components/climacell/translations/sensor.el.json deleted file mode 100644 index facd86ed7c6..00000000000 --- a/homeassistant/components/climacell/translations/sensor.el.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "\u039a\u03b1\u03bb\u03cc", - "hazardous": "\u0395\u03c0\u03b9\u03ba\u03af\u03bd\u03b4\u03c5\u03bd\u03bf", - "moderate": "\u039c\u03ad\u03c4\u03c1\u03b9\u03bf", - "unhealthy": "\u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc", - "unhealthy_for_sensitive_groups": "\u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc \u03b3\u03b9\u03b1 \u03b5\u03c5\u03b1\u03af\u03c3\u03b8\u03b7\u03c4\u03b5\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b5\u03c2", - "very_unhealthy": "\u03a0\u03bf\u03bb\u03cd \u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc" - }, - "climacell__pollen_index": { - "high": "\u03a5\u03c8\u03b7\u03bb\u03cc", - "low": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03cc", - "medium": "\u039c\u03b5\u03c3\u03b1\u03af\u03bf", - "none": "\u03a4\u03af\u03c0\u03bf\u03c4\u03b1", - "very_high": "\u03a0\u03bf\u03bb\u03cd \u03c5\u03c8\u03b7\u03bb\u03cc", - "very_low": "\u03a0\u03bf\u03bb\u03cd \u03c7\u03b1\u03bc\u03b7\u03bb\u03cc" - }, - "climacell__precipitation_type": { - "freezing_rain": "\u03a0\u03b1\u03b3\u03c9\u03bc\u03ad\u03bd\u03b7 \u03b2\u03c1\u03bf\u03c7\u03ae", - "ice_pellets": "\u03a0\u03ad\u03bb\u03bb\u03b5\u03c4 \u03c0\u03ac\u03b3\u03bf\u03c5", - "none": "\u03a4\u03af\u03c0\u03bf\u03c4\u03b1", - "rain": "\u0392\u03c1\u03bf\u03c7\u03ae", - "snow": "\u03a7\u03b9\u03cc\u03bd\u03b9" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.en.json b/homeassistant/components/climacell/translations/sensor.en.json deleted file mode 100644 index 0cb1d27aaec..00000000000 --- a/homeassistant/components/climacell/translations/sensor.en.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Good", - "hazardous": "Hazardous", - "moderate": "Moderate", - "unhealthy": "Unhealthy", - "unhealthy_for_sensitive_groups": "Unhealthy for Sensitive Groups", - "very_unhealthy": "Very Unhealthy" - }, - "climacell__pollen_index": { - "high": "High", - "low": "Low", - "medium": "Medium", - "none": "None", - "very_high": "Very High", - "very_low": "Very Low" - }, - "climacell__precipitation_type": { - "freezing_rain": "Freezing Rain", - "ice_pellets": "Ice Pellets", - "none": "None", - "rain": "Rain", - "snow": "Snow" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.es-419.json b/homeassistant/components/climacell/translations/sensor.es-419.json deleted file mode 100644 index 127177e84b4..00000000000 --- a/homeassistant/components/climacell/translations/sensor.es-419.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Bueno", - "hazardous": "Peligroso", - "moderate": "Moderado", - "unhealthy": "Insalubre", - "unhealthy_for_sensitive_groups": "Insalubre para grupos sensibles", - "very_unhealthy": "Muy poco saludable" - }, - "climacell__pollen_index": { - "high": "Alto", - "low": "Bajo", - "medium": "Medio", - "none": "Ninguno", - "very_high": "Muy alto", - "very_low": "Muy bajo" - }, - "climacell__precipitation_type": { - "freezing_rain": "Lluvia helada", - "ice_pellets": "Gr\u00e1nulos de hielo", - "none": "Ninguno", - "rain": "Lluvia", - "snow": "Nieve" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.es.json b/homeassistant/components/climacell/translations/sensor.es.json deleted file mode 100644 index 4cb1b34eb21..00000000000 --- a/homeassistant/components/climacell/translations/sensor.es.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Bueno", - "hazardous": "Peligroso", - "moderate": "Moderado", - "unhealthy": "No saludable", - "unhealthy_for_sensitive_groups": "No es saludable para grupos sensibles", - "very_unhealthy": "Muy poco saludable" - }, - "climacell__pollen_index": { - "high": "Alto", - "low": "Bajo", - "medium": "Medio", - "none": "Ninguno", - "very_high": "Muy alto", - "very_low": "Muy bajo" - }, - "climacell__precipitation_type": { - "freezing_rain": "Lluvia helada", - "ice_pellets": "Granizo", - "none": "Ninguna", - "rain": "Lluvia", - "snow": "Nieve" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.et.json b/homeassistant/components/climacell/translations/sensor.et.json deleted file mode 100644 index a0b7ac0562b..00000000000 --- a/homeassistant/components/climacell/translations/sensor.et.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Normaalne", - "hazardous": "Ohtlik", - "moderate": "M\u00f5\u00f5dukas", - "unhealthy": "Ebatervislik", - "unhealthy_for_sensitive_groups": "Ebatervislik riskir\u00fchmale", - "very_unhealthy": "V\u00e4ga ebatervislik" - }, - "climacell__pollen_index": { - "high": "K\u00f5rge", - "low": "Madal", - "medium": "Keskmine", - "none": "Puudub", - "very_high": "V\u00e4ga k\u00f5rge", - "very_low": "V\u00e4ga madal" - }, - "climacell__precipitation_type": { - "freezing_rain": "J\u00e4\u00e4vihm", - "ice_pellets": "J\u00e4\u00e4kruubid", - "none": "Sademeid pole", - "rain": "Vihm", - "snow": "Lumi" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.fr.json b/homeassistant/components/climacell/translations/sensor.fr.json deleted file mode 100644 index acff91fc570..00000000000 --- a/homeassistant/components/climacell/translations/sensor.fr.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Bon", - "hazardous": "Dangereux", - "moderate": "Mod\u00e9r\u00e9", - "unhealthy": "Mauvais pour la sant\u00e9", - "unhealthy_for_sensitive_groups": "Mauvaise qualit\u00e9 pour les groupes sensibles", - "very_unhealthy": "Tr\u00e8s mauvais pour la sant\u00e9" - }, - "climacell__pollen_index": { - "high": "Haut", - "low": "Faible", - "medium": "Moyen", - "none": "Aucun", - "very_high": "Tr\u00e8s \u00e9lev\u00e9", - "very_low": "Tr\u00e8s faible" - }, - "climacell__precipitation_type": { - "freezing_rain": "Pluie vergla\u00e7ante", - "ice_pellets": "Gr\u00e9sil", - "none": "Aucun", - "rain": "Pluie", - "snow": "Neige" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.he.json b/homeassistant/components/climacell/translations/sensor.he.json deleted file mode 100644 index 2a509464928..00000000000 --- a/homeassistant/components/climacell/translations/sensor.he.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "unhealthy_for_sensitive_groups": "\u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0 \u05dc\u05e7\u05d1\u05d5\u05e6\u05d5\u05ea \u05e8\u05d2\u05d9\u05e9\u05d5\u05ea" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.hu.json b/homeassistant/components/climacell/translations/sensor.hu.json deleted file mode 100644 index 656a460f429..00000000000 --- a/homeassistant/components/climacell/translations/sensor.hu.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "J\u00f3", - "hazardous": "Vesz\u00e9lyes", - "moderate": "M\u00e9rs\u00e9kelt", - "unhealthy": "Eg\u00e9szs\u00e9gtelen", - "unhealthy_for_sensitive_groups": "Eg\u00e9szs\u00e9gtelen \u00e9rz\u00e9keny csoportok sz\u00e1m\u00e1ra", - "very_unhealthy": "Nagyon eg\u00e9szs\u00e9gtelen" - }, - "climacell__pollen_index": { - "high": "Magas", - "low": "Alacsony", - "medium": "K\u00f6zepes", - "none": "Nincs", - "very_high": "Nagyon magas", - "very_low": "Nagyon alacsony" - }, - "climacell__precipitation_type": { - "freezing_rain": "Havas es\u0151", - "ice_pellets": "\u00d3nos es\u0151", - "none": "Nincs", - "rain": "Es\u0151", - "snow": "Havaz\u00e1s" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.id.json b/homeassistant/components/climacell/translations/sensor.id.json deleted file mode 100644 index 37ac0f7d876..00000000000 --- a/homeassistant/components/climacell/translations/sensor.id.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Bagus", - "hazardous": "Berbahaya", - "moderate": "Sedang", - "unhealthy": "Tidak Sehat", - "unhealthy_for_sensitive_groups": "Tidak Sehat untuk Kelompok Sensitif", - "very_unhealthy": "Sangat Tidak Sehat" - }, - "climacell__pollen_index": { - "high": "Tinggi", - "low": "Rendah", - "medium": "Sedang", - "none": "Tidak Ada", - "very_high": "Sangat Tinggi", - "very_low": "Sangat Rendah" - }, - "climacell__precipitation_type": { - "freezing_rain": "Hujan Beku", - "ice_pellets": "Hujan Es", - "none": "Tidak Ada", - "rain": "Hujan", - "snow": "Salju" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.is.json b/homeassistant/components/climacell/translations/sensor.is.json deleted file mode 100644 index bc22f9c67a9..00000000000 --- a/homeassistant/components/climacell/translations/sensor.is.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "hazardous": "H\u00e6ttulegt", - "unhealthy": "\u00d3hollt" - }, - "climacell__precipitation_type": { - "rain": "Rigning", - "snow": "Snj\u00f3r" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.it.json b/homeassistant/components/climacell/translations/sensor.it.json deleted file mode 100644 index b9326be886e..00000000000 --- a/homeassistant/components/climacell/translations/sensor.it.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Buono", - "hazardous": "Pericoloso", - "moderate": "Moderato", - "unhealthy": "Malsano", - "unhealthy_for_sensitive_groups": "Malsano per gruppi sensibili", - "very_unhealthy": "Molto malsano" - }, - "climacell__pollen_index": { - "high": "Alto", - "low": "Basso", - "medium": "Medio", - "none": "Nessuno", - "very_high": "Molto alto", - "very_low": "Molto basso" - }, - "climacell__precipitation_type": { - "freezing_rain": "Grandine", - "ice_pellets": "Pioggia gelata", - "none": "Nessuno", - "rain": "Pioggia", - "snow": "Neve" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.ja.json b/homeassistant/components/climacell/translations/sensor.ja.json deleted file mode 100644 index 6d8df99ca70..00000000000 --- a/homeassistant/components/climacell/translations/sensor.ja.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "\u826f\u597d", - "hazardous": "\u5371\u967a", - "moderate": "\u7a4f\u3084\u304b\u306a", - "unhealthy": "\u4e0d\u5065\u5eb7", - "unhealthy_for_sensitive_groups": "\u654f\u611f\u306a\u30b0\u30eb\u30fc\u30d7\u306b\u3068\u3063\u3066\u306f\u4e0d\u5065\u5eb7", - "very_unhealthy": "\u975e\u5e38\u306b\u4e0d\u5065\u5eb7" - }, - "climacell__pollen_index": { - "high": "\u9ad8\u3044", - "low": "\u4f4e\u3044", - "medium": "\u4e2d", - "none": "\u306a\u3057", - "very_high": "\u975e\u5e38\u306b\u9ad8\u3044", - "very_low": "\u3068\u3066\u3082\u4f4e\u3044" - }, - "climacell__precipitation_type": { - "freezing_rain": "\u51cd\u3066\u3064\u304f\u96e8", - "ice_pellets": "\u51cd\u96e8", - "none": "\u306a\u3057", - "rain": "\u96e8", - "snow": "\u96ea" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.ko.json b/homeassistant/components/climacell/translations/sensor.ko.json deleted file mode 100644 index e5ec616959e..00000000000 --- a/homeassistant/components/climacell/translations/sensor.ko.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "state": { - "climacell__precipitation_type": { - "snow": "\ub208" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.lv.json b/homeassistant/components/climacell/translations/sensor.lv.json deleted file mode 100644 index a0010b4e4a8..00000000000 --- a/homeassistant/components/climacell/translations/sensor.lv.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Labs", - "hazardous": "B\u012bstams", - "moderate": "M\u0113rens", - "unhealthy": "Nevesel\u012bgs", - "unhealthy_for_sensitive_groups": "Nevesel\u012bgs jut\u012bg\u0101m grup\u0101m", - "very_unhealthy": "\u013boti nevesel\u012bgs" - }, - "climacell__pollen_index": { - "high": "Augsts", - "low": "Zems", - "medium": "Vid\u0113js", - "none": "Nav", - "very_high": "\u013boti augsts", - "very_low": "\u013boti zems" - }, - "climacell__precipitation_type": { - "freezing_rain": "Sasalsto\u0161s lietus", - "ice_pellets": "Krusa", - "none": "Nav", - "rain": "Lietus", - "snow": "Sniegs" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.nl.json b/homeassistant/components/climacell/translations/sensor.nl.json deleted file mode 100644 index 710198156d1..00000000000 --- a/homeassistant/components/climacell/translations/sensor.nl.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Goed", - "hazardous": "Gevaarlijk", - "moderate": "Gematigd", - "unhealthy": "Ongezond", - "unhealthy_for_sensitive_groups": "Ongezond voor gevoelige groepen", - "very_unhealthy": "Zeer ongezond" - }, - "climacell__pollen_index": { - "high": "Hoog", - "low": "Laag", - "medium": "Medium", - "none": "Geen", - "very_high": "Zeer Hoog", - "very_low": "Zeer Laag" - }, - "climacell__precipitation_type": { - "freezing_rain": "IJzel", - "ice_pellets": "IJskorrels", - "none": "Geen", - "rain": "Regen", - "snow": "Sneeuw" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.no.json b/homeassistant/components/climacell/translations/sensor.no.json deleted file mode 100644 index 10f2a02db72..00000000000 --- a/homeassistant/components/climacell/translations/sensor.no.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Bra", - "hazardous": "Farlig", - "moderate": "Moderat", - "unhealthy": "Usunt", - "unhealthy_for_sensitive_groups": "Usunt for sensitive grupper", - "very_unhealthy": "Veldig usunt" - }, - "climacell__pollen_index": { - "high": "H\u00f8y", - "low": "Lav", - "medium": "Medium", - "none": "Ingen", - "very_high": "Veldig h\u00f8y", - "very_low": "Veldig lav" - }, - "climacell__precipitation_type": { - "freezing_rain": "Underkj\u00f8lt regn", - "ice_pellets": "Is tapper", - "none": "Ingen", - "rain": "Regn", - "snow": "Sn\u00f8" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.pl.json b/homeassistant/components/climacell/translations/sensor.pl.json deleted file mode 100644 index 67a0217a7ea..00000000000 --- a/homeassistant/components/climacell/translations/sensor.pl.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "dobre", - "hazardous": "niebezpieczne", - "moderate": "umiarkowane", - "unhealthy": "niezdrowe", - "unhealthy_for_sensitive_groups": "niezdrowe dla grup wra\u017cliwych", - "very_unhealthy": "bardzo niezdrowe" - }, - "climacell__pollen_index": { - "high": "wysokie", - "low": "niskie", - "medium": "\u015brednie", - "none": "brak", - "very_high": "bardzo wysokie", - "very_low": "bardzo niskie" - }, - "climacell__precipitation_type": { - "freezing_rain": "marzn\u0105cy deszcz", - "ice_pellets": "granulki lodu", - "none": "brak", - "rain": "deszcz", - "snow": "\u015bnieg" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.pt-BR.json b/homeassistant/components/climacell/translations/sensor.pt-BR.json deleted file mode 100644 index eb3814331b9..00000000000 --- a/homeassistant/components/climacell/translations/sensor.pt-BR.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Bom", - "hazardous": "Perigosos", - "moderate": "Moderado", - "unhealthy": "Pouco saud\u00e1vel", - "unhealthy_for_sensitive_groups": "Insalubre para grupos sens\u00edveis", - "very_unhealthy": "Muito prejudicial \u00e0 sa\u00fade" - }, - "climacell__pollen_index": { - "high": "Alto", - "low": "Baixo", - "medium": "M\u00e9dio", - "none": "Nenhum", - "very_high": "Muito alto", - "very_low": "Muito baixo" - }, - "climacell__precipitation_type": { - "freezing_rain": "Chuva congelante", - "ice_pellets": "Granizo", - "none": "Nenhum", - "rain": "Chuva", - "snow": "Neve" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.pt.json b/homeassistant/components/climacell/translations/sensor.pt.json deleted file mode 100644 index 30ba0f75808..00000000000 --- a/homeassistant/components/climacell/translations/sensor.pt.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "unhealthy_for_sensitive_groups": "Pouco saud\u00e1vel para grupos sens\u00edveis" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.ru.json b/homeassistant/components/climacell/translations/sensor.ru.json deleted file mode 100644 index 3a5d1a07a7e..00000000000 --- a/homeassistant/components/climacell/translations/sensor.ru.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "\u0425\u043e\u0440\u043e\u0448\u043e", - "hazardous": "\u041e\u043f\u0430\u0441\u043d\u043e", - "moderate": "\u0421\u0440\u0435\u0434\u043d\u0435", - "unhealthy": "\u0412\u0440\u0435\u0434\u043d\u043e", - "unhealthy_for_sensitive_groups": "\u0412\u0440\u0435\u0434\u043d\u043e \u0434\u043b\u044f \u0443\u044f\u0437\u0432\u0438\u043c\u044b\u0445 \u0433\u0440\u0443\u043f\u043f", - "very_unhealthy": "\u041e\u0447\u0435\u043d\u044c \u0432\u0440\u0435\u0434\u043d\u043e" - }, - "climacell__pollen_index": { - "high": "\u0412\u044b\u0441\u043e\u043a\u0438\u0439", - "low": "\u041d\u0438\u0437\u043a\u0438\u0439", - "medium": "\u0421\u0440\u0435\u0434\u043d\u0438\u0439", - "none": "\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442", - "very_high": "\u041e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0438\u0439", - "very_low": "\u041e\u0447\u0435\u043d\u044c \u043d\u0438\u0437\u043a\u0438\u0439" - }, - "climacell__precipitation_type": { - "freezing_rain": "\u041b\u0435\u0434\u044f\u043d\u043e\u0439 \u0434\u043e\u0436\u0434\u044c", - "ice_pellets": "\u041b\u0435\u0434\u044f\u043d\u0430\u044f \u043a\u0440\u0443\u043f\u0430", - "none": "\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442", - "rain": "\u0414\u043e\u0436\u0434\u044c", - "snow": "\u0421\u043d\u0435\u0433" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.sk.json b/homeassistant/components/climacell/translations/sensor.sk.json deleted file mode 100644 index dfe997964cc..00000000000 --- a/homeassistant/components/climacell/translations/sensor.sk.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "unhealthy": "Nezdrav\u00e9" - }, - "climacell__pollen_index": { - "low": "N\u00edzke", - "medium": "Stredn\u00e9", - "very_high": "Ve\u013emi vysok\u00e9", - "very_low": "Ve\u013emi n\u00edzke" - }, - "climacell__precipitation_type": { - "freezing_rain": "Mrzn\u00faci d\u00e1\u017e\u010f", - "rain": "D\u00e1\u017e\u010f", - "snow": "Sneh" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.sv.json b/homeassistant/components/climacell/translations/sensor.sv.json deleted file mode 100644 index d6172566c7a..00000000000 --- a/homeassistant/components/climacell/translations/sensor.sv.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "Bra", - "hazardous": "Farligt", - "moderate": "M\u00e5ttligt", - "unhealthy": "Oh\u00e4lsosamt", - "unhealthy_for_sensitive_groups": "Oh\u00e4lsosamt f\u00f6r k\u00e4nsliga grupper", - "very_unhealthy": "Mycket oh\u00e4lsosamt" - }, - "climacell__pollen_index": { - "high": "H\u00f6gt", - "low": "L\u00e5gt", - "medium": "Medium", - "none": "Inget", - "very_high": "V\u00e4ldigt h\u00f6gt", - "very_low": "V\u00e4ldigt l\u00e5gt" - }, - "climacell__precipitation_type": { - "freezing_rain": "Underkylt regn", - "ice_pellets": "Hagel", - "none": "Ingen", - "rain": "Regn", - "snow": "Sn\u00f6" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.tr.json b/homeassistant/components/climacell/translations/sensor.tr.json deleted file mode 100644 index 6c58f82bb94..00000000000 --- a/homeassistant/components/climacell/translations/sensor.tr.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "\u0130yi", - "hazardous": "Tehlikeli", - "moderate": "Il\u0131ml\u0131", - "unhealthy": "Sa\u011fl\u0131ks\u0131z", - "unhealthy_for_sensitive_groups": "Hassas Gruplar \u0130\u00e7in Sa\u011fl\u0131ks\u0131z", - "very_unhealthy": "\u00c7ok Sa\u011fl\u0131ks\u0131z" - }, - "climacell__pollen_index": { - "high": "Y\u00fcksek", - "low": "D\u00fc\u015f\u00fck", - "medium": "Orta", - "none": "Hi\u00e7biri", - "very_high": "\u00c7ok Y\u00fcksek", - "very_low": "\u00c7ok D\u00fc\u015f\u00fck" - }, - "climacell__precipitation_type": { - "freezing_rain": "Dondurucu Ya\u011fmur", - "ice_pellets": "Buz Peletleri", - "none": "Hi\u00e7biri", - "rain": "Ya\u011fmur", - "snow": "Kar" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.zh-Hant.json b/homeassistant/components/climacell/translations/sensor.zh-Hant.json deleted file mode 100644 index c9898fcfe4d..00000000000 --- a/homeassistant/components/climacell/translations/sensor.zh-Hant.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "climacell__health_concern": { - "good": "\u826f\u597d", - "hazardous": "\u5371\u96aa", - "moderate": "\u4e2d\u7b49", - "unhealthy": "\u4e0d\u5065\u5eb7", - "unhealthy_for_sensitive_groups": "\u5c0d\u654f\u611f\u65cf\u7fa4\u4e0d\u5065\u5eb7", - "very_unhealthy": "\u975e\u5e38\u4e0d\u5065\u5eb7" - }, - "climacell__pollen_index": { - "high": "\u9ad8", - "low": "\u4f4e", - "medium": "\u4e2d", - "none": "\u7121", - "very_high": "\u6975\u9ad8", - "very_low": "\u6975\u4f4e" - }, - "climacell__precipitation_type": { - "freezing_rain": "\u51cd\u96e8", - "ice_pellets": "\u51b0\u73e0", - "none": "\u7121", - "rain": "\u4e0b\u96e8", - "snow": "\u4e0b\u96ea" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sv.json b/homeassistant/components/climacell/translations/sv.json deleted file mode 100644 index 2382ec64324..00000000000 --- a/homeassistant/components/climacell/translations/sv.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Min. Mellan NowCast-prognoser" - }, - "description": "Om du v\u00e4ljer att aktivera \"nowcast\"-prognosentiteten kan du konfigurera antalet minuter mellan varje prognos. Antalet prognoser som tillhandah\u00e5lls beror p\u00e5 antalet minuter som v\u00e4ljs mellan prognoserna.", - "title": "Uppdatera ClimaCell-alternativ" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/tr.json b/homeassistant/components/climacell/translations/tr.json deleted file mode 100644 index 54e24f813e4..00000000000 --- a/homeassistant/components/climacell/translations/tr.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "Min. NowCast Tahminleri Aras\u0131nda" - }, - "description": "'Nowcast' tahmin varl\u0131\u011f\u0131n\u0131 etkinle\u015ftirmeyi se\u00e7erseniz, her tahmin aras\u0131ndaki dakika say\u0131s\u0131n\u0131 yap\u0131land\u0131rabilirsiniz. Sa\u011flanan tahmin say\u0131s\u0131, tahminler aras\u0131nda se\u00e7ilen dakika say\u0131s\u0131na ba\u011fl\u0131d\u0131r.", - "title": "ClimaCell Se\u00e7eneklerini G\u00fcncelleyin" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/zh-Hant.json b/homeassistant/components/climacell/translations/zh-Hant.json deleted file mode 100644 index 309b39ab242..00000000000 --- a/homeassistant/components/climacell/translations/zh-Hant.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "options": { - "step": { - "init": { - "data": { - "timestep": "NowCast \u9810\u5831\u9593\u9694\u5206\u9418" - }, - "description": "\u5047\u5982\u9078\u64c7\u958b\u555f `nowcast` \u9810\u5831\u5be6\u9ad4\u3001\u5c07\u53ef\u4ee5\u8a2d\u5b9a\u9810\u5831\u983b\u7387\u9593\u9694\u5206\u9418\u6578\u3002\u6839\u64da\u6240\u8f38\u5165\u7684\u9593\u9694\u6642\u9593\u5c07\u6c7a\u5b9a\u9810\u5831\u7684\u6578\u76ee\u3002", - "title": "\u66f4\u65b0 ClimaCell \u9078\u9805" - } - } - } -} \ No newline at end of file From 97b40b5f491033d5c71d187d549224d5c60310fb Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 23 Nov 2022 20:05:31 +0200 Subject: [PATCH 0651/1033] Make hassfest strictly typed (#82091) --- script/hassfest/__main__.py | 13 ++++-- script/hassfest/application_credentials.py | 2 +- script/hassfest/bluetooth.py | 6 +-- script/hassfest/brand.py | 2 +- script/hassfest/codeowners.py | 6 +-- script/hassfest/config_flow.py | 29 ++++++++------ script/hassfest/coverage.py | 2 +- script/hassfest/dependencies.py | 31 ++++++++------- script/hassfest/dhcp.py | 6 +-- script/hassfest/json.py | 8 ++-- script/hassfest/manifest.py | 12 +++--- script/hassfest/model.py | 2 +- script/hassfest/mqtt.py | 7 ++-- script/hassfest/requirements.py | 19 ++++++--- script/hassfest/serializer.py | 12 ++++-- script/hassfest/services.py | 9 +++-- script/hassfest/ssdp.py | 7 ++-- script/hassfest/translations.py | 46 ++++++++++++++-------- script/hassfest/usb.py | 2 +- script/hassfest/zeroconf.py | 8 ++-- 20 files changed, 132 insertions(+), 97 deletions(-) diff --git a/script/hassfest/__main__.py b/script/hassfest/__main__.py index 27252969118..87024619765 100644 --- a/script/hassfest/__main__.py +++ b/script/hassfest/__main__.py @@ -1,4 +1,6 @@ """Validate manifests.""" +from __future__ import annotations + import argparse import pathlib import sys @@ -55,7 +57,7 @@ ALL_PLUGIN_NAMES = [ ] -def valid_integration_path(integration_path): +def valid_integration_path(integration_path: pathlib.Path | str) -> pathlib.Path: """Test if it's a valid integration.""" path = pathlib.Path(integration_path) if not path.is_dir(): @@ -124,7 +126,7 @@ def get_config() -> Config: ) -def main(): +def main() -> int: """Validate manifests.""" try: config = get_config() @@ -218,7 +220,12 @@ def main(): return 1 -def print_integrations_status(config, integrations, *, show_fixable_errors=True): +def print_integrations_status( + config: Config, + integrations: list[Integration], + *, + show_fixable_errors: bool = True, +) -> None: """Print integration status.""" for integration in sorted(integrations, key=lambda itg: itg.domain): extra = f" - {integration.path}" if config.specific_integrations else "" diff --git a/script/hassfest/application_credentials.py b/script/hassfest/application_credentials.py index aed8b892f50..1be644054c4 100644 --- a/script/hassfest/application_credentials.py +++ b/script/hassfest/application_credentials.py @@ -41,7 +41,7 @@ def validate(integrations: dict[str, Integration], config: Config) -> None: ) -def generate(integrations: dict[str, Integration], config: Config): +def generate(integrations: dict[str, Integration], config: Config) -> None: """Generate application_credentials data.""" application_credentials_path = ( config.root / "homeassistant/generated/application_credentials.py" diff --git a/script/hassfest/bluetooth.py b/script/hassfest/bluetooth.py index 57772edd7f4..a4b2ffc8cb1 100644 --- a/script/hassfest/bluetooth.py +++ b/script/hassfest/bluetooth.py @@ -5,7 +5,7 @@ from .model import Config, Integration from .serializer import format_python_namespace -def generate_and_validate(integrations: list[dict[str, str]]): +def generate_and_validate(integrations: dict[str, Integration]) -> str: """Validate and generate bluetooth data.""" match_list = [] @@ -29,7 +29,7 @@ def generate_and_validate(integrations: list[dict[str, str]]): ) -def validate(integrations: dict[str, Integration], config: Config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Validate bluetooth file.""" bluetooth_path = config.root / "homeassistant/generated/bluetooth.py" config.cache["bluetooth"] = content = generate_and_validate(integrations) @@ -48,7 +48,7 @@ def validate(integrations: dict[str, Integration], config: Config): return -def generate(integrations: dict[str, Integration], config: Config): +def generate(integrations: dict[str, Integration], config: Config) -> None: """Generate bluetooth file.""" bluetooth_path = config.root / "homeassistant/generated/bluetooth.py" with open(str(bluetooth_path), "w") as fp: diff --git a/script/hassfest/brand.py b/script/hassfest/brand.py index c35f50599ff..1083cc911bd 100644 --- a/script/hassfest/brand.py +++ b/script/hassfest/brand.py @@ -55,7 +55,7 @@ def _validate_brand( ): config.add_error( "brand", - f"{brand.path.name}: Brand '{brand.brand['domain']}' " + f"{brand.path.name}: Brand '{brand.domain}' " f"is an integration but is missing in the brand's 'integrations' list'", ) diff --git a/script/hassfest/codeowners.py b/script/hassfest/codeowners.py index 0cc58012162..c300976c193 100644 --- a/script/hassfest/codeowners.py +++ b/script/hassfest/codeowners.py @@ -42,7 +42,7 @@ REMOVE_CODEOWNERS = """ """ -def generate_and_validate(integrations: dict[str, Integration], config: Config): +def generate_and_validate(integrations: dict[str, Integration], config: Config) -> str: """Generate CODEOWNERS.""" parts = [BASE] @@ -77,7 +77,7 @@ def generate_and_validate(integrations: dict[str, Integration], config: Config): return "\n".join(parts) -def validate(integrations: dict[str, Integration], config: Config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Validate CODEOWNERS.""" codeowners_path = config.root / "CODEOWNERS" config.cache["codeowners"] = content = generate_and_validate(integrations, config) @@ -95,7 +95,7 @@ def validate(integrations: dict[str, Integration], config: Config): return -def generate(integrations: dict[str, Integration], config: Config): +def generate(integrations: dict[str, Integration], config: Config) -> None: """Generate CODEOWNERS.""" codeowners_path = config.root / "CODEOWNERS" with open(str(codeowners_path), "w") as fp: diff --git a/script/hassfest/config_flow.py b/script/hassfest/config_flow.py index e668846505f..2c3988cc488 100644 --- a/script/hassfest/config_flow.py +++ b/script/hassfest/config_flow.py @@ -3,6 +3,7 @@ from __future__ import annotations import json import pathlib +from typing import Any from .brand import validate as validate_brands from .model import Brand, Config, Integration @@ -11,12 +12,12 @@ from .serializer import format_python_namespace UNIQUE_ID_IGNORE = {"huawei_lte", "mqtt", "adguard"} -def _validate_integration(config: Config, integration: Integration): +def _validate_integration(config: Config, integration: Integration) -> None: """Validate config flow of an integration.""" config_flow_file = integration.path / "config_flow.py" if not config_flow_file.is_file(): - if integration.manifest.get("config_flow"): + if (integration.manifest or {}).get("config_flow"): integration.add_error( "config_flow", "Config flows need to be defined in the file config_flow.py", @@ -60,9 +61,9 @@ def _validate_integration(config: Config, integration: Integration): ) -def _generate_and_validate(integrations: dict[str, Integration], config: Config): +def _generate_and_validate(integrations: dict[str, Integration], config: Config) -> str: """Validate and generate config flow data.""" - domains = { + domains: dict[str, list[str]] = { "integration": [], "helper": [], } @@ -84,9 +85,9 @@ def _generate_and_validate(integrations: dict[str, Integration], config: Config) def _populate_brand_integrations( - integration_data: dict, + integration_data: dict[str, Any], integrations: dict[str, Integration], - brand_metadata: dict, + brand_metadata: dict[str, Any], sub_integrations: list[str], ) -> None: """Add referenced integrations to a brand's metadata.""" @@ -99,7 +100,7 @@ def _populate_brand_integrations( "system", ): continue - metadata = { + metadata: dict[str, Any] = { "integration_type": integration.integration_type, } # Always set the config_flow key to avoid breaking the frontend @@ -119,11 +120,13 @@ def _populate_brand_integrations( def _generate_integrations( - brands: dict[str, Brand], integrations: dict[str, Integration], config: Config -): + brands: dict[str, Brand], + integrations: dict[str, Integration], + config: Config, +) -> str: """Generate integrations data.""" - result = { + result: dict[str, Any] = { "integration": {}, "helper": {}, "translated_name": set(), @@ -154,7 +157,7 @@ def _generate_integrations( # Generate the config flow index for domain in sorted(primary_domains): - metadata = {} + metadata: dict[str, Any] = {} if brand := brands.get(domain): metadata["name"] = brand.name @@ -199,7 +202,7 @@ def _generate_integrations( ) -def validate(integrations: dict[str, Integration], config: Config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Validate config flow file.""" config_flow_path = config.root / "homeassistant/generated/config_flows.py" integrations_path = config.root / "homeassistant/generated/integrations.json" @@ -233,7 +236,7 @@ def validate(integrations: dict[str, Integration], config: Config): ) -def generate(integrations: dict[str, Integration], config: Config): +def generate(integrations: dict[str, Integration], config: Config) -> None: """Generate config flow file.""" config_flow_path = config.root / "homeassistant/generated/config_flows.py" integrations_path = config.root / "homeassistant/generated/integrations.json" diff --git a/script/hassfest/coverage.py b/script/hassfest/coverage.py index 7c259adbfa3..71d2e3ce57c 100644 --- a/script/hassfest/coverage.py +++ b/script/hassfest/coverage.py @@ -30,7 +30,7 @@ ALLOWED_IGNORE_VIOLATIONS = { } -def validate(integrations: dict[str, Integration], config: Config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Validate coverage.""" coverage_path = config.root / ".coveragerc" diff --git a/script/hassfest/dependencies.py b/script/hassfest/dependencies.py index b5d82f7b348..fae628270af 100644 --- a/script/hassfest/dependencies.py +++ b/script/hassfest/dependencies.py @@ -7,19 +7,19 @@ from pathlib import Path from homeassistant.const import Platform from homeassistant.requirements import DISCOVERY_INTEGRATIONS -from .model import Integration +from .model import Config, Integration class ImportCollector(ast.NodeVisitor): """Collect all integrations referenced.""" - def __init__(self, integration: Integration): + def __init__(self, integration: Integration) -> None: """Initialize the import collector.""" self.integration = integration self.referenced: dict[Path, set[str]] = {} # Current file or dir we're inspecting - self._cur_fil_dir = None + self._cur_fil_dir: Path | None = None def collect(self) -> None: """Collect imports from a source file.""" @@ -32,11 +32,12 @@ class ImportCollector(ast.NodeVisitor): self.visit(ast.parse(fil.read_text())) self._cur_fil_dir = None - def _add_reference(self, reference_domain: str): + def _add_reference(self, reference_domain: str) -> None: """Add a reference.""" + assert self._cur_fil_dir self.referenced[self._cur_fil_dir].add(reference_domain) - def visit_ImportFrom(self, node): + def visit_ImportFrom(self, node: ast.ImportFrom) -> None: """Visit ImportFrom node.""" if node.module is None: return @@ -59,14 +60,14 @@ class ImportCollector(ast.NodeVisitor): for name_node in node.names: self._add_reference(name_node.name) - def visit_Import(self, node): + def visit_Import(self, node: ast.Import) -> None: """Visit Import node.""" # import homeassistant.components.hue as hue for name_node in node.names: if name_node.name.startswith("homeassistant.components."): self._add_reference(name_node.name.split(".")[2]) - def visit_Attribute(self, node): + def visit_Attribute(self, node: ast.Attribute) -> None: """Visit Attribute node.""" # hass.components.hue.async_create() # Name(id=hass) @@ -156,15 +157,16 @@ IGNORE_VIOLATIONS = { def calc_allowed_references(integration: Integration) -> set[str]: """Return a set of allowed references.""" + manifest = integration.manifest allowed_references = ( ALLOWED_USED_COMPONENTS - | set(integration.manifest.get("dependencies", [])) - | set(integration.manifest.get("after_dependencies", [])) + | set(manifest.get("dependencies", [])) + | set(manifest.get("after_dependencies", [])) ) # Discovery requirements are ok if referenced in manifest for check_domain, to_check in DISCOVERY_INTEGRATIONS.items(): - if any(check in integration.manifest for check in to_check): + if any(check in manifest for check in to_check): allowed_references.add(check_domain) return allowed_references @@ -174,7 +176,7 @@ def find_non_referenced_integrations( integrations: dict[str, Integration], integration: Integration, references: dict[Path, set[str]], -): +) -> set[str]: """Find intergrations that are not allowed to be referenced.""" allowed_references = calc_allowed_references(integration) referenced = set() @@ -219,8 +221,9 @@ def find_non_referenced_integrations( def validate_dependencies( - integrations: dict[str, Integration], integration: Integration -): + integrations: dict[str, Integration], + integration: Integration, +) -> None: """Validate all dependencies.""" # Some integrations are allowed to have violations. if integration.domain in IGNORE_VIOLATIONS: @@ -242,7 +245,7 @@ def validate_dependencies( ) -def validate(integrations: dict[str, Integration], config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Handle dependencies for integrations.""" # check for non-existing dependencies for integration in integrations.values(): diff --git a/script/hassfest/dhcp.py b/script/hassfest/dhcp.py index 992e1f615a1..c143f5940c1 100644 --- a/script/hassfest/dhcp.py +++ b/script/hassfest/dhcp.py @@ -5,7 +5,7 @@ from .model import Config, Integration from .serializer import format_python_namespace -def generate_and_validate(integrations: list[dict[str, str]]): +def generate_and_validate(integrations: dict[str, Integration]) -> str: """Validate and generate dhcp data.""" match_list = [] @@ -29,7 +29,7 @@ def generate_and_validate(integrations: list[dict[str, str]]): ) -def validate(integrations: dict[str, Integration], config: Config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Validate dhcp file.""" dhcp_path = config.root / "homeassistant/generated/dhcp.py" config.cache["dhcp"] = content = generate_and_validate(integrations) @@ -48,7 +48,7 @@ def validate(integrations: dict[str, Integration], config: Config): return -def generate(integrations: dict[str, Integration], config: Config): +def generate(integrations: dict[str, Integration], config: Config) -> None: """Generate dhcp file.""" dhcp_path = config.root / "homeassistant/generated/dhcp.py" with open(str(dhcp_path), "w") as fp: diff --git a/script/hassfest/json.py b/script/hassfest/json.py index 49ebb05bbea..f0eae9f4152 100644 --- a/script/hassfest/json.py +++ b/script/hassfest/json.py @@ -3,10 +3,10 @@ from __future__ import annotations import json -from .model import Integration +from .model import Config, Integration -def validate_json_files(integration: Integration): +def validate_json_files(integration: Integration) -> None: """Validate JSON files for integration.""" for json_file in integration.path.glob("**/*.json"): if not json_file.is_file(): @@ -18,10 +18,8 @@ def validate_json_files(integration: Integration): relative_path = json_file.relative_to(integration.path) integration.add_error("json", f"Invalid JSON file {relative_path}") - return - -def validate(integrations: dict[str, Integration], config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Handle JSON files inside integrations.""" if not config.specific_integrations: return diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py index b4130de1dee..ecf04648330 100644 --- a/script/hassfest/manifest.py +++ b/script/hassfest/manifest.py @@ -119,7 +119,7 @@ def documentation_url(value: str) -> str: return value -def verify_lowercase(value: str): +def verify_lowercase(value: str) -> str: """Verify a value is lowercase.""" if value.lower() != value: raise vol.Invalid("Value needs to be lowercase") @@ -127,7 +127,7 @@ def verify_lowercase(value: str): return value -def verify_uppercase(value: str): +def verify_uppercase(value: str) -> str: """Verify a value is uppercase.""" if value.upper() != value: raise vol.Invalid("Value needs to be uppercase") @@ -135,7 +135,7 @@ def verify_uppercase(value: str): return value -def verify_version(value: str): +def verify_version(value: str) -> str: """Verify the version.""" try: AwesomeVersion( @@ -153,7 +153,7 @@ def verify_version(value: str): return value -def verify_wildcard(value: str): +def verify_wildcard(value: str) -> str: """Verify the matcher contains a wildcard.""" if "*" not in value: raise vol.Invalid(f"'{value}' needs to contain a wildcard matcher") @@ -286,13 +286,13 @@ CUSTOM_INTEGRATION_MANIFEST_SCHEMA = INTEGRATION_MANIFEST_SCHEMA.extend( ) -def validate_version(integration: Integration): +def validate_version(integration: Integration) -> None: """ Validate the version of the integration. Will be removed when the version key is no longer optional for custom integrations. """ - if not integration.manifest.get("version"): + if not (integration.manifest and integration.manifest.get("version")): integration.add_error("manifest", "No 'version' key in the manifest file.") return diff --git a/script/hassfest/model.py b/script/hassfest/model.py index fa1580b0317..ee0a3ab32d9 100644 --- a/script/hassfest/model.py +++ b/script/hassfest/model.py @@ -25,7 +25,7 @@ class Error: class Config: """Config for the run.""" - specific_integrations: pathlib.Path | None = attr.ib() + specific_integrations: list[pathlib.Path] | None = attr.ib() root: pathlib.Path = attr.ib() action: str = attr.ib() requirements: bool = attr.ib() diff --git a/script/hassfest/mqtt.py b/script/hassfest/mqtt.py index 46ba4dfcf44..d83cb95391a 100644 --- a/script/hassfest/mqtt.py +++ b/script/hassfest/mqtt.py @@ -7,7 +7,7 @@ from .model import Config, Integration from .serializer import format_python_namespace -def generate_and_validate(integrations: dict[str, Integration]): +def generate_and_validate(integrations: dict[str, Integration]) -> str: """Validate and generate MQTT data.""" data = defaultdict(list) @@ -29,7 +29,7 @@ def generate_and_validate(integrations: dict[str, Integration]): return format_python_namespace({"MQTT": data}) -def validate(integrations: dict[str, Integration], config: Config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Validate MQTT file.""" mqtt_path = config.root / "homeassistant/generated/mqtt.py" config.cache["mqtt"] = content = generate_and_validate(integrations) @@ -44,10 +44,9 @@ def validate(integrations: dict[str, Integration], config: Config): "File mqtt.py is not up to date. Run python3 -m script.hassfest", fixable=True, ) - return -def generate(integrations: dict[str, Integration], config: Config): +def generate(integrations: dict[str, Integration], config: Config) -> None: """Generate MQTT file.""" mqtt_path = config.root / "homeassistant/generated/mqtt.py" with open(str(mqtt_path), "w") as fp: diff --git a/script/hassfest/requirements.py b/script/hassfest/requirements.py index 0e72e2eabd6..74fed8b7c7c 100644 --- a/script/hassfest/requirements.py +++ b/script/hassfest/requirements.py @@ -8,6 +8,7 @@ import os import re import subprocess import sys +from typing import Any from awesomeversion import AwesomeVersion, AwesomeVersionStrategy from stdlib_list import stdlib_list @@ -53,7 +54,7 @@ IGNORE_VIOLATIONS = { } -def validate(integrations: dict[str, Integration], config: Config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Handle requirements for integrations.""" # Check if we are doing format-only validation. if not config.requirements: @@ -63,7 +64,7 @@ def validate(integrations: dict[str, Integration], config: Config): # check for incompatible requirements - disable_tqdm = config.specific_integrations or os.environ.get("CI", False) + disable_tqdm = bool(config.specific_integrations or os.environ.get("CI")) for integration in tqdm(integrations.values(), disable=disable_tqdm): if not integration.manifest: @@ -87,7 +88,13 @@ def validate_requirements_format(integration: Integration) -> bool: ) continue - pkg, sep, version = PACKAGE_REGEX.match(req).groups() + if not (match := PACKAGE_REGEX.match(req)): + integration.add_error( + "requirements", + f'Requirement "{req}" does not match package regex pattern', + ) + continue + pkg, sep, version = match.groups() if integration.core and sep != "==": integration.add_error( @@ -115,7 +122,7 @@ def validate_requirements_format(integration: Integration) -> bool: return len(integration.errors) == start_errors -def validate_requirements(integration: Integration): +def validate_requirements(integration: Integration) -> None: """Validate requirements.""" if not validate_requirements_format(integration): return @@ -167,7 +174,7 @@ def validate_requirements(integration: Integration): @cache -def get_pipdeptree(): +def get_pipdeptree() -> dict[str, dict[str, Any]]: """Get pipdeptree output. Cached on first invocation. { @@ -254,7 +261,7 @@ def install_requirements(integration: Integration, requirements: set[str]) -> bo if normalized and "==" in requirement_arg: ver = requirement_arg.split("==")[-1] item = deptree.get(normalized) - is_installed = item and item["installed_version"] == ver + is_installed = bool(item and item["installed_version"] == ver) if not is_installed: try: diff --git a/script/hassfest/serializer.py b/script/hassfest/serializer.py index 5ba386148f3..101aea6837e 100644 --- a/script/hassfest/serializer.py +++ b/script/hassfest/serializer.py @@ -5,6 +5,7 @@ from collections.abc import Collection, Iterable, Mapping from typing import Any import black +from black.mode import Mode DEFAULT_GENERATOR = "script.hassfest" @@ -13,7 +14,7 @@ def _wrap_items( items: Iterable[str], opener: str, closer: str, - sort=False, + sort: bool = False, ) -> str: """Wrap pre-formatted Python reprs in braces, optionally sorting them.""" # The trailing comma is imperative so Black doesn't format some items @@ -23,7 +24,7 @@ def _wrap_items( return f"{opener}{','.join(items)},{closer}" -def _mapping_to_str(data: Mapping) -> str: +def _mapping_to_str(data: Mapping[Any, Any]) -> str: """Return a string representation of a mapping.""" return _wrap_items( (f"{to_string(key)}:{to_string(value)}" for key, value in data.items()), @@ -34,7 +35,10 @@ def _mapping_to_str(data: Mapping) -> str: def _collection_to_str( - data: Collection, opener: str = "[", closer: str = "]", sort=False + data: Collection[Any], + opener: str = "[", + closer: str = "]", + sort: bool = False, ) -> str: """Return a string representation of a collection.""" items = (to_string(value) for value in data) @@ -66,7 +70,7 @@ To update, run python3 -m {generator} {content} """ - return black.format_str(content.strip(), mode=black.Mode()) + return black.format_str(content.strip(), mode=Mode()) def format_python_namespace( diff --git a/script/hassfest/services.py b/script/hassfest/services.py index a5d10f8dda5..f7070d2dc5f 100644 --- a/script/hassfest/services.py +++ b/script/hassfest/services.py @@ -3,6 +3,7 @@ from __future__ import annotations import pathlib import re +from typing import Any import voluptuous as vol from voluptuous.humanize import humanize_error @@ -12,10 +13,10 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv, selector from homeassistant.util.yaml import load_yaml -from .model import Integration +from .model import Config, Integration -def exists(value): +def exists(value: Any) -> Any: """Check if value exists.""" if value is None: raise vol.Invalid("Value cannot be None") @@ -63,7 +64,7 @@ def grep_dir(path: pathlib.Path, glob_pattern: str, search_pattern: str) -> bool return False -def validate_services(integration: Integration): +def validate_services(integration: Integration) -> None: """Validate services.""" try: data = load_yaml(str(integration.path / "services.yaml")) @@ -92,7 +93,7 @@ def validate_services(integration: Integration): ) -def validate(integrations: dict[str, Integration], config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Handle dependencies for integrations.""" # check services.yaml is cool for integration in integrations.values(): diff --git a/script/hassfest/ssdp.py b/script/hassfest/ssdp.py index cbe0c3ee76f..aebead6e97f 100644 --- a/script/hassfest/ssdp.py +++ b/script/hassfest/ssdp.py @@ -7,7 +7,7 @@ from .model import Config, Integration from .serializer import format_python_namespace -def generate_and_validate(integrations: dict[str, Integration]): +def generate_and_validate(integrations: dict[str, Integration]) -> str: """Validate and generate ssdp data.""" data = defaultdict(list) @@ -29,7 +29,7 @@ def generate_and_validate(integrations: dict[str, Integration]): return format_python_namespace({"SSDP": data}) -def validate(integrations: dict[str, Integration], config: Config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Validate ssdp file.""" ssdp_path = config.root / "homeassistant/generated/ssdp.py" config.cache["ssdp"] = content = generate_and_validate(integrations) @@ -44,10 +44,9 @@ def validate(integrations: dict[str, Integration], config: Config): "File ssdp.py is not up to date. Run python3 -m script.hassfest", fixable=True, ) - return -def generate(integrations: dict[str, Integration], config: Config): +def generate(integrations: dict[str, Integration], config: Config) -> None: """Generate ssdp file.""" ssdp_path = config.root / "homeassistant/generated/ssdp.py" with open(str(ssdp_path), "w") as fp: diff --git a/script/hassfest/translations.py b/script/hassfest/translations.py index ab2961e0506..8f2816e503c 100644 --- a/script/hassfest/translations.py +++ b/script/hassfest/translations.py @@ -5,6 +5,7 @@ from functools import partial from itertools import chain import json import re +from typing import Any import voluptuous as vol from voluptuous.humanize import humanize_error @@ -51,7 +52,7 @@ MOVED_TRANSLATIONS_DIRECTORY_MSG = ( ) -def allow_name_translation(integration: Integration): +def allow_name_translation(integration: Integration) -> bool: """Validate that the translation name is not the same as the integration name.""" # Only enforce for core because custom integrations can't be # added to allow list. @@ -74,7 +75,11 @@ def check_translations_directory_name(integration: Integration) -> None: integration.add_error("translations", MOVED_TRANSLATIONS_DIRECTORY_MSG) -def find_references(strings, prefix, found): +def find_references( + strings: dict[str, Any], + prefix: str, + found: list[dict[str, str]], +) -> None: """Find references.""" for key, value in strings.items(): if isinstance(value, dict): @@ -87,7 +92,11 @@ def find_references(strings, prefix, found): found.append({"source": f"{prefix}::{key}", "ref": match.groups()[0]}) -def removed_title_validator(config, integration, value): +def removed_title_validator( + config: Config, + integration: Integration, + value: Any, +) -> Any: """Mark removed title.""" if not config.specific_integrations: raise vol.Invalid(REMOVED_TITLE_MSG) @@ -97,7 +106,7 @@ def removed_title_validator(config, integration, value): return value -def lowercase_validator(value): +def lowercase_validator(value: str) -> str: """Validate value is lowercase.""" if value.lower() != value: raise vol.Invalid("Needs to be lowercase") @@ -112,7 +121,7 @@ def gen_data_entry_schema( flow_title: int, require_step_title: bool, mandatory_description: str | None = None, -): +) -> vol.All: """Generate a data entry schema.""" step_title_class = vol.Required if require_step_title else vol.Optional schema = { @@ -138,7 +147,7 @@ def gen_data_entry_schema( removed_title_validator, config, integration ) - def data_description_validator(value): + def data_description_validator(value: dict[str, Any]) -> dict[str, Any]: """Validate data description.""" for step_info in value["step"].values(): if "data_description" not in step_info: @@ -154,7 +163,7 @@ def gen_data_entry_schema( if mandatory_description is not None: - def validate_description_set(value): + def validate_description_set(value: dict[str, Any]) -> dict[str, Any]: """Validate description is set.""" steps = value["step"] if mandatory_description not in steps: @@ -169,7 +178,7 @@ def gen_data_entry_schema( if not allow_name_translation(integration): - def name_validator(value): + def name_validator(value: dict[str, Any]) -> dict[str, Any]: """Validate name.""" for step_id, info in value["step"].items(): if info.get("title") == integration.name: @@ -250,7 +259,7 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema: ) -def gen_auth_schema(config: Config, integration: Integration): +def gen_auth_schema(config: Config, integration: Integration) -> vol.Schema: """Generate auth schema.""" return vol.Schema( { @@ -266,7 +275,7 @@ def gen_auth_schema(config: Config, integration: Integration): ) -def gen_platform_strings_schema(config: Config, integration: Integration): +def gen_platform_strings_schema(config: Config, integration: Integration) -> vol.Schema: """Generate platform strings schema like strings.sensor.json. Example of valid data: @@ -279,7 +288,7 @@ def gen_platform_strings_schema(config: Config, integration: Integration): } """ - def device_class_validator(value): + def device_class_validator(value: str) -> str: """Key validator for platform states. Platform states are only allowed to provide states for device classes they prefix. @@ -313,8 +322,10 @@ ONBOARDING_SCHEMA = vol.Schema({vol.Required("area"): {str: cv.string_with_no_ht def validate_translation_file( # noqa: C901 - config: Config, integration: Integration, all_strings -): + config: Config, + integration: Integration, + all_strings: dict[str, Any] | None, +) -> None: """Validate translation files for integration.""" if config.specific_integrations: check_translations_directory_name(integration) @@ -326,7 +337,7 @@ def validate_translation_file( # noqa: C901 # Only English needs to be always complete strings_files.append(integration.path / "translations/en.json") - references = [] + references: list[dict[str, str]] = [] if integration.domain == "auth": strings_schema = gen_auth_schema(config, integration) @@ -405,6 +416,9 @@ def validate_translation_file( # noqa: C901 if config.specific_integrations: return + if not all_strings: # Nothing to validate against + return + # Validate references for reference in references: parts = reference["ref"].split("::") @@ -421,12 +435,12 @@ def validate_translation_file( # noqa: C901 ) -def validate(integrations: dict[str, Integration], config: Config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Handle JSON files inside integrations.""" if config.specific_integrations: all_strings = None else: - all_strings = upload.generate_upload_data() + all_strings = upload.generate_upload_data() # type: ignore[no-untyped-call] for integration in integrations.values(): validate_translation_file(config, integration, all_strings) diff --git a/script/hassfest/usb.py b/script/hassfest/usb.py index 3e5ce6e3963..5420957e201 100644 --- a/script/hassfest/usb.py +++ b/script/hassfest/usb.py @@ -5,7 +5,7 @@ from .model import Config, Integration from .serializer import format_python_namespace -def generate_and_validate(integrations: list[dict[str, str]]) -> str: +def generate_and_validate(integrations: dict[str, Integration]) -> str: """Validate and generate usb data.""" match_list = [] diff --git a/script/hassfest/zeroconf.py b/script/hassfest/zeroconf.py index 0c372035bdc..7f8687bded5 100644 --- a/script/hassfest/zeroconf.py +++ b/script/hassfest/zeroconf.py @@ -9,10 +9,10 @@ from .model import Config, Integration from .serializer import format_python_namespace -def generate_and_validate(integrations: dict[str, Integration]): +def generate_and_validate(integrations: dict[str, Integration]) -> str: """Validate and generate zeroconf data.""" service_type_dict = defaultdict(list) - homekit_dict = {} + homekit_dict: dict[str, str] = {} for domain in sorted(integrations): integration = integrations[domain] @@ -77,7 +77,7 @@ def generate_and_validate(integrations: dict[str, Integration]): ) -def validate(integrations: dict[str, Integration], config: Config): +def validate(integrations: dict[str, Integration], config: Config) -> None: """Validate zeroconf file.""" zeroconf_path = config.root / "homeassistant/generated/zeroconf.py" config.cache["zeroconf"] = content = generate_and_validate(integrations) @@ -96,7 +96,7 @@ def validate(integrations: dict[str, Integration], config: Config): return -def generate(integrations: dict[str, Integration], config: Config): +def generate(integrations: dict[str, Integration], config: Config) -> None: """Generate zeroconf file.""" zeroconf_path = config.root / "homeassistant/generated/zeroconf.py" with open(str(zeroconf_path), "w") as fp: From 8704f1aa47db9e7849e2c0bba1ff937847969639 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Wed, 23 Nov 2022 19:54:12 +0100 Subject: [PATCH 0652/1033] Fully type min_max (#79496) --- .strict-typing | 1 + homeassistant/components/min_max/sensor.py | 92 +++++++++++++------- mypy.ini | 10 +++ tests/components/min_max/test_config_flow.py | 4 +- tests/components/min_max/test_sensor.py | 26 +++--- 5 files changed, 86 insertions(+), 47 deletions(-) diff --git a/.strict-typing b/.strict-typing index 44e303152ce..559ee64ddd7 100644 --- a/.strict-typing +++ b/.strict-typing @@ -182,6 +182,7 @@ homeassistant.components.media_player.* homeassistant.components.media_source.* homeassistant.components.metoffice.* homeassistant.components.mikrotik.* +homeassistant.components.min_max.* homeassistant.components.mjpeg.* homeassistant.components.modbus.* homeassistant.components.modem_callerid.* diff --git a/homeassistant/components/min_max/sensor.py b/homeassistant/components/min_max/sensor.py index 87edcd38766..c052cbc211d 100644 --- a/homeassistant/components/min_max/sensor.py +++ b/homeassistant/components/min_max/sensor.py @@ -1,8 +1,10 @@ """Support for displaying minimal, maximal, mean or median values.""" from __future__ import annotations +from datetime import datetime import logging import statistics +from typing import Any import voluptuous as vol @@ -20,12 +22,17 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) -from homeassistant.core import Event, HomeAssistant, callback +from homeassistant.core import Event, HomeAssistant, State, callback from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_track_state_change_event from homeassistant.helpers.reload import async_setup_reload_service -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.helpers.typing import ( + ConfigType, + DiscoveryInfoType, + EventType, + StateType, +) from . import PLATFORMS from .const import CONF_ENTITY_IDS, CONF_ROUND_DIGITS, DOMAIN @@ -62,7 +69,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Optional(CONF_NAME): cv.string, vol.Required(CONF_ENTITY_IDS): cv.entity_ids, vol.Optional(CONF_ROUND_DIGITS, default=2): vol.Coerce(int), - vol.Optional(CONF_UNIQUE_ID): str, + vol.Optional(CONF_UNIQUE_ID): cv.string, } ) @@ -100,10 +107,10 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the min/max/mean sensor.""" - entity_ids = config.get(CONF_ENTITY_IDS) - name = config.get(CONF_NAME) - sensor_type = config.get(CONF_TYPE) - round_digits = config.get(CONF_ROUND_DIGITS) + entity_ids: list[str] = config[CONF_ENTITY_IDS] + name: str | None = config.get(CONF_NAME) + sensor_type: str = config[CONF_TYPE] + round_digits: int = config[CONF_ROUND_DIGITS] unique_id = config.get(CONF_UNIQUE_ID) await async_setup_reload_service(hass, DOMAIN, PLATFORMS) @@ -113,10 +120,10 @@ async def async_setup_platform( ) -def calc_min(sensor_values): +def calc_min(sensor_values: list[tuple[str, Any]]) -> tuple[str | None, float | None]: """Calculate min value, honoring unknown states.""" - val = None - entity_id = None + val: float | None = None + entity_id: str | None = None for sensor_id, sensor_value in sensor_values: if sensor_value not in [STATE_UNKNOWN, STATE_UNAVAILABLE] and ( val is None or val > sensor_value @@ -125,10 +132,10 @@ def calc_min(sensor_values): return entity_id, val -def calc_max(sensor_values): +def calc_max(sensor_values: list[tuple[str, Any]]) -> tuple[str | None, float | None]: """Calculate max value, honoring unknown states.""" - val = None - entity_id = None + val: float | None = None + entity_id: str | None = None for sensor_id, sensor_value in sensor_values: if sensor_value not in [STATE_UNKNOWN, STATE_UNAVAILABLE] and ( val is None or val < sensor_value @@ -137,7 +144,7 @@ def calc_max(sensor_values): return entity_id, val -def calc_mean(sensor_values, round_digits): +def calc_mean(sensor_values: list[tuple[str, Any]], round_digits: int) -> float | None: """Calculate mean value, honoring unknown states.""" result = [ sensor_value @@ -147,10 +154,13 @@ def calc_mean(sensor_values, round_digits): if not result: return None - return round(statistics.mean(result), round_digits) + value: float = round(statistics.mean(result), round_digits) + return value -def calc_median(sensor_values, round_digits): +def calc_median( + sensor_values: list[tuple[str, Any]], round_digits: int +) -> float | None: """Calculate median value, honoring unknown states.""" result = [ sensor_value @@ -160,10 +170,11 @@ def calc_median(sensor_values, round_digits): if not result: return None - return round(statistics.median(result), round_digits) + value: float = round(statistics.median(result), round_digits) + return value -def calc_range(sensor_values, round_digits): +def calc_range(sensor_values: list[tuple[str, Any]], round_digits: int) -> float | None: """Calculate range value, honoring unknown states.""" result = [ sensor_value @@ -173,7 +184,8 @@ def calc_range(sensor_values, round_digits): if not result: return None - return round(max(result) - min(result), round_digits) + value: float = round(max(result) - min(result), round_digits) + return value class MinMaxSensor(SensorEntity): @@ -183,7 +195,14 @@ class MinMaxSensor(SensorEntity): _attr_should_poll = False _attr_state_class = SensorStateClass.MEASUREMENT - def __init__(self, entity_ids, name, sensor_type, round_digits, unique_id): + def __init__( + self, + entity_ids: list[str], + name: str | None, + sensor_type: str, + round_digits: int, + unique_id: str | None, + ) -> None: """Initialize the min/max sensor.""" self._attr_unique_id = unique_id self._entity_ids = entity_ids @@ -197,11 +216,17 @@ class MinMaxSensor(SensorEntity): self._sensor_attr = SENSOR_TYPE_TO_ATTR[self._sensor_type] self._unit_of_measurement = None self._unit_of_measurement_mismatch = False - self.min_value = self.max_value = self.mean = self.last = self.median = None - self.range = None - self.min_entity_id = self.max_entity_id = self.last_entity_id = None + self.min_value: float | None = None + self.max_value: float | None = None + self.mean: float | None = None + self.last: float | None = None + self.median: float | None = None + self.range: float | None = None + self.min_entity_id: str | None = None + self.max_entity_id: str | None = None + self.last_entity_id: str | None = None self.count_sensors = len(self._entity_ids) - self.states = {} + self.states: dict[str, Any] = {} async def async_added_to_hass(self) -> None: """Handle added to Hass.""" @@ -220,21 +245,22 @@ class MinMaxSensor(SensorEntity): self._calc_values() @property - def native_value(self): + def native_value(self) -> StateType | datetime: """Return the state of the sensor.""" if self._unit_of_measurement_mismatch: return None - return getattr(self, self._sensor_attr) + value: StateType | datetime = getattr(self, self._sensor_attr) + return value @property - def native_unit_of_measurement(self): + def native_unit_of_measurement(self) -> str | None: """Return the unit the value is expressed in.""" if self._unit_of_measurement_mismatch: return "ERR" return self._unit_of_measurement @property - def extra_state_attributes(self): + def extra_state_attributes(self) -> dict[str, Any] | None: """Return the state attributes of the sensor.""" if self._sensor_type == "min": return {ATTR_MIN_ENTITY_ID: self.min_entity_id} @@ -245,10 +271,12 @@ class MinMaxSensor(SensorEntity): return None @callback - def _async_min_max_sensor_state_listener(self, event, update_state=True): + def _async_min_max_sensor_state_listener( + self, event: EventType, update_state: bool = True + ) -> None: """Handle the sensor state changes.""" - new_state = event.data.get("new_state") - entity = event.data.get("entity_id") + new_state: State | None = event.data.get("new_state") + entity: str = event.data["entity_id"] if ( new_state is None @@ -296,7 +324,7 @@ class MinMaxSensor(SensorEntity): self.async_write_ha_state() @callback - def _calc_values(self): + def _calc_values(self) -> None: """Calculate the values.""" sensor_values = [ (entity_id, self.states[entity_id]) diff --git a/mypy.ini b/mypy.ini index c0628a2fc71..a347218cb70 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1573,6 +1573,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.min_max.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.mjpeg.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/tests/components/min_max/test_config_flow.py b/tests/components/min_max/test_config_flow.py index 0eb334763d6..2503eefb1b0 100644 --- a/tests/components/min_max/test_config_flow.py +++ b/tests/components/min_max/test_config_flow.py @@ -12,7 +12,7 @@ from tests.common import MockConfigEntry @pytest.mark.parametrize("platform", ("sensor",)) -async def test_config_flow(hass: HomeAssistant, platform) -> None: +async def test_config_flow(hass: HomeAssistant, platform: str) -> None: """Test the config flow.""" input_sensors = ["sensor.input_one", "sensor.input_two"] @@ -66,7 +66,7 @@ def get_suggested(schema, key): @pytest.mark.parametrize("platform", ("sensor",)) -async def test_options(hass: HomeAssistant, platform) -> None: +async def test_options(hass: HomeAssistant, platform: str) -> None: """Test reconfiguring.""" hass.states.async_set("sensor.input_one", "10") hass.states.async_set("sensor.input_two", "20") diff --git a/tests/components/min_max/test_sensor.py b/tests/components/min_max/test_sensor.py index 417bcda2d91..96cbf7317c7 100644 --- a/tests/components/min_max/test_sensor.py +++ b/tests/components/min_max/test_sensor.py @@ -35,7 +35,7 @@ RANGE_1_DIGIT = round(max(VALUES) - min(VALUES), 1) RANGE_4_DIGITS = round(max(VALUES) - min(VALUES), 4) -async def test_default_name_sensor(hass): +async def test_default_name_sensor(hass: HomeAssistant) -> None: """Test the min sensor with a default name.""" config = { "sensor": { @@ -60,7 +60,7 @@ async def test_default_name_sensor(hass): assert entity_ids[2] == state.attributes.get("min_entity_id") -async def test_min_sensor(hass): +async def test_min_sensor(hass: HomeAssistant) -> None: """Test the min sensor.""" config = { "sensor": { @@ -92,7 +92,7 @@ async def test_min_sensor(hass): assert entity.unique_id == "very_unique_id" -async def test_max_sensor(hass): +async def test_max_sensor(hass: HomeAssistant) -> None: """Test the max sensor.""" config = { "sensor": { @@ -119,7 +119,7 @@ async def test_max_sensor(hass): assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT -async def test_mean_sensor(hass): +async def test_mean_sensor(hass: HomeAssistant) -> None: """Test the mean sensor.""" config = { "sensor": { @@ -145,7 +145,7 @@ async def test_mean_sensor(hass): assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT -async def test_mean_1_digit_sensor(hass): +async def test_mean_1_digit_sensor(hass: HomeAssistant) -> None: """Test the mean with 1-digit precision sensor.""" config = { "sensor": { @@ -171,7 +171,7 @@ async def test_mean_1_digit_sensor(hass): assert str(float(MEAN_1_DIGIT)) == state.state -async def test_mean_4_digit_sensor(hass): +async def test_mean_4_digit_sensor(hass: HomeAssistant) -> None: """Test the mean with 4-digit precision sensor.""" config = { "sensor": { @@ -197,7 +197,7 @@ async def test_mean_4_digit_sensor(hass): assert str(float(MEAN_4_DIGITS)) == state.state -async def test_median_sensor(hass): +async def test_median_sensor(hass: HomeAssistant) -> None: """Test the median sensor.""" config = { "sensor": { @@ -223,7 +223,7 @@ async def test_median_sensor(hass): assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT -async def test_range_4_digit_sensor(hass): +async def test_range_4_digit_sensor(hass: HomeAssistant) -> None: """Test the range with 4-digit precision sensor.""" config = { "sensor": { @@ -249,7 +249,7 @@ async def test_range_4_digit_sensor(hass): assert str(float(RANGE_4_DIGITS)) == state.state -async def test_range_1_digit_sensor(hass): +async def test_range_1_digit_sensor(hass: HomeAssistant) -> None: """Test the range with 1-digit precision sensor.""" config = { "sensor": { @@ -275,7 +275,7 @@ async def test_range_1_digit_sensor(hass): assert str(float(RANGE_1_DIGIT)) == state.state -async def test_not_enough_sensor_value(hass): +async def test_not_enough_sensor_value(hass: HomeAssistant) -> None: """Test that there is nothing done if not enough values available.""" config = { "sensor": { @@ -327,7 +327,7 @@ async def test_not_enough_sensor_value(hass): assert state.attributes.get("max_value") is None -async def test_different_unit_of_measurement(hass): +async def test_different_unit_of_measurement(hass: HomeAssistant) -> None: """Test for different unit of measurement.""" config = { "sensor": { @@ -374,7 +374,7 @@ async def test_different_unit_of_measurement(hass): assert state.attributes.get("unit_of_measurement") == "ERR" -async def test_last_sensor(hass): +async def test_last_sensor(hass: HomeAssistant) -> None: """Test the last sensor.""" config = { "sensor": { @@ -399,7 +399,7 @@ async def test_last_sensor(hass): assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT -async def test_reload(hass): +async def test_reload(hass: HomeAssistant) -> None: """Verify we can reload filter sensors.""" hass.states.async_set("sensor.test_1", 12345) hass.states.async_set("sensor.test_2", 45678) From 27e6b69656c3892078a0149c002a5d726684f6f5 Mon Sep 17 00:00:00 2001 From: Tom Schneider Date: Wed, 23 Nov 2022 20:19:20 +0100 Subject: [PATCH 0653/1033] Add yamaha brand configuration (#81267) Co-authored-by: Franck Nijhof --- homeassistant/brands/yamaha.json | 5 +++++ homeassistant/generated/integrations.json | 25 ++++++++++++++--------- 2 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 homeassistant/brands/yamaha.json diff --git a/homeassistant/brands/yamaha.json b/homeassistant/brands/yamaha.json new file mode 100644 index 00000000000..d25e85f1b25 --- /dev/null +++ b/homeassistant/brands/yamaha.json @@ -0,0 +1,5 @@ +{ + "domain": "yamaha", + "name": "Yamaha", + "integrations": ["yamaha", "yamaha_musiccast"] +} diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 0eb5ed5fc6f..b407decbd34 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -6156,16 +6156,21 @@ } }, "yamaha": { - "name": "Yamaha Network Receivers", - "integration_type": "hub", - "config_flow": false, - "iot_class": "local_polling" - }, - "yamaha_musiccast": { - "name": "MusicCast", - "integration_type": "hub", - "config_flow": true, - "iot_class": "local_push" + "name": "Yamaha", + "integrations": { + "yamaha": { + "integration_type": "hub", + "config_flow": false, + "iot_class": "local_polling", + "name": "Yamaha Network Receivers" + }, + "yamaha_musiccast": { + "integration_type": "hub", + "config_flow": true, + "iot_class": "local_push", + "name": "MusicCast" + } + } }, "yandex": { "name": "Yandex", From 23bc39b7f4415782b5612631cd9b22641b8967d5 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 23 Nov 2022 20:26:55 +0100 Subject: [PATCH 0654/1033] Add type hints on `Template().__init__()` (#82574) --- homeassistant/components/esphome/__init__.py | 3 +-- homeassistant/components/mqtt/models.py | 2 +- .../components/shell_command/__init__.py | 2 +- .../components/websocket_api/commands.py | 2 +- homeassistant/helpers/config_validation.py | 4 ++-- homeassistant/helpers/template.py | 21 +++++++++++-------- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index 894928e597a..3315711f4ad 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -146,8 +146,7 @@ async def async_setup_entry( # noqa: C901 if service.data_template: try: data_template = { - key: Template(value) # type: ignore[no-untyped-call] - for key, value in service.data_template.items() + key: Template(value) for key, value in service.data_template.items() } template.attach(hass, data_template) service_data.update( diff --git a/homeassistant/components/mqtt/models.py b/homeassistant/components/mqtt/models.py index a00097a6839..c0b299c7582 100644 --- a/homeassistant/components/mqtt/models.py +++ b/homeassistant/components/mqtt/models.py @@ -152,7 +152,7 @@ class MqttCommandTemplate: values[ATTR_NAME] = self._entity.name if not self._template_state: self._template_state = template.TemplateStateFromEntityId( - self._command_template.hass, self._entity.entity_id + self._entity.hass, self._entity.entity_id ) values[ATTR_THIS] = self._template_state diff --git a/homeassistant/components/shell_command/__init__.py b/homeassistant/components/shell_command/__init__.py index a0dfd3388b4..ac47830f840 100644 --- a/homeassistant/components/shell_command/__init__.py +++ b/homeassistant/components/shell_command/__init__.py @@ -43,7 +43,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: cache[cmd] = prog, args, args_compiled else: prog, args = cmd.split(" ", 1) - args_compiled = template.Template(args, hass) + args_compiled = template.Template(str(args), hass) cache[cmd] = prog, args, args_compiled if args_compiled: diff --git a/homeassistant/components/websocket_api/commands.py b/homeassistant/components/websocket_api/commands.py index 19ec505e449..b4a18ab9ff0 100644 --- a/homeassistant/components/websocket_api/commands.py +++ b/homeassistant/components/websocket_api/commands.py @@ -446,7 +446,7 @@ async def handle_render_template( ) -> None: """Handle render_template command.""" template_str = msg["template"] - template_obj = template.Template(template_str, hass) # type: ignore[no-untyped-call] + template_obj = template.Template(template_str, hass) variables = msg.get("variables") timeout = msg.get("timeout") info = None diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 6963b2cfb05..667aec9fccf 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -594,7 +594,7 @@ def template(value: Any | None) -> template_helper.Template: if isinstance(value, (list, dict, template_helper.Template)): raise vol.Invalid("template value should be a string") - template_value = template_helper.Template(str(value)) # type: ignore[no-untyped-call] + template_value = template_helper.Template(str(value)) try: template_value.ensure_valid() @@ -612,7 +612,7 @@ def dynamic_template(value: Any | None) -> template_helper.Template: if not template_helper.is_template_string(str(value)): raise vol.Invalid("template value does not contain a dynamic template") - template_value = template_helper.Template(str(value)) # type: ignore[no-untyped-call] + template_value = template_helper.Template(str(value)) try: template_value.ensure_valid() return template_value diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 69986570a69..87240d35c8f 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -19,6 +19,7 @@ import re import statistics from struct import error as StructError, pack, unpack_from import sys +from types import CodeType from typing import Any, NoReturn, TypeVar, cast, overload from urllib.parse import urlencode as urllib_urlencode import weakref @@ -330,19 +331,19 @@ class Template: "_hash_cache", ) - def __init__(self, template, hass=None): + def __init__(self, template: str, hass: HomeAssistant | None = None) -> None: """Instantiate a template.""" if not isinstance(template, str): raise TypeError("Expected template to be a string") self.template: str = template.strip() - self._compiled_code = None + self._compiled_code: CodeType | None = None self._compiled: jinja2.Template | None = None self.hass = hass self.is_static = not is_template_string(template) - self._exc_info = None - self._limited = None - self._strict = None + self._exc_info: sys._OptExcInfo | None = None + self._limited: bool | None = None + self._strict: bool | None = None self._hash_cache: int = hash(self.template) @property @@ -383,10 +384,10 @@ class Template: If limited is True, the template is not allowed to access any function or filter depending on hass or the state machine. """ if self.is_static: - if not parse_result or self.hass.config.legacy_templates: + if not parse_result or self.hass and self.hass.config.legacy_templates: return self.template return self._parse_result(self.template) - + assert self.hass is not None, "hass variable not set on template" return run_callback_threadsafe( self.hass.loop, partial(self.async_render, variables, parse_result, limited, **kwargs), @@ -408,7 +409,7 @@ class Template: If limited is True, the template is not allowed to access any function or filter depending on hass or the state machine. """ if self.is_static: - if not parse_result or self.hass.config.legacy_templates: + if not parse_result or self.hass and self.hass.config.legacy_templates: return self.template return self._parse_result(self.template) @@ -424,7 +425,7 @@ class Template: render_result = render_result.strip() - if self.hass.config.legacy_templates or not parse_result: + if not parse_result or self.hass and self.hass.config.legacy_templates: return render_result return self._parse_result(render_result) @@ -494,6 +495,7 @@ class Template: finish_event = asyncio.Event() def _render_template() -> None: + assert self.hass is not None, "hass variable not set on template" try: _render_with_context(self.template, compiled, **kwargs) except TimeoutError: @@ -608,6 +610,7 @@ class Template: self._strict is None or self._strict == strict ), "can't change between strict and non strict template" assert not (strict and limited), "can't combine strict and limited template" + assert self._compiled_code is not None, "template code was not compiled" self._limited = limited self._strict = strict From 6b85d17e7c0d1e2b89c958d575eacabb4e3ff756 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 23 Nov 2022 20:28:52 +0100 Subject: [PATCH 0655/1033] Add type hints to template tests (#82566) --- tests/helpers/test_template.py | 311 ++++++++++++++++++--------------- 1 file changed, 170 insertions(+), 141 deletions(-) diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 63a6154a190..cd73e663b71 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -1,8 +1,12 @@ """Test Home Assistant template helper methods.""" +from __future__ import annotations + +from collections.abc import Iterable from datetime import datetime, timedelta import logging import math import random +from typing import Any from unittest.mock import patch from freezegun import freeze_time @@ -27,6 +31,7 @@ from homeassistant.exceptions import TemplateError from homeassistant.helpers import device_registry as dr, entity, template from homeassistant.helpers.entity_platform import EntityPlatform from homeassistant.helpers.json import json_dumps +from homeassistant.helpers.typing import TemplateVarsType from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util from homeassistant.util.unit_system import UnitSystem @@ -39,7 +44,7 @@ from tests.common import ( ) -def _set_up_units(hass): +def _set_up_units(hass: HomeAssistant) -> None: """Set up the tests.""" hass.config.units = UnitSystem( "custom", @@ -54,25 +59,37 @@ def _set_up_units(hass): ) -def render(hass, template_str, variables=None): +def render( + hass: HomeAssistant, template_str: str, variables: TemplateVarsType | None = None +) -> Any: """Create render info from template.""" tmp = template.Template(template_str, hass) return tmp.async_render(variables) -def render_to_info(hass, template_str, variables=None): +def render_to_info( + hass: HomeAssistant, template_str: str, variables: TemplateVarsType | None = None +) -> template.RenderInfo: """Create render info from template.""" tmp = template.Template(template_str, hass) return tmp.async_render_to_info(variables) -def extract_entities(hass, template_str, variables=None): +def extract_entities( + hass: HomeAssistant, template_str: str, variables: TemplateVarsType | None = None +) -> set[str]: """Extract entities from a template.""" info = render_to_info(hass, template_str, variables) return info.entities -def assert_result_info(info, result, entities=None, domains=None, all_states=False): +def assert_result_info( + info: template.RenderInfo, + result: Any, + entities: Iterable[str] | None = None, + domains: Iterable[str] | None = None, + all_states: bool = False, +) -> None: """Check result info.""" assert info.result() == result assert info.all_states == all_states @@ -91,7 +108,7 @@ def assert_result_info(info, result, entities=None, domains=None, all_states=Fal assert not hasattr(info, "_domains") -def test_template_equality(): +def test_template_equality() -> None: """Test template comparison and hashing.""" template_one = template.Template("{{ template_one }}") template_one_1 = template.Template("{{ template_one }}") @@ -108,7 +125,7 @@ def test_template_equality(): template.Template(["{{ template_one }}"]) -def test_invalid_template(hass): +def test_invalid_template(hass: HomeAssistant) -> None: """Invalid template raises error.""" tmpl = template.Template("{{", hass) @@ -130,7 +147,7 @@ def test_invalid_template(hass): tmpl.async_render() -def test_referring_states_by_entity_id(hass): +def test_referring_states_by_entity_id(hass: HomeAssistant) -> None: """Test referring states by entity id.""" hass.states.async_set("test.object", "happy") assert ( @@ -148,7 +165,7 @@ def test_referring_states_by_entity_id(hass): ) -def test_invalid_entity_id(hass): +def test_invalid_entity_id(hass: HomeAssistant) -> None: """Test referring states by entity id.""" with pytest.raises(TemplateError): template.Template('{{ states["big.fat..."] }}', hass).async_render() @@ -158,13 +175,13 @@ def test_invalid_entity_id(hass): template.Template('{{ states["invalid/domain"] }}', hass).async_render() -def test_raise_exception_on_error(hass): +def test_raise_exception_on_error(hass: HomeAssistant) -> None: """Test raising an exception on error.""" with pytest.raises(TemplateError): template.Template("{{ invalid_syntax").ensure_valid() -def test_iterating_all_states(hass): +def test_iterating_all_states(hass: HomeAssistant) -> None: """Test iterating all states.""" tmpl_str = "{% for state in states %}{{ state.state }}{% endfor %}" @@ -179,7 +196,7 @@ def test_iterating_all_states(hass): assert_result_info(info, "10happy", entities=[], all_states=True) -def test_iterating_all_states_unavailable(hass): +def test_iterating_all_states_unavailable(hass: HomeAssistant) -> None: """Test iterating all states unavailable.""" hass.states.async_set("test.object", "on") @@ -197,7 +214,7 @@ def test_iterating_all_states_unavailable(hass): assert_result_info(info, 1, entities=[], all_states=True) -def test_iterating_domain_states(hass): +def test_iterating_domain_states(hass: HomeAssistant) -> None: """Test iterating domain states.""" tmpl_str = "{% for state in states.sensor %}{{ state.state }}{% endfor %}" @@ -218,7 +235,7 @@ def test_iterating_domain_states(hass): ) -def test_float_function(hass): +def test_float_function(hass: HomeAssistant) -> None: """Test float function.""" hass.states.async_set("sensor.temperature", "12") @@ -245,7 +262,7 @@ def test_float_function(hass): assert render(hass, "{{ float('bad', default=1) }}") == 1 -def test_float_filter(hass): +def test_float_filter(hass: HomeAssistant) -> None: """Test float filter.""" hass.states.async_set("sensor.temperature", "12") @@ -261,7 +278,7 @@ def test_float_filter(hass): assert render(hass, "{{ 'bad' | float(default=1) }}") == 1 -def test_int_filter(hass): +def test_int_filter(hass: HomeAssistant) -> None: """Test int filter.""" hass.states.async_set("sensor.temperature", "12.2") assert render(hass, "{{ states.sensor.temperature.state | int }}") == 12 @@ -279,7 +296,7 @@ def test_int_filter(hass): assert render(hass, "{{ 'bad' | int(default=1) }}") == 1 -def test_int_function(hass): +def test_int_function(hass: HomeAssistant) -> None: """Test int filter.""" hass.states.async_set("sensor.temperature", "12.2") assert render(hass, "{{ int(states.sensor.temperature.state) }}") == 12 @@ -297,7 +314,7 @@ def test_int_function(hass): assert render(hass, "{{ int('bad', default=1) }}") == 1 -def test_bool_function(hass): +def test_bool_function(hass: HomeAssistant) -> None: """Test bool function.""" assert render(hass, "{{ bool(true) }}") is True assert render(hass, "{{ bool(false) }}") is False @@ -311,7 +328,7 @@ def test_bool_function(hass): assert render(hass, "{{ bool('unavailable', default=none) }}") is None -def test_bool_filter(hass): +def test_bool_filter(hass: HomeAssistant) -> None: """Test bool filter.""" assert render(hass, "{{ true | bool }}") is True assert render(hass, "{{ false | bool }}") is False @@ -366,7 +383,7 @@ def test_isnumber(hass, value, expected): ) -def test_rounding_value(hass): +def test_rounding_value(hass: HomeAssistant) -> None: """Test rounding value.""" hass.states.async_set("sensor.temperature", 12.78) @@ -406,7 +423,7 @@ def test_rounding_value(hass): ) -def test_rounding_value_on_error(hass): +def test_rounding_value_on_error(hass: HomeAssistant) -> None: """Test rounding value handling of error.""" # Test handling of invalid input with pytest.raises(TemplateError): @@ -419,7 +436,7 @@ def test_rounding_value_on_error(hass): assert render(hass, "{{ 'no_number' | round(default=1) }}") == 1 -def test_multiply(hass): +def test_multiply(hass: HomeAssistant) -> None: """Test multiply.""" tests = {10: 100} @@ -440,7 +457,7 @@ def test_multiply(hass): assert render(hass, "{{ 'no_number' | multiply(10, default=1) }}") == 1 -def test_logarithm(hass): +def test_logarithm(hass: HomeAssistant) -> None: """Test logarithm.""" tests = [ (4, 2, 2.0), @@ -482,7 +499,7 @@ def test_logarithm(hass): assert render(hass, "{{ log(0, 10, default=1) }}") == 1 -def test_sine(hass): +def test_sine(hass: HomeAssistant) -> None: """Test sine.""" tests = [ (0, 0.0), @@ -512,7 +529,7 @@ def test_sine(hass): assert render(hass, "{{ sin('no_number', default=1) }}") == 1 -def test_cos(hass): +def test_cos(hass: HomeAssistant) -> None: """Test cosine.""" tests = [ (0, 1.0), @@ -542,7 +559,7 @@ def test_cos(hass): assert render(hass, "{{ cos('no_number', default=1) }}") == 1 -def test_tan(hass): +def test_tan(hass: HomeAssistant) -> None: """Test tangent.""" tests = [ (0, 0.0), @@ -572,7 +589,7 @@ def test_tan(hass): assert render(hass, "{{ tan('no_number', default=1) }}") == 1 -def test_sqrt(hass): +def test_sqrt(hass: HomeAssistant) -> None: """Test square root.""" tests = [ (0, 0.0), @@ -602,7 +619,7 @@ def test_sqrt(hass): assert render(hass, "{{ sqrt('no_number', default=1) }}") == 1 -def test_arc_sine(hass): +def test_arc_sine(hass: HomeAssistant) -> None: """Test arcus sine.""" tests = [ (-1.0, -1.571), @@ -639,7 +656,7 @@ def test_arc_sine(hass): assert render(hass, "{{ asin('no_number', default=1) }}") == 1 -def test_arc_cos(hass): +def test_arc_cos(hass: HomeAssistant) -> None: """Test arcus cosine.""" tests = [ (-1.0, 3.142), @@ -676,7 +693,7 @@ def test_arc_cos(hass): assert render(hass, "{{ acos('no_number', default=1) }}") == 1 -def test_arc_tan(hass): +def test_arc_tan(hass: HomeAssistant) -> None: """Test arcus tangent.""" tests = [ (-10.0, -1.471), @@ -710,7 +727,7 @@ def test_arc_tan(hass): assert render(hass, "{{ atan('no_number', default=1) }}") == 1 -def test_arc_tan2(hass): +def test_arc_tan2(hass: HomeAssistant) -> None: """Test two parameter version of arcus tangent.""" tests = [ (-10.0, -10.0, -2.356), @@ -754,7 +771,7 @@ def test_arc_tan2(hass): assert render(hass, "{{ atan2('duck', 'goose', default=1) }}") == 1 -def test_strptime(hass): +def test_strptime(hass: HomeAssistant) -> None: """Test the parse timestamp method.""" tests = [ ("2016-10-19 15:22:05.588122 UTC", "%Y-%m-%d %H:%M:%S.%f %Z", None), @@ -790,7 +807,7 @@ def test_strptime(hass): assert render(hass, "{{ strptime('invalid', '%Y', default=1) }}") == 1 -def test_timestamp_custom(hass): +def test_timestamp_custom(hass: HomeAssistant) -> None: """Test the timestamps to custom filter.""" hass.config.set_time_zone("UTC") now = dt_util.utcnow() @@ -832,7 +849,7 @@ def test_timestamp_custom(hass): assert render(hass, "{{ None | timestamp_custom(default=1) }}") == 1 -def test_timestamp_local(hass): +def test_timestamp_local(hass: HomeAssistant) -> None: """Test the timestamps to local filter.""" hass.config.set_time_zone("UTC") tests = [ @@ -886,7 +903,7 @@ def test_as_datetime(hass, input): ) -def test_as_datetime_from_timestamp(hass): +def test_as_datetime_from_timestamp(hass: HomeAssistant) -> None: """Test converting a UNIX timestamp to a date object.""" tests = [ (1469119144, "2016-07-21 16:39:04+00:00"), @@ -916,7 +933,7 @@ def test_as_datetime_from_timestamp(hass): ) -def test_as_local(hass): +def test_as_local(hass: HomeAssistant) -> None: """Test converting time to local.""" hass.states.async_set("test.object", "available") @@ -929,7 +946,7 @@ def test_as_local(hass): ).async_render() == str(dt_util.as_local(last_updated)) -def test_to_json(hass): +def test_to_json(hass: HomeAssistant) -> None: """Test the object to JSON string filter.""" # Note that we're not testing the actual json.loads and json.dumps methods, @@ -941,7 +958,7 @@ def test_to_json(hass): assert actual_result == expected_result -def test_to_json_string(hass): +def test_to_json_string(hass: HomeAssistant) -> None: """Test the object to JSON string filter.""" # Note that we're not testing the actual json.loads and json.dumps methods, @@ -956,7 +973,7 @@ def test_to_json_string(hass): assert actual_value == '"Bar ҝ éèà"' -def test_from_json(hass): +def test_from_json(hass: HomeAssistant) -> None: """Test the JSON string to object filter.""" # Note that we're not testing the actual json.loads and json.dumps methods, @@ -968,7 +985,7 @@ def test_from_json(hass): assert actual_result == expected_result -def test_average(hass): +def test_average(hass: HomeAssistant) -> None: """Test the average filter.""" assert template.Template("{{ [1, 2, 3] | average }}", hass).async_render() == 2 assert template.Template("{{ average([1, 2, 3]) }}", hass).async_render() == 2 @@ -996,7 +1013,7 @@ def test_average(hass): template.Template("{{ average([]) }}", hass).async_render() -def test_min(hass): +def test_min(hass: HomeAssistant) -> None: """Test the min filter.""" assert template.Template("{{ [1, 2, 3] | min }}", hass).async_render() == 1 assert template.Template("{{ min([1, 2, 3]) }}", hass).async_render() == 1 @@ -1012,7 +1029,7 @@ def test_min(hass): template.Template("{{ min(1) }}", hass).async_render() -def test_max(hass): +def test_max(hass: HomeAssistant) -> None: """Test the max filter.""" assert template.Template("{{ [1, 2, 3] | max }}", hass).async_render() == 3 assert template.Template("{{ max([1, 2, 3]) }}", hass).async_render() == 3 @@ -1095,12 +1112,12 @@ def test_min_max_attribute(hass, attribute): ) -def test_ord(hass): +def test_ord(hass: HomeAssistant) -> None: """Test the ord filter.""" assert template.Template('{{ "d" | ord }}', hass).async_render() == 100 -def test_base64_encode(hass): +def test_base64_encode(hass: HomeAssistant) -> None: """Test the base64_encode filter.""" assert ( template.Template('{{ "homeassistant" | base64_encode }}', hass).async_render() @@ -1108,7 +1125,7 @@ def test_base64_encode(hass): ) -def test_base64_decode(hass): +def test_base64_decode(hass: HomeAssistant) -> None: """Test the base64_decode filter.""" assert ( template.Template( @@ -1118,7 +1135,7 @@ def test_base64_decode(hass): ) -def test_slugify(hass): +def test_slugify(hass: HomeAssistant) -> None: """Test the slugify filter.""" assert ( template.Template('{{ slugify("Home Assistant") }}', hass).async_render() @@ -1138,7 +1155,7 @@ def test_slugify(hass): ) -def test_ordinal(hass): +def test_ordinal(hass: HomeAssistant) -> None: """Test the ordinal filter.""" tests = [ (1, "1st"), @@ -1158,7 +1175,7 @@ def test_ordinal(hass): ) -def test_timestamp_utc(hass): +def test_timestamp_utc(hass: HomeAssistant) -> None: """Test the timestamps to local filter.""" now = dt_util.utcnow() tests = [ @@ -1186,7 +1203,7 @@ def test_timestamp_utc(hass): assert render(hass, "{{ None | timestamp_utc(default=1) }}") == 1 -def test_as_timestamp(hass): +def test_as_timestamp(hass: HomeAssistant) -> None: """Test the as_timestamp function.""" with pytest.raises(TemplateError): template.Template('{{ as_timestamp("invalid") }}', hass).async_render() @@ -1218,24 +1235,24 @@ def test_random_every_time(test_choice, hass): assert tpl.async_render() == "bar" -def test_passing_vars_as_keywords(hass): +def test_passing_vars_as_keywords(hass: HomeAssistant) -> None: """Test passing variables as keywords.""" assert template.Template("{{ hello }}", hass).async_render(hello=127) == 127 -def test_passing_vars_as_vars(hass): +def test_passing_vars_as_vars(hass: HomeAssistant) -> None: """Test passing variables as variables.""" assert template.Template("{{ hello }}", hass).async_render({"hello": 127}) == 127 -def test_passing_vars_as_list(hass): +def test_passing_vars_as_list(hass: HomeAssistant) -> None: """Test passing variables as list.""" assert template.render_complex( template.Template("{{ hello }}", hass), {"hello": ["foo", "bar"]} ) == ["foo", "bar"] -def test_passing_vars_as_list_element(hass): +def test_passing_vars_as_list_element(hass: HomeAssistant) -> None: """Test passing variables as list.""" assert ( template.render_complex( @@ -1245,7 +1262,7 @@ def test_passing_vars_as_list_element(hass): ) -def test_passing_vars_as_dict_element(hass): +def test_passing_vars_as_dict_element(hass: HomeAssistant) -> None: """Test passing variables as list.""" assert ( template.render_complex( @@ -1255,44 +1272,50 @@ def test_passing_vars_as_dict_element(hass): ) -def test_passing_vars_as_dict(hass): +def test_passing_vars_as_dict(hass: HomeAssistant) -> None: """Test passing variables as list.""" assert template.render_complex( template.Template("{{ hello }}", hass), {"hello": {"foo": "bar"}} ) == {"foo": "bar"} -def test_render_with_possible_json_value_with_valid_json(hass): +def test_render_with_possible_json_value_with_valid_json(hass: HomeAssistant) -> None: """Render with possible JSON value with valid JSON.""" tpl = template.Template("{{ value_json.hello }}", hass) assert tpl.async_render_with_possible_json_value('{"hello": "world"}') == "world" -def test_render_with_possible_json_value_with_invalid_json(hass): +def test_render_with_possible_json_value_with_invalid_json(hass: HomeAssistant) -> None: """Render with possible JSON value with invalid JSON.""" tpl = template.Template("{{ value_json }}", hass) assert tpl.async_render_with_possible_json_value("{ I AM NOT JSON }") == "" -def test_render_with_possible_json_value_with_template_error_value(hass): +def test_render_with_possible_json_value_with_template_error_value( + hass: HomeAssistant, +) -> None: """Render with possible JSON value with template error value.""" tpl = template.Template("{{ non_existing.variable }}", hass) assert tpl.async_render_with_possible_json_value("hello", "-") == "-" -def test_render_with_possible_json_value_with_missing_json_value(hass): +def test_render_with_possible_json_value_with_missing_json_value( + hass: HomeAssistant, +) -> None: """Render with possible JSON value with unknown JSON object.""" tpl = template.Template("{{ value_json.goodbye }}", hass) assert tpl.async_render_with_possible_json_value('{"hello": "world"}') == "" -def test_render_with_possible_json_value_valid_with_is_defined(hass): +def test_render_with_possible_json_value_valid_with_is_defined( + hass: HomeAssistant, +) -> None: """Render with possible JSON value with known JSON object.""" tpl = template.Template("{{ value_json.hello|is_defined }}", hass) assert tpl.async_render_with_possible_json_value('{"hello": "world"}') == "world" -def test_render_with_possible_json_value_undefined_json(hass): +def test_render_with_possible_json_value_undefined_json(hass: HomeAssistant) -> None: """Render with possible JSON value with unknown JSON object.""" tpl = template.Template("{{ value_json.bye|is_defined }}", hass) assert ( @@ -1301,13 +1324,15 @@ def test_render_with_possible_json_value_undefined_json(hass): ) -def test_render_with_possible_json_value_undefined_json_error_value(hass): +def test_render_with_possible_json_value_undefined_json_error_value( + hass: HomeAssistant, +) -> None: """Render with possible JSON value with unknown JSON object.""" tpl = template.Template("{{ value_json.bye|is_defined }}", hass) assert tpl.async_render_with_possible_json_value('{"hello": "world"}', "") == "" -def test_render_with_possible_json_value_non_string_value(hass): +def test_render_with_possible_json_value_non_string_value(hass: HomeAssistant) -> None: """Render with possible JSON value with non-string value.""" tpl = template.Template( """ @@ -1320,7 +1345,7 @@ def test_render_with_possible_json_value_non_string_value(hass): assert tpl.async_render_with_possible_json_value(value) == expected -def test_if_state_exists(hass): +def test_if_state_exists(hass: HomeAssistant) -> None: """Test if state exists works.""" hass.states.async_set("test.object", "available") tpl = template.Template( @@ -1329,7 +1354,7 @@ def test_if_state_exists(hass): assert tpl.async_render() == "exists" -def test_is_state(hass): +def test_is_state(hass: HomeAssistant) -> None: """Test is_state method.""" hass.states.async_set("test.object", "available") tpl = template.Template( @@ -1365,7 +1390,7 @@ def test_is_state(hass): assert tpl.async_render() == "test.object" -def test_is_state_attr(hass): +def test_is_state_attr(hass: HomeAssistant) -> None: """Test is_state_attr method.""" hass.states.async_set("test.object", "available", {"mode": "on"}) tpl = template.Template( @@ -1401,7 +1426,7 @@ def test_is_state_attr(hass): assert tpl.async_render() == "test.object" -def test_state_attr(hass): +def test_state_attr(hass: HomeAssistant) -> None: """Test state_attr method.""" hass.states.async_set( "test.object", "available", {"effect": "action", "mode": "on"} @@ -1439,7 +1464,7 @@ def test_state_attr(hass): assert tpl.async_render() == "action" -def test_states_function(hass): +def test_states_function(hass: HomeAssistant) -> None: """Test using states as a function.""" hass.states.async_set("test.object", "available") tpl = template.Template('{{ states("test.object") }}', hass) @@ -1648,7 +1673,7 @@ def test_timedelta(mock_is_safe, hass): assert result == "15 days" -def test_version(hass): +def test_version(hass: HomeAssistant) -> None: """Test version filter and function.""" filter_result = template.Template( "{{ '2099.9.9' | version}}", @@ -1687,7 +1712,7 @@ def test_version(hass): ).async_render() -def test_regex_match(hass): +def test_regex_match(hass: HomeAssistant) -> None: """Test regex_match method.""" tpl = template.Template( r""" @@ -1722,7 +1747,7 @@ def test_regex_match(hass): assert tpl.async_render() is True -def test_match_test(hass): +def test_match_test(hass: HomeAssistant) -> None: """Test match test.""" tpl = template.Template( r""" @@ -1733,7 +1758,7 @@ def test_match_test(hass): assert tpl.async_render() is True -def test_regex_search(hass): +def test_regex_search(hass: HomeAssistant) -> None: """Test regex_search method.""" tpl = template.Template( r""" @@ -1768,7 +1793,7 @@ def test_regex_search(hass): assert tpl.async_render() is True -def test_search_test(hass): +def test_search_test(hass: HomeAssistant) -> None: """Test search test.""" tpl = template.Template( r""" @@ -1779,7 +1804,7 @@ def test_search_test(hass): assert tpl.async_render() is True -def test_regex_replace(hass): +def test_regex_replace(hass: HomeAssistant) -> None: """Test regex_replace method.""" tpl = template.Template( r""" @@ -1798,7 +1823,7 @@ def test_regex_replace(hass): assert tpl.async_render() == ["Home Assistant test"] -def test_regex_findall(hass): +def test_regex_findall(hass: HomeAssistant) -> None: """Test regex_findall method.""" tpl = template.Template( """ @@ -1809,7 +1834,7 @@ def test_regex_findall(hass): assert tpl.async_render() == ["JFK", "LHR"] -def test_regex_findall_index(hass): +def test_regex_findall_index(hass: HomeAssistant) -> None: """Test regex_findall_index method.""" tpl = template.Template( """ @@ -1836,7 +1861,7 @@ def test_regex_findall_index(hass): assert tpl.async_render() == "LHR" -def test_bitwise_and(hass): +def test_bitwise_and(hass: HomeAssistant) -> None: """Test bitwise_and method.""" tpl = template.Template( """ @@ -1861,7 +1886,7 @@ def test_bitwise_and(hass): assert tpl.async_render() == 8 & 2 -def test_bitwise_or(hass): +def test_bitwise_or(hass: HomeAssistant) -> None: """Test bitwise_or method.""" tpl = template.Template( """ @@ -2020,7 +2045,7 @@ def test_unpack(hass, caplog): ) -def test_distance_function_with_1_state(hass): +def test_distance_function_with_1_state(hass: HomeAssistant) -> None: """Test distance function with 1 state.""" _set_up_units(hass) hass.states.async_set( @@ -2030,7 +2055,7 @@ def test_distance_function_with_1_state(hass): assert tpl.async_render() == 187 -def test_distance_function_with_2_states(hass): +def test_distance_function_with_2_states(hass: HomeAssistant) -> None: """Test distance function with 2 states.""" _set_up_units(hass) hass.states.async_set( @@ -2047,14 +2072,14 @@ def test_distance_function_with_2_states(hass): assert tpl.async_render() == 187 -def test_distance_function_with_1_coord(hass): +def test_distance_function_with_1_coord(hass: HomeAssistant) -> None: """Test distance function with 1 coord.""" _set_up_units(hass) tpl = template.Template('{{ distance("32.87336", "-117.22943") | round }}', hass) assert tpl.async_render() == 187 -def test_distance_function_with_2_coords(hass): +def test_distance_function_with_2_coords(hass: HomeAssistant) -> None: """Test distance function with 2 coords.""" _set_up_units(hass) assert ( @@ -2067,7 +2092,7 @@ def test_distance_function_with_2_coords(hass): ) -def test_distance_function_with_1_state_1_coord(hass): +def test_distance_function_with_1_state_1_coord(hass: HomeAssistant) -> None: """Test distance function with 1 state 1 coord.""" _set_up_units(hass) hass.states.async_set( @@ -2088,7 +2113,7 @@ def test_distance_function_with_1_state_1_coord(hass): assert tpl2.async_render() == 187 -def test_distance_function_return_none_if_invalid_state(hass): +def test_distance_function_return_none_if_invalid_state(hass: HomeAssistant) -> None: """Test distance function return None if invalid state.""" hass.states.async_set("test.object_2", "happy", {"latitude": 10}) tpl = template.Template("{{ distance(states.test.object_2) | round }}", hass) @@ -2096,7 +2121,7 @@ def test_distance_function_return_none_if_invalid_state(hass): tpl.async_render() -def test_distance_function_return_none_if_invalid_coord(hass): +def test_distance_function_return_none_if_invalid_coord(hass: HomeAssistant) -> None: """Test distance function return None if invalid coord.""" assert ( template.Template('{{ distance("123", "abc") }}', hass).async_render() is None @@ -2113,7 +2138,7 @@ def test_distance_function_return_none_if_invalid_coord(hass): assert tpl.async_render() is None -def test_distance_function_with_2_entity_ids(hass): +def test_distance_function_with_2_entity_ids(hass: HomeAssistant) -> None: """Test distance function with 2 entity ids.""" _set_up_units(hass) hass.states.async_set( @@ -2130,7 +2155,7 @@ def test_distance_function_with_2_entity_ids(hass): assert tpl.async_render() == 187 -def test_distance_function_with_1_entity_1_coord(hass): +def test_distance_function_with_1_entity_1_coord(hass: HomeAssistant) -> None: """Test distance function with 1 entity_id and 1 coord.""" _set_up_units(hass) hass.states.async_set( @@ -2144,7 +2169,7 @@ def test_distance_function_with_1_entity_1_coord(hass): assert tpl.async_render() == 187 -def test_closest_function_home_vs_domain(hass): +def test_closest_function_home_vs_domain(hass: HomeAssistant) -> None: """Test closest function home vs domain.""" hass.states.async_set( "test_domain.object", @@ -2176,7 +2201,7 @@ def test_closest_function_home_vs_domain(hass): ) -def test_closest_function_home_vs_all_states(hass): +def test_closest_function_home_vs_all_states(hass: HomeAssistant) -> None: """Test closest function home vs all states.""" hass.states.async_set( "test_domain.object", @@ -2204,7 +2229,7 @@ def test_closest_function_home_vs_all_states(hass): ) -async def test_closest_function_home_vs_group_entity_id(hass): +async def test_closest_function_home_vs_group_entity_id(hass: HomeAssistant) -> None: """Test closest function home vs group entity id.""" hass.states.async_set( "test_domain.object", @@ -2232,7 +2257,7 @@ async def test_closest_function_home_vs_group_entity_id(hass): assert info.rate_limit is None -async def test_closest_function_home_vs_group_state(hass): +async def test_closest_function_home_vs_group_state(hass: HomeAssistant) -> None: """Test closest function home vs group state.""" hass.states.async_set( "test_domain.object", @@ -2266,7 +2291,7 @@ async def test_closest_function_home_vs_group_state(hass): assert info.rate_limit is None -async def test_expand(hass): +async def test_expand(hass: HomeAssistant) -> None: """Test expand function.""" info = render_to_info(hass, "{{ expand('test.object') }}") assert_result_info(info, [], ["test.object"]) @@ -2429,7 +2454,7 @@ async def test_expand(hass): ) -async def test_device_entities(hass): +async def test_device_entities(hass: HomeAssistant) -> None: """Test device_entities function.""" config_entry = MockConfigEntry(domain="light") device_registry = mock_device_registry(hass) @@ -2502,7 +2527,7 @@ async def test_device_entities(hass): assert info.rate_limit is None -async def test_integration_entities(hass): +async def test_integration_entities(hass: HomeAssistant) -> None: """Test integration_entities function.""" entity_registry = mock_registry(hass) @@ -2540,7 +2565,7 @@ async def test_integration_entities(hass): assert info.rate_limit is None -async def test_config_entry_id(hass): +async def test_config_entry_id(hass: HomeAssistant) -> None: """Test config_entry_id function.""" config_entry = MockConfigEntry(domain="light", title="Some integration") config_entry.add_to_hass(hass) @@ -2566,7 +2591,7 @@ async def test_config_entry_id(hass): assert info.rate_limit is None -async def test_device_id(hass): +async def test_device_id(hass: HomeAssistant) -> None: """Test device_id function.""" config_entry = MockConfigEntry(domain="light") device_registry = mock_device_registry(hass) @@ -2609,7 +2634,7 @@ async def test_device_id(hass): assert info.rate_limit is None -async def test_device_attr(hass): +async def test_device_attr(hass: HomeAssistant) -> None: """Test device_attr and is_device_attr functions.""" config_entry = MockConfigEntry(domain="light") device_registry = mock_device_registry(hass) @@ -2714,7 +2739,7 @@ async def test_device_attr(hass): assert info.rate_limit is None -async def test_area_id(hass): +async def test_area_id(hass: HomeAssistant) -> None: """Test area_id function.""" config_entry = MockConfigEntry(domain="light") device_registry = mock_device_registry(hass) @@ -2818,7 +2843,7 @@ async def test_area_id(hass): assert info.rate_limit is None -async def test_area_name(hass): +async def test_area_name(hass: HomeAssistant) -> None: """Test area_name function.""" config_entry = MockConfigEntry(domain="light") device_registry = mock_device_registry(hass) @@ -2897,7 +2922,7 @@ async def test_area_name(hass): assert info.rate_limit is None -async def test_area_entities(hass): +async def test_area_entities(hass: HomeAssistant) -> None: """Test area_entities function.""" config_entry = MockConfigEntry(domain="light") entity_registry = mock_registry(hass) @@ -2950,7 +2975,7 @@ async def test_area_entities(hass): assert info.rate_limit is None -async def test_area_devices(hass): +async def test_area_devices(hass: HomeAssistant) -> None: """Test area_devices function.""" config_entry = MockConfigEntry(domain="light") device_registry = mock_device_registry(hass) @@ -2982,7 +3007,7 @@ async def test_area_devices(hass): assert info.rate_limit is None -def test_closest_function_to_coord(hass): +def test_closest_function_to_coord(hass: HomeAssistant) -> None: """Test closest function to coord.""" hass.states.async_set( "test_domain.closest_home", @@ -3028,7 +3053,7 @@ def test_closest_function_to_coord(hass): assert tpl.async_render() == "test_domain.closest_zone" -def test_async_render_to_info_with_branching(hass): +def test_async_render_to_info_with_branching(hass: HomeAssistant) -> None: """Test async_render_to_info function by domain.""" hass.states.async_set("light.a", "off") hass.states.async_set("light.b", "on") @@ -3060,7 +3085,7 @@ def test_async_render_to_info_with_branching(hass): assert info.rate_limit is None -def test_async_render_to_info_with_complex_branching(hass): +def test_async_render_to_info_with_complex_branching(hass: HomeAssistant) -> None: """Test async_render_to_info function by domain.""" hass.states.async_set("light.a", "off") hass.states.async_set("light.b", "on") @@ -3097,7 +3122,9 @@ def test_async_render_to_info_with_complex_branching(hass): assert info.rate_limit == template.DOMAIN_STATES_RATE_LIMIT -async def test_async_render_to_info_with_wildcard_matching_entity_id(hass): +async def test_async_render_to_info_with_wildcard_matching_entity_id( + hass: HomeAssistant, +) -> None: """Test tracking template with a wildcard.""" template_complex_str = r""" @@ -3119,7 +3146,9 @@ async def test_async_render_to_info_with_wildcard_matching_entity_id(hass): assert info.rate_limit == template.DOMAIN_STATES_RATE_LIMIT -async def test_async_render_to_info_with_wildcard_matching_state(hass): +async def test_async_render_to_info_with_wildcard_matching_state( + hass: HomeAssistant, +) -> None: """Test tracking template with a wildcard.""" template_complex_str = """ @@ -3170,7 +3199,7 @@ async def test_async_render_to_info_with_wildcard_matching_state(hass): assert info.rate_limit == template.DOMAIN_STATES_RATE_LIMIT -def test_nested_async_render_to_info_case(hass): +def test_nested_async_render_to_info_case(hass: HomeAssistant) -> None: """Test a deeply nested state with async_render_to_info.""" hass.states.async_set("input_select.picker", "vacuum.a") @@ -3183,7 +3212,7 @@ def test_nested_async_render_to_info_case(hass): assert info.rate_limit is None -def test_result_as_boolean(hass): +def test_result_as_boolean(hass: HomeAssistant) -> None: """Test converting a template result to a boolean.""" assert template.result_as_boolean(True) is True @@ -3213,7 +3242,7 @@ def test_result_as_boolean(hass): assert template.result_as_boolean(None) is False -def test_closest_function_to_entity_id(hass): +def test_closest_function_to_entity_id(hass: HomeAssistant) -> None: """Test closest function to entity id.""" hass.states.async_set( "test_domain.closest_home", @@ -3270,7 +3299,7 @@ def test_closest_function_to_entity_id(hass): ) -def test_closest_function_to_state(hass): +def test_closest_function_to_state(hass: HomeAssistant) -> None: """Test closest function to state.""" hass.states.async_set( "test_domain.closest_home", @@ -3307,7 +3336,7 @@ def test_closest_function_to_state(hass): ) -def test_closest_function_invalid_state(hass): +def test_closest_function_invalid_state(hass: HomeAssistant) -> None: """Test closest function invalid state.""" hass.states.async_set( "test_domain.closest_home", @@ -3325,7 +3354,7 @@ def test_closest_function_invalid_state(hass): ) -def test_closest_function_state_with_invalid_location(hass): +def test_closest_function_state_with_invalid_location(hass: HomeAssistant) -> None: """Test closest function state with invalid location.""" hass.states.async_set( "test_domain.closest_home", @@ -3341,7 +3370,7 @@ def test_closest_function_state_with_invalid_location(hass): ) -def test_closest_function_invalid_coordinates(hass): +def test_closest_function_invalid_coordinates(hass: HomeAssistant) -> None: """Test closest function invalid coordinates.""" hass.states.async_set( "test_domain.closest_home", @@ -3366,14 +3395,14 @@ def test_closest_function_invalid_coordinates(hass): ) -def test_closest_function_no_location_states(hass): +def test_closest_function_no_location_states(hass: HomeAssistant) -> None: """Test closest function without location states.""" assert ( template.Template("{{ closest(states).entity_id }}", hass).async_render() == "" ) -def test_generate_filter_iterators(hass): +def test_generate_filter_iterators(hass: HomeAssistant) -> None: """Test extract entities function with none entities stuff.""" info = render_to_info( hass, @@ -3430,7 +3459,7 @@ def test_generate_filter_iterators(hass): assert_result_info(info, "sensor.test_sensor=value,", [], ["sensor"]) -def test_generate_select(hass): +def test_generate_select(hass: HomeAssistant) -> None: """Test extract entities function with none entities stuff.""" template_str = """ {{ states.sensor|selectattr("state","equalto","off") @@ -3455,7 +3484,7 @@ def test_generate_select(hass): assert info.domains_lifecycle == {"sensor"} -async def test_async_render_to_info_in_conditional(hass): +async def test_async_render_to_info_in_conditional(hass: HomeAssistant) -> None: """Test extract entities function with none entities stuff.""" template_str = """ {{ states("sensor.xyz") == "dog" }} @@ -3491,7 +3520,7 @@ async def test_async_render_to_info_in_conditional(hass): assert_result_info(info, "oink", ["sensor.xyz", "sensor.pig"], []) -def test_jinja_namespace(hass): +def test_jinja_namespace(hass: HomeAssistant) -> None: """Test Jinja's namespace command can be used.""" test_template = template.Template( ( @@ -3509,7 +3538,7 @@ def test_jinja_namespace(hass): assert test_template.async_render() == "another value" -def test_state_with_unit(hass): +def test_state_with_unit(hass: HomeAssistant) -> None: """Test the state_with_unit property helper.""" hass.states.async_set("sensor.test", "23", {ATTR_UNIT_OF_MEASUREMENT: "beers"}) hass.states.async_set("sensor.test2", "wow") @@ -3533,7 +3562,7 @@ def test_state_with_unit(hass): assert tpl.async_render() == "" -def test_length_of_states(hass): +def test_length_of_states(hass: HomeAssistant) -> None: """Test fetching the length of states.""" hass.states.async_set("sensor.test", "23") hass.states.async_set("sensor.test2", "wow") @@ -3546,14 +3575,14 @@ def test_length_of_states(hass): assert tpl.async_render() == 2 -def test_render_complex_handling_non_template_values(hass): +def test_render_complex_handling_non_template_values(hass: HomeAssistant) -> None: """Test that we can render non-template fields.""" assert template.render_complex( {True: 1, False: template.Template("{{ hello }}", hass)}, {"hello": 2} ) == {True: 1, False: 2} -def test_urlencode(hass): +def test_urlencode(hass: HomeAssistant) -> None: """Test the urlencode method.""" tpl = template.Template( ("{% set dict = {'foo': 'x&y', 'bar': 42} %}{{ dict | urlencode }}"), @@ -3611,7 +3640,7 @@ def test_iif(hass: HomeAssistant) -> None: assert tpl.async_render() == "no" -async def test_cache_garbage_collection(): +async def test_cache_garbage_collection() -> None: """Test caching a template.""" template_string = ( "{% set dict = {'foo': 'x&y', 'bar': 42} %} {{ dict | urlencode }}" @@ -3642,7 +3671,7 @@ async def test_cache_garbage_collection(): ) # pylint: disable=protected-access -def test_is_template_string(): +def test_is_template_string() -> None: """Test is template string.""" assert template.is_template_string("{{ x }}") is True assert template.is_template_string("{% if x == 2 %}1{% else %}0{%end if %}") is True @@ -3651,7 +3680,7 @@ def test_is_template_string(): assert template.is_template_string("Some Text") is False -async def test_protected_blocked(hass): +async def test_protected_blocked(hass: HomeAssistant) -> None: """Test accessing __getattr__ produces a template error.""" tmp = template.Template('{{ states.__getattr__("any") }}', hass) with pytest.raises(TemplateError): @@ -3666,7 +3695,7 @@ async def test_protected_blocked(hass): tmp.async_render() -async def test_demo_template(hass): +async def test_demo_template(hass: HomeAssistant) -> None: """Test the demo template works as expected.""" hass.states.async_set( "sun.sun", @@ -3709,7 +3738,7 @@ For loop example getting 3 entity values: assert "sun" in result -async def test_slice_states(hass): +async def test_slice_states(hass: HomeAssistant) -> None: """Test iterating states with a slice.""" hass.states.async_set("sensor.test", "23") @@ -3720,7 +3749,7 @@ async def test_slice_states(hass): assert tpl.async_render() == "sensor.test" -async def test_lifecycle(hass): +async def test_lifecycle(hass: HomeAssistant) -> None: """Test that we limit template render info for lifecycle events.""" hass.states.async_set("sun.sun", "above", {"elevation": 50, "next_rising": "later"}) for i in range(2): @@ -3756,7 +3785,7 @@ async def test_lifecycle(hass): assert info.filter_lifecycle("sensor.removed") is True -async def test_template_timeout(hass): +async def test_template_timeout(hass: HomeAssistant) -> None: """Test to see if a template will timeout.""" for i in range(2): hass.states.async_set(f"sensor.sensor{i}", "on") @@ -3781,14 +3810,14 @@ async def test_template_timeout(hass): assert await tmp5.async_render_will_timeout(0.000001) is True -async def test_template_timeout_raise(hass): +async def test_template_timeout_raise(hass: HomeAssistant) -> None: """Test we can raise from.""" tmp2 = template.Template("{{ error_invalid + 1 }}", hass) with pytest.raises(TemplateError): assert await tmp2.async_render_will_timeout(3) is False -async def test_lights(hass): +async def test_lights(hass: HomeAssistant) -> None: """Test we can sort lights.""" tmpl = """ @@ -3818,7 +3847,7 @@ async def test_lights(hass): assert f"sensor{i}" in info.result() -async def test_template_errors(hass): +async def test_template_errors(hass: HomeAssistant) -> None: """Test template rendering wraps exceptions with TemplateError.""" with pytest.raises(TemplateError): @@ -3834,7 +3863,7 @@ async def test_template_errors(hass): template.Template("{{ utcnow() | random }}", hass).async_render() -async def test_state_attributes(hass): +async def test_state_attributes(hass: HomeAssistant) -> None: """Test state attributes.""" hass.states.async_set("sensor.test", "23") @@ -3882,7 +3911,7 @@ async def test_state_attributes(hass): tpl.async_render() -async def test_unavailable_states(hass): +async def test_unavailable_states(hass: HomeAssistant) -> None: """Test watching unavailable states.""" for i in range(10): @@ -3905,7 +3934,7 @@ async def test_unavailable_states(hass): assert tpl.async_render() == "light.none, light.unavailable, light.unknown" -async def test_legacy_templates(hass): +async def test_legacy_templates(hass: HomeAssistant) -> None: """Test if old template behavior works when legacy templates are enabled.""" hass.states.async_set("sensor.temperature", "12") @@ -3921,7 +3950,7 @@ async def test_legacy_templates(hass): ) -async def test_no_result_parsing(hass): +async def test_no_result_parsing(hass: HomeAssistant) -> None: """Test if templates results are not parsed.""" hass.states.async_set("sensor.temperature", "12") @@ -3943,14 +3972,14 @@ async def test_no_result_parsing(hass): ) -async def test_is_static_still_ast_evals(hass): +async def test_is_static_still_ast_evals(hass: HomeAssistant) -> None: """Test is_static still converts to native type.""" tpl = template.Template("[1, 2]", hass) assert tpl.is_static assert tpl.async_render() == [1, 2] -async def test_result_wrappers(hass): +async def test_result_wrappers(hass: HomeAssistant) -> None: """Test result wrappers.""" for text, native, orig_type, schema in ( ("[1, 2]", [1, 2], list, vol.Schema([int])), @@ -3973,7 +4002,7 @@ async def test_result_wrappers(hass): ) -async def test_parse_result(hass): +async def test_parse_result(hass: HomeAssistant) -> None: """Test parse result.""" for tpl, result in ( ('{{ "{{}}" }}', "{{}}"), @@ -4010,7 +4039,7 @@ async def test_undefined_variable(hass, caplog): ) -async def test_template_states_blocks_setitem(hass): +async def test_template_states_blocks_setitem(hass: HomeAssistant) -> None: """Test we cannot setitem on TemplateStates.""" hass.states.async_set("light.new", STATE_ON) state = hass.states.get("light.new") @@ -4019,7 +4048,7 @@ async def test_template_states_blocks_setitem(hass): template_state["any"] = "any" -async def test_template_states_can_serialize(hass): +async def test_template_states_can_serialize(hass: HomeAssistant) -> None: """Test TemplateState is serializable.""" hass.states.async_set("light.new", STATE_ON) state = hass.states.get("light.new") From f91e250e902fcfcc7b0de6853f5e5c0a354c18a3 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 23 Nov 2022 20:30:32 +0100 Subject: [PATCH 0656/1033] Fix TemplateError definition (#82570) --- homeassistant/exceptions.py | 7 +++++-- homeassistant/helpers/template.py | 2 +- tests/components/mqtt/test_trigger.py | 2 +- tests/test_exceptions.py | 20 +++++++++++++++++++- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/homeassistant/exceptions.py b/homeassistant/exceptions.py index 052d3de4768..4f2599aa2da 100644 --- a/homeassistant/exceptions.py +++ b/homeassistant/exceptions.py @@ -25,9 +25,12 @@ class NoEntitySpecifiedError(HomeAssistantError): class TemplateError(HomeAssistantError): """Error during template rendering.""" - def __init__(self, exception: Exception) -> None: + def __init__(self, exception: Exception | str) -> None: """Init the error.""" - super().__init__(f"{exception.__class__.__name__}: {exception}") + if isinstance(exception, str): + super().__init__(exception) + else: + super().__init__(f"{exception.__class__.__name__}: {exception}") @attr.s diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 87240d35c8f..3c76c96ec5b 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -918,7 +918,7 @@ def _state_generator( def _get_state_if_valid(hass: HomeAssistant, entity_id: str) -> TemplateState | None: state = hass.states.get(entity_id) if state is None and not valid_entity_id(entity_id): - raise TemplateError(f"Invalid entity ID '{entity_id}'") # type: ignore[arg-type] + raise TemplateError(f"Invalid entity ID '{entity_id}'") return _get_template_state_from_state(hass, entity_id, state) diff --git a/tests/components/mqtt/test_trigger.py b/tests/components/mqtt/test_trigger.py index 4c0a70707eb..bebf319d36f 100644 --- a/tests/components/mqtt/test_trigger.py +++ b/tests/components/mqtt/test_trigger.py @@ -194,7 +194,7 @@ async def test_non_allowed_templates(hass, calls, caplog): ) assert ( - "Got error 'TemplateError: str: Use of 'states' is not supported in limited templates' when setting up triggers" + "Got error 'TemplateError: Use of 'states' is not supported in limited templates' when setting up triggers" in caplog.text ) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index e353c0dd82d..acd11caece2 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -1,12 +1,17 @@ """Test to verify that Home Assistant exceptions work.""" +from __future__ import annotations + +import pytest + from homeassistant.exceptions import ( ConditionErrorContainer, ConditionErrorIndex, ConditionErrorMessage, + TemplateError, ) -def test_conditionerror_format(): +def test_conditionerror_format() -> None: """Test ConditionError stringifiers.""" error1 = ConditionErrorMessage("test", "A test error") assert str(error1) == "In 'test' condition: A test error" @@ -43,3 +48,16 @@ In 'box' (item 2 of 2): == """In 'box': In 'test' condition: A test error""" ) + + +@pytest.mark.parametrize( + "arg, expected", + [ + ("message", "message"), + (Exception("message"), "Exception: message"), + ], +) +def test_template_message(arg: str | Exception, expected: str) -> None: + """Ensure we can create TemplateError.""" + template_error = TemplateError(arg) + assert str(template_error) == expected From f1ffb25d997f67a3974730a89a28b4a3078ffe60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Wed, 23 Nov 2022 20:32:23 +0100 Subject: [PATCH 0657/1033] Add Airzone DHCP discovery support (#82339) --- .../components/airzone/config_flow.py | 93 +++++++- .../components/airzone/manifest.json | 7 +- homeassistant/components/airzone/strings.json | 13 +- .../components/airzone/translations/en.json | 13 +- homeassistant/generated/dhcp.py | 4 + tests/components/airzone/test_config_flow.py | 209 +++++++++++++++++- tests/components/airzone/util.py | 5 + 7 files changed, 332 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/airzone/config_flow.py b/homeassistant/components/airzone/config_flow.py index 89a2d7f1f9e..7a8fdbf884b 100644 --- a/homeassistant/components/airzone/config_flow.py +++ b/homeassistant/components/airzone/config_flow.py @@ -1,6 +1,7 @@ """Config flow for Airzone.""" from __future__ import annotations +import logging from typing import Any from aioairzone.const import DEFAULT_PORT, DEFAULT_SYSTEM_ID @@ -9,13 +10,16 @@ from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions import voluptuous as vol from homeassistant import config_entries +from homeassistant.components import dhcp from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT -from homeassistant.data_entry_flow import FlowResult +from homeassistant.data_entry_flow import AbortFlow, FlowResult from homeassistant.helpers import aiohttp_client from homeassistant.helpers.device_registry import format_mac from .const import DOMAIN +_LOGGER = logging.getLogger(__name__) + CONFIG_SCHEMA = vol.Schema( { vol.Required(CONF_HOST): str, @@ -29,9 +33,17 @@ SYSTEM_ID_SCHEMA = CONFIG_SCHEMA.extend( ) +def short_mac(addr: str) -> str: + """Convert MAC address to short address.""" + return addr.replace(":", "")[-4:].upper() + + class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle config flow for an Airzone device.""" + _discovered_ip: str | None = None + _discovered_mac: str | None = None + async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -60,7 +72,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors["base"] = "cannot_connect" else: if mac: - await self.async_set_unique_id(format_mac(mac)) + await self.async_set_unique_id( + format_mac(mac), raise_on_progress=False + ) self._abort_if_unique_id_configured( updates={ CONF_HOST: user_input[CONF_HOST], @@ -76,3 +90,78 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): data_schema=data_schema, errors=errors, ) + + async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult: + """Handle DHCP discovery.""" + self._discovered_ip = discovery_info.ip + self._discovered_mac = discovery_info.macaddress + + _LOGGER.debug( + "DHCP discovery detected Airzone WebServer: %s", self._discovered_mac + ) + + self._async_abort_entries_match({CONF_HOST: self._discovered_ip}) + + await self.async_set_unique_id(format_mac(self._discovered_mac)) + self._abort_if_unique_id_configured() + + options = ConnectionOptions(self._discovered_ip) + airzone = AirzoneLocalApi( + aiohttp_client.async_get_clientsession(self.hass), options + ) + try: + await airzone.get_version() + except AirzoneError as err: + raise AbortFlow("cannot_connect") from err + + return await self.async_step_discovered_connection() + + async def async_step_discovered_connection( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Confirm discovery.""" + assert self._discovered_ip is not None + assert self._discovered_mac is not None + + errors = {} + base_schema = {vol.Required(CONF_PORT, default=DEFAULT_PORT): int} + + if user_input is not None: + airzone = AirzoneLocalApi( + aiohttp_client.async_get_clientsession(self.hass), + ConnectionOptions( + self._discovered_ip, + user_input[CONF_PORT], + user_input.get(CONF_ID, DEFAULT_SYSTEM_ID), + ), + ) + + try: + mac = await airzone.validate() + except InvalidSystem: + base_schema[vol.Required(CONF_ID, default=1)] = int + errors[CONF_ID] = "invalid_system_id" + except AirzoneError: + errors["base"] = "cannot_connect" + else: + user_input[CONF_HOST] = self._discovered_ip + + if mac is None: + mac = self._discovered_mac + + await self.async_set_unique_id(format_mac(mac)) + self._abort_if_unique_id_configured( + updates={ + CONF_HOST: user_input[CONF_HOST], + CONF_PORT: user_input[CONF_PORT], + } + ) + + title = f"Airzone {short_mac(mac)}" + return self.async_create_entry(title=title, data=user_input) + + return self.async_show_form( + step_id="discovered_connection", + data_schema=vol.Schema(base_schema), + errors=errors, + ) diff --git a/homeassistant/components/airzone/manifest.json b/homeassistant/components/airzone/manifest.json index 25b853db290..142ace5e70b 100644 --- a/homeassistant/components/airzone/manifest.json +++ b/homeassistant/components/airzone/manifest.json @@ -6,5 +6,10 @@ "requirements": ["aioairzone==0.5.1"], "codeowners": ["@Noltari"], "iot_class": "local_polling", - "loggers": ["aioairzone"] + "loggers": ["aioairzone"], + "dhcp": [ + { + "macaddress": "E84F25*" + } + ] } diff --git a/homeassistant/components/airzone/strings.json b/homeassistant/components/airzone/strings.json index 855b5615482..306e63da36c 100644 --- a/homeassistant/components/airzone/strings.json +++ b/homeassistant/components/airzone/strings.json @@ -8,12 +8,19 @@ "invalid_system_id": "Invalid Airzone System ID" }, "step": { - "user": { + "discovered_connection": { "data": { + "id": "[%key:component::airzone::config::step::user::data::id%]", "host": "[%key:common::config_flow::data::host%]", "port": "[%key:common::config_flow::data::port%]" - }, - "description": "Set up Airzone integration." + } + }, + "user": { + "data": { + "id": "System ID", + "host": "[%key:common::config_flow::data::host%]", + "port": "[%key:common::config_flow::data::port%]" + } } } } diff --git a/homeassistant/components/airzone/translations/en.json b/homeassistant/components/airzone/translations/en.json index 85da81afd55..862887818d6 100644 --- a/homeassistant/components/airzone/translations/en.json +++ b/homeassistant/components/airzone/translations/en.json @@ -8,12 +8,19 @@ "invalid_system_id": "Invalid Airzone System ID" }, "step": { - "user": { + "discovered_connection": { "data": { + "id": "System ID", "host": "Host", "port": "Port" - }, - "description": "Set up Airzone integration." + } + }, + "user": { + "data": { + "id": "System ID", + "host": "Host", + "port": "Port" + } } } } diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index 7aaa183c114..dd156b29f22 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -6,6 +6,10 @@ To update, run python3 -m script.hassfest from __future__ import annotations DHCP: list[dict[str, str | bool]] = [ + { + "domain": "airzone", + "macaddress": "E84F25*", + }, { "domain": "august", "hostname": "connect", diff --git a/tests/components/airzone/test_config_flow.py b/tests/components/airzone/test_config_flow.py index 32eaade93ee..a4b4c310f38 100644 --- a/tests/components/airzone/test_config_flow.py +++ b/tests/components/airzone/test_config_flow.py @@ -2,7 +2,7 @@ from unittest.mock import patch -from aioairzone.const import API_SYSTEMS +from aioairzone.const import API_MAC, API_SYSTEMS from aioairzone.exceptions import ( AirzoneError, InvalidMethod, @@ -10,16 +10,28 @@ from aioairzone.exceptions import ( SystemOutOfRange, ) -from homeassistant import data_entry_flow +from homeassistant import config_entries, data_entry_flow +from homeassistant.components import dhcp +from homeassistant.components.airzone.config_flow import short_mac from homeassistant.components.airzone.const import DOMAIN from homeassistant.config_entries import SOURCE_USER, ConfigEntryState from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT from homeassistant.core import HomeAssistant -from .util import CONFIG, CONFIG_ID1, HVAC_MOCK, HVAC_WEBSERVER_MOCK +from .util import CONFIG, CONFIG_ID1, HVAC_MOCK, HVAC_VERSION_MOCK, HVAC_WEBSERVER_MOCK from tests.common import MockConfigEntry +DHCP_SERVICE_INFO = dhcp.DhcpServiceInfo( + hostname="airzone", + ip="192.168.1.100", + macaddress="E84F25000000", +) + +TEST_ID = 1 +TEST_IP = DHCP_SERVICE_INFO.ip +TEST_PORT = 3000 + async def test_form(hass: HomeAssistant) -> None: """Test that the form is served with valid input.""" @@ -145,3 +157,194 @@ async def test_connection_error(hass: HomeAssistant): ) assert result["errors"] == {"base": "cannot_connect"} + + +async def test_dhcp_flow(hass: HomeAssistant) -> None: + """Test that DHCP discovery works.""" + + with patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_version", + return_value=HVAC_VERSION_MOCK, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + data=DHCP_SERVICE_INFO, + context={"source": config_entries.SOURCE_DHCP}, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "discovered_connection" + + with patch( + "homeassistant.components.airzone.async_setup_entry", + return_value=True, + ) as mock_setup_entry, patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_hvac", + return_value=HVAC_MOCK, + ), patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_hvac_systems", + side_effect=SystemOutOfRange, + ), patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_webserver", + return_value=HVAC_WEBSERVER_MOCK, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_PORT: TEST_PORT, + }, + ) + + assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result2["data"] == { + CONF_HOST: TEST_IP, + CONF_PORT: TEST_PORT, + } + + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_dhcp_flow_error(hass: HomeAssistant) -> None: + """Test that DHCP discovery fails.""" + + with patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_version", + side_effect=AirzoneError, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + data=DHCP_SERVICE_INFO, + context={"source": config_entries.SOURCE_DHCP}, + ) + + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "cannot_connect" + + +async def test_dhcp_connection_error(hass: HomeAssistant): + """Test DHCP connection to host error.""" + + with patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_version", + return_value=HVAC_VERSION_MOCK, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + data=DHCP_SERVICE_INFO, + context={"source": config_entries.SOURCE_DHCP}, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "discovered_connection" + + with patch( + "homeassistant.components.airzone.AirzoneLocalApi.validate", + side_effect=AirzoneError, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_PORT: 3001, + }, + ) + + assert result["errors"] == {"base": "cannot_connect"} + + with patch( + "homeassistant.components.airzone.async_setup_entry", + return_value=True, + ) as mock_setup_entry, patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_hvac", + return_value=HVAC_MOCK, + ), patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_hvac_systems", + side_effect=SystemOutOfRange, + ), patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_webserver", + return_value=HVAC_WEBSERVER_MOCK, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_PORT: TEST_PORT, + }, + ) + + await hass.async_block_till_done() + + conf_entries = hass.config_entries.async_entries(DOMAIN) + entry = conf_entries[0] + assert entry.state is ConfigEntryState.LOADED + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == f"Airzone {short_mac(HVAC_WEBSERVER_MOCK[API_MAC])}" + assert result["data"][CONF_HOST] == TEST_IP + assert result["data"][CONF_PORT] == TEST_PORT + + mock_setup_entry.assert_called_once() + + +async def test_dhcp_invalid_system_id(hass: HomeAssistant) -> None: + """Test Invalid System ID 0.""" + + with patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_version", + return_value=HVAC_VERSION_MOCK, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + data=DHCP_SERVICE_INFO, + context={"source": config_entries.SOURCE_DHCP}, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "discovered_connection" + + with patch( + "homeassistant.components.airzone.async_setup_entry", + return_value=True, + ) as mock_setup_entry, patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_hvac", + side_effect=InvalidSystem, + ) as mock_hvac, patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_hvac_systems", + side_effect=SystemOutOfRange, + ), patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_webserver", + side_effect=InvalidMethod, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_PORT: TEST_PORT, + }, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "discovered_connection" + assert result["errors"] == {CONF_ID: "invalid_system_id"} + + mock_hvac.return_value = HVAC_MOCK[API_SYSTEMS][0] + mock_hvac.side_effect = None + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_PORT: TEST_PORT, + CONF_ID: TEST_ID, + }, + ) + + await hass.async_block_till_done() + + conf_entries = hass.config_entries.async_entries(DOMAIN) + entry = conf_entries[0] + assert entry.state is ConfigEntryState.LOADED + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == f"Airzone {short_mac(DHCP_SERVICE_INFO.macaddress)}" + assert result["data"][CONF_HOST] == TEST_IP + assert result["data"][CONF_PORT] == TEST_PORT + assert result["data"][CONF_ID] == TEST_ID + + mock_setup_entry.assert_called_once() diff --git a/tests/components/airzone/util.py b/tests/components/airzone/util.py index 6b81c493eb6..5bed0fc1d99 100644 --- a/tests/components/airzone/util.py +++ b/tests/components/airzone/util.py @@ -30,6 +30,7 @@ from aioairzone.const import ( API_THERMOS_RADIO, API_THERMOS_TYPE, API_UNITS, + API_VERSION, API_WIFI_CHANNEL, API_WIFI_RSSI, API_ZONE_ID, @@ -191,6 +192,10 @@ HVAC_SYSTEMS_MOCK = { ] } +HVAC_VERSION_MOCK = { + API_VERSION: "1.62", +} + HVAC_WEBSERVER_MOCK = { API_MAC: "11:22:33:44:55:66", API_WIFI_CHANNEL: 6, From 25c7754edb6bdcf2a93a1d8064e8824c11313928 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Wed, 23 Nov 2022 20:36:17 +0100 Subject: [PATCH 0658/1033] Upgrade arcam_fmj to entity naming (#82586) --- .../components/arcam_fmj/media_player.py | 26 +++++++------------ .../components/arcam_fmj/test_media_player.py | 6 ++--- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/arcam_fmj/media_player.py b/homeassistant/components/arcam_fmj/media_player.py index 7a15a50e375..08a65b71193 100644 --- a/homeassistant/components/arcam_fmj/media_player.py +++ b/homeassistant/components/arcam_fmj/media_player.py @@ -61,6 +61,7 @@ class ArcamFmj(MediaPlayerEntity): """Representation of a media device.""" _attr_should_poll = False + _attr_has_entity_name = True def __init__( self, @@ -70,9 +71,7 @@ class ArcamFmj(MediaPlayerEntity): ) -> None: """Initialize device.""" self._state = state - self._device_name = device_name - self._attr_name = f"{device_name} - Zone: {state.zn}" - self._uuid = uuid + self._attr_name = f"Zone {state.zn}" self._attr_supported_features = ( MediaPlayerEntityFeature.SELECT_SOURCE | MediaPlayerEntityFeature.PLAY_MEDIA @@ -87,6 +86,14 @@ class ArcamFmj(MediaPlayerEntity): self._attr_supported_features |= MediaPlayerEntityFeature.SELECT_SOUND_MODE self._attr_unique_id = f"{uuid}-{state.zn}" self._attr_entity_registry_enabled_default = state.zn == 1 + self._attr_device_info = DeviceInfo( + identifiers={ + (DOMAIN, uuid), + }, + manufacturer="Arcam", + model="Arcam FMJ AVR", + name=device_name, + ) @property def state(self) -> MediaPlayerState: @@ -95,19 +102,6 @@ class ArcamFmj(MediaPlayerEntity): return MediaPlayerState.ON return MediaPlayerState.OFF - @property - def device_info(self) -> DeviceInfo: - """Return a device description for device registry.""" - return DeviceInfo( - identifiers={ - (DOMAIN, self._uuid), - (DOMAIN, self._state.client.host, self._state.client.port), # type: ignore[arg-type] - }, - manufacturer="Arcam", - model="Arcam FMJ AVR", - name=self._device_name, - ) - async def async_added_to_hass(self) -> None: """Once registered, add listener for events.""" await self._state.start() diff --git a/tests/components/arcam_fmj/test_media_player.py b/tests/components/arcam_fmj/test_media_player.py index 19a0f456d7f..6a53b70d9e7 100644 --- a/tests/components/arcam_fmj/test_media_player.py +++ b/tests/components/arcam_fmj/test_media_player.py @@ -20,7 +20,7 @@ from homeassistant.const import ( ATTR_NAME, ) -from .conftest import MOCK_HOST, MOCK_NAME, MOCK_PORT, MOCK_UUID +from .conftest import MOCK_HOST, MOCK_UUID MOCK_TURN_ON = { "service": "switch.turn_on", @@ -41,7 +41,6 @@ async def test_properties(player, state): ATTR_NAME: f"Arcam FMJ ({MOCK_HOST})", ATTR_IDENTIFIERS: { ("arcam_fmj", MOCK_UUID), - ("arcam_fmj", MOCK_HOST, MOCK_PORT), }, ATTR_MODEL: "Arcam FMJ AVR", ATTR_MANUFACTURER: "Arcam", @@ -102,7 +101,8 @@ async def test_mute_volume(player, state, mute): async def test_name(player): """Test name.""" - assert player.name == f"{MOCK_NAME} - Zone: 1" + data = await update(player) + assert data.attributes["friendly_name"] == "Zone 1" async def test_update(player, state): From 8bd41253902086df3270f3525eb44dfcfc73995e Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Wed, 23 Nov 2022 20:53:28 +0100 Subject: [PATCH 0659/1033] Test corrections noticed during pytest upgrade (#82579) * Withing trigger a call to coordinator after init * Stop discovery task on STOP event * Stop dsmr connection task on STOP * Use autospec in modem_serial tests close on AioSerial is non async, and should not be mocked as a coroutine * Make sure responder is specced correctly tests/components/emulated_hue/test_init.py::test_setup_works /Users/joakim/src/hass/home-assistant/homeassistant/components/emulated_hue/__init__.py:119: RuntimeWarning: coroutine 'AsyncMockMixin._execute_mock_call' was never awaited protocol.close() * Don't assume Pymodbus is the only thing logging * Use gather instead of wait ``` homeassistant/components/homeassistant/__init__.py:208: DeprecationWarning: The explicit passing of coroutine objects to asyncio.wait() is deprecated since Python 3.8, and scheduled for removal in Python 3.11. ``` * Increase wait time for automation tests Assuming exit within 1 seconds is unreasonable on a potentially loaded test machine. * Avoid changing app state after startup --- homeassistant/components/dsmr/sensor.py | 11 ++++++++++- homeassistant/components/homeassistant/__init__.py | 2 +- homeassistant/components/http/ban.py | 5 ++--- homeassistant/components/lifx/__init__.py | 13 +++++++++++-- tests/components/automation/test_init.py | 4 ++-- tests/components/emulated_hue/test_init.py | 4 ++++ tests/components/modbus/test_init.py | 2 +- tests/components/modem_callerid/test_init.py | 4 ++-- tests/components/withings/test_init.py | 4 ++++ 9 files changed, 37 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/dsmr/sensor.py b/homeassistant/components/dsmr/sensor.py index 02a14601cc3..5cbd3e14be0 100644 --- a/homeassistant/components/dsmr/sensor.py +++ b/homeassistant/components/dsmr/sensor.py @@ -30,7 +30,7 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_STOP, VOLUME_CUBIC_METERS, ) -from homeassistant.core import CoreState, HomeAssistant, callback +from homeassistant.core import CoreState, Event, HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import EventType, StateType @@ -505,6 +505,15 @@ async def async_setup_entry( # Can't be hass.async_add_job because job runs forever task = asyncio.create_task(connect_and_reconnect()) + @callback + async def _async_stop(_: Event) -> None: + task.cancel() + + # Make sure task is cancelled on shutdown (or tests complete) + entry.async_on_unload( + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop) + ) + # Save the task to be able to cancel it when unloading hass.data[DOMAIN][entry.entry_id][DATA_TASK] = task diff --git a/homeassistant/components/homeassistant/__init__.py b/homeassistant/components/homeassistant/__init__.py index 81d79b03c10..7e557e0808e 100644 --- a/homeassistant/components/homeassistant/__init__.py +++ b/homeassistant/components/homeassistant/__init__.py @@ -205,7 +205,7 @@ async def async_setup(hass: ha.HomeAssistant, config: ConfigType) -> bool: # no ] if tasks: - await asyncio.wait(tasks) + await asyncio.gather(*tasks) async_register_admin_service( hass, ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP, async_handle_core_service diff --git a/homeassistant/components/http/ban.py b/homeassistant/components/http/ban.py index d2f5f9d8ba5..7be8634212a 100644 --- a/homeassistant/components/http/ban.py +++ b/homeassistant/components/http/ban.py @@ -51,12 +51,11 @@ def setup_bans(hass: HomeAssistant, app: Application, login_threshold: int) -> N app.middlewares.append(ban_middleware) app[KEY_FAILED_LOGIN_ATTEMPTS] = defaultdict(int) app[KEY_LOGIN_THRESHOLD] = login_threshold + app[KEY_BAN_MANAGER] = IpBanManager(hass) async def ban_startup(app: Application) -> None: """Initialize bans when app starts up.""" - ban_manager = IpBanManager(hass) - await ban_manager.async_load() - app[KEY_BAN_MANAGER] = ban_manager + await app[KEY_BAN_MANAGER].async_load() app.on_startup.append(ban_startup) diff --git a/homeassistant/components/lifx/__init__.py b/homeassistant/components/lifx/__init__.py index 786ddd6abbf..abe35c0a0d8 100644 --- a/homeassistant/components/lifx/__init__.py +++ b/homeassistant/components/lifx/__init__.py @@ -17,9 +17,10 @@ from homeassistant.const import ( CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STARTED, + EVENT_HOMEASSISTANT_STOP, Platform, ) -from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback +from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback from homeassistant.exceptions import ConfigEntryNotReady import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import async_call_later, async_track_time_interval @@ -163,7 +164,15 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: We do not want the discovery task to block startup. """ - asyncio.create_task(discovery_manager.async_discovery()) + task = asyncio.create_task(discovery_manager.async_discovery()) + + @callback + def _async_stop(_: Event) -> None: + if not task.done(): + task.cancel() + + # Task must be shut down when home assistant is closing + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop) # Let the system settle a bit before starting discovery # to reduce the risk we miss devices because the event diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index 742ae85ed68..1ce6cb616da 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -2140,12 +2140,12 @@ async def test_recursive_automation_starting_script( hass.bus.async_listen("automation_triggered", async_automation_triggered) hass.bus.async_fire("trigger_automation") - await asyncio.wait_for(script_done_event.wait(), 1) + await asyncio.wait_for(script_done_event.wait(), 10) # Trigger 1st stage script shutdown hass.state = CoreState.stopping hass.bus.async_fire("homeassistant_stop") - await asyncio.wait_for(stop_scripts_at_shutdown_called.wait(), 1) + await asyncio.wait_for(stop_scripts_at_shutdown_called.wait(), 10) # Trigger 2nd stage script shutdown async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=60)) diff --git a/tests/components/emulated_hue/test_init.py b/tests/components/emulated_hue/test_init.py index 408a44cda00..cfe4dd3a7bb 100644 --- a/tests/components/emulated_hue/test_init.py +++ b/tests/components/emulated_hue/test_init.py @@ -8,6 +8,7 @@ from homeassistant.components.emulated_hue.config import ( SAVE_DELAY, Config, ) +from homeassistant.components.emulated_hue.upnp import UPNPResponderProtocol from homeassistant.const import EVENT_HOMEASSISTANT_START from homeassistant.setup import async_setup_component from homeassistant.util import utcnow @@ -127,6 +128,9 @@ async def test_setup_works(hass): ) as mock_create_upnp_datagram_endpoint, patch( "homeassistant.components.emulated_hue.async_get_source_ip" ): + mock_create_upnp_datagram_endpoint.return_value = AsyncMock( + spec=UPNPResponderProtocol + ) assert await async_setup_component(hass, "emulated_hue", {}) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() diff --git a/tests/components/modbus/test_init.py b/tests/components/modbus/test_init.py index e238ecc6170..73b1f9743c2 100644 --- a/tests/components/modbus/test_init.py +++ b/tests/components/modbus/test_init.py @@ -529,7 +529,7 @@ async def test_pb_service_write( data[do_write[DATA]], ) if do_return[DATA]: - assert caplog.messages[-1].startswith("Pymodbus:") + assert any(message.startswith("Pymodbus:") for message in caplog.messages) @pytest.fixture(name="mock_modbus_read_pymodbus") diff --git a/tests/components/modem_callerid/test_init.py b/tests/components/modem_callerid/test_init.py index 0465fb24a07..de49e229ef1 100644 --- a/tests/components/modem_callerid/test_init.py +++ b/tests/components/modem_callerid/test_init.py @@ -1,5 +1,5 @@ """Test Modem Caller ID integration.""" -from unittest.mock import AsyncMock, patch +from unittest.mock import patch from phone_modem import exceptions @@ -20,7 +20,7 @@ async def test_setup_entry(hass: HomeAssistant): data={CONF_DEVICE: com_port().device}, ) entry.add_to_hass(hass) - with patch("aioserial.AioSerial", return_value=AsyncMock()), patch( + with patch("aioserial.AioSerial", autospec=True), patch( "homeassistant.components.modem_callerid.PhoneModem._get_response", return_value="OK", ), patch("phone_modem.PhoneModem._modem_sm"): diff --git a/tests/components/withings/test_init.py b/tests/components/withings/test_init.py index 83e8a622e78..70c4d177e59 100644 --- a/tests/components/withings/test_init.py +++ b/tests/components/withings/test_init.py @@ -177,6 +177,10 @@ async def test_set_config_unique_id( spec=DataUpdateCoordinator ) data_manager.poll_data_update_coordinator.last_update_success = True + data_manager.subscription_update_coordinator = MagicMock( + spec=DataUpdateCoordinator + ) + data_manager.subscription_update_coordinator.last_update_success = True mock.return_value = data_manager config_entry.add_to_hass(hass) From f43f0c4bcc070664519d14485cca1a462014a597 Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Wed, 23 Nov 2022 20:54:16 +0100 Subject: [PATCH 0660/1033] Use assignment expression for alexa init (#81242) --- homeassistant/components/alexa/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/alexa/__init__.py b/homeassistant/components/alexa/__init__.py index d28ff5dc804..0008ba26f8a 100644 --- a/homeassistant/components/alexa/__init__.py +++ b/homeassistant/components/alexa/__init__.py @@ -1,4 +1,8 @@ """Support for Alexa skill service end point.""" +from __future__ import annotations + +from typing import Any + import voluptuous as vol from homeassistant.const import ( @@ -87,18 +91,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: config = config[DOMAIN] - flash_briefings_config = config.get(CONF_FLASH_BRIEFINGS) - intent.async_setup(hass) - if flash_briefings_config: + if flash_briefings_config := config.get(CONF_FLASH_BRIEFINGS): flash_briefings.async_setup(hass, flash_briefings_config) - try: - smart_home_config = config[CONF_SMART_HOME] - except KeyError: - pass - else: + # smart_home being absent is not the same as smart_home being None + if CONF_SMART_HOME in config: + smart_home_config: dict[str, Any] | None = config[CONF_SMART_HOME] smart_home_config = smart_home_config or SMART_HOME_SCHEMA({}) await smart_home_http.async_setup(hass, smart_home_config) From d7f0b904d078e132146723df4a1bd16bccba8de0 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 23 Nov 2022 21:50:45 +0100 Subject: [PATCH 0661/1033] Add type to template function (#82564) --- homeassistant/helpers/template.py | 44 ++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 3c76c96ec5b..fe073dcb742 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -20,7 +20,7 @@ import statistics from struct import error as StructError, pack, unpack_from import sys from types import CodeType -from typing import Any, NoReturn, TypeVar, cast, overload +from typing import Any, Literal, NoReturn, TypeVar, cast, overload from urllib.parse import urlencode as urllib_urlencode import weakref @@ -368,7 +368,7 @@ class Template: return try: - self._compiled_code = self._env.compile(self.template) # type: ignore[no-untyped-call] + self._compiled_code = self._env.compile(self.template) except jinja2.TemplateError as err: raise TemplateError(err) from err @@ -2183,7 +2183,36 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment): return super().is_safe_attribute(obj, attr, value) - def compile(self, source, name=None, filename=None, raw=False, defer_init=False): + @overload + def compile( # type: ignore[misc] + self, + source: str | jinja2.nodes.Template, + name: str | None = None, + filename: str | None = None, + raw: Literal[False] = False, + defer_init: bool = False, + ) -> CodeType: + ... + + @overload + def compile( + self, + source: str | jinja2.nodes.Template, + name: str | None = None, + filename: str | None = None, + raw: Literal[True] = ..., + defer_init: bool = False, + ) -> str: + ... + + def compile( + self, + source: str | jinja2.nodes.Template, + name: str | None = None, + filename: str | None = None, + raw: bool = False, + defer_init: bool = False, + ) -> CodeType | str: """Compile the template.""" if ( name is not None @@ -2194,8 +2223,15 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment): # If there are any non-default keywords args, we do # not cache. In prodution we currently do not have # any instance of this. - return super().compile(source, name, filename, raw, defer_init) + return super().compile( # type: ignore[no-any-return,call-overload] + source, + name, + filename, + raw, + defer_init, + ) + cached: CodeType | str | None if (cached := self.template_cache.get(source)) is None: cached = self.template_cache[source] = super().compile(source) From 845bcf3f7d48fd1bc7dd9c6f4a074879e36a52c9 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Thu, 24 Nov 2022 00:08:43 +0100 Subject: [PATCH 0662/1033] Translate `PowerOff` state of `select` entity in Harmony integration (#77491) * Add custom device_class for select entity * Update tests * Make the state PowerOff translatable * Update strings.select.json file * add select.en.json Co-authored-by: J. Nick Koston --- homeassistant/components/harmony/select.py | 12 ++++++++++-- homeassistant/components/harmony/strings.select.json | 7 +++++++ .../components/harmony/translations/select.en.json | 7 +++++++ tests/components/harmony/test_select.py | 10 ++++++---- 4 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 homeassistant/components/harmony/strings.select.json create mode 100644 homeassistant/components/harmony/translations/select.en.json diff --git a/homeassistant/components/harmony/select.py b/homeassistant/components/harmony/select.py index 3728c4b17a4..a3f059d2c00 100644 --- a/homeassistant/components/harmony/select.py +++ b/homeassistant/components/harmony/select.py @@ -16,6 +16,8 @@ from .subscriber import HarmonyCallback _LOGGER = logging.getLogger(__name__) +TRANSLATABLE_POWER_OFF = "power_off" + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback @@ -31,6 +33,8 @@ async def async_setup_entry( class HarmonyActivitySelect(HarmonyEntity, SelectEntity): """Select representation of a Harmony activities.""" + _attr_device_class = f"{DOMAIN}__activities" + def __init__(self, name: str, data: HarmonyData) -> None: """Initialize HarmonyActivitySelect class.""" super().__init__(data=data) @@ -42,23 +46,27 @@ class HarmonyActivitySelect(HarmonyEntity, SelectEntity): @property def icon(self) -> str: """Return a representative icon.""" - if not self.available or self.current_option == ACTIVITY_POWER_OFF: + if not self.available or self.current_option == TRANSLATABLE_POWER_OFF: return "mdi:remote-tv-off" return "mdi:remote-tv" @property def options(self) -> list[str]: """Return a set of selectable options.""" - return [ACTIVITY_POWER_OFF] + sorted(self._data.activity_names) + return [TRANSLATABLE_POWER_OFF] + sorted(self._data.activity_names) @property def current_option(self) -> str | None: """Return the current activity.""" _, activity_name = self._data.current_activity + if activity_name == ACTIVITY_POWER_OFF: + return TRANSLATABLE_POWER_OFF return activity_name async def async_select_option(self, option: str) -> None: """Change the current activity.""" + if option == TRANSLATABLE_POWER_OFF: + await self._data.async_start_activity(ACTIVITY_POWER_OFF) await self._data.async_start_activity(option) async def async_added_to_hass(self) -> None: diff --git a/homeassistant/components/harmony/strings.select.json b/homeassistant/components/harmony/strings.select.json new file mode 100644 index 00000000000..5dbdf1a1c3d --- /dev/null +++ b/homeassistant/components/harmony/strings.select.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Power Off" + } + } +} diff --git a/homeassistant/components/harmony/translations/select.en.json b/homeassistant/components/harmony/translations/select.en.json new file mode 100644 index 00000000000..5dbdf1a1c3d --- /dev/null +++ b/homeassistant/components/harmony/translations/select.en.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Power Off" + } + } +} diff --git a/tests/components/harmony/test_select.py b/tests/components/harmony/test_select.py index 4607f035893..60bb85ed0c3 100644 --- a/tests/components/harmony/test_select.py +++ b/tests/components/harmony/test_select.py @@ -9,6 +9,7 @@ from homeassistant.components.select import ( SERVICE_SELECT_OPTION, ) from homeassistant.const import ( + ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, @@ -68,11 +69,12 @@ async def test_options(mock_hc, hass, mock_write_config): # assert we have all options state = hass.states.get(ENTITY_SELECT) assert state.attributes.get("options") == [ - "PowerOff", + "power_off", "Nile-TV", "Play Music", "Watch TV", ] + assert state.attributes.get(ATTR_DEVICE_CLASS) == "harmony__activities" async def test_select_option(mock_hc, hass, mock_write_config): @@ -94,10 +96,10 @@ async def test_select_option(mock_hc, hass, mock_write_config): assert hass.states.is_state(ENTITY_REMOTE, STATE_ON) assert hass.states.is_state(ENTITY_SELECT, "Play Music") - # turn off harmony by selecting PowerOff activity - await _select_option_and_wait(hass, ENTITY_SELECT, "PowerOff") + # turn off harmony by selecting power_off activity + await _select_option_and_wait(hass, ENTITY_SELECT, "power_off") assert hass.states.is_state(ENTITY_REMOTE, STATE_OFF) - assert hass.states.is_state(ENTITY_SELECT, "PowerOff") + assert hass.states.is_state(ENTITY_SELECT, "power_off") async def _select_option_and_wait(hass, entity, option): From 33d391a110676842a24a9f5f2e47185a6d54beff Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 24 Nov 2022 00:26:32 +0000 Subject: [PATCH 0663/1033] [ci skip] Translation update --- .../components/airzone/translations/ca.json | 8 +++ .../components/airzone/translations/en.json | 7 ++- .../components/airzone/translations/es.json | 8 +++ .../components/airzone/translations/pl.json | 8 +++ .../components/anthemav/translations/ca.json | 6 -- .../components/anthemav/translations/de.json | 6 -- .../components/anthemav/translations/el.json | 6 -- .../components/anthemav/translations/en.json | 6 -- .../components/anthemav/translations/es.json | 6 -- .../components/anthemav/translations/et.json | 6 -- .../components/anthemav/translations/fr.json | 5 -- .../components/anthemav/translations/hu.json | 6 -- .../components/anthemav/translations/id.json | 6 -- .../components/anthemav/translations/it.json | 6 -- .../components/anthemav/translations/ja.json | 6 -- .../components/anthemav/translations/nl.json | 5 -- .../components/anthemav/translations/no.json | 6 -- .../components/anthemav/translations/pl.json | 6 -- .../anthemav/translations/pt-BR.json | 6 -- .../components/anthemav/translations/ru.json | 6 -- .../components/anthemav/translations/sv.json | 6 -- .../components/anthemav/translations/tr.json | 6 -- .../anthemav/translations/zh-Hant.json | 6 -- .../components/awair/translations/bg.json | 12 ---- .../components/awair/translations/ca.json | 15 ----- .../components/awair/translations/cs.json | 16 ----- .../components/awair/translations/de.json | 15 ----- .../components/awair/translations/el.json | 15 ----- .../components/awair/translations/en.json | 15 ----- .../components/awair/translations/es-419.json | 3 - .../components/awair/translations/es.json | 15 ----- .../components/awair/translations/et.json | 15 ----- .../components/awair/translations/fi.json | 5 -- .../components/awair/translations/fr.json | 15 ----- .../components/awair/translations/he.json | 13 ----- .../components/awair/translations/hu.json | 15 ----- .../components/awair/translations/id.json | 15 ----- .../components/awair/translations/it.json | 15 ----- .../components/awair/translations/ja.json | 15 ----- .../components/awair/translations/ko.json | 12 ---- .../components/awair/translations/lb.json | 12 ---- .../components/awair/translations/nl.json | 17 ------ .../components/awair/translations/no.json | 15 ----- .../components/awair/translations/pl.json | 15 ----- .../components/awair/translations/pt-BR.json | 15 ----- .../components/awair/translations/pt.json | 14 ----- .../components/awair/translations/ru.json | 15 ----- .../components/awair/translations/sk.json | 17 ------ .../components/awair/translations/sv.json | 15 ----- .../components/awair/translations/tr.json | 15 ----- .../components/awair/translations/uk.json | 12 ---- .../awair/translations/zh-Hant.json | 15 ----- .../components/bluetooth/translations/bg.json | 4 -- .../components/bluetooth/translations/ca.json | 7 +-- .../components/bluetooth/translations/cs.json | 3 - .../components/bluetooth/translations/de.json | 7 +-- .../components/bluetooth/translations/el.json | 7 +-- .../components/bluetooth/translations/en.json | 7 +-- .../components/bluetooth/translations/es.json | 7 +-- .../components/bluetooth/translations/et.json | 7 +-- .../components/bluetooth/translations/fr.json | 6 +- .../components/bluetooth/translations/he.json | 7 +-- .../components/bluetooth/translations/hu.json | 7 +-- .../components/bluetooth/translations/id.json | 7 +-- .../components/bluetooth/translations/it.json | 7 +-- .../components/bluetooth/translations/ja.json | 7 +-- .../components/bluetooth/translations/nl.json | 12 ---- .../components/bluetooth/translations/no.json | 7 +-- .../components/bluetooth/translations/pl.json | 7 +-- .../bluetooth/translations/pt-BR.json | 7 +-- .../components/bluetooth/translations/ru.json | 7 +-- .../components/bluetooth/translations/sv.json | 7 +-- .../components/bluetooth/translations/tr.json | 7 +-- .../bluetooth/translations/zh-Hant.json | 7 +-- .../components/climacell/translations/af.json | 9 +++ .../components/climacell/translations/ca.json | 13 +++++ .../components/climacell/translations/de.json | 13 +++++ .../components/climacell/translations/el.json | 13 +++++ .../climacell/translations/es-419.json | 13 +++++ .../components/climacell/translations/es.json | 13 +++++ .../components/climacell/translations/et.json | 13 +++++ .../components/climacell/translations/fr.json | 13 +++++ .../components/climacell/translations/hu.json | 13 +++++ .../components/climacell/translations/id.json | 13 +++++ .../components/climacell/translations/it.json | 13 +++++ .../components/climacell/translations/ja.json | 13 +++++ .../components/climacell/translations/ko.json | 13 +++++ .../components/climacell/translations/nl.json | 13 +++++ .../components/climacell/translations/no.json | 13 +++++ .../components/climacell/translations/pl.json | 13 +++++ .../climacell/translations/pt-BR.json | 13 +++++ .../components/climacell/translations/ru.json | 13 +++++ .../climacell/translations/sensor.bg.json | 8 +++ .../climacell/translations/sensor.ca.json | 27 +++++++++ .../climacell/translations/sensor.de.json | 27 +++++++++ .../climacell/translations/sensor.el.json | 27 +++++++++ .../climacell/translations/sensor.es-419.json | 27 +++++++++ .../climacell/translations/sensor.es.json | 27 +++++++++ .../climacell/translations/sensor.et.json | 27 +++++++++ .../climacell/translations/sensor.fr.json | 27 +++++++++ .../climacell/translations/sensor.he.json | 7 +++ .../climacell/translations/sensor.hu.json | 27 +++++++++ .../climacell/translations/sensor.id.json | 27 +++++++++ .../climacell/translations/sensor.is.json | 12 ++++ .../climacell/translations/sensor.it.json | 27 +++++++++ .../climacell/translations/sensor.ja.json | 27 +++++++++ .../climacell/translations/sensor.ko.json | 7 +++ .../climacell/translations/sensor.lv.json | 27 +++++++++ .../climacell/translations/sensor.nl.json | 27 +++++++++ .../climacell/translations/sensor.no.json | 27 +++++++++ .../climacell/translations/sensor.pl.json | 27 +++++++++ .../climacell/translations/sensor.pt-BR.json | 27 +++++++++ .../climacell/translations/sensor.pt.json | 7 +++ .../climacell/translations/sensor.ru.json | 27 +++++++++ .../climacell/translations/sensor.sk.json | 18 ++++++ .../climacell/translations/sensor.sv.json | 27 +++++++++ .../climacell/translations/sensor.tr.json | 27 +++++++++ .../translations/sensor.zh-Hant.json | 27 +++++++++ .../components/climacell/translations/sv.json | 13 +++++ .../components/climacell/translations/tr.json | 13 +++++ .../climacell/translations/zh-Hant.json | 13 +++++ .../components/coinbase/translations/bg.json | 6 -- .../components/coinbase/translations/ca.json | 6 -- .../components/coinbase/translations/de.json | 6 -- .../components/coinbase/translations/el.json | 6 -- .../components/coinbase/translations/en.json | 6 -- .../components/coinbase/translations/es.json | 6 -- .../components/coinbase/translations/et.json | 6 -- .../components/coinbase/translations/hu.json | 6 -- .../components/coinbase/translations/id.json | 6 -- .../components/coinbase/translations/it.json | 6 -- .../components/coinbase/translations/nb.json | 5 -- .../components/coinbase/translations/nl.json | 5 -- .../components/coinbase/translations/no.json | 6 -- .../components/coinbase/translations/pl.json | 6 -- .../coinbase/translations/pt-BR.json | 6 -- .../components/coinbase/translations/ru.json | 6 -- .../components/coinbase/translations/tr.json | 6 -- .../coinbase/translations/zh-Hant.json | 6 -- .../components/ecowitt/translations/bg.json | 4 -- .../components/ecowitt/translations/ca.json | 8 --- .../components/ecowitt/translations/cs.json | 8 --- .../components/ecowitt/translations/de.json | 8 --- .../components/ecowitt/translations/el.json | 8 --- .../components/ecowitt/translations/en.json | 8 --- .../components/ecowitt/translations/es.json | 8 --- .../components/ecowitt/translations/et.json | 8 --- .../components/ecowitt/translations/fr.json | 7 --- .../components/ecowitt/translations/he.json | 7 --- .../components/ecowitt/translations/hu.json | 8 --- .../components/ecowitt/translations/id.json | 8 --- .../components/ecowitt/translations/it.json | 8 --- .../components/ecowitt/translations/ja.json | 8 --- .../components/ecowitt/translations/nb.json | 7 --- .../components/ecowitt/translations/nl.json | 4 -- .../components/ecowitt/translations/no.json | 8 --- .../components/ecowitt/translations/pl.json | 8 --- .../ecowitt/translations/pt-BR.json | 8 --- .../components/ecowitt/translations/pt.json | 7 --- .../components/ecowitt/translations/ru.json | 8 --- .../components/ecowitt/translations/sv.json | 8 --- .../components/ecowitt/translations/tr.json | 8 --- .../ecowitt/translations/zh-Hant.json | 8 --- .../fireservicerota/translations/bg.json | 5 -- .../fireservicerota/translations/ca.json | 6 -- .../fireservicerota/translations/cs.json | 6 -- .../fireservicerota/translations/de.json | 6 -- .../fireservicerota/translations/el.json | 6 -- .../fireservicerota/translations/en.json | 6 -- .../fireservicerota/translations/es-419.json | 3 - .../fireservicerota/translations/es.json | 6 -- .../fireservicerota/translations/et.json | 6 -- .../fireservicerota/translations/fr.json | 6 -- .../fireservicerota/translations/he.json | 5 -- .../fireservicerota/translations/hu.json | 6 -- .../fireservicerota/translations/id.json | 6 -- .../fireservicerota/translations/it.json | 6 -- .../fireservicerota/translations/ja.json | 6 -- .../fireservicerota/translations/ka.json | 6 -- .../fireservicerota/translations/ko.json | 6 -- .../fireservicerota/translations/lb.json | 6 -- .../fireservicerota/translations/nl.json | 6 -- .../fireservicerota/translations/no.json | 6 -- .../fireservicerota/translations/pl.json | 6 -- .../fireservicerota/translations/pt-BR.json | 6 -- .../fireservicerota/translations/pt.json | 5 -- .../fireservicerota/translations/ru.json | 6 -- .../fireservicerota/translations/sk.json | 5 -- .../fireservicerota/translations/sl.json | 6 -- .../fireservicerota/translations/sv.json | 6 -- .../fireservicerota/translations/tr.json | 6 -- .../fireservicerota/translations/uk.json | 6 -- .../fireservicerota/translations/zh-Hant.json | 6 -- .../fully_kiosk/translations/bg.json | 1 - .../fully_kiosk/translations/ca.json | 1 - .../fully_kiosk/translations/cs.json | 1 - .../fully_kiosk/translations/de.json | 1 - .../fully_kiosk/translations/el.json | 1 - .../fully_kiosk/translations/en.json | 1 - .../fully_kiosk/translations/es.json | 1 - .../fully_kiosk/translations/et.json | 1 - .../fully_kiosk/translations/fr.json | 1 - .../fully_kiosk/translations/he.json | 1 - .../fully_kiosk/translations/hu.json | 1 - .../fully_kiosk/translations/id.json | 1 - .../fully_kiosk/translations/it.json | 1 - .../fully_kiosk/translations/ja.json | 1 - .../fully_kiosk/translations/nl.json | 1 - .../fully_kiosk/translations/no.json | 1 - .../fully_kiosk/translations/pl.json | 1 - .../fully_kiosk/translations/pt-BR.json | 1 - .../fully_kiosk/translations/ru.json | 1 - .../fully_kiosk/translations/sv.json | 1 - .../fully_kiosk/translations/tr.json | 1 - .../fully_kiosk/translations/zh-Hant.json | 1 - .../components/generic/translations/bg.json | 16 ----- .../components/generic/translations/ca.json | 24 -------- .../components/generic/translations/cs.json | 13 ----- .../components/generic/translations/de.json | 24 -------- .../components/generic/translations/el.json | 24 -------- .../components/generic/translations/en.json | 24 -------- .../components/generic/translations/es.json | 24 -------- .../components/generic/translations/et.json | 24 -------- .../components/generic/translations/fr.json | 24 -------- .../components/generic/translations/he.json | 24 -------- .../components/generic/translations/hu.json | 24 -------- .../components/generic/translations/id.json | 24 -------- .../components/generic/translations/it.json | 24 -------- .../components/generic/translations/ja.json | 24 -------- .../components/generic/translations/ko.json | 9 --- .../components/generic/translations/nl.json | 24 -------- .../components/generic/translations/no.json | 24 -------- .../components/generic/translations/pl.json | 24 -------- .../generic/translations/pt-BR.json | 24 -------- .../components/generic/translations/pt.json | 5 -- .../components/generic/translations/ru.json | 24 -------- .../components/generic/translations/sk.json | 3 - .../components/generic/translations/sv.json | 24 -------- .../components/generic/translations/tr.json | 24 -------- .../generic/translations/zh-Hant.json | 24 -------- .../components/glances/translations/bg.json | 17 +----- .../components/glances/translations/ca.json | 17 +----- .../components/glances/translations/cs.json | 17 +----- .../components/glances/translations/da.json | 17 +----- .../components/glances/translations/de.json | 17 +----- .../components/glances/translations/el.json | 17 +----- .../components/glances/translations/en.json | 17 +----- .../glances/translations/es-419.json | 17 +----- .../components/glances/translations/es.json | 17 +----- .../components/glances/translations/et.json | 17 +----- .../components/glances/translations/fi.json | 1 - .../components/glances/translations/fr.json | 17 +----- .../components/glances/translations/he.json | 1 - .../components/glances/translations/hu.json | 17 +----- .../components/glances/translations/id.json | 17 +----- .../components/glances/translations/it.json | 17 +----- .../components/glances/translations/ja.json | 17 +----- .../components/glances/translations/ko.json | 17 +----- .../components/glances/translations/lb.json | 17 +----- .../components/glances/translations/nl.json | 17 +----- .../components/glances/translations/no.json | 17 +----- .../components/glances/translations/pl.json | 17 +----- .../glances/translations/pt-BR.json | 17 +----- .../components/glances/translations/pt.json | 10 ---- .../components/glances/translations/ru.json | 17 +----- .../components/glances/translations/sk.json | 10 ---- .../components/glances/translations/sl.json | 17 +----- .../components/glances/translations/sv.json | 17 +----- .../components/glances/translations/tr.json | 17 +----- .../components/glances/translations/uk.json | 17 +----- .../glances/translations/zh-Hans.json | 17 +----- .../glances/translations/zh-Hant.json | 17 +----- .../components/google/translations/bg.json | 5 -- .../components/google/translations/ca.json | 10 ---- .../components/google/translations/de.json | 10 ---- .../components/google/translations/el.json | 10 ---- .../components/google/translations/en.json | 10 ---- .../components/google/translations/es.json | 10 ---- .../components/google/translations/et.json | 10 ---- .../components/google/translations/fr.json | 5 -- .../components/google/translations/hu.json | 10 ---- .../components/google/translations/id.json | 10 ---- .../components/google/translations/it.json | 10 ---- .../components/google/translations/ja.json | 10 ---- .../components/google/translations/nl.json | 5 -- .../components/google/translations/no.json | 10 ---- .../components/google/translations/pl.json | 10 ---- .../components/google/translations/pt-BR.json | 10 ---- .../components/google/translations/ru.json | 10 ---- .../components/google/translations/sv.json | 10 ---- .../components/google/translations/tr.json | 10 ---- .../google/translations/zh-Hant.json | 10 ---- .../components/group/translations/bg.json | 6 ++ .../components/guardian/translations/bg.json | 11 ---- .../components/guardian/translations/ca.json | 11 ---- .../components/guardian/translations/de.json | 11 ---- .../components/guardian/translations/el.json | 11 ---- .../components/guardian/translations/en.json | 11 ---- .../components/guardian/translations/es.json | 11 ---- .../components/guardian/translations/et.json | 11 ---- .../components/guardian/translations/fr.json | 11 ---- .../components/guardian/translations/hu.json | 11 ---- .../components/guardian/translations/id.json | 11 ---- .../components/guardian/translations/it.json | 11 ---- .../components/guardian/translations/nl.json | 10 ---- .../components/guardian/translations/no.json | 11 ---- .../components/guardian/translations/pl.json | 11 ---- .../guardian/translations/pt-BR.json | 11 ---- .../components/guardian/translations/ru.json | 11 ---- .../components/guardian/translations/sv.json | 11 ---- .../components/guardian/translations/tr.json | 11 ---- .../guardian/translations/zh-Hant.json | 11 ---- .../harmony/translations/select.en.json | 10 ++-- .../homeassistant_yellow/translations/bg.json | 3 +- .../homeassistant_yellow/translations/ca.json | 3 +- .../homeassistant_yellow/translations/et.json | 2 +- .../homeassistant_yellow/translations/id.json | 2 +- .../homeassistant_yellow/translations/no.json | 5 +- .../homeassistant_yellow/translations/pl.json | 5 +- .../homekit_controller/translations/id.json | 2 +- .../homekit_controller/translations/no.json | 2 +- .../homekit_controller/translations/pl.json | 2 +- .../components/icloud/translations/bg.json | 5 -- .../components/icloud/translations/ca.json | 7 --- .../components/icloud/translations/cs.json | 7 --- .../components/icloud/translations/de.json | 7 --- .../components/icloud/translations/el.json | 7 --- .../components/icloud/translations/en.json | 7 --- .../components/icloud/translations/es.json | 7 --- .../components/icloud/translations/et.json | 7 --- .../components/icloud/translations/fr.json | 7 --- .../components/icloud/translations/he.json | 7 --- .../components/icloud/translations/hu.json | 7 --- .../components/icloud/translations/id.json | 7 --- .../components/icloud/translations/it.json | 7 --- .../components/icloud/translations/ja.json | 7 --- .../components/icloud/translations/ka.json | 9 --- .../components/icloud/translations/ko.json | 7 --- .../components/icloud/translations/lb.json | 7 --- .../components/icloud/translations/nl.json | 7 --- .../components/icloud/translations/no.json | 7 --- .../components/icloud/translations/pl.json | 7 --- .../components/icloud/translations/pt-BR.json | 7 --- .../components/icloud/translations/pt.json | 7 --- .../components/icloud/translations/ru.json | 7 --- .../components/icloud/translations/sk.json | 5 -- .../components/icloud/translations/sv.json | 7 --- .../components/icloud/translations/tr.json | 7 --- .../components/icloud/translations/uk.json | 7 --- .../icloud/translations/zh-Hans.json | 5 -- .../icloud/translations/zh-Hant.json | 7 --- .../components/knx/translations/bg.json | 19 ------ .../components/knx/translations/ca.json | 48 +-------------- .../components/knx/translations/cs.json | 10 ---- .../components/knx/translations/de.json | 48 +-------------- .../components/knx/translations/el.json | 48 +-------------- .../components/knx/translations/en.json | 48 +-------------- .../components/knx/translations/es.json | 48 +-------------- .../components/knx/translations/et.json | 48 +-------------- .../components/knx/translations/fr.json | 48 +-------------- .../components/knx/translations/he.json | 16 ----- .../components/knx/translations/hu.json | 48 +-------------- .../components/knx/translations/id.json | 48 +-------------- .../components/knx/translations/it.json | 48 +-------------- .../components/knx/translations/ja.json | 56 +----------------- .../components/knx/translations/ko.json | 3 - .../components/knx/translations/nl.json | 50 +--------------- .../components/knx/translations/no.json | 48 +-------------- .../components/knx/translations/pl.json | 48 +-------------- .../components/knx/translations/pt-BR.json | 48 +-------------- .../components/knx/translations/pt.json | 12 ---- .../components/knx/translations/ru.json | 48 +-------------- .../components/knx/translations/sk.json | 17 ------ .../components/knx/translations/sl.json | 15 ----- .../components/knx/translations/sv.json | 56 +----------------- .../components/knx/translations/tr.json | 56 +----------------- .../components/knx/translations/zh-Hant.json | 48 +-------------- .../landisgyr_heat_meter/translations/bg.json | 4 -- .../landisgyr_heat_meter/translations/ca.json | 4 -- .../landisgyr_heat_meter/translations/cs.json | 4 -- .../landisgyr_heat_meter/translations/de.json | 4 -- .../landisgyr_heat_meter/translations/el.json | 4 -- .../landisgyr_heat_meter/translations/en.json | 4 -- .../landisgyr_heat_meter/translations/es.json | 4 -- .../landisgyr_heat_meter/translations/et.json | 4 -- .../landisgyr_heat_meter/translations/fr.json | 4 -- .../landisgyr_heat_meter/translations/he.json | 4 -- .../landisgyr_heat_meter/translations/hu.json | 4 -- .../landisgyr_heat_meter/translations/id.json | 4 -- .../landisgyr_heat_meter/translations/it.json | 4 -- .../landisgyr_heat_meter/translations/ja.json | 4 -- .../landisgyr_heat_meter/translations/nb.json | 7 --- .../landisgyr_heat_meter/translations/nl.json | 4 -- .../landisgyr_heat_meter/translations/no.json | 4 -- .../landisgyr_heat_meter/translations/pl.json | 4 -- .../translations/pt-BR.json | 4 -- .../landisgyr_heat_meter/translations/ru.json | 4 -- .../landisgyr_heat_meter/translations/sv.json | 4 -- .../landisgyr_heat_meter/translations/tr.json | 4 -- .../translations/zh-Hant.json | 4 -- .../lg_soundbar/translations/ar.json | 3 +- .../lg_soundbar/translations/ca.json | 3 +- .../lg_soundbar/translations/de.json | 3 +- .../lg_soundbar/translations/el.json | 3 +- .../lg_soundbar/translations/en.json | 3 +- .../lg_soundbar/translations/es.json | 3 +- .../lg_soundbar/translations/et.json | 3 +- .../lg_soundbar/translations/fr.json | 3 +- .../lg_soundbar/translations/hu.json | 3 +- .../lg_soundbar/translations/id.json | 3 +- .../lg_soundbar/translations/it.json | 3 +- .../lg_soundbar/translations/ja.json | 3 +- .../lg_soundbar/translations/nl.json | 3 +- .../lg_soundbar/translations/no.json | 3 +- .../lg_soundbar/translations/pl.json | 3 +- .../lg_soundbar/translations/pt-BR.json | 3 +- .../lg_soundbar/translations/ru.json | 3 +- .../lg_soundbar/translations/sk.json | 3 +- .../lg_soundbar/translations/sv.json | 3 +- .../lg_soundbar/translations/tr.json | 3 +- .../lg_soundbar/translations/zh-Hant.json | 3 +- .../components/lidarr/translations/ca.json | 10 ---- .../components/lidarr/translations/de.json | 10 ---- .../components/lidarr/translations/el.json | 10 ---- .../components/lidarr/translations/en.json | 10 ---- .../components/lidarr/translations/es.json | 10 ---- .../components/lidarr/translations/et.json | 10 ---- .../components/lidarr/translations/fr.json | 10 ---- .../components/lidarr/translations/hu.json | 10 ---- .../components/lidarr/translations/id.json | 10 ---- .../components/lidarr/translations/it.json | 10 ---- .../components/lidarr/translations/no.json | 10 ---- .../components/lidarr/translations/pl.json | 10 ---- .../components/lidarr/translations/pt-BR.json | 10 ---- .../components/lidarr/translations/ru.json | 10 ---- .../components/lidarr/translations/sv.json | 10 ---- .../components/lidarr/translations/tr.json | 10 ---- .../lidarr/translations/zh-Hant.json | 10 ---- .../components/life360/translations/bg.json | 5 -- .../components/life360/translations/ca.json | 8 +-- .../components/life360/translations/cs.json | 8 +-- .../components/life360/translations/da.json | 7 --- .../components/life360/translations/de.json | 8 +-- .../components/life360/translations/el.json | 8 +-- .../components/life360/translations/en.json | 8 +-- .../life360/translations/es-419.json | 7 --- .../components/life360/translations/es.json | 8 +-- .../components/life360/translations/et.json | 8 +-- .../components/life360/translations/fr.json | 8 +-- .../components/life360/translations/he.json | 4 +- .../components/life360/translations/hr.json | 6 -- .../components/life360/translations/hu.json | 8 +-- .../components/life360/translations/id.json | 8 +-- .../components/life360/translations/it.json | 8 +-- .../components/life360/translations/ja.json | 8 +-- .../components/life360/translations/ka.json | 3 +- .../components/life360/translations/ko.json | 8 +-- .../components/life360/translations/lb.json | 8 +-- .../components/life360/translations/nb.json | 3 - .../components/life360/translations/nl.json | 8 +-- .../components/life360/translations/no.json | 8 +-- .../components/life360/translations/pl.json | 8 +-- .../life360/translations/pt-BR.json | 8 +-- .../components/life360/translations/pt.json | 4 +- .../components/life360/translations/ru.json | 8 +-- .../components/life360/translations/sl.json | 7 --- .../components/life360/translations/sv.json | 8 +-- .../components/life360/translations/tr.json | 8 +-- .../components/life360/translations/uk.json | 8 +-- .../life360/translations/zh-Hans.json | 3 +- .../life360/translations/zh-Hant.json | 8 +-- .../components/lifx/translations/bg.json | 6 +- .../components/lifx/translations/ca.json | 6 +- .../components/lifx/translations/cs.json | 6 +- .../components/lifx/translations/da.json | 8 +-- .../components/lifx/translations/de.json | 6 +- .../components/lifx/translations/el.json | 6 +- .../components/lifx/translations/en.json | 6 +- .../components/lifx/translations/es-419.json | 8 +-- .../components/lifx/translations/es.json | 6 +- .../components/lifx/translations/et.json | 6 +- .../components/lifx/translations/fi.json | 9 --- .../components/lifx/translations/fr.json | 6 +- .../components/lifx/translations/he.json | 3 +- .../components/lifx/translations/hr.json | 8 +-- .../components/lifx/translations/hu.json | 6 +- .../components/lifx/translations/id.json | 6 +- .../components/lifx/translations/it.json | 6 +- .../components/lifx/translations/ja.json | 6 +- .../components/lifx/translations/ko.json | 8 +-- .../components/lifx/translations/lb.json | 8 +-- .../components/lifx/translations/nl.json | 6 +- .../components/lifx/translations/no.json | 6 +- .../components/lifx/translations/pl.json | 6 +- .../components/lifx/translations/pt-BR.json | 6 +- .../components/lifx/translations/pt.json | 8 +-- .../components/lifx/translations/ro.json | 8 +-- .../components/lifx/translations/ru.json | 6 +- .../components/lifx/translations/sl.json | 8 +-- .../components/lifx/translations/sv.json | 6 +- .../components/lifx/translations/tr.json | 6 +- .../components/lifx/translations/uk.json | 8 +-- .../components/lifx/translations/zh-Hans.json | 8 +-- .../components/lifx/translations/zh-Hant.json | 6 +- .../components/mqtt/translations/bg.json | 1 - .../components/mqtt/translations/ca.json | 13 ++++- .../components/mqtt/translations/cs.json | 1 - .../components/mqtt/translations/da.json | 1 - .../components/mqtt/translations/de.json | 13 ++++- .../components/mqtt/translations/el.json | 1 - .../components/mqtt/translations/en.json | 9 ++- .../components/mqtt/translations/es-419.json | 1 - .../components/mqtt/translations/es.json | 13 ++++- .../components/mqtt/translations/et.json | 13 ++++- .../components/mqtt/translations/fi.json | 1 - .../components/mqtt/translations/fr.json | 1 - .../components/mqtt/translations/he.json | 1 - .../components/mqtt/translations/hr.json | 1 - .../components/mqtt/translations/hu.json | 1 - .../components/mqtt/translations/id.json | 13 ++++- .../components/mqtt/translations/it.json | 1 - .../components/mqtt/translations/ja.json | 1 - .../components/mqtt/translations/ko.json | 1 - .../components/mqtt/translations/lb.json | 1 - .../components/mqtt/translations/nl.json | 1 - .../components/mqtt/translations/no.json | 1 - .../components/mqtt/translations/pl.json | 13 ++++- .../components/mqtt/translations/pt-BR.json | 1 - .../components/mqtt/translations/pt.json | 1 - .../components/mqtt/translations/ro.json | 1 - .../components/mqtt/translations/ru.json | 1 - .../components/mqtt/translations/sl.json | 1 - .../components/mqtt/translations/sv.json | 1 - .../components/mqtt/translations/th.json | 1 - .../components/mqtt/translations/tr.json | 1 - .../components/mqtt/translations/uk.json | 1 - .../components/mqtt/translations/zh-Hans.json | 1 - .../components/mqtt/translations/zh-Hant.json | 1 - .../components/mysensors/translations/ca.json | 1 - .../components/mysensors/translations/de.json | 1 - .../components/mysensors/translations/el.json | 1 - .../components/mysensors/translations/en.json | 1 - .../components/mysensors/translations/es.json | 1 - .../components/mysensors/translations/et.json | 1 - .../components/mysensors/translations/fr.json | 1 - .../components/mysensors/translations/he.json | 1 - .../components/mysensors/translations/hu.json | 1 - .../components/mysensors/translations/id.json | 1 - .../components/mysensors/translations/it.json | 1 - .../components/mysensors/translations/ja.json | 1 - .../components/mysensors/translations/ko.json | 1 - .../components/mysensors/translations/nl.json | 1 - .../components/mysensors/translations/no.json | 1 - .../components/mysensors/translations/pl.json | 1 - .../mysensors/translations/pt-BR.json | 1 - .../components/mysensors/translations/ru.json | 1 - .../components/mysensors/translations/sv.json | 1 - .../components/mysensors/translations/tr.json | 1 - .../mysensors/translations/zh-Hans.json | 3 - .../mysensors/translations/zh-Hant.json | 1 - .../components/nest/translations/af.json | 12 ---- .../components/nest/translations/ca.json | 8 --- .../components/nest/translations/cs.json | 6 -- .../components/nest/translations/de.json | 8 --- .../components/nest/translations/el.json | 8 --- .../components/nest/translations/en.json | 8 --- .../components/nest/translations/es.json | 8 --- .../components/nest/translations/et.json | 8 --- .../components/nest/translations/fr.json | 8 --- .../components/nest/translations/he.json | 6 -- .../components/nest/translations/hu.json | 8 --- .../components/nest/translations/id.json | 8 --- .../components/nest/translations/it.json | 8 --- .../components/nest/translations/ja.json | 8 --- .../components/nest/translations/ko.json | 1 - .../components/nest/translations/lb.json | 1 - .../components/nest/translations/nl.json | 8 --- .../components/nest/translations/no.json | 8 --- .../components/nest/translations/pl.json | 8 --- .../components/nest/translations/pt-BR.json | 8 --- .../components/nest/translations/pt.json | 6 -- .../components/nest/translations/ru.json | 8 --- .../components/nest/translations/sk.json | 7 --- .../components/nest/translations/sl.json | 6 -- .../components/nest/translations/sv.json | 8 --- .../components/nest/translations/th.json | 3 - .../components/nest/translations/tr.json | 8 --- .../components/nest/translations/uk.json | 1 - .../components/nest/translations/zh-Hans.json | 4 -- .../components/nest/translations/zh-Hant.json | 8 --- .../nibe_heatpump/translations/bg.json | 3 - .../nibe_heatpump/translations/ca.json | 15 ----- .../nibe_heatpump/translations/de.json | 15 ----- .../nibe_heatpump/translations/el.json | 15 ----- .../nibe_heatpump/translations/en.json | 15 ----- .../nibe_heatpump/translations/es.json | 15 ----- .../nibe_heatpump/translations/et.json | 15 ----- .../nibe_heatpump/translations/fr.json | 9 --- .../nibe_heatpump/translations/he.json | 3 - .../nibe_heatpump/translations/hu.json | 15 ----- .../nibe_heatpump/translations/id.json | 15 ----- .../nibe_heatpump/translations/it.json | 15 ----- .../nibe_heatpump/translations/ja.json | 13 ----- .../nibe_heatpump/translations/nb.json | 3 - .../nibe_heatpump/translations/nl.json | 3 - .../nibe_heatpump/translations/no.json | 15 ----- .../nibe_heatpump/translations/pl.json | 15 ----- .../nibe_heatpump/translations/pt-BR.json | 15 ----- .../nibe_heatpump/translations/ru.json | 15 ----- .../nibe_heatpump/translations/sk.json | 7 --- .../nibe_heatpump/translations/sv.json | 13 ----- .../nibe_heatpump/translations/tr.json | 15 ----- .../nibe_heatpump/translations/zh-Hans.json | 14 ----- .../nibe_heatpump/translations/zh-Hant.json | 15 ----- .../openexchangerates/translations/ca.json | 6 -- .../openexchangerates/translations/de.json | 6 -- .../openexchangerates/translations/el.json | 6 -- .../openexchangerates/translations/en.json | 6 -- .../openexchangerates/translations/es.json | 6 -- .../openexchangerates/translations/et.json | 6 -- .../openexchangerates/translations/fr.json | 5 -- .../openexchangerates/translations/hu.json | 6 -- .../openexchangerates/translations/id.json | 6 -- .../openexchangerates/translations/it.json | 6 -- .../openexchangerates/translations/ja.json | 6 -- .../openexchangerates/translations/no.json | 6 -- .../openexchangerates/translations/pl.json | 6 -- .../openexchangerates/translations/pt-BR.json | 6 -- .../openexchangerates/translations/pt.json | 6 -- .../openexchangerates/translations/ru.json | 6 -- .../openexchangerates/translations/sv.json | 6 -- .../openexchangerates/translations/tr.json | 6 -- .../translations/zh-Hant.json | 6 -- .../components/overkiz/translations/bg.json | 1 - .../components/overkiz/translations/bn.json | 7 --- .../components/overkiz/translations/ca.json | 1 - .../components/overkiz/translations/de.json | 1 - .../components/overkiz/translations/el.json | 1 - .../components/overkiz/translations/en.json | 1 - .../components/overkiz/translations/es.json | 1 - .../components/overkiz/translations/et.json | 1 - .../components/overkiz/translations/fr.json | 1 - .../components/overkiz/translations/hu.json | 1 - .../components/overkiz/translations/id.json | 1 - .../components/overkiz/translations/it.json | 1 - .../components/overkiz/translations/ja.json | 3 +- .../components/overkiz/translations/no.json | 1 - .../components/overkiz/translations/pl.json | 1 - .../overkiz/translations/pt-BR.json | 1 - .../components/overkiz/translations/pt.json | 3 +- .../components/overkiz/translations/ru.json | 1 - .../components/overkiz/translations/sv.json | 1 - .../components/overkiz/translations/tr.json | 1 - .../overkiz/translations/zh-Hant.json | 1 - .../p1_monitor/translations/bg.json | 3 +- .../p1_monitor/translations/ca.json | 3 +- .../p1_monitor/translations/cs.json | 3 +- .../p1_monitor/translations/de.json | 3 +- .../p1_monitor/translations/el.json | 3 +- .../p1_monitor/translations/en.json | 3 +- .../p1_monitor/translations/es.json | 3 +- .../p1_monitor/translations/et.json | 3 +- .../p1_monitor/translations/fr.json | 3 +- .../p1_monitor/translations/he.json | 3 +- .../p1_monitor/translations/hu.json | 3 +- .../p1_monitor/translations/id.json | 3 +- .../p1_monitor/translations/it.json | 3 +- .../p1_monitor/translations/ja.json | 3 +- .../p1_monitor/translations/nl.json | 3 +- .../p1_monitor/translations/no.json | 3 +- .../p1_monitor/translations/pl.json | 3 +- .../p1_monitor/translations/pt-BR.json | 3 +- .../p1_monitor/translations/pt.json | 3 +- .../p1_monitor/translations/ru.json | 3 +- .../p1_monitor/translations/sk.json | 3 +- .../p1_monitor/translations/sv.json | 3 +- .../p1_monitor/translations/tr.json | 3 +- .../p1_monitor/translations/zh-Hant.json | 3 +- .../components/plugwise/translations/bg.json | 7 --- .../components/plugwise/translations/ca.json | 22 ------- .../components/plugwise/translations/cs.json | 22 ------- .../components/plugwise/translations/de.json | 22 ------- .../components/plugwise/translations/el.json | 22 ------- .../components/plugwise/translations/en.json | 22 ------- .../components/plugwise/translations/es.json | 22 ------- .../components/plugwise/translations/et.json | 22 ------- .../components/plugwise/translations/fr.json | 22 ------- .../components/plugwise/translations/he.json | 9 --- .../components/plugwise/translations/hu.json | 22 ------- .../components/plugwise/translations/id.json | 22 ------- .../components/plugwise/translations/it.json | 22 ------- .../components/plugwise/translations/ja.json | 22 ------- .../components/plugwise/translations/ka.json | 21 ------- .../components/plugwise/translations/ko.json | 24 -------- .../components/plugwise/translations/lb.json | 23 -------- .../components/plugwise/translations/nl.json | 22 ------- .../components/plugwise/translations/no.json | 22 ------- .../components/plugwise/translations/pl.json | 22 ------- .../plugwise/translations/pt-BR.json | 22 ------- .../components/plugwise/translations/pt.json | 17 ------ .../components/plugwise/translations/ru.json | 22 ------- .../components/plugwise/translations/sk.json | 7 --- .../components/plugwise/translations/sv.json | 22 ------- .../components/plugwise/translations/tr.json | 22 ------- .../components/plugwise/translations/uk.json | 24 -------- .../plugwise/translations/zh-Hans.json | 12 ---- .../plugwise/translations/zh-Hant.json | 22 ------- .../components/pushover/translations/ca.json | 4 -- .../components/pushover/translations/de.json | 4 -- .../components/pushover/translations/el.json | 4 -- .../components/pushover/translations/en.json | 4 -- .../components/pushover/translations/es.json | 4 -- .../components/pushover/translations/et.json | 4 -- .../components/pushover/translations/fr.json | 5 -- .../components/pushover/translations/hu.json | 4 -- .../components/pushover/translations/id.json | 4 -- .../components/pushover/translations/it.json | 4 -- .../components/pushover/translations/ja.json | 6 -- .../components/pushover/translations/no.json | 4 -- .../components/pushover/translations/pl.json | 4 -- .../pushover/translations/pt-BR.json | 4 -- .../components/pushover/translations/pt.json | 6 -- .../components/pushover/translations/ru.json | 4 -- .../components/pushover/translations/sv.json | 6 -- .../components/pushover/translations/tr.json | 4 -- .../pushover/translations/zh-Hant.json | 4 -- .../components/radarr/translations/bg.json | 3 - .../components/radarr/translations/ca.json | 4 -- .../components/radarr/translations/de.json | 4 -- .../components/radarr/translations/el.json | 4 -- .../components/radarr/translations/en.json | 4 -- .../components/radarr/translations/es.json | 4 -- .../components/radarr/translations/et.json | 4 -- .../components/radarr/translations/fr.json | 3 - .../components/radarr/translations/hu.json | 4 -- .../components/radarr/translations/id.json | 4 -- .../components/radarr/translations/it.json | 4 -- .../components/radarr/translations/nl.json | 3 - .../components/radarr/translations/no.json | 4 -- .../components/radarr/translations/pl.json | 4 -- .../components/radarr/translations/pt-BR.json | 4 -- .../components/radarr/translations/ru.json | 4 -- .../components/radarr/translations/sv.json | 4 -- .../components/radarr/translations/tr.json | 4 -- .../radarr/translations/zh-Hant.json | 4 -- .../rainmachine/translations/bg.json | 12 ---- .../rainmachine/translations/ca.json | 13 ----- .../rainmachine/translations/de.json | 13 ----- .../rainmachine/translations/el.json | 13 ----- .../rainmachine/translations/en.json | 13 ----- .../rainmachine/translations/es.json | 13 ----- .../rainmachine/translations/et.json | 13 ----- .../rainmachine/translations/fr.json | 13 ----- .../rainmachine/translations/hu.json | 13 ----- .../rainmachine/translations/id.json | 13 ----- .../rainmachine/translations/it.json | 13 ----- .../rainmachine/translations/nl.json | 12 ---- .../rainmachine/translations/no.json | 13 ----- .../rainmachine/translations/pl.json | 13 ----- .../rainmachine/translations/pt-BR.json | 13 ----- .../rainmachine/translations/ru.json | 13 ----- .../rainmachine/translations/sv.json | 13 ----- .../rainmachine/translations/tr.json | 13 ----- .../rainmachine/translations/zh-Hant.json | 13 ----- .../components/risco/translations/bg.json | 5 -- .../components/risco/translations/ca.json | 5 -- .../components/risco/translations/cs.json | 7 --- .../components/risco/translations/de.json | 5 -- .../components/risco/translations/el.json | 5 -- .../components/risco/translations/en.json | 5 -- .../components/risco/translations/es.json | 5 -- .../components/risco/translations/et.json | 5 -- .../components/risco/translations/fr.json | 5 -- .../components/risco/translations/he.json | 7 --- .../components/risco/translations/hu.json | 5 -- .../components/risco/translations/id.json | 5 -- .../components/risco/translations/it.json | 5 -- .../components/risco/translations/ja.json | 5 -- .../components/risco/translations/ko.json | 9 --- .../components/risco/translations/lb.json | 9 --- .../components/risco/translations/nl.json | 7 --- .../components/risco/translations/no.json | 5 -- .../components/risco/translations/pl.json | 5 -- .../components/risco/translations/pt-BR.json | 5 -- .../components/risco/translations/pt.json | 5 -- .../components/risco/translations/ru.json | 5 -- .../components/risco/translations/sk.json | 5 -- .../components/risco/translations/sv.json | 5 -- .../components/risco/translations/tr.json | 5 -- .../components/risco/translations/uk.json | 9 --- .../risco/translations/zh-Hans.json | 11 ---- .../risco/translations/zh-Hant.json | 5 -- .../components/scrape/translations/bg.json | 10 ---- .../components/scrape/translations/ca.json | 58 ++++++++++--------- .../components/scrape/translations/de.json | 28 --------- .../components/scrape/translations/el.json | 28 --------- .../components/scrape/translations/en.json | 28 --------- .../components/scrape/translations/es.json | 28 --------- .../components/scrape/translations/et.json | 28 --------- .../components/scrape/translations/fr.json | 28 --------- .../components/scrape/translations/he.json | 8 --- .../components/scrape/translations/hu.json | 28 --------- .../components/scrape/translations/id.json | 28 --------- .../components/scrape/translations/it.json | 28 --------- .../components/scrape/translations/ja.json | 28 --------- .../components/scrape/translations/nl.json | 28 --------- .../components/scrape/translations/no.json | 58 ++++++++++--------- .../components/scrape/translations/pl.json | 56 +++++++++--------- .../components/scrape/translations/pt-BR.json | 28 --------- .../components/scrape/translations/pt.json | 17 ------ .../components/scrape/translations/ru.json | 28 --------- .../components/scrape/translations/sk.json | 8 +-- .../components/scrape/translations/sv.json | 28 --------- .../components/scrape/translations/tr.json | 28 --------- .../scrape/translations/zh-Hant.json | 28 --------- .../components/shelly/translations/ca.json | 3 + .../simplepush/translations/ca.json | 4 -- .../simplepush/translations/de.json | 4 -- .../simplepush/translations/el.json | 4 -- .../simplepush/translations/en.json | 4 -- .../simplepush/translations/es.json | 4 -- .../simplepush/translations/et.json | 4 -- .../simplepush/translations/fr.json | 3 - .../simplepush/translations/hu.json | 4 -- .../simplepush/translations/id.json | 4 -- .../simplepush/translations/it.json | 4 -- .../simplepush/translations/ja.json | 4 -- .../simplepush/translations/nl.json | 5 -- .../simplepush/translations/no.json | 4 -- .../simplepush/translations/pl.json | 4 -- .../simplepush/translations/pt-BR.json | 4 -- .../simplepush/translations/pt.json | 6 -- .../simplepush/translations/ru.json | 4 -- .../simplepush/translations/sv.json | 4 -- .../simplepush/translations/tr.json | 4 -- .../simplepush/translations/zh-Hant.json | 4 -- .../simplisafe/translations/bg.json | 18 +----- .../simplisafe/translations/ca.json | 21 +------ .../simplisafe/translations/cs.json | 15 ----- .../simplisafe/translations/da.json | 8 --- .../simplisafe/translations/de.json | 21 +------ .../simplisafe/translations/el.json | 21 +------ .../simplisafe/translations/en.json | 21 +------ .../simplisafe/translations/es-419.json | 8 --- .../simplisafe/translations/es.json | 21 +------ .../simplisafe/translations/et.json | 21 +------ .../simplisafe/translations/fi.json | 12 ---- .../simplisafe/translations/fr.json | 21 +------ .../simplisafe/translations/he.json | 20 ------- .../simplisafe/translations/hr.json | 12 ---- .../simplisafe/translations/hu.json | 21 +------ .../simplisafe/translations/id.json | 21 +------ .../simplisafe/translations/it.json | 21 +------ .../simplisafe/translations/ja.json | 21 +------ .../simplisafe/translations/ko.json | 18 ------ .../simplisafe/translations/lb.json | 15 ----- .../simplisafe/translations/nl.json | 21 ------- .../simplisafe/translations/no.json | 21 +------ .../simplisafe/translations/pl.json | 21 +------ .../simplisafe/translations/pt-BR.json | 21 +------ .../simplisafe/translations/pt.json | 14 ----- .../simplisafe/translations/ro.json | 12 ---- .../simplisafe/translations/ru.json | 21 +------ .../simplisafe/translations/sk.json | 13 ----- .../simplisafe/translations/sl.json | 8 --- .../simplisafe/translations/sv.json | 21 +------ .../simplisafe/translations/th.json | 11 ---- .../simplisafe/translations/tr.json | 21 +------ .../simplisafe/translations/uk.json | 15 ----- .../simplisafe/translations/zh-Hans.json | 8 --- .../simplisafe/translations/zh-Hant.json | 21 +------ .../speedtestdotnet/translations/bg.json | 12 ---- .../speedtestdotnet/translations/ca.json | 13 ----- .../speedtestdotnet/translations/de.json | 13 ----- .../speedtestdotnet/translations/el.json | 13 ----- .../speedtestdotnet/translations/en.json | 13 ----- .../speedtestdotnet/translations/es.json | 13 ----- .../speedtestdotnet/translations/et.json | 13 ----- .../speedtestdotnet/translations/fr.json | 13 ----- .../speedtestdotnet/translations/hu.json | 13 ----- .../speedtestdotnet/translations/id.json | 13 ----- .../speedtestdotnet/translations/it.json | 13 ----- .../speedtestdotnet/translations/ja.json | 13 ----- .../speedtestdotnet/translations/no.json | 13 ----- .../speedtestdotnet/translations/pl.json | 13 ----- .../speedtestdotnet/translations/pt-BR.json | 13 ----- .../speedtestdotnet/translations/pt.json | 13 ----- .../speedtestdotnet/translations/ru.json | 13 ----- .../speedtestdotnet/translations/sv.json | 13 ----- .../speedtestdotnet/translations/tr.json | 13 ----- .../speedtestdotnet/translations/zh-Hant.json | 13 ----- .../components/switchbee/translations/ca.json | 10 ---- .../components/switchbee/translations/cs.json | 9 --- .../components/switchbee/translations/de.json | 10 ---- .../components/switchbee/translations/el.json | 10 ---- .../components/switchbee/translations/en.json | 10 ---- .../components/switchbee/translations/es.json | 10 ---- .../components/switchbee/translations/et.json | 10 ---- .../components/switchbee/translations/fr.json | 10 ---- .../components/switchbee/translations/hu.json | 10 ---- .../components/switchbee/translations/id.json | 10 ---- .../components/switchbee/translations/it.json | 10 ---- .../components/switchbee/translations/ja.json | 9 --- .../components/switchbee/translations/ko.json | 10 ---- .../components/switchbee/translations/no.json | 10 ---- .../components/switchbee/translations/pl.json | 10 ---- .../switchbee/translations/pt-BR.json | 10 ---- .../components/switchbee/translations/ru.json | 10 ---- .../components/switchbee/translations/sv.json | 10 ---- .../components/switchbee/translations/tr.json | 10 ---- .../switchbee/translations/zh-Hant.json | 10 ---- .../components/switchbot/translations/bg.json | 5 +- .../components/switchbot/translations/ca.json | 13 +---- .../components/switchbot/translations/cs.json | 6 -- .../components/switchbot/translations/de.json | 13 +---- .../components/switchbot/translations/el.json | 13 +---- .../components/switchbot/translations/en.json | 13 +---- .../components/switchbot/translations/es.json | 13 +---- .../components/switchbot/translations/et.json | 13 +---- .../components/switchbot/translations/fr.json | 13 +---- .../components/switchbot/translations/he.json | 12 +--- .../components/switchbot/translations/hu.json | 13 +---- .../components/switchbot/translations/id.json | 13 +---- .../components/switchbot/translations/it.json | 13 +---- .../components/switchbot/translations/ja.json | 13 +---- .../components/switchbot/translations/nl.json | 13 +---- .../components/switchbot/translations/no.json | 13 +---- .../components/switchbot/translations/pl.json | 13 +---- .../switchbot/translations/pt-BR.json | 13 +---- .../components/switchbot/translations/pt.json | 20 ------- .../components/switchbot/translations/ru.json | 13 +---- .../components/switchbot/translations/sk.json | 14 +---- .../components/switchbot/translations/sv.json | 13 +---- .../components/switchbot/translations/tr.json | 13 +---- .../switchbot/translations/zh-Hant.json | 13 +---- .../tankerkoenig/translations/bg.json | 9 --- .../tankerkoenig/translations/ca.json | 1 - .../tankerkoenig/translations/de.json | 1 - .../tankerkoenig/translations/el.json | 1 - .../tankerkoenig/translations/en.json | 1 - .../tankerkoenig/translations/es.json | 1 - .../tankerkoenig/translations/et.json | 1 - .../tankerkoenig/translations/fr.json | 1 - .../tankerkoenig/translations/hu.json | 1 - .../tankerkoenig/translations/id.json | 1 - .../tankerkoenig/translations/it.json | 1 - .../tankerkoenig/translations/ja.json | 1 - .../tankerkoenig/translations/nl.json | 1 - .../tankerkoenig/translations/no.json | 1 - .../tankerkoenig/translations/pl.json | 1 - .../tankerkoenig/translations/pt-BR.json | 1 - .../tankerkoenig/translations/ru.json | 1 - .../tankerkoenig/translations/sk.json | 9 --- .../tankerkoenig/translations/sv.json | 1 - .../tankerkoenig/translations/tr.json | 1 - .../tankerkoenig/translations/zh-Hant.json | 1 - .../components/tautulli/translations/bg.json | 3 +- .../components/tautulli/translations/ca.json | 3 +- .../components/tautulli/translations/de.json | 3 +- .../components/tautulli/translations/el.json | 3 +- .../components/tautulli/translations/en.json | 3 +- .../components/tautulli/translations/es.json | 3 +- .../components/tautulli/translations/et.json | 3 +- .../components/tautulli/translations/fr.json | 3 +- .../components/tautulli/translations/he.json | 3 +- .../components/tautulli/translations/hu.json | 3 +- .../components/tautulli/translations/id.json | 3 +- .../components/tautulli/translations/it.json | 3 +- .../components/tautulli/translations/ja.json | 3 +- .../components/tautulli/translations/nl.json | 3 +- .../components/tautulli/translations/no.json | 3 +- .../components/tautulli/translations/pl.json | 3 +- .../tautulli/translations/pt-BR.json | 3 +- .../components/tautulli/translations/ru.json | 3 +- .../components/tautulli/translations/sv.json | 3 +- .../components/tautulli/translations/tr.json | 3 +- .../tautulli/translations/zh-Hant.json | 3 +- .../unifiprotect/translations/bg.json | 12 ---- .../unifiprotect/translations/ca.json | 8 +-- .../unifiprotect/translations/de.json | 5 -- .../unifiprotect/translations/el.json | 5 -- .../unifiprotect/translations/en.json | 5 -- .../unifiprotect/translations/es.json | 5 -- .../unifiprotect/translations/et.json | 5 -- .../unifiprotect/translations/fr.json | 4 -- .../unifiprotect/translations/hu.json | 5 -- .../unifiprotect/translations/id.json | 5 -- .../unifiprotect/translations/it.json | 5 -- .../unifiprotect/translations/ja.json | 4 -- .../unifiprotect/translations/no.json | 5 -- .../unifiprotect/translations/pl.json | 5 -- .../unifiprotect/translations/pt-BR.json | 5 -- .../unifiprotect/translations/ru.json | 5 -- .../unifiprotect/translations/sv.json | 4 -- .../unifiprotect/translations/tr.json | 4 -- .../unifiprotect/translations/zh-Hant.json | 5 -- .../volvooncall/translations/bg.json | 1 - .../volvooncall/translations/ca.json | 1 - .../volvooncall/translations/de.json | 1 - .../volvooncall/translations/el.json | 1 - .../volvooncall/translations/en.json | 1 - .../volvooncall/translations/es.json | 1 - .../volvooncall/translations/et.json | 1 - .../volvooncall/translations/fr.json | 1 - .../volvooncall/translations/hu.json | 1 - .../volvooncall/translations/id.json | 1 - .../volvooncall/translations/it.json | 1 - .../volvooncall/translations/ja.json | 1 - .../volvooncall/translations/no.json | 1 - .../volvooncall/translations/pl.json | 1 - .../volvooncall/translations/pt-BR.json | 1 - .../volvooncall/translations/pt.json | 3 +- .../volvooncall/translations/ru.json | 1 - .../volvooncall/translations/sv.json | 1 - .../volvooncall/translations/tr.json | 1 - .../volvooncall/translations/zh-Hant.json | 1 - .../components/withings/translations/bg.json | 3 - .../components/withings/translations/ca.json | 4 -- .../components/withings/translations/cs.json | 3 - .../components/withings/translations/de.json | 4 -- .../components/withings/translations/el.json | 4 -- .../components/withings/translations/en.json | 4 -- .../components/withings/translations/es.json | 4 -- .../components/withings/translations/et.json | 4 -- .../components/withings/translations/fr.json | 4 -- .../components/withings/translations/he.json | 3 - .../components/withings/translations/hu.json | 4 -- .../components/withings/translations/id.json | 4 -- .../components/withings/translations/it.json | 4 -- .../components/withings/translations/ja.json | 4 -- .../components/withings/translations/ko.json | 4 -- .../components/withings/translations/lb.json | 4 -- .../components/withings/translations/nl.json | 4 -- .../components/withings/translations/no.json | 4 -- .../components/withings/translations/pl.json | 4 -- .../withings/translations/pt-BR.json | 4 -- .../components/withings/translations/pt.json | 3 - .../components/withings/translations/ru.json | 4 -- .../components/withings/translations/sv.json | 4 -- .../components/withings/translations/tr.json | 4 -- .../components/withings/translations/uk.json | 4 -- .../withings/translations/zh-Hant.json | 4 -- .../components/ws66i/translations/bg.json | 3 - .../components/ws66i/translations/ca.json | 3 - .../components/ws66i/translations/cs.json | 3 - .../components/ws66i/translations/de.json | 3 - .../components/ws66i/translations/el.json | 3 - .../components/ws66i/translations/en.json | 3 - .../components/ws66i/translations/es.json | 3 - .../components/ws66i/translations/et.json | 3 - .../components/ws66i/translations/fr.json | 3 - .../components/ws66i/translations/he.json | 3 - .../components/ws66i/translations/hu.json | 3 - .../components/ws66i/translations/id.json | 3 - .../components/ws66i/translations/it.json | 3 - .../components/ws66i/translations/ja.json | 3 - .../components/ws66i/translations/ko.json | 3 - .../components/ws66i/translations/nl.json | 3 - .../components/ws66i/translations/no.json | 3 - .../components/ws66i/translations/pl.json | 3 - .../components/ws66i/translations/pt-BR.json | 3 - .../components/ws66i/translations/ru.json | 3 - .../components/ws66i/translations/sk.json | 3 - .../components/ws66i/translations/sv.json | 3 - .../components/ws66i/translations/tr.json | 3 - .../ws66i/translations/zh-Hant.json | 3 - .../xiaomi_ble/translations/ca.json | 6 -- .../xiaomi_ble/translations/de.json | 6 -- .../xiaomi_ble/translations/el.json | 6 -- .../xiaomi_ble/translations/en.json | 6 -- .../xiaomi_ble/translations/es.json | 6 -- .../xiaomi_ble/translations/et.json | 6 -- .../xiaomi_ble/translations/hu.json | 6 -- .../xiaomi_ble/translations/id.json | 6 -- .../xiaomi_ble/translations/it.json | 6 -- .../xiaomi_ble/translations/ja.json | 6 -- .../xiaomi_ble/translations/nl.json | 6 -- .../xiaomi_ble/translations/no.json | 6 -- .../xiaomi_ble/translations/pl.json | 6 -- .../xiaomi_ble/translations/pt-BR.json | 6 -- .../xiaomi_ble/translations/pt.json | 3 - .../xiaomi_ble/translations/ru.json | 6 -- .../xiaomi_ble/translations/sv.json | 6 -- .../xiaomi_ble/translations/tr.json | 6 -- .../xiaomi_ble/translations/zh-Hant.json | 6 -- .../components/yeelight/translations/bg.json | 4 +- .../components/zha/translations/bg.json | 16 ----- .../components/zha/translations/ca.json | 23 -------- .../components/zha/translations/cs.json | 13 ----- .../components/zha/translations/da.json | 25 -------- .../components/zha/translations/de.json | 23 -------- .../components/zha/translations/el.json | 23 -------- .../components/zha/translations/en.json | 23 -------- .../components/zha/translations/es-419.json | 5 -- .../components/zha/translations/es.json | 23 -------- .../components/zha/translations/et.json | 23 -------- .../components/zha/translations/fi.json | 21 ------- .../components/zha/translations/fr.json | 23 -------- .../components/zha/translations/he.json | 6 -- .../components/zha/translations/hr.json | 5 -- .../components/zha/translations/hu.json | 23 -------- .../components/zha/translations/id.json | 23 -------- .../components/zha/translations/it.json | 23 -------- .../components/zha/translations/ja.json | 23 -------- .../components/zha/translations/ko.json | 27 +-------- .../components/zha/translations/lb.json | 25 -------- .../components/zha/translations/nl.json | 23 -------- .../components/zha/translations/nn.json | 10 ---- .../components/zha/translations/no.json | 23 -------- .../components/zha/translations/pl.json | 23 -------- .../components/zha/translations/pt-BR.json | 23 -------- .../components/zha/translations/pt.json | 3 - .../components/zha/translations/ru.json | 23 -------- .../components/zha/translations/sl.json | 5 -- .../components/zha/translations/sv.json | 23 -------- .../components/zha/translations/tr.json | 23 -------- .../components/zha/translations/uk.json | 25 -------- .../components/zha/translations/zh-Hans.json | 25 -------- .../components/zha/translations/zh-Hant.json | 23 -------- 1119 files changed, 1386 insertions(+), 9401 deletions(-) create mode 100644 homeassistant/components/climacell/translations/af.json create mode 100644 homeassistant/components/climacell/translations/ca.json create mode 100644 homeassistant/components/climacell/translations/de.json create mode 100644 homeassistant/components/climacell/translations/el.json create mode 100644 homeassistant/components/climacell/translations/es-419.json create mode 100644 homeassistant/components/climacell/translations/es.json create mode 100644 homeassistant/components/climacell/translations/et.json create mode 100644 homeassistant/components/climacell/translations/fr.json create mode 100644 homeassistant/components/climacell/translations/hu.json create mode 100644 homeassistant/components/climacell/translations/id.json create mode 100644 homeassistant/components/climacell/translations/it.json create mode 100644 homeassistant/components/climacell/translations/ja.json create mode 100644 homeassistant/components/climacell/translations/ko.json create mode 100644 homeassistant/components/climacell/translations/nl.json create mode 100644 homeassistant/components/climacell/translations/no.json create mode 100644 homeassistant/components/climacell/translations/pl.json create mode 100644 homeassistant/components/climacell/translations/pt-BR.json create mode 100644 homeassistant/components/climacell/translations/ru.json create mode 100644 homeassistant/components/climacell/translations/sensor.bg.json create mode 100644 homeassistant/components/climacell/translations/sensor.ca.json create mode 100644 homeassistant/components/climacell/translations/sensor.de.json create mode 100644 homeassistant/components/climacell/translations/sensor.el.json create mode 100644 homeassistant/components/climacell/translations/sensor.es-419.json create mode 100644 homeassistant/components/climacell/translations/sensor.es.json create mode 100644 homeassistant/components/climacell/translations/sensor.et.json create mode 100644 homeassistant/components/climacell/translations/sensor.fr.json create mode 100644 homeassistant/components/climacell/translations/sensor.he.json create mode 100644 homeassistant/components/climacell/translations/sensor.hu.json create mode 100644 homeassistant/components/climacell/translations/sensor.id.json create mode 100644 homeassistant/components/climacell/translations/sensor.is.json create mode 100644 homeassistant/components/climacell/translations/sensor.it.json create mode 100644 homeassistant/components/climacell/translations/sensor.ja.json create mode 100644 homeassistant/components/climacell/translations/sensor.ko.json create mode 100644 homeassistant/components/climacell/translations/sensor.lv.json create mode 100644 homeassistant/components/climacell/translations/sensor.nl.json create mode 100644 homeassistant/components/climacell/translations/sensor.no.json create mode 100644 homeassistant/components/climacell/translations/sensor.pl.json create mode 100644 homeassistant/components/climacell/translations/sensor.pt-BR.json create mode 100644 homeassistant/components/climacell/translations/sensor.pt.json create mode 100644 homeassistant/components/climacell/translations/sensor.ru.json create mode 100644 homeassistant/components/climacell/translations/sensor.sk.json create mode 100644 homeassistant/components/climacell/translations/sensor.sv.json create mode 100644 homeassistant/components/climacell/translations/sensor.tr.json create mode 100644 homeassistant/components/climacell/translations/sensor.zh-Hant.json create mode 100644 homeassistant/components/climacell/translations/sv.json create mode 100644 homeassistant/components/climacell/translations/tr.json create mode 100644 homeassistant/components/climacell/translations/zh-Hant.json delete mode 100644 homeassistant/components/ecowitt/translations/cs.json delete mode 100644 homeassistant/components/ecowitt/translations/he.json delete mode 100644 homeassistant/components/ecowitt/translations/nb.json delete mode 100644 homeassistant/components/generic/translations/ko.json delete mode 100644 homeassistant/components/knx/translations/pt.json delete mode 100644 homeassistant/components/landisgyr_heat_meter/translations/nb.json delete mode 100644 homeassistant/components/lifx/translations/fi.json delete mode 100644 homeassistant/components/nest/translations/af.json delete mode 100644 homeassistant/components/nibe_heatpump/translations/sk.json delete mode 100644 homeassistant/components/nibe_heatpump/translations/zh-Hans.json delete mode 100644 homeassistant/components/overkiz/translations/bn.json delete mode 100644 homeassistant/components/plugwise/translations/ka.json delete mode 100644 homeassistant/components/plugwise/translations/zh-Hans.json delete mode 100644 homeassistant/components/risco/translations/zh-Hans.json delete mode 100644 homeassistant/components/simplisafe/translations/fi.json delete mode 100644 homeassistant/components/simplisafe/translations/hr.json delete mode 100644 homeassistant/components/simplisafe/translations/ro.json delete mode 100644 homeassistant/components/simplisafe/translations/th.json delete mode 100644 homeassistant/components/switchbot/translations/pt.json diff --git a/homeassistant/components/airzone/translations/ca.json b/homeassistant/components/airzone/translations/ca.json index 111a53fcf3a..4f37cf005f3 100644 --- a/homeassistant/components/airzone/translations/ca.json +++ b/homeassistant/components/airzone/translations/ca.json @@ -8,9 +8,17 @@ "invalid_system_id": "ID de sistema Airzone inv\u00e0lid" }, "step": { + "discovered_connection": { + "data": { + "host": "Amfitri\u00f3", + "id": "ID de sistema", + "port": "Port" + } + }, "user": { "data": { "host": "Amfitri\u00f3", + "id": "ID de sistema", "port": "Port" }, "description": "Configura la integraci\u00f3 Airzone." diff --git a/homeassistant/components/airzone/translations/en.json b/homeassistant/components/airzone/translations/en.json index 862887818d6..638f542e1ca 100644 --- a/homeassistant/components/airzone/translations/en.json +++ b/homeassistant/components/airzone/translations/en.json @@ -10,17 +10,18 @@ "step": { "discovered_connection": { "data": { - "id": "System ID", "host": "Host", + "id": "System ID", "port": "Port" } }, "user": { "data": { - "id": "System ID", "host": "Host", + "id": "System ID", "port": "Port" - } + }, + "description": "Set up Airzone integration." } } } diff --git a/homeassistant/components/airzone/translations/es.json b/homeassistant/components/airzone/translations/es.json index 20c05fa714f..858cb488344 100644 --- a/homeassistant/components/airzone/translations/es.json +++ b/homeassistant/components/airzone/translations/es.json @@ -8,9 +8,17 @@ "invalid_system_id": "ID del sistema Airzone no v\u00e1lido" }, "step": { + "discovered_connection": { + "data": { + "host": "Host", + "id": "ID del sistema", + "port": "Puerto" + } + }, "user": { "data": { "host": "Host", + "id": "ID del sistema", "port": "Puerto" }, "description": "Configura la integraci\u00f3n Airzone." diff --git a/homeassistant/components/airzone/translations/pl.json b/homeassistant/components/airzone/translations/pl.json index e389618ff80..42efddb4310 100644 --- a/homeassistant/components/airzone/translations/pl.json +++ b/homeassistant/components/airzone/translations/pl.json @@ -8,9 +8,17 @@ "invalid_system_id": "Nieprawid\u0142owy identyfikator systemu Airzone" }, "step": { + "discovered_connection": { + "data": { + "host": "Nazwa hosta lub adres IP", + "id": "Identyfikator systemu", + "port": "Port" + } + }, "user": { "data": { "host": "Nazwa hosta lub adres IP", + "id": "Identyfikator systemu", "port": "Port" }, "description": "Skonfiguruj integracj\u0119 Airzone." diff --git a/homeassistant/components/anthemav/translations/ca.json b/homeassistant/components/anthemav/translations/ca.json index 20785a9e67d..723883d5c1a 100644 --- a/homeassistant/components/anthemav/translations/ca.json +++ b/homeassistant/components/anthemav/translations/ca.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "La configuraci\u00f3 d'Anthem A/V Receivers mitjan\u00e7ant YAML s'eliminar\u00e0 de Home Assistant.\n\nLa configuraci\u00f3 YAML existent s'ha importat autom\u00e0ticament a la interf\u00edcie d'usuari.\n\nElimina la configuraci\u00f3 YAML d'Anthem A/V Receivers del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", - "title": "La configuraci\u00f3 YAML d'Anthem A/V Receivers est\u00e0 sent eliminada" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/de.json b/homeassistant/components/anthemav/translations/de.json index d751349b005..622384629fe 100644 --- a/homeassistant/components/anthemav/translations/de.json +++ b/homeassistant/components/anthemav/translations/de.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Die Konfiguration von Anthem A/V-Receivern mit YAML wird entfernt.\n\nDeine bestehende YAML-Konfiguration wurde automatisch in die Benutzeroberfl\u00e4che importiert.\n\nEntferne die Anthem A/V Receivers YAML Konfiguration aus deiner configuration.yaml Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die YAML-Konfiguration von Anthem A/V Receivers wird entfernt" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/el.json b/homeassistant/components/anthemav/translations/el.json index 5da08413782..983e89155e8 100644 --- a/homeassistant/components/anthemav/translations/el.json +++ b/homeassistant/components/anthemav/translations/el.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "\u0397 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03c4\u03ce\u03bd Anthem A/V \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03b5\u03b9\u03c3\u03b1\u03c7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03c4\u03bf\u03c5 Anthem A/V Receivers \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", - "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03c4\u03bf\u03c5 Anthem A/V Receivers \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/en.json b/homeassistant/components/anthemav/translations/en.json index af4c83eb2a8..9177d5a6e70 100644 --- a/homeassistant/components/anthemav/translations/en.json +++ b/homeassistant/components/anthemav/translations/en.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Configuring Anthem A/V Receivers using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Anthem A/V Receivers YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", - "title": "The Anthem A/V Receivers YAML configuration is being removed" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/es.json b/homeassistant/components/anthemav/translations/es.json index 7759dbd8607..f9609ad7c6b 100644 --- a/homeassistant/components/anthemav/translations/es.json +++ b/homeassistant/components/anthemav/translations/es.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Se va a eliminar la configuraci\u00f3n de los Receptores A/V Anthem mediante YAML. \n\nTu configuraci\u00f3n YAML existente se ha importado a la IU autom\u00e1ticamente. \n\nElimina la configuraci\u00f3n YAML de los Receptores A/V Anthem de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", - "title": "Se va a eliminar la configuraci\u00f3n YAML de los Receptores A/V Anthem" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/et.json b/homeassistant/components/anthemav/translations/et.json index d5b9d4f224f..4ec356c8902 100644 --- a/homeassistant/components/anthemav/translations/et.json +++ b/homeassistant/components/anthemav/translations/et.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Anthem A/V-vastuv\u00f5tjate konfigureerimine YAML-i abil eemaldatakse. \n\n Teie olemasolev YAML-i konfiguratsioon imporditi kasutajaliidesesse automaatselt. \n\n Selle probleemi lahendamiseks eemaldage failist configuration.yaml konfiguratsioon Anthem A/V Receivers YAML ja taask\u00e4ivitage Home Assistant.", - "title": "Anthem A/V-vastuv\u00f5tjate YAML-konfiguratsioon eemaldatakse" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/fr.json b/homeassistant/components/anthemav/translations/fr.json index d08ef70d6b7..faf417552ce 100644 --- a/homeassistant/components/anthemav/translations/fr.json +++ b/homeassistant/components/anthemav/translations/fr.json @@ -15,10 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "title": "La configuration YAML pour les r\u00e9cepteurs A/V Anthem sera bient\u00f4t supprim\u00e9e" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/hu.json b/homeassistant/components/anthemav/translations/hu.json index af7d008356c..f13544fff61 100644 --- a/homeassistant/components/anthemav/translations/hu.json +++ b/homeassistant/components/anthemav/translations/hu.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Az Anthem A/V egys\u00e9gek YAML-ben megadott konfigur\u00e1ci\u00f3ja elt\u00e1vol\u00edt\u00e1sra ker\u00fcl.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3 automatikusan import\u00e1l\u00e1sra ker\u00fclt a felhaszn\u00e1l\u00f3i fel\u00fcletre.\n\nA hiba kijav\u00edt\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el az Anthem A/V egys\u00e9gek YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", - "title": "Az Anthem A/V Receivers YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/id.json b/homeassistant/components/anthemav/translations/id.json index 99843443ab9..1eb2ba0b5a1 100644 --- a/homeassistant/components/anthemav/translations/id.json +++ b/homeassistant/components/anthemav/translations/id.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Proses konfigurasi Integrasi Receiver Anthem A/V lewat YAML dalam proses penghapusan.\n\nKonfigurasi YAML yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML Integrasi Receiver Anthem A/V dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", - "title": "Konfigurasi YAML Integrasi Anthem A/V Receiver dalam proses penghapusan" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/it.json b/homeassistant/components/anthemav/translations/it.json index 847332b57ad..12b0df56f0f 100644 --- a/homeassistant/components/anthemav/translations/it.json +++ b/homeassistant/components/anthemav/translations/it.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "La configurazione di Anthem A/V Receivers tramite YAML sar\u00e0 rimossa.\n\nLa configurazione YAML esistente \u00e8 stata importata automaticamente nell'interfaccia utente.\n\nRimuovi la configurazione YAML di Anthem A/V Receivers dal file configuration.yaml e riavvia Home Assistant per risolvere questo problema.", - "title": "La configurazione YAML di Anthem A/V Receivers sar\u00e0 rimossa" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/ja.json b/homeassistant/components/anthemav/translations/ja.json index 9a349743cf5..8c87d02e557 100644 --- a/homeassistant/components/anthemav/translations/ja.json +++ b/homeassistant/components/anthemav/translations/ja.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Anthem A/V Receivers\u306eYAML\u3092\u4f7f\u7528\u3057\u305f\u8a2d\u5b9a\u306f\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002\n\n\u306a\u304a\u3001\u65e2\u5b58\u306eYAML\u8a2d\u5b9a\u306f\u3001UI\u306b\u81ea\u52d5\u7684\u306b\u30a4\u30f3\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059\u3002\n\n\u3053\u306e\u554f\u984c\u3092\u89e3\u6c7a\u3059\u308b\u306b\u306f\u3001configuration.yaml\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u3001Anthem A/V Receivers\u306eYAML\u8a2d\u5b9a\u3092\u524a\u9664\u3057\u3001Home Assistant\u3092\u518d\u8d77\u52d5\u3057\u307e\u3059\u3002", - "title": "Anthem A/V Receivers YAML\u306e\u8a2d\u5b9a\u306f\u524a\u9664\u3055\u308c\u3066\u3044\u307e\u3059" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/nl.json b/homeassistant/components/anthemav/translations/nl.json index 8754427ea61..c09dde1bfc3 100644 --- a/homeassistant/components/anthemav/translations/nl.json +++ b/homeassistant/components/anthemav/translations/nl.json @@ -14,10 +14,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "title": "De Anthem A/V Receivers YAML-configuratie wordt verwijderd" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/no.json b/homeassistant/components/anthemav/translations/no.json index ae6b59cc89c..e7b3f66ae8d 100644 --- a/homeassistant/components/anthemav/translations/no.json +++ b/homeassistant/components/anthemav/translations/no.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Konfigurering av Anthem A/V-mottakere ved hjelp av YAML blir fjernet. \n\n Din eksisterende YAML-konfigurasjon har blitt importert til brukergrensesnittet automatisk. \n\n Fjern Anthem A/V Receivers YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", - "title": "Anthem A/V-mottakernes YAML-konfigurasjon blir fjernet" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/pl.json b/homeassistant/components/anthemav/translations/pl.json index ca40384e6f0..18eaecb9845 100644 --- a/homeassistant/components/anthemav/translations/pl.json +++ b/homeassistant/components/anthemav/translations/pl.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Konfiguracja Anthem A/V Receivers przy u\u017cyciu YAML zostanie usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML zosta\u0142a automatycznie zaimportowana do interfejsu u\u017cytkownika. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", - "title": "Konfiguracja YAML dla Anthem A/V Receivers zostanie usuni\u0119ta" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/pt-BR.json b/homeassistant/components/anthemav/translations/pt-BR.json index dbfe4b35801..096d41e214f 100644 --- a/homeassistant/components/anthemav/translations/pt-BR.json +++ b/homeassistant/components/anthemav/translations/pt-BR.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "A configura\u00e7\u00e3o de receptores A/V Anthem usando YAML est\u00e1 sendo removida. \n\n Sua configura\u00e7\u00e3o YAML existente foi importada para a interface do usu\u00e1rio automaticamente. \n\n Remova a configura\u00e7\u00e3o YAML dos receptores A/V do Anthem do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", - "title": "A configura\u00e7\u00e3o YAML dos receptores A/V do Anthem est\u00e1 sendo removida" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/ru.json b/homeassistant/components/anthemav/translations/ru.json index f56475d331d..0f343609e4c 100644 --- a/homeassistant/components/anthemav/translations/ru.json +++ b/homeassistant/components/anthemav/translations/ru.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 AV-\u0440\u0435\u0441\u0438\u0432\u0435\u0440\u043e\u0432 Anthem \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", - "title": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 AV-\u0440\u0435\u0441\u0438\u0432\u0435\u0440\u043e\u0432 Anthem \u0447\u0435\u0440\u0435\u0437 YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/sv.json b/homeassistant/components/anthemav/translations/sv.json index dd3f6f891e2..cdb8ea7b9fe 100644 --- a/homeassistant/components/anthemav/translations/sv.json +++ b/homeassistant/components/anthemav/translations/sv.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Konfigurering av Anthem A/V-mottagare med YAML tas bort. \n\n Din befintliga YAML-konfiguration har automatiskt importerats till anv\u00e4ndargr\u00e4nssnittet. \n\n Ta bort Anthem A/V Receivers YAML-konfigurationen fr\u00e5n filen configuration.yaml och starta om Home Assistant f\u00f6r att \u00e5tg\u00e4rda problemet.", - "title": "Anthem A/V-mottagarens YAML-konfiguration tas bort" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/tr.json b/homeassistant/components/anthemav/translations/tr.json index c77f5a1f14a..cbe85a5319c 100644 --- a/homeassistant/components/anthemav/translations/tr.json +++ b/homeassistant/components/anthemav/translations/tr.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Anthem A/V Al\u0131c\u0131lar\u0131n\u0131 YAML kullanarak yap\u0131land\u0131rma kald\u0131r\u0131l\u0131yor.\n\nMevcut YAML yap\u0131land\u0131rman\u0131z otomatik olarak kullan\u0131c\u0131 aray\u00fcz\u00fcne aktar\u0131ld\u0131.\n\nAnthem A/V Al\u0131c\u0131lar\u0131 YAML yap\u0131land\u0131rmas\u0131n\u0131 configuration.yaml dosyan\u0131zdan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", - "title": "Anthem A/V Al\u0131c\u0131lar\u0131 YAML yap\u0131land\u0131rmas\u0131 kald\u0131r\u0131l\u0131yor" - } } } \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/zh-Hant.json b/homeassistant/components/anthemav/translations/zh-Hant.json index d68bd690c48..d1b286afd81 100644 --- a/homeassistant/components/anthemav/translations/zh-Hant.json +++ b/homeassistant/components/anthemav/translations/zh-Hant.json @@ -15,11 +15,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a\u7684 Anthem A/V \u63a5\u6536\u5668\u5373\u5c07\u9032\u884c\u79fb\u9664\u3002\n\n\u65e2\u6709\u7684 YAML \u8a2d\u5b9a\u5c07\u81ea\u52d5\u532f\u5165\u81f3 UI \u5167\u3002\n\n\u8acb\u65bc configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Anthem A/V \u63a5\u6536\u5668 YAML \u8a2d\u5b9a\u4e26\u91cd\u65b0\u555f\u52d5 Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", - "title": "Anthem A/V \u63a5\u6536\u5668 YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" - } } } \ No newline at end of file diff --git a/homeassistant/components/awair/translations/bg.json b/homeassistant/components/awair/translations/bg.json index 3f1e4734e29..89e4326353a 100644 --- a/homeassistant/components/awair/translations/bg.json +++ b/homeassistant/components/awair/translations/bg.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "already_configured_account": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "already_configured_device": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e", "no_devices_found": "\u041d\u044f\u043c\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430", @@ -23,9 +22,6 @@ "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 {model} ({device_id})?" }, "local": { - "data": { - "host": "IP \u0430\u0434\u0440\u0435\u0441" - }, "description": "\u0421\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 [\u0442\u0435\u0437\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438]({url}) \u0437\u0430 \u0442\u043e\u0432\u0430 \u043a\u0430\u043a \u0434\u0430 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u0442\u0435 \u043b\u043e\u043a\u0430\u043b\u043d\u0438\u044f API \u043d\u0430 Awair.\n\n\u0429\u0440\u0430\u043a\u043d\u0435\u0442\u0435 \u0432\u044a\u0440\u0445\u0443 \"\u0418\u0417\u041f\u0420\u0410\u0429\u0410\u041d\u0415\", \u043a\u043e\u0433\u0430\u0442\u043e \u0441\u0442\u0435 \u0433\u043e\u0442\u043e\u0432\u0438." }, "local_pick": { @@ -34,20 +30,12 @@ "host": "IP \u0430\u0434\u0440\u0435\u0441" } }, - "reauth": { - "data": { - "email": "\u0418\u043c\u0435\u0439\u043b" - } - }, "reauth_confirm": { "data": { "email": "\u0418\u043c\u0435\u0439\u043b" } }, "user": { - "data": { - "email": "\u0418\u043c\u0435\u0439\u043b" - }, "menu_options": { "cloud": "\u0421\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0447\u0440\u0435\u0437 \u043e\u0431\u043b\u0430\u043a\u0430", "local": "\u0421\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u043b\u043e\u043a\u0430\u043b\u043d\u043e (\u0437\u0430 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u043d\u0435)" diff --git a/homeassistant/components/awair/translations/ca.json b/homeassistant/components/awair/translations/ca.json index 89e461c709d..940233bf405 100644 --- a/homeassistant/components/awair/translations/ca.json +++ b/homeassistant/components/awair/translations/ca.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "El compte ja est\u00e0 configurat", "already_configured_account": "El compte ja est\u00e0 configurat", "already_configured_device": "El dispositiu ja est\u00e0 configurat", "no_devices_found": "No s'han trobat dispositius a la xarxa", @@ -26,9 +25,6 @@ "description": "Vols configurar {model} ({device_id})?" }, "local": { - "data": { - "host": "Adre\u00e7a IP" - }, "description": "Segueix [aquestes instruccions]({url}) per com activar l'API local d'Awair.\n\nPrem 'envia' quan hagis acabat." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "Adre\u00e7a IP" } }, - "reauth": { - "data": { - "access_token": "Token d'acc\u00e9s", - "email": "Correu electr\u00f2nic" - }, - "description": "Torna a introduir el token d'acc\u00e9s de desenvolupador d'Awair." - }, "reauth_confirm": { "data": { "access_token": "Token d'acc\u00e9s", @@ -52,10 +41,6 @@ "description": "Torna a introduir el 'token' d'acc\u00e9s de desenvolupador d'Awair." }, "user": { - "data": { - "access_token": "Token d'acc\u00e9s", - "email": "Correu electr\u00f2nic" - }, "description": "Tria local per a la millor experi\u00e8ncia. Utilitza 'al n\u00favol' si el teu dispositiu no est\u00e0 connectat a la mateixa xarxa que Home Assistant, o si tens un dispositiu antic.", "menu_options": { "cloud": "Connecta't a trav\u00e9s del n\u00favol", diff --git a/homeassistant/components/awair/translations/cs.json b/homeassistant/components/awair/translations/cs.json index 6821388f2d4..7e2b5318288 100644 --- a/homeassistant/components/awair/translations/cs.json +++ b/homeassistant/components/awair/translations/cs.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je ji\u017e nastaven", "already_configured_account": "\u00da\u010det je ji\u017e nastaven", "already_configured_device": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", "no_devices_found": "V s\u00edti nebyla nalezena \u017e\u00e1dn\u00e1 za\u0159\u00edzen\u00ed", @@ -24,27 +23,12 @@ "discovery_confirm": { "description": "Chcete nastavit {model} ({device_id})?" }, - "local": { - "data": { - "host": "IP adresa" - } - }, "local_pick": { "data": { "host": "IP adresa" } }, - "reauth": { - "data": { - "access_token": "P\u0159\u00edstupov\u00fd token", - "email": "E-mail" - } - }, "user": { - "data": { - "access_token": "P\u0159\u00edstupov\u00fd token", - "email": "E-mail" - }, "description": "Pro p\u0159\u00edstupov\u00fd token v\u00fdvoj\u00e1\u0159e Awair se mus\u00edte zaregistrovat na: https://developer.getawair.com/onboard/login" } } diff --git a/homeassistant/components/awair/translations/de.json b/homeassistant/components/awair/translations/de.json index af7aaaafff7..51d86b35618 100644 --- a/homeassistant/components/awair/translations/de.json +++ b/homeassistant/components/awair/translations/de.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "Konto wurde bereits konfiguriert", "already_configured_account": "Konto wurde bereits konfiguriert", "already_configured_device": "Ger\u00e4t ist bereits konfiguriert", "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", @@ -26,9 +25,6 @@ "description": "M\u00f6chtest du {model} ({device_id}) einrichten?" }, "local": { - "data": { - "host": "IP-Adresse" - }, "description": "Befolge [diese Anweisungen]({url}), um die Awair Local API zu aktivieren. \n\nDr\u00fccke abschlie\u00dfend auf Senden." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "IP-Adresse" } }, - "reauth": { - "data": { - "access_token": "Zugangstoken", - "email": "E-Mail" - }, - "description": "Bitte gib deinen Awair-Entwicklerzugriffstoken erneut ein." - }, "reauth_confirm": { "data": { "access_token": "Zugangstoken", @@ -52,10 +41,6 @@ "description": "Bitte gib deinen Awair-Entwicklerzugriffstoken erneut ein." }, "user": { - "data": { - "access_token": "Zugangstoken", - "email": "E-Mail" - }, "description": "W\u00e4hle lokal f\u00fcr die beste Erfahrung. Verwende die Cloud nur, wenn das Ger\u00e4t nicht mit demselben Netzwerk wie Home Assistant verbunden ist oder wenn du ein \u00e4lteres Ger\u00e4t hast.", "menu_options": { "cloud": "Verbindung \u00fcber die Cloud", diff --git a/homeassistant/components/awair/translations/el.json b/homeassistant/components/awair/translations/el.json index 187551f40a3..849dc24270f 100644 --- a/homeassistant/components/awair/translations/el.json +++ b/homeassistant/components/awair/translations/el.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "already_configured_account": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "already_configured_device": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf", @@ -26,9 +25,6 @@ "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {model} ({device_id});" }, "local": { - "data": { - "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP" - }, "description": "\u03a4\u03bf Awair Local API \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ce\u03bd\u03c4\u03b1\u03c2 \u03b1\u03c5\u03c4\u03ac \u03c4\u03b1 \u03b2\u03ae\u03bc\u03b1\u03c4\u03b1: {url}" }, "local_pick": { @@ -37,13 +33,6 @@ "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP" } }, - "reauth": { - "data": { - "access_token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "email": "Email" - }, - "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03bf \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c0\u03c1\u03bf\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1\u03c4\u03b9\u03c3\u03c4\u03ae Awair." - }, "reauth_confirm": { "data": { "access_token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", @@ -52,10 +41,6 @@ "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03bf \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c0\u03c1\u03bf\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1\u03c4\u03b9\u03c3\u03c4\u03ae Awair." }, "user": { - "data": { - "access_token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "email": "Email" - }, "description": "\u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03b5\u03af\u03c4\u03b5 \u03b3\u03b9\u03b1 \u03ad\u03bd\u03b1 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c0\u03c1\u03bf\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1\u03c4\u03b9\u03c3\u03c4\u03ae Awair \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7: https://developer.getawair.com/onboard/login", "menu_options": { "cloud": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03bc\u03ad\u03c3\u03c9 cloud", diff --git a/homeassistant/components/awair/translations/en.json b/homeassistant/components/awair/translations/en.json index dfc06d2346a..9746eac4ca1 100644 --- a/homeassistant/components/awair/translations/en.json +++ b/homeassistant/components/awair/translations/en.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "Account is already configured", "already_configured_account": "Account is already configured", "already_configured_device": "Device is already configured", "no_devices_found": "No devices found on the network", @@ -26,9 +25,6 @@ "description": "Do you want to setup {model} ({device_id})?" }, "local": { - "data": { - "host": "IP Address" - }, "description": "Follow [these instructions]({url}) on how to enable the Awair Local API.\n\nClick submit when done." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "IP Address" } }, - "reauth": { - "data": { - "access_token": "Access Token", - "email": "Email" - }, - "description": "Please re-enter your Awair developer access token." - }, "reauth_confirm": { "data": { "access_token": "Access Token", @@ -52,10 +41,6 @@ "description": "Please re-enter your Awair developer access token." }, "user": { - "data": { - "access_token": "Access Token", - "email": "Email" - }, "description": "Pick local for the best experience. Only use cloud if the device is not connected to the same network as Home Assistant, or if you have a legacy device.", "menu_options": { "cloud": "Connect via the cloud", diff --git a/homeassistant/components/awair/translations/es-419.json b/homeassistant/components/awair/translations/es-419.json index f487cd397c4..a38eb327d52 100644 --- a/homeassistant/components/awair/translations/es-419.json +++ b/homeassistant/components/awair/translations/es-419.json @@ -1,9 +1,6 @@ { "config": { "step": { - "reauth": { - "description": "Vuelva a ingresar su token de acceso de desarrollador de Awair." - }, "user": { "description": "Debe registrarse para obtener un token de acceso de desarrollador de Awair en: https://developer.getawair.com/onboard/login" } diff --git a/homeassistant/components/awair/translations/es.json b/homeassistant/components/awair/translations/es.json index 1f2508ec6e3..0e1efdc6f49 100644 --- a/homeassistant/components/awair/translations/es.json +++ b/homeassistant/components/awair/translations/es.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "La cuenta ya est\u00e1 configurada", "already_configured_account": "La cuenta ya est\u00e1 configurada", "already_configured_device": "El dispositivo ya est\u00e1 configurado", "no_devices_found": "No se encontraron dispositivos en la red", @@ -26,9 +25,6 @@ "description": "\u00bfQuieres configurar {model} ({device_id})?" }, "local": { - "data": { - "host": "Direcci\u00f3n IP" - }, "description": "Sigue [estas instrucciones]({url}) sobre c\u00f3mo habilitar la API local de Awair. \n\nHaz clic en enviar cuando hayas terminado." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "Direcci\u00f3n IP" } }, - "reauth": { - "data": { - "access_token": "Token de acceso", - "email": "Correo electr\u00f3nico" - }, - "description": "Por favor, vuelve a introducir tu token de acceso de desarrollador Awair." - }, "reauth_confirm": { "data": { "access_token": "Token de acceso", @@ -52,10 +41,6 @@ "description": "Por favor, vuelve a introducir tu token de acceso de desarrollador Awair." }, "user": { - "data": { - "access_token": "Token de acceso", - "email": "Correo electr\u00f3nico" - }, "description": "Elige local para la mejor experiencia. Usa solo la nube si el dispositivo no est\u00e1 conectado a la misma red que Home Assistant, o si tienes un dispositivo heredado.", "menu_options": { "cloud": "Conectar a trav\u00e9s de la nube", diff --git a/homeassistant/components/awair/translations/et.json b/homeassistant/components/awair/translations/et.json index 327379ace6e..13f8b2115b6 100644 --- a/homeassistant/components/awair/translations/et.json +++ b/homeassistant/components/awair/translations/et.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "Konto on juba seadistatud", "already_configured_account": "Konto on juba seadistatud", "already_configured_device": "Seade on juba h\u00e4\u00e4lestatud", "no_devices_found": "V\u00f5rgust ei leitud Awair seadmeid", @@ -26,9 +25,6 @@ "description": "Kas seadistada {model} ({device_id})?" }, "local": { - "data": { - "host": "IP aadress" - }, "description": "J\u00e4rgi [neid juhiseid]({url}) Awair Local API lubamise kohta.\n\nKui oled l\u00f5petanud, kl\u00f5psa nuppu Esita." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "IP aadress" } }, - "reauth": { - "data": { - "access_token": "Juurdep\u00e4\u00e4sut\u00f5end", - "email": "E-post" - }, - "description": "Taassisesta oma Awairi arendaja juurdep\u00e4\u00e4suluba." - }, "reauth_confirm": { "data": { "access_token": "Juurdep\u00e4\u00e4sut\u00f5end", @@ -52,10 +41,6 @@ "description": "Taassisesta oma Awairi arendaja juurdep\u00e4\u00e4suluba." }, "user": { - "data": { - "access_token": "Juurdep\u00e4\u00e4sut\u00f5end", - "email": "E-post" - }, "description": "Parima kogemuse saamiseks vali kohalik \u00fchendus. Kasuta pilve ainult siis, kui seade ei ole \u00fchendatud samasse v\u00f5rku kui Home Assistant v\u00f5i kui on vanem seade.", "menu_options": { "cloud": "Pilve\u00fchendus", diff --git a/homeassistant/components/awair/translations/fi.json b/homeassistant/components/awair/translations/fi.json index edbdcb1b086..f17986ff80a 100644 --- a/homeassistant/components/awair/translations/fi.json +++ b/homeassistant/components/awair/translations/fi.json @@ -5,11 +5,6 @@ "data": { "email": "S\u00e4hk\u00f6posti" } - }, - "local": { - "data": { - "host": "IP Osoite" - } } } } diff --git a/homeassistant/components/awair/translations/fr.json b/homeassistant/components/awair/translations/fr.json index 5d9636eccee..d4b420ee7ae 100644 --- a/homeassistant/components/awair/translations/fr.json +++ b/homeassistant/components/awair/translations/fr.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9", "already_configured_account": "Le compte est d\u00e9j\u00e0 configur\u00e9", "already_configured_device": "L'appareil est d\u00e9j\u00e0 configur\u00e9", "no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau", @@ -26,9 +25,6 @@ "description": "Voulez-vous configurer {model} ({device_id})\u00a0?" }, "local": { - "data": { - "host": "Adresse IP" - }, "description": "Suivez [ces instructions]({url}) pour activer l\u2019API locale Awair.\n\nCliquez sur \u00ab\u00a0Valider\u00a0\u00bb apr\u00e8s avoir termin\u00e9." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "Adresse IP" } }, - "reauth": { - "data": { - "access_token": "Jeton d'acc\u00e8s", - "email": "Courriel" - }, - "description": "Veuillez ressaisir votre jeton d'acc\u00e8s d\u00e9veloppeur Awair." - }, "reauth_confirm": { "data": { "access_token": "Jeton d'acc\u00e8s", @@ -52,10 +41,6 @@ "description": "Veuillez ressaisir votre jeton d'acc\u00e8s d\u00e9veloppeur Awair." }, "user": { - "data": { - "access_token": "Jeton d'acc\u00e8s", - "email": "Courriel" - }, "description": "S\u00e9lectionnez le mode local pour une exp\u00e9rience optimale. S\u00e9lectionnez le mode cloud uniquement si l'appareil n'est pas connect\u00e9 au m\u00eame r\u00e9seau que Home Assistant ou si vous disposez d'un appareil ancien.", "menu_options": { "cloud": "Connexion cloud", diff --git a/homeassistant/components/awair/translations/he.json b/homeassistant/components/awair/translations/he.json index 387e7fe9a62..f5a985463d1 100644 --- a/homeassistant/components/awair/translations/he.json +++ b/homeassistant/components/awair/translations/he.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" }, @@ -16,23 +15,11 @@ "host": "\u05db\u05ea\u05d5\u05d1\u05ea IP" } }, - "reauth": { - "data": { - "access_token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4", - "email": "\u05d3\u05d5\u05d0\"\u05dc" - } - }, "reauth_confirm": { "data": { "access_token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4", "email": "\u05d3\u05d5\u05d0\"\u05dc" } - }, - "user": { - "data": { - "access_token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4", - "email": "\u05d3\u05d5\u05d0\"\u05dc" - } } } } diff --git a/homeassistant/components/awair/translations/hu.json b/homeassistant/components/awair/translations/hu.json index 2e7be72e57f..fd720372ac3 100644 --- a/homeassistant/components/awair/translations/hu.json +++ b/homeassistant/components/awair/translations/hu.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van", "already_configured_account": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van", "already_configured_device": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", @@ -26,9 +25,6 @@ "description": "Be\u00e1ll\u00edtja a k\u00f6vetkez\u0151t: {model} ({device_id})?" }, "local": { - "data": { - "host": "IP c\u00edm" - }, "description": "K\u00f6vesse [az utas\u00edt\u00e1sokat]({url}) az Awair lok\u00e1lis API enged\u00e9lyez\u00e9s\u00e9hez. \n\n Ha k\u00e9sz, kattintson a MEHET gombra." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "IP c\u00edm" } }, - "reauth": { - "data": { - "access_token": "Hozz\u00e1f\u00e9r\u00e9si token", - "email": "E-mail" - }, - "description": "Adja meg \u00fajra az Awair fejleszt\u0151i hozz\u00e1f\u00e9r\u00e9si tokent." - }, "reauth_confirm": { "data": { "access_token": "Hozz\u00e1f\u00e9r\u00e9si token", @@ -52,10 +41,6 @@ "description": "Adja meg \u00fajra az Awair fejleszt\u0151i hozz\u00e1f\u00e9r\u00e9si tokent." }, "user": { - "data": { - "access_token": "Hozz\u00e1f\u00e9r\u00e9si token", - "email": "E-mail" - }, "description": "V\u00e1lassza a helyi lehet\u0151s\u00e9get a legjobb \u00e9lm\u00e9ny \u00e9rdek\u00e9ben. Csak akkor haszn\u00e1lja a felh\u0151t, ha az eszk\u00f6z nem ugyanahhoz a h\u00e1l\u00f3zathoz csatlakozik, mint a Home Assistant, vagy ha r\u00e9gebbi eszk\u00f6zzel rendelkezik.", "menu_options": { "cloud": "Felh\u0151n kereszt\u00fcli csatlakoz\u00e1s", diff --git a/homeassistant/components/awair/translations/id.json b/homeassistant/components/awair/translations/id.json index 3d633070f20..b613c3fb833 100644 --- a/homeassistant/components/awair/translations/id.json +++ b/homeassistant/components/awair/translations/id.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "Akun sudah dikonfigurasi", "already_configured_account": "Akun sudah dikonfigurasi", "already_configured_device": "Perangkat sudah dikonfigurasi", "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan", @@ -26,9 +25,6 @@ "description": "Ingin menyiapkan {model} ({device_id})?" }, "local": { - "data": { - "host": "Alamat IP" - }, "description": "Ikuti [petunjuk ini]( {url} ) untuk mengaktifkan API Lokal Awair. \n\n Klik kirim setelah selesai." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "Alamat IP" } }, - "reauth": { - "data": { - "access_token": "Token Akses", - "email": "Email" - }, - "description": "Masukkan kembali token akses pengembang Awair Anda." - }, "reauth_confirm": { "data": { "access_token": "Token Akses", @@ -52,10 +41,6 @@ "description": "Masukkan kembali token akses pengembang Awair Anda." }, "user": { - "data": { - "access_token": "Token Akses", - "email": "Email" - }, "description": "Pilih lokal untuk pengalaman terbaik. Hanya gunakan opsi cloud jika perangkat tidak terhubung ke jaringan yang sama dengan Home Assistant, atau jika Anda memiliki perangkat versi lawas.", "menu_options": { "cloud": "Terhubung melalui cloud", diff --git a/homeassistant/components/awair/translations/it.json b/homeassistant/components/awair/translations/it.json index c8c1f1f6ec6..fb090e6d222 100644 --- a/homeassistant/components/awair/translations/it.json +++ b/homeassistant/components/awair/translations/it.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "L'account \u00e8 gi\u00e0 configurato", "already_configured_account": "L'account \u00e8 gi\u00e0 configurato", "already_configured_device": "Il dispositivo \u00e8 gi\u00e0 configurato", "no_devices_found": "Nessun dispositivo trovato sulla rete", @@ -26,9 +25,6 @@ "description": "Vuoi configurare {model} ({device_id})?" }, "local": { - "data": { - "host": "Indirizzo IP" - }, "description": "Segui [queste istruzioni]({url}) su come abilitare l'API Awair Local. \n\n Fai clic su Invia quando hai finito." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "Indirizzo IP" } }, - "reauth": { - "data": { - "access_token": "Token di accesso", - "email": "Email" - }, - "description": "Inserisci nuovamente il tuo token di accesso per sviluppatori Awair." - }, "reauth_confirm": { "data": { "access_token": "Token di accesso", @@ -52,10 +41,6 @@ "description": "Inserisci nuovamente il tuo token di accesso sviluppatore Awair." }, "user": { - "data": { - "access_token": "Token di accesso", - "email": "Email" - }, "description": "Scegli locale per la migliore esperienza. Utilizza il cloud solo se il dispositivo non \u00e8 connesso alla stessa rete di Home Assistant o se disponi di un dispositivo legacy.", "menu_options": { "cloud": "Connettiti tramite il cloud", diff --git a/homeassistant/components/awair/translations/ja.json b/homeassistant/components/awair/translations/ja.json index 11c151082b9..a222204084a 100644 --- a/homeassistant/components/awair/translations/ja.json +++ b/homeassistant/components/awair/translations/ja.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "already_configured_account": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "already_configured_device": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093", @@ -26,9 +25,6 @@ "description": "{model} ({device_id}) \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f" }, "local": { - "data": { - "host": "IP\u30a2\u30c9\u30ec\u30b9" - }, "description": "[\u3053\u308c\u3089\u306e\u624b\u9806]( {url} ) \u306b\u5f93\u3063\u3066\u3001Awair Local API \u3092\u6709\u52b9\u306b\u3059\u308b\u65b9\u6cd5\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 \n\n\u5b8c\u4e86\u3057\u305f\u3089\u9001\u4fe1\u3092\u30af\u30ea\u30c3\u30af\u3057\u307e\u3059\u3002" }, "local_pick": { @@ -37,13 +33,6 @@ "host": "IP\u30a2\u30c9\u30ec\u30b9" } }, - "reauth": { - "data": { - "access_token": "\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3", - "email": "E\u30e1\u30fc\u30eb" - }, - "description": "Awair developer access token\u3092\u518d\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" - }, "reauth_confirm": { "data": { "access_token": "\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3", @@ -52,10 +41,6 @@ "description": "Awair developer access token\u3092\u518d\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "user": { - "data": { - "access_token": "\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3", - "email": "E\u30e1\u30fc\u30eb" - }, "description": "Awair developer access token\u306e\u767b\u9332\u306f\u4ee5\u4e0b\u306e\u30b5\u30a4\u30c8\u3067\u884c\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059: https://developer.getawair.com/onboard/login", "menu_options": { "cloud": "\u30af\u30e9\u30a6\u30c9\u7d4c\u7531\u3067\u63a5\u7d9a", diff --git a/homeassistant/components/awair/translations/ko.json b/homeassistant/components/awair/translations/ko.json index 22677f8ab45..0f9f06d9e46 100644 --- a/homeassistant/components/awair/translations/ko.json +++ b/homeassistant/components/awair/translations/ko.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, @@ -10,18 +9,7 @@ "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { - "reauth": { - "data": { - "access_token": "\uc561\uc138\uc2a4 \ud1a0\ud070", - "email": "\uc774\uba54\uc77c" - }, - "description": "Awair \uac1c\ubc1c\uc790 \uc561\uc138\uc2a4 \ud1a0\ud070\uc744 \ub2e4\uc2dc \uc785\ub825\ud574\uc8fc\uc138\uc694." - }, "user": { - "data": { - "access_token": "\uc561\uc138\uc2a4 \ud1a0\ud070", - "email": "\uc774\uba54\uc77c" - }, "description": "https://developer.getawair.com/onboard/login \uc5d0 Awair \uac1c\ubc1c\uc790 \uc561\uc138\uc2a4 \ud1a0\ud070\uc744 \ub4f1\ub85d\ud574\uc57c\ud569\ub2c8\ub2e4" } } diff --git a/homeassistant/components/awair/translations/lb.json b/homeassistant/components/awair/translations/lb.json index cb2f758113a..3807a8b35a8 100644 --- a/homeassistant/components/awair/translations/lb.json +++ b/homeassistant/components/awair/translations/lb.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "Kont ass", "no_devices_found": "Keng Apparater am Netzwierk fonnt", "reauth_successful": "Erfollegr\u00e4ich aktualis\u00e9iert" }, @@ -10,18 +9,7 @@ "unknown": "Onerwaarte Feeler" }, "step": { - "reauth": { - "data": { - "access_token": "Acc\u00e8s Jeton", - "email": "E-Mail" - }, - "description": "G\u00ebff d\u00e4in Awair Developpeur Acc\u00e8s jeton nach emol un." - }, "user": { - "data": { - "access_token": "Acc\u00e8s Jeton", - "email": "E-Mail" - }, "description": "Du muss dech fir een Awair Developpeur Acc\u00e8s Jeton registr\u00e9ien op:\nhttps://developer.getawair.com/onboard/login" } } diff --git a/homeassistant/components/awair/translations/nl.json b/homeassistant/components/awair/translations/nl.json index 7c70960c1b1..c3d1e1d9583 100644 --- a/homeassistant/components/awair/translations/nl.json +++ b/homeassistant/components/awair/translations/nl.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "Account is al geconfigureerd", "already_configured_account": "Account is al geconfigureerd", "already_configured_device": "Apparaat is al geconfigureerd", "no_devices_found": "Geen apparaten gevonden op het netwerk", @@ -21,24 +20,12 @@ "email": "E-mail" } }, - "local": { - "data": { - "host": "IP-adres" - } - }, "local_pick": { "data": { "device": "Apparaat", "host": "IP-adres" } }, - "reauth": { - "data": { - "access_token": "Toegangstoken", - "email": "E-mail" - }, - "description": "Voer uw Awair-ontwikkelaarstoegangstoken opnieuw in." - }, "reauth_confirm": { "data": { "access_token": "Toegangstoken", @@ -46,10 +33,6 @@ } }, "user": { - "data": { - "access_token": "Toegangstoken", - "email": "E-mail" - }, "description": "U moet zich registreren voor een Awair-toegangstoken voor ontwikkelaars op: https://developer.getawair.com/onboard/login", "menu_options": { "cloud": "Verbinden via de cloud" diff --git a/homeassistant/components/awair/translations/no.json b/homeassistant/components/awair/translations/no.json index 983a47ecfed..636060df9da 100644 --- a/homeassistant/components/awair/translations/no.json +++ b/homeassistant/components/awair/translations/no.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "Kontoen er allerede konfigurert", "already_configured_account": "Kontoen er allerede konfigurert", "already_configured_device": "Enheten er allerede konfigurert", "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket", @@ -26,9 +25,6 @@ "description": "Vil du konfigurere {model} ( {device_id} )?" }, "local": { - "data": { - "host": "IP adresse" - }, "description": "F\u00f8lg [disse instruksjonene]( {url} ) om hvordan du aktiverer Awair Local API. \n\n Klikk p\u00e5 send n\u00e5r du er ferdig." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "IP adresse" } }, - "reauth": { - "data": { - "access_token": "Tilgangstoken", - "email": "E-post" - }, - "description": "Skriv inn tilgangstokenet for Awair-utviklere p\u00e5 nytt." - }, "reauth_confirm": { "data": { "access_token": "Tilgangstoken", @@ -52,10 +41,6 @@ "description": "Skriv inn Awair-utviklertilgangstokenet ditt p\u00e5 nytt." }, "user": { - "data": { - "access_token": "Tilgangstoken", - "email": "E-post" - }, "description": "Velg lokal for den beste opplevelsen. Bruk bare sky hvis enheten ikke er koblet til samme nettverk som Home Assistant, eller hvis du har en eldre enhet.", "menu_options": { "cloud": "Koble til via skyen", diff --git a/homeassistant/components/awair/translations/pl.json b/homeassistant/components/awair/translations/pl.json index c42f9863b1c..072d8871daf 100644 --- a/homeassistant/components/awair/translations/pl.json +++ b/homeassistant/components/awair/translations/pl.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "Konto jest ju\u017c skonfigurowane", "already_configured_account": "Konto jest ju\u017c skonfigurowane", "already_configured_device": "Urz\u0105dzenie jest ju\u017c skonfigurowane", "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci", @@ -26,9 +25,6 @@ "description": "Czy chcesz skonfigurowa\u0107 {model} ({device_id})?" }, "local": { - "data": { - "host": "Adres IP" - }, "description": "Post\u0119puj zgodnie z [tymi instrukcjami]({url}), aby dowiedzie\u0107 si\u0119, jak w\u0142\u0105czy\u0107 lokalny interfejs API Awair. \n\n Po zako\u0144czeniu kliknij Zatwierd\u017a." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "Adres IP" } }, - "reauth": { - "data": { - "access_token": "Token dost\u0119pu", - "email": "Adres e-mail" - }, - "description": "Wprowad\u017a ponownie token dost\u0119pu programisty Awair." - }, "reauth_confirm": { "data": { "access_token": "Token dost\u0119pu", @@ -52,10 +41,6 @@ "description": "Wprowad\u017a ponownie token dost\u0119pu programisty Awair." }, "user": { - "data": { - "access_token": "Token dost\u0119pu", - "email": "Adres e-mail" - }, "description": "Wybierz lokalny, aby uzyska\u0107 najlepsze efekty. Korzystaj z chmury tylko wtedy, gdy urz\u0105dzenie nie jest pod\u0142\u0105czone do tej samej sieci co Home Assistant lub je\u015bli masz starsze urz\u0105dzenie.", "menu_options": { "cloud": "Po\u0142\u0105cz si\u0119 przez chmur\u0119", diff --git a/homeassistant/components/awair/translations/pt-BR.json b/homeassistant/components/awair/translations/pt-BR.json index 109f123f9e2..36b2fa61ebf 100644 --- a/homeassistant/components/awair/translations/pt-BR.json +++ b/homeassistant/components/awair/translations/pt-BR.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "A conta j\u00e1 foi configurada", "already_configured_account": "A conta j\u00e1 foi configurada", "already_configured_device": "Dispositivo j\u00e1 est\u00e1 configurado", "no_devices_found": "Nenhum dispositivo encontrado na rede", @@ -26,9 +25,6 @@ "description": "Deseja configurar {model} ({device_id})?" }, "local": { - "data": { - "host": "Endere\u00e7o IP" - }, "description": "Siga [estas instru\u00e7\u00f5es]({url}) sobre como ativar a API local Awair. \n\n Clique em enviar quando terminar." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "Endere\u00e7o IP" } }, - "reauth": { - "data": { - "access_token": "Token de acesso", - "email": "Email" - }, - "description": "Insira novamente seu token de acesso de desenvolvedor Awair." - }, "reauth_confirm": { "data": { "access_token": "Token de acesso", @@ -52,10 +41,6 @@ "description": "Insira novamente seu token de acesso de desenvolvedor Awair." }, "user": { - "data": { - "access_token": "Token de acesso", - "email": "Email" - }, "description": "Escolha local para a melhor experi\u00eancia. Use a nuvem apenas se o dispositivo n\u00e3o estiver conectado \u00e0 mesma rede que o Home Assistant ou se voc\u00ea tiver um dispositivo legado.", "menu_options": { "cloud": "Conecte-se pela nuvem", diff --git a/homeassistant/components/awair/translations/pt.json b/homeassistant/components/awair/translations/pt.json index 1f922776179..085b6dd056d 100644 --- a/homeassistant/components/awair/translations/pt.json +++ b/homeassistant/components/awair/translations/pt.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "Conta j\u00e1 configurada", "already_configured_account": "Conta j\u00e1 configurada", "already_configured_device": "O dispositivo j\u00e1 est\u00e1 configurado", "no_devices_found": "Nenhum dispositivo encontrado na rede", @@ -26,9 +25,6 @@ "description": "Deseja configurar {model} ( {device_id} )?" }, "local": { - "data": { - "host": "Endere\u00e7o IP" - }, "description": "Siga [estas instru\u00e7\u00f5es]( {url} ) sobre como ativar a API local Awair. \n\n Clique em enviar quando terminar." }, "local_pick": { @@ -37,12 +33,6 @@ "host": "Endere\u00e7o IP" } }, - "reauth": { - "data": { - "access_token": "Token de Acesso", - "email": "Email" - } - }, "reauth_confirm": { "data": { "access_token": "Token de Acesso", @@ -50,10 +40,6 @@ } }, "user": { - "data": { - "access_token": "Token de Acesso", - "email": "Email" - }, "menu_options": { "cloud": "Conecte-se pela nuvem", "local": "Conecte-se localmente (preferencial)" diff --git a/homeassistant/components/awair/translations/ru.json b/homeassistant/components/awair/translations/ru.json index d59c4ed50f1..1725669af20 100644 --- a/homeassistant/components/awair/translations/ru.json +++ b/homeassistant/components/awair/translations/ru.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", "already_configured_account": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", "already_configured_device": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", @@ -26,9 +25,6 @@ "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 {model} ({device_id})?" }, "local": { - "data": { - "host": "IP-\u0430\u0434\u0440\u0435\u0441" - }, "description": "\u0421\u043b\u0435\u0434\u0443\u0439\u0442\u0435 [\u044d\u0442\u0438\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c]({url}), \u0447\u0442\u043e\u0431\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 API Awair.\n\n\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u041f\u041e\u0414\u0422\u0412\u0415\u0420\u0414\u0418\u0422\u042c \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "IP-\u0430\u0434\u0440\u0435\u0441" } }, - "reauth": { - "data": { - "access_token": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430", - "email": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b" - }, - "description": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0412\u0430\u0448 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430." - }, "reauth_confirm": { "data": { "access_token": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430", @@ -52,10 +41,6 @@ "description": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0412\u0430\u0448 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430." }, "user": { - "data": { - "access_token": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430", - "email": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b" - }, "description": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043e\u0431\u043b\u0430\u043a\u043e, \u0435\u0441\u043b\u0438 \u0443 \u0412\u0430\u0441 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0435\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e, \u0438\u043b\u0438 \u0435\u0441\u043b\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e \u043a \u0442\u043e\u0439 \u0436\u0435 \u0441\u0435\u0442\u0438, \u0447\u0442\u043e \u0438 Home Assistant. \u0412 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u043e\u0442\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0435\u043d\u0438\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e.", "menu_options": { "cloud": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u043e\u0431\u043b\u0430\u043a\u043e", diff --git a/homeassistant/components/awair/translations/sk.json b/homeassistant/components/awair/translations/sk.json index 38e668addf9..d80daa08883 100644 --- a/homeassistant/components/awair/translations/sk.json +++ b/homeassistant/components/awair/translations/sk.json @@ -10,28 +10,11 @@ "discovery_confirm": { "description": "Chcete nastavi\u0165 {model} ({device_id})?" }, - "local": { - "data": { - "host": "IP adresa" - } - }, "local_pick": { "data": { "device": "Zaradenie", "host": "IP adresa" } - }, - "reauth": { - "data": { - "access_token": "Pr\u00edstupov\u00fd token", - "email": "Email" - } - }, - "user": { - "data": { - "access_token": "Pr\u00edstupov\u00fd token", - "email": "Email" - } } } } diff --git a/homeassistant/components/awair/translations/sv.json b/homeassistant/components/awair/translations/sv.json index 3fbdec53f83..a0fc18ac01b 100644 --- a/homeassistant/components/awair/translations/sv.json +++ b/homeassistant/components/awair/translations/sv.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "Konto har redan konfigurerats", "already_configured_account": "Konto har redan konfigurerats", "already_configured_device": "Enheten \u00e4r redan konfigurerad", "no_devices_found": "Inga enheter hittades i n\u00e4tverket", @@ -26,9 +25,6 @@ "description": "Vill du konfigurera {model} ( {device_id} )?" }, "local": { - "data": { - "host": "IP-adress" - }, "description": "F\u00f6lj [dessa instruktioner]({url}) om hur du aktiverar Awair Local API.\n\nKlicka p\u00e5 skicka n\u00e4r du \u00e4r klar." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "IP-adress" } }, - "reauth": { - "data": { - "access_token": "\u00c5tkomstnyckel", - "email": "E-post" - }, - "description": "Ange din Awair-utvecklar\u00e5tkomsttoken igen." - }, "reauth_confirm": { "data": { "access_token": "\u00c5tkomsttoken", @@ -52,10 +41,6 @@ "description": "Ange din Awair-utvecklar\u00e5tkomsttoken igen." }, "user": { - "data": { - "access_token": "\u00c5tkomstnyckel", - "email": "E-post" - }, "description": "Du m\u00e5ste registrera dig f\u00f6r en Awair-utvecklar\u00e5tkomsttoken p\u00e5: https://developer.getawair.com/onboard/login", "menu_options": { "cloud": "Anslut via molnet", diff --git a/homeassistant/components/awair/translations/tr.json b/homeassistant/components/awair/translations/tr.json index 87f90a564bc..7f9f025a566 100644 --- a/homeassistant/components/awair/translations/tr.json +++ b/homeassistant/components/awair/translations/tr.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "already_configured_account": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "already_configured_device": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "no_devices_found": "A\u011fda cihaz bulunamad\u0131", @@ -26,9 +25,6 @@ "description": "{model} ( {device_id} ) kurulumunu yapmak istiyor musunuz?" }, "local": { - "data": { - "host": "IP Adresi" - }, "description": "Awair Yerel API'sinin nas\u0131l etkinle\u015ftirilece\u011fiyle ilgili [bu talimatlar\u0131]( {url} ) uygulay\u0131n. \n\n \u0130\u015finiz bitti\u011finde g\u00f6nder'i t\u0131klay\u0131n." }, "local_pick": { @@ -37,13 +33,6 @@ "host": "IP Adresi" } }, - "reauth": { - "data": { - "access_token": "Eri\u015fim Anahtar\u0131", - "email": "E-posta" - }, - "description": "L\u00fctfen Awair geli\u015ftirici eri\u015fim anahtar\u0131n\u0131 yeniden girin." - }, "reauth_confirm": { "data": { "access_token": "Eri\u015fim Anahtar\u0131", @@ -52,10 +41,6 @@ "description": "L\u00fctfen Awair geli\u015ftirici eri\u015fim anahtar\u0131n\u0131 yeniden girin." }, "user": { - "data": { - "access_token": "Eri\u015fim Anahtar\u0131", - "email": "E-posta" - }, "description": "En iyi deneyim i\u00e7in yerel se\u00e7in. Bulutu yaln\u0131zca cihaz Home Assistant ile ayn\u0131 a\u011fa ba\u011fl\u0131 de\u011filse veya eski bir cihaz\u0131n\u0131z varsa kullan\u0131n.", "menu_options": { "cloud": "Bulut \u00fczerinden ba\u011flan\u0131n", diff --git a/homeassistant/components/awair/translations/uk.json b/homeassistant/components/awair/translations/uk.json index f8150ad7faf..d14e748df6f 100644 --- a/homeassistant/components/awair/translations/uk.json +++ b/homeassistant/components/awair/translations/uk.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "\u0426\u0435\u0439 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u0438\u0439 \u0437\u0430\u043f\u0438\u0441 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u043e \u0432 Home Assistant.", "no_devices_found": "\u041f\u0440\u0438\u0441\u0442\u0440\u043e\u0457 \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u0456 \u0432 \u043c\u0435\u0440\u0435\u0436\u0456.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f \u043f\u0440\u043e\u0439\u0448\u043b\u0430 \u0443\u0441\u043f\u0456\u0448\u043d\u043e" }, @@ -10,18 +9,7 @@ "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" }, "step": { - "reauth": { - "data": { - "access_token": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0443", - "email": "\u0410\u0434\u0440\u0435\u0441\u0430 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0457 \u043f\u043e\u0448\u0442\u0438" - }, - "description": "\u0411\u0443\u0434\u044c \u043b\u0430\u0441\u043a\u0430, \u0432\u0432\u0435\u0434\u0456\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0412\u0430\u0448 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0443." - }, "user": { - "data": { - "access_token": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0443", - "email": "\u0410\u0434\u0440\u0435\u0441\u0430 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0457 \u043f\u043e\u0448\u0442\u0438" - }, "description": "\u0414\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0443 \u0434\u043e Awair \u0412\u0438 \u043f\u043e\u0432\u0438\u043d\u043d\u0456 \u0437\u0430\u0440\u0435\u0454\u0441\u0442\u0440\u0443\u0432\u0430\u0442\u0438\u0441\u044f \u0437\u0430 \u0430\u0434\u0440\u0435\u0441\u043e\u044e: https://developer.getawair.com/onboard/login" } } diff --git a/homeassistant/components/awair/translations/zh-Hant.json b/homeassistant/components/awair/translations/zh-Hant.json index fb6e6acf9cb..d97564cf778 100644 --- a/homeassistant/components/awair/translations/zh-Hant.json +++ b/homeassistant/components/awair/translations/zh-Hant.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "already_configured_account": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "already_configured_device": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e", @@ -26,9 +25,6 @@ "description": "\u662f\u5426\u8981\u8a2d\u5b9a {model} ({device_id})\uff1f" }, "local": { - "data": { - "host": "IP \u4f4d\u5740" - }, "description": "\u8ddf\u96a8 [\u4ee5\u4e0b\u6b65\u9a5f]({url}) \u4ee5\u555f\u7528 Awair \u672c\u5730\u7aef API\u3002\n\n\u5b8c\u6210\u5f8c\u9ede\u9078\u50b3\u9001\u3002" }, "local_pick": { @@ -37,13 +33,6 @@ "host": "IP \u4f4d\u5740" } }, - "reauth": { - "data": { - "access_token": "\u5b58\u53d6\u6b0a\u6756", - "email": "\u96fb\u5b50\u90f5\u4ef6" - }, - "description": "\u8acb\u91cd\u65b0\u8f38\u5165 Awair \u958b\u767c\u8005\u5b58\u53d6\u6b0a\u6756\u3002" - }, "reauth_confirm": { "data": { "access_token": "\u5b58\u53d6\u6b0a\u6756", @@ -52,10 +41,6 @@ "description": "\u8acb\u91cd\u65b0\u8f38\u5165 Awair \u958b\u767c\u8005\u5b58\u53d6\u6b0a\u6756\u3002" }, "user": { - "data": { - "access_token": "\u5b58\u53d6\u6b0a\u6756", - "email": "\u96fb\u5b50\u90f5\u4ef6" - }, "description": "\u9078\u64c7\u672c\u5730\u7aef\u4ee5\u7372\u5f97\u6700\u4f73\u4f7f\u7528\u9ad4\u9a57\u3002\u50c5\u65bc\u88dd\u7f6e\u8207 Home Assistant \u4e26\u975e\u8655\u65bc\u76f8\u540c\u7db2\u8def\u4e0b\u3001\u6216\u8005\u4f7f\u7528\u820a\u8a2d\u5099\u7684\u60c5\u6cc1\u4e0b\u4f7f\u7528\u96f2\u7aef\u3002", "menu_options": { "cloud": "\u900f\u904e\u96f2\u7aef\u9023\u7dda", diff --git a/homeassistant/components/bluetooth/translations/bg.json b/homeassistant/components/bluetooth/translations/bg.json index 7da387cae4c..6bcfe5a8b87 100644 --- a/homeassistant/components/bluetooth/translations/bg.json +++ b/homeassistant/components/bluetooth/translations/bg.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 {name}?" }, - "enable_bluetooth": { - "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 Bluetooth?" - }, "multiple_adapters": { "data": { "adapter": "\u0410\u0434\u0430\u043f\u0442\u0435\u0440" @@ -38,7 +35,6 @@ "step": { "init": { "data": { - "adapter": "Bluetooth \u0430\u0434\u0430\u043f\u0442\u0435\u0440, \u043a\u043e\u0439\u0442\u043e \u0434\u0430 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u0437\u0430 \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u043d\u0435", "passive": "\u041f\u0430\u0441\u0438\u0432\u043d\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u043d\u0435" } } diff --git a/homeassistant/components/bluetooth/translations/ca.json b/homeassistant/components/bluetooth/translations/ca.json index 8c124446672..b7e3bc3eea3 100644 --- a/homeassistant/components/bluetooth/translations/ca.json +++ b/homeassistant/components/bluetooth/translations/ca.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "Vols configurar {name}?" }, - "enable_bluetooth": { - "description": "Vols configurar Bluetooth?" - }, "multiple_adapters": { "data": { "adapter": "Adaptador" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "Adaptador Bluetooth a utilitzar per escanejar", "passive": "Escaneig passiu" - }, - "description": "L'escolta passiva necessita BlueZ 5.63 o posterior i les funcions experimentals activades." + } } } } diff --git a/homeassistant/components/bluetooth/translations/cs.json b/homeassistant/components/bluetooth/translations/cs.json index e53690d3458..005d948fbcf 100644 --- a/homeassistant/components/bluetooth/translations/cs.json +++ b/homeassistant/components/bluetooth/translations/cs.json @@ -5,9 +5,6 @@ "bluetooth_confirm": { "description": "Chcete nastavit {name}?" }, - "enable_bluetooth": { - "description": "Chcete nastavit Bluetooth?" - }, "single_adapter": { "description": "Chcete nastavit Bluetooth adapt\u00e9r {name}?" }, diff --git a/homeassistant/components/bluetooth/translations/de.json b/homeassistant/components/bluetooth/translations/de.json index 63bbf51c59e..2ddbc4496ac 100644 --- a/homeassistant/components/bluetooth/translations/de.json +++ b/homeassistant/components/bluetooth/translations/de.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "M\u00f6chtest du {name} einrichten?" }, - "enable_bluetooth": { - "description": "M\u00f6chtest du Bluetooth einrichten?" - }, "multiple_adapters": { "data": { "adapter": "Adapter" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "Der zum Scannen zu verwendende Bluetooth-Adapter", "passive": "Passives Scannen" - }, - "description": "Passives Mith\u00f6ren erfordert BlueZ 5.63 oder h\u00f6her mit aktivierten experimentellen Funktionen." + } } } } diff --git a/homeassistant/components/bluetooth/translations/el.json b/homeassistant/components/bluetooth/translations/el.json index f31e930b3fa..0e0e512e9a5 100644 --- a/homeassistant/components/bluetooth/translations/el.json +++ b/homeassistant/components/bluetooth/translations/el.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name};" }, - "enable_bluetooth": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf Bluetooth;" - }, "multiple_adapters": { "data": { "adapter": "\u03a0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03b3\u03ad\u03b1\u03c2" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "\u039f \u03c0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03b3\u03ad\u03b1\u03c2 Bluetooth \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7", "passive": "\u03a0\u03b1\u03b8\u03b7\u03c4\u03b9\u03ba\u03ae \u03b1\u03ba\u03c1\u03cc\u03b1\u03c3\u03b7" - }, - "description": "\u0397 \u03c0\u03b1\u03b8\u03b7\u03c4\u03b9\u03ba\u03ae \u03b1\u03ba\u03c1\u03cc\u03b1\u03c3\u03b7 \u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af BlueZ 5.63 \u03ae \u03bc\u03b5\u03c4\u03b1\u03b3\u03b5\u03bd\u03ad\u03c3\u03c4\u03b5\u03c1\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03bc\u03b5 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b5\u03c2 \u03c4\u03b9\u03c2 \u03c0\u03b5\u03b9\u03c1\u03b1\u03bc\u03b1\u03c4\u03b9\u03ba\u03ad\u03c2 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b5\u03c2." + } } } } diff --git a/homeassistant/components/bluetooth/translations/en.json b/homeassistant/components/bluetooth/translations/en.json index 73ed74356fd..beefb842204 100644 --- a/homeassistant/components/bluetooth/translations/en.json +++ b/homeassistant/components/bluetooth/translations/en.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "Do you want to setup {name}?" }, - "enable_bluetooth": { - "description": "Do you want to setup Bluetooth?" - }, "multiple_adapters": { "data": { "adapter": "Adapter" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "The Bluetooth Adapter to use for scanning", "passive": "Passive scanning" - }, - "description": "Passive listening requires BlueZ 5.63 or later with experimental features enabled." + } } } } diff --git a/homeassistant/components/bluetooth/translations/es.json b/homeassistant/components/bluetooth/translations/es.json index 4cfa38df9c2..f72348c4794 100644 --- a/homeassistant/components/bluetooth/translations/es.json +++ b/homeassistant/components/bluetooth/translations/es.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "\u00bfQuieres configurar {name}?" }, - "enable_bluetooth": { - "description": "\u00bfQuieres configurar Bluetooth?" - }, "multiple_adapters": { "data": { "adapter": "Adaptador" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "El adaptador Bluetooth que se usar\u00e1 para escanear", "passive": "Escaneo pasivo" - }, - "description": "La escucha pasiva requiere BlueZ 5.63 o posterior con funciones experimentales habilitadas." + } } } } diff --git a/homeassistant/components/bluetooth/translations/et.json b/homeassistant/components/bluetooth/translations/et.json index 5579ab5b62d..d6413e23648 100644 --- a/homeassistant/components/bluetooth/translations/et.json +++ b/homeassistant/components/bluetooth/translations/et.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "Kas seadistada {name} ?" }, - "enable_bluetooth": { - "description": "Kas soovid Bluetoothi seadistada?" - }, "multiple_adapters": { "data": { "adapter": "Adapter" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "Sk\u00e4nnimiseks kasutatav Bluetoothi adapter", "passive": "Passiivne sk\u00e4nnimine" - }, - "description": "Passiivseks kuulamiseks on vaja BlueZ 5.63 v\u00f5i uuemat versiooni koos lubatud eksperimentaalsete funktsioonidega." + } } } } diff --git a/homeassistant/components/bluetooth/translations/fr.json b/homeassistant/components/bluetooth/translations/fr.json index c53c306b987..03cc6857910 100644 --- a/homeassistant/components/bluetooth/translations/fr.json +++ b/homeassistant/components/bluetooth/translations/fr.json @@ -8,9 +8,6 @@ "bluetooth_confirm": { "description": "Voulez-vous configurer {name}\u00a0?" }, - "enable_bluetooth": { - "description": "Voulez-vous configurer le Bluetooth\u00a0?" - }, "multiple_adapters": { "data": { "adapter": "Adaptateur" @@ -39,8 +36,7 @@ "init": { "data": { "passive": "Recherche passive" - }, - "description": "L'\u00e9coute passive n\u00e9cessite BlueZ version 5.63 ou ult\u00e9rieure avec les fonctions exp\u00e9rimentales activ\u00e9es." + } } } } diff --git a/homeassistant/components/bluetooth/translations/he.json b/homeassistant/components/bluetooth/translations/he.json index 0bf1deb0adf..b2cb1c6814d 100644 --- a/homeassistant/components/bluetooth/translations/he.json +++ b/homeassistant/components/bluetooth/translations/he.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea {name}?" }, - "enable_bluetooth": { - "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05e9\u05df \u05db\u05d7\u05d5\u05dc\u05d4?" - }, "multiple_adapters": { "data": { "adapter": "\u05de\u05ea\u05d0\u05dd" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "\u05de\u05ea\u05d0\u05dd \u05d4\u05e9\u05df \u05d4\u05db\u05d7\u05d5\u05dc\u05d4 \u05dc\u05e9\u05d9\u05de\u05d5\u05e9 \u05dc\u05e1\u05e8\u05d9\u05e7\u05d4", "passive": "\u05e1\u05e8\u05d9\u05e7\u05d4 \u05e4\u05e1\u05d9\u05d1\u05d9\u05ea" - }, - "description": "\u05d4\u05d0\u05d6\u05e0\u05d4 \u05e4\u05e1\u05d9\u05d1\u05d9\u05ea \u05d3\u05d5\u05e8\u05e9\u05ea BlueZ 5.63 \u05d5\u05d0\u05d9\u05dc\u05da \u05e2\u05dd \u05ea\u05db\u05d5\u05e0\u05d5\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05e0\u05d9\u05d5\u05ea \u05de\u05d5\u05e4\u05e2\u05dc\u05d5\u05ea." + } } } } diff --git a/homeassistant/components/bluetooth/translations/hu.json b/homeassistant/components/bluetooth/translations/hu.json index d063d089ded..85060552cb1 100644 --- a/homeassistant/components/bluetooth/translations/hu.json +++ b/homeassistant/components/bluetooth/translations/hu.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name}?" }, - "enable_bluetooth": { - "description": "Szeretn\u00e9 be\u00e1ll\u00edtani a Bluetooth-ot?" - }, "multiple_adapters": { "data": { "adapter": "Adapter" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "A szkennel\u00e9shez haszn\u00e1lhat\u00f3 Bluetooth-adapter", "passive": "Passz\u00edv figyel\u00e9s" - }, - "description": "A passz\u00edv hallgat\u00e1shoz BlueZ 5.63 vagy \u00fajabb verzi\u00f3ra van sz\u00fcks\u00e9g, a k\u00eds\u00e9rleti funkci\u00f3k enged\u00e9lyez\u00e9s\u00e9vel." + } } } } diff --git a/homeassistant/components/bluetooth/translations/id.json b/homeassistant/components/bluetooth/translations/id.json index 282071fc4f1..0c049279b9c 100644 --- a/homeassistant/components/bluetooth/translations/id.json +++ b/homeassistant/components/bluetooth/translations/id.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "Ingin menyiapkan {name}?" }, - "enable_bluetooth": { - "description": "Ingin menyiapkan Bluetooth?" - }, "multiple_adapters": { "data": { "adapter": "Adaptor" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "Adaptor Bluetooth yang digunakan untuk pemindaian", "passive": "Memindai secara pasif" - }, - "description": "Mendengarkan secara pasif memerlukan BlueZ 5.63 atau lebih baru dengan fitur eksperimental yang diaktifkan." + } } } } diff --git a/homeassistant/components/bluetooth/translations/it.json b/homeassistant/components/bluetooth/translations/it.json index 6d3adac2f76..4252a84c889 100644 --- a/homeassistant/components/bluetooth/translations/it.json +++ b/homeassistant/components/bluetooth/translations/it.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "Vuoi configurare {name}?" }, - "enable_bluetooth": { - "description": "Vuoi configurare il Bluetooth?" - }, "multiple_adapters": { "data": { "adapter": "Adattatore" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "L'adattatore Bluetooth da utilizzare per la scansione", "passive": "Scansione passiva" - }, - "description": "L'ascolto passivo richiede BlueZ 5.63 o successivo con funzionalit\u00e0 sperimentali abilitate." + } } } } diff --git a/homeassistant/components/bluetooth/translations/ja.json b/homeassistant/components/bluetooth/translations/ja.json index e19ee5b7dd2..97d77df507b 100644 --- a/homeassistant/components/bluetooth/translations/ja.json +++ b/homeassistant/components/bluetooth/translations/ja.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "{name} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f" }, - "enable_bluetooth": { - "description": "Bluetooth\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f" - }, "multiple_adapters": { "data": { "adapter": "\u30a2\u30c0\u30d7\u30bf\u30fc" @@ -33,10 +30,8 @@ "step": { "init": { "data": { - "adapter": "\u30b9\u30ad\u30e3\u30f3\u306b\u4f7f\u7528\u3059\u308bBluetooth\u30a2\u30c0\u30d7\u30bf\u30fc", "passive": "\u30d1\u30c3\u30b7\u30d6\u30b9\u30ad\u30e3\u30f3" - }, - "description": "\u30d1\u30c3\u30b7\u30d6 \u30ea\u30b9\u30cb\u30f3\u30b0\u306b\u306f\u3001\u5b9f\u9a13\u7684\u306a\u6a5f\u80fd\u3092\u6709\u52b9\u306b\u3057\u305f\u3001BlueZ 5.63\u4ee5\u964d\u304c\u5fc5\u8981\u3067\u3059\u3002" + } } } } diff --git a/homeassistant/components/bluetooth/translations/nl.json b/homeassistant/components/bluetooth/translations/nl.json index 22ec504f244..3fee489370e 100644 --- a/homeassistant/components/bluetooth/translations/nl.json +++ b/homeassistant/components/bluetooth/translations/nl.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "Wilt u {name} instellen?" }, - "enable_bluetooth": { - "description": "Wilt u Bluetooth instellen?" - }, "multiple_adapters": { "data": { "adapter": "Adapter" @@ -29,14 +26,5 @@ "haos_outdated": { "title": "Update naar het Home Assistant-besturingssysteem versie 9.0 of hoger" } - }, - "options": { - "step": { - "init": { - "data": { - "adapter": "De Bluetooth-adapter die gebruikt moet worden voor het scannen." - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/bluetooth/translations/no.json b/homeassistant/components/bluetooth/translations/no.json index 687b651eaca..2246f72a748 100644 --- a/homeassistant/components/bluetooth/translations/no.json +++ b/homeassistant/components/bluetooth/translations/no.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "Vil du konfigurere {name}?" }, - "enable_bluetooth": { - "description": "Vil du konfigurere Bluetooth?" - }, "multiple_adapters": { "data": { "adapter": "Adapter" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "Bluetooth-adapteren som skal brukes til skanning", "passive": "Passiv skanning" - }, - "description": "Passiv lytting krever BlueZ 5.63 eller nyere med eksperimentelle funksjoner aktivert." + } } } } diff --git a/homeassistant/components/bluetooth/translations/pl.json b/homeassistant/components/bluetooth/translations/pl.json index 2de99ab69fe..286457bf3b2 100644 --- a/homeassistant/components/bluetooth/translations/pl.json +++ b/homeassistant/components/bluetooth/translations/pl.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "Czy chcesz skonfigurowa\u0107 {name}?" }, - "enable_bluetooth": { - "description": "Czy chcesz skonfigurowa\u0107 Bluetooth?" - }, "multiple_adapters": { "data": { "adapter": "Adapter" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "Adapter Bluetooth u\u017cywany do skanowania", "passive": "Skanowanie pasywne" - }, - "description": "Nas\u0142uchiwanie pasywne wymaga BlueZ 5.63 lub nowszego z w\u0142\u0105czonymi funkcjami eksperymentalnymi." + } } } } diff --git a/homeassistant/components/bluetooth/translations/pt-BR.json b/homeassistant/components/bluetooth/translations/pt-BR.json index 389205c20db..3a18865e33c 100644 --- a/homeassistant/components/bluetooth/translations/pt-BR.json +++ b/homeassistant/components/bluetooth/translations/pt-BR.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "Deseja configurar {name}?" }, - "enable_bluetooth": { - "description": "Deseja configurar o Bluetooth?" - }, "multiple_adapters": { "data": { "adapter": "Adaptador" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "O adaptador Bluetooth a ser usado para escaneamento", "passive": "Varredura passiva" - }, - "description": "A escuta passiva requer BlueZ 5.63 ou posterior com recursos experimentais ativados." + } } } } diff --git a/homeassistant/components/bluetooth/translations/ru.json b/homeassistant/components/bluetooth/translations/ru.json index 1be63589bb4..f2cd2e000a6 100644 --- a/homeassistant/components/bluetooth/translations/ru.json +++ b/homeassistant/components/bluetooth/translations/ru.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name}?" }, - "enable_bluetooth": { - "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Bluetooth?" - }, "multiple_adapters": { "data": { "adapter": "\u0410\u0434\u0430\u043f\u0442\u0435\u0440" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "\u0410\u0434\u0430\u043f\u0442\u0435\u0440 Bluetooth, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f", "passive": "\u041f\u0430\u0441\u0441\u0438\u0432\u043d\u043e\u0435 \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435" - }, - "description": "\u0414\u043b\u044f \u043f\u0430\u0441\u0441\u0438\u0432\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f BlueZ 5.63 \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0437\u0434\u043d\u044f\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0441 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c\u0438 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438." + } } } } diff --git a/homeassistant/components/bluetooth/translations/sv.json b/homeassistant/components/bluetooth/translations/sv.json index ee2d433fc1c..1dd27b29042 100644 --- a/homeassistant/components/bluetooth/translations/sv.json +++ b/homeassistant/components/bluetooth/translations/sv.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "Vill du konfigurera {name}?" }, - "enable_bluetooth": { - "description": "Vill du s\u00e4tta upp Bluetooth?" - }, "multiple_adapters": { "data": { "adapter": "Adapter" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "Bluetooth-adaptern som ska anv\u00e4ndas f\u00f6r skanning", "passive": "Passiv skanning" - }, - "description": "Passiv lyssning kr\u00e4ver BlueZ 5.63 eller senare med experimentella funktioner aktiverade." + } } } } diff --git a/homeassistant/components/bluetooth/translations/tr.json b/homeassistant/components/bluetooth/translations/tr.json index 787bc1fed94..9e8516ba25c 100644 --- a/homeassistant/components/bluetooth/translations/tr.json +++ b/homeassistant/components/bluetooth/translations/tr.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "{name} kurulumunu yapmak istiyor musunuz?" }, - "enable_bluetooth": { - "description": "Bluetooth'u kurmak istiyor musunuz?" - }, "multiple_adapters": { "data": { "adapter": "Adapt\u00f6r" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "Tarama i\u00e7in kullan\u0131lacak Bluetooth Adapt\u00f6r\u00fc", "passive": "Pasif tarama" - }, - "description": "Pasif dinleme, BlueZ 5.63 veya daha yenisini ve deneysel \u00f6zelliklerin etkinle\u015ftirilmesini gerektirir." + } } } } diff --git a/homeassistant/components/bluetooth/translations/zh-Hant.json b/homeassistant/components/bluetooth/translations/zh-Hant.json index a45ccc52d44..bd554d76b3c 100644 --- a/homeassistant/components/bluetooth/translations/zh-Hant.json +++ b/homeassistant/components/bluetooth/translations/zh-Hant.json @@ -9,9 +9,6 @@ "bluetooth_confirm": { "description": "\u662f\u5426\u8981\u8a2d\u5b9a {name}\uff1f" }, - "enable_bluetooth": { - "description": "\u662f\u5426\u8981\u8a2d\u5b9a\u85cd\u7259\uff1f" - }, "multiple_adapters": { "data": { "adapter": "\u50b3\u8f38\u5668" @@ -39,10 +36,8 @@ "step": { "init": { "data": { - "adapter": "\u7528\u4ee5\u9032\u884c\u5075\u6e2c\u7684\u85cd\u7259\u50b3\u8f38\u5668", "passive": "\u88ab\u52d5\u6383\u63cf" - }, - "description": "\u88ab\u52d5\u76e3\u807d\u9700\u8981 BlueZ 5.63 \u6216\u66f4\u65b0\u7248\u672c\u3001\u4e26\u958b\u555f\u5be6\u9a57\u529f\u80fd\u3002" + } } } } diff --git a/homeassistant/components/climacell/translations/af.json b/homeassistant/components/climacell/translations/af.json new file mode 100644 index 00000000000..d05e07e4eff --- /dev/null +++ b/homeassistant/components/climacell/translations/af.json @@ -0,0 +1,9 @@ +{ + "options": { + "step": { + "init": { + "title": "Update [%key:component::climacell::title%] opties" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ca.json b/homeassistant/components/climacell/translations/ca.json new file mode 100644 index 00000000000..2b6abb46737 --- /dev/null +++ b/homeassistant/components/climacell/translations/ca.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Minuts entre previsions NowCast" + }, + "description": "Si decideixes activar l'entitat de previsi\u00f3 `nowcast`, podr\u00e0s configurar l'interval en minuts entre cada previsi\u00f3. El nombre de previsions proporcionades dep\u00e8n d'aquest interval de minuts.", + "title": "Actualitzaci\u00f3 d'opcions de ClimaCell" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/de.json b/homeassistant/components/climacell/translations/de.json new file mode 100644 index 00000000000..7c3e929dde2 --- /dev/null +++ b/homeassistant/components/climacell/translations/de.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Minuten zwischen den NowCast Kurzvorhersagen" + }, + "description": "Wenn du die Vorhersage-Entitit\u00e4t \"Kurzvorhersage\" aktivierst, kannst du die Anzahl der Minuten zwischen den einzelnen Vorhersagen konfigurieren. Die Anzahl der bereitgestellten Vorhersagen h\u00e4ngt von der Anzahl der zwischen den Vorhersagen gew\u00e4hlten Minuten ab.", + "title": "ClimaCell-Optionen aktualisieren" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/el.json b/homeassistant/components/climacell/translations/el.json new file mode 100644 index 00000000000..392573f693c --- /dev/null +++ b/homeassistant/components/climacell/translations/el.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "\u039b\u03b5\u03c0\u03c4\u03ac \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03c4\u03c9\u03bd \u03b4\u03b5\u03bb\u03c4\u03af\u03c9\u03bd NowCast" + }, + "description": "\u0395\u03ac\u03bd \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b4\u03b5\u03bb\u03c4\u03af\u03c9\u03bd 'nowcast', \u03bc\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03bb\u03b5\u03c0\u03c4\u03ce\u03bd \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03ba\u03ac\u03b8\u03b5 \u03b4\u03b5\u03bb\u03c4\u03af\u03bf\u03c5. \u039f \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03c4\u03c9\u03bd \u03b4\u03b5\u03bb\u03c4\u03af\u03c9\u03bd \u03c0\u03bf\u03c5 \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b5\u03be\u03b1\u03c1\u03c4\u03ac\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03bb\u03b5\u03c0\u03c4\u03ce\u03bd \u03c0\u03bf\u03c5 \u03b5\u03c0\u03b9\u03bb\u03ad\u03b3\u03bf\u03bd\u03c4\u03b1\u03b9 \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03c4\u03c9\u03bd \u03b4\u03b5\u03bb\u03c4\u03af\u03c9\u03bd.", + "title": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd ClimaCell" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/es-419.json b/homeassistant/components/climacell/translations/es-419.json new file mode 100644 index 00000000000..449ad1ba367 --- /dev/null +++ b/homeassistant/components/climacell/translations/es-419.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. entre pron\u00f3sticos de NowCast" + }, + "description": "Si elige habilitar la entidad de pron\u00f3stico \"nowcast\", puede configurar el n\u00famero de minutos entre cada pron\u00f3stico. El n\u00famero de pron\u00f3sticos proporcionados depende del n\u00famero de minutos elegidos entre los pron\u00f3sticos.", + "title": "Actualizar opciones de ClimaCell" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/es.json b/homeassistant/components/climacell/translations/es.json new file mode 100644 index 00000000000..270d72bd58c --- /dev/null +++ b/homeassistant/components/climacell/translations/es.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. entre pron\u00f3sticos de NowCast" + }, + "description": "Si eliges habilitar la entidad de pron\u00f3stico `nowcast`, puedes configurar la cantidad de minutos entre cada pron\u00f3stico. La cantidad de pron\u00f3sticos proporcionados depende de la cantidad de minutos elegidos entre los pron\u00f3sticos.", + "title": "Actualizar opciones de ClimaCell" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/et.json b/homeassistant/components/climacell/translations/et.json new file mode 100644 index 00000000000..5d915a87d80 --- /dev/null +++ b/homeassistant/components/climacell/translations/et.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Minuteid NowCasti prognooside vahel" + }, + "description": "Kui otsustad lubada \"nowcast\" prognoosi\u00fcksuse, saad seadistada minutite arvu iga prognoosi vahel. Esitatavate prognooside arv s\u00f5ltub prognooside vahel valitud minutite arvust.", + "title": "V\u00e4rskenda [%key:component::climacell::title%] suvandeid" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/fr.json b/homeassistant/components/climacell/translations/fr.json new file mode 100644 index 00000000000..b2c1285ecc9 --- /dev/null +++ b/homeassistant/components/climacell/translations/fr.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. entre les pr\u00e9visions NowCast" + }, + "description": "Si vous choisissez d'activer l'entit\u00e9 de pr\u00e9vision \u00ab\u00a0nowcast\u00a0\u00bb, vous pouvez configurer le nombre de minutes entre chaque pr\u00e9vision. Le nombre de pr\u00e9visions fournies d\u00e9pend du nombre de minutes choisies entre les pr\u00e9visions.", + "title": "Mettre \u00e0 jour les options ClimaCell" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/hu.json b/homeassistant/components/climacell/translations/hu.json new file mode 100644 index 00000000000..4cad1eaaa0f --- /dev/null +++ b/homeassistant/components/climacell/translations/hu.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. A NowCast el\u0151rejelz\u00e9sek k\u00f6z\u00f6tt" + }, + "description": "Ha a `nowcast` el\u0151rejelz\u00e9si entit\u00e1s enged\u00e9lyez\u00e9s\u00e9t v\u00e1lasztja, be\u00e1ll\u00edthatja az egyes el\u0151rejelz\u00e9sek k\u00f6z\u00f6tti percek sz\u00e1m\u00e1t. A megadott el\u0151rejelz\u00e9sek sz\u00e1ma az el\u0151rejelz\u00e9sek k\u00f6z\u00f6tt kiv\u00e1lasztott percek sz\u00e1m\u00e1t\u00f3l f\u00fcgg.", + "title": "ClimaCell be\u00e1ll\u00edt\u00e1sok friss\u00edt\u00e9se" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/id.json b/homeassistant/components/climacell/translations/id.json new file mode 100644 index 00000000000..4d020351665 --- /dev/null +++ b/homeassistant/components/climacell/translations/id.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Jarak Interval Prakiraan NowCast dalam Menit" + }, + "description": "Jika Anda memilih untuk mengaktifkan entitas prakiraan `nowcast`, Anda dapat mengonfigurasi jarak interval prakiraan dalam menit. Jumlah prakiraan yang diberikan tergantung pada nilai interval yang dipilih.", + "title": "Perbarui Opsi ClimaCell" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/it.json b/homeassistant/components/climacell/translations/it.json new file mode 100644 index 00000000000..b9667d6bfb1 --- /dev/null +++ b/homeassistant/components/climacell/translations/it.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Minuti tra le previsioni di NowCast" + }, + "description": "Se scegli di abilitare l'entit\u00e0 di previsione `nowcast`, puoi configurare il numero di minuti tra ogni previsione. Il numero di previsioni fornite dipende dal numero di minuti scelti tra le previsioni.", + "title": "Aggiorna le opzioni di ClimaCell" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ja.json b/homeassistant/components/climacell/translations/ja.json new file mode 100644 index 00000000000..e2742d11435 --- /dev/null +++ b/homeassistant/components/climacell/translations/ja.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "\u6700\u5c0f\u3002 NowCast \u4e88\u6e2c\u306e\u9593" + }, + "description": "`nowcast` forecast(\u4e88\u6e2c) \u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u6709\u52b9\u306b\u3059\u308b\u3053\u3068\u3092\u9078\u629e\u3057\u305f\u5834\u5408\u3001\u5404\u4e88\u6e2c\u9593\u306e\u5206\u6570\u3092\u8a2d\u5b9a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u63d0\u4f9b\u3055\u308c\u308bforecast(\u4e88\u6e2c)\u306e\u6570\u306f\u3001forecast(\u4e88\u6e2c)\u306e\u9593\u306b\u9078\u629e\u3057\u305f\u5206\u6570\u306b\u4f9d\u5b58\u3057\u307e\u3059\u3002", + "title": "ClimaCell \u30aa\u30d7\u30b7\u30e7\u30f3\u306e\u66f4\u65b0" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ko.json b/homeassistant/components/climacell/translations/ko.json new file mode 100644 index 00000000000..8accc07410d --- /dev/null +++ b/homeassistant/components/climacell/translations/ko.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "\ub2e8\uae30\uc608\uce21 \uc77c\uae30\uc608\ubcf4 \uac04 \ucd5c\uc18c \uc2dc\uac04" + }, + "description": "`nowcast` \uc77c\uae30\uc608\ubcf4 \uad6c\uc131\uc694\uc18c\ub97c \uc0ac\uc6a9\ud558\ub3c4\ub85d \uc120\ud0dd\ud55c \uacbd\uc6b0 \uac01 \uc77c\uae30\uc608\ubcf4 \uc0ac\uc774\uc758 \uc2dc\uac04(\ubd84)\uc744 \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \uc81c\uacf5\ub41c \uc77c\uae30\uc608\ubcf4 \ud69f\uc218\ub294 \uc608\uce21 \uac04 \uc120\ud0dd\ud55c \uc2dc\uac04(\ubd84)\uc5d0 \ub530\ub77c \ub2ec\ub77c\uc9d1\ub2c8\ub2e4.", + "title": "[%key:component::climacell::title%] \uc635\uc158 \uc5c5\ub370\uc774\ud2b8\ud558\uae30" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/nl.json b/homeassistant/components/climacell/translations/nl.json new file mode 100644 index 00000000000..a895fa8234d --- /dev/null +++ b/homeassistant/components/climacell/translations/nl.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. Tussen NowCast-voorspellingen" + }, + "description": "Als u ervoor kiest om de `nowcast` voorspellingsentiteit in te schakelen, kan u het aantal minuten tussen elke voorspelling configureren. Het aantal voorspellingen hangt af van het aantal gekozen minuten tussen de voorspellingen.", + "title": "Update ClimaCell Opties" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/no.json b/homeassistant/components/climacell/translations/no.json new file mode 100644 index 00000000000..9f050624967 --- /dev/null +++ b/homeassistant/components/climacell/translations/no.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. mellom NowCast prognoser" + }, + "description": "Hvis du velger \u00e5 aktivere \u00abnowcast\u00bb -varselentiteten, kan du konfigurere antall minutter mellom hver prognose. Antall angitte prognoser avhenger av antall minutter som er valgt mellom prognosene.", + "title": "Oppdater ClimaCell-alternativer" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/pl.json b/homeassistant/components/climacell/translations/pl.json new file mode 100644 index 00000000000..5f69764ffab --- /dev/null +++ b/homeassistant/components/climacell/translations/pl.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Czas (min) mi\u0119dzy prognozami NowCast" + }, + "description": "Je\u015bli zdecydujesz si\u0119 w\u0142\u0105czy\u0107 encj\u0119 prognozy \u201enowcast\u201d, mo\u017cesz skonfigurowa\u0107 liczb\u0119 minut mi\u0119dzy ka\u017cd\u0105 prognoz\u0105. Liczba dostarczonych prognoz zale\u017cy od liczby minut wybranych mi\u0119dzy prognozami.", + "title": "Opcje aktualizacji ClimaCell" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/pt-BR.json b/homeassistant/components/climacell/translations/pt-BR.json new file mode 100644 index 00000000000..b7e71d45971 --- /dev/null +++ b/homeassistant/components/climacell/translations/pt-BR.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "M\u00ednimo entre previs\u00f5es NowCast" + }, + "description": "Se voc\u00ea optar por ativar a entidade de previs\u00e3o `nowcast`, poder\u00e1 configurar o n\u00famero de minutos entre cada previs\u00e3o. O n\u00famero de previs\u00f5es fornecidas depende do n\u00famero de minutos escolhidos entre as previs\u00f5es.", + "title": "Atualizar as op\u00e7\u00f5es do ClimaCell" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ru.json b/homeassistant/components/climacell/translations/ru.json new file mode 100644 index 00000000000..9f3219ce4d6 --- /dev/null +++ b/homeassistant/components/climacell/translations/ru.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f (\u0432 \u043c\u0438\u043d\u0443\u0442\u0430\u0445)" + }, + "description": "\u0415\u0441\u043b\u0438 \u0412\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u0442\u0435 \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 'nowcast', \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430.", + "title": "\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 ClimaCell" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.bg.json b/homeassistant/components/climacell/translations/sensor.bg.json new file mode 100644 index 00000000000..04f393f1d99 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.bg.json @@ -0,0 +1,8 @@ +{ + "state": { + "climacell__precipitation_type": { + "rain": "\u0414\u044a\u0436\u0434", + "snow": "\u0421\u043d\u044f\u0433" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.ca.json b/homeassistant/components/climacell/translations/sensor.ca.json new file mode 100644 index 00000000000..359857925da --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.ca.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Bo", + "hazardous": "Perill\u00f3s", + "moderate": "Moderat", + "unhealthy": "Poc saludable", + "unhealthy_for_sensitive_groups": "No saludable per a grups sensibles", + "very_unhealthy": "Gens saludable" + }, + "climacell__pollen_index": { + "high": "Alt", + "low": "Baix", + "medium": "Mitj\u00e0", + "none": "Cap", + "very_high": "Molt alt", + "very_low": "Molt baix" + }, + "climacell__precipitation_type": { + "freezing_rain": "Pluja congelada", + "ice_pellets": "Gran\u00eds", + "none": "Cap", + "rain": "Pluja", + "snow": "Neu" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.de.json b/homeassistant/components/climacell/translations/sensor.de.json new file mode 100644 index 00000000000..93a1e5e8e98 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.de.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Gut", + "hazardous": "Gef\u00e4hrlich", + "moderate": "M\u00e4\u00dfig", + "unhealthy": "Ungesund", + "unhealthy_for_sensitive_groups": "Ungesund f\u00fcr sensible Gruppen", + "very_unhealthy": "Sehr ungesund" + }, + "climacell__pollen_index": { + "high": "Hoch", + "low": "Niedrig", + "medium": "Mittel", + "none": "Keine", + "very_high": "Sehr hoch", + "very_low": "Sehr niedrig" + }, + "climacell__precipitation_type": { + "freezing_rain": "Gefrierender Regen", + "ice_pellets": "Graupel", + "none": "Keine", + "rain": "Regen", + "snow": "Schnee" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.el.json b/homeassistant/components/climacell/translations/sensor.el.json new file mode 100644 index 00000000000..facd86ed7c6 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.el.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "\u039a\u03b1\u03bb\u03cc", + "hazardous": "\u0395\u03c0\u03b9\u03ba\u03af\u03bd\u03b4\u03c5\u03bd\u03bf", + "moderate": "\u039c\u03ad\u03c4\u03c1\u03b9\u03bf", + "unhealthy": "\u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc", + "unhealthy_for_sensitive_groups": "\u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc \u03b3\u03b9\u03b1 \u03b5\u03c5\u03b1\u03af\u03c3\u03b8\u03b7\u03c4\u03b5\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b5\u03c2", + "very_unhealthy": "\u03a0\u03bf\u03bb\u03cd \u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc" + }, + "climacell__pollen_index": { + "high": "\u03a5\u03c8\u03b7\u03bb\u03cc", + "low": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03cc", + "medium": "\u039c\u03b5\u03c3\u03b1\u03af\u03bf", + "none": "\u03a4\u03af\u03c0\u03bf\u03c4\u03b1", + "very_high": "\u03a0\u03bf\u03bb\u03cd \u03c5\u03c8\u03b7\u03bb\u03cc", + "very_low": "\u03a0\u03bf\u03bb\u03cd \u03c7\u03b1\u03bc\u03b7\u03bb\u03cc" + }, + "climacell__precipitation_type": { + "freezing_rain": "\u03a0\u03b1\u03b3\u03c9\u03bc\u03ad\u03bd\u03b7 \u03b2\u03c1\u03bf\u03c7\u03ae", + "ice_pellets": "\u03a0\u03ad\u03bb\u03bb\u03b5\u03c4 \u03c0\u03ac\u03b3\u03bf\u03c5", + "none": "\u03a4\u03af\u03c0\u03bf\u03c4\u03b1", + "rain": "\u0392\u03c1\u03bf\u03c7\u03ae", + "snow": "\u03a7\u03b9\u03cc\u03bd\u03b9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.es-419.json b/homeassistant/components/climacell/translations/sensor.es-419.json new file mode 100644 index 00000000000..127177e84b4 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.es-419.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Bueno", + "hazardous": "Peligroso", + "moderate": "Moderado", + "unhealthy": "Insalubre", + "unhealthy_for_sensitive_groups": "Insalubre para grupos sensibles", + "very_unhealthy": "Muy poco saludable" + }, + "climacell__pollen_index": { + "high": "Alto", + "low": "Bajo", + "medium": "Medio", + "none": "Ninguno", + "very_high": "Muy alto", + "very_low": "Muy bajo" + }, + "climacell__precipitation_type": { + "freezing_rain": "Lluvia helada", + "ice_pellets": "Gr\u00e1nulos de hielo", + "none": "Ninguno", + "rain": "Lluvia", + "snow": "Nieve" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.es.json b/homeassistant/components/climacell/translations/sensor.es.json new file mode 100644 index 00000000000..4cb1b34eb21 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.es.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Bueno", + "hazardous": "Peligroso", + "moderate": "Moderado", + "unhealthy": "No saludable", + "unhealthy_for_sensitive_groups": "No es saludable para grupos sensibles", + "very_unhealthy": "Muy poco saludable" + }, + "climacell__pollen_index": { + "high": "Alto", + "low": "Bajo", + "medium": "Medio", + "none": "Ninguno", + "very_high": "Muy alto", + "very_low": "Muy bajo" + }, + "climacell__precipitation_type": { + "freezing_rain": "Lluvia helada", + "ice_pellets": "Granizo", + "none": "Ninguna", + "rain": "Lluvia", + "snow": "Nieve" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.et.json b/homeassistant/components/climacell/translations/sensor.et.json new file mode 100644 index 00000000000..a0b7ac0562b --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.et.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Normaalne", + "hazardous": "Ohtlik", + "moderate": "M\u00f5\u00f5dukas", + "unhealthy": "Ebatervislik", + "unhealthy_for_sensitive_groups": "Ebatervislik riskir\u00fchmale", + "very_unhealthy": "V\u00e4ga ebatervislik" + }, + "climacell__pollen_index": { + "high": "K\u00f5rge", + "low": "Madal", + "medium": "Keskmine", + "none": "Puudub", + "very_high": "V\u00e4ga k\u00f5rge", + "very_low": "V\u00e4ga madal" + }, + "climacell__precipitation_type": { + "freezing_rain": "J\u00e4\u00e4vihm", + "ice_pellets": "J\u00e4\u00e4kruubid", + "none": "Sademeid pole", + "rain": "Vihm", + "snow": "Lumi" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.fr.json b/homeassistant/components/climacell/translations/sensor.fr.json new file mode 100644 index 00000000000..acff91fc570 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.fr.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Bon", + "hazardous": "Dangereux", + "moderate": "Mod\u00e9r\u00e9", + "unhealthy": "Mauvais pour la sant\u00e9", + "unhealthy_for_sensitive_groups": "Mauvaise qualit\u00e9 pour les groupes sensibles", + "very_unhealthy": "Tr\u00e8s mauvais pour la sant\u00e9" + }, + "climacell__pollen_index": { + "high": "Haut", + "low": "Faible", + "medium": "Moyen", + "none": "Aucun", + "very_high": "Tr\u00e8s \u00e9lev\u00e9", + "very_low": "Tr\u00e8s faible" + }, + "climacell__precipitation_type": { + "freezing_rain": "Pluie vergla\u00e7ante", + "ice_pellets": "Gr\u00e9sil", + "none": "Aucun", + "rain": "Pluie", + "snow": "Neige" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.he.json b/homeassistant/components/climacell/translations/sensor.he.json new file mode 100644 index 00000000000..2a509464928 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.he.json @@ -0,0 +1,7 @@ +{ + "state": { + "climacell__health_concern": { + "unhealthy_for_sensitive_groups": "\u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0 \u05dc\u05e7\u05d1\u05d5\u05e6\u05d5\u05ea \u05e8\u05d2\u05d9\u05e9\u05d5\u05ea" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.hu.json b/homeassistant/components/climacell/translations/sensor.hu.json new file mode 100644 index 00000000000..656a460f429 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.hu.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "J\u00f3", + "hazardous": "Vesz\u00e9lyes", + "moderate": "M\u00e9rs\u00e9kelt", + "unhealthy": "Eg\u00e9szs\u00e9gtelen", + "unhealthy_for_sensitive_groups": "Eg\u00e9szs\u00e9gtelen \u00e9rz\u00e9keny csoportok sz\u00e1m\u00e1ra", + "very_unhealthy": "Nagyon eg\u00e9szs\u00e9gtelen" + }, + "climacell__pollen_index": { + "high": "Magas", + "low": "Alacsony", + "medium": "K\u00f6zepes", + "none": "Nincs", + "very_high": "Nagyon magas", + "very_low": "Nagyon alacsony" + }, + "climacell__precipitation_type": { + "freezing_rain": "Havas es\u0151", + "ice_pellets": "\u00d3nos es\u0151", + "none": "Nincs", + "rain": "Es\u0151", + "snow": "Havaz\u00e1s" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.id.json b/homeassistant/components/climacell/translations/sensor.id.json new file mode 100644 index 00000000000..37ac0f7d876 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.id.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Bagus", + "hazardous": "Berbahaya", + "moderate": "Sedang", + "unhealthy": "Tidak Sehat", + "unhealthy_for_sensitive_groups": "Tidak Sehat untuk Kelompok Sensitif", + "very_unhealthy": "Sangat Tidak Sehat" + }, + "climacell__pollen_index": { + "high": "Tinggi", + "low": "Rendah", + "medium": "Sedang", + "none": "Tidak Ada", + "very_high": "Sangat Tinggi", + "very_low": "Sangat Rendah" + }, + "climacell__precipitation_type": { + "freezing_rain": "Hujan Beku", + "ice_pellets": "Hujan Es", + "none": "Tidak Ada", + "rain": "Hujan", + "snow": "Salju" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.is.json b/homeassistant/components/climacell/translations/sensor.is.json new file mode 100644 index 00000000000..bc22f9c67a9 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.is.json @@ -0,0 +1,12 @@ +{ + "state": { + "climacell__health_concern": { + "hazardous": "H\u00e6ttulegt", + "unhealthy": "\u00d3hollt" + }, + "climacell__precipitation_type": { + "rain": "Rigning", + "snow": "Snj\u00f3r" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.it.json b/homeassistant/components/climacell/translations/sensor.it.json new file mode 100644 index 00000000000..b9326be886e --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.it.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Buono", + "hazardous": "Pericoloso", + "moderate": "Moderato", + "unhealthy": "Malsano", + "unhealthy_for_sensitive_groups": "Malsano per gruppi sensibili", + "very_unhealthy": "Molto malsano" + }, + "climacell__pollen_index": { + "high": "Alto", + "low": "Basso", + "medium": "Medio", + "none": "Nessuno", + "very_high": "Molto alto", + "very_low": "Molto basso" + }, + "climacell__precipitation_type": { + "freezing_rain": "Grandine", + "ice_pellets": "Pioggia gelata", + "none": "Nessuno", + "rain": "Pioggia", + "snow": "Neve" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.ja.json b/homeassistant/components/climacell/translations/sensor.ja.json new file mode 100644 index 00000000000..6d8df99ca70 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.ja.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "\u826f\u597d", + "hazardous": "\u5371\u967a", + "moderate": "\u7a4f\u3084\u304b\u306a", + "unhealthy": "\u4e0d\u5065\u5eb7", + "unhealthy_for_sensitive_groups": "\u654f\u611f\u306a\u30b0\u30eb\u30fc\u30d7\u306b\u3068\u3063\u3066\u306f\u4e0d\u5065\u5eb7", + "very_unhealthy": "\u975e\u5e38\u306b\u4e0d\u5065\u5eb7" + }, + "climacell__pollen_index": { + "high": "\u9ad8\u3044", + "low": "\u4f4e\u3044", + "medium": "\u4e2d", + "none": "\u306a\u3057", + "very_high": "\u975e\u5e38\u306b\u9ad8\u3044", + "very_low": "\u3068\u3066\u3082\u4f4e\u3044" + }, + "climacell__precipitation_type": { + "freezing_rain": "\u51cd\u3066\u3064\u304f\u96e8", + "ice_pellets": "\u51cd\u96e8", + "none": "\u306a\u3057", + "rain": "\u96e8", + "snow": "\u96ea" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.ko.json b/homeassistant/components/climacell/translations/sensor.ko.json new file mode 100644 index 00000000000..e5ec616959e --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.ko.json @@ -0,0 +1,7 @@ +{ + "state": { + "climacell__precipitation_type": { + "snow": "\ub208" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.lv.json b/homeassistant/components/climacell/translations/sensor.lv.json new file mode 100644 index 00000000000..a0010b4e4a8 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.lv.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Labs", + "hazardous": "B\u012bstams", + "moderate": "M\u0113rens", + "unhealthy": "Nevesel\u012bgs", + "unhealthy_for_sensitive_groups": "Nevesel\u012bgs jut\u012bg\u0101m grup\u0101m", + "very_unhealthy": "\u013boti nevesel\u012bgs" + }, + "climacell__pollen_index": { + "high": "Augsts", + "low": "Zems", + "medium": "Vid\u0113js", + "none": "Nav", + "very_high": "\u013boti augsts", + "very_low": "\u013boti zems" + }, + "climacell__precipitation_type": { + "freezing_rain": "Sasalsto\u0161s lietus", + "ice_pellets": "Krusa", + "none": "Nav", + "rain": "Lietus", + "snow": "Sniegs" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.nl.json b/homeassistant/components/climacell/translations/sensor.nl.json new file mode 100644 index 00000000000..710198156d1 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.nl.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Goed", + "hazardous": "Gevaarlijk", + "moderate": "Gematigd", + "unhealthy": "Ongezond", + "unhealthy_for_sensitive_groups": "Ongezond voor gevoelige groepen", + "very_unhealthy": "Zeer ongezond" + }, + "climacell__pollen_index": { + "high": "Hoog", + "low": "Laag", + "medium": "Medium", + "none": "Geen", + "very_high": "Zeer Hoog", + "very_low": "Zeer Laag" + }, + "climacell__precipitation_type": { + "freezing_rain": "IJzel", + "ice_pellets": "IJskorrels", + "none": "Geen", + "rain": "Regen", + "snow": "Sneeuw" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.no.json b/homeassistant/components/climacell/translations/sensor.no.json new file mode 100644 index 00000000000..10f2a02db72 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.no.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Bra", + "hazardous": "Farlig", + "moderate": "Moderat", + "unhealthy": "Usunt", + "unhealthy_for_sensitive_groups": "Usunt for sensitive grupper", + "very_unhealthy": "Veldig usunt" + }, + "climacell__pollen_index": { + "high": "H\u00f8y", + "low": "Lav", + "medium": "Medium", + "none": "Ingen", + "very_high": "Veldig h\u00f8y", + "very_low": "Veldig lav" + }, + "climacell__precipitation_type": { + "freezing_rain": "Underkj\u00f8lt regn", + "ice_pellets": "Is tapper", + "none": "Ingen", + "rain": "Regn", + "snow": "Sn\u00f8" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.pl.json b/homeassistant/components/climacell/translations/sensor.pl.json new file mode 100644 index 00000000000..67a0217a7ea --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.pl.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "dobre", + "hazardous": "niebezpieczne", + "moderate": "umiarkowane", + "unhealthy": "niezdrowe", + "unhealthy_for_sensitive_groups": "niezdrowe dla grup wra\u017cliwych", + "very_unhealthy": "bardzo niezdrowe" + }, + "climacell__pollen_index": { + "high": "wysokie", + "low": "niskie", + "medium": "\u015brednie", + "none": "brak", + "very_high": "bardzo wysokie", + "very_low": "bardzo niskie" + }, + "climacell__precipitation_type": { + "freezing_rain": "marzn\u0105cy deszcz", + "ice_pellets": "granulki lodu", + "none": "brak", + "rain": "deszcz", + "snow": "\u015bnieg" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.pt-BR.json b/homeassistant/components/climacell/translations/sensor.pt-BR.json new file mode 100644 index 00000000000..eb3814331b9 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.pt-BR.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Bom", + "hazardous": "Perigosos", + "moderate": "Moderado", + "unhealthy": "Pouco saud\u00e1vel", + "unhealthy_for_sensitive_groups": "Insalubre para grupos sens\u00edveis", + "very_unhealthy": "Muito prejudicial \u00e0 sa\u00fade" + }, + "climacell__pollen_index": { + "high": "Alto", + "low": "Baixo", + "medium": "M\u00e9dio", + "none": "Nenhum", + "very_high": "Muito alto", + "very_low": "Muito baixo" + }, + "climacell__precipitation_type": { + "freezing_rain": "Chuva congelante", + "ice_pellets": "Granizo", + "none": "Nenhum", + "rain": "Chuva", + "snow": "Neve" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.pt.json b/homeassistant/components/climacell/translations/sensor.pt.json new file mode 100644 index 00000000000..30ba0f75808 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.pt.json @@ -0,0 +1,7 @@ +{ + "state": { + "climacell__health_concern": { + "unhealthy_for_sensitive_groups": "Pouco saud\u00e1vel para grupos sens\u00edveis" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.ru.json b/homeassistant/components/climacell/translations/sensor.ru.json new file mode 100644 index 00000000000..3a5d1a07a7e --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.ru.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "\u0425\u043e\u0440\u043e\u0448\u043e", + "hazardous": "\u041e\u043f\u0430\u0441\u043d\u043e", + "moderate": "\u0421\u0440\u0435\u0434\u043d\u0435", + "unhealthy": "\u0412\u0440\u0435\u0434\u043d\u043e", + "unhealthy_for_sensitive_groups": "\u0412\u0440\u0435\u0434\u043d\u043e \u0434\u043b\u044f \u0443\u044f\u0437\u0432\u0438\u043c\u044b\u0445 \u0433\u0440\u0443\u043f\u043f", + "very_unhealthy": "\u041e\u0447\u0435\u043d\u044c \u0432\u0440\u0435\u0434\u043d\u043e" + }, + "climacell__pollen_index": { + "high": "\u0412\u044b\u0441\u043e\u043a\u0438\u0439", + "low": "\u041d\u0438\u0437\u043a\u0438\u0439", + "medium": "\u0421\u0440\u0435\u0434\u043d\u0438\u0439", + "none": "\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442", + "very_high": "\u041e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0438\u0439", + "very_low": "\u041e\u0447\u0435\u043d\u044c \u043d\u0438\u0437\u043a\u0438\u0439" + }, + "climacell__precipitation_type": { + "freezing_rain": "\u041b\u0435\u0434\u044f\u043d\u043e\u0439 \u0434\u043e\u0436\u0434\u044c", + "ice_pellets": "\u041b\u0435\u0434\u044f\u043d\u0430\u044f \u043a\u0440\u0443\u043f\u0430", + "none": "\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442", + "rain": "\u0414\u043e\u0436\u0434\u044c", + "snow": "\u0421\u043d\u0435\u0433" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.sk.json b/homeassistant/components/climacell/translations/sensor.sk.json new file mode 100644 index 00000000000..dfe997964cc --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.sk.json @@ -0,0 +1,18 @@ +{ + "state": { + "climacell__health_concern": { + "unhealthy": "Nezdrav\u00e9" + }, + "climacell__pollen_index": { + "low": "N\u00edzke", + "medium": "Stredn\u00e9", + "very_high": "Ve\u013emi vysok\u00e9", + "very_low": "Ve\u013emi n\u00edzke" + }, + "climacell__precipitation_type": { + "freezing_rain": "Mrzn\u00faci d\u00e1\u017e\u010f", + "rain": "D\u00e1\u017e\u010f", + "snow": "Sneh" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.sv.json b/homeassistant/components/climacell/translations/sensor.sv.json new file mode 100644 index 00000000000..d6172566c7a --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.sv.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Bra", + "hazardous": "Farligt", + "moderate": "M\u00e5ttligt", + "unhealthy": "Oh\u00e4lsosamt", + "unhealthy_for_sensitive_groups": "Oh\u00e4lsosamt f\u00f6r k\u00e4nsliga grupper", + "very_unhealthy": "Mycket oh\u00e4lsosamt" + }, + "climacell__pollen_index": { + "high": "H\u00f6gt", + "low": "L\u00e5gt", + "medium": "Medium", + "none": "Inget", + "very_high": "V\u00e4ldigt h\u00f6gt", + "very_low": "V\u00e4ldigt l\u00e5gt" + }, + "climacell__precipitation_type": { + "freezing_rain": "Underkylt regn", + "ice_pellets": "Hagel", + "none": "Ingen", + "rain": "Regn", + "snow": "Sn\u00f6" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.tr.json b/homeassistant/components/climacell/translations/sensor.tr.json new file mode 100644 index 00000000000..6c58f82bb94 --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.tr.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "\u0130yi", + "hazardous": "Tehlikeli", + "moderate": "Il\u0131ml\u0131", + "unhealthy": "Sa\u011fl\u0131ks\u0131z", + "unhealthy_for_sensitive_groups": "Hassas Gruplar \u0130\u00e7in Sa\u011fl\u0131ks\u0131z", + "very_unhealthy": "\u00c7ok Sa\u011fl\u0131ks\u0131z" + }, + "climacell__pollen_index": { + "high": "Y\u00fcksek", + "low": "D\u00fc\u015f\u00fck", + "medium": "Orta", + "none": "Hi\u00e7biri", + "very_high": "\u00c7ok Y\u00fcksek", + "very_low": "\u00c7ok D\u00fc\u015f\u00fck" + }, + "climacell__precipitation_type": { + "freezing_rain": "Dondurucu Ya\u011fmur", + "ice_pellets": "Buz Peletleri", + "none": "Hi\u00e7biri", + "rain": "Ya\u011fmur", + "snow": "Kar" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.zh-Hant.json b/homeassistant/components/climacell/translations/sensor.zh-Hant.json new file mode 100644 index 00000000000..c9898fcfe4d --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.zh-Hant.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "\u826f\u597d", + "hazardous": "\u5371\u96aa", + "moderate": "\u4e2d\u7b49", + "unhealthy": "\u4e0d\u5065\u5eb7", + "unhealthy_for_sensitive_groups": "\u5c0d\u654f\u611f\u65cf\u7fa4\u4e0d\u5065\u5eb7", + "very_unhealthy": "\u975e\u5e38\u4e0d\u5065\u5eb7" + }, + "climacell__pollen_index": { + "high": "\u9ad8", + "low": "\u4f4e", + "medium": "\u4e2d", + "none": "\u7121", + "very_high": "\u6975\u9ad8", + "very_low": "\u6975\u4f4e" + }, + "climacell__precipitation_type": { + "freezing_rain": "\u51cd\u96e8", + "ice_pellets": "\u51b0\u73e0", + "none": "\u7121", + "rain": "\u4e0b\u96e8", + "snow": "\u4e0b\u96ea" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sv.json b/homeassistant/components/climacell/translations/sv.json new file mode 100644 index 00000000000..2382ec64324 --- /dev/null +++ b/homeassistant/components/climacell/translations/sv.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. Mellan NowCast-prognoser" + }, + "description": "Om du v\u00e4ljer att aktivera \"nowcast\"-prognosentiteten kan du konfigurera antalet minuter mellan varje prognos. Antalet prognoser som tillhandah\u00e5lls beror p\u00e5 antalet minuter som v\u00e4ljs mellan prognoserna.", + "title": "Uppdatera ClimaCell-alternativ" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/tr.json b/homeassistant/components/climacell/translations/tr.json new file mode 100644 index 00000000000..54e24f813e4 --- /dev/null +++ b/homeassistant/components/climacell/translations/tr.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. NowCast Tahminleri Aras\u0131nda" + }, + "description": "'Nowcast' tahmin varl\u0131\u011f\u0131n\u0131 etkinle\u015ftirmeyi se\u00e7erseniz, her tahmin aras\u0131ndaki dakika say\u0131s\u0131n\u0131 yap\u0131land\u0131rabilirsiniz. Sa\u011flanan tahmin say\u0131s\u0131, tahminler aras\u0131nda se\u00e7ilen dakika say\u0131s\u0131na ba\u011fl\u0131d\u0131r.", + "title": "ClimaCell Se\u00e7eneklerini G\u00fcncelleyin" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/zh-Hant.json b/homeassistant/components/climacell/translations/zh-Hant.json new file mode 100644 index 00000000000..309b39ab242 --- /dev/null +++ b/homeassistant/components/climacell/translations/zh-Hant.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "NowCast \u9810\u5831\u9593\u9694\u5206\u9418" + }, + "description": "\u5047\u5982\u9078\u64c7\u958b\u555f `nowcast` \u9810\u5831\u5be6\u9ad4\u3001\u5c07\u53ef\u4ee5\u8a2d\u5b9a\u9810\u5831\u983b\u7387\u9593\u9694\u5206\u9418\u6578\u3002\u6839\u64da\u6240\u8f38\u5165\u7684\u9593\u9694\u6642\u9593\u5c07\u6c7a\u5b9a\u9810\u5831\u7684\u6578\u76ee\u3002", + "title": "\u66f4\u65b0 ClimaCell \u9078\u9805" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/coinbase/translations/bg.json b/homeassistant/components/coinbase/translations/bg.json index cce4b6f5c2a..eb72ab1d10d 100644 --- a/homeassistant/components/coinbase/translations/bg.json +++ b/homeassistant/components/coinbase/translations/bg.json @@ -16,12 +16,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Coinbase \u0441 \u043f\u043e\u043c\u043e\u0449\u0442\u0430 \u043d\u0430 YAML \u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u043e.\n\n\u0412\u0430\u0448\u0430\u0442\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u043e\u0442 Home Assistant.\n\n\u041f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043e\u0442 \u0432\u0430\u0448\u0438\u044f \u0444\u0430\u0439\u043b configuration.yaml \u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant, \u0437\u0430 \u0434\u0430 \u043a\u043e\u0440\u0438\u0433\u0438\u0440\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.", - "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Coinbase \u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u0430" - } - }, "options": { "error": { "currency_unavailable": "\u0415\u0434\u043d\u043e \u0438\u043b\u0438 \u043f\u043e\u0432\u0435\u0447\u0435 \u043e\u0442 \u0438\u0441\u043a\u0430\u043d\u0438\u0442\u0435 \u0432\u0430\u043b\u0443\u0442\u043d\u0438 \u0441\u0430\u043b\u0434\u0430 \u043d\u0435 \u0441\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u044f\u0442 \u043e\u0442 \u0432\u0430\u0448\u0438\u044f Coinbase API.", diff --git a/homeassistant/components/coinbase/translations/ca.json b/homeassistant/components/coinbase/translations/ca.json index a545f8a278f..116b611f272 100644 --- a/homeassistant/components/coinbase/translations/ca.json +++ b/homeassistant/components/coinbase/translations/ca.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "La configuraci\u00f3 de Coinbase mitjan\u00e7ant YAML s'ha eliminat.\n\nHome Assistant ja no utilitza la configuraci\u00f3 YAML existent.\n\nElimina la configuraci\u00f3 YAML corresponent del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", - "title": "La configuraci\u00f3 YAML de Coinbase s'ha eliminat" - } - }, "options": { "error": { "currency_unavailable": "L'API de Coinbase no proporciona algun/s dels saldos de moneda que has sol\u00b7licitat.", diff --git a/homeassistant/components/coinbase/translations/de.json b/homeassistant/components/coinbase/translations/de.json index 8f91208e58f..f6200633950 100644 --- a/homeassistant/components/coinbase/translations/de.json +++ b/homeassistant/components/coinbase/translations/de.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "Die Konfiguration von Coinbase mit YAML wurde entfernt. \n\nDeine vorhandene YAML-Konfiguration wird von Home Assistant nicht verwendet. \n\nEntferne die YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Coinbase YAML-Konfiguration wurde entfernt" - } - }, "options": { "error": { "currency_unavailable": "Eine oder mehrere der angeforderten W\u00e4hrungssalden werden von deiner Coinbase-API nicht bereitgestellt.", diff --git a/homeassistant/components/coinbase/translations/el.json b/homeassistant/components/coinbase/translations/el.json index b4c1108b19a..6b2141f091f 100644 --- a/homeassistant/components/coinbase/translations/el.json +++ b/homeassistant/components/coinbase/translations/el.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Coinbase \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", - "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03c4\u03bf\u03c5 Coinbase \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af" - } - }, "options": { "error": { "currency_unavailable": "\u0388\u03bd\u03b1 \u03ae \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03b1\u03c0\u03cc \u03c4\u03b1 \u03b6\u03b7\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03b1 \u03c5\u03c0\u03cc\u03bb\u03bf\u03b9\u03c0\u03b1 \u03bd\u03bf\u03bc\u03b9\u03c3\u03bc\u03ac\u03c4\u03c9\u03bd \u03b4\u03b5\u03bd \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf API \u03c4\u03b7\u03c2 Coinbase.", diff --git a/homeassistant/components/coinbase/translations/en.json b/homeassistant/components/coinbase/translations/en.json index d755427d446..019159c8057 100644 --- a/homeassistant/components/coinbase/translations/en.json +++ b/homeassistant/components/coinbase/translations/en.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "Configuring Coinbase using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", - "title": "The Coinbase YAML configuration has been removed" - } - }, "options": { "error": { "currency_unavailable": "One or more of the requested currency balances is not provided by your Coinbase API.", diff --git a/homeassistant/components/coinbase/translations/es.json b/homeassistant/components/coinbase/translations/es.json index d0df4dee967..8d89b9b546b 100644 --- a/homeassistant/components/coinbase/translations/es.json +++ b/homeassistant/components/coinbase/translations/es.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "Se ha eliminado la configuraci\u00f3n de Coinbase mediante YAML. \n\nHome Assistant no utiliza tu configuraci\u00f3n YAML existente. \n\nElimina la configuraci\u00f3n YAML de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", - "title": "Se ha eliminado la configuraci\u00f3n YAML de Coinbase" - } - }, "options": { "error": { "currency_unavailable": "Tu API de Coinbase no proporciona uno o m\u00e1s de los saldos de divisas solicitados.", diff --git a/homeassistant/components/coinbase/translations/et.json b/homeassistant/components/coinbase/translations/et.json index d91201b3eb0..14bd1eea370 100644 --- a/homeassistant/components/coinbase/translations/et.json +++ b/homeassistant/components/coinbase/translations/et.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "Coinbase'i seadistamine YAML-i abil on eemaldatud.\n\nHome Assistant ei kasuta olemasolevat YAML-i konfiguratsiooni.\n\nEemalda sidumine failist configuration.yaml ja taask\u00e4ivita selle probleemi lahendamiseks Home Assistant.", - "title": "Coinbase YAML konfiguratsioon on eemaldatud" - } - }, "options": { "error": { "currency_unavailable": "Coinbase API ei paku \u00fchte v\u00f5i mitut soovitud valuutasaldot.", diff --git a/homeassistant/components/coinbase/translations/hu.json b/homeassistant/components/coinbase/translations/hu.json index 3eee97475f3..54122d29966 100644 --- a/homeassistant/components/coinbase/translations/hu.json +++ b/homeassistant/components/coinbase/translations/hu.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "A Coinbase YAML haszn\u00e1lat\u00e1val t\u00f6rt\u00e9n\u0151 konfigur\u00e1l\u00e1sa elt\u00e1vol\u00edt\u00e1sra ker\u00fclt.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3j\u00e1t a Home Assistant nem haszn\u00e1lja.\n\nA probl\u00e9ma megold\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", - "title": "A Coinbase YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fclt" - } - }, "options": { "error": { "currency_unavailable": "A k\u00e9rt valutaegyenlegek k\u00f6z\u00fcl egyet vagy t\u00f6bbet nem biztos\u00edt a Coinbase API.", diff --git a/homeassistant/components/coinbase/translations/id.json b/homeassistant/components/coinbase/translations/id.json index 7cb9c5c4992..114c69acce2 100644 --- a/homeassistant/components/coinbase/translations/id.json +++ b/homeassistant/components/coinbase/translations/id.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "Proses konfigurasi Integrasi Coinbase lewat YAML telah dihapus.\n\nKonfigurasi YAML yang ada tidak digunakan oleh Home Assistant.\n\nHapus konfigurasi YAML dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", - "title": "Konfigurasi YAML Integrasi Coinbase telah dihapus" - } - }, "options": { "error": { "currency_unavailable": "Satu atau beberapa saldo mata uang yang diminta tidak disediakan oleh API Coinbase Anda.", diff --git a/homeassistant/components/coinbase/translations/it.json b/homeassistant/components/coinbase/translations/it.json index 06507e2e71c..f26e08a727c 100644 --- a/homeassistant/components/coinbase/translations/it.json +++ b/homeassistant/components/coinbase/translations/it.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "La configurazione di Coinbase tramite YAML \u00e8 stata rimossa. \n\nLa tua configurazione YAML esistente non \u00e8 utilizzata da Home Assistant. \n\nRimuovi la configurazione YAML dal file configuration.yaml e riavvia Home Assistant per risolvere questo problema.", - "title": "La configurazione YAML di Coinbase \u00e8 stata rimossa" - } - }, "options": { "error": { "currency_unavailable": "Uno o pi\u00f9 dei saldi in valuta richiesti non sono forniti dalla tua API Coinbase.", diff --git a/homeassistant/components/coinbase/translations/nb.json b/homeassistant/components/coinbase/translations/nb.json index 4209449f49b..42a62fb5164 100644 --- a/homeassistant/components/coinbase/translations/nb.json +++ b/homeassistant/components/coinbase/translations/nb.json @@ -4,11 +4,6 @@ "unknown": "Uventet feil" } }, - "issues": { - "removed_yaml": { - "description": "Konfigurering av Coinbase med YAML er fjernet.\n\nDin eksisterende YAML-konfigurasjon brukes ikke av Home Assistant.\n\nFjern YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 l\u00f8se dette problemet." - } - }, "options": { "error": { "unknown": "Uventet feil" diff --git a/homeassistant/components/coinbase/translations/nl.json b/homeassistant/components/coinbase/translations/nl.json index 5c47abfebbd..472a15659c0 100644 --- a/homeassistant/components/coinbase/translations/nl.json +++ b/homeassistant/components/coinbase/translations/nl.json @@ -21,11 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "title": "De Coinbase YAML-configuratie is verwijderd" - } - }, "options": { "error": { "currency_unavailable": "Een of meer van de gevraagde valutabalansen wordt niet geleverd door uw Coinbase API.", diff --git a/homeassistant/components/coinbase/translations/no.json b/homeassistant/components/coinbase/translations/no.json index 1cac2a2e741..c3f2b34cf92 100644 --- a/homeassistant/components/coinbase/translations/no.json +++ b/homeassistant/components/coinbase/translations/no.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "Konfigurering av Coinbase med YAML er fjernet.\n\nDin eksisterende YAML-konfigurasjon brukes ikke av Home Assistant.\n\nFjern YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 l\u00f8se dette problemet.", - "title": "Coinbase YAML-konfigurasjonen er fjernet" - } - }, "options": { "error": { "currency_unavailable": "En eller flere av de forespurte valutasaldoene leveres ikke av Coinbase API.", diff --git a/homeassistant/components/coinbase/translations/pl.json b/homeassistant/components/coinbase/translations/pl.json index 3b81c0f7aca..70a1a021cdf 100644 --- a/homeassistant/components/coinbase/translations/pl.json +++ b/homeassistant/components/coinbase/translations/pl.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "Konfiguracja Coinbase za pomoc\u0105 YAML zosta\u0142a usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML nie jest u\u017cywana przez Home Assistant. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistant, aby rozwi\u0105za\u0107 ten problem.", - "title": "Konfiguracja YAML dla Coinbase zosta\u0142a usuni\u0119ta" - } - }, "options": { "error": { "currency_unavailable": "Jeden lub wi\u0119cej \u017c\u0105danych sald walutowych nie jest dostarczanych przez interfejs API Coinbase.", diff --git a/homeassistant/components/coinbase/translations/pt-BR.json b/homeassistant/components/coinbase/translations/pt-BR.json index 6a8afe42dba..5f2bb7d96e3 100644 --- a/homeassistant/components/coinbase/translations/pt-BR.json +++ b/homeassistant/components/coinbase/translations/pt-BR.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "A configura\u00e7\u00e3o do Coinbase usando YAML foi removida. \n\n Sua configura\u00e7\u00e3o YAML existente n\u00e3o \u00e9 usada pelo Home Assistant. \n\n Remova a configura\u00e7\u00e3o YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", - "title": "A configura\u00e7\u00e3o YAML do Coinbase foi removida" - } - }, "options": { "error": { "currency_unavailable": "Um ou mais dos saldos de moeda solicitados n\u00e3o s\u00e3o fornecidos pela sua API Coinbase.", diff --git a/homeassistant/components/coinbase/translations/ru.json b/homeassistant/components/coinbase/translations/ru.json index 85ec5646749..cbdf39e61a6 100644 --- a/homeassistant/components/coinbase/translations/ru.json +++ b/homeassistant/components/coinbase/translations/ru.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \"Coinbase\" \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f YAML \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Home Assistant.\n\n\u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", - "title": "\u0423\u0434\u0430\u043b\u0435\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Coinbase \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML" - } - }, "options": { "error": { "currency_unavailable": "\u041e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043d\u044b\u0445 \u043e\u0441\u0442\u0430\u0442\u043a\u043e\u0432 \u0432\u0430\u043b\u044e\u0442\u044b \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0412\u0430\u0448\u0438\u043c API Coinbase.", diff --git a/homeassistant/components/coinbase/translations/tr.json b/homeassistant/components/coinbase/translations/tr.json index ca91c29200e..b84e2bf740e 100644 --- a/homeassistant/components/coinbase/translations/tr.json +++ b/homeassistant/components/coinbase/translations/tr.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "Coinbase'i YAML kullanarak yap\u0131land\u0131rma kald\u0131r\u0131ld\u0131. \n\n Mevcut YAML yap\u0131land\u0131rman\u0131z Home Assistant taraf\u0131ndan kullan\u0131lm\u0131yor. \n\n YAML yap\u0131land\u0131rmas\u0131n\u0131 configuration.yaml dosyan\u0131zdan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", - "title": "Coinbase YAML yap\u0131land\u0131rmas\u0131 kald\u0131r\u0131ld\u0131" - } - }, "options": { "error": { "currency_unavailable": "\u0130stenen para birimi bakiyelerinden biri veya daha fazlas\u0131 Coinbase API'niz taraf\u0131ndan sa\u011flanm\u0131yor.", diff --git a/homeassistant/components/coinbase/translations/zh-Hant.json b/homeassistant/components/coinbase/translations/zh-Hant.json index 2b73d75d9bc..ea48d90fc7e 100644 --- a/homeassistant/components/coinbase/translations/zh-Hant.json +++ b/homeassistant/components/coinbase/translations/zh-Hant.json @@ -21,12 +21,6 @@ } } }, - "issues": { - "removed_yaml": { - "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a Coinbase \u7684\u529f\u80fd\u5373\u5c07\u79fb\u9664\u3002\n\nHome Assistant \u5c07\u4e0d\u518d\u4f7f\u7528\u73fe\u6709\u7684 YAML \u8a2d\u5b9a\u3002\n\n\u7531 configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 YAML \u8a2d\u5b9a\u4e26\u91cd\u555f Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", - "title": "Coinbase YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" - } - }, "options": { "error": { "currency_unavailable": "Coinbase API \u672a\u63d0\u4f9b\u4e00\u500b\u6216\u591a\u500b\u6240\u8981\u6c42\u7684\u8ca8\u5e63\u9918\u984d\u3002", diff --git a/homeassistant/components/ecowitt/translations/bg.json b/homeassistant/components/ecowitt/translations/bg.json index 92e4c1888a4..630925a02f5 100644 --- a/homeassistant/components/ecowitt/translations/bg.json +++ b/homeassistant/components/ecowitt/translations/bg.json @@ -1,9 +1,5 @@ { "config": { - "error": { - "invalid_port": "\u041f\u043e\u0440\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430.", - "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" - }, "step": { "user": { "description": "\u0421\u0438\u0433\u0443\u0440\u043d\u0438 \u043b\u0438 \u0441\u0442\u0435, \u0447\u0435 \u0438\u0441\u043a\u0430\u0442\u0435 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 Ecowitt?" diff --git a/homeassistant/components/ecowitt/translations/ca.json b/homeassistant/components/ecowitt/translations/ca.json index 5dd00992145..bfd4e1111cd 100644 --- a/homeassistant/components/ecowitt/translations/ca.json +++ b/homeassistant/components/ecowitt/translations/ca.json @@ -3,16 +3,8 @@ "create_entry": { "default": "Per acabar de configurar la integraci\u00f3, utilitza l'aplicaci\u00f3 Ecowitt (al m\u00f2bil) o v\u00e9s a Ecowitt WebUI a trav\u00e9s d'un navegador anant a l'adre\u00e7a IP de l'estaci\u00f3.\n\nTria la teva estaci\u00f3 -> Men\u00fa Altres -> Servidors de pujada DIY. Prem seg\u00fcent (next) i selecciona 'Personalitzat' ('Customized')\n\n- IP del servidor: `{servidor}`\n- Ruta: `{path}`\n- Port: `{port}`\n\nFes clic a 'Desa' ('Save')." }, - "error": { - "invalid_port": "Aquest port ja est\u00e0 en \u00fas.", - "unknown": "Error inesperat" - }, "step": { "user": { - "data": { - "path": "Cam\u00ed amb testimoni de seguretat", - "port": "Port d'escolta" - }, "description": "Est\u00e0s segur que vols configurar Ecowitt?" } } diff --git a/homeassistant/components/ecowitt/translations/cs.json b/homeassistant/components/ecowitt/translations/cs.json deleted file mode 100644 index b9301d0099d..00000000000 --- a/homeassistant/components/ecowitt/translations/cs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "config": { - "error": { - "invalid_port": "Port je ji\u017e pou\u017e\u00edv\u00e1n", - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/ecowitt/translations/de.json b/homeassistant/components/ecowitt/translations/de.json index 752363c97e7..4a3bfa9f31f 100644 --- a/homeassistant/components/ecowitt/translations/de.json +++ b/homeassistant/components/ecowitt/translations/de.json @@ -3,16 +3,8 @@ "create_entry": { "default": "Um die Integration abzuschlie\u00dfen, verwende die Ecowitt App (auf deinem Telefon) oder rufe die Ecowitt WebUI in einem Browser unter der IP-Adresse der Station auf.\n\nW\u00e4hle deine Station -> Men\u00fc Andere -> DIY Upload Servers. Klicke auf \"Weiter\" und w\u00e4hle \"Angepasst\".\n\n- Server IP: `{server}`\n- Pfad: `{path}`\n- Anschluss: `{port}`\n\nKlicke auf \"Speichern\"." }, - "error": { - "invalid_port": "Port wird bereits verwendet.", - "unknown": "Unerwarteter Fehler" - }, "step": { "user": { - "data": { - "path": "Pfad mit Sicherheits-Token", - "port": "Listening-Port" - }, "description": "M\u00f6chtest du Ecowitt wirklich einrichten?" } } diff --git a/homeassistant/components/ecowitt/translations/el.json b/homeassistant/components/ecowitt/translations/el.json index e8022f8808a..9c5910d71e6 100644 --- a/homeassistant/components/ecowitt/translations/el.json +++ b/homeassistant/components/ecowitt/translations/el.json @@ -3,16 +3,8 @@ "create_entry": { "default": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae Ecowitt App (\u03c3\u03c4\u03bf \u03c4\u03b7\u03bb\u03ad\u03c6\u03c9\u03bd\u03cc \u03c3\u03b1\u03c2) \u03ae \u03b1\u03c0\u03bf\u03ba\u03c4\u03ae\u03c3\u03c4\u03b5 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7 \u03c3\u03c4\u03bf Ecowitt WebUI \u03c3\u03b5 \u03ad\u03bd\u03b1 \u03c0\u03c1\u03cc\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1 \u03c0\u03b5\u03c1\u03b9\u03ae\u03b3\u03b7\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03c4\u03bf\u03c5 \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd.\n\n\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf \u03c3\u03c4\u03b1\u03b8\u03bc\u03cc \u03c3\u03b1\u03c2 -> \u039c\u03b5\u03bd\u03bf\u03cd \u0386\u03bb\u03bb\u03bf\u03b9 -> \u0395\u03be\u03c5\u03c0\u03b7\u03c1\u03b5\u03c4\u03b7\u03c4\u03ad\u03c2 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7\u03c2 DIY. \u03a0\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 next \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 'Customized' (\u03a0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03c3\u03bc\u03ad\u03bd\u03bf)\n\n- IP \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae: `{server}`\n- \u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae: `{path}`\n- \u0398\u03cd\u03c1\u03b1: `{port}`\n\n\u039a\u03ac\u03bd\u03c4\u03b5 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03b7\u03bd '\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7'." }, - "error": { - "invalid_port": "\u0397 \u03b8\u03cd\u03c1\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7.", - "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" - }, "step": { "user": { - "data": { - "path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03bc\u03b5 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2", - "port": "\u0398\u03cd\u03c1\u03b1 \u03b1\u03ba\u03c1\u03cc\u03b1\u03c3\u03b7\u03c2" - }, "description": "\u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03ba\u03c4\u03b5\u03bb\u03b5\u03c3\u03c4\u03bf\u03cd\u03bd \u03c4\u03b1 \u03b1\u03ba\u03cc\u03bb\u03bf\u03c5\u03b8\u03b1 \u03b2\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7\u03bd \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7. \n\n \u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae Ecowitt (\u03c3\u03c4\u03bf \u03c4\u03b7\u03bb\u03ad\u03c6\u03c9\u03bd\u03cc \u03c3\u03b1\u03c2) \u03ae \u03b1\u03c0\u03bf\u03ba\u03c4\u03ae\u03c3\u03c4\u03b5 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7 \u03c3\u03c4\u03bf Ecowitt WebUI \u03c3\u03b5 \u03ad\u03bd\u03b1 \u03c0\u03c1\u03cc\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1 \u03c0\u03b5\u03c1\u03b9\u03ae\u03b3\u03b7\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03c4\u03bf\u03c5 \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd.\n \u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf \u03c3\u03c4\u03b1\u03b8\u03bc\u03cc \u03c3\u03b1\u03c2 - > \u039c\u03b5\u03bd\u03bf\u03cd \u0386\u03bb\u03bb\u03b1 - > \u0394\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ad\u03c2 \u03bc\u03b5\u03c4\u03b1\u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7\u03c2 DIY.\n \u03a0\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 \u03b5\u03c0\u03cc\u03bc\u03b5\u03bd\u03bf \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \"\u03a0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03c3\u03bc\u03ad\u03bd\u03bf\" \n\n \u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf \u03c0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf Ecowitt \u03ba\u03b1\u03b9 \u03b2\u03ac\u03bb\u03c4\u03b5 \u03c4\u03bf ip/hostname \u03c4\u03bf\u03c5 hass server \u03c3\u03b1\u03c2.\n \u0397 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c4\u03b1\u03b9\u03c1\u03b9\u03ac\u03b6\u03b5\u03b9, \u03bc\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03b1\u03bd\u03c4\u03b9\u03b3\u03c1\u03ac\u03c8\u03b5\u03c4\u03b5 \u03bc\u03b5 \u03b1\u03c3\u03c6\u03b1\u03bb\u03ad\u03c2 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc /.\n \u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2. \u03a4\u03bf Ecowitt \u03b8\u03b1 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1 \u03bd\u03b1 \u03b1\u03c1\u03c7\u03af\u03c3\u03b5\u03b9 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03b5\u03af \u03bd\u03b1 \u03c3\u03c4\u03b5\u03af\u03bb\u03b5\u03b9 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 \u03c3\u03c4\u03bf\u03bd \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae \u03c3\u03b1\u03c2." } } diff --git a/homeassistant/components/ecowitt/translations/en.json b/homeassistant/components/ecowitt/translations/en.json index 77b50cd6462..b8ce69c10b8 100644 --- a/homeassistant/components/ecowitt/translations/en.json +++ b/homeassistant/components/ecowitt/translations/en.json @@ -3,16 +3,8 @@ "create_entry": { "default": "To finish setting up the integration, use the Ecowitt App (on your phone) or access the Ecowitt WebUI in a browser at the station IP address.\n\nPick your station -> Menu Others -> DIY Upload Servers. Hit next and select 'Customized'\n\n- Server IP: `{server}`\n- Path: `{path}`\n- Port: `{port}`\n\nClick on 'Save'." }, - "error": { - "invalid_port": "Port is already used.", - "unknown": "Unexpected error" - }, "step": { "user": { - "data": { - "path": "Path with Security token", - "port": "Listening port" - }, "description": "Are you sure you want to set up Ecowitt?" } } diff --git a/homeassistant/components/ecowitt/translations/es.json b/homeassistant/components/ecowitt/translations/es.json index 94e21c4782c..1c7816bc51c 100644 --- a/homeassistant/components/ecowitt/translations/es.json +++ b/homeassistant/components/ecowitt/translations/es.json @@ -3,16 +3,8 @@ "create_entry": { "default": "Para terminar de configurar la integraci\u00f3n, usa la aplicaci\u00f3n Ecowitt (en tu tel\u00e9fono) o accede a Ecowitt WebUI en un navegador en la direcci\u00f3n IP de la estaci\u00f3n. \n\nElige tu estaci\u00f3n - > Men\u00fa Otros - > Servidores de carga de bricolaje. Presiona siguiente y selecciona 'Personalizado' \n\n- IP del servidor: `{server}`\n- Ruta: `{path}`\n- Puerto: `{port}` \n\nHaz clic en 'Guardar'." }, - "error": { - "invalid_port": "El puerto ya est\u00e1 en uso.", - "unknown": "Error inesperado" - }, "step": { "user": { - "data": { - "path": "Ruta con token de seguridad", - "port": "Puerto de escucha" - }, "description": "\u00bfEst\u00e1s seguro de que quieres configurar Ecowitt?" } } diff --git a/homeassistant/components/ecowitt/translations/et.json b/homeassistant/components/ecowitt/translations/et.json index e132191ca37..2e07e97953f 100644 --- a/homeassistant/components/ecowitt/translations/et.json +++ b/homeassistant/components/ecowitt/translations/et.json @@ -3,16 +3,8 @@ "create_entry": { "default": "Sidumise seadistamise l\u00f5petamiseks kasuta Ecowitti rakendust (telefonis) v\u00f5i sisene Ecowitt WebUI-sse brauseris jaama IP-aadressil.\n\nVali oma jaam -> men\u00fc\u00fc Muud -> DIY Upload Servers. Vajuta nuppu next ja vali 'Customized' (kohandatud)\n\n- Serveri IP: `{server}`\n- Path: `{path}`\n- Port: `{port}`\n\nVajuta nupule 'Save'." }, - "error": { - "invalid_port": "Port on juba kasutusel.", - "unknown": "Ootamatu t\u00f5rge" - }, "step": { "user": { - "data": { - "path": "Turvam\u00e4rgiga asukoht", - "port": "Kuulamisport" - }, "description": "Kas oled kindel, et soovid Ecowitti seadistada?" } } diff --git a/homeassistant/components/ecowitt/translations/fr.json b/homeassistant/components/ecowitt/translations/fr.json index c4f3bfbb937..c22d5aec5a7 100644 --- a/homeassistant/components/ecowitt/translations/fr.json +++ b/homeassistant/components/ecowitt/translations/fr.json @@ -1,14 +1,7 @@ { "config": { - "error": { - "invalid_port": "Le port est d\u00e9j\u00e0 utilis\u00e9.", - "unknown": "Erreur inattendue" - }, "step": { "user": { - "data": { - "port": "Port d'\u00e9coute" - }, "description": "Voulez-vous vraiment configurer Ecowitt\u00a0?" } } diff --git a/homeassistant/components/ecowitt/translations/he.json b/homeassistant/components/ecowitt/translations/he.json deleted file mode 100644 index 822dcf2be14..00000000000 --- a/homeassistant/components/ecowitt/translations/he.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "config": { - "error": { - "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/ecowitt/translations/hu.json b/homeassistant/components/ecowitt/translations/hu.json index 920602311bf..90b289d3257 100644 --- a/homeassistant/components/ecowitt/translations/hu.json +++ b/homeassistant/components/ecowitt/translations/hu.json @@ -3,16 +3,8 @@ "create_entry": { "default": "Az integr\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1s\u00e1nak befejez\u00e9s\u00e9hez haszn\u00e1lja az Ecowitt alkalmaz\u00e1st (a telefonj\u00e1n), vagy l\u00e9pjen be az Ecowitt WebUI-ba egy b\u00f6ng\u00e9sz\u0151ben az \u00e1llom\u00e1s IP-c\u00edm\u00e9n.\n\nV\u00e1lassza ki az \u00e1llom\u00e1s\u00e1t -> 'Others' men\u00fc -> 'DIY Upload Servers'. Nyomja meg a 'Next' gombot, \u00e9s v\u00e1lassza a 'Customized' lehet\u0151s\u00e9get.\n\n- Szerver IP: `{server}`\n- \u00datvonal: `{path}`\n- Port: `{port}`\n\nKattintson a 'Save' gombra." }, - "error": { - "invalid_port": "A port m\u00e1r haszn\u00e1latban van.", - "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" - }, "step": { "user": { - "data": { - "path": "Biztons\u00e1gi tokennel ell\u00e1tott el\u00e9r\u00e9si \u00fatvonal", - "port": "Figyel\u0151port" - }, "description": "Biztos benne, hogy be szeretn\u00e9 \u00e1ll\u00edtani: Ecowitt?" } } diff --git a/homeassistant/components/ecowitt/translations/id.json b/homeassistant/components/ecowitt/translations/id.json index 36479f19729..60f71335e10 100644 --- a/homeassistant/components/ecowitt/translations/id.json +++ b/homeassistant/components/ecowitt/translations/id.json @@ -3,16 +3,8 @@ "create_entry": { "default": "Untuk menyelesaikan pengaturan integrasi, gunakan Ecowitt App (pada ponsel Anda) atau akses Ecowitt WebUI di browser pada alamat IP stasiun.\n\nPilih stasiun Anda -> Menu Others -> DIY Upload Servers. Tekan 'Next' dan pilih 'Customized'\n\n- Server IP: `{server}`\n- Path: `{path}`\n- Port: `{port}`\n\nKlik 'Simpan'." }, - "error": { - "invalid_port": "Port sudah digunakan.", - "unknown": "Kesalahan yang tidak diharapkan" - }, "step": { "user": { - "data": { - "path": "Jalur dengan token Keamanan", - "port": "Port mendengarkan" - }, "description": "Yakin ingin menyiapkan Ecowitt?" } } diff --git a/homeassistant/components/ecowitt/translations/it.json b/homeassistant/components/ecowitt/translations/it.json index 91cfcd52fe2..1cc82c0cf0e 100644 --- a/homeassistant/components/ecowitt/translations/it.json +++ b/homeassistant/components/ecowitt/translations/it.json @@ -3,16 +3,8 @@ "create_entry": { "default": "Per completare la configurazione dell'integrazione, utilizzare l'App Ecowitt (sul telefono) o accedere all'Ecowitt WebUI in un browser all'indirizzo IP della stazione. \n\nScegli la tua stazione - > Menu Altri - > Server di caricamento fai-da-te. Premi Avanti e seleziona \"Personalizzata\" \n\n - IP del server: `{server}`\n - Percorso: `{path}`\n - Porta: `{port}` \n\n Fai clic su \"Salva\"." }, - "error": { - "invalid_port": "La porta \u00e8 gi\u00e0 utilizzata.", - "unknown": "Errore imprevisto" - }, "step": { "user": { - "data": { - "path": "Percorso con token di sicurezza", - "port": "Porta di ascolto" - }, "description": "Sei sicuro di voler configurare Ecowitt?" } } diff --git a/homeassistant/components/ecowitt/translations/ja.json b/homeassistant/components/ecowitt/translations/ja.json index 0853f5068b1..fed7a566b3b 100644 --- a/homeassistant/components/ecowitt/translations/ja.json +++ b/homeassistant/components/ecowitt/translations/ja.json @@ -3,16 +3,8 @@ "create_entry": { "default": "\u7d71\u5408\u306e\u8a2d\u5b9a\u3092\u5b8c\u4e86\u3059\u308b\u306b\u306f\u3001Ecowitt \u30a2\u30d7\u30ea (\u96fb\u8a71\u3067) \u3092\u4f7f\u7528\u3059\u308b\u304b\u3001\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u306e IP \u30a2\u30c9\u30ec\u30b9\u3067\u30d6\u30e9\u30a6\u30b6\u30fc\u3067 Ecowitt WebUI \u306b\u30a2\u30af\u30bb\u30b9\u3057\u307e\u3059\u3002 \n\n\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u3092\u9078\u629e - >\u30e1\u30cb\u30e5\u30fc\u306e [\u305d\u306e\u4ed6] - > DIY \u30a2\u30c3\u30d7\u30ed\u30fc\u30c9 \u30b5\u30fc\u30d0\u30fc] \u3092\u9078\u629e\u3057\u307e\u3059\u3002\u6b21\u306b\u30d2\u30c3\u30c8\u3057\u3001\u300c\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u300d\u3092\u9078\u629e\u3057\u307e\u3059\n\n - \u30b5\u30fc\u30d0\u30fc IP: ` {server} `\n - \u30d1\u30b9: ` {path} `\n - \u30dd\u30fc\u30c8: ` {port} ` \n\n \u300c\u4fdd\u5b58\u300d\u3092\u30af\u30ea\u30c3\u30af\u3057\u307e\u3059\u3002" }, - "error": { - "invalid_port": "\u30dd\u30fc\u30c8\u306f\u3059\u3067\u306b\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u3059\u3002", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" - }, "step": { "user": { - "data": { - "path": "\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30c8\u30fc\u30af\u30f3\u3092\u542b\u3080\u30d1\u30b9", - "port": "\u30ea\u30b9\u30cb\u30f3\u30b0\u30dd\u30fc\u30c8" - }, "description": "Ecowitt\u3092\u3001\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b\uff1f" } } diff --git a/homeassistant/components/ecowitt/translations/nb.json b/homeassistant/components/ecowitt/translations/nb.json deleted file mode 100644 index a22f7eef3d6..00000000000 --- a/homeassistant/components/ecowitt/translations/nb.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "config": { - "error": { - "unknown": "Uventet feil" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/ecowitt/translations/nl.json b/homeassistant/components/ecowitt/translations/nl.json index 112651607f4..67f34ac63aa 100644 --- a/homeassistant/components/ecowitt/translations/nl.json +++ b/homeassistant/components/ecowitt/translations/nl.json @@ -1,9 +1,5 @@ { "config": { - "error": { - "invalid_port": "Poort wordt al gebruikt.", - "unknown": "Onverwachte fout" - }, "step": { "user": { "description": "Weet u zeker dat u Ecowitt wilt instellen?" diff --git a/homeassistant/components/ecowitt/translations/no.json b/homeassistant/components/ecowitt/translations/no.json index 61372b6f49f..e41c2ef373c 100644 --- a/homeassistant/components/ecowitt/translations/no.json +++ b/homeassistant/components/ecowitt/translations/no.json @@ -3,16 +3,8 @@ "create_entry": { "default": "For \u00e5 fullf\u00f8re konfigureringen av integrasjonen, bruk Ecowitt-appen (p\u00e5 telefonen) eller g\u00e5 til Ecowitt WebUI i en nettleser p\u00e5 stasjonens IP-adresse. \n\n Velg stasjonen din - > Meny Andre - > DIY-opplastingsservere. Trykk neste og velg \"Tilpasset\" \n\n - Server IP: ` {server} `\n - Bane: ` {path} `\n - Port: ` {port} ` \n\n Klikk p\u00e5 'Lagre'." }, - "error": { - "invalid_port": "Porten er allerede i bruk.", - "unknown": "Uventet feil" - }, "step": { "user": { - "data": { - "path": "Bane med sikkerhetstoken", - "port": "Lytteport" - }, "description": "Er du sikker p\u00e5 at du vil sette opp Ecowitt?" } } diff --git a/homeassistant/components/ecowitt/translations/pl.json b/homeassistant/components/ecowitt/translations/pl.json index 64fb3e6e4ef..88769e0a810 100644 --- a/homeassistant/components/ecowitt/translations/pl.json +++ b/homeassistant/components/ecowitt/translations/pl.json @@ -3,16 +3,8 @@ "create_entry": { "default": "Aby zako\u0144czy\u0107 konfiguracj\u0119 integracji, u\u017cyj aplikacji Ecowitt (na telefonie) lub uzyskaj dost\u0119p do Ecowitt WebUI w przegl\u0105darce pod adresem IP stacji. \n\nWybierz swoj\u0105 stacj\u0119 - > Menu \"Others\" - > DIY Upload Servers. Kliknij dalej i wybierz \"Customized\" \n\n- IP serwera: `{server}`\n- \u015acie\u017cka: `{path}`\n- Port: `{port}` \n\nKliknij \u201eZapisz\u201d." }, - "error": { - "invalid_port": "Port jest ju\u017c u\u017cywany.", - "unknown": "Nieoczekiwany b\u0142\u0105d" - }, "step": { "user": { - "data": { - "path": "\u015acie\u017cka do tokena bezpiecze\u0144stwa", - "port": "Port nas\u0142uchiwania" - }, "description": "Czy chcesz rozpocz\u0105\u0107 konfiguracj\u0119?" } } diff --git a/homeassistant/components/ecowitt/translations/pt-BR.json b/homeassistant/components/ecowitt/translations/pt-BR.json index b0c23d7a35d..bfe6c893356 100644 --- a/homeassistant/components/ecowitt/translations/pt-BR.json +++ b/homeassistant/components/ecowitt/translations/pt-BR.json @@ -3,16 +3,8 @@ "create_entry": { "default": "Para finalizar a configura\u00e7\u00e3o da integra\u00e7\u00e3o, use o app Ecowitt (no seu smartphone) ou acesse o Ecowitt WebUI em um navegador no endere\u00e7o IP da esta\u00e7\u00e3o. \n\n Escolha sua esta\u00e7\u00e3o - > Menu Outros - > Servidores de Upload DIY. Clique em pr\u00f3ximo e selecione 'Personalizado' \n\n - IP do servidor: `{server}`\n - Caminho: `{path}`\n - Porta: `{port}` \n\n Clique em 'Salvar'." }, - "error": { - "invalid_port": "A porta j\u00e1 \u00e9 usada.", - "unknown": "Erro inesperado" - }, "step": { "user": { - "data": { - "path": "Caminho com token de seguran\u00e7a", - "port": "Porta de escuta" - }, "description": "Tem certeza de que deseja configurar o Ecowitt?" } } diff --git a/homeassistant/components/ecowitt/translations/pt.json b/homeassistant/components/ecowitt/translations/pt.json index 71a66816a83..366abdcfef0 100644 --- a/homeassistant/components/ecowitt/translations/pt.json +++ b/homeassistant/components/ecowitt/translations/pt.json @@ -3,15 +3,8 @@ "create_entry": { "default": "Para finalizar a configura\u00e7\u00e3o da integra\u00e7\u00e3o, use o Ecowitt App (no seu telefone) ou acesse o Ecowitt WebUI em um navegador no endere\u00e7o IP da esta\u00e7\u00e3o. \n\n Escolha sua esta\u00e7\u00e3o - > Menu Outros - > Servidores de Upload DIY. Clique em pr\u00f3ximo e selecione 'Personalizado' \n\n - IP do servidor: ` {server} `\n - Caminho: ` {path} `\n - Porta: ` {port} ` \n\n Clique em 'Salvar'." }, - "error": { - "invalid_port": "A porta j\u00e1 \u00e9 usada." - }, "step": { "user": { - "data": { - "path": "Caminho com token de seguran\u00e7a", - "port": "Porta de escuta" - }, "description": "Tem certeza de que deseja configurar o Ecowitt?" } } diff --git a/homeassistant/components/ecowitt/translations/ru.json b/homeassistant/components/ecowitt/translations/ru.json index 97532b0726b..2299e98db0b 100644 --- a/homeassistant/components/ecowitt/translations/ru.json +++ b/homeassistant/components/ecowitt/translations/ru.json @@ -3,16 +3,8 @@ "create_entry": { "default": "\u0427\u0442\u043e\u0431\u044b \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Ecowitt (\u043d\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0435) \u0438\u043b\u0438 \u0432\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Ecowitt \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043f\u043e IP-\u0430\u0434\u0440\u0435\u0441\u0443 \u0441\u0442\u0430\u043d\u0446\u0438\u0438. \n\n\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0432\u043e\u044e \u0441\u0442\u0430\u043d\u0446\u0438\u044e - > \u041c\u0435\u043d\u044e 'Others' - > 'DIY Upload Servers'. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 'Next' \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 'Customized'. \n\n- IP-\u0430\u0434\u0440\u0435\u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430: `{server}`\n- \u041f\u0443\u0442\u044c: `{path}`\n- \u041f\u043e\u0440\u0442: `{port}` \n\n\u041d\u0430\u0436\u043c\u0438\u0442\u0435 'Save'." }, - "error": { - "invalid_port": "\u041f\u043e\u0440\u0442 \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f.", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." - }, "step": { "user": { - "data": { - "path": "\u041f\u0443\u0442\u044c \u0441 \u0442\u043e\u043a\u0435\u043d\u043e\u043c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438", - "port": "\u041f\u043e\u0440\u0442 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f" - }, "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Ecowitt?" } } diff --git a/homeassistant/components/ecowitt/translations/sv.json b/homeassistant/components/ecowitt/translations/sv.json index 0edd1d70fa9..830741f4403 100644 --- a/homeassistant/components/ecowitt/translations/sv.json +++ b/homeassistant/components/ecowitt/translations/sv.json @@ -3,16 +3,8 @@ "create_entry": { "default": "F\u00f6r att avsluta inst\u00e4llningen av integrationen, anv\u00e4nd Ecowitt-appen (p\u00e5 din telefon) eller g\u00e5 till Ecowitt WebUI i en webbl\u00e4sare p\u00e5 stationens IP-adress. \n\n V\u00e4lj din station - > Meny \u00d6vriga - > DIY Upload Servers. Klicka p\u00e5 n\u00e4sta och v\u00e4lj \"Anpassad\" \n\n - Server-IP: ` {server} `\n - S\u00f6kv\u00e4g: ` {path} `\n - Port: ` {port} ` \n\n Klicka p\u00e5 'Spara'." }, - "error": { - "invalid_port": "Porten anv\u00e4nds redan.", - "unknown": "Ov\u00e4ntat fel" - }, "step": { "user": { - "data": { - "path": "S\u00f6kv\u00e4g med s\u00e4kerhetstoken", - "port": "Lyssningsport" - }, "description": "\u00c4r du s\u00e4ker p\u00e5 att du vill konfigurera Ecowitt?" } } diff --git a/homeassistant/components/ecowitt/translations/tr.json b/homeassistant/components/ecowitt/translations/tr.json index 8e4d6906e4b..3c529f0c856 100644 --- a/homeassistant/components/ecowitt/translations/tr.json +++ b/homeassistant/components/ecowitt/translations/tr.json @@ -3,16 +3,8 @@ "create_entry": { "default": "Entegrasyon kurulumunu tamamlamak i\u00e7in Ecowitt Uygulamas\u0131n\u0131 (telefonunuzda) kullan\u0131n veya istasyonun IP adresindeki bir taray\u0131c\u0131da Ecowitt WebUI'ye eri\u015fin. \n\n \u0130stasyonunuzu se\u00e7in - > Men\u00fc Di\u011ferleri - > Kendin Yap Y\u00fckleme Sunucular\u0131. \u0130leri'ye bas\u0131n ve '\u00d6zelle\u015ftirilmi\u015f'i se\u00e7in \n\n - Sunucu IP'si: ` {server} `\n - Yol: ` {path} `\n - Ba\u011flant\u0131 noktas\u0131: ` {port} ` \n\n 'Kaydet'e t\u0131klay\u0131n." }, - "error": { - "invalid_port": "Ba\u011flant\u0131 noktas\u0131 zaten kullan\u0131l\u0131yor.", - "unknown": "Beklenmeyen hata" - }, "step": { "user": { - "data": { - "path": "G\u00fcvenlik anahtar\u0131 i\u00e7eren yol", - "port": "Dinleme ba\u011flant\u0131 noktas\u0131" - }, "description": "Ecowitt'i kurmak istedi\u011finizden emin misiniz?" } } diff --git a/homeassistant/components/ecowitt/translations/zh-Hant.json b/homeassistant/components/ecowitt/translations/zh-Hant.json index 3ad87a5733b..69373927626 100644 --- a/homeassistant/components/ecowitt/translations/zh-Hant.json +++ b/homeassistant/components/ecowitt/translations/zh-Hant.json @@ -3,16 +3,8 @@ "create_entry": { "default": "\u5fc5\u9808\u57f7\u884c\u4ee5\u4e0b\u6b65\u9a5f\u4ee5\u8a2d\u5b9a\u6b64\u6574\u5408\u3001\u65bc\u624b\u6a5f\u4e0a\u4f7f\u7528 Ecowitt App \u6216\u4f7f\u7528\u700f\u89bd\u5668\u8f38\u5165\u7ad9\u9ede IP \u4f4d\u5740\u9032\u5165 Ecowitt WebUI\u3002\n\n\u9078\u64c7\u7ad9\u9ede -> \u9078\u55ae\u4e2d\u5176\u4ed6 -> DIY \u4e0a\u50b3\u4f3a\u670d\u5668\u3001\u9ede\u9078\u4e0b\u4e00\u6b65\u4e26\u9078\u64c7 '\u81ea\u8a02'\n\n- \u4f3a\u670d\u5668 IP\uff1a`{server}`\n- \u8def\u5f91\uff1a`{path}`\n- \u901a\u8a0a\u57e0\uff1a`{port}`\n\n\u9ede\u9078 '\u5132\u5b58'\u3002" }, - "error": { - "invalid_port": "\u901a\u8a0a\u57e0\u5df2\u88ab\u4f7f\u7528\u3002", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4" - }, "step": { "user": { - "data": { - "path": "\u52a0\u5bc6\u6b0a\u6756\u8def\u5f91", - "port": "\u76e3\u807d\u901a\u8a0a\u57e0" - }, "description": "\u662f\u5426\u8981\u8a2d\u5b9a Ecowitt\uff1f" } } diff --git a/homeassistant/components/fireservicerota/translations/bg.json b/homeassistant/components/fireservicerota/translations/bg.json index ae6e31fb3e0..738c6de5612 100644 --- a/homeassistant/components/fireservicerota/translations/bg.json +++ b/homeassistant/components/fireservicerota/translations/bg.json @@ -11,11 +11,6 @@ "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" }, "step": { - "reauth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u0430" - } - }, "reauth_confirm": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u0430" diff --git a/homeassistant/components/fireservicerota/translations/ca.json b/homeassistant/components/fireservicerota/translations/ca.json index 9961f00f38f..c46748da8bc 100644 --- a/homeassistant/components/fireservicerota/translations/ca.json +++ b/homeassistant/components/fireservicerota/translations/ca.json @@ -11,12 +11,6 @@ "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida" }, "step": { - "reauth": { - "data": { - "password": "Contrasenya" - }, - "description": "Els tokens d'autenticaci\u00f3 ja no s\u00f3n v\u00e0lids, inicia sessi\u00f3 per tornar-los a generar." - }, "reauth_confirm": { "data": { "password": "Contrasenya" diff --git a/homeassistant/components/fireservicerota/translations/cs.json b/homeassistant/components/fireservicerota/translations/cs.json index 7ae758dab52..bd87c16b325 100644 --- a/homeassistant/components/fireservicerota/translations/cs.json +++ b/homeassistant/components/fireservicerota/translations/cs.json @@ -11,12 +11,6 @@ "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" }, "step": { - "reauth": { - "data": { - "password": "Heslo" - }, - "description": "Ov\u011b\u0159ovac\u00ed tokeny jsou neplatn\u00e9. Chcete-li je znovu vytvo\u0159it, p\u0159ihlaste se." - }, "user": { "data": { "password": "Heslo", diff --git a/homeassistant/components/fireservicerota/translations/de.json b/homeassistant/components/fireservicerota/translations/de.json index 5be147663ff..8f571f27133 100644 --- a/homeassistant/components/fireservicerota/translations/de.json +++ b/homeassistant/components/fireservicerota/translations/de.json @@ -11,12 +11,6 @@ "invalid_auth": "Ung\u00fcltige Authentifizierung" }, "step": { - "reauth": { - "data": { - "password": "Passwort" - }, - "description": "Authentifizierungs-Tokens sind ung\u00fcltig, melde dich an, um sie neu zu erstellen." - }, "reauth_confirm": { "data": { "password": "Passwort" diff --git a/homeassistant/components/fireservicerota/translations/el.json b/homeassistant/components/fireservicerota/translations/el.json index bb6b01c97c8..d78f43c6212 100644 --- a/homeassistant/components/fireservicerota/translations/el.json +++ b/homeassistant/components/fireservicerota/translations/el.json @@ -11,12 +11,6 @@ "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" }, "step": { - "reauth": { - "data": { - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" - }, - "description": "\u03a4\u03b1 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03ac \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ad\u03b3\u03b9\u03bd\u03b1\u03bd \u03ac\u03ba\u03c5\u03c1\u03b1, \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c4\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac." - }, "reauth_confirm": { "data": { "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" diff --git a/homeassistant/components/fireservicerota/translations/en.json b/homeassistant/components/fireservicerota/translations/en.json index 71c4f757b53..38762b614f4 100644 --- a/homeassistant/components/fireservicerota/translations/en.json +++ b/homeassistant/components/fireservicerota/translations/en.json @@ -11,12 +11,6 @@ "invalid_auth": "Invalid authentication" }, "step": { - "reauth": { - "data": { - "password": "Password" - }, - "description": "Authentication tokens became invalid, login to recreate them." - }, "reauth_confirm": { "data": { "password": "Password" diff --git a/homeassistant/components/fireservicerota/translations/es-419.json b/homeassistant/components/fireservicerota/translations/es-419.json index cf14204ec0c..62f98f2dc38 100644 --- a/homeassistant/components/fireservicerota/translations/es-419.json +++ b/homeassistant/components/fireservicerota/translations/es-419.json @@ -1,9 +1,6 @@ { "config": { "step": { - "reauth": { - "description": "Los tokens de autenticaci\u00f3n dejaron de ser v\u00e1lidos, inicie sesi\u00f3n para volver a crearlos." - }, "user": { "data": { "url": "Sitio web" diff --git a/homeassistant/components/fireservicerota/translations/es.json b/homeassistant/components/fireservicerota/translations/es.json index 297653c7708..ddf96231ae7 100644 --- a/homeassistant/components/fireservicerota/translations/es.json +++ b/homeassistant/components/fireservicerota/translations/es.json @@ -11,12 +11,6 @@ "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" }, "step": { - "reauth": { - "data": { - "password": "Contrase\u00f1a" - }, - "description": "Los tokens de autenticaci\u00f3n dejaron de ser v\u00e1lidos, inicia sesi\u00f3n para volver a crearlos." - }, "reauth_confirm": { "data": { "password": "Contrase\u00f1a" diff --git a/homeassistant/components/fireservicerota/translations/et.json b/homeassistant/components/fireservicerota/translations/et.json index c949db5fd9c..c6400c1adeb 100644 --- a/homeassistant/components/fireservicerota/translations/et.json +++ b/homeassistant/components/fireservicerota/translations/et.json @@ -11,12 +11,6 @@ "invalid_auth": "Vigane autentimine" }, "step": { - "reauth": { - "data": { - "password": "Salas\u00f5na" - }, - "description": "Tuvastusstring aegus, taasloomiseks logi sisse." - }, "reauth_confirm": { "data": { "password": "Salas\u00f5na" diff --git a/homeassistant/components/fireservicerota/translations/fr.json b/homeassistant/components/fireservicerota/translations/fr.json index 835af421158..55d130e1d75 100644 --- a/homeassistant/components/fireservicerota/translations/fr.json +++ b/homeassistant/components/fireservicerota/translations/fr.json @@ -11,12 +11,6 @@ "invalid_auth": "Authentification non valide" }, "step": { - "reauth": { - "data": { - "password": "Mot de passe" - }, - "description": "Les jetons d'authentification ne sont plus valides, connectez-vous pour les recr\u00e9er." - }, "reauth_confirm": { "data": { "password": "Mot de passe" diff --git a/homeassistant/components/fireservicerota/translations/he.json b/homeassistant/components/fireservicerota/translations/he.json index 313b46455f2..79970f0235a 100644 --- a/homeassistant/components/fireservicerota/translations/he.json +++ b/homeassistant/components/fireservicerota/translations/he.json @@ -11,11 +11,6 @@ "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" }, "step": { - "reauth": { - "data": { - "password": "\u05e1\u05d9\u05e1\u05de\u05d4" - } - }, "reauth_confirm": { "data": { "password": "\u05e1\u05d9\u05e1\u05de\u05d4" diff --git a/homeassistant/components/fireservicerota/translations/hu.json b/homeassistant/components/fireservicerota/translations/hu.json index 47ce01be5d3..dc4edd2d080 100644 --- a/homeassistant/components/fireservicerota/translations/hu.json +++ b/homeassistant/components/fireservicerota/translations/hu.json @@ -11,12 +11,6 @@ "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s" }, "step": { - "reauth": { - "data": { - "password": "Jelsz\u00f3" - }, - "description": "A hiteles\u00edt\u00e9si tokenek \u00e9rv\u00e9nytelenn\u00e9 v\u00e1ltak, a l\u00e9trehoz\u00e1shoz jelentkezzen be." - }, "reauth_confirm": { "data": { "password": "Jelsz\u00f3" diff --git a/homeassistant/components/fireservicerota/translations/id.json b/homeassistant/components/fireservicerota/translations/id.json index c7a5c894f62..13430c261ef 100644 --- a/homeassistant/components/fireservicerota/translations/id.json +++ b/homeassistant/components/fireservicerota/translations/id.json @@ -11,12 +11,6 @@ "invalid_auth": "Autentikasi tidak valid" }, "step": { - "reauth": { - "data": { - "password": "Kata Sandi" - }, - "description": "Token autentikasi menjadi tidak valid, masuk untuk membuat token lagi." - }, "reauth_confirm": { "data": { "password": "Kata Sandi" diff --git a/homeassistant/components/fireservicerota/translations/it.json b/homeassistant/components/fireservicerota/translations/it.json index 1dbd077521e..41bcbb888d2 100644 --- a/homeassistant/components/fireservicerota/translations/it.json +++ b/homeassistant/components/fireservicerota/translations/it.json @@ -11,12 +11,6 @@ "invalid_auth": "Autenticazione non valida" }, "step": { - "reauth": { - "data": { - "password": "Password" - }, - "description": "I token di autenticazione non sono validi, esegui l'accesso per ricrearli." - }, "reauth_confirm": { "data": { "password": "Password" diff --git a/homeassistant/components/fireservicerota/translations/ja.json b/homeassistant/components/fireservicerota/translations/ja.json index 00358bb1f36..642077d2449 100644 --- a/homeassistant/components/fireservicerota/translations/ja.json +++ b/homeassistant/components/fireservicerota/translations/ja.json @@ -11,12 +11,6 @@ "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c" }, "step": { - "reauth": { - "data": { - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" - }, - "description": "\u8a8d\u8a3c\u30c8\u30fc\u30af\u30f3\u304c\u7121\u52b9\u306b\u306a\u3063\u305f\u306e\u3067\u3001\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u518d\u4f5c\u6210\u3057\u3066\u304f\u3060\u3055\u3044\u3002" - }, "user": { "data": { "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", diff --git a/homeassistant/components/fireservicerota/translations/ka.json b/homeassistant/components/fireservicerota/translations/ka.json index 422f3137d5c..84fed0f1964 100644 --- a/homeassistant/components/fireservicerota/translations/ka.json +++ b/homeassistant/components/fireservicerota/translations/ka.json @@ -11,12 +11,6 @@ "invalid_auth": "\u10db\u10ea\u10d3\u10d0\u10e0\u10d8 \u10d0\u10d5\u10d7\u10d4\u10dc\u10e2\u10d8\u10e4\u10d8\u10d9\u10d0\u10ea\u10d8\u10d0" }, "step": { - "reauth": { - "data": { - "password": "\u10de\u10d0\u10e0\u10dd\u10da\u10d8" - }, - "description": "\u10d0\u10d5\u10d7\u10d4\u10dc\u10e2\u10d8\u10e4\u10d8\u10d9\u10d0\u10ea\u10d8\u10d8\u10e1 \u10e2\u10dd\u10d9\u10d4\u10dc\u10d8 \u10db\u10ea\u10d3\u10d0\u10e0\u10d8\u10d0, \u10e8\u10d4\u10d3\u10d8\u10d7 \u10e1\u10d8\u10e1\u10e2\u10d4\u10db\u10d0\u10e8\u10d8 \u10ee\u10d4\u10da\u10d0\u10ee\u10da\u10d0 \u10e8\u10d4\u10e1\u10d0\u10e5\u10db\u10dc\u10d4\u10da\u10d0\u10d3." - }, "user": { "data": { "password": "\u10de\u10d0\u10e0\u10dd\u10da\u10d8", diff --git a/homeassistant/components/fireservicerota/translations/ko.json b/homeassistant/components/fireservicerota/translations/ko.json index 843371ed035..6326713da73 100644 --- a/homeassistant/components/fireservicerota/translations/ko.json +++ b/homeassistant/components/fireservicerota/translations/ko.json @@ -11,12 +11,6 @@ "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "step": { - "reauth": { - "data": { - "password": "\ube44\ubc00\ubc88\ud638" - }, - "description": "\uc778\uc99d \ud1a0\ud070\uc774 \ub354 \uc774\uc0c1 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc0dd\uc131\ud558\ub824\uba74 \ub85c\uadf8\uc778\ud574\uc8fc\uc138\uc694." - }, "user": { "data": { "password": "\ube44\ubc00\ubc88\ud638", diff --git a/homeassistant/components/fireservicerota/translations/lb.json b/homeassistant/components/fireservicerota/translations/lb.json index 9f852c8fdfb..b7473abf339 100644 --- a/homeassistant/components/fireservicerota/translations/lb.json +++ b/homeassistant/components/fireservicerota/translations/lb.json @@ -11,12 +11,6 @@ "invalid_auth": "Ong\u00eblteg Authentifikatioun" }, "step": { - "reauth": { - "data": { - "password": "Passwuert" - }, - "description": "Acc\u00e8s Jetons sin ong\u00eblteg, verbann dech fir se n\u00e9i z'erstellen" - }, "user": { "data": { "password": "Passwuert", diff --git a/homeassistant/components/fireservicerota/translations/nl.json b/homeassistant/components/fireservicerota/translations/nl.json index 62085c9a333..3a7179a7c1f 100644 --- a/homeassistant/components/fireservicerota/translations/nl.json +++ b/homeassistant/components/fireservicerota/translations/nl.json @@ -11,12 +11,6 @@ "invalid_auth": "Ongeldige authenticatie" }, "step": { - "reauth": { - "data": { - "password": "Wachtwoord" - }, - "description": "Authenticatietokens zijn ongeldig geworden, log in om ze opnieuw te maken." - }, "user": { "data": { "password": "Wachtwoord", diff --git a/homeassistant/components/fireservicerota/translations/no.json b/homeassistant/components/fireservicerota/translations/no.json index 9b228f15ccd..6e11be56f56 100644 --- a/homeassistant/components/fireservicerota/translations/no.json +++ b/homeassistant/components/fireservicerota/translations/no.json @@ -11,12 +11,6 @@ "invalid_auth": "Ugyldig godkjenning" }, "step": { - "reauth": { - "data": { - "password": "Passord" - }, - "description": "Autentiseringstokener ble ugyldige, logg inn for \u00e5 gjenskape dem." - }, "reauth_confirm": { "data": { "password": "Passord" diff --git a/homeassistant/components/fireservicerota/translations/pl.json b/homeassistant/components/fireservicerota/translations/pl.json index b0c45bea46b..d4a446aaf09 100644 --- a/homeassistant/components/fireservicerota/translations/pl.json +++ b/homeassistant/components/fireservicerota/translations/pl.json @@ -11,12 +11,6 @@ "invalid_auth": "Niepoprawne uwierzytelnienie" }, "step": { - "reauth": { - "data": { - "password": "Has\u0142o" - }, - "description": "Tokeny uwierzytelniaj\u0105ce straci\u0142y wa\u017cno\u015b\u0107. Zaloguj si\u0119, aby je odtworzy\u0107." - }, "reauth_confirm": { "data": { "password": "Has\u0142o" diff --git a/homeassistant/components/fireservicerota/translations/pt-BR.json b/homeassistant/components/fireservicerota/translations/pt-BR.json index 9e6abd2964a..82f4a820ffb 100644 --- a/homeassistant/components/fireservicerota/translations/pt-BR.json +++ b/homeassistant/components/fireservicerota/translations/pt-BR.json @@ -11,12 +11,6 @@ "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { - "reauth": { - "data": { - "password": "Senha" - }, - "description": "Os tokens de autentica\u00e7\u00e3o se tornaram inv\u00e1lidos, fa\u00e7a login para recri\u00e1-los." - }, "reauth_confirm": { "data": { "password": "Senha" diff --git a/homeassistant/components/fireservicerota/translations/pt.json b/homeassistant/components/fireservicerota/translations/pt.json index c78c9a5aba5..7c8a7dfb9d4 100644 --- a/homeassistant/components/fireservicerota/translations/pt.json +++ b/homeassistant/components/fireservicerota/translations/pt.json @@ -11,11 +11,6 @@ "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { - "reauth": { - "data": { - "password": "Palavra-passe" - } - }, "user": { "data": { "password": "Palavra-passe", diff --git a/homeassistant/components/fireservicerota/translations/ru.json b/homeassistant/components/fireservicerota/translations/ru.json index b6ae8c4280e..03be316df52 100644 --- a/homeassistant/components/fireservicerota/translations/ru.json +++ b/homeassistant/components/fireservicerota/translations/ru.json @@ -11,12 +11,6 @@ "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438." }, "step": { - "reauth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" - }, - "description": "\u0422\u043e\u043a\u0435\u043d\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b, \u0432\u043e\u0439\u0434\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0438\u0445 \u0437\u0430\u043d\u043e\u0432\u043e." - }, "reauth_confirm": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c" diff --git a/homeassistant/components/fireservicerota/translations/sk.json b/homeassistant/components/fireservicerota/translations/sk.json index 9b831d2d901..40fc5cd34be 100644 --- a/homeassistant/components/fireservicerota/translations/sk.json +++ b/homeassistant/components/fireservicerota/translations/sk.json @@ -10,11 +10,6 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { - "reauth": { - "data": { - "password": "Heslo" - } - }, "reauth_confirm": { "data": { "password": "Heslo" diff --git a/homeassistant/components/fireservicerota/translations/sl.json b/homeassistant/components/fireservicerota/translations/sl.json index e38e7f99169..7aa3a6ccee2 100644 --- a/homeassistant/components/fireservicerota/translations/sl.json +++ b/homeassistant/components/fireservicerota/translations/sl.json @@ -11,12 +11,6 @@ "invalid_auth": "Napaka pri overjanju" }, "step": { - "reauth": { - "data": { - "password": "Geslo" - }, - "description": "Overitveni \u017eetoni niso ve\u010d veljavni, ponovno se prijavite, da jih znova ustvarite." - }, "user": { "data": { "password": "Geslo", diff --git a/homeassistant/components/fireservicerota/translations/sv.json b/homeassistant/components/fireservicerota/translations/sv.json index 79167d65334..23624a5fb81 100644 --- a/homeassistant/components/fireservicerota/translations/sv.json +++ b/homeassistant/components/fireservicerota/translations/sv.json @@ -11,12 +11,6 @@ "invalid_auth": "Ogiltig autentisering" }, "step": { - "reauth": { - "data": { - "password": "L\u00f6senord" - }, - "description": "Autentiseringstokens blev ogiltiga, logga in f\u00f6r att \u00e5terskapa dem." - }, "user": { "data": { "password": "L\u00f6senord", diff --git a/homeassistant/components/fireservicerota/translations/tr.json b/homeassistant/components/fireservicerota/translations/tr.json index 2b9c1b9cb0a..155dcaaf43d 100644 --- a/homeassistant/components/fireservicerota/translations/tr.json +++ b/homeassistant/components/fireservicerota/translations/tr.json @@ -11,12 +11,6 @@ "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama" }, "step": { - "reauth": { - "data": { - "password": "Parola" - }, - "description": "Kimlik do\u011frulama jetonlar\u0131 ge\u00e7ersiz, yeniden olu\u015fturmak i\u00e7in oturum a\u00e7\u0131n." - }, "user": { "data": { "password": "Parola", diff --git a/homeassistant/components/fireservicerota/translations/uk.json b/homeassistant/components/fireservicerota/translations/uk.json index 2d3bf8c596e..199120a54ae 100644 --- a/homeassistant/components/fireservicerota/translations/uk.json +++ b/homeassistant/components/fireservicerota/translations/uk.json @@ -11,12 +11,6 @@ "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f." }, "step": { - "reauth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" - }, - "description": "\u0422\u043e\u043a\u0435\u043d\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u0457 \u043d\u0435\u0434\u0456\u0439\u0441\u043d\u0456, \u0443\u0432\u0456\u0439\u0434\u0456\u0442\u044c, \u0449\u043e\u0431 \u0441\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u0457\u0445 \u0437\u0430\u043d\u043e\u0432\u043e." - }, "user": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c", diff --git a/homeassistant/components/fireservicerota/translations/zh-Hant.json b/homeassistant/components/fireservicerota/translations/zh-Hant.json index af84ae5eab3..6d1b720296f 100644 --- a/homeassistant/components/fireservicerota/translations/zh-Hant.json +++ b/homeassistant/components/fireservicerota/translations/zh-Hant.json @@ -11,12 +11,6 @@ "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548" }, "step": { - "reauth": { - "data": { - "password": "\u5bc6\u78bc" - }, - "description": "\u8a8d\u8b49\u6b0a\u6756\u5df2\u7d93\u5931\u6548\uff0c\u8acb\u767b\u5165\u91cd\u65b0\u65b0\u589e\u3002" - }, "reauth_confirm": { "data": { "password": "\u5bc6\u78bc" diff --git a/homeassistant/components/fully_kiosk/translations/bg.json b/homeassistant/components/fully_kiosk/translations/bg.json index 8dbd96c7099..b25e4f02de1 100644 --- a/homeassistant/components/fully_kiosk/translations/bg.json +++ b/homeassistant/components/fully_kiosk/translations/bg.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/ca.json b/homeassistant/components/fully_kiosk/translations/ca.json index 2cb3945ed01..a0199949870 100644 --- a/homeassistant/components/fully_kiosk/translations/ca.json +++ b/homeassistant/components/fully_kiosk/translations/ca.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3", - "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", "unknown": "Error inesperat" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/cs.json b/homeassistant/components/fully_kiosk/translations/cs.json index 737979c68e8..b6786dfc3ef 100644 --- a/homeassistant/components/fully_kiosk/translations/cs.json +++ b/homeassistant/components/fully_kiosk/translations/cs.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/de.json b/homeassistant/components/fully_kiosk/translations/de.json index cd099ee8ef9..b20b20a08e3 100644 --- a/homeassistant/components/fully_kiosk/translations/de.json +++ b/homeassistant/components/fully_kiosk/translations/de.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", - "invalid_auth": "Ung\u00fcltige Authentifizierung", "unknown": "Unerwarteter Fehler" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/el.json b/homeassistant/components/fully_kiosk/translations/el.json index 9af93e0c36a..10025a5ca12 100644 --- a/homeassistant/components/fully_kiosk/translations/el.json +++ b/homeassistant/components/fully_kiosk/translations/el.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/en.json b/homeassistant/components/fully_kiosk/translations/en.json index 338c50514fb..24823d68a60 100644 --- a/homeassistant/components/fully_kiosk/translations/en.json +++ b/homeassistant/components/fully_kiosk/translations/en.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Failed to connect", - "invalid_auth": "Invalid authentication", "unknown": "Unexpected error" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/es.json b/homeassistant/components/fully_kiosk/translations/es.json index 1b617892060..6b878ebe6f8 100644 --- a/homeassistant/components/fully_kiosk/translations/es.json +++ b/homeassistant/components/fully_kiosk/translations/es.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "No se pudo conectar", - "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", "unknown": "Error inesperado" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/et.json b/homeassistant/components/fully_kiosk/translations/et.json index b046deb0527..b586d35aafb 100644 --- a/homeassistant/components/fully_kiosk/translations/et.json +++ b/homeassistant/components/fully_kiosk/translations/et.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "\u00dchendamine nurjus", - "invalid_auth": "Tuvastamine nurjus", "unknown": "Ootamatu t\u00f5rge" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/fr.json b/homeassistant/components/fully_kiosk/translations/fr.json index a4a0a822a88..a668af67773 100644 --- a/homeassistant/components/fully_kiosk/translations/fr.json +++ b/homeassistant/components/fully_kiosk/translations/fr.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "\u00c9chec de connexion", - "invalid_auth": "Authentification non valide", "unknown": "Erreur inattendue" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/he.json b/homeassistant/components/fully_kiosk/translations/he.json index 2a59e340e54..85cb9d13979 100644 --- a/homeassistant/components/fully_kiosk/translations/he.json +++ b/homeassistant/components/fully_kiosk/translations/he.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/hu.json b/homeassistant/components/fully_kiosk/translations/hu.json index cd08f258be2..722d4459786 100644 --- a/homeassistant/components/fully_kiosk/translations/hu.json +++ b/homeassistant/components/fully_kiosk/translations/hu.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/id.json b/homeassistant/components/fully_kiosk/translations/id.json index d9be1351db4..9696b4ced5e 100644 --- a/homeassistant/components/fully_kiosk/translations/id.json +++ b/homeassistant/components/fully_kiosk/translations/id.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Gagal terhubung", - "invalid_auth": "Autentikasi tidak valid", "unknown": "Kesalahan yang tidak diharapkan" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/it.json b/homeassistant/components/fully_kiosk/translations/it.json index f8b414166c9..2e4fce4c0d0 100644 --- a/homeassistant/components/fully_kiosk/translations/it.json +++ b/homeassistant/components/fully_kiosk/translations/it.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Impossibile connettersi", - "invalid_auth": "Autenticazione non valida", "unknown": "Errore imprevisto" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/ja.json b/homeassistant/components/fully_kiosk/translations/ja.json index b5ef5895312..0cefeae46ba 100644 --- a/homeassistant/components/fully_kiosk/translations/ja.json +++ b/homeassistant/components/fully_kiosk/translations/ja.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/nl.json b/homeassistant/components/fully_kiosk/translations/nl.json index 359990b3e69..4262578285b 100644 --- a/homeassistant/components/fully_kiosk/translations/nl.json +++ b/homeassistant/components/fully_kiosk/translations/nl.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Kan geen verbinding maken", - "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/no.json b/homeassistant/components/fully_kiosk/translations/no.json index 3234879412b..8fa6faa2a15 100644 --- a/homeassistant/components/fully_kiosk/translations/no.json +++ b/homeassistant/components/fully_kiosk/translations/no.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Tilkobling mislyktes", - "invalid_auth": "Ugyldig godkjenning", "unknown": "Uventet feil" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/pl.json b/homeassistant/components/fully_kiosk/translations/pl.json index 905cdeb6149..d74d269219b 100644 --- a/homeassistant/components/fully_kiosk/translations/pl.json +++ b/homeassistant/components/fully_kiosk/translations/pl.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "invalid_auth": "Niepoprawne uwierzytelnienie", "unknown": "Nieoczekiwany b\u0142\u0105d" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/pt-BR.json b/homeassistant/components/fully_kiosk/translations/pt-BR.json index 2649409ede7..de6f3a24ee9 100644 --- a/homeassistant/components/fully_kiosk/translations/pt-BR.json +++ b/homeassistant/components/fully_kiosk/translations/pt-BR.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Falha ao conectar", - "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/ru.json b/homeassistant/components/fully_kiosk/translations/ru.json index 00a9a3616ff..80783663c09 100644 --- a/homeassistant/components/fully_kiosk/translations/ru.json +++ b/homeassistant/components/fully_kiosk/translations/ru.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/sv.json b/homeassistant/components/fully_kiosk/translations/sv.json index 3aaa8ccb3aa..96e41fdcac2 100644 --- a/homeassistant/components/fully_kiosk/translations/sv.json +++ b/homeassistant/components/fully_kiosk/translations/sv.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Det gick inte att ansluta.", - "invalid_auth": "Ogiltig autentisering", "unknown": "Ov\u00e4ntat fel" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/tr.json b/homeassistant/components/fully_kiosk/translations/tr.json index 5d1e2c90e1b..e9130af340b 100644 --- a/homeassistant/components/fully_kiosk/translations/tr.json +++ b/homeassistant/components/fully_kiosk/translations/tr.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", - "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", "unknown": "Beklenmeyen hata" }, "step": { diff --git a/homeassistant/components/fully_kiosk/translations/zh-Hant.json b/homeassistant/components/fully_kiosk/translations/zh-Hant.json index 5b13923ac70..f2add6e2da6 100644 --- a/homeassistant/components/fully_kiosk/translations/zh-Hant.json +++ b/homeassistant/components/fully_kiosk/translations/zh-Hant.json @@ -5,7 +5,6 @@ }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", - "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { diff --git a/homeassistant/components/generic/translations/bg.json b/homeassistant/components/generic/translations/bg.json index 07c7f4e8734..1a93acfc2da 100644 --- a/homeassistant/components/generic/translations/bg.json +++ b/homeassistant/components/generic/translations/bg.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "\u041d\u044f\u043c\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430", "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." }, "error": { @@ -9,15 +8,6 @@ "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { - "confirm": { - "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u0437\u0430\u043f\u043e\u0447\u043d\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0432\u0430\u043d\u0435\u0442\u043e?" - }, - "content_type": { - "data": { - "content_type": "\u0422\u0438\u043f \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435" - }, - "description": "\u041f\u043e\u0441\u043e\u0447\u0435\u0442\u0435 \u0442\u0438\u043f\u0430 \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435 \u0437\u0430 \u043f\u043e\u0442\u043e\u043a\u0430." - }, "user": { "data": { "authentication": "\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", @@ -48,12 +38,6 @@ "description": "![\u041f\u0440\u0435\u0433\u043b\u0435\u0434 \u043d\u0430 \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043e\u0442 \u043a\u0430\u043c\u0435\u0440\u0430\u0442\u0430]({preview_url})", "title": "\u041f\u0440\u0435\u0433\u043b\u0435\u0434" }, - "content_type": { - "data": { - "content_type": "\u0422\u0438\u043f \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435" - }, - "description": "\u041f\u043e\u0441\u043e\u0447\u0435\u0442\u0435 \u0442\u0438\u043f\u0430 \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435 \u0437\u0430 \u043f\u043e\u0442\u043e\u043a\u0430." - }, "init": { "data": { "authentication": "\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", diff --git a/homeassistant/components/generic/translations/ca.json b/homeassistant/components/generic/translations/ca.json index 8a03666919e..38da6c6a2cd 100644 --- a/homeassistant/components/generic/translations/ca.json +++ b/homeassistant/components/generic/translations/ca.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "No s'han trobat dispositius a la xarxa", "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "URL mal format", "no_still_image_or_stream_url": "Has d'especificar almenys una imatge un URL de flux", "relative_url": "Els URL relatius no s'admeten", - "stream_file_not_found": "Fitxer no trobat mentre s'intentava connectar al flux de dades (est\u00e0 instal\u00b7lat ffmpeg?)", - "stream_http_not_found": "HTTP 404 'Not found' a l'intentar connectar-se al flux de dades ('stream')", "stream_io_error": "Error d'entrada/sortida mentre s'intentava connectar al flux de dades. Protocol de transport RTSP incorrecte?", "stream_no_route_to_host": "No s'ha pogut trobar l'amfitri\u00f3 mentre intentava connectar al flux de dades", - "stream_no_video": "El flux no cont\u00e9 v\u00eddeo", "stream_not_permitted": "Operaci\u00f3 no permesa mentre s'intentava connectar al flux de dades. Protocol de transport RTSP incorrecte?", - "stream_unauthorised": "L'autoritzaci\u00f3 ha fallat mentre s'intentava connectar amb el flux de dades", "template_error": "Error renderitzant plantilla. Consulta els registres per m\u00e9s informaci\u00f3.", "timeout": "El temps m\u00e0xim de c\u00e0rrega de l'URL ha expirat", "unable_still_load": "No s'ha pogut carregar cap imatge v\u00e0lida des de l'URL d'imatge fixa (pot ser per un amfitri\u00f3 o URL inv\u00e0lid o un error d'autenticaci\u00f3). Revisa els registres per a m\u00e9s informaci\u00f3.", "unknown": "Error inesperat" }, "step": { - "confirm": { - "description": "Vols comen\u00e7ar la configuraci\u00f3?" - }, - "content_type": { - "data": { - "content_type": "Tipus de contingut" - }, - "description": "Especifica el tipus de contingut per al flux de dades (stream)." - }, "user": { "data": { "authentication": "Autenticaci\u00f3", @@ -62,13 +48,9 @@ "malformed_url": "URL mal format", "no_still_image_or_stream_url": "Has d'especificar almenys una imatge un URL de flux", "relative_url": "Els URL relatius no s'admeten", - "stream_file_not_found": "Fitxer no trobat mentre s'intentava connectar al flux de dades (est\u00e0 instal\u00b7lat ffmpeg?)", - "stream_http_not_found": "HTTP 404 'Not found' a l'intentar connectar-se al flux de dades ('stream')", "stream_io_error": "Error d'entrada/sortida mentre s'intentava connectar al flux de dades. Protocol de transport RTSP incorrecte?", "stream_no_route_to_host": "No s'ha pogut trobar l'amfitri\u00f3 mentre intentava connectar al flux de dades", - "stream_no_video": "El flux no cont\u00e9 v\u00eddeo", "stream_not_permitted": "Operaci\u00f3 no permesa mentre s'intentava connectar al flux de dades. Protocol de transport RTSP incorrecte?", - "stream_unauthorised": "L'autoritzaci\u00f3 ha fallat mentre s'intentava connectar amb el flux de dades", "template_error": "Error renderitzant plantilla. Consulta els registres per m\u00e9s informaci\u00f3.", "timeout": "El temps m\u00e0xim de c\u00e0rrega de l'URL ha expirat", "unable_still_load": "No s'ha pogut carregar cap imatge v\u00e0lida des de l'URL d'imatge fixa (pot ser per un amfitri\u00f3 o URL inv\u00e0lid o un error d'autenticaci\u00f3). Revisa els registres per a m\u00e9s informaci\u00f3.", @@ -82,12 +64,6 @@ "description": "![Vista pr\u00e8via de la imatge de la c\u00e0mera]({preview_url})", "title": "Vista pr\u00e8via" }, - "content_type": { - "data": { - "content_type": "Tipus de contingut" - }, - "description": "Especifica el tipus de contingut per al flux de dades (stream)." - }, "init": { "data": { "authentication": "Autenticaci\u00f3", diff --git a/homeassistant/components/generic/translations/cs.json b/homeassistant/components/generic/translations/cs.json index 3e7973c2d8d..4cba991c071 100644 --- a/homeassistant/components/generic/translations/cs.json +++ b/homeassistant/components/generic/translations/cs.json @@ -1,17 +1,9 @@ { "config": { - "abort": { - "no_devices_found": "V s\u00edti nebyla nalezena \u017e\u00e1dn\u00e1 za\u0159\u00edzen\u00ed" - }, "error": { "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { - "content_type": { - "data": { - "content_type": "Typ obsahu" - } - }, "user": { "data": { "password": "Heslo", @@ -32,11 +24,6 @@ "confirm_still": { "title": "N\u00e1hled" }, - "content_type": { - "data": { - "content_type": "Typ obsahu" - } - }, "init": { "data": { "password": "Heslo", diff --git a/homeassistant/components/generic/translations/de.json b/homeassistant/components/generic/translations/de.json index 503f78fea32..d45de3644f4 100644 --- a/homeassistant/components/generic/translations/de.json +++ b/homeassistant/components/generic/translations/de.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "Falsch formatierte URL", "no_still_image_or_stream_url": "Du musst mindestens eine Standbild- oder Stream-URL angeben", "relative_url": "Relative URLs sind nicht zul\u00e4ssig", - "stream_file_not_found": "Datei nicht gefunden beim Versuch, eine Verbindung zum Stream herzustellen (ist ffmpeg installiert?)", - "stream_http_not_found": "HTTP 404 Not found beim Versuch, eine Verbindung zum Stream herzustellen", "stream_io_error": "Eingabe-/Ausgabefehler beim Versuch, eine Verbindung zum Stream herzustellen. Falsches RTSP-Transportprotokoll?", "stream_no_route_to_host": "Beim Versuch, eine Verbindung zum Stream herzustellen, konnte der Host nicht gefunden werden", - "stream_no_video": "Stream enth\u00e4lt kein Video", "stream_not_permitted": "Beim Versuch, eine Verbindung zum Stream herzustellen, ist ein Vorgang nicht zul\u00e4ssig. Falsches RTSP-Transportprotokoll?", - "stream_unauthorised": "Autorisierung beim Versuch, eine Verbindung zum Stream herzustellen, fehlgeschlagen", "template_error": "Fehler beim Rendern der Vorlage. \u00dcberpr\u00fcfe das Protokoll f\u00fcr weitere Informationen.", "timeout": "Zeit\u00fcberschreitung beim Laden der URL", "unable_still_load": "Es konnte kein g\u00fcltiges Bild von der Standbild-URL geladen werden (z. B. ung\u00fcltiger Host, URL oder Authentifizierungsfehler). \u00dcberpr\u00fcfe das Protokoll f\u00fcr weitere Informationen.", "unknown": "Unerwarteter Fehler" }, "step": { - "confirm": { - "description": "M\u00f6chtest Du mit der Einrichtung beginnen?" - }, - "content_type": { - "data": { - "content_type": "Inhaltstyp" - }, - "description": "Gib den Inhaltstyp des Streams an." - }, "user": { "data": { "authentication": "Authentifizierung", @@ -62,13 +48,9 @@ "malformed_url": "Falsch formatierte URL", "no_still_image_or_stream_url": "Du musst mindestens eine Standbild- oder Stream-URL angeben", "relative_url": "Relative URLs sind nicht zul\u00e4ssig", - "stream_file_not_found": "Datei nicht gefunden beim Versuch, eine Verbindung zum Stream herzustellen (ist ffmpeg installiert?)", - "stream_http_not_found": "HTTP 404 Not found beim Versuch, eine Verbindung zum Stream herzustellen", "stream_io_error": "Eingabe-/Ausgabefehler beim Versuch, eine Verbindung zum Stream herzustellen. Falsches RTSP-Transportprotokoll?", "stream_no_route_to_host": "Beim Versuch, eine Verbindung zum Stream herzustellen, konnte der Host nicht gefunden werden", - "stream_no_video": "Stream enth\u00e4lt kein Video", "stream_not_permitted": "Beim Versuch, eine Verbindung zum Stream herzustellen, ist ein Vorgang nicht zul\u00e4ssig. Falsches RTSP-Transportprotokoll?", - "stream_unauthorised": "Autorisierung beim Versuch, eine Verbindung zum Stream herzustellen, fehlgeschlagen", "template_error": "Fehler beim Rendern der Vorlage. \u00dcberpr\u00fcfe das Protokoll f\u00fcr weitere Informationen.", "timeout": "Zeit\u00fcberschreitung beim Laden der URL", "unable_still_load": "Es konnte kein g\u00fcltiges Bild von der Standbild-URL geladen werden (z. B. ung\u00fcltiger Host, URL oder Authentifizierungsfehler). \u00dcberpr\u00fcfe das Protokoll f\u00fcr weitere Informationen.", @@ -82,12 +64,6 @@ "description": "![Kamera-Standbildvorschau]({preview_url})", "title": "Vorschau" }, - "content_type": { - "data": { - "content_type": "Inhaltstyp" - }, - "description": "Gib den Inhaltstyp des Streams an." - }, "init": { "data": { "authentication": "Authentifizierung", diff --git a/homeassistant/components/generic/translations/el.json b/homeassistant/components/generic/translations/el.json index b3480bb85be..6486f13849c 100644 --- a/homeassistant/components/generic/translations/el.json +++ b/homeassistant/components/generic/translations/el.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf", "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "\u039b\u03b1\u03bd\u03b8\u03b1\u03c3\u03bc\u03ad\u03bd\u03b7 \u03bc\u03bf\u03c1\u03c6\u03ae URL", "no_still_image_or_stream_url": "\u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03ba\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03c5\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf\u03bd \u03bc\u03b9\u03b1 \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1 \u03ae \u03bc\u03b9\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c1\u03bf\u03ae\u03c2", "relative_url": "\u0394\u03b5\u03bd \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03bf\u03bd\u03c4\u03b1\u03b9 \u03bf\u03b9 \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ad\u03c2 \u03b4\u03b9\u03b5\u03c5\u03b8\u03cd\u03bd\u03c3\u03b5\u03b9\u03c2 URL", - "stream_file_not_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03b5 \u03c1\u03bf\u03ae (\u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b5\u03c3\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf \u03c4\u03bf ffmpeg;)", - "stream_http_not_found": "HTTP 404 \u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae", "stream_io_error": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5/\u03b5\u03be\u03cc\u03b4\u03bf\u03c5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae. \u039b\u03ac\u03b8\u03bf\u03c2 \u03c0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP;", "stream_no_route_to_host": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae", - "stream_no_video": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03b2\u03af\u03bd\u03c4\u03b5\u03bf", "stream_not_permitted": "\u0394\u03b5\u03bd \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03b5\u03c4\u03b1\u03b9 \u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03b5 \u03c1\u03bf\u03ae. \u039b\u03ac\u03b8\u03bf\u03c2 \u03c0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP;", - "stream_unauthorised": "\u0397 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7 \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae", "template_error": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b1\u03c0\u03cc\u03b4\u03bf\u03c3\u03b7\u03c2 \u03c0\u03c1\u03bf\u03c4\u03cd\u03c0\u03bf\u03c5. \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03ba\u03b1\u03c4\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2.", "timeout": "\u039b\u03ae\u03be\u03b7 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03bf\u03cd \u03bf\u03c1\u03af\u03bf\u03c5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7\u03c2 URL", "unable_still_load": "\u0391\u03b4\u03c5\u03bd\u03b1\u03bc\u03af\u03b1 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7\u03c2 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03b1\u03ba\u03af\u03bd\u03b7\u03c4\u03b7\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 (\u03c0.\u03c7. \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2, \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03ae \u03b1\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2). \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03ba\u03b1\u03c4\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2.", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "step": { - "confirm": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7;" - }, - "content_type": { - "data": { - "content_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5" - }, - "description": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03cd\u03c0\u03bf \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c1\u03bf\u03ae." - }, "user": { "data": { "authentication": "\u0395\u03bb\u03ad\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", @@ -62,13 +48,9 @@ "malformed_url": "\u039b\u03b1\u03bd\u03b8\u03b1\u03c3\u03bc\u03ad\u03bd\u03b7 \u03bc\u03bf\u03c1\u03c6\u03ae URL", "no_still_image_or_stream_url": "\u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03ba\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03c5\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf\u03bd \u03bc\u03b9\u03b1 \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1 \u03ae \u03bc\u03b9\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c1\u03bf\u03ae\u03c2", "relative_url": "\u0394\u03b5\u03bd \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03bf\u03bd\u03c4\u03b1\u03b9 \u03bf\u03b9 \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ad\u03c2 \u03b4\u03b9\u03b5\u03c5\u03b8\u03cd\u03bd\u03c3\u03b5\u03b9\u03c2 URL", - "stream_file_not_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03b5 \u03c1\u03bf\u03ae (\u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b5\u03c3\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf \u03c4\u03bf ffmpeg;)", - "stream_http_not_found": "HTTP 404 \u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae", "stream_io_error": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5/\u03b5\u03be\u03cc\u03b4\u03bf\u03c5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae. \u039b\u03ac\u03b8\u03bf\u03c2 \u03c0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP;", "stream_no_route_to_host": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae", - "stream_no_video": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03b2\u03af\u03bd\u03c4\u03b5\u03bf", "stream_not_permitted": "\u0394\u03b5\u03bd \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03b5\u03c4\u03b1\u03b9 \u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03b5 \u03c1\u03bf\u03ae. \u039b\u03ac\u03b8\u03bf\u03c2 \u03c0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP;", - "stream_unauthorised": "\u0397 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7 \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae", "template_error": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b1\u03c0\u03cc\u03b4\u03bf\u03c3\u03b7\u03c2 \u03c0\u03c1\u03bf\u03c4\u03cd\u03c0\u03bf\u03c5. \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03ba\u03b1\u03c4\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2.", "timeout": "\u039b\u03ae\u03be\u03b7 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03bf\u03cd \u03bf\u03c1\u03af\u03bf\u03c5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7\u03c2 URL", "unable_still_load": "\u0391\u03b4\u03c5\u03bd\u03b1\u03bc\u03af\u03b1 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7\u03c2 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03b1\u03ba\u03af\u03bd\u03b7\u03c4\u03b7\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 (\u03c0.\u03c7. \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2, \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03ae \u03b1\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2). \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03ba\u03b1\u03c4\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2.", @@ -82,12 +64,6 @@ "description": "! [\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03c3\u03ba\u03cc\u03c0\u03b7\u03c3\u03b7 \u03c3\u03c4\u03b1\u03c4\u03b9\u03ba\u03ae\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 \u03ba\u03ac\u03bc\u03b5\u03c1\u03b1\u03c2] ({preview_url})", "title": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03c3\u03ba\u03cc\u03c0\u03b7\u03c3\u03b7" }, - "content_type": { - "data": { - "content_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5" - }, - "description": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03cd\u03c0\u03bf \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c1\u03bf\u03ae." - }, "init": { "data": { "authentication": "\u0395\u03bb\u03ad\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", diff --git a/homeassistant/components/generic/translations/en.json b/homeassistant/components/generic/translations/en.json index a9ea9a82d13..1215078ca33 100644 --- a/homeassistant/components/generic/translations/en.json +++ b/homeassistant/components/generic/translations/en.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "No devices found on the network", "single_instance_allowed": "Already configured. Only a single configuration possible." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "Malformed URL", "no_still_image_or_stream_url": "You must specify at least a still image or stream URL", "relative_url": "Relative URLs are not allowed", - "stream_file_not_found": "File not found while trying to connect to stream (is ffmpeg installed?)", - "stream_http_not_found": "HTTP 404 Not found while trying to connect to stream", "stream_io_error": "Input/Output error while trying to connect to stream. Wrong RTSP transport protocol?", "stream_no_route_to_host": "Could not find host while trying to connect to stream", - "stream_no_video": "Stream has no video", "stream_not_permitted": "Operation not permitted while trying to connect to stream. Wrong RTSP transport protocol?", - "stream_unauthorised": "Authorisation failed while trying to connect to stream", "template_error": "Error rendering template. Review log for more info.", "timeout": "Timeout while loading URL", "unable_still_load": "Unable to load valid image from still image URL (e.g. invalid host, URL or authentication failure). Review log for more info.", "unknown": "Unexpected error" }, "step": { - "confirm": { - "description": "Do you want to start set up?" - }, - "content_type": { - "data": { - "content_type": "Content Type" - }, - "description": "Specify the content type for the stream." - }, "user": { "data": { "authentication": "Authentication", @@ -62,13 +48,9 @@ "malformed_url": "Malformed URL", "no_still_image_or_stream_url": "You must specify at least a still image or stream URL", "relative_url": "Relative URLs are not allowed", - "stream_file_not_found": "File not found while trying to connect to stream (is ffmpeg installed?)", - "stream_http_not_found": "HTTP 404 Not found while trying to connect to stream", "stream_io_error": "Input/Output error while trying to connect to stream. Wrong RTSP transport protocol?", "stream_no_route_to_host": "Could not find host while trying to connect to stream", - "stream_no_video": "Stream has no video", "stream_not_permitted": "Operation not permitted while trying to connect to stream. Wrong RTSP transport protocol?", - "stream_unauthorised": "Authorisation failed while trying to connect to stream", "template_error": "Error rendering template. Review log for more info.", "timeout": "Timeout while loading URL", "unable_still_load": "Unable to load valid image from still image URL (e.g. invalid host, URL or authentication failure). Review log for more info.", @@ -82,12 +64,6 @@ "description": "![Camera Still Image Preview]({preview_url})", "title": "Preview" }, - "content_type": { - "data": { - "content_type": "Content Type" - }, - "description": "Specify the content type for the stream." - }, "init": { "data": { "authentication": "Authentication", diff --git a/homeassistant/components/generic/translations/es.json b/homeassistant/components/generic/translations/es.json index 42362863623..564e0488dfa 100644 --- a/homeassistant/components/generic/translations/es.json +++ b/homeassistant/components/generic/translations/es.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "No se encontraron dispositivos en la red", "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "URL con formato incorrecto", "no_still_image_or_stream_url": "Debes especificar al menos una imagen fija o URL de transmisi\u00f3n", "relative_url": "No se permiten URLs relativas", - "stream_file_not_found": "No se encontr\u00f3 el archivo al intentar conectarse a la transmisi\u00f3n (\u00bfest\u00e1 instalado ffmpeg?)", - "stream_http_not_found": "HTTP 404 Not found al intentar conectarse a la transmisi\u00f3n", "stream_io_error": "Error de entrada/salida al intentar conectarse a la transmisi\u00f3n. \u00bfProtocolo de transporte RTSP incorrecto?", "stream_no_route_to_host": "No se pudo encontrar el host al intentar conectarse a la transmisi\u00f3n", - "stream_no_video": "La transmisi\u00f3n no tiene video", "stream_not_permitted": "Operaci\u00f3n no permitida al intentar conectarse a la transmisi\u00f3n. \u00bfProtocolo de transporte RTSP incorrecto?", - "stream_unauthorised": "La autorizaci\u00f3n fall\u00f3 al intentar conectarse a la transmisi\u00f3n", "template_error": "Error al renderizar la plantilla. Revisa el registro para obtener m\u00e1s informaci\u00f3n.", "timeout": "Tiempo de espera al cargar la URL", "unable_still_load": "No se puede cargar una imagen v\u00e1lida desde la URL de la imagen fija (p. ej., host no v\u00e1lido, URL o error de autenticaci\u00f3n). Revisa el registro para obtener m\u00e1s informaci\u00f3n.", "unknown": "Error inesperado" }, "step": { - "confirm": { - "description": "\u00bfQuieres iniciar la configuraci\u00f3n?" - }, - "content_type": { - "data": { - "content_type": "Tipos de contenido" - }, - "description": "Especifica el tipo de contenido para el flujo." - }, "user": { "data": { "authentication": "Autenticaci\u00f3n", @@ -62,13 +48,9 @@ "malformed_url": "URL con formato incorrecto", "no_still_image_or_stream_url": "Debes especificar al menos una imagen fija o URL de transmisi\u00f3n", "relative_url": "No se permiten URLs relativas", - "stream_file_not_found": "No se encontr\u00f3 el archivo al intentar conectarse a la transmisi\u00f3n (\u00bfest\u00e1 instalado ffmpeg?)", - "stream_http_not_found": "HTTP 404 Not found al intentar conectarse a la transmisi\u00f3n", "stream_io_error": "Error de entrada/salida al intentar conectarse a la transmisi\u00f3n. \u00bfProtocolo de transporte RTSP incorrecto?", "stream_no_route_to_host": "No se pudo encontrar el host al intentar conectarse a la transmisi\u00f3n", - "stream_no_video": "La transmisi\u00f3n no tiene video", "stream_not_permitted": "Operaci\u00f3n no permitida al intentar conectarse a la transmisi\u00f3n. \u00bfProtocolo de transporte RTSP incorrecto?", - "stream_unauthorised": "La autorizaci\u00f3n fall\u00f3 al intentar conectarse a la transmisi\u00f3n", "template_error": "Error al renderizar la plantilla. Revisa el registro para obtener m\u00e1s informaci\u00f3n.", "timeout": "Tiempo de espera al cargar la URL", "unable_still_load": "No se puede cargar una imagen v\u00e1lida desde la URL de la imagen fija (p. ej., host no v\u00e1lido, URL o error de autenticaci\u00f3n). Revisa el registro para obtener m\u00e1s informaci\u00f3n.", @@ -82,12 +64,6 @@ "description": "![Vista previa de imagen fija de c\u00e1mara]({preview_url})", "title": "Vista previa" }, - "content_type": { - "data": { - "content_type": "Tipos de contenido" - }, - "description": "Especifica el tipo de contenido para el flujo." - }, "init": { "data": { "authentication": "Autenticaci\u00f3n", diff --git a/homeassistant/components/generic/translations/et.json b/homeassistant/components/generic/translations/et.json index e89e968a2f9..c5f9b045b15 100644 --- a/homeassistant/components/generic/translations/et.json +++ b/homeassistant/components/generic/translations/et.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "V\u00f5rgust ei leitud \u00fchtegi seadet", "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "Vigane URL", "no_still_image_or_stream_url": "Pead m\u00e4\u00e4rama v\u00e4hemalt liikumatu pildi v\u00f5i voo URL-i", "relative_url": "Osalised URL-id pole lubatud", - "stream_file_not_found": "Vooga \u00fchenduse loomisel ei leitud faili (kas ffmpeg on installitud?)", - "stream_http_not_found": "HTTP 404 viga kui \u00fcritatakse vooga \u00fchendust luua", "stream_io_error": "Sisend-/v\u00e4ljundviga vooga \u00fchenduse loomisel. Vale RTSP transpordiprotokoll?", "stream_no_route_to_host": "Vooga \u00fchenduse loomisel ei leitud hosti", - "stream_no_video": "Voos pole videot", "stream_not_permitted": "Vooga \u00fchenduse loomisel pole toiming lubatud. Vale RTSP transpordiprotokoll?", - "stream_unauthorised": "Autoriseerimine eba\u00f5nnestus vooga \u00fchendamise ajal", "template_error": "Viga malli renderdamisel. Lisateabe saamiseks vaata logi.", "timeout": "URL-i laadimise ajal\u00f5pp", "unable_still_load": "Pilti ei saa laadida URL-ist (nt kehtetu host, URL v\u00f5i autentimise t\u00f5rge). Lisateabe saamiseks vaata logi.", "unknown": "Ootamatu t\u00f5rge" }, "step": { - "confirm": { - "description": "Kas alustada seadistamist?" - }, - "content_type": { - "data": { - "content_type": "Sisu t\u00fc\u00fcp" - }, - "description": "M\u00e4\u00e4ra voo sisut\u00fc\u00fcp." - }, "user": { "data": { "authentication": "Autentimine", @@ -62,13 +48,9 @@ "malformed_url": "Vigane URL", "no_still_image_or_stream_url": "Pead m\u00e4\u00e4rama v\u00e4hemalt liikumatu pildi v\u00f5i voo URL-i", "relative_url": "Osalised URL-id pole lubatud", - "stream_file_not_found": "Vooga \u00fchenduse loomisel ei leitud faili (kas ffmpeg on installitud?)", - "stream_http_not_found": "HTTP 404 viga kui \u00fcritatakse vooga \u00fchendust luua", "stream_io_error": "Sisend-/v\u00e4ljundviga vooga \u00fchenduse loomisel. Vale RTSP transpordiprotokoll?", "stream_no_route_to_host": "Vooga \u00fchenduse loomisel ei leitud hosti", - "stream_no_video": "Voos pole videot", "stream_not_permitted": "Vooga \u00fchenduse loomisel pole toiming lubatud. Vale RTSP transpordiprotokoll?", - "stream_unauthorised": "Autoriseerimine eba\u00f5nnestus vooga \u00fchendamise ajal", "template_error": "Viga malli renderdamisel. Lisateabe saamiseks vaata logi.", "timeout": "URL-i laadimise ajal\u00f5pp", "unable_still_load": "Pilti ei saa laadida URL-ist (nt kehtetu host, URL v\u00f5i autentimise t\u00f5rge). Lisateabe saamiseks vaata logi.", @@ -82,12 +64,6 @@ "description": "![Camera Still Image Preview]({preview_url})", "title": "Eelvaade" }, - "content_type": { - "data": { - "content_type": "Sisu t\u00fc\u00fcp" - }, - "description": "M\u00e4\u00e4ra voo sisut\u00fc\u00fcp." - }, "init": { "data": { "authentication": "Autentimine", diff --git a/homeassistant/components/generic/translations/fr.json b/homeassistant/components/generic/translations/fr.json index 1592208480c..538ff8ae1fe 100644 --- a/homeassistant/components/generic/translations/fr.json +++ b/homeassistant/components/generic/translations/fr.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau", "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "URL mal form\u00e9e", "no_still_image_or_stream_url": "Vous devez au moins renseigner une URL d'image fixe ou de flux", "relative_url": "Les URL relatives ne sont pas autoris\u00e9es", - "stream_file_not_found": "Fichier non trouv\u00e9 lors de la tentative de connexion au flux (ffmpeg est-il install\u00e9\u00a0?)", - "stream_http_not_found": "Erreur\u00a0404 (introuvable) lors de la tentative de connexion au flux", "stream_io_error": "Erreur d'entr\u00e9e/sortie lors de la tentative de connexion au flux. Mauvais protocole de transport RTSP\u00a0?", "stream_no_route_to_host": "Impossible de trouver l'h\u00f4te lors de la tentative de connexion au flux", - "stream_no_video": "Le flux ne contient pas de vid\u00e9o", "stream_not_permitted": "Op\u00e9ration non autoris\u00e9e lors de la tentative de connexion au flux. Mauvais protocole de transport RTSP\u00a0?", - "stream_unauthorised": "\u00c9chec de l'autorisation lors de la tentative de connexion au flux", "template_error": "Erreur lors du rendu du mod\u00e8le. Consultez le journal pour plus d'informations.", "timeout": "D\u00e9lai d'attente expir\u00e9 lors du chargement de l'URL", "unable_still_load": "Impossible de charger une image valide depuis l'URL d'image fixe (cela pourrait \u00eatre d\u00fb \u00e0 un h\u00f4te ou \u00e0 une URL non valide, ou \u00e0 un \u00e9chec de l'authentification). Consultez le journal pour plus d'informations.", "unknown": "Erreur inattendue" }, "step": { - "confirm": { - "description": "Voulez-vous commencer la configuration\u00a0?" - }, - "content_type": { - "data": { - "content_type": "Type de contenu" - }, - "description": "Sp\u00e9cifiez le type de contenu du flux." - }, "user": { "data": { "authentication": "Authentification", @@ -61,13 +47,9 @@ "malformed_url": "URL mal form\u00e9e", "no_still_image_or_stream_url": "Vous devez au moins renseigner une URL d'image fixe ou de flux", "relative_url": "Les URL relatives ne sont pas autoris\u00e9es", - "stream_file_not_found": "Fichier non trouv\u00e9 lors de la tentative de connexion au flux (ffmpeg est-il install\u00e9\u00a0?)", - "stream_http_not_found": "Erreur\u00a0404 (introuvable) lors de la tentative de connexion au flux", "stream_io_error": "Erreur d'entr\u00e9e/sortie lors de la tentative de connexion au flux. Mauvais protocole de transport RTSP\u00a0?", "stream_no_route_to_host": "Impossible de trouver l'h\u00f4te lors de la tentative de connexion au flux", - "stream_no_video": "Le flux ne contient pas de vid\u00e9o", "stream_not_permitted": "Op\u00e9ration non autoris\u00e9e lors de la tentative de connexion au flux. Mauvais protocole de transport RTSP\u00a0?", - "stream_unauthorised": "\u00c9chec de l'autorisation lors de la tentative de connexion au flux", "template_error": "Erreur lors du rendu du mod\u00e8le. Consultez le journal pour plus d'informations.", "timeout": "D\u00e9lai d'attente expir\u00e9 lors du chargement de l'URL", "unable_still_load": "Impossible de charger une image valide depuis l'URL d'image fixe (cela pourrait \u00eatre d\u00fb \u00e0 un h\u00f4te ou \u00e0 une URL non valide, ou \u00e0 un \u00e9chec de l'authentification). Consultez le journal pour plus d'informations.", @@ -80,12 +62,6 @@ }, "title": "Aper\u00e7u" }, - "content_type": { - "data": { - "content_type": "Type de contenu" - }, - "description": "Sp\u00e9cifiez le type de contenu du flux." - }, "init": { "data": { "authentication": "Authentification", diff --git a/homeassistant/components/generic/translations/he.json b/homeassistant/components/generic/translations/he.json index 6eaec7b7c9d..e92df2f384b 100644 --- a/homeassistant/components/generic/translations/he.json +++ b/homeassistant/components/generic/translations/he.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05e9\u05d2\u05d5\u05d9\u05d4", "no_still_image_or_stream_url": "\u05d9\u05e9 \u05dc\u05e6\u05d9\u05d9\u05df \u05dc\u05e4\u05d7\u05d5\u05ea \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 \u05d0\u05d5 \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05e9\u05dc \u05d4\u05d6\u05e8\u05de\u05d4", "relative_url": "\u05db\u05ea\u05d5\u05d1\u05d5\u05ea \u05d0\u05ea\u05e8\u05d9\u05dd \u05d9\u05d7\u05e1\u05d9\u05d5\u05ea \u05d0\u05d9\u05e0\u05df \u05de\u05d5\u05ea\u05e8\u05d5\u05ea", - "stream_file_not_found": "\u05d4\u05e7\u05d5\u05d1\u05e5 \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4 (\u05d4\u05d0\u05dd ffmpeg \u05de\u05d5\u05ea\u05e7\u05df?)", - "stream_http_not_found": "HTTP 404 \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", "stream_io_error": "\u05e9\u05d2\u05d9\u05d0\u05ea \u05e7\u05dc\u05d8/\u05e4\u05dc\u05d8 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4. \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05ea\u05e2\u05d1\u05d5\u05e8\u05d4 \u05e9\u05d2\u05d5\u05d9 \u05e9\u05dc RTSP?", "stream_no_route_to_host": "\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05d4\u05d9\u05d4 \u05dc\u05de\u05e6\u05d5\u05d0 \u05d0\u05ea \u05d4\u05de\u05d0\u05e8\u05d7 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", - "stream_no_video": "\u05d0\u05d9\u05df \u05d5\u05d9\u05d3\u05d9\u05d0\u05d5 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", "stream_not_permitted": "\u05d4\u05e4\u05e2\u05d5\u05dc\u05d4 \u05d0\u05d9\u05e0\u05d4 \u05de\u05d5\u05ea\u05e8\u05ea \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4. \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05ea\u05e2\u05d1\u05d5\u05e8\u05d4 \u05e9\u05d2\u05d5\u05d9 \u05e9\u05dc RTSP?", - "stream_unauthorised": "\u05d4\u05d4\u05e8\u05e9\u05d0\u05d4 \u05e0\u05db\u05e9\u05dc\u05d4 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", "template_error": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05e2\u05d9\u05d1\u05d5\u05d3 \u05d4\u05ea\u05d1\u05e0\u05d9\u05ea. \u05e2\u05d9\u05d9\u05df \u05d1\u05d9\u05d5\u05de\u05df \u05dc\u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", "timeout": "\u05d6\u05de\u05df \u05e7\u05e6\u05d5\u05d1 \u05d1\u05e2\u05ea \u05d8\u05e2\u05d9\u05e0\u05ea \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8", "unable_still_load": "\u05d0\u05d9\u05df \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05d8\u05e2\u05d5\u05df \u05ea\u05de\u05d5\u05e0\u05d4 \u05d7\u05d5\u05e7\u05d9\u05ea \u05de\u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, \u05db\u05e9\u05dc \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9 \u05d1\u05de\u05d7\u05e9\u05d1 \u05de\u05d0\u05e8\u05d7, \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d0\u05d5 \u05d0\u05d9\u05de\u05d5\u05ea). \u05e0\u05d0 \u05dc\u05e2\u05d9\u05d9\u05df \u05d1\u05d9\u05d5\u05de\u05df \u05d4\u05e8\u05d9\u05e9\u05d5\u05dd \u05dc\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { - "confirm": { - "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05ea\u05d7\u05d9\u05dc \u05d1\u05d4\u05d2\u05d3\u05e8\u05d4?" - }, - "content_type": { - "data": { - "content_type": "\u05e1\u05d5\u05d2 \u05ea\u05d5\u05db\u05df" - }, - "description": "\u05e0\u05d0 \u05dc\u05e6\u05d9\u05d9\u05df \u05d0\u05ea \u05e1\u05d5\u05d2 \u05d4\u05ea\u05d5\u05db\u05df \u05e2\u05d1\u05d5\u05e8 \u05d4\u05d6\u05e8\u05dd." - }, "user": { "data": { "authentication": "\u05d0\u05d9\u05de\u05d5\u05ea", @@ -62,13 +48,9 @@ "malformed_url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05e9\u05d2\u05d5\u05d9\u05d4", "no_still_image_or_stream_url": "\u05d9\u05e9 \u05dc\u05e6\u05d9\u05d9\u05df \u05dc\u05e4\u05d7\u05d5\u05ea \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 \u05d0\u05d5 \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05e9\u05dc \u05d4\u05d6\u05e8\u05de\u05d4", "relative_url": "\u05db\u05ea\u05d5\u05d1\u05d5\u05ea \u05d0\u05ea\u05e8\u05d9\u05dd \u05d9\u05d7\u05e1\u05d9\u05d5\u05ea \u05d0\u05d9\u05e0\u05df \u05de\u05d5\u05ea\u05e8\u05d5\u05ea", - "stream_file_not_found": "\u05d4\u05e7\u05d5\u05d1\u05e5 \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4 (\u05d4\u05d0\u05dd ffmpeg \u05de\u05d5\u05ea\u05e7\u05df?)", - "stream_http_not_found": "HTTP 404 \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", "stream_io_error": "\u05e9\u05d2\u05d9\u05d0\u05ea \u05e7\u05dc\u05d8/\u05e4\u05dc\u05d8 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4. \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05ea\u05e2\u05d1\u05d5\u05e8\u05d4 \u05e9\u05d2\u05d5\u05d9 \u05e9\u05dc RTSP?", "stream_no_route_to_host": "\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05d4\u05d9\u05d4 \u05dc\u05de\u05e6\u05d5\u05d0 \u05d0\u05ea \u05d4\u05de\u05d0\u05e8\u05d7 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", - "stream_no_video": "\u05d0\u05d9\u05df \u05d5\u05d9\u05d3\u05d9\u05d0\u05d5 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", "stream_not_permitted": "\u05d4\u05e4\u05e2\u05d5\u05dc\u05d4 \u05d0\u05d9\u05e0\u05d4 \u05de\u05d5\u05ea\u05e8\u05ea \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4. \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05ea\u05e2\u05d1\u05d5\u05e8\u05d4 \u05e9\u05d2\u05d5\u05d9 \u05e9\u05dc RTSP?", - "stream_unauthorised": "\u05d4\u05d4\u05e8\u05e9\u05d0\u05d4 \u05e0\u05db\u05e9\u05dc\u05d4 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", "template_error": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05e2\u05d9\u05d1\u05d5\u05d3 \u05d4\u05ea\u05d1\u05e0\u05d9\u05ea. \u05e2\u05d9\u05d9\u05df \u05d1\u05d9\u05d5\u05de\u05df \u05dc\u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", "timeout": "\u05d6\u05de\u05df \u05e7\u05e6\u05d5\u05d1 \u05d1\u05e2\u05ea \u05d8\u05e2\u05d9\u05e0\u05ea \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8", "unable_still_load": "\u05d0\u05d9\u05df \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05d8\u05e2\u05d5\u05df \u05ea\u05de\u05d5\u05e0\u05d4 \u05d7\u05d5\u05e7\u05d9\u05ea \u05de\u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, \u05db\u05e9\u05dc \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9 \u05d1\u05de\u05d7\u05e9\u05d1 \u05de\u05d0\u05e8\u05d7, \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d0\u05d5 \u05d0\u05d9\u05de\u05d5\u05ea). \u05e0\u05d0 \u05dc\u05e2\u05d9\u05d9\u05df \u05d1\u05d9\u05d5\u05de\u05df \u05d4\u05e8\u05d9\u05e9\u05d5\u05dd \u05dc\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", @@ -82,12 +64,6 @@ "description": "![\u05ea\u05e6\u05d5\u05d2\u05d4 \u05de\u05e7\u05d3\u05d9\u05de\u05d4 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 \u05d1\u05de\u05e6\u05dc\u05de\u05d4]({preview_url})", "title": "\u05ea\u05e6\u05d5\u05d2\u05d4 \u05de\u05e7\u05d3\u05d9\u05de\u05d4" }, - "content_type": { - "data": { - "content_type": "\u05e1\u05d5\u05d2 \u05ea\u05d5\u05db\u05df" - }, - "description": "\u05e0\u05d0 \u05dc\u05e6\u05d9\u05d9\u05df \u05d0\u05ea \u05e1\u05d5\u05d2 \u05d4\u05ea\u05d5\u05db\u05df \u05e2\u05d1\u05d5\u05e8 \u05d4\u05d6\u05e8\u05dd." - }, "init": { "data": { "authentication": "\u05d0\u05d9\u05de\u05d5\u05ea", diff --git a/homeassistant/components/generic/translations/hu.json b/homeassistant/components/generic/translations/hu.json index 0b643a2e492..87a292910ab 100644 --- a/homeassistant/components/generic/translations/hu.json +++ b/homeassistant/components/generic/translations/hu.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "Hib\u00e1s URL", "no_still_image_or_stream_url": "Legal\u00e1bb egy \u00e1ll\u00f3k\u00e9pet vagy stream URL-c\u00edmet kell megadnia.", "relative_url": "A relat\u00edv URL-ek nem enged\u00e9lyezettek", - "stream_file_not_found": "F\u00e1jl nem tal\u00e1lhat\u00f3 a streamhez val\u00f3 csatlakoz\u00e1s sor\u00e1n (telep\u00edtve van az ffmpeg?)", - "stream_http_not_found": "HTTP 404 Not found - hiba az adatfolyamhoz val\u00f3 csatlakoz\u00e1s k\u00f6zben", "stream_io_error": "Bemeneti/kimeneti hiba t\u00f6rt\u00e9nt az adatfolyamhoz val\u00f3 kapcsol\u00f3d\u00e1s k\u00f6zben. Rossz RTSP sz\u00e1ll\u00edt\u00e1si protokoll?", "stream_no_route_to_host": "Nem tal\u00e1lhat\u00f3 a c\u00edm, mik\u00f6zben a rendszer az adatfolyamhoz pr\u00f3b\u00e1l csatlakozni", - "stream_no_video": "Az adatfolyamban nincs vide\u00f3", "stream_not_permitted": "A m\u0171velet nem enged\u00e9lyezett, mik\u00f6zben megpr\u00f3b\u00e1l csatlakozni a folyamhoz. Rossz fajta RTSP protokoll?", - "stream_unauthorised": "A hiteles\u00edt\u00e9s meghi\u00fasult, mik\u00f6zben megpr\u00f3b\u00e1lt csatlakozni az adatfolyamhoz", "template_error": "Hiba t\u00f6rt\u00e9nt a sablon renderel\u00e9se k\u00f6zben. Tov\u00e1bbi inform\u00e1ci\u00f3\u00e9rt tekintse \u00e1t a napl\u00f3t.", "timeout": "Id\u0151t\u00fall\u00e9p\u00e9s az URL bet\u00f6lt\u00e9se k\u00f6zben", "unable_still_load": "Nem siker\u00fclt \u00e9rv\u00e9nyes k\u00e9pet bet\u00f6lteni az \u00e1ll\u00f3k\u00e9p URL-c\u00edm\u00e9r\u0151l (pl. \u00e9rv\u00e9nytelen host, URL vagy hiteles\u00edt\u00e9si hiba). Tov\u00e1bbi inform\u00e1ci\u00f3\u00e9rt tekintse \u00e1t a napl\u00f3t.", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { - "confirm": { - "description": "El szeretn\u00e9 kezdeni a be\u00e1ll\u00edt\u00e1st?" - }, - "content_type": { - "data": { - "content_type": "Tartalom t\u00edpus" - }, - "description": "Az adatfolyam tartalomt\u00edpua (Content-Type)." - }, "user": { "data": { "authentication": "Hiteles\u00edt\u00e9s", @@ -62,13 +48,9 @@ "malformed_url": "Hib\u00e1s URL", "no_still_image_or_stream_url": "Legal\u00e1bb egy \u00e1ll\u00f3k\u00e9pet vagy stream URL-c\u00edmet kell megadnia.", "relative_url": "A relat\u00edv URL-ek nem enged\u00e9lyezettek", - "stream_file_not_found": "F\u00e1jl nem tal\u00e1lhat\u00f3 a streamhez val\u00f3 csatlakoz\u00e1s sor\u00e1n (telep\u00edtve van az ffmpeg?)", - "stream_http_not_found": "HTTP 404 Not found - hiba az adatfolyamhoz val\u00f3 csatlakoz\u00e1s k\u00f6zben", "stream_io_error": "Bemeneti/kimeneti hiba t\u00f6rt\u00e9nt az adatfolyamhoz val\u00f3 kapcsol\u00f3d\u00e1s k\u00f6zben. Rossz RTSP sz\u00e1ll\u00edt\u00e1si protokoll?", "stream_no_route_to_host": "Nem tal\u00e1lhat\u00f3 a c\u00edm, mik\u00f6zben a rendszer az adatfolyamhoz pr\u00f3b\u00e1l csatlakozni", - "stream_no_video": "Az adatfolyamban nincs vide\u00f3", "stream_not_permitted": "A m\u0171velet nem enged\u00e9lyezett, mik\u00f6zben megpr\u00f3b\u00e1l csatlakozni a folyamhoz. Rossz fajta RTSP protokoll?", - "stream_unauthorised": "A hiteles\u00edt\u00e9s meghi\u00fasult, mik\u00f6zben megpr\u00f3b\u00e1lt csatlakozni az adatfolyamhoz", "template_error": "Hiba t\u00f6rt\u00e9nt a sablon renderel\u00e9se k\u00f6zben. Tov\u00e1bbi inform\u00e1ci\u00f3\u00e9rt tekintse \u00e1t a napl\u00f3t.", "timeout": "Id\u0151t\u00fall\u00e9p\u00e9s az URL bet\u00f6lt\u00e9se k\u00f6zben", "unable_still_load": "Nem siker\u00fclt \u00e9rv\u00e9nyes k\u00e9pet bet\u00f6lteni az \u00e1ll\u00f3k\u00e9p URL-c\u00edm\u00e9r\u0151l (pl. \u00e9rv\u00e9nytelen host, URL vagy hiteles\u00edt\u00e9si hiba). Tov\u00e1bbi inform\u00e1ci\u00f3\u00e9rt tekintse \u00e1t a napl\u00f3t.", @@ -82,12 +64,6 @@ "description": "![Kamerak\u00e9p el\u0151n\u00e9zet]({preview_url})", "title": "El\u0151n\u00e9zet" }, - "content_type": { - "data": { - "content_type": "Tartalom t\u00edpus" - }, - "description": "Az adatfolyam tartalomt\u00edpua (Content-Type)." - }, "init": { "data": { "authentication": "Hiteles\u00edt\u00e9s", diff --git a/homeassistant/components/generic/translations/id.json b/homeassistant/components/generic/translations/id.json index f3796310744..7843f58e7a4 100644 --- a/homeassistant/components/generic/translations/id.json +++ b/homeassistant/components/generic/translations/id.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan", "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "URL salah format", "no_still_image_or_stream_url": "Anda harus menentukan setidaknya gambar diam atau URL streaming", "relative_url": "URL relatif tidak diizinkan", - "stream_file_not_found": "File tidak ditemukan saat mencoba menyambung ke streaming (sudahkah ffmpeg diinstal?)", - "stream_http_not_found": "HTTP 404 Tidak ditemukan saat mencoba menyambung ke streaming", "stream_io_error": "Kesalahan Input/Output saat mencoba menyambung ke streaming. Apakah protokol transportasi RTSP salah?", "stream_no_route_to_host": "Tidak dapat menemukan host saat mencoba menyambung ke streaming", - "stream_no_video": "Streaming tidak memiliki video", "stream_not_permitted": "Operasi tidak diizinkan saat mencoba menyambung ke streaming. Apakah protokol transportasi RTSP salah?", - "stream_unauthorised": "Otorisasi gagal saat mencoba menyambung ke streaming", "template_error": "Kesalahan saat merender templat. Tinjau log untuk info lebih lanjut.", "timeout": "Tenggang waktu habis saat memuat URL", "unable_still_load": "Tidak dapat memuat gambar yang valid dari URL gambar diam (mis. host yang tidak valid, URL, atau kegagalan autentikasi). Tinjau log untuk info lebih lanjut.", "unknown": "Kesalahan yang tidak diharapkan" }, "step": { - "confirm": { - "description": "Ingin memulai penyiapan?" - }, - "content_type": { - "data": { - "content_type": "Jenis Konten" - }, - "description": "Tentukan jenis konten untuk streaming." - }, "user": { "data": { "authentication": "Autentikasi", @@ -62,13 +48,9 @@ "malformed_url": "URL salah format", "no_still_image_or_stream_url": "Anda harus menentukan setidaknya gambar diam atau URL streaming", "relative_url": "URL relatif tidak diizinkan", - "stream_file_not_found": "File tidak ditemukan saat mencoba menyambung ke streaming (sudahkah ffmpeg diinstal?)", - "stream_http_not_found": "HTTP 404 Tidak ditemukan saat mencoba menyambung ke streaming", "stream_io_error": "Kesalahan Input/Output saat mencoba menyambung ke streaming. Apakah protokol transportasi RTSP salah?", "stream_no_route_to_host": "Tidak dapat menemukan host saat mencoba menyambung ke streaming", - "stream_no_video": "Streaming tidak memiliki video", "stream_not_permitted": "Operasi tidak diizinkan saat mencoba menyambung ke streaming. Apakah protokol transportasi RTSP salah?", - "stream_unauthorised": "Otorisasi gagal saat mencoba menyambung ke streaming", "template_error": "Kesalahan saat merender templat. Tinjau log untuk info lebih lanjut.", "timeout": "Tenggang waktu habis saat memuat URL", "unable_still_load": "Tidak dapat memuat gambar yang valid dari URL gambar diam (mis. host yang tidak valid, URL, atau kegagalan autentikasi). Tinjau log untuk info lebih lanjut.", @@ -82,12 +64,6 @@ "description": "![Pratinjau Gambar Diam Kamera]({preview_url})", "title": "Pratinjau" }, - "content_type": { - "data": { - "content_type": "Jenis Konten" - }, - "description": "Tentukan jenis konten untuk streaming." - }, "init": { "data": { "authentication": "Autentikasi", diff --git a/homeassistant/components/generic/translations/it.json b/homeassistant/components/generic/translations/it.json index 092e1aa45a6..3337d1e4552 100644 --- a/homeassistant/components/generic/translations/it.json +++ b/homeassistant/components/generic/translations/it.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "Nessun dispositivo trovato sulla rete", "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "URL non valido", "no_still_image_or_stream_url": "Devi specificare almeno un'immagine fissa o un URL di un flusso", "relative_url": "Non sono consentiti URL relativi", - "stream_file_not_found": "File non trovato durante il tentativo di connessione al (\u00e8 installato ffmpeg?)", - "stream_http_not_found": "HTTP 404 Non trovato durante il tentativo di connessione al flusso", "stream_io_error": "Errore di input/output durante il tentativo di connessione al flusso. Protocollo di trasporto RTSP errato?", "stream_no_route_to_host": "Impossibile trovare l'host durante il tentativo di connessione al flusso", - "stream_no_video": "Il flusso non ha video", "stream_not_permitted": "Operazione non consentita durante il tentativo di connessione al . Protocollo di trasporto RTSP errato?", - "stream_unauthorised": "Autorizzazione non riuscita durante il tentativo di connessione al flusso", "template_error": "Errore durante l'esecuzione del modello. Esamina il registro per ulteriori informazioni.", "timeout": "Timeout durante il caricamento dell'URL", "unable_still_load": "Impossibile caricare un'immagine valida dall'URL dell'immagine fissa (ad es. host, URL non valido o errore di autenticazione). Esamina il registro per ulteriori informazioni.", "unknown": "Errore imprevisto" }, "step": { - "confirm": { - "description": "Vuoi iniziare la configurazione?" - }, - "content_type": { - "data": { - "content_type": "Tipo di contenuto" - }, - "description": "Specificare il tipo di contenuto per il flusso." - }, "user": { "data": { "authentication": "Autenticazione", @@ -62,13 +48,9 @@ "malformed_url": "URL non valido", "no_still_image_or_stream_url": "Devi specificare almeno un'immagine fissa o un URL di un flusso", "relative_url": "Non sono consentiti URL relativi", - "stream_file_not_found": "File non trovato durante il tentativo di connessione al (\u00e8 installato ffmpeg?)", - "stream_http_not_found": "HTTP 404 Non trovato durante il tentativo di connessione al flusso", "stream_io_error": "Errore di input/output durante il tentativo di connessione al flusso. Protocollo di trasporto RTSP errato?", "stream_no_route_to_host": "Impossibile trovare l'host durante il tentativo di connessione al flusso", - "stream_no_video": "Il flusso non ha video", "stream_not_permitted": "Operazione non consentita durante il tentativo di connessione al . Protocollo di trasporto RTSP errato?", - "stream_unauthorised": "Autorizzazione non riuscita durante il tentativo di connessione al flusso", "template_error": "Errore durante l'esecuzione del modello. Esamina il registro per ulteriori informazioni.", "timeout": "Timeout durante il caricamento dell'URL", "unable_still_load": "Impossibile caricare un'immagine valida dall'URL dell'immagine fissa (ad es. host, URL non valido o errore di autenticazione). Esamina il registro per ulteriori informazioni.", @@ -82,12 +64,6 @@ "description": "![Anteprima immagine fissa della fotocamera]({preview_url})", "title": "Anteprima" }, - "content_type": { - "data": { - "content_type": "Tipo di contenuto" - }, - "description": "Specificare il tipo di contenuto per il flusso." - }, "init": { "data": { "authentication": "Autenticazione", diff --git a/homeassistant/components/generic/translations/ja.json b/homeassistant/components/generic/translations/ja.json index f07da6e04fc..9f06c618c3a 100644 --- a/homeassistant/components/generic/translations/ja.json +++ b/homeassistant/components/generic/translations/ja.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093", "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u8a2d\u5b9a\u3067\u304d\u308b\u306e\u306f1\u3064\u3060\u3051\u3067\u3059\u3002" }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "\u4e0d\u6b63\u306a\u5f62\u5f0f\u306eURL", "no_still_image_or_stream_url": "\u9759\u6b62\u753b\u50cf\u3082\u3057\u304f\u306f\u3001\u30b9\u30c8\u30ea\u30fc\u30e0URL\u306e\u3069\u3061\u3089\u304b\u3092\u6307\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", "relative_url": "\u76f8\u5bfeURL(Relative URLs)\u306f\u8a31\u53ef\u3055\u308c\u3066\u3044\u307e\u305b\u3093", - "stream_file_not_found": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u3068\u304d\u306b\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093(ffmpeg\u304c\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3055\u308c\u3066\u3044\u307e\u3059\u304b\uff1f)", - "stream_http_not_found": "\u30b9\u30c8\u30ea\u30fc\u30e0\u3078\u306e\u63a5\u7d9a\u6642\u306b\u3001HTTP 404\u3067\u898b\u3064\u304b\u308a\u307e\u305b\u3093", "stream_io_error": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u305f\u3068\u304d\u306b\u5165\u51fa\u529b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002RTSP\u30c8\u30e9\u30f3\u30b9\u30dd\u30fc\u30c8\u30d7\u30ed\u30c8\u30b3\u30eb\u3092\u9593\u9055\u3048\u305f\uff1f", "stream_no_route_to_host": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u307e\u3057\u305f\u304c\u3001\u30db\u30b9\u30c8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f", - "stream_no_video": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u52d5\u753b\u304c\u3042\u308a\u307e\u305b\u3093", "stream_not_permitted": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u9593\u3001\u64cd\u4f5c\u3067\u304d\u307e\u305b\u3093\u3002RTSP\u30c8\u30e9\u30f3\u30b9\u30dd\u30fc\u30c8\u30d7\u30ed\u30c8\u30b3\u30eb\u3092\u9593\u9055\u3048\u305f\uff1f", - "stream_unauthorised": "\u30b9\u30c8\u30ea\u30fc\u30e0\u3078\u306e\u63a5\u7d9a\u6642\u306b\u3001\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "template_error": "\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u30ec\u30f3\u30c0\u30ea\u30f3\u30b0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "timeout": "URL\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8", "unable_still_load": "\u9759\u6b62\u753b\u306eURL\u304b\u3089\u6709\u52b9\u306a\u753b\u50cf\u3092\u8aad\u307f\u8fbc\u3080\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\uff08\u4f8b: \u7121\u52b9\u306a\u30db\u30b9\u30c8\u3001URL\u3001\u307e\u305f\u306f\u8a8d\u8a3c\u5931\u6557)\u3002\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { - "confirm": { - "description": "\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u958b\u59cb\u3057\u307e\u3059\u304b\uff1f" - }, - "content_type": { - "data": { - "content_type": "\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u7a2e\u985e" - }, - "description": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u30bf\u30a4\u30d7\u3092\u6307\u5b9a\u3057\u307e\u3059\u3002" - }, "user": { "data": { "authentication": "\u8a8d\u8a3c", @@ -58,25 +44,15 @@ "malformed_url": "\u4e0d\u6b63\u306a\u5f62\u5f0f\u306eURL", "no_still_image_or_stream_url": "\u9759\u6b62\u753b\u50cf\u3082\u3057\u304f\u306f\u3001\u30b9\u30c8\u30ea\u30fc\u30e0URL\u306e\u3069\u3061\u3089\u304b\u3092\u6307\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", "relative_url": "\u76f8\u5bfeURL(Relative URLs)\u306f\u8a31\u53ef\u3055\u308c\u3066\u3044\u307e\u305b\u3093", - "stream_file_not_found": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u3068\u304d\u306b\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093(ffmpeg\u304c\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3055\u308c\u3066\u3044\u307e\u3059\u304b\uff1f)", - "stream_http_not_found": "\u30b9\u30c8\u30ea\u30fc\u30e0\u3078\u306e\u63a5\u7d9a\u6642\u306b\u3001HTTP 404\u3067\u898b\u3064\u304b\u308a\u307e\u305b\u3093", "stream_io_error": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u305f\u3068\u304d\u306b\u5165\u51fa\u529b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002RTSP\u30c8\u30e9\u30f3\u30b9\u30dd\u30fc\u30c8\u30d7\u30ed\u30c8\u30b3\u30eb\u3092\u9593\u9055\u3048\u305f\uff1f", "stream_no_route_to_host": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u307e\u3057\u305f\u304c\u3001\u30db\u30b9\u30c8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f", - "stream_no_video": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u52d5\u753b\u304c\u3042\u308a\u307e\u305b\u3093", "stream_not_permitted": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u9593\u3001\u64cd\u4f5c\u3067\u304d\u307e\u305b\u3093\u3002RTSP\u30c8\u30e9\u30f3\u30b9\u30dd\u30fc\u30c8\u30d7\u30ed\u30c8\u30b3\u30eb\u3092\u9593\u9055\u3048\u305f\uff1f", - "stream_unauthorised": "\u30b9\u30c8\u30ea\u30fc\u30e0\u3078\u306e\u63a5\u7d9a\u6642\u306b\u3001\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "template_error": "\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u30ec\u30f3\u30c0\u30ea\u30f3\u30b0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "timeout": "URL\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8", "unable_still_load": "\u9759\u6b62\u753b\u306eURL\u304b\u3089\u6709\u52b9\u306a\u753b\u50cf\u3092\u8aad\u307f\u8fbc\u3080\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\uff08\u4f8b: \u7121\u52b9\u306a\u30db\u30b9\u30c8\u3001URL\u3001\u307e\u305f\u306f\u8a8d\u8a3c\u5931\u6557)\u3002\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { - "content_type": { - "data": { - "content_type": "\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u7a2e\u985e" - }, - "description": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u30bf\u30a4\u30d7\u3092\u6307\u5b9a\u3057\u307e\u3059\u3002" - }, "init": { "data": { "authentication": "\u8a8d\u8a3c", diff --git a/homeassistant/components/generic/translations/ko.json b/homeassistant/components/generic/translations/ko.json deleted file mode 100644 index 20ad990e862..00000000000 --- a/homeassistant/components/generic/translations/ko.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "config": { - "step": { - "confirm": { - "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/nl.json b/homeassistant/components/generic/translations/nl.json index b7727190810..d6d1d380990 100644 --- a/homeassistant/components/generic/translations/nl.json +++ b/homeassistant/components/generic/translations/nl.json @@ -1,35 +1,21 @@ { "config": { "abort": { - "no_devices_found": "Geen apparaten gevonden op het netwerk", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "already_exists": "Een camera met deze URL instellingen bestaat al.", "invalid_still_image": "URL heeft geen geldig stilstaand beeld geretourneerd", "no_still_image_or_stream_url": "U moet ten minste een stilstaand beeld of stream-URL specificeren", - "stream_file_not_found": "Bestand niet gevonden tijdens verbinding met stream (is ffmpeg ge\u00efnstalleerd?)", - "stream_http_not_found": "HTTP 404 Niet gevonden bij poging om verbinding te maken met stream", "stream_io_error": "Input/Output fout bij het proberen te verbinden met stream. Verkeerde RTSP transport protocol?", "stream_no_route_to_host": "Kan de host niet vinden terwijl u verbinding probeert te maken met de stream", - "stream_no_video": "Stream heeft geen video", "stream_not_permitted": "Operatie niet toegestaan bij poging om verbinding te maken met stream. Verkeerd RTSP transport protocol?", - "stream_unauthorised": "Autorisatie mislukt bij poging om verbinding te maken met stream", "template_error": "Fout bij het weergeven van sjabloon. Bekijk het logboek voor meer informatie.", "timeout": "Time-out tijdens het laden van URL", "unable_still_load": "Kan geen geldige afbeelding laden van stilstaande afbeelding URL (b.v. ongeldige host, URL of authenticatie fout). Bekijk het log voor meer informatie.", "unknown": "Onverwachte fout" }, "step": { - "confirm": { - "description": "Wil je beginnen met instellen?" - }, - "content_type": { - "data": { - "content_type": "Inhoudstype" - }, - "description": "Geef het inhoudstype voor de stream op." - }, "user": { "data": { "authentication": "Authenticatie", @@ -51,25 +37,15 @@ "already_exists": "Een camera met deze URL instellingen bestaat al.", "invalid_still_image": "URL heeft geen geldig stilstaand beeld geretourneerd", "no_still_image_or_stream_url": "U moet ten minste een stilstaand beeld of stream-URL specificeren", - "stream_file_not_found": "Bestand niet gevonden tijdens verbinding met stream (is ffmpeg ge\u00efnstalleerd?)", - "stream_http_not_found": "HTTP 404 Niet gevonden bij poging om verbinding te maken met stream", "stream_io_error": "Input/Output fout bij het proberen te verbinden met stream. Verkeerde RTSP transport protocol?", "stream_no_route_to_host": "Kan de host niet vinden terwijl u verbinding probeert te maken met de stream", - "stream_no_video": "Stream heeft geen video", "stream_not_permitted": "Operatie niet toegestaan bij poging om verbinding te maken met stream. Verkeerd RTSP transport protocol?", - "stream_unauthorised": "Autorisatie mislukt bij poging om verbinding te maken met stream", "template_error": "Fout bij het weergeven van sjabloon. Bekijk het logboek voor meer informatie.", "timeout": "Time-out tijdens het laden van URL", "unable_still_load": "Kan geen geldige afbeelding laden van stilstaande afbeelding URL (b.v. ongeldige host, URL of authenticatie fout). Bekijk het log voor meer informatie.", "unknown": "Onverwachte fout" }, "step": { - "content_type": { - "data": { - "content_type": "Inhoudstype" - }, - "description": "Geef het inhoudstype voor de stream op." - }, "init": { "data": { "authentication": "Authenticatie", diff --git a/homeassistant/components/generic/translations/no.json b/homeassistant/components/generic/translations/no.json index a4c9c27bf69..0960b781ea9 100644 --- a/homeassistant/components/generic/translations/no.json +++ b/homeassistant/components/generic/translations/no.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket", "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "Feil utforming p\u00e5 URL", "no_still_image_or_stream_url": "Du m\u00e5 angi minst en URL-adresse for stillbilde eller dataflyt", "relative_url": "Relative URL-adresser ikke tillatt", - "stream_file_not_found": "Filen ble ikke funnet under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8m (er ffmpeg installert?)", - "stream_http_not_found": "HTTP 404 Ikke funnet under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8m", "stream_io_error": "Inn-/utdatafeil under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8m. Feil RTSP-transportprotokoll?", "stream_no_route_to_host": "Kunne ikke finne verten under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8mmen", - "stream_no_video": "Stream har ingen video", "stream_not_permitted": "Operasjon er ikke tillatt mens du pr\u00f8ver \u00e5 koble til str\u00f8m. Feil RTSP-transportprotokoll?", - "stream_unauthorised": "Autorisasjonen mislyktes under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8mmen", "template_error": "Feil ved gjengivelse av mal. Se gjennom loggen for mer informasjon.", "timeout": "Tidsavbrudd under innlasting av URL", "unable_still_load": "Kan ikke laste inn gyldig bilde fra URL-adresse for stillbilde (f.eks. ugyldig verts-, URL- eller godkjenningsfeil). Se gjennom loggen hvis du vil ha mer informasjon.", "unknown": "Uventet feil" }, "step": { - "confirm": { - "description": "Vil du starte oppsettet?" - }, - "content_type": { - "data": { - "content_type": "Innholdstype" - }, - "description": "Angi innholdstypen for str\u00f8mmen." - }, "user": { "data": { "authentication": "Godkjenning", @@ -62,13 +48,9 @@ "malformed_url": "Feil utforming p\u00e5 URL", "no_still_image_or_stream_url": "Du m\u00e5 angi minst en URL-adresse for stillbilde eller dataflyt", "relative_url": "Relative URL-adresser ikke tillatt", - "stream_file_not_found": "Filen ble ikke funnet under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8m (er ffmpeg installert?)", - "stream_http_not_found": "HTTP 404 Ikke funnet under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8m", "stream_io_error": "Inn-/utdatafeil under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8m. Feil RTSP-transportprotokoll?", "stream_no_route_to_host": "Kunne ikke finne verten under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8mmen", - "stream_no_video": "Stream har ingen video", "stream_not_permitted": "Operasjon er ikke tillatt mens du pr\u00f8ver \u00e5 koble til str\u00f8m. Feil RTSP-transportprotokoll?", - "stream_unauthorised": "Autorisasjonen mislyktes under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8mmen", "template_error": "Feil ved gjengivelse av mal. Se gjennom loggen for mer informasjon.", "timeout": "Tidsavbrudd under innlasting av URL", "unable_still_load": "Kan ikke laste inn gyldig bilde fra URL-adresse for stillbilde (f.eks. ugyldig verts-, URL- eller godkjenningsfeil). Se gjennom loggen hvis du vil ha mer informasjon.", @@ -82,12 +64,6 @@ "description": "![Camera Still Image Preview]( {preview_url} )", "title": "Forh\u00e5ndsvisning" }, - "content_type": { - "data": { - "content_type": "Innholdstype" - }, - "description": "Angi innholdstypen for str\u00f8mmen." - }, "init": { "data": { "authentication": "Godkjenning", diff --git a/homeassistant/components/generic/translations/pl.json b/homeassistant/components/generic/translations/pl.json index e4ee551b524..f6a8704b9bc 100644 --- a/homeassistant/components/generic/translations/pl.json +++ b/homeassistant/components/generic/translations/pl.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci", "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "Nieprawid\u0142owy adres URL", "no_still_image_or_stream_url": "Musisz poda\u0107 przynajmniej nieruchomy obraz (still image) lub adres URL strumienia", "relative_url": "Wzgl\u0119dne adresy URL s\u0105 niedozwolone", - "stream_file_not_found": "Nie znaleziono pliku podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem (czy ffmpeg jest zainstalowany?)", - "stream_http_not_found": "\"HTTP 404 Nie znaleziono\" podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem", "stream_io_error": "B\u0142\u0105d wej\u015bcia/wyj\u015bcia podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem. Z\u0142y protok\u00f3\u0142 transportowy RTSP?", "stream_no_route_to_host": "Nie mo\u017cna znale\u017a\u0107 hosta podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem", - "stream_no_video": "Strumie\u0144 nie zawiera wideo", "stream_not_permitted": "Operacja nie jest dozwolona podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem. Z\u0142y protok\u00f3\u0142 transportowy RTSP?", - "stream_unauthorised": "Autoryzacja nie powiod\u0142a si\u0119 podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem", "template_error": "B\u0142\u0105d renderowania szablonu. Przejrzyj log, aby uzyska\u0107 wi\u0119cej informacji.", "timeout": "Przekroczono limit czasu podczas \u0142adowania adresu URL", "unable_still_load": "Nie mo\u017cna za\u0142adowa\u0107 prawid\u0142owego obrazu z adresu URL nieruchomego obrazu (np. nieprawid\u0142owy host, adres URL lub b\u0142\u0105d uwierzytelniania). Przejrzyj logi, aby uzyska\u0107 wi\u0119cej informacji.", "unknown": "Nieoczekiwany b\u0142\u0105d" }, "step": { - "confirm": { - "description": "Czy chcesz rozpocz\u0105\u0107 konfiguracj\u0119?" - }, - "content_type": { - "data": { - "content_type": "Typ zawarto\u015bci" - }, - "description": "Okre\u015bl typ zawarto\u015bci strumienia." - }, "user": { "data": { "authentication": "Uwierzytelnianie", @@ -62,13 +48,9 @@ "malformed_url": "Nieprawid\u0142owy adres URL", "no_still_image_or_stream_url": "Musisz poda\u0107 przynajmniej nieruchomy obraz (still image) lub adres URL strumienia", "relative_url": "Wzgl\u0119dne adresy URL s\u0105 niedozwolone", - "stream_file_not_found": "Nie znaleziono pliku podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem (czy ffmpeg jest zainstalowany?)", - "stream_http_not_found": "\"HTTP 404 Nie znaleziono\" podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem", "stream_io_error": "B\u0142\u0105d wej\u015bcia/wyj\u015bcia podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem. Z\u0142y protok\u00f3\u0142 transportowy RTSP?", "stream_no_route_to_host": "Nie mo\u017cna znale\u017a\u0107 hosta podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem", - "stream_no_video": "Strumie\u0144 nie zawiera wideo", "stream_not_permitted": "Operacja nie jest dozwolona podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem. Z\u0142y protok\u00f3\u0142 transportowy RTSP?", - "stream_unauthorised": "Autoryzacja nie powiod\u0142a si\u0119 podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem", "template_error": "B\u0142\u0105d renderowania szablonu. Przejrzyj log, aby uzyska\u0107 wi\u0119cej informacji.", "timeout": "Przekroczono limit czasu podczas \u0142adowania adresu URL", "unable_still_load": "Nie mo\u017cna za\u0142adowa\u0107 prawid\u0142owego obrazu z adresu URL nieruchomego obrazu (np. nieprawid\u0142owy host, adres URL lub b\u0142\u0105d uwierzytelniania). Przejrzyj logi, aby uzyska\u0107 wi\u0119cej informacji.", @@ -82,12 +64,6 @@ "description": "![Podgl\u0105d nieruchomego obrazu z kamery]({preview_url})", "title": "Podgl\u0105d" }, - "content_type": { - "data": { - "content_type": "Typ zawarto\u015bci" - }, - "description": "Okre\u015bl typ zawarto\u015bci strumienia." - }, "init": { "data": { "authentication": "Uwierzytelnianie", diff --git a/homeassistant/components/generic/translations/pt-BR.json b/homeassistant/components/generic/translations/pt-BR.json index e6d5260b49e..e5fb0fb7db7 100644 --- a/homeassistant/components/generic/translations/pt-BR.json +++ b/homeassistant/components/generic/translations/pt-BR.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "Nenhum dispositivo encontrado na rede", "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "URL malformada", "no_still_image_or_stream_url": "Voc\u00ea deve especificar pelo menos uma imagem est\u00e1tica ou uma URL de stream", "relative_url": "URLs relativas n\u00e3o s\u00e3o permitidas", - "stream_file_not_found": "Arquivo n\u00e3o encontrado ao tentar se conectar a stream (o ffmpeg est\u00e1 instalado?)", - "stream_http_not_found": "HTTP 404 n\u00e3o encontrado ao tentar se conectar a stream", "stream_io_error": "Erro de entrada/sa\u00edda ao tentar se conectar a stream. Protocolo RTSP errado?", "stream_no_route_to_host": "N\u00e3o foi poss\u00edvel encontrar o host ao tentar se conectar a stream", - "stream_no_video": "A stream n\u00e3o tem v\u00eddeo", "stream_not_permitted": "Opera\u00e7\u00e3o n\u00e3o permitida ao tentar se conectar a stream. Protocolo RTSP errado?", - "stream_unauthorised": "Falha na autoriza\u00e7\u00e3o ao tentar se conectar a stream", "template_error": "Erro ao renderizar o modelo. Revise o registro para obter mais informa\u00e7\u00f5es.", "timeout": "Tempo limite ao carregar a URL", "unable_still_load": "N\u00e3o foi poss\u00edvel carregar uma imagem v\u00e1lida do URL da imagem est\u00e1tica (por exemplo, host inv\u00e1lido, URL ou falha de autentica\u00e7\u00e3o). Revise o log para obter mais informa\u00e7\u00f5es.", "unknown": "Erro inesperado" }, "step": { - "confirm": { - "description": "Deseja iniciar a configura\u00e7\u00e3o?" - }, - "content_type": { - "data": { - "content_type": "Tipo de conte\u00fado" - }, - "description": "Especifique o tipo de conte\u00fado para o stream." - }, "user": { "data": { "authentication": "Autentica\u00e7\u00e3o", @@ -62,13 +48,9 @@ "malformed_url": "URL malformada", "no_still_image_or_stream_url": "Voc\u00ea deve especificar pelo menos uma imagem est\u00e1tica ou uma URL de stream", "relative_url": "URLs relativas n\u00e3o s\u00e3o permitidas", - "stream_file_not_found": "Arquivo n\u00e3o encontrado ao tentar se conectar a stream (o ffmpeg est\u00e1 instalado?)", - "stream_http_not_found": "HTTP 404 n\u00e3o encontrado ao tentar se conectar a stream", "stream_io_error": "Erro de entrada/sa\u00edda ao tentar se conectar a stream. Protocolo RTSP errado?", "stream_no_route_to_host": "N\u00e3o foi poss\u00edvel encontrar o host ao tentar se conectar a stream", - "stream_no_video": "A stream n\u00e3o tem v\u00eddeo", "stream_not_permitted": "Opera\u00e7\u00e3o n\u00e3o permitida ao tentar se conectar a stream. Protocolo RTSP errado?", - "stream_unauthorised": "Falha na autoriza\u00e7\u00e3o ao tentar se conectar a stream", "template_error": "Erro ao renderizar o modelo. Revise o registro para obter mais informa\u00e7\u00f5es.", "timeout": "Tempo limite ao carregar a URL", "unable_still_load": "N\u00e3o foi poss\u00edvel carregar uma imagem v\u00e1lida do URL da imagem est\u00e1tica (por exemplo, host inv\u00e1lido, URL ou falha de autentica\u00e7\u00e3o). Revise o log para obter mais informa\u00e7\u00f5es.", @@ -82,12 +64,6 @@ "description": "![Visualiza\u00e7\u00e3o da imagem est\u00e1tica da c\u00e2mera]({preview_url})", "title": "Visualizar" }, - "content_type": { - "data": { - "content_type": "Tipo de conte\u00fado" - }, - "description": "Especifique o tipo de conte\u00fado para o stream." - }, "init": { "data": { "authentication": "Autentica\u00e7\u00e3o", diff --git a/homeassistant/components/generic/translations/pt.json b/homeassistant/components/generic/translations/pt.json index 06abd0a7dfa..d2f1d6aa0f9 100644 --- a/homeassistant/components/generic/translations/pt.json +++ b/homeassistant/components/generic/translations/pt.json @@ -2,11 +2,6 @@ "config": { "error": { "unknown": "Erro inesperado" - }, - "step": { - "confirm": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" - } } }, "options": { diff --git a/homeassistant/components/generic/translations/ru.json b/homeassistant/components/generic/translations/ru.json index b40123785dd..c0e6ec31bfb 100644 --- a/homeassistant/components/generic/translations/ru.json +++ b/homeassistant/components/generic/translations/ru.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "\u041d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0439 URL-\u0430\u0434\u0440\u0435\u0441.", "no_still_image_or_stream_url": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c URL-\u0430\u0434\u0440\u0435\u0441 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043f\u043e\u0442\u043e\u043a\u0430.", "relative_url": "\u041e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u043d\u0435 \u0434\u043e\u043f\u0443\u0441\u043a\u0430\u044e\u0442\u0441\u044f.", - "stream_file_not_found": "\u0424\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u043b\u0438 ffmpeg?", - "stream_http_not_found": "HTTP 404 \u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443.", "stream_io_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0432\u043e\u0434\u0430/\u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443. \u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b RTSP?", "stream_no_route_to_host": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0445\u043e\u0441\u0442 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443.", - "stream_no_video": "\u0412 \u043f\u043e\u0442\u043e\u043a\u0435 \u043d\u0435\u0442 \u0432\u0438\u0434\u0435\u043e.", "stream_not_permitted": "\u041e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0430 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443. \u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b RTSP?", - "stream_unauthorised": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443.", "template_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u0430. \u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0436\u0443\u0440\u043d\u0430\u043b \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", "timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 URL-\u0430\u0434\u0440\u0435\u0441\u0430.", "unable_still_load": "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f (\u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0445\u043e\u0441\u0442, URL-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438). \u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0436\u0443\u0440\u043d\u0430\u043b \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { - "confirm": { - "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443?" - }, - "content_type": { - "data": { - "content_type": "\u0422\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e" - }, - "description": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0442\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u0434\u043b\u044f \u043f\u043e\u0442\u043e\u043a\u0430." - }, "user": { "data": { "authentication": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", @@ -62,13 +48,9 @@ "malformed_url": "\u041d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0439 URL-\u0430\u0434\u0440\u0435\u0441.", "no_still_image_or_stream_url": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c URL-\u0430\u0434\u0440\u0435\u0441 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043f\u043e\u0442\u043e\u043a\u0430.", "relative_url": "\u041e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u043d\u0435 \u0434\u043e\u043f\u0443\u0441\u043a\u0430\u044e\u0442\u0441\u044f.", - "stream_file_not_found": "\u0424\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u043b\u0438 ffmpeg?", - "stream_http_not_found": "HTTP 404 \u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443.", "stream_io_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0432\u043e\u0434\u0430/\u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443. \u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b RTSP?", "stream_no_route_to_host": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0445\u043e\u0441\u0442 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443.", - "stream_no_video": "\u0412 \u043f\u043e\u0442\u043e\u043a\u0435 \u043d\u0435\u0442 \u0432\u0438\u0434\u0435\u043e.", "stream_not_permitted": "\u041e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0430 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443. \u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b RTSP?", - "stream_unauthorised": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443.", "template_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u0430. \u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0436\u0443\u0440\u043d\u0430\u043b \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", "timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 URL-\u0430\u0434\u0440\u0435\u0441\u0430.", "unable_still_load": "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f (\u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0445\u043e\u0441\u0442, URL-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438). \u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0436\u0443\u0440\u043d\u0430\u043b \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", @@ -82,12 +64,6 @@ "description": "![\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441 \u043a\u0430\u043c\u0435\u0440\u044b]({preview_url})", "title": "\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440" }, - "content_type": { - "data": { - "content_type": "\u0422\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e" - }, - "description": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0442\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u0434\u043b\u044f \u043f\u043e\u0442\u043e\u043a\u0430." - }, "init": { "data": { "authentication": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", diff --git a/homeassistant/components/generic/translations/sk.json b/homeassistant/components/generic/translations/sk.json index dcb28c2222e..9ff8487afaa 100644 --- a/homeassistant/components/generic/translations/sk.json +++ b/homeassistant/components/generic/translations/sk.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" - }, "step": { "user": { "data": { diff --git a/homeassistant/components/generic/translations/sv.json b/homeassistant/components/generic/translations/sv.json index 4db8e007a1d..a794a478e76 100644 --- a/homeassistant/components/generic/translations/sv.json +++ b/homeassistant/components/generic/translations/sv.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "Inga enheter hittades i n\u00e4tverket", "single_instance_allowed": "Redan konfigurerad. Endast en konfiguration m\u00f6jlig." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "Ogiltig URL", "no_still_image_or_stream_url": "Du m\u00e5ste ange \u00e5tminstone en stillbilds- eller stream-URL", "relative_url": "Relativa URL:er \u00e4r inte till\u00e5tna", - "stream_file_not_found": "Filen hittades inte n\u00e4r du f\u00f6rs\u00f6kte ansluta till str\u00f6m (\u00e4r ffmpeg installerat?)", - "stream_http_not_found": "HTTP 404 Ej funnen n\u00e4r du f\u00f6rs\u00f6ker ansluta till str\u00f6mmen", "stream_io_error": "Inmatnings-/utg\u00e5ngsfel vid f\u00f6rs\u00f6k att ansluta till stream. Fel RTSP-transportprotokoll?", "stream_no_route_to_host": "Kunde inte hitta v\u00e4rddatorn n\u00e4r jag f\u00f6rs\u00f6kte ansluta till str\u00f6mmen", - "stream_no_video": "Str\u00f6mmen har ingen video", "stream_not_permitted": "\u00c5tg\u00e4rden \u00e4r inte till\u00e5ten n\u00e4r du f\u00f6rs\u00f6ker ansluta till streamen. Fel RTSP-transportprotokoll?", - "stream_unauthorised": "Auktoriseringen misslyckades n\u00e4r du f\u00f6rs\u00f6kte ansluta till str\u00f6mmen", "template_error": "Problem att rendera mall. Kolla i loggen f\u00f6r mer information.", "timeout": "Timeout vid h\u00e4mtning fr\u00e5n URL", "unable_still_load": "Det g\u00e5r inte att ladda giltig bild fr\u00e5n stillbilds-URL (t.ex. ogiltig v\u00e4rd, URL eller autentiseringsfel). Granska loggen f\u00f6r mer information.", "unknown": "Ov\u00e4ntat fel" }, "step": { - "confirm": { - "description": "Vill du starta konfigurationen?" - }, - "content_type": { - "data": { - "content_type": "Inneh\u00e5llstyp" - }, - "description": "Ange inneh\u00e5llstypen f\u00f6r str\u00f6mmen." - }, "user": { "data": { "authentication": "Autentiseringen", @@ -62,25 +48,15 @@ "malformed_url": "Ogiltig URL", "no_still_image_or_stream_url": "Du m\u00e5ste ange \u00e5tminstone en stillbilds- eller stream-URL", "relative_url": "Relativa URL:er \u00e4r inte till\u00e5tet", - "stream_file_not_found": "Filen hittades inte n\u00e4r du f\u00f6rs\u00f6kte ansluta till str\u00f6m (\u00e4r ffmpeg installerat?)", - "stream_http_not_found": "HTTP 404 Ej funnen n\u00e4r du f\u00f6rs\u00f6ker ansluta till str\u00f6mmen", "stream_io_error": "Inmatnings-/utg\u00e5ngsfel vid f\u00f6rs\u00f6k att ansluta till stream. Fel RTSP-transportprotokoll?", "stream_no_route_to_host": "Kunde inte hitta v\u00e4rddatorn n\u00e4r jag f\u00f6rs\u00f6kte ansluta till str\u00f6mmen", - "stream_no_video": "Str\u00f6mmen har ingen video", "stream_not_permitted": "\u00c5tg\u00e4rden \u00e4r inte till\u00e5ten n\u00e4r du f\u00f6rs\u00f6ker ansluta till streamen. Fel RTSP-transportprotokoll?", - "stream_unauthorised": "Auktoriseringen misslyckades n\u00e4r du f\u00f6rs\u00f6kte ansluta till str\u00f6mmen", "template_error": "Problem att rendera mall. Kolla i loggen f\u00f6r mer information.", "timeout": "Timeout vid h\u00e4mtning fr\u00e5n URL", "unable_still_load": "Det g\u00e5r inte att ladda giltig bild fr\u00e5n stillbilds-URL (t.ex. ogiltig v\u00e4rd, URL eller autentiseringsfel). Granska loggen f\u00f6r mer information.", "unknown": "Ov\u00e4ntat fel" }, "step": { - "content_type": { - "data": { - "content_type": "Inneh\u00e5llstyp" - }, - "description": "Ange tyen av inneh\u00e5ll f\u00f6r str\u00f6mmen" - }, "init": { "data": { "authentication": "Autentiseringen", diff --git a/homeassistant/components/generic/translations/tr.json b/homeassistant/components/generic/translations/tr.json index c3561d18e2a..c6cc30bb377 100644 --- a/homeassistant/components/generic/translations/tr.json +++ b/homeassistant/components/generic/translations/tr.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "A\u011fda cihaz bulunamad\u0131", "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "Hatal\u0131 bi\u00e7imlendirilmi\u015f URL", "no_still_image_or_stream_url": "En az\u0131ndan bir dura\u011fan resim veya ak\u0131\u015f URL'si belirtmelisiniz", "relative_url": "G\u00f6receli URL'lere izin verilmez", - "stream_file_not_found": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken dosya bulunamad\u0131 (ffmpeg y\u00fckl\u00fc m\u00fc?)", - "stream_http_not_found": "HTTP 404 Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken bulunamad\u0131", "stream_io_error": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken Giri\u015f/\u00c7\u0131k\u0131\u015f hatas\u0131. Yanl\u0131\u015f RTSP aktar\u0131m protokol\u00fc?", "stream_no_route_to_host": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken ana bilgisayar bulunamad\u0131", - "stream_no_video": "Ak\u0131\u015fta video yok", "stream_not_permitted": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken i\u015fleme izin verilmiyor. Yanl\u0131\u015f RTSP aktar\u0131m protokol\u00fc?", - "stream_unauthorised": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken yetkilendirme ba\u015far\u0131s\u0131z oldu", "template_error": "\u015eablon olu\u015fturma hatas\u0131. Daha fazla bilgi i\u00e7in g\u00fcnl\u00fc\u011f\u00fc inceleyin.", "timeout": "URL y\u00fcklenirken zaman a\u015f\u0131m\u0131", "unable_still_load": "Hareketsiz resim URL'sinden ge\u00e7erli resim y\u00fcklenemiyor (\u00f6r. ge\u00e7ersiz ana bilgisayar, URL veya kimlik do\u011frulama hatas\u0131). Daha fazla bilgi i\u00e7in g\u00fcnl\u00fc\u011f\u00fc inceleyin.", "unknown": "Beklenmeyen hata" }, "step": { - "confirm": { - "description": "Kuruluma ba\u015flamak ister misiniz?" - }, - "content_type": { - "data": { - "content_type": "\u0130\u00e7erik T\u00fcr\u00fc" - }, - "description": "Ak\u0131\u015f i\u00e7in i\u00e7erik t\u00fcr\u00fcn\u00fc belirtin." - }, "user": { "data": { "authentication": "Kimlik Do\u011frulama", @@ -62,13 +48,9 @@ "malformed_url": "Hatal\u0131 bi\u00e7imlendirilmi\u015f URL", "no_still_image_or_stream_url": "En az\u0131ndan bir dura\u011fan resim veya ak\u0131\u015f URL'si belirtmelisiniz", "relative_url": "G\u00f6receli URL'lere izin verilmez", - "stream_file_not_found": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken dosya bulunamad\u0131 (ffmpeg y\u00fckl\u00fc m\u00fc?)", - "stream_http_not_found": "HTTP 404 Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken bulunamad\u0131", "stream_io_error": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken Giri\u015f/\u00c7\u0131k\u0131\u015f hatas\u0131. Yanl\u0131\u015f RTSP aktar\u0131m protokol\u00fc?", "stream_no_route_to_host": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken ana bilgisayar bulunamad\u0131", - "stream_no_video": "Ak\u0131\u015fta video yok", "stream_not_permitted": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken i\u015fleme izin verilmiyor. Yanl\u0131\u015f RTSP aktar\u0131m protokol\u00fc?", - "stream_unauthorised": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken yetkilendirme ba\u015far\u0131s\u0131z oldu", "template_error": "\u015eablon olu\u015fturma hatas\u0131. Daha fazla bilgi i\u00e7in g\u00fcnl\u00fc\u011f\u00fc inceleyin.", "timeout": "URL y\u00fcklenirken zaman a\u015f\u0131m\u0131", "unable_still_load": "Hareketsiz resim URL'sinden ge\u00e7erli resim y\u00fcklenemiyor (\u00f6r. ge\u00e7ersiz ana bilgisayar, URL veya kimlik do\u011frulama hatas\u0131). Daha fazla bilgi i\u00e7in g\u00fcnl\u00fc\u011f\u00fc inceleyin.", @@ -82,12 +64,6 @@ "description": "![Kamera Dura\u011fan G\u00f6r\u00fcnt\u00fc \u00d6nizlemesi]( {preview_url} )", "title": "\u00d6nizleme" }, - "content_type": { - "data": { - "content_type": "\u0130\u00e7erik T\u00fcr\u00fc" - }, - "description": "Ak\u0131\u015f i\u00e7in i\u00e7erik t\u00fcr\u00fcn\u00fc belirtin." - }, "init": { "data": { "authentication": "Kimlik Do\u011frulama", diff --git a/homeassistant/components/generic/translations/zh-Hant.json b/homeassistant/components/generic/translations/zh-Hant.json index 7ce9f004c37..34a8d5c6f78 100644 --- a/homeassistant/components/generic/translations/zh-Hant.json +++ b/homeassistant/components/generic/translations/zh-Hant.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e", "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" }, "error": { @@ -10,28 +9,15 @@ "malformed_url": "URL \u683c\u5f0f\u932f\u8aa4", "no_still_image_or_stream_url": "\u5fc5\u9808\u81f3\u5c11\u6307\u5b9a\u975c\u614b\u5f71\u50cf\u6216\u4e32\u6d41 URL", "relative_url": "\u4e0d\u5141\u8a31\u4f7f\u7528\u76f8\u5c0d\u61c9 URL", - "stream_file_not_found": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u51fa\u73fe\u627e\u4e0d\u5230\u6a94\u6848\u932f\u8aa4\uff08\u662f\u5426\u5df2\u5b89\u88dd ffmpeg\uff1f\uff09", - "stream_http_not_found": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u51fa\u73fe HTTP 404 \u672a\u627e\u5230\u932f\u8aa4", "stream_io_error": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u51fa\u73fe\u8f38\u5165/\u8f38\u51fa\u932f\u8aa4\u3002\u8f38\u5165\u932f\u8aa4\u7684 RTSP \u50b3\u8f38\u5354\u5b9a\uff1f", "stream_no_route_to_host": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u627e\u4e0d\u5230\u4e3b\u6a5f", - "stream_no_video": "\u4e32\u6d41\u6c92\u6709\u5f71\u50cf", "stream_not_permitted": "\u5617\u8a66\u4e32\u6d41\u9023\u7dda\u6642\u4e0d\u5141\u8a31\u64cd\u4f5c\u3002\u8f38\u5165\u932f\u8aa4\u7684 RTSP \u50b3\u8f38\u5354\u5b9a\uff1f", - "stream_unauthorised": "\u5617\u8a66\u4e32\u6d41\u9023\u7dda\u6642\u8a8d\u8b49\u5931\u6557", "template_error": "\u6a21\u7248\u6e32\u67d3\u932f\u8aa4\u3001\u8acb\u53c3\u95b1\u65e5\u8a8c\u4ee5\u7372\u5f97\u66f4\u8a73\u7d30\u8cc7\u6599\u3002", "timeout": "\u8f09\u5165 URL \u903e\u6642\u6642\u9593", "unable_still_load": "\u7121\u6cd5\u7531\u8a2d\u5b9a\u975c\u614b\u5f71\u50cf URL \u8f09\u5165\u6709\u6548\u5f71\u50cf\uff08\u4f8b\u5982\uff1a\u7121\u6548\u4e3b\u6a5f\u3001URL \u6216\u8a8d\u8b49\u5931\u6557\uff09\u3002\u8acb\u53c3\u95b1\u65e5\u8a8c\u4ee5\u7372\u5f97\u66f4\u8a73\u7d30\u8a0a\u606f\u3002", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { - "confirm": { - "description": "\u662f\u5426\u8981\u958b\u59cb\u8a2d\u5b9a\uff1f" - }, - "content_type": { - "data": { - "content_type": "\u5167\u5bb9\u985e\u578b" - }, - "description": "\u6307\u5b9a\u4e32\u6d41\u5167\u5bb9\u985e\u5225" - }, "user": { "data": { "authentication": "\u9a57\u8b49", @@ -62,13 +48,9 @@ "malformed_url": "URL \u683c\u5f0f\u932f\u8aa4", "no_still_image_or_stream_url": "\u5fc5\u9808\u81f3\u5c11\u6307\u5b9a\u975c\u614b\u5f71\u50cf\u6216\u4e32\u6d41 URL", "relative_url": "\u4e0d\u5141\u8a31\u4f7f\u7528\u76f8\u5c0d\u61c9 URL", - "stream_file_not_found": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u51fa\u73fe\u627e\u4e0d\u5230\u6a94\u6848\u932f\u8aa4\uff08\u662f\u5426\u5df2\u5b89\u88dd ffmpeg\uff1f\uff09", - "stream_http_not_found": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u51fa\u73fe HTTP 404 \u672a\u627e\u5230\u932f\u8aa4", "stream_io_error": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u51fa\u73fe\u8f38\u5165/\u8f38\u51fa\u932f\u8aa4\u3002\u8f38\u5165\u932f\u8aa4\u7684 RTSP \u50b3\u8f38\u5354\u5b9a\uff1f", "stream_no_route_to_host": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u627e\u4e0d\u5230\u4e3b\u6a5f", - "stream_no_video": "\u4e32\u6d41\u6c92\u6709\u5f71\u50cf", "stream_not_permitted": "\u5617\u8a66\u4e32\u6d41\u9023\u7dda\u6642\u4e0d\u5141\u8a31\u64cd\u4f5c\u3002\u8f38\u5165\u932f\u8aa4\u7684 RTSP \u50b3\u8f38\u5354\u5b9a\uff1f", - "stream_unauthorised": "\u5617\u8a66\u4e32\u6d41\u9023\u7dda\u6642\u8a8d\u8b49\u5931\u6557", "template_error": "\u6a21\u7248\u6e32\u67d3\u932f\u8aa4\u3001\u8acb\u53c3\u95b1\u65e5\u8a8c\u4ee5\u7372\u5f97\u66f4\u8a73\u7d30\u8cc7\u6599\u3002", "timeout": "\u8f09\u5165 URL \u903e\u6642\u6642\u9593", "unable_still_load": "\u7121\u6cd5\u7531\u8a2d\u5b9a\u975c\u614b\u5f71\u50cf URL \u8f09\u5165\u6709\u6548\u5f71\u50cf\uff08\u4f8b\u5982\uff1a\u7121\u6548\u4e3b\u6a5f\u3001URL \u6216\u8a8d\u8b49\u5931\u6557\uff09\u3002\u8acb\u53c3\u95b1\u65e5\u8a8c\u4ee5\u7372\u5f97\u66f4\u8a73\u7d30\u8a0a\u606f\u3002", @@ -82,12 +64,6 @@ "description": "![\u651d\u5f71\u6a5f\u975c\u614b\u9810\u89bd]({preview_url})", "title": "\u9810\u89bd" }, - "content_type": { - "data": { - "content_type": "\u5167\u5bb9\u985e\u578b" - }, - "description": "\u6307\u5b9a\u4e32\u6d41\u5167\u5bb9\u985e\u5225" - }, "init": { "data": { "authentication": "\u9a57\u8b49", diff --git a/homeassistant/components/glances/translations/bg.json b/homeassistant/components/glances/translations/bg.json index ef60201a57f..86c979aed41 100644 --- a/homeassistant/components/glances/translations/bg.json +++ b/homeassistant/components/glances/translations/bg.json @@ -4,32 +4,19 @@ "already_configured": "\u0410\u0434\u0440\u0435\u0441\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d." }, "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u0430\u0434\u0440\u0435\u0441\u0430", - "wrong_version": "\u0412\u0435\u0440\u0441\u0438\u044f\u0442\u0430 \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430 (\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0438 \u0432\u0435\u0440\u0441\u0438\u0438: 2 \u0438\u043b\u0438 3)" + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u0430\u0434\u0440\u0435\u0441\u0430" }, "step": { "user": { "data": { "host": "\u0410\u0434\u0440\u0435\u0441", - "name": "\u0418\u043c\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "port": "\u041f\u043e\u0440\u0442", "ssl": "\u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0439\u0442\u0435 SSL/TLS, \u0437\u0430 \u0434\u0430 \u0441\u0435 \u0441\u0432\u044a\u0440\u0436\u0435\u0442\u0435 \u043a\u044a\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0442\u0430 Glances", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435", "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0442\u0430", "version": "Glances API \u0432\u0435\u0440\u0441\u0438\u044f (2 \u0438\u043b\u0438 3)" - }, - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043d\u0430 Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u0427\u0435\u0441\u0442\u043e\u0442\u0430 \u043d\u0430 \u0430\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u043d\u0435" - }, - "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043e\u043f\u0446\u0438\u0438 \u0437\u0430 Glances" + } } } } diff --git a/homeassistant/components/glances/translations/ca.json b/homeassistant/components/glances/translations/ca.json index 1ef17e201a4..b3d372a39bc 100644 --- a/homeassistant/components/glances/translations/ca.json +++ b/homeassistant/components/glances/translations/ca.json @@ -4,32 +4,19 @@ "already_configured": "El dispositiu ja est\u00e0 configurat" }, "error": { - "cannot_connect": "Ha fallat la connexi\u00f3", - "wrong_version": "Versi\u00f3 no compatible (2 o 3 necess\u00e0ria)" + "cannot_connect": "Ha fallat la connexi\u00f3" }, "step": { "user": { "data": { "host": "Amfitri\u00f3", - "name": "Nom", "password": "Contrasenya", "port": "Port", "ssl": "Utilitza un certificat SSL", "username": "Nom d'usuari", "verify_ssl": "Verifica el certificat SSL", "version": "Versi\u00f3 de l'API de Glances (2 o 3)" - }, - "title": "Configuraci\u00f3 de Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Freq\u00fc\u00e8ncia d'actualitzaci\u00f3" - }, - "description": "Opcions de configuraci\u00f3 de Glances" + } } } } diff --git a/homeassistant/components/glances/translations/cs.json b/homeassistant/components/glances/translations/cs.json index 198731bdb3e..a1faf48c3bc 100644 --- a/homeassistant/components/glances/translations/cs.json +++ b/homeassistant/components/glances/translations/cs.json @@ -4,32 +4,19 @@ "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno" }, "error": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "wrong_version": "Verze nen\u00ed podporov\u00e1na (pouze 2 nebo 3)" + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" }, "step": { "user": { "data": { "host": "Hostitel", - "name": "Jm\u00e9no", "password": "Heslo", "port": "Port", "ssl": "Pou\u017e\u00edv\u00e1 SSL certifik\u00e1t", "username": "U\u017eivatelsk\u00e9 jm\u00e9no", "verify_ssl": "Ov\u011b\u0159it certifik\u00e1t SSL", "version": "Verze API pro Glances (2 nebo 3)" - }, - "title": "Nastavte Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frekvence aktualizac\u00ed" - }, - "description": "Nastavte mo\u017enosti pro Glances" + } } } } diff --git a/homeassistant/components/glances/translations/da.json b/homeassistant/components/glances/translations/da.json index 995ae9d3bba..b95ec888446 100644 --- a/homeassistant/components/glances/translations/da.json +++ b/homeassistant/components/glances/translations/da.json @@ -4,32 +4,19 @@ "already_configured": "V\u00e6rten er allerede konfigureret." }, "error": { - "cannot_connect": "Kunne ikke oprette forbindelse til v\u00e6rt", - "wrong_version": "Version underst\u00f8ttes ikke (kun 2 eller 3)" + "cannot_connect": "Kunne ikke oprette forbindelse til v\u00e6rt" }, "step": { "user": { "data": { "host": "V\u00e6rt", - "name": "Navn", "password": "Adgangskode", "port": "Port", "ssl": "Brug SSL/TLS til at oprette forbindelse til Glances-systemet", "username": "Brugernavn", "verify_ssl": "Bekr\u00e6ft certificering af systemet", "version": "Glances API version (2 eller 3)" - }, - "title": "Ops\u00e6tning af Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Opdateringsfrekvens" - }, - "description": "Konfigurationsindstillinger for Glances" + } } } } diff --git a/homeassistant/components/glances/translations/de.json b/homeassistant/components/glances/translations/de.json index 8c91e4fb2e3..a642e25a80e 100644 --- a/homeassistant/components/glances/translations/de.json +++ b/homeassistant/components/glances/translations/de.json @@ -4,32 +4,19 @@ "already_configured": "Ger\u00e4t ist bereits konfiguriert" }, "error": { - "cannot_connect": "Verbindung fehlgeschlagen", - "wrong_version": "Version nicht unterst\u00fctzt (nur 2 oder 3)" + "cannot_connect": "Verbindung fehlgeschlagen" }, "step": { "user": { "data": { "host": "Host", - "name": "Name", "password": "Passwort", "port": "Port", "ssl": "Verwendet ein SSL-Zertifikat", "username": "Benutzername", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen", "version": "Glances API-Version (2 oder 3)" - }, - "title": "Glances einrichten" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Aktualisierungsfrequenz" - }, - "description": "Konfiguriere die Optionen f\u00fcr Glances" + } } } } diff --git a/homeassistant/components/glances/translations/el.json b/homeassistant/components/glances/translations/el.json index f0f927fcc71..c5ccbb28bb2 100644 --- a/homeassistant/components/glances/translations/el.json +++ b/homeassistant/components/glances/translations/el.json @@ -4,32 +4,19 @@ "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" }, "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "wrong_version": "\u0397 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 (\u03bc\u03cc\u03bd\u03bf 2 \u03ae 3)" + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "step": { "user": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "port": "\u0398\u03cd\u03c1\u03b1", "ssl": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03ad\u03bd\u03b1 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", "verify_ssl": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL", "version": "\u0388\u03ba\u03b4\u03bf\u03c3\u03b7 API Glances (2 \u03ae 3)" - }, - "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u03a3\u03c5\u03c7\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7\u03c2" - }, - "description": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd \u03b3\u03b9\u03b1 \u03c4\u03bf Glances" + } } } } diff --git a/homeassistant/components/glances/translations/en.json b/homeassistant/components/glances/translations/en.json index 87c53c3cf48..425fba95703 100644 --- a/homeassistant/components/glances/translations/en.json +++ b/homeassistant/components/glances/translations/en.json @@ -4,32 +4,19 @@ "already_configured": "Device is already configured" }, "error": { - "cannot_connect": "Failed to connect", - "wrong_version": "Version not supported (2 or 3 only)" + "cannot_connect": "Failed to connect" }, "step": { "user": { "data": { "host": "Host", - "name": "Name", "password": "Password", "port": "Port", "ssl": "Uses an SSL certificate", "username": "Username", "verify_ssl": "Verify SSL certificate", "version": "Glances API Version (2 or 3)" - }, - "title": "Setup Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Update frequency" - }, - "description": "Configure options for Glances" + } } } } diff --git a/homeassistant/components/glances/translations/es-419.json b/homeassistant/components/glances/translations/es-419.json index 5e060b20d47..3cee11a4d01 100644 --- a/homeassistant/components/glances/translations/es-419.json +++ b/homeassistant/components/glances/translations/es-419.json @@ -4,32 +4,19 @@ "already_configured": "El host ya est\u00e1 configurado." }, "error": { - "cannot_connect": "No se puede conectar al host", - "wrong_version": "Versi\u00f3n no compatible (2 o 3 solamente)" + "cannot_connect": "No se puede conectar al host" }, "step": { "user": { "data": { "host": "Host", - "name": "Nombre", "password": "Contrase\u00f1a", "port": "Puerto", "ssl": "Use SSL/TLS para conectarse al sistema Glances", "username": "Nombre de usuario", "verify_ssl": "Verificar la certificaci\u00f3n del sistema", "version": "Versi\u00f3n de API de Glances (2 o 3)" - }, - "title": "Configurar Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frecuencia de actualizaci\u00f3n" - }, - "description": "Configurar opciones para Glances" + } } } } diff --git a/homeassistant/components/glances/translations/es.json b/homeassistant/components/glances/translations/es.json index 22187e65793..e08c1162721 100644 --- a/homeassistant/components/glances/translations/es.json +++ b/homeassistant/components/glances/translations/es.json @@ -4,32 +4,19 @@ "already_configured": "El dispositivo ya est\u00e1 configurado" }, "error": { - "cannot_connect": "No se pudo conectar", - "wrong_version": "Versi\u00f3n no soportada (s\u00f3lo 2 o 3)" + "cannot_connect": "No se pudo conectar" }, "step": { "user": { "data": { "host": "Host", - "name": "Nombre", "password": "Contrase\u00f1a", "port": "Puerto", "ssl": "Utiliza un certificado SSL", "username": "Nombre de usuario", "verify_ssl": "Verificar el certificado SSL", "version": "Versi\u00f3n API de Glances (2 o 3)" - }, - "title": "Configurar Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frecuencia de actualizaci\u00f3n" - }, - "description": "Configurar opciones para Glances" + } } } } diff --git a/homeassistant/components/glances/translations/et.json b/homeassistant/components/glances/translations/et.json index 7f5e1bb63be..2135321945b 100644 --- a/homeassistant/components/glances/translations/et.json +++ b/homeassistant/components/glances/translations/et.json @@ -4,32 +4,19 @@ "already_configured": "Seade on juba h\u00e4\u00e4lestatud" }, "error": { - "cannot_connect": "\u00dchendamine nurjus", - "wrong_version": "Versiooni ei toetata (ainult 2 v\u00f5i 3)" + "cannot_connect": "\u00dchendamine nurjus" }, "step": { "user": { "data": { "host": "", - "name": "Nimi", "password": "Salas\u00f5na", "port": "Port", "ssl": "Kasutab SSL serti", "username": "Kasutajanimi", "verify_ssl": "Kontrolli SSL sertifikaati", "version": "Glances API versioon (2 v\u00f5i 3)" - }, - "title": "Seadista Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "V\u00e4rskendussagedus" - }, - "description": "Seadista Glances valikud" + } } } } diff --git a/homeassistant/components/glances/translations/fi.json b/homeassistant/components/glances/translations/fi.json index 70a013677c0..053107f5939 100644 --- a/homeassistant/components/glances/translations/fi.json +++ b/homeassistant/components/glances/translations/fi.json @@ -6,7 +6,6 @@ "step": { "user": { "data": { - "name": "Nimi", "password": "Salasana", "port": "portti" } diff --git a/homeassistant/components/glances/translations/fr.json b/homeassistant/components/glances/translations/fr.json index 6fafa8a3a51..05777784a4d 100644 --- a/homeassistant/components/glances/translations/fr.json +++ b/homeassistant/components/glances/translations/fr.json @@ -4,32 +4,19 @@ "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" }, "error": { - "cannot_connect": "\u00c9chec de connexion", - "wrong_version": "Version non prise en charge (2 ou 3 uniquement)" + "cannot_connect": "\u00c9chec de connexion" }, "step": { "user": { "data": { "host": "H\u00f4te", - "name": "Nom", "password": "Mot de passe", "port": "Port", "ssl": "Utilise un certificat SSL", "username": "Nom d'utilisateur", "verify_ssl": "V\u00e9rifier le certificat SSL", "version": "Glances API Version (2 ou 3)" - }, - "title": "Installation de Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Fr\u00e9quence de mise \u00e0 jour" - }, - "description": "Configurer les options pour Glances" + } } } } diff --git a/homeassistant/components/glances/translations/he.json b/homeassistant/components/glances/translations/he.json index f5ba6464a4e..e7c469e452a 100644 --- a/homeassistant/components/glances/translations/he.json +++ b/homeassistant/components/glances/translations/he.json @@ -10,7 +10,6 @@ "user": { "data": { "host": "\u05de\u05d0\u05e8\u05d7", - "name": "\u05e9\u05dd", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", "port": "\u05e4\u05ea\u05d7\u05d4", "ssl": "\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05d0\u05d9\u05e9\u05d5\u05e8 SSL", diff --git a/homeassistant/components/glances/translations/hu.json b/homeassistant/components/glances/translations/hu.json index 71649b51d34..22dfa8a46dd 100644 --- a/homeassistant/components/glances/translations/hu.json +++ b/homeassistant/components/glances/translations/hu.json @@ -4,32 +4,19 @@ "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" }, "error": { - "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "wrong_version": "Nem t\u00e1mogatott verzi\u00f3 (2 vagy 3 csak)" + "cannot_connect": "Sikertelen csatlakoz\u00e1s" }, "step": { "user": { "data": { "host": "C\u00edm", - "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "port": "Port", "ssl": "SSL tan\u00fas\u00edtv\u00e1ny haszn\u00e1lata", "username": "Felhaszn\u00e1l\u00f3n\u00e9v", "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se", "version": "Glances API-verzi\u00f3 (2 vagy 3)" - }, - "title": "Glances Be\u00e1ll\u00edt\u00e1sa" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Friss\u00edt\u00e9si gyakoris\u00e1g" - }, - "description": "A Glances be\u00e1ll\u00edt\u00e1sainak konfigur\u00e1l\u00e1sa" + } } } } diff --git a/homeassistant/components/glances/translations/id.json b/homeassistant/components/glances/translations/id.json index 13127e74322..ac06b9b1962 100644 --- a/homeassistant/components/glances/translations/id.json +++ b/homeassistant/components/glances/translations/id.json @@ -4,32 +4,19 @@ "already_configured": "Perangkat sudah dikonfigurasi" }, "error": { - "cannot_connect": "Gagal terhubung", - "wrong_version": "Versi tidak didukung (hanya versi 2 atau versi 3)" + "cannot_connect": "Gagal terhubung" }, "step": { "user": { "data": { "host": "Host", - "name": "Nama", "password": "Kata Sandi", "port": "Port", "ssl": "Menggunakan sertifikat SSL", "username": "Nama Pengguna", "verify_ssl": "Verifikasi sertifikat SSL", "version": "Versi API Glances (2 atau 3)" - }, - "title": "Siapkan Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frekuensi pembaruan" - }, - "description": "Konfigurasikan opsi untuk Glances" + } } } } diff --git a/homeassistant/components/glances/translations/it.json b/homeassistant/components/glances/translations/it.json index f7af778e17d..92791653d51 100644 --- a/homeassistant/components/glances/translations/it.json +++ b/homeassistant/components/glances/translations/it.json @@ -4,32 +4,19 @@ "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" }, "error": { - "cannot_connect": "Impossibile connettersi", - "wrong_version": "Versione non supportata (solo 2 o 3)" + "cannot_connect": "Impossibile connettersi" }, "step": { "user": { "data": { "host": "Host", - "name": "Nome", "password": "Password", "port": "Porta", "ssl": "Utilizza un certificato SSL", "username": "Nome utente", "verify_ssl": "Verifica il certificato SSL", "version": "Glances API Version (2 o 3)" - }, - "title": "Configura Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frequenza di aggiornamento" - }, - "description": "Configura le opzioni per Glances" + } } } } diff --git a/homeassistant/components/glances/translations/ja.json b/homeassistant/components/glances/translations/ja.json index 0267110e0d0..fec577c2435 100644 --- a/homeassistant/components/glances/translations/ja.json +++ b/homeassistant/components/glances/translations/ja.json @@ -4,32 +4,19 @@ "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" }, "error": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "wrong_version": "\u5bfe\u5fdc\u3057\u3066\u3044\u306a\u3044\u30d0\u30fc\u30b8\u30e7\u30f3(2\u307e\u305f\u306f3\u306e\u307f)" + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" }, "step": { "user": { "data": { "host": "\u30db\u30b9\u30c8", - "name": "\u540d\u524d", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "port": "\u30dd\u30fc\u30c8", "ssl": "SSL\u8a3c\u660e\u66f8\u3092\u4f7f\u7528\u3059\u308b", "username": "\u30e6\u30fc\u30b6\u30fc\u540d", "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b", "version": "Glances API\u30d0\u30fc\u30b8\u30e7\u30f3(2\u307e\u305f\u306f3)" - }, - "title": "Glances\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u66f4\u65b0\u983b\u5ea6" - }, - "description": "Glances\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a" + } } } } diff --git a/homeassistant/components/glances/translations/ko.json b/homeassistant/components/glances/translations/ko.json index e50206fade5..cbd2f4e2e3e 100644 --- a/homeassistant/components/glances/translations/ko.json +++ b/homeassistant/components/glances/translations/ko.json @@ -4,32 +4,19 @@ "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "error": { - "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", - "wrong_version": "\ud574\ub2f9 \ubc84\uc804\uc740 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4 (2 \ub610\ub294 3\ub9cc \uc9c0\uc6d0)" + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "step": { "user": { "data": { "host": "\ud638\uc2a4\ud2b8", - "name": "\uc774\ub984", "password": "\ube44\ubc00\ubc88\ud638", "port": "\ud3ec\ud2b8", "ssl": "SSL \uc778\uc99d\uc11c \uc0ac\uc6a9", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778", "version": "Glances API \ubc84\uc804 (2 \ub610\ub294 3)" - }, - "title": "Glances \uc124\uce58\ud558\uae30" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\uc5c5\ub370\uc774\ud2b8 \ube48\ub3c4" - }, - "description": "Glances\uc5d0 \ub300\ud55c \uc635\uc158 \uad6c\uc131\ud558\uae30" + } } } } diff --git a/homeassistant/components/glances/translations/lb.json b/homeassistant/components/glances/translations/lb.json index 68c6131cb5a..7fdd5b36583 100644 --- a/homeassistant/components/glances/translations/lb.json +++ b/homeassistant/components/glances/translations/lb.json @@ -4,32 +4,19 @@ "already_configured": "Apparat ass scho konfigur\u00e9iert" }, "error": { - "cannot_connect": "Feeler beim verbannen", - "wrong_version": "Versioun net \u00ebnnerst\u00ebtzt (n\u00ebmmen 2 oder 3)" + "cannot_connect": "Feeler beim verbannen" }, "step": { "user": { "data": { "host": "Apparat", - "name": "Numm", "password": "Passwuert", "port": "Port", "ssl": "Benotzt ee SSLZertifikat", "username": "Benotzernumm", "verify_ssl": "SSL Zertifikat iwwerpr\u00e9iwen", "version": "API Versioun vun den Usiichten (2 oder 3)" - }, - "title": "Usiichten konfigur\u00e9ieren" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Intervalle vun de Mise \u00e0 jour" - }, - "description": "Optioune konfigur\u00e9ieren fir d'Usiichten" + } } } } diff --git a/homeassistant/components/glances/translations/nl.json b/homeassistant/components/glances/translations/nl.json index ca414a92ab9..ee2c30382d3 100644 --- a/homeassistant/components/glances/translations/nl.json +++ b/homeassistant/components/glances/translations/nl.json @@ -4,32 +4,19 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kan geen verbinding maken", - "wrong_version": "Versie niet ondersteund (alleen 2 of 3)" + "cannot_connect": "Kan geen verbinding maken" }, "step": { "user": { "data": { "host": "Host", - "name": "Naam", "password": "Wachtwoord", "port": "Poort", "ssl": "Maakt gebruik van een SSL-certificaat", "username": "Gebruikersnaam", "verify_ssl": "SSL-certificaat verifi\u00ebren", "version": "Glances API-versie (2 of 3)" - }, - "title": "Glances instellen" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Update frequentie" - }, - "description": "Configureer opties voor Glances" + } } } } diff --git a/homeassistant/components/glances/translations/no.json b/homeassistant/components/glances/translations/no.json index 073d764f67a..0f3d263fc5a 100644 --- a/homeassistant/components/glances/translations/no.json +++ b/homeassistant/components/glances/translations/no.json @@ -4,32 +4,19 @@ "already_configured": "Enheten er allerede konfigurert" }, "error": { - "cannot_connect": "Tilkobling mislyktes", - "wrong_version": "Versjonen st\u00f8ttes ikke (bare 2 eller 3)" + "cannot_connect": "Tilkobling mislyktes" }, "step": { "user": { "data": { "host": "Vert", - "name": "Navn", "password": "Passord", "port": "Port", "ssl": "Bruker et SSL-sertifikat", "username": "Brukernavn", "verify_ssl": "Verifisere SSL-sertifikat", "version": "Glances API-versjon (2 eller 3)" - }, - "title": "Oppsett av Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Oppdater frekvens" - }, - "description": "Konfigurasjonsalternativer for Glances" + } } } } diff --git a/homeassistant/components/glances/translations/pl.json b/homeassistant/components/glances/translations/pl.json index abef0a78208..bf69dd9f134 100644 --- a/homeassistant/components/glances/translations/pl.json +++ b/homeassistant/components/glances/translations/pl.json @@ -4,32 +4,19 @@ "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" }, "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "wrong_version": "Wersja nieobs\u0142ugiwana (tylko 2 lub 3)" + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" }, "step": { "user": { "data": { "host": "Nazwa hosta lub adres IP", - "name": "Nazwa", "password": "Has\u0142o", "port": "Port", "ssl": "Certyfikat SSL", "username": "Nazwa u\u017cytkownika", "verify_ssl": "Weryfikacja certyfikatu SSL", "version": "Glances wersja API (2 lub 3)" - }, - "title": "Konfiguracja Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Cz\u0119stotliwo\u015b\u0107 aktualizacji" - }, - "description": "Konfiguracja opcji dla Glances" + } } } } diff --git a/homeassistant/components/glances/translations/pt-BR.json b/homeassistant/components/glances/translations/pt-BR.json index d081c897d38..2f1e45ec4dd 100644 --- a/homeassistant/components/glances/translations/pt-BR.json +++ b/homeassistant/components/glances/translations/pt-BR.json @@ -4,32 +4,19 @@ "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha ao conectar", - "wrong_version": "Vers\u00e3o n\u00e3o suportada (somente 2 ou 3)" + "cannot_connect": "Falha ao conectar" }, "step": { "user": { "data": { "host": "Nome do host", - "name": "Nome", "password": "Senha", "port": "Porta", "ssl": "Usar um certificado SSL", "username": "Usu\u00e1rio", "verify_ssl": "Verifique o certificado SSL", "version": "Vers\u00e3o da API Glances (2 ou 3)" - }, - "title": "Configura\u00e7\u00e3o Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frequ\u00eancia de atualiza\u00e7\u00e3o" - }, - "description": "Configure op\u00e7\u00f5es para Glances" + } } } } diff --git a/homeassistant/components/glances/translations/pt.json b/homeassistant/components/glances/translations/pt.json index 0d8cc552dd2..711e6452a8e 100644 --- a/homeassistant/components/glances/translations/pt.json +++ b/homeassistant/components/glances/translations/pt.json @@ -10,7 +10,6 @@ "user": { "data": { "host": "Servidor", - "name": "Nome", "password": "Palavra-passe", "port": "Porta", "ssl": "Utiliza um certificado SSL", @@ -19,14 +18,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frequ\u00eancia de atualiza\u00e7\u00e3o" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/glances/translations/ru.json b/homeassistant/components/glances/translations/ru.json index aecffe204c8..52a939a85c6 100644 --- a/homeassistant/components/glances/translations/ru.json +++ b/homeassistant/components/glances/translations/ru.json @@ -4,32 +4,19 @@ "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." }, "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "wrong_version": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0435\u0440\u0441\u0438\u0438 2 \u0438 3." + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." }, "step": { "user": { "data": { "host": "\u0425\u043e\u0441\u0442", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "port": "\u041f\u043e\u0440\u0442", "ssl": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL", "version": "\u0412\u0435\u0440\u0441\u0438\u044f API Glances (2 \u0438\u043b\u0438 3)" - }, - "title": "Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f" - }, - "description": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b Glances" + } } } } diff --git a/homeassistant/components/glances/translations/sk.json b/homeassistant/components/glances/translations/sk.json index fcb9ef6d04f..1742fc019cc 100644 --- a/homeassistant/components/glances/translations/sk.json +++ b/homeassistant/components/glances/translations/sk.json @@ -7,20 +7,10 @@ "user": { "data": { "host": "Hostite\u013e", - "name": "N\u00e1zov", "password": "Heslo", "port": "Port" } } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frekvencia aktualiz\u00e1cie" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/glances/translations/sl.json b/homeassistant/components/glances/translations/sl.json index 081b9ebfda1..29949946ad7 100644 --- a/homeassistant/components/glances/translations/sl.json +++ b/homeassistant/components/glances/translations/sl.json @@ -4,32 +4,19 @@ "already_configured": "Gostitelj je \u017ee konfiguriran." }, "error": { - "cannot_connect": "Ni mogo\u010de vzpostaviti povezave z gostiteljem", - "wrong_version": "Razli\u010dica ni podprta (samo 2 ali 3)" + "cannot_connect": "Ni mogo\u010de vzpostaviti povezave z gostiteljem" }, "step": { "user": { "data": { "host": "Host", - "name": "Ime", "password": "Geslo", "port": "Vrata", "ssl": "Za povezavo s sistemom Glances uporabite SSL/TLS", "username": "Uporabni\u0161ko ime", "verify_ssl": "Preverite veljavnost potrdila sistema", "version": "Glances API Version (2 ali 3)" - }, - "title": "Nastavite Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Pogostost posodabljanja" - }, - "description": "Konfiguracija mo\u017enosti za Glances" + } } } } diff --git a/homeassistant/components/glances/translations/sv.json b/homeassistant/components/glances/translations/sv.json index c4ead9e6aa6..ac9afb675b3 100644 --- a/homeassistant/components/glances/translations/sv.json +++ b/homeassistant/components/glances/translations/sv.json @@ -4,32 +4,19 @@ "already_configured": "V\u00e4rden \u00e4r redan konfigurerad." }, "error": { - "cannot_connect": "Det g\u00e5r inte att ansluta till v\u00e4rden", - "wrong_version": "Version st\u00f6ds inte (endast 2 eller 3)" + "cannot_connect": "Det g\u00e5r inte att ansluta till v\u00e4rden" }, "step": { "user": { "data": { "host": "V\u00e4rd", - "name": "Namn", "password": "L\u00f6senord", "port": "Port", "ssl": "Anv\u00e4nd SSL / TLS f\u00f6r att ansluta till Glances-systemet", "username": "Anv\u00e4ndarnamn", "verify_ssl": "Verifiera certifieringen av systemet", "version": "Glances API-version (2 eller 3)" - }, - "title": "St\u00e4ll in Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Uppdateringsfrekvens" - }, - "description": "Konfigurera alternativ f\u00f6r Glances" + } } } } diff --git a/homeassistant/components/glances/translations/tr.json b/homeassistant/components/glances/translations/tr.json index 50b2ef9cef1..11a08d65c2a 100644 --- a/homeassistant/components/glances/translations/tr.json +++ b/homeassistant/components/glances/translations/tr.json @@ -4,32 +4,19 @@ "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" }, "error": { - "cannot_connect": "Ba\u011flanma hatas\u0131", - "wrong_version": "S\u00fcr\u00fcm desteklenmiyor (yaln\u0131zca 2 veya 3)" + "cannot_connect": "Ba\u011flanma hatas\u0131" }, "step": { "user": { "data": { "host": "Sunucu", - "name": "Ad", "password": "Parola", "port": "Port", "ssl": "SSL sertifikas\u0131 kullan\u0131r", "username": "Kullan\u0131c\u0131 Ad\u0131", "verify_ssl": "SSL sertifikas\u0131n\u0131 do\u011frulay\u0131n", "version": "Glances API S\u00fcr\u00fcm\u00fc (2 veya 3)" - }, - "title": "Glances Kurulumu" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "G\u00fcncelleme s\u0131kl\u0131\u011f\u0131" - }, - "description": "Glances i\u00e7in se\u00e7enekleri yap\u0131land\u0131r\u0131n" + } } } } diff --git a/homeassistant/components/glances/translations/uk.json b/homeassistant/components/glances/translations/uk.json index 1fab197fe42..e1ff65cf4ab 100644 --- a/homeassistant/components/glances/translations/uk.json +++ b/homeassistant/components/glances/translations/uk.json @@ -4,32 +4,19 @@ "already_configured": "\u0426\u0435\u0439 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u043e \u0432 Home Assistant." }, "error": { - "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f", - "wrong_version": "\u041f\u0456\u0434\u0442\u0440\u0438\u043c\u0443\u044e\u0442\u044c\u0441\u044f \u0442\u0456\u043b\u044c\u043a\u0438 \u0432\u0435\u0440\u0441\u0456\u0457 2 \u0442\u0430 3." + "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f" }, "step": { "user": { "data": { "host": "\u0425\u043e\u0441\u0442", - "name": "\u041d\u0430\u0437\u0432\u0430", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "port": "\u041f\u043e\u0440\u0442", "ssl": "\u0412\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0454\u0442\u044c\u0441\u044f \u0441\u0435\u0440\u0442\u0438\u0444\u0456\u043a\u0430\u0442 SSL", "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430", "verify_ssl": "\u041f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0430 \u0441\u0435\u0440\u0442\u0438\u0444\u0456\u043a\u0430\u0442\u0430 SSL", "version": "\u0412\u0435\u0440\u0441\u0456\u044f API Glances (2 \u0430\u0431\u043e 3)" - }, - "title": "Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u043e\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f" - }, - "description": "\u0420\u043e\u0437\u0448\u0438\u0440\u0435\u043d\u0456 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438 Glances" + } } } } diff --git a/homeassistant/components/glances/translations/zh-Hans.json b/homeassistant/components/glances/translations/zh-Hans.json index a62b5f8b32e..55e14d47150 100644 --- a/homeassistant/components/glances/translations/zh-Hans.json +++ b/homeassistant/components/glances/translations/zh-Hans.json @@ -4,32 +4,19 @@ "already_configured": "\u8bbe\u5907\u5df2\u88ab\u8fde\u63a5" }, "error": { - "cannot_connect": "\u8fde\u63a5\u5931\u8d25", - "wrong_version": "\u4e0d\u652f\u6301\u7684\u7248\u672c (\u4ec5\u96502\u62163)" + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" }, "step": { "user": { "data": { "host": "\u4e3b\u673a\u5730\u5740", - "name": "\u540d\u79f0", "password": "\u5bc6\u7801", "port": "\u7aef\u53e3", "ssl": "\u4f7f\u7528 SSL \u51ed\u8bc1", "username": "\u7528\u6237\u540d", "verify_ssl": "\u9a8c\u8bc1 SSL \u8bc1\u4e66", "version": "Glances API \u7248\u672c (2 \u6216 3)" - }, - "title": "\u8bbe\u7f6e Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u66f4\u65b0\u9891\u7387" - }, - "description": "\u914d\u7f6e Glances \u9009\u9879" + } } } } diff --git a/homeassistant/components/glances/translations/zh-Hant.json b/homeassistant/components/glances/translations/zh-Hant.json index 3b0ddcd947a..6ea5775f099 100644 --- a/homeassistant/components/glances/translations/zh-Hant.json +++ b/homeassistant/components/glances/translations/zh-Hant.json @@ -4,32 +4,19 @@ "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" }, "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557", - "wrong_version": "\u7248\u672c\u4e0d\u652f\u63f4\uff08\u50c5 2 \u6216 3\uff09" + "cannot_connect": "\u9023\u7dda\u5931\u6557" }, "step": { "user": { "data": { "host": "\u4e3b\u6a5f\u7aef", - "name": "\u540d\u7a31", "password": "\u5bc6\u78bc", "port": "\u901a\u8a0a\u57e0", "ssl": "\u4f7f\u7528 SSL \u8a8d\u8b49", "username": "\u4f7f\u7528\u8005\u540d\u7a31", "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49", "version": "Glances API \u7248\u672c\uff082 \u6216 3\uff09" - }, - "title": "\u8a2d\u5b9a Glances" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u66f4\u65b0\u983b\u7387" - }, - "description": "Glances \u8a2d\u5b9a\u9078\u9805" + } } } } diff --git a/homeassistant/components/google/translations/bg.json b/homeassistant/components/google/translations/bg.json index 2cf37955e1e..7d8e0d3083e 100644 --- a/homeassistant/components/google/translations/bg.json +++ b/homeassistant/components/google/translations/bg.json @@ -18,11 +18,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Google Calendar \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/ca.json b/homeassistant/components/google/translations/ca.json index b7645abe54b..066630df50d 100644 --- a/homeassistant/components/google/translations/ca.json +++ b/homeassistant/components/google/translations/ca.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "La configuraci\u00f3 de Google Calentdar mitjan\u00e7ant YAML s'eliminar\u00e0 de Home Assistant a la versi\u00f3 2022.9. \n\nLa configuraci\u00f3 existent de credencials d'aplicaci\u00f3 OAuth i d'acc\u00e9s s'ha importat autom\u00e0ticament a la interf\u00edcie d'usuari. Elimina la configuraci\u00f3 YAML corresponent del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", - "title": "La configuraci\u00f3 YAML de Google Calendar est\u00e0 sent eliminada" - }, - "removed_track_new_yaml": { - "description": "Has desactivat el seguiment d'entitats de Google Calendar a configuration.yaml, que ja no \u00e9s compatible. Per desactivar les entitats descobertes recentment, a partir d'ara, has de canviar manualment les opcions de sistema de la integraci\u00f3 a trav\u00e9s de la interf\u00edcie d'usuari. Elimina la configuraci\u00f3 'track_new' de configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", - "title": "El seguiment d'entitats de Google Calendar ha canviat" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/de.json b/homeassistant/components/google/translations/de.json index 377cafe035e..6569dbce676 100644 --- a/homeassistant/components/google/translations/de.json +++ b/homeassistant/components/google/translations/de.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "Die Konfiguration des Google Kalenders in configuration.yaml wird in Home Assistant 2022.9 entfernt. \n\nDeine bestehenden OAuth-Anwendungsdaten und Zugriffseinstellungen wurden automatisch in die Benutzeroberfl\u00e4che importiert. Entferne die YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Google Calendar YAML-Konfiguration wird entfernt" - }, - "removed_track_new_yaml": { - "description": "Du hast die Entit\u00e4tsverfolgung f\u00fcr Google Kalender in configuration.yaml deaktiviert, was nicht mehr unterst\u00fctzt wird. Du musst die Integrationssystemoptionen in der Benutzeroberfl\u00e4che manuell \u00e4ndern, um neu entdeckte Entit\u00e4ten in Zukunft zu deaktivieren. Entferne die Einstellung track_new aus configuration.yaml und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Google Calendar Entity Tracking hat sich ge\u00e4ndert" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/el.json b/homeassistant/components/google/translations/el.json index 0bf592d60d4..21e5580da3d 100644 --- a/homeassistant/components/google/translations/el.json +++ b/homeassistant/components/google/translations/el.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u0397\u03bc\u03b5\u03c1\u03bf\u03bb\u03bf\u03b3\u03af\u03bf\u03c5 Google \u03c3\u03c4\u03bf configuration.yaml \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf Home Assistant 2022.9. \n\n \u03a4\u03b1 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03bd\u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 OAuth \u03ba\u03b1\u03b9 \u03bf\u03b9 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03ad\u03c7\u03bf\u03c5\u03bd \u03b5\u03b9\u03c3\u03b1\u03c7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7. \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", - "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03c4\u03bf\u03c5 \u0397\u03bc\u03b5\u03c1\u03bf\u03bb\u03bf\u03b3\u03af\u03bf\u03c5 Google \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" - }, - "removed_track_new_yaml": { - "description": "\u0388\u03c7\u03b5\u03c4\u03b5 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03b9 \u03c4\u03b7\u03bd \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7 \u03bf\u03bd\u03c4\u03bf\u03c4\u03ae\u03c4\u03c9\u03bd \u03b3\u03b9\u03b1 \u03c4\u03bf \u0397\u03bc\u03b5\u03c1\u03bf\u03bb\u03cc\u03b3\u03b9\u03bf Google \u03c3\u03c4\u03bf configuration.yaml, \u03c4\u03bf \u03bf\u03c0\u03bf\u03af\u03bf \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd. \u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03c4\u03b5 \u03bc\u03b5 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf \u03c4\u03c1\u03cc\u03c0\u03bf \u03c4\u03b9\u03c2 \u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03c0\u03bf\u03c5 \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03c0\u03c1\u03cc\u03c3\u03c6\u03b1\u03c4\u03b1. \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 track_new \u03b1\u03c0\u03cc \u03c4\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", - "title": "\u0397 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7 \u03bf\u03bd\u03c4\u03bf\u03c4\u03ae\u03c4\u03c9\u03bd \u03c4\u03bf\u03c5 \u0397\u03bc\u03b5\u03c1\u03bf\u03bb\u03bf\u03b3\u03af\u03bf\u03c5 Google \u03ad\u03c7\u03b5\u03b9 \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03b9" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/en.json b/homeassistant/components/google/translations/en.json index 4ce207ccd5b..1720e8c1454 100644 --- a/homeassistant/components/google/translations/en.json +++ b/homeassistant/components/google/translations/en.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "Configuring the Google Calendar in configuration.yaml is being removed in Home Assistant 2022.9.\n\nYour existing OAuth Application Credentials and access settings have been imported into the UI automatically. Remove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", - "title": "The Google Calendar YAML configuration is being removed" - }, - "removed_track_new_yaml": { - "description": "You have disabled entity tracking for Google Calendar in configuration.yaml, which is no longer supported. You must manually change the integration System Options in the UI to disable newly discovered entities going forward. Remove the track_new setting from configuration.yaml and restart Home Assistant to fix this issue.", - "title": "Google Calendar entity tracking has changed" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/es.json b/homeassistant/components/google/translations/es.json index 107a320eb30..64b54f81064 100644 --- a/homeassistant/components/google/translations/es.json +++ b/homeassistant/components/google/translations/es.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "Se va a eliminar la configuraci\u00f3n de Google Calendar en configuration.yaml en Home Assistant 2022.9. \n\nTus credenciales OAuth de aplicaci\u00f3n existentes y la configuraci\u00f3n de acceso se han importado a la IU autom\u00e1ticamente. Elimina la configuraci\u00f3n YAML de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", - "title": "Se va a eliminar la configuraci\u00f3n YAML de Google Calendar" - }, - "removed_track_new_yaml": { - "description": "Has deshabilitado el rastreo de entidades para Google Calendar en configuration.yaml, que ya no es compatible. Debes cambiar manualmente las opciones de sistema de la integraci\u00f3n en la IU para deshabilitar las entidades reci\u00e9n descubiertas en el futuro. Elimina la configuraci\u00f3n track_new de configuration.yaml y reinicia Home Assistant para solucionar este problema.", - "title": "El rastreo de entidades de Google Calendar ha cambiado" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/et.json b/homeassistant/components/google/translations/et.json index 83dda4e151c..c516c9201e2 100644 --- a/homeassistant/components/google/translations/et.json +++ b/homeassistant/components/google/translations/et.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "Google'i kalendri konfigureerimine failis configuration.yaml eemaldatakse versioonis Home Assistant 2022.9.\n\nTeie olemasolevad OAuth-rakenduse volitused ja juurdep\u00e4\u00e4su seaded on automaatselt kasutajaliidesesse imporditud. Probleemi lahendamiseks eemaldage YAML-konfiguratsioon failist configuration.yaml ja taask\u00e4ivitage Home Assistant.", - "title": "Google'i kalendri YAML-i konfiguratsioon eemaldatakse" - }, - "removed_track_new_yaml": { - "description": "Oled keelanud Google'i kalendri olemite j\u00e4lgimise rakenduses configuration.yaml, mida enam ei toetata. Peate kasutajaliideses integratsioonis\u00fcsteemi suvandeid k\u00e4sitsi muutma, et \u00e4sja avastatud olemid edaspidi keelata. Eemaldage saidilt configuration.yaml s\u00e4te track_new ja taask\u00e4ivitage home assistant selle probleemi lahendamiseks.", - "title": "Google'i kalendri olemi j\u00e4lgimine on muutunud" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/fr.json b/homeassistant/components/google/translations/fr.json index b404fbb29be..389f769cdb9 100644 --- a/homeassistant/components/google/translations/fr.json +++ b/homeassistant/components/google/translations/fr.json @@ -33,11 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "title": "La configuration YAML pour Google\u00a0Agenda sera bient\u00f4t supprim\u00e9e" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/hu.json b/homeassistant/components/google/translations/hu.json index 467b1a663f2..b27e06b15c7 100644 --- a/homeassistant/components/google/translations/hu.json +++ b/homeassistant/components/google/translations/hu.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "A Google Napt\u00e1r konfigur\u00e1l\u00e1sa a configuration.yaml f\u00e1jlban a 2022.9-es Home Assistant verzi\u00f3ban elt\u00e1vol\u00edt\u00e1sra ker\u00fcl.\n\nA megl\u00e9v\u0151 OAuth alkalmaz\u00e1s hiteles\u00edt\u0151 adatai \u00e9s hozz\u00e1f\u00e9r\u00e9si be\u00e1ll\u00edt\u00e1sai automatikusan import\u00e1l\u00e1sra ker\u00fcltek a felhaszn\u00e1l\u00f3i fel\u00fcletbe. A probl\u00e9ma megold\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", - "title": "A Google Calendar YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" - }, - "removed_track_new_yaml": { - "description": "A configuration.yaml f\u00e1jlban a Google Calendar sz\u00e1m\u00e1ra az entit\u00e1sk\u00f6vet\u00e9s ki lett kapcsolva, ami m\u00e1r nem t\u00e1mogatott. Manu\u00e1lisan sz\u00fcks\u00e9ges m\u00f3dos\u00edtani az integr\u00e1ci\u00f3s rendszerbe\u00e1ll\u00edt\u00e1sokat a felhaszn\u00e1l\u00f3i fel\u00fcleten, hogy a j\u00f6v\u0151ben letiltsa az \u00fajonnan felfedezett entit\u00e1sokat. A probl\u00e9ma megold\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a track_new be\u00e1ll\u00edt\u00e1st a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", - "title": "A Google Napt\u00e1r entit\u00e1sk\u00f6vet\u00e9se megv\u00e1ltozott" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/id.json b/homeassistant/components/google/translations/id.json index 0488e5abbeb..20ed21a56be 100644 --- a/homeassistant/components/google/translations/id.json +++ b/homeassistant/components/google/translations/id.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "Proses konfigurasi Integrasi Google Kalender di configuration.yaml dalam proses penghapusan di Home Assistant 2022.9.\n\nKredensial Aplikasi OAuth yang Anda dan setelan akses telah diimpor ke antarmuka secara otomatis. Hapus konfigurasi YAML dari file configuration.yaml Anda dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", - "title": "Konfigurasi YAML Integrasi Google Kalender dalam proses penghapusan" - }, - "removed_track_new_yaml": { - "description": "Anda telah menonaktifkan pelacakan entitas untuk Google Kalender di configuration.yaml, yang kini tidak lagi didukung. Anda harus secara manual mengubah Opsi Sistem integrasi di antarmuka untuk menonaktifkan entitas yang baru ditemukan di masa datang. Hapus pengaturan track_new dari configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", - "title": "Pelacakan entitas Google Kalender telah berubah" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/it.json b/homeassistant/components/google/translations/it.json index 282c1d06544..782fed55d5b 100644 --- a/homeassistant/components/google/translations/it.json +++ b/homeassistant/components/google/translations/it.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "La configurazione di Google Calendar in configuration.yaml sar\u00e0 rimossa in Home Assistant 2022.9. \n\nLe credenziali dell'applicazione OAuth esistenti e le impostazioni di accesso sono state importate automaticamente nell'interfaccia utente. Rimuovi la configurazione YAML dal file configuration.yaml e riavvia Home Assistant per risolvere questo problema.", - "title": "La configurazione YAML di Google Calendar sar\u00e0 rimossa" - }, - "removed_track_new_yaml": { - "description": "Hai disabilitato il tracciamento delle entit\u00e0 per Google Calendar in configuration.yaml, che non \u00e8 pi\u00f9 supportato. \u00c8 necessario modificare manualmente le opzioni di sistema dell'integrazione nell'interfaccia utente per disabilitare le nuove entit\u00e0 rilevate in futuro. Rimuovi l'impostazione track_new da configuration.yaml e riavvia Home Assistant per risolvere questo problema.", - "title": "Il tracciamento dell'entit\u00e0 di Google Calendar \u00e8 cambiato" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/ja.json b/homeassistant/components/google/translations/ja.json index 057734a2bca..6e2aac00c5d 100644 --- a/homeassistant/components/google/translations/ja.json +++ b/homeassistant/components/google/translations/ja.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "configuration.yaml\u3092\u4f7f\u7528\u3057\u305f\u3001Google\u30ab\u30ec\u30f3\u30c0\u30fc\u306e\u8a2d\u5b9a\u306f\u3001Home Assistant 2022.9\u3067\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002 \n\n\u306a\u304a\u3001OAuth \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u8cc7\u683c\u60c5\u5831\u3068\u30a2\u30af\u30bb\u30b9\u8a2d\u5b9a\u306f\u3001UI\u306b\u81ea\u52d5\u7684\u306b\u30a4\u30f3\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u3053\u306e\u554f\u984c\u3092\u89e3\u6c7a\u3059\u308b\u306b\u306f\u3001configuration.yaml \u30d5\u30a1\u30a4\u30eb\u304b\u3089YAML\u8a2d\u5b9a\u3092\u524a\u9664\u3057\u3001Home Assistant\u3092\u518d\u8d77\u52d5\u3057\u307e\u3059\u3002", - "title": "Google\u30ab\u30ec\u30f3\u30c0\u30fcyaml\u306e\u8a2d\u5b9a\u306f\u524a\u9664\u3055\u308c\u3066\u3044\u307e\u3059" - }, - "removed_track_new_yaml": { - "description": "configuration.yaml\u3067\u3001Google \u30ab\u30ec\u30f3\u30c0\u30fc\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u8ffd\u8de1\u3092\u7121\u52b9\u306b\u3067\u304d\u307e\u3057\u305f\u304c\u3001\u3053\u308c\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u306a\u304f\u306a\u308a\u307e\u3057\u305f\u3002UI\u306e\u7d71\u5408\u30b7\u30b9\u30c6\u30e0\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u624b\u52d5\u3067\u5909\u66f4\u3057\u3066\u3001\u65b0\u3057\u304f\u691c\u51fa\u3055\u308c\u305f\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u4eca\u5f8c\u7121\u52b9\u306b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059(disable newly discovered entities going forward)\u3002configuration.yaml\u304b\u3089track_new\u8a2d\u5b9a\u3092\u524a\u9664\u3057\u3001Home Assistant\u3092\u518d\u8d77\u52d5\u3057\u3066\u3053\u306e\u554f\u984c\u3092\u89e3\u6c7a\u3057\u307e\u3059\u3002", - "title": "Google\u30ab\u30ec\u30f3\u30c0\u30fc\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u30c8\u30e9\u30c3\u30ad\u30f3\u30b0\u304c\u5909\u66f4\u3055\u308c\u307e\u3057\u305f" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/nl.json b/homeassistant/components/google/translations/nl.json index 572ac985041..05b053497c4 100644 --- a/homeassistant/components/google/translations/nl.json +++ b/homeassistant/components/google/translations/nl.json @@ -30,11 +30,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "title": "De Google Calendar YAML-configuratie wordt verwijderd" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/no.json b/homeassistant/components/google/translations/no.json index d020a0f294e..cbc39b8ff6a 100644 --- a/homeassistant/components/google/translations/no.json +++ b/homeassistant/components/google/translations/no.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "Konfigurering av Google Kalender i configuration.yaml blir fjernet i Home Assistant 2022.9. \n\n Din eksisterende OAuth-applikasjonslegitimasjon og tilgangsinnstillinger er automatisk importert til brukergrensesnittet. Fjern YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", - "title": "Google Kalender YAML-konfigurasjonen blir fjernet" - }, - "removed_track_new_yaml": { - "description": "Du har deaktivert enhetssporing for Google Kalender i configuration.yaml, som ikke lenger st\u00f8ttes. Du m\u00e5 manuelt endre integreringssystemalternativene i brukergrensesnittet for \u00e5 deaktivere nyoppdagede enheter fremover. Fjern track_new-innstillingen fra configuration.yaml og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", - "title": "Google Kalender-enhetssporing er endret" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/pl.json b/homeassistant/components/google/translations/pl.json index fb85af430df..ff7b8af3bfc 100644 --- a/homeassistant/components/google/translations/pl.json +++ b/homeassistant/components/google/translations/pl.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "Konfiguracja Kalendarza Google w configuration.yaml zostanie usuni\u0119ta w Home Assistant 2022.9. \n\nTwoje istniej\u0105ce po\u015bwiadczenia aplikacji OAuth i ustawienia dost\u0119pu zosta\u0142y automatycznie zaimportowane do interfejsu u\u017cytkownika. Usu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", - "title": "Konfiguracja YAML dla Kalendarza Google zostanie usuni\u0119ta" - }, - "removed_track_new_yaml": { - "description": "Wy\u0142\u0105czy\u0142e\u015b \u015bledzenie encji w Kalendarzu Google w pliku configuration.yaml, kt\u00f3ry nie jest ju\u017c obs\u0142ugiwany. Musisz r\u0119cznie zmieni\u0107 ustawienie w Opcjach Systemu integracji, aby wy\u0142\u0105czy\u0107 nowo wykryte encje w przysz\u0142o\u015bci. Usu\u0144 ustawienie track_new z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", - "title": "\u015aledzenie encji Kalendarza Google uleg\u0142o zmianie" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/pt-BR.json b/homeassistant/components/google/translations/pt-BR.json index 709737dbe2d..c7068a94602 100644 --- a/homeassistant/components/google/translations/pt-BR.json +++ b/homeassistant/components/google/translations/pt-BR.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "A configura\u00e7\u00e3o do Google Agenda em configuration.yaml est\u00e1 sendo removida no Home Assistant 2022.9. \n\n Suas credenciais de aplicativo OAuth e configura\u00e7\u00f5es de acesso existentes foram importadas para a interface do usu\u00e1rio automaticamente. Remova a configura\u00e7\u00e3o YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", - "title": "A configura\u00e7\u00e3o YAML do Google Agenda est\u00e1 sendo removida" - }, - "removed_track_new_yaml": { - "description": "Voc\u00ea desativou as entidades de rastreamento para o Google Agenda em configuration.yaml, que n\u00e3o \u00e9 mais compat\u00edvel. Voc\u00ea deve alterar manualmente as op\u00e7\u00f5es do sistema de integra\u00e7\u00e3o na interface do usu\u00e1rio para desativar as entidades rec\u00e9m-descobertas daqui para frente. Remova a configura\u00e7\u00e3o track_new de configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", - "title": "A entidade de rastreamento do Google Agenda foi alterado" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/ru.json b/homeassistant/components/google/translations/ru.json index be7b92a707c..5fc2cb03feb 100644 --- a/homeassistant/components/google/translations/ru.json +++ b/homeassistant/components/google/translations/ru.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Google Calendar \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430 \u0432 Home Assistant \u0432\u0435\u0440\u0441\u0438\u0438 2022.9.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", - "title": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Google Calendar \u0447\u0435\u0440\u0435\u0437 YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" - }, - "removed_track_new_yaml": { - "description": "\u0412\u044b \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u043b\u0438 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0434\u043b\u044f Google Calendar \u0432 \u0444\u0430\u0439\u043b\u0435 configuration.yaml, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f. \u0427\u0442\u043e\u0431\u044b \u043d\u043e\u0432\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043d\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u043b\u0438\u0441\u044c \u0432 Home Assistant \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438, \u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 track_new \u0438\u0437 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", - "title": "\u0418\u0437\u043c\u0435\u043d\u0435\u043d \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 Google Calendar" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/sv.json b/homeassistant/components/google/translations/sv.json index 499ea375547..110ecd8773a 100644 --- a/homeassistant/components/google/translations/sv.json +++ b/homeassistant/components/google/translations/sv.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "Konfigurering av Google Kalender i configuration.yaml tas bort i Home Assistant 2022.9. \n\n Dina befintliga OAuth-applikationsuppgifter och \u00e5tkomstinst\u00e4llningar har importerats till anv\u00e4ndargr\u00e4nssnittet automatiskt. Ta bort YAML-konfigurationen fr\u00e5n filen configuration.yaml och starta om Home Assistant f\u00f6r att \u00e5tg\u00e4rda problemet.", - "title": "Google Kalender YAML-konfigurationen tas bort" - }, - "removed_track_new_yaml": { - "description": "Du har inaktiverat enhetssp\u00e5rning f\u00f6r Google Kalender i configuration.yaml, som inte l\u00e4ngre st\u00f6ds. Du m\u00e5ste manuellt \u00e4ndra integrationssystemalternativen i anv\u00e4ndargr\u00e4nssnittet f\u00f6r att inaktivera nyuppt\u00e4ckta enheter fram\u00f6ver. Ta bort inst\u00e4llningen track_new fr\u00e5n configuration.yaml och starta om Home Assistant f\u00f6r att \u00e5tg\u00e4rda problemet.", - "title": "Sp\u00e5rning av enheter i Google Kalender har \u00e4ndrats" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/tr.json b/homeassistant/components/google/translations/tr.json index a7074b69127..7d67018630f 100644 --- a/homeassistant/components/google/translations/tr.json +++ b/homeassistant/components/google/translations/tr.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "Google Takvim'in configuration.yaml dosyas\u0131nda yap\u0131land\u0131r\u0131lmas\u0131 Home Assistant 2022.9'da kald\u0131r\u0131l\u0131yor.\n\nMevcut OAuth Uygulama Kimlik Bilgileriniz ve eri\u015fim ayarlar\u0131n\u0131z otomatik olarak kullan\u0131c\u0131 aray\u00fcz\u00fcne aktar\u0131lm\u0131\u015ft\u0131r. YAML yap\u0131land\u0131rmas\u0131n\u0131 configuration.yaml dosyan\u0131zdan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", - "title": "Google Takvim YAML yap\u0131land\u0131rmas\u0131 kald\u0131r\u0131l\u0131yor" - }, - "removed_track_new_yaml": { - "description": "Art\u0131k desteklenmeyen configuration.yaml dosyas\u0131nda Google Takvim i\u00e7in varl\u0131k izlemeyi devre d\u0131\u015f\u0131 b\u0131rakt\u0131n\u0131z. \u0130leride yeni ke\u015ffedilen varl\u0131klar\u0131 devre d\u0131\u015f\u0131 b\u0131rakmak i\u00e7in kullan\u0131c\u0131 aray\u00fcz\u00fcndeki entegrasyon Sistem Se\u00e7eneklerini manuel olarak de\u011fi\u015ftirmeniz gerekir. Bu sorunu \u00e7\u00f6zmek i\u00e7in configuration.yaml dosyas\u0131ndan track_new ayar\u0131n\u0131 kald\u0131r\u0131n ve Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", - "title": "Google Takvim varl\u0131k takibi de\u011fi\u015fti" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/google/translations/zh-Hant.json b/homeassistant/components/google/translations/zh-Hant.json index 93e2fa8f7ba..45dc83c1b3c 100644 --- a/homeassistant/components/google/translations/zh-Hant.json +++ b/homeassistant/components/google/translations/zh-Hant.json @@ -33,16 +33,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a\u7684 Google \u65e5\u66c6\u5373\u5c07\u65bc Home Assistant 2022.9 \u7248\u4e2d\u9032\u884c\u79fb\u9664\u3002\n\n\u65e2\u6709\u7684 OAuth \u61c9\u7528\u6191\u8b49\u8207\u5b58\u53d6\u6b0a\u9650\u5c07\u81ea\u52d5\u532f\u5165\u81f3 UI \u5167\u3002\u8acb\u65bc configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 YAML \u8a2d\u5b9a\u4e26\u91cd\u65b0\u555f\u52d5 Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", - "title": "Google \u65e5\u66c6 YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" - }, - "removed_track_new_yaml": { - "description": "\u65bc configuration.yaml \u5167\u6240\u8a2d\u5b9a\u7684 Google \u65e5\u66c6\u5be6\u9ad4\u8ffd\u8e64\u529f\u80fd\uff0c\u7531\u65bc\u4e0d\u518d\u652f\u6301\u3001\u5df2\u7d93\u906d\u5230\u95dc\u9589\u3002\u4e4b\u5f8c\u5fc5\u9808\u624b\u52d5\u900f\u904e\u4ecb\u9762\u5167\u7684\u6574\u5408\u529f\u80fd\u3001\u4ee5\u95dc\u9589\u4efb\u4f55\u65b0\u767c\u73fe\u7684\u5be6\u9ad4\u3002\u8acb\u7531 configuration.yaml \u4e2d\u79fb\u9664R track_new \u4e26\u91cd\u555f Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", - "title": "Google \u65e5\u66c6\u5be6\u9ad4\u8ffd\u8e64\u5df2\u7d93\u8b8a\u66f4" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/group/translations/bg.json b/homeassistant/components/group/translations/bg.json index 5d63f29aff0..e1c4489e0e0 100644 --- a/homeassistant/components/group/translations/bg.json +++ b/homeassistant/components/group/translations/bg.json @@ -48,6 +48,12 @@ "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" }, "user": { + "description": "\u0413\u0440\u0443\u043f\u0438\u0442\u0435 \u0412\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0432\u0430\u0442 \u0434\u0430 \u0441\u044a\u0437\u0434\u0430\u0434\u0435\u0442\u0435 \u043d\u043e\u0432 \u043e\u0431\u0435\u043a\u0442, \u043a\u043e\u0439\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0432\u0430 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043e\u0431\u0435\u043a\u0442\u0438 \u043e\u0442 \u0435\u0434\u0438\u043d \u0438 \u0441\u044a\u0449\u0438 \u0442\u0438\u043f.", + "menu_options": { + "fan": "\u0413\u0440\u0443\u043f\u0430 \u0432\u0435\u043d\u0442\u0438\u043b\u0430\u0442\u043e\u0440\u0438", + "light": "\u0413\u0440\u0443\u043f\u0430 \u0441\u0432\u0435\u0442\u043b\u0438\u043d\u0438", + "media_player": "\u0413\u0440\u0443\u043f\u0430 \u043c\u0435\u0434\u0438\u0439\u043d\u0438 \u043f\u043b\u0435\u0439\u044a\u0440\u0438" + }, "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" } } diff --git a/homeassistant/components/guardian/translations/bg.json b/homeassistant/components/guardian/translations/bg.json index d48caec927f..c8098f9355a 100644 --- a/homeassistant/components/guardian/translations/bg.json +++ b/homeassistant/components/guardian/translations/bg.json @@ -23,17 +23,6 @@ } }, "title": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 {deprecated_service} \u0449\u0435 \u0431\u044a\u0434\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u0430" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "\u0410\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u0439\u0442\u0435 \u0432\u0441\u0438\u0447\u043a\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u043b\u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u0435, \u043a\u043e\u0438\u0442\u043e \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442 \u0442\u043e\u0437\u0438 \u043e\u0431\u0435\u043a\u0442, \u0442\u0430\u043a\u0430 \u0447\u0435 \u0432\u043c\u0435\u0441\u0442\u043e \u043d\u0435\u0433\u043e \u0434\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442 `{replacement_entity_id}`.", - "title": "\u041e\u0431\u0435\u043a\u0442\u044a\u0442 {old_entity_id} \u0449\u0435 \u0431\u044a\u0434\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442" - } - } - }, - "title": "\u041e\u0431\u0435\u043a\u0442\u044a\u0442 {old_entity_id} \u0449\u0435 \u0431\u044a\u0434\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/ca.json b/homeassistant/components/guardian/translations/ca.json index dee6614924d..709edc2476f 100644 --- a/homeassistant/components/guardian/translations/ca.json +++ b/homeassistant/components/guardian/translations/ca.json @@ -29,17 +29,6 @@ } }, "title": "El servei {deprecated_service} ser\u00e0 eliminat" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Actualitza totes les automatitzacions o 'scripts' que utilitzin aquesta entitat perqu\u00e8 passin a utilitzar l'entitat `{replacement_entity_id}`.", - "title": "L'entitat {old_entity_id} s'eliminar\u00e0" - } - } - }, - "title": "L'entitat {old_entity_id} s'eliminar\u00e0" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/de.json b/homeassistant/components/guardian/translations/de.json index 9d5c0c0265c..ac9c71d14e1 100644 --- a/homeassistant/components/guardian/translations/de.json +++ b/homeassistant/components/guardian/translations/de.json @@ -29,17 +29,6 @@ } }, "title": "Der Dienst {deprecated_service} wird entfernt" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Aktualisiere alle Automatisierungen oder Skripte, die diese Entit\u00e4t verwenden, um stattdessen `{replacement_entity_id}` zu verwenden.", - "title": "Die Entit\u00e4t {old_entity_id} wird entfernt" - } - } - }, - "title": "Die Entit\u00e4t {old_entity_id} wird entfernt" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/el.json b/homeassistant/components/guardian/translations/el.json index 0857e0284a4..2a4963c8649 100644 --- a/homeassistant/components/guardian/translations/el.json +++ b/homeassistant/components/guardian/translations/el.json @@ -29,17 +29,6 @@ } }, "title": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 {deprecated_service} \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "\u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03c4\u03b5 \u03cc\u03bb\u03bf\u03c5\u03c2 \u03c4\u03bf\u03c5\u03c2 \u03b1\u03c5\u03c4\u03bf\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03bf\u03cd\u03c2 \u03ae \u03c4\u03b1 \u03c3\u03b5\u03bd\u03ac\u03c1\u03b9\u03b1 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7\u03bd \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03ce\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd \u03b1\u03bd\u03c4\u03af \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03bf `{replacement_entity_id}`.", - "title": "\u0397 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 {old_entity_id} \u03b8\u03b1 \u03b1\u03c6\u03b1\u03b9\u03c1\u03b5\u03b8\u03b5\u03af" - } - } - }, - "title": "\u0397 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 {old_entity_id} \u03b8\u03b1 \u03b1\u03c6\u03b1\u03b9\u03c1\u03b5\u03b8\u03b5\u03af" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/en.json b/homeassistant/components/guardian/translations/en.json index ac87ae36506..1aaf8b888c8 100644 --- a/homeassistant/components/guardian/translations/en.json +++ b/homeassistant/components/guardian/translations/en.json @@ -29,17 +29,6 @@ } }, "title": "The {deprecated_service} service will be removed" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Update any automations or scripts that use this entity to instead use `{replacement_entity_id}`.", - "title": "The {old_entity_id} entity will be removed" - } - } - }, - "title": "The {old_entity_id} entity will be removed" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/es.json b/homeassistant/components/guardian/translations/es.json index 16233859836..e1e189a650f 100644 --- a/homeassistant/components/guardian/translations/es.json +++ b/homeassistant/components/guardian/translations/es.json @@ -29,17 +29,6 @@ } }, "title": "Se va a eliminar el servicio {deprecated_service}" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Actualiza cualquier automatizaci\u00f3n o script que use esta entidad para usar `{replacement_entity_id}`.", - "title": "Se eliminar\u00e1 la entidad {old_entity_id}" - } - } - }, - "title": "Se eliminar\u00e1 la entidad {old_entity_id}" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/et.json b/homeassistant/components/guardian/translations/et.json index 95c98315435..2af9912946d 100644 --- a/homeassistant/components/guardian/translations/et.json +++ b/homeassistant/components/guardian/translations/et.json @@ -29,17 +29,6 @@ } }, "title": "Teenus {deprecated_service} eemaldatakse" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Uuenda k\u00f5iki automaatikaid v\u00f5i skripte, mis kasutavad seda olemit, et kasutada selle asemel `{replacement_entity_id}}.", - "title": "Olem {old_entity_id} eemaldatakse" - } - } - }, - "title": "Olem {old_entity_id} eemaldatakse" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/fr.json b/homeassistant/components/guardian/translations/fr.json index 253d28ec770..eead1a91a22 100644 --- a/homeassistant/components/guardian/translations/fr.json +++ b/homeassistant/components/guardian/translations/fr.json @@ -29,17 +29,6 @@ } }, "title": "Le service {deprecated_service} sera bient\u00f4t supprim\u00e9" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Modifiez tout script ou automatisation utilisant cette entit\u00e9 afin qu'ils utilisent `{replacement_entity_id}` \u00e0 la place.", - "title": "L'entit\u00e9 {old_entity_id} sera supprim\u00e9e" - } - } - }, - "title": "L'entit\u00e9 {old_entity_id} sera supprim\u00e9e" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/hu.json b/homeassistant/components/guardian/translations/hu.json index 20b5d00e2f1..c9ad858f34f 100644 --- a/homeassistant/components/guardian/translations/hu.json +++ b/homeassistant/components/guardian/translations/hu.json @@ -29,17 +29,6 @@ } }, "title": "{deprecated_service} szolg\u00e1ltat\u00e1s elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Friss\u00edtse az ezt az entit\u00e1st haszn\u00e1l\u00f3 automatiz\u00e1l\u00e1sokat vagy szkripteket, hogy helyette a k\u00f6vetkez\u0151t haszn\u00e1ja: `{replacement_entity_id}`", - "title": "{old_entity_id} entit\u00e1s el lesz t\u00e1vol\u00edtva" - } - } - }, - "title": "{old_entity_id} entit\u00e1s el lesz t\u00e1vol\u00edtva" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/id.json b/homeassistant/components/guardian/translations/id.json index e62f48eae8f..6558dfa262d 100644 --- a/homeassistant/components/guardian/translations/id.json +++ b/homeassistant/components/guardian/translations/id.json @@ -29,17 +29,6 @@ } }, "title": "Layanan {deprecated_service} akan dihapus" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Perbarui setiap otomasi atau skrip yang menggunakan entitas ini untuk menggunakan `{replacement_entity_id}`.", - "title": "Entitas {old_entity_id} akan dihapus" - } - } - }, - "title": "Entitas {old_entity_id} akan dihapus" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/it.json b/homeassistant/components/guardian/translations/it.json index 29c1d90ed87..7b5062222d2 100644 --- a/homeassistant/components/guardian/translations/it.json +++ b/homeassistant/components/guardian/translations/it.json @@ -29,17 +29,6 @@ } }, "title": "Il servizio {deprecated_service} verr\u00e0 rimosso" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Aggiorna tutte le automazioni o gli script che utilizzano questa entit\u00e0 in modo che utilizzino invece `{replacement_entity_id}`.", - "title": "L'entit\u00e0 {old_entity_id} verr\u00e0 rimossa" - } - } - }, - "title": "L'entit\u00e0 {old_entity_id} verr\u00e0 rimossa" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/nl.json b/homeassistant/components/guardian/translations/nl.json index 0fadd4dbf51..39a397375e2 100644 --- a/homeassistant/components/guardian/translations/nl.json +++ b/homeassistant/components/guardian/translations/nl.json @@ -28,16 +28,6 @@ } }, "title": "De {deprecated_service}-service wordt verwijderd" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "title": "De {old_entity_id}-entiteit wordt verwijderd" - } - } - }, - "title": "De {old_entity_id}-entiteit wordt verwijderd" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/no.json b/homeassistant/components/guardian/translations/no.json index b550bf9c43d..425b973aa5d 100644 --- a/homeassistant/components/guardian/translations/no.json +++ b/homeassistant/components/guardian/translations/no.json @@ -29,17 +29,6 @@ } }, "title": "{deprecated_service} -tjenesten vil bli fjernet" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Oppdater eventuelle automatiseringer eller skript som bruker denne enheten til i stedet \u00e5 bruke ` {replacement_entity_id} `.", - "title": "{old_entity_id} vil bli fjernet" - } - } - }, - "title": "{old_entity_id} vil bli fjernet" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/pl.json b/homeassistant/components/guardian/translations/pl.json index f1cb2893050..4fb2f523807 100644 --- a/homeassistant/components/guardian/translations/pl.json +++ b/homeassistant/components/guardian/translations/pl.json @@ -29,17 +29,6 @@ } }, "title": "Us\u0142uga {deprecated_service} zostanie usuni\u0119ta" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Zaktualizuj wszystkie automatyzacje lub skrypty, kt\u00f3re u\u017cywaj\u0105 tej encji, aby zamiast tego u\u017cywa\u0142y `{replacement_entity_id}`.", - "title": "Encja {old_entity_id} zostanie usuni\u0119ta" - } - } - }, - "title": "Encja {old_entity_id} zostanie usuni\u0119ta" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/pt-BR.json b/homeassistant/components/guardian/translations/pt-BR.json index 53e3c724b7c..b01f925744e 100644 --- a/homeassistant/components/guardian/translations/pt-BR.json +++ b/homeassistant/components/guardian/translations/pt-BR.json @@ -29,17 +29,6 @@ } }, "title": "O servi\u00e7o {deprecated_service} ser\u00e1 removido" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Atualize quaisquer automa\u00e7\u00f5es ou scripts que usam essa entidade para usar `{replacement_entity_id}`.", - "title": "A entidade {old_entity_id} ser\u00e1 removida" - } - } - }, - "title": "A entidade {old_entity_id} ser\u00e1 removida" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/ru.json b/homeassistant/components/guardian/translations/ru.json index 6cfc857f0de..7e64fe528ff 100644 --- a/homeassistant/components/guardian/translations/ru.json +++ b/homeassistant/components/guardian/translations/ru.json @@ -29,17 +29,6 @@ } }, "title": "\u0421\u043b\u0443\u0436\u0431\u0430 {deprecated_service} \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "\u0412 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f\u0445 \u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u0430\u0445, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0445 \u044d\u0442\u043e\u0442 \u043e\u0431\u044a\u0435\u043a\u0442, \u0442\u0435\u043f\u0435\u0440\u044c \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 `{replacement_entity_id}`.", - "title": "\u041e\u0431\u044a\u0435\u043a\u0442 {old_entity_id} \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d" - } - } - }, - "title": "\u041e\u0431\u044a\u0435\u043a\u0442 {old_entity_id} \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/sv.json b/homeassistant/components/guardian/translations/sv.json index 0912dd4094b..af41cc85efe 100644 --- a/homeassistant/components/guardian/translations/sv.json +++ b/homeassistant/components/guardian/translations/sv.json @@ -29,17 +29,6 @@ } }, "title": "Tj\u00e4nsten {deprecated_service} tas bort" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Uppdatera alla automatiseringar eller skript som anv\u00e4nder denna enhet f\u00f6r att ist\u00e4llet anv\u00e4nda ` {replacement_entity_id} `.", - "title": "{old_entity_id} kommer att tas bort" - } - } - }, - "title": "{old_entity_id} kommer att tas bort" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/tr.json b/homeassistant/components/guardian/translations/tr.json index c7e557a38f2..dea0e3fa9ec 100644 --- a/homeassistant/components/guardian/translations/tr.json +++ b/homeassistant/components/guardian/translations/tr.json @@ -29,17 +29,6 @@ } }, "title": "{deprecated_service} hizmeti kald\u0131r\u0131lacak" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Bunun yerine ` {replacement_entity_id} ` kullanmak i\u00e7in bu varl\u0131\u011f\u0131 kullanan t\u00fcm otomasyonlar\u0131 veya komut dosyalar\u0131n\u0131 g\u00fcncelleyin.", - "title": "{old_entity_id} varl\u0131\u011f\u0131 kald\u0131r\u0131lacak" - } - } - }, - "title": "{old_entity_id} varl\u0131\u011f\u0131 kald\u0131r\u0131lacak" } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/zh-Hant.json b/homeassistant/components/guardian/translations/zh-Hant.json index bd30a848b35..54649e2bf31 100644 --- a/homeassistant/components/guardian/translations/zh-Hant.json +++ b/homeassistant/components/guardian/translations/zh-Hant.json @@ -29,17 +29,6 @@ } }, "title": "{deprecated_service} \u670d\u52d9\u5c07\u79fb\u9664" - }, - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "\u4f7f\u7528\u6b64\u5be6\u9ad4\u4ee5\u66f4\u65b0\u4efb\u4f55\u81ea\u52d5\u5316\u6216\u8173\u672c\uff0c\u4ee5\u53d6\u4ee3 `{replacement_entity_id}`\u3002", - "title": "{old_entity_id} \u5be6\u9ad4\u5c07\u9032\u884c\u79fb\u9664" - } - } - }, - "title": "{old_entity_id} \u5be6\u9ad4\u5c07\u9032\u884c\u79fb\u9664" } } } \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.en.json b/homeassistant/components/harmony/translations/select.en.json index 5dbdf1a1c3d..539a1c14c21 100644 --- a/homeassistant/components/harmony/translations/select.en.json +++ b/homeassistant/components/harmony/translations/select.en.json @@ -1,7 +1,7 @@ { - "state": { - "harmony__activities": { - "power_off": "Power Off" + "state": { + "harmony__activities": { + "power_off": "Power Off" + } } - } -} +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/bg.json b/homeassistant/components/homeassistant_yellow/translations/bg.json index 7bd4bbd2aa1..4c009671060 100644 --- a/homeassistant/components/homeassistant_yellow/translations/bg.json +++ b/homeassistant/components/homeassistant_yellow/translations/bg.json @@ -5,7 +5,8 @@ "addon_install_failed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Silicon Labs Multiprotocol.", "addon_set_config_failed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0437\u0430\u0434\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Silicon Labs Multiprotocol.", "addon_start_failed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Silicon Labs Multiprotocol.", - "not_hassio": "\u0425\u0430\u0440\u0434\u0443\u0435\u0440\u043d\u0438\u0442\u0435 \u043e\u043f\u0446\u0438\u0438 \u043c\u043e\u0433\u0430\u0442 \u0434\u0430 \u0441\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0442 \u0441\u0430\u043c\u043e \u0437\u0430 \u0438\u043d\u0441\u0442\u0430\u043b\u0430\u0446\u0438\u0438 \u043d\u0430 HassOS." + "not_hassio": "\u0425\u0430\u0440\u0434\u0443\u0435\u0440\u043d\u0438\u0442\u0435 \u043e\u043f\u0446\u0438\u0438 \u043c\u043e\u0433\u0430\u0442 \u0434\u0430 \u0441\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0442 \u0441\u0430\u043c\u043e \u0437\u0430 \u0438\u043d\u0441\u0442\u0430\u043b\u0430\u0446\u0438\u0438 \u043d\u0430 HassOS.", + "zha_migration_failed": "\u041c\u0438\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 ZHA \u043d\u0435 \u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430." }, "error": { "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" diff --git a/homeassistant/components/homeassistant_yellow/translations/ca.json b/homeassistant/components/homeassistant_yellow/translations/ca.json index e3f318dca31..c7fdf1a4acd 100644 --- a/homeassistant/components/homeassistant_yellow/translations/ca.json +++ b/homeassistant/components/homeassistant_yellow/translations/ca.json @@ -5,7 +5,8 @@ "addon_install_failed": "No s'ha pogut instal\u00b7lar el complement Silicon Labs Multiprotocol-", "addon_set_config_failed": "No s'ha pogut establir la configuraci\u00f3 de Silicon Labs Multiprotocol.", "addon_start_failed": "No s'ha pogut iniciar el complement Silicon Labs Multiprotocol.", - "not_hassio": "Les opcions de maquinari nom\u00e9s es poden configurar a les instal\u00b7lacions HassOS." + "not_hassio": "Les opcions de maquinari nom\u00e9s es poden configurar a les instal\u00b7lacions HassOS.", + "zha_migration_failed": "La migraci\u00f3 ZHA no ha tingut \u00e8xit." }, "error": { "unknown": "Error inesperat" diff --git a/homeassistant/components/homeassistant_yellow/translations/et.json b/homeassistant/components/homeassistant_yellow/translations/et.json index b1ecacaf12e..2f2203d1e3c 100644 --- a/homeassistant/components/homeassistant_yellow/translations/et.json +++ b/homeassistant/components/homeassistant_yellow/translations/et.json @@ -6,7 +6,7 @@ "addon_set_config_failed": "Silicon Labs Multiprotocol konfiguratsiooni seadistamine eba\u00f5nnestus.", "addon_start_failed": "Silicon Labsi mitmeprotokolli lisandmooduli k\u00e4ivitamine nurjus.", "not_hassio": "Riistvaravalikuid saab konfigureerida ainult HassOS-i paigaldustes.", - "zha_migration_failed": "ZHA r\u00e4nne nurjus." + "zha_migration_failed": "ZHA sidumise siirdamine nurjus." }, "error": { "unknown": "Ootamatu t\u00f5rge" diff --git a/homeassistant/components/homeassistant_yellow/translations/id.json b/homeassistant/components/homeassistant_yellow/translations/id.json index cf100f4f07c..e3237d7b95e 100644 --- a/homeassistant/components/homeassistant_yellow/translations/id.json +++ b/homeassistant/components/homeassistant_yellow/translations/id.json @@ -23,7 +23,7 @@ "data": { "enable_multi_pan": "Aktifkan dukungan multiprotokol" }, - "description": "Jika dukungan multiprotocol diaktifkan, radio IEEE 802.15.4 Home Assistant Yellow dapat digunakan untuk Zigbee dan Thread (digunakan oleh Matter) secara bersamaan. Catatan: Ini adalah fitur eksperimental.", + "description": "Jika dukungan multiprotocol diaktifkan, radio IEEE 802.15.4 Home Assistant Yellow dapat digunakan untuk Zigbee dan Thread (digunakan oleh Matter) secara bersamaan. Catatan: Ini adalah fitur eksperimental. Jika komponen radio telah digunakan oleh integrasi ZHA Zigbee, ZHA akan dikonfigurasi ulang untuk menggunakan firmware multiprotokol.\n\nCatatan: Fitur ini bersifat eksperimental.", "title": "Aktifkan dukungan multiprotokol pada radio IEEE 802.15.4" }, "install_addon": { diff --git a/homeassistant/components/homeassistant_yellow/translations/no.json b/homeassistant/components/homeassistant_yellow/translations/no.json index d53c6b0fbcd..d93b078dd4b 100644 --- a/homeassistant/components/homeassistant_yellow/translations/no.json +++ b/homeassistant/components/homeassistant_yellow/translations/no.json @@ -5,7 +5,8 @@ "addon_install_failed": "Kunne ikke installere Silicon Labs Multiprotocol-tillegget.", "addon_set_config_failed": "Kunne ikke angi Silicon Labs Multiprotocol-konfigurasjon.", "addon_start_failed": "Kunne ikke starte Silicon Labs Multiprotocol-tillegget.", - "not_hassio": "Maskinvarealternativene kan bare konfigureres p\u00e5 HassOS-installasjoner." + "not_hassio": "Maskinvarealternativene kan bare konfigureres p\u00e5 HassOS-installasjoner.", + "zha_migration_failed": "ZHA-migreringen lyktes ikke." }, "error": { "unknown": "Uventet feil" @@ -22,7 +23,7 @@ "data": { "enable_multi_pan": "Aktiver st\u00f8tte for multiprotokoll" }, - "description": "N\u00e5r multiprotokollst\u00f8tte er aktivert, kan Home Assistant Yellows IEEE 802.15.4-radio brukes for b\u00e5de Zigbee og Thread (brukt av Matter) samtidig. Merk: Dette er en eksperimentell funksjon.", + "description": "N\u00e5r multiprotokollst\u00f8tte er aktivert, kan Home Assistant Yellows IEEE 802.15.4-radio brukes for b\u00e5de Zigbee og Thread (brukt av Matter) samtidig. Hvis radioen allerede brukes av ZHA Zigbee-integrasjonen, vil ZHA bli rekonfigurert til \u00e5 bruke multiprotokollfastvaren. \n\n Merk: Dette er en eksperimentell funksjon.", "title": "Aktiver st\u00f8tte for multiprotokoll p\u00e5 IEEE 802.15.4-radioen" }, "install_addon": { diff --git a/homeassistant/components/homeassistant_yellow/translations/pl.json b/homeassistant/components/homeassistant_yellow/translations/pl.json index a52ba1df93f..a0c4c902730 100644 --- a/homeassistant/components/homeassistant_yellow/translations/pl.json +++ b/homeassistant/components/homeassistant_yellow/translations/pl.json @@ -5,7 +5,8 @@ "addon_install_failed": "Nie uda\u0142o si\u0119 zainstalowa\u0107 dodatku Silicon Labs Multiprotocol.", "addon_set_config_failed": "Nie uda\u0142o si\u0119 ustawi\u0107 konfiguracji Silicon Labs Multiprotocol.", "addon_start_failed": "Nie uda\u0142o si\u0119 uruchomi\u0107 dodatku Silicon Labs Multiprotocol.", - "not_hassio": "Opcje sprz\u0119towe mo\u017cna skonfigurowa\u0107 tylko w instalacjach HassOS." + "not_hassio": "Opcje sprz\u0119towe mo\u017cna skonfigurowa\u0107 tylko w instalacjach HassOS.", + "zha_migration_failed": "Migracja ZHA nie powiod\u0142a si\u0119." }, "error": { "unknown": "Nieoczekiwany b\u0142\u0105d" @@ -22,7 +23,7 @@ "data": { "enable_multi_pan": "W\u0142\u0105cz obs\u0142ug\u0119 Multiprotocol" }, - "description": "Gdy w\u0142\u0105czona jest obs\u0142uga Multiprotocol, radio IEEE 802.15.4 Home Assistant Yellow mo\u017ce by\u0107 u\u017cywane jednocze\u015bnie dla Zigbee i Thread (u\u017cywane przez Matter). Uwaga: jest to funkcja eksperymentalna.", + "description": "Gdy w\u0142\u0105czona jest obs\u0142uga Multiprotocol, radio IEEE 802.15.4 Home Assistant Yellow mo\u017ce by\u0107 u\u017cywane jednocze\u015bnie dla Zigbee i Thread (u\u017cywane przez Matter). Uwaga: jest to funkcja eksperymentalna. Je\u015bli radio jest ju\u017c u\u017cywane przez integracj\u0119 ZHA Zigbee, ZHA zostanie ponownie skonfigurowane, aby korzysta\u0107 z oprogramowania multiprotocol.\n\nUwaga: jest to funkcja eksperymentalna.", "title": "W\u0142\u0105cz obs\u0142ug\u0119 multiprotocol w radiu IEEE 802.15.4" }, "install_addon": { diff --git a/homeassistant/components/homekit_controller/translations/id.json b/homeassistant/components/homekit_controller/translations/id.json index 0514fcca4ed..aa2d5111756 100644 --- a/homeassistant/components/homekit_controller/translations/id.json +++ b/homeassistant/components/homekit_controller/translations/id.json @@ -14,7 +14,7 @@ "authentication_error": "Kode HomeKit salah. Periksa dan coba lagi.", "insecure_setup_code": "Kode penyiapan yang diminta tidak aman karena sifatnya yang sepele. Aksesori ini gagal memenuhi persyaratan keamanan dasar.", "max_peers_error": "Perangkat menolak untuk menambahkan pemasangan karena tidak memiliki penyimpanan pemasangan yang tersedia.", - "pairing_failed": "Terjadi kesalahan yang tidak tertangani saat mencoba memasangkan dengan perangkat ini. Ini mungkin kegagalan sementara atau perangkat Anda mungkin tidak didukung saat ini.", + "pairing_failed": "Terjadi kesalahan yang tidak tertangani saat mencoba memasangkan dengan perangkat ini. Ini mungkin kegagalan sementara atau perangkat Anda mungkin tidak didukung saat ini: {error}", "unable_to_pair": "Gagal memasangkan, coba lagi.", "unknown_error": "Perangkat melaporkan kesalahan yang tidak diketahui. Pemasangan gagal." }, diff --git a/homeassistant/components/homekit_controller/translations/no.json b/homeassistant/components/homekit_controller/translations/no.json index 70b74d51f64..3775734f7c0 100644 --- a/homeassistant/components/homekit_controller/translations/no.json +++ b/homeassistant/components/homekit_controller/translations/no.json @@ -14,7 +14,7 @@ "authentication_error": "Ugyldig HomeKit kode. Vennligst sjekk den og pr\u00f8v igjen.", "insecure_setup_code": "Den forespurte installasjonskoden er usikker p\u00e5 grunn av triviell natur. Dette tilbeh\u00f8ret oppfyller ikke grunnleggende sikkerhetskrav.", "max_peers_error": "Enheten nekter \u00e5 sammenkoble da den ikke har ledig sammenkoblingslagring.", - "pairing_failed": "En uh\u00e5ndtert feil oppstod under fors\u00f8k p\u00e5 \u00e5 koble til denne enheten. Dette kan v\u00e6re en midlertidig feil, eller at enheten din kan ikke st\u00f8ttes for \u00f8yeblikket.", + "pairing_failed": "Det oppstod en uh\u00e5ndtert feil under fors\u00f8k p\u00e5 \u00e5 pare med denne enheten. Dette kan v\u00e6re en midlertidig feil eller enheten din st\u00f8ttes kanskje ikke for \u00f8yeblikket: {error}", "unable_to_pair": "Kunne ikke koble til, vennligst pr\u00f8v igjen.", "unknown_error": "Enheten rapporterte en ukjent feil. Sammenkobling mislyktes." }, diff --git a/homeassistant/components/homekit_controller/translations/pl.json b/homeassistant/components/homekit_controller/translations/pl.json index d7aba3a17c7..efd4f616553 100644 --- a/homeassistant/components/homekit_controller/translations/pl.json +++ b/homeassistant/components/homekit_controller/translations/pl.json @@ -14,7 +14,7 @@ "authentication_error": "Niepoprawny kod parowania HomeKit. Sprawd\u017a go i spr\u00f3buj ponownie.", "insecure_setup_code": "\u017b\u0105dany kod instalacyjny jest niezabezpieczony ze wzgl\u0119du na jego trywialny charakter. To akcesorium nie spe\u0142nia podstawowych wymaga\u0144 bezpiecze\u0144stwa.", "max_peers_error": "Urz\u0105dzenie odm\u00f3wi\u0142o parowania, poniewa\u017c nie ma wolnej pami\u0119ci parowania", - "pairing_failed": "Wyst\u0105pi\u0142 nieobs\u0142ugiwany b\u0142\u0105d podczas pr\u00f3by sparowania z tym urz\u0105dzeniem. Mo\u017ce to by\u0107 tymczasowa awaria lub urz\u0105dzenie mo\u017ce nie by\u0107 obecnie obs\u0142ugiwane.", + "pairing_failed": "Wyst\u0105pi\u0142 nieobs\u0142ugiwany b\u0142\u0105d podczas pr\u00f3by sparowania z tym urz\u0105dzeniem. Mo\u017ce to by\u0107 tymczasowa awaria lub urz\u0105dzenie mo\u017ce nie by\u0107 obecnie obs\u0142ugiwane: {error}", "unable_to_pair": "Nie mo\u017cna sparowa\u0107, spr\u00f3buj ponownie", "unknown_error": "Urz\u0105dzenie zg\u0142osi\u0142o nieznany b\u0142\u0105d. Parowanie nie powiod\u0142o si\u0119." }, diff --git a/homeassistant/components/icloud/translations/bg.json b/homeassistant/components/icloud/translations/bg.json index fb3d0a4ce48..92b2550d51d 100644 --- a/homeassistant/components/icloud/translations/bg.json +++ b/homeassistant/components/icloud/translations/bg.json @@ -9,11 +9,6 @@ "validate_verification_code": "\u041f\u043e\u0442\u0432\u044a\u0440\u0436\u0434\u0430\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u043a\u043e\u0434\u0430 \u0437\u0430 \u043f\u043e\u0442\u0432\u044a\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u043d\u0435 \u0431\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e" }, "step": { - "reauth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u0430" - } - }, "reauth_confirm": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u0430" diff --git a/homeassistant/components/icloud/translations/ca.json b/homeassistant/components/icloud/translations/ca.json index 187617b23e7..4205e80233c 100644 --- a/homeassistant/components/icloud/translations/ca.json +++ b/homeassistant/components/icloud/translations/ca.json @@ -11,13 +11,6 @@ "validate_verification_code": "No s'ha pogut verificar el codi de verificaci\u00f3, torna-ho a provar" }, "step": { - "reauth": { - "data": { - "password": "Contrasenya" - }, - "description": "La contrasenya introdu\u00efda anteriorment per a {username} ja no funciona. Actualitza la contrasenya per continuar utilitzant aquesta integraci\u00f3.", - "title": "Reautenticaci\u00f3 de la integraci\u00f3" - }, "reauth_confirm": { "data": { "password": "Contrasenya" diff --git a/homeassistant/components/icloud/translations/cs.json b/homeassistant/components/icloud/translations/cs.json index 72dc892d15f..5103dfcff7f 100644 --- a/homeassistant/components/icloud/translations/cs.json +++ b/homeassistant/components/icloud/translations/cs.json @@ -11,13 +11,6 @@ "validate_verification_code": "Nepoda\u0159ilo se ov\u011b\u0159it v\u00e1\u0161 ov\u011b\u0159ovac\u00ed k\u00f3d, vyberte d\u016fv\u011bryhodn\u00e9 za\u0159\u00edzen\u00ed a spus\u0165te ov\u011b\u0159en\u00ed znovu" }, "step": { - "reauth": { - "data": { - "password": "Heslo" - }, - "description": "Va\u0161e zadan\u00e9 heslo pro {username} ji\u017e nefunguje. Chcete-li tuto d\u00e1le integraci pou\u017e\u00edvat, aktualizujte sv\u00e9 heslo.", - "title": "Znovu ov\u011b\u0159it integraci" - }, "reauth_confirm": { "data": { "password": "Heslo" diff --git a/homeassistant/components/icloud/translations/de.json b/homeassistant/components/icloud/translations/de.json index 4d9c0d63d0c..db18c846934 100644 --- a/homeassistant/components/icloud/translations/de.json +++ b/homeassistant/components/icloud/translations/de.json @@ -11,13 +11,6 @@ "validate_verification_code": "Verifizierung des Verifizierungscodes fehlgeschlagen. W\u00e4hle ein vertrauensw\u00fcrdiges Ger\u00e4t aus und starte die Verifizierung erneut" }, "step": { - "reauth": { - "data": { - "password": "Passwort" - }, - "description": "Dein zuvor eingegebenes Passwort f\u00fcr {username} funktioniert nicht mehr. Aktualisiere dein Passwort, um diese Integration weiterhin zu verwenden.", - "title": "Integration erneut authentifizieren" - }, "reauth_confirm": { "data": { "password": "Passwort" diff --git a/homeassistant/components/icloud/translations/el.json b/homeassistant/components/icloud/translations/el.json index d47a4349648..32067114dd4 100644 --- a/homeassistant/components/icloud/translations/el.json +++ b/homeassistant/components/icloud/translations/el.json @@ -11,13 +11,6 @@ "validate_verification_code": "\u0391\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5 \u03b7 \u03b5\u03c0\u03b1\u03bb\u03ae\u03b8\u03b5\u03c5\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03cd \u03b5\u03c0\u03b1\u03bb\u03ae\u03b8\u03b5\u03c5\u03c3\u03b7\u03c2, \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac" }, "step": { - "reauth": { - "data": { - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" - }, - "description": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c0\u03bf\u03c5 \u03b5\u03af\u03c7\u03b1\u03c4\u03b5 \u03b5\u03b9\u03c3\u03ac\u03b3\u03b5\u03b9 \u03c0\u03c1\u03bf\u03b7\u03b3\u03bf\u03c5\u03bc\u03ad\u03bd\u03c9\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03bf {username} \u03b4\u03b5\u03bd \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03b5\u03af \u03c0\u03bb\u03ad\u03bf\u03bd. \u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03ae\u03c2 \u03c3\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7.", - "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" - }, "reauth_confirm": { "data": { "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" diff --git a/homeassistant/components/icloud/translations/en.json b/homeassistant/components/icloud/translations/en.json index 0052a858ede..65a8892c480 100644 --- a/homeassistant/components/icloud/translations/en.json +++ b/homeassistant/components/icloud/translations/en.json @@ -11,13 +11,6 @@ "validate_verification_code": "Failed to verify your verification code, try again" }, "step": { - "reauth": { - "data": { - "password": "Password" - }, - "description": "Your previously entered password for {username} is no longer working. Update your password to keep using this integration.", - "title": "Reauthenticate Integration" - }, "reauth_confirm": { "data": { "password": "Password" diff --git a/homeassistant/components/icloud/translations/es.json b/homeassistant/components/icloud/translations/es.json index ef1e6804469..9aa38aabd65 100644 --- a/homeassistant/components/icloud/translations/es.json +++ b/homeassistant/components/icloud/translations/es.json @@ -11,13 +11,6 @@ "validate_verification_code": "No se ha podido verificar el c\u00f3digo de verificaci\u00f3n, int\u00e9ntalo de nuevo" }, "step": { - "reauth": { - "data": { - "password": "Contrase\u00f1a" - }, - "description": "La contrase\u00f1a introducida anteriormente para {username} ya no funciona. Actualiza tu contrase\u00f1a para seguir usando esta integraci\u00f3n.", - "title": "Volver a autenticar la integraci\u00f3n" - }, "reauth_confirm": { "data": { "password": "Contrase\u00f1a" diff --git a/homeassistant/components/icloud/translations/et.json b/homeassistant/components/icloud/translations/et.json index 686205f3572..3fecdbefdcd 100644 --- a/homeassistant/components/icloud/translations/et.json +++ b/homeassistant/components/icloud/translations/et.json @@ -11,13 +11,6 @@ "validate_verification_code": "Tuvastuskoodi kinnitamine nurjus. Vali usaldusseade ja proovi uuesti" }, "step": { - "reauth": { - "data": { - "password": "Salas\u00f5na" - }, - "description": "Varem sisestatud salas\u00f5na kasutajale {username} ei t\u00f6\u00f6ta enam. Selle sidumise kasutamise j\u00e4tkamiseks v\u00e4rskenda oma salas\u00f5na.", - "title": "iCloudi tuvastusandmed" - }, "reauth_confirm": { "data": { "password": "Salas\u00f5na" diff --git a/homeassistant/components/icloud/translations/fr.json b/homeassistant/components/icloud/translations/fr.json index dec1bbdb34a..3d268fabe3b 100644 --- a/homeassistant/components/icloud/translations/fr.json +++ b/homeassistant/components/icloud/translations/fr.json @@ -11,13 +11,6 @@ "validate_verification_code": "Impossible de v\u00e9rifier votre code de v\u00e9rification, choisissez un appareil de confiance et recommencez la v\u00e9rification" }, "step": { - "reauth": { - "data": { - "password": "Mot de passe" - }, - "description": "Votre mot de passe pr\u00e9c\u00e9demment saisi pour {username} ne fonctionne plus. Mettez \u00e0 jour votre mot de passe pour continuer \u00e0 utiliser cette int\u00e9gration.", - "title": "R\u00e9-authentifier l'int\u00e9gration" - }, "reauth_confirm": { "data": { "password": "Mot de passe" diff --git a/homeassistant/components/icloud/translations/he.json b/homeassistant/components/icloud/translations/he.json index 95ed8490521..1bb30db4a0a 100644 --- a/homeassistant/components/icloud/translations/he.json +++ b/homeassistant/components/icloud/translations/he.json @@ -8,13 +8,6 @@ "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" }, "step": { - "reauth": { - "data": { - "password": "\u05e1\u05d9\u05e1\u05de\u05d4" - }, - "description": "\u05d4\u05e1\u05d9\u05e1\u05de\u05d4 \u05e9\u05d4\u05d6\u05e0\u05ea \u05d1\u05e2\u05d1\u05e8 \u05e2\u05d1\u05d5\u05e8 {username} \u05d0\u05d9\u05e0\u05d4 \u05e4\u05d5\u05e2\u05dc\u05ea \u05e2\u05d5\u05d3. \u05e2\u05d3\u05db\u05df \u05d0\u05ea \u05d4\u05e1\u05d9\u05e1\u05de\u05d4 \u05e9\u05dc\u05da \u05db\u05d3\u05d9 \u05dc\u05d4\u05de\u05e9\u05d9\u05da \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e9\u05d9\u05dc\u05d5\u05d1 \u05d6\u05d4.", - "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1" - }, "reauth_confirm": { "data": { "password": "\u05e1\u05d9\u05e1\u05de\u05d4" diff --git a/homeassistant/components/icloud/translations/hu.json b/homeassistant/components/icloud/translations/hu.json index 539b3740e24..177a3106a74 100644 --- a/homeassistant/components/icloud/translations/hu.json +++ b/homeassistant/components/icloud/translations/hu.json @@ -11,13 +11,6 @@ "validate_verification_code": "Nem siker\u00fclt hiteles\u00edteni az ellen\u0151rz\u0151 k\u00f3dot, k\u00e9rem, pr\u00f3b\u00e1lja meg \u00fajra" }, "step": { - "reauth": { - "data": { - "password": "Jelsz\u00f3" - }, - "description": "{username} kor\u00e1bban megadott jelszava m\u00e1r nem m\u0171k\u00f6dik. Az integr\u00e1ci\u00f3 haszn\u00e1lat\u00e1hoz friss\u00edtse jelszav\u00e1t.", - "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" - }, "reauth_confirm": { "data": { "password": "Jelsz\u00f3" diff --git a/homeassistant/components/icloud/translations/id.json b/homeassistant/components/icloud/translations/id.json index 1f6ed7c84c9..cd8a348ce98 100644 --- a/homeassistant/components/icloud/translations/id.json +++ b/homeassistant/components/icloud/translations/id.json @@ -11,13 +11,6 @@ "validate_verification_code": "Gagal memverifikasi kode verifikasi Anda, coba lagi" }, "step": { - "reauth": { - "data": { - "password": "Kata Sandi" - }, - "description": "Kata sandi yang Anda masukkan sebelumnya untuk {username} tidak lagi berfungsi. Perbarui kata sandi Anda untuk tetap menggunakan integrasi ini.", - "title": "Autentikasi Ulang Integrasi" - }, "reauth_confirm": { "data": { "password": "Kata Sandi" diff --git a/homeassistant/components/icloud/translations/it.json b/homeassistant/components/icloud/translations/it.json index 856ed30d767..ff91140e180 100644 --- a/homeassistant/components/icloud/translations/it.json +++ b/homeassistant/components/icloud/translations/it.json @@ -11,13 +11,6 @@ "validate_verification_code": "Impossibile verificare il codice di verifica, riprova" }, "step": { - "reauth": { - "data": { - "password": "Password" - }, - "description": "La password inserita in precedenza per {username} non funziona pi\u00f9. Aggiorna la tua password per continuare a utilizzare questa integrazione.", - "title": "Autentica nuovamente l'integrazione" - }, "reauth_confirm": { "data": { "password": "Password" diff --git a/homeassistant/components/icloud/translations/ja.json b/homeassistant/components/icloud/translations/ja.json index 4d9230ec150..1010a6c80f6 100644 --- a/homeassistant/components/icloud/translations/ja.json +++ b/homeassistant/components/icloud/translations/ja.json @@ -11,13 +11,6 @@ "validate_verification_code": "\u8a8d\u8a3c\u30b3\u30fc\u30c9\u306e\u78ba\u8a8d\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3001\u518d\u5ea6\u8a66\u3057\u304f\u3060\u3055\u3044\u3002" }, "step": { - "reauth": { - "data": { - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" - }, - "description": "\u4ee5\u524d\u306b\u5165\u529b\u3057\u305f {username} \u306e\u30d1\u30b9\u30ef\u30fc\u30c9\u306f\u4f7f\u3048\u306a\u304f\u306a\u308a\u307e\u3057\u305f\u3002\u3053\u306e\u7d71\u5408\u3092\u5f15\u304d\u7d9a\u304d\u4f7f\u7528\u3059\u308b\u306b\u306f\u3001\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u66f4\u65b0\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "\u7d71\u5408\u306e\u518d\u8a8d\u8a3c" - }, "reauth_confirm": { "data": { "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" diff --git a/homeassistant/components/icloud/translations/ka.json b/homeassistant/components/icloud/translations/ka.json index 950b4e2f327..949e95dec34 100644 --- a/homeassistant/components/icloud/translations/ka.json +++ b/homeassistant/components/icloud/translations/ka.json @@ -2,15 +2,6 @@ "config": { "abort": { "reauth_successful": "\u10e0\u10d0-\u10d0\u10d5\u10d7\u10d4\u10dc\u10d7\u10d8\u10d9\u10d0\u10ea\u10d8\u10d0 \u10d8\u10e7\u10dd \u10ec\u10d0\u10e0\u10db\u10d0\u10e2\u10d4\u10d1\u10e3\u10da\u10d8" - }, - "step": { - "reauth": { - "data": { - "password": "\u10de\u10d0\u10e0\u10dd\u10da\u10d8" - }, - "description": "\u10e8\u10d4\u10dc\u10d8 \u10d0\u10d3\u10e0\u10d4 \u10e8\u10d4\u10e7\u10d5\u10d0\u10dc\u10d8\u10da\u10d8 \u10de\u10d0\u10e0\u10dd\u10da\u10d8 {username} \u10d0\u10e6\u10d0\u10e0 \u10db\u10e3\u10e8\u10d0\u10dd\u10d1\u10e1. \u10d2\u10d0\u10dc\u10d0\u10d0\u10ee\u10da\u10d4\u10d7 \u10d7\u10e5\u10d5\u10d4\u10dc\u10d8 \u10de\u10d0\u10e0\u10dd\u10da\u10d8, \u10e0\u10dd\u10db \u10d2\u10d0\u10dc\u10d0\u10d2\u10e0\u10eb\u10dd\u10d7 \u10d8\u10dc\u10e2\u10d4\u10d2\u10e0\u10d0\u10ea\u10d8\u10d7 \u10e1\u10d0\u10e0\u10d2\u10d4\u10d1\u10da\u10dd\u10d1\u10d0.", - "title": "\u10e0\u10d4\u10d0\u10d5\u10d7\u10d4\u10dc\u10d7\u10d8\u10d9\u10d0\u10ea\u10d8\u10d8\u10e1 \u10d8\u10dc\u10e2\u10d4\u10d2\u10e0\u10d0\u10ea\u10d8\u10d0" - } } } } \ No newline at end of file diff --git a/homeassistant/components/icloud/translations/ko.json b/homeassistant/components/icloud/translations/ko.json index 52319b888ca..fcc89a25722 100644 --- a/homeassistant/components/icloud/translations/ko.json +++ b/homeassistant/components/icloud/translations/ko.json @@ -11,13 +11,6 @@ "validate_verification_code": "\uc778\uc99d \ucf54\ub4dc \ud655\uc778\uc5d0 \uc2e4\ud328\ud558\uc600\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694" }, "step": { - "reauth": { - "data": { - "password": "\ube44\ubc00\ubc88\ud638" - }, - "description": "\uc774\uc804\uc5d0 \uc785\ub825\ud55c {username}\uc5d0 \ub300\ud55c \ube44\ubc00\ubc88\ud638\uac00 \ub354 \uc774\uc0c1 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \uc774 \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uacc4\uc18d \uc0ac\uc6a9\ud558\ub824\uba74 \ube44\ubc00\ubc88\ud638\ub97c \uc5c5\ub370\uc774\ud2b8\ud574\uc8fc\uc138\uc694.", - "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" - }, "trusted_device": { "data": { "trusted_device": "\uc2e0\ub8b0\ud560 \uc218 \uc788\ub294 \uae30\uae30" diff --git a/homeassistant/components/icloud/translations/lb.json b/homeassistant/components/icloud/translations/lb.json index 6f0caa00528..77c9b0c7c30 100644 --- a/homeassistant/components/icloud/translations/lb.json +++ b/homeassistant/components/icloud/translations/lb.json @@ -11,13 +11,6 @@ "validate_verification_code": "Feeler beim iwwerpr\u00e9iwe vum Verifikatiouns Code, wielt ee vertrauten Apparat aus a start d'Iwwerpr\u00e9iwung nei" }, "step": { - "reauth": { - "data": { - "password": "Passwuert" - }, - "description": "D\u00e4in Passwuert fir {username} funktionn\u00e9iert net m\u00e9i. Aktualis\u00e9ier d\u00e4in Passwuert fir d\u00ebs Integratioun weider ze benotzen.", - "title": "Integratioun re-authentifiz\u00e9ieren" - }, "trusted_device": { "data": { "trusted_device": "Vertrauten Apparat" diff --git a/homeassistant/components/icloud/translations/nl.json b/homeassistant/components/icloud/translations/nl.json index 6c831c2f65b..e163942597c 100644 --- a/homeassistant/components/icloud/translations/nl.json +++ b/homeassistant/components/icloud/translations/nl.json @@ -11,13 +11,6 @@ "validate_verification_code": "Kan uw verificatiecode niet verifi\u00ebren, kies een vertrouwensapparaat en start de verificatie opnieuw" }, "step": { - "reauth": { - "data": { - "password": "Wachtwoord" - }, - "description": "Uw eerder ingevoerde wachtwoord voor {username} werkt niet meer. Update uw wachtwoord om deze integratie te blijven gebruiken.", - "title": "Integratie herauthenticeren" - }, "reauth_confirm": { "data": { "password": "Wachtwoord" diff --git a/homeassistant/components/icloud/translations/no.json b/homeassistant/components/icloud/translations/no.json index 1423f117126..83003dfd4ca 100644 --- a/homeassistant/components/icloud/translations/no.json +++ b/homeassistant/components/icloud/translations/no.json @@ -11,13 +11,6 @@ "validate_verification_code": "Kunne ikke bekrefte bekreftelseskoden, pr\u00f8v p\u00e5 nytt" }, "step": { - "reauth": { - "data": { - "password": "Passord" - }, - "description": "Ditt tidligere angitte passord for {username} fungerer ikke lenger. Oppdater passordet ditt for \u00e5 fortsette \u00e5 bruke denne integrasjonen.", - "title": "Godkjenne integrering p\u00e5 nytt" - }, "reauth_confirm": { "data": { "password": "Passord" diff --git a/homeassistant/components/icloud/translations/pl.json b/homeassistant/components/icloud/translations/pl.json index a726cd5a78d..303caa918d8 100644 --- a/homeassistant/components/icloud/translations/pl.json +++ b/homeassistant/components/icloud/translations/pl.json @@ -11,13 +11,6 @@ "validate_verification_code": "Nie uda\u0142o si\u0119 zweryfikowa\u0107 kodu weryfikacyjnego, spr\u00f3buj ponownie" }, "step": { - "reauth": { - "data": { - "password": "Has\u0142o" - }, - "description": "Twoje poprzednio wprowadzone has\u0142o dla {username} ju\u017c nie dzia\u0142a. Zaktualizuj swoje has\u0142o, aby nadal korzysta\u0107 z tej integracji.", - "title": "Ponownie uwierzytelnij integracj\u0119" - }, "reauth_confirm": { "data": { "password": "Has\u0142o" diff --git a/homeassistant/components/icloud/translations/pt-BR.json b/homeassistant/components/icloud/translations/pt-BR.json index 99f779e9ead..c431ce4f26a 100644 --- a/homeassistant/components/icloud/translations/pt-BR.json +++ b/homeassistant/components/icloud/translations/pt-BR.json @@ -11,13 +11,6 @@ "validate_verification_code": "Falha ao verificar seu c\u00f3digo de verifica\u00e7\u00e3o, tente novamente" }, "step": { - "reauth": { - "data": { - "password": "Senha" - }, - "description": "Sua senha inserida anteriormente para {username} n\u00e3o est\u00e1 mais funcionando. Atualize sua senha para continuar usando esta integra\u00e7\u00e3o.", - "title": "Reautenticar Integra\u00e7\u00e3o" - }, "reauth_confirm": { "data": { "password": "Senha" diff --git a/homeassistant/components/icloud/translations/pt.json b/homeassistant/components/icloud/translations/pt.json index da7711298fc..7eb0b4f4498 100644 --- a/homeassistant/components/icloud/translations/pt.json +++ b/homeassistant/components/icloud/translations/pt.json @@ -8,13 +8,6 @@ "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { - "reauth": { - "data": { - "password": "Palavra-passe" - }, - "description": "A sua palavra-passe anteriormente introduzida para {username} j\u00e1 n\u00e3o \u00e9 v\u00e1lida. Atualize sua palavra-passe para continuar a utilizar esta integra\u00e7\u00e3o.", - "title": "Reautenticar integra\u00e7\u00e3o" - }, "reauth_confirm": { "description": "Sua senha inserida anteriormente para {username} n\u00e3o est\u00e1 mais funcionando. Atualize sua senha para continuar usando esta integra\u00e7\u00e3o." }, diff --git a/homeassistant/components/icloud/translations/ru.json b/homeassistant/components/icloud/translations/ru.json index 17acded5703..5860f2a096c 100644 --- a/homeassistant/components/icloud/translations/ru.json +++ b/homeassistant/components/icloud/translations/ru.json @@ -11,13 +11,6 @@ "validate_verification_code": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043a\u043e\u0434 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437." }, "step": { - "reauth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" - }, - "description": "\u0420\u0430\u043d\u0435\u0435 \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f {username} \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0439\u0442\u0435\u0441\u044c, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e.", - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" - }, "reauth_confirm": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c" diff --git a/homeassistant/components/icloud/translations/sk.json b/homeassistant/components/icloud/translations/sk.json index af66dbba08d..a70d7f739d1 100644 --- a/homeassistant/components/icloud/translations/sk.json +++ b/homeassistant/components/icloud/translations/sk.json @@ -7,11 +7,6 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { - "reauth": { - "data": { - "password": "Heslo" - } - }, "reauth_confirm": { "data": { "password": "Heslo" diff --git a/homeassistant/components/icloud/translations/sv.json b/homeassistant/components/icloud/translations/sv.json index b76a5408319..b3fd0149cc4 100644 --- a/homeassistant/components/icloud/translations/sv.json +++ b/homeassistant/components/icloud/translations/sv.json @@ -11,13 +11,6 @@ "validate_verification_code": "Det gick inte att verifiera verifieringskoden, v\u00e4lj en betrodd enhet och starta verifieringen igen" }, "step": { - "reauth": { - "data": { - "password": "L\u00f6senord" - }, - "description": "Ditt tidigare angivna l\u00f6senord f\u00f6r {username} fungerar inte l\u00e4ngre. Uppdatera ditt l\u00f6senord f\u00f6r att forts\u00e4tta anv\u00e4nda denna integration.", - "title": "\u00c5terautenticera integration" - }, "reauth_confirm": { "data": { "password": "L\u00f6senord" diff --git a/homeassistant/components/icloud/translations/tr.json b/homeassistant/components/icloud/translations/tr.json index e220141f24d..35540db8526 100644 --- a/homeassistant/components/icloud/translations/tr.json +++ b/homeassistant/components/icloud/translations/tr.json @@ -11,13 +11,6 @@ "validate_verification_code": "Do\u011frulama kodunuz do\u011frulanamad\u0131, tekrar deneyin" }, "step": { - "reauth": { - "data": { - "password": "Parola" - }, - "description": "{username} i\u00e7in \u00f6nceden girdi\u011finiz \u015fifreniz art\u0131k \u00e7al\u0131\u015fm\u0131yor. Bu entegrasyonu kullanmaya devam etmek i\u00e7in \u015fifrenizi g\u00fcncelleyin.", - "title": "Entegrasyonu Yeniden Do\u011frula" - }, "reauth_confirm": { "data": { "password": "Parola" diff --git a/homeassistant/components/icloud/translations/uk.json b/homeassistant/components/icloud/translations/uk.json index ac65157f050..cd416b8058c 100644 --- a/homeassistant/components/icloud/translations/uk.json +++ b/homeassistant/components/icloud/translations/uk.json @@ -11,13 +11,6 @@ "validate_verification_code": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0435\u0440\u0435\u0432\u0456\u0440\u0438\u0442\u0438 \u043a\u043e\u0434 \u043f\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0436\u0435\u043d\u043d\u044f, \u0432\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0434\u043e\u0432\u0456\u0440\u0435\u043d\u0438\u0439 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0442\u0430 \u043f\u043e\u0447\u043d\u0456\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0443 \u0437\u043d\u043e\u0432\u0443." }, "step": { - "reauth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" - }, - "description": "\u0420\u0430\u043d\u0456\u0448\u0435 \u0432\u0432\u0435\u0434\u0435\u043d\u0438\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f {username} \u0431\u0456\u043b\u044c\u0448\u0435 \u043d\u0435 \u043f\u0440\u0430\u0446\u044e\u0454. \u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0439\u0442\u0435\u0441\u044c, \u0449\u043e\u0431 \u043f\u0440\u043e\u0434\u043e\u0432\u0436\u0438\u0442\u0438 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438 \u0446\u044e \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u044e.", - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0432\u0430\u0442\u0438 \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u044e" - }, "trusted_device": { "data": { "trusted_device": "\u0414\u043e\u0432\u0456\u0440\u0435\u043d\u0438\u0439 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439" diff --git a/homeassistant/components/icloud/translations/zh-Hans.json b/homeassistant/components/icloud/translations/zh-Hans.json index 96b16faec97..bd57ef72fb7 100644 --- a/homeassistant/components/icloud/translations/zh-Hans.json +++ b/homeassistant/components/icloud/translations/zh-Hans.json @@ -8,11 +8,6 @@ "validate_verification_code": "\u65e0\u6cd5\u9a8c\u8bc1\u9a8c\u8bc1\u7801\uff0c\u8bf7\u9009\u62e9\u53d7\u4fe1\u4efb\u7684\u8bbe\u5907\u5e76\u91cd\u65b0\u5f00\u59cb\u9a8c\u8bc1" }, "step": { - "reauth": { - "data": { - "password": "\u5bc6\u7801" - } - }, "trusted_device": { "data": { "trusted_device": "\u53d7\u4fe1\u4efb\u7684\u8bbe\u5907" diff --git a/homeassistant/components/icloud/translations/zh-Hant.json b/homeassistant/components/icloud/translations/zh-Hant.json index 91f14636dd2..5231889afdb 100644 --- a/homeassistant/components/icloud/translations/zh-Hant.json +++ b/homeassistant/components/icloud/translations/zh-Hant.json @@ -11,13 +11,6 @@ "validate_verification_code": "\u9a57\u8b49\u8f38\u5165\u9a57\u8b49\u78bc\u5931\u6557\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002" }, "step": { - "reauth": { - "data": { - "password": "\u5bc6\u78bc" - }, - "description": "\u5148\u524d\u91dd\u5c0d\u5e33\u865f {username} \u6240\u8f38\u5165\u7684\u5bc6\u78bc\u5df2\u5931\u6548\u3002\u8acb\u66f4\u65b0\u5bc6\u78bc\u4ee5\u4f7f\u7528\u6b64\u6574\u5408\u3002", - "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" - }, "reauth_confirm": { "data": { "password": "\u5bc6\u78bc" diff --git a/homeassistant/components/knx/translations/bg.json b/homeassistant/components/knx/translations/bg.json index b4dc6afc1b8..9ce61dfc316 100644 --- a/homeassistant/components/knx/translations/bg.json +++ b/homeassistant/components/knx/translations/bg.json @@ -28,12 +28,6 @@ "local_ip": "\u041e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0437\u0430 \u0434\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435." } }, - "secure_manual": { - "data": { - "user_id": "\u0418\u0414 \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f", - "user_password": "\u041f\u0430\u0440\u043e\u043b\u0430 \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f" - } - }, "secure_tunnel_manual": { "data": { "user_id": "ID \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f", @@ -48,14 +42,6 @@ "invalid_ip_address": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d IPv4 \u0430\u0434\u0440\u0435\u0441." }, "step": { - "init": { - "data": { - "local_ip": "\u041b\u043e\u043a\u0430\u043b\u0435\u043d IP \u0430\u0434\u0440\u0435\u0441 (\u043e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0430\u043a\u043e \u043d\u0435 \u0441\u0442\u0435 \u0441\u0438\u0433\u0443\u0440\u043d\u0438)" - }, - "data_description": { - "local_ip": "\u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0439\u0442\u0435 `0.0.0.0` \u0437\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435." - } - }, "manual_tunnel": { "data": { "host": "\u0425\u043e\u0441\u0442", @@ -76,11 +62,6 @@ } }, "tunnel": { - "data": { - "host": "\u0425\u043e\u0441\u0442", - "port": "\u041f\u043e\u0440\u0442", - "tunneling_type": "KNX \u0442\u0443\u043d\u0435\u043b\u0435\u043d \u0442\u0438\u043f" - }, "description": "\u041c\u043e\u043b\u044f, \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0448\u043b\u044e\u0437 \u043e\u0442 \u0441\u043f\u0438\u0441\u044a\u043a\u0430." } } diff --git a/homeassistant/components/knx/translations/ca.json b/homeassistant/components/knx/translations/ca.json index 5b984ff4987..c7ebe728b9f 100644 --- a/homeassistant/components/knx/translations/ca.json +++ b/homeassistant/components/knx/translations/ca.json @@ -60,19 +60,6 @@ }, "description": "Introdueix la informaci\u00f3 del teu fitxer `.knxkeys`." }, - "secure_manual": { - "data": { - "device_authentication": "Contrasenya d'autenticaci\u00f3 del dispositiu", - "user_id": "ID d'usuari", - "user_password": "Contrasenya d'usuari" - }, - "data_description": { - "device_authentication": "S'estableix al panell 'IP' de la interf\u00edcie d'ETS.", - "user_id": "Sovint \u00e9s el n\u00famero del t\u00fanel +1. Per tant, 'T\u00fanel 2' tindria l'ID d'usuari '3'.", - "user_password": "Contrasenya per a la connexi\u00f3 t\u00fanel espec\u00edfica configurada al panell 'Propietats' del t\u00fanel a ETS." - }, - "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure)." - }, "secure_tunnel_manual": { "data": { "device_authentication": "Contrasenya d'autenticaci\u00f3 del dispositiu", @@ -90,7 +77,6 @@ "description": "Selecciona com vols configurar KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Utilitza un fitxer `.knxkeys` que contingui les claus de seguretat IP (IP Secure)", - "secure_manual": "Configura manualment les claus de seguretat IP (IP Secure)", "secure_tunnel_manual": "Configura manualment les claus de seguretat IP (IP Secure)" } }, @@ -99,12 +85,6 @@ "gateway": "Connexi\u00f3 t\u00fanel KNX" }, "description": "Selecciona una passarel\u00b7la d'enlla\u00e7 de la llista." - }, - "type": { - "data": { - "connection_type": "Tipus de connexi\u00f3 KNX" - }, - "description": "Introdueix el tipus de connexi\u00f3 a utilitzar per a la connexi\u00f3 KNX.\n AUTOM\u00c0TICA: la integraci\u00f3 s'encarrega de la connectivitat al bus KNX realitzant una exploraci\u00f3 de la passarel\u00b7la.\n T\u00daNEL: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant un t\u00fanel.\n ENCAMINAMENT: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant l'encaminament." } } }, @@ -135,25 +115,6 @@ }, "description": "Introdueix el tipus de connexi\u00f3 a utilitzar per a la connexi\u00f3 KNX.\n AUTOM\u00c0TICA: la integraci\u00f3 s'encarrega de la connectivitat al bus KNX realitzant una exploraci\u00f3 de la passarel\u00b7la.\n T\u00daNEL: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant un t\u00fanel.\n ENCAMINAMENT: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant l'encaminament." }, - "init": { - "data": { - "connection_type": "Tipus de connexi\u00f3 KNX", - "individual_address": "Adre\u00e7a individual predeterminada", - "local_ip": "IP local de Home Assistant", - "multicast_group": "Grup multidifusi\u00f3", - "multicast_port": "Port multidifusi\u00f3", - "rate_limit": "Freq\u00fc\u00e8ncia m\u00e0xima", - "state_updater": "Actualitzador d'estat" - }, - "data_description": { - "individual_address": "Adre\u00e7a KNX per utilitzar amb Home Assistant, p. ex. `0.0.4`", - "local_ip": "Utilitza `0.0.0.0` per al descobriment autom\u00e0tic.", - "multicast_group": "Utilitzada per a l'encaminament i el descobriment. Per defecte: `224.0.23.12`", - "multicast_port": "Utilitzat per a l'encaminament i el descobriment. Per defecte: `3671`", - "rate_limit": "Telegrames de sortida m\u00e0xims per segon.\nRecomanat: de 20 a 40", - "state_updater": "Configuraci\u00f3 predeterminadament per llegir els estats del bus KNX. Si est\u00e0 desactivat, Home Assistant no obtindr\u00e0 activament els estats del bus KNX. Les opcions d'entitat `sync_state` poden substituir-ho." - } - }, "manual_tunnel": { "data": { "host": "Amfitri\u00f3", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "Connexi\u00f3 t\u00fanel KNX", - "host": "Amfitri\u00f3", - "port": "Port", - "tunneling_type": "Tipus de t\u00fanel KNX" - }, - "data_description": { - "host": "Adre\u00e7a IP del dispositiu de tunelitzaci\u00f3 KNX/IP.", - "port": "Port del dispositiu de tunelitzaci\u00f3 KNX/IP." + "gateway": "Connexi\u00f3 t\u00fanel KNX" }, "description": "Selecciona una passarel\u00b7la d'enlla\u00e7 de la llista." } diff --git a/homeassistant/components/knx/translations/cs.json b/homeassistant/components/knx/translations/cs.json index 90c988aaeac..e9e0a0d87f6 100644 --- a/homeassistant/components/knx/translations/cs.json +++ b/homeassistant/components/knx/translations/cs.json @@ -14,15 +14,5 @@ } } } - }, - "options": { - "step": { - "tunnel": { - "data": { - "host": "Hostitel", - "port": "Port" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/knx/translations/de.json b/homeassistant/components/knx/translations/de.json index 2d624346e00..f6a4e6a9616 100644 --- a/homeassistant/components/knx/translations/de.json +++ b/homeassistant/components/knx/translations/de.json @@ -60,19 +60,6 @@ }, "description": "Bitte gib die Informationen f\u00fcr deine `.knxkeys`-Datei ein." }, - "secure_manual": { - "data": { - "device_authentication": "Ger\u00e4te-Authentifizierungscode", - "user_id": "Benutzer-ID", - "user_password": "Benutzer-Passwort" - }, - "data_description": { - "device_authentication": "Dies wird im Feld \"IP\" der Schnittstelle in ETS eingestellt.", - "user_id": "Dies ist oft die Tunnelnummer +1. \u201eTunnel 2\u201c h\u00e4tte also die Benutzer-ID \u201e3\u201c.", - "user_password": "Passwort f\u00fcr die spezifische Tunnelverbindung, die im Bereich \u201eEigenschaften\u201c des Tunnels in ETS festgelegt wurde." - }, - "description": "Bitte gib deine IP-Secure Informationen ein." - }, "secure_tunnel_manual": { "data": { "device_authentication": "Ger\u00e4te-Authentifizierungscode", @@ -90,7 +77,6 @@ "description": "W\u00e4hle aus, wie du KNX/IP-Secure konfigurieren m\u00f6chtest.", "menu_options": { "secure_knxkeys": "Verwende eine `.knxkeys`-Datei, die IP-Secure-Schl\u00fcssel enth\u00e4lt", - "secure_manual": "IP-Secure Schl\u00fcssel manuell konfigurieren", "secure_tunnel_manual": "IP-Secure Schl\u00fcssel manuell konfigurieren" } }, @@ -99,12 +85,6 @@ "gateway": "KNX Tunnel Verbindung" }, "description": "Bitte w\u00e4hle eine Schnittstelle aus der Liste aus." - }, - "type": { - "data": { - "connection_type": "KNX-Verbindungstyp" - }, - "description": "Bitte gib den Verbindungstyp ein, den wir f\u00fcr deine KNX-Verbindung verwenden sollen. \n AUTOMATISCH - Die Integration k\u00fcmmert sich um die Verbindung zu deinem KNX Bus, indem sie einen Gateway-Scan durchf\u00fchrt. \n TUNNELING - Die Integration stellt die Verbindung zu deinem KNX Bus \u00fcber Tunneling her. \n ROUTING - Die Integration stellt die Verbindung zu deinem KNX-Bus \u00fcber Routing her." } } }, @@ -135,25 +115,6 @@ }, "description": "Bitte gib den Verbindungstyp ein, den wir f\u00fcr deine KNX-Verbindung verwenden sollen. \n AUTOMATISCH - Die Integration k\u00fcmmert sich um die Verbindung zu deinem KNX Bus, indem sie einen Gateway-Scan durchf\u00fchrt. \n TUNNELING - Die Integration stellt die Verbindung zu deinem KNX Bus \u00fcber Tunneling her. \n ROUTING - Die Integration stellt die Verbindung zu deinem KNX-Bus \u00fcber Routing her." }, - "init": { - "data": { - "connection_type": "KNX-Verbindungstyp", - "individual_address": "Standard physikalische Adresse", - "local_ip": "Lokale IP von Home Assistant", - "multicast_group": "Multicast-Gruppe", - "multicast_port": "Multicast-Port", - "rate_limit": "Telegrammdrossel", - "state_updater": "Status-Updater" - }, - "data_description": { - "individual_address": "Physikalische Adresse, die von Home Assistant verwendet werden soll, z.\u00a0B. \u201e0.0.4\u201c.", - "local_ip": "Verwende \"0.0.0.0\" f\u00fcr die automatische Erkennung.", - "multicast_group": "Wird f\u00fcr Routing und Netzwerkerkennung verwendet. Standard: `224.0.23.12`", - "multicast_port": "Wird f\u00fcr Routing und Netzwerkerkennung verwendet. Standard: \u201e3671\u201c.", - "rate_limit": "Maximal gesendete Telegramme pro Sekunde.\nEmpfohlen: 20 bis 40", - "state_updater": "Standardeinstellung f\u00fcr das Lesen von Zust\u00e4nden aus dem KNX-Bus. Wenn diese Option deaktiviert ist, wird der Home Assistant den Zustand der Entit\u00e4ten nicht aktiv vom KNX-Bus abrufen. Kann durch die Entity-Optionen `sync_state` au\u00dfer Kraft gesetzt werden." - } - }, "manual_tunnel": { "data": { "host": "Host", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "KNX Tunnel Verbindung", - "host": "Host", - "port": "Port", - "tunneling_type": "KNX Tunneling Typ" - }, - "data_description": { - "host": "IP-Adresse der KNX/IP-Tunneling Schnittstelle.", - "port": "Port der KNX/IP-Tunneling Schnittstelle." + "gateway": "KNX Tunnel Verbindung" }, "description": "Bitte w\u00e4hle eine Schnittstelle aus der Liste aus." } diff --git a/homeassistant/components/knx/translations/el.json b/homeassistant/components/knx/translations/el.json index a0753789ca9..660ce9218f9 100644 --- a/homeassistant/components/knx/translations/el.json +++ b/homeassistant/components/knx/translations/el.json @@ -60,19 +60,6 @@ }, "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03c3\u03b1\u03c2." }, - "secure_manual": { - "data": { - "device_authentication": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", - "user_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", - "user_password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" - }, - "data_description": { - "device_authentication": "\u0391\u03c5\u03c4\u03cc \u03bf\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf\u03bd \u03c0\u03af\u03bd\u03b1\u03ba\u03b1 \u00abIP\u00bb \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae\u03c2 \u03c3\u03c4\u03bf ETS.", - "user_id": "\u0391\u03c5\u03c4\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03c7\u03bd\u03ac \u03c4\u03bf \u03bd\u03bf\u03cd\u03bc\u03b5\u03c1\u03bf +1 \u03c4\u03b7\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2. \u0388\u03c4\u03c3\u03b9, \u03b7 '\u03a3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1 2' \u03b8\u03b1 \u03ad\u03c7\u03b5\u03b9 User-ID '3'.", - "user_password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 \u03c0\u03bf\u03c5 \u03ad\u03c7\u03b5\u03b9 \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af \u03c3\u03c4\u03bf\u03bd \u03c0\u03af\u03bd\u03b1\u03ba\u03b1 \"\u0399\u03b4\u03b9\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\" \u03c4\u03b7\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 \u03c3\u03c4\u03bf ETS." - }, - "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 IP secure." - }, "secure_tunnel_manual": { "data": { "device_authentication": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", @@ -90,7 +77,6 @@ "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03ce\u03c2 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf IP Secure.", "menu_options": { "secure_knxkeys": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf `.knxkeys` \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ac \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP", - "secure_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 IP secure \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b1", "secure_tunnel_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ce\u03bd \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP \u03bc\u03b5 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf \u03c4\u03c1\u03cc\u03c0\u03bf" } }, @@ -99,12 +85,6 @@ "gateway": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c0\u03cd\u03bb\u03b7 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03bb\u03af\u03c3\u03c4\u03b1." - }, - "type": { - "data": { - "connection_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 KNX" - }, - "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03cd\u03c0\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c0\u03bf\u03c5 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03bf\u03c5\u03bc\u03b5 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 KNX. \n \u0391\u03a5\u03a4\u039f\u039c\u0391\u03a4\u0397 - \u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c6\u03c1\u03bf\u03bd\u03c4\u03af\u03b6\u03b5\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03bd\u03b4\u03b5\u03c3\u03b9\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1 \u03bc\u03b5 \u03c4\u03bf\u03bd \u03b4\u03af\u03b1\u03c5\u03bb\u03bf KNX \u03b5\u03ba\u03c4\u03b5\u03bb\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7 \u03c0\u03cd\u03bb\u03b7\u03c2. \n TUNNELING - \u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b8\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03c3\u03c4\u03bf \u03b4\u03af\u03b1\u03c5\u03bb\u03cc \u03c3\u03b1\u03c2 KNX \u03bc\u03ad\u03c3\u03c9 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2. \n ROUTING - \u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b8\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03c3\u03c4\u03bf \u03b4\u03af\u03b1\u03c5\u03bb\u03cc \u03c3\u03b1\u03c2 KNX \u03bc\u03ad\u03c3\u03c9 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7\u03c2." } } }, @@ -135,25 +115,6 @@ }, "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03cd\u03c0\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c0\u03bf\u03c5 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03bf\u03c5\u03bc\u03b5 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03ae \u03c3\u03b1\u03c2 KNX.\n AUTOMATIC - \u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c6\u03c1\u03bf\u03bd\u03c4\u03af\u03b6\u03b5\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03bd\u03b4\u03b5\u03c3\u03b9\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1 \u03bc\u03b5 \u03c4\u03bf KNX Bus \u03c3\u03b1\u03c2 \u03b5\u03ba\u03c4\u03b5\u03bb\u03ce\u03bd\u03c4\u03b1\u03c2 \u03bc\u03b9\u03b1 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7 \u03c0\u03cd\u03bb\u03b7\u03c2.\n TUNNELING - \u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b8\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03bc\u03b5 \u03c4\u03bf \u03b4\u03af\u03b1\u03c5\u03bb\u03bf KNX \u03bc\u03ad\u03c3\u03c9 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2.\n \u0394\u03a1\u039f\u039c\u039f\u039b\u039f\u0393\u0397\u03a3\u0397 - \u0397 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b8\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03bc\u03b5 \u03c4\u03bf \u03b4\u03af\u03b1\u03c5\u03bb\u03bf KNX \u03bc\u03ad\u03c3\u03c9 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7\u03c2." }, - "init": { - "data": { - "connection_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 KNX", - "individual_address": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03c6\u03c5\u03c3\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7", - "local_ip": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae IP \u03c4\u03bf\u03c5 Home Assistant (\u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 0.0.0.0.0 \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7)", - "multicast_group": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03bd\u03bf\u03bc\u03ae\u03c2 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7", - "multicast_port": "\u0398\u03cd\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03bd\u03bf\u03bc\u03ae\u03c2 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7", - "rate_limit": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03b1 \u03b5\u03be\u03b5\u03c1\u03c7\u03cc\u03bc\u03b5\u03bd\u03b1 \u03c4\u03b7\u03bb\u03b5\u03b3\u03c1\u03b1\u03c6\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b1\u03bd\u03ac \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03bf", - "state_updater": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b1\u03b8\u03bf\u03bb\u03b9\u03ba\u03ac \u03c4\u03b9\u03c2 \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2 \u03b1\u03c0\u03cc \u03c4\u03bf KNX Bus" - }, - "data_description": { - "individual_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant, \u03c0.\u03c7. `0.0.4`.", - "local_ip": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 `0.0.0.0.0` \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7.", - "multicast_group": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7. \u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae: `224.0.23.12`", - "multicast_port": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7. \u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae: `3671`", - "rate_limit": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03b1 \u03b5\u03be\u03b5\u03c1\u03c7\u03cc\u03bc\u03b5\u03bd\u03b1 \u03c4\u03b7\u03bb\u03b5\u03b3\u03c1\u03b1\u03c6\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b1\u03bd\u03ac \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03bf.\n \u03a0\u03c1\u03bf\u03c4\u03b5\u03af\u03bd\u03b5\u03c4\u03b1\u03b9: 20 \u03ad\u03c9\u03c2 40", - "state_updater": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2 \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03c9\u03bd \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b4\u03af\u03b1\u03c5\u03bb\u03bf KNX. \u038c\u03c4\u03b1\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf, \u03c4\u03bf Home Assistant \u03b4\u03b5\u03bd \u03b8\u03b1 \u03b1\u03bd\u03b1\u03ba\u03c4\u03ac \u03b5\u03bd\u03b5\u03c1\u03b3\u03ac \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 \u03b1\u03c0\u03cc \u03c4\u03bf KNX Bus, \u03bf\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 `sync_state` \u03b4\u03b5\u03bd \u03b8\u03b1 \u03ad\u03c7\u03bf\u03c5\u03bd \u03ba\u03b1\u03bc\u03af\u03b1 \u03b5\u03c0\u03af\u03b4\u03c1\u03b1\u03c3\u03b7." - } - }, "manual_tunnel": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX", - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "port": "\u0398\u03cd\u03c1\u03b1", - "tunneling_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" - }, - "data_description": { - "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP.", - "port": "\u0398\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP." + "gateway": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c0\u03cd\u03bb\u03b7 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03bb\u03af\u03c3\u03c4\u03b1." } diff --git a/homeassistant/components/knx/translations/en.json b/homeassistant/components/knx/translations/en.json index 920cd21b1cc..c45c98b070a 100644 --- a/homeassistant/components/knx/translations/en.json +++ b/homeassistant/components/knx/translations/en.json @@ -60,19 +60,6 @@ }, "description": "Please enter the information for your `.knxkeys` file." }, - "secure_manual": { - "data": { - "device_authentication": "Device authentication password", - "user_id": "User ID", - "user_password": "User password" - }, - "data_description": { - "device_authentication": "This is set in the 'IP' panel of the interface in ETS.", - "user_id": "This is often tunnel number +1. So 'Tunnel 2' would have User-ID '3'.", - "user_password": "Password for the specific tunnel connection set in the 'Properties' panel of the tunnel in ETS." - }, - "description": "Please enter your IP secure information." - }, "secure_tunnel_manual": { "data": { "device_authentication": "Device authentication password", @@ -90,7 +77,6 @@ "description": "Select how you want to configure KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", - "secure_manual": "Configure IP secure keys manually", "secure_tunnel_manual": "Configure IP secure keys manually" } }, @@ -99,12 +85,6 @@ "gateway": "KNX Tunnel Connection" }, "description": "Please select a gateway from the list." - }, - "type": { - "data": { - "connection_type": "KNX Connection Type" - }, - "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing." } } }, @@ -135,25 +115,6 @@ }, "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing." }, - "init": { - "data": { - "connection_type": "KNX Connection Type", - "individual_address": "Default individual address", - "local_ip": "Local IP of Home Assistant", - "multicast_group": "Multicast group", - "multicast_port": "Multicast port", - "rate_limit": "Rate limit", - "state_updater": "State updater" - }, - "data_description": { - "individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`", - "local_ip": "Use `0.0.0.0` for auto-discovery.", - "multicast_group": "Used for routing and discovery. Default: `224.0.23.12`", - "multicast_port": "Used for routing and discovery. Default: `3671`", - "rate_limit": "Maximum outgoing telegrams per second.\nRecommended: 20 to 40", - "state_updater": "Set default for reading states from the KNX Bus. When disabled, Home Assistant will not actively retrieve entity states from the KNX Bus. Can be overridden by `sync_state` entity options." - } - }, "manual_tunnel": { "data": { "host": "Host", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "KNX Tunnel Connection", - "host": "Host", - "port": "Port", - "tunneling_type": "KNX Tunneling Type" - }, - "data_description": { - "host": "IP address of the KNX/IP tunneling device.", - "port": "Port of the KNX/IP tunneling device." + "gateway": "KNX Tunnel Connection" }, "description": "Please select a gateway from the list." } diff --git a/homeassistant/components/knx/translations/es.json b/homeassistant/components/knx/translations/es.json index df422c28208..d11b0446701 100644 --- a/homeassistant/components/knx/translations/es.json +++ b/homeassistant/components/knx/translations/es.json @@ -60,19 +60,6 @@ }, "description": "Por favor, introduce la informaci\u00f3n de tu archivo `.knxkeys`." }, - "secure_manual": { - "data": { - "device_authentication": "Contrase\u00f1a de autenticaci\u00f3n del dispositivo", - "user_id": "ID de usuario", - "user_password": "Contrase\u00f1a de usuario" - }, - "data_description": { - "device_authentication": "Esto se configura en el panel 'IP' de la interfaz en ETS.", - "user_id": "Este suele ser el n\u00famero de t\u00fanel +1. Por tanto, 'T\u00fanel 2' tendr\u00eda ID de usuario '3'.", - "user_password": "Contrase\u00f1a para la conexi\u00f3n de t\u00fanel espec\u00edfica establecida en el panel 'Propiedades' del t\u00fanel en ETS." - }, - "description": "Por favor, introduce tu informaci\u00f3n de IP segura." - }, "secure_tunnel_manual": { "data": { "device_authentication": "Contrase\u00f1a de autenticaci\u00f3n del dispositivo", @@ -90,7 +77,6 @@ "description": "Selecciona c\u00f3mo quieres configurar KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Utilizar un archivo `.knxkeys` que contenga claves seguras de IP", - "secure_manual": "Configurar claves seguras de IP manualmente", "secure_tunnel_manual": "Configurar claves seguras de IP manualmente" } }, @@ -99,12 +85,6 @@ "gateway": "Conexi\u00f3n de t\u00fanel KNX" }, "description": "Por favor, selecciona una puerta de enlace de la lista." - }, - "type": { - "data": { - "connection_type": "Tipo de conexi\u00f3n KNX" - }, - "description": "Por favor, introduce el tipo de conexi\u00f3n que debemos usar para tu conexi\u00f3n KNX.\n AUTOM\u00c1TICO: la integraci\u00f3n se encarga de la conectividad con tu bus KNX mediante la realizaci\u00f3n de un escaneo de la puerta de enlace.\n T\u00daNELES: la integraci\u00f3n se conectar\u00e1 a tu bus KNX a trav\u00e9s de t\u00faneles.\n ENRUTAMIENTO: la integraci\u00f3n se conectar\u00e1 a tu bus KNX a trav\u00e9s del enrutamiento." } } }, @@ -135,25 +115,6 @@ }, "description": "Por favor, introduce el tipo de conexi\u00f3n que debemos usar para tu conexi\u00f3n KNX.\n AUTOM\u00c1TICO: la integraci\u00f3n se encarga de la conectividad a tu bus KNX mediante la realizaci\u00f3n de un escaneo de la puerta de enlace.\n T\u00daNELES: la integraci\u00f3n se conectar\u00e1 a tu bus KNX a trav\u00e9s de t\u00faneles.\n ENRUTAMIENTO: la integraci\u00f3n se conectar\u00e1 a su tus KNX a trav\u00e9s del enrutamiento." }, - "init": { - "data": { - "connection_type": "Tipo de conexi\u00f3n KNX", - "individual_address": "Direcci\u00f3n individual predeterminada", - "local_ip": "IP local de Home Assistant", - "multicast_group": "Grupo multicast", - "multicast_port": "Puerto multicast", - "rate_limit": "Frecuencia m\u00e1xima", - "state_updater": "Actualizador de estado" - }, - "data_description": { - "individual_address": "Direcci\u00f3n KNX que usar\u00e1 Home Assistant, por ejemplo, `0.0.4`", - "local_ip": "Usar `0.0.0.0` para el descubrimiento autom\u00e1tico.", - "multicast_group": "Se utiliza para el enrutamiento y el descubrimiento. Predeterminado: `224.0.23.12`", - "multicast_port": "Se utiliza para el enrutamiento y el descubrimiento. Predeterminado: `3671`", - "rate_limit": "N\u00famero m\u00e1ximo de telegramas salientes por segundo.\nRecomendado: 20 a 40", - "state_updater": "Establece los valores predeterminados para leer los estados del bus KNX. Cuando est\u00e1 deshabilitado, Home Assistant no recuperar\u00e1 activamente los estados de entidad del bus KNX. Puede ser anulado por las opciones de entidad `sync_state`." - } - }, "manual_tunnel": { "data": { "host": "Host", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "Conexi\u00f3n de t\u00fanel KNX", - "host": "Host", - "port": "Puerto", - "tunneling_type": "Tipo de t\u00fanel KNX" - }, - "data_description": { - "host": "Direcci\u00f3n IP del dispositivo de t\u00fanel KNX/IP.", - "port": "Puerto del dispositivo de t\u00fanel KNX/IP." + "gateway": "Conexi\u00f3n de t\u00fanel KNX" }, "description": "Por favor, selecciona una puerta de enlace de la lista." } diff --git a/homeassistant/components/knx/translations/et.json b/homeassistant/components/knx/translations/et.json index 3ed43438021..7693a38106d 100644 --- a/homeassistant/components/knx/translations/et.json +++ b/homeassistant/components/knx/translations/et.json @@ -60,19 +60,6 @@ }, "description": "Sisesta oma `.knxkeys` faili teave." }, - "secure_manual": { - "data": { - "device_authentication": "Seadme autentimise parool", - "user_id": "Kasutaja ID", - "user_password": "Kasutaja salas\u00f5na" - }, - "data_description": { - "device_authentication": "See m\u00e4\u00e4ratakse ETSi liidese IP-paneelil.", - "user_id": "See on sageli tunneli number +1. Nii et tunnel 2 oleks kasutaja ID-ga 3.", - "user_password": "Konkreetse tunneli\u00fchenduse parool, mis on m\u00e4\u00e4ratud ETS-i tunneli paneelil \u201eAtribuudid\u201d." - }, - "description": "Sisesta IP Secure teave." - }, "secure_tunnel_manual": { "data": { "device_authentication": "Seadme autentimise parool", @@ -90,7 +77,6 @@ "description": "Vali kuidas soovid KNX/IP Secure'i seadistada.", "menu_options": { "secure_knxkeys": "Kasuta knxkeys fail, mis sisaldab IP Secure teavet.", - "secure_manual": "IP Secure v\u00f5tmete k\u00e4sitsi seadistamine", "secure_tunnel_manual": "IP Secure v\u00f5tmete k\u00e4sitsi seadistamine" } }, @@ -99,12 +85,6 @@ "gateway": "KNX tunneli \u00fchendus" }, "description": "Vali loendist l\u00fc\u00fcs." - }, - "type": { - "data": { - "connection_type": "KNX \u00fchenduse t\u00fc\u00fcp" - }, - "description": "Sisesta \u00fchenduse t\u00fc\u00fcp, mida kasutada KNX-\u00fchenduse jaoks. \n AUTOMAATNE \u2013 sidumine hoolitseb KNX siini \u00fchenduvuse eest, tehes l\u00fc\u00fcsikontrolli. \n TUNNELING - sidumine \u00fchendub KNX siiniga tunneli kaudu. \n MARSRUUTIMINE \u2013 sidumine \u00fchendub marsruudi kaudu KNX siiniga." } } }, @@ -135,25 +115,6 @@ }, "description": "Sisesta \u00fchenduse t\u00fc\u00fcp, mida kasutada KNX-\u00fchenduse jaoks. \n AUTOMAATNE \u2013 sidumine hoolitseb KNX siini \u00fchenduvuse eest, tehes l\u00fc\u00fcsikontrolli. \n TUNNELING - sidumine \u00fchendub KNX siiniga tunneli kaudu. \n MARSRUUTIMINE \u2013 sidumine \u00fchendub marsruudi kaudu KNX siiniga." }, - "init": { - "data": { - "connection_type": "KNX \u00fchenduse t\u00fc\u00fcp", - "individual_address": "Vaikimisi individuaalne aadress", - "local_ip": "Home Assistanti kohalik IP aadress", - "multicast_group": "Multicast grupp", - "multicast_port": "Mulicasti port", - "rate_limit": "Teavituste m\u00e4\u00e4r", - "state_updater": "Oleku uuendaja" - }, - "data_description": { - "individual_address": "Home Assistantis kasutatav KNX-aadress, nt \"0.0.4\".", - "local_ip": "Automaatse tuvastamise jaoks kasuta `0.0.0.0.0`.", - "multicast_group": "Kasutatakse marsruutimiseks ja avastamiseks. Vaikimisi: \"224.0.23.12\"", - "multicast_port": "Kasutatakse marsruutimiseks ja avastamiseks. Vaikev\u00e4\u00e4rtus: \"3671\"", - "rate_limit": "Maksimaalne v\u00e4ljaminevate telegrammide arv sekundis.\nSoovitatav: 20 kuni 40", - "state_updater": "M\u00e4\u00e4ra KNX siini olekute lugemise vaikev\u00e4\u00e4rtused. Kui see on keelatud, ei too Home Assistant aktiivselt olemi olekuid KNX siinilt. Saab alistada olemivalikute s\u00fcnkroonimise_olekuga." - } - }, "manual_tunnel": { "data": { "host": "Host", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "KNX tunnel\u00fchendus", - "host": "Host", - "port": "Port", - "tunneling_type": "KNX tunneli t\u00fc\u00fcp" - }, - "data_description": { - "host": "KNX/IP tunneldusseadme IP-aadress.", - "port": "KNX/IP-tunneldusseadme port." + "gateway": "KNX tunnel\u00fchendus" }, "description": "Vali nimekirjast l\u00fc\u00fcs" } diff --git a/homeassistant/components/knx/translations/fr.json b/homeassistant/components/knx/translations/fr.json index 7ec7a06b7ad..4ca70107530 100644 --- a/homeassistant/components/knx/translations/fr.json +++ b/homeassistant/components/knx/translations/fr.json @@ -55,19 +55,6 @@ }, "description": "Veuillez saisir les informations relatives \u00e0 votre fichier `.knxkeys`." }, - "secure_manual": { - "data": { - "device_authentication": "Mot de passe d'authentification de l'appareil", - "user_id": "ID de l'utilisateur", - "user_password": "Mot de passe de l'utilisateur" - }, - "data_description": { - "device_authentication": "D\u00e9fini dans le panneau \u00ab\u00a0IP\u00a0\u00bb de l'interface dans ETS.", - "user_id": "G\u00e9n\u00e9ralement le num\u00e9ro du tunnel +\u00a01. Par exemple, \u00ab\u00a0Tunnel 2\u00a0\u00bb aurait l'ID utilisateur \u00ab\u00a03\u00a0\u00bb.", - "user_password": "Mot de passe pour la connexion de tunnel sp\u00e9cifique, d\u00e9fini dans le panneau \u00ab\u00a0Propri\u00e9t\u00e9s\u00a0\u00bb du tunnel dans ETS." - }, - "description": "Veuillez saisir vos informations de s\u00e9curit\u00e9 IP." - }, "secure_tunnel_manual": { "data": { "device_authentication": "Mot de passe d\u2019authentification de l\u2019appareil", @@ -85,7 +72,6 @@ "description": "S\u00e9lectionnez la mani\u00e8re dont vous souhaitez configurer la s\u00e9curit\u00e9 IP de KNX.", "menu_options": { "secure_knxkeys": "Utiliser un fichier `.knxkeys` contenant les cl\u00e9s de s\u00e9curit\u00e9 IP", - "secure_manual": "Configurer manuellement les cl\u00e9s de s\u00e9curit\u00e9 IP", "secure_tunnel_manual": "Configurer manuellement les cl\u00e9s de s\u00e9curit\u00e9 IP" } }, @@ -94,12 +80,6 @@ "gateway": "Connexion tunnel KNX" }, "description": "Veuillez s\u00e9lectionner une passerelle dans la liste." - }, - "type": { - "data": { - "connection_type": "Type de connexion KNX" - }, - "description": "Veuillez saisir le type de connexion que nous devons utiliser pour votre connexion KNX.\n AUTOMATIQUE - L'int\u00e9gration prend en charge la connectivit\u00e9 \u00e0 votre bus KNX en effectuant un scan de passerelle.\n TUNNELING - L'int\u00e9gration se connectera \u00e0 votre bus KNX via tunneling.\n ROUTAGE - L'int\u00e9gration se connectera \u00e0 votre bus KNX via le routage." } } }, @@ -117,25 +97,6 @@ "connection_type": "Type de connexion KNX" } }, - "init": { - "data": { - "connection_type": "Type de connexion KNX", - "individual_address": "Adresse individuelle par d\u00e9faut", - "local_ip": "IP locale de Home Assistant", - "multicast_group": "Groupe multicast", - "multicast_port": "Port multicast", - "rate_limit": "Limite d'envoi", - "state_updater": "Mises \u00e0 jour d'\u00e9tat" - }, - "data_description": { - "individual_address": "Adresse KNX que Home Assistant doit utiliser, par exemple `0.0.4`.", - "local_ip": "Utilisez `0.0.0.0` pour la d\u00e9couverte automatique.", - "multicast_group": "Utilis\u00e9 pour le routage et la d\u00e9couverte. Valeur par d\u00e9faut\u00a0: `224.0.23.12`", - "multicast_port": "Utilis\u00e9 pour le routage et la d\u00e9couverte. Valeur par d\u00e9faut\u00a0: `3671`", - "rate_limit": "Nombre maximal de t\u00e9l\u00e9grammes sortants par seconde.\nValeur recommand\u00e9e\u00a0: entre 20 et 40", - "state_updater": "Active ou d\u00e9sactive globalement la lecture des \u00e9tats depuis le bus KNX. Lorsqu'elle est d\u00e9sactiv\u00e9e, Home Assistant ne r\u00e9cup\u00e8re pas activement les \u00e9tats depuis le bus KNX. Peut \u00eatre remplac\u00e9 par les options d'entit\u00e9 `sync_state`." - } - }, "manual_tunnel": { "data": { "host": "H\u00f4te", @@ -202,14 +163,7 @@ }, "tunnel": { "data": { - "gateway": "Connexion tunnel KNX", - "host": "H\u00f4te", - "port": "Port", - "tunneling_type": "Type de tunnel KNX" - }, - "data_description": { - "host": "Adresse IP de l'appareil de tunnel KNX/IP.", - "port": "Port de l'appareil de tunnel KNX/IP." + "gateway": "Connexion tunnel KNX" }, "description": "Veuillez s\u00e9lectionner une passerelle dans la liste." } diff --git a/homeassistant/components/knx/translations/he.json b/homeassistant/components/knx/translations/he.json index dea454b5f6c..8bf31c4e7c7 100644 --- a/homeassistant/components/knx/translations/he.json +++ b/homeassistant/components/knx/translations/he.json @@ -21,21 +21,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "multicast_group": "\u05e7\u05d1\u05d5\u05e6\u05ea \u05e9\u05d9\u05d3\u05d5\u05e8 \u05dc\u05e7\u05d1\u05d5\u05e6\u05d4", - "multicast_port": "\u05d9\u05e6\u05d9\u05d0\u05ea \u05e9\u05d9\u05d3\u05d5\u05e8 \u05dc\u05e7\u05d1\u05d5\u05e6\u05d4" - } - }, - "tunnel": { - "data": { - "host": "\u05de\u05d0\u05e8\u05d7", - "port": "\u05e4\u05ea\u05d7\u05d4" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/knx/translations/hu.json b/homeassistant/components/knx/translations/hu.json index 1aac9fb1b89..ab6e3a8c9af 100644 --- a/homeassistant/components/knx/translations/hu.json +++ b/homeassistant/components/knx/translations/hu.json @@ -60,19 +60,6 @@ }, "description": "K\u00e9rem, adja meg a '.knxkeys' f\u00e1jl adatait." }, - "secure_manual": { - "data": { - "device_authentication": "Eszk\u00f6z hiteles\u00edt\u00e9si jelsz\u00f3", - "user_id": "Felhaszn\u00e1l\u00f3i azonos\u00edt\u00f3", - "user_password": "Felhaszn\u00e1l\u00f3i jelsz\u00f3" - }, - "data_description": { - "device_authentication": "Ezt az ETS-ben az interf\u00e9sz \"IP\" panelj\u00e9n kell be\u00e1ll\u00edtani.", - "user_id": "Ez gyakran a tunnel sz\u00e1ma +1. Teh\u00e1t a \"Tunnel 2\" felhaszn\u00e1l\u00f3i azonos\u00edt\u00f3ja \"3\".", - "user_password": "Jelsz\u00f3 az adott tunnelhez, amely a tunnel \u201eProperties\u201d panelj\u00e9n van be\u00e1ll\u00edtva az ETS-ben." - }, - "description": "K\u00e9rem, adja meg az IP secure adatokat." - }, "secure_tunnel_manual": { "data": { "device_authentication": "Eszk\u00f6z hiteles\u00edt\u00e9si jelsz\u00f3", @@ -90,7 +77,6 @@ "description": "V\u00e1lassza ki, hogyan szeretn\u00e9 konfigur\u00e1lni az KNX/IP secure-t.", "menu_options": { "secure_knxkeys": "IP secure kulcsokat tartalmaz\u00f3 '.knxkeys' f\u00e1jl haszn\u00e1lata", - "secure_manual": "IP secure kulcsok manu\u00e1lis be\u00e1ll\u00edt\u00e1sa", "secure_tunnel_manual": "IP secure kulcsok manu\u00e1lis be\u00e1ll\u00edt\u00e1sa" } }, @@ -99,12 +85,6 @@ "gateway": "KNX alag\u00fat (tunnel) kapcsolat" }, "description": "V\u00e1lasszon egy \u00e1tj\u00e1r\u00f3t a list\u00e1b\u00f3l." - }, - "type": { - "data": { - "connection_type": "KNX csatlakoz\u00e1s t\u00edpusa" - }, - "description": "K\u00e9rem, adja meg a KNX-kapcsolathoz haszn\u00e1land\u00f3 kapcsolatt\u00edpust. \n AUTOMATIKUS - Az integr\u00e1ci\u00f3 gondoskodik a KNX buszhoz val\u00f3 kapcsol\u00f3d\u00e1sr\u00f3l egy \u00e1tj\u00e1r\u00f3 keres\u00e9s elv\u00e9gz\u00e9s\u00e9vel. \n TUNNELING - Az integr\u00e1ci\u00f3 alag\u00faton kereszt\u00fcl csatlakozik a KNX buszhoz. \n ROUTING - Az integr\u00e1ci\u00f3 a KNX buszhoz \u00fatv\u00e1laszt\u00e1ssal csatlakozik." } } }, @@ -135,25 +115,6 @@ }, "description": "K\u00e9rem, adja meg a KNX-kapcsolathoz haszn\u00e1land\u00f3 kapcsolatt\u00edpust. \n AUTOMATIKUS - Az integr\u00e1ci\u00f3 gondoskodik a KNX buszhoz val\u00f3 kapcsol\u00f3d\u00e1sr\u00f3l egy \u00e1tj\u00e1r\u00f3 keres\u00e9s elv\u00e9gz\u00e9s\u00e9vel. \n TUNNELING - Az integr\u00e1ci\u00f3 alag\u00faton kereszt\u00fcl csatlakozik a KNX buszhoz. \n ROUTING - Az integr\u00e1ci\u00f3 a KNX buszhoz \u00fatv\u00e1laszt\u00e1ssal csatlakozik." }, - "init": { - "data": { - "connection_type": "KNX csatlakoz\u00e1s t\u00edpusa", - "individual_address": "Alap\u00e9rtelmezett egy\u00e9ni c\u00edm", - "local_ip": "Home Assistant lok\u00e1lis IP c\u00edme", - "multicast_group": "Multicast csoport", - "multicast_port": "Multicast portsz\u00e1m", - "rate_limit": "Lek\u00e9r\u00e9si korl\u00e1toz\u00e1s", - "state_updater": "\u00c1llapot friss\u00edt\u0151" - }, - "data_description": { - "individual_address": "A Home Assistant \u00e1ltal haszn\u00e1land\u00f3 KNX-c\u00edm, pl. \"0.0.4\".", - "local_ip": "Haszn\u00e1lja a `0.0.0.0` c\u00edmet az automatikus felder\u00edt\u00e9shez.", - "multicast_group": "\u00datv\u00e1laszt\u00e1shoz \u00e9s felder\u00edt\u00e9shez haszn\u00e1latos. Alap\u00e9rtelmezett: `224.0.23.12`.", - "multicast_port": "\u00datv\u00e1laszt\u00e1shoz \u00e9s felder\u00edt\u00e9shez haszn\u00e1latos. Alap\u00e9rtelmezett: `3671`", - "rate_limit": "Maxim\u00e1lis kimen\u0151 \u00fczenet m\u00e1sodpercenk\u00e9nt.\nAj\u00e1nlott: 20 \u00e9s 40 k\u00f6z\u00f6tt", - "state_updater": "Alap\u00e9rtelmezett be\u00e1ll\u00edt\u00e1s a KNX busz \u00e1llapotainak olvas\u00e1s\u00e1hoz. Ha le va tiltva, Home Assistant nem fog akt\u00edvan lek\u00e9rdezni egys\u00e9g\u00e1llapotokat a KNX buszr\u00f3l. Fel\u00fclb\u00edr\u00e1lhat\u00f3 a `sync_state` entit\u00e1s opci\u00f3kkal." - } - }, "manual_tunnel": { "data": { "host": "C\u00edm", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "KNX alag\u00fat (tunnel) kapcsolat", - "host": "C\u00edm", - "port": "Port", - "tunneling_type": "KNX alag\u00fat t\u00edpusa" - }, - "data_description": { - "host": "A KNX/IP tunnel eszk\u00f6z IP-c\u00edme.", - "port": "A KNX/IP tunnel eszk\u00f6z portsz\u00e1ma." + "gateway": "KNX alag\u00fat (tunnel) kapcsolat" }, "description": "V\u00e1lasszon egy \u00e1tj\u00e1r\u00f3t a list\u00e1b\u00f3l." } diff --git a/homeassistant/components/knx/translations/id.json b/homeassistant/components/knx/translations/id.json index 6b4977e543a..012a027154a 100644 --- a/homeassistant/components/knx/translations/id.json +++ b/homeassistant/components/knx/translations/id.json @@ -60,19 +60,6 @@ }, "description": "Masukkan informasi untuk file `.knxkeys` Anda." }, - "secure_manual": { - "data": { - "device_authentication": "Kata sandi autentikasi perangkat", - "user_id": "ID pengguna", - "user_password": "Kata sandi pengguna" - }, - "data_description": { - "device_authentication": "Ini diatur dalam panel 'IP' dalam antarmuka di ETS.", - "user_id": "Ini sering kali merupakan tunnel nomor +1. Jadi 'Tunnel 2' akan memiliki User-ID '3'.", - "user_password": "Kata sandi untuk koneksi tunnel tertentu yang diatur di panel 'Properties' tunnel di ETS." - }, - "description": "Masukkan informasi IP aman Anda." - }, "secure_tunnel_manual": { "data": { "device_authentication": "Kata sandi autentikasi perangkat", @@ -90,7 +77,6 @@ "description": "Pilih cara Anda ingin mengonfigurasi KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Gunakan file `.knxkeys` yang berisi kunci aman IP", - "secure_manual": "Konfigurasikan kunci aman IP secara manual", "secure_tunnel_manual": "Konfigurasikan kunci aman IP secara manual" } }, @@ -99,12 +85,6 @@ "gateway": "Koneksi Tunnel KNX" }, "description": "Pilih gateway dari daftar." - }, - "type": { - "data": { - "connection_type": "Jenis Koneksi KNX" - }, - "description": "Masukkan jenis koneksi yang harus kami gunakan untuk koneksi KNX Anda. \nOTOMATIS - Integrasi melakukan konektivitas ke bus KNX Anda dengan melakukan pemindaian gateway. \nTUNNELING - Integrasi akan terhubung ke bus KNX Anda melalui tunneling. \nROUTING - Integrasi akan terhubung ke bus KNX Anda melalui routing." } } }, @@ -135,25 +115,6 @@ }, "description": "Masukkan jenis koneksi yang harus kami gunakan untuk koneksi KNX Anda. \nOTOMATIS - Integrasi melakukan konektivitas ke bus KNX Anda dengan melakukan pemindaian gateway. \nTUNNELING - Integrasi akan terhubung ke bus KNX Anda melalui tunneling. \nROUTING - Integrasi akan terhubung ke bus KNX Anda melalui routing." }, - "init": { - "data": { - "connection_type": "Jenis Koneksi KNX", - "individual_address": "Alamat individu default", - "local_ip": "IP lokal Home Assistant", - "multicast_group": "Grup multicast", - "multicast_port": "Port multicast", - "rate_limit": "Batas data", - "state_updater": "Pembaruan status" - }, - "data_description": { - "individual_address": "Alamat KNX yang akan digunakan oleh Home Assistant, misalnya `0.0.4`", - "local_ip": "Gunakan `0.0.0.0` untuk penemuan otomatis.", - "multicast_group": "Digunakan untuk perutean dan penemuan. Bawaan: `224.0.23.12`", - "multicast_port": "Digunakan untuk perutean dan penemuan. Bawaan: `3671`", - "rate_limit": "Telegram keluar maksimum per detik.\nDirekomendasikan: 20 hingga 40", - "state_updater": "Menyetel default untuk status pembacaan KNX Bus. Saat dinonaktifkan, Home Assistant tidak akan secara aktif mengambil status entitas dari KNX Bus. Hal ini bisa ditimpa dengan opsi entitas `sync_state`." - } - }, "manual_tunnel": { "data": { "host": "Host", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "Koneksi Tunnel KNX", - "host": "Host", - "port": "Port", - "tunneling_type": "Jenis Tunnel KNX" - }, - "data_description": { - "host": "Alamat IP perangkat tunneling KNX/IP.", - "port": "Port perangkat tunneling KNX/IP." + "gateway": "Koneksi Tunnel KNX" }, "description": "Pilih gateway dari daftar." } diff --git a/homeassistant/components/knx/translations/it.json b/homeassistant/components/knx/translations/it.json index 4a7bc92652d..75c6cda6ab5 100644 --- a/homeassistant/components/knx/translations/it.json +++ b/homeassistant/components/knx/translations/it.json @@ -60,19 +60,6 @@ }, "description": "Inserisci le informazioni per il tuo file `.knxkeys`." }, - "secure_manual": { - "data": { - "device_authentication": "Password di autenticazione del dispositivo", - "user_id": "ID utente", - "user_password": "Password utente" - }, - "data_description": { - "device_authentication": "Questo \u00e8 impostato nel pannello 'IP' dell'interfaccia in ETS.", - "user_id": "Questo \u00e8 spesso il tunnel numero +1. Quindi \"Tunnel 2\" avrebbe l'ID utente \"3\".", - "user_password": "Password per la connessione specifica del tunnel impostata nel pannello 'Propriet\u00e0' del tunnel in ETS." - }, - "description": "Inserisci le tue informazioni di sicurezza IP." - }, "secure_tunnel_manual": { "data": { "device_authentication": "Password di autenticazione del dispositivo", @@ -90,7 +77,6 @@ "description": "Seleziona come vuoi configurare KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Utilizza un file `.knxkeys` contenente chiavi di sicurezza IP", - "secure_manual": "Configura manualmente le chiavi di sicurezza IP", "secure_tunnel_manual": "Configura manualmente le chiavi di sicurezza IP" } }, @@ -99,12 +85,6 @@ "gateway": "Connessione tunnel KNX" }, "description": "Seleziona un gateway dall'elenco." - }, - "type": { - "data": { - "connection_type": "Tipo di connessione KNX" - }, - "description": "Inserisci il tipo di connessione che dovremmo usare per la tua connessione KNX.\n AUTOMATICO - L'integrazione si occupa della connettivit\u00e0 al tuo Bus KNX eseguendo una scansione del gateway.\n TUNNELING - L'integrazione si collegher\u00e0 al bus KNX tramite tunnel.\n ROUTING - L'integrazione si collegher\u00e0 al bus KNX tramite instradamento." } } }, @@ -135,25 +115,6 @@ }, "description": "Inserisci il tipo di connessione che dovremmo usare per la tua connessione KNX. \n AUTOMATICO - L'integrazione si occupa della connettivit\u00e0 al tuo bus KNX eseguendo una scansione del gateway. \n TUNNELING - L'integrazione si collegher\u00e0 al tuo bus KNX tramite tunneling. \n ROUTING - L'integrazione si connetter\u00e0 al tuo bus KNX tramite instradamento." }, - "init": { - "data": { - "connection_type": "Tipo di connessione KNX", - "individual_address": "Indirizzo individuale predefinito", - "local_ip": "IP locale di Home Assistant", - "multicast_group": "Gruppo multicast", - "multicast_port": "Porta multicast", - "rate_limit": "Limite di tariffa", - "state_updater": "Aggiornatore di stato" - }, - "data_description": { - "individual_address": "Indirizzo KNX che deve essere utilizzato da Home Assistant, ad es. `0.0.4`", - "local_ip": "Usa `0.0.0.0` per il rilevamento automatico.", - "multicast_group": "Utilizzato per l'instradamento e il rilevamento. Predefinito: `224.0.23.12`", - "multicast_port": "Utilizzato per l'instradamento e il rilevamento. Predefinito: `3671`", - "rate_limit": "Numero massimo di telegrammi in uscita al secondo.\n Consigliato: da 20 a 40", - "state_updater": "Impostazione predefinita per la lettura degli stati dal bus KNX. Se disabilitata Home Assistant non recuperer\u00e0 attivamente gli stati delle entit\u00e0 dal bus KNX. Pu\u00f2 essere sovrascritta dalle opzioni dell'entit\u00e0 `sync_state`." - } - }, "manual_tunnel": { "data": { "host": "Host", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "Connessione tunnel KNX", - "host": "Host", - "port": "Porta", - "tunneling_type": "Tipo tunnel KNX" - }, - "data_description": { - "host": "Indirizzo IP del dispositivo di tunneling KNX/IP.", - "port": "Porta del dispositivo di tunneling KNX/IP." + "gateway": "Connessione tunnel KNX" }, "description": "Seleziona un gateway dall'elenco." } diff --git a/homeassistant/components/knx/translations/ja.json b/homeassistant/components/knx/translations/ja.json index 3272508a525..47af3f3998b 100644 --- a/homeassistant/components/knx/translations/ja.json +++ b/homeassistant/components/knx/translations/ja.json @@ -50,24 +50,10 @@ }, "description": "'.knxkeys'\u30d5\u30a1\u30a4\u30eb\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, - "secure_manual": { - "data": { - "device_authentication": "\u30c7\u30d0\u30a4\u30b9\u8a8d\u8a3c\u30d1\u30b9\u30ef\u30fc\u30c9", - "user_id": "\u30e6\u30fc\u30b6\u30fcID", - "user_password": "\u30e6\u30fc\u30b6\u30fc\u30d1\u30b9\u30ef\u30fc\u30c9" - }, - "data_description": { - "device_authentication": "\u3053\u308c\u306f\u3001ETS\u306e\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9\u306e 'IP' \u30d1\u30cd\u30eb\u3067\u8a2d\u5b9a\u3057\u307e\u3059\u3002", - "user_id": "\u591a\u304f\u306e\u5834\u5408\u3001\u3053\u308c\u306f\u30c8\u30f3\u30cd\u30eb\u756a\u53f7+1\u3067\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001 '\u30c8\u30f3\u30cd\u30eb2' \u306e\u30e6\u30fc\u30b6\u30fcID\u306f\u3001'3 '\u306b\u306a\u308a\u307e\u3059\u3002", - "user_password": "ETS\u306e\u30c8\u30f3\u30cd\u30eb\u306e\u3001'\u30d7\u30ed\u30d1\u30c6\u30a3' \u30d1\u30cd\u30eb\u3067\u8a2d\u5b9a\u3055\u308c\u305f\u7279\u5b9a\u306e\u30c8\u30f3\u30cd\u30eb\u63a5\u7d9a\u7528\u306e\u30d1\u30b9\u30ef\u30fc\u30c9\u3002" - }, - "description": "IP \u30bb\u30ad\u30e5\u30a2\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" - }, "secure_tunneling": { "description": "KNX/IP \u30bb\u30ad\u30e5\u30a2\u3092\u69cb\u6210\u3059\u308b\u65b9\u6cd5\u3092\u9078\u629e\u3057\u307e\u3059\u3002", "menu_options": { - "secure_knxkeys": "IP \u30bb\u30ad\u30e5\u30a2 \u30ad\u30fc\u3092\u542b\u3080\u300c.knxkeys\u300d\u30d5\u30a1\u30a4\u30eb\u3092\u4f7f\u7528\u3059\u308b", - "secure_manual": "IP \u30bb\u30ad\u30e5\u30a2 \u30ad\u30fc\u3092\u624b\u52d5\u3067\u69cb\u6210\u3059\u308b" + "secure_knxkeys": "IP \u30bb\u30ad\u30e5\u30a2 \u30ad\u30fc\u3092\u542b\u3080\u300c.knxkeys\u300d\u30d5\u30a1\u30a4\u30eb\u3092\u4f7f\u7528\u3059\u308b" } }, "tunnel": { @@ -75,46 +61,6 @@ "gateway": "KNX\u30c8\u30f3\u30cd\u30eb\u63a5\u7d9a" }, "description": "\u30ea\u30b9\u30c8\u304b\u3089\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002" - }, - "type": { - "data": { - "connection_type": "KNX\u63a5\u7d9a\u30bf\u30a4\u30d7" - }, - "description": "KNX\u63a5\u7d9a\u306b\u4f7f\u7528\u3059\u308b\u63a5\u7d9a\u30bf\u30a4\u30d7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 \n AUTOMATIC - \u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u30b9\u30ad\u30e3\u30f3\u3092\u5b9f\u884c\u3057\u3066\u3001KNX \u30d0\u30b9\u3078\u306e\u63a5\u7d9a\u3092\u884c\u3044\u307e\u3059\u3002 \n TUNNELING - \u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u3092\u4ecb\u3057\u3066\u3001KNX\u30d0\u30b9\u306b\u63a5\u7d9a\u3057\u307e\u3059\u3002 \n ROUTING - \u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3092\u4ecb\u3057\u3066\u3001KNX \u30d0\u30b9\u306b\u63a5\u7d9a\u3057\u307e\u3059\u3002" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "connection_type": "KNX\u63a5\u7d9a\u30bf\u30a4\u30d7", - "individual_address": "\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u500b\u5225\u30a2\u30c9\u30ec\u30b9", - "local_ip": "\u30ed\u30fc\u30ab\u30ebIP(\u4e0d\u660e\u306a\u5834\u5408\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", - "multicast_group": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3068\u691c\u51fa(discovery)\u306b\u4f7f\u7528\u3055\u308c\u308b\u30de\u30eb\u30c1\u30ad\u30e3\u30b9\u30c8\u30b0\u30eb\u30fc\u30d7", - "multicast_port": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3068\u691c\u51fa(discovery)\u306b\u4f7f\u7528\u3055\u308c\u308b\u30de\u30eb\u30c1\u30ad\u30e3\u30b9\u30c8\u30dd\u30fc\u30c8", - "rate_limit": "1 \u79d2\u3042\u305f\u308a\u306e\u6700\u5927\u9001\u4fe1\u96fb\u5831(telegrams )\u6570", - "state_updater": "KNX\u30d0\u30b9\u304b\u3089\u306e\u8aad\u307f\u53d6\u308a\u72b6\u614b\u3092\u30b0\u30ed\u30fc\u30d0\u30eb\u306b\u6709\u52b9\u306b\u3059\u308b" - }, - "data_description": { - "individual_address": "Home Assistant\u304c\u4f7f\u7528\u3059\u308bKNX\u30a2\u30c9\u30ec\u30b9\u3001\u4f8b. `0.0.4`", - "local_ip": "\u81ea\u52d5\u691c\u51fa\u306b\u306f\u3001`0.0.0.0` \u3092\u4f7f\u7528\u3057\u307e\u3059\u3002", - "multicast_group": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3068\u691c\u51fa\u306b\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002\u30c7\u30d5\u30a9\u30eb\u30c8t: `224.0.23.12`", - "multicast_port": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3068\u691c\u51fa\u306b\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002\u30c7\u30d5\u30a9\u30eb\u30c8: `3671`", - "rate_limit": "1\u79d2\u3042\u305f\u308a\u306e\u6700\u5927\u9001\u4fe1\u30c6\u30ec\u30b0\u30e9\u30e0\u3002\n\u63a8\u5968: 20\uff5e40", - "state_updater": "KNX \u30d0\u30b9\u304b\u3089\u72b6\u614b\u3092\u8aad\u307f\u53d6\u308b\u305f\u3081\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002\u7121\u52b9\u306b\u3059\u308b\u3068\u3001Home Assistant \u306f KNX \u30d0\u30b9\u304b\u3089\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u306e\u72b6\u614b\u3092\u7a4d\u6975\u7684\u306b\u53d6\u5f97\u3057\u307e\u305b\u3093\u3002 \u300csync_state\u300d\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3 \u30aa\u30d7\u30b7\u30e7\u30f3\u3067\u30aa\u30fc\u30d0\u30fc\u30e9\u30a4\u30c9\u3067\u304d\u307e\u3059\u3002" - } - }, - "tunnel": { - "data": { - "host": "\u30db\u30b9\u30c8", - "port": "\u30dd\u30fc\u30c8", - "tunneling_type": "KNX\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30bf\u30a4\u30d7" - }, - "data_description": { - "host": "KNX/IP\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30c7\u30d0\u30a4\u30b9\u306eIP\u30a2\u30c9\u30ec\u30b9\u3002", - "port": "KNX/IP\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30c7\u30d0\u30a4\u30b9\u306e\u30dd\u30fc\u30c8\u3002" - } } } } diff --git a/homeassistant/components/knx/translations/ko.json b/homeassistant/components/knx/translations/ko.json index 0aac9be9341..c65ad01ff79 100644 --- a/homeassistant/components/knx/translations/ko.json +++ b/homeassistant/components/knx/translations/ko.json @@ -5,9 +5,6 @@ "data": { "knxkeys_filename": "`.knxkeys` \ud30c\uc77c\uc758 \ud30c\uc77c \uc774\ub984(\ud655\uc7a5\uc790 \ud3ec\ud568)" } - }, - "secure_manual": { - "description": "IP \ubcf4\uc548 \uc815\ubcf4\ub97c \uc785\ub825\ud558\uc138\uc694." } } } diff --git a/homeassistant/components/knx/translations/nl.json b/homeassistant/components/knx/translations/nl.json index a9c52b68e75..897b2dc533e 100644 --- a/homeassistant/components/knx/translations/nl.json +++ b/homeassistant/components/knx/translations/nl.json @@ -50,24 +50,10 @@ }, "description": "Voer de informatie voor uw `.knxkeys` bestand in." }, - "secure_manual": { - "data": { - "device_authentication": "Wachtwoord voor apparaatverificatie", - "user_id": "User ID", - "user_password": "Gebruikerswachtwoord" - }, - "data_description": { - "device_authentication": "Dit wordt ingesteld in het \"IP\"-paneel van de interface in ETS.", - "user_id": "Dit is vaak tunnelnummer +1. Dus 'Tunnel 2' zou User-ID '3' hebben.", - "user_password": "Wachtwoord voor de specifieke tunnelverbinding, ingesteld in het paneel \"Eigenschappen\" van de tunnel in ETS." - }, - "description": "Voer uw beveiligde IP-gegevens in." - }, "secure_tunneling": { "description": "Kies hoe u KNX/IP Secure wilt configureren.", "menu_options": { - "secure_knxkeys": "Gebruik een `.knxkeys` bestand met IP beveiligde sleutels", - "secure_manual": "IP-beveiligingssleutels handmatig configureren" + "secure_knxkeys": "Gebruik een `.knxkeys` bestand met IP beveiligde sleutels" } }, "tunnel": { @@ -75,12 +61,6 @@ "gateway": "KNX Tunnel Connection" }, "description": "Selecteer een gateway uit de lijst." - }, - "type": { - "data": { - "connection_type": "KNX-verbindingstype" - }, - "description": "Voer het verbindingstype in dat we moeten gebruiken voor uw KNX-verbinding.\n AUTOMATISCH - De integratie zorgt voor de connectiviteit met uw KNX-bus door een gateway-scan uit te voeren.\n TUNNELING - De integratie maakt verbinding met uw KNX-bus via tunneling.\n ROUTING - De integratie maakt via routing verbinding met uw KNX-bus." } } }, @@ -91,25 +71,6 @@ "invalid_individual_address": "Waarde komt niet overeen met patroon voor KNX individueel adres.\n\"area.line.device" }, "step": { - "init": { - "data": { - "connection_type": "KNX-verbindingstype", - "individual_address": "Standaard individueel adres", - "local_ip": "Lokale IP van Home Assistant", - "multicast_group": "Multicast-groep", - "multicast_port": "Multicast-poort", - "rate_limit": "Rate limit", - "state_updater": "Statusupdater" - }, - "data_description": { - "individual_address": "KNX-adres dat door Home Assistant moet worden gebruikt, bijv. `0.0.4`", - "local_ip": "Gebruik `0.0.0.0` voor auto-discovery.", - "multicast_group": "Gebruikt voor routing en discovery. Standaard: `224.0.23.12`.", - "multicast_port": "Gebruikt voor routing en discovery. Standaard: `3671`", - "rate_limit": "Maximaal aantal uitgaande telegrammen per seconde.\nAanbevolen: 20 tot 40", - "state_updater": "Globaal in- of uitschakelen van het lezen van de status van de KNX bus. Indien uitgeschakeld, zal Home Assistant niet actief de status van de KNX Bus ophalen, `sync_state` entiteitsopties zullen geen effect hebben." - } - }, "manual_tunnel": { "data_description": { "local_ip": "Leeg laten om auto-discovery te gebruiken.", @@ -149,14 +110,7 @@ }, "tunnel": { "data": { - "gateway": "KNX Tunnel Connection", - "host": "Host", - "port": "Poort", - "tunneling_type": "KNX Tunneling Type" - }, - "data_description": { - "host": "IP adres van het KNX/IP tunneling apparaat.", - "port": "Poort van het KNX/IP-tunnelapparaat." + "gateway": "KNX Tunnel Connection" }, "description": "Selecteer een gateway uit de lijst." } diff --git a/homeassistant/components/knx/translations/no.json b/homeassistant/components/knx/translations/no.json index f6aaca60d18..361595394f1 100644 --- a/homeassistant/components/knx/translations/no.json +++ b/homeassistant/components/knx/translations/no.json @@ -60,19 +60,6 @@ }, "description": "Vennligst skriv inn informasjonen for `.knxkeys`-filen." }, - "secure_manual": { - "data": { - "device_authentication": "Passord for enhetsgodkjenning", - "user_id": "bruker-ID", - "user_password": "Brukerpassord" - }, - "data_description": { - "device_authentication": "Dette settes i 'IP'-panelet til grensesnittet i ETS.", - "user_id": "Dette er ofte tunnelnummer +1. S\u00e5 'Tunnel 2' ville ha bruker-ID '3'.", - "user_password": "Passord for den spesifikke tunnelforbindelsen satt i 'Egenskaper'-panelet i tunnelen i ETS." - }, - "description": "Vennligst skriv inn din sikre IP-informasjon." - }, "secure_tunnel_manual": { "data": { "device_authentication": "Enhetsautentiseringspassord", @@ -90,7 +77,6 @@ "description": "Velg hvordan du vil konfigurere KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Bruk en `.knxkeys`-fil som inneholder IP-sikre n\u00f8kler", - "secure_manual": "Konfigurer IP-sikre n\u00f8kler manuelt", "secure_tunnel_manual": "Konfigurer IP-sikre n\u00f8kler manuelt" } }, @@ -99,12 +85,6 @@ "gateway": "KNX Tunneltilkobling" }, "description": "Vennligst velg en gateway fra listen." - }, - "type": { - "data": { - "connection_type": "KNX tilkoblingstype" - }, - "description": "Vennligst skriv inn tilkoblingstypen vi skal bruke for din KNX-tilkobling.\n AUTOMATISK - Integrasjonen tar seg av tilkoblingen til KNX-bussen ved \u00e5 utf\u00f8re en gateway-skanning.\n TUNNELING - Integrasjonen vil kobles til KNX-bussen din via tunnelering.\n ROUTING - Integrasjonen vil kobles til din KNX-bussen via ruting." } } }, @@ -135,25 +115,6 @@ }, "description": "Vennligst skriv inn tilkoblingstypen vi skal bruke for din KNX-tilkobling.\n AUTOMATISK - Integrasjonen tar seg av tilkoblingen til KNX-bussen ved \u00e5 utf\u00f8re en gateway-skanning.\n TUNNELING - Integrasjonen vil kobles til din KNX-bussen via tunnelering.\n ROUTING - Integrasjonen vil koble til din KNX-bussen via ruting." }, - "init": { - "data": { - "connection_type": "KNX tilkoblingstype", - "individual_address": "Standard individuell adresse", - "local_ip": "Lokal IP for hjemmeassistent", - "multicast_group": "Multicast gruppe", - "multicast_port": "Multicast port", - "rate_limit": "Satsgrense", - "state_updater": "Statens oppdatering" - }, - "data_description": { - "individual_address": "KNX-adresse som skal brukes av Home Assistant, f.eks. `0.0.4`", - "local_ip": "Bruk `0.0.0.0` for automatisk oppdagelse.", - "multicast_group": "Brukes til ruting og oppdagelse. Standard: `224.0.23.12`", - "multicast_port": "Brukes til ruting og oppdagelse. Standard: `3671`", - "rate_limit": "Maksimalt utg\u00e5ende telegrammer per sekund.\n Anbefalt: 20 til 40", - "state_updater": "Sett standard for lesing av tilstander fra KNX-bussen. N\u00e5r den er deaktivert, vil ikke Home Assistant aktivt hente enhetstilstander fra KNX-bussen. Kan overstyres av entitetsalternativer for \"sync_state\"." - } - }, "manual_tunnel": { "data": { "host": "Vert", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "KNX Tunneltilkobling", - "host": "Vert", - "port": "Port", - "tunneling_type": "KNX tunneltype" - }, - "data_description": { - "host": "IP-adressen til KNX/IP-tunnelenheten.", - "port": "Port p\u00e5 KNX/IP-tunnelenheten." + "gateway": "KNX Tunneltilkobling" }, "description": "Vennligst velg en gateway fra listen." } diff --git a/homeassistant/components/knx/translations/pl.json b/homeassistant/components/knx/translations/pl.json index 0bb9bbdfb1a..98a9e968595 100644 --- a/homeassistant/components/knx/translations/pl.json +++ b/homeassistant/components/knx/translations/pl.json @@ -60,19 +60,6 @@ }, "description": "Wprowad\u017a informacje dotycz\u0105ce pliku `.knxkeys`." }, - "secure_manual": { - "data": { - "device_authentication": "Has\u0142o uwierzytelniania urz\u0105dzenia", - "user_id": "Identyfikator u\u017cytkownika", - "user_password": "Has\u0142o u\u017cytkownika" - }, - "data_description": { - "device_authentication": "Jest to ustawiane w panelu \u201eIP\u201d interfejsu w ETS.", - "user_id": "Cz\u0119sto jest to numer tunelu plus 1. Tak wi\u0119c \u201eTunnel 2\u201d mia\u0142by identyfikator u\u017cytkownika \u201e3\u201d.", - "user_password": "Has\u0142o dla konkretnego po\u0142\u0105czenia tunelowego ustawione w panelu \u201eW\u0142a\u015bciwo\u015bci\u201d tunelu w ETS." - }, - "description": "Wprowad\u017a informacje o IP secure." - }, "secure_tunnel_manual": { "data": { "device_authentication": "Has\u0142o uwierzytelniania urz\u0105dzenia", @@ -90,7 +77,6 @@ "description": "Wybierz, jak chcesz skonfigurowa\u0107 KNX/IP secure.", "menu_options": { "secure_knxkeys": "U\u017cyj pliku `.knxkeys` zawieraj\u0105cego klucze IP secure", - "secure_manual": "R\u0119czna konfiguracja kluczy IP secure", "secure_tunnel_manual": "R\u0119czna konfiguracja kluczy IP secure" } }, @@ -99,12 +85,6 @@ "gateway": "Po\u0142\u0105czenie tunelowe KNX" }, "description": "Prosz\u0119 wybra\u0107 bramk\u0119 z listy." - }, - "type": { - "data": { - "connection_type": "Typ po\u0142\u0105czenia KNX" - }, - "description": "Prosz\u0119 wprowadzi\u0107 typ po\u0142\u0105czenia, kt\u00f3rego powinni\u015bmy u\u017cy\u0107 dla po\u0142\u0105czenia KNX. \n AUTOMATIC - Integracja sama zadba o po\u0142\u0105czenie z magistral\u0105 KNX poprzez skanowanie bramki. \n TUNNELING - Integracja po\u0142\u0105czy si\u0119 z magistral\u0105 KNX poprzez tunelowanie. \n ROUTING - Integracja po\u0142\u0105czy si\u0119 z magistral\u0105 KNX poprzez routing." } } }, @@ -135,25 +115,6 @@ }, "description": "Prosz\u0119 wprowadzi\u0107 typ po\u0142\u0105czenia, kt\u00f3rego powinni\u015bmy u\u017cy\u0107 dla po\u0142\u0105czenia KNX. \nAUTOMATIC - Integracja sama zadba o po\u0142\u0105czenie z magistral\u0105 KNX poprzez skanowanie bramki. \nTUNNELING - Integracja po\u0142\u0105czy si\u0119 z magistral\u0105 KNX poprzez tunelowanie. \nROUTING - Integracja po\u0142\u0105czy si\u0119 z magistral\u0105 KNX poprzez routing." }, - "init": { - "data": { - "connection_type": "Typ po\u0142\u0105czenia KNX", - "individual_address": "Domy\u015blny adres indywidualny", - "local_ip": "Lokalny adres IP Home Assistanta", - "multicast_group": "Grupa multicast", - "multicast_port": "Port multicast", - "rate_limit": "Limit", - "state_updater": "Aktualizator stanu" - }, - "data_description": { - "individual_address": "Adres KNX u\u017cywany przez Home Assistanta, np. `0.0.4`", - "local_ip": "U\u017cyj `0.0.0.0` do automatycznego wykrywania.", - "multicast_group": "U\u017cywany do routingu i wykrywania. Domy\u015blnie: `224.0.23.12`", - "multicast_port": "U\u017cywany do routingu i wykrywania. Domy\u015blnie: `3671`", - "rate_limit": "Maksymalna liczba wychodz\u0105cych wiadomo\u015bci na sekund\u0119.\nZalecane: od 20 do 40", - "state_updater": "Ustaw domy\u015blne odczytywanie stan\u00f3w z magistrali KNX. Po wy\u0142\u0105czeniu, Home Assistant nie b\u0119dzie aktywnie pobiera\u0107 stan\u00f3w encji z magistrali KNX. Mo\u017cna to zast\u0105pi\u0107 przez opcj\u0119 encji `sync_state`." - } - }, "manual_tunnel": { "data": { "host": "Nazwa hosta lub adres IP", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "Po\u0142\u0105czenie tunelowe KNX", - "host": "Nazwa hosta lub adres IP", - "port": "Port", - "tunneling_type": "Typ tunelowania KNX" - }, - "data_description": { - "host": "Adres IP urz\u0105dzenia tuneluj\u0105cego KNX/IP.", - "port": "Port urz\u0105dzenia tuneluj\u0105cego KNX/IP." + "gateway": "Po\u0142\u0105czenie tunelowe KNX" }, "description": "Prosz\u0119 wybra\u0107 bramk\u0119 z listy." } diff --git a/homeassistant/components/knx/translations/pt-BR.json b/homeassistant/components/knx/translations/pt-BR.json index 52c23d388d7..4c6d2189742 100644 --- a/homeassistant/components/knx/translations/pt-BR.json +++ b/homeassistant/components/knx/translations/pt-BR.json @@ -60,19 +60,6 @@ }, "description": "Por favor, insira as informa\u00e7\u00f5es para o seu arquivo `.knxkeys`." }, - "secure_manual": { - "data": { - "device_authentication": "Senha de autentica\u00e7\u00e3o do dispositivo", - "user_id": "ID do usu\u00e1rio", - "user_password": "Senha do usu\u00e1rio" - }, - "data_description": { - "device_authentication": "Isso \u00e9 definido no painel 'IP' da interface no ETS.", - "user_id": "Isso geralmente \u00e9 o n\u00famero do t\u00fanel +1. Portanto, 'T\u00fanel 2' teria o ID de usu\u00e1rio '3'.", - "user_password": "Senha para a conex\u00e3o de t\u00fanel espec\u00edfica definida no painel 'Propriedades' do t\u00fanel no ETS." - }, - "description": "Por favor, insira suas informa\u00e7\u00f5es seguras de IP." - }, "secure_tunnel_manual": { "data": { "device_authentication": "Senha de autentica\u00e7\u00e3o do dispositivo", @@ -90,7 +77,6 @@ "description": "Selecione como deseja configurar o KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Use um arquivo `.knxkeys` contendo chaves seguras de IP", - "secure_manual": "Configurar manualmente as chaves de seguran\u00e7a IP", "secure_tunnel_manual": "Configurar chaves seguras de IP manualmente" } }, @@ -99,12 +85,6 @@ "gateway": "Conex\u00e3o do t\u00fanel KNX" }, "description": "Selecione um gateway na lista." - }, - "type": { - "data": { - "connection_type": "Tipo de conex\u00e3o KNX" - }, - "description": "Insira o tipo de conex\u00e3o que devemos usar para sua conex\u00e3o KNX.\n AUTOM\u00c1TICO - A integra\u00e7\u00e3o cuida da conectividade ao seu KNX Bus realizando uma varredura de gateway.\n TUNNELING - A integra\u00e7\u00e3o ser\u00e1 conectada ao seu barramento KNX via tunelamento.\n ROUTING - A integra\u00e7\u00e3o ligar-se-\u00e1 ao seu bus KNX atrav\u00e9s de encaminhamento." } } }, @@ -135,25 +115,6 @@ }, "description": "Insira o tipo de conex\u00e3o que devemos usar para sua conex\u00e3o KNX.\n AUTOM\u00c1TICO - A integra\u00e7\u00e3o cuida da conectividade com o seu KNX Bus realizando uma varredura de gateway.\n TUNNELING - A integra\u00e7\u00e3o ser\u00e1 conectada ao seu barramento KNX via tunelamento.\n ROUTING - A integra\u00e7\u00e3o ligar-se-\u00e1 ao seu bus KNX atrav\u00e9s de encaminhamento." }, - "init": { - "data": { - "connection_type": "Tipo de conex\u00e3o KNX", - "individual_address": "Endere\u00e7o individual padr\u00e3o", - "local_ip": "IP local do Home Assistant", - "multicast_group": "Grupo multicast", - "multicast_port": "Porta multicast", - "rate_limit": "Taxa limite", - "state_updater": "Atualizador de estado" - }, - "data_description": { - "individual_address": "Endere\u00e7o KNX a ser usado pelo Home Assistant, por exemplo, `0.0.4`", - "local_ip": "Use `0.0.0.0` para descoberta autom\u00e1tica.", - "multicast_group": "Usado para roteamento e descoberta. Padr\u00e3o: `224.0.23.12`", - "multicast_port": "Usado para roteamento e descoberta. Padr\u00e3o: `3671`", - "rate_limit": "M\u00e1ximo de telegramas de sa\u00edda por segundo.\n Recomendado: 20 a 40", - "state_updater": "Defina o padr\u00e3o para estados de leitura do barramento KNX. Quando desativado, o Home Assistant n\u00e3o recuperar\u00e1 ativamente os estados de entidade do barramento KNX. Pode ser substitu\u00eddo pelas op\u00e7\u00f5es de entidade `sync_state`." - } - }, "manual_tunnel": { "data": { "host": "Nome do host", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "Conex\u00e3o do t\u00fanel KNX", - "host": "Nome do host", - "port": "Porta", - "tunneling_type": "Tipo de t\u00fanel KNX" - }, - "data_description": { - "host": "Endere\u00e7o IP do dispositivo de tunelamento KNX/IP.", - "port": "Porta do dispositivo de tunelamento KNX/IP." + "gateway": "Conex\u00e3o do t\u00fanel KNX" }, "description": "Selecione um gateway na lista." } diff --git a/homeassistant/components/knx/translations/pt.json b/homeassistant/components/knx/translations/pt.json deleted file mode 100644 index 7220ef495c9..00000000000 --- a/homeassistant/components/knx/translations/pt.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "options": { - "step": { - "tunnel": { - "data": { - "host": "Anfitri\u00e3o", - "port": "Porta" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/knx/translations/ru.json b/homeassistant/components/knx/translations/ru.json index d31d356aea0..406c9b70a1f 100644 --- a/homeassistant/components/knx/translations/ru.json +++ b/homeassistant/components/knx/translations/ru.json @@ -60,19 +60,6 @@ }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0444\u0430\u0439\u043b\u0435 `.knxkeys`." }, - "secure_manual": { - "data": { - "device_authentication": "\u041f\u0430\u0440\u043e\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", - "user_id": "ID \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", - "user_password": "\u041f\u0430\u0440\u043e\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" - }, - "data_description": { - "device_authentication": "\u042d\u0442\u043e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 'IP' \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 ETS.", - "user_id": "\u0427\u0430\u0441\u0442\u043e \u043d\u043e\u043c\u0435\u0440 \u0442\u0443\u043d\u043d\u0435\u043b\u044f +1. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, 'Tunnel 2' \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c User-ID '3'.", - "user_password": "\u041f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0442\u0443\u043d\u043d\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f, \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 'Properties' \u0442\u0443\u043d\u043d\u0435\u043b\u044f \u0432 ETS." - }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e IP Secure." - }, "secure_tunnel_manual": { "data": { "device_authentication": "\u041f\u0430\u0440\u043e\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", @@ -90,7 +77,6 @@ "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 KNX/IP Secure.", "menu_options": { "secure_knxkeys": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b `.knxkeys`, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043a\u043b\u044e\u0447\u0438 IP secure", - "secure_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0438 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e", "secure_tunnel_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0438 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e" } }, @@ -99,12 +85,6 @@ "gateway": "\u0422\u0443\u043d\u043d\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f KNX" }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0448\u043b\u044e\u0437 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430." - }, - "type": { - "data": { - "connection_type": "\u0422\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f KNX" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c.\nAUTOMATIC \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0448\u0438\u043d\u0435 KNX, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044f \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0448\u043b\u044e\u0437\u0430.\nTUNNELING \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a \u0448\u0438\u043d\u0435 KNX, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435.\nROUTING \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a \u0448\u0438\u043d\u0435 KNX, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u044e." } } }, @@ -135,25 +115,6 @@ }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c.\nAUTOMATIC \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0448\u0438\u043d\u0435 KNX, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044f \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0448\u043b\u044e\u0437\u0430.\nTUNNELING \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a \u0448\u0438\u043d\u0435 KNX, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435.\nROUTING \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a \u0448\u0438\u043d\u0435 KNX, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u044e." }, - "init": { - "data": { - "connection_type": "\u0422\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f KNX", - "individual_address": "\u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e", - "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant", - "multicast_group": "\u0413\u0440\u0443\u043f\u043f\u0430 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438", - "multicast_port": "\u041f\u043e\u0440\u0442 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438", - "rate_limit": "\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438", - "state_updater": "\u0421\u0440\u0435\u0434\u0441\u0442\u0432\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f" - }, - "data_description": { - "individual_address": "\u0410\u0434\u0440\u0435\u0441 KNX, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f Home Assistant, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, `0.0.4`", - "local_ip": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 `0.0.0.0` \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f.", - "multicast_group": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: `224.0.23.12`", - "multicast_port": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: `3671`", - "rate_limit": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443.\n\u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f: \u043e\u0442 20 \u0434\u043e 40", - "state_updater": "\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0438\u0437 \u0448\u0438\u043d\u044b KNX. \u0415\u0441\u043b\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e, Home Assistant \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441 \u0448\u0438\u043d\u044b KNX. \u041c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0442\u043c\u0435\u043d\u0435\u043d \u043e\u043f\u0446\u0438\u044f\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 `sync_state`." - } - }, "manual_tunnel": { "data": { "host": "\u0425\u043e\u0441\u0442", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "\u0422\u0443\u043d\u043d\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f KNX", - "host": "\u0425\u043e\u0441\u0442", - "port": "\u041f\u043e\u0440\u0442", - "tunneling_type": "\u0422\u0438\u043f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX" - }, - "data_description": { - "host": "IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP.", - "port": "\u041f\u043e\u0440\u0442 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP." + "gateway": "\u0422\u0443\u043d\u043d\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f KNX" }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0448\u043b\u044e\u0437 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430." } diff --git a/homeassistant/components/knx/translations/sk.json b/homeassistant/components/knx/translations/sk.json index 82e96300c36..183bbc74bdf 100644 --- a/homeassistant/components/knx/translations/sk.json +++ b/homeassistant/components/knx/translations/sk.json @@ -16,12 +16,6 @@ "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" } }, - "secure_manual": { - "data": { - "device_authentication": "Heslo na overenie zariadenia", - "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" - } - }, "secure_tunnel_manual": { "data": { "device_authentication": "Heslo na overenie zariadenia", @@ -38,11 +32,6 @@ "connection_type": "Typ pripojenia KNX" } }, - "init": { - "data": { - "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" - } - }, "manual_tunnel": { "data": { "host": "Hostite\u013e", @@ -61,12 +50,6 @@ "user_id": "ID pou\u017e\u00edvate\u013ea", "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" } - }, - "tunnel": { - "data": { - "host": "Hostite\u013e", - "port": "Port" - } } } } diff --git a/homeassistant/components/knx/translations/sl.json b/homeassistant/components/knx/translations/sl.json index d44b7dbd9cb..6d4ffbc590c 100644 --- a/homeassistant/components/knx/translations/sl.json +++ b/homeassistant/components/knx/translations/sl.json @@ -24,20 +24,5 @@ "description": "Prosimo, izberite prehod s seznama." } } - }, - "options": { - "step": { - "init": { - "data": { - "individual_address": "Privzet individualni naslov" - } - }, - "tunnel": { - "data": { - "host": "Gostitelj", - "port": "Vrata" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/knx/translations/sv.json b/homeassistant/components/knx/translations/sv.json index 8abd3264e18..9ee17f9dec2 100644 --- a/homeassistant/components/knx/translations/sv.json +++ b/homeassistant/components/knx/translations/sv.json @@ -50,24 +50,10 @@ }, "description": "V\u00e4nligen ange informationen f\u00f6r din `.knxkeys`-fil." }, - "secure_manual": { - "data": { - "device_authentication": "L\u00f6senord f\u00f6r enhetsautentisering", - "user_id": "Anv\u00e4ndar-ID", - "user_password": "Anv\u00e4ndarl\u00f6senord" - }, - "data_description": { - "device_authentication": "Detta st\u00e4lls in i 'IP'-panelen i gr\u00e4nssnittet i ETS.", - "user_id": "Detta \u00e4r ofta tunnelnummer +1. S\u00e5 'Tunnel 2' skulle ha anv\u00e4ndar-ID '3'.", - "user_password": "L\u00f6senord f\u00f6r den specifika tunnelanslutningen som anges i panelen \"Egenskaper\" i tunneln i ETS." - }, - "description": "Ange din s\u00e4kra IP-information." - }, "secure_tunneling": { "description": "V\u00e4lj hur du vill konfigurera KNX/IP Secure.", "menu_options": { - "secure_knxkeys": "Anv\u00e4nd en fil `.knxkeys` som inneh\u00e5ller s\u00e4kra IP-nycklar.", - "secure_manual": "Konfigurera s\u00e4kra IP nycklar manuellt" + "secure_knxkeys": "Anv\u00e4nd en fil `.knxkeys` som inneh\u00e5ller s\u00e4kra IP-nycklar." } }, "tunnel": { @@ -75,46 +61,6 @@ "gateway": "KNX-tunnelanslutning" }, "description": "V\u00e4lj en gateway fr\u00e5n listan." - }, - "type": { - "data": { - "connection_type": "KNX anslutningstyp" - }, - "description": "Ange vilken anslutningstyp vi ska anv\u00e4nda f\u00f6r din KNX-anslutning.\n AUTOMATISK - Integrationen tar hand om anslutningen till din KNX Bus genom att utf\u00f6ra en gateway-skanning.\n TUNNELING - Integrationen kommer att ansluta till din KNX-buss via tunnling.\n ROUTING - Integrationen kommer att ansluta till din KNX-buss via routing." - } - } - }, - "options": { - "step": { - "init": { - "data": { - "connection_type": "KNX anslutningstyp", - "individual_address": "Enskild standardadress", - "local_ip": "Lokal IP f\u00f6r Home Assistant", - "multicast_group": "Multicast-grupp", - "multicast_port": "Multicast-port", - "rate_limit": "Hastighetsgr\u00e4ns", - "state_updater": "Tillst\u00e5ndsuppdaterare" - }, - "data_description": { - "individual_address": "KNX-adress som ska anv\u00e4ndas av Home Assistant, t.ex. `0.0.4`", - "local_ip": "Anv\u00e4nd `0.0.0.0.0` f\u00f6r automatisk identifiering.", - "multicast_group": "Anv\u00e4nds f\u00f6r routing och uppt\u00e4ckt. Standard: \"224.0.23.12\".", - "multicast_port": "Anv\u00e4nds f\u00f6r routing och uppt\u00e4ckt. Standard: \"3671\".", - "rate_limit": "Maximalt antal utg\u00e5ende telegram per sekund.\n Rekommenderad: 20 till 40", - "state_updater": "St\u00e4ll in som standard f\u00f6r att l\u00e4sa tillst\u00e5nd fr\u00e5n KNX-bussen. N\u00e4r den \u00e4r inaktiverad kommer Home Assistant inte aktivt att h\u00e4mta entitetstillst\u00e5nd fr\u00e5n KNX-bussen. Kan \u00e5sidos\u00e4ttas av entitetsalternativ \"sync_state\"." - } - }, - "tunnel": { - "data": { - "host": "V\u00e4rd", - "port": "Port", - "tunneling_type": "KNX tunneltyp" - }, - "data_description": { - "host": "IP adress till KNX/IP tunnelingsenhet.", - "port": "Port p\u00e5 KNX/IP tunnelingsenhet." - } } } } diff --git a/homeassistant/components/knx/translations/tr.json b/homeassistant/components/knx/translations/tr.json index 6ed12e70aee..e1ddb01e317 100644 --- a/homeassistant/components/knx/translations/tr.json +++ b/homeassistant/components/knx/translations/tr.json @@ -50,24 +50,10 @@ }, "description": "L\u00fctfen `.knxkeys` dosyan\u0131z i\u00e7in bilgileri girin." }, - "secure_manual": { - "data": { - "device_authentication": "Cihaz do\u011frulama \u015fifresi", - "user_id": "Kullan\u0131c\u0131 Kimli\u011fi", - "user_password": "Kullan\u0131c\u0131 \u015fifresi" - }, - "data_description": { - "device_authentication": "Bu, ETS'deki aray\u00fcz\u00fcn 'IP' panelinde ayarlan\u0131r.", - "user_id": "Bu genellikle t\u00fcnel numaras\u0131 +1'dir. Yani 'T\u00fcnel 2' Kullan\u0131c\u0131 Kimli\u011fi '3' olacakt\u0131r.", - "user_password": "ETS'de t\u00fcnelin '\u00d6zellikler' panelinde ayarlanan belirli t\u00fcnel ba\u011flant\u0131s\u0131 i\u00e7in \u015fifre." - }, - "description": "L\u00fctfen IP g\u00fcvenli bilgilerinizi giriniz." - }, "secure_tunneling": { "description": "KNX/IP Secure'u nas\u0131l yap\u0131land\u0131rmak istedi\u011finizi se\u00e7in.", "menu_options": { - "secure_knxkeys": "IP g\u00fcvenli anahtarlar\u0131 i\u00e7eren bir \".knxkeys\" dosyas\u0131 kullan\u0131n", - "secure_manual": "IP g\u00fcvenli anahtarlar\u0131n\u0131 manuel olarak yap\u0131land\u0131r\u0131n" + "secure_knxkeys": "IP g\u00fcvenli anahtarlar\u0131 i\u00e7eren bir \".knxkeys\" dosyas\u0131 kullan\u0131n" } }, "tunnel": { @@ -75,46 +61,6 @@ "gateway": "KNX T\u00fcnel Ba\u011flant\u0131s\u0131" }, "description": "L\u00fctfen listeden bir a\u011f ge\u00e7idi se\u00e7in." - }, - "type": { - "data": { - "connection_type": "KNX Ba\u011flant\u0131 T\u00fcr\u00fc" - }, - "description": "L\u00fctfen KNX ba\u011flant\u0131n\u0131z i\u00e7in kullanmam\u0131z gereken ba\u011flant\u0131 tipini giriniz.\n OTOMAT\u0130K - Entegrasyon, bir a\u011f ge\u00e7idi taramas\u0131 ger\u00e7ekle\u015ftirerek KNX Bus'\u0131n\u0131za olan ba\u011flant\u0131y\u0131 halleder.\n T\u00dcNELLEME - Entegrasyon, t\u00fcnelleme yoluyla KNX veri yolunuza ba\u011flanacakt\u0131r.\n Y\u00d6NLEND\u0130RME - Entegrasyon, y\u00f6nlendirme yoluyla KNX veri yolunuza ba\u011flanacakt\u0131r." - } - } - }, - "options": { - "step": { - "init": { - "data": { - "connection_type": "KNX Ba\u011flant\u0131 T\u00fcr\u00fc", - "individual_address": "Varsay\u0131lan bireysel adres", - "local_ip": "Home Asistan\u0131n\u0131n Yerel IP'si", - "multicast_group": "\u00c7ok noktaya yay\u0131n grubu", - "multicast_port": "\u00c7ok noktaya yay\u0131n ba\u011flant\u0131 noktas\u0131", - "rate_limit": "H\u0131z s\u0131n\u0131r\u0131", - "state_updater": "Durum g\u00fcncelleyici" - }, - "data_description": { - "individual_address": "Home Assistant taraf\u0131ndan kullan\u0131lacak KNX adresi, \u00f6r. \"0.0.4\"", - "local_ip": "Otomatik ke\u015fif i\u00e7in \"0.0.0.0\"\u0131 kullan\u0131n.", - "multicast_group": "Y\u00f6nlendirme ve ke\u015fif i\u00e7in kullan\u0131l\u0131r. Varsay\u0131lan: \"224.0.23.12\"", - "multicast_port": "Y\u00f6nlendirme ve ke\u015fif i\u00e7in kullan\u0131l\u0131r. Varsay\u0131lan: \"3671\"", - "rate_limit": "Saniyede maksimum giden telegram say\u0131s\u0131.\n \u00d6nerilen: 20 ila 40", - "state_updater": "KNX Bus'tan okuma durumlar\u0131 i\u00e7in varsay\u0131lan\u0131 ayarlay\u0131n. Devre d\u0131\u015f\u0131 b\u0131rak\u0131ld\u0131\u011f\u0131nda, Home Assistant varl\u0131k durumlar\u0131n\u0131 KNX Bus'tan aktif olarak almaz. 'sync_state' varl\u0131k se\u00e7enekleri taraf\u0131ndan ge\u00e7ersiz k\u0131l\u0131nabilir." - } - }, - "tunnel": { - "data": { - "host": "Sunucu", - "port": "Port", - "tunneling_type": "KNX T\u00fcnel Tipi" - }, - "data_description": { - "host": "KNX/IP t\u00fcnelleme cihaz\u0131n\u0131n IP adresi.", - "port": "KNX/IP t\u00fcnelleme cihaz\u0131n\u0131n ba\u011flant\u0131 noktas\u0131." - } } } } diff --git a/homeassistant/components/knx/translations/zh-Hant.json b/homeassistant/components/knx/translations/zh-Hant.json index 90f98a31187..0d5f3a41c4a 100644 --- a/homeassistant/components/knx/translations/zh-Hant.json +++ b/homeassistant/components/knx/translations/zh-Hant.json @@ -60,19 +60,6 @@ }, "description": "\u8acb\u8f38\u5165 `.knxkeys` \u6a94\u6848\u8cc7\u8a0a\u3002" }, - "secure_manual": { - "data": { - "device_authentication": "\u88dd\u7f6e\u8a8d\u8b49\u5bc6\u78bc", - "user_id": "\u4f7f\u7528\u8005 ID", - "user_password": "\u4f7f\u7528\u8005\u5bc6\u78bc" - }, - "data_description": { - "device_authentication": "\u65bc EST \u4ecb\u9762\u4e2d 'IP' \u9762\u677f\u9032\u884c\u8a2d\u5b9a\u3002", - "user_id": "\u901a\u5e38\u70ba\u901a\u9053\u6578 +1\u3002\u56e0\u6b64 'Tunnel 2' \u5c07\u5177\u6709\u4f7f\u7528\u8005 ID '3'\u3002", - "user_password": "\u65bc ETS \u901a\u9053 'Properties' \u9762\u677f\u53ef\u8a2d\u5b9a\u6307\u5b9a\u901a\u9053\u9023\u7dda\u5bc6\u78bc\u3002" - }, - "description": "\u8acb\u8f38\u5165 IP \u52a0\u5bc6\u8cc7\u8a0a\u3002" - }, "secure_tunnel_manual": { "data": { "device_authentication": "\u88dd\u7f6e\u8a8d\u8b49\u5bc6\u78bc", @@ -90,7 +77,6 @@ "description": "\u9078\u64c7\u5982\u4f55\u8a2d\u5b9a KNX/IP \u52a0\u5bc6\u3002", "menu_options": { "secure_knxkeys": "\u4f7f\u7528\u5305\u542b IP \u52a0\u5bc6\u91d1\u8000\u7684 knxkeys \u6a94\u6848", - "secure_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6\u91d1\u8000", "secure_tunnel_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6\u91d1\u8000" } }, @@ -99,12 +85,6 @@ "gateway": "KNX \u901a\u9053\u9023\u7dda" }, "description": "\u8acb\u5f9e\u5217\u8868\u4e2d\u9078\u64c7\u4e00\u7d44\u9598\u9053\u5668\u3002" - }, - "type": { - "data": { - "connection_type": "KNX \u9023\u7dda\u985e\u5225" - }, - "description": "\u8acb\u8f38\u5165 KNX \u9023\u7dda\u6240\u4f7f\u7528\u4e4b\u9023\u7dda\u985e\u5225\u3002 \n \u81ea\u52d5\uff08AUTOMATIC\uff09 - \u6574\u5408\u81ea\u52d5\u85c9\u7531\u9598\u9053\u5668\u6383\u63cf\u5f8c\u8655\u7406\u9023\u7dda\u554f\u984c\u3002\n \u901a\u9053\uff08TUNNELING\uff09 - \u6574\u5408\u5c07\u6703\u900f\u904e\u901a\u9053\u65b9\u5f0f\u8207 KNX Bus \u9032\u884c\u9023\u7dda\u3002\n \u8def\u7531\uff08ROUTING\uff09 - \u6574\u5408\u5c07\u6703\u900f\u904e\u8def\u7531\u65b9\u5f0f\u8207 KNX Bus \u9032\u884c\u9023\u7dda\u3002" } } }, @@ -135,25 +115,6 @@ }, "description": "\u8acb\u8f38\u5165 KNX \u9023\u7dda\u6240\u4f7f\u7528\u4e4b\u9023\u7dda\u985e\u5225\u3002 \n \u81ea\u52d5\uff08AUTOMATIC\uff09 - \u6574\u5408\u81ea\u52d5\u85c9\u7531\u9598\u9053\u5668\u6383\u63cf\u5f8c\u8655\u7406\u9023\u7dda\u554f\u984c\u3002\n \u901a\u9053\uff08TUNNELING\uff09 - \u6574\u5408\u5c07\u6703\u900f\u904e\u901a\u9053\u65b9\u5f0f\u8207 KNX Bus \u9032\u884c\u9023\u7dda\u3002\n \u8def\u7531\uff08ROUTING\uff09 - \u6574\u5408\u5c07\u6703\u900f\u904e\u8def\u7531\u65b9\u5f0f\u8207 KNX Bus \u9032\u884c\u9023\u7dda\u3002" }, - "init": { - "data": { - "connection_type": "KNX \u9023\u7dda\u985e\u5225", - "individual_address": "\u9810\u8a2d\u500b\u5225\u4f4d\u5740", - "local_ip": "Home Assistant \u672c\u5730\u7aef IP", - "multicast_group": "Multicast \u7fa4\u7d44", - "multicast_port": "Multicast \u901a\u8a0a\u57e0", - "rate_limit": "\u983b\u7387\u9650\u5236", - "state_updater": "\u72c0\u614b\u66f4\u65b0\u5668" - }, - "data_description": { - "individual_address": "Home Assistant \u6240\u4f7f\u7528\u4e4b KNX \u4f4d\u5740\u3002\u4f8b\u5982\uff1a`0.0.4`", - "local_ip": "\u4f7f\u7528 `0.0.0.0` \u9032\u884c\u81ea\u52d5\u641c\u7d22\u3002", - "multicast_group": "\u4f7f\u7528\u65bc\u8def\u7531\u8207\u81ea\u52d5\u641c\u7d22\u3002\u9810\u8a2d\u503c\uff1a`224.0.23.12`", - "multicast_port": "\u4f7f\u7528\u65bc\u8def\u7531\u8207\u81ea\u52d5\u641c\u7d22\u3002\u9810\u8a2d\u503c\uff1a`3671`", - "rate_limit": "\u6bcf\u79d2\u6700\u5927 Telegram \u767c\u9001\u91cf\u3002\u5efa\u8b70\uff1a20 - 40", - "state_updater": "\u8a2d\u5b9a\u9810\u8a2d KNX Bus \u8b80\u53d6\u72c0\u614b\u3002\u7576\u95dc\u9589\u6642\u3001Home Assistant \u5c07\u4e0d\u6703\u4e3b\u52d5\u5f9e KNX Bus \u7372\u53d6\u5be6\u9ad4\u72c0\u614b\uff0c\u53ef\u88ab`sync_state` \u5be6\u9ad4\u9078\u9805\u8986\u84cb\u3002" - } - }, "manual_tunnel": { "data": { "host": "\u4e3b\u6a5f\u7aef", @@ -222,14 +183,7 @@ }, "tunnel": { "data": { - "gateway": "KNX \u901a\u9053\u9023\u7dda", - "host": "\u4e3b\u6a5f\u7aef", - "port": "\u901a\u8a0a\u57e0", - "tunneling_type": "KNX \u901a\u9053\u985e\u5225" - }, - "data_description": { - "host": "KNX/IP \u901a\u9053\u88dd\u7f6e IP \u4f4d\u5740\u3002", - "port": "KNX/IP \u901a\u9053\u88dd\u7f6e\u901a\u8a0a\u57e0\u3002" + "gateway": "KNX \u901a\u9053\u9023\u7dda" }, "description": "\u8acb\u5f9e\u5217\u8868\u4e2d\u9078\u64c7\u4e00\u7d44\u9598\u9053\u5668\u3002" } diff --git a/homeassistant/components/landisgyr_heat_meter/translations/bg.json b/homeassistant/components/landisgyr_heat_meter/translations/bg.json index 6120c9152d7..8ae6c2c37ca 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/bg.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/bg.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" }, - "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" - }, "step": { "user": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/ca.json b/homeassistant/components/landisgyr_heat_meter/translations/ca.json index 1ad4d3b9362..aaa12ad2b85 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/ca.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/ca.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "El dispositiu ja est\u00e0 configurat" }, - "error": { - "cannot_connect": "Ha fallat la connexi\u00f3", - "unknown": "Error inesperat" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/cs.json b/homeassistant/components/landisgyr_heat_meter/translations/cs.json index 500211d103c..ec3ffb78c6f 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/cs.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/cs.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno" }, - "error": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/de.json b/homeassistant/components/landisgyr_heat_meter/translations/de.json index e8a48a02c51..3c162b69e3a 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/de.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/de.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert" }, - "error": { - "cannot_connect": "Verbindung fehlgeschlagen", - "unknown": "Unerwarteter Fehler" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/el.json b/homeassistant/components/landisgyr_heat_meter/translations/el.json index 1f7d7b27df9..081888c7a5d 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/el.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/el.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" }, - "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/en.json b/homeassistant/components/landisgyr_heat_meter/translations/en.json index 84caa2819a4..d2e4bff5ae2 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/en.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/en.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Device is already configured" }, - "error": { - "cannot_connect": "Failed to connect", - "unknown": "Unexpected error" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/es.json b/homeassistant/components/landisgyr_heat_meter/translations/es.json index 956cebf852d..dc9fd6c9e1d 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/es.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/es.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "El dispositivo ya est\u00e1 configurado" }, - "error": { - "cannot_connect": "No se pudo conectar", - "unknown": "Error inesperado" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/et.json b/homeassistant/components/landisgyr_heat_meter/translations/et.json index 9553db6f7ca..ce0e6b5d759 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/et.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/et.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Seade on juba h\u00e4\u00e4lestatud" }, - "error": { - "cannot_connect": "\u00dchendamine nurjus", - "unknown": "Ootamatu t\u00f5rge" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/fr.json b/homeassistant/components/landisgyr_heat_meter/translations/fr.json index 42d2fe61555..02c3ec487ad 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/fr.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/fr.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" }, - "error": { - "cannot_connect": "\u00c9chec de connexion", - "unknown": "Erreur inattendue" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/he.json b/homeassistant/components/landisgyr_heat_meter/translations/he.json index 25d9c78d8cf..1c5713312c7 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/he.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/he.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" }, - "error": { - "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/hu.json b/homeassistant/components/landisgyr_heat_meter/translations/hu.json index 030fe6853f2..5e04076bf2a 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/hu.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/hu.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" }, - "error": { - "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/id.json b/homeassistant/components/landisgyr_heat_meter/translations/id.json index 97bb43eb4ba..47102d37e1c 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/id.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/id.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Perangkat sudah dikonfigurasi" }, - "error": { - "cannot_connect": "Gagal terhubung", - "unknown": "Kesalahan yang tidak diharapkan" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/it.json b/homeassistant/components/landisgyr_heat_meter/translations/it.json index 1c320671a40..6b1cd3f8f5c 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/it.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/it.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" }, - "error": { - "cannot_connect": "Impossibile connettersi", - "unknown": "Errore imprevisto" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/ja.json b/homeassistant/components/landisgyr_heat_meter/translations/ja.json index b9ac41b8244..0fd5a1c5752 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/ja.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/ja.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" }, - "error": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/nb.json b/homeassistant/components/landisgyr_heat_meter/translations/nb.json deleted file mode 100644 index a22f7eef3d6..00000000000 --- a/homeassistant/components/landisgyr_heat_meter/translations/nb.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "config": { - "error": { - "unknown": "Uventet feil" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/landisgyr_heat_meter/translations/nl.json b/homeassistant/components/landisgyr_heat_meter/translations/nl.json index 67eea59125f..436b82d20e1 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/nl.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/nl.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Apparaat is al geconfigureerd" }, - "error": { - "cannot_connect": "Kan geen verbinding maken", - "unknown": "Onverwachte fout" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/no.json b/homeassistant/components/landisgyr_heat_meter/translations/no.json index 7c3a6e348f3..b583d0b86e0 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/no.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/no.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Enheten er allerede konfigurert" }, - "error": { - "cannot_connect": "Tilkobling mislyktes", - "unknown": "Uventet feil" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/pl.json b/homeassistant/components/landisgyr_heat_meter/translations/pl.json index 3478a5c0c2b..f3f5299624d 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/pl.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/pl.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" }, - "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "unknown": "Nieoczekiwany b\u0142\u0105d" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/pt-BR.json b/homeassistant/components/landisgyr_heat_meter/translations/pt-BR.json index 97cced694cf..acb8b394232 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/pt-BR.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/pt-BR.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" }, - "error": { - "cannot_connect": "Falha ao conectar", - "unknown": "Erro inesperado" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/ru.json b/homeassistant/components/landisgyr_heat_meter/translations/ru.json index b4977ecdc39..5c358e82b11 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/ru.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/ru.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." }, - "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/sv.json b/homeassistant/components/landisgyr_heat_meter/translations/sv.json index 4fde3cf2755..99521c6c4d5 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/sv.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/sv.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Enheten \u00e4r redan konfigurerad" }, - "error": { - "cannot_connect": "Det gick inte att ansluta.", - "unknown": "Ov\u00e4ntat fel" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/tr.json b/homeassistant/components/landisgyr_heat_meter/translations/tr.json index 1ff9d1c85d0..898ed7c5025 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/tr.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/tr.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" }, - "error": { - "cannot_connect": "Ba\u011flanma hatas\u0131", - "unknown": "Beklenmeyen hata" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/landisgyr_heat_meter/translations/zh-Hant.json b/homeassistant/components/landisgyr_heat_meter/translations/zh-Hant.json index 718576d8a26..5a6d25c5a2a 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/zh-Hant.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/zh-Hant.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" }, - "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4" - }, "step": { "setup_serial_manual_path": { "data": { diff --git a/homeassistant/components/lg_soundbar/translations/ar.json b/homeassistant/components/lg_soundbar/translations/ar.json index 3fc833f41f1..0c025d8ce76 100644 --- a/homeassistant/components/lg_soundbar/translations/ar.json +++ b/homeassistant/components/lg_soundbar/translations/ar.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "\u0627\u0644\u062e\u062f\u0645\u0629 \u062a\u0645 \u062a\u0647\u064a\u0623\u062a\u0647\u0627 \u0645\u0633\u0628\u0642\u0627", - "existing_instance_updated": "\u062a\u062d\u062f\u064a\u062b \u0627\u0644\u062a\u0643\u0648\u064a\u0646 \u0627\u0644\u062d\u0627\u0644\u064a" + "already_configured": "\u0627\u0644\u062e\u062f\u0645\u0629 \u062a\u0645 \u062a\u0647\u064a\u0623\u062a\u0647\u0627 \u0645\u0633\u0628\u0642\u0627" }, "error": { "cannot_connect": "\u0641\u0634\u0644 \u0641\u064a \u0627\u0644\u0627\u062a\u0635\u0627\u0644" diff --git a/homeassistant/components/lg_soundbar/translations/ca.json b/homeassistant/components/lg_soundbar/translations/ca.json index 3d1b6f3bc98..b49df6df970 100644 --- a/homeassistant/components/lg_soundbar/translations/ca.json +++ b/homeassistant/components/lg_soundbar/translations/ca.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "El dispositiu ja est\u00e0 configurat", - "existing_instance_updated": "S'ha actualitzat la configuraci\u00f3 existent." + "already_configured": "El dispositiu ja est\u00e0 configurat" }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3" diff --git a/homeassistant/components/lg_soundbar/translations/de.json b/homeassistant/components/lg_soundbar/translations/de.json index b8458a653aa..4c1588bfcad 100644 --- a/homeassistant/components/lg_soundbar/translations/de.json +++ b/homeassistant/components/lg_soundbar/translations/de.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Ger\u00e4t ist bereits konfiguriert", - "existing_instance_updated": "Bestehende Konfiguration wurde aktualisiert." + "already_configured": "Ger\u00e4t ist bereits konfiguriert" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen" diff --git a/homeassistant/components/lg_soundbar/translations/el.json b/homeassistant/components/lg_soundbar/translations/el.json index 7fa31f8fe9d..687f872ea14 100644 --- a/homeassistant/components/lg_soundbar/translations/el.json +++ b/homeassistant/components/lg_soundbar/translations/el.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af", - "existing_instance_updated": "\u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5 \u03b7 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7." + "already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af" }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" diff --git a/homeassistant/components/lg_soundbar/translations/en.json b/homeassistant/components/lg_soundbar/translations/en.json index cbf35dc2976..10441d21536 100644 --- a/homeassistant/components/lg_soundbar/translations/en.json +++ b/homeassistant/components/lg_soundbar/translations/en.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Device is already configured", - "existing_instance_updated": "Updated existing configuration." + "already_configured": "Device is already configured" }, "error": { "cannot_connect": "Failed to connect" diff --git a/homeassistant/components/lg_soundbar/translations/es.json b/homeassistant/components/lg_soundbar/translations/es.json index dc78afa232b..b9b39a74d9d 100644 --- a/homeassistant/components/lg_soundbar/translations/es.json +++ b/homeassistant/components/lg_soundbar/translations/es.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "El dispositivo ya est\u00e1 configurado", - "existing_instance_updated": "Se ha actualizado la configuraci\u00f3n existente." + "already_configured": "El dispositivo ya est\u00e1 configurado" }, "error": { "cannot_connect": "No se pudo conectar" diff --git a/homeassistant/components/lg_soundbar/translations/et.json b/homeassistant/components/lg_soundbar/translations/et.json index f5c73126124..2ee852479c9 100644 --- a/homeassistant/components/lg_soundbar/translations/et.json +++ b/homeassistant/components/lg_soundbar/translations/et.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Seade on juba h\u00e4\u00e4lestatud", - "existing_instance_updated": "V\u00e4rskendati olemasolevat konfiguratsiooni." + "already_configured": "Seade on juba h\u00e4\u00e4lestatud" }, "error": { "cannot_connect": "\u00dchendamine nurjus" diff --git a/homeassistant/components/lg_soundbar/translations/fr.json b/homeassistant/components/lg_soundbar/translations/fr.json index 5f6977ed3ab..33580a8eae3 100644 --- a/homeassistant/components/lg_soundbar/translations/fr.json +++ b/homeassistant/components/lg_soundbar/translations/fr.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", - "existing_instance_updated": "La configuration existante a \u00e9t\u00e9 mise \u00e0 jour." + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" }, "error": { "cannot_connect": "\u00c9chec de connexion" diff --git a/homeassistant/components/lg_soundbar/translations/hu.json b/homeassistant/components/lg_soundbar/translations/hu.json index c39033d273a..382c4f2d8d3 100644 --- a/homeassistant/components/lg_soundbar/translations/hu.json +++ b/homeassistant/components/lg_soundbar/translations/hu.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van", - "existing_instance_updated": "A megl\u00e9v\u0151 konfigur\u00e1ci\u00f3 friss\u00edtve." + "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van" }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s" diff --git a/homeassistant/components/lg_soundbar/translations/id.json b/homeassistant/components/lg_soundbar/translations/id.json index 3f6d9ea8f81..c8236f5ec73 100644 --- a/homeassistant/components/lg_soundbar/translations/id.json +++ b/homeassistant/components/lg_soundbar/translations/id.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Perangkat sudah dikonfigurasi", - "existing_instance_updated": "Memperbarui konfigurasi yang ada." + "already_configured": "Perangkat sudah dikonfigurasi" }, "error": { "cannot_connect": "Gagal terhubung" diff --git a/homeassistant/components/lg_soundbar/translations/it.json b/homeassistant/components/lg_soundbar/translations/it.json index 30a7b328038..9cb86e4ee5a 100644 --- a/homeassistant/components/lg_soundbar/translations/it.json +++ b/homeassistant/components/lg_soundbar/translations/it.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", - "existing_instance_updated": "Configurazione esistente aggiornata." + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" }, "error": { "cannot_connect": "Impossibile connettersi" diff --git a/homeassistant/components/lg_soundbar/translations/ja.json b/homeassistant/components/lg_soundbar/translations/ja.json index cd40cd88044..4be154a597a 100644 --- a/homeassistant/components/lg_soundbar/translations/ja.json +++ b/homeassistant/components/lg_soundbar/translations/ja.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "\u30b5\u30fc\u30d3\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", - "existing_instance_updated": "\u65e2\u5b58\u306e\u69cb\u6210\u3092\u66f4\u65b0\u3057\u307e\u3057\u305f\u3002" + "already_configured": "\u30b5\u30fc\u30d3\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" diff --git a/homeassistant/components/lg_soundbar/translations/nl.json b/homeassistant/components/lg_soundbar/translations/nl.json index 7345479d97a..e0c21697099 100644 --- a/homeassistant/components/lg_soundbar/translations/nl.json +++ b/homeassistant/components/lg_soundbar/translations/nl.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Dienst is al geconfigureerd", - "existing_instance_updated": "Bestaande configuratie bijgewerkt." + "already_configured": "Dienst is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken" diff --git a/homeassistant/components/lg_soundbar/translations/no.json b/homeassistant/components/lg_soundbar/translations/no.json index 58d4c11916b..7457ffad8a4 100644 --- a/homeassistant/components/lg_soundbar/translations/no.json +++ b/homeassistant/components/lg_soundbar/translations/no.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Enheten er allerede konfigurert", - "existing_instance_updated": "Oppdatert eksisterende konfigurasjon." + "already_configured": "Enheten er allerede konfigurert" }, "error": { "cannot_connect": "Tilkobling mislyktes" diff --git a/homeassistant/components/lg_soundbar/translations/pl.json b/homeassistant/components/lg_soundbar/translations/pl.json index 4a6b3d077df..1b4e47469aa 100644 --- a/homeassistant/components/lg_soundbar/translations/pl.json +++ b/homeassistant/components/lg_soundbar/translations/pl.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", - "existing_instance_updated": "Zaktualizowano istniej\u0105c\u0105 konfiguracj\u0119" + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" diff --git a/homeassistant/components/lg_soundbar/translations/pt-BR.json b/homeassistant/components/lg_soundbar/translations/pt-BR.json index 60e047a8acf..fb9b1f4c79e 100644 --- a/homeassistant/components/lg_soundbar/translations/pt-BR.json +++ b/homeassistant/components/lg_soundbar/translations/pt-BR.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", - "existing_instance_updated": "Configura\u00e7\u00e3o existente atualizada." + "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" }, "error": { "cannot_connect": "Falha ao conectar" diff --git a/homeassistant/components/lg_soundbar/translations/ru.json b/homeassistant/components/lg_soundbar/translations/ru.json index 38f8ad9f92a..2475f72e796 100644 --- a/homeassistant/components/lg_soundbar/translations/ru.json +++ b/homeassistant/components/lg_soundbar/translations/ru.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", - "existing_instance_updated": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430." + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." diff --git a/homeassistant/components/lg_soundbar/translations/sk.json b/homeassistant/components/lg_soundbar/translations/sk.json index 92d25505bbc..3c3d27a6689 100644 --- a/homeassistant/components/lg_soundbar/translations/sk.json +++ b/homeassistant/components/lg_soundbar/translations/sk.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "existing_instance_updated": "Aktualizovan\u00e1 existuj\u00faca konfigur\u00e1cia." + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/sv.json b/homeassistant/components/lg_soundbar/translations/sv.json index 9b8ff6ea1aa..d8dcec22289 100644 --- a/homeassistant/components/lg_soundbar/translations/sv.json +++ b/homeassistant/components/lg_soundbar/translations/sv.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Tj\u00e4nsten \u00e4r redan konfigurerad", - "existing_instance_updated": "Uppdaterade existerande konfiguration." + "already_configured": "Tj\u00e4nsten \u00e4r redan konfigurerad" }, "error": { "cannot_connect": "Det gick inte att ansluta." diff --git a/homeassistant/components/lg_soundbar/translations/tr.json b/homeassistant/components/lg_soundbar/translations/tr.json index c80f5540643..181980a8917 100644 --- a/homeassistant/components/lg_soundbar/translations/tr.json +++ b/homeassistant/components/lg_soundbar/translations/tr.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", - "existing_instance_updated": "Mevcut yap\u0131land\u0131rma g\u00fcncellendi." + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131" diff --git a/homeassistant/components/lg_soundbar/translations/zh-Hant.json b/homeassistant/components/lg_soundbar/translations/zh-Hant.json index 8680a863901..7582133c92f 100644 --- a/homeassistant/components/lg_soundbar/translations/zh-Hant.json +++ b/homeassistant/components/lg_soundbar/translations/zh-Hant.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", - "existing_instance_updated": "\u5df2\u66f4\u65b0\u73fe\u6709\u8a2d\u5b9a\u3002" + "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557" diff --git a/homeassistant/components/lidarr/translations/ca.json b/homeassistant/components/lidarr/translations/ca.json index 9cc30d6f893..7718ae1939b 100644 --- a/homeassistant/components/lidarr/translations/ca.json +++ b/homeassistant/components/lidarr/translations/ca.json @@ -28,15 +28,5 @@ "description": "La clau API es pot recuperar autom\u00e0ticament si les credencials d'inici de sessi\u00f3 no s'han establert a l'aplicaci\u00f3.\nLa teva clau API es pot trobar a Configuraci\u00f3 ('Settings') > General, a la interf\u00edcie web de Lidarr." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "Nombre m\u00e0xim de registres a mostrar a la cua i a desitjats", - "upcoming_days": "Nombre dies propers a mostrar al calendari" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/de.json b/homeassistant/components/lidarr/translations/de.json index a51b9c24a2f..e586992af2a 100644 --- a/homeassistant/components/lidarr/translations/de.json +++ b/homeassistant/components/lidarr/translations/de.json @@ -28,15 +28,5 @@ "description": "Der API-Schl\u00fcssel kann automatisch abgerufen werden, wenn in der Anwendung keine Anmeldeinformationen festgelegt wurden.\nDeinen API-Schl\u00fcssel findest du unter Einstellungen > Allgemein in der Lidarr-Web-Benutzeroberfl\u00e4che." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "Anzahl der maximal anzuzeigenden Datens\u00e4tze f\u00fcr Gesucht und Warteschlange", - "upcoming_days": "Anzahl der kommenden Tage, die im Kalender angezeigt werden sollen" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/el.json b/homeassistant/components/lidarr/translations/el.json index 01a54904034..c3ef8b46f64 100644 --- a/homeassistant/components/lidarr/translations/el.json +++ b/homeassistant/components/lidarr/translations/el.json @@ -28,15 +28,5 @@ "description": "\u03a4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b1\u03bd\u03b1\u03ba\u03c4\u03b7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03b5\u03ac\u03bd \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03b4\u03b5\u03bd \u03ad\u03c7\u03bf\u03c5\u03bd \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae.\n\u03a4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b2\u03c1\u03b5\u03b8\u03b5\u03af \u03c3\u03c4\u03b9\u03c2 \u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 > \u0393\u03b5\u03bd\u03b9\u03ba\u03ac \u03c3\u03c4\u03bf Lidarr Web UI." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "\u0391\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03bc\u03ad\u03b3\u03b9\u03c3\u03c4\u03c9\u03bd \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ce\u03bd \u03b3\u03b9\u03b1 \u03b5\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03c3\u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03b8\u03c5\u03bc\u03b7\u03c4\u03ae \u03ba\u03b1\u03b9 \u03c3\u03c4\u03b7\u03bd \u03bf\u03c5\u03c1\u03ac", - "upcoming_days": "\u0391\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03b5\u03c0\u03b5\u03c1\u03c7\u03cc\u03bc\u03b5\u03bd\u03c9\u03bd \u03b7\u03bc\u03b5\u03c1\u03ce\u03bd \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03b5\u03bc\u03c6\u03b1\u03bd\u03af\u03b6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf \u03b7\u03bc\u03b5\u03c1\u03bf\u03bb\u03cc\u03b3\u03b9\u03bf" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/en.json b/homeassistant/components/lidarr/translations/en.json index 0e0475d25cd..cdb21be7fb2 100644 --- a/homeassistant/components/lidarr/translations/en.json +++ b/homeassistant/components/lidarr/translations/en.json @@ -28,15 +28,5 @@ "description": "API key can be retrieved automatically if login credentials were not set in application.\nYour API key can be found in Settings > General in the Lidarr Web UI." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "Number of maximum records to display on wanted and queue", - "upcoming_days": "Number of upcoming days to display on calendar" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/es.json b/homeassistant/components/lidarr/translations/es.json index 071ee1312ec..ea4f66f431d 100644 --- a/homeassistant/components/lidarr/translations/es.json +++ b/homeassistant/components/lidarr/translations/es.json @@ -28,15 +28,5 @@ "description": "La clave API se puede recuperar autom\u00e1ticamente si las credenciales de inicio de sesi\u00f3n no se configuraron en la aplicaci\u00f3n.\nTu clave API se puede encontrar en Configuraci\u00f3n > General en la IU web de Lidarr." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "N\u00famero m\u00e1ximo de registros para mostrar en b\u00fasqueda y cola", - "upcoming_days": "N\u00famero de pr\u00f3ximos d\u00edas para mostrar en el calendario" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/et.json b/homeassistant/components/lidarr/translations/et.json index 0a88c87659f..a28f5b8435b 100644 --- a/homeassistant/components/lidarr/translations/et.json +++ b/homeassistant/components/lidarr/translations/et.json @@ -28,15 +28,5 @@ "description": "API-v\u00f5tme saab automaatselt alla laadida, kui rakenduses pole sisselogimismandaate m\u00e4\u00e4ratud.\n API-v\u00f5tme leiate Lidarri veebikasutajaliidese jaotisest Seaded > \u00dcldine." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "Soovitud ja j\u00e4rjekorras kuvatavate kirjete maksimaalne arv", - "upcoming_days": "Kalendris kuvatavate eelseisvate p\u00e4evade arv" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/fr.json b/homeassistant/components/lidarr/translations/fr.json index 9eb6bf92cd2..2e2c289800b 100644 --- a/homeassistant/components/lidarr/translations/fr.json +++ b/homeassistant/components/lidarr/translations/fr.json @@ -27,15 +27,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "Nombre maximal d'enregistrements \u00e0 afficher sur la recherche et la file d'attente", - "upcoming_days": "Nombre de jours \u00e0 venir \u00e0 afficher sur le calendrier" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/hu.json b/homeassistant/components/lidarr/translations/hu.json index a47d23df43c..7981b025ce0 100644 --- a/homeassistant/components/lidarr/translations/hu.json +++ b/homeassistant/components/lidarr/translations/hu.json @@ -28,15 +28,5 @@ "description": "Az API-kulcs automatikusan lek\u00e9rhet\u0151, ha a bejelentkez\u00e9si hiteles\u00edt\u0151 adatok nem lettek be\u00e1ll\u00edtva az alkalmaz\u00e1sban.\nAz API-kulcs a Lidarr webes felhaszn\u00e1l\u00f3i fel\u00fclet Be\u00e1ll\u00edt\u00e1sok > \u00c1ltal\u00e1nos men\u00fcpontj\u00e1ban tal\u00e1lhat\u00f3." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "A keresett \u00e9s a v\u00e1r\u00f3list\u00e1n megjelen\u00edtend\u0151 maxim\u00e1lis rekordok sz\u00e1ma", - "upcoming_days": "A napt\u00e1rban megjelen\u00edtend\u0151 k\u00f6vetkez\u0151 napok sz\u00e1ma" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/id.json b/homeassistant/components/lidarr/translations/id.json index 1514a016dc1..6a38e128ffa 100644 --- a/homeassistant/components/lidarr/translations/id.json +++ b/homeassistant/components/lidarr/translations/id.json @@ -28,15 +28,5 @@ "description": "Kunci API dapat diambil secara otomatis jika kredensial login tidak diatur dalam aplikasi.\nKunci API Anda dapat ditemukan di Settings > General di antarmuka web Lidarr." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "Jumlah data maksimum untuk ditampilkan pada wanted dan queue", - "upcoming_days": "Jumlah hari yang akan datang untuk ditampilkan pada kalender" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/it.json b/homeassistant/components/lidarr/translations/it.json index b040d3eeb00..45a621e036b 100644 --- a/homeassistant/components/lidarr/translations/it.json +++ b/homeassistant/components/lidarr/translations/it.json @@ -28,15 +28,5 @@ "description": "La chiave API pu\u00f2 essere recuperata automaticamente se le credenziali di accesso non sono state impostate nell'applicazione.\nLa tua chiave API pu\u00f2 essere trovata in Impostazioni > Generali nell'interfaccia utente web di Lidarr." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "Numero massimo di record da visualizzare su ricercato e coda", - "upcoming_days": "Numero di giorni successivi da visualizzare sul calendario" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/no.json b/homeassistant/components/lidarr/translations/no.json index 23c63f562b5..d7a2094302c 100644 --- a/homeassistant/components/lidarr/translations/no.json +++ b/homeassistant/components/lidarr/translations/no.json @@ -28,15 +28,5 @@ "description": "API-n\u00f8kkel kan hentes automatisk hvis p\u00e5loggingsinformasjon ikke ble angitt i applikasjonen.\n API-n\u00f8kkelen din finner du i Innstillinger > Generelt i Lidarr Web UI." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "Antall maksimale poster \u00e5 vise p\u00e5 \u00f8nsket og k\u00f8", - "upcoming_days": "Antall kommende dager som skal vises i kalenderen" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/pl.json b/homeassistant/components/lidarr/translations/pl.json index 33d0deee79b..f329eb9efe8 100644 --- a/homeassistant/components/lidarr/translations/pl.json +++ b/homeassistant/components/lidarr/translations/pl.json @@ -28,15 +28,5 @@ "description": "Klucz API mo\u017ce zosta\u0107 pobrany automatycznie, je\u015bli dane logowania nie zosta\u0142y ustawione w aplikacji.\nTw\u00f3j klucz API mo\u017cesz znale\u017a\u0107 w Ustawienia > Og\u00f3lne, na swoim koncie Lidarr." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "Maksymalna liczba wpis\u00f3w do wy\u015bwietlenia w poszukiwanych i w kolejce", - "upcoming_days": "Liczba nadchodz\u0105cych dni do wy\u015bwietlenia w kalendarzu" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/pt-BR.json b/homeassistant/components/lidarr/translations/pt-BR.json index 5d9b99704c4..6a46d2d7f78 100644 --- a/homeassistant/components/lidarr/translations/pt-BR.json +++ b/homeassistant/components/lidarr/translations/pt-BR.json @@ -28,15 +28,5 @@ "description": "A chave de API pode ser recuperada automaticamente se as credenciais de login n\u00e3o tiverem sido definidas no aplicativo.\n Sua chave de API pode ser encontrada em Configura\u00e7\u00f5es > Geral na IU da Web do Lidarr." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "N\u00famero m\u00e1ximo de registros a serem exibidos em desejados e em fila", - "upcoming_days": "N\u00famero de pr\u00f3ximos dias a serem exibidos no calend\u00e1rio" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/ru.json b/homeassistant/components/lidarr/translations/ru.json index afda2835228..cf319489331 100644 --- a/homeassistant/components/lidarr/translations/ru.json +++ b/homeassistant/components/lidarr/translations/ru.json @@ -28,15 +28,5 @@ "description": "\u041a\u043b\u044e\u0447 API \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438, \u0435\u0441\u043b\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u043d\u0435 \u0431\u044b\u043b\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438.\n\u0412\u0430\u0448 \u043a\u043b\u044e\u0447 API \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u00ab\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 > \u00ab\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435\u00bb \u0432 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 Lidarr." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u043f\u043e\u0438\u0441\u043a\u0435 \u0438 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u0438", - "upcoming_days": "\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u043e\u044f\u0449\u0438\u0445 \u0434\u043d\u0435\u0439 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u0435" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/sv.json b/homeassistant/components/lidarr/translations/sv.json index 6e87010feae..e5022336cb7 100644 --- a/homeassistant/components/lidarr/translations/sv.json +++ b/homeassistant/components/lidarr/translations/sv.json @@ -28,15 +28,5 @@ "description": "API-nyckel kan h\u00e4mtas automatiskt om inloggningsuppgifter inte st\u00e4llts in i applikationen.\n Din API-nyckel finns i Inst\u00e4llningar > Allm\u00e4nt i Lidarr Web UI." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "Antal maximala poster att visa p\u00e5 \u00f6nskad och k\u00f6", - "upcoming_days": "Antal kommande dagar att visa i kalendern" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/tr.json b/homeassistant/components/lidarr/translations/tr.json index 39785efb9b0..2bc8e6239cf 100644 --- a/homeassistant/components/lidarr/translations/tr.json +++ b/homeassistant/components/lidarr/translations/tr.json @@ -28,15 +28,5 @@ "description": "Giri\u015f kimlik bilgileri uygulamada ayarlanmad\u0131ysa API anahtar\u0131 otomatik olarak al\u0131nabilir.\n API anahtar\u0131n\u0131z, Lidarr Web Kullan\u0131c\u0131 Aray\u00fcz\u00fcndeki Ayarlar > Genel b\u00f6l\u00fcm\u00fcnde bulunabilir." } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "Aranan ve kuyrukta g\u00f6r\u00fcnt\u00fclenecek maksimum kay\u0131t say\u0131s\u0131", - "upcoming_days": "Takvimde g\u00f6r\u00fcnt\u00fclenecek yakla\u015fan g\u00fcn say\u0131s\u0131" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/zh-Hant.json b/homeassistant/components/lidarr/translations/zh-Hant.json index d4d5b860b20..eaac487dfd3 100644 --- a/homeassistant/components/lidarr/translations/zh-Hant.json +++ b/homeassistant/components/lidarr/translations/zh-Hant.json @@ -28,15 +28,5 @@ "description": "\u5047\u5982\u6c92\u6709\u65bc\u61c9\u7528\u7a0b\u5f0f\u4e2d\u8a2d\u5b9a\u767b\u5165\u6191\u8b49\uff0c\u5247\u53ef\u4ee5\u81ea\u52d5\u53d6\u5f97 API \u91d1\u9470\u3002\n\u91d1\u9470\u53ef\u4ee5\u65bc Lidarr Web \u4ecb\u9762\u4e2d\u8a2d\u5b9a\uff08Settings\uff09 > \u4e00\u822c\uff08General\uff09\u4e2d\u53d6\u5f97\u3002" } } - }, - "options": { - "step": { - "init": { - "data": { - "max_records": "\u986f\u793a\u60f3\u8981\u8207\u6392\u968a\u6700\u9ad8\u7d00\u9304\u6578\u76ee", - "upcoming_days": "\u5373\u5c07\u5230\u4f86\u884c\u4e8b\u66c6\u986f\u793a\u5929\u6578" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/life360/translations/bg.json b/homeassistant/components/life360/translations/bg.json index cf5cca16dd3..7e4d5848bd3 100644 --- a/homeassistant/components/life360/translations/bg.json +++ b/homeassistant/components/life360/translations/bg.json @@ -5,13 +5,9 @@ "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, - "create_entry": { - "default": "\u0417\u0430 \u0434\u0430 \u0437\u0430\u0434\u0430\u0434\u0435\u0442\u0435 \u0440\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438 \u043e\u043f\u0446\u0438\u0438, \u0432\u0438\u0436\u0442\u0435 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043d\u0430 Life360]({docs_url})." - }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", - "invalid_username": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { @@ -26,7 +22,6 @@ "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" }, - "description": "\u0417\u0430 \u0434\u0430 \u0437\u0430\u0434\u0430\u0434\u0435\u0442\u0435 \u0440\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438 \u043e\u043f\u0446\u0438\u0438, \u0432\u0438\u0436\u0442\u0435 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043d\u0430 Life360]({docs_url}). \u041f\u0440\u0435\u043f\u043e\u0440\u044a\u0447\u0438\u0442\u0435\u043b\u043d\u043e \u0435 \u0434\u0430 \u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442\u0435 \u0442\u043e\u0432\u0430 \u043f\u0440\u0435\u0434\u0438 \u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u0435 \u043f\u0440\u043e\u0444\u0438\u043b\u0438.", "title": "\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0437\u0430 Life360 \u043f\u0440\u043e\u0444\u0438\u043b" } } diff --git a/homeassistant/components/life360/translations/ca.json b/homeassistant/components/life360/translations/ca.json index f6b7a081863..04f1ec16dfb 100644 --- a/homeassistant/components/life360/translations/ca.json +++ b/homeassistant/components/life360/translations/ca.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "El compte ja est\u00e0 configurat", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", - "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament", - "unknown": "Error inesperat" - }, - "create_entry": { - "default": "Per configurar les opcions avan\u00e7ades mira la [documentaci\u00f3 de Life360]({docs_url})." + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" }, "error": { "already_configured": "El compte ja est\u00e0 configurat", "cannot_connect": "Ha fallat la connexi\u00f3", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", - "invalid_username": "Nom d'usuari incorrecte", "unknown": "Error inesperat" }, "step": { @@ -28,7 +23,6 @@ "password": "Contrasenya", "username": "Nom d'usuari" }, - "description": "Per configurar les opcions avan\u00e7ades mira la [documentaci\u00f3 de Life360]({docs_url}). Pot ser que ho hagis de fer abans d'afegir cap compte.", "title": "Configuraci\u00f3 del compte Life360" } } diff --git a/homeassistant/components/life360/translations/cs.json b/homeassistant/components/life360/translations/cs.json index 89e4299178d..a490d12695d 100644 --- a/homeassistant/components/life360/translations/cs.json +++ b/homeassistant/components/life360/translations/cs.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "\u00da\u010det je ji\u017e nastaven", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", - "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9", - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" - }, - "create_entry": { - "default": "Chcete-li nastavit pokro\u010dil\u00e9 mo\u017enosti, pod\u00edvejte se do [dokumentace Life360]({docs_url})." + "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "error": { "already_configured": "\u00da\u010det je ji\u017e nastaven", "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", - "invalid_username": "Neplatn\u00e9 u\u017eivatelsk\u00e9 jm\u00e9no", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { @@ -28,7 +23,6 @@ "password": "Heslo", "username": "U\u017eivatelsk\u00e9 jm\u00e9no" }, - "description": "Chcete-li nastavit pokro\u010dil\u00e9 mo\u017enosti, pod\u00edvejte se do [dokumentace Life360]({docs_url}). Mo\u017en\u00e1 to budete cht\u00edt ud\u011blat p\u0159ed p\u0159id\u00e1n\u00edm \u00fa\u010dtu.", "title": "Informace o \u00fa\u010dtu Life360" } } diff --git a/homeassistant/components/life360/translations/da.json b/homeassistant/components/life360/translations/da.json index 71ce5215f25..6f5fd5164cc 100644 --- a/homeassistant/components/life360/translations/da.json +++ b/homeassistant/components/life360/translations/da.json @@ -1,18 +1,11 @@ { "config": { - "create_entry": { - "default": "Hvis du vil angive avancerede indstillinger skal du se [Life360 dokumentation]({docs_url})." - }, - "error": { - "invalid_username": "Ugyldigt brugernavn" - }, "step": { "user": { "data": { "password": "Adgangskode", "username": "Brugernavn" }, - "description": "Hvis du vil angive avancerede indstillinger skal du se [Life360 dokumentation]({docs_url}).\nDu \u00f8nsker m\u00e5ske at g\u00f8re dette f\u00f8r du tilf\u00f8jer konti.", "title": "Life360-kontooplysninger" } } diff --git a/homeassistant/components/life360/translations/de.json b/homeassistant/components/life360/translations/de.json index 9e6e819a179..e084616c413 100644 --- a/homeassistant/components/life360/translations/de.json +++ b/homeassistant/components/life360/translations/de.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "Konto wurde bereits konfiguriert", "invalid_auth": "Ung\u00fcltige Authentifizierung", - "reauth_successful": "Die erneute Authentifizierung war erfolgreich", - "unknown": "Unerwarteter Fehler" - }, - "create_entry": { - "default": "M\u00f6gliche erweiterte Einstellungen finden sich unter [Life360-Dokumentation]({docs_url})." + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { "already_configured": "Konto wurde bereits konfiguriert", "cannot_connect": "Verbindung fehlgeschlagen", "invalid_auth": "Ung\u00fcltige Authentifizierung", - "invalid_username": "Ung\u00fcltiger Benutzername", "unknown": "Unerwarteter Fehler" }, "step": { @@ -28,7 +23,6 @@ "password": "Passwort", "username": "Benutzername" }, - "description": "Erweiterte Optionen sind in der [Life360-Dokumentation]({docs_url}) zu finden.\nDies sollte vor dem Hinzuf\u00fcgen von Kontoinformationen getan werden.", "title": "Life360-Konto konfigurieren" } } diff --git a/homeassistant/components/life360/translations/el.json b/homeassistant/components/life360/translations/el.json index f0db8e10ed6..b9105c05200 100644 --- a/homeassistant/components/life360/translations/el.json +++ b/homeassistant/components/life360/translations/el.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", - "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", - "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" - }, - "create_entry": { - "default": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03bf\u03c1\u03af\u03c3\u03b5\u03c4\u03b5 \u03c0\u03c1\u03bf\u03b7\u03b3\u03bc\u03ad\u03bd\u03b5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2, \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd [\u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 Life360]({docs_url})." + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" }, "error": { "already_configured": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", - "invalid_username": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "step": { @@ -28,7 +23,6 @@ "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" }, - "description": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03bf\u03c1\u03af\u03c3\u03b5\u03c4\u03b5 \u03c0\u03c1\u03bf\u03b7\u03b3\u03bc\u03ad\u03bd\u03b5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2, \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd [\u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 Life360]({docs_url}).\n\u039c\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c4\u03bf \u03ba\u03ac\u03bd\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c0\u03c1\u03b9\u03bd \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd\u03c2.", "title": "\u03a0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd Life360" } } diff --git a/homeassistant/components/life360/translations/en.json b/homeassistant/components/life360/translations/en.json index 4e7ff35c814..5547b2d16ef 100644 --- a/homeassistant/components/life360/translations/en.json +++ b/homeassistant/components/life360/translations/en.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "Account is already configured", "invalid_auth": "Invalid authentication", - "reauth_successful": "Re-authentication was successful", - "unknown": "Unexpected error" - }, - "create_entry": { - "default": "To set advanced options, see [Life360 documentation]({docs_url})." + "reauth_successful": "Re-authentication was successful" }, "error": { "already_configured": "Account is already configured", "cannot_connect": "Failed to connect", "invalid_auth": "Invalid authentication", - "invalid_username": "Invalid username", "unknown": "Unexpected error" }, "step": { @@ -28,7 +23,6 @@ "password": "Password", "username": "Username" }, - "description": "To set advanced options, see [Life360 documentation]({docs_url}).\nYou may want to do that before adding accounts.", "title": "Configure Life360 Account" } } diff --git a/homeassistant/components/life360/translations/es-419.json b/homeassistant/components/life360/translations/es-419.json index 29b62e160fd..6a6f46181d6 100644 --- a/homeassistant/components/life360/translations/es-419.json +++ b/homeassistant/components/life360/translations/es-419.json @@ -1,18 +1,11 @@ { "config": { - "create_entry": { - "default": "Para establecer opciones avanzadas, consulte [Documentaci\u00f3n de Life360] ({docs_url})." - }, - "error": { - "invalid_username": "Nombre de usuario inv\u00e1lido" - }, "step": { "user": { "data": { "password": "Contrase\u00f1a", "username": "Nombre de usuario" }, - "description": "Para establecer opciones avanzadas, consulte [Documentaci\u00f3n de Life360] ( {docs_url} ). \n Es posible que desee hacer eso antes de agregar cuentas.", "title": "Informaci\u00f3n de la cuenta Life360" } } diff --git a/homeassistant/components/life360/translations/es.json b/homeassistant/components/life360/translations/es.json index a9b53fffd86..c73c30de627 100644 --- a/homeassistant/components/life360/translations/es.json +++ b/homeassistant/components/life360/translations/es.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "La cuenta ya est\u00e1 configurada", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", - "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente", - "unknown": "Error inesperado" - }, - "create_entry": { - "default": "Para configurar las opciones avanzadas, consulta la [documentaci\u00f3n de Life360]({docs_url})." + "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente" }, "error": { "already_configured": "La cuenta ya est\u00e1 configurada", "cannot_connect": "No se pudo conectar", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", - "invalid_username": "Nombre de usuario no v\u00e1lido", "unknown": "Error inesperado" }, "step": { @@ -28,7 +23,6 @@ "password": "Contrase\u00f1a", "username": "Nombre de usuario" }, - "description": "Para configurar las opciones avanzadas, consulta la [documentaci\u00f3n de Life360]({docs_url}).\nEs posible que quieras hacerlo antes de a\u00f1adir cuentas.", "title": "Configurar cuenta de Life360" } } diff --git a/homeassistant/components/life360/translations/et.json b/homeassistant/components/life360/translations/et.json index 360aa8275f7..84ac7cc0617 100644 --- a/homeassistant/components/life360/translations/et.json +++ b/homeassistant/components/life360/translations/et.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "Konto on juba h\u00e4\u00e4lestatud", "invalid_auth": "Tuvastamise viga", - "reauth_successful": "Taastuvastamine \u00f5nnestus", - "unknown": "Ootamatu t\u00f5rge" - }, - "create_entry": { - "default": "T\u00e4psemate suvandite kohta leiad teemat [Life360 documentation]({docs_url})." + "reauth_successful": "Taastuvastamine \u00f5nnestus" }, "error": { "already_configured": "Kasutaja on juba seadistatud", "cannot_connect": "\u00dchendamine nurjus", "invalid_auth": "Tuvastamise viga", - "invalid_username": "Vale kasutajanimi", "unknown": "Ootamatu t\u00f5rge" }, "step": { @@ -28,7 +23,6 @@ "password": "Salas\u00f5na", "username": "Kasutajanimi" }, - "description": "T\u00e4psemate suvandite kohta leiad teemat [Life360 documentation]({docs_url}).\nTee seda enne uute kontode lisamist.", "title": "Seadista Life360 konto" } } diff --git a/homeassistant/components/life360/translations/fr.json b/homeassistant/components/life360/translations/fr.json index ce1fd3f7757..a872c9909a7 100644 --- a/homeassistant/components/life360/translations/fr.json +++ b/homeassistant/components/life360/translations/fr.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9", "invalid_auth": "Authentification non valide", - "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi", - "unknown": "Erreur inattendue" - }, - "create_entry": { - "default": "Pour d\u00e9finir les options avanc\u00e9es, voir [Documentation de Life360]( {docs_url} )." + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" }, "error": { "already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9", "cannot_connect": "\u00c9chec de connexion", "invalid_auth": "Authentification non valide", - "invalid_username": "Nom d'utilisateur non valide", "unknown": "Erreur inattendue" }, "step": { @@ -28,7 +23,6 @@ "password": "Mot de passe", "username": "Nom d'utilisateur" }, - "description": "Pour d\u00e9finir des options avanc\u00e9es, voir [Documentation Life360]({docs_url}).\nVous pouvez le faire avant d'ajouter des comptes.", "title": "Configuration du compte Life360" } } diff --git a/homeassistant/components/life360/translations/he.json b/homeassistant/components/life360/translations/he.json index e4998f86963..d10579282e6 100644 --- a/homeassistant/components/life360/translations/he.json +++ b/homeassistant/components/life360/translations/he.json @@ -3,14 +3,12 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", - "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7", - "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" }, "error": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", - "invalid_username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { diff --git a/homeassistant/components/life360/translations/hr.json b/homeassistant/components/life360/translations/hr.json index bb4a0b4fcf9..addfc0cbe81 100644 --- a/homeassistant/components/life360/translations/hr.json +++ b/homeassistant/components/life360/translations/hr.json @@ -1,11 +1,5 @@ { "config": { - "create_entry": { - "default": "Da biste postavili napredne opcije, pogledajte [Life360 dokumentacija] ( {docs_url} )." - }, - "error": { - "invalid_username": "Neispravno korisni\u010dko ime" - }, "step": { "user": { "data": { diff --git a/homeassistant/components/life360/translations/hu.json b/homeassistant/components/life360/translations/hu.json index 1eec6f50643..571f6525700 100644 --- a/homeassistant/components/life360/translations/hu.json +++ b/homeassistant/components/life360/translations/hu.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", - "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", - "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" - }, - "create_entry": { - "default": "A speci\u00e1lis be\u00e1ll\u00edt\u00e1sok megad\u00e1s\u00e1hoz l\u00e1sd: [Life360 dokument\u00e1ci\u00f3]({docs_url})." + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." }, "error": { "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", - "invalid_username": "\u00c9rv\u00e9nytelen felhaszn\u00e1l\u00f3n\u00e9v", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { @@ -28,7 +23,6 @@ "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, - "description": "A speci\u00e1lis be\u00e1ll\u00edt\u00e1sok megad\u00e1s\u00e1hoz l\u00e1sd a [Life360 dokument\u00e1ci\u00f3]({docs_url}) c\u00edm\u0171 r\u00e9szt.\n \u00c9rdemes ezt megtenni a fi\u00f3kok hozz\u00e1ad\u00e1sa el\u0151tt.", "title": "Life360 fi\u00f3k be\u00e1ll\u00edt\u00e1sa" } } diff --git a/homeassistant/components/life360/translations/id.json b/homeassistant/components/life360/translations/id.json index dfcdf97f46f..466b5bff996 100644 --- a/homeassistant/components/life360/translations/id.json +++ b/homeassistant/components/life360/translations/id.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "Akun sudah dikonfigurasi", "invalid_auth": "Autentikasi tidak valid", - "reauth_successful": "Autentikasi ulang berhasil", - "unknown": "Kesalahan yang tidak diharapkan" - }, - "create_entry": { - "default": "Untuk mengatur opsi tingkat lanjut, baca [dokumentasi Life360]({docs_url})." + "reauth_successful": "Autentikasi ulang berhasil" }, "error": { "already_configured": "Akun sudah dikonfigurasi", "cannot_connect": "Gagal terhubung", "invalid_auth": "Autentikasi tidak valid", - "invalid_username": "Nama pengguna tidak valid", "unknown": "Kesalahan yang tidak diharapkan" }, "step": { @@ -28,7 +23,6 @@ "password": "Kata Sandi", "username": "Nama Pengguna" }, - "description": "Untuk mengatur opsi tingkat lanjut, baca [dokumentasi Life360]({docs_url}).\nAnda mungkin ingin melakukannya sebelum menambahkan akun.", "title": "Konfigurasikan Akun Life360" } } diff --git a/homeassistant/components/life360/translations/it.json b/homeassistant/components/life360/translations/it.json index 4f139301274..179cb0b27dc 100644 --- a/homeassistant/components/life360/translations/it.json +++ b/homeassistant/components/life360/translations/it.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "L'account \u00e8 gi\u00e0 configurato", "invalid_auth": "Autenticazione non valida", - "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", - "unknown": "Errore imprevisto" - }, - "create_entry": { - "default": "Per impostare le opzioni avanzate, consultare la [Documentazione Life360]({docs_url})." + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" }, "error": { "already_configured": "L'account \u00e8 gi\u00e0 configurato", "cannot_connect": "Impossibile connettersi", "invalid_auth": "Autenticazione non valida", - "invalid_username": "Nome utente non valido", "unknown": "Errore imprevisto" }, "step": { @@ -28,7 +23,6 @@ "password": "Password", "username": "Nome utente" }, - "description": "Per impostare le opzioni avanzate, vedere [Documentazione di Life360]({docs_url}).\n\u00c8 consigliabile eseguire questa operazione prima di aggiungere gli account.", "title": "Configura l'account Life360" } } diff --git a/homeassistant/components/life360/translations/ja.json b/homeassistant/components/life360/translations/ja.json index ab320748086..e3b07855deb 100644 --- a/homeassistant/components/life360/translations/ja.json +++ b/homeassistant/components/life360/translations/ja.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", - "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" - }, - "create_entry": { - "default": "\u8a73\u7d30\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3059\u308b\u306b\u306f\u3001[Life360\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8]({docs_url}) \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" }, "error": { "already_configured": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", - "invalid_username": "\u7121\u52b9\u306a\u30e6\u30fc\u30b6\u30fc\u540d", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { @@ -28,7 +23,6 @@ "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "username": "\u30e6\u30fc\u30b6\u30fc\u540d" }, - "description": "\u8a73\u7d30\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3059\u308b\u306b\u306f\u3001[Life360\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8]({docs_url}) \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u8ffd\u52a0\u3059\u308b\u524d\u306b\u884c\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002", "title": "Life360\u30a2\u30ab\u30a6\u30f3\u30c8\u60c5\u5831" } } diff --git a/homeassistant/components/life360/translations/ka.json b/homeassistant/components/life360/translations/ka.json index 35a27bfc78f..7aea64c3439 100644 --- a/homeassistant/components/life360/translations/ka.json +++ b/homeassistant/components/life360/translations/ka.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "invalid_auth": "\u10db\u10ea\u10d3\u10d0\u10e0\u10d8 \u10d0\u10e3\u10d7\u10d4\u10dc\u10d7\u10d8\u10d9\u10d0\u10ea\u10d8\u10d0", - "unknown": "\u10d2\u10d0\u10e3\u10d7\u10d5\u10d0\u10da\u10d8\u10e1\u10ec\u10d8\u10dc\u10d4\u10d1\u10d4\u10da\u10d8 \u10e8\u10d4\u10ea\u10d3\u10dd\u10db\u10d0" + "invalid_auth": "\u10db\u10ea\u10d3\u10d0\u10e0\u10d8 \u10d0\u10e3\u10d7\u10d4\u10dc\u10d7\u10d8\u10d9\u10d0\u10ea\u10d8\u10d0" }, "error": { "already_configured": "\u10d0\u10dc\u10d2\u10d0\u10e0\u10d8\u10e8\u10d8 \u10e3\u10d9\u10d5\u10d4 \u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d8\u10e0\u10d4\u10d1\u10e3\u10da\u10d8\u10d0", diff --git a/homeassistant/components/life360/translations/ko.json b/homeassistant/components/life360/translations/ko.json index d2ebd7c674f..eb00b434591 100644 --- a/homeassistant/components/life360/translations/ko.json +++ b/homeassistant/components/life360/translations/ko.json @@ -1,16 +1,11 @@ { "config": { "abort": { - "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" - }, - "create_entry": { - "default": "\uace0\uae09 \uc635\uc158\uc744 \uc124\uc815\ud558\ub824\uba74 [Life360 \uc124\uba85\uc11c]({docs_url}) \ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694." + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "error": { "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "invalid_username": "\uc0ac\uc6a9\uc790 \uc774\ub984\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { @@ -19,7 +14,6 @@ "password": "\ube44\ubc00\ubc88\ud638", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" }, - "description": "\uace0\uae09 \uc635\uc158\uc744 \uc124\uc815\ud558\ub824\uba74 [Life360 \uc124\uba85\uc11c]({docs_url}) \ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694. \uacc4\uc815\uc744 \ucd94\uac00\ud558\uc2dc\uae30 \uc804\uc5d0 \uc77d\uc5b4\ubcf4\uc2dc\ub294\uac83\uc744 \ucd94\ucc9c\ub4dc\ub9bd\ub2c8\ub2e4.", "title": "Life360 \uacc4\uc815 \uc815\ubcf4" } } diff --git a/homeassistant/components/life360/translations/lb.json b/homeassistant/components/life360/translations/lb.json index ef359f37810..a01eea8e330 100644 --- a/homeassistant/components/life360/translations/lb.json +++ b/homeassistant/components/life360/translations/lb.json @@ -1,16 +1,11 @@ { "config": { "abort": { - "invalid_auth": "Ong\u00eblteg Authentifikatioun", - "unknown": "Onerwaarte Feeler" - }, - "create_entry": { - "default": "Fir erweidert Optiounen anzestellen, kuckt [Life360 Dokumentatioun]({docs_url})." + "invalid_auth": "Ong\u00eblteg Authentifikatioun" }, "error": { "already_configured": "Kont ass scho konfigur\u00e9iert", "invalid_auth": "Ong\u00eblteg Authentifikatioun", - "invalid_username": "Ong\u00ebltege Benotzernumm", "unknown": "Onerwaarte Feeler" }, "step": { @@ -19,7 +14,6 @@ "password": "Passwuert", "username": "Benotzernumm" }, - "description": "Fir erweidert Optiounen anzestellen, kuckt [Life360 Dokumentatioun]({docs_url}).\nMaacht dat am beschten ier dir Konte b\u00e4isetzt.", "title": "Life360 Kont Informatiounen" } } diff --git a/homeassistant/components/life360/translations/nb.json b/homeassistant/components/life360/translations/nb.json index d00b0b51267..a22f7eef3d6 100644 --- a/homeassistant/components/life360/translations/nb.json +++ b/homeassistant/components/life360/translations/nb.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "unknown": "Uventet feil" - }, "error": { "unknown": "Uventet feil" } diff --git a/homeassistant/components/life360/translations/nl.json b/homeassistant/components/life360/translations/nl.json index b0e54bde3c5..f818a43a73a 100644 --- a/homeassistant/components/life360/translations/nl.json +++ b/homeassistant/components/life360/translations/nl.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "Account is al geconfigureerd", "invalid_auth": "Ongeldige authenticatie", - "reauth_successful": "Herauthenticatie geslaagd", - "unknown": "Onverwachte fout" - }, - "create_entry": { - "default": "Om geavanceerde opties in te stellen, zie [Life360 documentatie]({docs_url})." + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "already_configured": "Account is al geconfigureerd", "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", - "invalid_username": "Ongeldige gebruikersnaam", "unknown": "Onverwachte fout" }, "step": { @@ -28,7 +23,6 @@ "password": "Wachtwoord", "username": "Gebruikersnaam" }, - "description": "Om geavanceerde opties in te stellen, zie [Life360 documentatie]({docs_url}).\nMisschien wilt u dat doen voordat u accounts toevoegt.", "title": "Life360-accountgegevens" } } diff --git a/homeassistant/components/life360/translations/no.json b/homeassistant/components/life360/translations/no.json index 5095ced59f0..b0c590ec700 100644 --- a/homeassistant/components/life360/translations/no.json +++ b/homeassistant/components/life360/translations/no.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "Kontoen er allerede konfigurert", "invalid_auth": "Ugyldig godkjenning", - "reauth_successful": "Re-autentisering var vellykket", - "unknown": "Uventet feil" - }, - "create_entry": { - "default": "For \u00e5 angi avanserte alternativer, se [Life360 dokumentasjon]({docs_url})." + "reauth_successful": "Re-autentisering var vellykket" }, "error": { "already_configured": "Kontoen er allerede konfigurert", "cannot_connect": "Tilkobling mislyktes", "invalid_auth": "Ugyldig godkjenning", - "invalid_username": "Ugyldig brukernavn", "unknown": "Uventet feil" }, "step": { @@ -28,7 +23,6 @@ "password": "Passord", "username": "Brukernavn" }, - "description": "For \u00e5 angi avanserte alternativer, se [Life360 dokumentasjon]({docs_url}). \nDet kan hende du vil gj\u00f8re det f\u00f8r du legger til kontoer.", "title": "Konfigurer Life360-konto" } } diff --git a/homeassistant/components/life360/translations/pl.json b/homeassistant/components/life360/translations/pl.json index 2b1c7138079..bf4d2457835 100644 --- a/homeassistant/components/life360/translations/pl.json +++ b/homeassistant/components/life360/translations/pl.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "Konto jest ju\u017c skonfigurowane", "invalid_auth": "Niepoprawne uwierzytelnienie", - "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119", - "unknown": "Nieoczekiwany b\u0142\u0105d" - }, - "create_entry": { - "default": "Aby skonfigurowa\u0107 zaawansowane ustawienia, zapoznaj si\u0119 z [dokumentacj\u0105 Life360]({docs_url})." + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" }, "error": { "already_configured": "Konto jest ju\u017c skonfigurowane", "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "invalid_auth": "Niepoprawne uwierzytelnienie", - "invalid_username": "Nieprawid\u0142owa nazwa u\u017cytkownika", "unknown": "Nieoczekiwany b\u0142\u0105d" }, "step": { @@ -28,7 +23,6 @@ "password": "Has\u0142o", "username": "Nazwa u\u017cytkownika" }, - "description": "Aby skonfigurowa\u0107 zaawansowane ustawienia, zapoznaj si\u0119 z [dokumentacj\u0105 Life360]({docs_url}). Mo\u017cesz to zrobi\u0107 przed dodaniem kont.", "title": "Konfiguracja konta Life360" } } diff --git a/homeassistant/components/life360/translations/pt-BR.json b/homeassistant/components/life360/translations/pt-BR.json index 25e917f2578..2beaa127927 100644 --- a/homeassistant/components/life360/translations/pt-BR.json +++ b/homeassistant/components/life360/translations/pt-BR.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "A conta j\u00e1 foi configurada", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", - "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida", - "unknown": "Erro inesperado" - }, - "create_entry": { - "default": "Para definir op\u00e7\u00f5es avan\u00e7adas, consulte [Documenta\u00e7\u00e3o da Life360] ({docs_url})." + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" }, "error": { "already_configured": "A conta j\u00e1 foi configurada", "cannot_connect": "Falha ao conectar", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", - "invalid_username": "Nome de usu\u00e1rio Inv\u00e1lido", "unknown": "Erro inesperado" }, "step": { @@ -28,7 +23,6 @@ "password": "Senha", "username": "Usu\u00e1rio" }, - "description": "Para definir op\u00e7\u00f5es avan\u00e7adas, consulte [Documenta\u00e7\u00e3o da Life360] ({docs_url}). \n Voc\u00ea pode querer fazer isso antes de adicionar contas.", "title": "Configurar conta Life360" } } diff --git a/homeassistant/components/life360/translations/pt.json b/homeassistant/components/life360/translations/pt.json index cc3b190458f..f0b8db82092 100644 --- a/homeassistant/components/life360/translations/pt.json +++ b/homeassistant/components/life360/translations/pt.json @@ -3,14 +3,12 @@ "abort": { "already_configured": "Conta j\u00e1 configurada", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", - "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida", - "unknown": "Erro inesperado" + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { "already_configured": "Conta j\u00e1 configurada", "cannot_connect": "Falha na liga\u00e7\u00e3o", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", - "invalid_username": "Nome de utilizador incorreto", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/life360/translations/ru.json b/homeassistant/components/life360/translations/ru.json index c0cd51a72d4..7bd5d776a05 100644 --- a/homeassistant/components/life360/translations/ru.json +++ b/homeassistant/components/life360/translations/ru.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." - }, - "create_entry": { - "default": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439]({docs_url}) \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438." + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." }, "error": { "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", - "invalid_username": "\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { @@ -28,7 +23,6 @@ "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, - "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439]({docs_url}) \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430.", "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Life360" } } diff --git a/homeassistant/components/life360/translations/sl.json b/homeassistant/components/life360/translations/sl.json index 354c8c2618a..81742418d4b 100644 --- a/homeassistant/components/life360/translations/sl.json +++ b/homeassistant/components/life360/translations/sl.json @@ -1,18 +1,11 @@ { "config": { - "create_entry": { - "default": "\u010ce \u017eelite nastaviti napredne mo\u017enosti, glejte [Life360 dokumentacija]({docs_url})." - }, - "error": { - "invalid_username": "Napa\u010dno uporabni\u0161ko ime" - }, "step": { "user": { "data": { "password": "Geslo", "username": "Uporabni\u0161ko ime" }, - "description": "\u010ce \u017eelite nastaviti napredne mo\u017enosti, glejte [Life360 dokumentacija]({docs_url}). \n To lahko storite pred dodajanjem ra\u010dunov.", "title": "Podatki ra\u010duna Life360" } } diff --git a/homeassistant/components/life360/translations/sv.json b/homeassistant/components/life360/translations/sv.json index 9f9168abdd2..8b9a76589f9 100644 --- a/homeassistant/components/life360/translations/sv.json +++ b/homeassistant/components/life360/translations/sv.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "Konto har redan konfigurerats", "invalid_auth": "Ogiltig autentisering", - "reauth_successful": "\u00c5terautentisering lyckades", - "unknown": "Ov\u00e4ntat fel" - }, - "create_entry": { - "default": "F\u00f6r att st\u00e4lla in avancerade alternativ, se [Life360 documentation]({docs_url})." + "reauth_successful": "\u00c5terautentisering lyckades" }, "error": { "already_configured": "Konto har redan konfigurerats", "cannot_connect": "Det gick inte att ansluta.", "invalid_auth": "Ogiltig autentisering", - "invalid_username": "Ogiltigt anv\u00e4ndarnmn", "unknown": "Ov\u00e4ntat fel" }, "step": { @@ -28,7 +23,6 @@ "password": "L\u00f6senord", "username": "Anv\u00e4ndarnamn" }, - "description": "F\u00f6r att st\u00e4lla in avancerade alternativ, se [Life360 documentation]({docs_url}).\nDu kanske vill g\u00f6ra det innan du l\u00e4gger till konton.", "title": "Life360 kontoinformation" } } diff --git a/homeassistant/components/life360/translations/tr.json b/homeassistant/components/life360/translations/tr.json index 52b083b83fd..c38acc09134 100644 --- a/homeassistant/components/life360/translations/tr.json +++ b/homeassistant/components/life360/translations/tr.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", - "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu", - "unknown": "Beklenmeyen hata" - }, - "create_entry": { - "default": "Geli\u015fmi\u015f se\u00e7enekleri ayarlamak i\u00e7in [Life360 belgelerine]( {docs_url} ) bak\u0131n." + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" }, "error": { "already_configured": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "cannot_connect": "Ba\u011flanma hatas\u0131", "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", - "invalid_username": "Ge\u00e7ersiz kullan\u0131c\u0131 ad\u0131", "unknown": "Beklenmeyen hata" }, "step": { @@ -28,7 +23,6 @@ "password": "Parola", "username": "Kullan\u0131c\u0131 Ad\u0131" }, - "description": "Geli\u015fmi\u015f se\u00e7enekleri ayarlamak i\u00e7in [Life360 belgelerine]( {docs_url} ) bak\u0131n.\n Bunu hesap eklemeden \u00f6nce yapmak isteyebilirsiniz.", "title": "Life360 Hesab\u0131n\u0131 Yap\u0131land\u0131r" } } diff --git a/homeassistant/components/life360/translations/uk.json b/homeassistant/components/life360/translations/uk.json index caecf494388..6f74c07cc19 100644 --- a/homeassistant/components/life360/translations/uk.json +++ b/homeassistant/components/life360/translations/uk.json @@ -1,16 +1,11 @@ { "config": { "abort": { - "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f.", - "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" - }, - "create_entry": { - "default": "\u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 [\u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u044f\u043c\u0438]({docs_url}) \u0434\u043b\u044f \u0440\u043e\u0437\u0448\u0438\u0440\u0435\u043d\u0438\u0445 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u044c." + "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f." }, "error": { "already_configured": "\u0426\u0435\u0439 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u0438\u0439 \u0437\u0430\u043f\u0438\u0441 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u043e \u0432 Home Assistant.", "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f.", - "invalid_username": "\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0435 \u0456\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430.", "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" }, "step": { @@ -19,7 +14,6 @@ "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430" }, - "description": "\u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 [\u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u044f\u043c\u0438]({docs_url}) \u0434\u043b\u044f \u0440\u043e\u0437\u0448\u0438\u0440\u0435\u043d\u0438\u0445 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u044c. \u0412\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u0437\u0440\u043e\u0431\u0438\u0442\u0438 \u0446\u0435 \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430.", "title": "Life360" } } diff --git a/homeassistant/components/life360/translations/zh-Hans.json b/homeassistant/components/life360/translations/zh-Hans.json index a429b31dd82..296f1dba295 100644 --- a/homeassistant/components/life360/translations/zh-Hans.json +++ b/homeassistant/components/life360/translations/zh-Hans.json @@ -4,8 +4,7 @@ "invalid_auth": "\u65e0\u6548\u7684\u8eab\u4efd\u9a8c\u8bc1" }, "error": { - "invalid_auth": "\u65e0\u6548\u7684\u8eab\u4efd\u9a8c\u8bc1", - "invalid_username": "\u65e0\u6548\u7684\u7528\u6237\u540d" + "invalid_auth": "\u65e0\u6548\u7684\u8eab\u4efd\u9a8c\u8bc1" }, "step": { "user": { diff --git a/homeassistant/components/life360/translations/zh-Hant.json b/homeassistant/components/life360/translations/zh-Hant.json index 55e55bb30c7..6fa1834ac41 100644 --- a/homeassistant/components/life360/translations/zh-Hant.json +++ b/homeassistant/components/life360/translations/zh-Hant.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", - "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4" - }, - "create_entry": { - "default": "\u6b32\u8a2d\u5b9a\u9032\u968e\u9078\u9805\uff0c\u8acb\u53c3\u95b1 [Life360 \u6587\u4ef6]({docs_url})\u3002" + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, "error": { "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "cannot_connect": "\u9023\u7dda\u5931\u6557", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", - "invalid_username": "\u4f7f\u7528\u8005\u540d\u7a31\u7121\u6548", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { @@ -28,7 +23,6 @@ "password": "\u5bc6\u78bc", "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, - "description": "\u6b32\u8a2d\u5b9a\u9032\u968e\u9078\u9805\uff0c\u8acb\u53c3\u95b1 [Life360 \u6587\u4ef6]({docs_url})\u3002\n\u5efa\u8b70\u65bc\u65b0\u589e\u5e33\u865f\u524d\uff0c\u5148\u9032\u884c\u4e86\u89e3\u3002", "title": "\u8a2d\u5b9a Life360 \u5e33\u865f" } } diff --git a/homeassistant/components/lifx/translations/bg.json b/homeassistant/components/lifx/translations/bg.json index 056e965f723..068c07dbdca 100644 --- a/homeassistant/components/lifx/translations/bg.json +++ b/homeassistant/components/lifx/translations/bg.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "no_devices_found": "\u0412 \u043c\u0440\u0435\u0436\u0430\u0442\u0430 \u043d\u044f\u043c\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 LIFX \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430.", - "single_instance_allowed": "\u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0430 LIFX." + "no_devices_found": "\u0412 \u043c\u0440\u0435\u0436\u0430\u0442\u0430 \u043d\u044f\u043c\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 LIFX \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430." }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 LIFX?" - }, "discovery_confirm": { "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 {label} ({host}) {serial}?" }, diff --git a/homeassistant/components/lifx/translations/ca.json b/homeassistant/components/lifx/translations/ca.json index 8d0efc4de8a..57c706e19df 100644 --- a/homeassistant/components/lifx/translations/ca.json +++ b/homeassistant/components/lifx/translations/ca.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "El dispositiu ja est\u00e0 configurat", "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", - "no_devices_found": "No s'han trobat dispositius a la xarxa", - "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." + "no_devices_found": "No s'han trobat dispositius a la xarxa" }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "Vols configurar LIFX?" - }, "discovery_confirm": { "description": "Vols configurar {label} ({host}) {serial}?" }, diff --git a/homeassistant/components/lifx/translations/cs.json b/homeassistant/components/lifx/translations/cs.json index 660884bc1e7..d533e7fffab 100644 --- a/homeassistant/components/lifx/translations/cs.json +++ b/homeassistant/components/lifx/translations/cs.json @@ -3,16 +3,12 @@ "abort": { "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", - "no_devices_found": "V s\u00edti nebyla nalezena \u017e\u00e1dn\u00e1 za\u0159\u00edzen\u00ed", - "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." + "no_devices_found": "V s\u00edti nebyla nalezena \u017e\u00e1dn\u00e1 za\u0159\u00edzen\u00ed" }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" }, "step": { - "confirm": { - "description": "Chcete nastavit LIFX?" - }, "discovery_confirm": { "description": "Chcete nastavit {label} ({host}) {serial}?" }, diff --git a/homeassistant/components/lifx/translations/da.json b/homeassistant/components/lifx/translations/da.json index 14fbf83cbed..b6767f48f39 100644 --- a/homeassistant/components/lifx/translations/da.json +++ b/homeassistant/components/lifx/translations/da.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "Der blev ikke fundet nogen LIFX-enheder p\u00e5 netv\u00e6rket.", - "single_instance_allowed": "Kun en enkelt konfiguration af LIFX er mulig." - }, - "step": { - "confirm": { - "description": "Konfigurer LIFX?" - } + "no_devices_found": "Der blev ikke fundet nogen LIFX-enheder p\u00e5 netv\u00e6rket." } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/de.json b/homeassistant/components/lifx/translations/de.json index 82e37b39c8b..ae056f136d7 100644 --- a/homeassistant/components/lifx/translations/de.json +++ b/homeassistant/components/lifx/translations/de.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", - "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", - "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." + "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "M\u00f6chtest du LIFX einrichten?" - }, "discovery_confirm": { "description": "M\u00f6chtest du {label} ({host}) {serial} einrichten?" }, diff --git a/homeassistant/components/lifx/translations/el.json b/homeassistant/components/lifx/translations/el.json index 4ebea49190d..51556cc2af2 100644 --- a/homeassistant/components/lifx/translations/el.json +++ b/homeassistant/components/lifx/translations/el.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", - "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf", - "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." + "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf" }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf LIFX;" - }, "discovery_confirm": { "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {label} ({host}) {serial};" }, diff --git a/homeassistant/components/lifx/translations/en.json b/homeassistant/components/lifx/translations/en.json index 1f7cf981f5d..119259457a7 100644 --- a/homeassistant/components/lifx/translations/en.json +++ b/homeassistant/components/lifx/translations/en.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "Device is already configured", "already_in_progress": "Configuration flow is already in progress", - "no_devices_found": "No devices found on the network", - "single_instance_allowed": "Already configured. Only a single configuration possible." + "no_devices_found": "No devices found on the network" }, "error": { "cannot_connect": "Failed to connect" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "Do you want to set up LIFX?" - }, "discovery_confirm": { "description": "Do you want to setup {label} ({host}) {serial}?" }, diff --git a/homeassistant/components/lifx/translations/es-419.json b/homeassistant/components/lifx/translations/es-419.json index 023cec6a6db..8eb34ef0e34 100644 --- a/homeassistant/components/lifx/translations/es-419.json +++ b/homeassistant/components/lifx/translations/es-419.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "No se han encontrado dispositivos LIFX en la red.", - "single_instance_allowed": "S\u00f3lo es posible una \u00fanica configuraci\u00f3n de LIFX." - }, - "step": { - "confirm": { - "description": "\u00bfDesea configurar LIFX?" - } + "no_devices_found": "No se han encontrado dispositivos LIFX en la red." } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/es.json b/homeassistant/components/lifx/translations/es.json index 6bc2249182e..227e9fb3cdd 100644 --- a/homeassistant/components/lifx/translations/es.json +++ b/homeassistant/components/lifx/translations/es.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "El dispositivo ya est\u00e1 configurado", "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en curso", - "no_devices_found": "No se encontraron dispositivos en la red", - "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." + "no_devices_found": "No se encontraron dispositivos en la red" }, "error": { "cannot_connect": "No se pudo conectar" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "\u00bfQuieres configurar LIFX?" - }, "discovery_confirm": { "description": "\u00bfQuieres configurar {label} ({host}) {serial}?" }, diff --git a/homeassistant/components/lifx/translations/et.json b/homeassistant/components/lifx/translations/et.json index 6d06cbb17ba..fe05f4044ba 100644 --- a/homeassistant/components/lifx/translations/et.json +++ b/homeassistant/components/lifx/translations/et.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "Seade on juba h\u00e4\u00e4lestatud", "already_in_progress": "Seadistamine juba k\u00e4ib", - "no_devices_found": "V\u00f5rgust ei leitud seadmeid", - "single_instance_allowed": "Juba seadistatud, lubatud on ainult \u00fcks sidumine." + "no_devices_found": "V\u00f5rgust ei leitud seadmeid" }, "error": { "cannot_connect": "\u00dchendamine nurjus" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "Kas soovid seadistada LIFX-i?" - }, "discovery_confirm": { "description": "Kas seadistada {label} ( {host} ) {serial} ?" }, diff --git a/homeassistant/components/lifx/translations/fi.json b/homeassistant/components/lifx/translations/fi.json deleted file mode 100644 index a92bc699280..00000000000 --- a/homeassistant/components/lifx/translations/fi.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "config": { - "step": { - "confirm": { - "description": "Haluatko m\u00e4\u00e4ritt\u00e4\u00e4 LIFX:n?" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/fr.json b/homeassistant/components/lifx/translations/fr.json index c3f0561b085..5bfbe3fa87d 100644 --- a/homeassistant/components/lifx/translations/fr.json +++ b/homeassistant/components/lifx/translations/fr.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", - "no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau", - "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." + "no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau" }, "error": { "cannot_connect": "\u00c9chec de connexion" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "Voulez-vous configurer LIFX?" - }, "discovery_confirm": { "description": "Voulez-vous configurer {label} ({host}) {serial}\u00a0?" }, diff --git a/homeassistant/components/lifx/translations/he.json b/homeassistant/components/lifx/translations/he.json index e40655a5fbd..9237bf45294 100644 --- a/homeassistant/components/lifx/translations/he.json +++ b/homeassistant/components/lifx/translations/he.json @@ -3,8 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", - "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" diff --git a/homeassistant/components/lifx/translations/hr.json b/homeassistant/components/lifx/translations/hr.json index d7692d2d6cf..7aff376ef2a 100644 --- a/homeassistant/components/lifx/translations/hr.json +++ b/homeassistant/components/lifx/translations/hr.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "Nijedan ure\u0111aj nije prona\u0111en na mre\u017ei", - "single_instance_allowed": "Ve\u0107 konfigurirano. Mogu\u0107a samo jedna konfiguracija." - }, - "step": { - "confirm": { - "description": "\u017delite li postaviti LIFX?" - } + "no_devices_found": "Nijedan ure\u0111aj nije prona\u0111en na mre\u017ei" } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/hu.json b/homeassistant/components/lifx/translations/hu.json index 588d5932e10..048509a9952 100644 --- a/homeassistant/components/lifx/translations/hu.json +++ b/homeassistant/components/lifx/translations/hu.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", - "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", - "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." + "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton" }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: LIFX?" - }, "discovery_confirm": { "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {label} ({host}) {serial}?" }, diff --git a/homeassistant/components/lifx/translations/id.json b/homeassistant/components/lifx/translations/id.json index 8781581bb0f..b9b3bb59207 100644 --- a/homeassistant/components/lifx/translations/id.json +++ b/homeassistant/components/lifx/translations/id.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "Perangkat sudah dikonfigurasi", "already_in_progress": "Alur konfigurasi sedang berlangsung", - "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan", - "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." + "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan" }, "error": { "cannot_connect": "Gagal terhubung" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "Ingin menyiapkan LIFX?" - }, "discovery_confirm": { "description": "Ingin menyiapkan {label} ({host}) {serial}?" }, diff --git a/homeassistant/components/lifx/translations/it.json b/homeassistant/components/lifx/translations/it.json index 9e8c090ad0d..8f6172f79ae 100644 --- a/homeassistant/components/lifx/translations/it.json +++ b/homeassistant/components/lifx/translations/it.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", - "no_devices_found": "Nessun dispositivo trovato sulla rete", - "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." + "no_devices_found": "Nessun dispositivo trovato sulla rete" }, "error": { "cannot_connect": "Impossibile connettersi" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "Vuoi configurare LIFX?" - }, "discovery_confirm": { "description": "Vuoi configurare {label} ({host}) {serial}?" }, diff --git a/homeassistant/components/lifx/translations/ja.json b/homeassistant/components/lifx/translations/ja.json index c3b144222a4..6b67ea51e28 100644 --- a/homeassistant/components/lifx/translations/ja.json +++ b/homeassistant/components/lifx/translations/ja.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", - "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093", - "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u8a2d\u5b9a\u3067\u304d\u308b\u306e\u306f1\u3064\u3060\u3051\u3067\u3059\u3002" + "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093" }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "LIFX\u306e\u8a2d\u5b9a\u3092\u3057\u307e\u3059\u304b\uff1f" - }, "discovery_confirm": { "description": "{label} ({host}) {serial} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b?" }, diff --git a/homeassistant/components/lifx/translations/ko.json b/homeassistant/components/lifx/translations/ko.json index 4d388cbeda2..d50e5e705bb 100644 --- a/homeassistant/components/lifx/translations/ko.json +++ b/homeassistant/components/lifx/translations/ko.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", - "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." - }, - "step": { - "confirm": { - "description": "LIFX\ub97c \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" - } + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/lb.json b/homeassistant/components/lifx/translations/lb.json index 5455195f822..47a897ef157 100644 --- a/homeassistant/components/lifx/translations/lb.json +++ b/homeassistant/components/lifx/translations/lb.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "Keng Apparater am Netzwierk fonnt.", - "single_instance_allowed": "Scho konfigur\u00e9iert. N\u00ebmmen eng eenzeg Konfiguratioun m\u00e9iglech." - }, - "step": { - "confirm": { - "description": "Soll LIFX konfigur\u00e9iert ginn?" - } + "no_devices_found": "Keng Apparater am Netzwierk fonnt." } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/nl.json b/homeassistant/components/lifx/translations/nl.json index 51091fcd365..fc7feecd443 100644 --- a/homeassistant/components/lifx/translations/nl.json +++ b/homeassistant/components/lifx/translations/nl.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "Apparaat is al geconfigureerd", "already_in_progress": "De configuratie is momenteel al bezig", - "no_devices_found": "Geen apparaten gevonden op het netwerk", - "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." + "no_devices_found": "Geen apparaten gevonden op het netwerk" }, "error": { "cannot_connect": "Kan geen verbinding maken" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "Wilt u LIFX instellen?" - }, "discovery_confirm": { "description": "Wilt u {label} ({host}) {serial} instellen?" }, diff --git a/homeassistant/components/lifx/translations/no.json b/homeassistant/components/lifx/translations/no.json index 49ff5dea624..00bcd009eed 100644 --- a/homeassistant/components/lifx/translations/no.json +++ b/homeassistant/components/lifx/translations/no.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "Enheten er allerede konfigurert", "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", - "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket", - "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." + "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket" }, "error": { "cannot_connect": "Tilkobling mislyktes" }, "flow_title": "{label} ( {host} ) {serial}", "step": { - "confirm": { - "description": "\u00d8nsker du \u00e5 sette opp LIFX?" - }, "discovery_confirm": { "description": "Vil du sette opp {label} ( {host} ) {serial} ?" }, diff --git a/homeassistant/components/lifx/translations/pl.json b/homeassistant/components/lifx/translations/pl.json index 817867d7c62..9bf20bc1e40 100644 --- a/homeassistant/components/lifx/translations/pl.json +++ b/homeassistant/components/lifx/translations/pl.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", "already_in_progress": "Konfiguracja jest ju\u017c w toku", - "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci", - "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." + "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci" }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "Czy chcesz rozpocz\u0105\u0107 konfiguracj\u0119?" - }, "discovery_confirm": { "description": "Czy chcesz skonfigurowa\u0107 {label} ({host}) {serial}?" }, diff --git a/homeassistant/components/lifx/translations/pt-BR.json b/homeassistant/components/lifx/translations/pt-BR.json index 616f3f03cc8..3ae1087f327 100644 --- a/homeassistant/components/lifx/translations/pt-BR.json +++ b/homeassistant/components/lifx/translations/pt-BR.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", - "no_devices_found": "Nenhum dispositivo encontrado na rede", - "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." + "no_devices_found": "Nenhum dispositivo encontrado na rede" }, "error": { "cannot_connect": "Falha ao conectar" }, "flow_title": "{label} ( {host} ) {serial}", "step": { - "confirm": { - "description": "Voc\u00ea quer configurar o LIFX?" - }, "discovery_confirm": { "description": "Deseja configurar {label} ( {host} ) {serial}?" }, diff --git a/homeassistant/components/lifx/translations/pt.json b/homeassistant/components/lifx/translations/pt.json index 594ac7dacc4..5d7fdf356ef 100644 --- a/homeassistant/components/lifx/translations/pt.json +++ b/homeassistant/components/lifx/translations/pt.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "Nenhum dispositivo LIFX encontrado na rede.", - "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." - }, - "step": { - "confirm": { - "description": "Deseja configurar o LIFX?" - } + "no_devices_found": "Nenhum dispositivo LIFX encontrado na rede." } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/ro.json b/homeassistant/components/lifx/translations/ro.json index 56e9307a8b3..ce0856d2bff 100644 --- a/homeassistant/components/lifx/translations/ro.json +++ b/homeassistant/components/lifx/translations/ro.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "Nu exist\u0103 dispozitive LIFX g\u0103site \u00een re\u021bea.", - "single_instance_allowed": "Doar o singur\u0103 configura\u021bie de LIFX este posibil\u0103." - }, - "step": { - "confirm": { - "description": "Dori\u021bi s\u0103 configura\u021bi LIFX?" - } + "no_devices_found": "Nu exist\u0103 dispozitive LIFX g\u0103site \u00een re\u021bea." } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/ru.json b/homeassistant/components/lifx/translations/ru.json index 9e9a9460e19..cea461cb10d 100644 --- a/homeassistant/components/lifx/translations/ru.json +++ b/homeassistant/components/lifx/translations/ru.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", - "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", - "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." + "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c LIFX?" - }, "discovery_confirm": { "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {label} ({host}) {serial}?" }, diff --git a/homeassistant/components/lifx/translations/sl.json b/homeassistant/components/lifx/translations/sl.json index dbbe051afd1..7047d591679 100644 --- a/homeassistant/components/lifx/translations/sl.json +++ b/homeassistant/components/lifx/translations/sl.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "V omre\u017eju ni najdenih naprav LIFX.", - "single_instance_allowed": "Mo\u017ena je samo ena konfiguracija LIFX-a." - }, - "step": { - "confirm": { - "description": "Ali \u017eelite nastaviti LIFX?" - } + "no_devices_found": "V omre\u017eju ni najdenih naprav LIFX." } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/sv.json b/homeassistant/components/lifx/translations/sv.json index dfd7de02d94..ba9248d4f4d 100644 --- a/homeassistant/components/lifx/translations/sv.json +++ b/homeassistant/components/lifx/translations/sv.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "Enheten \u00e4r redan konfigurerad", "already_in_progress": "Konfigurationsfl\u00f6det p\u00e5g\u00e5r redan", - "no_devices_found": "Inga LIFX enheter hittas i n\u00e4tverket.", - "single_instance_allowed": "Endast en enda konfiguration av LIFX \u00e4r m\u00f6jlig." + "no_devices_found": "Inga LIFX enheter hittas i n\u00e4tverket." }, "error": { "cannot_connect": "Det gick inte att ansluta." }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "Vill du st\u00e4lla in LIFX?" - }, "discovery_confirm": { "description": "Vill du st\u00e4lla in {label} ( {host} ) {serial} ?" }, diff --git a/homeassistant/components/lifx/translations/tr.json b/homeassistant/components/lifx/translations/tr.json index 0f212e225be..a11798e6513 100644 --- a/homeassistant/components/lifx/translations/tr.json +++ b/homeassistant/components/lifx/translations/tr.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", - "no_devices_found": "A\u011fda cihaz bulunamad\u0131", - "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." + "no_devices_found": "A\u011fda cihaz bulunamad\u0131" }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "LIFX'i kurmak istiyor musunuz?" - }, "discovery_confirm": { "description": "{label} ( {host} ) {serial} kurmak istiyor musunuz?" }, diff --git a/homeassistant/components/lifx/translations/uk.json b/homeassistant/components/lifx/translations/uk.json index 556729e895b..1efd10692f9 100644 --- a/homeassistant/components/lifx/translations/uk.json +++ b/homeassistant/components/lifx/translations/uk.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u041f\u0440\u0438\u0441\u0442\u0440\u043e\u0457 \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u0456 \u0432 \u043c\u0435\u0440\u0435\u0436\u0456.", - "single_instance_allowed": "\u0412\u0436\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u043e\u0432\u0430\u043d\u043e. \u041c\u043e\u0436\u043b\u0438\u0432\u0430 \u043b\u0438\u0448\u0435 \u043e\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044f." - }, - "step": { - "confirm": { - "description": "\u0412\u0438 \u0432\u043f\u0435\u0432\u043d\u0435\u043d\u0456, \u0449\u043e \u0445\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 LIFX?" - } + "no_devices_found": "\u041f\u0440\u0438\u0441\u0442\u0440\u043e\u0457 \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u0456 \u0432 \u043c\u0435\u0440\u0435\u0436\u0456." } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/zh-Hans.json b/homeassistant/components/lifx/translations/zh-Hans.json index bf9b4277312..a501a8ace78 100644 --- a/homeassistant/components/lifx/translations/zh-Hans.json +++ b/homeassistant/components/lifx/translations/zh-Hans.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u6ca1\u6709\u5728\u7f51\u7edc\u4e0a\u627e\u5230 LIFX \u8bbe\u5907\u3002", - "single_instance_allowed": "LIFX \u53ea\u80fd\u914d\u7f6e\u4e00\u6b21\u3002" - }, - "step": { - "confirm": { - "description": "\u60a8\u60f3\u8981\u914d\u7f6e LIFX \u5417\uff1f" - } + "no_devices_found": "\u6ca1\u6709\u5728\u7f51\u7edc\u4e0a\u627e\u5230 LIFX \u8bbe\u5907\u3002" } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/zh-Hant.json b/homeassistant/components/lifx/translations/zh-Hant.json index e8ff08be901..6fc7318a7b1 100644 --- a/homeassistant/components/lifx/translations/zh-Hant.json +++ b/homeassistant/components/lifx/translations/zh-Hant.json @@ -3,17 +3,13 @@ "abort": { "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", - "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e", - "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" + "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e" }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557" }, "flow_title": "{label} ({host}) {serial}", "step": { - "confirm": { - "description": "\u662f\u5426\u8981\u8a2d\u5b9a LIFX\uff1f" - }, "discovery_confirm": { "description": "\u662f\u5426\u8981\u8a2d\u5b9a {label} ({host}) {serial}\uff1f" }, diff --git a/homeassistant/components/mqtt/translations/bg.json b/homeassistant/components/mqtt/translations/bg.json index c3dd9f6ec2f..b1d5f535a67 100644 --- a/homeassistant/components/mqtt/translations/bg.json +++ b/homeassistant/components/mqtt/translations/bg.json @@ -15,7 +15,6 @@ "advanced_options": "\u0420\u0430\u0437\u0448\u0438\u0440\u0435\u043d\u0438 \u043e\u043f\u0446\u0438\u0438", "broker": "\u0411\u0440\u043e\u043a\u0435\u0440", "client_id": "ID \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 (\u043e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e \u0437\u0430 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u043d)", - "discovery": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e\u0442\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "port": "\u041f\u043e\u0440\u0442", "protocol": "MQTT \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b", diff --git a/homeassistant/components/mqtt/translations/ca.json b/homeassistant/components/mqtt/translations/ca.json index 7dc484cdcd4..0878737057b 100644 --- a/homeassistant/components/mqtt/translations/ca.json +++ b/homeassistant/components/mqtt/translations/ca.json @@ -12,6 +12,7 @@ "bad_client_key": "Clau privada inv\u00e0lida, assegura't que la codificaci\u00f3 del fitxer sigui PEM i sense contrasenya", "bad_discovery_prefix": "Prefix de descobriment inv\u00e0lid", "bad_will": "T\u00f2pic del missatge d'\u00faltima voluntat ('will') inv\u00e0lid", + "bad_ws_headers": "Proporciona cap\u00e7aleres HTTP v\u00e0lides en format d'objecte JSON", "cannot_connect": "Ha fallat la connexi\u00f3", "invalid_inclusion": "El certificat de client i la clau privada s'han de configurar conjuntament" }, @@ -24,7 +25,6 @@ "client_cert": "Puja fitxer de certificat client", "client_id": "ID de client (deixa-ho buit per generar-lo aleat\u00f2riament)", "client_key": "Puja fitxer de clau privada", - "discovery": "Habilita el descobriment autom\u00e0tic", "keepalive": "Temps entre enviaments de missatges de manteniment viu ('keep alive')", "password": "Contrasenya", "port": "Port", @@ -32,7 +32,10 @@ "set_ca_cert": "Validaci\u00f3 del certificat del 'broker'", "set_client_cert": "Utilitza un certificat de client", "tls_insecure": "Ignora la validaci\u00f3 del certificat del 'broker'", - "username": "Nom d'usuari" + "transport": "Transport MQTT", + "username": "Nom d'usuari", + "ws_headers": "Cap\u00e7aleres del WebSocket en format JSON", + "ws_path": "Ruta del WebSocket" }, "description": "Introdueix la informaci\u00f3 de connexi\u00f3 del teu broker MQTT." }, @@ -86,6 +89,7 @@ "bad_client_key": "Clau privada inv\u00e0lida, assegura't que la codificaci\u00f3 del fitxer sigui PEM i sense contrasenya", "bad_discovery_prefix": "Prefix de descobriment inv\u00e0lid", "bad_will": "T\u00f2pic del missatge d'\u00faltima voluntat ('will') inv\u00e0lid", + "bad_ws_headers": "Proporciona cap\u00e7aleres HTTP v\u00e0lides en format d'objecte JSON", "cannot_connect": "Ha fallat la connexi\u00f3", "invalid_inclusion": "El certificat de client i la clau privada s'han de configurar conjuntament" }, @@ -105,7 +109,10 @@ "set_ca_cert": "Validaci\u00f3 del certificat del 'broker'", "set_client_cert": "Utilitza un certificat de client", "tls_insecure": "Ignora la validaci\u00f3 del certificat del 'broker'", - "username": "Nom d'usuari" + "transport": "Transport MQTT", + "username": "Nom d'usuari", + "ws_headers": "Cap\u00e7aleres del WebSocket en format JSON", + "ws_path": "Ruta del WebSocket" }, "description": "Introdueix la informaci\u00f3 de connexi\u00f3 del teu broker MQTT.", "title": "Opcions del broker" diff --git a/homeassistant/components/mqtt/translations/cs.json b/homeassistant/components/mqtt/translations/cs.json index f82a3f1c973..347a560b52a 100644 --- a/homeassistant/components/mqtt/translations/cs.json +++ b/homeassistant/components/mqtt/translations/cs.json @@ -11,7 +11,6 @@ "broker": { "data": { "broker": "Broker", - "discovery": "Povolit automatick\u00e9 vyhled\u00e1v\u00e1n\u00ed za\u0159\u00edzen\u00ed", "password": "Heslo", "port": "Port", "username": "U\u017eivatelsk\u00e9 jm\u00e9no" diff --git a/homeassistant/components/mqtt/translations/da.json b/homeassistant/components/mqtt/translations/da.json index 9b853a2dae2..03b8539abd7 100644 --- a/homeassistant/components/mqtt/translations/da.json +++ b/homeassistant/components/mqtt/translations/da.json @@ -10,7 +10,6 @@ "broker": { "data": { "broker": "Broker", - "discovery": "Aktiv\u00e9r automatisk fund", "password": "Adgangskode", "port": "Port", "username": "Brugernavn" diff --git a/homeassistant/components/mqtt/translations/de.json b/homeassistant/components/mqtt/translations/de.json index 425425a24a1..d23d944c958 100644 --- a/homeassistant/components/mqtt/translations/de.json +++ b/homeassistant/components/mqtt/translations/de.json @@ -12,6 +12,7 @@ "bad_client_key": "Ung\u00fcltiger privater Schl\u00fcssel. Stelle sicher, dass eine PEM-codierte Datei ohne Passwort bereitgestellt wird", "bad_discovery_prefix": "Ung\u00fcltiges Discovery-Pr\u00e4fix", "bad_will": "Ung\u00fcltiges \u201eWill\u201c-Thema", + "bad_ws_headers": "Bereitstellung g\u00fcltiger HTTP-Header als JSON-Objekt", "cannot_connect": "Verbindung fehlgeschlagen", "invalid_inclusion": "Das Client-Zertifikat und der private Schl\u00fcssel m\u00fcssen gemeinsam konfiguriert werden" }, @@ -24,7 +25,6 @@ "client_cert": "Client-Zertifikatsdatei hochladen", "client_id": "Client-ID (leer lassen, um eine zuf\u00e4llig generierte zu erhalten)", "client_key": "Private Schl\u00fcsseldatei hochladen", - "discovery": "Suche aktivieren", "keepalive": "Die Zeit zwischen dem Senden von Keep-Alive-Nachrichten", "password": "Passwort", "port": "Port", @@ -32,7 +32,10 @@ "set_ca_cert": "Validierung des Broker-Zertifikats", "set_client_cert": "Ein Client-Zertifikat verwenden", "tls_insecure": "Validierung des Broker-Zertifikats ignorieren", - "username": "Benutzername" + "transport": "MQTT-Transport", + "username": "Benutzername", + "ws_headers": "WebSocket-Header im JSON-Format", + "ws_path": "WebSocket-Pfad" }, "description": "Bitte gib die Verbindungsinformationen deines MQTT-Brokers ein." }, @@ -86,6 +89,7 @@ "bad_client_key": "Ung\u00fcltiger privater Schl\u00fcssel. Stelle sicher, dass eine PEM-codierte Datei ohne Passwort bereitgestellt wird", "bad_discovery_prefix": "Ung\u00fcltiges Discovery-Pr\u00e4fix", "bad_will": "Ung\u00fcltiges \u201eWill\u201c-Thema", + "bad_ws_headers": "Bereitstellung g\u00fcltiger HTTP-Header als JSON-Objekt", "cannot_connect": "Verbindung fehlgeschlagen", "invalid_inclusion": "Das Client-Zertifikat und der private Schl\u00fcssel m\u00fcssen gemeinsam konfiguriert werden" }, @@ -105,7 +109,10 @@ "set_ca_cert": "Validierung des Broker-Zertifikats", "set_client_cert": "Ein Client-Zertifikat verwenden", "tls_insecure": "Validierung des Broker-Zertifikats ignorieren", - "username": "Benutzername" + "transport": "MQTT-Transport", + "username": "Benutzername", + "ws_headers": "WebSocket-Header im JSON-Format", + "ws_path": "WebSocket-Pfad" }, "description": "Bitte gib die Verbindungsinformationen deines MQTT-Brokers ein.", "title": "Broker-Optionen" diff --git a/homeassistant/components/mqtt/translations/el.json b/homeassistant/components/mqtt/translations/el.json index a822cd2a927..c20b9461eb2 100644 --- a/homeassistant/components/mqtt/translations/el.json +++ b/homeassistant/components/mqtt/translations/el.json @@ -24,7 +24,6 @@ "client_cert": "\u0391\u03bd\u03ad\u03b2\u03b1\u03c3\u03bc\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7", "client_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7 (\u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b5\u03bd\u03cc \u03c3\u03b5 \u03ad\u03bd\u03b1 \u03c0\u03bf\u03c5 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03b8\u03b7\u03ba\u03b5 \u03c4\u03c5\u03c7\u03b1\u03af\u03b1)", "client_key": "\u039c\u03b5\u03c4\u03b1\u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03bf\u03cd \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd", - "discovery": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7\u03c2", "keepalive": "\u039f \u03c7\u03c1\u03cc\u03bd\u03bf\u03c2 \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03c4\u03b7\u03c2 \u03b1\u03c0\u03bf\u03c3\u03c4\u03bf\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03c4\u03b7\u03c1\u03b5\u03af \u03b6\u03c9\u03bd\u03c4\u03b1\u03bd\u03ac \u03c4\u03b1 \u03bc\u03b7\u03bd\u03cd\u03bc\u03b1\u03c4\u03b1", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "port": "\u0398\u03cd\u03c1\u03b1", diff --git a/homeassistant/components/mqtt/translations/en.json b/homeassistant/components/mqtt/translations/en.json index 51e7918b5cc..78f91c27f30 100644 --- a/homeassistant/components/mqtt/translations/en.json +++ b/homeassistant/components/mqtt/translations/en.json @@ -25,7 +25,6 @@ "client_cert": "Upload client certificate file", "client_id": "Client ID (leave empty to randomly generated one)", "client_key": "Upload private key file", - "discovery": "Enable discovery", "keepalive": "The time between sending keep alive messages", "password": "Password", "port": "Port", @@ -35,8 +34,8 @@ "tls_insecure": "Ignore broker certificate validation", "transport": "MQTT transport", "username": "Username", - "ws_path": "WebSocket path", - "ws_headers": "WebSocket headers" + "ws_headers": "WebSocket headers in JSON format", + "ws_path": "WebSocket path" }, "description": "Please enter the connection information of your MQTT broker." }, @@ -112,8 +111,8 @@ "tls_insecure": "Ignore broker certificate validation", "transport": "MQTT transport", "username": "Username", - "ws_path": "WebSocket path", - "ws_headers": "WebSocket headers" + "ws_headers": "WebSocket headers in JSON format", + "ws_path": "WebSocket path" }, "description": "Please enter the connection information of your MQTT broker.", "title": "Broker options" diff --git a/homeassistant/components/mqtt/translations/es-419.json b/homeassistant/components/mqtt/translations/es-419.json index a69be795f77..9cccbf8658b 100644 --- a/homeassistant/components/mqtt/translations/es-419.json +++ b/homeassistant/components/mqtt/translations/es-419.json @@ -10,7 +10,6 @@ "broker": { "data": { "broker": "Broker", - "discovery": "Habilitar descubrimiento", "password": "Contrase\u00f1a", "port": "Puerto", "username": "Nombre de usuario" diff --git a/homeassistant/components/mqtt/translations/es.json b/homeassistant/components/mqtt/translations/es.json index 8ad74693dde..95680744242 100644 --- a/homeassistant/components/mqtt/translations/es.json +++ b/homeassistant/components/mqtt/translations/es.json @@ -12,6 +12,7 @@ "bad_client_key": "Clave privada no v\u00e1lida, aseg\u00farate de que se proporcione un archivo codificado PEM sin contrase\u00f1a", "bad_discovery_prefix": "Prefijo de descubrimiento no v\u00e1lido", "bad_will": "Tema de voluntad no v\u00e1lido", + "bad_ws_headers": "Proporciona cabeceras HTTP v\u00e1lidas como un objeto JSON", "cannot_connect": "No se pudo conectar", "invalid_inclusion": "El certificado del cliente y la clave privada deben configurarse juntos" }, @@ -24,7 +25,6 @@ "client_cert": "Subir archivo de certificado de cliente", "client_id": "ID de cliente (dejar vac\u00edo para generar uno aleatoriamente)", "client_key": "Subir archivo de clave privada", - "discovery": "Habilitar descubrimiento", "keepalive": "El tiempo entre el env\u00edo de mensajes keep alive", "password": "Contrase\u00f1a", "port": "Puerto", @@ -32,7 +32,10 @@ "set_ca_cert": "Validaci\u00f3n del certificado del br\u00f3ker", "set_client_cert": "Utilizar un certificado de cliente", "tls_insecure": "Ignorar la validaci\u00f3n del certificado del br\u00f3ker", - "username": "Nombre de usuario" + "transport": "Transporte MQTT", + "username": "Nombre de usuario", + "ws_headers": "Cabeceras WebSocket en formato JSON", + "ws_path": "Ruta del WebSocket" }, "description": "Por favor, introduce la informaci\u00f3n de conexi\u00f3n de tu br\u00f3ker MQTT." }, @@ -86,6 +89,7 @@ "bad_client_key": "Clave privada no v\u00e1lida, aseg\u00farate de que se proporcione un archivo codificado PEM sin contrase\u00f1a", "bad_discovery_prefix": "Prefijo de descubrimiento no v\u00e1lido", "bad_will": "Tema de voluntad no v\u00e1lido", + "bad_ws_headers": "Proporciona cabeceras HTTP v\u00e1lidas como un objeto JSON", "cannot_connect": "No se pudo conectar", "invalid_inclusion": "El certificado del cliente y la clave privada deben configurarse juntos" }, @@ -105,7 +109,10 @@ "set_ca_cert": "Validaci\u00f3n del certificado del br\u00f3ker", "set_client_cert": "Utilizar un certificado de cliente", "tls_insecure": "Ignorar la validaci\u00f3n del certificado del br\u00f3ker", - "username": "Nombre de usuario" + "transport": "Transporte MQTT", + "username": "Nombre de usuario", + "ws_headers": "Cabeceras WebSocket en formato JSON", + "ws_path": "Ruta del WebSocket" }, "description": "Por favor, introduce la informaci\u00f3n de conexi\u00f3n de tu br\u00f3ker MQTT.", "title": "Opciones del br\u00f3ker" diff --git a/homeassistant/components/mqtt/translations/et.json b/homeassistant/components/mqtt/translations/et.json index 47bf2cfd264..677d304e21e 100644 --- a/homeassistant/components/mqtt/translations/et.json +++ b/homeassistant/components/mqtt/translations/et.json @@ -12,6 +12,7 @@ "bad_client_key": "Kehtetu privaatv\u00f5ti, veendu, et PEM-kodeeritud fail tarnitakse ilma paroolita", "bad_discovery_prefix": "Sobimatu tuvastuse eesliide", "bad_will": "Kehtetu l\u00f5petamise teavitus", + "bad_ws_headers": "Esita korrektsed HTTP p\u00e4ised JSON objektina", "cannot_connect": "Vahendajaga ei saa \u00fchendust luua.", "invalid_inclusion": "Kliendisertifikaat ja privaatne v\u00f5ti tuleb konfigureerida koos." }, @@ -24,7 +25,6 @@ "client_cert": "Lae \u00fcles kliendi sertifikaadifail", "client_id": "Kliendi ID (juhuslikult genereeritud ID jaoks j\u00e4ta t\u00fchjaks)", "client_key": "Lae \u00fcles privaatv\u00f5tme fail", - "discovery": "Luba automaatne avastamine", "keepalive": "Aegumiss\u00f5numite saatmise vaheline aeg", "password": "Salas\u00f5na", "port": "Port", @@ -32,7 +32,10 @@ "set_ca_cert": "Sertifikaadi kinnitamine", "set_client_cert": "Kasuta kliendi sertifikaati", "tls_insecure": "Eira serdi valideerimist", - "username": "Kasutajanimi" + "transport": "MQTT \u00fclekanne", + "username": "Kasutajanimi", + "ws_headers": "WebSocketi p\u00e4ised JSON vormingus", + "ws_path": "WebSocketi rada" }, "description": "Sisesta oma MQTT vahendaja andmed." }, @@ -86,6 +89,7 @@ "bad_client_key": "Kehtetu privaatv\u00f5ti, veendu, et PEM-kodeeritud fail tarnitakse ilma paroolita", "bad_discovery_prefix": "Sobimatu tuvastuse eesliide", "bad_will": "Kehtetu l\u00f5petamise teavitus", + "bad_ws_headers": "Esita korrektsed HTTP p\u00e4ised JSON objektina", "cannot_connect": "\u00dchendamine nurjus", "invalid_inclusion": "Kliendisertifikaat ja privaatne v\u00f5ti tuleb konfigureerida koos." }, @@ -105,7 +109,10 @@ "set_ca_cert": "Sertifikaadi kinnitamine", "set_client_cert": "Kasuta kliendi sertifikaati", "tls_insecure": "Eira serdi valideerimist", - "username": "Kasutajanimi" + "transport": "MQTT \u00fclekanne", + "username": "Kasutajanimi", + "ws_headers": "WebSocketi p\u00e4ised JSON vormingus", + "ws_path": "WebSocketi rada" }, "description": "Sisesta oma MQTT vahendaja \u00fchenduse teave.", "title": "MQTT maakleri valikud" diff --git a/homeassistant/components/mqtt/translations/fi.json b/homeassistant/components/mqtt/translations/fi.json index 62bb5b0f48e..41003b522b0 100644 --- a/homeassistant/components/mqtt/translations/fi.json +++ b/homeassistant/components/mqtt/translations/fi.json @@ -7,7 +7,6 @@ "broker": { "data": { "broker": "V\u00e4litt\u00e4j\u00e4", - "discovery": "Ota etsint\u00e4 k\u00e4ytt\u00f6\u00f6n", "password": "Salasana", "port": "Portti", "username": "K\u00e4ytt\u00e4j\u00e4tunnus" diff --git a/homeassistant/components/mqtt/translations/fr.json b/homeassistant/components/mqtt/translations/fr.json index c005f4ad787..f3d7b4b0ca4 100644 --- a/homeassistant/components/mqtt/translations/fr.json +++ b/homeassistant/components/mqtt/translations/fr.json @@ -16,7 +16,6 @@ "certificate": "T\u00e9l\u00e9verser le certificat de l\u2019autorit\u00e9 de certification personnalis\u00e9", "client_cert": "T\u00e9l\u00e9verser le certificat client", "client_key": "T\u00e9l\u00e9verser la cl\u00e9 priv\u00e9e", - "discovery": "Activer la d\u00e9couverte", "password": "Mot de passe", "port": "Port", "protocol": "Protocole MQTT", diff --git a/homeassistant/components/mqtt/translations/he.json b/homeassistant/components/mqtt/translations/he.json index 53e67968d95..8a5809c76c1 100644 --- a/homeassistant/components/mqtt/translations/he.json +++ b/homeassistant/components/mqtt/translations/he.json @@ -24,7 +24,6 @@ "client_cert": "\u05e0\u05ea\u05d9\u05d1 \u05dc\u05e7\u05d5\u05d1\u05e5 \u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7", "client_id": "\u05de\u05d6\u05d4\u05d4 \u05dc\u05e7\u05d5\u05d7 (\u05dc\u05d4\u05e9\u05d0\u05d9\u05e8 \u05e8\u05d9\u05e7 \u05dc\u05de\u05d6\u05d4\u05d4 \u05e9\u05e0\u05d5\u05e6\u05e8 \u05d1\u05d0\u05d5\u05e4\u05df \u05d0\u05e7\u05e8\u05d0\u05d9)", "client_key": "\u05e0\u05ea\u05d9\u05d1 \u05dc\u05e7\u05d5\u05d1\u05e5 \u05de\u05e4\u05ea\u05d7 \u05e4\u05e8\u05d8\u05d9", - "discovery": "\u05d0\u05e4\u05e9\u05e8 \u05d2\u05d9\u05dc\u05d5\u05d9", "keepalive": "\u05d4\u05d6\u05de\u05df \u05e9\u05d1\u05d9\u05df \u05e9\u05dc\u05d9\u05d7\u05ea \u05d4\u05d5\u05d3\u05e2\u05d5\u05ea \u05dc\u05e9\u05de\u05d5\u05e8 \u05d1\u05d7\u05d9\u05d9\u05dd", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", "port": "\u05e4\u05ea\u05d7\u05d4", diff --git a/homeassistant/components/mqtt/translations/hr.json b/homeassistant/components/mqtt/translations/hr.json index c18a994254f..eef7ca62225 100644 --- a/homeassistant/components/mqtt/translations/hr.json +++ b/homeassistant/components/mqtt/translations/hr.json @@ -18,7 +18,6 @@ "client_cert": "Put do datoteke klijentskog certifikata", "client_id": "ID klijenta (ostavite prazno za nasumi\u010dno generiran)", "client_key": "Put do datoteke privatnog klju\u010da", - "discovery": "Omogu\u0107i otkrivanje", "keepalive": "Vrijeme izme\u0111u slanja poruka keep alive", "password": "Lozinka", "port": "Port", diff --git a/homeassistant/components/mqtt/translations/hu.json b/homeassistant/components/mqtt/translations/hu.json index cbc557d4021..1a6c62d7e15 100644 --- a/homeassistant/components/mqtt/translations/hu.json +++ b/homeassistant/components/mqtt/translations/hu.json @@ -24,7 +24,6 @@ "client_cert": "\u00dcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny f\u00e1jl felt\u00f6lt\u00e9se", "client_id": "\u00dcgyf\u00e9l azonos\u00edt\u00f3 (hagyja \u00fcresen a v\u00e9letlenszer\u0171en gener\u00e1lt azonos\u00edt\u00f3hoz)", "client_key": "Priv\u00e1t kulcsf\u00e1jl felt\u00f6lt\u00e9se", - "discovery": "Felfedez\u00e9s enged\u00e9lyez\u00e9se", "keepalive": "A keep alive \u00fczenetek k\u00fcld\u00e9se k\u00f6z\u00f6tti id\u0151", "password": "Jelsz\u00f3", "port": "Port", diff --git a/homeassistant/components/mqtt/translations/id.json b/homeassistant/components/mqtt/translations/id.json index 3f2a6cb18cb..6a22f1c0cbd 100644 --- a/homeassistant/components/mqtt/translations/id.json +++ b/homeassistant/components/mqtt/translations/id.json @@ -12,6 +12,7 @@ "bad_client_key": "Kunci pribadi tidak valid, pastikan file dengan format PEM diberikan tanpa kata sandi", "bad_discovery_prefix": "Prefiks topik penemuan tidak valid", "bad_will": "Topik will tidak valid", + "bad_ws_headers": "Berikan header HTTP yang valid sebagai objek JSON", "cannot_connect": "Gagal terhubung", "invalid_inclusion": "Sertifikat klien dan kunci pribadi harus dikonfigurasi bersama" }, @@ -24,7 +25,6 @@ "client_cert": "Unggah file sertifikat klien", "client_id": "ID Klien (biarkan kosong agar dihasilkan secara acak)", "client_key": "Unggah file kunci pribadi", - "discovery": "Aktifkan penemuan", "keepalive": "Waktu antara mengirim pesan tetap hidup", "password": "Kata Sandi", "port": "Port", @@ -32,7 +32,10 @@ "set_ca_cert": "Validasi sertifikat broker", "set_client_cert": "Gunakan sertifikat klien", "tls_insecure": "Abaikan validasi sertifikat broker", - "username": "Nama Pengguna" + "transport": "Transportasi MQTT", + "username": "Nama Pengguna", + "ws_headers": "Header WebSocket dalam format JSON", + "ws_path": "Jalur WebSocket" }, "description": "Masukkan informasi koneksi broker MQTT Anda." }, @@ -86,6 +89,7 @@ "bad_client_key": "Kunci pribadi tidak valid, pastikan file dengan format PEM diberikan tanpa kata sandi", "bad_discovery_prefix": "Prefiks topik penemuan tidak valid", "bad_will": "Topik will tidak valid", + "bad_ws_headers": "Berikan header HTTP yang valid sebagai objek JSON", "cannot_connect": "Gagal terhubung", "invalid_inclusion": "Sertifikat klien dan kunci pribadi harus dikonfigurasi bersama" }, @@ -105,7 +109,10 @@ "set_ca_cert": "Validasi sertifikat broker", "set_client_cert": "Gunakan sertifikat klien", "tls_insecure": "Abaikan validasi sertifikat broker", - "username": "Nama Pengguna" + "transport": "Transportasi MQTT", + "username": "Nama Pengguna", + "ws_headers": "Header WebSocket dalam format JSON", + "ws_path": "Jalur WebSocket" }, "description": "Masukkan informasi koneksi broker MQTT Anda.", "title": "Opsi broker" diff --git a/homeassistant/components/mqtt/translations/it.json b/homeassistant/components/mqtt/translations/it.json index 74febaf9cae..e3a572b1d36 100644 --- a/homeassistant/components/mqtt/translations/it.json +++ b/homeassistant/components/mqtt/translations/it.json @@ -24,7 +24,6 @@ "client_cert": "Carica il file del certificato client", "client_id": "ID client (lasciare vuoto per generarne uno casualmente)", "client_key": "Carica il file della chiave privata", - "discovery": "Attiva il rilevamento", "keepalive": "L'intervallo di tempo tra l'invio di messaggi di mantenimento in attivit\u00e0", "password": "Password", "port": "Porta", diff --git a/homeassistant/components/mqtt/translations/ja.json b/homeassistant/components/mqtt/translations/ja.json index 274e7666a30..2d11e594cca 100644 --- a/homeassistant/components/mqtt/translations/ja.json +++ b/homeassistant/components/mqtt/translations/ja.json @@ -11,7 +11,6 @@ "broker": { "data": { "broker": "Broker", - "discovery": "\u691c\u51fa\u3092\u6709\u52b9\u306b\u3059\u308b", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "port": "\u30dd\u30fc\u30c8", "username": "\u30e6\u30fc\u30b6\u30fc\u540d" diff --git a/homeassistant/components/mqtt/translations/ko.json b/homeassistant/components/mqtt/translations/ko.json index 454b1e0368f..25c5db0edb2 100644 --- a/homeassistant/components/mqtt/translations/ko.json +++ b/homeassistant/components/mqtt/translations/ko.json @@ -10,7 +10,6 @@ "broker": { "data": { "broker": "\ube0c\ub85c\ucee4", - "discovery": "\uae30\uae30 \uac80\uc0c9 \ud65c\uc131\ud654", "password": "\ube44\ubc00\ubc88\ud638", "port": "\ud3ec\ud2b8", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" diff --git a/homeassistant/components/mqtt/translations/lb.json b/homeassistant/components/mqtt/translations/lb.json index fd9cd351858..885062e4777 100644 --- a/homeassistant/components/mqtt/translations/lb.json +++ b/homeassistant/components/mqtt/translations/lb.json @@ -10,7 +10,6 @@ "broker": { "data": { "broker": "Broker", - "discovery": "Entdeckung aktiv\u00e9ieren", "password": "Passwuert", "port": "Port", "username": "Benotzernumm" diff --git a/homeassistant/components/mqtt/translations/nl.json b/homeassistant/components/mqtt/translations/nl.json index 13fcd4a9595..8f25e3826b7 100644 --- a/homeassistant/components/mqtt/translations/nl.json +++ b/homeassistant/components/mqtt/translations/nl.json @@ -21,7 +21,6 @@ "advanced_options": "Geavanceerde opties", "broker": "Broker", "client_id": "Client ID (leeg laten voor een willekeurig ID)", - "discovery": "Detectie inschakelen", "keepalive": "Tijd tussen het verzenden van keep-a-live berichten", "password": "Wachtwoord", "port": "Poort", diff --git a/homeassistant/components/mqtt/translations/no.json b/homeassistant/components/mqtt/translations/no.json index e1f27d29ae9..7e91def7a96 100644 --- a/homeassistant/components/mqtt/translations/no.json +++ b/homeassistant/components/mqtt/translations/no.json @@ -24,7 +24,6 @@ "client_cert": "Last opp klientsertifikatfil", "client_id": "Klient-ID (la st\u00e5 tomt til tilfeldig generert)", "client_key": "Last opp privat n\u00f8kkelfil", - "discovery": "Aktiver oppdagelse", "keepalive": "Tiden mellom sending hold levende meldinger", "password": "Passord", "port": "Port", diff --git a/homeassistant/components/mqtt/translations/pl.json b/homeassistant/components/mqtt/translations/pl.json index 33284dcd7c5..b361fa11acb 100644 --- a/homeassistant/components/mqtt/translations/pl.json +++ b/homeassistant/components/mqtt/translations/pl.json @@ -12,6 +12,7 @@ "bad_client_key": "Nieprawid\u0142owy klucz prywatny, upewnij si\u0119, \u017ce zakodowany plik PEM jest dostarczony bez has\u0142a", "bad_discovery_prefix": "Nieprawid\u0142owy prefiks wykrywania", "bad_will": "Nieprawid\u0142owy temat \"will\"", + "bad_ws_headers": "Podaj prawid\u0142owe nag\u0142\u00f3wki HTTP jako obiekt JSON", "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "invalid_inclusion": "Certyfikat klienta i klucz prywatny musz\u0105 by\u0107 skonfigurowane razem" }, @@ -24,7 +25,6 @@ "client_cert": "Prze\u015blij plik certyfikatu klienta", "client_id": "Identyfikator klienta (pozostaw puste, aby wygenerowa\u0107 losowo)", "client_key": "Prze\u015blij plik klucza prywatnego", - "discovery": "W\u0142\u0105cz wykrywanie", "keepalive": "Czas pomi\u0119dzy wys\u0142aniem wiadomo\u015bci \"keep alive\"", "password": "Has\u0142o", "port": "Port", @@ -32,7 +32,10 @@ "set_ca_cert": "Sprawdzanie certyfikatu brokera", "set_client_cert": "U\u017cyj certyfikatu klienta", "tls_insecure": "Ignoruj sprawdzanie certyfikatu brokera", - "username": "Nazwa u\u017cytkownika" + "transport": "MQTT transport", + "username": "Nazwa u\u017cytkownika", + "ws_headers": "Nag\u0142\u00f3wki WebSocket w formacie JSON", + "ws_path": "\u015acie\u017cka WebSocket" }, "description": "Wprowad\u017a informacje o po\u0142\u0105czeniu po\u015brednika MQTT." }, @@ -86,6 +89,7 @@ "bad_client_key": "Nieprawid\u0142owy klucz prywatny, upewnij si\u0119, \u017ce zakodowany plik PEM jest dostarczony bez has\u0142a", "bad_discovery_prefix": "Nieprawid\u0142owy prefiks wykrywania", "bad_will": "Nieprawid\u0142owy temat \"will\"", + "bad_ws_headers": "Podaj prawid\u0142owe nag\u0142\u00f3wki HTTP jako obiekt JSON", "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "invalid_inclusion": "Certyfikat klienta i klucz prywatny musz\u0105 by\u0107 skonfigurowane razem" }, @@ -105,7 +109,10 @@ "set_ca_cert": "Sprawdzanie certyfikatu brokera", "set_client_cert": "U\u017cyj certyfikatu klienta", "tls_insecure": "Ignoruj sprawdzanie certyfikatu brokera", - "username": "Nazwa u\u017cytkownika" + "transport": "MQTT transport", + "username": "Nazwa u\u017cytkownika", + "ws_headers": "Nag\u0142\u00f3wki WebSocket w formacie JSON", + "ws_path": "\u015acie\u017cka WebSocket" }, "description": "Wprowad\u017a informacje o po\u0142\u0105czeniu po\u015brednika MQTT.", "title": "Opcje brokera" diff --git a/homeassistant/components/mqtt/translations/pt-BR.json b/homeassistant/components/mqtt/translations/pt-BR.json index a1676396477..653b567b2f5 100644 --- a/homeassistant/components/mqtt/translations/pt-BR.json +++ b/homeassistant/components/mqtt/translations/pt-BR.json @@ -24,7 +24,6 @@ "client_cert": "Carregar arquivo de certificado do cliente", "client_id": "ID do cliente (deixe em branco para um gerado aleatoriamente)", "client_key": "Carregar arquivo de chave privada", - "discovery": "Ativar descoberta", "keepalive": "O tempo entre o envio de mensagens de manuten\u00e7\u00e3o viva", "password": "Senha", "port": "Porta", diff --git a/homeassistant/components/mqtt/translations/pt.json b/homeassistant/components/mqtt/translations/pt.json index 6ff10cf515c..47354149caf 100644 --- a/homeassistant/components/mqtt/translations/pt.json +++ b/homeassistant/components/mqtt/translations/pt.json @@ -10,7 +10,6 @@ "broker": { "data": { "broker": "Broker", - "discovery": "Ativar descoberta", "password": "Palavra-passe", "port": "Porto", "username": "Nome de Utilizador" diff --git a/homeassistant/components/mqtt/translations/ro.json b/homeassistant/components/mqtt/translations/ro.json index a98818be937..e65a30ed761 100644 --- a/homeassistant/components/mqtt/translations/ro.json +++ b/homeassistant/components/mqtt/translations/ro.json @@ -10,7 +10,6 @@ "broker": { "data": { "broker": "Broker", - "discovery": "Activa\u021bi descoperirea", "password": "Parol\u0103", "port": "Port", "username": "Nume de utilizator" diff --git a/homeassistant/components/mqtt/translations/ru.json b/homeassistant/components/mqtt/translations/ru.json index e0de1f14936..ea478b5ac20 100644 --- a/homeassistant/components/mqtt/translations/ru.json +++ b/homeassistant/components/mqtt/translations/ru.json @@ -24,7 +24,6 @@ "client_cert": "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430", "client_id": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 (\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c)", "client_key": "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430", - "discovery": "\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432", "keepalive": "\u0412\u0440\u0435\u043c\u044f \u043c\u0435\u0436\u0434\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 Keep Alive", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "port": "\u041f\u043e\u0440\u0442", diff --git a/homeassistant/components/mqtt/translations/sl.json b/homeassistant/components/mqtt/translations/sl.json index 9f16209d524..327b33af684 100644 --- a/homeassistant/components/mqtt/translations/sl.json +++ b/homeassistant/components/mqtt/translations/sl.json @@ -10,7 +10,6 @@ "broker": { "data": { "broker": "Posrednik", - "discovery": "Omogo\u010di odkrivanje", "password": "Geslo", "port": "port", "username": "Uporabni\u0161ko ime" diff --git a/homeassistant/components/mqtt/translations/sv.json b/homeassistant/components/mqtt/translations/sv.json index 811da569992..249c8153a58 100644 --- a/homeassistant/components/mqtt/translations/sv.json +++ b/homeassistant/components/mqtt/translations/sv.json @@ -11,7 +11,6 @@ "broker": { "data": { "broker": "Broker", - "discovery": "Aktivera uppt\u00e4ckt", "password": "L\u00f6senord", "port": "Port", "username": "Anv\u00e4ndarnamn" diff --git a/homeassistant/components/mqtt/translations/th.json b/homeassistant/components/mqtt/translations/th.json index 624df71b786..3be3637e94b 100644 --- a/homeassistant/components/mqtt/translations/th.json +++ b/homeassistant/components/mqtt/translations/th.json @@ -6,7 +6,6 @@ "step": { "broker": { "data": { - "discovery": "\u0e40\u0e1b\u0e34\u0e14\u0e43\u0e0a\u0e49\u0e01\u0e32\u0e23\u0e04\u0e49\u0e19\u0e2b\u0e32\u0e2d\u0e38\u0e1b\u0e01\u0e23\u0e13\u0e4c", "password": "\u0e23\u0e2b\u0e31\u0e2a\u0e1c\u0e48\u0e32\u0e19", "username": "\u0e0a\u0e37\u0e48\u0e2d\u0e1c\u0e39\u0e49\u0e43\u0e0a\u0e49" } diff --git a/homeassistant/components/mqtt/translations/tr.json b/homeassistant/components/mqtt/translations/tr.json index c6ed1221a05..0cf9e430f88 100644 --- a/homeassistant/components/mqtt/translations/tr.json +++ b/homeassistant/components/mqtt/translations/tr.json @@ -24,7 +24,6 @@ "client_cert": "\u0130stemci sertifika dosyas\u0131n\u0131n yolu", "client_id": "M\u00fc\u015fteri Kimli\u011fi (rastgele olu\u015fturulmu\u015f olana kadar bo\u015f b\u0131rak\u0131n)", "client_key": "\u00d6zel anahtar dosyas\u0131n\u0131n yolu", - "discovery": "Ke\u015ffetmeyi etkinle\u015ftir", "keepalive": "Canl\u0131 tutma mesajlar\u0131 g\u00f6nderme aras\u0131ndaki s\u00fcre", "password": "Parola", "port": "Port", diff --git a/homeassistant/components/mqtt/translations/uk.json b/homeassistant/components/mqtt/translations/uk.json index b684595b170..ec09eef166c 100644 --- a/homeassistant/components/mqtt/translations/uk.json +++ b/homeassistant/components/mqtt/translations/uk.json @@ -10,7 +10,6 @@ "broker": { "data": { "broker": "\u0411\u0440\u043e\u043a\u0435\u0440", - "discovery": "\u0414\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u0438 \u0410\u0432\u0442\u043e\u0432\u0438\u044f\u0432\u043b\u0435\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0457\u0432", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "port": "\u041f\u043e\u0440\u0442", "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430" diff --git a/homeassistant/components/mqtt/translations/zh-Hans.json b/homeassistant/components/mqtt/translations/zh-Hans.json index f897bee3c9b..5671a9085f9 100644 --- a/homeassistant/components/mqtt/translations/zh-Hans.json +++ b/homeassistant/components/mqtt/translations/zh-Hans.json @@ -11,7 +11,6 @@ "broker": { "data": { "broker": "\u670d\u52a1\u5668", - "discovery": "\u542f\u7528\u53d1\u73b0", "password": "\u5bc6\u7801", "port": "\u7aef\u53e3", "username": "\u7528\u6237\u540d" diff --git a/homeassistant/components/mqtt/translations/zh-Hant.json b/homeassistant/components/mqtt/translations/zh-Hant.json index 23cd9b3e109..2916f6f5065 100644 --- a/homeassistant/components/mqtt/translations/zh-Hant.json +++ b/homeassistant/components/mqtt/translations/zh-Hant.json @@ -24,7 +24,6 @@ "client_cert": "\u4e0a\u50b3\u5ba2\u6236\u7aef\u6191\u8b49\u6a94\u6848", "client_id": "\u5ba2\u6236\u7aef ID (\u4fdd\u6301\u7a7a\u767d\u5c07\u81ea\u52d5\u7522\u751f)", "client_key": "\u4e0a\u50b3\u79c1\u9470\u6a94\u6848", - "discovery": "\u958b\u555f\u641c\u5c0b", "keepalive": "\u50b3\u9001\u4fdd\u6301\u6d3b\u52d5\u8a0a\u606f\u9593\u9694\u6642\u9593", "password": "\u5bc6\u78bc", "port": "\u901a\u8a0a\u57e0", diff --git a/homeassistant/components/mysensors/translations/ca.json b/homeassistant/components/mysensors/translations/ca.json index 94ba8b80c2f..23c07adc9fa 100644 --- a/homeassistant/components/mysensors/translations/ca.json +++ b/homeassistant/components/mysensors/translations/ca.json @@ -34,7 +34,6 @@ "invalid_serial": "Port s\u00e8rie inv\u00e0lid", "invalid_subscribe_topic": "Topic de subscripci\u00f3 inv\u00e0lid", "invalid_version": "Versi\u00f3 de MySensors inv\u00e0lida", - "mqtt_required": "La integraci\u00f3 MQTT no est\u00e0 configurada", "not_a_number": "Introdueix un n\u00famero", "port_out_of_range": "El n\u00famero de port ha d'estar entre 1 i 65535", "same_topic": "Els topics de publicaci\u00f3 i subscripci\u00f3 son els mateixos", diff --git a/homeassistant/components/mysensors/translations/de.json b/homeassistant/components/mysensors/translations/de.json index 2ab3da62431..5c477806600 100644 --- a/homeassistant/components/mysensors/translations/de.json +++ b/homeassistant/components/mysensors/translations/de.json @@ -34,7 +34,6 @@ "invalid_serial": "Ung\u00fcltiger Serieller Port", "invalid_subscribe_topic": "Ung\u00fcltiges Abonnementthema", "invalid_version": "Ung\u00fcltige MySensors Version", - "mqtt_required": "Die MQTT-Integration ist nicht eingerichtet", "not_a_number": "Bitte eine Nummer eingeben", "port_out_of_range": "Die Portnummer muss mindestens 1 und darf h\u00f6chstens 65535 sein", "same_topic": "Themen zum Abonnieren und Ver\u00f6ffentlichen sind gleich", diff --git a/homeassistant/components/mysensors/translations/el.json b/homeassistant/components/mysensors/translations/el.json index a761f148c27..f90d3770f7a 100644 --- a/homeassistant/components/mysensors/translations/el.json +++ b/homeassistant/components/mysensors/translations/el.json @@ -34,7 +34,6 @@ "invalid_serial": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03c3\u03b5\u03b9\u03c1\u03b9\u03b1\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1", "invalid_subscribe_topic": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b8\u03ad\u03bc\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2", "invalid_version": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 MySensors", - "mqtt_required": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 MQTT \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af", "not_a_number": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03ce \u03b5\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc", "port_out_of_range": "\u039f \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03b8\u03cd\u03c1\u03b1\u03c2 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c4\u03bf\u03c5\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf\u03bd 1 \u03ba\u03b1\u03b9 \u03c4\u03bf \u03c0\u03bf\u03bb\u03cd 65535", "same_topic": "\u03a4\u03b1 \u03b8\u03ad\u03bc\u03b1\u03c4\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03ba\u03b1\u03b9 \u03b4\u03b7\u03bc\u03bf\u03c3\u03af\u03b5\u03c5\u03c3\u03b7\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c4\u03b1 \u03af\u03b4\u03b9\u03b1", diff --git a/homeassistant/components/mysensors/translations/en.json b/homeassistant/components/mysensors/translations/en.json index 081ae3a2b95..b85a28fb7d3 100644 --- a/homeassistant/components/mysensors/translations/en.json +++ b/homeassistant/components/mysensors/translations/en.json @@ -34,7 +34,6 @@ "invalid_serial": "Invalid serial port", "invalid_subscribe_topic": "Invalid subscribe topic", "invalid_version": "Invalid MySensors version", - "mqtt_required": "The MQTT integration is not set up", "not_a_number": "Please enter a number", "port_out_of_range": "Port number must be at least 1 and at most 65535", "same_topic": "Subscribe and publish topics are the same", diff --git a/homeassistant/components/mysensors/translations/es.json b/homeassistant/components/mysensors/translations/es.json index 62010958d67..03619e114bf 100644 --- a/homeassistant/components/mysensors/translations/es.json +++ b/homeassistant/components/mysensors/translations/es.json @@ -34,7 +34,6 @@ "invalid_serial": "Puerto serie no v\u00e1lido", "invalid_subscribe_topic": "Tema de suscripci\u00f3n no v\u00e1lido", "invalid_version": "Versi\u00f3n no v\u00e1lida de MySensors", - "mqtt_required": "La integraci\u00f3n MQTT no est\u00e1 configurada", "not_a_number": "Por favor, introduce un n\u00famero", "port_out_of_range": "El n\u00famero de puerto debe ser al menos 1 y como m\u00e1ximo 65535", "same_topic": "Los temas de suscripci\u00f3n y publicaci\u00f3n son los mismos", diff --git a/homeassistant/components/mysensors/translations/et.json b/homeassistant/components/mysensors/translations/et.json index a1bcacc1852..06cdc0e74a1 100644 --- a/homeassistant/components/mysensors/translations/et.json +++ b/homeassistant/components/mysensors/translations/et.json @@ -34,7 +34,6 @@ "invalid_serial": "Sobimatu jadaport", "invalid_subscribe_topic": "Kehtetu tellimisteema", "invalid_version": "Sobimatu MySensors versioon", - "mqtt_required": "MQTT sidumine on loomata", "not_a_number": "Sisesta number", "port_out_of_range": "Pordi number peab olema v\u00e4hemalt 1 ja k\u00f5ige rohkem 65535", "same_topic": "Tellimise ja avaldamise teemad kattuvad", diff --git a/homeassistant/components/mysensors/translations/fr.json b/homeassistant/components/mysensors/translations/fr.json index 0c691e64f2f..96724b4d5a9 100644 --- a/homeassistant/components/mysensors/translations/fr.json +++ b/homeassistant/components/mysensors/translations/fr.json @@ -34,7 +34,6 @@ "invalid_serial": "Port s\u00e9rie non valide", "invalid_subscribe_topic": "Sujet d'abonnement non valide", "invalid_version": "Version de MySensors non valide", - "mqtt_required": "L'int\u00e9gration MQTT n'est pas configur\u00e9e", "not_a_number": "Veuillez saisir un nombre", "port_out_of_range": "Le num\u00e9ro de port doit \u00eatre au moins 1 et au plus 65535", "same_topic": "Les sujets de souscription et de publication sont identiques", diff --git a/homeassistant/components/mysensors/translations/he.json b/homeassistant/components/mysensors/translations/he.json index 9787081e32e..f0a12592138 100644 --- a/homeassistant/components/mysensors/translations/he.json +++ b/homeassistant/components/mysensors/translations/he.json @@ -11,7 +11,6 @@ "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", - "mqtt_required": "\u05e9\u05d9\u05dc\u05d5\u05d1 MQTT \u05d0\u05d9\u05e0\u05d5 \u05de\u05d5\u05d2\u05d3\u05e8", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { diff --git a/homeassistant/components/mysensors/translations/hu.json b/homeassistant/components/mysensors/translations/hu.json index 5253dd7b427..4a921724dd3 100644 --- a/homeassistant/components/mysensors/translations/hu.json +++ b/homeassistant/components/mysensors/translations/hu.json @@ -34,7 +34,6 @@ "invalid_serial": "\u00c9rv\u00e9nytelen soros port", "invalid_subscribe_topic": "\u00c9rv\u00e9nytelen feliratkoz\u00e1si (subscribe) topik", "invalid_version": "\u00c9rv\u00e9nytelen MySensors verzi\u00f3", - "mqtt_required": "Az MQTT integr\u00e1ci\u00f3 nincs be\u00e1ll\u00edtva", "not_a_number": "K\u00e9rem, sz\u00e1mot adjon meg", "port_out_of_range": "A portsz\u00e1mnak legal\u00e1bb 1-nek \u00e9s legfeljebb 65535-nek kell lennie", "same_topic": "A feliratkoz\u00e1s \u00e9s a k\u00f6zz\u00e9t\u00e9tel t\u00e9m\u00e1i ugyanazok", diff --git a/homeassistant/components/mysensors/translations/id.json b/homeassistant/components/mysensors/translations/id.json index 67a7469b48a..e5776c23316 100644 --- a/homeassistant/components/mysensors/translations/id.json +++ b/homeassistant/components/mysensors/translations/id.json @@ -34,7 +34,6 @@ "invalid_serial": "Port serial tidak valid", "invalid_subscribe_topic": "Topik langganan tidak valid", "invalid_version": "Versi MySensors tidak valid", - "mqtt_required": "Integrasi MQTT belum disiapkan", "not_a_number": "Masukkan angka", "port_out_of_range": "Nilai port minimal 1 dan maksimal 65535", "same_topic": "Topik subscribe dan publish sama", diff --git a/homeassistant/components/mysensors/translations/it.json b/homeassistant/components/mysensors/translations/it.json index 0f4a2746790..b7063bb5e47 100644 --- a/homeassistant/components/mysensors/translations/it.json +++ b/homeassistant/components/mysensors/translations/it.json @@ -34,7 +34,6 @@ "invalid_serial": "Porta seriale non valida", "invalid_subscribe_topic": "Argomento di sottoscrizione non valido", "invalid_version": "Versione di MySensors non valida", - "mqtt_required": "L'integrazione MQTT non \u00e8 configurata", "not_a_number": "Digita un numero", "port_out_of_range": "Il numero di porta deve essere almeno 1 e al massimo 65535", "same_topic": "Gli argomenti di sottoscrizione e pubblicazione sono gli stessi", diff --git a/homeassistant/components/mysensors/translations/ja.json b/homeassistant/components/mysensors/translations/ja.json index fd2294ab820..689e7041f6d 100644 --- a/homeassistant/components/mysensors/translations/ja.json +++ b/homeassistant/components/mysensors/translations/ja.json @@ -34,7 +34,6 @@ "invalid_serial": "\u7121\u52b9\u306a\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8", "invalid_subscribe_topic": "\u7121\u52b9\u306a\u30b5\u30d6\u30b9\u30af\u30e9\u30a4\u30d6 \u30c8\u30d4\u30c3\u30af", "invalid_version": "MySensors\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u7121\u52b9\u3067\u3059", - "mqtt_required": "MQTT\u7d71\u5408\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093", "not_a_number": "\u6570\u5b57\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044", "port_out_of_range": "\u30dd\u30fc\u30c8\u756a\u53f7\u306f1\u4ee5\u4e0a65535\u4ee5\u4e0b\u3067\u3042\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", "same_topic": "\u30b5\u30d6\u30b9\u30af\u30e9\u30a4\u30d6\u3068\u30d1\u30d6\u30ea\u30c3\u30b7\u30e5\u306e\u30c8\u30d4\u30c3\u30af\u304c\u540c\u3058\u3067\u3059", diff --git a/homeassistant/components/mysensors/translations/ko.json b/homeassistant/components/mysensors/translations/ko.json index 4d5be0f44e3..7a6f3e856a7 100644 --- a/homeassistant/components/mysensors/translations/ko.json +++ b/homeassistant/components/mysensors/translations/ko.json @@ -33,7 +33,6 @@ "invalid_serial": "\uc2dc\ub9ac\uc5bc \ud3ec\ud2b8\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "invalid_subscribe_topic": "\uad6c\ub3c5 \ud1a0\ud53d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "invalid_version": "MySensors \ubc84\uc804\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "mqtt_required": "MQTT \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uac00 \uc124\uc815\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", "not_a_number": "\uc22b\uc790\ub85c \uc785\ub825\ud574\uc8fc\uc138\uc694", "port_out_of_range": "\ud3ec\ud2b8 \ubc88\ud638\ub294 1 \uc774\uc0c1 65535 \uc774\ud558\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4", "same_topic": "\uad6c\ub3c5 \ubc0f \ubc1c\ud589 \ud1a0\ud53d\uc740 \ub3d9\uc77c\ud569\ub2c8\ub2e4", diff --git a/homeassistant/components/mysensors/translations/nl.json b/homeassistant/components/mysensors/translations/nl.json index 51ddd56e6d6..ba2eecff5d8 100644 --- a/homeassistant/components/mysensors/translations/nl.json +++ b/homeassistant/components/mysensors/translations/nl.json @@ -34,7 +34,6 @@ "invalid_serial": "Ongeldige seri\u00eble poort", "invalid_subscribe_topic": "Ongeldig abonneer topic", "invalid_version": "Ongeldige MySensors-versie", - "mqtt_required": "De MQTT integratie is niet ingesteld", "not_a_number": "Voer een nummer in", "port_out_of_range": "Poortnummer moet minimaal 1 en maximaal 65535 zijn", "same_topic": "De topics abonneren en publiceren zijn hetzelfde", diff --git a/homeassistant/components/mysensors/translations/no.json b/homeassistant/components/mysensors/translations/no.json index 1e45899d981..0f119f61028 100644 --- a/homeassistant/components/mysensors/translations/no.json +++ b/homeassistant/components/mysensors/translations/no.json @@ -34,7 +34,6 @@ "invalid_serial": "Ugyldig serieport", "invalid_subscribe_topic": "Ugyldig abonnementsemne", "invalid_version": "Ugyldig MySensors-versjon", - "mqtt_required": "MQTT-integrasjonen er ikke satt opp", "not_a_number": "Vennligst skriv inn et nummer", "port_out_of_range": "Portnummer m\u00e5 v\u00e6re minst 1 og maksimalt 65535", "same_topic": "Abonner og publiser emner er de samme", diff --git a/homeassistant/components/mysensors/translations/pl.json b/homeassistant/components/mysensors/translations/pl.json index ef473a6aff5..689bc021e89 100644 --- a/homeassistant/components/mysensors/translations/pl.json +++ b/homeassistant/components/mysensors/translations/pl.json @@ -34,7 +34,6 @@ "invalid_serial": "Nieprawid\u0142owy port szeregowy", "invalid_subscribe_topic": "Nieprawid\u0142owy temat \"subscribe\"", "invalid_version": "Nieprawid\u0142owa wersja MySensors", - "mqtt_required": "Integracja MQTT nie jest skonfigurowana", "not_a_number": "Prosz\u0119 wpisa\u0107 numer", "port_out_of_range": "Numer portu musi by\u0107 pomi\u0119dzy 1 a 65535", "same_topic": "Tematy \"subscribe\" i \"publish\" s\u0105 takie same", diff --git a/homeassistant/components/mysensors/translations/pt-BR.json b/homeassistant/components/mysensors/translations/pt-BR.json index 29b8a9e9372..ea76d19f358 100644 --- a/homeassistant/components/mysensors/translations/pt-BR.json +++ b/homeassistant/components/mysensors/translations/pt-BR.json @@ -34,7 +34,6 @@ "invalid_serial": "Porta serial inv\u00e1lida", "invalid_subscribe_topic": "T\u00f3pico de inscri\u00e7\u00e3o inv\u00e1lido", "invalid_version": "Vers\u00e3o MySensors inv\u00e1lida", - "mqtt_required": "A integra\u00e7\u00e3o do MQTT n\u00e3o est\u00e1 configurada", "not_a_number": "Por favor, digite um n\u00famero", "port_out_of_range": "O n\u00famero da porta deve ser no m\u00ednimo 1 e no m\u00e1ximo 65535", "same_topic": "Subscrever e publicar t\u00f3picos s\u00e3o os mesmos", diff --git a/homeassistant/components/mysensors/translations/ru.json b/homeassistant/components/mysensors/translations/ru.json index 8d24debaf33..eaac7b9230e 100644 --- a/homeassistant/components/mysensors/translations/ru.json +++ b/homeassistant/components/mysensors/translations/ru.json @@ -34,7 +34,6 @@ "invalid_serial": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442.", "invalid_subscribe_topic": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u043e\u043f\u0438\u043a \u0434\u043b\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438.", "invalid_version": "\u041d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f MySensors.", - "mqtt_required": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f MQTT \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430.", "not_a_number": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0447\u0438\u0441\u043b\u043e.", "port_out_of_range": "\u041d\u043e\u043c\u0435\u0440 \u043f\u043e\u0440\u0442\u0430 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043e\u0442 1 \u0434\u043e 65535.", "same_topic": "\u0422\u043e\u043f\u0438\u043a\u0438 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u0438 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u044e\u0442.", diff --git a/homeassistant/components/mysensors/translations/sv.json b/homeassistant/components/mysensors/translations/sv.json index 7398b60346a..077987f5dd4 100644 --- a/homeassistant/components/mysensors/translations/sv.json +++ b/homeassistant/components/mysensors/translations/sv.json @@ -34,7 +34,6 @@ "invalid_serial": "Ogiltig serieport", "invalid_subscribe_topic": "Ogiltigt \u00e4mne f\u00f6r prenumeration", "invalid_version": "Ogiltig version av MySensors", - "mqtt_required": "MQTT-integrationen \u00e4r inte konfigurerad", "not_a_number": "Ange ett nummer", "port_out_of_range": "Portnummer m\u00e5ste vara minst 1 och h\u00f6gst 65535", "same_topic": "\u00c4mnen f\u00f6r prenumeration och publicering \u00e4r desamma", diff --git a/homeassistant/components/mysensors/translations/tr.json b/homeassistant/components/mysensors/translations/tr.json index 9fc26a7119b..cd66bb79a0b 100644 --- a/homeassistant/components/mysensors/translations/tr.json +++ b/homeassistant/components/mysensors/translations/tr.json @@ -34,7 +34,6 @@ "invalid_serial": "Ge\u00e7ersiz seri ba\u011flant\u0131 noktas\u0131", "invalid_subscribe_topic": "Ge\u00e7ersiz abone konusu", "invalid_version": "Ge\u00e7ersiz MySensors s\u00fcr\u00fcm\u00fc", - "mqtt_required": "MQTT entegrasyonu kurulmam\u0131\u015f", "not_a_number": "L\u00fctfen bir numara giriniz", "port_out_of_range": "Port numaras\u0131 en az 1, en fazla 65535 olmal\u0131d\u0131r", "same_topic": "Abone olma ve yay\u0131nlama konular\u0131 ayn\u0131d\u0131r", diff --git a/homeassistant/components/mysensors/translations/zh-Hans.json b/homeassistant/components/mysensors/translations/zh-Hans.json index a1df0a563a5..bb7390e01e1 100644 --- a/homeassistant/components/mysensors/translations/zh-Hans.json +++ b/homeassistant/components/mysensors/translations/zh-Hans.json @@ -1,8 +1,5 @@ { "config": { - "error": { - "mqtt_required": "\u672a\u914d\u7f6e MQTT \u96c6\u6210" - }, "step": { "gw_mqtt": { "data": { diff --git a/homeassistant/components/mysensors/translations/zh-Hant.json b/homeassistant/components/mysensors/translations/zh-Hant.json index 378624f65b0..a86f7c480ff 100644 --- a/homeassistant/components/mysensors/translations/zh-Hant.json +++ b/homeassistant/components/mysensors/translations/zh-Hant.json @@ -34,7 +34,6 @@ "invalid_serial": "\u5e8f\u5217\u57e0\u7121\u6548", "invalid_subscribe_topic": "\u8a02\u95b1\u4e3b\u984c\u7121\u6548", "invalid_version": "MySensors \u7248\u672c\u7121\u6548", - "mqtt_required": "MQTT \u6574\u5408\u5c1a\u672a\u8a2d\u5b9a", "not_a_number": "\u8acb\u8f38\u5165\u6578\u5b57", "port_out_of_range": "\u8acb\u8f38\u5165\u4ecb\u65bc 1 \u81f3 65535 \u4e4b\u9593\u7684\u865f\u78bc", "same_topic": "\u8a02\u95b1\u8207\u767c\u4f48\u4e3b\u984c\u76f8\u540c", diff --git a/homeassistant/components/nest/translations/af.json b/homeassistant/components/nest/translations/af.json deleted file mode 100644 index cedc2123597..00000000000 --- a/homeassistant/components/nest/translations/af.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "config": { - "step": { - "auth": { - "data": { - "code": "Hozz\u00e1f\u00e9r\u00e9si token" - }, - "title": "Google fi\u00f3k kapcsol\u00e1sa" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/nest/translations/ca.json b/homeassistant/components/nest/translations/ca.json index e3d55a7c84d..a55bd393b6a 100644 --- a/homeassistant/components/nest/translations/ca.json +++ b/homeassistant/components/nest/translations/ca.json @@ -10,7 +10,6 @@ "missing_configuration": "El component no est\u00e0 configurat. Mira'n la documentaci\u00f3.", "no_url_available": "No hi ha cap URL disponible. Per a m\u00e9s informaci\u00f3 sobre aquest error, [consulta la secci\u00f3 d'ajuda]({docs_url})", "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament", - "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3.", "unknown_authorize_url_generation": "S'ha produ\u00eft un error desconegut al generar URL d'autoritzaci\u00f3." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "Introdueix un ID de projecte Cloud v\u00e0lid (era el mateix que l'ID de projecte Device Access)" }, "step": { - "auth": { - "data": { - "code": "Token d'acc\u00e9s" - }, - "description": "Per enlla\u00e7ar un compte de Google, [autoritza el compte]({url}). \n\nDespr\u00e9s de l'autoritzaci\u00f3, copia i enganxa a continuaci\u00f3 el codi 'token' d'autenticaci\u00f3 proporcionat.", - "title": "Vinculaci\u00f3 amb compte de Google" - }, "auth_upgrade": { "description": "Google ha deixat d'utilitzar l'autenticaci\u00f3 d'aplicacions per millorar la seguretat i has de crear noves credencials d'aplicaci\u00f3. \n\nConsulta la [documentaci\u00f3]({more_info_url}) i segueix els passos que et guiaran per tornar a tenir acc\u00e9s als teus dispositius Nest.", "title": "Nest: l'autenticaci\u00f3 d'aplicaci\u00f3 s'acaba" diff --git a/homeassistant/components/nest/translations/cs.json b/homeassistant/components/nest/translations/cs.json index b41ce556cfa..32579a47392 100644 --- a/homeassistant/components/nest/translations/cs.json +++ b/homeassistant/components/nest/translations/cs.json @@ -6,7 +6,6 @@ "missing_configuration": "Komponenta nen\u00ed nastavena. Postupujte podle dokumentace.", "no_url_available": "Nen\u00ed k dispozici \u017e\u00e1dn\u00e1 adresa URL. Informace o t\u00e9to chyb\u011b naleznete [v sekci n\u00e1pov\u011bdy]({docs_url})", "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9", - "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace.", "unknown_authorize_url_generation": "Nezn\u00e1m\u00e1 chyba p\u0159i generov\u00e1n\u00ed autoriza\u010dn\u00ed URL adresy." }, "create_entry": { @@ -19,11 +18,6 @@ "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { - "auth": { - "data": { - "code": "P\u0159\u00edstupov\u00fd token" - } - }, "init": { "data": { "flow_impl": "Poskytovatel" diff --git a/homeassistant/components/nest/translations/de.json b/homeassistant/components/nest/translations/de.json index efe0ee8c115..448c103ae17 100644 --- a/homeassistant/components/nest/translations/de.json +++ b/homeassistant/components/nest/translations/de.json @@ -10,7 +10,6 @@ "missing_configuration": "Die Komponente ist nicht konfiguriert. Bitte der Dokumentation folgen.", "no_url_available": "Keine URL verf\u00fcgbar. Informationen zu diesem Fehler findest du [im Hilfebereich]({docs_url}).", "reauth_successful": "Die erneute Authentifizierung war erfolgreich", - "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich.", "unknown_authorize_url_generation": "Beim Generieren einer Authentifizierungs-URL ist ein unbekannter Fehler aufgetreten" }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "Gib eine g\u00fcltige Cloud-Projekt-ID ein (identisch mit der Projekt-ID f\u00fcr den Ger\u00e4tezugriff)." }, "step": { - "auth": { - "data": { - "code": "Zugangstoken" - }, - "description": "Um dein Google-Konto zu verkn\u00fcpfen, w\u00e4hle [Konto autorisieren]({url}).\n\nKopiere nach der Autorisierung den unten angegebenen Authentifizierungstoken-Code.", - "title": "Google-Konto verkn\u00fcpfen" - }, "auth_upgrade": { "description": "App Auth wurde von Google abgeschafft, um die Sicherheit zu verbessern, und du musst Ma\u00dfnahmen ergreifen, indem du neue Anmeldedaten f\u00fcr die Anwendung erstellst.\n\n\u00d6ffnen die [Dokumentation]({more_info_url}) und folge den n\u00e4chsten Schritten, die du durchf\u00fchren musst, um den Zugriff auf deine Nest-Ger\u00e4te wiederherzustellen.", "title": "Nest: Einstellung der App-Authentifizierung" diff --git a/homeassistant/components/nest/translations/el.json b/homeassistant/components/nest/translations/el.json index 69b6f096d8a..6964337c919 100644 --- a/homeassistant/components/nest/translations/el.json +++ b/homeassistant/components/nest/translations/el.json @@ -10,7 +10,6 @@ "missing_configuration": "\u03a4\u03bf \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03bf \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7.", "no_url_available": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL. \u0393\u03b9\u03b1 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1, [\u03b5\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b2\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1\u03c2] ( {docs_url} )", "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", - "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae.", "unknown_authorize_url_generation": "\u0386\u03b3\u03bd\u03c9\u03c3\u03c4\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7\u03c2 URL \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03ad\u03c1\u03b3\u03bf\u03c5 Cloud (\u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c4\u03bf \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03ad\u03c1\u03b3\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2)" }, "step": { - "auth": { - "data": { - "code": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" - }, - "description": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2 \u03c3\u03c4\u03bf Google, [\u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2]({url}).\n\n\u039c\u03b5\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7, \u03b1\u03bd\u03c4\u03b9\u03b3\u03c1\u03ac\u03c8\u03c4\u03b5-\u03b5\u03c0\u03b9\u03ba\u03bf\u03bb\u03bb\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9 \u03c0\u03b1\u03c1\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc Auth Token.", - "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd Google" - }, "auth_upgrade": { "description": "\u03a4\u03bf App Auth \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd Google \u03b3\u03b9\u03b1 \u03b2\u03b5\u03bb\u03c4\u03af\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b1\u03c3\u03c6\u03ac\u03bb\u03b5\u03b9\u03b1\u03c2 \u03ba\u03b1\u03b9 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03b5 \u03b5\u03bd\u03ad\u03c1\u03b3\u03b5\u03b9\u03b5\u03c2 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ce\u03bd\u03c4\u03b1\u03c2 \u03bd\u03ad\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2. \n\n \u0391\u03bd\u03bf\u03af\u03be\u03c4\u03b5 \u03c4\u03bf [documentation]( {more_info_url} ) \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03b5\u03c4\u03b5, \u03ba\u03b1\u03b8\u03ce\u03c2 \u03c4\u03b1 \u03b5\u03c0\u03cc\u03bc\u03b5\u03bd\u03b1 \u03b2\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b8\u03b1 \u03c3\u03b1\u03c2 \u03ba\u03b1\u03b8\u03bf\u03b4\u03b7\u03b3\u03ae\u03c3\u03bf\u03c5\u03bd \u03c3\u03c4\u03b1 \u03b2\u03ae\u03bc\u03b1\u03c4\u03b1 \u03c0\u03bf\u03c5 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03b5\u03c4\u03b5 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b5\u03c0\u03b1\u03bd\u03b1\u03c6\u03ad\u03c1\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7 \u03c3\u03c4\u03b9\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 Nest.", "title": "Nest: \u039a\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2" diff --git a/homeassistant/components/nest/translations/en.json b/homeassistant/components/nest/translations/en.json index 07678227547..b29cffc2d91 100644 --- a/homeassistant/components/nest/translations/en.json +++ b/homeassistant/components/nest/translations/en.json @@ -10,7 +10,6 @@ "missing_configuration": "The component is not configured. Please follow the documentation.", "no_url_available": "No URL available. For information about this error, [check the help section]({docs_url})", "reauth_successful": "Re-authentication was successful", - "single_instance_allowed": "Already configured. Only a single configuration possible.", "unknown_authorize_url_generation": "Unknown error generating an authorize URL." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "Please enter a valid Cloud Project ID (was same as Device Access Project ID)" }, "step": { - "auth": { - "data": { - "code": "Access Token" - }, - "description": "To link your Google account, [authorize your account]({url}).\n\nAfter authorization, copy-paste the provided Auth Token code below.", - "title": "Link Google Account" - }, "auth_upgrade": { "description": "App Auth has been deprecated by Google to improve security, and you need to take action by creating new application credentials.\n\nOpen the [documentation]({more_info_url}) to follow along as the next steps will guide you through the steps you need to take to restore access to your Nest devices.", "title": "Nest: App Auth Deprecation" diff --git a/homeassistant/components/nest/translations/es.json b/homeassistant/components/nest/translations/es.json index 95c8aa5ef04..8f919a535f4 100644 --- a/homeassistant/components/nest/translations/es.json +++ b/homeassistant/components/nest/translations/es.json @@ -10,7 +10,6 @@ "missing_configuration": "El componente no est\u00e1 configurado. Por favor, sigue la documentaci\u00f3n.", "no_url_available": "No hay URL disponible. Para obtener informaci\u00f3n sobre este error, [revisa la secci\u00f3n de ayuda]({docs_url})", "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente", - "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n.", "unknown_authorize_url_generation": "Error desconocido al generar una URL de autorizaci\u00f3n." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "Por favor, introduce un ID de proyecto en la nube v\u00e1lido (era el mismo que el ID del proyecto de acceso al dispositivo)" }, "step": { - "auth": { - "data": { - "code": "Token de acceso" - }, - "description": "Para vincular tu cuenta de Google, [autoriza tu cuenta]({url}).\n\nDespu\u00e9s de la autorizaci\u00f3n, copia y pega el c\u00f3digo Auth Token proporcionado a continuaci\u00f3n.", - "title": "Vincular cuenta de Google" - }, "auth_upgrade": { "description": "Google ha dejado de usar App Auth para mejorar la seguridad, y debes tomar medidas creando nuevas credenciales de aplicaci\u00f3n. \n\nAbre la [documentaci\u00f3n]({more_info_url}) para seguir, ya que los siguientes pasos te guiar\u00e1n a trav\u00e9s de los pasos que debes seguir para restaurar el acceso a tus dispositivos Nest.", "title": "Nest: desactivaci\u00f3n de App Auth" diff --git a/homeassistant/components/nest/translations/et.json b/homeassistant/components/nest/translations/et.json index 655515e93f8..dc882db22da 100644 --- a/homeassistant/components/nest/translations/et.json +++ b/homeassistant/components/nest/translations/et.json @@ -10,7 +10,6 @@ "missing_configuration": "Osis pole seadistatud. Vaata dokumentatsiooni.", "no_url_available": "URL pole saadaval. Selle t\u00f5rke kohta teabe saamiseks vaata [spikrijaotis]({docs_url})", "reauth_successful": "Taastuvastamine \u00f5nnestus", - "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine.", "unknown_authorize_url_generation": "Tundmatu viga tuvastamise URL-i loomisel." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "Sisesta kehtiv pilveprojekti ID (leitud seadme juurdep\u00e4\u00e4su projekti ID)" }, "step": { - "auth": { - "data": { - "code": "Juurdep\u00e4\u00e4sut\u00f5end" - }, - "description": "Oma Google'i konto sidumiseks vali [autoriseeri oma konto]({url}).\n\nP\u00e4rast autoriseerimist kopeeri ja aseta allpool esitatud Auth Token'i kood.", - "title": "Google'i konto linkimine" - }, "auth_upgrade": { "description": "Google on app Authi turvalisuse parandamiseks tauninud ja pead tegutsema, luues uusi rakenduse mandaate.\n\nAva [dokumentatsioon]({more_info_url}), mida j\u00e4rgida, kuna j\u00e4rgmised juhised juhendavad teid nest-seadmetele juurdep\u00e4\u00e4su taastamiseks vajalike juhiste kaudu.", "title": "Nest: App Auth Deprecation" diff --git a/homeassistant/components/nest/translations/fr.json b/homeassistant/components/nest/translations/fr.json index 4d7f2d35f06..7ef70e386db 100644 --- a/homeassistant/components/nest/translations/fr.json +++ b/homeassistant/components/nest/translations/fr.json @@ -7,7 +7,6 @@ "missing_configuration": "Le composant n'est pas configur\u00e9. Veuillez suivre la documentation.", "no_url_available": "Aucune URL disponible. Pour plus d'informations sur cette erreur, [consultez la section d'aide]({docs_url})", "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi", - "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible.", "unknown_authorize_url_generation": "Erreur inconnue lors de la g\u00e9n\u00e9ration d'une URL d'autorisation." }, "create_entry": { @@ -23,13 +22,6 @@ "wrong_project_id": "Veuillez saisir un ID de projet Cloud valide (\u00e9tait identique \u00e0 l'ID de projet d'acc\u00e8s \u00e0 l'appareil)" }, "step": { - "auth": { - "data": { - "code": "Jeton d'acc\u00e8s" - }, - "description": "Pour lier votre compte Google, [autorisez votre compte]( {url} ). \n\n Apr\u00e8s autorisation, copiez-collez le code d'authentification fourni ci-dessous.", - "title": "Associer un compte Google" - }, "auth_upgrade": { "title": "Nest\u00a0: abandon de l'authentification d'application" }, diff --git a/homeassistant/components/nest/translations/he.json b/homeassistant/components/nest/translations/he.json index 7c41202e85e..a3576547944 100644 --- a/homeassistant/components/nest/translations/he.json +++ b/homeassistant/components/nest/translations/he.json @@ -7,7 +7,6 @@ "missing_configuration": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05e8\u05db\u05d9\u05d1 \u05dc\u05d0 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e0\u05d0 \u05e2\u05e7\u05d5\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3.", "no_url_available": "\u05d0\u05d9\u05df \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d6\u05de\u05d9\u05e0\u05d4. \u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e2\u05dc \u05e9\u05d2\u05d9\u05d0\u05d4 \u05d6\u05d5, [\u05e2\u05d9\u05d9\u05df \u05d1\u05e1\u05e2\u05d9\u05e3 \u05d4\u05e2\u05d6\u05e8\u05d4] ({docs_url})", "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7", - "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea.", "unknown_authorize_url_generation": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05dc\u05d0 \u05d9\u05d3\u05d5\u05e2\u05d4 \u05d1\u05d9\u05e6\u05d9\u05e8\u05ea \u05db\u05ea\u05d5\u05d1\u05ea URL \u05e9\u05dc \u05d4\u05e8\u05e9\u05d0\u05d4." }, "create_entry": { @@ -20,11 +19,6 @@ "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { - "auth": { - "data": { - "code": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4" - } - }, "init": { "data": { "flow_impl": "\u05e1\u05e4\u05e7" diff --git a/homeassistant/components/nest/translations/hu.json b/homeassistant/components/nest/translations/hu.json index 6f868eb7aab..ee372dc0314 100644 --- a/homeassistant/components/nest/translations/hu.json +++ b/homeassistant/components/nest/translations/hu.json @@ -10,7 +10,6 @@ "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", - "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges.", "unknown_authorize_url_generation": "Ismeretlen hiba t\u00f6rt\u00e9nt a hiteles\u00edt\u00e9si link gener\u00e1l\u00e1sa sor\u00e1n." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "K\u00e9rem, adjon meg egy \u00e9rv\u00e9nyes Cloud Project ID-t (Device Access Project ID-vrl azonos)" }, "step": { - "auth": { - "data": { - "code": "Hozz\u00e1f\u00e9r\u00e9si token" - }, - "description": "[Enged\u00e9lyezze]({url}) Google-fi\u00f3kj\u00e1t az \u00f6sszekapcsol\u00e1hoz.\n\nAz enged\u00e9lyez\u00e9s ut\u00e1n m\u00e1solja \u00e1t a kapott token k\u00f3dot.", - "title": "\u00d6sszekapcsol\u00e1s Google-al" - }, "auth_upgrade": { "description": "A Google a biztons\u00e1g jav\u00edt\u00e1sa \u00e9rdek\u00e9ben megsz\u00fcntette az App Auth szolg\u00e1ltat\u00e1st, \u00e9s \u00d6nnek \u00faj alkalmaz\u00e1s hiteles\u00edt\u00e9si adatainak l\u00e9trehoz\u00e1s\u00e1val kell tennie valamit. \n\n Nyissa meg a [dokument\u00e1ci\u00f3t]({more_info_url}), hogy k\u00f6vesse, mivel a k\u00f6vetkez\u0151 l\u00e9p\u00e9sek v\u00e9gigvezetik a Nest-eszk\u00f6zeihez val\u00f3 hozz\u00e1f\u00e9r\u00e9s vissza\u00e1ll\u00edt\u00e1s\u00e1hoz sz\u00fcks\u00e9ges l\u00e9p\u00e9seken.", "title": "Nest: Az alkalmaz\u00e1shiteles\u00edt\u00e9s megsz\u00fcntet\u00e9se" diff --git a/homeassistant/components/nest/translations/id.json b/homeassistant/components/nest/translations/id.json index 9401016b990..28807e5a46c 100644 --- a/homeassistant/components/nest/translations/id.json +++ b/homeassistant/components/nest/translations/id.json @@ -10,7 +10,6 @@ "missing_configuration": "Komponen tidak dikonfigurasi. Ikuti petunjuk dalam dokumentasi.", "no_url_available": "Tidak ada URL yang tersedia. Untuk informasi tentang kesalahan ini, [lihat bagian bantuan]({docs_url})", "reauth_successful": "Autentikasi ulang berhasil", - "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan.", "unknown_authorize_url_generation": "Kesalahan tidak dikenal ketika menghasilkan URL otorisasi." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "Masukkan ID Proyek Cloud yang valid (sebelumnya sama dengan ID Proyek Akses Perangkat)" }, "step": { - "auth": { - "data": { - "code": "Token Akses" - }, - "description": "Untuk menautkan akun Google Anda, [otorisasi akun Anda]({url}).\n\nSetelah otorisasi, salin dan tempel Token Auth yang disediakan di bawah ini.", - "title": "Tautkan Akun Google" - }, "auth_upgrade": { "description": "Autentikasi Aplikasi tidak digunakan lagi oleh Google untuk meningkatkan keamanan, dan Anda perlu mengambil tindakan dengan membuat kredensial aplikasi baru. \n\nBuka [dokumentasi]({more_info_url}) untuk panduan langkah selanjutnya yang perlu diambil untuk memulihkan akses ke perangkat Nest Anda.", "title": "Nest: Penghentian Autentikasi Aplikasi" diff --git a/homeassistant/components/nest/translations/it.json b/homeassistant/components/nest/translations/it.json index 6cdfa35b194..dede26423e1 100644 --- a/homeassistant/components/nest/translations/it.json +++ b/homeassistant/components/nest/translations/it.json @@ -10,7 +10,6 @@ "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", - "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione.", "unknown_authorize_url_generation": "Errore sconosciuto durante la generazione di un URL di autorizzazione." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "Inserisci un ID progetto cloud valido (uguale all'ID progetto di accesso al dispositivo)" }, "step": { - "auth": { - "data": { - "code": "Token di accesso" - }, - "description": "Per collegare l'account Google, [authorize your account]({url}).\n\nDopo l'autorizzazione, copia-incolla il codice PIN fornito.", - "title": "Connetti l'account Google" - }, "auth_upgrade": { "description": "App Auth \u00e8 stato ritirato da Google per migliorare la sicurezza ed \u00e8 necessario intervenire creando nuove credenziali per l'applicazione. \n\nApri la [documentazione]({more_info_url}) per seguire i passaggi successivi che ti guideranno attraverso gli stadi necessari per ripristinare l'accesso ai tuoi dispositivi Nest.", "title": "Nest: ritiro dell'autenticazione dell'app" diff --git a/homeassistant/components/nest/translations/ja.json b/homeassistant/components/nest/translations/ja.json index 68085a7e30a..f93a4ecea9c 100644 --- a/homeassistant/components/nest/translations/ja.json +++ b/homeassistant/components/nest/translations/ja.json @@ -10,7 +10,6 @@ "missing_configuration": "\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306b\u5f93\u3063\u3066\u304f\u3060\u3055\u3044\u3002", "no_url_available": "\u4f7f\u7528\u53ef\u80fd\u306aURL\u304c\u3042\u308a\u307e\u305b\u3093\u3002\u3053\u306e\u30a8\u30e9\u30fc\u306e\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001[\u30d8\u30eb\u30d7\u30bb\u30af\u30b7\u30e7\u30f3\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044]({docs_url})", "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f", - "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u8a2d\u5b9a\u3067\u304d\u308b\u306e\u306f1\u3064\u3060\u3051\u3067\u3059\u3002", "unknown_authorize_url_generation": "\u8a8d\u8a3cURL\u306e\u751f\u6210\u4e2d\u306b\u4e0d\u660e\u306a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002" }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "\u6709\u52b9\u306aCloud Project ID\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044(\u30c7\u30d0\u30a4\u30b9\u30a2\u30af\u30bb\u30b9 \u30d7\u30ed\u30b8\u30a7\u30af\u30c8ID\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f)" }, "step": { - "auth": { - "data": { - "code": "\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3" - }, - "description": "Google\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u30ea\u30f3\u30af\u3059\u308b\u306b\u306f\u3001 [authorize your account]({url}) \u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n\u8a8d\u8a3c\u5f8c\u3001\u63d0\u4f9b\u3055\u308c\u305f\u8a8d\u8a3c\u30c8\u30fc\u30af\u30f3\u306e\u30b3\u30fc\u30c9\u3092\u4ee5\u4e0b\u306b\u30b3\u30d4\u30fc\u30da\u30fc\u30b9\u30c8\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "Google\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u30ea\u30f3\u30af\u3059\u308b" - }, "auth_upgrade": { "description": "App Auth\u306f\u3001\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u3092\u5411\u4e0a\u3055\u305b\u308b\u305f\u3081\u306bGoogle\u306b\u3088\u3063\u3066\u5ec3\u6b62\u3055\u308c\u307e\u3057\u305f\u3002\u65b0\u3057\u3044\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u8cc7\u683c\u60c5\u5831\u3092\u4f5c\u6210\u3059\u308b\u3053\u3068\u304c\u5fc5\u8981\u3067\u3059\u3002 \n\n [\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8]({more_info_url}) \u3092\u958b\u304d\u3001\u6b21\u306e\u624b\u9806\u306b\u5f93\u3063\u3066\u3001Nest\u30c7\u30d0\u30a4\u30b9\u3078\u306e\u30a2\u30af\u30bb\u30b9\u3092\u5fa9\u5143\u3059\u308b\u305f\u3081\u306b\u5fc5\u8981\u306a\u624b\u9806\u3092\u8aac\u660e\u3057\u307e\u3059\u3002", "title": "\u30cd\u30b9\u30c8: \u30a2\u30d7\u30ea\u8a8d\u8a3c\u306e\u975e\u63a8\u5968" diff --git a/homeassistant/components/nest/translations/ko.json b/homeassistant/components/nest/translations/ko.json index 1b4563c800f..5e38c522b49 100644 --- a/homeassistant/components/nest/translations/ko.json +++ b/homeassistant/components/nest/translations/ko.json @@ -5,7 +5,6 @@ "missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.", "no_url_available": "\uc0ac\uc6a9 \uac00\ub2a5\ud55c URL\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774 \uc624\ub958\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [\ub3c4\uc6c0\ub9d0 \uc139\uc158]({docs_url}) \uc744(\ub97c) \ucc38\uc870\ud574\uc8fc\uc138\uc694.", "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", - "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "unknown_authorize_url_generation": "\uc778\uc99d URL \uc744 \uc0dd\uc131\ud558\ub294 \ub3d9\uc548 \uc54c \uc218 \uc5c6\ub294 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4." }, "create_entry": { diff --git a/homeassistant/components/nest/translations/lb.json b/homeassistant/components/nest/translations/lb.json index 010f34e0dc5..dcf3c42ace5 100644 --- a/homeassistant/components/nest/translations/lb.json +++ b/homeassistant/components/nest/translations/lb.json @@ -4,7 +4,6 @@ "authorize_url_timeout": "Z\u00e4it Iwwerschreidung beim gener\u00e9ieren vun der Autorisatiouns URL.", "missing_configuration": "Komponent net konfigur\u00e9iert. Folleg w.e.g der Dokumentatioun.", "reauth_successful": "Re-authentifikatioun war erfollegr\u00e4ich", - "single_instance_allowed": "Scho konfigur\u00e9iert. N\u00ebmmen eng eenzeg Konfiguratioun m\u00e9iglech.", "unknown_authorize_url_generation": "Onbekannte Feeler beim erstellen vun der Authorisatiouns URL." }, "create_entry": { diff --git a/homeassistant/components/nest/translations/nl.json b/homeassistant/components/nest/translations/nl.json index 95ddcd4184d..eee30bf455c 100644 --- a/homeassistant/components/nest/translations/nl.json +++ b/homeassistant/components/nest/translations/nl.json @@ -7,7 +7,6 @@ "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})", "reauth_successful": "Herauthenticatie geslaagd", - "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", "unknown_authorize_url_generation": "Onbekende fout bij het genereren van een autorisatie-URL." }, "create_entry": { @@ -23,13 +22,6 @@ "wrong_project_id": "Voer een geldig Cloud Project ID in (found Device Acces Project ID)" }, "step": { - "auth": { - "data": { - "code": "Toegangstoken" - }, - "description": "Om uw Google account te koppelen, [authoriseer uw account]({url}).\n\nNa autorisatie, copy-paste u de gegeven toegangstoken hieronder.", - "title": "Link Google Account" - }, "init": { "data": { "flow_impl": "Leverancier" diff --git a/homeassistant/components/nest/translations/no.json b/homeassistant/components/nest/translations/no.json index c72577c1744..80b842aa622 100644 --- a/homeassistant/components/nest/translations/no.json +++ b/homeassistant/components/nest/translations/no.json @@ -10,7 +10,6 @@ "missing_configuration": "Komponenten er ikke konfigurert, vennligst f\u00f8lg dokumentasjonen", "no_url_available": "Ingen URL tilgjengelig. For informasjon om denne feilen, [sjekk hjelpseksjonen]({docs_url})", "reauth_successful": "Re-autentisering var vellykket", - "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig.", "unknown_authorize_url_generation": "Ukjent feil under generering av en autoriserings-URL." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "Angi en gyldig Cloud Project ID (var den samme som Device Access Project ID)" }, "step": { - "auth": { - "data": { - "code": "Tilgangstoken" - }, - "description": "For \u00e5 koble til Google-kontoen din, [autoriser kontoen din]( {url} ). \n\n Etter autorisasjon, kopier og lim inn den oppgitte Auth Token-koden nedenfor.", - "title": "Koble til Google-kontoen" - }, "auth_upgrade": { "description": "App Auth har blitt avviklet av Google for \u00e5 forbedre sikkerheten, og du m\u00e5 iverksette tiltak ved \u00e5 opprette ny applikasjonslegitimasjon. \n\n \u00c5pne [dokumentasjonen]( {more_info_url} ) for \u00e5 f\u00f8lge med, da de neste trinnene vil lede deg gjennom trinnene du m\u00e5 ta for \u00e5 gjenopprette tilgangen til Nest-enhetene dine.", "title": "Nest: Appautentisering avvikelse" diff --git a/homeassistant/components/nest/translations/pl.json b/homeassistant/components/nest/translations/pl.json index 11da4335614..48ed71157f4 100644 --- a/homeassistant/components/nest/translations/pl.json +++ b/homeassistant/components/nest/translations/pl.json @@ -10,7 +10,6 @@ "missing_configuration": "Komponent nie jest skonfigurowany. Post\u0119puj zgodnie z dokumentacj\u0105.", "no_url_available": "Brak dost\u0119pnego adresu URL. Aby uzyska\u0107 informacje na temat tego b\u0142\u0119du, [sprawd\u017a sekcj\u0119 pomocy] ({docs_url})", "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119", - "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja.", "unknown_authorize_url_generation": "Nieznany b\u0142\u0105d podczas generowania URL autoryzacji" }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "Podaj prawid\u0142owy Identyfikator projektu chmury (taki sam jak identyfikator projektu dost\u0119pu do urz\u0105dzenia)" }, "step": { - "auth": { - "data": { - "code": "Token dost\u0119pu" - }, - "description": "Aby po\u0142\u0105czy\u0107 swoje konto Google, [authorize your account]({url}). \n\nPo autoryzacji skopiuj i wklej podany poni\u017cej token uwierzytelniaj\u0105cy.", - "title": "Po\u0142\u0105czenie z kontem Google" - }, "auth_upgrade": { "description": "App Auth zosta\u0142o wycofane przez Google w celu poprawy bezpiecze\u0144stwa i musisz podj\u0105\u0107 dzia\u0142ania, tworz\u0105c nowe dane logowania do aplikacji. \n\nOtw\u00f3rz [dokumentacj\u0119]({more_info_url}), aby przej\u015b\u0107 dalej. Kolejne kroki poprowadz\u0105 Ci\u0119 przez instrukcje, kt\u00f3re musisz wykona\u0107, aby przywr\u00f3ci\u0107 dost\u0119p do urz\u0105dze\u0144 Nest.", "title": "Nest: wycofanie App Auth" diff --git a/homeassistant/components/nest/translations/pt-BR.json b/homeassistant/components/nest/translations/pt-BR.json index 74f68f01775..2fa8a2bab2f 100644 --- a/homeassistant/components/nest/translations/pt-BR.json +++ b/homeassistant/components/nest/translations/pt-BR.json @@ -10,7 +10,6 @@ "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", "no_url_available": "N\u00e3o h\u00e1 URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a se\u00e7\u00e3o de ajuda]({docs_url})", "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida", - "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel.", "unknown_authorize_url_generation": "Erro desconhecido ao gerar um URL de autoriza\u00e7\u00e3o." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "Insira um ID de projeto da nuvem v\u00e1lido (\u00e9 o mesmo que o ID do projeto de acesso ao dispositivo)" }, "step": { - "auth": { - "data": { - "code": "Token de acesso" - }, - "description": "Para vincular sua conta do Google, [autorize sua conta]( {url} ). \n\n Ap\u00f3s a autoriza\u00e7\u00e3o, copie e cole o c\u00f3digo de token de autentica\u00e7\u00e3o fornecido abaixo.", - "title": "Vincular Conta do Google" - }, "auth_upgrade": { "description": "A autentica\u00e7\u00e3o de aplicativo foi preterida pelo Google para melhorar a seguran\u00e7a, e voc\u00ea precisa agir criando novas credenciais de aplicativo. \n\n Abra a [documenta\u00e7\u00e3o]( {more_info_url} ) para acompanhar, pois as pr\u00f3ximas etapas o guiar\u00e3o pelas etapas necess\u00e1rias para restaurar o acesso aos seus dispositivos Nest.", "title": "Nest: suspens\u00e3o de uso da autentica\u00e7\u00e3o do aplicativo" diff --git a/homeassistant/components/nest/translations/pt.json b/homeassistant/components/nest/translations/pt.json index 4e5c4c2c34a..e1022b86b01 100644 --- a/homeassistant/components/nest/translations/pt.json +++ b/homeassistant/components/nest/translations/pt.json @@ -6,7 +6,6 @@ "invalid_access_token": "Token de acesso inv\u00e1lido", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})", - "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel.", "unknown_authorize_url_generation": "Erro desconhecido ao gerar um URL de autoriza\u00e7\u00e3o." }, "create_entry": { @@ -19,11 +18,6 @@ "unknown": "Erro inesperado" }, "step": { - "auth": { - "data": { - "code": "Token de Acesso" - } - }, "init": { "data": { "flow_impl": "Fornecedor" diff --git a/homeassistant/components/nest/translations/ru.json b/homeassistant/components/nest/translations/ru.json index 5a995fb35ae..df461ad87a4 100644 --- a/homeassistant/components/nest/translations/ru.json +++ b/homeassistant/components/nest/translations/ru.json @@ -10,7 +10,6 @@ "missing_configuration": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439.", "no_url_available": "URL-\u0430\u0434\u0440\u0435\u0441 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439]({docs_url}) \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0431 \u044d\u0442\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0435.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.", - "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e.", "unknown_authorize_url_generation": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 Cloud Project ID (\u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u043a\u0430\u043a \u0438 Device Access Project ID)" }, "step": { - "auth": { - "data": { - "code": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430" - }, - "description": "[\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0439\u0442\u0435\u0441\u044c]({url}), \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c Google. \n\n\u041f\u043e\u0441\u043b\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043a\u043e\u043f\u0438\u0440\u0443\u0439\u0442\u0435 \u043f\u0440\u0438\u043b\u0430\u0433\u0430\u0435\u043c\u044b\u0439 \u0442\u043e\u043a\u0435\u043d.", - "title": "\u041f\u0440\u0438\u0432\u044f\u0437\u043a\u0430 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430 Google" - }, "auth_upgrade": { "description": "\u041f\u0440\u0435\u0436\u043d\u0438\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0431\u044b\u043b \u0443\u043f\u0440\u0430\u0437\u0434\u043d\u0435\u043d Google \u0434\u043b\u044f \u043f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438. \u0412\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.\n\n\u0427\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0434\u043b\u044f \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c Nest \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0437\u043d\u0430\u0442\u044c \u0432 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438]({more_info_url}).", "title": "Nest: \u043f\u0440\u0435\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435" diff --git a/homeassistant/components/nest/translations/sk.json b/homeassistant/components/nest/translations/sk.json index 73c43e25db2..038baf1ff9a 100644 --- a/homeassistant/components/nest/translations/sk.json +++ b/homeassistant/components/nest/translations/sk.json @@ -8,13 +8,6 @@ }, "error": { "invalid_pin": "Nespr\u00e1vny PIN" - }, - "step": { - "auth": { - "data": { - "code": "Pr\u00edstupov\u00fd token" - } - } } }, "device_automation": { diff --git a/homeassistant/components/nest/translations/sl.json b/homeassistant/components/nest/translations/sl.json index 836ae7761e8..84f07fdcf42 100644 --- a/homeassistant/components/nest/translations/sl.json +++ b/homeassistant/components/nest/translations/sl.json @@ -11,12 +11,6 @@ "unknown": "Neznana napaka pri preverjanju kode" }, "step": { - "auth": { - "data": { - "code": "\u017deton za dostop" - }, - "title": "Pove\u017eite Google Ra\u010dun" - }, "init": { "data": { "flow_impl": "Ponudnik" diff --git a/homeassistant/components/nest/translations/sv.json b/homeassistant/components/nest/translations/sv.json index 3eae7e1c547..42ea1a7a70f 100644 --- a/homeassistant/components/nest/translations/sv.json +++ b/homeassistant/components/nest/translations/sv.json @@ -10,7 +10,6 @@ "missing_configuration": "Komponenten har inte konfigurerats. F\u00f6lj dokumentationen.", "no_url_available": "Ingen webbadress tillg\u00e4nglig. F\u00f6r information om detta fel, [kolla hj\u00e4lpavsnittet]({docs_url})", "reauth_successful": "\u00c5terautentisering lyckades", - "single_instance_allowed": "Redan konfigurerad. Endast en konfiguration m\u00f6jlig.", "unknown_authorize_url_generation": "Ok\u00e4nt fel vid generering av en auktoriserad URL." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "Ange ett giltigt Cloud Project ID (var samma som Device Access Project ID)" }, "step": { - "auth": { - "data": { - "code": "\u00c5tkomstnyckel" - }, - "description": "F\u00f6r att l\u00e4nka ditt Google-konto, [auktorisera ditt konto]( {url} ). \n\n Efter auktorisering, kopiera och klistra in den medf\u00f6ljande Auth Token-koden nedan.", - "title": "L\u00e4nka Google-konto" - }, "auth_upgrade": { "description": "App Auth har fasats ut av Google f\u00f6r att f\u00f6rb\u00e4ttra s\u00e4kerheten, och du m\u00e5ste vidta \u00e5tg\u00e4rder genom att skapa nya applikationsuppgifter. \n\n \u00d6ppna [dokumentationen]( {more_info_url} ) f\u00f6r att f\u00f6lja med eftersom n\u00e4sta steg guidar dig genom stegen du beh\u00f6ver ta f\u00f6r att \u00e5terst\u00e4lla \u00e5tkomsten till dina Nest-enheter.", "title": "Nest: Utfasning av appautentisering" diff --git a/homeassistant/components/nest/translations/th.json b/homeassistant/components/nest/translations/th.json index 99efbb30cad..5f14558e2b5 100644 --- a/homeassistant/components/nest/translations/th.json +++ b/homeassistant/components/nest/translations/th.json @@ -1,9 +1,6 @@ { "config": { "step": { - "auth": { - "title": "\u0e40\u0e0a\u0e37\u0e48\u0e2d\u0e21\u0e15\u0e48\u0e2d\u0e1a\u0e31\u0e0d\u0e0a\u0e35\u0e02\u0e2d\u0e07 oogle" - }, "link": { "data": { "code": "Pin code" diff --git a/homeassistant/components/nest/translations/tr.json b/homeassistant/components/nest/translations/tr.json index 4e52843ca51..80d72d93d2a 100644 --- a/homeassistant/components/nest/translations/tr.json +++ b/homeassistant/components/nest/translations/tr.json @@ -10,7 +10,6 @@ "missing_configuration": "Bile\u015fen yap\u0131land\u0131r\u0131lmam\u0131\u015f. L\u00fctfen belgeleri takip edin.", "no_url_available": "Kullan\u0131labilir URL yok. Bu hata hakk\u0131nda bilgi i\u00e7in [yard\u0131m b\u00f6l\u00fcm\u00fcne bak\u0131n]({docs_url})", "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu", - "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr.", "unknown_authorize_url_generation": "Yetkilendirme url'si olu\u015fturulurken bilinmeyen hata." }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "L\u00fctfen ge\u00e7erli bir Bulut Projesi Kimli\u011fi girin (Cihaz Eri\u015fimi Proje Kimli\u011fi ile ayn\u0131yd\u0131)" }, "step": { - "auth": { - "data": { - "code": "Eri\u015fim Anahtar\u0131" - }, - "description": "Google hesab\u0131n\u0131z\u0131 ba\u011flamak i\u00e7in [hesab\u0131n\u0131z\u0131 yetkilendirin]( {url} ). \n\n Yetkilendirmeden sonra, sa\u011flanan Auth Token kodunu a\u015fa\u011f\u0131ya kopyalay\u0131p yap\u0131\u015ft\u0131r\u0131n.", - "title": "Google Hesab\u0131n\u0131 Ba\u011fla" - }, "auth_upgrade": { "description": "App Auth, g\u00fcenli\u011fi art\u0131rmak i\u00e7in Google taraf\u0131ndan kullan\u0131mdan kald\u0131r\u0131ld\u0131 ve yeni uygulama kimlik bilgileri olu\u015fturarak i\u015flem yapman\u0131z gerekiyor. \n\n Takip etmek i\u00e7in [belgeleri]( {more_info_url} ) a\u00e7\u0131n, \u00e7\u00fcnk\u00fc sonraki ad\u0131mlar Nest cihazlar\u0131n\u0131za eri\u015fimi geri y\u00fcklemek i\u00e7in atman\u0131z gereken ad\u0131mlar konusunda size rehberlik edecektir.", "title": "Nest: Uygulama Yetkilendirmesinin Kullan\u0131mdan Kald\u0131r\u0131lmas\u0131" diff --git a/homeassistant/components/nest/translations/uk.json b/homeassistant/components/nest/translations/uk.json index cfdb2c91ee2..7f09a23e493 100644 --- a/homeassistant/components/nest/translations/uk.json +++ b/homeassistant/components/nest/translations/uk.json @@ -6,7 +6,6 @@ "missing_configuration": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0438 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f. \u0411\u0443\u0434\u044c \u043b\u0430\u0441\u043a\u0430, \u043e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u044f\u043c\u0438.", "no_url_available": "URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430. \u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0456\u0454\u044e] ({docs_url}) \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u0457 \u043f\u0440\u043e \u0446\u044e \u043f\u043e\u043c\u0438\u043b\u043a\u0443.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f \u043f\u0440\u043e\u0439\u0448\u043b\u0430 \u0443\u0441\u043f\u0456\u0448\u043d\u043e", - "single_instance_allowed": "\u0412\u0436\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u043e\u0432\u0430\u043d\u043e. \u041c\u043e\u0436\u043b\u0438\u0432\u0430 \u043b\u0438\u0448\u0435 \u043e\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044f.", "unknown_authorize_url_generation": "\u041d\u0435\u0432\u0456\u0434\u043e\u043c\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430 \u043f\u0440\u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0456\u0457 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0456\u0457." }, "create_entry": { diff --git a/homeassistant/components/nest/translations/zh-Hans.json b/homeassistant/components/nest/translations/zh-Hans.json index c033f8be626..3d481dec9d5 100644 --- a/homeassistant/components/nest/translations/zh-Hans.json +++ b/homeassistant/components/nest/translations/zh-Hans.json @@ -10,10 +10,6 @@ "unknown": "\u9a8c\u8bc1\u7801\u672a\u77e5\u9519\u8bef" }, "step": { - "auth": { - "description": "\u8981\u5173\u8054\u60a8\u7684 Google \u5e10\u6237\uff0c\u8bf7[\u524d\u5f80\u6388\u6743]({url})\u3002 \n\n\u6388\u6743\u6210\u529f\u540e\uff0c\u8bf7\u590d\u5236\u4e0b\u65b9\u7684 Auth Token \u4ee3\u7801\u3002", - "title": "\u5173\u8054 Google \u5e10\u6237" - }, "init": { "data": { "flow_impl": "\u8ba4\u8bc1\u63d0\u4f9b\u8005" diff --git a/homeassistant/components/nest/translations/zh-Hant.json b/homeassistant/components/nest/translations/zh-Hant.json index a0ff9cab7f8..7780b42cb84 100644 --- a/homeassistant/components/nest/translations/zh-Hant.json +++ b/homeassistant/components/nest/translations/zh-Hant.json @@ -10,7 +10,6 @@ "missing_configuration": "\u5143\u4ef6\u5c1a\u672a\u8a2d\u7f6e\uff0c\u8acb\u53c3\u95b1\u6587\u4ef6\u8aaa\u660e\u3002", "no_url_available": "\u6c92\u6709\u53ef\u7528\u7684\u7db2\u5740\u3002\u95dc\u65bc\u6b64\u932f\u8aa4\u66f4\u8a73\u7d30\u8a0a\u606f\uff0c[\u9ede\u9078\u5354\u52a9\u7ae0\u7bc0]({docs_url})", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f", - "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002", "unknown_authorize_url_generation": "\u7522\u751f\u8a8d\u8b49 URL \u6642\u767c\u751f\u672a\u77e5\u932f\u8aa4\u3002" }, "create_entry": { @@ -26,13 +25,6 @@ "wrong_project_id": "\u8acb\u8f38\u5165\u6709\u6548 Cloud \u5c08\u6848 ID\uff08\u8207\u88dd\u7f6e\u5b58\u53d6\u5c08\u6848 ID \u76f8\u540c\uff09" }, "step": { - "auth": { - "data": { - "code": "\u5b58\u53d6\u6b0a\u6756" - }, - "description": "\u6b32\u9023\u7d50 Google \u5e33\u865f\u3001\u8acb\u5148 [\u8a8d\u8b49\u5e33\u865f]({url})\u3002\n\n\u65bc\u8a8d\u8b49\u5f8c\u3001\u65bc\u4e0b\u65b9\u8cbc\u4e0a\u8a8d\u8b49\u6b0a\u6756\u4ee3\u78bc\u3002", - "title": "\u9023\u7d50 Google \u5e33\u865f" - }, "auth_upgrade": { "description": "Google \u5df2\u4e0d\u518d\u63a8\u85a6\u4f7f\u7528 App Auth \u4ee5\u63d0\u9ad8\u5b89\u5168\u6027\u3001\u56e0\u6b64\u60a8\u9700\u8981\u5efa\u7acb\u65b0\u7684\u61c9\u7528\u7a0b\u5f0f\u6191\u8b49\u3002\n\n\u958b\u555f [\u76f8\u95dc\u6587\u4ef6]({more_info_url}) \u4e26\u8ddf\u96a8\u6b65\u9a5f\u6307\u5f15\u3001\u5c07\u5e36\u9818\u60a8\u5b58\u53d6\u6216\u56de\u5fa9\u60a8\u7684 Nest \u88dd\u7f6e\u3002", "title": "Nest: App Auth \u5df2\u4e0d\u63a8\u85a6\u4f7f\u7528" diff --git a/homeassistant/components/nibe_heatpump/translations/bg.json b/homeassistant/components/nibe_heatpump/translations/bg.json index 838fde81831..92456e60027 100644 --- a/homeassistant/components/nibe_heatpump/translations/bg.json +++ b/homeassistant/components/nibe_heatpump/translations/bg.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" - }, "error": { "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, diff --git a/homeassistant/components/nibe_heatpump/translations/ca.json b/homeassistant/components/nibe_heatpump/translations/ca.json index a8a91740a7d..f489d017e86 100644 --- a/homeassistant/components/nibe_heatpump/translations/ca.json +++ b/homeassistant/components/nibe_heatpump/translations/ca.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "El dispositiu ja est\u00e0 configurat" - }, "error": { "address": "Adre\u00e7a remota inv\u00e0lida. L'adre\u00e7a ha de ser una adre\u00e7a IP o un nom d'amfitri\u00f3 resoluble.", "address_in_use": "El port d'escolta seleccionat ja est\u00e0 en \u00fas en aquest sistema.", @@ -41,18 +38,6 @@ "description": "Abans d'intentar configurar la integraci\u00f3, comprova que:\n - La unitat NibeGW est\u00e0 connectada a una bomba de calor.\n - S'ha activat l'accessori MODBUS40 a la configuraci\u00f3 de la bomba de calor.\n - La bomba no ha entrat en estat d'alarma per falta de l'accessori MODBUS40." }, "user": { - "data": { - "ip_address": "Adre\u00e7a remota", - "listening_port": "Port local d'escolta", - "remote_read_port": "Port remot de lectura", - "remote_write_port": "Port remot d'escriptura" - }, - "data_description": { - "ip_address": "Adre\u00e7a de la unitat NibeGW. El dispositiu hauria d'estar configurat amb una adre\u00e7a est\u00e0tica.", - "listening_port": "Port local d'aquest sistema al qual la unitat NibeGW est\u00e0 configurada per enviar-hi dades.", - "remote_read_port": "Port on la unitat NibeGW espera les sol\u00b7licituds de lectura.", - "remote_write_port": "Port on la unitat NibeGW espera les sol\u00b7licituds d'escriptura." - }, "description": "Tria el m\u00e8tode de connexi\u00f3 a la teva bomba. En general, les bombes de la s\u00e8rie F necessiten un accessori personalitzat NibeGW, mentre que les bombes de la s\u00e8rie S tenen Modbus integrat.", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/nibe_heatpump/translations/de.json b/homeassistant/components/nibe_heatpump/translations/de.json index 4e1fc0ce17b..0e885e730f0 100644 --- a/homeassistant/components/nibe_heatpump/translations/de.json +++ b/homeassistant/components/nibe_heatpump/translations/de.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Ger\u00e4t ist bereits konfiguriert" - }, "error": { "address": "Ung\u00fcltige Remote-Adresse angegeben. Die Adresse muss eine IP-Adresse oder ein aufl\u00f6sbarer Hostname sein.", "address_in_use": "Der ausgew\u00e4hlte Listening-Port wird auf diesem System bereits verwendet.", @@ -41,18 +38,6 @@ "description": "Bevor du versuchst, die Integration zu konfigurieren, \u00fcberpr\u00fcfe folgendes:\n - Das NibeGW-Ger\u00e4t ist an eine W\u00e4rmepumpe angeschlossen.\n - Das MODBUS40-Zubeh\u00f6r wurde in der Konfiguration der W\u00e4rmepumpe aktiviert.\n - Die Pumpe ist nicht in einen Alarmzustand wegen fehlendem MODBUS40-Zubeh\u00f6r \u00fcbergegangen." }, "user": { - "data": { - "ip_address": "Remote-Adresse", - "listening_port": "Lokaler Leseport", - "remote_read_port": "Remote-Leseport", - "remote_write_port": "Remote-Schreibport" - }, - "data_description": { - "ip_address": "Die Adresse des NibeGW-Ger\u00e4ts. Das Ger\u00e4t sollte mit einer statischen Adresse konfiguriert worden sein.", - "listening_port": "Der lokale Port auf diesem System, an den das NibeGW-Ger\u00e4t Daten senden soll.", - "remote_read_port": "Der Port, an dem das NibeGW-Ger\u00e4t auf Leseanfragen wartet.", - "remote_write_port": "Der Port, an dem das NibeGW-Ger\u00e4t auf Schreibanfragen wartet." - }, "description": "W\u00e4hle die Verbindungsmethode zu deiner Pumpe. Im Allgemeinen erfordern Pumpen der F-Serie ein kundenspezifisches NibeGW-Zubeh\u00f6r, w\u00e4hrend eine Pumpe der S-Serie \u00fcber eine integrierte Modbus-Unterst\u00fctzung verf\u00fcgt.", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/nibe_heatpump/translations/el.json b/homeassistant/components/nibe_heatpump/translations/el.json index aa8a2008cbc..1933e062a09 100644 --- a/homeassistant/components/nibe_heatpump/translations/el.json +++ b/homeassistant/components/nibe_heatpump/translations/el.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" - }, "error": { "address": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03c4\u03b7\u03ba\u03b5 \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b1\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP. \u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IPV4.", "address_in_use": "\u0397 \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b8\u03cd\u03c1\u03b1 \u03b1\u03ba\u03c1\u03cc\u03b1\u03c3\u03b7\u03c2 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1.", @@ -41,18 +38,6 @@ "description": "\u03a0\u03c1\u03b9\u03bd \u03b5\u03c0\u03b9\u03c7\u03b5\u03b9\u03c1\u03ae\u03c3\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7, \u03b2\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9:\n - \u0397 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 NibeGW \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03b7 \u03bc\u03b5 \u03b1\u03bd\u03c4\u03bb\u03af\u03b1 \u03b8\u03b5\u03c1\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2.\n - \u03a4\u03bf \u03b5\u03be\u03ac\u03c1\u03c4\u03b7\u03bc\u03b1 MODBUS40 \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b1\u03bd\u03c4\u03bb\u03af\u03b1\u03c2 \u03b8\u03b5\u03c1\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2.\n - \u0397 \u03b1\u03bd\u03c4\u03bb\u03af\u03b1 \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c4\u03b5\u03b8\u03b5\u03af \u03c3\u03b5 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c3\u03c5\u03bd\u03b1\u03b3\u03b5\u03c1\u03bc\u03bf\u03cd \u03b3\u03b9\u03b1 \u03ad\u03bb\u03bb\u03b5\u03b9\u03c8\u03b7 \u03b5\u03be\u03b1\u03c1\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2 MODBUS40." }, "user": { - "data": { - "ip_address": "\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP", - "listening_port": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03b1\u03ba\u03c1\u03cc\u03b1\u03c3\u03b7\u03c2", - "remote_read_port": "\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b8\u03cd\u03c1\u03b1 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2", - "remote_write_port": "\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b8\u03cd\u03c1\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2" - }, - "data_description": { - "ip_address": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1\u03c2 NibeGW. \u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b8\u03b1 \u03ad\u03c0\u03c1\u03b5\u03c0\u03b5 \u03bd\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af \u03bc\u03b5 \u03c3\u03c4\u03b1\u03c4\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7.", - "listening_port": "\u0397 \u03c4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2, \u03c3\u03c4\u03b7\u03bd \u03bf\u03c0\u03bf\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af \u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 NibeGW \u03b3\u03b9\u03b1 \u03b1\u03c0\u03bf\u03c3\u03c4\u03bf\u03bb\u03ae \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd.", - "remote_read_port": "\u0397 \u03b8\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1\u03c2 NibeGW \u03b1\u03ba\u03bf\u03cd\u03b5\u03b9 \u03b3\u03b9\u03b1 \u03b1\u03b9\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2.", - "remote_write_port": "\u0397 \u03b8\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1\u03c2 NibeGW \u03b1\u03ba\u03bf\u03cd\u03b5\u03b9 \u03b3\u03b9\u03b1 \u03b1\u03b9\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2." - }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03bc\u03ad\u03b8\u03bf\u03b4\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03bc\u03b5 \u03c4\u03b7\u03bd \u03b1\u03bd\u03c4\u03bb\u03af\u03b1 \u03c3\u03b1\u03c2. \u0393\u03b5\u03bd\u03b9\u03ba\u03ac, \u03bf\u03b9 \u03b1\u03bd\u03c4\u03bb\u03af\u03b5\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03b5\u03b9\u03c1\u03ac\u03c2 F \u03b1\u03c0\u03b1\u03b9\u03c4\u03bf\u03cd\u03bd \u03c0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03c3\u03bc\u03ad\u03bd\u03bf \u03b5\u03be\u03ac\u03c1\u03c4\u03b7\u03bc\u03b1 NibeGW, \u03b5\u03bd\u03ce \u03bf\u03b9 \u03b1\u03bd\u03c4\u03bb\u03af\u03b5\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03b5\u03b9\u03c1\u03ac\u03c2 S \u03ad\u03c7\u03bf\u03c5\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03b1\u03c4\u03c9\u03bc\u03ad\u03bd\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 Modbus.", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/nibe_heatpump/translations/en.json b/homeassistant/components/nibe_heatpump/translations/en.json index 3b50bb0986a..5d0aaf4dd2d 100644 --- a/homeassistant/components/nibe_heatpump/translations/en.json +++ b/homeassistant/components/nibe_heatpump/translations/en.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Device is already configured" - }, "error": { "address": "Invalid remote address specified. Address must be an IP address or a resolvable hostname.", "address_in_use": "The selected listening port is already in use on this system.", @@ -41,18 +38,6 @@ "description": "Before attempting to configure the integration, verify that:\n - The NibeGW unit is connected to a heat pump.\n - The MODBUS40 accessory has been enabled in the heat pump configuration.\n - The pump has not gone into an alarm state about missing MODBUS40 accessory." }, "user": { - "data": { - "ip_address": "Remote address", - "listening_port": "Local listening port", - "remote_read_port": "Remote read port", - "remote_write_port": "Remote write port" - }, - "data_description": { - "ip_address": "The address of the NibeGW unit. The device should have been configured with a static address.", - "listening_port": "The local port on this system, that the NibeGW unit is configured to send data to.", - "remote_read_port": "The port the NibeGW unit is listening for read requests on.", - "remote_write_port": "The port the NibeGW unit is listening for write requests on." - }, "description": "Pick the connection method to your pump. In general, F-series pumps require a NibeGW custom accessory, while an S-series pump has Modbus support built-in.", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/nibe_heatpump/translations/es.json b/homeassistant/components/nibe_heatpump/translations/es.json index c241724f14a..fb5d35d209b 100644 --- a/homeassistant/components/nibe_heatpump/translations/es.json +++ b/homeassistant/components/nibe_heatpump/translations/es.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "El dispositivo ya est\u00e1 configurado" - }, "error": { "address": "Se especific\u00f3 una direcci\u00f3n remota no v\u00e1lida. La direcci\u00f3n debe ser una direcci\u00f3n IP o un nombre de host resoluble.", "address_in_use": "El puerto de escucha seleccionado ya est\u00e1 en uso en este sistema.", @@ -41,18 +38,6 @@ "description": "Antes de intentar configurar la integraci\u00f3n, verifica que:\n - La unidad NibeGW est\u00e1 conectada a una bomba de calor.\n - Se ha habilitado el accesorio MODBUS40 en la configuraci\u00f3n de la bomba de calor.\n - La bomba no ha entrado en estado de alarma por falta del accesorio MODBUS40." }, "user": { - "data": { - "ip_address": "Direcci\u00f3n remota", - "listening_port": "Puerto de escucha local", - "remote_read_port": "Puerto de lectura remoto", - "remote_write_port": "Puerto de escritura remoto" - }, - "data_description": { - "ip_address": "La direcci\u00f3n de la unidad NibeGW. El dispositivo deber\u00eda haber sido configurado con una direcci\u00f3n est\u00e1tica.", - "listening_port": "El puerto local en este sistema, al que la unidad NibeGW est\u00e1 configurada para enviar datos.", - "remote_read_port": "El puerto en el que la unidad NibeGW est\u00e1 escuchando las peticiones de lectura.", - "remote_write_port": "El puerto en el que la unidad NibeGW est\u00e1 escuchando peticiones de escritura." - }, "description": "Elige el m\u00e9todo de conexi\u00f3n a tu bomba. En general, las bombas de la serie F requieren un accesorio personalizado NibeGW, mientras que una bomba de la serie S tiene soporte Modbus incorporado.", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/nibe_heatpump/translations/et.json b/homeassistant/components/nibe_heatpump/translations/et.json index 56ffec08aec..1876e93305a 100644 --- a/homeassistant/components/nibe_heatpump/translations/et.json +++ b/homeassistant/components/nibe_heatpump/translations/et.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Seade on juba h\u00e4\u00e4lestatud" - }, "error": { "address": "M\u00e4\u00e4ratud vale kaugaadress. Aadress peab olema IP-aadress v\u00f5i lahendatav hostinimi.", "address_in_use": "Valitud kuulamisport on selles s\u00fcsteemis juba kasutusel.", @@ -41,18 +38,6 @@ "description": "Enne seadistamist veendu, et:\n - NibeGW seade on \u00fchendatud soojuspumbaga.\n - MODBUS40 lisaseade on soojuspumba konfiguratsioonis lubatud.\n - Pump ei ole MODBUS40 lisaseadme puudumise t\u00f5ttu h\u00e4ireolekusse l\u00e4inud." }, "user": { - "data": { - "ip_address": "Kaug-IP-aadress", - "listening_port": "Kohalik kuulamisport", - "remote_read_port": "Kauglugemise port", - "remote_write_port": "Kaugkirjutusport" - }, - "data_description": { - "ip_address": "NibeGW-\u00fcksuse aadress. Seade peaks olema seadistatud staatilise aadressiga.", - "listening_port": "Selle s\u00fcsteemi kohalik port kuhu NibeGW seade on seadistatud andmeid saatma.", - "remote_read_port": "Port, mille kaudu NibeGW-\u00fcksus loeb lugemisp\u00e4ringuid.", - "remote_write_port": "Port, mille kaudu NibeGW-\u00fcksus kuulab kirjutamisp\u00e4ringuid." - }, "description": "Vali pumbaga \u00fchendamise viis. \u00dcldiselt vajavad F-seeria pumbad Nibe GW kohandatud tarvikut, S-seeria pumbal on aga sisseehitatud Modbusi tugi.", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/nibe_heatpump/translations/fr.json b/homeassistant/components/nibe_heatpump/translations/fr.json index 9a0b8f0954a..799ee8c2b73 100644 --- a/homeassistant/components/nibe_heatpump/translations/fr.json +++ b/homeassistant/components/nibe_heatpump/translations/fr.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" - }, "error": { "model": "Le mod\u00e8le s\u00e9lectionn\u00e9 ne semble pas prendre en charge MODBUS40", "unknown": "Erreur inattendue" @@ -25,12 +22,6 @@ } }, "user": { - "data": { - "ip_address": "Adresse distante", - "listening_port": "Port d'\u00e9coute local", - "remote_read_port": "Port de lecture distant", - "remote_write_port": "Port d'\u00e9criture distant" - }, "menu_options": { "modbus": "Modbus", "nibegw": "NibeGW" diff --git a/homeassistant/components/nibe_heatpump/translations/he.json b/homeassistant/components/nibe_heatpump/translations/he.json index ea40181bd9a..822dcf2be14 100644 --- a/homeassistant/components/nibe_heatpump/translations/he.json +++ b/homeassistant/components/nibe_heatpump/translations/he.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" - }, "error": { "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" } diff --git a/homeassistant/components/nibe_heatpump/translations/hu.json b/homeassistant/components/nibe_heatpump/translations/hu.json index 35d9ac53291..3f6c9845656 100644 --- a/homeassistant/components/nibe_heatpump/translations/hu.json +++ b/homeassistant/components/nibe_heatpump/translations/hu.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" - }, "error": { "address": "\u00c9rv\u00e9nytelen t\u00e1voli c\u00edm van megadva. A c\u00edmnek IP-c\u00edmnek vagy feloldhat\u00f3 g\u00e9pn\u00e9vnek kell lennie.", "address_in_use": "A kiv\u00e1lasztott port m\u00e1r haszn\u00e1latban van ezen a rendszeren.", @@ -41,18 +38,6 @@ "description": "Miel\u0151tt megpr\u00f3b\u00e1ln\u00e1 konfigur\u00e1lni az integr\u00e1ci\u00f3t, ellen\u0151rizze, hogy:\n - A NibeGW egys\u00e9g h\u0151szivatty\u00fahoz van csatlakoztatva.\n - A MODBUS40 kieg\u00e9sz\u00edt\u0151 enged\u00e9lyezve van a h\u0151szivatty\u00fa konfigur\u00e1ci\u00f3j\u00e1ban.\n - A szivatty\u00fa nem l\u00e9pett riaszt\u00e1si \u00e1llapotba a MODBUS40 tartoz\u00e9k hi\u00e1nya miatt." }, "user": { - "data": { - "ip_address": "T\u00e1voli IP-c\u00edm", - "listening_port": "Helyi port", - "remote_read_port": "T\u00e1voli olvas\u00e1si port", - "remote_write_port": "T\u00e1voli \u00edr\u00e1si port" - }, - "data_description": { - "ip_address": "A NibeGW egys\u00e9g c\u00edme. A k\u00e9sz\u00fcl\u00e9ket statikus c\u00edmmel kell konfigur\u00e1lni.", - "listening_port": "A rendszer azon helyi portja, amelyre a NibeGW egys\u00e9g az adatok k\u00fcld\u00e9s\u00e9re van konfigur\u00e1lva.", - "remote_read_port": "A port, amelyen a NibeGW egys\u00e9g olvas\u00e1si k\u00e9r\u00e9seket fogad.", - "remote_write_port": "A port, amelyen a NibeGW egys\u00e9g \u00edr\u00e1si k\u00e9r\u00e9seket fogad." - }, "description": "V\u00e1lassza ki a szivatty\u00fahoz val\u00f3 csatlakoz\u00e1si m\u00f3dot. Az F-sorozat\u00fa szivatty\u00fakhoz \u00e1ltal\u00e1ban Nibe GW egyedi tartoz\u00e9kra van sz\u00fcks\u00e9g, m\u00edg az S-sorozat\u00fa szivatty\u00fak be\u00e9p\u00edtett Modbus-t\u00e1mogat\u00e1ssal rendelkeznek.", "menu_options": { "modbus": "ModBUS", diff --git a/homeassistant/components/nibe_heatpump/translations/id.json b/homeassistant/components/nibe_heatpump/translations/id.json index 7a5657508fc..4e9e7c181d0 100644 --- a/homeassistant/components/nibe_heatpump/translations/id.json +++ b/homeassistant/components/nibe_heatpump/translations/id.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Perangkat sudah dikonfigurasi" - }, "error": { "address": "Alamat IP jarak jauh yang ditentukan tidak valid. Alamat harus berupa alamat IP atau nama host yang dapat ditemukan.", "address_in_use": "Port mendengarkan yang dipilih sudah digunakan pada sistem ini.", @@ -41,18 +38,6 @@ "description": "Sebelum mencoba mengonfigurasi integrasi, pastikan bahwa:\n - Unit NibeGW terhubung ke pompa panas.\n - Aksesori MODBUS40 telah diaktifkan dalam konfigurasi pompa panas.\n - Pompa tidak sedang dalam status alarm tentang aksesori MODBUS40 yang tidak tersedia." }, "user": { - "data": { - "ip_address": "Alamat jarak jauh", - "listening_port": "Port mendengarkan lokal", - "remote_read_port": "Port baca jarak jauh", - "remote_write_port": "Port tulis jarak jauh" - }, - "data_description": { - "ip_address": "Alamat unit NibeGW. Perangkat harus dikonfigurasi dengan alamat statis.", - "listening_port": "Port lokal pada sistem ini, tempat unit NibeGW dikonfigurasi untuk mengirim data.", - "remote_read_port": "Port yang digunakan unit NibeGW untuk mendengarkan permintaan baca.", - "remote_write_port": "Port yang digunakan unit NibeGW untuk mendengarkan permintaan tulis." - }, "description": "Pilih metode koneksi ke pompa. Secara umum, pompa seri F memerlukan aksesori khusus NibeGW, sementara pompa seri S memiliki dukungan Modbus bawaan.", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/nibe_heatpump/translations/it.json b/homeassistant/components/nibe_heatpump/translations/it.json index 96b686f9818..dd629388f0d 100644 --- a/homeassistant/components/nibe_heatpump/translations/it.json +++ b/homeassistant/components/nibe_heatpump/translations/it.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" - }, "error": { "address": "Indirizzo remoto specificato non valido. L'indirizzo deve essere un indirizzo IP o un nome host risolvibile.", "address_in_use": "La porta di ascolto selezionata \u00e8 gi\u00e0 in uso su questo sistema.", @@ -41,18 +38,6 @@ "description": "Prima di tentare di configurare l'integrazione, verificare che:\n - L'unit\u00e0 NibeGW \u00e8 collegata a una pompa di calore.\n - Nella configurazione della pompa di calore \u00e8 stato abilitato l'accessorio MODBUS40.\n - La pompa non \u00e8 andata in stato di allarme per la mancanza dell'accessorio MODBUS40." }, "user": { - "data": { - "ip_address": "Indirizzo remoto", - "listening_port": "Porta di ascolto locale", - "remote_read_port": "Porta di lettura remota", - "remote_write_port": "Porta di scrittura remota" - }, - "data_description": { - "ip_address": "L'indirizzo dell'unit\u00e0 NibeGW. Il dispositivo dovrebbe essere stato configurato con un indirizzo statico.", - "listening_port": "La porta locale su questo sistema a cui l'unit\u00e0 NibeGW \u00e8 configurata per inviare i dati.", - "remote_read_port": "La porta su cui l'unit\u00e0 NibeGW \u00e8 in ascolto per le richieste di lettura.", - "remote_write_port": "La porta su cui l'unit\u00e0 NibeGW \u00e8 in ascolto per le richieste di scrittura." - }, "description": "Scegli il metodo di connessione alla tua pompa. In generale, le pompe della serie F richiedono un accessorio personalizzato NibeGW, mentre una pompa della serie S ha il supporto Modbus integrato.", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/nibe_heatpump/translations/ja.json b/homeassistant/components/nibe_heatpump/translations/ja.json index 6ca4ad37a81..9ad7fd4a7aa 100644 --- a/homeassistant/components/nibe_heatpump/translations/ja.json +++ b/homeassistant/components/nibe_heatpump/translations/ja.json @@ -1,20 +1,7 @@ { "config": { - "abort": { - "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" - }, "error": { "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" - }, - "step": { - "user": { - "data": { - "ip_address": "\u30ea\u30e2\u30fc\u30c8IP\u30a2\u30c9\u30ec\u30b9", - "listening_port": "\u30ed\u30fc\u30ab\u30eb\u30ea\u30b9\u30cb\u30f3\u30b0\u30dd\u30fc\u30c8", - "remote_read_port": "\u30ea\u30e2\u30fc\u30c8\u8aad\u307f\u53d6\u308a\u30dd\u30fc\u30c8", - "remote_write_port": "\u30ea\u30e2\u30fc\u30c8\u66f8\u304d\u8fbc\u307f\u30dd\u30fc\u30c8" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/nibe_heatpump/translations/nb.json b/homeassistant/components/nibe_heatpump/translations/nb.json index 2e302a80d31..a22f7eef3d6 100644 --- a/homeassistant/components/nibe_heatpump/translations/nb.json +++ b/homeassistant/components/nibe_heatpump/translations/nb.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Enheten er allerede konfigurert" - }, "error": { "unknown": "Uventet feil" } diff --git a/homeassistant/components/nibe_heatpump/translations/nl.json b/homeassistant/components/nibe_heatpump/translations/nl.json index c227699ff21..7e198e836d7 100644 --- a/homeassistant/components/nibe_heatpump/translations/nl.json +++ b/homeassistant/components/nibe_heatpump/translations/nl.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Apparaat is al geconfigureerd" - }, "error": { "unknown": "Onverwachte fout" } diff --git a/homeassistant/components/nibe_heatpump/translations/no.json b/homeassistant/components/nibe_heatpump/translations/no.json index d7da2189116..bc0963c6a64 100644 --- a/homeassistant/components/nibe_heatpump/translations/no.json +++ b/homeassistant/components/nibe_heatpump/translations/no.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Enheten er allerede konfigurert" - }, "error": { "address": "Ugyldig ekstern adresse er angitt. Adressen m\u00e5 v\u00e6re en IP-adresse eller et vertsnavn som kan l\u00f8ses.", "address_in_use": "Den valgte lytteporten er allerede i bruk p\u00e5 dette systemet.", @@ -41,18 +38,6 @@ "description": "F\u00f8r du pr\u00f8ver \u00e5 konfigurere integrasjonen, kontroller at:\n - NibeGW-enheten er koblet til en varmepumpe.\n - MODBUS40-tilbeh\u00f8ret er aktivert i varmepumpekonfigurasjonen.\n - Pumpen har ikke g\u00e5tt i alarmtilstand om manglende MODBUS40-tilbeh\u00f8r." }, "user": { - "data": { - "ip_address": "Ekstern adresse", - "listening_port": "Lokal lytteport", - "remote_read_port": "Ekstern leseport", - "remote_write_port": "Ekstern skriveport" - }, - "data_description": { - "ip_address": "Adressen til NibeGW-enheten. Enheten skal ha blitt konfigurert med en statisk adresse.", - "listening_port": "Den lokale porten p\u00e5 dette systemet, som NibeGW-enheten er konfigurert til \u00e5 sende data til.", - "remote_read_port": "Porten NibeGW-enheten lytter etter leseforesp\u00f8rsler p\u00e5.", - "remote_write_port": "Porten NibeGW-enheten lytter etter skriveforesp\u00f8rsler p\u00e5." - }, "description": "Velg tilkoblingsmetoden til pumpen din. Generelt krever pumper i F-serien et NibeGW-tilpasset tilbeh\u00f8r, mens en pumpe i S-serien har Modbus-st\u00f8tte innebygd.", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/nibe_heatpump/translations/pl.json b/homeassistant/components/nibe_heatpump/translations/pl.json index e97517d2a22..0e66092c5ff 100644 --- a/homeassistant/components/nibe_heatpump/translations/pl.json +++ b/homeassistant/components/nibe_heatpump/translations/pl.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" - }, "error": { "address": "Podano nieprawid\u0142owy zdalny adres IP. Adres musi by\u0107 adresem IP lub rozpoznawaln\u0105 nazw\u0105 hosta.", "address_in_use": "Wybrany port nas\u0142uchiwania jest ju\u017c u\u017cywany w tym systemie.", @@ -41,18 +38,6 @@ "description": "Przed przyst\u0105pieniem do konfiguracji integracji sprawd\u017a, czy:\n - Urz\u0105dzenie NibeGW jest pod\u0142\u0105czona do pompy ciep\u0142a.\n - Akcesorium MODBUS40 zosta\u0142o w\u0142\u0105czone w konfiguracji pompy ciep\u0142a.\n - Pompa nie wesz\u0142a w stan alarmowy z powodu braku akcesorium MODBUS40." }, "user": { - "data": { - "ip_address": "Zdalny adres IP", - "listening_port": "Lokalny port nas\u0142uchiwania", - "remote_read_port": "Zdalny port odczytu", - "remote_write_port": "Zdalny port zapisu" - }, - "data_description": { - "ip_address": "Adres urz\u0105dzenia NibeGW. Urz\u0105dzenie powinno by\u0107 skonfigurowane z adresem statycznym.", - "listening_port": "Port lokalny w tym systemie, do kt\u00f3rego urz\u0105dzenie NibeGW jest skonfigurowane do wysy\u0142ania danych.", - "remote_read_port": "Port, na kt\u00f3rym urz\u0105dzenie NibeGW nas\u0142uchuje \u017c\u0105da\u0144 odczytu.", - "remote_write_port": "Port, na kt\u00f3rym urz\u0105dzenie NibeGW nas\u0142uchuje \u017c\u0105da\u0144 zapisu." - }, "description": "Wybierz metod\u0119 po\u0142\u0105czenia z pomp\u0105. Og\u00f3lnie rzecz bior\u0105c, pompy serii F wymagaj\u0105 niestandardowego akcesorium NibeGW, podczas gdy pompy serii S maj\u0105 wbudowan\u0105 obs\u0142ug\u0119 protoko\u0142u Modbus.", "menu_options": { "modbus": "MODBUS", diff --git a/homeassistant/components/nibe_heatpump/translations/pt-BR.json b/homeassistant/components/nibe_heatpump/translations/pt-BR.json index 6059385b861..c3e97bd3e3d 100644 --- a/homeassistant/components/nibe_heatpump/translations/pt-BR.json +++ b/homeassistant/components/nibe_heatpump/translations/pt-BR.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" - }, "error": { "address": "Endere\u00e7o remoto inv\u00e1lido especificado. O endere\u00e7o deve ser um endere\u00e7o IP ou um nome de host resolv\u00edvel.", "address_in_use": "A porta de escuta selecionada j\u00e1 est\u00e1 em uso neste sistema.", @@ -41,18 +38,6 @@ "description": "Antes de tentar configurar a integra\u00e7\u00e3o, verifique se:\n - A unidade NibeGW est\u00e1 conectada a uma bomba de calor.\n - O acess\u00f3rio MODBUS40 foi habilitado na configura\u00e7\u00e3o da bomba de calor.\n - A bomba n\u00e3o entrou em estado de alarme por falta de acess\u00f3rio MODBUS40." }, "user": { - "data": { - "ip_address": "Endere\u00e7o IP remoto", - "listening_port": "Porta de escuta local", - "remote_read_port": "Porta de leitura remota", - "remote_write_port": "Porta de grava\u00e7\u00e3o remota" - }, - "data_description": { - "ip_address": "O endere\u00e7o da unidade NibeGW. O dispositivo deve ter sido configurado com um endere\u00e7o est\u00e1tico.", - "listening_port": "A porta local neste sistema para a qual a unidade NibeGW est\u00e1 configurada para enviar dados.", - "remote_read_port": "A porta na qual a unidade NibeGW est\u00e1 escutando solicita\u00e7\u00f5es de leitura.", - "remote_write_port": "A porta na qual a unidade NibeGW est\u00e1 escutando solicita\u00e7\u00f5es de grava\u00e7\u00e3o." - }, "description": "Escolha o m\u00e9todo de conex\u00e3o para sua bomba. Em geral, as bombas da s\u00e9rie F requerem um acess\u00f3rio personalizado NibeGW, enquanto uma bomba da s\u00e9rie S tem suporte Modbus integrado.", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/nibe_heatpump/translations/ru.json b/homeassistant/components/nibe_heatpump/translations/ru.json index de67991f5af..d4b8a2608ee 100644 --- a/homeassistant/components/nibe_heatpump/translations/ru.json +++ b/homeassistant/components/nibe_heatpump/translations/ru.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." - }, "error": { "address": "\u0423\u043a\u0430\u0437\u0430\u043d \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441. \u0421\u043b\u0435\u0434\u0443\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430.", "address_in_use": "\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u044d\u0442\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435.", @@ -41,18 +38,6 @@ "description": "\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e:\n - \u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e \u043a \u0442\u0435\u043f\u043b\u043e\u0432\u043e\u043c\u0443 \u043d\u0430\u0441\u043e\u0441\u0443.\n - \u0410\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440 MODBUS40 \u0431\u044b\u043b \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0442\u0435\u043f\u043b\u043e\u0432\u043e\u0433\u043e \u043d\u0430\u0441\u043e\u0441\u0430.\n - \u041d\u0430\u0441\u043e\u0441 \u043d\u0435 \u043f\u0435\u0440\u0435\u0448\u0435\u043b \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0442\u0440\u0435\u0432\u043e\u0433\u0438 \u0438\u0437 \u0437\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044f \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u0430 MODBUS40." }, "user": { - "data": { - "ip_address": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441", - "listening_port": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u043d\u0438\u044f", - "remote_read_port": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0447\u0442\u0435\u043d\u0438\u044f", - "remote_write_port": "\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0437\u0430\u043f\u0438\u0441\u0438" - }, - "data_description": { - "ip_address": "\u0410\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 NibeGW. \u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u0441\u043e \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0430\u0434\u0440\u0435\u0441\u043e\u043c.", - "listening_port": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.", - "remote_read_port": "\u041f\u043e\u0440\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435.", - "remote_write_port": "\u041f\u043e\u0440\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e NibeGW \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c." - }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0412\u0430\u0448\u0435\u043c\u0443 \u043d\u0430\u0441\u043e\u0441\u0443. \u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0434\u043b\u044f \u043d\u0430\u0441\u043e\u0441\u043e\u0432 \u0441\u0435\u0440\u0438\u0438 F \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440 NibeGW, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u043d\u0430\u0441\u043e\u0441\u044b \u0441\u0435\u0440\u0438\u0438 S \u0438\u043c\u0435\u044e\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 Modbus.", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/nibe_heatpump/translations/sk.json b/homeassistant/components/nibe_heatpump/translations/sk.json deleted file mode 100644 index 793f8eff278..00000000000 --- a/homeassistant/components/nibe_heatpump/translations/sk.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/nibe_heatpump/translations/sv.json b/homeassistant/components/nibe_heatpump/translations/sv.json index 4e0c9cdd7ca..5406e5b407f 100644 --- a/homeassistant/components/nibe_heatpump/translations/sv.json +++ b/homeassistant/components/nibe_heatpump/translations/sv.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Enheten \u00e4r redan konfigurerad" - }, "error": { "address": "Ogiltig fj\u00e4rr-IP-adress har angetts. Adressen m\u00e5ste vara en IPv4-adress.", "address_in_use": "Den valda lyssningsporten anv\u00e4nds redan p\u00e5 detta system.", @@ -10,16 +7,6 @@ "read": "Fel p\u00e5 l\u00e4sf\u00f6rfr\u00e5gan fr\u00e5n pumpen. Verifiera din \"Fj\u00e4rrl\u00e4sningsport\" eller \"Fj\u00e4rr-IP-adress\".", "unknown": "Ov\u00e4ntat fel", "write": "Fel vid skrivbeg\u00e4ran till pumpen. Verifiera din `Fj\u00e4rrskrivport` eller `Fj\u00e4rr-IP-adress`." - }, - "step": { - "user": { - "data": { - "ip_address": "Fj\u00e4rr IP-adress", - "listening_port": "Lokal lyssningsport", - "remote_read_port": "Port f\u00f6r fj\u00e4rravl\u00e4sning", - "remote_write_port": "Port f\u00f6r fj\u00e4rrskrivning" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/nibe_heatpump/translations/tr.json b/homeassistant/components/nibe_heatpump/translations/tr.json index 53722d2a50a..05b75d58f18 100644 --- a/homeassistant/components/nibe_heatpump/translations/tr.json +++ b/homeassistant/components/nibe_heatpump/translations/tr.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" - }, "error": { "address": "Ge\u00e7ersiz uzak adres belirtildi. Adres bir IP adresi veya \u00e7\u00f6z\u00fclebilir bir ana bilgisayar ad\u0131 olmal\u0131d\u0131r.", "address_in_use": "Se\u00e7ilen dinleme ba\u011flant\u0131 noktas\u0131 bu sistemde zaten kullan\u0131l\u0131yor.", @@ -13,18 +10,6 @@ }, "step": { "user": { - "data": { - "ip_address": "Uzak adres", - "listening_port": "Yerel dinleme ba\u011flant\u0131 noktas\u0131", - "remote_read_port": "Uzaktan okuma ba\u011flant\u0131 noktas\u0131", - "remote_write_port": "Uzaktan yazma ba\u011flant\u0131 noktas\u0131" - }, - "data_description": { - "ip_address": "NibeGW biriminin adresi. Cihaz statik bir adresle yap\u0131land\u0131r\u0131lm\u0131\u015f olmal\u0131d\u0131r.", - "listening_port": "NibeGW biriminin veri g\u00f6ndermek \u00fczere yap\u0131land\u0131r\u0131ld\u0131\u011f\u0131 bu sistemdeki yerel ba\u011flant\u0131 noktas\u0131.", - "remote_read_port": "NibeGW biriminin okuma isteklerini dinledi\u011fi ba\u011flant\u0131 noktas\u0131.", - "remote_write_port": "NibeGW biriminin yazma isteklerini dinledi\u011fi ba\u011flant\u0131 noktas\u0131." - }, "description": "Entegrasyonu yap\u0131land\u0131rmaya \u00e7al\u0131\u015fmadan \u00f6nce \u015funlar\u0131 do\u011frulay\u0131n:\n - NibeGW \u00fcnitesi bir \u0131s\u0131 pompas\u0131na ba\u011fl\u0131d\u0131r.\n - Is\u0131 pompas\u0131 konfig\u00fcrasyonunda MODBUS40 aksesuar\u0131 etkinle\u015ftirildi.\n - Pompa, eksik MODBUS40 aksesuar\u0131 ile ilgili alarm durumuna ge\u00e7medi." } } diff --git a/homeassistant/components/nibe_heatpump/translations/zh-Hans.json b/homeassistant/components/nibe_heatpump/translations/zh-Hans.json deleted file mode 100644 index 527e3717c4a..00000000000 --- a/homeassistant/components/nibe_heatpump/translations/zh-Hans.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "ip_address": "\u8fdc\u7a0bIP\u5730\u5740", - "listening_port": "\u672c\u5730\u76d1\u542c\u7aef\u53e3", - "remote_read_port": "\u8fdc\u7a0b\u8bfb\u53d6\u7aef\u53e3", - "remote_write_port": "\u8fdc\u7a0b\u5199\u5165\u7aef\u53e3" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/nibe_heatpump/translations/zh-Hant.json b/homeassistant/components/nibe_heatpump/translations/zh-Hant.json index ff9cb6fe24f..f56a9ad6f55 100644 --- a/homeassistant/components/nibe_heatpump/translations/zh-Hant.json +++ b/homeassistant/components/nibe_heatpump/translations/zh-Hant.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" - }, "error": { "address": "\u6307\u5b9a\u7684\u9060\u7aef\u4f4d\u5740\u7121\u6548\u3002\u4f4d\u5740\u5fc5\u9808\u70ba IP \u4f4d\u5740\u6216\u53ef\u89e3\u6790\u7684\u4e3b\u6a5f\u540d\u7a31\u3002", "address_in_use": "\u6240\u9078\u64c7\u7684\u76e3\u807d\u901a\u8a0a\u57e0\u5df2\u7d93\u88ab\u7cfb\u7d71\u6240\u4f7f\u7528\u3002", @@ -41,18 +38,6 @@ "description": "\u65bc\u5617\u8a66\u8a2d\u5b9a\u6574\u5408\u524d\u3001\u8acb\u78ba\u8a8d\uff1a\n - NibeGW \u8a2d\u5099\u5df2\u7d93\u9023\u7dda\u81f3\u71b1\u6cf5\u3002\n - \u5df2\u7d93\u65bc\u71b1\u6cf5\u8a2d\u5b9a\u4e2d\u555f\u7528 MODBUS40 \u914d\u4ef6\u3002\n - \u6cf5\u4e26\u6c92\u6709\u51fa\u73fe\u7f3a\u5c11 MODBUS40 \u914d\u4ef6\u4e4b\u8b66\u544a\u3002" }, "user": { - "data": { - "ip_address": "\u9060\u7aef\u4f4d\u5740", - "listening_port": "\u672c\u5730\u76e3\u807d\u901a\u8a0a\u57e0", - "remote_read_port": "\u9060\u7aef\u8b80\u53d6\u57e0", - "remote_write_port": "\u9060\u7aef\u5beb\u5165\u57e0" - }, - "data_description": { - "ip_address": "NibeGW \u8a2d\u5099\u4f4d\u5740\u3002\u88dd\u7f6e\u61c9\u8a72\u5df2\u7d93\u8a2d\u5b9a\u70ba\u975c\u614b\u4f4d\u5740\uff0c", - "listening_port": "\u7cfb\u7d71\u672c\u5730\u901a\u8a0a\u57e0\u3001\u4f9b NibeGW \u8a2d\u5099\u8a2d\u5b9a\u50b3\u9001\u8cc7\u6599\u3002", - "remote_read_port": "NibeGW \u8a2d\u5099\u76e3\u807d\u8b80\u53d6\u8acb\u6c42\u901a\u8a0a\u57e0\u3002", - "remote_write_port": "NibeGW \u8a2d\u5099\u76e3\u807d\u5beb\u5165\u8acb\u6c42\u901a\u8a0a\u57e0\u3002" - }, "description": "\u9078\u64c7\u6cf5\u9023\u7dda\u6a21\u5f0f\u3002\u901a\u5e38\u3001F \u7cfb\u5217\u6cf5\u9700\u8981 Nibe GW \u81ea\u8a02\u914d\u4ef6\u3001\u800c S \u7cfb\u5217\u6cf5\u70ba\u5167\u5efa Modbus\u3002", "menu_options": { "modbus": "Modbus", diff --git a/homeassistant/components/openexchangerates/translations/ca.json b/homeassistant/components/openexchangerates/translations/ca.json index 81d359599ae..f3f3a0aad04 100644 --- a/homeassistant/components/openexchangerates/translations/ca.json +++ b/homeassistant/components/openexchangerates/translations/ca.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "La configuraci\u00f3 d'Open Exchange Rates mitjan\u00e7ant YAML s'ha eliminat de Home Assistant.\n\nElimina la configuraci\u00f3 YAML d'Open Exchange Rates del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", - "title": "La configuraci\u00f3 YAML d'Open Exchange Rates s'ha eliminat" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/de.json b/homeassistant/components/openexchangerates/translations/de.json index a0f974d3374..265147d31da 100644 --- a/homeassistant/components/openexchangerates/translations/de.json +++ b/homeassistant/components/openexchangerates/translations/de.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Das Konfigurieren von Open Exchange Rates mit YAML wurde entfernt. \n\nEntferne die YAML-Konfiguration f\u00fcr Open Exchange Rates aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Open Exchange Rates YAML-Konfiguration wurde entfernt" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/el.json b/homeassistant/components/openexchangerates/translations/el.json index dee7e836d01..59c0501a38d 100644 --- a/homeassistant/components/openexchangerates/translations/el.json +++ b/homeassistant/components/openexchangerates/translations/el.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "\u0397 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03b1\u03bd\u03bf\u03b9\u03ba\u03c4\u03ce\u03bd \u03b9\u03c3\u03bf\u03c4\u03b9\u03bc\u03b9\u03ce\u03bd \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03ac\u03b3\u03bc\u03b1\u03c4\u03bf\u03c2 \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03b5\u03b9\u03c3\u03b1\u03c7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Open Exchange Rates YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", - "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Open Exchange Rates YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/en.json b/homeassistant/components/openexchangerates/translations/en.json index f4827c4df4d..eb41ae0ca14 100644 --- a/homeassistant/components/openexchangerates/translations/en.json +++ b/homeassistant/components/openexchangerates/translations/en.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Configuring Open Exchange Rates using YAML has been removed.\n\nRemove the Open Exchange Rates YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", - "title": "The Open Exchange Rates YAML configuration has been removed" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/es.json b/homeassistant/components/openexchangerates/translations/es.json index b71ef652770..982035eb782 100644 --- a/homeassistant/components/openexchangerates/translations/es.json +++ b/homeassistant/components/openexchangerates/translations/es.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Se ha eliminado la configuraci\u00f3n de Open Exchange Rates mediante YAML. \n\nElimina la configuraci\u00f3n YAML de Open Exchange Rates de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", - "title": "Se ha eliminado la configuraci\u00f3n YAML de Open Exchange Rates" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/et.json b/homeassistant/components/openexchangerates/translations/et.json index 45ed7fb1cfc..ebe4d41ab69 100644 --- a/homeassistant/components/openexchangerates/translations/et.json +++ b/homeassistant/components/openexchangerates/translations/et.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Open Exchange Rates konfigureerimine YAML-i abil eemaldati.\n\nEemalda Open Exchange Rates YAML-konfiguratsioon oma configuration.yaml-failist ja k\u00e4ivita Home Assistant uuesti, et see probleem lahendada.", - "title": "Open Exchange Rates YAML-konfiguratsioon eemaldati" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/fr.json b/homeassistant/components/openexchangerates/translations/fr.json index c6d0bb24444..a6b5929245a 100644 --- a/homeassistant/components/openexchangerates/translations/fr.json +++ b/homeassistant/components/openexchangerates/translations/fr.json @@ -23,10 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "title": "La configuration YAML pour Open Exchange Rates a \u00e9t\u00e9 supprim\u00e9e" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/hu.json b/homeassistant/components/openexchangerates/translations/hu.json index 51843cd899a..2bd735e177c 100644 --- a/homeassistant/components/openexchangerates/translations/hu.json +++ b/homeassistant/components/openexchangerates/translations/hu.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Az Open Exchange Rates konfigur\u00e1l\u00e1sa YAML haszn\u00e1lat\u00e1val elt\u00e1vol\u00edt\u00e1sra ker\u00fcl.\n\nA probl\u00e9ma megold\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", - "title": "Az Open Exchange Rates YAML-konfigur\u00e1ci\u00f3ja elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/id.json b/homeassistant/components/openexchangerates/translations/id.json index d53a8e9ef5f..e59732c319f 100644 --- a/homeassistant/components/openexchangerates/translations/id.json +++ b/homeassistant/components/openexchangerates/translations/id.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Proses konfigurasi Integrasi Open Exchange Rates lewat YAML telah dihapus.\n\nHapus konfigurasi YAML Integrasi Open Exchange Rates dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", - "title": "Konfigurasi YAML Integrasi Open Exchange Rates telah dihapus" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/it.json b/homeassistant/components/openexchangerates/translations/it.json index 491a44e7508..5547fa57ba1 100644 --- a/homeassistant/components/openexchangerates/translations/it.json +++ b/homeassistant/components/openexchangerates/translations/it.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "La configurazione di Open Exchange Rates tramite YAML \u00e8 stata rimossa. \n\nRimuovi la configurazione YAML di Open Exchange Rates dal file configuration.yaml e riavvia Home Assistant per risolvere questo problema.", - "title": "La configurazione YAML di Open Exchange Rates \u00e8 stata rimossa" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/ja.json b/homeassistant/components/openexchangerates/translations/ja.json index 9c7212a44b5..01f62ccd060 100644 --- a/homeassistant/components/openexchangerates/translations/ja.json +++ b/homeassistant/components/openexchangerates/translations/ja.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Open Exchange Rates\u306eYAML\u3092\u4f7f\u7528\u3057\u305f\u8a2d\u5b9a\u306f\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002\n\n\u306a\u304a\u3001\u65e2\u5b58\u306eYAML\u8a2d\u5b9a\u306f\u3001UI\u306b\u81ea\u52d5\u7684\u306b\u30a4\u30f3\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059\u3002\n\n\u3053\u306e\u554f\u984c\u3092\u89e3\u6c7a\u3059\u308b\u306b\u306f\u3001configuration.yaml\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u3001Open Exchange Rates\u306eYAML\u8a2d\u5b9a\u3092\u524a\u9664\u3057\u3001Home Assistant\u3092\u518d\u8d77\u52d5\u3057\u307e\u3059\u3002", - "title": "Open Exchange Rates YAML\u306e\u8a2d\u5b9a\u306f\u524a\u9664\u3055\u308c\u3066\u3044\u307e\u3059" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/no.json b/homeassistant/components/openexchangerates/translations/no.json index 1e810f5a52e..6fdbbcdd598 100644 --- a/homeassistant/components/openexchangerates/translations/no.json +++ b/homeassistant/components/openexchangerates/translations/no.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Konfigurering av \u00e5pne valutakurser med YAML er fjernet. \n\n Fjern Open Exchange Rates YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", - "title": "Open Exchange Rates YAML-konfigurasjonen er fjernet" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/pl.json b/homeassistant/components/openexchangerates/translations/pl.json index a9bb2278d90..48e289f5d2e 100644 --- a/homeassistant/components/openexchangerates/translations/pl.json +++ b/homeassistant/components/openexchangerates/translations/pl.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Konfiguracja Open Exchange Rates przy u\u017cyciu YAML zosta\u0142a usuni\u0119ta. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", - "title": "Konfiguracja YAML dla Open Exchange Rates zosta\u0142a usuni\u0119ta" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/pt-BR.json b/homeassistant/components/openexchangerates/translations/pt-BR.json index 7f6edf42577..d2cf35cbf63 100644 --- a/homeassistant/components/openexchangerates/translations/pt-BR.json +++ b/homeassistant/components/openexchangerates/translations/pt-BR.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "A configura\u00e7\u00e3o de Open Exchange Rates usando YAML est\u00e1 sendo removida. \n\n Sua configura\u00e7\u00e3o YAML existente foi importada para a interface do usu\u00e1rio automaticamente. \n\n Remova a configura\u00e7\u00e3o YAML do Open Exchange Rates do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", - "title": "A configura\u00e7\u00e3o de YAML de Open Exchange Rates est\u00e1 sendo removida" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/pt.json b/homeassistant/components/openexchangerates/translations/pt.json index 1da8a0cc5ab..18a9b3af81a 100644 --- a/homeassistant/components/openexchangerates/translations/pt.json +++ b/homeassistant/components/openexchangerates/translations/pt.json @@ -10,11 +10,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "A configura\u00e7\u00e3o de taxas de c\u00e2mbio abertas usando YAML est\u00e1 sendo removida. \n\n Sua configura\u00e7\u00e3o YAML existente foi importada para a interface do usu\u00e1rio automaticamente. \n\n Remova a configura\u00e7\u00e3o Open Exchange Rates YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", - "title": "A configura\u00e7\u00e3o de YAML de taxas de c\u00e2mbio abertas est\u00e1 sendo removida" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/ru.json b/homeassistant/components/openexchangerates/translations/ru.json index cfc8edb0e8d..c6f19823ba7 100644 --- a/homeassistant/components/openexchangerates/translations/ru.json +++ b/homeassistant/components/openexchangerates/translations/ru.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \"Open Exchange Rates\" \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f YAML \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Home Assistant.\n\n\u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", - "title": "\u0423\u0434\u0430\u043b\u0435\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Open Exchange Rates \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/sv.json b/homeassistant/components/openexchangerates/translations/sv.json index 578d74864ba..eaa56b5b38d 100644 --- a/homeassistant/components/openexchangerates/translations/sv.json +++ b/homeassistant/components/openexchangerates/translations/sv.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Konfigurering av Open Exchange Rates med YAML tas bort. \n\n Din befintliga YAML-konfiguration har automatiskt importerats till anv\u00e4ndargr\u00e4nssnittet. \n\n Ta bort Open Exchange Rates YAML-konfigurationen fr\u00e5n filen configuration.yaml och starta om Home Assistant f\u00f6r att \u00e5tg\u00e4rda problemet.", - "title": "Open Exchange Rates YAML-konfigurationen tas bort" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/tr.json b/homeassistant/components/openexchangerates/translations/tr.json index 4149d5bd52d..cce9561913f 100644 --- a/homeassistant/components/openexchangerates/translations/tr.json +++ b/homeassistant/components/openexchangerates/translations/tr.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "YAML kullanarak A\u00e7\u0131k D\u00f6viz Kurlar\u0131n\u0131 yap\u0131land\u0131rma kald\u0131r\u0131ld\u0131. \n\n Open Exchange Rates YAML yap\u0131land\u0131rmas\u0131n\u0131 configuration.yaml dosyan\u0131zdan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", - "title": "A\u00e7\u0131k D\u00f6viz Kurlar\u0131 YAML yap\u0131land\u0131rmas\u0131 kald\u0131r\u0131ld\u0131" - } } } \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/zh-Hant.json b/homeassistant/components/openexchangerates/translations/zh-Hant.json index d2b9b7ecb27..e24c925086b 100644 --- a/homeassistant/components/openexchangerates/translations/zh-Hant.json +++ b/homeassistant/components/openexchangerates/translations/zh-Hant.json @@ -23,11 +23,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a\u7684 Open Exchange Rates \u5df2\u79fb\u9664\u3002\n\n\u8acb\u65bc configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Open Exchange Rates YAML \u8a2d\u5b9a\u4e26\u91cd\u65b0\u555f\u52d5 Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", - "title": "Open Exchange Rates YAML \u8a2d\u5b9a\u5df2\u79fb\u9664" - } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/bg.json b/homeassistant/components/overkiz/translations/bg.json index 4f427272738..b15966c0221 100644 --- a/homeassistant/components/overkiz/translations/bg.json +++ b/homeassistant/components/overkiz/translations/bg.json @@ -11,7 +11,6 @@ "server_in_maintenance": "\u0421\u044a\u0440\u0432\u044a\u0440\u044a\u0442 \u0435 \u0441\u043f\u0440\u044f\u043d \u0437\u0430 \u043f\u043e\u0434\u0434\u0440\u044a\u0436\u043a\u0430", "too_many_requests": "\u0422\u0432\u044a\u0440\u0434\u0435 \u043c\u043d\u043e\u0433\u043e \u0437\u0430\u044f\u0432\u043a\u0438, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e \u043f\u043e-\u043a\u044a\u0441\u043d\u043e.", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430", - "unknown_user": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043d \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b. \u0410\u043a\u0430\u0443\u043d\u0442\u0438\u0442\u0435 \u043d\u0430 Somfy Protect \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u0442 \u043e\u0442 \u0442\u0430\u0437\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f.", "unsupported_hardware": "\u0412\u0430\u0448\u0438\u044f\u0442 \u0445\u0430\u0440\u0434\u0443\u0435\u0440 {unsupported_device} \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430 \u043e\u0442 \u0442\u0430\u0437\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f." }, "flow_title": "\u0428\u043b\u044e\u0437: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/bn.json b/homeassistant/components/overkiz/translations/bn.json deleted file mode 100644 index de652521c3c..00000000000 --- a/homeassistant/components/overkiz/translations/bn.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "config": { - "error": { - "unknown_user": "\u0985\u09aa\u09b0\u09bf\u099a\u09bf\u09a4 \u09ac\u09cd\u09af\u09ac\u09b9\u09be\u09b0\u0995\u09be\u09b0\u09c0\u0964 Somfy Protect \u0985\u09cd\u09af\u09be\u0995\u09be\u0989\u09a8\u09cd\u099f\u0997\u09c1\u09b2\u09bf \u098f\u0987 \u0987\u09a8\u09cd\u099f\u09bf\u0997\u09cd\u09b0\u09c7\u09b6\u09a8 \u09a6\u09cd\u09ac\u09be\u09b0\u09be \u09b8\u09ae\u09b0\u09cd\u09a5\u09bf\u09a4 \u09a8\u09af\u09bc\u0964" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/ca.json b/homeassistant/components/overkiz/translations/ca.json index d1707e0cbf2..fd2cef81b32 100644 --- a/homeassistant/components/overkiz/translations/ca.json +++ b/homeassistant/components/overkiz/translations/ca.json @@ -12,7 +12,6 @@ "too_many_attempts": "Massa intents amb un 'token' inv\u00e0lid, bloquejat temporalment", "too_many_requests": "Massa sol\u00b7licituds, torna-ho a provar m\u00e9s tard", "unknown": "Error inesperat", - "unknown_user": "Usuari desconegut. Els comptes de Somfy Protect no s\u00f3n compatibles amb aquesta integraci\u00f3.", "unsupported_hardware": "{unsupported_device} no \u00e9s compatible amb aquesta integraci\u00f3." }, "flow_title": "Passarel\u00b7la: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/de.json b/homeassistant/components/overkiz/translations/de.json index ff7536e0363..a1e9ea43dea 100644 --- a/homeassistant/components/overkiz/translations/de.json +++ b/homeassistant/components/overkiz/translations/de.json @@ -12,7 +12,6 @@ "too_many_attempts": "Zu viele Versuche mit einem ung\u00fcltigen Token, vor\u00fcbergehend gesperrt", "too_many_requests": "Zu viele Anfragen, versuche es sp\u00e4ter erneut.", "unknown": "Unerwarteter Fehler", - "unknown_user": "Unbekannter Benutzer. Somfy Protect-Konten werden von dieser Integration nicht unterst\u00fctzt.", "unsupported_hardware": "Deine {unsupported_device} Hardware wird von dieser Integration nicht unterst\u00fctzt." }, "flow_title": "Gateway: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/el.json b/homeassistant/components/overkiz/translations/el.json index eb308c470d3..4a61e3ac380 100644 --- a/homeassistant/components/overkiz/translations/el.json +++ b/homeassistant/components/overkiz/translations/el.json @@ -12,7 +12,6 @@ "too_many_attempts": "\u03a0\u03ac\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03ad\u03c2 \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b5\u03c2 \u03bc\u03b5 \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc, \u03c0\u03c1\u03bf\u03c3\u03c9\u03c1\u03b9\u03bd\u03ac \u03b1\u03c0\u03bf\u03ba\u03bb\u03b5\u03b9\u03c3\u03bc\u03ad\u03bd\u03b5\u03c2", "too_many_requests": "\u03a0\u03ac\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03ac \u03b1\u03b9\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1, \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03b1\u03c1\u03b3\u03cc\u03c4\u03b5\u03c1\u03b1", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1", - "unknown_user": "\u0386\u03b3\u03bd\u03c9\u03c3\u03c4\u03bf\u03c2 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2. \u039f\u03b9 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03af Somfy Protect \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7.", "unsupported_hardware": "\u03a4\u03bf \u03c5\u03bb\u03b9\u03ba\u03cc \u03c3\u03b1\u03c2 {unsupported_device} \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7." }, "flow_title": "\u03a0\u03cd\u03bb\u03b7: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/en.json b/homeassistant/components/overkiz/translations/en.json index d7dcd2a79ac..2c534a64cb6 100644 --- a/homeassistant/components/overkiz/translations/en.json +++ b/homeassistant/components/overkiz/translations/en.json @@ -12,7 +12,6 @@ "too_many_attempts": "Too many attempts with an invalid token, temporarily banned", "too_many_requests": "Too many requests, try again later", "unknown": "Unexpected error", - "unknown_user": "Unknown user. Somfy Protect accounts are not supported by this integration.", "unsupported_hardware": "Your {unsupported_device} hardware is not supported by this integration." }, "flow_title": "Gateway: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/es.json b/homeassistant/components/overkiz/translations/es.json index 1cec438abbb..aae98a32900 100644 --- a/homeassistant/components/overkiz/translations/es.json +++ b/homeassistant/components/overkiz/translations/es.json @@ -12,7 +12,6 @@ "too_many_attempts": "Demasiados intentos con un token no v\u00e1lido, prohibido temporalmente", "too_many_requests": "Demasiadas solicitudes, vuelve a intentarlo m\u00e1s tarde", "unknown": "Error inesperado", - "unknown_user": "Usuario desconocido. Las cuentas de Somfy Protect no son compatibles con esta integraci\u00f3n.", "unsupported_hardware": "Tu hardware {unsupported_device} no es compatible con esta integraci\u00f3n." }, "flow_title": "Puerta de enlace: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/et.json b/homeassistant/components/overkiz/translations/et.json index 2170fff5c45..1e34227b0d1 100644 --- a/homeassistant/components/overkiz/translations/et.json +++ b/homeassistant/components/overkiz/translations/et.json @@ -12,7 +12,6 @@ "too_many_attempts": "Liiga palju katseid kehtetu v\u00f5tmega, ajutiselt keelatud", "too_many_requests": "Liiga palju p\u00e4ringuid, proovi hiljem uuesti", "unknown": "Ootamatu t\u00f5rge", - "unknown_user": "Tundmatu kasutaja. See sidumine ei toeta Somfy Protecti kontosid.", "unsupported_hardware": "See sidumine ei toeta {unsupported_device} riistvara." }, "flow_title": "L\u00fc\u00fcs: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/fr.json b/homeassistant/components/overkiz/translations/fr.json index 0fd17d822f5..82997fbd1ae 100644 --- a/homeassistant/components/overkiz/translations/fr.json +++ b/homeassistant/components/overkiz/translations/fr.json @@ -12,7 +12,6 @@ "too_many_attempts": "Trop de tentatives avec un jeton non valide\u00a0: banni temporairement", "too_many_requests": "Trop de demandes, r\u00e9essayez plus tard.", "unknown": "Erreur inattendue", - "unknown_user": "Utilisateur inconnu. Les comptes Somfy Protect ne sont pas pris en charge par cette int\u00e9gration.", "unsupported_hardware": "Votre mat\u00e9riel {unsupported_device} n'est pas pris en charge par cette int\u00e9gration." }, "flow_title": "Passerelle\u00a0: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/hu.json b/homeassistant/components/overkiz/translations/hu.json index 95e3090add0..85827bcee50 100644 --- a/homeassistant/components/overkiz/translations/hu.json +++ b/homeassistant/components/overkiz/translations/hu.json @@ -12,7 +12,6 @@ "too_many_attempts": "T\u00fal sok pr\u00f3b\u00e1lkoz\u00e1s \u00e9rv\u00e9nytelen tokennel, ideiglenesen kitiltva", "too_many_requests": "T\u00fal sok a k\u00e9r\u00e9s, pr\u00f3b\u00e1lja meg k\u00e9s\u0151bb \u00fajra.", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt", - "unknown_user": "Ismeretlen felhaszn\u00e1l\u00f3. Ez az integr\u00e1ci\u00f3 nem t\u00e1mogatja a Somfy Protect fi\u00f3kokat.", "unsupported_hardware": "{unsupported_device} hardver\u00e9t ez az integr\u00e1ci\u00f3 nem t\u00e1mogatja." }, "flow_title": "\u00c1tj\u00e1r\u00f3: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/id.json b/homeassistant/components/overkiz/translations/id.json index 8f4b1912366..0fa0e0b22bb 100644 --- a/homeassistant/components/overkiz/translations/id.json +++ b/homeassistant/components/overkiz/translations/id.json @@ -12,7 +12,6 @@ "too_many_attempts": "Terlalu banyak percobaan dengan token yang tidak valid, untuk sementara diblokir", "too_many_requests": "Terlalu banyak permintaan, coba lagi nanti.", "unknown": "Kesalahan yang tidak diharapkan", - "unknown_user": "Pengguna tidak dikenal. Akun Somfy Protect tidak didukung oleh integrasi ini.", "unsupported_hardware": "Perangkat keras {unsupported_device} Anda tidak didukung oleh integrasi ini." }, "flow_title": "Gateway: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/it.json b/homeassistant/components/overkiz/translations/it.json index 49cc4b9a567..96a337b84e9 100644 --- a/homeassistant/components/overkiz/translations/it.json +++ b/homeassistant/components/overkiz/translations/it.json @@ -12,7 +12,6 @@ "too_many_attempts": "Troppi tentativi con un token non valido, temporaneamente bandito", "too_many_requests": "Troppe richieste, riprova pi\u00f9 tardi.", "unknown": "Errore imprevisto", - "unknown_user": "Utente sconosciuto. Gli account Somfy Protect non sono supportati da questa integrazione.", "unsupported_hardware": "L'hardware {unsupported_device} non \u00e8 supportato da questa integrazione." }, "flow_title": "Gateway: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/ja.json b/homeassistant/components/overkiz/translations/ja.json index d2f72355dfd..df1a438d8ca 100644 --- a/homeassistant/components/overkiz/translations/ja.json +++ b/homeassistant/components/overkiz/translations/ja.json @@ -11,8 +11,7 @@ "server_in_maintenance": "\u30e1\u30f3\u30c6\u30ca\u30f3\u30b9\u306e\u305f\u3081\u30b5\u30fc\u30d0\u30fc\u304c\u30c0\u30a6\u30f3\u3057\u3066\u3044\u307e\u3059", "too_many_attempts": "\u7121\u52b9\u306a\u30c8\u30fc\u30af\u30f3\u306b\u3088\u308b\u8a66\u884c\u56de\u6570\u304c\u591a\u3059\u304e\u305f\u305f\u3081\u3001\u4e00\u6642\u7684\u306b\u7981\u6b62\u3055\u308c\u307e\u3057\u305f\u3002", "too_many_requests": "\u30ea\u30af\u30a8\u30b9\u30c8\u304c\u591a\u3059\u304e\u307e\u3059\u3002\u3057\u3070\u3089\u304f\u3057\u3066\u304b\u3089\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc", - "unknown_user": "\u4e0d\u660e\u306a\u30e6\u30fc\u30b6\u30fc\u3067\u3059\u3002Somfy Protect\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3001\u3053\u306e\u7d71\u5408\u3067\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002" + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "flow_title": "\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4: {gateway_id}", "step": { diff --git a/homeassistant/components/overkiz/translations/no.json b/homeassistant/components/overkiz/translations/no.json index 062cf053fbd..a1c7c03853d 100644 --- a/homeassistant/components/overkiz/translations/no.json +++ b/homeassistant/components/overkiz/translations/no.json @@ -12,7 +12,6 @@ "too_many_attempts": "For mange fors\u00f8k med et ugyldig token, midlertidig utestengt", "too_many_requests": "For mange foresp\u00f8rsler. Pr\u00f8v igjen senere", "unknown": "Uventet feil", - "unknown_user": "Ukjent bruker. Somfy Protect-kontoer st\u00f8ttes ikke av denne integrasjonen.", "unsupported_hardware": "Maskinvaren din for {unsupported_device} st\u00f8ttes ikke av denne integrasjonen." }, "flow_title": "Gateway: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/pl.json b/homeassistant/components/overkiz/translations/pl.json index 517ea42ac47..87c365c099f 100644 --- a/homeassistant/components/overkiz/translations/pl.json +++ b/homeassistant/components/overkiz/translations/pl.json @@ -12,7 +12,6 @@ "too_many_attempts": "Zbyt wiele pr\u00f3b z nieprawid\u0142owym tokenem, konto tymczasowo zablokowane", "too_many_requests": "Zbyt wiele \u017c\u0105da\u0144, spr\u00f3buj ponownie p\u00f3\u017aniej.", "unknown": "Nieoczekiwany b\u0142\u0105d", - "unknown_user": "Nieznany u\u017cytkownik. Konta Somfy Protect nie s\u0105 obs\u0142ugiwane przez t\u0119 integracj\u0119.", "unsupported_hardware": "Twoje urz\u0105dzenie {unsupported_device} nie jest wspierane przez t\u0119 integracj\u0119." }, "flow_title": "Bramka: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/pt-BR.json b/homeassistant/components/overkiz/translations/pt-BR.json index 206b8632656..a7e3f6ff206 100644 --- a/homeassistant/components/overkiz/translations/pt-BR.json +++ b/homeassistant/components/overkiz/translations/pt-BR.json @@ -12,7 +12,6 @@ "too_many_attempts": "Muitas tentativas com um token inv\u00e1lido, banido temporariamente", "too_many_requests": "Muitas solicita\u00e7\u00f5es, tente novamente mais tarde", "unknown": "Erro inesperado", - "unknown_user": "Usu\u00e1rio desconhecido. As contas Somfy Protect n\u00e3o s\u00e3o suportadas por esta integra\u00e7\u00e3o.", "unsupported_hardware": "Seu hardware {unsupported_device} n\u00e3o \u00e9 compat\u00edvel com esta integra\u00e7\u00e3o." }, "flow_title": "Gateway: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/pt.json b/homeassistant/components/overkiz/translations/pt.json index 5b2dd940959..1e3d9138c84 100644 --- a/homeassistant/components/overkiz/translations/pt.json +++ b/homeassistant/components/overkiz/translations/pt.json @@ -1,8 +1,7 @@ { "config": { "error": { - "unknown": "Erro inesperado", - "unknown_user": "Usu\u00e1rio desconhecido. As contas Somfy Protect n\u00e3o s\u00e3o suportadas por esta integra\u00e7\u00e3o." + "unknown": "Erro inesperado" }, "step": { "user": { diff --git a/homeassistant/components/overkiz/translations/ru.json b/homeassistant/components/overkiz/translations/ru.json index 128792152dc..67ca42a6947 100644 --- a/homeassistant/components/overkiz/translations/ru.json +++ b/homeassistant/components/overkiz/translations/ru.json @@ -12,7 +12,6 @@ "too_many_attempts": "\u0421\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043f\u044b\u0442\u043e\u043a \u0441 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0442\u043e\u043a\u0435\u043d\u043e\u043c, \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043e.", "too_many_requests": "\u0421\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u043f\u043e\u0437\u0436\u0435.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430.", - "unknown_user": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c. \u042d\u0442\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 Somfy Protect.", "unsupported_hardware": "{unsupported_device} \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u044d\u0442\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0435\u0439." }, "flow_title": "\u0428\u043b\u044e\u0437: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/sv.json b/homeassistant/components/overkiz/translations/sv.json index 2ae1ca66d32..a5eb948b98b 100644 --- a/homeassistant/components/overkiz/translations/sv.json +++ b/homeassistant/components/overkiz/translations/sv.json @@ -12,7 +12,6 @@ "too_many_attempts": "F\u00f6r m\u00e5nga f\u00f6rs\u00f6k med en ogiltig token, tillf\u00e4lligt avst\u00e4ngd", "too_many_requests": "F\u00f6r m\u00e5nga f\u00f6rfr\u00e5gningar, f\u00f6rs\u00f6k igen senare", "unknown": "Ov\u00e4ntat fel", - "unknown_user": "Ok\u00e4nd anv\u00e4ndare. Somfy Protect-konton st\u00f6ds inte av denna integration.", "unsupported_hardware": "Din {unsupported_device} h\u00e5rdvara st\u00f6ds inte av den h\u00e4r integrationen." }, "flow_title": "Gateway: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/tr.json b/homeassistant/components/overkiz/translations/tr.json index ed82e138673..f3cb5b70d45 100644 --- a/homeassistant/components/overkiz/translations/tr.json +++ b/homeassistant/components/overkiz/translations/tr.json @@ -12,7 +12,6 @@ "too_many_attempts": "Ge\u00e7ersiz anahtarla \u00e7ok fazla deneme, ge\u00e7ici olarak yasakland\u0131", "too_many_requests": "\u00c7ok fazla istek var, daha sonra tekrar deneyin", "unknown": "Beklenmeyen hata", - "unknown_user": "Bilinmeyen kullan\u0131c\u0131. Somfy Protect hesaplar\u0131 bu entegrasyon taraf\u0131ndan desteklenmez.", "unsupported_hardware": "{unsupported_device} donan\u0131m\u0131n\u0131z bu entegrasyon taraf\u0131ndan desteklenmiyor." }, "flow_title": "A\u011f ge\u00e7idi: {gateway_id}", diff --git a/homeassistant/components/overkiz/translations/zh-Hant.json b/homeassistant/components/overkiz/translations/zh-Hant.json index ea8ebcd29dc..072233f23bc 100644 --- a/homeassistant/components/overkiz/translations/zh-Hant.json +++ b/homeassistant/components/overkiz/translations/zh-Hant.json @@ -12,7 +12,6 @@ "too_many_attempts": "\u4f7f\u7528\u7121\u6548\u6b0a\u6756\u5617\u8a66\u6b21\u6578\u904e\u591a\uff0c\u66ab\u6642\u906d\u5230\u5c01\u9396", "too_many_requests": "\u8acb\u6c42\u6b21\u6578\u904e\u591a\uff0c\u8acb\u7a0d\u5f8c\u91cd\u8a66\u3002", "unknown": "\u672a\u9810\u671f\u932f\u8aa4", - "unknown_user": "\u672a\u77e5\u4f7f\u7528\u8005\u3001\u6b64\u6574\u5408\u4e0d\u652f\u63f4 Somfy Protect \u5e33\u865f\u3002", "unsupported_hardware": "\u6b64\u6574\u5408\u4e0d\u652f\u63f4\u60a8\u7684 {unsupported_device} \u786c\u9ad4\u3002" }, "flow_title": "\u9598\u9053\u5668\uff1a{gateway_id}", diff --git a/homeassistant/components/p1_monitor/translations/bg.json b/homeassistant/components/p1_monitor/translations/bg.json index acbebfb4d36..824249b169b 100644 --- a/homeassistant/components/p1_monitor/translations/bg.json +++ b/homeassistant/components/p1_monitor/translations/bg.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "\u0425\u043e\u0441\u0442", - "name": "\u0418\u043c\u0435" + "host": "\u0425\u043e\u0441\u0442" } } } diff --git a/homeassistant/components/p1_monitor/translations/ca.json b/homeassistant/components/p1_monitor/translations/ca.json index 6d65ba16b5c..5c806805c24 100644 --- a/homeassistant/components/p1_monitor/translations/ca.json +++ b/homeassistant/components/p1_monitor/translations/ca.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Amfitri\u00f3", - "name": "Nom" + "host": "Amfitri\u00f3" }, "data_description": { "host": "Adre\u00e7a IP o nom d'amfitri\u00f3 de la instal\u00b7laci\u00f3 P1 Monitor." diff --git a/homeassistant/components/p1_monitor/translations/cs.json b/homeassistant/components/p1_monitor/translations/cs.json index 7a27355056b..44f546c1c29 100644 --- a/homeassistant/components/p1_monitor/translations/cs.json +++ b/homeassistant/components/p1_monitor/translations/cs.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Hostitel", - "name": "Jm\u00e9no" + "host": "Hostitel" } } } diff --git a/homeassistant/components/p1_monitor/translations/de.json b/homeassistant/components/p1_monitor/translations/de.json index 8740c9dccbb..1da6e7da32d 100644 --- a/homeassistant/components/p1_monitor/translations/de.json +++ b/homeassistant/components/p1_monitor/translations/de.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Name" + "host": "Host" }, "data_description": { "host": "Die IP-Adresse oder der Hostname deiner P1 Monitor-Installation." diff --git a/homeassistant/components/p1_monitor/translations/el.json b/homeassistant/components/p1_monitor/translations/el.json index bd30b70ef63..fe6dfaf6fd0 100644 --- a/homeassistant/components/p1_monitor/translations/el.json +++ b/homeassistant/components/p1_monitor/translations/el.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1" + "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" }, "data_description": { "host": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03ae \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae \u03c4\u03b7\u03c2 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03bf\u03b8\u03cc\u03bd\u03b7\u03c2 P1." diff --git a/homeassistant/components/p1_monitor/translations/en.json b/homeassistant/components/p1_monitor/translations/en.json index 4347e2d89d2..394b6c0767b 100644 --- a/homeassistant/components/p1_monitor/translations/en.json +++ b/homeassistant/components/p1_monitor/translations/en.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Name" + "host": "Host" }, "data_description": { "host": "The IP address or hostname of your P1 Monitor installation." diff --git a/homeassistant/components/p1_monitor/translations/es.json b/homeassistant/components/p1_monitor/translations/es.json index 3893952a1ec..4ed069e96e4 100644 --- a/homeassistant/components/p1_monitor/translations/es.json +++ b/homeassistant/components/p1_monitor/translations/es.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Nombre" + "host": "Host" }, "data_description": { "host": "La direcci\u00f3n IP o el nombre de host de tu instalaci\u00f3n de P1 Monitor." diff --git a/homeassistant/components/p1_monitor/translations/et.json b/homeassistant/components/p1_monitor/translations/et.json index 18a561ea30b..e3e21aa3ef7 100644 --- a/homeassistant/components/p1_monitor/translations/et.json +++ b/homeassistant/components/p1_monitor/translations/et.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Nimi" + "host": "Host" }, "data_description": { "host": "P1 Monitori paigalduse IP-aadress v\u00f5i hostinimi." diff --git a/homeassistant/components/p1_monitor/translations/fr.json b/homeassistant/components/p1_monitor/translations/fr.json index 5da5fe439cd..22e683fbf44 100644 --- a/homeassistant/components/p1_monitor/translations/fr.json +++ b/homeassistant/components/p1_monitor/translations/fr.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "H\u00f4te", - "name": "Nom" + "host": "H\u00f4te" }, "data_description": { "host": "L'adresse IP ou le nom d'h\u00f4te de votre installation P1 Monitor." diff --git a/homeassistant/components/p1_monitor/translations/he.json b/homeassistant/components/p1_monitor/translations/he.json index 33660936e12..ab620ca81eb 100644 --- a/homeassistant/components/p1_monitor/translations/he.json +++ b/homeassistant/components/p1_monitor/translations/he.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "\u05de\u05d0\u05e8\u05d7", - "name": "\u05e9\u05dd" + "host": "\u05de\u05d0\u05e8\u05d7" } } } diff --git a/homeassistant/components/p1_monitor/translations/hu.json b/homeassistant/components/p1_monitor/translations/hu.json index 0b6071be83f..bac2951ba2f 100644 --- a/homeassistant/components/p1_monitor/translations/hu.json +++ b/homeassistant/components/p1_monitor/translations/hu.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "C\u00edm", - "name": "Elnevez\u00e9s" + "host": "C\u00edm" }, "data_description": { "host": "A P1 Monitor rendszer\u00e9nek IP-c\u00edme vagy hostneve." diff --git a/homeassistant/components/p1_monitor/translations/id.json b/homeassistant/components/p1_monitor/translations/id.json index 52bb67d00a6..ce5860ec401 100644 --- a/homeassistant/components/p1_monitor/translations/id.json +++ b/homeassistant/components/p1_monitor/translations/id.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Nama" + "host": "Host" }, "data_description": { "host": "Alamat IP atau nama host instalasi P1 Monitor Anda." diff --git a/homeassistant/components/p1_monitor/translations/it.json b/homeassistant/components/p1_monitor/translations/it.json index ae529d7cb92..e0c54b5d426 100644 --- a/homeassistant/components/p1_monitor/translations/it.json +++ b/homeassistant/components/p1_monitor/translations/it.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Nome" + "host": "Host" }, "data_description": { "host": "L'indirizzo IP o il nome host dell'installazione di P1 Monitor." diff --git a/homeassistant/components/p1_monitor/translations/ja.json b/homeassistant/components/p1_monitor/translations/ja.json index 66e79ebdc0b..765e8930f5f 100644 --- a/homeassistant/components/p1_monitor/translations/ja.json +++ b/homeassistant/components/p1_monitor/translations/ja.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "\u30db\u30b9\u30c8", - "name": "\u540d\u524d" + "host": "\u30db\u30b9\u30c8" }, "data_description": { "host": "P1 Monitor\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306e\u3001IP\u30a2\u30c9\u30ec\u30b9\u307e\u305f\u306f\u30db\u30b9\u30c8\u540d\u3002" diff --git a/homeassistant/components/p1_monitor/translations/nl.json b/homeassistant/components/p1_monitor/translations/nl.json index 1f5e9e5a422..614a0079589 100644 --- a/homeassistant/components/p1_monitor/translations/nl.json +++ b/homeassistant/components/p1_monitor/translations/nl.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Naam" + "host": "Host" }, "description": "Stel P1 Monitor in om te integreren met Home Assistant." } diff --git a/homeassistant/components/p1_monitor/translations/no.json b/homeassistant/components/p1_monitor/translations/no.json index 16e58ba7c7b..4aa965a7bf9 100644 --- a/homeassistant/components/p1_monitor/translations/no.json +++ b/homeassistant/components/p1_monitor/translations/no.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Vert", - "name": "Navn" + "host": "Vert" }, "data_description": { "host": "IP-adressen eller vertsnavnet til P1 Monitor-installasjonen." diff --git a/homeassistant/components/p1_monitor/translations/pl.json b/homeassistant/components/p1_monitor/translations/pl.json index 93e2b7a83bd..d935010937d 100644 --- a/homeassistant/components/p1_monitor/translations/pl.json +++ b/homeassistant/components/p1_monitor/translations/pl.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Nazwa hosta lub adres IP", - "name": "Nazwa" + "host": "Nazwa hosta lub adres IP" }, "data_description": { "host": "Adres IP lub nazwa hosta instalacji monitora P1." diff --git a/homeassistant/components/p1_monitor/translations/pt-BR.json b/homeassistant/components/p1_monitor/translations/pt-BR.json index 97dfd9a0ec3..b49bbab0c36 100644 --- a/homeassistant/components/p1_monitor/translations/pt-BR.json +++ b/homeassistant/components/p1_monitor/translations/pt-BR.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Nome do host", - "name": "Nome" + "host": "Nome do host" }, "data_description": { "host": "O endere\u00e7o IP ou o nome do host da instala\u00e7\u00e3o do P1 Monitor." diff --git a/homeassistant/components/p1_monitor/translations/pt.json b/homeassistant/components/p1_monitor/translations/pt.json index ab627843537..602d9c6d009 100644 --- a/homeassistant/components/p1_monitor/translations/pt.json +++ b/homeassistant/components/p1_monitor/translations/pt.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Servidor", - "name": "Nome" + "host": "Servidor" }, "data_description": { "host": "O endere\u00e7o IP ou nome de host da instala\u00e7\u00e3o do Monitor P1." diff --git a/homeassistant/components/p1_monitor/translations/ru.json b/homeassistant/components/p1_monitor/translations/ru.json index 10524a44fa5..f5873b76db7 100644 --- a/homeassistant/components/p1_monitor/translations/ru.json +++ b/homeassistant/components/p1_monitor/translations/ru.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "\u0425\u043e\u0441\u0442", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" + "host": "\u0425\u043e\u0441\u0442" }, "data_description": { "host": "IP-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0412\u0430\u0448\u0435\u0439 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 P1 Monitor." diff --git a/homeassistant/components/p1_monitor/translations/sk.json b/homeassistant/components/p1_monitor/translations/sk.json index 965b3bb8919..842ff61bd79 100644 --- a/homeassistant/components/p1_monitor/translations/sk.json +++ b/homeassistant/components/p1_monitor/translations/sk.json @@ -3,8 +3,7 @@ "step": { "user": { "data": { - "host": "Hostite\u013e", - "name": "N\u00e1zov" + "host": "Hostite\u013e" } } } diff --git a/homeassistant/components/p1_monitor/translations/sv.json b/homeassistant/components/p1_monitor/translations/sv.json index 2a4c3c62277..fbce408ee45 100644 --- a/homeassistant/components/p1_monitor/translations/sv.json +++ b/homeassistant/components/p1_monitor/translations/sv.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "V\u00e4rd", - "name": "Namn" + "host": "V\u00e4rd" }, "data_description": { "host": "IP-adressen eller v\u00e4rdnamnet f\u00f6r din P1 Monitor-installation." diff --git a/homeassistant/components/p1_monitor/translations/tr.json b/homeassistant/components/p1_monitor/translations/tr.json index 1ee8c351c8d..80dce588bc7 100644 --- a/homeassistant/components/p1_monitor/translations/tr.json +++ b/homeassistant/components/p1_monitor/translations/tr.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "Sunucu", - "name": "Ad" + "host": "Sunucu" }, "data_description": { "host": "P1 Monitor kurulumunuzun IP adresi veya ana bilgisayar ad\u0131." diff --git a/homeassistant/components/p1_monitor/translations/zh-Hant.json b/homeassistant/components/p1_monitor/translations/zh-Hant.json index 27e0200f1b2..0d899fb74b6 100644 --- a/homeassistant/components/p1_monitor/translations/zh-Hant.json +++ b/homeassistant/components/p1_monitor/translations/zh-Hant.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "host": "\u4e3b\u6a5f\u7aef", - "name": "\u540d\u7a31" + "host": "\u4e3b\u6a5f\u7aef" }, "data_description": { "host": "P1 Monitor \u5b89\u88dd IP \u4f4d\u5740\u6216\u4e3b\u6a5f\u540d\u7a31\u3002" diff --git a/homeassistant/components/plugwise/translations/bg.json b/homeassistant/components/plugwise/translations/bg.json index 18450edfce7..23d635d2b14 100644 --- a/homeassistant/components/plugwise/translations/bg.json +++ b/homeassistant/components/plugwise/translations/bg.json @@ -8,7 +8,6 @@ "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, - "flow_title": "{name}", "step": { "user": { "data": { @@ -16,12 +15,6 @@ "port": "\u041f\u043e\u0440\u0442" }, "description": "\u041f\u0440\u043e\u0434\u0443\u043a\u0442:" - }, - "user_gateway": { - "data": { - "host": "IP \u0430\u0434\u0440\u0435\u0441", - "port": "\u041f\u043e\u0440\u0442" - } } } } diff --git a/homeassistant/components/plugwise/translations/ca.json b/homeassistant/components/plugwise/translations/ca.json index ccd3d344f39..47aa93725ea 100644 --- a/homeassistant/components/plugwise/translations/ca.json +++ b/homeassistant/components/plugwise/translations/ca.json @@ -10,11 +10,9 @@ "invalid_setup": "Afegeix l'Adam en lloc de l'Anna; consulta la documentaci\u00f3 de la integraci\u00f3 Plugwise de Home Assistant per a m\u00e9s informaci\u00f3.", "unknown": "Error inesperat" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Tipus de connexi\u00f3", "host": "Adre\u00e7a IP", "password": "ID de Smile", "port": "Port", @@ -22,26 +20,6 @@ }, "description": "Introdueix", "title": "Connexi\u00f3 amb Smile" - }, - "user_gateway": { - "data": { - "host": "Adre\u00e7a IP", - "password": "ID de Smile", - "port": "Port", - "username": "Nom d'usuari de Smile" - }, - "description": "Introdueix", - "title": "Connexi\u00f3 amb Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Interval d'escaneig (segons)" - }, - "description": "Ajusta les opcions de Plugwise" } } } diff --git a/homeassistant/components/plugwise/translations/cs.json b/homeassistant/components/plugwise/translations/cs.json index a7f5dae7c97..201c2135722 100644 --- a/homeassistant/components/plugwise/translations/cs.json +++ b/homeassistant/components/plugwise/translations/cs.json @@ -8,35 +8,13 @@ "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, - "flow_title": "Smile: {name}", "step": { "user": { "data": { - "flow_type": "Typ p\u0159ipojen\u00ed", "host": "IP adresa" }, "description": "Produkt:", "title": "Typ Plugwise" - }, - "user_gateway": { - "data": { - "host": "IP adresa", - "password": "Smile ID", - "port": "Port", - "username": "U\u017eivatelsk\u00e9 jm\u00e9no Smile" - }, - "description": "Pros\u00edm zadejte", - "title": "P\u0159ipojen\u00ed k Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Interval sledov\u00e1n\u00ed (v sekund\u00e1ch)" - }, - "description": "Upravte mo\u017enosti Plugwise" } } } diff --git a/homeassistant/components/plugwise/translations/de.json b/homeassistant/components/plugwise/translations/de.json index fb80fecef25..987ac94e91b 100644 --- a/homeassistant/components/plugwise/translations/de.json +++ b/homeassistant/components/plugwise/translations/de.json @@ -10,11 +10,9 @@ "invalid_setup": "F\u00fcge deinen Adam anstelle deiner Anna hinzu. Weitere Informationen findest du in der Dokumentation zur Integration von Home Assistant Plugwise.", "unknown": "Unerwarteter Fehler" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Verbindungstyp", "host": "IP-Adresse", "password": "Smile ID", "port": "Port", @@ -22,26 +20,6 @@ }, "description": "Bitte eingeben", "title": "Stelle eine Verbindung zu Smile her" - }, - "user_gateway": { - "data": { - "host": "IP-Adresse", - "password": "Smile ID", - "port": "Port", - "username": "Smile-Benutzername" - }, - "description": "Bitte eingeben", - "title": "Stelle eine Verbindung zu Smile her" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Scanintervall (Sekunden)" - }, - "description": "Plugwise-Optionen einstellen" } } } diff --git a/homeassistant/components/plugwise/translations/el.json b/homeassistant/components/plugwise/translations/el.json index 18a50e86b66..2f15147e674 100644 --- a/homeassistant/components/plugwise/translations/el.json +++ b/homeassistant/components/plugwise/translations/el.json @@ -10,11 +10,9 @@ "invalid_setup": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd Adam \u03c3\u03b1\u03c2 \u03b1\u03bd\u03c4\u03af \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd Anna \u03c3\u03b1\u03c2, \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Home Assistant Plugwise \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP", "password": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc Smile", "port": "\u0398\u03cd\u03c1\u03b1", @@ -22,26 +20,6 @@ }, "description": "\u03a0\u03c1\u03bf\u03ca\u03cc\u03bd:", "title": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03b2\u03cd\u03c3\u03bc\u03b1\u03c4\u03bf\u03c2" - }, - "user_gateway": { - "data": { - "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP", - "password": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc Smile", - "port": "\u0398\u03cd\u03c1\u03b1", - "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 Smile" - }, - "description": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03ce \u03b5\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5", - "title": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u0394\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7\u03c2 (\u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1)" - }, - "description": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd \u03c4\u03bf\u03c0\u03bf\u03b8\u03ad\u03c4\u03b7\u03c3\u03b7\u03c2" } } } diff --git a/homeassistant/components/plugwise/translations/en.json b/homeassistant/components/plugwise/translations/en.json index cd10502d0c3..aa5a318bbff 100644 --- a/homeassistant/components/plugwise/translations/en.json +++ b/homeassistant/components/plugwise/translations/en.json @@ -10,11 +10,9 @@ "invalid_setup": "Add your Adam instead of your Anna, see the Home Assistant Plugwise integration documentation for more information", "unknown": "Unexpected error" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Connection type", "host": "IP Address", "password": "Smile ID", "port": "Port", @@ -22,26 +20,6 @@ }, "description": "Please enter", "title": "Connect to the Smile" - }, - "user_gateway": { - "data": { - "host": "IP Address", - "password": "Smile ID", - "port": "Port", - "username": "Smile Username" - }, - "description": "Please enter", - "title": "Connect to the Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Scan Interval (seconds)" - }, - "description": "Adjust Plugwise Options" } } } diff --git a/homeassistant/components/plugwise/translations/es.json b/homeassistant/components/plugwise/translations/es.json index fed26040384..7a0cd53122b 100644 --- a/homeassistant/components/plugwise/translations/es.json +++ b/homeassistant/components/plugwise/translations/es.json @@ -10,11 +10,9 @@ "invalid_setup": "A\u00f1ade tu Adam en lugar de tu Anna, consulta la documentaci\u00f3n de la integraci\u00f3n Home Assistant Plugwise para m\u00e1s informaci\u00f3n", "unknown": "Error inesperado" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Tipo de conexi\u00f3n", "host": "Direcci\u00f3n IP", "password": "ID de Smile", "port": "Puerto", @@ -22,26 +20,6 @@ }, "description": "Por favor, introduce", "title": "Conectar a Smile" - }, - "user_gateway": { - "data": { - "host": "Direcci\u00f3n IP", - "password": "ID de Smile", - "port": "Puerto", - "username": "Nombre de usuario Smile" - }, - "description": "Por favor, introduce", - "title": "Conectar a Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Intervalo de escaneo (segundos)" - }, - "description": "Ajustar las opciones de Plugwise" } } } diff --git a/homeassistant/components/plugwise/translations/et.json b/homeassistant/components/plugwise/translations/et.json index 9f2f2e0b1b6..53ec22d53ab 100644 --- a/homeassistant/components/plugwise/translations/et.json +++ b/homeassistant/components/plugwise/translations/et.json @@ -10,11 +10,9 @@ "invalid_setup": "Lisa oma Anna asemel oma Adam, lisateabe saamiseks vaata Home Assistant Plugwise'i sidumise dokumentatsiooni", "unknown": "Tundmatu viga" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "\u00dchenduse t\u00fc\u00fcp", "host": "IP aadress", "password": "Smile ID", "port": "Port", @@ -22,26 +20,6 @@ }, "description": "Sisesta andmed", "title": "Loo \u00fchendus Smile-ga" - }, - "user_gateway": { - "data": { - "host": "IP aadress", - "password": "Smile ID", - "port": "Port", - "username": "Smile kasutajanimi" - }, - "description": "Palun sisesta:", - "title": "Loo \u00fchendus Smile-ga" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "P\u00e4ringute intervall (sekundites)" - }, - "description": "Kohanda Plugwise s\u00e4tteid" } } } diff --git a/homeassistant/components/plugwise/translations/fr.json b/homeassistant/components/plugwise/translations/fr.json index 85f4f652c18..ee2b91b189e 100644 --- a/homeassistant/components/plugwise/translations/fr.json +++ b/homeassistant/components/plugwise/translations/fr.json @@ -10,11 +10,9 @@ "invalid_setup": "Ajoutez votre Adam au lieu de votre Anna\u00a0; consultez la documentation de l'int\u00e9gration Plugwise de Home Assistant pour plus d'informations", "unknown": "Erreur inattendue" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Type de connexion", "host": "Adresse IP", "password": "ID Smile", "port": "Port", @@ -22,26 +20,6 @@ }, "description": "Veuillez saisir", "title": "Se connecter \u00e0 Smile" - }, - "user_gateway": { - "data": { - "host": "Adresse IP", - "password": "ID Smile", - "port": "Port", - "username": "Nom d'utilisateur Smile" - }, - "description": "Veuillez saisir :", - "title": "Se connecter \u00e0 Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Intervalle de mise \u00e0 jour (secondes)" - }, - "description": "Ajuster les options Plugwise" } } } diff --git a/homeassistant/components/plugwise/translations/he.json b/homeassistant/components/plugwise/translations/he.json index f8a4c722a8f..ed7adcac577 100644 --- a/homeassistant/components/plugwise/translations/he.json +++ b/homeassistant/components/plugwise/translations/he.json @@ -8,7 +8,6 @@ "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, - "flow_title": "{name}", "step": { "user": { "data": { @@ -16,14 +15,6 @@ "port": "\u05e4\u05ea\u05d7\u05d4" }, "description": "\u05de\u05d5\u05e6\u05e8:" - }, - "user_gateway": { - "data": { - "host": "\u05db\u05ea\u05d5\u05d1\u05ea IP", - "password": "\u05de\u05d6\u05d4\u05d4 Smile", - "port": "\u05e4\u05ea\u05d7\u05d4", - "username": "\u05d7\u05d9\u05d9\u05da \u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" - } } } } diff --git a/homeassistant/components/plugwise/translations/hu.json b/homeassistant/components/plugwise/translations/hu.json index c97911bad09..cdfb76fcc5c 100644 --- a/homeassistant/components/plugwise/translations/hu.json +++ b/homeassistant/components/plugwise/translations/hu.json @@ -10,11 +10,9 @@ "invalid_setup": "Adja hozz\u00e1 Adamot Anna helyett. Tov\u00e1bbi inform\u00e1ci\u00f3\u00e9rt tekintse meg a Home Assistant Plugwise integr\u00e1ci\u00f3s dokument\u00e1ci\u00f3j\u00e1t", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Kapcsolat t\u00edpusa", "host": "IP c\u00edm", "password": "Smile azonos\u00edt\u00f3", "port": "Port", @@ -22,26 +20,6 @@ }, "description": "K\u00e9rem, adja meg", "title": "Csatlakoz\u00e1s a Smile-hoz" - }, - "user_gateway": { - "data": { - "host": "IP c\u00edm", - "password": "Smile azonos\u00edt\u00f3", - "port": "Port", - "username": "Smile Felhaszn\u00e1l\u00f3n\u00e9v" - }, - "description": "K\u00e9rem, adja meg", - "title": "Csatlakoz\u00e1s a Smile-hoz" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Szkennel\u00e9si intervallum (m\u00e1sodperc)" - }, - "description": "\u00c1ll\u00edtsa be a Plugwise lehet\u0151s\u00e9get" } } } diff --git a/homeassistant/components/plugwise/translations/id.json b/homeassistant/components/plugwise/translations/id.json index daa2824df27..3aabe38c8cc 100644 --- a/homeassistant/components/plugwise/translations/id.json +++ b/homeassistant/components/plugwise/translations/id.json @@ -10,11 +10,9 @@ "invalid_setup": "Tambahkan Adam Anda, alih-alih Anna. Baca dokumentasi integrasi Plugwise Home Assistant untuk informasi lebih lanjut", "unknown": "Kesalahan yang tidak diharapkan" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Jenis koneksi", "host": "Alamat IP", "password": "ID Smile", "port": "Port", @@ -22,26 +20,6 @@ }, "description": "Masukkan", "title": "Hubungkan ke Smile" - }, - "user_gateway": { - "data": { - "host": "Alamat IP", - "password": "ID Smile", - "port": "Port", - "username": "Nama Pengguna Smile" - }, - "description": "Masukkan", - "title": "Hubungkan ke Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Interval Pindai (detik)" - }, - "description": "Sesuaikan Opsi Plugwise" } } } diff --git a/homeassistant/components/plugwise/translations/it.json b/homeassistant/components/plugwise/translations/it.json index e4bf239b7bc..8d0555a12a7 100644 --- a/homeassistant/components/plugwise/translations/it.json +++ b/homeassistant/components/plugwise/translations/it.json @@ -10,11 +10,9 @@ "invalid_setup": "Aggiungi il tuo Adam invece di Anna, consulta la documentazione sull'integrazione di Home Assistant Plugwise per ulteriori informazioni", "unknown": "Errore imprevisto" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Tipo di connessione", "host": "Indirizzo IP", "password": "ID Smile", "port": "Porta", @@ -22,26 +20,6 @@ }, "description": "Inserisci", "title": "Connettiti allo Smile" - }, - "user_gateway": { - "data": { - "host": "Indirizzo IP", - "password": "ID Smile", - "port": "Porta", - "username": "Nome utente Smile" - }, - "description": "Inserisci", - "title": "Connettiti allo Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Intervallo di scansione (secondi)" - }, - "description": "Regolare le opzioni Plugwise" } } } diff --git a/homeassistant/components/plugwise/translations/ja.json b/homeassistant/components/plugwise/translations/ja.json index 15eb388d2c8..bc7ea107a5e 100644 --- a/homeassistant/components/plugwise/translations/ja.json +++ b/homeassistant/components/plugwise/translations/ja.json @@ -10,11 +10,9 @@ "invalid_setup": "Anna\u306e\u4ee3\u308f\u308a\u306b\u3001Adam\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001Home Assistant Plugwise\u7d71\u5408\u306e\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "\u63a5\u7d9a\u30bf\u30a4\u30d7", "host": "IP\u30a2\u30c9\u30ec\u30b9", "password": "Smile\u306eID", "port": "\u30dd\u30fc\u30c8", @@ -22,26 +20,6 @@ }, "description": "\u30d7\u30ed\u30c0\u30af\u30c8:", "title": "Plugwise type" - }, - "user_gateway": { - "data": { - "host": "IP\u30a2\u30c9\u30ec\u30b9", - "password": "Smile ID", - "port": "\u30dd\u30fc\u30c8", - "username": "Smile \u30e6\u30fc\u30b6\u30fc\u540d" - }, - "description": "\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "Smile\u306b\u63a5\u7d9a" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u30b9\u30ad\u30e3\u30f3\u30a4\u30f3\u30bf\u30fc\u30d0\u30eb(\u79d2)" - }, - "description": "Plugwise\u30aa\u30d7\u30b7\u30e7\u30f3\u306e\u8abf\u6574" } } } diff --git a/homeassistant/components/plugwise/translations/ka.json b/homeassistant/components/plugwise/translations/ka.json deleted file mode 100644 index d4b446b309c..00000000000 --- a/homeassistant/components/plugwise/translations/ka.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "flow_type": "\u1c99\u10d0\u10d5\u10e8\u10d8\u10e0\u10d8\u10e1 \u10e2\u10d8\u10de\u10d8" - } - }, - "user_gateway": { - "data": { - "host": "IP \u10db\u10d8\u10e1\u10d0\u10db\u10d0\u10e0\u10d7\u10d8", - "password": "Smile ID", - "port": "\u10de\u10dd\u10e0\u10e2\u10d8", - "username": "\u10e6\u10d8\u10db\u10d8\u10da\u10d8\u10d0\u10dc\u10d8 \u10db\u10dd\u10db\u10ee\u10db\u10d0\u10e0\u10d4\u10d1\u10da\u10d8\u10e1 \u10e1\u10d0\u10ee\u10d4\u10da\u10d8" - }, - "description": "\u10d2\u10d7\u10ee\u10dd\u10d5\u10d7 \u10e8\u10d4\u10d8\u10e7\u10d5\u10d0\u10dc\u10dd\u10d7", - "title": "\u10d3\u10d0\u10e3\u10d9\u10d0\u10d5\u10e8\u10d8\u10e0\u10d3\u10d8\u10d7 Smile-\u10e1" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/ko.json b/homeassistant/components/plugwise/translations/ko.json index 7d856d1fe28..5873415b066 100644 --- a/homeassistant/components/plugwise/translations/ko.json +++ b/homeassistant/components/plugwise/translations/ko.json @@ -8,34 +8,10 @@ "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, - "flow_title": "Smile: {name}", "step": { "user": { - "data": { - "flow_type": "\uc5f0\uacb0 \uc720\ud615" - }, "description": "\uc81c\ud488:", "title": "Plugwise \uc720\ud615" - }, - "user_gateway": { - "data": { - "host": "IP \uc8fc\uc18c", - "password": "Smile ID", - "port": "\ud3ec\ud2b8", - "username": "Smile \uc0ac\uc6a9\uc790 \uc774\ub984" - }, - "description": "\uc785\ub825\ud574\uc8fc\uc138\uc694", - "title": "Smile\uc5d0 \uc5f0\uacb0\ud558\uae30" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\uc2a4\uce94 \uac04\uaca9 (\ucd08)" - }, - "description": "Plugwise \uc635\uc158 \uc870\uc815\ud558\uae30" } } } diff --git a/homeassistant/components/plugwise/translations/lb.json b/homeassistant/components/plugwise/translations/lb.json index a3618bc911e..8c160412faa 100644 --- a/homeassistant/components/plugwise/translations/lb.json +++ b/homeassistant/components/plugwise/translations/lb.json @@ -8,33 +8,10 @@ "invalid_auth": "Ong\u00eblteg Authentifikatioun", "unknown": "Onerwaarte Feeler" }, - "flow_title": "Smile: {name}", "step": { "user": { - "data": { - "flow_type": "Typ vun der Verbindung" - }, "description": "Produkt:", "title": "Typ vu Plugwise" - }, - "user_gateway": { - "data": { - "host": "IP Adress", - "password": "Smile ID", - "port": "Port", - "username": "Smile Benotzernumm" - }, - "title": "Mam Smile verbannen" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Scan Intervall (sekonnen)" - }, - "description": "Plugwise Optioune \u00e4nneren" } } } diff --git a/homeassistant/components/plugwise/translations/nl.json b/homeassistant/components/plugwise/translations/nl.json index 14d25d6716e..b6417aed1ae 100644 --- a/homeassistant/components/plugwise/translations/nl.json +++ b/homeassistant/components/plugwise/translations/nl.json @@ -9,11 +9,9 @@ "invalid_setup": "Voeg je Adam toe in plaats van je Anna, zie de Home Assistant Plugwise integratiedocumentatie voor meer informatie", "unknown": "Onverwachte fout" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Verbindingstype", "host": "IP-adres", "password": "Smile-ID", "port": "Poort", @@ -21,26 +19,6 @@ }, "description": "Product:", "title": "Plugwise type" - }, - "user_gateway": { - "data": { - "host": "IP-adres", - "password": "Smile-ID", - "port": "Poort", - "username": "Smile gebruikersnaam" - }, - "description": "Voer in", - "title": "Maak verbinding met de Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Scaninterval (seconden)" - }, - "description": "Plugwise opties aanpassen" } } } diff --git a/homeassistant/components/plugwise/translations/no.json b/homeassistant/components/plugwise/translations/no.json index ad95ab8e4ee..7d2b4628b86 100644 --- a/homeassistant/components/plugwise/translations/no.json +++ b/homeassistant/components/plugwise/translations/no.json @@ -10,11 +10,9 @@ "invalid_setup": "Legg til din Adam i stedet for din Anna, se integrasjonsdokumentasjonen for Home Assistant Plugwise for mer informasjon", "unknown": "Uventet feil" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Tilkoblingstype", "host": "IP adresse", "password": "Smile ID", "port": "Port", @@ -22,26 +20,6 @@ }, "description": "Vennligst skriv inn", "title": "Koble til Smile" - }, - "user_gateway": { - "data": { - "host": "IP adresse", - "password": "Smile ID", - "port": "Port", - "username": "Smile brukernavn" - }, - "description": "Vennligst skriv inn", - "title": "Koble til Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Skanneintervall (sekunder)" - }, - "description": "Juster Plugwise-alternativer" } } } diff --git a/homeassistant/components/plugwise/translations/pl.json b/homeassistant/components/plugwise/translations/pl.json index 8de8da8e4e9..cfbd6009a37 100644 --- a/homeassistant/components/plugwise/translations/pl.json +++ b/homeassistant/components/plugwise/translations/pl.json @@ -10,11 +10,9 @@ "invalid_setup": "Dodaj urz\u0105dzenie Adam zamiast Anna. Zobacz dokumentacj\u0119 integracji Plugwise dla Home Assistant, aby uzyska\u0107 wi\u0119cej informacji.", "unknown": "Nieoczekiwany b\u0142\u0105d" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Typ po\u0142\u0105czenia", "host": "Adres IP", "password": "Identyfikator Smile", "port": "Port", @@ -22,26 +20,6 @@ }, "description": "Wprowad\u017a:", "title": "Po\u0142\u0105czenie ze Smile" - }, - "user_gateway": { - "data": { - "host": "Adres IP", - "password": "Identyfikator Smile", - "port": "Port", - "username": "Nazwa u\u017cytkownika" - }, - "description": "Wprowad\u017a:", - "title": "Po\u0142\u0105czenie ze Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Cz\u0119stotliwo\u015b\u0107 skanowania (w sekundach)" - }, - "description": "Dostosowywanie opcji Plugwise" } } } diff --git a/homeassistant/components/plugwise/translations/pt-BR.json b/homeassistant/components/plugwise/translations/pt-BR.json index 5f667f8d6ff..ef75a1ce686 100644 --- a/homeassistant/components/plugwise/translations/pt-BR.json +++ b/homeassistant/components/plugwise/translations/pt-BR.json @@ -10,11 +10,9 @@ "invalid_setup": "Adicione seu Adam em vez de sua Anna, consulte a documenta\u00e7\u00e3o de integra\u00e7\u00e3o do Home Assistant Plugwise para obter mais informa\u00e7\u00f5es", "unknown": "Erro inesperado" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Tipo de conex\u00e3o", "host": "Endere\u00e7o IP", "password": "ID do Smile", "port": "Porta", @@ -22,26 +20,6 @@ }, "description": "Por favor, insira", "title": "Conecte-se ao Smile" - }, - "user_gateway": { - "data": { - "host": "Endere\u00e7o IP", - "password": "ID do Smile", - "port": "Porta", - "username": "Nome de usu\u00e1rio Smile" - }, - "description": "Por favor, insira", - "title": "Conecte-se ao Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Intervalo de escaneamento (segundos)" - }, - "description": "Ajustar as op\u00e7\u00f5es Plugwise" } } } diff --git a/homeassistant/components/plugwise/translations/pt.json b/homeassistant/components/plugwise/translations/pt.json index dd40927a7c7..3481b1de025 100644 --- a/homeassistant/components/plugwise/translations/pt.json +++ b/homeassistant/components/plugwise/translations/pt.json @@ -7,23 +7,6 @@ "cannot_connect": "Falha na liga\u00e7\u00e3o", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" - }, - "step": { - "user_gateway": { - "data": { - "host": "Endere\u00e7o IP", - "port": "Porta" - } - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frequ\u00eancia de atualiza\u00e7\u00e3o (segundos)" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/ru.json b/homeassistant/components/plugwise/translations/ru.json index a6a31b7b63a..d1a631af4bd 100644 --- a/homeassistant/components/plugwise/translations/ru.json +++ b/homeassistant/components/plugwise/translations/ru.json @@ -10,11 +10,9 @@ "invalid_setup": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 Adam \u0432\u043c\u0435\u0441\u0442\u043e Anna. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439 \u043f\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 Plugwise \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "\u0422\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", "host": "IP-\u0430\u0434\u0440\u0435\u0441", "password": "Smile ID", "port": "\u041f\u043e\u0440\u0442", @@ -22,26 +20,6 @@ }, "description": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435", "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a Smile" - }, - "user_gateway": { - "data": { - "host": "IP-\u0430\u0434\u0440\u0435\u0441", - "password": "Smile ID", - "port": "\u041f\u043e\u0440\u0442", - "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f Smile" - }, - "description": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435:", - "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)" - }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Plugwise" } } } diff --git a/homeassistant/components/plugwise/translations/sk.json b/homeassistant/components/plugwise/translations/sk.json index b700b57b22c..b8437f98a1e 100644 --- a/homeassistant/components/plugwise/translations/sk.json +++ b/homeassistant/components/plugwise/translations/sk.json @@ -3,18 +3,11 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, - "flow_title": "{name}", "step": { "user": { "data": { "host": "IP adresa" } - }, - "user_gateway": { - "data": { - "host": "IP adresa", - "port": "Port" - } } } } diff --git a/homeassistant/components/plugwise/translations/sv.json b/homeassistant/components/plugwise/translations/sv.json index 379e1eb8a8c..6829648b9fb 100644 --- a/homeassistant/components/plugwise/translations/sv.json +++ b/homeassistant/components/plugwise/translations/sv.json @@ -10,11 +10,9 @@ "invalid_setup": "L\u00e4gg till din Adam ist\u00e4llet f\u00f6r din Anna, se Home Assistant Plugwise integrationsdokumentation f\u00f6r mer information", "unknown": "Ov\u00e4ntat fel" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Anslutningstyp", "host": "IP-adress", "password": "Smile ID", "port": "Port", @@ -22,26 +20,6 @@ }, "description": "Ange", "title": "Anslut till leendet" - }, - "user_gateway": { - "data": { - "host": "IP address", - "password": "Smile ID", - "port": "Port", - "username": "Smile Anv\u00e4ndarnamn" - }, - "description": "V\u00e4nligen ange:", - "title": "Anslut till leendet" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Skanningsintervall (sekunder)" - }, - "description": "Justera Plugwise-alternativ" } } } diff --git a/homeassistant/components/plugwise/translations/tr.json b/homeassistant/components/plugwise/translations/tr.json index 41f52761dbf..fb14d8da2d8 100644 --- a/homeassistant/components/plugwise/translations/tr.json +++ b/homeassistant/components/plugwise/translations/tr.json @@ -10,11 +10,9 @@ "invalid_setup": "Anna'n\u0131z yerine Adam'\u0131n\u0131z\u0131 ekleyin, daha fazla bilgi i\u00e7in Home Assistant Plugwise entegrasyon belgelerine bak\u0131n", "unknown": "Beklenmeyen hata" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "Ba\u011flant\u0131 t\u00fcr\u00fc", "host": "IP Adresi", "password": "Smile Kimli\u011fi", "port": "Port", @@ -22,26 +20,6 @@ }, "description": "L\u00fctfen girin", "title": "Smile'a Ba\u011flan\u0131n" - }, - "user_gateway": { - "data": { - "host": "\u0130p Adresi", - "password": "G\u00fcl\u00fcmseme Kimli\u011fi", - "port": "Port", - "username": "Smile Kullan\u0131c\u0131 Ad\u0131" - }, - "description": "L\u00fctfen girin", - "title": "Smile'a Ba\u011flan\u0131n" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Tarama Aral\u0131\u011f\u0131 (saniye)" - }, - "description": "Plugwise Se\u00e7eneklerini Ayarlay\u0131n" } } } diff --git a/homeassistant/components/plugwise/translations/uk.json b/homeassistant/components/plugwise/translations/uk.json index 6c6f54612b1..ac62753459b 100644 --- a/homeassistant/components/plugwise/translations/uk.json +++ b/homeassistant/components/plugwise/translations/uk.json @@ -8,34 +8,10 @@ "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f.", "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" }, - "flow_title": "Smile: {name}", "step": { "user": { - "data": { - "flow_type": "\u0422\u0438\u043f \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f" - }, "description": "\u041f\u0440\u043e\u0434\u0443\u043a\u0442:", "title": "\u0422\u0438\u043f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e Plugwise" - }, - "user_gateway": { - "data": { - "host": "IP-\u0430\u0434\u0440\u0435\u0441\u0430", - "password": "Smile ID", - "port": "\u041f\u043e\u0440\u0442", - "username": "\u041b\u043e\u0433\u0456\u043d Smile" - }, - "description": "\u0411\u0443\u0434\u044c \u043b\u0430\u0441\u043a\u0430, \u0432\u0432\u0435\u0434\u0456\u0442\u044c:", - "title": "\u041f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f \u0434\u043e Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u0406\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0441\u043a\u0430\u043d\u0443\u0432\u0430\u043d\u043d\u044f (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)" - }, - "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f Plugwise" } } } diff --git a/homeassistant/components/plugwise/translations/zh-Hans.json b/homeassistant/components/plugwise/translations/zh-Hans.json deleted file mode 100644 index 80c129af78d..00000000000 --- a/homeassistant/components/plugwise/translations/zh-Hans.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "config": { - "step": { - "user_gateway": { - "data": { - "host": "IP\u5730\u5740", - "port": "\u7aef\u53e3" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/zh-Hant.json b/homeassistant/components/plugwise/translations/zh-Hant.json index 2ea2c4b09ba..7e43aaffbb0 100644 --- a/homeassistant/components/plugwise/translations/zh-Hant.json +++ b/homeassistant/components/plugwise/translations/zh-Hant.json @@ -10,11 +10,9 @@ "invalid_setup": "\u65b0\u589e Adam \u800c\u975e Anna\u3001\u8acb\u53c3\u95b1 Home Assistant Plugwise \u6574\u5408\u8aaa\u660e\u6587\u4ef6\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u6599", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, - "flow_title": "{name}", "step": { "user": { "data": { - "flow_type": "\u9023\u7dda\u985e\u5225", "host": "IP \u4f4d\u5740", "password": "Smile ID", "port": "\u901a\u8a0a\u57e0", @@ -22,26 +20,6 @@ }, "description": "\u8acb\u8f38\u5165", "title": "\u9023\u7dda\u81f3 Smile" - }, - "user_gateway": { - "data": { - "host": "IP \u4f4d\u5740", - "password": "Smile ID", - "port": "\u901a\u8a0a\u57e0", - "username": "Smile \u4f7f\u7528\u8005\u540d\u7a31" - }, - "description": "\u8acb\u8f38\u5165\uff1a", - "title": "\u9023\u7dda\u81f3 Smile" - } - } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u6383\u63cf\u9593\u8ddd\uff08\u79d2\uff09" - }, - "description": "\u8abf\u6574 Plugwise \u9078\u9805" } } } diff --git a/homeassistant/components/pushover/translations/ca.json b/homeassistant/components/pushover/translations/ca.json index cbfc95c86ba..5a1beef3f9a 100644 --- a/homeassistant/components/pushover/translations/ca.json +++ b/homeassistant/components/pushover/translations/ca.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "La configuraci\u00f3 de Pushover mitjan\u00e7ant YAML s'eliminar\u00e0 de Home Assistant.\n\nLa configuraci\u00f3 YAML existent s'ha importat autom\u00e0ticament a la interf\u00edcie d'usuari.\n\nElimina la configuraci\u00f3 YAML de Pushover del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", - "title": "La configuraci\u00f3 YAML de Pushover est\u00e0 sent eliminada" - }, "removed_yaml": { "description": "La configuraci\u00f3 de Pushover mitjan\u00e7ant YAML s'ha eliminat de Home Assistant.\n\nHome Assistant ja no utilitza la configuraci\u00f3 YAML existent.\n\nElimina la configuraci\u00f3 YAML de Pushover del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", "title": "La configuraci\u00f3 YAML de Pushover s'ha eliminat" diff --git a/homeassistant/components/pushover/translations/de.json b/homeassistant/components/pushover/translations/de.json index 1a99ef663fa..4dbb017e6d3 100644 --- a/homeassistant/components/pushover/translations/de.json +++ b/homeassistant/components/pushover/translations/de.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Das Konfigurieren von Pushover mit YAML wird entfernt. \n\nDeine vorhandene YAML-Konfiguration wurde automatisch in die Benutzeroberfl\u00e4che importiert. \n\nEntferne die Pushover-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Pushover-YAML-Konfiguration wird entfernt" - }, "removed_yaml": { "description": "Das Konfigurieren von Pushover mit YAML wurde entfernt. \n\nDeine vorhandene YAML-Konfiguration wird von Home Assistant nicht verwendet. \n\nEntferne die Pushover-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", "title": "Die Pushover-YAML-Konfiguration wurde entfernt" diff --git a/homeassistant/components/pushover/translations/el.json b/homeassistant/components/pushover/translations/el.json index 433ceaaddb6..620383bfc67 100644 --- a/homeassistant/components/pushover/translations/el.json +++ b/homeassistant/components/pushover/translations/el.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Pushover \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03b5\u03b9\u03c3\u03b1\u03c7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Pushover YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", - "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Pushover YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" - }, "removed_yaml": { "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Pushover \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Pushover YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Pushover YAML \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c6\u03b1\u03b9\u03c1\u03b5\u03b8\u03b5\u03af" diff --git a/homeassistant/components/pushover/translations/en.json b/homeassistant/components/pushover/translations/en.json index 4a126782dda..c16b5d285d0 100644 --- a/homeassistant/components/pushover/translations/en.json +++ b/homeassistant/components/pushover/translations/en.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Configuring Pushover using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Pushover YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", - "title": "The Pushover YAML configuration is being removed" - }, "removed_yaml": { "description": "Configuring Pushover using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the Pushover YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", "title": "The Pushover YAML configuration has been removed" diff --git a/homeassistant/components/pushover/translations/es.json b/homeassistant/components/pushover/translations/es.json index 418fe68c28e..e7958d06eee 100644 --- a/homeassistant/components/pushover/translations/es.json +++ b/homeassistant/components/pushover/translations/es.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Se va a eliminar la configuraci\u00f3n de Pushover mediante YAML. \n\nTu configuraci\u00f3n YAML existente se ha importado a la IU autom\u00e1ticamente. \n\nElimina la configuraci\u00f3n YAML de Pushover de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", - "title": "Se va a eliminar la configuraci\u00f3n YAML de Pushover" - }, "removed_yaml": { "description": "Se ha eliminado la configuraci\u00f3n de Pushover mediante YAML. \n\nHome Assistant no utiliza tu configuraci\u00f3n YAML existente. \n\nElimina la configuraci\u00f3n Pushover YAML de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", "title": "Se ha eliminado la configuraci\u00f3n YAML de Pushover" diff --git a/homeassistant/components/pushover/translations/et.json b/homeassistant/components/pushover/translations/et.json index 7ae9c0abbf1..28c721f19b9 100644 --- a/homeassistant/components/pushover/translations/et.json +++ b/homeassistant/components/pushover/translations/et.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Pushoveri seadistamine YAML-i abil eemaldatakse.\n\nTeie olemasolev YAML-i konfiguratsioon imporditakse kasutajaliidesesse automaatselt.\n\nEemaldage failist configuration.yaml-i pushover-konfiguratsioon ja taask\u00e4ivitage selle probleemi lahendamiseks Home Assistant.", - "title": "Pushover YAML konfiguratsioon eemaldatakse" - }, "removed_yaml": { "description": "Pushoveri konfigureerimine YAML-i abil on eemaldatud. \n\n Koduassistent ei kasuta teie olemasolevat YAML-i konfiguratsiooni. \n\n Selle probleemi lahendamiseks eemaldage Pushoveri YAML-i konfiguratsioon failist configuration.yaml ja taask\u00e4ivitage Home Assistant.", "title": "Pushoveri YAML-i konfiguratsioon on eemaldatud" diff --git a/homeassistant/components/pushover/translations/fr.json b/homeassistant/components/pushover/translations/fr.json index 2982b17064c..a8d7a213d64 100644 --- a/homeassistant/components/pushover/translations/fr.json +++ b/homeassistant/components/pushover/translations/fr.json @@ -24,10 +24,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "title": "La configuration YAML pour Pushover sera bient\u00f4t supprim\u00e9e" - } } } \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/hu.json b/homeassistant/components/pushover/translations/hu.json index aa7624eca0b..d517f037b3f 100644 --- a/homeassistant/components/pushover/translations/hu.json +++ b/homeassistant/components/pushover/translations/hu.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "A Pushover konfigur\u00e1l\u00e1sa YAML haszn\u00e1lat\u00e1val elt\u00e1vol\u00edt\u00e1sra ker\u00fcl.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3 automatikusan import\u00e1l\u00e1sra ker\u00fclt a felhaszn\u00e1l\u00f3i fel\u00fcletre.\n\nA hiba kijav\u00edt\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a Pushover YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", - "title": "A Pushover YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" - }, "removed_yaml": { "description": "A Pushover YAML haszn\u00e1lat\u00e1val t\u00f6rt\u00e9n\u0151 konfigur\u00e1l\u00e1sa elt\u00e1vol\u00edt\u00e1sra ker\u00fclt.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3j\u00e1t a Home Assistant nem haszn\u00e1lja.\n\nA hiba kijav\u00edt\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a Pushover YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", "title": "A Pushover YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fclt" diff --git a/homeassistant/components/pushover/translations/id.json b/homeassistant/components/pushover/translations/id.json index b7aa1d9fd33..5643fd5aa3b 100644 --- a/homeassistant/components/pushover/translations/id.json +++ b/homeassistant/components/pushover/translations/id.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Proses konfigurasi Integrasi Pushover lewat YAML dalam proses penghapusan.\n\nKonfigurasi YAML yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML Integrasi Pushover dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", - "title": "Konfigurasi YAML Integrasi Pushover dalam proses penghapusan" - }, "removed_yaml": { "description": "Proses konfigurasi Integrasi Pushover lewat YAML telah dihapus.\n\nKonfigurasi YAML yang ada tidak digunakan oleh Home Assistant.\n\nHapus konfigurasi YAML Pushover dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", "title": "Konfigurasi YAML Integrasi Pushover telah dihapus" diff --git a/homeassistant/components/pushover/translations/it.json b/homeassistant/components/pushover/translations/it.json index 1fe83629c68..3ce54d1c6d5 100644 --- a/homeassistant/components/pushover/translations/it.json +++ b/homeassistant/components/pushover/translations/it.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "La configurazione di Pushover tramite YAML sar\u00e0 rimossa.\n\nLa configurazione YAML esistente \u00e8 stata importata automaticamente nell'interfaccia utente.\n\nRimuovi la configurazione YAML di Pushover dal file configuration.yaml e riavvia Home Assistant per risolvere questo problema.", - "title": "La configurazione YAML di Pushover sar\u00e0 rimossa" - }, "removed_yaml": { "description": "La configurazione di Pushover tramite YAML \u00e8 stata rimossa.\n\nLa configurazione YAML esistente non sar\u00e0 utilizzata da Home Assistant.\n\nRimuovere la configurazione YAML di Pushover dal file configuration.yaml e riavviare Home Assistant per risolvere il problema.", "title": "La configurazione YAML di Pushover \u00e8 stata rimossa" diff --git a/homeassistant/components/pushover/translations/ja.json b/homeassistant/components/pushover/translations/ja.json index 9a229d5d08a..af5eb3eda52 100644 --- a/homeassistant/components/pushover/translations/ja.json +++ b/homeassistant/components/pushover/translations/ja.json @@ -24,11 +24,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Pushover\u306eYAML\u3092\u4f7f\u7528\u3057\u305f\u8a2d\u5b9a\u306f\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002\n\n\u306a\u304a\u3001\u65e2\u5b58\u306eYAML\u8a2d\u5b9a\u306f\u3001UI\u306b\u81ea\u52d5\u7684\u306b\u30a4\u30f3\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059\u3002\n\n\u3053\u306e\u554f\u984c\u3092\u89e3\u6c7a\u3059\u308b\u306b\u306f\u3001configuration.yaml\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u3001Pushover\u306eYAML\u8a2d\u5b9a\u3092\u524a\u9664\u3057\u3001Home Assistant\u3092\u518d\u8d77\u52d5\u3057\u307e\u3059\u3002", - "title": "Pushover YAML\u306e\u8a2d\u5b9a\u306f\u524a\u9664\u3055\u308c\u3066\u3044\u307e\u3059" - } } } \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/no.json b/homeassistant/components/pushover/translations/no.json index dd8713b73a4..141e0326c8c 100644 --- a/homeassistant/components/pushover/translations/no.json +++ b/homeassistant/components/pushover/translations/no.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Konfigurering av Pushover med YAML blir fjernet. \n\n Din eksisterende YAML-konfigurasjon har blitt importert til brukergrensesnittet automatisk. \n\n Fjern Pushover YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", - "title": "Pushover YAML-konfigurasjonen blir fjernet" - }, "removed_yaml": { "description": "Konfigurering av Pushover med YAML er fjernet. \n\n Din eksisterende YAML-konfigurasjon brukes ikke av Home Assistant. \n\n Fjern Pushover YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", "title": "Pushover YAML-konfigurasjonen er fjernet" diff --git a/homeassistant/components/pushover/translations/pl.json b/homeassistant/components/pushover/translations/pl.json index 46b3596dd1c..1066a5c725a 100644 --- a/homeassistant/components/pushover/translations/pl.json +++ b/homeassistant/components/pushover/translations/pl.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Konfiguracja Pushover przy u\u017cyciu YAML zostanie usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML zosta\u0142a automatycznie zaimportowana do interfejsu u\u017cytkownika. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", - "title": "Konfiguracja YAML dla Pushover zostanie usuni\u0119ta" - }, "removed_yaml": { "description": "Konfiguracja Pushover za pomoc\u0105 YAML zosta\u0142a usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML nie jest u\u017cywana przez Home Assistant. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", "title": "Konfiguracja YAML dla Pushover zosta\u0142a usuni\u0119ta" diff --git a/homeassistant/components/pushover/translations/pt-BR.json b/homeassistant/components/pushover/translations/pt-BR.json index 6e2f6412602..0b11e4ea2a6 100644 --- a/homeassistant/components/pushover/translations/pt-BR.json +++ b/homeassistant/components/pushover/translations/pt-BR.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "A configura\u00e7\u00e3o do Pushover usando YAML est\u00e1 sendo removida. \n\n Sua configura\u00e7\u00e3o YAML existente foi importada para a interface do usu\u00e1rio automaticamente. \n\n Remova a configura\u00e7\u00e3o Pushover YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", - "title": "A configura\u00e7\u00e3o do Pushover YAML est\u00e1 sendo removida" - }, "removed_yaml": { "description": "A configura\u00e7\u00e3o do Pushover usando YAML foi removida. \n\n Sua configura\u00e7\u00e3o YAML existente n\u00e3o \u00e9 usada pelo Home Assistant. \n\n Remova a configura\u00e7\u00e3o Pushover YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", "title": "A configura\u00e7\u00e3o YAML do Pushover foi removida" diff --git a/homeassistant/components/pushover/translations/pt.json b/homeassistant/components/pushover/translations/pt.json index 519ac06a01f..9b06668589f 100644 --- a/homeassistant/components/pushover/translations/pt.json +++ b/homeassistant/components/pushover/translations/pt.json @@ -7,11 +7,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "A configura\u00e7\u00e3o do Pushover usando YAML est\u00e1 sendo removida. \n\n Sua configura\u00e7\u00e3o YAML existente foi importada para a interface do usu\u00e1rio automaticamente. \n\n Remova a configura\u00e7\u00e3o Pushover YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", - "title": "A configura\u00e7\u00e3o do Pushover YAML est\u00e1 sendo removida" - } } } \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/ru.json b/homeassistant/components/pushover/translations/ru.json index 55fae74e217..6a4095e67b4 100644 --- a/homeassistant/components/pushover/translations/ru.json +++ b/homeassistant/components/pushover/translations/ru.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Pushover \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", - "title": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Pushover \u0447\u0435\u0440\u0435\u0437 YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" - }, "removed_yaml": { "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \"Pushover\" \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f YAML \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Home Assistant.\n\n\u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", "title": "\u0423\u0434\u0430\u043b\u0435\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Pushover \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML" diff --git a/homeassistant/components/pushover/translations/sv.json b/homeassistant/components/pushover/translations/sv.json index 5e85503bb4b..e6bfee469c6 100644 --- a/homeassistant/components/pushover/translations/sv.json +++ b/homeassistant/components/pushover/translations/sv.json @@ -24,11 +24,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "Konfigurering av Pushover med YAML tas bort. \n\n Din befintliga YAML-konfiguration har automatiskt importerats till anv\u00e4ndargr\u00e4nssnittet. \n\n Ta bort Pushover YAML-konfigurationen fr\u00e5n filen configuration.yaml och starta om Home Assistant f\u00f6r att \u00e5tg\u00e4rda problemet.", - "title": "Pushover YAML-konfigurationen tas bort" - } } } \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/tr.json b/homeassistant/components/pushover/translations/tr.json index 5ab133b70a9..2e5ef735b42 100644 --- a/homeassistant/components/pushover/translations/tr.json +++ b/homeassistant/components/pushover/translations/tr.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "YAML kullanarak Pushover'\u0131 yap\u0131land\u0131rma kald\u0131r\u0131l\u0131yor. \n\n Mevcut YAML yap\u0131land\u0131rman\u0131z otomatik olarak kullan\u0131c\u0131 aray\u00fcz\u00fcne aktar\u0131ld\u0131. \n\n Pushover YAML yap\u0131land\u0131rmas\u0131n\u0131 configuration.yaml dosyan\u0131zdan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", - "title": "Pushover YAML yap\u0131land\u0131rmas\u0131 kald\u0131r\u0131l\u0131yor" - }, "removed_yaml": { "description": "Pushover'i YAML kullanarak yap\u0131land\u0131rma kald\u0131r\u0131ld\u0131.\n\nMevcut YAML yap\u0131land\u0131rman\u0131z Home Assistant taraf\u0131ndan kullan\u0131lmaz.\n\nYAML yap\u0131land\u0131rmas\u0131n\u0131 configuration.yaml dosyan\u0131zdan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", "title": "Pushover YAML yap\u0131land\u0131rmas\u0131 kald\u0131r\u0131l\u0131yor" diff --git a/homeassistant/components/pushover/translations/zh-Hant.json b/homeassistant/components/pushover/translations/zh-Hant.json index 6294000ccbb..2051bc6ea94 100644 --- a/homeassistant/components/pushover/translations/zh-Hant.json +++ b/homeassistant/components/pushover/translations/zh-Hant.json @@ -26,10 +26,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a\u7684 Pushover \u5373\u5c07\u9032\u884c\u79fb\u9664\u3002\n\n\u65e2\u6709\u7684 YAML \u8a2d\u5b9a\u5c07\u81ea\u52d5\u532f\u5165\u81f3 UI \u5167\u3002\n\n\u8acb\u65bc configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Pushover YAML \u8a2d\u5b9a\u4e26\u91cd\u65b0\u555f\u52d5 Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", - "title": "Pushover YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" - }, "removed_yaml": { "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a Pushover \u7684\u529f\u80fd\u5373\u5c07\u79fb\u9664\u3002\n\nHome Assistant \u5c07\u4e0d\u518d\u4f7f\u7528\u73fe\u6709\u7684 YAML \u8a2d\u5b9a\u3002\n\n\u7531 configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Pushover YAML \u8a2d\u5b9a\u4e26\u91cd\u555f Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", "title": "Pushover YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" diff --git a/homeassistant/components/radarr/translations/bg.json b/homeassistant/components/radarr/translations/bg.json index 449bb233d01..4ceaa5793e1 100644 --- a/homeassistant/components/radarr/translations/bg.json +++ b/homeassistant/components/radarr/translations/bg.json @@ -26,9 +26,6 @@ "deprecated_yaml": { "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Radarr \u0441 \u043f\u043e\u043c\u043e\u0449\u0442\u0430 \u043d\u0430 YAML \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430.\n\n\u0412\u0430\u0448\u0430\u0442\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0430\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0432 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u041f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Radarr \u043e\u0442 \u0432\u0430\u0448\u0438\u044f \u0444\u0430\u0439\u043b configuration.yaml \u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant, \u0437\u0430 \u0434\u0430 \u043a\u043e\u0440\u0438\u0433\u0438\u0440\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.", "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Radarr \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430" - }, - "removed_attributes": { - "title": "\u041f\u0440\u043e\u043c\u0435\u043d\u0438 \u0432 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Radarr" } }, "options": { diff --git a/homeassistant/components/radarr/translations/ca.json b/homeassistant/components/radarr/translations/ca.json index c94cd1cd901..4cd6b930f7a 100644 --- a/homeassistant/components/radarr/translations/ca.json +++ b/homeassistant/components/radarr/translations/ca.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "La configuraci\u00f3 de Radarr mitjan\u00e7ant YAML s'eliminar\u00e0 de Home Assistant.\n\nLa configuraci\u00f3 YAML existent s'ha importat autom\u00e0ticament a la interf\u00edcie d'usuari.\n\nElimina la configuraci\u00f3 YAML de Radarr del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", "title": "La configuraci\u00f3 YAML de Radarr est\u00e0 sent eliminada" - }, - "removed_attributes": { - "description": "Per precauci\u00f3, s'han fet alguns canvis importants en el sensor recompte de pel\u00b7l\u00edcules.\n\nAquest sensor pot causar problemes amb bases de dades molt grans. Si encara vols utilitzar-lo, pots fer-ho. \n\nEls noms de pel\u00b7l\u00edcules ja no s'inclouen com a atributs al sensor pel\u00b7l\u00edcules. \n\nPropers s'ha eliminat. S'ha modernitzat com a elements de calendari. L'espai del disc ara es divideix en diferents sensors, un per a cada carpeta. \n\nL'estat i les comandes s'han eliminat perqu\u00e8 no sembla que tinguin un valor real per a les automatitzacions.", - "title": "Canvis a la integraci\u00f3 Radarr" } }, "options": { diff --git a/homeassistant/components/radarr/translations/de.json b/homeassistant/components/radarr/translations/de.json index 81aafd0a351..609af8288c6 100644 --- a/homeassistant/components/radarr/translations/de.json +++ b/homeassistant/components/radarr/translations/de.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "Die Konfiguration von Radarr mit YAML wird entfernt. \n\nDeine vorhandene YAML-Konfiguration wurde automatisch in die Benutzeroberfl\u00e4che importiert. \n\nEntferne die Radarr-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", "title": "Die Radarr-YAML-Konfiguration wird entfernt" - }, - "removed_attributes": { - "description": "Es wurden einige \u00c4nderungen vorgenommen, um den Filmz\u00e4hlsensor aus Vorsicht zu deaktivieren.\n\nDieser Sensor kann bei gro\u00dfen Datenbanken Probleme verursachen. Wenn du ihn dennoch verwenden m\u00f6chtest, kannst du dies tun.\n\nFilmnamen werden nicht mehr als Attribute in den Filmsensor aufgenommen.\n\nUpcoming wurde entfernt. Er wird modernisiert, so wie es bei Kalenderelementen sein sollte. Der Speicherplatz ist jetzt in verschiedene Sensoren aufgeteilt, einen f\u00fcr jeden Ordner.\n\nStatus und Befehle wurden entfernt, da sie f\u00fcr Automatisierungen keinen wirklichen Wert zu haben scheinen.", - "title": "\u00c4nderungen an der Radarr-Integration" } }, "options": { diff --git a/homeassistant/components/radarr/translations/el.json b/homeassistant/components/radarr/translations/el.json index f7eb031cba5..005f18e5e69 100644 --- a/homeassistant/components/radarr/translations/el.json +++ b/homeassistant/components/radarr/translations/el.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Radarr \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03b5\u03b9\u03c3\u03b1\u03c7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Radarr YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Radarr YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" - }, - "removed_attributes": { - "description": "\u0388\u03b3\u03b9\u03bd\u03b1\u03bd \u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b5\u03c2 \u03c3\u03b7\u03bc\u03b1\u03bd\u03c4\u03b9\u03ba\u03ad\u03c2 \u03b1\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 \u03c3\u03c4\u03b7\u03bd \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2 \u03c4\u03b1\u03b9\u03bd\u03b9\u03ce\u03bd \u03c7\u03c9\u03c1\u03af\u03c2 \u03c0\u03c1\u03bf\u03c3\u03bf\u03c7\u03ae. \n\n \u0391\u03c5\u03c4\u03cc\u03c2 \u03bf \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c0\u03c1\u03bf\u03ba\u03b1\u03bb\u03ad\u03c3\u03b5\u03b9 \u03c0\u03c1\u03bf\u03b2\u03bb\u03ae\u03bc\u03b1\u03c4\u03b1 \u03bc\u03b5 \u03c4\u03b5\u03c1\u03ac\u03c3\u03c4\u03b9\u03b5\u03c2 \u03b2\u03ac\u03c3\u03b5\u03b9\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd. \u0395\u03ac\u03bd \u03b5\u03be\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c4\u03bf \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5, \u03bc\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03c4\u03bf \u03ba\u03ac\u03bd\u03b5\u03c4\u03b5. \n\n \u03a4\u03b1 \u03bf\u03bd\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c4\u03b1\u03b9\u03bd\u03b9\u03ce\u03bd \u03b4\u03b5\u03bd \u03c0\u03b5\u03c1\u03b9\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03bf\u03bd\u03c4\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd \u03c9\u03c2 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03ac \u03c3\u03c4\u03bf\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03c4\u03b1\u03b9\u03bd\u03b9\u03ce\u03bd. \n\n \u03a4\u03bf \u03b5\u03c0\u03b5\u03c1\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c6\u03b1\u03b9\u03c1\u03b5\u03b8\u03b5\u03af. \u0395\u03ba\u03c3\u03c5\u03b3\u03c7\u03c1\u03bf\u03bd\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03cc\u03c0\u03c9\u03c2 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c4\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03b1 \u03c4\u03bf\u03c5 \u03b7\u03bc\u03b5\u03c1\u03bf\u03bb\u03bf\u03b3\u03af\u03bf\u03c5. \u039f \u03c7\u03ce\u03c1\u03bf\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03c3\u03ba\u03bf \u03c7\u03c9\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd \u03c3\u03b5 \u03b4\u03b9\u03b1\u03c6\u03bf\u03c1\u03b5\u03c4\u03b9\u03ba\u03bf\u03cd\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2, \u03ad\u03bd\u03b1\u03bd \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 \u03c6\u03ac\u03ba\u03b5\u03bb\u03bf. \n\n \u0397 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03bf\u03b9 \u03b5\u03bd\u03c4\u03bf\u03bb\u03ad\u03c2 \u03ad\u03c7\u03bf\u03c5\u03bd \u03b1\u03c6\u03b1\u03b9\u03c1\u03b5\u03b8\u03b5\u03af \u03ba\u03b1\u03b8\u03ce\u03c2 \u03b4\u03b5\u03bd \u03c6\u03b1\u03af\u03bd\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03ad\u03c7\u03bf\u03c5\u03bd \u03c0\u03c1\u03b1\u03b3\u03bc\u03b1\u03c4\u03b9\u03ba\u03ae \u03b1\u03be\u03af\u03b1 \u03b3\u03b9\u03b1 \u03c4\u03bf\u03c5\u03c2 \u03b1\u03c5\u03c4\u03bf\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03bf\u03cd\u03c2.", - "title": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 \u03c3\u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Radarr" } }, "options": { diff --git a/homeassistant/components/radarr/translations/en.json b/homeassistant/components/radarr/translations/en.json index 168c3cc2fe2..cba064fd429 100644 --- a/homeassistant/components/radarr/translations/en.json +++ b/homeassistant/components/radarr/translations/en.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "Configuring Radarr using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Radarr YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", "title": "The Radarr YAML configuration is being removed" - }, - "removed_attributes": { - "description": "Some breaking changes has been made in disabling the Movies count sensor out of caution.\n\nThis sensor can cause problems with massive databases. If you still wish to use it, you may do so.\n\nMovie names are no longer included as attributes in the movies sensor.\n\nUpcoming has been removed. It is being modernized as calendar items should be. Disk space is now split into different sensors, one for each folder.\n\nStatus and commands have been removed as they don't appear to have real value for automations.", - "title": "Changes to the Radarr integration" } }, "options": { diff --git a/homeassistant/components/radarr/translations/es.json b/homeassistant/components/radarr/translations/es.json index 5f88c7ef8f4..fe85efc0e20 100644 --- a/homeassistant/components/radarr/translations/es.json +++ b/homeassistant/components/radarr/translations/es.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "Se va a eliminar la configuraci\u00f3n de Radarr mediante YAML. \n\nTu configuraci\u00f3n YAML existente se ha importado a la IU autom\u00e1ticamente. \n\nElimina la configuraci\u00f3n YAML de Radarr de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", "title": "Se va a eliminar la configuraci\u00f3n YAML de Radarr" - }, - "removed_attributes": { - "description": "Se han realizado algunos cambios importantes al deshabilitar el sensor de conteo de pel\u00edculas por precauci\u00f3n. \n\nEste sensor puede causar problemas con bases de datos enormes. Si a\u00fan deseas utilizarlo, puedes hacerlo. \n\nLos nombres de las pel\u00edculas ya no se incluyen como atributos en el sensor de pel\u00edculas. \n\nPr\u00f3ximamente ha sido eliminado. Se est\u00e1 modernizando como deber\u00edan ser los elementos del calendario. El espacio en disco ahora se divide en diferentes sensores, uno para cada carpeta. \n\nEl estado y los comandos se han eliminado porque no parecen tener un valor real para las automatizaciones.", - "title": "Cambios en la integraci\u00f3n de Radarr" } }, "options": { diff --git a/homeassistant/components/radarr/translations/et.json b/homeassistant/components/radarr/translations/et.json index 0f91bc2f47b..175628112e8 100644 --- a/homeassistant/components/radarr/translations/et.json +++ b/homeassistant/components/radarr/translations/et.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "Radarri seadistamine YAML-i abil eemaldatakse.\n\nTeie olemasolev YAML-i konfiguratsioon imporditakse kasutajaliidesesse automaatselt.\n\nEemaldage failist configuration.yaml radarr YAML konfiguratsioon ja taask\u00e4ivitage selle probleemi lahendamiseks Home Assistant.", "title": "Radarr YAML-i konfiguratsiooni eemaldatakse" - }, - "removed_attributes": { - "description": "M\u00f5ned murrangulised muudatused on tehtud liikumiste arvuanduri v\u00e4ljal\u00fclitamisel ettevaatusabin\u00f5ude t\u00f5ttu.\n\nSee andur v\u00f5ib p\u00f5hjustada probleeme massiivsete andmebaaside puhul. Kui soovite seda siiski kasutada, v\u00f5ite seda teha.\n\nFilmide nimed ei ole enam filmide anduri atribuutidena lisatud.\n\nTulevikus on eemaldatud. Seda ajakohastatakse, nagu kalendrielemendid peaksid olema. Kettaruum on n\u00fc\u00fcd jagatud erinevateks anduriteks, \u00fcks iga kausta jaoks.\n\nStaatus ja k\u00e4sud on eemaldatud, kuna neil ei tundu olevat t\u00f5elist v\u00e4\u00e4rtust automaatika jaoks.", - "title": "Muudatused Radarri integratsioonis" } }, "options": { diff --git a/homeassistant/components/radarr/translations/fr.json b/homeassistant/components/radarr/translations/fr.json index 5f8d2a9f78d..9dbf28c5129 100644 --- a/homeassistant/components/radarr/translations/fr.json +++ b/homeassistant/components/radarr/translations/fr.json @@ -28,9 +28,6 @@ "issues": { "deprecated_yaml": { "title": "La configuration YAML pour Radarr sera bient\u00f4t supprim\u00e9e" - }, - "removed_attributes": { - "title": "Modifications apport\u00e9es \u00e0 l'int\u00e9gration Radarr" } }, "options": { diff --git a/homeassistant/components/radarr/translations/hu.json b/homeassistant/components/radarr/translations/hu.json index f00034f5f5f..cf5a08d3fec 100644 --- a/homeassistant/components/radarr/translations/hu.json +++ b/homeassistant/components/radarr/translations/hu.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "A Radarr YAML haszn\u00e1lat\u00e1val t\u00f6rt\u00e9n\u0151 konfigur\u00e1l\u00e1sa elt\u00e1vol\u00edt\u00e1sra ker\u00fcl.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3 automatikusan import\u00e1l\u00e1sra ker\u00fclt a felhaszn\u00e1l\u00f3i fel\u00fcletre.\n\nA hiba kijav\u00edt\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a Radarr YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", "title": "A Radarr YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" - }, - "removed_attributes": { - "description": "N\u00e9h\u00e1ny v\u00e1ltoztat\u00e1s t\u00f6rt\u00e9nt a Filmek sz\u00e1m\u00e1nak \u00e9rz\u00e9kel\u0151j\u00e9nek \u00f3vatoss\u00e1gb\u00f3l t\u00f6rt\u00e9n\u0151 letilt\u00e1s\u00e1ban.\n\nEz az \u00e9rz\u00e9kel\u0151 probl\u00e9m\u00e1kat okozhat hatalmas adatb\u00e1zisok eset\u00e9n. Ha tov\u00e1bbra is haszn\u00e1lni szeretn\u00e9, megteheti.\n\nA filmek nevei t\u00f6bb\u00e9 nem szerepelnek attrib\u00fatumk\u00e9nt a filmek \u00e9rz\u00e9kel\u0151ben.\n\nAz Upcoming elt\u00e1vol\u00edt\u00e1sra ker\u00fclt. Korszer\u0171s\u00edt\u00e9sre ker\u00fcl, ahogyan a napt\u00e1relemeknek is kell. A lemezter\u00fclet mostant\u00f3l k\u00fcl\u00f6nb\u00f6z\u0151 \u00e9rz\u00e9kel\u0151kre van felosztva, egy-egy mapp\u00e1hoz.\n\nA st\u00e1tusz \u00e9s a parancsok elt\u00e1vol\u00edt\u00e1sra ker\u00fcltek, mivel \u00fagy t\u0171nik, hogy nincs val\u00f3di \u00e9rt\u00e9k\u00fck az automatiz\u00e1l\u00e1sok sz\u00e1m\u00e1ra.", - "title": "A Radarr-integr\u00e1ci\u00f3 v\u00e1ltoz\u00e1sai" } }, "options": { diff --git a/homeassistant/components/radarr/translations/id.json b/homeassistant/components/radarr/translations/id.json index 95f19f7136e..108dcec98c4 100644 --- a/homeassistant/components/radarr/translations/id.json +++ b/homeassistant/components/radarr/translations/id.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "Proses konfigurasi Integrasi Radarr lewat YAML dalam proses penghapusan.\n\nKonfigurasi YAML yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML Integrasi Radarr dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", "title": "Konfigurasi YAML Integrasi Radarr dalam proses penghapusan" - }, - "removed_attributes": { - "description": "Beberapa perubahan besar telah dilakukan dalam menonaktifkan sensor hitungan Film dengan alasan kehati-hatian.\n\nSensor ini bisa menyebabkan masalah dengan database yang sangat besar. Jika masih ingin menggunakannya, Anda dapat melakukannya.\n\nNama film tidak lagi disertakan sebagai atribut dalam sensor film.\n\nItem \"Yang akan datang\" telah dihapus. Sensor ini sedang dimodernisasi sebagaimana layaknya item kalender. Ruang disk sekarang dipecah ke dalam sensor yang berbeda, satu untuk setiap folder.\n\nStatus dan perintah telah dihapus karena tampaknya tidak membawa nilai dalam otomasi.", - "title": "Perubahan pada integrasi Radarr" } }, "options": { diff --git a/homeassistant/components/radarr/translations/it.json b/homeassistant/components/radarr/translations/it.json index afc64b6bc8e..d6a1445493d 100644 --- a/homeassistant/components/radarr/translations/it.json +++ b/homeassistant/components/radarr/translations/it.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "La configurazione di Radarr tramite YAML \u00e8 stata rimossa.\n\nLa configurazione YAML esistente \u00e8 stata importata automaticamente nell'interfaccia utente.\n\nRimuovere la configurazione YAML di Radarr dal file configuration.yaml e riavviare Home Assistant per risolvere il problema.", "title": "La configurazione YAML di Radarr \u00e8 stata rimossa" - }, - "removed_attributes": { - "description": "Sono state apportate alcune modifiche alla disabilitazione del sensore di conteggio dei filmati per prudenza.\n\nQuesto sensore pu\u00f2 causare problemi con database di grandi dimensioni. Se si desidera ancora utilizzarlo, \u00e8 possibile farlo.\n\nI nomi dei film non sono pi\u00f9 inclusi come attributi nel sensore dei film.\n\nLa voce Upcoming \u00e8 stata rimossa. \u00c8 stato modernizzato come dovrebbero essere gli elementi del calendario. Lo spazio su disco \u00e8 ora suddiviso in diversi sensori, uno per ogni cartella.\n\nStato e comandi sono stati rimossi perch\u00e9 non sembrano avere un valore reale per le automazioni.", - "title": "Modifiche all'integrazione Radarr" } }, "options": { diff --git a/homeassistant/components/radarr/translations/nl.json b/homeassistant/components/radarr/translations/nl.json index b4f1fcd7106..10df421d00e 100644 --- a/homeassistant/components/radarr/translations/nl.json +++ b/homeassistant/components/radarr/translations/nl.json @@ -26,9 +26,6 @@ "issues": { "deprecated_yaml": { "title": "De Radarr YAML-configuratie wordt verwijderd" - }, - "removed_attributes": { - "title": "Wijzigingen in de Radarr-integratie" } } } \ No newline at end of file diff --git a/homeassistant/components/radarr/translations/no.json b/homeassistant/components/radarr/translations/no.json index 4a86867a3fd..f94d72d610c 100644 --- a/homeassistant/components/radarr/translations/no.json +++ b/homeassistant/components/radarr/translations/no.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "Konfigurering av Radarr med YAML blir fjernet. \n\n Din eksisterende YAML-konfigurasjon har blitt importert til brukergrensesnittet automatisk. \n\n Fjern Radarr YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", "title": "Radarr YAML-konfigurasjonen blir fjernet" - }, - "removed_attributes": { - "description": "Noen bruddendringer er gjort for \u00e5 deaktivere filmtellingssensoren ut av forsiktighet. \n\n Denne sensoren kan for\u00e5rsake problemer med massive databaser. Hvis du fortsatt \u00f8nsker \u00e5 bruke den, kan du gj\u00f8re det. \n\n Filmnavn er ikke lenger inkludert som attributter i filmsensoren. \n\n Kommende er fjernet. Den moderniseres slik kalenderposter skal v\u00e6re. Diskplass er n\u00e5 delt inn i forskjellige sensorer, en for hver mappe. \n\n Status og kommandoer er fjernet da de ikke ser ut til \u00e5 ha reell verdi for automatisering.", - "title": "Endringer i Radarr-integrasjonen" } }, "options": { diff --git a/homeassistant/components/radarr/translations/pl.json b/homeassistant/components/radarr/translations/pl.json index ff7092eaff0..49d773258b9 100644 --- a/homeassistant/components/radarr/translations/pl.json +++ b/homeassistant/components/radarr/translations/pl.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "Konfiguracja Radarr przy u\u017cyciu YAML zostanie usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML zosta\u0142a automatycznie zaimportowana do interfejsu u\u017cytkownika. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", "title": "Konfiguracja YAML dla Radarr zostanie usuni\u0119ta" - }, - "removed_attributes": { - "description": "Z powodu ostro\u017cno\u015bci wprowadzono pewne prze\u0142omowe zmiany w wy\u0142\u0105czaniu sensora liczby film\u00f3w. \n\nTen sensor mo\u017ce powodowa\u0107 problemy z ogromnymi bazami danych. Je\u015bli nadal chcesz z niego korzysta\u0107, mo\u017cesz to zrobi\u0107. \n\n\"Nazwy film\u00f3w\" nie s\u0105 ju\u017c uwzgl\u0119dniane w atrybutach sensora film\u00f3w. \n\n\"Nadchodz\u0105ce\" zosta\u0142o usuni\u0119te. Jest unowocze\u015bniany tak, jak przysta\u0142o na kalendarz. \"Miejsce na dysku\" jest teraz podzielone na r\u00f3\u017cne sensory, po jednym dla ka\u017cdego folderu. \n\n\"Status\" i \"Polecenia\" zosta\u0142y usuni\u0119te, poniewa\u017c nie wydaj\u0105 si\u0119 mie\u0107 rzeczywistej warto\u015bci dla automatyzacji.", - "title": "Zmiany w integracji Radarr" } }, "options": { diff --git a/homeassistant/components/radarr/translations/pt-BR.json b/homeassistant/components/radarr/translations/pt-BR.json index 78b9078cb9f..544b38ec81a 100644 --- a/homeassistant/components/radarr/translations/pt-BR.json +++ b/homeassistant/components/radarr/translations/pt-BR.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "A configura\u00e7\u00e3o do Radarr usando YAML est\u00e1 sendo removida. \n\n Sua configura\u00e7\u00e3o YAML existente foi importada para a interface do usu\u00e1rio automaticamente. \n\n Remova a configura\u00e7\u00e3o YAML do Radarr do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", "title": "A configura\u00e7\u00e3o YAML do Radarr est\u00e1 sendo removida" - }, - "removed_attributes": { - "description": "Algumas mudan\u00e7as importantes foram feitas na desativa\u00e7\u00e3o do sensor de contagem de filmes por precau\u00e7\u00e3o. \n\n Este sensor pode causar problemas com bancos de dados massivos. Se voc\u00ea ainda deseja us\u00e1-lo, voc\u00ea pode faz\u00ea-lo. \n\n Os nomes dos filmes n\u00e3o s\u00e3o mais inclu\u00eddos como atributos no sensor de filmes. \n\n O pr\u00f3ximo foi removido. Ele est\u00e1 sendo modernizado como os itens do calend\u00e1rio devem ser. O espa\u00e7o em disco agora \u00e9 dividido em diferentes sensores, um para cada pasta. \n\n Status e comandos foram removidos, pois n\u00e3o parecem ter valor real para automa\u00e7\u00f5es.", - "title": "Mudan\u00e7as na integra\u00e7\u00e3o do Radarr" } }, "options": { diff --git a/homeassistant/components/radarr/translations/ru.json b/homeassistant/components/radarr/translations/ru.json index 46fd877aa3a..05b6ff2b2dc 100644 --- a/homeassistant/components/radarr/translations/ru.json +++ b/homeassistant/components/radarr/translations/ru.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Radarr \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", "title": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Radarr \u0447\u0435\u0440\u0435\u0437 YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" - }, - "removed_attributes": { - "description": "\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043c\u0435\u0440\u044b \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u043e\u0440\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0431\u044b\u043b \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d \u0441\u0435\u043d\u0441\u043e\u0440 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0444\u0438\u043b\u044c\u043c\u043e\u0432. \u042d\u0442\u043e\u0442 \u0441\u0435\u043d\u0441\u043e\u0440 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u043c\u0430\u0441\u0441\u0438\u0432\u043d\u044b\u043c\u0438 \u0431\u0430\u0437\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u043e \u043f\u0440\u0438 \u0436\u0435\u043b\u0430\u043d\u0438\u0438 \u0412\u044b \u0432\u0441\u0435 \u0435\u0449\u0451 \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e.\n\n\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0444\u0438\u043b\u044c\u043c\u043e\u0432 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0442\u0441\u044f \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0432 \u0441\u0435\u043d\u0441\u043e\u0440 \u0444\u0438\u043b\u044c\u043c\u043e\u0432.\n\n\u041f\u0440\u0435\u0434\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0431\u044b\u043b\u043e \u0443\u0434\u0430\u043b\u0435\u043d\u043e. \u041e\u043d\u043e \u043c\u043e\u0434\u0435\u0440\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043e, \u043a\u0430\u043a \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044f. \u0414\u0438\u0441\u043a\u043e\u0432\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u043e \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0435 \u0441\u0435\u043d\u0441\u043e\u0440\u044b, \u043f\u043e \u043e\u0434\u043d\u043e\u043c\u0443 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u043f\u0430\u043f\u043a\u0438.\n\n\u0421\u0442\u0430\u0442\u0443\u0441 \u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0431\u044b\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u044b, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d\u0438 \u043d\u0435 \u0438\u043c\u0435\u044e\u0442 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0446\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438.", - "title": "\u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 Radarr" } }, "options": { diff --git a/homeassistant/components/radarr/translations/sv.json b/homeassistant/components/radarr/translations/sv.json index a5f84833bd9..132fc4c4335 100644 --- a/homeassistant/components/radarr/translations/sv.json +++ b/homeassistant/components/radarr/translations/sv.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "Konfigurering av Radarr med YAML tas bort. \n\n Din befintliga YAML-konfiguration har automatiskt importerats till anv\u00e4ndargr\u00e4nssnittet. \n\n Ta bort Radarr YAML-konfigurationen fr\u00e5n filen configuration.yaml och starta om Home Assistant f\u00f6r att \u00e5tg\u00e4rda problemet.", "title": "Radarr YAML-konfigurationen tas bort" - }, - "removed_attributes": { - "description": "Vissa f\u00f6r\u00e4ndringar har gjorts f\u00f6r att inaktivera r\u00e4knesensorn f\u00f6r filmer av f\u00f6rsiktighet. \n\n Denna sensor kan orsaka problem med massiva databaser. Om du fortfarande vill anv\u00e4nda den kan du g\u00f6ra det. \n\n Filmnamn ing\u00e5r inte l\u00e4ngre som attribut i filmsensorn. \n\n Kommande har tagits bort. Det h\u00e5ller p\u00e5 att moderniseras som kalenderobjekt ska vara. Diskutrymme \u00e4r nu uppdelat i olika sensorer, en f\u00f6r varje mapp. \n\n Status och kommandon har tagits bort eftersom de inte verkar ha n\u00e5got verkligt v\u00e4rde f\u00f6r automatiseringar.", - "title": "\u00c4ndringar av Radarr-integrationen" } }, "options": { diff --git a/homeassistant/components/radarr/translations/tr.json b/homeassistant/components/radarr/translations/tr.json index 256cba85647..ef2eb8c5575 100644 --- a/homeassistant/components/radarr/translations/tr.json +++ b/homeassistant/components/radarr/translations/tr.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "Radarr'\u0131n YAML kullan\u0131larak yap\u0131land\u0131r\u0131lmas\u0131 kald\u0131r\u0131l\u0131yor. \n\n Mevcut YAML yap\u0131land\u0131rman\u0131z otomatik olarak kullan\u0131c\u0131 aray\u00fcz\u00fcne aktar\u0131ld\u0131. \n\n Radarr YAML yap\u0131land\u0131rmas\u0131n\u0131 configuration.yaml dosyan\u0131zdan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", "title": "Radarr YAML yap\u0131land\u0131rmas\u0131 kald\u0131r\u0131l\u0131yor" - }, - "removed_attributes": { - "description": "Film sayma sens\u00f6r\u00fcn\u00fcn dikkatli bir \u015fekilde devre d\u0131\u015f\u0131 b\u0131rak\u0131lmas\u0131nda baz\u0131 \u00f6nemli de\u011fi\u015fiklikler yap\u0131ld\u0131. \n\n Bu sens\u00f6r, b\u00fcy\u00fck veritabanlar\u0131nda sorunlara neden olabilir. Hala kullanmak istiyorsan\u0131z, bunu yapabilirsiniz. \n\n Film adlar\u0131 art\u0131k film sens\u00f6r\u00fcne \u00f6znitelik olarak dahil edilmemektedir. \n\n Yakla\u015fan kald\u0131r\u0131ld\u0131. Takvim \u00f6\u011feleri olmas\u0131 gerekti\u011fi gibi modernize ediliyor. Disk alan\u0131 art\u0131k her klas\u00f6r i\u00e7in bir tane olmak \u00fczere farkl\u0131 sens\u00f6rlere b\u00f6l\u00fcnm\u00fc\u015ft\u00fcr. \n\n Otomasyonlar i\u00e7in ger\u00e7ek de\u011feri olmad\u0131\u011f\u0131 i\u00e7in durum ve komutlar kald\u0131r\u0131ld\u0131.", - "title": "Radarr entegrasyonundaki de\u011fi\u015fiklikler" } }, "options": { diff --git a/homeassistant/components/radarr/translations/zh-Hant.json b/homeassistant/components/radarr/translations/zh-Hant.json index 6b381643c1a..694f26b7bf2 100644 --- a/homeassistant/components/radarr/translations/zh-Hant.json +++ b/homeassistant/components/radarr/translations/zh-Hant.json @@ -30,10 +30,6 @@ "deprecated_yaml": { "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a\u7684 Radarr \u5373\u5c07\u9032\u884c\u79fb\u9664\u3002\n\n\u65e2\u6709\u7684 YAML \u8a2d\u5b9a\u5c07\u81ea\u52d5\u532f\u5165\u81f3 UI \u5167\u3002\n\n\u8acb\u65bc configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Radarr YAML \u8a2d\u5b9a\u4e26\u91cd\u65b0\u555f\u52d5 Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", "title": "Radarr YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" - }, - "removed_attributes": { - "description": "\u5728\u8b39\u614e\u8003\u91cf\u5f8c\u3001\u95dc\u9589\u96fb\u5f71\u6578\u611f\u6e2c\u5668\u4e0a\u505a\u4e86\u4e00\u4e9b\u91cd\u5927\u8b8a\u66f4\u3002\n\n\u7531\u65bc\u5de8\u5927\u7684\u8cc7\u6599\u5eab\u53ef\u80fd\u6703\u5c0e\u81f4\u611f\u6e2c\u5668\u51fa\u73fe\u554f\u984c\u3002\u5047\u5982\u60a8\u4ecd\u60f3\u7e7c\u7e8c\u4f7f\u7528\u3001\u8acb\u6ce8\u610f\u76f8\u95dc\u554f\u984c\u3002\n\n\u96fb\u5f71\u611f\u6e2c\u5668\u5c6c\u6027\u4e2d\u5c07\u4e0d\u518d\u5305\u542b\u96fb\u5f71\u540d\u7a31\u3002\n\n\u5373\u5c07\u4e0a\u6620\u90e8\u5206\u5df2\u7d93\u79fb\u9664\u3002\u6b63\u8ddf\u8457\u884c\u4e8b\u66c6\u529f\u80fd\u9032\u884c\u66f4\u65b0\uff0c\u78c1\u789f\u7a7a\u9593\u5c07\u6703\u4f9d\u64da\u4e0d\u540c\u611f\u6e2c\u5668\u9032\u884c\u5206\u9694\u65bc\u5404\u81ea\u7684\u8cc7\u6599\u593e\u3002\n\n\u7531\u65bc\u5c0d\u65bc\u81ea\u52d5\u5316\u7684\u7528\u9014\u4e0d\u9ad8\uff0c\u72c0\u614b\u8207\u547d\u4ee4\u4e5f\u5df2\u7d93\u79fb\u9664\u3002", - "title": "\u8b8a\u66f4\u81f3 Radarr \u6574\u5408" } }, "options": { diff --git a/homeassistant/components/rainmachine/translations/bg.json b/homeassistant/components/rainmachine/translations/bg.json index 4ba51cf991e..1239915231b 100644 --- a/homeassistant/components/rainmachine/translations/bg.json +++ b/homeassistant/components/rainmachine/translations/bg.json @@ -11,17 +11,5 @@ "title": "\u041f\u043e\u043f\u044a\u043b\u043d\u0435\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\u0442\u0430 \u0441\u0438" } } - }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "title": "\u041e\u0431\u0435\u043a\u0442\u044a\u0442 {old_entity_id} \u0449\u0435 \u0431\u044a\u0434\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442" - } - } - }, - "title": "\u041e\u0431\u0435\u043a\u0442\u044a\u0442 {old_entity_id} \u0449\u0435 \u0431\u044a\u0434\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442" - } } } \ No newline at end of file diff --git a/homeassistant/components/rainmachine/translations/ca.json b/homeassistant/components/rainmachine/translations/ca.json index a96548c5f0c..ecace654255 100644 --- a/homeassistant/components/rainmachine/translations/ca.json +++ b/homeassistant/components/rainmachine/translations/ca.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Actualitza totes les automatitzacions o 'scripts' que utilitzin aquesta entitat perqu\u00e8 passin a utilitzar l'entitat `{replacement_entity_id}`.", - "title": "L'entitat {old_entity_id} s'eliminar\u00e0" - } - } - }, - "title": "L'entitat {old_entity_id} s'eliminar\u00e0" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/de.json b/homeassistant/components/rainmachine/translations/de.json index bff980b82e2..74416d0165c 100644 --- a/homeassistant/components/rainmachine/translations/de.json +++ b/homeassistant/components/rainmachine/translations/de.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Aktualisiere alle Automatisierungen oder Skripte, die diese Entit\u00e4t verwenden, um stattdessen `{replacement_entity_id}` zu verwenden.", - "title": "Die Entit\u00e4t {old_entity_id} wird entfernt" - } - } - }, - "title": "Die Entit\u00e4t {old_entity_id} wird entfernt" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/el.json b/homeassistant/components/rainmachine/translations/el.json index f64454dbbbc..a1811b90057 100644 --- a/homeassistant/components/rainmachine/translations/el.json +++ b/homeassistant/components/rainmachine/translations/el.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "\u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03c4\u03b5 \u03cc\u03bb\u03bf\u03c5\u03c2 \u03c4\u03bf\u03c5\u03c2 \u03b1\u03c5\u03c4\u03bf\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03bf\u03cd\u03c2 \u03ae \u03c4\u03b1 \u03c3\u03b5\u03bd\u03ac\u03c1\u03b9\u03b1 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7\u03bd \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03ce\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd \u03b1\u03bd\u03c4\u03af \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03bf `{replacement_entity_id}`.", - "title": "\u0397 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 {old_entity_id} \u03b8\u03b1 \u03b1\u03c6\u03b1\u03b9\u03c1\u03b5\u03b8\u03b5\u03af" - } - } - }, - "title": "\u0397 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 {old_entity_id} \u03b8\u03b1 \u03b1\u03c6\u03b1\u03b9\u03c1\u03b5\u03b8\u03b5\u03af" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/en.json b/homeassistant/components/rainmachine/translations/en.json index f1883a725d2..701a338e88d 100644 --- a/homeassistant/components/rainmachine/translations/en.json +++ b/homeassistant/components/rainmachine/translations/en.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Update any automations or scripts that use this entity to instead use `{replacement_entity_id}`.", - "title": "The {old_entity_id} entity will be removed" - } - } - }, - "title": "The {old_entity_id} entity will be removed" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/es.json b/homeassistant/components/rainmachine/translations/es.json index dfd0691ad96..22726b4bf40 100644 --- a/homeassistant/components/rainmachine/translations/es.json +++ b/homeassistant/components/rainmachine/translations/es.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Actualiza cualquier automatizaci\u00f3n o script que use esta entidad para usar `{replacement_entity_id}`.", - "title": "Se eliminar\u00e1 la entidad {old_entity_id}" - } - } - }, - "title": "Se eliminar\u00e1 la entidad {old_entity_id}" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/et.json b/homeassistant/components/rainmachine/translations/et.json index 84ec424e716..1f45d8d3c30 100644 --- a/homeassistant/components/rainmachine/translations/et.json +++ b/homeassistant/components/rainmachine/translations/et.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "V\u00e4rskendage automaatikaid v\u00f5i skripte, mis seda olemit kasutavad, et kasutada selle asemel '{replacement_entity_id}'.", - "title": "\u00dcksus {old_entity_id} eemaldatakse" - } - } - }, - "title": "\u00dcksus {old_entity_id} eemaldatakse" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/fr.json b/homeassistant/components/rainmachine/translations/fr.json index 63f5af55527..d9a6c011755 100644 --- a/homeassistant/components/rainmachine/translations/fr.json +++ b/homeassistant/components/rainmachine/translations/fr.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Modifiez tout script ou automatisation utilisant cette entit\u00e9 afin qu'ils utilisent `{replacement_entity_id}` \u00e0 la place.", - "title": "L'entit\u00e9 {old_entity_id} sera supprim\u00e9e" - } - } - }, - "title": "L'entit\u00e9 {old_entity_id} sera supprim\u00e9e" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/hu.json b/homeassistant/components/rainmachine/translations/hu.json index f458009997c..419ab2cf4b0 100644 --- a/homeassistant/components/rainmachine/translations/hu.json +++ b/homeassistant/components/rainmachine/translations/hu.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Friss\u00edtse az ezt az entit\u00e1st haszn\u00e1l\u00f3 automatiz\u00e1l\u00e1sokat vagy szkripteket, hogy helyette a k\u00f6vetkez\u0151t haszn\u00e1ja: `{replacement_entity_id}`", - "title": "{old_entity_id} entit\u00e1s elt\u00e1vol\u00edt\u00e1sra ker\u00fcl." - } - } - }, - "title": "{old_entity_id} entit\u00e1s elt\u00e1vol\u00edt\u00e1sra ker\u00fcl." - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/id.json b/homeassistant/components/rainmachine/translations/id.json index 3c1434b7b4b..03ea324b62b 100644 --- a/homeassistant/components/rainmachine/translations/id.json +++ b/homeassistant/components/rainmachine/translations/id.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Perbarui semua otomasi atau skrip yang menggunakan entitas ini untuk menggunakan `{replacement_entity_id}`.", - "title": "Entitas {old_entity_id} akan dihapus" - } - } - }, - "title": "Entitas {old_entity_id} akan dihapus" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/it.json b/homeassistant/components/rainmachine/translations/it.json index daef760ca42..da26d3f3c65 100644 --- a/homeassistant/components/rainmachine/translations/it.json +++ b/homeassistant/components/rainmachine/translations/it.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Aggiorna tutte le automazioni o gli script che utilizzano questa entit\u00e0 in modo che utilizzino invece `{replacement_entity_id}`.", - "title": "L'entit\u00e0 {old_entity_id} verr\u00e0 rimossa" - } - } - }, - "title": "L'entit\u00e0 {old_entity_id} verr\u00e0 rimossa" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/nl.json b/homeassistant/components/rainmachine/translations/nl.json index 4fa3285c03b..cbf76b879cb 100644 --- a/homeassistant/components/rainmachine/translations/nl.json +++ b/homeassistant/components/rainmachine/translations/nl.json @@ -18,18 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "title": "De {old_entity_id}-entiteit wordt verwijderd" - } - } - }, - "title": "De {old_entity_id}-entiteit wordt verwijderd" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/no.json b/homeassistant/components/rainmachine/translations/no.json index f738be0ad7d..180674b0bb4 100644 --- a/homeassistant/components/rainmachine/translations/no.json +++ b/homeassistant/components/rainmachine/translations/no.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Oppdater eventuelle automatiseringer eller skript som bruker denne enheten til i stedet \u00e5 bruke ` {replacement_entity_id} `.", - "title": "{old_entity_id} vil bli fjernet" - } - } - }, - "title": "{old_entity_id} vil bli fjernet" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/pl.json b/homeassistant/components/rainmachine/translations/pl.json index 8f81ed34ea8..36f3531a0a0 100644 --- a/homeassistant/components/rainmachine/translations/pl.json +++ b/homeassistant/components/rainmachine/translations/pl.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Zaktualizuj wszystkie automatyzacje lub skrypty, kt\u00f3re u\u017cywaj\u0105 tej encji, aby zamiast tego u\u017cywa\u0142y `{replacement_entity_id}`.", - "title": "Encja {old_entity_id} zostanie usuni\u0119ta" - } - } - }, - "title": "Encja {old_entity_id} zostanie usuni\u0119ta" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/pt-BR.json b/homeassistant/components/rainmachine/translations/pt-BR.json index a274c77a4ca..25662e1004d 100644 --- a/homeassistant/components/rainmachine/translations/pt-BR.json +++ b/homeassistant/components/rainmachine/translations/pt-BR.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Atualize quaisquer automa\u00e7\u00f5es ou scripts que usam essa entidade para usar `{replacement_entity_id}`.", - "title": "A entidade {old_entity_id} ser\u00e1 removida" - } - } - }, - "title": "A entidade {old_entity_id} ser\u00e1 removida" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/ru.json b/homeassistant/components/rainmachine/translations/ru.json index c2c904fd0a8..28d82d0b24c 100644 --- a/homeassistant/components/rainmachine/translations/ru.json +++ b/homeassistant/components/rainmachine/translations/ru.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "\u0412 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f\u0445 \u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u0430\u0445, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0445 \u044d\u0442\u043e\u0442 \u043e\u0431\u044a\u0435\u043a\u0442, \u0442\u0435\u043f\u0435\u0440\u044c \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 `{replacement_entity_id}`.", - "title": "\u041e\u0431\u044a\u0435\u043a\u0442 {old_entity_id} \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d" - } - } - }, - "title": "\u041e\u0431\u044a\u0435\u043a\u0442 {old_entity_id} \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/sv.json b/homeassistant/components/rainmachine/translations/sv.json index 9cf860dee34..10e06693207 100644 --- a/homeassistant/components/rainmachine/translations/sv.json +++ b/homeassistant/components/rainmachine/translations/sv.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Uppdatera alla automatiseringar eller skript som anv\u00e4nder denna enhet f\u00f6r att ist\u00e4llet anv\u00e4nda ` {replacement_entity_id} `.", - "title": "{old_entity_id} kommer att tas bort" - } - } - }, - "title": "{old_entity_id} kommer att tas bort" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/tr.json b/homeassistant/components/rainmachine/translations/tr.json index 1b81ef2c09b..011b4fdd0a3 100644 --- a/homeassistant/components/rainmachine/translations/tr.json +++ b/homeassistant/components/rainmachine/translations/tr.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "Bunun yerine ` {replacement_entity_id} ` kullanmak i\u00e7in bu varl\u0131\u011f\u0131 kullanan t\u00fcm otomasyonlar\u0131 veya komut dosyalar\u0131n\u0131 g\u00fcncelleyin.", - "title": "{old_entity_id} varl\u0131\u011f\u0131 kald\u0131r\u0131lacak" - } - } - }, - "title": "{old_entity_id} varl\u0131\u011f\u0131 kald\u0131r\u0131lacak" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/rainmachine/translations/zh-Hant.json b/homeassistant/components/rainmachine/translations/zh-Hant.json index a912bdb40c9..886840b2c68 100644 --- a/homeassistant/components/rainmachine/translations/zh-Hant.json +++ b/homeassistant/components/rainmachine/translations/zh-Hant.json @@ -18,19 +18,6 @@ } } }, - "issues": { - "replaced_old_entity": { - "fix_flow": { - "step": { - "confirm": { - "description": "\u4f7f\u7528\u6b64\u5be6\u9ad4\u4ee5\u66f4\u65b0\u4efb\u4f55\u81ea\u52d5\u5316\u6216\u8173\u672c\uff0c\u4ee5\u53d6\u4ee3 `{replacement_entity_id}`\u3002", - "title": "{old_entity_id} \u5be6\u9ad4\u5c07\u9032\u884c\u79fb\u9664" - } - } - }, - "title": "{old_entity_id} \u5be6\u9ad4\u5c07\u9032\u884c\u79fb\u9664" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/risco/translations/bg.json b/homeassistant/components/risco/translations/bg.json index 5e165a9dcfe..a738c47d6f2 100644 --- a/homeassistant/components/risco/translations/bg.json +++ b/homeassistant/components/risco/translations/bg.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u0430", - "pin": "\u041f\u0418\u041d \u043a\u043e\u0434", - "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" - }, "menu_options": { "cloud": "Risco Cloud (\u043f\u0440\u0435\u043f\u043e\u0440\u044a\u0447\u0438\u0442\u0435\u043b\u043d\u043e)", "local": "\u041b\u043e\u043a\u0430\u043b\u0435\u043d \u043f\u0430\u043d\u0435\u043b Risco (\u0437\u0430 \u043d\u0430\u043f\u0440\u0435\u0434\u043d\u0430\u043b\u0438)" diff --git a/homeassistant/components/risco/translations/ca.json b/homeassistant/components/risco/translations/ca.json index 072f9521543..7f3cbf0d52c 100644 --- a/homeassistant/components/risco/translations/ca.json +++ b/homeassistant/components/risco/translations/ca.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Contrasenya", - "pin": "Codi PIN", - "username": "Nom d'usuari" - }, "menu_options": { "cloud": "Risco Cloud (recomanat)", "local": "Panell Risco local (avan\u00e7at)" diff --git a/homeassistant/components/risco/translations/cs.json b/homeassistant/components/risco/translations/cs.json index 8698052c0fa..0e2bd5924b0 100644 --- a/homeassistant/components/risco/translations/cs.json +++ b/homeassistant/components/risco/translations/cs.json @@ -22,13 +22,6 @@ "pin": "PIN k\u00f3d", "port": "Port" } - }, - "user": { - "data": { - "password": "Heslo", - "pin": "PIN k\u00f3d", - "username": "U\u017eivatelsk\u00e9 jm\u00e9no" - } } } }, diff --git a/homeassistant/components/risco/translations/de.json b/homeassistant/components/risco/translations/de.json index 6b356a473ce..c4704b1d92d 100644 --- a/homeassistant/components/risco/translations/de.json +++ b/homeassistant/components/risco/translations/de.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Passwort", - "pin": "PIN-Code", - "username": "Benutzername" - }, "menu_options": { "cloud": "Risco-Cloud (empfohlen)", "local": "Lokales Risco-Panel (fortgeschritten)" diff --git a/homeassistant/components/risco/translations/el.json b/homeassistant/components/risco/translations/el.json index 4d68d564184..9b4424600c7 100644 --- a/homeassistant/components/risco/translations/el.json +++ b/homeassistant/components/risco/translations/el.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "pin": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN", - "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" - }, "menu_options": { "cloud": "Risco Cloud (\u03c3\u03c5\u03bd\u03b9\u03c3\u03c4\u03ac\u03c4\u03b1\u03b9)", "local": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03cc \u03a0\u03ac\u03bd\u03b5\u03bb Risco (\u03b3\u03b9\u03b1 \u03c0\u03c1\u03bf\u03c7\u03c9\u03c1\u03b7\u03bc\u03ad\u03bd\u03bf\u03c5\u03c2)" diff --git a/homeassistant/components/risco/translations/en.json b/homeassistant/components/risco/translations/en.json index 0d72ba3cca2..95dd395e501 100644 --- a/homeassistant/components/risco/translations/en.json +++ b/homeassistant/components/risco/translations/en.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Password", - "pin": "PIN Code", - "username": "Username" - }, "menu_options": { "cloud": "Risco Cloud (recommended)", "local": "Local Risco Panel (advanced)" diff --git a/homeassistant/components/risco/translations/es.json b/homeassistant/components/risco/translations/es.json index 690887b66d2..ee6a998da07 100644 --- a/homeassistant/components/risco/translations/es.json +++ b/homeassistant/components/risco/translations/es.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Contrase\u00f1a", - "pin": "C\u00f3digo PIN", - "username": "Nombre de usuario" - }, "menu_options": { "cloud": "Risco Cloud (recomendado)", "local": "Panel Risco local (avanzado)" diff --git a/homeassistant/components/risco/translations/et.json b/homeassistant/components/risco/translations/et.json index 4f8140c12fc..d28ebdfd167 100644 --- a/homeassistant/components/risco/translations/et.json +++ b/homeassistant/components/risco/translations/et.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Salas\u00f5na", - "pin": "PIN kood", - "username": "Kasutajanimi" - }, "menu_options": { "cloud": "Risco Cloud (soovitatav)", "local": "Kohalik Risco paneel (t\u00e4psem)" diff --git a/homeassistant/components/risco/translations/fr.json b/homeassistant/components/risco/translations/fr.json index 3d12d0be5c8..ff7edbbda4e 100644 --- a/homeassistant/components/risco/translations/fr.json +++ b/homeassistant/components/risco/translations/fr.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Mot de passe", - "pin": "Code PIN", - "username": "Nom d'utilisateur" - }, "menu_options": { "cloud": "Risco Cloud (recommand\u00e9)", "local": "Risco Panel local (avanc\u00e9)" diff --git a/homeassistant/components/risco/translations/he.json b/homeassistant/components/risco/translations/he.json index 926afdf8abf..fac0ad1a0b1 100644 --- a/homeassistant/components/risco/translations/he.json +++ b/homeassistant/components/risco/translations/he.json @@ -22,13 +22,6 @@ "pin": "\u05e7\u05d5\u05d3 PIN", "port": "\u05e4\u05ea\u05d7\u05d4" } - }, - "user": { - "data": { - "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "pin": "\u05e7\u05d5\u05d3 PIN", - "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" - } } } }, diff --git a/homeassistant/components/risco/translations/hu.json b/homeassistant/components/risco/translations/hu.json index 0de2158f626..20b7daa3687 100644 --- a/homeassistant/components/risco/translations/hu.json +++ b/homeassistant/components/risco/translations/hu.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Jelsz\u00f3", - "pin": "PIN-k\u00f3d", - "username": "Felhaszn\u00e1l\u00f3n\u00e9v" - }, "menu_options": { "cloud": "Risco Cloud (aj\u00e1nlott)", "local": "Helyi Risco panel (halad\u00f3)" diff --git a/homeassistant/components/risco/translations/id.json b/homeassistant/components/risco/translations/id.json index 438ff72a4ed..7fcdca45d38 100644 --- a/homeassistant/components/risco/translations/id.json +++ b/homeassistant/components/risco/translations/id.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Kata Sandi", - "pin": "Kode PIN", - "username": "Nama Pengguna" - }, "menu_options": { "cloud": "Risco Cloud (disarankan)", "local": "Panel Risco Lokal (lanjutan)" diff --git a/homeassistant/components/risco/translations/it.json b/homeassistant/components/risco/translations/it.json index ebe78f338d9..df714cbd9fc 100644 --- a/homeassistant/components/risco/translations/it.json +++ b/homeassistant/components/risco/translations/it.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Password", - "pin": "Codice PIN", - "username": "Nome utente" - }, "menu_options": { "cloud": "Risco Cloud (consigliato)", "local": "Pannello Risco locale (avanzato)" diff --git a/homeassistant/components/risco/translations/ja.json b/homeassistant/components/risco/translations/ja.json index a2919b167e5..f2243bbbe6a 100644 --- a/homeassistant/components/risco/translations/ja.json +++ b/homeassistant/components/risco/translations/ja.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "pin": "PIN\u30b3\u30fc\u30c9", - "username": "\u30e6\u30fc\u30b6\u30fc\u540d" - }, "menu_options": { "cloud": "Risco Cloud(\u63a8\u5968)", "local": "Local Risco Panel(\u30a2\u30c9\u30d0\u30f3\u30b9\u30c9)" diff --git a/homeassistant/components/risco/translations/ko.json b/homeassistant/components/risco/translations/ko.json index 6ceaedbc6dc..f9cd9a28cc4 100644 --- a/homeassistant/components/risco/translations/ko.json +++ b/homeassistant/components/risco/translations/ko.json @@ -7,15 +7,6 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" - }, - "step": { - "user": { - "data": { - "password": "\ube44\ubc00\ubc88\ud638", - "pin": "PIN \ucf54\ub4dc", - "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" - } - } } }, "options": { diff --git a/homeassistant/components/risco/translations/lb.json b/homeassistant/components/risco/translations/lb.json index ae136cb1843..1cab6cbfa32 100644 --- a/homeassistant/components/risco/translations/lb.json +++ b/homeassistant/components/risco/translations/lb.json @@ -7,15 +7,6 @@ "cannot_connect": "Feeler beim verbannen", "invalid_auth": "Ong\u00eblteg Authentifikatioun", "unknown": "Onerwaarte Feeler" - }, - "step": { - "user": { - "data": { - "password": "Passwuert", - "pin": "PIN Code", - "username": "Benotzernumm" - } - } } }, "options": { diff --git a/homeassistant/components/risco/translations/nl.json b/homeassistant/components/risco/translations/nl.json index db2a4996c77..e16f43d1767 100644 --- a/homeassistant/components/risco/translations/nl.json +++ b/homeassistant/components/risco/translations/nl.json @@ -22,13 +22,6 @@ "pin": "Pincode", "port": "Poort" } - }, - "user": { - "data": { - "password": "Wachtwoord", - "pin": "Pincode", - "username": "Gebruikersnaam" - } } } }, diff --git a/homeassistant/components/risco/translations/no.json b/homeassistant/components/risco/translations/no.json index 177a092f4f3..03c75284d53 100644 --- a/homeassistant/components/risco/translations/no.json +++ b/homeassistant/components/risco/translations/no.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Passord", - "pin": "PIN kode", - "username": "Brukernavn" - }, "menu_options": { "cloud": "Risco Cloud (anbefalt)", "local": "Lokalt Risco-panel (avansert)" diff --git a/homeassistant/components/risco/translations/pl.json b/homeassistant/components/risco/translations/pl.json index 0e4ea5302d7..f14d10a4bd6 100644 --- a/homeassistant/components/risco/translations/pl.json +++ b/homeassistant/components/risco/translations/pl.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Has\u0142o", - "pin": "Kod PIN", - "username": "Nazwa u\u017cytkownika" - }, "menu_options": { "cloud": "Chmura Risco (zalecane)", "local": "Lokalny Panel Risco (zaawansowane)" diff --git a/homeassistant/components/risco/translations/pt-BR.json b/homeassistant/components/risco/translations/pt-BR.json index 7e3a9ef6818..14e07f4c09d 100644 --- a/homeassistant/components/risco/translations/pt-BR.json +++ b/homeassistant/components/risco/translations/pt-BR.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Senha", - "pin": "C\u00f3digo PIN", - "username": "Usu\u00e1rio" - }, "menu_options": { "cloud": "Risco Cloud (recomendado)", "local": "Painel de Risco Local (avan\u00e7ado)" diff --git a/homeassistant/components/risco/translations/pt.json b/homeassistant/components/risco/translations/pt.json index 30b87b0a0da..bdbab687bc0 100644 --- a/homeassistant/components/risco/translations/pt.json +++ b/homeassistant/components/risco/translations/pt.json @@ -10,11 +10,6 @@ }, "step": { "user": { - "data": { - "password": "Palavra-passe", - "pin": "C\u00f3digo PIN", - "username": "Nome de Utilizador" - }, "menu_options": { "cloud": "Risco Cloud (recomendado)", "local": "Painel de Risco Local (avan\u00e7ado)" diff --git a/homeassistant/components/risco/translations/ru.json b/homeassistant/components/risco/translations/ru.json index 116168a60de..d1f5748f81a 100644 --- a/homeassistant/components/risco/translations/ru.json +++ b/homeassistant/components/risco/translations/ru.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "pin": "PIN-\u043a\u043e\u0434", - "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" - }, "menu_options": { "cloud": "\u041e\u0431\u043b\u0430\u043a\u043e Risco (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f)", "local": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u0430\u043d\u0435\u043b\u044c Risco (\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f)" diff --git a/homeassistant/components/risco/translations/sk.json b/homeassistant/components/risco/translations/sk.json index 8311c3f50c2..4c651486bd4 100644 --- a/homeassistant/components/risco/translations/sk.json +++ b/homeassistant/components/risco/translations/sk.json @@ -16,11 +16,6 @@ "data": { "host": "Hostite\u013e" } - }, - "user": { - "data": { - "password": "Heslo" - } } } }, diff --git a/homeassistant/components/risco/translations/sv.json b/homeassistant/components/risco/translations/sv.json index dd89d9c43f7..862a9a3b1ba 100644 --- a/homeassistant/components/risco/translations/sv.json +++ b/homeassistant/components/risco/translations/sv.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "L\u00f6senord", - "pin": "Pin-kod", - "username": "Anv\u00e4ndarnamn" - }, "menu_options": { "cloud": "Risco Cloud (rekommenderas)", "local": "Lokal Risco Panel (avancerat)" diff --git a/homeassistant/components/risco/translations/tr.json b/homeassistant/components/risco/translations/tr.json index 6572e50de4f..83b7bc5c5f9 100644 --- a/homeassistant/components/risco/translations/tr.json +++ b/homeassistant/components/risco/translations/tr.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "Parola", - "pin": "PIN Kodu", - "username": "Kullan\u0131c\u0131 Ad\u0131" - }, "menu_options": { "cloud": "Risco Cloud (\u00f6nerilir)", "local": "Yerel Risco Paneli (geli\u015fmi\u015f)" diff --git a/homeassistant/components/risco/translations/uk.json b/homeassistant/components/risco/translations/uk.json index 53b64344f2e..794fcfdbcda 100644 --- a/homeassistant/components/risco/translations/uk.json +++ b/homeassistant/components/risco/translations/uk.json @@ -7,15 +7,6 @@ "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f", "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f.", "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" - }, - "step": { - "user": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "pin": "PIN-\u043a\u043e\u0434", - "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430" - } - } } }, "options": { diff --git a/homeassistant/components/risco/translations/zh-Hans.json b/homeassistant/components/risco/translations/zh-Hans.json deleted file mode 100644 index a5f4ff11f09..00000000000 --- a/homeassistant/components/risco/translations/zh-Hans.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "username": "\u7528\u6237\u540d" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/risco/translations/zh-Hant.json b/homeassistant/components/risco/translations/zh-Hant.json index 852ba76e208..a235abffc52 100644 --- a/homeassistant/components/risco/translations/zh-Hant.json +++ b/homeassistant/components/risco/translations/zh-Hant.json @@ -24,11 +24,6 @@ } }, "user": { - "data": { - "password": "\u5bc6\u78bc", - "pin": "PIN \u78bc", - "username": "\u4f7f\u7528\u8005\u540d\u7a31" - }, "menu_options": { "cloud": "Risco Cloud\uff08\u63a8\u85a6\uff09", "local": "\u672c\u5730\u7aef Risco \u9762\u677f\uff08\u9032\u968e\uff09" diff --git a/homeassistant/components/scrape/translations/bg.json b/homeassistant/components/scrape/translations/bg.json index b75cbbb4e6e..059fe59152c 100644 --- a/homeassistant/components/scrape/translations/bg.json +++ b/homeassistant/components/scrape/translations/bg.json @@ -18,14 +18,9 @@ }, "user": { "data": { - "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", "authentication": "\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", - "index": "\u0418\u043d\u0434\u0435\u043a\u0441", "method": "\u041c\u0435\u0442\u043e\u0434", - "name": "\u0418\u043c\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", - "select": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435", - "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0437\u0430 \u0438\u0437\u043c\u0435\u0440\u0432\u0430\u043d\u0435", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" } } @@ -40,14 +35,9 @@ "step": { "init": { "data": { - "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", "authentication": "\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", - "index": "\u0418\u043d\u0434\u0435\u043a\u0441", "method": "\u041c\u0435\u0442\u043e\u0434", - "name": "\u0418\u043c\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", - "select": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435", - "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0437\u0430 \u0438\u0437\u043c\u0435\u0440\u0432\u0430\u043d\u0435", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" } } diff --git a/homeassistant/components/scrape/translations/ca.json b/homeassistant/components/scrape/translations/ca.json index 8998a7a9c69..2d53d459611 100644 --- a/homeassistant/components/scrape/translations/ca.json +++ b/homeassistant/components/scrape/translations/ca.json @@ -3,34 +3,47 @@ "abort": { "already_configured": "El compte ja est\u00e0 configurat" }, + "error": { + "resource_error": "No s'han pogut actualitzar les dades restants. Verifica la configuraci\u00f3" + }, "step": { - "user": { + "sensor": { "data": { "attribute": "Atribut", - "authentication": "Autenticaci\u00f3", "device_class": "Classe de dispositiu", - "headers": "Cap\u00e7aleres", "index": "\u00cdndex", "name": "Nom", - "password": "Contrasenya", - "resource": "Recurs", "select": "Selecciona", "state_class": "Classe d'estat", "unit_of_measurement": "Unitat de mesura", - "username": "Nom d'usuari", - "value_template": "Plantilla de valor", - "verify_ssl": "Verifica el certificat SSL" + "value_template": "Plantilla de valor" }, "data_description": { "attribute": "Obt\u00e9 el valor d'un atribut de l'etiqueta seleccionada", - "authentication": "Tipus d'autenticaci\u00f3 HTTP. O b\u00e0sica o 'digest'", - "device_class": "Tipus/classe del sensor per configurar-ne la icona a la interf\u00edcie", - "headers": "Cap\u00e7aleres a utilitzar per a la sol\u00b7licitud web", + "device_class": "Tipus/classe del sensor per configurar-ne la icona a la interf\u00edcie d'usuari", "index": "Defineix quins dels elements retornats pel selector CSS utilitzar", - "resource": "URL del lloc web que cont\u00e9 el valor", "select": "Defineix quina etiqueta s'ha de buscar. Consulta els selectors CSS de Beautifulsoup per m\u00e9s informaci\u00f3", - "state_class": "La state_class del sensor", - "value_template": "Defineix una plantilla per obtenir l'estat del sensor", + "state_class": "'state_class' del sensor", + "unit_of_measurement": "Tria la unitat de mesura de temperatura o crea la teva", + "value_template": "Plantilla per obtenir l'estat del sensor" + } + }, + "user": { + "data": { + "authentication": "Autenticaci\u00f3", + "headers": "Cap\u00e7aleres", + "method": "M\u00e8tode", + "password": "Contrasenya", + "resource": "Recurs", + "timeout": "Temps d'espera", + "username": "Nom d'usuari", + "verify_ssl": "Verifica el certificat SSL" + }, + "data_description": { + "authentication": "Tipus d'autenticaci\u00f3 HTTP. O b\u00e0sica o 'digest'", + "headers": "Cap\u00e7aleres a utilitzar per a la sol\u00b7licitud web", + "resource": "URL del lloc web que cont\u00e9 el valor", + "timeout": "Temps d'espera de connexi\u00f3 al lloc web", "verify_ssl": "Activa/desactiva la verificaci\u00f3 del certificat SSL/TLS, per exemple, si est\u00e0 autosignat" } } @@ -46,31 +59,20 @@ "step": { "init": { "data": { - "attribute": "Atribut", "authentication": "Autenticaci\u00f3", - "device_class": "Classe de dispositiu", "headers": "Cap\u00e7aleres", - "index": "\u00cdndex", - "name": "Nom", + "method": "M\u00e8tode", "password": "Contrasenya", "resource": "Recurs", - "select": "Selecciona", - "state_class": "Classe d'estat", - "unit_of_measurement": "Unitat de mesura", + "timeout": "Temps d'espera", "username": "Nom d'usuari", - "value_template": "Plantilla de valor", "verify_ssl": "Verifica el certificat SSL" }, "data_description": { - "attribute": "Obt\u00e9 el valor d'un atribut de l'etiqueta seleccionada", "authentication": "Tipus d'autenticaci\u00f3 HTTP. O b\u00e0sica o 'digest'", - "device_class": "Tipus/classe del sensor per configurar-ne la icona a la interf\u00edcie", "headers": "Cap\u00e7aleres a utilitzar per a la sol\u00b7licitud web", - "index": "Defineix quins dels elements retornats pel selector CSS utilitzar", "resource": "URL del lloc web que cont\u00e9 el valor", - "select": "Defineix quina etiqueta s'ha de buscar. Consulta els selectors CSS de Beautifulsoup per m\u00e9s informaci\u00f3", - "state_class": "La state_class del sensor", - "value_template": "Defineix una plantilla per obtenir l'estat del sensor", + "timeout": "Temps d'espera de connexi\u00f3 al lloc web", "verify_ssl": "Activa/desactiva la verificaci\u00f3 del certificat SSL/TLS, per exemple, si est\u00e0 autosignat" } } diff --git a/homeassistant/components/scrape/translations/de.json b/homeassistant/components/scrape/translations/de.json index b44ee29f3d1..7d1af5b3a93 100644 --- a/homeassistant/components/scrape/translations/de.json +++ b/homeassistant/components/scrape/translations/de.json @@ -30,34 +30,20 @@ }, "user": { "data": { - "attribute": "Attribut", "authentication": "Authentifizierungsmethode ausw\u00e4hlen", - "device_class": "Ger\u00e4teklasse", "headers": "Header", - "index": "Index", "method": "Methode", - "name": "Name", "password": "Passwort", "resource": "Ressource", - "select": "Ausw\u00e4hlen", - "state_class": "Zustandsklasse", "timeout": "Zeit\u00fcberschreitung", - "unit_of_measurement": "Ma\u00dfeinheit", "username": "Benutzername", - "value_template": "Wertvorlage", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" }, "data_description": { - "attribute": "Wert eines Attributs auf dem ausgew\u00e4hlten Tag abrufen", "authentication": "Typ der HTTP-Authentifizierung. Entweder basic oder digest", - "device_class": "Der Typ/die Klasse des Sensors, um das Symbol im Frontend festzulegen", "headers": "F\u00fcr die Webanforderung zu verwendende Header", - "index": "Definiert, welche der vom CSS-Selektor zur\u00fcckgegebenen Elemente verwendet werden sollen", "resource": "Die URL der Website, die den Wert enth\u00e4lt", - "select": "Legt fest, nach welchem Tag gesucht werden soll. Siehe Beautifulsoup CSS-Selektoren f\u00fcr Details", - "state_class": "Die state_class des Sensors", "timeout": "Zeit\u00fcberschreitung f\u00fcr die Verbindung zur Website", - "value_template": "Definiert eine Vorlage, um den Zustand des Sensors zu ermitteln", "verify_ssl": "Aktiviert/deaktiviert die \u00dcberpr\u00fcfung des SSL/TLS-Zertifikats, z.B. wenn es selbst signiert ist" } } @@ -73,34 +59,20 @@ "step": { "init": { "data": { - "attribute": "Attribut", "authentication": "Authentifizierungsmethode ausw\u00e4hlen", - "device_class": "Ger\u00e4teklasse", "headers": "Header", - "index": "Index", "method": "Methode", - "name": "Name", "password": "Passwort", "resource": "Ressource", - "select": "Ausw\u00e4hlen", - "state_class": "Zustandsklasse", "timeout": "Zeit\u00fcberschreitung", - "unit_of_measurement": "Ma\u00dfeinheit", "username": "Benutzername", - "value_template": "Wertvorlage", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" }, "data_description": { - "attribute": "Wert eines Attributs auf dem ausgew\u00e4hlten Tag abrufen", "authentication": "Typ der HTTP-Authentifizierung. Entweder basic oder digest", - "device_class": "Der Typ/die Klasse des Sensors, um das Symbol im Frontend festzulegen", "headers": "F\u00fcr die Webanforderung zu verwendende Header", - "index": "Definiert, welche der vom CSS-Selektor zur\u00fcckgegebenen Elemente verwendet werden sollen", "resource": "Die URL der Website, die den Wert enth\u00e4lt", - "select": "Legt fest, nach welchem Tag gesucht werden soll. Siehe Beautifulsoup CSS-Selektoren f\u00fcr Details", - "state_class": "Die state_class des Sensors", "timeout": "Zeit\u00fcberschreitung f\u00fcr die Verbindung zur Website", - "value_template": "Definiert eine Vorlage, um den Zustand des Sensors zu ermitteln", "verify_ssl": "Aktiviert/deaktiviert die \u00dcberpr\u00fcfung des SSL/TLS-Zertifikats, z.B. wenn es selbst signiert ist" } } diff --git a/homeassistant/components/scrape/translations/el.json b/homeassistant/components/scrape/translations/el.json index 4dddb51b27a..b516f9b37d7 100644 --- a/homeassistant/components/scrape/translations/el.json +++ b/homeassistant/components/scrape/translations/el.json @@ -30,34 +30,20 @@ }, "user": { "data": { - "attribute": "\u03a7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc", "authentication": "\u0395\u03bb\u03ad\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", - "device_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", "headers": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b5\u03c2", - "index": "\u0394\u03b5\u03af\u03ba\u03c4\u03b7\u03c2", "method": "\u039c\u03ad\u03b8\u03bf\u03b4\u03bf\u03c2", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "resource": "\u03a0\u03cc\u03c1\u03bf\u03c2", - "select": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae", - "state_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf", - "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", - "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2", "verify_ssl": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL" }, "data_description": { - "attribute": "\u039b\u03ae\u03c8\u03b7 \u03c4\u03b7\u03c2 \u03c4\u03b9\u03bc\u03ae\u03c2 \u03b5\u03bd\u03cc\u03c2 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03bf\u03cd \u03c3\u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1", "authentication": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c4\u03bf\u03c5 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 HTTP. \u0395\u03af\u03c4\u03b5 \u03b2\u03b1\u03c3\u03b9\u03ba\u03cc\u03c2 \u03b5\u03af\u03c4\u03b5 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2", - "device_class": "\u039f \u03c4\u03cd\u03c0\u03bf\u03c2/\u03ba\u03bb\u03ac\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03bf\u03c1\u03b9\u03c3\u03bc\u03cc \u03c4\u03bf\u03c5 \u03b5\u03b9\u03ba\u03bf\u03bd\u03b9\u03b4\u03af\u03bf\u03c5 \u03c3\u03c4\u03bf frontend", "headers": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b5\u03c2 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03bf\u03cd\u03bd \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03af\u03c4\u03b7\u03c3\u03b7 \u03b9\u03c3\u03c4\u03bf\u03cd", - "index": "\u039f\u03c1\u03af\u03b6\u03b5\u03b9 \u03c0\u03bf\u03b9\u03b1 \u03b1\u03c0\u03cc \u03c4\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03b1 \u03c0\u03bf\u03c5 \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03b1 CSS \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03bf\u03cd\u03bd", "resource": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c3\u03c4\u03bf\u03bd \u03b9\u03c3\u03c4\u03cc\u03c4\u03bf\u03c0\u03bf \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03c4\u03b7\u03bd \u03c4\u03b9\u03bc\u03ae", - "select": "\u039f\u03c1\u03af\u03b6\u03b5\u03b9 \u03c0\u03bf\u03b9\u03b1 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1 \u03b8\u03b1 \u03b1\u03bd\u03b1\u03b6\u03b7\u03c4\u03b7\u03b8\u03b5\u03af. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03bf\u03c5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03b5\u03af\u03c2 CSS \u03c4\u03bf\u03c5 Beautifulsoup \u03b3\u03b9\u03b1 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2", - "state_class": "\u0397 state_class \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2", "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03bf\u03bd \u03b9\u03c3\u03c4\u03cc\u03c4\u03bf\u03c0\u03bf", - "value_template": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03ad\u03bd\u03b1 \u03c0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03c0\u03b1\u03c1\u03b1\u03bb\u03b1\u03b2\u03ae \u03c4\u03b7\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", "verify_ssl": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af/\u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c4\u03b7\u03bd \u03b5\u03c0\u03b1\u03bb\u03ae\u03b8\u03b5\u03c5\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd SSL/TLS, \u03c0.\u03c7. \u03b1\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c5\u03c4\u03bf-\u03c5\u03c0\u03bf\u03b3\u03b5\u03b3\u03c1\u03b1\u03bc\u03bc\u03ad\u03bd\u03bf." } } @@ -73,34 +59,20 @@ "step": { "init": { "data": { - "attribute": "\u03a7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc", "authentication": "\u0395\u03bb\u03ad\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", - "device_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", "headers": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b5\u03c2", - "index": "\u0394\u03b5\u03af\u03ba\u03c4\u03b7\u03c2", "method": "\u039c\u03ad\u03b8\u03bf\u03b4\u03bf\u03c2", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "password": "\u039a\u03b5\u03bd\u03cc", "resource": "\u03a0\u03cc\u03c1\u03bf\u03c2", - "select": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae", - "state_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf", - "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2", "username": "\u039a\u03b5\u03bd\u03cc", - "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2", "verify_ssl": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL" }, "data_description": { - "attribute": "\u039b\u03ae\u03c8\u03b7 \u03c4\u03b7\u03c2 \u03c4\u03b9\u03bc\u03ae\u03c2 \u03b5\u03bd\u03cc\u03c2 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03bf\u03cd \u03c3\u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1", "authentication": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c4\u03bf\u03c5 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 HTTP. \u0395\u03af\u03c4\u03b5 \u03b2\u03b1\u03c3\u03b9\u03ba\u03cc\u03c2 \u03b5\u03af\u03c4\u03b5 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2", - "device_class": "\u039f \u03c4\u03cd\u03c0\u03bf\u03c2/\u03ba\u03bb\u03ac\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03bf\u03c1\u03b9\u03c3\u03bc\u03cc \u03c4\u03bf\u03c5 \u03b5\u03b9\u03ba\u03bf\u03bd\u03b9\u03b4\u03af\u03bf\u03c5 \u03c3\u03c4\u03bf frontend", "headers": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b5\u03c2 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03bf\u03cd\u03bd \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03af\u03c4\u03b7\u03c3\u03b7 \u03b9\u03c3\u03c4\u03bf\u03cd", - "index": "\u039f\u03c1\u03af\u03b6\u03b5\u03b9 \u03c0\u03bf\u03b9\u03b1 \u03b1\u03c0\u03cc \u03c4\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03b1 \u03c0\u03bf\u03c5 \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03b1 CSS \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03bf\u03cd\u03bd", "resource": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c3\u03c4\u03bf\u03bd \u03b9\u03c3\u03c4\u03cc\u03c4\u03bf\u03c0\u03bf \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03c4\u03b7\u03bd \u03c4\u03b9\u03bc\u03ae", - "select": "\u039f\u03c1\u03af\u03b6\u03b5\u03b9 \u03c0\u03bf\u03b9\u03b1 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1 \u03b8\u03b1 \u03b1\u03bd\u03b1\u03b6\u03b7\u03c4\u03b7\u03b8\u03b5\u03af. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03bf\u03c5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03b5\u03af\u03c2 CSS \u03c4\u03bf\u03c5 Beautifulsoup \u03b3\u03b9\u03b1 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2", - "state_class": "\u0397 state_class \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03bf\u03bd \u03b9\u03c3\u03c4\u03cc\u03c4\u03bf\u03c0\u03bf", - "value_template": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03ad\u03bd\u03b1 \u03c0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03c0\u03b1\u03c1\u03b1\u03bb\u03b1\u03b2\u03ae \u03c4\u03b7\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", "verify_ssl": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af/\u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c4\u03b7\u03bd \u03b5\u03c0\u03b1\u03bb\u03ae\u03b8\u03b5\u03c5\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd SSL/TLS, \u03c0.\u03c7. \u03b1\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c5\u03c4\u03bf-\u03c5\u03c0\u03bf\u03b3\u03b5\u03b3\u03c1\u03b1\u03bc\u03bc\u03ad\u03bd\u03bf." } } diff --git a/homeassistant/components/scrape/translations/en.json b/homeassistant/components/scrape/translations/en.json index 042e9d913b0..8cc642c12a2 100644 --- a/homeassistant/components/scrape/translations/en.json +++ b/homeassistant/components/scrape/translations/en.json @@ -30,34 +30,20 @@ }, "user": { "data": { - "attribute": "Attribute", "authentication": "Select authentication method", - "device_class": "Device Class", "headers": "Headers", - "index": "Index", "method": "Method", - "name": "Name", "password": "Password", "resource": "Resource", - "select": "Select", - "state_class": "State Class", "timeout": "Timeout", - "unit_of_measurement": "Unit of Measurement", "username": "Username", - "value_template": "Value Template", "verify_ssl": "Verify SSL certificate" }, "data_description": { - "attribute": "Get value of an attribute on the selected tag", "authentication": "Type of the HTTP authentication. Either basic or digest", - "device_class": "The type/class of the sensor to set the icon in the frontend", "headers": "Headers to use for the web request", - "index": "Defines which of the elements returned by the CSS selector to use", "resource": "The URL to the website that contains the value", - "select": "Defines what tag to search for. Check Beautifulsoup CSS selectors for details", - "state_class": "The state_class of the sensor", "timeout": "Timeout for connection to website", - "value_template": "Defines a template to get the state of the sensor", "verify_ssl": "Enables/disables verification of SSL/TLS certificate, for example if it is self-signed" } } @@ -73,34 +59,20 @@ "step": { "init": { "data": { - "attribute": "Attribute", "authentication": "Select authentication method", - "device_class": "Device Class", "headers": "Headers", - "index": "Index", "method": "Method", - "name": "Name", "password": "Password", "resource": "Resource", - "select": "Select", - "state_class": "State Class", "timeout": "Timeout", - "unit_of_measurement": "Unit of Measurement", "username": "Username", - "value_template": "Value Template", "verify_ssl": "Verify SSL certificate" }, "data_description": { - "attribute": "Get value of an attribute on the selected tag", "authentication": "Type of the HTTP authentication. Either basic or digest", - "device_class": "The type/class of the sensor to set the icon in the frontend", "headers": "Headers to use for the web request", - "index": "Defines which of the elements returned by the CSS selector to use", "resource": "The URL to the website that contains the value", - "select": "Defines what tag to search for. Check Beautifulsoup CSS selectors for details", - "state_class": "The state_class of the sensor", "timeout": "Timeout for connection to website", - "value_template": "Defines a template to get the state of the sensor", "verify_ssl": "Enables/disables verification of SSL/TLS certificate, for example if it is self-signed" } } diff --git a/homeassistant/components/scrape/translations/es.json b/homeassistant/components/scrape/translations/es.json index 9f893f0ee67..f7573ef9406 100644 --- a/homeassistant/components/scrape/translations/es.json +++ b/homeassistant/components/scrape/translations/es.json @@ -30,34 +30,20 @@ }, "user": { "data": { - "attribute": "Atributo", "authentication": "Selecciona el m\u00e9todo de autenticaci\u00f3n", - "device_class": "Clase de dispositivo", "headers": "Cabeceras", - "index": "\u00cdndice", "method": "M\u00e9todo", - "name": "Nombre", "password": "Contrase\u00f1a", "resource": "Recurso", - "select": "Seleccionar", - "state_class": "Clase de estado", "timeout": "Tiempo de espera", - "unit_of_measurement": "Unidad de medida", "username": "Nombre de usuario", - "value_template": "Plantilla de valor", "verify_ssl": "Verificar el certificado SSL" }, "data_description": { - "attribute": "Obtener el valor de un atributo en la etiqueta seleccionada", "authentication": "Tipo de autenticaci\u00f3n HTTP. Puede ser basic o digest", - "device_class": "El tipo/clase del sensor para establecer el icono en el frontend", "headers": "Cabeceras a usar para la petici\u00f3n web", - "index": "Define cu\u00e1l de los elementos devueltos por el selector CSS se va a usar", "resource": "La URL del sitio web que contiene el valor.", - "select": "Define qu\u00e9 etiqueta buscar. Revisa los selectores CSS de Beautifulsoup para obtener m\u00e1s informaci\u00f3n.", - "state_class": "El state_class del sensor", "timeout": "Tiempo de espera para la conexi\u00f3n al sitio web", - "value_template": "Define una plantilla para obtener el estado del sensor", "verify_ssl": "Habilita/deshabilita la verificaci\u00f3n del certificado SSL/TLS, por ejemplo, si est\u00e1 autofirmado" } } @@ -73,34 +59,20 @@ "step": { "init": { "data": { - "attribute": "Atributo", "authentication": "Selecciona el m\u00e9todo de autenticaci\u00f3n", - "device_class": "Clase de dispositivo", "headers": "Cabeceras", - "index": "\u00cdndice", "method": "M\u00e9todo", - "name": "Nombre", "password": "Contrase\u00f1a", "resource": "Recurso", - "select": "Seleccionar", - "state_class": "Clase de estado", "timeout": "Tiempo de espera", - "unit_of_measurement": "Unidad de medida", "username": "Nombre de usuario", - "value_template": "Plantilla de valor", "verify_ssl": "Verificar el certificado SSL" }, "data_description": { - "attribute": "Obtener el valor de un atributo en la etiqueta seleccionada", "authentication": "Tipo de autenticaci\u00f3n HTTP. Puede ser basic o digest", - "device_class": "El tipo/clase del sensor para establecer el icono en el frontend", "headers": "Cabeceras a usar para la petici\u00f3n web", - "index": "Define cu\u00e1l de los elementos devueltos por el selector CSS se va a usar", "resource": "La URL del sitio web que contiene el valor.", - "select": "Define qu\u00e9 etiqueta buscar. Revisa los selectores CSS de Beautifulsoup para obtener m\u00e1s informaci\u00f3n.", - "state_class": "El state_class del sensor", "timeout": "Tiempo de espera para la conexi\u00f3n al sitio web", - "value_template": "Define una plantilla para obtener el estado del sensor", "verify_ssl": "Habilita/deshabilita la verificaci\u00f3n del certificado SSL/TLS, por ejemplo, si est\u00e1 autofirmado" } } diff --git a/homeassistant/components/scrape/translations/et.json b/homeassistant/components/scrape/translations/et.json index 9bfd788e86d..02d8ba32c4e 100644 --- a/homeassistant/components/scrape/translations/et.json +++ b/homeassistant/components/scrape/translations/et.json @@ -30,34 +30,20 @@ }, "user": { "data": { - "attribute": "Atribuut", "authentication": "Vali tuvastusmeetod", - "device_class": "Seadme klass", "headers": "P\u00e4ised", - "index": "Indeks", "method": "Meetod", - "name": "Nimi", "password": "Salas\u00f5na", "resource": "Resurss", - "select": "Vali", - "state_class": "Oleku klass", "timeout": "Ajal\u00f5pp", - "unit_of_measurement": "M\u00f5\u00f5t\u00fchik", "username": "Kasutajanimi", - "value_template": "V\u00e4\u00e4rtuse mall", "verify_ssl": "Kontrolli SSL serti" }, "data_description": { - "attribute": "Hangi valitud sildi atribuudi v\u00e4\u00e4rtus", "authentication": "HTTP-autentimise t\u00fc\u00fcp. Kas basic v\u00f5i digest", - "device_class": "Anduri t\u00fc\u00fcp/klass ikooni seadmiseks kasutajaliideses", "headers": "Veebip\u00e4ringu jaoks kasutatavad p\u00e4ised", - "index": "M\u00e4\u00e4rab, milliseid CSS selektoriga tagastatud elemente kasutada.", "resource": "V\u00e4\u00e4rtust sisaldava veebisaidi URL", - "select": "M\u00e4\u00e4rab, millist silti otsida. Lisateavet leiad Beautifulsoup CSS-i valijatest", - "state_class": "Anduri oleku klass", "timeout": "Veebilehe ajal\u00f5pp", - "value_template": "M\u00e4\u00e4rab malli anduri oleku saamiseks", "verify_ssl": "Lubab/keelab SSL/TLS-sertifikaadi kontrollimise, n\u00e4iteks kui see on ise allkirjastatud" } } @@ -73,34 +59,20 @@ "step": { "init": { "data": { - "attribute": "Atribuut", "authentication": "Tuvastamine", - "device_class": "Seadme klss", "headers": "P\u00e4ised", - "index": "Indeks", "method": "Meetod", - "name": "", "password": "Salas\u00f5na", "resource": "Resurss", - "select": "Vali", - "state_class": "Oleku klass", "timeout": "Ajal\u00f5pp", - "unit_of_measurement": "M\u00f5\u00f5t\u00fchik", "username": "", - "value_template": "V\u00e4\u00e4rtuse mall", "verify_ssl": "" }, "data_description": { - "attribute": "Hangi valitud elemendi atribuudi v\u00e4\u00e4rtus", "authentication": "HTTP kasutaja tuvastamise meetod; algeline v\u00f5i muu", - "device_class": "Kasutajaliidesesse lisatava anduri ikooni t\u00fc\u00fcp/klass", "headers": "Veebip\u00e4ringus kasutatav p\u00e4is", - "index": "M\u00e4\u00e4rab milline element tagastatakse kasutatava CSS valiku alusel", "resource": "Veebilehe URL ei sisalda soovitud v\u00e4\u00e4rtusi", - "select": "M\u00e4\u00e4rab otsitava v\u00f5tmes\u00f5na. Vaata Beatifulsoup CSS valimeid", - "state_class": "Anduri olekuklass", "timeout": "Veebilehe ajal\u00f5pp", - "value_template": "M\u00e4\u00e4rab anduri oleku saamiseks vajaliku malli", "verify_ssl": "Lubab v\u00f5i keelab SSL/TLS serdi tuvastamise n\u00e4iteks juhul kui sert on ise allkirjastatud" } } diff --git a/homeassistant/components/scrape/translations/fr.json b/homeassistant/components/scrape/translations/fr.json index 86257268448..6e4c0f45b67 100644 --- a/homeassistant/components/scrape/translations/fr.json +++ b/homeassistant/components/scrape/translations/fr.json @@ -18,34 +18,20 @@ }, "user": { "data": { - "attribute": "Attribut", "authentication": "S\u00e9lectionner la m\u00e9thode d\u2019authentification", - "device_class": "Classe d'appareil", "headers": "En-t\u00eates", - "index": "Index", "method": "M\u00e9thode", - "name": "Nom", "password": "Mot de passe", "resource": "Ressource", - "select": "S\u00e9lectionner", - "state_class": "Classe d'\u00e9tat", "timeout": "D\u00e9lai d\u2019expiration", - "unit_of_measurement": "Unit\u00e9 de mesure", "username": "Nom d'utilisateur", - "value_template": "Mod\u00e8le de valeur", "verify_ssl": "V\u00e9rifier le certificat SSL" }, "data_description": { - "attribute": "Obtenir la valeur d'un attribut de la balise s\u00e9lectionn\u00e9e", "authentication": "M\u00e9thode d'authentification HTTP. \u00ab\u00a0basic\u00a0\u00bb ou \u00ab\u00a0digest\u00a0\u00bb", - "device_class": "Le type (la classe) du capteur qui d\u00e9finira l'ic\u00f4ne dans l'interface", "headers": "Les en-t\u00eates \u00e0 utiliser pour la requ\u00eate Web", - "index": "D\u00e9finit l'\u00e9l\u00e9ment \u00e0 utiliser parmi ceux renvoy\u00e9s par le s\u00e9lecteur CSS", "resource": "L'URL du site web qui contient la valeur", - "select": "D\u00e9finit la balise \u00e0 rechercher. Consultez les s\u00e9lecteurs CSS de Beautifulsoup pour plus de d\u00e9tails", - "state_class": "La state_class du capteur", "timeout": "D\u00e9lai d\u2019expiration pour la connexion au site Web", - "value_template": "D\u00e9finit un mod\u00e8le pour obtenir l'\u00e9tat du capteur", "verify_ssl": "Active ou d\u00e9sactive la v\u00e9rification du certificat SSL/TLS, par exemple s'il est auto-sign\u00e9" } } @@ -55,34 +41,20 @@ "step": { "init": { "data": { - "attribute": "Attribut", "authentication": "S\u00e9lectionner la m\u00e9thode d\u2019authentification", - "device_class": "Classe d'appareil", "headers": "En-t\u00eates", - "index": "Index", "method": "M\u00e9thode", - "name": "Nom", "password": "Mot de passe", "resource": "Ressource", - "select": "S\u00e9lectionner", - "state_class": "Classe d'\u00e9tat", "timeout": "D\u00e9lai d\u2019expiration", - "unit_of_measurement": "Unit\u00e9 de mesure", "username": "Nom d'utilisateur", - "value_template": "Mod\u00e8le de valeur", "verify_ssl": "V\u00e9rifier le certificat SSL" }, "data_description": { - "attribute": "Obtenir la valeur d'un attribut de la balise s\u00e9lectionn\u00e9e", "authentication": "M\u00e9thode d'authentification HTTP. \u00ab\u00a0basic\u00a0\u00bb ou \u00ab\u00a0digest\u00a0\u00bb", - "device_class": "Le type (la classe) du capteur qui d\u00e9finira l'ic\u00f4ne dans l'interface", "headers": "Les en-t\u00eates \u00e0 utiliser pour la requ\u00eate Web", - "index": "D\u00e9finit l'\u00e9l\u00e9ment \u00e0 utiliser parmi ceux renvoy\u00e9s par le s\u00e9lecteur CSS", "resource": "L'URL du site web qui contient la valeur", - "select": "D\u00e9finit la balise \u00e0 rechercher. Consultez les s\u00e9lecteurs CSS de Beautifulsoup pour plus de d\u00e9tails", - "state_class": "La state_class du capteur", "timeout": "D\u00e9lai d\u2019expiration pour la connexion au site Web", - "value_template": "D\u00e9finit un mod\u00e8le pour obtenir l'\u00e9tat du capteur", "verify_ssl": "Active ou d\u00e9sactive la v\u00e9rification du certificat SSL/TLS, par exemple s'il est auto-sign\u00e9" } } diff --git a/homeassistant/components/scrape/translations/he.json b/homeassistant/components/scrape/translations/he.json index 6dd1e6845de..144a9567d17 100644 --- a/homeassistant/components/scrape/translations/he.json +++ b/homeassistant/components/scrape/translations/he.json @@ -6,13 +6,9 @@ "step": { "user": { "data": { - "name": "\u05e9\u05dd", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9", "verify_ssl": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 SSL" - }, - "data_description": { - "state_class": "\u05d4-state_class \u05e9\u05dc \u05d4\u05d7\u05d9\u05d9\u05e9\u05df" } } } @@ -21,13 +17,9 @@ "step": { "init": { "data": { - "name": "\u05e9\u05dd", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9", "verify_ssl": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 SSL" - }, - "data_description": { - "state_class": "\u05d4-state_class \u05e9\u05dc \u05d4\u05d7\u05d9\u05d9\u05e9\u05df" } } } diff --git a/homeassistant/components/scrape/translations/hu.json b/homeassistant/components/scrape/translations/hu.json index 002891fb47d..1bf1f159154 100644 --- a/homeassistant/components/scrape/translations/hu.json +++ b/homeassistant/components/scrape/translations/hu.json @@ -6,31 +6,17 @@ "step": { "user": { "data": { - "attribute": "Attrib\u00fatum", "authentication": "Hiteles\u00edt\u00e9s", - "device_class": "Eszk\u00f6zoszt\u00e1ly", "headers": "Fejl\u00e9cek", - "index": "Index", - "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "resource": "Er\u0151forr\u00e1s", - "select": "Kiv\u00e1laszt\u00e1s", - "state_class": "\u00c1llapotoszt\u00e1ly", - "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g", "username": "Felhaszn\u00e1l\u00f3n\u00e9v", - "value_template": "\u00c9rt\u00e9ksablon", "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se" }, "data_description": { - "attribute": "Egy attrib\u00fatum \u00e9rt\u00e9k\u00e9nek lek\u00e9r\u00e9se a kiv\u00e1lasztott c\u00edmk\u00e9n", "authentication": "A HTTP-hiteles\u00edt\u00e9s t\u00edpusa. Basic vagy digest", - "device_class": "Az \u00e9rz\u00e9kel\u0151 t\u00edpusa/oszt\u00e1lya az ikonnak a kezl\u0151fel\u00fcleten val\u00f3 be\u00e1ll\u00edt\u00e1s\u00e1hoz", "headers": "A webes k\u00e9r\u00e9shez haszn\u00e1land\u00f3 fejl\u00e9cek", - "index": "Meghat\u00e1rozza, hogy a CSS-v\u00e1laszt\u00f3 \u00e1ltal visszaadott elemek k\u00f6z\u00fcl melyiket haszn\u00e1lja.", "resource": "Az \u00e9rt\u00e9ket tartalmaz\u00f3 weboldal URL c\u00edme", - "select": "Meghat\u00e1rozza, hogy milyen c\u00edmk\u00e9t keressen. N\u00e9zze meg a Beautifulsoup CSS szelektorokat a r\u00e9szletek\u00e9rt", - "state_class": "Az \u00e9rz\u00e9kel\u0151 \u00e1llapot oszt\u00e1lya", - "value_template": "Meghat\u00e1roz egy sablont az \u00e9rz\u00e9kel\u0151 \u00e1llapot\u00e1nak lek\u00e9rdez\u00e9s\u00e9re.", "verify_ssl": "Az SSL/TLS tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9s\u00e9nek enged\u00e9lyez\u00e9se/letilt\u00e1sa, p\u00e9ld\u00e1ul ha saj\u00e1t al\u00e1\u00edr\u00e1s\u00fa." } } @@ -46,31 +32,17 @@ "step": { "init": { "data": { - "attribute": "Attrib\u00fatum", "authentication": "Hiteles\u00edt\u00e9s", - "device_class": "Eszk\u00f6zoszt\u00e1ly", "headers": "Fejl\u00e9cek", - "index": "Index", - "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "resource": "Er\u0151forr\u00e1s", - "select": "Kiv\u00e1laszt\u00e1s", - "state_class": "\u00c1llapotoszt\u00e1ly", - "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g", "username": "Felhaszn\u00e1l\u00f3n\u00e9v", - "value_template": "\u00c9rt\u00e9ksablon", "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se" }, "data_description": { - "attribute": "Egy attrib\u00fatum \u00e9rt\u00e9k\u00e9nek lek\u00e9r\u00e9se a kiv\u00e1lasztott c\u00edmk\u00e9n", "authentication": "A HTTP-hiteles\u00edt\u00e9s t\u00edpusa. Basic vagy digest", - "device_class": "Az \u00e9rz\u00e9kel\u0151 t\u00edpusa/oszt\u00e1lya az ikonnak a kezl\u0151fel\u00fcleten val\u00f3 be\u00e1ll\u00edt\u00e1s\u00e1hoz", "headers": "A webes k\u00e9r\u00e9shez haszn\u00e1land\u00f3 fejl\u00e9cek", - "index": "Meghat\u00e1rozza, hogy a CSS-v\u00e1laszt\u00f3 \u00e1ltal visszaadott elemek k\u00f6z\u00fcl melyiket haszn\u00e1lja.", "resource": "Az \u00e9rt\u00e9ket tartalmaz\u00f3 weboldal URL c\u00edme", - "select": "Meghat\u00e1rozza, hogy milyen c\u00edmk\u00e9t keressen. N\u00e9zze meg a Beautifulsoup CSS szelektorokat a r\u00e9szletek\u00e9rt", - "state_class": "Az \u00e9rz\u00e9kel\u0151 \u00e1llapot oszt\u00e1lya", - "value_template": "Meghat\u00e1roz egy sablont az \u00e9rz\u00e9kel\u0151 \u00e1llapot\u00e1nak lek\u00e9rdez\u00e9s\u00e9re.", "verify_ssl": "Az SSL/TLS tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9s\u00e9nek enged\u00e9lyez\u00e9se/letilt\u00e1sa, p\u00e9ld\u00e1ul ha saj\u00e1t al\u00e1\u00edr\u00e1s\u00fa." } } diff --git a/homeassistant/components/scrape/translations/id.json b/homeassistant/components/scrape/translations/id.json index b4c03197ecb..1eb23995e61 100644 --- a/homeassistant/components/scrape/translations/id.json +++ b/homeassistant/components/scrape/translations/id.json @@ -27,34 +27,20 @@ }, "user": { "data": { - "attribute": "Atribut", "authentication": "Pilih metode autentikasi", - "device_class": "Kelas Perangkat", "headers": "Header", - "index": "Indeks", "method": "Metode", - "name": "Nama", "password": "Kata Sandi", "resource": "Sumber Daya", - "select": "Pilihan", - "state_class": "Kelas Status", "timeout": "Tenggang waktu", - "unit_of_measurement": "Satuan Pengukuran", "username": "Nama Pengguna", - "value_template": "Nilai Templat", "verify_ssl": "Verifikasi sertifikat SSL" }, "data_description": { - "attribute": "Dapatkan nilai atribut pada tag yang dipilih", "authentication": "Jenis autentikasi HTTP. Salah satu dari basic atau digest", - "device_class": "Jenis/kelas sensor untuk mengatur ikon di antarmuka", "headers": "Header yang digunakan untuk permintaan web", - "index": "Menentukan elemen mana yang dikembalikan oleh selektor CSS untuk digunakan", "resource": "URL ke situs web yang mengandung nilai", - "select": "Menentukan tag yang harus dicari. Periksa selektor CSS Beautifulsoup untuk melihat detailnya", - "state_class": "Nilai state_class dari sensor", "timeout": "Tenggang waktu untuk koneksi ke situs web", - "value_template": "Mendefinisikan templat untuk mendapatkan status sensor", "verify_ssl": "Mengaktifkan/menonaktifkan verifikasi sertifikat SSL/TLS, misalnya jika sertifikat ditandatangani sendiri" } } @@ -70,34 +56,20 @@ "step": { "init": { "data": { - "attribute": "Atribut", "authentication": "Pilih metode autentikasi", - "device_class": "Kelas Perangkat", "headers": "Header", - "index": "Indeks", "method": "Metode", - "name": "Nama", "password": "Kata Sandi", "resource": "Sumber Daya", - "select": "Pilihan", - "state_class": "Kelas Status", "timeout": "Tenggang waktu", - "unit_of_measurement": "Satuan Pengukuran", "username": "Nama Pengguna", - "value_template": "Nilai Templat", "verify_ssl": "Verifikasi sertifikat SSL" }, "data_description": { - "attribute": "Dapatkan nilai atribut pada tag yang dipilih", "authentication": "Jenis autentikasi HTTP. Salah satu dari basic atau digest", - "device_class": "Jenis/kelas sensor untuk mengatur ikon di antarmuka", "headers": "Header yang digunakan untuk permintaan web", - "index": "Menentukan elemen mana yang dikembalikan oleh selektor CSS untuk digunakan", "resource": "URL ke situs web yang mengandung nilai", - "select": "Menentukan tag yang harus dicari. Periksa selektor CSS Beautifulsoup untuk melihat detailnya", - "state_class": "Nilai state_class dari sensor", "timeout": "Tenggang waktu untuk koneksi ke situs web", - "value_template": "Mendefinisikan templat untuk mendapatkan status sensor", "verify_ssl": "Mengaktifkan/menonaktifkan verifikasi sertifikat SSL/TLS, misalnya jika sertifikat ditandatangani sendiri" } } diff --git a/homeassistant/components/scrape/translations/it.json b/homeassistant/components/scrape/translations/it.json index 32d2fdcbd31..b20393efb93 100644 --- a/homeassistant/components/scrape/translations/it.json +++ b/homeassistant/components/scrape/translations/it.json @@ -6,31 +6,17 @@ "step": { "user": { "data": { - "attribute": "Attributo", "authentication": "Autenticazione", - "device_class": "Classe del dispositivo", "headers": "Intestazioni", - "index": "Indice", - "name": "Nome", "password": "Password", "resource": "Risorsa", - "select": "Seleziona", - "state_class": "Classe di Stato", - "unit_of_measurement": "Unit\u00e0 di misura", "username": "Nome utente", - "value_template": "Modello di valore", "verify_ssl": "Verifica il certificato SSL" }, "data_description": { - "attribute": "Ottieni il valore di un attributo sull'etichetta selezionata", "authentication": "Tipo di autenticazione HTTP. basic o digest", - "device_class": "Il tipo/classe del sensore per impostare l'icona nel frontend", "headers": "Intestazioni da utilizzare per la richiesta web", - "index": "Definisce quale degli elementi restituiti dal selettore CSS utilizzare", "resource": "L'URL del sito Web che contiene il valore", - "select": "Definisce quale etichetta cercare. Controlla i selettori CSS di Beautifulsoup per i dettagli", - "state_class": "La state_class del sensore", - "value_template": "Definisce un modello per ottenere lo stato del sensore", "verify_ssl": "Abilita/disabilita la verifica del certificato SSL/TLS, ad esempio se \u00e8 autofirmato" } } @@ -46,31 +32,17 @@ "step": { "init": { "data": { - "attribute": "Attributo", "authentication": "Autenticazione", - "device_class": "Classe del dispositivo", "headers": "Intestazioni", - "index": "Indice", - "name": "Nome", "password": "Password", "resource": "Risorsa", - "select": "Seleziona", - "state_class": "Classe di Stato", - "unit_of_measurement": "Unit\u00e0 di misura", "username": "Nome utente", - "value_template": "Modello di valore", "verify_ssl": "Verifica il certificato SSL" }, "data_description": { - "attribute": "Ottieni il valore di un attributo sull'etichetta selezionata", "authentication": "Tipo di autenticazione HTTP. basic o digest", - "device_class": "Il tipo/classe del sensore per impostare l'icona nel frontend", "headers": "Intestazioni da utilizzare per la richiesta web", - "index": "Definisce quale degli elementi restituiti dal selettore CSS utilizzare", "resource": "L'URL del sito Web che contiene il valore", - "select": "Definisce quale etichetta cercare. Controlla i selettori CSS di Beautifulsoup per i dettagli", - "state_class": "La state_class del sensore", - "value_template": "Definisce un modello per ottenere lo stato del sensore", "verify_ssl": "Abilita/disabilita la verifica del certificato SSL/TLS, ad esempio se \u00e8 autofirmato" } } diff --git a/homeassistant/components/scrape/translations/ja.json b/homeassistant/components/scrape/translations/ja.json index 554a9d2c37b..1caac0ac2ec 100644 --- a/homeassistant/components/scrape/translations/ja.json +++ b/homeassistant/components/scrape/translations/ja.json @@ -6,31 +6,17 @@ "step": { "user": { "data": { - "attribute": "\u5c5e\u6027", "authentication": "\u8a8d\u8a3c", - "device_class": "\u30c7\u30d0\u30a4\u30b9\u30af\u30e9\u30b9", "headers": "\u30d8\u30c3\u30c0\u30fc", - "index": "\u30a4\u30f3\u30c7\u30c3\u30af\u30b9", - "name": "\u540d\u524d", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "resource": "\u30ea\u30bd\u30fc\u30b9", - "select": "\u9078\u629e", - "state_class": "\u72b6\u614b\u30af\u30e9\u30b9(State Class)", - "unit_of_measurement": "\u6e2c\u5b9a\u306e\u5358\u4f4d", "username": "\u30e6\u30fc\u30b6\u30fc\u540d", - "value_template": "\u5024\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8", "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b" }, "data_description": { - "attribute": "\u9078\u629e\u3057\u305f\u30bf\u30b0\u306e\u5c5e\u6027\u306e\u5024\u3092\u53d6\u5f97\u3059\u308b", "authentication": "HTTP\u8a8d\u8a3c\u306e\u7a2e\u985e\u3002\u30d9\u30fc\u30b7\u30c3\u30af\u307e\u305f\u306f\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u306e\u3069\u3061\u3089\u304b", - "device_class": "\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u306b\u30a2\u30a4\u30b3\u30f3\u3092\u8a2d\u5b9a\u3059\u308b\u30bb\u30f3\u30b5\u30fc\u306e\u30bf\u30a4\u30d7/\u30af\u30e9\u30b9", "headers": "Web\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u4f7f\u7528\u3059\u308b\u30d8\u30c3\u30c0\u30fc", - "index": "CSS\u30bb\u30ec\u30af\u30bf\u304c\u8fd4\u3059\u8981\u7d20\u306e\u3046\u3061\u3001\u3069\u306e\u8981\u7d20\u3092\u4f7f\u7528\u3059\u308b\u304b\u3092\u5b9a\u7fa9\u3057\u307e\u3059", "resource": "\u5024\u3092\u542b\u3080\u30a6\u30a7\u30d6\u30b5\u30a4\u30c8\u306eURL", - "select": "\u691c\u7d22\u3059\u308b\u30bf\u30b0\u3092\u5b9a\u7fa9\u3057\u307e\u3059\u3002\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001Beautifulsoup CSS selectors\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044", - "state_class": "\u30bb\u30f3\u30b5\u30fc\u306e\u72b6\u614b\u30af\u30e9\u30b9(state_class)", - "value_template": "\u30bb\u30f3\u30b5\u30fc\u306e\u72b6\u614b\u3092\u53d6\u5f97\u3059\u308b\u305f\u3081\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u5b9a\u7fa9\u3057\u307e\u3059", "verify_ssl": "SSL/TLS\u8a3c\u660e\u66f8\u306e\u691c\u8a3c\u3092\u6709\u52b9/\u7121\u52b9\u306b\u3057\u307e\u3059\u3002(\u81ea\u5df1\u7f72\u540d\u306e\u5834\u5408\u306a\u3069)" } } @@ -40,31 +26,17 @@ "step": { "init": { "data": { - "attribute": "\u5c5e\u6027", "authentication": "\u8a8d\u8a3c", - "device_class": "\u30c7\u30d0\u30a4\u30b9\u30af\u30e9\u30b9", "headers": "\u30d8\u30c3\u30c0\u30fc", - "index": "\u30a4\u30f3\u30c7\u30c3\u30af\u30b9", - "name": "\u540d\u524d", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "resource": "\u30ea\u30bd\u30fc\u30b9", - "select": "\u9078\u629e", - "state_class": "\u72b6\u614b\u30af\u30e9\u30b9(State Class)", - "unit_of_measurement": "\u6e2c\u5b9a\u306e\u5358\u4f4d", "username": "\u30e6\u30fc\u30b6\u30fc\u540d", - "value_template": "\u5024\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8", "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b" }, "data_description": { - "attribute": "\u9078\u629e\u3057\u305f\u30bf\u30b0\u306e\u5c5e\u6027\u306e\u5024\u3092\u53d6\u5f97\u3059\u308b", "authentication": "HTTP\u8a8d\u8a3c\u306e\u7a2e\u985e\u3002\u30d9\u30fc\u30b7\u30c3\u30af\u307e\u305f\u306f\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u306e\u3069\u3061\u3089\u304b", - "device_class": "\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u306b\u30a2\u30a4\u30b3\u30f3\u3092\u8a2d\u5b9a\u3059\u308b\u30bb\u30f3\u30b5\u30fc\u306e\u30bf\u30a4\u30d7/\u30af\u30e9\u30b9", "headers": "Web\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u4f7f\u7528\u3059\u308b\u30d8\u30c3\u30c0\u30fc", - "index": "CSS\u30bb\u30ec\u30af\u30bf\u304c\u8fd4\u3059\u8981\u7d20\u306e\u3046\u3061\u3001\u3069\u306e\u8981\u7d20\u3092\u4f7f\u7528\u3059\u308b\u304b\u3092\u5b9a\u7fa9\u3057\u307e\u3059", "resource": "\u5024\u3092\u542b\u3080\u30a6\u30a7\u30d6\u30b5\u30a4\u30c8\u306eURL", - "select": "\u691c\u7d22\u3059\u308b\u30bf\u30b0\u3092\u5b9a\u7fa9\u3057\u307e\u3059\u3002\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001Beautifulsoup CSS selectors\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044", - "state_class": "\u30bb\u30f3\u30b5\u30fc\u306e\u72b6\u614b\u30af\u30e9\u30b9(state_class)", - "value_template": "\u30bb\u30f3\u30b5\u30fc\u306e\u72b6\u614b\u3092\u53d6\u5f97\u3059\u308b\u305f\u3081\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u5b9a\u7fa9\u3057\u307e\u3059", "verify_ssl": "SSL/TLS\u8a3c\u660e\u66f8\u306e\u691c\u8a3c\u3092\u6709\u52b9/\u7121\u52b9\u306b\u3057\u307e\u3059\u3002(\u81ea\u5df1\u7f72\u540d\u306e\u5834\u5408\u306a\u3069)" } } diff --git a/homeassistant/components/scrape/translations/nl.json b/homeassistant/components/scrape/translations/nl.json index 90e85d34677..45461f45354 100644 --- a/homeassistant/components/scrape/translations/nl.json +++ b/homeassistant/components/scrape/translations/nl.json @@ -6,31 +6,17 @@ "step": { "user": { "data": { - "attribute": "Attribuut", "authentication": "Authenticatie", - "device_class": "Apparaatklasse", "headers": "Headers", - "index": "Index", - "name": "Naam", "password": "Wachtwoord", "resource": "Bron", - "select": "Selecteer", - "state_class": "Staatklasse", - "unit_of_measurement": "Meeteenheid", "username": "Gebruikersnaam", - "value_template": "Waardetemplate", "verify_ssl": "SSL-certificaat verifi\u00ebren" }, "data_description": { - "attribute": "Haal de waarde op van een attribuut op de geselecteerde tag", "authentication": "Type van de HTTP-authenticatie. Ofwel basic of digest", - "device_class": "Het type/klasse van de sensor om het pictogram in de frontend in te stellen", "headers": "Headers om te gebruiken voor het webverzoek", - "index": "Definieert welke van de door de CSS-selector geretourneerde elementen moeten worden gebruikt", "resource": "De URL naar de website die de waarde bevat", - "select": "Definieert naar welke tag moet worden gezocht. Controleer Beautifulsoup CSS-selectors voor details", - "state_class": "De state_class van de sensor", - "value_template": "Definieert een sjabloon om de status van de sensor te krijgen", "verify_ssl": "Activeert/de-activeert verificatie van SSL/TLS certificaat, als voorbeeld of het is zelf-getekend" } } @@ -40,31 +26,17 @@ "step": { "init": { "data": { - "attribute": "Attribuut", "authentication": "Authenticatie", - "device_class": "Apparaatklasse", "headers": "Headers", - "index": "Index", - "name": "Naam", "password": "Wachtwoord", "resource": "Bron", - "select": "Selecteer", - "state_class": "Staatklasse", - "unit_of_measurement": "Meeteenheid", "username": "Gebruikersnaam", - "value_template": "Waardetemplate", "verify_ssl": "SSL-certificaat verifi\u00ebren" }, "data_description": { - "attribute": "Haal de waarde op van een attribuut op de geselecteerde tag", "authentication": "Type van de HTTP-authenticatie. Ofwel basic of digest", - "device_class": "Het type/klasse van de sensor om het pictogram in de frontend in te stellen", "headers": "Headers om te gebruiken voor het webverzoek", - "index": "Definieert welke van de door de CSS-selector geretourneerde elementen moeten worden gebruikt", "resource": "De URL naar de website die de waarde bevat", - "select": "Definieert naar welke tag moet worden gezocht. Controleer Beautifulsoup CSS-selectors voor details", - "state_class": "De state_class van de sensor", - "value_template": "Definieert een sjabloon om de status van de sensor te krijgen", "verify_ssl": "Activeert/de-activeert verificatie van SSL/TLS certificaat, als voorbeeld of het is zelf-getekend" } } diff --git a/homeassistant/components/scrape/translations/no.json b/homeassistant/components/scrape/translations/no.json index abf5580074a..8b18c7f1b8c 100644 --- a/homeassistant/components/scrape/translations/no.json +++ b/homeassistant/components/scrape/translations/no.json @@ -3,34 +3,47 @@ "abort": { "already_configured": "Kontoen er allerede konfigurert" }, + "error": { + "resource_error": "Kunne ikke oppdatere hviledata. Bekreft konfigurasjonen din" + }, "step": { - "user": { + "sensor": { "data": { "attribute": "Attributt", - "authentication": "Godkjenning", "device_class": "Enhetsklasse", - "headers": "Overskrifter", "index": "Indeks", "name": "Navn", - "password": "Passord", - "resource": "Ressurs", "select": "Velg", "state_class": "Statsklasse", "unit_of_measurement": "M\u00e5leenhet", - "username": "Brukernavn", - "value_template": "Verdimal", - "verify_ssl": "Verifisere SSL-sertifikat" + "value_template": "Verdimal" }, "data_description": { "attribute": "F\u00e5 verdien av et attributt p\u00e5 den valgte taggen", - "authentication": "Type HTTP-godkjenning. Enten grunnleggende eller ufullstendig", - "device_class": "Typen/klassen til sensoren for \u00e5 angi ikonet i frontend", - "headers": "Overskrifter som skal brukes for nettforesp\u00f8rselen", + "device_class": "Type/klasse av sensoren for \u00e5 angi ikonet i frontend", "index": "Definerer hvilke av elementene som returneres av CSS-velgeren som skal brukes", - "resource": "URL-en til nettstedet som inneholder verdien", "select": "Definerer hvilken tag som skal s\u00f8kes etter. Sjekk Beautifulsoup CSS-velgere for detaljer", "state_class": "Sensorens state_class", - "value_template": "Definerer en mal for \u00e5 f\u00e5 tilstanden til sensoren", + "unit_of_measurement": "Velg temperaturm\u00e5ling eller lag din egen", + "value_template": "Definerer en mal for \u00e5 f\u00e5 tilstanden til sensoren" + } + }, + "user": { + "data": { + "authentication": "Velg autentiseringsmetode", + "headers": "Overskrifter", + "method": "Metode", + "password": "Passord", + "resource": "Ressurs", + "timeout": "Tidsavbrudd", + "username": "Brukernavn", + "verify_ssl": "Verifisere SSL-sertifikat" + }, + "data_description": { + "authentication": "Type HTTP-godkjenning. Enten grunnleggende eller ufullstendig", + "headers": "Overskrifter som skal brukes for nettforesp\u00f8rselen", + "resource": "URL-en til nettstedet som inneholder verdien", + "timeout": "Tidsavbrudd for tilkobling til nettside", "verify_ssl": "Aktiverer/deaktiverer verifisering av SSL/TLS-sertifikat, for eksempel hvis det er selvsignert" } } @@ -46,31 +59,20 @@ "step": { "init": { "data": { - "attribute": "Attributt", - "authentication": "Godkjenning", - "device_class": "Enhetsklasse", + "authentication": "Velg autentiseringsmetode", "headers": "Overskrifter", - "index": "Indeks", - "name": "Navn", + "method": "Metode", "password": "Passord", "resource": "Ressurs", - "select": "Velg", - "state_class": "Statsklasse", - "unit_of_measurement": "M\u00e5leenhet", + "timeout": "Tidsavbrudd", "username": "Brukernavn", - "value_template": "Verdimal", "verify_ssl": "Verifisere SSL-sertifikat" }, "data_description": { - "attribute": "F\u00e5 verdien av et attributt p\u00e5 den valgte taggen", "authentication": "Type HTTP-godkjenning. Enten grunnleggende eller ufullstendig", - "device_class": "Typen/klassen til sensoren for \u00e5 angi ikonet i frontend", "headers": "Overskrifter som skal brukes for nettforesp\u00f8rselen", - "index": "Definerer hvilke av elementene som returneres av CSS-velgeren som skal brukes", "resource": "URL-en til nettstedet som inneholder verdien", - "select": "Definerer hvilken tag som skal s\u00f8kes etter. Sjekk Beautifulsoup CSS-velgere for detaljer", - "state_class": "Sensorens state_class", - "value_template": "Definerer en mal for \u00e5 f\u00e5 tilstanden til sensoren", + "timeout": "Tidsavbrudd for tilkobling til nettside", "verify_ssl": "Aktiverer/deaktiverer verifisering av SSL/TLS-sertifikat, for eksempel hvis det er selvsignert" } } diff --git a/homeassistant/components/scrape/translations/pl.json b/homeassistant/components/scrape/translations/pl.json index aa551e8f08c..8e05d2e9474 100644 --- a/homeassistant/components/scrape/translations/pl.json +++ b/homeassistant/components/scrape/translations/pl.json @@ -3,34 +3,47 @@ "abort": { "already_configured": "Konto jest ju\u017c skonfigurowane" }, + "error": { + "resource_error": "Nie mo\u017cna zaktualizowa\u0107 danych \"rest\". Sprawd\u017a swoj\u0105 konfiguracj\u0119." + }, "step": { - "user": { + "sensor": { "data": { "attribute": "Atrybut", - "authentication": "Uwierzytelnianie", "device_class": "Klasa urz\u0105dzenia", - "headers": "Nag\u0142\u00f3wki", "index": "Indeks", "name": "Nazwa", - "password": "Has\u0142o", - "resource": "Zas\u00f3b", "select": "Wybierz", "state_class": "Klasa stanu", "unit_of_measurement": "Jednostka miary", - "username": "Nazwa u\u017cytkownika", - "value_template": "Szablon warto\u015bci", - "verify_ssl": "Weryfikacja certyfikatu SSL" + "value_template": "Szablon warto\u015bci" }, "data_description": { "attribute": "Pobierz warto\u015b\u0107 atrybutu w wybranym tagu", - "authentication": "Typ uwierzytelniania HTTP. Podstawowy lub digest.", "device_class": "Typ/klasa sensora do ustawienia ikony w interfejsie u\u017cytkownika", - "headers": "Nag\u0142\u00f3wki do u\u017cycia w \u017c\u0105daniu internetowym", "index": "Okre\u015bla, kt\u00f3rego z element\u00f3w zwracanych przez selektor CSS nale\u017cy u\u017cy\u0107", - "resource": "Adres URL strony internetowej zawieraj\u0105cej t\u0105 warto\u015b\u0107", "select": "Okre\u015bla jakiego taga szuka\u0107. Sprawd\u017a selektory CSS Beautifulsoup, aby uzyska\u0107 szczeg\u00f3\u0142owe informacje.", "state_class": "state_class sensora", - "value_template": "Szablon, kt\u00f3ry pozwala uzyska\u0107 stan czujnika", + "unit_of_measurement": "Wybierz pomiar temperatury lub stw\u00f3rz w\u0142asny", + "value_template": "Szablon, kt\u00f3ry pozwala uzyska\u0107 stan czujnika" + } + }, + "user": { + "data": { + "authentication": "Wybierz metod\u0119 uwierzytelniania", + "headers": "Nag\u0142\u00f3wki", + "method": "Metoda", + "password": "Has\u0142o", + "resource": "Zas\u00f3b", + "timeout": "Limit czasu", + "username": "Nazwa u\u017cytkownika", + "verify_ssl": "Weryfikacja certyfikatu SSL" + }, + "data_description": { + "authentication": "Typ uwierzytelniania HTTP. Podstawowy lub digest.", + "headers": "Nag\u0142\u00f3wki do u\u017cycia w \u017c\u0105daniu internetowym", + "resource": "Adres URL strony internetowej zawieraj\u0105cej t\u0105 warto\u015b\u0107", + "timeout": "Limit czasu na po\u0142\u0105czenie z witryn\u0105", "verify_ssl": "W\u0142\u0105cza/wy\u0142\u0105cza weryfikacj\u0119 certyfikatu SSL/TLS, na przyk\u0142ad, je\u015bli jest on samopodpisany." } } @@ -46,31 +59,20 @@ "step": { "init": { "data": { - "attribute": "Atrybut", - "authentication": "Uwierzytelnianie", - "device_class": "Klasa urz\u0105dzenia", + "authentication": "Wybierz metod\u0119 uwierzytelniania", "headers": "Nag\u0142\u00f3wki", - "index": "Indeks", - "name": "Nazwa", + "method": "Metoda", "password": "Has\u0142o", "resource": "Zas\u00f3b", - "select": "Wybierz", - "state_class": "Klasa stanu", - "unit_of_measurement": "Jednostka miary", + "timeout": "Limit czasu", "username": "Nazwa u\u017cytkownika", - "value_template": "Szablon warto\u015bci", "verify_ssl": "Weryfikacja certyfikatu SSL" }, "data_description": { - "attribute": "Pobierz warto\u015b\u0107 atrybutu w wybranym tagu", "authentication": "Typ uwierzytelniania HTTP. Podstawowy lub digest.", - "device_class": "Typ/klasa sensora do ustawienia ikony w interfejsie u\u017cytkownika", "headers": "Nag\u0142\u00f3wki do u\u017cycia w \u017c\u0105daniu internetowym", - "index": "Okre\u015bla, kt\u00f3rego z element\u00f3w zwracanych przez selektor CSS nale\u017cy u\u017cy\u0107", "resource": "Adres URL strony internetowej zawieraj\u0105cej t\u0105 warto\u015b\u0107", - "select": "Okre\u015bla jakiego taga szuka\u0107. Sprawd\u017a selektory CSS Beautifulsoup, aby uzyska\u0107 szczeg\u00f3\u0142owe informacje.", - "state_class": "state_class sensora", - "value_template": "Szablon, kt\u00f3ry pozwala uzyska\u0107 stan czujnika", + "timeout": "Limit czasu na po\u0142\u0105czenie z witryn\u0105", "verify_ssl": "W\u0142\u0105cza/wy\u0142\u0105cza weryfikacj\u0119 certyfikatu SSL/TLS, na przyk\u0142ad, je\u015bli jest on samopodpisany." } } diff --git a/homeassistant/components/scrape/translations/pt-BR.json b/homeassistant/components/scrape/translations/pt-BR.json index 55e322ab6c9..4a10e16d1c0 100644 --- a/homeassistant/components/scrape/translations/pt-BR.json +++ b/homeassistant/components/scrape/translations/pt-BR.json @@ -6,31 +6,17 @@ "step": { "user": { "data": { - "attribute": "Atributo", "authentication": "Autentica\u00e7\u00e3o", - "device_class": "Classe do dispositivo", "headers": "Cabe\u00e7alhos", - "index": "\u00cdndice", - "name": "Nome", "password": "Senha", "resource": "Recurso", - "select": "Selecionar", - "state_class": "Classe de estado", - "unit_of_measurement": "Unidade de medida", "username": "Usu\u00e1rio", - "value_template": "Modelo de valor", "verify_ssl": "Verifique o certificado SSL" }, "data_description": { - "attribute": "Obter valor de um atributo na tag selecionada", "authentication": "Tipo de autentica\u00e7\u00e3o HTTP. b\u00e1sica ou digerida", - "device_class": "O tipo/classe do sensor para definir o \u00edcone na frontend", "headers": "Cabe\u00e7alhos a serem usados para a solicita\u00e7\u00e3o da web", - "index": "Define qual dos elementos retornados pelo seletor CSS usar", "resource": "A URL para o site que cont\u00e9m o valor", - "select": "Define qual tag pesquisar. Verifique os seletores CSS da Beautiful Soup para obter detalhes", - "state_class": "A classe de estado do sensor", - "value_template": "Define um modelo para obter o estado do sensor", "verify_ssl": "Ativa/desativa a verifica\u00e7\u00e3o do certificado SSL/TLS, por exemplo, se for autoassinado" } } @@ -46,31 +32,17 @@ "step": { "init": { "data": { - "attribute": "Atributo", "authentication": "Autentica\u00e7\u00e3o", - "device_class": "Classe do dispositivo", "headers": "Cabe\u00e7alhos", - "index": "\u00cdndice", - "name": "Nome", "password": "Senha", "resource": "Recurso", - "select": "Selecionar", - "state_class": "Classe de estado", - "unit_of_measurement": "Unidade de medida", "username": "Usu\u00e1rio", - "value_template": "Modelo de valor", "verify_ssl": "Verificar SSL" }, "data_description": { - "attribute": "Obter valor de um atributo na tag selecionada", "authentication": "Tipo de autentica\u00e7\u00e3o HTTP. b\u00e1sica ou digerida", - "device_class": "O tipo/classe do sensor para definir o \u00edcone na frontend", "headers": "Cabe\u00e7alhos a serem usados para a solicita\u00e7\u00e3o da web", - "index": "Define qual dos elementos retornados pelo seletor CSS usar", "resource": "A URL para o site que cont\u00e9m o valor", - "select": "Define qual tag pesquisar. Verifique os seletores CSS da Beautiful Soup para obter detalhes", - "state_class": "A classe de estado do sensor", - "value_template": "Define um modelo para obter o estado do sensor", "verify_ssl": "Ativa/desativa a verifica\u00e7\u00e3o do certificado SSL/TLS, por exemplo, se for autoassinado" } } diff --git a/homeassistant/components/scrape/translations/pt.json b/homeassistant/components/scrape/translations/pt.json index 003da0eed66..3ad22ead218 100644 --- a/homeassistant/components/scrape/translations/pt.json +++ b/homeassistant/components/scrape/translations/pt.json @@ -2,23 +2,6 @@ "config": { "abort": { "already_configured": "Conta j\u00e1 configurada" - }, - "step": { - "user": { - "data": { - "name": "Nome", - "unit_of_measurement": "Unidade de Medida" - } - } - } - }, - "options": { - "step": { - "init": { - "data": { - "unit_of_measurement": "Unidade de Medida" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/ru.json b/homeassistant/components/scrape/translations/ru.json index 139b006ce69..d901123236f 100644 --- a/homeassistant/components/scrape/translations/ru.json +++ b/homeassistant/components/scrape/translations/ru.json @@ -30,34 +30,20 @@ }, "user": { "data": { - "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", "authentication": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438", - "device_class": "\u041a\u043b\u0430\u0441\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", "headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", - "index": "\u0418\u043d\u0434\u0435\u043a\u0441", "method": "\u041c\u0435\u0442\u043e\u0434", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "resource": "\u0420\u0435\u0441\u0443\u0440\u0441", - "select": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c", - "state_class": "\u041a\u043b\u0430\u0441\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f", "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442", - "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", - "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f", "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL" }, "data_description": { - "attribute": "\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u0442\u0435\u0433\u0430.", "authentication": "\u0422\u0438\u043f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 HTTP: basic \u0438\u043b\u0438 digest.", - "device_class": "\u0422\u0438\u043f/\u043a\u043b\u0430\u0441\u0441 \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435.", "headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0434\u043b\u044f \u0432\u0435\u0431-\u0437\u0430\u043f\u0440\u043e\u0441\u0430.", - "index": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0438\u0437 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u043c CSS \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c.", "resource": "URL-\u0430\u0434\u0440\u0435\u0441 \u0432\u0435\u0431-\u0441\u0430\u0439\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.", - "select": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0442\u0435\u0433 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 CSS Beautifulsoup.", - "state_class": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 state_class \u0434\u043b\u044f \u0441\u0435\u043d\u0441\u043e\u0440\u0430.", "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0441\u0430\u0439\u0442\u0443", - "value_template": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u0430.", "verify_ssl": "\u0412\u043a\u043b\u044e\u0447\u0430\u0435\u0442/\u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 SSL/TLS. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0435\u0441\u043b\u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u0441\u0430\u043c\u043e\u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439." } } @@ -67,34 +53,20 @@ "step": { "init": { "data": { - "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", "authentication": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438", - "device_class": "\u041a\u043b\u0430\u0441\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", "headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", - "index": "\u0418\u043d\u0434\u0435\u043a\u0441", "method": "\u041c\u0435\u0442\u043e\u0434", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "resource": "\u0420\u0435\u0441\u0443\u0440\u0441", - "select": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c", - "state_class": "\u041a\u043b\u0430\u0441\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f", "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442", - "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", - "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f", "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL" }, "data_description": { - "attribute": "\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u0442\u0435\u0433\u0430.", "authentication": "\u0422\u0438\u043f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 HTTP: basic \u0438\u043b\u0438 digest.", - "device_class": "\u0422\u0438\u043f/\u043a\u043b\u0430\u0441\u0441 \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435.", "headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0434\u043b\u044f \u0432\u0435\u0431-\u0437\u0430\u043f\u0440\u043e\u0441\u0430.", - "index": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0438\u0437 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u043c CSS \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c.", "resource": "URL-\u0430\u0434\u0440\u0435\u0441 \u0432\u0435\u0431-\u0441\u0430\u0439\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.", - "select": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0442\u0435\u0433 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 CSS Beautifulsoup.", - "state_class": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 state_class \u0434\u043b\u044f \u0441\u0435\u043d\u0441\u043e\u0440\u0430.", "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0441\u0430\u0439\u0442\u0443", - "value_template": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u0430.", "verify_ssl": "\u0412\u043a\u043b\u044e\u0447\u0430\u0435\u0442/\u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 SSL/TLS. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0435\u0441\u043b\u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u0441\u0430\u043c\u043e\u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439." } } diff --git a/homeassistant/components/scrape/translations/sk.json b/homeassistant/components/scrape/translations/sk.json index f24cd357ee8..cb2ca5a6b94 100644 --- a/homeassistant/components/scrape/translations/sk.json +++ b/homeassistant/components/scrape/translations/sk.json @@ -14,9 +14,7 @@ }, "user": { "data": { - "device_class": "Trieda zariadenia", - "password": "Heslo", - "unit_of_measurement": "Jednotka merania" + "password": "Heslo" } } } @@ -25,9 +23,7 @@ "step": { "init": { "data": { - "device_class": "Trieda zariadenia", - "password": "Heslo", - "unit_of_measurement": "Jednotka merania" + "password": "Heslo" } } } diff --git a/homeassistant/components/scrape/translations/sv.json b/homeassistant/components/scrape/translations/sv.json index 130d7505018..96ecf2a934e 100644 --- a/homeassistant/components/scrape/translations/sv.json +++ b/homeassistant/components/scrape/translations/sv.json @@ -6,31 +6,17 @@ "step": { "user": { "data": { - "attribute": "Attribut", "authentication": "Autentisering", - "device_class": "Enhetsklass", "headers": "Headers", - "index": "Index", - "name": "Namn", "password": "L\u00f6senord", "resource": "Resurs", - "select": "V\u00e4lj", - "state_class": "Tillst\u00e5ndsklass", - "unit_of_measurement": "M\u00e5ttenhet", "username": "Anv\u00e4ndarnamn", - "value_template": "V\u00e4rdemall", "verify_ssl": "Verifiera SSL-certifikat" }, "data_description": { - "attribute": "H\u00e4mta v\u00e4rdet av ett attribut p\u00e5 den valda taggen", "authentication": "Typ av HTTP-autentisering. Antingen basic eller digest", - "device_class": "Typ/klass av sensorn f\u00f6r att st\u00e4lla in ikonen i frontend", "headers": "Rubriker att anv\u00e4nda f\u00f6r webbf\u00f6rfr\u00e5gan", - "index": "Definierar vilka av elementen som returneras av CSS-v\u00e4ljaren som ska anv\u00e4ndas", "resource": "Webbadressen till webbplatsen som inneh\u00e5ller v\u00e4rdet", - "select": "Definierar vilken tagg som ska s\u00f6kas efter. Se Beautifulsoup CSS-selektorer f\u00f6r mer information.", - "state_class": "Tillst\u00e5ndsklassen f\u00f6r sensorn", - "value_template": "Definierar en mall f\u00f6r att f\u00e5 sensorns tillst\u00e5nd", "verify_ssl": "Aktiverar/inaktiverar verifiering av SSL/TLS-certifikat, till exempel om det \u00e4r sj\u00e4lvsignerat" } } @@ -40,31 +26,17 @@ "step": { "init": { "data": { - "attribute": "Attribut", "authentication": "Autentisering", - "device_class": "Enhetsklass", "headers": "Headers", - "index": "Index", - "name": "Namn", "password": "L\u00f6senord", "resource": "Resurs", - "select": "V\u00e4lj", - "state_class": "Tillst\u00e5ndsklass", - "unit_of_measurement": "M\u00e5ttenhet", "username": "Anv\u00e4ndarnamn", - "value_template": "V\u00e4rdemall", "verify_ssl": "Verifiera SSL-certifikat" }, "data_description": { - "attribute": "H\u00e4mta v\u00e4rdet av ett attribut p\u00e5 den valda taggen", "authentication": "Typ av HTTP-autentisering. Antingen basic eller digest", - "device_class": "Typ/klass av sensorn f\u00f6r att st\u00e4lla in ikonen i frontend", "headers": "Rubriker att anv\u00e4nda f\u00f6r webbf\u00f6rfr\u00e5gan", - "index": "Definierar vilka av elementen som returneras av CSS-v\u00e4ljaren som ska anv\u00e4ndas", "resource": "Webbadressen till webbplatsen som inneh\u00e5ller v\u00e4rdet", - "select": "Definierar vilken tagg som ska s\u00f6kas efter. Se Beautifulsoup CSS-selektorer f\u00f6r mer information.", - "state_class": "Tillst\u00e5ndsklassen f\u00f6r sensorn", - "value_template": "Definierar en mall f\u00f6r att f\u00e5 sensorns tillst\u00e5nd", "verify_ssl": "Aktiverar/inaktiverar verifiering av SSL/TLS-certifikat, till exempel om det \u00e4r sj\u00e4lvsignerat" } } diff --git a/homeassistant/components/scrape/translations/tr.json b/homeassistant/components/scrape/translations/tr.json index 954ce1ad052..1bc92367580 100644 --- a/homeassistant/components/scrape/translations/tr.json +++ b/homeassistant/components/scrape/translations/tr.json @@ -6,31 +6,17 @@ "step": { "user": { "data": { - "attribute": "\u00d6znitelik", "authentication": "Kimlik do\u011frulama", - "device_class": "Cihaz S\u0131n\u0131f\u0131", "headers": "Ba\u015fl\u0131klar", - "index": "Dizin", - "name": "Ad", "password": "Parola", "resource": "Kaynak", - "select": "Se\u00e7", - "state_class": "Durum S\u0131n\u0131f\u0131", - "unit_of_measurement": "\u00d6l\u00e7\u00fc Birimi", "username": "Kullan\u0131c\u0131 Ad\u0131", - "value_template": "De\u011fer \u015eablonu", "verify_ssl": "SSL sertifikas\u0131n\u0131 do\u011frulay\u0131n" }, "data_description": { - "attribute": "Se\u00e7ilen etikette bir \u00f6zelli\u011fin de\u011ferini al\u0131n", "authentication": "HTTP kimlik do\u011frulamas\u0131n\u0131n t\u00fcr\u00fc. Temel veya basit", - "device_class": "\u00d6nu\u00e7taki simgeyi ayarlamak i\u00e7in sens\u00f6r\u00fcn t\u00fcr\u00fc/s\u0131n\u0131f\u0131", "headers": "Web iste\u011fi i\u00e7in kullan\u0131lacak ba\u015fl\u0131klar", - "index": "CSS se\u00e7ici taraf\u0131ndan d\u00f6nd\u00fcr\u00fclen \u00f6\u011felerden hangisinin kullan\u0131laca\u011f\u0131n\u0131 tan\u0131mlar", "resource": "De\u011feri i\u00e7eren web sitesinin URL'si", - "select": "Hangi etiketin aranaca\u011f\u0131n\u0131 tan\u0131mlar. Ayr\u0131nt\u0131lar i\u00e7in Beautifulsoup CSS se\u00e7icilerini kontrol edin", - "state_class": "Sens\u00f6r\u00fcn state_class", - "value_template": "Sens\u00f6r\u00fcn durumunu almak i\u00e7in bir \u015fablon tan\u0131mlar", "verify_ssl": "\u00d6rne\u011fin, kendinden imzal\u0131ysa, SSL/TLS sertifikas\u0131n\u0131n do\u011frulanmas\u0131n\u0131 etkinle\u015ftirir/devre d\u0131\u015f\u0131 b\u0131rak\u0131r" } } @@ -40,31 +26,17 @@ "step": { "init": { "data": { - "attribute": "\u00d6znitelik", "authentication": "Kimlik do\u011frulama", - "device_class": "Cihaz S\u0131n\u0131f\u0131", "headers": "Ba\u015fl\u0131klar", - "index": "Dizin", - "name": "Ad", "password": "Parola", "resource": "Kaynak", - "select": "Se\u00e7", - "state_class": "Durum S\u0131n\u0131f\u0131", - "unit_of_measurement": "\u00d6l\u00e7\u00fc Birimi", "username": "Kullan\u0131c\u0131 Ad\u0131", - "value_template": "De\u011fer \u015eablonu", "verify_ssl": "SSL sertifikas\u0131n\u0131 do\u011frulay\u0131n" }, "data_description": { - "attribute": "Se\u00e7ilen etikette bir \u00f6zelli\u011fin de\u011ferini al\u0131n", "authentication": "HTTP kimlik do\u011frulamas\u0131n\u0131n t\u00fcr\u00fc. Temel veya basit", - "device_class": "\u00d6nu\u00e7taki simgeyi ayarlamak i\u00e7in sens\u00f6r\u00fcn t\u00fcr\u00fc/s\u0131n\u0131f\u0131", "headers": "Web iste\u011fi i\u00e7in kullan\u0131lacak ba\u015fl\u0131klar", - "index": "CSS se\u00e7ici taraf\u0131ndan d\u00f6nd\u00fcr\u00fclen \u00f6\u011felerden hangisinin kullan\u0131laca\u011f\u0131n\u0131 tan\u0131mlar", "resource": "De\u011feri i\u00e7eren web sitesinin URL'si", - "select": "Hangi etiketin aranaca\u011f\u0131n\u0131 tan\u0131mlar. Ayr\u0131nt\u0131lar i\u00e7in Beautifulsoup CSS se\u00e7icilerini kontrol edin", - "state_class": "Sens\u00f6r\u00fcn state_class", - "value_template": "Sens\u00f6r\u00fcn durumunu almak i\u00e7in bir \u015fablon tan\u0131mlar", "verify_ssl": "\u00d6rne\u011fin, kendinden imzal\u0131ysa, SSL/TLS sertifikas\u0131n\u0131n do\u011frulanmas\u0131n\u0131 etkinle\u015ftirir/devre d\u0131\u015f\u0131 b\u0131rak\u0131r" } } diff --git a/homeassistant/components/scrape/translations/zh-Hant.json b/homeassistant/components/scrape/translations/zh-Hant.json index d188fc7894e..7bf8cd4a82d 100644 --- a/homeassistant/components/scrape/translations/zh-Hant.json +++ b/homeassistant/components/scrape/translations/zh-Hant.json @@ -6,31 +6,17 @@ "step": { "user": { "data": { - "attribute": "\u5c6c\u6027", "authentication": "\u9a57\u8b49", - "device_class": "\u88dd\u7f6e\u985e\u5225", "headers": "Headers", - "index": "\u6307\u6578", - "name": "\u540d\u7a31", "password": "\u5bc6\u78bc", "resource": "\u4f86\u6e90", - "select": "\u9078\u64c7", - "state_class": "\u72c0\u614b\u985e\u5225", - "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d", "username": "\u4f7f\u7528\u8005\u540d\u7a31", - "value_template": "\u6578\u503c\u6a21\u677f", "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" }, "data_description": { - "attribute": "\u7372\u53d6\u6240\u9078\u6a19\u7c64\u5c6c\u6027\u6578\u503c", "authentication": "HTTP \u9a57\u8b49\u985e\u578b\u3002\u57fa\u672c\u6216\u6458\u8981", - "device_class": "\u65bc Frontend \u4e2d\u8a2d\u5b9a\u4e4b\u50b3\u611f\u5668\u985e\u578b/\u985e\u5225\u5716\u793a", "headers": "\u7528\u65bc Web \u8acb\u6c42\u4e4b Headers", - "index": "\u5b9a\u7fa9\u4f7f\u7528 CSS selector \u56de\u8986\u5143\u7d20", "resource": "\u5305\u542b\u6578\u503c\u7684\u7db2\u7ad9 URL", - "select": "\u5b9a\u7fa9\u8981\u7d22\u7684\u6a19\u7c64\u3002\u53c3\u95b1 Beautifulsoup CSS selector \u4ee5\u7372\u5f97\u8a73\u7d30\u8cc7\u8a0a", - "state_class": "\u611f\u6e2c\u5668 state_class", - "value_template": "\u5b9a\u7fa9\u6a21\u677f\u4ee5\u53d6\u5f97\u611f\u6e2c\u5668\u72c0\u614b", "verify_ssl": "\u958b\u555f/\u95dc\u9589 SSL/TLS \u9a57\u8b49\u8a8d\u8b49\uff0c\u4f8b\u5982\u81ea\u7c3d\u7ae0\u6191\u8b49" } } @@ -46,31 +32,17 @@ "step": { "init": { "data": { - "attribute": "\u5c6c\u6027", "authentication": "\u9a57\u8b49", - "device_class": "\u88dd\u7f6e\u985e\u5225", "headers": "Headers", - "index": "\u6307\u6578", - "name": "\u540d\u7a31", "password": "\u5bc6\u78bc", "resource": "\u4f86\u6e90", - "select": "\u9078\u64c7", - "state_class": "\u72c0\u614b\u985e\u5225", - "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d", "username": "\u4f7f\u7528\u8005\u540d\u7a31", - "value_template": "\u6578\u503c\u6a21\u677f", "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" }, "data_description": { - "attribute": "\u7372\u53d6\u6240\u9078\u6a19\u7c64\u5c6c\u6027\u6578\u503c", "authentication": "HTTP \u9a57\u8b49\u985e\u578b\u3002\u57fa\u672c\u6216\u6458\u8981", - "device_class": "\u65bc Frontend \u4e2d\u8a2d\u5b9a\u4e4b\u50b3\u611f\u5668\u985e\u578b/\u985e\u5225\u5716\u793a", "headers": "\u7528\u65bc Web \u8acb\u6c42\u4e4b Headers", - "index": "\u5b9a\u7fa9\u4f7f\u7528 CSS selector \u56de\u8986\u5143\u7d20", "resource": "\u5305\u542b\u6578\u503c\u7684\u7db2\u7ad9 URL", - "select": "\u5b9a\u7fa9\u8981\u7d22\u7684\u6a19\u7c64\u3002\u53c3\u95b1 Beautifulsoup CSS selector \u4ee5\u7372\u5f97\u8a73\u7d30\u8cc7\u8a0a", - "state_class": "\u611f\u6e2c\u5668 state_class", - "value_template": "\u5b9a\u7fa9\u6a21\u677f\u4ee5\u53d6\u5f97\u611f\u6e2c\u5668\u72c0\u614b", "verify_ssl": "\u958b\u555f/\u95dc\u9589 SSL/TLS \u9a57\u8b49\u8a8d\u8b49\uff0c\u4f8b\u5982\u81ea\u7c3d\u7ae0\u6191\u8b49" } } diff --git a/homeassistant/components/shelly/translations/ca.json b/homeassistant/components/shelly/translations/ca.json index 4406edce9f7..73736725969 100644 --- a/homeassistant/components/shelly/translations/ca.json +++ b/homeassistant/components/shelly/translations/ca.json @@ -60,6 +60,9 @@ } }, "options": { + "abort": { + "ble_unsupported": "La compatibilitat amb Bluetooth necessita la versi\u00f3 de microprogramari {ble_min_version} o una de m\u00e9s recent." + }, "step": { "init": { "data": { diff --git a/homeassistant/components/simplepush/translations/ca.json b/homeassistant/components/simplepush/translations/ca.json index 4252be0764e..2527b2a4329 100644 --- a/homeassistant/components/simplepush/translations/ca.json +++ b/homeassistant/components/simplepush/translations/ca.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "La configuraci\u00f3 de Simplepush mitjan\u00e7ant YAML s'eliminar\u00e0 de Home Assistant.\n\nLa configuraci\u00f3 YAML existent s'ha importat autom\u00e0ticament a la interf\u00edcie d'usuari.\n\nElimina la configuraci\u00f3 YAML de Simplepush del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", - "title": "La configuraci\u00f3 YAML de Simplepush est\u00e0 sent eliminada" - }, "removed_yaml": { "description": "La configuraci\u00f3 de Simplepush mitjan\u00e7ant YAML s'ha eliminat de Home Assistant.\n\nHome Assistant ja no utilitza la configuraci\u00f3 YAML existent.\n\nElimina la configuraci\u00f3 YAML de Simplepush del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", "title": "La configuraci\u00f3 YAML de Simplepush s'ha eliminat" diff --git a/homeassistant/components/simplepush/translations/de.json b/homeassistant/components/simplepush/translations/de.json index 16e916f6d2f..86db336b042 100644 --- a/homeassistant/components/simplepush/translations/de.json +++ b/homeassistant/components/simplepush/translations/de.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Die Konfiguration von Simplepush mittels YAML wird entfernt.\n\nDeine bestehende YAML-Konfiguration wurde automatisch in die Benutzeroberfl\u00e4che importiert.\n\nEntferne die Simplepush-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte den Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Simplepush YAML-Konfiguration wird entfernt" - }, "removed_yaml": { "description": "Die Konfiguration von Simplepush mittels YAML wurde entfernt.\n\nDeine bestehende YAML-Konfiguration wird vom Home Assistant nicht verwendet.\n\nEntferne die Simplepush-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte den Home Assistant neu, um dieses Problem zu beheben.", "title": "Die Simplepush YAML-Konfiguration wurde entfernt" diff --git a/homeassistant/components/simplepush/translations/el.json b/homeassistant/components/simplepush/translations/el.json index 13a961c6aff..9c11e2ea32f 100644 --- a/homeassistant/components/simplepush/translations/el.json +++ b/homeassistant/components/simplepush/translations/el.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Simplepush \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03b5\u03b9\u03c3\u03b1\u03c7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Simplepush YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", - "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Simplepush YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" - }, "removed_yaml": { "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Simplepush \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c4\u03bf\u03c5 YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03b8\u03b7\u03ba\u03b5. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Simplepush YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Simplepush YAML \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c6\u03b1\u03b9\u03c1\u03b5\u03b8\u03b5\u03af" diff --git a/homeassistant/components/simplepush/translations/en.json b/homeassistant/components/simplepush/translations/en.json index cb294813b88..205d3549a52 100644 --- a/homeassistant/components/simplepush/translations/en.json +++ b/homeassistant/components/simplepush/translations/en.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Configuring Simplepush using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Simplepush YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", - "title": "The Simplepush YAML configuration is being removed" - }, "removed_yaml": { "description": "Configuring Simplepush using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the Simplepush YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", "title": "The Simplepush YAML configuration has been removed" diff --git a/homeassistant/components/simplepush/translations/es.json b/homeassistant/components/simplepush/translations/es.json index e1bb97650ff..df24d479813 100644 --- a/homeassistant/components/simplepush/translations/es.json +++ b/homeassistant/components/simplepush/translations/es.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Se va a eliminar la configuraci\u00f3n de Simplepush mediante YAML. \n\nTu configuraci\u00f3n YAML existente se ha importado a la IU autom\u00e1ticamente. \n\nElimina la configuraci\u00f3n YAML de Simplepush de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", - "title": "Se va a eliminar la configuraci\u00f3n YAML de Simplepush" - }, "removed_yaml": { "description": "Se ha eliminado la configuraci\u00f3n de Simplepush usando YAML. \n\nHome Assistant no utiliza tu configuraci\u00f3n YAML existente. \n\nElimina la configuraci\u00f3n YAML de Simplepush de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", "title": "Se ha eliminado la configuraci\u00f3n YAML de Simplepush" diff --git a/homeassistant/components/simplepush/translations/et.json b/homeassistant/components/simplepush/translations/et.json index 7cae6c69edf..8b4e2967d58 100644 --- a/homeassistant/components/simplepush/translations/et.json +++ b/homeassistant/components/simplepush/translations/et.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Simplepushi konfigureerimine YAML-i abil eemaldatakse.\n\nTeie olemasolev YAML-konfiguratsioon on automaatselt kasutajaliidesesse imporditud.\n\nEemaldage Simplepushi YAML-konfiguratsioon oma configuration.yaml-failist ja k\u00e4ivitage Home Assistant uuesti, et see probleem lahendada.", - "title": "Simplepush YAML-i konfiguratsioon eemaldatakse" - }, "removed_yaml": { "description": "Simplepushi konfigureerimine YAMLi abil on eemaldatud.\n\nTeie olemasolevat YAML-konfiguratsiooni ei kasuta Home Assistant.\n\nEemaldage Simplepushi YAML-konfiguratsioon oma configuration.yaml-failist ja k\u00e4ivitage Home Assistant uuesti, et see probleem lahendada.", "title": "Simplepush YAML-i konfiguratsioon on eemaldatud" diff --git a/homeassistant/components/simplepush/translations/fr.json b/homeassistant/components/simplepush/translations/fr.json index 508d31437cb..4e2493c9359 100644 --- a/homeassistant/components/simplepush/translations/fr.json +++ b/homeassistant/components/simplepush/translations/fr.json @@ -19,9 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "title": "La configuration YAML pour Simplepush sera bient\u00f4t supprim\u00e9e" - }, "removed_yaml": { "title": "La configuration YAML pour Simplepush a \u00e9t\u00e9 supprim\u00e9e" } diff --git a/homeassistant/components/simplepush/translations/hu.json b/homeassistant/components/simplepush/translations/hu.json index b5809898fb1..e8583071f34 100644 --- a/homeassistant/components/simplepush/translations/hu.json +++ b/homeassistant/components/simplepush/translations/hu.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "A Simplepush YAML-ben megadott konfigur\u00e1ci\u00f3ja elt\u00e1vol\u00edt\u00e1sra ker\u00fcl.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3 automatikusan import\u00e1l\u00e1sra ker\u00fclt a felhaszn\u00e1l\u00f3i fel\u00fcletre.\n\nA probl\u00e9ma megold\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a Simplepush YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", - "title": "A Simplepush YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" - }, "removed_yaml": { "description": "A Simplepush YAML-ban t\u00f6rt\u00e9n\u0151 konfigur\u00e1l\u00e1sa elt\u00e1vol\u00edt\u00e1sra ker\u00fclt.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3t a Home Assistant nem haszn\u00e1lja.\n\nA probl\u00e9ma megold\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a Simplepush YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", "title": "A Simplepush YAML konfigur\u00e1ci\u00f3ja elt\u00e1vol\u00edt\u00e1sra ker\u00fclt" diff --git a/homeassistant/components/simplepush/translations/id.json b/homeassistant/components/simplepush/translations/id.json index 954e025cf89..92cbfead8df 100644 --- a/homeassistant/components/simplepush/translations/id.json +++ b/homeassistant/components/simplepush/translations/id.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Proses konfigurasi Simplepush lewat YAML dalam proses penghapusan.\n\nKonfigurasi YAML yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML Simplepush dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", - "title": "Konfigurasi YAML Simplepush dalam proses penghapusan" - }, "removed_yaml": { "description": "Proses konfigurasi Integrasi Simplepush lewat YAML telah dihapus.\n\nKonfigurasi YAML yang ada tidak digunakan oleh Home Assistant.\n\nHapus konfigurasi YAML Simplepush dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", "title": "Konfigurasi YAML Integrasi Simplepush telah dihapus" diff --git a/homeassistant/components/simplepush/translations/it.json b/homeassistant/components/simplepush/translations/it.json index be311f7e0c3..cad83f3670f 100644 --- a/homeassistant/components/simplepush/translations/it.json +++ b/homeassistant/components/simplepush/translations/it.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "La configurazione di Simplepush tramite YAML sar\u00e0 rimossa.\n\nLa configurazione YAML esistente \u00e8 stata importata automaticamente nell'interfaccia utente.\n\nRimuovi la configurazione YAML di Simplepush dal file configuration.yaml e riavvia Home Assistant per risolvere questo problema.", - "title": "La configurazione YAML di Simplepush sar\u00e0 rimossa" - }, "removed_yaml": { "description": "La configurazione di Simplepush tramite YAML \u00e8 stata rimossa. \n\n La tua configurazione YAML esistente non \u00e8 utilizzata da Home Assistant. \n\nRimuovi la configurazione YAML di Simplepush dal file configuration.yaml e riavvia Home Assistant per risolvere questo problema.", "title": "La configurazione YAML di Simplepush \u00e8 stata rimossa" diff --git a/homeassistant/components/simplepush/translations/ja.json b/homeassistant/components/simplepush/translations/ja.json index 5c4da266036..1c0b745829c 100644 --- a/homeassistant/components/simplepush/translations/ja.json +++ b/homeassistant/components/simplepush/translations/ja.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Simplepush\u306eYAML\u3092\u4f7f\u7528\u3057\u305f\u8a2d\u5b9a\u306f\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002\n\n\u306a\u304a\u3001\u65e2\u5b58\u306eYAML\u8a2d\u5b9a\u306f\u3001UI\u306b\u81ea\u52d5\u7684\u306b\u30a4\u30f3\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059\u3002\n\n\u3053\u306e\u554f\u984c\u3092\u89e3\u6c7a\u3059\u308b\u306b\u306f\u3001configuration.yaml\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u3001Simplepush\u306eYAML\u8a2d\u5b9a\u3092\u524a\u9664\u3057\u3001Home Assistant\u3092\u518d\u8d77\u52d5\u3057\u307e\u3059\u3002", - "title": "Simplepush YAML\u306e\u8a2d\u5b9a\u306f\u524a\u9664\u3055\u308c\u3066\u3044\u307e\u3059" - }, "removed_yaml": { "description": "Simplepush\u306eYAML\u3092\u4f7f\u7528\u3057\u305f\u8a2d\u5b9a\u306f\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002\n\n\u3059\u3067\u306b\u65e2\u5b58\u306eYAML\u8a2d\u5b9a\u306f\u3001Home Assistant\u3067\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093\u3002\n\n\u3053\u306e\u554f\u984c\u3092\u89e3\u6c7a\u3059\u308b\u306b\u306f\u3001configuration.yaml\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u3001Simplepush\u306eYAML\u8a2d\u5b9a\u3092\u524a\u9664\u3057\u3001Home Assistant\u3092\u518d\u8d77\u52d5\u3057\u307e\u3059\u3002", "title": "Simplepush YAML\u306e\u8a2d\u5b9a\u306f\u524a\u9664\u3055\u308c\u307e\u3057\u305f" diff --git a/homeassistant/components/simplepush/translations/nl.json b/homeassistant/components/simplepush/translations/nl.json index 8916c7db473..176318b3f3c 100644 --- a/homeassistant/components/simplepush/translations/nl.json +++ b/homeassistant/components/simplepush/translations/nl.json @@ -13,10 +13,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "title": "De Simplepush YAML-configuratie wordt verwijderd" - } } } \ No newline at end of file diff --git a/homeassistant/components/simplepush/translations/no.json b/homeassistant/components/simplepush/translations/no.json index 453632f348e..5e71c76bd6d 100644 --- a/homeassistant/components/simplepush/translations/no.json +++ b/homeassistant/components/simplepush/translations/no.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Konfigurering av Simplepush med YAML blir fjernet. \n\n Din eksisterende YAML-konfigurasjon har blitt importert til brukergrensesnittet automatisk. \n\n Fjern Simplepush YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", - "title": "Simplepush YAML-konfigurasjonen blir fjernet" - }, "removed_yaml": { "description": "Konfigurering av Simplepush med YAML er fjernet. \n\n Din eksisterende YAML-konfigurasjon brukes ikke av Home Assistant. \n\n Fjern Simplepush YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", "title": "Simplepush YAML-konfigurasjonen er fjernet" diff --git a/homeassistant/components/simplepush/translations/pl.json b/homeassistant/components/simplepush/translations/pl.json index a3269fda96c..39e8e4aaca9 100644 --- a/homeassistant/components/simplepush/translations/pl.json +++ b/homeassistant/components/simplepush/translations/pl.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Konfiguracja Simplepush przy u\u017cyciu YAML zostanie usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML zosta\u0142a automatycznie zaimportowana do interfejsu u\u017cytkownika. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", - "title": "Konfiguracja YAML dla Simplepush zostanie usuni\u0119ta" - }, "removed_yaml": { "description": "Konfiguracja Simplepush za pomoc\u0105 YAML zosta\u0142a usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML nie jest u\u017cywana przez Home Assistant. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistant, aby rozwi\u0105za\u0107 ten problem.", "title": "Konfiguracja YAML dla Simplepush zosta\u0142a usuni\u0119ta" diff --git a/homeassistant/components/simplepush/translations/pt-BR.json b/homeassistant/components/simplepush/translations/pt-BR.json index 993c8d7214b..faf55738e88 100644 --- a/homeassistant/components/simplepush/translations/pt-BR.json +++ b/homeassistant/components/simplepush/translations/pt-BR.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "A configura\u00e7\u00e3o do Simplepush usando YAML est\u00e1 sendo removida. \n\n Sua configura\u00e7\u00e3o YAML existente foi importada para a interface do usu\u00e1rio automaticamente. \n\n Remova a configura\u00e7\u00e3o Simplepush YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", - "title": "A configura\u00e7\u00e3o Simplepush YAML est\u00e1 sendo removida" - }, "removed_yaml": { "description": "A configura\u00e7\u00e3o do Simplepush usando YAML foi removida. \n\n Sua configura\u00e7\u00e3o YAML existente n\u00e3o \u00e9 usada pelo Home Assistant. \n\n Remova a configura\u00e7\u00e3o YAML do Simplepush do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", "title": "A configura\u00e7\u00e3o Simplepush YAML foi removida" diff --git a/homeassistant/components/simplepush/translations/pt.json b/homeassistant/components/simplepush/translations/pt.json index 67316f99a70..d7e598b33e4 100644 --- a/homeassistant/components/simplepush/translations/pt.json +++ b/homeassistant/components/simplepush/translations/pt.json @@ -13,11 +13,5 @@ } } } - }, - "issues": { - "deprecated_yaml": { - "description": "A configura\u00e7\u00e3o do Simplepush usando YAML est\u00e1 sendo removida. \n\n Sua configura\u00e7\u00e3o YAML existente foi importada para a interface do usu\u00e1rio automaticamente. \n\n Remova a configura\u00e7\u00e3o Simplepush YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", - "title": "A configura\u00e7\u00e3o Simplepush YAML est\u00e1 sendo removida" - } } } \ No newline at end of file diff --git a/homeassistant/components/simplepush/translations/ru.json b/homeassistant/components/simplepush/translations/ru.json index be00e5deb89..63ec7dabd9f 100644 --- a/homeassistant/components/simplepush/translations/ru.json +++ b/homeassistant/components/simplepush/translations/ru.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Simplepush \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", - "title": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Simplepush \u0447\u0435\u0440\u0435\u0437 YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" - }, "removed_yaml": { "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \"Simplepush\" \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Home Assistant. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", "title": "\u0423\u0434\u0430\u043b\u0435\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Simplepush \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML" diff --git a/homeassistant/components/simplepush/translations/sv.json b/homeassistant/components/simplepush/translations/sv.json index 2572b2cce75..bedf1087178 100644 --- a/homeassistant/components/simplepush/translations/sv.json +++ b/homeassistant/components/simplepush/translations/sv.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Konfigurering av Simplepush med YAML tas bort. \n\n Din befintliga YAML-konfiguration har automatiskt importerats till anv\u00e4ndargr\u00e4nssnittet. \n\n Ta bort Simplepush YAML-konfigurationen fr\u00e5n filen configuration.yaml och starta om Home Assistant f\u00f6r att \u00e5tg\u00e4rda problemet.", - "title": "Simplepush YAML-konfigurationen tas bort" - }, "removed_yaml": { "description": "Konfigurering av Simplepush med YAML har tagits bort. \n\n Din befintliga YAML-konfiguration anv\u00e4nds inte av Home Assistant. \n\n Ta bort Simplepush YAML-konfigurationen fr\u00e5n filen configuration.yaml och starta om Home Assistant f\u00f6r att \u00e5tg\u00e4rda problemet.", "title": "Simplepush YAML-konfigurationen har tagits bort" diff --git a/homeassistant/components/simplepush/translations/tr.json b/homeassistant/components/simplepush/translations/tr.json index 0a3183d7c90..f8aa2e05c0c 100644 --- a/homeassistant/components/simplepush/translations/tr.json +++ b/homeassistant/components/simplepush/translations/tr.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Simplepush'un YAML kullan\u0131larak yap\u0131land\u0131r\u0131lmas\u0131 kald\u0131r\u0131l\u0131yor. \n\n Mevcut YAML yap\u0131land\u0131rman\u0131z otomatik olarak kullan\u0131c\u0131 aray\u00fcz\u00fcne aktar\u0131ld\u0131. \n\n Simplepush YAML yap\u0131land\u0131rmas\u0131n\u0131 configuration.yaml dosyan\u0131zdan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", - "title": "Simplepush YAML yap\u0131land\u0131rmas\u0131 kald\u0131r\u0131l\u0131yor" - }, "removed_yaml": { "description": "Simplepush'u YAML kullanarak yap\u0131land\u0131rma kald\u0131r\u0131ld\u0131. \n\n Mevcut YAML yap\u0131land\u0131rman\u0131z Home Assistant taraf\u0131ndan kullan\u0131lm\u0131yor. \n\n Simplepush YAML yap\u0131land\u0131rmas\u0131n\u0131 configuration.yaml dosyan\u0131zdan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", "title": "Simplepush YAML yap\u0131land\u0131rmas\u0131 kald\u0131r\u0131ld\u0131" diff --git a/homeassistant/components/simplepush/translations/zh-Hant.json b/homeassistant/components/simplepush/translations/zh-Hant.json index ea51e58e648..67c70cf4d17 100644 --- a/homeassistant/components/simplepush/translations/zh-Hant.json +++ b/homeassistant/components/simplepush/translations/zh-Hant.json @@ -19,10 +19,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a\u7684 Simplepush \u5373\u5c07\u9032\u884c\u79fb\u9664\u3002\n\n\u65e2\u6709\u7684 YAML \u8a2d\u5b9a\u5c07\u81ea\u52d5\u532f\u5165\u81f3 UI \u5167\u3002\n\n\u8acb\u65bc configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Simplepush YAML \u8a2d\u5b9a\u4e26\u91cd\u65b0\u555f\u52d5 Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", - "title": "Simplepush YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" - }, "removed_yaml": { "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a Simplepush \u7684\u529f\u80fd\u5df2\u7d93\u79fb\u9664\u3002\n\nHome Assistant \u5c07\u4e0d\u518d\u4f7f\u7528\u73fe\u6709\u7684 YAML \u8a2d\u5b9a\u3002\n\n\u7531 configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Simplepush YAML \u8a2d\u5b9a\u4e26\u91cd\u555f Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", "title": "Simplepush YAML \u8a2d\u5b9a\u5df2\u7d93\u79fb\u9664" diff --git a/homeassistant/components/simplisafe/translations/bg.json b/homeassistant/components/simplisafe/translations/bg.json index f5c6862820c..dddb8e8ab5d 100644 --- a/homeassistant/components/simplisafe/translations/bg.json +++ b/homeassistant/components/simplisafe/translations/bg.json @@ -8,26 +8,10 @@ "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, - "progress": { - "email_2fa": "\u041f\u0440\u043e\u0432\u0435\u0440\u0435\u0442\u0435 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0430\u0442\u0430 \u0441\u0438 \u043f\u043e\u0449\u0430 \u0437\u0430 \u0432\u0440\u044a\u0437\u043a\u0430 \u0437\u0430 \u043f\u043e\u0442\u0432\u044a\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u043e\u0442 Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u0430" - }, - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" - }, - "sms_2fa": { - "data": { - "code": "\u041a\u043e\u0434" - } - }, "user": { "data": { - "auth_code": "\u041a\u043e\u0434 \u0437\u0430 \u043e\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f", - "password": "\u041f\u0430\u0440\u043e\u043b\u0430", - "username": "E-mail \u0430\u0434\u0440\u0435\u0441" + "auth_code": "\u041a\u043e\u0434 \u0437\u0430 \u043e\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f" } } } diff --git a/homeassistant/components/simplisafe/translations/ca.json b/homeassistant/components/simplisafe/translations/ca.json index 159b46eeefb..476453cd5b4 100644 --- a/homeassistant/components/simplisafe/translations/ca.json +++ b/homeassistant/components/simplisafe/translations/ca.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "Aquest compte SimpliSafe ja est\u00e0 en \u00fas.", - "email_2fa_timed_out": "S'ha esgotat el temps d'espera de l'autenticaci\u00f3 de dos factors a trav\u00e9s de correu electr\u00f2nic.", "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament", "wrong_account": "Les credencials d'usuari proporcionades no coincideixen amb les d'aquest compte SimpliSafe." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "Els codis d'autoritzaci\u00f3 de SimpliSafe tenen una longitud de 45 car\u00e0cters", "unknown": "Error inesperat" }, - "progress": { - "email_2fa": "Mira el correu electr\u00f2nic on hauries de trobar l'enlla\u00e7 de verificaci\u00f3 de Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Contrasenya" - }, - "description": "Torna a introduir la contrasenya de {username}", - "title": "Reautenticaci\u00f3 de la integraci\u00f3" - }, - "sms_2fa": { - "data": { - "code": "Codi" - }, - "description": "Introdueix el codi d'autenticaci\u00f3 de dos factors que s'ha enviat per SMS." - }, "user": { "data": { - "auth_code": "Codi d'autoritzaci\u00f3", - "password": "Contrasenya", - "username": "Nom d'usuari" + "auth_code": "Codi d'autoritzaci\u00f3" }, "description": "SimpliSafe autentica els seus usuaris a trav\u00e9s de la seva aplicaci\u00f3 web. A causa de les limitacions t\u00e8cniques, hi ha un pas manual al final d'aquest proc\u00e9s; assegura't de llegir la [documentaci\u00f3](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) abans de comen\u00e7ar.\n\nQuan ja estiguis, fes clic [aqu\u00ed]({url}) per obrir l'aplicaci\u00f3 web de SimpliSafe i introdueix les teves credencials. Si ja has iniciat sessi\u00f3 a SimpliSafe a trav\u00e9s del navegador, potser hauries d'obrir una nova pestanya i copiar-hi l'URL de dalt.\n\nQuan el proc\u00e9s s'hagi completat, torna aqu\u00ed i introdueix, a sota, el codi d'autoritzaci\u00f3 de l'URL `com.simplisafe.mobile`." } diff --git a/homeassistant/components/simplisafe/translations/cs.json b/homeassistant/components/simplisafe/translations/cs.json index 520dcc2567e..8864710e8d1 100644 --- a/homeassistant/components/simplisafe/translations/cs.json +++ b/homeassistant/components/simplisafe/translations/cs.json @@ -7,21 +7,6 @@ "error": { "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" - }, - "step": { - "reauth_confirm": { - "data": { - "password": "Heslo" - }, - "description": "Platnost va\u0161eho p\u0159\u00edstupov\u00e9ho tokenu vypr\u0161ela nebo byla zru\u0161ena. Chcete-li sv\u016fj \u00fa\u010det znovu propojit, zadejte sv\u00e9 heslo.", - "title": "Znovu ov\u011b\u0159it integraci" - }, - "user": { - "data": { - "password": "Heslo", - "username": "E-mail" - } - } } }, "options": { diff --git a/homeassistant/components/simplisafe/translations/da.json b/homeassistant/components/simplisafe/translations/da.json index 8133eee3ec9..b500098acfa 100644 --- a/homeassistant/components/simplisafe/translations/da.json +++ b/homeassistant/components/simplisafe/translations/da.json @@ -2,14 +2,6 @@ "config": { "abort": { "already_configured": "Denne SimpliSafe-konto er allerede i brug." - }, - "step": { - "user": { - "data": { - "password": "Adgangskode", - "username": "Emailadresse" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/de.json b/homeassistant/components/simplisafe/translations/de.json index 9ada40c252d..7815189cb0e 100644 --- a/homeassistant/components/simplisafe/translations/de.json +++ b/homeassistant/components/simplisafe/translations/de.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "Dieses SimpliSafe-Konto wird bereits verwendet.", - "email_2fa_timed_out": "Zeit\u00fcberschreitung beim Warten auf E-Mail-basierte Zwei-Faktor-Authentifizierung.", "reauth_successful": "Die erneute Authentifizierung war erfolgreich", "wrong_account": "Die angegebenen Benutzeranmeldeinformationen stimmen nicht mit diesem SimpliSafe-Konto \u00fcberein." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "SimpliSafe Autorisierungscodes sind 45 Zeichen lang", "unknown": "Unerwarteter Fehler" }, - "progress": { - "email_2fa": "\u00dcberpr\u00fcfe deine E-Mails auf einen Best\u00e4tigungslink von Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Passwort" - }, - "description": "Bitte gib das Passwort f\u00fcr {username} erneut ein.", - "title": "Integration erneut authentifizieren" - }, - "sms_2fa": { - "data": { - "code": "Code" - }, - "description": "Gib den Code f\u00fcr die Zwei-Faktor-Authentifizierung ein, den du per SMS erhalten hast." - }, "user": { "data": { - "auth_code": "Autorisierungscode", - "password": "Passwort", - "username": "Benutzername" + "auth_code": "Autorisierungscode" }, "description": "SimpliSafe authentifiziert die Benutzer \u00fcber seine Web-App. Aufgrund technischer Beschr\u00e4nkungen gibt es am Ende dieses Prozesses einen manuellen Schritt; bitte stelle sicher, dass du die [Dokumentation] (http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) liest, bevor du beginnst.\n\nWenn du bereit bist, dr\u00fccke [hier]({url}), um die SimpliSafe-Webanwendung zu \u00f6ffnen und deine Anmeldedaten einzugeben. Wenn du dich bereits bei SimpliSafe in deinem Browser angemeldet hast, kannst du eine neue Registerkarte \u00f6ffnen und dann die oben genannte URL in diese Registerkarte kopieren/einf\u00fcgen.\n\nWenn der Vorgang abgeschlossen ist, kehre hierher zur\u00fcck und gib den Autorisierungscode von der URL \"com.simplisafe.mobile\" ein." } diff --git a/homeassistant/components/simplisafe/translations/el.json b/homeassistant/components/simplisafe/translations/el.json index d4cd15d93f0..896c5bb8909 100644 --- a/homeassistant/components/simplisafe/translations/el.json +++ b/homeassistant/components/simplisafe/translations/el.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "\u0391\u03c5\u03c4\u03cc\u03c2 \u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 SimpliSafe \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7.", - "email_2fa_timed_out": "\u03a4\u03bf \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03ad\u03bb\u03b7\u03be\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b1\u03bd\u03b1\u03bc\u03bf\u03bd\u03ae \u03b3\u03b9\u03b1 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b4\u03cd\u03bf \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd \u03c0\u03bf\u03c5 \u03b2\u03b1\u03c3\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03b5 email.", "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", "wrong_account": "\u03a4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 \u03c0\u03bf\u03c5 \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b4\u03b5\u03bd \u03c4\u03b1\u03b9\u03c1\u03b9\u03ac\u03b6\u03bf\u03c5\u03bd \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc SimpliSafe." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "\u039f\u03b9 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03af \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2 SimpliSafe \u03ad\u03c7\u03bf\u03c5\u03bd \u03bc\u03ae\u03ba\u03bf\u03c2 45 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03b5\u03c2", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, - "progress": { - "email_2fa": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b4\u03cd\u03bf \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd\n\u03c0\u03bf\u03c5 \u03c3\u03b1\u03c2 \u03b5\u03c3\u03c4\u03ac\u03bb\u03b7 \u03bc\u03ad\u03c3\u03c9 email." - }, "step": { - "reauth_confirm": { - "data": { - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" - }, - "description": "\u0397 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03ae \u03c3\u03b1\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03bb\u03ae\u03be\u03b5\u03b9 \u03ae \u03b1\u03bd\u03b1\u03ba\u03bb\u03b7\u03b8\u03b5\u03af. \u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03ae\u03c2 \u03c3\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2.", - "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" - }, - "sms_2fa": { - "data": { - "code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2" - }, - "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b4\u03cd\u03bf \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd \u03c0\u03bf\u03c5 \u03c3\u03b1\u03c2 \u03b5\u03c3\u03c4\u03ac\u03bb\u03b7 \u03bc\u03ad\u03c3\u03c9 SMS." - }, "user": { "data": { - "auth_code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2", - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "username": "Email" + "auth_code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2" }, "description": "\u03a4\u03bf SimpliSafe \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03c4\u03bf Home Assistant \u03bc\u03ad\u03c3\u03c9 \u03c4\u03b7\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 SimpliSafe web. \u039b\u03cc\u03b3\u03c9 \u03c4\u03b5\u03c7\u03bd\u03b9\u03ba\u03ce\u03bd \u03c0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03ce\u03bd, \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03ad\u03bd\u03b1 \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03bf \u03b2\u03ae\u03bc\u03b1 \u03c3\u03c4\u03bf \u03c4\u03ad\u03bb\u03bf\u03c2 \u03b1\u03c5\u03c4\u03ae\u03c2 \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b1\u03b4\u03b9\u03ba\u03b1\u03c3\u03af\u03b1\u03c2- \u03b2\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03ad\u03c7\u03b5\u03c4\u03b5 \u03b4\u03b9\u03b1\u03b2\u03ac\u03c3\u03b5\u03b9 \u03c4\u03b7\u03bd [\u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7]({docs_url}) \u03c0\u03c1\u03b9\u03bd \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5.\n\n1. \u039a\u03ac\u03bd\u03c4\u03b5 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03bf [\u03b5\u03b4\u03ce]({url}) \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b1\u03bd\u03bf\u03af\u03be\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae SimpliSafe web \u03ba\u03b1\u03b9 \u03bd\u03b1 \u03b5\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03ac \u03c3\u03b1\u03c2.\n\n2. \u038c\u03c4\u03b1\u03bd \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03c9\u03b8\u03b5\u03af \u03b7 \u03b4\u03b9\u03b1\u03b4\u03b9\u03ba\u03b1\u03c3\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2, \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c8\u03c4\u03b5 \u03b5\u03b4\u03ce \u03ba\u03b1\u03b9 \u03b5\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9 \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2." } diff --git a/homeassistant/components/simplisafe/translations/en.json b/homeassistant/components/simplisafe/translations/en.json index 60e641a597c..724e3b5af28 100644 --- a/homeassistant/components/simplisafe/translations/en.json +++ b/homeassistant/components/simplisafe/translations/en.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "This SimpliSafe account is already in use.", - "email_2fa_timed_out": "Timed out while waiting for email-based two-factor authentication.", "reauth_successful": "Re-authentication was successful", "wrong_account": "The user credentials provided do not match this SimpliSafe account." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "SimpliSafe authorization codes are 45 characters in length", "unknown": "Unexpected error" }, - "progress": { - "email_2fa": "Check your email for a verification link from Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Password" - }, - "description": "Please re-enter the password for {username}.", - "title": "Reauthenticate Integration" - }, - "sms_2fa": { - "data": { - "code": "Code" - }, - "description": "Input the two-factor authentication code sent to you via SMS." - }, "user": { "data": { - "auth_code": "Authorization Code", - "password": "Password", - "username": "Username" + "auth_code": "Authorization Code" }, "description": "SimpliSafe authenticates users via its web app. Due to technical limitations, there is a manual step at the end of this process; please ensure that you read the [documentation](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) before starting.\n\nWhen you are ready, click [here]({url}) to open the SimpliSafe web app and input your credentials. If you've already logged into SimpliSafe in your browser, you may want to open a new tab, then copy/paste the above URL into that tab.\n\nWhen the process is complete, return here and input the authorization code from the `com.simplisafe.mobile` URL." } diff --git a/homeassistant/components/simplisafe/translations/es-419.json b/homeassistant/components/simplisafe/translations/es-419.json index 868d4d4f53d..8001cd18849 100644 --- a/homeassistant/components/simplisafe/translations/es-419.json +++ b/homeassistant/components/simplisafe/translations/es-419.json @@ -2,14 +2,6 @@ "config": { "abort": { "already_configured": "Esta cuenta SimpliSafe ya est\u00e1 en uso." - }, - "step": { - "user": { - "data": { - "password": "Contrase\u00f1a", - "username": "Direcci\u00f3n de correo electr\u00f3nico" - } - } } }, "options": { diff --git a/homeassistant/components/simplisafe/translations/es.json b/homeassistant/components/simplisafe/translations/es.json index 38936c22ec2..3da88d4b576 100644 --- a/homeassistant/components/simplisafe/translations/es.json +++ b/homeassistant/components/simplisafe/translations/es.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "Esta cuenta SimpliSafe ya est\u00e1 en uso.", - "email_2fa_timed_out": "Se agot\u00f3 el tiempo de espera para la autenticaci\u00f3n de dos factores basada en correo electr\u00f3nico.", "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente", "wrong_account": "Las credenciales de usuario proporcionadas no coinciden con esta cuenta de SimpliSafe." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "Los c\u00f3digos de autorizaci\u00f3n de SimpliSafe tienen 45 caracteres de longitud", "unknown": "Error inesperado" }, - "progress": { - "email_2fa": "Revisa tu correo electr\u00f3nico para obtener un enlace de verificaci\u00f3n de Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Contrase\u00f1a" - }, - "description": "Por favor, vuelve a introducir la contrase\u00f1a de {username}", - "title": "Volver a autenticar la integraci\u00f3n" - }, - "sms_2fa": { - "data": { - "code": "C\u00f3digo" - }, - "description": "Introduce el c\u00f3digo de autenticaci\u00f3n de dos factores que se te envi\u00f3 por SMS." - }, "user": { "data": { - "auth_code": "C\u00f3digo de Autorizaci\u00f3n", - "password": "Contrase\u00f1a", - "username": "Nombre de usuario" + "auth_code": "C\u00f3digo de Autorizaci\u00f3n" }, "description": "SimpliSafe autentica a los usuarios a trav\u00e9s de su aplicaci\u00f3n web. Debido a limitaciones t\u00e9cnicas, existe un paso manual al final de este proceso; por favor, aseg\u00farate de leer la [documentaci\u00f3n](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) antes de comenzar. \n\nCuando est\u00e9s listo, haz clic [aqu\u00ed]({url}) para abrir la aplicaci\u00f3n web SimpliSafe e introduce tus credenciales. Si ya iniciaste sesi\u00f3n en SimpliSafe en tu navegador, es posible que desees abrir una nueva pesta\u00f1a y luego copiar/pegar la URL anterior en esa pesta\u00f1a. \n\nCuando se complete el proceso, regresa aqu\u00ed e introduce el c\u00f3digo de autorizaci\u00f3n de la URL `com.simplisafe.mobile`." } diff --git a/homeassistant/components/simplisafe/translations/et.json b/homeassistant/components/simplisafe/translations/et.json index d1403a88303..1c1f65cd51d 100644 --- a/homeassistant/components/simplisafe/translations/et.json +++ b/homeassistant/components/simplisafe/translations/et.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "See SimpliSafe'i konto on juba kasutusel.", - "email_2fa_timed_out": "Meilip\u00f5hise kahefaktorilise autentimise ajal\u00f5pp.", "reauth_successful": "Taastuvastamine \u00f5nnestus", "wrong_account": "Esitatud kasutaja mandaadid ei \u00fchti selle SimpliSafe kontoga." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "SimpliSafe autoriseerimiskoodid on 45 t\u00e4hem\u00e4rki pikad.", "unknown": "Tundmatu viga" }, - "progress": { - "email_2fa": "Kontrolli oma meili Simplisafe'i kinnituslingi saamiseks." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Salas\u00f5na" - }, - "description": "Taassisesta salas\u00f5na kasutajanimele {username}.", - "title": "Taastuvasta SimpliSafe'i konto" - }, - "sms_2fa": { - "data": { - "code": "Kood" - }, - "description": "Sisesta SMS-iga saadetud kahefaktoriline autentimiskood." - }, "user": { "data": { - "auth_code": "Tuvastuskood", - "password": "Salas\u00f5na", - "username": "Kasutajanimi" + "auth_code": "Tuvastuskood" }, "description": "SimpliSafe autentib kasutajad veebirakenduse kaudu. Tehniliste piirangute t\u00f5ttu on selle protsessi l\u00f5pus manuaalne samm; palun lugege enne alustamist kindlasti [dokumentatsiooni](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code).\n\nKui olete valmis, kl\u00f5psake [siin]({url}), et avada SimpliSafe veebirakendus ja sisestada oma volitused. Kui olete juba SimpliSafe'ile oma brauseris sisse loginud, siis avage uus vahekaart ja kopeerige/liidke \u00fclaltoodud URL sellesse vahekaarti.\n\nKui protsess on l\u00f5pule viidud, naaske siia ja sisestage autoriseerimiskood `com.simplisafe.mobile` URL-i." } diff --git a/homeassistant/components/simplisafe/translations/fi.json b/homeassistant/components/simplisafe/translations/fi.json deleted file mode 100644 index 47a52e9a0c6..00000000000 --- a/homeassistant/components/simplisafe/translations/fi.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "password": "Salasana", - "username": "S\u00e4hk\u00f6postiosoite" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/fr.json b/homeassistant/components/simplisafe/translations/fr.json index ed355f5faf8..0f9eb3f2023 100644 --- a/homeassistant/components/simplisafe/translations/fr.json +++ b/homeassistant/components/simplisafe/translations/fr.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "Ce compte SimpliSafe est d\u00e9j\u00e0 utilis\u00e9.", - "email_2fa_timed_out": "D\u00e9lai d'attente de l'authentification \u00e0 deux facteurs par courriel expir\u00e9.", "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi", "wrong_account": "Les informations d'identification d'utilisateur fournies ne correspondent pas \u00e0 ce compte SimpliSafe." }, @@ -11,28 +10,10 @@ "invalid_auth": "Authentification non valide", "unknown": "Erreur inattendue" }, - "progress": { - "email_2fa": "Vous devriez recevoir un lien de v\u00e9rification par courriel envoy\u00e9 par Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Mot de passe" - }, - "description": "Veuillez de nouveau saisir le mot de passe pour {username}.", - "title": "R\u00e9-authentifier l'int\u00e9gration" - }, - "sms_2fa": { - "data": { - "code": "Code" - }, - "description": "Saisissez le code d'authentification \u00e0 deux facteurs qui vous a \u00e9t\u00e9 envoy\u00e9 par SMS." - }, "user": { "data": { - "auth_code": "Code d'autorisation", - "password": "Mot de passe", - "username": "Nom d'utilisateur" + "auth_code": "Code d'autorisation" } } } diff --git a/homeassistant/components/simplisafe/translations/he.json b/homeassistant/components/simplisafe/translations/he.json index 70ab1cff6ac..6cf7126ef70 100644 --- a/homeassistant/components/simplisafe/translations/he.json +++ b/homeassistant/components/simplisafe/translations/he.json @@ -6,26 +6,6 @@ "error": { "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" - }, - "step": { - "reauth_confirm": { - "data": { - "password": "\u05e1\u05d9\u05e1\u05de\u05d4" - }, - "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05de\u05d7\u05d3\u05e9 \u05d0\u05ea \u05d4\u05e1\u05d9\u05e1\u05de\u05d4 \u05e2\u05d1\u05d5\u05e8 {username}.", - "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1" - }, - "sms_2fa": { - "data": { - "code": "\u05e7\u05d5\u05d3" - } - }, - "user": { - "data": { - "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/hr.json b/homeassistant/components/simplisafe/translations/hr.json deleted file mode 100644 index addfc0cbe81..00000000000 --- a/homeassistant/components/simplisafe/translations/hr.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "password": "Lozinka", - "username": "Korisni\u010dko ime" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/hu.json b/homeassistant/components/simplisafe/translations/hu.json index d908785a266..1d2f4842c76 100644 --- a/homeassistant/components/simplisafe/translations/hu.json +++ b/homeassistant/components/simplisafe/translations/hu.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "Ez a SimpliSafe-fi\u00f3k m\u00e1r haszn\u00e1latban van.", - "email_2fa_timed_out": "Az e-mail alap\u00fa k\u00e9tfaktoros hiteles\u00edt\u00e9sre val\u00f3 v\u00e1rakoz\u00e1s ideje lej\u00e1rt", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", "wrong_account": "A megadott felhaszn\u00e1l\u00f3i hiteles\u00edt\u0151 adatok nem j\u00f3k ehhez a SimpliSafe fi\u00f3khoz." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "A SimpliSafe enged\u00e9lyez\u00e9si k\u00f3dok 45 karakter hossz\u00faak.", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, - "progress": { - "email_2fa": "Ellen\u0151rizze az e-mailj\u00e9t a Simplisafe \u00e1ltal k\u00fcld\u00f6tt ellen\u0151rz\u0151 link\u00e9rt." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Jelsz\u00f3" - }, - "description": "K\u00e9rem, adja meg ism\u00e9t {username} jelszav\u00e1t.", - "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" - }, - "sms_2fa": { - "data": { - "code": "K\u00f3d" - }, - "description": "\u00cdrja be a k\u00e9tfaktoros hiteles\u00edt\u00e9si k\u00f3dot, amelyet SMS-ben k\u00fcldtek \u00d6nnek." - }, "user": { "data": { - "auth_code": "Enged\u00e9lyez\u00e9si k\u00f3d", - "password": "Jelsz\u00f3", - "username": "Felhaszn\u00e1l\u00f3n\u00e9v" + "auth_code": "Enged\u00e9lyez\u00e9si k\u00f3d" }, "description": "A SimpliSafe a webes alkalmaz\u00e1son kereszt\u00fcl hiteles\u00edti a felhaszn\u00e1l\u00f3kat. A technikai korl\u00e1toz\u00e1sok miatt a folyamat v\u00e9g\u00e9n van egy manu\u00e1lis l\u00e9p\u00e9s; k\u00e9rj\u00fck, hogy a kezd\u00e9s el\u0151tt olvassa el a [dokument\u00e1ci\u00f3t](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code).\n\nHa k\u00e9szen \u00e1ll, kattintson [ide]({url}) a SimpliSafe webes alkalmaz\u00e1s megnyit\u00e1s\u00e1hoz \u00e9s adja meg a hiteles\u00edt\u0151 adatokat. Ha m\u00e1r bejelentkezett a SimpliSafe rendszerbe a b\u00f6ng\u00e9sz\u0151ben, akkor \u00e9rdemes egy \u00faj lapot nyitni, majd a fenti URL-t bem\u00e1solni/beilleszteni abba a lapba.\n\nHa a folyamat befejez\u0151d\u00f6tt, t\u00e9rjen vissza ide, \u00e9s adja meg az enged\u00e9lyez\u00e9si k\u00f3dot a `com.simplisafe.mobile` URL-r\u0151l." } diff --git a/homeassistant/components/simplisafe/translations/id.json b/homeassistant/components/simplisafe/translations/id.json index 0bb888eceed..66614fffdb4 100644 --- a/homeassistant/components/simplisafe/translations/id.json +++ b/homeassistant/components/simplisafe/translations/id.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "Akun SimpliSafe ini sudah digunakan.", - "email_2fa_timed_out": "Tenggang waktu habis ketika menunggu autentikasi dua faktor berbasis email.", "reauth_successful": "Autentikasi ulang berhasil", "wrong_account": "Kredensial pengguna yang diberikan tidak cocok dengan akun SimpliSafe ini." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "Panjang kode otorisasi SimpliSafe adalah 45 karakter", "unknown": "Kesalahan yang tidak diharapkan" }, - "progress": { - "email_2fa": "Periksa email Anda untuk tautan verifikasi dari Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Kata Sandi" - }, - "description": "Masukkan kembali kata sandi untuk {username}.", - "title": "Autentikasi Ulang Integrasi" - }, - "sms_2fa": { - "data": { - "code": "Kode" - }, - "description": "Masukkan kode autentikasi dua faktor yang dikirimkan kepada Anda melalui SMS." - }, "user": { "data": { - "auth_code": "Kode Otorisasi", - "password": "Kata Sandi", - "username": "Nama Pengguna" + "auth_code": "Kode Otorisasi" }, "description": "SimpliSafe mengautentikasi pengguna melalui aplikasi webnya. Karena keterbatasan teknis, ada langkah manual di akhir proses ini; pastikan bahwa Anda membaca [dokumentasi] (http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) sebelum memulai.\n\nJika sudah siap, klik [di sini]({url}) untuk membuka aplikasi web SimpliSafe dan memasukkan kredensial Anda. Jika Anda sedang masuk ke SimpliSafe di browser, Anda mungkin harus membuka tab baru, kemudian menyalin-tempel URL di atas di tab baru tersebut.\n\nSetelah proses selesai, kembali ke sini dan masukkan kode otorisasi dari URL `com.simplisafe.mobile`." } diff --git a/homeassistant/components/simplisafe/translations/it.json b/homeassistant/components/simplisafe/translations/it.json index dda07835a81..b261ce66ade 100644 --- a/homeassistant/components/simplisafe/translations/it.json +++ b/homeassistant/components/simplisafe/translations/it.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "Questo account SimpliSafe \u00e8 gi\u00e0 in uso.", - "email_2fa_timed_out": "Timeout durante l'attesa dell'autenticazione a due fattori basata su email.", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", "wrong_account": "Le credenziali utente fornite non corrispondono a questo account SimpliSafe." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "I codici di autorizzazione SimpliSafe sono lunghi 45 caratteri", "unknown": "Errore imprevisto" }, - "progress": { - "email_2fa": "Verifica la presenza nella tua email di un collegamento di verifica da Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Password" - }, - "description": "Digita nuovamente la password per {username}.", - "title": "Autentica nuovamente l'integrazione" - }, - "sms_2fa": { - "data": { - "code": "Codice" - }, - "description": "Digita il codice di autenticazione a due fattori che ti \u00e8 stato inviato tramite SMS." - }, "user": { "data": { - "auth_code": "Codice di autorizzazione", - "password": "Password", - "username": "Nome utente" + "auth_code": "Codice di autorizzazione" }, "description": "SimpliSafe autentica gli utenti tramite la sua app web. A causa di limitazioni tecniche, alla fine di questo processo \u00e8 previsto un passaggio manuale; assicurati, prima di iniziare, di leggere la [documentazione](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code). \n\nQuando sei pronto, fai clic [qui]({url}) per aprire l'app web di SimpliSafe e inserisci le tue credenziali. Se hai gi\u00e0 effettuato l'accesso a SimpliSafe nel tuo browser, puoi aprire una nuova scheda e copiare/incollare l'URL sopra indicato in quella nuova scheda.\n\nAl termine del processo, ritorna qui e inserisci il codice di autorizzazione dall'URL `com.simplisafe.mobile`." } diff --git a/homeassistant/components/simplisafe/translations/ja.json b/homeassistant/components/simplisafe/translations/ja.json index c448222f286..55a0367b54d 100644 --- a/homeassistant/components/simplisafe/translations/ja.json +++ b/homeassistant/components/simplisafe/translations/ja.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "\u3053\u306eSimpliSafe account\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002", - "email_2fa_timed_out": "\u96fb\u5b50\u30e1\u30fc\u30eb\u306b\u3088\u308b2\u8981\u7d20\u8a8d\u8a3c\u306e\u5f85\u6a5f\u4e2d\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f\u3002", "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f", "wrong_account": "\u63d0\u4f9b\u3055\u308c\u305f\u30e6\u30fc\u30b6\u30fc\u8a8d\u8a3c\u60c5\u5831\u306f\u3001\u3053\u306eSimpliSafe\u30a2\u30ab\u30a6\u30f3\u30c8\u3068\u4e00\u81f4\u3057\u307e\u305b\u3093\u3002" }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "SimpliSafe\u306e\u8a8d\u8a3c\u30b3\u30fc\u30c9\u306f45\u6587\u5b57\u3067\u3059", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, - "progress": { - "email_2fa": "Simplisafe\u304b\u3089\u306e\u78ba\u8a8d\u30ea\u30f3\u30af\u304c\u306a\u3044\u304b\u30e1\u30fc\u30eb\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002" - }, "step": { - "reauth_confirm": { - "data": { - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" - }, - "description": "\u30a2\u30af\u30bb\u30b9\u306e\u6709\u52b9\u671f\u9650\u304c\u5207\u308c\u3066\u3044\u308b\u304b\u3001\u53d6\u308a\u6d88\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5165\u529b\u3057\u3066\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u30ea\u30f3\u30af\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "\u7d71\u5408\u306e\u518d\u8a8d\u8a3c" - }, - "sms_2fa": { - "data": { - "code": "\u30b3\u30fc\u30c9" - }, - "description": "SMS\u3067\u9001\u3089\u308c\u3066\u304d\u305f2\u8981\u7d20\u8a8d\u8a3c\u30b3\u30fc\u30c9\u3092\u5165\u529b\u3057\u307e\u3059\u3002" - }, "user": { "data": { - "auth_code": "\u8a8d\u8a3c\u30b3\u30fc\u30c9", - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "username": "E\u30e1\u30fc\u30eb" + "auth_code": "\u8a8d\u8a3c\u30b3\u30fc\u30c9" }, "description": "SimpliSafe \u306f\u3001Web \u30a2\u30d7\u30ea\u3092\u4ecb\u3057\u3066\u30e6\u30fc\u30b6\u30fc\u3092\u8a8d\u8a3c\u3057\u307e\u3059\u3002\u6280\u8853\u7684\u306a\u5236\u9650\u306b\u3088\u308a\u3001\u3053\u306e\u30d7\u30ed\u30bb\u30b9\u306e\u6700\u5f8c\u306b\u624b\u52d5\u306e\u624b\u9806\u304c\u3042\u308a\u307e\u3059\u3002\u958b\u59cb\u3059\u308b\u524d\u306b\u3001[\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code)\u3092\u5fc5\u305a\u304a\u8aad\u307f\u304f\u3060\u3055\u3044\u3002 \n\n\u6e96\u5099\u304c\u3067\u304d\u305f\u3089\u3001[\u3053\u3053]( {url} ) \u3092\u30af\u30ea\u30c3\u30af\u3057\u3066 SimpliSafe Web \u30a2\u30d7\u30ea\u3092\u958b\u304d\u3001\u8cc7\u683c\u60c5\u5831\u3092\u5165\u529b\u3057\u307e\u3059\u3002\u30d6\u30e9\u30a6\u30b6\u3067\u65e2\u306b SimpliSafe \u306b\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u65b0\u3057\u3044\u30bf\u30d6\u3092\u958b\u304d\u3001\u4e0a\u8a18\u306e URL \u3092\u30b3\u30d4\u30fc\u3057\u3066\u305d\u306e\u30bf\u30d6\u306b\u8cbc\u308a\u4ed8\u3051\u307e\u3059\u3002 \n\n\u51e6\u7406\u304c\u5b8c\u4e86\u3057\u305f\u3089\u3001\u3053\u3053\u306b\u623b\u308a\u3001\u300ccom.simplisafe.mobile\u300d\u306e URL \u304b\u3089\u8a8d\u8a3c\u30b3\u30fc\u30c9\u3092\u5165\u529b\u3057\u307e\u3059\u3002" } diff --git a/homeassistant/components/simplisafe/translations/ko.json b/homeassistant/components/simplisafe/translations/ko.json index 4cbc24533e0..2c04b58d413 100644 --- a/homeassistant/components/simplisafe/translations/ko.json +++ b/homeassistant/components/simplisafe/translations/ko.json @@ -7,24 +7,6 @@ "error": { "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" - }, - "progress": { - "email_2fa": "\uc774\uba54\uc77c\uc5d0\uc11c Simplisafe\uc758 \uc778\uc99d \ub9c1\ud06c\ub97c \ud655\uc778\ud558\uc2ed\uc2dc\uc624." - }, - "step": { - "reauth_confirm": { - "data": { - "password": "\ube44\ubc00\ubc88\ud638" - }, - "description": "\uc561\uc138\uc2a4 \ud1a0\ud070\uc774 \ub9cc\ub8cc\ub418\uc5c8\uac70\ub098 \ud574\uc9c0\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \uacc4\uc815\uc744 \ub2e4\uc2dc \uc5f0\uacb0\ud558\ub824\uba74 \ube44\ubc00\ubc88\ud638\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694.", - "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" - }, - "user": { - "data": { - "password": "\ube44\ubc00\ubc88\ud638", - "username": "\uc774\uba54\uc77c" - } - } } }, "options": { diff --git a/homeassistant/components/simplisafe/translations/lb.json b/homeassistant/components/simplisafe/translations/lb.json index e9034565e93..33ec46a86f3 100644 --- a/homeassistant/components/simplisafe/translations/lb.json +++ b/homeassistant/components/simplisafe/translations/lb.json @@ -7,21 +7,6 @@ "error": { "invalid_auth": "Ong\u00eblteg Authentifikatioun", "unknown": "Onerwaarte Feeler" - }, - "step": { - "reauth_confirm": { - "data": { - "password": "Passwuert" - }, - "description": "D\u00e4in Acc\u00e8s Jeton as ofgelaf oder gouf revok\u00e9iert. G\u00ebff d\u00e4i Passwuert an fir d\u00e4i Kont fr\u00ebsch ze verbannen.", - "title": "Integratioun re-authentifiz\u00e9ieren" - }, - "user": { - "data": { - "password": "Passwuert", - "username": "E-Mail" - } - } } }, "options": { diff --git a/homeassistant/components/simplisafe/translations/nl.json b/homeassistant/components/simplisafe/translations/nl.json index b24bde70fac..d7da5fbd4ce 100644 --- a/homeassistant/components/simplisafe/translations/nl.json +++ b/homeassistant/components/simplisafe/translations/nl.json @@ -2,35 +2,14 @@ "config": { "abort": { "already_configured": "Dit SimpliSafe-account is al in gebruik.", - "email_2fa_timed_out": "Timed out tijdens het wachten op email-gebaseerde twee-factor authenticatie.", "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, - "progress": { - "email_2fa": "Controleer uw e-mail voor een verificatielink van Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Wachtwoord" - }, - "description": "Voer het wachtwoord voor {username} opnieuw in.", - "title": "Integratie herauthenticeren" - }, - "sms_2fa": { - "data": { - "code": "Code" - }, - "description": "Voer de twee-factor authenticatiecode in die u heeft ontvangen via SMS" - }, "user": { - "data": { - "password": "Wachtwoord", - "username": "Gebruikersnaam" - }, "description": "Voer uw gebruikersnaam en wachtwoord in." } } diff --git a/homeassistant/components/simplisafe/translations/no.json b/homeassistant/components/simplisafe/translations/no.json index 5d6f81c4444..3ffa0f65387 100644 --- a/homeassistant/components/simplisafe/translations/no.json +++ b/homeassistant/components/simplisafe/translations/no.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "Denne SimpliSafe-kontoen er allerede i bruk.", - "email_2fa_timed_out": "Tidsavbrudd mens du ventet p\u00e5 e-postbasert tofaktorautentisering.", "reauth_successful": "Re-autentisering var vellykket", "wrong_account": "Oppgitt brukerlegitimasjon samsvarer ikke med denne SimpliSafe-kontoen." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "SimpliSafe-autorisasjonskoder er p\u00e5 45 tegn", "unknown": "Uventet feil" }, - "progress": { - "email_2fa": "Sjekk e-posten din for en bekreftelseslenke fra Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Passord" - }, - "description": "Vennligst skriv inn passordet for {username} p\u00e5 nytt.", - "title": "Godkjenne integrering p\u00e5 nytt" - }, - "sms_2fa": { - "data": { - "code": "Kode" - }, - "description": "Skriv inn tofaktorautentiseringskoden sendt til deg via SMS." - }, "user": { "data": { - "auth_code": "Autorisasjonskode", - "password": "Passord", - "username": "Brukernavn" + "auth_code": "Autorisasjonskode" }, "description": "SimpliSafe autentiserer brukere via sin nettapp. P\u00e5 grunn av tekniske begrensninger er det et manuelt trinn p\u00e5 slutten av denne prosessen; s\u00f8rg for at du leser [dokumentasjonen](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) f\u00f8r du starter. \n\n N\u00e5r du er klar, klikk [her]( {url} ) for \u00e5 \u00e5pne SimpliSafe-nettappen og angi legitimasjonen din. Hvis du allerede har logget p\u00e5 SimpliSafe i nettleseren din, kan det v\u00e6re lurt \u00e5 \u00e5pne en ny fane, og deretter kopiere/lime inn URL-en ovenfor i den fanen. \n\n N\u00e5r prosessen er fullf\u00f8rt, g\u00e5 tilbake hit og skriv inn autorisasjonskoden fra `com.simplisafe.mobile` URL." } diff --git a/homeassistant/components/simplisafe/translations/pl.json b/homeassistant/components/simplisafe/translations/pl.json index 0af42cadfd3..cbdba2d95f2 100644 --- a/homeassistant/components/simplisafe/translations/pl.json +++ b/homeassistant/components/simplisafe/translations/pl.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "To konto SimpliSafe jest ju\u017c w u\u017cyciu", - "email_2fa_timed_out": "Przekroczono limit czasu oczekiwania na e-mailowe uwierzytelnianie dwusk\u0142adnikowe.", "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119", "wrong_account": "Podane dane uwierzytelniaj\u0105ce u\u017cytkownika nie pasuj\u0105 do tego konta SimpliSafe." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "Kody autoryzacji SimpliSafe maj\u0105 d\u0142ugo\u015b\u0107 45 znak\u00f3w", "unknown": "Nieoczekiwany b\u0142\u0105d" }, - "progress": { - "email_2fa": "Sprawd\u017a e-mail z linkiem weryfikacyjnym od Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Has\u0142o" - }, - "description": "Wprowad\u017a ponownie has\u0142o dla u\u017cytkownika {username}.", - "title": "Ponownie uwierzytelnij integracj\u0119" - }, - "sms_2fa": { - "data": { - "code": "Kod" - }, - "description": "Wprowad\u017a kod uwierzytelniania dwusk\u0142adnikowego, kt\u00f3ry zosta\u0142 wys\u0142any do Ciebie SMS-em." - }, "user": { "data": { - "auth_code": "Kod autoryzacji", - "password": "Has\u0142o", - "username": "Nazwa u\u017cytkownika" + "auth_code": "Kod autoryzacji" }, "description": "SimpliSafe uwierzytelnia u\u017cytkownik\u00f3w za po\u015brednictwem swojej aplikacji internetowej. Ze wzgl\u0119du na ograniczenia techniczne na ko\u0144cu tego procesu znajduje si\u0119 r\u0119czny krok; upewnij si\u0119, \u017ce przeczyta\u0142e\u015b [dokumentacj\u0119](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) przed rozpocz\u0119ciem. \n\nGdy b\u0119dziesz ju\u017c gotowy, kliknij [tutaj]({url}), aby otworzy\u0107 aplikacj\u0119 internetow\u0105 SimpliSafe i wprowadzi\u0107 swoje dane uwierzytelniaj\u0105ce. Je\u015bli ju\u017c zalogowa\u0142e\u015b si\u0119 do Simplisafe w swojej przegl\u0105darce, mo\u017cesz otworzy\u0107 now\u0105 kart\u0119, a nast\u0119pnie skopiowa\u0107/wklei\u0107 powy\u017cszy adres URL.\n\nPo zako\u0144czeniu procesu wr\u00f3\u0107 tutaj i wprowad\u017a kod autoryzacji z adresu URL 'com.simplisafe.mobile'." } diff --git a/homeassistant/components/simplisafe/translations/pt-BR.json b/homeassistant/components/simplisafe/translations/pt-BR.json index 4dadf67720f..c5c0c399df4 100644 --- a/homeassistant/components/simplisafe/translations/pt-BR.json +++ b/homeassistant/components/simplisafe/translations/pt-BR.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "A conta j\u00e1 foi configurada", - "email_2fa_timed_out": "Expirou enquanto aguardava a autentica\u00e7\u00e3o de dois fatores enviada por e-mail.", "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida", "wrong_account": "As credenciais de usu\u00e1rio fornecidas n\u00e3o correspondem a esta conta SimpliSafe." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "Os c\u00f3digos de autoriza\u00e7\u00e3o SimpliSafe t\u00eam 45 caracteres", "unknown": "Erro inesperado" }, - "progress": { - "email_2fa": "Verifique seu e-mail para obter um link de verifica\u00e7\u00e3o do Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Senha" - }, - "description": "Por favor, digite novamente a senha para {username}.", - "title": "Reautenticar Integra\u00e7\u00e3o" - }, - "sms_2fa": { - "data": { - "code": "C\u00f3digo" - }, - "description": "Insira o c\u00f3digo de autentica\u00e7\u00e3o de dois fatores enviado a voc\u00ea via SMS." - }, "user": { "data": { - "auth_code": "C\u00f3digo de autoriza\u00e7\u00e3o", - "password": "Senha", - "username": "Usu\u00e1rio" + "auth_code": "C\u00f3digo de autoriza\u00e7\u00e3o" }, "description": "O SimpliSafe autentica os usu\u00e1rios por meio de seu aplicativo da web. Por limita\u00e7\u00f5es t\u00e9cnicas, existe uma etapa manual ao final deste processo; certifique-se de ler a [documenta\u00e7\u00e3o](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) antes de come\u00e7ar. \n\n Quando estiver pronto, clique [aqui]( {url} ) para abrir o aplicativo da web SimpliSafe e insira suas credenciais. Se voc\u00ea j\u00e1 fez login no SimpliSafe em seu navegador, voc\u00ea pode querer abrir uma nova guia e copiar/colar o URL acima nessa guia. \n\n Quando o processo estiver conclu\u00eddo, retorne aqui e insira o c\u00f3digo de autoriza\u00e7\u00e3o da URL `com.simplisafe.mobile`." } diff --git a/homeassistant/components/simplisafe/translations/pt.json b/homeassistant/components/simplisafe/translations/pt.json index ba84255bc9d..f7ccc23fedd 100644 --- a/homeassistant/components/simplisafe/translations/pt.json +++ b/homeassistant/components/simplisafe/translations/pt.json @@ -6,20 +6,6 @@ "error": { "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" - }, - "step": { - "reauth_confirm": { - "data": { - "password": "Palavra-passe" - }, - "title": "Reautenticar integra\u00e7\u00e3o" - }, - "user": { - "data": { - "password": "Palavra-passe", - "username": "Email" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/ro.json b/homeassistant/components/simplisafe/translations/ro.json deleted file mode 100644 index efbbe49f38a..00000000000 --- a/homeassistant/components/simplisafe/translations/ro.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "password": "Parola", - "username": "Adresa de email" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/ru.json b/homeassistant/components/simplisafe/translations/ru.json index 327cb296ff3..afebb27e5aa 100644 --- a/homeassistant/components/simplisafe/translations/ru.json +++ b/homeassistant/components/simplisafe/translations/ru.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", - "email_2fa_timed_out": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.", "wrong_account": "\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0443\u0447\u0451\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u044d\u0442\u043e\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 SimpliSafe." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "\u041a\u043e\u0434\u044b \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 SimpliSafe \u0441\u043e\u0441\u0442\u043e\u044f\u0442 \u0438\u0437 45 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, - "progress": { - "email_2fa": "\u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0441\u0432\u043e\u044e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0443\u044e \u043f\u043e\u0447\u0442\u0443 \u043d\u0430 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043e\u0442 Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" - }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f {username}.", - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" - }, - "sms_2fa": { - "data": { - "code": "\u041a\u043e\u0434" - }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0434 \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0412\u0430\u043c \u0432 SMS." - }, "user": { "data": { - "auth_code": "\u041a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + "auth_code": "\u041a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438" }, "description": "SimpliSafe \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u043e\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u0418\u0437-\u0437\u0430 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439, \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) \u043f\u0435\u0440\u0435\u0434 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438. \n\n\u041a\u043e\u0433\u0434\u0430 \u0412\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0433\u043e\u0442\u043e\u0432\u044b, \u043d\u0430\u0436\u043c\u0438\u0442\u0435 [\u0441\u044e\u0434\u0430]({url}), \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 SimpliSafe \u0438 \u0432\u0432\u0435\u0441\u0442\u0438 \u0441\u0432\u043e\u0438 \u0443\u0447\u0451\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435. \u0415\u0441\u043b\u0438 \u0412\u044b \u0443\u0436\u0435 \u0432\u043e\u0448\u043b\u0438 \u0432 SimpliSafe \u0432 \u0441\u0432\u043e\u0435\u043c \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u0432\u043a\u043b\u0430\u0434\u043a\u0443, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0432\u044b\u0448\u0435\u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 URL-\u0430\u0434\u0440\u0435\u0441.\n\n\u041a\u043e\u0433\u0434\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d, \u0432\u0435\u0440\u043d\u0438\u0442\u0435\u0441\u044c \u0441\u044e\u0434\u0430 \u0438 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 URL-\u0430\u0434\u0440\u0435\u0441\u0430 `com.simplisafe.mobile`." } diff --git a/homeassistant/components/simplisafe/translations/sk.json b/homeassistant/components/simplisafe/translations/sk.json index d1d3d1408a9..71a7aea5018 100644 --- a/homeassistant/components/simplisafe/translations/sk.json +++ b/homeassistant/components/simplisafe/translations/sk.json @@ -5,19 +5,6 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" - }, - "step": { - "reauth_confirm": { - "data": { - "password": "Heslo" - } - }, - "user": { - "data": { - "password": "Heslo", - "username": "Email" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/sl.json b/homeassistant/components/simplisafe/translations/sl.json index d98ad9c20c7..d78f80e074a 100644 --- a/homeassistant/components/simplisafe/translations/sl.json +++ b/homeassistant/components/simplisafe/translations/sl.json @@ -2,14 +2,6 @@ "config": { "abort": { "already_configured": "Ta ra\u010dun SimpliSafe je \u017ee v uporabi." - }, - "step": { - "user": { - "data": { - "password": "Geslo", - "username": "E-po\u0161tni naslov" - } - } } }, "options": { diff --git a/homeassistant/components/simplisafe/translations/sv.json b/homeassistant/components/simplisafe/translations/sv.json index a2d75f36697..87a013a68e9 100644 --- a/homeassistant/components/simplisafe/translations/sv.json +++ b/homeassistant/components/simplisafe/translations/sv.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "Det h\u00e4r SimpliSafe-kontot har redan konfigurerats.", - "email_2fa_timed_out": "Tidsgr\u00e4nsen tog slut i v\u00e4ntan p\u00e5 tv\u00e5faktorsautentisering", "reauth_successful": "\u00c5terautentisering lyckades", "wrong_account": "De angivna anv\u00e4ndaruppgifterna matchar inte detta SimpliSafe-konto." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "SimpliSafe auktoriseringskoder \u00e4r 45 tecken l\u00e5nga", "unknown": "Ov\u00e4ntat fel" }, - "progress": { - "email_2fa": "Kontrollera din e-post f\u00f6r en verifieringsl\u00e4nk fr\u00e5n Simplisafe." - }, "step": { - "reauth_confirm": { - "data": { - "password": "L\u00f6senord" - }, - "description": "Skriv om l\u00f6senordet f\u00f6r {username}", - "title": "\u00c5terautenticera integration" - }, - "sms_2fa": { - "data": { - "code": "Kod" - }, - "description": "Ange tv\u00e5faktorsautentiseringskoden som skickats till dig via SMS." - }, "user": { "data": { - "auth_code": "Auktoriseringskod", - "password": "L\u00f6senord", - "username": "E-postadress" + "auth_code": "Auktoriseringskod" }, "description": "SimpliSafe autentiserar anv\u00e4ndare via sin webbapp. P\u00e5 grund av tekniska begr\u00e4nsningar finns det ett manuellt steg i slutet av denna process; se till att du l\u00e4ser [dokumentationen](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) innan du b\u00f6rjar. \n\n N\u00e4r du \u00e4r redo klickar du [h\u00e4r]( {url} ) f\u00f6r att \u00f6ppna webbappen SimpliSafe och ange dina referenser. N\u00e4r processen \u00e4r klar, \u00e5terv\u00e4nd hit och mata in auktoriseringskoden fr\u00e5n SimpliSafe-webbappens URL." } diff --git a/homeassistant/components/simplisafe/translations/th.json b/homeassistant/components/simplisafe/translations/th.json deleted file mode 100644 index 84fcb89add1..00000000000 --- a/homeassistant/components/simplisafe/translations/th.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "username": "\u0e17\u0e35\u0e48\u0e2d\u0e22\u0e39\u0e48\u0e2d\u0e35\u0e40\u0e21\u0e25" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/tr.json b/homeassistant/components/simplisafe/translations/tr.json index f7073bb3df7..70f327e9967 100644 --- a/homeassistant/components/simplisafe/translations/tr.json +++ b/homeassistant/components/simplisafe/translations/tr.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "Bu SimpliSafe hesab\u0131 zaten kullan\u0131mda.", - "email_2fa_timed_out": "E-posta tabanl\u0131 iki fakt\u00f6rl\u00fc kimlik do\u011frulama i\u00e7in beklerken zaman a\u015f\u0131m\u0131na u\u011frad\u0131.", "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu", "wrong_account": "Sa\u011flanan kullan\u0131c\u0131 kimlik bilgileri bu SimpliSafe hesab\u0131yla e\u015fle\u015fmiyor." }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "SimpliSafe yetkilendirme kodlar\u0131 45 karakter uzunlu\u011fundad\u0131r", "unknown": "Beklenmeyen hata" }, - "progress": { - "email_2fa": "Simplisafe'den bir do\u011frulama ba\u011flant\u0131s\u0131 i\u00e7in e-postan\u0131z\u0131 kontrol edin." - }, "step": { - "reauth_confirm": { - "data": { - "password": "Parola" - }, - "description": "L\u00fctfen {username} \u015fifresini tekrar girin.", - "title": "Entegrasyonu Yeniden Do\u011frula" - }, - "sms_2fa": { - "data": { - "code": "Kod" - }, - "description": "Size SMS ile g\u00f6nderilen iki fakt\u00f6rl\u00fc do\u011frulama kodunu girin." - }, "user": { "data": { - "auth_code": "Yetkilendirme Kodu", - "password": "Parola", - "username": "Kullan\u0131c\u0131 Ad\u0131" + "auth_code": "Yetkilendirme Kodu" }, "description": "SimpliSafe, web uygulamas\u0131 arac\u0131l\u0131\u011f\u0131yla kullan\u0131c\u0131lar\u0131n kimli\u011fini do\u011frular. Teknik s\u0131n\u0131rlamalar nedeniyle bu i\u015flemin sonunda manuel bir ad\u0131m vard\u0131r; l\u00fctfen ba\u015flamadan \u00f6nce [belgeleri](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) okudu\u011funuzdan emin olun. \n\n Haz\u0131r oldu\u011funuzda SimpliSafe web uygulamas\u0131n\u0131 a\u00e7mak ve kimlik bilgilerinizi girmek i\u00e7in [buray\u0131]( {url} ) t\u0131klay\u0131n. Taray\u0131c\u0131n\u0131zda SimpliSafe'e zaten giri\u015f yapt\u0131ysan\u0131z, yeni bir sekme a\u00e7mak ve ard\u0131ndan yukar\u0131daki URL'yi o sekmeye kopyalay\u0131p/yap\u0131\u015ft\u0131rmak isteyebilirsiniz. \n\n \u0130\u015flem tamamland\u0131\u011f\u0131nda buraya d\u00f6n\u00fcn ve \"com.simplisafe.mobile\" URL'sinden yetkilendirme kodunu girin." } diff --git a/homeassistant/components/simplisafe/translations/uk.json b/homeassistant/components/simplisafe/translations/uk.json index 1df0e00c6cc..6d86c8572b4 100644 --- a/homeassistant/components/simplisafe/translations/uk.json +++ b/homeassistant/components/simplisafe/translations/uk.json @@ -7,21 +7,6 @@ "error": { "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f.", "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" - }, - "step": { - "reauth_confirm": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" - }, - "description": "\u0412\u0430\u0448 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0443 \u0437\u0430\u043a\u0456\u043d\u0447\u0438\u0432\u0441\u044f \u0430\u0431\u043e \u0431\u0443\u0432 \u0430\u043d\u0443\u043b\u044c\u043e\u0432\u0430\u043d\u0438\u0439. \u0412\u0432\u0435\u0434\u0456\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c, \u0449\u043e\u0431 \u0437\u0430\u043d\u043e\u0432\u043e \u043f\u0440\u0438\u0432'\u044f\u0437\u0430\u0442\u0438 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u0438\u0439 \u0437\u0430\u043f\u0438\u0441.", - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0432\u0430\u0442\u0438 \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u044e" - }, - "user": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "username": "\u0410\u0434\u0440\u0435\u0441\u0430 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0457 \u043f\u043e\u0448\u0442\u0438" - } - } } }, "issues": { diff --git a/homeassistant/components/simplisafe/translations/zh-Hans.json b/homeassistant/components/simplisafe/translations/zh-Hans.json index d28951425ec..2750a3ae308 100644 --- a/homeassistant/components/simplisafe/translations/zh-Hans.json +++ b/homeassistant/components/simplisafe/translations/zh-Hans.json @@ -2,14 +2,6 @@ "config": { "error": { "invalid_auth": "\u65e0\u6548\u7684\u8eab\u4efd\u9a8c\u8bc1" - }, - "step": { - "user": { - "data": { - "password": "\u5bc6\u7801", - "username": "\u7535\u5b50\u90ae\u4ef6\u5730\u5740" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/zh-Hant.json b/homeassistant/components/simplisafe/translations/zh-Hant.json index 199e96e6792..a7e31e74341 100644 --- a/homeassistant/components/simplisafe/translations/zh-Hant.json +++ b/homeassistant/components/simplisafe/translations/zh-Hant.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "\u6b64 SimpliSafe \u5e33\u865f\u5df2\u88ab\u4f7f\u7528\u3002", - "email_2fa_timed_out": "\u7b49\u5f85\u5169\u6b65\u9a5f\u9a57\u8b49\u78bc\u90f5\u4ef6\u903e\u6642\u3002", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f", "wrong_account": "\u6240\u4ee5\u63d0\u4f9b\u7684\u6191\u8b49\u8207 Simplisafe \u5e33\u865f\u4e0d\u7b26\u3002" }, @@ -12,28 +11,10 @@ "invalid_auth_code_length": "SimpliSafe \u8a8d\u8b49\u78bc\u70ba 45 \u500b\u5b57\u5143\u9577", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, - "progress": { - "email_2fa": "\u8f38\u5165\u90f5\u4ef6\u6240\u6536\u5230 \u7684Simplisafe \u9a57\u8b49\u9023\u7d50\u3002" - }, "step": { - "reauth_confirm": { - "data": { - "password": "\u5bc6\u78bc" - }, - "description": "\u8acb\u8f38\u5165\u5e33\u865f {username} \u5bc6\u78bc\u3002", - "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" - }, - "sms_2fa": { - "data": { - "code": "\u9a57\u8b49\u78bc" - }, - "description": "\u8f38\u5165\u7c21\u8a0a\u6240\u6536\u5230\u7684\u5169\u6b65\u9a5f\u9a57\u8b49\u78bc\u3002" - }, "user": { "data": { - "auth_code": "\u8a8d\u8b49\u78bc", - "password": "\u5bc6\u78bc", - "username": "\u4f7f\u7528\u8005\u540d\u7a31" + "auth_code": "\u8a8d\u8b49\u78bc" }, "description": "SimpliSafe \u70ba\u900f\u904e Web App \u65b9\u5f0f\u7684\u8a8d\u8b49\u5176\u4f7f\u7528\u8005\u3002\u7531\u65bc\u6280\u8853\u9650\u5236\u3001\u65bc\u6b64\u904e\u7a0b\u7d50\u675f\u6642\u5c07\u6703\u6709\u4e00\u6b65\u624b\u52d5\u968e\u6bb5\uff1b\u65bc\u958b\u59cb\u524d\u3001\u8acb\u78ba\u5b9a\u53c3\u95b1 [\u76f8\u95dc\u6587\u4ef6](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code)\u3002\n\n\u6e96\u5099\u5c31\u7dd2\u5f8c\u3001\u9ede\u9078 [\u6b64\u8655]({url}) \u4ee5\u958b\u555f SimpliSafe Web App \u4e26\u8f38\u5165\u9a57\u8b49\u3002\u5047\u5982\u5df2\u7d93\u65bc\u700f\u89bd\u5668\u4e2d\u767b\u5165 SimpliSafe\uff0c\u53ef\u80fd\u9700\u8981\u958b\u555f\u65b0\u9801\u9762\u3001\u7136\u5f8c\u65bc\u9801\u9762\u7db2\u5740\u8907\u88fd/\u8cbc\u4e0a\u4e0a\u65b9 URL\u3002\n\n\u5b8c\u6210\u5f8c\u56de\u5230\u9019\u88e1\u4e26\u8f38\u5165\u7531 `com.simplisafe.mobile` \u6240\u53d6\u5f97\u7684\u8a8d\u8b49\u78bc\u3002" } diff --git a/homeassistant/components/speedtestdotnet/translations/bg.json b/homeassistant/components/speedtestdotnet/translations/bg.json index 31163643662..0e175ee9902 100644 --- a/homeassistant/components/speedtestdotnet/translations/bg.json +++ b/homeassistant/components/speedtestdotnet/translations/bg.json @@ -9,18 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "title": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 Speedtest \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430" - } - } - }, - "title": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 Speedtest \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/ca.json b/homeassistant/components/speedtestdotnet/translations/ca.json index fa29c792992..c1c5dda71cf 100644 --- a/homeassistant/components/speedtestdotnet/translations/ca.json +++ b/homeassistant/components/speedtestdotnet/translations/ca.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Actualitza totes les automatitzacions o 'scripts' que utilitzin aquest servei perqu\u00e8 passin a utilitzar el servei `homeassistant.update_entity` amb un 'entity_id' objectiu o 'target' de Speedtest. Despr\u00e9s, fes clic a ENVIA per marcar aquest problema com a resolt.", - "title": "El servei speedtest est\u00e0 sent eliminat" - } - } - }, - "title": "El servei speedtest est\u00e0 sent eliminat" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/de.json b/homeassistant/components/speedtestdotnet/translations/de.json index f33c088998f..81910cb9c70 100644 --- a/homeassistant/components/speedtestdotnet/translations/de.json +++ b/homeassistant/components/speedtestdotnet/translations/de.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Aktualisiere alle Automatisierungen oder Skripte, die diesen Dienst verwenden, um stattdessen den Dienst \"homeassistant.update_entity\" mit einer Speedtest-Entity_id zu verwenden. Dr\u00fccke dann unten auf SENDEN, um dieses Problem als behoben zu markieren.", - "title": "Der Speedtest-Dienst wird entfernt" - } - } - }, - "title": "Der Speedtest-Dienst wird entfernt" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/el.json b/homeassistant/components/speedtestdotnet/translations/el.json index 1b4637fd76a..25b5e23ab69 100644 --- a/homeassistant/components/speedtestdotnet/translations/el.json +++ b/homeassistant/components/speedtestdotnet/translations/el.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "\u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03c5\u03c7\u03cc\u03bd \u03b1\u03c5\u03c4\u03bf\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03bf\u03cd\u03c2 \u03ae \u03c3\u03b5\u03bd\u03ac\u03c1\u03b9\u03b1 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7\u03bd \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ce\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd \u03c4\u03b7\u03bd \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 `homeassistant.update_entity` \u03bc\u03b5 \u03ad\u03bd\u03b1 target Speedtest entity_id. \u03a3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1, \u03ba\u03ac\u03bd\u03c4\u03b5 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03bf \u03ba\u03bf\u03c5\u03bc\u03c0\u03af \u03a5\u03a0\u039f\u0392\u039f\u039b\u0397 \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b5\u03c0\u03b9\u03c3\u03b7\u03bc\u03ac\u03bd\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03b6\u03ae\u03c4\u03b7\u03bc\u03b1 \u03c9\u03c2 \u03b5\u03c0\u03b9\u03bb\u03c5\u03bc\u03ad\u03bd\u03bf.", - "title": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 speedtest \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" - } - } - }, - "title": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 speedtest \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/en.json b/homeassistant/components/speedtestdotnet/translations/en.json index 623aee3b2e8..eab480073bc 100644 --- a/homeassistant/components/speedtestdotnet/translations/en.json +++ b/homeassistant/components/speedtestdotnet/translations/en.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Update any automations or scripts that use this service to instead use the `homeassistant.update_entity` service with a target Speedtest entity_id. Then, click SUBMIT below to mark this issue as resolved.", - "title": "The speedtest service is being removed" - } - } - }, - "title": "The speedtest service is being removed" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/es.json b/homeassistant/components/speedtestdotnet/translations/es.json index f439b2e1079..9ba5fcbd4bb 100644 --- a/homeassistant/components/speedtestdotnet/translations/es.json +++ b/homeassistant/components/speedtestdotnet/translations/es.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Actualiza cualquier automatizaci\u00f3n o script que use este servicio para usar en su lugar el servicio `homeassistant.update_entity` con un entity_id de Speedtest como objetivo. Luego, haz clic en ENVIAR a continuaci\u00f3n para marcar este problema como resuelto.", - "title": "Se va a eliminar el servicio speedtest" - } - } - }, - "title": "Se va a eliminar el servicio speedtest" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/et.json b/homeassistant/components/speedtestdotnet/translations/et.json index abfe42c3cfa..ac1915e760a 100644 --- a/homeassistant/components/speedtestdotnet/translations/et.json +++ b/homeassistant/components/speedtestdotnet/translations/et.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "V\u00e4rskenda k\u00f5iki seda teenust kasutavaid automatiseerimisi v\u00f5i skripte, et kasutada selle asemel teenust \"homeassistant.update_entity\" sihtv\u00e4\u00e4rtusega Speedtest entity_id. Seej\u00e4rel kl\u00f5psa selle probleemi lahendatuks m\u00e4rkimiseks allpool nuppu ESITA.", - "title": "Speedtest teenus eemaldatakse" - } - } - }, - "title": "Speedtest teenus eemaldatakse" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/fr.json b/homeassistant/components/speedtestdotnet/translations/fr.json index a89b7e89977..d2efebd0eb1 100644 --- a/homeassistant/components/speedtestdotnet/translations/fr.json +++ b/homeassistant/components/speedtestdotnet/translations/fr.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Modifiez tout script ou automatisation utilisant ce service afin qu'ils utilisent \u00e0 la place le service `homeassistant.update_entity` avec l'entity_id d'un Speedtest pour cible. Cliquez ensuite sur VALIDER ci-dessous afin de marquer ce probl\u00e8me comme r\u00e9solu.", - "title": "Le service speedtest sera bient\u00f4t supprim\u00e9" - } - } - }, - "title": "Le service speedtest sera bient\u00f4t supprim\u00e9" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/hu.json b/homeassistant/components/speedtestdotnet/translations/hu.json index f99db19fe4a..c223e8b9376 100644 --- a/homeassistant/components/speedtestdotnet/translations/hu.json +++ b/homeassistant/components/speedtestdotnet/translations/hu.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Friss\u00edtsen minden olyan automatiz\u00e1l\u00e1st vagy szkriptet, amely ezt a szolg\u00e1ltat\u00e1st haszn\u00e1lja, hogy helyette a `homeassistant.update_entity` szolg\u00e1ltat\u00e1st haszn\u00e1lja a Speedtest entity_id azonos\u00edt\u00f3val. Ezut\u00e1n kattintson az al\u00e1bbi MEHET gombra a probl\u00e9ma megoldottk\u00e9nt val\u00f3 megjel\u00f6l\u00e9s\u00e9hez.", - "title": "A speedtest szolg\u00e1ltat\u00e1s elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" - } - } - }, - "title": "A speedtest szolg\u00e1ltat\u00e1s elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/id.json b/homeassistant/components/speedtestdotnet/translations/id.json index 8a5e3711d93..f609c3d384a 100644 --- a/homeassistant/components/speedtestdotnet/translations/id.json +++ b/homeassistant/components/speedtestdotnet/translations/id.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Perbarui semua otomasi atau skrip yang menggunakan layanan ini untuk menggunakan layanan `homeassistant.update_entity` dengan target ID entitas Speedtest. Kemudian, klik KIRIM di bawah ini untuk menandai masalah ini sebagai terselesaikan.", - "title": "Layanan speedtest dalam proses penghapusan" - } - } - }, - "title": "Layanan speedtest dalam proses penghapusan" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/it.json b/homeassistant/components/speedtestdotnet/translations/it.json index c9e02758bb2..07615fe093c 100644 --- a/homeassistant/components/speedtestdotnet/translations/it.json +++ b/homeassistant/components/speedtestdotnet/translations/it.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Aggiorna tutte le automazioni o gli script che utilizzano questo servizio per utilizzare invece il servizio `homeassistant.update_entity` con un entity_id Speedtest di destinazione. Quindi, fai clic su INVIA di seguito per contrassegnare questo problema come risolto.", - "title": "Il servizio speedtest \u00e8 stato rimosso" - } - } - }, - "title": "Il servizio speedtest \u00e8 stato rimosso" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/ja.json b/homeassistant/components/speedtestdotnet/translations/ja.json index c09a2a6032a..40f592b2c46 100644 --- a/homeassistant/components/speedtestdotnet/translations/ja.json +++ b/homeassistant/components/speedtestdotnet/translations/ja.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "\u3053\u306e\u30b5\u30fc\u30d3\u30b9\u3092\u4f7f\u7528\u3059\u308b\u3059\u3079\u3066\u306e\u81ea\u52d5\u5316\u307e\u305f\u306f\u30b9\u30af\u30ea\u30d7\u30c8\u3092\u66f4\u65b0\u3057\u3066\u3001\u4ee3\u308f\u308a\u306b\u30bf\u30fc\u30b2\u30c3\u30c8 Speedtest entity_id \u3067\u300chomeassistant.update_entity\u300d\u30b5\u30fc\u30d3\u30b9\u3092\u4f7f\u7528\u3057\u307e\u3059\u3002\u6b21\u306b\u3001\u4e0b\u306e [\u9001\u4fe1] \u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u3001\u3053\u306e\u554f\u984c\u3092\u89e3\u6c7a\u6e08\u307f\u3068\u3057\u3066\u30de\u30fc\u30af\u3057\u307e\u3059\u3002", - "title": "speedtest\u30b5\u30fc\u30d3\u30b9\u306f\u524a\u9664\u3055\u308c\u3066\u3044\u307e\u3059" - } - } - }, - "title": "speedtest\u30b5\u30fc\u30d3\u30b9\u306f\u524a\u9664\u3055\u308c\u3066\u3044\u307e\u3059" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/no.json b/homeassistant/components/speedtestdotnet/translations/no.json index 127541b5926..01909d39f06 100644 --- a/homeassistant/components/speedtestdotnet/translations/no.json +++ b/homeassistant/components/speedtestdotnet/translations/no.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Oppdater eventuelle automatiseringer eller skript som bruker denne tjenesten for i stedet \u00e5 bruke `homeassistant.update_entity`-tjenesten med en m\u00e5l Speedtest-entity_id. Klikk deretter SEND nedenfor for \u00e5 merke dette problemet som l\u00f8st.", - "title": "Speedtest-tjenesten fjernes" - } - } - }, - "title": "Speedtest-tjenesten blir fjernet" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/pl.json b/homeassistant/components/speedtestdotnet/translations/pl.json index 237d05ffd9c..b06d7cdd285 100644 --- a/homeassistant/components/speedtestdotnet/translations/pl.json +++ b/homeassistant/components/speedtestdotnet/translations/pl.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Zaktualizuj wszystkie automatyzacje lub skrypty korzystaj\u0105ce z tej us\u0142ugi, aby zamiast tego u\u017cywa\u0142y us\u0142ugi \u201ehomeassistant.update_entity\u201d z encj\u0105 docelow\u0105 Speedtest. Nast\u0119pnie kliknij ZATWIERD\u0179 poni\u017cej, aby oznaczy\u0107 ten problem jako rozwi\u0105zany.", - "title": "Us\u0142uga speedtest zostanie usuni\u0119ta" - } - } - }, - "title": "Us\u0142uga speedtest zostanie usuni\u0119ta" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/pt-BR.json b/homeassistant/components/speedtestdotnet/translations/pt-BR.json index 7a159970a7f..739b3b41875 100644 --- a/homeassistant/components/speedtestdotnet/translations/pt-BR.json +++ b/homeassistant/components/speedtestdotnet/translations/pt-BR.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Atualize quaisquer automa\u00e7\u00f5es ou scripts que usam este servi\u00e7o para usar o servi\u00e7o `homeassistant.update_entity` com um ID de entidade do Speedtest. Em seguida, clique em ENVIAR abaixo para marcar este problema como resolvido.", - "title": "O servi\u00e7o speedtest est\u00e1 sendo removido" - } - } - }, - "title": "O servi\u00e7o speedtest est\u00e1 sendo removido" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/pt.json b/homeassistant/components/speedtestdotnet/translations/pt.json index b0c105531f2..c299020ce9a 100644 --- a/homeassistant/components/speedtestdotnet/translations/pt.json +++ b/homeassistant/components/speedtestdotnet/translations/pt.json @@ -8,18 +8,5 @@ "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" } } - }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Atualize quaisquer automa\u00e7\u00f5es ou scripts que usam este servi\u00e7o para usar o servi\u00e7o `homeassistant.update_entity` com um ID de entidade do Speedtest de destino. Em seguida, clique em ENVIAR abaixo para marcar este problema como resolvido.", - "title": "O servi\u00e7o speedtest est\u00e1 sendo removido" - } - } - }, - "title": "O servi\u00e7o speedtest est\u00e1 sendo removido" - } } } \ No newline at end of file diff --git a/homeassistant/components/speedtestdotnet/translations/ru.json b/homeassistant/components/speedtestdotnet/translations/ru.json index 45b3a7da55a..3ffcd10bf31 100644 --- a/homeassistant/components/speedtestdotnet/translations/ru.json +++ b/homeassistant/components/speedtestdotnet/translations/ru.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "\u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0439 \u0441\u043b\u0443\u0436\u0431\u044b \u0442\u0435\u043f\u0435\u0440\u044c \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0443\u0436\u0431\u0443 `homeassistant.update_entity` \u0441 \u0446\u0435\u043b\u0435\u0432\u044b\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c Speedtest. \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u0438 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u041f\u041e\u0414\u0422\u0412\u0415\u0420\u0414\u0418\u0422\u042c, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043a\u0430\u043a \u0443\u0441\u0442\u0440\u0430\u043d\u0451\u043d\u043d\u0443\u044e.", - "title": "\u0421\u043b\u0443\u0436\u0431\u0430 speedtest \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" - } - } - }, - "title": "\u0421\u043b\u0443\u0436\u0431\u0430 speedtest \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/sv.json b/homeassistant/components/speedtestdotnet/translations/sv.json index 81e07b927c7..cde9095fba8 100644 --- a/homeassistant/components/speedtestdotnet/translations/sv.json +++ b/homeassistant/components/speedtestdotnet/translations/sv.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Uppdatera eventuella automatiseringar eller skript som anv\u00e4nder den h\u00e4r tj\u00e4nsten f\u00f6r att ist\u00e4llet anv\u00e4nda tj\u00e4nsten `homeassistant.update_entity` med ett m\u00e5l Speedtest-entity_id. Klicka sedan p\u00e5 SKICKA nedan f\u00f6r att markera problemet som l\u00f6st.", - "title": "Tj\u00e4nsten speedtest tas bort" - } - } - }, - "title": "Tj\u00e4nsten speedtest tas bort" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/tr.json b/homeassistant/components/speedtestdotnet/translations/tr.json index d096c2c264d..5becafdf153 100644 --- a/homeassistant/components/speedtestdotnet/translations/tr.json +++ b/homeassistant/components/speedtestdotnet/translations/tr.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "Bu hizmeti kullanan t\u00fcm otomasyonlar\u0131 veya komut dosyalar\u0131n\u0131, bunun yerine \"homeassistant.update_entity\" hizmetini bir hedef Speedtest entity_id ile kullanacak \u015fekilde g\u00fcncelleyin. Ard\u0131ndan, bu sorunu \u00e7\u00f6z\u00fcld\u00fc olarak i\u015faretlemek i\u00e7in a\u015fa\u011f\u0131daki G\u00d6NDER'i t\u0131klay\u0131n.", - "title": "Speedtest hizmeti kald\u0131r\u0131l\u0131yor" - } - } - }, - "title": "Speedtest hizmeti kald\u0131r\u0131l\u0131yor" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/speedtestdotnet/translations/zh-Hant.json b/homeassistant/components/speedtestdotnet/translations/zh-Hant.json index 3d12b9454d0..43d30d4aeb8 100644 --- a/homeassistant/components/speedtestdotnet/translations/zh-Hant.json +++ b/homeassistant/components/speedtestdotnet/translations/zh-Hant.json @@ -9,19 +9,6 @@ } } }, - "issues": { - "deprecated_service": { - "fix_flow": { - "step": { - "confirm": { - "description": "\u4f7f\u7528\u6b64\u670d\u52d9\u4ee5\u66f4\u65b0\u4efb\u4f55\u81ea\u52d5\u5316\u6216\u8173\u672c\u3001\u4ee5\u53d6\u4ee3\u4f7f\u7528\u76ee\u6a19 entity_id \u70ba Speedtest \u4e4b `homeassistant.update_entity` \u670d\u52d9\uff0c\u7136\u5f8c\u9ede\u9078\u50b3\u9001\u4ee5\u6a19\u793a\u554f\u984c\u5df2\u89e3\u6c7a\u3002", - "title": "Seedtest \u670d\u52d9\u5373\u5c07\u79fb\u9664" - } - } - }, - "title": "Speedtest \u670d\u52d9\u5373\u5c07\u79fb\u9664" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/switchbee/translations/ca.json b/homeassistant/components/switchbee/translations/ca.json index 745b3ed79cf..204f9ab801d 100644 --- a/homeassistant/components/switchbee/translations/ca.json +++ b/homeassistant/components/switchbee/translations/ca.json @@ -13,20 +13,10 @@ "data": { "host": "Amfitri\u00f3", "password": "Contrasenya", - "switch_as_light": "Inicialitza els interruptors com a entitats de llum", "username": "Nom d'usuari" }, "description": "Configura la integraci\u00f3 SwitchBee amb Home Assistant." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Dispositius a incloure" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/cs.json b/homeassistant/components/switchbee/translations/cs.json index 4b40956d82b..0f02cd974c2 100644 --- a/homeassistant/components/switchbee/translations/cs.json +++ b/homeassistant/components/switchbee/translations/cs.json @@ -17,14 +17,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Zahrnout za\u0159\u00edzen\u00ed" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/de.json b/homeassistant/components/switchbee/translations/de.json index 99862c61d84..4507dfd8ff9 100644 --- a/homeassistant/components/switchbee/translations/de.json +++ b/homeassistant/components/switchbee/translations/de.json @@ -13,20 +13,10 @@ "data": { "host": "Host", "password": "Passwort", - "switch_as_light": "Schalter als Lichteinheiten initialisieren", "username": "Benutzername" }, "description": "Einrichten der SwitchBee-Integration mit Home Assistant." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Einzuschlie\u00dfende Ger\u00e4te" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/el.json b/homeassistant/components/switchbee/translations/el.json index ad806294956..e0e460c7823 100644 --- a/homeassistant/components/switchbee/translations/el.json +++ b/homeassistant/components/switchbee/translations/el.json @@ -13,20 +13,10 @@ "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "switch_as_light": "\u0391\u03c1\u03c7\u03b9\u03ba\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c4\u03c9\u03bd \u03b4\u03b9\u03b1\u03ba\u03bf\u03c0\u03c4\u03ce\u03bd \u03c9\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03c6\u03c9\u03c4\u03cc\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" }, "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 SwitchBee \u03bc\u03b5 \u03c4\u03bf Home Assistant." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c0\u03bf\u03c5 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b7\u03c6\u03b8\u03bf\u03cd\u03bd" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/en.json b/homeassistant/components/switchbee/translations/en.json index 41f9ee6a043..555d0e03f34 100644 --- a/homeassistant/components/switchbee/translations/en.json +++ b/homeassistant/components/switchbee/translations/en.json @@ -13,20 +13,10 @@ "data": { "host": "Host", "password": "Password", - "switch_as_light": "Initialize switches as light entities", "username": "Username" }, "description": "Setup SwitchBee integration with Home Assistant." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Devices to include" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/es.json b/homeassistant/components/switchbee/translations/es.json index fc22055048d..8df68cfeaa3 100644 --- a/homeassistant/components/switchbee/translations/es.json +++ b/homeassistant/components/switchbee/translations/es.json @@ -13,20 +13,10 @@ "data": { "host": "Host", "password": "Contrase\u00f1a", - "switch_as_light": "Inicializar interruptores como entidades luces", "username": "Nombre de usuario" }, "description": "Configurar la integraci\u00f3n SwitchBee con Home Assistant." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Dispositivos a incluir" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/et.json b/homeassistant/components/switchbee/translations/et.json index fa6c114d434..d43a291194e 100644 --- a/homeassistant/components/switchbee/translations/et.json +++ b/homeassistant/components/switchbee/translations/et.json @@ -13,20 +13,10 @@ "data": { "host": "Host", "password": "Salas\u00f5na", - "switch_as_light": "L\u00e4htesta l\u00fclitid valgusolemitena", "username": "Kasutajanimi" }, "description": "Seadista SwitchBee sidumine Home Assistantiga." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Kaasatavad seadmed" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/fr.json b/homeassistant/components/switchbee/translations/fr.json index 1f1d36f5da0..eb09f124182 100644 --- a/homeassistant/components/switchbee/translations/fr.json +++ b/homeassistant/components/switchbee/translations/fr.json @@ -13,20 +13,10 @@ "data": { "host": "H\u00f4te", "password": "Mot de passe", - "switch_as_light": "Initialiser les commutateurs en tant que lumi\u00e8res", "username": "Nom d'utilisateur" }, "description": "Configurez l'int\u00e9gration de SwitchBee avec Home Assistant." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Appareils \u00e0 inclure" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/hu.json b/homeassistant/components/switchbee/translations/hu.json index 01b3bc0010b..debc3d0956f 100644 --- a/homeassistant/components/switchbee/translations/hu.json +++ b/homeassistant/components/switchbee/translations/hu.json @@ -13,20 +13,10 @@ "data": { "host": "C\u00edm", "password": "Jelsz\u00f3", - "switch_as_light": "A kapcsol\u00f3k l\u00e1mpak\u00e9nt t\u00f6rt\u00e9n\u0151 inicializ\u00e1l\u00e1sa", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, "description": "SwitchBee integr\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1sa a Home Assistant rendszerrel." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "A benne foglalt eszk\u00f6z\u00f6k" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/id.json b/homeassistant/components/switchbee/translations/id.json index 71fc2092c6c..3476e712583 100644 --- a/homeassistant/components/switchbee/translations/id.json +++ b/homeassistant/components/switchbee/translations/id.json @@ -13,20 +13,10 @@ "data": { "host": "Host", "password": "Kata Sandi", - "switch_as_light": "Inisialisasi sakelar sebagai entitas lampu", "username": "Nama Pengguna" }, "description": "Siapkan integrasi SwitchBee dengan Home Assistant." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Perangkat yang disertakan" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/it.json b/homeassistant/components/switchbee/translations/it.json index ebd7915f049..4694ab92b85 100644 --- a/homeassistant/components/switchbee/translations/it.json +++ b/homeassistant/components/switchbee/translations/it.json @@ -13,20 +13,10 @@ "data": { "host": "Host", "password": "Password", - "switch_as_light": "Inizializza gli interruttori come entit\u00e0 luce", "username": "Nome utente" }, "description": "Imposta l'integrazione di SwitchBee con Home Assistant." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Dispositivi da includere" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/ja.json b/homeassistant/components/switchbee/translations/ja.json index b0bdd860094..a9d2ddfd3ac 100644 --- a/homeassistant/components/switchbee/translations/ja.json +++ b/homeassistant/components/switchbee/translations/ja.json @@ -17,14 +17,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "\u542b\u3081\u308b\u30c7\u30d0\u30a4\u30b9" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/ko.json b/homeassistant/components/switchbee/translations/ko.json index 5af63d34313..d9b021bccbc 100644 --- a/homeassistant/components/switchbee/translations/ko.json +++ b/homeassistant/components/switchbee/translations/ko.json @@ -3,19 +3,9 @@ "step": { "user": { "data": { - "switch_as_light": "\uc2a4\uc704\uce58\ub97c \uc870\uba85 \uac1c\uccb4\ub85c \ucd08\uae30\ud654", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" } } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "\ud3ec\ud568\ud560 \uc7a5\uce58" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/no.json b/homeassistant/components/switchbee/translations/no.json index 22f16d5a328..d97d103cd1d 100644 --- a/homeassistant/components/switchbee/translations/no.json +++ b/homeassistant/components/switchbee/translations/no.json @@ -13,20 +13,10 @@ "data": { "host": "Vert", "password": "Passord", - "switch_as_light": "Initialiser brytere som lysenheter", "username": "Brukernavn" }, "description": "Sett opp SwitchBee-integrasjon med Home Assistant." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Enheter som skal inkluderes" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/pl.json b/homeassistant/components/switchbee/translations/pl.json index f2233b1550c..2859ba20d73 100644 --- a/homeassistant/components/switchbee/translations/pl.json +++ b/homeassistant/components/switchbee/translations/pl.json @@ -13,20 +13,10 @@ "data": { "host": "Nazwa hosta lub adres IP", "password": "Has\u0142o", - "switch_as_light": "Zainicjuj prze\u0142\u0105czniki jako encje \u015bwiat\u0142a", "username": "Nazwa u\u017cytkownika" }, "description": "Konfiguracja integracji SwitchBee z Home Assistantem." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Urz\u0105dzenia do uwzgl\u0119dnienia" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/pt-BR.json b/homeassistant/components/switchbee/translations/pt-BR.json index a99ffe41150..098b62a109e 100644 --- a/homeassistant/components/switchbee/translations/pt-BR.json +++ b/homeassistant/components/switchbee/translations/pt-BR.json @@ -13,20 +13,10 @@ "data": { "host": "Nome do host", "password": "Senha", - "switch_as_light": "Inicializar switches como entidades de luz", "username": "Nome de usu\u00e1rio" }, "description": "Configure a integra\u00e7\u00e3o do SwitchBee com o Home Assistant." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Dispositivos para incluir" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/ru.json b/homeassistant/components/switchbee/translations/ru.json index 13ea7317296..a4900df81c4 100644 --- a/homeassistant/components/switchbee/translations/ru.json +++ b/homeassistant/components/switchbee/translations/ru.json @@ -13,20 +13,10 @@ "data": { "host": "\u0425\u043e\u0441\u0442", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "switch_as_light": "\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u0435\u0439 \u043a\u0430\u043a \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u0435", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 SwitchBee." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/sv.json b/homeassistant/components/switchbee/translations/sv.json index 42d3330f48e..b99d85f4a86 100644 --- a/homeassistant/components/switchbee/translations/sv.json +++ b/homeassistant/components/switchbee/translations/sv.json @@ -13,20 +13,10 @@ "data": { "host": "V\u00e4rd", "password": "L\u00f6senord", - "switch_as_light": "Initiera omkopplare som ljusenheter", "username": "Anv\u00e4ndarnamn" }, "description": "Konfigurera SwitchBee-integrationen med Home Assistant." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Enheter att inkludera" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/tr.json b/homeassistant/components/switchbee/translations/tr.json index b3bd4cde1b1..bb708bf8b1c 100644 --- a/homeassistant/components/switchbee/translations/tr.json +++ b/homeassistant/components/switchbee/translations/tr.json @@ -13,20 +13,10 @@ "data": { "host": "Sunucu", "password": "Parola", - "switch_as_light": "Anahtarlar\u0131 \u0131\u015f\u0131k varl\u0131klar\u0131 olarak ba\u015flat", "username": "Kullan\u0131c\u0131 Ad\u0131" }, "description": "Home Assistant ile SwitchBee entegrasyonunu kurun." } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "Dahil edilecek cihazlar" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/zh-Hant.json b/homeassistant/components/switchbee/translations/zh-Hant.json index baa704a5235..94dfc1a466c 100644 --- a/homeassistant/components/switchbee/translations/zh-Hant.json +++ b/homeassistant/components/switchbee/translations/zh-Hant.json @@ -13,20 +13,10 @@ "data": { "host": "\u4e3b\u6a5f\u7aef", "password": "\u5bc6\u78bc", - "switch_as_light": "\u5c07\u958b\u95dc\u521d\u59cb\u70ba\u71c8\u5149\u5be6\u9ad4", "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, "description": "\u8a2d\u5b9a SwitchBee \u63a5\u5165 Home Assistant \u6574\u5408\u3002" } } - }, - "options": { - "step": { - "init": { - "data": { - "devices": "\u5305\u542b\u88dd\u7f6e" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/switchbot/translations/bg.json b/homeassistant/components/switchbot/translations/bg.json index 196b5511f77..218cc017cc8 100644 --- a/homeassistant/components/switchbot/translations/bg.json +++ b/homeassistant/components/switchbot/translations/bg.json @@ -19,10 +19,7 @@ }, "user": { "data": { - "address": "\u0410\u0434\u0440\u0435\u0441 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e", - "mac": "MAC \u0430\u0434\u0440\u0435\u0441 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e", - "name": "\u0418\u043c\u0435", - "password": "\u041f\u0430\u0440\u043e\u043b\u0430" + "address": "\u0410\u0434\u0440\u0435\u0441 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e" } } } diff --git a/homeassistant/components/switchbot/translations/ca.json b/homeassistant/components/switchbot/translations/ca.json index 04c1af6d69f..b117dd02c05 100644 --- a/homeassistant/components/switchbot/translations/ca.json +++ b/homeassistant/components/switchbot/translations/ca.json @@ -20,12 +20,8 @@ }, "user": { "data": { - "address": "Adre\u00e7a del dispositiu", - "mac": "Adre\u00e7a MAC del dispositiu", - "name": "Nom", - "password": "Contrasenya" - }, - "title": "Configuraci\u00f3 de dispositiu Switchbot" + "address": "Adre\u00e7a del dispositiu" + } } } }, @@ -33,10 +29,7 @@ "step": { "init": { "data": { - "retry_count": "Nombre de reintents", - "retry_timeout": "Temps d'espera entre reintents", - "scan_timeout": "Quant de temps s'ha d'escanejar en busca de dades d'alerta", - "update_time": "Temps entre actualitzacions (segons)" + "retry_count": "Nombre de reintents" } } } diff --git a/homeassistant/components/switchbot/translations/cs.json b/homeassistant/components/switchbot/translations/cs.json index 5e57c3b60a7..0c72857af0c 100644 --- a/homeassistant/components/switchbot/translations/cs.json +++ b/homeassistant/components/switchbot/translations/cs.json @@ -14,12 +14,6 @@ "data": { "password": "Heslo" } - }, - "user": { - "data": { - "name": "Jm\u00e9no", - "password": "Heslo" - } } } } diff --git a/homeassistant/components/switchbot/translations/de.json b/homeassistant/components/switchbot/translations/de.json index 2b4fd5ba8cf..fa62c6fa343 100644 --- a/homeassistant/components/switchbot/translations/de.json +++ b/homeassistant/components/switchbot/translations/de.json @@ -20,12 +20,8 @@ }, "user": { "data": { - "address": "Ger\u00e4teadresse", - "mac": "MAC-Adresse des Ger\u00e4ts", - "name": "Name", - "password": "Passwort" - }, - "title": "Switchbot-Ger\u00e4t einrichten" + "address": "Ger\u00e4teadresse" + } } } }, @@ -33,10 +29,7 @@ "step": { "init": { "data": { - "retry_count": "Anzahl der Wiederholungen", - "retry_timeout": "Zeit\u00fcberschreitung zwischen Wiederholungsversuchen", - "scan_timeout": "Wie lange nach Anzeigendaten suchen", - "update_time": "Zeit zwischen Aktualisierungen (Sekunden)" + "retry_count": "Anzahl der Wiederholungen" } } } diff --git a/homeassistant/components/switchbot/translations/el.json b/homeassistant/components/switchbot/translations/el.json index df151167751..096a6d05fe9 100644 --- a/homeassistant/components/switchbot/translations/el.json +++ b/homeassistant/components/switchbot/translations/el.json @@ -24,12 +24,8 @@ }, "user": { "data": { - "address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", - "mac": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 MAC \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1", - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" - }, - "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 Switchbot" + "address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2" + } } } }, @@ -37,10 +33,7 @@ "step": { "init": { "data": { - "retry_count": "\u0391\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03b5\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03ce\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03b5\u03b9\u03ce\u03bd", - "retry_timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03b5\u03c0\u03b1\u03bd\u03b1\u03bb\u03ae\u03c8\u03b5\u03c9\u03bd", - "scan_timeout": "\u03a0\u03cc\u03c3\u03bf\u03c2 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 \u03b4\u03b9\u03b1\u03c6\u03ae\u03bc\u03b9\u03c3\u03b7\u03c2", - "update_time": "\u03a7\u03c1\u03cc\u03bd\u03bf\u03c2 \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03b5\u03c9\u03bd (\u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1)" + "retry_count": "\u0391\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03b5\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03ce\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03b5\u03b9\u03ce\u03bd" } } } diff --git a/homeassistant/components/switchbot/translations/en.json b/homeassistant/components/switchbot/translations/en.json index 6a0231c9bed..e262ed53175 100644 --- a/homeassistant/components/switchbot/translations/en.json +++ b/homeassistant/components/switchbot/translations/en.json @@ -20,12 +20,8 @@ }, "user": { "data": { - "address": "Device address", - "mac": "Device MAC address", - "name": "Name", - "password": "Password" - }, - "title": "Setup Switchbot device" + "address": "Device address" + } } } }, @@ -33,10 +29,7 @@ "step": { "init": { "data": { - "retry_count": "Retry count", - "retry_timeout": "Timeout between retries", - "scan_timeout": "How long to scan for advertisement data", - "update_time": "Time between updates (seconds)" + "retry_count": "Retry count" } } } diff --git a/homeassistant/components/switchbot/translations/es.json b/homeassistant/components/switchbot/translations/es.json index a4cca573f7d..55a374de412 100644 --- a/homeassistant/components/switchbot/translations/es.json +++ b/homeassistant/components/switchbot/translations/es.json @@ -20,12 +20,8 @@ }, "user": { "data": { - "address": "Direcci\u00f3n del dispositivo", - "mac": "Direcci\u00f3n MAC del dispositivo", - "name": "Nombre", - "password": "Contrase\u00f1a" - }, - "title": "Configurar el dispositivo Switchbot" + "address": "Direcci\u00f3n del dispositivo" + } } } }, @@ -33,10 +29,7 @@ "step": { "init": { "data": { - "retry_count": "Recuento de reintentos", - "retry_timeout": "Tiempo de espera entre reintentos", - "scan_timeout": "Cu\u00e1nto tiempo escanear en busca de datos de anuncio", - "update_time": "Tiempo entre actualizaciones (segundos)" + "retry_count": "Recuento de reintentos" } } } diff --git a/homeassistant/components/switchbot/translations/et.json b/homeassistant/components/switchbot/translations/et.json index 0f5998dee99..ad76e84f976 100644 --- a/homeassistant/components/switchbot/translations/et.json +++ b/homeassistant/components/switchbot/translations/et.json @@ -20,12 +20,8 @@ }, "user": { "data": { - "address": "Seadme aadress", - "mac": "Seadme MAC-aadress", - "name": "Nimi", - "password": "Salas\u00f5na" - }, - "title": "Switchbot seadme seadistamine" + "address": "Seadme aadress" + } } } }, @@ -33,10 +29,7 @@ "step": { "init": { "data": { - "retry_count": "Korduskatsete arv", - "retry_timeout": "Korduskatsete vaheline aeg", - "scan_timeout": "Kui kaua andmeid otsida", - "update_time": "V\u00e4rskenduste vaheline aeg (sekundites)" + "retry_count": "Korduskatsete arv" } } } diff --git a/homeassistant/components/switchbot/translations/fr.json b/homeassistant/components/switchbot/translations/fr.json index 2b35ec9634e..cf8a3709a3d 100644 --- a/homeassistant/components/switchbot/translations/fr.json +++ b/homeassistant/components/switchbot/translations/fr.json @@ -20,12 +20,8 @@ }, "user": { "data": { - "address": "Adresse de l'appareil", - "mac": "Adresse MAC de l'appareil", - "name": "Nom", - "password": "Mot de passe" - }, - "title": "Configurer l'appareil Switchbot" + "address": "Adresse de l'appareil" + } } } }, @@ -33,10 +29,7 @@ "step": { "init": { "data": { - "retry_count": "Nombre de nouvelles tentatives", - "retry_timeout": "D\u00e9lai d'attente entre les tentatives", - "scan_timeout": "Dur\u00e9e de la recherche de donn\u00e9es publicitaires", - "update_time": "Intervalle de temps entre deux mises \u00e0 jour (en secondes)" + "retry_count": "Nombre de nouvelles tentatives" } } } diff --git a/homeassistant/components/switchbot/translations/he.json b/homeassistant/components/switchbot/translations/he.json index b4cb968ff23..223bce5b5c7 100644 --- a/homeassistant/components/switchbot/translations/he.json +++ b/homeassistant/components/switchbot/translations/he.json @@ -11,13 +11,6 @@ "data": { "password": "\u05e1\u05d9\u05e1\u05de\u05d4" } - }, - "user": { - "data": { - "name": "\u05e9\u05dd", - "password": "\u05e1\u05d9\u05e1\u05de\u05d4" - }, - "title": "\u05d4\u05ea\u05e7\u05e0\u05ea \u05d1\u05d5\u05e8\u05e8 \u05db\u05d9\u05d5\u05d5\u05e0\u05d5\u05df" } } }, @@ -25,10 +18,7 @@ "step": { "init": { "data": { - "retry_count": "\u05e1\u05e4\u05d9\u05e8\u05ea \u05e0\u05e1\u05d9\u05d5\u05e0\u05d5\u05ea \u05d7\u05d5\u05d6\u05e8\u05d9\u05dd", - "retry_timeout": "\u05e4\u05e1\u05e7 \u05d6\u05de\u05df \u05d1\u05d9\u05df \u05e0\u05d9\u05e1\u05d9\u05d5\u05e0\u05d5\u05ea \u05d7\u05d5\u05d6\u05e8\u05d9\u05dd", - "scan_timeout": "\u05db\u05de\u05d4 \u05d6\u05de\u05df \u05dc\u05e1\u05e8\u05d5\u05e7 \u05e0\u05ea\u05d5\u05e0\u05d9 \u05e4\u05e8\u05e1\u05d5\u05de\u05ea", - "update_time": "\u05d6\u05de\u05df \u05d1\u05d9\u05df \u05e2\u05d3\u05db\u05d5\u05e0\u05d9\u05dd (\u05e9\u05e0\u05d9\u05d5\u05ea)" + "retry_count": "\u05e1\u05e4\u05d9\u05e8\u05ea \u05e0\u05e1\u05d9\u05d5\u05e0\u05d5\u05ea \u05d7\u05d5\u05d6\u05e8\u05d9\u05dd" } } } diff --git a/homeassistant/components/switchbot/translations/hu.json b/homeassistant/components/switchbot/translations/hu.json index 88f5756c5fa..52f020dcf1e 100644 --- a/homeassistant/components/switchbot/translations/hu.json +++ b/homeassistant/components/switchbot/translations/hu.json @@ -24,12 +24,8 @@ }, "user": { "data": { - "address": "Eszk\u00f6z c\u00edme", - "mac": "Eszk\u00f6z MAC-c\u00edme", - "name": "Elnevez\u00e9s", - "password": "Jelsz\u00f3" - }, - "title": "Switchbot eszk\u00f6z be\u00e1ll\u00edt\u00e1sa" + "address": "Eszk\u00f6z c\u00edme" + } } } }, @@ -37,10 +33,7 @@ "step": { "init": { "data": { - "retry_count": "\u00dajrapr\u00f3b\u00e1lkoz\u00e1sok sz\u00e1ma", - "retry_timeout": "\u00dajrapr\u00f3b\u00e1lkoz\u00e1sok k\u00f6z\u00f6tti id\u0151korl\u00e1t", - "scan_timeout": "Mennyi ideig keresse a hirdet\u00e9si adatokat", - "update_time": "Friss\u00edt\u00e9sek k\u00f6z\u00f6tti id\u0151 (m\u00e1sodperc)" + "retry_count": "\u00dajrapr\u00f3b\u00e1lkoz\u00e1sok sz\u00e1ma" } } } diff --git a/homeassistant/components/switchbot/translations/id.json b/homeassistant/components/switchbot/translations/id.json index d9d4ade13b1..c7f4cbcc466 100644 --- a/homeassistant/components/switchbot/translations/id.json +++ b/homeassistant/components/switchbot/translations/id.json @@ -20,12 +20,8 @@ }, "user": { "data": { - "address": "Alamat perangkat", - "mac": "Alamat MAC perangkat", - "name": "Nama", - "password": "Kata Sandi" - }, - "title": "Siapkan perangkat Switchbot" + "address": "Alamat perangkat" + } } } }, @@ -33,10 +29,7 @@ "step": { "init": { "data": { - "retry_count": "Jumlah percobaan", - "retry_timeout": "Tenggang waktu antara percobaan ulang", - "scan_timeout": "Berapa lama untuk memindai data iklan", - "update_time": "Waktu antara pembaruan (detik)" + "retry_count": "Jumlah percobaan" } } } diff --git a/homeassistant/components/switchbot/translations/it.json b/homeassistant/components/switchbot/translations/it.json index 3592ce065e3..515c4bdd173 100644 --- a/homeassistant/components/switchbot/translations/it.json +++ b/homeassistant/components/switchbot/translations/it.json @@ -24,12 +24,8 @@ }, "user": { "data": { - "address": "Indirizzo del dispositivo", - "mac": "Indirizzo MAC del dispositivo", - "name": "Nome", - "password": "Password" - }, - "title": "Imposta il dispositivo Switchbot" + "address": "Indirizzo del dispositivo" + } } } }, @@ -37,10 +33,7 @@ "step": { "init": { "data": { - "retry_count": "Conteggio dei tentativi di ripetizione", - "retry_timeout": "Tempo scaduto tra i tentativi", - "scan_timeout": "Per quanto tempo eseguire la scansione dei dati pubblicitari", - "update_time": "Tempo tra gli aggiornamenti (secondi)" + "retry_count": "Conteggio dei tentativi di ripetizione" } } } diff --git a/homeassistant/components/switchbot/translations/ja.json b/homeassistant/components/switchbot/translations/ja.json index 133c9f44d86..b51fde14699 100644 --- a/homeassistant/components/switchbot/translations/ja.json +++ b/homeassistant/components/switchbot/translations/ja.json @@ -23,12 +23,8 @@ }, "user": { "data": { - "address": "\u30c7\u30d0\u30a4\u30b9\u30a2\u30c9\u30ec\u30b9", - "mac": "\u30c7\u30d0\u30a4\u30b9\u306eMAC\u30a2\u30c9\u30ec\u30b9", - "name": "\u540d\u524d", - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" - }, - "title": "Switchbot\u30c7\u30d0\u30a4\u30b9\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" + "address": "\u30c7\u30d0\u30a4\u30b9\u30a2\u30c9\u30ec\u30b9" + } } } }, @@ -36,10 +32,7 @@ "step": { "init": { "data": { - "retry_count": "\u518d\u8a66\u884c\u56de\u6570", - "retry_timeout": "\u518d\u8a66\u884c\u306e\u9593\u306e\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8", - "scan_timeout": "\u5e83\u544a\u30c7\u30fc\u30bf\u3092\u30b9\u30ad\u30e3\u30f3\u3059\u308b\u6642\u9593", - "update_time": "\u66f4\u65b0\u9593\u9694(\u79d2)" + "retry_count": "\u518d\u8a66\u884c\u56de\u6570" } } } diff --git a/homeassistant/components/switchbot/translations/nl.json b/homeassistant/components/switchbot/translations/nl.json index 1860024e011..66720f89013 100644 --- a/homeassistant/components/switchbot/translations/nl.json +++ b/homeassistant/components/switchbot/translations/nl.json @@ -16,14 +16,6 @@ "data": { "password": "Wachtwoord" } - }, - "user": { - "data": { - "mac": "MAC-adres apparaat", - "name": "Naam", - "password": "Wachtwoord" - }, - "title": "Switchbot-apparaat instellen" } } }, @@ -31,10 +23,7 @@ "step": { "init": { "data": { - "retry_count": "Aantal herhalingen", - "retry_timeout": "Time-out tussen nieuwe pogingen", - "scan_timeout": "Hoe lang te scannen voor advertentiegegevens", - "update_time": "Tijd tussen updates (seconden)" + "retry_count": "Aantal herhalingen" } } } diff --git a/homeassistant/components/switchbot/translations/no.json b/homeassistant/components/switchbot/translations/no.json index 53767627d2d..bb3ca766c70 100644 --- a/homeassistant/components/switchbot/translations/no.json +++ b/homeassistant/components/switchbot/translations/no.json @@ -20,12 +20,8 @@ }, "user": { "data": { - "address": "Enhetsadresse", - "mac": "Enhetens MAC -adresse", - "name": "Navn", - "password": "Passord" - }, - "title": "Sett opp Switchbot-enhet" + "address": "Enhetsadresse" + } } } }, @@ -33,10 +29,7 @@ "step": { "init": { "data": { - "retry_count": "Antall nye fors\u00f8k", - "retry_timeout": "Tidsavbrudd mellom fors\u00f8k", - "scan_timeout": "Hvor lenge skal jeg s\u00f8ke etter annonsedata", - "update_time": "Tid mellom oppdateringer (sekunder)" + "retry_count": "Antall nye fors\u00f8k" } } } diff --git a/homeassistant/components/switchbot/translations/pl.json b/homeassistant/components/switchbot/translations/pl.json index 5dbc87c07af..ffe36408692 100644 --- a/homeassistant/components/switchbot/translations/pl.json +++ b/homeassistant/components/switchbot/translations/pl.json @@ -26,12 +26,8 @@ }, "user": { "data": { - "address": "Adres urz\u0105dzenia", - "mac": "Adres MAC urz\u0105dzenia", - "name": "Nazwa", - "password": "Has\u0142o" - }, - "title": "Konfiguracja urz\u0105dzenia Switchbot" + "address": "Adres urz\u0105dzenia" + } } } }, @@ -39,10 +35,7 @@ "step": { "init": { "data": { - "retry_count": "Liczba ponownych pr\u00f3b", - "retry_timeout": "Limit czasu mi\u0119dzy kolejnymi pr\u00f3bami", - "scan_timeout": "Jak d\u0142ugo skanowa\u0107 w poszukiwaniu danych reklamowych", - "update_time": "Czas mi\u0119dzy aktualizacjami (sekundy)" + "retry_count": "Liczba ponownych pr\u00f3b" } } } diff --git a/homeassistant/components/switchbot/translations/pt-BR.json b/homeassistant/components/switchbot/translations/pt-BR.json index 8508185870c..6fe4662469b 100644 --- a/homeassistant/components/switchbot/translations/pt-BR.json +++ b/homeassistant/components/switchbot/translations/pt-BR.json @@ -24,12 +24,8 @@ }, "user": { "data": { - "address": "Endere\u00e7o do dispositivo", - "mac": "Endere\u00e7o MAC do dispositivo", - "name": "Nome", - "password": "Senha" - }, - "title": "Configurar dispositivo Switchbot" + "address": "Endere\u00e7o do dispositivo" + } } } }, @@ -37,10 +33,7 @@ "step": { "init": { "data": { - "retry_count": "Contagem de tentativas", - "retry_timeout": "Intervalo entre tentativas", - "scan_timeout": "Quanto tempo para verificar os dados do an\u00fancio", - "update_time": "Tempo entre atualiza\u00e7\u00f5es (segundos)" + "retry_count": "Contagem de tentativas" } } } diff --git a/homeassistant/components/switchbot/translations/pt.json b/homeassistant/components/switchbot/translations/pt.json deleted file mode 100644 index e2dc9fee9b4..00000000000 --- a/homeassistant/components/switchbot/translations/pt.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "password": "Palavra-passe" - } - } - } - }, - "options": { - "step": { - "init": { - "data": { - "update_time": "Tempo entre actualiza\u00e7\u00f5es (segundos)" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/switchbot/translations/ru.json b/homeassistant/components/switchbot/translations/ru.json index ddf26f9a40e..4bd32239c72 100644 --- a/homeassistant/components/switchbot/translations/ru.json +++ b/homeassistant/components/switchbot/translations/ru.json @@ -20,12 +20,8 @@ }, "user": { "data": { - "address": "\u0410\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", - "mac": "MAC-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" - }, - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Switchbot" + "address": "\u0410\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" + } } } }, @@ -33,10 +29,7 @@ "step": { "init": { "data": { - "retry_count": "\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0445 \u043f\u043e\u043f\u044b\u0442\u043e\u043a", - "retry_timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u043c\u0435\u0436\u0434\u0443 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u043c\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0430\u043c\u0438", - "scan_timeout": "\u041a\u0430\u043a \u0434\u043e\u043b\u0433\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u043a\u043b\u0430\u043c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435", - "update_time": "\u0412\u0440\u0435\u043c\u044f \u043c\u0435\u0436\u0434\u0443 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f\u043c\u0438 (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)" + "retry_count": "\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0445 \u043f\u043e\u043f\u044b\u0442\u043e\u043a" } } } diff --git a/homeassistant/components/switchbot/translations/sk.json b/homeassistant/components/switchbot/translations/sk.json index 75880ae9770..51b7fcdba41 100644 --- a/homeassistant/components/switchbot/translations/sk.json +++ b/homeassistant/components/switchbot/translations/sk.json @@ -18,19 +18,7 @@ }, "user": { "data": { - "address": "Adresa zariadenia", - "mac": "MAC adresa zariadenia", - "name": "N\u00e1zov", - "password": "Heslo" - } - } - } - }, - "options": { - "step": { - "init": { - "data": { - "update_time": "\u010cas medzi aktualiz\u00e1ciami (sekundy)" + "address": "Adresa zariadenia" } } } diff --git a/homeassistant/components/switchbot/translations/sv.json b/homeassistant/components/switchbot/translations/sv.json index 8124e80fd73..7caefebe073 100644 --- a/homeassistant/components/switchbot/translations/sv.json +++ b/homeassistant/components/switchbot/translations/sv.json @@ -20,12 +20,8 @@ }, "user": { "data": { - "address": "Enhetsadress", - "mac": "Enhetens MAC-adress", - "name": "Namn", - "password": "L\u00f6senord" - }, - "title": "Konfigurera Switchbot-enhet" + "address": "Enhetsadress" + } } } }, @@ -33,10 +29,7 @@ "step": { "init": { "data": { - "retry_count": "Antal ompr\u00f6vningar", - "retry_timeout": "Timeout mellan \u00e5terf\u00f6rs\u00f6k", - "scan_timeout": "Hur l\u00e4nge ska man s\u00f6ka efter annonsdata", - "update_time": "Tid mellan uppdateringar (sekunder)" + "retry_count": "Antal ompr\u00f6vningar" } } } diff --git a/homeassistant/components/switchbot/translations/tr.json b/homeassistant/components/switchbot/translations/tr.json index d60be4c0d73..f135a79b549 100644 --- a/homeassistant/components/switchbot/translations/tr.json +++ b/homeassistant/components/switchbot/translations/tr.json @@ -24,12 +24,8 @@ }, "user": { "data": { - "address": "Cihaz adresi", - "mac": "Cihaz MAC adresi", - "name": "Ad", - "password": "Parola" - }, - "title": "Switchbot cihaz\u0131n\u0131 kurun" + "address": "Cihaz adresi" + } } } }, @@ -37,10 +33,7 @@ "step": { "init": { "data": { - "retry_count": "Yeniden deneme say\u0131s\u0131", - "retry_timeout": "Yeniden denemeler aras\u0131ndaki zaman a\u015f\u0131m\u0131", - "scan_timeout": "Reklam verilerinin taranmas\u0131 ne kadar s\u00fcrer?", - "update_time": "G\u00fcncellemeler aras\u0131ndaki s\u00fcre (saniye)" + "retry_count": "Yeniden deneme say\u0131s\u0131" } } } diff --git a/homeassistant/components/switchbot/translations/zh-Hant.json b/homeassistant/components/switchbot/translations/zh-Hant.json index 082ad32f84c..43611eeb571 100644 --- a/homeassistant/components/switchbot/translations/zh-Hant.json +++ b/homeassistant/components/switchbot/translations/zh-Hant.json @@ -20,12 +20,8 @@ }, "user": { "data": { - "address": "\u88dd\u7f6e\u4f4d\u5740", - "mac": "\u88dd\u7f6e MAC \u4f4d\u5740", - "name": "\u540d\u7a31", - "password": "\u5bc6\u78bc" - }, - "title": "\u8a2d\u5b9a Switchbot \u88dd\u7f6e" + "address": "\u88dd\u7f6e\u4f4d\u5740" + } } } }, @@ -33,10 +29,7 @@ "step": { "init": { "data": { - "retry_count": "\u91cd\u8a66\u6b21\u6578", - "retry_timeout": "\u903e\u6642", - "scan_timeout": "\u6383\u63cf\u5ee3\u544a\u6578\u64da\u7684\u6642\u9593", - "update_time": "\u66f4\u65b0\u9593\u9694\u6642\u9593\uff08\u79d2\uff09" + "retry_count": "\u91cd\u8a66\u6b21\u6578" } } } diff --git a/homeassistant/components/tankerkoenig/translations/bg.json b/homeassistant/components/tankerkoenig/translations/bg.json index b33f9039b6e..f2e73b70b6d 100644 --- a/homeassistant/components/tankerkoenig/translations/bg.json +++ b/homeassistant/components/tankerkoenig/translations/bg.json @@ -22,14 +22,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043d\u0430 \u0430\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/ca.json b/homeassistant/components/tankerkoenig/translations/ca.json index 46d523276e7..aa0e032b369 100644 --- a/homeassistant/components/tankerkoenig/translations/ca.json +++ b/homeassistant/components/tankerkoenig/translations/ca.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Interval d'actualitzaci\u00f3", "show_on_map": "Mostra les estacions al mapa", "stations": "Estacions" }, diff --git a/homeassistant/components/tankerkoenig/translations/de.json b/homeassistant/components/tankerkoenig/translations/de.json index f0ad25857a5..2a23f855e57 100644 --- a/homeassistant/components/tankerkoenig/translations/de.json +++ b/homeassistant/components/tankerkoenig/translations/de.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Update-Intervall", "show_on_map": "Stationen auf der Karte anzeigen", "stations": "Stationen" }, diff --git a/homeassistant/components/tankerkoenig/translations/el.json b/homeassistant/components/tankerkoenig/translations/el.json index 82dc5b9019b..1c11770bf9c 100644 --- a/homeassistant/components/tankerkoenig/translations/el.json +++ b/homeassistant/components/tankerkoenig/translations/el.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "\u0394\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7\u03c2", "show_on_map": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03c3\u03c4\u03b1\u03b8\u03bc\u03ce\u03bd \u03c3\u03c4\u03bf \u03c7\u03ac\u03c1\u03c4\u03b7", "stations": "\u03a3\u03c4\u03b1\u03b8\u03bc\u03bf\u03af" }, diff --git a/homeassistant/components/tankerkoenig/translations/en.json b/homeassistant/components/tankerkoenig/translations/en.json index 64c585f838b..9a69c8c812e 100644 --- a/homeassistant/components/tankerkoenig/translations/en.json +++ b/homeassistant/components/tankerkoenig/translations/en.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Update Interval", "show_on_map": "Show stations on map", "stations": "Stations" }, diff --git a/homeassistant/components/tankerkoenig/translations/es.json b/homeassistant/components/tankerkoenig/translations/es.json index c83d6d68c74..66af638e9a3 100644 --- a/homeassistant/components/tankerkoenig/translations/es.json +++ b/homeassistant/components/tankerkoenig/translations/es.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Intervalo de actualizaci\u00f3n", "show_on_map": "Muestra las estaciones en el mapa", "stations": "Estaciones" }, diff --git a/homeassistant/components/tankerkoenig/translations/et.json b/homeassistant/components/tankerkoenig/translations/et.json index 028bac46d44..0270d26833e 100644 --- a/homeassistant/components/tankerkoenig/translations/et.json +++ b/homeassistant/components/tankerkoenig/translations/et.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "V\u00e4rskendamise intervall", "show_on_map": "N\u00e4ita jaamu kaardil", "stations": "Tanklad" }, diff --git a/homeassistant/components/tankerkoenig/translations/fr.json b/homeassistant/components/tankerkoenig/translations/fr.json index 410150263ab..c683f3113b9 100644 --- a/homeassistant/components/tankerkoenig/translations/fr.json +++ b/homeassistant/components/tankerkoenig/translations/fr.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Intervalle de mise \u00e0 jour", "show_on_map": "Afficher les stations-services sur la carte", "stations": "Stations-services" }, diff --git a/homeassistant/components/tankerkoenig/translations/hu.json b/homeassistant/components/tankerkoenig/translations/hu.json index 502ccd6fd9c..4910226697c 100644 --- a/homeassistant/components/tankerkoenig/translations/hu.json +++ b/homeassistant/components/tankerkoenig/translations/hu.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Friss\u00edt\u00e9si id\u0151k\u00f6z", "show_on_map": "\u00c1llom\u00e1sok megjelen\u00edt\u00e9se a t\u00e9rk\u00e9pen", "stations": "\u00c1llom\u00e1sok" }, diff --git a/homeassistant/components/tankerkoenig/translations/id.json b/homeassistant/components/tankerkoenig/translations/id.json index ed0e2e15104..0ada5b4f42f 100644 --- a/homeassistant/components/tankerkoenig/translations/id.json +++ b/homeassistant/components/tankerkoenig/translations/id.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Interval pembaruan", "show_on_map": "Tampilkan SPBU di peta", "stations": "SPBU" }, diff --git a/homeassistant/components/tankerkoenig/translations/it.json b/homeassistant/components/tankerkoenig/translations/it.json index b98598d10a3..99c937d7e6b 100644 --- a/homeassistant/components/tankerkoenig/translations/it.json +++ b/homeassistant/components/tankerkoenig/translations/it.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Intervallo di aggiornamento", "show_on_map": "Mostra stazioni sulla mappa", "stations": "Stazioni" }, diff --git a/homeassistant/components/tankerkoenig/translations/ja.json b/homeassistant/components/tankerkoenig/translations/ja.json index 45e3233f2fd..7e3f4dad642 100644 --- a/homeassistant/components/tankerkoenig/translations/ja.json +++ b/homeassistant/components/tankerkoenig/translations/ja.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "\u66f4\u65b0\u9593\u9694", "show_on_map": "\u5730\u56f3\u4e0a\u306b\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u3092\u8868\u793a\u3059\u308b", "stations": "\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3" }, diff --git a/homeassistant/components/tankerkoenig/translations/nl.json b/homeassistant/components/tankerkoenig/translations/nl.json index 57a8a1fcfe7..f2061ee4f30 100644 --- a/homeassistant/components/tankerkoenig/translations/nl.json +++ b/homeassistant/components/tankerkoenig/translations/nl.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Update Interval", "show_on_map": "Toon stations op kaart", "stations": "Stations" }, diff --git a/homeassistant/components/tankerkoenig/translations/no.json b/homeassistant/components/tankerkoenig/translations/no.json index f0eac9a8f0e..a4683753d8e 100644 --- a/homeassistant/components/tankerkoenig/translations/no.json +++ b/homeassistant/components/tankerkoenig/translations/no.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Oppdateringsintervall", "show_on_map": "Vis stasjoner p\u00e5 kart", "stations": "Stasjoner" }, diff --git a/homeassistant/components/tankerkoenig/translations/pl.json b/homeassistant/components/tankerkoenig/translations/pl.json index 288b4f8aae7..2f49c8dbd3a 100644 --- a/homeassistant/components/tankerkoenig/translations/pl.json +++ b/homeassistant/components/tankerkoenig/translations/pl.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Cz\u0119stotliwo\u015b\u0107 aktualizacji", "show_on_map": "Poka\u017c stacje na mapie", "stations": "Stacje" }, diff --git a/homeassistant/components/tankerkoenig/translations/pt-BR.json b/homeassistant/components/tankerkoenig/translations/pt-BR.json index af26b6167b3..af87f41252c 100644 --- a/homeassistant/components/tankerkoenig/translations/pt-BR.json +++ b/homeassistant/components/tankerkoenig/translations/pt-BR.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Intervalo de atualiza\u00e7\u00e3o", "show_on_map": "Mostrar postos no mapa", "stations": "Esta\u00e7\u00f5es" }, diff --git a/homeassistant/components/tankerkoenig/translations/ru.json b/homeassistant/components/tankerkoenig/translations/ru.json index d2b34eb264b..b444e5d3d17 100644 --- a/homeassistant/components/tankerkoenig/translations/ru.json +++ b/homeassistant/components/tankerkoenig/translations/ru.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f", "show_on_map": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u043d\u0430 \u043a\u0430\u0440\u0442\u0435", "stations": "\u0421\u0442\u0430\u043d\u0446\u0438\u0438" }, diff --git a/homeassistant/components/tankerkoenig/translations/sk.json b/homeassistant/components/tankerkoenig/translations/sk.json index 82a41b014ca..06c74b52725 100644 --- a/homeassistant/components/tankerkoenig/translations/sk.json +++ b/homeassistant/components/tankerkoenig/translations/sk.json @@ -7,14 +7,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Interval aktualiz\u00e1cie" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/sv.json b/homeassistant/components/tankerkoenig/translations/sv.json index 55c362cc717..a2f8d953e28 100644 --- a/homeassistant/components/tankerkoenig/translations/sv.json +++ b/homeassistant/components/tankerkoenig/translations/sv.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "Uppdateringsintervall", "show_on_map": "Visa stationer p\u00e5 kartan", "stations": "Stationer" }, diff --git a/homeassistant/components/tankerkoenig/translations/tr.json b/homeassistant/components/tankerkoenig/translations/tr.json index ca0038b6dbb..c75a35c8a79 100644 --- a/homeassistant/components/tankerkoenig/translations/tr.json +++ b/homeassistant/components/tankerkoenig/translations/tr.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "G\u00fcncelle\u015ftirme aral\u0131\u011f\u0131", "show_on_map": "\u0130stasyonlar\u0131 haritada g\u00f6ster", "stations": "\u0130stasyonlar" }, diff --git a/homeassistant/components/tankerkoenig/translations/zh-Hant.json b/homeassistant/components/tankerkoenig/translations/zh-Hant.json index 1e1a5d6e15a..cfc12640c3b 100644 --- a/homeassistant/components/tankerkoenig/translations/zh-Hant.json +++ b/homeassistant/components/tankerkoenig/translations/zh-Hant.json @@ -37,7 +37,6 @@ "step": { "init": { "data": { - "scan_interval": "\u66f4\u65b0\u983b\u7387", "show_on_map": "\u65bc\u5730\u5716\u986f\u793a\u52a0\u6cb9\u7ad9", "stations": "\u52a0\u6cb9\u7ad9" }, diff --git a/homeassistant/components/tautulli/translations/bg.json b/homeassistant/components/tautulli/translations/bg.json index 9603d8e9169..a8cc4448aca 100644 --- a/homeassistant/components/tautulli/translations/bg.json +++ b/homeassistant/components/tautulli/translations/bg.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", - "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", diff --git a/homeassistant/components/tautulli/translations/ca.json b/homeassistant/components/tautulli/translations/ca.json index cc1ea05a46e..b56fb98f2a0 100644 --- a/homeassistant/components/tautulli/translations/ca.json +++ b/homeassistant/components/tautulli/translations/ca.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "El servei ja est\u00e0 configurat", - "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament", - "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3", diff --git a/homeassistant/components/tautulli/translations/de.json b/homeassistant/components/tautulli/translations/de.json index fe6cc4f82ac..d58c60f88fc 100644 --- a/homeassistant/components/tautulli/translations/de.json +++ b/homeassistant/components/tautulli/translations/de.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "Der Dienst ist bereits konfiguriert", - "reauth_successful": "Die erneute Authentifizierung war erfolgreich", - "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", diff --git a/homeassistant/components/tautulli/translations/el.json b/homeassistant/components/tautulli/translations/el.json index 6f105458435..f0cf9de5479 100644 --- a/homeassistant/components/tautulli/translations/el.json +++ b/homeassistant/components/tautulli/translations/el.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af", - "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", - "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", diff --git a/homeassistant/components/tautulli/translations/en.json b/homeassistant/components/tautulli/translations/en.json index daefd71bf2c..90cb2723ad6 100644 --- a/homeassistant/components/tautulli/translations/en.json +++ b/homeassistant/components/tautulli/translations/en.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "Service is already configured", - "reauth_successful": "Re-authentication was successful", - "single_instance_allowed": "Already configured. Only a single configuration possible." + "reauth_successful": "Re-authentication was successful" }, "error": { "cannot_connect": "Failed to connect", diff --git a/homeassistant/components/tautulli/translations/es.json b/homeassistant/components/tautulli/translations/es.json index 2bbdc4facea..e08f94b1d61 100644 --- a/homeassistant/components/tautulli/translations/es.json +++ b/homeassistant/components/tautulli/translations/es.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "El servicio ya est\u00e1 configurado", - "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente", - "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." + "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente" }, "error": { "cannot_connect": "No se pudo conectar", diff --git a/homeassistant/components/tautulli/translations/et.json b/homeassistant/components/tautulli/translations/et.json index 30ef733c976..229a0741bb7 100644 --- a/homeassistant/components/tautulli/translations/et.json +++ b/homeassistant/components/tautulli/translations/et.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "Teenus on juba h\u00e4\u00e4lestatud", - "reauth_successful": "Taastuvastamine \u00f5nnestus", - "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." + "reauth_successful": "Taastuvastamine \u00f5nnestus" }, "error": { "cannot_connect": "\u00dchendamine nurjus", diff --git a/homeassistant/components/tautulli/translations/fr.json b/homeassistant/components/tautulli/translations/fr.json index ad9c327c551..48b42647484 100644 --- a/homeassistant/components/tautulli/translations/fr.json +++ b/homeassistant/components/tautulli/translations/fr.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9", - "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi", - "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" }, "error": { "cannot_connect": "\u00c9chec de connexion", diff --git a/homeassistant/components/tautulli/translations/he.json b/homeassistant/components/tautulli/translations/he.json index 7091be81520..3718a91d27f 100644 --- a/homeassistant/components/tautulli/translations/he.json +++ b/homeassistant/components/tautulli/translations/he.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8", - "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7", - "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", diff --git a/homeassistant/components/tautulli/translations/hu.json b/homeassistant/components/tautulli/translations/hu.json index 7d31ad678f2..176207b53a2 100644 --- a/homeassistant/components/tautulli/translations/hu.json +++ b/homeassistant/components/tautulli/translations/hu.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van", - "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", - "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", diff --git a/homeassistant/components/tautulli/translations/id.json b/homeassistant/components/tautulli/translations/id.json index 18669b36f29..5b8a6b4201e 100644 --- a/homeassistant/components/tautulli/translations/id.json +++ b/homeassistant/components/tautulli/translations/id.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "Layanan sudah dikonfigurasi", - "reauth_successful": "Autentikasi ulang berhasil", - "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." + "reauth_successful": "Autentikasi ulang berhasil" }, "error": { "cannot_connect": "Gagal terhubung", diff --git a/homeassistant/components/tautulli/translations/it.json b/homeassistant/components/tautulli/translations/it.json index fcc456a8763..e7bb8dce1ba 100644 --- a/homeassistant/components/tautulli/translations/it.json +++ b/homeassistant/components/tautulli/translations/it.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "Il servizio \u00e8 gi\u00e0 configurato", - "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", - "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" }, "error": { "cannot_connect": "Impossibile connettersi", diff --git a/homeassistant/components/tautulli/translations/ja.json b/homeassistant/components/tautulli/translations/ja.json index fd51dc92c43..ab5376b83e0 100644 --- a/homeassistant/components/tautulli/translations/ja.json +++ b/homeassistant/components/tautulli/translations/ja.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "\u30b5\u30fc\u30d3\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", - "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f", - "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u8a2d\u5b9a\u3067\u304d\u308b\u306e\u306f1\u3064\u3060\u3051\u3067\u3059\u3002" + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", diff --git a/homeassistant/components/tautulli/translations/nl.json b/homeassistant/components/tautulli/translations/nl.json index f01a1fdb17d..e71bba57779 100644 --- a/homeassistant/components/tautulli/translations/nl.json +++ b/homeassistant/components/tautulli/translations/nl.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "Dienst is al geconfigureerd", - "reauth_successful": "Herauthenticatie geslaagd", - "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/tautulli/translations/no.json b/homeassistant/components/tautulli/translations/no.json index 0528a97beb9..27303223573 100644 --- a/homeassistant/components/tautulli/translations/no.json +++ b/homeassistant/components/tautulli/translations/no.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "Tjenesten er allerede konfigurert", - "reauth_successful": "Re-autentisering var vellykket", - "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." + "reauth_successful": "Re-autentisering var vellykket" }, "error": { "cannot_connect": "Tilkobling mislyktes", diff --git a/homeassistant/components/tautulli/translations/pl.json b/homeassistant/components/tautulli/translations/pl.json index 6dac9a79617..49f833f8811 100644 --- a/homeassistant/components/tautulli/translations/pl.json +++ b/homeassistant/components/tautulli/translations/pl.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "Us\u0142uga jest ju\u017c skonfigurowana", - "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119", - "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", diff --git a/homeassistant/components/tautulli/translations/pt-BR.json b/homeassistant/components/tautulli/translations/pt-BR.json index e5732024e3a..c8d7f364088 100644 --- a/homeassistant/components/tautulli/translations/pt-BR.json +++ b/homeassistant/components/tautulli/translations/pt-BR.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado", - "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida", - "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" }, "error": { "cannot_connect": "Falha ao conectar", diff --git a/homeassistant/components/tautulli/translations/ru.json b/homeassistant/components/tautulli/translations/ru.json index 4f777441385..6db4b0ae948 100644 --- a/homeassistant/components/tautulli/translations/ru.json +++ b/homeassistant/components/tautulli/translations/ru.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.", - "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", diff --git a/homeassistant/components/tautulli/translations/sv.json b/homeassistant/components/tautulli/translations/sv.json index 1d4ad84cf58..4702d828c22 100644 --- a/homeassistant/components/tautulli/translations/sv.json +++ b/homeassistant/components/tautulli/translations/sv.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "Tj\u00e4nsten \u00e4r redan konfigurerad", - "reauth_successful": "\u00c5terautentisering lyckades", - "single_instance_allowed": "Redan konfigurerad. Endast en konfiguration m\u00f6jlig." + "reauth_successful": "\u00c5terautentisering lyckades" }, "error": { "cannot_connect": "Det gick inte att ansluta.", diff --git a/homeassistant/components/tautulli/translations/tr.json b/homeassistant/components/tautulli/translations/tr.json index e5fd6c14b67..39b9ce97750 100644 --- a/homeassistant/components/tautulli/translations/tr.json +++ b/homeassistant/components/tautulli/translations/tr.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "Hizmet zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", - "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu", - "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", diff --git a/homeassistant/components/tautulli/translations/zh-Hant.json b/homeassistant/components/tautulli/translations/zh-Hant.json index c21af61cfa5..06df0275ea9 100644 --- a/homeassistant/components/tautulli/translations/zh-Hant.json +++ b/homeassistant/components/tautulli/translations/zh-Hant.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", - "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f", - "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", diff --git a/homeassistant/components/unifiprotect/translations/bg.json b/homeassistant/components/unifiprotect/translations/bg.json index ca8d83e0111..924204073df 100644 --- a/homeassistant/components/unifiprotect/translations/bg.json +++ b/homeassistant/components/unifiprotect/translations/bg.json @@ -48,17 +48,5 @@ }, "title": "UniFi Protect v{version} \u0435 \u0432\u0435\u0440\u0441\u0438\u044f \u0441 \u0440\u0430\u043d\u0435\u043d \u0434\u043e\u0441\u0442\u044a\u043f" } - }, - "options": { - "error": { - "invalid_mac_list": "\u0422\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0431\u044a\u0434\u0435 \u0441\u043f\u0438\u0441\u044a\u043a \u0441 MAC \u0430\u0434\u0440\u0435\u0441\u0438, \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438 \u0441\u044a\u0441 \u0437\u0430\u043f\u0435\u0442\u0430\u0438" - }, - "step": { - "init": { - "data": { - "ignored_devices": "\u0421\u043f\u0438\u0441\u044a\u043a \u0441 MAC \u0430\u0434\u0440\u0435\u0441\u0438 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430, \u043a\u043e\u0438\u0442\u043e \u0434\u0430 \u0441\u0435 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0430\u0442, \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d \u0441\u044a\u0441 \u0437\u0430\u043f\u0435\u0442\u0430\u0438" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/ca.json b/homeassistant/components/unifiprotect/translations/ca.json index 225b35c6225..31a15c62848 100644 --- a/homeassistant/components/unifiprotect/translations/ca.json +++ b/homeassistant/components/unifiprotect/translations/ca.json @@ -42,8 +42,10 @@ } }, "issues": { + "ea_setup_failed": { + "title": "Error de configuraci\u00f3 amb la versi\u00f3 d'acc\u00e9s anticipat" + }, "ea_warning": { - "description": "Est\u00e0s utilitzant UniFi Protect v{version} que \u00e9s una versi\u00f3 d'acc\u00e9s anticipat. Les versions d'acc\u00e9s anticipat no s\u00f3n compatibles amb Home Assistant i poden fer que la teva integraci\u00f3 d'UniFi Protect s'espatlli o no funcioni correctament.", "fix_flow": { "step": { "confirm": { @@ -58,15 +60,11 @@ } }, "options": { - "error": { - "invalid_mac_list": "Ha de ser una llista d'adreces MAC separades per comes" - }, "step": { "init": { "data": { "all_updates": "M\u00e8triques en temps real (ALERTA: augmenta considerablement l'\u00fas de CPU)", "disable_rtsp": "Desactiva el flux RTSP", - "ignored_devices": "Llista d'adreces MAC dels dispositius a ignorar, separades per comes", "max_media": "Nombre m\u00e0xim d'esdeveniments a carregar al navegador multim\u00e8dia (augmenta l'\u00fas de RAM)", "override_connection_host": "Substitueix l'amfitri\u00f3 de connexi\u00f3" }, diff --git a/homeassistant/components/unifiprotect/translations/de.json b/homeassistant/components/unifiprotect/translations/de.json index d7c3e127b63..843bb839046 100644 --- a/homeassistant/components/unifiprotect/translations/de.json +++ b/homeassistant/components/unifiprotect/translations/de.json @@ -47,7 +47,6 @@ "title": "Einrichtungsfehler bei Verwendung der Early-Access-Version" }, "ea_warning": { - "description": "Du verwendest v{version} von UniFi Protect, einer Early-Access-Version. Early-Access-Versionen werden von Home Assistant nicht unterst\u00fctzt und k\u00f6nnen dazu f\u00fchren, dass deine UniFi Protect-Integration nicht oder nicht wie erwartet funktioniert.", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "Muss eine durch Kommas getrennte Liste von MAC-Adressen sein" - }, "step": { "init": { "data": { "all_updates": "Echtzeitmetriken (WARNUNG: Erh\u00f6ht die CPU-Auslastung erheblich)", "allow_ea": "Early-Access-Versionen von Protect zulassen (WARNUNG: Markiert deine Integration als nicht unterst\u00fctzt)", "disable_rtsp": "RTSP-Stream deaktivieren", - "ignored_devices": "Kommagetrennte Liste von MAC-Adressen von Ger\u00e4ten, die ignoriert werden sollen", "max_media": "Maximale Anzahl von Ereignissen, die f\u00fcr den Medienbrowser geladen werden (erh\u00f6ht die RAM-Nutzung)", "override_connection_host": "Verbindungshost \u00fcberschreiben" }, diff --git a/homeassistant/components/unifiprotect/translations/el.json b/homeassistant/components/unifiprotect/translations/el.json index 2872232eb76..9c2d33e15d6 100644 --- a/homeassistant/components/unifiprotect/translations/el.json +++ b/homeassistant/components/unifiprotect/translations/el.json @@ -47,7 +47,6 @@ "title": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7\u03c2 Early Access" }, "ea_warning": { - "description": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 v{version} \u03c4\u03bf\u03c5 UniFi Protect, \u03b7 \u03bf\u03c0\u03bf\u03af\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03c0\u03c1\u03ce\u03b9\u03bc\u03b7\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2. \u039f\u03b9 \u03b5\u03ba\u03b4\u03cc\u03c3\u03b5\u03b9\u03c2 Early Access \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant \u03ba\u03b1\u03b9 \u03b5\u03bd\u03b4\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03ba\u03b1\u03bb\u03ad\u03c3\u03bf\u03c5\u03bd \u03b4\u03b9\u03b1\u03ba\u03bf\u03c0\u03ae \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 UniFi Protect \u03ae \u03bd\u03b1 \u03bc\u03b7\u03bd \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03bf\u03cd\u03bd \u03cc\u03c0\u03c9\u03c2 \u03b1\u03bd\u03b1\u03bc\u03ad\u03bd\u03b5\u03c4\u03b1\u03b9.", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "\u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1\u03c2 \u03ba\u03b1\u03c4\u03ac\u03bb\u03bf\u03b3\u03bf\u03c2 \u03b4\u03b9\u03b5\u03c5\u03b8\u03cd\u03bd\u03c3\u03b5\u03c9\u03bd MAC \u03c0\u03bf\u03c5 \u03c7\u03c9\u03c1\u03af\u03b6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03ba\u03cc\u03bc\u03bc\u03b1." - }, "step": { "init": { "data": { "all_updates": "\u039c\u03b5\u03c4\u03c1\u03ae\u03c3\u03b5\u03b9\u03c2 \u03c3\u03b5 \u03c0\u03c1\u03b1\u03b3\u03bc\u03b1\u03c4\u03b9\u03ba\u03cc \u03c7\u03c1\u03cc\u03bd\u03bf (\u03a0\u03a1\u039f\u0395\u0399\u0394\u039f\u03a0\u039f\u0399\u0397\u03a3\u0397: \u0391\u03c5\u03be\u03ac\u03bd\u03b5\u03b9 \u03c3\u03b7\u03bc\u03b1\u03bd\u03c4\u03b9\u03ba\u03ac \u03c4\u03b7 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c4\u03b7\u03c2 CPU)", "allow_ea": "\u039d\u03b1 \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03bf\u03bd\u03c4\u03b1\u03b9 \u03bf\u03b9 \u03b5\u03ba\u03b4\u03cc\u03c3\u03b5\u03b9\u03c2 Early Access \u03c4\u03bf\u03c5 Protect (\u03a0\u03a1\u039f\u0395\u0399\u0394\u039f\u03a0\u039f\u0399\u0397\u03a3\u0397: \u0398\u03b1 \u03b5\u03c0\u03b9\u03c3\u03b7\u03bc\u03b1\u03bd\u03b8\u03b5\u03af \u03b7 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 \u03c9\u03c2 \u03bc\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03b7)", "disable_rtsp": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03bf\u03ae RTSP", - "ignored_devices": "\u039a\u03b1\u03c4\u03ac\u03bb\u03bf\u03b3\u03bf\u03c2 \u03b4\u03b9\u03b5\u03c5\u03b8\u03cd\u03bd\u03c3\u03b5\u03c9\u03bd MAC \u03c4\u03c9\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ce\u03bd \u03c0\u03bf\u03c5 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b1\u03b3\u03bd\u03bf\u03b7\u03b8\u03bf\u03cd\u03bd \u03bc\u03b5 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03cc \u03ba\u03cc\u03bc\u03bc\u03b1\u03c4\u03bf\u03c2", "max_media": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03bf\u03c2 \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd\u03c4\u03c9\u03bd \u03c0\u03c1\u03bf\u03c2 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03c0\u03c1\u03cc\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1 \u03c0\u03b5\u03c1\u03b9\u03ae\u03b3\u03b7\u03c3\u03b7\u03c2 \u03c0\u03bf\u03bb\u03c5\u03bc\u03ad\u03c3\u03c9\u03bd (\u03b1\u03c5\u03be\u03ac\u03bd\u03b5\u03b9 \u03c4\u03b7 \u03c7\u03c1\u03ae\u03c3\u03b7 RAM)", "override_connection_host": "\u03a0\u03b1\u03c1\u03ac\u03ba\u03b1\u03bc\u03c8\u03b7 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, diff --git a/homeassistant/components/unifiprotect/translations/en.json b/homeassistant/components/unifiprotect/translations/en.json index 31b06ac9a17..65a398375fe 100644 --- a/homeassistant/components/unifiprotect/translations/en.json +++ b/homeassistant/components/unifiprotect/translations/en.json @@ -47,7 +47,6 @@ "title": "Setup error using Early Access version" }, "ea_warning": { - "description": "You are using v{version} of UniFi Protect which is an Early Access version. Early Access versions are not supported by Home Assistant and may cause your UniFi Protect integration to break or not work as expected.", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "Must be a list of MAC addresses seperated by commas" - }, "step": { "init": { "data": { "all_updates": "Realtime metrics (WARNING: Greatly increases CPU usage)", "allow_ea": "Allow Early Access versions of Protect (WARNING: Will mark your integration as unsupported)", "disable_rtsp": "Disable the RTSP stream", - "ignored_devices": "Comma separated list of MAC addresses of devices to ignore", "max_media": "Max number of event to load for Media Browser (increases RAM usage)", "override_connection_host": "Override Connection Host" }, diff --git a/homeassistant/components/unifiprotect/translations/es.json b/homeassistant/components/unifiprotect/translations/es.json index 5168bd988e2..c6e9d9ea7eb 100644 --- a/homeassistant/components/unifiprotect/translations/es.json +++ b/homeassistant/components/unifiprotect/translations/es.json @@ -47,7 +47,6 @@ "title": "Error de configuraci\u00f3n al usar la versi\u00f3n Early Access" }, "ea_warning": { - "description": "Est\u00e1s utilizando v{version} de UniFi Protect, que es una versi\u00f3n Early Access. Las versiones Early Access no son compatibles con Home Assistant y pueden causar que tu integraci\u00f3n con UniFi Protect no funcione por completo o como se esperaba.", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "Debe ser una lista de direcciones MAC separadas por comas" - }, "step": { "init": { "data": { "all_updates": "M\u00e9tricas en tiempo real (ADVERTENCIA: aumenta considerablemente el uso de la CPU)", "allow_ea": "Permitir versiones Early Access de Protect (ADVERTENCIA: marcar\u00e1 tu integraci\u00f3n como no admitida)", "disable_rtsp": "Deshabilitar la transmisi\u00f3n RTSP", - "ignored_devices": "Lista separada por comas de direcciones MAC de dispositivos para ignorar", "max_media": "N\u00famero m\u00e1ximo de eventos a cargar para el Navegador de Medios (aumenta el uso de RAM)", "override_connection_host": "Anular la conexi\u00f3n del host" }, diff --git a/homeassistant/components/unifiprotect/translations/et.json b/homeassistant/components/unifiprotect/translations/et.json index f6ef320e38e..1ae73482a13 100644 --- a/homeassistant/components/unifiprotect/translations/et.json +++ b/homeassistant/components/unifiprotect/translations/et.json @@ -47,7 +47,6 @@ "title": "Varajase juurdep\u00e4\u00e4su versiooni h\u00e4\u00e4lestamise t\u00f5rge" }, "ea_warning": { - "description": "Kasutad UniFi Protecti v{version}. Home Assistant ei toeta varajase juurdep\u00e4\u00e4su versioone ja see v\u00f5ib p\u00f5hjustada UniFi Protecti sidumise katkemist v\u00f5i ei t\u00f6\u00f6ta see ootusp\u00e4raselt.", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "Peab olema komadega eraldatud MAC-aadresside loend" - }, "step": { "init": { "data": { "all_updates": "Reaalajas m\u00f5\u00f5dikud (HOIATUS: suurendab oluliselt CPU kasutust)", "allow_ea": "Protecti varajase juurdep\u00e4\u00e4su versioonide lubamine (HOIATUS: m\u00e4rgib sidumise mitte toetatuks)", "disable_rtsp": "Keela RTSP voog", - "ignored_devices": "Komaga eraldatud loend nende seadmete MAC-aadressidest mida eirata", "max_media": "Meediumibrauserisse laaditavate s\u00fcndmuste maksimaalne arv (suurendab RAM-i kasutamist)", "override_connection_host": "\u00dchenduse hosti alistamine" }, diff --git a/homeassistant/components/unifiprotect/translations/fr.json b/homeassistant/components/unifiprotect/translations/fr.json index 8cb4b819715..c6527d39a39 100644 --- a/homeassistant/components/unifiprotect/translations/fr.json +++ b/homeassistant/components/unifiprotect/translations/fr.json @@ -42,15 +42,11 @@ } }, "options": { - "error": { - "invalid_mac_list": "Doit \u00eatre une liste d'adresses MAC s\u00e9par\u00e9es par des virgules" - }, "step": { "init": { "data": { "all_updates": "M\u00e9triques en temps r\u00e9el (AVERTISSEMENT\u00a0: augmente consid\u00e9rablement l'utilisation du processeur)", "disable_rtsp": "D\u00e9sactiver le flux RTSP", - "ignored_devices": "Liste s\u00e9par\u00e9e par des virgules des adresses MAC des appareils \u00e0 ignorer", "max_media": "Nombre maximal d'\u00e9v\u00e9nements \u00e0 charger pour le navigateur multim\u00e9dia (augmente l'utilisation de la RAM)", "override_connection_host": "Ignorer l'h\u00f4te de connexion" }, diff --git a/homeassistant/components/unifiprotect/translations/hu.json b/homeassistant/components/unifiprotect/translations/hu.json index cbc240cae13..600eeec24c7 100644 --- a/homeassistant/components/unifiprotect/translations/hu.json +++ b/homeassistant/components/unifiprotect/translations/hu.json @@ -47,7 +47,6 @@ "title": "Be\u00e1ll\u00edt\u00e1si hiba a korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3 haszn\u00e1lat\u00e1val" }, "ea_warning": { - "description": "\u00d6n az UniFi Protect {version} verzi\u00f3j\u00e1t haszn\u00e1lja. A korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3kat Home Assistant nem t\u00e1mogatja, \u00e9s az UniFi Protect integr\u00e1ci\u00f3ja le\u00e1llhat, vagy nem az elv\u00e1rt m\u00f3don m\u0171k\u00f6dhet.", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "Egy list\u00e1ra van sz\u00fcks\u00e9g, melyben a MAC-c\u00edmek vessz\u0151vel vannak elv\u00e1lasztva" - }, "step": { "init": { "data": { "all_updates": "Val\u00f3s idej\u0171 m\u00e9r\u0151sz\u00e1mok (FIGYELEM: nagym\u00e9rt\u00e9kben n\u00f6veli a CPU terhel\u00e9st)", "allow_ea": "A Protect korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3inak enged\u00e9lyez\u00e9se (FIGYELMEZTET\u00c9S: Az integr\u00e1ci\u00f3t nem t\u00e1mogatottk\u00e9nt jel\u00f6li meg)", "disable_rtsp": "Az RTSP adatfolyam letilt\u00e1sa", - "ignored_devices": "A figyelmen k\u00edv\u00fcl hagyand\u00f3 eszk\u00f6z\u00f6k MAC-c\u00edm\u00e9nek vessz\u0151vel elv\u00e1lasztott list\u00e1ja", "max_media": "A m\u00e9diab\u00f6ng\u00e9sz\u0151be bet\u00f6ltend\u0151 esem\u00e9nyek maxim\u00e1lis sz\u00e1ma (n\u00f6veli a RAM-haszn\u00e1latot)", "override_connection_host": "Kapcsolat c\u00edm\u00e9nek fel\u00fclb\u00edr\u00e1l\u00e1sa" }, diff --git a/homeassistant/components/unifiprotect/translations/id.json b/homeassistant/components/unifiprotect/translations/id.json index 075fb120e70..6453d8c8d86 100644 --- a/homeassistant/components/unifiprotect/translations/id.json +++ b/homeassistant/components/unifiprotect/translations/id.json @@ -47,7 +47,6 @@ "title": "Kesalahan penyiapan menggunakan versi Early Access" }, "ea_warning": { - "description": "Anda menggunakan UniFi Protect v{version} yang merupakan versi Early Access. Versi Early Access tidak didukung oleh Home Assistant dan dapat menyebabkan integrasi UniFi Protect rusak atau tidak berfungsi seperti yang diharapkan.", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "Harus berupa daftar alamat MAC yang dipisahkan dengan koma" - }, "step": { "init": { "data": { "all_updates": "Metrik waktu nyata (PERINGATAN: Meningkatkan penggunaan CPU)", "allow_ea": "Izinkan versi Early Access UniFi Protect (PERINGATAN: Akan menandai integrasi Anda sebagai tidak didukung)", "disable_rtsp": "Nonaktifkan aliran RTSP", - "ignored_devices": "Daftar alamat MAC perangkat yang dipisahkan koma untuk diabaikan", "max_media": "Jumlah maksimum peristiwa yang akan dimuat untuk Browser Media (meningkatkan penggunaan RAM)", "override_connection_host": "Timpa Host Koneksi" }, diff --git a/homeassistant/components/unifiprotect/translations/it.json b/homeassistant/components/unifiprotect/translations/it.json index 609eb432fa2..d37fbcea83c 100644 --- a/homeassistant/components/unifiprotect/translations/it.json +++ b/homeassistant/components/unifiprotect/translations/it.json @@ -47,7 +47,6 @@ "title": "Errore di configurazione durante l'utilizzo della versione ad accesso anticipato" }, "ea_warning": { - "description": "Si sta utilizzando v{version} di UniFi Protect che \u00e8 una versione in accesso anticipato. Le versioni in accesso anticipato non sono supportate da Home Assistant e potrebbero causare l'interruzione o il mancato funzionamento dell'integrazione di UniFi Protect.", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "Deve essere un elenco di indirizzi MAC separati da virgole" - }, "step": { "init": { "data": { "all_updates": "Metriche in tempo reale (ATTENZIONE: aumenta notevolmente l'utilizzo della CPU)", "allow_ea": "Consenti versioni ad accesso anticipato di Protect (ATTENZIONE: l'integrazione verr\u00e0 contrassegnata come non supportata)", "disable_rtsp": "Disabilita il flusso RTSP", - "ignored_devices": "Elenco separato da virgole di indirizzi MAC di dispositivi da ignorare", "max_media": "Numero massimo di eventi da caricare per Media Browser (aumenta l'utilizzo della RAM)", "override_connection_host": "Sostituisci host di connessione" }, diff --git a/homeassistant/components/unifiprotect/translations/ja.json b/homeassistant/components/unifiprotect/translations/ja.json index b55c9c53e84..711e039edaf 100644 --- a/homeassistant/components/unifiprotect/translations/ja.json +++ b/homeassistant/components/unifiprotect/translations/ja.json @@ -42,15 +42,11 @@ } }, "options": { - "error": { - "invalid_mac_list": "\u30ab\u30f3\u30de\u3067\u533a\u5207\u3089\u308c\u305fMAC\u30a2\u30c9\u30ec\u30b9\u306e\u30ea\u30b9\u30c8\u3067\u3042\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059" - }, "step": { "init": { "data": { "all_updates": "\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u30e1\u30c8\u30ea\u30c3\u30af(Realtime metrics)(\u8b66\u544a: CPU\u4f7f\u7528\u7387\u304c\u5927\u5e45\u306b\u5897\u52a0\u3057\u307e\u3059)", "disable_rtsp": "RTSP\u30b9\u30c8\u30ea\u30fc\u30e0\u3092\u7121\u52b9\u306b\u3059\u308b", - "ignored_devices": "\u7121\u8996\u3059\u308b\u6a5f\u5668\u306eMAC\u30a2\u30c9\u30ec\u30b9\u306e\u30ab\u30f3\u30de\u533a\u5207\u308a\u30ea\u30b9\u30c8", "max_media": "\u30e1\u30c7\u30a3\u30a2\u30d6\u30e9\u30a6\u30b6\u306b\u30ed\u30fc\u30c9\u3059\u308b\u30a4\u30d9\u30f3\u30c8\u306e\u6700\u5927\u6570(RAM\u4f7f\u7528\u91cf\u304c\u5897\u52a0)", "override_connection_host": "\u63a5\u7d9a\u30db\u30b9\u30c8\u3092\u4e0a\u66f8\u304d" }, diff --git a/homeassistant/components/unifiprotect/translations/no.json b/homeassistant/components/unifiprotect/translations/no.json index d6e453b243a..74b59ebb9b4 100644 --- a/homeassistant/components/unifiprotect/translations/no.json +++ b/homeassistant/components/unifiprotect/translations/no.json @@ -47,7 +47,6 @@ "title": "Konfigurasjonsfeil ved bruk av tidlig tilgangsversjon" }, "ea_warning": { - "description": "Du bruker v {version} av UniFi Protect som er en tidlig tilgangsversjon. Early Access-versjoner st\u00f8ttes ikke av Home Assistant og kan f\u00f8re til at UniFi Protect-integrasjonen din g\u00e5r i stykker eller ikke fungerer som forventet.", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "M\u00e5 v\u00e6re en liste over MAC-adresser atskilt med komma" - }, "step": { "init": { "data": { "all_updates": "Sanntidsm\u00e5linger (ADVARSEL: \u00d8ker CPU-bruken betraktelig)", "allow_ea": "Tillat tidlig tilgangsversjoner av Protect (ADVARSEL: Vil merke integrasjonen din som ikke st\u00f8ttet)", "disable_rtsp": "Deaktiver RTSP-str\u00f8mmen", - "ignored_devices": "Kommadelt liste over MAC-adresser til enheter som skal ignoreres", "max_media": "Maks antall hendelser som skal lastes for medienettleseren (\u00f8ker RAM-bruken)", "override_connection_host": "Overstyr tilkoblingsvert" }, diff --git a/homeassistant/components/unifiprotect/translations/pl.json b/homeassistant/components/unifiprotect/translations/pl.json index 7b74d044bcb..03175f0728b 100644 --- a/homeassistant/components/unifiprotect/translations/pl.json +++ b/homeassistant/components/unifiprotect/translations/pl.json @@ -47,7 +47,6 @@ "title": "B\u0142\u0105d konfiguracji w wersji Early Access" }, "ea_warning": { - "description": "U\u017cywasz UniFi Protect v{version}, kt\u00f3ra jest wersj\u0105 Early Access. Wersje Early Access nie s\u0105 obs\u0142ugiwane przez Home Assistanta i mog\u0105 spowodowa\u0107 uszkodzenie integracji UniFi Protect lub niedzia\u0142anie zgodnie z oczekiwaniami.", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "Musi to by\u0107 lista adres\u00f3w MAC oddzielonych przecinkami" - }, "step": { "init": { "data": { "all_updates": "Metryki w czasie rzeczywistym (UWAGA: Znacznie zwi\u0119ksza u\u017cycie CPU)", "allow_ea": "Zezw\u00f3l na wersje UniFi Protect Early Access (OSTRZE\u017bENIE: Twoja integracja zostanie oznaczona jako nieobs\u0142ugiwana)", "disable_rtsp": "Wy\u0142\u0105cz strumie\u0144 RTSP", - "ignored_devices": "Oddzielona przecinkami lista adres\u00f3w MAC urz\u0105dze\u0144 do zignorowania", "max_media": "Maksymalna liczba zdarze\u0144 do za\u0142adowania dla przegl\u0105darki medi\u00f3w (zwi\u0119ksza u\u017cycie pami\u0119ci RAM)", "override_connection_host": "Zast\u0105p host po\u0142\u0105czenia" }, diff --git a/homeassistant/components/unifiprotect/translations/pt-BR.json b/homeassistant/components/unifiprotect/translations/pt-BR.json index b0e58459815..57be639a7f5 100644 --- a/homeassistant/components/unifiprotect/translations/pt-BR.json +++ b/homeassistant/components/unifiprotect/translations/pt-BR.json @@ -47,7 +47,6 @@ "title": "Erro de configura\u00e7\u00e3o usando a vers\u00e3o de acesso antecipado" }, "ea_warning": { - "description": "Voc\u00ea est\u00e1 usando v {version} do UniFi Protect, que \u00e9 uma vers\u00e3o de acesso antecipado. As vers\u00f5es de acesso antecipado n\u00e3o s\u00e3o suportadas pelo Home Assistant e podem fazer com que a integra\u00e7\u00e3o do UniFi Protect seja interrompida ou n\u00e3o funcione conforme o esperado.", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "Deve ser uma lista de endere\u00e7os MAC separados por v\u00edrgulas" - }, "step": { "init": { "data": { "all_updates": "M\u00e9tricas em tempo real (AVISO: aumenta muito o uso da CPU)", "allow_ea": "Permitir vers\u00f5es de acesso antecipado do Protect (AVISO: marcar\u00e1 sua integra\u00e7\u00e3o como n\u00e3o suportada)", "disable_rtsp": "Desativar o fluxo RTSP", - "ignored_devices": "Lista separada por v\u00edrgulas de endere\u00e7os MAC de dispositivos a serem ignorados", "max_media": "N\u00famero m\u00e1ximo de eventos a serem carregados para o Media Browser (aumenta o uso de RAM)", "override_connection_host": "Anular o host de conex\u00e3o" }, diff --git a/homeassistant/components/unifiprotect/translations/ru.json b/homeassistant/components/unifiprotect/translations/ru.json index bcb45a539db..5472a1b2037 100644 --- a/homeassistant/components/unifiprotect/translations/ru.json +++ b/homeassistant/components/unifiprotect/translations/ru.json @@ -47,7 +47,6 @@ "title": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0432\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430" }, "ea_warning": { - "description": "\u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 UniFi Protect v{version}. \u0412\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f Home Assistant \u0438 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0431\u043e\u0439 \u0441\u043f\u0438\u0441\u043e\u043a MAC-\u0430\u0434\u0440\u0435\u0441\u043e\u0432, \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u044f\u0442\u044b\u043c\u0438." - }, "step": { "init": { "data": { "all_updates": "\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 (\u0412\u041d\u0418\u041c\u0410\u041d\u0418\u0415: \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u0426\u041f)", "allow_ea": "\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 UniFi Protect (\u0412\u041d\u0418\u041c\u0410\u041d\u0418\u0415: \u0412\u0430\u0448\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043c\u0435\u0447\u0435\u043d\u0430 \u043a\u0430\u043a \u043d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f)", "disable_rtsp": "\u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0442\u043e\u043a RTSP", - "ignored_devices": "\u0421\u043f\u0438\u0441\u043e\u043a MAC-\u0430\u0434\u0440\u0435\u0441\u043e\u0432 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043f\u044f\u0442\u0443\u044e", "max_media": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0434\u043b\u044f \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u043c\u0443\u043b\u044c\u0442\u0438\u043c\u0435\u0434\u0438\u0430 (\u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438)", "override_connection_host": "\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0443\u0437\u0435\u043b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f" }, diff --git a/homeassistant/components/unifiprotect/translations/sv.json b/homeassistant/components/unifiprotect/translations/sv.json index 98b1862cc3d..37c3c792f15 100644 --- a/homeassistant/components/unifiprotect/translations/sv.json +++ b/homeassistant/components/unifiprotect/translations/sv.json @@ -42,15 +42,11 @@ } }, "options": { - "error": { - "invalid_mac_list": "M\u00e5ste vara en lista \u00f6ver MAC-adresser separerade med kommatecken" - }, "step": { "init": { "data": { "all_updates": "Realtidsm\u00e4tningar (VARNING: \u00d6kar CPU-anv\u00e4ndningen avsev\u00e4rt)", "disable_rtsp": "Inaktivera RTSP-str\u00f6mmen", - "ignored_devices": "Kommaseparerad lista \u00f6ver MAC-adresser f\u00f6r enheter att ignorera", "max_media": "Max antal h\u00e4ndelser som ska laddas f\u00f6r Media Browser (\u00f6kar RAM-anv\u00e4ndning)", "override_connection_host": "\u00c5sidos\u00e4tt anslutningsv\u00e4rd" }, diff --git a/homeassistant/components/unifiprotect/translations/tr.json b/homeassistant/components/unifiprotect/translations/tr.json index 65a8c52f368..d26f6af41ce 100644 --- a/homeassistant/components/unifiprotect/translations/tr.json +++ b/homeassistant/components/unifiprotect/translations/tr.json @@ -42,15 +42,11 @@ } }, "options": { - "error": { - "invalid_mac_list": "Virg\u00fclle ayr\u0131lm\u0131\u015f bir MAC adresleri listesi olmal\u0131d\u0131r" - }, "step": { "init": { "data": { "all_updates": "Ger\u00e7ek zamanl\u0131 \u00f6l\u00e7\u00fcmler (UYARI: CPU kullan\u0131m\u0131n\u0131 b\u00fcy\u00fck \u00f6l\u00e7\u00fcde art\u0131r\u0131r)", "disable_rtsp": "RTSP ak\u0131\u015f\u0131n\u0131 devre d\u0131\u015f\u0131 b\u0131rak\u0131n", - "ignored_devices": "Yok say\u0131lacak ayg\u0131tlar\u0131n MAC adreslerinin virg\u00fclle ayr\u0131lm\u0131\u015f listesi", "max_media": "Medya Taray\u0131c\u0131 i\u00e7in y\u00fcklenecek maksimum olay say\u0131s\u0131 (RAM kullan\u0131m\u0131n\u0131 art\u0131r\u0131r)", "override_connection_host": "Ba\u011flant\u0131 Ana Bilgisayar\u0131n\u0131 Ge\u00e7ersiz K\u0131l" }, diff --git a/homeassistant/components/unifiprotect/translations/zh-Hant.json b/homeassistant/components/unifiprotect/translations/zh-Hant.json index c358ea9dec2..8c1fbe273da 100644 --- a/homeassistant/components/unifiprotect/translations/zh-Hant.json +++ b/homeassistant/components/unifiprotect/translations/zh-Hant.json @@ -47,7 +47,6 @@ "title": "\u4f7f\u7528\u6436\u5148\u9ad4\u9a57\u7248\u8a2d\u5b9a\u932f\u8aa4" }, "ea_warning": { - "description": "\u6b63\u5728\u4f7f\u7528\u7684 UniFi Protect {version} \u7248\u3001\u70ba Home Assistant \u4e0d\u652f\u63f4\u7684\u6436\u5148\u9ad4\u9a57\u7248\u672c\uff0c\u53ef\u80fd\u6703\u5c0e\u81f4 UniFi Protect \u6574\u5408\u51fa\u73fe\u554f\u984c\u3001\u6216\u7121\u6cd5\u6b63\u5e38\u5de5\u4f5c\u3002", "fix_flow": { "step": { "confirm": { @@ -64,16 +63,12 @@ } }, "options": { - "error": { - "invalid_mac_list": "\u5fc5\u9808\u70ba\u4ee5\u9017\u865f\uff08\uff1a\uff09\u5206\u9694\u958b\u7684 MAC \u5730\u5740\u5217\u8868" - }, "step": { "init": { "data": { "all_updates": "\u5373\u6642\u6307\u6a19\uff08\u8b66\u544a\uff1a\u5927\u91cf\u63d0\u5347 CPU \u4f7f\u7528\u7387\uff09", "allow_ea": "\u5141\u8a31\u4f7f\u7528\u6436\u5148\u9ad4\u9a57\u7248 Protect\uff08\u8b66\u544a\uff1a\u53ef\u80fd\u6703\u5c0e\u81f4\u6574\u5408\u8b8a\u70ba\u4e0d\u652f\u63f4\uff09)", "disable_rtsp": "\u95dc\u9589 RTSP \u4e32\u6d41", - "ignored_devices": "\u4ee5\u9017\u865f\u5206\u9694\u7684\u5ffd\u7565 MAC \u4f4d\u5740\u5217\u8868", "max_media": "\u5a92\u9ad4\u700f\u89bd\u5668\u6700\u9ad8\u8f09\u5165\u4e8b\u4ef6\u6578\uff08\u589e\u52a0\u8a18\u61b6\u9ad4\u4f7f\u7528\uff09", "override_connection_host": "\u7f6e\u63db\u9023\u7dda\u4e3b\u6a5f\u7aef" }, diff --git a/homeassistant/components/volvooncall/translations/bg.json b/homeassistant/components/volvooncall/translations/bg.json index aaa9b046b2d..1227fb35873 100644 --- a/homeassistant/components/volvooncall/translations/bg.json +++ b/homeassistant/components/volvooncall/translations/bg.json @@ -14,7 +14,6 @@ "mutable": "\u0420\u0430\u0437\u0440\u0435\u0448\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u0434\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u043e\u043d\u043d\u043e \u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u043d\u0435 / \u0437\u0430\u043a\u043b\u044e\u0447\u0432\u0430\u043d\u0435 \u0438 \u0434\u0440.", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "region": "\u0420\u0435\u0433\u0438\u043e\u043d", - "scandinavian_miles": "\u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u0441\u043a\u0430\u043d\u0434\u0438\u043d\u0430\u0432\u0441\u043a\u0438 \u043c\u0438\u043b\u0438", "unit_system": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0438", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" } diff --git a/homeassistant/components/volvooncall/translations/ca.json b/homeassistant/components/volvooncall/translations/ca.json index b261c0dc095..4f5859b5f01 100644 --- a/homeassistant/components/volvooncall/translations/ca.json +++ b/homeassistant/components/volvooncall/translations/ca.json @@ -14,7 +14,6 @@ "mutable": "Permet l'engegada / bloqueig / etc, remot.", "password": "Contrasenya", "region": "Regi\u00f3", - "scandinavian_miles": "Utilitza milles escandinaves", "unit_system": "Sistema d'unitats", "username": "Nom d'usuari" } diff --git a/homeassistant/components/volvooncall/translations/de.json b/homeassistant/components/volvooncall/translations/de.json index 5d00fbb00a5..18da232c19e 100644 --- a/homeassistant/components/volvooncall/translations/de.json +++ b/homeassistant/components/volvooncall/translations/de.json @@ -14,7 +14,6 @@ "mutable": "Fernstart / -verriegelung / etc. zulassen", "password": "Passwort", "region": "Region", - "scandinavian_miles": "Skandinavische Meilen verwenden", "unit_system": "Einheitensystem", "username": "Benutzername" } diff --git a/homeassistant/components/volvooncall/translations/el.json b/homeassistant/components/volvooncall/translations/el.json index f216755a0b8..c845bec3eba 100644 --- a/homeassistant/components/volvooncall/translations/el.json +++ b/homeassistant/components/volvooncall/translations/el.json @@ -14,7 +14,6 @@ "mutable": "\u039d\u03b1 \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03b5\u03c4\u03b1\u03b9 \u03b7 \u03b1\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7 / \u03ba\u03bb\u03b5\u03af\u03b4\u03c9\u03bc\u03b1 / \u03ba.\u03bb\u03c0.", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "region": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae", - "scandinavian_miles": "\u03a7\u03c1\u03ae\u03c3\u03b7 \u03a3\u03ba\u03b1\u03bd\u03b4\u03b9\u03bd\u03b1\u03b2\u03b9\u03ba\u03ce\u03bd \u039c\u03b9\u03bb\u03af\u03c9\u03bd", "unit_system": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03a3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" } diff --git a/homeassistant/components/volvooncall/translations/en.json b/homeassistant/components/volvooncall/translations/en.json index fca96e5e0ed..55e5baa9b5c 100644 --- a/homeassistant/components/volvooncall/translations/en.json +++ b/homeassistant/components/volvooncall/translations/en.json @@ -14,7 +14,6 @@ "mutable": "Allow Remote Start / Lock / etc.", "password": "Password", "region": "Region", - "scandinavian_miles": "Use Scandinavian Miles", "unit_system": "Unit System", "username": "Username" } diff --git a/homeassistant/components/volvooncall/translations/es.json b/homeassistant/components/volvooncall/translations/es.json index bcca5a0da4d..548d634f29d 100644 --- a/homeassistant/components/volvooncall/translations/es.json +++ b/homeassistant/components/volvooncall/translations/es.json @@ -14,7 +14,6 @@ "mutable": "Permitir el arranque / bloqueo a distancia / etc.", "password": "Contrase\u00f1a", "region": "Regi\u00f3n", - "scandinavian_miles": "Utilizar millas escandinavas", "unit_system": "Sistema de unidades", "username": "Nombre de usuario" } diff --git a/homeassistant/components/volvooncall/translations/et.json b/homeassistant/components/volvooncall/translations/et.json index 9f2912b5d53..a14cbde6e5c 100644 --- a/homeassistant/components/volvooncall/translations/et.json +++ b/homeassistant/components/volvooncall/translations/et.json @@ -14,7 +14,6 @@ "mutable": "Luba kaugk\u00e4ivitus / lukustamine / jne.", "password": "Salas\u00f5na", "region": "Piirkond", - "scandinavian_miles": "Kasuta Scandinavian Miles", "unit_system": "\u00dchikute s\u00fcsteem", "username": "Kasutajanimi" } diff --git a/homeassistant/components/volvooncall/translations/fr.json b/homeassistant/components/volvooncall/translations/fr.json index 2449ab4bed4..c720557ccf2 100644 --- a/homeassistant/components/volvooncall/translations/fr.json +++ b/homeassistant/components/volvooncall/translations/fr.json @@ -14,7 +14,6 @@ "mutable": "Autoriser le d\u00e9marrage, le verrouillage, etc. \u00e0 distance", "password": "Mot de passe", "region": "R\u00e9gion", - "scandinavian_miles": "Utiliser les miles scandinaves", "unit_system": "Syst\u00e8me d'unit\u00e9s", "username": "Nom d'utilisateur" } diff --git a/homeassistant/components/volvooncall/translations/hu.json b/homeassistant/components/volvooncall/translations/hu.json index 5b382780bea..e4d4b17c48f 100644 --- a/homeassistant/components/volvooncall/translations/hu.json +++ b/homeassistant/components/volvooncall/translations/hu.json @@ -14,7 +14,6 @@ "mutable": "Enged\u00e9lyezze a t\u00e1voli ind\u00edt\u00e1st / z\u00e1r\u00e1st / stb.", "password": "Jelsz\u00f3", "region": "R\u00e9gi\u00f3", - "scandinavian_miles": "Skandin\u00e1v m\u00e9rf\u00f6ld haszn\u00e1lata", "unit_system": "Egys\u00e9grendszer", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" } diff --git a/homeassistant/components/volvooncall/translations/id.json b/homeassistant/components/volvooncall/translations/id.json index f1090bd2509..a144ef366b5 100644 --- a/homeassistant/components/volvooncall/translations/id.json +++ b/homeassistant/components/volvooncall/translations/id.json @@ -14,7 +14,6 @@ "mutable": "Izinkan Mulai/Kunci Jarak Jauh, dll.", "password": "Kata Sandi", "region": "Wilayah", - "scandinavian_miles": "Gunakan Mil Skandinavia", "unit_system": "Sistem Unit", "username": "Nama Pengguna" } diff --git a/homeassistant/components/volvooncall/translations/it.json b/homeassistant/components/volvooncall/translations/it.json index d96646b7873..781233e5356 100644 --- a/homeassistant/components/volvooncall/translations/it.json +++ b/homeassistant/components/volvooncall/translations/it.json @@ -14,7 +14,6 @@ "mutable": "Consenti da remoto l'avvio / il blocco / ecc.", "password": "Password", "region": "Regione", - "scandinavian_miles": "Usa le miglia scandinave", "unit_system": "Unit\u00e0 di misura", "username": "Nome utente" } diff --git a/homeassistant/components/volvooncall/translations/ja.json b/homeassistant/components/volvooncall/translations/ja.json index 4127b966710..3cd114643b0 100644 --- a/homeassistant/components/volvooncall/translations/ja.json +++ b/homeassistant/components/volvooncall/translations/ja.json @@ -14,7 +14,6 @@ "mutable": "\u30ea\u30e2\u30fc\u30c8\u30b9\u30bf\u30fc\u30c8/\u30ed\u30c3\u30af\u306a\u3069\u3092\u8a31\u53ef\u3057\u307e\u3059\u3002", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "region": "\u30ea\u30fc\u30b8\u30e7\u30f3", - "scandinavian_miles": "\u30b9\u30ab\u30f3\u30b8\u30ca\u30d3\u30a2\u30de\u30a4\u30eb(Scandinavian Miles)\u3092\u4f7f\u7528\u3059\u308b", "unit_system": "\u5358\u4f4d\u30b7\u30b9\u30c6\u30e0", "username": "\u30e6\u30fc\u30b6\u30fc\u540d" } diff --git a/homeassistant/components/volvooncall/translations/no.json b/homeassistant/components/volvooncall/translations/no.json index 48639f07b67..963ea6f1ca7 100644 --- a/homeassistant/components/volvooncall/translations/no.json +++ b/homeassistant/components/volvooncall/translations/no.json @@ -14,7 +14,6 @@ "mutable": "Tillat fjernstart / l\u00e5s / etc.", "password": "Passord", "region": "Region", - "scandinavian_miles": "Bruk Skandinaviske Miles", "unit_system": "Enhetssystem", "username": "Brukernavn" } diff --git a/homeassistant/components/volvooncall/translations/pl.json b/homeassistant/components/volvooncall/translations/pl.json index 5bdf7a253b5..fa69a7e1a51 100644 --- a/homeassistant/components/volvooncall/translations/pl.json +++ b/homeassistant/components/volvooncall/translations/pl.json @@ -14,7 +14,6 @@ "mutable": "Zezwalaj na zdalne uruchamianie / zamykanie / itp.", "password": "Has\u0142o", "region": "Region", - "scandinavian_miles": "U\u017cywaj skandynawskich mil", "unit_system": "System metryczny", "username": "Nazwa u\u017cytkownika" } diff --git a/homeassistant/components/volvooncall/translations/pt-BR.json b/homeassistant/components/volvooncall/translations/pt-BR.json index d8bb8a945d4..3a8ded27da9 100644 --- a/homeassistant/components/volvooncall/translations/pt-BR.json +++ b/homeassistant/components/volvooncall/translations/pt-BR.json @@ -14,7 +14,6 @@ "mutable": "Permitir partida remota / bloqueio / etc.", "password": "Senha", "region": "Regi\u00e3o", - "scandinavian_miles": "Usar milhas escandinavas", "unit_system": "Sistema de Unidades", "username": "Usu\u00e1rio" } diff --git a/homeassistant/components/volvooncall/translations/pt.json b/homeassistant/components/volvooncall/translations/pt.json index 232da9d130d..b89c4a9f74a 100644 --- a/homeassistant/components/volvooncall/translations/pt.json +++ b/homeassistant/components/volvooncall/translations/pt.json @@ -4,8 +4,7 @@ "user": { "data": { "mutable": "Permitir Partida/Bloqueio Remoto/etc.", - "region": "Regi\u00e3o", - "scandinavian_miles": "Use milhas escandinavas" + "region": "Regi\u00e3o" } } } diff --git a/homeassistant/components/volvooncall/translations/ru.json b/homeassistant/components/volvooncall/translations/ru.json index 3a125859e72..a1924c539cf 100644 --- a/homeassistant/components/volvooncall/translations/ru.json +++ b/homeassistant/components/volvooncall/translations/ru.json @@ -14,7 +14,6 @@ "mutable": "\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0434\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a / \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0443 \u0438 \u0442.\u0434.", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "region": "\u041e\u0431\u043b\u0430\u0441\u0442\u044c", - "scandinavian_miles": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043a\u0430\u043d\u0434\u0438\u043d\u0430\u0432\u0441\u043a\u0438\u0435 \u043c\u0438\u043b\u0438", "unit_system": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043c\u0435\u0440", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" } diff --git a/homeassistant/components/volvooncall/translations/sv.json b/homeassistant/components/volvooncall/translations/sv.json index 48d56656c6a..a8fc9c1c058 100644 --- a/homeassistant/components/volvooncall/translations/sv.json +++ b/homeassistant/components/volvooncall/translations/sv.json @@ -14,7 +14,6 @@ "mutable": "Till\u00e5t fj\u00e4rrstart / l\u00e5s / etc.", "password": "L\u00f6senord", "region": "Region", - "scandinavian_miles": "Anv\u00e4nd Skandinaviska mil", "unit_system": "Enhetssystem", "username": "Anv\u00e4ndarnamn" } diff --git a/homeassistant/components/volvooncall/translations/tr.json b/homeassistant/components/volvooncall/translations/tr.json index 4db94970086..3052feeb865 100644 --- a/homeassistant/components/volvooncall/translations/tr.json +++ b/homeassistant/components/volvooncall/translations/tr.json @@ -14,7 +14,6 @@ "mutable": "Uzaktan \u00c7al\u0131\u015ft\u0131rmaya / Kilitlemeye / vb. izin verin.", "password": "Parola", "region": "B\u00f6lge", - "scandinavian_miles": "\u0130skandinav Millerini Kullan\u0131n", "unit_system": "Birim Sistemi", "username": "Kullan\u0131c\u0131 Ad\u0131" } diff --git a/homeassistant/components/volvooncall/translations/zh-Hant.json b/homeassistant/components/volvooncall/translations/zh-Hant.json index 65aeee8325f..e52f960a406 100644 --- a/homeassistant/components/volvooncall/translations/zh-Hant.json +++ b/homeassistant/components/volvooncall/translations/zh-Hant.json @@ -14,7 +14,6 @@ "mutable": "\u5141\u8a31\u9060\u7aef\u555f\u52d5/\u4e0a\u9396/\u7b49\u529f\u80fd\u3002", "password": "\u5bc6\u78bc", "region": "\u5340\u57df", - "scandinavian_miles": "\u4f7f\u7528\u7d0d\u7dad\u4e9e\u82f1\u91cc", "unit_system": "\u55ae\u4f4d\u7cfb\u7d71", "username": "\u4f7f\u7528\u8005\u540d\u7a31" } diff --git a/homeassistant/components/withings/translations/bg.json b/homeassistant/components/withings/translations/bg.json index ad1284c7a48..fd9e1c4f352 100644 --- a/homeassistant/components/withings/translations/bg.json +++ b/homeassistant/components/withings/translations/bg.json @@ -18,9 +18,6 @@ "description": "\u041a\u043e\u0439 \u043f\u0440\u043e\u0444\u0438\u043b \u0441\u0442\u0435 \u0438\u0437\u0431\u0440\u0430\u043b\u0438 \u043d\u0430 \u0443\u0435\u0431\u0441\u0430\u0439\u0442\u0430 \u043d\u0430 Withings? \u0412\u0430\u0436\u043d\u043e \u0435 \u043f\u0440\u043e\u0444\u0438\u043b\u0438\u0442\u0435 \u0434\u0430 \u0441\u044a\u0432\u043f\u0430\u0434\u0430\u0442, \u0432 \u043f\u0440\u043e\u0442\u0438\u0432\u0435\u043d \u0441\u043b\u0443\u0447\u0430\u0439 \u0434\u0430\u043d\u043d\u0438\u0442\u0435 \u0449\u0435 \u0431\u044a\u0434\u0430\u0442 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u043d\u043e \u043e\u0437\u043d\u0430\u0447\u0435\u043d\u0438.", "title": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u0438 \u043f\u0440\u043e\u0444\u0438\u043b." }, - "reauth": { - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" - }, "reauth_confirm": { "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" } diff --git a/homeassistant/components/withings/translations/ca.json b/homeassistant/components/withings/translations/ca.json index 91be6ffabbe..2525bfb82b4 100644 --- a/homeassistant/components/withings/translations/ca.json +++ b/homeassistant/components/withings/translations/ca.json @@ -24,10 +24,6 @@ "description": "Ha de proporcionar un nom de perfil \u00fanic per a aquestes dades. Normalment \u00e9s el nom del perfil seleccionat en el pas anterior.", "title": "Perfil d'usuari." }, - "reauth": { - "description": "El perfil \"{profile}\" s'ha de tornar a autenticar per poder continuar rebent dades de Withings.", - "title": "Reautenticaci\u00f3 de la integraci\u00f3" - }, "reauth_confirm": { "description": "El perfil \"{profile}\" s'ha de tornar a autenticar per poder continuar rebent dades de Withings.", "title": "Reautenticar la integraci\u00f3" diff --git a/homeassistant/components/withings/translations/cs.json b/homeassistant/components/withings/translations/cs.json index 06762a5fb1b..285bb39f1e7 100644 --- a/homeassistant/components/withings/translations/cs.json +++ b/homeassistant/components/withings/translations/cs.json @@ -23,9 +23,6 @@ "description": "Zadejte jedine\u010dn\u00e9 jm\u00e9no profilu pro tato data. Obvykle se jedn\u00e1 o jm\u00e9no profilu, kter\u00e9 jste vybrali v p\u0159edchoz\u00edm kroku.", "title": "U\u017eivatelsk\u00fd profil." }, - "reauth": { - "title": "Znovu ov\u011b\u0159it integraci" - }, "reauth_confirm": { "title": "Znovu ov\u011b\u0159it integraci" } diff --git a/homeassistant/components/withings/translations/de.json b/homeassistant/components/withings/translations/de.json index 672ced9ca5c..8b1bc3041e1 100644 --- a/homeassistant/components/withings/translations/de.json +++ b/homeassistant/components/withings/translations/de.json @@ -24,10 +24,6 @@ "description": "Gib einen eindeutigen Profilnamen f\u00fcr diese Daten an. Normalerweise ist dies der Name des Profils, das du im vorherigen Schritt ausgew\u00e4hlt hast.", "title": "Benutzerprofil" }, - "reauth": { - "description": "Das Profil \"{profile}\" muss neu authentifiziert werden, um weiterhin Withings-Daten zu empfangen.", - "title": "Integration erneut authentifizieren" - }, "reauth_confirm": { "description": "Das Profil \"{profile}\" muss neu authentifiziert werden, um weiterhin Withings-Daten zu empfangen.", "title": "Integration erneut authentifizieren" diff --git a/homeassistant/components/withings/translations/el.json b/homeassistant/components/withings/translations/el.json index 068d347467a..75f90e3d206 100644 --- a/homeassistant/components/withings/translations/el.json +++ b/homeassistant/components/withings/translations/el.json @@ -24,10 +24,6 @@ "description": "\u0394\u03ce\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03bc\u03bf\u03bd\u03b1\u03b4\u03b9\u03ba\u03cc \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c0\u03c1\u03bf\u03c6\u03af\u03bb \u03b3\u03b9\u03b1 \u03c4\u03b1 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 \u03b1\u03c5\u03c4\u03ac. \u03a3\u03c5\u03bd\u03ae\u03b8\u03c9\u03c2 \u03c0\u03c1\u03cc\u03ba\u03b5\u03b9\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03bf\u03c6\u03af\u03bb \u03c0\u03bf\u03c5 \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03b1\u03c4\u03b5 \u03c3\u03c4\u03bf \u03c0\u03c1\u03bf\u03b7\u03b3\u03bf\u03cd\u03bc\u03b5\u03bd\u03bf \u03b2\u03ae\u03bc\u03b1.", "title": "\u03a0\u03c1\u03bf\u03c6\u03af\u03bb \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7." }, - "reauth": { - "description": "\u03a4\u03bf \u03c0\u03c1\u03bf\u03c6\u03af\u03bb \"{profile}\" \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03b5\u03b9 \u03bd\u03b1 \u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03b9 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 Withings.", - "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" - }, "reauth_confirm": { "description": "\u03a4\u03bf \u03c0\u03c1\u03bf\u03c6\u03af\u03bb \"{profile}\" \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03b5\u03b9 \u03bd\u03b1 \u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03b9 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 Withings.", "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" diff --git a/homeassistant/components/withings/translations/en.json b/homeassistant/components/withings/translations/en.json index 490e60512f9..ca969626510 100644 --- a/homeassistant/components/withings/translations/en.json +++ b/homeassistant/components/withings/translations/en.json @@ -24,10 +24,6 @@ "description": "Provide a unique profile name for this data. Typically this is the name of the profile you selected in the previous step.", "title": "User Profile." }, - "reauth": { - "description": "The \"{profile}\" profile needs to be re-authenticated in order to continue receiving Withings data.", - "title": "Reauthenticate Integration" - }, "reauth_confirm": { "description": "The \"{profile}\" profile needs to be re-authenticated in order to continue receiving Withings data.", "title": "Reauthenticate Integration" diff --git a/homeassistant/components/withings/translations/es.json b/homeassistant/components/withings/translations/es.json index e3a101b1892..e49df5be40f 100644 --- a/homeassistant/components/withings/translations/es.json +++ b/homeassistant/components/withings/translations/es.json @@ -24,10 +24,6 @@ "description": "Proporciona un nombre de perfil \u00fanico para estos datos. Por lo general, este es el nombre del perfil que seleccionaste en el paso anterior.", "title": "Perfil de usuario." }, - "reauth": { - "description": "El perfil \"{profile}\" debe volver a autenticarse para continuar recibiendo datos de Withings.", - "title": "Volver a autenticar la integraci\u00f3n" - }, "reauth_confirm": { "description": "El perfil \"{profile}\" debe volver a autenticarse para continuar recibiendo datos de Withings.", "title": "Volver a autenticar la integraci\u00f3n" diff --git a/homeassistant/components/withings/translations/et.json b/homeassistant/components/withings/translations/et.json index a336b6b0bdf..b0dfa6157e1 100644 --- a/homeassistant/components/withings/translations/et.json +++ b/homeassistant/components/withings/translations/et.json @@ -24,10 +24,6 @@ "description": "Anna neile andmetele ainulaadne profiilinimi. Tavaliselt on see eelmises etapis valitud profiili nimi.", "title": "Kasutaja profiil." }, - "reauth": { - "description": "Withingi andmete jsaamiseks tuleb kasutaja {profile} taastuvastada.", - "title": "Taastuvasta sidumine" - }, "reauth_confirm": { "description": "Profiil \"{profile}\" tuleb uuesti tuvastada, et j\u00e4tkata Withingsi andmete saamist.", "title": "Taastuvasta sidumine" diff --git a/homeassistant/components/withings/translations/fr.json b/homeassistant/components/withings/translations/fr.json index e0d9e8db08d..18528a34330 100644 --- a/homeassistant/components/withings/translations/fr.json +++ b/homeassistant/components/withings/translations/fr.json @@ -24,10 +24,6 @@ "description": "Quel profil avez-vous s\u00e9lectionn\u00e9 sur le site Withings? Il est important que les profils correspondent, sinon les donn\u00e9es seront mal \u00e9tiquet\u00e9es.", "title": "Profil utilisateur" }, - "reauth": { - "description": "Le profile \u00ab\u00a0{profile}\u00a0\u00bb doit \u00eatre r\u00e9-authentifi\u00e9 afin de continuer \u00e0 recevoir les donn\u00e9es Withings.", - "title": "R\u00e9-authentifier l'int\u00e9gration" - }, "reauth_confirm": { "description": "Le profile \u00ab\u00a0{profile}\u00a0\u00bb doit \u00eatre r\u00e9-authentifi\u00e9 afin de continuer \u00e0 recevoir les donn\u00e9es Withings.", "title": "R\u00e9-authentifier l'int\u00e9gration" diff --git a/homeassistant/components/withings/translations/he.json b/homeassistant/components/withings/translations/he.json index 5624b795692..ea76011ee2d 100644 --- a/homeassistant/components/withings/translations/he.json +++ b/homeassistant/components/withings/translations/he.json @@ -13,9 +13,6 @@ "pick_implementation": { "title": "\u05d1\u05d7\u05e8 \u05e9\u05d9\u05d8\u05ea \u05d0\u05d9\u05de\u05d5\u05ea" }, - "reauth": { - "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1" - }, "reauth_confirm": { "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1" } diff --git a/homeassistant/components/withings/translations/hu.json b/homeassistant/components/withings/translations/hu.json index a157d5e3688..e965e742da5 100644 --- a/homeassistant/components/withings/translations/hu.json +++ b/homeassistant/components/withings/translations/hu.json @@ -24,10 +24,6 @@ "description": "K\u00e9rem, adjon meg egy egyedi profilnevet. Ez \u00e1ltal\u00e1ban az el\u0151z\u0151 l\u00e9p\u00e9sben kiv\u00e1lasztott profil neve.", "title": "Felhaszn\u00e1l\u00f3i profil." }, - "reauth": { - "description": "A \u201e{profile}\u201d profilt \u00fajra hiteles\u00edteni kell, hogy tov\u00e1bbra is fogadni tudja a Withings adatokat.", - "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" - }, "reauth_confirm": { "description": "A \u201e{profile}\u201d profilt \u00fajra kell hiteles\u00edteni, hogy tov\u00e1bbra is megkaphassa a Withings-adatokat.", "title": "Az integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" diff --git a/homeassistant/components/withings/translations/id.json b/homeassistant/components/withings/translations/id.json index 4b4490a617b..169a55d4cb2 100644 --- a/homeassistant/components/withings/translations/id.json +++ b/homeassistant/components/withings/translations/id.json @@ -24,10 +24,6 @@ "description": "Berikan nama profil unik untuk data ini. Umumnya namanya sama dengan nama profil yang Anda pilih di langkah sebelumnya.", "title": "Profil Pengguna." }, - "reauth": { - "description": "Profil \"{profile}\" perlu diautentikasi ulang untuk terus menerima data Withings.", - "title": "Autentikasi Ulang Integrasi" - }, "reauth_confirm": { "description": "Profil \"{profile}\" perlu diautentikasi ulang untuk terus menerima data Withings.", "title": "Autentikasi Ulang Integrasi" diff --git a/homeassistant/components/withings/translations/it.json b/homeassistant/components/withings/translations/it.json index 30836be5da5..0a1ba582fb8 100644 --- a/homeassistant/components/withings/translations/it.json +++ b/homeassistant/components/withings/translations/it.json @@ -24,10 +24,6 @@ "description": "Fornire un nome di profilo univoco per questi dati. Di solito questo \u00e8 il nome del profilo selezionato nella fase precedente.", "title": "Profilo utente." }, - "reauth": { - "description": "Il profilo \"{profile}\" deve essere autenticato nuovamente per continuare a ricevere i dati Withings.", - "title": "Autentica nuovamente l'integrazione" - }, "reauth_confirm": { "description": "Il profilo \"{profile}\" deve essere nuovamente autenticato per continuare a ricevere i dati di Withings.", "title": "Autentica nuovamente l'integrazione" diff --git a/homeassistant/components/withings/translations/ja.json b/homeassistant/components/withings/translations/ja.json index 3fdcffdb918..8353c64846d 100644 --- a/homeassistant/components/withings/translations/ja.json +++ b/homeassistant/components/withings/translations/ja.json @@ -24,10 +24,6 @@ "description": "\u3053\u306e\u30c7\u30fc\u30bf\u306b\u30e6\u30cb\u30fc\u30af(\u4e00\u610f)\u306a\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u540d\u3092\u6307\u5b9a\u3057\u307e\u3059\u3002\u901a\u5e38\u3001\u3053\u308c\u306f\u524d\u306e\u624b\u9806\u3067\u9078\u629e\u3057\u305f\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u306e\u540d\u524d\u3067\u3059\u3002", "title": "\u30e6\u30fc\u30b6\u30fc\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u3002" }, - "reauth": { - "description": "Withings data\u306e\u53d7\u4fe1\u3092\u7d99\u7d9a\u3059\u308b\u306b\u306f\u3001\"{profile}\" \u306e\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002", - "title": "\u7d71\u5408\u306e\u518d\u8a8d\u8a3c" - }, "reauth_confirm": { "description": "Withings data\u306e\u53d7\u4fe1\u3092\u7d99\u7d9a\u3059\u308b\u306b\u306f\u3001\"{profile}\" \u306e\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002", "title": "\u7d71\u5408\u306e\u518d\u8a8d\u8a3c" diff --git a/homeassistant/components/withings/translations/ko.json b/homeassistant/components/withings/translations/ko.json index 4823061de41..80eb0648e07 100644 --- a/homeassistant/components/withings/translations/ko.json +++ b/homeassistant/components/withings/translations/ko.json @@ -23,10 +23,6 @@ }, "description": "\uace0\uc720\ud55c \ud504\ub85c\ud544 \uc774\ub984\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694. \uc77c\ubc18\uc801\uc73c\ub85c \uc774\uc804 \ub2e8\uacc4\uc5d0\uc11c \uc120\ud0dd\ud55c \ud504\ub85c\ud544\uc758 \uc774\ub984\uc785\ub2c8\ub2e4.", "title": "\uc0ac\uc6a9\uc790 \ud504\ub85c\ud544." - }, - "reauth": { - "description": "Withings \ub370\uc774\ud130\ub97c \uacc4\uc18d \uc218\uc2e0\ud558\ub824\uba74 \"{profile}\" \ud504\ub85c\ud544\uc744 \ub2e4\uc2dc \uc778\uc99d\ud574\uc57c \ud569\ub2c8\ub2e4.", - "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" } } } diff --git a/homeassistant/components/withings/translations/lb.json b/homeassistant/components/withings/translations/lb.json index 7169517eb09..3ddf6c881ca 100644 --- a/homeassistant/components/withings/translations/lb.json +++ b/homeassistant/components/withings/translations/lb.json @@ -23,10 +23,6 @@ }, "description": "G\u00ebff een eenzegartegen Profil Numm un. Typescherweise ass dat den Numm vum Profil deens du am viirechte Schr\u00ebtt ausgewielt hues.", "title": "Benotzer Profil." - }, - "reauth": { - "description": "De Profil \"{profile}\" muss fr\u00ebsch authentifi\u00e9iert ginn fir weiderhinn Donn\u00e9e\u00eb vun Withing z'empf\u00e4nken.", - "title": "Integratioun re-authentifiz\u00e9ieren" } } } diff --git a/homeassistant/components/withings/translations/nl.json b/homeassistant/components/withings/translations/nl.json index 8e40180b75e..f7d9b73bc23 100644 --- a/homeassistant/components/withings/translations/nl.json +++ b/homeassistant/components/withings/translations/nl.json @@ -24,10 +24,6 @@ "description": "Geef een unieke profielnaam op voor deze gegevens. Meestal is dit de naam van het profiel dat u in de vorige stap hebt geselecteerd.", "title": "Gebruikersprofiel." }, - "reauth": { - "description": "Het {profile} \" moet opnieuw worden geverifieerd om Withings-gegevens te blijven ontvangen.", - "title": "Integratie herauthenticeren" - }, "reauth_confirm": { "title": "Integratie herauthenticeren" } diff --git a/homeassistant/components/withings/translations/no.json b/homeassistant/components/withings/translations/no.json index 488a43592a5..fcb8c5b8f53 100644 --- a/homeassistant/components/withings/translations/no.json +++ b/homeassistant/components/withings/translations/no.json @@ -24,10 +24,6 @@ "description": "Oppgi et unikt profilnavn for disse dataene. Dette er vanligvis navnet p\u00e5 profilen du valgte i forrige trinn.", "title": "Brukerprofil." }, - "reauth": { - "description": "Profilen {profile} m\u00e5 godkjennes p\u00e5 nytt for \u00e5 kunne fortsette \u00e5 motta Withings-data.", - "title": "Godkjenne integrering p\u00e5 nytt" - }, "reauth_confirm": { "description": "Profilen {profile} m\u00e5 godkjennes p\u00e5 nytt for \u00e5 kunne fortsette \u00e5 motta Withings-data.", "title": "Re-autentiser integrasjon" diff --git a/homeassistant/components/withings/translations/pl.json b/homeassistant/components/withings/translations/pl.json index 27544b29fa4..efdfdb701c0 100644 --- a/homeassistant/components/withings/translations/pl.json +++ b/homeassistant/components/withings/translations/pl.json @@ -24,10 +24,6 @@ "description": "Podaj unikaln\u0105 nazw\u0119 profilu. Zwykle jest to nazwa profilu wybranego w poprzednim kroku.", "title": "Profil u\u017cytkownika" }, - "reauth": { - "description": "Profil \"{profile}\" musi zosta\u0107 ponownie uwierzytelniony, aby nadal otrzymywa\u0107 dane Withings.", - "title": "Ponownie uwierzytelnij integracj\u0119" - }, "reauth_confirm": { "description": "Profil \"{profile}\" musi zosta\u0107 ponownie uwierzytelniony, aby nadal otrzymywa\u0107 dane Withings.", "title": "Ponownie uwierzytelnij integracj\u0119" diff --git a/homeassistant/components/withings/translations/pt-BR.json b/homeassistant/components/withings/translations/pt-BR.json index 4ea2fd4a92c..d80209af0fa 100644 --- a/homeassistant/components/withings/translations/pt-BR.json +++ b/homeassistant/components/withings/translations/pt-BR.json @@ -24,10 +24,6 @@ "description": "Forne\u00e7a um nome de perfil exclusivo para esses dados. Normalmente, esse \u00e9 o nome do perfil selecionado na etapa anterior.", "title": "Perfil de usu\u00e1rio." }, - "reauth": { - "description": "O perfil \"{profile}\" precisa ser autenticado novamente para continuar recebendo dados do Withings", - "title": "Reautenticar Integra\u00e7\u00e3o" - }, "reauth_confirm": { "description": "O perfil \"{profile}\" precisa ser autenticado novamente para continuar recebendo dados do Withings.", "title": "Reautenticar Integra\u00e7\u00e3o" diff --git a/homeassistant/components/withings/translations/pt.json b/homeassistant/components/withings/translations/pt.json index fa97013c5c0..204a4fd2b69 100644 --- a/homeassistant/components/withings/translations/pt.json +++ b/homeassistant/components/withings/translations/pt.json @@ -18,9 +18,6 @@ }, "description": "Fornecer um nome de perfil \u00fanico para estes dados. Normalmente, este \u00e9 o nome do perfil que seleccionou na etapa anterior." }, - "reauth": { - "title": "Re-autenticar Perfil" - }, "reauth_confirm": { "title": "Reautenticar integra\u00e7\u00e3o" } diff --git a/homeassistant/components/withings/translations/ru.json b/homeassistant/components/withings/translations/ru.json index 78f8f3ec5e4..121dbeebf18 100644 --- a/homeassistant/components/withings/translations/ru.json +++ b/homeassistant/components/withings/translations/ru.json @@ -24,10 +24,6 @@ "description": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0444\u0438\u043b\u044f \u0434\u043b\u044f \u044d\u0442\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u044d\u0442\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435, \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435 \u043d\u0430 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u0448\u0430\u0433\u0435.", "title": "Withings" }, - "reauth": { - "description": "\u041f\u0440\u043e\u0444\u0438\u043b\u044c \"{profile}\" \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 Withings.", - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" - }, "reauth_confirm": { "description": "\u041f\u0440\u043e\u0444\u0438\u043b\u044c \"{profile}\" \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 Withings.", "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" diff --git a/homeassistant/components/withings/translations/sv.json b/homeassistant/components/withings/translations/sv.json index 8bf931882f4..1e437167d00 100644 --- a/homeassistant/components/withings/translations/sv.json +++ b/homeassistant/components/withings/translations/sv.json @@ -24,10 +24,6 @@ "description": "Vilken profil valde du p\u00e5 Withings webbplats? Det \u00e4r viktigt att profilerna matchar, annars kommer data att vara felm\u00e4rkta.", "title": "Anv\u00e4ndarprofil." }, - "reauth": { - "description": "Profilen \" {profile} \" m\u00e5ste autentiseras p\u00e5 nytt f\u00f6r att kunna forts\u00e4tta att ta emot Withings-data.", - "title": "\u00c5terautenticera integration" - }, "reauth_confirm": { "description": "Profilen \" {profile} \" m\u00e5ste autentiseras p\u00e5 nytt f\u00f6r att kunna forts\u00e4tta att ta emot Withings-data.", "title": "G\u00f6r om autentiseringen f\u00f6r integrationen" diff --git a/homeassistant/components/withings/translations/tr.json b/homeassistant/components/withings/translations/tr.json index 698fe41988c..11fc361c33a 100644 --- a/homeassistant/components/withings/translations/tr.json +++ b/homeassistant/components/withings/translations/tr.json @@ -24,10 +24,6 @@ "description": "Bu veriler i\u00e7in benzersiz bir profil ad\u0131 sa\u011flay\u0131n. Genellikle bu, \u00f6nceki ad\u0131mda se\u00e7ti\u011finiz profilin ad\u0131d\u0131r.", "title": "Kullan\u0131c\u0131 profili." }, - "reauth": { - "description": "Withings verilerini almaya devam etmek i\u00e7in \" {profile}", - "title": "Entegrasyonu Yeniden Do\u011frula" - }, "reauth_confirm": { "description": "Withings verilerini almaya devam etmek i\u00e7in \" {profile} \" profilinin yeniden do\u011frulanmas\u0131 gerekiyor.", "title": "Entegrasyonu Yeniden Do\u011frula" diff --git a/homeassistant/components/withings/translations/uk.json b/homeassistant/components/withings/translations/uk.json index 5efc27042b1..3ea7883fbcd 100644 --- a/homeassistant/components/withings/translations/uk.json +++ b/homeassistant/components/withings/translations/uk.json @@ -23,10 +23,6 @@ }, "description": "\u0412\u043a\u0430\u0436\u0456\u0442\u044c \u0443\u043d\u0456\u043a\u0430\u043b\u044c\u043d\u0435 \u0456\u043c'\u044f \u043f\u0440\u043e\u0444\u0456\u043b\u044e \u0434\u043b\u044f \u0446\u0438\u0445 \u0434\u0430\u043d\u0438\u0445. \u042f\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0446\u0435 \u043d\u0430\u0437\u0432\u0430, \u043e\u0431\u0440\u0430\u043d\u0430 \u043d\u0430 \u043f\u043e\u043f\u0435\u0440\u0435\u0434\u043d\u044c\u043e\u043c\u0443 \u043a\u0440\u043e\u0446\u0456.", "title": "Withings" - }, - "reauth": { - "description": "\u041f\u0440\u043e\u0444\u0456\u043b\u044c \"{profile}\" \u043f\u043e\u0432\u0438\u043d\u0435\u043d \u0431\u0443\u0442\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u043e\u0432\u0430\u043d\u0438\u0439 \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u043e\u0432\u0436\u0435\u043d\u043d\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0434\u0430\u043d\u0438\u0445 Withings.", - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0432\u0430\u0442\u0438 \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u044e" } } } diff --git a/homeassistant/components/withings/translations/zh-Hant.json b/homeassistant/components/withings/translations/zh-Hant.json index 35328ea9353..b5e8b16029d 100644 --- a/homeassistant/components/withings/translations/zh-Hant.json +++ b/homeassistant/components/withings/translations/zh-Hant.json @@ -24,10 +24,6 @@ "description": "\u8acb\u70ba\u8cc7\u6599\u8a2d\u5b9a\u4e00\u7d44\u7368\u4e00\u7684\u500b\u4eba\u8a2d\u7f6e\u540d\u7a31\u3002\u901a\u5e38\u8207\u524d\u4e00\u6b65\u9a5f\u6240\u9078\u64c7\u4e4b\u8a2d\u7f6e\u6587\u4ef6\u540d\u7a31\u76f8\u540c\u3002", "title": "\u500b\u4eba\u8a2d\u5b9a\u3002" }, - "reauth": { - "description": "\"{profile}\" \u8a2d\u5b9a\u6a94\u9700\u8981\u91cd\u65b0\u8a8d\u8b49\u4ee5\u4fdd\u6301\u63a5\u6536 Withings \u8cc7\u6599\u3002", - "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" - }, "reauth_confirm": { "description": "\"{profile}\" \u8a2d\u5b9a\u6a94\u9700\u8981\u91cd\u65b0\u8a8d\u8b49\u4ee5\u4fdd\u6301\u63a5\u6536 Withings \u8cc7\u6599\u3002", "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" diff --git a/homeassistant/components/ws66i/translations/bg.json b/homeassistant/components/ws66i/translations/bg.json index ba078ae5f11..a354fcc347b 100644 --- a/homeassistant/components/ws66i/translations/bg.json +++ b/homeassistant/components/ws66i/translations/bg.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" - }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" diff --git a/homeassistant/components/ws66i/translations/ca.json b/homeassistant/components/ws66i/translations/ca.json index 789edb86fb2..f293839739b 100644 --- a/homeassistant/components/ws66i/translations/ca.json +++ b/homeassistant/components/ws66i/translations/ca.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "El dispositiu ja est\u00e0 configurat" - }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3", "unknown": "Error inesperat" diff --git a/homeassistant/components/ws66i/translations/cs.json b/homeassistant/components/ws66i/translations/cs.json index 04f18366eaf..b5a9fe10981 100644 --- a/homeassistant/components/ws66i/translations/cs.json +++ b/homeassistant/components/ws66i/translations/cs.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno" - }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" diff --git a/homeassistant/components/ws66i/translations/de.json b/homeassistant/components/ws66i/translations/de.json index cab3e062d8e..8a3c2c63cb9 100644 --- a/homeassistant/components/ws66i/translations/de.json +++ b/homeassistant/components/ws66i/translations/de.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Ger\u00e4t ist bereits konfiguriert" - }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", "unknown": "Unerwarteter Fehler" diff --git a/homeassistant/components/ws66i/translations/el.json b/homeassistant/components/ws66i/translations/el.json index 4a1365c3a77..b919c07fb53 100644 --- a/homeassistant/components/ws66i/translations/el.json +++ b/homeassistant/components/ws66i/translations/el.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" - }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" diff --git a/homeassistant/components/ws66i/translations/en.json b/homeassistant/components/ws66i/translations/en.json index 30ef1e4205a..fd4b170b378 100644 --- a/homeassistant/components/ws66i/translations/en.json +++ b/homeassistant/components/ws66i/translations/en.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Device is already configured" - }, "error": { "cannot_connect": "Failed to connect", "unknown": "Unexpected error" diff --git a/homeassistant/components/ws66i/translations/es.json b/homeassistant/components/ws66i/translations/es.json index 075dd41ed56..59ee53b4499 100644 --- a/homeassistant/components/ws66i/translations/es.json +++ b/homeassistant/components/ws66i/translations/es.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "El dispositivo ya est\u00e1 configurado" - }, "error": { "cannot_connect": "No se pudo conectar", "unknown": "Error inesperado" diff --git a/homeassistant/components/ws66i/translations/et.json b/homeassistant/components/ws66i/translations/et.json index 83b238d74f5..b7eef141129 100644 --- a/homeassistant/components/ws66i/translations/et.json +++ b/homeassistant/components/ws66i/translations/et.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Seade on juba h\u00e4\u00e4lestatud" - }, "error": { "cannot_connect": "\u00dchendamine nurjus", "unknown": "Ootamatu t\u00f5rge" diff --git a/homeassistant/components/ws66i/translations/fr.json b/homeassistant/components/ws66i/translations/fr.json index d4d3f6e7350..173f05fedbd 100644 --- a/homeassistant/components/ws66i/translations/fr.json +++ b/homeassistant/components/ws66i/translations/fr.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" - }, "error": { "cannot_connect": "\u00c9chec de connexion", "unknown": "Erreur inattendue" diff --git a/homeassistant/components/ws66i/translations/he.json b/homeassistant/components/ws66i/translations/he.json index fa770b28bf4..b426f27e591 100644 --- a/homeassistant/components/ws66i/translations/he.json +++ b/homeassistant/components/ws66i/translations/he.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" - }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" diff --git a/homeassistant/components/ws66i/translations/hu.json b/homeassistant/components/ws66i/translations/hu.json index 9d6585e8f6b..34d1ef39880 100644 --- a/homeassistant/components/ws66i/translations/hu.json +++ b/homeassistant/components/ws66i/translations/hu.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" - }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" diff --git a/homeassistant/components/ws66i/translations/id.json b/homeassistant/components/ws66i/translations/id.json index 54cb3043a11..095e17b6750 100644 --- a/homeassistant/components/ws66i/translations/id.json +++ b/homeassistant/components/ws66i/translations/id.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Perangkat sudah dikonfigurasi" - }, "error": { "cannot_connect": "Gagal terhubung", "unknown": "Kesalahan yang tidak diharapkan" diff --git a/homeassistant/components/ws66i/translations/it.json b/homeassistant/components/ws66i/translations/it.json index c98714c98ad..8aab3117ca8 100644 --- a/homeassistant/components/ws66i/translations/it.json +++ b/homeassistant/components/ws66i/translations/it.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" - }, "error": { "cannot_connect": "Impossibile connettersi", "unknown": "Errore imprevisto" diff --git a/homeassistant/components/ws66i/translations/ja.json b/homeassistant/components/ws66i/translations/ja.json index 2ae21b3916c..46d7c262c3b 100644 --- a/homeassistant/components/ws66i/translations/ja.json +++ b/homeassistant/components/ws66i/translations/ja.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" - }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" diff --git a/homeassistant/components/ws66i/translations/ko.json b/homeassistant/components/ws66i/translations/ko.json index ba31d74c21a..33191483bdd 100644 --- a/homeassistant/components/ws66i/translations/ko.json +++ b/homeassistant/components/ws66i/translations/ko.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" - }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" diff --git a/homeassistant/components/ws66i/translations/nl.json b/homeassistant/components/ws66i/translations/nl.json index 0ff24636b88..5e31bbd4e76 100644 --- a/homeassistant/components/ws66i/translations/nl.json +++ b/homeassistant/components/ws66i/translations/nl.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Apparaat is al geconfigureerd" - }, "error": { "cannot_connect": "Kan geen verbinding maken", "unknown": "Onverwachte fout" diff --git a/homeassistant/components/ws66i/translations/no.json b/homeassistant/components/ws66i/translations/no.json index fa132ab681a..ff5e34742a5 100644 --- a/homeassistant/components/ws66i/translations/no.json +++ b/homeassistant/components/ws66i/translations/no.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Enheten er allerede konfigurert" - }, "error": { "cannot_connect": "Tilkobling mislyktes", "unknown": "Uventet feil" diff --git a/homeassistant/components/ws66i/translations/pl.json b/homeassistant/components/ws66i/translations/pl.json index 1fc8c8b1cc9..e4ca696f55b 100644 --- a/homeassistant/components/ws66i/translations/pl.json +++ b/homeassistant/components/ws66i/translations/pl.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" - }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "unknown": "Nieoczekiwany b\u0142\u0105d" diff --git a/homeassistant/components/ws66i/translations/pt-BR.json b/homeassistant/components/ws66i/translations/pt-BR.json index 68bc805d08c..e50b556dbdc 100644 --- a/homeassistant/components/ws66i/translations/pt-BR.json +++ b/homeassistant/components/ws66i/translations/pt-BR.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" - }, "error": { "cannot_connect": "Falha ao conectar", "unknown": "Erro inesperado" diff --git a/homeassistant/components/ws66i/translations/ru.json b/homeassistant/components/ws66i/translations/ru.json index b7e244cf2b0..aa9a8125f2b 100644 --- a/homeassistant/components/ws66i/translations/ru.json +++ b/homeassistant/components/ws66i/translations/ru.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." - }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." diff --git a/homeassistant/components/ws66i/translations/sk.json b/homeassistant/components/ws66i/translations/sk.json index 70e5f25b908..5184388d901 100644 --- a/homeassistant/components/ws66i/translations/sk.json +++ b/homeassistant/components/ws66i/translations/sk.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" - }, "step": { "user": { "title": "Pripojte sa k zariadeniu" diff --git a/homeassistant/components/ws66i/translations/sv.json b/homeassistant/components/ws66i/translations/sv.json index 2ed5262ea3d..1bd654e40a4 100644 --- a/homeassistant/components/ws66i/translations/sv.json +++ b/homeassistant/components/ws66i/translations/sv.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Enheten \u00e4r redan konfigurerad" - }, "error": { "cannot_connect": "Det gick inte att ansluta.", "unknown": "Ov\u00e4ntat fel" diff --git a/homeassistant/components/ws66i/translations/tr.json b/homeassistant/components/ws66i/translations/tr.json index 5baea0cee9d..4fec56f89b8 100644 --- a/homeassistant/components/ws66i/translations/tr.json +++ b/homeassistant/components/ws66i/translations/tr.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" - }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", "unknown": "Beklenmeyen hata" diff --git a/homeassistant/components/ws66i/translations/zh-Hant.json b/homeassistant/components/ws66i/translations/zh-Hant.json index a583ac2217f..780927c7a66 100644 --- a/homeassistant/components/ws66i/translations/zh-Hant.json +++ b/homeassistant/components/ws66i/translations/zh-Hant.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" - }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" diff --git a/homeassistant/components/xiaomi_ble/translations/ca.json b/homeassistant/components/xiaomi_ble/translations/ca.json index d36daedf3a7..019d50b34ae 100644 --- a/homeassistant/components/xiaomi_ble/translations/ca.json +++ b/homeassistant/components/xiaomi_ble/translations/ca.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "El dispositiu ja est\u00e0 configurat", "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", - "decryption_failed": "La clau d'enlla\u00e7 proporcionada no ha funcionat, les dades del sensor no s'han pogut desxifrar. Comprova-la i torna-ho a provar.", - "expected_24_characters": "S'espera una clau d'enlla\u00e7 de 24 car\u00e0cters hexadecimals.", - "expected_32_characters": "S'espera una clau d'enlla\u00e7 de 32 car\u00e0cters hexadecimals.", "no_devices_found": "No s'han trobat dispositius a la xarxa", "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" }, @@ -34,9 +31,6 @@ }, "description": "Les dades del sensor emeses estan xifrades. Per desxifrar-les necessites una clau d'enlla\u00e7 de 24 car\u00e0cters hexadecimals." }, - "slow_confirm": { - "description": "No s'ha em\u00e8s cap 'broadcast' des d'aquest dispositiu durant l'\u00faltim minut, per tant no estem segurs de si aquest dispositiu utilitza encriptaci\u00f3 o no. Aix\u00f2 pot ser perqu\u00e8 el dispositiu utilitza un interval de 'broadcast' lent. Confirma per afegir aquest dispositiu de totes maneres, i la pr\u00f2xima vegada que rebi un 'broadcast' se't demanar\u00e0 que introdueixis la seva clau d'enlla\u00e7 si \u00e9s necessari." - }, "user": { "data": { "address": "Dispositiu" diff --git a/homeassistant/components/xiaomi_ble/translations/de.json b/homeassistant/components/xiaomi_ble/translations/de.json index 448a160c3ab..07cd3312a1c 100644 --- a/homeassistant/components/xiaomi_ble/translations/de.json +++ b/homeassistant/components/xiaomi_ble/translations/de.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", - "decryption_failed": "Der bereitgestellte Bindkey funktionierte nicht, Sensordaten konnten nicht entschl\u00fcsselt werden. Bitte \u00fcberpr\u00fcfe es und versuche es erneut.", - "expected_24_characters": "Erwartet wird ein 24-stelliger hexadezimaler Bindkey.", - "expected_32_characters": "Erwartet wird ein 32-stelliger hexadezimaler Bindkey.", "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, @@ -34,9 +31,6 @@ }, "description": "Die vom Sensor \u00fcbertragenen Sensordaten sind verschl\u00fcsselt. Um sie zu entschl\u00fcsseln, ben\u00f6tigen wir einen 24-stelligen hexadezimalen Bindkey." }, - "slow_confirm": { - "description": "Von diesem Ger\u00e4t wurde in der letzten Minute kein Broadcast gesendet, so dass wir nicht sicher sind, ob dieses Ger\u00e4t Verschl\u00fcsselung verwendet oder nicht. Dies kann daran liegen, dass das Ger\u00e4t ein langsames Sendeintervall verwendet. Best\u00e4tige, dass du das Ger\u00e4t trotzdem hinzuf\u00fcgen m\u00f6chtest. Wenn das n\u00e4chste Mal ein Broadcast empfangen wird, wirst du aufgefordert, den Bindkey einzugeben, falls er ben\u00f6tigt wird." - }, "user": { "data": { "address": "Ger\u00e4t" diff --git a/homeassistant/components/xiaomi_ble/translations/el.json b/homeassistant/components/xiaomi_ble/translations/el.json index e6c5efce91f..5876ca38651 100644 --- a/homeassistant/components/xiaomi_ble/translations/el.json +++ b/homeassistant/components/xiaomi_ble/translations/el.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", - "decryption_failed": "\u03a4\u03bf \u03c0\u03b1\u03c1\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03b4\u03b5\u03bd \u03bb\u03b5\u03b9\u03c4\u03bf\u03cd\u03c1\u03b3\u03b7\u03c3\u03b5, \u03c4\u03b1 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03bf\u03cd\u03c3\u03b1\u03bd \u03bd\u03b1 \u03b1\u03c0\u03bf\u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03b1\u03c6\u03b7\u03b8\u03bf\u03cd\u03bd. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b1\u03b9 \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac.", - "expected_24_characters": "\u0391\u03bd\u03b1\u03bc\u03b5\u03bd\u03cc\u03c4\u03b1\u03bd \u03b4\u03b5\u03ba\u03b1\u03b5\u03be\u03b1\u03b4\u03b9\u03ba\u03cc \u03b4\u03b5\u03c3\u03bc\u03b5\u03c5\u03c4\u03b9\u03ba\u03cc \u03ba\u03bb\u03b5\u03b9\u03b4\u03af 24 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03c9\u03bd.", - "expected_32_characters": "\u0391\u03bd\u03b1\u03bc\u03b5\u03bd\u03cc\u03c4\u03b1\u03bd \u03b4\u03b5\u03ba\u03b1\u03b5\u03be\u03b1\u03b4\u03b9\u03ba\u03cc \u03b4\u03b5\u03c3\u03bc\u03b5\u03c5\u03c4\u03b9\u03ba\u03cc \u03ba\u03bb\u03b5\u03b9\u03b4\u03af 32 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03c9\u03bd.", "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf", "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" }, @@ -34,9 +31,6 @@ }, "description": "\u03a4\u03b1 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03c0\u03bf\u03c5 \u03bc\u03b5\u03c4\u03b1\u03b4\u03af\u03b4\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03b1\u03c6\u03b7\u03bc\u03ad\u03bd\u03b1. \u0393\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03ac\u03c6\u03b7\u03c3\u03ae \u03c4\u03bf\u03c5\u03c2 \u03c7\u03c1\u03b5\u03b9\u03b1\u03b6\u03cc\u03bc\u03b1\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03b4\u03ad\u03c3\u03bc\u03b5\u03c5\u03c3\u03b7\u03c2 24 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b5\u03be\u03b1\u03b4\u03b9\u03ba\u03bf\u03cd \u03b1\u03c1\u03b9\u03b8\u03bc\u03bf\u03cd." }, - "slow_confirm": { - "description": "\u0394\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03b3\u03af\u03bd\u03b5\u03b9 \u03bc\u03b5\u03c4\u03ac\u03b4\u03bf\u03c3\u03b7 \u03b1\u03c0\u03cc \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c4\u03b7\u03bd \u03c4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03b1 \u03c3\u03c4\u03b9\u03b3\u03bc\u03ae, \u03b5\u03c0\u03bf\u03bc\u03ad\u03bd\u03c9\u03c2 \u03b4\u03b5\u03bd \u03b5\u03af\u03bc\u03b1\u03c3\u03c4\u03b5 \u03c3\u03af\u03b3\u03bf\u03c5\u03c1\u03bf\u03b9 \u03b1\u03bd \u03b1\u03c5\u03c4\u03ae \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03ac\u03c6\u03b7\u03c3\u03b7 \u03ae \u03cc\u03c7\u03b9. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03bf\u03c6\u03b5\u03af\u03bb\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf \u03cc\u03c4\u03b9 \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03ad\u03bd\u03b1 \u03b1\u03c1\u03b3\u03cc \u03b4\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1 \u03bc\u03b5\u03c4\u03ac\u03b4\u03bf\u03c3\u03b7\u03c2. \u0395\u03c0\u03b9\u03b2\u03b5\u03b2\u03b1\u03b9\u03ce\u03c3\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03bf\u03cd\u03c4\u03c9\u03c2 \u03ae \u03ac\u03bb\u03bb\u03c9\u03c2, \u03c3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b5\u03c0\u03cc\u03bc\u03b5\u03bd\u03b7 \u03c6\u03bf\u03c1\u03ac \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03bb\u03b7\u03c6\u03b8\u03b5\u03af \u03bc\u03b9\u03b1 \u03bc\u03b5\u03c4\u03ac\u03b4\u03bf\u03c3\u03b7 \u03b8\u03b1 \u03c3\u03b1\u03c2 \u03b6\u03b7\u03c4\u03b7\u03b8\u03b5\u03af \u03bd\u03b1 \u03b5\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03b4\u03b5\u03c3\u03bc\u03b5\u03c5\u03c4\u03b9\u03ba\u03cc \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c4\u03b7\u03c2, \u03b5\u03ac\u03bd \u03c7\u03c1\u03b5\u03b9\u03ac\u03b6\u03b5\u03c4\u03b1\u03b9." - }, "user": { "data": { "address": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" diff --git a/homeassistant/components/xiaomi_ble/translations/en.json b/homeassistant/components/xiaomi_ble/translations/en.json index be75cc007b2..2cb77dd2c07 100644 --- a/homeassistant/components/xiaomi_ble/translations/en.json +++ b/homeassistant/components/xiaomi_ble/translations/en.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Device is already configured", "already_in_progress": "Configuration flow is already in progress", - "decryption_failed": "The provided bindkey did not work, sensor data could not be decrypted. Please check it and try again.", - "expected_24_characters": "Expected a 24 character hexadecimal bindkey.", - "expected_32_characters": "Expected a 32 character hexadecimal bindkey.", "no_devices_found": "No devices found on the network", "reauth_successful": "Re-authentication was successful" }, @@ -34,9 +31,6 @@ }, "description": "The sensor data broadcast by the sensor is encrypted. In order to decrypt it we need a 24 character hexadecimal bindkey." }, - "slow_confirm": { - "description": "There hasn't been a broadcast from this device in the last minute so we aren't sure if this device uses encryption or not. This may be because the device uses a slow broadcast interval. Confirm to add this device anyway, then the next time a broadcast is received you will be prompted to enter its bindkey if it's needed." - }, "user": { "data": { "address": "Device" diff --git a/homeassistant/components/xiaomi_ble/translations/es.json b/homeassistant/components/xiaomi_ble/translations/es.json index cf441adbefa..a1b796cd665 100644 --- a/homeassistant/components/xiaomi_ble/translations/es.json +++ b/homeassistant/components/xiaomi_ble/translations/es.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "El dispositivo ya est\u00e1 configurado", "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en curso", - "decryption_failed": "La clave de enlace proporcionada no funcion\u00f3, los datos del sensor no se pudieron descifrar. Por favor, compru\u00e9balo e int\u00e9ntalo de nuevo.", - "expected_24_characters": "Se esperaba una clave de enlace hexadecimal de 24 caracteres.", - "expected_32_characters": "Se esperaba una clave de enlace hexadecimal de 32 caracteres.", "no_devices_found": "No se encontraron dispositivos en la red", "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente" }, @@ -34,9 +31,6 @@ }, "description": "Los datos del sensor transmitidos por el sensor est\u00e1n cifrados. Para descifrarlos necesitamos una clave de enlace hexadecimal de 24 caracteres." }, - "slow_confirm": { - "description": "No ha habido una transmisi\u00f3n desde este dispositivo en el \u00faltimo minuto, por lo que no estamos seguros de si este dispositivo usa cifrado o no. Esto puede deberse a que el dispositivo utiliza un intervalo de transmisi\u00f3n lento. Confirma para agregar este dispositivo de todos modos, luego, la pr\u00f3xima vez que se reciba una transmisi\u00f3n, se te pedir\u00e1 que ingreses su clave de enlace si es necesario." - }, "user": { "data": { "address": "Dispositivo" diff --git a/homeassistant/components/xiaomi_ble/translations/et.json b/homeassistant/components/xiaomi_ble/translations/et.json index 41bf99207e2..49c7031a3a4 100644 --- a/homeassistant/components/xiaomi_ble/translations/et.json +++ b/homeassistant/components/xiaomi_ble/translations/et.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Seade on juba h\u00e4\u00e4lestatud", "already_in_progress": "Seadistamine on juba k\u00e4imas", - "decryption_failed": "Esitatud sidumisv\u00f5ti ei t\u00f6\u00f6tanud, sensori andmeid ei saanud dekr\u00fcpteerida. Palun kontrolli seda ja proovi uuesti.", - "expected_24_characters": "Eeldati 24-m\u00e4rgilist kuueteistk\u00fcmnends\u00fcsteemi sidumisv\u00f5tit.", - "expected_32_characters": "Eeldati 32-m\u00e4rgilist kuueteistk\u00fcmnends\u00fcsteemi sidumisv\u00f5tit.", "no_devices_found": "V\u00f6rgust seadmeid ei leitud", "reauth_successful": "Taastuvastamine \u00f5nnestus" }, @@ -34,9 +31,6 @@ }, "description": "Anduri edastatavad andmed on kr\u00fcpteeritud. Selle dekr\u00fcpteerimiseks vajame 24-m\u00e4rgilist kuueteistk\u00fcmnends\u00fcsteemi sidumisv\u00f5tit." }, - "slow_confirm": { - "description": "Sellest seadmest ei ole viimasel minutil \u00fchtegi saadet olnud, nii et me ei ole kindlad, kas see seade kasutab kr\u00fcpteerimist v\u00f5i mitte. See v\u00f5ib olla tingitud sellest, et seade kasutab aeglast saateintervalli. Kinnita, et lisate selle seadme ikkagi, siis j\u00e4rgmisel korral, kui saade saabub, palutakse sisestada selle sidumisv\u00f5ti, kui seda on vaja." - }, "user": { "data": { "address": "Seade" diff --git a/homeassistant/components/xiaomi_ble/translations/hu.json b/homeassistant/components/xiaomi_ble/translations/hu.json index fed82381dcb..95adee995dc 100644 --- a/homeassistant/components/xiaomi_ble/translations/hu.json +++ b/homeassistant/components/xiaomi_ble/translations/hu.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", - "decryption_failed": "A megadott kulcs nem m\u0171k\u00f6d\u00f6tt, az \u00e9rz\u00e9kel\u0151adatokat nem lehetett kiolvasni. K\u00e9rj\u00fck, ellen\u0151rizze \u00e9s pr\u00f3b\u00e1lja meg \u00fajra.", - "expected_24_characters": "24 karakterb\u0151l \u00e1ll\u00f3 hexadecim\u00e1lis kulcsra van sz\u00fcks\u00e9g.", - "expected_32_characters": "32 karakterb\u0151l \u00e1ll\u00f3 hexadecim\u00e1lis kulcsra van sz\u00fcks\u00e9g.", "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." }, @@ -34,9 +31,6 @@ }, "description": "Az \u00e9rz\u00e9kel\u0151 adatai titkos\u00edtva vannak. A visszafejt\u00e9shez egy 24 karakterb\u0151l \u00e1ll\u00f3 hexadecim\u00e1lis kulcsra van sz\u00fcks\u00e9g." }, - "slow_confirm": { - "description": "Az elm\u00falt egy percben nem \u00e9rkezett ad\u00e1sjel az eszk\u00f6zt\u0151l, \u00edgy nem az nem \u00e1llap\u00edthat\u00f3 meg egy\u00e9rtelm\u0171en, hogy ez a k\u00e9sz\u00fcl\u00e9k haszn\u00e1l-e titkos\u00edt\u00e1st vagy sem. Ez az\u00e9rt lehet, mert az eszk\u00f6z ritka jelad\u00e1si intervallumot haszn\u00e1l. Meger\u0151s\u00edtheti most az eszk\u00f6z hozz\u00e1ad\u00e1s\u00e1t, de a k\u00f6vetkez\u0151 ad\u00e1sjel fogad\u00e1sakor a rendszer k\u00e9rni fogja, hogy adja meg az eszk\u00f6z kulcs\u00e1t (bindkeyt), ha az sz\u00fcks\u00e9ges." - }, "user": { "data": { "address": "Eszk\u00f6z" diff --git a/homeassistant/components/xiaomi_ble/translations/id.json b/homeassistant/components/xiaomi_ble/translations/id.json index e6e29966bc7..13fbccc1333 100644 --- a/homeassistant/components/xiaomi_ble/translations/id.json +++ b/homeassistant/components/xiaomi_ble/translations/id.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Perangkat sudah dikonfigurasi", "already_in_progress": "Alur konfigurasi sedang berlangsung", - "decryption_failed": "Bindkey yang disediakan tidak berfungsi, data sensor tidak dapat didekripsi. Silakan periksa dan coba lagi.", - "expected_24_characters": "Diharapkan bindkey berupa 24 karakter heksadesimal.", - "expected_32_characters": "Diharapkan bindkey berupa 32 karakter heksadesimal.", "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan", "reauth_successful": "Autentikasi ulang berhasil" }, @@ -34,9 +31,6 @@ }, "description": "Data sensor yang disiarkan oleh sensor telah dienkripsi. Untuk mendekripsinya, diperlukan 24 karakter bindkey heksadesimal ." }, - "slow_confirm": { - "description": "Belum ada siaran dari perangkat ini dalam menit terakhir jadi kami tidak yakin apakah perangkat ini menggunakan enkripsi atau tidak. Ini mungkin terjadi karena perangkat menggunakan interval siaran yang lambat. Konfirmasikan sekarang untuk menambahkan perangkat ini, dan ketika siaran diterima nanti, Anda akan diminta untuk memasukkan kunci bind jika diperlukan." - }, "user": { "data": { "address": "Perangkat" diff --git a/homeassistant/components/xiaomi_ble/translations/it.json b/homeassistant/components/xiaomi_ble/translations/it.json index bf5ee87b949..7d6837d35ee 100644 --- a/homeassistant/components/xiaomi_ble/translations/it.json +++ b/homeassistant/components/xiaomi_ble/translations/it.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", - "decryption_failed": "La chiave di collegamento fornita non funziona, i dati del sensore non possono essere decifrati. Controlla e riprova.", - "expected_24_characters": "Prevista una chiave di collegamento esadecimale di 24 caratteri.", - "expected_32_characters": "Prevista una chiave di collegamento esadecimale di 32 caratteri.", "no_devices_found": "Nessun dispositivo trovato sulla rete", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" }, @@ -34,9 +31,6 @@ }, "description": "I dati trasmessi dal sensore sono criptati. Per decifrarli \u00e8 necessaria una chiave di collegamento esadecimale di 24 caratteri." }, - "slow_confirm": { - "description": "Non c'\u00e8 stata una trasmissione da questo dispositivo nell'ultimo minuto, quindi non siamo sicuri se questo dispositivo utilizzi la crittografia o meno. Ci\u00f2 potrebbe essere dovuto al fatto che il dispositivo utilizza un intervallo di trasmissione lento. Conferma per aggiungere comunque questo dispositivo, la prossima volta che viene ricevuta una trasmissione ti verr\u00e0 chiesto di inserire la sua chiave di collegamento se necessario." - }, "user": { "data": { "address": "Dispositivo" diff --git a/homeassistant/components/xiaomi_ble/translations/ja.json b/homeassistant/components/xiaomi_ble/translations/ja.json index 3d84421b1b3..8352df42c0c 100644 --- a/homeassistant/components/xiaomi_ble/translations/ja.json +++ b/homeassistant/components/xiaomi_ble/translations/ja.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", - "decryption_failed": "\u63d0\u4f9b\u3055\u308c\u305f\u30d0\u30a4\u30f3\u30c9\u30ad\u30fc\u304c\u6a5f\u80fd\u305b\u305a\u3001\u30bb\u30f3\u30b5\u30fc \u30c7\u30fc\u30bf\u3092\u5fa9\u53f7\u5316\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u78ba\u8a8d\u306e\u4e0a\u3001\u3082\u3046\u4e00\u5ea6\u8a66\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "expected_24_characters": "24\u6587\u5b57\u306716\u9032\u6570\u306a\u30d0\u30a4\u30f3\u30c9\u30ad\u30fc\u304c\u5fc5\u8981\u3067\u3059\u3002", - "expected_32_characters": "32\u6587\u5b57\u304b\u3089\u306a\u308b16\u9032\u6570\u306e\u30d0\u30a4\u30f3\u30c9\u30ad\u30fc\u304c\u5fc5\u8981\u3067\u3059\u3002", "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093", "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" }, @@ -34,9 +31,6 @@ }, "description": "\u30bb\u30f3\u30b5\u30fc\u304b\u3089\u30d6\u30ed\u30fc\u30c9\u30ad\u30e3\u30b9\u30c8\u3055\u308c\u308b\u30bb\u30f3\u30b5\u30fc\u30c7\u30fc\u30bf\u306f\u6697\u53f7\u5316\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5fa9\u53f7\u5316\u3059\u308b\u306b\u306f\u300116\u9032\u6570\u306724\u6587\u5b57\u306a\u30d0\u30a4\u30f3\u30c9\u30ad\u30fc\u304c\u5fc5\u8981\u3067\u3059\u3002" }, - "slow_confirm": { - "description": "\u76f4\u524d\u306b\u3053\u306e\u30c7\u30d0\u30a4\u30b9\u304b\u3089\u306e\u30d6\u30ed\u30fc\u30c9\u30ad\u30e3\u30b9\u30c8\u304c\u306a\u304b\u3063\u305f\u305f\u3081\u3001\u3053\u306e\u30c7\u30d0\u30a4\u30b9\u304c\u6697\u53f7\u5316\u3092\u4f7f\u7528\u3057\u3066\u3044\u308b\u304b\u3069\u3046\u304b\u306f\u308f\u304b\u308a\u307e\u305b\u3093\u3002\u3053\u308c\u306f\u3001\u30c7\u30d0\u30a4\u30b9\u304c\u9045\u3044\u30d6\u30ed\u30fc\u30c9\u30ad\u30e3\u30b9\u30c8\u9593\u9694\u3092\u4f7f\u7528\u3057\u3066\u3044\u308b\u3053\u3068\u304c\u539f\u56e0\u3067\u3042\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u3068\u306b\u304b\u304f\u3053\u306e\u30c7\u30d0\u30a4\u30b9\u3092\u8ffd\u52a0\u3059\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u6b21\u306b\u30d6\u30ed\u30fc\u30c9\u30ad\u30e3\u30b9\u30c8\u3092\u53d7\u4fe1\u3057\u305f\u3068\u304d\u306b\u3001\u5fc5\u8981\u306b\u5fdc\u3058\u3066\u30d0\u30a4\u30f3\u30c9\u30ad\u30fc\u3092\u5165\u529b\u3059\u308b\u3088\u3046\u6c42\u3081\u3089\u308c\u307e\u3059\u3002" - }, "user": { "data": { "address": "\u30c7\u30d0\u30a4\u30b9" diff --git a/homeassistant/components/xiaomi_ble/translations/nl.json b/homeassistant/components/xiaomi_ble/translations/nl.json index 2378ad0d84b..de54d8aff8c 100644 --- a/homeassistant/components/xiaomi_ble/translations/nl.json +++ b/homeassistant/components/xiaomi_ble/translations/nl.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Apparaat is al geconfigureerd", "already_in_progress": "De configuratie is momenteel al bezig", - "decryption_failed": "De opgegeven `bindkey` werkte niet, de sensorgegevens konden niet worden ontsleuteld. Controleer dit en probeer het opnieuw.", - "expected_24_characters": "Verwachtte een hexadecimale `bindkey` van 24 karakters.", - "expected_32_characters": "Verwachtte een hexadecimale `bindkey` van 32 karakters.", "no_devices_found": "Geen apparaten gevonden op het netwerk", "reauth_successful": "Herauthenticatie geslaagd" }, @@ -28,9 +25,6 @@ "get_encryption_key_legacy": { "description": "De sensorgegevens van de sensor zijn versleuteld. Om te ontcijferen is een hexadecimale sleutel van 24 tekens nodig." }, - "slow_confirm": { - "description": "Er is de laatste minuut geen aankondigingsbericht van dit apparaat ontvangen, dus we weten niet zeker of dit apparaat encryptie gebruikt of niet. Dit kan zijn omdat het apparaat een trage aankodigings interval heeft. Bevestig om dit apparaat hoe dan ook toe te voegen, dan zal wanneer binnenkort een aankodiging wordt ontvangen worden gevraagd om de `bindkey` als dat nodig is." - }, "user": { "data": { "address": "Apparaat" diff --git a/homeassistant/components/xiaomi_ble/translations/no.json b/homeassistant/components/xiaomi_ble/translations/no.json index 46a8158cad9..3c8670c3c94 100644 --- a/homeassistant/components/xiaomi_ble/translations/no.json +++ b/homeassistant/components/xiaomi_ble/translations/no.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Enheten er allerede konfigurert", "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", - "decryption_failed": "Den oppgitte bindingsn\u00f8kkelen fungerte ikke, sensordata kunne ikke dekrypteres. Vennligst sjekk det og pr\u00f8v igjen.", - "expected_24_characters": "Forventet en heksadesimal bindingsn\u00f8kkel p\u00e5 24 tegn.", - "expected_32_characters": "Forventet en heksadesimal bindingsn\u00f8kkel p\u00e5 32 tegn.", "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket", "reauth_successful": "Re-autentisering var vellykket" }, @@ -34,9 +31,6 @@ }, "description": "Sensordataene som sendes av sensoren er kryptert. For \u00e5 dekryptere den trenger vi en heksadesimal bindn\u00f8kkel p\u00e5 24 tegn." }, - "slow_confirm": { - "description": "Det har ikke v\u00e6rt en kringkasting fra denne enheten i siste \u00f8yeblikk, s\u00e5 vi er ikke sikre p\u00e5 om denne enheten bruker kryptering eller ikke. Dette kan skyldes at enheten bruker et sakte kringkastingsintervall. Bekreft \u00e5 legge til denne enheten uansett, s\u00e5 neste gang en kringkasting mottas, blir du bedt om \u00e5 angi bindingsn\u00f8kkelen hvis det er n\u00f8dvendig." - }, "user": { "data": { "address": "Enhet" diff --git a/homeassistant/components/xiaomi_ble/translations/pl.json b/homeassistant/components/xiaomi_ble/translations/pl.json index 7bb0c5da454..e5a9d7bf3ef 100644 --- a/homeassistant/components/xiaomi_ble/translations/pl.json +++ b/homeassistant/components/xiaomi_ble/translations/pl.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", "already_in_progress": "Konfiguracja jest ju\u017c w toku", - "decryption_failed": "Podany klucz (bindkey) nie zadzia\u0142a\u0142, dane czujnika nie mog\u0142y zosta\u0107 odszyfrowane. Sprawd\u017a go i spr\u00f3buj ponownie.", - "expected_24_characters": "Oczekiwano 24-znakowego szesnastkowego klucza bindkey.", - "expected_32_characters": "Oczekiwano 32-znakowego szesnastkowego klucza bindkey.", "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci", "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" }, @@ -34,9 +31,6 @@ }, "description": "Dane przesy\u0142ane przez sensor s\u0105 szyfrowane. Aby je odszyfrowa\u0107, potrzebujemy 24-znakowego szesnastkowego klucza bindkey." }, - "slow_confirm": { - "description": "W ci\u0105gu ostatniej minuty nie by\u0142o transmisji z tego urz\u0105dzenia, wi\u0119c nie jeste\u015bmy pewni, czy to urz\u0105dzenie u\u017cywa szyfrowania, czy nie. Mo\u017ce to by\u0107 spowodowane tym, \u017ce urz\u0105dzenie u\u017cywa wolnego od\u015bwie\u017cania transmisji. Potwierd\u017a, aby mimo wszystko doda\u0107 to urz\u0105dzenie, a przy nast\u0119pnym odebraniu transmisji zostaniesz poproszony o wprowadzenie klucza bindkey, je\u015bli jest to konieczne." - }, "user": { "data": { "address": "Urz\u0105dzenie" diff --git a/homeassistant/components/xiaomi_ble/translations/pt-BR.json b/homeassistant/components/xiaomi_ble/translations/pt-BR.json index a21c3e1dd9c..b9475b760f3 100644 --- a/homeassistant/components/xiaomi_ble/translations/pt-BR.json +++ b/homeassistant/components/xiaomi_ble/translations/pt-BR.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", - "decryption_failed": "A bindkey fornecida n\u00e3o funcionou, os dados do sensor n\u00e3o puderam ser descriptografados. Por favor verifique e tente novamente.", - "expected_24_characters": "Espera-se uma bindkey hexadecimal de 24 caracteres.", - "expected_32_characters": "Esperado um bindkey hexadecimal de 32 caracteres.", "no_devices_found": "Nenhum dispositivo encontrado na rede", "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" }, @@ -34,9 +31,6 @@ }, "description": "Os dados do sensor transmitidos pelo sensor s\u00e3o criptografados. Para decifr\u00e1-lo, precisamos de uma bindkey hexadecimal de 24 caracteres." }, - "slow_confirm": { - "description": "N\u00e3o houve uma transmiss\u00e3o deste dispositivo no \u00faltimo minuto, por isso n\u00e3o temos certeza se este dispositivo usa criptografia ou n\u00e3o. Isso pode ocorrer porque o dispositivo usa um intervalo de transmiss\u00e3o lento. Confirme para adicionar este dispositivo de qualquer maneira e, na pr\u00f3xima vez que uma transmiss\u00e3o for recebida, voc\u00ea ser\u00e1 solicitado a inserir sua bindkey, se necess\u00e1rio." - }, "user": { "data": { "address": "Dispositivo" diff --git a/homeassistant/components/xiaomi_ble/translations/pt.json b/homeassistant/components/xiaomi_ble/translations/pt.json index 9c78f327cb9..e7b9aaefbed 100644 --- a/homeassistant/components/xiaomi_ble/translations/pt.json +++ b/homeassistant/components/xiaomi_ble/translations/pt.json @@ -8,9 +8,6 @@ "step": { "confirm_slow": { "description": "N\u00e3o houve uma transmiss\u00e3o deste dispositivo no \u00faltimo minuto, por isso n\u00e3o temos certeza se este dispositivo usa criptografia ou n\u00e3o. Isso pode ocorrer porque o dispositivo usa um intervalo de transmiss\u00e3o lento. Confirme para adicionar este dispositivo de qualquer maneira e, na pr\u00f3xima vez que uma transmiss\u00e3o for recebida, voc\u00ea ser\u00e1 solicitado a inserir sua chave de liga\u00e7\u00e3o, se necess\u00e1rio." - }, - "slow_confirm": { - "description": "N\u00e3o houve uma transmiss\u00e3o deste dispositivo no \u00faltimo minuto, por isso n\u00e3o temos certeza se este dispositivo usa criptografia ou n\u00e3o. Isso pode ocorrer porque o dispositivo usa um intervalo de transmiss\u00e3o lento. Confirme para adicionar este dispositivo de qualquer maneira e, na pr\u00f3xima vez que uma transmiss\u00e3o for recebida, voc\u00ea ser\u00e1 solicitado a inserir sua chave de liga\u00e7\u00e3o, se necess\u00e1rio." } } } diff --git a/homeassistant/components/xiaomi_ble/translations/ru.json b/homeassistant/components/xiaomi_ble/translations/ru.json index 3c0bf6cce78..069255a3d50 100644 --- a/homeassistant/components/xiaomi_ble/translations/ru.json +++ b/homeassistant/components/xiaomi_ble/translations/ru.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", - "decryption_failed": "\u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438 \u043d\u0435 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u043b, \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u0442\u044c. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0435\u0433\u043e \u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443.", - "expected_24_characters": "\u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f 24-\u0441\u0438\u043c\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u0448\u0435\u0441\u0442\u043d\u0430\u0434\u0446\u0430\u0442\u0435\u0440\u0438\u0447\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438.", - "expected_32_characters": "\u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f 32-\u0441\u0438\u043c\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u0448\u0435\u0441\u0442\u043d\u0430\u0434\u0446\u0430\u0442\u0435\u0440\u0438\u0447\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438.", "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." }, @@ -34,9 +31,6 @@ }, "description": "\u041f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u043e\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u044b. \u0414\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445, \u043d\u0443\u0436\u0435\u043d 24-\u0441\u0438\u043c\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u0448\u0435\u0441\u0442\u043d\u0430\u0434\u0446\u0430\u0442\u0435\u0440\u0438\u0447\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438." }, - "slow_confirm": { - "description": "\u0412 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u043c\u0438\u043d\u0443\u0442\u044b \u043e\u0442 \u044d\u0442\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u0431\u044b\u043b\u043e \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0448\u0438\u0440\u043e\u043a\u043e\u0432\u0435\u0449\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u044d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438\u043b\u0438 \u043d\u0435\u0442. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 \u0442\u0435\u043c, \u0447\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0432\u0435\u0449\u0430\u043d\u0438\u044f. \u0415\u0441\u043b\u0438 \u0412\u044b \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u044d\u0442\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430, \u043f\u0440\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0448\u0438\u0440\u043e\u043a\u043e\u0432\u0435\u0449\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0412\u0430\u043c \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u0435\u0433\u043e \u043a\u043b\u044e\u0447 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438, \u0435\u0441\u043b\u0438 \u043e\u043d \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c." - }, "user": { "data": { "address": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" diff --git a/homeassistant/components/xiaomi_ble/translations/sv.json b/homeassistant/components/xiaomi_ble/translations/sv.json index 85dd1bdf557..76c41778dca 100644 --- a/homeassistant/components/xiaomi_ble/translations/sv.json +++ b/homeassistant/components/xiaomi_ble/translations/sv.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Enheten \u00e4r redan konfigurerad", "already_in_progress": "Konfiguration redan ig\u00e5ng", - "decryption_failed": "Den tillhandah\u00e5llna bindningsnyckeln fungerade inte, sensordata kunde inte dekrypteras. Kontrollera den och f\u00f6rs\u00f6k igen.", - "expected_24_characters": "F\u00f6rv\u00e4ntade ett hexadecimalt bindningsnyckel med 24 tecken.", - "expected_32_characters": "F\u00f6rv\u00e4ntade ett hexadecimalt bindningsnyckel med 32 tecken.", "no_devices_found": "Inga enheter hittades p\u00e5 n\u00e4tverket", "reauth_successful": "\u00c5terautentisering lyckades" }, @@ -34,9 +31,6 @@ }, "description": "De sensordata som s\u00e4nds av sensorn \u00e4r krypterade. F\u00f6r att dekryptera dem beh\u00f6ver vi en hexadecimal bindningsnyckel med 24 tecken." }, - "slow_confirm": { - "description": "Det har inte s\u00e4nts fr\u00e5n den h\u00e4r enheten under den senaste minuten s\u00e5 vi \u00e4r inte s\u00e4kra p\u00e5 om den h\u00e4r enheten anv\u00e4nder kryptering eller inte. Det kan bero p\u00e5 att enheten anv\u00e4nder ett l\u00e5ngsamt s\u00e4ndningsintervall. Bekr\u00e4fta att l\u00e4gga till den h\u00e4r enheten \u00e4nd\u00e5, n\u00e4sta g\u00e5ng en s\u00e4ndning tas emot kommer du att uppmanas att ange dess bindnyckel om det beh\u00f6vs." - }, "user": { "data": { "address": "Enhet" diff --git a/homeassistant/components/xiaomi_ble/translations/tr.json b/homeassistant/components/xiaomi_ble/translations/tr.json index 9d1b1931e79..cd4f6f6772d 100644 --- a/homeassistant/components/xiaomi_ble/translations/tr.json +++ b/homeassistant/components/xiaomi_ble/translations/tr.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", - "decryption_failed": "Sa\u011flanan ba\u011flama anahtar\u0131 \u00e7al\u0131\u015fmad\u0131, sens\u00f6r verilerinin \u015fifresi \u00e7\u00f6z\u00fclemedi. L\u00fctfen kontrol edin ve tekrar deneyin.", - "expected_24_characters": "24 karakterlik onalt\u0131l\u0131k bir ba\u011flama anahtar\u0131 bekleniyor.", - "expected_32_characters": "32 karakterlik onalt\u0131l\u0131k bir ba\u011flama anahtar\u0131 bekleniyor.", "no_devices_found": "A\u011fda cihaz bulunamad\u0131", "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" }, @@ -34,9 +31,6 @@ }, "description": "Sens\u00f6r taraf\u0131ndan yay\u0131nlanan sens\u00f6r verileri \u015fifrelenmi\u015ftir. \u015eifreyi \u00e7\u00f6zmek i\u00e7in 24 karakterlik onalt\u0131l\u0131k bir ba\u011flama anahtar\u0131na ihtiyac\u0131m\u0131z var." }, - "slow_confirm": { - "description": "Son dakikada bu cihazdan bir yay\u0131n olmad\u0131\u011f\u0131 i\u00e7in bu cihaz\u0131n \u015fifreleme kullan\u0131p kullanmad\u0131\u011f\u0131ndan emin de\u011filiz. Bunun nedeni, cihaz\u0131n yava\u015f bir yay\u0131n aral\u0131\u011f\u0131 kullanmas\u0131 olabilir. Yine de bu cihaz\u0131 eklemeyi onaylay\u0131n, ard\u0131ndan bir sonraki yay\u0131n al\u0131nd\u0131\u011f\u0131nda gerekirse bindkey'i girmeniz istenecektir." - }, "user": { "data": { "address": "Cihaz" diff --git a/homeassistant/components/xiaomi_ble/translations/zh-Hant.json b/homeassistant/components/xiaomi_ble/translations/zh-Hant.json index fdb720b2777..6faa7422e56 100644 --- a/homeassistant/components/xiaomi_ble/translations/zh-Hant.json +++ b/homeassistant/components/xiaomi_ble/translations/zh-Hant.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", - "decryption_failed": "\u6240\u63d0\u4f9b\u7684\u7d81\u5b9a\u78bc\u7121\u6cd5\u4f7f\u7528\u3001\u50b3\u611f\u5668\u8cc7\u6599\u7121\u6cd5\u89e3\u5bc6\u3002\u8acb\u4fee\u6b63\u5f8c\u3001\u518d\u8a66\u4e00\u6b21\u3002", - "expected_24_characters": "\u9700\u8981 24 \u500b\u5b57\u5143\u4e4b\u5341\u516d\u9032\u4f4d\u7d81\u5b9a\u78bc\u3002", - "expected_32_characters": "\u9700\u8981 32 \u500b\u5b57\u5143\u4e4b\u5341\u516d\u9032\u4f4d\u7d81\u5b9a\u78bc\u3002", "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, @@ -34,9 +31,6 @@ }, "description": "\u7531\u50b3\u611f\u5668\u6240\u5ee3\u64ad\u4e4b\u8cc7\u6599\u70ba\u52a0\u5bc6\u8cc7\u6599\u3002\u82e5\u8981\u89e3\u78bc\u3001\u9700\u8981 24 \u500b\u5b57\u5143\u4e4b\u7d81\u5b9a\u78bc\u3002" }, - "slow_confirm": { - "description": "\u8a72\u88dd\u7f6e\u65bc\u904e\u53bb\u4e00\u5206\u9418\u5167\u3001\u672a\u9032\u884c\u4efb\u4f55\u72c0\u614b\u5ee3\u64ad\uff0c\u56e0\u6b64\u7121\u6cd5\u78ba\u5b9a\u88dd\u7f6e\u662f\u5426\u4f7f\u7528\u52a0\u5bc6\u901a\u8a0a\u3002\u4e5f\u53ef\u80fd\u56e0\u70ba\u88dd\u7f6e\u7684\u66f4\u65b0\u983b\u7387\u8f03\u6162\u3002\u78ba\u8a8d\u9084\u662f\u8981\u65b0\u589e\u6b64\u88dd\u7f6e\u3001\u65bc\u4e0b\u6b21\u6536\u5230\u88dd\u7f6e\u5ee3\u64ad\u6642\uff0c\u5982\u679c\u9700\u8981\u3001\u5c07\u63d0\u793a\u60a8\u8f38\u5165\u7d81\u5b9a\u78bc\u3002" - }, "user": { "data": { "address": "\u88dd\u7f6e" diff --git a/homeassistant/components/yeelight/translations/bg.json b/homeassistant/components/yeelight/translations/bg.json index 4a962bbf3d0..ceac6288f03 100644 --- a/homeassistant/components/yeelight/translations/bg.json +++ b/homeassistant/components/yeelight/translations/bg.json @@ -5,7 +5,7 @@ "no_devices_found": "\u041d\u0435 \u0441\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430" }, "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "flow_title": "{model} {id} ({host})", "step": { @@ -25,7 +25,7 @@ "step": { "init": { "data": { - "model": "\u041c\u043e\u0434\u0435\u043b (\u043f\u043e \u0438\u0437\u0431\u043e\u0440)" + "model": "\u041c\u043e\u0434\u0435\u043b" } } } diff --git a/homeassistant/components/zha/translations/bg.json b/homeassistant/components/zha/translations/bg.json index 8130df0e837..3fa4c4fbdf5 100644 --- a/homeassistant/components/zha/translations/bg.json +++ b/homeassistant/components/zha/translations/bg.json @@ -39,26 +39,10 @@ "description": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u0442\u0435 \u043d\u0430 \u0441\u0435\u0440\u0438\u0439\u043d\u0438\u044f \u043f\u043e\u0440\u0442", "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430 \u0441\u0435\u0440\u0438\u0439\u043d\u0438\u044f \u043f\u043e\u0440\u0442" }, - "pick_radio": { - "data": { - "radio_type": "\u0422\u0438\u043f \u0440\u0430\u0434\u0438\u043e" - }, - "title": "\u0422\u0438\u043f \u0440\u0430\u0434\u0438\u043e" - }, - "port_config": { - "data": { - "baudrate": "\u0441\u043a\u043e\u0440\u043e\u0441\u0442 \u043d\u0430 \u043f\u043e\u0440\u0442\u0430" - }, - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "\u041a\u0430\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u0444\u0430\u0439\u043b" } - }, - "user": { - "description": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0441\u0435\u0440\u0438\u0435\u043d \u043f\u043e\u0440\u0442 \u0437\u0430 Zigbee \u0440\u0430\u0434\u0438\u043e", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/ca.json b/homeassistant/components/zha/translations/ca.json index db5d77047f5..22ae3910518 100644 --- a/homeassistant/components/zha/translations/ca.json +++ b/homeassistant/components/zha/translations/ca.json @@ -64,35 +64,12 @@ "description": "La teva c\u00f2pia de seguretat t\u00e9 una adre\u00e7a IEEE diferent de la teva r\u00e0dio. Perqu\u00e8 la xarxa funcioni correctament, tamb\u00e9 s'ha de canviar l'adre\u00e7a IEEE de la teva r\u00e0dio. \n\nAquesta \u00e9s una operaci\u00f3 permanent.", "title": "Sobreescriu l'adre\u00e7a IEEE r\u00e0dio" }, - "pick_radio": { - "data": { - "radio_type": "Tipus de r\u00e0dio" - }, - "description": "Tria el teu tipus de r\u00e0dio Zigbee", - "title": "Tipus de r\u00e0dio" - }, - "port_config": { - "data": { - "baudrate": "velocitat del port", - "flow_control": "control de flux de dades", - "path": "Ruta del port s\u00e8rie al dispositiu" - }, - "description": "Introdueix la configuraci\u00f3 espec\u00edfica de port", - "title": "Configuraci\u00f3" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Puja un fitxer" }, "description": "Restaura la configuraci\u00f3 de xarxa des d'un fitxer JSON de c\u00f2pia de seguretat penjat. Pots baixar-ne un des d'una instal\u00b7laci\u00f3 ZHA diferent anant a **Configuraci\u00f3 de xarxa** o utilitzar un fitxer `coordinator_backup.json` de Zigbee2MQTT.", "title": "Pujada de c\u00f2pia de seguretat manual" - }, - "user": { - "data": { - "path": "Ruta del port s\u00e8rie al dispositiu" - }, - "description": "Selecciona el port s\u00e8rie per a la r\u00e0dio Zigbee", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/cs.json b/homeassistant/components/zha/translations/cs.json index 13e8eabddad..f64524191a9 100644 --- a/homeassistant/components/zha/translations/cs.json +++ b/homeassistant/components/zha/translations/cs.json @@ -41,25 +41,12 @@ }, "title": "P\u0159epsat adresu IEEE r\u00e1dia" }, - "pick_radio": { - "description": "Vyberte typ sv\u00e9ho r\u00e1dia Zigbee" - }, - "port_config": { - "data": { - "baudrate": "rychlost portu" - }, - "title": "Nastaven\u00ed" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Nahr\u00e1t soubor" }, "description": "Obnovit nastaven\u00ed s\u00edt\u011b z nahran\u00e9ho z\u00e1lo\u017en\u00edho souboru JSON. Soubor m\u016f\u017eete st\u00e1hnout z jin\u00e9 instalace ZHA v **Nastaven\u00ed s\u00edt\u011b** nebo pou\u017eijte soubor `coordinator_backup.json` z integrace Zigbee2MQTT.", "title": "Nahr\u00e1t ru\u010dn\u00ed z\u00e1lohu" - }, - "user": { - "description": "Vyberte s\u00e9riov\u00fd port pro r\u00e1dio Zigbee", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/da.json b/homeassistant/components/zha/translations/da.json index 6a6e34c2581..5914cd9d53d 100644 --- a/homeassistant/components/zha/translations/da.json +++ b/homeassistant/components/zha/translations/da.json @@ -5,31 +5,6 @@ }, "error": { "cannot_connect": "Kunne ikke oprette forbindelse til ZHA-enhed." - }, - "step": { - "pick_radio": { - "data": { - "radio_type": "Radiotype" - }, - "description": "V\u00e6lg en type Zigbee-radio", - "title": "Radiotype" - }, - "port_config": { - "data": { - "baudrate": "porthastighed", - "flow_control": "dataflowstyring", - "path": "Sti til seriel enhed" - }, - "description": "Angiv portspecifikke indstillinger", - "title": "Indstillinger" - }, - "user": { - "data": { - "path": "Stien til seriel enhed" - }, - "description": "V\u00e6lg seriel port til Zigbee-radio", - "title": "ZHA" - } } }, "device_automation": { diff --git a/homeassistant/components/zha/translations/de.json b/homeassistant/components/zha/translations/de.json index 5be0add1ae2..78a3b36626b 100644 --- a/homeassistant/components/zha/translations/de.json +++ b/homeassistant/components/zha/translations/de.json @@ -64,35 +64,12 @@ "description": "Dein Backup hat eine andere IEEE-Adresse als dein Funkger\u00e4t. Damit dein Netzwerk ordnungsgem\u00e4\u00df funktioniert, sollte auch die IEEE-Adresse deines Funkger\u00e4ts ge\u00e4ndert werden.\n\nDies ist ein permanenter Vorgang.", "title": "Funk-IEEE-Adresse \u00fcberschreiben" }, - "pick_radio": { - "data": { - "radio_type": "Funktyp" - }, - "description": "W\u00e4hle den Typ deines Zigbee-Funks", - "title": "Funktyp" - }, - "port_config": { - "data": { - "baudrate": "Port-Geschwindigkeit", - "flow_control": "Datenflusskontrolle", - "path": "Serieller Ger\u00e4tepfad" - }, - "description": "Gib die portspezifischen Einstellungen ein", - "title": "Einstellungen" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Datei hochladen" }, "description": "Stelle deine Netzwerkeinstellungen aus einer hochgeladenen Backup-JSON-Datei wieder her. Du kannst eine von einer anderen ZHA-Installation unter **Netzwerkeinstellungen** herunterladen oder eine Zigbee2MQTT-Datei \"coordinator_backup.json\" verwenden.", "title": "Manuelles Backup hochladen" - }, - "user": { - "data": { - "path": "Serieller Ger\u00e4tepfad" - }, - "description": "W\u00e4hle die serielle Schnittstelle f\u00fcr den ZigBee-Funk", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/el.json b/homeassistant/components/zha/translations/el.json index 2d3ca09560c..3a85b35b27b 100644 --- a/homeassistant/components/zha/translations/el.json +++ b/homeassistant/components/zha/translations/el.json @@ -64,35 +64,12 @@ "description": "\u03a4\u03bf \u03b5\u03c6\u03b5\u03b4\u03c1\u03b9\u03ba\u03cc \u03c3\u03b1\u03c2 \u03b1\u03bd\u03c4\u03af\u03b3\u03c1\u03b1\u03c6\u03bf \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03c6\u03bf\u03c1\u03b5\u03c4\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IEEE \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b1\u03c3\u03cd\u03c1\u03bc\u03b1\u03c4\u03cc \u03c3\u03b1\u03c2. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03b9 \u03c3\u03c9\u03c3\u03c4\u03ac \u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03cc \u03c3\u03b1\u03c2, \u03b8\u03b1 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03b9 \u03ba\u03b1\u03b9 \u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IEEE \u03c4\u03bf\u03c5 \u03c1\u03b1\u03b4\u03b9\u03bf\u03c6\u03ce\u03bd\u03bf\u03c5 \u03c3\u03b1\u03c2.\n\n\u0391\u03c5\u03c4\u03ae \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03b9\u03b1 \u03bc\u03cc\u03bd\u03b9\u03bc\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1.", "title": "\u0391\u03bd\u03c4\u03b9\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IEEE Radio" }, - "pick_radio": { - "data": { - "radio_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03ba\u03b5\u03c1\u03b1\u03af\u03b1\u03c2" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03b5\u03bd\u03cc\u03c2 \u03c4\u03cd\u03c0\u03bf\u03c5 \u03ba\u03b5\u03c1\u03b1\u03af\u03b1\u03c2 Zigbee", - "title": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03ba\u03b5\u03c1\u03b1\u03af\u03b1\u03c2" - }, - "port_config": { - "data": { - "baudrate": "\u03c4\u03b1\u03c7\u03cd\u03c4\u03b7\u03c4\u03b1 \u03b8\u03cd\u03c1\u03b1\u03c2", - "flow_control": "\u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c1\u03bf\u03ae\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd", - "path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03c3\u03b5\u03b9\u03c1\u03b9\u03b1\u03ba\u03ae\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2" - }, - "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03c9\u03bd \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c9\u03bd \u03b8\u03cd\u03c1\u03b1\u03c2", - "title": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "\u0391\u03bd\u03b5\u03b2\u03ac\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf" }, "description": "\u0395\u03c0\u03b1\u03bd\u03b1\u03c6\u03ad\u03c1\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5 \u03c3\u03b1\u03c2 \u03b1\u03c0\u03cc \u03ad\u03bd\u03b1 \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03c4\u03c9\u03bc\u03ad\u03bd\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03b1\u03bd\u03c4\u03b9\u03b3\u03c1\u03ac\u03c6\u03bf\u03c5 \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 JSON. \u039c\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03ba\u03ac\u03bd\u03b5\u03c4\u03b5 \u03bb\u03ae\u03c8\u03b7 \u03b5\u03bd\u03cc\u03c2 \u03b1\u03c0\u03cc \u03b4\u03b9\u03b1\u03c6\u03bf\u03c1\u03b5\u03c4\u03b9\u03ba\u03ae \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 ZHA \u03b1\u03c0\u03cc \u03c4\u03b9\u03c2 **\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5** \u03ae \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u00abcoordinator_backup.json\u00bb Zigbee2MQTT.", "title": "\u0391\u03bd\u03b5\u03b2\u03ac\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03bf \u03b1\u03bd\u03c4\u03af\u03b3\u03c1\u03b1\u03c6\u03bf \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2" - }, - "user": { - "data": { - "path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03c3\u03b5\u03b9\u03c1\u03b9\u03b1\u03ba\u03ae\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c3\u03b5\u03b9\u03c1\u03b9\u03b1\u03ba\u03ae\u03c2 \u03b8\u03cd\u03c1\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b5\u03c1\u03b1\u03af\u03b1 Zigbee", - "title": "\u0396\u0397\u0391" } } }, diff --git a/homeassistant/components/zha/translations/en.json b/homeassistant/components/zha/translations/en.json index f624f4d5499..bb62ccca64a 100644 --- a/homeassistant/components/zha/translations/en.json +++ b/homeassistant/components/zha/translations/en.json @@ -64,35 +64,12 @@ "description": "Your backup has a different IEEE address than your radio. For your network to function properly, the IEEE address of your radio should also be changed.\n\nThis is a permanent operation.", "title": "Overwrite Radio IEEE Address" }, - "pick_radio": { - "data": { - "radio_type": "Radio Type" - }, - "description": "Pick a type of your Zigbee radio", - "title": "Radio Type" - }, - "port_config": { - "data": { - "baudrate": "port speed", - "flow_control": "data flow control", - "path": "Serial device path" - }, - "description": "Enter port specific settings", - "title": "Settings" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Upload a file" }, "description": "Restore your network settings from an uploaded backup JSON file. You can download one from a different ZHA installation from **Network Settings**, or use a Zigbee2MQTT `coordinator_backup.json` file.", "title": "Upload a Manual Backup" - }, - "user": { - "data": { - "path": "Serial Device Path" - }, - "description": "Select serial port for Zigbee radio", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/es-419.json b/homeassistant/components/zha/translations/es-419.json index f2aaf80c78a..ae67dc82444 100644 --- a/homeassistant/components/zha/translations/es-419.json +++ b/homeassistant/components/zha/translations/es-419.json @@ -5,11 +5,6 @@ }, "error": { "cannot_connect": "No se puede conectar al dispositivo ZHA." - }, - "step": { - "user": { - "title": "ZHA" - } } }, "device_automation": { diff --git a/homeassistant/components/zha/translations/es.json b/homeassistant/components/zha/translations/es.json index b31f1c10d78..3b5315f2fbd 100644 --- a/homeassistant/components/zha/translations/es.json +++ b/homeassistant/components/zha/translations/es.json @@ -64,35 +64,12 @@ "description": "Tu copia de seguridad tiene una direcci\u00f3n IEEE diferente a la de tu radio. Para que tu red funcione correctamente, tambi\u00e9n debes cambiar la direcci\u00f3n IEEE de tu radio. \n\nEsta es una operaci\u00f3n permanente.", "title": "Sobrescribir la direcci\u00f3n IEEE de la radio" }, - "pick_radio": { - "data": { - "radio_type": "Tipo de Radio" - }, - "description": "Selecciona el tipo de tu radio Zigbee", - "title": "Tipo de Radio" - }, - "port_config": { - "data": { - "baudrate": "velocidad del puerto", - "flow_control": "control de flujo de datos", - "path": "Ruta del dispositivo serie" - }, - "description": "Introduce los ajustes espec\u00edficos del puerto", - "title": "Configuraci\u00f3n" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Subir un archivo" }, "description": "Restaura la configuraci\u00f3n de tu red desde un archivo JSON de copia de seguridad subido. Puedes descargar uno de una instalaci\u00f3n diferente de ZHA desde **Configuraci\u00f3n de red**, o usar un archivo `coordinator_backup.json` de Zigbee2MQTT.", "title": "Subir una copia de seguridad manual" - }, - "user": { - "data": { - "path": "Ruta del Dispositivo Serie" - }, - "description": "Selecciona puerto serie para radio Zigbee", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/et.json b/homeassistant/components/zha/translations/et.json index 54cabbe4c82..d0e8a062120 100644 --- a/homeassistant/components/zha/translations/et.json +++ b/homeassistant/components/zha/translations/et.json @@ -64,35 +64,12 @@ "description": "Varukoopial on erinev IEEE aadress kui raadiol. V\u00f5rgu n\u00f5uetekohaseks toimimiseks tuleks muuta ka raadio IEEE aadressi.\n\nSee on p\u00fcsiv toiming.", "title": "Raadio IEEE aadressi \u00fclekirjutamine" }, - "pick_radio": { - "data": { - "radio_type": "Seadme raadio t\u00fc\u00fcp" - }, - "description": "Vali oma Zigbee raadio t\u00fc\u00fcp", - "title": "Seadme raadio t\u00fc\u00fcp" - }, - "port_config": { - "data": { - "baudrate": "pordi kiirus", - "flow_control": "andmevoo juhtimine", - "path": "Jadaseadme tee" - }, - "description": "Sisesta pordispetsiifilised seaded", - "title": "Seaded" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Faili \u00fcleslaadimine" }, "description": "Taasta oma v\u00f5rgus\u00e4tted \u00fcleslaaditud JSON-varufailist. Saad selle alla laadida teisest ZHA paigaldusest **V\u00f5rguseaded** v\u00f5i kasutada Zigbee2MQTT 'coordinator_backup.json' faili.", "title": "K\u00e4sitsi varundamise \u00fcleslaadimine" - }, - "user": { - "data": { - "path": "Jadaseadme tee" - }, - "description": "Vali Zigbee raadio jadaport", - "title": "" } } }, diff --git a/homeassistant/components/zha/translations/fi.json b/homeassistant/components/zha/translations/fi.json index e22705142d4..c027787ebc2 100644 --- a/homeassistant/components/zha/translations/fi.json +++ b/homeassistant/components/zha/translations/fi.json @@ -2,27 +2,6 @@ "config": { "error": { "cannot_connect": "Yhteyden muodostaminen ZHA-laitteeseen ei onnistu." - }, - "step": { - "pick_radio": { - "data": { - "radio_type": "Radiotyyppi" - }, - "description": "Valitse Zigbee-radion tyyppi", - "title": "Radion tyyppi" - }, - "port_config": { - "data": { - "baudrate": "portin nopeus", - "flow_control": "tietovirran hallinta", - "path": "Sarjalaitteen polku" - }, - "description": "Anna porttikohtaiset asetukset", - "title": "Asetukset" - }, - "user": { - "title": "ZHA" - } } } } \ No newline at end of file diff --git a/homeassistant/components/zha/translations/fr.json b/homeassistant/components/zha/translations/fr.json index 505c6780d6a..0fccc144fb1 100644 --- a/homeassistant/components/zha/translations/fr.json +++ b/homeassistant/components/zha/translations/fr.json @@ -61,34 +61,11 @@ }, "title": "\u00c9craser l'adresse IEEE de la radio" }, - "pick_radio": { - "data": { - "radio_type": "Type de radio" - }, - "description": "Choisissez un type de radio Zigbee", - "title": "Type de radio" - }, - "port_config": { - "data": { - "baudrate": "vitesse du port", - "flow_control": "contr\u00f4le du flux de donn\u00e9es", - "path": "Chemin du p\u00e9riph\u00e9rique s\u00e9rie" - }, - "description": "Saisir les param\u00e8tres sp\u00e9cifiques au port", - "title": "R\u00e9glages" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "T\u00e9l\u00e9verser un fichier" }, "title": "T\u00e9l\u00e9verser une sauvegarde manuelle" - }, - "user": { - "data": { - "path": "Chemin du p\u00e9riph\u00e9rique s\u00e9rie" - }, - "description": "S\u00e9lectionnez le port s\u00e9rie de la radio Zigbee", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/he.json b/homeassistant/components/zha/translations/he.json index 93c614699f2..ed0117bcbe3 100644 --- a/homeassistant/components/zha/translations/he.json +++ b/homeassistant/components/zha/translations/he.json @@ -8,17 +8,11 @@ }, "flow_title": "{name}", "step": { - "port_config": { - "title": "\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "\u05d4\u05e2\u05dc\u05d0\u05ea \u05e7\u05d5\u05d1\u05e5" }, "title": "\u05d4\u05e2\u05dc\u05d0\u05ea \u05d2\u05d9\u05d1\u05d5\u05d9 \u05d9\u05d3\u05e0\u05d9" - }, - "user": { - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/hr.json b/homeassistant/components/zha/translations/hr.json index 2098f53ca2b..e76ee16fc0c 100644 --- a/homeassistant/components/zha/translations/hr.json +++ b/homeassistant/components/zha/translations/hr.json @@ -5,11 +5,6 @@ }, "error": { "cannot_connect": "Povezivanje nije uspjelo" - }, - "step": { - "user": { - "title": "ZHA" - } } }, "options": { diff --git a/homeassistant/components/zha/translations/hu.json b/homeassistant/components/zha/translations/hu.json index b70bfcd597b..8b596df47d7 100644 --- a/homeassistant/components/zha/translations/hu.json +++ b/homeassistant/components/zha/translations/hu.json @@ -64,35 +64,12 @@ "description": "A biztons\u00e1gi m\u00e1solat IEEE-c\u00edme elt\u00e9r a r\u00e1di\u00f3\u00e9t\u00f3l. A h\u00e1l\u00f3zat megfelel\u0151 m\u0171k\u00f6d\u00e9s\u00e9hez a r\u00e1di\u00f3 IEEE-c\u00edm\u00e9t is meg kell v\u00e1ltoztatni. \n\n Ez egy v\u00e9gleles m\u0171velet.", "title": "A r\u00e1di\u00f3 IEEE-c\u00edm\u00e9nek fel\u00fcl\u00edr\u00e1sa" }, - "pick_radio": { - "data": { - "radio_type": "R\u00e1di\u00f3 t\u00edpusa" - }, - "description": "V\u00e1lassza ki a Zigbee r\u00e1di\u00f3 t\u00edpus\u00e1t", - "title": "R\u00e1di\u00f3 t\u00edpusa" - }, - "port_config": { - "data": { - "baudrate": "port sebess\u00e9g", - "flow_control": "adat\u00e1raml\u00e1s szab\u00e1lyoz\u00e1sa", - "path": "Soros eszk\u00f6z el\u00e9r\u00e9si \u00fatja" - }, - "description": "Adja meg a port specifikus be\u00e1ll\u00edt\u00e1sokat", - "title": "Be\u00e1ll\u00edt\u00e1sok" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "F\u00e1jl felt\u00f6lt\u00e9se" }, "description": "H\u00e1l\u00f3zati be\u00e1ll\u00edt\u00e1sok vissza\u00e1ll\u00edt\u00e1sa egy felt\u00f6lt\u00f6tt JSON biztons\u00e1gi m\u00e1solatb\u00f3l. Let\u00f6lthet egyet egy m\u00e1sik ZHA telep\u00edt\u00e9sb\u0151l a **H\u00e1l\u00f3zati be\u00e1ll\u00edt\u00e1sok** men\u00fcpontb\u00f3l, vagy haszn\u00e1lhat egy Zigbee2MQTT `coordinator_backup.json` f\u00e1jlt.", "title": "K\u00e9zi biztons\u00e1gi m\u00e1solat felt\u00f6lt\u00e9se" - }, - "user": { - "data": { - "path": "Soros eszk\u00f6z el\u00e9r\u00e9si \u00fatja" - }, - "description": "V\u00e1lassza ki a Zigbee r\u00e1di\u00f3 soros portj\u00e1t", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/id.json b/homeassistant/components/zha/translations/id.json index 10ad82bc980..9ede5ff9d98 100644 --- a/homeassistant/components/zha/translations/id.json +++ b/homeassistant/components/zha/translations/id.json @@ -64,35 +64,12 @@ "description": "Cadangan Anda memiliki alamat IEEE yang berbeda dari radio Anda. Agar jaringan berfungsi dengan baik, alamat IEEE radio Anda juga harus diubah.\n\nOperasi ini bersifat permanen.", "title": "Timpa Alamat IEEE Radio" }, - "pick_radio": { - "data": { - "radio_type": "Jenis Radio" - }, - "description": "Pilih jenis radio Zigbee Anda", - "title": "Jenis Radio" - }, - "port_config": { - "data": { - "baudrate": "kecepatan port", - "flow_control": "kontrol data flow", - "path": "Jalur perangkat serial" - }, - "description": "Masukkan pengaturan khusus port", - "title": "Setelan" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Unggah file" }, "description": "Pulihkan pengaturan jaringan Anda dari file JSON cadangan yang diunggah. Anda dapat mengunduhnya dari instalasi ZHA yang berbeda dari **Pengaturan Jaringan**, atau menggunakan file Zigbee2MQTT 'coordinator_backup.json'.", "title": "Unggah Cadangan Manual" - }, - "user": { - "data": { - "path": "Jalur Perangkat Serial" - }, - "description": "Pilih port serial untuk radio Zigbee", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/it.json b/homeassistant/components/zha/translations/it.json index 02b3549d263..56f0f28e7f6 100644 --- a/homeassistant/components/zha/translations/it.json +++ b/homeassistant/components/zha/translations/it.json @@ -64,35 +64,12 @@ "description": "Il tuo backup ha un indirizzo IEEE diverso dalla tua radio. Affinch\u00e9 la rete funzioni correttamente, \u00e8 necessario modificare anche l'indirizzo IEEE della radio. \n\nQuesta \u00e8 un'operazione permanente.", "title": "Sovrascrivi indirizzo IEEE radio" }, - "pick_radio": { - "data": { - "radio_type": "Tipo di radio" - }, - "description": "Scegli un tipo di radio Zigbee", - "title": "Tipo di radio" - }, - "port_config": { - "data": { - "baudrate": "velocit\u00e0 della porta", - "flow_control": "controllo del flusso di dati", - "path": "Percorso del dispositivo seriale" - }, - "description": "Inserire le impostazioni specifiche della porta", - "title": "Impostazioni" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Carica un file" }, "description": "Ripristina le impostazioni di rete da un file JSON di backup caricato. Puoi scaricarne uno da un'installazione ZHA diversa da **Impostazioni di rete** o utilizzare un file Zigbee2MQTT `coordinator_backup.json`.", "title": "Carica un backup manuale" - }, - "user": { - "data": { - "path": "Percorso del dispositivo seriale" - }, - "description": "Seleziona la porta seriale per la radio Zigbee", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/ja.json b/homeassistant/components/zha/translations/ja.json index 5cf05092122..030c38106c5 100644 --- a/homeassistant/components/zha/translations/ja.json +++ b/homeassistant/components/zha/translations/ja.json @@ -64,35 +64,12 @@ "description": "\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u306b\u306f\u3001\u7121\u7dda\u3068\u306f\u7570\u306a\u308bIEEE\u30a2\u30c9\u30ec\u30b9\u304c\u3042\u308a\u307e\u3059\u3002\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u304c\u6b63\u3057\u304f\u6a5f\u80fd\u3059\u308b\u306b\u306f\u3001\u7121\u7dda\u306eIEEE\u30a2\u30c9\u30ec\u30b9\u3082\u5909\u66f4\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 \n\n\u3053\u308c\u306f\u6052\u4e45\u7684\u306a\u64cd\u4f5c\u3067\u3059\u3002", "title": "\u7121\u7dda\u306eIEEE\u30a2\u30c9\u30ec\u30b9\u306e\u4e0a\u66f8\u304d" }, - "pick_radio": { - "data": { - "radio_type": "\u7121\u7dda\u30bf\u30a4\u30d7" - }, - "description": "Zigbee\u7121\u7dda\u6a5f\u306e\u30bf\u30a4\u30d7\u3092\u9078\u3076", - "title": "\u7121\u7dda\u30bf\u30a4\u30d7" - }, - "port_config": { - "data": { - "baudrate": "\u30dd\u30fc\u30c8\u901f\u5ea6", - "flow_control": "\u30c7\u30fc\u30bf\u30d5\u30ed\u30fc\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb", - "path": "\u30b7\u30ea\u30a2\u30eb \u30c7\u30d0\u30a4\u30b9\u306e\u30d1\u30b9" - }, - "description": "\u30dd\u30fc\u30c8\u56fa\u6709\u306e\u8a2d\u5b9a\u3092\u5165\u529b", - "title": "\u8a2d\u5b9a" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "\u30d5\u30a1\u30a4\u30eb\u3092\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3059\u308b" }, "description": "\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3055\u308c\u305f\u30d0\u30c3\u30af\u30a2\u30c3\u30d7 JSON \u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u8a2d\u5b9a\u3092\u5fa9\u5143\u3057\u307e\u3059\u3002 **Network Settings** \u304b\u3089\u5225\u306e ZHA \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u304b\u3089\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u304b\u3001Zigbee2MQTT `coordinator_backup.json` \u30d5\u30a1\u30a4\u30eb\u3092\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002", "title": "\u624b\u52d5\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3092\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3059\u308b" - }, - "user": { - "data": { - "path": "\u30b7\u30ea\u30a2\u30eb \u30c7\u30d0\u30a4\u30b9\u306e\u30d1\u30b9" - }, - "description": "Zigbee radio\u7528\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8\u3092\u9078\u629e", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/ko.json b/homeassistant/components/zha/translations/ko.json index 89dfaeba9be..f537347d9cd 100644 --- a/homeassistant/components/zha/translations/ko.json +++ b/homeassistant/components/zha/translations/ko.json @@ -6,32 +6,7 @@ "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, - "flow_title": "ZHA: {name}", - "step": { - "pick_radio": { - "data": { - "radio_type": "\ubb34\uc120 \uc720\ud615" - }, - "description": "\uc9c0\uadf8\ube44 \ubb34\uc120 \uc720\ud615\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694", - "title": "\ubb34\uc120 \uc720\ud615" - }, - "port_config": { - "data": { - "baudrate": "\ud3ec\ud2b8 \uc18d\ub3c4", - "flow_control": "\ub370\uc774\ud130 \ud750\ub984 \uc81c\uc5b4", - "path": "\uc2dc\ub9ac\uc5bc \uc7a5\uce58 \uacbd\ub85c" - }, - "description": "\uac01 \ud3ec\ud2b8\ubcc4 \uc124\uc815\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694", - "title": "\uc124\uc815\ub0b4\uc6a9" - }, - "user": { - "data": { - "path": "\uc2dc\ub9ac\uc5bc \uc7a5\uce58 \uacbd\ub85c" - }, - "description": "Zigbee \ubb34\uc120 \uc7a5\uce58\uc758 \uc2dc\ub9ac\uc5bc \ud3ec\ud2b8\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694", - "title": "ZHA" - } - } + "flow_title": "ZHA: {name}" }, "device_automation": { "action_type": { diff --git a/homeassistant/components/zha/translations/lb.json b/homeassistant/components/zha/translations/lb.json index 3e076eca496..efb94a2539f 100644 --- a/homeassistant/components/zha/translations/lb.json +++ b/homeassistant/components/zha/translations/lb.json @@ -5,31 +5,6 @@ }, "error": { "cannot_connect": "Feeler beim verbannen" - }, - "step": { - "pick_radio": { - "data": { - "radio_type": "Typ vun Radio" - }, - "description": "Typ vum Zigbee Radio auswielen", - "title": "Typ vun Radio" - }, - "port_config": { - "data": { - "baudrate": "Vitesse vum Port", - "flow_control": "Data Flow Kontroll", - "path": "Pad zum seriellen Apparat" - }, - "description": "G\u00ebff spezifesch Port Astellungen an.", - "title": "Astellungen" - }, - "user": { - "data": { - "path": "Pad zum seriellen Apparat" - }, - "description": "Serielle Port fir Zigbee Radio auswielen", - "title": "ZHA" - } } }, "device_automation": { diff --git a/homeassistant/components/zha/translations/nl.json b/homeassistant/components/zha/translations/nl.json index f7bb65db547..f47f71da21c 100644 --- a/homeassistant/components/zha/translations/nl.json +++ b/homeassistant/components/zha/translations/nl.json @@ -35,35 +35,12 @@ "baudrate": "poortsnelheid" } }, - "pick_radio": { - "data": { - "radio_type": "Radio type" - }, - "description": "Kies een type Zigbee-radio", - "title": "Radio type" - }, - "port_config": { - "data": { - "baudrate": "poort snelheid", - "flow_control": "gegevensstroombeheer", - "path": "Serieel apparaatpad" - }, - "description": "Voer poortspecifieke instellingen in", - "title": "Instellingen" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Een bestand uploaden" }, "description": "Herstel je netwerkinstellingen van ge-upload backup-JSON-bestand. Je kunt deze dpwnloaden van een andere ZHA installatie via **Netwerkinstellingen**, of gebruik een Zigbee2MQTT `coordinator_backup.json` bestand.", "title": "Upload een handmatige back-up" - }, - "user": { - "data": { - "path": "Serieel apparaatpad" - }, - "description": "Selecteer seri\u00eble poort voor Zigbee-radio", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/nn.json b/homeassistant/components/zha/translations/nn.json index 9e9b677ddc1..65c3047fcc4 100644 --- a/homeassistant/components/zha/translations/nn.json +++ b/homeassistant/components/zha/translations/nn.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "port_config": { - "title": "Innstillinger" - }, - "user": { - "title": "ZHA" - } - } - }, "device_automation": { "action_type": { "squawk": "Squawk" diff --git a/homeassistant/components/zha/translations/no.json b/homeassistant/components/zha/translations/no.json index 989409f7436..1434d243522 100644 --- a/homeassistant/components/zha/translations/no.json +++ b/homeassistant/components/zha/translations/no.json @@ -64,35 +64,12 @@ "description": "Sikkerhetskopien din har en annen IEEE-adresse enn radioen din. For at nettverket skal fungere ordentlig, b\u00f8r IEEE-adressen til radioen ogs\u00e5 endres. \n\n Dette er en permanent operasjon.", "title": "Overskriv radio IEEE-adresse" }, - "pick_radio": { - "data": { - "radio_type": "Radio type" - }, - "description": "Velg din type Zigbee-radio", - "title": "Radio type" - }, - "port_config": { - "data": { - "baudrate": "porthastighet", - "flow_control": "data flytkontroll", - "path": "Seriell enhetsbane" - }, - "description": "Angi portspesifikke innstillinger", - "title": "Innstillinger" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Last opp en fil" }, "description": "Gjenopprett nettverksinnstillingene fra en opplastet backup JSON-fil. Du kan laste ned en fra en annen ZHA-installasjon fra **Nettverksinnstillinger**, eller bruke en Zigbee2MQTT `coordinator_backup.json`-fil.", "title": "Last opp en manuell sikkerhetskopiering" - }, - "user": { - "data": { - "path": "Seriell enhetsbane" - }, - "description": "Velg seriell port for Zigbee radio", - "title": "" } } }, diff --git a/homeassistant/components/zha/translations/pl.json b/homeassistant/components/zha/translations/pl.json index b2046848f0a..c3593b862ce 100644 --- a/homeassistant/components/zha/translations/pl.json +++ b/homeassistant/components/zha/translations/pl.json @@ -64,35 +64,12 @@ "description": "Twoja kopia zapasowa ma inny adres IEEE ni\u017c twoje radio. Aby sie\u0107 dzia\u0142a\u0142a prawid\u0142owo, nale\u017cy r\u00f3wnie\u017c zmieni\u0107 adres IEEE radia. \n\nTo jest trwa\u0142a operacja.", "title": "Nadpisanie adresu IEEE radia" }, - "pick_radio": { - "data": { - "radio_type": "Typ radia" - }, - "description": "Wyb\u00f3r typu radia Zigbee", - "title": "Typ radia" - }, - "port_config": { - "data": { - "baudrate": "pr\u0119dko\u015b\u0107 portu", - "flow_control": "kontrola przep\u0142ywu danych", - "path": "\u015acie\u017cka urz\u0105dzenia szeregowego" - }, - "description": "Wprowadzanie ustawie\u0144 dla portu", - "title": "Ustawienia" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Prze\u015blij plik" }, "description": "Przywr\u00f3\u0107 ustawienia sieciowe z pliku kopii zapasowej JSON. Mo\u017cesz go pobra\u0107 z innej instalacji ZHA z **Ustawienia sieci** lub u\u017cy\u0107 pliku `coordinator_backup.json` z Zigbee2MQTT.", "title": "Przesy\u0142anie r\u0119cznej kopii zapasowej" - }, - "user": { - "data": { - "path": "\u015acie\u017cka urz\u0105dzenia szeregowego" - }, - "description": "Wyb\u00f3r portu szeregowego dla radia Zigbee", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/pt-BR.json b/homeassistant/components/zha/translations/pt-BR.json index ba0ac930f1e..0eac3642f58 100644 --- a/homeassistant/components/zha/translations/pt-BR.json +++ b/homeassistant/components/zha/translations/pt-BR.json @@ -64,35 +64,12 @@ "description": "Seu backup tem um endere\u00e7o IEEE diferente do seu r\u00e1dio. Para que sua rede funcione corretamente, o endere\u00e7o IEEE do seu r\u00e1dio tamb\u00e9m deve ser alterado. \n\n Esta \u00e9 uma opera\u00e7\u00e3o permanente.", "title": "Sobrescrever o endere\u00e7o IEEE do r\u00e1dio" }, - "pick_radio": { - "data": { - "radio_type": "Tipo de hub zigbee" - }, - "description": "Escolha o tipo de seu hub Zigbee", - "title": "Tipo de hub zigbee" - }, - "port_config": { - "data": { - "baudrate": "velocidade da porta", - "flow_control": "controle de fluxo de dados", - "path": "Caminho do dispositivo serial" - }, - "description": "Digite configura\u00e7\u00f5es espec\u00edficas da porta", - "title": "Configura\u00e7\u00f5es" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Carregar um arquivo" }, "description": "Restaure suas configura\u00e7\u00f5es de rede de um arquivo JSON de backup carregado. Voc\u00ea pode baixar um de uma instala\u00e7\u00e3o ZHA diferente em **Network Settings**, ou usar um arquivo Zigbee2MQTT `coordinator_backup.json`.", "title": "Carregar um backup manualmente" - }, - "user": { - "data": { - "path": "Caminho do dispositivo serial" - }, - "description": "Selecione a porta serial para o hub Zigbee", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/pt.json b/homeassistant/components/zha/translations/pt.json index 7fe13349c9f..c6207a73c61 100644 --- a/homeassistant/components/zha/translations/pt.json +++ b/homeassistant/components/zha/translations/pt.json @@ -64,9 +64,6 @@ }, "description": "Restaure suas configura\u00e7\u00f5es de rede de um arquivo JSON de backup carregado. Voc\u00ea pode baixar um de uma instala\u00e7\u00e3o ZHA diferente em **Network Settings**, ou usar um arquivo Zigbee2MQTT `coordinator_backup.json`.", "title": "Carregar um backup manual" - }, - "user": { - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/ru.json b/homeassistant/components/zha/translations/ru.json index a8e58f3ecd9..bc935c9aa75 100644 --- a/homeassistant/components/zha/translations/ru.json +++ b/homeassistant/components/zha/translations/ru.json @@ -64,35 +64,12 @@ "description": "\u0412 \u0412\u0430\u0448\u0435\u0439 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438 IEEE-\u0430\u0434\u0440\u0435\u0441 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e \u0441\u0435\u0439\u0447\u0430\u0441. \u0427\u0442\u043e\u0431\u044b \u0412\u0430\u0448\u0430 \u0441\u0435\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043b\u0430 \u0434\u043e\u043b\u0436\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, IEEE-\u0430\u0434\u0440\u0435\u0441 \u0412\u0430\u0448\u0435\u0433\u043e \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044f \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d. \n\n\u042d\u0442\u043e \u043d\u0435\u043e\u0431\u0440\u0430\u0442\u0438\u043c\u0430\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f.", "title": "\u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044c IEEE-\u0430\u0434\u0440\u0435\u0441\u0430 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044f" }, - "pick_radio": { - "data": { - "radio_type": "\u0422\u0438\u043f \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044f" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044f Zigbee", - "title": "\u0422\u0438\u043f \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044f" - }, - "port_config": { - "data": { - "baudrate": "\u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043f\u043e\u0440\u0442\u0430", - "flow_control": "\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0442\u043e\u043a\u043e\u043c \u0434\u0430\u043d\u043d\u044b\u0445", - "path": "\u041f\u0443\u0442\u044c \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443" - }, - "description": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e\u0440\u0442\u0430", - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b" }, "description": "\u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0441\u0435\u0442\u0438 \u0438\u0437 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430 JSON. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0435\u0433\u043e \u0438\u0437 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 ZHA \u0438\u0437 \u0440\u0430\u0437\u0434\u0435\u043b\u0430 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0435\u0442\u0438** \u0438\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b \u0438\u0437 Zigbee2MQTT `coordinator_backup.json`.", "title": "\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0439 \u043a\u043e\u043f\u0438\u0438 \u0432\u0440\u0443\u0447\u043d\u0443\u044e" - }, - "user": { - "data": { - "path": "\u041f\u0443\u0442\u044c \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u043e\u0440\u0430 \u0441\u0435\u0442\u0438 Zigbee", - "title": "Zigbee Home Automation" } } }, diff --git a/homeassistant/components/zha/translations/sl.json b/homeassistant/components/zha/translations/sl.json index 54d87c087bb..d60f7de4dcf 100644 --- a/homeassistant/components/zha/translations/sl.json +++ b/homeassistant/components/zha/translations/sl.json @@ -5,11 +5,6 @@ }, "error": { "cannot_connect": "Ne morem se povezati napravo ZHA." - }, - "step": { - "user": { - "title": "ZHA" - } } }, "device_automation": { diff --git a/homeassistant/components/zha/translations/sv.json b/homeassistant/components/zha/translations/sv.json index a95dc2970fc..5fb36e9644b 100644 --- a/homeassistant/components/zha/translations/sv.json +++ b/homeassistant/components/zha/translations/sv.json @@ -64,35 +64,12 @@ "description": "Din s\u00e4kerhetskopia har en annan IEEE-adress \u00e4n din radio. F\u00f6r att ditt n\u00e4tverk ska fungera korrekt b\u00f6r IEEE-adressen f\u00f6r din radio ocks\u00e5 \u00e4ndras. \n\n Detta \u00e4r en permanent \u00e5tg\u00e4rd.", "title": "Skriv \u00f6ver Radio IEEE-adress" }, - "pick_radio": { - "data": { - "radio_type": "Radiotyp" - }, - "description": "V\u00e4lj en typ av Zigbee radio", - "title": "Radiotyp" - }, - "port_config": { - "data": { - "baudrate": "port hastighet", - "flow_control": "datafl\u00f6deskontroll", - "path": "Seriell enhetsv\u00e4g" - }, - "description": "Ange portspecifika inst\u00e4llningar", - "title": "Inst\u00e4llningar" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Ladda upp en fil" }, "description": "\u00c5terst\u00e4ll dina n\u00e4tverksinst\u00e4llningar fr\u00e5n en uppladdad backup-JSON-fil. Du kan ladda ner en fr\u00e5n en annan ZHA-installation fr\u00e5n **N\u00e4tverksinst\u00e4llningar**, eller anv\u00e4nda en Zigbee2MQTT `coordinator_backup.json`-fil.", "title": "Ladda upp en manuell s\u00e4kerhetskopia" - }, - "user": { - "data": { - "path": "Seriell enhetsv\u00e4g" - }, - "description": "V\u00e4lj serieport f\u00f6r Zigbee-radio", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/tr.json b/homeassistant/components/zha/translations/tr.json index f309ecf92a0..e4409a7a1f0 100644 --- a/homeassistant/components/zha/translations/tr.json +++ b/homeassistant/components/zha/translations/tr.json @@ -64,35 +64,12 @@ "description": "Yedeklemenizin, telsizinizden farkl\u0131 bir IEEE adresi var. A\u011f\u0131n\u0131z\u0131n d\u00fczg\u00fcn \u00e7al\u0131\u015fmas\u0131 i\u00e7in telsizinizin IEEE adresinin de de\u011fi\u015ftirilmesi gerekir. \n\n Bu kal\u0131c\u0131 bir operasyondur.", "title": "Radyo IEEE Adresinin \u00dczerine Yaz" }, - "pick_radio": { - "data": { - "radio_type": "Radyo Tipi" - }, - "description": "Zigbee radyonuzun bir t\u00fcr\u00fcn\u00fc se\u00e7in", - "title": "Radyo Tipi" - }, - "port_config": { - "data": { - "baudrate": "ba\u011flant\u0131 noktas\u0131 h\u0131z\u0131", - "flow_control": "veri ak\u0131\u015f\u0131 denetimi", - "path": "Seri cihaz yolu" - }, - "description": "Ba\u011flant\u0131 noktas\u0131na \u00f6zel ayarlar\u0131 girin", - "title": "Ayarlar" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Bir dosya y\u00fckleyin" }, "description": "Y\u00fcklenen bir yedek JSON dosyas\u0131ndan a\u011f ayarlar\u0131n\u0131z\u0131 geri y\u00fckleyin. **A\u011f Ayarlar\u0131**'ndan farkl\u0131 bir ZHA kurulumundan bir tane indirebilir veya bir Zigbee2MQTT `coordinator_backup.json` dosyas\u0131 kullanabilirsiniz.", "title": "Manuel Yedekleme Y\u00fckleyin" - }, - "user": { - "data": { - "path": "Seri Cihaz Yolu" - }, - "description": "Zigbee radyo i\u00e7in seri ba\u011flant\u0131 noktas\u0131 se\u00e7in", - "title": "ZHA" } } }, diff --git a/homeassistant/components/zha/translations/uk.json b/homeassistant/components/zha/translations/uk.json index f7206911534..7e9fdbbca54 100644 --- a/homeassistant/components/zha/translations/uk.json +++ b/homeassistant/components/zha/translations/uk.json @@ -5,31 +5,6 @@ }, "error": { "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f" - }, - "step": { - "pick_radio": { - "data": { - "radio_type": "\u0422\u0438\u043f \u0440\u0430\u0434\u0456\u043e\u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e" - }, - "description": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0442\u0438\u043f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e Zigbee", - "title": "\u0422\u0438\u043f \u0440\u0430\u0434\u0456\u043e\u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e" - }, - "port_config": { - "data": { - "baudrate": "\u0448\u0432\u0438\u0434\u043a\u0456\u0441\u0442\u044c \u043f\u043e\u0440\u0442\u0443", - "flow_control": "\u043a\u0435\u0440\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u043e\u0442\u043e\u043a\u043e\u043c \u0434\u0430\u043d\u0438\u0445", - "path": "\u0428\u043b\u044f\u0445 \u0434\u043e \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e" - }, - "description": "\u0412\u043a\u0430\u0436\u0456\u0442\u044c \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u043e\u0440\u0442\u0443", - "title": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438" - }, - "user": { - "data": { - "path": "\u0428\u043b\u044f\u0445 \u0434\u043e \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e" - }, - "description": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043f\u043e\u0441\u043b\u0456\u0434\u043e\u0432\u043d\u0438\u0439 \u043f\u043e\u0440\u0442 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u043e\u0440\u0430 \u043c\u0435\u0440\u0435\u0436\u0456 Zigbee", - "title": "Zigbee Home Automation" - } } }, "device_automation": { diff --git a/homeassistant/components/zha/translations/zh-Hans.json b/homeassistant/components/zha/translations/zh-Hans.json index c2b8d9f7cc2..f8db217decc 100644 --- a/homeassistant/components/zha/translations/zh-Hans.json +++ b/homeassistant/components/zha/translations/zh-Hans.json @@ -5,31 +5,6 @@ }, "error": { "cannot_connect": "\u65e0\u6cd5\u8fde\u63a5\u5230 ZHA \u8bbe\u5907\u3002" - }, - "step": { - "pick_radio": { - "data": { - "radio_type": "\u65e0\u7ebf\u7535\u7c7b\u578b" - }, - "description": "\u8bf7\u9009\u62e9 Zigbee \u65e0\u7ebf\u7535\u7c7b\u578b", - "title": "\u65e0\u7ebf\u7535\u7c7b\u578b" - }, - "port_config": { - "data": { - "baudrate": "\u6ce2\u7279\u7387", - "flow_control": "\u6570\u636e\u6d41\u63a7\u5236", - "path": "\u4e32\u884c\u8bbe\u5907\u8def\u5f84" - }, - "description": "\u8f93\u5165\u7aef\u53e3\u7684\u7279\u5b9a\u8bbe\u7f6e", - "title": "\u8bbe\u7f6e" - }, - "user": { - "data": { - "path": "\u4e32\u884c\u8bbe\u5907\u8def\u5f84" - }, - "description": "\u9009\u62e9 Zigbee \u7684\u4e32\u884c\u7aef\u53e3", - "title": "ZHA" - } } }, "device_automation": { diff --git a/homeassistant/components/zha/translations/zh-Hant.json b/homeassistant/components/zha/translations/zh-Hant.json index 23d64c8cc42..78cd1dfdb66 100644 --- a/homeassistant/components/zha/translations/zh-Hant.json +++ b/homeassistant/components/zha/translations/zh-Hant.json @@ -64,35 +64,12 @@ "description": "\u5099\u4efd\u4e2d\u7684 IEEE \u4f4d\u5740\u8207\u73fe\u6709\u7121\u7dda\u96fb\u4e0d\u540c\u3002\u70ba\u4e86\u78ba\u8a8d\u7db2\u8def\u6b63\u5e38\u5de5\u4f5c\uff0c\u7121\u7dda\u96fb\u7684 IEEE \u4f4d\u5740\u5fc5\u9808\u9032\u884c\u8b8a\u66f4\u3002\n\n\u6b64\u70ba\u6c38\u4e45\u6027\u64cd\u4f5c\u3002.", "title": "\u8986\u5beb\u7121\u7dda\u96fb IEEE \u4f4d\u5740" }, - "pick_radio": { - "data": { - "radio_type": "\u7121\u7dda\u96fb\u985e\u5225" - }, - "description": "\u9078\u64c7 Zigbee \u7121\u7dda\u96fb\u985e\u5225", - "title": "\u7121\u7dda\u96fb\u985e\u5225" - }, - "port_config": { - "data": { - "baudrate": "\u901a\u8a0a\u57e0\u901f\u5ea6", - "flow_control": "\u8cc7\u6599\u6d41\u91cf\u63a7\u5236", - "path": "\u5e8f\u5217\u88dd\u7f6e\u8def\u5f91" - }, - "description": "\u8f38\u5165\u901a\u8a0a\u57e0\u7279\u5b9a\u8a2d\u5b9a", - "title": "\u8a2d\u5b9a" - }, "upload_manual_backup": { "data": { "uploaded_backup_file": "\u4e0a\u50b3\u6a94\u6848" }, "description": "\u7531\u4e0a\u50b3\u7684\u5099\u4efd JSON \u6a94\u6848\u4e2d\u56de\u5fa9\u7db2\u8def\u8a2d\u5b9a\u3002\u53ef\u4ee5\u7531\u4e0d\u540c\u7684 ZHA \u5b89\u88dd\u4e2d\u7684 **\u7db2\u8def\u8a2d\u5b9a** \u9032\u884c\u4e0b\u8f09\u3001\u6216\u4f7f\u7528 Zigbee2MQTT \u4e2d\u7684 `coordinator_backup.json` \u6a94\u6848\u3002", "title": "\u4e0a\u50b3\u624b\u52d5\u5099\u4efd" - }, - "user": { - "data": { - "path": "\u5e8f\u5217\u88dd\u7f6e\u8def\u5f91" - }, - "description": "\u9078\u64c7 Zigbee \u7121\u7dda\u96fb\u5e8f\u5217\u57e0", - "title": "ZHA" } } }, From ae07e2a9a863d6aedef2c8d1cb68da194612c348 Mon Sep 17 00:00:00 2001 From: mkmer Date: Wed, 23 Nov 2022 22:40:33 -0500 Subject: [PATCH 0664/1033] Add reauth config flow to Whirlpool (#82532) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add ReauthFlow * Update homeassistant/components/whirlpool/config_flow.py Darn it - thought I caught all of these. Co-authored-by: Abílio Costa * Update homeassistant/components/whirlpool/config_flow.py Co-authored-by: Abílio Costa * Update homeassistant/components/whirlpool/config_flow.py Co-authored-by: Abílio Costa * Update homeassistant/components/whirlpool/config_flow.py Co-authored-by: Abílio Costa * Update homeassistant/components/whirlpool/config_flow.py Co-authored-by: Abílio Costa * Update homeassistant/components/whirlpool/config_flow.py Co-authored-by: Abílio Costa Co-authored-by: Abílio Costa --- .../components/whirlpool/__init__.py | 4 +- .../components/whirlpool/config_flow.py | 48 ++++++ .../components/whirlpool/test_config_flow.py | 155 ++++++++++++++++-- 3 files changed, 194 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/whirlpool/__init__.py b/homeassistant/components/whirlpool/__init__.py index c6721197abc..0d6ae706f0c 100644 --- a/homeassistant/components/whirlpool/__init__.py +++ b/homeassistant/components/whirlpool/__init__.py @@ -10,7 +10,7 @@ from whirlpool.backendselector import BackendSelector, Brand, Region from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from .const import DOMAIN @@ -32,7 +32,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if not auth.is_access_token_valid(): _LOGGER.error("Authentication failed") - return False + raise ConfigEntryAuthFailed("Incorrect Password") appliances_manager = AppliancesManager(backend_selector, auth) if not await appliances_manager.fetch_appliances(): diff --git a/homeassistant/components/whirlpool/config_flow.py b/homeassistant/components/whirlpool/config_flow.py index dbc59f82416..4a41c353d7f 100644 --- a/homeassistant/components/whirlpool/config_flow.py +++ b/homeassistant/components/whirlpool/config_flow.py @@ -2,7 +2,9 @@ from __future__ import annotations import asyncio +from collections.abc import Mapping import logging +from typing import Any import aiohttp import voluptuous as vol @@ -21,6 +23,8 @@ STEP_USER_DATA_SCHEMA = vol.Schema( {vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str} ) +REAUTH_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str}) + async def validate_input( hass: core.HomeAssistant, data: dict[str, str] @@ -46,6 +50,50 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for Whirlpool Sixth Sense.""" VERSION = 1 + entry: config_entries.ConfigEntry | None + + async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: + """Handle re-authentication with Whirlpool Sixth Sense.""" + + self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Confirm re-authentication with Whirlpool Sixth Sense.""" + errors: dict[str, str] = {} + + if user_input: + assert self.entry is not None + password = user_input[CONF_PASSWORD] + data = { + CONF_USERNAME: self.entry.data[CONF_USERNAME], + CONF_PASSWORD: password, + } + + try: + await validate_input(self.hass, data) + except InvalidAuth: + errors["base"] = "invalid_auth" + except (CannotConnect, asyncio.TimeoutError): + errors["base"] = "cannot_connect" + else: + self.hass.config_entries.async_update_entry( + self.entry, + data={ + **self.entry.data, + CONF_PASSWORD: password, + }, + ) + await self.hass.config_entries.async_reload(self.entry.entry_id) + return self.async_abort(reason="reauth_successful") + + return self.async_show_form( + step_id="reauth_confirm", + data_schema=REAUTH_SCHEMA, + errors=errors, + ) async def async_step_user(self, user_input=None) -> FlowResult: """Handle the initial step.""" diff --git a/tests/components/whirlpool/test_config_flow.py b/tests/components/whirlpool/test_config_flow.py index 721a4da9383..6e188b68219 100644 --- a/tests/components/whirlpool/test_config_flow.py +++ b/tests/components/whirlpool/test_config_flow.py @@ -3,9 +3,13 @@ import asyncio from unittest.mock import patch import aiohttp +from aiohttp.client_exceptions import ClientConnectionError from homeassistant import config_entries from homeassistant.components.whirlpool.const import DOMAIN +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType from tests.common import MockConfigEntry @@ -56,8 +60,8 @@ async def test_form_invalid_auth(hass): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { - "username": "test-username", - "password": "test-password", + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", }, ) assert result2["type"] == "form" @@ -76,8 +80,8 @@ async def test_form_cannot_connect(hass): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { - "username": "test-username", - "password": "test-password", + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", }, ) assert result2["type"] == "form" @@ -96,8 +100,8 @@ async def test_form_auth_timeout(hass): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { - "username": "test-username", - "password": "test-password", + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", }, ) assert result2["type"] == "form" @@ -116,8 +120,8 @@ async def test_form_generic_auth_exception(hass): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { - "username": "test-username", - "password": "test-password", + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", }, ) assert result2["type"] == "form" @@ -128,7 +132,7 @@ async def test_form_already_configured(hass): """Test we handle cannot connect error.""" mock_entry = MockConfigEntry( domain=DOMAIN, - data={"username": "test-username", "password": "test-password"}, + data={CONF_USERNAME: "test-username", CONF_PASSWORD: "test-password"}, unique_id="test-username", ) mock_entry.add_to_hass(hass) @@ -147,11 +151,140 @@ async def test_form_already_configured(hass): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { - "username": "test-username", - "password": "test-password", + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", }, ) await hass.async_block_till_done() assert result2["type"] == "abort" assert result2["reason"] == "already_configured" + + +async def test_reauth_flow(hass: HomeAssistant) -> None: + """Test a successful reauth flow.""" + + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_USERNAME: "test-username", CONF_PASSWORD: "test-password"}, + unique_id="test-username", + ) + mock_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_REAUTH, + "unique_id": mock_entry.unique_id, + "entry_id": mock_entry.entry_id, + }, + data={"username": "test-username", "password": "new-password"}, + ) + + assert result["step_id"] == "reauth_confirm" + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {} + + with patch( + "homeassistant.components.whirlpool.async_setup_entry", + return_value=True, + ), patch("homeassistant.components.whirlpool.config_flow.Auth.do_auth"), patch( + "homeassistant.components.whirlpool.config_flow.Auth.is_access_token_valid", + return_value=True, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_PASSWORD: "new-password"}, + ) + await hass.async_block_till_done() + + assert result2["type"] == FlowResultType.ABORT + assert result2["reason"] == "reauth_successful" + assert mock_entry.data == { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "new-password", + } + + +async def test_reauth_flow_auth_error(hass: HomeAssistant) -> None: + """Test an authorization error reauth flow.""" + + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={"username": "test-username", "password": "test-password"}, + unique_id="test-username", + ) + mock_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_REAUTH, + "unique_id": mock_entry.unique_id, + "entry_id": mock_entry.entry_id, + }, + data={"username": "test-username", "password": "new-password"}, + ) + + assert result["step_id"] == "reauth_confirm" + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {} + with patch( + "homeassistant.components.whirlpool.async_setup_entry", + return_value=True, + ), patch("homeassistant.components.whirlpool.config_flow.Auth.do_auth"), patch( + "homeassistant.components.whirlpool.config_flow.Auth.is_access_token_valid", + return_value=False, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_PASSWORD: "new-password"}, + ) + await hass.async_block_till_done() + + assert result2["type"] == FlowResultType.FORM + assert result2["errors"] == {"base": "invalid_auth"} + + +async def test_reauth_flow_connnection_error(hass: HomeAssistant) -> None: + """Test a connection error reauth flow.""" + + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={"username": "test-username", "password": "test-password"}, + unique_id="test-username", + ) + mock_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_REAUTH, + "unique_id": mock_entry.unique_id, + "entry_id": mock_entry.entry_id, + }, + data={CONF_USERNAME: "test-username", CONF_PASSWORD: "new-password"}, + ) + + assert result["step_id"] == "reauth_confirm" + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {} + + with patch( + "homeassistant.components.whirlpool.async_setup_entry", + return_value=True, + ), patch( + "homeassistant.components.whirlpool.config_flow.Auth.do_auth", + side_effect=ClientConnectionError, + ), patch( + "homeassistant.components.whirlpool.config_flow.Auth.is_access_token_valid", + return_value=False, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_PASSWORD: "new-password"}, + ) + await hass.async_block_till_done() + + assert result2["type"] == FlowResultType.FORM + assert result2["errors"] == {"base": "cannot_connect"} From 621068211f2045fc9ddb47704066b12af1ed2c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Thu, 24 Nov 2022 08:21:54 +0100 Subject: [PATCH 0665/1033] Do not delete issue when aborting repairs fix flow (#82593) * Do not delete issue when aborting repairs fix flow * lint * types --- .../components/repairs/issue_handler.py | 3 +- .../components/repairs/test_websocket_api.py | 76 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/repairs/issue_handler.py b/homeassistant/components/repairs/issue_handler.py index 9c5e3854773..4914be8b520 100644 --- a/homeassistant/components/repairs/issue_handler.py +++ b/homeassistant/components/repairs/issue_handler.py @@ -85,7 +85,8 @@ class RepairsFlowManager(data_entry_flow.FlowManager): self, flow: data_entry_flow.FlowHandler, result: data_entry_flow.FlowResult ) -> data_entry_flow.FlowResult: """Complete a fix flow.""" - async_delete_issue(self.hass, flow.handler, flow.init_data["issue_id"]) + if result.get("type") != data_entry_flow.FlowResultType.ABORT: + async_delete_issue(self.hass, flow.handler, flow.init_data["issue_id"]) if "result" not in result: result["result"] = None return result diff --git a/tests/components/repairs/test_websocket_api.py b/tests/components/repairs/test_websocket_api.py index 0ba4c3043df..acbd7b879ab 100644 --- a/tests/components/repairs/test_websocket_api.py +++ b/tests/components/repairs/test_websocket_api.py @@ -1,9 +1,11 @@ """Test the repairs websocket API.""" from __future__ import annotations +from collections.abc import Awaitable, Callable from http import HTTPStatus from unittest.mock import ANY, AsyncMock, Mock +from aiohttp import ClientSession, ClientWebSocketResponse from freezegun import freeze_time import pytest import voluptuous as vol @@ -75,6 +77,7 @@ async def create_issues(hass, ws_client, issues=None): EXPECTED_DATA = { "issue_1": None, "issue_2": {"blah": "bleh"}, + "abort_issue1": None, } @@ -101,6 +104,16 @@ class MockFixFlow(RepairsFlow): return self.async_show_form(step_id="custom_step", data_schema=vol.Schema({})) +class MockFixFlowAbort(RepairsFlow): + """Handler for an issue fixing flow that aborts.""" + + async def async_step_init( + self, user_input: dict[str, str] | None = None + ) -> data_entry_flow.FlowResult: + """Handle the first step of a fix flow.""" + return self.async_abort(reason="not_given") + + @pytest.fixture(autouse=True) async def mock_repairs_integration(hass): """Mock a repairs integration.""" @@ -110,6 +123,8 @@ async def mock_repairs_integration(hass): assert issue_id in EXPECTED_DATA assert data == EXPECTED_DATA[issue_id] + if issue_id == "abort_issue1": + return MockFixFlowAbort() return MockFixFlow() mock_platform( @@ -491,3 +506,64 @@ async def test_list_issues(hass: HomeAssistant, hass_storage, hass_ws_client) -> for issue in issues ] } + + +async def test_fix_issue_aborted( + hass: HomeAssistant, + hass_client: Callable[..., Awaitable[ClientSession]], + hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]], +) -> None: + """Test we can fix an issue.""" + assert await async_setup_component(hass, "http", {}) + assert await async_setup_component(hass, DOMAIN, {}) + + ws_client = await hass_ws_client(hass) + client = await hass_client() + + await create_issues( + hass, + ws_client, + issues=[ + { + **DEFAULT_ISSUES[0], + "domain": "fake_integration", + "issue_id": "abort_issue1", + } + ], + ) + + await ws_client.send_json({"id": 3, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + assert len(msg["result"]["issues"]) == 1 + + first_issue = msg["result"]["issues"][0] + + assert first_issue["domain"] == "fake_integration" + assert first_issue["issue_id"] == "abort_issue1" + + resp = await client.post( + "/api/repairs/issues/fix", + json={"handler": "fake_integration", "issue_id": "abort_issue1"}, + ) + + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data == { + "type": "abort", + "flow_id": flow_id, + "handler": "fake_integration", + "reason": "not_given", + "description_placeholders": None, + "result": None, + } + + await ws_client.send_json({"id": 4, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + assert len(msg["result"]["issues"]) == 1 + assert msg["result"]["issues"][0] == first_issue From 697b5db3f2091480cace57713313f55402846d52 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 24 Nov 2022 08:23:30 +0100 Subject: [PATCH 0666/1033] Migrate precipitation units to an enum (#81143) --- homeassistant/const.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 4201c7f2de4..e01a9775917 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -743,15 +743,27 @@ class UnitOfVolumetricFlux(StrEnum): """Derived from mm³/(mm².h)""" -# Precipitation units -# The derivation of these units is a volume of rain amassing in a container -# with constant cross section -PRECIPITATION_INCHES: Final = "in" -PRECIPITATION_MILLIMETERS: Final = "mm" +class UnitOfPrecipitationDepth(StrEnum): + """Precipitation depth. + The derivation of these units is a volume of rain amassing in a container + with constant cross section + """ + + INCHES = "in" + """Derived from in³/in²""" + + MILLIMETERS = "mm" + """Derived from mm³/mm²""" + + +# Precipitation units +PRECIPITATION_INCHES: Final = "in" +"""Deprecated: please use UnitOfPrecipitationDepth.INCHES""" +PRECIPITATION_MILLIMETERS: Final = "mm" +"""Deprecated: please use UnitOfPrecipitationDepth.MILLIMETERS""" PRECIPITATION_MILLIMETERS_PER_HOUR: Final = "mm/h" """Deprecated: please use UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR""" - PRECIPITATION_INCHES_PER_HOUR: Final = "in/h" """Deprecated: please use UnitOfVolumetricFlux.INCHES_PER_HOUR""" From 8a8732f0bc2a7cd891a3ddaff3edbe9c246d6ebf Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 24 Nov 2022 08:25:44 +0100 Subject: [PATCH 0667/1033] Strict type hints for MQTT integration (#82317) * Strict type hints for MQTT integration * Fix errors * Additional corrections * Use cv.template to avoid untyped calls * Enable strict typing policy for MQTT integration * Use ignore[no-untyped-call] * Use # type: ignore[unreachable] * Correct cast * Refactor getting discovery_payload * Remove unused type ignore comments --- .strict-typing | 1 + .../components/mqtt/alarm_control_panel.py | 2 +- homeassistant/components/mqtt/client.py | 26 ++++---- homeassistant/components/mqtt/discovery.py | 62 +++++++++++-------- .../components/mqtt/light/schema_basic.py | 2 +- homeassistant/components/mqtt/mixins.py | 5 +- homeassistant/components/mqtt/models.py | 14 +++-- homeassistant/components/mqtt/subscription.py | 6 +- homeassistant/components/mqtt/util.py | 12 ++-- mypy.ini | 10 +++ 10 files changed, 87 insertions(+), 53 deletions(-) diff --git a/.strict-typing b/.strict-typing index 559ee64ddd7..7ad69dc477f 100644 --- a/.strict-typing +++ b/.strict-typing @@ -187,6 +187,7 @@ homeassistant.components.mjpeg.* homeassistant.components.modbus.* homeassistant.components.modem_callerid.* homeassistant.components.moon.* +homeassistant.components.mqtt.* homeassistant.components.mysensors.* homeassistant.components.nam.* homeassistant.components.nanoleaf.* diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index 41021fe0d37..a0d065cc7fe 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -249,7 +249,7 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): @property def code_arm_required(self) -> bool: """Whether the code is required for arm actions.""" - return self._config[CONF_CODE_ARM_REQUIRED] + return bool(self._config[CONF_CODE_ARM_REQUIRED]) async def async_alarm_disarm(self, code: str | None = None) -> None: """Send disarm command. diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py index 66f1e130ff7..414027776a3 100644 --- a/homeassistant/components/mqtt/client.py +++ b/homeassistant/components/mqtt/client.py @@ -80,7 +80,6 @@ if TYPE_CHECKING: # because integrations should be able to optionally rely on MQTT. import paho.mqtt.client as mqtt - _LOGGER = logging.getLogger(__name__) DISCOVERY_COOLDOWN = 2 @@ -148,16 +147,19 @@ AsyncDeprecatedMessageCallbackType = Callable[ [str, ReceivePayloadType, int], Coroutine[Any, Any, None] ] DeprecatedMessageCallbackType = Callable[[str, ReceivePayloadType, int], None] +DeprecatedMessageCallbackTypes = Union[ + AsyncDeprecatedMessageCallbackType, DeprecatedMessageCallbackType +] def wrap_msg_callback( - msg_callback: AsyncDeprecatedMessageCallbackType | DeprecatedMessageCallbackType, + msg_callback: DeprecatedMessageCallbackTypes, ) -> AsyncMessageCallbackType | MessageCallbackType: """Wrap an MQTT message callback to support deprecated signature.""" # Check for partials to properly determine if coroutine function check_func = msg_callback while isinstance(check_func, partial): - check_func = check_func.func + check_func = check_func.func # type: ignore[unreachable] wrapper_func: AsyncMessageCallbackType | MessageCallbackType if asyncio.iscoroutinefunction(check_func): @@ -170,14 +172,15 @@ def wrap_msg_callback( ) wrapper_func = async_wrapper - else: + return wrapper_func - @wraps(msg_callback) - def wrapper(msg: ReceiveMessage) -> None: - """Call with deprecated signature.""" - msg_callback(msg.topic, msg.payload, msg.qos) + @wraps(msg_callback) + def wrapper(msg: ReceiveMessage) -> None: + """Call with deprecated signature.""" + msg_callback(msg.topic, msg.payload, msg.qos) + + wrapper_func = wrapper - wrapper_func = wrapper return wrapper_func @@ -187,8 +190,7 @@ async def async_subscribe( topic: str, msg_callback: AsyncMessageCallbackType | MessageCallbackType - | DeprecatedMessageCallbackType - | AsyncDeprecatedMessageCallbackType, + | DeprecatedMessageCallbackTypes, qos: int = DEFAULT_QOS, encoding: str | None = DEFAULT_ENCODING, ) -> CALLBACK_TYPE: @@ -219,7 +221,7 @@ async def async_subscribe( msg_callback.__name__, ) wrapped_msg_callback = wrap_msg_callback( - cast(DeprecatedMessageCallbackType, msg_callback) + cast(DeprecatedMessageCallbackTypes, msg_callback) ) async_remove = await mqtt_data.client.async_subscribe( diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index 84f14d26146..9907de18ee9 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -97,7 +97,7 @@ async def async_start( # noqa: C901 mqtt_data = get_mqtt_data(hass) mqtt_integrations = {} - async def async_discovery_message_received(msg) -> None: + async def async_discovery_message_received(msg: ReceiveMessage) -> None: """Process the received message.""" mqtt_data.last_discovery = time.time() payload = msg.payload @@ -122,46 +122,50 @@ async def async_start( # noqa: C901 if payload: try: - payload = json_loads(payload) + discovery_payload = MQTTDiscoveryPayload(json_loads(payload)) except ValueError: _LOGGER.warning("Unable to parse JSON %s: '%s'", object_id, payload) return + else: + discovery_payload = MQTTDiscoveryPayload({}) - payload = MQTTDiscoveryPayload(payload) - - for key in list(payload): + for key in list(discovery_payload): abbreviated_key = key key = ABBREVIATIONS.get(key, key) - payload[key] = payload.pop(abbreviated_key) + discovery_payload[key] = discovery_payload.pop(abbreviated_key) - if CONF_DEVICE in payload: - device = payload[CONF_DEVICE] + if CONF_DEVICE in discovery_payload: + device = discovery_payload[CONF_DEVICE] for key in list(device): abbreviated_key = key key = DEVICE_ABBREVIATIONS.get(key, key) device[key] = device.pop(abbreviated_key) - if CONF_AVAILABILITY in payload: - for availability_conf in cv.ensure_list(payload[CONF_AVAILABILITY]): + if CONF_AVAILABILITY in discovery_payload: + for availability_conf in cv.ensure_list( + discovery_payload[CONF_AVAILABILITY] + ): if isinstance(availability_conf, dict): for key in list(availability_conf): abbreviated_key = key key = ABBREVIATIONS.get(key, key) availability_conf[key] = availability_conf.pop(abbreviated_key) - if TOPIC_BASE in payload: - base = payload.pop(TOPIC_BASE) - for key, value in payload.items(): + if TOPIC_BASE in discovery_payload: + base = discovery_payload.pop(TOPIC_BASE) + for key, value in discovery_payload.items(): if isinstance(value, str) and value: if value[0] == TOPIC_BASE and key.endswith("topic"): - payload[key] = f"{base}{value[1:]}" + discovery_payload[key] = f"{base}{value[1:]}" if value[-1] == TOPIC_BASE and key.endswith("topic"): - payload[key] = f"{value[:-1]}{base}" - if payload.get(CONF_AVAILABILITY): - for availability_conf in cv.ensure_list(payload[CONF_AVAILABILITY]): + discovery_payload[key] = f"{value[:-1]}{base}" + if discovery_payload.get(CONF_AVAILABILITY): + for availability_conf in cv.ensure_list( + discovery_payload[CONF_AVAILABILITY] + ): if not isinstance(availability_conf, dict): continue - if topic := availability_conf.get(CONF_TOPIC): + if topic := str(availability_conf.get(CONF_TOPIC)): if topic[0] == TOPIC_BASE: availability_conf[CONF_TOPIC] = f"{base}{topic[1:]}" if topic[-1] == TOPIC_BASE: @@ -171,21 +175,25 @@ async def async_start( # noqa: C901 discovery_id = " ".join((node_id, object_id)) if node_id else object_id discovery_hash = (component, discovery_id) - if payload: + if discovery_payload: # Attach MQTT topic to the payload, used for debug prints - setattr(payload, "__configuration_source__", f"MQTT (topic: '{topic}')") + setattr( + discovery_payload, + "__configuration_source__", + f"MQTT (topic: '{topic}')", + ) discovery_data = { ATTR_DISCOVERY_HASH: discovery_hash, - ATTR_DISCOVERY_PAYLOAD: payload, + ATTR_DISCOVERY_PAYLOAD: discovery_payload, ATTR_DISCOVERY_TOPIC: topic, } - setattr(payload, "discovery_data", discovery_data) + setattr(discovery_payload, "discovery_data", discovery_data) - payload[CONF_PLATFORM] = "mqtt" + discovery_payload[CONF_PLATFORM] = "mqtt" if discovery_hash in mqtt_data.discovery_pending_discovered: pending = mqtt_data.discovery_pending_discovered[discovery_hash]["pending"] - pending.appendleft(payload) + pending.appendleft(discovery_payload) _LOGGER.info( "Component has already been discovered: %s %s, queuing update", component, @@ -193,7 +201,9 @@ async def async_start( # noqa: C901 ) return - await async_process_discovery_payload(component, discovery_id, payload) + await async_process_discovery_payload( + component, discovery_id, discovery_payload + ) async def async_process_discovery_payload( component: str, discovery_id: str, payload: MQTTDiscoveryPayload @@ -204,7 +214,7 @@ async def async_start( # noqa: C901 discovery_hash = (component, discovery_id) if discovery_hash in mqtt_data.discovery_already_discovered or payload: - async def discovery_done(_) -> None: + async def discovery_done(_: Any) -> None: pending = mqtt_data.discovery_pending_discovered[discovery_hash][ "pending" ] diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index dfa8af0097d..689d388b92a 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -680,7 +680,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): restore_state(ATTR_HS_COLOR, ATTR_XY_COLOR) @property - def assumed_state(self): + def assumed_state(self) -> bool: """Return true if we do optimistic updates.""" return self._optimistic diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 3cb208f3adc..6df545d7508 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -620,7 +620,8 @@ async def cleanup_device_registry( def get_discovery_hash(discovery_data: DiscoveryInfoType) -> tuple[str, str]: """Get the discovery hash from the discovery data.""" - return discovery_data[ATTR_DISCOVERY_HASH] + discovery_hash: tuple[str, str] = discovery_data[ATTR_DISCOVERY_HASH] + return discovery_hash def send_discovery_done(hass: HomeAssistant, discovery_data: DiscoveryInfoType) -> None: @@ -1113,7 +1114,7 @@ class MqttEntity( @property def entity_registry_enabled_default(self) -> bool: """Return if the entity should be enabled when first added to the entity registry.""" - return self._config[CONF_ENABLED_BY_DEFAULT] + return bool(self._config[CONF_ENABLED_BY_DEFAULT]) @property def entity_category(self) -> EntityCategory | None: diff --git a/homeassistant/components/mqtt/models.py b/homeassistant/components/mqtt/models.py index c0b299c7582..aaef5e3e3e8 100644 --- a/homeassistant/components/mqtt/models.py +++ b/homeassistant/components/mqtt/models.py @@ -150,7 +150,7 @@ class MqttCommandTemplate: if self._entity: values[ATTR_ENTITY_ID] = self._entity.entity_id values[ATTR_NAME] = self._entity.name - if not self._template_state: + if not self._template_state and self._command_template.hass is not None: self._template_state = template.TemplateStateFromEntityId( self._entity.hass, self._entity.entity_id ) @@ -200,6 +200,8 @@ class MqttValueTemplate: variables: TemplateVarsType = None, ) -> ReceivePayloadType: """Render with possible json value or pass-though a received MQTT value.""" + rendered_payload: ReceivePayloadType + if self._value_template is None: return payload @@ -227,9 +229,12 @@ class MqttValueTemplate: values, self._value_template, ) - return self._value_template.async_render_with_possible_json_value( - payload, variables=values + rendered_payload = ( + self._value_template.async_render_with_possible_json_value( + payload, variables=values + ) ) + return rendered_payload _LOGGER.debug( "Rendering incoming payload '%s' with variables %s with default value '%s' and %s", @@ -238,9 +243,10 @@ class MqttValueTemplate: default, self._value_template, ) - return self._value_template.async_render_with_possible_json_value( + rendered_payload = self._value_template.async_render_with_possible_json_value( payload, default, variables=values ) + return rendered_payload class EntityTopicState: diff --git a/homeassistant/components/mqtt/subscription.py b/homeassistant/components/mqtt/subscription.py index 87f5d3882bb..e3fd5e50093 100644 --- a/homeassistant/components/mqtt/subscription.py +++ b/homeassistant/components/mqtt/subscription.py @@ -19,7 +19,7 @@ class EntitySubscription: """Class to hold data about an active entity topic subscription.""" hass: HomeAssistant = attr.ib() - topic: str = attr.ib() + topic: str | None = attr.ib() message_callback: MessageCallbackType = attr.ib() subscribe_task: Coroutine[Any, Any, Callable[[], None]] | None = attr.ib() unsubscribe_callback: Callable[[], None] | None = attr.ib() @@ -39,7 +39,7 @@ class EntitySubscription: other.unsubscribe_callback() # Clear debug data if it exists debug_info.remove_subscription( - self.hass, other.message_callback, other.topic + self.hass, other.message_callback, str(other.topic) ) if self.topic is None: @@ -112,7 +112,7 @@ def async_prepare_subscribe_topics( remaining.unsubscribe_callback() # Clear debug data if it exists debug_info.remove_subscription( - hass, remaining.message_callback, remaining.topic + hass, remaining.message_callback, str(remaining.topic) ) return new_state diff --git a/homeassistant/components/mqtt/util.py b/homeassistant/components/mqtt/util.py index bfd961871d3..97bb120f842 100644 --- a/homeassistant/components/mqtt/util.py +++ b/homeassistant/components/mqtt/util.py @@ -97,7 +97,7 @@ def valid_subscribe_topic(topic: Any) -> str: def valid_subscribe_topic_template(value: Any) -> template.Template: """Validate either a jinja2 template or a valid MQTT subscription topic.""" - tpl = template.Template(value) + tpl = cv.template(value) if tpl.is_static: valid_subscribe_topic(value) @@ -115,7 +115,8 @@ def valid_publish_topic(topic: Any) -> str: def valid_qos_schema(qos: Any) -> int: """Validate that QOS value is valid.""" - return _VALID_QOS_SCHEMA(qos) + validated_qos: int = _VALID_QOS_SCHEMA(qos) + return validated_qos _MQTT_WILL_BIRTH_SCHEMA = vol.Schema( @@ -138,9 +139,12 @@ def valid_birth_will(config: ConfigType) -> ConfigType: def get_mqtt_data(hass: HomeAssistant, ensure_exists: bool = False) -> MqttData: """Return typed MqttData from hass.data[DATA_MQTT].""" + mqtt_data: MqttData if ensure_exists: - return hass.data.setdefault(DATA_MQTT, MqttData()) - return hass.data[DATA_MQTT] + mqtt_data = hass.data.setdefault(DATA_MQTT, MqttData()) + return mqtt_data + mqtt_data = hass.data[DATA_MQTT] + return mqtt_data async def async_create_certificate_temp_files( diff --git a/mypy.ini b/mypy.ini index a347218cb70..737761ae5a5 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1623,6 +1623,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.mqtt.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.mysensors.*] check_untyped_defs = true disallow_incomplete_defs = true From 9dd1b85cbb20fe9b0f125a513b3fceb78242df55 Mon Sep 17 00:00:00 2001 From: On Freund Date: Thu, 24 Nov 2022 09:31:17 +0200 Subject: [PATCH 0668/1033] Allow `device_attr` and `is_device_attr` to be used as a filter and a test (respectively) (#81924) Co-authored-by: Franck Nijhof --- homeassistant/helpers/template.py | 3 +++ tests/helpers/test_template.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index fe073dcb742..3d74024cb00 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -2094,7 +2094,10 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment): self.filters["device_entities"] = pass_context(self.globals["device_entities"]) self.globals["device_attr"] = hassfunction(device_attr) + self.filters["device_attr"] = pass_context(self.globals["device_attr"]) + self.globals["is_device_attr"] = hassfunction(is_device_attr) + self.tests["is_device_attr"] = pass_eval_context(self.globals["is_device_attr"]) self.globals["config_entry_id"] = hassfunction(config_entry_id) self.filters["config_entry_id"] = pass_context(self.globals["config_entry_id"]) diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index cd73e663b71..07463787a8b 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -2738,6 +2738,21 @@ async def test_device_attr(hass: HomeAssistant) -> None: assert_result_info(info, True) assert info.rate_limit is None + # Test filter syntax (device_attr) + info = render_to_info( + hass, f"{{{{ '{entity_entry.entity_id}' | device_attr('model') }}}}" + ) + assert_result_info(info, "test") + assert info.rate_limit is None + + # Test test syntax (is_device_attr) + info = render_to_info( + hass, + f"{{{{ ['{device_entry.id}'] | select('is_device_attr', 'model', 'test') | list }}}}", + ) + assert_result_info(info, [device_entry.id]) + assert info.rate_limit is None + async def test_area_id(hass: HomeAssistant) -> None: """Test area_id function.""" From b525259878233fbf355fa2ca1f20358f4c9810df Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 24 Nov 2022 08:41:14 +0100 Subject: [PATCH 0669/1033] Add type hints to template result wrapper (#82575) * Add type hints to template result wrapper * Remove `ignore[call-arg]` * Use tuple * Alphabetise --- homeassistant/helpers/template.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 3d74024cb00..2b191d7537a 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -170,10 +170,10 @@ class ResultWrapper: render_result: str | None -def gen_result_wrapper(kls): +def gen_result_wrapper(kls: type[dict | list | set]) -> type: """Generate a result wrapper.""" - class Wrapper(kls, ResultWrapper): + class Wrapper(kls, ResultWrapper): # type: ignore[valid-type,misc] """Wrapper of a kls that can store render_result.""" def __init__(self, *args: Any, render_result: str | None = None) -> None: @@ -186,7 +186,7 @@ def gen_result_wrapper(kls): if kls is set: return str(set(self)) - return cast(str, kls.__str__(self)) + return kls.__str__(self) return self.render_result @@ -214,10 +214,8 @@ class TupleWrapper(tuple, ResultWrapper): return self.render_result -RESULT_WRAPPERS: dict[type, type] = { - kls: gen_result_wrapper(kls) # type: ignore[no-untyped-call] - for kls in (list, dict, set) -} +_types: tuple[type[dict | list | set], ...] = (dict, list, set) +RESULT_WRAPPERS: dict[type, type] = {kls: gen_result_wrapper(kls) for kls in _types} RESULT_WRAPPERS[tuple] = TupleWrapper From 4c38a5d773e7e70489b0c78bf66b71c0ad3dbe19 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 24 Nov 2022 12:18:09 +0100 Subject: [PATCH 0670/1033] Add OptionsFlow helper class (#82531) * Add OptionsFlow helper classes * More integrations * Adjust SchemaOptionsFlowHandler * Use single class * Simplify access to options * Reduce PR * Make _options private * Add test --- homeassistant/config_entries.py | 12 ++++++++++- .../helpers/schema_config_entry_flow.py | 12 ++++++----- tests/test_config_entries.py | 20 +++++++++++++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 5679eeabcc7..fa37e9ffa1c 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -5,6 +5,7 @@ import asyncio from collections import ChainMap from collections.abc import Callable, Coroutine, Generator, Iterable, Mapping from contextvars import ContextVar +from copy import deepcopy from enum import Enum import functools import logging @@ -1672,11 +1673,20 @@ class OptionsFlowManager(data_entry_flow.FlowManager): class OptionsFlow(data_entry_flow.FlowHandler): - """Base class for config option flows.""" + """Base class for config options flows.""" handler: str +class OptionsFlowWithConfigEntry(OptionsFlow): + """Base class for options flows with config entry and options.""" + + def __init__(self, config_entry: ConfigEntry) -> None: + """Initialize options flow.""" + self.config_entry = config_entry + self._options = deepcopy(dict(config_entry.options)) + + class EntityRegistryDisabledHandler: """Handler to handle when entities related to config entries updating disabled_by.""" diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 405d9333776..13797483172 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -75,12 +75,12 @@ class SchemaCommonFlowHandler: self, handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, flow: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep], - config_entry: config_entries.ConfigEntry | None, + options: dict[str, Any] | None, ) -> None: """Initialize a common handler.""" self._flow = flow self._handler = handler - self._options = dict(config_entry.options) if config_entry is not None else {} + self._options = options if options is not None else {} async def async_step( self, step_id: str, user_input: dict[str, Any] | None = None @@ -300,7 +300,7 @@ class SchemaConfigFlowHandler(config_entries.ConfigFlow): ) -class SchemaOptionsFlowHandler(config_entries.OptionsFlow): +class SchemaOptionsFlowHandler(config_entries.OptionsFlowWithConfigEntry): """Handle a schema based options flow.""" def __init__( @@ -310,8 +310,10 @@ class SchemaOptionsFlowHandler(config_entries.OptionsFlow): async_options_flow_finished: Callable[[HomeAssistant, Mapping[str, Any]], None], ) -> None: """Initialize options flow.""" - self._common_handler = SchemaCommonFlowHandler(self, options_flow, config_entry) - self.config_entry = config_entry + super().__init__(config_entry) + self._common_handler = SchemaCommonFlowHandler( + self, options_flow, self._options + ) self._async_options_flow_finished = async_options_flow_finished for step in options_flow: diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 272b3d321b8..68ece465226 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -3426,3 +3426,23 @@ async def test_async_wait_component_startup(hass: HomeAssistant): # The component has been loaded assert "test" in hass.config.components + + +async def test_options_flow_options_not_mutated() -> None: + """Test that OptionsFlowWithConfigEntry doesn't mutate entry options.""" + entry = MockConfigEntry( + domain="test", + data={"first": True}, + options={"sub_dict": {"1": "one"}, "sub_list": ["one"]}, + ) + + options_flow = config_entries.OptionsFlowWithConfigEntry(entry) + + options_flow._options["sub_dict"]["2"] = "two" + options_flow._options["sub_list"].append("two") + + assert options_flow._options == { + "sub_dict": {"1": "one", "2": "two"}, + "sub_list": ["one", "two"], + } + assert entry.options == {"sub_dict": {"1": "one"}, "sub_list": ["one"]} From 072bbcf203202ac0398eec838db9ae0ee7928d59 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 24 Nov 2022 13:46:39 +0100 Subject: [PATCH 0671/1033] Fix round typing [homewizard] (#82628) --- homeassistant/components/homewizard/number.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/homewizard/number.py b/homeassistant/components/homewizard/number.py index 43dccc364bd..6acbbcfa37b 100644 --- a/homeassistant/components/homewizard/number.py +++ b/homeassistant/components/homewizard/number.py @@ -1,6 +1,8 @@ """Creates HomeWizard Number entities.""" from __future__ import annotations +from typing import Optional, cast + from homeassistant.components.number import NumberEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE @@ -60,6 +62,7 @@ class HWEnergyNumberEntity( @property def native_value(self) -> float | None: """Return the current value.""" - if self.coordinator.data["state"].brightness is None: + brightness = cast(Optional[float], self.coordinator.data["state"].brightness) + if brightness is None: return None - return round(self.coordinator.data["state"].brightness * (100 / 255)) + return round(brightness * (100 / 255)) From ba18571cbe06e557ed442fced0e9b86071ab11c7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 24 Nov 2022 14:37:55 +0100 Subject: [PATCH 0672/1033] Set last_step in SchemaCommonFlowHandler (#82616) * Set last_step in SchemaCommonFlowHandler * Always use boolean * Adjust next_step definition --- homeassistant/helpers/schema_config_entry_flow.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 13797483172..5dcda69aae0 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -42,8 +42,8 @@ class SchemaFlowFormStep: # The next_step function is called if the schema validates successfully or if no # schema is defined. The next_step function is passed the union of config entry # options and user input from previous steps. - # If next_step returns None, the flow is ended with FlowResultType.CREATE_ENTRY. - next_step: Callable[[dict[str, Any]], str | None] = lambda _: None + # If next_step is None, the flow is ended with FlowResultType.CREATE_ENTRY. + next_step: Callable[[dict[str, Any]], str] | None = None # Optional function to allow amending a form schema. # The update_form_schema function is called before async_show_form is called. The @@ -136,12 +136,11 @@ class SchemaCommonFlowHandler: next_step_id: str = step_id if user_input is not None or form_step.schema is None: # Get next step - next_step_id_or_end_flow = form_step.next_step(self._options) - if next_step_id_or_end_flow is None: + if form_step.next_step is None: # Flow done, create entry or update config entry options return self._handler.async_create_entry(data=self._options) - next_step_id = next_step_id_or_end_flow + next_step_id = form_step.next_step(self._options) return self._show_next_step(next_step_id) @@ -188,7 +187,10 @@ class SchemaCommonFlowHandler: # Show form for next step return self._handler.async_show_form( - step_id=next_step_id, data_schema=data_schema, errors=errors + step_id=next_step_id, + data_schema=data_schema, + errors=errors, + last_step=form_step.next_step is None, ) async def _async_menu_step( From c0425619e2c31657ed421324f25a8ac4b2bae03c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 24 Nov 2022 14:39:01 +0100 Subject: [PATCH 0673/1033] Fix round typing [mqtt] (#82629) --- homeassistant/components/mqtt/cover.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 533698d91b3..4d230c20ae2 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -720,8 +720,8 @@ class MqttCover(MqttEntity, CoverEntity): """Find the 0-100% value within the specified range.""" # the range of motion as defined by the min max values if range_type == COVER_PAYLOAD: - max_range = self._config[CONF_POSITION_OPEN] - min_range = self._config[CONF_POSITION_CLOSED] + max_range: int = self._config[CONF_POSITION_OPEN] + min_range: int = self._config[CONF_POSITION_CLOSED] else: max_range = self._config[CONF_TILT_MAX] min_range = self._config[CONF_TILT_MIN] @@ -748,8 +748,8 @@ class MqttCover(MqttEntity, CoverEntity): returning the offset """ if range_type == COVER_PAYLOAD: - max_range = self._config[CONF_POSITION_OPEN] - min_range = self._config[CONF_POSITION_CLOSED] + max_range: int = self._config[CONF_POSITION_OPEN] + min_range: int = self._config[CONF_POSITION_CLOSED] else: max_range = self._config[CONF_TILT_MAX] min_range = self._config[CONF_TILT_MIN] From a856abf47f006465572e403a6303c8b19e922ed7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 24 Nov 2022 14:43:40 +0100 Subject: [PATCH 0674/1033] Make async_options_flow_finished optional (#82615) * Make async_options_flow_finished optional * Adjust docstring --- homeassistant/helpers/schema_config_entry_flow.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 5dcda69aae0..39fa5164f62 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -309,9 +309,16 @@ class SchemaOptionsFlowHandler(config_entries.OptionsFlowWithConfigEntry): self, config_entry: config_entries.ConfigEntry, options_flow: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep], - async_options_flow_finished: Callable[[HomeAssistant, Mapping[str, Any]], None], + async_options_flow_finished: Callable[[HomeAssistant, Mapping[str, Any]], None] + | None = None, ) -> None: - """Initialize options flow.""" + """Initialize options flow. + + If needed, `async_options_flow_finished` can be set to take necessary actions + after the options flow is finished. The second parameter contains config entry + options, which is the union of stored options and user input from the options + flow steps. + """ super().__init__(config_entry) self._common_handler = SchemaCommonFlowHandler( self, options_flow, self._options @@ -346,7 +353,8 @@ class SchemaOptionsFlowHandler(config_entries.OptionsFlowWithConfigEntry): **kwargs: Any, ) -> FlowResult: """Finish config flow and create a config entry.""" - self._async_options_flow_finished(self.hass, data) + if self._async_options_flow_finished: + self._async_options_flow_finished(self.hass, data) return super().async_create_entry(title="", data=data, **kwargs) From d0390860fb5cc8e2f228db27594f21bb6e30dd33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Thu, 24 Nov 2022 14:54:41 +0100 Subject: [PATCH 0675/1033] Add repair abort flow to demo integration (#82614) --- homeassistant/components/demo/__init__.py | 9 ++++++ homeassistant/components/demo/repairs.py | 14 ++++++++++ homeassistant/components/demo/strings.json | 9 ++++++ .../components/demo/translations/en.json | 12 ++++++++ tests/components/demo/test_init.py | 28 +++++++++++++++++++ 5 files changed, 72 insertions(+) diff --git a/homeassistant/components/demo/__init__.py b/homeassistant/components/demo/__init__.py index 3bbafefa6ae..dd14d9f7b2a 100644 --- a/homeassistant/components/demo/__init__.py +++ b/homeassistant/components/demo/__init__.py @@ -230,6 +230,15 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: translation_key="bad_psu", ) + async_create_issue( + hass, + DOMAIN, + "cold_tea", + is_fixable=True, + severity=IssueSeverity.WARNING, + translation_key="cold_tea", + ) + return True diff --git a/homeassistant/components/demo/repairs.py b/homeassistant/components/demo/repairs.py index cddc937a71a..1ea00374457 100644 --- a/homeassistant/components/demo/repairs.py +++ b/homeassistant/components/demo/repairs.py @@ -29,6 +29,16 @@ class DemoFixFlow(RepairsFlow): return self.async_show_form(step_id="confirm", data_schema=vol.Schema({})) +class DemoColdTeaFixFlow(RepairsFlow): + """Handler for cold tea.""" + + async def async_step_init( + self, user_input: dict[str, str] | None = None + ) -> data_entry_flow.FlowResult: + """Handle the first step of a fix flow.""" + return self.async_abort(reason="not_tea_time") + + async def async_create_fix_flow( hass: HomeAssistant, issue_id: str, @@ -39,5 +49,9 @@ async def async_create_fix_flow( # The bad_psu issue doesn't have its own flow return ConfirmRepairFlow() + if issue_id == "cold_tea": + # The cold_tea issue have it's own flow + return DemoColdTeaFixFlow() + # Other issues have a custom flow return DemoFixFlow() diff --git a/homeassistant/components/demo/strings.json b/homeassistant/components/demo/strings.json index e02d64f157f..7be1a133a74 100644 --- a/homeassistant/components/demo/strings.json +++ b/homeassistant/components/demo/strings.json @@ -23,6 +23,15 @@ } } }, + "cold_tea": { + "title": "The tea is cold", + "fix_flow": { + "step": {}, + "abort": { + "not_tea_time": "Can not re-heat the tea at this time" + } + } + }, "transmogrifier_deprecated": { "title": "The transmogrifier component is deprecated", "description": "The transmogrifier component is now deprecated due to the lack of local control available in the new API" diff --git a/homeassistant/components/demo/translations/en.json b/homeassistant/components/demo/translations/en.json index a98f0d3c28d..150ee6c8830 100644 --- a/homeassistant/components/demo/translations/en.json +++ b/homeassistant/components/demo/translations/en.json @@ -11,6 +11,15 @@ }, "title": "The power supply is not stable" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "Can not re-heat the tea at this time" + }, + "step": {} + }, + "title": "The tea is cold" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { @@ -33,6 +42,9 @@ }, "options": { "step": { + "init": { + "data": {} + }, "options_1": { "data": { "bool": "Optional boolean", diff --git a/tests/components/demo/test_init.py b/tests/components/demo/test_init.py index 1dd049f8ab4..7ba339a71d2 100644 --- a/tests/components/demo/test_init.py +++ b/tests/components/demo/test_init.py @@ -192,6 +192,20 @@ async def test_issues_created(mock_history, hass, hass_client, hass_ws_client): "translation_key": "bad_psu", "translation_placeholders": None, }, + { + "breaks_in_ha_version": None, + "created": ANY, + "dismissed_version": None, + "domain": "demo", + "is_fixable": True, + "issue_domain": None, + "issue_id": "cold_tea", + "learn_more_url": None, + "severity": "warning", + "translation_key": "cold_tea", + "translation_placeholders": None, + "ignored": False, + }, ] } @@ -280,5 +294,19 @@ async def test_issues_created(mock_history, hass, hass_client, hass_ws_client): "translation_key": "bad_psu", "translation_placeholders": None, }, + { + "breaks_in_ha_version": None, + "created": ANY, + "dismissed_version": None, + "domain": "demo", + "is_fixable": True, + "issue_domain": None, + "issue_id": "cold_tea", + "learn_more_url": None, + "severity": "warning", + "translation_key": "cold_tea", + "translation_placeholders": None, + "ignored": False, + }, ] } From e386bab682019c0c69691221478e3cbc37f6c249 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 24 Nov 2022 15:30:21 +0100 Subject: [PATCH 0676/1033] Add type hint to template wrapper (#82563) * Add type to template hassfunction decorator * Adjust to use EvalContext * Use runtime.Context * Use TypeVar for context * Use jinja2.runtime.Context * Reverse declarations * Use Any * Update homeassistant/helpers/template.py Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> --- homeassistant/helpers/template.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 2b191d7537a..8d284f042c2 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -29,6 +29,7 @@ import jinja2 from jinja2 import pass_context, pass_environment, pass_eval_context from jinja2.sandbox import ImmutableSandboxedEnvironment from jinja2.utils import Namespace +from typing_extensions import Concatenate, ParamSpec import voluptuous as vol from homeassistant.const import ( @@ -96,6 +97,8 @@ _COLLECTABLE_STATE_ATTRIBUTES = { } _T = TypeVar("_T") +_R = TypeVar("_R") +_P = ParamSpec("_P") ALL_STATES_RATE_LIMIT = timedelta(minutes=1) DOMAIN_STATES_RATE_LIMIT = timedelta(seconds=1) @@ -2079,12 +2082,14 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment): # evaluated fresh with every execution, rather than executed # at compile time and the value stored. The context itself # can be discarded, we only need to get at the hass object. - def hassfunction(func): + def hassfunction( + func: Callable[Concatenate[HomeAssistant, _P], _R], + ) -> Callable[Concatenate[Any, _P], _R]: """Wrap function that depend on hass.""" @wraps(func) - def wrapper(*args, **kwargs): - return func(hass, *args[1:], **kwargs) + def wrapper(_: Any, *args: _P.args, **kwargs: _P.kwargs) -> _R: + return func(hass, *args, **kwargs) return pass_context(wrapper) From bb517c269eb619d79aac7942f725e88280b335ba Mon Sep 17 00:00:00 2001 From: Rogelio Orts Date: Thu, 24 Nov 2022 16:28:43 +0100 Subject: [PATCH 0677/1033] Add unknown tuya modes as presets (#82056) Fixes https://github.com/home-assistant/core/issues/81681 fixes undefined --- homeassistant/components/tuya/climate.py | 48 ++++++++++++++++-------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/tuya/climate.py b/homeassistant/components/tuya/climate.py index 1b393dd5872..20e36028dba 100644 --- a/homeassistant/components/tuya/climate.py +++ b/homeassistant/components/tuya/climate.py @@ -25,7 +25,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import HomeAssistantTuyaData from .base import IntegerTypeData, TuyaEntity -from .const import DOMAIN, LOGGER, TUYA_DISCOVERY_NEW, DPCode, DPType +from .const import DOMAIN, TUYA_DISCOVERY_NEW, DPCode, DPType TUYA_HVAC_TO_HA = { "auto": HVACMode.HEAT_COOL, @@ -205,10 +205,19 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity): DPCode.MODE, dptype=DPType.ENUM, prefer_function=True ): self._attr_hvac_modes = [HVACMode.OFF] - for tuya_mode, ha_mode in TUYA_HVAC_TO_HA.items(): - if tuya_mode in enum_type.range: + unknown_hvac_modes: list[str] = [] + for tuya_mode in enum_type.range: + if tuya_mode in TUYA_HVAC_TO_HA: + ha_mode = TUYA_HVAC_TO_HA[tuya_mode] self._hvac_to_tuya[ha_mode] = tuya_mode self._attr_hvac_modes.append(ha_mode) + else: + unknown_hvac_modes.append(tuya_mode) + + if unknown_hvac_modes: # Tuya modes are presets instead of hvac_modes + self._attr_hvac_modes.append(description.switch_only_hvac_mode) + self._attr_preset_modes = unknown_hvac_modes + self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE elif self.find_dpcode(DPCode.SWITCH, prefer_function=True): self._attr_hvac_modes = [ HVACMode.OFF, @@ -263,18 +272,6 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity): """Call when entity is added to hass.""" await super().async_added_to_hass() - # Log unknown modes - if enum_type := self.find_dpcode( - DPCode.MODE, dptype=DPType.ENUM, prefer_function=True - ): - for tuya_mode in enum_type.range: - if tuya_mode not in TUYA_HVAC_TO_HA: - LOGGER.warning( - "Unknown HVAC mode '%s' for device %s; assuming it as off", - tuya_mode, - self.device.name, - ) - def set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set new target hvac mode.""" commands = [{"code": DPCode.SWITCH, "value": hvac_mode != HVACMode.OFF}] @@ -284,6 +281,11 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity): ) self._send_command(commands) + def set_preset_mode(self, preset_mode): + """Set new target preset mode.""" + commands = [{"code": DPCode.MODE, "value": preset_mode}] + self._send_command(commands) + def set_fan_mode(self, fan_mode: str) -> None: """Set new target fan mode.""" self._send_command([{"code": DPCode.FAN_SPEED_ENUM, "value": fan_mode}]) @@ -420,8 +422,24 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity): ) is not None and mode in TUYA_HVAC_TO_HA: return TUYA_HVAC_TO_HA[mode] + # If the switch is on, and the mode does not match any hvac mode. + if self.device.status.get(DPCode.SWITCH, False): + return self.entity_description.switch_only_hvac_mode + return HVACMode.OFF + @property + def preset_mode(self) -> str | None: + """Return preset mode.""" + if DPCode.MODE not in self.device.function: + return None + + mode = self.device.status.get(DPCode.MODE) + if mode in TUYA_HVAC_TO_HA: + return None + + return mode + @property def fan_mode(self) -> str | None: """Return fan mode.""" From bba119affab28789215b4e09a89c1c2f162d0e31 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Thu, 24 Nov 2022 08:01:20 -0800 Subject: [PATCH 0678/1033] Bump gcal_sync to 4.0.3 (#82606) fixes undefined --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index 7de3a735b96..bc6c719c8fd 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==4.0.2", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==4.0.3", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index d715e53c1c8..396e0a9206e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -738,7 +738,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==4.0.2 +gcal-sync==4.0.3 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fc73125f821..a66569a76bc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -554,7 +554,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==4.0.2 +gcal-sync==4.0.3 # homeassistant.components.geocaching geocachingapi==0.2.1 From 8577310e6d88f3564586d0cd2dd1f3ba542c7a05 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 24 Nov 2022 17:30:01 +0100 Subject: [PATCH 0679/1033] Fix show_menu support in SchemaFlowFormStep (#82634) * Fix show_menu support in SchemaFlowFormStep * Add test * Fix test --- .../helpers/schema_config_entry_flow.py | 17 +++-- .../helpers/test_schema_config_entry_flow.py | 67 +++++++++++++++++++ 2 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 tests/helpers/test_schema_config_entry_flow.py diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 39fa5164f62..c1936b4bd1e 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -151,14 +151,19 @@ class SchemaCommonFlowHandler: user_input: dict[str, Any] | None = None, ) -> FlowResult: """Show form for next step.""" - form_step: SchemaFlowFormStep = cast( - SchemaFlowFormStep, self._flow[next_step_id] - ) - options = dict(self._options) if user_input: options.update(user_input) + if isinstance(self._flow[next_step_id], SchemaFlowMenuStep): + menu_step = cast(SchemaFlowMenuStep, self._flow[next_step_id]) + return self._handler.async_show_menu( + step_id=next_step_id, + menu_options=menu_step.options, + ) + + form_step = cast(SchemaFlowFormStep, self._flow[next_step_id]) + if ( data_schema := self._get_schema(form_step, self._options) ) and data_schema.schema: @@ -197,10 +202,10 @@ class SchemaCommonFlowHandler: self, step_id: str, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle a menu step.""" - form_step: SchemaFlowMenuStep = cast(SchemaFlowMenuStep, self._flow[step_id]) + menu_step: SchemaFlowMenuStep = cast(SchemaFlowMenuStep, self._flow[step_id]) return self._handler.async_show_menu( step_id=step_id, - menu_options=form_step.options, + menu_options=menu_step.options, ) diff --git a/tests/helpers/test_schema_config_entry_flow.py b/tests/helpers/test_schema_config_entry_flow.py new file mode 100644 index 00000000000..e54582db606 --- /dev/null +++ b/tests/helpers/test_schema_config_entry_flow.py @@ -0,0 +1,67 @@ +"""Tests for the schema based data entry flows.""" +from __future__ import annotations + +from unittest.mock import patch + +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaConfigFlowHandler, + SchemaFlowFormStep, + SchemaFlowMenuStep, +) + +from tests.common import mock_platform + +TEST_DOMAIN = "test" + + +async def test_menu_step(hass: HomeAssistant) -> None: + """Test menu step.""" + + MENU_1 = ["option1", "option2"] + MENU_2 = ["option3", "option4"] + + CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { + "user": SchemaFlowMenuStep(MENU_1), + "option1": SchemaFlowFormStep(vol.Schema({}), next_step=lambda _: "menu2"), + "menu2": SchemaFlowMenuStep(MENU_2), + "option3": SchemaFlowFormStep(vol.Schema({})), + } + + class TestConfigFlow(SchemaConfigFlowHandler, domain=TEST_DOMAIN): + """Handle a config or options flow for Derivative.""" + + config_flow = CONFIG_FLOW + + mock_platform(hass, f"{TEST_DOMAIN}.config_flow") + with patch.dict(config_entries.HANDLERS, {TEST_DOMAIN: TestConfigFlow}): + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": "user"} + ) + assert result["type"] == FlowResultType.MENU + assert result["step_id"] == "user" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"next_step_id": "option1"}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "option1" + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + assert result["type"] == FlowResultType.MENU + assert result["step_id"] == "menu2" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"next_step_id": "option3"}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "option3" + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + assert result["type"] == FlowResultType.CREATE_ENTRY From 285aff154d4a86a5f0d7eaa91f3ea7bd5ea6dccd Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 24 Nov 2022 18:33:13 +0100 Subject: [PATCH 0680/1033] Remove Google Chat/Hangouts integration (#82645) --- .coveragerc | 5 - homeassistant/brands/google.json | 1 - homeassistant/components/hangouts/__init__.py | 160 -------- .../components/hangouts/config_flow.py | 111 ------ homeassistant/components/hangouts/const.py | 80 ---- .../components/hangouts/hangouts_bot.py | 361 ------------------ .../components/hangouts/hangups_utils.py | 96 ----- homeassistant/components/hangouts/intents.py | 31 -- .../components/hangouts/manifest.json | 10 - homeassistant/components/hangouts/notify.py | 57 --- .../components/hangouts/services.yaml | 32 -- .../components/hangouts/strings.json | 29 -- .../components/hangouts/translations/bg.json | 29 -- .../components/hangouts/translations/ca.json | 31 -- .../components/hangouts/translations/cs.json | 31 -- .../components/hangouts/translations/da.json | 29 -- .../components/hangouts/translations/de.json | 31 -- .../components/hangouts/translations/el.json | 31 -- .../components/hangouts/translations/en.json | 29 -- .../hangouts/translations/es-419.json | 29 -- .../components/hangouts/translations/es.json | 29 -- .../components/hangouts/translations/et.json | 31 -- .../components/hangouts/translations/fi.json | 29 -- .../components/hangouts/translations/fr.json | 31 -- .../components/hangouts/translations/he.json | 28 -- .../components/hangouts/translations/hr.json | 12 - .../components/hangouts/translations/hu.json | 31 -- .../components/hangouts/translations/id.json | 31 -- .../components/hangouts/translations/it.json | 31 -- .../components/hangouts/translations/ja.json | 31 -- .../components/hangouts/translations/ko.json | 31 -- .../components/hangouts/translations/lb.json | 31 -- .../components/hangouts/translations/lt.json | 12 - .../components/hangouts/translations/nb.json | 7 - .../components/hangouts/translations/nl.json | 31 -- .../components/hangouts/translations/nn.json | 28 -- .../components/hangouts/translations/no.json | 31 -- .../components/hangouts/translations/pl.json | 31 -- .../hangouts/translations/pt-BR.json | 31 -- .../components/hangouts/translations/pt.json | 30 -- .../components/hangouts/translations/ro.json | 27 -- .../components/hangouts/translations/ru.json | 31 -- .../components/hangouts/translations/sk.json | 12 - .../components/hangouts/translations/sl.json | 31 -- .../components/hangouts/translations/sv.json | 31 -- .../components/hangouts/translations/th.json | 16 - .../components/hangouts/translations/tr.json | 31 -- .../components/hangouts/translations/uk.json | 31 -- .../components/hangouts/translations/vi.json | 7 - .../hangouts/translations/zh-Hans.json | 30 -- .../hangouts/translations/zh-Hant.json | 31 -- homeassistant/generated/config_flows.py | 1 - homeassistant/generated/integrations.json | 6 - requirements_all.txt | 3 - requirements_test_all.txt | 3 - tests/components/hangouts/__init__.py | 1 - tests/components/hangouts/test_config_flow.py | 132 ------- 57 files changed, 2184 deletions(-) delete mode 100644 homeassistant/components/hangouts/__init__.py delete mode 100644 homeassistant/components/hangouts/config_flow.py delete mode 100644 homeassistant/components/hangouts/const.py delete mode 100644 homeassistant/components/hangouts/hangouts_bot.py delete mode 100644 homeassistant/components/hangouts/hangups_utils.py delete mode 100644 homeassistant/components/hangouts/intents.py delete mode 100644 homeassistant/components/hangouts/manifest.json delete mode 100644 homeassistant/components/hangouts/notify.py delete mode 100644 homeassistant/components/hangouts/services.yaml delete mode 100644 homeassistant/components/hangouts/strings.json delete mode 100644 homeassistant/components/hangouts/translations/bg.json delete mode 100644 homeassistant/components/hangouts/translations/ca.json delete mode 100644 homeassistant/components/hangouts/translations/cs.json delete mode 100644 homeassistant/components/hangouts/translations/da.json delete mode 100644 homeassistant/components/hangouts/translations/de.json delete mode 100644 homeassistant/components/hangouts/translations/el.json delete mode 100644 homeassistant/components/hangouts/translations/en.json delete mode 100644 homeassistant/components/hangouts/translations/es-419.json delete mode 100644 homeassistant/components/hangouts/translations/es.json delete mode 100644 homeassistant/components/hangouts/translations/et.json delete mode 100644 homeassistant/components/hangouts/translations/fi.json delete mode 100644 homeassistant/components/hangouts/translations/fr.json delete mode 100644 homeassistant/components/hangouts/translations/he.json delete mode 100644 homeassistant/components/hangouts/translations/hr.json delete mode 100644 homeassistant/components/hangouts/translations/hu.json delete mode 100644 homeassistant/components/hangouts/translations/id.json delete mode 100644 homeassistant/components/hangouts/translations/it.json delete mode 100644 homeassistant/components/hangouts/translations/ja.json delete mode 100644 homeassistant/components/hangouts/translations/ko.json delete mode 100644 homeassistant/components/hangouts/translations/lb.json delete mode 100644 homeassistant/components/hangouts/translations/lt.json delete mode 100644 homeassistant/components/hangouts/translations/nb.json delete mode 100644 homeassistant/components/hangouts/translations/nl.json delete mode 100644 homeassistant/components/hangouts/translations/nn.json delete mode 100644 homeassistant/components/hangouts/translations/no.json delete mode 100644 homeassistant/components/hangouts/translations/pl.json delete mode 100644 homeassistant/components/hangouts/translations/pt-BR.json delete mode 100644 homeassistant/components/hangouts/translations/pt.json delete mode 100644 homeassistant/components/hangouts/translations/ro.json delete mode 100644 homeassistant/components/hangouts/translations/ru.json delete mode 100644 homeassistant/components/hangouts/translations/sk.json delete mode 100644 homeassistant/components/hangouts/translations/sl.json delete mode 100644 homeassistant/components/hangouts/translations/sv.json delete mode 100644 homeassistant/components/hangouts/translations/th.json delete mode 100644 homeassistant/components/hangouts/translations/tr.json delete mode 100644 homeassistant/components/hangouts/translations/uk.json delete mode 100644 homeassistant/components/hangouts/translations/vi.json delete mode 100644 homeassistant/components/hangouts/translations/zh-Hans.json delete mode 100644 homeassistant/components/hangouts/translations/zh-Hant.json delete mode 100644 tests/components/hangouts/__init__.py delete mode 100644 tests/components/hangouts/test_config_flow.py diff --git a/.coveragerc b/.coveragerc index bf493a6a0a8..511c57a2ce9 100644 --- a/.coveragerc +++ b/.coveragerc @@ -483,11 +483,6 @@ omit = homeassistant/components/habitica/__init__.py homeassistant/components/habitica/const.py homeassistant/components/habitica/sensor.py - homeassistant/components/hangouts/__init__.py - homeassistant/components/hangouts/hangouts_bot.py - homeassistant/components/hangouts/hangups_utils.py - homeassistant/components/hangouts/intents.py - homeassistant/components/hangouts/notify.py homeassistant/components/harman_kardon_avr/media_player.py homeassistant/components/harmony/const.py homeassistant/components/harmony/data.py diff --git a/homeassistant/brands/google.json b/homeassistant/brands/google.json index 5f37de46180..de27fa7c515 100644 --- a/homeassistant/brands/google.json +++ b/homeassistant/brands/google.json @@ -14,7 +14,6 @@ "google", "nest", "cast", - "hangouts", "dialogflow" ] } diff --git a/homeassistant/components/hangouts/__init__.py b/homeassistant/components/hangouts/__init__.py deleted file mode 100644 index 6b0e10705c0..00000000000 --- a/homeassistant/components/hangouts/__init__.py +++ /dev/null @@ -1,160 +0,0 @@ -"""Support for Hangouts.""" -import logging - -from hangups.auth import GoogleAuthError -import voluptuous as vol - -from homeassistant import config_entries -from homeassistant.components.conversation.util import create_matcher -from homeassistant.config_entries import ConfigEntry -from homeassistant.const import EVENT_HOMEASSISTANT_STOP -from homeassistant.core import HomeAssistant -from homeassistant.helpers import dispatcher, intent -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.typing import ConfigType - -# We need an import from .config_flow, without it .config_flow is never loaded. -from .config_flow import HangoutsFlowHandler # noqa: F401 -from .const import ( - CONF_BOT, - CONF_DEFAULT_CONVERSATIONS, - CONF_ERROR_SUPPRESSED_CONVERSATIONS, - CONF_INTENTS, - CONF_MATCHERS, - CONF_REFRESH_TOKEN, - CONF_SENTENCES, - DOMAIN, - EVENT_HANGOUTS_CONNECTED, - EVENT_HANGOUTS_CONVERSATIONS_CHANGED, - EVENT_HANGOUTS_CONVERSATIONS_RESOLVED, - INTENT_HELP, - INTENT_SCHEMA, - MESSAGE_SCHEMA, - SERVICE_RECONNECT, - SERVICE_SEND_MESSAGE, - SERVICE_UPDATE, - TARGETS_SCHEMA, -) -from .hangouts_bot import HangoutsBot -from .intents import HelpIntent - -_LOGGER = logging.getLogger(__name__) - -CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Optional(CONF_INTENTS, default={}): vol.Schema( - {cv.string: INTENT_SCHEMA} - ), - vol.Optional(CONF_DEFAULT_CONVERSATIONS, default=[]): [TARGETS_SCHEMA], - vol.Optional(CONF_ERROR_SUPPRESSED_CONVERSATIONS, default=[]): [ - TARGETS_SCHEMA - ], - } - ) - }, - extra=vol.ALLOW_EXTRA, -) - - -async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up the Hangouts bot component.""" - if (conf := config.get(DOMAIN)) is None: - hass.data[DOMAIN] = { - CONF_INTENTS: {}, - CONF_DEFAULT_CONVERSATIONS: [], - CONF_ERROR_SUPPRESSED_CONVERSATIONS: [], - } - return True - - hass.data[DOMAIN] = { - CONF_INTENTS: conf[CONF_INTENTS], - CONF_DEFAULT_CONVERSATIONS: conf[CONF_DEFAULT_CONVERSATIONS], - CONF_ERROR_SUPPRESSED_CONVERSATIONS: conf[CONF_ERROR_SUPPRESSED_CONVERSATIONS], - } - - if ( - hass.data[DOMAIN][CONF_INTENTS] - and INTENT_HELP not in hass.data[DOMAIN][CONF_INTENTS] - ): - hass.data[DOMAIN][CONF_INTENTS][INTENT_HELP] = {CONF_SENTENCES: ["HELP"]} - - for data in hass.data[DOMAIN][CONF_INTENTS].values(): - matchers = [] - for sentence in data[CONF_SENTENCES]: - matchers.append(create_matcher(sentence)) - - data[CONF_MATCHERS] = matchers - - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_IMPORT} - ) - ) - - return True - - -async def async_setup_entry(hass: HomeAssistant, config: ConfigEntry) -> bool: - """Set up a config entry.""" - try: - bot = HangoutsBot( - hass, - config.data.get(CONF_REFRESH_TOKEN), - hass.data[DOMAIN][CONF_INTENTS], - hass.data[DOMAIN][CONF_DEFAULT_CONVERSATIONS], - hass.data[DOMAIN][CONF_ERROR_SUPPRESSED_CONVERSATIONS], - ) - hass.data[DOMAIN][CONF_BOT] = bot - except GoogleAuthError as exception: - _LOGGER.error("Hangouts failed to log in: %s", str(exception)) - return False - - dispatcher.async_dispatcher_connect( - hass, EVENT_HANGOUTS_CONNECTED, bot.async_handle_update_users_and_conversations - ) - - dispatcher.async_dispatcher_connect( - hass, EVENT_HANGOUTS_CONVERSATIONS_CHANGED, bot.async_resolve_conversations - ) - - dispatcher.async_dispatcher_connect( - hass, - EVENT_HANGOUTS_CONVERSATIONS_RESOLVED, - bot.async_update_conversation_commands, - ) - - config.async_on_unload( - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, bot.async_handle_hass_stop) - ) - - await bot.async_connect() - - hass.services.async_register( - DOMAIN, - SERVICE_SEND_MESSAGE, - bot.async_handle_send_message, - schema=MESSAGE_SCHEMA, - ) - hass.services.async_register( - DOMAIN, - SERVICE_UPDATE, - bot.async_handle_update_users_and_conversations, - schema=vol.Schema({}), - ) - - hass.services.async_register( - DOMAIN, SERVICE_RECONNECT, bot.async_handle_reconnect, schema=vol.Schema({}) - ) - - intent.async_register(hass, HelpIntent(hass)) - - return True - - -async def async_unload_entry(hass: HomeAssistant, _: ConfigEntry) -> bool: - """Unload a config entry.""" - bot = hass.data[DOMAIN].pop(CONF_BOT) - await bot.async_disconnect() - return True diff --git a/homeassistant/components/hangouts/config_flow.py b/homeassistant/components/hangouts/config_flow.py deleted file mode 100644 index 598c7fbd9cb..00000000000 --- a/homeassistant/components/hangouts/config_flow.py +++ /dev/null @@ -1,111 +0,0 @@ -"""Config flow to configure Google Hangouts.""" -import functools - -from hangups import get_auth -import voluptuous as vol - -from homeassistant import config_entries -from homeassistant.const import CONF_EMAIL, CONF_PASSWORD - -from .const import CONF_2FA, CONF_AUTH_CODE, CONF_REFRESH_TOKEN, DOMAIN -from .hangups_utils import ( - Google2FAError, - GoogleAuthError, - HangoutsCredentials, - HangoutsRefreshToken, -) - - -class HangoutsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): - """Config flow Google Hangouts.""" - - VERSION = 1 - - def __init__(self): - """Initialize Google Hangouts config flow.""" - self._credentials = None - self._refresh_token = None - - async def async_step_user(self, user_input=None): - """Handle a flow start.""" - errors = {} - - self._async_abort_entries_match() - - if user_input is not None: - user_email = user_input[CONF_EMAIL] - user_password = user_input[CONF_PASSWORD] - user_auth_code = user_input.get(CONF_AUTH_CODE) - manual_login = user_auth_code is not None - - user_pin = None - self._credentials = HangoutsCredentials( - user_email, user_password, user_pin, user_auth_code - ) - self._refresh_token = HangoutsRefreshToken(None) - try: - await self.hass.async_add_executor_job( - functools.partial( - get_auth, - self._credentials, - self._refresh_token, - manual_login=manual_login, - ) - ) - - return await self.async_step_final() - except GoogleAuthError as err: - if isinstance(err, Google2FAError): - return await self.async_step_2fa() - msg = str(err) - if msg == "Unknown verification code input": - errors["base"] = "invalid_2fa_method" - else: - errors["base"] = "invalid_login" - - return self.async_show_form( - step_id="user", - data_schema=vol.Schema( - { - vol.Required(CONF_EMAIL): str, - vol.Required(CONF_PASSWORD): str, - vol.Optional(CONF_AUTH_CODE): str, - } - ), - errors=errors, - ) - - async def async_step_2fa(self, user_input=None): - """Handle the 2fa step, if needed.""" - errors = {} - - if user_input is not None: - self._credentials.set_verification_code(user_input[CONF_2FA]) - try: - await self.hass.async_add_executor_job( - get_auth, self._credentials, self._refresh_token - ) - - return await self.async_step_final() - except GoogleAuthError: - errors["base"] = "invalid_2fa" - - return self.async_show_form( - step_id=CONF_2FA, - data_schema=vol.Schema({vol.Required(CONF_2FA): str}), - errors=errors, - ) - - async def async_step_final(self): - """Handle the final step, create the config entry.""" - return self.async_create_entry( - title=self._credentials.get_email(), - data={ - CONF_EMAIL: self._credentials.get_email(), - CONF_REFRESH_TOKEN: self._refresh_token.get(), - }, - ) - - async def async_step_import(self, _): - """Handle a flow import.""" - return await self.async_step_user() diff --git a/homeassistant/components/hangouts/const.py b/homeassistant/components/hangouts/const.py deleted file mode 100644 index 3a78e9bbe80..00000000000 --- a/homeassistant/components/hangouts/const.py +++ /dev/null @@ -1,80 +0,0 @@ -"""Constants for Google Hangouts Component.""" -import voluptuous as vol - -from homeassistant.components.notify import ATTR_DATA, ATTR_MESSAGE, ATTR_TARGET -import homeassistant.helpers.config_validation as cv - -DOMAIN = "hangouts" - -CONF_2FA = "2fa" -CONF_AUTH_CODE = "authorization_code" -CONF_REFRESH_TOKEN = "refresh_token" -CONF_BOT = "bot" - -CONF_CONVERSATIONS = "conversations" -CONF_DEFAULT_CONVERSATIONS = "default_conversations" -CONF_ERROR_SUPPRESSED_CONVERSATIONS = "error_suppressed_conversations" - -CONF_INTENTS = "intents" -CONF_INTENT_TYPE = "intent_type" -CONF_SENTENCES = "sentences" -CONF_MATCHERS = "matchers" - -INTENT_HELP = "HangoutsHelp" - -EVENT_HANGOUTS_CONNECTED = "hangouts_connected" -EVENT_HANGOUTS_DISCONNECTED = "hangouts_disconnected" -EVENT_HANGOUTS_USERS_CHANGED = "hangouts_users_changed" -EVENT_HANGOUTS_CONVERSATIONS_CHANGED = "hangouts_conversations_changed" -EVENT_HANGOUTS_CONVERSATIONS_RESOLVED = "hangouts_conversations_resolved" -EVENT_HANGOUTS_MESSAGE_RECEIVED = "hangouts_message_received" - -CONF_CONVERSATION_ID = "id" -CONF_CONVERSATION_NAME = "name" - -SERVICE_SEND_MESSAGE = "send_message" -SERVICE_UPDATE = "update" -SERVICE_RECONNECT = "reconnect" - - -TARGETS_SCHEMA = vol.All( - vol.Schema( - { - vol.Exclusive(CONF_CONVERSATION_ID, "id or name"): cv.string, - vol.Exclusive(CONF_CONVERSATION_NAME, "id or name"): cv.string, - } - ), - cv.has_at_least_one_key(CONF_CONVERSATION_ID, CONF_CONVERSATION_NAME), -) -MESSAGE_SEGMENT_SCHEMA = vol.Schema( - { - vol.Required("text"): cv.string, - vol.Optional("is_bold"): cv.boolean, - vol.Optional("is_italic"): cv.boolean, - vol.Optional("is_strikethrough"): cv.boolean, - vol.Optional("is_underline"): cv.boolean, - vol.Optional("parse_str"): cv.boolean, - vol.Optional("link_target"): cv.string, - } -) -MESSAGE_DATA_SCHEMA = vol.Schema( - {vol.Optional("image_file"): cv.string, vol.Optional("image_url"): cv.string} -) - -MESSAGE_SCHEMA = vol.Schema( - { - vol.Required(ATTR_TARGET): [TARGETS_SCHEMA], - vol.Required(ATTR_MESSAGE): [MESSAGE_SEGMENT_SCHEMA], - vol.Optional(ATTR_DATA): MESSAGE_DATA_SCHEMA, - } -) - -INTENT_SCHEMA = vol.All( - # Basic Schema - vol.Schema( - { - vol.Required(CONF_SENTENCES): vol.All(cv.ensure_list, [cv.string]), - vol.Optional(CONF_CONVERSATIONS): [TARGETS_SCHEMA], - } - ) -) diff --git a/homeassistant/components/hangouts/hangouts_bot.py b/homeassistant/components/hangouts/hangouts_bot.py deleted file mode 100644 index c3c363ef55e..00000000000 --- a/homeassistant/components/hangouts/hangouts_bot.py +++ /dev/null @@ -1,361 +0,0 @@ -"""The Hangouts Bot.""" -from __future__ import annotations - -import asyncio -from contextlib import suppress -from http import HTTPStatus -import io -import logging - -import aiohttp -import hangups -from hangups import ChatMessageEvent, ChatMessageSegment, Client, get_auth, hangouts_pb2 - -from homeassistant.core import ServiceCall, callback -from homeassistant.helpers import dispatcher, intent -from homeassistant.helpers.aiohttp_client import async_get_clientsession - -from .const import ( - ATTR_DATA, - ATTR_MESSAGE, - ATTR_TARGET, - CONF_CONVERSATION_ID, - CONF_CONVERSATION_NAME, - CONF_CONVERSATIONS, - CONF_MATCHERS, - DOMAIN, - EVENT_HANGOUTS_CONNECTED, - EVENT_HANGOUTS_CONVERSATIONS_CHANGED, - EVENT_HANGOUTS_CONVERSATIONS_RESOLVED, - EVENT_HANGOUTS_DISCONNECTED, - EVENT_HANGOUTS_MESSAGE_RECEIVED, - INTENT_HELP, -) -from .hangups_utils import HangoutsCredentials, HangoutsRefreshToken - -_LOGGER = logging.getLogger(__name__) - - -class HangoutsBot: - """The Hangouts Bot.""" - - def __init__( - self, hass, refresh_token, intents, default_convs, error_suppressed_convs - ): - """Set up the client.""" - self.hass = hass - self._connected = False - - self._refresh_token = refresh_token - - self._intents = intents - self._conversation_intents = None - - self._client = None - self._user_list = None - self._conversation_list = None - self._default_convs = default_convs - self._default_conv_ids = None - self._error_suppressed_convs = error_suppressed_convs - self._error_suppressed_conv_ids = None - - dispatcher.async_dispatcher_connect( - self.hass, - EVENT_HANGOUTS_MESSAGE_RECEIVED, - self._async_handle_conversation_message, - ) - - def _resolve_conversation_id(self, obj): - if CONF_CONVERSATION_ID in obj: - return obj[CONF_CONVERSATION_ID] - if CONF_CONVERSATION_NAME in obj: - conv = self._resolve_conversation_name(obj[CONF_CONVERSATION_NAME]) - if conv is not None: - return conv.id_ - return None - - def _resolve_conversation_name(self, name): - for conv in self._conversation_list.get_all(): - if conv.name == name: - return conv - return None - - @callback - def async_update_conversation_commands(self): - """Refresh the commands for every conversation.""" - self._conversation_intents = {} - - for intent_type, data in self._intents.items(): - if data.get(CONF_CONVERSATIONS): - conversations = [] - for conversation in data.get(CONF_CONVERSATIONS): - conv_id = self._resolve_conversation_id(conversation) - if conv_id is not None: - conversations.append(conv_id) - data[f"_{CONF_CONVERSATIONS}"] = conversations - elif self._default_conv_ids: - data[f"_{CONF_CONVERSATIONS}"] = self._default_conv_ids - else: - data[f"_{CONF_CONVERSATIONS}"] = [ - conv.id_ for conv in self._conversation_list.get_all() - ] - - for conv_id in data[f"_{CONF_CONVERSATIONS}"]: - if conv_id not in self._conversation_intents: - self._conversation_intents[conv_id] = {} - - self._conversation_intents[conv_id][intent_type] = data - - with suppress(ValueError): - self._conversation_list.on_event.remove_observer( - self._async_handle_conversation_event - ) - self._conversation_list.on_event.add_observer( - self._async_handle_conversation_event - ) - - @callback - def async_resolve_conversations(self, _): - """Resolve the list of default and error suppressed conversations.""" - self._default_conv_ids = [] - self._error_suppressed_conv_ids = [] - - for conversation in self._default_convs: - conv_id = self._resolve_conversation_id(conversation) - if conv_id is not None: - self._default_conv_ids.append(conv_id) - - for conversation in self._error_suppressed_convs: - conv_id = self._resolve_conversation_id(conversation) - if conv_id is not None: - self._error_suppressed_conv_ids.append(conv_id) - dispatcher.async_dispatcher_send( - self.hass, EVENT_HANGOUTS_CONVERSATIONS_RESOLVED - ) - - async def _async_handle_conversation_event(self, event): - if isinstance(event, ChatMessageEvent): - dispatcher.async_dispatcher_send( - self.hass, - EVENT_HANGOUTS_MESSAGE_RECEIVED, - event.conversation_id, - event.user_id, - event, - ) - - async def _async_handle_conversation_message(self, conv_id, user_id, event): - """Handle a message sent to a conversation.""" - user = self._user_list.get_user(user_id) - if user.is_self: - return - message = event.text - - _LOGGER.debug("Handling message '%s' from %s", message, user.full_name) - - intents = self._conversation_intents.get(conv_id) - if intents is not None: - is_error = False - try: - intent_result = await self._async_process(intents, message, conv_id) - except (intent.UnknownIntent, intent.IntentHandleError) as err: - is_error = True - intent_result = intent.IntentResponse() - intent_result.async_set_speech(str(err)) - - if intent_result is None: - is_error = True - intent_result = intent.IntentResponse() - intent_result.async_set_speech("Sorry, I didn't understand that") - - message = ( - intent_result.as_dict().get("speech", {}).get("plain", {}).get("speech") - ) - - if (message is not None) and not ( - is_error and conv_id in self._error_suppressed_conv_ids - ): - await self._async_send_message( - [{"text": message, "parse_str": True}], - [{CONF_CONVERSATION_ID: conv_id}], - None, - ) - - async def _async_process(self, intents, text, conv_id): - """Detect a matching intent.""" - for intent_type, data in intents.items(): - for matcher in data.get(CONF_MATCHERS, []): - if not (match := matcher.match(text)): - continue - if intent_type == INTENT_HELP: - return await intent.async_handle( - self.hass, - DOMAIN, - intent_type, - {"conv_id": {"value": conv_id}}, - text, - ) - - return await intent.async_handle( - self.hass, - DOMAIN, - intent_type, - {"conv_id": {"value": conv_id}} - | { - key: {"value": value} - for key, value in match.groupdict().items() - }, - text, - ) - - async def async_connect(self): - """Login to the Google Hangouts.""" - session = await self.hass.async_add_executor_job( - get_auth, - HangoutsCredentials(None, None, None), - HangoutsRefreshToken(self._refresh_token), - ) - - self._client = Client(session) - self._client.on_connect.add_observer(self._on_connect) - self._client.on_disconnect.add_observer(self._on_disconnect) - - self.hass.loop.create_task(self._client.connect()) - - def _on_connect(self): - _LOGGER.debug("Connected!") - self._connected = True - dispatcher.async_dispatcher_send(self.hass, EVENT_HANGOUTS_CONNECTED) - - async def _on_disconnect(self): - """Handle disconnecting.""" - if self._connected: - _LOGGER.debug("Connection lost! Reconnect") - await self.async_connect() - else: - dispatcher.async_dispatcher_send(self.hass, EVENT_HANGOUTS_DISCONNECTED) - - async def async_disconnect(self): - """Disconnect the client if it is connected.""" - if self._connected: - self._connected = False - await self._client.disconnect() - - async def async_handle_hass_stop(self, _): - """Run once when Home Assistant stops.""" - await self.async_disconnect() - - async def _async_send_message(self, message, targets, data): - conversations = [] - for target in targets: - conversation = None - if CONF_CONVERSATION_ID in target: - conversation = self._conversation_list.get(target[CONF_CONVERSATION_ID]) - elif CONF_CONVERSATION_NAME in target: - conversation = self._resolve_conversation_name( - target[CONF_CONVERSATION_NAME] - ) - if conversation is not None: - conversations.append(conversation) - - if not conversations: - return False - - messages = [] - for segment in message: - if messages: - messages.append( - ChatMessageSegment( - "", segment_type=hangouts_pb2.SEGMENT_TYPE_LINE_BREAK - ) - ) - if "parse_str" in segment and segment["parse_str"]: - messages.extend(ChatMessageSegment.from_str(segment["text"])) - else: - if "parse_str" in segment: - del segment["parse_str"] - messages.append(ChatMessageSegment(**segment)) - - image_file = None - if data: - if data.get("image_url"): - uri = data.get("image_url") - try: - websession = async_get_clientsession(self.hass) - async with websession.get(uri, timeout=5) as response: - if response.status != HTTPStatus.OK: - _LOGGER.error( - "Fetch image failed, %s, %s", response.status, response - ) - image_file = None - else: - image_data = await response.read() - image_file = io.BytesIO(image_data) - image_file.name = "image.png" - except (asyncio.TimeoutError, aiohttp.ClientError) as error: - _LOGGER.error("Failed to fetch image, %s", type(error)) - image_file = None - elif data.get("image_file"): - uri = data.get("image_file") - if self.hass.config.is_allowed_path(uri): - try: - # pylint: disable=consider-using-with - image_file = open(uri, "rb") - except OSError as error: - _LOGGER.error( - "Image file I/O error(%s): %s", error.errno, error.strerror - ) - else: - _LOGGER.error('Path "%s" not allowed', uri) - - if not messages: - return False - for conv in conversations: - await conv.send_message(messages, image_file) - - async def _async_list_conversations(self): - ( - self._user_list, - self._conversation_list, - ) = await hangups.build_user_conversation_list(self._client) - conversations = {} - for i, conv in enumerate(self._conversation_list.get_all()): - users_in_conversation = [] - for user in conv.users: - users_in_conversation.append(user.full_name) - conversations[str(i)] = { - CONF_CONVERSATION_ID: str(conv.id_), - CONF_CONVERSATION_NAME: conv.name, - "users": users_in_conversation, - } - - self.hass.states.async_set( - f"{DOMAIN}.conversations", - len(self._conversation_list.get_all()), - attributes=conversations, - ) - dispatcher.async_dispatcher_send( - self.hass, EVENT_HANGOUTS_CONVERSATIONS_CHANGED, conversations - ) - - async def async_handle_send_message(self, service: ServiceCall) -> None: - """Handle the send_message service.""" - await self._async_send_message( - service.data[ATTR_MESSAGE], - service.data[ATTR_TARGET], - service.data.get(ATTR_DATA, {}), - ) - - async def async_handle_update_users_and_conversations( - self, service: ServiceCall | None = None - ) -> None: - """Handle the update_users_and_conversations service.""" - await self._async_list_conversations() - - async def async_handle_reconnect(self, service: ServiceCall | None = None) -> None: - """Handle the reconnect service.""" - await self.async_disconnect() - await self.async_connect() - - def get_intents(self, conv_id): - """Return the intents for a specific conversation.""" - return self._conversation_intents.get(conv_id) diff --git a/homeassistant/components/hangouts/hangups_utils.py b/homeassistant/components/hangouts/hangups_utils.py deleted file mode 100644 index d2556ac15a0..00000000000 --- a/homeassistant/components/hangouts/hangups_utils.py +++ /dev/null @@ -1,96 +0,0 @@ -"""Utils needed for Google Hangouts.""" - -from hangups import CredentialsPrompt, GoogleAuthError, RefreshTokenCache - - -class Google2FAError(GoogleAuthError): - """A Google authentication request failed.""" - - -class HangoutsCredentials(CredentialsPrompt): - """Google account credentials. - - This implementation gets the user data as params. - """ - - def __init__(self, email, password, pin=None, auth_code=None): - """Google account credentials. - - :param email: Google account email address. - :param password: Google account password. - :param pin: Google account verification code. - """ - self._email = email - self._password = password - self._pin = pin - self._auth_code = auth_code - - def get_email(self): - """Return email. - - :return: Google account email address. - """ - return self._email - - def get_password(self): - """Return password. - - :return: Google account password. - """ - return self._password - - def get_verification_code(self): - """Return the verification code. - - :return: Google account verification code. - """ - if self._pin is None: - raise Google2FAError() - return self._pin - - def set_verification_code(self, pin): - """Set the verification code. - - :param pin: Google account verification code. - """ - self._pin = pin - - def get_authorization_code(self): - """Return the oauth authorization code. - - :return: Google oauth code. - """ - return self._auth_code - - def set_authorization_code(self, code): - """Set the google oauth authorization code. - - :param code: Oauth code returned after authentication with google. - """ - self._auth_code = code - - -class HangoutsRefreshToken(RefreshTokenCache): - """Memory-based cache for refresh token.""" - - def __init__(self, token): - """Memory-based cache for refresh token. - - :param token: Initial refresh token. - """ - super().__init__("") - self._token = token - - def get(self): - """Get cached refresh token. - - :return: Cached refresh token. - """ - return self._token - - def set(self, refresh_token): - """Cache a refresh token. - - :param refresh_token: Refresh token to cache. - """ - self._token = refresh_token diff --git a/homeassistant/components/hangouts/intents.py b/homeassistant/components/hangouts/intents.py deleted file mode 100644 index 5e4c6ff206b..00000000000 --- a/homeassistant/components/hangouts/intents.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Intents for the Hangouts component.""" -from homeassistant.helpers import intent -import homeassistant.helpers.config_validation as cv - -from .const import CONF_BOT, DOMAIN, INTENT_HELP - - -class HelpIntent(intent.IntentHandler): - """Handle Help intents.""" - - intent_type = INTENT_HELP - slot_schema = {"conv_id": cv.string} - - def __init__(self, hass): - """Set up the intent.""" - self.hass = hass - - async def async_handle(self, intent_obj): - """Handle the intent.""" - slots = self.async_validate_slots(intent_obj.slots) - conv_id = slots["conv_id"]["value"] - - intents = self.hass.data[DOMAIN][CONF_BOT].get_intents(conv_id) - response = intent_obj.create_response() - help_text = "I understand the following sentences:" - for intent_data in intents.values(): - for sentence in intent_data["sentences"]: - help_text += f"\n'{sentence}'" - response.async_set_speech(help_text) - - return response diff --git a/homeassistant/components/hangouts/manifest.json b/homeassistant/components/hangouts/manifest.json deleted file mode 100644 index b8b1004bb78..00000000000 --- a/homeassistant/components/hangouts/manifest.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "domain": "hangouts", - "name": "Google Chat", - "config_flow": true, - "documentation": "https://www.home-assistant.io/integrations/hangouts", - "requirements": ["hangups==0.4.18"], - "codeowners": [], - "iot_class": "cloud_push", - "loggers": ["hangups", "urwid"] -} diff --git a/homeassistant/components/hangouts/notify.py b/homeassistant/components/hangouts/notify.py deleted file mode 100644 index 77dcec8111a..00000000000 --- a/homeassistant/components/hangouts/notify.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Support for Hangouts notifications.""" -import voluptuous as vol - -from homeassistant.components.notify import ( - ATTR_DATA, - ATTR_MESSAGE, - ATTR_TARGET, - PLATFORM_SCHEMA, - BaseNotificationService, -) - -from .const import ( - CONF_DEFAULT_CONVERSATIONS, - DOMAIN, - SERVICE_SEND_MESSAGE, - TARGETS_SCHEMA, -) - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - {vol.Required(CONF_DEFAULT_CONVERSATIONS): [TARGETS_SCHEMA]} -) - - -def get_service(hass, config, discovery_info=None): - """Get the Hangouts notification service.""" - return HangoutsNotificationService(config.get(CONF_DEFAULT_CONVERSATIONS)) - - -class HangoutsNotificationService(BaseNotificationService): - """Send Notifications to Hangouts conversations.""" - - def __init__(self, default_conversations): - """Set up the notification service.""" - self._default_conversations = default_conversations - - def send_message(self, message="", **kwargs): - """Send the message to the Google Hangouts server.""" - target_conversations = None - if ATTR_TARGET in kwargs: - target_conversations = [] - for target in kwargs.get(ATTR_TARGET): - target_conversations.append({"id": target}) - else: - target_conversations = self._default_conversations - - messages = [] - if "title" in kwargs: - messages.append({"text": kwargs["title"], "is_bold": True}) - - messages.append({"text": message, "parse_str": True}) - service_data = {ATTR_TARGET: target_conversations, ATTR_MESSAGE: messages} - if kwargs[ATTR_DATA]: - service_data[ATTR_DATA] = kwargs[ATTR_DATA] - - return self.hass.services.call( - DOMAIN, SERVICE_SEND_MESSAGE, service_data=service_data - ) diff --git a/homeassistant/components/hangouts/services.yaml b/homeassistant/components/hangouts/services.yaml deleted file mode 100644 index 041c21b5c25..00000000000 --- a/homeassistant/components/hangouts/services.yaml +++ /dev/null @@ -1,32 +0,0 @@ -update: - name: Update - description: Updates the list of conversations. - -send_message: - name: Send message - description: Send a notification to a specific target. - fields: - target: - name: Target - description: List of targets with id or name. - required: true - example: '[{"id": "UgxrXzVrARmjx_C6AZx4AaABAagBo-6UCw"}, {"name": "Test Conversation"}]' - selector: - object: - message: - name: Message - description: List of message segments, only the "text" field is required in every segment. - required: true - example: '[{"text":"test", "is_bold": false, "is_italic": false, "is_strikethrough": false, "is_underline": false, "parse_str": false, "link_target": "http://google.com"}]' - selector: - object: - data: - name: Data - description: Other options ['image_file' / 'image_url'] - example: '{ "image_file": "file" }' - selector: - object: - -reconnect: - name: Reconnect - description: Reconnect the bot. diff --git a/homeassistant/components/hangouts/strings.json b/homeassistant/components/hangouts/strings.json deleted file mode 100644 index fcc2da456bb..00000000000 --- a/homeassistant/components/hangouts/strings.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_service%]", - "unknown": "[%key:common::config_flow::error::unknown%]" - }, - "error": { - "invalid_login": "Invalid Login, please try again.", - "invalid_2fa": "Invalid 2 Factor Authentication, please try again.", - "invalid_2fa_method": "Invalid 2FA Method (verify on Phone)." - }, - "step": { - "user": { - "data": { - "email": "[%key:common::config_flow::data::email%]", - "password": "[%key:common::config_flow::data::password%]", - "authorization_code": "Authorization Code (required for manual authentication)" - }, - "title": "Google Chat Login" - }, - "2fa": { - "data": { - "2fa": "2FA PIN" - }, - "title": "2-Factor-Authentication" - } - } - } -} diff --git a/homeassistant/components/hangouts/translations/bg.json b/homeassistant/components/hangouts/translations/bg.json deleted file mode 100644 index 8d8dae90ce0..00000000000 --- a/homeassistant/components/hangouts/translations/bg.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Google Hangouts \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", - "unknown": "\u0412\u044a\u0437\u043d\u0438\u043a\u043d\u0430 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430." - }, - "error": { - "invalid_2fa": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430 2-\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f, \u043c\u043e\u043b\u044f, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.", - "invalid_2fa_method": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d \u043c\u0435\u0442\u043e\u0434 2FA (\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430).", - "invalid_login": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0432\u043b\u0438\u0437\u0430\u043d\u0435, \u043c\u043e\u043b\u044f, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA PIN" - }, - "title": "\u0414\u0432\u0443-\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" - }, - "user": { - "data": { - "authorization_code": "\u041a\u043e\u0434 \u0437\u0430 \u043e\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f (\u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0437\u0430 \u0440\u044a\u0447\u043d\u043e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0438\u0440\u0430\u043d\u0435)", - "email": "\u0418\u043c\u0435\u0439\u043b", - "password": "\u041f\u0430\u0440\u043e\u043b\u0430" - }, - "title": "\u0412\u0445\u043e\u0434 \u0432 Google Hangouts" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/ca.json b/homeassistant/components/hangouts/translations/ca.json deleted file mode 100644 index 8b629201cc1..00000000000 --- a/homeassistant/components/hangouts/translations/ca.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "El servei ja est\u00e0 configurat", - "unknown": "Error inesperat" - }, - "error": { - "invalid_2fa": "La verificaci\u00f3 en dos passos no \u00e9s v\u00e0lida, torna-ho a provar.", - "invalid_2fa_method": "M\u00e8tode 2FA inv\u00e0lid (verifica-ho al m\u00f2bil).", - "invalid_login": "L'inici de sessi\u00f3 no \u00e9s v\u00e0lid, torna-ho a provar." - }, - "step": { - "2fa": { - "data": { - "2fa": "PIN 2FA" - }, - "description": "Buit", - "title": "Verificaci\u00f3 en dos passos" - }, - "user": { - "data": { - "authorization_code": "Codi d'autoritzaci\u00f3 (necessari per a l'autenticaci\u00f3 manual)", - "email": "Correu electr\u00f2nic", - "password": "Contrasenya" - }, - "description": "Buit", - "title": "Inici de sessi\u00f3 de Google Chat" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/cs.json b/homeassistant/components/hangouts/translations/cs.json deleted file mode 100644 index 11bef6d1d1a..00000000000 --- a/homeassistant/components/hangouts/translations/cs.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Slu\u017eba je ji\u017e nastavena", - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" - }, - "error": { - "invalid_2fa": "Dfoufaktorov\u00e9 ov\u011b\u0159en\u00ed se nezda\u0159ilo. Zkuste to znovu.", - "invalid_2fa_method": "Neplatn\u00e1 metoda 2FA (ov\u011b\u0159en\u00ed na telefonu).", - "invalid_login": "Neplatn\u00e9 p\u0159ihla\u0161ovac\u00ed jm\u00e9no, pros\u00edm zkuste to znovu." - }, - "step": { - "2fa": { - "data": { - "2fa": "Dvoufaktorov\u00fd ov\u011b\u0159ovac\u00ed k\u00f3d" - }, - "description": "Pr\u00e1zdn\u00e9", - "title": "Dvoufaktorov\u00e9 ov\u011b\u0159en\u00ed" - }, - "user": { - "data": { - "authorization_code": "Autoriza\u010dn\u00ed k\u00f3d (vy\u017eadov\u00e1n pro ru\u010dn\u00ed ov\u011b\u0159en\u00ed)", - "email": "E-mail", - "password": "Heslo" - }, - "description": "Pr\u00e1zdn\u00e9", - "title": "P\u0159ihl\u00e1\u0161en\u00ed do slu\u017eby Google Hangouts" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/da.json b/homeassistant/components/hangouts/translations/da.json deleted file mode 100644 index e490c33805d..00000000000 --- a/homeassistant/components/hangouts/translations/da.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Google Hangouts er allerede konfigureret", - "unknown": "Ukendt fejl opstod" - }, - "error": { - "invalid_2fa": "Ugyldig tofaktor-godkendelse, pr\u00f8v igen.", - "invalid_2fa_method": "Ugyldig 2FA-metode (Bekr\u00e6ft p\u00e5 telefon).", - "invalid_login": "Ugyldig login, pr\u00f8v venligst igen." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA pin" - }, - "title": "Tofaktor-godkendelse" - }, - "user": { - "data": { - "authorization_code": "Godkendelseskode (kr\u00e6vet til manuel godkendelse)", - "email": "Emailadresse", - "password": "Adgangskode" - }, - "title": "Google Hangouts login" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/de.json b/homeassistant/components/hangouts/translations/de.json deleted file mode 100644 index 53225644a2d..00000000000 --- a/homeassistant/components/hangouts/translations/de.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Der Dienst ist bereits konfiguriert", - "unknown": "Unerwarteter Fehler" - }, - "error": { - "invalid_2fa": "Ung\u00fcltige 2-Faktor Authentifizierung, bitte versuche es erneut.", - "invalid_2fa_method": "Ung\u00fcltige 2FA Methode (mit Telefon verifizieren)", - "invalid_login": "Ung\u00fcltiges Login, bitte versuche es erneut." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA PIN" - }, - "description": "Leer", - "title": "2-Faktor-Authentifizierung" - }, - "user": { - "data": { - "authorization_code": "Autorisierungscode (f\u00fcr die manuelle Authentifizierung erforderlich)", - "email": "E-Mail", - "password": "Passwort" - }, - "description": "Leer", - "title": "Google Chat Anmeldung" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/el.json b/homeassistant/components/hangouts/translations/el.json deleted file mode 100644 index 4b453c5bbaa..00000000000 --- a/homeassistant/components/hangouts/translations/el.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af", - "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" - }, - "error": { - "invalid_2fa": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac.", - "invalid_2fa_method": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03bc\u03ad\u03b8\u03bf\u03b4\u03bf\u03c2 2FA (\u03b5\u03c0\u03b1\u03bb\u03ae\u03b8\u03b5\u03c5\u03c3\u03b7 \u03c3\u03c4\u03bf \u03c4\u03b7\u03bb\u03ad\u03c6\u03c9\u03bd\u03bf).", - "invalid_login": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac." - }, - "step": { - "2fa": { - "data": { - "2fa": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd" - }, - "description": "\u039a\u03b5\u03bd\u03cc", - "title": "\u03a0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd" - }, - "user": { - "data": { - "authorization_code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2 (\u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03bf \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2)", - "email": "Email", - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" - }, - "description": "\u039a\u03b5\u03bd\u03cc", - "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 Google Hangouts" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/en.json b/homeassistant/components/hangouts/translations/en.json deleted file mode 100644 index 4829e843c6c..00000000000 --- a/homeassistant/components/hangouts/translations/en.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Service is already configured", - "unknown": "Unexpected error" - }, - "error": { - "invalid_2fa": "Invalid 2 Factor Authentication, please try again.", - "invalid_2fa_method": "Invalid 2FA Method (verify on Phone).", - "invalid_login": "Invalid Login, please try again." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA PIN" - }, - "title": "2-Factor-Authentication" - }, - "user": { - "data": { - "authorization_code": "Authorization Code (required for manual authentication)", - "email": "Email", - "password": "Password" - }, - "title": "Google Chat Login" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/es-419.json b/homeassistant/components/hangouts/translations/es-419.json deleted file mode 100644 index a8ae41ec21e..00000000000 --- a/homeassistant/components/hangouts/translations/es-419.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Google Hangouts ya est\u00e1 configurado", - "unknown": "Se produjo un error desconocido." - }, - "error": { - "invalid_2fa": "Autenticaci\u00f3n de 2 factores no v\u00e1lida, intente nuevamente.", - "invalid_2fa_method": "M\u00e9todo 2FA no v\u00e1lido (verificar en el tel\u00e9fono).", - "invalid_login": "Inicio de sesi\u00f3n no v\u00e1lido, por favor, int\u00e9ntalo de nuevo." - }, - "step": { - "2fa": { - "data": { - "2fa": "Pin 2FA" - }, - "title": "Autenticaci\u00f3n de 2 factores" - }, - "user": { - "data": { - "authorization_code": "C\u00f3digo de autorizaci\u00f3n (requerido para la autenticaci\u00f3n manual)", - "email": "Direcci\u00f3n de correo electr\u00f3nico", - "password": "Contrase\u00f1a" - }, - "title": "Inicio de sesi\u00f3n de Google Hangouts" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/es.json b/homeassistant/components/hangouts/translations/es.json deleted file mode 100644 index 29fe36ea23c..00000000000 --- a/homeassistant/components/hangouts/translations/es.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "El servicio ya est\u00e1 configurado", - "unknown": "Error inesperado" - }, - "error": { - "invalid_2fa": "Autenticaci\u00f3n de 2 factores no v\u00e1lida, por favor, int\u00e9ntalo de nuevo.", - "invalid_2fa_method": "M\u00e9todo 2FA no v\u00e1lido (verificar en el tel\u00e9fono).", - "invalid_login": "Inicio de sesi\u00f3n no v\u00e1lido, por favor, int\u00e9ntalo de nuevo." - }, - "step": { - "2fa": { - "data": { - "2fa": "PIN 2FA" - }, - "title": "Autenticaci\u00f3n de 2 factores" - }, - "user": { - "data": { - "authorization_code": "C\u00f3digo de autorizaci\u00f3n (requerido para la autenticaci\u00f3n manual)", - "email": "Correo electr\u00f3nico", - "password": "Contrase\u00f1a" - }, - "title": "Inicio de sesi\u00f3n de Google Chat" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/et.json b/homeassistant/components/hangouts/translations/et.json deleted file mode 100644 index 96ed8b998ec..00000000000 --- a/homeassistant/components/hangouts/translations/et.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Teenus on juba seadistatud", - "unknown": "Tundmatu viga" - }, - "error": { - "invalid_2fa": "Vale 2-teguriline autentimine, proovi uuesti.", - "invalid_2fa_method": "Kehtetu kaheastmelise tuvastuse meetod (kontrolli telefonistl).", - "invalid_login": "Vale kasutajanimi, palun proovi uuesti." - }, - "step": { - "2fa": { - "data": { - "2fa": "Kaheastmelise tuvastuse PIN" - }, - "description": "", - "title": "Kaheastmeline autentimine" - }, - "user": { - "data": { - "authorization_code": "Autoriseerimiskood (vajalik k\u00e4sitsi autentimiseks)", - "email": "E-posti aadress", - "password": "Salas\u00f5na" - }, - "description": "", - "title": "Google Chat'i sisselogimine" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/fi.json b/homeassistant/components/hangouts/translations/fi.json deleted file mode 100644 index e93642a952d..00000000000 --- a/homeassistant/components/hangouts/translations/fi.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "config": { - "abort": { - "unknown": "Tapahtui tuntematon virhe." - }, - "error": { - "invalid_2fa": "Virheellinen kaksitekij\u00e4todennus, yrit\u00e4 uudelleen.", - "invalid_2fa_method": "Virheellinen 2FA-menetelm\u00e4 (tarkista puhelimessa).", - "invalid_login": "Virheellinen kirjautuminen, yrit\u00e4 uudelleen." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA-pin" - }, - "description": "Tyhj\u00e4", - "title": "Kaksivaiheinen tunnistus" - }, - "user": { - "data": { - "email": "S\u00e4hk\u00f6postiosoite", - "password": "Salasana" - }, - "description": "Tyhj\u00e4", - "title": "Google Hangouts -kirjautuminen" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/fr.json b/homeassistant/components/hangouts/translations/fr.json deleted file mode 100644 index 78a2517d29a..00000000000 --- a/homeassistant/components/hangouts/translations/fr.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9", - "unknown": "Erreur inattendue" - }, - "error": { - "invalid_2fa": "Authentification \u00e0 deux facteurs non valide, veuillez r\u00e9essayer.", - "invalid_2fa_method": "M\u00e9thode 2FA non valide (v\u00e9rifiez sur le t\u00e9l\u00e9phone).", - "invalid_login": "Identifiant non valide, veuillez r\u00e9essayer." - }, - "step": { - "2fa": { - "data": { - "2fa": "Code NIP d'authentification \u00e0 2 facteurs" - }, - "description": "Vide", - "title": "Authentification \u00e0 2 facteurs" - }, - "user": { - "data": { - "authorization_code": "Code d'autorisation (requis pour l'authentification manuelle)", - "email": "Courriel", - "password": "Mot de passe" - }, - "description": "Vide", - "title": "Connexion \u00e0 Google Chat" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/he.json b/homeassistant/components/hangouts/translations/he.json deleted file mode 100644 index ad696cad365..00000000000 --- a/homeassistant/components/hangouts/translations/he.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8", - "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" - }, - "error": { - "invalid_2fa": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d3\u05d5 \u05e9\u05dc\u05d1\u05d9 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d1\u05d1\u05e7\u05e9\u05d4 \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1.", - "invalid_2fa_method": "\u05d3\u05e8\u05da \u05dc\u05d0\u05d9\u05de\u05d5\u05ea \u05d3\u05d5 \u05e9\u05dc\u05d1\u05d9 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05ea (\u05d0\u05de\u05ea \u05d1\u05d8\u05dc\u05e4\u05d5\u05df).", - "invalid_login": "\u05db\u05e0\u05d9\u05e1\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05ea, \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1." - }, - "step": { - "2fa": { - "data": { - "2fa": "\u05e7\u05d5\u05d3 \u05d0\u05d9\u05de\u05d5\u05ea \u05d3\u05d5 \u05e9\u05dc\u05d1\u05d9" - }, - "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d3\u05d5 \u05e9\u05dc\u05d1\u05d9" - }, - "user": { - "data": { - "email": "\u05d3\u05d5\u05d0\"\u05dc", - "password": "\u05e1\u05d9\u05e1\u05de\u05d4" - }, - "title": "\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05dc\u05e6'\u05d0\u05d8 \u05e9\u05dc \u05d2\u05d5\u05d2\u05dc" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/hr.json b/homeassistant/components/hangouts/translations/hr.json deleted file mode 100644 index 59b98ae9ae9..00000000000 --- a/homeassistant/components/hangouts/translations/hr.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "email": "Email", - "password": "Lozinka" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/hu.json b/homeassistant/components/hangouts/translations/hu.json deleted file mode 100644 index 1ea997aa098..00000000000 --- a/homeassistant/components/hangouts/translations/hu.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van", - "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" - }, - "error": { - "invalid_2fa": "\u00c9rv\u00e9nytelen K\u00e9tfaktoros hiteles\u00edt\u00e9s, pr\u00f3b\u00e1ld \u00fajra.", - "invalid_2fa_method": "\u00c9rv\u00e9nytelen 2FA M\u00f3dszer (Ellen\u0151rz\u00e9s a Telefonon).", - "invalid_login": "\u00c9rv\u00e9nytelen bejelentkez\u00e9s, pr\u00f3b\u00e1ld \u00fajra." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA PIN" - }, - "description": "\u00dcres", - "title": "K\u00e9tfaktoros Hiteles\u00edt\u00e9s" - }, - "user": { - "data": { - "authorization_code": "Enged\u00e9lyez\u00e9si k\u00f3d (k\u00e9zi hiteles\u00edt\u00e9shez sz\u00fcks\u00e9ges)", - "email": "E-mail", - "password": "Jelsz\u00f3" - }, - "description": "\u00dcres", - "title": "Google Chat bejelentkez\u00e9s" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/id.json b/homeassistant/components/hangouts/translations/id.json deleted file mode 100644 index 2336b211c9e..00000000000 --- a/homeassistant/components/hangouts/translations/id.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Layanan sudah dikonfigurasi", - "unknown": "Kesalahan yang tidak diharapkan" - }, - "error": { - "invalid_2fa": "Autentikasi 2 Faktor Tidak Valid, coba lagi.", - "invalid_2fa_method": "Metode 2FA Tidak Valid (Verifikasikan di Ponsel).", - "invalid_login": "Info Masuk Tidak Valid, coba lagi." - }, - "step": { - "2fa": { - "data": { - "2fa": "PIN 2FA" - }, - "description": "Kosong", - "title": "Autentikasi Dua Faktor" - }, - "user": { - "data": { - "authorization_code": "Kode Otorisasi (diperlukan untuk autentikasi manual)", - "email": "Email", - "password": "Kata Sandi" - }, - "description": "Kosong", - "title": "Info Masuk Google Chat" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/it.json b/homeassistant/components/hangouts/translations/it.json deleted file mode 100644 index 76d81a184d9..00000000000 --- a/homeassistant/components/hangouts/translations/it.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Il servizio \u00e8 gi\u00e0 configurato", - "unknown": "Errore imprevisto" - }, - "error": { - "invalid_2fa": "Autenticazione a 2 fattori non valida, riprova.", - "invalid_2fa_method": "Metodo 2FA non valido (verifica sul telefono).", - "invalid_login": "Accesso non valido, riprova." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA PIN" - }, - "description": "Vuoto", - "title": "Autenticazione a due fattori" - }, - "user": { - "data": { - "authorization_code": "Codice di autorizzazione (necessario per l'autenticazione manuale)", - "email": "Email", - "password": "Password" - }, - "description": "Vuoto", - "title": "Accesso a Google Chat" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/ja.json b/homeassistant/components/hangouts/translations/ja.json deleted file mode 100644 index 14637ee1155..00000000000 --- a/homeassistant/components/hangouts/translations/ja.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "\u30b5\u30fc\u30d3\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" - }, - "error": { - "invalid_2fa": "2\u8981\u7d20\u8a8d\u8a3c\u304c\u7121\u52b9\u3067\u3059\u3002\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002", - "invalid_2fa_method": "2\u8981\u7d20\u8a8d\u8a3c\u304c\u7121\u52b9(\u96fb\u8a71\u3067\u78ba\u8a8d)", - "invalid_login": "\u30ed\u30b0\u30a4\u30f3\u3067\u304d\u307e\u305b\u3093\u3001\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002" - }, - "step": { - "2fa": { - "data": { - "2fa": "2\u8981\u7d20 PIN" - }, - "description": "\u7a7a", - "title": "2\u8981\u7d20\u8a8d\u8a3c" - }, - "user": { - "data": { - "authorization_code": "\u8a8d\u8a3c\u30b3\u30fc\u30c9(\u624b\u52d5\u8a8d\u8a3c\u6642\u306b\u5fc5\u8981)", - "email": "E\u30e1\u30fc\u30eb", - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" - }, - "description": "\u7a7a", - "title": "Google \u30cf\u30f3\u30b0\u30a2\u30a6\u30c8 \u30ed\u30b0\u30a4\u30f3" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/ko.json b/homeassistant/components/hangouts/translations/ko.json deleted file mode 100644 index 56c3c577a89..00000000000 --- a/homeassistant/components/hangouts/translations/ko.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" - }, - "error": { - "invalid_2fa": "2\ub2e8\uacc4 \uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694.", - "invalid_2fa_method": "2\ub2e8\uacc4 \uc778\uc99d \ubc29\ubc95\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4. (\uc804\ud654\uae30\uc5d0\uc11c \ud655\uc778)", - "invalid_login": "\ub85c\uadf8\uc778\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694." - }, - "step": { - "2fa": { - "data": { - "2fa": "2\ub2e8\uacc4 \uc778\uc99d PIN" - }, - "description": "\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \uad00\ub828 \ub0b4\uc6a9\uc774 \uc544\uc9c1 \uc5c5\ub370\uc774\ud2b8 \ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \ucd94\ud6c4\uc5d0 \ubc18\uc601\ub420 \uc608\uc815\uc774\ub2c8 \uc870\uae08\ub9cc \uae30\ub2e4\ub824\uc8fc\uc138\uc694.", - "title": "2\ub2e8\uacc4 \uc778\uc99d" - }, - "user": { - "data": { - "authorization_code": "\uc778\uc99d \ucf54\ub4dc (\uc218\ub3d9 \uc778\uc99d\uc5d0 \ud544\uc694)", - "email": "\uc774\uba54\uc77c", - "password": "\ube44\ubc00\ubc88\ud638" - }, - "description": "\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \uad00\ub828 \ub0b4\uc6a9\uc774 \uc544\uc9c1 \uc5c5\ub370\uc774\ud2b8 \ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \ucd94\ud6c4\uc5d0 \ubc18\uc601\ub420 \uc608\uc815\uc774\ub2c8 \uc870\uae08\ub9cc \uae30\ub2e4\ub824\uc8fc\uc138\uc694.", - "title": "Google \ud589\uc544\uc6c3 \ub85c\uadf8\uc778" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/lb.json b/homeassistant/components/hangouts/translations/lb.json deleted file mode 100644 index a91f2e7b040..00000000000 --- a/homeassistant/components/hangouts/translations/lb.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Service ass scho konfigur\u00e9iert", - "unknown": "Onerwaarte Feeler" - }, - "error": { - "invalid_2fa": "Ong\u00eblteg 2-Faktor Authentifikatioun, prob\u00e9iert w.e.g. nach emol.", - "invalid_2fa_method": "Ong\u00eblteg 2FA Methode (Iwwerpr\u00e9ift et um Telefon)", - "invalid_login": "Ong\u00ebltege Login, prob\u00e9iert w.e.g. nach emol." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA Pin" - }, - "description": "Eidel", - "title": "2-Faktor-Authentifikatioun" - }, - "user": { - "data": { - "authorization_code": "Autorisatioun's Code (n\u00e9ideg fir eng manuell Authentifikatioun)", - "email": "E-Mail", - "password": "Passwuert" - }, - "description": "Eidel", - "title": "Google Hangouts Login" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/lt.json b/homeassistant/components/hangouts/translations/lt.json deleted file mode 100644 index 13dbbf8bdbc..00000000000 --- a/homeassistant/components/hangouts/translations/lt.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "config": { - "step": { - "2fa": { - "data": { - "2fa": "2FA PIN" - }, - "title": "2 veiksni\u0173 autentifikavimas" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/nb.json b/homeassistant/components/hangouts/translations/nb.json deleted file mode 100644 index 11a4fc139b8..00000000000 --- a/homeassistant/components/hangouts/translations/nb.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "config": { - "abort": { - "unknown": "Uventet feil" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/nl.json b/homeassistant/components/hangouts/translations/nl.json deleted file mode 100644 index 826bc560045..00000000000 --- a/homeassistant/components/hangouts/translations/nl.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Dienst is al geconfigureerd", - "unknown": "Onverwachte fout" - }, - "error": { - "invalid_2fa": "Ongeldige twee-factor-authenticatie, probeer het opnieuw.", - "invalid_2fa_method": "Ongeldige 2FA-methode (verifi\u00ebren op telefoon).", - "invalid_login": "Ongeldige aanmelding, probeer het opnieuw." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA pin" - }, - "description": "Leeg", - "title": "Twee-factor-authenticatie" - }, - "user": { - "data": { - "authorization_code": "Autorisatiecode (vereist voor handmatige authenticatie)", - "email": "E-mail", - "password": "Wachtwoord" - }, - "description": "Leeg", - "title": "Google Chat-login" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/nn.json b/homeassistant/components/hangouts/translations/nn.json deleted file mode 100644 index 883a53441af..00000000000 --- a/homeassistant/components/hangouts/translations/nn.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Google Hangouts er allereie konfigurert", - "unknown": "Det hende ein ukjent feil" - }, - "error": { - "invalid_2fa": "Ugyldig to-faktor-autentisering. Ver vennleg og pr\u00f8v igjen.", - "invalid_2fa_method": "Ugyldig 2FA-metode (godkjenn p\u00e5 telefonen).", - "invalid_login": "Ugyldig innlogging. Pr\u00f8v igjen." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA PIN" - }, - "title": "To-faktor-autentisering" - }, - "user": { - "data": { - "email": "Epostadresse", - "password": "Passord" - }, - "title": "Google Hangouts Login" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/no.json b/homeassistant/components/hangouts/translations/no.json deleted file mode 100644 index 751d54c852e..00000000000 --- a/homeassistant/components/hangouts/translations/no.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Tjenesten er allerede konfigurert", - "unknown": "Uventet feil" - }, - "error": { - "invalid_2fa": "Ugyldig totrinnsbekreftelse, vennligst pr\u00f8v igjen.", - "invalid_2fa_method": "Ugyldig 2FA-metode (bekreft p\u00e5 telefon).", - "invalid_login": "Ugyldig innlogging, vennligst pr\u00f8v igjen." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA PIN" - }, - "description": "", - "title": "Totrinnsbekreftelse" - }, - "user": { - "data": { - "authorization_code": "Godkjenningskode (kreves for manuell godkjenning)", - "email": "E-post", - "password": "Passord" - }, - "description": "", - "title": "Google Chat-p\u00e5logging" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/pl.json b/homeassistant/components/hangouts/translations/pl.json deleted file mode 100644 index 4dafdc5d996..00000000000 --- a/homeassistant/components/hangouts/translations/pl.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Us\u0142uga jest ju\u017c skonfigurowana", - "unknown": "Nieoczekiwany b\u0142\u0105d" - }, - "error": { - "invalid_2fa": "Nieprawid\u0142owe uwierzytelnienie dwusk\u0142adnikowe, spr\u00f3buj ponownie", - "invalid_2fa_method": "Nieprawid\u0142owa metoda uwierzytelniania dwusk\u0142adnikowego (u\u017cyj weryfikacji przez telefon)", - "invalid_login": "Nieprawid\u0142owy login, spr\u00f3buj ponownie" - }, - "step": { - "2fa": { - "data": { - "2fa": "Kod uwierzytelniania dwusk\u0142adnikowego" - }, - "description": "Pusty", - "title": "Uwierzytelnianie dwusk\u0142adnikowe" - }, - "user": { - "data": { - "authorization_code": "Kod autoryzacji (wymagany do r\u0119cznego uwierzytelnienia)", - "email": "Adres e-mail", - "password": "Has\u0142o" - }, - "description": "Pusty", - "title": "Logowanie do Google Chat" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/pt-BR.json b/homeassistant/components/hangouts/translations/pt-BR.json deleted file mode 100644 index 9e4d04cd989..00000000000 --- a/homeassistant/components/hangouts/translations/pt-BR.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado", - "unknown": "Erro inesperado" - }, - "error": { - "invalid_2fa": "Autentica\u00e7\u00e3o de 2 fatores inv\u00e1lida, por favor, tente novamente.", - "invalid_2fa_method": "M\u00e9todo 2FA inv\u00e1lido (verificar no telefone).", - "invalid_login": "Login inv\u00e1lido, por favor, tente novamente." - }, - "step": { - "2fa": { - "data": { - "2fa": "C\u00f3digo 2FA" - }, - "description": "Vazio", - "title": "Autentica\u00e7\u00e3o de 2 Fatores" - }, - "user": { - "data": { - "authorization_code": "C\u00f3digo de Autoriza\u00e7\u00e3o (requerido para autentica\u00e7\u00e3o manual)", - "email": "Email", - "password": "Senha" - }, - "description": "Vazio", - "title": "Login no Google Chat" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/pt.json b/homeassistant/components/hangouts/translations/pt.json deleted file mode 100644 index b4feb91c76d..00000000000 --- a/homeassistant/components/hangouts/translations/pt.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Google Hangouts j\u00e1 est\u00e1 configurado", - "unknown": "Erro inesperado" - }, - "error": { - "invalid_2fa": "Autentica\u00e7\u00e3o por 2 fatores inv\u00e1lida, por favor, tente novamente.", - "invalid_2fa_method": "M\u00e9todo 2FA inv\u00e1lido (verificar no telefone).", - "invalid_login": "Login inv\u00e1lido, por favor, tente novamente." - }, - "step": { - "2fa": { - "data": { - "2fa": "Pin 2FA" - }, - "description": "Vazio", - "title": "Autentica\u00e7\u00e3o de 2 Fatores" - }, - "user": { - "data": { - "email": "E-mail", - "password": "Palavra-passe" - }, - "description": "Vazio", - "title": "Login Google Hangouts" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/ro.json b/homeassistant/components/hangouts/translations/ro.json deleted file mode 100644 index 682d561929c..00000000000 --- a/homeassistant/components/hangouts/translations/ro.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Google Hangouts este deja configurat", - "unknown": "Sa produs o eroare necunoscut\u0103." - }, - "error": { - "invalid_2fa_method": "Metoda 2FA invalid\u0103 (Verifica\u021bi pe telefon).", - "invalid_login": "Conectare invalid\u0103, \u00eencerca\u021bi din nou." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA Pin" - } - }, - "user": { - "data": { - "email": "Adresa de email", - "password": "Parol\u0103" - }, - "description": "Gol", - "title": "Conectare Google Hangouts" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/ru.json b/homeassistant/components/hangouts/translations/ru.json deleted file mode 100644 index 781e1e25eef..00000000000 --- a/homeassistant/components/hangouts/translations/ru.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." - }, - "error": { - "invalid_2fa": "\u041d\u0435\u0432\u0435\u0440\u043d\u0430\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430.", - "invalid_2fa_method": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (\u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0435).", - "invalid_login": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043b\u043e\u0433\u0438\u043d, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430." - }, - "step": { - "2fa": { - "data": { - "2fa": "\u041f\u0438\u043d-\u043a\u043e\u0434 \u0434\u043b\u044f \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438" - }, - "description": "\u043f\u0443\u0441\u0442\u043e", - "title": "\u0414\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" - }, - "user": { - "data": { - "authorization_code": "\u041a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 (\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0440\u0443\u0447\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438)", - "email": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" - }, - "description": "\u043f\u0443\u0441\u0442\u043e", - "title": "Google Chat" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/sk.json b/homeassistant/components/hangouts/translations/sk.json deleted file mode 100644 index 45123261c43..00000000000 --- a/homeassistant/components/hangouts/translations/sk.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "email": "Email", - "password": "Heslo" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/sl.json b/homeassistant/components/hangouts/translations/sl.json deleted file mode 100644 index 853dfa1487a..00000000000 --- a/homeassistant/components/hangouts/translations/sl.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Google Hangouts je \u017ee konfiguriran", - "unknown": "Pri\u0161lo je do neznane napake" - }, - "error": { - "invalid_2fa": "Neveljavna 2FA avtorizacija, prosimo, poskusite znova.", - "invalid_2fa_method": "Neveljavna 2FA Metoda (Preverite na Telefonu).", - "invalid_login": "Neveljavna Prijava, prosimo, poskusite znova." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA Pin" - }, - "description": "prazno", - "title": "Dvofaktorska avtorizacija" - }, - "user": { - "data": { - "authorization_code": "Koda pooblastila (potrebna za ro\u010dno overjanje)", - "email": "E-po\u0161tni naslov", - "password": "Geslo" - }, - "description": "prazno", - "title": "Prijava za Google Hangouts" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/sv.json b/homeassistant/components/hangouts/translations/sv.json deleted file mode 100644 index f9e5ec14c54..00000000000 --- a/homeassistant/components/hangouts/translations/sv.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Google Hangouts \u00e4r redan inst\u00e4llt", - "unknown": "Ett ok\u00e4nt fel intr\u00e4ffade" - }, - "error": { - "invalid_2fa": "Ogiltig 2FA autentisering, f\u00f6rs\u00f6k igen.", - "invalid_2fa_method": "Ogiltig 2FA-metod (Verifiera med telefon).", - "invalid_login": "Ogiltig inloggning, f\u00f6rs\u00f6k igen." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA Pinkod" - }, - "description": "Missing english translation", - "title": "Tv\u00e5faktorsautentisering" - }, - "user": { - "data": { - "authorization_code": "Auktoriseringskod (kr\u00e4vs vid manuell verifiering)", - "email": "E-postadress", - "password": "L\u00f6senord" - }, - "description": "Missing english translation", - "title": "Google Hangouts-inloggning" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/th.json b/homeassistant/components/hangouts/translations/th.json deleted file mode 100644 index bcc59392e2e..00000000000 --- a/homeassistant/components/hangouts/translations/th.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "config": { - "step": { - "2fa": { - "title": "\u0e23\u0e2b\u0e31\u0e2a\u0e23\u0e31\u0e1a\u0e23\u0e2d\u0e07\u0e04\u0e27\u0e32\u0e21\u0e16\u0e39\u0e01\u0e15\u0e49\u0e2d\u0e07\u0e2a\u0e2d\u0e07\u0e1b\u0e31\u0e08\u0e08\u0e31\u0e22" - }, - "user": { - "data": { - "email": "\u0e17\u0e35\u0e48\u0e2d\u0e22\u0e39\u0e48\u0e2d\u0e35\u0e40\u0e21\u0e25", - "password": "\u0e23\u0e2b\u0e31\u0e2a\u0e1c\u0e48\u0e32\u0e19" - }, - "description": "\u0e27\u0e48\u0e32\u0e07\u0e40\u0e1b\u0e25\u0e48\u0e32" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/tr.json b/homeassistant/components/hangouts/translations/tr.json deleted file mode 100644 index 5ddf2a64cbb..00000000000 --- a/homeassistant/components/hangouts/translations/tr.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Hizmet zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", - "unknown": "Beklenmeyen hata" - }, - "error": { - "invalid_2fa": "Ge\u00e7ersiz 2 Fakt\u00f6rl\u00fc Kimlik Do\u011frulama, l\u00fctfen tekrar deneyin.", - "invalid_2fa_method": "Ge\u00e7ersiz 2FA Y\u00f6ntemi (Telefonda do\u011frulay\u0131n).", - "invalid_login": "Ge\u00e7ersiz Giri\u015f, l\u00fctfen tekrar deneyin." - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA PIN'i" - }, - "description": "Bo\u015f", - "title": "2-Fakt\u00f6rl\u00fc Kimlik Do\u011frulama" - }, - "user": { - "data": { - "authorization_code": "Yetkilendirme Kodu (manuel kimlik do\u011frulama i\u00e7in gereklidir)", - "email": "E-posta", - "password": "Parola" - }, - "description": "Bo\u015f", - "title": "Google Sohbet Giri\u015fi" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/uk.json b/homeassistant/components/hangouts/translations/uk.json deleted file mode 100644 index 93eb699d37c..00000000000 --- a/homeassistant/components/hangouts/translations/uk.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "\u0426\u044f \u0441\u043b\u0443\u0436\u0431\u0430 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u0430 \u0432 Home Assistant.", - "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" - }, - "error": { - "invalid_2fa": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0456\u044f, \u0431\u0443\u0434\u044c \u043b\u0430\u0441\u043a\u0430, \u0441\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0437\u043d\u043e\u0432\u0443.", - "invalid_2fa_method": "\u041d\u0435\u043f\u0440\u0438\u043f\u0443\u0441\u0442\u0438\u043c\u0438\u0439 \u0441\u043f\u043e\u0441\u0456\u0431 \u0434\u0432\u043e\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0457 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u0457 (\u043f\u0435\u0440\u0435\u0432\u0456\u0440\u0438\u0442\u0438 \u043d\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0456).", - "invalid_login": "\u041d\u0435\u0432\u0456\u0440\u043d\u0438\u0439 \u043b\u043e\u0433\u0456\u043d, \u0431\u0443\u0434\u044c \u043b\u0430\u0441\u043a\u0430, \u0441\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0437\u043d\u043e\u0432\u0443." - }, - "step": { - "2fa": { - "data": { - "2fa": "\u041f\u0456\u043d-\u043a\u043e\u0434 \u0434\u043b\u044f \u0434\u0432\u043e\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0457 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u0457" - }, - "description": "\u043f\u043e\u0440\u043e\u0436\u043d\u044c\u043e", - "title": "\u0414\u0432\u043e\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f" - }, - "user": { - "data": { - "authorization_code": "\u041a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0456\u0457 (\u0432\u0438\u043c\u0430\u0433\u0430\u0454\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0440\u0443\u0447\u043d\u043e\u0457 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u0457)", - "email": "\u0410\u0434\u0440\u0435\u0441\u0430 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0457 \u043f\u043e\u0448\u0442\u0438", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" - }, - "description": "\u043f\u043e\u0440\u043e\u0436\u043d\u044c\u043e", - "title": "Google Hangouts" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/vi.json b/homeassistant/components/hangouts/translations/vi.json deleted file mode 100644 index d794a0b5afa..00000000000 --- a/homeassistant/components/hangouts/translations/vi.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "config": { - "error": { - "invalid_2fa_method": "Ph\u01b0\u01a1ng ph\u00e1p 2FA kh\u00f4ng h\u1ee3p l\u1ec7 (X\u00e1c minh tr\u00ean \u0111i\u1ec7n tho\u1ea1i)." - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/zh-Hans.json b/homeassistant/components/hangouts/translations/zh-Hans.json deleted file mode 100644 index 46d1de99c73..00000000000 --- a/homeassistant/components/hangouts/translations/zh-Hans.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Google Hangouts \u5df2\u914d\u7f6e\u5b8c\u6210", - "unknown": "\u53d1\u751f\u672a\u77e5\u9519\u8bef\u3002" - }, - "error": { - "invalid_2fa": "\u53cc\u91cd\u8ba4\u8bc1\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5\u3002", - "invalid_2fa_method": "\u65e0\u6548\u7684\u53cc\u91cd\u8ba4\u8bc1\u65b9\u6cd5\uff08\u7535\u8bdd\u9a8c\u8bc1\uff09\u3002", - "invalid_login": "\u767b\u9646\u5931\u8d25\uff0c\u8bf7\u518d\u8bd5\u4e00\u6b21\u3002" - }, - "step": { - "2fa": { - "data": { - "2fa": "2FA Pin" - }, - "description": "\u65e0", - "title": "\u53cc\u91cd\u8ba4\u8bc1" - }, - "user": { - "data": { - "email": "\u7535\u5b50\u90ae\u4ef6\u5730\u5740", - "password": "\u5bc6\u7801" - }, - "description": "\u65e0", - "title": "\u767b\u5f55 Google Hangouts" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/hangouts/translations/zh-Hant.json b/homeassistant/components/hangouts/translations/zh-Hant.json deleted file mode 100644 index f9884d5e214..00000000000 --- a/homeassistant/components/hangouts/translations/zh-Hant.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4" - }, - "error": { - "invalid_2fa": "\u96d9\u91cd\u8a8d\u8b49\u7121\u6548\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002", - "invalid_2fa_method": "\u5169\u968e\u6bb5\u8a8d\u8b49\u65b9\u5f0f\u7121\u6548\uff08\u65bc\u96fb\u8a71\u4e0a\u9a57\u8b49\uff09\u3002", - "invalid_login": "\u767b\u5165\u5931\u6557\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002" - }, - "step": { - "2fa": { - "data": { - "2fa": "\u5169\u968e\u6bb5\u8a8d\u8b49\u78bc" - }, - "description": "\u7a7a\u767d", - "title": "\u96d9\u91cd\u8a8d\u8b49" - }, - "user": { - "data": { - "authorization_code": "\u9a57\u8b49\u78bc\uff08\u624b\u52d5\u9a57\u8b49\u5fc5\u9808\uff09", - "email": "\u96fb\u5b50\u90f5\u4ef6", - "password": "\u5bc6\u78bc" - }, - "description": "\u7a7a\u767d", - "title": "\u767b\u5165 Google Chat" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 33579c6b5ee..4d7ed69257d 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -162,7 +162,6 @@ FLOWS = { "growatt_server", "guardian", "habitica", - "hangouts", "harmony", "heos", "here_travel_time", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index b407decbd34..ebd7d4f934e 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -2022,12 +2022,6 @@ "iot_class": "local_polling", "name": "Google Cast" }, - "hangouts": { - "integration_type": "hub", - "config_flow": true, - "iot_class": "cloud_push", - "name": "Google Chat" - }, "dialogflow": { "integration_type": "hub", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 396e0a9206e..bf2e3bf419d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -847,9 +847,6 @@ ha-philipsjs==2.9.0 # homeassistant.components.habitica habitipy==0.2.0 -# homeassistant.components.hangouts -hangups==0.4.18 - # homeassistant.components.cloud hass-nabucasa==0.56.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a66569a76bc..5b6f2b2668c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -639,9 +639,6 @@ ha-philipsjs==2.9.0 # homeassistant.components.habitica habitipy==0.2.0 -# homeassistant.components.hangouts -hangups==0.4.18 - # homeassistant.components.cloud hass-nabucasa==0.56.0 diff --git a/tests/components/hangouts/__init__.py b/tests/components/hangouts/__init__.py deleted file mode 100644 index 81174356c2e..00000000000 --- a/tests/components/hangouts/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the Hangouts Component.""" diff --git a/tests/components/hangouts/test_config_flow.py b/tests/components/hangouts/test_config_flow.py deleted file mode 100644 index 5df675a0f05..00000000000 --- a/tests/components/hangouts/test_config_flow.py +++ /dev/null @@ -1,132 +0,0 @@ -"""Tests for the Google Hangouts config flow.""" - -from unittest.mock import patch - -from homeassistant import data_entry_flow -from homeassistant.components.hangouts import config_flow -from homeassistant.const import CONF_EMAIL, CONF_PASSWORD - -EMAIL = "test@test.com" -PASSWORD = "1232456" - - -async def test_flow_works(hass, aioclient_mock): - """Test config flow without 2fa.""" - flow = config_flow.HangoutsFlowHandler() - - flow.hass = hass - - with patch("homeassistant.components.hangouts.config_flow.get_auth"): - result = await flow.async_step_user( - {CONF_EMAIL: EMAIL, CONF_PASSWORD: PASSWORD} - ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["title"] == EMAIL - - -async def test_flow_works_with_authcode(hass, aioclient_mock): - """Test config flow without 2fa.""" - flow = config_flow.HangoutsFlowHandler() - - flow.hass = hass - - with patch("homeassistant.components.hangouts.config_flow.get_auth"): - result = await flow.async_step_user( - { - CONF_EMAIL: EMAIL, - CONF_PASSWORD: PASSWORD, - "authorization_code": "c29tZXJhbmRvbXN0cmluZw==", - } - ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["title"] == EMAIL - - -async def test_flow_works_with_2fa(hass, aioclient_mock): - """Test config flow with 2fa.""" - from homeassistant.components.hangouts.hangups_utils import Google2FAError - - flow = config_flow.HangoutsFlowHandler() - - flow.hass = hass - - with patch( - "homeassistant.components.hangouts.config_flow.get_auth", - side_effect=Google2FAError, - ): - result = await flow.async_step_user( - {CONF_EMAIL: EMAIL, CONF_PASSWORD: PASSWORD} - ) - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["step_id"] == "2fa" - - with patch("homeassistant.components.hangouts.config_flow.get_auth"): - result = await flow.async_step_2fa({"2fa": 123456}) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["title"] == EMAIL - - -async def test_flow_with_unknown_2fa(hass, aioclient_mock): - """Test config flow with invalid 2fa method.""" - from homeassistant.components.hangouts.hangups_utils import GoogleAuthError - - flow = config_flow.HangoutsFlowHandler() - - flow.hass = hass - - with patch( - "homeassistant.components.hangouts.config_flow.get_auth", - side_effect=GoogleAuthError("Unknown verification code input"), - ): - result = await flow.async_step_user( - {CONF_EMAIL: EMAIL, CONF_PASSWORD: PASSWORD} - ) - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["errors"]["base"] == "invalid_2fa_method" - - -async def test_flow_invalid_login(hass, aioclient_mock): - """Test config flow with invalid 2fa method.""" - from homeassistant.components.hangouts.hangups_utils import GoogleAuthError - - flow = config_flow.HangoutsFlowHandler() - - flow.hass = hass - - with patch( - "homeassistant.components.hangouts.config_flow.get_auth", - side_effect=GoogleAuthError, - ): - result = await flow.async_step_user( - {CONF_EMAIL: EMAIL, CONF_PASSWORD: PASSWORD} - ) - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["errors"]["base"] == "invalid_login" - - -async def test_flow_invalid_2fa(hass, aioclient_mock): - """Test config flow with 2fa.""" - from homeassistant.components.hangouts.hangups_utils import Google2FAError - - flow = config_flow.HangoutsFlowHandler() - - flow.hass = hass - - with patch( - "homeassistant.components.hangouts.config_flow.get_auth", - side_effect=Google2FAError, - ): - result = await flow.async_step_user( - {CONF_EMAIL: EMAIL, CONF_PASSWORD: PASSWORD} - ) - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["step_id"] == "2fa" - - with patch( - "homeassistant.components.hangouts.config_flow.get_auth", - side_effect=Google2FAError, - ): - result = await flow.async_step_2fa({"2fa": 123456}) - - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["errors"]["base"] == "invalid_2fa" From caa99ea9fb2d6c1325f4d4de80e8ceb8c46f01cc Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 24 Nov 2022 18:55:53 +0100 Subject: [PATCH 0681/1033] Allow next_step to be a string (#82647) * Allow next_step to be a string * Adjust docstrings * Add test --- .../components/scrape/config_flow.py | 2 +- .../helpers/schema_config_entry_flow.py | 29 ++++++++++++------- .../helpers/test_schema_config_entry_flow.py | 7 ++++- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/scrape/config_flow.py b/homeassistant/components/scrape/config_flow.py index 034667a4a76..6bb68d2fdec 100644 --- a/homeassistant/components/scrape/config_flow.py +++ b/homeassistant/components/scrape/config_flow.py @@ -134,7 +134,7 @@ DATA_SCHEMA_SENSOR = vol.Schema(SENSOR_SETUP) CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { "user": SchemaFlowFormStep( schema=DATA_SCHEMA_RESOURCE, - next_step=lambda _: "sensor", + next_step="sensor", validate_user_input=validate_rest_setup, ), "sensor": SchemaFlowFormStep( diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index c1936b4bd1e..2154ebc216f 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -32,18 +32,22 @@ class SchemaFlowFormStep: vol.Schema | None, ] | None - # Optional function to validate user input. - # The validate_user_input function is called if the schema validates successfully. - # The validate_user_input function is passed the user input from the current step. - # The validate_user_input should raise SchemaFlowError is user input is invalid. validate_user_input: Callable[[dict[str, Any]], dict[str, Any]] = lambda x: x + """Optional function to validate user input. - # Optional function to identify next step. - # The next_step function is called if the schema validates successfully or if no - # schema is defined. The next_step function is passed the union of config entry - # options and user input from previous steps. - # If next_step is None, the flow is ended with FlowResultType.CREATE_ENTRY. - next_step: Callable[[dict[str, Any]], str] | None = None + - The `validate_user_input` function is called if the schema validates successfully. + - The `validate_user_input` function is passed the user input from the current step. + - The `validate_user_input` should raise `SchemaFlowError` is user input is invalid. + """ + + next_step: Callable[[dict[str, Any]], str] | str | None = None + """Optional property to identify next step. + + - If `next_step` is a function, it is called if the schema validates successfully or + if no schema is defined. The `next_step` function is passed the union of config entry + options and user input from previous steps. + - If `next_step` is None, the flow is ended with `FlowResultType.CREATE_ENTRY`. + """ # Optional function to allow amending a form schema. # The update_form_schema function is called before async_show_form is called. The @@ -140,7 +144,10 @@ class SchemaCommonFlowHandler: # Flow done, create entry or update config entry options return self._handler.async_create_entry(data=self._options) - next_step_id = form_step.next_step(self._options) + if isinstance(form_step.next_step, str): + next_step_id = form_step.next_step + else: + next_step_id = form_step.next_step(self._options) return self._show_next_step(next_step_id) diff --git a/tests/helpers/test_schema_config_entry_flow.py b/tests/helpers/test_schema_config_entry_flow.py index e54582db606..7ea5768fcad 100644 --- a/tests/helpers/test_schema_config_entry_flow.py +++ b/tests/helpers/test_schema_config_entry_flow.py @@ -29,7 +29,8 @@ async def test_menu_step(hass: HomeAssistant) -> None: "user": SchemaFlowMenuStep(MENU_1), "option1": SchemaFlowFormStep(vol.Schema({}), next_step=lambda _: "menu2"), "menu2": SchemaFlowMenuStep(MENU_2), - "option3": SchemaFlowFormStep(vol.Schema({})), + "option3": SchemaFlowFormStep(vol.Schema({}), next_step="option4"), + "option4": SchemaFlowFormStep(vol.Schema({})), } class TestConfigFlow(SchemaConfigFlowHandler, domain=TEST_DOMAIN): @@ -63,5 +64,9 @@ async def test_menu_step(hass: HomeAssistant) -> None: assert result["type"] == FlowResultType.FORM assert result["step_id"] == "option3" + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "option4" + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.CREATE_ENTRY From 1e68e8c4b4836c9aabe5451426053428b2af905c Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Thu, 24 Nov 2022 20:07:19 +0200 Subject: [PATCH 0682/1033] Add Shelly tests coverage (#82642) * Add Shelly tests coverage * Review comments * Remove leftovers --- .coveragerc | 2 - .../components/shelly/coordinator.py | 19 +- tests/components/shelly/__init__.py | 6 +- .../shelly/bluetooth/test_scanner.py | 13 + tests/components/shelly/conftest.py | 6 +- tests/components/shelly/test_binary_sensor.py | 24 + tests/components/shelly/test_config_flow.py | 24 +- tests/components/shelly/test_coordinator.py | 531 ++++++++++++++++++ tests/components/shelly/test_utils.py | 225 ++++++++ 9 files changed, 818 insertions(+), 32 deletions(-) create mode 100644 tests/components/shelly/test_coordinator.py create mode 100644 tests/components/shelly/test_utils.py diff --git a/.coveragerc b/.coveragerc index 511c57a2ce9..6322dc0f7b3 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1105,8 +1105,6 @@ omit = homeassistant/components/sesame/lock.py homeassistant/components/seven_segments/image_processing.py homeassistant/components/seventeentrack/sensor.py - homeassistant/components/shelly/coordinator.py - homeassistant/components/shelly/utils.py homeassistant/components/shiftr/* homeassistant/components/shodan/sensor.py homeassistant/components/sia/__init__.py diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py index 11bdf933037..6190181dca9 100644 --- a/homeassistant/components/shelly/coordinator.py +++ b/homeassistant/components/shelly/coordinator.py @@ -169,6 +169,7 @@ class ShellyBlockCoordinator(DataUpdateCoordinator): "inputEvent" not in block.sensor_ids or "inputEventCnt" not in block.sensor_ids ): + LOGGER.debug("Skipping non-input event block %s", block.description) continue channel = int(block.channel or 0) + 1 @@ -181,6 +182,7 @@ class ShellyBlockCoordinator(DataUpdateCoordinator): or last_event_count == block.inputEventCnt or event_type == "" ): + LOGGER.debug("Skipping block event %s", event_type) continue if event_type in INPUTS_EVENTS_DICT: @@ -194,12 +196,6 @@ class ShellyBlockCoordinator(DataUpdateCoordinator): ATTR_GENERATION: 1, }, ) - else: - LOGGER.warning( - "Shelly input event %s for device %s is not supported, please open issue", - event_type, - self.name, - ) if self._last_cfg_changed is not None and cfg_changed > self._last_cfg_changed: LOGGER.info( @@ -610,11 +606,6 @@ class ShellyRpcPollingCoordinator(DataUpdateCoordinator): except InvalidAuthError: self.entry.async_start_reauth(self.hass) - @property - def model(self) -> str: - """Model of the device.""" - return cast(str, self.entry.data["model"]) - @property def mac(self) -> str: """Mac address of the device.""" @@ -625,9 +616,6 @@ def get_block_coordinator_by_device_id( hass: HomeAssistant, device_id: str ) -> ShellyBlockCoordinator | None: """Get a Shelly block device coordinator for the given device id.""" - if not hass.data.get(DOMAIN): - return None - dev_reg = device_registry.async_get(hass) if device := dev_reg.async_get(device_id): for config_entry in device.config_entries: @@ -644,9 +632,6 @@ def get_rpc_coordinator_by_device_id( hass: HomeAssistant, device_id: str ) -> ShellyRpcCoordinator | None: """Get a Shelly RPC device coordinator for the given device id.""" - if not hass.data.get(DOMAIN): - return None - dev_reg = device_registry.async_get(hass) if device := dev_reg.async_get(device_id): for config_entry in device.config_entries: diff --git a/tests/components/shelly/__init__.py b/tests/components/shelly/__init__.py index 36a43984c4a..b58b147a9a3 100644 --- a/tests/components/shelly/__init__.py +++ b/tests/components/shelly/__init__.py @@ -78,11 +78,9 @@ def inject_rpc_device_event( mock_rpc_device.mock_event() -async def mock_rest_update(hass: HomeAssistant): +async def mock_rest_update(hass: HomeAssistant, seconds=REST_SENSORS_UPDATE_INTERVAL): """Move time to create REST sensors update event.""" - async_fire_time_changed( - hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL) - ) + async_fire_time_changed(hass, dt.utcnow() + timedelta(seconds=seconds)) await hass.async_block_till_done() diff --git a/tests/components/shelly/bluetooth/test_scanner.py b/tests/components/shelly/bluetooth/test_scanner.py index 8a429e20532..160dc557897 100644 --- a/tests/components/shelly/bluetooth/test_scanner.py +++ b/tests/components/shelly/bluetooth/test_scanner.py @@ -104,6 +104,19 @@ async def test_scanner_ignores_wrong_version_and_logs( assert "Unsupported BLE scan result version: 0" in caplog.text +async def test_scanner_minimum_firmware_log_error( + hass, mock_rpc_device, monkeypatch, caplog +): + """Test scanner log error if device firmware incompatible.""" + monkeypatch.setattr(mock_rpc_device, "version", "0.11.0") + await init_integration( + hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} + ) + assert mock_rpc_device.initialized is True + + assert "BLE not supported on device" in caplog.text + + async def test_scanner_warns_on_corrupt_event( hass, mock_rpc_device, monkeypatch, caplog ): diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py index bfa8e903155..80e53ac6796 100644 --- a/tests/components/shelly/conftest.py +++ b/tests/components/shelly/conftest.py @@ -90,7 +90,7 @@ MOCK_BLOCKS = [ ), ), Mock( - sensor_ids={}, + sensor_ids={"mode": "color", "effect": 0}, channel="0", output=mock_light_set_state()["ison"], colorTemp=mock_light_set_state()["temp"], @@ -115,6 +115,8 @@ MOCK_BLOCKS = [ cfgChanged=0, mode=0, valvePos=50, + inputEvent="S", + wakeupEvent=["button"], description="device_0", type="device", ), @@ -127,6 +129,7 @@ MOCK_CONFIG = { "sys": { "ui_data": {}, "device": {"name": "Test name"}, + "wakeup_period": 0, }, } @@ -255,6 +258,7 @@ def _mock_rpc_device(version: str | None = None): event={}, shelly=MOCK_SHELLY_RPC, version=version or "0.12.0", + hostname="test-host", status=MOCK_STATUS_RPC, firmware_version="some fw string", initialized=True, diff --git a/tests/components/shelly/test_binary_sensor.py b/tests/components/shelly/test_binary_sensor.py index 6257bd191e6..b39a395d11b 100644 --- a/tests/components/shelly/test_binary_sensor.py +++ b/tests/components/shelly/test_binary_sensor.py @@ -2,6 +2,7 @@ from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN +from homeassistant.components.shelly.const import SLEEP_PERIOD_MULTIPLIER from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.core import State from homeassistant.helpers.entity_registry import async_get @@ -66,6 +67,29 @@ async def test_block_rest_binary_sensor(hass, mock_block_device, monkeypatch): assert hass.states.get(entity_id).state == STATE_ON +async def test_block_rest_binary_sensor_connected_battery_devices( + hass, mock_block_device, monkeypatch +): + """Test block REST binary sensor for connected battery devices.""" + entity_id = register_entity(hass, BINARY_SENSOR_DOMAIN, "test_name_cloud", "cloud") + monkeypatch.setitem(mock_block_device.status, "cloud", {"connected": False}) + monkeypatch.setitem(mock_block_device.settings["device"], "type", "SHMOS-01") + monkeypatch.setitem(mock_block_device.settings["coiot"], "update_period", 3600) + await init_integration(hass, 1, model="SHMOS-01") + + assert hass.states.get(entity_id).state == STATE_OFF + + monkeypatch.setitem(mock_block_device.status["cloud"], "connected", True) + + # Verify no update on fast intervals + await mock_rest_update(hass) + assert hass.states.get(entity_id).state == STATE_OFF + + # Verify update on slow intervals + await mock_rest_update(hass, seconds=SLEEP_PERIOD_MULTIPLIER * 3600) + assert hass.states.get(entity_id).state == STATE_ON + + async def test_block_sleeping_binary_sensor(hass, mock_block_device, monkeypatch): """Test block sleeping binary sensor.""" entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_motion" diff --git a/tests/components/shelly/test_config_flow.py b/tests/components/shelly/test_config_flow.py index 29f2804054f..1a1acea16a3 100644 --- a/tests/components/shelly/test_config_flow.py +++ b/tests/components/shelly/test_config_flow.py @@ -522,18 +522,26 @@ async def test_form_auth_errors_test_connection_gen2(hass, error): assert result3["errors"] == {"base": base_error} -async def test_zeroconf(hass): +@pytest.mark.parametrize( + "gen, get_info", + [ + (1, {"mac": "test-mac", "type": "SHSW-1", "auth": False, "gen": 1}), + (2, {"mac": "test-mac", "model": "SHSW-1", "auth": False, "gen": 2}), + ], +) +async def test_zeroconf(hass, gen, get_info): """Test we get the form.""" - with patch( - "aioshelly.common.get_info", - return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False}, - ), patch( + with patch("aioshelly.common.get_info", return_value=get_info), patch( "aioshelly.block_device.BlockDevice.create", + new=AsyncMock(return_value=Mock(model="SHSW-1", settings=MOCK_SETTINGS)), + ), patch( + "aioshelly.rpc_device.RpcDevice.create", new=AsyncMock( return_value=Mock( - model="SHSW-1", - settings=MOCK_SETTINGS, + shelly={"model": "SHSW-1", "gen": gen}, + config=MOCK_CONFIG, + shutdown=AsyncMock(), ) ), ): @@ -569,7 +577,7 @@ async def test_zeroconf(hass): "host": "1.1.1.1", "model": "SHSW-1", "sleep_period": 0, - "gen": 1, + "gen": gen, } assert len(mock_setup.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1 diff --git a/tests/components/shelly/test_coordinator.py b/tests/components/shelly/test_coordinator.py new file mode 100644 index 00000000000..eab5c21113a --- /dev/null +++ b/tests/components/shelly/test_coordinator.py @@ -0,0 +1,531 @@ +"""Tests for Shelly coordinator.""" +from datetime import timedelta +from unittest.mock import AsyncMock + +from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError + +from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.components.shelly.const import ( + ATTR_CHANNEL, + ATTR_CLICK_TYPE, + ATTR_DEVICE, + ATTR_GENERATION, + DOMAIN, + ENTRY_RELOAD_COOLDOWN, + RPC_RECONNECT_INTERVAL, + SLEEP_PERIOD_MULTIPLIER, + UPDATE_PERIOD_MULTIPLIER, +) +from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState +from homeassistant.const import ATTR_DEVICE_ID, STATE_ON, STATE_UNAVAILABLE +from homeassistant.helpers.device_registry import ( + async_entries_for_config_entry, + async_get as async_get_dev_reg, +) +from homeassistant.util import dt + +from . import ( + init_integration, + inject_rpc_device_event, + mock_polling_rpc_update, + mock_rest_update, + register_entity, +) + +from tests.common import async_fire_time_changed + +RELAY_BLOCK_ID = 0 +LIGHT_BLOCK_ID = 2 +SENSOR_BLOCK_ID = 3 +DEVICE_BLOCK_ID = 4 + + +async def test_block_reload_on_cfg_change(hass, mock_block_device, monkeypatch): + """Test block reload on config change.""" + await init_integration(hass, 1) + + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "cfgChanged", 1) + mock_block_device.mock_update() + await hass.async_block_till_done() + + # Generate config change from switch to light + monkeypatch.setitem( + mock_block_device.settings["relays"][RELAY_BLOCK_ID], "appliance_type", "light" + ) + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "cfgChanged", 2) + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get("switch.test_name_channel_1") is not None + + # Wait for debouncer + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=ENTRY_RELOAD_COOLDOWN) + ) + await hass.async_block_till_done() + + assert hass.states.get("switch.test_name_channel_1") is None + + +async def test_block_no_reload_on_bulb_changes(hass, mock_block_device, monkeypatch): + """Test block no reload on bulb mode/effect change.""" + await init_integration(hass, 1, model="SHBLB-1") + + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "cfgChanged", 1) + mock_block_device.mock_update() + await hass.async_block_till_done() + + # Test no reload on mode change + monkeypatch.setitem( + mock_block_device.settings["relays"][RELAY_BLOCK_ID], "appliance_type", "light" + ) + monkeypatch.setattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "mode", "white") + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "cfgChanged", 2) + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get("switch.test_name_channel_1") is not None + + # Wait for debouncer + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=ENTRY_RELOAD_COOLDOWN) + ) + await hass.async_block_till_done() + + assert hass.states.get("switch.test_name_channel_1") is not None + + # Test no reload on effect change + monkeypatch.setattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "effect", 1) + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "cfgChanged", 3) + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get("switch.test_name_channel_1") is not None + + # Wait for debouncer + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=ENTRY_RELOAD_COOLDOWN) + ) + await hass.async_block_till_done() + + assert hass.states.get("switch.test_name_channel_1") is not None + + +async def test_block_polling_auth_error(hass, mock_block_device, monkeypatch): + """Test block device polling authentication error.""" + monkeypatch.setattr( + mock_block_device, + "update", + AsyncMock(side_effect=InvalidAuthError), + ) + entry = await init_integration(hass, 1) + + assert entry.state == ConfigEntryState.LOADED + + # Move time to generate polling + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=UPDATE_PERIOD_MULTIPLIER * 15) + ) + await hass.async_block_till_done() + + assert entry.state == ConfigEntryState.LOADED + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + + flow = flows[0] + assert flow.get("step_id") == "reauth_confirm" + assert flow.get("handler") == DOMAIN + + assert "context" in flow + assert flow["context"].get("source") == SOURCE_REAUTH + assert flow["context"].get("entry_id") == entry.entry_id + + +async def test_block_rest_update_auth_error(hass, mock_block_device, monkeypatch): + """Test block REST update authentication error.""" + register_entity(hass, BINARY_SENSOR_DOMAIN, "test_name_cloud", "cloud") + monkeypatch.setitem(mock_block_device.status, "cloud", {"connected": False}) + monkeypatch.setitem(mock_block_device.status, "uptime", 1) + entry = await init_integration(hass, 1) + + monkeypatch.setattr( + mock_block_device, + "update_shelly", + AsyncMock(side_effect=InvalidAuthError), + ) + + assert entry.state == ConfigEntryState.LOADED + + await mock_rest_update(hass) + + assert entry.state == ConfigEntryState.LOADED + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + + flow = flows[0] + assert flow.get("step_id") == "reauth_confirm" + assert flow.get("handler") == DOMAIN + + assert "context" in flow + assert flow["context"].get("source") == SOURCE_REAUTH + assert flow["context"].get("entry_id") == entry.entry_id + + +async def test_block_polling_connection_error(hass, mock_block_device, monkeypatch): + """Test block device polling connection error.""" + monkeypatch.setattr( + mock_block_device, + "update", + AsyncMock(side_effect=DeviceConnectionError), + ) + await init_integration(hass, 1) + + assert hass.states.get("switch.test_name_channel_1").state == STATE_ON + + # Move time to generate polling + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=UPDATE_PERIOD_MULTIPLIER * 15) + ) + await hass.async_block_till_done() + + assert hass.states.get("switch.test_name_channel_1").state == STATE_UNAVAILABLE + + +async def test_block_rest_update_connection_error(hass, mock_block_device, monkeypatch): + """Test block REST update connection error.""" + entity_id = register_entity(hass, BINARY_SENSOR_DOMAIN, "test_name_cloud", "cloud") + monkeypatch.setitem(mock_block_device.status, "cloud", {"connected": True}) + monkeypatch.setitem(mock_block_device.status, "uptime", 1) + await init_integration(hass, 1) + + await mock_rest_update(hass) + assert hass.states.get(entity_id).state == STATE_ON + + monkeypatch.setattr( + mock_block_device, + "update_shelly", + AsyncMock(side_effect=DeviceConnectionError), + ) + await mock_rest_update(hass) + + assert hass.states.get(entity_id).state == STATE_UNAVAILABLE + + +async def test_block_sleeping_device_no_periodic_updates(hass, mock_block_device): + """Test block sleeping device no periodic updates.""" + entity_id = f"{SENSOR_DOMAIN}.test_name_temperature" + await init_integration(hass, 1, sleep_period=1000) + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == "22.1" + + # Move time to generate polling + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=SLEEP_PERIOD_MULTIPLIER * 1000) + ) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == STATE_UNAVAILABLE + + +async def test_block_button_click_event(hass, mock_block_device, events, monkeypatch): + """Test block click event for Shelly button.""" + monkeypatch.setattr(mock_block_device.blocks[RELAY_BLOCK_ID], "sensor_ids", {}) + monkeypatch.setattr( + mock_block_device.blocks[DEVICE_BLOCK_ID], + "sensor_ids", + {"inputEvent": "S", "inputEventCnt": 0}, + ) + entry = await init_integration(hass, 1, model="SHBTN-1", sleep_period=1000) + + # Make device online + mock_block_device.mock_update() + await hass.async_block_till_done() + + dev_reg = async_get_dev_reg(hass) + device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0] + + # Generate button click event + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert len(events) == 1 + assert events[0].data == { + ATTR_DEVICE_ID: device.id, + ATTR_DEVICE: "test-host", + ATTR_CHANNEL: 1, + ATTR_CLICK_TYPE: "single", + ATTR_GENERATION: 1, + } + + # Test ignore empty event + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "inputEvent", "") + mock_block_device.mock_update() + await hass.async_block_till_done() + + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert len(events) == 1 + + +async def test_rpc_reload_on_cfg_change(hass, mock_rpc_device, monkeypatch): + """Test RPC reload on config change.""" + await init_integration(hass, 2) + + # Generate config change from switch to light + monkeypatch.setitem( + mock_rpc_device.config["sys"]["ui_data"], "consumption_types", ["lights"] + ) + inject_rpc_device_event( + monkeypatch, + mock_rpc_device, + { + "events": [ + { + "data": [], + "event": "config_changed", + "id": 1, + "ts": 1668522399.2, + }, + { + "data": [], + "id": 2, + "ts": 1668522399.2, + }, + ], + "ts": 1668522399.2, + }, + ) + await hass.async_block_till_done() + + assert hass.states.get("switch.test_switch_0") is not None + + # Wait for debouncer + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=ENTRY_RELOAD_COOLDOWN) + ) + await hass.async_block_till_done() + + assert hass.states.get("switch.test_switch_0") is None + + +async def test_rpc_click_event(hass, mock_rpc_device, events, monkeypatch): + """Test RPC click event.""" + entry = await init_integration(hass, 2) + + dev_reg = async_get_dev_reg(hass) + device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0] + + # Generate config change from switch to light + inject_rpc_device_event( + monkeypatch, + mock_rpc_device, + { + "events": [ + { + "data": [], + "event": "single_push", + "id": 0, + "ts": 1668522399.2, + } + ], + "ts": 1668522399.2, + }, + ) + await hass.async_block_till_done() + + assert len(events) == 1 + assert events[0].data == { + ATTR_DEVICE_ID: device.id, + ATTR_DEVICE: "test-host", + ATTR_CHANNEL: 1, + ATTR_CLICK_TYPE: "single_push", + ATTR_GENERATION: 2, + } + + +async def test_rpc_update_entry_sleep_period(hass, mock_rpc_device, monkeypatch): + """Test RPC update entry sleep period.""" + entry = await init_integration(hass, 2, sleep_period=600) + register_entity( + hass, + SENSOR_DOMAIN, + "test_name_temperature", + "temperature:0-temperature_0", + entry, + ) + + # Make device online + mock_rpc_device.mock_update() + await hass.async_block_till_done() + + assert entry.data["sleep_period"] == 600 + + # Move time to generate sleep period update + monkeypatch.setitem(mock_rpc_device.status["sys"], "wakeup_period", 3600) + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=600 * SLEEP_PERIOD_MULTIPLIER) + ) + await hass.async_block_till_done() + + assert entry.data["sleep_period"] == 3600 + + +async def test_rpc_sleeping_device_no_periodic_updates( + hass, mock_rpc_device, monkeypatch +): + """Test RPC sleeping device no periodic updates.""" + entity_id = f"{SENSOR_DOMAIN}.test_name_temperature" + entry = await init_integration(hass, 2, sleep_period=1000) + register_entity( + hass, + SENSOR_DOMAIN, + "test_name_temperature", + "temperature:0-temperature_0", + entry, + ) + + # Make device online + mock_rpc_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == "22.9" + + # Move time to generate polling + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=SLEEP_PERIOD_MULTIPLIER * 1000) + ) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == STATE_UNAVAILABLE + + +async def test_rpc_reconnect_auth_error(hass, mock_rpc_device, monkeypatch): + """Test RPC reconnect authentication error.""" + entry = await init_integration(hass, 2) + + monkeypatch.setattr(mock_rpc_device, "connected", False) + monkeypatch.setattr( + mock_rpc_device, + "initialize", + AsyncMock( + side_effect=InvalidAuthError, + ), + ) + + assert entry.state == ConfigEntryState.LOADED + + # Move time to generate reconnect + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=RPC_RECONNECT_INTERVAL) + ) + await hass.async_block_till_done() + + assert entry.state == ConfigEntryState.LOADED + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + + flow = flows[0] + assert flow.get("step_id") == "reauth_confirm" + assert flow.get("handler") == DOMAIN + + assert "context" in flow + assert flow["context"].get("source") == SOURCE_REAUTH + assert flow["context"].get("entry_id") == entry.entry_id + + +async def test_rpc_polling_auth_error(hass, mock_rpc_device, monkeypatch) -> None: + """Test RPC polling authentication error.""" + register_entity(hass, SENSOR_DOMAIN, "test_name_rssi", "wifi-rssi") + entry = await init_integration(hass, 2) + + monkeypatch.setattr( + mock_rpc_device, + "update_status", + AsyncMock( + side_effect=InvalidAuthError, + ), + ) + + assert entry.state == ConfigEntryState.LOADED + + await mock_polling_rpc_update(hass) + + assert entry.state == ConfigEntryState.LOADED + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + + flow = flows[0] + assert flow.get("step_id") == "reauth_confirm" + assert flow.get("handler") == DOMAIN + + assert "context" in flow + assert flow["context"].get("source") == SOURCE_REAUTH + assert flow["context"].get("entry_id") == entry.entry_id + + +async def test_rpc_reconnect_error(hass, mock_rpc_device, monkeypatch): + """Test RPC reconnect error.""" + await init_integration(hass, 2) + + assert hass.states.get("switch.test_switch_0").state == STATE_ON + + monkeypatch.setattr(mock_rpc_device, "connected", False) + monkeypatch.setattr( + mock_rpc_device, + "initialize", + AsyncMock( + side_effect=DeviceConnectionError, + ), + ) + + # Move time to generate reconnect + async_fire_time_changed( + hass, dt.utcnow() + timedelta(seconds=RPC_RECONNECT_INTERVAL) + ) + await hass.async_block_till_done() + + assert hass.states.get("switch.test_switch_0").state == STATE_UNAVAILABLE + + +async def test_rpc_polling_connection_error(hass, mock_rpc_device, monkeypatch) -> None: + """Test RPC polling connection error.""" + entity_id = register_entity(hass, SENSOR_DOMAIN, "test_name_rssi", "wifi-rssi") + await init_integration(hass, 2) + + monkeypatch.setattr( + mock_rpc_device, + "update_status", + AsyncMock( + side_effect=DeviceConnectionError, + ), + ) + + assert hass.states.get(entity_id).state == "-63" + + await mock_polling_rpc_update(hass) + + assert hass.states.get(entity_id).state == STATE_UNAVAILABLE + + +async def test_rpc_polling_disconnected(hass, mock_rpc_device, monkeypatch) -> None: + """Test RPC polling device disconnected.""" + entity_id = register_entity(hass, SENSOR_DOMAIN, "test_name_rssi", "wifi-rssi") + await init_integration(hass, 2) + + monkeypatch.setattr(mock_rpc_device, "connected", False) + + assert hass.states.get(entity_id).state == "-63" + + await mock_polling_rpc_update(hass) + + assert hass.states.get(entity_id).state == STATE_UNAVAILABLE diff --git a/tests/components/shelly/test_utils.py b/tests/components/shelly/test_utils.py new file mode 100644 index 00000000000..c78adf4c37c --- /dev/null +++ b/tests/components/shelly/test_utils.py @@ -0,0 +1,225 @@ +"""Tests for Shelly utils.""" +from freezegun import freeze_time +import pytest + +from homeassistant.components.shelly.utils import ( + get_block_channel_name, + get_block_device_sleep_period, + get_block_input_triggers, + get_device_uptime, + get_number_of_channels, + get_rpc_channel_name, + get_rpc_input_triggers, + is_block_momentary_input, +) +from homeassistant.util import dt + +DEVICE_BLOCK_ID = 4 + + +async def test_block_get_number_of_channels(mock_block_device, monkeypatch): + """Test block get number of channels.""" + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "type", "emeter") + monkeypatch.setitem(mock_block_device.shelly, "num_emeters", 3) + + assert ( + get_number_of_channels( + mock_block_device, + mock_block_device.blocks[DEVICE_BLOCK_ID], + ) + == 3 + ) + + monkeypatch.setitem(mock_block_device.shelly, "num_inputs", 4) + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "type", "input") + assert ( + get_number_of_channels( + mock_block_device, + mock_block_device.blocks[DEVICE_BLOCK_ID], + ) + == 4 + ) + + monkeypatch.setitem(mock_block_device.settings["device"], "type", "SHDM-2") + assert ( + get_number_of_channels( + mock_block_device, + mock_block_device.blocks[DEVICE_BLOCK_ID], + ) + == 2 + ) + + +async def test_block_get_block_channel_name(mock_block_device, monkeypatch): + """Test block get block channel name.""" + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "type", "relay") + + assert ( + get_block_channel_name( + mock_block_device, + mock_block_device.blocks[DEVICE_BLOCK_ID], + ) + == "Test name channel 1" + ) + + monkeypatch.setitem(mock_block_device.settings["device"], "type", "SHEM-3") + + assert ( + get_block_channel_name( + mock_block_device, + mock_block_device.blocks[DEVICE_BLOCK_ID], + ) + == "Test name channel A" + ) + + monkeypatch.setitem( + mock_block_device.settings, "relays", [{"name": "test-channel"}] + ) + + assert ( + get_block_channel_name( + mock_block_device, + mock_block_device.blocks[DEVICE_BLOCK_ID], + ) + == "test-channel" + ) + + +async def test_is_block_momentary_input(mock_block_device, monkeypatch): + """Test is block momentary input.""" + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "type", "relay") + + monkeypatch.setitem(mock_block_device.settings, "mode", "roller") + monkeypatch.setitem( + mock_block_device.settings, "rollers", [{"button_type": "detached"}] + ) + assert ( + is_block_momentary_input( + mock_block_device.settings, + mock_block_device.blocks[DEVICE_BLOCK_ID], + ) + is False + ) + assert ( + is_block_momentary_input( + mock_block_device.settings, mock_block_device.blocks[DEVICE_BLOCK_ID], True + ) + is True + ) + + monkeypatch.setitem(mock_block_device.settings, "mode", "relay") + monkeypatch.setitem(mock_block_device.settings["device"], "type", "SHSW-L") + assert ( + is_block_momentary_input( + mock_block_device.settings, mock_block_device.blocks[DEVICE_BLOCK_ID], True + ) + is False + ) + + monkeypatch.delitem(mock_block_device.settings, "relays") + monkeypatch.delitem(mock_block_device.settings, "rollers") + assert ( + is_block_momentary_input( + mock_block_device.settings, + mock_block_device.blocks[DEVICE_BLOCK_ID], + ) + is False + ) + + monkeypatch.setitem(mock_block_device.settings["device"], "type", "SHBTN-2") + + assert ( + is_block_momentary_input( + mock_block_device.settings, + mock_block_device.blocks[DEVICE_BLOCK_ID], + ) + is True + ) + + +@pytest.mark.parametrize( + "settings, sleep_period", + [ + ({}, 0), + ({"sleep_mode": {"period": 1000, "unit": "m"}}, 1000 * 60), + ({"sleep_mode": {"period": 5, "unit": "h"}}, 5 * 3600), + ], +) +async def test_get_block_device_sleep_period(settings, sleep_period): + """Test get block device sleep period.""" + assert get_block_device_sleep_period(settings) == sleep_period + + +@freeze_time("2019-01-10 18:43:00+00:00") +async def test_get_device_uptime(): + """Test block test get device uptime.""" + assert get_device_uptime( + 55, dt.as_utc(dt.parse_datetime("2019-01-10 18:42:00+00:00")) + ) == dt.as_utc(dt.parse_datetime("2019-01-10 18:42:00+00:00")) + + assert get_device_uptime( + 50, dt.as_utc(dt.parse_datetime("2019-01-10 18:42:00+00:00")) + ) == dt.as_utc(dt.parse_datetime("2019-01-10 18:42:10+00:00")) + + +async def test_get_block_input_triggers(mock_block_device, monkeypatch): + """Test get block input triggers.""" + monkeypatch.setattr( + mock_block_device.blocks[DEVICE_BLOCK_ID], + "sensor_ids", + {"inputEvent": "S", "inputEventCnt": 0}, + ) + monkeypatch.setitem( + mock_block_device.settings, "rollers", [{"button_type": "detached"}] + ) + assert set( + get_block_input_triggers( + mock_block_device, mock_block_device.blocks[DEVICE_BLOCK_ID] + ) + ) == {("long", "button"), ("single", "button")} + + monkeypatch.setitem(mock_block_device.settings["device"], "type", "SHBTN-1") + assert set( + get_block_input_triggers( + mock_block_device, mock_block_device.blocks[DEVICE_BLOCK_ID] + ) + ) == { + ("long", "button"), + ("double", "button"), + ("single", "button"), + ("triple", "button"), + } + + monkeypatch.setitem(mock_block_device.settings["device"], "type", "SHIX3-1") + assert set( + get_block_input_triggers( + mock_block_device, mock_block_device.blocks[DEVICE_BLOCK_ID] + ) + ) == { + ("long_single", "button"), + ("single_long", "button"), + ("triple", "button"), + ("long", "button"), + ("single", "button"), + ("double", "button"), + } + + +async def test_get_rpc_channel_name(mock_rpc_device): + """Test get RPC channel name.""" + assert get_rpc_channel_name(mock_rpc_device, "input:0") == "test switch_0" + + +async def test_get_rpc_input_triggers(mock_rpc_device, monkeypatch): + """Test get RPC input triggers.""" + monkeypatch.setattr(mock_rpc_device, "config", {"input:0": {"type": "button"}}) + assert set(get_rpc_input_triggers(mock_rpc_device)) == { + ("long_push", "button1"), + ("single_push", "button1"), + ("btn_down", "button1"), + ("double_push", "button1"), + ("btn_up", "button1"), + } + + monkeypatch.setattr(mock_rpc_device, "config", {"input:0": {"type": "switch"}}) + assert not get_rpc_input_triggers(mock_rpc_device) From cd2377bc054ebe4c5c0432aac525d768dcfbe57a Mon Sep 17 00:00:00 2001 From: rappenze Date: Thu, 24 Nov 2022 19:16:33 +0100 Subject: [PATCH 0683/1033] Support hvacsystem in fibaro integration (#78234) fixes undefined --- homeassistant/components/fibaro/__init__.py | 1 + homeassistant/components/fibaro/climate.py | 105 +++++++++++++++----- 2 files changed, 81 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/fibaro/__init__.py b/homeassistant/components/fibaro/__init__.py index 19f36742740..6bf86be7296 100644 --- a/homeassistant/components/fibaro/__init__.py +++ b/homeassistant/components/fibaro/__init__.py @@ -79,6 +79,7 @@ FIBARO_TYPEMAP = { "com.fibaro.colorController": Platform.LIGHT, "com.fibaro.securitySensor": Platform.BINARY_SENSOR, "com.fibaro.hvac": Platform.CLIMATE, + "com.fibaro.hvacSystem": Platform.CLIMATE, "com.fibaro.setpoint": Platform.CLIMATE, "com.fibaro.FGT001": Platform.CLIMATE, "com.fibaro.thermostatDanfoss": Platform.CLIMATE, diff --git a/homeassistant/components/fibaro/climate.py b/homeassistant/components/fibaro/climate.py index be2b8c6a527..7731b2544c4 100644 --- a/homeassistant/components/fibaro/climate.py +++ b/homeassistant/components/fibaro/climate.py @@ -1,6 +1,7 @@ """Support for Fibaro thermostats.""" from __future__ import annotations +from contextlib import suppress import logging from typing import Any @@ -10,6 +11,7 @@ from homeassistant.components.climate import ( PRESET_BOOST, ClimateEntity, ClimateEntityFeature, + HVACAction, HVACMode, ) from homeassistant.config_entries import ConfigEntry @@ -98,6 +100,14 @@ HA_OPMODES_HVAC = { HVACMode.FAN_ONLY: 6, } +TARGET_TEMP_ACTIONS = ( + "setTargetLevel", + "setThermostatSetpoint", + "setHeatingThermostatSetpoint", +) + +OP_MODE_ACTIONS = ("setMode", "setOperatingMode", "setThermostatMode") + async def async_setup_entry( hass: HomeAssistant, @@ -149,16 +159,14 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): self._temp_sensor_device = FibaroDevice(device) tempunit = device.properties.unit - if ( - "setTargetLevel" in device.actions - or "setThermostatSetpoint" in device.actions - or "setHeatingThermostatSetpoint" in device.actions + if any( + action for action in TARGET_TEMP_ACTIONS if action in device.actions ): self._target_temp_device = FibaroDevice(device) self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE tempunit = device.properties.unit - if "setMode" in device.actions or "setOperatingMode" in device.actions: + if any(action for action in OP_MODE_ACTIONS if action in device.actions): self._op_mode_device = FibaroDevice(device) self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE @@ -188,18 +196,27 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): self._attr_preset_modes = [] self._attr_hvac_modes = [] prop = self._op_mode_device.fibaro_device.properties - if "supportedOperatingModes" in prop: - op_modes = prop.supportedOperatingModes.split(",") - elif "supportedModes" in prop: - op_modes = prop.supportedModes.split(",") - for mode in op_modes: - mode = int(mode) - if mode in OPMODES_HVAC: - mode_ha = OPMODES_HVAC[mode] - if mode_ha not in self._attr_hvac_modes: + if "supportedThermostatModes" in prop: + for mode in prop.supportedThermostatModes: + try: + self._attr_hvac_modes.append(HVACMode(mode.lower())) + except ValueError: + self._attr_preset_modes.append(mode) + else: + if "supportedOperatingModes" in prop: + op_modes = prop.supportedOperatingModes.split(",") + else: + op_modes = prop.supportedModes.split(",") + for mode in op_modes: + mode = int(mode) + if ( + mode in OPMODES_HVAC + and (mode_ha := OPMODES_HVAC.get(mode)) + and mode_ha not in self._attr_hvac_modes + ): self._attr_hvac_modes.append(mode_ha) - if mode in OPMODES_PRESET: - self._attr_preset_modes.append(OPMODES_PRESET[mode]) + if mode in OPMODES_PRESET: + self._attr_preset_modes.append(OPMODES_PRESET[mode]) async def async_added_to_hass(self) -> None: """Call when entity is added to hass.""" @@ -238,20 +255,30 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): self._fan_mode_device.action("setFanMode", HA_FANMODES[fan_mode]) @property - def fibaro_op_mode(self) -> int: + def fibaro_op_mode(self) -> str | int: """Return the operating mode of the device.""" if not self._op_mode_device: - return 3 # Default to AUTO + return HA_OPMODES_HVAC[HVACMode.AUTO] - if "operatingMode" in self._op_mode_device.fibaro_device.properties: - return int(self._op_mode_device.fibaro_device.properties.operatingMode) + prop = self._op_mode_device.fibaro_device.properties - return int(self._op_mode_device.fibaro_device.properties.mode) + if "operatingMode" in prop: + return int(prop.operatingMode) + if "thermostatMode" in prop: + return prop.thermostatMode + + return int(prop.mode) @property - def hvac_mode(self) -> HVACMode: - """Return current operation ie. heat, cool, idle.""" - return OPMODES_HVAC[self.fibaro_op_mode] + def hvac_mode(self) -> HVACMode | str | None: + """Return hvac operation ie. heat, cool, idle.""" + fibaro_operation_mode = self.fibaro_op_mode + if isinstance(fibaro_operation_mode, str): + with suppress(ValueError): + return HVACMode(fibaro_operation_mode.lower()) + elif fibaro_operation_mode in OPMODES_HVAC: + return OPMODES_HVAC[fibaro_operation_mode] + return None def set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set new target operation mode.""" @@ -262,9 +289,29 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): if "setOperatingMode" in self._op_mode_device.fibaro_device.actions: self._op_mode_device.action("setOperatingMode", HA_OPMODES_HVAC[hvac_mode]) + elif "setThermostatMode" in self._op_mode_device.fibaro_device.actions: + prop = self._op_mode_device.fibaro_device.properties + if "supportedThermostatModes" in prop: + for mode in prop.supportedThermostatModes: + if mode.lower() == hvac_mode: + self._op_mode_device.action("setThermostatMode", mode) + break elif "setMode" in self._op_mode_device.fibaro_device.actions: self._op_mode_device.action("setMode", HA_OPMODES_HVAC[hvac_mode]) + @property + def hvac_action(self) -> HVACAction | None: + """Return the current running hvac operation if supported.""" + if not self._op_mode_device: + return None + + prop = self._op_mode_device.fibaro_device.properties + if "thermostatOperatingState" in prop: + with suppress(ValueError): + return HVACAction(prop.thermostatOperatingState.lower()) + + return None + @property def preset_mode(self) -> str | None: """Return the current preset mode, e.g., home, away, temp. @@ -274,6 +321,11 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): if not self._op_mode_device: return None + if "thermostatMode" in self._op_mode_device.fibaro_device.properties: + mode = self._op_mode_device.fibaro_device.properties.thermostatMode + if self.preset_modes is not None and mode in self.preset_modes: + return mode + return None if "operatingMode" in self._op_mode_device.fibaro_device.properties: mode = int(self._op_mode_device.fibaro_device.properties.operatingMode) else: @@ -287,7 +339,10 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): """Set new preset mode.""" if self._op_mode_device is None: return - if "setOperatingMode" in self._op_mode_device.fibaro_device.actions: + + if "setThermostatMode" in self._op_mode_device.fibaro_device.actions: + self._op_mode_device.action("setThermostatMode", preset_mode) + elif "setOperatingMode" in self._op_mode_device.fibaro_device.actions: self._op_mode_device.action( "setOperatingMode", HA_OPMODES_PRESET[preset_mode] ) From dfa1ecd03228140e0e5696fbce20230e9de8a018 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Thu, 24 Nov 2022 15:21:42 -0300 Subject: [PATCH 0684/1033] Update base image to 2022.11.0 (#82527) fixes undefined --- build.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.yaml b/build.yaml index 14a59641388..eed35c9dd69 100644 --- a/build.yaml +++ b/build.yaml @@ -1,11 +1,11 @@ image: homeassistant/{arch}-homeassistant shadow_repository: ghcr.io/home-assistant build_from: - aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2022.10.0 - armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2022.10.0 - armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2022.10.0 - amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2022.10.0 - i386: ghcr.io/home-assistant/i386-homeassistant-base:2022.10.0 + aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2022.11.0 + armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2022.11.0 + armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2022.11.0 + amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2022.11.0 + i386: ghcr.io/home-assistant/i386-homeassistant-base:2022.11.0 codenotary: signer: notary@home-assistant.io base_image: notary@home-assistant.io From 34633b0ede23db3d616f8b649710570b1cfc5a2c Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Thu, 24 Nov 2022 19:24:47 +0100 Subject: [PATCH 0685/1033] Bump nextdns library to 1.2.0 (#82643) --- homeassistant/components/nextdns/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/nextdns/__init__.py | 2 ++ tests/components/nextdns/fixtures/settings.json | 2 ++ 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nextdns/manifest.json b/homeassistant/components/nextdns/manifest.json index 2a68107079e..44027cd7911 100644 --- a/homeassistant/components/nextdns/manifest.json +++ b/homeassistant/components/nextdns/manifest.json @@ -3,7 +3,7 @@ "name": "NextDNS", "documentation": "https://www.home-assistant.io/integrations/nextdns", "codeowners": ["@bieniu"], - "requirements": ["nextdns==1.1.1"], + "requirements": ["nextdns==1.2.0"], "config_flow": true, "iot_class": "cloud_polling", "loggers": ["nextdns"], diff --git a/requirements_all.txt b/requirements_all.txt index bf2e3bf419d..4d2363dec38 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1160,7 +1160,7 @@ nextcloudmonitor==1.1.0 nextcord==2.0.0a8 # homeassistant.components.nextdns -nextdns==1.1.1 +nextdns==1.2.0 # homeassistant.components.nibe_heatpump nibe==1.3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5b6f2b2668c..1cebc382a7b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -847,7 +847,7 @@ nexia==2.0.6 nextcord==2.0.0a8 # homeassistant.components.nextdns -nextdns==1.1.1 +nextdns==1.2.0 # homeassistant.components.nibe_heatpump nibe==1.3.0 diff --git a/tests/components/nextdns/__init__.py b/tests/components/nextdns/__init__.py index 8c80db1fdff..24f0004835d 100644 --- a/tests/components/nextdns/__init__.py +++ b/tests/components/nextdns/__init__.py @@ -52,6 +52,8 @@ SETTINGS = Settings( google_safe_browsing=False, idn_homograph_attacks_protection=True, logs=True, + logs_location="ch", + logs_retention=720, safesearch=False, threat_intelligence_feeds=True, typosquatting_protection=True, diff --git a/tests/components/nextdns/fixtures/settings.json b/tests/components/nextdns/fixtures/settings.json index 9593ca6325e..0a88ccb31ab 100644 --- a/tests/components/nextdns/fixtures/settings.json +++ b/tests/components/nextdns/fixtures/settings.json @@ -4,6 +4,8 @@ "cname_flattening": true, "anonymized_ecs": true, "logs": true, + "logs_location": "ch", + "logs_retention": 720, "web3": true, "allow_affiliate": true, "block_disguised_trackers": true, From 9132c420377b662631bbecd330f8a725fa079401 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 24 Nov 2022 19:27:26 +0100 Subject: [PATCH 0686/1033] Improve Selector typing (#82636) --- homeassistant/helpers/selector.py | 79 ++++++++++++++++--------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/homeassistant/helpers/selector.py b/homeassistant/helpers/selector.py index 01f46d7e119..b759ab65826 100644 --- a/homeassistant/helpers/selector.py +++ b/homeassistant/helpers/selector.py @@ -1,8 +1,8 @@ """Selectors for Home Assistant.""" from __future__ import annotations -from collections.abc import Callable, Sequence -from typing import Any, Literal, TypedDict, cast +from collections.abc import Callable, Mapping, Sequence +from typing import Any, Generic, Literal, TypedDict, TypeVar, cast from uuid import UUID import voluptuous as vol @@ -17,6 +17,8 @@ from . import config_validation as cv SELECTORS: decorator.Registry[str, type[Selector]] = decorator.Registry() +_T = TypeVar("_T", bound=Mapping[str, Any]) + def _get_selector_class(config: Any) -> type[Selector]: """Get selector class type.""" @@ -56,14 +58,14 @@ def validate_selector(config: Any) -> dict: } -class Selector: +class Selector(Generic[_T]): """Base class for selectors.""" CONFIG_SCHEMA: Callable - config: Any + config: _T selector_type: str - def __init__(self, config: Any = None) -> None: + def __init__(self, config: Mapping[str, Any] | None = None) -> None: """Instantiate a selector.""" # Selectors can be empty if config is None: @@ -71,7 +73,7 @@ class Selector: self.config = self.CONFIG_SCHEMA(config) - def serialize(self) -> Any: + def serialize(self) -> dict[str, dict[str, _T]]: """Serialize Selector for voluptuous_serialize.""" return {"selector": {self.selector_type: self.config}} @@ -124,7 +126,7 @@ class ActionSelectorConfig(TypedDict): @SELECTORS.register("action") -class ActionSelector(Selector): +class ActionSelector(Selector[ActionSelectorConfig]): """Selector of an action sequence (script syntax).""" selector_type = "action" @@ -148,7 +150,7 @@ class AddonSelectorConfig(TypedDict, total=False): @SELECTORS.register("addon") -class AddonSelector(Selector): +class AddonSelector(Selector[AddonSelectorConfig]): """Selector of a add-on.""" selector_type = "addon" @@ -179,7 +181,7 @@ class AreaSelectorConfig(TypedDict, total=False): @SELECTORS.register("area") -class AreaSelector(Selector): +class AreaSelector(Selector[AreaSelectorConfig]): """Selector of a single or list of areas.""" selector_type = "area" @@ -214,7 +216,7 @@ class AttributeSelectorConfig(TypedDict, total=False): @SELECTORS.register("attribute") -class AttributeSelector(Selector): +class AttributeSelector(Selector[AttributeSelectorConfig]): """Selector for an entity attribute.""" selector_type = "attribute" @@ -243,7 +245,7 @@ class BooleanSelectorConfig(TypedDict): @SELECTORS.register("boolean") -class BooleanSelector(Selector): +class BooleanSelector(Selector[BooleanSelectorConfig]): """Selector of a boolean value.""" selector_type = "boolean" @@ -265,7 +267,7 @@ class ColorRGBSelectorConfig(TypedDict): @SELECTORS.register("color_rgb") -class ColorRGBSelector(Selector): +class ColorRGBSelector(Selector[ColorRGBSelectorConfig]): """Selector of an RGB color value.""" selector_type = "color_rgb" @@ -290,7 +292,7 @@ class ColorTempSelectorConfig(TypedDict, total=False): @SELECTORS.register("color_temp") -class ColorTempSelector(Selector): +class ColorTempSelector(Selector[ColorTempSelectorConfig]): """Selector of an color temperature.""" selector_type = "color_temp" @@ -325,7 +327,7 @@ class ConfigEntrySelectorConfig(TypedDict, total=False): @SELECTORS.register("config_entry") -class ConfigEntrySelector(Selector): +class ConfigEntrySelector(Selector[ConfigEntrySelectorConfig]): """Selector of a config entry.""" selector_type = "config_entry" @@ -351,7 +353,7 @@ class DateSelectorConfig(TypedDict): @SELECTORS.register("date") -class DateSelector(Selector): +class DateSelector(Selector[DateSelectorConfig]): """Selector of a date.""" selector_type = "date" @@ -373,7 +375,7 @@ class DateTimeSelectorConfig(TypedDict): @SELECTORS.register("datetime") -class DateTimeSelector(Selector): +class DateTimeSelector(Selector[DateTimeSelectorConfig]): """Selector of a datetime.""" selector_type = "datetime" @@ -401,7 +403,7 @@ class DeviceSelectorConfig(TypedDict, total=False): @SELECTORS.register("device") -class DeviceSelector(Selector): +class DeviceSelector(Selector[DeviceSelectorConfig]): """Selector of a single or list of devices.""" selector_type = "device" @@ -431,7 +433,7 @@ class DurationSelectorConfig(TypedDict, total=False): @SELECTORS.register("duration") -class DurationSelector(Selector): +class DurationSelector(Selector[DurationSelectorConfig]): """Selector for a duration.""" selector_type = "duration" @@ -463,7 +465,7 @@ class EntitySelectorConfig(SingleEntitySelectorConfig, total=False): @SELECTORS.register("entity") -class EntitySelector(Selector): +class EntitySelector(Selector[EntitySelectorConfig]): """Selector of a single or list of entities.""" selector_type = "entity" @@ -517,7 +519,7 @@ class IconSelectorConfig(TypedDict, total=False): @SELECTORS.register("icon") -class IconSelector(Selector): +class IconSelector(Selector[IconSelectorConfig]): """Selector for an icon.""" selector_type = "icon" @@ -545,7 +547,7 @@ class LocationSelectorConfig(TypedDict, total=False): @SELECTORS.register("location") -class LocationSelector(Selector): +class LocationSelector(Selector[LocationSelectorConfig]): """Selector for a location.""" selector_type = "location" @@ -576,7 +578,7 @@ class MediaSelectorConfig(TypedDict): @SELECTORS.register("media") -class MediaSelector(Selector): +class MediaSelector(Selector[MediaSelectorConfig]): """Selector for media.""" selector_type = "media" @@ -636,7 +638,7 @@ def validate_slider(data: Any) -> Any: @SELECTORS.register("number") -class NumberSelector(Selector): +class NumberSelector(Selector[NumberSelectorConfig]): """Selector of a numeric value.""" selector_type = "number" @@ -682,7 +684,7 @@ class ObjectSelectorConfig(TypedDict): @SELECTORS.register("object") -class ObjectSelector(Selector): +class ObjectSelector(Selector[ObjectSelectorConfig]): """Selector for an arbitrary object.""" selector_type = "object" @@ -733,7 +735,7 @@ class SelectSelectorConfig(TypedDict, total=False): @SELECTORS.register("select") -class SelectSelector(Selector): +class SelectSelector(Selector[SelectSelectorConfig]): """Selector for an single-choice input select.""" selector_type = "select" @@ -755,12 +757,15 @@ class SelectSelector(Selector): def __call__(self, data: Any) -> Any: """Validate the passed selection.""" - options = [] - if self.config["options"]: - if isinstance(self.config["options"][0], str): - options = self.config["options"] + options: Sequence[str] = [] + if config_options := self.config["options"]: + if isinstance(config_options[0], str): + options = cast(Sequence[str], config_options) else: - options = [option["value"] for option in self.config["options"]] + options = [ + option["value"] + for option in cast(Sequence[SelectOptionDict], config_options) + ] parent_schema = vol.In(options) if self.config["custom_value"]: @@ -787,7 +792,7 @@ class StateSelectorConfig(TypedDict, total=False): @SELECTORS.register("state") -class StateSelector(Selector): +class StateSelector(Selector[StateSelectorConfig]): """Selector for an entity state.""" selector_type = "state" @@ -814,7 +819,7 @@ class StateSelector(Selector): @SELECTORS.register("target") -class TargetSelector(Selector): +class TargetSelector(Selector[TargetSelectorConfig]): """Selector of a target value (area ID, device ID, entity ID etc). Value should follow cv.TARGET_SERVICE_FIELDS format. @@ -846,7 +851,7 @@ class TemplateSelectorConfig(TypedDict): @SELECTORS.register("template") -class TemplateSelector(Selector): +class TemplateSelector(Selector[TemplateSelectorConfig]): """Selector for an template.""" selector_type = "template" @@ -891,7 +896,7 @@ class TextSelectorType(StrEnum): @SELECTORS.register("text") -class TextSelector(Selector): +class TextSelector(Selector[TextSelectorConfig]): """Selector for a multi-line text string.""" selector_type = "text" @@ -924,7 +929,7 @@ class ThemeSelectorConfig(TypedDict): @SELECTORS.register("theme") -class ThemeSelector(Selector): +class ThemeSelector(Selector[ThemeSelectorConfig]): """Selector for an theme.""" selector_type = "theme" @@ -946,7 +951,7 @@ class TimeSelectorConfig(TypedDict): @SELECTORS.register("time") -class TimeSelector(Selector): +class TimeSelector(Selector[TimeSelectorConfig]): """Selector of a time value.""" selector_type = "time" @@ -970,7 +975,7 @@ class FileSelectorConfig(TypedDict): @SELECTORS.register("file") -class FileSelector(Selector): +class FileSelector(Selector[FileSelectorConfig]): """Selector of a file.""" selector_type = "file" From 003e4224c89a6da381960dc5347750d1521d85c9 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 24 Nov 2022 14:01:01 -0500 Subject: [PATCH 0687/1033] Add `text` platform (#79454) Co-authored-by: Franck Nijhof Co-authored-by: Franck Nijhof --- CODEOWNERS | 2 + homeassistant/components/demo/__init__.py | 1 + homeassistant/components/demo/text.py | 101 +++++++++ homeassistant/components/text/__init__.py | 224 ++++++++++++++++++++ homeassistant/components/text/const.py | 11 + homeassistant/components/text/manifest.json | 8 + homeassistant/components/text/recorder.py | 12 ++ homeassistant/components/text/services.yaml | 14 ++ homeassistant/components/text/strings.json | 3 + homeassistant/const.py | 1 + tests/components/demo/test_text.py | 44 ++++ tests/components/text/__init__.py | 1 + tests/components/text/test_init.py | 104 +++++++++ tests/components/text/test_recorder.py | 41 ++++ 14 files changed, 567 insertions(+) create mode 100644 homeassistant/components/demo/text.py create mode 100644 homeassistant/components/text/__init__.py create mode 100644 homeassistant/components/text/const.py create mode 100644 homeassistant/components/text/manifest.json create mode 100644 homeassistant/components/text/recorder.py create mode 100644 homeassistant/components/text/services.yaml create mode 100644 homeassistant/components/text/strings.json create mode 100644 tests/components/demo/test_text.py create mode 100644 tests/components/text/__init__.py create mode 100644 tests/components/text/test_init.py create mode 100644 tests/components/text/test_recorder.py diff --git a/CODEOWNERS b/CODEOWNERS index 3646c36422d..46e66291eeb 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1156,6 +1156,8 @@ build.json @home-assistant/supervisor /tests/components/template/ @PhracturedBlue @tetienne @home-assistant/core /homeassistant/components/tesla_wall_connector/ @einarhauks /tests/components/tesla_wall_connector/ @einarhauks +/homeassistant/components/text/ @home-assistant/core +/tests/components/text/ @home-assistant/core /homeassistant/components/tfiac/ @fredrike @mellado /homeassistant/components/thermobeacon/ @bdraco /tests/components/thermobeacon/ @bdraco diff --git a/homeassistant/components/demo/__init__.py b/homeassistant/components/demo/__init__.py index dd14d9f7b2a..ae6912fa0f9 100644 --- a/homeassistant/components/demo/__init__.py +++ b/homeassistant/components/demo/__init__.py @@ -52,6 +52,7 @@ COMPONENTS_WITH_CONFIG_ENTRY_DEMO_PLATFORM = [ Platform.SENSOR, Platform.SIREN, Platform.SWITCH, + Platform.TEXT, Platform.UPDATE, Platform.VACUUM, Platform.WATER_HEATER, diff --git a/homeassistant/components/demo/text.py b/homeassistant/components/demo/text.py new file mode 100644 index 00000000000..efce1af5c37 --- /dev/null +++ b/homeassistant/components/demo/text.py @@ -0,0 +1,101 @@ +"""Demo platform that offers a fake text entity.""" +from __future__ import annotations + +from homeassistant.components.text import TextEntity, TextMode +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import DEVICE_DEFAULT_NAME +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType + +from . import DOMAIN + + +async def async_setup_platform( + hass: HomeAssistant, + config: ConfigType, + async_add_entities: AddEntitiesCallback, + discovery_info: DiscoveryInfoType | None = None, +) -> None: + """Set up the demo Text entity.""" + async_add_entities( + [ + DemoText( + unique_id="text", + name="Text", + icon=None, + native_value="Hello world", + ), + DemoText( + unique_id="password", + name="Password", + icon="mdi:text", + native_value="Hello world", + mode=TextMode.PASSWORD, + ), + DemoText( + unique_id="text_1_to_5_char", + name="Text with 1 to 5 characters", + icon="mdi:text", + native_value="Hello", + native_min=1, + native_max=5, + ), + DemoText( + unique_id="text_lowercase", + name="Text with only lower case characters", + icon="mdi:text", + native_value="world", + pattern=r"[a-z]+", + ), + ] + ) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Demo config entry.""" + await async_setup_platform(hass, {}, async_add_entities) + + +class DemoText(TextEntity): + """Representation of a demo text entity.""" + + _attr_should_poll = False + + def __init__( + self, + unique_id: str, + name: str, + icon: str | None, + native_value: str | None, + mode: TextMode = TextMode.TEXT, + native_max: int | None = None, + native_min: int | None = None, + pattern: str | None = None, + ) -> None: + """Initialize the Demo text entity.""" + self._attr_unique_id = unique_id + self._attr_name = name or DEVICE_DEFAULT_NAME + self._attr_native_value = native_value + self._attr_icon = icon + self._attr_mode = mode + if native_max is not None: + self._attr_native_max = native_max + if native_min is not None: + self._attr_native_min = native_min + if pattern is not None: + self._attr_pattern = pattern + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, unique_id)}, + name=name, + ) + + async def async_set_value(self, value: str) -> None: + """Update the value.""" + self._attr_native_value = value + self.async_write_ha_state() diff --git a/homeassistant/components/text/__init__.py b/homeassistant/components/text/__init__.py new file mode 100644 index 00000000000..35c7d5d94ef --- /dev/null +++ b/homeassistant/components/text/__init__.py @@ -0,0 +1,224 @@ +"""Component to allow setting text as platforms.""" +from __future__ import annotations + +from dataclasses import dataclass +from datetime import timedelta +import logging +import re +from typing import Any, final + +import voluptuous as vol + +from homeassistant.backports.enum import StrEnum +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import MAX_LENGTH_STATE_STATE +from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.config_validation import ( # noqa: F401 + PLATFORM_SCHEMA, + PLATFORM_SCHEMA_BASE, +) +from homeassistant.helpers.entity import Entity, EntityDescription +from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.typing import ConfigType + +from .const import ( + ATTR_MAX, + ATTR_MIN, + ATTR_MODE, + ATTR_PATTERN, + ATTR_VALUE, + DOMAIN, + SERVICE_SET_VALUE, +) + +SCAN_INTERVAL = timedelta(seconds=30) + +ENTITY_ID_FORMAT = DOMAIN + ".{}" + +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) + +_LOGGER = logging.getLogger(__name__) + +__all__ = ["DOMAIN", "TextEntity", "TextEntityDescription", "TextMode"] + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up Text entities.""" + component = hass.data[DOMAIN] = EntityComponent[TextEntity]( + _LOGGER, DOMAIN, hass, SCAN_INTERVAL + ) + await component.async_setup(config) + + component.async_register_entity_service( + SERVICE_SET_VALUE, + {vol.Required(ATTR_VALUE): cv.string}, + _async_set_value, + ) + + return True + + +async def _async_set_value(entity: TextEntity, service_call: ServiceCall) -> None: + """Service call wrapper to set a new value.""" + value = service_call.data[ATTR_VALUE] + if len(value) < entity.min: + raise ValueError( + f"Value {value} for {entity.name} is too short (minimum length {entity.min})" + ) + if len(value) > entity.max: + raise ValueError( + f"Value {value} for {entity.name} is too long (maximum length {entity.max})" + ) + if entity.pattern_cmp and not entity.pattern_cmp.match(value): + raise ValueError( + f"Value {value} for {entity.name} doesn't match pattern {entity.pattern}" + ) + await entity.async_set_value(value) + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up a config entry.""" + component: EntityComponent[TextEntity] = hass.data[DOMAIN] + return await component.async_setup_entry(entry) + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + component: EntityComponent[TextEntity] = hass.data[DOMAIN] + return await component.async_unload_entry(entry) + + +class TextMode(StrEnum): + """Modes for text entities.""" + + PASSWORD = "password" + TEXT = "text" + + +@dataclass +class TextEntityDescription(EntityDescription): + """A class that describes text entities.""" + + native_min: int = 0 + native_max: int = MAX_LENGTH_STATE_STATE + mode: TextMode = TextMode.TEXT + pattern: str | None = None + + +class TextEntity(Entity): + """Representation of a Text entity.""" + + entity_description: TextEntityDescription + _attr_mode: TextMode + _attr_native_value: str | None + _attr_native_min: int + _attr_native_max: int + _attr_pattern: str | None + _attr_state: None = None + __pattern_cmp: re.Pattern | None = None + + @property + def capability_attributes(self) -> dict[str, Any]: + """Return capability attributes.""" + return { + ATTR_MODE: self.mode, + ATTR_MIN: self.min, + ATTR_MAX: self.max, + ATTR_PATTERN: self.pattern, + } + + @property + @final + def state(self) -> str | None: + """Return the entity state.""" + if self.native_value is None: + return None + if len(self.native_value) < self.min: + raise ValueError( + f"Entity {self.entity_id} provides state {self.native_value} which is " + f"too short (minimum length {self.min})" + ) + if len(self.native_value) > self.max: + raise ValueError( + f"Entity {self.entity_id} provides state {self.native_value} which is " + f"too long (maximum length {self.max})" + ) + if self.pattern_cmp and not self.pattern_cmp.match(self.native_value): + raise ValueError( + f"Entity {self.entity_id} provides state {self.native_value} which " + f"does not match expected pattern {self.pattern}" + ) + return self.native_value + + @property + def mode(self) -> TextMode: + """Return the mode of the entity.""" + if hasattr(self, "_attr_mode"): + return self._attr_mode + if hasattr(self, "entity_description"): + return self.entity_description.mode + return TextMode.TEXT + + @property + def native_min(self) -> int: + """Return the minimum length of the value.""" + if hasattr(self, "_attr_native_min"): + return self._attr_native_min + if hasattr(self, "entity_description"): + return self.entity_description.native_min + return 0 + + @property + @final + def min(self) -> int: + """Return the minimum length of the value.""" + return max(self.native_min, 0) + + @property + def native_max(self) -> int: + """Return the maximum length of the value.""" + if hasattr(self, "_attr_native_max"): + return self._attr_native_max + if hasattr(self, "entity_description"): + return self.entity_description.native_max + return MAX_LENGTH_STATE_STATE + + @property + @final + def max(self) -> int: + """Return the maximum length of the value.""" + return min(self.native_max, MAX_LENGTH_STATE_STATE) + + @property + @final + def pattern_cmp(self) -> re.Pattern | None: + """Return a compiled pattern.""" + if self.pattern is None: + self.__pattern_cmp = None + return None + if not self.__pattern_cmp or self.pattern != self.__pattern_cmp.pattern: + self.__pattern_cmp = re.compile(self.pattern) + return self.__pattern_cmp + + @property + def pattern(self) -> str | None: + """Return the regex pattern that the value must match.""" + if hasattr(self, "_attr_pattern"): + return self._attr_pattern + if hasattr(self, "entity_description"): + return self.entity_description.pattern + return None + + @property + def native_value(self) -> str | None: + """Return the value reported by the text.""" + return self._attr_native_value + + def set_value(self, value: str) -> None: + """Change the value.""" + raise NotImplementedError() + + async def async_set_value(self, value: str) -> None: + """Change the value.""" + await self.hass.async_add_executor_job(self.set_value, value) diff --git a/homeassistant/components/text/const.py b/homeassistant/components/text/const.py new file mode 100644 index 00000000000..3670c30120b --- /dev/null +++ b/homeassistant/components/text/const.py @@ -0,0 +1,11 @@ +"""Provides the constants needed for the component.""" + +DOMAIN = "text" + +ATTR_MAX = "max" +ATTR_MIN = "min" +ATTR_MODE = "mode" +ATTR_PATTERN = "pattern" +ATTR_VALUE = "value" + +SERVICE_SET_VALUE = "set_value" diff --git a/homeassistant/components/text/manifest.json b/homeassistant/components/text/manifest.json new file mode 100644 index 00000000000..3e45499302a --- /dev/null +++ b/homeassistant/components/text/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "text", + "name": "Text", + "documentation": "https://www.home-assistant.io/integrations/text", + "codeowners": ["@home-assistant/core"], + "quality_scale": "internal", + "integration_type": "entity" +} diff --git a/homeassistant/components/text/recorder.py b/homeassistant/components/text/recorder.py new file mode 100644 index 00000000000..09642eb3079 --- /dev/null +++ b/homeassistant/components/text/recorder.py @@ -0,0 +1,12 @@ +"""Integration platform for recorder.""" +from __future__ import annotations + +from homeassistant.core import HomeAssistant, callback + +from . import ATTR_MAX, ATTR_MIN, ATTR_MODE, ATTR_PATTERN + + +@callback +def exclude_attributes(hass: HomeAssistant) -> set[str]: + """Exclude static attributes from being recorded in the database.""" + return {ATTR_MAX, ATTR_MIN, ATTR_MODE, ATTR_PATTERN} diff --git a/homeassistant/components/text/services.yaml b/homeassistant/components/text/services.yaml new file mode 100644 index 00000000000..00dd0ecafd2 --- /dev/null +++ b/homeassistant/components/text/services.yaml @@ -0,0 +1,14 @@ +set_value: + name: Set value + description: Set value of a text entity. + target: + entity: + domain: text + fields: + value: + name: Value + description: Value to set. + required: true + example: "Hello world!" + selector: + text: diff --git a/homeassistant/components/text/strings.json b/homeassistant/components/text/strings.json new file mode 100644 index 00000000000..8d4d14669a0 --- /dev/null +++ b/homeassistant/components/text/strings.json @@ -0,0 +1,3 @@ +{ + "title": "Text" +} diff --git a/homeassistant/const.py b/homeassistant/const.py index e01a9775917..22b7ea5ab50 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -49,6 +49,7 @@ class Platform(StrEnum): SIREN = "siren" STT = "stt" SWITCH = "switch" + TEXT = "text" TTS = "tts" VACUUM = "vacuum" UPDATE = "update" diff --git a/tests/components/demo/test_text.py b/tests/components/demo/test_text.py new file mode 100644 index 00000000000..df93531bd1a --- /dev/null +++ b/tests/components/demo/test_text.py @@ -0,0 +1,44 @@ +"""The tests for the demo text component.""" +import pytest + +from homeassistant.components.text import ( + ATTR_MAX, + ATTR_MIN, + ATTR_PATTERN, + ATTR_VALUE, + DOMAIN, + SERVICE_SET_VALUE, +) +from homeassistant.const import ATTR_ENTITY_ID, ATTR_MODE, MAX_LENGTH_STATE_STATE +from homeassistant.setup import async_setup_component + +ENTITY_TEXT = "text.text" + + +@pytest.fixture(autouse=True) +async def setup_demo_text(hass): + """Initialize setup demo text.""" + assert await async_setup_component(hass, DOMAIN, {"text": {"platform": "demo"}}) + await hass.async_block_till_done() + + +def test_setup_params(hass): + """Test the initial parameters.""" + state = hass.states.get(ENTITY_TEXT) + assert state.state == "Hello world" + assert state.attributes[ATTR_MIN] == 0 + assert state.attributes[ATTR_MAX] == MAX_LENGTH_STATE_STATE + assert state.attributes[ATTR_PATTERN] is None + assert state.attributes[ATTR_MODE] == "text" + + +async def test_set_value(hass): + """Test set value service.""" + await hass.services.async_call( + DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: ENTITY_TEXT, ATTR_VALUE: "new"}, + blocking=True, + ) + state = hass.states.get(ENTITY_TEXT) + assert state.state == "new" diff --git a/tests/components/text/__init__.py b/tests/components/text/__init__.py new file mode 100644 index 00000000000..e22fa1d34a1 --- /dev/null +++ b/tests/components/text/__init__.py @@ -0,0 +1 @@ +"""Tests for the text component.""" diff --git a/tests/components/text/test_init.py b/tests/components/text/test_init.py new file mode 100644 index 00000000000..1bbb3e1e1b0 --- /dev/null +++ b/tests/components/text/test_init.py @@ -0,0 +1,104 @@ +"""The tests for the text component.""" +import pytest + +from homeassistant.components.text import ( + ATTR_MAX, + ATTR_MIN, + ATTR_MODE, + ATTR_PATTERN, + ATTR_VALUE, + DOMAIN, + SERVICE_SET_VALUE, + TextEntity, + TextMode, + _async_set_value, +) +from homeassistant.const import MAX_LENGTH_STATE_STATE +from homeassistant.core import ServiceCall + + +class MockTextEntity(TextEntity): + """Mock text device to use in tests.""" + + def __init__( + self, native_value="test", native_min=None, native_max=None, pattern=None + ): + """Initialize mock text entity.""" + self._attr_native_value = native_value + if native_min is not None: + self._attr_native_min = native_min + if native_max is not None: + self._attr_native_max = native_max + if pattern is not None: + self._attr_pattern = pattern + + async def async_set_value(self, value: str) -> None: + """Set the value of the text.""" + self._attr_native_value = value + + +async def test_text_default(hass): + """Test text entity with defaults.""" + text = MockTextEntity() + text.hass = hass + + assert text.capability_attributes == { + ATTR_MIN: 0, + ATTR_MAX: MAX_LENGTH_STATE_STATE, + ATTR_MODE: TextMode.TEXT, + ATTR_PATTERN: None, + } + assert text.pattern is None + assert text.state == "test" + + +async def test_text_new_min_max_pattern(hass): + """Test text entity with new min, max, and pattern.""" + text = MockTextEntity(native_min=-1, native_max=500, pattern=r"[a-z]") + text.hass = hass + + assert text.capability_attributes == { + ATTR_MIN: 0, + ATTR_MAX: MAX_LENGTH_STATE_STATE, + ATTR_MODE: TextMode.TEXT, + ATTR_PATTERN: r"[a-z]", + } + + +async def test_text_set_value(hass): + """Test text entity with set_value service.""" + text = MockTextEntity(native_min=1, native_max=5, pattern=r"[a-z]") + text.hass = hass + + with pytest.raises(ValueError): + await _async_set_value( + text, ServiceCall(DOMAIN, SERVICE_SET_VALUE, {ATTR_VALUE: ""}) + ) + + with pytest.raises(ValueError): + await _async_set_value( + text, ServiceCall(DOMAIN, SERVICE_SET_VALUE, {ATTR_VALUE: "hello world!"}) + ) + + with pytest.raises(ValueError): + await _async_set_value( + text, ServiceCall(DOMAIN, SERVICE_SET_VALUE, {ATTR_VALUE: "HELLO"}) + ) + + await _async_set_value( + text, ServiceCall(DOMAIN, SERVICE_SET_VALUE, {ATTR_VALUE: "test2"}) + ) + + assert text.state == "test2" + + +async def test_text_value_outside_bounds(hass): + """Test text entity with value that is outside min and max.""" + with pytest.raises(ValueError): + MockTextEntity( + "hello world", native_min=2, native_max=5, pattern=r"[a-z]" + ).state + with pytest.raises(ValueError): + MockTextEntity( + "hello world", native_min=15, native_max=20, pattern=r"[a-z]" + ).state diff --git a/tests/components/text/test_recorder.py b/tests/components/text/test_recorder.py new file mode 100644 index 00000000000..6ad0df13ffa --- /dev/null +++ b/tests/components/text/test_recorder.py @@ -0,0 +1,41 @@ +"""The tests for text recorder.""" +from __future__ import annotations + +from datetime import timedelta + +from homeassistant.components import text +from homeassistant.components.recorder.db_schema import StateAttributes, States +from homeassistant.components.recorder.util import session_scope +from homeassistant.components.text import ATTR_MAX, ATTR_MIN, ATTR_MODE, ATTR_PATTERN +from homeassistant.const import ATTR_FRIENDLY_NAME +from homeassistant.core import State +from homeassistant.setup import async_setup_component +from homeassistant.util import dt as dt_util + +from tests.common import async_fire_time_changed +from tests.components.recorder.common import async_wait_recording_done + + +async def test_exclude_attributes(recorder_mock, hass): + """Test siren registered attributes to be excluded.""" + await async_setup_component(hass, text.DOMAIN, {text.DOMAIN: {"platform": "demo"}}) + await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=5)) + await hass.async_block_till_done() + await async_wait_recording_done(hass) + + def _fetch_states() -> list[State]: + with session_scope(hass=hass) as session: + native_states = [] + for db_state, db_state_attributes in session.query(States, StateAttributes): + state = db_state.to_native() + state.attributes = db_state_attributes.to_native() + native_states.append(state) + return native_states + + states: list[State] = await hass.async_add_executor_job(_fetch_states) + assert len(states) > 1 + for state in states: + for attr in (ATTR_MAX, ATTR_MIN, ATTR_MODE, ATTR_PATTERN): + assert attr not in state.attributes + assert ATTR_FRIENDLY_NAME in state.attributes From 5e32abd04efe5db5495e707032fe8af26356adc5 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Thu, 24 Nov 2022 21:04:51 +0200 Subject: [PATCH 0688/1033] Mark Shelly as Platinum integration (#82656) --- homeassistant/components/shelly/manifest.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/shelly/manifest.json b/homeassistant/components/shelly/manifest.json index aea453127fd..7c2fe3beef6 100644 --- a/homeassistant/components/shelly/manifest.json +++ b/homeassistant/components/shelly/manifest.json @@ -14,5 +14,6 @@ "codeowners": ["@balloob", "@bieniu", "@thecode", "@chemelli74", "@bdraco"], "iot_class": "local_push", "loggers": ["aioshelly"], - "integration_type": "device" + "integration_type": "device", + "quality_scale": "platinum" } From 6e2803a6d86aa79aba9ac8e34c6627029e61a135 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 24 Nov 2022 20:08:14 +0100 Subject: [PATCH 0689/1033] Use SchemaOptionsFlowHandler in Accuweather (#82627) Use SchemaOptionsFlowHandler in accuweather --- .../components/accuweather/config_flow.py | 53 ++++++------------- .../components/accuweather/strings.json | 2 +- .../accuweather/translations/en.json | 2 +- .../accuweather/test_config_flow.py | 2 +- 4 files changed, 19 insertions(+), 40 deletions(-) diff --git a/homeassistant/components/accuweather/config_flow.py b/homeassistant/components/accuweather/config_flow.py index b9244a3645c..357327b298b 100644 --- a/homeassistant/components/accuweather/config_flow.py +++ b/homeassistant/components/accuweather/config_flow.py @@ -17,9 +17,23 @@ from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaFlowFormStep, + SchemaFlowMenuStep, + SchemaOptionsFlowHandler, +) from .const import CONF_FORECAST, DOMAIN +OPTIONS_SCHEMA = vol.Schema( + { + vol.Optional(CONF_FORECAST, default=False): bool, + } +) +OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA) +} + class AccuWeatherFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Config flow for AccuWeather.""" @@ -84,41 +98,6 @@ class AccuWeatherFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): @staticmethod @callback - def async_get_options_flow( - config_entry: ConfigEntry, - ) -> AccuWeatherOptionsFlowHandler: + def async_get_options_flow(config_entry: ConfigEntry) -> SchemaOptionsFlowHandler: """Options callback for AccuWeather.""" - return AccuWeatherOptionsFlowHandler(config_entry) - - -class AccuWeatherOptionsFlowHandler(config_entries.OptionsFlow): - """Config flow options for AccuWeather.""" - - def __init__(self, entry: ConfigEntry) -> None: - """Initialize AccuWeather options flow.""" - self.config_entry = entry - - async def async_step_init( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Manage the options.""" - return await self.async_step_user() - - async def async_step_user( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Handle a flow initialized by the user.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - return self.async_show_form( - step_id="user", - data_schema=vol.Schema( - { - vol.Optional( - CONF_FORECAST, - default=self.config_entry.options.get(CONF_FORECAST, False), - ): bool - } - ), - ) + return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW) diff --git a/homeassistant/components/accuweather/strings.json b/homeassistant/components/accuweather/strings.json index 432cc095c7b..ba1bba21d9e 100644 --- a/homeassistant/components/accuweather/strings.json +++ b/homeassistant/components/accuweather/strings.json @@ -24,7 +24,7 @@ }, "options": { "step": { - "user": { + "init": { "description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 80 minutes instead of every 40 minutes.", "data": { "forecast": "Weather forecast" diff --git a/homeassistant/components/accuweather/translations/en.json b/homeassistant/components/accuweather/translations/en.json index d391ce83e1e..7a8599fb562 100644 --- a/homeassistant/components/accuweather/translations/en.json +++ b/homeassistant/components/accuweather/translations/en.json @@ -24,7 +24,7 @@ }, "options": { "step": { - "user": { + "init": { "data": { "forecast": "Weather forecast" }, diff --git a/tests/components/accuweather/test_config_flow.py b/tests/components/accuweather/test_config_flow.py index b4d804dceae..4abde4d7620 100644 --- a/tests/components/accuweather/test_config_flow.py +++ b/tests/components/accuweather/test_config_flow.py @@ -172,7 +172,7 @@ async def test_options_flow(hass): result = await hass.config_entries.options.async_init(config_entry.entry_id) assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["step_id"] == "user" + assert result["step_id"] == "init" result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={CONF_FORECAST: True} From cf681cd921ef25c71bcf61bf5f584b82625ca5e3 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Thu, 24 Nov 2022 20:14:08 +0100 Subject: [PATCH 0690/1033] Add myself as codeowner for min_max (#82658) codeowner --- CODEOWNERS | 4 ++-- homeassistant/components/min_max/manifest.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 46e66291eeb..51a2c5c6db2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -695,8 +695,8 @@ build.json @home-assistant/supervisor /tests/components/mikrotik/ @engrbm87 /homeassistant/components/mill/ @danielhiversen /tests/components/mill/ @danielhiversen -/homeassistant/components/min_max/ @fabaff -/tests/components/min_max/ @fabaff +/homeassistant/components/min_max/ @gjohansson-ST +/tests/components/min_max/ @gjohansson-ST /homeassistant/components/minecraft_server/ @elmurato /tests/components/minecraft_server/ @elmurato /homeassistant/components/minio/ @tkislan diff --git a/homeassistant/components/min_max/manifest.json b/homeassistant/components/min_max/manifest.json index 7324d682caa..788e259d4ec 100644 --- a/homeassistant/components/min_max/manifest.json +++ b/homeassistant/components/min_max/manifest.json @@ -3,7 +3,7 @@ "integration_type": "helper", "name": "Min/Max", "documentation": "https://www.home-assistant.io/integrations/min_max", - "codeowners": ["@fabaff"], + "codeowners": ["@gjohansson-ST"], "quality_scale": "internal", "iot_class": "calculated", "config_flow": true From 635d8c01fbfbf006d9622b4770079ee5ec511d7e Mon Sep 17 00:00:00 2001 From: G Johansson Date: Thu, 24 Nov 2022 20:21:09 +0100 Subject: [PATCH 0691/1033] Add sum to min_max helper (#82651) sum to min_max --- .../components/min_max/config_flow.py | 1 + homeassistant/components/min_max/sensor.py | 16 +++++ homeassistant/components/min_max/strings.json | 8 +-- .../components/min_max/translations/en.json | 10 ++-- tests/components/min_max/test_sensor.py | 58 +++++++++++++++++++ 5 files changed, 84 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/min_max/config_flow.py b/homeassistant/components/min_max/config_flow.py index 0fed67f15b9..d5c9ce43a99 100644 --- a/homeassistant/components/min_max/config_flow.py +++ b/homeassistant/components/min_max/config_flow.py @@ -23,6 +23,7 @@ _STATISTIC_MEASURES = [ selector.SelectOptionDict(value="median", label="Median"), selector.SelectOptionDict(value="last", label="Most recently updated"), selector.SelectOptionDict(value="range", label="Statistical range"), + selector.SelectOptionDict(value="sum", label="Sum"), ] diff --git a/homeassistant/components/min_max/sensor.py b/homeassistant/components/min_max/sensor.py index c052cbc211d..d0064d07511 100644 --- a/homeassistant/components/min_max/sensor.py +++ b/homeassistant/components/min_max/sensor.py @@ -48,6 +48,7 @@ ATTR_MEDIAN = "median" ATTR_LAST = "last" ATTR_LAST_ENTITY_ID = "last_entity_id" ATTR_RANGE = "range" +ATTR_SUM = "sum" ICON = "mdi:calculator" @@ -58,6 +59,7 @@ SENSOR_TYPES = { ATTR_MEDIAN: "median", ATTR_LAST: "last", ATTR_RANGE: "range", + ATTR_SUM: "sum", } SENSOR_TYPE_TO_ATTR = {v: k for k, v in SENSOR_TYPES.items()} @@ -188,6 +190,18 @@ def calc_range(sensor_values: list[tuple[str, Any]], round_digits: int) -> float return value +def calc_sum(sensor_values: list[tuple[str, Any]], round_digits: int) -> float | None: + """Calculate a sum of values, not honoring unknown states.""" + result = 0 + for _, sensor_value in sensor_values: + if sensor_value in [STATE_UNKNOWN, STATE_UNAVAILABLE]: + return None + result += sensor_value + + value: float = round(result, round_digits) + return value + + class MinMaxSensor(SensorEntity): """Representation of a min/max sensor.""" @@ -222,6 +236,7 @@ class MinMaxSensor(SensorEntity): self.last: float | None = None self.median: float | None = None self.range: float | None = None + self.sum: float | None = None self.min_entity_id: str | None = None self.max_entity_id: str | None = None self.last_entity_id: str | None = None @@ -336,3 +351,4 @@ class MinMaxSensor(SensorEntity): self.mean = calc_mean(sensor_values, self._round_digits) self.median = calc_median(sensor_values, self._round_digits) self.range = calc_range(sensor_values, self._round_digits) + self.sum = calc_sum(sensor_values, self._round_digits) diff --git a/homeassistant/components/min_max/strings.json b/homeassistant/components/min_max/strings.json index 596dd2250eb..67e8416bc2c 100644 --- a/homeassistant/components/min_max/strings.json +++ b/homeassistant/components/min_max/strings.json @@ -1,10 +1,10 @@ { - "title": "Min / max / mean / median sensor", + "title": "Combine the state of several sensors", "config": { "step": { "user": { - "title": "Add min / max / mean / median sensor", - "description": "Create a sensor that calculates a min, max, mean or median value from a list of input sensors.", + "title": "Combine the state of several sensors", + "description": "Create a sensor that calculates a min, max, mean, median or sum from a list of input sensors.", "data": { "entity_ids": "Input entities", "name": "Name", @@ -12,7 +12,7 @@ "type": "Statistic characteristic" }, "data_description": { - "round_digits": "Controls the number of decimal digits in the output when the statistics characteristic is mean or median." + "round_digits": "Controls the number of decimal digits in the output when the statistics characteristic is mean, median or sum." } } } diff --git a/homeassistant/components/min_max/translations/en.json b/homeassistant/components/min_max/translations/en.json index 8cc0d41c419..6c661c980c0 100644 --- a/homeassistant/components/min_max/translations/en.json +++ b/homeassistant/components/min_max/translations/en.json @@ -9,10 +9,10 @@ "type": "Statistic characteristic" }, "data_description": { - "round_digits": "Controls the number of decimal digits in the output when the statistics characteristic is mean or median." + "round_digits": "Controls the number of decimal digits in the output when the statistics characteristic is mean, median or sum." }, - "description": "Create a sensor that calculates a min, max, mean or median value from a list of input sensors.", - "title": "Add min / max / mean / median sensor" + "description": "Create a sensor that calculates a min, max, mean, median or sum from a list of input sensors.", + "title": "Combine the state of several sensors" } } }, @@ -25,10 +25,10 @@ "type": "Statistic characteristic" }, "data_description": { - "round_digits": "Controls the number of decimal digits in the output when the statistics characteristic is mean or median." + "round_digits": "Controls the number of decimal digits in the output when the statistics characteristic is mean, median or sum." } } } }, - "title": "Min / max / mean / median sensor" + "title": "Combine the state of several sensors" } \ No newline at end of file diff --git a/tests/components/min_max/test_sensor.py b/tests/components/min_max/test_sensor.py index 96cbf7317c7..9ba043427b5 100644 --- a/tests/components/min_max/test_sensor.py +++ b/tests/components/min_max/test_sensor.py @@ -33,6 +33,7 @@ MEAN_4_DIGITS = round(sum(VALUES) / COUNT, 4) MEDIAN = round(statistics.median(VALUES), 2) RANGE_1_DIGIT = round(max(VALUES) - min(VALUES), 1) RANGE_4_DIGITS = round(max(VALUES) - min(VALUES), 4) +SUM_VALUE = sum(VALUES) async def test_default_name_sensor(hass: HomeAssistant) -> None: @@ -466,3 +467,60 @@ async def test_sensor_incorrect_state( assert state.state == "15.3" assert "Unable to store state. Only numerical states are supported" in caplog.text + + +async def test_sum_sensor(hass: HomeAssistant) -> None: + """Test the sum sensor.""" + config = { + "sensor": { + "platform": "min_max", + "name": "test_sum", + "type": "sum", + "entity_ids": ["sensor.test_1", "sensor.test_2", "sensor.test_3"], + "unique_id": "very_unique_id_sum_sensor", + } + } + + assert await async_setup_component(hass, "sensor", config) + await hass.async_block_till_done() + + entity_ids = config["sensor"]["entity_ids"] + + for entity_id, value in dict(zip(entity_ids, VALUES)).items(): + hass.states.async_set(entity_id, value) + await hass.async_block_till_done() + + state = hass.states.get("sensor.test_sum") + + assert str(float(SUM_VALUE)) == state.state + assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT + + entity_reg = er.async_get(hass) + entity = entity_reg.async_get("sensor.test_sum") + assert entity.unique_id == "very_unique_id_sum_sensor" + + +async def test_sum_sensor_no_state(hass: HomeAssistant) -> None: + """Test the sum sensor with no state .""" + config = { + "sensor": { + "platform": "min_max", + "name": "test_sum", + "type": "sum", + "entity_ids": ["sensor.test_1", "sensor.test_2", "sensor.test_3"], + "unique_id": "very_unique_id_sum_sensor", + } + } + + assert await async_setup_component(hass, "sensor", config) + await hass.async_block_till_done() + + entity_ids = config["sensor"]["entity_ids"] + + for entity_id, value in dict(zip(entity_ids, VALUES_ERROR)).items(): + hass.states.async_set(entity_id, value) + await hass.async_block_till_done() + + state = hass.states.get("sensor.test_sum") + + assert state.state == STATE_UNKNOWN From 978122c88252430b4d0b357e4a7d141f2ab3dd73 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 24 Nov 2022 20:30:23 +0100 Subject: [PATCH 0692/1033] Fix docstring in SchemaFlowFormStep (#82612) --- .../helpers/schema_config_entry_flow.py | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 2154ebc216f..b132e3edd86 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -25,8 +25,13 @@ class SchemaFlowError(Exception): class SchemaFlowFormStep: """Define a config or options flow step.""" - # Optional schema for requesting and validating user input. If schema validation - # fails, the step will be retried. If the schema is None, no user input is requested. + # Optional voluptuous schema, or function which returns a schema or None, for + # requesting and validating user input. + # If a function is specified, the function will be passed the handler, which is + # either an instance of SchemaConfigFlowHandler or SchemaOptionsFlowHandler, and the + # union of config entry options and user input from previous steps. + # If schema validation fails, the step will be retried. If the schema is None, no + # user input is requested. schema: vol.Schema | Callable[ [SchemaConfigFlowHandler | SchemaOptionsFlowHandler, dict[str, Any]], vol.Schema | None, @@ -49,20 +54,6 @@ class SchemaFlowFormStep: - If `next_step` is None, the flow is ended with `FlowResultType.CREATE_ENTRY`. """ - # Optional function to allow amending a form schema. - # The update_form_schema function is called before async_show_form is called. The - # update_form_schema function is passed the handler, which is either an instance of - # SchemaConfigFlowHandler or SchemaOptionsFlowHandler, the schema, and the union of - # config entry options and user input from previous steps. - update_form_schema: Callable[ - [ - SchemaConfigFlowHandler | SchemaOptionsFlowHandler, - vol.Schema, - dict[str, Any], - ], - vol.Schema, - ] = lambda _handler, schema, _options: schema - @dataclass class SchemaFlowMenuStep: From 815dfe9134db71b9e182fa7ac974393aaf6910d5 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Thu, 24 Nov 2022 21:38:32 +0200 Subject: [PATCH 0693/1033] Fix Shelly gen2 channel name (#82655) * Fix Shelly gen2 channel name * Review comment --- homeassistant/components/shelly/utils.py | 2 +- tests/components/shelly/test_utils.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index bf242d47e6c..e13395999b1 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -298,7 +298,7 @@ def get_rpc_channel_name(device: RpcDevice, key: str) -> str: entity_name = device.config[key].get("name", device_name) if entity_name is None: - if [k for k in key if k.startswith(("input", "switch"))]: + if key.startswith(("input:", "switch:")): return f"{device_name} {key.replace(':', '_')}" return device_name diff --git a/tests/components/shelly/test_utils.py b/tests/components/shelly/test_utils.py index c78adf4c37c..c817b7d620c 100644 --- a/tests/components/shelly/test_utils.py +++ b/tests/components/shelly/test_utils.py @@ -208,6 +208,7 @@ async def test_get_block_input_triggers(mock_block_device, monkeypatch): async def test_get_rpc_channel_name(mock_rpc_device): """Test get RPC channel name.""" assert get_rpc_channel_name(mock_rpc_device, "input:0") == "test switch_0" + assert get_rpc_channel_name(mock_rpc_device, "input:3") == "Test name switch_3" async def test_get_rpc_input_triggers(mock_rpc_device, monkeypatch): From 3b0a42f8f49ea0f7bde3603a779c5f810ec7a302 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Thu, 24 Nov 2022 20:39:14 +0100 Subject: [PATCH 0694/1033] Code quality Local IP (#82050) --- homeassistant/components/local_ip/config_flow.py | 2 -- homeassistant/components/local_ip/sensor.py | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/local_ip/config_flow.py b/homeassistant/components/local_ip/config_flow.py index 27bd5340d40..be708f5d8b9 100644 --- a/homeassistant/components/local_ip/config_flow.py +++ b/homeassistant/components/local_ip/config_flow.py @@ -12,8 +12,6 @@ from .const import DOMAIN class SimpleConfigFlow(ConfigFlow, domain=DOMAIN): """Handle a config flow for local_ip.""" - VERSION = 1 - async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: diff --git a/homeassistant/components/local_ip/sensor.py b/homeassistant/components/local_ip/sensor.py index 56c1fac7c8f..4c502895b3f 100644 --- a/homeassistant/components/local_ip/sensor.py +++ b/homeassistant/components/local_ip/sensor.py @@ -7,7 +7,7 @@ from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN, SENSOR +from .const import SENSOR async def async_setup_entry( @@ -16,7 +16,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up the platform from config_entry.""" - name = entry.data.get(CONF_NAME) or DOMAIN + name = entry.data.get(CONF_NAME) or "Local IP" async_add_entities([IPSensor(name)], True) From e4fbbdfa052fb28da054c9ff0646721874a04b1d Mon Sep 17 00:00:00 2001 From: Teemu R Date: Thu, 24 Nov 2022 20:49:30 +0100 Subject: [PATCH 0695/1033] Do not crash on non-existing enum values in xiaomi_miio.select (#82625) fixes undefined --- homeassistant/components/xiaomi_miio/select.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/xiaomi_miio/select.py b/homeassistant/components/xiaomi_miio/select.py index 118f3cd5c77..9b5f0f50a06 100644 --- a/homeassistant/components/xiaomi_miio/select.py +++ b/homeassistant/components/xiaomi_miio/select.py @@ -2,6 +2,7 @@ from __future__ import annotations from dataclasses import dataclass, field +import logging from typing import NamedTuple from miio.fan_common import LedBrightness as FanLedBrightness @@ -66,6 +67,9 @@ ATTR_LED_BRIGHTNESS = "led_brightness" ATTR_PTC_LEVEL = "ptc_level" +_LOGGER = logging.getLogger(__name__) + + @dataclass class XiaomiMiioSelectDescription(SelectEntityDescription): """A class that describes select entities.""" @@ -248,11 +252,17 @@ class XiaomiGenericSelector(XiaomiSelector): @callback def _handle_coordinator_update(self): """Fetch state from the device.""" - attr = self._enum_class( - self._extract_value_from_attribute( + try: + value = self._extract_value_from_attribute( self.coordinator.data, self.entity_description.attr_name ) - ) + attr = self._enum_class(value) + except ValueError: # if the value does not exist in + _LOGGER.debug( + "Value '%s' does not exist in enum %s", value, self._enum_class + ) + attr = None + if attr is not None: self._current_attr = attr self.async_write_ha_state() From a63581b5c857984b4a2c254cd009fa3321149a3c Mon Sep 17 00:00:00 2001 From: G Johansson Date: Thu, 24 Nov 2022 21:12:47 +0100 Subject: [PATCH 0696/1033] Add unique id for Scrape config entry entities (#82508) * scrape unique id * fix uuid str * add back UoM --- homeassistant/components/scrape/config_flow.py | 12 +++++++++++- homeassistant/components/scrape/sensor.py | 3 ++- tests/components/scrape/conftest.py | 13 +++++++++++++ tests/components/scrape/test_config_flow.py | 4 ++++ tests/components/scrape/test_sensor.py | 5 +++++ 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/scrape/config_flow.py b/homeassistant/components/scrape/config_flow.py index 6bb68d2fdec..b5557daff15 100644 --- a/homeassistant/components/scrape/config_flow.py +++ b/homeassistant/components/scrape/config_flow.py @@ -3,6 +3,7 @@ from __future__ import annotations from collections.abc import Mapping from typing import Any +import uuid import voluptuous as vol @@ -24,6 +25,7 @@ from homeassistant.const import ( CONF_PASSWORD, CONF_RESOURCE, CONF_TIMEOUT, + CONF_UNIQUE_ID, CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME, CONF_VALUE_TEMPLATE, @@ -125,7 +127,15 @@ def validate_rest_setup(user_input: dict[str, Any]) -> dict[str, Any]: def validate_sensor_setup(user_input: dict[str, Any]) -> dict[str, Any]: """Validate sensor setup.""" - return {"sensor": [{**user_input, CONF_INDEX: int(user_input[CONF_INDEX])}]} + return { + "sensor": [ + { + **user_input, + CONF_INDEX: int(user_input[CONF_INDEX]), + CONF_UNIQUE_ID: str(uuid.uuid1()), + } + ] + } DATA_SCHEMA_RESOURCE = vol.Schema(RESOURCE_SETUP) diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index 943cda9564d..e16f6c20f8d 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -163,6 +163,7 @@ async def async_setup_entry( attr: str | None = sensor_config.get(CONF_ATTRIBUTE) index: int = int(sensor_config[CONF_INDEX]) value_string: str | None = sensor_config.get(CONF_VALUE_TEMPLATE) + unique_id: str = sensor_config[CONF_UNIQUE_ID] value_template: Template | None = ( Template(value_string, hass) if value_string is not None else None @@ -173,7 +174,7 @@ async def async_setup_entry( coordinator, sensor_config, name, - None, + unique_id, select, attr, index, diff --git a/tests/components/scrape/conftest.py b/tests/components/scrape/conftest.py index c3b63cbb6c1..fa90786ec2f 100644 --- a/tests/components/scrape/conftest.py +++ b/tests/components/scrape/conftest.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Any from unittest.mock import patch +import uuid import pytest @@ -15,6 +16,7 @@ from homeassistant.const import ( CONF_NAME, CONF_RESOURCE, CONF_TIMEOUT, + CONF_UNIQUE_ID, CONF_VERIFY_SSL, ) from homeassistant.core import HomeAssistant @@ -41,6 +43,7 @@ async def get_config_to_integration_load() -> dict[str, Any]: CONF_NAME: "Current version", CONF_SELECT: ".current-version h1", CONF_INDEX: 0, + CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120002", } ], } @@ -78,3 +81,13 @@ async def load_integration( await hass.async_block_till_done() return config_entry + + +@pytest.fixture(autouse=True) +def uuid_fixture() -> str: + """Automatically path uuid generator.""" + with patch( + "homeassistant.components.scrape.config_flow.uuid.uuid1", + return_value=uuid.UUID("3699ef88-69e6-11ed-a1eb-0242ac120002"), + ): + yield diff --git a/tests/components/scrape/test_config_flow.py b/tests/components/scrape/test_config_flow.py index b8a84fd73e9..b097dc7a5a0 100644 --- a/tests/components/scrape/test_config_flow.py +++ b/tests/components/scrape/test_config_flow.py @@ -18,6 +18,7 @@ from homeassistant.const import ( CONF_PASSWORD, CONF_RESOURCE, CONF_TIMEOUT, + CONF_UNIQUE_ID, CONF_USERNAME, CONF_VERIFY_SSL, ) @@ -78,6 +79,7 @@ async def test_form(hass: HomeAssistant, get_data: MockRestData) -> None: CONF_NAME: "Current version", CONF_SELECT: ".current-version h1", CONF_INDEX: 0.0, + CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120002", } ], } @@ -148,6 +150,7 @@ async def test_flow_fails(hass: HomeAssistant, get_data: MockRestData) -> None: CONF_NAME: "Current version", CONF_SELECT: ".current-version h1", CONF_INDEX: 0.0, + CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120002", } ], } @@ -192,6 +195,7 @@ async def test_options_flow(hass: HomeAssistant, loaded_entry: MockConfigEntry) CONF_NAME: "Current version", CONF_SELECT: ".current-version h1", CONF_INDEX: 0.0, + CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120002", } ], } diff --git a/tests/components/scrape/test_sensor.py b/tests/components/scrape/test_sensor.py index 2f83cdef239..83607f1b993 100644 --- a/tests/components/scrape/test_sensor.py +++ b/tests/components/scrape/test_sensor.py @@ -414,3 +414,8 @@ async def test_setup_config_entry( state = hass.states.get("sensor.current_version") assert state.state == "Current Version: 2021.12.10" + + entity_reg = er.async_get(hass) + entity = entity_reg.async_get("sensor.current_version") + + assert entity.unique_id == "3699ef88-69e6-11ed-a1eb-0242ac120002" From c30ce12c4960afaeb959ee23b1d1d577153d2a67 Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Thu, 24 Nov 2022 15:14:26 -0500 Subject: [PATCH 0697/1033] Remove deprecated Radarr YAML config (#81210) --- homeassistant/components/radarr/__init__.py | 24 ---------- .../components/radarr/config_flow.py | 33 +------------ homeassistant/components/radarr/sensor.py | 39 ++++----------- homeassistant/components/radarr/strings.json | 6 +-- .../components/radarr/translations/en.json | 6 +-- tests/components/radarr/__init__.py | 13 ----- tests/components/radarr/test_config_flow.py | 48 +------------------ 7 files changed, 20 insertions(+), 149 deletions(-) diff --git a/homeassistant/components/radarr/__init__.py b/homeassistant/components/radarr/__init__.py index 403bedda94a..3584d5242b6 100644 --- a/homeassistant/components/radarr/__init__.py +++ b/homeassistant/components/radarr/__init__.py @@ -6,12 +6,10 @@ from typing import Any, cast from aiopyarr.models.host_configuration import PyArrHostConfiguration from aiopyarr.radarr_client import RadarrClient -from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_SW_VERSION, CONF_API_KEY, - CONF_PLATFORM, CONF_URL, CONF_VERIFY_SSL, Platform, @@ -20,8 +18,6 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo, EntityDescription -from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue -from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DEFAULT_NAME, DOMAIN @@ -37,26 +33,6 @@ from .coordinator import ( PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] -async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up the Steam integration.""" - if SENSOR_DOMAIN not in config: - return True - - for entry in config[SENSOR_DOMAIN]: - if entry[CONF_PLATFORM] == DOMAIN: - async_create_issue( - hass, - DOMAIN, - "deprecated_yaml", - breaks_in_ha_version="2022.10.0", - is_fixable=False, - severity=IssueSeverity.WARNING, - translation_key="deprecated_yaml", - ) - - return True - - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Radarr from a config entry.""" host_configuration = PyArrHostConfiguration( diff --git a/homeassistant/components/radarr/config_flow.py b/homeassistant/components/radarr/config_flow.py index c37eeba4969..3feb9a01bea 100644 --- a/homeassistant/components/radarr/config_flow.py +++ b/homeassistant/components/radarr/config_flow.py @@ -11,19 +11,12 @@ from aiopyarr.radarr_client import RadarrClient import voluptuous as vol from homeassistant.config_entries import ConfigEntry, ConfigFlow -from homeassistant.const import ( - CONF_API_KEY, - CONF_HOST, - CONF_PORT, - CONF_SSL, - CONF_URL, - CONF_VERIFY_SSL, -) +from homeassistant.const import CONF_API_KEY, CONF_URL, CONF_VERIFY_SSL from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession -from .const import DEFAULT_NAME, DEFAULT_URL, DOMAIN, LOGGER +from .const import DEFAULT_NAME, DEFAULT_URL, DOMAIN class RadarrConfigFlow(ConfigFlow, domain=DOMAIN): @@ -106,28 +99,6 @@ class RadarrConfigFlow(ConfigFlow, domain=DOMAIN): errors=errors, ) - async def async_step_import(self, config: dict[str, Any]) -> FlowResult: - """Import a config entry from configuration.yaml.""" - for entry in self._async_current_entries(): - if entry.data[CONF_API_KEY] == config[CONF_API_KEY]: - _part = config[CONF_API_KEY][0:4] - _msg = f"Radarr yaml config with partial key {_part} has been imported. Please remove it" - LOGGER.warning(_msg) - return self.async_abort(reason="already_configured") - proto = "https" if config[CONF_SSL] else "http" - host_port = f"{config[CONF_HOST]}:{config[CONF_PORT]}" - path = "" - if config["urlbase"].rstrip("/") not in ("", "/", "/api"): - path = config["urlbase"].rstrip("/") - return self.async_create_entry( - title=DEFAULT_NAME, - data={ - CONF_URL: f"{proto}://{host_port}{path}", - CONF_API_KEY: config[CONF_API_KEY], - CONF_VERIFY_SSL: False, - }, - ) - async def validate_input( hass: HomeAssistant, data: dict[str, Any] diff --git a/homeassistant/components/radarr/sensor.py b/homeassistant/components/radarr/sensor.py index 27d1a5487a2..0f244b92a16 100644 --- a/homeassistant/components/radarr/sensor.py +++ b/homeassistant/components/radarr/sensor.py @@ -8,30 +8,23 @@ from datetime import datetime, timezone from typing import Any, Generic from aiopyarr import Diskspace, RootFolder, SystemStatus -import voluptuous as vol from homeassistant.components.sensor import ( - PLATFORM_SCHEMA, SensorDeviceClass, SensorEntity, SensorEntityDescription, ) -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - CONF_API_KEY, - CONF_HOST, - CONF_MONITORED_CONDITIONS, - CONF_PORT, - CONF_SSL, DATA_BYTES, DATA_GIGABYTES, DATA_KILOBYTES, DATA_MEGABYTES, ) from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import RadarrEntity @@ -112,22 +105,6 @@ BYTE_SIZES = [ DATA_MEGABYTES, DATA_GIGABYTES, ] -# Deprecated in Home Assistant 2022.10 -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_API_KEY): cv.string, - vol.Optional("days", default=1): cv.string, - vol.Optional(CONF_HOST, default="localhost"): cv.string, - vol.Optional("include_paths", default=[]): cv.ensure_list, - vol.Optional(CONF_MONITORED_CONDITIONS, default=["movies"]): vol.All( - cv.ensure_list - ), - vol.Optional(CONF_PORT, default=7878): cv.port, - vol.Optional(CONF_SSL, default=False): cv.boolean, - vol.Optional("unit", default=DATA_GIGABYTES): cv.string, - vol.Optional("urlbase", default=""): cv.string, - } -) PARALLEL_UPDATES = 1 @@ -139,10 +116,14 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Radarr platform.""" - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=config - ) + async_create_issue( + hass, + DOMAIN, + "removed_yaml", + breaks_in_ha_version="2022.12.0", + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key="removed_yaml", ) diff --git a/homeassistant/components/radarr/strings.json b/homeassistant/components/radarr/strings.json index 6fa9b64c2c8..299dd0a56b0 100644 --- a/homeassistant/components/radarr/strings.json +++ b/homeassistant/components/radarr/strings.json @@ -36,9 +36,9 @@ } }, "issues": { - "deprecated_yaml": { - "title": "The Radarr YAML configuration is being removed", - "description": "Configuring Radarr using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Radarr YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue." + "removed_yaml": { + "title": "The Radarr YAML configuration has been removed", + "description": "Configuring Radarr using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue." } } } diff --git a/homeassistant/components/radarr/translations/en.json b/homeassistant/components/radarr/translations/en.json index cba064fd429..0feccae33fd 100644 --- a/homeassistant/components/radarr/translations/en.json +++ b/homeassistant/components/radarr/translations/en.json @@ -27,9 +27,9 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Configuring Radarr using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Radarr YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", - "title": "The Radarr YAML configuration is being removed" + "removed_yaml": { + "title": "The Radarr YAML configuration has been removed", + "description": "Configuring Radarr using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue." } }, "options": { diff --git a/tests/components/radarr/__init__.py b/tests/components/radarr/__init__.py index 639d548e4be..7e574b1e3e0 100644 --- a/tests/components/radarr/__init__.py +++ b/tests/components/radarr/__init__.py @@ -7,10 +7,6 @@ from aiohttp.client_exceptions import ClientError from homeassistant.components.radarr.const import DOMAIN from homeassistant.const import ( CONF_API_KEY, - CONF_HOST, - CONF_MONITORED_CONDITIONS, - CONF_PORT, - CONF_SSL, CONF_URL, CONF_VERIFY_SSL, CONTENT_TYPE_JSON, @@ -32,15 +28,6 @@ MOCK_USER_INPUT = { CONF_VERIFY_SSL: False, } -CONF_IMPORT_DATA = { - CONF_API_KEY: API_KEY, - CONF_HOST: "192.168.1.189", - CONF_MONITORED_CONDITIONS: ["Stream count"], - CONF_PORT: "7887", - "urlbase": "/test", - CONF_SSL: False, -} - CONF_DATA = { CONF_URL: URL, CONF_API_KEY: API_KEY, diff --git a/tests/components/radarr/test_config_flow.py b/tests/components/radarr/test_config_flow.py index 6aac4e369fe..0e328b50f94 100644 --- a/tests/components/radarr/test_config_flow.py +++ b/tests/components/radarr/test_config_flow.py @@ -1,18 +1,17 @@ """Test Radarr config flow.""" -from unittest.mock import AsyncMock, patch +from unittest.mock import patch from aiopyarr import exceptions from homeassistant import data_entry_flow from homeassistant.components.radarr.const import DEFAULT_NAME, DOMAIN -from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_REAUTH, SOURCE_USER +from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER from homeassistant.const import CONF_API_KEY, CONF_SOURCE, CONF_URL, CONF_VERIFY_SSL from homeassistant.core import HomeAssistant from . import ( API_KEY, CONF_DATA, - CONF_IMPORT_DATA, MOCK_REAUTH_INPUT, MOCK_USER_INPUT, URL, @@ -23,52 +22,9 @@ from . import ( setup_integration, ) -from tests.common import MockConfigEntry from tests.test_util.aiohttp import AiohttpClientMocker -def _patch_setup(): - return patch("homeassistant.components.radarr.async_setup_entry") - - -async def test_flow_import(hass: HomeAssistant): - """Test import step.""" - with patch( - "homeassistant.components.radarr.config_flow.RadarrClient.async_get_system_status", - return_value=AsyncMock(), - ), _patch_setup(): - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data=CONF_IMPORT_DATA, - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["title"] == DEFAULT_NAME - assert result["data"] == CONF_DATA | { - CONF_URL: "http://192.168.1.189:7887/test" - } - assert result["data"][CONF_URL] == "http://192.168.1.189:7887/test" - - -async def test_flow_import_already_configured(hass: HomeAssistant): - """Test import step already configured.""" - entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_INPUT) - entry.add_to_hass(hass) - - with patch( - "homeassistant.components.radarr.config_flow.RadarrClient.async_get_system_status", - return_value=AsyncMock(), - ): - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data=CONF_IMPORT_DATA, - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "already_configured" - - async def test_show_user_form(hass: HomeAssistant) -> None: """Test that the user set up form is served.""" result = await hass.config_entries.flow.async_init( From 18dd605a74fd88a7f01ac682d98dc540a64408a2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 24 Nov 2022 13:38:53 -0700 Subject: [PATCH 0698/1033] Fix un-retrieved future in esphome ble client when library raises (#82537) --- .../components/esphome/bluetooth/client.py | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index a5075da0bf9..66b687b6de9 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -265,11 +265,26 @@ class ESPHomeClient(BaseBleakClient): if not (scanner := async_scanner_by_source(self._hass, self._source)): raise BleakError("Scanner disappeared for {self._source}") with scanner.connecting(): - self._cancel_connection_state = await self._client.bluetooth_device_connect( - self._address_as_int, - _on_bluetooth_connection_state, - timeout=timeout, - ) + try: + self._cancel_connection_state = ( + await self._client.bluetooth_device_connect( + self._address_as_int, + _on_bluetooth_connection_state, + timeout=timeout, + ) + ) + except Exception: # pylint: disable=broad-except + with contextlib.suppress(BleakError): + # If the connect call throws an exception, + # we need to make sure we await the future + # to avoid a warning about an un-retrieved + # exception since we prefer to raise the + # exception from the connect call as it + # will be more descriptive. + if connected_future.done(): + await connected_future + connected_future.cancel() + raise await connected_future await self.get_services(dangerous_use_bleak_cache=dangerous_use_bleak_cache) self._disconnected_event = asyncio.Event() From 6c024c88750e1ef56671a85b98c268eaa01259c6 Mon Sep 17 00:00:00 2001 From: Gleb Sinyavskiy Date: Thu, 24 Nov 2022 21:51:18 +0100 Subject: [PATCH 0699/1033] Bump aiotractive, properly handle auth errors when reading events (#76715) * Bump aiotractive, start handling authorzation errors when reading events * Bump aiotractive, start handling authorzation errors when reading events * Properly handle unauthorized errors * Update homeassistant/components/tractive/__init__.py Co-authored-by: J. Nick Koston * Use await when unsibscribing after auth error * Update homeassistant/components/tractive/__init__.py Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston --- homeassistant/components/tractive/__init__.py | 18 ++++++++++++++++-- .../components/tractive/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/tractive/__init__.py b/homeassistant/components/tractive/__init__.py index aa5048b89c0..cc0b2b2b6cb 100644 --- a/homeassistant/components/tractive/__init__.py +++ b/homeassistant/components/tractive/__init__.py @@ -83,7 +83,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await client.close() raise ConfigEntryNotReady from error - tractive = TractiveClient(hass, client, creds["user_id"]) + tractive = TractiveClient(hass, client, creds["user_id"], entry) tractive.subscribe() try: @@ -148,7 +148,11 @@ class TractiveClient: """A Tractive client.""" def __init__( - self, hass: HomeAssistant, client: aiotractive.Tractive, user_id: str + self, + hass: HomeAssistant, + client: aiotractive.Tractive, + user_id: str, + config_entry: ConfigEntry, ) -> None: """Initialize the client.""" self._hass = hass @@ -157,6 +161,7 @@ class TractiveClient: self._last_hw_time = 0 self._last_pos_time = 0 self._listen_task: asyncio.Task | None = None + self._config_entry = config_entry @property def user_id(self) -> str: @@ -210,6 +215,15 @@ class TractiveClient: ): self._last_pos_time = event["position"]["time"] self._send_position_update(event) + except aiotractive.exceptions.UnauthorizedError: + self._config_entry.async_start_reauth(self._hass) + await self.unsubscribe() + _LOGGER.error( + "Authentication failed for %s, try reconfiguring device", + self._config_entry.data[CONF_EMAIL], + ) + return + except aiotractive.exceptions.TractiveError: _LOGGER.debug( "Tractive is not available. Internet connection is down? Sleeping %i seconds and retrying", diff --git a/homeassistant/components/tractive/manifest.json b/homeassistant/components/tractive/manifest.json index 308f190a063..496c69ddcf7 100644 --- a/homeassistant/components/tractive/manifest.json +++ b/homeassistant/components/tractive/manifest.json @@ -3,7 +3,7 @@ "name": "Tractive", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tractive", - "requirements": ["aiotractive==0.5.4"], + "requirements": ["aiotractive==0.5.5"], "codeowners": ["@Danielhiversen", "@zhulik", "@bieniu"], "iot_class": "cloud_push", "loggers": ["aiotractive"], diff --git a/requirements_all.txt b/requirements_all.txt index 4d2363dec38..cf2504c8513 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -279,7 +279,7 @@ aioswitcher==3.1.0 aiosyncthing==0.5.1 # homeassistant.components.tractive -aiotractive==0.5.4 +aiotractive==0.5.5 # homeassistant.components.unifi aiounifi==41 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1cebc382a7b..6e96e13f4b9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -254,7 +254,7 @@ aioswitcher==3.1.0 aiosyncthing==0.5.1 # homeassistant.components.tractive -aiotractive==0.5.4 +aiotractive==0.5.5 # homeassistant.components.unifi aiounifi==41 From 9f9114cb4ae4674c87309b363495f18e9a42dd81 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 24 Nov 2022 21:59:41 +0100 Subject: [PATCH 0700/1033] Simplify SchemaFlowStep typing (#82661) * Simplify SchemaFlowStep typing * Adjust accuweather --- .../components/accuweather/config_flow.py | 5 ++- .../components/derivative/config_flow.py | 9 +++-- homeassistant/components/group/config_flow.py | 4 +-- .../components/integration/config_flow.py | 9 +++-- .../components/min_max/config_flow.py | 9 +++-- .../components/scrape/config_flow.py | 5 ++- .../components/switch_as_x/config_flow.py | 3 +- .../components/threshold/config_flow.py | 5 ++- homeassistant/components/tod/config_flow.py | 9 +++-- .../components/utility_meter/config_flow.py | 7 ++-- .../helpers/schema_config_entry_flow.py | 33 +++++++++++-------- 11 files changed, 48 insertions(+), 50 deletions(-) diff --git a/homeassistant/components/accuweather/config_flow.py b/homeassistant/components/accuweather/config_flow.py index 357327b298b..1480f6c1352 100644 --- a/homeassistant/components/accuweather/config_flow.py +++ b/homeassistant/components/accuweather/config_flow.py @@ -19,7 +19,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv from homeassistant.helpers.schema_config_entry_flow import ( SchemaFlowFormStep, - SchemaFlowMenuStep, SchemaOptionsFlowHandler, ) @@ -30,8 +29,8 @@ OPTIONS_SCHEMA = vol.Schema( vol.Optional(CONF_FORECAST, default=False): bool, } ) -OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "init": SchemaFlowFormStep(OPTIONS_SCHEMA) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), } diff --git a/homeassistant/components/derivative/config_flow.py b/homeassistant/components/derivative/config_flow.py index eea2b303a12..1b98f4af352 100644 --- a/homeassistant/components/derivative/config_flow.py +++ b/homeassistant/components/derivative/config_flow.py @@ -18,7 +18,6 @@ from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, SchemaFlowFormStep, - SchemaFlowMenuStep, ) from .const import ( @@ -76,12 +75,12 @@ CONFIG_SCHEMA = vol.Schema( } ).extend(OPTIONS_SCHEMA.schema) -CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "user": SchemaFlowFormStep(CONFIG_SCHEMA) +CONFIG_FLOW = { + "user": SchemaFlowFormStep(CONFIG_SCHEMA), } -OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "init": SchemaFlowFormStep(OPTIONS_SCHEMA) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), } diff --git a/homeassistant/components/group/config_flow.py b/homeassistant/components/group/config_flow.py index f47dbcb3b44..5453d3024f5 100644 --- a/homeassistant/components/group/config_flow.py +++ b/homeassistant/components/group/config_flow.py @@ -115,7 +115,7 @@ def set_group_type(group_type: str) -> Callable[[dict[str, Any]], dict[str, Any] return _set_group_type -CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { +CONFIG_FLOW = { "user": SchemaFlowMenuStep(GROUP_TYPES), "binary_sensor": SchemaFlowFormStep( BINARY_SENSOR_CONFIG_SCHEMA, set_group_type("binary_sensor") @@ -139,7 +139,7 @@ CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { } -OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { +OPTIONS_FLOW = { "init": SchemaFlowFormStep(None, next_step=choose_options_step), "binary_sensor": SchemaFlowFormStep(binary_sensor_options_schema), "cover": SchemaFlowFormStep(partial(basic_group_options_schema, "cover")), diff --git a/homeassistant/components/integration/config_flow.py b/homeassistant/components/integration/config_flow.py index aab5671f64b..dba3103906f 100644 --- a/homeassistant/components/integration/config_flow.py +++ b/homeassistant/components/integration/config_flow.py @@ -18,7 +18,6 @@ from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, SchemaFlowFormStep, - SchemaFlowMenuStep, ) from .const import ( @@ -89,12 +88,12 @@ CONFIG_SCHEMA = vol.Schema( } ) -CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "user": SchemaFlowFormStep(CONFIG_SCHEMA) +CONFIG_FLOW = { + "user": SchemaFlowFormStep(CONFIG_SCHEMA), } -OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "init": SchemaFlowFormStep(OPTIONS_SCHEMA) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), } diff --git a/homeassistant/components/min_max/config_flow.py b/homeassistant/components/min_max/config_flow.py index d5c9ce43a99..ecf47dc0c63 100644 --- a/homeassistant/components/min_max/config_flow.py +++ b/homeassistant/components/min_max/config_flow.py @@ -11,7 +11,6 @@ from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, SchemaFlowFormStep, - SchemaFlowMenuStep, ) from .const import CONF_ENTITY_IDS, CONF_ROUND_DIGITS, DOMAIN @@ -49,12 +48,12 @@ CONFIG_SCHEMA = vol.Schema( } ).extend(OPTIONS_SCHEMA.schema) -CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "user": SchemaFlowFormStep(CONFIG_SCHEMA) +CONFIG_FLOW = { + "user": SchemaFlowFormStep(CONFIG_SCHEMA), } -OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "init": SchemaFlowFormStep(OPTIONS_SCHEMA) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), } diff --git a/homeassistant/components/scrape/config_flow.py b/homeassistant/components/scrape/config_flow.py index b5557daff15..eedc584a394 100644 --- a/homeassistant/components/scrape/config_flow.py +++ b/homeassistant/components/scrape/config_flow.py @@ -39,7 +39,6 @@ from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, SchemaFlowError, SchemaFlowFormStep, - SchemaFlowMenuStep, SchemaOptionsFlowHandler, ) from homeassistant.helpers.selector import ( @@ -141,7 +140,7 @@ def validate_sensor_setup(user_input: dict[str, Any]) -> dict[str, Any]: DATA_SCHEMA_RESOURCE = vol.Schema(RESOURCE_SETUP) DATA_SCHEMA_SENSOR = vol.Schema(SENSOR_SETUP) -CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { +CONFIG_FLOW = { "user": SchemaFlowFormStep( schema=DATA_SCHEMA_RESOURCE, next_step="sensor", @@ -152,7 +151,7 @@ CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { validate_user_input=validate_sensor_setup, ), } -OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { +OPTIONS_FLOW = { "init": SchemaFlowFormStep(DATA_SCHEMA_RESOURCE), } diff --git a/homeassistant/components/switch_as_x/config_flow.py b/homeassistant/components/switch_as_x/config_flow.py index 991f4f33a6b..8b6527eb49e 100644 --- a/homeassistant/components/switch_as_x/config_flow.py +++ b/homeassistant/components/switch_as_x/config_flow.py @@ -11,7 +11,6 @@ from homeassistant.helpers import entity_registry as er, selector from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, SchemaFlowFormStep, - SchemaFlowMenuStep, wrapped_entity_config_entry_title, ) @@ -25,7 +24,7 @@ TARGET_DOMAIN_OPTIONS = [ selector.SelectOptionDict(value=Platform.SIREN, label="Siren"), ] -CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { +CONFIG_FLOW = { "user": SchemaFlowFormStep( vol.Schema( { diff --git a/homeassistant/components/threshold/config_flow.py b/homeassistant/components/threshold/config_flow.py index e3af2e9c567..20cedcfca6b 100644 --- a/homeassistant/components/threshold/config_flow.py +++ b/homeassistant/components/threshold/config_flow.py @@ -12,7 +12,6 @@ from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, SchemaFlowError, SchemaFlowFormStep, - SchemaFlowMenuStep, ) from .const import CONF_HYSTERESIS, CONF_LOWER, CONF_UPPER, DEFAULT_HYSTERESIS, DOMAIN @@ -56,11 +55,11 @@ CONFIG_SCHEMA = vol.Schema( } ).extend(OPTIONS_SCHEMA.schema) -CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { +CONFIG_FLOW = { "user": SchemaFlowFormStep(CONFIG_SCHEMA, validate_user_input=_validate_mode) } -OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { +OPTIONS_FLOW = { "init": SchemaFlowFormStep(OPTIONS_SCHEMA, validate_user_input=_validate_mode) } diff --git a/homeassistant/components/tod/config_flow.py b/homeassistant/components/tod/config_flow.py index 5155d15561b..6e21b8046a1 100644 --- a/homeassistant/components/tod/config_flow.py +++ b/homeassistant/components/tod/config_flow.py @@ -11,7 +11,6 @@ from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, SchemaFlowFormStep, - SchemaFlowMenuStep, ) from .const import CONF_AFTER_TIME, CONF_BEFORE_TIME, DOMAIN @@ -29,12 +28,12 @@ CONFIG_SCHEMA = vol.Schema( } ).extend(OPTIONS_SCHEMA.schema) -CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "user": SchemaFlowFormStep(CONFIG_SCHEMA) +CONFIG_FLOW = { + "user": SchemaFlowFormStep(CONFIG_SCHEMA), } -OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "init": SchemaFlowFormStep(OPTIONS_SCHEMA) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), } diff --git a/homeassistant/components/utility_meter/config_flow.py b/homeassistant/components/utility_meter/config_flow.py index cabd90ba8bd..fbdf302e9b7 100644 --- a/homeassistant/components/utility_meter/config_flow.py +++ b/homeassistant/components/utility_meter/config_flow.py @@ -12,7 +12,6 @@ from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, SchemaFlowError, SchemaFlowFormStep, - SchemaFlowMenuStep, ) from .const import ( @@ -93,12 +92,12 @@ CONFIG_SCHEMA = vol.Schema( } ) -CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { +CONFIG_FLOW = { "user": SchemaFlowFormStep(CONFIG_SCHEMA, validate_user_input=_validate_config) } -OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "init": SchemaFlowFormStep(OPTIONS_SCHEMA) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), } diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index b132e3edd86..526f59bd103 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -22,20 +22,27 @@ class SchemaFlowError(Exception): @dataclass -class SchemaFlowFormStep: +class SchemaFlowStep: """Define a config or options flow step.""" - # Optional voluptuous schema, or function which returns a schema or None, for - # requesting and validating user input. - # If a function is specified, the function will be passed the handler, which is - # either an instance of SchemaConfigFlowHandler or SchemaOptionsFlowHandler, and the - # union of config entry options and user input from previous steps. - # If schema validation fails, the step will be retried. If the schema is None, no - # user input is requested. + +@dataclass +class SchemaFlowFormStep(SchemaFlowStep): + """Define a config or options flow form step.""" + schema: vol.Schema | Callable[ [SchemaConfigFlowHandler | SchemaOptionsFlowHandler, dict[str, Any]], vol.Schema | None, ] | None + """Optional voluptuous schema, or function which returns a schema or None, for + requesting and validating user input. + + - If a function is specified, the function will be passed the handler, which is + either an instance of SchemaConfigFlowHandler or SchemaOptionsFlowHandler, and the + union of config entry options and user input from previous steps. + - If schema validation fails, the step will be retried. If the schema is None, no + user input is requested. + """ validate_user_input: Callable[[dict[str, Any]], dict[str, Any]] = lambda x: x """Optional function to validate user input. @@ -56,7 +63,7 @@ class SchemaFlowFormStep: @dataclass -class SchemaFlowMenuStep: +class SchemaFlowMenuStep(SchemaFlowStep): """Define a config or options flow menu step.""" # Menu options @@ -69,7 +76,7 @@ class SchemaCommonFlowHandler: def __init__( self, handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, - flow: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep], + flow: Mapping[str, SchemaFlowStep], options: dict[str, Any] | None, ) -> None: """Initialize a common handler.""" @@ -210,8 +217,8 @@ class SchemaCommonFlowHandler: class SchemaConfigFlowHandler(config_entries.ConfigFlow): """Handle a schema based config flow.""" - config_flow: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] - options_flow: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] | None = None + config_flow: Mapping[str, SchemaFlowStep] + options_flow: Mapping[str, SchemaFlowStep] | None = None VERSION = 1 @@ -311,7 +318,7 @@ class SchemaOptionsFlowHandler(config_entries.OptionsFlowWithConfigEntry): def __init__( self, config_entry: config_entries.ConfigEntry, - options_flow: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep], + options_flow: Mapping[str, SchemaFlowStep], async_options_flow_finished: Callable[[HomeAssistant, Mapping[str, Any]], None] | None = None, ) -> None: From b94e1e9ef839270290f8570afba165fb6e955dd3 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 24 Nov 2022 22:01:36 +0100 Subject: [PATCH 0701/1033] Fire events when long term statistics is updated (#82492) * Fire events when long term statistics is updated * Allow the new events to be subscribed to by anyone * Address review comments * Finish renaming events * Finish renaming events * Fix do_adhoc_statistics * Adjust tests * Adjust tests --- homeassistant/components/recorder/__init__.py | 4 +- homeassistant/components/recorder/const.py | 3 + homeassistant/components/recorder/core.py | 10 +-- .../components/recorder/statistics.py | 15 +++- homeassistant/components/recorder/tasks.py | 5 +- .../components/websocket_api/manifest.json | 3 +- .../components/websocket_api/permissions.py | 6 ++ tests/components/recorder/common.py | 2 +- tests/components/recorder/test_init.py | 76 +++++++++++++++++-- tests/test_requirements.py | 7 +- 10 files changed, 108 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 0d4bfe8e59b..3e1e8264642 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -21,10 +21,12 @@ from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass from . import statistics, websocket_api -from .const import ( +from .const import ( # noqa: F401 CONF_DB_INTEGRITY_CHECK, DATA_INSTANCE, DOMAIN, + EVENT_RECORDER_5MIN_STATISTICS_GENERATED, + EVENT_RECORDER_HOURLY_STATISTICS_GENERATED, EXCLUDE_ATTRIBUTES, SQLITE_URL_PREFIX, ) diff --git a/homeassistant/components/recorder/const.py b/homeassistant/components/recorder/const.py index 66a9818b4b8..8db4b43e04e 100644 --- a/homeassistant/components/recorder/const.py +++ b/homeassistant/components/recorder/const.py @@ -14,6 +14,9 @@ MYSQLDB_URL_PREFIX = "mysql://" MYSQLDB_PYMYSQL_URL_PREFIX = "mysql+pymysql://" DOMAIN = "recorder" +EVENT_RECORDER_5MIN_STATISTICS_GENERATED = "recorder_5min_statistics_generated" +EVENT_RECORDER_HOURLY_STATISTICS_GENERATED = "recorder_hourly_statistics_generated" + CONF_DB_INTEGRITY_CHECK = "db_integrity_check" MAX_QUEUE_BACKLOG = 40000 diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 0ef8c20d5c7..61b75783783 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -375,12 +375,6 @@ class Recorder(threading.Thread): # Unknown what it is. return True - def do_adhoc_statistics(self, **kwargs: Any) -> None: - """Trigger an adhoc statistics run.""" - if not (start := kwargs.get("start")): - start = statistics.get_start_time() - self.queue_task(StatisticsTask(start)) - def _empty_queue(self, event: Event) -> None: """Empty the queue if its still present at final write.""" @@ -479,7 +473,7 @@ class Recorder(threading.Thread): Short term statistics run every 5 minutes """ start = statistics.get_start_time() - self.queue_task(StatisticsTask(start)) + self.queue_task(StatisticsTask(start, True)) @callback def async_adjust_statistics( @@ -1193,7 +1187,7 @@ class Recorder(threading.Thread): while start < last_period: end = start + timedelta(minutes=5) _LOGGER.debug("Compiling missing statistics for %s-%s", start, end) - self.queue_task(StatisticsTask(start)) + self.queue_task(StatisticsTask(start, end >= last_period)) start = end def _end_session(self) -> None: diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index e0ef46856c9..9e1feebc682 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -45,7 +45,13 @@ from homeassistant.util.unit_conversion import ( VolumeConverter, ) -from .const import DOMAIN, MAX_ROWS_TO_PURGE, SupportedDialect +from .const import ( + DOMAIN, + EVENT_RECORDER_5MIN_STATISTICS_GENERATED, + EVENT_RECORDER_HOURLY_STATISTICS_GENERATED, + MAX_ROWS_TO_PURGE, + SupportedDialect, +) from .db_schema import ( Statistics, StatisticsBase, @@ -640,7 +646,7 @@ def _compile_hourly_statistics(session: Session, start: datetime) -> None: @retryable_database_job("statistics") -def compile_statistics(instance: Recorder, start: datetime) -> bool: +def compile_statistics(instance: Recorder, start: datetime, fire_events: bool) -> bool: """Compile 5-minute statistics for all integrations with a recorder platform. The actual calculation is delegated to the platforms. @@ -696,6 +702,11 @@ def compile_statistics(instance: Recorder, start: datetime) -> bool: session.add(StatisticsRuns(start=start)) + if fire_events: + instance.hass.bus.fire(EVENT_RECORDER_5MIN_STATISTICS_GENERATED) + if start.minute == 55: + instance.hass.bus.fire(EVENT_RECORDER_HOURLY_STATISTICS_GENERATED) + return True diff --git a/homeassistant/components/recorder/tasks.py b/homeassistant/components/recorder/tasks.py index 1b8e03ebf17..01723a50960 100644 --- a/homeassistant/components/recorder/tasks.py +++ b/homeassistant/components/recorder/tasks.py @@ -133,13 +133,14 @@ class StatisticsTask(RecorderTask): """An object to insert into the recorder queue to run a statistics task.""" start: datetime + fire_events: bool def run(self, instance: Recorder) -> None: """Run statistics task.""" - if statistics.compile_statistics(instance, self.start): + if statistics.compile_statistics(instance, self.start, self.fire_events): return # Schedule a new statistics task if this one didn't finish - instance.queue_task(StatisticsTask(self.start)) + instance.queue_task(StatisticsTask(self.start, self.fire_events)) @dataclass diff --git a/homeassistant/components/websocket_api/manifest.json b/homeassistant/components/websocket_api/manifest.json index f40d2940561..73b05594bd5 100644 --- a/homeassistant/components/websocket_api/manifest.json +++ b/homeassistant/components/websocket_api/manifest.json @@ -5,5 +5,6 @@ "dependencies": ["http"], "codeowners": ["@home-assistant/core"], "quality_scale": "internal", - "integration_type": "system" + "integration_type": "system", + "after_dependencies": ["recorder"] } diff --git a/homeassistant/components/websocket_api/permissions.py b/homeassistant/components/websocket_api/permissions.py index 6100c2ea13c..f3a0cebe51f 100644 --- a/homeassistant/components/websocket_api/permissions.py +++ b/homeassistant/components/websocket_api/permissions.py @@ -11,6 +11,10 @@ from homeassistant.components.lovelace import EVENT_LOVELACE_UPDATED from homeassistant.components.persistent_notification import ( EVENT_PERSISTENT_NOTIFICATIONS_UPDATED, ) +from homeassistant.components.recorder import ( + EVENT_RECORDER_5MIN_STATISTICS_GENERATED, + EVENT_RECORDER_HOURLY_STATISTICS_GENERATED, +) from homeassistant.components.shopping_list import EVENT_SHOPPING_LIST_UPDATED from homeassistant.const import ( EVENT_COMPONENT_LOADED, @@ -35,6 +39,8 @@ SUBSCRIBE_ALLOWLIST: Final[set[str]] = { EVENT_LOVELACE_UPDATED, EVENT_PANELS_UPDATED, EVENT_PERSISTENT_NOTIFICATIONS_UPDATED, + EVENT_RECORDER_5MIN_STATISTICS_GENERATED, + EVENT_RECORDER_HOURLY_STATISTICS_GENERATED, EVENT_SERVICE_REGISTERED, EVENT_SERVICE_REMOVED, EVENT_SHOPPING_LIST_UPDATED, diff --git a/tests/components/recorder/common.py b/tests/components/recorder/common.py index 701d05175a7..ce9ed30797b 100644 --- a/tests/components/recorder/common.py +++ b/tests/components/recorder/common.py @@ -53,7 +53,7 @@ def do_adhoc_statistics(hass: HomeAssistant, **kwargs: Any) -> None: """Trigger an adhoc statistics run.""" if not (start := kwargs.get("start")): start = statistics.get_start_time() - get_instance(hass).queue_task(StatisticsTask(start)) + get_instance(hass).queue_task(StatisticsTask(start, False)) def wait_recording_done(hass: HomeAssistant) -> None: diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index ca4cbc9a4f9..e13f1b873bd 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -26,8 +26,13 @@ from homeassistant.components.recorder import ( Recorder, get_instance, pool, + statistics, +) +from homeassistant.components.recorder.const import ( + EVENT_RECORDER_5MIN_STATISTICS_GENERATED, + EVENT_RECORDER_HOURLY_STATISTICS_GENERATED, + KEEPALIVE_TIME, ) -from homeassistant.components.recorder.const import KEEPALIVE_TIME from homeassistant.components.recorder.db_schema import ( SCHEMA_VERSION, EventData, @@ -933,7 +938,7 @@ def test_auto_purge_disabled(hass_recorder): @pytest.mark.parametrize("enable_statistics", [True]) -def test_auto_statistics(hass_recorder): +def test_auto_statistics(hass_recorder, freezer): """Test periodic statistics scheduling.""" hass = hass_recorder() @@ -942,43 +947,82 @@ def test_auto_statistics(hass_recorder): tz = dt_util.get_time_zone("Europe/Copenhagen") dt_util.set_default_time_zone(tz) + stats_5min = [] + stats_hourly = [] + + @callback + def async_5min_stats_updated_listener(event: Event) -> None: + """Handle recorder 5 min stat updated.""" + stats_5min.append(event) + + def async_hourly_stats_updated_listener(event: Event) -> None: + """Handle recorder 5 min stat updated.""" + stats_hourly.append(event) + # Statistics is scheduled to happen every 5 minutes. Exercise this behavior by # firing time changed events and advancing the clock around this time. Pick an # arbitrary year in the future to avoid boundary conditions relative to the current # date. # - # The clock is started at 4:16am then advanced forward below + # The clock is started at 4:51am then advanced forward below now = dt_util.utcnow() - test_time = datetime(now.year + 2, 1, 1, 4, 16, 0, tzinfo=tz) + test_time = datetime(now.year + 2, 1, 1, 4, 51, 0, tzinfo=tz) + freezer.move_to(test_time.isoformat()) run_tasks_at_time(hass, test_time) + hass.block_till_done() + hass.bus.listen( + EVENT_RECORDER_5MIN_STATISTICS_GENERATED, async_5min_stats_updated_listener + ) + hass.bus.listen( + EVENT_RECORDER_HOURLY_STATISTICS_GENERATED, async_hourly_stats_updated_listener + ) + + real_compile_statistics = statistics.compile_statistics with patch( "homeassistant.components.recorder.statistics.compile_statistics", - return_value=True, + side_effect=real_compile_statistics, + autospec=True, ) as compile_statistics: # Advance 5 minutes, and the statistics task should run test_time = test_time + timedelta(minutes=5) + freezer.move_to(test_time.isoformat()) run_tasks_at_time(hass, test_time) assert len(compile_statistics.mock_calls) == 1 + hass.block_till_done() + assert len(stats_5min) == 1 + assert len(stats_hourly) == 0 compile_statistics.reset_mock() # Advance 5 minutes, and the statistics task should run again test_time = test_time + timedelta(minutes=5) + freezer.move_to(test_time.isoformat()) run_tasks_at_time(hass, test_time) assert len(compile_statistics.mock_calls) == 1 + hass.block_till_done() + assert len(stats_5min) == 2 + assert len(stats_hourly) == 1 compile_statistics.reset_mock() # Advance less than 5 minutes. The task should not run. test_time = test_time + timedelta(minutes=3) + freezer.move_to(test_time.isoformat()) run_tasks_at_time(hass, test_time) assert len(compile_statistics.mock_calls) == 0 + hass.block_till_done() + assert len(stats_5min) == 2 + assert len(stats_hourly) == 1 # Advance 5 minutes, and the statistics task should run again test_time = test_time + timedelta(minutes=5) + freezer.move_to(test_time.isoformat()) run_tasks_at_time(hass, test_time) assert len(compile_statistics.mock_calls) == 1 + hass.block_till_done() + assert len(stats_5min) == 3 + assert len(stats_hourly) == 1 dt_util.set_default_time_zone(original_tz) @@ -1027,8 +1071,27 @@ def test_compile_missing_statistics(tmpdir, freezer): hass.stop() # Start Home Assistant one hour later + stats_5min = [] + stats_hourly = [] + + @callback + def async_5min_stats_updated_listener(event: Event) -> None: + """Handle recorder 5 min stat updated.""" + stats_5min.append(event) + + def async_hourly_stats_updated_listener(event: Event) -> None: + """Handle recorder 5 min stat updated.""" + stats_hourly.append(event) + freezer.tick(timedelta(hours=1)) hass = get_test_home_assistant() + hass.bus.listen( + EVENT_RECORDER_5MIN_STATISTICS_GENERATED, async_5min_stats_updated_listener + ) + hass.bus.listen( + EVENT_RECORDER_HOURLY_STATISTICS_GENERATED, async_hourly_stats_updated_listener + ) + recorder_helper.async_initialize_recorder(hass) setup_component(hass, DOMAIN, {DOMAIN: {CONF_DB_URL: dburl}}) hass.start() @@ -1041,6 +1104,9 @@ def test_compile_missing_statistics(tmpdir, freezer): last_run = process_timestamp(statistics_runs[1].start) assert last_run == now + assert len(stats_5min) == 1 + assert len(stats_hourly) == 1 + wait_recording_done(hass) wait_recording_done(hass) hass.stop() diff --git a/tests/test_requirements.py b/tests/test_requirements.py index 77499707489..826150e47c4 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -418,14 +418,15 @@ async def test_discovery_requirements_ssdp(hass): ) as mock_process: await async_get_integration_with_requirements(hass, "ssdp_comp") - assert len(mock_process.mock_calls) == 4 + assert len(mock_process.mock_calls) == 5 assert mock_process.mock_calls[0][1][1] == ssdp.requirements # Ensure zeroconf is a dep for ssdp assert { mock_process.mock_calls[1][1][0], mock_process.mock_calls[2][1][0], mock_process.mock_calls[3][1][0], - } == {"network", "zeroconf", "http"} + mock_process.mock_calls[4][1][0], + } == {"http", "network", "recorder", "zeroconf"} @pytest.mark.parametrize( @@ -447,7 +448,7 @@ async def test_discovery_requirements_zeroconf(hass, partial_manifest): ) as mock_process: await async_get_integration_with_requirements(hass, "comp") - assert len(mock_process.mock_calls) == 3 # zeroconf also depends on http + assert len(mock_process.mock_calls) == 4 # zeroconf also depends on http assert mock_process.mock_calls[0][1][1] == zeroconf.requirements From 9995cef0f945641d6794c5f638365524c771b9e5 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 24 Nov 2022 22:19:47 +0100 Subject: [PATCH 0702/1033] Pass sensor DOMAIN constant to EntitySelectorConfig (#82670) --- homeassistant/components/derivative/config_flow.py | 3 ++- homeassistant/components/integration/config_flow.py | 3 ++- homeassistant/components/min_max/config_flow.py | 3 ++- homeassistant/components/threshold/config_flow.py | 3 ++- homeassistant/components/utility_meter/config_flow.py | 5 +++-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/derivative/config_flow.py b/homeassistant/components/derivative/config_flow.py index 1b98f4af352..b250a910032 100644 --- a/homeassistant/components/derivative/config_flow.py +++ b/homeassistant/components/derivative/config_flow.py @@ -6,6 +6,7 @@ from typing import Any, cast import voluptuous as vol +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import ( CONF_NAME, CONF_SOURCE, @@ -70,7 +71,7 @@ CONFIG_SCHEMA = vol.Schema( { vol.Required(CONF_NAME): selector.TextSelector(), vol.Required(CONF_SOURCE): selector.EntitySelector( - selector.EntitySelectorConfig(domain="sensor"), + selector.EntitySelectorConfig(domain=SENSOR_DOMAIN), ), } ).extend(OPTIONS_SCHEMA.schema) diff --git a/homeassistant/components/integration/config_flow.py b/homeassistant/components/integration/config_flow.py index dba3103906f..446132ece7c 100644 --- a/homeassistant/components/integration/config_flow.py +++ b/homeassistant/components/integration/config_flow.py @@ -6,6 +6,7 @@ from typing import Any, cast import voluptuous as vol +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import ( CONF_METHOD, CONF_NAME, @@ -64,7 +65,7 @@ CONFIG_SCHEMA = vol.Schema( { vol.Required(CONF_NAME): selector.TextSelector(), vol.Required(CONF_SOURCE_SENSOR): selector.EntitySelector( - selector.EntitySelectorConfig(domain="sensor") + selector.EntitySelectorConfig(domain=SENSOR_DOMAIN) ), vol.Required(CONF_METHOD, default=METHOD_TRAPEZOIDAL): selector.SelectSelector( selector.SelectSelectorConfig(options=INTEGRATION_METHODS), diff --git a/homeassistant/components/min_max/config_flow.py b/homeassistant/components/min_max/config_flow.py index ecf47dc0c63..c3e1c2e884e 100644 --- a/homeassistant/components/min_max/config_flow.py +++ b/homeassistant/components/min_max/config_flow.py @@ -6,6 +6,7 @@ from typing import Any, cast import voluptuous as vol +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import CONF_TYPE from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( @@ -29,7 +30,7 @@ _STATISTIC_MEASURES = [ OPTIONS_SCHEMA = vol.Schema( { vol.Required(CONF_ENTITY_IDS): selector.EntitySelector( - selector.EntitySelectorConfig(domain="sensor", multiple=True), + selector.EntitySelectorConfig(domain=SENSOR_DOMAIN, multiple=True), ), vol.Required(CONF_TYPE): selector.SelectSelector( selector.SelectSelectorConfig(options=_STATISTIC_MEASURES), diff --git a/homeassistant/components/threshold/config_flow.py b/homeassistant/components/threshold/config_flow.py index 20cedcfca6b..52b2fb44be9 100644 --- a/homeassistant/components/threshold/config_flow.py +++ b/homeassistant/components/threshold/config_flow.py @@ -6,6 +6,7 @@ from typing import Any import voluptuous as vol +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import CONF_ENTITY_ID, CONF_NAME from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( @@ -50,7 +51,7 @@ CONFIG_SCHEMA = vol.Schema( { vol.Required(CONF_NAME): selector.TextSelector(), vol.Required(CONF_ENTITY_ID): selector.EntitySelector( - selector.EntitySelectorConfig(domain="sensor") + selector.EntitySelectorConfig(domain=SENSOR_DOMAIN) ), } ).extend(OPTIONS_SCHEMA.schema) diff --git a/homeassistant/components/utility_meter/config_flow.py b/homeassistant/components/utility_meter/config_flow.py index fbdf302e9b7..0f43ccf29cc 100644 --- a/homeassistant/components/utility_meter/config_flow.py +++ b/homeassistant/components/utility_meter/config_flow.py @@ -6,6 +6,7 @@ from typing import Any, cast import voluptuous as vol +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import CONF_NAME from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( @@ -58,7 +59,7 @@ def _validate_config(data: Any) -> Any: OPTIONS_SCHEMA = vol.Schema( { vol.Required(CONF_SOURCE_SENSOR): selector.EntitySelector( - selector.EntitySelectorConfig(domain="sensor"), + selector.EntitySelectorConfig(domain=SENSOR_DOMAIN), ), } ) @@ -67,7 +68,7 @@ CONFIG_SCHEMA = vol.Schema( { vol.Required(CONF_NAME): selector.TextSelector(), vol.Required(CONF_SOURCE_SENSOR): selector.EntitySelector( - selector.EntitySelectorConfig(domain="sensor"), + selector.EntitySelectorConfig(domain=SENSOR_DOMAIN), ), vol.Required(CONF_METER_TYPE): selector.SelectSelector( selector.SelectSelectorConfig(options=METER_TYPES), From 6cb004b7702a0110b5952f41349f8628280b1f56 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Thu, 24 Nov 2022 22:34:39 +0100 Subject: [PATCH 0703/1033] Selector for SingleEntitySelectorConfig allow domain to be a list (#82666) --- homeassistant/helpers/selector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/helpers/selector.py b/homeassistant/helpers/selector.py index b759ab65826..c7a4be175fb 100644 --- a/homeassistant/helpers/selector.py +++ b/homeassistant/helpers/selector.py @@ -94,7 +94,7 @@ class SingleEntitySelectorConfig(TypedDict, total=False): """Class to represent a single entity selector config.""" integration: str - domain: str + domain: str | list[str] device_class: str From fcfc0e64d55810c12be54c081243f5c199480359 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Fri, 25 Nov 2022 05:41:26 +0800 Subject: [PATCH 0704/1033] Fix mime type in forked_daapd test (#82660) --- tests/components/forked_daapd/test_browse_media.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/components/forked_daapd/test_browse_media.py b/tests/components/forked_daapd/test_browse_media.py index 957c52a88c5..1cb0260f058 100644 --- a/tests/components/forked_daapd/test_browse_media.py +++ b/tests/components/forked_daapd/test_browse_media.py @@ -379,7 +379,7 @@ async def test_async_browse_image(hass, hass_client, config_entry): (MediaType.ARTIST, "3815427709949443149"), (MediaType.TRACK, "456"), ): - mock_fetch_image.return_value = (b"image_bytes", media_type) + mock_fetch_image.return_value = (b"image_bytes", "image/jpeg") media_content_id = create_media_content_id( "title", media_type=media_type, id_or_path=media_id ) @@ -391,7 +391,7 @@ async def test_async_browse_image(hass, hass_client, config_entry): == f"http://owntone_instance/some_{media_type}_image" ) assert resp.status == HTTPStatus.OK - assert resp.content_type == media_type + assert resp.content_type == "image/jpeg" assert await resp.read() == b"image_bytes" From 47cec8da8e7646a9ca38088976115a2b7afd8818 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 24 Nov 2022 22:41:56 +0100 Subject: [PATCH 0705/1033] Add integration filter to utility_meter.calibrate service (#82671) --- homeassistant/components/utility_meter/services.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/utility_meter/services.yaml b/homeassistant/components/utility_meter/services.yaml index 194eff5d7d0..4252f796199 100644 --- a/homeassistant/components/utility_meter/services.yaml +++ b/homeassistant/components/utility_meter/services.yaml @@ -13,6 +13,7 @@ calibrate: target: entity: domain: sensor + integration: utility_meter fields: value: name: Value From 09c3df7eb258295211a8216c2039843b09aa244b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 24 Nov 2022 15:20:19 -0700 Subject: [PATCH 0706/1033] Fix iBeacons with infrequent random mac address changes unexpectedly going unavailable (#82668) fixes https://github.com/home-assistant/core/issues/79781 --- .../components/ibeacon/coordinator.py | 20 ++- .../components/ibeacon/test_device_tracker.py | 117 +++++++++++++++++- 2 files changed, 135 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/ibeacon/coordinator.py b/homeassistant/components/ibeacon/coordinator.py index bab0a5b5655..d38f935cb77 100644 --- a/homeassistant/components/ibeacon/coordinator.py +++ b/homeassistant/components/ibeacon/coordinator.py @@ -354,7 +354,25 @@ class IBeaconCoordinator: for group_id in self._group_ids_random_macs if group_id not in self._unavailable_group_ids and (service_info := self._last_seen_by_group_id.get(group_id)) - and now - service_info.time > UNAVAILABLE_TIMEOUT + and ( + # We will not be callbacks for iBeacons with random macs + # that rotate infrequently since their advertisement data is + # does not change as the bluetooth.async_register_callback API + # suppresses callbacks for duplicate advertisements to avoid + # exposing integrations to the firehose of bluetooth advertisements. + # + # To solve this we need to ask for the latest service info for + # the address we last saw to get the latest timestamp. + # + # If there is no last service info for the address we know that + # the device is no longer advertising. + not ( + latest_service_info := bluetooth.async_last_service_info( + self.hass, service_info.address, connectable=False + ) + ) + or now - latest_service_info.time > UNAVAILABLE_TIMEOUT + ) ] for group_id in gone_unavailable: self._unavailable_group_ids.add(group_id) diff --git a/tests/components/ibeacon/test_device_tracker.py b/tests/components/ibeacon/test_device_tracker.py index b16144d7d90..2f86ccb9042 100644 --- a/tests/components/ibeacon/test_device_tracker.py +++ b/tests/components/ibeacon/test_device_tracker.py @@ -7,8 +7,17 @@ from unittest.mock import patch import pytest +from homeassistant.components.bluetooth import ( + BluetoothServiceInfoBleak, + async_ble_device_from_address, + async_last_service_info, +) from homeassistant.components.bluetooth.const import UNAVAILABLE_TRACK_SECONDS -from homeassistant.components.ibeacon.const import DOMAIN, UNAVAILABLE_TIMEOUT +from homeassistant.components.ibeacon.const import ( + DOMAIN, + UNAVAILABLE_TIMEOUT, + UPDATE_INTERVAL, +) from homeassistant.const import ( ATTR_FRIENDLY_NAME, STATE_HOME, @@ -27,6 +36,7 @@ from . import ( from tests.common import MockConfigEntry, async_fire_time_changed from tests.components.bluetooth import ( inject_bluetooth_service_info, + inject_bluetooth_service_info_bleak, patch_all_discovered_devices, ) @@ -130,3 +140,108 @@ async def test_device_tracker_random_address(hass): tracker_attributes = tracker.attributes assert tracker.state == STATE_HOME assert tracker_attributes[ATTR_FRIENDLY_NAME] == "RandomAddress_1234" + + +async def test_device_tracker_random_address_infrequent_changes(hass): + """Test creating and updating device_tracker with a random mac that only changes once per day.""" + entry = MockConfigEntry( + domain=DOMAIN, + ) + entry.add_to_hass(hass) + start_time = time.monotonic() + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + for i in range(20): + inject_bluetooth_service_info( + hass, + replace( + BEACON_RANDOM_ADDRESS_SERVICE_INFO, address=f"AA:BB:CC:DD:EE:{i:02X}" + ), + ) + await hass.async_block_till_done() + + tracker = hass.states.get("device_tracker.randomaddress_1234") + tracker_attributes = tracker.attributes + assert tracker.state == STATE_HOME + assert tracker_attributes[ATTR_FRIENDLY_NAME] == "RandomAddress_1234" + + await hass.async_block_till_done() + with patch_all_discovered_devices([]), patch( + "homeassistant.components.ibeacon.coordinator.MONOTONIC_TIME", + return_value=start_time + UNAVAILABLE_TIMEOUT + 1, + ): + async_fire_time_changed( + hass, dt_util.utcnow() + timedelta(seconds=UNAVAILABLE_TIMEOUT) + ) + await hass.async_block_till_done() + + tracker = hass.states.get("device_tracker.randomaddress_1234") + assert tracker.state == STATE_NOT_HOME + + inject_bluetooth_service_info( + hass, replace(BEACON_RANDOM_ADDRESS_SERVICE_INFO, address="AA:BB:CC:DD:EE:14") + ) + await hass.async_block_till_done() + + tracker = hass.states.get("device_tracker.randomaddress_1234") + tracker_attributes = tracker.attributes + assert tracker.state == STATE_HOME + assert tracker_attributes[ATTR_FRIENDLY_NAME] == "RandomAddress_1234" + + inject_bluetooth_service_info( + hass, replace(BEACON_RANDOM_ADDRESS_SERVICE_INFO, address="AA:BB:CC:DD:EE:14") + ) + device = async_ble_device_from_address(hass, "AA:BB:CC:DD:EE:14", False) + + with patch_all_discovered_devices([device]), patch( + "homeassistant.components.ibeacon.coordinator.MONOTONIC_TIME", + return_value=start_time + UPDATE_INTERVAL.total_seconds() + 1, + ): + async_fire_time_changed(hass, dt_util.utcnow() + UPDATE_INTERVAL) + await hass.async_block_till_done() + + tracker = hass.states.get("device_tracker.randomaddress_1234") + tracker_attributes = tracker.attributes + assert tracker.state == STATE_HOME + assert tracker_attributes[ATTR_FRIENDLY_NAME] == "RandomAddress_1234" + + one_day_future = start_time + 86400 + previous_service_info = async_last_service_info( + hass, "AA:BB:CC:DD:EE:14", connectable=False + ) + inject_bluetooth_service_info_bleak( + hass, + BluetoothServiceInfoBleak( + name="RandomAddress_1234", + address="AA:BB:CC:DD:EE:14", + rssi=-63, + service_data={}, + manufacturer_data={76: b"\x02\x15RandCharmBeacons\x0e\xfe\x13U\xc5"}, + service_uuids=[], + source="local", + time=one_day_future, + connectable=False, + device=device, + advertisement=previous_service_info.advertisement, + ), + ) + device = async_ble_device_from_address(hass, "AA:BB:CC:DD:EE:14", False) + assert ( + async_last_service_info(hass, "AA:BB:CC:DD:EE:14", connectable=False).time + == one_day_future + ) + + with patch_all_discovered_devices([device]), patch( + "homeassistant.components.ibeacon.coordinator.MONOTONIC_TIME", + return_value=start_time + UNAVAILABLE_TIMEOUT + 1, + ): + async_fire_time_changed( + hass, dt_util.utcnow() + timedelta(seconds=UNAVAILABLE_TIMEOUT + 1) + ) + await hass.async_block_till_done() + + tracker = hass.states.get("device_tracker.randomaddress_1234") + tracker_attributes = tracker.attributes + assert tracker.state == STATE_HOME + assert tracker_attributes[ATTR_FRIENDLY_NAME] == "RandomAddress_1234" From e1338adf1a27edbc0e3513fa67cd6690c7a8fbc0 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 24 Nov 2022 23:25:50 +0100 Subject: [PATCH 0707/1033] Allow configuring country and language in core config (#81734) * Allow configuring country and language in core config * Add script for updating list of countries * Use black for formatting * Fix quoting * Move country codes to a separate file * Address review comments * Add generated/countries.py * Get default language from owner account * Remove unused variable * Add script to generate list of supported languages * Add tests * Fix stale docsring * Use format_python_namespace * Correct async_user_store * Improve typing * Fix with_store decorator * Initialize language in core store migration * Fix startup * Tweak * Apply suggestions from code review Co-authored-by: Franck Nijhof * Update storage.py Co-authored-by: Franck Nijhof --- .pre-commit-config.yaml | 2 +- homeassistant/bootstrap.py | 47 ++-- homeassistant/components/config/core.py | 2 + homeassistant/components/frontend/storage.py | 44 +++- homeassistant/config.py | 8 + homeassistant/const.py | 2 + homeassistant/core.py | 46 +++- homeassistant/generated/countries.py | 260 +++++++++++++++++++ homeassistant/generated/languages.py | 68 +++++ homeassistant/helpers/config_validation.py | 6 + script/countries.py | 27 ++ script/languages.py | 25 ++ tests/common.py | 3 +- tests/components/config/test_core.py | 4 + tests/helpers/test_config_validation.py | 24 ++ tests/test_config.py | 88 ++++++- tests/test_core.py | 4 + 17 files changed, 623 insertions(+), 37 deletions(-) create mode 100644 homeassistant/generated/countries.py create mode 100644 homeassistant/generated/languages.py create mode 100644 script/countries.py create mode 100644 script/languages.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 823c13a4d55..a2c84d9eebe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: codespell args: - - --ignore-words-list=alot,ba,bre,datas,dof,dur,ether,farenheit,haa,hass,hist,iam,iff,iif,incomfort,ines,ist,lightsensor,mut,nd,pres,pullrequests,referer,rime,ser,serie,sur,te,technik,ue,uint,visability,wan,wanna,withing + - --ignore-words-list=alot,ba,bre,datas,dof,dur,ether,farenheit,fo,haa,hass,hist,iam,iff,iif,incomfort,ines,ist,lightsensor,mut,nd,pres,pullrequests,referer,rime,ser,serie,sur,te,technik,ue,uint,visability,wan,wanna,withing - --skip="./.*,*.csv,*.json" - --quiet-level=2 exclude_types: [csv, json] diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 8771fb41247..7d1ea91b9aa 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -53,6 +53,7 @@ ERROR_LOG_FILENAME = "home-assistant.log" # hass.data key for logging information. DATA_LOGGING = "logging" +DATA_REGISTRIES_LOADED = "bootstrap_registries_loaded" LOG_SLOW_STARTUP_INTERVAL = 60 SLOW_STARTUP_CHECK_INTERVAL = 1 @@ -216,6 +217,32 @@ def open_hass_ui(hass: core.HomeAssistant) -> None: ) +async def load_registries(hass: core.HomeAssistant) -> None: + """Load the registries and cache the result of platform.uname().processor.""" + if DATA_REGISTRIES_LOADED in hass.data: + return + hass.data[DATA_REGISTRIES_LOADED] = None + + def _cache_uname_processor() -> None: + """Cache the result of platform.uname().processor in the executor. + + Multiple modules call this function at startup which + executes a blocking subprocess call. This is a problem for the + asyncio event loop. By primeing the cache of uname we can + avoid the blocking call in the event loop. + """ + platform.uname().processor # pylint: disable=expression-not-assigned + + # Load the registries and cache the result of platform.uname().processor + await asyncio.gather( + area_registry.async_load(hass), + device_registry.async_load(hass), + entity_registry.async_load(hass), + issue_registry.async_load(hass), + hass.async_add_executor_job(_cache_uname_processor), + ) + + async def async_from_config_dict( config: ConfigType, hass: core.HomeAssistant ) -> core.HomeAssistant | None: @@ -228,6 +255,7 @@ async def async_from_config_dict( hass.config_entries = config_entries.ConfigEntries(hass, config) await hass.config_entries.async_initialize() + await load_registries(hass) # Set up core. _LOGGER.debug("Setting up %s", CORE_INTEGRATIONS) @@ -530,25 +558,6 @@ async def _async_set_up_integrations( _LOGGER.info("Domains to be set up: %s", domains_to_setup) - def _cache_uname_processor() -> None: - """Cache the result of platform.uname().processor in the executor. - - Multiple modules call this function at startup which - executes a blocking subprocess call. This is a problem for the - asyncio event loop. By primeing the cache of uname we can - avoid the blocking call in the event loop. - """ - platform.uname().processor # pylint: disable=expression-not-assigned - - # Load the registries and cache the result of platform.uname().processor - await asyncio.gather( - area_registry.async_load(hass), - device_registry.async_load(hass), - entity_registry.async_load(hass), - issue_registry.async_load(hass), - hass.async_add_executor_job(_cache_uname_processor), - ) - # Initialize recorder if "recorder" in domains_to_setup: recorder.async_initialize_recorder(hass) diff --git a/homeassistant/components/config/core.py b/homeassistant/components/config/core.py index c748395e95f..975cfc590e8 100644 --- a/homeassistant/components/config/core.py +++ b/homeassistant/components/config/core.py @@ -49,6 +49,8 @@ class CheckConfigView(HomeAssistantView): vol.Optional("external_url"): vol.Any(cv.url_no_path, None), vol.Optional("internal_url"): vol.Any(cv.url_no_path, None), vol.Optional("currency"): cv.currency, + vol.Optional("country"): cv.country, + vol.Optional("language"): cv.language, } ) @websocket_api.async_response diff --git a/homeassistant/components/frontend/storage.py b/homeassistant/components/frontend/storage.py index 018850f5960..82f169dc6c9 100644 --- a/homeassistant/components/frontend/storage.py +++ b/homeassistant/components/frontend/storage.py @@ -9,20 +9,47 @@ import voluptuous as vol from homeassistant.components import websocket_api from homeassistant.components.websocket_api.connection import ActiveConnection -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.storage import Store DATA_STORAGE = "frontend_storage" STORAGE_VERSION_USER_DATA = 1 +@callback +def _initialize_frontend_storage(hass: HomeAssistant) -> None: + """Set up frontend storage.""" + if DATA_STORAGE in hass.data: + return + hass.data[DATA_STORAGE] = ({}, {}) + + async def async_setup_frontend_storage(hass: HomeAssistant) -> None: """Set up frontend storage.""" - hass.data[DATA_STORAGE] = ({}, {}) + _initialize_frontend_storage(hass) websocket_api.async_register_command(hass, websocket_set_user_data) websocket_api.async_register_command(hass, websocket_get_user_data) +async def async_user_store( + hass: HomeAssistant, user_id: str +) -> tuple[Store, dict[str, Any]]: + """Access a user store.""" + _initialize_frontend_storage(hass) + stores, data = hass.data[DATA_STORAGE] + if (store := stores.get(user_id)) is None: + store = stores[user_id] = Store( + hass, + STORAGE_VERSION_USER_DATA, + f"frontend.user_data_{user_id}", + ) + + if user_id not in data: + data[user_id] = await store.async_load() or {} + + return store, data[user_id] + + def with_store(orig_func: Callable) -> Callable: """Decorate function to provide data.""" @@ -31,20 +58,11 @@ def with_store(orig_func: Callable) -> Callable: hass: HomeAssistant, connection: ActiveConnection, msg: dict ) -> None: """Provide user specific data and store to function.""" - stores, data = hass.data[DATA_STORAGE] user_id = connection.user.id - if (store := stores.get(user_id)) is None: - store = stores[user_id] = Store( - hass, - STORAGE_VERSION_USER_DATA, - f"frontend.user_data_{connection.user.id}", - ) + store, user_data = await async_user_store(hass, user_id) - if user_id not in data: - data[user_id] = await store.async_load() or {} - - await orig_func(hass, connection, msg, store, data[user_id]) + await orig_func(hass, connection, msg, store, user_data) return with_store_func diff --git a/homeassistant/config.py b/homeassistant/config.py index a422cbea1d9..963f3ee9876 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -27,6 +27,7 @@ from .const import ( CONF_ALLOWLIST_EXTERNAL_URLS, CONF_AUTH_MFA_MODULES, CONF_AUTH_PROVIDERS, + CONF_COUNTRY, CONF_CURRENCY, CONF_CUSTOMIZE, CONF_CUSTOMIZE_DOMAIN, @@ -35,6 +36,7 @@ from .const import ( CONF_EXTERNAL_URL, CONF_ID, CONF_INTERNAL_URL, + CONF_LANGUAGE, CONF_LATITUDE, CONF_LEGACY_TEMPLATES, CONF_LONGITUDE, @@ -281,6 +283,8 @@ CORE_CONFIG_SCHEMA = vol.All( vol.Optional(CONF_MEDIA_DIRS): cv.schema_with_slug_keys(vol.IsDir()), vol.Optional(CONF_LEGACY_TEMPLATES): cv.boolean, vol.Optional(CONF_CURRENCY): _validate_currency, + vol.Optional(CONF_COUNTRY): cv.country, + vol.Optional(CONF_LANGUAGE): cv.language, } ), _filter_bad_internal_external_urls, @@ -560,6 +564,8 @@ async def async_process_ha_core_config(hass: HomeAssistant, config: dict) -> Non CONF_EXTERNAL_URL, CONF_INTERNAL_URL, CONF_CURRENCY, + CONF_COUNTRY, + CONF_LANGUAGE, ) ): hac.config_source = ConfigSource.YAML @@ -574,6 +580,8 @@ async def async_process_ha_core_config(hass: HomeAssistant, config: dict) -> Non (CONF_MEDIA_DIRS, "media_dirs"), (CONF_LEGACY_TEMPLATES, "legacy_templates"), (CONF_CURRENCY, "currency"), + (CONF_COUNTRY, "country"), + (CONF_LANGUAGE, "language"), ): if key in config: setattr(hac, attr, config[key]) diff --git a/homeassistant/const.py b/homeassistant/const.py index 22b7ea5ab50..ca69307a485 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -121,6 +121,7 @@ CONF_CONDITIONS: Final = "conditions" CONF_CONTINUE_ON_ERROR: Final = "continue_on_error" CONF_CONTINUE_ON_TIMEOUT: Final = "continue_on_timeout" CONF_COUNT: Final = "count" +CONF_COUNTRY: Final = "country" CONF_COVERS: Final = "covers" CONF_CURRENCY: Final = "currency" CONF_CUSTOMIZE: Final = "customize" @@ -175,6 +176,7 @@ CONF_IF: Final = "if" CONF_INCLUDE: Final = "include" CONF_INTERNAL_URL: Final = "internal_url" CONF_IP_ADDRESS: Final = "ip_address" +CONF_LANGUAGE: Final = "language" CONF_LATITUDE: Final = "latitude" CONF_LEGACY_TEMPLATES: Final = "legacy_templates" CONF_LIGHTS: Final = "lights" diff --git a/homeassistant/core.py b/homeassistant/core.py index 8f9287aedac..9172c1d60b4 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -15,6 +15,7 @@ from collections.abc import ( Iterable, Mapping, ) +from contextlib import suppress from contextvars import ContextVar import datetime import enum @@ -113,7 +114,7 @@ CALLBACK_TYPE = Callable[[], None] # pylint: disable=invalid-name CORE_STORAGE_KEY = "core.config" CORE_STORAGE_VERSION = 1 -CORE_STORAGE_MINOR_VERSION = 2 +CORE_STORAGE_MINOR_VERSION = 3 DOMAIN = "homeassistant" @@ -1807,6 +1808,8 @@ class Config: self.internal_url: str | None = None self.external_url: str | None = None self.currency: str = "EUR" + self.country: str | None = None + self.language: str = "en" self.config_source: ConfigSource = ConfigSource.DEFAULT @@ -1913,6 +1916,8 @@ class Config: "external_url": self.external_url, "internal_url": self.internal_url, "currency": self.currency, + "country": self.country, + "language": self.language, } def set_time_zone(self, time_zone_str: str) -> None: @@ -1938,6 +1943,8 @@ class Config: external_url: str | dict[Any, Any] | None = _UNDEF, internal_url: str | dict[Any, Any] | None = _UNDEF, currency: str | None = None, + country: str | dict[Any, Any] | None = _UNDEF, + language: str | None = None, ) -> None: """Update the configuration from a dictionary.""" self.config_source = source @@ -1962,6 +1969,10 @@ class Config: self.internal_url = cast(Optional[str], internal_url) if currency is not None: self.currency = currency + if country is not _UNDEF: + self.country = cast(Optional[str], country) + if language is not None: + self.language = language async def async_update(self, **kwargs: Any) -> None: """Update the configuration from a dictionary.""" @@ -1999,6 +2010,8 @@ class Config: external_url=data.get("external_url", _UNDEF), internal_url=data.get("internal_url", _UNDEF), currency=data.get("currency"), + country=data.get("country"), + language=data.get("language"), ) async def _async_store(self) -> None: @@ -2015,6 +2028,8 @@ class Config: "external_url": self.external_url, "internal_url": self.internal_url, "currency": self.currency, + "country": self.country, + "language": self.language, } await self._store.async_save(data) @@ -2053,6 +2068,35 @@ class Config: data["unit_system_v2"] = self._original_unit_system if data["unit_system_v2"] == _CONF_UNIT_SYSTEM_IMPERIAL: data["unit_system_v2"] = _CONF_UNIT_SYSTEM_US_CUSTOMARY + if old_major_version == 1 and old_minor_version < 3: + # In 1.3, we add the key "language", initialize it from the owner account + data["language"] = "en" + try: + owner = await self.hass.auth.async_get_owner() + if owner is not None: + # pylint: disable-next=import-outside-toplevel + from .components.frontend import storage as frontend_store + + # pylint: disable-next=import-outside-toplevel + from .helpers import config_validation as cv + + _, owner_data = await frontend_store.async_user_store( + self.hass, owner.id + ) + + if ( + "language" in owner_data + and "language" in owner_data["language"] + ): + with suppress(vol.InInvalid): + # pylint: disable-next=protected-access + data["language"] = cv.language( + owner_data["language"]["language"] + ) + # pylint: disable-next=broad-except + except Exception: + _LOGGER.exception("Unexpected error during core config migration") + if old_major_version > 1: raise NotImplementedError return data diff --git a/homeassistant/generated/countries.py b/homeassistant/generated/countries.py new file mode 100644 index 00000000000..76482a524de --- /dev/null +++ b/homeassistant/generated/countries.py @@ -0,0 +1,260 @@ +"""This file is automatically generated. + +To update, run python3 -m script.countries + +The values are directly corresponding to the ISO 3166 standard. If you need changes +to the political situation in the world, please contact the ISO 3166 working group. + +""" + +COUNTRIES = { + "AD", + "AE", + "AF", + "AG", + "AI", + "AL", + "AM", + "AO", + "AQ", + "AR", + "AS", + "AT", + "AU", + "AW", + "AX", + "AZ", + "BA", + "BB", + "BD", + "BE", + "BF", + "BG", + "BH", + "BI", + "BJ", + "BL", + "BM", + "BN", + "BO", + "BQ", + "BR", + "BS", + "BT", + "BV", + "BW", + "BY", + "BZ", + "CA", + "CC", + "CD", + "CF", + "CG", + "CH", + "CI", + "CK", + "CL", + "CM", + "CN", + "CO", + "CR", + "CU", + "CV", + "CW", + "CX", + "CY", + "CZ", + "DE", + "DJ", + "DK", + "DM", + "DO", + "DZ", + "EC", + "EE", + "EG", + "EH", + "ER", + "ES", + "ET", + "FI", + "FJ", + "FK", + "FM", + "FO", + "FR", + "GA", + "GB", + "GD", + "GE", + "GF", + "GG", + "GH", + "GI", + "GL", + "GM", + "GN", + "GP", + "GQ", + "GR", + "GS", + "GT", + "GU", + "GW", + "GY", + "HK", + "HM", + "HN", + "HR", + "HT", + "HU", + "ID", + "IE", + "IL", + "IM", + "IN", + "IO", + "IQ", + "IR", + "IS", + "IT", + "JE", + "JM", + "JO", + "JP", + "KE", + "KG", + "KH", + "KI", + "KM", + "KN", + "KP", + "KR", + "KW", + "KY", + "KZ", + "LA", + "LB", + "LC", + "LI", + "LK", + "LR", + "LS", + "LT", + "LU", + "LV", + "LY", + "MA", + "MC", + "MD", + "ME", + "MF", + "MG", + "MH", + "MK", + "ML", + "MM", + "MN", + "MO", + "MP", + "MQ", + "MR", + "MS", + "MT", + "MU", + "MV", + "MW", + "MX", + "MY", + "MZ", + "NA", + "NC", + "NE", + "NF", + "NG", + "NI", + "NL", + "NO", + "NP", + "NR", + "NU", + "NZ", + "OM", + "PA", + "PE", + "PF", + "PG", + "PH", + "PK", + "PL", + "PM", + "PN", + "PR", + "PS", + "PT", + "PW", + "PY", + "QA", + "RE", + "RO", + "RS", + "RU", + "RW", + "SA", + "SB", + "SC", + "SD", + "SE", + "SG", + "SH", + "SI", + "SJ", + "SK", + "SL", + "SM", + "SN", + "SO", + "SR", + "SS", + "ST", + "SV", + "SX", + "SY", + "SZ", + "TC", + "TD", + "TF", + "TG", + "TH", + "TJ", + "TK", + "TL", + "TM", + "TN", + "TO", + "TR", + "TT", + "TV", + "TW", + "TZ", + "UA", + "UG", + "UM", + "US", + "UY", + "UZ", + "VA", + "VC", + "VE", + "VG", + "VI", + "VN", + "VU", + "WF", + "WS", + "YE", + "YT", + "ZA", + "ZM", + "ZW", +} diff --git a/homeassistant/generated/languages.py b/homeassistant/generated/languages.py new file mode 100644 index 00000000000..879d4a4cd41 --- /dev/null +++ b/homeassistant/generated/languages.py @@ -0,0 +1,68 @@ +"""This file is automatically generated. + +To update, run python3 -m script.languages [frontend_tag] +""" + +LANGUAGES = { + "af", + "ar", + "bg", + "bn", + "bs", + "ca", + "cs", + "cy", + "da", + "de", + "el", + "en", + "en-GB", + "eo", + "es", + "es-419", + "et", + "eu", + "fa", + "fi", + "fr", + "fy", + "gl", + "gsw", + "he", + "hi", + "hr", + "hu", + "hy", + "id", + "is", + "it", + "ja", + "ka", + "ko", + "lb", + "lt", + "lv", + "ml", + "nb", + "nl", + "nn", + "pl", + "pt", + "pt-BR", + "ro", + "ru", + "sk", + "sl", + "sr", + "sr-Latn", + "sv", + "ta", + "te", + "th", + "tr", + "uk", + "ur", + "vi", + "zh-Hans", + "zh-Hant", +} diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 667aec9fccf..fc71a586aee 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -89,6 +89,8 @@ from homeassistant.const import ( from homeassistant.core import split_entity_id, valid_entity_id from homeassistant.exceptions import TemplateError from homeassistant.generated import currencies +from homeassistant.generated.countries import COUNTRIES +from homeassistant.generated.languages import LANGUAGES from homeassistant.util import raise_if_invalid_path, slugify as util_slugify import homeassistant.util.dt as dt_util @@ -1662,3 +1664,7 @@ currency = vol.In( historic_currency = vol.In( currencies.HISTORIC_CURRENCIES, msg="invalid ISO 4217 formatted historic currency" ) + +country = vol.In(COUNTRIES, msg="invalid ISO 3166 formatted country") + +language = vol.In(LANGUAGES, msg="invalid RFC 5646 formatted language") diff --git a/script/countries.py b/script/countries.py new file mode 100644 index 00000000000..0d776f0805d --- /dev/null +++ b/script/countries.py @@ -0,0 +1,27 @@ +"""Helper script to update country list. + +ISO does not publish a machine readable list free of charge, so the list is generated +with help of the pycountry package. +""" +from pathlib import Path + +import pycountry + +from .hassfest.serializer import format_python_namespace + +countries = {x.alpha_2 for x in pycountry.countries} + +generator_string = """script.countries + +The values are directly corresponding to the ISO 3166 standard. If you need changes +to the political situation in the world, please contact the ISO 3166 working group. +""" + +Path("homeassistant/generated/countries.py").write_text( + format_python_namespace( + { + "COUNTRIES": countries, + }, + generator=generator_string, + ) +) diff --git a/script/languages.py b/script/languages.py new file mode 100644 index 00000000000..ad88a31b0b6 --- /dev/null +++ b/script/languages.py @@ -0,0 +1,25 @@ +"""Helper script to update language list from the frontend source.""" +import json +from pathlib import Path +import sys + +import requests + +from .hassfest.serializer import format_python_namespace + +tag = sys.argv[1] if len(sys.argv) > 1 else "dev" + +req = requests.get( + f"https://raw.githubusercontent.com/home-assistant/frontend/{tag}/src/translations/translationMetadata.json" +) +data = json.loads(req.content) +languages = set(data.keys()) + +Path("homeassistant/generated/languages.py").write_text( + format_python_namespace( + { + "LANGUAGES": languages, + }, + generator="script.languages [frontend_tag]", + ) +) diff --git a/tests/common.py b/tests/common.py index 48e51303a0c..b7a26cb5f6d 100644 --- a/tests/common.py +++ b/tests/common.py @@ -22,7 +22,7 @@ from unittest.mock import AsyncMock, Mock, patch from aiohttp.test_utils import unused_port as get_test_instance_port # noqa: F401 import voluptuous as vol -from homeassistant import auth, config_entries, core as ha, loader +from homeassistant import auth, bootstrap, config_entries, core as ha, loader from homeassistant.auth import ( auth_store, models as auth_models, @@ -306,6 +306,7 @@ async def async_test_home_assistant(loop, load_registries=True): issue_registry.async_load(hass), ) await hass.async_block_till_done() + hass.data[bootstrap.DATA_REGISTRIES_LOADED] = None hass.state = ha.CoreState.running diff --git a/tests/components/config/test_core.py b/tests/components/config/test_core.py index 33309f6b6c6..67977c96a2b 100644 --- a/tests/components/config/test_core.py +++ b/tests/components/config/test_core.py @@ -60,6 +60,8 @@ async def test_websocket_core_update(hass, client): assert hass.config.external_url != "https://www.example.com" assert hass.config.internal_url != "http://example.com" assert hass.config.currency == "EUR" + assert hass.config.country != "SE" + assert hass.config.language != "sv" with patch("homeassistant.util.dt.set_default_time_zone") as mock_set_tz: await client.send_json( @@ -75,6 +77,8 @@ async def test_websocket_core_update(hass, client): "external_url": "https://www.example.com", "internal_url": "http://example.local", "currency": "USD", + "country": "SE", + "language": "sv", } ) diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index da9fa2cc68d..3b087b6a40b 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -1344,3 +1344,27 @@ def test_historic_currency(): for value in ("DEM", "NLG"): assert schema(value) + + +def test_country(): + """Test country validator.""" + schema = vol.Schema(cv.country) + + for value in (None, "Candyland", "USA"): + with pytest.raises(vol.MultipleInvalid): + schema(value) + + for value in ("NL", "SE"): + assert schema(value) + + +def test_language(): + """Test language validator.""" + schema = vol.Schema(cv.language) + + for value in (None, "Klingon", "english"): + with pytest.raises(vol.MultipleInvalid): + schema(value) + + for value in ("en", "sv"): + assert schema(value) diff --git a/tests/test_config.py b/tests/test_config.py index ef364638725..17b3898eb2a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -40,7 +40,7 @@ from homeassistant.util.unit_system import ( ) from homeassistant.util.yaml import SECRET_YAML -from tests.common import get_test_config_dir, patch_yaml_files +from tests.common import MockUser, get_test_config_dir, patch_yaml_files CONFIG_DIR = get_test_config_dir() YAML_PATH = os.path.join(CONFIG_DIR, config_util.YAML_CONFIG_FILE) @@ -214,6 +214,8 @@ def test_core_config_schema(): {"customize": "bla"}, {"customize": {"light.sensor": 100}}, {"customize": {"entity_id": []}}, + {"country": "xx"}, + {"language": "xx"}, ): with pytest.raises(MultipleInvalid): config_util.CORE_CONFIG_SCHEMA(value) @@ -228,6 +230,8 @@ def test_core_config_schema(): CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, "currency": "USD", "customize": {"sensor.temperature": {"hidden": True}}, + "country": "SE", + "language": "sv", } ) @@ -393,9 +397,12 @@ async def test_loading_configuration_from_storage(hass, hass_storage): "external_url": "https://www.example.com", "internal_url": "http://example.local", "currency": "EUR", + "country": "SE", + "language": "sv", }, "key": "core.config", "version": 1, + "minor_version": 3, } await config_util.async_process_ha_core_config( hass, {"allowlist_external_dirs": "/etc"} @@ -410,6 +417,8 @@ async def test_loading_configuration_from_storage(hass, hass_storage): assert hass.config.external_url == "https://www.example.com" assert hass.config.internal_url == "http://example.local" assert hass.config.currency == "EUR" + assert hass.config.country == "SE" + assert hass.config.language == "sv" assert len(hass.config.allowlist_external_dirs) == 3 assert "/etc" in hass.config.allowlist_external_dirs assert hass.config.config_source is ConfigSource.STORAGE @@ -475,10 +484,15 @@ async def test_migration_and_updating_configuration(hass, hass_storage): expected_new_core_data["data"]["currency"] = "USD" # 1.1 -> 1.2 store migration with migrated unit system expected_new_core_data["data"]["unit_system_v2"] = "us_customary" - expected_new_core_data["minor_version"] = 2 + expected_new_core_data["minor_version"] = 3 + # defaults for country and language + expected_new_core_data["data"]["country"] = None + expected_new_core_data["data"]["language"] = "en" assert hass_storage["core.config"] == expected_new_core_data assert hass.config.latitude == 50 assert hass.config.currency == "USD" + assert hass.config.country is None + assert hass.config.language == "en" async def test_override_stored_configuration(hass, hass_storage): @@ -527,6 +541,8 @@ async def test_loading_configuration(hass): "media_dirs": {"mymedia": "/usr"}, "legacy_templates": True, "currency": "EUR", + "country": "SE", + "language": "sv", }, ) @@ -545,6 +561,74 @@ async def test_loading_configuration(hass): assert hass.config.config_source is ConfigSource.YAML assert hass.config.legacy_templates is True assert hass.config.currency == "EUR" + assert hass.config.country == "SE" + assert hass.config.language == "sv" + + +@pytest.mark.parametrize( + "minor_version, users, user_data, default_language", + ( + (2, (), {}, "en"), + (2, ({"is_owner": True},), {}, "en"), + ( + 2, + ({"id": "user1", "is_owner": True},), + {"user1": {"language": {"language": "sv"}}}, + "sv", + ), + ( + 2, + ({"id": "user1", "is_owner": False},), + {"user1": {"language": {"language": "sv"}}}, + "en", + ), + (3, (), {}, "en"), + (3, ({"is_owner": True},), {}, "en"), + ( + 3, + ({"id": "user1", "is_owner": True},), + {"user1": {"language": {"language": "sv"}}}, + "en", + ), + ( + 3, + ({"id": "user1", "is_owner": False},), + {"user1": {"language": {"language": "sv"}}}, + "en", + ), + ), +) +async def test_language_default( + hass, hass_storage, minor_version, users, user_data, default_language +): + """Test language config default to owner user's language during migration. + + This should only happen if the core store version < 1.3 + """ + core_data = { + "data": {}, + "key": "core.config", + "version": 1, + "minor_version": minor_version, + } + hass_storage["core.config"] = dict(core_data) + + for user_config in users: + user = MockUser(**user_config).add_to_hass(hass) + if user.id not in user_data: + continue + storage_key = f"frontend.user_data_{user.id}" + hass_storage[storage_key] = { + "key": storage_key, + "version": 1, + "data": user_data[user.id], + } + + await config_util.async_process_ha_core_config( + hass, + {}, + ) + assert hass.config.language == default_language async def test_loading_configuration_default_media_dirs_docker(hass): diff --git a/tests/test_core.py b/tests/test_core.py index 017c8b3b607..80f7c18d254 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -948,6 +948,8 @@ async def test_config_defaults(): assert config.safe_mode is False assert config.legacy_templates is False assert config.currency == "EUR" + assert config.country is None + assert config.language == "en" async def test_config_path_with_file(): @@ -989,6 +991,8 @@ async def test_config_as_dict(): "external_url": None, "internal_url": None, "currency": "EUR", + "country": None, + "language": "en", } assert expected == config.as_dict() From f3b3193f7a9def042bfd7abadc90136e6dafb971 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 25 Nov 2022 00:24:19 +0000 Subject: [PATCH 0708/1033] [ci skip] Translation update --- .../accuweather/translations/de.json | 6 ++++ .../accuweather/translations/el.json | 6 ++++ .../accuweather/translations/en.json | 6 ++++ .../accuweather/translations/es.json | 12 +++++-- .../accuweather/translations/sk.json | 9 ++++- .../components/adax/translations/sk.json | 15 +++++++- .../components/adguard/translations/sk.json | 6 +++- .../components/aemet/translations/sk.json | 13 ++++++- .../components/airly/translations/sk.json | 8 ++++- .../components/airnow/translations/sk.json | 5 ++- .../components/airq/translations/sk.json | 1 + .../components/airthings/translations/sk.json | 6 +++- .../components/airvisual/translations/sk.json | 1 + .../components/airzone/translations/bg.json | 8 +++++ .../components/airzone/translations/de.json | 8 +++++ .../components/airzone/translations/el.json | 8 +++++ .../components/airzone/translations/et.json | 8 +++++ .../components/airzone/translations/fr.json | 6 ++++ .../components/airzone/translations/id.json | 8 +++++ .../components/airzone/translations/it.json | 8 +++++ .../components/airzone/translations/no.json | 8 +++++ .../airzone/translations/pt-BR.json | 8 +++++ .../components/airzone/translations/ru.json | 8 +++++ .../components/airzone/translations/sk.json | 5 +++ .../airzone/translations/zh-Hant.json | 8 +++++ .../alarmdecoder/translations/sk.json | 8 +++++ .../amberelectric/translations/sk.json | 1 + .../ambiclimate/translations/sk.json | 1 + .../components/androidtv/translations/sk.json | 5 ++- .../components/anthemav/translations/sk.json | 4 +++ .../components/apcupsd/translations/sk.json | 6 +++- .../components/apple_tv/translations/sk.json | 13 +++++-- .../components/arcam_fmj/translations/sk.json | 3 +- .../aseko_pool_live/translations/sk.json | 7 +++- .../components/asuswrt/translations/sk.json | 5 ++- .../components/atag/translations/sk.json | 3 ++ .../components/august/translations/sk.json | 5 ++- .../components/aurora/translations/sk.json | 3 ++ .../aussie_broadband/translations/sk.json | 16 +++++++-- .../components/auth/translations/sk.json | 20 +++++++++++ .../components/awair/translations/sk.json | 9 ++++- .../components/axis/translations/sk.json | 5 ++- .../azure_devops/translations/sk.json | 10 +++++- .../azure_event_hub/translations/sk.json | 4 +++ .../components/balboa/translations/sk.json | 4 +++ .../binary_sensor/translations/sk.json | 4 +++ .../components/blebox/translations/sk.json | 6 ++++ .../components/blink/translations/sk.json | 14 +++++++- .../bluemaestro/translations/sk.json | 8 +++++ .../components/bluetooth/translations/sk.json | 3 ++ .../bmw_connected_drive/translations/sk.json | 13 +++++++ .../components/bond/translations/sk.json | 7 +++- .../components/broadlink/translations/sk.json | 8 +++-- .../components/brother/translations/sk.json | 1 + .../components/brunt/translations/sk.json | 5 ++- .../components/bsblan/translations/sk.json | 6 +++- .../components/bthome/translations/sk.json | 11 ++++++ .../buienradar/translations/sk.json | 6 ++++ .../components/canary/translations/sk.json | 6 ++++ .../components/cast/translations/sk.json | 6 ++++ .../cert_expiry/translations/sk.json | 8 +++++ .../components/climacell/translations/en.json | 13 +++++++ .../components/climacell/translations/es.json | 4 +-- .../climacell/translations/sensor.en.json | 27 +++++++++++++++ .../components/climate/translations/sk.json | 6 ++++ .../components/coinbase/translations/sk.json | 8 +++-- .../components/control4/translations/sk.json | 7 ++-- .../coolmaster/translations/sk.json | 3 ++ .../coronavirus/translations/sk.json | 8 +++++ .../components/cover/translations/sk.json | 3 +- .../components/cpuspeed/translations/sk.json | 7 ++++ .../crownstone/translations/sk.json | 6 +++- .../components/daikin/translations/sk.json | 8 ++++- .../components/deconz/translations/sk.json | 5 +++ .../components/deluge/translations/sk.json | 10 +++++- .../components/demo/translations/de.json | 8 +++++ .../components/demo/translations/el.json | 8 +++++ .../components/demo/translations/en.json | 6 +--- .../components/demo/translations/es.json | 8 +++++ .../components/demo/translations/id.json | 8 +++++ .../components/demo/translations/it.json | 8 +++++ .../components/demo/translations/zh-Hant.json | 8 +++++ .../components/denonavr/translations/sk.json | 4 +++ .../devolo_home_control/translations/sk.json | 1 + .../devolo_home_network/translations/sk.json | 9 +++++ .../components/dexcom/translations/sk.json | 7 +++- .../components/directv/translations/sk.json | 6 +++- .../components/discord/translations/sk.json | 12 +++++++ .../components/dlna_dmr/translations/sk.json | 4 +++ .../components/dlna_dms/translations/sk.json | 1 + .../components/dnsip/translations/sk.json | 5 +++ .../components/doorbird/translations/sk.json | 3 +- .../components/dsmr/translations/sk.json | 3 +- .../components/ebusd/translations/sk.json | 5 +++ .../components/econet/translations/sk.json | 1 + .../components/efergy/translations/sk.json | 4 ++- .../components/elgato/translations/sk.json | 6 +++- .../components/elkm1/translations/sk.json | 13 +++++-- .../components/elmax/translations/sk.json | 5 ++- .../components/emonitor/translations/sk.json | 7 ++++ .../emulated_roku/translations/sk.json | 3 ++ .../components/enocean/translations/sk.json | 7 ++++ .../enphase_envoy/translations/sk.json | 5 ++- .../environment_canada/translations/sk.json | 5 +++ .../components/epson/translations/sk.json | 3 ++ .../evil_genius_labs/translations/sk.json | 4 +++ .../components/ezviz/translations/sk.json | 5 +++ .../faa_delays/translations/sk.json | 4 ++- .../components/fibaro/translations/sk.json | 6 ++++ .../components/filesize/translations/sk.json | 11 ++++++ .../fireservicerota/translations/sk.json | 8 +++-- .../components/firmata/translations/sk.json | 7 ++++ .../components/fivem/translations/sk.json | 6 ++++ .../flick_electric/translations/sk.json | 7 +++- .../components/flipr/translations/sk.json | 4 ++- .../components/flo/translations/sk.json | 4 ++- .../components/flume/translations/sk.json | 5 ++- .../forked_daapd/translations/sk.json | 1 + .../components/foscam/translations/sk.json | 4 ++- .../components/freebox/translations/sk.json | 5 +++ .../freedompro/translations/sk.json | 1 + .../components/fritz/translations/sk.json | 5 ++- .../components/fritzbox/translations/sk.json | 1 + .../components/fronius/translations/sk.json | 7 +++- .../fully_kiosk/translations/sk.json | 7 ++++ .../garages_amsterdam/translations/sk.json | 4 ++- .../components/gdacs/translations/sk.json | 7 ++++ .../components/generic/translations/sk.json | 15 ++++++++ .../geocaching/translations/sk.json | 9 +++++ .../geonetnz_quakes/translations/sk.json | 7 ++++ .../geonetnz_volcano/translations/sk.json | 7 ++++ .../components/gios/translations/sk.json | 6 ++++ .../components/glances/translations/sk.json | 3 ++ .../components/goalzero/translations/sk.json | 7 ++-- .../components/gogogate2/translations/sk.json | 5 +++ .../components/goodwe/translations/sk.json | 3 ++ .../components/google/translations/sk.json | 9 +++++ .../google_sheets/translations/sk.json | 12 +++++++ .../google_travel_time/translations/sk.json | 4 +++ .../components/govee_ble/translations/sk.json | 8 +++++ .../components/guardian/translations/sk.json | 17 +++++++++- .../components/habitica/translations/sk.json | 6 ++-- .../harmony/translations/select.de.json | 7 ++++ .../harmony/translations/select.el.json | 7 ++++ .../harmony/translations/select.es.json | 7 ++++ .../harmony/translations/select.et.json | 7 ++++ .../harmony/translations/select.fr.json | 7 ++++ .../harmony/translations/select.id.json | 7 ++++ .../harmony/translations/select.it.json | 7 ++++ .../harmony/translations/select.no.json | 7 ++++ .../harmony/translations/select.pl.json | 7 ++++ .../harmony/translations/select.pt-BR.json | 7 ++++ .../harmony/translations/select.ru.json | 7 ++++ .../harmony/translations/select.zh-Hant.json | 7 ++++ .../components/harmony/translations/sk.json | 7 ++++ .../components/hassio/translations/sk.json | 7 ++++ .../components/heos/translations/sk.json | 3 ++ .../here_travel_time/translations/sk.json | 4 +++ .../components/hive/translations/sk.json | 18 +++++++++- .../components/hlk_sw16/translations/sk.json | 7 +++- .../home_plus_control/translations/sk.json | 1 + .../homeassistant/translations/sk.json | 7 ++++ .../homeassistant_yellow/translations/it.json | 3 +- .../translations/pt-BR.json | 5 +-- .../homeassistant_yellow/translations/sk.json | 5 +++ .../translations/zh-Hant.json | 5 +-- .../translations/pt-BR.json | 2 +- .../homekit_controller/translations/sk.json | 10 ++++++ .../translations/zh-Hant.json | 2 +- .../homewizard/translations/sk.json | 3 ++ .../huawei_lte/translations/sk.json | 9 +++++ .../components/hue/translations/sk.json | 5 ++- .../huisbaasje/translations/sk.json | 3 +- .../translations/sk.json | 4 +++ .../hvv_departures/translations/sk.json | 25 +++++++++++++- .../components/hyperion/translations/sk.json | 7 ++++ .../components/ialarm/translations/sk.json | 4 +++ .../components/iaqualink/translations/sk.json | 1 + .../components/icloud/translations/sk.json | 5 ++- .../components/inkbird/translations/sk.json | 8 +++++ .../components/insteon/translations/sk.json | 19 +++++++++++ .../intellifire/translations/sk.json | 5 +++ .../components/iotawatt/translations/sk.json | 4 ++- .../components/ipma/translations/sk.json | 3 ++ .../components/ipp/translations/sk.json | 4 ++- .../components/iqvia/translations/sk.json | 7 ++++ .../components/isy994/translations/sk.json | 3 +- .../components/jellyfin/translations/sk.json | 4 ++- .../components/juicenet/translations/sk.json | 7 +++- .../justnimbus/translations/sk.json | 5 +++ .../kaleidescape/translations/sk.json | 3 ++ .../keenetic_ndms2/translations/sk.json | 6 ++++ .../components/kegtron/translations/sk.json | 8 +++++ .../keymitt_ble/translations/sk.json | 4 ++- .../components/kmtronic/translations/sk.json | 4 ++- .../components/knx/translations/sk.json | 16 +++++++++ .../components/kodi/translations/sk.json | 8 +++-- .../components/konnected/translations/sk.json | 10 +++++- .../kostal_plenticore/translations/sk.json | 7 +++- .../components/kraken/translations/sk.json | 5 +++ .../lacrosse_view/translations/sk.json | 5 +++ .../components/lametric/translations/sk.json | 15 +++++++- .../components/laundrify/translations/sk.json | 9 +++++ .../components/led_ble/translations/sk.json | 10 +++++- .../components/lidarr/translations/sk.json | 7 ++++ .../components/life360/translations/sk.json | 2 ++ .../components/lifx/translations/sk.json | 8 ++++- .../litterrobot/translations/sk.json | 3 ++ .../components/livisi/translations/sk.json | 4 ++- .../components/local_ip/translations/sk.json | 10 ++++++ .../logi_circle/translations/sk.json | 3 ++ .../components/lookin/translations/sk.json | 10 +++++- .../components/luftdaten/translations/sk.json | 2 ++ .../lutron_caseta/translations/sk.json | 11 ++++-- .../components/mazda/translations/sk.json | 6 +++- .../components/meater/translations/sk.json | 4 ++- .../components/melcloud/translations/sk.json | 4 ++- .../met_eireann/translations/es.json | 2 +- .../met_eireann/translations/sk.json | 3 ++ .../meteo_france/translations/es.json | 2 +- .../meteo_france/translations/sk.json | 8 +++++ .../meteoclimatic/translations/sk.json | 3 +- .../components/metoffice/translations/sk.json | 7 ++++ .../components/mikrotik/translations/sk.json | 7 +++- .../components/mill/translations/sk.json | 9 +++++ .../components/min_max/translations/de.json | 10 +++--- .../components/min_max/translations/es.json | 10 +++--- .../minecraft_server/translations/sk.json | 3 ++ .../components/mjpeg/translations/sk.json | 2 ++ .../components/moat/translations/sk.json | 8 +++++ .../modem_callerid/translations/sk.json | 3 ++ .../modern_forms/translations/sk.json | 6 +++- .../moehlenhoff_alpha2/translations/sk.json | 4 +++ .../components/monoprice/translations/sk.json | 3 ++ .../components/motioneye/translations/sk.json | 1 + .../components/mqtt/translations/el.json | 12 +++++-- .../components/mqtt/translations/it.json | 12 +++++-- .../components/mqtt/translations/no.json | 12 +++++-- .../components/mqtt/translations/pt-BR.json | 12 +++++-- .../components/mqtt/translations/ru.json | 12 +++++-- .../components/mqtt/translations/zh-Hant.json | 12 +++++-- .../components/myq/translations/sk.json | 1 + .../components/mysensors/translations/sk.json | 4 +++ .../components/nest/translations/sk.json | 1 + .../components/nextdns/translations/sk.json | 7 ++++ .../nibe_heatpump/translations/sk.json | 14 ++++++++ .../nmap_tracker/translations/sk.json | 19 +++++++++++ .../components/nobo_hub/translations/sk.json | 10 ++++++ .../components/notion/translations/sk.json | 1 + .../components/nws/translations/sk.json | 6 +++- .../components/nzbget/translations/sk.json | 9 +++++ .../components/oncue/translations/sk.json | 3 ++ .../open_meteo/translations/es.json | 2 +- .../openexchangerates/translations/sk.json | 7 ++++ .../opentherm_gw/translations/sk.json | 4 +++ .../components/openuv/translations/sk.json | 10 ++++++ .../components/oralb/translations/sk.json | 8 +++++ .../overkiz/translations/sensor.sk.json | 4 +++ .../components/overkiz/translations/sk.json | 1 + .../ovo_energy/translations/sk.json | 4 ++- .../panasonic_viera/translations/sk.json | 3 ++ .../components/peco/translations/sk.json | 7 ++++ .../components/plaato/translations/sk.json | 13 +++++++ .../components/plex/translations/sk.json | 1 + .../components/plugwise/translations/sk.json | 3 ++ .../plum_lightpad/translations/sk.json | 3 ++ .../components/point/translations/sk.json | 3 ++ .../components/powerwall/translations/sk.json | 1 + .../components/ps4/translations/sk.json | 13 +++++++ .../components/pushover/translations/sk.json | 7 ++++ .../pvpc_hourly_pricing/translations/sk.json | 17 +++++++++- .../components/qingping/translations/sk.json | 8 +++++ .../components/rachio/translations/sk.json | 9 +++++ .../components/radarr/translations/el.json | 4 +++ .../components/radarr/translations/en.json | 8 +++-- .../components/radarr/translations/es.json | 4 +++ .../components/radarr/translations/sk.json | 7 ++++ .../recollect_waste/translations/sk.json | 10 ++++++ .../components/recorder/translations/sk.json | 7 ++++ .../components/renault/translations/sk.json | 1 + .../components/rfxtrx/translations/sk.json | 6 +++- .../components/ridwell/translations/sk.json | 1 + .../components/ring/translations/sk.json | 3 ++ .../components/scrape/translations/it.json | 30 ++++++++++++++++ .../components/scrape/translations/pt-BR.json | 34 +++++++++++++++++-- .../components/scrape/translations/sk.json | 3 ++ .../scrape/translations/zh-Hant.json | 34 +++++++++++++++++-- .../screenlogic/translations/sk.json | 1 + .../season/translations/sensor.sk.json | 1 + .../components/season/translations/sk.json | 14 ++++++++ .../components/select/translations/sk.json | 10 ++++++ .../components/senseme/translations/sk.json | 6 ++-- .../components/sensibo/translations/sk.json | 3 ++ .../components/sensor/translations/sk.json | 34 ++++++++++++++++++- .../components/senz/translations/sk.json | 8 +++++ .../components/sharkiq/translations/sk.json | 1 + .../components/sia/translations/sk.json | 6 ++++ .../simplisafe/translations/sk.json | 8 +++++ .../components/skybell/translations/sk.json | 3 ++ .../components/slack/translations/sk.json | 3 ++ .../components/sleepiq/translations/sk.json | 3 ++ .../components/smarttub/translations/sk.json | 4 ++- .../components/smhi/translations/sk.json | 3 ++ .../components/snooz/translations/sk.json | 8 +++++ .../components/solax/translations/sk.json | 1 + .../components/soma/translations/sk.json | 3 ++ .../somfy_mylink/translations/sk.json | 6 +++- .../components/sonarr/translations/sk.json | 1 + .../components/songpal/translations/sk.json | 3 ++ .../components/sql/translations/sk.json | 3 ++ .../steam_online/translations/sk.json | 7 ++++ .../stookalert/translations/sk.json | 7 ++++ .../components/subaru/translations/sk.json | 3 ++ .../surepetcare/translations/sk.json | 3 ++ .../components/syncthing/translations/sk.json | 3 ++ .../components/syncthru/translations/sk.json | 3 ++ .../synology_dsm/translations/sk.json | 9 +++++ .../system_bridge/translations/sk.json | 1 + .../tankerkoenig/translations/sk.json | 3 ++ .../components/tautulli/translations/sk.json | 7 ++++ .../tellduslive/translations/sk.json | 10 ++++++ .../components/text/translations/de.json | 3 ++ .../components/text/translations/el.json | 3 ++ .../components/text/translations/en.json | 3 ++ .../components/text/translations/es.json | 3 ++ .../thermobeacon/translations/sk.json | 8 +++++ .../components/thermopro/translations/sk.json | 8 +++++ .../components/tibber/translations/sk.json | 3 ++ .../components/tile/translations/sk.json | 1 + .../components/tilt_ble/translations/sk.json | 8 +++++ .../components/tolo/translations/sk.json | 3 +- .../tomorrowio/translations/es.json | 2 +- .../tomorrowio/translations/sk.json | 3 ++ .../components/toon/translations/sk.json | 12 +++++++ .../totalconnect/translations/sk.json | 1 + .../trafikverket_ferry/translations/sk.json | 14 ++++++++ .../trafikverket_train/translations/sk.json | 14 ++++++++ .../translations/sk.json | 3 ++ .../transmission/translations/sk.json | 12 +++++++ .../tuya/translations/select.sk.json | 7 +++- .../twentemilieu/translations/sk.json | 10 ++++++ .../ukraine_alarm/translations/sk.json | 7 ++++ .../components/unifi/translations/sk.json | 4 ++- .../unifiprotect/translations/sk.json | 3 ++ .../components/upb/translations/sk.json | 10 ++++++ .../uptimerobot/translations/sk.json | 1 + .../components/vallox/translations/sk.json | 1 + .../components/verisure/translations/sk.json | 1 + .../vlc_telnet/translations/sk.json | 1 + .../volvooncall/translations/sk.json | 3 ++ .../components/vulcan/translations/sk.json | 8 +++++ .../components/wallbox/translations/sk.json | 4 ++- .../components/watttime/translations/sk.json | 4 ++- .../waze_travel_time/translations/sk.json | 3 ++ .../components/whirlpool/translations/sk.json | 4 ++- .../components/whois/translations/sk.json | 3 ++ .../components/wiffi/translations/sk.json | 4 +++ .../components/wilight/translations/sk.json | 3 +- .../components/withings/translations/sk.json | 10 ++++++ .../components/wiz/translations/sk.json | 5 +++ .../components/wled/translations/sk.json | 6 +++- .../components/wolflink/translations/sk.json | 7 +++- .../components/ws66i/translations/sk.json | 7 ++++ .../xiaomi_aqara/translations/sk.json | 4 +++ .../xiaomi_ble/translations/sk.json | 1 + .../xiaomi_miio/translations/sk.json | 8 ++++- .../yale_smart_alarm/translations/sk.json | 7 ++++ .../yalexs_ble/translations/sk.json | 6 ++++ .../translations/select.sk.json | 10 ++++++ .../components/yeelight/translations/sk.json | 3 ++ .../components/yolink/translations/sk.json | 9 +++++ .../components/youless/translations/sk.json | 3 ++ .../components/zha/translations/sk.json | 10 ++++++ .../components/zone/translations/sk.json | 3 ++ .../zoneminder/translations/sk.json | 3 ++ .../components/zwave_js/translations/sk.json | 14 ++++++-- 376 files changed, 2284 insertions(+), 161 deletions(-) create mode 100644 homeassistant/components/auth/translations/sk.json create mode 100644 homeassistant/components/climacell/translations/en.json create mode 100644 homeassistant/components/climacell/translations/sensor.en.json create mode 100644 homeassistant/components/coronavirus/translations/sk.json create mode 100644 homeassistant/components/cpuspeed/translations/sk.json create mode 100644 homeassistant/components/discord/translations/sk.json create mode 100644 homeassistant/components/ebusd/translations/sk.json create mode 100644 homeassistant/components/enocean/translations/sk.json create mode 100644 homeassistant/components/filesize/translations/sk.json create mode 100644 homeassistant/components/firmata/translations/sk.json create mode 100644 homeassistant/components/gdacs/translations/sk.json create mode 100644 homeassistant/components/geocaching/translations/sk.json create mode 100644 homeassistant/components/geonetnz_quakes/translations/sk.json create mode 100644 homeassistant/components/geonetnz_volcano/translations/sk.json create mode 100644 homeassistant/components/google_sheets/translations/sk.json create mode 100644 homeassistant/components/harmony/translations/select.de.json create mode 100644 homeassistant/components/harmony/translations/select.el.json create mode 100644 homeassistant/components/harmony/translations/select.es.json create mode 100644 homeassistant/components/harmony/translations/select.et.json create mode 100644 homeassistant/components/harmony/translations/select.fr.json create mode 100644 homeassistant/components/harmony/translations/select.id.json create mode 100644 homeassistant/components/harmony/translations/select.it.json create mode 100644 homeassistant/components/harmony/translations/select.no.json create mode 100644 homeassistant/components/harmony/translations/select.pl.json create mode 100644 homeassistant/components/harmony/translations/select.pt-BR.json create mode 100644 homeassistant/components/harmony/translations/select.ru.json create mode 100644 homeassistant/components/harmony/translations/select.zh-Hant.json create mode 100644 homeassistant/components/hassio/translations/sk.json create mode 100644 homeassistant/components/homeassistant/translations/sk.json create mode 100644 homeassistant/components/iqvia/translations/sk.json create mode 100644 homeassistant/components/laundrify/translations/sk.json create mode 100644 homeassistant/components/lidarr/translations/sk.json create mode 100644 homeassistant/components/local_ip/translations/sk.json create mode 100644 homeassistant/components/meteo_france/translations/sk.json create mode 100644 homeassistant/components/nextdns/translations/sk.json create mode 100644 homeassistant/components/nibe_heatpump/translations/sk.json create mode 100644 homeassistant/components/openexchangerates/translations/sk.json create mode 100644 homeassistant/components/peco/translations/sk.json create mode 100644 homeassistant/components/pushover/translations/sk.json create mode 100644 homeassistant/components/radarr/translations/sk.json create mode 100644 homeassistant/components/recorder/translations/sk.json create mode 100644 homeassistant/components/season/translations/sk.json create mode 100644 homeassistant/components/select/translations/sk.json create mode 100644 homeassistant/components/senz/translations/sk.json create mode 100644 homeassistant/components/steam_online/translations/sk.json create mode 100644 homeassistant/components/stookalert/translations/sk.json create mode 100644 homeassistant/components/tautulli/translations/sk.json create mode 100644 homeassistant/components/text/translations/de.json create mode 100644 homeassistant/components/text/translations/el.json create mode 100644 homeassistant/components/text/translations/en.json create mode 100644 homeassistant/components/text/translations/es.json create mode 100644 homeassistant/components/toon/translations/sk.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/sk.json create mode 100644 homeassistant/components/trafikverket_train/translations/sk.json create mode 100644 homeassistant/components/twentemilieu/translations/sk.json create mode 100644 homeassistant/components/ukraine_alarm/translations/sk.json create mode 100644 homeassistant/components/withings/translations/sk.json create mode 100644 homeassistant/components/yamaha_musiccast/translations/select.sk.json create mode 100644 homeassistant/components/yolink/translations/sk.json diff --git a/homeassistant/components/accuweather/translations/de.json b/homeassistant/components/accuweather/translations/de.json index f7b02feb091..5958332676a 100644 --- a/homeassistant/components/accuweather/translations/de.json +++ b/homeassistant/components/accuweather/translations/de.json @@ -24,6 +24,12 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "Wettervorhersage" + }, + "description": "Aufgrund der Einschr\u00e4nkungen der kostenlosen Version des AccuWeather-API-Schl\u00fcssels werden bei aktivierter Wettervorhersage Datenaktualisierungen alle 80 Minuten statt alle 40 Minuten durchgef\u00fchrt." + }, "user": { "data": { "forecast": "Wettervorhersage" diff --git a/homeassistant/components/accuweather/translations/el.json b/homeassistant/components/accuweather/translations/el.json index b8d7d22df70..a0eca3e75fc 100644 --- a/homeassistant/components/accuweather/translations/el.json +++ b/homeassistant/components/accuweather/translations/el.json @@ -24,6 +24,12 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "\u03a0\u03c1\u03cc\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9\u03c1\u03bf\u03cd" + }, + "description": "\u039b\u03cc\u03b3\u03c9 \u03c4\u03c9\u03bd \u03c0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03ce\u03bd \u03c4\u03b7\u03c2 \u03b4\u03c9\u03c1\u03b5\u03ac\u03bd \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd AccuWeather API, \u03cc\u03c4\u03b1\u03bd \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03cc\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9\u03c1\u03bf\u03cd, \u03bf\u03b9 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03b5\u03b9\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd \u03b8\u03b1 \u03b5\u03ba\u03c4\u03b5\u03bb\u03bf\u03cd\u03bd\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 80 \u03bb\u03b5\u03c0\u03c4\u03ac \u03b1\u03bd\u03c4\u03af \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 40 \u03bb\u03b5\u03c0\u03c4\u03ac." + }, "user": { "data": { "forecast": "\u03a0\u03c1\u03cc\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9\u03c1\u03bf\u03cd" diff --git a/homeassistant/components/accuweather/translations/en.json b/homeassistant/components/accuweather/translations/en.json index 7a8599fb562..844970f0d2b 100644 --- a/homeassistant/components/accuweather/translations/en.json +++ b/homeassistant/components/accuweather/translations/en.json @@ -29,6 +29,12 @@ "forecast": "Weather forecast" }, "description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 80 minutes instead of every 40 minutes." + }, + "user": { + "data": { + "forecast": "Weather forecast" + }, + "description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 80 minutes instead of every 40 minutes." } } }, diff --git a/homeassistant/components/accuweather/translations/es.json b/homeassistant/components/accuweather/translations/es.json index 9ec67fd88b8..094d725c2f4 100644 --- a/homeassistant/components/accuweather/translations/es.json +++ b/homeassistant/components/accuweather/translations/es.json @@ -4,7 +4,7 @@ "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, "create_entry": { - "default": "Algunos sensores no est\u00e1n habilitados de forma predeterminada. Puedes habilitarlos en el registro de la entidad despu\u00e9s de la configuraci\u00f3n de la integraci\u00f3n.\nEl pron\u00f3stico del tiempo no est\u00e1 habilitado de forma predeterminada. Puedes habilitarlo en las opciones de integraci\u00f3n." + "default": "Algunos sensores no est\u00e1n habilitados de forma predeterminada. Puedes habilitarlos en el registro de la entidad despu\u00e9s de la configuraci\u00f3n de la integraci\u00f3n.\nLa previsi\u00f3n meteorol\u00f3gica no est\u00e1 habilitada de forma predeterminada. Puedes habilitarla en las opciones de integraci\u00f3n." }, "error": { "cannot_connect": "No se pudo conectar", @@ -24,11 +24,17 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "Previsi\u00f3n meteorol\u00f3gica" + }, + "description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilitas la previsi\u00f3n meteorol\u00f3gica, las actualizaciones de datos se realizar\u00e1n cada 80 minutos en lugar de cada 40 minutos." + }, "user": { "data": { - "forecast": "Pron\u00f3stico del tiempo" + "forecast": "Previsi\u00f3n meteorol\u00f3gica" }, - "description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilitas el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 80 minutos en lugar de cada 40 minutos." + "description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilitas la previsi\u00f3n meteorol\u00f3gica, las actualizaciones de datos se realizar\u00e1n cada 80 minutos en lugar de cada 40 minutos." } } }, diff --git a/homeassistant/components/accuweather/translations/sk.json b/homeassistant/components/accuweather/translations/sk.json index cc5fe25ca87..6891a048069 100644 --- a/homeassistant/components/accuweather/translations/sk.json +++ b/homeassistant/components/accuweather/translations/sk.json @@ -5,7 +5,8 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", + "requests_exceeded": "Povolen\u00fd po\u010det po\u017eiadaviek na rozhranie Accuweather API bol prekro\u010den\u00fd. Mus\u00edte po\u010dka\u0165 alebo zmeni\u0165 k\u013e\u00fa\u010d API." }, "step": { "user": { @@ -26,5 +27,11 @@ } } } + }, + "system_health": { + "info": { + "can_reach_server": "Oslovi\u0165 server AccuWeather", + "remaining_requests": "Zost\u00e1vaj\u00face povolen\u00e9 \u017eiadosti" + } } } \ No newline at end of file diff --git a/homeassistant/components/adax/translations/sk.json b/homeassistant/components/adax/translations/sk.json index 7f23f337f53..5232969e1a8 100644 --- a/homeassistant/components/adax/translations/sk.json +++ b/homeassistant/components/adax/translations/sk.json @@ -2,18 +2,31 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "heater_not_available": "Ohrieva\u010d nie je k dispoz\u00edcii. Sk\u00faste resetova\u0165 ohrieva\u010d stla\u010den\u00edm + a OK na nieko\u013eko sek\u00fand.", + "heater_not_found": "Ohrieva\u010d sa nena\u0161iel. Sk\u00faste presun\u00fa\u0165 ohrieva\u010d bli\u017e\u0161ie k Home Assistant.", "invalid_auth": "Neplatn\u00e9 overenie" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "cloud": { "data": { + "account_id": "ID \u00fa\u010dtu", "password": "Heslo" } }, "local": { "data": { - "wifi_pswd": "Heslo Wi-Fi" + "wifi_pswd": "Heslo Wi-Fi", + "wifi_ssid": "Wi-Fi SSID" } + }, + "user": { + "data": { + "connection_type": "Vyberte typ pripojenia" + }, + "description": "Vyberte typ pripojenia. Miestne vy\u017eaduje ohrieva\u010de s bluetooth" } } } diff --git a/homeassistant/components/adguard/translations/sk.json b/homeassistant/components/adguard/translations/sk.json index 0a425483090..0464e2f2f72 100644 --- a/homeassistant/components/adguard/translations/sk.json +++ b/homeassistant/components/adguard/translations/sk.json @@ -1,12 +1,16 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "existing_instance_updated": "Aktualizovan\u00e1 existuj\u00faca konfigur\u00e1cia." }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { + "hassio_confirm": { + "title": "AdGuard Home cez doplnok Home Assistant" + }, "user": { "data": { "host": "Hostite\u013e", diff --git a/homeassistant/components/aemet/translations/sk.json b/homeassistant/components/aemet/translations/sk.json index f14039f810f..dffe47482f1 100644 --- a/homeassistant/components/aemet/translations/sk.json +++ b/homeassistant/components/aemet/translations/sk.json @@ -11,7 +11,18 @@ "data": { "api_key": "API k\u013e\u00fa\u010d", "latitude": "Zemepisn\u00e1 \u0161\u00edrka", - "longitude": "Zemepisn\u00e1 d\u013a\u017eka" + "longitude": "Zemepisn\u00e1 d\u013a\u017eka", + "name": "N\u00e1zov integr\u00e1cie" + }, + "description": "Ak chcete vygenerova\u0165 k\u013e\u00fa\u010d API, prejdite na https://opendata.aemet.es/centrodedescargas/altaUsuario" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "station_updates": "Zhroma\u017edi\u0165 \u00fadaje z meteorologick\u00fdch stan\u00edc AEMET" } } } diff --git a/homeassistant/components/airly/translations/sk.json b/homeassistant/components/airly/translations/sk.json index 6f941ead750..ccea62b47aa 100644 --- a/homeassistant/components/airly/translations/sk.json +++ b/homeassistant/components/airly/translations/sk.json @@ -4,7 +4,8 @@ "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", + "wrong_location": "V tejto oblasti nie s\u00fa \u017eiadne meracie stanice Airly." }, "step": { "user": { @@ -16,5 +17,10 @@ } } } + }, + "system_health": { + "info": { + "requests_per_day": "Povolen\u00e9 po\u017eiadavky za de\u0148" + } } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/sk.json b/homeassistant/components/airnow/translations/sk.json index c0aa708a993..7de74db3936 100644 --- a/homeassistant/components/airnow/translations/sk.json +++ b/homeassistant/components/airnow/translations/sk.json @@ -4,7 +4,10 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_location": "Pre t\u00fato lokalitu sa nena\u0161li \u017eiadne v\u00fdsledky", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/airq/translations/sk.json b/homeassistant/components/airq/translations/sk.json index ddf1a8dc9b3..6598d0f17af 100644 --- a/homeassistant/components/airq/translations/sk.json +++ b/homeassistant/components/airq/translations/sk.json @@ -14,6 +14,7 @@ "ip_address": "IP adresa", "password": "Heslo" }, + "description": "Zadajte IP adresu alebo mDNS zariadenia a jeho heslo", "title": "Identifikujte zariadenie" } } diff --git a/homeassistant/components/airthings/translations/sk.json b/homeassistant/components/airthings/translations/sk.json index a404aeedf8e..45a1da112b7 100644 --- a/homeassistant/components/airthings/translations/sk.json +++ b/homeassistant/components/airthings/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", @@ -8,7 +11,8 @@ "step": { "user": { "data": { - "id": "ID" + "id": "ID", + "secret": "Tajn\u00e9" } } } diff --git a/homeassistant/components/airvisual/translations/sk.json b/homeassistant/components/airvisual/translations/sk.json index f54803951c5..74551f1c24e 100644 --- a/homeassistant/components/airvisual/translations/sk.json +++ b/homeassistant/components/airvisual/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9 alebo ID uzla/Pro je u\u017e zaregistrovan\u00e9.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/airzone/translations/bg.json b/homeassistant/components/airzone/translations/bg.json index cc5f200ef95..3cc33b64ef5 100644 --- a/homeassistant/components/airzone/translations/bg.json +++ b/homeassistant/components/airzone/translations/bg.json @@ -7,9 +7,17 @@ "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "step": { + "discovered_connection": { + "data": { + "host": "\u0425\u043e\u0441\u0442", + "id": "ID \u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0442\u0430", + "port": "\u041f\u043e\u0440\u0442" + } + }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442", + "id": "ID \u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0442\u0430", "port": "\u041f\u043e\u0440\u0442" } } diff --git a/homeassistant/components/airzone/translations/de.json b/homeassistant/components/airzone/translations/de.json index 2edb50330f4..8cd14ac4f6d 100644 --- a/homeassistant/components/airzone/translations/de.json +++ b/homeassistant/components/airzone/translations/de.json @@ -8,9 +8,17 @@ "invalid_system_id": "Ung\u00fcltige Airzone-System-ID" }, "step": { + "discovered_connection": { + "data": { + "host": "Host", + "id": "System-ID", + "port": "Port" + } + }, "user": { "data": { "host": "Host", + "id": "System-ID", "port": "Port" }, "description": "Richte die Airzone-Integration ein." diff --git a/homeassistant/components/airzone/translations/el.json b/homeassistant/components/airzone/translations/el.json index 13da6efe27d..2da19364c28 100644 --- a/homeassistant/components/airzone/translations/el.json +++ b/homeassistant/components/airzone/translations/el.json @@ -8,9 +8,17 @@ "invalid_system_id": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2 Airzone" }, "step": { + "discovered_connection": { + "data": { + "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", + "id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", + "port": "\u0398\u03cd\u03c1\u03b1" + } + }, "user": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", + "id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", "port": "\u0398\u03cd\u03c1\u03b1" }, "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Airzone." diff --git a/homeassistant/components/airzone/translations/et.json b/homeassistant/components/airzone/translations/et.json index dff9d1173f6..c209a688c2d 100644 --- a/homeassistant/components/airzone/translations/et.json +++ b/homeassistant/components/airzone/translations/et.json @@ -8,9 +8,17 @@ "invalid_system_id": "Airzone'i s\u00fcsteemi ID on vigane" }, "step": { + "discovered_connection": { + "data": { + "host": "Host", + "id": "S\u00fcsteemi ID", + "port": "Port" + } + }, "user": { "data": { "host": "Host", + "id": "S\u00fcsteemi ID", "port": "Port" }, "description": "Seadista Airzone'i sidumine" diff --git a/homeassistant/components/airzone/translations/fr.json b/homeassistant/components/airzone/translations/fr.json index 40d22ad8bd9..2d09bb7f1e4 100644 --- a/homeassistant/components/airzone/translations/fr.json +++ b/homeassistant/components/airzone/translations/fr.json @@ -8,6 +8,12 @@ "invalid_system_id": "ID syst\u00e8me Airzone non valide" }, "step": { + "discovered_connection": { + "data": { + "host": "Serveur", + "port": "Port" + } + }, "user": { "data": { "host": "H\u00f4te", diff --git a/homeassistant/components/airzone/translations/id.json b/homeassistant/components/airzone/translations/id.json index ec37639811b..f80012a3ad4 100644 --- a/homeassistant/components/airzone/translations/id.json +++ b/homeassistant/components/airzone/translations/id.json @@ -8,9 +8,17 @@ "invalid_system_id": "ID Sistem Airzone Tidak Valid" }, "step": { + "discovered_connection": { + "data": { + "host": "Host", + "id": "ID Sistem", + "port": "Port" + } + }, "user": { "data": { "host": "Host", + "id": "ID Sistem", "port": "Port" }, "description": "Siapkan integrasi Airzone" diff --git a/homeassistant/components/airzone/translations/it.json b/homeassistant/components/airzone/translations/it.json index 32f8c67b545..10297d1e288 100644 --- a/homeassistant/components/airzone/translations/it.json +++ b/homeassistant/components/airzone/translations/it.json @@ -8,9 +8,17 @@ "invalid_system_id": "ID sistema Airzone non valido" }, "step": { + "discovered_connection": { + "data": { + "host": "Host", + "id": "ID di sistema", + "port": "Porta" + } + }, "user": { "data": { "host": "Host", + "id": "ID di sistema", "port": "Porta" }, "description": "Imposta l'integrazione Airzone." diff --git a/homeassistant/components/airzone/translations/no.json b/homeassistant/components/airzone/translations/no.json index 6eeaee3a53a..2ab24253236 100644 --- a/homeassistant/components/airzone/translations/no.json +++ b/homeassistant/components/airzone/translations/no.json @@ -8,9 +8,17 @@ "invalid_system_id": "Ugyldig Airzone System ID" }, "step": { + "discovered_connection": { + "data": { + "host": "Vert", + "id": "System-ID", + "port": "Port" + } + }, "user": { "data": { "host": "Vert", + "id": "System-ID", "port": "Port" }, "description": "Sett opp Airzone-integrasjon." diff --git a/homeassistant/components/airzone/translations/pt-BR.json b/homeassistant/components/airzone/translations/pt-BR.json index c2668c937b4..10a0e4555b3 100644 --- a/homeassistant/components/airzone/translations/pt-BR.json +++ b/homeassistant/components/airzone/translations/pt-BR.json @@ -8,9 +8,17 @@ "invalid_system_id": "ID do sistema Airzone inv\u00e1lido" }, "step": { + "discovered_connection": { + "data": { + "host": "Host", + "id": "ID do sistema", + "port": "Porta" + } + }, "user": { "data": { "host": "Nome do host", + "id": "ID do sistema", "port": "Porta" }, "description": "Configure a integra\u00e7\u00e3o Airzone." diff --git a/homeassistant/components/airzone/translations/ru.json b/homeassistant/components/airzone/translations/ru.json index d480866b262..6ee7cb98950 100644 --- a/homeassistant/components/airzone/translations/ru.json +++ b/homeassistant/components/airzone/translations/ru.json @@ -8,9 +8,17 @@ "invalid_system_id": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0438\u0441\u0442\u0435\u043c\u044b Airzone." }, "step": { + "discovered_connection": { + "data": { + "host": "\u0425\u043e\u0441\u0442", + "id": "ID \u0441\u0438\u0441\u0442\u0435\u043c\u044b", + "port": "\u041f\u043e\u0440\u0442" + } + }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442", + "id": "ID \u0441\u0438\u0441\u0442\u0435\u043c\u044b", "port": "\u041f\u043e\u0440\u0442" }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Airzone." diff --git a/homeassistant/components/airzone/translations/sk.json b/homeassistant/components/airzone/translations/sk.json index 04558fa44e4..bf8ee141cec 100644 --- a/homeassistant/components/airzone/translations/sk.json +++ b/homeassistant/components/airzone/translations/sk.json @@ -7,6 +7,11 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { + "discovered_connection": { + "data": { + "host": "Hostite\u013e" + } + }, "user": { "data": { "host": "Hostite\u013e", diff --git a/homeassistant/components/airzone/translations/zh-Hant.json b/homeassistant/components/airzone/translations/zh-Hant.json index 42166fe39ec..b53c19ad364 100644 --- a/homeassistant/components/airzone/translations/zh-Hant.json +++ b/homeassistant/components/airzone/translations/zh-Hant.json @@ -8,9 +8,17 @@ "invalid_system_id": "\u7121\u6548\u7684 Airzone \u7cfb\u7d71 ID" }, "step": { + "discovered_connection": { + "data": { + "host": "\u4e3b\u6a5f\u7aef", + "id": "\u7cfb\u7d71 ID", + "port": "\u901a\u8a0a\u57e0" + } + }, "user": { "data": { "host": "\u4e3b\u6a5f\u7aef", + "id": "\u7cfb\u7d71 ID", "port": "\u901a\u8a0a\u57e0" }, "description": "\u8a2d\u5b9a Airzone \u6574\u5408\u3002" diff --git a/homeassistant/components/alarmdecoder/translations/sk.json b/homeassistant/components/alarmdecoder/translations/sk.json index c5139d40774..999e4490764 100644 --- a/homeassistant/components/alarmdecoder/translations/sk.json +++ b/homeassistant/components/alarmdecoder/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "protocol": { "data": { @@ -15,6 +18,10 @@ } }, "options": { + "error": { + "int": "Pole ni\u017e\u0161ie mus\u00ed by\u0165 cel\u00e9 \u010d\u00edslo.", + "loop_range": "RF Loop mus\u00ed by\u0165 cel\u00e9 \u010d\u00edslo od 1 do 4." + }, "step": { "init": { "data": { @@ -25,6 +32,7 @@ "data": { "zone_loop": "RF slu\u010dka", "zone_name": "N\u00e1zov z\u00f3ny", + "zone_relayaddr": "Adresa rel\u00e9", "zone_rfid": "RF Serial" } }, diff --git a/homeassistant/components/amberelectric/translations/sk.json b/homeassistant/components/amberelectric/translations/sk.json index 8b5990b5086..3f4b526a190 100644 --- a/homeassistant/components/amberelectric/translations/sk.json +++ b/homeassistant/components/amberelectric/translations/sk.json @@ -2,6 +2,7 @@ "config": { "error": { "invalid_api_token": "Neplatn\u00fd API k\u013e\u00fa\u010d", + "no_site": "Nebola poskytnut\u00e1 \u017eiadna str\u00e1nka", "unknown_error": "Neo\u010dak\u00e1van\u00e1 chyba" } } diff --git a/homeassistant/components/ambiclimate/translations/sk.json b/homeassistant/components/ambiclimate/translations/sk.json index 86647261f75..77910e52422 100644 --- a/homeassistant/components/ambiclimate/translations/sk.json +++ b/homeassistant/components/ambiclimate/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "access_token": "Nezn\u00e1ma chyba pri generovan\u00ed pr\u00edstupov\u00e9ho tokenu.", "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie." }, diff --git a/homeassistant/components/androidtv/translations/sk.json b/homeassistant/components/androidtv/translations/sk.json index 8e18f3c18cc..f281814b01a 100644 --- a/homeassistant/components/androidtv/translations/sk.json +++ b/homeassistant/components/androidtv/translations/sk.json @@ -4,11 +4,14 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { + "adb_server_ip": "IP adresa servera ADB (nechajte pr\u00e1zdnu, ak sa nepou\u017e\u00edva)", "device_class": "Typ zariadenia", "host": "Hostite\u013e", "port": "Port" diff --git a/homeassistant/components/anthemav/translations/sk.json b/homeassistant/components/anthemav/translations/sk.json index 3c3d27a6689..8301c6c87af 100644 --- a/homeassistant/components/anthemav/translations/sk.json +++ b/homeassistant/components/anthemav/translations/sk.json @@ -3,6 +3,10 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "cannot_receive_deviceinfo": "Nepodarilo sa na\u010d\u00edta\u0165 adresu MAC. Uistite sa, \u017ee je zariadenie zapnut\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/apcupsd/translations/sk.json b/homeassistant/components/apcupsd/translations/sk.json index 3c3d27a6689..bc0f2277bf9 100644 --- a/homeassistant/components/apcupsd/translations/sk.json +++ b/homeassistant/components/apcupsd/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_status": "Z Hostite\u013e nie je hl\u00e1sen\u00fd \u017eiadny stav" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { "user": { diff --git a/homeassistant/components/apple_tv/translations/sk.json b/homeassistant/components/apple_tv/translations/sk.json index 63c0bc85d40..f3bf36ec308 100644 --- a/homeassistant/components/apple_tv/translations/sk.json +++ b/homeassistant/components/apple_tv/translations/sk.json @@ -5,21 +5,30 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", - "setup_failed": "Zariadenie sa nepodarilo nastavi\u0165." + "setup_failed": "Zariadenie sa nepodarilo nastavi\u0165.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "invalid_auth": "Neplatn\u00e9 overenie", - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({type})", "step": { + "pair_no_pin": { + "description": "Pre slu\u017ebu `{protocol}` sa vy\u017eaduje p\u00e1rovanie. Ak chcete pokra\u010dova\u0165, zadajte na svojom zariaden\u00ed k\u00f3d PIN {pin}." + }, "password": { "title": "Vy\u017eaduje sa heslo" }, "reconfigure": { "title": "Rekonfigur\u00e1cia zariadenia" }, + "service_problem": { + "description": "Pri p\u00e1rovan\u00ed protokolu `{protocol}` do\u0161lo k probl\u00e9mu. Bude sa ignorova\u0165.", + "title": "Slu\u017ebu sa nepodarilo prida\u0165" + }, "user": { "data": { "device_input": "Zariadenie" diff --git a/homeassistant/components/arcam_fmj/translations/sk.json b/homeassistant/components/arcam_fmj/translations/sk.json index 087244c9027..ad75e5f9df3 100644 --- a/homeassistant/components/arcam_fmj/translations/sk.json +++ b/homeassistant/components/arcam_fmj/translations/sk.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "flow_title": "{host}", "step": { diff --git a/homeassistant/components/aseko_pool_live/translations/sk.json b/homeassistant/components/aseko_pool_live/translations/sk.json index 87352b48fad..97c939543c0 100644 --- a/homeassistant/components/aseko_pool_live/translations/sk.json +++ b/homeassistant/components/aseko_pool_live/translations/sk.json @@ -1,7 +1,12 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/asuswrt/translations/sk.json b/homeassistant/components/asuswrt/translations/sk.json index d61369ad97b..e92a75763f2 100644 --- a/homeassistant/components/asuswrt/translations/sk.json +++ b/homeassistant/components/asuswrt/translations/sk.json @@ -1,7 +1,10 @@ { "config": { "error": { - "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", + "ssh_not_file": "S\u00fabor s k\u013e\u00fa\u010dom SSH nebol n\u00e1jden\u00fd", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/atag/translations/sk.json b/homeassistant/components/atag/translations/sk.json index 92621fc08c4..14d096336a4 100644 --- a/homeassistant/components/atag/translations/sk.json +++ b/homeassistant/components/atag/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Zariadenie je u\u017e nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/august/translations/sk.json b/homeassistant/components/august/translations/sk.json index bbdf6a7d2e6..93a534f8fdd 100644 --- a/homeassistant/components/august/translations/sk.json +++ b/homeassistant/components/august/translations/sk.json @@ -1,10 +1,13 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_validate": { diff --git a/homeassistant/components/aurora/translations/sk.json b/homeassistant/components/aurora/translations/sk.json index 81532ef4801..ad49a4a2551 100644 --- a/homeassistant/components/aurora/translations/sk.json +++ b/homeassistant/components/aurora/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/aussie_broadband/translations/sk.json b/homeassistant/components/aussie_broadband/translations/sk.json index 6008bee5113..2aa7bdc0b18 100644 --- a/homeassistant/components/aussie_broadband/translations/sk.json +++ b/homeassistant/components/aussie_broadband/translations/sk.json @@ -1,10 +1,14 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "no_services_found": "Pre tento \u00fa\u010det sa nena\u0161li \u017eiadne slu\u017eby", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { @@ -14,6 +18,9 @@ "description": "Aktualizova\u0165 heslo pre {username}" }, "service": { + "data": { + "services": "Slu\u017eby" + }, "title": "Vyberte slu\u017eby" }, "user": { @@ -25,10 +32,15 @@ }, "options": { "abort": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "init": { + "data": { + "services": "Slu\u017eby" + }, "title": "Vyberte slu\u017eby" } } diff --git a/homeassistant/components/auth/translations/sk.json b/homeassistant/components/auth/translations/sk.json new file mode 100644 index 00000000000..1eccd378944 --- /dev/null +++ b/homeassistant/components/auth/translations/sk.json @@ -0,0 +1,20 @@ +{ + "mfa_setup": { + "notify": { + "abort": { + "no_available_service": "Nie s\u00fa k dispoz\u00edcii \u017eiadne notifika\u010dn\u00e9 slu\u017eby." + }, + "error": { + "invalid_code": "Neplatn\u00fd k\u00f3d, sk\u00faste to znova." + }, + "step": { + "init": { + "description": "Vyberte si jednu z notifika\u010dn\u00fdch slu\u017eieb:" + }, + "setup": { + "description": "Jednorazov\u00e9 heslo bolo odoslan\u00e9 prostredn\u00edctvom **notify.{notify_service}**. Zadajte ho, pros\u00edm, ni\u017e\u0161ie:" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/awair/translations/sk.json b/homeassistant/components/awair/translations/sk.json index d80daa08883..1ccc5d4eff0 100644 --- a/homeassistant/components/awair/translations/sk.json +++ b/homeassistant/components/awair/translations/sk.json @@ -1,9 +1,16 @@ { "config": { "abort": { + "already_configured_account": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "unreachable": "Nepodarilo sa pripoji\u0165" + }, + "error": { + "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", + "unreachable": "Nepodarilo sa pripoji\u0165" }, "flow_title": "{model} ({device_id})", "step": { diff --git a/homeassistant/components/axis/translations/sk.json b/homeassistant/components/axis/translations/sk.json index 085b5538318..4529c2fbe90 100644 --- a/homeassistant/components/axis/translations/sk.json +++ b/homeassistant/components/axis/translations/sk.json @@ -1,10 +1,13 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "link_local_address": "Lok\u00e1lne adresy odkazov nie s\u00fa podporovan\u00e9" }, "error": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "flow_title": "{name} ({host})", diff --git a/homeassistant/components/azure_devops/translations/sk.json b/homeassistant/components/azure_devops/translations/sk.json index 71a7aea5018..6fe0cf7d89a 100644 --- a/homeassistant/components/azure_devops/translations/sk.json +++ b/homeassistant/components/azure_devops/translations/sk.json @@ -1,10 +1,18 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "project_error": "Nepodarilo sa z\u00edska\u0165 inform\u00e1cie o projekte." + }, + "step": { + "reauth": { + "description": "Overenie toto\u017enosti pre {project_url} zlyhalo. Zadajte svoje aktu\u00e1lne poverenia." + } } } } \ No newline at end of file diff --git a/homeassistant/components/azure_event_hub/translations/sk.json b/homeassistant/components/azure_event_hub/translations/sk.json index 3f20d345b26..17ae6622292 100644 --- a/homeassistant/components/azure_event_hub/translations/sk.json +++ b/homeassistant/components/azure_event_hub/translations/sk.json @@ -1,6 +1,10 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" } } diff --git a/homeassistant/components/balboa/translations/sk.json b/homeassistant/components/balboa/translations/sk.json index 3c3d27a6689..bd2e41a926f 100644 --- a/homeassistant/components/balboa/translations/sk.json +++ b/homeassistant/components/balboa/translations/sk.json @@ -3,6 +3,10 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/binary_sensor/translations/sk.json b/homeassistant/components/binary_sensor/translations/sk.json index 018a7494aeb..bc4b05bdb73 100644 --- a/homeassistant/components/binary_sensor/translations/sk.json +++ b/homeassistant/components/binary_sensor/translations/sk.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_bat_low": "{entity_name} bat\u00e9ria je vybit\u00e1", "is_motion": "{entity_name} zaznamen\u00e1va pohyb", "is_moving": "{entity_name} sa h\u00fdbe", "is_no_gas": "{entity_name} nedetekuje plyn", @@ -11,6 +12,7 @@ "is_no_sound": "{entity_name} nerozpozn\u00e1va zvuk", "is_no_update": "{entity_name} je aktu\u00e1lne", "is_no_vibration": "{entity_name} nezaznamen\u00e1va vibr\u00e1cie", + "is_not_bat_low": "{entity_name} bat\u00e9ria je norm\u00e1lna", "is_not_cold": "{entity_name} nie je studen\u00e9", "is_not_connected": "{entity_name} je odpojen\u00fd", "is_not_hot": "{entity_name} nie je hor\u00face", @@ -19,7 +21,9 @@ "is_not_moving": "{entity_name} sa neh\u00fdbe" }, "trigger_type": { + "bat_low": "{entity_name} bat\u00e9ria vybit\u00e1", "connected": "{entity_name} je pripojen\u00fd", + "not_bat_low": "{entity_name} bat\u00e9ria norm\u00e1lna", "not_plugged_in": "{entity_name} odpojen\u00fd", "not_powered": "{entity_name} nie je nap\u00e1jan\u00e9" } diff --git a/homeassistant/components/blebox/translations/sk.json b/homeassistant/components/blebox/translations/sk.json index da190d975bf..fa1e3a90bbd 100644 --- a/homeassistant/components/blebox/translations/sk.json +++ b/homeassistant/components/blebox/translations/sk.json @@ -1,8 +1,14 @@ { "config": { "abort": { + "address_already_configured": "Zariadenie BleBox je u\u017e nakonfigurovan\u00e9 na {address}.", "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", + "unsupported_version": "Zariadenie BleBox m\u00e1 zastaran\u00fd firmv\u00e9r. Najprv ho aktualizujte." + }, "flow_title": "{name} ({host})", "step": { "user": { diff --git a/homeassistant/components/blink/translations/sk.json b/homeassistant/components/blink/translations/sk.json index 8f06d99549b..3a039c48717 100644 --- a/homeassistant/components/blink/translations/sk.json +++ b/homeassistant/components/blink/translations/sk.json @@ -4,7 +4,10 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { @@ -13,5 +16,14 @@ } } } + }, + "options": { + "step": { + "simple_options": { + "data": { + "scan_interval": "Interval skenovania (sekundy)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/bluemaestro/translations/sk.json b/homeassistant/components/bluemaestro/translations/sk.json index 25ca828afe4..48a6116c8ee 100644 --- a/homeassistant/components/bluemaestro/translations/sk.json +++ b/homeassistant/components/bluemaestro/translations/sk.json @@ -2,8 +2,16 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "not_supported": "Zariadenie nie je podporovan\u00e9" + }, + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/bluetooth/translations/sk.json b/homeassistant/components/bluetooth/translations/sk.json index 1d375717246..df959bc0275 100644 --- a/homeassistant/components/bluetooth/translations/sk.json +++ b/homeassistant/components/bluetooth/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, "flow_title": "{name}", "step": { "bluetooth_confirm": { diff --git a/homeassistant/components/bmw_connected_drive/translations/sk.json b/homeassistant/components/bmw_connected_drive/translations/sk.json index 1b1e671c054..793ea2bdcf1 100644 --- a/homeassistant/components/bmw_connected_drive/translations/sk.json +++ b/homeassistant/components/bmw_connected_drive/translations/sk.json @@ -1,6 +1,10 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { @@ -10,5 +14,14 @@ } } } + }, + "options": { + "step": { + "account_options": { + "data": { + "read_only": "Len na \u010d\u00edtanie (len senzory a notifik\u00e1cie, \u017eiadne vykon\u00e1vanie slu\u017eieb, \u017eiadny z\u00e1mok)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/bond/translations/sk.json b/homeassistant/components/bond/translations/sk.json index f98254c3683..1a56678f20d 100644 --- a/homeassistant/components/bond/translations/sk.json +++ b/homeassistant/components/bond/translations/sk.json @@ -1,7 +1,12 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({host})", "step": { diff --git a/homeassistant/components/broadlink/translations/sk.json b/homeassistant/components/broadlink/translations/sk.json index 2ea0040fd3a..8b133bf55f9 100644 --- a/homeassistant/components/broadlink/translations/sk.json +++ b/homeassistant/components/broadlink/translations/sk.json @@ -3,11 +3,15 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", - "not_supported": "Zariadenie nie je podporovan\u00e9" + "not_supported": "Zariadenie nie je podporovan\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { - "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({model} na {host})", "step": { diff --git a/homeassistant/components/brother/translations/sk.json b/homeassistant/components/brother/translations/sk.json index ce151b96477..58e173e521d 100644 --- a/homeassistant/components/brother/translations/sk.json +++ b/homeassistant/components/brother/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "wrong_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa." }, "flow_title": "{model} {serial_number}", diff --git a/homeassistant/components/brunt/translations/sk.json b/homeassistant/components/brunt/translations/sk.json index b86eef0e0df..506de390bc0 100644 --- a/homeassistant/components/brunt/translations/sk.json +++ b/homeassistant/components/brunt/translations/sk.json @@ -1,10 +1,13 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/bsblan/translations/sk.json b/homeassistant/components/bsblan/translations/sk.json index 5266d9beff4..eb382b0d15e 100644 --- a/homeassistant/components/bsblan/translations/sk.json +++ b/homeassistant/components/bsblan/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/bthome/translations/sk.json b/homeassistant/components/bthome/translations/sk.json index 8444b9fbeae..767536da844 100644 --- a/homeassistant/components/bthome/translations/sk.json +++ b/homeassistant/components/bthome/translations/sk.json @@ -2,7 +2,18 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "error": { + "expected_32_characters": "O\u010dak\u00e1van\u00fd 32-znakov\u00fd hexadecim\u00e1lny bindkey." + }, + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/buienradar/translations/sk.json b/homeassistant/components/buienradar/translations/sk.json index d77712e768a..825db0b86a7 100644 --- a/homeassistant/components/buienradar/translations/sk.json +++ b/homeassistant/components/buienradar/translations/sk.json @@ -1,5 +1,11 @@ { "config": { + "abort": { + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/canary/translations/sk.json b/homeassistant/components/canary/translations/sk.json index 6a802cc7398..afab4d9b25c 100644 --- a/homeassistant/components/canary/translations/sk.json +++ b/homeassistant/components/canary/translations/sk.json @@ -1,5 +1,11 @@ { "config": { + "abort": { + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/cast/translations/sk.json b/homeassistant/components/cast/translations/sk.json index f2782828716..69e7a4ea84b 100644 --- a/homeassistant/components/cast/translations/sk.json +++ b/homeassistant/components/cast/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "invalid_known_hosts": "Zn\u00e1mi hostitelia musia by\u0165 v zozname hostite\u013eov oddelen\u00fd \u010diarkami." + }, "step": { "config": { "data": { @@ -12,6 +15,9 @@ } }, "options": { + "error": { + "invalid_known_hosts": "Zn\u00e1mi hostitelia musia by\u0165 v zozname hostite\u013eov oddelen\u00fd \u010diarkami." + }, "step": { "basic_options": { "data": { diff --git a/homeassistant/components/cert_expiry/translations/sk.json b/homeassistant/components/cert_expiry/translations/sk.json index 33d93b62475..db1aa31eaa5 100644 --- a/homeassistant/components/cert_expiry/translations/sk.json +++ b/homeassistant/components/cert_expiry/translations/sk.json @@ -1,5 +1,13 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "connection_refused": "Pri prip\u00e1jan\u00ed k hostite\u013eovi bolo pripojenie odmietnut\u00e9", + "connection_timeout": "\u010casov\u00fd limit pri pripojen\u00ed k tomuto hostite\u013eovi", + "resolve_failed": "Tento hostite\u013e sa ned\u00e1 vyrie\u0161i\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/climacell/translations/en.json b/homeassistant/components/climacell/translations/en.json new file mode 100644 index 00000000000..a35be85d5b2 --- /dev/null +++ b/homeassistant/components/climacell/translations/en.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. Between NowCast Forecasts" + }, + "description": "If you choose to enable the `nowcast` forecast entity, you can configure the number of minutes between each forecast. The number of forecasts provided depends on the number of minutes chosen between forecasts.", + "title": "Update ClimaCell Options" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/es.json b/homeassistant/components/climacell/translations/es.json index 270d72bd58c..438007171f0 100644 --- a/homeassistant/components/climacell/translations/es.json +++ b/homeassistant/components/climacell/translations/es.json @@ -3,9 +3,9 @@ "step": { "init": { "data": { - "timestep": "Min. entre pron\u00f3sticos de NowCast" + "timestep": "Min. entre previsiones de NowCast" }, - "description": "Si eliges habilitar la entidad de pron\u00f3stico `nowcast`, puedes configurar la cantidad de minutos entre cada pron\u00f3stico. La cantidad de pron\u00f3sticos proporcionados depende de la cantidad de minutos elegidos entre los pron\u00f3sticos.", + "description": "Si eliges habilitar la entidad de previsi\u00f3n `nowcast`, puedes configurar la cantidad de minutos entre cada previsi\u00f3n. La cantidad de previsiones proporcionados depende de la cantidad de minutos elegidos entre las mismas.", "title": "Actualizar opciones de ClimaCell" } } diff --git a/homeassistant/components/climacell/translations/sensor.en.json b/homeassistant/components/climacell/translations/sensor.en.json new file mode 100644 index 00000000000..0cb1d27aaec --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.en.json @@ -0,0 +1,27 @@ +{ + "state": { + "climacell__health_concern": { + "good": "Good", + "hazardous": "Hazardous", + "moderate": "Moderate", + "unhealthy": "Unhealthy", + "unhealthy_for_sensitive_groups": "Unhealthy for Sensitive Groups", + "very_unhealthy": "Very Unhealthy" + }, + "climacell__pollen_index": { + "high": "High", + "low": "Low", + "medium": "Medium", + "none": "None", + "very_high": "Very High", + "very_low": "Very Low" + }, + "climacell__precipitation_type": { + "freezing_rain": "Freezing Rain", + "ice_pellets": "Ice Pellets", + "none": "None", + "rain": "Rain", + "snow": "Snow" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climate/translations/sk.json b/homeassistant/components/climate/translations/sk.json index 15536f9b879..0b964a90799 100644 --- a/homeassistant/components/climate/translations/sk.json +++ b/homeassistant/components/climate/translations/sk.json @@ -1,4 +1,10 @@ { + "device_automation": { + "trigger_type": { + "current_humidity_changed": "{entity_name} sa zmenila nameran\u00e1 vlhkos\u0165", + "current_temperature_changed": "{entity_name} sa zmenila nameran\u00e1 teplota" + } + }, "state": { "_": { "auto": "Automatika", diff --git a/homeassistant/components/coinbase/translations/sk.json b/homeassistant/components/coinbase/translations/sk.json index f2ffd76bc74..ec8f33d2d7b 100644 --- a/homeassistant/components/coinbase/translations/sk.json +++ b/homeassistant/components/coinbase/translations/sk.json @@ -4,7 +4,10 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_auth_key": "Poverenia API zamietnut\u00e9 spolo\u010dnos\u0165ou Coinbase z d\u00f4vodu neplatn\u00e9ho k\u013e\u00fa\u010da API.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { @@ -16,7 +19,8 @@ }, "options": { "error": { - "currency_unavailable": "Jeden alebo viacero po\u017eadovan\u00fdch zostatkov mien nie s\u00fa poskytovan\u00e9 Va\u0161\u00edm Coinbase API." + "currency_unavailable": "Jeden alebo viacero po\u017eadovan\u00fdch zostatkov mien nie s\u00fa poskytovan\u00e9 Va\u0161\u00edm Coinbase API.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" } } } \ No newline at end of file diff --git a/homeassistant/components/control4/translations/sk.json b/homeassistant/components/control4/translations/sk.json index f6b92d16c96..b011fc12c90 100644 --- a/homeassistant/components/control4/translations/sk.json +++ b/homeassistant/components/control4/translations/sk.json @@ -4,14 +4,17 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { "host": "IP adresa", "password": "Heslo" - } + }, + "description": "Zadajte \u00fadaje o svojom \u00fa\u010dte Control4 a IP adresu miestneho kontrol\u00e9ra." } } }, diff --git a/homeassistant/components/coolmaster/translations/sk.json b/homeassistant/components/coolmaster/translations/sk.json index 842ff61bd79..bf16a99d5e1 100644 --- a/homeassistant/components/coolmaster/translations/sk.json +++ b/homeassistant/components/coolmaster/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/coronavirus/translations/sk.json b/homeassistant/components/coronavirus/translations/sk.json new file mode 100644 index 00000000000..fe468d25520 --- /dev/null +++ b/homeassistant/components/coronavirus/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cover/translations/sk.json b/homeassistant/components/cover/translations/sk.json index 8962c6b8db3..335ab691980 100644 --- a/homeassistant/components/cover/translations/sk.json +++ b/homeassistant/components/cover/translations/sk.json @@ -11,7 +11,8 @@ "is_closing": "{entity_name} sa zatv\u00e1ra", "is_open": "{entity_name} je otvoren\u00e1", "is_opening": "{entity_name} sa otv\u00e1ra", - "is_position": "Aktu\u00e1lna poz\u00edcia {entity_name} je" + "is_position": "Aktu\u00e1lna poz\u00edcia {entity_name} je", + "is_tilt_position": "Aktu\u00e1lna poloha naklonenia {entity_name} je" }, "trigger_type": { "closed": "{entity_name} zatvoren\u00e9", diff --git a/homeassistant/components/cpuspeed/translations/sk.json b/homeassistant/components/cpuspeed/translations/sk.json new file mode 100644 index 00000000000..89f5b8fb3b9 --- /dev/null +++ b/homeassistant/components/cpuspeed/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/crownstone/translations/sk.json b/homeassistant/components/crownstone/translations/sk.json index 87352b48fad..895bdde28cd 100644 --- a/homeassistant/components/crownstone/translations/sk.json +++ b/homeassistant/components/crownstone/translations/sk.json @@ -1,7 +1,11 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/daikin/translations/sk.json b/homeassistant/components/daikin/translations/sk.json index 17cde55b9f2..191d0ea32f4 100644 --- a/homeassistant/components/daikin/translations/sk.json +++ b/homeassistant/components/daikin/translations/sk.json @@ -1,8 +1,14 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "error": { "api_password": "Neplatn\u00e9 overenie, pou\u017eite bu\u010f API k\u013e\u00fa\u010d alebo heslo.", - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/deconz/translations/sk.json b/homeassistant/components/deconz/translations/sk.json index 8fb170e8bc7..26fcc552124 100644 --- a/homeassistant/components/deconz/translations/sk.json +++ b/homeassistant/components/deconz/translations/sk.json @@ -1,8 +1,12 @@ { "config": { "abort": { + "already_configured": "Bridge je u\u017e nakonfigurovan\u00fd", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "error": { + "no_key": "Nepodarilo sa z\u00edska\u0165 k\u013e\u00fa\u010d API" + }, "flow_title": "{host}", "step": { "manual_input": { @@ -15,6 +19,7 @@ }, "device_automation": { "trigger_subtype": { + "button_2": "Druh\u00e9 tla\u010didlo", "close": "Zavrie\u0165", "dim_down": "Stlmi\u0165", "dim_up": "Zv\u00fd\u0161i\u0165", diff --git a/homeassistant/components/deluge/translations/sk.json b/homeassistant/components/deluge/translations/sk.json index bc2d39d1a3c..d2976f03929 100644 --- a/homeassistant/components/deluge/translations/sk.json +++ b/homeassistant/components/deluge/translations/sk.json @@ -1,12 +1,20 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie" + }, "step": { "user": { "data": { "host": "Hostite\u013e", "password": "Heslo", "port": "Port", - "username": "U\u017e\u00edvate\u013esk\u00e9 meno" + "username": "U\u017e\u00edvate\u013esk\u00e9 meno", + "web_port": "Webov\u00fd port (pre n\u00e1v\u0161tevu slu\u017eby)" } } } diff --git a/homeassistant/components/demo/translations/de.json b/homeassistant/components/demo/translations/de.json index 8f5950f49f8..6848443e0b3 100644 --- a/homeassistant/components/demo/translations/de.json +++ b/homeassistant/components/demo/translations/de.json @@ -11,6 +11,14 @@ }, "title": "Das Netzteil ist nicht stabil" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "Der Tee kann zu diesem Zeitpunkt nicht wieder aufgew\u00e4rmt werden." + } + }, + "title": "Der Tee ist kalt" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/demo/translations/el.json b/homeassistant/components/demo/translations/el.json index ea1787ab8f0..cb47f52ed42 100644 --- a/homeassistant/components/demo/translations/el.json +++ b/homeassistant/components/demo/translations/el.json @@ -11,6 +11,14 @@ }, "title": "\u03a4\u03bf \u03c4\u03c1\u03bf\u03c6\u03bf\u03b4\u03bf\u03c4\u03b9\u03ba\u03cc \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03cc" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "\u0394\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03be\u03b1\u03bd\u03b1\u03b8\u03b5\u03c1\u03bc\u03ac\u03bd\u03b5\u03c4\u03b5 \u03c4\u03bf \u03c4\u03c3\u03ac\u03b9 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7 \u03c3\u03c4\u03b9\u03b3\u03bc\u03ae" + } + }, + "title": "\u03a4\u03bf \u03c4\u03c3\u03ac\u03b9 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ba\u03c1\u03cd\u03bf" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/demo/translations/en.json b/homeassistant/components/demo/translations/en.json index 150ee6c8830..9f32e982947 100644 --- a/homeassistant/components/demo/translations/en.json +++ b/homeassistant/components/demo/translations/en.json @@ -15,8 +15,7 @@ "fix_flow": { "abort": { "not_tea_time": "Can not re-heat the tea at this time" - }, - "step": {} + } }, "title": "The tea is cold" }, @@ -42,9 +41,6 @@ }, "options": { "step": { - "init": { - "data": {} - }, "options_1": { "data": { "bool": "Optional boolean", diff --git a/homeassistant/components/demo/translations/es.json b/homeassistant/components/demo/translations/es.json index 3fd439a1b05..70fc94480a7 100644 --- a/homeassistant/components/demo/translations/es.json +++ b/homeassistant/components/demo/translations/es.json @@ -11,6 +11,14 @@ }, "title": "La fuente de alimentaci\u00f3n no es estable." }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "No se puede volver a calentar el t\u00e9 en este momento" + } + }, + "title": "El t\u00e9 est\u00e1 fr\u00edo" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/demo/translations/id.json b/homeassistant/components/demo/translations/id.json index 7acf127caa8..08594a7353b 100644 --- a/homeassistant/components/demo/translations/id.json +++ b/homeassistant/components/demo/translations/id.json @@ -11,6 +11,14 @@ }, "title": "Catu daya tidak stabil" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "Tidak bisa memanaskan kembali teh saat ini" + } + }, + "title": "Tehnya dingin" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/demo/translations/it.json b/homeassistant/components/demo/translations/it.json index e8265baebf7..df45eb12982 100644 --- a/homeassistant/components/demo/translations/it.json +++ b/homeassistant/components/demo/translations/it.json @@ -11,6 +11,14 @@ }, "title": "L'alimentazione non \u00e8 stabile" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "Non \u00e8 possibile riscaldare nuovamente il t\u00e8 in questo momento" + } + }, + "title": "Il t\u00e8 \u00e8 freddo" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/demo/translations/zh-Hant.json b/homeassistant/components/demo/translations/zh-Hant.json index 60f465316a0..0018f67e065 100644 --- a/homeassistant/components/demo/translations/zh-Hant.json +++ b/homeassistant/components/demo/translations/zh-Hant.json @@ -11,6 +11,14 @@ }, "title": "\u96fb\u6e90\u4f9b\u61c9\u4e0d\u7a69\u5b9a" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "\u76ee\u524d\u7121\u6cd5\u518d\u52a0\u71b1\u8336" + } + }, + "title": "\u8336\u6dbc\u4e86" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/denonavr/translations/sk.json b/homeassistant/components/denonavr/translations/sk.json index 0136b344d18..d1959ed36e3 100644 --- a/homeassistant/components/denonavr/translations/sk.json +++ b/homeassistant/components/denonavr/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "flow_title": "{name}", @@ -13,6 +14,9 @@ "user": { "data": { "host": "IP adresa" + }, + "data_description": { + "host": "Ak chcete pou\u017ei\u0165 automatick\u00e9 zis\u0165ovanie, nechajte pole pr\u00e1zdne" } } } diff --git a/homeassistant/components/devolo_home_control/translations/sk.json b/homeassistant/components/devolo_home_control/translations/sk.json index 001d94eb0a8..04647f3c4fd 100644 --- a/homeassistant/components/devolo_home_control/translations/sk.json +++ b/homeassistant/components/devolo_home_control/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/devolo_home_network/translations/sk.json b/homeassistant/components/devolo_home_network/translations/sk.json index 16c033491b2..ff279f8d0e4 100644 --- a/homeassistant/components/devolo_home_network/translations/sk.json +++ b/homeassistant/components/devolo_home_network/translations/sk.json @@ -3,12 +3,21 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "flow_title": "{product} ({name})", "step": { "reauth_confirm": { "data": { "password": "Heslo" } + }, + "user": { + "data": { + "ip_address": "IP adresa" + } } } } diff --git a/homeassistant/components/dexcom/translations/sk.json b/homeassistant/components/dexcom/translations/sk.json index 5ac7d0818ba..17717e7c7fb 100644 --- a/homeassistant/components/dexcom/translations/sk.json +++ b/homeassistant/components/dexcom/translations/sk.json @@ -1,7 +1,12 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/directv/translations/sk.json b/homeassistant/components/directv/translations/sk.json index 59a11c7e4e2..22f56dce8ab 100644 --- a/homeassistant/components/directv/translations/sk.json +++ b/homeassistant/components/directv/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/discord/translations/sk.json b/homeassistant/components/discord/translations/sk.json new file mode 100644 index 00000000000..f6e3414b2b0 --- /dev/null +++ b/homeassistant/components/discord/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dlna_dmr/translations/sk.json b/homeassistant/components/dlna_dmr/translations/sk.json index 8ac7aa5d949..ea6244cb478 100644 --- a/homeassistant/components/dlna_dmr/translations/sk.json +++ b/homeassistant/components/dlna_dmr/translations/sk.json @@ -3,8 +3,12 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "alternative_integration": "Zariadenie je lep\u0161ie podporovan\u00e9 inou integr\u00e1ciou", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "non_unique_id": "Viacer\u00e9 zariadenia n\u00e1jden\u00e9 s rovnak\u00fdm jedine\u010dn\u00fdm identifik\u00e1torom" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/dlna_dms/translations/sk.json b/homeassistant/components/dlna_dms/translations/sk.json index 8715062cf81..64d81e7c78b 100644 --- a/homeassistant/components/dlna_dms/translations/sk.json +++ b/homeassistant/components/dlna_dms/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, "flow_title": "{name}", diff --git a/homeassistant/components/dnsip/translations/sk.json b/homeassistant/components/dnsip/translations/sk.json index 69a87c21915..06557ec1e79 100644 --- a/homeassistant/components/dnsip/translations/sk.json +++ b/homeassistant/components/dnsip/translations/sk.json @@ -3,5 +3,10 @@ "error": { "invalid_hostname": "Neplatn\u00fd n\u00e1zov hostite\u013ea" } + }, + "options": { + "error": { + "invalid_resolver": "Neplatn\u00e1 adresa IP pre resolver" + } } } \ No newline at end of file diff --git a/homeassistant/components/doorbird/translations/sk.json b/homeassistant/components/doorbird/translations/sk.json index 4a4df7037bc..ce36e211841 100644 --- a/homeassistant/components/doorbird/translations/sk.json +++ b/homeassistant/components/doorbird/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "link_local_address": "Lok\u00e1lne adresy odkazov nie s\u00fa podporovan\u00e9" }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" diff --git a/homeassistant/components/dsmr/translations/sk.json b/homeassistant/components/dsmr/translations/sk.json index b43117f7056..266b3c98961 100644 --- a/homeassistant/components/dsmr/translations/sk.json +++ b/homeassistant/components/dsmr/translations/sk.json @@ -11,7 +11,8 @@ "data": { "host": "Hostite\u013e", "port": "Port" - } + }, + "title": "Vyberte adresu pripojenia" }, "setup_serial": { "data": { diff --git a/homeassistant/components/ebusd/translations/sk.json b/homeassistant/components/ebusd/translations/sk.json new file mode 100644 index 00000000000..674290ece76 --- /dev/null +++ b/homeassistant/components/ebusd/translations/sk.json @@ -0,0 +1,5 @@ +{ + "state": { + "day": "De\u0148" + } +} \ No newline at end of file diff --git a/homeassistant/components/econet/translations/sk.json b/homeassistant/components/econet/translations/sk.json index f4894a1aed6..6aa492da657 100644 --- a/homeassistant/components/econet/translations/sk.json +++ b/homeassistant/components/econet/translations/sk.json @@ -5,6 +5,7 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { diff --git a/homeassistant/components/efergy/translations/sk.json b/homeassistant/components/efergy/translations/sk.json index 890673b969f..8aa53bb641e 100644 --- a/homeassistant/components/efergy/translations/sk.json +++ b/homeassistant/components/efergy/translations/sk.json @@ -5,7 +5,9 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/elgato/translations/sk.json b/homeassistant/components/elgato/translations/sk.json index 142f47cf1fd..9276d3ef9be 100644 --- a/homeassistant/components/elgato/translations/sk.json +++ b/homeassistant/components/elgato/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { "user": { diff --git a/homeassistant/components/elkm1/translations/sk.json b/homeassistant/components/elkm1/translations/sk.json index 6b5aee05996..08b703bf56b 100644 --- a/homeassistant/components/elkm1/translations/sk.json +++ b/homeassistant/components/elkm1/translations/sk.json @@ -1,20 +1,27 @@ { "config": { "abort": { - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{mac_address} ({host})", "step": { "discovered_connection": { "data": { "password": "Heslo" - } + }, + "description": "Pripojte sa k objaven\u00e9mu syst\u00e9mu: {mac_address} ({host})" }, "manual_connection": { "data": { + "address": "IP adresa alebo dom\u00e9na alebo s\u00e9riov\u00fd port, ak sa prip\u00e1jate cez s\u00e9riov\u00fd port.", "password": "Heslo" } }, diff --git a/homeassistant/components/elmax/translations/sk.json b/homeassistant/components/elmax/translations/sk.json index 8f06d99549b..80f2ebf02a3 100644 --- a/homeassistant/components/elmax/translations/sk.json +++ b/homeassistant/components/elmax/translations/sk.json @@ -4,7 +4,10 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_pin": "Poskytnut\u00fd k\u00f3d PIN je neplatn\u00fd", + "network_error": "Vyskytla sa chyba siete", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/emonitor/translations/sk.json b/homeassistant/components/emonitor/translations/sk.json index 2f510ec91da..323aa28cafb 100644 --- a/homeassistant/components/emonitor/translations/sk.json +++ b/homeassistant/components/emonitor/translations/sk.json @@ -1,5 +1,12 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "flow_title": "{name}", "step": { "confirm": { diff --git a/homeassistant/components/emulated_roku/translations/sk.json b/homeassistant/components/emulated_roku/translations/sk.json index 26dc85eff39..90ef443df16 100644 --- a/homeassistant/components/emulated_roku/translations/sk.json +++ b/homeassistant/components/emulated_roku/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/enocean/translations/sk.json b/homeassistant/components/enocean/translations/sk.json new file mode 100644 index 00000000000..96528a925df --- /dev/null +++ b/homeassistant/components/enocean/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "invalid_dongle_path": "Pre t\u00fato cestu nebol n\u00e1jden\u00fd \u017eiadny platn\u00fd k\u013e\u00fa\u010d" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/enphase_envoy/translations/sk.json b/homeassistant/components/enphase_envoy/translations/sk.json index 2c5e58fb5ea..9d7ff8d00ac 100644 --- a/homeassistant/components/enphase_envoy/translations/sk.json +++ b/homeassistant/components/enphase_envoy/translations/sk.json @@ -1,10 +1,13 @@ { "config": { "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{serial} ( {host} )", "step": { diff --git a/homeassistant/components/environment_canada/translations/sk.json b/homeassistant/components/environment_canada/translations/sk.json index e6945904d90..b87d36cc6e0 100644 --- a/homeassistant/components/environment_canada/translations/sk.json +++ b/homeassistant/components/environment_canada/translations/sk.json @@ -1,5 +1,10 @@ { "config": { + "error": { + "bad_station_id": "ID stanice je neplatn\u00e9, ch\u00fdba alebo sa nenach\u00e1dza v datab\u00e1ze ID stanice", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/epson/translations/sk.json b/homeassistant/components/epson/translations/sk.json index 965b3bb8919..1820d9d8d42 100644 --- a/homeassistant/components/epson/translations/sk.json +++ b/homeassistant/components/epson/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/evil_genius_labs/translations/sk.json b/homeassistant/components/evil_genius_labs/translations/sk.json index 842ff61bd79..4456ea9c0fc 100644 --- a/homeassistant/components/evil_genius_labs/translations/sk.json +++ b/homeassistant/components/evil_genius_labs/translations/sk.json @@ -1,5 +1,9 @@ { "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/ezviz/translations/sk.json b/homeassistant/components/ezviz/translations/sk.json index d10b6dbf6be..75a6a00160d 100644 --- a/homeassistant/components/ezviz/translations/sk.json +++ b/homeassistant/components/ezviz/translations/sk.json @@ -1,6 +1,11 @@ { "config": { + "abort": { + "already_configured_account": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, diff --git a/homeassistant/components/faa_delays/translations/sk.json b/homeassistant/components/faa_delays/translations/sk.json index bbadecd391b..bbea900554a 100644 --- a/homeassistant/components/faa_delays/translations/sk.json +++ b/homeassistant/components/faa_delays/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "error": { - "invalid_airport": "K\u00f3d letiska je neplatn\u00fd" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_airport": "K\u00f3d letiska je neplatn\u00fd", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" } } } \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/sk.json b/homeassistant/components/fibaro/translations/sk.json index 1a831c812f7..03982dd960c 100644 --- a/homeassistant/components/fibaro/translations/sk.json +++ b/homeassistant/components/fibaro/translations/sk.json @@ -3,6 +3,11 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "reauth_confirm": { "data": { @@ -12,6 +17,7 @@ "user": { "data": { "password": "Heslo", + "url": "URL vo form\u00e1te http://HOST/api/", "username": "U\u017e\u00edvate\u013esk\u00e9 meno" } } diff --git a/homeassistant/components/filesize/translations/sk.json b/homeassistant/components/filesize/translations/sk.json new file mode 100644 index 00000000000..f11a9743f59 --- /dev/null +++ b/homeassistant/components/filesize/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "not_allowed": "Cesta nie je povolen\u00e1", + "not_valid": "Cesta nie je platn\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fireservicerota/translations/sk.json b/homeassistant/components/fireservicerota/translations/sk.json index 40fc5cd34be..3ffaaefa060 100644 --- a/homeassistant/components/fireservicerota/translations/sk.json +++ b/homeassistant/components/fireservicerota/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "create_entry": { @@ -13,11 +14,14 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Autentifika\u010dn\u00e9 tokeny sa stali neplatn\u00fdmi, prihl\u00e1ste sa a vytvorte ich znova." }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "url": "Webov\u00e1 lokalita", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/firmata/translations/sk.json b/homeassistant/components/firmata/translations/sk.json new file mode 100644 index 00000000000..641bd9c13ee --- /dev/null +++ b/homeassistant/components/firmata/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fivem/translations/sk.json b/homeassistant/components/fivem/translations/sk.json index 1c96c63ac91..f9219d753f0 100644 --- a/homeassistant/components/fivem/translations/sk.json +++ b/homeassistant/components/fivem/translations/sk.json @@ -1,5 +1,11 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "unknown_error": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/flick_electric/translations/sk.json b/homeassistant/components/flick_electric/translations/sk.json index 1b1e671c054..2139002b30d 100644 --- a/homeassistant/components/flick_electric/translations/sk.json +++ b/homeassistant/components/flick_electric/translations/sk.json @@ -1,7 +1,12 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/flipr/translations/sk.json b/homeassistant/components/flipr/translations/sk.json index bfca61034ec..ab95d8060d4 100644 --- a/homeassistant/components/flipr/translations/sk.json +++ b/homeassistant/components/flipr/translations/sk.json @@ -4,7 +4,9 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/flo/translations/sk.json b/homeassistant/components/flo/translations/sk.json index 254174d26ab..2ee96a72a30 100644 --- a/homeassistant/components/flo/translations/sk.json +++ b/homeassistant/components/flo/translations/sk.json @@ -4,7 +4,9 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/flume/translations/sk.json b/homeassistant/components/flume/translations/sk.json index bd3a9e24cf1..7ec2e77007d 100644 --- a/homeassistant/components/flume/translations/sk.json +++ b/homeassistant/components/flume/translations/sk.json @@ -1,10 +1,13 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/forked_daapd/translations/sk.json b/homeassistant/components/forked_daapd/translations/sk.json index ac802e99cb9..ed242c407e0 100644 --- a/homeassistant/components/forked_daapd/translations/sk.json +++ b/homeassistant/components/forked_daapd/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "wrong_host_or_port": "Ned\u00e1 sa pripoji\u0165. Skontrolujte hostite\u013ea a port.", "wrong_password": "Nespr\u00e1vne heslo." }, "flow_title": "{name} ({host})", diff --git a/homeassistant/components/foscam/translations/sk.json b/homeassistant/components/foscam/translations/sk.json index e97579ab1d0..0a9521e9c76 100644 --- a/homeassistant/components/foscam/translations/sk.json +++ b/homeassistant/components/foscam/translations/sk.json @@ -4,8 +4,10 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", - "invalid_response": "Neplatn\u00e1 odpove\u010f zo zariadenia" + "invalid_response": "Neplatn\u00e1 odpove\u010f zo zariadenia", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/freebox/translations/sk.json b/homeassistant/components/freebox/translations/sk.json index 142f47cf1fd..41da4a04be1 100644 --- a/homeassistant/components/freebox/translations/sk.json +++ b/homeassistant/components/freebox/translations/sk.json @@ -3,6 +3,11 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "register_failed": "Registr\u00e1cia zlyhala, sk\u00faste to znova", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/freedompro/translations/sk.json b/homeassistant/components/freedompro/translations/sk.json index f5f4789c7fe..52746415ddf 100644 --- a/homeassistant/components/freedompro/translations/sk.json +++ b/homeassistant/components/freedompro/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { diff --git a/homeassistant/components/fritz/translations/sk.json b/homeassistant/components/fritz/translations/sk.json index 75d1a5bf763..19cd874e55d 100644 --- a/homeassistant/components/fritz/translations/sk.json +++ b/homeassistant/components/fritz/translations/sk.json @@ -3,12 +3,15 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "ignore_ip6_link_local": "Lok\u00e1lna adresa odkazu IPv6 nie je podporovan\u00e1.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "upnp_not_configured": "Ch\u00fdbaj\u00face nastavenia UPnP v zariaden\u00ed." }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/fritzbox/translations/sk.json b/homeassistant/components/fritzbox/translations/sk.json index bc1a0e4b469..54b2a464a3d 100644 --- a/homeassistant/components/fritzbox/translations/sk.json +++ b/homeassistant/components/fritzbox/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "ignore_ip6_link_local": "Lok\u00e1lna adresa odkazu IPv6 nie je podporovan\u00e1.", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, diff --git a/homeassistant/components/fronius/translations/sk.json b/homeassistant/components/fronius/translations/sk.json index 4a0d1d21e7d..7ff40710d48 100644 --- a/homeassistant/components/fronius/translations/sk.json +++ b/homeassistant/components/fronius/translations/sk.json @@ -4,6 +4,10 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "flow_title": "{device}", "step": { "confirm_discovery": { @@ -12,7 +16,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Nakonfigurujte IP adresu alebo miestny n\u00e1zov hostite\u013ea v\u00e1\u0161ho zariadenia Fronius." } } } diff --git a/homeassistant/components/fully_kiosk/translations/sk.json b/homeassistant/components/fully_kiosk/translations/sk.json index 9efe27e4e38..74dc2c0fc8e 100644 --- a/homeassistant/components/fully_kiosk/translations/sk.json +++ b/homeassistant/components/fully_kiosk/translations/sk.json @@ -1,5 +1,12 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/garages_amsterdam/translations/sk.json b/homeassistant/components/garages_amsterdam/translations/sk.json index 793f8eff278..040687a74a4 100644 --- a/homeassistant/components/garages_amsterdam/translations/sk.json +++ b/homeassistant/components/garages_amsterdam/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" } } } \ No newline at end of file diff --git a/homeassistant/components/gdacs/translations/sk.json b/homeassistant/components/gdacs/translations/sk.json new file mode 100644 index 00000000000..f04d4a327f4 --- /dev/null +++ b/homeassistant/components/gdacs/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/sk.json b/homeassistant/components/generic/translations/sk.json index 9ff8487afaa..13913c503ee 100644 --- a/homeassistant/components/generic/translations/sk.json +++ b/homeassistant/components/generic/translations/sk.json @@ -1,5 +1,13 @@ { "config": { + "error": { + "already_exists": "Kamera s t\u00fdmito nastaveniami URL u\u017e existuje.", + "invalid_still_image": "Adresa URL nevr\u00e1tila platn\u00fd statick\u00fd obr\u00e1zok", + "relative_url": "Relat\u00edvne adresy URL nie s\u00fa povolen\u00e9", + "stream_no_route_to_host": "Pri pokuse o pripojenie k streamu sa nepodarilo n\u00e1js\u0165 hostite\u013ea", + "timeout": "\u010casov\u00fd limit pri na\u010d\u00edtan\u00ed adresy URL", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { @@ -9,6 +17,13 @@ } }, "options": { + "error": { + "already_exists": "Kamera s t\u00fdmito nastaveniami URL u\u017e existuje.", + "invalid_still_image": "Adresa URL nevr\u00e1tila platn\u00fd statick\u00fd obr\u00e1zok", + "stream_no_route_to_host": "Pri pokuse o pripojenie k streamu sa nepodarilo n\u00e1js\u0165 hostite\u013ea", + "timeout": "\u010casov\u00fd limit pri na\u010d\u00edtan\u00ed adresy URL", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/geocaching/translations/sk.json b/homeassistant/components/geocaching/translations/sk.json new file mode 100644 index 00000000000..c3bed1eca3b --- /dev/null +++ b/homeassistant/components/geocaching/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geonetnz_quakes/translations/sk.json b/homeassistant/components/geonetnz_quakes/translations/sk.json new file mode 100644 index 00000000000..f04d4a327f4 --- /dev/null +++ b/homeassistant/components/geonetnz_quakes/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geonetnz_volcano/translations/sk.json b/homeassistant/components/geonetnz_volcano/translations/sk.json new file mode 100644 index 00000000000..529d2911584 --- /dev/null +++ b/homeassistant/components/geonetnz_volcano/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/gios/translations/sk.json b/homeassistant/components/gios/translations/sk.json index af15f92c2f2..cae59423a4a 100644 --- a/homeassistant/components/gios/translations/sk.json +++ b/homeassistant/components/gios/translations/sk.json @@ -1,5 +1,11 @@ { "config": { + "abort": { + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/glances/translations/sk.json b/homeassistant/components/glances/translations/sk.json index 1742fc019cc..28c47d663dc 100644 --- a/homeassistant/components/glances/translations/sk.json +++ b/homeassistant/components/glances/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/goalzero/translations/sk.json b/homeassistant/components/goalzero/translations/sk.json index 27a7d78c377..39b5e1560b2 100644 --- a/homeassistant/components/goalzero/translations/sk.json +++ b/homeassistant/components/goalzero/translations/sk.json @@ -2,10 +2,13 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { - "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/gogogate2/translations/sk.json b/homeassistant/components/gogogate2/translations/sk.json index 9a6b8022c06..f38a3717bd6 100644 --- a/homeassistant/components/gogogate2/translations/sk.json +++ b/homeassistant/components/gogogate2/translations/sk.json @@ -1,12 +1,17 @@ { "config": { + "abort": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "flow_title": "{device} ({ip_address})", "step": { "user": { "data": { + "ip_address": "IP adresa", "password": "Heslo" } } diff --git a/homeassistant/components/goodwe/translations/sk.json b/homeassistant/components/goodwe/translations/sk.json index ca49bb2fe7b..74df23fc689 100644 --- a/homeassistant/components/goodwe/translations/sk.json +++ b/homeassistant/components/goodwe/translations/sk.json @@ -4,6 +4,9 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "error": { + "connection_error": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/google/translations/sk.json b/homeassistant/components/google/translations/sk.json index c0763b92aef..d0bcbcd88ff 100644 --- a/homeassistant/components/google/translations/sk.json +++ b/homeassistant/components/google/translations/sk.json @@ -1,4 +1,13 @@ { + "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", + "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu." + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/google_sheets/translations/sk.json b/homeassistant/components/google_sheets/translations/sk.json new file mode 100644 index 00000000000..7cba9218abc --- /dev/null +++ b/homeassistant/components/google_sheets/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", + "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_travel_time/translations/sk.json b/homeassistant/components/google_travel_time/translations/sk.json index 52d93a1a18e..4a0ddda2566 100644 --- a/homeassistant/components/google_travel_time/translations/sk.json +++ b/homeassistant/components/google_travel_time/translations/sk.json @@ -3,6 +3,10 @@ "abort": { "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/govee_ble/translations/sk.json b/homeassistant/components/govee_ble/translations/sk.json index 8444b9fbeae..a8fa63be59a 100644 --- a/homeassistant/components/govee_ble/translations/sk.json +++ b/homeassistant/components/govee_ble/translations/sk.json @@ -2,7 +2,15 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/sk.json b/homeassistant/components/guardian/translations/sk.json index 99d87189f7b..1e35b169d86 100644 --- a/homeassistant/components/guardian/translations/sk.json +++ b/homeassistant/components/guardian/translations/sk.json @@ -2,14 +2,29 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { "user": { "data": { + "ip_address": "IP adresa", "port": "Port" } } } + }, + "issues": { + "deprecated_service": { + "fix_flow": { + "step": { + "confirm": { + "description": "Aktualizujte v\u0161etky automatiz\u00e1cie alebo skripty, ktor\u00e9 pou\u017e\u00edvaj\u00fa t\u00fato slu\u017ebu, aby namiesto nej pou\u017e\u00edvali slu\u017ebu `{alternate_service}` s ID cie\u013eovej entity `{alternate_target}`.", + "title": "Slu\u017eba {deprecated_service} bude odstr\u00e1nen\u00e1" + } + } + }, + "title": "Slu\u017eba {deprecated_service} bude odstr\u00e1nen\u00e1" + } } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/sk.json b/homeassistant/components/habitica/translations/sk.json index bcfc3880d99..fbfa0d6ad80 100644 --- a/homeassistant/components/habitica/translations/sk.json +++ b/homeassistant/components/habitica/translations/sk.json @@ -1,12 +1,14 @@ { "config": { "error": { - "invalid_credentials": "Neplatn\u00e9 overenie" + "invalid_credentials": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" + "api_key": "API k\u013e\u00fa\u010d", + "name": "Prep\u00edsa\u0165 pou\u017e\u00edvate\u013esk\u00e9 meno Habitica. Bude sa pou\u017e\u00edva\u0165 na servisn\u00e9 hovory" } } } diff --git a/homeassistant/components/harmony/translations/select.de.json b/homeassistant/components/harmony/translations/select.de.json new file mode 100644 index 00000000000..4cb96d0a72c --- /dev/null +++ b/homeassistant/components/harmony/translations/select.de.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Ausschalten" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.el.json b/homeassistant/components/harmony/translations/select.el.json new file mode 100644 index 00000000000..72243ee6e28 --- /dev/null +++ b/homeassistant/components/harmony/translations/select.el.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.es.json b/homeassistant/components/harmony/translations/select.es.json new file mode 100644 index 00000000000..23b2c4d5b91 --- /dev/null +++ b/homeassistant/components/harmony/translations/select.es.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Apagar" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.et.json b/homeassistant/components/harmony/translations/select.et.json new file mode 100644 index 00000000000..47802c3f211 --- /dev/null +++ b/homeassistant/components/harmony/translations/select.et.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Toide v\u00e4lja" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.fr.json b/homeassistant/components/harmony/translations/select.fr.json new file mode 100644 index 00000000000..a3c7eb3ebee --- /dev/null +++ b/homeassistant/components/harmony/translations/select.fr.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "\u00c9teint" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.id.json b/homeassistant/components/harmony/translations/select.id.json new file mode 100644 index 00000000000..8fb17a3cbec --- /dev/null +++ b/homeassistant/components/harmony/translations/select.id.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Daya Mati" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.it.json b/homeassistant/components/harmony/translations/select.it.json new file mode 100644 index 00000000000..4f6842c3c9b --- /dev/null +++ b/homeassistant/components/harmony/translations/select.it.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Spegni" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.no.json b/homeassistant/components/harmony/translations/select.no.json new file mode 100644 index 00000000000..c26762bd22f --- /dev/null +++ b/homeassistant/components/harmony/translations/select.no.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Sl\u00e5 av" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.pl.json b/homeassistant/components/harmony/translations/select.pl.json new file mode 100644 index 00000000000..6786518c447 --- /dev/null +++ b/homeassistant/components/harmony/translations/select.pl.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "wy\u0142\u0105czony" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.pt-BR.json b/homeassistant/components/harmony/translations/select.pt-BR.json new file mode 100644 index 00000000000..cd353b00716 --- /dev/null +++ b/homeassistant/components/harmony/translations/select.pt-BR.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Desligar" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.ru.json b/homeassistant/components/harmony/translations/select.ru.json new file mode 100644 index 00000000000..4902ddbb823 --- /dev/null +++ b/homeassistant/components/harmony/translations/select.ru.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u0438\u0442\u0430\u043d\u0438\u044f" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.zh-Hant.json b/homeassistant/components/harmony/translations/select.zh-Hant.json new file mode 100644 index 00000000000..622264706e7 --- /dev/null +++ b/homeassistant/components/harmony/translations/select.zh-Hant.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "\u95dc\u6a5f" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/sk.json b/homeassistant/components/harmony/translations/sk.json index 427503789ce..ff396ca4d3e 100644 --- a/homeassistant/components/harmony/translations/sk.json +++ b/homeassistant/components/harmony/translations/sk.json @@ -1,5 +1,12 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "flow_title": "{name}", "step": { "link": { diff --git a/homeassistant/components/hassio/translations/sk.json b/homeassistant/components/hassio/translations/sk.json new file mode 100644 index 00000000000..6f36cf3bf8a --- /dev/null +++ b/homeassistant/components/hassio/translations/sk.json @@ -0,0 +1,7 @@ +{ + "system_health": { + "info": { + "host_os": "Hostite\u013esk\u00fd opera\u010dn\u00fd syst\u00e9m" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/translations/sk.json b/homeassistant/components/heos/translations/sk.json index 842ff61bd79..bf16a99d5e1 100644 --- a/homeassistant/components/heos/translations/sk.json +++ b/homeassistant/components/heos/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/here_travel_time/translations/sk.json b/homeassistant/components/here_travel_time/translations/sk.json index 793f8eff278..2d1d7299de3 100644 --- a/homeassistant/components/here_travel_time/translations/sk.json +++ b/homeassistant/components/here_travel_time/translations/sk.json @@ -2,6 +2,10 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" } } } \ No newline at end of file diff --git a/homeassistant/components/hive/translations/sk.json b/homeassistant/components/hive/translations/sk.json index 29946cdbfba..34cd991751e 100644 --- a/homeassistant/components/hive/translations/sk.json +++ b/homeassistant/components/hive/translations/sk.json @@ -1,8 +1,14 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, + "error": { + "invalid_username": "Nepodarilo sa prihl\u00e1si\u0165 do syst\u00e9mu Hive. Va\u0161a e-mailov\u00e1 adresa nie je rozpoznan\u00e1.", + "no_internet_available": "Na pripojenie k Hive je potrebn\u00e9 internetov\u00e9 pripojenie.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "configuration": { "data": { @@ -16,7 +22,17 @@ }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "scan_interval": "Interval skenovania (sekundy)" + } + } + } + }, + "options": { + "step": { + "user": { + "data": { + "scan_interval": "Interval skenovania (sekundy)" } } } diff --git a/homeassistant/components/hlk_sw16/translations/sk.json b/homeassistant/components/hlk_sw16/translations/sk.json index 08d8e8f6516..2ee96a72a30 100644 --- a/homeassistant/components/hlk_sw16/translations/sk.json +++ b/homeassistant/components/hlk_sw16/translations/sk.json @@ -1,7 +1,12 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/home_plus_control/translations/sk.json b/homeassistant/components/home_plus_control/translations/sk.json index 97ac5f20ed2..62d09ce813a 100644 --- a/homeassistant/components/home_plus_control/translations/sk.json +++ b/homeassistant/components/home_plus_control/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "create_entry": { diff --git a/homeassistant/components/homeassistant/translations/sk.json b/homeassistant/components/homeassistant/translations/sk.json new file mode 100644 index 00000000000..d49f9f57329 --- /dev/null +++ b/homeassistant/components/homeassistant/translations/sk.json @@ -0,0 +1,7 @@ +{ + "issues": { + "python_version": { + "title": "Podpora pre Python {current_python_version} sa odstra\u0148uje" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/it.json b/homeassistant/components/homeassistant_yellow/translations/it.json index 353d65ea7a5..556d0cd3737 100644 --- a/homeassistant/components/homeassistant_yellow/translations/it.json +++ b/homeassistant/components/homeassistant_yellow/translations/it.json @@ -5,7 +5,8 @@ "addon_install_failed": "Impossibile installare il componente aggiuntivo Silicon Labs Multiprotocol.", "addon_set_config_failed": "Impossibile impostare la configurazione di Silicon Labs Multiprotocol.", "addon_start_failed": "Impossibile avviare il componente aggiuntivo Silicon Labs Multiprotocol.", - "not_hassio": "Le opzioni hardware possono essere configurate solo su installazioni HassOS." + "not_hassio": "Le opzioni hardware possono essere configurate solo su installazioni HassOS.", + "zha_migration_failed": "La migrazione ZHA non \u00e8 riuscita." }, "error": { "unknown": "Errore imprevisto" diff --git a/homeassistant/components/homeassistant_yellow/translations/pt-BR.json b/homeassistant/components/homeassistant_yellow/translations/pt-BR.json index ecbc1a36d62..b828d7a9d5b 100644 --- a/homeassistant/components/homeassistant_yellow/translations/pt-BR.json +++ b/homeassistant/components/homeassistant_yellow/translations/pt-BR.json @@ -5,7 +5,8 @@ "addon_install_failed": "Falha ao instalar o add-on Silicon Labs Multiprotocol.", "addon_set_config_failed": "Falha ao definir a configura\u00e7\u00e3o multiprotocolo da Silicon Labs.", "addon_start_failed": "Falha ao iniciar o add-on Silicon Labs Multiprotocol.", - "not_hassio": "As op\u00e7\u00f5es de hardware s\u00f3 podem ser configuradas em instala\u00e7\u00f5es HassOS." + "not_hassio": "As op\u00e7\u00f5es de hardware s\u00f3 podem ser configuradas em instala\u00e7\u00f5es HassOS.", + "zha_migration_failed": "A migra\u00e7\u00e3o ZHA n\u00e3o foi bem-sucedida." }, "error": { "unknown": "Erro inesperado" @@ -22,7 +23,7 @@ "data": { "enable_multi_pan": "Habilitar suporte multiprotocolo" }, - "description": "Quando o suporte multiprotocolo est\u00e1 ativado, o r\u00e1dio IEEE 802.15.4 do Home Assistant Yellow pode ser usado para Zigbee e Thread (usado por Matter) ao mesmo tempo. Nota: Este \u00e9 um recurso experimental.", + "description": "Quando o suporte multiprotocolo est\u00e1 ativado, o r\u00e1dio IEEE 802.15.4 do Home Assistant Yellow pode ser usado para Zigbee e Thread (usado por Matter) ao mesmo tempo. Se o r\u00e1dio j\u00e1 estiver sendo usado pela integra\u00e7\u00e3o ZHA Zigbee, o ZHA ser\u00e1 reconfigurado para usar o firmware multiprotocolo. \n\n Nota: Este \u00e9 um recurso experimental.", "title": "Habilitar o suporte multiprotocolo no r\u00e1dio IEEE 802.15.4" }, "install_addon": { diff --git a/homeassistant/components/homeassistant_yellow/translations/sk.json b/homeassistant/components/homeassistant_yellow/translations/sk.json index 03a9c39219d..f40c05f8068 100644 --- a/homeassistant/components/homeassistant_yellow/translations/sk.json +++ b/homeassistant/components/homeassistant_yellow/translations/sk.json @@ -2,6 +2,11 @@ "options": { "error": { "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "addon_installed_other_device": { + "title": "Podpora viacer\u00fdch protokolov je u\u017e povolen\u00e1 pre in\u00e9 zariadenie" + } } } } \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/zh-Hant.json b/homeassistant/components/homeassistant_yellow/translations/zh-Hant.json index 9b0fffd7464..bab420862dc 100644 --- a/homeassistant/components/homeassistant_yellow/translations/zh-Hant.json +++ b/homeassistant/components/homeassistant_yellow/translations/zh-Hant.json @@ -5,7 +5,8 @@ "addon_install_failed": "\u5b89\u88dd Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", "addon_set_config_failed": "\u8a2d\u5b9a Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", "addon_start_failed": "\u555f\u52d5 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", - "not_hassio": "\u786c\u9ad4\u9078\u9805\u50c5\u80fd\u65bc HassOS \u5b89\u88dd\u6a21\u5f0f\u9032\u884c\u8a2d\u5b9a\u3002" + "not_hassio": "\u786c\u9ad4\u9078\u9805\u50c5\u80fd\u65bc HassOS \u5b89\u88dd\u6a21\u5f0f\u9032\u884c\u8a2d\u5b9a\u3002", + "zha_migration_failed": "ZHA \u9077\u79fb\u672a\u6210\u529f\u3002" }, "error": { "unknown": "\u672a\u9810\u671f\u932f\u8aa4" @@ -22,7 +23,7 @@ "data": { "enable_multi_pan": "\u555f\u7528 Multiprotocol \u652f\u63f4" }, - "description": "\u7576\u555f\u7528 Multiprotocol \u652f\u63f4\u6642\u3001Home Assistant Yellow \u7684 IEEE 802.15.4 radio \u53ef\u540c\u6642\u4f5c\u70ba Zigbee \u8207 Thread \uff08\u7528\u65bc Matter\uff09\u4f7f\u7528\u3002\u6ce8\u610f\uff1a\u76ee\u524d\u70ba\u5be6\u9a57\u6027\u529f\u80fd\u3002", + "description": "\u7576\u555f\u7528 Multiprotocol \u652f\u63f4\u6642\u3001Home Assistant Yellow \u7684 IEEE 802.15.4 radio \u53ef\u540c\u6642\u4f5c\u70ba Zigbee \u8207 Thread \uff08\u7528\u65bc Matter\uff09\u4f7f\u7528\u3002\u5047\u5982 Radio \u5df2\u7d93\u88ab ZHA Zigbee \u6574\u5408\u6240\u4f7f\u7528\u3001ZHA \u5c07\u6703\u9032\u884c\u91cd\u65b0\u8a2d\u5b9a\u4ee5\u4f7f\u7528 Multiprotocol \u97cc\u9ad4\u3002\n\n\u6ce8\u610f\uff1a\u76ee\u524d\u70ba\u5be6\u9a57\u6027\u529f\u80fd\u3002", "title": "\u65bc IEEE 802.15.4 radio \u4e0a\u555f\u7528 Multiprotocol \u652f\u63f4" }, "install_addon": { diff --git a/homeassistant/components/homekit_controller/translations/pt-BR.json b/homeassistant/components/homekit_controller/translations/pt-BR.json index f84ff6bac37..1055cd6c921 100644 --- a/homeassistant/components/homekit_controller/translations/pt-BR.json +++ b/homeassistant/components/homekit_controller/translations/pt-BR.json @@ -14,7 +14,7 @@ "authentication_error": "C\u00f3digo HomeKit incorreto. Por favor verifique e tente novamente.", "insecure_setup_code": "O c\u00f3digo de configura\u00e7\u00e3o solicitado \u00e9 inseguro devido \u00e0 sua natureza trivial. Este acess\u00f3rio n\u00e3o atende aos requisitos b\u00e1sicos de seguran\u00e7a.", "max_peers_error": "O dispositivo recusou-se a adicionar o emparelhamento, pois n\u00e3o tem armazenamento de emparelhamento gratuito.", - "pairing_failed": "Ocorreu um erro sem tratamento ao tentar emparelhar com este dispositivo. Isso pode ser uma falha tempor\u00e1ria ou o dispositivo pode n\u00e3o ser suportado no momento.", + "pairing_failed": "Ocorreu um erro n\u00e3o tratado ao tentar emparelhar com este dispositivo. Esta pode ser uma falha tempor\u00e1ria ou seu dispositivo pode n\u00e3o ser suportado atualmente: {error}", "unable_to_pair": "N\u00e3o \u00e9 poss\u00edvel parear, tente novamente.", "unknown_error": "O dispositivo relatou um erro desconhecido. O pareamento falhou." }, diff --git a/homeassistant/components/homekit_controller/translations/sk.json b/homeassistant/components/homekit_controller/translations/sk.json index 84988df072f..29f900b66d1 100644 --- a/homeassistant/components/homekit_controller/translations/sk.json +++ b/homeassistant/components/homekit_controller/translations/sk.json @@ -1,10 +1,20 @@ { "config": { "abort": { + "accessory_not_found_error": "Nie je mo\u017en\u00e9 prida\u0165 p\u00e1rovanie, preto\u017ee zariadenie u\u017e nie je mo\u017en\u00e9 n\u00e1js\u0165.", + "already_configured": "Pr\u00edslu\u0161enstvo je u\u017e nakonfigurovan\u00e9 s t\u00fdmto kontrol\u00e9rom.", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "error": { + "pairing_failed": "Pri pokuse o sp\u00e1rovanie s t\u00fdmto zariaden\u00edm do\u0161lo k neobsluhovanej chybe. M\u00f4\u017ee \u00eds\u0165 o do\u010dasn\u00fa chybu alebo va\u0161e zariadenie nemus\u00ed by\u0165 v s\u00fa\u010dasnosti podporovan\u00e9: {error}", + "unable_to_pair": "Nie je mo\u017en\u00e9 sp\u00e1rova\u0165, sk\u00faste to znova.", + "unknown_error": "Zariadenie hl\u00e1silo nezn\u00e1mu chybu. Sp\u00e1rovanie zlyhalo." + }, "flow_title": "{name} ({category})", "step": { + "busy_error": { + "title": "Zariadenie sa u\u017e sp\u00e1ruje s in\u00fdm kontrol\u00e9rom" + }, "user": { "data": { "device": "Zariadenie" diff --git a/homeassistant/components/homekit_controller/translations/zh-Hant.json b/homeassistant/components/homekit_controller/translations/zh-Hant.json index 63b4b45f520..4614676e797 100644 --- a/homeassistant/components/homekit_controller/translations/zh-Hant.json +++ b/homeassistant/components/homekit_controller/translations/zh-Hant.json @@ -14,7 +14,7 @@ "authentication_error": "Homekit \u4ee3\u78bc\u932f\u8aa4\uff0c\u8acb\u78ba\u5b9a\u5f8c\u518d\u8a66\u4e00\u6b21\u3002", "insecure_setup_code": "\u7531\u65bc\u5176\u7463\u788e\u7279\u6027\u3001\u6240\u8acb\u6c42\u7684\u8a2d\u5b9a\u4ee3\u78bc\u4e0d\u5b89\u5168\u3002\u6b64\u914d\u4ef6\u7121\u6cd5\u9054\u5230\u6700\u4f4e\u5b89\u5168\u9700\u6c42\u3002", "max_peers_error": "\u88dd\u7f6e\u5df2\u7121\u5269\u9918\u914d\u5c0d\u7a7a\u9593\uff0c\u62d2\u7d55\u9032\u884c\u914d\u5c0d\u3002", - "pairing_failed": "\u7576\u8a66\u5716\u8207\u88dd\u7f6e\u914d\u5c0d\u6642\u767c\u751f\u7121\u6cd5\u8655\u7406\u932f\u8aa4\uff0c\u53ef\u80fd\u50c5\u70ba\u66ab\u6642\u5931\u6548\u3001\u6216\u8005\u88dd\u7f6e\u76ee\u524d\u4e0d\u652f\u63f4\u3002", + "pairing_failed": "\u7576\u8a66\u5716\u8207\u88dd\u7f6e\u914d\u5c0d\u6642\u767c\u751f\u7121\u6cd5\u8655\u7406\u932f\u8aa4\uff0c\u53ef\u80fd\u50c5\u70ba\u66ab\u6642\u5931\u6548\u3001\u6216\u8005\u88dd\u7f6e\u76ee\u524d\u4e0d\u652f\u63f4\uff1a{error}", "unable_to_pair": "\u7121\u6cd5\u914d\u5c0d\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002", "unknown_error": "\u88dd\u7f6e\u56de\u5831\u672a\u77e5\u932f\u8aa4\u3002\u914d\u5c0d\u5931\u6557\u3002" }, diff --git a/homeassistant/components/homewizard/translations/sk.json b/homeassistant/components/homewizard/translations/sk.json index 0a8865e3b27..a393c32026b 100644 --- a/homeassistant/components/homewizard/translations/sk.json +++ b/homeassistant/components/homewizard/translations/sk.json @@ -9,6 +9,9 @@ "description": "Chcete nastavi\u0165 {product_type} ({serial}) na {ip_address}?" }, "user": { + "data": { + "ip_address": "IP adresa" + }, "title": "Konfigur\u00e1cia zariadenia" } } diff --git a/homeassistant/components/huawei_lte/translations/sk.json b/homeassistant/components/huawei_lte/translations/sk.json index 27394040ea5..390736db02e 100644 --- a/homeassistant/components/huawei_lte/translations/sk.json +++ b/homeassistant/components/huawei_lte/translations/sk.json @@ -18,5 +18,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "name": "N\u00e1zov notifika\u010dnej slu\u017eby (zmena vy\u017eaduje re\u0161tart)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/hue/translations/sk.json b/homeassistant/components/hue/translations/sk.json index cc98dc0c8e8..a932a3768d6 100644 --- a/homeassistant/components/hue/translations/sk.json +++ b/homeassistant/components/hue/translations/sk.json @@ -31,7 +31,10 @@ }, "device_automation": { "trigger_subtype": { - "1": "Prv\u00e9 tla\u010didlo" + "1": "Prv\u00e9 tla\u010didlo", + "2": "Druh\u00e9 tla\u010didlo", + "button_2": "Druh\u00e9 tla\u010didlo", + "double_buttons_2_4": "Druh\u00e9 a \u0161tvrt\u00e9 tla\u010didlo" }, "trigger_type": { "remote_button_long_release": "\"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed", diff --git a/homeassistant/components/huisbaasje/translations/sk.json b/homeassistant/components/huisbaasje/translations/sk.json index 8f06d99549b..49b712d3ea1 100644 --- a/homeassistant/components/huisbaasje/translations/sk.json +++ b/homeassistant/components/huisbaasje/translations/sk.json @@ -4,7 +4,8 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/hunterdouglas_powerview/translations/sk.json b/homeassistant/components/hunterdouglas_powerview/translations/sk.json index c1f2a447006..bd0a4fe1d64 100644 --- a/homeassistant/components/hunterdouglas_powerview/translations/sk.json +++ b/homeassistant/components/hunterdouglas_powerview/translations/sk.json @@ -3,6 +3,10 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "flow_title": "{name} ({host})", "step": { "link": { diff --git a/homeassistant/components/hvv_departures/translations/sk.json b/homeassistant/components/hvv_departures/translations/sk.json index 254174d26ab..df2114b37f9 100644 --- a/homeassistant/components/hvv_departures/translations/sk.json +++ b/homeassistant/components/hvv_departures/translations/sk.json @@ -4,9 +4,23 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "no_results": "\u017diadne v\u00fdsledky. Sk\u00faste pou\u017ei\u0165 in\u00fa stanicu/adresu" }, "step": { + "station": { + "data": { + "station": "Stanica/Adresa" + }, + "title": "Zadajte stanicu/adresu" + }, + "station_select": { + "data": { + "station": "Stanica/Adresa" + }, + "title": "Zvo\u013ete Stanica/Adresa" + }, "user": { "data": { "host": "Hostite\u013e", @@ -14,5 +28,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "offset": "Posun (v min\u00fatach)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/hyperion/translations/sk.json b/homeassistant/components/hyperion/translations/sk.json index 41c1548a85d..ea5aabee25f 100644 --- a/homeassistant/components/hyperion/translations/sk.json +++ b/homeassistant/components/hyperion/translations/sk.json @@ -1,9 +1,16 @@ { "config": { "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "auth_new_token_not_granted_error": "Novovytvoren\u00fd token nebol schv\u00e1len\u00fd v pou\u017e\u00edvate\u013eskom rozhran\u00ed Hyperion", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/ialarm/translations/sk.json b/homeassistant/components/ialarm/translations/sk.json index 142f47cf1fd..e3d2afb6266 100644 --- a/homeassistant/components/ialarm/translations/sk.json +++ b/homeassistant/components/ialarm/translations/sk.json @@ -3,6 +3,10 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/iaqualink/translations/sk.json b/homeassistant/components/iaqualink/translations/sk.json index 1b1e671c054..0cb745b92e7 100644 --- a/homeassistant/components/iaqualink/translations/sk.json +++ b/homeassistant/components/iaqualink/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { diff --git a/homeassistant/components/icloud/translations/sk.json b/homeassistant/components/icloud/translations/sk.json index a70d7f739d1..9711125c5e1 100644 --- a/homeassistant/components/icloud/translations/sk.json +++ b/homeassistant/components/icloud/translations/sk.json @@ -1,10 +1,13 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "send_verification_code": "Odoslanie overovacieho k\u00f3du zlyhalo", + "validate_verification_code": "Nepodarilo sa overi\u0165 overovac\u00ed k\u00f3d, sk\u00faste to znova" }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/inkbird/translations/sk.json b/homeassistant/components/inkbird/translations/sk.json index 8444b9fbeae..a8fa63be59a 100644 --- a/homeassistant/components/inkbird/translations/sk.json +++ b/homeassistant/components/inkbird/translations/sk.json @@ -2,7 +2,15 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/insteon/translations/sk.json b/homeassistant/components/insteon/translations/sk.json index a7f1d45f5d4..d10b7bfdc96 100644 --- a/homeassistant/components/insteon/translations/sk.json +++ b/homeassistant/components/insteon/translations/sk.json @@ -3,6 +3,10 @@ "abort": { "cannot_connect": "Nepodarilo sa pripoji\u0165" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "select_single": "Vyberte jednu mo\u017enos\u0165." + }, "flow_title": "{name}", "step": { "confirm_usb": { @@ -24,6 +28,11 @@ } }, "options": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "input_error": "Neplatn\u00e9 polo\u017eky, skontrolujte svoje hodnoty.", + "select_single": "Vyberte jednu mo\u017enos\u0165." + }, "step": { "add_override": { "data": { @@ -38,6 +47,16 @@ "password": "Heslo", "port": "Port" } + }, + "remove_override": { + "data": { + "address": "Vyberte adresu zariadenia, ktor\u00fa chcete odstr\u00e1ni\u0165" + } + }, + "remove_x10": { + "data": { + "address": "Vyberte adresu zariadenia, ktor\u00fa chcete odstr\u00e1ni\u0165" + } } } } diff --git a/homeassistant/components/intellifire/translations/sk.json b/homeassistant/components/intellifire/translations/sk.json index c1e58930394..a3d99983a20 100644 --- a/homeassistant/components/intellifire/translations/sk.json +++ b/homeassistant/components/intellifire/translations/sk.json @@ -3,6 +3,11 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "api_error": "Prihl\u00e1senie zlyhalo", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "iftapi_connect": "Chyba pri prip\u00e1jan\u00ed k iftapi.net" + }, "flow_title": "{serial} ({host})", "step": { "api_config": { diff --git a/homeassistant/components/iotawatt/translations/sk.json b/homeassistant/components/iotawatt/translations/sk.json index 0d9f93851ce..ff5ef67d0e3 100644 --- a/homeassistant/components/iotawatt/translations/sk.json +++ b/homeassistant/components/iotawatt/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "auth": { diff --git a/homeassistant/components/ipma/translations/sk.json b/homeassistant/components/ipma/translations/sk.json index e5a635afe10..ff6f9b0683b 100644 --- a/homeassistant/components/ipma/translations/sk.json +++ b/homeassistant/components/ipma/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "name_exists": "N\u00e1zov u\u017e existuje" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/ipp/translations/sk.json b/homeassistant/components/ipp/translations/sk.json index 10e176c9797..868d5e6ccd9 100644 --- a/homeassistant/components/ipp/translations/sk.json +++ b/homeassistant/components/ipp/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "ipp_error": "Vyskytla sa chyba IPP." }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/iqvia/translations/sk.json b/homeassistant/components/iqvia/translations/sk.json new file mode 100644 index 00000000000..f04d4a327f4 --- /dev/null +++ b/homeassistant/components/iqvia/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/isy994/translations/sk.json b/homeassistant/components/isy994/translations/sk.json index cdd7aaba085..d28f0fb7922 100644 --- a/homeassistant/components/isy994/translations/sk.json +++ b/homeassistant/components/isy994/translations/sk.json @@ -4,7 +4,8 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({host})", "step": { diff --git a/homeassistant/components/jellyfin/translations/sk.json b/homeassistant/components/jellyfin/translations/sk.json index 1b1e671c054..41d0727a9b4 100644 --- a/homeassistant/components/jellyfin/translations/sk.json +++ b/homeassistant/components/jellyfin/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/juicenet/translations/sk.json b/homeassistant/components/juicenet/translations/sk.json index 5ada995aa6e..b838556fe17 100644 --- a/homeassistant/components/juicenet/translations/sk.json +++ b/homeassistant/components/juicenet/translations/sk.json @@ -1,7 +1,12 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" } } } \ No newline at end of file diff --git a/homeassistant/components/justnimbus/translations/sk.json b/homeassistant/components/justnimbus/translations/sk.json index 793f8eff278..bee35ba27c1 100644 --- a/homeassistant/components/justnimbus/translations/sk.json +++ b/homeassistant/components/justnimbus/translations/sk.json @@ -2,6 +2,11 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" } } } \ No newline at end of file diff --git a/homeassistant/components/kaleidescape/translations/sk.json b/homeassistant/components/kaleidescape/translations/sk.json index 5799d863b27..0dce503bffa 100644 --- a/homeassistant/components/kaleidescape/translations/sk.json +++ b/homeassistant/components/kaleidescape/translations/sk.json @@ -2,9 +2,12 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", "unsupported": "Nepodporovan\u00e9 zariadenie" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "unsupported": "Nepodporovan\u00e9 zariadenie" }, "flow_title": "{model} ({name})", diff --git a/homeassistant/components/keenetic_ndms2/translations/sk.json b/homeassistant/components/keenetic_ndms2/translations/sk.json index 2f376202caa..aab65ab747b 100644 --- a/homeassistant/components/keenetic_ndms2/translations/sk.json +++ b/homeassistant/components/keenetic_ndms2/translations/sk.json @@ -1,5 +1,11 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{name} ({host})", "step": { "user": { diff --git a/homeassistant/components/kegtron/translations/sk.json b/homeassistant/components/kegtron/translations/sk.json index 25ca828afe4..48a6116c8ee 100644 --- a/homeassistant/components/kegtron/translations/sk.json +++ b/homeassistant/components/kegtron/translations/sk.json @@ -2,8 +2,16 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "not_supported": "Zariadenie nie je podporovan\u00e9" + }, + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/keymitt_ble/translations/sk.json b/homeassistant/components/keymitt_ble/translations/sk.json index 2db5c07a5ea..caf350e7f81 100644 --- a/homeassistant/components/keymitt_ble/translations/sk.json +++ b/homeassistant/components/keymitt_ble/translations/sk.json @@ -2,7 +2,9 @@ "config": { "abort": { "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "no_unconfigured_devices": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia." + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "no_unconfigured_devices": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/kmtronic/translations/sk.json b/homeassistant/components/kmtronic/translations/sk.json index 254174d26ab..2ee96a72a30 100644 --- a/homeassistant/components/kmtronic/translations/sk.json +++ b/homeassistant/components/kmtronic/translations/sk.json @@ -4,7 +4,9 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/knx/translations/sk.json b/homeassistant/components/knx/translations/sk.json index 183bbc74bdf..10cf86d539a 100644 --- a/homeassistant/components/knx/translations/sk.json +++ b/homeassistant/components/knx/translations/sk.json @@ -3,6 +3,10 @@ "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_ip_address": "Neplatn\u00e1 adresa IPv4." + }, "step": { "manual_tunnel": { "data": { @@ -13,7 +17,11 @@ }, "routing": { "data": { + "individual_address": "Individu\u00e1lna adresa", "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" + }, + "data_description": { + "individual_address": "Adresa KNX, ktor\u00fa bude pou\u017e\u00edva\u0165 Home Assistant, napr. `0.0.4`" } }, "secure_tunnel_manual": { @@ -26,6 +34,10 @@ } }, "options": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_ip_address": "Neplatn\u00e1 adresa IPv4." + }, "step": { "connection_type": { "data": { @@ -41,7 +53,11 @@ }, "routing": { "data": { + "individual_address": "Individu\u00e1lna adresa", "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" + }, + "data_description": { + "individual_address": "Adresa KNX, ktor\u00fa bude pou\u017e\u00edva\u0165 Home Assistant, napr. `0.0.4`" } }, "secure_tunnel_manual": { diff --git a/homeassistant/components/kodi/translations/sk.json b/homeassistant/components/kodi/translations/sk.json index 9a6a4bcb484..534dd4ed347 100644 --- a/homeassistant/components/kodi/translations/sk.json +++ b/homeassistant/components/kodi/translations/sk.json @@ -2,10 +2,14 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/konnected/translations/sk.json b/homeassistant/components/konnected/translations/sk.json index 11a47f35157..ef7dc74d64f 100644 --- a/homeassistant/components/konnected/translations/sk.json +++ b/homeassistant/components/konnected/translations/sk.json @@ -2,9 +2,17 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { + "confirm": { + "description": "Model: {model}\n ID: {id}\n Hostite\u013e: {host}\n Port: {port} \n\n Spr\u00e1vanie IO a panela m\u00f4\u017eete nakonfigurova\u0165 v nastaveniach panela Pripojen\u00fd alarm." + }, "user": { "data": { "host": "IP adresa", diff --git a/homeassistant/components/kostal_plenticore/translations/sk.json b/homeassistant/components/kostal_plenticore/translations/sk.json index 08d8e8f6516..2ee96a72a30 100644 --- a/homeassistant/components/kostal_plenticore/translations/sk.json +++ b/homeassistant/components/kostal_plenticore/translations/sk.json @@ -1,7 +1,12 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/kraken/translations/sk.json b/homeassistant/components/kraken/translations/sk.json index ba5e13223a7..73566432806 100644 --- a/homeassistant/components/kraken/translations/sk.json +++ b/homeassistant/components/kraken/translations/sk.json @@ -1,4 +1,9 @@ { + "config": { + "abort": { + "already_configured": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/lacrosse_view/translations/sk.json b/homeassistant/components/lacrosse_view/translations/sk.json index d1c130a108b..12787c93e41 100644 --- a/homeassistant/components/lacrosse_view/translations/sk.json +++ b/homeassistant/components/lacrosse_view/translations/sk.json @@ -3,6 +3,11 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "invalid_auth": "Neplatn\u00e9 overenie", + "no_locations": "Nena\u0161li sa \u017eiadne lokality", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/lametric/translations/sk.json b/homeassistant/components/lametric/translations/sk.json index 793f8eff278..c80688dab97 100644 --- a/homeassistant/components/lametric/translations/sk.json +++ b/homeassistant/components/lametric/translations/sk.json @@ -1,7 +1,20 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "link_local_address": "Lok\u00e1lne adresy odkazov nie s\u00fa podporovan\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "manual_entry": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/sk.json b/homeassistant/components/laundrify/translations/sk.json new file mode 100644 index 00000000000..50419cf7edf --- /dev/null +++ b/homeassistant/components/laundrify/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_format": "Neplatn\u00fd form\u00e1t. Zadajte ako xxx-xxx." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/led_ble/translations/sk.json b/homeassistant/components/led_ble/translations/sk.json index fe721216387..afb81ec3123 100644 --- a/homeassistant/components/led_ble/translations/sk.json +++ b/homeassistant/components/led_ble/translations/sk.json @@ -2,10 +2,18 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "no_unconfigured_devices": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia.", "not_supported": "Zariadenie nie je podporovan\u00e9" }, - "flow_title": "{name}" + "flow_title": "{name}", + "step": { + "user": { + "data": { + "address": "Adresa Bluetooth" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/sk.json b/homeassistant/components/lidarr/translations/sk.json new file mode 100644 index 00000000000..f04d4a327f4 --- /dev/null +++ b/homeassistant/components/lidarr/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/life360/translations/sk.json b/homeassistant/components/life360/translations/sk.json index b4d12b85362..47e3b3ae618 100644 --- a/homeassistant/components/life360/translations/sk.json +++ b/homeassistant/components/life360/translations/sk.json @@ -1,9 +1,11 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "invalid_auth": "Neplatn\u00e9 overenie" }, "error": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { diff --git a/homeassistant/components/lifx/translations/sk.json b/homeassistant/components/lifx/translations/sk.json index af15d63baa2..4914a2e64dc 100644 --- a/homeassistant/components/lifx/translations/sk.json +++ b/homeassistant/components/lifx/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "flow_title": "{label} ({host}) {serial}", "step": { @@ -12,6 +13,11 @@ "data": { "device": "Zariadenie" } + }, + "user": { + "data": { + "host": "Hostite\u013e" + } } } } diff --git a/homeassistant/components/litterrobot/translations/sk.json b/homeassistant/components/litterrobot/translations/sk.json index 4b1e8bf1a77..6dbe2a76791 100644 --- a/homeassistant/components/litterrobot/translations/sk.json +++ b/homeassistant/components/litterrobot/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/livisi/translations/sk.json b/homeassistant/components/livisi/translations/sk.json index 6ca95b4f1f6..805af52a823 100644 --- a/homeassistant/components/livisi/translations/sk.json +++ b/homeassistant/components/livisi/translations/sk.json @@ -2,6 +2,7 @@ "config": { "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", + "wrong_ip_address": "IP adresa je nespr\u00e1vna alebo SHC nie je mo\u017en\u00e9 lok\u00e1lne dosiahnu\u0165.", "wrong_password": "Heslo je nespr\u00e1vne." }, "step": { @@ -9,7 +10,8 @@ "data": { "host": "IP adresa", "password": "Heslo" - } + }, + "description": "Zadajte IP adresu a (miestne) heslo SHC." } } } diff --git a/homeassistant/components/local_ip/translations/sk.json b/homeassistant/components/local_ip/translations/sk.json new file mode 100644 index 00000000000..58545d48767 --- /dev/null +++ b/homeassistant/components/local_ip/translations/sk.json @@ -0,0 +1,10 @@ +{ + "config": { + "step": { + "user": { + "title": "Lok\u00e1lna IP adresa" + } + } + }, + "title": "Lok\u00e1lna IP adresa" +} \ No newline at end of file diff --git a/homeassistant/components/logi_circle/translations/sk.json b/homeassistant/components/logi_circle/translations/sk.json index 5ada995aa6e..a243b0ad8eb 100644 --- a/homeassistant/components/logi_circle/translations/sk.json +++ b/homeassistant/components/logi_circle/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" } diff --git a/homeassistant/components/lookin/translations/sk.json b/homeassistant/components/lookin/translations/sk.json index 3474144425e..ff1b0e0b78b 100644 --- a/homeassistant/components/lookin/translations/sk.json +++ b/homeassistant/components/lookin/translations/sk.json @@ -3,10 +3,13 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, "error": { - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({host})", "step": { @@ -17,6 +20,11 @@ }, "discovery_confirm": { "description": "Chcete nastavi\u0165 {name} ({host})?" + }, + "user": { + "data": { + "ip_address": "IP adresa" + } } } } diff --git a/homeassistant/components/luftdaten/translations/sk.json b/homeassistant/components/luftdaten/translations/sk.json index 4aa3ebe30a9..698a3e835fa 100644 --- a/homeassistant/components/luftdaten/translations/sk.json +++ b/homeassistant/components/luftdaten/translations/sk.json @@ -1,6 +1,8 @@ { "config": { "error": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_sensor": "Sn\u00edma\u010d nie je dostupn\u00fd alebo je neplatn\u00fd" }, "step": { diff --git a/homeassistant/components/lutron_caseta/translations/sk.json b/homeassistant/components/lutron_caseta/translations/sk.json index 7b3c7b86ff7..9aef4795f42 100644 --- a/homeassistant/components/lutron_caseta/translations/sk.json +++ b/homeassistant/components/lutron_caseta/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "flow_title": "{name} ({host})", "step": { @@ -21,7 +25,10 @@ "button_4": "\u0160tvrt\u00e9 tla\u010didlo", "button_5": "Piate tla\u010didlo", "button_6": "\u0160ieste tla\u010didlo", - "button_7": "Siedme tla\u010didlo" + "button_7": "Siedme tla\u010didlo", + "group_1_button_2": "Prv\u00e1 skupina druh\u00e9 tla\u010didlo", + "group_2_button_1": "Druh\u00e1 skupina prv\u00e9 tla\u010didlo", + "group_2_button_2": "Druh\u00e1 skupina druh\u00e9 tla\u010didlo" } } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/sk.json b/homeassistant/components/mazda/translations/sk.json index bf7ebd86a62..7a2c6414f82 100644 --- a/homeassistant/components/mazda/translations/sk.json +++ b/homeassistant/components/mazda/translations/sk.json @@ -1,10 +1,14 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "account_locked": "\u00da\u010det je zablokovan\u00fd. Sk\u00faste nesk\u00f4r pros\u00edm.", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/meater/translations/sk.json b/homeassistant/components/meater/translations/sk.json index 1dc09dd1da0..6bf84cbc8e3 100644 --- a/homeassistant/components/meater/translations/sk.json +++ b/homeassistant/components/meater/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "error": { - "service_unavailable_error": "Rozhranie API je moment\u00e1lne nedostupn\u00e9, sk\u00faste to pros\u00edm nesk\u00f4r." + "invalid_auth": "Neplatn\u00e9 overenie", + "service_unavailable_error": "Rozhranie API je moment\u00e1lne nedostupn\u00e9, sk\u00faste to pros\u00edm nesk\u00f4r.", + "unknown_auth_error": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/melcloud/translations/sk.json b/homeassistant/components/melcloud/translations/sk.json index 7f2f6229304..8d7a1129c85 100644 --- a/homeassistant/components/melcloud/translations/sk.json +++ b/homeassistant/components/melcloud/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/met_eireann/translations/es.json b/homeassistant/components/met_eireann/translations/es.json index d476d15366e..1aae7e78022 100644 --- a/homeassistant/components/met_eireann/translations/es.json +++ b/homeassistant/components/met_eireann/translations/es.json @@ -11,7 +11,7 @@ "longitude": "Longitud", "name": "Nombre" }, - "description": "Introduce tu ubicaci\u00f3n para usar los datos meteorol\u00f3gicos de la API del pron\u00f3stico meteorol\u00f3gico p\u00fablico de Met \u00c9ireann", + "description": "Introduce tu ubicaci\u00f3n para usar los datos meteorol\u00f3gicos de la API de la previsi\u00f3n meteorol\u00f3gica p\u00fablica de Met \u00c9ireann", "title": "Ubicaci\u00f3n" } } diff --git a/homeassistant/components/met_eireann/translations/sk.json b/homeassistant/components/met_eireann/translations/sk.json index 492ab052d9a..c631fc4b440 100644 --- a/homeassistant/components/met_eireann/translations/sk.json +++ b/homeassistant/components/met_eireann/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/meteo_france/translations/es.json b/homeassistant/components/meteo_france/translations/es.json index d0a713119af..4645d06d596 100644 --- a/homeassistant/components/meteo_france/translations/es.json +++ b/homeassistant/components/meteo_france/translations/es.json @@ -26,7 +26,7 @@ "step": { "init": { "data": { - "mode": "Modo de pron\u00f3stico" + "mode": "Modo de previsi\u00f3n" } } } diff --git a/homeassistant/components/meteo_france/translations/sk.json b/homeassistant/components/meteo_france/translations/sk.json new file mode 100644 index 00000000000..e3bda44ccc6 --- /dev/null +++ b/homeassistant/components/meteo_france/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meteoclimatic/translations/sk.json b/homeassistant/components/meteoclimatic/translations/sk.json index fa6643bfcab..8ae0056e613 100644 --- a/homeassistant/components/meteoclimatic/translations/sk.json +++ b/homeassistant/components/meteoclimatic/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { "not_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" diff --git a/homeassistant/components/metoffice/translations/sk.json b/homeassistant/components/metoffice/translations/sk.json index abb3969f6b4..3ce9c0780c2 100644 --- a/homeassistant/components/metoffice/translations/sk.json +++ b/homeassistant/components/metoffice/translations/sk.json @@ -1,5 +1,12 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/mikrotik/translations/sk.json b/homeassistant/components/mikrotik/translations/sk.json index 384e693a043..3293e1a5870 100644 --- a/homeassistant/components/mikrotik/translations/sk.json +++ b/homeassistant/components/mikrotik/translations/sk.json @@ -1,7 +1,12 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "name_exists": "N\u00e1zov existuje" }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/mill/translations/sk.json b/homeassistant/components/mill/translations/sk.json index f14b98631b1..7ee3205fdbf 100644 --- a/homeassistant/components/mill/translations/sk.json +++ b/homeassistant/components/mill/translations/sk.json @@ -1,5 +1,11 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "cloud": { "data": { @@ -7,6 +13,9 @@ } }, "local": { + "data": { + "ip_address": "IP adresa" + }, "description": "Lok\u00e1lna IP adresa zariadenia." } } diff --git a/homeassistant/components/min_max/translations/de.json b/homeassistant/components/min_max/translations/de.json index cb4ad7b9996..81003aed00e 100644 --- a/homeassistant/components/min_max/translations/de.json +++ b/homeassistant/components/min_max/translations/de.json @@ -9,10 +9,10 @@ "type": "Statistisches Merkmal" }, "data_description": { - "round_digits": "Steuert die Anzahl der Dezimalstellen in der Ausgabe, wenn das Statistikmerkmal Mittelwert oder Median ist." + "round_digits": "Steuert die Anzahl der Dezimalstellen in der Ausgabe, wenn das Statistikmerkmal Mittelwert, Median oder Summe ist." }, - "description": "Erstelle einen Sensor, der einen Mindest-, H\u00f6chst-, Mittel- oder Medianwert aus einer Liste von Eingabesensoren berechnet.", - "title": "Min/Max/Mittelwert/Median-Sensor hinzuf\u00fcgen" + "description": "Erstelle einen Sensor, der einen Mindest-, H\u00f6chst-, Mittelwert, Median oder eine Summe aus einer Liste von Eingabesensoren berechnet.", + "title": "Kombiniere den Zustand mehrerer Sensoren" } } }, @@ -25,10 +25,10 @@ "type": "Statistisches Merkmal" }, "data_description": { - "round_digits": "Steuert die Anzahl der Dezimalstellen in der Ausgabe, wenn das Statistikmerkmal Mittelwert oder Median ist." + "round_digits": "Steuert die Anzahl der Dezimalstellen in der Ausgabe, wenn das Statistikmerkmal Mittelwert, Median oder Summe ist." } } } }, - "title": "Min / Max / Mittelwert / Mediansensor" + "title": "Kombiniere den Zustand mehrerer Sensoren" } \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/es.json b/homeassistant/components/min_max/translations/es.json index 149f3f030d3..cd2d9295189 100644 --- a/homeassistant/components/min_max/translations/es.json +++ b/homeassistant/components/min_max/translations/es.json @@ -9,10 +9,10 @@ "type": "Caracter\u00edstica estad\u00edstica" }, "data_description": { - "round_digits": "Controla el n\u00famero de d\u00edgitos decimales en la salida cuando la caracter\u00edstica estad\u00edstica es media o mediana." + "round_digits": "Controla el n\u00famero de d\u00edgitos decimales en la salida cuando la caracter\u00edstica estad\u00edstica es media, mediana o suma." }, - "description": "Crea un sensor que calcula el valor m\u00ednimo, m\u00e1ximo, medio o mediano a partir de una lista de sensores de entrada.", - "title": "A\u00f1adir sensor m\u00edn / m\u00e1x / media / mediana" + "description": "Crea un sensor que calcule un m\u00ednimo, un m\u00e1ximo, una media, una mediana o una suma a partir de una lista de sensores de entrada.", + "title": "Combinar el estado de varios sensores" } } }, @@ -25,10 +25,10 @@ "type": "Caracter\u00edstica estad\u00edstica" }, "data_description": { - "round_digits": "Controla el n\u00famero de d\u00edgitos decimales en la salida cuando la caracter\u00edstica estad\u00edstica es media o mediana." + "round_digits": "Controla el n\u00famero de d\u00edgitos decimales en la salida cuando la caracter\u00edstica estad\u00edstica es media, mediana o suma." } } } }, - "title": "Sensor m\u00edn / m\u00e1x / media / mediana" + "title": "Combinar el estado de varios sensores" } \ No newline at end of file diff --git a/homeassistant/components/minecraft_server/translations/sk.json b/homeassistant/components/minecraft_server/translations/sk.json index 965b3bb8919..d93d7f14e5d 100644 --- a/homeassistant/components/minecraft_server/translations/sk.json +++ b/homeassistant/components/minecraft_server/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/mjpeg/translations/sk.json b/homeassistant/components/mjpeg/translations/sk.json index 1b1ae53738b..7f91dfe2158 100644 --- a/homeassistant/components/mjpeg/translations/sk.json +++ b/homeassistant/components/mjpeg/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { @@ -18,6 +19,7 @@ "options": { "error": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { diff --git a/homeassistant/components/moat/translations/sk.json b/homeassistant/components/moat/translations/sk.json index 8444b9fbeae..a8fa63be59a 100644 --- a/homeassistant/components/moat/translations/sk.json +++ b/homeassistant/components/moat/translations/sk.json @@ -2,7 +2,15 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/modem_callerid/translations/sk.json b/homeassistant/components/modem_callerid/translations/sk.json index 3a991747995..28f532167a7 100644 --- a/homeassistant/components/modem_callerid/translations/sk.json +++ b/homeassistant/components/modem_callerid/translations/sk.json @@ -4,6 +4,9 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/modern_forms/translations/sk.json b/homeassistant/components/modern_forms/translations/sk.json index 835f107f7af..ed248c10965 100644 --- a/homeassistant/components/modern_forms/translations/sk.json +++ b/homeassistant/components/modern_forms/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/moehlenhoff_alpha2/translations/sk.json b/homeassistant/components/moehlenhoff_alpha2/translations/sk.json index 3c3d27a6689..bd2e41a926f 100644 --- a/homeassistant/components/moehlenhoff_alpha2/translations/sk.json +++ b/homeassistant/components/moehlenhoff_alpha2/translations/sk.json @@ -3,6 +3,10 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/monoprice/translations/sk.json b/homeassistant/components/monoprice/translations/sk.json index cb264915fdd..0c5645b08b5 100644 --- a/homeassistant/components/monoprice/translations/sk.json +++ b/homeassistant/components/monoprice/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/motioneye/translations/sk.json b/homeassistant/components/motioneye/translations/sk.json index 71a7aea5018..d61d1c26d16 100644 --- a/homeassistant/components/motioneye/translations/sk.json +++ b/homeassistant/components/motioneye/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/mqtt/translations/el.json b/homeassistant/components/mqtt/translations/el.json index c20b9461eb2..d04488af9dd 100644 --- a/homeassistant/components/mqtt/translations/el.json +++ b/homeassistant/components/mqtt/translations/el.json @@ -12,6 +12,7 @@ "bad_client_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03cc \u03ba\u03bb\u03b5\u03b9\u03b4\u03af, \u03b2\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03bc\u03b5 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 PEM \u03c0\u03b1\u03c1\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03c7\u03c9\u03c1\u03af\u03c2 \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "bad_discovery_prefix": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7\u03c2", "bad_will": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b8\u03ad\u03bc\u03b1", + "bad_ws_headers": "\u03a0\u03b1\u03c1\u03bf\u03c7\u03ae \u03ad\u03b3\u03ba\u03c5\u03c1\u03c9\u03bd \u03b5\u03c0\u03b9\u03ba\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03c9\u03bd HTTP \u03c9\u03c2 \u03b1\u03bd\u03c4\u03b9\u03ba\u03b5\u03af\u03bc\u03b5\u03bd\u03bf JSON", "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "invalid_inclusion": "\u03a4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7 \u03ba\u03b1\u03b9 \u03c4\u03bf \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03cc \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03bf\u03cd\u03bd \u03bc\u03b1\u03b6\u03af" }, @@ -31,7 +32,10 @@ "set_ca_cert": "\u0395\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03b5\u03c3\u03af\u03c4\u03b7", "set_client_cert": "\u03a7\u03c1\u03ae\u03c3\u03b7 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7", "tls_insecure": "\u0391\u03b3\u03bd\u03bf\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03b5\u03c3\u03af\u03c4\u03b7", - "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" + "transport": "\u039c\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac MQTT", + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", + "ws_headers": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b5\u03c2 WebSocket \u03c3\u03b5 \u03bc\u03bf\u03c1\u03c6\u03ae JSON", + "ws_path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae WebSocket" }, "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 MQTT broker." }, @@ -85,6 +89,7 @@ "bad_client_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03cc \u03ba\u03bb\u03b5\u03b9\u03b4\u03af, \u03b2\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03bc\u03b5 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 PEM \u03c0\u03b1\u03c1\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03c7\u03c9\u03c1\u03af\u03c2 \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "bad_discovery_prefix": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7\u03c2", "bad_will": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b8\u03ad\u03bc\u03b1 will.", + "bad_ws_headers": "\u03a0\u03b1\u03c1\u03bf\u03c7\u03ae \u03ad\u03b3\u03ba\u03c5\u03c1\u03c9\u03bd \u03b5\u03c0\u03b9\u03ba\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03c9\u03bd HTTP \u03c9\u03c2 \u03b1\u03bd\u03c4\u03b9\u03ba\u03b5\u03af\u03bc\u03b5\u03bd\u03bf JSON", "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "invalid_inclusion": "\u03a4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7 \u03ba\u03b1\u03b9 \u03c4\u03bf \u03b9\u03b4\u03b9\u03c9\u03c4\u03b9\u03ba\u03cc \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03bf\u03cd\u03bd \u03bc\u03b1\u03b6\u03af" }, @@ -104,7 +109,10 @@ "set_ca_cert": "\u0395\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03b5\u03c3\u03af\u03c4\u03b7", "set_client_cert": "\u03a7\u03c1\u03ae\u03c3\u03b7 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7", "tls_insecure": "\u0391\u03b3\u03bd\u03bf\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03b5\u03c3\u03af\u03c4\u03b7", - "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" + "transport": "\u039c\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac MQTT", + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", + "ws_headers": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b5\u03c2 WebSocket \u03c3\u03b5 \u03bc\u03bf\u03c1\u03c6\u03ae JSON", + "ws_path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae WebSocket" }, "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03bc\u03b5\u03c3\u03af\u03c4\u03b7 MQTT.", "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 broker" diff --git a/homeassistant/components/mqtt/translations/it.json b/homeassistant/components/mqtt/translations/it.json index e3a572b1d36..a90a95da470 100644 --- a/homeassistant/components/mqtt/translations/it.json +++ b/homeassistant/components/mqtt/translations/it.json @@ -12,6 +12,7 @@ "bad_client_key": "Chiave privata non valida, assicurarsi che venga fornito un file codificato PEM senza password", "bad_discovery_prefix": "Prefisso di rilevamento non valido", "bad_will": "Argomento testamento non valido", + "bad_ws_headers": "Fornisci intestazioni HTTP valide come oggetto JSON", "cannot_connect": "Impossibile connettersi", "invalid_inclusion": "Il certificato del client e la chiave privata devono essere configurati insieme" }, @@ -31,7 +32,10 @@ "set_ca_cert": "Convalida del certificato del broker", "set_client_cert": "Usa un certificato client", "tls_insecure": "Ignora la convalida del certificato del broker", - "username": "Nome utente" + "transport": "Trasporto MQTT", + "username": "Nome utente", + "ws_headers": "Intestazioni WebSocket in formato JSON", + "ws_path": "Percorso WebSocket" }, "description": "Inserisci le informazioni di connessione del tuo broker MQTT." }, @@ -85,6 +89,7 @@ "bad_client_key": "Chiave privata non valida, assicurarsi che venga fornito un file codificato PEM senza password", "bad_discovery_prefix": "Prefisso di rilevamento non valido", "bad_will": "Argomento testamento non valido", + "bad_ws_headers": "Fornisci intestazioni HTTP valide come oggetto JSON", "cannot_connect": "Impossibile connettersi", "invalid_inclusion": "Il certificato del client e la chiave privata devono essere configurati insieme" }, @@ -104,7 +109,10 @@ "set_ca_cert": "Convalida del certificato del broker", "set_client_cert": "Usa un certificato client", "tls_insecure": "Ignora la convalida del certificato del broker", - "username": "Nome utente" + "transport": "Trasporto MQTT", + "username": "Nome utente", + "ws_headers": "Intestazioni WebSocket in formato JSON", + "ws_path": "Percorso WebSocket" }, "description": "Inserisci le informazioni di connessione del tuo broker MQTT.", "title": "Opzioni del broker" diff --git a/homeassistant/components/mqtt/translations/no.json b/homeassistant/components/mqtt/translations/no.json index 7e91def7a96..fae51fabb20 100644 --- a/homeassistant/components/mqtt/translations/no.json +++ b/homeassistant/components/mqtt/translations/no.json @@ -12,6 +12,7 @@ "bad_client_key": "Ugyldig privat n\u00f8kkel, s\u00f8rg for at en PEM-kodet fil leveres uten passord", "bad_discovery_prefix": "Ugyldig oppdagelsesprefiks", "bad_will": "Ugyldig viljeemne", + "bad_ws_headers": "Oppgi gyldige HTTP-hoder som et JSON-objekt", "cannot_connect": "Tilkobling mislyktes", "invalid_inclusion": "Klientsertifikatet og den private n\u00f8kkelen m\u00e5 konfigureres sammen" }, @@ -31,7 +32,10 @@ "set_ca_cert": "Validering av meglersertifikat", "set_client_cert": "Bruk et klientsertifikat", "tls_insecure": "Ignorer validering av meglersertifikat", - "username": "Brukernavn" + "transport": "MQTT transport", + "username": "Brukernavn", + "ws_headers": "WebSocket-hoder i JSON-format", + "ws_path": "WebSocket-bane" }, "description": "Vennligst fyll ut tilkoblingsinformasjonen for din MQTT megler." }, @@ -85,6 +89,7 @@ "bad_client_key": "Ugyldig privat n\u00f8kkel, s\u00f8rg for at en PEM-kodet fil leveres uten passord", "bad_discovery_prefix": "Ugyldig oppdagelsesprefiks", "bad_will": "Ugyldig viljeemne", + "bad_ws_headers": "Oppgi gyldige HTTP-hoder som et JSON-objekt", "cannot_connect": "Tilkobling mislyktes", "invalid_inclusion": "Klientsertifikatet og den private n\u00f8kkelen m\u00e5 konfigureres sammen" }, @@ -104,7 +109,10 @@ "set_ca_cert": "Validering av meglersertifikat", "set_client_cert": "Bruk et klientsertifikat", "tls_insecure": "Ignorer validering av meglersertifikat", - "username": "Brukernavn" + "transport": "MQTT transport", + "username": "Brukernavn", + "ws_headers": "WebSocket-hoder i JSON-format", + "ws_path": "WebSocket-bane" }, "description": "Vennligst fyll ut tilkoblingsinformasjonen for din MQTT megler.", "title": "Megleralternativer" diff --git a/homeassistant/components/mqtt/translations/pt-BR.json b/homeassistant/components/mqtt/translations/pt-BR.json index 653b567b2f5..1735edfdd2a 100644 --- a/homeassistant/components/mqtt/translations/pt-BR.json +++ b/homeassistant/components/mqtt/translations/pt-BR.json @@ -12,6 +12,7 @@ "bad_client_key": "Chave privada inv\u00e1lida, certifique-se de que um arquivo codificado PEM seja fornecido sem senha", "bad_discovery_prefix": "Prefixo de descoberta inv\u00e1lido", "bad_will": "T\u00f3pico de vontade inv\u00e1lido", + "bad_ws_headers": "Forne\u00e7a cabe\u00e7alhos HTTP v\u00e1lidos como um objeto JSON", "cannot_connect": "Falha ao conectar", "invalid_inclusion": "O certificado do cliente e a chave privada devem ser configurados juntos" }, @@ -31,7 +32,10 @@ "set_ca_cert": "Valida\u00e7\u00e3o do certificado do corretor", "set_client_cert": "Usar um certificado de cliente", "tls_insecure": "Ignorar a valida\u00e7\u00e3o do certificado do corretor", - "username": "Usu\u00e1rio" + "transport": "Transporte MQTT", + "username": "Usu\u00e1rio", + "ws_headers": "Cabe\u00e7alhos WebSocket no formato JSON", + "ws_path": "Caminho do WebSocket" }, "description": "Por favor, insira as informa\u00e7\u00f5es de conex\u00e3o do seu agente MQTT." }, @@ -85,6 +89,7 @@ "bad_client_key": "Chave privada inv\u00e1lida, certifique-se de que um arquivo codificado PEM seja fornecido sem senha", "bad_discovery_prefix": "Prefixo de descoberta inv\u00e1lido", "bad_will": "T\u00f3pico de vontade inv\u00e1lido", + "bad_ws_headers": "Forne\u00e7a cabe\u00e7alhos HTTP v\u00e1lidos como um objeto JSON", "cannot_connect": "Falha ao conectar", "invalid_inclusion": "O certificado do cliente e a chave privada devem ser configurados juntos" }, @@ -104,7 +109,10 @@ "set_ca_cert": "Valida\u00e7\u00e3o do certificado do corretor", "set_client_cert": "Usar um certificado de cliente", "tls_insecure": "Ignorar a valida\u00e7\u00e3o do certificado do corretor", - "username": "Usu\u00e1rio" + "transport": "Transporte MQTT", + "username": "Usu\u00e1rio", + "ws_headers": "Cabe\u00e7alhos WebSocket no formato JSON", + "ws_path": "Caminho do WebSocket" }, "description": "Por favor, insira as informa\u00e7\u00f5es de conex\u00e3o do seu agente MQTT.", "title": "Op\u00e7\u00f5es do broker" diff --git a/homeassistant/components/mqtt/translations/ru.json b/homeassistant/components/mqtt/translations/ru.json index ea478b5ac20..acbf55512a2 100644 --- a/homeassistant/components/mqtt/translations/ru.json +++ b/homeassistant/components/mqtt/translations/ru.json @@ -12,6 +12,7 @@ "bad_client_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0444\u0430\u0439\u043b \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 PEM \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f.", "bad_discovery_prefix": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u0442\u043e\u043f\u0438\u043a\u0430 \u0430\u0432\u0442\u043e\u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f.", "bad_will": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u043e\u043f\u0438\u043a \u043e\u0431 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438.", + "bad_ws_headers": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 HTTP \u0432 \u0432\u0438\u0434\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 JSON.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_inclusion": "\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0432\u043c\u0435\u0441\u0442\u0435." }, @@ -31,7 +32,10 @@ "set_ca_cert": "\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0431\u0440\u043e\u043a\u0435\u0440\u0430", "set_client_cert": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430", "tls_insecure": "\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0431\u0440\u043e\u043a\u0435\u0440\u0430", - "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + "transport": "\u0422\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442 MQTT", + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", + "ws_headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 WebSocket \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON", + "ws_path": "\u041f\u0443\u0442\u044c \u043a WebSocket" }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a \u0412\u0430\u0448\u0435\u043c\u0443 \u0431\u0440\u043e\u043a\u0435\u0440\u0443 MQTT." }, @@ -81,6 +85,7 @@ "bad_client_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0444\u0430\u0439\u043b \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 PEM \u0431\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u044f.", "bad_discovery_prefix": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u0442\u043e\u043f\u0438\u043a\u0430 \u0430\u0432\u0442\u043e\u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f.", "bad_will": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u043e\u043f\u0438\u043a \u043e\u0431 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438.", + "bad_ws_headers": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 HTTP \u0432 \u0432\u0438\u0434\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 JSON.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_inclusion": "\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0432\u043c\u0435\u0441\u0442\u0435." }, @@ -100,7 +105,10 @@ "set_ca_cert": "\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0431\u0440\u043e\u043a\u0435\u0440\u0430", "set_client_cert": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430", "tls_insecure": "\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0431\u0440\u043e\u043a\u0435\u0440\u0430", - "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + "transport": "\u0422\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442 MQTT", + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", + "ws_headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 WebSocket \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON", + "ws_path": "\u041f\u0443\u0442\u044c \u043a WebSocket" }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a \u0412\u0430\u0448\u0435\u043c\u0443 \u0431\u0440\u043e\u043a\u0435\u0440\u0443 MQTT.", "title": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0411\u0440\u043e\u043a\u0435\u0440\u0430" diff --git a/homeassistant/components/mqtt/translations/zh-Hant.json b/homeassistant/components/mqtt/translations/zh-Hant.json index 2916f6f5065..110eac492c5 100644 --- a/homeassistant/components/mqtt/translations/zh-Hant.json +++ b/homeassistant/components/mqtt/translations/zh-Hant.json @@ -12,6 +12,7 @@ "bad_client_key": "\u79c1\u9470\u7121\u6548\u3001\u8acb\u78ba\u5b9a\u63d0\u4f9b\u70ba PEM \u7de8\u78bc\u6a94\u6848\u4e26\u4e0d\u5177\u5bc6\u78bc", "bad_discovery_prefix": "\u641c\u7d22\u4e3b\u984c prefix \u7121\u6548", "bad_will": "Will \u4e3b\u984c\u7121\u6548", + "bad_ws_headers": "\u63d0\u4f9b\u6709\u6548\u7684 HTTP header \u4f5c\u70ba JSON \u7269\u4ef6", "cannot_connect": "\u9023\u7dda\u5931\u6557", "invalid_inclusion": "\u5ba2\u6236\u7aef\u6191\u8b49\u8207\u79c1\u9470\u5fc5\u9808\u4e00\u8d77\u8a2d\u5b9a" }, @@ -31,7 +32,10 @@ "set_ca_cert": "\u4ee3\u7406\u4f3a\u670d\u5668\u6191\u8b49\u9a57\u8b49", "set_client_cert": "\u4f7f\u7528\u5ba2\u6236\u7aef\u6191\u8b49", "tls_insecure": "\u5ffd\u7565\u4ee3\u7406\u4f3a\u670d\u5668\u6191\u8b49\u9a57\u8b49", - "username": "\u4f7f\u7528\u8005\u540d\u7a31" + "transport": "MQTT \u50b3\u8f38", + "username": "\u4f7f\u7528\u8005\u540d\u7a31", + "ws_headers": "JSON \u683c\u5f0f\u7684 WebSocket header", + "ws_path": "WebSocket \u8def\u5f91" }, "description": "\u8acb\u8f38\u5165 MQTT Broker \u9023\u7dda\u8cc7\u8a0a\u3002" }, @@ -85,6 +89,7 @@ "bad_client_key": "\u79c1\u9470\u7121\u6548\u3001\u8acb\u78ba\u5b9a\u63d0\u4f9b\u70ba PEM \u7de8\u78bc\u6a94\u6848\u4e26\u4e0d\u5177\u5bc6\u78bc", "bad_discovery_prefix": "\u641c\u7d22\u4e3b\u984c prefix \u7121\u6548", "bad_will": "Will \u4e3b\u984c\u7121\u6548", + "bad_ws_headers": "\u63d0\u4f9b\u6709\u6548\u7684 HTTP header \u4f5c\u70ba JSON \u7269\u4ef6", "cannot_connect": "\u9023\u7dda\u5931\u6557", "invalid_inclusion": "\u5ba2\u6236\u7aef\u6191\u8b49\u8207\u79c1\u9470\u5fc5\u9808\u4e00\u8d77\u8a2d\u5b9a" }, @@ -104,7 +109,10 @@ "set_ca_cert": "\u4ee3\u7406\u4f3a\u670d\u5668\u6191\u8b49\u9a57\u8b49", "set_client_cert": "\u4f7f\u7528\u5ba2\u6236\u7aef\u6191\u8b49", "tls_insecure": "\u5ffd\u7565\u4ee3\u7406\u4f3a\u670d\u5668\u6191\u8b49\u9a57\u8b49", - "username": "\u4f7f\u7528\u8005\u540d\u7a31" + "transport": "MQTT \u50b3\u8f38", + "username": "\u4f7f\u7528\u8005\u540d\u7a31", + "ws_headers": "JSON \u683c\u5f0f\u7684 WebSocket header", + "ws_path": "WebSocket \u8def\u5f91" }, "description": "\u8acb\u8f38\u5165 MQTT Broker \u9023\u7dda\u8cc7\u8a0a\u3002", "title": "Broker \u9078\u9805" diff --git a/homeassistant/components/myq/translations/sk.json b/homeassistant/components/myq/translations/sk.json index bd3a9e24cf1..8ff8168d1e7 100644 --- a/homeassistant/components/myq/translations/sk.json +++ b/homeassistant/components/myq/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/mysensors/translations/sk.json b/homeassistant/components/mysensors/translations/sk.json index c5206b30284..2daeb9e3561 100644 --- a/homeassistant/components/mysensors/translations/sk.json +++ b/homeassistant/components/mysensors/translations/sk.json @@ -2,6 +2,8 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "duplicate_persistence_file": "Perzistentn\u00fd s\u00fabor sa u\u017e pou\u017e\u00edva", + "duplicate_topic": "T\u00e9ma sa u\u017e pou\u017e\u00edva", "invalid_auth": "Neplatn\u00e9 overenie", "invalid_device": "Neplatn\u00e9 zariadenie", "invalid_ip": "Neplatn\u00e1 IP adresa", @@ -13,6 +15,8 @@ "error": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "duplicate_persistence_file": "Perzistentn\u00fd s\u00fabor sa u\u017e pou\u017e\u00edva", + "duplicate_topic": "T\u00e9ma sa u\u017e pou\u017e\u00edva", "invalid_auth": "Neplatn\u00e9 overenie", "invalid_device": "Neplatn\u00e9 zariadenie", "invalid_ip": "Neplatn\u00e1 IP adresa", diff --git a/homeassistant/components/nest/translations/sk.json b/homeassistant/components/nest/translations/sk.json index 038baf1ff9a..4781474c4e6 100644 --- a/homeassistant/components/nest/translations/sk.json +++ b/homeassistant/components/nest/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "create_entry": { diff --git a/homeassistant/components/nextdns/translations/sk.json b/homeassistant/components/nextdns/translations/sk.json new file mode 100644 index 00000000000..e2db0e016f7 --- /dev/null +++ b/homeassistant/components/nextdns/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Tento profil NextDNS je u\u017e nakonfigurovan\u00fd." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nibe_heatpump/translations/sk.json b/homeassistant/components/nibe_heatpump/translations/sk.json new file mode 100644 index 00000000000..062077bf047 --- /dev/null +++ b/homeassistant/components/nibe_heatpump/translations/sk.json @@ -0,0 +1,14 @@ +{ + "config": { + "error": { + "address_in_use": "Vybran\u00fd port po\u010d\u00favania sa u\u017e v tomto syst\u00e9me pou\u017e\u00edva." + }, + "step": { + "nibegw": { + "data": { + "ip_address": "Vzdialen\u00e1 adresa" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nmap_tracker/translations/sk.json b/homeassistant/components/nmap_tracker/translations/sk.json index c255a357016..19d1a53c160 100644 --- a/homeassistant/components/nmap_tracker/translations/sk.json +++ b/homeassistant/components/nmap_tracker/translations/sk.json @@ -1,12 +1,31 @@ { "config": { + "abort": { + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_hosts": "Neplatn\u00ed hostitelia" + }, + "step": { + "user": { + "data": { + "exclude": "Sie\u0165ov\u00e9 adresy (oddelen\u00e9 \u010diarkou), ktor\u00e9 sa maj\u00fa vyl\u00fa\u010di\u0165 z kontroly", + "hosts": "Sie\u0165ov\u00e9 adresy (oddelen\u00e9 \u010diarkami), ktor\u00e9 sa maj\u00fa skenova\u0165" + } + } } }, "options": { "error": { "invalid_hosts": "Neplatn\u00ed hostitelia" + }, + "step": { + "init": { + "data": { + "hosts": "Sie\u0165ov\u00e9 adresy (oddelen\u00e9 \u010diarkami), ktor\u00e9 sa maj\u00fa skenova\u0165", + "interval_seconds": "Interval skenovania" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/nobo_hub/translations/sk.json b/homeassistant/components/nobo_hub/translations/sk.json index 793f8eff278..ff1d25e970f 100644 --- a/homeassistant/components/nobo_hub/translations/sk.json +++ b/homeassistant/components/nobo_hub/translations/sk.json @@ -2,6 +2,16 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "invalid_ip": "Neplatn\u00e1 IP adresa" + }, + "step": { + "manual": { + "data": { + "ip_address": "IP adresa" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/notion/translations/sk.json b/homeassistant/components/notion/translations/sk.json index bd3a9e24cf1..5520e679328 100644 --- a/homeassistant/components/notion/translations/sk.json +++ b/homeassistant/components/notion/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/nws/translations/sk.json b/homeassistant/components/nws/translations/sk.json index abb3969f6b4..5a78acc060d 100644 --- a/homeassistant/components/nws/translations/sk.json +++ b/homeassistant/components/nws/translations/sk.json @@ -1,12 +1,16 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, "step": { "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka" - } + }, + "title": "Pripojenie k N\u00e1rodnej meteorologickej slu\u017ebe" } } } diff --git a/homeassistant/components/nzbget/translations/sk.json b/homeassistant/components/nzbget/translations/sk.json index dbffe2972bc..8946f220e6b 100644 --- a/homeassistant/components/nzbget/translations/sk.json +++ b/homeassistant/components/nzbget/translations/sk.json @@ -11,5 +11,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Frekvencia aktualiz\u00e1cie (sekundy)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/oncue/translations/sk.json b/homeassistant/components/oncue/translations/sk.json index a74f3ca1a9f..46f45d53967 100644 --- a/homeassistant/components/oncue/translations/sk.json +++ b/homeassistant/components/oncue/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" diff --git a/homeassistant/components/open_meteo/translations/es.json b/homeassistant/components/open_meteo/translations/es.json index 87bc3b879be..f8cde4cbba9 100644 --- a/homeassistant/components/open_meteo/translations/es.json +++ b/homeassistant/components/open_meteo/translations/es.json @@ -5,7 +5,7 @@ "data": { "zone": "Zona" }, - "description": "Selecciona la ubicaci\u00f3n que se usar\u00e1 para el pron\u00f3stico del tiempo" + "description": "Selecciona la ubicaci\u00f3n que se usar\u00e1 para la previsi\u00f3n meteorol\u00f3gica" } } } diff --git a/homeassistant/components/openexchangerates/translations/sk.json b/homeassistant/components/openexchangerates/translations/sk.json new file mode 100644 index 00000000000..f04d4a327f4 --- /dev/null +++ b/homeassistant/components/openexchangerates/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/opentherm_gw/translations/sk.json b/homeassistant/components/opentherm_gw/translations/sk.json index e7a2eaabb7b..e2b3ddda5b3 100644 --- a/homeassistant/components/opentherm_gw/translations/sk.json +++ b/homeassistant/components/opentherm_gw/translations/sk.json @@ -1,5 +1,9 @@ { "config": { + "error": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "id_exists": "ID br\u00e1ny u\u017e existuje" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/openuv/translations/sk.json b/homeassistant/components/openuv/translations/sk.json index 19eed2a3fe1..322fcfa33a3 100644 --- a/homeassistant/components/openuv/translations/sk.json +++ b/homeassistant/components/openuv/translations/sk.json @@ -18,6 +18,16 @@ } } }, + "issues": { + "deprecated_service_multiple_alternate_targets": { + "description": "Aktualizujte v\u0161etky automatiz\u00e1cie alebo skripty, ktor\u00e9 pou\u017e\u00edvaj\u00fa t\u00fato slu\u017ebu, aby namiesto nej pou\u017e\u00edvali slu\u017ebu `{alternate_service}` s jedn\u00fdm z t\u00fdchto ID ent\u00edt ako cie\u013eom: `{alternate_targets}`.", + "title": "Slu\u017eba {deprecated_service} sa odstra\u0148uje" + }, + "deprecated_service_single_alternate_target": { + "description": "Aktualizujte v\u0161etky automatiz\u00e1cie alebo skripty, ktor\u00e9 pou\u017e\u00edvaj\u00fa t\u00fato slu\u017ebu, aby namiesto nej pou\u017e\u00edvali slu\u017ebu `{alternate_service}` s cie\u013eom `{alternate_targets}`.", + "title": "Slu\u017eba {deprecated_service} sa odstra\u0148uje" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/oralb/translations/sk.json b/homeassistant/components/oralb/translations/sk.json index 25ca828afe4..48a6116c8ee 100644 --- a/homeassistant/components/oralb/translations/sk.json +++ b/homeassistant/components/oralb/translations/sk.json @@ -2,8 +2,16 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "not_supported": "Zariadenie nie je podporovan\u00e9" + }, + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/sensor.sk.json b/homeassistant/components/overkiz/translations/sensor.sk.json index ba996535149..3ce2cb1aea5 100644 --- a/homeassistant/components/overkiz/translations/sensor.sk.json +++ b/homeassistant/components/overkiz/translations/sensor.sk.json @@ -1,6 +1,7 @@ { "state": { "overkiz__battery": { + "full": "Pln\u00e1", "low": "N\u00edzke", "normal": "Norm\u00e1lne", "verylow": "Ve\u013emi n\u00edzke" @@ -19,6 +20,9 @@ "user": "Pou\u017e\u00edvate\u013e", "wind": "Vietor" }, + "overkiz__sensor_defect": { + "low_battery": "Slab\u00e1 bat\u00e9ria" + }, "overkiz__three_way_handle_direction": { "closed": "Zatvoren\u00e9", "open": "Otvori\u0165" diff --git a/homeassistant/components/overkiz/translations/sk.json b/homeassistant/components/overkiz/translations/sk.json index c9c2a93b5a9..46a71cd5811 100644 --- a/homeassistant/components/overkiz/translations/sk.json +++ b/homeassistant/components/overkiz/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/ovo_energy/translations/sk.json b/homeassistant/components/ovo_energy/translations/sk.json index 053375ea711..5bec28db5ed 100644 --- a/homeassistant/components/ovo_energy/translations/sk.json +++ b/homeassistant/components/ovo_energy/translations/sk.json @@ -1,13 +1,15 @@ { "config": { "error": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { "reauth": { "data": { "password": "Heslo" - } + }, + "description": "Autentifik\u00e1cia pre OVO Energy zlyhala. Zadajte svoje aktu\u00e1lne poverenia." }, "user": { "data": { diff --git a/homeassistant/components/panasonic_viera/translations/sk.json b/homeassistant/components/panasonic_viera/translations/sk.json index 0bf52ff8f0f..7af031dddc0 100644 --- a/homeassistant/components/panasonic_viera/translations/sk.json +++ b/homeassistant/components/panasonic_viera/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/peco/translations/sk.json b/homeassistant/components/peco/translations/sk.json new file mode 100644 index 00000000000..f04d4a327f4 --- /dev/null +++ b/homeassistant/components/peco/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/plaato/translations/sk.json b/homeassistant/components/plaato/translations/sk.json index a81e0400e19..6d0d7cebc1c 100644 --- a/homeassistant/components/plaato/translations/sk.json +++ b/homeassistant/components/plaato/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "step": { "user": { "data": { @@ -7,5 +10,15 @@ } } } + }, + "options": { + "step": { + "user": { + "data": { + "update_interval": "Interval aktualiz\u00e1cie (v min\u00fatach)" + }, + "description": "Nastavte interval aktualiz\u00e1cie (v min\u00fatach)" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plex/translations/sk.json b/homeassistant/components/plex/translations/sk.json index b28db64fe23..31d35b4827c 100644 --- a/homeassistant/components/plex/translations/sk.json +++ b/homeassistant/components/plex/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "all_configured": "V\u0161etky prepojen\u00e9 servery s\u00fa u\u017e nakonfigurovan\u00e9", "already_configured": "Tento Plex server u\u017e je nakonfigurovan\u00fd", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", diff --git a/homeassistant/components/plugwise/translations/sk.json b/homeassistant/components/plugwise/translations/sk.json index b8437f98a1e..24b4520c72c 100644 --- a/homeassistant/components/plugwise/translations/sk.json +++ b/homeassistant/components/plugwise/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/plum_lightpad/translations/sk.json b/homeassistant/components/plum_lightpad/translations/sk.json index cc9161d1644..b36de9a2ce5 100644 --- a/homeassistant/components/plum_lightpad/translations/sk.json +++ b/homeassistant/components/plum_lightpad/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/point/translations/sk.json b/homeassistant/components/point/translations/sk.json index c19b1a0b70c..25f026c0a69 100644 --- a/homeassistant/components/point/translations/sk.json +++ b/homeassistant/components/point/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_setup": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" } diff --git a/homeassistant/components/powerwall/translations/sk.json b/homeassistant/components/powerwall/translations/sk.json index 0ac8e2c85cf..f15f2cd038f 100644 --- a/homeassistant/components/powerwall/translations/sk.json +++ b/homeassistant/components/powerwall/translations/sk.json @@ -19,6 +19,7 @@ }, "user": { "data": { + "ip_address": "IP adresa", "password": "Heslo" } } diff --git a/homeassistant/components/ps4/translations/sk.json b/homeassistant/components/ps4/translations/sk.json index 4a31453350b..ac8eab24ba0 100644 --- a/homeassistant/components/ps4/translations/sk.json +++ b/homeassistant/components/ps4/translations/sk.json @@ -4,11 +4,24 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "error": { + "credential_timeout": "\u010casov\u00fd limit slu\u017eby poveren\u00ed vypr\u0161al. Re\u0161tartujte stla\u010den\u00edm tla\u010didla Odosla\u0165.", + "no_ipaddress": "Zadajte IP adresa konzoly PlayStation 4, ktor\u00fa chcete nakonfigurova\u0165." + }, "step": { "link": { "data": { + "ip_address": "IP adresa", "name": "N\u00e1zov" } + }, + "mode": { + "data": { + "ip_address": "IP adresa (Ak pou\u017e\u00edvate automatick\u00e9 zis\u0165ovanie, nechajte pr\u00e1zdne)." + }, + "data_description": { + "ip_address": "Ak vyberiete automatick\u00e9 zis\u0165ovanie, ponechajte pr\u00e1zdne." + } } } } diff --git a/homeassistant/components/pushover/translations/sk.json b/homeassistant/components/pushover/translations/sk.json new file mode 100644 index 00000000000..f04d4a327f4 --- /dev/null +++ b/homeassistant/components/pushover/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/sk.json b/homeassistant/components/pvpc_hourly_pricing/translations/sk.json index bf9a293e4f8..009602df740 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/sk.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/sk.json @@ -1,9 +1,24 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, "step": { "user": { "data": { - "name": "N\u00e1zov sn\u00edma\u010da" + "name": "N\u00e1zov sn\u00edma\u010da", + "power": "Zmluvn\u00fd v\u00fdkon (kW)", + "tariff": "Platn\u00e1 tarifa pod\u013ea geografickej z\u00f3ny" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "power": "Zmluvn\u00fd v\u00fdkon (kW)", + "tariff": "Platn\u00e1 tarifa pod\u013ea geografickej z\u00f3ny" } } } diff --git a/homeassistant/components/qingping/translations/sk.json b/homeassistant/components/qingping/translations/sk.json index 25ca828afe4..48a6116c8ee 100644 --- a/homeassistant/components/qingping/translations/sk.json +++ b/homeassistant/components/qingping/translations/sk.json @@ -2,8 +2,16 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "not_supported": "Zariadenie nie je podporovan\u00e9" + }, + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/rachio/translations/sk.json b/homeassistant/components/rachio/translations/sk.json index f5f4789c7fe..14ac4c6c068 100644 --- a/homeassistant/components/rachio/translations/sk.json +++ b/homeassistant/components/rachio/translations/sk.json @@ -13,5 +13,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "manual_run_mins": "Trvanie v min\u00fatach pri aktiv\u00e1cii sp\u00edna\u010da z\u00f3ny" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/radarr/translations/el.json b/homeassistant/components/radarr/translations/el.json index 005f18e5e69..e10f213a156 100644 --- a/homeassistant/components/radarr/translations/el.json +++ b/homeassistant/components/radarr/translations/el.json @@ -30,6 +30,10 @@ "deprecated_yaml": { "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Radarr \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03b5\u03b9\u03c3\u03b1\u03c7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Radarr YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Radarr YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" + }, + "removed_yaml": { + "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Radarr \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", + "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Radarr YAML \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c6\u03b1\u03b9\u03c1\u03b5\u03b8\u03b5\u03af" } }, "options": { diff --git a/homeassistant/components/radarr/translations/en.json b/homeassistant/components/radarr/translations/en.json index 0feccae33fd..928dc3a99f9 100644 --- a/homeassistant/components/radarr/translations/en.json +++ b/homeassistant/components/radarr/translations/en.json @@ -27,9 +27,13 @@ } }, "issues": { + "deprecated_yaml": { + "description": "Configuring Radarr using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Radarr YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", + "title": "The Radarr YAML configuration is being removed" + }, "removed_yaml": { - "title": "The Radarr YAML configuration has been removed", - "description": "Configuring Radarr using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue." + "description": "Configuring Radarr using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", + "title": "The Radarr YAML configuration has been removed" } }, "options": { diff --git a/homeassistant/components/radarr/translations/es.json b/homeassistant/components/radarr/translations/es.json index fe85efc0e20..c3d087fa3b0 100644 --- a/homeassistant/components/radarr/translations/es.json +++ b/homeassistant/components/radarr/translations/es.json @@ -30,6 +30,10 @@ "deprecated_yaml": { "description": "Se va a eliminar la configuraci\u00f3n de Radarr mediante YAML. \n\nTu configuraci\u00f3n YAML existente se ha importado a la IU autom\u00e1ticamente. \n\nElimina la configuraci\u00f3n YAML de Radarr de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", "title": "Se va a eliminar la configuraci\u00f3n YAML de Radarr" + }, + "removed_yaml": { + "description": "Se ha eliminado la configuraci\u00f3n de Radarr usando YAML. \n\nHome Assistant no utiliza tu configuraci\u00f3n YAML existente. \n\nElimina la configuraci\u00f3n YAML de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", + "title": "Se ha eliminado la configuraci\u00f3n YAML de Radarr" } }, "options": { diff --git a/homeassistant/components/radarr/translations/sk.json b/homeassistant/components/radarr/translations/sk.json new file mode 100644 index 00000000000..f04d4a327f4 --- /dev/null +++ b/homeassistant/components/radarr/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recollect_waste/translations/sk.json b/homeassistant/components/recollect_waste/translations/sk.json index 793f8eff278..09913d47d95 100644 --- a/homeassistant/components/recollect_waste/translations/sk.json +++ b/homeassistant/components/recollect_waste/translations/sk.json @@ -2,6 +2,16 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "invalid_place_or_service_id": "Neplatn\u00e9 ID miesta alebo slu\u017eby" + }, + "step": { + "user": { + "data": { + "service_id": "ID slu\u017eby" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/sk.json b/homeassistant/components/recorder/translations/sk.json new file mode 100644 index 00000000000..2e6c96765f9 --- /dev/null +++ b/homeassistant/components/recorder/translations/sk.json @@ -0,0 +1,7 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Aktu\u00e1lny \u010das spustenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/renault/translations/sk.json b/homeassistant/components/renault/translations/sk.json index a6e63951200..e3c85b89917 100644 --- a/homeassistant/components/renault/translations/sk.json +++ b/homeassistant/components/renault/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/rfxtrx/translations/sk.json b/homeassistant/components/rfxtrx/translations/sk.json index 0335755c3a5..b23f3bc47d2 100644 --- a/homeassistant/components/rfxtrx/translations/sk.json +++ b/homeassistant/components/rfxtrx/translations/sk.json @@ -1,11 +1,15 @@ { "config": { + "abort": { + "already_configured": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "step": { "setup_network": { "data": { "host": "Hostite\u013e", "port": "Port" - } + }, + "title": "Vyberte adresu pripojenia" }, "setup_serial": { "data": { diff --git a/homeassistant/components/ridwell/translations/sk.json b/homeassistant/components/ridwell/translations/sk.json index bd3a9e24cf1..5520e679328 100644 --- a/homeassistant/components/ridwell/translations/sk.json +++ b/homeassistant/components/ridwell/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/ring/translations/sk.json b/homeassistant/components/ring/translations/sk.json index 1b1e671c054..8f06d99549b 100644 --- a/homeassistant/components/ring/translations/sk.json +++ b/homeassistant/components/ring/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/scrape/translations/it.json b/homeassistant/components/scrape/translations/it.json index b20393efb93..6367fd70613 100644 --- a/homeassistant/components/scrape/translations/it.json +++ b/homeassistant/components/scrape/translations/it.json @@ -3,13 +3,39 @@ "abort": { "already_configured": "L'account \u00e8 gi\u00e0 configurato" }, + "error": { + "resource_error": "Impossibile aggiornare i dati rest. Verificare la configurazione" + }, "step": { + "sensor": { + "data": { + "attribute": "Attributo", + "device_class": "Classe del dispositivo", + "index": "Indice", + "name": "Nome", + "select": "Seleziona", + "state_class": "Classe di stato", + "unit_of_measurement": "Unit\u00e0 di misura", + "value_template": "Modello di valore" + }, + "data_description": { + "attribute": "Ottenere il valore di un attributo dell'etichetta selezionata", + "device_class": "Il tipo/classe del sensore per impostare l'icona nel frontend", + "index": "Definisce quale degli elementi restituiti dal selettore CSS utilizzare", + "select": "Definisce quale etichetta cercare. Controlla i selettori CSS di Beautifulsoup per i dettagli", + "state_class": "La state_class del sensore", + "unit_of_measurement": "Scegli l'unit\u00e0 di misura della temperatura o crearne una tua", + "value_template": "Definisce un modello per ottenere lo stato del sensore" + } + }, "user": { "data": { "authentication": "Autenticazione", "headers": "Intestazioni", + "method": "Metodo", "password": "Password", "resource": "Risorsa", + "timeout": "Tempo scaduto", "username": "Nome utente", "verify_ssl": "Verifica il certificato SSL" }, @@ -17,6 +43,7 @@ "authentication": "Tipo di autenticazione HTTP. basic o digest", "headers": "Intestazioni da utilizzare per la richiesta web", "resource": "L'URL del sito Web che contiene il valore", + "timeout": "Tiempo scaduto per la connessione al sito web", "verify_ssl": "Abilita/disabilita la verifica del certificato SSL/TLS, ad esempio se \u00e8 autofirmato" } } @@ -34,8 +61,10 @@ "data": { "authentication": "Autenticazione", "headers": "Intestazioni", + "method": "Metodo", "password": "Password", "resource": "Risorsa", + "timeout": "Tempo scaduto", "username": "Nome utente", "verify_ssl": "Verifica il certificato SSL" }, @@ -43,6 +72,7 @@ "authentication": "Tipo di autenticazione HTTP. basic o digest", "headers": "Intestazioni da utilizzare per la richiesta web", "resource": "L'URL del sito Web che contiene il valore", + "timeout": "Tiempo scaduto per la connessione al sito web", "verify_ssl": "Abilita/disabilita la verifica del certificato SSL/TLS, ad esempio se \u00e8 autofirmato" } } diff --git a/homeassistant/components/scrape/translations/pt-BR.json b/homeassistant/components/scrape/translations/pt-BR.json index 4a10e16d1c0..5654259df77 100644 --- a/homeassistant/components/scrape/translations/pt-BR.json +++ b/homeassistant/components/scrape/translations/pt-BR.json @@ -3,13 +3,39 @@ "abort": { "already_configured": "A conta j\u00e1 foi configurada" }, + "error": { + "resource_error": "N\u00e3o foi poss\u00edvel atualizar os dados de descanso. Verifique sua configura\u00e7\u00e3o" + }, "step": { + "sensor": { + "data": { + "attribute": "Atributo", + "device_class": "Classe do dispositivo", + "index": "\u00cdndice", + "name": "Nome", + "select": "Selecione", + "state_class": "Classe do Estado", + "unit_of_measurement": "Unidade de medida", + "value_template": "Modelo de valor" + }, + "data_description": { + "attribute": "Obtenha o valor de um atributo na tag selecionada", + "device_class": "O tipo/classe do sensor para definir o \u00edcone no frontend", + "index": "Define qual dos elementos retornados pelo seletor CSS usar", + "select": "Define qual tag procurar. Verifique os seletores CSS do Beautifulsoup para obter detalhes", + "state_class": "O estado_classe do sensor", + "unit_of_measurement": "Escolha a medi\u00e7\u00e3o de temperatura ou crie a sua pr\u00f3pria", + "value_template": "Define um modelo para obter o estado do sensor" + } + }, "user": { "data": { - "authentication": "Autentica\u00e7\u00e3o", + "authentication": "Selecione o m\u00e9todo de autentica\u00e7\u00e3o", "headers": "Cabe\u00e7alhos", + "method": "M\u00e9todo", "password": "Senha", "resource": "Recurso", + "timeout": "Tempo limite", "username": "Usu\u00e1rio", "verify_ssl": "Verifique o certificado SSL" }, @@ -17,6 +43,7 @@ "authentication": "Tipo de autentica\u00e7\u00e3o HTTP. b\u00e1sica ou digerida", "headers": "Cabe\u00e7alhos a serem usados para a solicita\u00e7\u00e3o da web", "resource": "A URL para o site que cont\u00e9m o valor", + "timeout": "Tempo limite para conex\u00e3o com o site", "verify_ssl": "Ativa/desativa a verifica\u00e7\u00e3o do certificado SSL/TLS, por exemplo, se for autoassinado" } } @@ -32,10 +59,12 @@ "step": { "init": { "data": { - "authentication": "Autentica\u00e7\u00e3o", + "authentication": "Selecione o m\u00e9todo de autentica\u00e7\u00e3o", "headers": "Cabe\u00e7alhos", + "method": "M\u00e9todo", "password": "Senha", "resource": "Recurso", + "timeout": "Tempo limite", "username": "Usu\u00e1rio", "verify_ssl": "Verificar SSL" }, @@ -43,6 +72,7 @@ "authentication": "Tipo de autentica\u00e7\u00e3o HTTP. b\u00e1sica ou digerida", "headers": "Cabe\u00e7alhos a serem usados para a solicita\u00e7\u00e3o da web", "resource": "A URL para o site que cont\u00e9m o valor", + "timeout": "Tempo limite para conex\u00e3o com o site", "verify_ssl": "Ativa/desativa a verifica\u00e7\u00e3o do certificado SSL/TLS, por exemplo, se for autoassinado" } } diff --git a/homeassistant/components/scrape/translations/sk.json b/homeassistant/components/scrape/translations/sk.json index cb2ca5a6b94..33439335185 100644 --- a/homeassistant/components/scrape/translations/sk.json +++ b/homeassistant/components/scrape/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "step": { "sensor": { "data": { diff --git a/homeassistant/components/scrape/translations/zh-Hant.json b/homeassistant/components/scrape/translations/zh-Hant.json index 7bf8cd4a82d..f923208c232 100644 --- a/homeassistant/components/scrape/translations/zh-Hant.json +++ b/homeassistant/components/scrape/translations/zh-Hant.json @@ -3,13 +3,39 @@ "abort": { "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" }, + "error": { + "resource_error": "\u7121\u6cd5\u66f4\u65b0\u91cd\u7f6e\u8cc7\u6599\uff0c\u8acb\u78ba\u8a8d\u8a2d\u5b9a\u3002" + }, "step": { + "sensor": { + "data": { + "attribute": "\u5c6c\u6027", + "device_class": "\u88dd\u7f6e\u985e\u5225", + "index": "\u6307\u6578", + "name": "\u540d\u7a31", + "select": "\u9078\u64c7", + "state_class": "\u72c0\u614b\u985e\u5225", + "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d", + "value_template": "\u6578\u503c\u6a21\u677f" + }, + "data_description": { + "attribute": "\u7372\u53d6\u6240\u9078\u6a19\u7c64\u5c6c\u6027\u6578\u503c", + "device_class": "\u65bc Frontend \u4e2d\u8a2d\u5b9a\u4e4b\u50b3\u611f\u5668\u985e\u578b/\u985e\u5225\u5716\u793a", + "index": "\u5b9a\u7fa9\u4f7f\u7528 CSS selector \u56de\u8986\u5143\u7d20", + "select": "\u5b9a\u7fa9\u8981\u7d22\u7684\u6a19\u7c64\u3002\u53c3\u95b1 Beautifulsoup CSS selector \u4ee5\u7372\u5f97\u8a73\u7d30\u8cc7\u8a0a", + "state_class": "\u611f\u6e2c\u5668 state_class", + "unit_of_measurement": "\u9078\u64c7\u6eab\u5ea6\u6e2c\u91cf\u6216\u8005\u5275\u5efa\u81ea\u5b9a\u7fa9", + "value_template": "\u5b9a\u7fa9\u6a21\u677f\u4ee5\u53d6\u5f97\u611f\u6e2c\u5668\u72c0\u614b" + } + }, "user": { "data": { - "authentication": "\u9a57\u8b49", + "authentication": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f", "headers": "Headers", + "method": "\u65b9\u5f0f", "password": "\u5bc6\u78bc", "resource": "\u4f86\u6e90", + "timeout": "\u903e\u6642", "username": "\u4f7f\u7528\u8005\u540d\u7a31", "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" }, @@ -17,6 +43,7 @@ "authentication": "HTTP \u9a57\u8b49\u985e\u578b\u3002\u57fa\u672c\u6216\u6458\u8981", "headers": "\u7528\u65bc Web \u8acb\u6c42\u4e4b Headers", "resource": "\u5305\u542b\u6578\u503c\u7684\u7db2\u7ad9 URL", + "timeout": "\u7db2\u7ad9\u9023\u7dda\u903e\u6642", "verify_ssl": "\u958b\u555f/\u95dc\u9589 SSL/TLS \u9a57\u8b49\u8a8d\u8b49\uff0c\u4f8b\u5982\u81ea\u7c3d\u7ae0\u6191\u8b49" } } @@ -32,10 +59,12 @@ "step": { "init": { "data": { - "authentication": "\u9a57\u8b49", + "authentication": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f", "headers": "Headers", + "method": "\u65b9\u5f0f", "password": "\u5bc6\u78bc", "resource": "\u4f86\u6e90", + "timeout": "\u903e\u6642", "username": "\u4f7f\u7528\u8005\u540d\u7a31", "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" }, @@ -43,6 +72,7 @@ "authentication": "HTTP \u9a57\u8b49\u985e\u578b\u3002\u57fa\u672c\u6216\u6458\u8981", "headers": "\u7528\u65bc Web \u8acb\u6c42\u4e4b Headers", "resource": "\u5305\u542b\u6578\u503c\u7684\u7db2\u7ad9 URL", + "timeout": "\u7db2\u7ad9\u9023\u7dda\u903e\u6642", "verify_ssl": "\u958b\u555f/\u95dc\u9589 SSL/TLS \u9a57\u8b49\u8a8d\u8b49\uff0c\u4f8b\u5982\u81ea\u7c3d\u7ae0\u6191\u8b49" } } diff --git a/homeassistant/components/screenlogic/translations/sk.json b/homeassistant/components/screenlogic/translations/sk.json index 492389f5677..e1d8590f264 100644 --- a/homeassistant/components/screenlogic/translations/sk.json +++ b/homeassistant/components/screenlogic/translations/sk.json @@ -7,6 +7,7 @@ "step": { "gateway_entry": { "data": { + "ip_address": "IP adresa", "port": "Port" } } diff --git a/homeassistant/components/season/translations/sensor.sk.json b/homeassistant/components/season/translations/sensor.sk.json index 548b77defd0..58cc37ac3dd 100644 --- a/homeassistant/components/season/translations/sensor.sk.json +++ b/homeassistant/components/season/translations/sensor.sk.json @@ -3,6 +3,7 @@ "season__season": { "autumn": "Jese\u0148", "spring": "Jar", + "summer": "Leto", "winter": "Zima" }, "season__season__": { diff --git a/homeassistant/components/season/translations/sk.json b/homeassistant/components/season/translations/sk.json new file mode 100644 index 00000000000..2e87a326f7c --- /dev/null +++ b/homeassistant/components/season/translations/sk.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "step": { + "user": { + "data": { + "type": "Typ defin\u00edcie ro\u010dn\u00e9ho obdobia" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/select/translations/sk.json b/homeassistant/components/select/translations/sk.json new file mode 100644 index 00000000000..96441fb5f0d --- /dev/null +++ b/homeassistant/components/select/translations/sk.json @@ -0,0 +1,10 @@ +{ + "device_automation": { + "condition_type": { + "selected_option": "Aktu\u00e1lna vybrat\u00e1 mo\u017enos\u0165 {entity_name}" + }, + "trigger_type": { + "current_option_changed": "Mo\u017enos\u0165 {entity_name} sa zmenila" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senseme/translations/sk.json b/homeassistant/components/senseme/translations/sk.json index 2f000bd7e8d..a161f40537c 100644 --- a/homeassistant/components/senseme/translations/sk.json +++ b/homeassistant/components/senseme/translations/sk.json @@ -15,12 +15,14 @@ "manual": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Zadajte IP adresu." }, "user": { "data": { "device": "Zariadenie" - } + }, + "description": "Vyberte zariadenie alebo vyberte mo\u017enos\u0165 \"IP adresa\", ak chcete manu\u00e1lne zada\u0165 IP adresu." } } } diff --git a/homeassistant/components/sensibo/translations/sk.json b/homeassistant/components/sensibo/translations/sk.json index 630d49c3423..53902d72f41 100644 --- a/homeassistant/components/sensibo/translations/sk.json +++ b/homeassistant/components/sensibo/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { "incorrect_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d pre vybran\u00fd \u00fa\u010det", "no_devices": "Nena\u0161li sa \u017eiadne zariadenia" diff --git a/homeassistant/components/sensor/translations/sk.json b/homeassistant/components/sensor/translations/sk.json index 133573ea24e..943a00921be 100644 --- a/homeassistant/components/sensor/translations/sk.json +++ b/homeassistant/components/sensor/translations/sk.json @@ -1,7 +1,39 @@ { "device_automation": { "condition_type": { - "is_voltage": "S\u00fa\u010dasn\u00e9 nap\u00e4tie {entity_name}" + "is_apparent_power": "Aktu\u00e1lny zdanliv\u00fd v\u00fdkon {entity_name}", + "is_battery_level": "Aktu\u00e1lna \u00farove\u0148 nabitia bat\u00e9rie {entity_name}", + "is_carbon_dioxide": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie oxidu uhli\u010dit\u00e9ho {entity_name}", + "is_carbon_monoxide": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie oxidu uho\u013enat\u00e9ho {entity_name}", + "is_current": "Aktu\u00e1lny {entity_name} pr\u00fad", + "is_distance": "Aktu\u00e1lna vzdialenos\u0165 {entity_name}", + "is_energy": "Aktu\u00e1lna energia {entity_name}", + "is_frequency": "Aktu\u00e1lna frekvencia {entity_name}", + "is_gas": "Aktu\u00e1lny plyn {entity_name}", + "is_humidity": "Aktu\u00e1lna vlhkos\u0165 {entity_name}", + "is_illuminance": "Aktu\u00e1lne osvetlenie {entity_name}", + "is_moisture": "Aktu\u00e1lna vlhkos\u0165 {entity_name}", + "is_nitrogen_dioxide": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie oxidu dusi\u010dit\u00e9ho {entity_name}", + "is_nitrogen_monoxide": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie oxidu uho\u013enat\u00e9ho {entity_name}", + "is_nitrous_oxide": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie oxidu dusn\u00e9ho {entity_name}", + "is_ozone": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie oz\u00f3nu {entity_name}", + "is_pm1": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie {entity_name} PM1", + "is_pm10": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie {entity_name} PM10", + "is_pm25": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie {entity_name} PM2,5", + "is_power": "Aktu\u00e1lny v\u00fdkon {entity_name}", + "is_power_factor": "Aktu\u00e1lny \u00fa\u010dinn\u00edk {entity_name}", + "is_pressure": "Aktu\u00e1lny tlak {entity_name}", + "is_reactive_power": "Aktu\u00e1lny jalov\u00fd v\u00fdkon {entity_name}", + "is_signal_strength": "Aktu\u00e1lna sila sign\u00e1lu {entity_name}", + "is_speed": "Aktu\u00e1lna r\u00fdchlos\u0165 {entity_name}", + "is_sulphur_dioxide": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie oxidu siri\u010dit\u00e9ho {entity_name}", + "is_temperature": "Aktu\u00e1lna teplota {entity_name}", + "is_value": "Aktu\u00e1lna hodnota {entity_name}", + "is_volatile_organic_compounds": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie prchav\u00fdch organick\u00fdch zl\u00fa\u010den\u00edn {entity_name}", + "is_voltage": "S\u00fa\u010dasn\u00e9 nap\u00e4tie {entity_name}", + "is_volume": "Aktu\u00e1lny oddiel {entity_name}", + "is_water": "Aktu\u00e1lna voda {entity_name}", + "is_weight": "Aktu\u00e1lna hmotnos\u0165 {entity_name}" }, "trigger_type": { "current": "{entity_name} pr\u00fad sa zmen\u00ed", diff --git a/homeassistant/components/senz/translations/sk.json b/homeassistant/components/senz/translations/sk.json new file mode 100644 index 00000000000..1ec9120e295 --- /dev/null +++ b/homeassistant/components/senz/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sharkiq/translations/sk.json b/homeassistant/components/sharkiq/translations/sk.json index 82277c439cf..5fa187bfe4c 100644 --- a/homeassistant/components/sharkiq/translations/sk.json +++ b/homeassistant/components/sharkiq/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/sia/translations/sk.json b/homeassistant/components/sia/translations/sk.json index d703643c7de..f65e1400be1 100644 --- a/homeassistant/components/sia/translations/sk.json +++ b/homeassistant/components/sia/translations/sk.json @@ -1,6 +1,12 @@ { "config": { + "error": { + "invalid_ping": "Interval pingu mus\u00ed by\u0165 medzi 1 a 1440 min\u00fatami." + }, "step": { + "additional_account": { + "title": "Pridajte \u010fal\u0161\u00ed \u00fa\u010det k aktu\u00e1lnemu portu." + }, "user": { "data": { "port": "Port", diff --git a/homeassistant/components/simplisafe/translations/sk.json b/homeassistant/components/simplisafe/translations/sk.json index 71a7aea5018..c0e740e861a 100644 --- a/homeassistant/components/simplisafe/translations/sk.json +++ b/homeassistant/components/simplisafe/translations/sk.json @@ -1,10 +1,18 @@ { "config": { "abort": { + "already_configured": "Toto konto SimpliSafe sa u\u017e pou\u017e\u00edva.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "identifier_exists": "\u00da\u010det je u\u017e zaregistrovan\u00fd", "invalid_auth": "Neplatn\u00e9 overenie" } + }, + "issues": { + "deprecated_service": { + "description": "Aktualizujte v\u0161etky automatiz\u00e1cie alebo skripty, ktor\u00e9 pou\u017e\u00edvaj\u00fa t\u00fato slu\u017ebu, aby namiesto nej pou\u017e\u00edvali slu\u017ebu `{alternate_service}` s ID cie\u013eovej entity `{alternate_target}`. Potom kliknite na tla\u010didlo SUBMIT (Odosla\u0165) ni\u017e\u0161ie a ozna\u010dte tento probl\u00e9m ako vyrie\u0161en\u00fd.", + "title": "Slu\u017eba {deprecated_service} sa odstra\u0148uje" + } } } \ No newline at end of file diff --git a/homeassistant/components/skybell/translations/sk.json b/homeassistant/components/skybell/translations/sk.json index a262779e103..55eb9050870 100644 --- a/homeassistant/components/skybell/translations/sk.json +++ b/homeassistant/components/skybell/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "step": { "reauth_confirm": { "data": { diff --git a/homeassistant/components/slack/translations/sk.json b/homeassistant/components/slack/translations/sk.json index 4e2a5fa0ed7..a4674cfbe37 100644 --- a/homeassistant/components/slack/translations/sk.json +++ b/homeassistant/components/slack/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" diff --git a/homeassistant/components/sleepiq/translations/sk.json b/homeassistant/components/sleepiq/translations/sk.json index 4b1e8bf1a77..6dbe2a76791 100644 --- a/homeassistant/components/sleepiq/translations/sk.json +++ b/homeassistant/components/sleepiq/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/smarttub/translations/sk.json b/homeassistant/components/smarttub/translations/sk.json index bf7ebd86a62..610179dfac5 100644 --- a/homeassistant/components/smarttub/translations/sk.json +++ b/homeassistant/components/smarttub/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -11,7 +12,8 @@ "data": { "email": "Email", "password": "Heslo" - } + }, + "description": "Na prihl\u00e1senie zadajte svoju e-mailov\u00fa adresu a heslo SmartTub" } } } diff --git a/homeassistant/components/smhi/translations/sk.json b/homeassistant/components/smhi/translations/sk.json index e6945904d90..60b95d94721 100644 --- a/homeassistant/components/smhi/translations/sk.json +++ b/homeassistant/components/smhi/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/snooz/translations/sk.json b/homeassistant/components/snooz/translations/sk.json index 8444b9fbeae..a8fa63be59a 100644 --- a/homeassistant/components/snooz/translations/sk.json +++ b/homeassistant/components/snooz/translations/sk.json @@ -2,7 +2,15 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/solax/translations/sk.json b/homeassistant/components/solax/translations/sk.json index 599a1b7a304..fc02a749e8d 100644 --- a/homeassistant/components/solax/translations/sk.json +++ b/homeassistant/components/solax/translations/sk.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "ip_address": "IP adresa", "password": "Heslo", "port": "Port" } diff --git a/homeassistant/components/soma/translations/sk.json b/homeassistant/components/soma/translations/sk.json index c808fb0e695..aa78c3496ee 100644 --- a/homeassistant/components/soma/translations/sk.json +++ b/homeassistant/components/soma/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_setup": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" }, diff --git a/homeassistant/components/somfy_mylink/translations/sk.json b/homeassistant/components/somfy_mylink/translations/sk.json index 6f2d6c9c475..14225502e94 100644 --- a/homeassistant/components/somfy_mylink/translations/sk.json +++ b/homeassistant/components/somfy_mylink/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, @@ -8,7 +11,8 @@ "data": { "host": "Hostite\u013e", "port": "Port" - } + }, + "description": "Syst\u00e9mov\u00e9 ID je mo\u017en\u00e9 z\u00edska\u0165 v aplik\u00e1cii MyLink v \u010dasti Integr\u00e1cia v\u00fdberom akejko\u013evek inej ne\u017e cloudovej slu\u017eby." } } } diff --git a/homeassistant/components/sonarr/translations/sk.json b/homeassistant/components/sonarr/translations/sk.json index e6cc7a1767a..0ccb181be0e 100644 --- a/homeassistant/components/sonarr/translations/sk.json +++ b/homeassistant/components/sonarr/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/songpal/translations/sk.json b/homeassistant/components/songpal/translations/sk.json index fb1495d284c..09de84115f3 100644 --- a/homeassistant/components/songpal/translations/sk.json +++ b/homeassistant/components/songpal/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name} ({host})", "step": { "init": { diff --git a/homeassistant/components/sql/translations/sk.json b/homeassistant/components/sql/translations/sk.json index 474e7ffebe9..58b490fa9f0 100644 --- a/homeassistant/components/sql/translations/sk.json +++ b/homeassistant/components/sql/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/steam_online/translations/sk.json b/homeassistant/components/steam_online/translations/sk.json new file mode 100644 index 00000000000..f04d4a327f4 --- /dev/null +++ b/homeassistant/components/steam_online/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/stookalert/translations/sk.json b/homeassistant/components/stookalert/translations/sk.json new file mode 100644 index 00000000000..f04d4a327f4 --- /dev/null +++ b/homeassistant/components/stookalert/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/subaru/translations/sk.json b/homeassistant/components/subaru/translations/sk.json index 1b1e671c054..d50ce04d74a 100644 --- a/homeassistant/components/subaru/translations/sk.json +++ b/homeassistant/components/subaru/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/surepetcare/translations/sk.json b/homeassistant/components/surepetcare/translations/sk.json index 1b1e671c054..d50ce04d74a 100644 --- a/homeassistant/components/surepetcare/translations/sk.json +++ b/homeassistant/components/surepetcare/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/syncthing/translations/sk.json b/homeassistant/components/syncthing/translations/sk.json index 5ada995aa6e..ad320852cf5 100644 --- a/homeassistant/components/syncthing/translations/sk.json +++ b/homeassistant/components/syncthing/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" } diff --git a/homeassistant/components/syncthru/translations/sk.json b/homeassistant/components/syncthru/translations/sk.json index bd95d061070..ef73b0f8d70 100644 --- a/homeassistant/components/syncthru/translations/sk.json +++ b/homeassistant/components/syncthru/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "flow_title": "{name}", "step": { "confirm": { diff --git a/homeassistant/components/synology_dsm/translations/sk.json b/homeassistant/components/synology_dsm/translations/sk.json index e021f42bd88..ba7397614cb 100644 --- a/homeassistant/components/synology_dsm/translations/sk.json +++ b/homeassistant/components/synology_dsm/translations/sk.json @@ -29,5 +29,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Min\u00faty medzi skenovaniami" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/sk.json b/homeassistant/components/system_bridge/translations/sk.json index b099280098f..70d5bcbd97d 100644 --- a/homeassistant/components/system_bridge/translations/sk.json +++ b/homeassistant/components/system_bridge/translations/sk.json @@ -18,6 +18,7 @@ "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", + "host": "Hostite\u013e", "port": "Port" } } diff --git a/homeassistant/components/tankerkoenig/translations/sk.json b/homeassistant/components/tankerkoenig/translations/sk.json index 06c74b52725..2c694e299a7 100644 --- a/homeassistant/components/tankerkoenig/translations/sk.json +++ b/homeassistant/components/tankerkoenig/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/tautulli/translations/sk.json b/homeassistant/components/tautulli/translations/sk.json new file mode 100644 index 00000000000..f04d4a327f4 --- /dev/null +++ b/homeassistant/components/tautulli/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tellduslive/translations/sk.json b/homeassistant/components/tellduslive/translations/sk.json index 5ada995aa6e..1b2bf27f1fc 100644 --- a/homeassistant/components/tellduslive/translations/sk.json +++ b/homeassistant/components/tellduslive/translations/sk.json @@ -1,7 +1,17 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "host": "Hostite\u013e" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/text/translations/de.json b/homeassistant/components/text/translations/de.json new file mode 100644 index 00000000000..19b840cdb8f --- /dev/null +++ b/homeassistant/components/text/translations/de.json @@ -0,0 +1,3 @@ +{ + "title": "Text" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/el.json b/homeassistant/components/text/translations/el.json new file mode 100644 index 00000000000..89853ef69f6 --- /dev/null +++ b/homeassistant/components/text/translations/el.json @@ -0,0 +1,3 @@ +{ + "title": "\u039a\u03b5\u03af\u03bc\u03b5\u03bd\u03bf" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/en.json b/homeassistant/components/text/translations/en.json new file mode 100644 index 00000000000..19b840cdb8f --- /dev/null +++ b/homeassistant/components/text/translations/en.json @@ -0,0 +1,3 @@ +{ + "title": "Text" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/es.json b/homeassistant/components/text/translations/es.json new file mode 100644 index 00000000000..07996c47b61 --- /dev/null +++ b/homeassistant/components/text/translations/es.json @@ -0,0 +1,3 @@ +{ + "title": "Texto" +} \ No newline at end of file diff --git a/homeassistant/components/thermobeacon/translations/sk.json b/homeassistant/components/thermobeacon/translations/sk.json index 25ca828afe4..48a6116c8ee 100644 --- a/homeassistant/components/thermobeacon/translations/sk.json +++ b/homeassistant/components/thermobeacon/translations/sk.json @@ -2,8 +2,16 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "not_supported": "Zariadenie nie je podporovan\u00e9" + }, + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/thermopro/translations/sk.json b/homeassistant/components/thermopro/translations/sk.json index 8444b9fbeae..a8fa63be59a 100644 --- a/homeassistant/components/thermopro/translations/sk.json +++ b/homeassistant/components/thermopro/translations/sk.json @@ -2,7 +2,15 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/tibber/translations/sk.json b/homeassistant/components/tibber/translations/sk.json index 13ca7333f5c..06bc63983d0 100644 --- a/homeassistant/components/tibber/translations/sk.json +++ b/homeassistant/components/tibber/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/tile/translations/sk.json b/homeassistant/components/tile/translations/sk.json index d1d3d1408a9..c14faaae46e 100644 --- a/homeassistant/components/tile/translations/sk.json +++ b/homeassistant/components/tile/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/tilt_ble/translations/sk.json b/homeassistant/components/tilt_ble/translations/sk.json index 8444b9fbeae..a8fa63be59a 100644 --- a/homeassistant/components/tilt_ble/translations/sk.json +++ b/homeassistant/components/tilt_ble/translations/sk.json @@ -2,7 +2,15 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "step": { + "user": { + "data": { + "address": "Zaradenie" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/sk.json b/homeassistant/components/tolo/translations/sk.json index 835f107f7af..8430e2bd6f3 100644 --- a/homeassistant/components/tolo/translations/sk.json +++ b/homeassistant/components/tolo/translations/sk.json @@ -8,7 +8,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Zadajte n\u00e1zov hostite\u013ea alebo IP adresu v\u00e1\u0161ho zariadenia TOLO Sauna." } } } diff --git a/homeassistant/components/tomorrowio/translations/es.json b/homeassistant/components/tomorrowio/translations/es.json index 27eeea44515..c18fdffebbc 100644 --- a/homeassistant/components/tomorrowio/translations/es.json +++ b/homeassistant/components/tomorrowio/translations/es.json @@ -23,7 +23,7 @@ "data": { "timestep": "Min. entre previsiones de NowCast" }, - "description": "Si eliges habilitar la entidad de pron\u00f3stico del tiempo `nowcast`, puedes configurar la cantidad de minutos entre cada pron\u00f3stico. La cantidad de pron\u00f3sticos proporcionados depende de la cantidad de minutos elegidos entre los pron\u00f3sticos.", + "description": "Si eliges habilitar la entidad de previsi\u00f3n `nowcast`, puedes configurar la cantidad de minutos entre cada previsi\u00f3n. La cantidad de previsiones proporcionadas depende de la cantidad de minutos elegidos entre las mismas.", "title": "Actualizar las opciones de Tomorrow.io" } } diff --git a/homeassistant/components/tomorrowio/translations/sk.json b/homeassistant/components/tomorrowio/translations/sk.json index 7f480c9778c..2c20494750b 100644 --- a/homeassistant/components/tomorrowio/translations/sk.json +++ b/homeassistant/components/tomorrowio/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "rate_limited": "Moment\u00e1lne je r\u00fdchlos\u0165 obmedzen\u00e1, sk\u00faste to pros\u00edm nesk\u00f4r." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/toon/translations/sk.json b/homeassistant/components/toon/translations/sk.json new file mode 100644 index 00000000000..c460d0a408b --- /dev/null +++ b/homeassistant/components/toon/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "already_configured": "Vybran\u00e1 dohoda je u\u017e nakonfigurovan\u00e1." + }, + "step": { + "agreement": { + "description": "Vyberte adresu zmluvy, ktor\u00fa chcete prida\u0165." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/totalconnect/translations/sk.json b/homeassistant/components/totalconnect/translations/sk.json index 93eb8dcc1b5..7643b634843 100644 --- a/homeassistant/components/totalconnect/translations/sk.json +++ b/homeassistant/components/totalconnect/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/trafikverket_ferry/translations/sk.json b/homeassistant/components/trafikverket_ferry/translations/sk.json new file mode 100644 index 00000000000..d6524c4eff8 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/sk.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, + "step": { + "user": { + "data": { + "weekday": "Pracovn\u00e9 dni" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/sk.json b/homeassistant/components/trafikverket_train/translations/sk.json new file mode 100644 index 00000000000..af85174f93a --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/sk.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, + "step": { + "user": { + "data": { + "weekday": "Dni" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_weatherstation/translations/sk.json b/homeassistant/components/trafikverket_weatherstation/translations/sk.json index ff853127803..1981cb3a60b 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/sk.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/transmission/translations/sk.json b/homeassistant/components/transmission/translations/sk.json index 586008f54cb..6ff2f8b99a3 100644 --- a/homeassistant/components/transmission/translations/sk.json +++ b/homeassistant/components/transmission/translations/sk.json @@ -23,6 +23,18 @@ } } }, + "issues": { + "deprecated_key": { + "fix_flow": { + "step": { + "confirm": { + "description": "Aktualizujte v\u0161etky automatiz\u00e1cie alebo skripty, ktor\u00e9 pou\u017e\u00edvaj\u00fa t\u00fato slu\u017ebu, a nahra\u010fte k\u013e\u00fa\u010d n\u00e1zvu k\u013e\u00fa\u010dom entry_id.", + "title": "K\u013e\u00fa\u010d s n\u00e1zvom v slu\u017eb\u00e1ch prenosu sa odstra\u0148uje" + } + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tuya/translations/select.sk.json b/homeassistant/components/tuya/translations/select.sk.json index bcd41ff83e0..af7ccf4fcdb 100644 --- a/homeassistant/components/tuya/translations/select.sk.json +++ b/homeassistant/components/tuya/translations/select.sk.json @@ -1,7 +1,12 @@ { "state": { "tuya__countdown": { - "3h": "3 hodiny" + "1h": "1 hodina", + "2h": "2 hodiny", + "3h": "3 hodiny", + "4h": "4 hodiny", + "5h": "5 hod\u00edn", + "6h": "6 hod\u00edn" }, "tuya__decibel_sensitivity": { "1": "Vysok\u00e1 citlivos\u0165" diff --git a/homeassistant/components/twentemilieu/translations/sk.json b/homeassistant/components/twentemilieu/translations/sk.json new file mode 100644 index 00000000000..75368faa73d --- /dev/null +++ b/homeassistant/components/twentemilieu/translations/sk.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "invalid_address": "Adresa sa nena\u0161la v servisnej oblasti Twente Milieu." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/sk.json b/homeassistant/components/ukraine_alarm/translations/sk.json new file mode 100644 index 00000000000..529d2911584 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifi/translations/sk.json b/homeassistant/components/unifi/translations/sk.json index 96cb696c161..26b0ac2d57f 100644 --- a/homeassistant/components/unifi/translations/sk.json +++ b/homeassistant/components/unifi/translations/sk.json @@ -4,7 +4,9 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "faulty_credentials": "Neplatn\u00e9 overenie" + "faulty_credentials": "Neplatn\u00e9 overenie", + "service_unavailable": "Nepodarilo sa pripoji\u0165", + "unknown_client_mac": "Na tejto adrese MAC nie je k dispoz\u00edcii \u017eiadny klient" }, "flow_title": "{site} ({host})", "step": { diff --git a/homeassistant/components/unifiprotect/translations/sk.json b/homeassistant/components/unifiprotect/translations/sk.json index 4668f4f8d07..d833c47005e 100644 --- a/homeassistant/components/unifiprotect/translations/sk.json +++ b/homeassistant/components/unifiprotect/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/upb/translations/sk.json b/homeassistant/components/upb/translations/sk.json index 3f20d345b26..11e19ce7a72 100644 --- a/homeassistant/components/upb/translations/sk.json +++ b/homeassistant/components/upb/translations/sk.json @@ -1,7 +1,17 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "address": "Adresa (pozri popis vy\u0161\u0161ie)" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/sk.json b/homeassistant/components/uptimerobot/translations/sk.json index a41b646034b..af59a66b0d6 100644 --- a/homeassistant/components/uptimerobot/translations/sk.json +++ b/homeassistant/components/uptimerobot/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/vallox/translations/sk.json b/homeassistant/components/vallox/translations/sk.json index 4116024575b..16a7a7bb845 100644 --- a/homeassistant/components/vallox/translations/sk.json +++ b/homeassistant/components/vallox/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, "error": { diff --git a/homeassistant/components/verisure/translations/sk.json b/homeassistant/components/verisure/translations/sk.json index 4ceb4d02151..35e9a7996b7 100644 --- a/homeassistant/components/verisure/translations/sk.json +++ b/homeassistant/components/verisure/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/vlc_telnet/translations/sk.json b/homeassistant/components/vlc_telnet/translations/sk.json index 0a5cf6b52a8..89e80deac2d 100644 --- a/homeassistant/components/vlc_telnet/translations/sk.json +++ b/homeassistant/components/vlc_telnet/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", "invalid_auth": "Neplatn\u00e9 overenie", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, diff --git a/homeassistant/components/volvooncall/translations/sk.json b/homeassistant/components/volvooncall/translations/sk.json index 66e8cd431a4..c04d2dbe1d3 100644 --- a/homeassistant/components/volvooncall/translations/sk.json +++ b/homeassistant/components/volvooncall/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/vulcan/translations/sk.json b/homeassistant/components/vulcan/translations/sk.json index c16ed208d24..ec44fa4db21 100644 --- a/homeassistant/components/vulcan/translations/sk.json +++ b/homeassistant/components/vulcan/translations/sk.json @@ -1,5 +1,13 @@ { "config": { + "abort": { + "all_student_already_configured": "V\u0161etci \u0161tudenti u\u017e boli pridan\u00ed.", + "already_configured": "Tento \u0161tudent u\u017e bol pridan\u00fd." + }, + "error": { + "invalid_token": "Neplatn\u00fd token", + "unknown": "Vyskytla sa nezn\u00e1ma chyba" + }, "step": { "add_next_config_entry": { "description": "Prida\u0165 \u010fal\u0161ieho \u0161tudenta." diff --git a/homeassistant/components/wallbox/translations/sk.json b/homeassistant/components/wallbox/translations/sk.json index b557456a8fe..7081168d334 100644 --- a/homeassistant/components/wallbox/translations/sk.json +++ b/homeassistant/components/wallbox/translations/sk.json @@ -5,7 +5,9 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/watttime/translations/sk.json b/homeassistant/components/watttime/translations/sk.json index 546d563046d..b8a538e476e 100644 --- a/homeassistant/components/watttime/translations/sk.json +++ b/homeassistant/components/watttime/translations/sk.json @@ -5,7 +5,9 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", + "unknown_coordinates": "\u017diadne \u00fadaje o zemepisnej \u0161\u00edrke/d\u013a\u017eke" }, "step": { "coordinates": { diff --git a/homeassistant/components/waze_travel_time/translations/sk.json b/homeassistant/components/waze_travel_time/translations/sk.json index ce32d575ee2..cae59423a4a 100644 --- a/homeassistant/components/waze_travel_time/translations/sk.json +++ b/homeassistant/components/waze_travel_time/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/whirlpool/translations/sk.json b/homeassistant/components/whirlpool/translations/sk.json index 1b1e671c054..41d0727a9b4 100644 --- a/homeassistant/components/whirlpool/translations/sk.json +++ b/homeassistant/components/whirlpool/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/whois/translations/sk.json b/homeassistant/components/whois/translations/sk.json index 102f110d9ba..804590e7c19 100644 --- a/homeassistant/components/whois/translations/sk.json +++ b/homeassistant/components/whois/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Service is already configured" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/wiffi/translations/sk.json b/homeassistant/components/wiffi/translations/sk.json index 892b8b2cd91..cf4a04ee532 100644 --- a/homeassistant/components/wiffi/translations/sk.json +++ b/homeassistant/components/wiffi/translations/sk.json @@ -1,5 +1,9 @@ { "config": { + "abort": { + "addr_in_use": "Port servera sa u\u017e pou\u017e\u00edva.", + "already_configured": "Port servera je u\u017e nakonfigurovan\u00fd." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/wilight/translations/sk.json b/homeassistant/components/wilight/translations/sk.json index c9177d7b5d2..058e40d91a4 100644 --- a/homeassistant/components/wilight/translations/sk.json +++ b/homeassistant/components/wilight/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "not_supported_device": "Toto WiLight moment\u00e1lne nie je podporovan\u00e9" }, "flow_title": "{name}" } diff --git a/homeassistant/components/withings/translations/sk.json b/homeassistant/components/withings/translations/sk.json new file mode 100644 index 00000000000..1a45e4d6196 --- /dev/null +++ b/homeassistant/components/withings/translations/sk.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "already_configured": "Konfigur\u00e1cia profilu bola aktualizovan\u00e1." + }, + "error": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wiz/translations/sk.json b/homeassistant/components/wiz/translations/sk.json index a0cedfaf1bb..d5a802b858e 100644 --- a/homeassistant/components/wiz/translations/sk.json +++ b/homeassistant/components/wiz/translations/sk.json @@ -5,6 +5,11 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "no_ip": "Neplatn\u00e1 adresa IP.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "flow_title": "{name} ({host})", "step": { "discovery_confirm": { diff --git a/homeassistant/components/wled/translations/sk.json b/homeassistant/components/wled/translations/sk.json index 835f107f7af..ed248c10965 100644 --- a/homeassistant/components/wled/translations/sk.json +++ b/homeassistant/components/wled/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/wolflink/translations/sk.json b/homeassistant/components/wolflink/translations/sk.json index 23825cbb7ba..61eff649dab 100644 --- a/homeassistant/components/wolflink/translations/sk.json +++ b/homeassistant/components/wolflink/translations/sk.json @@ -1,7 +1,12 @@ { "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "device": { diff --git a/homeassistant/components/ws66i/translations/sk.json b/homeassistant/components/ws66i/translations/sk.json index 5184388d901..a33cac735e4 100644 --- a/homeassistant/components/ws66i/translations/sk.json +++ b/homeassistant/components/ws66i/translations/sk.json @@ -1,7 +1,14 @@ { "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { + "data": { + "ip_address": "IP adresa" + }, "title": "Pripojte sa k zariadeniu" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/sk.json b/homeassistant/components/xiaomi_aqara/translations/sk.json index 2fbc4f458b4..f5e89b6c809 100644 --- a/homeassistant/components/xiaomi_aqara/translations/sk.json +++ b/homeassistant/components/xiaomi_aqara/translations/sk.json @@ -4,6 +4,10 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "error": { + "invalid_interface": "Neplatn\u00e9 sie\u0165ov\u00e9 rozhranie", + "invalid_mac": "Neplatn\u00e1 adresa Mac" + }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/xiaomi_ble/translations/sk.json b/homeassistant/components/xiaomi_ble/translations/sk.json index 8444b9fbeae..92ca31d6c47 100644 --- a/homeassistant/components/xiaomi_ble/translations/sk.json +++ b/homeassistant/components/xiaomi_ble/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" } } diff --git a/homeassistant/components/xiaomi_miio/translations/sk.json b/homeassistant/components/xiaomi_miio/translations/sk.json index 4b66c25fa57..3eebf9ec1a9 100644 --- a/homeassistant/components/xiaomi_miio/translations/sk.json +++ b/homeassistant/components/xiaomi_miio/translations/sk.json @@ -3,7 +3,13 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown_device": "Model zariadenia nie je zn\u00e1my, nie je mo\u017en\u00e9 nastavi\u0165 zariadenie pomocou konfigura\u010dn\u00e9ho toku.", + "wrong_token": "Chyba kontroln\u00e9ho s\u00fa\u010dtu, nespr\u00e1vny token" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/yale_smart_alarm/translations/sk.json b/homeassistant/components/yale_smart_alarm/translations/sk.json index 0e707dc558b..a0da00adc5d 100644 --- a/homeassistant/components/yale_smart_alarm/translations/sk.json +++ b/homeassistant/components/yale_smart_alarm/translations/sk.json @@ -1,9 +1,11 @@ { "config": { "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { @@ -20,5 +22,10 @@ } } } + }, + "options": { + "error": { + "code_format_mismatch": "K\u00f3d nezodpoved\u00e1 po\u017eadovan\u00e9mu po\u010dtu \u010d\u00edslic" + } } } \ No newline at end of file diff --git a/homeassistant/components/yalexs_ble/translations/sk.json b/homeassistant/components/yalexs_ble/translations/sk.json index c4cd811a778..3fc88152de7 100644 --- a/homeassistant/components/yalexs_ble/translations/sk.json +++ b/homeassistant/components/yalexs_ble/translations/sk.json @@ -2,9 +2,15 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "no_unconfigured_devices": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia." }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "flow_title": "{name}", "step": { "integration_discovery_confirm": { diff --git a/homeassistant/components/yamaha_musiccast/translations/select.sk.json b/homeassistant/components/yamaha_musiccast/translations/select.sk.json new file mode 100644 index 00000000000..0311b5fd0d7 --- /dev/null +++ b/homeassistant/components/yamaha_musiccast/translations/select.sk.json @@ -0,0 +1,10 @@ +{ + "state": { + "yamaha_musiccast__zone_sleep": { + "120 min": "120 min\u00fat", + "30 min": "30 min\u00fat", + "60 min": "60 min\u00fat", + "90 min": "90 min\u00fat" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yeelight/translations/sk.json b/homeassistant/components/yeelight/translations/sk.json index 2216797f253..69b33c786a5 100644 --- a/homeassistant/components/yeelight/translations/sk.json +++ b/homeassistant/components/yeelight/translations/sk.json @@ -4,6 +4,9 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{model} {id} ({host})", "step": { "discovery_confirm": { diff --git a/homeassistant/components/yolink/translations/sk.json b/homeassistant/components/yolink/translations/sk.json new file mode 100644 index 00000000000..c3bed1eca3b --- /dev/null +++ b/homeassistant/components/yolink/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/youless/translations/sk.json b/homeassistant/components/youless/translations/sk.json index 965b3bb8919..1820d9d8d42 100644 --- a/homeassistant/components/youless/translations/sk.json +++ b/homeassistant/components/youless/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/zha/translations/sk.json b/homeassistant/components/zha/translations/sk.json index d0bd56a816a..4f482e38d06 100644 --- a/homeassistant/components/zha/translations/sk.json +++ b/homeassistant/components/zha/translations/sk.json @@ -1,5 +1,9 @@ { "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_backup_json": "Neplatn\u00e1 z\u00e1loha JSON" + }, "flow_title": "{name}", "step": { "confirm": { @@ -20,5 +24,11 @@ "button_5": "Piate tla\u010didlo", "button_6": "\u0160ieste tla\u010didlo" } + }, + "options": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_backup_json": "Neplatn\u00e1 z\u00e1loha JSON" + } } } \ No newline at end of file diff --git a/homeassistant/components/zone/translations/sk.json b/homeassistant/components/zone/translations/sk.json index 5272ec1315a..30992294e6a 100644 --- a/homeassistant/components/zone/translations/sk.json +++ b/homeassistant/components/zone/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "name_exists": "N\u00e1zov u\u017e existuje" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/zoneminder/translations/sk.json b/homeassistant/components/zoneminder/translations/sk.json index 8bacfc145e2..66b0ba88923 100644 --- a/homeassistant/components/zoneminder/translations/sk.json +++ b/homeassistant/components/zoneminder/translations/sk.json @@ -1,9 +1,12 @@ { "config": { "abort": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "error": { + "auth_fail": "U\u017eivate\u013esk\u00e9 meno alebo heslo je nespr\u00e1vne.", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { diff --git a/homeassistant/components/zwave_js/translations/sk.json b/homeassistant/components/zwave_js/translations/sk.json index 7db446dc6c3..10bbe0adb94 100644 --- a/homeassistant/components/zwave_js/translations/sk.json +++ b/homeassistant/components/zwave_js/translations/sk.json @@ -2,7 +2,12 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name}" }, @@ -17,7 +22,12 @@ }, "options": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" } } } \ No newline at end of file From a4dbb9a24e676d3d87003b678f3b14347630b1a9 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 25 Nov 2022 09:29:54 +0100 Subject: [PATCH 0709/1033] Add handler to validate_user_input (#82681) * Add handler to validate_user_input * Adjust group config flow --- homeassistant/components/group/config_flow.py | 9 +++++++-- homeassistant/components/scrape/config_flow.py | 9 +++++++-- homeassistant/components/threshold/config_flow.py | 9 ++++++--- homeassistant/components/utility_meter/config_flow.py | 9 ++++++--- homeassistant/helpers/schema_config_entry_flow.py | 8 +++++--- 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/group/config_flow.py b/homeassistant/components/group/config_flow.py index 5453d3024f5..1dd01f6193a 100644 --- a/homeassistant/components/group/config_flow.py +++ b/homeassistant/components/group/config_flow.py @@ -11,6 +11,7 @@ from homeassistant.const import CONF_ENTITIES from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er, selector from homeassistant.helpers.schema_config_entry_flow import ( + SchemaCommonFlowHandler, SchemaConfigFlowHandler, SchemaFlowFormStep, SchemaFlowMenuStep, @@ -104,11 +105,15 @@ def choose_options_step(options: dict[str, Any]) -> str: return cast(str, options["group_type"]) -def set_group_type(group_type: str) -> Callable[[dict[str, Any]], dict[str, Any]]: +def set_group_type( + group_type: str, +) -> Callable[[SchemaCommonFlowHandler, dict[str, Any]], dict[str, Any]]: """Set group type.""" @callback - def _set_group_type(user_input: dict[str, Any]) -> dict[str, Any]: + def _set_group_type( + handler: SchemaCommonFlowHandler, user_input: dict[str, Any] + ) -> dict[str, Any]: """Add group type to user input.""" return {"group_type": group_type, **user_input} diff --git a/homeassistant/components/scrape/config_flow.py b/homeassistant/components/scrape/config_flow.py index eedc584a394..b53f2f12bd2 100644 --- a/homeassistant/components/scrape/config_flow.py +++ b/homeassistant/components/scrape/config_flow.py @@ -36,6 +36,7 @@ from homeassistant.const import ( ) from homeassistant.core import async_get_hass from homeassistant.helpers.schema_config_entry_flow import ( + SchemaCommonFlowHandler, SchemaConfigFlowHandler, SchemaFlowError, SchemaFlowFormStep, @@ -113,7 +114,9 @@ SENSOR_SETUP = { } -def validate_rest_setup(user_input: dict[str, Any]) -> dict[str, Any]: +def validate_rest_setup( + handler: SchemaCommonFlowHandler, user_input: dict[str, Any] +) -> dict[str, Any]: """Validate rest setup.""" hass = async_get_hass() rest_config: dict[str, Any] = COMBINED_SCHEMA(user_input) @@ -124,7 +127,9 @@ def validate_rest_setup(user_input: dict[str, Any]) -> dict[str, Any]: return user_input -def validate_sensor_setup(user_input: dict[str, Any]) -> dict[str, Any]: +def validate_sensor_setup( + handler: SchemaCommonFlowHandler, user_input: dict[str, Any] +) -> dict[str, Any]: """Validate sensor setup.""" return { "sensor": [ diff --git a/homeassistant/components/threshold/config_flow.py b/homeassistant/components/threshold/config_flow.py index 52b2fb44be9..373e48e7ba3 100644 --- a/homeassistant/components/threshold/config_flow.py +++ b/homeassistant/components/threshold/config_flow.py @@ -10,6 +10,7 @@ from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import CONF_ENTITY_ID, CONF_NAME from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( + SchemaCommonFlowHandler, SchemaConfigFlowHandler, SchemaFlowError, SchemaFlowFormStep, @@ -18,11 +19,13 @@ from homeassistant.helpers.schema_config_entry_flow import ( from .const import CONF_HYSTERESIS, CONF_LOWER, CONF_UPPER, DEFAULT_HYSTERESIS, DOMAIN -def _validate_mode(data: Any) -> Any: +def _validate_mode( + handler: SchemaCommonFlowHandler, user_input: dict[str, Any] +) -> dict[str, Any]: """Validate the threshold mode, and set limits to None if not set.""" - if CONF_LOWER not in data and CONF_UPPER not in data: + if CONF_LOWER not in user_input and CONF_UPPER not in user_input: raise SchemaFlowError("need_lower_upper") - return {CONF_LOWER: None, CONF_UPPER: None, **data} + return {CONF_LOWER: None, CONF_UPPER: None, **user_input} OPTIONS_SCHEMA = vol.Schema( diff --git a/homeassistant/components/utility_meter/config_flow.py b/homeassistant/components/utility_meter/config_flow.py index 0f43ccf29cc..59bde5ac300 100644 --- a/homeassistant/components/utility_meter/config_flow.py +++ b/homeassistant/components/utility_meter/config_flow.py @@ -10,6 +10,7 @@ from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import CONF_NAME from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( + SchemaCommonFlowHandler, SchemaConfigFlowHandler, SchemaFlowError, SchemaFlowFormStep, @@ -46,14 +47,16 @@ METER_TYPES = [ ] -def _validate_config(data: Any) -> Any: +def _validate_config( + handler: SchemaCommonFlowHandler, user_input: dict[str, Any] +) -> dict[str, Any]: """Validate config.""" try: - vol.Unique()(data[CONF_TARIFFS]) + vol.Unique()(user_input[CONF_TARIFFS]) except vol.Invalid as exc: raise SchemaFlowError("tariffs_not_unique") from exc - return data + return user_input OPTIONS_SCHEMA = vol.Schema( diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 526f59bd103..dd4de4e3f33 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -44,7 +44,9 @@ class SchemaFlowFormStep(SchemaFlowStep): user input is requested. """ - validate_user_input: Callable[[dict[str, Any]], dict[str, Any]] = lambda x: x + validate_user_input: Callable[ + [SchemaCommonFlowHandler, dict[str, Any]], dict[str, Any] + ] | None = None """Optional function to validate user input. - The `validate_user_input` function is called if the schema validates successfully. @@ -124,10 +126,10 @@ class SchemaCommonFlowHandler: ): user_input[str(key.schema)] = key.default() - if user_input is not None and form_step.schema is not None: + if user_input is not None and form_step.validate_user_input is not None: # Do extra validation of user input try: - user_input = form_step.validate_user_input(user_input) + user_input = form_step.validate_user_input(self, user_input) except SchemaFlowError as exc: return self._show_next_step(step_id, exc, user_input) From 01b0f4d5652b4194b7db3d49852f886f5546df7f Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 25 Nov 2022 10:22:12 +0100 Subject: [PATCH 0710/1033] Announce a deprecated callback for MQTT subscribe will stop working (#82610) * Announce a deprecated callback will stop working * Use 2 months grace period * Use 2 months grace period * Use 2 months grace period * Add deprecation comment to tests --- homeassistant/components/mqtt/client.py | 5 ++++- tests/components/mqtt/test_init.py | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py index 414027776a3..36e12c47419 100644 --- a/homeassistant/components/mqtt/client.py +++ b/homeassistant/components/mqtt/client.py @@ -152,6 +152,7 @@ DeprecatedMessageCallbackTypes = Union[ ] +# Support for a deprecated callback type will be removed from HA core 2023.2.0 def wrap_msg_callback( msg_callback: DeprecatedMessageCallbackTypes, ) -> AsyncMessageCallbackType | MessageCallbackType: @@ -203,6 +204,7 @@ async def async_subscribe( raise HomeAssistantError( f"Cannot subscribe to topic '{topic}', MQTT is not enabled" ) + # Support for a deprecated callback type will be removed from HA core 2023.2.0 # Count callback parameters which don't have a default value non_default = 0 if msg_callback: @@ -216,7 +218,8 @@ async def async_subscribe( if non_default == 3: module = inspect.getmodule(msg_callback) _LOGGER.warning( - "Signature of MQTT msg_callback '%s.%s' is deprecated", + "Signature of MQTT msg_callback '%s.%s' is deprecated, " + "this will stop working with HA core 2023.2", module.__name__ if module else "", msg_callback.__name__, ) diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 2e4a44b9525..12b36a2fae3 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -882,6 +882,7 @@ async def test_subscribe_bad_topic( await mqtt.async_subscribe(hass, 55, record_calls) +# Support for a deprecated callback type will be removed from HA core 2023.2.0 async def test_subscribe_deprecated(hass, mqtt_mock_entry_no_yaml_config): """Test the subscription of a topic using deprecated callback signature.""" mqtt_mock = await mqtt_mock_entry_no_yaml_config() @@ -930,6 +931,7 @@ async def test_subscribe_deprecated(hass, mqtt_mock_entry_no_yaml_config): assert len(calls) == 1 +# Support for a deprecated callback type will be removed from HA core 2023.2.0 async def test_subscribe_deprecated_async(hass, mqtt_mock_entry_no_yaml_config): """Test the subscription of a topic using deprecated coroutine signature.""" mqtt_mock = await mqtt_mock_entry_no_yaml_config() From 9feb64cebdb841ad9f94f0d197d68e146acd548f Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 25 Nov 2022 10:50:38 +0100 Subject: [PATCH 0711/1033] Simplify schema callback in SchemaFlowFormStep (#82682) * Simplify SchemaFlowFormStep.schema callback * Expose parent handler * Adjust docstrings --- homeassistant/components/group/config_flow.py | 23 +++++--------- .../helpers/schema_config_entry_flow.py | 30 +++++++++---------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/group/config_flow.py b/homeassistant/components/group/config_flow.py index 1dd01f6193a..4d689914378 100644 --- a/homeassistant/components/group/config_flow.py +++ b/homeassistant/components/group/config_flow.py @@ -25,16 +25,14 @@ from .const import CONF_HIDE_MEMBERS def basic_group_options_schema( - domain: str, - handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, - options: dict[str, Any], + domain: str, handler: SchemaCommonFlowHandler ) -> vol.Schema: """Generate options schema.""" - handler = cast(SchemaOptionsFlowHandler, handler) return vol.Schema( { vol.Required(CONF_ENTITIES): entity_selector_without_own_entities( - handler, selector.EntitySelectorConfig(domain=domain, multiple=True) + cast(SchemaOptionsFlowHandler, handler.parent_handler), + selector.EntitySelectorConfig(domain=domain, multiple=True), ), vol.Required(CONF_HIDE_MEMBERS, default=False): selector.BooleanSelector(), } @@ -54,12 +52,9 @@ def basic_group_config_schema(domain: str) -> vol.Schema: ) -def binary_sensor_options_schema( - handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, - options: dict[str, Any], -) -> vol.Schema: +def binary_sensor_options_schema(handler: SchemaCommonFlowHandler) -> vol.Schema: """Generate options schema.""" - return basic_group_options_schema("binary_sensor", handler, options).extend( + return basic_group_options_schema("binary_sensor", handler).extend( { vol.Required(CONF_ALL, default=False): selector.BooleanSelector(), } @@ -74,12 +69,10 @@ BINARY_SENSOR_CONFIG_SCHEMA = basic_group_config_schema("binary_sensor").extend( def light_switch_options_schema( - domain: str, - handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, - options: dict[str, Any], + domain: str, handler: SchemaCommonFlowHandler ) -> vol.Schema: """Generate options schema.""" - return basic_group_options_schema(domain, handler, options).extend( + return basic_group_options_schema(domain, handler).extend( { vol.Required( CONF_ALL, default=False, description={"advanced": True} @@ -145,7 +138,7 @@ CONFIG_FLOW = { OPTIONS_FLOW = { - "init": SchemaFlowFormStep(None, next_step=choose_options_step), + "init": SchemaFlowFormStep(next_step=choose_options_step), "binary_sensor": SchemaFlowFormStep(binary_sensor_options_schema), "cover": SchemaFlowFormStep(partial(basic_group_options_schema, "cover")), "fan": SchemaFlowFormStep(partial(basic_group_options_schema, "fan")), diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index dd4de4e3f33..c721b9d4ad6 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -31,15 +31,13 @@ class SchemaFlowFormStep(SchemaFlowStep): """Define a config or options flow form step.""" schema: vol.Schema | Callable[ - [SchemaConfigFlowHandler | SchemaOptionsFlowHandler, dict[str, Any]], - vol.Schema | None, - ] | None + [SchemaCommonFlowHandler], vol.Schema | None + ] | None = None """Optional voluptuous schema, or function which returns a schema or None, for requesting and validating user input. - - If a function is specified, the function will be passed the handler, which is - either an instance of SchemaConfigFlowHandler or SchemaOptionsFlowHandler, and the - union of config entry options and user input from previous steps. + - If a function is specified, the function will be passed the current + `SchemaCommonFlowHandler`. - If schema validation fails, the step will be retried. If the schema is None, no user input is requested. """ @@ -50,7 +48,8 @@ class SchemaFlowFormStep(SchemaFlowStep): """Optional function to validate user input. - The `validate_user_input` function is called if the schema validates successfully. - - The `validate_user_input` function is passed the user input from the current step. + - The first argument is a reference to the current `SchemaCommonFlowHandler`. + - The second argument is the user input from the current step. - The `validate_user_input` should raise `SchemaFlowError` is user input is invalid. """ @@ -86,6 +85,11 @@ class SchemaCommonFlowHandler: self._handler = handler self._options = options if options is not None else {} + @property + def parent_handler(self) -> SchemaConfigFlowHandler | SchemaOptionsFlowHandler: + """Return parent handler.""" + return self._handler + async def async_step( self, step_id: str, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -94,14 +98,12 @@ class SchemaCommonFlowHandler: return await self._async_form_step(step_id, user_input) return await self._async_menu_step(step_id, user_input) - def _get_schema( - self, form_step: SchemaFlowFormStep, options: dict[str, Any] - ) -> vol.Schema | None: + def _get_schema(self, form_step: SchemaFlowFormStep) -> vol.Schema | None: if form_step.schema is None: return None if isinstance(form_step.schema, vol.Schema): return form_step.schema - return form_step.schema(self._handler, options) + return form_step.schema(self) async def _async_form_step( self, step_id: str, user_input: dict[str, Any] | None = None @@ -111,7 +113,7 @@ class SchemaCommonFlowHandler: if ( user_input is not None - and (data_schema := self._get_schema(form_step, self._options)) + and (data_schema := self._get_schema(form_step)) and data_schema.schema and not self._handler.show_advanced_options ): @@ -171,9 +173,7 @@ class SchemaCommonFlowHandler: form_step = cast(SchemaFlowFormStep, self._flow[next_step_id]) - if ( - data_schema := self._get_schema(form_step, self._options) - ) and data_schema.schema: + if (data_schema := self._get_schema(form_step)) and data_schema.schema: # Make a copy of the schema with suggested values set to saved options schema = {} for key, val in data_schema.schema.items(): From ea1868b7b9a051a1d1911cef4b1c63b2689f6166 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk <11290930+bouwew@users.noreply.github.com> Date: Fri, 25 Nov 2022 10:55:51 +0100 Subject: [PATCH 0712/1033] Bump plugwise to v0.25.12 (#82146) Co-authored-by: Franck Nijhof fixes undefined --- .../components/plugwise/binary_sensor.py | 6 +++ .../components/plugwise/config_flow.py | 19 +++++--- .../components/plugwise/coordinator.py | 20 ++++++-- homeassistant/components/plugwise/gateway.py | 29 ++++++----- .../components/plugwise/manifest.json | 2 +- .../components/plugwise/strings.json | 6 ++- .../components/plugwise/translations/en.json | 6 ++- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../anna_heatpump_heating/all_data.json | 13 ++--- .../m_anna_heatpump_cooling/all_data.json | 2 +- .../m_anna_heatpump_idle/all_data.json | 2 +- .../components/plugwise/test_binary_sensor.py | 2 +- tests/components/plugwise/test_climate.py | 9 ++-- tests/components/plugwise/test_config_flow.py | 19 +++++--- tests/components/plugwise/test_init.py | 48 +++++++++++++++---- 16 files changed, 123 insertions(+), 64 deletions(-) diff --git a/homeassistant/components/plugwise/binary_sensor.py b/homeassistant/components/plugwise/binary_sensor.py index 164135a607b..10e4836f73a 100644 --- a/homeassistant/components/plugwise/binary_sensor.py +++ b/homeassistant/components/plugwise/binary_sensor.py @@ -36,6 +36,12 @@ BINARY_SENSORS: tuple[PlugwiseBinarySensorEntityDescription, ...] = ( icon_off="mdi:hvac-off", entity_category=EntityCategory.DIAGNOSTIC, ), + PlugwiseBinarySensorEntityDescription( + key="cooling_enabled", + name="Cooling enabled", + icon="mdi:snowflake-thermometer", + entity_category=EntityCategory.DIAGNOSTIC, + ), PlugwiseBinarySensorEntityDescription( key="dhw_state", name="DHW state", diff --git a/homeassistant/components/plugwise/config_flow.py b/homeassistant/components/plugwise/config_flow.py index 09b919e9d1d..3bbd5725b29 100644 --- a/homeassistant/components/plugwise/config_flow.py +++ b/homeassistant/components/plugwise/config_flow.py @@ -4,9 +4,12 @@ from __future__ import annotations from typing import Any from plugwise.exceptions import ( + ConnectionFailedError, InvalidAuthentication, InvalidSetupError, - PlugwiseException, + InvalidXMLError, + ResponseError, + UnsupportedDeviceError, ) from plugwise.smile import Smile import voluptuous as vol @@ -32,7 +35,6 @@ from .const import ( DOMAIN, FLOW_SMILE, FLOW_STRETCH, - LOGGER, PW_TYPE, SMILE, STRETCH, @@ -175,14 +177,17 @@ class PlugwiseConfigFlow(ConfigFlow, domain=DOMAIN): try: api = await validate_gw_input(self.hass, user_input) - except InvalidSetupError: - errors[CONF_BASE] = "invalid_setup" + except ConnectionFailedError: + errors[CONF_BASE] = "cannot_connect" except InvalidAuthentication: errors[CONF_BASE] = "invalid_auth" - except PlugwiseException: - errors[CONF_BASE] = "cannot_connect" + except InvalidSetupError: + errors[CONF_BASE] = "invalid_setup" + except (InvalidXMLError, ResponseError): + errors[CONF_BASE] = "response_error" + except UnsupportedDeviceError: + errors[CONF_BASE] = "unsupported" except Exception: # pylint: disable=broad-except - LOGGER.exception("Unexpected exception") errors[CONF_BASE] = "unknown" else: await self.async_set_unique_id( diff --git a/homeassistant/components/plugwise/coordinator.py b/homeassistant/components/plugwise/coordinator.py index 6fd44efda84..5e5cab7d816 100644 --- a/homeassistant/components/plugwise/coordinator.py +++ b/homeassistant/components/plugwise/coordinator.py @@ -4,7 +4,13 @@ from typing import NamedTuple, cast from plugwise import Smile from plugwise.constants import DeviceData, GatewayData -from plugwise.exceptions import PlugwiseException, XMLDataMissingError +from plugwise.exceptions import ( + ConnectionFailedError, + InvalidAuthentication, + InvalidXMLError, + ResponseError, + UnsupportedDeviceError, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.debounce import Debouncer @@ -47,12 +53,16 @@ class PlugwiseDataUpdateCoordinator(DataUpdateCoordinator[PlugwiseData]): """Fetch data from Plugwise.""" try: data = await self.api.async_update() - except XMLDataMissingError as err: + except InvalidAuthentication as err: + raise UpdateFailed("Authentication failed") from err + except (InvalidXMLError, ResponseError) as err: raise UpdateFailed( - f"No XML data received for: {self.api.smile_name}" + "Invalid XML data, or error indication received for the Plugwise Adam/Smile/Stretch" ) from err - except PlugwiseException as err: - raise UpdateFailed(f"Updated failed for: {self.api.smile_name}") from err + except UnsupportedDeviceError as err: + raise UpdateFailed("Device with unsupported firmware") from err + except ConnectionFailedError as err: + raise UpdateFailed("Failed to connect") from err return PlugwiseData( gateway=cast(GatewayData, data[0]), devices=cast(dict[str, DeviceData], data[1]), diff --git a/homeassistant/components/plugwise/gateway.py b/homeassistant/components/plugwise/gateway.py index 16b6a977569..81cb81e9bbf 100644 --- a/homeassistant/components/plugwise/gateway.py +++ b/homeassistant/components/plugwise/gateway.py @@ -1,17 +1,21 @@ """Plugwise platform for Home Assistant Core.""" from __future__ import annotations -import asyncio from typing import Any -from aiohttp import ClientConnectionError -from plugwise.exceptions import InvalidAuthentication, PlugwiseException +from plugwise.exceptions import ( + ConnectionFailedError, + InvalidAuthentication, + InvalidXMLError, + ResponseError, + UnsupportedDeviceError, +) from plugwise.smile import Smile from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME from homeassistant.core import HomeAssistant, callback -from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -42,17 +46,16 @@ async def async_setup_entry_gw(hass: HomeAssistant, entry: ConfigEntry) -> bool: try: connected = await api.connect() - except InvalidAuthentication: - LOGGER.error("Invalid username or Smile ID") - return False - except (ClientConnectionError, PlugwiseException) as err: + except ConnectionFailedError as err: + raise ConfigEntryNotReady("Failed to connect to the Plugwise Smile") from err + except InvalidAuthentication as err: + raise HomeAssistantError("Invalid username or Smile ID") from err + except (InvalidXMLError, ResponseError) as err: raise ConfigEntryNotReady( - f"Error while communicating to device {api.smile_name}" - ) from err - except asyncio.TimeoutError as err: - raise ConfigEntryNotReady( - f"Timeout while connecting to Smile {api.smile_name}" + "Error while communicating to the Plugwise Smile" ) from err + except UnsupportedDeviceError as err: + raise HomeAssistantError("Device with unsupported firmware") from err if not connected: raise ConfigEntryNotReady("Unable to connect to Smile") diff --git a/homeassistant/components/plugwise/manifest.json b/homeassistant/components/plugwise/manifest.json index 6bb1c941bf3..6a11b47da57 100644 --- a/homeassistant/components/plugwise/manifest.json +++ b/homeassistant/components/plugwise/manifest.json @@ -2,7 +2,7 @@ "domain": "plugwise", "name": "Plugwise", "documentation": "https://www.home-assistant.io/integrations/plugwise", - "requirements": ["plugwise==0.25.7"], + "requirements": ["plugwise==0.25.12"], "codeowners": ["@CoMPaTech", "@bouwew", "@brefra", "@frenck"], "zeroconf": ["_plugwise._tcp.local."], "config_flow": true, diff --git a/homeassistant/components/plugwise/strings.json b/homeassistant/components/plugwise/strings.json index 7278f6c4414..781a17a1d10 100644 --- a/homeassistant/components/plugwise/strings.json +++ b/homeassistant/components/plugwise/strings.json @@ -15,8 +15,10 @@ "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", - "invalid_setup": "Add your Adam instead of your Anna, see the Home Assistant Plugwise integration documentation for more information", - "unknown": "[%key:common::config_flow::error::unknown%]" + "invalid_setup": "Add your Adam instead of your Anna, see the documentation", + "response_error": "Invalid XML data, or error indication received", + "unknown": "[%key:common::config_flow::error::unknown%]", + "unsupported": "Device with unsupported firmware" }, "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_service%]", diff --git a/homeassistant/components/plugwise/translations/en.json b/homeassistant/components/plugwise/translations/en.json index aa5a318bbff..ecc5af9218d 100644 --- a/homeassistant/components/plugwise/translations/en.json +++ b/homeassistant/components/plugwise/translations/en.json @@ -7,8 +7,10 @@ "error": { "cannot_connect": "Failed to connect", "invalid_auth": "Invalid authentication", - "invalid_setup": "Add your Adam instead of your Anna, see the Home Assistant Plugwise integration documentation for more information", - "unknown": "Unexpected error" + "invalid_setup": "Add your Adam instead of your Anna, see the documentation", + "response_error": "Invalid XML data, or error indication received", + "unknown": "Unexpected error", + "unsupported": "Device with unsupported firmware" }, "step": { "user": { diff --git a/requirements_all.txt b/requirements_all.txt index cf2504c8513..e844738a1ab 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1329,7 +1329,7 @@ plexauth==0.0.6 plexwebsocket==0.0.13 # homeassistant.components.plugwise -plugwise==0.25.7 +plugwise==0.25.12 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6e96e13f4b9..6eae3f35265 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -956,7 +956,7 @@ plexauth==0.0.6 plexwebsocket==0.0.13 # homeassistant.components.plugwise -plugwise==0.25.7 +plugwise==0.25.12 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/tests/components/plugwise/fixtures/anna_heatpump_heating/all_data.json b/tests/components/plugwise/fixtures/anna_heatpump_heating/all_data.json index 546a11b2c68..1dc0fb6f2ad 100644 --- a/tests/components/plugwise/fixtures/anna_heatpump_heating/all_data.json +++ b/tests/components/plugwise/fixtures/anna_heatpump_heating/all_data.json @@ -3,7 +3,7 @@ "smile_name": "Smile Anna", "gateway_id": "015ae9ea3f964e668e490fa39da3870b", "heater_id": "1cbf783bb11e4a7c8a6843dee3a86927", - "cooling_present": true, + "cooling_present": false, "notifications": {} }, { @@ -21,10 +21,10 @@ }, "available": true, "binary_sensors": { + "cooling_enabled": false, "dhw_state": false, "heating_state": true, "compressor_state": true, - "cooling_state": false, "slave_boiler_state": false, "flame_state": false }, @@ -66,13 +66,11 @@ "name": "Anna", "vendor": "Plugwise", "thermostat": { - "setpoint_low": 20.5, - "setpoint_high": 24.0, + "setpoint": 20.5, "lower_bound": 4.0, "upper_bound": 30.0, "resolution": 0.1 }, - "available": true, "preset_modes": ["no_frost", "home", "away", "asleep", "vacation"], "active_preset": "home", "available_schedules": ["standaard"], @@ -81,11 +79,10 @@ "mode": "auto", "sensors": { "temperature": 19.3, + "setpoint": 20.5, "illuminance": 86.0, "cooling_activation_outdoor_temperature": 21.0, - "cooling_deactivation_threshold": 4.0, - "setpoint_low": 20.5, - "setpoint_high": 24.0 + "cooling_deactivation_threshold": 4.0 } } } diff --git a/tests/components/plugwise/fixtures/m_anna_heatpump_cooling/all_data.json b/tests/components/plugwise/fixtures/m_anna_heatpump_cooling/all_data.json index 6326a02fedb..cb9e340efb8 100644 --- a/tests/components/plugwise/fixtures/m_anna_heatpump_cooling/all_data.json +++ b/tests/components/plugwise/fixtures/m_anna_heatpump_cooling/all_data.json @@ -21,6 +21,7 @@ }, "available": true, "binary_sensors": { + "cooling_enabled": true, "dhw_state": false, "heating_state": false, "compressor_state": true, @@ -72,7 +73,6 @@ "upper_bound": 30.0, "resolution": 0.1 }, - "available": true, "preset_modes": ["no_frost", "home", "away", "asleep", "vacation"], "active_preset": "home", "available_schedules": ["standaard"], diff --git a/tests/components/plugwise/fixtures/m_anna_heatpump_idle/all_data.json b/tests/components/plugwise/fixtures/m_anna_heatpump_idle/all_data.json index cd2747f423b..0a421be5343 100644 --- a/tests/components/plugwise/fixtures/m_anna_heatpump_idle/all_data.json +++ b/tests/components/plugwise/fixtures/m_anna_heatpump_idle/all_data.json @@ -21,6 +21,7 @@ }, "available": true, "binary_sensors": { + "cooling_enabled": true, "dhw_state": false, "heating_state": false, "compressor_state": false, @@ -72,7 +73,6 @@ "upper_bound": 30.0, "resolution": 0.1 }, - "available": true, "preset_modes": ["no_frost", "home", "away", "asleep", "vacation"], "active_preset": "home", "available_schedules": ["standaard"], diff --git a/tests/components/plugwise/test_binary_sensor.py b/tests/components/plugwise/test_binary_sensor.py index 1e4df8fb673..f4f2a3f3c5f 100644 --- a/tests/components/plugwise/test_binary_sensor.py +++ b/tests/components/plugwise/test_binary_sensor.py @@ -26,7 +26,7 @@ async def test_anna_climate_binary_sensor_entities( assert state assert state.state == STATE_ON - state = hass.states.get("binary_sensor.opentherm_cooling") + state = hass.states.get("binary_sensor.opentherm_cooling_enabled") assert state assert state.state == STATE_OFF diff --git a/tests/components/plugwise/test_climate.py b/tests/components/plugwise/test_climate.py index ad5443a678c..5636523a919 100644 --- a/tests/components/plugwise/test_climate.py +++ b/tests/components/plugwise/test_climate.py @@ -202,7 +202,7 @@ async def test_anna_climate_entity_attributes( assert state.state == HVACMode.AUTO assert state.attributes["hvac_action"] == "heating" assert state.attributes["hvac_modes"] == [ - HVACMode.HEAT_COOL, + HVACMode.HEAT, HVACMode.AUTO, ] @@ -211,9 +211,8 @@ async def test_anna_climate_entity_attributes( assert state.attributes["current_temperature"] == 19.3 assert state.attributes["preset_mode"] == "home" - assert state.attributes["supported_features"] == 18 - assert state.attributes["target_temp_high"] == 24.0 - assert state.attributes["target_temp_low"] == 20.5 + assert state.attributes["supported_features"] == 17 + assert state.attributes["temperature"] == 20.5 assert state.attributes["min_temp"] == 4.0 assert state.attributes["max_temp"] == 30.0 assert state.attributes["target_temp_step"] == 0.1 @@ -286,7 +285,7 @@ async def test_anna_climate_entity_climate_changes( await hass.services.async_call( "climate", "set_hvac_mode", - {"entity_id": "climate.anna", "hvac_mode": "heat_cool"}, + {"entity_id": "climate.anna", "hvac_mode": "heat"}, blocking=True, ) diff --git a/tests/components/plugwise/test_config_flow.py b/tests/components/plugwise/test_config_flow.py index 84d2335b16f..b569cb08ddf 100644 --- a/tests/components/plugwise/test_config_flow.py +++ b/tests/components/plugwise/test_config_flow.py @@ -5,7 +5,9 @@ from plugwise.exceptions import ( ConnectionFailedError, InvalidAuthentication, InvalidSetupError, - PlugwiseException, + InvalidXMLError, + ResponseError, + UnsupportedDeviceError, ) import pytest @@ -97,9 +99,12 @@ def mock_smile(): with patch( "homeassistant.components.plugwise.config_flow.Smile", ) as smile_mock: - smile_mock.PlugwiseException = PlugwiseException - smile_mock.InvalidAuthentication = InvalidAuthentication smile_mock.ConnectionFailedError = ConnectionFailedError + smile_mock.InvalidAuthentication = InvalidAuthentication + smile_mock.InvalidSetupError = InvalidSetupError + smile_mock.InvalidXMLError = InvalidXMLError + smile_mock.ResponseError = ResponseError + smile_mock.UnsupportedDeviceError = UnsupportedDeviceError smile_mock.return_value.connect.return_value = True yield smile_mock.return_value @@ -266,13 +271,15 @@ async def test_zercoconf_discovery_update_configuration( @pytest.mark.parametrize( - "side_effect,reason", + "side_effect, reason", [ + (ConnectionFailedError, "cannot_connect"), (InvalidAuthentication, "invalid_auth"), (InvalidSetupError, "invalid_setup"), - (ConnectionFailedError, "cannot_connect"), - (PlugwiseException, "cannot_connect"), + (InvalidXMLError, "response_error"), + (ResponseError, "response_error"), (RuntimeError, "unknown"), + (UnsupportedDeviceError, "unsupported"), ], ) async def test_flow_errors( diff --git a/tests/components/plugwise/test_init.py b/tests/components/plugwise/test_init.py index 557680cb060..e37ee6b320c 100644 --- a/tests/components/plugwise/test_init.py +++ b/tests/components/plugwise/test_init.py @@ -1,12 +1,12 @@ """Tests for the Plugwise Climate integration.""" -import asyncio from unittest.mock import MagicMock -import aiohttp from plugwise.exceptions import ( ConnectionFailedError, - PlugwiseException, - XMLDataMissingError, + InvalidAuthentication, + InvalidXMLError, + ResponseError, + UnsupportedDeviceError, ) import pytest @@ -43,24 +43,52 @@ async def test_load_unload_config_entry( assert mock_config_entry.state is ConfigEntryState.NOT_LOADED +@pytest.mark.parametrize( + "side_effect, entry_state", + [ + (ConnectionFailedError, ConfigEntryState.SETUP_RETRY), + (InvalidAuthentication, ConfigEntryState.SETUP_ERROR), + (InvalidXMLError, ConfigEntryState.SETUP_RETRY), + (ResponseError, ConfigEntryState.SETUP_RETRY), + (UnsupportedDeviceError, ConfigEntryState.SETUP_ERROR), + ], +) +async def test_gateway_config_entry_not_ready( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_smile_anna: MagicMock, + side_effect: Exception, + entry_state: ConfigEntryState, +) -> None: + """Test the Plugwise configuration entry not ready.""" + mock_smile_anna.connect.side_effect = side_effect + + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert len(mock_smile_anna.connect.mock_calls) == 1 + assert mock_config_entry.state is entry_state + + @pytest.mark.parametrize( "side_effect", [ (ConnectionFailedError), - (PlugwiseException), - (XMLDataMissingError), - (asyncio.TimeoutError), - (aiohttp.ClientConnectionError), + (InvalidAuthentication), + (InvalidXMLError), + (ResponseError), + (UnsupportedDeviceError), ], ) -async def test_config_entry_not_ready( +async def test_coord_config_entry_not_ready( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_smile_anna: MagicMock, side_effect: Exception, ) -> None: """Test the Plugwise configuration entry not ready.""" - mock_smile_anna.connect.side_effect = side_effect + mock_smile_anna.async_update.side_effect = side_effect mock_config_entry.add_to_hass(hass) await hass.config_entries.async_setup(mock_config_entry.entry_id) From e7d4f745ec15f7fc4e1158c999f1aa43ca9d2544 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 25 Nov 2022 11:13:44 +0100 Subject: [PATCH 0713/1033] Expose config_entry and options as properties (#82691) Expose config_entry and options as a property --- homeassistant/config_entries.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index fa37e9ffa1c..4956955cd21 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -1683,9 +1683,19 @@ class OptionsFlowWithConfigEntry(OptionsFlow): def __init__(self, config_entry: ConfigEntry) -> None: """Initialize options flow.""" - self.config_entry = config_entry + self._config_entry = config_entry self._options = deepcopy(dict(config_entry.options)) + @property + def config_entry(self) -> ConfigEntry: + """Return the config entry.""" + return self._config_entry + + @property + def options(self) -> dict[str, Any]: + """Return a mutable copy of the config entry options.""" + return self._options + class EntityRegistryDisabledHandler: """Handler to handle when entities related to config entries updating disabled_by.""" From c71503501662fc9de86937748fecf6ad780432cb Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 25 Nov 2022 11:33:03 +0100 Subject: [PATCH 0714/1033] Add support for raising ConfigEntryError (#82689) --- .../components/fjaraskupan/__init__.py | 2 + homeassistant/components/hassio/__init__.py | 5 +- homeassistant/config_entries.py | 17 +++- homeassistant/exceptions.py | 4 + homeassistant/helpers/update_coordinator.py | 24 ++++- tests/test_config_entries.py | 91 +++++++++++++++++++ 6 files changed, 139 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/fjaraskupan/__init__.py b/homeassistant/components/fjaraskupan/__init__.py index 4a6c40fbeae..66de2ddb8a6 100644 --- a/homeassistant/components/fjaraskupan/__init__.py +++ b/homeassistant/components/fjaraskupan/__init__.py @@ -70,12 +70,14 @@ class Coordinator(DataUpdateCoordinator[State]): log_failures: bool = True, raise_on_auth_failed: bool = False, scheduled: bool = False, + raise_on_entry_error: bool = False, ) -> None: self._refresh_was_scheduled = scheduled await super()._async_refresh( log_failures=log_failures, raise_on_auth_failed=raise_on_auth_failed, scheduled=scheduled, + raise_on_entry_error=raise_on_entry_error, ) async def _async_update_data(self) -> State: diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 598871f57d5..467d1e878b9 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -866,6 +866,7 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator): log_failures: bool = True, raise_on_auth_failed: bool = False, scheduled: bool = False, + raise_on_entry_error: bool = False, ) -> None: """Refresh data.""" if not scheduled: @@ -874,4 +875,6 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator): await self.hassio.refresh_updates() except HassioAPIError as err: _LOGGER.warning("Error on Supervisor API: %s", err) - await super()._async_refresh(log_failures, raise_on_auth_failed, scheduled) + await super()._async_refresh( + log_failures, raise_on_auth_failed, scheduled, raise_on_entry_error + ) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 4956955cd21..e619c3e73f2 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -19,7 +19,12 @@ from .components import persistent_notification from .const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP, Platform from .core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback from .data_entry_flow import FlowResult -from .exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady, HomeAssistantError +from .exceptions import ( + ConfigEntryAuthFailed, + ConfigEntryError, + ConfigEntryNotReady, + HomeAssistantError, +) from .helpers import device_registry, entity_registry, storage from .helpers.dispatcher import async_dispatcher_send from .helpers.event import async_call_later @@ -371,6 +376,16 @@ class ConfigEntry: "%s.async_setup_entry did not return boolean", integration.domain ) result = False + except ConfigEntryError as ex: + error_reason = str(ex) or "Unknown fatal config entry error" + _LOGGER.exception( + "Error setting up entry %s for %s: %s", + self.title, + self.domain, + error_reason, + ) + await self._async_process_on_unload() + result = False except ConfigEntryAuthFailed as ex: message = str(ex) auth_base_message = "could not authenticate" diff --git a/homeassistant/exceptions.py b/homeassistant/exceptions.py index 4f2599aa2da..77ac2938cf2 100644 --- a/homeassistant/exceptions.py +++ b/homeassistant/exceptions.py @@ -114,6 +114,10 @@ class PlatformNotReady(IntegrationError): """Error to indicate that platform is not ready.""" +class ConfigEntryError(IntegrationError): + """Error to indicate that config entry setup has failed.""" + + class ConfigEntryNotReady(IntegrationError): """Error to indicate that config entry is not ready.""" diff --git a/homeassistant/helpers/update_coordinator.py b/homeassistant/helpers/update_coordinator.py index f25691ae504..205a7848613 100644 --- a/homeassistant/helpers/update_coordinator.py +++ b/homeassistant/helpers/update_coordinator.py @@ -15,7 +15,11 @@ import requests from homeassistant import config_entries from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback -from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.exceptions import ( + ConfigEntryAuthFailed, + ConfigEntryError, + ConfigEntryNotReady, +) from homeassistant.util.dt import utcnow from . import entity, event @@ -183,7 +187,9 @@ class DataUpdateCoordinator(Generic[_T]): fails. Additionally logging is handled by config entry setup to ensure that multiple retries do not cause log spam. """ - await self._async_refresh(log_failures=False, raise_on_auth_failed=True) + await self._async_refresh( + log_failures=False, raise_on_auth_failed=True, raise_on_entry_error=True + ) if self.last_update_success: return ex = ConfigEntryNotReady() @@ -199,6 +205,7 @@ class DataUpdateCoordinator(Generic[_T]): log_failures: bool = True, raise_on_auth_failed: bool = False, scheduled: bool = False, + raise_on_entry_error: bool = False, ) -> None: """Refresh data.""" if self._unsub_refresh: @@ -250,6 +257,19 @@ class DataUpdateCoordinator(Generic[_T]): self.logger.error("Error fetching %s data: %s", self.name, err) self.last_update_success = False + except ConfigEntryError as err: + self.last_exception = err + if self.last_update_success: + if log_failures: + self.logger.error( + "Config entry setup failed while fetching %s data: %s", + self.name, + err, + ) + self.last_update_success = False + if raise_on_entry_error: + raise + except ConfigEntryAuthFailed as err: auth_failed = True self.last_exception = err diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 68ece465226..b1e3fd760d5 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -20,6 +20,7 @@ from homeassistant.core import CoreState, Event, HomeAssistant, callback from homeassistant.data_entry_flow import BaseServiceInfo, FlowResult, FlowResultType from homeassistant.exceptions import ( ConfigEntryAuthFailed, + ConfigEntryError, ConfigEntryNotReady, HomeAssistantError, ) @@ -2866,6 +2867,96 @@ async def test_entry_reload_calls_on_unload_listeners(hass, manager): assert entry.state is config_entries.ConfigEntryState.LOADED +async def test_setup_raise_entry_error(hass, caplog): + """Test a setup raising ConfigEntryError.""" + entry = MockConfigEntry(title="test_title", domain="test") + + mock_setup_entry = AsyncMock( + side_effect=ConfigEntryError("Incompatible firmware version") + ) + mock_integration(hass, MockModule("test", async_setup_entry=mock_setup_entry)) + mock_entity_platform(hass, "config_flow.test", None) + + await entry.async_setup(hass) + await hass.async_block_till_done() + assert ( + "Error setting up entry test_title for test: Incompatible firmware version" + in caplog.text + ) + + assert entry.state is config_entries.ConfigEntryState.SETUP_ERROR + assert entry.reason == "Incompatible firmware version" + + +async def test_setup_raise_entry_error_from_first_coordinator_update(hass, caplog): + """Test async_config_entry_first_refresh raises ConfigEntryError.""" + entry = MockConfigEntry(title="test_title", domain="test") + + async def async_setup_entry(hass, entry): + """Mock setup entry with a simple coordinator.""" + + async def _async_update_data(): + raise ConfigEntryError("Incompatible firmware version") + + coordinator = DataUpdateCoordinator( + hass, + logging.getLogger(__name__), + name="any", + update_method=_async_update_data, + update_interval=timedelta(seconds=1000), + ) + + await coordinator.async_config_entry_first_refresh() + return True + + mock_integration(hass, MockModule("test", async_setup_entry=async_setup_entry)) + mock_entity_platform(hass, "config_flow.test", None) + + await entry.async_setup(hass) + await hass.async_block_till_done() + assert ( + "Error setting up entry test_title for test: Incompatible firmware version" + in caplog.text + ) + + assert entry.state is config_entries.ConfigEntryState.SETUP_ERROR + assert entry.reason == "Incompatible firmware version" + + +async def test_setup_not_raise_entry_error_from_future_coordinator_update(hass, caplog): + """Test a coordinator not raises ConfigEntryError in the future.""" + entry = MockConfigEntry(title="test_title", domain="test") + + async def async_setup_entry(hass, entry): + """Mock setup entry with a simple coordinator.""" + + async def _async_update_data(): + raise ConfigEntryError("Incompatible firmware version") + + coordinator = DataUpdateCoordinator( + hass, + logging.getLogger(__name__), + name="any", + update_method=_async_update_data, + update_interval=timedelta(seconds=1000), + ) + + await coordinator.async_refresh() + return True + + mock_integration(hass, MockModule("test", async_setup_entry=async_setup_entry)) + mock_entity_platform(hass, "config_flow.test", None) + + await entry.async_setup(hass) + await hass.async_block_till_done() + assert ( + "Config entry setup failed while fetching any data: Incompatible firmware version" + in caplog.text + ) + + assert entry.state is config_entries.ConfigEntryState.LOADED + + async def test_setup_raise_auth_failed(hass, caplog): """Test a setup raising ConfigEntryAuthFailed.""" entry = MockConfigEntry(title="test_title", domain="test") From 5257875ac604426c7195df2a32788dd1b73e3096 Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Fri, 25 Nov 2022 12:30:33 +0100 Subject: [PATCH 0715/1033] Use SensorDeviceClass.DISTANCE for here_travel_time (#79159) * Use SensorDeviceClass.DISTANCE in here_travel_time * Removed unused import * Use explicit units and conversion --- .../components/here_travel_time/__init__.py | 3 +- .../here_travel_time/config_flow.py | 39 +++----- .../components/here_travel_time/const.py | 6 -- .../here_travel_time/coordinator.py | 25 ++---- .../components/here_travel_time/model.py | 1 - .../components/here_travel_time/sensor.py | 40 +++------ .../here_travel_time/test_config_flow.py | 30 +------ .../components/here_travel_time/test_init.py | 4 +- .../here_travel_time/test_sensor.py | 89 ++++--------------- 9 files changed, 48 insertions(+), 189 deletions(-) diff --git a/homeassistant/components/here_travel_time/__init__.py b/homeassistant/components/here_travel_time/__init__.py index 28ecf3f3d8d..57ad77e0654 100644 --- a/homeassistant/components/here_travel_time/__init__.py +++ b/homeassistant/components/here_travel_time/__init__.py @@ -4,7 +4,7 @@ from __future__ import annotations import logging from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_API_KEY, CONF_MODE, CONF_UNIT_SYSTEM, Platform +from homeassistant.const import CONF_API_KEY, CONF_MODE, Platform from homeassistant.core import HomeAssistant from homeassistant.util import dt @@ -55,7 +55,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b origin_entity_id=config_entry.data.get(CONF_ORIGIN_ENTITY_ID), travel_mode=config_entry.data[CONF_MODE], route_mode=config_entry.options[CONF_ROUTE_MODE], - units=config_entry.options[CONF_UNIT_SYSTEM], arrival=arrival, departure=departure, ) diff --git a/homeassistant/components/here_travel_time/config_flow.py b/homeassistant/components/here_travel_time/config_flow.py index c0b00ffc876..48a500f17f0 100644 --- a/homeassistant/components/here_travel_time/config_flow.py +++ b/homeassistant/components/here_travel_time/config_flow.py @@ -21,9 +21,8 @@ from homeassistant.const import ( CONF_LONGITUDE, CONF_MODE, CONF_NAME, - CONF_UNIT_SYSTEM, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult import homeassistant.helpers.config_validation as cv from homeassistant.helpers.selector import ( @@ -31,7 +30,6 @@ from homeassistant.helpers.selector import ( LocationSelector, TimeSelector, ) -from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM from .const import ( CONF_ARRIVAL_TIME, @@ -47,18 +45,21 @@ from .const import ( CONF_ROUTE_MODE, DEFAULT_NAME, DOMAIN, - IMPERIAL_UNITS, - METRIC_UNITS, ROUTE_MODE_FASTEST, ROUTE_MODES, TRAVEL_MODE_CAR, TRAVEL_MODE_PUBLIC, TRAVEL_MODES, - UNITS, ) _LOGGER = logging.getLogger(__name__) +DEFAULT_OPTIONS = { + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_ARRIVAL_TIME: None, + CONF_DEPARTURE_TIME: None, +} + async def async_validate_api_key(api_key: str) -> None: """Validate the user input allows us to connect.""" @@ -90,19 +91,6 @@ def get_user_step_schema(data: dict[str, Any]) -> vol.Schema: ) -def default_options(hass: HomeAssistant) -> dict[str, str | None]: - """Get the default options.""" - default = { - CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, - CONF_ARRIVAL_TIME: None, - CONF_DEPARTURE_TIME: None, - CONF_UNIT_SYSTEM: METRIC_UNITS, - } - if hass.config.units is US_CUSTOMARY_SYSTEM: - default[CONF_UNIT_SYSTEM] = IMPERIAL_UNITS - return default - - class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for HERE Travel Time.""" @@ -198,7 +186,7 @@ class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_create_entry( title=self._config[CONF_NAME], data=self._config, - options=default_options(self.hass), + options=DEFAULT_OPTIONS, ) schema = vol.Schema( { @@ -227,7 +215,7 @@ class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_create_entry( title=self._config[CONF_NAME], data=self._config, - options=default_options(self.hass), + options=DEFAULT_OPTIONS, ) schema = vol.Schema( {vol.Required(CONF_DESTINATION_ENTITY_ID): EntitySelector()} @@ -254,21 +242,14 @@ class HERETravelTimeOptionsFlow(config_entries.OptionsFlow): menu_options=["departure_time", "arrival_time", "no_time"], ) - defaults = default_options(self.hass) schema = vol.Schema( { vol.Optional( CONF_ROUTE_MODE, default=self.config_entry.options.get( - CONF_ROUTE_MODE, defaults[CONF_ROUTE_MODE] + CONF_ROUTE_MODE, DEFAULT_OPTIONS[CONF_ROUTE_MODE] ), ): vol.In(ROUTE_MODES), - vol.Optional( - CONF_UNIT_SYSTEM, - default=self.config_entry.options.get( - CONF_UNIT_SYSTEM, defaults[CONF_UNIT_SYSTEM] - ), - ): vol.In(UNITS), } ) diff --git a/homeassistant/components/here_travel_time/const.py b/homeassistant/components/here_travel_time/const.py index 4fce053f768..300bbf617cf 100644 --- a/homeassistant/components/here_travel_time/const.py +++ b/homeassistant/components/here_travel_time/const.py @@ -51,17 +51,11 @@ ICONS = { TRAVEL_MODE_TRUCK: ICON_TRUCK, } -IMPERIAL_UNITS = "imperial" -METRIC_UNITS = "metric" -UNITS = [METRIC_UNITS, IMPERIAL_UNITS] - ATTR_DURATION = "duration" ATTR_DISTANCE = "distance" ATTR_ORIGIN = "origin" ATTR_DESTINATION = "destination" -ATTR_UNIT_SYSTEM = "unit_system" - ATTR_DURATION_IN_TRAFFIC = "duration_in_traffic" ATTR_ORIGIN_NAME = "origin_name" ATTR_DESTINATION_NAME = "destination_name" diff --git a/homeassistant/components/here_travel_time/coordinator.py b/homeassistant/components/here_travel_time/coordinator.py index 0d30df51f67..1d3a4edb6b0 100644 --- a/homeassistant/components/here_travel_time/coordinator.py +++ b/homeassistant/components/here_travel_time/coordinator.py @@ -10,7 +10,7 @@ import here_transit from here_transit import HERETransitApi import voluptuous as vol -from homeassistant.const import ATTR_ATTRIBUTION, LENGTH_METERS, LENGTH_MILES +from homeassistant.const import ATTR_ATTRIBUTION, UnitOfLength from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.location import find_coordinates @@ -28,7 +28,6 @@ from .const import ( ATTR_ORIGIN_NAME, DEFAULT_SCAN_INTERVAL, DOMAIN, - IMPERIAL_UNITS, ROUTE_MODE_FASTEST, ) from .model import HERETravelTimeConfig, HERETravelTimeData @@ -100,13 +99,9 @@ class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator): mapped_origin_lon: float = section["departure"]["place"]["location"]["lng"] mapped_destination_lat: float = section["arrival"]["place"]["location"]["lat"] mapped_destination_lon: float = section["arrival"]["place"]["location"]["lng"] - distance: float = summary["length"] - if self.config.units == IMPERIAL_UNITS: - # Convert to miles. - distance = DistanceConverter.convert(distance, LENGTH_METERS, LENGTH_MILES) - else: - # Convert to kilometers - distance = distance / 1000 + distance: float = DistanceConverter.convert( + summary["length"], UnitOfLength.METERS, UnitOfLength.KILOMETERS + ) origin_name: str | None = None if (names := section["spans"][0].get("names")) is not None: origin_name = names[0]["value"] @@ -189,18 +184,14 @@ class HERETransitDataUpdateCoordinator(DataUpdateCoordinator): mapped_destination_lon: float = sections[-1]["arrival"]["place"]["location"][ "lng" ] - distance: float = sum( - section["travelSummary"]["length"] for section in sections + distance: float = DistanceConverter.convert( + sum(section["travelSummary"]["length"] for section in sections), + UnitOfLength.METERS, + UnitOfLength.KILOMETERS, ) duration: float = sum( section["travelSummary"]["duration"] for section in sections ) - if self.config.units == IMPERIAL_UNITS: - # Convert to miles. - distance = DistanceConverter.convert(distance, LENGTH_METERS, LENGTH_MILES) - else: - # Convert to kilometers - distance = distance / 1000 return HERETravelTimeData( { ATTR_ATTRIBUTION: attribution, diff --git a/homeassistant/components/here_travel_time/model.py b/homeassistant/components/here_travel_time/model.py index 737aae78a03..6d8d4826007 100644 --- a/homeassistant/components/here_travel_time/model.py +++ b/homeassistant/components/here_travel_time/model.py @@ -31,6 +31,5 @@ class HERETravelTimeConfig: origin_entity_id: str | None travel_mode: str route_mode: str - units: str arrival: time | None departure: time | None diff --git a/homeassistant/components/here_travel_time/sensor.py b/homeassistant/components/here_travel_time/sensor.py index 1fec002377e..e69d1efe4a4 100644 --- a/homeassistant/components/here_travel_time/sensor.py +++ b/homeassistant/components/here_travel_time/sensor.py @@ -6,6 +6,7 @@ from datetime import timedelta from typing import Any from homeassistant.components.sensor import ( + SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, @@ -17,9 +18,8 @@ from homeassistant.const import ( ATTR_LONGITUDE, CONF_MODE, CONF_NAME, - LENGTH_KILOMETERS, - LENGTH_MILES, TIME_MINUTES, + UnitOfLength, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType @@ -39,7 +39,6 @@ from .const import ( DOMAIN, ICON_CAR, ICONS, - IMPERIAL_UNITS, ) from .coordinator import HERERoutingDataUpdateCoordinator @@ -63,6 +62,14 @@ def sensor_descriptions(travel_mode: str) -> tuple[SensorEntityDescription, ...] state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=TIME_MINUTES, ), + SensorEntityDescription( + name="Distance", + icon=ICONS.get(travel_mode, ICON_CAR), + key=ATTR_DISTANCE, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.DISTANCE, + native_unit_of_measurement=UnitOfLength.KILOMETERS, + ), ) @@ -89,7 +96,6 @@ async def async_setup_entry( ) sensors.append(OriginSensor(entry_id, name, coordinator)) sensors.append(DestinationSensor(entry_id, name, coordinator)) - sensors.append(DistanceSensor(entry_id, name, coordinator)) async_add_entities(sensors) @@ -193,29 +199,3 @@ class DestinationSensor(HERETravelTimeSensor): ATTR_LONGITUDE: self.coordinator.data[ATTR_DESTINATION].split(",")[1], } return None - - -class DistanceSensor(HERETravelTimeSensor): - """Sensor holding information about the distance.""" - - def __init__( - self, - unique_id_prefix: str, - name: str, - coordinator: HERERoutingDataUpdateCoordinator, - ) -> None: - """Initialize the sensor.""" - sensor_description = SensorEntityDescription( - name="Distance", - icon=ICONS.get(coordinator.config.travel_mode, ICON_CAR), - key=ATTR_DISTANCE, - state_class=SensorStateClass.MEASUREMENT, - ) - super().__init__(unique_id_prefix, name, sensor_description, coordinator) - - @property - def native_unit_of_measurement(self) -> str | None: - """Return the unit of measurement of the sensor.""" - if self.coordinator.config.units == IMPERIAL_UNITS: - return LENGTH_MILES - return LENGTH_KILOMETERS diff --git a/tests/components/here_travel_time/test_config_flow.py b/tests/components/here_travel_time/test_config_flow.py index d22d36a4d66..42add4192e5 100644 --- a/tests/components/here_travel_time/test_config_flow.py +++ b/tests/components/here_travel_time/test_config_flow.py @@ -19,20 +19,8 @@ from homeassistant.components.here_travel_time.const import ( TRAVEL_MODE_CAR, TRAVEL_MODE_PUBLIC, ) -from homeassistant.const import ( - CONF_API_KEY, - CONF_MODE, - CONF_NAME, - CONF_UNIT_SYSTEM, - CONF_UNIT_SYSTEM_IMPERIAL, - CONF_UNIT_SYSTEM_METRIC, -) +from homeassistant.const import CONF_API_KEY, CONF_MODE, CONF_NAME from homeassistant.core import HomeAssistant -from homeassistant.util.unit_system import ( - METRIC_SYSTEM, - US_CUSTOMARY_SYSTEM, - UnitSystem, -) from .const import ( API_KEY, @@ -98,7 +86,6 @@ async def option_init_result_fixture(hass: HomeAssistant) -> data_entry_flow.Flo flow["flow_id"], user_input={ CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, - CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, }, ) return result @@ -229,21 +216,11 @@ async def test_step_destination_coordinates( @pytest.mark.usefixtures("valid_response") -@pytest.mark.parametrize( - "unit_system, expected_unit_option", - [ - (METRIC_SYSTEM, CONF_UNIT_SYSTEM_METRIC), - (US_CUSTOMARY_SYSTEM, CONF_UNIT_SYSTEM_IMPERIAL), - ], -) async def test_step_destination_entity( hass: HomeAssistant, origin_step_result: data_entry_flow.FlowResult, - unit_system: UnitSystem, - expected_unit_option: str, ) -> None: """Test the origin coordinates step.""" - hass.config.units = unit_system menu_result = await hass.config_entries.flow.async_configure( origin_step_result["flow_id"], {"next_step_id": "destination_entity"} ) @@ -264,7 +241,6 @@ async def test_step_destination_entity( CONF_MODE: TRAVEL_MODE_CAR, } assert entry.options == { - CONF_UNIT_SYSTEM: expected_unit_option, CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, CONF_ARRIVAL_TIME: None, CONF_DEPARTURE_TIME: None, @@ -340,7 +316,6 @@ async def test_options_flow(hass: HomeAssistant) -> None: result["flow_id"], user_input={ CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, - CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, }, ) @@ -366,7 +341,6 @@ async def test_options_flow_arrival_time_step( assert time_selector_result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY entry = hass.config_entries.async_entries(DOMAIN)[0] assert entry.options == { - CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, CONF_ARRIVAL_TIME: "08:00:00", } @@ -391,7 +365,6 @@ async def test_options_flow_departure_time_step( assert time_selector_result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY entry = hass.config_entries.async_entries(DOMAIN)[0] assert entry.options == { - CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, CONF_DEPARTURE_TIME: "08:00:00", } @@ -409,6 +382,5 @@ async def test_options_flow_no_time_step( assert menu_result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY entry = hass.config_entries.async_entries(DOMAIN)[0] assert entry.options == { - CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, } diff --git a/tests/components/here_travel_time/test_init.py b/tests/components/here_travel_time/test_init.py index b8bbdec241a..18ef2e45410 100644 --- a/tests/components/here_travel_time/test_init.py +++ b/tests/components/here_travel_time/test_init.py @@ -2,7 +2,7 @@ import pytest -from homeassistant.components.here_travel_time.config_flow import default_options +from homeassistant.components.here_travel_time.config_flow import DEFAULT_OPTIONS from homeassistant.components.here_travel_time.const import DOMAIN from homeassistant.core import HomeAssistant @@ -18,7 +18,7 @@ async def test_unload_entry(hass: HomeAssistant) -> None: domain=DOMAIN, unique_id="0123456789", data=DEFAULT_CONFIG, - options=default_options(hass), + options=DEFAULT_OPTIONS, ) entry.add_to_hass(hass) diff --git a/tests/components/here_travel_time/test_sensor.py b/tests/components/here_travel_time/test_sensor.py index 8a6f7f3f5d3..435d24d7b1d 100644 --- a/tests/components/here_travel_time/test_sensor.py +++ b/tests/components/here_travel_time/test_sensor.py @@ -11,7 +11,7 @@ from here_routing import ( ) import pytest -from homeassistant.components.here_travel_time.config_flow import default_options +from homeassistant.components.here_travel_time.config_flow import DEFAULT_OPTIONS from homeassistant.components.here_travel_time.const import ( CONF_ARRIVAL_TIME, CONF_DEPARTURE_TIME, @@ -27,8 +27,6 @@ from homeassistant.components.here_travel_time.const import ( ICON_CAR, ICON_PEDESTRIAN, ICON_TRUCK, - IMPERIAL_UNITS, - METRIC_UNITS, ROUTE_MODE_FASTEST, TRAVEL_MODE_BICYCLE, TRAVEL_MODE_CAR, @@ -41,14 +39,10 @@ from homeassistant.const import ( ATTR_ICON, ATTR_LATITUDE, ATTR_LONGITUDE, - ATTR_UNIT_OF_MEASUREMENT, CONF_API_KEY, CONF_MODE, CONF_NAME, - CONF_UNIT_SYSTEM, EVENT_HOMEASSISTANT_START, - LENGTH_KILOMETERS, - LENGTH_MILES, TIME_MINUTES, ) from homeassistant.core import HomeAssistant @@ -66,51 +60,31 @@ from tests.common import MockConfigEntry @pytest.mark.parametrize( - "mode,icon,unit_system,arrival_time,departure_time,expected_duration,expected_distance,expected_duration_in_traffic,expected_distance_unit", + "mode,icon,arrival_time,departure_time", [ ( TRAVEL_MODE_CAR, ICON_CAR, - "metric", None, None, - "26", - 13.682, - "30", - LENGTH_KILOMETERS, ), ( TRAVEL_MODE_BICYCLE, ICON_BICYCLE, - "metric", None, None, - "26", - 13.682, - "30", - LENGTH_KILOMETERS, ), ( TRAVEL_MODE_PEDESTRIAN, ICON_PEDESTRIAN, - "imperial", None, - None, - "26", - 8.5016, - "30", - LENGTH_MILES, + "08:00:00", ), ( TRAVEL_MODE_TRUCK, ICON_TRUCK, - "metric", None, "08:00:00", - "26", - 13.682, - "30", - LENGTH_KILOMETERS, ), ], ) @@ -119,13 +93,8 @@ async def test_sensor( hass: HomeAssistant, mode, icon, - unit_system, arrival_time, departure_time, - expected_duration, - expected_distance, - expected_duration_in_traffic, - expected_distance_unit, ): """Test that sensor works.""" entry = MockConfigEntry( @@ -144,7 +113,6 @@ async def test_sensor( CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, CONF_ARRIVAL_TIME: arrival_time, CONF_DEPARTURE_TIME: departure_time, - CONF_UNIT_SYSTEM: unit_system, }, ) entry.add_to_hass(hass) @@ -156,23 +124,10 @@ async def test_sensor( duration = hass.states.get("sensor.test_duration") assert duration.attributes.get("unit_of_measurement") == TIME_MINUTES assert duration.attributes.get(ATTR_ICON) == icon - assert duration.state == expected_duration + assert duration.state == "26" - assert ( - hass.states.get("sensor.test_duration_in_traffic").state - == expected_duration_in_traffic - ) - assert float(hass.states.get("sensor.test_distance").state) == pytest.approx( - expected_distance - ) - assert ( - hass.states.get("sensor.test_distance").attributes.get(ATTR_UNIT_OF_MEASUREMENT) - == expected_distance_unit - ) - assert ( - hass.states.get("sensor.test_duration_in_traffic").state - == expected_duration_in_traffic - ) + assert float(hass.states.get("sensor.test_distance").state) == pytest.approx(13.682) + assert hass.states.get("sensor.test_duration_in_traffic").state == "30" assert hass.states.get("sensor.test_origin").state == "22nd St NW" assert ( hass.states.get("sensor.test_origin").attributes.get(ATTR_LATITUDE) @@ -213,7 +168,7 @@ async def test_circular_ref(hass: HomeAssistant, caplog): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, - options=default_options(hass), + options=DEFAULT_OPTIONS, ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -226,16 +181,7 @@ async def test_circular_ref(hass: HomeAssistant, caplog): @pytest.mark.usefixtures("valid_response") -@pytest.mark.parametrize( - "unit_system,expected_distance", - [ - (METRIC_UNITS, "1.883"), - (IMPERIAL_UNITS, "1.1700419549829"), - ], -) -async def test_public_transport( - hass: HomeAssistant, unit_system: str, expected_distance: str -): +async def test_public_transport(hass: HomeAssistant): """Test that public transport mode is handled.""" entry = MockConfigEntry( domain=DOMAIN, @@ -253,7 +199,6 @@ async def test_public_transport( CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, CONF_ARRIVAL_TIME: "08:00:00", CONF_DEPARTURE_TIME: None, - CONF_UNIT_SYSTEM: unit_system, }, ) entry.add_to_hass(hass) @@ -267,9 +212,7 @@ async def test_public_transport( hass.states.get("sensor.test_duration").attributes.get(ATTR_ATTRIBUTION) == "http://creativecommons.org/licenses/by/3.0/it/,Some line names used in this product or service were edited to align with official transportation maps." ) - assert hass.states.get("sensor.test_distance").state == pytest.approx( - expected_distance - ) + assert hass.states.get("sensor.test_distance").state == "1.883" @pytest.mark.usefixtures("no_attribution_response") @@ -287,7 +230,7 @@ async def test_no_attribution_response(hass: HomeAssistant): CONF_MODE: TRAVEL_MODE_PUBLIC, CONF_NAME: "test", }, - options=default_options(hass), + options=DEFAULT_OPTIONS, ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -333,7 +276,7 @@ async def test_entity_ids(hass: HomeAssistant, valid_response: MagicMock): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, - options=default_options(hass), + options=DEFAULT_OPTIONS, ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -370,7 +313,7 @@ async def test_destination_entity_not_found(hass: HomeAssistant, caplog): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, - options=default_options(hass), + options=DEFAULT_OPTIONS, ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -396,7 +339,7 @@ async def test_origin_entity_not_found(hass: HomeAssistant, caplog): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, - options=default_options(hass), + options=DEFAULT_OPTIONS, ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -426,7 +369,7 @@ async def test_invalid_destination_entity_state(hass: HomeAssistant, caplog): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, - options=default_options(hass), + options=DEFAULT_OPTIONS, ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -456,7 +399,7 @@ async def test_invalid_origin_entity_state(hass: HomeAssistant, caplog): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, - options=default_options(hass), + options=DEFAULT_OPTIONS, ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -488,7 +431,7 @@ async def test_route_not_found(hass: HomeAssistant, caplog): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, - options=default_options(hass), + options=DEFAULT_OPTIONS, ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) From 5d7f9aee272966ac67f6998bf9bcd133d06d6907 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Fri, 25 Nov 2022 12:38:51 +0100 Subject: [PATCH 0716/1033] Add number entities to config flow for min_max (#82665) --- homeassistant/components/min_max/config_flow.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/min_max/config_flow.py b/homeassistant/components/min_max/config_flow.py index c3e1c2e884e..b515608042f 100644 --- a/homeassistant/components/min_max/config_flow.py +++ b/homeassistant/components/min_max/config_flow.py @@ -6,6 +6,8 @@ from typing import Any, cast import voluptuous as vol +from homeassistant.components.input_number import DOMAIN as INPUT_NUMBER_DOMAIN +from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import CONF_TYPE from homeassistant.helpers import selector @@ -30,7 +32,10 @@ _STATISTIC_MEASURES = [ OPTIONS_SCHEMA = vol.Schema( { vol.Required(CONF_ENTITY_IDS): selector.EntitySelector( - selector.EntitySelectorConfig(domain=SENSOR_DOMAIN, multiple=True), + selector.EntitySelectorConfig( + domain=[SENSOR_DOMAIN, NUMBER_DOMAIN, INPUT_NUMBER_DOMAIN], + multiple=True, + ), ), vol.Required(CONF_TYPE): selector.SelectSelector( selector.SelectSelectorConfig(options=_STATISTIC_MEASURES), From 95e6faad0aebb7f1483901b5bbfdbcaacf6ee9e8 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 25 Nov 2022 13:41:46 +0100 Subject: [PATCH 0717/1033] Set mqtt quality scale to gold (#82696) --- homeassistant/components/mqtt/manifest.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/manifest.json b/homeassistant/components/mqtt/manifest.json index d37b15769ad..fa6dc8f655b 100644 --- a/homeassistant/components/mqtt/manifest.json +++ b/homeassistant/components/mqtt/manifest.json @@ -6,5 +6,6 @@ "requirements": ["paho-mqtt==1.6.1"], "dependencies": ["file_upload", "http"], "codeowners": ["@emontnemery"], - "iot_class": "local_push" + "iot_class": "local_push", + "quality_scale": "gold" } From 6c615016b854562e83810fdf23b8940f64e74cd3 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 25 Nov 2022 14:30:02 +0100 Subject: [PATCH 0718/1033] Fix None schema in SchemaCommonFlowHandler (#82699) --- .../helpers/schema_config_entry_flow.py | 27 +++++++++------- .../helpers/test_schema_config_entry_flow.py | 31 +++++++++++++++++++ 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index c721b9d4ad6..457f541abfc 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -139,19 +139,21 @@ class SchemaCommonFlowHandler: # User input was validated successfully, update options self._options.update(user_input) - next_step_id: str = step_id if user_input is not None or form_step.schema is None: - # Get next step - if form_step.next_step is None: - # Flow done, create entry or update config entry options - return self._handler.async_create_entry(data=self._options) + return self._show_next_step_or_create_entry(form_step) - if isinstance(form_step.next_step, str): - next_step_id = form_step.next_step - else: - next_step_id = form_step.next_step(self._options) + return self._show_next_step(step_id) - return self._show_next_step(next_step_id) + def _show_next_step_or_create_entry( + self, form_step: SchemaFlowFormStep + ) -> FlowResult: + if form_step.next_step is None: + # Flow done, create entry or update config entry options + return self._handler.async_create_entry(data=self._options) + + if isinstance(form_step.next_step, str): + return self._show_next_step(form_step.next_step) + return self._show_next_step(form_step.next_step(self._options)) def _show_next_step( self, @@ -173,7 +175,10 @@ class SchemaCommonFlowHandler: form_step = cast(SchemaFlowFormStep, self._flow[next_step_id]) - if (data_schema := self._get_schema(form_step)) and data_schema.schema: + if (data_schema := self._get_schema(form_step)) is None: + return self._show_next_step_or_create_entry(form_step) + + if data_schema.schema: # Make a copy of the schema with suggested values set to saved options schema = {} for key, val in data_schema.schema.items(): diff --git a/tests/helpers/test_schema_config_entry_flow.py b/tests/helpers/test_schema_config_entry_flow.py index 7ea5768fcad..7efc70cb5d5 100644 --- a/tests/helpers/test_schema_config_entry_flow.py +++ b/tests/helpers/test_schema_config_entry_flow.py @@ -70,3 +70,34 @@ async def test_menu_step(hass: HomeAssistant) -> None: result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.CREATE_ENTRY + + +async def test_schema_none(hass: HomeAssistant) -> None: + """Test SchemaFlowFormStep with schema set to None.""" + + CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { + "user": SchemaFlowFormStep(next_step="option1"), + "option1": SchemaFlowFormStep(vol.Schema({}), next_step="pass"), + "pass": SchemaFlowFormStep(next_step="option3"), + "option3": SchemaFlowFormStep(vol.Schema({})), + } + + class TestConfigFlow(SchemaConfigFlowHandler, domain=TEST_DOMAIN): + """Handle a config or options flow for Derivative.""" + + config_flow = CONFIG_FLOW + + mock_platform(hass, f"{TEST_DOMAIN}.config_flow") + with patch.dict(config_entries.HANDLERS, {TEST_DOMAIN: TestConfigFlow}): + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": "user"} + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "option1" + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "option3" + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + assert result["type"] == FlowResultType.CREATE_ENTRY From fcba9974e58f724677deebbec7ce05973c0a3f05 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 25 Nov 2022 14:40:20 +0100 Subject: [PATCH 0719/1033] Rename options to suggested_values (#82700) --- homeassistant/helpers/schema_config_entry_flow.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 457f541abfc..f992b187e9d 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -162,9 +162,9 @@ class SchemaCommonFlowHandler: user_input: dict[str, Any] | None = None, ) -> FlowResult: """Show form for next step.""" - options = dict(self._options) + suggested_values = dict(self._options) if user_input: - options.update(user_input) + suggested_values.update(user_input) if isinstance(self._flow[next_step_id], SchemaFlowMenuStep): menu_step = cast(SchemaFlowMenuStep, self._flow[next_step_id]) @@ -193,10 +193,10 @@ class SchemaCommonFlowHandler: continue new_key = key - if key in options and isinstance(key, vol.Marker): + if key in suggested_values and isinstance(key, vol.Marker): # Copy the marker to not modify the flow schema new_key = copy.copy(key) - new_key.description = {"suggested_value": options[key]} + new_key.description = {"suggested_value": suggested_values[key]} schema[new_key] = val data_schema = vol.Schema(schema) From be13f3fbcfe92ec4c7f0d273f958e9a68a779c5a Mon Sep 17 00:00:00 2001 From: Klaas Schoute Date: Fri, 25 Nov 2022 15:05:01 +0100 Subject: [PATCH 0720/1033] Add API key validation for Forecast.Solar (#80856) Co-authored-by: Martin Hjelmare Co-authored-by: Franck Nijhof Co-authored-by: Franck Nijhof --- .../components/forecast_solar/config_flow.py | 16 ++- .../components/forecast_solar/strings.json | 3 + .../forecast_solar/translations/en.json | 3 + tests/components/forecast_solar/conftest.py | 11 +- .../forecast_solar/test_config_flow.py | 121 ++++++++++++++---- 5 files changed, 126 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/forecast_solar/config_flow.py b/homeassistant/components/forecast_solar/config_flow.py index 600ed363a8b..e74585da35b 100644 --- a/homeassistant/components/forecast_solar/config_flow.py +++ b/homeassistant/components/forecast_solar/config_flow.py @@ -1,6 +1,7 @@ """Config flow for Forecast.Solar integration.""" from __future__ import annotations +import re from typing import Any import voluptuous as vol @@ -9,7 +10,7 @@ from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult -import homeassistant.helpers.config_validation as cv +from homeassistant.helpers import config_validation as cv from .const import ( CONF_AZIMUTH, @@ -20,6 +21,8 @@ from .const import ( DOMAIN, ) +RE_API_KEY = re.compile(r"^[a-zA-Z0-9]{16}$") + class ForecastSolarFlowHandler(ConfigFlow, domain=DOMAIN): """Handle a config flow for Forecast.Solar.""" @@ -88,8 +91,16 @@ class ForecastSolarOptionFlowHandler(OptionsFlow): self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Manage the options.""" + errors = {} if user_input is not None: - return self.async_create_entry(title="", data=user_input) + if (api_key := user_input.get(CONF_API_KEY)) and RE_API_KEY.match( + api_key + ) is None: + errors[CONF_API_KEY] = "invalid_api_key" + else: + return self.async_create_entry( + title="", data=user_input | {CONF_API_KEY: api_key or None} + ) return self.async_show_form( step_id="init", @@ -129,4 +140,5 @@ class ForecastSolarOptionFlowHandler(OptionsFlow): ): vol.Coerce(int), } ), + errors=errors, ) diff --git a/homeassistant/components/forecast_solar/strings.json b/homeassistant/components/forecast_solar/strings.json index 041935c59ef..a7bc0190f5f 100644 --- a/homeassistant/components/forecast_solar/strings.json +++ b/homeassistant/components/forecast_solar/strings.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]" + }, "step": { "init": { "description": "These values allow tweaking the Forecast.Solar result. Please refer to the documentation if a field is unclear.", diff --git a/homeassistant/components/forecast_solar/translations/en.json b/homeassistant/components/forecast_solar/translations/en.json index 56c210a7c19..35542dcdf75 100644 --- a/homeassistant/components/forecast_solar/translations/en.json +++ b/homeassistant/components/forecast_solar/translations/en.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Invalid API key" + }, "step": { "init": { "data": { diff --git a/tests/components/forecast_solar/conftest.py b/tests/components/forecast_solar/conftest.py index 31256c95866..ea6eb40b542 100644 --- a/tests/components/forecast_solar/conftest.py +++ b/tests/components/forecast_solar/conftest.py @@ -2,7 +2,7 @@ from collections.abc import Generator from datetime import datetime, timedelta -from unittest.mock import MagicMock, patch +from unittest.mock import AsyncMock, MagicMock, patch from forecast_solar import models import pytest @@ -22,6 +22,15 @@ from homeassistant.util import dt as dt_util from tests.common import MockConfigEntry +@pytest.fixture +def mock_setup_entry() -> Generator[AsyncMock, None, None]: + """Mock setting up a config entry.""" + with patch( + "homeassistant.components.forecast_solar.async_setup_entry", return_value=True + ) as mock_setup: + yield mock_setup + + @pytest.fixture def mock_config_entry() -> MockConfigEntry: """Return the default mocked config entry.""" diff --git a/tests/components/forecast_solar/test_config_flow.py b/tests/components/forecast_solar/test_config_flow.py index 2380e65aabb..616dcba4a36 100644 --- a/tests/components/forecast_solar/test_config_flow.py +++ b/tests/components/forecast_solar/test_config_flow.py @@ -1,5 +1,5 @@ """Test the Forecast.Solar config flow.""" -from unittest.mock import patch +from unittest.mock import AsyncMock from homeassistant.components.forecast_solar.const import ( CONF_AZIMUTH, @@ -17,7 +17,7 @@ from homeassistant.data_entry_flow import FlowResultType from tests.common import MockConfigEntry -async def test_user_flow(hass: HomeAssistant) -> None: +async def test_user_flow(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None: """Test the full user configuration flow.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} @@ -27,20 +27,17 @@ async def test_user_flow(hass: HomeAssistant) -> None: assert result.get("step_id") == SOURCE_USER assert "flow_id" in result - with patch( - "homeassistant.components.forecast_solar.async_setup_entry", return_value=True - ) as mock_setup_entry: - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={ - CONF_NAME: "Name", - CONF_LATITUDE: 52.42, - CONF_LONGITUDE: 4.42, - CONF_AZIMUTH: 142, - CONF_DECLINATION: 42, - CONF_MODULES_POWER: 4242, - }, - ) + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_NAME: "Name", + CONF_LATITUDE: 52.42, + CONF_LONGITUDE: 4.42, + CONF_AZIMUTH: 142, + CONF_DECLINATION: 42, + CONF_MODULES_POWER: 4242, + }, + ) assert result2.get("type") == FlowResultType.CREATE_ENTRY assert result2.get("title") == "Name" @@ -57,16 +54,15 @@ async def test_user_flow(hass: HomeAssistant) -> None: assert len(mock_setup_entry.mock_calls) == 1 -async def test_options_flow( - hass: HomeAssistant, mock_config_entry: MockConfigEntry +async def test_options_flow_invalid_api( + hass: HomeAssistant, + mock_setup_entry: AsyncMock, + mock_config_entry: MockConfigEntry, ) -> None: - """Test config flow options.""" + """Test options config flow when API key is invalid.""" mock_config_entry.add_to_hass(hass) - with patch( - "homeassistant.components.forecast_solar.async_setup_entry", return_value=True - ): - await hass.config_entries.async_setup(mock_config_entry.entry_id) - await hass.async_block_till_done() + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() result = await hass.config_entries.options.async_init(mock_config_entry.entry_id) @@ -85,10 +81,85 @@ async def test_options_flow( CONF_INVERTER_SIZE: 2000, }, ) + await hass.async_block_till_done() + + assert result2.get("type") == FlowResultType.FORM + assert result2["errors"] == {CONF_API_KEY: "invalid_api_key"} + + +async def test_options_flow( + hass: HomeAssistant, + mock_setup_entry: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test config flow options.""" + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + result = await hass.config_entries.options.async_init(mock_config_entry.entry_id) + + assert result.get("type") == FlowResultType.FORM + assert result.get("step_id") == "init" + assert "flow_id" in result + + # With the API key + result2 = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_API_KEY: "SolarForecast150", + CONF_DECLINATION: 21, + CONF_AZIMUTH: 22, + CONF_MODULES_POWER: 2122, + CONF_DAMPING: 0.25, + CONF_INVERTER_SIZE: 2000, + }, + ) + await hass.async_block_till_done() assert result2.get("type") == FlowResultType.CREATE_ENTRY assert result2.get("data") == { - CONF_API_KEY: "solarPOWER!", + CONF_API_KEY: "SolarForecast150", + CONF_DECLINATION: 21, + CONF_AZIMUTH: 22, + CONF_MODULES_POWER: 2122, + CONF_DAMPING: 0.25, + CONF_INVERTER_SIZE: 2000, + } + + +async def test_options_flow_without_key( + hass: HomeAssistant, + mock_setup_entry: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test config flow options.""" + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + result = await hass.config_entries.options.async_init(mock_config_entry.entry_id) + + assert result.get("type") == FlowResultType.FORM + assert result.get("step_id") == "init" + assert "flow_id" in result + + # Without the API key + result2 = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_DECLINATION: 21, + CONF_AZIMUTH: 22, + CONF_MODULES_POWER: 2122, + CONF_DAMPING: 0.25, + CONF_INVERTER_SIZE: 2000, + }, + ) + await hass.async_block_till_done() + + assert result2.get("type") == FlowResultType.CREATE_ENTRY + assert result2.get("data") == { + CONF_API_KEY: None, CONF_DECLINATION: 21, CONF_AZIMUTH: 22, CONF_MODULES_POWER: 2122, From fd3e996a1e413f05bb1ebdeec3a8070bea064724 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 25 Nov 2022 15:11:09 +0100 Subject: [PATCH 0721/1033] Merge test files (#82703) --- .../helpers/test_helper_config_entry_flow.py | 286 ------------------ .../helpers/test_schema_config_entry_flow.py | 278 ++++++++++++++++- 2 files changed, 276 insertions(+), 288 deletions(-) delete mode 100644 tests/helpers/test_helper_config_entry_flow.py diff --git a/tests/helpers/test_helper_config_entry_flow.py b/tests/helpers/test_helper_config_entry_flow.py deleted file mode 100644 index 2967b202efe..00000000000 --- a/tests/helpers/test_helper_config_entry_flow.py +++ /dev/null @@ -1,286 +0,0 @@ -"""Test schema_config_entry_flow.""" -import pytest -import voluptuous as vol - -from homeassistant import data_entry_flow -from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er -from homeassistant.helpers.schema_config_entry_flow import ( - SchemaConfigFlowHandler, - SchemaFlowFormStep, - SchemaFlowMenuStep, - wrapped_entity_config_entry_title, -) -from homeassistant.util.decorator import Registry - -from tests.common import MockConfigEntry - - -@pytest.fixture -def manager(): - """Return a flow manager.""" - handlers = Registry() - entries = [] - - class FlowManager(data_entry_flow.FlowManager): - """Test flow manager.""" - - async def async_create_flow(self, handler_key, *, context, data): - """Test create flow.""" - handler = handlers.get(handler_key) - - if handler is None: - raise data_entry_flow.UnknownHandler - - flow = handler() - flow.init_step = context.get("init_step", "init") - return flow - - async def async_finish_flow(self, flow, result): - """Test finish flow.""" - if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY: - result["source"] = flow.context.get("source") - entries.append(result) - return result - - mgr = FlowManager(None) - mgr.mock_created_entries = entries - mgr.mock_reg_handler = handlers.register - return mgr - - -async def test_name(hass: HomeAssistant) -> None: - """Test the config flow name is copied from registry entry, with fallback to state.""" - registry = er.async_get(hass) - entity_id = "switch.ceiling" - - # No entry or state, use Object ID - assert wrapped_entity_config_entry_title(hass, entity_id) == "ceiling" - - # State set, use name from state - hass.states.async_set(entity_id, "on", {"friendly_name": "State Name"}) - assert wrapped_entity_config_entry_title(hass, entity_id) == "State Name" - - # Entity registered, use original name from registry entry - hass.states.async_remove(entity_id) - entry = registry.async_get_or_create( - "switch", - "test", - "unique", - suggested_object_id="ceiling", - original_name="Original Name", - ) - hass.states.async_set(entity_id, "on", {"friendly_name": "State Name"}) - assert entry.entity_id == entity_id - assert wrapped_entity_config_entry_title(hass, entity_id) == "Original Name" - assert wrapped_entity_config_entry_title(hass, entry.id) == "Original Name" - - # Entity has customized name - registry.async_update_entity("switch.ceiling", name="Custom Name") - assert wrapped_entity_config_entry_title(hass, entity_id) == "Custom Name" - assert wrapped_entity_config_entry_title(hass, entry.id) == "Custom Name" - - -@pytest.mark.parametrize("marker", (vol.Required, vol.Optional)) -async def test_config_flow_advanced_option( - hass: HomeAssistant, manager: data_entry_flow.FlowManager, marker -): - """Test handling of advanced options in config flow.""" - manager.hass = hass - - CONFIG_SCHEMA = vol.Schema( - { - marker("option1"): str, - marker("advanced_no_default", description={"advanced": True}): str, - marker( - "advanced_default", - default="a very reasonable default", - description={"advanced": True}, - ): str, - } - ) - - CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "init": SchemaFlowFormStep(CONFIG_SCHEMA) - } - - @manager.mock_reg_handler("test") - class TestFlow(SchemaConfigFlowHandler): - config_flow = CONFIG_FLOW - - # Start flow in basic mode - result = await manager.async_init("test") - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert list(result["data_schema"].schema.keys()) == ["option1"] - - result = await manager.async_configure(result["flow_id"], {"option1": "blabla"}) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["data"] == {} - assert result["options"] == { - "advanced_default": "a very reasonable default", - "option1": "blabla", - } - for option in result["options"]: - # Make sure we didn't get the Optional or Required instance as key - assert isinstance(option, str) - - # Start flow in advanced mode - result = await manager.async_init("test", context={"show_advanced_options": True}) - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert list(result["data_schema"].schema.keys()) == [ - "option1", - "advanced_no_default", - "advanced_default", - ] - - result = await manager.async_configure( - result["flow_id"], {"advanced_no_default": "abc123", "option1": "blabla"} - ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["data"] == {} - assert result["options"] == { - "advanced_default": "a very reasonable default", - "advanced_no_default": "abc123", - "option1": "blabla", - } - for option in result["options"]: - # Make sure we didn't get the Optional or Required instance as key - assert isinstance(option, str) - - # Start flow in advanced mode - result = await manager.async_init("test", context={"show_advanced_options": True}) - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert list(result["data_schema"].schema.keys()) == [ - "option1", - "advanced_no_default", - "advanced_default", - ] - - result = await manager.async_configure( - result["flow_id"], - { - "advanced_default": "not default", - "advanced_no_default": "abc123", - "option1": "blabla", - }, - ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["data"] == {} - assert result["options"] == { - "advanced_default": "not default", - "advanced_no_default": "abc123", - "option1": "blabla", - } - for option in result["options"]: - # Make sure we didn't get the Optional or Required instance as key - assert isinstance(option, str) - - -@pytest.mark.parametrize("marker", (vol.Required, vol.Optional)) -async def test_options_flow_advanced_option( - hass: HomeAssistant, manager: data_entry_flow.FlowManager, marker -): - """Test handling of advanced options in options flow.""" - manager.hass = hass - - OPTIONS_SCHEMA = vol.Schema( - { - marker("option1"): str, - marker("advanced_no_default", description={"advanced": True}): str, - marker( - "advanced_default", - default="a very reasonable default", - description={"advanced": True}, - ): str, - } - ) - - OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { - "init": SchemaFlowFormStep(OPTIONS_SCHEMA) - } - - class TestFlow(SchemaConfigFlowHandler, domain="test"): - config_flow = {} - options_flow = OPTIONS_FLOW - - config_entry = MockConfigEntry( - data={}, - domain="test", - options={ - "option1": "blabla", - "advanced_no_default": "abc123", - "advanced_default": "not default", - }, - ) - config_entry.add_to_hass(hass) - - # Start flow in basic mode - result = await hass.config_entries.options.async_init(config_entry.entry_id) - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert list(result["data_schema"].schema.keys()) == ["option1"] - - result = await hass.config_entries.options.async_configure( - result["flow_id"], {"option1": "blublu"} - ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["data"] == { - "advanced_default": "not default", - "advanced_no_default": "abc123", - "option1": "blublu", - } - for option in result["data"]: - # Make sure we didn't get the Optional or Required instance as key - assert isinstance(option, str) - - # Start flow in advanced mode - result = await hass.config_entries.options.async_init( - config_entry.entry_id, context={"show_advanced_options": True} - ) - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert list(result["data_schema"].schema.keys()) == [ - "option1", - "advanced_no_default", - "advanced_default", - ] - - result = await hass.config_entries.options.async_configure( - result["flow_id"], {"advanced_no_default": "def456", "option1": "blabla"} - ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["data"] == { - "advanced_default": "a very reasonable default", - "advanced_no_default": "def456", - "option1": "blabla", - } - for option in result["data"]: - # Make sure we didn't get the Optional or Required instance as key - assert isinstance(option, str) - - # Start flow in advanced mode - result = await hass.config_entries.options.async_init( - config_entry.entry_id, context={"show_advanced_options": True} - ) - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert list(result["data_schema"].schema.keys()) == [ - "option1", - "advanced_no_default", - "advanced_default", - ] - - result = await hass.config_entries.options.async_configure( - result["flow_id"], - { - "advanced_default": "also not default", - "advanced_no_default": "abc123", - "option1": "blabla", - }, - ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["data"] == { - "advanced_default": "also not default", - "advanced_no_default": "abc123", - "option1": "blabla", - } - for option in result["data"]: - # Make sure we didn't get the Optional or Required instance as key - assert isinstance(option, str) diff --git a/tests/helpers/test_schema_config_entry_flow.py b/tests/helpers/test_schema_config_entry_flow.py index 7efc70cb5d5..de99c7b3bce 100644 --- a/tests/helpers/test_schema_config_entry_flow.py +++ b/tests/helpers/test_schema_config_entry_flow.py @@ -3,22 +3,296 @@ from __future__ import annotations from unittest.mock import patch +import pytest import voluptuous as vol -from homeassistant import config_entries +from homeassistant import config_entries, data_entry_flow from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, SchemaFlowFormStep, SchemaFlowMenuStep, + wrapped_entity_config_entry_title, ) +from homeassistant.util.decorator import Registry -from tests.common import mock_platform +from tests.common import MockConfigEntry, mock_platform TEST_DOMAIN = "test" +@pytest.fixture +def manager(): + """Return a flow manager.""" + handlers = Registry() + entries = [] + + class FlowManager(data_entry_flow.FlowManager): + """Test flow manager.""" + + async def async_create_flow(self, handler_key, *, context, data): + """Test create flow.""" + handler = handlers.get(handler_key) + + if handler is None: + raise data_entry_flow.UnknownHandler + + flow = handler() + flow.init_step = context.get("init_step", "init") + return flow + + async def async_finish_flow(self, flow, result): + """Test finish flow.""" + if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY: + result["source"] = flow.context.get("source") + entries.append(result) + return result + + mgr = FlowManager(None) + mgr.mock_created_entries = entries + mgr.mock_reg_handler = handlers.register + return mgr + + +async def test_name(hass: HomeAssistant) -> None: + """Test the config flow name is copied from registry entry, with fallback to state.""" + registry = er.async_get(hass) + entity_id = "switch.ceiling" + + # No entry or state, use Object ID + assert wrapped_entity_config_entry_title(hass, entity_id) == "ceiling" + + # State set, use name from state + hass.states.async_set(entity_id, "on", {"friendly_name": "State Name"}) + assert wrapped_entity_config_entry_title(hass, entity_id) == "State Name" + + # Entity registered, use original name from registry entry + hass.states.async_remove(entity_id) + entry = registry.async_get_or_create( + "switch", + "test", + "unique", + suggested_object_id="ceiling", + original_name="Original Name", + ) + hass.states.async_set(entity_id, "on", {"friendly_name": "State Name"}) + assert entry.entity_id == entity_id + assert wrapped_entity_config_entry_title(hass, entity_id) == "Original Name" + assert wrapped_entity_config_entry_title(hass, entry.id) == "Original Name" + + # Entity has customized name + registry.async_update_entity("switch.ceiling", name="Custom Name") + assert wrapped_entity_config_entry_title(hass, entity_id) == "Custom Name" + assert wrapped_entity_config_entry_title(hass, entry.id) == "Custom Name" + + +@pytest.mark.parametrize("marker", (vol.Required, vol.Optional)) +async def test_config_flow_advanced_option( + hass: HomeAssistant, manager: data_entry_flow.FlowManager, marker +): + """Test handling of advanced options in config flow.""" + manager.hass = hass + + CONFIG_SCHEMA = vol.Schema( + { + marker("option1"): str, + marker("advanced_no_default", description={"advanced": True}): str, + marker( + "advanced_default", + default="a very reasonable default", + description={"advanced": True}, + ): str, + } + ) + + CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { + "init": SchemaFlowFormStep(CONFIG_SCHEMA) + } + + @manager.mock_reg_handler("test") + class TestFlow(SchemaConfigFlowHandler): + config_flow = CONFIG_FLOW + + # Start flow in basic mode + result = await manager.async_init("test") + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert list(result["data_schema"].schema.keys()) == ["option1"] + + result = await manager.async_configure(result["flow_id"], {"option1": "blabla"}) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"] == {} + assert result["options"] == { + "advanced_default": "a very reasonable default", + "option1": "blabla", + } + for option in result["options"]: + # Make sure we didn't get the Optional or Required instance as key + assert isinstance(option, str) + + # Start flow in advanced mode + result = await manager.async_init("test", context={"show_advanced_options": True}) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert list(result["data_schema"].schema.keys()) == [ + "option1", + "advanced_no_default", + "advanced_default", + ] + + result = await manager.async_configure( + result["flow_id"], {"advanced_no_default": "abc123", "option1": "blabla"} + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"] == {} + assert result["options"] == { + "advanced_default": "a very reasonable default", + "advanced_no_default": "abc123", + "option1": "blabla", + } + for option in result["options"]: + # Make sure we didn't get the Optional or Required instance as key + assert isinstance(option, str) + + # Start flow in advanced mode + result = await manager.async_init("test", context={"show_advanced_options": True}) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert list(result["data_schema"].schema.keys()) == [ + "option1", + "advanced_no_default", + "advanced_default", + ] + + result = await manager.async_configure( + result["flow_id"], + { + "advanced_default": "not default", + "advanced_no_default": "abc123", + "option1": "blabla", + }, + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"] == {} + assert result["options"] == { + "advanced_default": "not default", + "advanced_no_default": "abc123", + "option1": "blabla", + } + for option in result["options"]: + # Make sure we didn't get the Optional or Required instance as key + assert isinstance(option, str) + + +@pytest.mark.parametrize("marker", (vol.Required, vol.Optional)) +async def test_options_flow_advanced_option( + hass: HomeAssistant, manager: data_entry_flow.FlowManager, marker +): + """Test handling of advanced options in options flow.""" + manager.hass = hass + + OPTIONS_SCHEMA = vol.Schema( + { + marker("option1"): str, + marker("advanced_no_default", description={"advanced": True}): str, + marker( + "advanced_default", + default="a very reasonable default", + description={"advanced": True}, + ): str, + } + ) + + OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA) + } + + class TestFlow(SchemaConfigFlowHandler, domain="test"): + config_flow = {} + options_flow = OPTIONS_FLOW + + config_entry = MockConfigEntry( + data={}, + domain="test", + options={ + "option1": "blabla", + "advanced_no_default": "abc123", + "advanced_default": "not default", + }, + ) + config_entry.add_to_hass(hass) + + # Start flow in basic mode + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert list(result["data_schema"].schema.keys()) == ["option1"] + + result = await hass.config_entries.options.async_configure( + result["flow_id"], {"option1": "blublu"} + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"] == { + "advanced_default": "not default", + "advanced_no_default": "abc123", + "option1": "blublu", + } + for option in result["data"]: + # Make sure we didn't get the Optional or Required instance as key + assert isinstance(option, str) + + # Start flow in advanced mode + result = await hass.config_entries.options.async_init( + config_entry.entry_id, context={"show_advanced_options": True} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert list(result["data_schema"].schema.keys()) == [ + "option1", + "advanced_no_default", + "advanced_default", + ] + + result = await hass.config_entries.options.async_configure( + result["flow_id"], {"advanced_no_default": "def456", "option1": "blabla"} + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"] == { + "advanced_default": "a very reasonable default", + "advanced_no_default": "def456", + "option1": "blabla", + } + for option in result["data"]: + # Make sure we didn't get the Optional or Required instance as key + assert isinstance(option, str) + + # Start flow in advanced mode + result = await hass.config_entries.options.async_init( + config_entry.entry_id, context={"show_advanced_options": True} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert list(result["data_schema"].schema.keys()) == [ + "option1", + "advanced_no_default", + "advanced_default", + ] + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + { + "advanced_default": "also not default", + "advanced_no_default": "abc123", + "option1": "blabla", + }, + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"] == { + "advanced_default": "also not default", + "advanced_no_default": "abc123", + "option1": "blabla", + } + for option in result["data"]: + # Make sure we didn't get the Optional or Required instance as key + assert isinstance(option, str) + + async def test_menu_step(hass: HomeAssistant) -> None: """Test menu step.""" From 13458dc72218b849920b20d3b45a08816de3243f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 25 Nov 2022 15:56:58 +0100 Subject: [PATCH 0722/1033] Centralize exception handling in Plugwise (#82694) --- .../components/plugwise/coordinator.py | 43 ++++++++++--- homeassistant/components/plugwise/gateway.py | 61 ++----------------- tests/components/plugwise/conftest.py | 16 ++--- tests/components/plugwise/test_init.py | 29 +-------- 4 files changed, 48 insertions(+), 101 deletions(-) diff --git a/homeassistant/components/plugwise/coordinator.py b/homeassistant/components/plugwise/coordinator.py index 5e5cab7d816..30adca12819 100644 --- a/homeassistant/components/plugwise/coordinator.py +++ b/homeassistant/components/plugwise/coordinator.py @@ -12,11 +12,15 @@ from plugwise.exceptions import ( UnsupportedDeviceError, ) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryError +from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER +from .const import DEFAULT_PORT, DEFAULT_SCAN_INTERVAL, DEFAULT_USERNAME, DOMAIN, LOGGER class PlugwiseData(NamedTuple): @@ -29,15 +33,15 @@ class PlugwiseData(NamedTuple): class PlugwiseDataUpdateCoordinator(DataUpdateCoordinator[PlugwiseData]): """Class to manage fetching Plugwise data from single endpoint.""" - def __init__(self, hass: HomeAssistant, api: Smile) -> None: + _connected: bool = False + + def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: """Initialize the coordinator.""" super().__init__( hass, LOGGER, - name=api.smile_name or DOMAIN, - update_interval=DEFAULT_SCAN_INTERVAL.get( - str(api.smile_type), timedelta(seconds=60) - ), + name=DOMAIN, + update_interval=timedelta(seconds=60), # Don't refresh immediately, give the device time to process # the change in state before we query it. request_refresh_debouncer=Debouncer( @@ -47,22 +51,41 @@ class PlugwiseDataUpdateCoordinator(DataUpdateCoordinator[PlugwiseData]): immediate=False, ), ) - self.api = api + + self.api = Smile( + host=entry.data[CONF_HOST], + username=entry.data.get(CONF_USERNAME, DEFAULT_USERNAME), + password=entry.data[CONF_PASSWORD], + port=entry.data.get(CONF_PORT, DEFAULT_PORT), + timeout=30, + websession=async_get_clientsession(hass, verify_ssl=False), + ) + + async def _connect(self) -> None: + """Connect to the Plugwise Smile.""" + self._connected = await self.api.connect() + self.api.get_all_devices() + self.name = self.api.smile_name + self.update_interval = DEFAULT_SCAN_INTERVAL.get( + str(self.api.smile_type), timedelta(seconds=60) + ) async def _async_update_data(self) -> PlugwiseData: """Fetch data from Plugwise.""" try: + if not self._connected: + await self._connect() data = await self.api.async_update() except InvalidAuthentication as err: - raise UpdateFailed("Authentication failed") from err + raise ConfigEntryError("Invalid username or Smile ID") from err except (InvalidXMLError, ResponseError) as err: raise UpdateFailed( "Invalid XML data, or error indication received for the Plugwise Adam/Smile/Stretch" ) from err except UnsupportedDeviceError as err: - raise UpdateFailed("Device with unsupported firmware") from err + raise ConfigEntryError("Device with unsupported firmware") from err except ConnectionFailedError as err: - raise UpdateFailed("Failed to connect") from err + raise UpdateFailed("Failed to connect to the Plugwise Smile") from err return PlugwiseData( gateway=cast(GatewayData, data[0]), devices=cast(dict[str, DeviceData], data[1]), diff --git a/homeassistant/components/plugwise/gateway.py b/homeassistant/components/plugwise/gateway.py index 81cb81e9bbf..57db57e0e8f 100644 --- a/homeassistant/components/plugwise/gateway.py +++ b/homeassistant/components/plugwise/gateway.py @@ -3,30 +3,11 @@ from __future__ import annotations from typing import Any -from plugwise.exceptions import ( - ConnectionFailedError, - InvalidAuthentication, - InvalidXMLError, - ResponseError, - UnsupportedDeviceError, -) -from plugwise.smile import Smile - from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME from homeassistant.core import HomeAssistant, callback -from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError from homeassistant.helpers import device_registry as dr, entity_registry as er -from homeassistant.helpers.aiohttp_client import async_get_clientsession -from .const import ( - DEFAULT_PORT, - DEFAULT_USERNAME, - DOMAIN, - LOGGER, - PLATFORMS_GATEWAY, - Platform, -) +from .const import DOMAIN, LOGGER, PLATFORMS_GATEWAY, Platform from .coordinator import PlugwiseDataUpdateCoordinator @@ -34,37 +15,7 @@ async def async_setup_entry_gw(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Plugwise Smiles from a config entry.""" await er.async_migrate_entries(hass, entry.entry_id, async_migrate_entity_entry) - websession = async_get_clientsession(hass, verify_ssl=False) - api = Smile( - host=entry.data[CONF_HOST], - username=entry.data.get(CONF_USERNAME, DEFAULT_USERNAME), - password=entry.data[CONF_PASSWORD], - port=entry.data.get(CONF_PORT, DEFAULT_PORT), - timeout=30, - websession=websession, - ) - - try: - connected = await api.connect() - except ConnectionFailedError as err: - raise ConfigEntryNotReady("Failed to connect to the Plugwise Smile") from err - except InvalidAuthentication as err: - raise HomeAssistantError("Invalid username or Smile ID") from err - except (InvalidXMLError, ResponseError) as err: - raise ConfigEntryNotReady( - "Error while communicating to the Plugwise Smile" - ) from err - except UnsupportedDeviceError as err: - raise HomeAssistantError("Device with unsupported firmware") from err - - if not connected: - raise ConfigEntryNotReady("Unable to connect to Smile") - api.get_all_devices() - - if entry.unique_id is None and api.smile_version[0] != "1.8.0": - hass.config_entries.async_update_entry(entry, unique_id=api.smile_hostname) - - coordinator = PlugwiseDataUpdateCoordinator(hass, api) + coordinator = PlugwiseDataUpdateCoordinator(hass, entry) await coordinator.async_config_entry_first_refresh() migrate_sensor_entities(hass, coordinator) @@ -73,11 +24,11 @@ async def async_setup_entry_gw(hass: HomeAssistant, entry: ConfigEntry) -> bool: device_registry = dr.async_get(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, - identifiers={(DOMAIN, str(api.gateway_id))}, + identifiers={(DOMAIN, str(coordinator.api.gateway_id))}, manufacturer="Plugwise", - model=api.smile_model, - name=api.smile_name, - sw_version=api.smile_version[0], + model=coordinator.api.smile_model, + name=coordinator.api.smile_name, + sw_version=coordinator.api.smile_version[0], ) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS_GATEWAY) diff --git a/tests/components/plugwise/conftest.py b/tests/components/plugwise/conftest.py index aa34fc8bed6..0c70a2bed6d 100644 --- a/tests/components/plugwise/conftest.py +++ b/tests/components/plugwise/conftest.py @@ -75,7 +75,7 @@ def mock_smile_adam() -> Generator[None, MagicMock, None]: chosen_env = "adam_multiple_devices_per_zone" with patch( - "homeassistant.components.plugwise.gateway.Smile", autospec=True + "homeassistant.components.plugwise.coordinator.Smile", autospec=True ) as smile_mock: smile = smile_mock.return_value @@ -101,7 +101,7 @@ def mock_smile_adam_2() -> Generator[None, MagicMock, None]: chosen_env = "m_adam_heating" with patch( - "homeassistant.components.plugwise.gateway.Smile", autospec=True + "homeassistant.components.plugwise.coordinator.Smile", autospec=True ) as smile_mock: smile = smile_mock.return_value @@ -127,7 +127,7 @@ def mock_smile_adam_3() -> Generator[None, MagicMock, None]: chosen_env = "m_adam_cooling" with patch( - "homeassistant.components.plugwise.gateway.Smile", autospec=True + "homeassistant.components.plugwise.coordinator.Smile", autospec=True ) as smile_mock: smile = smile_mock.return_value @@ -152,7 +152,7 @@ def mock_smile_anna() -> Generator[None, MagicMock, None]: """Create a Mock Anna environment for testing exceptions.""" chosen_env = "anna_heatpump_heating" with patch( - "homeassistant.components.plugwise.gateway.Smile", autospec=True + "homeassistant.components.plugwise.coordinator.Smile", autospec=True ) as smile_mock: smile = smile_mock.return_value @@ -177,7 +177,7 @@ def mock_smile_anna_2() -> Generator[None, MagicMock, None]: """Create a 2nd Mock Anna environment for testing exceptions.""" chosen_env = "m_anna_heatpump_cooling" with patch( - "homeassistant.components.plugwise.gateway.Smile", autospec=True + "homeassistant.components.plugwise.coordinator.Smile", autospec=True ) as smile_mock: smile = smile_mock.return_value @@ -202,7 +202,7 @@ def mock_smile_anna_3() -> Generator[None, MagicMock, None]: """Create a 3rd Mock Anna environment for testing exceptions.""" chosen_env = "m_anna_heatpump_idle" with patch( - "homeassistant.components.plugwise.gateway.Smile", autospec=True + "homeassistant.components.plugwise.coordinator.Smile", autospec=True ) as smile_mock: smile = smile_mock.return_value @@ -227,7 +227,7 @@ def mock_smile_p1() -> Generator[None, MagicMock, None]: """Create a Mock P1 DSMR environment for testing exceptions.""" chosen_env = "p1v3_full_option" with patch( - "homeassistant.components.plugwise.gateway.Smile", autospec=True + "homeassistant.components.plugwise.coordinator.Smile", autospec=True ) as smile_mock: smile = smile_mock.return_value @@ -252,7 +252,7 @@ def mock_stretch() -> Generator[None, MagicMock, None]: """Create a Mock Stretch environment for testing exceptions.""" chosen_env = "stretch_v31" with patch( - "homeassistant.components.plugwise.gateway.Smile", autospec=True + "homeassistant.components.plugwise.coordinator.Smile", autospec=True ) as smile_mock: smile = smile_mock.return_value diff --git a/tests/components/plugwise/test_init.py b/tests/components/plugwise/test_init.py index e37ee6b320c..cbca5d19ab5 100644 --- a/tests/components/plugwise/test_init.py +++ b/tests/components/plugwise/test_init.py @@ -59,33 +59,6 @@ async def test_gateway_config_entry_not_ready( mock_smile_anna: MagicMock, side_effect: Exception, entry_state: ConfigEntryState, -) -> None: - """Test the Plugwise configuration entry not ready.""" - mock_smile_anna.connect.side_effect = side_effect - - mock_config_entry.add_to_hass(hass) - await hass.config_entries.async_setup(mock_config_entry.entry_id) - await hass.async_block_till_done() - - assert len(mock_smile_anna.connect.mock_calls) == 1 - assert mock_config_entry.state is entry_state - - -@pytest.mark.parametrize( - "side_effect", - [ - (ConnectionFailedError), - (InvalidAuthentication), - (InvalidXMLError), - (ResponseError), - (UnsupportedDeviceError), - ], -) -async def test_coord_config_entry_not_ready( - hass: HomeAssistant, - mock_config_entry: MockConfigEntry, - mock_smile_anna: MagicMock, - side_effect: Exception, ) -> None: """Test the Plugwise configuration entry not ready.""" mock_smile_anna.async_update.side_effect = side_effect @@ -95,7 +68,7 @@ async def test_coord_config_entry_not_ready( await hass.async_block_till_done() assert len(mock_smile_anna.connect.mock_calls) == 1 - assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + assert mock_config_entry.state is entry_state @pytest.mark.parametrize( From e9ce08763c4ed05f0facfb6229907b4dcc4e1e97 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 25 Nov 2022 16:00:26 +0100 Subject: [PATCH 0723/1033] Allow SchemaFlowFormStep.next_step to return None (#82707) --- .../helpers/schema_config_entry_flow.py | 24 ++++--- .../helpers/test_schema_config_entry_flow.py | 68 +++++++++++++++++++ 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index f992b187e9d..f29fdfe56b8 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -53,12 +53,13 @@ class SchemaFlowFormStep(SchemaFlowStep): - The `validate_user_input` should raise `SchemaFlowError` is user input is invalid. """ - next_step: Callable[[dict[str, Any]], str] | str | None = None + next_step: Callable[[dict[str, Any]], str | None] | str | None = None """Optional property to identify next step. - If `next_step` is a function, it is called if the schema validates successfully or if no schema is defined. The `next_step` function is passed the union of config entry - options and user input from previous steps. + options and user input from previous steps. If the function returns None, the flow is + ended with `FlowResultType.CREATE_ENTRY`. - If `next_step` is None, the flow is ended with `FlowResultType.CREATE_ENTRY`. """ @@ -147,13 +148,17 @@ class SchemaCommonFlowHandler: def _show_next_step_or_create_entry( self, form_step: SchemaFlowFormStep ) -> FlowResult: - if form_step.next_step is None: + next_step_id_or_end_flow: str | None + + if callable(form_step.next_step): + next_step_id_or_end_flow = form_step.next_step(self._options) + else: + next_step_id_or_end_flow = form_step.next_step + + if next_step_id_or_end_flow is None: # Flow done, create entry or update config entry options return self._handler.async_create_entry(data=self._options) - - if isinstance(form_step.next_step, str): - return self._show_next_step(form_step.next_step) - return self._show_next_step(form_step.next_step(self._options)) + return self._show_next_step(next_step_id_or_end_flow) def _show_next_step( self, @@ -203,11 +208,14 @@ class SchemaCommonFlowHandler: errors = {"base": str(error)} if error else None # Show form for next step + last_step = None + if not callable(form_step.next_step): + last_step = form_step.next_step is None return self._handler.async_show_form( step_id=next_step_id, data_schema=data_schema, errors=errors, - last_step=form_step.next_step is None, + last_step=last_step, ) async def _async_menu_step( diff --git a/tests/helpers/test_schema_config_entry_flow.py b/tests/helpers/test_schema_config_entry_flow.py index de99c7b3bce..a9af60b8ec3 100644 --- a/tests/helpers/test_schema_config_entry_flow.py +++ b/tests/helpers/test_schema_config_entry_flow.py @@ -375,3 +375,71 @@ async def test_schema_none(hass: HomeAssistant) -> None: result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.CREATE_ENTRY + + +async def test_last_step(hass: HomeAssistant) -> None: + """Test SchemaFlowFormStep with schema set to None.""" + + CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { + "user": SchemaFlowFormStep(next_step="step1"), + "step1": SchemaFlowFormStep(vol.Schema({}), next_step="step2"), + "step2": SchemaFlowFormStep(vol.Schema({}), next_step=lambda _: "step3"), + "step3": SchemaFlowFormStep(vol.Schema({}), next_step=None), + } + + class TestConfigFlow(SchemaConfigFlowHandler, domain=TEST_DOMAIN): + """Handle a config or options flow for Derivative.""" + + config_flow = CONFIG_FLOW + + mock_platform(hass, f"{TEST_DOMAIN}.config_flow") + with patch.dict(config_entries.HANDLERS, {TEST_DOMAIN: TestConfigFlow}): + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": "user"} + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "step1" + assert result["last_step"] is False + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "step2" + assert result["last_step"] is None + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "step3" + assert result["last_step"] is True + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + assert result["type"] == FlowResultType.CREATE_ENTRY + + +async def test_next_step_function(hass: HomeAssistant) -> None: + """Test SchemaFlowFormStep with a next_step function.""" + + CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { + "user": SchemaFlowFormStep(next_step="step1"), + "step1": SchemaFlowFormStep(vol.Schema({}), next_step=lambda _: "step2"), + "step2": SchemaFlowFormStep(vol.Schema({}), next_step=lambda _: None), + } + + class TestConfigFlow(SchemaConfigFlowHandler, domain=TEST_DOMAIN): + """Handle a config or options flow for Derivative.""" + + config_flow = CONFIG_FLOW + + mock_platform(hass, f"{TEST_DOMAIN}.config_flow") + with patch.dict(config_entries.HANDLERS, {TEST_DOMAIN: TestConfigFlow}): + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": "user"} + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "step1" + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "step2" + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + assert result["type"] == FlowResultType.CREATE_ENTRY From e00808bea86fe9e4267e01ff56f0d395aaad21d4 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 25 Nov 2022 16:09:01 +0100 Subject: [PATCH 0724/1033] Use SchemaOptionsFlowHandler in apple_tv (#82688) --- .../components/apple_tv/config_flow.py | 46 ++++++------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/apple_tv/config_flow.py b/homeassistant/components/apple_tv/config_flow.py index b78add3260e..d000c0346af 100644 --- a/homeassistant/components/apple_tv/config_flow.py +++ b/homeassistant/components/apple_tv/config_flow.py @@ -22,6 +22,10 @@ from homeassistant.core import callback from homeassistant.data_entry_flow import AbortFlow, FlowResult from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaFlowFormStep, + SchemaOptionsFlowHandler, +) from homeassistant.util.network import is_ipv6_address from .const import CONF_CREDENTIALS, CONF_IDENTIFIERS, CONF_START_OFF, DOMAIN @@ -36,6 +40,15 @@ DEFAULT_START_OFF = False DISCOVERY_AGGREGATION_TIME = 15 # seconds +OPTIONS_SCHEMA = vol.Schema( + { + vol.Optional(CONF_START_OFF, default=DEFAULT_START_OFF): bool, + } +) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), +} + async def device_scan(hass, identifier, loop): """Scan for a specific device using identifier as filter.""" @@ -76,9 +89,9 @@ class AppleTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @callback def async_get_options_flow( config_entry: config_entries.ConfigEntry, - ) -> AppleTVOptionsFlow: + ) -> SchemaOptionsFlowHandler: """Get options flow for this handler.""" - return AppleTVOptionsFlow(config_entry) + return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW) def __init__(self): """Initialize a new AppleTVConfigFlow.""" @@ -525,35 +538,6 @@ class AppleTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_create_entry(title=self.atv.name, data=data) -class AppleTVOptionsFlow(config_entries.OptionsFlow): - """Handle Apple TV options.""" - - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: - """Initialize Apple TV options flow.""" - self.config_entry = config_entry - self.options = dict(config_entry.options) - - async def async_step_init(self, user_input=None): - """Manage the Apple TV options.""" - if user_input is not None: - self.options[CONF_START_OFF] = user_input[CONF_START_OFF] - return self.async_create_entry(title="", data=self.options) - - return self.async_show_form( - step_id="init", - data_schema=vol.Schema( - { - vol.Optional( - CONF_START_OFF, - default=self.config_entry.options.get( - CONF_START_OFF, DEFAULT_START_OFF - ), - ): bool, - } - ), - ) - - class DeviceNotFound(HomeAssistantError): """Error to indicate device could not be found.""" From 283ee94cf345b204033592c963907800477a99dc Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 25 Nov 2022 16:09:09 +0100 Subject: [PATCH 0725/1033] Use SchemaOptionsFlowHandler in aurora (#82687) --- .../components/aurora/config_flow.py | 50 +++++++------------ 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/aurora/config_flow.py b/homeassistant/components/aurora/config_flow.py index a2331c19ec6..4649a3adc08 100644 --- a/homeassistant/components/aurora/config_flow.py +++ b/homeassistant/components/aurora/config_flow.py @@ -11,11 +11,26 @@ from homeassistant import config_entries from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME from homeassistant.core import callback from homeassistant.helpers import aiohttp_client +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaFlowFormStep, + SchemaOptionsFlowHandler, +) from .const import CONF_THRESHOLD, DEFAULT_NAME, DEFAULT_THRESHOLD, DOMAIN _LOGGER = logging.getLogger(__name__) +OPTIONS_SCHEMA = vol.Schema( + { + vol.Required(CONF_THRESHOLD, default=DEFAULT_THRESHOLD): vol.All( + vol.Coerce(int), vol.Range(min=0, max=100) + ), + } +) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), +} + class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for NOAA Aurora Integration.""" @@ -26,9 +41,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @callback def async_get_options_flow( config_entry: config_entries.ConfigEntry, - ) -> OptionsFlowHandler: + ) -> SchemaOptionsFlowHandler: """Get the options flow for this handler.""" - return OptionsFlowHandler(config_entry) + return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW) async def async_step_user(self, user_input=None): """Handle the initial step.""" @@ -81,34 +96,3 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ), errors=errors, ) - - -class OptionsFlowHandler(config_entries.OptionsFlow): - """Handle options flow changes.""" - - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: - """Initialize options flow.""" - self.config_entry = config_entry - - async def async_step_init(self, user_input=None): - """Manage options.""" - - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - return self.async_show_form( - step_id="init", - data_schema=vol.Schema( - { - vol.Required( - CONF_THRESHOLD, - default=self.config_entry.options.get( - CONF_THRESHOLD, DEFAULT_THRESHOLD - ), - ): vol.All( - vol.Coerce(int), - vol.Range(min=0, max=100), - ), - } - ), - ) From 34757fa539dde3897333fb8c40fcdebc33324026 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 25 Nov 2022 16:09:18 +0100 Subject: [PATCH 0726/1033] Use SchemaOptionsFlowHandler in airvisual (#82686) --- .../components/airvisual/config_flow.py | 44 ++++++------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/airvisual/config_flow.py b/homeassistant/components/airvisual/config_flow.py index 9510c938cb0..8ba75c43bdb 100644 --- a/homeassistant/components/airvisual/config_flow.py +++ b/homeassistant/components/airvisual/config_flow.py @@ -17,7 +17,7 @@ from pyairvisual.node import NodeProError import voluptuous as vol from homeassistant import config_entries -from homeassistant.config_entries import ConfigEntry, OptionsFlow +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_API_KEY, CONF_IP_ADDRESS, @@ -30,6 +30,10 @@ from homeassistant.const import ( from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import aiohttp_client, config_validation as cv +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaFlowFormStep, + SchemaOptionsFlowHandler, +) from . import async_get_geography_id from .const import ( @@ -66,6 +70,13 @@ PICK_INTEGRATION_TYPE_SCHEMA = vol.Schema( } ) +OPTIONS_SCHEMA = vol.Schema( + {vol.Required(CONF_SHOW_ON_MAP): bool}, +) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), +} + class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Handle an AirVisual config flow.""" @@ -165,9 +176,9 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): @staticmethod @callback - def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow: + def async_get_options_flow(config_entry: ConfigEntry) -> SchemaOptionsFlowHandler: """Define the config flow to handle options.""" - return AirVisualOptionsFlowHandler(config_entry) + return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW) async def async_step_geography_by_coords( self, user_input: dict[str, str] | None = None @@ -258,30 +269,3 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if user_input["type"] == INTEGRATION_TYPE_GEOGRAPHY_NAME: return await self.async_step_geography_by_name() return await self.async_step_node_pro() - - -class AirVisualOptionsFlowHandler(config_entries.OptionsFlow): - """Handle an AirVisual options flow.""" - - def __init__(self, entry: ConfigEntry) -> None: - """Initialize.""" - self.entry = entry - - async def async_step_init( - self, user_input: dict[str, str] | None = None - ) -> FlowResult: - """Manage the options.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - return self.async_show_form( - step_id="init", - data_schema=vol.Schema( - { - vol.Required( - CONF_SHOW_ON_MAP, - default=self.entry.options.get(CONF_SHOW_ON_MAP), - ): bool - } - ), - ) From 4335b0db25f21b2d4b6008eeb3e9bb4bc34b669e Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 25 Nov 2022 16:11:22 +0100 Subject: [PATCH 0727/1033] Clean up or correct from removed deprecation for MQTT platform (#82684) Clean up or correct from removed deprecation --- homeassistant/components/mqtt/alarm_control_panel.py | 2 +- homeassistant/components/mqtt/binary_sensor.py | 2 +- homeassistant/components/mqtt/button.py | 2 +- homeassistant/components/mqtt/camera.py | 2 +- homeassistant/components/mqtt/climate.py | 2 +- homeassistant/components/mqtt/cover.py | 2 +- homeassistant/components/mqtt/device_tracker.py | 2 +- homeassistant/components/mqtt/fan.py | 3 +-- homeassistant/components/mqtt/humidifier.py | 2 +- homeassistant/components/mqtt/light/__init__.py | 2 +- homeassistant/components/mqtt/light/schema_basic.py | 2 +- homeassistant/components/mqtt/light/schema_json.py | 2 +- homeassistant/components/mqtt/light/schema_template.py | 2 +- homeassistant/components/mqtt/lock.py | 2 +- homeassistant/components/mqtt/number.py | 2 +- homeassistant/components/mqtt/scene.py | 2 +- homeassistant/components/mqtt/select.py | 2 +- homeassistant/components/mqtt/sensor.py | 2 +- homeassistant/components/mqtt/siren.py | 2 +- homeassistant/components/mqtt/switch.py | 2 +- homeassistant/components/mqtt/vacuum/__init__.py | 4 ++-- homeassistant/components/mqtt/vacuum/schema_legacy.py | 2 +- homeassistant/components/mqtt/vacuum/schema_state.py | 2 +- tests/components/mqtt/test_climate.py | 1 - tests/components/mqtt/test_discovery.py | 1 - tests/components/mqtt/test_init.py | 6 ++---- tests/components/mqtt/test_light.py | 9 +++++---- 27 files changed, 31 insertions(+), 35 deletions(-) diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index a0d065cc7fe..aa796d9ea8f 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -112,7 +112,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -# Configuring MQTT alarm control panels under the alarm_control_panel platform key is deprecated in HA Core 2022.6 +# Configuring MQTT alarm control panels under the alarm_control_panel platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(alarm.DOMAIN), diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 12b402065ce..bbbcb97ada6 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -69,7 +69,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_RO_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -# Configuring MQTT Binary sensors under the binary_sensor platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Binary sensors under the binary_sensor platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(binary_sensor.DOMAIN), diff --git a/homeassistant/components/mqtt/button.py b/homeassistant/components/mqtt/button.py index 71f59b8cda7..929e270f300 100644 --- a/homeassistant/components/mqtt/button.py +++ b/homeassistant/components/mqtt/button.py @@ -46,7 +46,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -# Configuring MQTT Buttons under the button platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Buttons under the button platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(button.DOMAIN), diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index 26688756aa4..6ece232775a 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -56,7 +56,7 @@ PLATFORM_SCHEMA_MODERN = vol.All( PLATFORM_SCHEMA_BASE.schema, ) -# Configuring MQTT Camera under the camera platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Camera under the camera platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(camera.DOMAIN), diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 0d3313a4475..7d6d2ff7b2c 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -307,7 +307,7 @@ PLATFORM_SCHEMA_MODERN = vol.All( valid_preset_mode_configuration, ) -# Configuring MQTT Climate under the climate platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Climate under the climate platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(climate.DOMAIN), diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 4d230c20ae2..7f9b5dc3e85 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -204,7 +204,7 @@ PLATFORM_SCHEMA_MODERN = vol.All( validate_options, ) -# Configuring MQTT Covers under the cover platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Covers under the cover platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(cover.DOMAIN), diff --git a/homeassistant/components/mqtt/device_tracker.py b/homeassistant/components/mqtt/device_tracker.py index 41902349de4..26dc016e07e 100644 --- a/homeassistant/components/mqtt/device_tracker.py +++ b/homeassistant/components/mqtt/device_tracker.py @@ -59,7 +59,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_RO_SCHEMA.extend( DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) -# Configuring MQTT Device Trackers under the device_tracker platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Device Trackers under the device_tracker platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All(warn_for_legacy_schema(device_tracker.DOMAIN)) diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 9da7721a7dc..2623845fffd 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -183,7 +183,7 @@ _PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -# Configuring MQTT Fans under the fan platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Fans under the fan platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(fan.DOMAIN), @@ -518,7 +518,6 @@ class MqttFan(MqttEntity, FanEntity): # The default for FanEntity is to compute it based on percentage return self._attr_is_on - # The speed attribute deprecated in the schema, support will be removed after a quarter (2021.7) async def async_turn_on( self, percentage: int | None = None, diff --git a/homeassistant/components/mqtt/humidifier.py b/homeassistant/components/mqtt/humidifier.py index d386aade32a..38d3e46ea45 100644 --- a/homeassistant/components/mqtt/humidifier.py +++ b/homeassistant/components/mqtt/humidifier.py @@ -149,7 +149,7 @@ _PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -# Configuring MQTT Humidifiers under the humidifier platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Humidifiers under the humidifier platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(humidifier.DOMAIN), diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index 5860a011ab5..e7b2dcf5ae4 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -72,7 +72,7 @@ DISCOVERY_SCHEMA = vol.All( validate_mqtt_light_discovery, ) -# Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Lights under the light platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(light.DOMAIN), diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index 689d388b92a..d2f8a5ac03e 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -224,7 +224,7 @@ _PLATFORM_SCHEMA_BASE = ( .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) -# The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 +# The use of PLATFORM_SCHEMA was deprecated in HA Core 2022.6 PLATFORM_SCHEMA_BASIC = vol.All( cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), ) diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index 9547b6d31a8..09413e1f0ac 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -158,7 +158,7 @@ _PLATFORM_SCHEMA_BASE = ( .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) -# Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Lights under the light platform key was deprecated in HA Core 2022.6 PLATFORM_SCHEMA_JSON = vol.All( cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), valid_color_configuration, diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 7e3c8a0a751..21691acc916 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -112,7 +112,7 @@ _PLATFORM_SCHEMA_BASE = ( .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) -# Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Lights under the light platform key was deprecated in HA Core 2022.6 PLATFORM_SCHEMA_TEMPLATE = vol.All( cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), ) diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index 525d85b5a6c..b956f2e1b88 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -70,7 +70,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -# Configuring MQTT Locks under the lock platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Locks under the lock platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(lock.DOMAIN), diff --git a/homeassistant/components/mqtt/number.py b/homeassistant/components/mqtt/number.py index b81ce113a88..6d56354368e 100644 --- a/homeassistant/components/mqtt/number.py +++ b/homeassistant/components/mqtt/number.py @@ -107,7 +107,7 @@ PLATFORM_SCHEMA_MODERN = vol.All( validate_config, ) -# Configuring MQTT Number under the number platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Number under the number platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(number.DOMAIN), diff --git a/homeassistant/components/mqtt/scene.py b/homeassistant/components/mqtt/scene.py index f671daea2b2..3454102e5e0 100644 --- a/homeassistant/components/mqtt/scene.py +++ b/homeassistant/components/mqtt/scene.py @@ -45,7 +45,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend( } ).extend(MQTT_AVAILABILITY_SCHEMA.schema) -# Configuring MQTT Scenes under the scene platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Scenes under the scene platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(scene.DOMAIN), diff --git a/homeassistant/components/mqtt/select.py b/homeassistant/components/mqtt/select.py index c7e00420cbc..d574cf081ba 100644 --- a/homeassistant/components/mqtt/select.py +++ b/homeassistant/components/mqtt/select.py @@ -67,7 +67,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( }, ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -# Configuring MQTT Select under the select platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Select under the select platform key was deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(select.DOMAIN), ) diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index fd42d5539ec..092ab88b28d 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -114,7 +114,7 @@ PLATFORM_SCHEMA_MODERN = vol.All( validate_options, ) -# Configuring MQTT Sensors under the sensor platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Sensors under the sensor platform key was deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(sensor.DOMAIN), ) diff --git a/homeassistant/components/mqtt/siren.py b/homeassistant/components/mqtt/siren.py index 0e6c3c3dec1..350f02427e5 100644 --- a/homeassistant/components/mqtt/siren.py +++ b/homeassistant/components/mqtt/siren.py @@ -97,7 +97,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( }, ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -# Configuring MQTT Sirens under the siren platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Sirens under the siren platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(siren.DOMAIN), diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index ffcb829e3a7..09e72955e63 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -66,7 +66,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -# Configuring MQTT Switches under the switch platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Switches under the switch platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(switch.DOMAIN), diff --git a/homeassistant/components/mqtt/vacuum/__init__.py b/homeassistant/components/mqtt/vacuum/__init__.py index 87b76930312..60f8d7a7d45 100644 --- a/homeassistant/components/mqtt/vacuum/__init__.py +++ b/homeassistant/components/mqtt/vacuum/__init__.py @@ -34,7 +34,7 @@ def validate_mqtt_vacuum_discovery(config_value: ConfigType) -> ConfigType: return config -# Configuring MQTT Vacuums under the vacuum platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Vacuums under the vacuum platform key was deprecated in HA Core 2022.6 def validate_mqtt_vacuum(config_value: ConfigType) -> ConfigType: """Validate MQTT vacuum schema (deprecated).""" schemas = {LEGACY: PLATFORM_SCHEMA_LEGACY, STATE: PLATFORM_SCHEMA_STATE} @@ -56,7 +56,7 @@ DISCOVERY_SCHEMA = vol.All( MQTT_VACUUM_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_vacuum_discovery ) -# Configuring MQTT Vacuums under the vacuum platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Vacuums under the vacuum platform key was deprecated in HA Core 2022.6 # Setup for the legacy YAML format was removed in HA Core 2022.12 PLATFORM_SCHEMA = vol.All( warn_for_legacy_schema(vacuum.DOMAIN), diff --git a/homeassistant/components/mqtt/vacuum/schema_legacy.py b/homeassistant/components/mqtt/vacuum/schema_legacy.py index 68618aba20c..c8f8afd70df 100644 --- a/homeassistant/components/mqtt/vacuum/schema_legacy.py +++ b/homeassistant/components/mqtt/vacuum/schema_legacy.py @@ -160,7 +160,7 @@ PLATFORM_SCHEMA_LEGACY_MODERN = ( .extend(MQTT_VACUUM_SCHEMA.schema) ) -# Configuring MQTT Vacuums under the vacuum platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Vacuums under the vacuum platform key was deprecated in HA Core 2022.6 PLATFORM_SCHEMA_LEGACY = vol.All( cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_LEGACY_MODERN.schema), warn_for_legacy_schema(VACUUM_DOMAIN), diff --git a/homeassistant/components/mqtt/vacuum/schema_state.py b/homeassistant/components/mqtt/vacuum/schema_state.py index a854d3b0620..4e020c630a5 100644 --- a/homeassistant/components/mqtt/vacuum/schema_state.py +++ b/homeassistant/components/mqtt/vacuum/schema_state.py @@ -154,7 +154,7 @@ PLATFORM_SCHEMA_STATE_MODERN = ( .extend(MQTT_VACUUM_SCHEMA.schema) ) -# Configuring MQTT Vacuums under the vacuum platform key is deprecated in HA Core 2022.6 +# Configuring MQTT Vacuums under the vacuum platform key was deprecated in HA Core 2022.6 PLATFORM_SCHEMA_STATE = vol.All( cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_STATE_MODERN.schema), warn_for_legacy_schema(VACUUM_DOMAIN), diff --git a/tests/components/mqtt/test_climate.py b/tests/components/mqtt/test_climate.py index eb4c71dd254..dd86c41dcc7 100644 --- a/tests/components/mqtt/test_climate.py +++ b/tests/components/mqtt/test_climate.py @@ -120,7 +120,6 @@ async def test_preset_none_in_preset_modes(hass, caplog): assert "Invalid config for [mqtt]: not a valid value" in caplog.text -# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 @pytest.mark.parametrize( "parameter,config_value", [ diff --git a/tests/components/mqtt/test_discovery.py b/tests/components/mqtt/test_discovery.py index 48c1d88a9a8..89a56903c3b 100644 --- a/tests/components/mqtt/test_discovery.py +++ b/tests/components/mqtt/test_discovery.py @@ -1238,7 +1238,6 @@ async def test_no_implicit_state_topic_switch( async_fire_mqtt_message(hass, "homeassistant/switch/bla/config", data) await hass.async_block_till_done() - assert "implicit state_topic is deprecated" not in caplog.text state = hass.states.get("switch.Test1") assert state is not None diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 12b36a2fae3..ced93a8c997 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -2720,8 +2720,8 @@ async def test_subscribe_connection_status( assert mqtt_connected_calls[1] is False -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 +# Test existence of removed YAML configuration under the platform key +# This warning and test is to be removed from HA core 2023.6 async def test_one_deprecation_warning_per_platform( hass, mqtt_mock_entry_with_yaml_config, caplog ): @@ -2810,8 +2810,6 @@ async def test_publish_or_subscribe_without_valid_config_entry(hass, caplog): @patch("homeassistant.components.mqtt.PLATFORMS", [Platform.LIGHT]) async def test_reload_entry_with_new_config(hass, tmp_path): """Test reloading the config entry with a new yaml config.""" - # Test deprecated YAML configuration under the platform key - # Scheduled to be removed in HA core 2022.12 config_old = { "mqtt": {"light": [{"name": "test_old1", "command_topic": "test-topic_old"}]} } diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index 9e64d1c45a7..2404d8a0f1f 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -2216,11 +2216,12 @@ async def test_discovery_removal_light(hass, mqtt_mock_entry_no_yaml_config, cap ) -# Test deprecated YAML configuration under the platform key -# Scheduled to be removed in HA core 2022.12 -async def test_discovery_deprecated(hass, mqtt_mock_entry_no_yaml_config, caplog): - """Test discovery of mqtt light with deprecated platform option.""" +async def test_discovery_ignores_extra_keys( + hass, mqtt_mock_entry_no_yaml_config, caplog +): + """Test discovery ignores extra keys that are not blocked.""" await mqtt_mock_entry_no_yaml_config() + # inserted `platform` key should be ignored data = ( '{ "name": "Beer",' ' "platform": "mqtt",' ' "command_topic": "test_topic"}' ) From 424d38ecc74d868c0a435688b1c16d8821e361ba Mon Sep 17 00:00:00 2001 From: On Freund Date: Fri, 25 Nov 2022 17:12:31 +0200 Subject: [PATCH 0728/1033] Add exception trace in Risco config flow (#82683) --- homeassistant/components/risco/config_flow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/risco/config_flow.py b/homeassistant/components/risco/config_flow.py index 91e12a2548a..e0d51a041dd 100644 --- a/homeassistant/components/risco/config_flow.py +++ b/homeassistant/components/risco/config_flow.py @@ -158,6 +158,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): try: info = await validate_local_input(self.hass, user_input) except CannotConnectError: + _LOGGER.exception("Cannot connect") errors["base"] = "cannot_connect" except UnauthorizedError: errors["base"] = "invalid_auth" From 5381f9f92fe42e7d7c085ad5fd1c3aa59e1c549b Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Fri, 25 Nov 2022 23:16:16 +0800 Subject: [PATCH 0729/1033] Fix owntone websocket reconnection (#82680) --- homeassistant/components/forked_daapd/media_player.py | 3 ++- tests/components/forked_daapd/test_media_player.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/forked_daapd/media_player.py b/homeassistant/components/forked_daapd/media_player.py index 1562a68b696..c60a90176a9 100644 --- a/homeassistant/components/forked_daapd/media_player.py +++ b/homeassistant/components/forked_daapd/media_player.py @@ -926,7 +926,8 @@ class ForkedDaapdUpdater: else: _LOGGER.error("Invalid websocket port") - def _disconnected_callback(self): + async def _disconnected_callback(self): + """Send update signals when the websocket gets disconnected.""" async_dispatcher_send( self.hass, SIGNAL_UPDATE_MASTER.format(self._entry_id), False ) diff --git a/tests/components/forked_daapd/test_media_player.py b/tests/components/forked_daapd/test_media_player.py index ae5e29bee47..9973bcd0d72 100644 --- a/tests/components/forked_daapd/test_media_player.py +++ b/tests/components/forked_daapd/test_media_player.py @@ -785,7 +785,7 @@ async def test_websocket_disconnect(hass, mock_api_object): assert hass.states.get(TEST_MASTER_ENTITY_NAME).state != STATE_UNAVAILABLE assert hass.states.get(TEST_ZONE_ENTITY_NAMES[0]).state != STATE_UNAVAILABLE updater_disconnected = mock_api_object.start_websocket_handler.call_args[0][4] - updater_disconnected() + await updater_disconnected() await hass.async_block_till_done() assert hass.states.get(TEST_MASTER_ENTITY_NAME).state == STATE_UNAVAILABLE assert hass.states.get(TEST_ZONE_ENTITY_NAMES[0]).state == STATE_UNAVAILABLE From 20474e500c2c75ca18889dd5cdfa9d6cedca5657 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 25 Nov 2022 16:16:43 +0100 Subject: [PATCH 0730/1033] Use SchemaOptionsFlowHandler in aemet (#82638) * Use SchemaOptionsFlowHandler in aemet * Update homeassistant/components/aemet/config_flow.py * Update config_flow.py * black --- homeassistant/components/aemet/config_flow.py | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/aemet/config_flow.py b/homeassistant/components/aemet/config_flow.py index 1188df5b94f..9db0c6f7db1 100644 --- a/homeassistant/components/aemet/config_flow.py +++ b/homeassistant/components/aemet/config_flow.py @@ -8,9 +8,22 @@ from homeassistant import config_entries from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME from homeassistant.core import callback import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaFlowFormStep, + SchemaOptionsFlowHandler, +) from .const import CONF_STATION_UPDATES, DEFAULT_NAME, DOMAIN +OPTIONS_SCHEMA = vol.Schema( + { + vol.Required(CONF_STATION_UPDATES): bool, + } +) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), +} + class AemetConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Config flow for AEMET OpenData.""" @@ -54,32 +67,9 @@ class AemetConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @callback def async_get_options_flow( config_entry: config_entries.ConfigEntry, - ) -> OptionsFlowHandler: + ) -> SchemaOptionsFlowHandler: """Get the options flow for this handler.""" - return OptionsFlowHandler(config_entry) - - -class OptionsFlowHandler(config_entries.OptionsFlow): - """Handle a option flow for AEMET.""" - - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: - """Initialize options flow.""" - self.config_entry = config_entry - - async def async_step_init(self, user_input=None): - """Handle options flow.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - data_schema = vol.Schema( - { - vol.Required( - CONF_STATION_UPDATES, - default=self.config_entry.options.get(CONF_STATION_UPDATES), - ): bool, - } - ) - return self.async_show_form(step_id="init", data_schema=data_schema) + return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW) async def _is_aemet_api_online(hass, api_key): From 0a4549e202ee280d9a336956c006971f758e6495 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 25 Nov 2022 16:38:58 +0100 Subject: [PATCH 0731/1033] Add callback to SchemaFlowFormStep for suggested_values (#82706) * Allow callback for suggested_values * docstring --- .../helpers/schema_config_entry_flow.py | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index f29fdfe56b8..86cd578039a 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -63,6 +63,16 @@ class SchemaFlowFormStep(SchemaFlowStep): - If `next_step` is None, the flow is ended with `FlowResultType.CREATE_ENTRY`. """ + suggested_values: Callable[[SchemaCommonFlowHandler], dict[str, Any]] | None = None + """Optional property to populate suggested values. + + - If `suggested_values` is None, each key in the schema will get a suggested value + from an option with the same key. + + Note: if a step is retried due to a validation failure, then the user input will have + priority over the suggested values. + """ + @dataclass class SchemaFlowMenuStep(SchemaFlowStep): @@ -167,10 +177,6 @@ class SchemaCommonFlowHandler: user_input: dict[str, Any] | None = None, ) -> FlowResult: """Show form for next step.""" - suggested_values = dict(self._options) - if user_input: - suggested_values.update(user_input) - if isinstance(self._flow[next_step_id], SchemaFlowMenuStep): menu_step = cast(SchemaFlowMenuStep, self._flow[next_step_id]) return self._handler.async_show_menu( @@ -183,6 +189,15 @@ class SchemaCommonFlowHandler: if (data_schema := self._get_schema(form_step)) is None: return self._show_next_step_or_create_entry(form_step) + if form_step.suggested_values: + suggested_values = form_step.suggested_values(self) + else: + suggested_values = self._options + if user_input: + # We don't want to mutate the existing options + suggested_values = copy.deepcopy(suggested_values) + suggested_values.update(user_input) + if data_schema.schema: # Make a copy of the schema with suggested values set to saved options schema = {} From 3c3f48fe28b840fae40efb83eabbcaec54cdb98c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 25 Nov 2022 17:26:50 +0100 Subject: [PATCH 0732/1033] Use SchemaOptionsFlowHandler in azure-event-hub (#82619) * Use SchemaOptionsFlowHandler in azure-event-hub * Update config_flow.py * black --- .../components/azure_event_hub/config_flow.py | 46 ++++++------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/azure_event_hub/config_flow.py b/homeassistant/components/azure_event_hub/config_flow.py index 26980231dc1..c789b85aebb 100644 --- a/homeassistant/components/azure_event_hub/config_flow.py +++ b/homeassistant/components/azure_event_hub/config_flow.py @@ -11,6 +11,10 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaFlowFormStep, + SchemaOptionsFlowHandler, +) from .client import AzureEventHubClient from .const import ( @@ -52,6 +56,15 @@ SAS_SCHEMA = vol.Schema( } ) +OPTIONS_SCHEMA = vol.Schema( + { + vol.Required(CONF_SEND_INTERVAL): int, + } +) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), +} + async def validate_data(data: dict[str, Any]) -> dict[str, str] | None: """Validate the input.""" @@ -81,9 +94,9 @@ class AEHConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @callback def async_get_options_flow( config_entry: config_entries.ConfigEntry, - ) -> AEHOptionsFlowHandler: + ) -> SchemaOptionsFlowHandler: """Get the options flow for this handler.""" - return AEHOptionsFlowHandler(config_entry) + return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW) async def async_step_user( self, user_input: dict[str, Any] | None = None @@ -167,32 +180,3 @@ class AEHConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return None self._data.update(user_input) return await validate_data(self._data) - - -class AEHOptionsFlowHandler(config_entries.OptionsFlow): - """Handle azure event hub options.""" - - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: - """Initialize AEH options flow.""" - self.config_entry = config_entry - self.options = deepcopy(dict(config_entry.options)) - - async def async_step_init( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Manage the AEH options.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - return self.async_show_form( - step_id="init", - data_schema=vol.Schema( - { - vol.Required( - CONF_SEND_INTERVAL, - default=self.options.get(CONF_SEND_INTERVAL), - ): int - } - ), - last_step=True, - ) From 35dbc3151a83c832c616089cd282639182afa325 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 25 Nov 2022 18:20:33 +0100 Subject: [PATCH 0733/1033] Fix docstring for entity helper (#82704) Fix some language in entity.py --- homeassistant/helpers/entity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index d4f8128f643..255b0c2d834 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -144,7 +144,7 @@ def get_supported_features(hass: HomeAssistant, entity_id: str) -> int: def get_unit_of_measurement(hass: HomeAssistant, entity_id: str) -> str | None: - """Get unit of measurement class of an entity. + """Get unit of measurement of an entity. First try the statemachine, then entity registry. """ From 7b5c80720a9415f89d1076e62adfbdd0b6c543e0 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 25 Nov 2022 20:12:27 +0100 Subject: [PATCH 0734/1033] Tweak scaffold script (#82705) --- .../templates/config_flow_helper/integration/config_flow.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/scaffold/templates/config_flow_helper/integration/config_flow.py b/script/scaffold/templates/config_flow_helper/integration/config_flow.py index 015aac2ca91..f9b8e81d364 100644 --- a/script/scaffold/templates/config_flow_helper/integration/config_flow.py +++ b/script/scaffold/templates/config_flow_helper/integration/config_flow.py @@ -6,6 +6,7 @@ from typing import Any, cast import voluptuous as vol +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import CONF_ENTITY_ID from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( @@ -19,7 +20,7 @@ from .const import DOMAIN OPTIONS_SCHEMA = vol.Schema( { vol.Required(CONF_ENTITY_ID): selector.EntitySelector( - selector.EntitySelectorConfig(domain="sensor") + selector.EntitySelectorConfig(domain=SENSOR_DOMAIN) ), } ) From e9a9ab9025c16ed186599d605008e3a7692a805b Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 25 Nov 2022 21:07:08 +0100 Subject: [PATCH 0735/1033] Correction in mqtt error string (#82712) Text correction in mqtt error string --- homeassistant/components/mqtt/strings.json | 2 +- homeassistant/components/mqtt/translations/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/strings.json b/homeassistant/components/mqtt/strings.json index 30cb57bb85a..0ef5ea29068 100644 --- a/homeassistant/components/mqtt/strings.json +++ b/homeassistant/components/mqtt/strings.json @@ -52,7 +52,7 @@ "bad_certificate": "The CA certificate is invalid", "bad_client_cert": "Invalid client certificate, ensure a PEM coded file is supplied", "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", - "bad_client_cert_key": "Client certificate and private are no valid pair", + "bad_client_cert_key": "Client certificate and private key are not a valid pair", "bad_ws_headers": "Supply valid HTTP headers as a JSON object", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_inclusion": "The client certificate and private key must be configurered together" diff --git a/homeassistant/components/mqtt/translations/en.json b/homeassistant/components/mqtt/translations/en.json index 78f91c27f30..dd3b69ec2f4 100644 --- a/homeassistant/components/mqtt/translations/en.json +++ b/homeassistant/components/mqtt/translations/en.json @@ -8,7 +8,7 @@ "bad_birth": "Invalid birth topic", "bad_certificate": "The CA certificate is invalid", "bad_client_cert": "Invalid client certificate, ensure a PEM coded file is supplied", - "bad_client_cert_key": "Client certificate and private are no valid pair", + "bad_client_cert_key": "Client certificate and private key are not a valid pair", "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", "bad_discovery_prefix": "Invalid discovery prefix", "bad_will": "Invalid will topic", From 1c9b068331c82ec249fb1eac94c554a86e9556d4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 25 Nov 2022 11:34:36 -1000 Subject: [PATCH 0736/1033] Fix grammar in ibeacon comments (#82718) * Fix grammar in ibeacon comments * Update homeassistant/components/ibeacon/coordinator.py --- homeassistant/components/ibeacon/coordinator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/ibeacon/coordinator.py b/homeassistant/components/ibeacon/coordinator.py index d38f935cb77..2e9af4ad9e6 100644 --- a/homeassistant/components/ibeacon/coordinator.py +++ b/homeassistant/components/ibeacon/coordinator.py @@ -355,8 +355,8 @@ class IBeaconCoordinator: if group_id not in self._unavailable_group_ids and (service_info := self._last_seen_by_group_id.get(group_id)) and ( - # We will not be callbacks for iBeacons with random macs - # that rotate infrequently since their advertisement data is + # We will not get callbacks for iBeacons with random macs + # that rotate infrequently since their advertisement data # does not change as the bluetooth.async_register_callback API # suppresses callbacks for duplicate advertisements to avoid # exposing integrations to the firehose of bluetooth advertisements. From 797dc51bc5825dac4906825e1e5d914b103b81d0 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 26 Nov 2022 00:22:30 +0000 Subject: [PATCH 0737/1033] [ci skip] Translation update --- .../accuweather/translations/bg.json | 6 +++ .../accuweather/translations/ca.json | 5 +++ .../accuweather/translations/cs.json | 6 +++ .../accuweather/translations/et.json | 6 +++ .../accuweather/translations/nl.json | 6 +++ .../accuweather/translations/ru.json | 6 +++ .../accuweather/translations/zh-Hant.json | 6 +++ .../components/airly/translations/cs.json | 3 +- .../components/airq/translations/cs.json | 20 +++++++++ .../components/airzone/translations/cs.json | 8 ++++ .../components/airzone/translations/nl.json | 7 +++ .../components/apcupsd/translations/cs.json | 7 +++ .../components/asuswrt/translations/cs.json | 1 + .../components/braviatv/translations/cs.json | 4 +- .../components/coinbase/translations/cs.json | 1 + .../components/demo/translations/bg.json | 3 ++ .../components/demo/translations/ca.json | 8 ++++ .../components/demo/translations/cs.json | 10 +++++ .../components/demo/translations/et.json | 8 ++++ .../components/demo/translations/nl.json | 8 ++++ .../components/demo/translations/ru.json | 8 ++++ .../devolo_home_network/translations/cs.json | 8 +++- .../components/dnsip/translations/cs.json | 7 +++ .../dsmr_reader/translations/cs.json | 7 +++ .../components/esphome/translations/bg.json | 2 +- .../components/esphome/translations/cs.json | 6 +++ .../components/esphome/translations/nl.json | 5 +++ .../components/filesize/translations/cs.json | 1 + .../fireservicerota/translations/cs.json | 6 +++ .../forecast_solar/translations/bg.json | 3 ++ .../forecast_solar/translations/ca.json | 3 ++ .../forecast_solar/translations/cs.json | 3 ++ .../forecast_solar/translations/de.json | 3 ++ .../forecast_solar/translations/el.json | 3 ++ .../forecast_solar/translations/et.json | 3 ++ .../forecast_solar/translations/nl.json | 3 ++ .../forecast_solar/translations/ru.json | 3 ++ .../components/fritz/translations/cs.json | 3 +- .../components/generic/translations/cs.json | 14 ++++++ .../geocaching/translations/cs.json | 1 + .../components/google/translations/cs.json | 3 +- .../google_sheets/translations/cs.json | 28 ++++++++++++ .../google_travel_time/translations/cs.json | 3 +- .../harmony/translations/select.ca.json | 7 +++ .../harmony/translations/select.cs.json | 7 +++ .../harmony/translations/select.nl.json | 7 +++ .../components/hassio/translations/cs.json | 37 ++++++++++++++++ .../components/hive/translations/cs.json | 10 +++++ .../homeassistant/translations/cs.json | 10 +++++ .../homeassistant_yellow/translations/cs.json | 41 +++++++++++++++++ .../homeassistant_yellow/translations/nl.json | 3 ++ .../huawei_lte/translations/cs.json | 10 ++++- .../components/ibeacon/translations/cs.json | 12 +++++ .../intellifire/translations/cs.json | 1 + .../components/isy994/translations/cs.json | 1 + .../components/kegtron/translations/cs.json | 22 ++++++++++ .../keymitt_ble/translations/cs.json | 10 +++++ .../components/knx/translations/cs.json | 32 +++++++++++++- .../components/kraken/translations/cs.json | 7 +++ .../components/lametric/translations/cs.json | 3 +- .../components/led_ble/translations/cs.json | 7 +++ .../components/lidarr/translations/cs.json | 28 ++++++++++++ .../components/livisi/translations/cs.json | 16 +++++++ .../components/mazda/translations/cs.json | 1 + .../components/min_max/translations/et.json | 8 ++-- .../components/min_max/translations/ru.json | 10 ++--- .../min_max/translations/zh-Hant.json | 10 ++--- .../components/mqtt/translations/cs.json | 17 +++++-- .../components/mqtt/translations/en.json | 2 +- .../components/mqtt/translations/nl.json | 17 +++++-- .../components/netgear/translations/cs.json | 3 +- .../components/nextdns/translations/nl.json | 8 ++++ .../nibe_heatpump/translations/cs.json | 19 ++++++++ .../nmap_tracker/translations/cs.json | 8 ++++ .../components/onvif/translations/cs.json | 3 +- .../components/oralb/translations/cs.json | 22 ++++++++++ .../components/overkiz/translations/cs.json | 3 +- .../components/plugwise/translations/bg.json | 3 +- .../components/plugwise/translations/ca.json | 3 +- .../components/plugwise/translations/cs.json | 5 ++- .../components/plugwise/translations/de.json | 6 ++- .../components/plugwise/translations/el.json | 4 +- .../components/plugwise/translations/es.json | 6 ++- .../components/plugwise/translations/et.json | 6 ++- .../components/plugwise/translations/nl.json | 4 +- .../components/plugwise/translations/ru.json | 6 ++- .../plugwise/translations/select.cs.json | 7 +++ .../plugwise/translations/zh-Hant.json | 6 ++- .../pushbullet/translations/cs.json | 25 +++++++++++ .../components/pushover/translations/cs.json | 5 +++ .../components/radarr/translations/bg.json | 4 ++ .../components/radarr/translations/cs.json | 44 +++++++++++++++++++ .../components/radarr/translations/de.json | 4 ++ .../components/radarr/translations/et.json | 4 ++ .../components/radarr/translations/nl.json | 3 ++ .../components/radarr/translations/ru.json | 4 ++ .../radarr/translations/zh-Hant.json | 4 ++ .../rtsp_to_webrtc/translations/cs.json | 9 ++++ .../components/scrape/translations/cs.json | 41 +++++++++++++++++ .../components/scrape/translations/nl.json | 30 +++++++++++++ .../sensibo/translations/sensor.cs.json | 9 ++++ .../components/shelly/translations/cs.json | 13 ++++++ .../components/sia/translations/cs.json | 1 + .../simplisafe/translations/cs.json | 1 + .../components/snooz/translations/cs.json | 21 +++++++++ .../tankerkoenig/translations/cs.json | 1 + .../components/tasmota/translations/cs.json | 5 +++ .../components/tautulli/translations/cs.json | 7 +++ .../components/text/translations/bg.json | 3 ++ .../components/text/translations/ca.json | 3 ++ .../components/text/translations/cs.json | 3 ++ .../components/text/translations/et.json | 3 ++ .../components/text/translations/nl.json | 3 ++ .../components/text/translations/ru.json | 3 ++ .../components/text/translations/zh-Hant.json | 3 ++ .../trafikverket_train/translations/cs.json | 7 +++ .../ukraine_alarm/translations/cs.json | 1 + .../unifiprotect/translations/cs.json | 28 ++++++++++++ .../wake_on_lan/translations/cs.json | 8 ++++ .../components/wiz/translations/cs.json | 1 + .../xiaomi_miio/translations/cs.json | 3 +- .../translations/select.cs.json | 5 +++ .../components/yolink/translations/cs.json | 1 + .../components/zamg/translations/cs.json | 11 +++++ .../components/zha/translations/cs.json | 3 ++ .../components/zwave_js/translations/cs.json | 3 +- 126 files changed, 989 insertions(+), 51 deletions(-) create mode 100644 homeassistant/components/airq/translations/cs.json create mode 100644 homeassistant/components/apcupsd/translations/cs.json create mode 100644 homeassistant/components/dnsip/translations/cs.json create mode 100644 homeassistant/components/dsmr_reader/translations/cs.json create mode 100644 homeassistant/components/google_sheets/translations/cs.json create mode 100644 homeassistant/components/harmony/translations/select.ca.json create mode 100644 homeassistant/components/harmony/translations/select.cs.json create mode 100644 homeassistant/components/harmony/translations/select.nl.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/cs.json create mode 100644 homeassistant/components/ibeacon/translations/cs.json create mode 100644 homeassistant/components/kegtron/translations/cs.json create mode 100644 homeassistant/components/keymitt_ble/translations/cs.json create mode 100644 homeassistant/components/kraken/translations/cs.json create mode 100644 homeassistant/components/lidarr/translations/cs.json create mode 100644 homeassistant/components/livisi/translations/cs.json create mode 100644 homeassistant/components/nibe_heatpump/translations/cs.json create mode 100644 homeassistant/components/oralb/translations/cs.json create mode 100644 homeassistant/components/plugwise/translations/select.cs.json create mode 100644 homeassistant/components/pushbullet/translations/cs.json create mode 100644 homeassistant/components/radarr/translations/cs.json create mode 100644 homeassistant/components/sensibo/translations/sensor.cs.json create mode 100644 homeassistant/components/snooz/translations/cs.json create mode 100644 homeassistant/components/text/translations/bg.json create mode 100644 homeassistant/components/text/translations/ca.json create mode 100644 homeassistant/components/text/translations/cs.json create mode 100644 homeassistant/components/text/translations/et.json create mode 100644 homeassistant/components/text/translations/nl.json create mode 100644 homeassistant/components/text/translations/ru.json create mode 100644 homeassistant/components/text/translations/zh-Hant.json create mode 100644 homeassistant/components/wake_on_lan/translations/cs.json create mode 100644 homeassistant/components/zamg/translations/cs.json diff --git a/homeassistant/components/accuweather/translations/bg.json b/homeassistant/components/accuweather/translations/bg.json index 8435bea00df..9c5e3075643 100644 --- a/homeassistant/components/accuweather/translations/bg.json +++ b/homeassistant/components/accuweather/translations/bg.json @@ -20,6 +20,12 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 \u0437\u0430 \u0432\u0440\u0435\u043c\u0435\u0442\u043e" + }, + "description": "\u041f\u043e\u0440\u0430\u0434\u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f\u0442\u0430 \u043d\u0430 \u0431\u0435\u0437\u043f\u043b\u0430\u0442\u043d\u0430\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u043d\u0430 API \u043a\u043b\u044e\u0447\u0430 \u043d\u0430 AccuWeather, \u043a\u043e\u0433\u0430\u0442\u043e \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u0442\u0435 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430\u0442\u0430 \u0437\u0430 \u0432\u0440\u0435\u043c\u0435\u0442\u043e, \u0430\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u0442\u0435 \u043d\u0430 \u0434\u0430\u043d\u043d\u0438 \u0449\u0435 \u0441\u0435 \u0438\u0437\u0432\u044a\u0440\u0448\u0432\u0430\u0442 \u043d\u0430 \u0432\u0441\u0435\u043a\u0438 80 \u043c\u0438\u043d\u0443\u0442\u0438 \u0432\u043c\u0435\u0441\u0442\u043e \u043d\u0430 \u0432\u0441\u0435\u043a\u0438 40 \u043c\u0438\u043d\u0443\u0442\u0438." + }, "user": { "data": { "forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 \u0437\u0430 \u0432\u0440\u0435\u043c\u0435\u0442\u043e" diff --git a/homeassistant/components/accuweather/translations/ca.json b/homeassistant/components/accuweather/translations/ca.json index e80a006141a..54b93643e8e 100644 --- a/homeassistant/components/accuweather/translations/ca.json +++ b/homeassistant/components/accuweather/translations/ca.json @@ -24,6 +24,11 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "Previsi\u00f3 meteorol\u00f2gica" + } + }, "user": { "data": { "forecast": "Previsi\u00f3 meteorol\u00f2gica" diff --git a/homeassistant/components/accuweather/translations/cs.json b/homeassistant/components/accuweather/translations/cs.json index e3ae982cddc..f0796ff4d1e 100644 --- a/homeassistant/components/accuweather/translations/cs.json +++ b/homeassistant/components/accuweather/translations/cs.json @@ -21,6 +21,12 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "P\u0159edpov\u011b\u010f po\u010das\u00ed" + }, + "description": "Vzhledem k omezen\u00edm bezplatn\u00e9 verze kl\u00ed\u010de AccuWeather API, kdy\u017e povol\u00edte p\u0159edpov\u011b\u010f po\u010das\u00ed, aktualizace dat se budou prov\u00e1d\u011bt ka\u017ed\u00fdch 80 minut m\u00edsto ka\u017ed\u00fdch 40 minut." + }, "user": { "data": { "forecast": "P\u0159edpov\u011b\u010f po\u010das\u00ed" diff --git a/homeassistant/components/accuweather/translations/et.json b/homeassistant/components/accuweather/translations/et.json index 2f85bd8663e..9ce64fdd91c 100644 --- a/homeassistant/components/accuweather/translations/et.json +++ b/homeassistant/components/accuweather/translations/et.json @@ -24,6 +24,12 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "Ilmateade" + }, + "description": "AccuWeather API tasuta versioonis toimub ilmaennustuse lubamisel andmete v\u00e4rskendamine iga 80 minuti j\u00e4rel (muidu 40 minutit)." + }, "user": { "data": { "forecast": "Ilmateade" diff --git a/homeassistant/components/accuweather/translations/nl.json b/homeassistant/components/accuweather/translations/nl.json index df5c71dcf45..9b18af3b6da 100644 --- a/homeassistant/components/accuweather/translations/nl.json +++ b/homeassistant/components/accuweather/translations/nl.json @@ -24,6 +24,12 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "Weersverwachting" + }, + "description": "Wanneer je de weersverwachting ingeschakeld zullen updates elke 80 minuten plaatsvinden i.p.v. elke 40 minuten, dit komt door de beperkingen van de gratis versie van de AccuWeather API sleutel." + }, "user": { "data": { "forecast": "Weervoorspelling" diff --git a/homeassistant/components/accuweather/translations/ru.json b/homeassistant/components/accuweather/translations/ru.json index 2f08263b4f5..8f137f71c74 100644 --- a/homeassistant/components/accuweather/translations/ru.json +++ b/homeassistant/components/accuweather/translations/ru.json @@ -24,6 +24,12 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b" + }, + "description": "\u0412 \u0441\u0432\u044f\u0437\u0438 \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u043a\u043b\u044e\u0447\u0430 API AccuWeather, \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 \u043f\u043e\u0433\u043e\u0434\u044b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0435 80 \u043c\u0438\u043d\u0443\u0442, \u0430 \u043d\u0435 \u043a\u0430\u0436\u0434\u044b\u0435 40 \u043c\u0438\u043d\u0443\u0442." + }, "user": { "data": { "forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b" diff --git a/homeassistant/components/accuweather/translations/zh-Hant.json b/homeassistant/components/accuweather/translations/zh-Hant.json index c7476b33460..03bfb362e30 100644 --- a/homeassistant/components/accuweather/translations/zh-Hant.json +++ b/homeassistant/components/accuweather/translations/zh-Hant.json @@ -24,6 +24,12 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "\u5929\u6c23\u9810\u5831" + }, + "description": "\u7531\u65bc AccuWeather API \u91d1\u9470\u514d\u8cbb\u7248\u672c\u9650\u5236\uff0c\u7576\u958b\u555f\u5929\u6c23\u9810\u5831\u6642\u3001\u6578\u64da\u6703\u6bcf 80 \u5206\u9418\u66f4\u65b0\u4e00\u6b21\uff0c\u800c\u975e 40 \u5206\u9418\u3002" + }, "user": { "data": { "forecast": "\u5929\u6c23\u9810\u5831" diff --git a/homeassistant/components/airly/translations/cs.json b/homeassistant/components/airly/translations/cs.json index 3b4f98f53f8..766ca31099f 100644 --- a/homeassistant/components/airly/translations/cs.json +++ b/homeassistant/components/airly/translations/cs.json @@ -21,7 +21,8 @@ }, "system_health": { "info": { - "can_reach_server": "Lze kontaktovat Airly server" + "can_reach_server": "Lze kontaktovat Airly server", + "requests_per_day": "Povolen\u00e9 po\u017eadavky za den" } } } \ No newline at end of file diff --git a/homeassistant/components/airq/translations/cs.json b/homeassistant/components/airq/translations/cs.json new file mode 100644 index 00000000000..efbce0227ab --- /dev/null +++ b/homeassistant/components/airq/translations/cs.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno" + }, + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", + "invalid_input": "Neplatn\u00fd hostitel nebo IP adresa" + }, + "step": { + "user": { + "data": { + "ip_address": "IP adresa", + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airzone/translations/cs.json b/homeassistant/components/airzone/translations/cs.json index aad89c1cbe3..9015e7fd0b6 100644 --- a/homeassistant/components/airzone/translations/cs.json +++ b/homeassistant/components/airzone/translations/cs.json @@ -7,9 +7,17 @@ "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" }, "step": { + "discovered_connection": { + "data": { + "host": "Hostitel", + "id": "ID syst\u00e9mu", + "port": "Port" + } + }, "user": { "data": { "host": "Hostitel", + "id": "ID syst\u00e9mu", "port": "Port" } } diff --git a/homeassistant/components/airzone/translations/nl.json b/homeassistant/components/airzone/translations/nl.json index e182d71963d..b87f0f3cab5 100644 --- a/homeassistant/components/airzone/translations/nl.json +++ b/homeassistant/components/airzone/translations/nl.json @@ -8,9 +8,16 @@ "invalid_system_id": "Ongeldige Airzone systeem ID" }, "step": { + "discovered_connection": { + "data": { + "id": "Systeem ID", + "port": "Poort" + } + }, "user": { "data": { "host": "Host", + "id": "Systeem ID", "port": "Poort" }, "description": "Airzone integratie instellen." diff --git a/homeassistant/components/apcupsd/translations/cs.json b/homeassistant/components/apcupsd/translations/cs.json new file mode 100644 index 00000000000..5d403348397 --- /dev/null +++ b/homeassistant/components/apcupsd/translations/cs.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/asuswrt/translations/cs.json b/homeassistant/components/asuswrt/translations/cs.json index 26358d8c4bd..883ace178af 100644 --- a/homeassistant/components/asuswrt/translations/cs.json +++ b/homeassistant/components/asuswrt/translations/cs.json @@ -3,6 +3,7 @@ "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "invalid_host": "Neplatn\u00fd hostitel nebo IP adresa", + "ssh_not_file": "Soubor kl\u00ed\u010de SSH nebyl nalezen", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { diff --git a/homeassistant/components/braviatv/translations/cs.json b/homeassistant/components/braviatv/translations/cs.json index 59eed7f4187..583ad34efac 100644 --- a/homeassistant/components/braviatv/translations/cs.json +++ b/homeassistant/components/braviatv/translations/cs.json @@ -1,10 +1,12 @@ { "config": { "abort": { - "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno" + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", + "not_bravia_device": "Za\u0159\u00edzen\u00ed nen\u00ed Bravia TV." }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "invalid_host": "Neplatn\u00fd hostitel nebo IP adresa", "unsupported_model": "V\u00e1\u0161 model televize nen\u00ed podporov\u00e1n." }, diff --git a/homeassistant/components/coinbase/translations/cs.json b/homeassistant/components/coinbase/translations/cs.json index 7ec64b2fa14..3209d809966 100644 --- a/homeassistant/components/coinbase/translations/cs.json +++ b/homeassistant/components/coinbase/translations/cs.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", + "invalid_auth_key": "P\u0159ihla\u0161ovac\u00ed \u00fadaje API zam\u00edtnuty Coinbasem z d\u016fvodu neplatn\u00e9ho kl\u00ed\u010de API.", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { diff --git a/homeassistant/components/demo/translations/bg.json b/homeassistant/components/demo/translations/bg.json index bd761c705ff..98f28e6d881 100644 --- a/homeassistant/components/demo/translations/bg.json +++ b/homeassistant/components/demo/translations/bg.json @@ -11,6 +11,9 @@ }, "title": "\u0417\u0430\u0445\u0440\u0430\u043d\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0435 \u0435 \u0441\u0442\u0430\u0431\u0438\u043b\u043d\u043e" }, + "cold_tea": { + "title": "\u0427\u0430\u044f\u0442 \u0435 \u0441\u0442\u0443\u0434\u0435\u043d" + }, "unfixable_problem": { "title": "\u0422\u043e\u0432\u0430 \u043d\u0435 \u0435 \u043f\u043e\u043f\u0440\u0430\u0432\u0438\u043c \u043f\u0440\u043e\u0431\u043b\u0435\u043c" } diff --git a/homeassistant/components/demo/translations/ca.json b/homeassistant/components/demo/translations/ca.json index cf6055bfda4..5a126471bf4 100644 --- a/homeassistant/components/demo/translations/ca.json +++ b/homeassistant/components/demo/translations/ca.json @@ -11,6 +11,14 @@ }, "title": "La font d'alimentaci\u00f3 no \u00e9s estable" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "No es pot tornar a escalfar el te en aquest moment" + } + }, + "title": "El te \u00e9s fred" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/demo/translations/cs.json b/homeassistant/components/demo/translations/cs.json index 1bac6710266..2bc9b387da0 100644 --- a/homeassistant/components/demo/translations/cs.json +++ b/homeassistant/components/demo/translations/cs.json @@ -1,4 +1,14 @@ { + "issues": { + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "V tuto chv\u00edli nelze \u010daj znovu oh\u0159\u00e1t" + } + }, + "title": "\u010caj je studen\u00fd" + } + }, "options": { "step": { "options_1": { diff --git a/homeassistant/components/demo/translations/et.json b/homeassistant/components/demo/translations/et.json index ad7d7e361b6..9cf984ccdd8 100644 --- a/homeassistant/components/demo/translations/et.json +++ b/homeassistant/components/demo/translations/et.json @@ -11,6 +11,14 @@ }, "title": "Toiteallikas ei ole stabiilne" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "Teed ei saa praegu uuesti soojendada." + } + }, + "title": "Tee on k\u00fclm" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/demo/translations/nl.json b/homeassistant/components/demo/translations/nl.json index 08aeec0fcd0..c1a0cf5590b 100644 --- a/homeassistant/components/demo/translations/nl.json +++ b/homeassistant/components/demo/translations/nl.json @@ -9,6 +9,14 @@ } }, "title": "De voeding is niet stabiel" + }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "Kan op dit moment de thee niet opnieuw verwarmen" + } + }, + "title": "De thee is koud" } }, "options": { diff --git a/homeassistant/components/demo/translations/ru.json b/homeassistant/components/demo/translations/ru.json index b7f3ddb20a7..7f2e10564f8 100644 --- a/homeassistant/components/demo/translations/ru.json +++ b/homeassistant/components/demo/translations/ru.json @@ -11,6 +11,14 @@ }, "title": "\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u043f\u0438\u0442\u0430\u043d\u0438\u044f \u043d\u0435 \u0441\u0442\u0430\u0431\u0438\u043b\u0435\u043d" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u043f\u043e\u0434\u043e\u0433\u0440\u0435\u0442\u044c \u0447\u0430\u0439 \u0432 \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f." + } + }, + "title": "\u0427\u0430\u0439 \u043e\u0441\u0442\u044b\u043b" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/devolo_home_network/translations/cs.json b/homeassistant/components/devolo_home_network/translations/cs.json index 04f18366eaf..42631f030f3 100644 --- a/homeassistant/components/devolo_home_network/translations/cs.json +++ b/homeassistant/components/devolo_home_network/translations/cs.json @@ -1,13 +1,19 @@ { "config": { "abort": { - "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno" + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", + "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { "ip_address": "IP adresa" diff --git a/homeassistant/components/dnsip/translations/cs.json b/homeassistant/components/dnsip/translations/cs.json new file mode 100644 index 00000000000..92fbbc7fe04 --- /dev/null +++ b/homeassistant/components/dnsip/translations/cs.json @@ -0,0 +1,7 @@ +{ + "options": { + "error": { + "invalid_resolver": "Neplatn\u00e1 IP adresa pro resolver" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dsmr_reader/translations/cs.json b/homeassistant/components/dsmr_reader/translations/cs.json new file mode 100644 index 00000000000..19f5d1e1587 --- /dev/null +++ b/homeassistant/components/dsmr_reader/translations/cs.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/bg.json b/homeassistant/components/esphome/translations/bg.json index 282d3503aec..553ca45a10f 100644 --- a/homeassistant/components/esphome/translations/bg.json +++ b/homeassistant/components/esphome/translations/bg.json @@ -6,7 +6,7 @@ }, "error": { "connection_error": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 ESP. \u041c\u043e\u043b\u044f, \u0443\u0432\u0435\u0440\u0435\u0442\u0435 \u0441\u0435, \u0447\u0435 \u0432\u0430\u0448\u0438\u044f\u0442 YAML \u0444\u0430\u0439\u043b \u0441\u044a\u0434\u044a\u0440\u0436\u0430 \u0440\u0435\u0434 \"api:\".", - "resolve_error": "\u041d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0441\u0435 \u043e\u0442\u043a\u0440\u0438\u0435 \u0430\u0434\u0440\u0435\u0441\u044a\u0442 \u043d\u0430 ESP. \u0410\u043a\u043e \u0442\u0430\u0437\u0438 \u0433\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0430\u0432\u0430, \u0437\u0430\u0434\u0430\u0439\u0442\u0435 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u043d IP \u0430\u0434\u0440\u0435\u0441: https://esphomelib.com/esphomeyaml/components/wifi.html#manual-ips" + "resolve_error": "\u041d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0441\u0435 \u043e\u0442\u043a\u0440\u0438\u0435 \u0430\u0434\u0440\u0435\u0441\u044a\u0442 \u043d\u0430 ESP. \u0410\u043a\u043e \u0442\u0430\u0437\u0438 \u0433\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0430\u0432\u0430, \u0437\u0430\u0434\u0430\u0439\u0442\u0435 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u043d IP \u0430\u0434\u0440\u0435\u0441." }, "flow_title": "ESPHome: {name}", "step": { diff --git a/homeassistant/components/esphome/translations/cs.json b/homeassistant/components/esphome/translations/cs.json index cdd86bd2577..9b4976ddaa1 100644 --- a/homeassistant/components/esphome/translations/cs.json +++ b/homeassistant/components/esphome/translations/cs.json @@ -43,5 +43,11 @@ "description": "Zadejte pros\u00edm nastaven\u00ed p\u0159ipojen\u00ed va\u0161eho [ESPHome](https://esphomelib.com/) uzlu." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "Chcete-li zlep\u0161it spolehlivost a v\u00fdkon Bluetooth, d\u016frazn\u011b doporu\u010dujeme aktualizovat {name} na ESPHome 2022.11.0 nebo nov\u011bj\u0161\u00ed.", + "title": "Aktualizovat {name} pomoc\u00ed ESPHome 2022.11.0 nebo nov\u011bj\u0161\u00ed" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/nl.json b/homeassistant/components/esphome/translations/nl.json index aae5b639eb8..f8410bd73ef 100644 --- a/homeassistant/components/esphome/translations/nl.json +++ b/homeassistant/components/esphome/translations/nl.json @@ -43,5 +43,10 @@ "description": "Voer de verbindingsinstellingen van je [ESPHome](https://esphome.io/) apparaat in." } } + }, + "issues": { + "ble_firmware_outdated": { + "title": "Werk {name} bij naar ESPHome versie 2022.11.0 of later" + } } } \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/cs.json b/homeassistant/components/filesize/translations/cs.json index 1c38c1deb5d..ced67575b71 100644 --- a/homeassistant/components/filesize/translations/cs.json +++ b/homeassistant/components/filesize/translations/cs.json @@ -1,6 +1,7 @@ { "config": { "error": { + "not_allowed": "Cesta nen\u00ed povolena", "not_valid": "Cesta nen\u00ed platn\u00e1" } } diff --git a/homeassistant/components/fireservicerota/translations/cs.json b/homeassistant/components/fireservicerota/translations/cs.json index bd87c16b325..6edd5e1a056 100644 --- a/homeassistant/components/fireservicerota/translations/cs.json +++ b/homeassistant/components/fireservicerota/translations/cs.json @@ -11,6 +11,12 @@ "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" }, "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + }, + "description": "Ov\u011b\u0159ovac\u00ed tokeny se staly neplatn\u00fdmi, p\u0159ihlaste se a vytvo\u0159te je znovu." + }, "user": { "data": { "password": "Heslo", diff --git a/homeassistant/components/forecast_solar/translations/bg.json b/homeassistant/components/forecast_solar/translations/bg.json index 289146783a4..210c38c6225 100644 --- a/homeassistant/components/forecast_solar/translations/bg.json +++ b/homeassistant/components/forecast_solar/translations/bg.json @@ -13,6 +13,9 @@ } }, "options": { + "error": { + "invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/ca.json b/homeassistant/components/forecast_solar/translations/ca.json index b2561bb0a79..65d1c3100a7 100644 --- a/homeassistant/components/forecast_solar/translations/ca.json +++ b/homeassistant/components/forecast_solar/translations/ca.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Clau API inv\u00e0lida" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/cs.json b/homeassistant/components/forecast_solar/translations/cs.json index d1c9b95470f..0fe5725fd01 100644 --- a/homeassistant/components/forecast_solar/translations/cs.json +++ b/homeassistant/components/forecast_solar/translations/cs.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Neplatn\u00fd kl\u00ed\u010d API" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/de.json b/homeassistant/components/forecast_solar/translations/de.json index 4e683878c11..4466dd15fcc 100644 --- a/homeassistant/components/forecast_solar/translations/de.json +++ b/homeassistant/components/forecast_solar/translations/de.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/el.json b/homeassistant/components/forecast_solar/translations/el.json index e2e75c05f65..31e2708207f 100644 --- a/homeassistant/components/forecast_solar/translations/el.json +++ b/homeassistant/components/forecast_solar/translations/el.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/et.json b/homeassistant/components/forecast_solar/translations/et.json index 3a3b3f4c689..2c611bafeba 100644 --- a/homeassistant/components/forecast_solar/translations/et.json +++ b/homeassistant/components/forecast_solar/translations/et.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Kehtetu API v\u00f5ti" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/nl.json b/homeassistant/components/forecast_solar/translations/nl.json index dbc966e59fc..3e92695648e 100644 --- a/homeassistant/components/forecast_solar/translations/nl.json +++ b/homeassistant/components/forecast_solar/translations/nl.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Ongeldige API sleutel" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/ru.json b/homeassistant/components/forecast_solar/translations/ru.json index f7d4d502691..88c6407d324 100644 --- a/homeassistant/components/forecast_solar/translations/ru.json +++ b/homeassistant/components/forecast_solar/translations/ru.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API." + }, "step": { "init": { "data": { diff --git a/homeassistant/components/fritz/translations/cs.json b/homeassistant/components/fritz/translations/cs.json index c4ca8595f52..7c7e0e13380 100644 --- a/homeassistant/components/fritz/translations/cs.json +++ b/homeassistant/components/fritz/translations/cs.json @@ -9,7 +9,8 @@ "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" + "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", + "upnp_not_configured": "Na za\u0159\u00edzen\u00ed chyb\u00ed nastaven\u00ed UPnP." }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/generic/translations/cs.json b/homeassistant/components/generic/translations/cs.json index 4cba991c071..4f46944044a 100644 --- a/homeassistant/components/generic/translations/cs.json +++ b/homeassistant/components/generic/translations/cs.json @@ -1,6 +1,10 @@ { "config": { "error": { + "already_exists": "Kamera s t\u011bmito nastaven\u00edmi URL ji\u017e existuje.", + "invalid_still_image": "Adresa URL nevr\u00e1tila platn\u00fd statick\u00fd obr\u00e1zek", + "relative_url": "Relativn\u00ed adresy URL nejsou povoleny", + "timeout": "P\u0159i na\u010d\u00edt\u00e1n\u00ed adresy URL vypr\u0161el \u010dasov\u00fd limit", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { @@ -12,16 +16,26 @@ "description": "Zadejte nastaven\u00ed pro p\u0159ipojen\u00ed ke kame\u0159e." }, "user_confirm_still": { + "data": { + "confirmed_ok": "Tento obr\u00e1zek vypad\u00e1 dob\u0159e." + }, + "description": "![N\u00e1hled statick\u00e9ho sn\u00edmku z fotoapar\u00e1tu]( {preview_url} )", "title": "N\u00e1hled" } } }, "options": { "error": { + "already_exists": "Kamera s t\u011bmito nastaven\u00edmi URL ji\u017e existuje.", + "invalid_still_image": "Adresa URL nevr\u00e1tila platn\u00fd statick\u00fd obr\u00e1zek", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { "confirm_still": { + "data": { + "confirmed_ok": "Tento obr\u00e1zek vypad\u00e1 dob\u0159e." + }, + "description": "![N\u00e1hled statick\u00e9ho sn\u00edmku z fotoapar\u00e1tu]( {preview_url} )", "title": "N\u00e1hled" }, "init": { diff --git a/homeassistant/components/geocaching/translations/cs.json b/homeassistant/components/geocaching/translations/cs.json index 5b7d9c2db8e..67665aec47e 100644 --- a/homeassistant/components/geocaching/translations/cs.json +++ b/homeassistant/components/geocaching/translations/cs.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u00da\u010det je ji\u017e nastaven", "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", + "oauth_error": "P\u0159ijata neplatn\u00e1 data tokenu.", "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "step": { diff --git a/homeassistant/components/google/translations/cs.json b/homeassistant/components/google/translations/cs.json index f1d2cc4c85a..039106b4e21 100644 --- a/homeassistant/components/google/translations/cs.json +++ b/homeassistant/components/google/translations/cs.json @@ -7,7 +7,8 @@ "invalid_access_token": "Neplatn\u00fd p\u0159\u00edstupov\u00fd token", "missing_configuration": "Komponenta nen\u00ed nastavena. Postupujte podle dokumentace.", "oauth_error": "P\u0159ijata neplatn\u00e1 data tokenu.", - "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" + "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9", + "timeout_connect": "Vypr\u0161el \u010dasov\u00fd limit pro nav\u00e1z\u00e1n\u00ed spojen\u00ed" }, "create_entry": { "default": "\u00dasp\u011b\u0161n\u011b ov\u011b\u0159eno" diff --git a/homeassistant/components/google_sheets/translations/cs.json b/homeassistant/components/google_sheets/translations/cs.json new file mode 100644 index 00000000000..1ce33bab1a9 --- /dev/null +++ b/homeassistant/components/google_sheets/translations/cs.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "\u00da\u010det je ji\u017e nastaven", + "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "create_spreadsheet_failure": "P\u0159i vytv\u00e1\u0159en\u00ed tabulky do\u0161lo k chyb\u011b, podrobnosti naleznete v protokolu chyb", + "invalid_access_token": "Neplatn\u00fd p\u0159\u00edstupov\u00fd token", + "missing_configuration": "Komponenta nen\u00ed nastavena. Postupujte podle dokumentace.", + "oauth_error": "P\u0159ijata neplatn\u00e1 data tokenu.", + "open_spreadsheet_failure": "Chyba p\u0159i otev\u00edr\u00e1n\u00ed tabulky, podrobnosti naleznete v protokolu chyb", + "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9", + "timeout_connect": "Vypr\u0161el \u010dasov\u00fd limit pro nav\u00e1z\u00e1n\u00ed spojen\u00ed", + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + }, + "create_entry": { + "default": "\u00dasp\u011b\u0161n\u011b ov\u011b\u0159eno a vytvo\u0159ena tabulka na adrese: {url}" + }, + "step": { + "auth": { + "title": "Propojit \u00fa\u010det Google" + }, + "reauth_confirm": { + "title": "Znovu ov\u011b\u0159it integraci" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_travel_time/translations/cs.json b/homeassistant/components/google_travel_time/translations/cs.json index 19da83d1596..edfdda6d4df 100644 --- a/homeassistant/components/google_travel_time/translations/cs.json +++ b/homeassistant/components/google_travel_time/translations/cs.json @@ -4,7 +4,8 @@ "already_configured": "Um\u00edst\u011bn\u00ed je ji\u017e nastaveno" }, "error": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" }, "step": { "user": { diff --git a/homeassistant/components/harmony/translations/select.ca.json b/homeassistant/components/harmony/translations/select.ca.json new file mode 100644 index 00000000000..2c11e9affa5 --- /dev/null +++ b/homeassistant/components/harmony/translations/select.ca.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Apaga" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.cs.json b/homeassistant/components/harmony/translations/select.cs.json new file mode 100644 index 00000000000..c3a5be2a9dc --- /dev/null +++ b/homeassistant/components/harmony/translations/select.cs.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Vypnout" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.nl.json b/homeassistant/components/harmony/translations/select.nl.json new file mode 100644 index 00000000000..48d4e0a8ff2 --- /dev/null +++ b/homeassistant/components/harmony/translations/select.nl.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Uitschakelen" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hassio/translations/cs.json b/homeassistant/components/hassio/translations/cs.json index 97f844a8c81..4c2b2e7eafb 100644 --- a/homeassistant/components/hassio/translations/cs.json +++ b/homeassistant/components/hassio/translations/cs.json @@ -1,4 +1,41 @@ { + "issues": { + "unsupported": { + "description": "Syst\u00e9m nen\u00ed podporov\u00e1n z {reason} . Pomoc\u00ed odkazu se dozv\u00edte v\u00edce a jak to opravit.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 {reason}" + }, + "unsupported_apparmor": { + "description": "Syst\u00e9m nen\u00ed podporov\u00e1n, proto\u017ee AppArmor nefunguje spr\u00e1vn\u011b a dopl\u0148ky b\u011b\u017e\u00ed nechr\u00e1n\u011bn\u00fdm a nezabezpe\u010den\u00fdm zp\u016fsobem. Pomoc\u00ed odkazu se dozv\u00edte v\u00edce a jak to opravit.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 probl\u00e9my s aplikac\u00ed AppArmor" + }, + "unsupported_cgroup_version": { + "description": "Syst\u00e9m nen\u00ed podporov\u00e1n, proto\u017ee se pou\u017e\u00edv\u00e1 nespr\u00e1vn\u00e1 verze Docker CGroup. Pomoc\u00ed odkazu se dozv\u00edte spr\u00e1vnou verzi a jak to opravit.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 verze CGroup" + }, + "unsupported_connectivity_check": { + "description": "Syst\u00e9m nen\u00ed podporov\u00e1n, proto\u017ee Home Assistant nem\u016f\u017ee ur\u010dit, kdy je dostupn\u00e9 p\u0159ipojen\u00ed k internetu. Pomoc\u00ed odkazu se dozv\u00edte v\u00edce a jak to opravit.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 Kontrola p\u0159ipojen\u00ed zak\u00e1z\u00e1na" + }, + "unsupported_content_trust": { + "description": "Syst\u00e9m nen\u00ed podporov\u00e1n, proto\u017ee Home Assistant nem\u016f\u017ee ov\u011b\u0159it, zda je spou\u0161t\u011bn\u00fd obsah d\u016fv\u011bryhodn\u00fd a nebyl \u00fato\u010dn\u00edky upraven. Pomoc\u00ed odkazu se dozv\u00edte v\u00edce a jak to opravit.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 Kontrola d\u016fv\u011bryhodnosti obsahu zak\u00e1z\u00e1na" + }, + "unsupported_dbus": { + "description": "Syst\u00e9m nen\u00ed podporov\u00e1n, proto\u017ee D-Bus nefunguje spr\u00e1vn\u011b. Mnoho v\u011bc\u00ed bez toho sel\u017ee, proto\u017ee supervizor nem\u016f\u017ee komunikovat s hostitelem. Pomoc\u00ed odkazu se dozv\u00edte v\u00edce a jak to opravit.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 probl\u00e9my s D-Bus" + }, + "unsupported_dns_server": { + "description": "Syst\u00e9m nen\u00ed podporov\u00e1n, proto\u017ee poskytnut\u00fd server DNS nefunguje spr\u00e1vn\u011b a z\u00e1lo\u017en\u00ed mo\u017enost DNS byla zak\u00e1z\u00e1na. Pomoc\u00ed odkazu se dozv\u00edte v\u00edce a jak to opravit.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 probl\u00e9my se serverem DNS" + }, + "unsupported_docker_configuration": { + "description": "Syst\u00e9m nen\u00ed podporov\u00e1n, proto\u017ee d\u00e9mon Docker b\u011b\u017e\u00ed neo\u010dek\u00e1van\u00fdm zp\u016fsobem. Pomoc\u00ed odkazu se dozv\u00edte v\u00edce a jak to opravit.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 Docker je \u0161patn\u011b nakonfigurov\u00e1n" + }, + "unsupported_systemd_resolved": { + "description": "Syst\u00e9m nen\u00ed podporov\u00e1n, proto\u017ee Systemd Resolved chyb\u00ed, je neaktivn\u00ed nebo je \u0161patn\u011b nakonfigurov\u00e1n. Pomoc\u00ed odkazu se dozv\u00edte v\u00edce a jak to opravit." + } + }, "system_health": { "info": { "board": "Deska", diff --git a/homeassistant/components/hive/translations/cs.json b/homeassistant/components/hive/translations/cs.json index 81e2a4b288e..85a259b8992 100644 --- a/homeassistant/components/hive/translations/cs.json +++ b/homeassistant/components/hive/translations/cs.json @@ -17,9 +17,19 @@ "user": { "data": { "password": "Heslo", + "scan_interval": "Interval skenov\u00e1n\u00ed (sekundy)", "username": "U\u017eivatelsk\u00e9 jm\u00e9no" } } } + }, + "options": { + "step": { + "user": { + "data": { + "scan_interval": "Interval skenov\u00e1n\u00ed (sekundy)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/homeassistant/translations/cs.json b/homeassistant/components/homeassistant/translations/cs.json index fbd96241e36..9aa26acbc2b 100644 --- a/homeassistant/components/homeassistant/translations/cs.json +++ b/homeassistant/components/homeassistant/translations/cs.json @@ -1,4 +1,14 @@ { + "issues": { + "historic_currency": { + "description": "M\u011bna {currency} se ji\u017e nepou\u017e\u00edv\u00e1, zm\u011b\u0148te pros\u00edm konfiguraci m\u011bny.", + "title": "Nakonfigurovan\u00e1 m\u011bna se ji\u017e nepou\u017e\u00edv\u00e1" + }, + "python_version": { + "description": "Podpora pro spou\u0161t\u011bn\u00ed Home Assistant v aktu\u00e1ln\u011b pou\u017e\u00edvan\u00e9 verzi Pythonu {current_python_version} je zastaral\u00e1 a bude odstran\u011bna v Home Assistant {breaks_in_ha_version}. Upgradujte pros\u00edm Python na {required_python_version}, abyste zabr\u00e1nili po\u0161kozen\u00ed instance Home Assistant.", + "title": "Podpora pro Python {current_python_version} se odstra\u0148uje" + } + }, "system_health": { "info": { "arch": "Architektura procesoru", diff --git a/homeassistant/components/homeassistant_yellow/translations/cs.json b/homeassistant/components/homeassistant_yellow/translations/cs.json new file mode 100644 index 00000000000..76899070cb1 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/cs.json @@ -0,0 +1,41 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Nepoda\u0159ilo se z\u00edskat informace o dopl\u0148ku Silicon Labs Multiprotocol.", + "addon_install_failed": "Nepoda\u0159ilo se nainstalovat dopln\u011bk Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Nepoda\u0159ilo se nastavit konfiguraci Silicon Labs Multiprotocol.", + "addon_start_failed": "Spu\u0161t\u011bn\u00ed dopl\u0148ku Silicon Labs Multiprotocol se nezda\u0159ilo.", + "not_hassio": "Mo\u017enosti hardwaru lze konfigurovat pouze v instalac\u00edch HassOS.", + "zha_migration_failed": "Migrace ZHA se nezda\u0159ila." + }, + "error": { + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + }, + "progress": { + "install_addon": "Po\u010dkejte pros\u00edm na dokon\u010den\u00ed instalace dopl\u0148ku Silicon Labs Multiprotocol. To m\u016f\u017ee trvat n\u011bkolik minut.", + "start_addon": "Po\u010dkejte pros\u00edm, ne\u017e se dokon\u010d\u00ed spu\u0161t\u011bn\u00ed dopl\u0148ku Silicon Labs Multiprotocol. To m\u016f\u017ee trvat n\u011bkolik sekund." + }, + "step": { + "addon_installed_other_device": { + "title": "Podpora v\u00edce protokol\u016f je ji\u017e povolena pro jin\u00e9 za\u0159\u00edzen\u00ed" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Povolit podporu v\u00edce protokol\u016f" + }, + "description": "Je-li povolena podpora v\u00edce protokol\u016f, lze r\u00e1dio IEEE 802.15.4 Home Assistant Yellow pou\u017e\u00edvat sou\u010dasn\u011b pro Zigbee i Thread (pou\u017e\u00edv\u00e1 Matter). Pokud je r\u00e1dio ji\u017e pou\u017e\u00edv\u00e1no integrac\u00ed ZHA Zigbee, bude ZHA p\u0159ekonfigurov\u00e1no pro pou\u017eit\u00ed multiprotokolov\u00e9ho firmwaru. \n\n Pozn\u00e1mka: Toto je experiment\u00e1ln\u00ed funkce.", + "title": "Povolit multiprotokolovou podporu na r\u00e1diu IEEE 802.15.4" + }, + "install_addon": { + "title": "Instalace dopl\u0148ku Silicon Labs Multiprotocol byla zah\u00e1jena" + }, + "show_revert_guide": { + "description": "Pokud chcete zm\u011bnit firmware pouze na Zigbee, prove\u010fte pros\u00edm n\u00e1sleduj\u00edc\u00ed ru\u010dn\u00ed kroky: \n\n * Odstra\u0148te dopln\u011bk Silicon Labs Multiprotocol \n\n * Flashujte pouze firmware Zigbee, postupujte podle n\u00e1vodu na https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * P\u0159ekonfigurujte ZHA pro migraci nastaven\u00ed do p\u0159eflashovan\u00e9ho r\u00e1dia", + "title": "Pro toto za\u0159\u00edzen\u00ed je povolena podpora v\u00edce protokol\u016f" + }, + "start_addon": { + "title": "Spou\u0161t\u00ed se dopln\u011bk Silicon Labs Multiprotocol." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/nl.json b/homeassistant/components/homeassistant_yellow/translations/nl.json index 879fd7717f6..2ac32361c06 100644 --- a/homeassistant/components/homeassistant_yellow/translations/nl.json +++ b/homeassistant/components/homeassistant_yellow/translations/nl.json @@ -1,5 +1,8 @@ { "options": { + "abort": { + "zha_migration_failed": "De ZHA migratie is niet gelukt." + }, "error": { "unknown": "Onverwachte fout" } diff --git a/homeassistant/components/huawei_lte/translations/cs.json b/homeassistant/components/huawei_lte/translations/cs.json index 7782b2fc622..e5518d722ca 100644 --- a/homeassistant/components/huawei_lte/translations/cs.json +++ b/homeassistant/components/huawei_lte/translations/cs.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "not_huawei_lte": "Nejedn\u00e1 se o za\u0159\u00edzen\u00ed Huawei LTE" + "not_huawei_lte": "Nejedn\u00e1 se o za\u0159\u00edzen\u00ed Huawei LTE", + "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "error": { "connection_timeout": "\u010casov\u00fd limit spojen\u00ed", @@ -15,6 +16,13 @@ }, "flow_title": "Huawei LTE: {name}", "step": { + "reauth_confirm": { + "data": { + "password": "Heslo", + "username": "U\u017eivatelsk\u00e9 jm\u00e9no" + }, + "title": "Znovu ov\u011b\u0159it integraci" + }, "user": { "data": { "password": "Heslo", diff --git a/homeassistant/components/ibeacon/translations/cs.json b/homeassistant/components/ibeacon/translations/cs.json new file mode 100644 index 00000000000..32cf458b0af --- /dev/null +++ b/homeassistant/components/ibeacon/translations/cs.json @@ -0,0 +1,12 @@ +{ + "options": { + "step": { + "init": { + "data": { + "min_rssi": "Minim\u00e1ln\u00ed RSSI" + }, + "description": "iBeacons s hodnotou RSSI ni\u017e\u0161\u00ed ne\u017e Minim\u00e1ln\u00ed RSSI budou ignorov\u00e1ny. Pokud integrace vid\u00ed sousedn\u00ed iBeacony, zv\u00fd\u0161en\u00ed t\u00e9to hodnoty m\u016f\u017ee pomoci." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/intellifire/translations/cs.json b/homeassistant/components/intellifire/translations/cs.json index 9ac5e9d9099..7f7a3e0bf58 100644 --- a/homeassistant/components/intellifire/translations/cs.json +++ b/homeassistant/components/intellifire/translations/cs.json @@ -5,6 +5,7 @@ "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "error": { + "api_error": "P\u0159ihl\u00e1\u0161en\u00ed selhalo", "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" }, "flow_title": "{serial} ({host})", diff --git a/homeassistant/components/isy994/translations/cs.json b/homeassistant/components/isy994/translations/cs.json index 45cd90895c7..cecdcde6bd3 100644 --- a/homeassistant/components/isy994/translations/cs.json +++ b/homeassistant/components/isy994/translations/cs.json @@ -10,6 +10,7 @@ "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, + "flow_title": "{name} ({host})", "step": { "reauth_confirm": { "data": { diff --git a/homeassistant/components/kegtron/translations/cs.json b/homeassistant/components/kegtron/translations/cs.json new file mode 100644 index 00000000000..1163b27775a --- /dev/null +++ b/homeassistant/components/kegtron/translations/cs.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", + "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", + "no_devices_found": "V s\u00edti nebyla nalezena \u017e\u00e1dn\u00e1 za\u0159\u00edzen\u00ed", + "not_supported": "Za\u0159\u00edzen\u00ed nen\u00ed podporov\u00e1no" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Chcete nastavit {name}?" + }, + "user": { + "data": { + "address": "Za\u0159\u00edzen\u00ed" + }, + "description": "Zvolte za\u0159\u00edzen\u00ed, kter\u00e9 chcete nastavit" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/keymitt_ble/translations/cs.json b/homeassistant/components/keymitt_ble/translations/cs.json new file mode 100644 index 00000000000..f0a8f0aab5a --- /dev/null +++ b/homeassistant/components/keymitt_ble/translations/cs.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "already_configured_device": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + }, + "flow_title": "{name}" + } +} \ No newline at end of file diff --git a/homeassistant/components/knx/translations/cs.json b/homeassistant/components/knx/translations/cs.json index e9e0a0d87f6..325e4710145 100644 --- a/homeassistant/components/knx/translations/cs.json +++ b/homeassistant/components/knx/translations/cs.json @@ -4,7 +4,8 @@ "already_configured": "Slu\u017eba je ji\u017e nastavena" }, "error": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "invalid_ip_address": "Neplatn\u00e1 adresa IPv4." }, "step": { "manual_tunnel": { @@ -12,6 +13,35 @@ "host": "Hostitel", "port": "Port" } + }, + "routing": { + "data_description": { + "individual_address": "Adresa KNX, kterou m\u00e1 pou\u017e\u00edvat Home Assistant, nap\u0159. `0.0.4`" + } + } + } + }, + "options": { + "error": { + "invalid_ip_address": "Neplatn\u00e1 adresa IPv4." + }, + "step": { + "manual_tunnel": { + "data": { + "host": "Hostitel", + "port": "Port" + } + }, + "options_init": { + "menu_options": { + "communication_settings": "Nastaven\u00ed komunikace", + "connection_type": "Konfigurace rozhran\u00ed KNX" + } + }, + "routing": { + "data_description": { + "individual_address": "Adresa KNX, kterou m\u00e1 pou\u017e\u00edvat Home Assistant, nap\u0159. `0.0.4`" + } } } } diff --git a/homeassistant/components/kraken/translations/cs.json b/homeassistant/components/kraken/translations/cs.json new file mode 100644 index 00000000000..7f5cce1ca73 --- /dev/null +++ b/homeassistant/components/kraken/translations/cs.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lametric/translations/cs.json b/homeassistant/components/lametric/translations/cs.json index 59280c6c2bc..3b3f849ac95 100644 --- a/homeassistant/components/lametric/translations/cs.json +++ b/homeassistant/components/lametric/translations/cs.json @@ -3,7 +3,8 @@ "abort": { "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", "authorize_url_timeout": "\u010casov\u00fd limit autoriza\u010dn\u00edho URL vypr\u0161el", - "no_url_available": "Nen\u00ed k dispozici \u017e\u00e1dn\u00e1 adresa URL. Informace o t\u00e9to chyb\u011b naleznete [v sekci n\u00e1pov\u011bdy]({docs_url})" + "no_url_available": "Nen\u00ed k dispozici \u017e\u00e1dn\u00e1 adresa URL. Informace o t\u00e9to chyb\u011b naleznete [v sekci n\u00e1pov\u011bdy]({docs_url})", + "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", diff --git a/homeassistant/components/led_ble/translations/cs.json b/homeassistant/components/led_ble/translations/cs.json index 99738ebc78e..ad445a240db 100644 --- a/homeassistant/components/led_ble/translations/cs.json +++ b/homeassistant/components/led_ble/translations/cs.json @@ -8,6 +8,13 @@ "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "address": "Bluetooth adresa" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/cs.json b/homeassistant/components/lidarr/translations/cs.json new file mode 100644 index 00000000000..6ef4d1eb7ac --- /dev/null +++ b/homeassistant/components/lidarr/translations/cs.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba je ji\u017e nastavena", + "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" + }, + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba", + "zeroconf_failed": "Kl\u00ed\u010d API nebyl nalezen. Zadejte jej pros\u00edm ru\u010dn\u011b" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Kl\u00ed\u010d API" + }, + "title": "Znovu ov\u011b\u0159it integraci" + }, + "user": { + "data": { + "api_key": "Kl\u00ed\u010d API", + "url": "URL", + "verify_ssl": "Ov\u011b\u0159it certifik\u00e1t SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/cs.json b/homeassistant/components/livisi/translations/cs.json new file mode 100644 index 00000000000..03bd102a2d4 --- /dev/null +++ b/homeassistant/components/livisi/translations/cs.json @@ -0,0 +1,16 @@ +{ + "config": { + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "wrong_password": "Heslo je nespr\u00e1vn\u00e9." + }, + "step": { + "user": { + "data": { + "host": "IP adresa", + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/cs.json b/homeassistant/components/mazda/translations/cs.json index c769fdc28dd..7e01ff38a0a 100644 --- a/homeassistant/components/mazda/translations/cs.json +++ b/homeassistant/components/mazda/translations/cs.json @@ -5,6 +5,7 @@ "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "error": { + "account_locked": "\u00da\u010det uzam\u010den. Pros\u00edm zkuste to znovu pozd\u011bji.", "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" diff --git a/homeassistant/components/min_max/translations/et.json b/homeassistant/components/min_max/translations/et.json index c57b4a055a8..e0b9e561aaf 100644 --- a/homeassistant/components/min_max/translations/et.json +++ b/homeassistant/components/min_max/translations/et.json @@ -9,10 +9,10 @@ "type": "Statistiline tunnus" }, "data_description": { - "round_digits": "M\u00e4\u00e4rab k\u00fcmnendkohtade arvu v\u00e4ljundis kui statistika tunnus on keskmine v\u00f5i mediaan." + "round_digits": "M\u00e4\u00e4rab k\u00fcmnendkohtade arvu v\u00e4ljundis kui statistika tunnus on keskmine, summa v\u00f5i mediaan." }, - "description": "Loo andur mis arvutab sisendandurite loendist minimaalse, maksimaalse, keskmise v\u00f5i mediaanv\u00e4\u00e4rtuse.", - "title": "Lisa min / max / keskmine / mediaanandur" + "description": "Loo andur mis arvutab sisendandurite loendist minimaalse, maksimaalse, keskmise, summa v\u00f5i mediaanv\u00e4\u00e4rtuse.", + "title": "\u00dchenda mitme anduri olek" } } }, @@ -30,5 +30,5 @@ } } }, - "title": "Min / max / keskmine / mediaanandur" + "title": "\u00dchenda mitme anduri olek" } \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/ru.json b/homeassistant/components/min_max/translations/ru.json index 0fdd9198a72..dab8747e1ae 100644 --- a/homeassistant/components/min_max/translations/ru.json +++ b/homeassistant/components/min_max/translations/ru.json @@ -9,10 +9,10 @@ "type": "\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430" }, "data_description": { - "round_digits": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439, \u043a\u043e\u0433\u0434\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0440\u0435\u0434\u043d\u0435\u0439 \u0438\u043b\u0438 \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0439." + "round_digits": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439, \u043a\u043e\u0433\u0434\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0440\u0435\u0434\u043d\u0435\u0439, \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0439 \u0438\u043b\u0438 \u0441\u0443\u043c\u043c\u043e\u0439." }, - "description": "\u0412\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435, \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435, \u0441\u0440\u0435\u0434\u043d\u0435\u0435 \u0438\u043b\u0438 \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432.", - "title": "\u041c\u0438\u043d\u0438\u043c\u0443\u043c / \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c / \u0441\u0440\u0435\u0434\u043d\u0435\u0435 / \u043c\u0435\u0434\u0438\u0430\u043d\u0430" + "description": "\u0412\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442 \u0441\u0443\u043c\u043c\u0443, \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435, \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435, \u0441\u0440\u0435\u0434\u043d\u0435\u0435 \u0438\u043b\u0438 \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432.", + "title": "\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432" } } }, @@ -25,10 +25,10 @@ "type": "\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430" }, "data_description": { - "round_digits": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439, \u043a\u043e\u0433\u0434\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0440\u0435\u0434\u043d\u0435\u0439 \u0438\u043b\u0438 \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0439." + "round_digits": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439, \u043a\u043e\u0433\u0434\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0440\u0435\u0434\u043d\u0435\u0439, \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0439 \u0438\u043b\u0438 \u0441\u0443\u043c\u043c\u043e\u0439." } } } }, - "title": "\u041d\u043e\u0432\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440" + "title": "\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432" } \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/zh-Hant.json b/homeassistant/components/min_max/translations/zh-Hant.json index abdf3fe737f..628e2458c65 100644 --- a/homeassistant/components/min_max/translations/zh-Hant.json +++ b/homeassistant/components/min_max/translations/zh-Hant.json @@ -9,10 +9,10 @@ "type": "\u7d71\u8a08\u7279\u5fb5" }, "data_description": { - "round_digits": "\u7576\u7d71\u8a08\u7279\u5fb5\u70ba\u5e73\u5747\u503c\u6216\u4e2d\u503c\u6642\u3001\u63a7\u5236\u8f38\u51fa\u5c0f\u6578\u4f4d\u6578\u3002" + "round_digits": "\u7576\u7d71\u8a08\u7279\u5fb5\u70ba\u5e73\u5747\u503c\u3001\u4e2d\u503c\u6216\u7e3d\u5408\u6642\u3001\u63a7\u5236\u8f38\u51fa\u5c0f\u6578\u4f4d\u6578\u3002" }, - "description": "\u65b0\u589e\u81ea\u8f38\u5165\u611f\u6e2c\u5668\u4e86\u8868\u4e2d\uff0c\u8a08\u7b97\u6700\u4f4e\u3001\u6700\u9ad8\u3001\u5e73\u5747\u503c\u6216\u4e2d\u503c\u611f\u6e2c\u5668\u3002", - "title": "\u65b0\u589e\u6700\u5c0f\u503c / \u6700\u5927\u503c / \u5e73\u5747\u503c / \u4e2d\u503c\u611f\u6e2c\u5668" + "description": "\u65b0\u589e\u81ea\u8f38\u5165\u611f\u6e2c\u5668\u4e86\u8868\u4e2d\uff0c\u8a08\u7b97\u6700\u4f4e\u3001\u6700\u9ad8\u3001\u5e73\u5747\u503c\u3001\u4e2d\u503c\u6216\u7e3d\u5408\u611f\u6e2c\u5668\u3002", + "title": "\u7d50\u5408\u591a\u500b\u611f\u6e2c\u5668\u72c0\u614b" } } }, @@ -25,10 +25,10 @@ "type": "\u7d71\u8a08\u7279\u5fb5" }, "data_description": { - "round_digits": "\u7576\u7d71\u8a08\u7279\u5fb5\u70ba\u5e73\u5747\u503c\u6216\u4e2d\u503c\u6642\u3001\u63a7\u5236\u8f38\u51fa\u5c0f\u6578\u4f4d\u6578\u3002" + "round_digits": "\u7576\u7d71\u8a08\u7279\u5fb5\u70ba\u5e73\u5747\u503c\u3001\u4e2d\u503c\u6216\u7e3d\u5408\u6642\u3001\u63a7\u5236\u8f38\u51fa\u5c0f\u6578\u4f4d\u6578\u3002" } } } }, - "title": "\u6700\u5c0f\u503c / \u6700\u5927\u503c / \u5e73\u5747\u503c / \u4e2d\u503c\u611f\u6e2c\u5668" + "title": "\u7d50\u5408\u591a\u500b\u611f\u6e2c\u5668\u72c0\u614b" } \ No newline at end of file diff --git a/homeassistant/components/mqtt/translations/cs.json b/homeassistant/components/mqtt/translations/cs.json index 347a560b52a..3aeae56f56e 100644 --- a/homeassistant/components/mqtt/translations/cs.json +++ b/homeassistant/components/mqtt/translations/cs.json @@ -5,15 +5,23 @@ "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." }, "error": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" + "bad_certificate": "Certifik\u00e1t CA je neplatn\u00fd", + "bad_client_cert_key": "Klientsk\u00fd certifik\u00e1t a soukrom\u00fd nejsou platn\u00fd p\u00e1r", + "bad_client_key": "Neplatn\u00fd soukrom\u00fd kl\u00ed\u010d, zajist\u011bte, aby byl soubor s k\u00f3dem PEM dod\u00e1n bez hesla", + "bad_ws_headers": "Zadejte platn\u00e9 HTTP hlavi\u010dky jako JSON objekt", + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "invalid_inclusion": "Klientsk\u00fd certifik\u00e1t a soukrom\u00fd kl\u00ed\u010d mus\u00ed b\u00fdt nakonfigurov\u00e1ny spole\u010dn\u011b" }, "step": { "broker": { "data": { + "advanced_options": "Pokro\u010dil\u00e9 mo\u017enosti", "broker": "Broker", "password": "Heslo", "port": "Port", - "username": "U\u017eivatelsk\u00e9 jm\u00e9no" + "username": "U\u017eivatelsk\u00e9 jm\u00e9no", + "ws_headers": "Hlavi\u010dky WebSocket ve form\u00e1tu JSON", + "ws_path": "Cesta WebSocketu" }, "description": "Zadejte informace proo p\u0159ipojen\u00ed zprost\u0159edkovatele protokolu MQTT." }, @@ -49,6 +57,7 @@ }, "options": { "error": { + "bad_ws_headers": "Zadejte platn\u00e9 HTTP hlavi\u010dky jako JSON objekt", "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" }, "step": { @@ -56,7 +65,9 @@ "data": { "password": "Heslo", "port": "Port", - "username": "U\u017eivatelsk\u00e9 jm\u00e9no" + "username": "U\u017eivatelsk\u00e9 jm\u00e9no", + "ws_headers": "Hlavi\u010dky WebSocket ve form\u00e1tu JSON", + "ws_path": "Cesta WebSocketu" }, "description": "Zadejte informace proo p\u0159ipojen\u00ed zprost\u0159edkovatele protokolu MQTT." }, diff --git a/homeassistant/components/mqtt/translations/en.json b/homeassistant/components/mqtt/translations/en.json index dd3b69ec2f4..1f092dfdc96 100644 --- a/homeassistant/components/mqtt/translations/en.json +++ b/homeassistant/components/mqtt/translations/en.json @@ -85,7 +85,7 @@ "bad_birth": "Invalid birth topic", "bad_certificate": "The CA certificate is invalid", "bad_client_cert": "Invalid client certificate, ensure a PEM coded file is supplied", - "bad_client_cert_key": "Client certificate and private are no valid pair", + "bad_client_cert_key": "Client certificate and private key are not a valid pair", "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", "bad_discovery_prefix": "Invalid discovery prefix", "bad_will": "Invalid will topic", diff --git a/homeassistant/components/mqtt/translations/nl.json b/homeassistant/components/mqtt/translations/nl.json index 8f25e3826b7..7fe65efd7cf 100644 --- a/homeassistant/components/mqtt/translations/nl.json +++ b/homeassistant/components/mqtt/translations/nl.json @@ -12,6 +12,7 @@ "bad_client_key": "Ongeldige priv\u00e9sleutel (private key), zorg voor een PEM gecodeerd bestand zonder wachtwoord", "bad_discovery_prefix": "Ongeldig discovery voorvoegsel", "bad_will": "Ongeldig `will` topic", + "bad_ws_headers": "Geef geldige HTTP-headers op als een JSON-object", "cannot_connect": "Kan geen verbinding maken", "invalid_inclusion": "Het client-certificaat en priv\u00e9sleutel moeten samen worden geconfigureerd" }, @@ -20,7 +21,10 @@ "data": { "advanced_options": "Geavanceerde opties", "broker": "Broker", + "certificate": "Aangepast CA certificaatbestand uploaden", + "client_cert": "Clientcertificaatbestand uploaden", "client_id": "Client ID (leeg laten voor een willekeurig ID)", + "client_key": "Priv\u00e9sleutelbestand bestand uploaden", "keepalive": "Tijd tussen het verzenden van keep-a-live berichten", "password": "Wachtwoord", "port": "Poort", @@ -28,7 +32,10 @@ "set_ca_cert": "Broker certificaatvalidatie", "set_client_cert": "Gebruik een client-certificaat", "tls_insecure": "Negeer validatie van brokercertificaten", - "username": "Gebruikersnaam" + "transport": "MQTT transport", + "username": "Gebruikersnaam", + "ws_headers": "HTTP-headers in JSON-object formaat", + "ws_path": "WebSocket pad" }, "description": "MQTT" }, @@ -69,7 +76,7 @@ "title": "Handmatig geconfigureerd platform {platform}(s) vereist aandacht" }, "deprecated_yaml_broker_settings": { - "description": "De volgende instellingen gevonden in `configuration.yaml` zijn gemigreerd naar de MQTT configuratieinstellingen en overschrijven nu de instellingen in `configuration.yaml`:\n`{deprecated_settings}`\n\nVerwijderd deze instellingen van `configuration.yaml` en herstart Home Assistant om dit probleem op te lossen. Zie de [documentatie]({more_info_url}), voor meer informatie.", + "description": "De volgende instellingen gevonden in `configuration.yaml` zijn gemigreerd naar de MQTT configuratieinstellingen en overschrijven nu de instellingen in `configuration.yaml`:\n`{deprecated_settings}`\n\nVerwijder deze instellingen van `configuration.yaml` en herstart Home Assistant om dit probleem op te lossen. Zie de [documentatie]({more_info_url}), voor meer informatie.", "title": "Verouderde MQTT instellingen gevonden in `configuration.yaml`" } }, @@ -82,6 +89,7 @@ "bad_client_key": "Ongeldige priv\u00e9sleutel (private key), zorg voor een PEM gecodeerd bestand zonder wachtwoord", "bad_discovery_prefix": "Ongeldig discovery voorvoegsel", "bad_will": "Ongeldig will topic", + "bad_ws_headers": "Geef geldige HTTP-headers op als een JSON-object", "cannot_connect": "Kan geen verbinding maken", "invalid_inclusion": "Het client-certificaat en priv\u00e9sleutel moeten samen worden geconfigureerd" }, @@ -101,7 +109,10 @@ "set_ca_cert": "Broker certificaatvalidatie", "set_client_cert": "Gebruik een client-certificaat", "tls_insecure": "Negeer validatie van brokercertificaten", - "username": "Gebruikersnaam" + "transport": "MQTT transport", + "username": "Gebruikersnaam", + "ws_headers": "WebSocket headers in JSON formaat", + "ws_path": "WebSocket pad" }, "description": "Voer de verbindingsgegevens van uw MQTT-broker in.", "title": "Broker opties" diff --git a/homeassistant/components/netgear/translations/cs.json b/homeassistant/components/netgear/translations/cs.json index 67e2611aa87..e96fd714d69 100644 --- a/homeassistant/components/netgear/translations/cs.json +++ b/homeassistant/components/netgear/translations/cs.json @@ -9,7 +9,8 @@ "host": "Hostitel (nepovinn\u00fd)", "password": "Heslo", "username": "U\u017eivatelsk\u00e9 jm\u00e9no (nepovinn\u00e9)" - } + }, + "description": "V\u00fdchoz\u00ed hostitel: {host}\nV\u00fdchoz\u00ed u\u017eivatelsk\u00e9 jm\u00e9no: {username}" } } } diff --git a/homeassistant/components/nextdns/translations/nl.json b/homeassistant/components/nextdns/translations/nl.json index 436e1a68b7d..73a61b8a34d 100644 --- a/homeassistant/components/nextdns/translations/nl.json +++ b/homeassistant/components/nextdns/translations/nl.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Dit NextDNS profiel is al geconfigureerd." + }, "error": { "cannot_connect": "Kan geen verbinding maken", "invalid_api_key": "Ongeldige API-sleutel", @@ -17,5 +20,10 @@ } } } + }, + "system_health": { + "info": { + "can_reach_server": "Server bereikbaar" + } } } \ No newline at end of file diff --git a/homeassistant/components/nibe_heatpump/translations/cs.json b/homeassistant/components/nibe_heatpump/translations/cs.json new file mode 100644 index 00000000000..5c7a625847f --- /dev/null +++ b/homeassistant/components/nibe_heatpump/translations/cs.json @@ -0,0 +1,19 @@ +{ + "config": { + "error": { + "url": "Zadan\u00e1 adresa URL nen\u00ed spr\u00e1vn\u011b zad\u00e1na ani podporov\u00e1na" + }, + "step": { + "modbus": { + "data": { + "modbus_url": "Modbus URL" + } + }, + "nibegw": { + "data": { + "ip_address": "Vzd\u00e1len\u00e1 adresa" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nmap_tracker/translations/cs.json b/homeassistant/components/nmap_tracker/translations/cs.json index 95f0bfc3ae8..093da4eff30 100644 --- a/homeassistant/components/nmap_tracker/translations/cs.json +++ b/homeassistant/components/nmap_tracker/translations/cs.json @@ -2,12 +2,20 @@ "config": { "abort": { "already_configured": "Um\u00edst\u011bn\u00ed je ji\u017e nastaveno" + }, + "step": { + "user": { + "data": { + "hosts": "S\u00ed\u0165ov\u00e9 adresy (odd\u011blen\u00e9 \u010d\u00e1rkami), kter\u00e9 se maj\u00ed skenovat" + } + } } }, "options": { "step": { "init": { "data": { + "hosts": "S\u00ed\u0165ov\u00e9 adresy (odd\u011blen\u00e9 \u010d\u00e1rkami), kter\u00e9 se maj\u00ed skenovat", "interval_seconds": "Interval skenov\u00e1n\u00ed" } } diff --git a/homeassistant/components/onvif/translations/cs.json b/homeassistant/components/onvif/translations/cs.json index 100c4eb3788..ebd4166392f 100644 --- a/homeassistant/components/onvif/translations/cs.json +++ b/homeassistant/components/onvif/translations/cs.json @@ -43,7 +43,8 @@ "step": { "onvif_devices": { "data": { - "extra_arguments": "Dal\u0161\u00ed FFMPEG argumenty" + "extra_arguments": "Dal\u0161\u00ed FFMPEG argumenty", + "use_wallclock_as_timestamps": "Pou\u017eijte n\u00e1st\u011bnn\u00e9 hodiny jako \u010dasov\u00e1 raz\u00edtka" }, "title": "Mo\u017enosti za\u0159\u00edzen\u00ed ONVIF" } diff --git a/homeassistant/components/oralb/translations/cs.json b/homeassistant/components/oralb/translations/cs.json new file mode 100644 index 00000000000..1163b27775a --- /dev/null +++ b/homeassistant/components/oralb/translations/cs.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", + "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", + "no_devices_found": "V s\u00edti nebyla nalezena \u017e\u00e1dn\u00e1 za\u0159\u00edzen\u00ed", + "not_supported": "Za\u0159\u00edzen\u00ed nen\u00ed podporov\u00e1no" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Chcete nastavit {name}?" + }, + "user": { + "data": { + "address": "Za\u0159\u00edzen\u00ed" + }, + "description": "Zvolte za\u0159\u00edzen\u00ed, kter\u00e9 chcete nastavit" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/cs.json b/homeassistant/components/overkiz/translations/cs.json index 95baa636720..e82a5515f96 100644 --- a/homeassistant/components/overkiz/translations/cs.json +++ b/homeassistant/components/overkiz/translations/cs.json @@ -7,7 +7,8 @@ "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba", + "unsupported_hardware": "V\u00e1\u0161 hardware {unsupported_device} nen\u00ed touto integrac\u00ed podporov\u00e1n." }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/bg.json b/homeassistant/components/plugwise/translations/bg.json index 23d635d2b14..c74eb0a71ac 100644 --- a/homeassistant/components/plugwise/translations/bg.json +++ b/homeassistant/components/plugwise/translations/bg.json @@ -6,7 +6,8 @@ "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", - "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430", + "unsupported": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0441 \u043d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d \u0444\u044a\u0440\u043c\u0443\u0435\u0440" }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/ca.json b/homeassistant/components/plugwise/translations/ca.json index 47aa93725ea..b018a764124 100644 --- a/homeassistant/components/plugwise/translations/ca.json +++ b/homeassistant/components/plugwise/translations/ca.json @@ -8,7 +8,8 @@ "cannot_connect": "Ha fallat la connexi\u00f3", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", "invalid_setup": "Afegeix l'Adam en lloc de l'Anna; consulta la documentaci\u00f3 de la integraci\u00f3 Plugwise de Home Assistant per a m\u00e9s informaci\u00f3.", - "unknown": "Error inesperat" + "unknown": "Error inesperat", + "unsupported": "Dispositiu amb programari no compatible" }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/cs.json b/homeassistant/components/plugwise/translations/cs.json index 201c2135722..02a806f4548 100644 --- a/homeassistant/components/plugwise/translations/cs.json +++ b/homeassistant/components/plugwise/translations/cs.json @@ -6,7 +6,10 @@ "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + "invalid_setup": "P\u0159idejte sv\u00e9ho Adama m\u00edsto sv\u00e9 Anny, viz dokumentace", + "response_error": "Byla p\u0159ijata neplatn\u00e1 data XML nebo indikace chyby", + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba", + "unsupported": "Za\u0159\u00edzen\u00ed s nepodporovan\u00fdm firmwarem" }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/de.json b/homeassistant/components/plugwise/translations/de.json index 987ac94e91b..6aeba3e5605 100644 --- a/homeassistant/components/plugwise/translations/de.json +++ b/homeassistant/components/plugwise/translations/de.json @@ -7,8 +7,10 @@ "error": { "cannot_connect": "Verbindung fehlgeschlagen", "invalid_auth": "Ung\u00fcltige Authentifizierung", - "invalid_setup": "F\u00fcge deinen Adam anstelle deiner Anna hinzu. Weitere Informationen findest du in der Dokumentation zur Integration von Home Assistant Plugwise.", - "unknown": "Unerwarteter Fehler" + "invalid_setup": "F\u00fcge deinen Adam anstelle deiner Anna hinzu, siehe Dokumentation.", + "response_error": "Ung\u00fcltige XML-Daten oder Fehleranzeige empfangen", + "unknown": "Unerwarteter Fehler", + "unsupported": "Ger\u00e4t mit nicht unterst\u00fctzter Firmware" }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/el.json b/homeassistant/components/plugwise/translations/el.json index 2f15147e674..11907c3bd03 100644 --- a/homeassistant/components/plugwise/translations/el.json +++ b/homeassistant/components/plugwise/translations/el.json @@ -8,7 +8,9 @@ "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "invalid_setup": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd Adam \u03c3\u03b1\u03c2 \u03b1\u03bd\u03c4\u03af \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd Anna \u03c3\u03b1\u03c2, \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Home Assistant Plugwise \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2", - "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + "response_error": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b1 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 XML \u03ae \u03bb\u03b7\u03c6\u03b8\u03b5\u03af\u03c3\u03b1 \u03ad\u03bd\u03b4\u03b5\u03b9\u03be\u03b7 \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1\u03c4\u03bf\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1", + "unsupported": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03bc\u03b5 \u03bc\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc" }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/es.json b/homeassistant/components/plugwise/translations/es.json index 7a0cd53122b..9797cffa43c 100644 --- a/homeassistant/components/plugwise/translations/es.json +++ b/homeassistant/components/plugwise/translations/es.json @@ -7,8 +7,10 @@ "error": { "cannot_connect": "No se pudo conectar", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", - "invalid_setup": "A\u00f1ade tu Adam en lugar de tu Anna, consulta la documentaci\u00f3n de la integraci\u00f3n Home Assistant Plugwise para m\u00e1s informaci\u00f3n", - "unknown": "Error inesperado" + "invalid_setup": "A\u00f1ade tu Adam en lugar de tu Anna, consulta la documentaci\u00f3n", + "response_error": "Datos XML no v\u00e1lidos o indicaci\u00f3n de error recibida", + "unknown": "Error inesperado", + "unsupported": "Dispositivo con firmware no compatible" }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/et.json b/homeassistant/components/plugwise/translations/et.json index 53ec22d53ab..3ed7329fbb1 100644 --- a/homeassistant/components/plugwise/translations/et.json +++ b/homeassistant/components/plugwise/translations/et.json @@ -7,8 +7,10 @@ "error": { "cannot_connect": "\u00dchendamine nurjus", "invalid_auth": "Tuvastamine nurjus", - "invalid_setup": "Lisa oma Anna asemel oma Adam, lisateabe saamiseks vaata Home Assistant Plugwise'i sidumise dokumentatsiooni", - "unknown": "Tundmatu viga" + "invalid_setup": "Lisa oma Anna asemel oma Adam, lisateabe saamiseks vaata dokumentatsiooni", + "response_error": "Vigased XML-andmed v\u00f5i saadud veam\u00e4rguanne", + "unknown": "Tundmatu viga", + "unsupported": "Toetamata p\u00fcsivaraga seade" }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/nl.json b/homeassistant/components/plugwise/translations/nl.json index b6417aed1ae..5a7e0dc687f 100644 --- a/homeassistant/components/plugwise/translations/nl.json +++ b/homeassistant/components/plugwise/translations/nl.json @@ -7,7 +7,9 @@ "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "invalid_setup": "Voeg je Adam toe in plaats van je Anna, zie de Home Assistant Plugwise integratiedocumentatie voor meer informatie", - "unknown": "Onverwachte fout" + "response_error": "Ongeldige XML-gegevens, of foutindicatie ontvangen", + "unknown": "Onverwachte fout", + "unsupported": "Apparaat met niet ondersteunde firmware" }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/ru.json b/homeassistant/components/plugwise/translations/ru.json index d1a631af4bd..21b3c00a99a 100644 --- a/homeassistant/components/plugwise/translations/ru.json +++ b/homeassistant/components/plugwise/translations/ru.json @@ -7,8 +7,10 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", - "invalid_setup": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 Adam \u0432\u043c\u0435\u0441\u0442\u043e Anna. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439 \u043f\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 Plugwise \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + "invalid_setup": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 Adam \u0432\u043c\u0435\u0441\u0442\u043e Anna. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439 \u043f\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.", + "response_error": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 XML, \u0438\u043b\u0438 \u0438\u043d\u0434\u0438\u043a\u0430\u0446\u0438\u044f \u043e\u0448\u0438\u0431\u043a\u0438.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430.", + "unsupported": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0441 \u043d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u043e\u0439 \u043f\u0440\u043e\u0448\u0438\u0432\u043a\u043e\u0439." }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/select.cs.json b/homeassistant/components/plugwise/translations/select.cs.json new file mode 100644 index 00000000000..2eef80268c6 --- /dev/null +++ b/homeassistant/components/plugwise/translations/select.cs.json @@ -0,0 +1,7 @@ +{ + "state": { + "plugwise__dhw_mode": { + "off": "Vypnuto" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/zh-Hant.json b/homeassistant/components/plugwise/translations/zh-Hant.json index 7e43aaffbb0..07ebe6e64ef 100644 --- a/homeassistant/components/plugwise/translations/zh-Hant.json +++ b/homeassistant/components/plugwise/translations/zh-Hant.json @@ -7,8 +7,10 @@ "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", - "invalid_setup": "\u65b0\u589e Adam \u800c\u975e Anna\u3001\u8acb\u53c3\u95b1 Home Assistant Plugwise \u6574\u5408\u8aaa\u660e\u6587\u4ef6\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u6599", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + "invalid_setup": "\u65b0\u589e Adam \u800c\u975e Anna\u3001\u8acb\u53c3\u95b1\u8aaa\u660e\u6587\u4ef6", + "response_error": "XML \u8cc7\u6599\u7121\u6548\u3001\u6216\u63a5\u6536\u5230\u932f\u8aa4\u6307\u793a", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4", + "unsupported": "\u88dd\u7f6e\u4f7f\u7528\u7684\u97cc\u9ad4\u4e0d\u652f\u63f4" }, "step": { "user": { diff --git a/homeassistant/components/pushbullet/translations/cs.json b/homeassistant/components/pushbullet/translations/cs.json new file mode 100644 index 00000000000..9b4579440a4 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/cs.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba je ji\u017e nastavena" + }, + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "invalid_api_key": "Neplatn\u00fd kl\u00ed\u010d API" + }, + "step": { + "user": { + "data": { + "api_key": "Kl\u00ed\u010d API", + "name": "Jm\u00e9no" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfigurace Pushbullet pomoc\u00ed YAML se odstra\u0148uje. \n\nVa\u0161e st\u00e1vaj\u00edc\u00ed konfigurace YAML byla importov\u00e1na do u\u017eivatelsk\u00e9ho rozhran\u00ed automaticky. \n\nOdstra\u0148te konfiguraci Pushbullet YAML ze souboru configuration.yaml a restartujte Home Assistant, abyste tento probl\u00e9m vy\u0159e\u0161ili.", + "title": "Konfigurace Pushbullet YAML se odstra\u0148uje" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/cs.json b/homeassistant/components/pushover/translations/cs.json index 55a2608b01f..5435b116411 100644 --- a/homeassistant/components/pushover/translations/cs.json +++ b/homeassistant/components/pushover/translations/cs.json @@ -22,5 +22,10 @@ } } } + }, + "issues": { + "removed_yaml": { + "title": "Konfigurace Pushover YAML byla odstran\u011bna" + } } } \ No newline at end of file diff --git a/homeassistant/components/radarr/translations/bg.json b/homeassistant/components/radarr/translations/bg.json index 4ceaa5793e1..070befea137 100644 --- a/homeassistant/components/radarr/translations/bg.json +++ b/homeassistant/components/radarr/translations/bg.json @@ -26,6 +26,10 @@ "deprecated_yaml": { "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Radarr \u0441 \u043f\u043e\u043c\u043e\u0449\u0442\u0430 \u043d\u0430 YAML \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430.\n\n\u0412\u0430\u0448\u0430\u0442\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0430\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0432 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u041f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Radarr \u043e\u0442 \u0432\u0430\u0448\u0438\u044f \u0444\u0430\u0439\u043b configuration.yaml \u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant, \u0437\u0430 \u0434\u0430 \u043a\u043e\u0440\u0438\u0433\u0438\u0440\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.", "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Radarr \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430" + }, + "removed_yaml": { + "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Radarr \u0441 \u043f\u043e\u043c\u043e\u0449\u0442\u0430 \u043d\u0430 YAML \u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u043e.\n\n\u0412\u0430\u0448\u0430\u0442\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u043e\u0442 Home Assistant.\n\n\u041f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043e\u0442 \u0432\u0430\u0448\u0438\u044f \u0444\u0430\u0439\u043b configuration.yaml \u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant, \u0437\u0430 \u0434\u0430 \u043a\u043e\u0440\u0438\u0433\u0438\u0440\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.", + "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Radarr \u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u0430" } }, "options": { diff --git a/homeassistant/components/radarr/translations/cs.json b/homeassistant/components/radarr/translations/cs.json new file mode 100644 index 00000000000..08b98b728ff --- /dev/null +++ b/homeassistant/components/radarr/translations/cs.json @@ -0,0 +1,44 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba je ji\u017e nastavena", + "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" + }, + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba", + "zeroconf_failed": "Kl\u00ed\u010d API nenalezen. Zadejte jej pros\u00edm ru\u010dn\u011b" + }, + "step": { + "reauth_confirm": { + "title": "Znovu ov\u011b\u0159it integraci" + }, + "user": { + "data": { + "api_key": "Kl\u00ed\u010d API", + "url": "URL", + "verify_ssl": "Ov\u011b\u0159it certifik\u00e1t SSL" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "title": "Konfigurace Radarr YAML se odstra\u0148uje" + }, + "removed_yaml": { + "description": "Konfigurace Radaru pomoc\u00ed YAML byla odstran\u011bna. \n\nVa\u0161i st\u00e1vaj\u00edc\u00ed konfigurace YAML nen\u00ed vyu\u017eita Home Assistantem. \n\nOdstra\u0148te konfiguraci YAML ze souboru configuration.yaml a restartujte Home Assistant, abyste se tento probl\u00e9m vy\u0159e\u0161il.", + "title": "Konfigurace Radarr YAML byla odstran\u011bna" + } + }, + "options": { + "step": { + "init": { + "data": { + "upcoming_days": "Po\u010det nadch\u00e1zej\u00edc\u00edch dn\u016f k zobrazen\u00ed" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/radarr/translations/de.json b/homeassistant/components/radarr/translations/de.json index 609af8288c6..33ce6eeba68 100644 --- a/homeassistant/components/radarr/translations/de.json +++ b/homeassistant/components/radarr/translations/de.json @@ -30,6 +30,10 @@ "deprecated_yaml": { "description": "Die Konfiguration von Radarr mit YAML wird entfernt. \n\nDeine vorhandene YAML-Konfiguration wurde automatisch in die Benutzeroberfl\u00e4che importiert. \n\nEntferne die Radarr-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", "title": "Die Radarr-YAML-Konfiguration wird entfernt" + }, + "removed_yaml": { + "description": "Die Konfiguration von Radarr mittels YAML wurde entfernt.\n\nIhre bestehende YAML-Konfiguration wird vom Home Assistant nicht verwendet.\n\nEntfernen Sie die YAML-Konfiguration aus Ihrer configuration.yaml Datei und starten Sie Home Assistant neu, um dieses Problem zu beheben.", + "title": "Die Radarr YAML-Konfiguration wurde entfernt" } }, "options": { diff --git a/homeassistant/components/radarr/translations/et.json b/homeassistant/components/radarr/translations/et.json index 175628112e8..64c48f67220 100644 --- a/homeassistant/components/radarr/translations/et.json +++ b/homeassistant/components/radarr/translations/et.json @@ -30,6 +30,10 @@ "deprecated_yaml": { "description": "Radarri seadistamine YAML-i abil eemaldatakse.\n\nTeie olemasolev YAML-i konfiguratsioon imporditakse kasutajaliidesesse automaatselt.\n\nEemaldage failist configuration.yaml radarr YAML konfiguratsioon ja taask\u00e4ivitage selle probleemi lahendamiseks Home Assistant.", "title": "Radarr YAML-i konfiguratsiooni eemaldatakse" + }, + "removed_yaml": { + "description": "Radarri konfigureerimine YAML-i abil on eemaldatud.\n\nTeie olemasolevat YAML-konfiguratsiooni ei kasuta Home Assistant.\n\nProbleemi lahendamiseks eemaldage YAML-konfiguratsioon failist configuration.yaml ja k\u00e4ivitage Home Assistant uuesti.", + "title": "Radarr YAML-i konfiguratsioon on eemaldatud" } }, "options": { diff --git a/homeassistant/components/radarr/translations/nl.json b/homeassistant/components/radarr/translations/nl.json index 10df421d00e..0047e41fe62 100644 --- a/homeassistant/components/radarr/translations/nl.json +++ b/homeassistant/components/radarr/translations/nl.json @@ -26,6 +26,9 @@ "issues": { "deprecated_yaml": { "title": "De Radarr YAML-configuratie wordt verwijderd" + }, + "removed_yaml": { + "title": "De Radarr YAML configuratie is verwijderd" } } } \ No newline at end of file diff --git a/homeassistant/components/radarr/translations/ru.json b/homeassistant/components/radarr/translations/ru.json index 05b6ff2b2dc..7013a6e3a04 100644 --- a/homeassistant/components/radarr/translations/ru.json +++ b/homeassistant/components/radarr/translations/ru.json @@ -30,6 +30,10 @@ "deprecated_yaml": { "description": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Radarr \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", "title": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Radarr \u0447\u0435\u0440\u0435\u0437 YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" + }, + "removed_yaml": { + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \"Radarr\" \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Home Assistant. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", + "title": "\u0423\u0434\u0430\u043b\u0435\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Radarr \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML" } }, "options": { diff --git a/homeassistant/components/radarr/translations/zh-Hant.json b/homeassistant/components/radarr/translations/zh-Hant.json index 694f26b7bf2..1c0275c23bf 100644 --- a/homeassistant/components/radarr/translations/zh-Hant.json +++ b/homeassistant/components/radarr/translations/zh-Hant.json @@ -30,6 +30,10 @@ "deprecated_yaml": { "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a\u7684 Radarr \u5373\u5c07\u9032\u884c\u79fb\u9664\u3002\n\n\u65e2\u6709\u7684 YAML \u8a2d\u5b9a\u5c07\u81ea\u52d5\u532f\u5165\u81f3 UI \u5167\u3002\n\n\u8acb\u65bc configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Radarr YAML \u8a2d\u5b9a\u4e26\u91cd\u65b0\u555f\u52d5 Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", "title": "Radarr YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" + }, + "removed_yaml": { + "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a Radarr \u7684\u529f\u80fd\u5373\u5c07\u79fb\u9664\u3002\n\nHome Assistant \u5c07\u4e0d\u518d\u4f7f\u7528\u73fe\u6709\u7684 YAML \u8a2d\u5b9a\u3002\n\n\u7531 configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 YAML \u8a2d\u5b9a\u4e26\u91cd\u555f Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", + "title": "Radarr YAML \u8a2d\u5b9a\u5df2\u7d93\u79fb\u9664" } }, "options": { diff --git a/homeassistant/components/rtsp_to_webrtc/translations/cs.json b/homeassistant/components/rtsp_to_webrtc/translations/cs.json index 19f5d1e1587..7a90e894e42 100644 --- a/homeassistant/components/rtsp_to_webrtc/translations/cs.json +++ b/homeassistant/components/rtsp_to_webrtc/translations/cs.json @@ -3,5 +3,14 @@ "abort": { "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." } + }, + "options": { + "step": { + "init": { + "data": { + "stun_server": "Adresa serveru Stun (host:port)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/cs.json b/homeassistant/components/scrape/translations/cs.json index 6c5458481b8..68cbf40a518 100644 --- a/homeassistant/components/scrape/translations/cs.json +++ b/homeassistant/components/scrape/translations/cs.json @@ -3,12 +3,53 @@ "abort": { "already_configured": "\u00da\u010det je ji\u017e nastaven" }, + "error": { + "resource_error": "Nepoda\u0159ilo se aktualizovat rest data. Ov\u011b\u0159te svou konfiguraci" + }, "step": { + "sensor": { + "data": { + "attribute": "Atribut", + "device_class": "T\u0159\u00edda za\u0159\u00edzen\u00ed", + "index": "Index", + "name": "Jm\u00e9no", + "select": "Vybrat", + "unit_of_measurement": "Jednotka m\u011b\u0159en\u00ed", + "value_template": "\u0160ablona hodnoty" + }, + "data_description": { + "attribute": "Z\u00edskat hodnotu atributu na vybran\u00e9m tagu", + "device_class": "Typ/t\u0159\u00edda senzoru pro nastaven\u00ed ikony v rozhran\u00ed", + "index": "Definuje, kter\u00fd z prvk\u016f vr\u00e1cen\u00fdch selektorem CSS se m\u00e1 pou\u017e\u00edt", + "select": "Definuje, jak\u00fd tag hledat. Podrobnosti najdete v selektorech CSS Beautifulsoup", + "state_class": "state_class senzoru", + "unit_of_measurement": "Zvolte m\u011b\u0159en\u00ed teploty nebo si vytvo\u0159te vlastn\u00ed", + "value_template": "Definuje \u0161ablonu pro z\u00edsk\u00e1n\u00ed stavu senzoru" + } + }, "user": { "data": { "headers": "Hlavi\u010dky", + "method": "Metoda", "password": "Heslo", + "timeout": "\u010casov\u00fd limit", "username": "U\u017eivatelsk\u00e9 jm\u00e9no" + }, + "data_description": { + "timeout": "\u010casov\u00fd limit pro p\u0159ipojen\u00ed k webu" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "method": "Metoda", + "timeout": "\u010casov\u00fd limit" + }, + "data_description": { + "timeout": "\u010casov\u00fd limit pro p\u0159ipojen\u00ed k webu" } } } diff --git a/homeassistant/components/scrape/translations/nl.json b/homeassistant/components/scrape/translations/nl.json index 45461f45354..3bb3fa570e5 100644 --- a/homeassistant/components/scrape/translations/nl.json +++ b/homeassistant/components/scrape/translations/nl.json @@ -3,13 +3,39 @@ "abort": { "already_configured": "Account is al geconfigureerd" }, + "error": { + "resource_error": "Kon de de REST data niet bijwerken, controleer je configuratie" + }, "step": { + "sensor": { + "data": { + "attribute": "Attribuut", + "device_class": "Apparaatklasse (device_class)", + "index": "Index", + "name": "Naam", + "select": "Selecteer", + "state_class": "Statusklasse (state_class)", + "unit_of_measurement": "Eenheid", + "value_template": "Waardesjabloon (template)" + }, + "data_description": { + "attribute": "Bepaal de waarde van een attribuut op van de geselecteerde tag", + "device_class": "Type/klasse van de sensor voor het icoon in de frontend", + "index": "Bepaalt welke van de elementen worden teruggegeven door de CSS selector om te gebruiken", + "select": "Bepaalt de tag om naar te zoeken. Zie Beautifulsoup CSS selectors voor meer details", + "state_class": "De state_class van de sensor", + "unit_of_measurement": "Kies een temperatuurmeting of cre\u00eber er zelf een", + "value_template": "Definieert een template om de status van de sensor te bepalen" + } + }, "user": { "data": { "authentication": "Authenticatie", "headers": "Headers", + "method": "Methode", "password": "Wachtwoord", "resource": "Bron", + "timeout": "Wachttijd verstreken", "username": "Gebruikersnaam", "verify_ssl": "SSL-certificaat verifi\u00ebren" }, @@ -17,6 +43,7 @@ "authentication": "Type van de HTTP-authenticatie. Ofwel basic of digest", "headers": "Headers om te gebruiken voor het webverzoek", "resource": "De URL naar de website die de waarde bevat", + "timeout": "Wachttijd verstreken bij het maken van een verbinding naar de website", "verify_ssl": "Activeert/de-activeert verificatie van SSL/TLS certificaat, als voorbeeld of het is zelf-getekend" } } @@ -28,8 +55,10 @@ "data": { "authentication": "Authenticatie", "headers": "Headers", + "method": "Methode", "password": "Wachtwoord", "resource": "Bron", + "timeout": "Wachttijd verstreken", "username": "Gebruikersnaam", "verify_ssl": "SSL-certificaat verifi\u00ebren" }, @@ -37,6 +66,7 @@ "authentication": "Type van de HTTP-authenticatie. Ofwel basic of digest", "headers": "Headers om te gebruiken voor het webverzoek", "resource": "De URL naar de website die de waarde bevat", + "timeout": "Wachttijd verstreken bij het maken van een verbinding naar de website", "verify_ssl": "Activeert/de-activeert verificatie van SSL/TLS certificaat, als voorbeeld of het is zelf-getekend" } } diff --git a/homeassistant/components/sensibo/translations/sensor.cs.json b/homeassistant/components/sensibo/translations/sensor.cs.json new file mode 100644 index 00000000000..12454dcbf68 --- /dev/null +++ b/homeassistant/components/sensibo/translations/sensor.cs.json @@ -0,0 +1,9 @@ +{ + "state": { + "sensibo__smart_type": { + "feelslike": "Pocitov\u011b", + "humidity": "Vlhkost vzduchu", + "temperature": "Teplota" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/cs.json b/homeassistant/components/shelly/translations/cs.json index c2f45d0f4c7..248a101a5b3 100644 --- a/homeassistant/components/shelly/translations/cs.json +++ b/homeassistant/components/shelly/translations/cs.json @@ -57,5 +57,18 @@ "single_push": "\"{subtype}\" stisknuto jednou", "triple": "\"{subtype}\" stisknuto t\u0159ikr\u00e1t" } + }, + "options": { + "abort": { + "ble_unsupported": "Podpora Bluetooth vy\u017eaduje verzi firmwaru {ble_min_version} nebo nov\u011bj\u0161\u00ed." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Re\u017eim skenov\u00e1n\u00ed Bluetooth" + }, + "description": "Bluetooth skenov\u00e1n\u00ed m\u016f\u017ee b\u00fdt aktivn\u00ed nebo pasivn\u00ed. Kdy\u017e je aktivn\u00ed, Shelly po\u017eaduje data z okoln\u00edch za\u0159\u00edzen\u00ed; s pasivn\u00ed, Shelly p\u0159ij\u00edm\u00e1 nevy\u017e\u00e1dan\u00e1 data z okoln\u00edch za\u0159\u00edzen\u00ed." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/cs.json b/homeassistant/components/sia/translations/cs.json index 7940c6378fe..1ff2a3eb539 100644 --- a/homeassistant/components/sia/translations/cs.json +++ b/homeassistant/components/sia/translations/cs.json @@ -1,6 +1,7 @@ { "config": { "error": { + "invalid_ping": "Interval pingu mus\u00ed b\u00fdt mezi 1 a 1440 minutami.", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { diff --git a/homeassistant/components/simplisafe/translations/cs.json b/homeassistant/components/simplisafe/translations/cs.json index 8864710e8d1..b662da2f8c0 100644 --- a/homeassistant/components/simplisafe/translations/cs.json +++ b/homeassistant/components/simplisafe/translations/cs.json @@ -5,6 +5,7 @@ "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "error": { + "identifier_exists": "\u00da\u010det je ji\u017e zaregistrov\u00e1n", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" } diff --git a/homeassistant/components/snooz/translations/cs.json b/homeassistant/components/snooz/translations/cs.json new file mode 100644 index 00000000000..925c1cbd337 --- /dev/null +++ b/homeassistant/components/snooz/translations/cs.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", + "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", + "no_devices_found": "V s\u00edti nebyla nalezena \u017e\u00e1dn\u00e1 za\u0159\u00edzen\u00ed" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Chcete nastavit {name}?" + }, + "user": { + "data": { + "address": "Za\u0159\u00edzen\u00ed" + }, + "description": "Zvolte za\u0159\u00edzen\u00ed, kter\u00e9 chcete nastavit" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/cs.json b/homeassistant/components/tankerkoenig/translations/cs.json index 3bfe94e68dd..8c13cb40b74 100644 --- a/homeassistant/components/tankerkoenig/translations/cs.json +++ b/homeassistant/components/tankerkoenig/translations/cs.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Um\u00edst\u011bn\u00ed je ji\u017e nastaveno", "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/tasmota/translations/cs.json b/homeassistant/components/tasmota/translations/cs.json index 673126f1cf0..5abe099df04 100644 --- a/homeassistant/components/tasmota/translations/cs.json +++ b/homeassistant/components/tasmota/translations/cs.json @@ -8,5 +8,10 @@ "description": "Chcete nastavit Tasmota?" } } + }, + "issues": { + "topic_duplicated": { + "description": "N\u011bkolik za\u0159\u00edzen\u00ed Tasmota sd\u00edl\u00ed t\u00e9ma {topic} . \n\n Za\u0159\u00edzen\u00ed Tasmota s t\u00edmto probl\u00e9mem: {offenders} ." + } } } \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/cs.json b/homeassistant/components/tautulli/translations/cs.json index e65f964f82d..c58c4e4d53d 100644 --- a/homeassistant/components/tautulli/translations/cs.json +++ b/homeassistant/components/tautulli/translations/cs.json @@ -8,6 +8,13 @@ "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "api_key": "Kl\u00ed\u010d API" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/text/translations/bg.json b/homeassistant/components/text/translations/bg.json new file mode 100644 index 00000000000..87a2a7629be --- /dev/null +++ b/homeassistant/components/text/translations/bg.json @@ -0,0 +1,3 @@ +{ + "title": "\u0422\u0435\u043a\u0441\u0442" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/ca.json b/homeassistant/components/text/translations/ca.json new file mode 100644 index 00000000000..19b840cdb8f --- /dev/null +++ b/homeassistant/components/text/translations/ca.json @@ -0,0 +1,3 @@ +{ + "title": "Text" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/cs.json b/homeassistant/components/text/translations/cs.json new file mode 100644 index 00000000000..19b840cdb8f --- /dev/null +++ b/homeassistant/components/text/translations/cs.json @@ -0,0 +1,3 @@ +{ + "title": "Text" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/et.json b/homeassistant/components/text/translations/et.json new file mode 100644 index 00000000000..6ac4d0d7b31 --- /dev/null +++ b/homeassistant/components/text/translations/et.json @@ -0,0 +1,3 @@ +{ + "title": "Tekst" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/nl.json b/homeassistant/components/text/translations/nl.json new file mode 100644 index 00000000000..6ac4d0d7b31 --- /dev/null +++ b/homeassistant/components/text/translations/nl.json @@ -0,0 +1,3 @@ +{ + "title": "Tekst" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/ru.json b/homeassistant/components/text/translations/ru.json new file mode 100644 index 00000000000..87a2a7629be --- /dev/null +++ b/homeassistant/components/text/translations/ru.json @@ -0,0 +1,3 @@ +{ + "title": "\u0422\u0435\u043a\u0441\u0442" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/zh-Hant.json b/homeassistant/components/text/translations/zh-Hant.json new file mode 100644 index 00000000000..b6aaf2f6191 --- /dev/null +++ b/homeassistant/components/text/translations/zh-Hant.json @@ -0,0 +1,3 @@ +{ + "title": "\u6587\u5b57" +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/cs.json b/homeassistant/components/trafikverket_train/translations/cs.json index 89ded7d388c..8d62fe5c226 100644 --- a/homeassistant/components/trafikverket_train/translations/cs.json +++ b/homeassistant/components/trafikverket_train/translations/cs.json @@ -7,6 +7,13 @@ "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" + }, + "step": { + "user": { + "data": { + "weekday": "Dny" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/cs.json b/homeassistant/components/ukraine_alarm/translations/cs.json index 5073c9248e0..e45d8f4ef7e 100644 --- a/homeassistant/components/ukraine_alarm/translations/cs.json +++ b/homeassistant/components/ukraine_alarm/translations/cs.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Um\u00edst\u011bn\u00ed je ji\u017e nastaveno", "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" } diff --git a/homeassistant/components/unifiprotect/translations/cs.json b/homeassistant/components/unifiprotect/translations/cs.json index 855cc6da6da..fa05d98a41c 100644 --- a/homeassistant/components/unifiprotect/translations/cs.json +++ b/homeassistant/components/unifiprotect/translations/cs.json @@ -32,5 +32,33 @@ } } } + }, + "issues": { + "ea_setup_failed": { + "title": "Chyba instalace pomoc\u00ed verze p\u0159edb\u011b\u017en\u00e9ho p\u0159\u00edstupu" + }, + "ea_warning": { + "fix_flow": { + "step": { + "confirm": { + "description": "Opravdu chcete spou\u0161t\u011bt nepodporovan\u00e9 verze UniFi Protect? To m\u016f\u017ee zp\u016fsobit p\u0159eru\u0161en\u00ed integrace Home Assistanta.", + "title": "v{version} je verze p\u0159edb\u011b\u017en\u00e9ho p\u0159\u00edstupu" + }, + "start": { + "description": "Pou\u017e\u00edv\u00e1te v{version} UniFi Protect, co\u017e je verze p\u0159edb\u011b\u017en\u00e9ho p\u0159\u00edstupu. [Verze s p\u0159edb\u011b\u017en\u00fdm p\u0159\u00edstupem nejsou podporov\u00e1ny Home Assistantem](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) a doporu\u010dujeme se co nejd\u0159\u00edve vr\u00e1tit ke stabiln\u00edmu vyd\u00e1n\u00ed. \n\n Odesl\u00e1n\u00edm tohoto formul\u00e1\u0159e bu\u010f [p\u0159ejdete na ni\u017e\u0161\u00ed verzi UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect), nebo souhlas\u00edte se spu\u0161t\u011bn\u00edm nepodporovan\u00e9 verze UniFi Protect.", + "title": "v{version} je verze p\u0159edb\u011b\u017en\u00e9ho p\u0159\u00edstupu" + } + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "allow_ea": "Povolit verze s p\u0159edb\u011b\u017en\u00fdm p\u0159\u00edstupem Protect (VAROV\u00c1N\u00cd: Va\u0161i integraci ozna\u010d\u00ed jako nepodporovanou)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/cs.json b/homeassistant/components/wake_on_lan/translations/cs.json new file mode 100644 index 00000000000..202a2726181 --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/cs.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "Konfigurace Wake on Lan pomoc\u00ed YAML byla p\u0159esunuta do integra\u010dn\u00edho kl\u00ed\u010de. \n\n Va\u0161e st\u00e1vaj\u00edc\u00ed konfigurace YAML bude fungovat pro dal\u0161\u00ed 2 verze. \n\n Prove\u010fte migraci konfigurace YAML na integra\u010dn\u00ed kl\u00ed\u010d podle dokumentace.", + "title": "Konfigurace Wake on Lan YAML byla p\u0159esunuta" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wiz/translations/cs.json b/homeassistant/components/wiz/translations/cs.json index b6b916d5d3a..aa21c1d63ea 100644 --- a/homeassistant/components/wiz/translations/cs.json +++ b/homeassistant/components/wiz/translations/cs.json @@ -7,6 +7,7 @@ }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "no_ip": "Neplatn\u00e1 adresa IP.", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { diff --git a/homeassistant/components/xiaomi_miio/translations/cs.json b/homeassistant/components/xiaomi_miio/translations/cs.json index 858546d915f..7abed174b7a 100644 --- a/homeassistant/components/xiaomi_miio/translations/cs.json +++ b/homeassistant/components/xiaomi_miio/translations/cs.json @@ -5,7 +5,8 @@ "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", "incomplete_info": "Ne\u00fapln\u00e9 informace pro nastaven\u00ed za\u0159\u00edzen\u00ed, chyb\u00ed hostitel nebo token.", "not_xiaomi_miio": "Za\u0159\u00edzen\u00ed (zat\u00edm) nen\u00ed podporov\u00e1no integrac\u00ed Xiaomi Miio.", - "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" + "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9", + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", diff --git a/homeassistant/components/yamaha_musiccast/translations/select.cs.json b/homeassistant/components/yamaha_musiccast/translations/select.cs.json index c416256dd99..8217b2f9128 100644 --- a/homeassistant/components/yamaha_musiccast/translations/select.cs.json +++ b/homeassistant/components/yamaha_musiccast/translations/select.cs.json @@ -1,5 +1,10 @@ { "state": { + "yamaha_musiccast__zone_sleep": { + "30 min": "30 minut", + "60 min": "60 minut", + "90 min": "90 minut" + }, "yamaha_musiccast__zone_surr_decoder_type": { "toggle": "P\u0159epnout" }, diff --git a/homeassistant/components/yolink/translations/cs.json b/homeassistant/components/yolink/translations/cs.json index 5b7d9c2db8e..67665aec47e 100644 --- a/homeassistant/components/yolink/translations/cs.json +++ b/homeassistant/components/yolink/translations/cs.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u00da\u010det je ji\u017e nastaven", "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", + "oauth_error": "P\u0159ijata neplatn\u00e1 data tokenu.", "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "step": { diff --git a/homeassistant/components/zamg/translations/cs.json b/homeassistant/components/zamg/translations/cs.json new file mode 100644 index 00000000000..60fa7fddced --- /dev/null +++ b/homeassistant/components/zamg/translations/cs.json @@ -0,0 +1,11 @@ +{ + "config": { + "abort": { + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" + }, + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zha/translations/cs.json b/homeassistant/components/zha/translations/cs.json index f64524191a9..42a473abf96 100644 --- a/homeassistant/components/zha/translations/cs.json +++ b/homeassistant/components/zha/translations/cs.json @@ -142,6 +142,9 @@ }, "title": "P\u0159epsat adresu IEEE r\u00e1dia" }, + "prompt_migrate_or_reconfigure": { + "description": "P\u0159ech\u00e1z\u00edte na nov\u00e9 r\u00e1dio nebo m\u011bn\u00edte konfiguraci st\u00e1vaj\u00edc\u00edho r\u00e1dia?" + }, "upload_manual_backup": { "data": { "uploaded_backup_file": "Nahr\u00e1t soubor" diff --git a/homeassistant/components/zwave_js/translations/cs.json b/homeassistant/components/zwave_js/translations/cs.json index 76bd5a35e96..ec325bae59a 100644 --- a/homeassistant/components/zwave_js/translations/cs.json +++ b/homeassistant/components/zwave_js/translations/cs.json @@ -3,7 +3,8 @@ "abort": { "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "not_zwave_js_addon": "Dopln\u011bk Discovered nen\u00ed ofici\u00e1ln\u00edm dopl\u0148kem Z-Wave JS." }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", From fb132f8a26efae23afa3a9d38bd6ad649935cfc7 Mon Sep 17 00:00:00 2001 From: Willem-Jan van Rootselaar Date: Sat, 26 Nov 2022 03:12:07 +0100 Subject: [PATCH 0738/1033] Add diagnostics to bsblan (#80957) * add diagnostics to bsblan * add device info check firmware. * add test for diagnostics * Update tests/components/bsblan/test_diagnostics.py Co-authored-by: Aarni Koskela * add fixture for diagnostics test Co-authored-by: Aarni Koskela --- .../components/bsblan/diagnostics.py | 22 ++++ .../bsblan/fixtures/diagnostics.json | 110 ++++++++++++++++++ tests/components/bsblan/test_diagnostics.py | 24 ++++ 3 files changed, 156 insertions(+) create mode 100644 homeassistant/components/bsblan/diagnostics.py create mode 100644 tests/components/bsblan/fixtures/diagnostics.json create mode 100644 tests/components/bsblan/test_diagnostics.py diff --git a/homeassistant/components/bsblan/diagnostics.py b/homeassistant/components/bsblan/diagnostics.py new file mode 100644 index 00000000000..91d959ea0e2 --- /dev/null +++ b/homeassistant/components/bsblan/diagnostics.py @@ -0,0 +1,22 @@ +"""Diagnostics support for BSBLan.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from . import HomeAssistantBSBLANData +from .const import DOMAIN + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + data: HomeAssistantBSBLANData = hass.data[DOMAIN][entry.entry_id] + return { + "info": data.info.dict(), + "device": data.device.dict(), + "state": data.coordinator.data.dict(), + } diff --git a/tests/components/bsblan/fixtures/diagnostics.json b/tests/components/bsblan/fixtures/diagnostics.json new file mode 100644 index 00000000000..b805a3b0b1b --- /dev/null +++ b/tests/components/bsblan/fixtures/diagnostics.json @@ -0,0 +1,110 @@ +{ + "info": { + "device_identification": { + "name": "Gerte-Identifikation", + "unit": "", + "desc": "", + "value": "RVS21.831F/127", + "dataType": 7 + }, + "controller_family": { + "name": "Device family", + "unit": "", + "desc": "", + "value": "211", + "dataType": 0 + }, + "controller_variant": { + "name": "Device variant", + "unit": "", + "desc": "", + "value": "127", + "dataType": 0 + } + }, + "device": { + "name": "BSB-LAN", + "version": "1.0.38-20200730234859", + "MAC": "00:80:41:19:69:90", + "uptime": 969402857 + }, + "state": { + "hvac_mode": { + "name": "Operating mode", + "unit": "", + "desc": "Komfort", + "value": "heat", + "dataType": 1 + }, + "hvac_mode2": { + "name": "Operating mode", + "unit": "", + "desc": "Reduziert", + "value": "2", + "dataType": 1 + }, + "target_temperature": { + "name": "Room temperature Comfort setpoint", + "unit": "°C", + "desc": "", + "value": "18.5", + "dataType": 0 + }, + "target_temperature_high": { + "name": "Komfortsollwert Maximum", + "unit": "°C", + "desc": "", + "value": "23.0", + "dataType": 0 + }, + "target_temperature_low": { + "name": "Room temp reduced setpoint", + "unit": "°C", + "desc": "", + "value": "17.0", + "dataType": 0 + }, + "min_temp": { + "name": "Room temp frost protection setpoint", + "unit": "°C", + "desc": "", + "value": "8.0", + "dataType": 0 + }, + "max_temp": { + "name": "Summer/winter changeover temp heat circuit 1", + "unit": "°C", + "desc": "", + "value": "20.0", + "dataType": 0 + }, + "hvac_action": { + "name": "Status heating circuit 1", + "unit": "", + "desc": "Raumtemp\u2019begrenzung", + "value": "122", + "dataType": 1 + }, + "current_temperature": { + "name": "Room temp 1 actual value", + "unit": "°C", + "desc": "", + "value": "18.6", + "dataType": 0 + }, + "room1_thermostat_mode": { + "name": "Raumthermostat 1", + "unit": "", + "desc": "Kein Bedarf", + "value": "0", + "dataType": 1 + }, + "outside_temperature": { + "name": "Outside temp sensor local", + "unit": "°C", + "desc": "", + "value": "6.1", + "dataType": 0 + } + } +} diff --git a/tests/components/bsblan/test_diagnostics.py b/tests/components/bsblan/test_diagnostics.py new file mode 100644 index 00000000000..6af6972e577 --- /dev/null +++ b/tests/components/bsblan/test_diagnostics.py @@ -0,0 +1,24 @@ +"""Tests for the diagnostics data provided by the BSBLan integration.""" +import json + +from aiohttp import ClientSession + +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry, load_fixture +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_diagnostics( + hass: HomeAssistant, + hass_client: ClientSession, + init_integration: MockConfigEntry, +): + """Test diagnostics.""" + + diagnostics_fixture = json.loads(load_fixture("bsblan/diagnostics.json")) + + assert ( + await get_diagnostics_for_config_entry(hass, hass_client, init_integration) + == diagnostics_fixture + ) From 6f1208b07fdf41bcab550a4b7adb42a1909d7898 Mon Sep 17 00:00:00 2001 From: mbo18 Date: Sat, 26 Nov 2022 05:40:02 +0100 Subject: [PATCH 0739/1033] Add more sensors to SensorEntityDescription for RFLink (#82036) * Add more sensors to SensorEntityDescription * changes from comments * add device_class precipitation * fix test * change state_class for total_rain --- homeassistant/components/rflink/sensor.py | 207 ++++++++++++++++++---- tests/components/rflink/test_sensor.py | 49 +++-- 2 files changed, 209 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/rflink/sensor.py b/homeassistant/components/rflink/sensor.py index d1c3682228c..38d2cd4dd09 100644 --- a/homeassistant/components/rflink/sensor.py +++ b/homeassistant/components/rflink/sensor.py @@ -18,8 +18,17 @@ from homeassistant.const import ( CONF_NAME, CONF_SENSOR_TYPE, CONF_UNIT_OF_MEASUREMENT, + DEGREE, + ELECTRIC_CURRENT_AMPERE, + ELECTRIC_POTENTIAL_VOLT, + LIGHT_LUX, + PERCENTAGE, + PRECIPITATION_MILLIMETERS, + UV_INDEX, + UnitOfPower, UnitOfSpeed, UnitOfTemperature, + UnitOfVolumetricFlux, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -41,18 +50,14 @@ from . import ( RflinkDevice, ) -SENSOR_ICONS = { - "humidity": "mdi:water-percent", - "battery": "mdi:battery", -} - SENSOR_TYPES = ( # check new descriptors against PACKET_FIELDS & UNITS from rflink.parser SensorEntityDescription( - key="distance", - name="Distance", - device_class=SensorDeviceClass.DISTANCE, + key="average_windspeed", + name="Average windspeed", + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, ), SensorEntityDescription( key="barometric_pressure", @@ -61,11 +66,169 @@ SENSOR_TYPES = ( state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( - key="average_windspeed", - name="Average windspeed", - device_class=SensorDeviceClass.WIND_SPEED, + key="battery", + name="Battery", + icon="mdi:battery", + ), + SensorEntityDescription( + key="co2_air_quality", + name="CO2 air quality", + device_class=SensorDeviceClass.CO2, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + ), + SensorEntityDescription( + key="command", + name="Command", + icon="mdi:text", + ), + SensorEntityDescription( + key="current_phase_1", + name="Current phase 1", + device_class=SensorDeviceClass.CURRENT, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + ), + SensorEntityDescription( + key="current_phase_2", + name="Current phase 2", + device_class=SensorDeviceClass.CURRENT, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + ), + SensorEntityDescription( + key="current_phase_3", + name="Current phase 3", + device_class=SensorDeviceClass.CURRENT, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + ), + SensorEntityDescription( + key="distance", + name="Distance", + device_class=SensorDeviceClass.DISTANCE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key="doorbell_melody", + name="Doorbell melody", + icon="mdi:bell", + ), + SensorEntityDescription( + key="firmware", + name="Firmware", + icon="mdi:information-outline", + ), + SensorEntityDescription( + key="hardware", + name="Hardware", + icon="mdi:chip", + ), + SensorEntityDescription( + key="humidity", + name="Humidity", + device_class=SensorDeviceClass.HUMIDITY, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=PERCENTAGE, + ), + SensorEntityDescription( + key="humidity_status", + name="Humidity status", + icon="mdi:water-percent", + ), + SensorEntityDescription( + key="kilowatt", + name="Kilowatt", + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, + ), + SensorEntityDescription( + key="light_intensity", + name="Light intensity", + device_class=SensorDeviceClass.ILLUMINANCE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=LIGHT_LUX, + ), + SensorEntityDescription( + key="meter_value", + name="Meter value", + icon="mdi:counter", + ), + SensorEntityDescription( + key="noise_level", + name="Noise level", + icon="mdi:bell-alert", + ), + SensorEntityDescription( + key="rain_rate", + name="Rain rate", + device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, + ), + SensorEntityDescription( + key="revision", + name="Revision", + icon="mdi:information", + ), + SensorEntityDescription( + key="temperature", + name="Temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + ), + SensorEntityDescription( + key="total_rain", + name="Total rain", + device_class=SensorDeviceClass.PRECIPITATION, + state_class=SensorStateClass.TOTAL_INCREASING, + native_unit_of_measurement=PRECIPITATION_MILLIMETERS, + ), + SensorEntityDescription( + key="uv_intensity", + name="UV intensity", + icon="mdi:sunglasses", + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UV_INDEX, + ), + SensorEntityDescription( + key="version", + name="Version", + icon="mdi:information", + ), + SensorEntityDescription( + key="voltage", + name="Voltage", + device_class=SensorDeviceClass.VOLTAGE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + ), + SensorEntityDescription( + key="watt", + name="Watt", + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfPower.WATT, + ), + SensorEntityDescription( + key="weather_forecast", + name="Weather forecast", + icon="mdi:weather-cloudy-clock", + ), + SensorEntityDescription( + key="windchill", + name="Wind chill", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + ), + SensorEntityDescription( + key="winddirection", + name="Wind direction", + icon="mdi:compass", + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=DEGREE, ), SensorEntityDescription( key="windgusts", @@ -81,13 +244,6 @@ SENSOR_TYPES = ( state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, ), - SensorEntityDescription( - key="temperature", - name="Temperature", - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=UnitOfTemperature.CELSIUS, - ), SensorEntityDescription( key="windtemp", name="Wind temperature", @@ -95,13 +251,6 @@ SENSOR_TYPES = ( state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), - SensorEntityDescription( - key="windchill", - name="Wind chill", - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=UnitOfTemperature.CELSIUS, - ), ) SENSOR_TYPES_DICT = {desc.key: desc for desc in SENSOR_TYPES} @@ -248,9 +397,3 @@ class RflinkSensor(RflinkDevice, SensorEntity): def native_value(self): """Return value.""" return self._state - - @property - def icon(self): - """Return possible sensor specific icon.""" - if self._sensor_type in SENSOR_ICONS: - return SENSOR_ICONS[self._sensor_type] diff --git a/tests/components/rflink/test_sensor.py b/tests/components/rflink/test_sensor.py index 4faa1eb6d07..0202894a41c 100644 --- a/tests/components/rflink/test_sensor.py +++ b/tests/components/rflink/test_sensor.py @@ -17,6 +17,7 @@ from homeassistant.const import ( ATTR_ICON, ATTR_UNIT_OF_MEASUREMENT, PERCENTAGE, + PRECIPITATION_MILLIMETERS, STATE_UNKNOWN, UnitOfTemperature, ) @@ -87,17 +88,15 @@ async def test_default_setup(hass, monkeypatch): ) # temperature uses SensorEntityDescription # test event for new unconfigured sensor - event_callback( - {"id": "test3", "sensor": "humidity", "value": 43, "unit": PERCENTAGE} - ) + event_callback({"id": "test3", "sensor": "battery", "value": "ok", "unit": None}) await hass.async_block_till_done() - # test state of hum sensor - hum_sensor = hass.states.get("sensor.test3") - assert hum_sensor - assert hum_sensor.state == "43" - assert hum_sensor.attributes[ATTR_UNIT_OF_MEASUREMENT] == PERCENTAGE - assert hum_sensor.attributes[ATTR_ICON] == "mdi:water-percent" + # test state of battery sensor + bat_sensor = hass.states.get("sensor.test3") + assert bat_sensor + assert bat_sensor.state == "ok" + assert ATTR_UNIT_OF_MEASUREMENT not in bat_sensor.attributes + assert bat_sensor.attributes[ATTR_ICON] == "mdi:battery" async def test_disable_automatic_add(hass, monkeypatch): @@ -195,7 +194,7 @@ async def test_aliases(hass, monkeypatch): ) await hass.async_block_till_done() - # test state of new sensor + # test state of new sensor updated_sensor = hass.states.get("sensor.test_02") assert updated_sensor assert updated_sensor.state == "65" @@ -221,11 +220,11 @@ async def test_race_condition(hass, monkeypatch): await hass.async_block_till_done() - # test state of new sensor + # test state of new sensor updated_sensor = hass.states.get("sensor.test3") assert updated_sensor - # test state of new sensor + # test state of new sensor new_sensor = hass.states.get(f"{DOMAIN}.test3") assert new_sensor assert new_sensor.state == "ok" @@ -235,7 +234,7 @@ async def test_race_condition(hass, monkeypatch): # tmp_entity must be deleted from EVENT_KEY_COMMAND assert tmp_entity not in hass.data[DATA_ENTITY_LOOKUP][EVENT_KEY_SENSOR]["test3"] - # test state of new sensor + # test state of new sensor new_sensor = hass.states.get(f"{DOMAIN}.test3") assert new_sensor assert new_sensor.state == "ko" @@ -249,6 +248,14 @@ async def test_sensor_attributes(hass, monkeypatch): DOMAIN: { "platform": "rflink", "devices": { + "my_meter_device_unique_id": { + "name": "meter_device", + "sensor_type": "meter_value", + }, + "my_rain_device_unique_id": { + "name": "rain_device", + "sensor_type": "total_rain", + }, "my_humidity_device_unique_id": { "name": "humidity_device", "sensor_type": "humidity", @@ -270,10 +277,22 @@ async def test_sensor_attributes(hass, monkeypatch): event_callback, _, _, _ = await mock_rflink(hass, config, DOMAIN, monkeypatch) # test sensor loaded from config + meter_state = hass.states.get("sensor.meter_device") + assert meter_state + assert "device_class" not in meter_state.attributes + assert "state_class" not in meter_state.attributes + assert "unit_of_measurement" not in meter_state.attributes + + rain_state = hass.states.get("sensor.rain_device") + assert rain_state + assert rain_state.attributes["device_class"] == SensorDeviceClass.PRECIPITATION + assert rain_state.attributes["state_class"] == SensorStateClass.TOTAL_INCREASING + assert rain_state.attributes["unit_of_measurement"] == PRECIPITATION_MILLIMETERS + humidity_state = hass.states.get("sensor.humidity_device") assert humidity_state - assert "device_class" not in humidity_state.attributes - assert "state_class" not in humidity_state.attributes + assert humidity_state.attributes["device_class"] == SensorDeviceClass.HUMIDITY + assert humidity_state.attributes["state_class"] == SensorStateClass.MEASUREMENT assert humidity_state.attributes["unit_of_measurement"] == PERCENTAGE temperature_state = hass.states.get("sensor.temperature_device") From daf56e92a02d19d698345f360db7a31ed67c358c Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Sat, 26 Nov 2022 06:49:11 +0100 Subject: [PATCH 0740/1033] Update xknx to 2.0.0 (#82709) --- homeassistant/components/knx/__init__.py | 8 +++--- homeassistant/components/knx/config_flow.py | 11 +++---- homeassistant/components/knx/const.py | 4 ++- homeassistant/components/knx/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/knx/conftest.py | 3 +- tests/components/knx/test_config_flow.py | 32 ++++++++++----------- tests/components/knx/test_diagnostic.py | 16 +++++------ tests/components/knx/test_init.py | 17 ++++++----- 10 files changed, 52 insertions(+), 45 deletions(-) diff --git a/homeassistant/components/knx/__init__.py b/homeassistant/components/knx/__init__.py index 2ca37cc39eb..a486582daec 100644 --- a/homeassistant/components/knx/__init__.py +++ b/homeassistant/components/knx/__init__.py @@ -362,11 +362,8 @@ class KNXModule: def init_xknx(self) -> None: """Initialize XKNX object.""" self.xknx = XKNX( - own_address=self.entry.data[CONF_KNX_INDIVIDUAL_ADDRESS], - rate_limit=self.entry.data[CONF_KNX_RATE_LIMIT], - multicast_group=self.entry.data[CONF_KNX_MCAST_GRP], - multicast_port=self.entry.data[CONF_KNX_MCAST_PORT], connection_config=self.connection_config(), + rate_limit=self.entry.data[CONF_KNX_RATE_LIMIT], state_updater=self.entry.data[CONF_KNX_STATE_UPDATER], ) @@ -384,6 +381,9 @@ class KNXModule: if _conn_type == CONF_KNX_ROUTING: return ConnectionConfig( connection_type=ConnectionType.ROUTING, + individual_address=self.entry.data[CONF_KNX_INDIVIDUAL_ADDRESS], + multicast_group=self.entry.data[CONF_KNX_MCAST_GRP], + multicast_port=self.entry.data[CONF_KNX_MCAST_PORT], local_ip=self.entry.data.get(CONF_KNX_LOCAL_IP), auto_reconnect=True, threaded=True, diff --git a/homeassistant/components/knx/config_flow.py b/homeassistant/components/knx/config_flow.py index 3d046ecaeec..c4107ed7e8b 100644 --- a/homeassistant/components/knx/config_flow.py +++ b/homeassistant/components/knx/config_flow.py @@ -6,10 +6,10 @@ from typing import Any, Final import voluptuous as vol from xknx import XKNX -from xknx.exceptions.exception import InvalidSignature +from xknx.exceptions.exception import InvalidSecureConfiguration from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT from xknx.io.gateway_scanner import GatewayDescriptor, GatewayScanner -from xknx.secure import load_key_ring +from xknx.secure import load_keyring from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow from homeassistant.const import CONF_HOST, CONF_PORT @@ -41,6 +41,7 @@ from .const import ( CONF_KNX_TUNNELING_TCP, CONF_KNX_TUNNELING_TCP_SECURE, CONST_KNX_STORAGE_KEY, + DEFAULT_ROUTING_IA, DOMAIN, KNXConfigEntryData, ) @@ -50,7 +51,7 @@ CONF_KNX_GATEWAY: Final = "gateway" CONF_MAX_RATE_LIMIT: Final = 60 DEFAULT_ENTRY_DATA = KNXConfigEntryData( - individual_address=XKNX.DEFAULT_ADDRESS, + individual_address=DEFAULT_ROUTING_IA, local_ip=None, multicast_group=DEFAULT_MCAST_GRP, multicast_port=DEFAULT_MCAST_PORT, @@ -347,13 +348,13 @@ class KNXCommonFlow(ABC, FlowHandler): assert self._tunneling_config storage_key = CONST_KNX_STORAGE_KEY + user_input[CONF_KNX_KNXKEY_FILENAME] try: - load_key_ring( + await load_keyring( path=self.hass.config.path(STORAGE_DIR, storage_key), password=user_input[CONF_KNX_KNXKEY_PASSWORD], ) except FileNotFoundError: errors[CONF_KNX_KNXKEY_FILENAME] = "file_not_found" - except InvalidSignature: + except InvalidSecureConfiguration: errors[CONF_KNX_KNXKEY_PASSWORD] = "invalid_signature" if not errors: diff --git a/homeassistant/components/knx/const.py b/homeassistant/components/knx/const.py index 3cc73a6dd35..a6a88b2010a 100644 --- a/homeassistant/components/knx/const.py +++ b/homeassistant/components/knx/const.py @@ -41,7 +41,9 @@ CONF_KNX_RATE_LIMIT: Final = "rate_limit" CONF_KNX_ROUTE_BACK: Final = "route_back" CONF_KNX_STATE_UPDATER: Final = "state_updater" CONF_KNX_DEFAULT_STATE_UPDATER: Final = True -CONF_KNX_DEFAULT_RATE_LIMIT: Final = 20 +CONF_KNX_DEFAULT_RATE_LIMIT: Final = 0 + +DEFAULT_ROUTING_IA: Final = "0.0.240" ## # Secure constants diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json index a7436ef1ae3..da0753a9220 100644 --- a/homeassistant/components/knx/manifest.json +++ b/homeassistant/components/knx/manifest.json @@ -3,7 +3,7 @@ "name": "KNX", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/knx", - "requirements": ["xknx==1.2.1"], + "requirements": ["xknx==2.0.0"], "codeowners": ["@Julius2342", "@farmio", "@marvin-w"], "quality_scale": "platinum", "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index e844738a1ab..dfa74930521 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2580,7 +2580,7 @@ xboxapi==2.0.1 xiaomi-ble==0.12.2 # homeassistant.components.knx -xknx==1.2.1 +xknx==2.0.0 # homeassistant.components.bluesound # homeassistant.components.fritz diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6eae3f35265..32592f4d9f6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1793,7 +1793,7 @@ xbox-webapi==2.0.11 xiaomi-ble==0.12.2 # homeassistant.components.knx -xknx==1.2.1 +xknx==2.0.0 # homeassistant.components.bluesound # homeassistant.components.fritz diff --git a/tests/components/knx/conftest.py b/tests/components/knx/conftest.py index 23e04b672ba..b6735df3624 100644 --- a/tests/components/knx/conftest.py +++ b/tests/components/knx/conftest.py @@ -23,6 +23,7 @@ from homeassistant.components.knx.const import ( CONF_KNX_MCAST_PORT, CONF_KNX_RATE_LIMIT, CONF_KNX_STATE_UPDATER, + DEFAULT_ROUTING_IA, DOMAIN as KNX_DOMAIN, ) from homeassistant.core import HomeAssistant @@ -224,7 +225,7 @@ def mock_config_entry() -> MockConfigEntry: CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, CONF_KNX_MCAST_PORT: DEFAULT_MCAST_PORT, CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_INDIVIDUAL_ADDRESS: XKNX.DEFAULT_ADDRESS, + CONF_KNX_INDIVIDUAL_ADDRESS: DEFAULT_ROUTING_IA, }, ) diff --git a/tests/components/knx/test_config_flow.py b/tests/components/knx/test_config_flow.py index fdef15ed217..d57007ed28b 100644 --- a/tests/components/knx/test_config_flow.py +++ b/tests/components/knx/test_config_flow.py @@ -2,7 +2,7 @@ from unittest.mock import patch import pytest -from xknx.exceptions.exception import InvalidSignature +from xknx.exceptions.exception import InvalidSecureConfiguration from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT from xknx.io.gateway_scanner import GatewayDescriptor @@ -212,7 +212,7 @@ async def test_routing_setup_advanced(hass: HomeAssistant) -> None: CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, CONF_HOST: "192.168.0.1", CONF_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", CONF_KNX_ROUTE_BACK: False, CONF_KNX_LOCAL_IP: None, }, @@ -229,7 +229,7 @@ async def test_routing_setup_advanced(hass: HomeAssistant) -> None: CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP, CONF_HOST: "192.168.0.1", CONF_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", CONF_KNX_ROUTE_BACK: False, CONF_KNX_LOCAL_IP: None, }, @@ -246,7 +246,7 @@ async def test_routing_setup_advanced(hass: HomeAssistant) -> None: CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, CONF_HOST: "192.168.0.1", CONF_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", CONF_KNX_ROUTE_BACK: True, CONF_KNX_LOCAL_IP: None, }, @@ -374,7 +374,7 @@ async def test_tunneling_setup_for_local_ip(hass: HomeAssistant) -> None: CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, CONF_HOST: "192.168.0.2", CONF_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", CONF_KNX_ROUTE_BACK: False, CONF_KNX_LOCAL_IP: "192.168.1.112", } @@ -420,7 +420,7 @@ async def test_tunneling_setup_for_multiple_found_gateways(hass: HomeAssistant) CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, CONF_HOST: "192.168.0.1", CONF_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", CONF_KNX_ROUTE_BACK: False, CONF_KNX_LOCAL_IP: None, } @@ -625,7 +625,7 @@ async def test_configure_secure_tunnel_manual(hass: HomeAssistant): CONF_KNX_SECURE_DEVICE_AUTHENTICATION: "device_auth", CONF_HOST: "192.168.0.1", CONF_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", CONF_KNX_ROUTE_BACK: False, CONF_KNX_LOCAL_IP: None, } @@ -649,7 +649,7 @@ async def test_configure_secure_knxkeys(hass: HomeAssistant): "homeassistant.components.knx.async_setup_entry", return_value=True, ) as mock_setup_entry, patch( - "homeassistant.components.knx.config_flow.load_key_ring", return_value=True + "homeassistant.components.knx.config_flow.load_keyring", return_value=True ): secure_knxkeys = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -667,7 +667,7 @@ async def test_configure_secure_knxkeys(hass: HomeAssistant): CONF_KNX_KNXKEY_PASSWORD: "password", CONF_HOST: "192.168.0.1", CONF_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", CONF_KNX_ROUTE_BACK: False, CONF_KNX_LOCAL_IP: None, } @@ -688,7 +688,7 @@ async def test_configure_secure_knxkeys_file_not_found(hass: HomeAssistant): assert not result["errors"] with patch( - "homeassistant.components.knx.config_flow.load_key_ring", + "homeassistant.components.knx.config_flow.load_keyring", side_effect=FileNotFoundError(), ): secure_knxkeys = await hass.config_entries.flow.async_configure( @@ -717,8 +717,8 @@ async def test_configure_secure_knxkeys_invalid_signature(hass: HomeAssistant): assert not result["errors"] with patch( - "homeassistant.components.knx.config_flow.load_key_ring", - side_effect=InvalidSignature(), + "homeassistant.components.knx.config_flow.load_keyring", + side_effect=InvalidSecureConfiguration(), ): secure_knxkeys = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -773,13 +773,13 @@ async def test_options_flow_connection_type( assert mock_config_entry.data == { CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, - CONF_KNX_INDIVIDUAL_ADDRESS: "15.15.250", + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", CONF_HOST: "192.168.0.1", CONF_PORT: 3675, CONF_KNX_LOCAL_IP: None, CONF_KNX_MCAST_PORT: DEFAULT_MCAST_PORT, CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_RATE_LIMIT: 20, + CONF_KNX_RATE_LIMIT: 0, CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, CONF_KNX_ROUTE_BACK: False, } @@ -804,7 +804,7 @@ async def test_options_communication_settings( result["flow_id"], user_input={ CONF_KNX_STATE_UPDATER: False, - CONF_KNX_RATE_LIMIT: 0, + CONF_KNX_RATE_LIMIT: 40, }, ) @@ -816,5 +816,5 @@ async def test_options_communication_settings( **DEFAULT_ENTRY_DATA, CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC, CONF_KNX_STATE_UPDATER: False, - CONF_KNX_RATE_LIMIT: 0, + CONF_KNX_RATE_LIMIT: 40, } diff --git a/tests/components/knx/test_diagnostic.py b/tests/components/knx/test_diagnostic.py index 5f4dd01bcb6..f40649ee1eb 100644 --- a/tests/components/knx/test_diagnostic.py +++ b/tests/components/knx/test_diagnostic.py @@ -2,7 +2,6 @@ from unittest.mock import patch from aiohttp import ClientSession -from xknx import XKNX from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT from homeassistant.components.knx.const import ( @@ -18,6 +17,7 @@ from homeassistant.components.knx.const import ( CONF_KNX_SECURE_DEVICE_AUTHENTICATION, CONF_KNX_SECURE_USER_PASSWORD, CONF_KNX_STATE_UPDATER, + DEFAULT_ROUTING_IA, DOMAIN as KNX_DOMAIN, ) from homeassistant.core import HomeAssistant @@ -45,10 +45,10 @@ async def test_diagnostics( ) == { "config_entry_data": { "connection_type": "automatic", - "individual_address": "15.15.250", + "individual_address": "0.0.240", "multicast_group": "224.0.23.12", "multicast_port": 3671, - "rate_limit": 20, + "rate_limit": 0, "state_updater": True, }, "configuration_error": None, @@ -77,10 +77,10 @@ async def test_diagnostic_config_error( ) == { "config_entry_data": { "connection_type": "automatic", - "individual_address": "15.15.250", + "individual_address": "0.0.240", "multicast_group": "224.0.23.12", "multicast_port": 3671, - "rate_limit": 20, + "rate_limit": 0, "state_updater": True, }, "configuration_error": "extra keys not allowed @ data['knx']['wrong_key']", @@ -103,7 +103,7 @@ async def test_diagnostic_redact( CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, CONF_KNX_MCAST_PORT: DEFAULT_MCAST_PORT, CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_INDIVIDUAL_ADDRESS: XKNX.DEFAULT_ADDRESS, + CONF_KNX_INDIVIDUAL_ADDRESS: DEFAULT_ROUTING_IA, CONF_KNX_KNXKEY_PASSWORD: "password", CONF_KNX_SECURE_USER_PASSWORD: "user_password", CONF_KNX_SECURE_DEVICE_AUTHENTICATION: "device_authentication", @@ -120,10 +120,10 @@ async def test_diagnostic_redact( ) == { "config_entry_data": { "connection_type": "automatic", - "individual_address": "15.15.250", + "individual_address": "0.0.240", "multicast_group": "224.0.23.12", "multicast_port": 3671, - "rate_limit": 20, + "rate_limit": 0, "state_updater": True, "knxkeys_password": "**REDACTED**", "user_password": "**REDACTED**", diff --git a/tests/components/knx/test_init.py b/tests/components/knx/test_init.py index 82ddf73f1ea..4bda407ace7 100644 --- a/tests/components/knx/test_init.py +++ b/tests/components/knx/test_init.py @@ -1,6 +1,5 @@ """Test KNX init.""" import pytest -from xknx import XKNX from xknx.io import ( DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT, @@ -9,6 +8,7 @@ from xknx.io import ( SecureConfig, ) +from homeassistant.components.knx.config_flow import DEFAULT_ROUTING_IA from homeassistant.components.knx.const import ( CONF_KNX_AUTOMATIC, CONF_KNX_CONNECTION_TYPE, @@ -51,7 +51,7 @@ from tests.common import MockConfigEntry CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, CONF_KNX_MCAST_PORT: DEFAULT_MCAST_PORT, CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_INDIVIDUAL_ADDRESS: XKNX.DEFAULT_ADDRESS, + CONF_KNX_INDIVIDUAL_ADDRESS: DEFAULT_ROUTING_IA, }, ConnectionConfig(threaded=True), ), @@ -63,10 +63,13 @@ from tests.common import MockConfigEntry CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, CONF_KNX_MCAST_PORT: DEFAULT_MCAST_PORT, CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_INDIVIDUAL_ADDRESS: XKNX.DEFAULT_ADDRESS, + CONF_KNX_INDIVIDUAL_ADDRESS: DEFAULT_ROUTING_IA, }, ConnectionConfig( connection_type=ConnectionType.ROUTING, + individual_address=DEFAULT_ROUTING_IA, + multicast_group=DEFAULT_MCAST_GRP, + multicast_port=DEFAULT_MCAST_PORT, local_ip="192.168.1.1", threaded=True, ), @@ -82,7 +85,7 @@ from tests.common import MockConfigEntry CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, CONF_KNX_MCAST_PORT: DEFAULT_MCAST_PORT, CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_INDIVIDUAL_ADDRESS: XKNX.DEFAULT_ADDRESS, + CONF_KNX_INDIVIDUAL_ADDRESS: DEFAULT_ROUTING_IA, }, ConnectionConfig( connection_type=ConnectionType.TUNNELING, @@ -103,7 +106,7 @@ from tests.common import MockConfigEntry CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, CONF_KNX_MCAST_PORT: DEFAULT_MCAST_PORT, CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_INDIVIDUAL_ADDRESS: XKNX.DEFAULT_ADDRESS, + CONF_KNX_INDIVIDUAL_ADDRESS: DEFAULT_ROUTING_IA, }, ConnectionConfig( connection_type=ConnectionType.TUNNELING_TCP, @@ -122,7 +125,7 @@ from tests.common import MockConfigEntry CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, CONF_KNX_MCAST_PORT: DEFAULT_MCAST_PORT, CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_INDIVIDUAL_ADDRESS: XKNX.DEFAULT_ADDRESS, + CONF_KNX_INDIVIDUAL_ADDRESS: DEFAULT_ROUTING_IA, CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", CONF_KNX_KNXKEY_PASSWORD: "password", }, @@ -146,7 +149,7 @@ from tests.common import MockConfigEntry CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, CONF_KNX_MCAST_PORT: DEFAULT_MCAST_PORT, CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_INDIVIDUAL_ADDRESS: XKNX.DEFAULT_ADDRESS, + CONF_KNX_INDIVIDUAL_ADDRESS: DEFAULT_ROUTING_IA, CONF_KNX_SECURE_USER_ID: 2, CONF_KNX_SECURE_USER_PASSWORD: "password", CONF_KNX_SECURE_DEVICE_AUTHENTICATION: "device_auth", From e1a0f8314efe4c477de7fe2db8bac4a1d544c378 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sat, 26 Nov 2022 10:56:09 +0100 Subject: [PATCH 0741/1033] Fix deCONZ Air Quality PPB device class and unit of measurement (#82726) Fix Air Quality PPB device class and unit of measurement --- homeassistant/components/deconz/sensor.py | 4 +++- tests/components/deconz/test_sensor.py | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index 5305ea625b8..6b47560b150 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -33,6 +33,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_TEMPERATURE, ATTR_VOLTAGE, + CONCENTRATION_PARTS_PER_BILLION, ENERGY_KILO_WATT_HOUR, LIGHT_LUX, PERCENTAGE, @@ -121,8 +122,9 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( instance_check=AirQuality, name_suffix="PPB", old_unique_id_suffix="ppb", - device_class=SensorDeviceClass.AQI, + device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION, ), DeconzSensorDescription[Consumption]( key="consumption", diff --git a/tests/components/deconz/test_sensor.py b/tests/components/deconz/test_sensor.py index ac8100caa3d..d877e63864a 100644 --- a/tests/components/deconz/test_sensor.py +++ b/tests/components/deconz/test_sensor.py @@ -100,12 +100,13 @@ TEST_DATA = [ "old_unique_id": "00:12:4b:00:14:4d:00:07-ppb", "state": "809", "entity_category": None, - "device_class": SensorDeviceClass.AQI, + "device_class": SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, "state_class": SensorStateClass.MEASUREMENT, "attributes": { - "state_class": "measurement", - "device_class": "aqi", + "device_class": "volatile_organic_compounds", "friendly_name": "BOSCH Air quality sensor PPB", + "state_class": "measurement", + "unit_of_measurement": "ppb", }, "websocket_event": {"state": {"airqualityppb": 1000}}, "next_state": "1000", From 8ed4ce64c3890420c36832223a228f209a9a7254 Mon Sep 17 00:00:00 2001 From: jan iversen Date: Sat, 26 Nov 2022 11:11:50 +0100 Subject: [PATCH 0742/1033] Solve modbus binary slave problem (#82338) * Solve modbus binary slave problem. --- .../components/modbus/binary_sensor.py | 15 +- tests/components/modbus/test_binary_sensor.py | 180 ++++++++---------- 2 files changed, 82 insertions(+), 113 deletions(-) diff --git a/homeassistant/components/modbus/binary_sensor.py b/homeassistant/components/modbus/binary_sensor.py index ed2f6fa0227..06d7b1b6a11 100644 --- a/homeassistant/components/modbus/binary_sensor.py +++ b/homeassistant/components/modbus/binary_sensor.py @@ -60,7 +60,7 @@ class ModbusBinarySensor(BasePlatform, RestoreEntity, BinarySensorEntity): """Initialize the Modbus binary sensor.""" self._count = slave_count + 1 self._coordinator: DataUpdateCoordinator[Any] | None = None - self._result = None + self._result: list = [] super().__init__(hub, entry) async def async_setup_slaves( @@ -106,15 +106,15 @@ class ModbusBinarySensor(BasePlatform, RestoreEntity, BinarySensorEntity): return self._lazy_errors = self._lazy_error_count self._attr_available = False - self._result = None + self._result = [] else: self._lazy_errors = self._lazy_error_count self._attr_available = True - self._result = result if self._input_type in (CALL_TYPE_COIL, CALL_TYPE_DISCRETE): - self._attr_is_on = bool(result.bits[0] & 1) + self._result = result.bits else: - self._attr_is_on = bool(result.registers[0] & 1) + self._result = result.registers + self._attr_is_on = bool(self._result[0] & 1) self.async_write_ha_state() if self._coordinator: @@ -132,8 +132,7 @@ class SlaveSensor(CoordinatorEntity, RestoreEntity, BinarySensorEntity): self._attr_name = f"{entry[CONF_NAME]} {idx}" self._attr_device_class = entry.get(CONF_DEVICE_CLASS) self._attr_available = False - self._result_inx = int(idx / 8) - self._result_bit = 2 ** (idx % 8) + self._result_inx = idx super().__init__(coordinator) async def async_added_to_hass(self) -> None: @@ -148,5 +147,5 @@ class SlaveSensor(CoordinatorEntity, RestoreEntity, BinarySensorEntity): """Handle updated data from the coordinator.""" result = self.coordinator.data if result: - self._attr_is_on = result.bits[self._result_inx] & self._result_bit + self._attr_is_on = bool(result[self._result_inx] & 1) super()._handle_coordinator_update() diff --git a/tests/components/modbus/test_binary_sensor.py b/tests/components/modbus/test_binary_sensor.py index cdd019c73f3..611f558cf1f 100644 --- a/tests/components/modbus/test_binary_sensor.py +++ b/tests/components/modbus/test_binary_sensor.py @@ -117,35 +117,50 @@ async def test_config_binary_sensor(hass, mock_modbus): "register_words,do_exception,expected", [ ( - [0xFF], + [True] * 8, False, STATE_ON, ), ( - [0x01], + [False] * 8, + False, + STATE_OFF, + ), + ( + [False] + [True] * 7, + False, + STATE_OFF, + ), + ( + [True] + [False] * 7, False, STATE_ON, ), ( - [0x00], - False, - STATE_OFF, - ), - ( - [0x80], - False, - STATE_OFF, - ), - ( - [0xFE], - False, - STATE_OFF, - ), - ( - [0x00], + [False] * 8, True, STATE_UNAVAILABLE, ), + ( + [1] * 8, + False, + STATE_ON, + ), + ( + [2] * 8, + False, + STATE_OFF, + ), + ( + [4] + [1] * 7, + False, + STATE_OFF, + ), + ( + [1] + [8] * 7, + False, + STATE_ON, + ), ], ) async def test_all_binary_sensor(hass, expected, mock_do_cycle): @@ -173,7 +188,7 @@ async def test_all_binary_sensor(hass, expected, mock_do_cycle): "register_words,do_exception,start_expect,end_expect", [ ( - [0x00], + [False * 16], True, STATE_UNKNOWN, STATE_UNAVAILABLE, @@ -289,6 +304,34 @@ async def test_config_slave_binary_sensor(hass, mock_modbus): { CONF_NAME: TEST_ENTITY_NAME, CONF_ADDRESS: 51, + CONF_INPUT_TYPE: CALL_TYPE_COIL, + } + ] + }, + { + CONF_BINARY_SENSORS: [ + { + CONF_NAME: TEST_ENTITY_NAME, + CONF_ADDRESS: 51, + CONF_INPUT_TYPE: CALL_TYPE_DISCRETE, + } + ] + }, + { + CONF_BINARY_SENSORS: [ + { + CONF_NAME: TEST_ENTITY_NAME, + CONF_ADDRESS: 51, + CONF_INPUT_TYPE: CALL_TYPE_REGISTER_HOLDING, + } + ] + }, + { + CONF_BINARY_SENSORS: [ + { + CONF_NAME: TEST_ENTITY_NAME, + CONF_ADDRESS: 51, + CONF_INPUT_TYPE: CALL_TYPE_REGISTER_INPUT, } ] }, @@ -299,106 +342,33 @@ async def test_config_slave_binary_sensor(hass, mock_modbus): [ ( {CONF_SLAVE_COUNT: 1}, - [0x01], - STATE_ON, - [ - STATE_OFF, - ], + [False] * 8, + STATE_OFF, + [STATE_OFF], ), ( {CONF_SLAVE_COUNT: 1}, - [0x02], - STATE_OFF, - [ - STATE_ON, - ], + [True] + [False] * 7, + STATE_ON, + [STATE_OFF], ), ( {CONF_SLAVE_COUNT: 1}, - [0x04], + [False, True] + [False] * 6, STATE_OFF, - [ - STATE_OFF, - ], + [STATE_ON], ), ( {CONF_SLAVE_COUNT: 7}, - [0x01], + [True, False] * 4, STATE_ON, - [ - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - ], + [STATE_OFF, STATE_ON] * 3 + [STATE_OFF], ), ( - {CONF_SLAVE_COUNT: 7}, - [0x82], - STATE_OFF, - [ - STATE_ON, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_ON, - ], - ), - ( - {CONF_SLAVE_COUNT: 10}, - [0x01, 0x00], + {CONF_SLAVE_COUNT: 31}, + [True, False] * 16, STATE_ON, - [ - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - ], - ), - ( - {CONF_SLAVE_COUNT: 10}, - [0x01, 0x01], - STATE_ON, - [ - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_ON, - STATE_OFF, - STATE_OFF, - ], - ), - ( - {CONF_SLAVE_COUNT: 10}, - [0x81, 0x01], - STATE_ON, - [ - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_ON, - STATE_ON, - STATE_OFF, - STATE_OFF, - ], + [STATE_OFF, STATE_ON] * 15 + [STATE_OFF], ), ], ) From 5adfae2235962fb0c56dbbdbdea0d6b69548ae69 Mon Sep 17 00:00:00 2001 From: On Freund Date: Sat, 26 Nov 2022 17:38:12 +0200 Subject: [PATCH 0743/1033] Reduce error trace in Risco config flow to debug (#82730) --- homeassistant/components/risco/config_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/risco/config_flow.py b/homeassistant/components/risco/config_flow.py index e0d51a041dd..0f532a376a1 100644 --- a/homeassistant/components/risco/config_flow.py +++ b/homeassistant/components/risco/config_flow.py @@ -158,7 +158,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): try: info = await validate_local_input(self.hass, user_input) except CannotConnectError: - _LOGGER.exception("Cannot connect") + _LOGGER.debug("Cannot connect", exc_info=1) errors["base"] = "cannot_connect" except UnauthorizedError: errors["base"] = "invalid_auth" From 405c2ca82d62f60fae02255689c567561ae988f1 Mon Sep 17 00:00:00 2001 From: mbo18 Date: Sat, 26 Nov 2022 16:40:57 +0100 Subject: [PATCH 0744/1033] Add native unit where device class is set in rflink (#82729) Add native unit where device class is set --- homeassistant/components/rflink/sensor.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/homeassistant/components/rflink/sensor.py b/homeassistant/components/rflink/sensor.py index 38d2cd4dd09..baccd47ebb3 100644 --- a/homeassistant/components/rflink/sensor.py +++ b/homeassistant/components/rflink/sensor.py @@ -14,6 +14,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.const import ( + CONCENTRATION_PARTS_PER_MILLION, CONF_DEVICES, CONF_NAME, CONF_SENSOR_TYPE, @@ -25,7 +26,9 @@ from homeassistant.const import ( PERCENTAGE, PRECIPITATION_MILLIMETERS, UV_INDEX, + UnitOfLength, UnitOfPower, + UnitOfPressure, UnitOfSpeed, UnitOfTemperature, UnitOfVolumetricFlux, @@ -64,6 +67,7 @@ SENSOR_TYPES = ( name="Barometric pressure", device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfPressure.HPA, ), SensorEntityDescription( key="battery", @@ -75,6 +79,7 @@ SENSOR_TYPES = ( name="CO2 air quality", device_class=SensorDeviceClass.CO2, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, ), SensorEntityDescription( key="command", @@ -107,6 +112,7 @@ SENSOR_TYPES = ( name="Distance", device_class=SensorDeviceClass.DISTANCE, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfLength.MILLIMETERS, ), SensorEntityDescription( key="doorbell_melody", From 2fe8e953092f1e3a653d8200284d5a990c398d66 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 26 Nov 2022 19:00:40 +0100 Subject: [PATCH 0745/1033] Add helper to calculate statistic period start and end (#82493) * Add helper to calculate statistic period start and end * Don't parse values in resolve_period * Add specific test for resolve_period * Improve typing * Move to recorder/util.py * Extract period schema --- homeassistant/components/recorder/models.py | 33 ++++++- homeassistant/components/recorder/util.py | 84 ++++++++++++++++- .../components/recorder/websocket_api.py | 94 +++---------------- tests/components/recorder/test_util.py | 81 +++++++++++++++- 4 files changed, 206 insertions(+), 86 deletions(-) diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 3ab8b890838..48b45b4da2e 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -1,9 +1,9 @@ """Models for Recorder.""" from __future__ import annotations -from datetime import datetime +from datetime import datetime, timedelta import logging -from typing import Any, TypedDict, overload +from typing import Any, Literal, TypedDict, overload from sqlalchemy.engine.row import Row @@ -284,3 +284,32 @@ def row_to_compressed_state( row_changed_changed ) return comp_state + + +class CalendarStatisticPeriod(TypedDict, total=False): + """Statistic period definition.""" + + period: Literal["hour", "day", "week", "month", "year"] + offset: int + + +class FixedStatisticPeriod(TypedDict, total=False): + """Statistic period definition.""" + + end_time: datetime + start_time: datetime + + +class RollingWindowStatisticPeriod(TypedDict, total=False): + """Statistic period definition.""" + + duration: timedelta + offset: timedelta + + +class StatisticPeriod(TypedDict, total=False): + """Statistic period definition.""" + + calendar: CalendarStatisticPeriod + fixed_period: FixedStatisticPeriod + rolling_window: RollingWindowStatisticPeriod diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index 8ee9a4e0401..a52a067b975 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -24,8 +24,10 @@ from sqlalchemy.orm.query import Query from sqlalchemy.orm.session import Session from sqlalchemy.sql.lambdas import StatementLambdaElement from typing_extensions import Concatenate, ParamSpec +import voluptuous as vol from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_validation as cv import homeassistant.util.dt as dt_util from .const import DATA_INSTANCE, SQLITE_URL_PREFIX, SupportedDialect @@ -35,7 +37,7 @@ from .db_schema import ( TABLES_TO_CHECK, RecorderRuns, ) -from .models import UnsupportedDialect, process_timestamp +from .models import StatisticPeriod, UnsupportedDialect, process_timestamp if TYPE_CHECKING: from . import Recorder @@ -604,3 +606,83 @@ def get_instance(hass: HomeAssistant) -> Recorder: """Get the recorder instance.""" instance: Recorder = hass.data[DATA_INSTANCE] return instance + + +PERIOD_SCHEMA = vol.Schema( + { + vol.Exclusive("calendar", "period"): vol.Schema( + { + vol.Required("period"): vol.Any("hour", "day", "week", "month", "year"), + vol.Optional("offset"): int, + } + ), + vol.Exclusive("fixed_period", "period"): vol.Schema( + { + vol.Optional("start_time"): vol.All(cv.datetime, dt_util.as_utc), + vol.Optional("end_time"): vol.All(cv.datetime, dt_util.as_utc), + } + ), + vol.Exclusive("rolling_window", "period"): vol.Schema( + { + vol.Required("duration"): cv.time_period_dict, + vol.Optional("offset"): cv.time_period_dict, + } + ), + } +) + + +def resolve_period( + period_def: StatisticPeriod, +) -> tuple[datetime | None, datetime | None]: + """Return start and end datetimes for a statistic period definition.""" + start_time = None + end_time = None + + if "calendar" in period_def: + calendar_period = period_def["calendar"]["period"] + start_of_day = dt_util.start_of_local_day() + cal_offset = period_def["calendar"].get("offset", 0) + if calendar_period == "hour": + start_time = dt_util.now().replace(minute=0, second=0, microsecond=0) + start_time += timedelta(hours=cal_offset) + end_time = start_time + timedelta(hours=1) + elif calendar_period == "day": + start_time = start_of_day + start_time += timedelta(days=cal_offset) + end_time = start_time + timedelta(days=1) + elif calendar_period == "week": + start_time = start_of_day - timedelta(days=start_of_day.weekday()) + start_time += timedelta(days=cal_offset * 7) + end_time = start_time + timedelta(weeks=1) + elif calendar_period == "month": + start_time = start_of_day.replace(day=28) + # This works for up to 48 months of offset + start_time = (start_time + timedelta(days=cal_offset * 31)).replace(day=1) + end_time = (start_time + timedelta(days=31)).replace(day=1) + else: # calendar_period = "year" + start_time = start_of_day.replace(month=12, day=31) + # This works for 100+ years of offset + start_time = (start_time + timedelta(days=cal_offset * 366)).replace( + month=1, day=1 + ) + end_time = (start_time + timedelta(days=365)).replace(day=1) + + start_time = dt_util.as_utc(start_time) + end_time = dt_util.as_utc(end_time) + + elif "fixed_period" in period_def: + start_time = period_def["fixed_period"].get("start_time") + end_time = period_def["fixed_period"].get("end_time") + + elif "rolling_window" in period_def: + duration = period_def["rolling_window"]["duration"] + now = dt_util.utcnow() + start_time = now - duration + end_time = start_time + duration + + if offset := period_def["rolling_window"].get("offset"): + start_time += offset + end_time += offset + + return (start_time, end_time) diff --git a/homeassistant/components/recorder/websocket_api.py b/homeassistant/components/recorder/websocket_api.py index a9fe1589654..35879bfc076 100644 --- a/homeassistant/components/recorder/websocket_api.py +++ b/homeassistant/components/recorder/websocket_api.py @@ -1,9 +1,9 @@ """The Recorder websocket API.""" from __future__ import annotations -from datetime import datetime as dt, timedelta +from datetime import datetime as dt import logging -from typing import Any, Literal +from typing import Any, Literal, cast import voluptuous as vol @@ -26,6 +26,7 @@ from homeassistant.util.unit_conversion import ( ) from .const import MAX_QUEUE_BACKLOG +from .models import StatisticPeriod from .statistics import ( STATISTIC_UNIT_TO_UNIT_CONVERTER, async_add_external_statistics, @@ -36,7 +37,13 @@ from .statistics import ( statistics_during_period, validate_statistics, ) -from .util import async_migration_in_progress, async_migration_is_live, get_instance +from .util import ( + PERIOD_SCHEMA, + async_migration_in_progress, + async_migration_is_live, + get_instance, + resolve_period, +) _LOGGER: logging.Logger = logging.getLogger(__package__) @@ -82,24 +89,6 @@ def _ws_get_statistic_during_period( @websocket_api.websocket_command( { vol.Required("type"): "recorder/statistic_during_period", - vol.Exclusive("calendar", "period"): vol.Schema( - { - vol.Required("period"): vol.Any("hour", "day", "week", "month", "year"), - vol.Optional("offset"): int, - } - ), - vol.Exclusive("fixed_period", "period"): vol.Schema( - { - vol.Optional("start_time"): str, - vol.Optional("end_time"): str, - } - ), - vol.Exclusive("rolling_window", "period"): vol.Schema( - { - vol.Required("duration"): cv.time_period_dict, - vol.Optional("offset"): cv.time_period_dict, - } - ), vol.Optional("statistic_id"): str, vol.Optional("types"): vol.All( [vol.Any("max", "mean", "min", "change")], vol.Coerce(set) @@ -116,6 +105,7 @@ def _ws_get_statistic_during_period( vol.Optional("volume"): vol.In(VolumeConverter.VALID_UNITS), } ), + **PERIOD_SCHEMA.schema, } ) @websocket_api.async_response @@ -128,67 +118,7 @@ async def ws_get_statistic_during_period( if "offset" in msg and "duration" not in msg: raise HomeAssistantError - start_time = None - end_time = None - - if "calendar" in msg: - calendar_period = msg["calendar"]["period"] - start_of_day = dt_util.start_of_local_day() - offset = msg["calendar"].get("offset", 0) - if calendar_period == "hour": - start_time = dt_util.now().replace(minute=0, second=0, microsecond=0) - start_time += timedelta(hours=offset) - end_time = start_time + timedelta(hours=1) - elif calendar_period == "day": - start_time = start_of_day - start_time += timedelta(days=offset) - end_time = start_time + timedelta(days=1) - elif calendar_period == "week": - start_time = start_of_day - timedelta(days=start_of_day.weekday()) - start_time += timedelta(days=offset * 7) - end_time = start_time + timedelta(weeks=1) - elif calendar_period == "month": - start_time = start_of_day.replace(day=28) - # This works for up to 48 months of offset - start_time = (start_time + timedelta(days=offset * 31)).replace(day=1) - end_time = (start_time + timedelta(days=31)).replace(day=1) - else: # calendar_period = "year" - start_time = start_of_day.replace(month=12, day=31) - # This works for 100+ years of offset - start_time = (start_time + timedelta(days=offset * 366)).replace( - month=1, day=1 - ) - end_time = (start_time + timedelta(days=365)).replace(day=1) - - start_time = dt_util.as_utc(start_time) - end_time = dt_util.as_utc(end_time) - - elif "fixed_period" in msg: - if start_time_str := msg["fixed_period"].get("start_time"): - if start_time := dt_util.parse_datetime(start_time_str): - start_time = dt_util.as_utc(start_time) - else: - connection.send_error( - msg["id"], "invalid_start_time", "Invalid start_time" - ) - return - - if end_time_str := msg["fixed_period"].get("end_time"): - if end_time := dt_util.parse_datetime(end_time_str): - end_time = dt_util.as_utc(end_time) - else: - connection.send_error(msg["id"], "invalid_end_time", "Invalid end_time") - return - - elif "rolling_window" in msg: - duration = msg["rolling_window"]["duration"] - now = dt_util.utcnow() - start_time = now - duration - end_time = start_time + duration - - if offset := msg["rolling_window"].get("offset"): - start_time += offset - end_time += offset + start_time, end_time = resolve_period(cast(StatisticPeriod, msg)) connection.send_message( await get_instance(hass).async_add_executor_job( diff --git a/tests/components/recorder/test_util.py b/tests/components/recorder/test_util.py index 9000379c17d..54f9bb9b9f2 100644 --- a/tests/components/recorder/test_util.py +++ b/tests/components/recorder/test_util.py @@ -1,9 +1,10 @@ """Test util methods.""" -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone import os import sqlite3 from unittest.mock import MagicMock, Mock, patch +from freezegun import freeze_time import pytest from sqlalchemy import text from sqlalchemy.engine.result import ChunkedIteratorResult @@ -19,6 +20,7 @@ from homeassistant.components.recorder.models import UnsupportedDialect from homeassistant.components.recorder.util import ( end_incomplete_runs, is_second_sunday, + resolve_period, session_scope, ) from homeassistant.const import EVENT_HOMEASSISTANT_STOP @@ -776,3 +778,80 @@ def test_execute_stmt_lambda_element(hass_recorder): with patch.object(session, "execute", MockExecutor): rows = util.execute_stmt_lambda_element(session, stmt, now, tomorrow) assert rows == ["mock_row"] + + +@freeze_time(datetime(2022, 10, 21, 7, 25, tzinfo=timezone.utc)) +async def test_resolve_period(hass): + """Test statistic_during_period.""" + + now = dt_util.utcnow() + + start_t, end_t = resolve_period({"calendar": {"period": "hour"}}) + assert start_t.isoformat() == "2022-10-21T07:00:00+00:00" + assert end_t.isoformat() == "2022-10-21T08:00:00+00:00" + + start_t, end_t = resolve_period({"calendar": {"period": "hour"}}) + assert start_t.isoformat() == "2022-10-21T07:00:00+00:00" + assert end_t.isoformat() == "2022-10-21T08:00:00+00:00" + + start_t, end_t = resolve_period({"calendar": {"period": "hour", "offset": -1}}) + assert start_t.isoformat() == "2022-10-21T06:00:00+00:00" + assert end_t.isoformat() == "2022-10-21T07:00:00+00:00" + + start_t, end_t = resolve_period({"calendar": {"period": "day"}}) + assert start_t.isoformat() == "2022-10-21T07:00:00+00:00" + assert end_t.isoformat() == "2022-10-22T07:00:00+00:00" + + start_t, end_t = resolve_period({"calendar": {"period": "day", "offset": -1}}) + assert start_t.isoformat() == "2022-10-20T07:00:00+00:00" + assert end_t.isoformat() == "2022-10-21T07:00:00+00:00" + + start_t, end_t = resolve_period({"calendar": {"period": "week"}}) + assert start_t.isoformat() == "2022-10-17T07:00:00+00:00" + assert end_t.isoformat() == "2022-10-24T07:00:00+00:00" + + start_t, end_t = resolve_period({"calendar": {"period": "week", "offset": -1}}) + assert start_t.isoformat() == "2022-10-10T07:00:00+00:00" + assert end_t.isoformat() == "2022-10-17T07:00:00+00:00" + + start_t, end_t = resolve_period({"calendar": {"period": "month"}}) + assert start_t.isoformat() == "2022-10-01T07:00:00+00:00" + assert end_t.isoformat() == "2022-11-01T07:00:00+00:00" + + start_t, end_t = resolve_period({"calendar": {"period": "month", "offset": -1}}) + assert start_t.isoformat() == "2022-09-01T07:00:00+00:00" + assert end_t.isoformat() == "2022-10-01T07:00:00+00:00" + + start_t, end_t = resolve_period({"calendar": {"period": "year"}}) + assert start_t.isoformat() == "2022-01-01T08:00:00+00:00" + assert end_t.isoformat() == "2023-01-01T08:00:00+00:00" + + start_t, end_t = resolve_period({"calendar": {"period": "year", "offset": -1}}) + assert start_t.isoformat() == "2021-01-01T08:00:00+00:00" + assert end_t.isoformat() == "2022-01-01T08:00:00+00:00" + + # Fixed period + assert resolve_period({}) == (None, None) + + assert resolve_period({"fixed_period": {"end_time": now}}) == (None, now) + + assert resolve_period({"fixed_period": {"start_time": now}}) == (now, None) + + assert resolve_period({"fixed_period": {"end_time": now, "start_time": now}}) == ( + now, + now, + ) + + # Rolling window + assert resolve_period( + {"rolling_window": {"duration": timedelta(hours=1, minutes=25)}} + ) == (now - timedelta(hours=1, minutes=25), now) + + assert resolve_period( + { + "rolling_window": { + "duration": timedelta(hours=1), + "offset": timedelta(minutes=-25), + } + } + ) == (now - timedelta(hours=1, minutes=25), now - timedelta(minutes=25)) From 987cf8b9d0b513d4ef184811aac2c75804140cfb Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 26 Nov 2022 19:01:22 +0100 Subject: [PATCH 0746/1033] Fix pushbullet test that break CI (#82744) pushbullet break ci --- tests/components/pushbullet/test_notify.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/components/pushbullet/test_notify.py b/tests/components/pushbullet/test_notify.py index 2773679f22a..a9c93e88bdb 100644 --- a/tests/components/pushbullet/test_notify.py +++ b/tests/components/pushbullet/test_notify.py @@ -157,11 +157,14 @@ async def test_pushbullet_push_mixed(hass, requests_mock): ) entry.add_to_hass(hass) assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + data = { "title": "Test Title", "message": "Test Message", "target": ["device/DESKTOP", "email/user@host.net"], } + await hass.services.async_call(NOTIFY_DOMAIN, "pushbullet", data) await hass.async_block_till_done() From dcf50e5e90ba100ae056b220e0e4f280a56d9af7 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sat, 26 Nov 2022 19:02:12 +0100 Subject: [PATCH 0747/1033] Bump `nextdns` library to version 1.2.2 (#82735) Bump nextdns library to version 1.2.2 --- homeassistant/components/nextdns/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nextdns/manifest.json b/homeassistant/components/nextdns/manifest.json index 44027cd7911..5538a759ff2 100644 --- a/homeassistant/components/nextdns/manifest.json +++ b/homeassistant/components/nextdns/manifest.json @@ -3,7 +3,7 @@ "name": "NextDNS", "documentation": "https://www.home-assistant.io/integrations/nextdns", "codeowners": ["@bieniu"], - "requirements": ["nextdns==1.2.0"], + "requirements": ["nextdns==1.2.2"], "config_flow": true, "iot_class": "cloud_polling", "loggers": ["nextdns"], diff --git a/requirements_all.txt b/requirements_all.txt index dfa74930521..02b27161e61 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1160,7 +1160,7 @@ nextcloudmonitor==1.1.0 nextcord==2.0.0a8 # homeassistant.components.nextdns -nextdns==1.2.0 +nextdns==1.2.2 # homeassistant.components.nibe_heatpump nibe==1.3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 32592f4d9f6..e5421ba6069 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -847,7 +847,7 @@ nexia==2.0.6 nextcord==2.0.0a8 # homeassistant.components.nextdns -nextdns==1.2.0 +nextdns==1.2.2 # homeassistant.components.nibe_heatpump nibe==1.3.0 From 05f89efd2c0f0d954897b2e1d43ec2a8505cb33a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 26 Nov 2022 09:19:06 -1000 Subject: [PATCH 0748/1033] Fix homekit controller triggers not attaching when integration is setup after startup (#82717) fixes https://github.com/home-assistant/core/issues/78852 --- .../homekit_controller/device_trigger.py | 86 +++++++----- .../homekit_controller/test_device_trigger.py | 127 ++++++++++++++++++ 2 files changed, 181 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/homekit_controller/device_trigger.py b/homeassistant/components/homekit_controller/device_trigger.py index ecffb902928..bc1434f4bd9 100644 --- a/homeassistant/components/homekit_controller/device_trigger.py +++ b/homeassistant/components/homekit_controller/device_trigger.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any from aiohomekit.model.characteristics import CharacteristicsTypes from aiohomekit.model.characteristics.const import InputEventValues -from aiohomekit.model.services import ServicesTypes +from aiohomekit.model.services import Service, ServicesTypes from aiohomekit.utils import clamp_enum_to_char import voluptuous as vol @@ -57,28 +57,41 @@ HK_TO_HA_INPUT_EVENT_VALUES = { class TriggerSource: """Represents a stateless source of event data from HomeKit.""" - def __init__( + def __init__(self, hass: HomeAssistant) -> None: + """Initialize a set of triggers for a device.""" + self._hass = hass + self._triggers: dict[tuple[str, str], dict[str, Any]] = {} + self._callbacks: dict[tuple[str, str], list[Callable[[Any], None]]] = {} + self._iid_trigger_keys: dict[int, set[tuple[str, str]]] = {} + + async def async_setup( self, connection: HKDevice, aid: int, triggers: list[dict[str, Any]] ) -> None: - """Initialize a set of triggers for a device.""" - self._hass = connection.hass - self._connection = connection - self._aid = aid - self._triggers: dict[tuple[str, str], dict[str, Any]] = {} - for trigger in triggers: - self._triggers[(trigger["type"], trigger["subtype"])] = trigger - self._callbacks: dict[int, list[Callable[[Any], None]]] = {} + """Set up a set of triggers for a device. - def fire(self, iid, value): + This function must be re-entrant since + it is called when the device is first added and + when the config entry is reloaded. + """ + for trigger_data in triggers: + trigger_key = (trigger_data[CONF_TYPE], trigger_data[CONF_SUBTYPE]) + self._triggers[trigger_key] = trigger_data + iid = trigger_data["characteristic"] + self._iid_trigger_keys.setdefault(iid, set()).add(trigger_key) + await connection.add_watchable_characteristics([(aid, iid)]) + + def fire(self, iid: int, value: dict[str, Any]) -> None: """Process events that have been received from a HomeKit accessory.""" - for event_handler in self._callbacks.get(iid, []): - event_handler(value) + for trigger_key in self._iid_trigger_keys.get(iid, set()): + for event_handler in self._callbacks.get(trigger_key, []): + event_handler(value) def async_get_triggers(self) -> Generator[tuple[str, str], None, None]: - """List device triggers for homekit devices.""" + """List device triggers for HomeKit devices.""" yield from self._triggers - async def async_attach_trigger( + @callback + def async_attach_trigger( self, config: ConfigType, action: TriggerActionType, @@ -86,28 +99,25 @@ class TriggerSource: ) -> CALLBACK_TYPE: """Attach a trigger.""" trigger_data = trigger_info["trigger_data"] + trigger_key = (config[CONF_TYPE], config[CONF_SUBTYPE]) job = HassJob(action) @callback - def event_handler(char): + def event_handler(char: dict[str, Any]) -> None: if config[CONF_SUBTYPE] != HK_TO_HA_INPUT_EVENT_VALUES[char["value"]]: return self._hass.async_run_hass_job(job, {"trigger": {**trigger_data, **config}}) - trigger = self._triggers[config[CONF_TYPE], config[CONF_SUBTYPE]] - iid = trigger["characteristic"] - - await self._connection.add_watchable_characteristics([(self._aid, iid)]) - self._callbacks.setdefault(iid, []).append(event_handler) + self._callbacks.setdefault(trigger_key, []).append(event_handler) def async_remove_handler(): - if iid in self._callbacks: - self._callbacks[iid].remove(event_handler) + if trigger_key in self._callbacks: + self._callbacks[trigger_key].remove(event_handler) return async_remove_handler -def enumerate_stateless_switch(service): +def enumerate_stateless_switch(service: Service) -> list[dict[str, Any]]: """Enumerate a stateless switch, like a single button.""" # A stateless switch that has a SERVICE_LABEL_INDEX is part of a group @@ -135,7 +145,7 @@ def enumerate_stateless_switch(service): ] -def enumerate_stateless_switch_group(service): +def enumerate_stateless_switch_group(service: Service) -> list[dict[str, Any]]: """Enumerate a group of stateless switches, like a remote control.""" switches = list( service.accessory.services.filter( @@ -165,7 +175,7 @@ def enumerate_stateless_switch_group(service): return results -def enumerate_doorbell(service): +def enumerate_doorbell(service: Service) -> list[dict[str, Any]]: """Enumerate doorbell buttons.""" input_event = service[CharacteristicsTypes.INPUT_EVENT] @@ -217,21 +227,32 @@ async def async_setup_triggers_for_entry( if device_id in hass.data[TRIGGERS]: return False - # Just because we recognise the service type doesn't mean we can actually + # Just because we recognize the service type doesn't mean we can actually # extract any triggers - so only proceed if we can triggers = TRIGGER_FINDERS[service_type](service) if len(triggers) == 0: return False - trigger = TriggerSource(conn, aid, triggers) - hass.data[TRIGGERS][device_id] = trigger + trigger = async_get_or_create_trigger_source(conn.hass, device_id) + hass.async_create_task(trigger.async_setup(conn, aid, triggers)) return True conn.add_listener(async_add_service) -def async_fire_triggers(conn: HKDevice, events: dict[tuple[int, int], Any]): +@callback +def async_get_or_create_trigger_source( + hass: HomeAssistant, device_id: str +) -> TriggerSource: + """Get or create a trigger source for a device id.""" + if not (source := hass.data[TRIGGERS].get(device_id)): + source = TriggerSource(hass) + hass.data[TRIGGERS][device_id] = source + return source + + +def async_fire_triggers(conn: HKDevice, events: dict[tuple[int, int], dict[str, Any]]): """Process events generated by a HomeKit accessory into automation triggers.""" trigger_sources: dict[str, TriggerSource] = conn.hass.data[TRIGGERS] for (aid, iid), ev in events.items(): @@ -271,5 +292,6 @@ async def async_attach_trigger( ) -> CALLBACK_TYPE: """Attach a trigger.""" device_id = config[CONF_DEVICE_ID] - device = hass.data[TRIGGERS][device_id] - return await device.async_attach_trigger(config, action, trigger_info) + return async_get_or_create_trigger_source(hass, device_id).async_attach_trigger( + config, action, trigger_info + ) diff --git a/tests/components/homekit_controller/test_device_trigger.py b/tests/components/homekit_controller/test_device_trigger.py index a09525d9dec..6f17f5db786 100644 --- a/tests/components/homekit_controller/test_device_trigger.py +++ b/tests/components/homekit_controller/test_device_trigger.py @@ -6,6 +6,7 @@ import pytest import homeassistant.components.automation as automation from homeassistant.components.device_automation import DeviceAutomationType from homeassistant.components.homekit_controller.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.setup import async_setup_component @@ -338,3 +339,129 @@ async def test_handle_events(hass, utcnow, calls): await hass.async_block_till_done() assert len(calls) == 2 + + +async def test_handle_events_late_setup(hass, utcnow, calls): + """Test that events are handled when setup happens after startup.""" + helper = await setup_test_component(hass, create_remote) + + entity_registry = er.async_get(hass) + entry = entity_registry.async_get("sensor.testdevice_battery") + + device_registry = dr.async_get(hass) + device = device_registry.async_get(entry.device_id) + + await hass.config_entries.async_unload(helper.config_entry.entry_id) + await hass.async_block_till_done() + assert helper.config_entry.state == ConfigEntryState.NOT_LOADED + + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + { + "alias": "single_press", + "trigger": { + "platform": "device", + "domain": DOMAIN, + "device_id": device.id, + "type": "button1", + "subtype": "single_press", + }, + "action": { + "service": "test.automation", + "data_template": { + "some": ( + "{{ trigger.platform}} - " + "{{ trigger.type }} - {{ trigger.subtype }} - " + "{{ trigger.id }}" + ) + }, + }, + }, + { + "alias": "long_press", + "trigger": { + "platform": "device", + "domain": DOMAIN, + "device_id": device.id, + "type": "button2", + "subtype": "long_press", + }, + "action": { + "service": "test.automation", + "data_template": { + "some": ( + "{{ trigger.platform}} - " + "{{ trigger.type }} - {{ trigger.subtype }} - " + "{{ trigger.id }}" + ) + }, + }, + }, + ] + }, + ) + await hass.async_block_till_done() + + await hass.config_entries.async_setup(helper.config_entry.entry_id) + await hass.async_block_till_done() + assert helper.config_entry.state == ConfigEntryState.LOADED + + # Make sure first automation (only) fires for single press + helper.pairing.testing.update_named_service( + "Button 1", {CharacteristicsTypes.INPUT_EVENT: 0} + ) + + await hass.async_block_till_done() + assert len(calls) == 1 + assert calls[0].data["some"] == "device - button1 - single_press - 0" + + # Make sure automation doesn't trigger for long press + helper.pairing.testing.update_named_service( + "Button 1", {CharacteristicsTypes.INPUT_EVENT: 1} + ) + + await hass.async_block_till_done() + assert len(calls) == 1 + + # Make sure automation doesn't trigger for double press + helper.pairing.testing.update_named_service( + "Button 1", {CharacteristicsTypes.INPUT_EVENT: 2} + ) + + await hass.async_block_till_done() + assert len(calls) == 1 + + # Make sure second automation fires for long press + helper.pairing.testing.update_named_service( + "Button 2", {CharacteristicsTypes.INPUT_EVENT: 2} + ) + + await hass.async_block_till_done() + assert len(calls) == 2 + assert calls[1].data["some"] == "device - button2 - long_press - 0" + + # Turn the automations off + await hass.services.async_call( + "automation", + "turn_off", + {"entity_id": "automation.long_press"}, + blocking=True, + ) + + await hass.services.async_call( + "automation", + "turn_off", + {"entity_id": "automation.single_press"}, + blocking=True, + ) + + # Make sure event no longer fires + helper.pairing.testing.update_named_service( + "Button 2", {CharacteristicsTypes.INPUT_EVENT: 2} + ) + + await hass.async_block_till_done() + assert len(calls) == 2 From 9390359fe33021e3a3887ffd89a09247d035faf0 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Sun, 27 Nov 2022 04:04:22 +0800 Subject: [PATCH 0749/1033] Bump httpx to 0.23.1 (#82725) --- homeassistant/package_constraints.txt | 8 ++++---- pyproject.toml | 2 +- requirements.txt | 2 +- requirements_test.txt | 2 +- script/gen_requirements_all.py | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 2c46489598c..ecd9f9dc333 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -23,7 +23,7 @@ fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.8.1 home-assistant-frontend==20221108.0 -httpx==0.23.0 +httpx==0.23.1 ifaddr==0.1.7 jinja2==3.1.2 lru-dict==1.1.8 @@ -87,9 +87,9 @@ regex==2021.8.28 # these requirements are quite loose. As the entire stack has some outstanding issues, and # even newer versions seem to introduce new issues, it's useful for us to pin all these # requirements so we can directly link HA versions to these library versions. -anyio==3.6.1 -h11==0.12.0 -httpcore==0.15.0 +anyio==3.6.2 +h11==0.14.0 +httpcore==0.16.2 # Ensure we have a hyperframe version that works in Python 3.10 # 5.2.0 fixed a collections abc deprecation diff --git a/pyproject.toml b/pyproject.toml index a32414da464..41d8d1b50f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ dependencies = [ "ciso8601==2.2.0", # When bumping httpx, please check the version pins of # httpcore, anyio, and h11 in gen_requirements_all - "httpx==0.23.0", + "httpx==0.23.1", "home-assistant-bluetooth==1.8.1", "ifaddr==0.1.7", "jinja2==3.1.2", diff --git a/requirements.txt b/requirements.txt index ba4d9fad807..eca20f3047b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ awesomeversion==22.9.0 bcrypt==3.1.7 certifi>=2021.5.30 ciso8601==2.2.0 -httpx==0.23.0 +httpx==0.23.1 home-assistant-bluetooth==1.8.1 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_test.txt b/requirements_test.txt index b4bfaa3038d..38ea4aa37ab 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -26,7 +26,7 @@ pytest-timeout==2.1.0 pytest-xdist==2.5.0 pytest==7.2.0 requests_mock==1.10.0 -respx==0.19.2 +respx==0.20.1 stdlib-list==0.7.0 tomli==2.0.1;python_version<"3.11" tqdm==4.64.0 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 264d9ff9f8a..ea77a99d2a5 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -99,9 +99,9 @@ regex==2021.8.28 # these requirements are quite loose. As the entire stack has some outstanding issues, and # even newer versions seem to introduce new issues, it's useful for us to pin all these # requirements so we can directly link HA versions to these library versions. -anyio==3.6.1 -h11==0.12.0 -httpcore==0.15.0 +anyio==3.6.2 +h11==0.14.0 +httpcore==0.16.2 # Ensure we have a hyperframe version that works in Python 3.10 # 5.2.0 fixed a collections abc deprecation From 6cfb40f080279a45c8e630419742cc2f3faedecb Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 26 Nov 2022 22:14:11 +0100 Subject: [PATCH 0750/1033] Bump pysensibo to 1.0.22 (#82738) fixes undefined --- homeassistant/components/sensibo/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensibo/manifest.json b/homeassistant/components/sensibo/manifest.json index af0a4b00f23..e685e80718f 100644 --- a/homeassistant/components/sensibo/manifest.json +++ b/homeassistant/components/sensibo/manifest.json @@ -2,7 +2,7 @@ "domain": "sensibo", "name": "Sensibo", "documentation": "https://www.home-assistant.io/integrations/sensibo", - "requirements": ["pysensibo==1.0.21"], + "requirements": ["pysensibo==1.0.22"], "config_flow": true, "codeowners": ["@andrey-git", "@gjohansson-ST"], "iot_class": "cloud_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 02b27161e61..72f84dbda08 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1878,7 +1878,7 @@ pysaj==0.0.16 pysdcp==1 # homeassistant.components.sensibo -pysensibo==1.0.21 +pysensibo==1.0.22 # homeassistant.components.serial # homeassistant.components.zha diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e5421ba6069..fae1fafb2f2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1328,7 +1328,7 @@ pyruckus==0.16 pysabnzbd==1.1.1 # homeassistant.components.sensibo -pysensibo==1.0.21 +pysensibo==1.0.22 # homeassistant.components.serial # homeassistant.components.zha From c2d52f8fcbb21e5c360f7aa20db351a3f85d0587 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 26 Nov 2022 23:05:54 +0100 Subject: [PATCH 0751/1033] Fix duplicate "devices" in DNSIP (#82741) * duplicate device * clean --- homeassistant/components/dnsip/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/dnsip/sensor.py b/homeassistant/components/dnsip/sensor.py index 93bf73f1b9d..bbc15f1b139 100644 --- a/homeassistant/components/dnsip/sensor.py +++ b/homeassistant/components/dnsip/sensor.py @@ -74,7 +74,7 @@ class WanIpSensor(SensorEntity): } self._attr_device_info = DeviceInfo( entry_type=DeviceEntryType.SERVICE, - identifiers={(DOMAIN, f"{hostname}_{ipv6}")}, + identifiers={(DOMAIN, hostname)}, manufacturer="DNS", model=aiodns.__version__, name=name, From 528a4819f084bdf634e30eba818087e391bb606d Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 26 Nov 2022 23:06:49 +0100 Subject: [PATCH 0752/1033] Bump pytrafikverket to 0.2.2 (#82739) Bump pytrafikverket 0.2.2 --- homeassistant/components/trafikverket_ferry/manifest.json | 2 +- homeassistant/components/trafikverket_train/manifest.json | 2 +- .../components/trafikverket_weatherstation/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/trafikverket_ferry/manifest.json b/homeassistant/components/trafikverket_ferry/manifest.json index 47b5784296d..26f395debb9 100644 --- a/homeassistant/components/trafikverket_ferry/manifest.json +++ b/homeassistant/components/trafikverket_ferry/manifest.json @@ -2,7 +2,7 @@ "domain": "trafikverket_ferry", "name": "Trafikverket Ferry", "documentation": "https://www.home-assistant.io/integrations/trafikverket_ferry", - "requirements": ["pytrafikverket==0.2.1"], + "requirements": ["pytrafikverket==0.2.2"], "codeowners": ["@gjohansson-ST"], "config_flow": true, "iot_class": "cloud_polling", diff --git a/homeassistant/components/trafikverket_train/manifest.json b/homeassistant/components/trafikverket_train/manifest.json index d8ccd62f956..16a125f9561 100644 --- a/homeassistant/components/trafikverket_train/manifest.json +++ b/homeassistant/components/trafikverket_train/manifest.json @@ -2,7 +2,7 @@ "domain": "trafikverket_train", "name": "Trafikverket Train", "documentation": "https://www.home-assistant.io/integrations/trafikverket_train", - "requirements": ["pytrafikverket==0.2.1"], + "requirements": ["pytrafikverket==0.2.2"], "codeowners": ["@endor-force", "@gjohansson-ST"], "config_flow": true, "iot_class": "cloud_polling", diff --git a/homeassistant/components/trafikverket_weatherstation/manifest.json b/homeassistant/components/trafikverket_weatherstation/manifest.json index fbe2435f841..a1d9ca21c02 100644 --- a/homeassistant/components/trafikverket_weatherstation/manifest.json +++ b/homeassistant/components/trafikverket_weatherstation/manifest.json @@ -2,7 +2,7 @@ "domain": "trafikverket_weatherstation", "name": "Trafikverket Weather Station", "documentation": "https://www.home-assistant.io/integrations/trafikverket_weatherstation", - "requirements": ["pytrafikverket==0.2.1"], + "requirements": ["pytrafikverket==0.2.2"], "codeowners": ["@endor-force", "@gjohansson-ST"], "config_flow": true, "iot_class": "cloud_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 72f84dbda08..3dc976ef1ec 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2086,7 +2086,7 @@ pytradfri[async]==9.0.0 # homeassistant.components.trafikverket_ferry # homeassistant.components.trafikverket_train # homeassistant.components.trafikverket_weatherstation -pytrafikverket==0.2.1 +pytrafikverket==0.2.2 # homeassistant.components.usb pyudev==0.23.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fae1fafb2f2..7ea1cafa666 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1449,7 +1449,7 @@ pytradfri[async]==9.0.0 # homeassistant.components.trafikverket_ferry # homeassistant.components.trafikverket_train # homeassistant.components.trafikverket_weatherstation -pytrafikverket==0.2.1 +pytrafikverket==0.2.2 # homeassistant.components.usb pyudev==0.23.2 From 4becb9454b4ef4331ec73df17e13ee076cc205c8 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 27 Nov 2022 00:26:07 +0000 Subject: [PATCH 0753/1033] [ci skip] Translation update --- .../accuweather/translations/id.json | 6 +++ .../accuweather/translations/pt-BR.json | 6 +++ .../accuweather/translations/zh-Hans.json | 10 +++++ .../airzone/translations/zh-Hans.json | 18 +++++++++ .../components/climate/translations/et.json | 4 +- .../components/demo/translations/pt-BR.json | 8 ++++ .../components/demo/translations/zh-Hans.json | 10 +++++ .../components/esphome/translations/nl.json | 1 + .../forecast_solar/translations/es.json | 3 ++ .../forecast_solar/translations/id.json | 3 ++ .../forecast_solar/translations/pt-BR.json | 3 ++ .../forecast_solar/translations/zh-Hans.json | 3 ++ .../harmony/translations/select.zh-Hans.json | 7 ++++ .../homeassistant/translations/nl.json | 6 +++ .../homeassistant_yellow/translations/nl.json | 30 ++++++++++++++ .../translations/zh-Hans.json | 10 +++++ .../components/min_max/translations/id.json | 10 ++--- .../min_max/translations/pt-BR.json | 10 ++--- .../components/mqtt/translations/de.json | 4 +- .../components/mqtt/translations/es.json | 4 +- .../components/mqtt/translations/et.json | 2 +- .../components/mqtt/translations/id.json | 4 +- .../components/mqtt/translations/pt-BR.json | 4 +- .../components/mqtt/translations/zh-Hans.json | 12 +++++- .../components/onvif/translations/nl.json | 3 +- .../components/plugwise/translations/id.json | 4 +- .../plugwise/translations/pt-BR.json | 6 ++- .../plugwise/translations/zh-Hans.json | 8 ++++ .../components/radarr/translations/id.json | 4 ++ .../components/radarr/translations/pt-BR.json | 4 ++ .../radarr/translations/zh-Hans.json | 6 +++ .../components/scrape/translations/nl.json | 2 +- .../scrape/translations/zh-Hans.json | 39 +++++++++++++++++++ .../components/shelly/translations/nl.json | 13 +++++++ .../components/text/translations/id.json | 3 ++ .../components/text/translations/pt-BR.json | 3 ++ .../components/text/translations/zh-Hans.json | 3 ++ .../unifiprotect/translations/nl.json | 21 ++++++++++ .../wake_on_lan/translations/nl.json | 8 ++++ 39 files changed, 277 insertions(+), 28 deletions(-) create mode 100644 homeassistant/components/airzone/translations/zh-Hans.json create mode 100644 homeassistant/components/harmony/translations/select.zh-Hans.json create mode 100644 homeassistant/components/homeassistant_yellow/translations/zh-Hans.json create mode 100644 homeassistant/components/plugwise/translations/zh-Hans.json create mode 100644 homeassistant/components/scrape/translations/zh-Hans.json create mode 100644 homeassistant/components/text/translations/id.json create mode 100644 homeassistant/components/text/translations/pt-BR.json create mode 100644 homeassistant/components/text/translations/zh-Hans.json create mode 100644 homeassistant/components/wake_on_lan/translations/nl.json diff --git a/homeassistant/components/accuweather/translations/id.json b/homeassistant/components/accuweather/translations/id.json index 1cd49752342..72a4fab86f8 100644 --- a/homeassistant/components/accuweather/translations/id.json +++ b/homeassistant/components/accuweather/translations/id.json @@ -24,6 +24,12 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "Prakiraan cuaca" + }, + "description": "Karena keterbatasan versi gratis kunci API AccuWeather, ketika Anda mengaktifkan prakiraan cuaca, pembaruan data akan dilakukan setiap 80 menit, bukan setiap 40 menit." + }, "user": { "data": { "forecast": "Prakiraan cuaca" diff --git a/homeassistant/components/accuweather/translations/pt-BR.json b/homeassistant/components/accuweather/translations/pt-BR.json index 3fc0d95ad2a..726de96c37d 100644 --- a/homeassistant/components/accuweather/translations/pt-BR.json +++ b/homeassistant/components/accuweather/translations/pt-BR.json @@ -24,6 +24,12 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "Previs\u00e3o do tempo" + }, + "description": "Devido \u00e0s limita\u00e7\u00f5es da vers\u00e3o gratuita da chave API AccuWeather, quando voc\u00ea ativa a previs\u00e3o do tempo, as atualiza\u00e7\u00f5es de dados s\u00e3o realizadas a cada 80 minutos em vez de 40 minutos." + }, "user": { "data": { "forecast": "Previs\u00e3o do Tempo" diff --git a/homeassistant/components/accuweather/translations/zh-Hans.json b/homeassistant/components/accuweather/translations/zh-Hans.json index f8879f5715f..248c9ff8e55 100644 --- a/homeassistant/components/accuweather/translations/zh-Hans.json +++ b/homeassistant/components/accuweather/translations/zh-Hans.json @@ -1,4 +1,14 @@ { + "options": { + "step": { + "init": { + "data": { + "forecast": "\u5929\u6c14\u9884\u62a5" + }, + "description": "\u7531\u4e8e AccuWeather API \u5bc6\u94a5\u514d\u8d39\u7248\u672c\u7684\u9650\u5236\uff0c\u5f53\u60a8\u542f\u7528\u5929\u6c14\u9884\u62a5\u65f6\uff0c\u6570\u636e\u66f4\u65b0\u5c06\u6bcf 80 \u5206\u949f\u6267\u884c\u4e00\u6b21\uff0c\u800c\u4e0d\u662f\u6bcf 40 \u5206\u949f\u4e00\u6b21\u3002" + } + } + }, "system_health": { "info": { "can_reach_server": "\u53ef\u8bbf\u95ee AccuWeather \u670d\u52a1\u5668", diff --git a/homeassistant/components/airzone/translations/zh-Hans.json b/homeassistant/components/airzone/translations/zh-Hans.json new file mode 100644 index 00000000000..17253bcf52f --- /dev/null +++ b/homeassistant/components/airzone/translations/zh-Hans.json @@ -0,0 +1,18 @@ +{ + "config": { + "step": { + "discovered_connection": { + "data": { + "host": "\u4e3b\u673a\u5730\u5740", + "id": "System ID", + "port": "\u7aef\u53e3\u53f7" + } + }, + "user": { + "data": { + "id": "System ID" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climate/translations/et.json b/homeassistant/components/climate/translations/et.json index c57a4d72991..2f947675510 100644 --- a/homeassistant/components/climate/translations/et.json +++ b/homeassistant/components/climate/translations/et.json @@ -20,8 +20,8 @@ "cool": "Jahuta", "dry": "Kuivata", "fan_only": "Ventileeri", - "heat": "Soojenda", - "heat_cool": "K\u00fcta/jahuta", + "heat": "K\u00fcta", + "heat_cool": "K\u00fcta", "off": "V\u00e4ljas" } }, diff --git a/homeassistant/components/demo/translations/pt-BR.json b/homeassistant/components/demo/translations/pt-BR.json index e38a9a1c7aa..16e4bb1396d 100644 --- a/homeassistant/components/demo/translations/pt-BR.json +++ b/homeassistant/components/demo/translations/pt-BR.json @@ -11,6 +11,14 @@ }, "title": "A fonte de alimenta\u00e7\u00e3o n\u00e3o \u00e9 est\u00e1vel" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "N\u00e3o \u00e9 poss\u00edvel reaquecer o ch\u00e1 neste momento" + } + }, + "title": "O ch\u00e1 est\u00e1 frio" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/demo/translations/zh-Hans.json b/homeassistant/components/demo/translations/zh-Hans.json index 1c40afabb6e..69c695d6f6d 100644 --- a/homeassistant/components/demo/translations/zh-Hans.json +++ b/homeassistant/components/demo/translations/zh-Hans.json @@ -1,4 +1,14 @@ { + "issues": { + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "\u76ee\u524d\u65e0\u6cd5\u91cd\u65b0\u52a0\u70ed\u8336" + } + }, + "title": "\u8336\u51c9\u4e86" + } + }, "options": { "step": { "options_1": { diff --git a/homeassistant/components/esphome/translations/nl.json b/homeassistant/components/esphome/translations/nl.json index f8410bd73ef..410850916ea 100644 --- a/homeassistant/components/esphome/translations/nl.json +++ b/homeassistant/components/esphome/translations/nl.json @@ -46,6 +46,7 @@ }, "issues": { "ble_firmware_outdated": { + "description": "Om de Bluetooth betrouwbaarheid en performance te verbeteren, raden we sterk aan om {name} bij te werken met ESPHome 2022.11.0 of later.", "title": "Werk {name} bij naar ESPHome versie 2022.11.0 of later" } } diff --git a/homeassistant/components/forecast_solar/translations/es.json b/homeassistant/components/forecast_solar/translations/es.json index a2f08dbc489..227eeab1351 100644 --- a/homeassistant/components/forecast_solar/translations/es.json +++ b/homeassistant/components/forecast_solar/translations/es.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Clave API no v\u00e1lida" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/id.json b/homeassistant/components/forecast_solar/translations/id.json index 9c30668d1de..66b993e3ae6 100644 --- a/homeassistant/components/forecast_solar/translations/id.json +++ b/homeassistant/components/forecast_solar/translations/id.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Kunci API tidak valid" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/pt-BR.json b/homeassistant/components/forecast_solar/translations/pt-BR.json index a6be7f053e1..2fe7ed3e900 100644 --- a/homeassistant/components/forecast_solar/translations/pt-BR.json +++ b/homeassistant/components/forecast_solar/translations/pt-BR.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Chave de API inv\u00e1lida" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/zh-Hans.json b/homeassistant/components/forecast_solar/translations/zh-Hans.json index 8a667cf9260..7d2e7846f2f 100644 --- a/homeassistant/components/forecast_solar/translations/zh-Hans.json +++ b/homeassistant/components/forecast_solar/translations/zh-Hans.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "\u65e0\u6548\u7684API\u5bc6\u94a5" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/harmony/translations/select.zh-Hans.json b/homeassistant/components/harmony/translations/select.zh-Hans.json new file mode 100644 index 00000000000..ee5367ba9c6 --- /dev/null +++ b/homeassistant/components/harmony/translations/select.zh-Hans.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "\u5173\u95ed" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant/translations/nl.json b/homeassistant/components/homeassistant/translations/nl.json index 0bafc7a3959..156c6fe4f1e 100644 --- a/homeassistant/components/homeassistant/translations/nl.json +++ b/homeassistant/components/homeassistant/translations/nl.json @@ -1,4 +1,10 @@ { + "issues": { + "python_version": { + "description": "Ondersteuning om Home Assistant met de huidige Python (versie {current_python_version}) gaat vervallen en zal vervalleen in Home Assistant {breaks_in_ha_version}. Werk de Python software bij naar minimaal versie {required_python_version} om te voorkomen dat je Home Assistant instantie niet meer goed werkt.", + "title": "Ondersteuning voor Python {current_python_version} gaat vervallen" + } + }, "system_health": { "info": { "arch": "CPU-architectuur", diff --git a/homeassistant/components/homeassistant_yellow/translations/nl.json b/homeassistant/components/homeassistant_yellow/translations/nl.json index 2ac32361c06..ee6cec8c81c 100644 --- a/homeassistant/components/homeassistant_yellow/translations/nl.json +++ b/homeassistant/components/homeassistant_yellow/translations/nl.json @@ -1,10 +1,40 @@ { "options": { "abort": { + "addon_info_failed": "Kan geen add-on informatie opvragen voor de Silicon Labs Multiprotocol add-on.", + "addon_install_failed": "Het installeren van de SIlicon Labs Multiprotocol add-on is mislukt.", + "addon_set_config_failed": "Het instellen van de Silicon Labs Multiprotocol configuratie is mislukt.", + "addon_start_failed": "Het opstarten van de Silicon Labs Multiprotocol add-on is mislukt.", + "not_hassio": "De hardware opties kunnen alleen worden ingesteld voor HassOS installaties.", "zha_migration_failed": "De ZHA migratie is niet gelukt." }, "error": { "unknown": "Onverwachte fout" + }, + "progress": { + "install_addon": "Wacht even tot de Silicon Labs Multiprotocol add-on installatie is voltooid. Dit kan enige minuten duren.", + "start_addon": "Wacht even tot de Silicon Labs Multiprotocol add-on is opgestart. Dit kan enige seconden duren." + }, + "step": { + "addon_installed_other_device": { + "title": "Multi-protocol ondersteuning is al voor een ander apparaat ingeschakeld" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Inschakelen multi-protocol ondersteuning" + }, + "description": "Wanneer multi-protocol ondersteuning is ingeschakeld, zal het Home Assistant Yellow IEEE 802.15.4 toegangspunt gelijktijdig voor zowel Zigbee als Thread (gebruikt door Matter) ingezet worden. Als het toegangspunt als wordt gebruikt door de ZHA Zigbee integratie, dan zal, ZHA worden be ge-reconfigureerd om de multi-protocol firmware te gaan gebruiken.\n\nOpmerking: Dit is een experimentele functie.", + "title": "Inschakelen multi-protocol ondersteuning op het IEEE 802.15.4 toegangspunt" + }, + "install_addon": { + "title": "De Silicon Labs Multiprotocol add-on is gestart" + }, + "show_revert_guide": { + "description": "Als je naar Zigbee-only firmware wilt overstappen, voltooi dan de volgende handmatig stappen:\n\n * Verwijderd de Silicon Labs Multiprotocol addon\n\n * Flash de Zigbee-only firmware, volg de aanwijzigingen op https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually\n\n * Herconfigureer ZHA om de instellingen te migreren naar het ge-reflashed toegangspunt" + }, + "start_addon": { + "title": "De Silicon Labs Multiprotocol add-on is aan het opstarten." + } } } } \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/zh-Hans.json b/homeassistant/components/homeassistant_yellow/translations/zh-Hans.json new file mode 100644 index 00000000000..af82bdcca0a --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/zh-Hans.json @@ -0,0 +1,10 @@ +{ + "options": { + "abort": { + "zha_migration_failed": "ZHA \u8fc1\u79fb\u672a\u6210\u529f\u3002" + }, + "error": { + "unknown": "\u610f\u6599\u4e4b\u5916\u7684\u9519\u8bef" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/id.json b/homeassistant/components/min_max/translations/id.json index 9e06b47de95..8ea76d6f73c 100644 --- a/homeassistant/components/min_max/translations/id.json +++ b/homeassistant/components/min_max/translations/id.json @@ -9,10 +9,10 @@ "type": "Karakteristik statistik" }, "data_description": { - "round_digits": "Mengontrol jumlah digit desimal dalam output ketika karakteristik statistik adalah rata-rata atau median." + "round_digits": "Mengontrol jumlah digit desimal dalam output ketika karakteristik statistik adalah rata-rata, median, atau jumlah." }, - "description": "Buat sensor yang menghitung nilai min, maks, rata-rata, atau median dari sejumlah sensor input.", - "title": "Tambahkan sensor min/maks/rata-rata/median" + "description": "Buat sensor yang menghitung nilai min, maks, rata-rata, median, atau jumlah dari sejumlah sensor input.", + "title": "Gabungkan status beberapa sensor" } } }, @@ -25,10 +25,10 @@ "type": "Karakteristik statistik" }, "data_description": { - "round_digits": "Mengontrol jumlah digit desimal dalam output ketika karakteristik statistik adalah rata-rata atau median." + "round_digits": "Mengontrol jumlah digit desimal dalam output ketika karakteristik statistik adalah rata-rata, median, atau jumlah." } } } }, - "title": "Sensor min/maks/rata-rata/median" + "title": "Gabungkan status beberapa sensor" } \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/pt-BR.json b/homeassistant/components/min_max/translations/pt-BR.json index 5a2c9a85d4f..49a3a018058 100644 --- a/homeassistant/components/min_max/translations/pt-BR.json +++ b/homeassistant/components/min_max/translations/pt-BR.json @@ -9,10 +9,10 @@ "type": "Caracter\u00edstica estat\u00edstica" }, "data_description": { - "round_digits": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda quando a caracter\u00edstica estat\u00edstica \u00e9 m\u00e9dia ou mediana." + "round_digits": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda quando a caracter\u00edstica estat\u00edstica \u00e9 m\u00e9dia, mediana ou soma." }, - "description": "Crie um sensor que calcula um valor m\u00ednimo, m\u00e1ximo, m\u00e9dio ou mediano de uma lista de sensores de entrada.", - "title": "Adicionar sensor de m\u00ednima / m\u00e1xima / m\u00e9dia / mediana" + "description": "Crie um sensor que calcule um m\u00ednimo, m\u00e1ximo, m\u00e9dia, mediana ou soma de uma lista de sensores de entrada.", + "title": "Combine o estado de v\u00e1rios sensores" } } }, @@ -25,10 +25,10 @@ "type": "Caracter\u00edstica estat\u00edstica" }, "data_description": { - "round_digits": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda quando a caracter\u00edstica estat\u00edstica \u00e9 m\u00e9dia ou mediana." + "round_digits": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda quando a caracter\u00edstica estat\u00edstica \u00e9 m\u00e9dia, mediana ou soma." } } } }, - "title": "Sensor m\u00edn./m\u00e1x./m\u00e9dio/mediano" + "title": "Combine o estado de v\u00e1rios sensores" } \ No newline at end of file diff --git a/homeassistant/components/mqtt/translations/de.json b/homeassistant/components/mqtt/translations/de.json index d23d944c958..12c85528f5d 100644 --- a/homeassistant/components/mqtt/translations/de.json +++ b/homeassistant/components/mqtt/translations/de.json @@ -8,7 +8,7 @@ "bad_birth": "Ung\u00fcltiges \u201eBirth\u201c-Thema", "bad_certificate": "Das CA-Zertifikat ist ung\u00fcltig", "bad_client_cert": "Ung\u00fcltiges Client-Zertifikat. Stelle sicher, dass eine PEM-codierte Datei bereitgestellt wird", - "bad_client_cert_key": "Client-Zertifikat und privates Zertifikat sind kein g\u00fcltiges Paar", + "bad_client_cert_key": "Client-Zertifikat und privater Schl\u00fcssel sind kein g\u00fcltiges Paar", "bad_client_key": "Ung\u00fcltiger privater Schl\u00fcssel. Stelle sicher, dass eine PEM-codierte Datei ohne Passwort bereitgestellt wird", "bad_discovery_prefix": "Ung\u00fcltiges Discovery-Pr\u00e4fix", "bad_will": "Ung\u00fcltiges \u201eWill\u201c-Thema", @@ -85,7 +85,7 @@ "bad_birth": "Ung\u00fcltiges \u201eBirth\u201c-Thema", "bad_certificate": "Das CA-Zertifikat ist ung\u00fcltig", "bad_client_cert": "Ung\u00fcltiges Client-Zertifikat. Stelle sicher, dass eine PEM-codierte Datei bereitgestellt wird", - "bad_client_cert_key": "Client-Zertifikat und privates Zertifikat sind kein g\u00fcltiges Paar", + "bad_client_cert_key": "Client-Zertifikat und privater Schl\u00fcssel sind kein g\u00fcltiges Paar", "bad_client_key": "Ung\u00fcltiger privater Schl\u00fcssel. Stelle sicher, dass eine PEM-codierte Datei ohne Passwort bereitgestellt wird", "bad_discovery_prefix": "Ung\u00fcltiges Discovery-Pr\u00e4fix", "bad_will": "Ung\u00fcltiges \u201eWill\u201c-Thema", diff --git a/homeassistant/components/mqtt/translations/es.json b/homeassistant/components/mqtt/translations/es.json index 95680744242..ea3753b3bd7 100644 --- a/homeassistant/components/mqtt/translations/es.json +++ b/homeassistant/components/mqtt/translations/es.json @@ -8,7 +8,7 @@ "bad_birth": "Tema de nacimiento no v\u00e1lido", "bad_certificate": "El certificado de la CA no es v\u00e1lido", "bad_client_cert": "Certificado de cliente no v\u00e1lido, aseg\u00farate de proporcionar un archivo codificado PEM", - "bad_client_cert_key": "Certificado de cliente y la clave privada no son un par v\u00e1lido", + "bad_client_cert_key": "El certificado de cliente y la clave privada no son un par v\u00e1lido", "bad_client_key": "Clave privada no v\u00e1lida, aseg\u00farate de que se proporcione un archivo codificado PEM sin contrase\u00f1a", "bad_discovery_prefix": "Prefijo de descubrimiento no v\u00e1lido", "bad_will": "Tema de voluntad no v\u00e1lido", @@ -85,7 +85,7 @@ "bad_birth": "Tema de nacimiento no v\u00e1lido", "bad_certificate": "El certificado de la CA no es v\u00e1lido", "bad_client_cert": "Certificado de cliente no v\u00e1lido, aseg\u00farate de proporcionar un archivo codificado PEM", - "bad_client_cert_key": "Certificado de cliente y la clave privada no son un par v\u00e1lido", + "bad_client_cert_key": "El certificado de cliente y la clave privada no son un par v\u00e1lido", "bad_client_key": "Clave privada no v\u00e1lida, aseg\u00farate de que se proporcione un archivo codificado PEM sin contrase\u00f1a", "bad_discovery_prefix": "Prefijo de descubrimiento no v\u00e1lido", "bad_will": "Tema de voluntad no v\u00e1lido", diff --git a/homeassistant/components/mqtt/translations/et.json b/homeassistant/components/mqtt/translations/et.json index 677d304e21e..fad3910f91b 100644 --- a/homeassistant/components/mqtt/translations/et.json +++ b/homeassistant/components/mqtt/translations/et.json @@ -8,7 +8,7 @@ "bad_birth": "Kehtetu loomise teavitus", "bad_certificate": "CA sertifikaat on kehtetu", "bad_client_cert": "Kehtetu kliendi sertifikaat, veendu, et on esitatud PEM-kodeeritud fail", - "bad_client_cert_key": "Kliendisertifikaat ja privaatne sertifikaat ei ole kehtiv paar", + "bad_client_cert_key": "Kliendisertifikaat ja privaatne v\u00f5ti ei ole kehtiv paar", "bad_client_key": "Kehtetu privaatv\u00f5ti, veendu, et PEM-kodeeritud fail tarnitakse ilma paroolita", "bad_discovery_prefix": "Sobimatu tuvastuse eesliide", "bad_will": "Kehtetu l\u00f5petamise teavitus", diff --git a/homeassistant/components/mqtt/translations/id.json b/homeassistant/components/mqtt/translations/id.json index 6a22f1c0cbd..1dbc0710453 100644 --- a/homeassistant/components/mqtt/translations/id.json +++ b/homeassistant/components/mqtt/translations/id.json @@ -8,7 +8,7 @@ "bad_birth": "Topik birth tidak valid", "bad_certificate": "Sertifikat CA tidak valid", "bad_client_cert": "Sertifikat klien tidak valid, pastikan file dengan format PEM diberikan", - "bad_client_cert_key": "Sertifikat klien dan pribadi bukan pasangan yang valid", + "bad_client_cert_key": "Sertifikat klien dan kunci pribadi bukan pasangan yang valid", "bad_client_key": "Kunci pribadi tidak valid, pastikan file dengan format PEM diberikan tanpa kata sandi", "bad_discovery_prefix": "Prefiks topik penemuan tidak valid", "bad_will": "Topik will tidak valid", @@ -85,7 +85,7 @@ "bad_birth": "Topik birth tidak valid", "bad_certificate": "Sertifikat CA tidak valid", "bad_client_cert": "Sertifikat klien tidak valid, pastikan file dengan format PEM diberikan", - "bad_client_cert_key": "Sertifikat klien dan pribadi bukan pasangan yang valid", + "bad_client_cert_key": "Sertifikat klien dan kunci pribadi bukan pasangan yang valid", "bad_client_key": "Kunci pribadi tidak valid, pastikan file dengan format PEM diberikan tanpa kata sandi", "bad_discovery_prefix": "Prefiks topik penemuan tidak valid", "bad_will": "Topik will tidak valid", diff --git a/homeassistant/components/mqtt/translations/pt-BR.json b/homeassistant/components/mqtt/translations/pt-BR.json index 1735edfdd2a..73753557d67 100644 --- a/homeassistant/components/mqtt/translations/pt-BR.json +++ b/homeassistant/components/mqtt/translations/pt-BR.json @@ -8,7 +8,7 @@ "bad_birth": "T\u00f3pico de nascimento inv\u00e1lido", "bad_certificate": "O certificado CA \u00e9 inv\u00e1lido", "bad_client_cert": "Certificado de cliente inv\u00e1lido, certifique-se de que um arquivo codificado PEM seja fornecido", - "bad_client_cert_key": "Certificado de cliente e privado n\u00e3o s\u00e3o pares v\u00e1lidos", + "bad_client_cert_key": "O certificado do cliente e a chave privada n\u00e3o s\u00e3o um par v\u00e1lido", "bad_client_key": "Chave privada inv\u00e1lida, certifique-se de que um arquivo codificado PEM seja fornecido sem senha", "bad_discovery_prefix": "Prefixo de descoberta inv\u00e1lido", "bad_will": "T\u00f3pico de vontade inv\u00e1lido", @@ -85,7 +85,7 @@ "bad_birth": "T\u00f3pico de nascimento inv\u00e1lido", "bad_certificate": "O certificado CA \u00e9 inv\u00e1lido", "bad_client_cert": "Certificado de cliente inv\u00e1lido, certifique-se de que um arquivo codificado PEM seja fornecido", - "bad_client_cert_key": "Certificado de cliente e privado n\u00e3o s\u00e3o pares v\u00e1lidos", + "bad_client_cert_key": "O certificado do cliente e a chave privada n\u00e3o s\u00e3o um par v\u00e1lido", "bad_client_key": "Chave privada inv\u00e1lida, certifique-se de que um arquivo codificado PEM seja fornecido sem senha", "bad_discovery_prefix": "Prefixo de descoberta inv\u00e1lido", "bad_will": "T\u00f3pico de vontade inv\u00e1lido", diff --git a/homeassistant/components/mqtt/translations/zh-Hans.json b/homeassistant/components/mqtt/translations/zh-Hans.json index 5671a9085f9..245728aa64c 100644 --- a/homeassistant/components/mqtt/translations/zh-Hans.json +++ b/homeassistant/components/mqtt/translations/zh-Hans.json @@ -5,6 +5,7 @@ "single_instance_allowed": "\u8be5\u96c6\u6210\u5df2\u7ecf\u914d\u7f6e\u8fc7\u4e86\uff0c\u4e14\u53ea\u80fd\u914d\u7f6e\u4e00\u6b21\u3002\u82e5\u8981\u91cd\u65b0\u914d\u7f6e\uff0c\u8bf7\u5148\u5220\u9664\u65e7\u96c6\u6210\u3002" }, "error": { + "bad_ws_headers": "\u63d0\u4f9b\u4ee5JSON\u5bf9\u8c61\u5f62\u5f0f\u7684\u6709\u6548HTTP headers", "cannot_connect": "\u65e0\u6cd5\u8fde\u63a5\u5230\u670d\u52a1\u5668\u3002" }, "step": { @@ -13,7 +14,10 @@ "broker": "\u670d\u52a1\u5668", "password": "\u5bc6\u7801", "port": "\u7aef\u53e3", - "username": "\u7528\u6237\u540d" + "transport": "MQTT\u4f20\u8f93", + "username": "\u7528\u6237\u540d", + "ws_headers": "WebSocket headers \uff08JSON\u683c\u5f0f\uff09", + "ws_path": "WebSocket \u8def\u5f84" }, "description": "\u8bf7\u8f93\u5165\u60a8\u7684 MQTT \u670d\u52a1\u5668\u7684\u8fde\u63a5\u4fe1\u606f\u3002" }, @@ -52,6 +56,7 @@ "error": { "bad_birth": "\u51fa\u751f\u6d88\u606f\u4e3b\u9898\u65e0\u6548", "bad_will": "\u9057\u5631\u6d88\u606f\u4e3b\u9898\u65e0\u6548", + "bad_ws_headers": "\u63d0\u4f9b\u4ee5JSON\u5bf9\u8c61\u5f62\u5f0f\u7684\u6709\u6548HTTP headers", "cannot_connect": "\u8fde\u63a5\u5931\u8d25" }, "step": { @@ -60,7 +65,10 @@ "broker": "\u670d\u52a1\u5668", "password": "\u5bc6\u7801", "port": "\u7aef\u53e3", - "username": "\u7528\u6237\u540d" + "transport": "MQTT\u4f20\u8f93", + "username": "\u7528\u6237\u540d", + "ws_headers": "WebSocket headers \uff08JSON\u683c\u5f0f\uff09", + "ws_path": "WebSocket\u5730\u5740" }, "description": "\u8bf7\u8f93\u5165 MQTT \u670d\u52a1\u5668\u7684\u8fde\u63a5\u4fe1\u606f\u3002", "title": "\u670d\u52a1\u5668\u9009\u9879" diff --git a/homeassistant/components/onvif/translations/nl.json b/homeassistant/components/onvif/translations/nl.json index f76aef12557..67b1da28e8d 100644 --- a/homeassistant/components/onvif/translations/nl.json +++ b/homeassistant/components/onvif/translations/nl.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "Extra FFMPEG argumenten", - "rtsp_transport": "RTSP-transportmechanisme" + "rtsp_transport": "RTSP-transportmechanisme", + "use_wallclock_as_timestamps": "Gebruik de lokale tijd voor tijdstempels" }, "title": "ONVIF-apparaatopties" } diff --git a/homeassistant/components/plugwise/translations/id.json b/homeassistant/components/plugwise/translations/id.json index 3aabe38c8cc..71578c7462c 100644 --- a/homeassistant/components/plugwise/translations/id.json +++ b/homeassistant/components/plugwise/translations/id.json @@ -8,7 +8,9 @@ "cannot_connect": "Gagal terhubung", "invalid_auth": "Autentikasi tidak valid", "invalid_setup": "Tambahkan Adam Anda, alih-alih Anna. Baca dokumentasi integrasi Plugwise Home Assistant untuk informasi lebih lanjut", - "unknown": "Kesalahan yang tidak diharapkan" + "response_error": "Data XML tidak valid, atau indikasi kesalahan diterima", + "unknown": "Kesalahan yang tidak diharapkan", + "unsupported": "Perangkat dengan firmware yang tidak didukung" }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/pt-BR.json b/homeassistant/components/plugwise/translations/pt-BR.json index ef75a1ce686..edb638bae7a 100644 --- a/homeassistant/components/plugwise/translations/pt-BR.json +++ b/homeassistant/components/plugwise/translations/pt-BR.json @@ -7,8 +7,10 @@ "error": { "cannot_connect": "Falha ao conectar", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", - "invalid_setup": "Adicione seu Adam em vez de sua Anna, consulte a documenta\u00e7\u00e3o de integra\u00e7\u00e3o do Home Assistant Plugwise para obter mais informa\u00e7\u00f5es", - "unknown": "Erro inesperado" + "invalid_setup": "Adicione seu Adam em vez de sua Anna, veja a documenta\u00e7\u00e3o", + "response_error": "Dados XML inv\u00e1lidos ou indica\u00e7\u00e3o de erro recebido", + "unknown": "Erro inesperado", + "unsupported": "Dispositivo com firmware n\u00e3o suportado" }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/zh-Hans.json b/homeassistant/components/plugwise/translations/zh-Hans.json new file mode 100644 index 00000000000..b8ed72ea7af --- /dev/null +++ b/homeassistant/components/plugwise/translations/zh-Hans.json @@ -0,0 +1,8 @@ +{ + "config": { + "error": { + "response_error": "\u65e0\u6548\u7684 XML \u6570\u636e\uff0c\u6216\u6536\u5230\u7684\u9519\u8bef\u6307\u793a", + "unsupported": "\u8bbe\u5907\u5b89\u88c5\u4e86\u4e0d\u88ab\u652f\u6301\u7684\u56fa\u4ef6" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/radarr/translations/id.json b/homeassistant/components/radarr/translations/id.json index 108dcec98c4..97311c8f48b 100644 --- a/homeassistant/components/radarr/translations/id.json +++ b/homeassistant/components/radarr/translations/id.json @@ -30,6 +30,10 @@ "deprecated_yaml": { "description": "Proses konfigurasi Integrasi Radarr lewat YAML dalam proses penghapusan.\n\nKonfigurasi YAML yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML Integrasi Radarr dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", "title": "Konfigurasi YAML Integrasi Radarr dalam proses penghapusan" + }, + "removed_yaml": { + "description": "Proses konfigurasi Integrasi Radarr lewat YAML telah dihapus.\n\nKonfigurasi YAML yang ada tidak digunakan oleh Home Assistant.\n\nHapus konfigurasi YAML dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", + "title": "Konfigurasi YAML Integrasi Radarr telah dihapus" } }, "options": { diff --git a/homeassistant/components/radarr/translations/pt-BR.json b/homeassistant/components/radarr/translations/pt-BR.json index 544b38ec81a..a9c955561f9 100644 --- a/homeassistant/components/radarr/translations/pt-BR.json +++ b/homeassistant/components/radarr/translations/pt-BR.json @@ -30,6 +30,10 @@ "deprecated_yaml": { "description": "A configura\u00e7\u00e3o do Radarr usando YAML est\u00e1 sendo removida. \n\n Sua configura\u00e7\u00e3o YAML existente foi importada para a interface do usu\u00e1rio automaticamente. \n\n Remova a configura\u00e7\u00e3o YAML do Radarr do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", "title": "A configura\u00e7\u00e3o YAML do Radarr est\u00e1 sendo removida" + }, + "removed_yaml": { + "description": "A configura\u00e7\u00e3o do Radarr usando YAML foi removida. \n\n Sua configura\u00e7\u00e3o YAML existente n\u00e3o \u00e9 usada pelo Home Assistant. \n\n Remova a configura\u00e7\u00e3o YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", + "title": "A configura\u00e7\u00e3o YAML de Radarr foi removida" } }, "options": { diff --git a/homeassistant/components/radarr/translations/zh-Hans.json b/homeassistant/components/radarr/translations/zh-Hans.json index fe3b28ff5b6..120079d5b62 100644 --- a/homeassistant/components/radarr/translations/zh-Hans.json +++ b/homeassistant/components/radarr/translations/zh-Hans.json @@ -20,5 +20,11 @@ } } } + }, + "issues": { + "removed_yaml": { + "description": "Radarr\u7684YAML\u914d\u7f6e\u5df2\u88ab\u5220\u9664\n\nHome Assistant\u4e0d\u4f7f\u7528\u73b0\u6709\u7684 YAML \u914d\u7f6e\u3002\n\n\u4ece configuration.yaml \u6587\u4ef6\u4e2d\u5220\u9664 YAML \u914d\u7f6e\uff0c\u7136\u540e\u91cd\u65b0\u542f\u52a8Home Assistant\u4ee5\u89e3\u51b3\u6b64\u95ee\u9898\u3002", + "title": "Radarr\u7684YAML\u914d\u7f6e\u5df2\u88ab\u5220\u9664" + } } } \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/nl.json b/homeassistant/components/scrape/translations/nl.json index 3bb3fa570e5..dba74863b52 100644 --- a/homeassistant/components/scrape/translations/nl.json +++ b/homeassistant/components/scrape/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Account is al geconfigureerd" }, "error": { - "resource_error": "Kon de de REST data niet bijwerken, controleer je configuratie" + "resource_error": "Kon de REST data niet bijwerken, controleer je configuratie" }, "step": { "sensor": { diff --git a/homeassistant/components/scrape/translations/zh-Hans.json b/homeassistant/components/scrape/translations/zh-Hans.json new file mode 100644 index 00000000000..c178e103c2e --- /dev/null +++ b/homeassistant/components/scrape/translations/zh-Hans.json @@ -0,0 +1,39 @@ +{ + "config": { + "step": { + "sensor": { + "data_description": { + "attribute": "\u83b7\u53d6\u6240\u9009\u6807\u7b7e\u4e0a\u4e00\u4e2a\u5c5e\u6027\u7684\u503c", + "device_class": "\u4f20\u611f\u5668\u7c7b\u522b\uff0c\u4ee5\u4fbf\u5728\u524d\u7aef\u754c\u9762\u8bbe\u7f6e\u56fe\u6807", + "index": "\u5b9a\u4e49\u8981\u4f7f\u7528\u7684\u5143\u7d20\uff08 \u7531CSS \u9009\u62e9\u5668\u8fd4\u56de\uff09", + "select": "\u5b9a\u4e49\u8981\u641c\u7d22\u7684\u6807\u8bb0\uff08tag\uff09\u3002\u67e5\u770bBeautifulsoup CSS \u9009\u62e9\u5668\u4e86\u89e3\u8be6\u7ec6\u4fe1\u606f", + "state_class": "\u4f20\u611f\u5668\u7684state_class", + "unit_of_measurement": "\u9009\u62e9\u6216\u521b\u5efa\u6e29\u5ea6\u6d4b\u91cf\u5355\u4f4d", + "value_template": "\u5b9a\u4e49\u7528\u4e8e\u83b7\u53d6\u4f20\u611f\u5668\u72b6\u6001\u7684\u6a21\u677f" + } + }, + "user": { + "data": { + "method": "\u8bf7\u6c42\u65b9\u5f0f", + "timeout": "\u8d85\u65f6" + }, + "data_description": { + "timeout": "\u8fde\u63a5\u5230\u7f51\u7ad9\u8d85\u65f6" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "method": "\u8bf7\u6c42\u65b9\u5f0f", + "timeout": "\u8d85\u65f6" + }, + "data_description": { + "timeout": "\u8fde\u63a5\u5230\u7f51\u7ad9\u8d85\u65f6" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/nl.json b/homeassistant/components/shelly/translations/nl.json index 70ffbad804b..4d2fa88822b 100644 --- a/homeassistant/components/shelly/translations/nl.json +++ b/homeassistant/components/shelly/translations/nl.json @@ -56,5 +56,18 @@ "single_push": "{subtype} een druk", "triple": "{subtype} driemaal geklikt" } + }, + "options": { + "abort": { + "ble_unsupported": "Bluetooth ondersteuning vereist firmwareversie {ble_min_version} of later." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Bluetooth scannermodus" + }, + "description": "Het scannen van Bluetooth kan actief of passief worden ingesteld. In de actieve modus zal Shelly de gegevens opvragen van apparaten van dichtbij. In de passieve modus zal Shelly ongevraagd gegevens van apparaten dichtbij opvragen." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/text/translations/id.json b/homeassistant/components/text/translations/id.json new file mode 100644 index 00000000000..ca3ba59d316 --- /dev/null +++ b/homeassistant/components/text/translations/id.json @@ -0,0 +1,3 @@ +{ + "title": "Teks" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/pt-BR.json b/homeassistant/components/text/translations/pt-BR.json new file mode 100644 index 00000000000..07996c47b61 --- /dev/null +++ b/homeassistant/components/text/translations/pt-BR.json @@ -0,0 +1,3 @@ +{ + "title": "Texto" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/zh-Hans.json b/homeassistant/components/text/translations/zh-Hans.json new file mode 100644 index 00000000000..5c958498042 --- /dev/null +++ b/homeassistant/components/text/translations/zh-Hans.json @@ -0,0 +1,3 @@ +{ + "title": "\u6587\u672c" +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/nl.json b/homeassistant/components/unifiprotect/translations/nl.json index a5b834fc4d4..d106b9df2f8 100644 --- a/homeassistant/components/unifiprotect/translations/nl.json +++ b/homeassistant/components/unifiprotect/translations/nl.json @@ -41,11 +41,32 @@ } } }, + "issues": { + "ea_setup_failed": { + "description": "Je gebruikt v{version} van UniFi Protect wat een Early Access versie is. Een onherstelbare fout is opgetreden terwijl de integratie aan het laden was. Ga [terug naar een stabiele versie](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) van UniFi Protect om de integratie binnen Home Assistant te kunnen gebruiken.\n\nFout: {error}", + "title": "Installatiefout bij het gebruik van een Early Access versie" + }, + "ea_warning": { + "fix_flow": { + "step": { + "confirm": { + "description": "Weet je zeker dat je een niet ondersteunde versie van Unify Protect wilt gebruiken? Dit kan veroorzaken dat je Home Assistant integratie vastloopt.", + "title": "v{version} is een Early Access versie" + }, + "start": { + "description": "Je gebruikt v{version} van UniFi Protect wat een Early Access versie is. [Early Access versies zijn niet ondersteund door Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) en het is aanbevolen om zo gauw mogelijk terug te gaan naar een stabiele versie.\n\nMet het verzenden van dit formulier beveistig je dat je [UniFi Protect hebt gedowngraded](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) of je stemt in met het gebruik van een niet ondersteunde versie van UniFi Protect.", + "title": "v{version} is een Early Access versie" + } + } + } + } + }, "options": { "step": { "init": { "data": { "all_updates": "Realtime metrieken (WAARSCHUWING: Verhoogt CPU gebruik)", + "allow_ea": "Toestaan van `Allow Early Access` versies van Protect (WAARSCHUWING: Dit zal je integratie als niet ondersteund markeren)", "disable_rtsp": "De RTSP-stream uitschakelen", "override_connection_host": "Verbindingshost overschrijven" }, diff --git a/homeassistant/components/wake_on_lan/translations/nl.json b/homeassistant/components/wake_on_lan/translations/nl.json new file mode 100644 index 00000000000..61c7e21fd8b --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/nl.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "De configuratie van Wake on LAN met YAML is verplaatst naar de integratiesleutel.\n\nDe bestaande YAML configuratie zal nog voor 2 versies werken.\n\nMigreer je YAML configuratie naar de integratiesleutel zoals is aangeven in de documentatie.", + "title": "De Wake on Lan YAML configuratie is verplaatst" + } + } +} \ No newline at end of file From 8345176ccc1ca17e5b12960be13447e14fe23544 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 26 Nov 2022 23:31:32 -1000 Subject: [PATCH 0754/1033] Adjust pip check for four conflicts (#82777) --- script/pip_check | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/pip_check b/script/pip_check index 9ed327b54f4..ae780b07d60 100755 --- a/script/pip_check +++ b/script/pip_check @@ -3,7 +3,7 @@ PIP_CACHE=$1 # Number of existing dependency conflicts # Update if a PR resolve one! -DEPENDENCY_CONFLICTS=3 +DEPENDENCY_CONFLICTS=4 PIP_CHECK=$(pip check --cache-dir=$PIP_CACHE) LINE_COUNT=$(echo "$PIP_CHECK" | wc -l) From cf249d85eee0776650ccc793c93c8f89672cfebe Mon Sep 17 00:00:00 2001 From: mkmer Date: Sun, 27 Nov 2022 10:31:41 -0500 Subject: [PATCH 0755/1033] Bump AIOAladdinConnect to 0.1.48 (#82791) --- homeassistant/components/aladdin_connect/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/aladdin_connect/manifest.json b/homeassistant/components/aladdin_connect/manifest.json index 6888eb4d8b2..71ad99a640d 100644 --- a/homeassistant/components/aladdin_connect/manifest.json +++ b/homeassistant/components/aladdin_connect/manifest.json @@ -2,7 +2,7 @@ "domain": "aladdin_connect", "name": "Aladdin Connect", "documentation": "https://www.home-assistant.io/integrations/aladdin_connect", - "requirements": ["AIOAladdinConnect==0.1.47"], + "requirements": ["AIOAladdinConnect==0.1.48"], "codeowners": ["@mkmer"], "iot_class": "cloud_polling", "loggers": ["aladdin_connect"], diff --git a/requirements_all.txt b/requirements_all.txt index 3dc976ef1ec..833dc57f6d2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -5,7 +5,7 @@ AEMET-OpenData==0.2.1 # homeassistant.components.aladdin_connect -AIOAladdinConnect==0.1.47 +AIOAladdinConnect==0.1.48 # homeassistant.components.adax Adax-local==0.1.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7ea1cafa666..d15f999da16 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -7,7 +7,7 @@ AEMET-OpenData==0.2.1 # homeassistant.components.aladdin_connect -AIOAladdinConnect==0.1.47 +AIOAladdinConnect==0.1.48 # homeassistant.components.adax Adax-local==0.1.5 From 093bd0080778f46a1f2f4c806de07d27c7e13303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Sun, 27 Nov 2022 16:46:42 +0100 Subject: [PATCH 0756/1033] Update pyTibber to 0.26.1 (#82787) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update tibber library, 0.26.0 Signed-off-by: Daniel Hjelseth Høyer * 0.26.1 Signed-off-by: Daniel Hjelseth Høyer Signed-off-by: Daniel Hjelseth Høyer --- homeassistant/components/tibber/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tibber/manifest.json b/homeassistant/components/tibber/manifest.json index 5341febc62a..892b47f39ca 100644 --- a/homeassistant/components/tibber/manifest.json +++ b/homeassistant/components/tibber/manifest.json @@ -3,7 +3,7 @@ "domain": "tibber", "name": "Tibber", "documentation": "https://www.home-assistant.io/integrations/tibber", - "requirements": ["pyTibber==0.25.6"], + "requirements": ["pyTibber==0.26.1"], "codeowners": ["@danielhiversen"], "quality_scale": "silver", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 833dc57f6d2..ec36b71d2cb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1426,7 +1426,7 @@ pyRFXtrx==0.30.0 pySwitchmate==0.5.1 # homeassistant.components.tibber -pyTibber==0.25.6 +pyTibber==0.26.1 # homeassistant.components.dlink pyW215==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d15f999da16..b60432568a1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1026,7 +1026,7 @@ pyMetno==0.9.0 pyRFXtrx==0.30.0 # homeassistant.components.tibber -pyTibber==0.25.6 +pyTibber==0.26.1 # homeassistant.components.nextbus py_nextbusnext==0.1.5 From 27bd1520e81ed736e85db5253cd41aed905016c3 Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Sun, 27 Nov 2022 20:26:15 +0100 Subject: [PATCH 0757/1033] Add support for HomeWizard enable/disable cloud feature (#82573) --- homeassistant/components/homewizard/const.py | 3 +- .../components/homewizard/coordinator.py | 5 ++ .../components/homewizard/diagnostics.py | 3 + .../components/homewizard/manifest.json | 2 +- homeassistant/components/homewizard/switch.py | 51 +++++++++++++++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/homewizard/conftest.py | 17 +++-- .../homewizard/fixtures/system.json | 3 + tests/components/homewizard/generator.py | 8 ++- .../components/homewizard/test_diagnostics.py | 1 + tests/components/homewizard/test_switch.py | 62 ++++++++++++++++++- 12 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 tests/components/homewizard/fixtures/system.json diff --git a/homeassistant/components/homewizard/const.py b/homeassistant/components/homewizard/const.py index 677f7ec46d9..f1aba6ca17a 100644 --- a/homeassistant/components/homewizard/const.py +++ b/homeassistant/components/homewizard/const.py @@ -5,7 +5,7 @@ from datetime import timedelta from typing import TypedDict # Set up. -from homewizard_energy.models import Data, Device, State +from homewizard_energy.models import Data, Device, State, System from homeassistant.const import Platform @@ -30,3 +30,4 @@ class DeviceResponseEntry(TypedDict): device: Device data: Data state: State + system: System diff --git a/homeassistant/components/homewizard/coordinator.py b/homeassistant/components/homewizard/coordinator.py index bab7b5d3ba3..4397d77a3ff 100644 --- a/homeassistant/components/homewizard/coordinator.py +++ b/homeassistant/components/homewizard/coordinator.py @@ -39,8 +39,13 @@ class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry] "device": await self.api.device(), "data": await self.api.data(), "state": await self.api.state(), + "system": None, } + features = await self.api.features() + if features.has_system: + data["system"] = await self.api.system() + except RequestError as ex: raise UpdateFailed("Device did not respond as expected") from ex diff --git a/homeassistant/components/homewizard/diagnostics.py b/homeassistant/components/homewizard/diagnostics.py index a97d2507098..a0c852cf4b6 100644 --- a/homeassistant/components/homewizard/diagnostics.py +++ b/homeassistant/components/homewizard/diagnostics.py @@ -27,6 +27,9 @@ async def async_get_config_entry_diagnostics( "state": asdict(coordinator.data["state"]) if coordinator.data["state"] is not None else None, + "system": asdict(coordinator.data["system"]) + if coordinator.data["system"] is not None + else None, } return { diff --git a/homeassistant/components/homewizard/manifest.json b/homeassistant/components/homewizard/manifest.json index 97b3c80b50d..baec844cc26 100644 --- a/homeassistant/components/homewizard/manifest.json +++ b/homeassistant/components/homewizard/manifest.json @@ -4,7 +4,7 @@ "documentation": "https://www.home-assistant.io/integrations/homewizard", "codeowners": ["@DCSBL"], "dependencies": [], - "requirements": ["python-homewizard-energy==1.1.0"], + "requirements": ["python-homewizard-energy==1.3.1"], "zeroconf": ["_hwenergy._tcp.local."], "config_flow": true, "iot_class": "local_polling", diff --git a/homeassistant/components/homewizard/switch.py b/homeassistant/components/homewizard/switch.py index eca8a7670be..7bf7d9a741e 100644 --- a/homeassistant/components/homewizard/switch.py +++ b/homeassistant/components/homewizard/switch.py @@ -30,6 +30,13 @@ async def async_setup_entry( ] ) + if coordinator.data["system"]: + async_add_entities( + [ + HWEnergyEnableCloudEntity(hass, coordinator, entry), + ] + ) + class HWEnergySwitchEntity( CoordinatorEntity[HWEnergyDeviceUpdateCoordinator], SwitchEntity @@ -124,3 +131,47 @@ class HWEnergySwitchLockEntity(HWEnergySwitchEntity): def is_on(self) -> bool: """Return true if switch is on.""" return bool(self.coordinator.data["state"].switch_lock) + + +class HWEnergyEnableCloudEntity(HWEnergySwitchEntity): + """ + Representation of the enable cloud configuration. + + Turning off 'cloud connection' turns off all communication to HomeWizard Cloud. + At this point, the device is fully local. + """ + + _attr_name = "Cloud connection" + _attr_device_class = SwitchDeviceClass.SWITCH + _attr_entity_category = EntityCategory.CONFIG + + def __init__( + self, + hass: HomeAssistant, + coordinator: HWEnergyDeviceUpdateCoordinator, + entry: ConfigEntry, + ) -> None: + """Initialize the switch.""" + super().__init__(coordinator, entry, "cloud_connection") + self.hass = hass + self.entry = entry + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn cloud connection on.""" + await self.coordinator.api.system_set(cloud_enabled=True) + await self.coordinator.async_refresh() + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn cloud connection off.""" + await self.coordinator.api.system_set(cloud_enabled=False) + await self.coordinator.async_refresh() + + @property + def icon(self) -> str | None: + """Return the icon.""" + return "mdi:cloud" if self.is_on else "mdi:cloud-off-outline" + + @property + def is_on(self) -> bool: + """Return true if cloud connection is active.""" + return bool(self.coordinator.data["system"].cloud_enabled) diff --git a/requirements_all.txt b/requirements_all.txt index ec36b71d2cb..b01377d3f27 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2003,7 +2003,7 @@ python-gc100==1.0.3a0 python-gitlab==1.6.0 # homeassistant.components.homewizard -python-homewizard-energy==1.1.0 +python-homewizard-energy==1.3.1 # homeassistant.components.hp_ilo python-hpilo==4.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b60432568a1..cf1b928e6a0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1399,7 +1399,7 @@ python-forecastio==1.4.0 python-fullykiosk==0.0.11 # homeassistant.components.homewizard -python-homewizard-energy==1.1.0 +python-homewizard-energy==1.3.1 # homeassistant.components.izone python-izone==1.2.9 diff --git a/tests/components/homewizard/conftest.py b/tests/components/homewizard/conftest.py index 1617db35458..c9a04c55dae 100644 --- a/tests/components/homewizard/conftest.py +++ b/tests/components/homewizard/conftest.py @@ -2,7 +2,8 @@ import json from unittest.mock import AsyncMock, patch -from homewizard_energy.models import Data, Device, State +from homewizard_energy.features import Features +from homewizard_energy.models import Data, Device, State, System import pytest from homeassistant.components.homewizard.const import DOMAIN @@ -37,26 +38,32 @@ def mock_config_entry() -> MockConfigEntry: @pytest.fixture def mock_homewizardenergy(): - """Return a mocked P1 meter.""" + """Return a mocked all-feature device.""" with patch( "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", ) as device: client = device.return_value + client.features = AsyncMock(return_value=Features("HWE-SKT", "3.01")) client.device = AsyncMock( - return_value=Device.from_dict( + side_effect=lambda: Device.from_dict( json.loads(load_fixture("homewizard/device.json")) ) ) client.data = AsyncMock( - return_value=Data.from_dict( + side_effect=lambda: Data.from_dict( json.loads(load_fixture("homewizard/data.json")) ) ) client.state = AsyncMock( - return_value=State.from_dict( + side_effect=lambda: State.from_dict( json.loads(load_fixture("homewizard/state.json")) ) ) + client.system = AsyncMock( + side_effect=lambda: System.from_dict( + json.loads(load_fixture("homewizard/system.json")) + ) + ) yield device diff --git a/tests/components/homewizard/fixtures/system.json b/tests/components/homewizard/fixtures/system.json new file mode 100644 index 00000000000..362491b3519 --- /dev/null +++ b/tests/components/homewizard/fixtures/system.json @@ -0,0 +1,3 @@ +{ + "cloud_enabled": true +} diff --git a/tests/components/homewizard/generator.py b/tests/components/homewizard/generator.py index 0f94580ad84..dff1a4462d3 100644 --- a/tests/components/homewizard/generator.py +++ b/tests/components/homewizard/generator.py @@ -2,6 +2,7 @@ from unittest.mock import AsyncMock +from homewizard_energy.features import Features from homewizard_energy.models import Device @@ -10,6 +11,7 @@ def get_mock_device( host="1.2.3.4", product_name="P1 meter", product_type="HWE-P1", + firmware_version="1.00", ): """Return a mock bridge.""" mock_device = AsyncMock() @@ -21,11 +23,15 @@ def get_mock_device( product_type=product_type, serial=serial, api_version="V1", - firmware_version="1.00", + firmware_version=firmware_version, ) ) mock_device.data = AsyncMock(return_value=None) mock_device.state = AsyncMock(return_value=None) + mock_device.system = AsyncMock(return_value=None) + mock_device.features = AsyncMock( + return_value=Features(product_type, firmware_version) + ) mock_device.close = AsyncMock() diff --git a/tests/components/homewizard/test_diagnostics.py b/tests/components/homewizard/test_diagnostics.py index 899bfb5fb2f..ae703c58cfd 100644 --- a/tests/components/homewizard/test_diagnostics.py +++ b/tests/components/homewizard/test_diagnostics.py @@ -45,5 +45,6 @@ async def test_diagnostics( "total_liter_m3": 1234.567, }, "state": {"power_on": True, "switch_lock": False, "brightness": 255}, + "system": {"cloud_enabled": True}, }, } diff --git a/tests/components/homewizard/test_switch.py b/tests/components/homewizard/test_switch.py index 64fb5a56909..a964d548dd3 100644 --- a/tests/components/homewizard/test_switch.py +++ b/tests/components/homewizard/test_switch.py @@ -2,7 +2,7 @@ from unittest.mock import AsyncMock, patch -from homewizard_energy.models import State +from homewizard_energy.models import State, System from homeassistant.components import switch from homeassistant.components.switch import SwitchDeviceClass @@ -286,3 +286,63 @@ async def test_switch_lock_sets_power_on_unavailable( == STATE_OFF ) assert len(api.state_set.mock_calls) == 2 + + +async def test_cloud_connection_on_off(hass, mock_config_entry_data, mock_config_entry): + """Test entity turns switch on and off.""" + + api = get_mock_device(product_type="HWE-SKT", firmware_version="3.02") + api.system = AsyncMock(return_value=System.from_dict({"cloud_enabled": False})) + + def system_set(cloud_enabled): + api.system = AsyncMock( + return_value=System.from_dict({"cloud_enabled": cloud_enabled}) + ) + + api.system_set = AsyncMock(side_effect=system_set) + + with patch( + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", + return_value=api, + ): + entry = mock_config_entry + entry.data = mock_config_entry_data + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert ( + hass.states.get("switch.product_name_aabbccddeeff_cloud_connection").state + == STATE_OFF + ) + + # Enable cloud + await hass.services.async_call( + switch.DOMAIN, + SERVICE_TURN_ON, + {"entity_id": "switch.product_name_aabbccddeeff_cloud_connection"}, + blocking=True, + ) + + await hass.async_block_till_done() + assert len(api.system_set.mock_calls) == 1 + assert ( + hass.states.get("switch.product_name_aabbccddeeff_cloud_connection").state + == STATE_ON + ) + + # Disable cloud + await hass.services.async_call( + switch.DOMAIN, + SERVICE_TURN_OFF, + {"entity_id": "switch.product_name_aabbccddeeff_cloud_connection"}, + blocking=True, + ) + + await hass.async_block_till_done() + assert ( + hass.states.get("switch.product_name_aabbccddeeff_cloud_connection").state + == STATE_OFF + ) + assert len(api.system_set.mock_calls) == 2 From 6662dbf3c8ad931c4e64c45c0b93a302e42d0418 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 27 Nov 2022 09:27:59 -1000 Subject: [PATCH 0758/1033] Bump bluetooth-auto-recovery to 0.5.2 (#82768) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index c6611704dc4..096b166d11c 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -9,7 +9,7 @@ "bleak==0.19.2", "bleak-retry-connector==2.8.5", "bluetooth-adapters==0.8.0", - "bluetooth-auto-recovery==0.4.0", + "bluetooth-auto-recovery==0.5.2", "bluetooth-data-tools==0.3.0", "dbus-fast==1.75.0" ], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index ecd9f9dc333..25efedf657a 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -13,7 +13,7 @@ bcrypt==3.1.7 bleak-retry-connector==2.8.5 bleak==0.19.2 bluetooth-adapters==0.8.0 -bluetooth-auto-recovery==0.4.0 +bluetooth-auto-recovery==0.5.2 bluetooth-data-tools==0.3.0 certifi>=2021.5.30 ciso8601==2.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index b01377d3f27..448af1af306 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -450,7 +450,7 @@ bluemaestro-ble==0.2.0 bluetooth-adapters==0.8.0 # homeassistant.components.bluetooth -bluetooth-auto-recovery==0.4.0 +bluetooth-auto-recovery==0.5.2 # homeassistant.components.bluetooth # homeassistant.components.led_ble diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cf1b928e6a0..91e0c7a5f9b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -364,7 +364,7 @@ bluemaestro-ble==0.2.0 bluetooth-adapters==0.8.0 # homeassistant.components.bluetooth -bluetooth-auto-recovery==0.4.0 +bluetooth-auto-recovery==0.5.2 # homeassistant.components.bluetooth # homeassistant.components.led_ble From 444ad52757a11b7b6b39fb7560b74c425bc50b4b Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Sun, 27 Nov 2022 20:34:01 +0100 Subject: [PATCH 0759/1033] Fix info message in config_entries handle_reload (#82798) --- homeassistant/config_entries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index e619c3e73f2..4830ec602a7 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -1778,7 +1778,7 @@ class EntityRegistryDisabledHandler: _LOGGER.info( "Reloading configuration entries because disabled_by changed in entity registry: %s", - ", ".join(self.changed), + ", ".join(to_reload), ) await asyncio.gather( From 31ad2085004b640db6ad00fc63e850dfb86b41cd Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Mon, 28 Nov 2022 03:35:03 +0800 Subject: [PATCH 0760/1033] Use async with to fetch HTTP streams in tests (#82788) --- tests/components/api/test_init.py | 34 ++++++++++++++-------------- tests/components/camera/test_init.py | 10 ++++---- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/tests/components/api/test_init.py b/tests/components/api/test_init.py index d56389c7230..d6bfd3de521 100644 --- a/tests/components/api/test_init.py +++ b/tests/components/api/test_init.py @@ -327,35 +327,35 @@ async def test_stream(hass, mock_api_client): """Test the stream.""" listen_count = _listen_count(hass) - resp = await mock_api_client.get(const.URL_API_STREAM) - assert resp.status == HTTPStatus.OK - assert listen_count + 1 == _listen_count(hass) + async with mock_api_client.get(const.URL_API_STREAM) as resp: + assert resp.status == HTTPStatus.OK + assert listen_count + 1 == _listen_count(hass) - hass.bus.async_fire("test_event") + hass.bus.async_fire("test_event") - data = await _stream_next_event(resp.content) + data = await _stream_next_event(resp.content) - assert data["event_type"] == "test_event" + assert data["event_type"] == "test_event" async def test_stream_with_restricted(hass, mock_api_client): """Test the stream with restrictions.""" listen_count = _listen_count(hass) - resp = await mock_api_client.get( + async with mock_api_client.get( f"{const.URL_API_STREAM}?restrict=test_event1,test_event3" - ) - assert resp.status == HTTPStatus.OK - assert listen_count + 1 == _listen_count(hass) + ) as resp: + assert resp.status == HTTPStatus.OK + assert listen_count + 1 == _listen_count(hass) - hass.bus.async_fire("test_event1") - data = await _stream_next_event(resp.content) - assert data["event_type"] == "test_event1" + hass.bus.async_fire("test_event1") + data = await _stream_next_event(resp.content) + assert data["event_type"] == "test_event1" - hass.bus.async_fire("test_event2") - hass.bus.async_fire("test_event3") - data = await _stream_next_event(resp.content) - assert data["event_type"] == "test_event3" + hass.bus.async_fire("test_event2") + hass.bus.async_fire("test_event3") + data = await _stream_next_event(resp.content) + assert data["event_type"] == "test_event3" async def _stream_next_event(stream): diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py index 3a78740684a..c6d3e1414b2 100644 --- a/tests/components/camera/test_init.py +++ b/tests/components/camera/test_init.py @@ -503,15 +503,17 @@ async def test_camera_proxy_stream(hass, mock_camera, hass_client): client = await hass_client() - response = await client.get("/api/camera_proxy_stream/camera.demo_camera") - assert response.status == HTTPStatus.OK + async with client.get("/api/camera_proxy_stream/camera.demo_camera") as response: + assert response.status == HTTPStatus.OK with patch( "homeassistant.components.demo.camera.DemoCamera.handle_async_mjpeg_stream", return_value=None, ): - response = await client.get("/api/camera_proxy_stream/camera.demo_camera") - assert response.status == HTTPStatus.BAD_GATEWAY + async with await client.get( + "/api/camera_proxy_stream/camera.demo_camera" + ) as response: + assert response.status == HTTPStatus.BAD_GATEWAY async def test_websocket_web_rtc_offer( From 682486c8644707d24d865c631b884b0eaf9dc863 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 27 Nov 2022 11:49:47 -0800 Subject: [PATCH 0761/1033] Bump pyrainbird to 0.6.3 and drop dependency conflict count (#82800) --- homeassistant/components/rainbird/manifest.json | 2 +- requirements_all.txt | 2 +- script/pip_check | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/rainbird/manifest.json b/homeassistant/components/rainbird/manifest.json index 2a49dc60843..23924bbad18 100644 --- a/homeassistant/components/rainbird/manifest.json +++ b/homeassistant/components/rainbird/manifest.json @@ -2,7 +2,7 @@ "domain": "rainbird", "name": "Rain Bird", "documentation": "https://www.home-assistant.io/integrations/rainbird", - "requirements": ["pyrainbird==0.6.2"], + "requirements": ["pyrainbird==0.6.3"], "codeowners": ["@konikvranik"], "iot_class": "local_polling", "loggers": ["pyrainbird"] diff --git a/requirements_all.txt b/requirements_all.txt index 448af1af306..f1213328700 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1851,7 +1851,7 @@ pyqwikswitch==0.93 pyrail==0.0.3 # homeassistant.components.rainbird -pyrainbird==0.6.2 +pyrainbird==0.6.3 # homeassistant.components.recswitch pyrecswitch==1.0.2 diff --git a/script/pip_check b/script/pip_check index ae780b07d60..9ed327b54f4 100755 --- a/script/pip_check +++ b/script/pip_check @@ -3,7 +3,7 @@ PIP_CACHE=$1 # Number of existing dependency conflicts # Update if a PR resolve one! -DEPENDENCY_CONFLICTS=4 +DEPENDENCY_CONFLICTS=3 PIP_CHECK=$(pip check --cache-dir=$PIP_CACHE) LINE_COUNT=$(echo "$PIP_CHECK" | wc -l) From 3e3138ef1b98cd2375071037e00672dc1288f449 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 27 Nov 2022 09:58:32 -1000 Subject: [PATCH 0762/1033] Bump yalexs-ble to 1.9.6 (#82779) --- homeassistant/components/yalexs_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/yalexs_ble/manifest.json b/homeassistant/components/yalexs_ble/manifest.json index b43ce18a7e9..b345f996998 100644 --- a/homeassistant/components/yalexs_ble/manifest.json +++ b/homeassistant/components/yalexs_ble/manifest.json @@ -3,7 +3,7 @@ "name": "Yale Access Bluetooth", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yalexs_ble", - "requirements": ["yalexs-ble==1.9.5"], + "requirements": ["yalexs-ble==1.9.6"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [ diff --git a/requirements_all.txt b/requirements_all.txt index f1213328700..d10244d8137 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2597,7 +2597,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.9.5 +yalexs-ble==1.9.6 # homeassistant.components.august yalexs==1.2.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 91e0c7a5f9b..bb6929c181b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1807,7 +1807,7 @@ xmltodict==0.13.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.9.5 +yalexs-ble==1.9.6 # homeassistant.components.august yalexs==1.2.6 From f0ae1cc6ce7bbd93b4d29b3a6d38135f66825c12 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 27 Nov 2022 09:59:37 -1000 Subject: [PATCH 0763/1033] Add bluetooth diagnostics to esphome (#82761) --- .../components/bluetooth/base_scanner.py | 10 +- .../components/esphome/bluetooth/scanner.py | 16 ++++ .../components/esphome/diagnostics.py | 10 ++ .../components/bluetooth/test_diagnostics.py | 91 ++++++++++++++++--- tests/components/esphome/test_diagnostics.py | 6 +- 5 files changed, 116 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/bluetooth/base_scanner.py b/homeassistant/components/bluetooth/base_scanner.py index 88332c79667..b8711b0017f 100644 --- a/homeassistant/components/bluetooth/base_scanner.py +++ b/homeassistant/components/bluetooth/base_scanner.py @@ -67,12 +67,14 @@ class BaseHaScanner: """Return diagnostic information about the scanner.""" return { "type": self.__class__.__name__, - "discovered_devices": [ + "discovered_devices_and_advertisement_data": [ { - "name": device.name, - "address": device.address, + "name": device_adv[0].name, + "address": device_adv[0].address, + "rssi": device_adv[0].rssi, + "advertisement_data": device_adv[1], } - for device in self.discovered_devices + for device_adv in self.discovered_devices_and_advertisement_data.values() ], } diff --git a/homeassistant/components/esphome/bluetooth/scanner.py b/homeassistant/components/esphome/bluetooth/scanner.py index ab7572fd4ac..3df9a4c6aaa 100644 --- a/homeassistant/components/esphome/bluetooth/scanner.py +++ b/homeassistant/components/esphome/bluetooth/scanner.py @@ -2,6 +2,7 @@ from __future__ import annotations import re +from typing import Any from aioesphomeapi import BluetoothLEAdvertisement @@ -27,3 +28,18 @@ class ESPHomeScanner(BaseHaRemoteScanner): adv.manufacturer_data, None, ) + + async def async_diagnostics(self) -> dict[str, Any]: + """Return diagnostic information about the scanner.""" + return await super().async_diagnostics() | { + "type": self.__class__.__name__, + "discovered_devices_and_advertisement_data": [ + { + "name": device_adv[0].name, + "address": device_adv[0].address, + "rssi": device_adv[0].rssi, + "advertisement_data": device_adv[1], + } + for device_adv in self.discovered_devices_and_advertisement_data.values() + ], + } diff --git a/homeassistant/components/esphome/diagnostics.py b/homeassistant/components/esphome/diagnostics.py index cc82fd536e7..68f195a23fb 100644 --- a/homeassistant/components/esphome/diagnostics.py +++ b/homeassistant/components/esphome/diagnostics.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Any, cast +from homeassistant.components.bluetooth import async_scanner_by_source from homeassistant.components.diagnostics import async_redact_data from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD @@ -29,4 +30,13 @@ async def async_get_config_entry_diagnostics( storage_data = cast("dict[str, Any]", storage_data) diag["storage_data"] = storage_data + if config_entry.unique_id and ( + scanner := async_scanner_by_source(hass, config_entry.unique_id) + ): + diag["bluetooth"] = { + "connections_free": entry_data.ble_connections_free, + "connections_limit": entry_data.ble_connections_limit, + "scanner": await scanner.async_diagnostics(), + } + return async_redact_data(diag, REDACT_KEYS) diff --git a/tests/components/bluetooth/test_diagnostics.py b/tests/components/bluetooth/test_diagnostics.py index bfbefe74151..20ffddf3297 100644 --- a/tests/components/bluetooth/test_diagnostics.py +++ b/tests/components/bluetooth/test_diagnostics.py @@ -24,8 +24,13 @@ async def test_diagnostics( # error if the test is not running on linux since we won't have the correct # deps installed when testing on MacOS. with patch( - "homeassistant.components.bluetooth.scanner.HaScanner.discovered_devices", - [BLEDevice(name="x", rssi=-60, address="44:44:33:11:23:45")], + "homeassistant.components.bluetooth.scanner.HaScanner.discovered_devices_and_advertisement_data", + { + "44:44:33:11:23:45": ( + BLEDevice(name="x", rssi=-60, address="44:44:33:11:23:45"), + generate_advertisement_data(local_name="x"), + ) + }, ), patch( "homeassistant.components.bluetooth.diagnostics.platform.system", return_value="Linux", @@ -120,8 +125,21 @@ async def test_diagnostics( "scanners": [ { "adapter": "hci0", - "discovered_devices": [ - {"address": "44:44:33:11:23:45", "name": "x"} + "discovered_devices_and_advertisement_data": [ + { + "address": "44:44:33:11:23:45", + "advertisement_data": [ + "x", + {}, + {}, + [], + -127, + -127, + [[]], + ], + "name": "x", + "rssi": -60, + } ], "last_detection": ANY, "name": "hci0 (00:00:00:00:00:01)", @@ -131,8 +149,21 @@ async def test_diagnostics( }, { "adapter": "hci0", - "discovered_devices": [ - {"address": "44:44:33:11:23:45", "name": "x"} + "discovered_devices_and_advertisement_data": [ + { + "address": "44:44:33:11:23:45", + "advertisement_data": [ + "x", + {}, + {}, + [], + -127, + -127, + [[]], + ], + "name": "x", + "rssi": -60, + } ], "last_detection": ANY, "name": "hci0 (00:00:00:00:00:01)", @@ -142,8 +173,21 @@ async def test_diagnostics( }, { "adapter": "hci1", - "discovered_devices": [ - {"address": "44:44:33:11:23:45", "name": "x"} + "discovered_devices_and_advertisement_data": [ + { + "address": "44:44:33:11:23:45", + "advertisement_data": [ + "x", + {}, + {}, + [], + -127, + -127, + [[]], + ], + "name": "x", + "rssi": -60, + } ], "last_detection": ANY, "name": "hci1 (00:00:00:00:00:02)", @@ -171,8 +215,13 @@ async def test_diagnostics_macos( ) with patch( - "homeassistant.components.bluetooth.scanner.HaScanner.discovered_devices", - [BLEDevice(name="x", rssi=-60, address="44:44:33:11:23:45")], + "homeassistant.components.bluetooth.scanner.HaScanner.discovered_devices_and_advertisement_data", + { + "44:44:33:11:23:45": ( + BLEDevice(name="x", rssi=-60, address="44:44:33:11:23:45"), + switchbot_adv, + ) + }, ), patch( "homeassistant.components.bluetooth.diagnostics.platform.system", return_value="Darwin", @@ -274,8 +323,26 @@ async def test_diagnostics_macos( "scanners": [ { "adapter": "Core Bluetooth", - "discovered_devices": [ - {"address": "44:44:33:11:23:45", "name": "x"} + "discovered_devices_and_advertisement_data": [ + { + "address": "44:44:33:11:23:45", + "advertisement_data": [ + "wohand", + { + "1": { + "__type": "", + "repr": "b'\\x01'", + } + }, + {}, + [], + -127, + -127, + [[]], + ], + "name": "x", + "rssi": -60, + } ], "last_detection": ANY, "name": "Core Bluetooth", diff --git a/tests/components/esphome/test_diagnostics.py b/tests/components/esphome/test_diagnostics.py index 319bc2602e1..522ae0c8345 100644 --- a/tests/components/esphome/test_diagnostics.py +++ b/tests/components/esphome/test_diagnostics.py @@ -1,6 +1,7 @@ """Tests for the diagnostics data provided by the ESPHome integration.""" from aiohttp import ClientSession +import pytest from homeassistant.components.esphome import CONF_NOISE_PSK from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT @@ -11,7 +12,10 @@ from tests.components.diagnostics import get_diagnostics_for_config_entry async def test_diagnostics( - hass: HomeAssistant, hass_client: ClientSession, init_integration: MockConfigEntry + hass: HomeAssistant, + hass_client: ClientSession, + init_integration: MockConfigEntry, + enable_bluetooth: pytest.fixture, ): """Test diagnostics for config entry.""" result = await get_diagnostics_for_config_entry(hass, hass_client, init_integration) From 4928c3d68301305969cdb2b5505c5da8fd13164c Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 27 Nov 2022 12:59:54 -0700 Subject: [PATCH 0764/1033] Add missing SimpliSafe keypad battery sensor (#82797) --- homeassistant/components/simplisafe/binary_sensor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/simplisafe/binary_sensor.py b/homeassistant/components/simplisafe/binary_sensor.py index 239f5468cb3..5ccbfb96afb 100644 --- a/homeassistant/components/simplisafe/binary_sensor.py +++ b/homeassistant/components/simplisafe/binary_sensor.py @@ -21,6 +21,7 @@ SUPPORTED_BATTERY_SENSOR_TYPES = [ DeviceTypes.CARBON_MONOXIDE, DeviceTypes.ENTRY, DeviceTypes.GLASS_BREAK, + DeviceTypes.KEYPAD, DeviceTypes.LEAK, DeviceTypes.LOCK_KEYPAD, DeviceTypes.MOTION, From 3853182ccf371fafeded60e81fe92d2897333af2 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sun, 27 Nov 2022 21:01:58 +0100 Subject: [PATCH 0765/1033] Add deconz_relative_rotary event for Hue Tap Dial (#82727) --- homeassistant/components/deconz/const.py | 3 + .../components/deconz/deconz_event.py | 45 +++++++- .../components/deconz/device_trigger.py | 3 +- homeassistant/components/deconz/gateway.py | 14 ++- tests/components/deconz/test_deconz_event.py | 103 ++++++++++++++++++ 5 files changed, 162 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/deconz/const.py b/homeassistant/components/deconz/const.py index 6070f83871f..ca38edf0625 100644 --- a/homeassistant/components/deconz/const.py +++ b/homeassistant/components/deconz/const.py @@ -57,3 +57,6 @@ POWER_PLUGS = [ CONF_ANGLE = "angle" CONF_GESTURE = "gesture" + +ATTR_DURATION = "duration" +ATTR_ROTATION = "rotation" diff --git a/homeassistant/components/deconz/deconz_event.py b/homeassistant/components/deconz/deconz_event.py index 35e1ba79948..9bde87e5e17 100644 --- a/homeassistant/components/deconz/deconz_event.py +++ b/homeassistant/components/deconz/deconz_event.py @@ -10,6 +10,7 @@ from pydeconz.models.sensor.ancillary_control import ( AncillaryControlAction, ) from pydeconz.models.sensor.presence import Presence, PresenceStatePresenceEvent +from pydeconz.models.sensor.relative_rotary import RelativeRotary, RelativeRotaryEvent from pydeconz.models.sensor.switch import Switch from homeassistant.const import ( @@ -23,13 +24,14 @@ from homeassistant.core import callback from homeassistant.helpers import device_registry as dr from homeassistant.util import slugify -from .const import CONF_ANGLE, CONF_GESTURE, LOGGER +from .const import ATTR_DURATION, ATTR_ROTATION, CONF_ANGLE, CONF_GESTURE, LOGGER from .deconz_device import DeconzBase from .gateway import DeconzGateway CONF_DECONZ_EVENT = "deconz_event" CONF_DECONZ_ALARM_EVENT = "deconz_alarm_event" CONF_DECONZ_PRESENCE_EVENT = "deconz_presence_event" +CONF_DECONZ_RELATIVE_ROTARY_EVENT = "deconz_relative_rotary_event" SUPPORTED_DECONZ_ALARM_EVENTS = { AncillaryControlAction.EMERGENCY, @@ -47,6 +49,10 @@ SUPPORTED_DECONZ_PRESENCE_EVENTS = { PresenceStatePresenceEvent.APPROACHING, PresenceStatePresenceEvent.ABSENTING, } +RELATIVE_ROTARY_DECONZ_TO_EVENT = { + RelativeRotaryEvent.NEW: "new", + RelativeRotaryEvent.REPEAT: "repeat", +} async def async_setup_events(gateway: DeconzGateway) -> None: @@ -55,7 +61,7 @@ async def async_setup_events(gateway: DeconzGateway) -> None: @callback def async_add_sensor(_: EventType, sensor_id: str) -> None: """Create DeconzEvent.""" - new_event: DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent + new_event: DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent | DeconzRelativeRotaryEvent sensor = gateway.api.sensors[sensor_id] if isinstance(sensor, Switch): @@ -69,6 +75,9 @@ async def async_setup_events(gateway: DeconzGateway) -> None: return new_event = DeconzPresenceEvent(sensor, gateway) + elif isinstance(sensor, RelativeRotary): + new_event = DeconzRelativeRotaryEvent(sensor, gateway) + gateway.hass.async_create_task(new_event.async_update_device_registry()) gateway.events.append(new_event) @@ -84,6 +93,10 @@ async def async_setup_events(gateway: DeconzGateway) -> None: async_add_sensor, gateway.api.sensors.presence, ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors.relative_rotary, + ) @callback @@ -104,7 +117,7 @@ class DeconzEventBase(DeconzBase): def __init__( self, - device: AncillaryControl | Presence | Switch, + device: AncillaryControl | Presence | RelativeRotary | Switch, gateway: DeconzGateway, ) -> None: """Register callback that will be used for signals.""" @@ -227,3 +240,29 @@ class DeconzPresenceEvent(DeconzEventBase): } self.gateway.hass.bus.async_fire(CONF_DECONZ_PRESENCE_EVENT, data) + + +class DeconzRelativeRotaryEvent(DeconzEventBase): + """Relative rotary event.""" + + _device: RelativeRotary + + @callback + def async_update_callback(self) -> None: + """Fire the event if reason is new action is updated.""" + if ( + self.gateway.ignore_state_updates + or "rotaryevent" not in self._device.changed_keys + ): + return + + data = { + CONF_ID: self.event_id, + CONF_UNIQUE_ID: self.serial, + CONF_DEVICE_ID: self.device_id, + CONF_EVENT: RELATIVE_ROTARY_DECONZ_TO_EVENT[self._device.rotary_event], + ATTR_ROTATION: self._device.expected_rotation, + ATTR_DURATION: self._device.expected_event_duration, + } + + self.gateway.hass.bus.async_fire(CONF_DECONZ_RELATIVE_ROTARY_EVENT, data) diff --git a/homeassistant/components/deconz/device_trigger.py b/homeassistant/components/deconz/device_trigger.py index 8c63a47f59c..76844b026ce 100644 --- a/homeassistant/components/deconz/device_trigger.py +++ b/homeassistant/components/deconz/device_trigger.py @@ -28,6 +28,7 @@ from .deconz_event import ( DeconzAlarmEvent, DeconzEvent, DeconzPresenceEvent, + DeconzRelativeRotaryEvent, ) from .gateway import DeconzGateway @@ -635,7 +636,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( def _get_deconz_event_from_device( hass: HomeAssistant, device: dr.DeviceEntry, -) -> DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent: +) -> DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent | DeconzRelativeRotaryEvent: """Resolve deconz event from device.""" gateways: dict[str, DeconzGateway] = hass.data.get(DOMAIN, {}) for gateway in gateways.values(): diff --git a/homeassistant/components/deconz/gateway.py b/homeassistant/components/deconz/gateway.py index 1c381bc194a..c1b8139e6f7 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -41,7 +41,12 @@ from .const import ( from .errors import AuthenticationRequired, CannotConnect if TYPE_CHECKING: - from .deconz_event import DeconzAlarmEvent, DeconzEvent, DeconzPresenceEvent + from .deconz_event import ( + DeconzAlarmEvent, + DeconzEvent, + DeconzPresenceEvent, + DeconzRelativeRotaryEvent, + ) SENSORS = ( sensors.SensorResourceManager, @@ -93,7 +98,12 @@ class DeconzGateway: self.deconz_ids: dict[str, str] = {} self.entities: dict[str, set[str]] = {} - self.events: list[DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent] = [] + self.events: list[ + DeconzAlarmEvent + | DeconzEvent + | DeconzPresenceEvent + | DeconzRelativeRotaryEvent + ] = [] self.clip_sensors: set[tuple[Callable[[EventType, str], None], str]] = set() self.deconz_groups: set[tuple[Callable[[EventType, str], None], str]] = set() self.ignored_devices: set[tuple[Callable[[EventType, str], None], str]] = set() diff --git a/tests/components/deconz/test_deconz_event.py b/tests/components/deconz/test_deconz_event.py index 0d99d33e571..597d9282136 100644 --- a/tests/components/deconz/test_deconz_event.py +++ b/tests/components/deconz/test_deconz_event.py @@ -10,9 +10,13 @@ from pydeconz.models.sensor.presence import PresenceStatePresenceEvent from homeassistant.components.deconz.const import DOMAIN as DECONZ_DOMAIN from homeassistant.components.deconz.deconz_event import ( + ATTR_DURATION, + ATTR_ROTATION, CONF_DECONZ_ALARM_EVENT, CONF_DECONZ_EVENT, CONF_DECONZ_PRESENCE_EVENT, + CONF_DECONZ_RELATIVE_ROTARY_EVENT, + RELATIVE_ROTARY_DECONZ_TO_EVENT, ) from homeassistant.const import ( CONF_DEVICE_ID, @@ -515,6 +519,105 @@ async def test_deconz_presence_events(hass, aioclient_mock, mock_deconz_websocke assert len(hass.states.async_all()) == 0 +async def test_deconz_relative_rotary_events( + hass, aioclient_mock, mock_deconz_websocket +): + """Test successful creation of deconz relative rotary events.""" + data = { + "sensors": { + "1": { + "config": { + "battery": 100, + "on": True, + "reachable": True, + }, + "etag": "463728970bdb7d04048fc4373654f45a", + "lastannounced": "2022-07-03T13:57:59Z", + "lastseen": "2022-07-03T14:02Z", + "manufacturername": "Signify Netherlands B.V.", + "modelid": "RDM002", + "name": "RDM002 44", + "state": { + "expectedeventduration": 400, + "expectedrotation": 75, + "lastupdated": "2022-07-03T11:37:49.586", + "rotaryevent": 2, + }, + "swversion": "2.59.19", + "type": "ZHARelativeRotary", + "uniqueid": "xx:xx:xx:xx:xx:xx:xx:xx-14-fc00", + } + } + } + with patch.dict(DECONZ_WEB_REQUEST, data): + config_entry = await setup_deconz_integration(hass, aioclient_mock) + + device_registry = dr.async_get(hass) + + assert len(hass.states.async_all()) == 1 + assert ( + len(dr.async_entries_for_config_entry(device_registry, config_entry.entry_id)) + == 3 + ) + + device = device_registry.async_get_device( + identifiers={(DECONZ_DOMAIN, "xx:xx:xx:xx:xx:xx:xx:xx")} + ) + + captured_events = async_capture_events(hass, CONF_DECONZ_RELATIVE_ROTARY_EVENT) + + for rotary_event, duration, rotation in ((1, 100, 50), (2, 200, -50)): + event_changed_sensor = { + "t": "event", + "e": "changed", + "r": "sensors", + "id": "1", + "state": { + "rotaryevent": rotary_event, + "expectedeventduration": duration, + "expectedrotation": rotation, + }, + } + await mock_deconz_websocket(data=event_changed_sensor) + await hass.async_block_till_done() + + assert len(captured_events) == 1 + assert captured_events[0].data == { + CONF_ID: "rdm002_44", + CONF_UNIQUE_ID: "xx:xx:xx:xx:xx:xx:xx:xx", + CONF_DEVICE_ID: device.id, + CONF_EVENT: RELATIVE_ROTARY_DECONZ_TO_EVENT[rotary_event], + ATTR_DURATION: duration, + ATTR_ROTATION: rotation, + } + captured_events.clear() + + # Unsupported relative rotary event + + event_changed_sensor = { + "t": "event", + "e": "changed", + "r": "sensors", + "id": "1", + "name": "123", + } + await mock_deconz_websocket(data=event_changed_sensor) + await hass.async_block_till_done() + + assert len(captured_events) == 0 + + await hass.config_entries.async_unload(config_entry.entry_id) + + states = hass.states.async_all() + assert len(hass.states.async_all()) == 1 + for state in states: + assert state.state == STATE_UNAVAILABLE + + await hass.config_entries.async_remove(config_entry.entry_id) + await hass.async_block_till_done() + assert len(hass.states.async_all()) == 0 + + async def test_deconz_events_bad_unique_id(hass, aioclient_mock): """Verify no devices are created if unique id is bad or missing.""" data = { From e1572872a1c72683515404bfcac4339cb0238c85 Mon Sep 17 00:00:00 2001 From: Mask3007 <31553023+Mask3007@users.noreply.github.com> Date: Sun, 27 Nov 2022 21:14:21 +0100 Subject: [PATCH 0766/1033] Update holidays dependency to latest 0.17.2 (#82794) fixes undefined --- homeassistant/components/workday/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/workday/manifest.json b/homeassistant/components/workday/manifest.json index 97dc2c37501..fa94319772c 100644 --- a/homeassistant/components/workday/manifest.json +++ b/homeassistant/components/workday/manifest.json @@ -2,7 +2,7 @@ "domain": "workday", "name": "Workday", "documentation": "https://www.home-assistant.io/integrations/workday", - "requirements": ["holidays==0.16"], + "requirements": ["holidays==0.17.2"], "codeowners": ["@fabaff"], "quality_scale": "internal", "iot_class": "local_polling", diff --git a/requirements_all.txt b/requirements_all.txt index d10244d8137..2ac05ea8208 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -881,7 +881,7 @@ hlk-sw16==0.0.9 hole==0.7.0 # homeassistant.components.workday -holidays==0.16 +holidays==0.17.2 # homeassistant.components.frontend home-assistant-frontend==20221108.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bb6929c181b..89b6fb6bbf1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -661,7 +661,7 @@ hlk-sw16==0.0.9 hole==0.7.0 # homeassistant.components.workday -holidays==0.16 +holidays==0.17.2 # homeassistant.components.frontend home-assistant-frontend==20221108.0 From 8a1805fa89e5d3001695467033c456e7a68587f2 Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Sun, 27 Nov 2022 15:31:38 -0500 Subject: [PATCH 0767/1033] Add Do Not Disturb sensor to Slack (#72380) --- .coveragerc | 1 + homeassistant/components/slack/__init__.py | 55 ++++++++++++++++++++-- homeassistant/components/slack/const.py | 4 ++ homeassistant/components/slack/notify.py | 3 +- homeassistant/components/slack/sensor.py | 53 +++++++++++++++++++++ 5 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 homeassistant/components/slack/sensor.py diff --git a/.coveragerc b/.coveragerc index 6322dc0f7b3..b2b23757014 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1138,6 +1138,7 @@ omit = homeassistant/components/skybell/switch.py homeassistant/components/slack/__init__.py homeassistant/components/slack/notify.py + homeassistant/components/slack/sensor.py homeassistant/components/slide/* homeassistant/components/slimproto/__init__.py homeassistant/components/slimproto/media_player.py diff --git a/homeassistant/components/slack/__init__.py b/homeassistant/components/slack/__init__.py index a89f645e9b6..36b457b75b7 100644 --- a/homeassistant/components/slack/__init__.py +++ b/homeassistant/components/slack/__init__.py @@ -1,4 +1,6 @@ """The slack integration.""" +from __future__ import annotations + import logging from aiohttp.client_exceptions import ClientError @@ -10,13 +12,23 @@ from homeassistant.const import CONF_API_KEY, CONF_PLATFORM, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import aiohttp_client, discovery +from homeassistant.helpers.device_registry import DeviceEntryType +from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription from homeassistant.helpers.typing import ConfigType -from .const import DATA_CLIENT, DATA_HASS_CONFIG, DOMAIN +from .const import ( + ATTR_URL, + ATTR_USER_ID, + DATA_CLIENT, + DATA_HASS_CONFIG, + DEFAULT_NAME, + DOMAIN, + SLACK_DATA, +) _LOGGER = logging.getLogger(__name__) -PLATFORMS = [Platform.NOTIFY] +PLATFORMS = [Platform.NOTIFY, Platform.SENSOR] async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: @@ -42,14 +54,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: slack = WebClient(token=entry.data[CONF_API_KEY], run_async=True, session=session) try: - await slack.auth_test() + res = await slack.auth_test() except (SlackApiError, ClientError) as ex: if isinstance(ex, SlackApiError) and ex.response["error"] == "invalid_auth": _LOGGER.error("Invalid API key") return False raise ConfigEntryNotReady("Error while setting up integration") from ex - - hass.data.setdefault(DOMAIN, {})[entry.entry_id] = entry.data | {DATA_CLIENT: slack} + data = { + DATA_CLIENT: slack, + ATTR_URL: res[ATTR_URL], + ATTR_USER_ID: res[ATTR_USER_ID], + } + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = entry.data | {SLACK_DATA: data} hass.async_create_task( discovery.async_load_platform( @@ -61,4 +77,33 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) ) + await hass.config_entries.async_forward_entry_setups( + entry, [platform for platform in PLATFORMS if platform != Platform.NOTIFY] + ) + return True + + +class SlackEntity(Entity): + """Representation of a Slack entity.""" + + _attr_attribution = "Data provided by Slack" + _attr_has_entity_name = True + + def __init__( + self, + data: dict[str, str | WebClient], + description: EntityDescription, + entry: ConfigEntry, + ) -> None: + """Initialize a Slack entity.""" + self._client = data[DATA_CLIENT] + self.entity_description = description + self._attr_unique_id = f"{data[ATTR_USER_ID]}_{description.key}" + self._attr_device_info = DeviceInfo( + configuration_url=data[ATTR_URL], + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, entry.entry_id)}, + manufacturer=DEFAULT_NAME, + name=entry.title, + ) diff --git a/homeassistant/components/slack/const.py b/homeassistant/components/slack/const.py index 83937f4a43e..ec0993e290b 100644 --- a/homeassistant/components/slack/const.py +++ b/homeassistant/components/slack/const.py @@ -6,13 +6,17 @@ ATTR_BLOCKS_TEMPLATE = "blocks_template" ATTR_FILE = "file" ATTR_PASSWORD = "password" ATTR_PATH = "path" +ATTR_SNOOZE = "snooze_endtime" ATTR_URL = "url" ATTR_USERNAME = "username" +ATTR_USER_ID = "user_id" CONF_DEFAULT_CHANNEL = "default_channel" DATA_CLIENT = "client" +DEFAULT_NAME = "Slack" DEFAULT_TIMEOUT_SECONDS = 15 DOMAIN: Final = "slack" +SLACK_DATA = "data" DATA_HASS_CONFIG = "slack_hass_config" diff --git a/homeassistant/components/slack/notify.py b/homeassistant/components/slack/notify.py index bfcf79ef676..d587f960704 100644 --- a/homeassistant/components/slack/notify.py +++ b/homeassistant/components/slack/notify.py @@ -41,6 +41,7 @@ from .const import ( ATTR_USERNAME, CONF_DEFAULT_CHANNEL, DATA_CLIENT, + SLACK_DATA, ) _LOGGER = logging.getLogger(__name__) @@ -121,7 +122,7 @@ async def async_get_service( return SlackNotificationService( hass, - discovery_info.pop(DATA_CLIENT), + discovery_info[SLACK_DATA][DATA_CLIENT], discovery_info, ) diff --git a/homeassistant/components/slack/sensor.py b/homeassistant/components/slack/sensor.py new file mode 100644 index 00000000000..b190e6151ed --- /dev/null +++ b/homeassistant/components/slack/sensor.py @@ -0,0 +1,53 @@ +"""Slack platform for sensor component.""" +from __future__ import annotations + +from slack import WebClient + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +import homeassistant.util.dt as dt_util + +from . import SlackEntity +from .const import ATTR_SNOOZE, DOMAIN, SLACK_DATA + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Slack select.""" + async_add_entities( + [ + SlackSensorEntity( + hass.data[DOMAIN][entry.entry_id][SLACK_DATA], + SensorEntityDescription( + key="do_not_disturb_until", + name="Do not disturb until", + icon="mdi:clock", + device_class=SensorDeviceClass.TIMESTAMP, + ), + entry, + ) + ], + True, + ) + + +class SlackSensorEntity(SlackEntity, SensorEntity): + """Representation of a Slack sensor.""" + + _client: WebClient + + async def async_update(self) -> None: + """Get the latest status.""" + if _time := (await self._client.dnd_info()).get(ATTR_SNOOZE): + self._attr_native_value = dt_util.utc_from_timestamp(_time) + else: + self._attr_native_value = None From 873b903cf0217c48049e32c45f7d5b115078b831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Sun, 27 Nov 2022 21:34:14 +0100 Subject: [PATCH 0768/1033] Add QNAP QSW binary sensors for each port (#76522) Co-authored-by: J. Nick Koston --- .../components/qnap_qsw/binary_sensor.py | 95 ++++++++++++++++--- homeassistant/components/qnap_qsw/entity.py | 28 +++++- .../components/qnap_qsw/test_binary_sensor.py | 68 ++++++++++++- 3 files changed, 174 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/qnap_qsw/binary_sensor.py b/homeassistant/components/qnap_qsw/binary_sensor.py index 71af89778b8..aeebb6cc055 100644 --- a/homeassistant/components/qnap_qsw/binary_sensor.py +++ b/homeassistant/components/qnap_qsw/binary_sensor.py @@ -1,10 +1,18 @@ """Support for the QNAP QSW binary sensors.""" from __future__ import annotations -from dataclasses import dataclass +from dataclasses import dataclass, replace from typing import Final -from aioqsw.const import QSD_ANOMALY, QSD_FIRMWARE_CONDITION, QSD_MESSAGE +from aioqsw.const import ( + QSD_ANOMALY, + QSD_FIRMWARE_CONDITION, + QSD_LACP_PORTS, + QSD_LINK, + QSD_MESSAGE, + QSD_PORTS, + QSD_PORTS_STATUS, +) from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, @@ -18,7 +26,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ATTR_MESSAGE, DOMAIN, QSW_COORD_DATA from .coordinator import QswDataCoordinator -from .entity import QswEntityDescription, QswSensorEntity +from .entity import QswEntityDescription, QswEntityType, QswSensorEntity @dataclass @@ -28,6 +36,8 @@ class QswBinarySensorEntityDescription( """A class that describes QNAP QSW binary sensor entities.""" attributes: dict[str, list[str]] | None = None + qsw_type: QswEntityType | None = None + sep_key: str = "_" BINARY_SENSOR_TYPES: Final[tuple[QswBinarySensorEntityDescription, ...]] = ( @@ -43,20 +53,77 @@ BINARY_SENSOR_TYPES: Final[tuple[QswBinarySensorEntityDescription, ...]] = ( ), ) +LACP_PORT_BINARY_SENSOR_TYPES: Final[tuple[QswBinarySensorEntityDescription, ...]] = ( + QswBinarySensorEntityDescription( + device_class=BinarySensorDeviceClass.CONNECTIVITY, + entity_registry_enabled_default=False, + key=QSD_PORTS_STATUS, + qsw_type=QswEntityType.LACP_PORT, + name="Link", + subkey=QSD_LINK, + ), +) + +PORT_BINARY_SENSOR_TYPES: Final[tuple[QswBinarySensorEntityDescription, ...]] = ( + QswBinarySensorEntityDescription( + device_class=BinarySensorDeviceClass.CONNECTIVITY, + entity_registry_enabled_default=False, + key=QSD_PORTS_STATUS, + qsw_type=QswEntityType.PORT, + name="Link", + subkey=QSD_LINK, + ), +) + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Add QNAP QSW binary sensors from a config_entry.""" coordinator: QswDataCoordinator = hass.data[DOMAIN][entry.entry_id][QSW_COORD_DATA] - async_add_entities( - QswBinarySensor(coordinator, description, entry) - for description in BINARY_SENSOR_TYPES + + entities: list[QswBinarySensor] = [] + + for description in BINARY_SENSOR_TYPES: if ( description.key in coordinator.data and description.subkey in coordinator.data[description.key] - ) - ) + ): + entities.append(QswBinarySensor(coordinator, description, entry)) + + for description in LACP_PORT_BINARY_SENSOR_TYPES: + if ( + description.key in coordinator.data + and QSD_LACP_PORTS in coordinator.data[description.key] + ): + for port_id, port_values in coordinator.data[description.key][ + QSD_LACP_PORTS + ].items(): + if description.subkey in port_values: + _desc = replace( + description, + sep_key=f"_lacp_port_{port_id}_", + name=f"LACP Port {port_id} {description.name}", + ) + entities.append(QswBinarySensor(coordinator, _desc, entry, port_id)) + + for description in PORT_BINARY_SENSOR_TYPES: + if ( + description.key in coordinator.data + and QSD_PORTS in coordinator.data[description.key] + ): + for port_id, port_values in coordinator.data[description.key][ + QSD_PORTS + ].items(): + if description.subkey in port_values: + _desc = replace( + description, + sep_key=f"_port_{port_id}_", + name=f"Port {port_id} {description.name}", + ) + entities.append(QswBinarySensor(coordinator, _desc, entry, port_id)) + + async_add_entities(entities) class QswBinarySensor(QswSensorEntity, BinarySensorEntity): @@ -69,13 +136,13 @@ class QswBinarySensor(QswSensorEntity, BinarySensorEntity): coordinator: QswDataCoordinator, description: QswBinarySensorEntityDescription, entry: ConfigEntry, + type_id: int | None = None, ) -> None: """Initialize.""" - super().__init__(coordinator, entry) + super().__init__(coordinator, entry, type_id) + self._attr_name = f"{self.product} {description.name}" - self._attr_unique_id = ( - f"{entry.unique_id}_{description.key}_{description.subkey}" - ) + self._attr_unique_id = f"{entry.unique_id}_{description.key}{description.sep_key}{description.subkey}" self.entity_description = description self._async_update_attrs() @@ -83,6 +150,8 @@ class QswBinarySensor(QswSensorEntity, BinarySensorEntity): def _async_update_attrs(self) -> None: """Update binary sensor attributes.""" self._attr_is_on = self.get_device_value( - self.entity_description.key, self.entity_description.subkey + self.entity_description.key, + self.entity_description.subkey, + self.entity_description.qsw_type, ) super()._async_update_attrs() diff --git a/homeassistant/components/qnap_qsw/entity.py b/homeassistant/components/qnap_qsw/entity.py index 7da47f9734f..7d1ec33ee71 100644 --- a/homeassistant/components/qnap_qsw/entity.py +++ b/homeassistant/components/qnap_qsw/entity.py @@ -7,11 +7,14 @@ from typing import Any from aioqsw.const import ( QSD_FIRMWARE, QSD_FIRMWARE_INFO, + QSD_LACP_PORTS, QSD_MAC, + QSD_PORTS, QSD_PRODUCT, QSD_SYSTEM_BOARD, ) +from homeassistant.backports.enum import StrEnum from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_URL from homeassistant.core import callback @@ -23,6 +26,13 @@ from .const import MANUFACTURER from .coordinator import QswDataCoordinator, QswFirmwareCoordinator +class QswEntityType(StrEnum): + """QNAP QSW Entity Type.""" + + LACP_PORT = QSD_LACP_PORTS + PORT = QSD_PORTS + + class QswDataEntity(CoordinatorEntity[QswDataCoordinator]): """Define an QNAP QSW entity.""" @@ -30,10 +40,12 @@ class QswDataEntity(CoordinatorEntity[QswDataCoordinator]): self, coordinator: QswDataCoordinator, entry: ConfigEntry, + type_id: int | None = None, ) -> None: """Initialize.""" super().__init__(coordinator) + self.type_id = type_id self.product = self.get_device_value(QSD_SYSTEM_BOARD, QSD_PRODUCT) self._attr_device_info = DeviceInfo( configuration_url=entry.data[CONF_URL], @@ -49,12 +61,24 @@ class QswDataEntity(CoordinatorEntity[QswDataCoordinator]): sw_version=self.get_device_value(QSD_FIRMWARE_INFO, QSD_FIRMWARE), ) - def get_device_value(self, key: str, subkey: str) -> Any: + def get_device_value( + self, + key: str, + subkey: str, + qsw_type: QswEntityType | None = None, + ) -> Any: """Return device value by key.""" value = None if key in self.coordinator.data: data = self.coordinator.data[key] - if subkey in data: + if qsw_type is not None and self.type_id is not None: + if ( + qsw_type in data + and self.type_id in data[qsw_type] + and subkey in data[qsw_type][self.type_id] + ): + value = data[qsw_type][self.type_id][subkey] + elif subkey in data: value = data[subkey] return value diff --git a/tests/components/qnap_qsw/test_binary_sensor.py b/tests/components/qnap_qsw/test_binary_sensor.py index a36a34f02be..a270e78f051 100644 --- a/tests/components/qnap_qsw/test_binary_sensor.py +++ b/tests/components/qnap_qsw/test_binary_sensor.py @@ -1,17 +1,81 @@ """The binary sensor tests for the QNAP QSW platform.""" +from unittest.mock import AsyncMock + from homeassistant.components.qnap_qsw.const import ATTR_MESSAGE -from homeassistant.const import STATE_OFF +from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry from .util import async_init_integration -async def test_qnap_qsw_create_binary_sensors(hass: HomeAssistant) -> None: +async def test_qnap_qsw_create_binary_sensors( + hass: HomeAssistant, entity_registry_enabled_by_default: AsyncMock +) -> None: """Test creation of binary sensors.""" await async_init_integration(hass) + er = entity_registry.async_get(hass) state = hass.states.get("binary_sensor.qsw_m408_4c_anomaly") assert state.state == STATE_OFF assert state.attributes.get(ATTR_MESSAGE) is None + + state = hass.states.get("binary_sensor.qsw_m408_4c_lacp_port_1_link") + assert state.state == STATE_OFF + entry = er.async_get(state.entity_id) + assert entry.unique_id == "qsw_unique_id_ports-status_lacp_port_1_link" + + state = hass.states.get("binary_sensor.qsw_m408_4c_lacp_port_2_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_lacp_port_3_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_lacp_port_4_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_lacp_port_5_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_lacp_port_6_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_port_1_link") + assert state.state == STATE_ON + entry = er.async_get(state.entity_id) + assert entry.unique_id == "qsw_unique_id_ports-status_port_1_link" + + state = hass.states.get("binary_sensor.qsw_m408_4c_port_2_link") + assert state.state == STATE_ON + + state = hass.states.get("binary_sensor.qsw_m408_4c_port_3_link") + assert state.state == STATE_ON + + state = hass.states.get("binary_sensor.qsw_m408_4c_port_4_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_port_5_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_port_6_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_port_7_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_port_8_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_port_9_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_port_10_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_port_11_link") + assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.qsw_m408_4c_port_12_link") + assert state.state == STATE_OFF From d6e287f47a50e83dd0e4b2b4bb34b1f2ae31ee89 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 27 Nov 2022 10:44:44 -1000 Subject: [PATCH 0769/1033] Add manufacturer and model to bluetooth adapter device (#82769) Co-authored-by: Paulus Schoutsen --- .../components/bluetooth/__init__.py | 4 +++ .../components/bluetooth/manifest.json | 2 +- .../components/nmap_tracker/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 4 +-- requirements_test_all.txt | 4 +-- tests/components/bluetooth/conftest.py | 16 ++++++++++ .../components/bluetooth/test_config_flow.py | 30 +++++++++++++++---- .../components/bluetooth/test_diagnostics.py | 24 +++++++++++++++ tests/conftest.py | 4 +++ 10 files changed, 79 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index be2d0d36fd7..8386178f459 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -10,10 +10,12 @@ from awesomeversion import AwesomeVersion from bluetooth_adapters import ( ADAPTER_ADDRESS, ADAPTER_HW_VERSION, + ADAPTER_MANUFACTURER, ADAPTER_SW_VERSION, DEFAULT_ADDRESS, AdapterDetails, adapter_human_name, + adapter_model, adapter_unique_name, get_adapters, ) @@ -276,6 +278,8 @@ async def async_update_device( config_entry_id=entry.entry_id, name=adapter_human_name(adapter, details[ADAPTER_ADDRESS]), connections={(dr.CONNECTION_BLUETOOTH, details[ADAPTER_ADDRESS])}, + manufacturer=details[ADAPTER_MANUFACTURER], + model=adapter_model(details), sw_version=details.get(ADAPTER_SW_VERSION), hw_version=details.get(ADAPTER_HW_VERSION), ) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 096b166d11c..bb090d2cf13 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -8,7 +8,7 @@ "requirements": [ "bleak==0.19.2", "bleak-retry-connector==2.8.5", - "bluetooth-adapters==0.8.0", + "bluetooth-adapters==0.11.0", "bluetooth-auto-recovery==0.5.2", "bluetooth-data-tools==0.3.0", "dbus-fast==1.75.0" diff --git a/homeassistant/components/nmap_tracker/manifest.json b/homeassistant/components/nmap_tracker/manifest.json index 6e7a9cbee53..a3e7ed50bd3 100644 --- a/homeassistant/components/nmap_tracker/manifest.json +++ b/homeassistant/components/nmap_tracker/manifest.json @@ -6,7 +6,7 @@ "requirements": [ "netmap==0.7.0.2", "getmac==0.8.2", - "mac-vendor-lookup==0.1.11" + "mac-vendor-lookup==0.1.12" ], "codeowners": [], "iot_class": "local_polling", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 25efedf657a..13204a293d2 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ awesomeversion==22.9.0 bcrypt==3.1.7 bleak-retry-connector==2.8.5 bleak==0.19.2 -bluetooth-adapters==0.8.0 +bluetooth-adapters==0.11.0 bluetooth-auto-recovery==0.5.2 bluetooth-data-tools==0.3.0 certifi>=2021.5.30 diff --git a/requirements_all.txt b/requirements_all.txt index 2ac05ea8208..47cf17e93d3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -447,7 +447,7 @@ bluemaestro-ble==0.2.0 # bluepy==1.3.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.8.0 +bluetooth-adapters==0.11.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.5.2 @@ -1055,7 +1055,7 @@ lw12==0.9.2 lxml==4.9.1 # homeassistant.components.nmap_tracker -mac-vendor-lookup==0.1.11 +mac-vendor-lookup==0.1.12 # homeassistant.components.magicseaweed magicseaweed==1.0.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 89b6fb6bbf1..12462c52496 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -361,7 +361,7 @@ blinkpy==0.19.2 bluemaestro-ble==0.2.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.8.0 +bluetooth-adapters==0.11.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.5.2 @@ -769,7 +769,7 @@ luftdaten==0.7.4 lxml==4.9.1 # homeassistant.components.nmap_tracker -mac-vendor-lookup==0.1.11 +mac-vendor-lookup==0.1.12 # homeassistant.components.maxcube maxcube-api==0.4.3 diff --git a/tests/components/bluetooth/conftest.py b/tests/components/bluetooth/conftest.py index c650a84df85..a6593adef49 100644 --- a/tests/components/bluetooth/conftest.py +++ b/tests/components/bluetooth/conftest.py @@ -106,6 +106,10 @@ def one_adapter_fixture(): "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": True, "sw_version": "homeassistant", + "manufacturer": "ACME", + "product": "Bluetooth Adapter 5.0", + "product_id": "aa01", + "vendor_id": "cc01", }, }, ): @@ -132,12 +136,20 @@ def two_adapters_fixture(): "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": False, "sw_version": "homeassistant", + "manufacturer": "ACME", + "product": "Bluetooth Adapter 5.0", + "product_id": "aa01", + "vendor_id": "cc01", }, "hci1": { "address": "00:00:00:00:00:02", "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": True, "sw_version": "homeassistant", + "manufacturer": "ACME", + "product": "Bluetooth Adapter 5.0", + "product_id": "aa01", + "vendor_id": "cc01", }, }, ): @@ -164,6 +176,10 @@ def one_adapter_old_bluez(): "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": False, "sw_version": "homeassistant", + "manufacturer": "ACME", + "product": "Bluetooth Adapter 5.0", + "product_id": "aa01", + "vendor_id": "cc01", }, }, ): diff --git a/tests/components/bluetooth/test_config_flow.py b/tests/components/bluetooth/test_config_flow.py index 76c0346026b..9219d37a399 100644 --- a/tests/components/bluetooth/test_config_flow.py +++ b/tests/components/bluetooth/test_config_flow.py @@ -130,7 +130,10 @@ async def test_async_step_integration_discovery(hass): """Test setting up from integration discovery.""" details = AdapterDetails( - address="00:00:00:00:00:01", sw_version="1.23.5", hw_version="1.2.3" + address="00:00:00:00:00:01", + sw_version="1.23.5", + hw_version="1.2.3", + manufacturer="ACME", ) result = await hass.config_entries.flow.async_init( @@ -159,7 +162,10 @@ async def test_async_step_integration_discovery_during_onboarding_one_adapter( ): """Test setting up from integration discovery during onboarding.""" details = AdapterDetails( - address="00:00:00:00:00:01", sw_version="1.23.5", hw_version="1.2.3" + address="00:00:00:00:00:01", + sw_version="1.23.5", + hw_version="1.2.3", + manufacturer="ACME", ) with patch( @@ -187,10 +193,16 @@ async def test_async_step_integration_discovery_during_onboarding_two_adapters( ): """Test setting up from integration discovery during onboarding.""" details1 = AdapterDetails( - address="00:00:00:00:00:01", sw_version="1.23.5", hw_version="1.2.3" + address="00:00:00:00:00:01", + sw_version="1.23.5", + hw_version="1.2.3", + manufacturer="ACME", ) details2 = AdapterDetails( - address="00:00:00:00:00:02", sw_version="1.23.5", hw_version="1.2.3" + address="00:00:00:00:00:02", + sw_version="1.23.5", + hw_version="1.2.3", + manufacturer="ACME", ) with patch( @@ -226,7 +238,10 @@ async def test_async_step_integration_discovery_during_onboarding_two_adapters( async def test_async_step_integration_discovery_during_onboarding(hass, macos_adapter): """Test setting up from integration discovery during onboarding.""" details = AdapterDetails( - address=DEFAULT_ADDRESS, sw_version="1.23.5", hw_version="1.2.3" + address=DEFAULT_ADDRESS, + sw_version="1.23.5", + hw_version="1.2.3", + manufacturer="ACME", ) with patch( @@ -252,7 +267,10 @@ async def test_async_step_integration_discovery_during_onboarding(hass, macos_ad async def test_async_step_integration_discovery_already_exists(hass): """Test setting up from integration discovery when an entry already exists.""" details = AdapterDetails( - address="00:00:00:00:00:01", sw_version="1.23.5", hw_version="1.2.3" + address="00:00:00:00:00:01", + sw_version="1.23.5", + hw_version="1.2.3", + manufacturer="ACME", ) entry = MockConfigEntry(domain=DOMAIN, unique_id="00:00:00:00:00:01") diff --git a/tests/components/bluetooth/test_diagnostics.py b/tests/components/bluetooth/test_diagnostics.py index 20ffddf3297..bfd39f74303 100644 --- a/tests/components/bluetooth/test_diagnostics.py +++ b/tests/components/bluetooth/test_diagnostics.py @@ -76,12 +76,20 @@ async def test_diagnostics( "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": False, "sw_version": "homeassistant", + "manufacturer": "ACME", + "product": "Bluetooth Adapter 5.0", + "product_id": "aa01", + "vendor_id": "cc01", }, "hci1": { "address": "00:00:00:00:00:02", "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": True, "sw_version": "homeassistant", + "manufacturer": "ACME", + "product": "Bluetooth Adapter 5.0", + "product_id": "aa01", + "vendor_id": "cc01", }, }, "dbus": { @@ -107,12 +115,20 @@ async def test_diagnostics( "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": False, "sw_version": "homeassistant", + "manufacturer": "ACME", + "product": "Bluetooth Adapter 5.0", + "product_id": "aa01", + "vendor_id": "cc01", }, "hci1": { "address": "00:00:00:00:00:02", "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": True, "sw_version": "homeassistant", + "manufacturer": "ACME", + "product": "Bluetooth Adapter 5.0", + "product_id": "aa01", + "vendor_id": "cc01", }, }, "advertisement_tracker": { @@ -249,6 +265,10 @@ async def test_diagnostics_macos( "address": "00:00:00:00:00:00", "passive_scan": False, "sw_version": ANY, + "manufacturer": "Apple", + "product": "Unknown MacOS Model", + "product_id": "Unknown", + "vendor_id": "Unknown", } }, "manager": { @@ -257,6 +277,10 @@ async def test_diagnostics_macos( "address": "00:00:00:00:00:00", "passive_scan": False, "sw_version": ANY, + "manufacturer": "Apple", + "product": "Unknown MacOS Model", + "product_id": "Unknown", + "vendor_id": "Unknown", } }, "advertisement_tracker": { diff --git a/tests/conftest.py b/tests/conftest.py index ca005625655..a508b8c32ef 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1050,6 +1050,10 @@ def mock_bluetooth_adapters(): "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": False, "sw_version": "homeassistant", + "manufacturer": "ACME", + "product": "Bluetooth Adapter 5.0", + "product_id": "aa01", + "vendor_id": "cc01", }, }, ): From 4517af509c3e3f0c795e1e22c7e443a2b81dad07 Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Sun, 27 Nov 2022 23:33:12 +0100 Subject: [PATCH 0770/1033] Add support for KNX IP-Secure routing (#82765) * always use instance variable for new entry data - change `self._tunneling_config` to non-optional `self.new_entry_data` - always use self.new_entry_data in `finish_flow()` * support secure routing * amend current tests * use sync latency tolerance * test secure routing config flow * diagnostics redact backbone_key * test xknx library setup * check length of backbone_key * better readable key validation --- homeassistant/components/knx/__init__.py | 37 +++- homeassistant/components/knx/config_flow.py | 168 ++++++++++++------ homeassistant/components/knx/const.py | 5 + homeassistant/components/knx/diagnostics.py | 2 + homeassistant/components/knx/strings.json | 40 ++++- .../components/knx/translations/en.json | 60 +++++-- tests/components/knx/test_config_flow.py | 167 ++++++++++++++++- tests/components/knx/test_diagnostic.py | 3 + tests/components/knx/test_init.py | 28 +++ 9 files changed, 422 insertions(+), 88 deletions(-) diff --git a/homeassistant/components/knx/__init__.py b/homeassistant/components/knx/__init__.py index a486582daec..c8b9ca40bd7 100644 --- a/homeassistant/components/knx/__init__.py +++ b/homeassistant/components/knx/__init__.py @@ -51,6 +51,9 @@ from .const import ( CONF_KNX_RATE_LIMIT, CONF_KNX_ROUTE_BACK, CONF_KNX_ROUTING, + CONF_KNX_ROUTING_BACKBONE_KEY, + CONF_KNX_ROUTING_SECURE, + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE, CONF_KNX_SECURE_DEVICE_AUTHENTICATION, CONF_KNX_SECURE_USER_ID, CONF_KNX_SECURE_USER_PASSWORD, @@ -406,15 +409,15 @@ class KNXModule: auto_reconnect=True, threaded=True, ) - if _conn_type == CONF_KNX_TUNNELING_TCP_SECURE: - knxkeys_file: str | None = ( - self.hass.config.path( - STORAGE_DIR, - self.entry.data[CONF_KNX_KNXKEY_FILENAME], - ) - if self.entry.data.get(CONF_KNX_KNXKEY_FILENAME) is not None - else None + knxkeys_file: str | None = ( + self.hass.config.path( + STORAGE_DIR, + self.entry.data[CONF_KNX_KNXKEY_FILENAME], ) + if self.entry.data.get(CONF_KNX_KNXKEY_FILENAME) is not None + else None + ) + if _conn_type == CONF_KNX_TUNNELING_TCP_SECURE: return ConnectionConfig( connection_type=ConnectionType.TUNNELING_TCP_SECURE, gateway_ip=self.entry.data[CONF_HOST], @@ -431,6 +434,24 @@ class KNXModule: auto_reconnect=True, threaded=True, ) + if _conn_type == CONF_KNX_ROUTING_SECURE: + return ConnectionConfig( + connection_type=ConnectionType.ROUTING_SECURE, + individual_address=self.entry.data[CONF_KNX_INDIVIDUAL_ADDRESS], + multicast_group=self.entry.data[CONF_KNX_MCAST_GRP], + multicast_port=self.entry.data[CONF_KNX_MCAST_PORT], + local_ip=self.entry.data.get(CONF_KNX_LOCAL_IP), + secure_config=SecureConfig( + backbone_key=self.entry.data.get(CONF_KNX_ROUTING_BACKBONE_KEY), + latency_ms=self.entry.data.get( + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE + ), + knxkeys_password=self.entry.data.get(CONF_KNX_KNXKEY_PASSWORD), + knxkeys_file_path=knxkeys_file, + ), + auto_reconnect=True, + threaded=True, + ) return ConnectionConfig( auto_reconnect=True, threaded=True, diff --git a/homeassistant/components/knx/config_flow.py b/homeassistant/components/knx/config_flow.py index c4107ed7e8b..e2ff0908bbe 100644 --- a/homeassistant/components/knx/config_flow.py +++ b/homeassistant/components/knx/config_flow.py @@ -33,6 +33,9 @@ from .const import ( CONF_KNX_RATE_LIMIT, CONF_KNX_ROUTE_BACK, CONF_KNX_ROUTING, + CONF_KNX_ROUTING_BACKBONE_KEY, + CONF_KNX_ROUTING_SECURE, + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE, CONF_KNX_SECURE_DEVICE_AUTHENTICATION, CONF_KNX_SECURE_USER_ID, CONF_KNX_SECURE_USER_PASSWORD, @@ -87,13 +90,13 @@ class KNXCommonFlow(ABC, FlowHandler): def __init__(self, initial_data: KNXConfigEntryData) -> None: """Initialize KNXCommonFlow.""" self.initial_data = initial_data + self.new_entry_data = KNXConfigEntryData() self._found_gateways: list[GatewayDescriptor] = [] self._found_tunnels: list[GatewayDescriptor] = [] self._selected_tunnel: GatewayDescriptor | None = None - self._tunneling_config: KNXConfigEntryData | None = None @abstractmethod - def finish_flow(self, new_entry_data: KNXConfigEntryData, title: str) -> FlowResult: + def finish_flow(self, title: str) -> FlowResult: """Finish the flow.""" async def async_step_connection_type( @@ -119,11 +122,8 @@ class KNXCommonFlow(ABC, FlowHandler): return await self.async_step_tunnel() # Automatic connection type - entry_data = KNXConfigEntryData(connection_type=CONF_KNX_AUTOMATIC) - return self.finish_flow( - new_entry_data=entry_data, - title=CONF_KNX_AUTOMATIC.capitalize(), - ) + self.new_entry_data = KNXConfigEntryData(connection_type=CONF_KNX_AUTOMATIC) + return self.finish_flow(title=CONF_KNX_AUTOMATIC.capitalize()) supported_connection_types = { CONF_KNX_TUNNELING: CONF_KNX_TUNNELING.capitalize(), @@ -163,7 +163,7 @@ class KNXCommonFlow(ABC, FlowHandler): if self._selected_tunnel.supports_tunnelling_tcp else CONF_KNX_TUNNELING ) - self._tunneling_config = KNXConfigEntryData( + self.new_entry_data = KNXConfigEntryData( host=self._selected_tunnel.ip_addr, port=self._selected_tunnel.port, route_back=False, @@ -171,13 +171,10 @@ class KNXCommonFlow(ABC, FlowHandler): ) if connection_type == CONF_KNX_TUNNELING_TCP_SECURE: return self.async_show_menu( - step_id="secure_tunneling", + step_id="secure_key_source", menu_options=["secure_knxkeys", "secure_tunnel_manual"], ) - return self.finish_flow( - new_entry_data=self._tunneling_config, - title=f"Tunneling @ {self._selected_tunnel}", - ) + return self.finish_flow(title=f"Tunneling @ {self._selected_tunnel}") if not self._found_tunnels: return await self.async_step_manual_tunnel() @@ -211,7 +208,7 @@ class KNXCommonFlow(ABC, FlowHandler): if not errors: connection_type = user_input[CONF_KNX_TUNNELING_TYPE] - self._tunneling_config = KNXConfigEntryData( + self.new_entry_data = KNXConfigEntryData( host=_host, port=user_input[CONF_PORT], route_back=user_input[CONF_KNX_ROUTE_BACK], @@ -221,13 +218,10 @@ class KNXCommonFlow(ABC, FlowHandler): if connection_type == CONF_KNX_TUNNELING_TCP_SECURE: return self.async_show_menu( - step_id="secure_tunneling", - menu_options=["secure_knxkeys", "secure_tunnel_manual"], + step_id="secure_key_source", + menu_options=["secure_knxkeys", "secure_routing_manual"], ) - return self.finish_flow( - new_entry_data=self._tunneling_config, - title=f"Tunneling @ {_host}", - ) + return self.finish_flow(title=f"Tunneling @ {_host}") _reconfiguring_existing_tunnel = ( self.initial_data.get(CONF_KNX_CONNECTION_TYPE) @@ -290,20 +284,18 @@ class KNXCommonFlow(ABC, FlowHandler): async def async_step_secure_tunnel_manual( self, user_input: dict | None = None ) -> FlowResult: - """Configure ip secure manually.""" + """Configure ip secure tunnelling manually.""" errors: dict = {} if user_input is not None: - assert self._tunneling_config - entry_data = self._tunneling_config | KNXConfigEntryData( + self.new_entry_data |= KNXConfigEntryData( connection_type=CONF_KNX_TUNNELING_TCP_SECURE, device_authentication=user_input[CONF_KNX_SECURE_DEVICE_AUTHENTICATION], user_id=user_input[CONF_KNX_SECURE_USER_ID], user_password=user_input[CONF_KNX_SECURE_USER_PASSWORD], ) return self.finish_flow( - new_entry_data=entry_data, - title=f"Secure Tunneling @ {self._tunneling_config[CONF_HOST]}", + title=f"Secure Tunneling @ {self.new_entry_data[CONF_HOST]}" ) fields = { @@ -338,6 +330,60 @@ class KNXCommonFlow(ABC, FlowHandler): errors=errors, ) + async def async_step_secure_routing_manual( + self, user_input: dict | None = None + ) -> FlowResult: + """Configure ip secure routing manually.""" + errors: dict = {} + + if user_input is not None: + try: + key_bytes = bytes.fromhex(user_input[CONF_KNX_ROUTING_BACKBONE_KEY]) + if len(key_bytes) != 16: + raise ValueError + except ValueError: + errors[CONF_KNX_ROUTING_BACKBONE_KEY] = "invalid_backbone_key" + if not errors: + self.new_entry_data |= KNXConfigEntryData( + backbone_key=user_input[CONF_KNX_ROUTING_BACKBONE_KEY], + sync_latency_tolerance=user_input[ + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE + ], + ) + return self.finish_flow( + title=f"Secure Routing as {self.new_entry_data[CONF_KNX_INDIVIDUAL_ADDRESS]}" + ) + + fields = { + vol.Required( + CONF_KNX_ROUTING_BACKBONE_KEY, + default=self.initial_data.get(CONF_KNX_ROUTING_BACKBONE_KEY), + ): selector.TextSelector( + selector.TextSelectorConfig(type=selector.TextSelectorType.PASSWORD), + ), + vol.Required( + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE, + default=self.initial_data.get(CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE) + or 1000, + ): vol.All( + selector.NumberSelector( + selector.NumberSelectorConfig( + min=400, + max=4000, + unit_of_measurement="ms", + mode=selector.NumberSelectorMode.BOX, + ), + ), + vol.Coerce(int), + ), + } + + return self.async_show_form( + step_id="secure_routing_manual", + data_schema=vol.Schema(fields), + errors=errors, + ) + async def async_step_secure_knxkeys( self, user_input: dict | None = None ) -> FlowResult: @@ -345,7 +391,6 @@ class KNXCommonFlow(ABC, FlowHandler): errors = {} if user_input is not None: - assert self._tunneling_config storage_key = CONST_KNX_STORAGE_KEY + user_input[CONF_KNX_KNXKEY_FILENAME] try: await load_keyring( @@ -358,15 +403,20 @@ class KNXCommonFlow(ABC, FlowHandler): errors[CONF_KNX_KNXKEY_PASSWORD] = "invalid_signature" if not errors: - entry_data = self._tunneling_config | KNXConfigEntryData( - connection_type=CONF_KNX_TUNNELING_TCP_SECURE, + self.new_entry_data |= KNXConfigEntryData( + backbone_key=None, + sync_latency_tolerance=None, knxkeys_filename=storage_key, knxkeys_password=user_input[CONF_KNX_KNXKEY_PASSWORD], ) - return self.finish_flow( - new_entry_data=entry_data, - title=f"Secure Tunneling @ {self._tunneling_config[CONF_HOST]}", - ) + if ( + self.new_entry_data[CONF_KNX_CONNECTION_TYPE] + == CONF_KNX_ROUTING_SECURE + ): + title = f"Secure Routing as {self.new_entry_data[CONF_KNX_INDIVIDUAL_ADDRESS]}" + else: + title = f"Secure Tunneling @ {self.new_entry_data[CONF_HOST]}" + return self.finish_flow(title=title) fields = { vol.Required( @@ -418,34 +468,46 @@ class KNXCommonFlow(ABC, FlowHandler): errors[CONF_KNX_LOCAL_IP] = "invalid_ip_address" if not errors: - entry_data = KNXConfigEntryData( - connection_type=CONF_KNX_ROUTING, + connection_type = ( + CONF_KNX_ROUTING_SECURE + if user_input[CONF_KNX_ROUTING_SECURE] + else CONF_KNX_ROUTING + ) + self.new_entry_data = KNXConfigEntryData( + connection_type=connection_type, individual_address=_individual_address, multicast_group=_multicast_group, multicast_port=_multicast_port, local_ip=_local_ip, ) - return self.finish_flow( - new_entry_data=entry_data, - title=f"Routing as {_individual_address}", - ) + if connection_type == CONF_KNX_ROUTING_SECURE: + return self.async_show_menu( + step_id="secure_key_source", + menu_options=["secure_knxkeys", "secure_routing_manual"], + ) + return self.finish_flow(title=f"Routing as {_individual_address}") + + routers = [router for router in self._found_gateways if router.supports_routing] + if not routers: + errors["base"] = "no_router_discovered" + default_secure_routing_enable = any( + router for router in routers if router.routing_requires_secure + ) fields = { vol.Required( CONF_KNX_INDIVIDUAL_ADDRESS, default=_individual_address ): _IA_SELECTOR, + vol.Required( + CONF_KNX_ROUTING_SECURE, default=default_secure_routing_enable + ): selector.BooleanSelector(), vol.Required(CONF_KNX_MCAST_GRP, default=_multicast_group): _IP_SELECTOR, vol.Required(CONF_KNX_MCAST_PORT, default=_multicast_port): _PORT_SELECTOR, } - if self.show_advanced_options: # Optional with default doesn't work properly in flow UI fields[vol.Optional(CONF_KNX_LOCAL_IP)] = _IP_SELECTOR - if not any( - router for router in self._found_gateways if router.supports_routing - ): - errors["base"] = "no_router_discovered" return self.async_show_form( step_id="routing", data_schema=vol.Schema(fields), errors=errors ) @@ -467,11 +529,11 @@ class KNXConfigFlow(KNXCommonFlow, ConfigFlow, domain=DOMAIN): return KNXOptionsFlow(config_entry) @callback - def finish_flow(self, new_entry_data: KNXConfigEntryData, title: str) -> FlowResult: + def finish_flow(self, title: str) -> FlowResult: """Create the ConfigEntry.""" return self.async_create_entry( title=title, - data=DEFAULT_ENTRY_DATA | new_entry_data, + data=DEFAULT_ENTRY_DATA | self.new_entry_data, ) async def async_step_user(self, user_input: dict | None = None) -> FlowResult: @@ -492,11 +554,9 @@ class KNXOptionsFlow(KNXCommonFlow, OptionsFlow): super().__init__(initial_data=config_entry.data) # type: ignore[arg-type] @callback - def finish_flow( - self, new_entry_data: KNXConfigEntryData, title: str | None - ) -> FlowResult: + def finish_flow(self, title: str | None) -> FlowResult: """Update the ConfigEntry and finish the flow.""" - new_data = DEFAULT_ENTRY_DATA | self.initial_data | new_entry_data + new_data = DEFAULT_ENTRY_DATA | self.initial_data | self.new_entry_data self.hass.config_entries.async_update_entry( self.config_entry, data=new_data, @@ -518,13 +578,11 @@ class KNXOptionsFlow(KNXCommonFlow, OptionsFlow): ) -> FlowResult: """Manage KNX communication settings.""" if user_input is not None: - return self.finish_flow( - new_entry_data=KNXConfigEntryData( - state_updater=user_input[CONF_KNX_STATE_UPDATER], - rate_limit=user_input[CONF_KNX_RATE_LIMIT], - ), - title=None, + self.new_entry_data = KNXConfigEntryData( + state_updater=user_input[CONF_KNX_STATE_UPDATER], + rate_limit=user_input[CONF_KNX_RATE_LIMIT], ) + return self.finish_flow(title=None) data_schema = { vol.Required( diff --git a/homeassistant/components/knx/const.py b/homeassistant/components/knx/const.py index a6a88b2010a..4a8b113516c 100644 --- a/homeassistant/components/knx/const.py +++ b/homeassistant/components/knx/const.py @@ -30,6 +30,9 @@ CONF_KNX_INDIVIDUAL_ADDRESS: Final = "individual_address" CONF_KNX_CONNECTION_TYPE: Final = "connection_type" CONF_KNX_AUTOMATIC: Final = "automatic" CONF_KNX_ROUTING: Final = "routing" +CONF_KNX_ROUTING_BACKBONE_KEY: Final = "backbone_key" +CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: Final = "sync_latency_tolerance" +CONF_KNX_ROUTING_SECURE: Final = "routing_secure" CONF_KNX_TUNNELING: Final = "tunneling" CONF_KNX_TUNNELING_TCP: Final = "tunneling_tcp" CONF_KNX_TUNNELING_TCP_SECURE: Final = "tunneling_tcp_secure" @@ -92,6 +95,8 @@ class KNXConfigEntryData(TypedDict, total=False): device_authentication: str knxkeys_filename: str knxkeys_password: str + backbone_key: str | None + sync_latency_tolerance: int | None class ColorTempModes(Enum): diff --git a/homeassistant/components/knx/diagnostics.py b/homeassistant/components/knx/diagnostics.py index c409b4116bf..3dd14aef653 100644 --- a/homeassistant/components/knx/diagnostics.py +++ b/homeassistant/components/knx/diagnostics.py @@ -13,12 +13,14 @@ from homeassistant.core import HomeAssistant from . import CONFIG_SCHEMA from .const import ( CONF_KNX_KNXKEY_PASSWORD, + CONF_KNX_ROUTING_BACKBONE_KEY, CONF_KNX_SECURE_DEVICE_AUTHENTICATION, CONF_KNX_SECURE_USER_PASSWORD, DOMAIN, ) TO_REDACT = { + CONF_KNX_ROUTING_BACKBONE_KEY, CONF_KNX_KNXKEY_PASSWORD, CONF_KNX_SECURE_USER_PASSWORD, CONF_KNX_SECURE_DEVICE_AUTHENTICATION, diff --git a/homeassistant/components/knx/strings.json b/homeassistant/components/knx/strings.json index d87ad6ac177..0fbb7fc3ae6 100644 --- a/homeassistant/components/knx/strings.json +++ b/homeassistant/components/knx/strings.json @@ -29,11 +29,12 @@ "local_ip": "Leave blank to use auto-discovery." } }, - "secure_tunneling": { + "secure_key_source": { "description": "Select how you want to configure KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", - "secure_tunnel_manual": "Configure IP secure keys manually" + "secure_tunnel_manual": "Configure IP secure credentials manually", + "secure_routing_manual": "Configure IP secure backbone key manually" } }, "secure_knxkeys": { @@ -60,10 +61,22 @@ "device_authentication": "This is set in the 'IP' panel of the interface in ETS." } }, + "secure_routing_manual": { + "description": "Please enter your IP secure information.", + "data": { + "backbone_key": "Backbone key", + "sync_latency_tolerance": "Network latency tolerance" + }, + "data_description": { + "backbone_key": "Can be seen in the 'Security' report of an ETS project. Eg. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Default is 1000." + } + }, "routing": { "description": "Please configure the routing options.", "data": { "individual_address": "Individual address", + "routing_secure": "Use KNX IP Secure", "multicast_group": "Multicast group", "multicast_port": "Multicast port", "local_ip": "Local IP of Home Assistant" @@ -80,6 +93,7 @@ }, "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_backbone_key": "Invalid backbone key. 32 hexadecimal numbers expected.", "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", "invalid_ip_address": "Invalid IPv4 address.", "invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", @@ -134,11 +148,12 @@ "local_ip": "[%key:component::knx::config::step::manual_tunnel::data_description::local_ip%]" } }, - "secure_tunneling": { - "description": "[%key:component::knx::config::step::secure_tunneling::description%]", + "secure_key_source": { + "description": "[%key:component::knx::config::step::secure_key_source::description%]", "menu_options": { - "secure_knxkeys": "[%key:component::knx::config::step::secure_tunneling::menu_options::secure_knxkeys%]", - "secure_tunnel_manual": "[%key:component::knx::config::step::secure_tunneling::menu_options::secure_tunnel_manual%]" + "secure_knxkeys": "[%key:component::knx::config::step::secure_key_source::menu_options::secure_knxkeys%]", + "secure_tunnel_manual": "[%key:component::knx::config::step::secure_key_source::menu_options::secure_tunnel_manual%]", + "secure_routing_manual": "[%key:component::knx::config::step::secure_key_source::menu_options::secure_routing_manual%]" } }, "secure_knxkeys": { @@ -165,10 +180,22 @@ "device_authentication": "[%key:component::knx::config::step::secure_tunnel_manual::data_description::device_authentication%]" } }, + "secure_routing_manual": { + "description": "[%key:component::knx::config::step::secure_routing_manual::description%]", + "data": { + "backbone_key": "[%key:component::knx::config::step::secure_routing_manual::data::backbone_key%]", + "sync_latency_tolerance": "[%key:component::knx::config::step::secure_routing_manual::data::sync_latency_tolerance%]" + }, + "data_description": { + "backbone_key": "[%key:component::knx::config::step::secure_routing_manual::data_description::backbone_key%]", + "sync_latency_tolerance": "[%key:component::knx::config::step::secure_routing_manual::data_description::sync_latency_tolerance%]" + } + }, "routing": { "description": "[%key:component::knx::config::step::routing::description%]", "data": { "individual_address": "[%key:component::knx::config::step::routing::data::individual_address%]", + "routing_secure": "[%key:component::knx::config::step::routing::data::routing_secure%]", "multicast_group": "[%key:component::knx::config::step::routing::data::multicast_group%]", "multicast_port": "[%key:component::knx::config::step::routing::data::multicast_port%]", "local_ip": "[%key:component::knx::config::step::routing::data::local_ip%]" @@ -181,6 +208,7 @@ }, "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_backbone_key": "[%key:component::knx::config::error::invalid_backbone_key%]", "invalid_individual_address": "[%key:component::knx::config::error::invalid_individual_address%]", "invalid_ip_address": "[%key:component::knx::config::error::invalid_ip_address%]", "invalid_signature": "[%key:component::knx::config::error::invalid_signature%]", diff --git a/homeassistant/components/knx/translations/en.json b/homeassistant/components/knx/translations/en.json index c45c98b070a..8ed45661d0d 100644 --- a/homeassistant/components/knx/translations/en.json +++ b/homeassistant/components/knx/translations/en.json @@ -7,6 +7,7 @@ "error": { "cannot_connect": "Failed to connect", "file_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/", + "invalid_backbone_key": "Invalid backbone key. 32 hexadecimal numbers expected.", "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", "invalid_ip_address": "Invalid IPv4 address.", "invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", @@ -41,7 +42,8 @@ "individual_address": "Individual address", "local_ip": "Local IP of Home Assistant", "multicast_group": "Multicast group", - "multicast_port": "Multicast port" + "multicast_port": "Multicast port", + "routing_secure": "Use KNX IP Secure" }, "data_description": { "individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`", @@ -49,6 +51,14 @@ }, "description": "Please configure the routing options." }, + "secure_key_source": { + "description": "Select how you want to configure KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", + "secure_routing_manual": "Configure IP secure backbone key manually", + "secure_tunnel_manual": "Configure IP secure credentials manually" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "The filename of your `.knxkeys` file (including extension)", @@ -60,6 +70,17 @@ }, "description": "Please enter the information for your `.knxkeys` file." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Backbone key", + "sync_latency_tolerance": "Network latency tolerance" + }, + "data_description": { + "backbone_key": "Can be seen in the 'Security' report of an ETS project. Eg. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Default is 1000." + }, + "description": "Please enter your IP secure information." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Device authentication password", @@ -73,13 +94,6 @@ }, "description": "Please enter your IP secure information." }, - "secure_tunneling": { - "description": "Select how you want to configure KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", - "secure_tunnel_manual": "Configure IP secure keys manually" - } - }, "tunnel": { "data": { "gateway": "KNX Tunnel Connection" @@ -92,6 +106,7 @@ "error": { "cannot_connect": "Failed to connect", "file_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/", + "invalid_backbone_key": "Invalid backbone key. 32 hexadecimal numbers expected.", "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", "invalid_ip_address": "Invalid IPv4 address.", "invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", @@ -142,7 +157,8 @@ "individual_address": "Individual address", "local_ip": "Local IP of Home Assistant", "multicast_group": "Multicast group", - "multicast_port": "Multicast port" + "multicast_port": "Multicast port", + "routing_secure": "Use KNX IP Secure" }, "data_description": { "individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`", @@ -150,6 +166,14 @@ }, "description": "Please configure the routing options." }, + "secure_key_source": { + "description": "Select how you want to configure KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", + "secure_routing_manual": "Configure IP secure backbone key manually", + "secure_tunnel_manual": "Configure IP secure credentials manually" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "The filename of your `.knxkeys` file (including extension)", @@ -161,6 +185,17 @@ }, "description": "Please enter the information for your `.knxkeys` file." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Backbone key", + "sync_latency_tolerance": "Network latency tolerance" + }, + "data_description": { + "backbone_key": "Can be seen in the 'Security' report of an ETS project. Eg. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Default is 1000." + }, + "description": "Please enter your IP secure information." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Device authentication password", @@ -174,13 +209,6 @@ }, "description": "Please enter your IP secure information." }, - "secure_tunneling": { - "description": "Select how you want to configure KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", - "secure_tunnel_manual": "Configure IP secure keys manually" - } - }, "tunnel": { "data": { "gateway": "KNX Tunnel Connection" diff --git a/tests/components/knx/test_config_flow.py b/tests/components/knx/test_config_flow.py index d57007ed28b..2ce3793937b 100644 --- a/tests/components/knx/test_config_flow.py +++ b/tests/components/knx/test_config_flow.py @@ -26,6 +26,9 @@ from homeassistant.components.knx.const import ( CONF_KNX_RATE_LIMIT, CONF_KNX_ROUTE_BACK, CONF_KNX_ROUTING, + CONF_KNX_ROUTING_BACKBONE_KEY, + CONF_KNX_ROUTING_SECURE, + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE, CONF_KNX_SECURE_DEVICE_AUTHENTICATION, CONF_KNX_SECURE_USER_ID, CONF_KNX_SECURE_USER_PASSWORD, @@ -197,6 +200,162 @@ async def test_routing_setup_advanced(hass: HomeAssistant) -> None: assert len(mock_setup_entry.mock_calls) == 1 +async def test_routing_secure_manual_setup(hass: HomeAssistant) -> None: + """Test routing secure setup with manual key config.""" + with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: + gateways.return_value = [] + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert not result["errors"] + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING, + }, + ) + await hass.async_block_till_done() + assert result2["type"] == FlowResultType.FORM + assert result2["step_id"] == "routing" + assert result2["errors"] == {"base": "no_router_discovered"} + + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + { + CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, + CONF_KNX_MCAST_PORT: 3671, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.123", + CONF_KNX_ROUTING_SECURE: True, + }, + ) + assert result3["type"] == FlowResultType.MENU + assert result3["step_id"] == "secure_key_source" + + result4 = await hass.config_entries.flow.async_configure( + result3["flow_id"], + {"next_step_id": "secure_routing_manual"}, + ) + assert result4["type"] == FlowResultType.FORM + assert result4["step_id"] == "secure_routing_manual" + assert not result4["errors"] + + result_invalid_key1 = await hass.config_entries.flow.async_configure( + result4["flow_id"], + { + CONF_KNX_ROUTING_BACKBONE_KEY: "xxaacc44bbaacc44bbaacc44bbaaccyy", # invalid hex string + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: 2000, + }, + ) + assert result_invalid_key1["type"] == FlowResultType.FORM + assert result_invalid_key1["step_id"] == "secure_routing_manual" + assert result_invalid_key1["errors"] == {"backbone_key": "invalid_backbone_key"} + + result_invalid_key2 = await hass.config_entries.flow.async_configure( + result4["flow_id"], + { + CONF_KNX_ROUTING_BACKBONE_KEY: "bbaacc44bbaacc44", # invalid length + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: 2000, + }, + ) + assert result_invalid_key2["type"] == FlowResultType.FORM + assert result_invalid_key2["step_id"] == "secure_routing_manual" + assert result_invalid_key2["errors"] == {"backbone_key": "invalid_backbone_key"} + + with patch( + "homeassistant.components.knx.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + secure_routing_manual = await hass.config_entries.flow.async_configure( + result_invalid_key2["flow_id"], + { + CONF_KNX_ROUTING_BACKBONE_KEY: "bbaacc44bbaacc44bbaacc44bbaacc44", + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: 2000, + }, + ) + await hass.async_block_till_done() + assert secure_routing_manual["type"] == FlowResultType.CREATE_ENTRY + assert secure_routing_manual["title"] == "Secure Routing as 0.0.123" + assert secure_routing_manual["data"] == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING_SECURE, + CONF_KNX_ROUTING_BACKBONE_KEY: "bbaacc44bbaacc44bbaacc44bbaacc44", + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: 2000, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.123", + } + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_routing_secure_keyfile(hass: HomeAssistant) -> None: + """Test routing secure setup with keyfile.""" + with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: + gateways.return_value = [] + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert not result["errors"] + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING, + }, + ) + await hass.async_block_till_done() + assert result2["type"] == FlowResultType.FORM + assert result2["step_id"] == "routing" + assert result2["errors"] == {"base": "no_router_discovered"} + + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + { + CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, + CONF_KNX_MCAST_PORT: 3671, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.123", + CONF_KNX_ROUTING_SECURE: True, + }, + ) + assert result3["type"] == FlowResultType.MENU + assert result3["step_id"] == "secure_key_source" + + result4 = await hass.config_entries.flow.async_configure( + result3["flow_id"], + {"next_step_id": "secure_knxkeys"}, + ) + assert result4["type"] == FlowResultType.FORM + assert result4["step_id"] == "secure_knxkeys" + assert not result4["errors"] + + with patch( + "homeassistant.components.knx.async_setup_entry", + return_value=True, + ) as mock_setup_entry, patch( + "homeassistant.components.knx.config_flow.load_keyring", return_value=True + ): + routing_secure_knxkeys = await hass.config_entries.flow.async_configure( + result4["flow_id"], + { + CONF_KNX_KNXKEY_FILENAME: "testcase.knxkeys", + CONF_KNX_KNXKEY_PASSWORD: "password", + }, + ) + await hass.async_block_till_done() + assert routing_secure_knxkeys["type"] == FlowResultType.CREATE_ENTRY + assert routing_secure_knxkeys["title"] == "Secure Routing as 0.0.123" + assert routing_secure_knxkeys["data"] == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING_SECURE, + CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", + CONF_KNX_KNXKEY_PASSWORD: "password", + CONF_KNX_ROUTING_BACKBONE_KEY: None, + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.123", + } + assert len(mock_setup_entry.mock_calls) == 1 + + @pytest.mark.parametrize( "user_input,config_entry_data", [ @@ -506,7 +665,7 @@ async def test_form_with_automatic_connection_handling(hass: HomeAssistant) -> N async def _get_menu_step(hass: HomeAssistant) -> FlowResult: - """Return flow in secure_tunnellinn menu step.""" + """Return flow in secure_tunnelling menu step.""" gateway = _gateway_descriptor( "192.168.0.1", 3675, @@ -538,7 +697,7 @@ async def _get_menu_step(hass: HomeAssistant) -> FlowResult: ) await hass.async_block_till_done() assert result3["type"] == FlowResultType.MENU - assert result3["step_id"] == "secure_tunneling" + assert result3["step_id"] == "secure_key_source" return result3 @@ -588,7 +747,7 @@ async def test_get_secure_menu_step_manual_tunnelling( ) await hass.async_block_till_done() assert result3["type"] == FlowResultType.MENU - assert result3["step_id"] == "secure_tunneling" + assert result3["step_id"] == "secure_key_source" async def test_configure_secure_tunnel_manual(hass: HomeAssistant): @@ -665,6 +824,8 @@ async def test_configure_secure_knxkeys(hass: HomeAssistant): CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", CONF_KNX_KNXKEY_PASSWORD: "password", + CONF_KNX_ROUTING_BACKBONE_KEY: None, + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, CONF_HOST: "192.168.0.1", CONF_PORT: 3675, CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", diff --git a/tests/components/knx/test_diagnostic.py b/tests/components/knx/test_diagnostic.py index f40649ee1eb..130f1f70f35 100644 --- a/tests/components/knx/test_diagnostic.py +++ b/tests/components/knx/test_diagnostic.py @@ -14,6 +14,7 @@ from homeassistant.components.knx.const import ( CONF_KNX_MCAST_GRP, CONF_KNX_MCAST_PORT, CONF_KNX_RATE_LIMIT, + CONF_KNX_ROUTING_BACKBONE_KEY, CONF_KNX_SECURE_DEVICE_AUTHENTICATION, CONF_KNX_SECURE_USER_PASSWORD, CONF_KNX_STATE_UPDATER, @@ -107,6 +108,7 @@ async def test_diagnostic_redact( CONF_KNX_KNXKEY_PASSWORD: "password", CONF_KNX_SECURE_USER_PASSWORD: "user_password", CONF_KNX_SECURE_DEVICE_AUTHENTICATION: "device_authentication", + CONF_KNX_ROUTING_BACKBONE_KEY: "bbaacc44bbaacc44bbaacc44bbaacc44", }, ) knx: KNXTestKit = KNXTestKit(hass, mock_config_entry) @@ -128,6 +130,7 @@ async def test_diagnostic_redact( "knxkeys_password": "**REDACTED**", "user_password": "**REDACTED**", "device_authentication": "**REDACTED**", + "backbone_key": "**REDACTED**", }, "configuration_error": None, "configuration_yaml": None, diff --git a/tests/components/knx/test_init.py b/tests/components/knx/test_init.py index 4bda407ace7..3a8a42fdf98 100644 --- a/tests/components/knx/test_init.py +++ b/tests/components/knx/test_init.py @@ -23,6 +23,9 @@ from homeassistant.components.knx.const import ( CONF_KNX_RATE_LIMIT, CONF_KNX_ROUTE_BACK, CONF_KNX_ROUTING, + CONF_KNX_ROUTING_BACKBONE_KEY, + CONF_KNX_ROUTING_SECURE, + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE, CONF_KNX_SECURE_DEVICE_AUTHENTICATION, CONF_KNX_SECURE_USER_ID, CONF_KNX_SECURE_USER_PASSWORD, @@ -167,6 +170,31 @@ from tests.common import MockConfigEntry threaded=True, ), ), + ( + { + CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING_SECURE, + CONF_KNX_LOCAL_IP: "192.168.1.1", + CONF_KNX_RATE_LIMIT: CONF_KNX_DEFAULT_RATE_LIMIT, + CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, + CONF_KNX_MCAST_PORT: DEFAULT_MCAST_PORT, + CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, + CONF_KNX_INDIVIDUAL_ADDRESS: DEFAULT_ROUTING_IA, + CONF_KNX_ROUTING_BACKBONE_KEY: "bbaacc44bbaacc44bbaacc44bbaacc44", + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: 2000, + }, + ConnectionConfig( + connection_type=ConnectionType.ROUTING_SECURE, + individual_address=DEFAULT_ROUTING_IA, + multicast_group=DEFAULT_MCAST_GRP, + multicast_port=DEFAULT_MCAST_PORT, + secure_config=SecureConfig( + backbone_key="bbaacc44bbaacc44bbaacc44bbaacc44", + latency_ms=2000, + ), + local_ip="192.168.1.1", + threaded=True, + ), + ), ], ) async def test_init_connection_handling( From b65d8cc1b35202e350409adaf5ffab23812a0174 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 28 Nov 2022 00:23:09 +0000 Subject: [PATCH 0771/1033] [ci skip] Translation update --- .../components/airly/translations/sk.json | 3 +- .../components/airnow/translations/sk.json | 3 +- .../components/airthings/translations/sk.json | 1 + .../airvisual/translations/sensor.sk.json | 2 ++ .../components/airzone/translations/sk.json | 10 +++++-- .../aladdin_connect/translations/sk.json | 1 + .../alarmdecoder/translations/sk.json | 9 +++++- .../components/almond/translations/sk.json | 1 + .../amberelectric/translations/sk.json | 9 ++++++ .../ambiclimate/translations/sk.json | 3 ++ .../android_ip_webcam/translations/sk.json | 4 ++- .../components/androidtv/translations/sk.json | 23 ++++++++++++++ .../components/apple_tv/translations/sk.json | 23 ++++++++++++-- .../components/aranet/translations/sk.json | 6 +++- .../components/asuswrt/translations/sk.json | 3 +- .../components/august/translations/sk.json | 13 ++++++-- .../aurora_abb_powerone/translations/sk.json | 10 ++++++- .../components/auth/translations/sk.json | 3 +- .../components/awair/translations/sk.json | 12 ++++++++ .../azure_devops/translations/sk.json | 14 ++++++++- .../azure_event_hub/translations/sk.json | 9 +++++- .../binary_sensor/translations/sk.json | 6 ++++ .../components/blink/translations/sk.json | 3 ++ .../bluemaestro/translations/sk.json | 7 ++++- .../components/bluetooth/translations/sk.json | 14 ++++++++- .../components/braviatv/translations/sk.json | 14 +++++++++ .../components/bthome/translations/sk.json | 7 ++++- .../components/cloud/translations/sk.json | 7 +++++ .../components/cpuspeed/translations/sk.json | 9 +++++- .../crownstone/translations/sk.json | 1 + .../derivative/translations/sk.json | 9 ++++++ .../devolo_home_control/translations/sk.json | 2 ++ .../devolo_home_network/translations/sk.json | 9 ++++-- .../components/discord/translations/sk.json | 12 ++++++++ .../components/dlna_dmr/translations/sk.json | 13 ++++++++ .../components/dlna_dms/translations/sk.json | 3 ++ .../components/ecobee/translations/sk.json | 6 +++- .../components/elkm1/translations/sk.json | 6 ++-- .../components/elmax/translations/sk.json | 6 ++++ .../components/ezviz/translations/sk.json | 11 +++++-- .../components/fibaro/translations/sk.json | 3 +- .../flick_electric/translations/sk.json | 1 + .../components/flume/translations/sk.json | 4 ++- .../components/flux_led/translations/sk.json | 3 ++ .../forecast_solar/translations/sk.json | 3 ++ .../forecast_solar/translations/zh-Hant.json | 3 ++ .../forked_daapd/translations/sk.json | 4 ++- .../components/fritzbox/translations/sk.json | 3 +- .../garages_amsterdam/translations/sk.json | 7 +++++ .../components/generic/translations/sk.json | 11 ++++++- .../geocaching/translations/sk.json | 1 + .../components/goalzero/translations/sk.json | 3 +- .../components/gogogate2/translations/sk.json | 3 +- .../components/google/translations/sk.json | 18 ++++++++++- .../google_sheets/translations/sk.json | 19 ++++++++++++ .../google_travel_time/translations/sk.json | 11 +++++++ .../components/govee_ble/translations/sk.json | 7 ++++- .../components/gree/translations/sk.json | 5 ++++ .../components/group/translations/sk.json | 30 +++++++++++++++---- .../growatt_server/translations/sk.json | 8 ++++- .../components/habitica/translations/sk.json | 3 +- .../components/hassio/translations/sk.json | 15 +++++++++- .../here_travel_time/translations/sk.json | 10 +++++++ .../home_connect/translations/sk.json | 3 ++ .../homeassistant/translations/sk.json | 13 ++++++++ .../homeassistant_alerts/translations/sk.json | 8 +++++ .../components/homekit/translations/sk.json | 9 ++++++ .../homematicip_cloud/translations/sk.json | 12 ++++++-- .../homewizard/translations/sk.json | 3 +- .../huawei_lte/translations/sk.json | 7 +++-- .../components/hue/translations/sk.json | 1 + .../humidifier/translations/sk.json | 9 +++++- .../components/hyperion/translations/sk.json | 13 ++++++++ .../components/iaqualink/translations/sk.json | 3 +- .../components/inkbird/translations/sk.json | 7 ++++- .../intellifire/translations/sk.json | 3 +- .../components/ipma/translations/sk.json | 1 + .../components/ipp/translations/sk.json | 7 ++++- .../components/iqvia/translations/sk.json | 10 +++++++ .../components/isy994/translations/sk.json | 9 +++++- .../components/jellyfin/translations/sk.json | 3 +- .../components/juicenet/translations/sk.json | 8 +++++ .../justnimbus/translations/sk.json | 7 +++++ .../keenetic_ndms2/translations/sk.json | 3 +- .../components/kegtron/translations/sk.json | 7 ++++- .../components/knx/translations/en.json | 14 +++++++++ .../components/konnected/translations/sk.json | 10 +++++++ .../components/kraken/translations/sk.json | 5 ++++ .../components/lametric/translations/sk.json | 2 ++ .../components/lidarr/translations/sk.json | 25 +++++++++++++++- .../litterrobot/translations/sk.json | 3 +- .../components/local_ip/translations/sk.json | 4 +++ .../components/locative/translations/sk.json | 9 ++++++ .../logi_circle/translations/sk.json | 1 + .../components/lyric/translations/sk.json | 1 + .../components/meater/translations/sk.json | 3 ++ .../components/mikrotik/translations/sk.json | 12 +++++++- .../components/min_max/translations/sk.json | 20 +++++++++++++ .../components/moat/translations/sk.json | 7 ++++- .../modem_callerid/translations/sk.json | 3 +- .../components/motioneye/translations/sk.json | 11 ++++++- .../components/mqtt/translations/sk.json | 30 +++++++++++++++++-- .../components/myq/translations/sk.json | 3 +- .../components/mysensors/translations/sk.json | 2 ++ .../components/nam/translations/sk.json | 3 +- .../components/nanoleaf/translations/sk.json | 1 + .../components/neato/translations/sk.json | 5 ++++ .../components/nest/translations/sk.json | 29 +++++++++++++++++- .../components/netatmo/translations/sk.json | 2 ++ .../components/nextdns/translations/sk.json | 17 +++++++++++ .../nibe_heatpump/translations/sk.json | 5 ++++ .../nightscout/translations/sk.json | 3 +- .../components/nobo_hub/translations/sk.json | 3 +- .../components/notion/translations/sk.json | 3 +- .../components/nuheat/translations/sk.json | 3 +- .../components/octoprint/translations/sk.json | 4 ++- .../ondilo_ico/translations/sk.json | 3 ++ .../components/onewire/translations/sk.json | 3 +- .../openexchangerates/translations/sk.json | 10 +++++++ .../opentherm_gw/translations/sk.json | 2 ++ .../components/openuv/translations/sk.json | 6 ++++ .../openweathermap/translations/sk.json | 3 +- .../components/oralb/translations/sk.json | 7 ++++- .../components/overkiz/translations/sk.json | 4 ++- .../p1_monitor/translations/sk.json | 3 ++ .../panasonic_viera/translations/sk.json | 9 ++++++ .../components/plaato/translations/sk.json | 12 +++++++- .../components/plex/translations/sk.json | 5 ++++ .../components/point/translations/sk.json | 12 +++++++- .../components/profiler/translations/sk.json | 9 ++++++ .../components/prusalink/translations/sk.json | 4 +++ .../components/ps4/translations/sk.json | 2 ++ .../pure_energie/translations/sk.json | 3 ++ .../components/pushover/translations/sk.json | 16 ++++++++++ .../components/pvoutput/translations/sk.json | 3 +- .../components/qingping/translations/sk.json | 7 ++++- .../components/qnap_qsw/translations/sk.json | 9 ++++-- .../components/radarr/translations/ca.json | 4 +++ .../components/radarr/translations/sk.json | 12 ++++++++ .../rainmachine/translations/sk.json | 1 + .../components/renault/translations/sk.json | 3 +- .../components/ridwell/translations/sk.json | 13 +++++--- .../components/risco/translations/sk.json | 6 ++-- .../components/roomba/translations/sk.json | 3 ++ .../components/roon/translations/sk.json | 3 +- .../rtsp_to_webrtc/translations/sk.json | 7 +++++ .../components/sabnzbd/translations/sk.json | 3 ++ .../components/samsungtv/translations/sk.json | 10 ++++++- .../components/sense/translations/sk.json | 9 +++++- .../components/sensibo/translations/sk.json | 12 ++++++++ .../components/sensor/translations/sk.json | 7 +++++ .../components/sentry/translations/sk.json | 7 +++++ .../components/sia/translations/sk.json | 9 ++++++ .../components/skybell/translations/sk.json | 4 +++ .../components/slack/translations/sk.json | 1 + .../components/smappee/translations/sk.json | 3 +- .../smartthings/translations/sk.json | 3 ++ .../components/sms/translations/sk.json | 10 +++++-- .../components/snooz/translations/sk.json | 7 ++++- .../components/solaredge/translations/sk.json | 1 + .../components/soma/translations/sk.json | 3 +- .../components/somfy/translations/sk.json | 3 ++ .../somfy_mylink/translations/sk.json | 1 + .../components/sonarr/translations/sk.json | 12 +++++++- .../speedtestdotnet/translations/sk.json | 7 +++++ .../components/spotify/translations/sk.json | 3 ++ .../components/sql/translations/sk.json | 12 ++++++++ .../srp_energy/translations/sk.json | 2 ++ .../components/starline/translations/sk.json | 10 +++++++ .../steam_online/translations/sk.json | 13 ++++++++ .../components/subaru/translations/sk.json | 15 ++++++++++ .../components/sun/translations/sk.json | 7 +++++ .../switch_as_x/translations/sk.json | 11 +++++++ .../switch_as_x/translations/sv.json | 2 +- .../components/syncthing/translations/sk.json | 7 +++++ .../components/syncthru/translations/sk.json | 10 +++++-- .../system_bridge/translations/sk.json | 7 +++-- .../tankerkoenig/translations/sk.json | 9 ++++++ .../components/tautulli/translations/sk.json | 16 ++++++++++ .../tellduslive/translations/sk.json | 7 +++-- .../thermobeacon/translations/sk.json | 7 ++++- .../components/thermopro/translations/sk.json | 7 ++++- .../components/threshold/translations/sk.json | 20 +++++++++++++ .../components/tibber/translations/sk.json | 3 ++ .../components/tilt_ble/translations/sk.json | 7 ++++- .../components/tolo/translations/sk.json | 3 ++ .../tomorrowio/translations/sensor.sk.json | 6 ++++ .../tomorrowio/translations/sk.json | 4 ++- .../components/toon/translations/sk.json | 5 +++- .../trafikverket_ferry/translations/sk.json | 10 +++++++ .../trafikverket_train/translations/sk.json | 9 ++++++ .../transmission/translations/sk.json | 3 +- .../tuya/translations/select.sk.json | 6 ++++ .../components/tuya/translations/sk.json | 3 +- .../components/twilio/translations/sk.json | 9 ++++++ .../unifiprotect/translations/sk.json | 16 ++++++++-- .../components/uptime/translations/sk.json | 9 ++++++ .../uptimerobot/translations/sk.json | 3 +- .../components/venstar/translations/sk.json | 3 +- .../components/vera/translations/sk.json | 17 +++++++++++ .../components/vicare/translations/sk.json | 3 +- .../components/vizio/translations/sk.json | 5 ++++ .../volvooncall/translations/sk.json | 3 ++ .../components/vulcan/translations/sk.json | 14 +++++++++ .../components/watttime/translations/sk.json | 6 ++-- .../components/whois/translations/sk.json | 3 ++ .../components/withings/translations/sk.json | 3 +- .../wled/translations/select.sk.json | 2 ++ .../wolflink/translations/sensor.sk.json | 3 +- .../components/xbox/translations/sk.json | 3 ++ .../xiaomi_aqara/translations/sk.json | 10 ++++++- .../xiaomi_ble/translations/sk.json | 12 ++++++++ .../xiaomi_miio/translations/sk.json | 6 ++++ .../yale_smart_alarm/translations/sk.json | 7 +++++ .../yalexs_ble/translations/sk.json | 7 +++++ .../yamaha_musiccast/translations/sk.json | 6 +++- .../components/yolink/translations/sk.json | 1 + .../components/zerproc/translations/sk.json | 5 ++++ .../components/zha/translations/sk.json | 3 ++ .../zoneminder/translations/sk.json | 1 + .../components/zwave_js/translations/sk.json | 29 +++++++++++++++++- .../components/zwave_me/translations/sk.json | 8 +++++ 222 files changed, 1458 insertions(+), 130 deletions(-) create mode 100644 homeassistant/components/cloud/translations/sk.json create mode 100644 homeassistant/components/homeassistant_alerts/translations/sk.json create mode 100644 homeassistant/components/homekit/translations/sk.json create mode 100644 homeassistant/components/locative/translations/sk.json create mode 100644 homeassistant/components/min_max/translations/sk.json create mode 100644 homeassistant/components/profiler/translations/sk.json create mode 100644 homeassistant/components/rtsp_to_webrtc/translations/sk.json create mode 100644 homeassistant/components/sentry/translations/sk.json create mode 100644 homeassistant/components/switch_as_x/translations/sk.json create mode 100644 homeassistant/components/threshold/translations/sk.json create mode 100644 homeassistant/components/twilio/translations/sk.json create mode 100644 homeassistant/components/uptime/translations/sk.json create mode 100644 homeassistant/components/vera/translations/sk.json diff --git a/homeassistant/components/airly/translations/sk.json b/homeassistant/components/airly/translations/sk.json index ccea62b47aa..1ff57355d56 100644 --- a/homeassistant/components/airly/translations/sk.json +++ b/homeassistant/components/airly/translations/sk.json @@ -14,7 +14,8 @@ "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka", "name": "N\u00e1zov" - } + }, + "description": "Ak chcete vygenerova\u0165 k\u013e\u00fa\u010d API, prejdite na https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airnow/translations/sk.json b/homeassistant/components/airnow/translations/sk.json index 7de74db3936..2cb10dea95c 100644 --- a/homeassistant/components/airnow/translations/sk.json +++ b/homeassistant/components/airnow/translations/sk.json @@ -15,7 +15,8 @@ "api_key": "API k\u013e\u00fa\u010d", "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka" - } + }, + "description": "Ak chcete vygenerova\u0165 k\u013e\u00fa\u010d API, prejdite na https://docs.airnowapi.org/account/request/" } } } diff --git a/homeassistant/components/airthings/translations/sk.json b/homeassistant/components/airthings/translations/sk.json index 45a1da112b7..cc0b6f42502 100644 --- a/homeassistant/components/airthings/translations/sk.json +++ b/homeassistant/components/airthings/translations/sk.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "description": "Ak chcete n\u00e1js\u0165 svoje poverenia, prihl\u00e1ste sa na adrese {url}", "id": "ID", "secret": "Tajn\u00e9" } diff --git a/homeassistant/components/airvisual/translations/sensor.sk.json b/homeassistant/components/airvisual/translations/sensor.sk.json index 2af833ce1f2..b550f1e6629 100644 --- a/homeassistant/components/airvisual/translations/sensor.sk.json +++ b/homeassistant/components/airvisual/translations/sensor.sk.json @@ -1,6 +1,8 @@ { "state": { "airvisual__pollutant_label": { + "co": "Oxid uho\u013enat\u00fd", + "n2": "Oxid dusi\u010dit\u00fd", "o3": "Oz\u00f3n", "p1": "PM10", "p2": "PM2,5", diff --git a/homeassistant/components/airzone/translations/sk.json b/homeassistant/components/airzone/translations/sk.json index bf8ee141cec..b256742f48f 100644 --- a/homeassistant/components/airzone/translations/sk.json +++ b/homeassistant/components/airzone/translations/sk.json @@ -4,19 +4,23 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_system_id": "Neplatn\u00e9 ID syst\u00e9mu Airzone" }, "step": { "discovered_connection": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "id": "ID syst\u00e9mu" } }, "user": { "data": { "host": "Hostite\u013e", + "id": "ID syst\u00e9mu", "port": "Port" - } + }, + "description": "Nastavte integr\u00e1ciu Airzone." } } } diff --git a/homeassistant/components/aladdin_connect/translations/sk.json b/homeassistant/components/aladdin_connect/translations/sk.json index bfab81adff8..030604ec1bc 100644 --- a/homeassistant/components/aladdin_connect/translations/sk.json +++ b/homeassistant/components/aladdin_connect/translations/sk.json @@ -13,6 +13,7 @@ "data": { "password": "Heslo" }, + "description": "Integr\u00e1cia Aladdin Connect potrebuje op\u00e4tovn\u00e9 overenie v\u00e1\u0161ho \u00fa\u010dtu", "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { diff --git a/homeassistant/components/alarmdecoder/translations/sk.json b/homeassistant/components/alarmdecoder/translations/sk.json index 999e4490764..46e52c8bcdb 100644 --- a/homeassistant/components/alarmdecoder/translations/sk.json +++ b/homeassistant/components/alarmdecoder/translations/sk.json @@ -13,6 +13,12 @@ "device_path": "Cesta k zariadeniu", "host": "Hostite\u013e", "port": "Port" + }, + "title": "Nakonfigurujte nastavenia pripojenia" + }, + "user": { + "data": { + "protocol": "Protokol" } } } @@ -26,7 +32,8 @@ "init": { "data": { "edit_select": "Upravi\u0165" - } + }, + "description": "\u010co by ste chceli upravi\u0165?" }, "zone_details": { "data": { diff --git a/homeassistant/components/almond/translations/sk.json b/homeassistant/components/almond/translations/sk.json index f5f8cd88968..a2709de3aac 100644 --- a/homeassistant/components/almond/translations/sk.json +++ b/homeassistant/components/almond/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "step": { diff --git a/homeassistant/components/amberelectric/translations/sk.json b/homeassistant/components/amberelectric/translations/sk.json index 3f4b526a190..9935c530ebc 100644 --- a/homeassistant/components/amberelectric/translations/sk.json +++ b/homeassistant/components/amberelectric/translations/sk.json @@ -4,6 +4,15 @@ "invalid_api_token": "Neplatn\u00fd API k\u013e\u00fa\u010d", "no_site": "Nebola poskytnut\u00e1 \u017eiadna str\u00e1nka", "unknown_error": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "api_token": "API token", + "site_id": "ID lokality" + }, + "description": "Prejdite na {api_url} a vygenerujte k\u013e\u00fa\u010d API" + } } } } \ No newline at end of file diff --git a/homeassistant/components/ambiclimate/translations/sk.json b/homeassistant/components/ambiclimate/translations/sk.json index 77910e52422..d13178679a4 100644 --- a/homeassistant/components/ambiclimate/translations/sk.json +++ b/homeassistant/components/ambiclimate/translations/sk.json @@ -7,6 +7,9 @@ }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" + }, + "error": { + "no_token": "Neoveren\u00e9 pomocou Ambiclimate" } } } \ No newline at end of file diff --git a/homeassistant/components/android_ip_webcam/translations/sk.json b/homeassistant/components/android_ip_webcam/translations/sk.json index 6455e273fb1..820da08a7b6 100644 --- a/homeassistant/components/android_ip_webcam/translations/sk.json +++ b/homeassistant/components/android_ip_webcam/translations/sk.json @@ -11,7 +11,9 @@ "user": { "data": { "host": "Hostite\u013e", - "password": "Heslo" + "password": "Heslo", + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/androidtv/translations/sk.json b/homeassistant/components/androidtv/translations/sk.json index f281814b01a..ad94a3ed3bf 100644 --- a/homeassistant/components/androidtv/translations/sk.json +++ b/homeassistant/components/androidtv/translations/sk.json @@ -18,5 +18,28 @@ } } } + }, + "options": { + "step": { + "apps": { + "data": { + "app_delete": "Za\u010diarknut\u00edm tejto mo\u017enosti odstr\u00e1nite t\u00fato aplik\u00e1ciu", + "app_id": "ID aplik\u00e1cie", + "app_name": "N\u00e1zov aplik\u00e1cie" + }, + "description": "Konfigurova\u0165 ID aplik\u00e1cie {app_id}", + "title": "Konfigur\u00e1cia aplik\u00e1ci\u00ed Android TV" + }, + "init": { + "data": { + "apps": "Konfigur\u00e1cia zoznamu aplik\u00e1ci\u00ed" + } + }, + "rules": { + "data": { + "rule_values": "Zoznam pravidiel zis\u0165ovania stavu (pozri dokument\u00e1ciu)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/sk.json b/homeassistant/components/apple_tv/translations/sk.json index f3bf36ec308..883dba9bd6d 100644 --- a/homeassistant/components/apple_tv/translations/sk.json +++ b/homeassistant/components/apple_tv/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "ipv6_not_supported": "IPv6 nie je podporovan\u00e9", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", "setup_failed": "Zariadenie sa nepodarilo nastavi\u0165.", @@ -16,13 +17,27 @@ }, "flow_title": "{name} ({type})", "step": { + "confirm": { + "title": "Potvr\u010fte pridanie Apple TV" + }, "pair_no_pin": { - "description": "Pre slu\u017ebu `{protocol}` sa vy\u017eaduje p\u00e1rovanie. Ak chcete pokra\u010dova\u0165, zadajte na svojom zariaden\u00ed k\u00f3d PIN {pin}." + "description": "Pre slu\u017ebu `{protocol}` sa vy\u017eaduje p\u00e1rovanie. Ak chcete pokra\u010dova\u0165, zadajte na svojom zariaden\u00ed k\u00f3d PIN {pin}.", + "title": "P\u00e1rovanie" + }, + "pair_with_pin": { + "data": { + "pin": "PIN k\u00f3d" + }, + "title": "P\u00e1rovanie" }, "password": { "title": "Vy\u017eaduje sa heslo" }, + "protocol_disabled": { + "title": "P\u00e1rovanie nie je mo\u017en\u00e9" + }, "reconfigure": { + "description": "Prekonfigurujte toto zariadenie, aby ste obnovili jeho funk\u010dnos\u0165.", "title": "Rekonfigur\u00e1cia zariadenia" }, "service_problem": { @@ -32,7 +47,8 @@ "user": { "data": { "device_input": "Zariadenie" - } + }, + "title": "Nastavte nov\u00fa Apple TV" } } }, @@ -41,7 +57,8 @@ "init": { "data": { "start_off": "Nezap\u00ednajte zariadenie pri sp\u00fa\u0161\u0165an\u00ed Home Assistant" - } + }, + "description": "Nakonfigurujte v\u0161eobecn\u00e9 nastavenia zariadenia" } } } diff --git a/homeassistant/components/aranet/translations/sk.json b/homeassistant/components/aranet/translations/sk.json index 6879ec0155c..a0fed806308 100644 --- a/homeassistant/components/aranet/translations/sk.json +++ b/homeassistant/components/aranet/translations/sk.json @@ -8,10 +8,14 @@ }, "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/asuswrt/translations/sk.json b/homeassistant/components/asuswrt/translations/sk.json index e92a75763f2..2adc19c0c1a 100644 --- a/homeassistant/components/asuswrt/translations/sk.json +++ b/homeassistant/components/asuswrt/translations/sk.json @@ -12,7 +12,8 @@ "host": "Hostite\u013e", "name": "N\u00e1zov", "password": "Heslo", - "port": "Port" + "port": "Port", + "protocol": "Komunika\u010dn\u00fd protokol, ktor\u00fd sa m\u00e1 pou\u017ei\u0165" } } } diff --git a/homeassistant/components/august/translations/sk.json b/homeassistant/components/august/translations/sk.json index 93a534f8fdd..ef420973986 100644 --- a/homeassistant/components/august/translations/sk.json +++ b/homeassistant/components/august/translations/sk.json @@ -13,12 +13,21 @@ "reauth_validate": { "data": { "password": "Heslo" - } + }, + "description": "Zadajte heslo pre {username}." }, "user_validate": { "data": { - "password": "Heslo" + "login_method": "Sp\u00f4sob prihl\u00e1senia", + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } + }, + "validation": { + "data": { + "code": "Overovac\u00ed k\u00f3d" + }, + "title": "Dvojfaktorov\u00e1 autentifik\u00e1cia" } } } diff --git a/homeassistant/components/aurora_abb_powerone/translations/sk.json b/homeassistant/components/aurora_abb_powerone/translations/sk.json index 793f8eff278..0eabe52686c 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/sk.json +++ b/homeassistant/components/aurora_abb_powerone/translations/sk.json @@ -1,7 +1,15 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_serial_ports": "Nena\u0161li sa \u017eiadne komunika\u010dn\u00e9 porty. Na komunik\u00e1ciu potrebujete platn\u00e9 zariadenie RS485." + }, + "step": { + "user": { + "data": { + "address": "Adresa meni\u010da" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/auth/translations/sk.json b/homeassistant/components/auth/translations/sk.json index 1eccd378944..9634beff2cc 100644 --- a/homeassistant/components/auth/translations/sk.json +++ b/homeassistant/components/auth/translations/sk.json @@ -14,7 +14,8 @@ "setup": { "description": "Jednorazov\u00e9 heslo bolo odoslan\u00e9 prostredn\u00edctvom **notify.{notify_service}**. Zadajte ho, pros\u00edm, ni\u017e\u0161ie:" } - } + }, + "title": "Ozn\u00e1menie jednorazov\u00e9ho hesla" } } } \ No newline at end of file diff --git a/homeassistant/components/awair/translations/sk.json b/homeassistant/components/awair/translations/sk.json index 1ccc5d4eff0..b208312589f 100644 --- a/homeassistant/components/awair/translations/sk.json +++ b/homeassistant/components/awair/translations/sk.json @@ -14,6 +14,12 @@ }, "flow_title": "{model} ({device_id})", "step": { + "cloud": { + "data": { + "access_token": "Pr\u00edstupov\u00fd token", + "email": "Email" + } + }, "discovery_confirm": { "description": "Chcete nastavi\u0165 {model} ({device_id})?" }, @@ -22,6 +28,12 @@ "device": "Zaradenie", "host": "IP adresa" } + }, + "reauth_confirm": { + "data": { + "access_token": "Pr\u00edstupov\u00fd token", + "email": "Email" + } } } } diff --git a/homeassistant/components/azure_devops/translations/sk.json b/homeassistant/components/azure_devops/translations/sk.json index 6fe0cf7d89a..bbfab69b01c 100644 --- a/homeassistant/components/azure_devops/translations/sk.json +++ b/homeassistant/components/azure_devops/translations/sk.json @@ -9,9 +9,21 @@ "invalid_auth": "Neplatn\u00e9 overenie", "project_error": "Nepodarilo sa z\u00edska\u0165 inform\u00e1cie o projekte." }, + "flow_title": "{project_url}", "step": { "reauth": { - "description": "Overenie toto\u017enosti pre {project_url} zlyhalo. Zadajte svoje aktu\u00e1lne poverenia." + "data": { + "personal_access_token": "Osobn\u00fd pr\u00edstupov\u00fd token (PAT)" + }, + "description": "Overenie toto\u017enosti pre {project_url} zlyhalo. Zadajte svoje aktu\u00e1lne poverenia.", + "title": "Op\u00e4tovn\u00e9 overenie" + }, + "user": { + "data": { + "organization": "Organiz\u00e1cia", + "personal_access_token": "Osobn\u00fd pr\u00edstupov\u00fd token (PAT)", + "project": "Projekt" + } } } } diff --git a/homeassistant/components/azure_event_hub/translations/sk.json b/homeassistant/components/azure_event_hub/translations/sk.json index 17ae6622292..2f499266544 100644 --- a/homeassistant/components/azure_event_hub/translations/sk.json +++ b/homeassistant/components/azure_event_hub/translations/sk.json @@ -1,11 +1,18 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "conn_string": { + "description": "Zadajte re\u0165azec pripojenia pre: {event_hub_instance_name}", + "title": "Met\u00f3da re\u0165azca pripojenia" + } } } } \ No newline at end of file diff --git a/homeassistant/components/binary_sensor/translations/sk.json b/homeassistant/components/binary_sensor/translations/sk.json index bc4b05bdb73..92d0262da88 100644 --- a/homeassistant/components/binary_sensor/translations/sk.json +++ b/homeassistant/components/binary_sensor/translations/sk.json @@ -2,8 +2,10 @@ "device_automation": { "condition_type": { "is_bat_low": "{entity_name} bat\u00e9ria je vybit\u00e1", + "is_co": "{entity_name} zis\u0165uje oxid uho\u013enat\u00fd", "is_motion": "{entity_name} zaznamen\u00e1va pohyb", "is_moving": "{entity_name} sa h\u00fdbe", + "is_no_co": "{entity_name} nezaznamen\u00e1va oxid uho\u013enat\u00fd", "is_no_gas": "{entity_name} nedetekuje plyn", "is_no_light": "{entity_name} nezaznamen\u00e1va svetlo", "is_no_motion": "{entity_name} nezaznamen\u00e1va pohyb", @@ -52,6 +54,10 @@ "off": "Nenab\u00edja sa", "on": "Nab\u00edja sa" }, + "carbon_monoxide": { + "off": "\u017diadny plyn", + "on": "Zachyten\u00fd plyn" + }, "cold": { "off": "Norm\u00e1lny", "on": "Studen\u00fd" diff --git a/homeassistant/components/blink/translations/sk.json b/homeassistant/components/blink/translations/sk.json index 3a039c48717..0eb24922bb2 100644 --- a/homeassistant/components/blink/translations/sk.json +++ b/homeassistant/components/blink/translations/sk.json @@ -10,6 +10,9 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "2fa": { + "description": "Zadajte PIN zaslan\u00fd na v\u00e1\u0161 e-mail" + }, "user": { "data": { "password": "Heslo" diff --git a/homeassistant/components/bluemaestro/translations/sk.json b/homeassistant/components/bluemaestro/translations/sk.json index 48a6116c8ee..8273d877c92 100644 --- a/homeassistant/components/bluemaestro/translations/sk.json +++ b/homeassistant/components/bluemaestro/translations/sk.json @@ -6,11 +6,16 @@ "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "not_supported": "Zariadenie nie je podporovan\u00e9" }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/bluetooth/translations/sk.json b/homeassistant/components/bluetooth/translations/sk.json index df959bc0275..bbb745622ac 100644 --- a/homeassistant/components/bluetooth/translations/sk.json +++ b/homeassistant/components/bluetooth/translations/sk.json @@ -1,13 +1,20 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "no_adapters": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 adapt\u00e9ry Bluetooth" }, "flow_title": "{name}", "step": { "bluetooth_confirm": { "description": "Chcete nastavi\u0165 {name}?" }, + "multiple_adapters": { + "data": { + "adapter": "Adapt\u00e9r" + }, + "description": "Vyberte adapt\u00e9r Bluetooth, ktor\u00fd chcete nastavi\u0165" + }, "single_adapter": { "description": "Chcete nastavi\u0165 adapt\u00e9r Bluetooth {name}?" }, @@ -18,5 +25,10 @@ "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } + }, + "issues": { + "haos_outdated": { + "title": "Aktualizujte na opera\u010dn\u00fd syst\u00e9m Home Assistant 9.0 alebo nov\u0161\u00ed" + } } } \ No newline at end of file diff --git a/homeassistant/components/braviatv/translations/sk.json b/homeassistant/components/braviatv/translations/sk.json index 70375f14897..5add13a86b3 100644 --- a/homeassistant/components/braviatv/translations/sk.json +++ b/homeassistant/components/braviatv/translations/sk.json @@ -4,9 +4,23 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "invalid_auth": "Neplatn\u00e9 overenie", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, "step": { + "authorize": { + "data": { + "pin": "PIN k\u00f3d" + } + }, + "confirm": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + }, + "reauth_confirm": { + "data": { + "pin": "PIN k\u00f3d" + } + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/bthome/translations/sk.json b/homeassistant/components/bthome/translations/sk.json index 767536da844..0d4e7c2f929 100644 --- a/homeassistant/components/bthome/translations/sk.json +++ b/homeassistant/components/bthome/translations/sk.json @@ -8,11 +8,16 @@ "error": { "expected_32_characters": "O\u010dak\u00e1van\u00fd 32-znakov\u00fd hexadecim\u00e1lny bindkey." }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/cloud/translations/sk.json b/homeassistant/components/cloud/translations/sk.json new file mode 100644 index 00000000000..8083824df50 --- /dev/null +++ b/homeassistant/components/cloud/translations/sk.json @@ -0,0 +1,7 @@ +{ + "system_health": { + "info": { + "google_enabled": "Google povolen\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cpuspeed/translations/sk.json b/homeassistant/components/cpuspeed/translations/sk.json index 89f5b8fb3b9..59f10788754 100644 --- a/homeassistant/components/cpuspeed/translations/sk.json +++ b/homeassistant/components/cpuspeed/translations/sk.json @@ -2,6 +2,13 @@ "config": { "abort": { "already_configured": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "user": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?", + "title": "R\u00fdchlos\u0165 CPU" + } } - } + }, + "title": "R\u00fdchlos\u0165 CPU" } \ No newline at end of file diff --git a/homeassistant/components/crownstone/translations/sk.json b/homeassistant/components/crownstone/translations/sk.json index 895bdde28cd..60e6ce58a97 100644 --- a/homeassistant/components/crownstone/translations/sk.json +++ b/homeassistant/components/crownstone/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, "error": { + "account_not_verified": "\u00da\u010det nie je overen\u00fd. Aktivujte si svoj \u00fa\u010det prostredn\u00edctvom aktiva\u010dn\u00e9ho e-mailu od Crownstone.", "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, diff --git a/homeassistant/components/derivative/translations/sk.json b/homeassistant/components/derivative/translations/sk.json index ac28b231df7..7fae67e64a0 100644 --- a/homeassistant/components/derivative/translations/sk.json +++ b/homeassistant/components/derivative/translations/sk.json @@ -8,5 +8,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data_description": { + "unit_prefix": "." + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/devolo_home_control/translations/sk.json b/homeassistant/components/devolo_home_control/translations/sk.json index 04647f3c4fd..bebb477ff50 100644 --- a/homeassistant/components/devolo_home_control/translations/sk.json +++ b/homeassistant/components/devolo_home_control/translations/sk.json @@ -10,12 +10,14 @@ "step": { "user": { "data": { + "mydevolo_url": "mydevolo URL", "password": "Heslo", "username": "Email / devolo ID" } }, "zeroconf_confirm": { "data": { + "mydevolo_url": "mydevolo URL", "password": "Heslo", "username": "Email / devolo ID" } diff --git a/homeassistant/components/devolo_home_network/translations/sk.json b/homeassistant/components/devolo_home_network/translations/sk.json index ff279f8d0e4..22afad80c00 100644 --- a/homeassistant/components/devolo_home_network/translations/sk.json +++ b/homeassistant/components/devolo_home_network/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", @@ -17,7 +18,11 @@ "user": { "data": { "ip_address": "IP adresa" - } + }, + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + }, + "zeroconf_confirm": { + "title": "Objaven\u00e9 zariadenie dom\u00e1cej siete devolo" } } } diff --git a/homeassistant/components/discord/translations/sk.json b/homeassistant/components/discord/translations/sk.json index f6e3414b2b0..71bc79a1ad6 100644 --- a/homeassistant/components/discord/translations/sk.json +++ b/homeassistant/components/discord/translations/sk.json @@ -7,6 +7,18 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "API token" + } + }, + "user": { + "data": { + "api_token": "API token" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/dlna_dmr/translations/sk.json b/homeassistant/components/dlna_dmr/translations/sk.json index ea6244cb478..38e6f0d765e 100644 --- a/homeassistant/components/dlna_dmr/translations/sk.json +++ b/homeassistant/components/dlna_dmr/translations/sk.json @@ -11,11 +11,24 @@ }, "flow_title": "{name}", "step": { + "confirm": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + }, + "manual": { + "data": { + "url": "URL" + } + }, "user": { "data": { "host": "Hostite\u013e" } } } + }, + "options": { + "error": { + "invalid_url": "Neplatn\u00e1 adresa URL" + } } } \ No newline at end of file diff --git a/homeassistant/components/dlna_dms/translations/sk.json b/homeassistant/components/dlna_dms/translations/sk.json index 64d81e7c78b..537609a4407 100644 --- a/homeassistant/components/dlna_dms/translations/sk.json +++ b/homeassistant/components/dlna_dms/translations/sk.json @@ -7,6 +7,9 @@ }, "flow_title": "{name}", "step": { + "confirm": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/ecobee/translations/sk.json b/homeassistant/components/ecobee/translations/sk.json index 9d5ee388dc3..b44cc667b4e 100644 --- a/homeassistant/components/ecobee/translations/sk.json +++ b/homeassistant/components/ecobee/translations/sk.json @@ -1,10 +1,14 @@ { "config": { + "error": { + "token_request_failed": "Chyba pri vy\u017eiadan\u00ed tokenov od ecobee; pros\u00edm sk\u00faste znova." + }, "step": { "user": { "data": { "api_key": "API k\u013e\u00fa\u010d" - } + }, + "title": "ecobee API k\u013e\u00fa\u010d" } } } diff --git a/homeassistant/components/elkm1/translations/sk.json b/homeassistant/components/elkm1/translations/sk.json index 08b703bf56b..597f97ac51f 100644 --- a/homeassistant/components/elkm1/translations/sk.json +++ b/homeassistant/components/elkm1/translations/sk.json @@ -15,14 +15,16 @@ "step": { "discovered_connection": { "data": { - "password": "Heslo" + "password": "Heslo", + "protocol": "Protokol" }, "description": "Pripojte sa k objaven\u00e9mu syst\u00e9mu: {mac_address} ({host})" }, "manual_connection": { "data": { "address": "IP adresa alebo dom\u00e9na alebo s\u00e9riov\u00fd port, ak sa prip\u00e1jate cez s\u00e9riov\u00fd port.", - "password": "Heslo" + "password": "Heslo", + "protocol": "Protokol" } }, "user": { diff --git a/homeassistant/components/elmax/translations/sk.json b/homeassistant/components/elmax/translations/sk.json index 80f2ebf02a3..150d8897ff3 100644 --- a/homeassistant/components/elmax/translations/sk.json +++ b/homeassistant/components/elmax/translations/sk.json @@ -10,6 +10,12 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "panels": { + "data": { + "panel_id": "ID panela", + "panel_pin": "PIN k\u00f3d" + } + }, "user": { "data": { "password": "Heslo" diff --git a/homeassistant/components/ezviz/translations/sk.json b/homeassistant/components/ezviz/translations/sk.json index 75a6a00160d..6fb1d443e0c 100644 --- a/homeassistant/components/ezviz/translations/sk.json +++ b/homeassistant/components/ezviz/translations/sk.json @@ -17,13 +17,18 @@ }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "url": "URL" } }, "user_custom_url": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "url": "URL", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "description": "Ru\u010dne zadajte adresu URL v\u00e1\u0161ho regi\u00f3nu", + "title": "Pripojenie k vlastnej adrese URL EZVIZ" } } } diff --git a/homeassistant/components/fibaro/translations/sk.json b/homeassistant/components/fibaro/translations/sk.json index 03982dd960c..42d7661d8ea 100644 --- a/homeassistant/components/fibaro/translations/sk.json +++ b/homeassistant/components/fibaro/translations/sk.json @@ -12,7 +12,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Aktualizujte svoje heslo pre {username}" }, "user": { "data": { diff --git a/homeassistant/components/flick_electric/translations/sk.json b/homeassistant/components/flick_electric/translations/sk.json index 2139002b30d..161864eb4b8 100644 --- a/homeassistant/components/flick_electric/translations/sk.json +++ b/homeassistant/components/flick_electric/translations/sk.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "client_id": "ID klienta (volite\u013en\u00e9)", "password": "Heslo" } } diff --git a/homeassistant/components/flume/translations/sk.json b/homeassistant/components/flume/translations/sk.json index 7ec2e77007d..13f019fb4da 100644 --- a/homeassistant/components/flume/translations/sk.json +++ b/homeassistant/components/flume/translations/sk.json @@ -13,10 +13,12 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Heslo pre {username} u\u017e nie je platn\u00e9." }, "user": { "data": { + "client_id": "ID klienta", "password": "Heslo" } } diff --git a/homeassistant/components/flux_led/translations/sk.json b/homeassistant/components/flux_led/translations/sk.json index 23d4e4927f4..8ff55f4dbbc 100644 --- a/homeassistant/components/flux_led/translations/sk.json +++ b/homeassistant/components/flux_led/translations/sk.json @@ -7,6 +7,9 @@ }, "flow_title": "{model} {id} ({ipaddr})", "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 {model} {id} ({ipaddr})?" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/forecast_solar/translations/sk.json b/homeassistant/components/forecast_solar/translations/sk.json index 939157fb837..ff539ab78db 100644 --- a/homeassistant/components/forecast_solar/translations/sk.json +++ b/homeassistant/components/forecast_solar/translations/sk.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/zh-Hant.json b/homeassistant/components/forecast_solar/translations/zh-Hant.json index ede97887887..1d044729708 100644 --- a/homeassistant/components/forecast_solar/translations/zh-Hant.json +++ b/homeassistant/components/forecast_solar/translations/zh-Hant.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "API \u91d1\u9470\u7121\u6548" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forked_daapd/translations/sk.json b/homeassistant/components/forked_daapd/translations/sk.json index ed242c407e0..ed77a87879e 100644 --- a/homeassistant/components/forked_daapd/translations/sk.json +++ b/homeassistant/components/forked_daapd/translations/sk.json @@ -11,7 +11,9 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "password": "Heslo API (ak heslo nem\u00e1te, nechajte pr\u00e1zdne)", + "port": "API port" } } } diff --git a/homeassistant/components/fritzbox/translations/sk.json b/homeassistant/components/fritzbox/translations/sk.json index 54b2a464a3d..d1434894661 100644 --- a/homeassistant/components/fritzbox/translations/sk.json +++ b/homeassistant/components/fritzbox/translations/sk.json @@ -28,7 +28,8 @@ "data": { "host": "Hostite\u013e", "password": "Heslo" - } + }, + "description": "Zadajte inform\u00e1cie o va\u0161om AVM FRITZ!Box." } } } diff --git a/homeassistant/components/garages_amsterdam/translations/sk.json b/homeassistant/components/garages_amsterdam/translations/sk.json index 040687a74a4..30b5d246821 100644 --- a/homeassistant/components/garages_amsterdam/translations/sk.json +++ b/homeassistant/components/garages_amsterdam/translations/sk.json @@ -4,6 +4,13 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "cannot_connect": "Nepodarilo sa pripoji\u0165", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "garage_name": "N\u00e1zov gar\u00e1\u017ee" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/generic/translations/sk.json b/homeassistant/components/generic/translations/sk.json index 13913c503ee..1ea7cb6a2cc 100644 --- a/homeassistant/components/generic/translations/sk.json +++ b/homeassistant/components/generic/translations/sk.json @@ -3,6 +3,7 @@ "error": { "already_exists": "Kamera s t\u00fdmito nastaveniami URL u\u017e existuje.", "invalid_still_image": "Adresa URL nevr\u00e1tila platn\u00fd statick\u00fd obr\u00e1zok", + "malformed_url": "Chybne vytvoren\u00e1 adresa URL", "relative_url": "Relat\u00edvne adresy URL nie s\u00fa povolen\u00e9", "stream_no_route_to_host": "Pri pokuse o pripojenie k streamu sa nepodarilo n\u00e1js\u0165 hostite\u013ea", "timeout": "\u010casov\u00fd limit pri na\u010d\u00edtan\u00ed adresy URL", @@ -12,7 +13,11 @@ "user": { "data": { "password": "Heslo" - } + }, + "description": "Zadajte nastavenia na pripojenie ku kamere." + }, + "user_confirm_still": { + "description": "![Uk\u00e1\u017eka statick\u00e9ho obr\u00e1zka z fotoapar\u00e1tu]({preview_url})" } } }, @@ -20,11 +25,15 @@ "error": { "already_exists": "Kamera s t\u00fdmito nastaveniami URL u\u017e existuje.", "invalid_still_image": "Adresa URL nevr\u00e1tila platn\u00fd statick\u00fd obr\u00e1zok", + "malformed_url": "Chybne vytvoren\u00e1 adresa URL", "stream_no_route_to_host": "Pri pokuse o pripojenie k streamu sa nepodarilo n\u00e1js\u0165 hostite\u013ea", "timeout": "\u010casov\u00fd limit pri na\u010d\u00edtan\u00ed adresy URL", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "confirm_still": { + "description": "![Uk\u00e1\u017eka statick\u00e9ho obr\u00e1zka z fotoapar\u00e1tu]({preview_url})" + }, "init": { "data": { "password": "Heslo" diff --git a/homeassistant/components/geocaching/translations/sk.json b/homeassistant/components/geocaching/translations/sk.json index c3bed1eca3b..1f9fe121b6d 100644 --- a/homeassistant/components/geocaching/translations/sk.json +++ b/homeassistant/components/geocaching/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu." } } diff --git a/homeassistant/components/goalzero/translations/sk.json b/homeassistant/components/goalzero/translations/sk.json index 39b5e1560b2..dc9ffc47876 100644 --- a/homeassistant/components/goalzero/translations/sk.json +++ b/homeassistant/components/goalzero/translations/sk.json @@ -15,7 +15,8 @@ "data": { "host": "Hostite\u013e", "name": "N\u00e1zov" - } + }, + "description": "Pozrite si dokument\u00e1ciu, aby ste sa uistili, \u017ee s\u00fa splnen\u00e9 v\u0161etky po\u017eiadavky." } } } diff --git a/homeassistant/components/gogogate2/translations/sk.json b/homeassistant/components/gogogate2/translations/sk.json index f38a3717bd6..48d0d65da03 100644 --- a/homeassistant/components/gogogate2/translations/sk.json +++ b/homeassistant/components/gogogate2/translations/sk.json @@ -13,7 +13,8 @@ "data": { "ip_address": "IP adresa", "password": "Heslo" - } + }, + "description": "Ni\u017e\u0161ie uve\u010fte po\u017eadovan\u00e9 inform\u00e1cie." } } } diff --git a/homeassistant/components/google/translations/sk.json b/homeassistant/components/google/translations/sk.json index d0bcbcd88ff..ded98554d3f 100644 --- a/homeassistant/components/google/translations/sk.json +++ b/homeassistant/components/google/translations/sk.json @@ -5,7 +5,23 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", - "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu." + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu.", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + }, + "create_entry": { + "default": "\u00daspe\u0161ne overen\u00e9" + }, + "progress": { + "exchange": "Ak chcete prepoji\u0165 svoj \u00fa\u010det Google, nav\u0161t\u00edvte [{url}]({url}) a zadajte k\u00f3d: \n\n {user_code}" + }, + "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + }, + "reauth_confirm": { + "title": "Znova overi\u0165 integr\u00e1ciu" + } } }, "options": { diff --git a/homeassistant/components/google_sheets/translations/sk.json b/homeassistant/components/google_sheets/translations/sk.json index 7cba9218abc..1bbce0d3a0e 100644 --- a/homeassistant/components/google_sheets/translations/sk.json +++ b/homeassistant/components/google_sheets/translations/sk.json @@ -4,9 +4,28 @@ "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "create_spreadsheet_failure": "Chyba pri vytv\u00e1ran\u00ed tabu\u013eky, podrobnosti n\u00e1jdete v protokole ch\u00fdb", "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu.", + "open_spreadsheet_failure": "Chyba pri otv\u00e1ran\u00ed tabu\u013eky, podrobnosti n\u00e1jdete v protokole ch\u00fdb", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "create_entry": { + "default": "\u00daspe\u0161ne overen\u00e9 a tabu\u013eka vytvoren\u00e1 na adrese: {url}" + }, + "step": { + "auth": { + "title": "Prepoji\u0165 \u00fa\u010det Google" + }, + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + }, + "reauth_confirm": { + "description": "Integr\u00e1cia Tabuliek Google vy\u017eaduje op\u00e4tovn\u00e9 overenie v\u00e1\u0161ho \u00fa\u010dtu", + "title": "Znova overi\u0165 integr\u00e1ciu" + } } } } \ No newline at end of file diff --git a/homeassistant/components/google_travel_time/translations/sk.json b/homeassistant/components/google_travel_time/translations/sk.json index 4a0ddda2566..233c48e1b42 100644 --- a/homeassistant/components/google_travel_time/translations/sk.json +++ b/homeassistant/components/google_travel_time/translations/sk.json @@ -15,5 +15,16 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "language": "Jazyk", + "time": "\u010cas", + "units": "Jednotky" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/govee_ble/translations/sk.json b/homeassistant/components/govee_ble/translations/sk.json index a8fa63be59a..b121bbc35a3 100644 --- a/homeassistant/components/govee_ble/translations/sk.json +++ b/homeassistant/components/govee_ble/translations/sk.json @@ -5,11 +5,16 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/gree/translations/sk.json b/homeassistant/components/gree/translations/sk.json index 69d8d942494..8183bb46360 100644 --- a/homeassistant/components/gree/translations/sk.json +++ b/homeassistant/components/gree/translations/sk.json @@ -2,6 +2,11 @@ "config": { "abort": { "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "step": { + "confirm": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/group/translations/sk.json b/homeassistant/components/group/translations/sk.json index 8668e59847d..b03f41f896d 100644 --- a/homeassistant/components/group/translations/sk.json +++ b/homeassistant/components/group/translations/sk.json @@ -5,42 +5,49 @@ "data": { "all": "V\u0161etky entity", "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" } }, "cover": { "data": { "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" } }, "fan": { "data": { "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" } }, "light": { "data": { "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" } }, "lock": { "data": { "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" } }, "media_player": { "data": { "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov", "name": "Meno" } }, "switch": { "data": { "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" } } @@ -51,34 +58,45 @@ "binary_sensor": { "data": { "all": "V\u0161etky entity", - "entities": "\u010clenovia" + "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov" } }, "cover": { "data": { - "entities": "\u010clenovia" + "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov" } }, "fan": { "data": { - "entities": "\u010clenovia" + "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov" } }, "light": { "data": { "all": "V\u0161etky entity", - "entities": "\u010clenovia" + "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov" + } + }, + "lock": { + "data": { + "hide_members": "Skry\u0165 \u010dlenov" } }, "media_player": { "data": { - "entities": "\u010clenovia" + "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov" } }, "switch": { "data": { "all": "V\u0161etky entity", - "entities": "\u010clenovia" + "entities": "\u010clenovia", + "hide_members": "Skry\u0165 \u010dlenov" } } } diff --git a/homeassistant/components/growatt_server/translations/sk.json b/homeassistant/components/growatt_server/translations/sk.json index fc428c8f4b9..217ebf99151 100644 --- a/homeassistant/components/growatt_server/translations/sk.json +++ b/homeassistant/components/growatt_server/translations/sk.json @@ -4,10 +4,16 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "plant": { + "data": { + "plant_id": "Rastlina" + } + }, "user": { "data": { "name": "N\u00e1zov", - "password": "Heslo" + "password": "Heslo", + "url": "URL" } } } diff --git a/homeassistant/components/habitica/translations/sk.json b/homeassistant/components/habitica/translations/sk.json index fbfa0d6ad80..25aa532742a 100644 --- a/homeassistant/components/habitica/translations/sk.json +++ b/homeassistant/components/habitica/translations/sk.json @@ -8,7 +8,8 @@ "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", - "name": "Prep\u00edsa\u0165 pou\u017e\u00edvate\u013esk\u00e9 meno Habitica. Bude sa pou\u017e\u00edva\u0165 na servisn\u00e9 hovory" + "name": "Prep\u00edsa\u0165 pou\u017e\u00edvate\u013esk\u00e9 meno Habitica. Bude sa pou\u017e\u00edva\u0165 na servisn\u00e9 hovory", + "url": "URL" } } } diff --git a/homeassistant/components/hassio/translations/sk.json b/homeassistant/components/hassio/translations/sk.json index 6f36cf3bf8a..2203b162891 100644 --- a/homeassistant/components/hassio/translations/sk.json +++ b/homeassistant/components/hassio/translations/sk.json @@ -1,7 +1,20 @@ { + "issues": { + "unsupported_dns_server": { + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 probl\u00e9my so serverom DNS" + }, + "unsupported_docker_configuration": { + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 Docker je nespr\u00e1vne nakonfigurovan\u00fd" + }, + "unsupported_docker_version": { + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 verzia Docker" + } + }, "system_health": { "info": { - "host_os": "Hostite\u013esk\u00fd opera\u010dn\u00fd syst\u00e9m" + "docker_version": "Verzia Dockera", + "host_os": "Hostite\u013esk\u00fd opera\u010dn\u00fd syst\u00e9m", + "version_api": "Verzia API" } } } \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/sk.json b/homeassistant/components/here_travel_time/translations/sk.json index 2d1d7299de3..c22ebc23076 100644 --- a/homeassistant/components/here_travel_time/translations/sk.json +++ b/homeassistant/components/here_travel_time/translations/sk.json @@ -6,6 +6,16 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "destination_entity_id": { + "title": "Vyberte cie\u013e" + }, + "user": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/home_connect/translations/sk.json b/homeassistant/components/home_connect/translations/sk.json index c19b1a0b70c..61bfdf7ad53 100644 --- a/homeassistant/components/home_connect/translations/sk.json +++ b/homeassistant/components/home_connect/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})" + }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" } diff --git a/homeassistant/components/homeassistant/translations/sk.json b/homeassistant/components/homeassistant/translations/sk.json index d49f9f57329..59a5d7f7c50 100644 --- a/homeassistant/components/homeassistant/translations/sk.json +++ b/homeassistant/components/homeassistant/translations/sk.json @@ -3,5 +3,18 @@ "python_version": { "title": "Podpora pre Python {current_python_version} sa odstra\u0148uje" } + }, + "system_health": { + "info": { + "arch": "Architekt\u00fara CPU", + "docker": "Docker", + "installation_type": "Typ in\u0161tal\u00e1cie", + "os_name": "Rodina opera\u010dn\u00fdch syst\u00e9mov", + "os_version": "Verzia opera\u010dn\u00e9ho syst\u00e9mu", + "python_version": "Verzia Pythonu", + "timezone": "\u010casov\u00e9 p\u00e1smo", + "user": "Pou\u017e\u00edvate\u013e", + "version": "Verzia" + } } } \ No newline at end of file diff --git a/homeassistant/components/homeassistant_alerts/translations/sk.json b/homeassistant/components/homeassistant_alerts/translations/sk.json new file mode 100644 index 00000000000..33cafb8bba3 --- /dev/null +++ b/homeassistant/components/homeassistant_alerts/translations/sk.json @@ -0,0 +1,8 @@ +{ + "issues": { + "alert": { + "description": "{description}", + "title": "{title}" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit/translations/sk.json b/homeassistant/components/homekit/translations/sk.json new file mode 100644 index 00000000000..211d1141244 --- /dev/null +++ b/homeassistant/components/homekit/translations/sk.json @@ -0,0 +1,9 @@ +{ + "options": { + "step": { + "yaml": { + "description": "T\u00e1to polo\u017eka sa ovl\u00e1da pomocou YAML" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/translations/sk.json b/homeassistant/components/homematicip_cloud/translations/sk.json index 47aa296310a..209e87329e3 100644 --- a/homeassistant/components/homematicip_cloud/translations/sk.json +++ b/homeassistant/components/homematicip_cloud/translations/sk.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "connection_aborted": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "error": { + "press_the_button": "Stla\u010dte pros\u00edm modr\u00e9 tla\u010didlo.", + "register_failed": "Registr\u00e1cia zlyhala, sk\u00faste to znova." }, "step": { "init": { "data": { - "name": "N\u00e1zov (volite\u013en\u00e9, pou\u017e\u00edva sa ako predpona pre v\u0161etky zariadenia)" + "hapid": "ID pr\u00edstupov\u00e9ho bodu (SGTIN)", + "name": "N\u00e1zov (volite\u013en\u00e9, pou\u017e\u00edva sa ako predpona pre v\u0161etky zariadenia)", + "pin": "PIN k\u00f3d" } } } diff --git a/homeassistant/components/homewizard/translations/sk.json b/homeassistant/components/homewizard/translations/sk.json index a393c32026b..dc965799bfd 100644 --- a/homeassistant/components/homewizard/translations/sk.json +++ b/homeassistant/components/homewizard/translations/sk.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "device_not_supported": "Toto zariadenie nie je podporovan\u00e9" + "device_not_supported": "Toto zariadenie nie je podporovan\u00e9", + "invalid_discovery_parameters": "Zisten\u00e1 nepodporovan\u00e1 verzia API" }, "step": { "discovery_confirm": { diff --git a/homeassistant/components/huawei_lte/translations/sk.json b/homeassistant/components/huawei_lte/translations/sk.json index 390736db02e..bfc3dbc8a1d 100644 --- a/homeassistant/components/huawei_lte/translations/sk.json +++ b/homeassistant/components/huawei_lte/translations/sk.json @@ -3,6 +3,7 @@ "error": { "incorrect_password": "Nespr\u00e1vne heslo", "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_url": "Neplatn\u00e1 adresa URL", "response_error": "Nezn\u00e1ma chyba zo zariadenia" }, "flow_title": "{name}", @@ -14,7 +15,8 @@ }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "url": "URL" } } } @@ -23,7 +25,8 @@ "step": { "init": { "data": { - "name": "N\u00e1zov notifika\u010dnej slu\u017eby (zmena vy\u017eaduje re\u0161tart)" + "name": "N\u00e1zov notifika\u010dnej slu\u017eby (zmena vy\u017eaduje re\u0161tart)", + "recipient": "Pr\u00edjemcovia upozornen\u00ed SMS" } } } diff --git a/homeassistant/components/hue/translations/sk.json b/homeassistant/components/hue/translations/sk.json index a932a3768d6..307a09a5a36 100644 --- a/homeassistant/components/hue/translations/sk.json +++ b/homeassistant/components/hue/translations/sk.json @@ -5,6 +5,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_host": "Neplatn\u00fd hostite\u013e", "no_bridges": "Neboli objaven\u00fd \u017eiaden Philips Hue bridge", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, diff --git a/homeassistant/components/humidifier/translations/sk.json b/homeassistant/components/humidifier/translations/sk.json index 3734130c308..deac076ac2d 100644 --- a/homeassistant/components/humidifier/translations/sk.json +++ b/homeassistant/components/humidifier/translations/sk.json @@ -6,5 +6,12 @@ "trigger_type": { "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9" } - } + }, + "state": { + "_": { + "off": "Neakt\u00edvny", + "on": "Akt\u00edvny" + } + }, + "title": "Zvlh\u010dova\u010d" } \ No newline at end of file diff --git a/homeassistant/components/hyperion/translations/sk.json b/homeassistant/components/hyperion/translations/sk.json index ea5aabee25f..270b19bdc1a 100644 --- a/homeassistant/components/hyperion/translations/sk.json +++ b/homeassistant/components/hyperion/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "auth_new_token_not_granted_error": "Novovytvoren\u00fd token nebol schv\u00e1len\u00fd v pou\u017e\u00edvate\u013eskom rozhran\u00ed Hyperion", + "auth_new_token_not_work_error": "Nepodarilo sa overi\u0165 pomocou novovytvoren\u00e9ho tokenu", "cannot_connect": "Nepodarilo sa pripoji\u0165", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, @@ -12,6 +13,18 @@ "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token" }, "step": { + "auth": { + "data": { + "create_token": "Automaticky vytvori\u0165 nov\u00fd token", + "token": "Alebo poskytnite u\u017e existuj\u00faci token" + } + }, + "create_token": { + "title": "Automaticky vytvori\u0165 nov\u00fd autentifika\u010dn\u00fd token" + }, + "create_token_external": { + "title": "Prijmite nov\u00fd token v pou\u017e\u00edvate\u013eskom rozhran\u00ed Hyperion" + }, "user": { "data": { "host": "Hostite\u013e", diff --git a/homeassistant/components/iaqualink/translations/sk.json b/homeassistant/components/iaqualink/translations/sk.json index 0cb745b92e7..13cce2dcec3 100644 --- a/homeassistant/components/iaqualink/translations/sk.json +++ b/homeassistant/components/iaqualink/translations/sk.json @@ -8,7 +8,8 @@ "user": { "data": { "password": "Heslo" - } + }, + "description": "Zadajte pou\u017e\u00edvate\u013esk\u00e9 meno a heslo pre v\u00e1\u0161 \u00fa\u010det iAqualink." } } } diff --git a/homeassistant/components/inkbird/translations/sk.json b/homeassistant/components/inkbird/translations/sk.json index a8fa63be59a..b121bbc35a3 100644 --- a/homeassistant/components/inkbird/translations/sk.json +++ b/homeassistant/components/inkbird/translations/sk.json @@ -5,11 +5,16 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/intellifire/translations/sk.json b/homeassistant/components/intellifire/translations/sk.json index a3d99983a20..70bdb6bfbcc 100644 --- a/homeassistant/components/intellifire/translations/sk.json +++ b/homeassistant/components/intellifire/translations/sk.json @@ -12,7 +12,8 @@ "step": { "api_config": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Email" } }, "dhcp_confirm": { diff --git a/homeassistant/components/ipma/translations/sk.json b/homeassistant/components/ipma/translations/sk.json index ff6f9b0683b..645383285b9 100644 --- a/homeassistant/components/ipma/translations/sk.json +++ b/homeassistant/components/ipma/translations/sk.json @@ -8,6 +8,7 @@ "data": { "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka", + "mode": "Re\u017eim", "name": "N\u00e1zov" }, "title": "Umiestnenie" diff --git a/homeassistant/components/ipp/translations/sk.json b/homeassistant/components/ipp/translations/sk.json index 868d5e6ccd9..d2ce15b8904 100644 --- a/homeassistant/components/ipp/translations/sk.json +++ b/homeassistant/components/ipp/translations/sk.json @@ -5,12 +5,17 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "ipp_error": "Vyskytla sa chyba IPP." }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{name}", "step": { "user": { "data": { "host": "Hostite\u013e", - "port": "Port" + "port": "Port", + "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } }, "zeroconf_confirm": { diff --git a/homeassistant/components/iqvia/translations/sk.json b/homeassistant/components/iqvia/translations/sk.json index f04d4a327f4..4691f8e2262 100644 --- a/homeassistant/components/iqvia/translations/sk.json +++ b/homeassistant/components/iqvia/translations/sk.json @@ -2,6 +2,16 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "invalid_zip_code": "PS\u010c je neplatn\u00e9" + }, + "step": { + "user": { + "data": { + "zip_code": "PS\u010c" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/isy994/translations/sk.json b/homeassistant/components/isy994/translations/sk.json index d28f0fb7922..3f3d21841e6 100644 --- a/homeassistant/components/isy994/translations/sk.json +++ b/homeassistant/components/isy994/translations/sk.json @@ -5,6 +5,7 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_host": "Z\u00e1znam hostite\u013ea nemal \u00fapln\u00fd form\u00e1t adresy URL, napr. http://192.168.10.100:80", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({host})", @@ -19,8 +20,14 @@ "data": { "host": "URL", "password": "Heslo" - } + }, + "description": "Polo\u017eka hostite\u013ea mus\u00ed by\u0165 v \u00faplnom form\u00e1te URL, napr. http://192.168.10.100:80" } } + }, + "system_health": { + "info": { + "host_reachable": "Hostite\u013e je dostupn\u00fd" + } } } \ No newline at end of file diff --git a/homeassistant/components/jellyfin/translations/sk.json b/homeassistant/components/jellyfin/translations/sk.json index 41d0727a9b4..1334ea2f532 100644 --- a/homeassistant/components/jellyfin/translations/sk.json +++ b/homeassistant/components/jellyfin/translations/sk.json @@ -8,7 +8,8 @@ "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "url": "URL" } } } diff --git a/homeassistant/components/juicenet/translations/sk.json b/homeassistant/components/juicenet/translations/sk.json index b838556fe17..2c2f08622c0 100644 --- a/homeassistant/components/juicenet/translations/sk.json +++ b/homeassistant/components/juicenet/translations/sk.json @@ -7,6 +7,14 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "api_token": "API token" + }, + "description": "Budete potrebova\u0165 API Token z https://home.juice.net/Manage." + } } } } \ No newline at end of file diff --git a/homeassistant/components/justnimbus/translations/sk.json b/homeassistant/components/justnimbus/translations/sk.json index bee35ba27c1..1fb2f793d1f 100644 --- a/homeassistant/components/justnimbus/translations/sk.json +++ b/homeassistant/components/justnimbus/translations/sk.json @@ -7,6 +7,13 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "client_id": "ID klienta" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/keenetic_ndms2/translations/sk.json b/homeassistant/components/keenetic_ndms2/translations/sk.json index aab65ab747b..a53f9a1b785 100644 --- a/homeassistant/components/keenetic_ndms2/translations/sk.json +++ b/homeassistant/components/keenetic_ndms2/translations/sk.json @@ -12,7 +12,8 @@ "data": { "host": "Hostite\u013e", "password": "Heslo", - "port": "Port" + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/kegtron/translations/sk.json b/homeassistant/components/kegtron/translations/sk.json index 48a6116c8ee..8273d877c92 100644 --- a/homeassistant/components/kegtron/translations/sk.json +++ b/homeassistant/components/kegtron/translations/sk.json @@ -6,11 +6,16 @@ "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "not_supported": "Zariadenie nie je podporovan\u00e9" }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/knx/translations/en.json b/homeassistant/components/knx/translations/en.json index 8ed45661d0d..76ed9ba27b2 100644 --- a/homeassistant/components/knx/translations/en.json +++ b/homeassistant/components/knx/translations/en.json @@ -94,6 +94,13 @@ }, "description": "Please enter your IP secure information." }, + "secure_tunneling": { + "description": "Select how you want to configure KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", + "secure_tunnel_manual": "Configure IP secure keys manually" + } + }, "tunnel": { "data": { "gateway": "KNX Tunnel Connection" @@ -209,6 +216,13 @@ }, "description": "Please enter your IP secure information." }, + "secure_tunneling": { + "description": "Select how you want to configure KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", + "secure_tunnel_manual": "Configure IP secure keys manually" + } + }, "tunnel": { "data": { "gateway": "KNX Tunnel Connection" diff --git a/homeassistant/components/konnected/translations/sk.json b/homeassistant/components/konnected/translations/sk.json index ef7dc74d64f..720e0991b48 100644 --- a/homeassistant/components/konnected/translations/sk.json +++ b/homeassistant/components/konnected/translations/sk.json @@ -22,18 +22,28 @@ } }, "options": { + "error": { + "bad_host": "Neplatn\u00e1 adresa URL hostite\u013ea API Override" + }, "step": { "options_binary": { "data": { "name": "N\u00e1zov (volite\u013en\u00fd)", "type": "Typ sn\u00edma\u010da" }, + "description": "mo\u017enosti {zone}", "title": "Konfigur\u00e1cia sn\u00edma\u010da" }, "options_digital": { "data": { "name": "N\u00e1zov (volite\u013en\u00fd)", "type": "Typ sn\u00edma\u010da" + }, + "description": "mo\u017enosti {zone}" + }, + "options_misc": { + "data": { + "api_host": "Prep\u00edsanie adresy URL hostite\u013ea API" } }, "options_switch": { diff --git a/homeassistant/components/kraken/translations/sk.json b/homeassistant/components/kraken/translations/sk.json index 73566432806..715c2e1496d 100644 --- a/homeassistant/components/kraken/translations/sk.json +++ b/homeassistant/components/kraken/translations/sk.json @@ -2,6 +2,11 @@ "config": { "abort": { "already_configured": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "user": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } } }, "options": { diff --git a/homeassistant/components/lametric/translations/sk.json b/homeassistant/components/lametric/translations/sk.json index c80688dab97..35890aecb26 100644 --- a/homeassistant/components/lametric/translations/sk.json +++ b/homeassistant/components/lametric/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "invalid_discovery_info": "Prijat\u00e9 neplatn\u00e9 inform\u00e1cie o zis\u0165ovan\u00ed", "link_local_address": "Lok\u00e1lne adresy odkazov nie s\u00fa podporovan\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, @@ -12,6 +13,7 @@ "step": { "manual_entry": { "data": { + "api_key": "API k\u013e\u00fa\u010d", "host": "Hostite\u013e" } } diff --git a/homeassistant/components/lidarr/translations/sk.json b/homeassistant/components/lidarr/translations/sk.json index f04d4a327f4..824b22a1b5d 100644 --- a/homeassistant/components/lidarr/translations/sk.json +++ b/homeassistant/components/lidarr/translations/sk.json @@ -1,7 +1,30 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", + "wrong_app": "Dosiahnut\u00e1 nespr\u00e1vna aplik\u00e1cia. Sk\u00faste to pros\u00edm znova", + "zeroconf_failed": "K\u013e\u00fa\u010d API sa nena\u0161iel. Zadajte ho ru\u010dne" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + }, + "title": "Znova overi\u0165 integr\u00e1ciu" + }, + "user": { + "data": { + "api_key": "API k\u013e\u00fa\u010d", + "url": "URL", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sk.json b/homeassistant/components/litterrobot/translations/sk.json index 6dbe2a76791..db3d1e419d4 100644 --- a/homeassistant/components/litterrobot/translations/sk.json +++ b/homeassistant/components/litterrobot/translations/sk.json @@ -10,7 +10,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Aktualizujte svoje heslo pre {username}" }, "user": { "data": { diff --git a/homeassistant/components/local_ip/translations/sk.json b/homeassistant/components/local_ip/translations/sk.json index 58545d48767..ccfaf6af209 100644 --- a/homeassistant/components/local_ip/translations/sk.json +++ b/homeassistant/components/local_ip/translations/sk.json @@ -1,7 +1,11 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "step": { "user": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?", "title": "Lok\u00e1lna IP adresa" } } diff --git a/homeassistant/components/locative/translations/sk.json b/homeassistant/components/locative/translations/sk.json new file mode 100644 index 00000000000..473cb1c2fa0 --- /dev/null +++ b/homeassistant/components/locative/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/logi_circle/translations/sk.json b/homeassistant/components/logi_circle/translations/sk.json index a243b0ad8eb..44e71ddaa41 100644 --- a/homeassistant/components/logi_circle/translations/sk.json +++ b/homeassistant/components/logi_circle/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, "error": { + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", "invalid_auth": "Neplatn\u00e9 overenie" } } diff --git a/homeassistant/components/lyric/translations/sk.json b/homeassistant/components/lyric/translations/sk.json index 520a3afd6d9..8862fc534f1 100644 --- a/homeassistant/components/lyric/translations/sk.json +++ b/homeassistant/components/lyric/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "create_entry": { diff --git a/homeassistant/components/meater/translations/sk.json b/homeassistant/components/meater/translations/sk.json index 6bf84cbc8e3..41d88c34432 100644 --- a/homeassistant/components/meater/translations/sk.json +++ b/homeassistant/components/meater/translations/sk.json @@ -14,6 +14,9 @@ "user": { "data": { "password": "Heslo" + }, + "data_description": { + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno slu\u017eby Meater Cloud, zvy\u010dajne e-mailov\u00e1 adresa." } } } diff --git a/homeassistant/components/mikrotik/translations/sk.json b/homeassistant/components/mikrotik/translations/sk.json index 3293e1a5870..4301cfb5c88 100644 --- a/homeassistant/components/mikrotik/translations/sk.json +++ b/homeassistant/components/mikrotik/translations/sk.json @@ -12,7 +12,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Heslo pre {username} je neplatn\u00e9." }, "user": { "data": { @@ -23,5 +24,14 @@ } } } + }, + "options": { + "step": { + "device_tracker": { + "data": { + "arp_ping": "Povoli\u0165 ARP ping" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/sk.json b/homeassistant/components/min_max/translations/sk.json new file mode 100644 index 00000000000..41019a4abc6 --- /dev/null +++ b/homeassistant/components/min_max/translations/sk.json @@ -0,0 +1,20 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Vstupn\u00e9 entity" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Vstupn\u00e9 entity" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/moat/translations/sk.json b/homeassistant/components/moat/translations/sk.json index a8fa63be59a..b121bbc35a3 100644 --- a/homeassistant/components/moat/translations/sk.json +++ b/homeassistant/components/moat/translations/sk.json @@ -5,11 +5,16 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/modem_callerid/translations/sk.json b/homeassistant/components/modem_callerid/translations/sk.json index 28f532167a7..a11a0c0cc82 100644 --- a/homeassistant/components/modem_callerid/translations/sk.json +++ b/homeassistant/components/modem_callerid/translations/sk.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "no_devices_found": "Nena\u0161li sa \u017eiadne zost\u00e1vaj\u00face zariadenia" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165" diff --git a/homeassistant/components/motioneye/translations/sk.json b/homeassistant/components/motioneye/translations/sk.json index d61d1c26d16..fdf5b73c8f2 100644 --- a/homeassistant/components/motioneye/translations/sk.json +++ b/homeassistant/components/motioneye/translations/sk.json @@ -5,7 +5,16 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_url": "Neplatn\u00e1 adresa URL" + }, + "step": { + "user": { + "data": { + "admin_password": "Spr\u00e1vca Heslo", + "surveillance_password": "Doh\u013ead Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/mqtt/translations/sk.json b/homeassistant/components/mqtt/translations/sk.json index 2bb5c5b89cf..873893643b8 100644 --- a/homeassistant/components/mqtt/translations/sk.json +++ b/homeassistant/components/mqtt/translations/sk.json @@ -1,13 +1,24 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "error": { + "bad_certificate": "Certifik\u00e1t CA je neplatn\u00fd", + "bad_ws_headers": "Zadajte platn\u00e9 hlavi\u010dky HTTP ako objekt JSON", + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { "broker": { "data": { + "advanced_options": "Pokro\u010dil\u00e9 nastavenia", + "client_key": "Nahrajte s\u00fabor so s\u00fakromn\u00fdm k\u013e\u00fa\u010dom", "password": "Heslo", "port": "Port", + "protocol": "MQTT protokol", + "set_client_cert": "Pou\u017eite klientsky certifik\u00e1t", + "transport": "MQTT transport", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, @@ -23,17 +34,32 @@ "button_3": "Tretie tla\u010didlo", "button_4": "\u0160tvrt\u00e9 tla\u010didlo", "button_5": "Piate tla\u010didlo", - "button_6": "\u0160ieste tla\u010didlo" + "button_6": "\u0160ieste tla\u010didlo", + "turn_off": "Vypn\u00fa\u0165", + "turn_on": "Zapn\u00fa\u0165" } }, "options": { + "error": { + "bad_certificate": "Certifik\u00e1t CA je neplatn\u00fd", + "bad_ws_headers": "Zadajte platn\u00e9 hlavi\u010dky HTTP ako objekt JSON", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "broker": { "data": { + "advanced_options": "Pokro\u010dil\u00e9 nastavenia", + "client_key": "Nahrajte s\u00fabor so s\u00fakromn\u00fdm k\u013e\u00fa\u010dom", "password": "Heslo", "port": "Port", + "protocol": "MQTT protokol", + "set_client_cert": "Pou\u017eite klientsky certifik\u00e1t", + "transport": "MQTT transport", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } + }, + "options": { + "title": "MQTT mo\u017enosti" } } } diff --git a/homeassistant/components/myq/translations/sk.json b/homeassistant/components/myq/translations/sk.json index 8ff8168d1e7..fe1ebf7793a 100644 --- a/homeassistant/components/myq/translations/sk.json +++ b/homeassistant/components/myq/translations/sk.json @@ -11,7 +11,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Heslo pre {username} u\u017e nie je platn\u00e9." }, "user": { "data": { diff --git a/homeassistant/components/mysensors/translations/sk.json b/homeassistant/components/mysensors/translations/sk.json index 2daeb9e3561..96d77b5a9f1 100644 --- a/homeassistant/components/mysensors/translations/sk.json +++ b/homeassistant/components/mysensors/translations/sk.json @@ -9,6 +9,7 @@ "invalid_ip": "Neplatn\u00e1 IP adresa", "invalid_port": "Neplatn\u00e9 \u010d\u00edslo portu", "invalid_serial": "Neplatn\u00fd s\u00e9riov\u00fd port", + "invalid_version": "Neplatn\u00e1 verzia MySensors", "not_a_number": "Zadajte \u010d\u00edslo", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, @@ -22,6 +23,7 @@ "invalid_ip": "Neplatn\u00e1 IP adresa", "invalid_port": "Neplatn\u00e9 \u010d\u00edslo portu", "invalid_serial": "Neplatn\u00fd s\u00e9riov\u00fd port", + "invalid_version": "Neplatn\u00e1 verzia MySensors", "not_a_number": "Zadajte \u010d\u00edslo", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, diff --git a/homeassistant/components/nam/translations/sk.json b/homeassistant/components/nam/translations/sk.json index 116df3a12bd..e9bcab9374a 100644 --- a/homeassistant/components/nam/translations/sk.json +++ b/homeassistant/components/nam/translations/sk.json @@ -13,7 +13,8 @@ "credentials": { "data": { "password": "Heslo" - } + }, + "description": "Zadajte pou\u017e\u00edvate\u013esk\u00e9 meno a heslo." }, "reauth_confirm": { "data": { diff --git a/homeassistant/components/nanoleaf/translations/sk.json b/homeassistant/components/nanoleaf/translations/sk.json index 164d511360f..8d6e5de117b 100644 --- a/homeassistant/components/nanoleaf/translations/sk.json +++ b/homeassistant/components/nanoleaf/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "invalid_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "flow_title": "{name}", diff --git a/homeassistant/components/neato/translations/sk.json b/homeassistant/components/neato/translations/sk.json index 67cfbafa4f8..074a1f1164a 100644 --- a/homeassistant/components/neato/translations/sk.json +++ b/homeassistant/components/neato/translations/sk.json @@ -6,6 +6,11 @@ }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" + }, + "step": { + "reauth_confirm": { + "title": "Chcete za\u010da\u0165 nastavova\u0165?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/nest/translations/sk.json b/homeassistant/components/nest/translations/sk.json index 4781474c4e6..66a3817501a 100644 --- a/homeassistant/components/nest/translations/sk.json +++ b/homeassistant/components/nest/translations/sk.json @@ -2,13 +2,40 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" }, "error": { - "invalid_pin": "Nespr\u00e1vny PIN" + "internal_error": "Intern\u00e1 chyba pri overovan\u00ed k\u00f3du", + "invalid_pin": "Nespr\u00e1vny PIN", + "timeout": "Overovac\u00ed k\u00f3d \u010dasov\u00e9ho limitu" + }, + "step": { + "cloud_project": { + "data": { + "cloud_project_id": "ID projektu Google Cloud" + }, + "title": "Hniezdo: Zadajte ID projektu Cloud" + }, + "init": { + "description": "Vyberte met\u00f3du overenia" + }, + "link": { + "data": { + "code": "PIN k\u00f3d" + }, + "description": "Ak chcete prepoji\u0165 svoje konto Nest, [autorizujte svoje konto]({url}).\n\nPo autoriz\u00e1cii skop\u00edrujte a vlo\u017ete poskytnut\u00fd k\u00f3d PIN uveden\u00fd ni\u017e\u0161ie." + }, + "pubsub": { + "data": { + "cloud_project_id": "ID projektu Google Cloud" + }, + "description": "Nav\u0161t\u00edvte [Cloud Console]({url}) a vyh\u013eadajte svoje ID projektu Google Cloud.", + "title": "Nakonfigurujte Google Cloud" + } } }, "device_automation": { diff --git a/homeassistant/components/netatmo/translations/sk.json b/homeassistant/components/netatmo/translations/sk.json index fcadc9ac0c9..211690bb3b8 100644 --- a/homeassistant/components/netatmo/translations/sk.json +++ b/homeassistant/components/netatmo/translations/sk.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "create_entry": { diff --git a/homeassistant/components/nextdns/translations/sk.json b/homeassistant/components/nextdns/translations/sk.json index e2db0e016f7..a748bf158fb 100644 --- a/homeassistant/components/nextdns/translations/sk.json +++ b/homeassistant/components/nextdns/translations/sk.json @@ -2,6 +2,23 @@ "config": { "abort": { "already_configured": "Tento profil NextDNS je u\u017e nakonfigurovan\u00fd." + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "profiles": { + "data": { + "profile": "Profil" + } + }, + "user": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/nibe_heatpump/translations/sk.json b/homeassistant/components/nibe_heatpump/translations/sk.json index 062077bf047..692f1569f7b 100644 --- a/homeassistant/components/nibe_heatpump/translations/sk.json +++ b/homeassistant/components/nibe_heatpump/translations/sk.json @@ -4,6 +4,11 @@ "address_in_use": "Vybran\u00fd port po\u010d\u00favania sa u\u017e v tomto syst\u00e9me pou\u017e\u00edva." }, "step": { + "modbus": { + "data": { + "modbus_url": "Modbus URL" + } + }, "nibegw": { "data": { "ip_address": "Vzdialen\u00e1 adresa" diff --git a/homeassistant/components/nightscout/translations/sk.json b/homeassistant/components/nightscout/translations/sk.json index f5f4789c7fe..4078f7bcb6c 100644 --- a/homeassistant/components/nightscout/translations/sk.json +++ b/homeassistant/components/nightscout/translations/sk.json @@ -9,7 +9,8 @@ "step": { "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" + "api_key": "API k\u013e\u00fa\u010d", + "url": "URL" } } } diff --git a/homeassistant/components/nobo_hub/translations/sk.json b/homeassistant/components/nobo_hub/translations/sk.json index ff1d25e970f..67c314052bd 100644 --- a/homeassistant/components/nobo_hub/translations/sk.json +++ b/homeassistant/components/nobo_hub/translations/sk.json @@ -4,7 +4,8 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_ip": "Neplatn\u00e1 IP adresa" + "invalid_ip": "Neplatn\u00e1 IP adresa", + "invalid_serial": "Neplatn\u00e9 s\u00e9riov\u00e9 \u010d\u00edslo" }, "step": { "manual": { diff --git a/homeassistant/components/notion/translations/sk.json b/homeassistant/components/notion/translations/sk.json index 5520e679328..5035a9ee157 100644 --- a/homeassistant/components/notion/translations/sk.json +++ b/homeassistant/components/notion/translations/sk.json @@ -11,7 +11,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Znova zadajte heslo pre {username}." }, "user": { "data": { diff --git a/homeassistant/components/nuheat/translations/sk.json b/homeassistant/components/nuheat/translations/sk.json index 8f06d99549b..6ccc4edd5ec 100644 --- a/homeassistant/components/nuheat/translations/sk.json +++ b/homeassistant/components/nuheat/translations/sk.json @@ -4,7 +4,8 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_thermostat": "S\u00e9riov\u00e9 \u010d\u00edslo termostatu je neplatn\u00e9." }, "step": { "user": { diff --git a/homeassistant/components/octoprint/translations/sk.json b/homeassistant/components/octoprint/translations/sk.json index 3c3d27a6689..ff59ac2967c 100644 --- a/homeassistant/components/octoprint/translations/sk.json +++ b/homeassistant/components/octoprint/translations/sk.json @@ -1,8 +1,10 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "auth_failed": "Nepodarilo sa na\u010d\u00edta\u0165 k\u013e\u00fa\u010d API aplik\u00e1cie" }, + "flow_title": "Tla\u010diare\u0148 OctoPrint: {host}", "step": { "user": { "data": { diff --git a/homeassistant/components/ondilo_ico/translations/sk.json b/homeassistant/components/ondilo_ico/translations/sk.json index c19b1a0b70c..91803ae5155 100644 --- a/homeassistant/components/ondilo_ico/translations/sk.json +++ b/homeassistant/components/ondilo_ico/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." + }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" } diff --git a/homeassistant/components/onewire/translations/sk.json b/homeassistant/components/onewire/translations/sk.json index b20cd8f3ca6..87147f54cd3 100644 --- a/homeassistant/components/onewire/translations/sk.json +++ b/homeassistant/components/onewire/translations/sk.json @@ -23,7 +23,8 @@ "configure_device": { "data": { "precision": "Presnos\u0165 sn\u00edma\u010da" - } + }, + "description": "Vyberte presnos\u0165 sn\u00edma\u010da pre {sensor_id}" } } } diff --git a/homeassistant/components/openexchangerates/translations/sk.json b/homeassistant/components/openexchangerates/translations/sk.json index f04d4a327f4..461dced0cd6 100644 --- a/homeassistant/components/openexchangerates/translations/sk.json +++ b/homeassistant/components/openexchangerates/translations/sk.json @@ -2,6 +2,16 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/opentherm_gw/translations/sk.json b/homeassistant/components/opentherm_gw/translations/sk.json index e2b3ddda5b3..ac89d84cb09 100644 --- a/homeassistant/components/opentherm_gw/translations/sk.json +++ b/homeassistant/components/opentherm_gw/translations/sk.json @@ -7,6 +7,8 @@ "step": { "init": { "data": { + "device": "Cesta alebo URL", + "id": "ID", "name": "N\u00e1zov" } } diff --git a/homeassistant/components/openuv/translations/sk.json b/homeassistant/components/openuv/translations/sk.json index 322fcfa33a3..36fc17456fb 100644 --- a/homeassistant/components/openuv/translations/sk.json +++ b/homeassistant/components/openuv/translations/sk.json @@ -7,6 +7,12 @@ "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + }, + "description": "Znova zadajte k\u013e\u00fa\u010d API pre {latitude}, {longitude}." + }, "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", diff --git a/homeassistant/components/openweathermap/translations/sk.json b/homeassistant/components/openweathermap/translations/sk.json index f14039f810f..64b72099594 100644 --- a/homeassistant/components/openweathermap/translations/sk.json +++ b/homeassistant/components/openweathermap/translations/sk.json @@ -12,7 +12,8 @@ "api_key": "API k\u013e\u00fa\u010d", "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka" - } + }, + "description": "Ak chcete vygenerova\u0165 k\u013e\u00fa\u010d API, prejdite na https://openweathermap.org/appid" } } } diff --git a/homeassistant/components/oralb/translations/sk.json b/homeassistant/components/oralb/translations/sk.json index 48a6116c8ee..8273d877c92 100644 --- a/homeassistant/components/oralb/translations/sk.json +++ b/homeassistant/components/oralb/translations/sk.json @@ -6,11 +6,16 @@ "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "not_supported": "Zariadenie nie je podporovan\u00e9" }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/overkiz/translations/sk.json b/homeassistant/components/overkiz/translations/sk.json index 46a71cd5811..33311c8c92e 100644 --- a/homeassistant/components/overkiz/translations/sk.json +++ b/homeassistant/components/overkiz/translations/sk.json @@ -5,8 +5,10 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "too_many_attempts": "Pr\u00edli\u0161 ve\u013ea pokusov s neplatn\u00fdm tokenom, do\u010dasne zak\u00e1zan\u00e9" }, + "flow_title": "Br\u00e1na: {gateway_id}", "step": { "user": { "data": { diff --git a/homeassistant/components/p1_monitor/translations/sk.json b/homeassistant/components/p1_monitor/translations/sk.json index 842ff61bd79..52ff5c28245 100644 --- a/homeassistant/components/p1_monitor/translations/sk.json +++ b/homeassistant/components/p1_monitor/translations/sk.json @@ -4,6 +4,9 @@ "user": { "data": { "host": "Hostite\u013e" + }, + "data_description": { + "host": "IP adresa alebo n\u00e1zov hostite\u013ea va\u0161ej in\u0161tal\u00e1cie monitora P1." } } } diff --git a/homeassistant/components/panasonic_viera/translations/sk.json b/homeassistant/components/panasonic_viera/translations/sk.json index 7af031dddc0..8b6520976a6 100644 --- a/homeassistant/components/panasonic_viera/translations/sk.json +++ b/homeassistant/components/panasonic_viera/translations/sk.json @@ -3,7 +3,16 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "invalid_pin_code": "Zadan\u00fd PIN k\u00f3d bol neplatn\u00fd" + }, "step": { + "pairing": { + "data": { + "pin": "PIN k\u00f3d" + }, + "description": "Zadajte PIN k\u00f3d zobrazen\u00fd na va\u0161om telev\u00edzore" + }, "user": { "data": { "host": "IP adresa", diff --git a/homeassistant/components/plaato/translations/sk.json b/homeassistant/components/plaato/translations/sk.json index 6d0d7cebc1c..b7ae80d4937 100644 --- a/homeassistant/components/plaato/translations/sk.json +++ b/homeassistant/components/plaato/translations/sk.json @@ -3,11 +3,21 @@ "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, + "error": { + "no_auth_token": "Mus\u00edte prida\u0165 autoriza\u010dn\u00fd token" + }, "step": { + "api_method": { + "data": { + "token": "Sem vlo\u017ete autoriza\u010dn\u00fd token" + }, + "title": "Vyberte met\u00f3du API" + }, "user": { "data": { "device_name": "Pomenujte svoje zariadenie" - } + }, + "description": "Chcete za\u010da\u0165 nastavova\u0165?" } } }, diff --git a/homeassistant/components/plex/translations/sk.json b/homeassistant/components/plex/translations/sk.json index 31d35b4827c..8b81cdd0ccf 100644 --- a/homeassistant/components/plex/translations/sk.json +++ b/homeassistant/components/plex/translations/sk.json @@ -5,8 +5,12 @@ "already_configured": "Tento Plex server u\u017e je nakonfigurovan\u00fd", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "token_request_timeout": "\u010casov\u00fd limit pri z\u00edskavan\u00ed tokenu vypr\u0161al", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, + "error": { + "faulty_credentials": "Autoriz\u00e1cia zlyhala, overte token" + }, "flow_title": "{name} ({host})", "step": { "manual_setup": { @@ -14,6 +18,7 @@ "host": "Hostite\u013e", "port": "Port", "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", + "token": "Token (volite\u013en\u00fd)", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } diff --git a/homeassistant/components/point/translations/sk.json b/homeassistant/components/point/translations/sk.json index 25f026c0a69..bd6f0ce772d 100644 --- a/homeassistant/components/point/translations/sk.json +++ b/homeassistant/components/point/translations/sk.json @@ -1,10 +1,20 @@ { "config": { "abort": { - "already_setup": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + "already_setup": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "unknown_authorize_url_generation": "Nezn\u00e1ma chyba pri generovan\u00ed autorizovanej adresy URL." }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" + }, + "error": { + "no_token": "Neplatn\u00fd pr\u00edstupov\u00fd token" + }, + "step": { + "user": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/profiler/translations/sk.json b/homeassistant/components/profiler/translations/sk.json new file mode 100644 index 00000000000..473cb1c2fa0 --- /dev/null +++ b/homeassistant/components/profiler/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/sk.json b/homeassistant/components/prusalink/translations/sk.json index 842ff61bd79..2c7ec866cf3 100644 --- a/homeassistant/components/prusalink/translations/sk.json +++ b/homeassistant/components/prusalink/translations/sk.json @@ -1,8 +1,12 @@ { "config": { + "error": { + "invalid_auth": "Neplatn\u00e9 overenie" + }, "step": { "user": { "data": { + "api_key": "API k\u013e\u00fa\u010d", "host": "Hostite\u013e" } } diff --git a/homeassistant/components/ps4/translations/sk.json b/homeassistant/components/ps4/translations/sk.json index ac8eab24ba0..74600b1e9ad 100644 --- a/homeassistant/components/ps4/translations/sk.json +++ b/homeassistant/components/ps4/translations/sk.json @@ -6,11 +6,13 @@ }, "error": { "credential_timeout": "\u010casov\u00fd limit slu\u017eby poveren\u00ed vypr\u0161al. Re\u0161tartujte stla\u010den\u00edm tla\u010didla Odosla\u0165.", + "login_failed": "Nepodarilo sa sp\u00e1rova\u0165 s PlayStation 4. Overte, \u010di je PIN k\u00f3d spr\u00e1vny.", "no_ipaddress": "Zadajte IP adresa konzoly PlayStation 4, ktor\u00fa chcete nakonfigurova\u0165." }, "step": { "link": { "data": { + "code": "PIN k\u00f3d", "ip_address": "IP adresa", "name": "N\u00e1zov" } diff --git a/homeassistant/components/pure_energie/translations/sk.json b/homeassistant/components/pure_energie/translations/sk.json index c1e7b583554..7b8f057ac6a 100644 --- a/homeassistant/components/pure_energie/translations/sk.json +++ b/homeassistant/components/pure_energie/translations/sk.json @@ -8,6 +8,9 @@ "user": { "data": { "host": "Hostite\u013e" + }, + "data_description": { + "host": "IP adresa alebo n\u00e1zov hostite\u013ea v\u00e1\u0161ho mera\u010da energie Pure." } } } diff --git a/homeassistant/components/pushover/translations/sk.json b/homeassistant/components/pushover/translations/sk.json index f04d4a327f4..af10343f303 100644 --- a/homeassistant/components/pushover/translations/sk.json +++ b/homeassistant/components/pushover/translations/sk.json @@ -2,6 +2,22 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", + "invalid_user_key": "Neplatn\u00fd k\u013e\u00fa\u010d pou\u017e\u00edvate\u013ea" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + } + }, + "user": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/pvoutput/translations/sk.json b/homeassistant/components/pvoutput/translations/sk.json index 4eba3bdc8bb..f82a2e35a42 100644 --- a/homeassistant/components/pvoutput/translations/sk.json +++ b/homeassistant/components/pvoutput/translations/sk.json @@ -14,7 +14,8 @@ }, "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" + "api_key": "API k\u013e\u00fa\u010d", + "system_id": "ID syst\u00e9mu" } } } diff --git a/homeassistant/components/qingping/translations/sk.json b/homeassistant/components/qingping/translations/sk.json index 48a6116c8ee..8273d877c92 100644 --- a/homeassistant/components/qingping/translations/sk.json +++ b/homeassistant/components/qingping/translations/sk.json @@ -6,11 +6,16 @@ "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "not_supported": "Zariadenie nie je podporovan\u00e9" }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/qnap_qsw/translations/sk.json b/homeassistant/components/qnap_qsw/translations/sk.json index 3671c1f6d2b..d3a803a0f68 100644 --- a/homeassistant/components/qnap_qsw/translations/sk.json +++ b/homeassistant/components/qnap_qsw/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "invalid_id": "Zariadenie vr\u00e1tilo neplatn\u00e9 jedine\u010dn\u00e9 ID" + }, + "error": { + "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { "discovered_connection": { @@ -11,7 +15,8 @@ }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "url": "URL" } } } diff --git a/homeassistant/components/radarr/translations/ca.json b/homeassistant/components/radarr/translations/ca.json index 4cd6b930f7a..de8c3861de9 100644 --- a/homeassistant/components/radarr/translations/ca.json +++ b/homeassistant/components/radarr/translations/ca.json @@ -30,6 +30,10 @@ "deprecated_yaml": { "description": "La configuraci\u00f3 de Radarr mitjan\u00e7ant YAML s'eliminar\u00e0 de Home Assistant.\n\nLa configuraci\u00f3 YAML existent s'ha importat autom\u00e0ticament a la interf\u00edcie d'usuari.\n\nElimina la configuraci\u00f3 YAML de Radarr del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", "title": "La configuraci\u00f3 YAML de Radarr est\u00e0 sent eliminada" + }, + "removed_yaml": { + "description": "La configuraci\u00f3 de Radarr mitjan\u00e7ant YAML s'ha eliminat de Home Assistant.\n\nHome Assistant ja no utilitza la configuraci\u00f3 YAML existent.\n\nElimina la configuraci\u00f3 YAML corresponent del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", + "title": "La configuraci\u00f3 YAML de Radarr s'ha eliminat" } }, "options": { diff --git a/homeassistant/components/radarr/translations/sk.json b/homeassistant/components/radarr/translations/sk.json index f04d4a327f4..04ba52596e5 100644 --- a/homeassistant/components/radarr/translations/sk.json +++ b/homeassistant/components/radarr/translations/sk.json @@ -2,6 +2,18 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "invalid_auth": "Neplatn\u00e9 overenie", + "zeroconf_failed": "K\u013e\u00fa\u010d API sa nena\u0161iel. Zadajte ho ru\u010dne" + }, + "step": { + "user": { + "data": { + "api_key": "API k\u013e\u00fa\u010d", + "url": "URL" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/rainmachine/translations/sk.json b/homeassistant/components/rainmachine/translations/sk.json index c9c84dd69a7..ebdda948792 100644 --- a/homeassistant/components/rainmachine/translations/sk.json +++ b/homeassistant/components/rainmachine/translations/sk.json @@ -6,6 +6,7 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{ip}", "step": { "user": { "data": { diff --git a/homeassistant/components/renault/translations/sk.json b/homeassistant/components/renault/translations/sk.json index e3c85b89917..facca5e688c 100644 --- a/homeassistant/components/renault/translations/sk.json +++ b/homeassistant/components/renault/translations/sk.json @@ -11,7 +11,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Aktualizujte svoje heslo pre {username}" }, "user": { "data": { diff --git a/homeassistant/components/ridwell/translations/sk.json b/homeassistant/components/ridwell/translations/sk.json index 5520e679328..d74f548d29a 100644 --- a/homeassistant/components/ridwell/translations/sk.json +++ b/homeassistant/components/ridwell/translations/sk.json @@ -5,18 +5,23 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Znova zadajte heslo pre {username}:", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "description": "Zadajte pou\u017e\u00edvate\u013esk\u00e9 meno a heslo:" } } } diff --git a/homeassistant/components/risco/translations/sk.json b/homeassistant/components/risco/translations/sk.json index 4c651486bd4..a53e56c8354 100644 --- a/homeassistant/components/risco/translations/sk.json +++ b/homeassistant/components/risco/translations/sk.json @@ -9,12 +9,14 @@ "step": { "cloud": { "data": { - "password": "Heslo" + "password": "Heslo", + "pin": "PIN k\u00f3d" } }, "local": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "pin": "PIN k\u00f3d" } } } diff --git a/homeassistant/components/roomba/translations/sk.json b/homeassistant/components/roomba/translations/sk.json index b941f41c75b..102b76ebec0 100644 --- a/homeassistant/components/roomba/translations/sk.json +++ b/homeassistant/components/roomba/translations/sk.json @@ -5,6 +5,9 @@ }, "flow_title": "{name} ({host})", "step": { + "link": { + "title": "Op\u00e4tovne z\u00edska\u0165 heslo" + }, "link_manual": { "data": { "password": "Heslo" diff --git a/homeassistant/components/roon/translations/sk.json b/homeassistant/components/roon/translations/sk.json index 8d62051fd1d..d38abd69c53 100644 --- a/homeassistant/components/roon/translations/sk.json +++ b/homeassistant/components/roon/translations/sk.json @@ -10,7 +10,8 @@ "fallback": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Server Roon sa nepodarilo objavi\u0165, zadajte n\u00e1zov hostite\u013ea a port." } } } diff --git a/homeassistant/components/rtsp_to_webrtc/translations/sk.json b/homeassistant/components/rtsp_to_webrtc/translations/sk.json new file mode 100644 index 00000000000..acecadcff42 --- /dev/null +++ b/homeassistant/components/rtsp_to_webrtc/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "invalid_url": "Mus\u00ed to by\u0165 platn\u00e1 adresa URL servera RTSPtoWebRTC, napr. https://example.com" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/sk.json b/homeassistant/components/sabnzbd/translations/sk.json index 9d5ee388dc3..7c6659c2e0b 100644 --- a/homeassistant/components/sabnzbd/translations/sk.json +++ b/homeassistant/components/sabnzbd/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/sk.json b/homeassistant/components/samsungtv/translations/sk.json index 3fb8c75cdae..30592fe63ac 100644 --- a/homeassistant/components/samsungtv/translations/sk.json +++ b/homeassistant/components/samsungtv/translations/sk.json @@ -3,14 +3,22 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "id_missing": "Toto zariadenie Samsung nem\u00e1 s\u00e9riov\u00e9 \u010d\u00edslo.", "not_supported": "Toto zariadenie Samsung moment\u00e1lne nie je podporovan\u00e9.", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "error": { + "invalid_pin": "PIN je neplatn\u00fd, sk\u00faste to znova." }, "flow_title": "{device}", "step": { "encrypted_pairing": { "description": "Zadajte k\u00f3d PIN zobrazen\u00fd na {device}." }, + "reauth_confirm_encrypted": { + "description": "Zadajte k\u00f3d PIN zobrazen\u00fd na {device}." + }, "user": { "data": { "host": "Hostite\u013e", diff --git a/homeassistant/components/sense/translations/sk.json b/homeassistant/components/sense/translations/sk.json index d6121453b25..bca908bc0c2 100644 --- a/homeassistant/components/sense/translations/sk.json +++ b/homeassistant/components/sense/translations/sk.json @@ -10,13 +10,20 @@ "reauth_validate": { "data": { "password": "Heslo" - } + }, + "description": "Integr\u00e1cia Sense vy\u017eaduje op\u00e4tovn\u00e9 overenie v\u00e1\u0161ho \u00fa\u010dtu {email}.", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { "email": "Email", "password": "Heslo" } + }, + "validation": { + "data": { + "code": "Overovac\u00ed k\u00f3d" + } } } } diff --git a/homeassistant/components/sensibo/translations/sk.json b/homeassistant/components/sensibo/translations/sk.json index 53902d72f41..2aca92ef50f 100644 --- a/homeassistant/components/sensibo/translations/sk.json +++ b/homeassistant/components/sensibo/translations/sk.json @@ -5,12 +5,24 @@ }, "error": { "incorrect_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d pre vybran\u00fd \u00fa\u010det", + "invalid_auth": "Neplatn\u00e9 overenie", "no_devices": "Nena\u0161li sa \u017eiadne zariadenia" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + }, + "data_description": { + "api_key": "Ak chcete z\u00edska\u0165 k\u013e\u00fa\u010d API, postupujte pod\u013ea dokument\u00e1cie" + } + }, "user": { "data": { "api_key": "API k\u013e\u00fa\u010d" + }, + "data_description": { + "api_key": "Ak chcete z\u00edska\u0165 k\u013e\u00fa\u010d API, postupujte pod\u013ea dokument\u00e1cie" } } } diff --git a/homeassistant/components/sensor/translations/sk.json b/homeassistant/components/sensor/translations/sk.json index 943a00921be..d6e664f221d 100644 --- a/homeassistant/components/sensor/translations/sk.json +++ b/homeassistant/components/sensor/translations/sk.json @@ -36,7 +36,14 @@ "is_weight": "Aktu\u00e1lna hmotnos\u0165 {entity_name}" }, "trigger_type": { + "carbon_dioxide": "{entity_name} sa men\u00ed koncentr\u00e1cia oxidu uhli\u010dit\u00e9ho", + "carbon_monoxide": "{entity_name} sa men\u00ed koncentr\u00e1cia oxidu uho\u013enat\u00e9ho", "current": "{entity_name} pr\u00fad sa zmen\u00ed", + "humidity": "{n\u00e1zov_objektu} zmeny vlhkosti", + "nitrogen_dioxide": "{entity_name} zmeny koncentr\u00e1cie oxidu dusi\u010dit\u00e9ho", + "nitrogen_monoxide": "{entity_name} zmeny koncentr\u00e1cie oxidu dusnat\u00e9ho", + "nitrous_oxide": "{entity_name} zmeny koncentr\u00e1cie oxidu dusn\u00e9ho", + "sulphur_dioxide": "{entity_name} zmeny koncentr\u00e1cie oxidu siri\u010dit\u00e9ho", "temperature": "Zmena teploty {entity_name}", "value": "Zmena hodnoty {entity_name}", "voltage": "{entity_name} zmen\u00ed nap\u00e4tie" diff --git a/homeassistant/components/sentry/translations/sk.json b/homeassistant/components/sentry/translations/sk.json new file mode 100644 index 00000000000..14ad46bdb6e --- /dev/null +++ b/homeassistant/components/sentry/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "bad_dsn": "Neplatn\u00e9 DSN" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sia/translations/sk.json b/homeassistant/components/sia/translations/sk.json index f65e1400be1..ff32f90e682 100644 --- a/homeassistant/components/sia/translations/sk.json +++ b/homeassistant/components/sia/translations/sk.json @@ -1,14 +1,23 @@ { "config": { "error": { + "invalid_account_format": "\u00da\u010det nie je hexadecim\u00e1lna hodnota, pou\u017e\u00edvajte len 0-9 a A-F.", + "invalid_account_length": "\u00da\u010det nem\u00e1 spr\u00e1vnu d\u013a\u017eku, mus\u00ed ma\u0165 od 3 do 16 znakov.", + "invalid_key_format": "K\u013e\u00fa\u010d nie je hexadecim\u00e1lna hodnota, pou\u017e\u00edvajte len 0-9 a A-F.", + "invalid_key_length": "K\u013e\u00fa\u010d nem\u00e1 spr\u00e1vnu d\u013a\u017eku, mus\u00ed ma\u0165 16, 24 alebo 32 hexadecim\u00e1lnych znakov.", "invalid_ping": "Interval pingu mus\u00ed by\u0165 medzi 1 a 1440 min\u00fatami." }, "step": { "additional_account": { + "data": { + "ping_interval": "Ping Interval (min)" + }, "title": "Pridajte \u010fal\u0161\u00ed \u00fa\u010det k aktu\u00e1lnemu portu." }, "user": { "data": { + "account": "ID \u00fa\u010dtu", + "ping_interval": "Ping Interval (min)", "port": "Port", "protocol": "Protokol" } diff --git a/homeassistant/components/skybell/translations/sk.json b/homeassistant/components/skybell/translations/sk.json index 55eb9050870..e9eacedcc19 100644 --- a/homeassistant/components/skybell/translations/sk.json +++ b/homeassistant/components/skybell/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, + "error": { + "invalid_auth": "Neplatn\u00e9 overenie" + }, "step": { "reauth_confirm": { "data": { @@ -12,6 +15,7 @@ }, "user": { "data": { + "email": "Email", "password": "Heslo" } } diff --git a/homeassistant/components/slack/translations/sk.json b/homeassistant/components/slack/translations/sk.json index a4674cfbe37..cb3801e953b 100644 --- a/homeassistant/components/slack/translations/sk.json +++ b/homeassistant/components/slack/translations/sk.json @@ -5,6 +5,7 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { diff --git a/homeassistant/components/smappee/translations/sk.json b/homeassistant/components/smappee/translations/sk.json index f91e0502cc3..3891c37bb35 100644 --- a/homeassistant/components/smappee/translations/sk.json +++ b/homeassistant/components/smappee/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "invalid_mdns": "Nepodporovan\u00e9 zariadenie pre integr\u00e1ciu Smappee." }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/smartthings/translations/sk.json b/homeassistant/components/smartthings/translations/sk.json index 534c1e859ee..0f2b3e1f616 100644 --- a/homeassistant/components/smartthings/translations/sk.json +++ b/homeassistant/components/smartthings/translations/sk.json @@ -10,6 +10,9 @@ "data": { "location_id": "Umiestnenie" } + }, + "user": { + "title": "Potvr\u010fte URL sp\u00e4tn\u00e9ho volania" } } } diff --git a/homeassistant/components/sms/translations/sk.json b/homeassistant/components/sms/translations/sk.json index 2a411e71b3f..2978b5d4fa3 100644 --- a/homeassistant/components/sms/translations/sk.json +++ b/homeassistant/components/sms/translations/sk.json @@ -1,13 +1,19 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { "device": "Zariadenie" - } + }, + "title": "Pripojte sa k modemu" } } } diff --git a/homeassistant/components/snooz/translations/sk.json b/homeassistant/components/snooz/translations/sk.json index a8fa63be59a..b121bbc35a3 100644 --- a/homeassistant/components/snooz/translations/sk.json +++ b/homeassistant/components/snooz/translations/sk.json @@ -5,11 +5,16 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/solaredge/translations/sk.json b/homeassistant/components/solaredge/translations/sk.json index 8d2e3cff838..f960764ffba 100644 --- a/homeassistant/components/solaredge/translations/sk.json +++ b/homeassistant/components/solaredge/translations/sk.json @@ -5,6 +5,7 @@ }, "error": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "could_not_connect": "Nepodarilo sa pripoji\u0165 k rozhraniu solaredge API", "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" }, "step": { diff --git a/homeassistant/components/soma/translations/sk.json b/homeassistant/components/soma/translations/sk.json index aa78c3496ee..722e55b6294 100644 --- a/homeassistant/components/soma/translations/sk.json +++ b/homeassistant/components/soma/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_setup": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + "already_setup": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" diff --git a/homeassistant/components/somfy/translations/sk.json b/homeassistant/components/somfy/translations/sk.json index c19b1a0b70c..91803ae5155 100644 --- a/homeassistant/components/somfy/translations/sk.json +++ b/homeassistant/components/somfy/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." + }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" } diff --git a/homeassistant/components/somfy_mylink/translations/sk.json b/homeassistant/components/somfy_mylink/translations/sk.json index 14225502e94..cd214d8dac2 100644 --- a/homeassistant/components/somfy_mylink/translations/sk.json +++ b/homeassistant/components/somfy_mylink/translations/sk.json @@ -6,6 +6,7 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{mac} ({ip})", "step": { "user": { "data": { diff --git a/homeassistant/components/sonarr/translations/sk.json b/homeassistant/components/sonarr/translations/sk.json index 0ccb181be0e..5c8678ab195 100644 --- a/homeassistant/components/sonarr/translations/sk.json +++ b/homeassistant/components/sonarr/translations/sk.json @@ -11,7 +11,17 @@ "step": { "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" + "api_key": "API k\u013e\u00fa\u010d", + "url": "URL" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "wanted_max_items": "Maxim\u00e1lny po\u010det po\u017eadovan\u00fdch polo\u017eiek na zobrazenie" } } } diff --git a/homeassistant/components/speedtestdotnet/translations/sk.json b/homeassistant/components/speedtestdotnet/translations/sk.json index 838b602b6df..c96ae922bbb 100644 --- a/homeassistant/components/speedtestdotnet/translations/sk.json +++ b/homeassistant/components/speedtestdotnet/translations/sk.json @@ -1,4 +1,11 @@ { + "config": { + "step": { + "user": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/spotify/translations/sk.json b/homeassistant/components/spotify/translations/sk.json index 63ae49fe727..bd8a83c2e34 100644 --- a/homeassistant/components/spotify/translations/sk.json +++ b/homeassistant/components/spotify/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." + }, "step": { "pick_implementation": { "title": "Vyberte met\u00f3du overenia" diff --git a/homeassistant/components/sql/translations/sk.json b/homeassistant/components/sql/translations/sk.json index 58b490fa9f0..115ad51719c 100644 --- a/homeassistant/components/sql/translations/sk.json +++ b/homeassistant/components/sql/translations/sk.json @@ -3,24 +3,36 @@ "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, + "error": { + "db_url_invalid": "Adresa URL datab\u00e1zy je neplatn\u00e1", + "query_invalid": "SQL neplatn\u00e1 po\u017eiadavka" + }, "step": { "user": { "data": { + "db_url": "URL datab\u00e1zy", "unit_of_measurement": "Mern\u00e1 jednotka" }, "data_description": { + "db_url": "Adresa URL datab\u00e1zy, nechajte pr\u00e1zdnu, ak chcete pou\u017ei\u0165 predvolen\u00fa datab\u00e1zu HA", "unit_of_measurement": "Jednotka miery (volite\u013en\u00e9)" } } } }, "options": { + "error": { + "db_url_invalid": "Adresa URL datab\u00e1zy je neplatn\u00e1", + "query_invalid": "SQL neplatn\u00e1 po\u017eiadavka" + }, "step": { "init": { "data": { + "db_url": "URL datab\u00e1zy", "unit_of_measurement": "Mern\u00e1 jednotka" }, "data_description": { + "db_url": "Adresa URL datab\u00e1zy, nechajte pr\u00e1zdnu, ak chcete pou\u017ei\u0165 predvolen\u00fa datab\u00e1zu HA", "unit_of_measurement": "Jednotka miery (volite\u013en\u00e9)" } } diff --git a/homeassistant/components/srp_energy/translations/sk.json b/homeassistant/components/srp_energy/translations/sk.json index 1b1e671c054..94d7b1fcdc6 100644 --- a/homeassistant/components/srp_energy/translations/sk.json +++ b/homeassistant/components/srp_energy/translations/sk.json @@ -1,11 +1,13 @@ { "config": { "error": { + "invalid_account": "ID \u00fa\u010dtu by malo by\u0165 9-miestne \u010d\u00edslo", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { "user": { "data": { + "id": "ID \u00fa\u010dtu", "password": "Heslo" } } diff --git a/homeassistant/components/starline/translations/sk.json b/homeassistant/components/starline/translations/sk.json index fef79e7eebe..de6f0da2dc8 100644 --- a/homeassistant/components/starline/translations/sk.json +++ b/homeassistant/components/starline/translations/sk.json @@ -4,6 +4,16 @@ "error_auth_user": "Nespr\u00e1vne u\u017e\u00edvate\u013esk\u00e9 meno alebo heslo" }, "step": { + "auth_app": { + "data": { + "app_id": "ID aplik\u00e1cie" + } + }, + "auth_mfa": { + "data": { + "mfa_code": "SMS k\u00f3d" + } + }, "auth_user": { "data": { "password": "Heslo" diff --git a/homeassistant/components/steam_online/translations/sk.json b/homeassistant/components/steam_online/translations/sk.json index f04d4a327f4..63d3be9f5ed 100644 --- a/homeassistant/components/steam_online/translations/sk.json +++ b/homeassistant/components/steam_online/translations/sk.json @@ -2,6 +2,19 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "invalid_account": "Neplatn\u00e9 ID \u00fa\u010dtu", + "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "account": "ID \u00fa\u010dtu Steam", + "api_key": "API k\u013e\u00fa\u010d" + }, + "description": "Na vyh\u013eadanie ID \u00fa\u010dtu Steam pou\u017eite {account_id_url}" + } } } } \ No newline at end of file diff --git a/homeassistant/components/subaru/translations/sk.json b/homeassistant/components/subaru/translations/sk.json index d50ce04d74a..7d6292fcc12 100644 --- a/homeassistant/components/subaru/translations/sk.json +++ b/homeassistant/components/subaru/translations/sk.json @@ -4,9 +4,24 @@ "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, "error": { + "bad_pin_format": "PIN by mal ma\u0165 4 \u010d\u00edslice", + "bad_validation_code_format": "Overovac\u00ed k\u00f3d by mal ma\u0165 6 \u010d\u00edslic", + "incorrect_pin": "Nespr\u00e1vny PIN", + "incorrect_validation_code": "Nespr\u00e1vny overovac\u00ed k\u00f3d", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "pin": { + "data": { + "pin": "PIN" + } + }, + "two_factor_validate": { + "data": { + "validation_code": "Overovac\u00ed k\u00f3d" + }, + "description": "Zadajte prijat\u00fd overovac\u00ed k\u00f3d" + }, "user": { "data": { "password": "Heslo" diff --git a/homeassistant/components/sun/translations/sk.json b/homeassistant/components/sun/translations/sk.json index a5bc4d5339e..b46baf83a1e 100644 --- a/homeassistant/components/sun/translations/sk.json +++ b/homeassistant/components/sun/translations/sk.json @@ -1,4 +1,11 @@ { + "config": { + "step": { + "user": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } + } + }, "state": { "_": { "above_horizon": "Nad horizontom", diff --git a/homeassistant/components/switch_as_x/translations/sk.json b/homeassistant/components/switch_as_x/translations/sk.json new file mode 100644 index 00000000000..ce94aced1f9 --- /dev/null +++ b/homeassistant/components/switch_as_x/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_id": "Prep\u00edna\u010d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/sv.json b/homeassistant/components/switch_as_x/translations/sv.json index 9ff8e548255..ed9da614178 100644 --- a/homeassistant/components/switch_as_x/translations/sv.json +++ b/homeassistant/components/switch_as_x/translations/sv.json @@ -10,5 +10,5 @@ } } }, - "title": "\u00c4ndra enhetstyp f\u00f6r en c" + "title": "\u00c4ndra enhetstyp f\u00f6r en brytare" } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/sk.json b/homeassistant/components/syncthing/translations/sk.json index ad320852cf5..644bf0f55e1 100644 --- a/homeassistant/components/syncthing/translations/sk.json +++ b/homeassistant/components/syncthing/translations/sk.json @@ -5,6 +5,13 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "url": "URL" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/syncthru/translations/sk.json b/homeassistant/components/syncthru/translations/sk.json index ef73b0f8d70..70a9f36e83f 100644 --- a/homeassistant/components/syncthru/translations/sk.json +++ b/homeassistant/components/syncthru/translations/sk.json @@ -3,16 +3,22 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "invalid_url": "Neplatn\u00e1 adresa URL", + "unknown_state": "Nezn\u00e1my stav tla\u010diarne, overte URL a sie\u0165ov\u00e9 pripojenie" + }, "flow_title": "{name}", "step": { "confirm": { "data": { - "name": "N\u00e1zov" + "name": "N\u00e1zov", + "url": "URL webov\u00e9ho rozhrania" } }, "user": { "data": { - "name": "N\u00e1zov" + "name": "N\u00e1zov", + "url": "URL webov\u00e9ho rozhrania" } } } diff --git a/homeassistant/components/system_bridge/translations/sk.json b/homeassistant/components/system_bridge/translations/sk.json index 70d5bcbd97d..97b3c8c2677 100644 --- a/homeassistant/components/system_bridge/translations/sk.json +++ b/homeassistant/components/system_bridge/translations/sk.json @@ -2,9 +2,11 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, @@ -20,7 +22,8 @@ "api_key": "API k\u013e\u00fa\u010d", "host": "Hostite\u013e", "port": "Port" - } + }, + "description": "Zadajte podrobnosti o pripojen\u00ed." } } } diff --git a/homeassistant/components/tankerkoenig/translations/sk.json b/homeassistant/components/tankerkoenig/translations/sk.json index 2c694e299a7..52934315c68 100644 --- a/homeassistant/components/tankerkoenig/translations/sk.json +++ b/homeassistant/components/tankerkoenig/translations/sk.json @@ -3,9 +3,18 @@ "abort": { "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "invalid_auth": "Neplatn\u00e9 overenie" + }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + } + }, "user": { "data": { + "api_key": "API k\u013e\u00fa\u010d", "radius": "Polomer vyh\u013ead\u00e1vania" } } diff --git a/homeassistant/components/tautulli/translations/sk.json b/homeassistant/components/tautulli/translations/sk.json index f04d4a327f4..9cb2290463a 100644 --- a/homeassistant/components/tautulli/translations/sk.json +++ b/homeassistant/components/tautulli/translations/sk.json @@ -2,6 +2,22 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "error": { + "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + } + }, + "user": { + "data": { + "api_key": "API k\u013e\u00fa\u010d", + "url": "URL" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/tellduslive/translations/sk.json b/homeassistant/components/tellduslive/translations/sk.json index 1b2bf27f1fc..e0af50871da 100644 --- a/homeassistant/components/tellduslive/translations/sk.json +++ b/homeassistant/components/tellduslive/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "unknown_authorize_url_generation": "Nezn\u00e1ma chyba pri generovan\u00ed autorizovanej adresy URL." }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" @@ -10,7 +12,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Pr\u00e1zdne" } } } diff --git a/homeassistant/components/thermobeacon/translations/sk.json b/homeassistant/components/thermobeacon/translations/sk.json index 48a6116c8ee..8273d877c92 100644 --- a/homeassistant/components/thermobeacon/translations/sk.json +++ b/homeassistant/components/thermobeacon/translations/sk.json @@ -6,11 +6,16 @@ "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "not_supported": "Zariadenie nie je podporovan\u00e9" }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/thermopro/translations/sk.json b/homeassistant/components/thermopro/translations/sk.json index a8fa63be59a..b121bbc35a3 100644 --- a/homeassistant/components/thermopro/translations/sk.json +++ b/homeassistant/components/thermopro/translations/sk.json @@ -5,11 +5,16 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/threshold/translations/sk.json b/homeassistant/components/threshold/translations/sk.json new file mode 100644 index 00000000000..fcfc77b7cf2 --- /dev/null +++ b/homeassistant/components/threshold/translations/sk.json @@ -0,0 +1,20 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_id": "Vstupn\u00fd sn\u00edma\u010d" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_id": "Vstupn\u00fd sn\u00edma\u010d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tibber/translations/sk.json b/homeassistant/components/tibber/translations/sk.json index 06bc63983d0..d9a0b7c1426 100644 --- a/homeassistant/components/tibber/translations/sk.json +++ b/homeassistant/components/tibber/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" }, + "error": { + "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/tilt_ble/translations/sk.json b/homeassistant/components/tilt_ble/translations/sk.json index a8fa63be59a..b121bbc35a3 100644 --- a/homeassistant/components/tilt_ble/translations/sk.json +++ b/homeassistant/components/tilt_ble/translations/sk.json @@ -5,11 +5,16 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "flow_title": "{name}", "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, "user": { "data": { "address": "Zaradenie" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" } } } diff --git a/homeassistant/components/tolo/translations/sk.json b/homeassistant/components/tolo/translations/sk.json index 8430e2bd6f3..9a90be4aa45 100644 --- a/homeassistant/components/tolo/translations/sk.json +++ b/homeassistant/components/tolo/translations/sk.json @@ -5,6 +5,9 @@ }, "flow_title": "{name}", "step": { + "confirm": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/tomorrowio/translations/sensor.sk.json b/homeassistant/components/tomorrowio/translations/sensor.sk.json index b4772750e4e..45600dcb861 100644 --- a/homeassistant/components/tomorrowio/translations/sensor.sk.json +++ b/homeassistant/components/tomorrowio/translations/sensor.sk.json @@ -4,6 +4,12 @@ "high": "Vysok\u00fd", "low": "N\u00edzka", "medium": "Stredn\u00fd" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "Mrzn\u00faci d\u00e1\u017e\u010f", + "ice_pellets": "\u013dadovec", + "rain": "D\u00e1\u017e\u010f", + "snow": "Sneh" } } } \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sk.json b/homeassistant/components/tomorrowio/translations/sk.json index 2c20494750b..adcf697b1c2 100644 --- a/homeassistant/components/tomorrowio/translations/sk.json +++ b/homeassistant/components/tomorrowio/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "error": { + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", "rate_limited": "Moment\u00e1lne je r\u00fdchlos\u0165 obmedzen\u00e1, sk\u00faste to pros\u00edm nesk\u00f4r." }, "step": { @@ -8,7 +9,8 @@ "data": { "api_key": "API k\u013e\u00fa\u010d", "name": "Meno" - } + }, + "description": "Ak chcete z\u00edska\u0165 k\u013e\u00fa\u010d API, zaregistrujte sa na [Tomorrow.io] (https://app.tomorrow.io/signup)." } } } diff --git a/homeassistant/components/toon/translations/sk.json b/homeassistant/components/toon/translations/sk.json index c460d0a408b..e5fd9d02aff 100644 --- a/homeassistant/components/toon/translations/sk.json +++ b/homeassistant/components/toon/translations/sk.json @@ -1,7 +1,10 @@ { "config": { "abort": { - "already_configured": "Vybran\u00e1 dohoda je u\u017e nakonfigurovan\u00e1." + "already_configured": "Vybran\u00e1 dohoda je u\u017e nakonfigurovan\u00e1.", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", + "unknown_authorize_url_generation": "Nezn\u00e1ma chyba pri generovan\u00ed autorizovanej adresy URL." }, "step": { "agreement": { diff --git a/homeassistant/components/trafikverket_ferry/translations/sk.json b/homeassistant/components/trafikverket_ferry/translations/sk.json index d6524c4eff8..c6af795c788 100644 --- a/homeassistant/components/trafikverket_ferry/translations/sk.json +++ b/homeassistant/components/trafikverket_ferry/translations/sk.json @@ -3,9 +3,19 @@ "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, + "error": { + "incorrect_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d pre vybran\u00fd \u00fa\u010det", + "invalid_auth": "Neplatn\u00e9 overenie" + }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + } + }, "user": { "data": { + "api_key": "API k\u013e\u00fa\u010d", "weekday": "Pracovn\u00e9 dni" } } diff --git a/homeassistant/components/trafikverket_train/translations/sk.json b/homeassistant/components/trafikverket_train/translations/sk.json index af85174f93a..4d320031844 100644 --- a/homeassistant/components/trafikverket_train/translations/sk.json +++ b/homeassistant/components/trafikverket_train/translations/sk.json @@ -3,9 +3,18 @@ "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, + "error": { + "incorrect_api_key": "Neplatn\u00fd k\u013e\u00fa\u010d API pre vybran\u00e9 konto" + }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + } + }, "user": { "data": { + "api_key": "API k\u013e\u00fa\u010d", "weekday": "Dni" } } diff --git a/homeassistant/components/transmission/translations/sk.json b/homeassistant/components/transmission/translations/sk.json index 6ff2f8b99a3..4e8f7da9460 100644 --- a/homeassistant/components/transmission/translations/sk.json +++ b/homeassistant/components/transmission/translations/sk.json @@ -11,7 +11,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Heslo pre {username} je neplatn\u00e9." }, "user": { "data": { diff --git a/homeassistant/components/tuya/translations/select.sk.json b/homeassistant/components/tuya/translations/select.sk.json index af7ccf4fcdb..fe62d98b32a 100644 --- a/homeassistant/components/tuya/translations/select.sk.json +++ b/homeassistant/components/tuya/translations/select.sk.json @@ -14,11 +14,17 @@ "tuya__fingerbot_mode": { "switch": "Prep\u00edna\u010d" }, + "tuya__humidifier_spray_mode": { + "humidity": "Vlhkos\u0165" + }, "tuya__motion_sensitivity": { "0": "N\u00edzka citlivos\u0165", "1": "Stredn\u00e1 citlivos\u0165", "2": "Vysok\u00e1 citlivos\u0165" }, + "tuya__relay_status": { + "memory": "Zapam\u00e4ta\u0165 posledn\u00fd stav" + }, "tuya__vacuum_mode": { "random": "N\u00e1hodn\u00fd" } diff --git a/homeassistant/components/tuya/translations/sk.json b/homeassistant/components/tuya/translations/sk.json index 90cb016cf9a..267c2bef955 100644 --- a/homeassistant/components/tuya/translations/sk.json +++ b/homeassistant/components/tuya/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "login_error": "Chyba pri prihlasovan\u00ed ({code}): {msg}" }, "step": { "user": { diff --git a/homeassistant/components/twilio/translations/sk.json b/homeassistant/components/twilio/translations/sk.json new file mode 100644 index 00000000000..473cb1c2fa0 --- /dev/null +++ b/homeassistant/components/twilio/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sk.json b/homeassistant/components/unifiprotect/translations/sk.json index d833c47005e..7e6baec983a 100644 --- a/homeassistant/components/unifiprotect/translations/sk.json +++ b/homeassistant/components/unifiprotect/translations/sk.json @@ -16,14 +16,26 @@ "reauth_confirm": { "data": { "password": "Heslo", - "port": "Port" + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "user": { "data": { "host": "Hostite\u013e", "password": "Heslo", - "port": "Port" + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "all_updates": "Metriky v re\u00e1lnom \u010dase (UPOZORNENIE: V\u00fdrazne zvy\u0161uje vyu\u017eitie procesora)" } } } diff --git a/homeassistant/components/uptime/translations/sk.json b/homeassistant/components/uptime/translations/sk.json new file mode 100644 index 00000000000..473cb1c2fa0 --- /dev/null +++ b/homeassistant/components/uptime/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/sk.json b/homeassistant/components/uptimerobot/translations/sk.json index af59a66b0d6..0ba01a3cf33 100644 --- a/homeassistant/components/uptimerobot/translations/sk.json +++ b/homeassistant/components/uptimerobot/translations/sk.json @@ -5,7 +5,8 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", + "not_main_key": "Bol zisten\u00fd nespr\u00e1vny typ k\u013e\u00fa\u010da API, pou\u017eite \u201emain\u201c k\u013e\u00fa\u010d API" }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/venstar/translations/sk.json b/homeassistant/components/venstar/translations/sk.json index c59db6e13d4..a7981d1ae40 100644 --- a/homeassistant/components/venstar/translations/sk.json +++ b/homeassistant/components/venstar/translations/sk.json @@ -7,7 +7,8 @@ "user": { "data": { "host": "Hostite\u013e", - "password": "Heslo" + "password": "Heslo", + "pin": "PIN k\u00f3d" } } } diff --git a/homeassistant/components/vera/translations/sk.json b/homeassistant/components/vera/translations/sk.json new file mode 100644 index 00000000000..23f69af3665 --- /dev/null +++ b/homeassistant/components/vera/translations/sk.json @@ -0,0 +1,17 @@ +{ + "config": { + "abort": { + "cannot_connect": "Nepodarilo sa pripoji\u0165 k kontrol\u00e9ru s adresou URL {base_url}" + }, + "step": { + "user": { + "data": { + "vera_controller_url": "URL kontrol\u00e9ra" + }, + "data_description": { + "vera_controller_url": "Malo by to vyzera\u0165 takto: http://192.168.1.161:3480" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vicare/translations/sk.json b/homeassistant/components/vicare/translations/sk.json index 00b93b9ac57..1f3e08e82e2 100644 --- a/homeassistant/components/vicare/translations/sk.json +++ b/homeassistant/components/vicare/translations/sk.json @@ -10,7 +10,8 @@ "client_id": "API k\u013e\u00fa\u010d", "password": "Heslo", "username": "Email" - } + }, + "description": "Nastavte integr\u00e1ciu ViCare. Ak chcete vygenerova\u0165 k\u013e\u00fa\u010d API, prejdite na https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vizio/translations/sk.json b/homeassistant/components/vizio/translations/sk.json index 2b36ca0550c..0fa210995d9 100644 --- a/homeassistant/components/vizio/translations/sk.json +++ b/homeassistant/components/vizio/translations/sk.json @@ -4,6 +4,11 @@ "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "step": { + "pair_tv": { + "data": { + "pin": "PIN k\u00f3d" + } + }, "user": { "data": { "access_token": "Pr\u00edstupov\u00fd token", diff --git a/homeassistant/components/volvooncall/translations/sk.json b/homeassistant/components/volvooncall/translations/sk.json index c04d2dbe1d3..d50ce04d74a 100644 --- a/homeassistant/components/volvooncall/translations/sk.json +++ b/homeassistant/components/volvooncall/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, + "error": { + "invalid_auth": "Neplatn\u00e9 overenie" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/vulcan/translations/sk.json b/homeassistant/components/vulcan/translations/sk.json index ec44fa4db21..a1053c17ee5 100644 --- a/homeassistant/components/vulcan/translations/sk.json +++ b/homeassistant/components/vulcan/translations/sk.json @@ -5,12 +5,26 @@ "already_configured": "Tento \u0161tudent u\u017e bol pridan\u00fd." }, "error": { + "expired_token": "Platnos\u0165 tokenu vypr\u0161ala \u2013 vygenerujte si nov\u00fd token", + "invalid_pin": "Neplatn\u00fd k\u00f3d PIN", "invalid_token": "Neplatn\u00fd token", "unknown": "Vyskytla sa nezn\u00e1ma chyba" }, "step": { "add_next_config_entry": { "description": "Prida\u0165 \u010fal\u0161ieho \u0161tudenta." + }, + "auth": { + "data": { + "pin": "Pin", + "token": "Token" + } + }, + "reauth_confirm": { + "data": { + "pin": "Pin", + "token": "Token" + } } } } diff --git a/homeassistant/components/watttime/translations/sk.json b/homeassistant/components/watttime/translations/sk.json index b8a538e476e..c95e5bab874 100644 --- a/homeassistant/components/watttime/translations/sk.json +++ b/homeassistant/components/watttime/translations/sk.json @@ -24,12 +24,14 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Znova zadajte heslo pre {username}:" }, "user": { "data": { "password": "Heslo" - } + }, + "description": "Zadajte pou\u017e\u00edvate\u013esk\u00e9 meno a heslo:" } } } diff --git a/homeassistant/components/whois/translations/sk.json b/homeassistant/components/whois/translations/sk.json index 804590e7c19..3ad01750f60 100644 --- a/homeassistant/components/whois/translations/sk.json +++ b/homeassistant/components/whois/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Service is already configured" }, + "error": { + "unexpected_response": "Neo\u010dak\u00e1van\u00e1 odpove\u010f zo servera whois" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/withings/translations/sk.json b/homeassistant/components/withings/translations/sk.json index 1a45e4d6196..adfad5b6b97 100644 --- a/homeassistant/components/withings/translations/sk.json +++ b/homeassistant/components/withings/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Konfigur\u00e1cia profilu bola aktualizovan\u00e1." + "already_configured": "Konfigur\u00e1cia profilu bola aktualizovan\u00e1.", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." }, "error": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" diff --git a/homeassistant/components/wled/translations/select.sk.json b/homeassistant/components/wled/translations/select.sk.json index fa75e69abe9..b7f43750f16 100644 --- a/homeassistant/components/wled/translations/select.sk.json +++ b/homeassistant/components/wled/translations/select.sk.json @@ -1,6 +1,8 @@ { "state": { "wled__live_override": { + "0": "Neakt\u00edvny", + "1": "Akt\u00edvny", "2": "K\u00fdm sa zariadenie nere\u0161tartuje" } } diff --git a/homeassistant/components/wolflink/translations/sensor.sk.json b/homeassistant/components/wolflink/translations/sensor.sk.json index dae86d4ec26..5ce49fcc5d6 100644 --- a/homeassistant/components/wolflink/translations/sensor.sk.json +++ b/homeassistant/components/wolflink/translations/sensor.sk.json @@ -9,7 +9,8 @@ "gasdruck": "Tlak plynu", "heizung": "Vykurovanie", "initialisierung": "Inicializ\u00e1cia", - "kalibration": "Kalibr\u00e1cia" + "kalibration": "Kalibr\u00e1cia", + "urlaubsmodus": "Dovolenkov\u00fd re\u017eim" } } } \ No newline at end of file diff --git a/homeassistant/components/xbox/translations/sk.json b/homeassistant/components/xbox/translations/sk.json index c19b1a0b70c..91803ae5155 100644 --- a/homeassistant/components/xbox/translations/sk.json +++ b/homeassistant/components/xbox/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." + }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" } diff --git a/homeassistant/components/xiaomi_aqara/translations/sk.json b/homeassistant/components/xiaomi_aqara/translations/sk.json index f5e89b6c809..842d0ddd655 100644 --- a/homeassistant/components/xiaomi_aqara/translations/sk.json +++ b/homeassistant/components/xiaomi_aqara/translations/sk.json @@ -5,14 +5,22 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "error": { + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa , pozrite si https://www.home-assistant.io/integrations/xiaomi_aqara/#connection-problem", "invalid_interface": "Neplatn\u00e9 sie\u0165ov\u00e9 rozhranie", + "invalid_key": "Neplatn\u00fd k\u013e\u00fa\u010d br\u00e1ny", "invalid_mac": "Neplatn\u00e1 adresa Mac" }, "flow_title": "{name}", "step": { + "select": { + "data": { + "select_ip": "IP adresa" + } + }, "user": { "data": { - "host": "IP adresa (volite\u013en\u00e1)" + "host": "IP adresa (volite\u013en\u00e1)", + "mac": "Adresa Mac (volite\u013en\u00e9)" } } } diff --git a/homeassistant/components/xiaomi_ble/translations/sk.json b/homeassistant/components/xiaomi_ble/translations/sk.json index 92ca31d6c47..b121bbc35a3 100644 --- a/homeassistant/components/xiaomi_ble/translations/sk.json +++ b/homeassistant/components/xiaomi_ble/translations/sk.json @@ -4,6 +4,18 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "flow_title": "{name}", + "step": { + "bluetooth_confirm": { + "description": "Chcete nastavi\u0165 {name}?" + }, + "user": { + "data": { + "address": "Zaradenie" + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nastavi\u0165" + } } } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/sk.json b/homeassistant/components/xiaomi_miio/translations/sk.json index 3eebf9ec1a9..94cb4db8af6 100644 --- a/homeassistant/components/xiaomi_miio/translations/sk.json +++ b/homeassistant/components/xiaomi_miio/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "incomplete_info": "Ne\u00fapln\u00e9 inform\u00e1cie na nastavenie zariadenia, \u017eiadny hostite\u013e alebo token.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, @@ -13,6 +14,11 @@ }, "flow_title": "{name}", "step": { + "cloud": { + "data": { + "cloud_password": "Heslo do cloudu" + } + }, "connect": { "data": { "model": "Model zariadenia" diff --git a/homeassistant/components/yale_smart_alarm/translations/sk.json b/homeassistant/components/yale_smart_alarm/translations/sk.json index a0da00adc5d..3d43eb45fac 100644 --- a/homeassistant/components/yale_smart_alarm/translations/sk.json +++ b/homeassistant/components/yale_smart_alarm/translations/sk.json @@ -26,6 +26,13 @@ "options": { "error": { "code_format_mismatch": "K\u00f3d nezodpoved\u00e1 po\u017eadovan\u00e9mu po\u010dtu \u010d\u00edslic" + }, + "step": { + "init": { + "data": { + "lock_code_digits": "Po\u010det \u010d\u00edslic v PIN k\u00f3de pre z\u00e1mky" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/yalexs_ble/translations/sk.json b/homeassistant/components/yalexs_ble/translations/sk.json index 3fc88152de7..09746a52f33 100644 --- a/homeassistant/components/yalexs_ble/translations/sk.json +++ b/homeassistant/components/yalexs_ble/translations/sk.json @@ -9,12 +9,19 @@ "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_key_format": "Offline k\u013e\u00fa\u010d mus\u00ed by\u0165 32-bajtov\u00fd hexadecim\u00e1lny re\u0165azec.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name}", "step": { "integration_discovery_confirm": { "description": "Chcete nastavi\u0165 {name} cez Bluetooth s adresou {address}?" + }, + "user": { + "data": { + "address": "Adresa Bluetooth", + "key": "Offline k\u013e\u00fa\u010d (32-bajtov\u00fd hexadecim\u00e1lny re\u0165azec)" + } } } } diff --git a/homeassistant/components/yamaha_musiccast/translations/sk.json b/homeassistant/components/yamaha_musiccast/translations/sk.json index 704da465e48..94a606fc1fe 100644 --- a/homeassistant/components/yamaha_musiccast/translations/sk.json +++ b/homeassistant/components/yamaha_musiccast/translations/sk.json @@ -1,9 +1,13 @@ { "config": { "abort": { - "already_configured": "Zariadenie je u\u017e nakonfigurovan\u00e9" + "already_configured": "Zariadenie je u\u017e nakonfigurovan\u00e9", + "yxc_control_url_missing": "Riadiaca adresa URL nie je uveden\u00e1 v popise ssdp." }, "step": { + "confirm": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/yolink/translations/sk.json b/homeassistant/components/yolink/translations/sk.json index c3bed1eca3b..2707fadc8e2 100644 --- a/homeassistant/components/yolink/translations/sk.json +++ b/homeassistant/components/yolink/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu." } } diff --git a/homeassistant/components/zerproc/translations/sk.json b/homeassistant/components/zerproc/translations/sk.json index 69d8d942494..8183bb46360 100644 --- a/homeassistant/components/zerproc/translations/sk.json +++ b/homeassistant/components/zerproc/translations/sk.json @@ -2,6 +2,11 @@ "config": { "abort": { "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "step": { + "confirm": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/zha/translations/sk.json b/homeassistant/components/zha/translations/sk.json index 4f482e38d06..6ed98fcd717 100644 --- a/homeassistant/components/zha/translations/sk.json +++ b/homeassistant/components/zha/translations/sk.json @@ -23,6 +23,9 @@ "button_4": "\u0160tvrt\u00e9 tla\u010didlo", "button_5": "Piate tla\u010didlo", "button_6": "\u0160ieste tla\u010didlo" + }, + "trigger_type": { + "device_offline": "Zariadenie je offline" } }, "options": { diff --git a/homeassistant/components/zoneminder/translations/sk.json b/homeassistant/components/zoneminder/translations/sk.json index 66b0ba88923..6855108262b 100644 --- a/homeassistant/components/zoneminder/translations/sk.json +++ b/homeassistant/components/zoneminder/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "auth_fail": "U\u017eivate\u013esk\u00e9 meno alebo heslo je nespr\u00e1vne.", "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/zwave_js/translations/sk.json b/homeassistant/components/zwave_js/translations/sk.json index 10bbe0adb94..38ba6565be8 100644 --- a/homeassistant/components/zwave_js/translations/sk.json +++ b/homeassistant/components/zwave_js/translations/sk.json @@ -7,9 +7,22 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_ws_url": "Neplatn\u00e1 adresa URL webov\u00e9ho soketu", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, - "flow_title": "{name}" + "flow_title": "{name}", + "step": { + "configure_addon": { + "data": { + "usb_path": "Cesta k zariadeniu USB" + } + }, + "manual": { + "data": { + "url": "URL" + } + } + } }, "device_automation": { "action_type": { @@ -27,7 +40,21 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_ws_url": "Neplatn\u00e1 adresa URL webov\u00e9ho soketu", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "configure_addon": { + "data": { + "log_level": "\u00darove\u0148 denn\u00edka", + "usb_path": "Cesta k zariadeniu USB" + } + }, + "manual": { + "data": { + "url": "URL" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/zwave_me/translations/sk.json b/homeassistant/components/zwave_me/translations/sk.json index 793f8eff278..badd53ca8a7 100644 --- a/homeassistant/components/zwave_me/translations/sk.json +++ b/homeassistant/components/zwave_me/translations/sk.json @@ -2,6 +2,14 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "step": { + "user": { + "data": { + "token": "API token", + "url": "URL" + } + } } } } \ No newline at end of file From 367b5e586b9a7c6f5c9d33283a8e92b3873d6e52 Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Mon, 28 Nov 2022 02:48:35 +0100 Subject: [PATCH 0772/1033] Add support for HomeWizard identify feature (#82375) * Add support for Identify feature * Add tests for button * Use only identifiers for device_info * Update homeassistant/components/homewizard/button.py --- homeassistant/components/homewizard/button.py | 57 ++++++++++++++++++ homeassistant/components/homewizard/const.py | 2 +- tests/components/homewizard/test_button.py | 59 +++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/homewizard/button.py create mode 100644 tests/components/homewizard/test_button.py diff --git a/homeassistant/components/homewizard/button.py b/homeassistant/components/homewizard/button.py new file mode 100644 index 00000000000..4bcc5016dec --- /dev/null +++ b/homeassistant/components/homewizard/button.py @@ -0,0 +1,57 @@ +"""Support for HomeWizard buttons.""" + +import logging + +from homeassistant.components.button import ButtonEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN +from .coordinator import HWEnergyDeviceUpdateCoordinator + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up the Identify button.""" + coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + + features = await coordinator.api.features() + if features.has_identify: + async_add_entities([HomeWizardIdentifyButton(coordinator, entry)]) + + +class HomeWizardIdentifyButton( + CoordinatorEntity[HWEnergyDeviceUpdateCoordinator], ButtonEntity +): + """Representation of a identify button.""" + + _attr_has_entity_name = True + + def __init__( + self, + coordinator: HWEnergyDeviceUpdateCoordinator, + entry: ConfigEntry, + ) -> None: + """Initialize button.""" + super().__init__(coordinator) + self._attr_unique_id = f"{entry.unique_id}_identify" + self._attr_device_info = { + "name": entry.title, + "manufacturer": "HomeWizard", + "sw_version": coordinator.data["device"].firmware_version, + "model": coordinator.data["device"].product_type, + "identifiers": {(DOMAIN, coordinator.data["device"].serial)}, + } + self._attr_name = "Identify" + self._attr_icon = "mdi:magnify" + self._attr_entity_category = EntityCategory.DIAGNOSTIC + + async def async_press(self) -> None: + """Identify the device.""" + await self.coordinator.api.identify() diff --git a/homeassistant/components/homewizard/const.py b/homeassistant/components/homewizard/const.py index f1aba6ca17a..8e24bd19e44 100644 --- a/homeassistant/components/homewizard/const.py +++ b/homeassistant/components/homewizard/const.py @@ -10,7 +10,7 @@ from homewizard_energy.models import Data, Device, State, System from homeassistant.const import Platform DOMAIN = "homewizard" -PLATFORMS = [Platform.SENSOR, Platform.SWITCH, Platform.NUMBER] +PLATFORMS = [Platform.SENSOR, Platform.SWITCH, Platform.NUMBER, Platform.BUTTON] # Platform config. CONF_API_ENABLED = "api_enabled" diff --git a/tests/components/homewizard/test_button.py b/tests/components/homewizard/test_button.py new file mode 100644 index 00000000000..5d1481f4c1d --- /dev/null +++ b/tests/components/homewizard/test_button.py @@ -0,0 +1,59 @@ +"""Test the identify button for HomeWizard.""" +from unittest.mock import patch + +from homeassistant.const import ATTR_FRIENDLY_NAME +from homeassistant.helpers import entity_registry as er + +from .generator import get_mock_device + + +async def test_identify_button_entity_not_loaded_when_not_available( + hass, mock_config_entry_data, mock_config_entry +): + """Does not load button when device has no support for it.""" + + api = get_mock_device(product_type="HWE-P1") + + with patch( + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", + return_value=api, + ): + entry = mock_config_entry + entry.data = mock_config_entry_data + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get("button.product_name_aabbccddeeff_identify") is None + + +async def test_identify_button_is_loaded( + hass, mock_config_entry_data, mock_config_entry +): + """Loads button when device has support.""" + + api = get_mock_device(product_type="HWE-SKT", firmware_version="3.02") + + with patch( + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", + return_value=api, + ): + entry = mock_config_entry + entry.data = mock_config_entry_data + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get("button.product_name_aabbccddeeff_identify") + assert state + assert ( + state.attributes.get(ATTR_FRIENDLY_NAME) + == "Product Name (aabbccddeeff) Identify" + ) + + entity_registry = er.async_get(hass) + entry = entity_registry.async_get("button.product_name_aabbccddeeff_identify") + assert entry + assert entry.unique_id == "aabbccddeeff_identify" From d451a74c2d3bf8eadf0c2000337cc49a0a3ce6e1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 27 Nov 2022 16:17:35 -1000 Subject: [PATCH 0773/1033] Add a destruction check to the ESPHome bluetooth client (#82760) --- .../components/esphome/bluetooth/client.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index 66b687b6de9..ef3a6197a37 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -23,7 +23,7 @@ from bleak.backends.service import BleakGATTServiceCollection from bleak.exc import BleakError from homeassistant.components.bluetooth import async_scanner_by_source -from homeassistant.core import CALLBACK_TYPE +from homeassistant.core import CALLBACK_TYPE, HomeAssistant from ..domain_data import DomainData from .characteristic import BleakGATTCharacteristicESPHome @@ -123,7 +123,7 @@ class ESPHomeClient(BaseBleakClient): """Initialize the ESPHomeClient.""" assert isinstance(address_or_ble_device, BLEDevice) super().__init__(address_or_ble_device, *args, **kwargs) - self._hass = kwargs["hass"] + self._hass: HomeAssistant = kwargs["hass"] self._ble_device = address_or_ble_device self._address_as_int = mac_to_int(self._ble_device.address) assert self._ble_device.details is not None @@ -540,3 +540,15 @@ class ESPHomeClient(BaseBleakClient): # to be consistent with the behavior of the BlueZ backend if coro := self._notify_cancels.pop(characteristic.handle, None): await coro() + + def __del__(self) -> None: + """Destructor to make sure the connection state is unsubscribed.""" + if self._cancel_connection_state: + _LOGGER.warning( + "%s: %s - %s: ESPHomeClient bleak client was not properly disconnected before destruction", + self._source, + self._ble_device.name, + self._ble_device.address, + ) + if not self._hass.loop.is_closed(): + self._hass.loop.call_soon_threadsafe(self._unsubscribe_connection_state) From cb06f8b66882037e81ffee94afd7ccdb3969e338 Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Mon, 28 Nov 2022 07:03:51 +0100 Subject: [PATCH 0774/1033] KNX Config/OptionsFlow: minimize wait time for interface discovery (#81982) * Minimize wait time for interface discovery * fix OptionsFlow when connection failed there is no `self.hass.data["KNX"]` when we are in retry mode after failed integration setup * review changes * fix new tests --- homeassistant/components/knx/config_flow.py | 37 ++-- tests/components/knx/test_config_flow.py | 193 +++++++++++++------- 2 files changed, 149 insertions(+), 81 deletions(-) diff --git a/homeassistant/components/knx/config_flow.py b/homeassistant/components/knx/config_flow.py index e2ff0908bbe..c043ea65ee5 100644 --- a/homeassistant/components/knx/config_flow.py +++ b/homeassistant/components/knx/config_flow.py @@ -2,6 +2,7 @@ from __future__ import annotations from abc import ABC, abstractmethod +from collections.abc import AsyncGenerator from typing import Any, Final import voluptuous as vol @@ -95,6 +96,9 @@ class KNXCommonFlow(ABC, FlowHandler): self._found_tunnels: list[GatewayDescriptor] = [] self._selected_tunnel: GatewayDescriptor | None = None + self._gatewayscanner: GatewayScanner | None = None + self._async_scan_gen: AsyncGenerator[GatewayDescriptor, None] | None = None + @abstractmethod def finish_flow(self, title: str) -> FlowResult: """Finish the flow.""" @@ -104,6 +108,13 @@ class KNXCommonFlow(ABC, FlowHandler): ) -> FlowResult: """Handle connection type configuration.""" if user_input is not None: + if self._async_scan_gen: + await self._async_scan_gen.aclose() # stop the scan + self._async_scan_gen = None + if self._gatewayscanner: + self._found_gateways = list( + self._gatewayscanner.found_gateways.values() + ) connection_type = user_input[CONF_KNX_CONNECTION_TYPE] if connection_type == CONF_KNX_ROUTING: return await self.async_step_routing() @@ -129,8 +140,21 @@ class KNXCommonFlow(ABC, FlowHandler): CONF_KNX_TUNNELING: CONF_KNX_TUNNELING.capitalize(), CONF_KNX_ROUTING: CONF_KNX_ROUTING.capitalize(), } - self._found_gateways = await scan_for_gateways() - if self._found_gateways: + + if isinstance(self, OptionsFlow) and (knx_module := self.hass.data.get(DOMAIN)): + xknx = knx_module.xknx + else: + xknx = XKNX() + self._gatewayscanner = GatewayScanner( + xknx, stop_on_found=0, timeout_in_seconds=2 + ) + # keep a reference to the generator to scan in background until user selects a connection type + self._async_scan_gen = self._gatewayscanner.async_scan() + try: + await self._async_scan_gen.__anext__() # pylint: disable=unnecessary-dunder-call + except StopAsyncIteration: + pass # scan finished, no interfaces discovered + else: # add automatic at first position only if a gateway responded supported_connection_types = { CONF_KNX_AUTOMATIC: CONF_KNX_AUTOMATIC.capitalize() @@ -614,12 +638,3 @@ class KNXOptionsFlow(KNXCommonFlow, OptionsFlow): data_schema=vol.Schema(data_schema), last_step=True, ) - - -async def scan_for_gateways(stop_on_found: int = 0) -> list[GatewayDescriptor]: - """Scan for gateways within the network.""" - xknx = XKNX() - gatewayscanner = GatewayScanner( - xknx, stop_on_found=stop_on_found, timeout_in_seconds=2 - ) - return await gatewayscanner.scan() diff --git a/tests/components/knx/test_config_flow.py b/tests/components/knx/test_config_flow.py index 2ce3793937b..eb593d20924 100644 --- a/tests/components/knx/test_config_flow.py +++ b/tests/components/knx/test_config_flow.py @@ -1,5 +1,5 @@ """Test the KNX config flow.""" -from unittest.mock import patch +from unittest.mock import Mock, patch import pytest from xknx.exceptions.exception import InvalidSecureConfiguration @@ -67,6 +67,24 @@ def _gateway_descriptor( return descriptor +class GatewayScannerMock: + """Mock GatewayScanner.""" + + def __init__(self, gateways=None): + """Initialize GatewayScannerMock.""" + # Key is a HPAI instance in xknx, but not used in HA anyway. + self.found_gateways = ( + {f"{gateway.ip_addr}:{gateway.port}": gateway for gateway in gateways} + if gateways + else {} + ) + + async def async_scan(self): + """Mock async generator.""" + for gateway in self.found_gateways: + yield gateway + + async def test_user_single_instance(hass): """Test we only allow a single config flow.""" MockConfigEntry(domain=DOMAIN).add_to_hass(hass) @@ -78,15 +96,17 @@ async def test_user_single_instance(hass): assert result["reason"] == "single_instance_allowed" -async def test_routing_setup(hass: HomeAssistant) -> None: +@patch( + "homeassistant.components.knx.config_flow.GatewayScanner", + return_value=GatewayScannerMock(), +) +async def test_routing_setup(gateway_scanner_mock, hass: HomeAssistant) -> None: """Test routing setup.""" - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [] - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert not result["errors"] result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -126,19 +146,23 @@ async def test_routing_setup(hass: HomeAssistant) -> None: assert len(mock_setup_entry.mock_calls) == 1 -async def test_routing_setup_advanced(hass: HomeAssistant) -> None: +@patch( + "homeassistant.components.knx.config_flow.GatewayScanner", + return_value=GatewayScannerMock(), +) +async def test_routing_setup_advanced( + gateway_scanner_mock, hass: HomeAssistant +) -> None: """Test routing setup with advanced options.""" - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [] - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={ - "source": config_entries.SOURCE_USER, - "show_advanced_options": True, - }, - ) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_USER, + "show_advanced_options": True, + }, + ) + assert result["type"] == FlowResultType.FORM + assert not result["errors"] result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -200,15 +224,19 @@ async def test_routing_setup_advanced(hass: HomeAssistant) -> None: assert len(mock_setup_entry.mock_calls) == 1 -async def test_routing_secure_manual_setup(hass: HomeAssistant) -> None: +@patch( + "homeassistant.components.knx.config_flow.GatewayScanner", + return_value=GatewayScannerMock(), +) +async def test_routing_secure_manual_setup( + gateway_scanner_mock, hass: HomeAssistant +) -> None: """Test routing secure setup with manual key config.""" - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [] - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert not result["errors"] result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -287,15 +315,19 @@ async def test_routing_secure_manual_setup(hass: HomeAssistant) -> None: assert len(mock_setup_entry.mock_calls) == 1 -async def test_routing_secure_keyfile(hass: HomeAssistant) -> None: +@patch( + "homeassistant.components.knx.config_flow.GatewayScanner", + return_value=GatewayScannerMock(), +) +async def test_routing_secure_keyfile( + gateway_scanner_mock, hass: HomeAssistant +) -> None: """Test routing secure setup with keyfile.""" - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [] - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert not result["errors"] result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -412,17 +444,19 @@ async def test_routing_secure_keyfile(hass: HomeAssistant) -> None: ), ], ) +@patch( + "homeassistant.components.knx.config_flow.GatewayScanner", + return_value=GatewayScannerMock(), +) async def test_tunneling_setup_manual( - hass: HomeAssistant, user_input, config_entry_data + gateway_scanner_mock, hass: HomeAssistant, user_input, config_entry_data ) -> None: """Test tunneling if no gateway was found found (or `manual` option was chosen).""" - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [] - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert not result["errors"] result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -451,19 +485,23 @@ async def test_tunneling_setup_manual( assert len(mock_setup_entry.mock_calls) == 1 -async def test_tunneling_setup_for_local_ip(hass: HomeAssistant) -> None: +@patch( + "homeassistant.components.knx.config_flow.GatewayScanner", + return_value=GatewayScannerMock(), +) +async def test_tunneling_setup_for_local_ip( + gateway_scanner_mock, hass: HomeAssistant +) -> None: """Test tunneling if only one gateway is found.""" - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [] - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={ - "source": config_entries.SOURCE_USER, - "show_advanced_options": True, - }, - ) - assert result["type"] == FlowResultType.FORM - assert not result["errors"] + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_USER, + "show_advanced_options": True, + }, + ) + assert result["type"] == FlowResultType.FORM + assert not result["errors"] result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -542,11 +580,13 @@ async def test_tunneling_setup_for_local_ip(hass: HomeAssistant) -> None: async def test_tunneling_setup_for_multiple_found_gateways(hass: HomeAssistant) -> None: - """Test tunneling if only one gateway is found.""" + """Test tunneling if multiple gateways are found.""" gateway = _gateway_descriptor("192.168.0.1", 3675) gateway2 = _gateway_descriptor("192.168.1.100", 3675) - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [gateway, gateway2] + with patch( + "homeassistant.components.knx.config_flow.GatewayScanner" + ) as gateway_scanner_mock: + gateway_scanner_mock.return_value = GatewayScannerMock([gateway, gateway2]) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -601,8 +641,10 @@ async def test_manual_tunnel_step_with_found_gateway( hass: HomeAssistant, gateway ) -> None: """Test manual tunnel if gateway was found and tunneling is selected.""" - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [gateway] + with patch( + "homeassistant.components.knx.config_flow.GatewayScanner" + ) as gateway_scanner_mock: + gateway_scanner_mock.return_value = GatewayScannerMock([gateway]) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -634,8 +676,12 @@ async def test_manual_tunnel_step_with_found_gateway( async def test_form_with_automatic_connection_handling(hass: HomeAssistant) -> None: """Test we get the form.""" - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [_gateway_descriptor("192.168.0.1", 3675)] + with patch( + "homeassistant.components.knx.config_flow.GatewayScanner" + ) as gateway_scanner_mock: + gateway_scanner_mock.return_value = GatewayScannerMock( + [_gateway_descriptor("192.168.0.1", 3675)] + ) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -672,8 +718,10 @@ async def _get_menu_step(hass: HomeAssistant) -> FlowResult: supports_tunnelling_tcp=True, requires_secure=True, ) - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [gateway] + with patch( + "homeassistant.components.knx.config_flow.GatewayScanner" + ) as gateway_scanner_mock: + gateway_scanner_mock.return_value = GatewayScannerMock([gateway]) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -711,8 +759,10 @@ async def test_get_secure_menu_step_manual_tunnelling( supports_tunnelling_tcp=True, requires_secure=True, ) - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [gateway] + with patch( + "homeassistant.components.knx.config_flow.GatewayScanner" + ) as gateway_scanner_mock: + gateway_scanner_mock.return_value = GatewayScannerMock([gateway]) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -899,12 +949,15 @@ async def test_options_flow_connection_type( ) -> None: """Test options flow changing interface.""" mock_config_entry.add_to_hass(hass) + hass.data[DOMAIN] = Mock() # GatewayScanner uses running XKNX() instance gateway = _gateway_descriptor("192.168.0.1", 3675) menu_step = await hass.config_entries.options.async_init(mock_config_entry.entry_id) - with patch("xknx.io.gateway_scanner.GatewayScanner.scan") as gateways: - gateways.return_value = [gateway] + with patch( + "homeassistant.components.knx.config_flow.GatewayScanner" + ) as gateway_scanner_mock: + gateway_scanner_mock.return_value = GatewayScannerMock([gateway]) result = await hass.config_entries.options.async_configure( menu_step["flow_id"], {"next_step_id": "connection_type"}, From ec823582eb2a7b288e7af755f08afa3142f10886 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 07:39:47 +0100 Subject: [PATCH 0775/1033] Use OptionsFlowWithConfigEntry in axis (#82804) --- homeassistant/components/axis/config_flow.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/axis/config_flow.py b/homeassistant/components/axis/config_flow.py index 1ce2f08c045..1fb9b9488fa 100644 --- a/homeassistant/components/axis/config_flow.py +++ b/homeassistant/components/axis/config_flow.py @@ -223,14 +223,10 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN): return await self.async_step_user() -class AxisOptionsFlowHandler(config_entries.OptionsFlow): +class AxisOptionsFlowHandler(config_entries.OptionsFlowWithConfigEntry): """Handle Axis device options.""" - def __init__(self, config_entry: ConfigEntry) -> None: - """Initialize Axis device options flow.""" - self.config_entry = config_entry - self.options = dict(config_entry.options) - self.device: AxisNetworkDevice | None = None + device: AxisNetworkDevice async def async_step_init( self, user_input: dict[str, Any] | None = None @@ -249,7 +245,6 @@ class AxisOptionsFlowHandler(config_entries.OptionsFlow): schema = {} - assert self.device vapix = self.device.api.vapix # Stream profiles From f97ac9fdcdad161ee521ffb23f5abcff0a7516f9 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Mon, 28 Nov 2022 10:06:14 +0200 Subject: [PATCH 0776/1033] Add Switcher button platform (#81245) --- .../components/switcher_kis/__init__.py | 8 +- .../components/switcher_kis/button.py | 159 ++++++++++++++++++ .../components/switcher_kis/climate.py | 6 +- .../components/switcher_kis/manifest.json | 2 +- .../components/switcher_kis/utils.py | 8 + requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/switcher_kis/test_button.py | 150 +++++++++++++++++ 8 files changed, 330 insertions(+), 7 deletions(-) create mode 100644 homeassistant/components/switcher_kis/button.py create mode 100644 tests/components/switcher_kis/test_button.py diff --git a/homeassistant/components/switcher_kis/__init__.py b/homeassistant/components/switcher_kis/__init__.py index be8f140711a..39710be4857 100644 --- a/homeassistant/components/switcher_kis/__init__.py +++ b/homeassistant/components/switcher_kis/__init__.py @@ -29,7 +29,13 @@ from .const import ( ) from .utils import async_start_bridge, async_stop_bridge -PLATFORMS = [Platform.CLIMATE, Platform.COVER, Platform.SENSOR, Platform.SWITCH] +PLATFORMS = [ + Platform.BUTTON, + Platform.CLIMATE, + Platform.COVER, + Platform.SENSOR, + Platform.SWITCH, +] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/switcher_kis/button.py b/homeassistant/components/switcher_kis/button.py new file mode 100644 index 00000000000..3668f11f037 --- /dev/null +++ b/homeassistant/components/switcher_kis/button.py @@ -0,0 +1,159 @@ +"""Switcher integration Button platform.""" +from __future__ import annotations + +import asyncio +from collections.abc import Callable +from dataclasses import dataclass + +from aioswitcher.api import ( + DeviceState, + SwitcherBaseResponse, + SwitcherType2Api, + ThermostatSwing, +) +from aioswitcher.api.remotes import SwitcherBreezeRemote +from aioswitcher.device import DeviceCategory + +from homeassistant.components.button import ButtonEntity, ButtonEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import device_registry +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import DeviceInfo, EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from . import SwitcherDataUpdateCoordinator +from .const import SIGNAL_DEVICE_ADD +from .utils import get_breeze_remote_manager + + +@dataclass +class SwitcherThermostatButtonDescriptionMixin: + """Mixin to describe a Switcher Thermostat Button entity.""" + + press_fn: Callable[[SwitcherType2Api, SwitcherBreezeRemote], SwitcherBaseResponse] + supported: Callable[[SwitcherBreezeRemote], bool] + + +@dataclass +class SwitcherThermostatButtonEntityDescription( + ButtonEntityDescription, SwitcherThermostatButtonDescriptionMixin +): + """Class to describe a Switcher Thermostat Button entity.""" + + +THERMOSTAT_BUTTONS = [ + SwitcherThermostatButtonEntityDescription( + key="assume_on", + name="Assume on", + icon="mdi:fan", + entity_category=EntityCategory.CONFIG, + press_fn=lambda api, remote: api.control_breeze_device( + remote, state=DeviceState.ON, update_state=True + ), + supported=lambda remote: bool(remote.on_off_type), + ), + SwitcherThermostatButtonEntityDescription( + key="assume_off", + name="Assume off", + icon="mdi:fan-off", + entity_category=EntityCategory.CONFIG, + press_fn=lambda api, remote: api.control_breeze_device( + remote, state=DeviceState.OFF, update_state=True + ), + supported=lambda remote: bool(remote.on_off_type), + ), + SwitcherThermostatButtonEntityDescription( + key="vertical_swing_on", + name="Vertical swing on", + icon="mdi:autorenew", + press_fn=lambda api, remote: api.control_breeze_device( + remote, swing=ThermostatSwing.ON + ), + supported=lambda remote: bool(remote.separated_swing_command), + ), + SwitcherThermostatButtonEntityDescription( + key="vertical_swing_off", + name="Vertical swing off", + icon="mdi:autorenew-off", + press_fn=lambda api, remote: api.control_breeze_device( + remote, swing=ThermostatSwing.OFF + ), + supported=lambda remote: bool(remote.separated_swing_command), + ), +] + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Switcher button from config entry.""" + + @callback + async def async_add_buttons(coordinator: SwitcherDataUpdateCoordinator) -> None: + """Get remote and add button from Switcher device.""" + if coordinator.data.device_type.category == DeviceCategory.THERMOSTAT: + remote: SwitcherBreezeRemote = await hass.async_add_executor_job( + get_breeze_remote_manager(hass).get_remote, coordinator.data.remote_id + ) + async_add_entities( + SwitcherThermostatButtonEntity(coordinator, description, remote) + for description in THERMOSTAT_BUTTONS + if description.supported(remote) + ) + + config_entry.async_on_unload( + async_dispatcher_connect(hass, SIGNAL_DEVICE_ADD, async_add_buttons) + ) + + +class SwitcherThermostatButtonEntity( + CoordinatorEntity[SwitcherDataUpdateCoordinator], ButtonEntity +): + """Representation of a Switcher climate entity.""" + + entity_description: SwitcherThermostatButtonEntityDescription + + def __init__( + self, + coordinator: SwitcherDataUpdateCoordinator, + description: SwitcherThermostatButtonEntityDescription, + remote: SwitcherBreezeRemote, + ) -> None: + """Initialize the entity.""" + super().__init__(coordinator) + self.entity_description = description + self._remote = remote + + self._attr_name = f"{coordinator.name} {description.name}" + self._attr_unique_id = f"{coordinator.mac_address}-{description.key}" + self._attr_device_info = DeviceInfo( + connections={ + (device_registry.CONNECTION_NETWORK_MAC, coordinator.mac_address) + } + ) + + async def async_press(self) -> None: + """Press the button.""" + response: SwitcherBaseResponse = None + error = None + + try: + async with SwitcherType2Api( + self.coordinator.data.ip_address, self.coordinator.data.device_id + ) as swapi: + response = await self.entity_description.press_fn(swapi, self._remote) + except (asyncio.TimeoutError, OSError, RuntimeError) as err: + error = repr(err) + + if error or not response or not response.successful: + self.coordinator.last_update_success = False + self.async_write_ha_state() + raise HomeAssistantError( + f"Call api for {self.name} failed, " + f"response/error: {response or error}" + ) diff --git a/homeassistant/components/switcher_kis/climate.py b/homeassistant/components/switcher_kis/climate.py index 8462c8f02f8..01f4c80da31 100644 --- a/homeassistant/components/switcher_kis/climate.py +++ b/homeassistant/components/switcher_kis/climate.py @@ -5,7 +5,7 @@ import asyncio from typing import Any, cast from aioswitcher.api import SwitcherBaseResponse, SwitcherType2Api -from aioswitcher.api.remotes import SwitcherBreezeRemote, SwitcherBreezeRemoteManager +from aioswitcher.api.remotes import SwitcherBreezeRemote from aioswitcher.device import ( DeviceCategory, DeviceState, @@ -37,6 +37,7 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import SwitcherDataUpdateCoordinator from .const import SIGNAL_DEVICE_ADD +from .utils import get_breeze_remote_manager DEVICE_MODE_TO_HA = { ThermostatMode.COOL: HVACMode.COOL, @@ -64,13 +65,12 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up Switcher climate from config entry.""" - remote_manager = SwitcherBreezeRemoteManager() async def async_add_climate(coordinator: SwitcherDataUpdateCoordinator) -> None: """Get remote and add climate from Switcher device.""" if coordinator.data.device_type.category == DeviceCategory.THERMOSTAT: remote: SwitcherBreezeRemote = await hass.async_add_executor_job( - remote_manager.get_remote, coordinator.data.remote_id + get_breeze_remote_manager(hass).get_remote, coordinator.data.remote_id ) async_add_entities([SwitcherClimateEntity(coordinator, remote)]) diff --git a/homeassistant/components/switcher_kis/manifest.json b/homeassistant/components/switcher_kis/manifest.json index 14f324d8cac..0dafb840dfa 100644 --- a/homeassistant/components/switcher_kis/manifest.json +++ b/homeassistant/components/switcher_kis/manifest.json @@ -3,7 +3,7 @@ "name": "Switcher", "documentation": "https://www.home-assistant.io/integrations/switcher_kis/", "codeowners": ["@tomerfi", "@thecode"], - "requirements": ["aioswitcher==3.1.0"], + "requirements": ["aioswitcher==3.2.0"], "quality_scale": "platinum", "iot_class": "local_push", "config_flow": true, diff --git a/homeassistant/components/switcher_kis/utils.py b/homeassistant/components/switcher_kis/utils.py index 5a35be8aa95..ad0414ae806 100644 --- a/homeassistant/components/switcher_kis/utils.py +++ b/homeassistant/components/switcher_kis/utils.py @@ -6,9 +6,11 @@ from collections.abc import Callable import logging from typing import Any +from aioswitcher.api.remotes import SwitcherBreezeRemoteManager from aioswitcher.bridge import SwitcherBase, SwitcherBridge from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import singleton from .const import DATA_BRIDGE, DISCOVERY_TIME_SEC, DOMAIN @@ -53,3 +55,9 @@ async def async_discover_devices() -> dict[str, SwitcherBase]: _LOGGER.debug("Finished discovery, discovered devices: %s", len(discovered_devices)) return discovered_devices + + +@singleton.singleton("switcher_breeze_remote_manager") +def get_breeze_remote_manager(hass: HomeAssistant) -> SwitcherBreezeRemoteManager: + """Get Switcher Breeze remote manager.""" + return SwitcherBreezeRemoteManager() diff --git a/requirements_all.txt b/requirements_all.txt index 47cf17e93d3..623c8db02db 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -273,7 +273,7 @@ aioslimproto==2.1.1 aiosteamist==0.3.2 # homeassistant.components.switcher_kis -aioswitcher==3.1.0 +aioswitcher==3.2.0 # homeassistant.components.syncthing aiosyncthing==0.5.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 12462c52496..67cadb6d083 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -248,7 +248,7 @@ aioslimproto==2.1.1 aiosteamist==0.3.2 # homeassistant.components.switcher_kis -aioswitcher==3.1.0 +aioswitcher==3.2.0 # homeassistant.components.syncthing aiosyncthing==0.5.1 diff --git a/tests/components/switcher_kis/test_button.py b/tests/components/switcher_kis/test_button.py new file mode 100644 index 00000000000..0e3431168dc --- /dev/null +++ b/tests/components/switcher_kis/test_button.py @@ -0,0 +1,150 @@ +"""Tests for Switcher button platform.""" +from unittest.mock import ANY, patch + +from aioswitcher.api import DeviceState, SwitcherBaseResponse, ThermostatSwing +import pytest + +from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS +from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.util import slugify + +from . import init_integration +from .consts import DUMMY_THERMOSTAT_DEVICE as DEVICE + +BASE_ENTITY_ID = f"{BUTTON_DOMAIN}.{slugify(DEVICE.name)}" +ASSUME_ON_EID = BASE_ENTITY_ID + "_assume_on" +ASSUME_OFF_EID = BASE_ENTITY_ID + "_assume_off" +SWING_ON_EID = BASE_ENTITY_ID + "_vertical_swing_on" +SWING_OFF_EID = BASE_ENTITY_ID + "_vertical_swing_off" + + +@pytest.mark.parametrize("mock_bridge", [[DEVICE]], indirect=True) +async def test_assume_button(hass: HomeAssistant, mock_bridge, mock_api): + """Test assume on/off button.""" + await init_integration(hass) + assert mock_bridge + + assert hass.states.get(ASSUME_ON_EID) is not None + assert hass.states.get(ASSUME_OFF_EID) is not None + assert hass.states.get(SWING_ON_EID) is None + assert hass.states.get(SWING_OFF_EID) is None + + with patch( + "homeassistant.components.switcher_kis.climate.SwitcherType2Api.control_breeze_device", + ) as mock_control_device: + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: ASSUME_ON_EID}, + blocking=True, + ) + assert mock_api.call_count == 2 + mock_control_device.assert_called_once_with( + ANY, state=DeviceState.ON, update_state=True + ) + + mock_control_device.reset_mock() + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: ASSUME_OFF_EID}, + blocking=True, + ) + assert mock_api.call_count == 4 + mock_control_device.assert_called_once_with( + ANY, state=DeviceState.OFF, update_state=True + ) + + +@pytest.mark.parametrize("mock_bridge", [[DEVICE]], indirect=True) +async def test_swing_button(hass: HomeAssistant, mock_bridge, mock_api, monkeypatch): + """Test vertical swing on/off button.""" + monkeypatch.setattr(DEVICE, "remote_id", "ELEC7022") + await init_integration(hass) + assert mock_bridge + + assert hass.states.get(ASSUME_ON_EID) is None + assert hass.states.get(ASSUME_OFF_EID) is None + assert hass.states.get(SWING_ON_EID) is not None + assert hass.states.get(SWING_OFF_EID) is not None + + with patch( + "homeassistant.components.switcher_kis.climate.SwitcherType2Api.control_breeze_device", + ) as mock_control_device: + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: SWING_ON_EID}, + blocking=True, + ) + assert mock_api.call_count == 2 + mock_control_device.assert_called_once_with(ANY, swing=ThermostatSwing.ON) + + mock_control_device.reset_mock() + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: SWING_OFF_EID}, + blocking=True, + ) + assert mock_api.call_count == 4 + mock_control_device.assert_called_once_with(ANY, swing=ThermostatSwing.OFF) + + +@pytest.mark.parametrize("mock_bridge", [[DEVICE]], indirect=True) +async def test_control_device_fail(hass, mock_bridge, mock_api, monkeypatch): + """Test control device fail.""" + await init_integration(hass) + assert mock_bridge + + assert hass.states.get(ASSUME_ON_EID) is not None + + # Test exception during set hvac mode + with patch( + "homeassistant.components.switcher_kis.climate.SwitcherType2Api.control_breeze_device", + side_effect=RuntimeError("fake error"), + ) as mock_control_device: + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: ASSUME_ON_EID}, + blocking=True, + ) + + assert mock_api.call_count == 2 + mock_control_device.assert_called_once_with( + ANY, state=DeviceState.ON, update_state=True + ) + + state = hass.states.get(ASSUME_ON_EID) + assert state.state == STATE_UNAVAILABLE + + # Make device available again + mock_bridge.mock_callbacks([DEVICE]) + await hass.async_block_till_done() + + assert hass.states.get(ASSUME_ON_EID) is not None + + # Test error response during turn on + with patch( + "homeassistant.components.switcher_kis.climate.SwitcherType2Api.control_breeze_device", + return_value=SwitcherBaseResponse(None), + ) as mock_control_device: + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: ASSUME_ON_EID}, + blocking=True, + ) + + assert mock_api.call_count == 4 + mock_control_device.assert_called_once_with( + ANY, state=DeviceState.ON, update_state=True + ) + + state = hass.states.get(ASSUME_ON_EID) + assert state.state == STATE_UNAVAILABLE From a236836796e489f9bfa269a5946c79999db0fac2 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:15:16 +0100 Subject: [PATCH 0777/1033] Fix typo in helper docstring (#82824) --- homeassistant/helpers/schema_config_entry_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 86cd578039a..64d3adc5d0c 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -50,7 +50,7 @@ class SchemaFlowFormStep(SchemaFlowStep): - The `validate_user_input` function is called if the schema validates successfully. - The first argument is a reference to the current `SchemaCommonFlowHandler`. - The second argument is the user input from the current step. - - The `validate_user_input` should raise `SchemaFlowError` is user input is invalid. + - The `validate_user_input` should raise `SchemaFlowError` if user input is invalid. """ next_step: Callable[[dict[str, Any]], str | None] | str | None = None From 5be36912b2f8eb15f4d72a5ff5c63d083569bd9d Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:19:41 +0100 Subject: [PATCH 0778/1033] Use _attr_state in openhome media player (#82833) --- .../components/openhome/media_player.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/openhome/media_player.py b/homeassistant/components/openhome/media_player.py index f352d7101ac..fba397c1326 100644 --- a/homeassistant/components/openhome/media_player.py +++ b/homeassistant/components/openhome/media_player.py @@ -124,7 +124,7 @@ class OpenhomeDevice(MediaPlayerEntity): self._source_index = {} self._source = {} self._name = None - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING self._available = True @property @@ -178,16 +178,16 @@ class OpenhomeDevice(MediaPlayerEntity): ) if self._in_standby: - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF elif self._transport_state == "Paused": - self._state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED elif self._transport_state in ("Playing", "Buffering"): - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING elif self._transport_state == "Stopped": - self._state = MediaPlayerState.IDLE + self._attr_state = MediaPlayerState.IDLE else: # Device is playing an external source with no transport controls - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING self._available = True except (asyncio.TimeoutError, aiohttp.ClientError, UpnpError): @@ -279,11 +279,6 @@ class OpenhomeDevice(MediaPlayerEntity): """Return a unique ID.""" return self._device.uuid() - @property - def state(self): - """Return the state of the device.""" - return self._state - @property def source_list(self): """List of available input sources.""" From 9ea8e0a7caab15253e0941154636843e46ee7523 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:28:08 +0100 Subject: [PATCH 0779/1033] Use _attr_state in harman kardon avr media player (#82826) --- .../components/harman_kardon_avr/media_player.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/harman_kardon_avr/media_player.py b/homeassistant/components/harman_kardon_avr/media_player.py index f222d4bd739..17b3d0c5717 100644 --- a/homeassistant/components/harman_kardon_avr/media_player.py +++ b/homeassistant/components/harman_kardon_avr/media_player.py @@ -66,18 +66,17 @@ class HkAvrDevice(MediaPlayerEntity): self._source_list = avr.sources - self._state = None self._muted = avr.muted self._current_source = avr.current_source def update(self) -> None: """Update the state of this media_player.""" if self._avr.is_on(): - self._state = MediaPlayerState.ON + self._attr_state = MediaPlayerState.ON elif self._avr.is_off(): - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF else: - self._state = None + self._attr_state = None self._muted = self._avr.muted self._current_source = self._avr.current_source @@ -87,11 +86,6 @@ class HkAvrDevice(MediaPlayerEntity): """Return the name of the device.""" return self._name - @property - def state(self): - """Return the state of the device.""" - return self._state - @property def is_volume_muted(self): """Muted status not available.""" From 3e35d869d2a98249c17719434be922f74be12944 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:29:04 +0100 Subject: [PATCH 0780/1033] Use _attr_state in horizon media player (#82827) --- .../components/horizon/media_player.py | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/horizon/media_player.py b/homeassistant/components/horizon/media_player.py index c75d47d06eb..680d2982f9f 100644 --- a/homeassistant/components/horizon/media_player.py +++ b/homeassistant/components/horizon/media_player.py @@ -86,7 +86,6 @@ class HorizonDevice(MediaPlayerEntity): """Initialize the remote.""" self._client = client self._name = name - self._state = None self._keys = remote_keys @property @@ -94,66 +93,61 @@ class HorizonDevice(MediaPlayerEntity): """Return the name of the remote.""" return self._name - @property - def state(self): - """Return the state of the device.""" - return self._state - @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update(self) -> None: """Update State using the media server running on the Horizon.""" try: if self._client.is_powered_on(): - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING else: - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF except OSError: - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF def turn_on(self) -> None: """Turn the device on.""" - if self._state == MediaPlayerState.OFF: + if self.state == MediaPlayerState.OFF: self._send_key(self._keys.POWER) def turn_off(self) -> None: """Turn the device off.""" - if self._state != MediaPlayerState.OFF: + if self.state != MediaPlayerState.OFF: self._send_key(self._keys.POWER) def media_previous_track(self) -> None: """Channel down.""" self._send_key(self._keys.CHAN_DOWN) - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING def media_next_track(self) -> None: """Channel up.""" self._send_key(self._keys.CHAN_UP) - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING def media_play(self) -> None: """Send play command.""" self._send_key(self._keys.PAUSE) - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING def media_pause(self) -> None: """Send pause command.""" self._send_key(self._keys.PAUSE) - self._state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED def media_play_pause(self) -> None: """Send play/pause command.""" self._send_key(self._keys.PAUSE) - if self._state == MediaPlayerState.PAUSED: - self._state = MediaPlayerState.PLAYING + if self.state == MediaPlayerState.PAUSED: + self._attr_state = MediaPlayerState.PLAYING else: - self._state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED def play_media(self, media_type: str, media_id: str, **kwargs: Any) -> None: """Play media / switch to channel.""" if MediaType.CHANNEL == media_type: try: self._select_channel(int(media_id)) - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING except ValueError: _LOGGER.error("Invalid channel: %s", media_id) else: From ff6737490102daee1a955050481ca58de4164d95 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:29:28 +0100 Subject: [PATCH 0781/1033] Use _attr_state in lg-netcast media player (#82828) --- .../components/lg_netcast/media_player.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/lg_netcast/media_player.py b/homeassistant/components/lg_netcast/media_player.py index 47cbebf939b..1af16a904d8 100644 --- a/homeassistant/components/lg_netcast/media_player.py +++ b/homeassistant/components/lg_netcast/media_player.py @@ -89,7 +89,6 @@ class LgTVDevice(MediaPlayerEntity): self._channel_id = None self._channel_name = "" self._program_name = "" - self._state = None self._sources = {} self._source_names = [] @@ -100,14 +99,14 @@ class LgTVDevice(MediaPlayerEntity): with self._client as client: client.send_command(command) except (LgNetCastError, RequestException): - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF def update(self) -> None: """Retrieve the latest data from the LG TV.""" try: with self._client as client: - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING self.__update_volume() @@ -142,7 +141,7 @@ class LgTVDevice(MediaPlayerEntity): ) self._source_names = [n for n, k in sorted_sources] except (LgNetCastError, RequestException): - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF def __update_volume(self): volume_info = self._client.get_volume() @@ -156,11 +155,6 @@ class LgTVDevice(MediaPlayerEntity): """Return the name of the device.""" return self._name - @property - def state(self): - """Return the state of the device.""" - return self._state - @property def is_volume_muted(self): """Boolean if volume is currently muted.""" @@ -249,13 +243,13 @@ class LgTVDevice(MediaPlayerEntity): def media_play(self) -> None: """Send play command.""" self._playing = True - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING self.send_command(LG_COMMAND.PLAY) def media_pause(self) -> None: """Send media pause command to media player.""" self._playing = False - self._state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED self.send_command(LG_COMMAND.PAUSE) def media_next_track(self) -> None: From b7f1ebe13c1d00117e2ece5360c9c24816fd9c5c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:29:46 +0100 Subject: [PATCH 0782/1033] Use _attr_state in mediaroom media player (#82830) --- .../components/mediaroom/media_player.py | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/mediaroom/media_player.py b/homeassistant/components/mediaroom/media_player.py index b5089c64b14..09e137c96d1 100644 --- a/homeassistant/components/mediaroom/media_player.py +++ b/homeassistant/components/mediaroom/media_player.py @@ -142,7 +142,7 @@ class MediaroomDevice(MediaPlayerEntity): State.UNKNOWN: None, } - self._state = state_map[mediaroom_state] + self._attr_state = state_map[mediaroom_state] def __init__(self, host, device_id, optimistic=False, timeout=DEFAULT_TIMEOUT): """Initialize the device.""" @@ -154,7 +154,7 @@ class MediaroomDevice(MediaPlayerEntity): ) self._channel = None self._optimistic = optimistic - self._state = ( + self._attr_state = ( MediaPlayerState.PLAYING if optimistic else MediaPlayerState.STANDBY ) self._name = f"Mediaroom {device_id if device_id else host}" @@ -179,7 +179,7 @@ class MediaroomDevice(MediaPlayerEntity): if not stb_state: return self.set_state(stb_state) - _LOGGER.debug("STB(%s) is [%s]", self.host, self._state) + _LOGGER.debug("STB(%s) is [%s]", self.host, self.state) self._available = True self.async_write_ha_state() @@ -215,7 +215,7 @@ class MediaroomDevice(MediaPlayerEntity): try: await self.stb.send_cmd(command) if self._optimistic: - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING self._available = True except PyMediaroomError: self._available = False @@ -231,11 +231,6 @@ class MediaroomDevice(MediaPlayerEntity): """Return the name of the device.""" return self._name - @property - def state(self): - """Return the state of the device.""" - return self._state - @property def media_channel(self): """Channel currently playing.""" @@ -247,7 +242,7 @@ class MediaroomDevice(MediaPlayerEntity): try: self.set_state(await self.stb.turn_on()) if self._optimistic: - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING self._available = True except PyMediaroomError: self._available = False @@ -259,7 +254,7 @@ class MediaroomDevice(MediaPlayerEntity): try: self.set_state(await self.stb.turn_off()) if self._optimistic: - self._state = MediaPlayerState.STANDBY + self._attr_state = MediaPlayerState.STANDBY self._available = True except PyMediaroomError: self._available = False @@ -272,7 +267,7 @@ class MediaroomDevice(MediaPlayerEntity): _LOGGER.debug("media_play()") await self.stb.send_cmd("PlayPause") if self._optimistic: - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING self._available = True except PyMediaroomError: self._available = False @@ -284,7 +279,7 @@ class MediaroomDevice(MediaPlayerEntity): try: await self.stb.send_cmd("PlayPause") if self._optimistic: - self._state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED self._available = True except PyMediaroomError: self._available = False @@ -296,7 +291,7 @@ class MediaroomDevice(MediaPlayerEntity): try: await self.stb.send_cmd("Stop") if self._optimistic: - self._state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED self._available = True except PyMediaroomError: self._available = False @@ -308,7 +303,7 @@ class MediaroomDevice(MediaPlayerEntity): try: await self.stb.send_cmd("ProgDown") if self._optimistic: - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING self._available = True except PyMediaroomError: self._available = False @@ -320,7 +315,7 @@ class MediaroomDevice(MediaPlayerEntity): try: await self.stb.send_cmd("ProgUp") if self._optimistic: - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING self._available = True except PyMediaroomError: self._available = False From 6e37aa425bb8a36065f6faa9b5c42b9a8e335973 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:46:45 +0100 Subject: [PATCH 0783/1033] Use _attr_state in gstreamer media player (#82846) --- .../components/gstreamer/media_player.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/gstreamer/media_player.py b/homeassistant/components/gstreamer/media_player.py index 2861dc4516b..cb6e6cee721 100644 --- a/homeassistant/components/gstreamer/media_player.py +++ b/homeassistant/components/gstreamer/media_player.py @@ -4,7 +4,7 @@ from __future__ import annotations import logging from typing import Any -from gsp import GstreamerPlayer +from gsp import STATE_IDLE, STATE_PAUSED, STATE_PLAYING, GstreamerPlayer import voluptuous as vol from homeassistant.components import media_source @@ -33,6 +33,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( {vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_PIPELINE): cv.string} ) +GSP_STATE_MAPPING = { + STATE_IDLE: MediaPlayerState.IDLE, + STATE_PAUSED: MediaPlayerState.PAUSED, + STATE_PLAYING: MediaPlayerState.PLAYING, +} + def setup_platform( hass: HomeAssistant, @@ -67,11 +73,11 @@ class GstreamerDevice(MediaPlayerEntity): | MediaPlayerEntityFeature.BROWSE_MEDIA ) - def __init__(self, player, name): + def __init__(self, player: GstreamerPlayer, name: str | None) -> None: """Initialize the Gstreamer device.""" self._player = player self._name = name or DOMAIN - self._state = MediaPlayerState.IDLE + self._attr_state = MediaPlayerState.IDLE self._volume = None self._duration = None self._uri = None @@ -81,7 +87,7 @@ class GstreamerDevice(MediaPlayerEntity): def update(self) -> None: """Update properties.""" - self._state = self._player.state + self._attr_state = GSP_STATE_MAPPING.get(self._player.state) self._volume = self._player.volume self._duration = self._player.duration self._uri = self._player.uri @@ -139,11 +145,6 @@ class GstreamerDevice(MediaPlayerEntity): """Return the volume level.""" return self._volume - @property - def state(self): - """Return the state of the player.""" - return self._state - @property def media_duration(self): """Duration of current playing media in seconds.""" From b2b3e1481033b089bfa2a778b51a46100b6c55d2 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:47:40 +0100 Subject: [PATCH 0784/1033] Use OptionsFlowWithConfigEntry in androidtv (#82805) --- .../components/androidtv/config_flow.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/androidtv/config_flow.py b/homeassistant/components/androidtv/config_flow.py index bdc067c4275..ea51ddedfdb 100644 --- a/homeassistant/components/androidtv/config_flow.py +++ b/homeassistant/components/androidtv/config_flow.py @@ -9,7 +9,11 @@ from typing import Any from androidtv import state_detection_rules_validator import voluptuous as vol -from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow +from homeassistant.config_entries import ( + ConfigEntry, + ConfigFlow, + OptionsFlowWithConfigEntry, +) from homeassistant.const import CONF_DEVICE_CLASS, CONF_HOST, CONF_PORT from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult @@ -168,22 +172,22 @@ class AndroidTVFlowHandler(ConfigFlow, domain=DOMAIN): @staticmethod @callback - def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow: + def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlowHandler: """Get the options flow for this handler.""" return OptionsFlowHandler(config_entry) -class OptionsFlowHandler(OptionsFlow): +class OptionsFlowHandler(OptionsFlowWithConfigEntry): """Handle an option flow for Android TV.""" def __init__(self, config_entry: ConfigEntry) -> None: """Initialize options flow.""" - self.config_entry = config_entry + super().__init__(config_entry) - apps = config_entry.options.get(CONF_APPS, {}) - det_rules = config_entry.options.get(CONF_STATE_DETECTION_RULES, {}) - self._apps: dict[str, Any] = apps.copy() - self._state_det_rules: dict[str, Any] = det_rules.copy() + self._apps: dict[str, Any] = self.options.setdefault(CONF_APPS, {}) + self._state_det_rules: dict[str, Any] = self.options.setdefault( + CONF_STATE_DETECTION_RULES, {} + ) self._conf_app_id: str | None = None self._conf_rule_id: str | None = None @@ -222,7 +226,7 @@ class OptionsFlowHandler(OptionsFlow): apps_list = {k: f"{v} ({k})" if v else k for k, v in self._apps.items()} apps = {APPS_NEW_ID: "Add new", **apps_list} rules = [RULES_NEW_ID] + list(self._state_det_rules) - options = self.config_entry.options + options = self.options data_schema = vol.Schema( { From 67e4f2c2027d472a4fbb07769c74384cabd44db8 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 28 Nov 2022 09:54:13 +0100 Subject: [PATCH 0785/1033] Raise repairs issue if country is not configured (#82685) --- .../components/homeassistant/strings.json | 4 ++ .../homeassistant/translations/en.json | 4 ++ homeassistant/config.py | 41 ++++++++++++++----- homeassistant/core.py | 9 ++++ tests/components/homeassistant/test_init.py | 1 + tests/test_config.py | 37 ++++++++++++++++- 6 files changed, 85 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/homeassistant/strings.json b/homeassistant/components/homeassistant/strings.json index d7c5216111c..06ecd4fe3ef 100644 --- a/homeassistant/components/homeassistant/strings.json +++ b/homeassistant/components/homeassistant/strings.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "title": "The country has not been configured", + "description": "No country has been configured, please update the configuration by clicking on the \"learn more\" button below." + }, "historic_currency": { "title": "The configured currency is no longer in use", "description": "The currency {currency} is no longer in use, please reconfigure the currency configuration." diff --git a/homeassistant/components/homeassistant/translations/en.json b/homeassistant/components/homeassistant/translations/en.json index b41e5753995..4b94cc37d2e 100644 --- a/homeassistant/components/homeassistant/translations/en.json +++ b/homeassistant/components/homeassistant/translations/en.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "No country has been configured, please update the configuration by clicking on the \"learn more\" button below.", + "title": "The country has not been configured" + }, "historic_currency": { "description": "The currency {currency} is no longer in use, please reconfigure the currency configuration.", "title": "The configured currency is no longer in use" diff --git a/homeassistant/config.py b/homeassistant/config.py index 963f3ee9876..e203c45f795 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -206,16 +206,36 @@ CUSTOMIZE_CONFIG_SCHEMA = vol.Schema( def _raise_issue_if_historic_currency(hass: HomeAssistant, currency: str) -> None: - if currency in HISTORIC_CURRENCIES: - ir.async_create_issue( - hass, - "homeassistant", - "historic_currency", - is_fixable=False, - severity=ir.IssueSeverity.WARNING, - translation_key="historic_currency", - translation_placeholders={"currency": currency}, - ) + if currency not in HISTORIC_CURRENCIES: + ir.async_delete_issue(hass, "homeassistant", "historic_currency") + return + + ir.async_create_issue( + hass, + "homeassistant", + "historic_currency", + is_fixable=False, + learn_more_url="homeassistant://config/general", + severity=ir.IssueSeverity.WARNING, + translation_key="historic_currency", + translation_placeholders={"currency": currency}, + ) + + +def _raise_issue_if_no_country(hass: HomeAssistant, country: str | None) -> None: + if country is not None: + ir.async_delete_issue(hass, "homeassistant", "country_not_configured") + return + + ir.async_create_issue( + hass, + "homeassistant", + "country_not_configured", + is_fixable=False, + learn_more_url="homeassistant://config/general", + severity=ir.IssueSeverity.WARNING, + translation_key="country_not_configured", + ) def _validate_currency(data: Any) -> Any: @@ -587,6 +607,7 @@ async def async_process_ha_core_config(hass: HomeAssistant, config: dict) -> Non setattr(hac, attr, config[key]) _raise_issue_if_historic_currency(hass, hass.config.currency) + _raise_issue_if_no_country(hass, hass.config.country) if CONF_TIME_ZONE in config: hac.set_time_zone(config[CONF_TIME_ZONE]) diff --git a/homeassistant/core.py b/homeassistant/core.py index 9172c1d60b4..c5a7599c369 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -1976,10 +1976,19 @@ class Config: async def async_update(self, **kwargs: Any) -> None: """Update the configuration from a dictionary.""" + # pylint: disable-next=import-outside-toplevel + from .config import ( + _raise_issue_if_historic_currency, + _raise_issue_if_no_country, + ) + self._update(source=ConfigSource.STORAGE, **kwargs) await self._async_store() self.hass.bus.async_fire(EVENT_CORE_CONFIG_UPDATE, kwargs) + _raise_issue_if_historic_currency(self.hass, self.currency) + _raise_issue_if_no_country(self.hass, self.country) + async def async_load(self) -> None: """Load [homeassistant] core config.""" if not (data := await self._store.async_load()): diff --git a/tests/components/homeassistant/test_init.py b/tests/components/homeassistant/test_init.py index 8e89be00433..0c980bcf07e 100644 --- a/tests/components/homeassistant/test_init.py +++ b/tests/components/homeassistant/test_init.py @@ -179,6 +179,7 @@ class TestComponentsCore(unittest.TestCase): config.YAML_CONFIG_FILE: yaml.dump( { ha.DOMAIN: { + "country": "SE", # To avoid creating issue country_not_configured "latitude": 10, "longitude": 20, "customize": {"test.Entity": {"hello": "world"}}, diff --git a/tests/test_config.py b/tests/test_config.py index 17b3898eb2a..ea9c81eae1a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1315,6 +1315,41 @@ async def test_core_store_historic_currency(hass, hass_storage): await config_util.async_process_ha_core_config(hass, {}) issue_registry = ir.async_get(hass) - issue = issue_registry.async_get_issue("homeassistant", "historic_currency") + issue_id = "historic_currency" + issue = issue_registry.async_get_issue("homeassistant", issue_id) assert issue assert issue.translation_placeholders == {"currency": "LTT"} + + await hass.config.async_update(**{"currency": "EUR"}) + issue = issue_registry.async_get_issue("homeassistant", issue_id) + assert not issue + + +async def test_core_config_schema_no_country(hass): + """Test core config schema.""" + await config_util.async_process_ha_core_config(hass, {}) + + issue_registry = ir.async_get(hass) + issue = issue_registry.async_get_issue("homeassistant", "country_not_configured") + assert issue + + +async def test_core_store_no_country(hass, hass_storage): + """Test core config store.""" + core_data = { + "data": {}, + "key": "core.config", + "version": 1, + "minor_version": 1, + } + hass_storage["core.config"] = dict(core_data) + await config_util.async_process_ha_core_config(hass, {}) + + issue_registry = ir.async_get(hass) + issue_id = "country_not_configured" + issue = issue_registry.async_get_issue("homeassistant", issue_id) + assert issue + + await hass.config.async_update(**{"country": "SE"}) + issue = issue_registry.async_get_issue("homeassistant", issue_id) + assert not issue From 8a20a903244b97476e7aa6b266995380aeaea5b6 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:56:08 +0100 Subject: [PATCH 0786/1033] Use SchemaOptionsFlowHandler in asuswrt (#82806) --- .../components/asuswrt/config_flow.py | 102 +++++++----------- tests/components/asuswrt/test_config_flow.py | 40 ++++++- 2 files changed, 79 insertions(+), 63 deletions(-) diff --git a/homeassistant/components/asuswrt/config_flow.py b/homeassistant/components/asuswrt/config_flow.py index 94843a4c07c..414dbc65d8b 100644 --- a/homeassistant/components/asuswrt/config_flow.py +++ b/homeassistant/components/asuswrt/config_flow.py @@ -5,7 +5,7 @@ from __future__ import annotations import logging import os import socket -from typing import Any +from typing import Any, cast import voluptuous as vol @@ -13,7 +13,7 @@ from homeassistant.components.device_tracker import ( CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME, ) -from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow +from homeassistant.config_entries import ConfigEntry, ConfigFlow from homeassistant.const import ( CONF_HOST, CONF_MODE, @@ -26,6 +26,11 @@ from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import config_validation as cv from homeassistant.helpers.device_registry import format_mac +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaCommonFlowHandler, + SchemaFlowFormStep, + SchemaOptionsFlowHandler, +) from .const import ( CONF_DNSMASQ, @@ -52,6 +57,35 @@ RESULT_UNKNOWN = "unknown" _LOGGER = logging.getLogger(__name__) +OPTIONS_SCHEMA = vol.Schema( + { + vol.Optional( + CONF_CONSIDER_HOME, default=DEFAULT_CONSIDER_HOME.total_seconds() + ): vol.All(vol.Coerce(int), vol.Clamp(min=0, max=900)), + vol.Optional(CONF_TRACK_UNKNOWN, default=DEFAULT_TRACK_UNKNOWN): bool, + vol.Required(CONF_INTERFACE, default=DEFAULT_INTERFACE): str, + vol.Required(CONF_DNSMASQ, default=DEFAULT_DNSMASQ): str, + } +) + + +def get_options_schema(handler: SchemaCommonFlowHandler) -> vol.Schema: + """Get options schema.""" + options_flow: SchemaOptionsFlowHandler + options_flow = cast(SchemaOptionsFlowHandler, handler.parent_handler) + if options_flow.config_entry.data[CONF_MODE] == MODE_AP: + return OPTIONS_SCHEMA.extend( + { + vol.Optional(CONF_REQUIRE_IP, default=True): bool, + } + ) + return OPTIONS_SCHEMA + + +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(get_options_schema), +} + def _is_file(value: str) -> bool: """Validate that the value is an existing file.""" @@ -203,62 +237,8 @@ class AsusWrtFlowHandler(ConfigFlow, domain=DOMAIN): @staticmethod @callback - def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow: - """Get the options flow for this handler.""" - return OptionsFlowHandler(config_entry) - - -class OptionsFlowHandler(OptionsFlow): - """Handle a option flow for AsusWrt.""" - - def __init__(self, config_entry: ConfigEntry) -> None: - """Initialize options flow.""" - self.config_entry = config_entry - - async def async_step_init( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Handle options flow.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - data_schema = vol.Schema( - { - vol.Optional( - CONF_CONSIDER_HOME, - default=self.config_entry.options.get( - CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds() - ), - ): vol.All(vol.Coerce(int), vol.Clamp(min=0, max=900)), - vol.Optional( - CONF_TRACK_UNKNOWN, - default=self.config_entry.options.get( - CONF_TRACK_UNKNOWN, DEFAULT_TRACK_UNKNOWN - ), - ): bool, - vol.Required( - CONF_INTERFACE, - default=self.config_entry.options.get( - CONF_INTERFACE, DEFAULT_INTERFACE - ), - ): str, - vol.Required( - CONF_DNSMASQ, - default=self.config_entry.options.get( - CONF_DNSMASQ, DEFAULT_DNSMASQ - ), - ): str, - } - ) - - if self.config_entry.data[CONF_MODE] == MODE_AP: - data_schema = data_schema.extend( - { - vol.Optional( - CONF_REQUIRE_IP, - default=self.config_entry.options.get(CONF_REQUIRE_IP, True), - ): bool, - } - ) - - return self.async_show_form(step_id="init", data_schema=data_schema) + def async_get_options_flow( + config_entry: ConfigEntry, + ) -> SchemaOptionsFlowHandler: + """Get options flow for this handler.""" + return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW) diff --git a/tests/components/asuswrt/test_config_flow.py b/tests/components/asuswrt/test_config_flow.py index 22a780fc12e..f9af800166a 100644 --- a/tests/components/asuswrt/test_config_flow.py +++ b/tests/components/asuswrt/test_config_flow.py @@ -23,6 +23,7 @@ from homeassistant.const import ( CONF_PROTOCOL, CONF_USERNAME, ) +from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry @@ -248,8 +249,8 @@ async def test_on_connect_failed(hass, side_effect, error): assert result["errors"] == {"base": error} -async def test_options_flow(hass): - """Test config flow options.""" +async def test_options_flow_ap(hass: HomeAssistant) -> None: + """Test config flow options for ap mode.""" config_entry = MockConfigEntry( domain=DOMAIN, data=CONFIG_DATA, @@ -264,6 +265,7 @@ async def test_options_flow(hass): assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "init" + assert CONF_REQUIRE_IP in result["data_schema"].schema result = await hass.config_entries.options.async_configure( result["flow_id"], @@ -282,3 +284,37 @@ async def test_options_flow(hass): assert config_entry.options[CONF_INTERFACE] == "aaa" assert config_entry.options[CONF_DNSMASQ] == "bbb" assert config_entry.options[CONF_REQUIRE_IP] is False + + +async def test_options_flow_router(hass: HomeAssistant) -> None: + """Test config flow options for router mode.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data={**CONFIG_DATA, CONF_MODE: "router"}, + ) + config_entry.add_to_hass(hass) + + with PATCH_SETUP_ENTRY: + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + result = await hass.config_entries.options.async_init(config_entry.entry_id) + + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "init" + assert CONF_REQUIRE_IP not in result["data_schema"].schema + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_CONSIDER_HOME: 20, + CONF_TRACK_UNKNOWN: True, + CONF_INTERFACE: "aaa", + CONF_DNSMASQ: "bbb", + }, + ) + + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert config_entry.options[CONF_CONSIDER_HOME] == 20 + assert config_entry.options[CONF_TRACK_UNKNOWN] is True + assert config_entry.options[CONF_INTERFACE] == "aaa" + assert config_entry.options[CONF_DNSMASQ] == "bbb" From e738df502b1d7cddb28bbd1cba60f7af3dc637a3 Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Mon, 28 Nov 2022 10:18:45 +0100 Subject: [PATCH 0787/1033] Fix KNX secure config switching from manual to keyfile (#82724) --- homeassistant/components/knx/config_flow.py | 13 ++- homeassistant/components/knx/const.py | 6 +- tests/components/knx/test_config_flow.py | 100 ++++++++++++++++++++ 3 files changed, 111 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/knx/config_flow.py b/homeassistant/components/knx/config_flow.py index c043ea65ee5..4ee46939f2d 100644 --- a/homeassistant/components/knx/config_flow.py +++ b/homeassistant/components/knx/config_flow.py @@ -313,7 +313,6 @@ class KNXCommonFlow(ABC, FlowHandler): if user_input is not None: self.new_entry_data |= KNXConfigEntryData( - connection_type=CONF_KNX_TUNNELING_TCP_SECURE, device_authentication=user_input[CONF_KNX_SECURE_DEVICE_AUTHENTICATION], user_id=user_input[CONF_KNX_SECURE_USER_ID], user_password=user_input[CONF_KNX_SECURE_USER_PASSWORD], @@ -428,10 +427,13 @@ class KNXCommonFlow(ABC, FlowHandler): if not errors: self.new_entry_data |= KNXConfigEntryData( - backbone_key=None, - sync_latency_tolerance=None, knxkeys_filename=storage_key, knxkeys_password=user_input[CONF_KNX_KNXKEY_PASSWORD], + backbone_key=None, + sync_latency_tolerance=None, + device_authentication=None, + user_id=None, + user_password=None, ) if ( self.new_entry_data[CONF_KNX_CONNECTION_TYPE] @@ -442,10 +444,11 @@ class KNXCommonFlow(ABC, FlowHandler): title = f"Secure Tunneling @ {self.new_entry_data[CONF_HOST]}" return self.finish_flow(title=title) + if _default_filename := self.initial_data.get(CONF_KNX_KNXKEY_FILENAME): + _default_filename = _default_filename.lstrip(CONST_KNX_STORAGE_KEY) fields = { vol.Required( - CONF_KNX_KNXKEY_FILENAME, - default=self.initial_data.get(CONF_KNX_KNXKEY_FILENAME), + CONF_KNX_KNXKEY_FILENAME, default=_default_filename ): selector.TextSelector(), vol.Required( CONF_KNX_KNXKEY_PASSWORD, diff --git a/homeassistant/components/knx/const.py b/homeassistant/components/knx/const.py index 4a8b113516c..f98d42fbd9e 100644 --- a/homeassistant/components/knx/const.py +++ b/homeassistant/components/knx/const.py @@ -90,9 +90,9 @@ class KNXConfigEntryData(TypedDict, total=False): host: str port: int - user_id: int - user_password: str - device_authentication: str + user_id: int | None + user_password: str | None + device_authentication: str | None knxkeys_filename: str knxkeys_password: str backbone_key: str | None diff --git a/tests/components/knx/test_config_flow.py b/tests/components/knx/test_config_flow.py index eb593d20924..c5e26561b92 100644 --- a/tests/components/knx/test_config_flow.py +++ b/tests/components/knx/test_config_flow.py @@ -383,6 +383,9 @@ async def test_routing_secure_keyfile( CONF_KNX_KNXKEY_PASSWORD: "password", CONF_KNX_ROUTING_BACKBONE_KEY: None, CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, + CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None, + CONF_KNX_SECURE_USER_ID: None, + CONF_KNX_SECURE_USER_PASSWORD: None, CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.123", } assert len(mock_setup_entry.mock_calls) == 1 @@ -876,6 +879,9 @@ async def test_configure_secure_knxkeys(hass: HomeAssistant): CONF_KNX_KNXKEY_PASSWORD: "password", CONF_KNX_ROUTING_BACKBONE_KEY: None, CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, + CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None, + CONF_KNX_SECURE_USER_ID: None, + CONF_KNX_SECURE_USER_PASSWORD: None, CONF_HOST: "192.168.0.1", CONF_PORT: 3675, CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", @@ -999,6 +1005,100 @@ async def test_options_flow_connection_type( } +async def test_options_flow_secure_manual_to_keyfile(hass: HomeAssistant) -> None: + """Test options flow changing secure credential source.""" + mock_config_entry = MockConfigEntry( + title="KNX", + domain="knx", + data={ + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, + CONF_KNX_SECURE_USER_ID: 2, + CONF_KNX_SECURE_USER_PASSWORD: "password", + CONF_KNX_SECURE_DEVICE_AUTHENTICATION: "device_auth", + CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", + CONF_KNX_KNXKEY_PASSWORD: "invalid_password", + CONF_HOST: "192.168.0.1", + CONF_PORT: 3675, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", + CONF_KNX_ROUTE_BACK: False, + CONF_KNX_LOCAL_IP: None, + }, + ) + mock_config_entry.add_to_hass(hass) + gateway = _gateway_descriptor( + "192.168.0.1", + 3675, + supports_tunnelling_tcp=True, + requires_secure=True, + ) + + menu_step = await hass.config_entries.options.async_init(mock_config_entry.entry_id) + with patch( + "homeassistant.components.knx.config_flow.GatewayScanner" + ) as gateway_scanner_mock: + gateway_scanner_mock.return_value = GatewayScannerMock([gateway]) + result = await hass.config_entries.options.async_configure( + menu_step["flow_id"], + {"next_step_id": "connection_type"}, + ) + assert result.get("type") == FlowResultType.FORM + assert result.get("step_id") == "connection_type" + + result2 = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, + }, + ) + assert result2["type"] == FlowResultType.FORM + assert result2["step_id"] == "tunnel" + assert not result2["errors"] + + result3 = await hass.config_entries.options.async_configure( + result2["flow_id"], + {CONF_KNX_GATEWAY: str(gateway)}, + ) + assert result3["type"] == FlowResultType.MENU + assert result3["step_id"] == "secure_key_source" + + result4 = await hass.config_entries.options.async_configure( + result3["flow_id"], + {"next_step_id": "secure_knxkeys"}, + ) + assert result4["type"] == FlowResultType.FORM + assert result4["step_id"] == "secure_knxkeys" + assert not result4["errors"] + + with patch( + "homeassistant.components.knx.config_flow.load_keyring", return_value=True + ): + secure_knxkeys = await hass.config_entries.options.async_configure( + result4["flow_id"], + { + CONF_KNX_KNXKEY_FILENAME: "testcase.knxkeys", + CONF_KNX_KNXKEY_PASSWORD: "password", + }, + ) + assert secure_knxkeys["type"] == FlowResultType.CREATE_ENTRY + assert mock_config_entry.data == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, + CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", + CONF_KNX_KNXKEY_PASSWORD: "password", + CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None, + CONF_KNX_SECURE_USER_ID: None, + CONF_KNX_SECURE_USER_PASSWORD: None, + CONF_KNX_ROUTING_BACKBONE_KEY: None, + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, + CONF_HOST: "192.168.0.1", + CONF_PORT: 3675, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", + CONF_KNX_ROUTE_BACK: False, + CONF_KNX_LOCAL_IP: None, + } + + async def test_options_communication_settings( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: From afe01c243445790f161a0bfc1239d9f8f1f6e2e9 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 10:19:47 +0100 Subject: [PATCH 0788/1033] Use attributes in demo media player (#78040) --- homeassistant/components/demo/media_player.py | 139 +++++------------- 1 file changed, 33 insertions(+), 106 deletions(-) diff --git a/homeassistant/components/demo/media_player.py b/homeassistant/components/demo/media_player.py index 646485e8f42..9d335c34cdb 100644 --- a/homeassistant/components/demo/media_player.py +++ b/homeassistant/components/demo/media_player.py @@ -105,8 +105,8 @@ NETFLIX_PLAYER_SUPPORT = ( class AbstractDemoPlayer(MediaPlayerEntity): """A demo media players.""" - _attr_sound_mode_list = SOUND_MODE_LIST _attr_should_poll = False + _attr_sound_mode_list = SOUND_MODE_LIST # We only implement the methods that we support @@ -185,6 +185,7 @@ class DemoYoutubePlayer(AbstractDemoPlayer): # We only implement the methods that we support + _attr_app_name = "YouTube" _attr_media_content_type = MediaType.MOVIE _attr_supported_features = YOUTUBE_PLAYER_SUPPORT @@ -193,36 +194,16 @@ class DemoYoutubePlayer(AbstractDemoPlayer): ) -> None: """Initialize the demo device.""" super().__init__(name) - self.youtube_id = youtube_id - self._media_title = media_title - self._duration = duration + self._attr_media_content_id = youtube_id + self._attr_media_title = media_title + self._attr_media_duration = duration self._progress: int | None = int(duration * 0.15) self._progress_updated_at = dt_util.utcnow() - @property - def media_content_id(self) -> str: - """Return the content ID of current playing media.""" - return self.youtube_id - - @property - def media_duration(self) -> int: - """Return the duration of current playing media in seconds.""" - return self._duration - @property def media_image_url(self) -> str: """Return the image url of current playing media.""" - return f"https://img.youtube.com/vi/{self.youtube_id}/hqdefault.jpg" - - @property - def media_title(self) -> str: - """Return the title of current playing media.""" - return self._media_title - - @property - def app_name(self) -> str: - """Return the current running application.""" - return "YouTube" + return f"https://img.youtube.com/vi/{self.media_content_id}/hqdefault.jpg" @property def media_position(self) -> int | None: @@ -249,9 +230,11 @@ class DemoYoutubePlayer(AbstractDemoPlayer): return self._progress_updated_at return None - def play_media(self, media_type: str, media_id: str, **kwargs: Any) -> None: + def play_media( + self, media_type: MediaType | str, media_id: str, **kwargs: Any + ) -> None: """Play a piece of media.""" - self.youtube_id = media_id + self._attr_media_content_id = media_id self.schedule_update_ha_state() def media_pause(self) -> None: @@ -266,7 +249,13 @@ class DemoMusicPlayer(AbstractDemoPlayer): # We only implement the methods that we support + _attr_media_album_name = "Bounzz" + _attr_media_content_id = "bounzz-1" _attr_media_content_type = MediaType.MUSIC + _attr_media_duration = 213 + _attr_media_image_url = ( + "https://graph.facebook.com/v2.5/107771475912710/picture?type=large" + ) _attr_supported_features = MUSIC_PLAYER_SUPPORT tracks = [ @@ -296,28 +285,8 @@ class DemoMusicPlayer(AbstractDemoPlayer): """Initialize the demo device.""" super().__init__(name) self._cur_track = 0 - self._group_members: list[str] = [] - self._repeat = RepeatMode.OFF - - @property - def group_members(self) -> list[str]: - """List of players which are currently grouped together.""" - return self._group_members - - @property - def media_content_id(self) -> str: - """Return the content ID of current playing media.""" - return "bounzz-1" - - @property - def media_duration(self) -> int: - """Return the duration of current playing media in seconds.""" - return 213 - - @property - def media_image_url(self) -> str: - """Return the image url of current playing media.""" - return "https://graph.facebook.com/v2.5/107771475912710/picture?type=large" + self._attr_group_members: list[str] = [] + self._attr_repeat = RepeatMode.OFF @property def media_title(self) -> str: @@ -329,21 +298,11 @@ class DemoMusicPlayer(AbstractDemoPlayer): """Return the artist of current playing media (Music track only).""" return self.tracks[self._cur_track][0] if self.tracks else "" - @property - def media_album_name(self) -> str: - """Return the album of current playing media (Music track only).""" - return "Bounzz" - @property def media_track(self) -> int: """Return the track number of current media (Music track only).""" return self._cur_track + 1 - @property - def repeat(self) -> RepeatMode: - """Return current repeat mode.""" - return self._repeat - def media_previous_track(self) -> None: """Send previous track command.""" if self._cur_track > 0: @@ -365,28 +324,37 @@ class DemoMusicPlayer(AbstractDemoPlayer): def set_repeat(self, repeat: RepeatMode) -> None: """Enable/disable repeat mode.""" - self._repeat = repeat + self._attr_repeat = repeat self.schedule_update_ha_state() def join_players(self, group_members: list[str]) -> None: """Join `group_members` as a player group with the current player.""" - self._group_members = [ + self._attr_group_members = [ self.entity_id, ] + group_members self.schedule_update_ha_state() def unjoin_player(self) -> None: """Remove this player from any group.""" - self._group_members = [] + self._attr_group_members = [] self.schedule_update_ha_state() class DemoTVShowPlayer(AbstractDemoPlayer): - """A Demo media player that only supports YouTube.""" + """A Demo media player that only supports Netflix.""" # We only implement the methods that we support + _attr_app_name = "Netflix" + _attr_media_content_id = "house-of-cards-1" _attr_media_content_type = MediaType.TVSHOW + _attr_media_duration = 3600 + _attr_media_image_url = ( + "https://graph.facebook.com/v2.5/HouseofCards/picture?width=400" + ) + _attr_media_season = "1" + _attr_media_series_title = "House of Cards" + _attr_source_list = ["dvd", "youtube"] _attr_supported_features = NETFLIX_PLAYER_SUPPORT def __init__(self) -> None: @@ -394,59 +362,18 @@ class DemoTVShowPlayer(AbstractDemoPlayer): super().__init__("Lounge room", MediaPlayerDeviceClass.TV) self._cur_episode = 1 self._episode_count = 13 - self._source = "dvd" - self._source_list = ["dvd", "youtube"] - - @property - def media_content_id(self) -> str: - """Return the content ID of current playing media.""" - return "house-of-cards-1" - - @property - def media_duration(self) -> int: - """Return the duration of current playing media in seconds.""" - return 3600 - - @property - def media_image_url(self) -> str: - """Return the image url of current playing media.""" - return "https://graph.facebook.com/v2.5/HouseofCards/picture?width=400" + self._attr_source = "dvd" @property def media_title(self) -> str: """Return the title of current playing media.""" return f"Chapter {self._cur_episode}" - @property - def media_series_title(self) -> str: - """Return the series title of current playing media (TV Show only).""" - return "House of Cards" - - @property - def media_season(self) -> str: - """Return the season of current playing media (TV Show only).""" - return "1" - @property def media_episode(self) -> str: """Return the episode of current playing media (TV Show only).""" return str(self._cur_episode) - @property - def app_name(self) -> str: - """Return the current running application.""" - return "Netflix" - - @property - def source(self) -> str: - """Return the current input source.""" - return self._source - - @property - def source_list(self) -> list[str]: - """List of available sources.""" - return self._source_list - def media_previous_track(self) -> None: """Send previous track command.""" if self._cur_episode > 1: @@ -461,5 +388,5 @@ class DemoTVShowPlayer(AbstractDemoPlayer): def select_source(self, source: str) -> None: """Set the input source.""" - self._source = source + self._attr_source = source self.schedule_update_ha_state() From 7a1907e540a88b741191bc0030a7a1a09e2a7e94 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 10:25:20 +0100 Subject: [PATCH 0789/1033] Enforce MediaPlayerState in denonavr media player (#82847) --- homeassistant/components/denonavr/media_player.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/denonavr/media_player.py b/homeassistant/components/denonavr/media_player.py index 32f233d5413..aa1fb7361e2 100644 --- a/homeassistant/components/denonavr/media_player.py +++ b/homeassistant/components/denonavr/media_player.py @@ -8,7 +8,7 @@ import logging from typing import Any, TypeVar from denonavr import DenonAVR -from denonavr.const import POWER_ON +from denonavr.const import POWER_ON, STATE_OFF, STATE_ON, STATE_PAUSED, STATE_PLAYING from denonavr.exceptions import ( AvrCommandError, AvrForbiddenError, @@ -78,6 +78,14 @@ _R = TypeVar("_R") _P = ParamSpec("_P") +DENON_STATE_MAPPING = { + STATE_ON: MediaPlayerState.ON, + STATE_OFF: MediaPlayerState.OFF, + STATE_PLAYING: MediaPlayerState.PLAYING, + STATE_PAUSED: MediaPlayerState.PAUSED, +} + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, @@ -243,9 +251,9 @@ class DenonDevice(MediaPlayerEntity): await self._receiver.async_update_audyssey() @property - def state(self): + def state(self) -> MediaPlayerState | None: """Return the state of the device.""" - return self._receiver.state + return DENON_STATE_MAPPING.get(self._receiver.state) @property def source_list(self): From 622466b3d33d8ed534f525873acc1754993126a7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 10:30:15 +0100 Subject: [PATCH 0790/1033] Use _attr_state in russound met media player (#82839) --- .../components/russound_rnet/media_player.py | 53 ++++--------------- 1 file changed, 9 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/russound_rnet/media_player.py b/homeassistant/components/russound_rnet/media_player.py index 6782d783a83..7a384656b66 100644 --- a/homeassistant/components/russound_rnet/media_player.py +++ b/homeassistant/components/russound_rnet/media_player.py @@ -82,15 +82,11 @@ class RussoundRNETDevice(MediaPlayerEntity): def __init__(self, hass, russ, sources, zone_id, extra): """Initialise the Russound RNET device.""" - self._name = extra["name"] + self._attr_name = extra["name"] self._russ = russ - self._sources = sources + self._attr_source_list = sources self._zone_id = zone_id - self._state = None - self._volume = None - self._source = None - def update(self) -> None: """Retrieve latest state.""" # Updated this function to make a single call to get_zone_info, so that @@ -101,47 +97,21 @@ class RussoundRNETDevice(MediaPlayerEntity): if ret is not None: _LOGGER.debug("Updating status for zone %s", self._zone_id) if ret[0] == 0: - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF else: - self._state = MediaPlayerState.ON - self._volume = ret[2] * 2 / 100.0 + self._attr_state = MediaPlayerState.ON + self._attr_volume_level = ret[2] * 2 / 100.0 # Returns 0 based index for source. index = ret[1] # Possibility exists that user has defined list of all sources. # If a source is set externally that is beyond the defined list then # an exception will be thrown. # In this case return and unknown source (None) - try: - self._source = self._sources[index] - except IndexError: - self._source = None + if self.source_list and 0 <= index < len(self.source_list): + self._attr_source = self.source_list[index] else: _LOGGER.error("Could not update status for zone %s", self._zone_id) - @property - def name(self): - """Return the name of the zone.""" - return self._name - - @property - def state(self): - """Return the state of the device.""" - return self._state - - @property - def source(self): - """Get the currently selected source.""" - return self._source - - @property - def volume_level(self): - """Volume level of the media player (0..1). - - Value is returned based on a range (0..100). - Therefore float divide by 100 to get to the required range. - """ - return self._volume - def set_volume_level(self, volume: float) -> None: """Set volume level. Volume has a range (0..1). @@ -164,12 +134,7 @@ class RussoundRNETDevice(MediaPlayerEntity): def select_source(self, source: str) -> None: """Set the input source.""" - if source in self._sources: - index = self._sources.index(source) + if self.source_list and source in self.source_list: + index = self.source_list.index(source) # 0 based value for source self._russ.set_source("1", self._zone_id, index) - - @property - def source_list(self): - """Return a list of available input sources.""" - return self._sources From cee716b89287cd76c0b50ce562487a0cc4ba0481 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 10:31:16 +0100 Subject: [PATCH 0791/1033] Use _attr_state in ziggo mediabox xl media player (#82844) --- .../ziggo_mediabox_xl/media_player.py | 50 +++++++------------ 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/ziggo_mediabox_xl/media_player.py b/homeassistant/components/ziggo_mediabox_xl/media_player.py index 48859cfb167..a0f789f1708 100644 --- a/homeassistant/components/ziggo_mediabox_xl/media_player.py +++ b/homeassistant/components/ziggo_mediabox_xl/media_player.py @@ -98,25 +98,24 @@ class ZiggoMediaboxXLDevice(MediaPlayerEntity): """Initialize the device.""" self._mediabox = mediabox self._host = host - self._name = name - self._available = available - self._state = None + self._attr_name = name + self._attr_available = available def update(self) -> None: """Retrieve the state of the device.""" try: if self._mediabox.test_connection(): if self._mediabox.turned_on(): - if self._state != MediaPlayerState.PAUSED: - self._state = MediaPlayerState.PLAYING + if self.state != MediaPlayerState.PAUSED: + self._attr_state = MediaPlayerState.PLAYING else: - self._state = MediaPlayerState.OFF - self._available = True + self._attr_state = MediaPlayerState.OFF + self._attr_available = True else: - self._available = False + self._attr_available = False except OSError: _LOGGER.error("Couldn't fetch state from %s", self._host) - self._available = False + self._attr_available = False def send_keys(self, keys): """Send keys to the device and handle exceptions.""" @@ -126,22 +125,7 @@ class ZiggoMediaboxXLDevice(MediaPlayerEntity): _LOGGER.error("Couldn't send keys to %s", self._host) @property - def name(self): - """Return the name of the device.""" - return self._name - - @property - def state(self): - """Return the state of the device.""" - return self._state - - @property - def available(self): - """Return True if the device is available.""" - return self._available - - @property - def source_list(self): + def source_list(self) -> list[str]: """List of available sources (channels).""" return [ self._mediabox.channels()[c] @@ -159,30 +143,30 @@ class ZiggoMediaboxXLDevice(MediaPlayerEntity): def media_play(self) -> None: """Send play command.""" self.send_keys(["PLAY"]) - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING def media_pause(self) -> None: """Send pause command.""" self.send_keys(["PAUSE"]) - self._state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED def media_play_pause(self) -> None: """Simulate play pause media player.""" self.send_keys(["PAUSE"]) - if self._state == MediaPlayerState.PAUSED: - self._state = MediaPlayerState.PLAYING + if self.state == MediaPlayerState.PAUSED: + self._attr_state = MediaPlayerState.PLAYING else: - self._state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED def media_next_track(self) -> None: """Channel up.""" self.send_keys(["CHAN_UP"]) - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING def media_previous_track(self) -> None: """Channel down.""" self.send_keys(["CHAN_DOWN"]) - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING def select_source(self, source): """Select the channel.""" @@ -201,4 +185,4 @@ class ZiggoMediaboxXLDevice(MediaPlayerEntity): return self.send_keys([f"NUM_{digit}" for digit in str(digits)]) - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING From 841c4083441358f94834a01689d0cae9f282adc1 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 10:33:08 +0100 Subject: [PATCH 0792/1033] Use _attr in pjlink media player (#82836) --- .../components/pjlink/media_player.py | 62 ++++++------------- 1 file changed, 18 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/pjlink/media_player.py b/homeassistant/components/pjlink/media_player.py index 0e1151e3dc4..e7c4eeea1ee 100644 --- a/homeassistant/components/pjlink/media_player.py +++ b/homeassistant/components/pjlink/media_player.py @@ -79,18 +79,17 @@ class PjLinkDevice(MediaPlayerEntity): """Iinitialize the PJLink device.""" self._host = host self._port = port - self._name = name + self._attr_name = name self._password = password self._encoding = encoding - self._muted = False - self._pwstate = MediaPlayerState.OFF - self._current_source = None + self._attr_is_volume_muted = False + self._attr_state = MediaPlayerState.OFF with self.projector() as projector: - if not self._name: - self._name = projector.get_name() + if not self._attr_name: + self._attr_name = projector.get_name() inputs = projector.get_inputs() self._source_name_mapping = {format_input_source(*x): x for x in inputs} - self._source_list = sorted(self._source_name_mapping.keys()) + self._attr_source_list = sorted(self._source_name_mapping.keys()) def projector(self): """Create PJLink Projector instance.""" @@ -108,53 +107,28 @@ class PjLinkDevice(MediaPlayerEntity): try: pwstate = projector.get_power() if pwstate in ("on", "warm-up"): - self._pwstate = MediaPlayerState.ON - self._muted = projector.get_mute()[1] - self._current_source = format_input_source(*projector.get_input()) + self._attr_state = MediaPlayerState.ON + self._attr_is_volume_muted = projector.get_mute()[1] + self._attr_source = format_input_source(*projector.get_input()) else: - self._pwstate = MediaPlayerState.OFF - self._muted = False - self._current_source = None + self._attr_state = MediaPlayerState.OFF + self._attr_is_volume_muted = False + self._attr_source = None except KeyError as err: if str(err) == "'OK'": - self._pwstate = MediaPlayerState.OFF - self._muted = False - self._current_source = None + self._attr_state = MediaPlayerState.OFF + self._attr_is_volume_muted = False + self._attr_source = None else: raise except ProjectorError as err: if str(err) == "unavailable time": - self._pwstate = MediaPlayerState.OFF - self._muted = False - self._current_source = None + self._attr_state = MediaPlayerState.OFF + self._attr_is_volume_muted = False + self._attr_source = None else: raise - @property - def name(self): - """Return the name of the device.""" - return self._name - - @property - def state(self): - """Return the state of the device.""" - return self._pwstate - - @property - def is_volume_muted(self): - """Return boolean indicating mute status.""" - return self._muted - - @property - def source(self): - """Return current input source.""" - return self._current_source - - @property - def source_list(self): - """Return all available input sources.""" - return self._source_list - def turn_off(self) -> None: """Turn projector off.""" with self.projector() as projector: From 9802c2a646ac2b336eedb89a89aa55d3822310b5 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 10:35:57 +0100 Subject: [PATCH 0793/1033] Use _attr in nad media player (#82831) --- homeassistant/components/nad/media_player.py | 93 ++++---------------- 1 file changed, 19 insertions(+), 74 deletions(-) diff --git a/homeassistant/components/nad/media_player.py b/homeassistant/components/nad/media_player.py index 531dabfde70..0e036a8cdfb 100644 --- a/homeassistant/components/nad/media_player.py +++ b/homeassistant/components/nad/media_player.py @@ -81,19 +81,19 @@ def setup_platform( class NAD(MediaPlayerEntity): """Representation of a NAD Receiver.""" + _attr_icon = "mdi:speaker-multiple" _attr_supported_features = SUPPORT_NAD def __init__(self, config): """Initialize the NAD Receiver device.""" self.config = config self._instantiate_nad_receiver() + self._attr_name = self.config[CONF_NAME] self._min_volume = config[CONF_MIN_VOLUME] self._max_volume = config[CONF_MAX_VOLUME] self._source_dict = config[CONF_SOURCE_DICT] self._reverse_mapping = {value: key for key, value in self._source_dict.items()} - self._volume = self._state = self._mute = self._source = None - def _instantiate_nad_receiver(self) -> NADReceiver: if self.config[CONF_TYPE] == "RS232": self._nad_receiver = NADReceiver(self.config[CONF_SERIAL_PORT]) @@ -102,31 +102,6 @@ class NAD(MediaPlayerEntity): port = self.config[CONF_PORT] self._nad_receiver = NADReceiverTelnet(host, port) - @property - def name(self): - """Return the name of the device.""" - return self.config[CONF_NAME] - - @property - def state(self): - """Return the state of the device.""" - return self._state - - @property - def icon(self): - """Return the icon for the device.""" - return "mdi:speaker-multiple" - - @property - def volume_level(self): - """Volume level of the media player (0..1).""" - return self._volume - - @property - def is_volume_muted(self): - """Boolean if volume is currently muted.""" - return self._mute - def turn_off(self) -> None: """Turn the media player off.""" self._nad_receiver.main_power("=", "Off") @@ -158,11 +133,6 @@ class NAD(MediaPlayerEntity): """Select input source.""" self._nad_receiver.main_source("=", self._reverse_mapping.get(source)) - @property - def source(self): - """Name of the current input source.""" - return self._source - @property def source_list(self): """List of available input sources.""" @@ -171,27 +141,31 @@ class NAD(MediaPlayerEntity): @property def available(self) -> bool: """Return if device is available.""" - return self._state is not None + return self.state is not None def update(self) -> None: """Retrieve latest state.""" power_state = self._nad_receiver.main_power("?") if not power_state: - self._state = None + self._attr_state = None return - self._state = ( + self._attr_state = ( MediaPlayerState.ON if self._nad_receiver.main_power("?") == "On" else MediaPlayerState.OFF ) - if self._state == MediaPlayerState.ON: - self._mute = self._nad_receiver.main_mute("?") == "On" + if self.state == MediaPlayerState.ON: + self._attr_is_volume_muted = self._nad_receiver.main_mute("?") == "On" volume = self._nad_receiver.main_volume("?") # Some receivers cannot report the volume, e.g. C 356BEE, # instead they only support stepping the volume up or down - self._volume = self.calc_volume(volume) if volume is not None else None - self._source = self._source_dict.get(self._nad_receiver.main_source("?")) + self._attr_volume_level = ( + self.calc_volume(volume) if volume is not None else None + ) + self._attr_source = self._source_dict.get( + self._nad_receiver.main_source("?") + ) def calc_volume(self, decibel): """ @@ -221,38 +195,14 @@ class NADtcp(MediaPlayerEntity): def __init__(self, config): """Initialize the amplifier.""" - self._name = config[CONF_NAME] + self._attr_name = config[CONF_NAME] self._nad_receiver = NADReceiverTCP(config.get(CONF_HOST)) self._min_vol = (config[CONF_MIN_VOLUME] + 90) * 2 # from dB to nad vol (0-200) self._max_vol = (config[CONF_MAX_VOLUME] + 90) * 2 # from dB to nad vol (0-200) self._volume_step = config[CONF_VOLUME_STEP] - self._state = None - self._mute = None self._nad_volume = None - self._volume = None - self._source = None self._source_list = self._nad_receiver.available_sources() - @property - def name(self): - """Return the name of the device.""" - return self._name - - @property - def state(self): - """Return the state of the device.""" - return self._state - - @property - def volume_level(self): - """Volume level of the media player (0..1).""" - return self._volume - - @property - def is_volume_muted(self): - """Boolean if volume is currently muted.""" - return self._mute - def turn_off(self) -> None: """Turn the media player off.""" self._nad_receiver.power_off() @@ -287,11 +237,6 @@ class NADtcp(MediaPlayerEntity): """Select input source.""" self._nad_receiver.select_source(source) - @property - def source(self): - """Name of the current input source.""" - return self._source - @property def source_list(self): """List of available input sources.""" @@ -308,19 +253,19 @@ class NADtcp(MediaPlayerEntity): # Update on/off state if nad_status["power"]: - self._state = MediaPlayerState.ON + self._attr_state = MediaPlayerState.ON else: - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF # Update current volume - self._volume = self.nad_vol_to_internal_vol(nad_status["volume"]) + self._attr_volume_level = self.nad_vol_to_internal_vol(nad_status["volume"]) self._nad_volume = nad_status["volume"] # Update muted state - self._mute = nad_status["muted"] + self._attr_is_volume_muted = nad_status["muted"] # Update current source - self._source = nad_status["source"] + self._attr_source = nad_status["source"] def nad_vol_to_internal_vol(self, nad_volume): """Convert nad volume range (0-200) to internal volume range. From 9ecbcd2d8fedc4f0a59f231d1f221157d4cfa359 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 10:40:32 +0100 Subject: [PATCH 0794/1033] Use _attr_state in panasonic bluray media player (#82834) --- .../panasonic_bluray/media_player.py | 64 +++++-------------- 1 file changed, 17 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/panasonic_bluray/media_player.py b/homeassistant/components/panasonic_bluray/media_player.py index 54062b36b1b..1e04d5ce53d 100644 --- a/homeassistant/components/panasonic_bluray/media_player.py +++ b/homeassistant/components/panasonic_bluray/media_player.py @@ -48,6 +48,7 @@ def setup_platform( class PanasonicBluRay(MediaPlayerEntity): """Representation of a Panasonic Blu-ray device.""" + _attr_icon = "mdi:disc-player" _attr_supported_features = ( MediaPlayerEntityFeature.TURN_ON | MediaPlayerEntityFeature.TURN_OFF @@ -59,41 +60,10 @@ class PanasonicBluRay(MediaPlayerEntity): def __init__(self, ip, name): """Initialize the Panasonic Blue-ray device.""" self._device = PanasonicBD(ip) - self._name = name - self._state = MediaPlayerState.OFF - self._position = 0 - self._duration = 0 - self._position_valid = 0 - - @property - def icon(self): - """Return a disc player icon for the device.""" - return "mdi:disc-player" - - @property - def name(self): - """Return the display name of this device.""" - return self._name - - @property - def state(self): - """Return _state variable, containing the appropriate constant.""" - return self._state - - @property - def media_duration(self): - """Duration of current playing media in seconds.""" - return self._duration - - @property - def media_position(self): - """Position of current playing media in seconds.""" - return self._position - - @property - def media_position_updated_at(self): - """When was the position of the current playing media valid.""" - return self._position_valid + self._attr_name = name + self._attr_state = MediaPlayerState.OFF + self._attr_media_position = 0 + self._attr_media_duration = 0 def update(self) -> None: """Update the internal state by querying the device.""" @@ -101,24 +71,24 @@ class PanasonicBluRay(MediaPlayerEntity): state = self._device.get_play_status() if state[0] == "error": - self._state = None + self._attr_state = None elif state[0] in ["off", "standby"]: # We map both of these to off. If it's really off we can't # turn it on, but from standby we can go to idle by pressing # POWER. - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF elif state[0] in ["paused", "stopped"]: - self._state = MediaPlayerState.IDLE + self._attr_state = MediaPlayerState.IDLE elif state[0] == "playing": - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING # Update our current media position + length if state[1] >= 0: - self._position = state[1] + self._attr_media_position = state[1] else: - self._position = 0 - self._position_valid = utcnow() - self._duration = state[2] + self._attr_media_position = 0 + self._attr_media_position_updated_at = utcnow() + self._attr_media_duration = state[2] def turn_off(self) -> None: """ @@ -129,17 +99,17 @@ class PanasonicBluRay(MediaPlayerEntity): our favour as it means the device is still accepting commands and we can thus turn it back on when desired. """ - if self._state != MediaPlayerState.OFF: + if self.state != MediaPlayerState.OFF: self._device.send_key("POWER") - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF def turn_on(self) -> None: """Wake the device back up from standby.""" - if self._state == MediaPlayerState.OFF: + if self.state == MediaPlayerState.OFF: self._device.send_key("POWER") - self._state = MediaPlayerState.IDLE + self._attr_state = MediaPlayerState.IDLE def media_play(self) -> None: """Send play command.""" From 19abba7f6ba24fe746889e33c5364702a62946bf Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 10:42:38 +0100 Subject: [PATCH 0795/1033] Use _attr in onkyo media player (#82832) --- .../components/onkyo/media_player.py | 126 ++++++------------ 1 file changed, 42 insertions(+), 84 deletions(-) diff --git a/homeassistant/components/onkyo/media_player.py b/homeassistant/components/onkyo/media_player.py index 11af0ea4013..d9de2730659 100644 --- a/homeassistant/components/onkyo/media_player.py +++ b/homeassistant/components/onkyo/media_player.py @@ -252,27 +252,25 @@ class OnkyoDevice(MediaPlayerEntity): ): """Initialize the Onkyo Receiver.""" self._receiver = receiver - self._muted = False - self._volume = 0 - self._pwstate = MediaPlayerState.OFF + self._attr_is_volume_muted = False + self._attr_volume_level = 0 + self._attr_state = MediaPlayerState.OFF if name: # not discovered - self._name = name - self._unique_id = None + self._attr_name = name else: # discovered - self._unique_id = ( + self._attr_unique_id = ( f"{receiver.info['model_name']}_{receiver.info['identifier']}" ) - self._name = self._unique_id + self._attr_name = self._attr_unique_id self._max_volume = max_volume self._receiver_max_volume = receiver_max_volume - self._current_source = None - self._source_list = list(sources.values()) + self._attr_source_list = list(sources.values()) self._source_mapping = sources self._reverse_mapping = {value: key for key, value in sources.items()} - self._attributes = {} + self._attr_extra_state_attributes = {} self._hdmi_out_supported = True self._audio_info_supported = True self._video_info_supported = True @@ -284,9 +282,9 @@ class OnkyoDevice(MediaPlayerEntity): except (ValueError, OSError, AttributeError, AssertionError): if self._receiver.command_socket: self._receiver.command_socket = None - _LOGGER.debug("Resetting connection to %s", self._name) + _LOGGER.debug("Resetting connection to %s", self.name) else: - _LOGGER.info("%s is disconnected. Attempting to reconnect", self._name) + _LOGGER.info("%s is disconnected. Attempting to reconnect", self.name) return False _LOGGER.debug("Result for %s: %s", command, result) return result @@ -298,13 +296,13 @@ class OnkyoDevice(MediaPlayerEntity): if not status: return if status[1] == "on": - self._pwstate = MediaPlayerState.ON + self._attr_state = MediaPlayerState.ON else: - self._pwstate = MediaPlayerState.OFF - self._attributes.pop(ATTR_AUDIO_INFORMATION, None) - self._attributes.pop(ATTR_VIDEO_INFORMATION, None) - self._attributes.pop(ATTR_PRESET, None) - self._attributes.pop(ATTR_VIDEO_OUT, None) + self._attr_state = MediaPlayerState.OFF + self._attr_extra_state_attributes.pop(ATTR_AUDIO_INFORMATION, None) + self._attr_extra_state_attributes.pop(ATTR_VIDEO_INFORMATION, None) + self._attr_extra_state_attributes.pop(ATTR_PRESET, None) + self._attr_extra_state_attributes.pop(ATTR_VIDEO_OUT, None) return volume_raw = self.command("volume query") @@ -331,67 +329,27 @@ class OnkyoDevice(MediaPlayerEntity): for source in sources: if source in self._source_mapping: - self._current_source = self._source_mapping[source] + self._attr_source = self._source_mapping[source] break - self._current_source = "_".join(sources) + self._attr_source = "_".join(sources) - if preset_raw and self._current_source.lower() == "radio": - self._attributes[ATTR_PRESET] = preset_raw[1] - elif ATTR_PRESET in self._attributes: - del self._attributes[ATTR_PRESET] + if preset_raw and self.source and self.source.lower() == "radio": + self._attr_extra_state_attributes[ATTR_PRESET] = preset_raw[1] + elif ATTR_PRESET in self._attr_extra_state_attributes: + del self._attr_extra_state_attributes[ATTR_PRESET] - self._muted = bool(mute_raw[1] == "on") + self._attr_is_volume_muted = bool(mute_raw[1] == "on") # AMP_VOL/MAX_RECEIVER_VOL*(MAX_VOL/100) - self._volume = volume_raw[1] / ( + self._attr_volume_level = volume_raw[1] / ( self._receiver_max_volume * self._max_volume / 100 ) if not hdmi_out_raw: return - self._attributes[ATTR_VIDEO_OUT] = ",".join(hdmi_out_raw[1]) + self._attr_extra_state_attributes[ATTR_VIDEO_OUT] = ",".join(hdmi_out_raw[1]) if hdmi_out_raw[1] == "N/A": self._hdmi_out_supported = False - @property - def unique_id(self): - """Return unique ID for this device.""" - return self._unique_id - - @property - def name(self): - """Return the name of the device.""" - return self._name - - @property - def state(self): - """Return the state of the device.""" - return self._pwstate - - @property - def volume_level(self): - """Return the volume level of the media player (0..1).""" - return self._volume - - @property - def is_volume_muted(self): - """Return boolean indicating mute status.""" - return self._muted - - @property - def source(self): - """Return the current input source of the device.""" - return self._current_source - - @property - def source_list(self): - """List of available input sources.""" - return self._source_list - - @property - def extra_state_attributes(self): - """Return device specific state attributes.""" - return self._attributes - def turn_off(self) -> None: """Turn the media player off.""" self.command("system-power standby") @@ -431,13 +389,13 @@ class OnkyoDevice(MediaPlayerEntity): def select_source(self, source: str) -> None: """Set the input source.""" - if source in self._source_list: + if self.source_list and source in self.source_list: source = self._reverse_mapping[source] self.command(f"input-selector {source}") def play_media(self, media_type: str, media_id: str, **kwargs: Any) -> None: """Play radio station by preset number.""" - source = self._reverse_mapping[self._current_source] + source = self._reverse_mapping[self._attr_source] if media_type.lower() == "radio" and source in DEFAULT_PLAYABLE_SOURCES: self.command(f"preset {media_id}") @@ -460,9 +418,9 @@ class OnkyoDevice(MediaPlayerEntity): "output_channels": _tuple_get(values, 5), "output_frequency": _tuple_get(values, 6), } - self._attributes[ATTR_AUDIO_INFORMATION] = info + self._attr_extra_state_attributes[ATTR_AUDIO_INFORMATION] = info else: - self._attributes.pop(ATTR_AUDIO_INFORMATION, None) + self._attr_extra_state_attributes.pop(ATTR_AUDIO_INFORMATION, None) def _parse_video_information(self, video_information_raw): values = _parse_onkyo_payload(video_information_raw) @@ -480,9 +438,9 @@ class OnkyoDevice(MediaPlayerEntity): "output_color_depth": _tuple_get(values, 7), "picture_mode": _tuple_get(values, 8), } - self._attributes[ATTR_VIDEO_INFORMATION] = info + self._attr_extra_state_attributes[ATTR_VIDEO_INFORMATION] = info else: - self._attributes.pop(ATTR_VIDEO_INFORMATION, None) + self._attr_extra_state_attributes.pop(ATTR_VIDEO_INFORMATION, None) class OnkyoDeviceZone(OnkyoDevice): @@ -509,9 +467,9 @@ class OnkyoDeviceZone(OnkyoDevice): if not status: return if status[1] == "on": - self._pwstate = MediaPlayerState.ON + self._attr_state = MediaPlayerState.ON else: - self._pwstate = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF return volume_raw = self.command(f"zone{self._zone}.volume=query") @@ -538,17 +496,17 @@ class OnkyoDeviceZone(OnkyoDevice): for source in current_source_tuples[1]: if source in self._source_mapping: - self._current_source = self._source_mapping[source] + self._attr_source = self._source_mapping[source] break - self._current_source = "_".join(current_source_tuples[1]) - self._muted = bool(mute_raw[1] == "on") - if preset_raw and self._current_source.lower() == "radio": - self._attributes[ATTR_PRESET] = preset_raw[1] - elif ATTR_PRESET in self._attributes: - del self._attributes[ATTR_PRESET] + self._attr_source = "_".join(current_source_tuples[1]) + self._attr_is_volume_muted = bool(mute_raw[1] == "on") + if preset_raw and self.source and self.source.lower() == "radio": + self._attr_extra_state_attributes[ATTR_PRESET] = preset_raw[1] + elif ATTR_PRESET in self._attr_extra_state_attributes: + del self._attr_extra_state_attributes[ATTR_PRESET] if self._supports_volume: # AMP_VOL/MAX_RECEIVER_VOL*(MAX_VOL/100) - self._volume = ( + self._attr_volume_level = ( volume_raw[1] / self._receiver_max_volume * (self._max_volume / 100) ) @@ -598,6 +556,6 @@ class OnkyoDeviceZone(OnkyoDevice): def select_source(self, source: str) -> None: """Set the input source.""" - if source in self._source_list: + if self.source_list and source in self.source_list: source = self._reverse_mapping[source] self.command(f"zone{self._zone}.selector={source}") From ac896121d32a4f1fcc2bbdb2ab46eafc60831c9f Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 10:47:11 +0100 Subject: [PATCH 0796/1033] Use _attr in pandora media player (#82835) --- .../components/pandora/media_player.py | 97 ++++++------------- 1 file changed, 32 insertions(+), 65 deletions(-) diff --git a/homeassistant/components/pandora/media_player.py b/homeassistant/components/pandora/media_player.py index c45c04a330d..b728767cb4e 100644 --- a/homeassistant/components/pandora/media_player.py +++ b/homeassistant/components/pandora/media_player.py @@ -81,30 +81,20 @@ class PandoraMediaPlayer(MediaPlayerEntity): def __init__(self, name): """Initialize the Pandora device.""" - self._name = name - self._player_state = MediaPlayerState.OFF - self._station = "" - self._media_title = "" - self._media_artist = "" - self._media_album = "" - self._stations = [] + self._attr_name = name + self._attr_state = MediaPlayerState.OFF + self._attr_source = "" + self._attr_media_title = "" + self._attr_media_artist = "" + self._attr_media_album_name = "" + self._attr_source_list = [] self._time_remaining = 0 - self._media_duration = 0 + self._attr_media_duration = 0 self._pianobar = None - @property - def name(self): - """Return the name of the media player.""" - return self._name - - @property - def state(self): - """Return the state of the player.""" - return self._player_state - def turn_on(self) -> None: """Turn the media player on.""" - if self._player_state != MediaPlayerState.OFF: + if self.state != MediaPlayerState.OFF: return self._pianobar = pexpect.spawn("pianobar") _LOGGER.info("Started pianobar subprocess") @@ -129,7 +119,7 @@ class PandoraMediaPlayer(MediaPlayerEntity): self._update_stations() self.update_playing_status() - self._player_state = MediaPlayerState.IDLE + self._attr_state = MediaPlayerState.IDLE self.schedule_update_ha_state() def turn_off(self) -> None: @@ -146,19 +136,19 @@ class PandoraMediaPlayer(MediaPlayerEntity): os.killpg(os.getpgid(self._pianobar.pid), signal.SIGTERM) _LOGGER.debug("Killed Pianobar subprocess") self._pianobar = None - self._player_state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF self.schedule_update_ha_state() def media_play(self) -> None: """Send play command.""" self._send_pianobar_command(SERVICE_MEDIA_PLAY_PAUSE) - self._player_state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING self.schedule_update_ha_state() def media_pause(self) -> None: """Send pause command.""" self._send_pianobar_command(SERVICE_MEDIA_PLAY_PAUSE) - self._player_state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED self.schedule_update_ha_state() def media_next_track(self) -> None: @@ -167,40 +157,17 @@ class PandoraMediaPlayer(MediaPlayerEntity): self.schedule_update_ha_state() @property - def source(self): - """Name of the current input source.""" - return self._station - - @property - def source_list(self): - """List of available input sources.""" - return self._stations - - @property - def media_title(self): + def media_title(self) -> str | None: """Title of current playing media.""" self.update_playing_status() - return self._media_title - - @property - def media_artist(self): - """Artist of current playing media, music track only.""" - return self._media_artist - - @property - def media_album_name(self): - """Album name of current playing media, music track only.""" - return self._media_album - - @property - def media_duration(self): - """Duration of current playing media in seconds.""" - return self._media_duration + return self._attr_media_title def select_source(self, source: str) -> None: """Choose a different Pandora station and play it.""" + if self.source_list is None: + return try: - station_index = self._stations.index(source) + station_index = self.source_list.index(source) except ValueError: _LOGGER.warning("Station %s is not in list", source) return @@ -208,7 +175,7 @@ class PandoraMediaPlayer(MediaPlayerEntity): self._send_station_list_command() self._pianobar.sendline(f"{station_index}") self._pianobar.expect("\r\n") - self._player_state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING def _send_station_list_command(self): """Send a station list command.""" @@ -269,8 +236,8 @@ class PandoraMediaPlayer(MediaPlayerEntity): def _update_current_station(self, response): """Update current station.""" if station_match := re.search(STATION_PATTERN, response): - self._station = station_match.group(1) - _LOGGER.debug("Got station as: %s", self._station) + self._attr_source = station_match.group(1) + _LOGGER.debug("Got station as: %s", self._attr_source) else: _LOGGER.warning("No station match") @@ -278,11 +245,11 @@ class PandoraMediaPlayer(MediaPlayerEntity): """Update info about current song.""" if song_match := re.search(CURRENT_SONG_PATTERN, response): ( - self._media_title, - self._media_artist, - self._media_album, + self._attr_media_title, + self._attr_media_artist, + self._attr_media_album_name, ) = song_match.groups() - _LOGGER.debug("Got song as: %s", self._media_title) + _LOGGER.debug("Got song as: %s", self._attr_media_title) else: _LOGGER.warning("No song match") @@ -302,12 +269,12 @@ class PandoraMediaPlayer(MediaPlayerEntity): total_seconds, ) = self._pianobar.match.groups() time_remaining = int(cur_minutes) * 60 + int(cur_seconds) - self._media_duration = int(total_minutes) * 60 + int(total_seconds) + self._attr_media_duration = int(total_minutes) * 60 + int(total_seconds) - if time_remaining not in (self._time_remaining, self._media_duration): - self._player_state = MediaPlayerState.PLAYING - elif self._player_state == MediaPlayerState.PLAYING: - self._player_state = MediaPlayerState.PAUSED + if time_remaining not in (self._time_remaining, self._attr_media_duration): + self._attr_state = MediaPlayerState.PLAYING + elif self.state == MediaPlayerState.PLAYING: + self._attr_state = MediaPlayerState.PAUSED self._time_remaining = time_remaining def _log_match(self): @@ -333,12 +300,12 @@ class PandoraMediaPlayer(MediaPlayerEntity): self._send_station_list_command() station_lines = self._pianobar.before.decode("utf-8") _LOGGER.debug("Getting stations: %s", station_lines) - self._stations = [] + self._attr_source_list = [] for line in station_lines.split("\r\n"): if match := re.search(r"\d+\).....(.+)", line): station = match.group(1).strip() _LOGGER.debug("Found station %s", station) - self._stations.append(station) + self._attr_source_list.append(station) else: _LOGGER.debug("No station match on %s", line) self._pianobar.sendcontrol("m") # press enter with blank line From 7ba234a7addf0fd9e91ebdaa598e35828b522364 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Mon, 28 Nov 2022 02:51:30 -0700 Subject: [PATCH 0797/1033] Add missing SimpliSafe alarm states (#82813) --- .../components/simplisafe/alarm_control_panel.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/simplisafe/alarm_control_panel.py b/homeassistant/components/simplisafe/alarm_control_panel.py index dc670393f18..ef3e09bc70f 100644 --- a/homeassistant/components/simplisafe/alarm_control_panel.py +++ b/homeassistant/components/simplisafe/alarm_control_panel.py @@ -15,7 +15,10 @@ from simplipy.websocket import ( EVENT_AWAY_EXIT_DELAY_BY_REMOTE, EVENT_DISARMED_BY_MASTER_PIN, EVENT_DISARMED_BY_REMOTE, + EVENT_ENTRY_DELAY, EVENT_HOME_EXIT_DELAY, + EVENT_SECRET_ALERT_TRIGGERED, + EVENT_USER_INITIATED_TEST, WebsocketEvent, ) @@ -66,9 +69,12 @@ STATE_MAP_FROM_REST_API = { SystemStates.ALARM_COUNT: STATE_ALARM_PENDING, SystemStates.AWAY: STATE_ALARM_ARMED_AWAY, SystemStates.AWAY_COUNT: STATE_ALARM_ARMING, + SystemStates.ENTRY_DELAY: STATE_ALARM_PENDING, SystemStates.EXIT_DELAY: STATE_ALARM_ARMING, SystemStates.HOME: STATE_ALARM_ARMED_HOME, + SystemStates.HOME_COUNT: STATE_ALARM_ARMING, SystemStates.OFF: STATE_ALARM_DISARMED, + SystemStates.TEST: STATE_ALARM_DISARMED, } STATE_MAP_FROM_WEBSOCKET_EVENT = { @@ -82,7 +88,10 @@ STATE_MAP_FROM_WEBSOCKET_EVENT = { EVENT_AWAY_EXIT_DELAY_BY_REMOTE: STATE_ALARM_ARMING, EVENT_DISARMED_BY_MASTER_PIN: STATE_ALARM_DISARMED, EVENT_DISARMED_BY_REMOTE: STATE_ALARM_DISARMED, + EVENT_ENTRY_DELAY: STATE_ALARM_PENDING, EVENT_HOME_EXIT_DELAY: STATE_ALARM_ARMING, + EVENT_SECRET_ALERT_TRIGGERED: STATE_ALARM_TRIGGERED, + EVENT_USER_INITIATED_TEST: STATE_ALARM_DISARMED, } WEBSOCKET_EVENTS_TO_LISTEN_FOR = ( @@ -156,13 +165,11 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity): """Set the state based on the latest REST API data.""" if self._system.alarm_going_off: self._attr_state = STATE_ALARM_TRIGGERED - elif self._system.state == SystemStates.ERROR: - self.async_increment_error_count() elif state := STATE_MAP_FROM_REST_API.get(self._system.state): self._attr_state = state self.async_reset_error_count() else: - LOGGER.error("Unknown system state (REST API): %s", self._system.state) + LOGGER.warning("Unexpected system state (REST API): %s", self._system.state) self.async_increment_error_count() async def async_alarm_disarm(self, code: str | None = None) -> None: From 4bf1475c951218f1386c08145b7be1dc07236574 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 12:23:20 +0100 Subject: [PATCH 0798/1033] Use attributes in ps4 media player (#82837) --- homeassistant/components/ps4/media_player.py | 139 ++++++------------- 1 file changed, 44 insertions(+), 95 deletions(-) diff --git a/homeassistant/components/ps4/media_player.py b/homeassistant/components/ps4/media_player.py index 5202825c85f..b5421af279b 100644 --- a/homeassistant/components/ps4/media_player.py +++ b/homeassistant/components/ps4/media_player.py @@ -67,6 +67,7 @@ async def async_setup_entry( class PS4Device(MediaPlayerEntity): """Representation of a PS4.""" + _attr_icon = ICON _attr_supported_features = ( MediaPlayerEntityFeature.TURN_OFF | MediaPlayerEntityFeature.TURN_ON @@ -80,20 +81,13 @@ class PS4Device(MediaPlayerEntity): self._entry_id = config.entry_id self._ps4 = ps4 self._host = host - self._name = name + self._attr_name = name self._region = region self._creds = creds - self._state = None - self._media_content_id = None - self._media_title = None self._media_image = None - self._media_type = None - self._source = None self._games = {} - self._source_list = [] self._retry = 0 self._disconnected = False - self._unique_id = None @callback def status_callback(self): @@ -161,7 +155,7 @@ class PS4Device(MediaPlayerEntity): def _parse_status(self): """Parse status.""" if (status := self._ps4.status) is not None: - self._games = load_games(self.hass, self._unique_id) + self._games = load_games(self.hass, self.unique_id) if self._games: self.get_source_list() @@ -172,24 +166,24 @@ class PS4Device(MediaPlayerEntity): name = status.get("running-app-name") if title_id and name is not None: - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING - if self._media_content_id != title_id: - self._media_content_id = title_id + if self.media_content_id != title_id: + self._attr_media_content_id = title_id if self._use_saved(): _LOGGER.debug("Using saved data for media: %s", title_id) return - self._media_title = name - self._source = self._media_title - self._media_type = None + self._attr_media_title = name + self._attr_source = self._attr_media_title + self._attr_media_content_type = None # Get data from PS Store. asyncio.ensure_future(self.async_get_title_data(title_id, name)) else: - if self._state != MediaPlayerState.IDLE: + if self.state != MediaPlayerState.IDLE: self.idle() else: - if self._state != MediaPlayerState.STANDBY: + if self.state != MediaPlayerState.STANDBY: self.state_standby() elif self._retry > DEFAULT_RETRIES: @@ -199,32 +193,32 @@ class PS4Device(MediaPlayerEntity): def _use_saved(self) -> bool: """Return True, Set media attrs if data is locked.""" - if self._media_content_id in self._games: - store = self._games[self._media_content_id] + if self.media_content_id in self._games: + store = self._games[self.media_content_id] # If locked get attributes from file. if store.get(ATTR_LOCKED): - self._media_title = store.get(ATTR_MEDIA_TITLE) - self._source = self._media_title + self._attr_media_title = store.get(ATTR_MEDIA_TITLE) + self._attr_source = self._attr_media_title self._media_image = store.get(ATTR_MEDIA_IMAGE_URL) - self._media_type = store.get(ATTR_MEDIA_CONTENT_TYPE) + self._attr_media_content_type = store.get(ATTR_MEDIA_CONTENT_TYPE) return True return False def idle(self): """Set states for state idle.""" self.reset_title() - self._state = MediaPlayerState.IDLE + self._attr_state = MediaPlayerState.IDLE def state_standby(self): """Set states for state standby.""" self.reset_title() - self._state = MediaPlayerState.STANDBY + self._attr_state = MediaPlayerState.STANDBY def state_unknown(self): """Set states for state unknown.""" self.reset_title() - self._state = None + self._attr_state = None if self._disconnected is False: _LOGGER.warning("PS4 could not be reached") self._disconnected = True @@ -232,10 +226,10 @@ class PS4Device(MediaPlayerEntity): def reset_title(self): """Update if there is no title.""" - self._media_title = None - self._media_content_id = None - self._media_type = None - self._source = None + self._attr_media_title = None + self._attr_media_content_id = None + self._attr_media_content_type = None + self._attr_source = None async def async_get_title_data(self, title_id, name): """Get PS Store Data.""" @@ -271,42 +265,42 @@ class PS4Device(MediaPlayerEntity): ) finally: - self._media_title = app_name or name - self._source = self._media_title + self._attr_media_title = app_name or name + self._attr_source = self._attr_media_title self._media_image = art or None - self._media_type = media_type + self._attr_media_content_type = media_type await self.hass.async_add_executor_job(self.update_list) self.async_write_ha_state() def update_list(self): """Update Game List, Correct data if different.""" - if self._media_content_id in self._games: - store = self._games[self._media_content_id] + if self.media_content_id in self._games: + store = self._games[self.media_content_id] if ( - store.get(ATTR_MEDIA_TITLE) != self._media_title + store.get(ATTR_MEDIA_TITLE) != self.media_title or store.get(ATTR_MEDIA_IMAGE_URL) != self._media_image ): - self._games.pop(self._media_content_id) + self._games.pop(self.media_content_id) - if self._media_content_id not in self._games: + if self.media_content_id not in self._games: self.add_games( - self._media_content_id, - self._media_title, + self.media_content_id, + self._attr_media_title, self._media_image, - self._media_type, + self._attr_media_content_type, ) - self._games = load_games(self.hass, self._unique_id) + self._games = load_games(self.hass, self.unique_id) self.get_source_list() - def get_source_list(self): + def get_source_list(self) -> None: """Parse data entry and update source list.""" games = [] for data in self._games.values(): games.append(data[ATTR_MEDIA_TITLE]) - self._source_list = sorted(games) + self._attr_source_list = sorted(games) def add_games(self, title_id, app_name, image, g_type, is_locked=False): """Add games to list.""" @@ -321,7 +315,7 @@ class PS4Device(MediaPlayerEntity): } } games.update(game) - save_games(self.hass, games, self._unique_id) + save_games(self.hass, games, self.unique_id) async def async_get_device_info(self, status): """Set device info for registry.""" @@ -332,7 +326,7 @@ class PS4Device(MediaPlayerEntity): d_registry = device_registry.async_get(self.hass) for entity_id, entry in e_registry.entities.items(): if entry.config_entry_id == self._entry_id: - self._unique_id = entry.unique_id + self._attr_unique_id = entry.unique_id self.entity_id = entity_id break for device in d_registry.devices.values(): @@ -358,7 +352,7 @@ class PS4Device(MediaPlayerEntity): sw_version=sw_version, ) - self._unique_id = format_unique_id(self._creds, status["host-id"]) + self._attr_unique_id = format_unique_id(self._creds, status["host-id"]) async def async_will_remove_from_hass(self) -> None: """Remove Entity from Home Assistant.""" @@ -368,17 +362,12 @@ class PS4Device(MediaPlayerEntity): self.unsubscribe_to_protocol() self.hass.data[PS4_DATA].devices.remove(self) - @property - def unique_id(self): - """Return Unique ID for entity.""" - return self._unique_id - @property def entity_picture(self): """Return picture.""" if ( - self._state == MediaPlayerState.PLAYING - and self._media_content_id is not None + self.state == MediaPlayerState.PLAYING + and self.media_content_id is not None and (image_hash := self.media_image_hash) is not None ): return ( @@ -387,53 +376,13 @@ class PS4Device(MediaPlayerEntity): ) return MEDIA_IMAGE_DEFAULT - @property - def name(self): - """Return the name of the device.""" - return self._name - - @property - def state(self): - """Return the state of the device.""" - return self._state - - @property - def icon(self): - """Icon.""" - return ICON - - @property - def media_content_id(self): - """Content ID of current playing media.""" - return self._media_content_id - - @property - def media_content_type(self): - """Content type of current playing media.""" - return self._media_type - @property def media_image_url(self): """Image url of current playing media.""" - if self._media_content_id is None: + if self.media_content_id is None: return MEDIA_IMAGE_DEFAULT return self._media_image - @property - def media_title(self): - """Title of current playing media.""" - return self._media_title - - @property - def source(self): - """Return the current input source.""" - return self._source - - @property - def source_list(self): - """List of available input sources.""" - return self._source_list - async def async_turn_off(self) -> None: """Turn off media player.""" await self._ps4.standby() @@ -468,7 +417,7 @@ class PS4Device(MediaPlayerEntity): "Starting PS4 game %s (%s) using source %s", game, title_id, source ) - await self._ps4.start_title(title_id, self._media_content_id) + await self._ps4.start_title(title_id, self.media_content_id) return _LOGGER.warning("Could not start title. '%s' is not in source list", source) From 6a17937dc3387a8f554356a071ad246cbb65ca37 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 12:25:06 +0100 Subject: [PATCH 0799/1033] Use attributes in ue smart radio media player (#82840) --- .../components/ue_smart_radio/media_player.py | 66 ++++--------------- 1 file changed, 14 insertions(+), 52 deletions(-) diff --git a/homeassistant/components/ue_smart_radio/media_player.py b/homeassistant/components/ue_smart_radio/media_player.py index 4dbbb1d5964..7fc727cf9fe 100644 --- a/homeassistant/components/ue_smart_radio/media_player.py +++ b/homeassistant/components/ue_smart_radio/media_player.py @@ -82,6 +82,7 @@ def setup_platform( class UERadioDevice(MediaPlayerEntity): """Representation of a Logitech UE Smart Radio device.""" + _attr_icon = ICON _attr_media_content_type = MediaType.MUSIC _attr_supported_features = ( MediaPlayerEntityFeature.PLAY @@ -99,13 +100,9 @@ class UERadioDevice(MediaPlayerEntity): """Initialize the Logitech UE Smart Radio device.""" self._session = session self._player_id = player_id - self._name = player_name - self._state = None - self._volume = 0 + self._attr_name = player_name + self._attr_volume_level = 0 self._last_volume = 0 - self._media_title = None - self._media_artist = None - self._media_artwork_url = None def send_command(self, command): """Send command to radio.""" @@ -128,63 +125,28 @@ class UERadioDevice(MediaPlayerEntity): ) if request["error"] is not None: - self._state = None + self._attr_state = None return if request["result"]["power"] == 0: - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF else: - self._state = PLAYBACK_DICT[request["result"]["mode"]] + self._attr_state = PLAYBACK_DICT[request["result"]["mode"]] media_info = request["result"]["playlist_loop"][0] - self._volume = request["result"]["mixer volume"] / 100 - self._media_artwork_url = media_info["artwork_url"] - self._media_title = media_info["title"] + self._attr_volume_level = request["result"]["mixer volume"] / 100 + self._attr_media_image_url = media_info["artwork_url"] + self._attr_media_title = media_info["title"] if "artist" in media_info: - self._media_artist = media_info["artist"] + self._attr_media_artist = media_info["artist"] else: - self._media_artist = media_info.get("remote_title") + self._attr_media_artist = media_info.get("remote_title") @property - def name(self): - """Return the name of the device.""" - return self._name - - @property - def state(self): - """Return the state of the device.""" - return self._state - - @property - def icon(self): - """Return the icon to use in the frontend, if any.""" - return ICON - - @property - def is_volume_muted(self): + def is_volume_muted(self) -> bool: """Boolean if volume is currently muted.""" - return self._volume <= 0 - - @property - def volume_level(self): - """Volume level of the media player (0..1).""" - return self._volume - - @property - def media_image_url(self): - """Image URL of current playing media.""" - return self._media_artwork_url - - @property - def media_artist(self): - """Artist of current playing media, music track only.""" - return self._media_artist - - @property - def media_title(self): - """Title of current playing media.""" - return self._media_title + return self.volume_level is not None and self.volume_level <= 0 def turn_on(self) -> None: """Turn on specified media player or all.""" @@ -217,7 +179,7 @@ class UERadioDevice(MediaPlayerEntity): def mute_volume(self, mute: bool) -> None: """Send mute command.""" if mute: - self._last_volume = self._volume + self._last_volume = self.volume_level self.send_command(["mixer", "volume", 0]) else: self.send_command(["mixer", "volume", self._last_volume * 100]) From a747a8f93659f6983e755bfc726629210abefdfd Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 12:25:47 +0100 Subject: [PATCH 0800/1033] Use attributes in vlc media player (#82841) --- homeassistant/components/vlc/media_player.py | 75 +++++--------------- 1 file changed, 17 insertions(+), 58 deletions(-) diff --git a/homeassistant/components/vlc/media_player.py b/homeassistant/components/vlc/media_player.py index a2f13de179d..9f28a60427c 100644 --- a/homeassistant/components/vlc/media_player.py +++ b/homeassistant/components/vlc/media_player.py @@ -67,69 +67,28 @@ class VlcDevice(MediaPlayerEntity): """Initialize the vlc device.""" self._instance = vlc.Instance(arguments) self._vlc = self._instance.media_player_new() - self._name = name - self._volume = None - self._muted = None - self._state = None - self._media_position_updated_at = None - self._media_position = None - self._media_duration = None + self._attr_name = name def update(self): """Get the latest details from the device.""" status = self._vlc.get_state() if status == vlc.State.Playing: - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING elif status == vlc.State.Paused: - self._state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED else: - self._state = MediaPlayerState.IDLE - self._media_duration = self._vlc.get_length() / 1000 - position = self._vlc.get_position() * self._media_duration - if position != self._media_position: - self._media_position_updated_at = dt_util.utcnow() - self._media_position = position + self._attr_state = MediaPlayerState.IDLE + self._attr_media_duration = self._vlc.get_length() / 1000 + position = self._vlc.get_position() * self._attr_media_duration + if position != self._attr_media_position: + self._attr_media_position_updated_at = dt_util.utcnow() + self._attr_media_position = position - self._volume = self._vlc.audio_get_volume() / 100 - self._muted = self._vlc.audio_get_mute() == 1 + self._attr_volume_level = self._vlc.audio_get_volume() / 100 + self._attr_is_volume_muted = self._vlc.audio_get_mute() == 1 return True - @property - def name(self): - """Return the name of the device.""" - return self._name - - @property - def state(self): - """Return the state of the device.""" - return self._state - - @property - def volume_level(self): - """Volume level of the media player (0..1).""" - return self._volume - - @property - def is_volume_muted(self): - """Boolean if volume is currently muted.""" - return self._muted - - @property - def media_duration(self): - """Duration of current playing media in seconds.""" - return self._media_duration - - @property - def media_position(self): - """Position of current playing media in seconds.""" - return self._media_position - - @property - def media_position_updated_at(self): - """When was the position of the current playing media valid.""" - return self._media_position_updated_at - def media_seek(self, position: float) -> None: """Seek the media to a specific location.""" track_length = self._vlc.get_length() / 1000 @@ -138,27 +97,27 @@ class VlcDevice(MediaPlayerEntity): def mute_volume(self, mute: bool) -> None: """Mute the volume.""" self._vlc.audio_set_mute(mute) - self._muted = mute + self._attr_is_volume_muted = mute def set_volume_level(self, volume: float) -> None: """Set volume level, range 0..1.""" self._vlc.audio_set_volume(int(volume * 100)) - self._volume = volume + self._attr_volume_level = volume def media_play(self) -> None: """Send play command.""" self._vlc.play() - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING def media_pause(self) -> None: """Send pause command.""" self._vlc.pause() - self._state = MediaPlayerState.PAUSED + self._attr_state = MediaPlayerState.PAUSED def media_stop(self) -> None: """Send stop command.""" self._vlc.stop() - self._state = MediaPlayerState.IDLE + self._attr_state = MediaPlayerState.IDLE async def async_play_media( self, media_type: MediaType | str, media_id: str, **kwargs: Any @@ -186,7 +145,7 @@ class VlcDevice(MediaPlayerEntity): self._vlc.play() await self.hass.async_add_executor_job(play) - self._state = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING async def async_browse_media( self, From e8a03ea8707f9e2bef92457b931397e716438829 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 12:26:24 +0100 Subject: [PATCH 0801/1033] Use attributes in xiaomi-tv media player (#82842) --- .../components/xiaomi_tv/media_player.py | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/xiaomi_tv/media_player.py b/homeassistant/components/xiaomi_tv/media_player.py index 9b9971f9568..5e045d1404a 100644 --- a/homeassistant/components/xiaomi_tv/media_player.py +++ b/homeassistant/components/xiaomi_tv/media_player.py @@ -58,6 +58,7 @@ def setup_platform( class XiaomiTV(MediaPlayerEntity): """Represent the Xiaomi TV for Home Assistant.""" + _attr_assumed_state = True _attr_supported_features = ( MediaPlayerEntityFeature.VOLUME_STEP | MediaPlayerEntityFeature.TURN_ON @@ -70,23 +71,8 @@ class XiaomiTV(MediaPlayerEntity): # Initialize the Xiaomi TV. self._tv = pymitv.TV(ip) # Default name value, only to be overridden by user. - self._name = name - self._state = MediaPlayerState.OFF - - @property - def name(self): - """Return the display name of this TV.""" - return self._name - - @property - def state(self): - """Return _state variable, containing the appropriate constant.""" - return self._state - - @property - def assumed_state(self): - """Indicate that state is assumed.""" - return True + self._attr_name = name + self._attr_state = MediaPlayerState.OFF def turn_off(self) -> None: """ @@ -96,17 +82,17 @@ class XiaomiTV(MediaPlayerEntity): because the TV won't accept any input when turned off. Thus, the user would be unable to turn the TV back on, unless it's done manually. """ - if self._state != MediaPlayerState.OFF: + if self.state != MediaPlayerState.OFF: self._tv.sleep() - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF def turn_on(self) -> None: """Wake the TV back up from sleep.""" - if self._state != MediaPlayerState.ON: + if self.state != MediaPlayerState.ON: self._tv.wake() - self._state = MediaPlayerState.ON + self._attr_state = MediaPlayerState.ON def volume_up(self) -> None: """Increase volume by one.""" From 49879fd257ed62f49288521986ec9273a12a03a1 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 13:51:09 +0100 Subject: [PATCH 0802/1033] Use attributes in yamaha media player (#82843) --- .../components/yamaha/media_player.py | 73 +++++-------------- 1 file changed, 17 insertions(+), 56 deletions(-) diff --git a/homeassistant/components/yamaha/media_player.py b/homeassistant/components/yamaha/media_player.py index 3f0b331c648..86bb0b11ec0 100644 --- a/homeassistant/components/yamaha/media_player.py +++ b/homeassistant/components/yamaha/media_player.py @@ -193,13 +193,9 @@ class YamahaDevice(MediaPlayerEntity): def __init__(self, name, receiver, source_ignore, source_names, zone_names): """Initialize the Yamaha Receiver.""" self.receiver = receiver - self._muted = False - self._volume = 0 - self._pwstate = MediaPlayerState.OFF - self._current_source = None - self._sound_mode = None - self._sound_mode_list = None - self._source_list = None + self._attr_is_volume_muted = False + self._attr_volume_level = 0 + self._attr_state = MediaPlayerState.OFF self._source_ignore = source_ignore or [] self._source_names = source_names or {} self._zone_names = zone_names or {} @@ -220,33 +216,33 @@ class YamahaDevice(MediaPlayerEntity): if self.receiver.on: if self._play_status is None: - self._pwstate = MediaPlayerState.ON + self._attr_state = MediaPlayerState.ON elif self._play_status.playing: - self._pwstate = MediaPlayerState.PLAYING + self._attr_state = MediaPlayerState.PLAYING else: - self._pwstate = MediaPlayerState.IDLE + self._attr_state = MediaPlayerState.IDLE else: - self._pwstate = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF - self._muted = self.receiver.mute - self._volume = (self.receiver.volume / 100) + 1 + self._attr_is_volume_muted = self.receiver.mute + self._attr_volume_level = (self.receiver.volume / 100) + 1 if self.source_list is None: self.build_source_list() current_source = self.receiver.input - self._current_source = self._source_names.get(current_source, current_source) + self._attr_source = self._source_names.get(current_source, current_source) self._playback_support = self.receiver.get_playback_support() self._is_playback_supported = self.receiver.is_playback_supported( - self._current_source + self._attr_source ) surround_programs = self.receiver.surround_programs() if surround_programs: - self._sound_mode = self.receiver.surround_program - self._sound_mode_list = surround_programs + self._attr_sound_mode = self.receiver.surround_program + self._attr_sound_mode_list = surround_programs else: - self._sound_mode = None - self._sound_mode_list = None + self._attr_sound_mode = None + self._attr_sound_mode_list = None def build_source_list(self): """Build the source list.""" @@ -254,7 +250,7 @@ class YamahaDevice(MediaPlayerEntity): alias: source for source, alias in self._source_names.items() } - self._source_list = sorted( + self._attr_source_list = sorted( self._source_names.get(source, source) for source in self.receiver.inputs() if source not in self._source_ignore @@ -270,41 +266,6 @@ class YamahaDevice(MediaPlayerEntity): name += f" {zone_name.replace('_', ' ')}" return name - @property - def state(self): - """Return the state of the device.""" - return self._pwstate - - @property - def volume_level(self): - """Volume level of the media player (0..1).""" - return self._volume - - @property - def is_volume_muted(self): - """Boolean if volume is currently muted.""" - return self._muted - - @property - def source(self): - """Return the current input source.""" - return self._current_source - - @property - def sound_mode(self): - """Return the current sound mode.""" - return self._sound_mode - - @property - def sound_mode_list(self): - """Return the current sound mode.""" - return self._sound_mode_list - - @property - def source_list(self): - """List of available input sources.""" - return self._source_list - @property def zone_id(self): """Return a zone_id to ensure 1 media player per zone.""" @@ -347,7 +308,7 @@ class YamahaDevice(MediaPlayerEntity): def turn_on(self) -> None: """Turn the media player on.""" self.receiver.on = True - self._volume = (self.receiver.volume / 100) + 1 + self._attr_volume_level = (self.receiver.volume / 100) + 1 def media_play(self) -> None: """Send play command.""" From caa981bbea5c0efb3d3a191ba04b050f3ea6dfba Mon Sep 17 00:00:00 2001 From: hahn-th Date: Mon, 28 Nov 2022 13:54:20 +0100 Subject: [PATCH 0803/1033] Bump homematicip 1.0.11 (#82852) fixes undefined --- .../homematicip_cloud/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/fixtures/homematicip_cloud.json | 49 +++++++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homematicip_cloud/manifest.json b/homeassistant/components/homematicip_cloud/manifest.json index 0d06d595f1b..d8d0b3e9836 100644 --- a/homeassistant/components/homematicip_cloud/manifest.json +++ b/homeassistant/components/homematicip_cloud/manifest.json @@ -3,7 +3,7 @@ "name": "HomematicIP Cloud", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homematicip_cloud", - "requirements": ["homematicip==1.0.7"], + "requirements": ["homematicip==1.0.11"], "codeowners": [], "quality_scale": "platinum", "iot_class": "cloud_push", diff --git a/requirements_all.txt b/requirements_all.txt index 623c8db02db..6ff981b2fa9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -890,7 +890,7 @@ home-assistant-frontend==20221108.0 homeconnect==0.7.2 # homeassistant.components.homematicip_cloud -homematicip==1.0.7 +homematicip==1.0.11 # homeassistant.components.home_plus_control homepluscontrol==0.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 67cadb6d083..eac4ea03896 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -670,7 +670,7 @@ home-assistant-frontend==20221108.0 homeconnect==0.7.2 # homeassistant.components.homematicip_cloud -homematicip==1.0.7 +homematicip==1.0.11 # homeassistant.components.home_plus_control homepluscontrol==0.0.5 diff --git a/tests/fixtures/homematicip_cloud.json b/tests/fixtures/homematicip_cloud.json index 5a7b0516763..1ca05d29af6 100644 --- a/tests/fixtures/homematicip_cloud.json +++ b/tests/fixtures/homematicip_cloud.json @@ -6830,6 +6830,55 @@ "serializedGlobalTradeItemNumber": "3014F7110000000000000WGC", "type": "WALL_MOUNTED_GARAGE_DOOR_CONTROLLER", "updateState": "UP_TO_DATE" + }, + "HUE00000-0000-0000-0000-000000000008": { + "connectionType": "EXTERNAL", + "deviceArchetype": "EXTERNAL", + "externalService": "HUE", + "firmwareVersion": "1.88.1", + "functionalChannels": { + "0": { + "channelId": "HUE00000-0000-0000-0000-000000000009", + "deviceId": "HUE00000-0000-0000-0000-000000000008", + "functionalChannelType": "EXTERNAL_BASE_CHANNEL", + "groupIndex": 0, + "groups": ["HUE00000-0000-0000-0000-000000000010"], + "index": 0, + "label": "", + "unreach": false + }, + "1": { + "channelId": "HUE00000-0000-0000-0000-000000000011", + "channelRole": "UNIVERSAL_LIGHT_ACTUATOR", + "colorTemperature": 3165, + "deviceId": "HUE00000-0000-0000-0000-000000000008", + "dimLevel": 0.0, + "functionalChannelType": "EXTERNAL_UNIVERSAL_LIGHT_CHANNEL", + "groupIndex": 1, + "groups": ["HUE00000-0000-0000-0000-000000000012"], + "hue": null, + "index": 1, + "label": "", + "maximumColorTemperature": 6500, + "minimalColorTemperature": 2000, + "on": false, + "saturationLevel": null, + "supportedOptionalFeatures": { + "IFeatureLightGroupActuatorChannel": true, + "IOptionalFeatureColorTemperature": true, + "IOptionalFeatureDimmerState": true + } + } + }, + "hasCustomLabel": false, + "homeId": "00000000-0000-0000-0000-000000000001", + "id": "HUE00000-0000-0000-0000-000000000008", + "label": "Hinten rechts", + "lastStatusUpdate": 1669539365772, + "modelType": "LTW013", + "permanentlyReachable": true, + "supported": true, + "type": "EXTERNAL" } }, "groups": { From 476821bbf8a7b6315e42b3145eeecd5d663be7e9 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 28 Nov 2022 13:55:04 +0100 Subject: [PATCH 0804/1033] Update pyupgrade to 3.2.2 (#82855) --- .pre-commit-config.yaml | 2 +- requirements_test_pre_commit.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a2c84d9eebe..08b71c2f49d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.1.0 + rev: v3.2.2 hooks: - id: pyupgrade args: [--py39-plus] diff --git a/requirements_test_pre_commit.txt b/requirements_test_pre_commit.txt index 2258b8c055c..472003c0998 100644 --- a/requirements_test_pre_commit.txt +++ b/requirements_test_pre_commit.txt @@ -13,5 +13,5 @@ mccabe==0.6.1 pycodestyle==2.8.0 pydocstyle==6.1.1 pyflakes==2.4.0 -pyupgrade==3.1.0 +pyupgrade==3.2.2 yamllint==1.28.0 From fa08916a0a56d2593107ebd6075a78117815fc87 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 14:11:55 +0100 Subject: [PATCH 0805/1033] Use attributes in roon media player (#82838) * Use _attr_state in roon media player * Remove more properties * Use early exit --- homeassistant/components/roon/media_player.py | 146 ++++-------------- 1 file changed, 33 insertions(+), 113 deletions(-) diff --git a/homeassistant/components/roon/media_player.py b/homeassistant/components/roon/media_player.py index c80f92834bb..227a7e5c6a1 100644 --- a/homeassistant/components/roon/media_player.py +++ b/homeassistant/components/roon/media_player.py @@ -98,24 +98,19 @@ class RoonDevice(MediaPlayerEntity): """Initialize Roon device object.""" self._remove_signal_status = None self._server = server - self._available = True - self._last_position_update = None + self._attr_available = True self._supports_standby = False - self._state = MediaPlayerState.IDLE - self._unique_id = None + self._attr_state = MediaPlayerState.IDLE self._zone_id = None self._output_id = None - self._name = DEVICE_DEFAULT_NAME - self._media_title = None - self._media_album_name = None - self._media_artist = None - self._media_position = 0 - self._media_duration = 0 - self._is_volume_muted = False - self._volume_step = 0 - self._shuffle = False - self._media_image_url = None - self._volume_level = 0 + self._attr_name = DEVICE_DEFAULT_NAME + self._attr_media_position = 0 + self._attr_media_duration = 0 + self._attr_is_volume_muted = False + self._attr_volume_step = 0 + self._attr_shuffle = False + self._attr_media_image_url = None + self._attr_volume_level = 0 self.update_data(player_data) async def async_added_to_hass(self) -> None: @@ -135,11 +130,6 @@ class RoonDevice(MediaPlayerEntity): self.update_data(player_data) self.async_write_ha_state() - @property - def available(self): - """Return True if entity is available.""" - return self._available - @property def group_members(self): """Return the grouped players.""" @@ -148,8 +138,10 @@ class RoonDevice(MediaPlayerEntity): return [self._server.entity_id(roon_name) for roon_name in roon_names] @property - def device_info(self) -> DeviceInfo: + def device_info(self) -> DeviceInfo | None: """Return the device info.""" + if self.unique_id is None: + return None if self.player_data.get("source_controls"): dev_model = self.player_data["source_controls"][0].get("display_name") return DeviceInfo( @@ -166,14 +158,14 @@ class RoonDevice(MediaPlayerEntity): self.player_data = player_data if not self.player_data["is_available"]: # this player was removed - self._available = False - self._state = MediaPlayerState.OFF + self._attr_available = False + self._attr_state = MediaPlayerState.OFF else: - self._available = True + self._attr_available = True # determine player state self.update_state() if self.state == MediaPlayerState.PLAYING: - self._last_position_update = utcnow() + self._attr_media_position_updated_at = utcnow() @classmethod def _parse_volume(cls, player_data): @@ -263,37 +255,25 @@ class RoonDevice(MediaPlayerEntity): new_state = MediaPlayerState.PAUSED else: new_state = MediaPlayerState.IDLE - self._state = new_state - self._unique_id = self.player_data["dev_id"] + self._attr_state = new_state + self._attr_unique_id = self.player_data["dev_id"] self._zone_id = self.player_data["zone_id"] self._output_id = self.player_data["output_id"] - self._shuffle = self.player_data["settings"]["shuffle"] - self._name = self.player_data["display_name"] + self._attr_shuffle = self.player_data["settings"]["shuffle"] + self._attr_name = self.player_data["display_name"] volume = RoonDevice._parse_volume(self.player_data) - self._is_volume_muted = volume["muted"] - self._volume_step = volume["step"] - self._is_volume_muted = volume["muted"] - self._volume_level = volume["level"] + self._attr_is_volume_muted = volume["muted"] + self._attr_volume_step = volume["step"] + self._attr_volume_level = volume["level"] now_playing = self._parse_now_playing(self.player_data) - self._media_title = now_playing["title"] - self._media_artist = now_playing["artist"] - self._media_album_name = now_playing["album"] - self._media_position = now_playing["position"] - self._media_duration = now_playing["duration"] - self._media_image_url = now_playing["image"] - - @property - def media_position_updated_at(self): - """When was the position of the current playing media valid.""" - # Returns value from homeassistant.util.dt.utcnow(). - return self._last_position_update - - @property - def unique_id(self): - """Return the id of this roon client.""" - return self._unique_id + self._attr_media_title = now_playing["title"] + self._attr_media_artist = now_playing["artist"] + self._attr_media_album_name = now_playing["album"] + self._attr_media_position = now_playing["position"] + self._attr_media_duration = now_playing["duration"] + self._attr_media_image_url = now_playing["image"] @property def zone_id(self): @@ -306,75 +286,15 @@ class RoonDevice(MediaPlayerEntity): return self._output_id @property - def name(self): - """Return device name.""" - return self._name - - @property - def media_title(self): - """Return title currently playing.""" - return self._media_title - - @property - def media_album_name(self): - """Album name of current playing media (Music track only).""" - return self._media_album_name - - @property - def media_artist(self): - """Artist of current playing media (Music track only).""" - return self._media_artist - - @property - def media_album_artist(self): + def media_album_artist(self) -> str | None: """Album artist of current playing media (Music track only).""" - return self._media_artist - - @property - def media_image_url(self): - """Image url of current playing media.""" - return self._media_image_url - - @property - def media_position(self): - """Return position currently playing.""" - return self._media_position - - @property - def media_duration(self): - """Return total runtime length.""" - return self._media_duration - - @property - def volume_level(self): - """Return current volume level.""" - return self._volume_level - - @property - def is_volume_muted(self): - """Return mute state.""" - return self._is_volume_muted - - @property - def volume_step(self): - """.Return volume step size.""" - return self._volume_step + return self.media_artist @property def supports_standby(self): """Return power state of source controls.""" return self._supports_standby - @property - def state(self): - """Return current playstate of the device.""" - return self._state - - @property - def shuffle(self): - """Boolean if shuffle is enabled.""" - return self._shuffle - def media_play(self) -> None: """Send play command to device.""" self._server.roonapi.playback_control(self.output_id, "play") @@ -403,7 +323,7 @@ class RoonDevice(MediaPlayerEntity): """Send seek command to device.""" self._server.roonapi.seek(self.output_id, position) # Seek doesn't cause an async update - so force one - self._media_position = position + self._attr_media_position = round(position) self.schedule_update_ha_state() def set_volume_level(self, volume: float) -> None: From 7dff44b3e97c77da9efc77cc932913021afcda7b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 28 Nov 2022 08:35:57 -0500 Subject: [PATCH 0806/1033] Fix mobile app passing incorrect device ID when scanning tag (#82820) --- .../components/mobile_app/webhook.py | 3 ++- tests/components/mobile_app/test_webhook.py | 19 ++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 7fecf461b13..cd54c4216b5 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -95,6 +95,7 @@ from .const import ( CONF_SECRET, DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, + DATA_DEVICES, DOMAIN, ERR_ENCRYPTION_ALREADY_ENABLED, ERR_ENCRYPTION_NOT_AVAILABLE, @@ -722,7 +723,7 @@ async def webhook_scan_tag( await tag.async_scan_tag( hass, data["tag_id"], - config_entry.data[ATTR_DEVICE_ID], + hass.data[DOMAIN][DATA_DEVICES][config_entry.data[CONF_WEBHOOK_ID]].id, registration_context(config_entry.data), ) return empty_okay_response() diff --git a/tests/components/mobile_app/test_webhook.py b/tests/components/mobile_app/test_webhook.py index 30036c0aba7..0794aab0fda 100644 --- a/tests/components/mobile_app/test_webhook.py +++ b/tests/components/mobile_app/test_webhook.py @@ -6,7 +6,8 @@ from unittest.mock import patch import pytest from homeassistant.components.camera import CameraEntityFeature -from homeassistant.components.mobile_app.const import CONF_SECRET +from homeassistant.components.mobile_app.const import CONF_SECRET, DOMAIN +from homeassistant.components.tag import EVENT_TAG_SCANNED from homeassistant.components.zone import DOMAIN as ZONE_DOMAIN from homeassistant.const import ( CONF_WEBHOOK_ID, @@ -16,11 +17,11 @@ from homeassistant.const import ( ) from homeassistant.core import callback from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import device_registry as dr, entity_registry as er from .const import CALL_SERVICE, FIRE_EVENT, REGISTER_CLEARTEXT, RENDER_TEMPLATE, UPDATE -from tests.common import async_mock_service +from tests.common import async_capture_events, async_mock_service def encrypt_payload(secret_key, payload, encode_json=True): @@ -840,14 +841,10 @@ async def test_webhook_camera_stream_stream_available_but_errors( async def test_webhook_handle_scan_tag(hass, create_registrations, webhook_client): """Test that we can scan tags.""" - events = [] + device = dr.async_get(hass).async_get_device({(DOMAIN, "mock-device-id")}) + assert device is not None - @callback - def store_event(event): - """Help store events.""" - events.append(event) - - hass.bus.async_listen("tag_scanned", store_event) + events = async_capture_events(hass, EVENT_TAG_SCANNED) resp = await webhook_client.post( "/api/webhook/{}".format(create_registrations[1]["webhook_id"]), @@ -860,7 +857,7 @@ async def test_webhook_handle_scan_tag(hass, create_registrations, webhook_clien assert len(events) == 1 assert events[0].data["tag_id"] == "mock-tag-id" - assert events[0].data["device_id"] == "mock-device-id" + assert events[0].data["device_id"] == device.id async def test_register_sensor_limits_state_class( From 15f95a078770cd4258802dcf42f337cbead310c6 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 28 Nov 2022 14:49:11 +0100 Subject: [PATCH 0807/1033] Update flake8 and related dependencies (#82854) --- .pre-commit-config.yaml | 14 +++++++------- requirements_test_pre_commit.txt | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 08b71c2f49d..f32640890c1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: - id: pyupgrade args: [--py39-plus] - repo: https://github.com/PyCQA/autoflake - rev: v1.7.7 + rev: v2.0.0 hooks: - id: autoflake args: @@ -30,17 +30,17 @@ repos: exclude_types: [csv, json] exclude: ^tests/fixtures/|homeassistant/generated/ - repo: https://github.com/PyCQA/flake8 - rev: 4.0.1 + rev: 6.0.0 hooks: - id: flake8 additional_dependencies: - - pycodestyle==2.8.0 - - pyflakes==2.4.0 + - pycodestyle==2.10.0 + - pyflakes==3.0.1 - flake8-docstrings==1.6.0 - pydocstyle==6.1.1 - - flake8-comprehensions==3.10.0 - - flake8-noqa==1.2.8 - - mccabe==0.6.1 + - flake8-comprehensions==3.10.1 + - flake8-noqa==1.3.0 + - mccabe==0.7.0 files: ^(homeassistant|script|tests)/.+\.py$ - repo: https://github.com/PyCQA/bandit rev: 1.7.4 diff --git a/requirements_test_pre_commit.txt b/requirements_test_pre_commit.txt index 472003c0998..f7746667654 100644 --- a/requirements_test_pre_commit.txt +++ b/requirements_test_pre_commit.txt @@ -1,17 +1,17 @@ # Automatically generated from .pre-commit-config.yaml by gen_requirements_all.py, do not edit -autoflake==1.7.7 +autoflake==2.0.0 bandit==1.7.4 black==22.10.0 codespell==2.1.0 -flake8-comprehensions==3.10.0 +flake8-comprehensions==3.10.1 flake8-docstrings==1.6.0 -flake8-noqa==1.2.8 -flake8==4.0.1 +flake8-noqa==1.3.0 +flake8==6.0.0 isort==5.10.1 -mccabe==0.6.1 -pycodestyle==2.8.0 +mccabe==0.7.0 +pycodestyle==2.10.0 pydocstyle==6.1.1 -pyflakes==2.4.0 +pyflakes==3.0.1 pyupgrade==3.2.2 yamllint==1.28.0 From 4c3d481b7bff847c408df66185ec409d539c758e Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Mon, 28 Nov 2022 15:57:52 +0200 Subject: [PATCH 0808/1033] Parametrize Switcher button tests (#82849) --- tests/components/switcher_kis/test_button.py | 52 +++++++++----------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/tests/components/switcher_kis/test_button.py b/tests/components/switcher_kis/test_button.py index 0e3431168dc..96cc7edef37 100644 --- a/tests/components/switcher_kis/test_button.py +++ b/tests/components/switcher_kis/test_button.py @@ -20,8 +20,15 @@ SWING_ON_EID = BASE_ENTITY_ID + "_vertical_swing_on" SWING_OFF_EID = BASE_ENTITY_ID + "_vertical_swing_off" +@pytest.mark.parametrize( + "entity, state", + [ + (ASSUME_ON_EID, DeviceState.ON), + (ASSUME_OFF_EID, DeviceState.OFF), + ], +) @pytest.mark.parametrize("mock_bridge", [[DEVICE]], indirect=True) -async def test_assume_button(hass: HomeAssistant, mock_bridge, mock_api): +async def test_assume_button(hass: HomeAssistant, entity, state, mock_bridge, mock_api): """Test assume on/off button.""" await init_integration(hass) assert mock_bridge @@ -37,29 +44,24 @@ async def test_assume_button(hass: HomeAssistant, mock_bridge, mock_api): await hass.services.async_call( BUTTON_DOMAIN, SERVICE_PRESS, - {ATTR_ENTITY_ID: ASSUME_ON_EID}, + {ATTR_ENTITY_ID: entity}, blocking=True, ) assert mock_api.call_count == 2 - mock_control_device.assert_called_once_with( - ANY, state=DeviceState.ON, update_state=True - ) - - mock_control_device.reset_mock() - await hass.services.async_call( - BUTTON_DOMAIN, - SERVICE_PRESS, - {ATTR_ENTITY_ID: ASSUME_OFF_EID}, - blocking=True, - ) - assert mock_api.call_count == 4 - mock_control_device.assert_called_once_with( - ANY, state=DeviceState.OFF, update_state=True - ) + mock_control_device.assert_called_once_with(ANY, state=state, update_state=True) +@pytest.mark.parametrize( + "entity, swing", + [ + (SWING_ON_EID, ThermostatSwing.ON), + (SWING_OFF_EID, ThermostatSwing.OFF), + ], +) @pytest.mark.parametrize("mock_bridge", [[DEVICE]], indirect=True) -async def test_swing_button(hass: HomeAssistant, mock_bridge, mock_api, monkeypatch): +async def test_swing_button( + hass: HomeAssistant, entity, swing, mock_bridge, mock_api, monkeypatch +): """Test vertical swing on/off button.""" monkeypatch.setattr(DEVICE, "remote_id", "ELEC7022") await init_integration(hass) @@ -76,21 +78,11 @@ async def test_swing_button(hass: HomeAssistant, mock_bridge, mock_api, monkeypa await hass.services.async_call( BUTTON_DOMAIN, SERVICE_PRESS, - {ATTR_ENTITY_ID: SWING_ON_EID}, + {ATTR_ENTITY_ID: entity}, blocking=True, ) assert mock_api.call_count == 2 - mock_control_device.assert_called_once_with(ANY, swing=ThermostatSwing.ON) - - mock_control_device.reset_mock() - await hass.services.async_call( - BUTTON_DOMAIN, - SERVICE_PRESS, - {ATTR_ENTITY_ID: SWING_OFF_EID}, - blocking=True, - ) - assert mock_api.call_count == 4 - mock_control_device.assert_called_once_with(ANY, swing=ThermostatSwing.OFF) + mock_control_device.assert_called_once_with(ANY, swing=swing) @pytest.mark.parametrize("mock_bridge", [[DEVICE]], indirect=True) From db480191adbfb5a7c588797e0a1e18c31d97efde Mon Sep 17 00:00:00 2001 From: G Johansson Date: Mon, 28 Nov 2022 15:12:22 +0100 Subject: [PATCH 0809/1033] Strict typing derivative (#82785) --- .strict-typing | 1 + homeassistant/components/derivative/sensor.py | 32 +++++++++++-------- mypy.ini | 10 ++++++ 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/.strict-typing b/.strict-typing index 7ad69dc477f..baa7f9e24f7 100644 --- a/.strict-typing +++ b/.strict-typing @@ -86,6 +86,7 @@ homeassistant.components.cpuspeed.* homeassistant.components.crownstone.* homeassistant.components.deconz.* homeassistant.components.demo.* +homeassistant.components.derivative.* homeassistant.components.device_automation.* homeassistant.components.device_tracker.* homeassistant.components.devolo_home_control.* diff --git a/homeassistant/components/derivative/sensor.py b/homeassistant/components/derivative/sensor.py index 59e661fce0b..8b8bc2f59f7 100644 --- a/homeassistant/components/derivative/sensor.py +++ b/homeassistant/components/derivative/sensor.py @@ -4,6 +4,7 @@ from __future__ import annotations from datetime import datetime, timedelta from decimal import Decimal, DecimalException import logging +from typing import TYPE_CHECKING import voluptuous as vol @@ -137,20 +138,20 @@ class DerivativeSensor(RestoreEntity, SensorEntity): def __init__( self, *, - name, - round_digits, - source_entity, - time_window, - unit_of_measurement, - unit_prefix, - unit_time, - unique_id, - ): + name: str | None, + round_digits: int, + source_entity: str, + time_window: timedelta, + unit_of_measurement: str | None, + unit_prefix: str | None, + unit_time: str, + unique_id: str | None, + ) -> None: """Initialize the derivative sensor.""" self._attr_unique_id = unique_id self._sensor_source_id = source_entity self._round_digits = round_digits - self._state = 0 + self._state: float | int | Decimal = 0 # List of tuples with (timestamp_start, timestamp_end, derivative) self._state_list: list[tuple[datetime, datetime, Decimal]] = [] @@ -231,7 +232,9 @@ class DerivativeSensor(RestoreEntity, SensorEntity): (old_state.last_updated, new_state.last_updated, new_derivative) ) - def calculate_weight(start, end, now): + def calculate_weight( + start: datetime, end: datetime, now: datetime + ) -> float: window_start = now - timedelta(seconds=self._time_window) if start < window_start: weight = (end - window_start).total_seconds() / self._time_window @@ -259,6 +262,9 @@ class DerivativeSensor(RestoreEntity, SensorEntity): ) @property - def native_value(self): + def native_value(self) -> float | int | Decimal: """Return the state of the sensor.""" - return round(self._state, self._round_digits) + value = round(self._state, self._round_digits) + if TYPE_CHECKING: + assert isinstance(value, (float, int, Decimal)) + return value diff --git a/mypy.ini b/mypy.ini index 737761ae5a5..8d15bee1460 100644 --- a/mypy.ini +++ b/mypy.ini @@ -613,6 +613,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.derivative.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.device_automation.*] check_untyped_defs = true disallow_incomplete_defs = true From f887aeedfe057682f8d5a3abd44082d02fe42758 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Mon, 28 Nov 2022 08:31:32 -0600 Subject: [PATCH 0810/1033] Fix Sonos alarm 'scheduled_today' attribute logic (#82816) fixes undefined --- homeassistant/components/sonos/switch.py | 26 +++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/sonos/switch.py b/homeassistant/components/sonos/switch.py index acf33ea34aa..bbeda6edb63 100644 --- a/homeassistant/components/sonos/switch.py +++ b/homeassistant/components/sonos/switch.py @@ -16,6 +16,7 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.event import async_track_time_change from .const import ( DATA_SONOS, @@ -91,6 +92,8 @@ FEATURE_ICONS = { ATTR_TOUCH_CONTROLS: "mdi:gesture-tap", } +WEEKEND_DAYS = (0, 6) + async def async_setup_entry( hass: HomeAssistant, @@ -233,6 +236,17 @@ class SonosAlarmEntity(SonosEntity, SwitchEntity): ) ) + async def async_write_state_daily(now: datetime.datetime) -> None: + """Update alarm state attributes each calendar day.""" + _LOGGER.debug("Updating state attributes for %s", self.name) + self.async_write_ha_state() + + self.async_on_remove( + async_track_time_change( + self.hass, async_write_state_daily, hour=0, minute=0, second=0 + ) + ) + @property def alarm(self) -> Alarm: """Return the alarm instance.""" @@ -304,14 +318,12 @@ class SonosAlarmEntity(SonosEntity, SwitchEntity): def _is_today(self) -> bool: """Return whether this alarm is scheduled for today.""" recurrence = self.alarm.recurrence - timestr = int(datetime.datetime.today().strftime("%w")) + daynum = int(datetime.datetime.today().strftime("%w")) return ( - bool(recurrence[:2] == "ON" and str(timestr) in recurrence) - or bool(recurrence == "DAILY") - or bool(recurrence == "WEEKDAYS" and int(timestr) not in [0, 7]) - or bool(recurrence == "ONCE") - or bool(recurrence == "WEEKDAYS" and int(timestr) not in [0, 7]) - or bool(recurrence == "WEEKENDS" and int(timestr) not in range(1, 7)) + recurrence in ("DAILY", "ONCE") + or (recurrence == "WEEKENDS" and daynum in WEEKEND_DAYS) + or (recurrence == "WEEKDAYS" and daynum not in WEEKEND_DAYS) + or (recurrence.startswith("ON_") and str(daynum) in recurrence) ) @property From 69d519418e5b835d1d1645b0888205007be8cb13 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 28 Nov 2022 16:33:14 +0200 Subject: [PATCH 0811/1033] Remove unnecessary truthiness checks for integration.manifest (#82319) --- script/gen_requirements_all.py | 4 ---- script/hassfest/bluetooth.py | 7 +------ script/hassfest/config_flow.py | 3 +-- script/hassfest/dependencies.py | 3 --- script/hassfest/dhcp.py | 7 +------ script/hassfest/json.py | 3 --- script/hassfest/manifest.py | 3 --- script/hassfest/mqtt.py | 7 +------ script/hassfest/requirements.py | 3 --- script/hassfest/services.py | 3 --- script/hassfest/ssdp.py | 7 +------ script/hassfest/usb.py | 7 +------ script/hassfest/zeroconf.py | 4 ---- 13 files changed, 6 insertions(+), 55 deletions(-) diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index ea77a99d2a5..6d547ab9fed 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -262,10 +262,6 @@ def gather_requirements_from_manifests( for domain in sorted(integrations): integration = integrations[domain] - if not integration.manifest: - errors.append(f"The manifest for integration {domain} is invalid.") - continue - if integration.disabled: continue diff --git a/script/hassfest/bluetooth.py b/script/hassfest/bluetooth.py index a4b2ffc8cb1..295bcac1d1e 100644 --- a/script/hassfest/bluetooth.py +++ b/script/hassfest/bluetooth.py @@ -10,12 +10,7 @@ def generate_and_validate(integrations: dict[str, Integration]) -> str: match_list = [] for domain in sorted(integrations): - integration = integrations[domain] - - if not integration.manifest or not integration.config_flow: - continue - - match_types = integration.manifest.get("bluetooth", []) + match_types = integrations[domain].manifest.get("bluetooth", []) if not match_types: continue diff --git a/script/hassfest/config_flow.py b/script/hassfest/config_flow.py index 2c3988cc488..e37075c5f75 100644 --- a/script/hassfest/config_flow.py +++ b/script/hassfest/config_flow.py @@ -70,8 +70,7 @@ def _generate_and_validate(integrations: dict[str, Integration], config: Config) for domain in sorted(integrations): integration = integrations[domain] - - if not integration.manifest or not integration.config_flow: + if not integration.config_flow: continue _validate_integration(config, integration) diff --git a/script/hassfest/dependencies.py b/script/hassfest/dependencies.py index fae628270af..fd2b340aa3f 100644 --- a/script/hassfest/dependencies.py +++ b/script/hassfest/dependencies.py @@ -249,9 +249,6 @@ def validate(integrations: dict[str, Integration], config: Config) -> None: """Handle dependencies for integrations.""" # check for non-existing dependencies for integration in integrations.values(): - if not integration.manifest: - continue - validate_dependencies(integrations, integration) if config.specific_integrations: diff --git a/script/hassfest/dhcp.py b/script/hassfest/dhcp.py index c143f5940c1..882e39f300e 100644 --- a/script/hassfest/dhcp.py +++ b/script/hassfest/dhcp.py @@ -10,12 +10,7 @@ def generate_and_validate(integrations: dict[str, Integration]) -> str: match_list = [] for domain in sorted(integrations): - integration = integrations[domain] - - if not integration.manifest or not integration.config_flow: - continue - - match_types = integration.manifest.get("dhcp", []) + match_types = integrations[domain].manifest.get("dhcp", []) if not match_types: continue diff --git a/script/hassfest/json.py b/script/hassfest/json.py index f0eae9f4152..5e3d05f78dc 100644 --- a/script/hassfest/json.py +++ b/script/hassfest/json.py @@ -25,7 +25,4 @@ def validate(integrations: dict[str, Integration], config: Config) -> None: return for integration in integrations.values(): - if not integration.manifest: - continue - validate_json_files(integration) diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py index ecf04648330..3c1f90a67d0 100644 --- a/script/hassfest/manifest.py +++ b/script/hassfest/manifest.py @@ -299,9 +299,6 @@ def validate_version(integration: Integration) -> None: def validate_manifest(integration: Integration, core_components_dir: Path) -> None: """Validate manifest.""" - if not integration.manifest: - return - try: if integration.core: manifest_schema(integration.manifest) diff --git a/script/hassfest/mqtt.py b/script/hassfest/mqtt.py index d83cb95391a..2619e22911a 100644 --- a/script/hassfest/mqtt.py +++ b/script/hassfest/mqtt.py @@ -13,12 +13,7 @@ def generate_and_validate(integrations: dict[str, Integration]) -> str: data = defaultdict(list) for domain in sorted(integrations): - integration = integrations[domain] - - if not integration.manifest or not integration.config_flow: - continue - - mqtt = integration.manifest.get("mqtt") + mqtt = integrations[domain].manifest.get("mqtt") if not mqtt: continue diff --git a/script/hassfest/requirements.py b/script/hassfest/requirements.py index 74fed8b7c7c..27dd0654dc6 100644 --- a/script/hassfest/requirements.py +++ b/script/hassfest/requirements.py @@ -67,9 +67,6 @@ def validate(integrations: dict[str, Integration], config: Config) -> None: disable_tqdm = bool(config.specific_integrations or os.environ.get("CI")) for integration in tqdm(integrations.values(), disable=disable_tqdm): - if not integration.manifest: - continue - validate_requirements(integration) diff --git a/script/hassfest/services.py b/script/hassfest/services.py index f7070d2dc5f..d9351b80370 100644 --- a/script/hassfest/services.py +++ b/script/hassfest/services.py @@ -97,7 +97,4 @@ def validate(integrations: dict[str, Integration], config: Config) -> None: """Handle dependencies for integrations.""" # check services.yaml is cool for integration in integrations.values(): - if not integration.manifest: - continue - validate_services(integration) diff --git a/script/hassfest/ssdp.py b/script/hassfest/ssdp.py index aebead6e97f..de548ee2992 100644 --- a/script/hassfest/ssdp.py +++ b/script/hassfest/ssdp.py @@ -13,12 +13,7 @@ def generate_and_validate(integrations: dict[str, Integration]) -> str: data = defaultdict(list) for domain in sorted(integrations): - integration = integrations[domain] - - if not integration.manifest or not integration.config_flow: - continue - - ssdp = integration.manifest.get("ssdp") + ssdp = integrations[domain].manifest.get("ssdp") if not ssdp: continue diff --git a/script/hassfest/usb.py b/script/hassfest/usb.py index 5420957e201..c5e3148f7b1 100644 --- a/script/hassfest/usb.py +++ b/script/hassfest/usb.py @@ -10,12 +10,7 @@ def generate_and_validate(integrations: dict[str, Integration]) -> str: match_list = [] for domain in sorted(integrations): - integration = integrations[domain] - - if not integration.manifest or not integration.config_flow: - continue - - match_types = integration.manifest.get("usb", []) + match_types = integrations[domain].manifest.get("usb", []) if not match_types: continue diff --git a/script/hassfest/zeroconf.py b/script/hassfest/zeroconf.py index 7f8687bded5..76a7be45c34 100644 --- a/script/hassfest/zeroconf.py +++ b/script/hassfest/zeroconf.py @@ -16,10 +16,6 @@ def generate_and_validate(integrations: dict[str, Integration]) -> str: for domain in sorted(integrations): integration = integrations[domain] - - if not integration.manifest or not integration.config_flow: - continue - service_types = integration.manifest.get("zeroconf", []) homekit = integration.manifest.get("homekit", {}) homekit_models = homekit.get("models", []) From effa9940ff185289dbad9ab8c88b72f7067184f5 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 28 Nov 2022 15:42:08 +0100 Subject: [PATCH 0812/1033] Save migrated store data (#82523) --- homeassistant/helpers/storage.py | 1 + tests/helpers/test_storage.py | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/helpers/storage.py b/homeassistant/helpers/storage.py index 6819a1eb48b..44a0da7866b 100644 --- a/homeassistant/helpers/storage.py +++ b/homeassistant/helpers/storage.py @@ -177,6 +177,7 @@ class Store(Generic[_T]): if data["version"] != self.version: raise stored = data["data"] + await self.async_save(stored) return stored diff --git a/tests/helpers/test_storage.py b/tests/helpers/test_storage.py index ca5cb92bfd5..0fcfbc46ef9 100644 --- a/tests/helpers/test_storage.py +++ b/tests/helpers/test_storage.py @@ -394,13 +394,13 @@ async def test_migration(hass, hass_storage, store_v_1_2): } assert calls == 0 - legacy_store = CustomStore(hass, 2, store_v_1_2.key, minor_version=1) - data = await legacy_store.async_load() + custom_store = CustomStore(hass, 2, store_v_1_2.key, minor_version=1) + data = await custom_store.async_load() assert calls == 1 assert hass_storage[store_v_1_2.key]["data"] == data - await legacy_store.async_save(MOCK_DATA) - assert hass_storage[legacy_store.key] == { + # Assert the migrated data has been saved + assert hass_storage[custom_store.key] == { "key": MOCK_KEY, "version": 2, "minor_version": 1, @@ -433,7 +433,7 @@ async def test_legacy_migration(hass, hass_storage, store_v_1_2): assert calls == 1 assert hass_storage[store_v_1_2.key]["data"] == data - await legacy_store.async_save(MOCK_DATA) + # Assert the migrated data has been saved assert hass_storage[legacy_store.key] == { "key": MOCK_KEY, "version": 2, From 42701a6872613c178f4aebc0ccd48a6bc72491bd Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 28 Nov 2022 16:41:14 +0100 Subject: [PATCH 0813/1033] Improve type hints in media player state (#82845) * Improve type hints in media player state * Reduce size of PR --- homeassistant/components/heos/media_player.py | 2 +- homeassistant/components/russound_rio/media_player.py | 3 ++- homeassistant/components/xbox/media_player.py | 4 ++-- homeassistant/components/yamaha_musiccast/media_player.py | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/heos/media_player.py b/homeassistant/components/heos/media_player.py index 0a5bcdc4d0a..cca8ad2bf4f 100644 --- a/homeassistant/components/heos/media_player.py +++ b/homeassistant/components/heos/media_player.py @@ -413,7 +413,7 @@ class HeosMediaPlayer(MediaPlayerEntity): return self._source_manager.source_list @property - def state(self) -> str: + def state(self) -> MediaPlayerState: """State of the player.""" return PLAY_STATE_TO_STATE[self._player.state] diff --git a/homeassistant/components/russound_rio/media_player.py b/homeassistant/components/russound_rio/media_player.py index c639e5ddc90..fc0dca341a8 100644 --- a/homeassistant/components/russound_rio/media_player.py +++ b/homeassistant/components/russound_rio/media_player.py @@ -126,13 +126,14 @@ class RussoundZoneDevice(MediaPlayerEntity): return self._zone_var("name", self._name) @property - def state(self): + def state(self) -> MediaPlayerState | None: """Return the state of the device.""" status = self._zone_var("status", "OFF") if status == "ON": return MediaPlayerState.ON if status == "OFF": return MediaPlayerState.OFF + return None @property def source(self): diff --git a/homeassistant/components/xbox/media_player.py b/homeassistant/components/xbox/media_player.py index 71ec382f04a..1d56cfc71c5 100644 --- a/homeassistant/components/xbox/media_player.py +++ b/homeassistant/components/xbox/media_player.py @@ -43,7 +43,7 @@ SUPPORT_XBOX = ( | MediaPlayerEntityFeature.PLAY_MEDIA ) -XBOX_STATE_MAP = { +XBOX_STATE_MAP: dict[PlaybackState | PowerState, MediaPlayerState | None] = { PlaybackState.Playing: MediaPlayerState.PLAYING, PlaybackState.Paused: MediaPlayerState.PAUSED, PowerState.On: MediaPlayerState.ON, @@ -99,7 +99,7 @@ class XboxMediaPlayer(CoordinatorEntity[XboxUpdateCoordinator], MediaPlayerEntit return self.coordinator.data.consoles[self._console.id] @property - def state(self): + def state(self) -> MediaPlayerState | None: """State of the player.""" status = self.data.status if status.playback_state in XBOX_STATE_MAP: diff --git a/homeassistant/components/yamaha_musiccast/media_player.py b/homeassistant/components/yamaha_musiccast/media_player.py index 9eb1be3c0fe..0a6203eddf2 100644 --- a/homeassistant/components/yamaha_musiccast/media_player.py +++ b/homeassistant/components/yamaha_musiccast/media_player.py @@ -145,7 +145,7 @@ class MusicCastMediaPlayer(MusicCastDeviceEntity, MediaPlayerEntity): return None @property - def state(self): + def state(self) -> MediaPlayerState: """Return the state of the player.""" if self.coordinator.data.zones[self._zone_id].power == "on": if self._is_netusb and self.coordinator.data.netusb_playback == "pause": From d72802cfb00f4a8eabd9959d7f3b9f19a830007a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 28 Nov 2022 16:42:20 +0100 Subject: [PATCH 0814/1033] Spelling - runtime and test changes (#82868) --- homeassistant/components/nzbget/const.py | 2 +- tests/components/octoprint/test_button.py | 24 +++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/nzbget/const.py b/homeassistant/components/nzbget/const.py index 673f2531a53..928487738eb 100644 --- a/homeassistant/components/nzbget/const.py +++ b/homeassistant/components/nzbget/const.py @@ -5,7 +5,7 @@ DOMAIN = "nzbget" ATTR_SPEED = "speed" # Data -DATA_COORDINATOR = "corrdinator" +DATA_COORDINATOR = "coordinator" DATA_UNDO_UPDATE_LISTENER = "undo_update_listener" # Defaults diff --git a/tests/components/octoprint/test_button.py b/tests/components/octoprint/test_button.py index 644c1e39437..cc99a8e4c1d 100644 --- a/tests/components/octoprint/test_button.py +++ b/tests/components/octoprint/test_button.py @@ -18,13 +18,13 @@ async def test_pause_job(hass: HomeAssistant): """Test the pause job button.""" await init_integration(hass, BUTTON_DOMAIN) - corrdinator: OctoprintDataUpdateCoordinator = hass.data[DOMAIN]["uuid"][ + coordinator: OctoprintDataUpdateCoordinator = hass.data[DOMAIN]["uuid"][ "coordinator" ] # Test pausing the printer when it is printing with patch("pyoctoprintapi.OctoprintClient.pause_job") as pause_command: - corrdinator.data["printer"] = OctoprintPrinterInfo( + coordinator.data["printer"] = OctoprintPrinterInfo( {"state": {"flags": {"printing": True}}, "temperature": []} ) await hass.services.async_call( @@ -40,7 +40,7 @@ async def test_pause_job(hass: HomeAssistant): # Test pausing the printer when it is paused with patch("pyoctoprintapi.OctoprintClient.pause_job") as pause_command: - corrdinator.data["printer"] = OctoprintPrinterInfo( + coordinator.data["printer"] = OctoprintPrinterInfo( {"state": {"flags": {"printing": False, "paused": True}}, "temperature": []} ) await hass.services.async_call( @@ -58,7 +58,7 @@ async def test_pause_job(hass: HomeAssistant): with patch( "pyoctoprintapi.OctoprintClient.pause_job" ) as pause_command, pytest.raises(InvalidPrinterState): - corrdinator.data["printer"] = OctoprintPrinterInfo( + coordinator.data["printer"] = OctoprintPrinterInfo( { "state": {"flags": {"printing": False, "paused": False}}, "temperature": [], @@ -78,13 +78,13 @@ async def test_resume_job(hass: HomeAssistant): """Test the resume job button.""" await init_integration(hass, BUTTON_DOMAIN) - corrdinator: OctoprintDataUpdateCoordinator = hass.data[DOMAIN]["uuid"][ + coordinator: OctoprintDataUpdateCoordinator = hass.data[DOMAIN]["uuid"][ "coordinator" ] # Test resuming the printer when it is paused with patch("pyoctoprintapi.OctoprintClient.resume_job") as resume_command: - corrdinator.data["printer"] = OctoprintPrinterInfo( + coordinator.data["printer"] = OctoprintPrinterInfo( {"state": {"flags": {"printing": False, "paused": True}}, "temperature": []} ) await hass.services.async_call( @@ -100,7 +100,7 @@ async def test_resume_job(hass: HomeAssistant): # Test resuming the printer when it is printing with patch("pyoctoprintapi.OctoprintClient.resume_job") as resume_command: - corrdinator.data["printer"] = OctoprintPrinterInfo( + coordinator.data["printer"] = OctoprintPrinterInfo( {"state": {"flags": {"printing": True, "paused": False}}, "temperature": []} ) await hass.services.async_call( @@ -118,7 +118,7 @@ async def test_resume_job(hass: HomeAssistant): with patch( "pyoctoprintapi.OctoprintClient.resume_job" ) as resume_command, pytest.raises(InvalidPrinterState): - corrdinator.data["printer"] = OctoprintPrinterInfo( + coordinator.data["printer"] = OctoprintPrinterInfo( { "state": {"flags": {"printing": False, "paused": False}}, "temperature": [], @@ -138,13 +138,13 @@ async def test_stop_job(hass: HomeAssistant): """Test the stop job button.""" await init_integration(hass, BUTTON_DOMAIN) - corrdinator: OctoprintDataUpdateCoordinator = hass.data[DOMAIN]["uuid"][ + coordinator: OctoprintDataUpdateCoordinator = hass.data[DOMAIN]["uuid"][ "coordinator" ] # Test stopping the printer when it is paused with patch("pyoctoprintapi.OctoprintClient.cancel_job") as stop_command: - corrdinator.data["printer"] = OctoprintPrinterInfo( + coordinator.data["printer"] = OctoprintPrinterInfo( {"state": {"flags": {"printing": False, "paused": True}}, "temperature": []} ) await hass.services.async_call( @@ -160,7 +160,7 @@ async def test_stop_job(hass: HomeAssistant): # Test stopping the printer when it is printing with patch("pyoctoprintapi.OctoprintClient.cancel_job") as stop_command: - corrdinator.data["printer"] = OctoprintPrinterInfo( + coordinator.data["printer"] = OctoprintPrinterInfo( {"state": {"flags": {"printing": True, "paused": False}}, "temperature": []} ) await hass.services.async_call( @@ -176,7 +176,7 @@ async def test_stop_job(hass: HomeAssistant): # Test stopping the printer when it is stopped with patch("pyoctoprintapi.OctoprintClient.cancel_job") as stop_command: - corrdinator.data["printer"] = OctoprintPrinterInfo( + coordinator.data["printer"] = OctoprintPrinterInfo( { "state": {"flags": {"printing": False, "paused": False}}, "temperature": [], From 63d519c1a896c6eb20f7ffb032cb7712bbac6b5c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 28 Nov 2022 16:51:43 +0100 Subject: [PATCH 0815/1033] Spelling updates (#82867) --- homeassistant/components/amcrest/camera.py | 2 +- homeassistant/components/ebusd/__init__.py | 2 +- homeassistant/components/huawei_lte/__init__.py | 2 +- homeassistant/components/jewish_calendar/sensor.py | 2 +- homeassistant/components/knx/schema.py | 2 +- homeassistant/components/meteo_france/weather.py | 2 +- homeassistant/components/nextbus/sensor.py | 2 +- homeassistant/components/onvif/event.py | 2 +- homeassistant/components/rtsp_to_webrtc/config_flow.py | 2 +- homeassistant/components/stream/__init__.py | 2 +- homeassistant/components/stream/diagnostics.py | 2 +- homeassistant/components/tasmota/__init__.py | 2 +- homeassistant/components/wilight/support.py | 2 +- homeassistant/helpers/service.py | 2 +- script/hassfest/dependencies.py | 2 +- tests/components/cpuspeed/conftest.py | 2 +- tests/components/lcn/test_config_flow.py | 2 +- tests/components/logbook/test_websocket_api.py | 2 +- tests/components/nest/test_config_flow_sdm.py | 2 +- tests/components/rtsp_to_webrtc/conftest.py | 2 +- tests/components/rtsp_to_webrtc/test_init.py | 2 +- tests/components/zha/test_climate.py | 2 +- tests/helpers/test_entityfilter.py | 6 +++--- tests/test_data_entry_flow.py | 2 +- tests/test_setup.py | 2 +- 25 files changed, 27 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/amcrest/camera.py b/homeassistant/components/amcrest/camera.py index df742b320db..9162d7841d1 100644 --- a/homeassistant/components/amcrest/camera.py +++ b/homeassistant/components/amcrest/camera.py @@ -225,7 +225,7 @@ class AmcrestCam(Camera): # Amcrest cameras only support one snapshot command at a time. # Hence need to wait if a previous snapshot has not yet finished. # Also need to check that camera is online and turned on before each wait - # and before initiating shapshot. + # and before initiating snapshot. while self._snapshot_task: self._check_snapshot_ok() _LOGGER.debug("Waiting for previous snapshot from %s", self._name) diff --git a/homeassistant/components/ebusd/__init__.py b/homeassistant/components/ebusd/__init__.py index 776a573faf6..14158b6c379 100644 --- a/homeassistant/components/ebusd/__init__.py +++ b/homeassistant/components/ebusd/__init__.py @@ -111,7 +111,7 @@ class EbusdData: raise RuntimeError(err) from err def write(self, call: ServiceCall) -> None: - """Call write methon on ebusd.""" + """Call write method on ebusd.""" name = call.data.get("name") value = call.data.get("value") diff --git a/homeassistant/components/huawei_lte/__init__.py b/homeassistant/components/huawei_lte/__init__.py index 17646fa3ed6..5a3461b02e9 100644 --- a/homeassistant/components/huawei_lte/__init__.py +++ b/homeassistant/components/huawei_lte/__init__.py @@ -183,7 +183,7 @@ class Router: if not self.subscriptions.get(key): return if key in self.inflight_gets: - _LOGGER.debug("Skipping already inflight get for %s", key) + _LOGGER.debug("Skipping already in-flight get for %s", key) return self.inflight_gets.add(key) _LOGGER.debug("Getting %s for subscribers %s", key, self.subscriptions[key]) diff --git a/homeassistant/components/jewish_calendar/sensor.py b/homeassistant/components/jewish_calendar/sensor.py index d9e1d55afbf..b20364f7e64 100644 --- a/homeassistant/components/jewish_calendar/sensor.py +++ b/homeassistant/components/jewish_calendar/sensor.py @@ -267,7 +267,7 @@ class JewishCalendarSensor(SensorEntity): class JewishCalendarTimeSensor(JewishCalendarSensor): - """Implement attrbutes for sensors returning times.""" + """Implement attributes for sensors returning times.""" _attr_device_class = SensorDeviceClass.TIMESTAMP diff --git a/homeassistant/components/knx/schema.py b/homeassistant/components/knx/schema.py index 3850119f387..370eaa7303e 100644 --- a/homeassistant/components/knx/schema.py +++ b/homeassistant/components/knx/schema.py @@ -134,7 +134,7 @@ def number_limit_sub_validator(entity_config: OrderedDict) -> OrderedDict: if dpt_class is None: raise vol.Invalid(f"'type: {value_type}' is not a valid numeric sensor type.") - # Inifinity is not supported by Home Assistant frontend so user defined + # Infinity is not supported by Home Assistant frontend so user defined # config is required if if xknx DPTNumeric subclass defines it as limit. if min_config is None and dpt_class.value_min == float("-inf"): raise vol.Invalid(f"'min' key required for value type '{value_type}'") diff --git a/homeassistant/components/meteo_france/weather.py b/homeassistant/components/meteo_france/weather.py index 2727b4470c1..787b82854af 100644 --- a/homeassistant/components/meteo_france/weather.py +++ b/homeassistant/components/meteo_france/weather.py @@ -178,7 +178,7 @@ class MeteoFranceWeather(CoordinatorEntity, WeatherEntity): ) else: for forecast in self.coordinator.data.daily_forecast: - # stop when we don't have a weather condition (can happen around last days of forcast, max 14) + # stop when we don't have a weather condition (can happen around last days of forecast, max 14) if not forecast.get("weather12H"): break forecast_data.append( diff --git a/homeassistant/components/nextbus/sensor.py b/homeassistant/components/nextbus/sensor.py index 5ab5d79caf7..4f24a7aa7f3 100644 --- a/homeassistant/components/nextbus/sensor.py +++ b/homeassistant/components/nextbus/sensor.py @@ -122,7 +122,7 @@ class NextBusDepartureSensor(SensorEntity): both the route and the stop. This is possibly a little convoluted to provide as it requires making a - request to the service to get these values. Perhaps it can be simplifed in + request to the service to get these values. Perhaps it can be simplified in the future using fuzzy logic and matching. """ diff --git a/homeassistant/components/onvif/event.py b/homeassistant/components/onvif/event.py index 2dd5d226e37..710e84f93bf 100644 --- a/homeassistant/components/onvif/event.py +++ b/homeassistant/components/onvif/event.py @@ -176,7 +176,7 @@ class EventManager: ).total_seconds() < 7200: await self.async_renew() except RemoteProtocolError: - # Likley a shutdown event, nothing to see here + # Likely a shutdown event, nothing to see here return except SUBSCRIPTION_ERRORS as err: LOGGER.warning( diff --git a/homeassistant/components/rtsp_to_webrtc/config_flow.py b/homeassistant/components/rtsp_to_webrtc/config_flow.py index 865a6bafcb6..f66a0a30d8c 100644 --- a/homeassistant/components/rtsp_to_webrtc/config_flow.py +++ b/homeassistant/components/rtsp_to_webrtc/config_flow.py @@ -73,7 +73,7 @@ class RTSPToWebRTCConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return None async def async_step_hassio(self, discovery_info: HassioServiceInfo) -> FlowResult: - """Prepare confiugration for the RTSPtoWebRTC server add-on discovery.""" + """Prepare configuration for the RTSPtoWebRTC server add-on discovery.""" if self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index acc242006ca..02aad1126f8 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -112,7 +112,7 @@ def create_stream( dynamic_stream_settings: DynamicStreamSettings, stream_label: str | None = None, ) -> Stream: - """Create a stream with the specified identfier based on the source url. + """Create a stream with the specified identifier based on the source url. The stream_source is typically an rtsp url (though any url accepted by ffmpeg is fine) and options (see STREAM_OPTIONS_SCHEMA) are converted and passed into pyav / ffmpeg. diff --git a/homeassistant/components/stream/diagnostics.py b/homeassistant/components/stream/diagnostics.py index 47370eeb5f9..13fd70cc957 100644 --- a/homeassistant/components/stream/diagnostics.py +++ b/homeassistant/components/stream/diagnostics.py @@ -19,7 +19,7 @@ class Diagnostics: self._values: dict[str, Any] = {} def increment(self, key: str) -> None: - """Increment a counter for the spcified key/event.""" + """Increment a counter for the specified key/event.""" self._counter.update(Counter({key: 1})) def set_value(self, key: str, value: Any) -> None: diff --git a/homeassistant/components/tasmota/__init__.py b/homeassistant/components/tasmota/__init__.py index da9d7e0d53b..2123ee74f1b 100644 --- a/homeassistant/components/tasmota/__init__.py +++ b/homeassistant/components/tasmota/__init__.py @@ -102,7 +102,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: for platform in PLATFORMS: hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format(platform))() - # deattach device triggers + # detach device triggers device_registry = dr.async_get(hass) devices = async_entries_for_config_entry(device_registry, entry.entry_id) for device in devices: diff --git a/homeassistant/components/wilight/support.py b/homeassistant/components/wilight/support.py index 6a03a854c70..9470bf74c8a 100644 --- a/homeassistant/components/wilight/support.py +++ b/homeassistant/components/wilight/support.py @@ -43,7 +43,7 @@ def wilight_trigger(value: Any) -> str | None: if (step == 6) & result_60: step = 7 - err_desc = "Active part shoul be less than 2" + err_desc = "Active part should be less than 2" if (step == 7) & result_2: return value diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index ad90998cd7d..74f8d088ffc 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -437,7 +437,7 @@ def _load_services_file(hass: HomeAssistant, integration: Integration) -> JSON_T def _load_services_files( hass: HomeAssistant, integrations: Iterable[Integration] ) -> list[JSON_TYPE]: - """Load service files for multiple intergrations.""" + """Load service files for multiple integrations.""" return [_load_services_file(hass, integration) for integration in integrations] diff --git a/script/hassfest/dependencies.py b/script/hassfest/dependencies.py index fd2b340aa3f..9993d5c52a9 100644 --- a/script/hassfest/dependencies.py +++ b/script/hassfest/dependencies.py @@ -177,7 +177,7 @@ def find_non_referenced_integrations( integration: Integration, references: dict[Path, set[str]], ) -> set[str]: - """Find intergrations that are not allowed to be referenced.""" + """Find integrations that are not allowed to be referenced.""" allowed_references = calc_allowed_references(integration) referenced = set() for path, refs in references.items(): diff --git a/tests/components/cpuspeed/conftest.py b/tests/components/cpuspeed/conftest.py index a5d7d1837ba..be5a87b8d13 100644 --- a/tests/components/cpuspeed/conftest.py +++ b/tests/components/cpuspeed/conftest.py @@ -27,7 +27,7 @@ def mock_config_entry() -> MockConfigEntry: def mock_cpuinfo_config_flow() -> Generator[MagicMock, None, None]: """Return a mocked get_cpu_info. - It is only used to check thruthy or falsy values, so it is mocked + It is only used to check truthy or falsy values, so it is mocked to return True. """ with patch( diff --git a/tests/components/lcn/test_config_flow.py b/tests/components/lcn/test_config_flow.py index 6f084f939d8..0a62f8d317c 100644 --- a/tests/components/lcn/test_config_flow.py +++ b/tests/components/lcn/test_config_flow.py @@ -56,7 +56,7 @@ async def test_step_import_existing_host(hass): mock_data.update({CONF_SK_NUM_TRIES: 3, CONF_DIM_MODE: 50}) mock_entry = MockConfigEntry(domain=DOMAIN, data=mock_data) mock_entry.add_to_hass(hass) - # Inititalize a config flow with different data but same host address + # Initialize a config flow with different data but same host address with patch("pypck.connection.PchkConnectionManager.async_connect"): imported_data = IMPORT_DATA.copy() result = await hass.config_entries.flow.async_init( diff --git a/tests/components/logbook/test_websocket_api.py b/tests/components/logbook/test_websocket_api.py index 42f604b3d4c..5b16c98998c 100644 --- a/tests/components/logbook/test_websocket_api.py +++ b/tests/components/logbook/test_websocket_api.py @@ -913,7 +913,7 @@ async def test_subscribe_unsubscribe_logbook_stream_included_entities( async def test_logbook_stream_excluded_entities_inherits_filters_from_recorder( recorder_mock, hass, hass_ws_client ): - """Test subscribe/unsubscribe logbook stream inherts filters from recorder.""" + """Test subscribe/unsubscribe logbook stream inherits filters from recorder.""" now = dt_util.utcnow() await asyncio.gather( *[ diff --git a/tests/components/nest/test_config_flow_sdm.py b/tests/components/nest/test_config_flow_sdm.py index b2ef95b138e..45e9c97550d 100644 --- a/tests/components/nest/test_config_flow_sdm.py +++ b/tests/components/nest/test_config_flow_sdm.py @@ -654,7 +654,7 @@ async def test_pubsub_subscriber_config_entry_reauth( result = await oauth.async_reauth(config_entry) await oauth.async_oauth_web_flow(result) - # Entering an updated access token refreshs the config entry. + # Entering an updated access token refreshes the config entry. entry = await oauth.async_finish_setup(result, {"code": "1234"}) entry.data["token"].pop("expires_at") assert entry.unique_id == PROJECT_ID diff --git a/tests/components/rtsp_to_webrtc/conftest.py b/tests/components/rtsp_to_webrtc/conftest.py index 5a0d6de01df..f6ee0d1a628 100644 --- a/tests/components/rtsp_to_webrtc/conftest.py +++ b/tests/components/rtsp_to_webrtc/conftest.py @@ -1,4 +1,4 @@ -"""Tests for RTSPtoWebRTC inititalization.""" +"""Tests for RTSPtoWebRTC initialization.""" from __future__ import annotations diff --git a/tests/components/rtsp_to_webrtc/test_init.py b/tests/components/rtsp_to_webrtc/test_init.py index afa365a3044..abbe3728a12 100644 --- a/tests/components/rtsp_to_webrtc/test_init.py +++ b/tests/components/rtsp_to_webrtc/test_init.py @@ -1,4 +1,4 @@ -"""Tests for RTSPtoWebRTC inititalization.""" +"""Tests for RTSPtoWebRTC initialization.""" from __future__ import annotations diff --git a/tests/components/zha/test_climate.py b/tests/components/zha/test_climate.py index f1b900400ea..d00e6bca795 100644 --- a/tests/components/zha/test_climate.py +++ b/tests/components/zha/test_climate.py @@ -486,7 +486,7 @@ async def test_climate_hvac_action_pi_demand(hass, device_climate): ), ) async def test_hvac_mode(hass, device_climate, sys_mode, hvac_mode): - """Test HVAC modee.""" + """Test HVAC mode.""" thrm_cluster = device_climate.device.endpoints[1].thermostat entity_id = await find_entity_id(Platform.CLIMATE, device_climate, hass) diff --git a/tests/helpers/test_entityfilter.py b/tests/helpers/test_entityfilter.py index f9d7ca47b4c..6a2012bb46e 100644 --- a/tests/helpers/test_entityfilter.py +++ b/tests/helpers/test_entityfilter.py @@ -186,11 +186,11 @@ def test_with_include_domain_glob_filtering_case4a_include_strong(): ) assert testfilter("sensor.working") - assert testfilter("sensor.notworking") is True # iclude is stronger + assert testfilter("sensor.notworking") is True # include is stronger assert testfilter("light.test") - assert testfilter("light.notworking") is True # iclude is stronger + assert testfilter("light.notworking") is True # include is stronger assert testfilter("light.ignoreme") is False - assert testfilter("binary_sensor.not_working") is True # iclude is stronger + assert testfilter("binary_sensor.not_working") is True # include is stronger assert testfilter("binary_sensor.another") is False assert testfilter("binary_sensor.specificly_included") is True assert testfilter("sun.sun") is False diff --git a/tests/test_data_entry_flow.py b/tests/test_data_entry_flow.py index 1d60e20a3f0..f0bcd2b5fd6 100644 --- a/tests/test_data_entry_flow.py +++ b/tests/test_data_entry_flow.py @@ -327,7 +327,7 @@ async def test_external_step(hass, manager): "refresh": True, } - # Frontend refreshses the flow + # Frontend refreshes the flow result = await manager.async_configure(result["flow_id"]) assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert result["title"] == "Hello" diff --git a/tests/test_setup.py b/tests/test_setup.py index 04924344c2b..fc07c68bad2 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -639,7 +639,7 @@ async def test_integration_logs_is_custom(hass, caplog): async def test_async_get_loaded_integrations(hass): - """Test we can enumerate loaded integations.""" + """Test we can enumerate loaded integrations.""" hass.config.components.add("notbase") hass.config.components.add("switch") hass.config.components.add("notbase.switch") From 9849735dc5c93099c922caeb5e096a060569df89 Mon Sep 17 00:00:00 2001 From: Avi Miller Date: Tue, 29 Nov 2022 03:27:27 +1100 Subject: [PATCH 0816/1033] Bump aiolifx to 0.8.7 and refactor config flow connection (#82818) Signed-off-by: Avi Miller Signed-off-by: Avi Miller --- homeassistant/components/lifx/config_flow.py | 26 +++++++++++++------- homeassistant/components/lifx/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/lifx/__init__.py | 1 + 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/lifx/config_flow.py b/homeassistant/components/lifx/config_flow.py index 4b2a5b0895e..fa83ff5c427 100644 --- a/homeassistant/components/lifx/config_flow.py +++ b/homeassistant/components/lifx/config_flow.py @@ -31,7 +31,7 @@ from .util import ( class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): - """Handle a config flow for tplink.""" + """Handle a config flow for LIFX.""" VERSION = 1 @@ -41,7 +41,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self._discovered_device: Light | None = None async def async_step_dhcp(self, discovery_info: DhcpServiceInfo) -> FlowResult: - """Handle discovery via dhcp.""" + """Handle discovery via DHCP.""" mac = discovery_info.macaddress host = discovery_info.ip hass = self.hass @@ -70,8 +70,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): async def async_step_integration_discovery( self, discovery_info: DiscoveryInfoType ) -> FlowResult: - """Handle discovery.""" - _LOGGER.debug("async_step_integration_discovery %s", discovery_info) + """Handle LIFX UDP broadcast discovery.""" serial = discovery_info[CONF_SERIAL] host = discovery_info[CONF_HOST] await self.async_set_unique_id(formatted_serial(serial)) @@ -82,7 +81,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self, host: str, serial: str | None = None ) -> FlowResult: """Handle any discovery.""" - _LOGGER.debug("Discovery %s %s", host, serial) self._async_abort_entries_match({CONF_HOST: host}) self.context[CONF_HOST] = host if any( @@ -222,20 +220,30 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): except socket.gaierror: return None device: Light = connection.device - device.get_hostfirmware() try: - message = await async_execute_lifx(device.get_color) + # get_hostfirmware required for MAC address offset + # get_version required for lifx_features() + # get_label required to log the name of the device + messages = await asyncio.gather( + *[ + async_execute_lifx(device.get_hostfirmware), + async_execute_lifx(device.get_version), + async_execute_lifx(device.get_label), + ] + ) except asyncio.TimeoutError: return None finally: connection.async_stop() if ( - lifx_features(device)["relays"] is True + messages is None + or len(messages) != 3 + or lifx_features(device)["relays"] is True or device.host_firmware_version is None ): return None # relays not supported # device.mac_addr is not the mac_address, its the serial number - device.mac_addr = serial or message.target_addr + device.mac_addr = serial or messages[0].target_addr await self.async_set_unique_id( formatted_serial(device.mac_addr), raise_on_progress=raise_on_progress ) diff --git a/homeassistant/components/lifx/manifest.json b/homeassistant/components/lifx/manifest.json index fc5422757b9..c1e7cc56915 100644 --- a/homeassistant/components/lifx/manifest.json +++ b/homeassistant/components/lifx/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/lifx", "requirements": [ - "aiolifx==0.8.6", + "aiolifx==0.8.7", "aiolifx_effects==0.3.0", "aiolifx_themes==0.2.0" ], diff --git a/requirements_all.txt b/requirements_all.txt index 6ff981b2fa9..9ef78ad2afc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -193,7 +193,7 @@ aiokafka==0.7.2 aiokef==0.2.16 # homeassistant.components.lifx -aiolifx==0.8.6 +aiolifx==0.8.7 # homeassistant.components.lifx aiolifx_effects==0.3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index eac4ea03896..64aebb87f74 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -171,7 +171,7 @@ aiohue==4.5.0 aiokafka==0.7.2 # homeassistant.components.lifx -aiolifx==0.8.6 +aiolifx==0.8.7 # homeassistant.components.lifx aiolifx_effects==0.3.0 diff --git a/tests/components/lifx/__init__.py b/tests/components/lifx/__init__.py index 774376e1a99..bb861c4a683 100644 --- a/tests/components/lifx/__init__.py +++ b/tests/components/lifx/__init__.py @@ -87,6 +87,7 @@ def _mocked_bulb() -> Light: bulb.set_reboot = Mock() bulb.try_sending = AsyncMock() bulb.set_infrared = MockLifxCommand(bulb) + bulb.get_label = MockLifxCommand(bulb) bulb.get_color = MockLifxCommand(bulb) bulb.set_power = MockLifxCommand(bulb) bulb.set_color = MockLifxCommand(bulb) From 2c438733174ddcc2bdbab2872a4864dd44cd6054 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 28 Nov 2022 17:52:15 +0100 Subject: [PATCH 0817/1033] Update codespell to 2.2.2 (#82856) --- .pre-commit-config.yaml | 4 ++-- requirements_test_pre_commit.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f32640890c1..f7b1e54d0a4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,11 +20,11 @@ repos: - --quiet files: ^((homeassistant|pylint|script|tests)/.+)?[^/]+\.py$ - repo: https://github.com/codespell-project/codespell - rev: v2.1.0 + rev: v2.2.2 hooks: - id: codespell args: - - --ignore-words-list=alot,ba,bre,datas,dof,dur,ether,farenheit,fo,haa,hass,hist,iam,iff,iif,incomfort,ines,ist,lightsensor,mut,nd,pres,pullrequests,referer,rime,ser,serie,sur,te,technik,ue,uint,visability,wan,wanna,withing + - --ignore-words-list=additionals,alot,ba,bre,bund,datas,dof,dur,ether,farenheit,falsy,fo,haa,hass,hist,iam,iff,iif,incomfort,ines,ist,lightsensor,mut,nam,nd,pres,pullrequests,referer,resset,rime,ser,serie,sur,te,technik,ue,uint,unsecure,visability,wan,wanna,withing,zar - --skip="./.*,*.csv,*.json" - --quiet-level=2 exclude_types: [csv, json] diff --git a/requirements_test_pre_commit.txt b/requirements_test_pre_commit.txt index f7746667654..da49f1e7bc4 100644 --- a/requirements_test_pre_commit.txt +++ b/requirements_test_pre_commit.txt @@ -3,7 +3,7 @@ autoflake==2.0.0 bandit==1.7.4 black==22.10.0 -codespell==2.1.0 +codespell==2.2.2 flake8-comprehensions==3.10.1 flake8-docstrings==1.6.0 flake8-noqa==1.3.0 From 1000f500dfd07715f09617bce5da5af6b72f8df4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 28 Nov 2022 07:40:04 -1000 Subject: [PATCH 0818/1033] Bump aioesphomeapi to 12.0.1 (#82873) --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index b38a4c1f4d5..7dbb4e99670 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==12.0.0"], + "requirements": ["aioesphomeapi==12.0.1"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/requirements_all.txt b/requirements_all.txt index 9ef78ad2afc..15a7774bd62 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -156,7 +156,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==12.0.0 +aioesphomeapi==12.0.1 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 64aebb87f74..44bb0e61c47 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -143,7 +143,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==12.0.0 +aioesphomeapi==12.0.1 # homeassistant.components.flo aioflo==2021.11.0 From 3a65537c5e14c4a6de8ad0bf90012715640c4379 Mon Sep 17 00:00:00 2001 From: pizzaboy192 Date: Mon, 28 Nov 2022 11:55:06 -0600 Subject: [PATCH 0819/1033] Enable Optional Indoor Humidity sensor for Lyric (#81420) --- homeassistant/components/lyric/sensor.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/homeassistant/components/lyric/sensor.py b/homeassistant/components/lyric/sensor.py index 4d132381d42..528161f3d6e 100644 --- a/homeassistant/components/lyric/sensor.py +++ b/homeassistant/components/lyric/sensor.py @@ -90,6 +90,22 @@ async def async_setup_entry( device, ) ) + if device.indoorHumidity: + entities.append( + LyricSensor( + coordinator, + LyricSensorEntityDescription( + key=f"{device.macID}_indoor_humidity", + name="Indoor Humidity", + device_class=SensorDeviceClass.HUMIDITY, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=PERCENTAGE, + value=lambda device: device.indoorHumidity, + ), + location, + device, + ) + ) if device.outdoorTemperature: entities.append( LyricSensor( From a038314d8b537bf022ac89df0b59b6c650f90e7d Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 28 Nov 2022 19:46:57 +0100 Subject: [PATCH 0820/1033] Add display unit to WS recorder/get_statistics_metadata (#82870) --- .../components/recorder/statistics.py | 29 ++++ tests/components/demo/test_init.py | 2 + tests/components/recorder/test_statistics.py | 2 + .../components/recorder/test_websocket_api.py | 136 +++++++++++++++++- tests/components/sensor/test_recorder.py | 40 ++++++ 5 files changed, 207 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 9e1feebc682..7d7efd8b571 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -147,6 +147,30 @@ def _get_unit_class(unit: str | None) -> str | None: return None +def get_display_unit( + hass: HomeAssistant, + statistic_id: str, + statistic_unit: str | None, +) -> str | None: + """Return the unit which the statistic will be displayed in.""" + + if statistic_unit is None: + return None + + if (converter := STATISTIC_UNIT_TO_UNIT_CONVERTER.get(statistic_unit)) is None: + return statistic_unit + + state_unit: str | None = statistic_unit + if state := hass.states.get(statistic_id): + state_unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) + + if state_unit == statistic_unit or state_unit not in converter.VALID_UNITS: + # Guard against invalid state unit in the DB + return statistic_unit + + return state_unit + + def _get_statistic_to_display_unit_converter( statistic_unit: str | None, state_unit: str | None, @@ -902,6 +926,9 @@ def list_statistic_ids( result = { meta["statistic_id"]: { + "display_unit_of_measurement": get_display_unit( + hass, meta["statistic_id"], meta["unit_of_measurement"] + ), "has_mean": meta["has_mean"], "has_sum": meta["has_sum"], "name": meta["name"], @@ -924,6 +951,7 @@ def list_statistic_ids( if key in result: continue result[key] = { + "display_unit_of_measurement": meta["unit_of_measurement"], "has_mean": meta["has_mean"], "has_sum": meta["has_sum"], "name": meta["name"], @@ -936,6 +964,7 @@ def list_statistic_ids( return [ { "statistic_id": _id, + "display_unit_of_measurement": info["display_unit_of_measurement"], "has_mean": info["has_mean"], "has_sum": info["has_sum"], "name": info.get("name"), diff --git a/tests/components/demo/test_init.py b/tests/components/demo/test_init.py index 7ba339a71d2..e5156f35317 100644 --- a/tests/components/demo/test_init.py +++ b/tests/components/demo/test_init.py @@ -63,6 +63,7 @@ async def test_demo_statistics(recorder_mock, mock_history, hass): list_statistic_ids, hass ) assert { + "display_unit_of_measurement": "°C", "has_mean": True, "has_sum": False, "name": "Outdoor temperature", @@ -72,6 +73,7 @@ async def test_demo_statistics(recorder_mock, mock_history, hass): "unit_class": "temperature", } in statistic_ids assert { + "display_unit_of_measurement": "kWh", "has_mean": False, "has_sum": True, "name": "Energy consumption 1", diff --git a/tests/components/recorder/test_statistics.py b/tests/components/recorder/test_statistics.py index 26c6734178c..8950365fd95 100644 --- a/tests/components/recorder/test_statistics.py +++ b/tests/components/recorder/test_statistics.py @@ -554,6 +554,7 @@ async def test_import_statistics( statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ { + "display_unit_of_measurement": "kWh", "has_mean": False, "has_sum": True, "statistic_id": statistic_id, @@ -650,6 +651,7 @@ async def test_import_statistics( statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ { + "display_unit_of_measurement": "kWh", "has_mean": False, "has_sum": True, "statistic_id": statistic_id, diff --git a/tests/components/recorder/test_websocket_api.py b/tests/components/recorder/test_websocket_api.py index 855737a4d7d..fefc8dbdda1 100644 --- a/tests/components/recorder/test_websocket_api.py +++ b/tests/components/recorder/test_websocket_api.py @@ -1350,6 +1350,7 @@ async def test_list_statistic_ids( assert response["result"] == [ { "statistic_id": "sensor.test", + "display_unit_of_measurement": display_unit, "has_mean": has_mean, "has_sum": has_sum, "name": None, @@ -1371,6 +1372,7 @@ async def test_list_statistic_ids( assert response["result"] == [ { "statistic_id": "sensor.test", + "display_unit_of_measurement": display_unit, "has_mean": has_mean, "has_sum": has_sum, "name": None, @@ -1395,6 +1397,7 @@ async def test_list_statistic_ids( assert response["result"] == [ { "statistic_id": "sensor.test", + "display_unit_of_measurement": display_unit, "has_mean": has_mean, "has_sum": has_sum, "name": None, @@ -1415,6 +1418,7 @@ async def test_list_statistic_ids( assert response["result"] == [ { "statistic_id": "sensor.test", + "display_unit_of_measurement": display_unit, "has_mean": has_mean, "has_sum": has_sum, "name": None, @@ -1427,6 +1431,121 @@ async def test_list_statistic_ids( assert response["result"] == [] +@pytest.mark.parametrize( + "attributes, attributes2, display_unit, statistics_unit, unit_class", + [ + ( + DISTANCE_SENSOR_M_ATTRIBUTES, + DISTANCE_SENSOR_FT_ATTRIBUTES, + "ft", + "m", + "distance", + ), + ( + ENERGY_SENSOR_WH_ATTRIBUTES, + ENERGY_SENSOR_KWH_ATTRIBUTES, + "kWh", + "Wh", + "energy", + ), + (GAS_SENSOR_FT3_ATTRIBUTES, GAS_SENSOR_M3_ATTRIBUTES, "m³", "ft³", "volume"), + (POWER_SENSOR_KW_ATTRIBUTES, POWER_SENSOR_W_ATTRIBUTES, "W", "kW", "power"), + ( + PRESSURE_SENSOR_HPA_ATTRIBUTES, + PRESSURE_SENSOR_PA_ATTRIBUTES, + "Pa", + "hPa", + "pressure", + ), + ( + SPEED_SENSOR_KPH_ATTRIBUTES, + SPEED_SENSOR_MPH_ATTRIBUTES, + "mph", + "km/h", + "speed", + ), + ( + TEMPERATURE_SENSOR_C_ATTRIBUTES, + TEMPERATURE_SENSOR_F_ATTRIBUTES, + "°F", + "°C", + "temperature", + ), + ( + VOLUME_SENSOR_FT3_ATTRIBUTES, + VOLUME_SENSOR_M3_ATTRIBUTES, + "m³", + "ft³", + "volume", + ), + ], +) +async def test_list_statistic_ids_unit_change( + recorder_mock, + hass, + hass_ws_client, + attributes, + attributes2, + display_unit, + statistics_unit, + unit_class, +): + """Test list_statistic_ids.""" + now = dt_util.utcnow() + has_mean = attributes["state_class"] == "measurement" + has_sum = not has_mean + + await async_setup_component(hass, "sensor", {}) + await async_recorder_block_till_done(hass) + + client = await hass_ws_client() + await client.send_json({"id": 1, "type": "recorder/list_statistic_ids"}) + response = await client.receive_json() + assert response["success"] + assert response["result"] == [] + + hass.states.async_set("sensor.test", 10, attributes=attributes) + await async_wait_recording_done(hass) + + do_adhoc_statistics(hass, start=now) + await async_recorder_block_till_done(hass) + + await client.send_json({"id": 2, "type": "recorder/list_statistic_ids"}) + response = await client.receive_json() + assert response["success"] + assert response["result"] == [ + { + "statistic_id": "sensor.test", + "display_unit_of_measurement": statistics_unit, + "has_mean": has_mean, + "has_sum": has_sum, + "name": None, + "source": "recorder", + "statistics_unit_of_measurement": statistics_unit, + "unit_class": unit_class, + } + ] + + # Change the state unit + hass.states.async_set("sensor.test", 10, attributes=attributes2) + + await client.send_json({"id": 3, "type": "recorder/list_statistic_ids"}) + response = await client.receive_json() + assert response["success"] + assert response["result"] == [ + { + "statistic_id": "sensor.test", + "display_unit_of_measurement": display_unit, + "has_mean": has_mean, + "has_sum": has_sum, + "name": None, + "source": "recorder", + "statistics_unit_of_measurement": statistics_unit, + "unit_class": unit_class, + } + ] + + async def test_validate_statistics(recorder_mock, hass, hass_ws_client): """Test validate_statistics can be called.""" id = 1 @@ -1570,10 +1689,11 @@ async def test_clear_statistics(recorder_mock, hass, hass_ws_client): @pytest.mark.parametrize( - "new_unit, new_unit_class", [("dogs", None), (None, None), ("W", "power")] + "new_unit, new_unit_class, new_display_unit", + [("dogs", None, "dogs"), (None, None, None), ("W", "power", "kW")], ) async def test_update_statistics_metadata( - recorder_mock, hass, hass_ws_client, new_unit, new_unit_class + recorder_mock, hass, hass_ws_client, new_unit, new_unit_class, new_display_unit ): """Test removing statistics.""" now = dt_util.utcnow() @@ -1599,6 +1719,7 @@ async def test_update_statistics_metadata( assert response["result"] == [ { "statistic_id": "sensor.test", + "display_unit_of_measurement": "kW", "has_mean": True, "has_sum": False, "name": None, @@ -1626,6 +1747,7 @@ async def test_update_statistics_metadata( assert response["result"] == [ { "statistic_id": "sensor.test", + "display_unit_of_measurement": new_display_unit, "has_mean": True, "has_sum": False, "name": None, @@ -1688,6 +1810,7 @@ async def test_change_statistics_unit(recorder_mock, hass, hass_ws_client): assert response["result"] == [ { "statistic_id": "sensor.test", + "display_unit_of_measurement": "kW", "has_mean": True, "has_sum": False, "name": None, @@ -1742,6 +1865,7 @@ async def test_change_statistics_unit(recorder_mock, hass, hass_ws_client): assert response["result"] == [ { "statistic_id": "sensor.test", + "display_unit_of_measurement": "kW", "has_mean": True, "has_sum": False, "name": None, @@ -1793,6 +1917,7 @@ async def test_change_statistics_unit_errors( expected_statistic_ids = [ { "statistic_id": "sensor.test", + "display_unit_of_measurement": "kW", "has_mean": True, "has_sum": False, "name": None, @@ -2177,6 +2302,7 @@ async def test_get_statistics_metadata( assert response["result"] == [ { "statistic_id": "test:total_gas", + "display_unit_of_measurement": unit, "has_mean": has_mean, "has_sum": has_sum, "name": "Total imported energy", @@ -2204,6 +2330,7 @@ async def test_get_statistics_metadata( assert response["result"] == [ { "statistic_id": "sensor.test", + "display_unit_of_measurement": attributes["unit_of_measurement"], "has_mean": has_mean, "has_sum": has_sum, "name": None, @@ -2231,6 +2358,7 @@ async def test_get_statistics_metadata( assert response["result"] == [ { "statistic_id": "sensor.test", + "display_unit_of_measurement": attributes["unit_of_measurement"], "has_mean": has_mean, "has_sum": has_sum, "name": None, @@ -2324,6 +2452,7 @@ async def test_import_statistics( statistic_ids = list_statistic_ids(hass) # TODO assert statistic_ids == [ { + "display_unit_of_measurement": "kWh", "has_mean": False, "has_sum": True, "statistic_id": statistic_id, @@ -2550,6 +2679,7 @@ async def test_adjust_sum_statistics_energy( statistic_ids = list_statistic_ids(hass) # TODO assert statistic_ids == [ { + "display_unit_of_measurement": "kWh", "has_mean": False, "has_sum": True, "statistic_id": statistic_id, @@ -2740,6 +2870,7 @@ async def test_adjust_sum_statistics_gas( statistic_ids = list_statistic_ids(hass) # TODO assert statistic_ids == [ { + "display_unit_of_measurement": "m³", "has_mean": False, "has_sum": True, "statistic_id": statistic_id, @@ -2946,6 +3077,7 @@ async def test_adjust_sum_statistics_errors( statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ { + "display_unit_of_measurement": state_unit, "has_mean": False, "has_sum": True, "statistic_id": statistic_id, diff --git a/tests/components/sensor/test_recorder.py b/tests/components/sensor/test_recorder.py index 83d61d0a7fc..1e129b7af92 100644 --- a/tests/components/sensor/test_recorder.py +++ b/tests/components/sensor/test_recorder.py @@ -141,6 +141,7 @@ def test_compile_hourly_statistics( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": True, "has_sum": False, "name": None, @@ -214,6 +215,7 @@ def test_compile_hourly_statistics_purged_state_changes( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": True, "has_sum": False, "name": None, @@ -282,6 +284,7 @@ def test_compile_hourly_statistics_wrong_unit(hass_recorder, caplog, attributes) assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": "°C", "has_mean": True, "has_sum": False, "name": None, @@ -290,6 +293,7 @@ def test_compile_hourly_statistics_wrong_unit(hass_recorder, caplog, attributes) "unit_class": "temperature", }, { + "display_unit_of_measurement": "invalid", "has_mean": True, "has_sum": False, "name": None, @@ -299,6 +303,7 @@ def test_compile_hourly_statistics_wrong_unit(hass_recorder, caplog, attributes) "unit_class": None, }, { + "display_unit_of_measurement": None, "has_mean": True, "has_sum": False, "name": None, @@ -309,6 +314,7 @@ def test_compile_hourly_statistics_wrong_unit(hass_recorder, caplog, attributes) }, { "statistic_id": "sensor.test6", + "display_unit_of_measurement": "°C", "has_mean": True, "has_sum": False, "name": None, @@ -318,6 +324,7 @@ def test_compile_hourly_statistics_wrong_unit(hass_recorder, caplog, attributes) }, { "statistic_id": "sensor.test7", + "display_unit_of_measurement": "°C", "has_mean": True, "has_sum": False, "name": None, @@ -473,6 +480,7 @@ async def test_compile_hourly_sum_statistics_amount( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": statistics_unit, "has_mean": False, "has_sum": True, "name": None, @@ -662,6 +670,7 @@ def test_compile_hourly_sum_statistics_amount_reset_every_state_change( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": False, "has_sum": True, "name": None, @@ -758,6 +767,7 @@ def test_compile_hourly_sum_statistics_amount_invalid_last_reset( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": False, "has_sum": True, "name": None, @@ -841,6 +851,7 @@ def test_compile_hourly_sum_statistics_nan_inf_state( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": False, "has_sum": True, "name": None, @@ -965,6 +976,7 @@ def test_compile_hourly_sum_statistics_negative_state( wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert { + "display_unit_of_measurement": display_unit, "has_mean": False, "has_sum": True, "name": None, @@ -1052,6 +1064,7 @@ def test_compile_hourly_sum_statistics_total_no_reset( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": False, "has_sum": True, "name": None, @@ -1151,6 +1164,7 @@ def test_compile_hourly_sum_statistics_total_increasing( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": False, "has_sum": True, "name": None, @@ -1261,6 +1275,7 @@ def test_compile_hourly_sum_statistics_total_increasing_small_dip( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": False, "has_sum": True, "name": None, @@ -1352,6 +1367,7 @@ def test_compile_hourly_energy_statistics_unsupported(hass_recorder, caplog): assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": "kWh", "has_mean": False, "has_sum": True, "name": None, @@ -1441,6 +1457,7 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog): assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": "kWh", "has_mean": False, "has_sum": True, "name": None, @@ -1450,6 +1467,7 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog): }, { "statistic_id": "sensor.test2", + "display_unit_of_measurement": "kWh", "has_mean": False, "has_sum": True, "name": None, @@ -1459,6 +1477,7 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog): }, { "statistic_id": "sensor.test3", + "display_unit_of_measurement": "Wh", "has_mean": False, "has_sum": True, "name": None, @@ -1814,6 +1833,7 @@ def test_list_statistic_ids( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": statistic_type == "mean", "has_sum": statistic_type == "sum", "name": None, @@ -1828,6 +1848,7 @@ def test_list_statistic_ids( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": statistic_type == "mean", "has_sum": statistic_type == "sum", "name": None, @@ -1921,6 +1942,7 @@ def test_compile_hourly_statistics_changing_units_1( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": state_unit, "has_mean": True, "has_sum": False, "name": None, @@ -1955,6 +1977,7 @@ def test_compile_hourly_statistics_changing_units_1( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": state_unit, "has_mean": True, "has_sum": False, "name": None, @@ -2029,6 +2052,7 @@ def test_compile_hourly_statistics_changing_units_2( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": "cats", "has_mean": True, "has_sum": False, "name": None, @@ -2094,6 +2118,7 @@ def test_compile_hourly_statistics_changing_units_3( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": True, "has_sum": False, "name": None, @@ -2128,6 +2153,7 @@ def test_compile_hourly_statistics_changing_units_3( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": True, "has_sum": False, "name": None, @@ -2206,6 +2232,7 @@ def test_compile_hourly_statistics_equivalent_units_1( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": state_unit, "has_mean": True, "has_sum": False, "name": None, @@ -2236,6 +2263,7 @@ def test_compile_hourly_statistics_equivalent_units_1( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": state_unit2, "has_mean": True, "has_sum": False, "name": None, @@ -2319,6 +2347,7 @@ def test_compile_hourly_statistics_equivalent_units_2( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": state_unit, "has_mean": True, "has_sum": False, "name": None, @@ -2384,6 +2413,7 @@ def test_compile_hourly_statistics_changing_device_class_1( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": state_unit, "has_mean": True, "has_sum": False, "name": None, @@ -2428,6 +2458,7 @@ def test_compile_hourly_statistics_changing_device_class_1( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": state_unit, "has_mean": True, "has_sum": False, "name": None, @@ -2482,6 +2513,7 @@ def test_compile_hourly_statistics_changing_device_class_1( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": state_unit, "has_mean": True, "has_sum": False, "name": None, @@ -2568,6 +2600,7 @@ def test_compile_hourly_statistics_changing_device_class_2( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": True, "has_sum": False, "name": None, @@ -2612,6 +2645,7 @@ def test_compile_hourly_statistics_changing_device_class_2( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": display_unit, "has_mean": True, "has_sum": False, "name": None, @@ -2690,6 +2724,7 @@ def test_compile_hourly_statistics_changing_statistics( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": None, "has_mean": True, "has_sum": False, "name": None, @@ -2725,6 +2760,7 @@ def test_compile_hourly_statistics_changing_statistics( assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": None, "has_mean": False, "has_sum": True, "name": None, @@ -2916,6 +2952,7 @@ def test_compile_statistics_hourly_daily_monthly_summary(hass_recorder, caplog): assert statistic_ids == [ { "statistic_id": "sensor.test1", + "display_unit_of_measurement": "%", "has_mean": True, "has_sum": False, "name": None, @@ -2925,6 +2962,7 @@ def test_compile_statistics_hourly_daily_monthly_summary(hass_recorder, caplog): }, { "statistic_id": "sensor.test2", + "display_unit_of_measurement": "%", "has_mean": True, "has_sum": False, "name": None, @@ -2934,6 +2972,7 @@ def test_compile_statistics_hourly_daily_monthly_summary(hass_recorder, caplog): }, { "statistic_id": "sensor.test3", + "display_unit_of_measurement": "%", "has_mean": True, "has_sum": False, "name": None, @@ -2943,6 +2982,7 @@ def test_compile_statistics_hourly_daily_monthly_summary(hass_recorder, caplog): }, { "statistic_id": "sensor.test4", + "display_unit_of_measurement": "EUR", "has_mean": False, "has_sum": True, "name": None, From 58b3a00b16543666b55afede114ad20ca74869e2 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 28 Nov 2022 19:48:06 +0100 Subject: [PATCH 0821/1033] Remove unnecessary DB access from statistic_during_period (#82871) --- homeassistant/components/recorder/statistics.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 7d7efd8b571..6c117b9698d 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -1502,13 +1502,6 @@ def statistic_during_period( main_start_time = start_time if head_end_time is None else head_end_time main_end_time = end_time if tail_start_time is None else tail_start_time - # Fetch metadata for the given statistic_id - metadata = get_metadata_with_session(session, statistic_ids=[statistic_id]) - if not metadata: - return result - - metadata_id = metadata[statistic_id][0] - if not types.isdisjoint({"max", "mean", "min"}): result = _get_max_mean_min_statistic( session, From 892be99ca0140cc5d7662d89329152b63653d775 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 28 Nov 2022 19:55:07 +0100 Subject: [PATCH 0822/1033] Check if Multi-PAN addon is using the HA Yellow's radio (#82853) --- .../homeassistant_yellow/__init__.py | 39 +++++++++----- .../components/homeassistant_yellow/const.py | 3 +- .../homeassistant_yellow/test_init.py | 52 +++++++++++++++++++ 3 files changed, 80 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/homeassistant_yellow/__init__.py b/homeassistant/components/homeassistant_yellow/__init__.py index a7e351535aa..1b61fe7d47f 100644 --- a/homeassistant/components/homeassistant_yellow/__init__.py +++ b/homeassistant/components/homeassistant_yellow/__init__.py @@ -18,23 +18,13 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from .const import ZHA_HW_DISCOVERY_DATA +from .const import RADIO_DEVICE, ZHA_HW_DISCOVERY_DATA _LOGGER = logging.getLogger(__name__) -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: - """Set up a Home Assistant Yellow config entry.""" - if (os_info := get_os_info(hass)) is None: - # The hassio integration has not yet fetched data from the supervisor - raise ConfigEntryNotReady - - board: str | None - if (board := os_info.get("board")) is None or not board == "yellow": - # Not running on a Home Assistant Yellow, Home Assistant may have been migrated - hass.async_create_task(hass.config_entries.async_remove(entry.entry_id)) - return False - +async def _multi_pan_addon_info(hass, entry: ConfigEntry) -> AddonInfo | None: + """Return AddonInfo if the multi-PAN addon is enabled for the Yellow's radio.""" addon_manager: AddonManager = get_addon_manager(hass) try: addon_info: AddonInfo = await addon_manager.async_get_addon_info() @@ -54,6 +44,29 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryNotReady if addon_info.state == AddonState.NOT_INSTALLED: + return None + + if addon_info.options["device"] != RADIO_DEVICE: + return None + + return addon_info + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up a Home Assistant Yellow config entry.""" + if (os_info := get_os_info(hass)) is None: + # The hassio integration has not yet fetched data from the supervisor + raise ConfigEntryNotReady + + board: str | None + if (board := os_info.get("board")) is None or not board == "yellow": + # Not running on a Home Assistant Yellow, Home Assistant may have been migrated + hass.async_create_task(hass.config_entries.async_remove(entry.entry_id)) + return False + + addon_info = await _multi_pan_addon_info(hass, entry) + + if not addon_info: hw_discovery_data = ZHA_HW_DISCOVERY_DATA else: hw_discovery_data = { diff --git a/homeassistant/components/homeassistant_yellow/const.py b/homeassistant/components/homeassistant_yellow/const.py index 5d693d819b6..8f1f9a4c2b8 100644 --- a/homeassistant/components/homeassistant_yellow/const.py +++ b/homeassistant/components/homeassistant_yellow/const.py @@ -2,10 +2,11 @@ DOMAIN = "homeassistant_yellow" +RADIO_DEVICE = "/dev/ttyAMA1" ZHA_HW_DISCOVERY_DATA = { "name": "Yellow", "port": { - "path": "/dev/ttyAMA1", + "path": RADIO_DEVICE, "baudrate": 115200, "flow_control": "hardware", }, diff --git a/tests/components/homeassistant_yellow/test_init.py b/tests/components/homeassistant_yellow/test_init.py index 6df8977b35d..85b027e82ed 100644 --- a/tests/components/homeassistant_yellow/test_init.py +++ b/tests/components/homeassistant_yellow/test_init.py @@ -110,6 +110,8 @@ async def test_setup_zha_multipan( """Test zha gets the right config.""" mock_integration(hass, MockModule("hassio")) + addon_info.return_value["options"]["device"] = "/dev/ttyAMA1" + # Setup the config entry config_entry = MockConfigEntry( data={}, @@ -152,6 +154,56 @@ async def test_setup_zha_multipan( assert config_entry.title == "Yellow Multi-PAN" +async def test_setup_zha_multipan_other_device( + hass: HomeAssistant, addon_info, addon_running +) -> None: + """Test zha gets the right config.""" + mock_integration(hass, MockModule("hassio")) + + addon_info.return_value["options"]["device"] = "/dev/not_yellow_radio" + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.homeassistant_yellow.get_os_info", + return_value={"board": "yellow"}, + ) as mock_get_os_info, patch( + "homeassistant.components.onboarding.async_is_onboarded", return_value=False + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert len(mock_get_os_info.mock_calls) == 1 + + # Finish setting up ZHA + zha_flows = hass.config_entries.flow.async_progress_by_handler("zha") + assert len(zha_flows) == 1 + assert zha_flows[0]["step_id"] == "choose_formation_strategy" + + await hass.config_entries.flow.async_configure( + zha_flows[0]["flow_id"], + user_input={"next_step_id": zha.config_flow.FORMATION_REUSE_SETTINGS}, + ) + await hass.async_block_till_done() + + config_entry = hass.config_entries.async_entries("zha")[0] + assert config_entry.data == { + "device": { + "baudrate": 115200, + "flow_control": "hardware", + "path": "/dev/ttyAMA1", + }, + "radio_type": "ezsp", + } + assert config_entry.options == {} + assert config_entry.title == "Yellow" + + async def test_setup_entry_wrong_board(hass: HomeAssistant) -> None: """Test setup of a config entry with wrong board type.""" mock_integration(hass, MockModule("hassio")) From b842e26d36b3179fcd1e86e5d955a1c8c644408b Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Mon, 28 Nov 2022 14:07:53 -0500 Subject: [PATCH 0823/1033] Split UniFi Protect object sensor into multiple (#82595) --- .../components/unifiprotect/binary_sensor.py | 94 +++++++++++++++---- .../components/unifiprotect/const.py | 3 + .../components/unifiprotect/entity.py | 48 +++++----- .../components/unifiprotect/migrate.py | 22 ++++- .../components/unifiprotect/models.py | 39 +++++++- .../components/unifiprotect/sensor.py | 53 ++++++----- .../components/unifiprotect/strings.json | 4 + .../unifiprotect/translations/en.json | 4 + .../unifiprotect/test_binary_sensor.py | 9 +- tests/components/unifiprotect/test_repairs.py | 72 ++++++++++++-- tests/components/unifiprotect/test_sensor.py | 13 ++- 11 files changed, 274 insertions(+), 87 deletions(-) diff --git a/homeassistant/components/unifiprotect/binary_sensor.py b/homeassistant/components/unifiprotect/binary_sensor.py index 05f7b37d6c8..3d018d6eee1 100644 --- a/homeassistant/components/unifiprotect/binary_sensor.py +++ b/homeassistant/components/unifiprotect/binary_sensor.py @@ -8,13 +8,13 @@ import logging from pyunifiprotect.data import ( NVR, Camera, - Event, Light, ModelType, MountType, ProtectAdoptableDeviceModel, ProtectModelWithId, Sensor, + SmartDetectObjectType, ) from pyunifiprotect.data.nvr import UOSDisk @@ -29,15 +29,15 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DISPATCH_ADOPT, DOMAIN +from .const import DEVICE_CLASS_DETECTION, DISPATCH_ADOPT, DOMAIN from .data import ProtectData from .entity import ( - EventThumbnailMixin, + EventEntityMixin, ProtectDeviceEntity, ProtectNVREntity, async_all_device_entities, ) -from .models import PermRequired, ProtectRequiredKeysMixin +from .models import PermRequired, ProtectEventMixin, ProtectRequiredKeysMixin from .utils import async_dispatch_id as _ufpd _LOGGER = logging.getLogger(__name__) @@ -51,6 +51,13 @@ class ProtectBinaryEntityDescription( """Describes UniFi Protect Binary Sensor entity.""" +@dataclass +class ProtectBinaryEventEntityDescription( + ProtectEventMixin, BinarySensorEntityDescription +): + """Describes UniFi Protect Binary Sensor entity.""" + + MOUNT_DEVICE_CLASS_MAP = { MountType.GARAGE: BinarySensorDeviceClass.GARAGE_DOOR, MountType.WINDOW: BinarySensorDeviceClass.WINDOW, @@ -179,7 +186,7 @@ CAMERA_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = ( ProtectBinaryEntityDescription( key="smart_face", name="Detections: Face", - icon="mdi:human-greeting", + icon="mdi:mdi-face", entity_category=EntityCategory.DIAGNOSTIC, ufp_required_field="can_detect_face", ufp_value="is_face_detection_on", @@ -313,12 +320,66 @@ SENSE_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = ( ), ) -MOTION_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = ( - ProtectBinaryEntityDescription( +MOTION_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = ( + ProtectBinaryEventEntityDescription( key="motion", name="Motion", device_class=BinarySensorDeviceClass.MOTION, ufp_value="is_motion_detected", + ufp_event_obj="last_motion_event", + ), + ProtectBinaryEventEntityDescription( + key="smart_obj_any", + name="Object Detected", + icon="mdi:eye", + device_class=DEVICE_CLASS_DETECTION, + ufp_value="is_smart_detected", + ufp_required_field="feature_flags.has_smart_detect", + ufp_event_obj="last_smart_detect_event", + ), + ProtectBinaryEventEntityDescription( + key="smart_obj_person", + name="Person Detected", + icon="mdi:walk", + device_class=DEVICE_CLASS_DETECTION, + ufp_value="is_smart_detected", + ufp_required_field="can_detect_person", + ufp_enabled="is_person_detection_on", + ufp_event_obj="last_smart_detect_event", + ufp_smart_type=SmartDetectObjectType.PERSON, + ), + ProtectBinaryEventEntityDescription( + key="smart_obj_vehicle", + name="Vehicle Detected", + icon="mdi:car", + device_class=DEVICE_CLASS_DETECTION, + ufp_value="is_smart_detected", + ufp_required_field="can_detect_vehicle", + ufp_enabled="is_vehicle_detection_on", + ufp_event_obj="last_smart_detect_event", + ufp_smart_type=SmartDetectObjectType.VEHICLE, + ), + ProtectBinaryEventEntityDescription( + key="smart_obj_face", + name="Face Detected", + device_class=DEVICE_CLASS_DETECTION, + icon="mdi:mdi-face", + ufp_value="is_smart_detected", + ufp_required_field="can_detect_face", + ufp_enabled="is_face_detection_on", + ufp_event_obj="last_smart_detect_event", + ufp_smart_type=SmartDetectObjectType.FACE, + ), + ProtectBinaryEventEntityDescription( + key="smart_obj_package", + name="Package Detected", + device_class=DEVICE_CLASS_DETECTION, + icon="mdi:package-variant-closed", + ufp_value="is_smart_detected", + ufp_required_field="can_detect_package", + ufp_enabled="is_package_detection_on", + ufp_event_obj="last_smart_detect_event", + ufp_smart_type=SmartDetectObjectType.PACKAGE, ), ) @@ -415,6 +476,8 @@ def _async_motion_entities( ) for device in devices: for description in MOTION_SENSORS: + if not description.has_required(device): + continue entities.append(ProtectEventBinarySensor(data, device, description)) _LOGGER.debug( "Adding binary sensor entity %s for %s", @@ -508,17 +571,12 @@ class ProtectDiskBinarySensor(ProtectNVREntity, BinarySensorEntity): self._attr_is_on = not self._disk.is_healthy -class ProtectEventBinarySensor(EventThumbnailMixin, ProtectDeviceBinarySensor): - """A UniFi Protect Device Binary Sensor with access tokens.""" +class ProtectEventBinarySensor(EventEntityMixin, BinarySensorEntity): + """A UniFi Protect Device Binary Sensor for events.""" - device: Camera + entity_description: ProtectBinaryEventEntityDescription @callback - def _async_get_event(self) -> Event | None: - """Get event from Protect device.""" - - event: Event | None = None - if self.device.is_motion_detected and self.device.last_motion_event is not None: - event = self.device.last_motion_event - - return event + def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None: + super()._async_update_device_from_protect(device) + self._attr_is_on = self.entity_description.get_ufp_value(self.device) diff --git a/homeassistant/components/unifiprotect/const.py b/homeassistant/components/unifiprotect/const.py index f751ed6a009..cbf15c076c5 100644 --- a/homeassistant/components/unifiprotect/const.py +++ b/homeassistant/components/unifiprotect/const.py @@ -7,6 +7,7 @@ from homeassistant.const import Platform DOMAIN = "unifiprotect" ATTR_EVENT_SCORE = "event_score" +ATTR_EVENT_ID = "event_id" ATTR_WIDTH = "width" ATTR_HEIGHT = "height" ATTR_FPS = "fps" @@ -67,3 +68,5 @@ PLATFORMS = [ DISPATCH_ADD = "add_device" DISPATCH_ADOPT = "adopt_device" DISPATCH_CHANNELS = "new_camera_channels" + +DEVICE_CLASS_DETECTION = "unifiprotect__detection" diff --git a/homeassistant/components/unifiprotect/entity.py b/homeassistant/components/unifiprotect/entity.py index 9777ccbd72a..a733f6ea1af 100644 --- a/homeassistant/components/unifiprotect/entity.py +++ b/homeassistant/components/unifiprotect/entity.py @@ -24,10 +24,15 @@ from homeassistant.core import callback import homeassistant.helpers.device_registry as dr from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription -from .const import ATTR_EVENT_SCORE, DEFAULT_ATTRIBUTION, DEFAULT_BRAND, DOMAIN +from .const import ( + ATTR_EVENT_ID, + ATTR_EVENT_SCORE, + DEFAULT_ATTRIBUTION, + DEFAULT_BRAND, + DOMAIN, +) from .data import ProtectData -from .models import PermRequired, ProtectRequiredKeysMixin -from .utils import get_nested_attr +from .models import PermRequired, ProtectEventMixin, ProtectRequiredKeysMixin _LOGGER = logging.getLogger(__name__) @@ -82,10 +87,8 @@ def _async_device_entities( ): continue - if description.ufp_required_field: - required_field = get_nested_attr(device, description.ufp_required_field) - if not required_field: - continue + if not description.has_required(device): + continue entities.append( klass( @@ -294,42 +297,39 @@ class ProtectNVREntity(ProtectDeviceEntity): self._attr_available = self.data.last_update_success -class EventThumbnailMixin(ProtectDeviceEntity): +class EventEntityMixin(ProtectDeviceEntity): """Adds motion event attributes to sensor.""" - def __init__(self, *args: Any, **kwarg: Any) -> None: + entity_description: ProtectEventMixin + + def __init__( + self, + *args: Any, + **kwarg: Any, + ) -> None: """Init an sensor that has event thumbnails.""" super().__init__(*args, **kwarg) self._event: Event | None = None @callback - def _async_get_event(self) -> Event | None: - """Get event from Protect device. - - To be overridden by child classes. - """ - raise NotImplementedError() - - @callback - def _async_thumbnail_extra_attrs(self) -> dict[str, Any]: - # Camera motion sensors with object detection - attrs: dict[str, Any] = { - ATTR_EVENT_SCORE: 0, - } + def _async_event_extra_attrs(self) -> dict[str, Any]: + attrs: dict[str, Any] = {} if self._event is None: return attrs + attrs[ATTR_EVENT_ID] = self._event.id attrs[ATTR_EVENT_SCORE] = self._event.score return attrs @callback def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None: super()._async_update_device_from_protect(device) - self._event = self._async_get_event() + self._attr_is_on: bool | None = self.entity_description.get_is_on(device) + self._event = self.entity_description.get_event_obj(device) attrs = self.extra_state_attributes or {} self._attr_extra_state_attributes = { **attrs, - **self._async_thumbnail_extra_attrs(), + **self._async_event_extra_attrs(), } diff --git a/homeassistant/components/unifiprotect/migrate.py b/homeassistant/components/unifiprotect/migrate.py index 893ca3e458a..b40f78d0ccf 100644 --- a/homeassistant/components/unifiprotect/migrate.py +++ b/homeassistant/components/unifiprotect/migrate.py @@ -12,7 +12,10 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import entity_registry as er, issue_registry as ir +from homeassistant.helpers.issue_registry import IssueSeverity + +from .const import DOMAIN _LOGGER = logging.getLogger(__name__) @@ -30,6 +33,23 @@ async def async_migrate_data( await async_migrate_device_ids(hass, entry, protect) _LOGGER.debug("Completed Migrate: async_migrate_device_ids") + entity_registry = er.async_get(hass) + for entity in er.async_entries_for_config_entry(entity_registry, entry.entry_id): + if ( + entity.domain == Platform.SENSOR + and entity.disabled_by is None + and "detected_object" in entity.unique_id + ): + ir.async_create_issue( + hass, + DOMAIN, + "deprecate_smart_sensor", + is_fixable=False, + breaks_in_ha_version="2023.2.0", + severity=IssueSeverity.WARNING, + translation_key="deprecate_smart_sensor", + ) + async def async_get_bootstrap(protect: ProtectApiClient) -> Bootstrap: """Get UniFi Protect bootstrap or raise appropriate HA error.""" diff --git a/homeassistant/components/unifiprotect/models.py b/homeassistant/components/unifiprotect/models.py index adab5c032e1..01d4820e9a9 100644 --- a/homeassistant/components/unifiprotect/models.py +++ b/homeassistant/components/unifiprotect/models.py @@ -5,9 +5,9 @@ from collections.abc import Callable, Coroutine from dataclasses import dataclass from enum import Enum import logging -from typing import Any, Generic, TypeVar, Union +from typing import Any, Generic, TypeVar, Union, cast -from pyunifiprotect.data import NVR, ProtectAdoptableDeviceModel +from pyunifiprotect.data import NVR, Event, ProtectAdoptableDeviceModel from homeassistant.helpers.entity import EntityDescription @@ -54,6 +54,41 @@ class ProtectRequiredKeysMixin(EntityDescription, Generic[T]): return bool(get_nested_attr(obj, self.ufp_enabled)) return True + def has_required(self, obj: T) -> bool: + """Return if has required field.""" + + if self.ufp_required_field is None: + return True + return bool(get_nested_attr(obj, self.ufp_required_field)) + + +@dataclass +class ProtectEventMixin(ProtectRequiredKeysMixin[T]): + """Mixin for events.""" + + ufp_event_obj: str | None = None + ufp_smart_type: str | None = None + + def get_event_obj(self, obj: T) -> Event | None: + """Return value from UniFi Protect device.""" + + if self.ufp_event_obj is not None: + return cast(Event, get_nested_attr(obj, self.ufp_event_obj)) + return None + + def get_is_on(self, obj: T) -> bool: + """Return value if event is active.""" + + value = bool(self.get_ufp_value(obj)) + if value: + event = self.get_event_obj(obj) + value = event is not None + + if event is not None and self.ufp_smart_type is not None: + value = self.ufp_smart_type in event.smart_detect_types + + return value + @dataclass class ProtectSetableKeysMixin(ProtectRequiredKeysMixin[T]): diff --git a/homeassistant/components/unifiprotect/sensor.py b/homeassistant/components/unifiprotect/sensor.py index 57bd4fc7230..168faebe8ac 100644 --- a/homeassistant/components/unifiprotect/sensor.py +++ b/homeassistant/components/unifiprotect/sensor.py @@ -9,7 +9,6 @@ from typing import Any, cast from pyunifiprotect.data import ( NVR, Camera, - Event, Light, ModelType, ProtectAdoptableDeviceModel, @@ -41,20 +40,19 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DISPATCH_ADOPT, DOMAIN +from .const import DEVICE_CLASS_DETECTION, DISPATCH_ADOPT, DOMAIN from .data import ProtectData from .entity import ( - EventThumbnailMixin, + EventEntityMixin, ProtectDeviceEntity, ProtectNVREntity, async_all_device_entities, ) -from .models import PermRequired, ProtectRequiredKeysMixin, T +from .models import PermRequired, ProtectEventMixin, ProtectRequiredKeysMixin, T from .utils import async_dispatch_id as _ufpd, async_get_light_motion_current _LOGGER = logging.getLogger(__name__) OBJECT_TYPE_NONE = "none" -DEVICE_CLASS_DETECTION = "unifiprotect__detection" @dataclass @@ -74,6 +72,13 @@ class ProtectSensorEntityDescription( return value +@dataclass +class ProtectSensorEventEntityDescription( + ProtectEventMixin[T], SensorEntityDescription +): + """Describes UniFi Protect Sensor entity.""" + + def _get_uptime(obj: ProtectDeviceModel) -> datetime | None: if obj.up_since is None: return None @@ -513,11 +518,14 @@ NVR_DISABLED_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( ), ) -MOTION_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( - ProtectSensorEntityDescription( +MOTION_SENSORS: tuple[ProtectSensorEventEntityDescription, ...] = ( + ProtectSensorEventEntityDescription( key="detected_object", name="Detected Object", device_class=DEVICE_CLASS_DETECTION, + entity_registry_enabled_default=False, + ufp_value="is_smart_detected", + ufp_event_obj="last_smart_detect_event", ), ) @@ -666,8 +674,8 @@ def _async_motion_entities( if not device.feature_flags.has_smart_detect: continue - for description in MOTION_SENSORS: - entities.append(ProtectEventSensor(data, device, description)) + for event_desc in MOTION_SENSORS: + entities.append(ProtectEventSensor(data, device, event_desc)) _LOGGER.debug( "Adding sensor entity %s for %s", description.name, @@ -730,29 +738,24 @@ class ProtectNVRSensor(ProtectNVREntity, SensorEntity): self._attr_native_value = self.entity_description.get_ufp_value(self.device) -class ProtectEventSensor(ProtectDeviceSensor, EventThumbnailMixin): +class ProtectEventSensor(EventEntityMixin, SensorEntity): """A UniFi Protect Device Sensor with access tokens.""" - device: Camera + entity_description: ProtectSensorEventEntityDescription - @callback - def _async_get_event(self) -> Event | None: - """Get event from Protect device.""" - - event: Event | None = None - if ( - self.device.is_smart_detected - and self.device.last_smart_detect_event is not None - and len(self.device.last_smart_detect_event.smart_detect_types) > 0 - ): - event = self.device.last_smart_detect_event - - return event + def __init__( + self, + data: ProtectData, + device: ProtectAdoptableDeviceModel, + description: ProtectSensorEventEntityDescription, + ) -> None: + """Initialize an UniFi Protect sensor.""" + super().__init__(data, device, description) @callback def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None: # do not call ProtectDeviceSensor method since we want event to get value here - EventThumbnailMixin._async_update_device_from_protect(self, device) + EventEntityMixin._async_update_device_from_protect(self, device) if self._event is None: self._attr_native_value = OBJECT_TYPE_NONE else: diff --git a/homeassistant/components/unifiprotect/strings.json b/homeassistant/components/unifiprotect/strings.json index abac7701279..8bdd844fdbf 100644 --- a/homeassistant/components/unifiprotect/strings.json +++ b/homeassistant/components/unifiprotect/strings.json @@ -75,6 +75,10 @@ "ea_setup_failed": { "title": "Setup error using Early Access version", "description": "You are using v{version} of UniFi Protect which is an Early Access version. An unrecoverable error occurred while trying to load the integration. Please [downgrade to a stable version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) of UniFi Protect to continue using the integration.\n\nError: {error}" + }, + "deprecate_smart_sensor": { + "title": "Smart Detection Sensor Deprecated", + "description": "The unified \"Detected Object\" sensor for smart detections is now deprecated. It has been replaced with individual smart detection binary sensors for each smart detection type. Please update any templates or automations accordingly." } } } diff --git a/homeassistant/components/unifiprotect/translations/en.json b/homeassistant/components/unifiprotect/translations/en.json index 65a398375fe..21b3cd64360 100644 --- a/homeassistant/components/unifiprotect/translations/en.json +++ b/homeassistant/components/unifiprotect/translations/en.json @@ -42,6 +42,10 @@ } }, "issues": { + "deprecate_smart_sensor": { + "description": "The unified \"Detected Object\" sensor for smart detections is now deprecated. It has been replaced with individual smart detection binary sensors for each smart detection type. Please update any templates or automations accordingly.", + "title": "Smart Detection Sensor Deprecated" + }, "ea_setup_failed": { "description": "You are using v{version} of UniFi Protect which is an Early Access version. An unrecoverable error occurred while trying to load the integration. Please [downgrade to a stable version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) of UniFi Protect to continue using the integration.\n\nError: {error}", "title": "Setup error using Early Access version" diff --git a/tests/components/unifiprotect/test_binary_sensor.py b/tests/components/unifiprotect/test_binary_sensor.py index b2eec518d40..cb8d7bb659c 100644 --- a/tests/components/unifiprotect/test_binary_sensor.py +++ b/tests/components/unifiprotect/test_binary_sensor.py @@ -50,11 +50,11 @@ async def test_binary_sensor_camera_remove( ufp.api.bootstrap.nvr.system_info.ustorage = None await init_entry(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.BINARY_SENSOR, 3, 3) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 6, 6) await remove_entities(hass, ufp, [doorbell, unadopted_camera]) assert_entity_counts(hass, Platform.BINARY_SENSOR, 0, 0) await adopt_devices(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.BINARY_SENSOR, 3, 3) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 6, 6) async def test_binary_sensor_light_remove( @@ -120,7 +120,7 @@ async def test_binary_sensor_setup_camera_all( ufp.api.bootstrap.nvr.system_info.ustorage = None await init_entry(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.BINARY_SENSOR, 3, 3) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 6, 6) entity_registry = er.async_get(hass) @@ -167,7 +167,6 @@ async def test_binary_sensor_setup_camera_all( assert state assert state.state == STATE_OFF assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION - assert state.attributes[ATTR_EVENT_SCORE] == 0 async def test_binary_sensor_setup_camera_none( @@ -263,7 +262,7 @@ async def test_binary_sensor_update_motion( """Test binary_sensor motion entity.""" await init_entry(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.BINARY_SENSOR, 9, 9) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 12, 12) _, entity_id = ids_from_device_description( Platform.BINARY_SENSOR, doorbell, MOTION_SENSORS[0] diff --git a/tests/components/unifiprotect/test_repairs.py b/tests/components/unifiprotect/test_repairs.py index f6f677a1976..a1279dbed84 100644 --- a/tests/components/unifiprotect/test_repairs.py +++ b/tests/components/unifiprotect/test_repairs.py @@ -6,7 +6,7 @@ from copy import copy from http import HTTPStatus from unittest.mock import Mock -from pyunifiprotect.data import Version +from pyunifiprotect.data import Camera, Version from homeassistant.components.repairs.issue_handler import ( async_process_repairs_platforms, @@ -16,7 +16,9 @@ from homeassistant.components.repairs.websocket_api import ( RepairsFlowResourceView, ) from homeassistant.components.unifiprotect.const import DOMAIN +from homeassistant.const import Platform from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er from .utils import MockUFPFixture, init_entry @@ -40,9 +42,12 @@ async def test_ea_warning_ignore( msg = await ws_client.receive_json() assert msg["success"] - assert len(msg["result"]["issues"]) == 1 - issue = msg["result"]["issues"][0] - assert issue["issue_id"] == "ea_warning" + assert len(msg["result"]["issues"]) > 0 + issue = None + for i in msg["result"]["issues"]: + if i["issue_id"] == "ea_warning": + issue = i + assert issue is not None url = RepairsFlowIndexView.url resp = await client.post(url, json={"handler": DOMAIN, "issue_id": "ea_warning"}) @@ -89,9 +94,12 @@ async def test_ea_warning_fix( msg = await ws_client.receive_json() assert msg["success"] - assert len(msg["result"]["issues"]) == 1 - issue = msg["result"]["issues"][0] - assert issue["issue_id"] == "ea_warning" + assert len(msg["result"]["issues"]) > 0 + issue = None + for i in msg["result"]["issues"]: + if i["issue_id"] == "ea_warning": + issue = i + assert issue is not None url = RepairsFlowIndexView.url resp = await client.post(url, json={"handler": DOMAIN, "issue_id": "ea_warning"}) @@ -118,3 +126,53 @@ async def test_ea_warning_fix( data = await resp.json() assert data["type"] == "create_entry" + + +async def test_deprecate_smart_default( + hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera +): + """Test Deprecate Sensor repair does not exist by default (new installs).""" + + await init_entry(hass, ufp, [doorbell]) + + await async_process_repairs_platforms(hass) + ws_client = await hass_ws_client(hass) + + await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + issue = None + for i in msg["result"]["issues"]: + if i["issue_id"] == "deprecate_smart_sensor": + issue = i + assert issue is None + + +async def test_deprecate_smart_active( + hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera +): + """Test Deprecate Sensor repair exists for existing installs.""" + + registry = er.async_get(hass) + registry.async_get_or_create( + Platform.SENSOR, + DOMAIN, + f"{doorbell.mac}_detected_object", + config_entry=ufp.entry, + ) + + await init_entry(hass, ufp, [doorbell]) + + await async_process_repairs_platforms(hass) + ws_client = await hass_ws_client(hass) + + await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + issue = None + for i in msg["result"]["issues"]: + if i["issue_id"] == "deprecate_smart_sensor": + issue = i + assert issue is not None diff --git a/tests/components/unifiprotect/test_sensor.py b/tests/components/unifiprotect/test_sensor.py index a712c112b6d..f095260c38e 100644 --- a/tests/components/unifiprotect/test_sensor.py +++ b/tests/components/unifiprotect/test_sensor.py @@ -62,11 +62,11 @@ async def test_sensor_camera_remove( ufp.api.bootstrap.nvr.system_info.ustorage = None await init_entry(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.SENSOR, 25, 13) + assert_entity_counts(hass, Platform.SENSOR, 25, 12) await remove_entities(hass, ufp, [doorbell, unadopted_camera]) assert_entity_counts(hass, Platform.SENSOR, 12, 9) await adopt_devices(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.SENSOR, 25, 13) + assert_entity_counts(hass, Platform.SENSOR, 25, 12) async def test_sensor_sensor_remove( @@ -318,7 +318,7 @@ async def test_sensor_setup_camera( """Test sensor entity setup for camera devices.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SENSOR, 25, 13) + assert_entity_counts(hass, Platform.SENSOR, 25, 12) entity_registry = er.async_get(hass) @@ -406,11 +406,12 @@ async def test_sensor_setup_camera( assert entity assert entity.unique_id == unique_id + await enable_entity(hass, ufp.entry.entry_id, entity_id) + state = hass.states.get(entity_id) assert state assert state.state == OBJECT_TYPE_NONE assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION - assert state.attributes[ATTR_EVENT_SCORE] == 0 async def test_sensor_setup_camera_with_last_trip_time( @@ -451,12 +452,14 @@ async def test_sensor_update_motion( """Test sensor motion entity.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SENSOR, 25, 13) + assert_entity_counts(hass, Platform.SENSOR, 25, 12) _, entity_id = ids_from_device_description( Platform.SENSOR, doorbell, MOTION_SENSORS[0] ) + await enable_entity(hass, ufp.entry.entry_id, entity_id) + event = Event( id="test_event_id", type=EventType.SMART_DETECT, From 35e81cf982acda676595269551dead65f0775bd8 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 28 Nov 2022 20:42:10 +0100 Subject: [PATCH 0824/1033] Update aiohttp to 3.8.3 (#78860) --- homeassistant/package_constraints.txt | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 13204a293d2..0db98c905b2 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -1,7 +1,7 @@ PyJWT==2.5.0 PyNaCl==1.5.0 aiodiscover==1.4.13 -aiohttp==3.8.1 +aiohttp==3.8.3 aiohttp_cors==0.7.0 astral==2.2 async-upnp-client==0.32.2 diff --git a/pyproject.toml b/pyproject.toml index 41d8d1b50f5..a5b6c205c89 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ ] requires-python = ">=3.9.0" dependencies = [ - "aiohttp==3.8.1", + "aiohttp==3.8.3", "astral==2.2", "async_timeout==4.0.2", "attrs==21.2.0", diff --git a/requirements.txt b/requirements.txt index eca20f3047b..6c878206b07 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ -c homeassistant/package_constraints.txt # Home Assistant Core -aiohttp==3.8.1 +aiohttp==3.8.3 astral==2.2 async_timeout==4.0.2 attrs==21.2.0 From 5d4c4a1293b00e864f6ad202fbc565388d613e71 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 28 Nov 2022 20:55:22 +0100 Subject: [PATCH 0825/1033] Add humidifier support for Alexa (#81329) --- .../components/alexa/capabilities.py | 56 +++++++ homeassistant/components/alexa/const.py | 3 +- homeassistant/components/alexa/entities.py | 28 ++++ homeassistant/components/alexa/handlers.py | 78 ++++++++-- tests/components/alexa/test_capabilities.py | 66 +++++++++ tests/components/alexa/test_smart_home.py | 139 ++++++++++++++++++ tests/components/alexa/test_state_report.py | 62 +++++++- 7 files changed, 417 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/alexa/capabilities.py b/homeassistant/components/alexa/capabilities.py index 15870c7bbfa..f54f66b814c 100644 --- a/homeassistant/components/alexa/capabilities.py +++ b/homeassistant/components/alexa/capabilities.py @@ -8,6 +8,7 @@ from homeassistant.components import ( climate, cover, fan, + humidifier, image_processing, input_button, input_number, @@ -398,6 +399,8 @@ class AlexaPowerController(AlexaCapability): is_on = self.entity.state != climate.HVACMode.OFF elif self.entity.domain == fan.DOMAIN: is_on = self.entity.state == fan.STATE_ON + elif self.entity.domain == humidifier.DOMAIN: + is_on = self.entity.state == humidifier.STATE_ON elif self.entity.domain == vacuum.DOMAIN: is_on = self.entity.state == vacuum.STATE_CLEANING elif self.entity.domain == timer.DOMAIN: @@ -1403,6 +1406,12 @@ class AlexaModeController(AlexaCapability): if mode in self.entity.attributes.get(fan.ATTR_PRESET_MODES, None): return f"{fan.ATTR_PRESET_MODE}.{mode}" + # Humidifier mode + if self.instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_MODE}": + mode = self.entity.attributes.get(humidifier.ATTR_MODE, None) + if mode in self.entity.attributes.get(humidifier.ATTR_AVAILABLE_MODES, []): + return f"{humidifier.ATTR_MODE}.{mode}" + # Cover Position if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}": # Return state instead of position when using ModeController. @@ -1459,6 +1468,20 @@ class AlexaModeController(AlexaCapability): ) return self._resource.serialize_capability_resources() + # Humidifier modes + if self.instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_MODE}": + self._resource = AlexaModeResource([AlexaGlobalCatalog.SETTING_MODE], False) + modes = self.entity.attributes.get(humidifier.ATTR_AVAILABLE_MODES, []) + for mode in modes: + self._resource.add_mode(f"{humidifier.ATTR_MODE}.{mode}", [mode]) + # Humidifiers or Fans with a single mode completely break Alexa discovery, add a + # fake preset (see issue #53832). + if len(modes) == 1: + self._resource.add_mode( + f"{humidifier.ATTR_MODE}.{PRESET_MODE_NA}", [PRESET_MODE_NA] + ) + return self._resource.serialize_capability_resources() + # Cover Position Resources if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}": self._resource = AlexaModeResource( @@ -1600,6 +1623,12 @@ class AlexaRangeController(AlexaCapability): return self.entity.attributes.get(fan.ATTR_PERCENTAGE) return 100 if self.entity.state == fan.STATE_ON else 0 + # Humidifier target humidity + if self.instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}": + # If the humidifier is turned off the target humidity attribute is not set. + # We return 0 to make clear we do not know the current value. + return self.entity.attributes.get(humidifier.ATTR_HUMIDITY, 0) + # Input Number Value if self.instance == f"{input_number.DOMAIN}.{input_number.ATTR_VALUE}": return float(self.entity.state) @@ -1640,6 +1669,17 @@ class AlexaRangeController(AlexaCapability): ) return self._resource.serialize_capability_resources() + # Humidifier Target Humidity Resources + if self.instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}": + self._resource = AlexaPresetResource( + labels=["Humidity", "Percentage", "Target humidity"], + min_value=self.entity.attributes.get(humidifier.ATTR_MIN_HUMIDITY, 10), + max_value=self.entity.attributes.get(humidifier.ATTR_MAX_HUMIDITY, 90), + precision=1, + unit=AlexaGlobalCatalog.UNIT_PERCENT, + ) + return self._resource.serialize_capability_resources() + # Cover Position Resources if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}": self._resource = AlexaPresetResource( @@ -1764,6 +1804,22 @@ class AlexaRangeController(AlexaCapability): ) return self._semantics.serialize_semantics() + # Target Humidity Percentage + if self.instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}": + lower_labels = [AlexaSemantics.ACTION_LOWER] + raise_labels = [AlexaSemantics.ACTION_RAISE] + self._semantics = AlexaSemantics() + min_value = self.entity.attributes.get(humidifier.ATTR_MIN_HUMIDITY, 10) + max_value = self.entity.attributes.get(humidifier.ATTR_MAX_HUMIDITY, 90) + + self._semantics.add_action_to_directive( + lower_labels, "SetRangeValue", {"rangeValue": min_value} + ) + self._semantics.add_action_to_directive( + raise_labels, "SetRangeValue", {"rangeValue": max_value} + ) + return self._semantics.serialize_semantics() + return None diff --git a/homeassistant/components/alexa/const.py b/homeassistant/components/alexa/const.py index d1061720718..a34355b7ddb 100644 --- a/homeassistant/components/alexa/const.py +++ b/homeassistant/components/alexa/const.py @@ -83,7 +83,8 @@ API_THERMOSTAT_MODES_CUSTOM = { } API_THERMOSTAT_PRESETS = {climate.PRESET_ECO: "ECO"} -# AlexaModeController does not like a single mode for the fan preset, we add PRESET_MODE_NA if a fan has only one preset_mode +# AlexaModeController does not like a single mode for the fan preset or humidifier mode, +# we add PRESET_MODE_NA if a fan / humidifier has only one preset_mode PRESET_MODE_NA = "-" diff --git a/homeassistant/components/alexa/entities.py b/homeassistant/components/alexa/entities.py index e002969952a..35313573b19 100644 --- a/homeassistant/components/alexa/entities.py +++ b/homeassistant/components/alexa/entities.py @@ -15,6 +15,7 @@ from homeassistant.components import ( cover, fan, group, + humidifier, image_processing, input_boolean, input_button, @@ -100,6 +101,9 @@ class DisplayCategory: # to HDMI1. Applies to Scenes ACTIVITY_TRIGGER = "ACTIVITY_TRIGGER" + # Indicates a device that cools the air in interior spaces. + AIR_CONDITIONER = "AIR_CONDITIONER" + # Indicates a device that emits pleasant odors and masks unpleasant odors in interior spaces. AIR_FRESHENER = "AIR_FRESHENER" @@ -583,6 +587,30 @@ class FanCapabilities(AlexaEntity): yield Alexa(self.hass) +@ENTITY_ADAPTERS.register(humidifier.DOMAIN) +class HumidifierCapabilities(AlexaEntity): + """Class to represent Humidifier capabilities.""" + + def default_display_categories(self): + """Return the display categories for this entity.""" + return [DisplayCategory.OTHER] + + def interfaces(self): + """Yield the supported interfaces.""" + yield AlexaPowerController(self.entity) + supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) + if supported & humidifier.HumidifierEntityFeature.MODES: + yield AlexaModeController( + self.entity, instance=f"{humidifier.DOMAIN}.{humidifier.ATTR_MODE}" + ) + yield AlexaRangeController( + self.entity, instance=f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}" + ) + + yield AlexaEndpointHealth(self.hass, self.entity) + yield Alexa(self.hass) + + @ENTITY_ADAPTERS.register(lock.DOMAIN) class LockCapabilities(AlexaEntity): """Class to represent Lock capabilities.""" diff --git a/homeassistant/components/alexa/handlers.py b/homeassistant/components/alexa/handlers.py index b4c842dd5b5..3f816501cf3 100644 --- a/homeassistant/components/alexa/handlers.py +++ b/homeassistant/components/alexa/handlers.py @@ -14,6 +14,7 @@ from homeassistant.components import ( cover, fan, group, + humidifier, input_button, input_number, light, @@ -154,6 +155,8 @@ async def async_api_turn_on( service = cover.SERVICE_OPEN_COVER elif domain == fan.DOMAIN: service = fan.SERVICE_TURN_ON + elif domain == humidifier.DOMAIN: + service = humidifier.SERVICE_TURN_ON elif domain == vacuum.DOMAIN: supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) if ( @@ -201,6 +204,8 @@ async def async_api_turn_off( service = cover.SERVICE_CLOSE_COVER elif domain == fan.DOMAIN: service = fan.SERVICE_TURN_OFF + elif domain == humidifier.DOMAIN: + service = humidifier.SERVICE_TURN_OFF elif domain == vacuum.DOMAIN: supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) if ( @@ -448,20 +453,31 @@ async def async_api_set_percentage( """Process a set percentage request.""" entity = directive.entity - if entity.domain != fan.DOMAIN: + if entity.domain == fan.DOMAIN: + percentage = int(directive.payload["percentage"]) + service = fan.SERVICE_SET_PERCENTAGE + data = { + ATTR_ENTITY_ID: entity.entity_id, + fan.ATTR_PERCENTAGE: percentage, + } + + await hass.services.async_call( + entity.domain, service, data, blocking=False, context=context + ) + elif entity.domain == humidifier.DOMAIN: + percentage = int(directive.payload["percentage"]) + service = humidifier.SERVICE_SET_HUMIDITY + data = { + ATTR_ENTITY_ID: entity.entity_id, + humidifier.ATTR_HUMIDITY: percentage, + } + + await hass.services.async_call( + entity.domain, service, data, blocking=False, context=context + ) + else: raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED) - percentage = int(directive.payload["percentage"]) - service = fan.SERVICE_SET_PERCENTAGE - data = { - ATTR_ENTITY_ID: entity.entity_id, - fan.ATTR_PERCENTAGE: percentage, - } - - await hass.services.async_call( - entity.domain, service, data, blocking=False, context=context - ) - return directive.response() @@ -1130,6 +1146,18 @@ async def async_api_set_mode( msg = f"Entity '{entity.entity_id}' does not support Preset '{preset_mode}'" raise AlexaInvalidValueError(msg) + # Humidifier mode + elif instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_MODE}": + mode = mode.split(".")[1] + if mode != PRESET_MODE_NA and mode in entity.attributes.get( + humidifier.ATTR_AVAILABLE_MODES + ): + service = humidifier.SERVICE_SET_MODE + data[humidifier.ATTR_MODE] = mode + else: + msg = f"Entity '{entity.entity_id}' does not support Mode '{mode}'" + raise AlexaInvalidValueError(msg) + # Cover Position elif instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}": position = mode.split(".")[1] @@ -1306,6 +1334,12 @@ async def async_api_set_range( else: service = fan.SERVICE_TURN_ON + # Humidifier target humidity + elif instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}": + range_value = int(range_value) + service = humidifier.SERVICE_SET_HUMIDITY + data[humidifier.ATTR_HUMIDITY] = range_value + # Input Number Value elif instance == f"{input_number.DOMAIN}.{input_number.ATTR_VALUE}": range_value = float(range_value) @@ -1414,6 +1448,26 @@ async def async_api_adjust_range( else: service = fan.SERVICE_TURN_OFF + # Humidifier target humidity + elif instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}": + percentage_step = 5 + range_delta = ( + int(range_delta * percentage_step) + if range_delta_default + else int(range_delta) + ) + service = humidifier.SERVICE_SET_HUMIDITY + if not (current := entity.attributes.get(humidifier.ATTR_HUMIDITY)): + msg = f"Unable to determine {entity.entity_id} current target humidity" + raise AlexaInvalidValueError(msg) + min_value = entity.attributes.get(humidifier.ATTR_MIN_HUMIDITY, 10) + max_value = entity.attributes.get(humidifier.ATTR_MAX_HUMIDITY, 90) + percentage = response_value = min( + max_value, max(min_value, range_delta + current) + ) + if percentage: + data[humidifier.ATTR_HUMIDITY] = percentage + # Input Number Value elif instance == f"{input_number.DOMAIN}.{input_number.ATTR_VALUE}": range_delta = float(range_delta) diff --git a/tests/components/alexa/test_capabilities.py b/tests/components/alexa/test_capabilities.py index 608ff428d04..13d095f1cc6 100644 --- a/tests/components/alexa/test_capabilities.py +++ b/tests/components/alexa/test_capabilities.py @@ -411,6 +411,72 @@ async def test_report_fan_speed_state(hass): properties.assert_equal("Alexa.RangeController", "rangeValue", 0) +async def test_report_humidifier_humidity_state(hass): + """Test PercentageController, PowerLevelController reports humidifier humidity correctly.""" + hass.states.async_set( + "humidifier.dry", + "on", + { + "friendly_name": "Humidifier dry", + "supported_features": 0, + "humidity": 25, + "min_humidity": 20, + "max_humidity": 90, + }, + ) + hass.states.async_set( + "humidifier.wet", + "on", + { + "friendly_name": "Humidifier wet", + "supported_features": 0, + "humidity": 80, + "min_humidity": 20, + "max_humidity": 90, + }, + ) + properties = await reported_properties(hass, "humidifier.dry") + properties.assert_equal("Alexa.RangeController", "rangeValue", 25) + + properties = await reported_properties(hass, "humidifier.wet") + properties.assert_equal("Alexa.RangeController", "rangeValue", 80) + + +async def test_report_humidifier_mode(hass): + """Test ModeController reports humidifier mode correctly.""" + hass.states.async_set( + "humidifier.auto", + "on", + { + "friendly_name": "Humidifier auto", + "supported_features": 1, + "humidity": 50, + "mode": "Auto", + "available_modes": ["Auto", "Low", "Medium", "High"], + "min_humidity": 20, + "max_humidity": 90, + }, + ) + properties = await reported_properties(hass, "humidifier.auto") + properties.assert_equal("Alexa.ModeController", "mode", "mode.Auto") + + hass.states.async_set( + "humidifier.medium", + "on", + { + "friendly_name": "Humidifier auto", + "supported_features": 1, + "humidity": 60, + "mode": "Medium", + "available_modes": ["Auto", "Low", "Medium", "High"], + "min_humidity": 20, + "max_humidity": 90, + }, + ) + properties = await reported_properties(hass, "humidifier.medium") + properties.assert_equal("Alexa.ModeController", "mode", "mode.Medium") + + async def test_report_fan_preset_mode(hass): """Test ModeController reports fan preset_mode correctly.""" hass.states.async_set( diff --git a/tests/components/alexa/test_smart_home.py b/tests/components/alexa/test_smart_home.py index e2ae8741f20..3b76654f312 100644 --- a/tests/components/alexa/test_smart_home.py +++ b/tests/components/alexa/test_smart_home.py @@ -950,6 +950,145 @@ async def test_single_preset_mode_fan(hass, caplog): caplog.clear() +@freeze_time("2022-04-19 07:53:05") +async def test_humidifier(hass, caplog): + """Test humidifier controller.""" + device = ( + "humidifier.test_1", + "on", + { + "friendly_name": "Humidifier test 1", + "humidity": 66, + "supported_features": 1, + "mode": "Auto", + "available_modes": ["Auto", "Low", "Medium", "High"], + "min_humidity": 20, + "max_humidity": 90, + }, + ) + await discovery_test(device, hass) + + await assert_power_controller_works( + "humidifier#test_1", + "humidifier.turn_on", + "humidifier.turn_off", + hass, + "2022-04-19T07:53:05Z", + ) + + call, _ = await assert_request_calls_service( + "Alexa.ModeController", + "SetMode", + "humidifier#test_1", + "humidifier.set_mode", + hass, + payload={"mode": "mode.Auto"}, + instance="humidifier.mode", + ) + assert call.data["mode"] == "Auto" + + with pytest.raises(AssertionError): + await assert_request_calls_service( + "Alexa.ModeController", + "SetMode", + "humidifier#test_1", + "humidifier.set_mode", + hass, + payload={"mode": "mode.-"}, + instance="humidifier.mode", + ) + assert "Entity 'humidifier.test_1' does not support Mode '-'" in caplog.text + caplog.clear() + + call, _ = await assert_request_calls_service( + "Alexa.RangeController", + "SetRangeValue", + "humidifier#test_1", + "humidifier.set_humidity", + hass, + payload={"rangeValue": "67"}, + instance="humidifier.humidity", + ) + assert call.data["humidity"] == 67 + call, _ = await assert_request_calls_service( + "Alexa.RangeController", + "SetRangeValue", + "humidifier#test_1", + "humidifier.set_humidity", + hass, + payload={"rangeValue": "33"}, + instance="humidifier.humidity", + ) + assert call.data["humidity"] == 33 + + +async def test_humidifier_without_modes(hass): + """Test humidifier discovery without modes.""" + + device = ( + "humidifier.test_2", + "on", + { + "friendly_name": "Humidifier test 2", + "humidity": 33, + "supported_features": 0, + "min_humidity": 20, + "max_humidity": 90, + }, + ) + appliance = await discovery_test(device, hass) + + assert appliance["endpointId"] == "humidifier#test_2" + assert appliance["displayCategories"][0] == "OTHER" + assert appliance["friendlyName"] == "Humidifier test 2" + capabilities = assert_endpoint_capabilities( + appliance, + "Alexa.RangeController", + "Alexa.PowerController", + "Alexa.EndpointHealth", + "Alexa", + ) + + power_capability = get_capability(capabilities, "Alexa.PowerController") + assert "capabilityResources" not in power_capability + assert "configuration" not in power_capability + + +async def test_humidifier_with_modes(hass): + """Test humidifier discovery with modes.""" + + device = ( + "humidifier.test_1", + "on", + { + "friendly_name": "Humidifier test 1", + "humidity": 66, + "supported_features": 1, + "mode": "Auto", + "available_modes": ["Auto", "Low", "Medium", "High"], + "min_humidity": 20, + "max_humidity": 90, + }, + ) + appliance = await discovery_test(device, hass) + + assert appliance["endpointId"] == "humidifier#test_1" + assert appliance["displayCategories"][0] == "OTHER" + assert appliance["friendlyName"] == "Humidifier test 1" + capabilities = assert_endpoint_capabilities( + appliance, + "Alexa.ModeController", + "Alexa.RangeController", + "Alexa.PowerController", + "Alexa.EndpointHealth", + "Alexa", + ) + + power_capability = get_capability(capabilities, "Alexa.PowerController") + assert "capabilityResources" not in power_capability + assert "configuration" not in power_capability + + async def test_lock(hass): """Test lock discovery.""" device = ("lock.test", "off", {"friendly_name": "Test lock"}) diff --git a/tests/components/alexa/test_state_report.py b/tests/components/alexa/test_state_report.py index bb61cea2413..ed70afc02d6 100644 --- a/tests/components/alexa/test_state_report.py +++ b/tests/components/alexa/test_state_report.py @@ -209,8 +209,8 @@ async def test_report_state_unsets_authorized_on_access_token_error( config._store.set_authorized.assert_called_once_with(False) -async def test_report_state_instance(hass, aioclient_mock): - """Test proactive state reports with instance.""" +async def test_report_state_fan(hass, aioclient_mock): + """Test proactive state reports with fan instance.""" aioclient_mock.post(TEST_URL, text="", status=202) hass.states.async_set( @@ -275,6 +275,64 @@ async def test_report_state_instance(hass, aioclient_mock): assert call_json["event"]["endpoint"]["endpointId"] == "fan#test_fan" +async def test_report_state_humidifier(hass, aioclient_mock): + """Test proactive state reports with humidifier instance.""" + aioclient_mock.post(TEST_URL, text="", status=202) + + hass.states.async_set( + "humidifier.test_humidifier", + "off", + { + "friendly_name": "Test humidifier", + "supported_features": 1, + "mode": None, + "available_modes": ["auto", "smart"], + }, + ) + + await state_report.async_enable_proactive_mode(hass, get_default_config(hass)) + + hass.states.async_set( + "humidifier.test_humidifier", + "on", + { + "friendly_name": "Test humidifier", + "supported_features": 1, + "mode": "smart", + "available_modes": ["auto", "smart"], + "humidity": 55, + }, + ) + + # To trigger event listener + await hass.async_block_till_done() + + assert len(aioclient_mock.mock_calls) == 1 + call = aioclient_mock.mock_calls + + call_json = call[0][2] + assert call_json["event"]["header"]["namespace"] == "Alexa" + assert call_json["event"]["header"]["name"] == "ChangeReport" + + change_reports = call_json["event"]["payload"]["change"]["properties"] + + checks = 0 + for report in change_reports: + if report["name"] == "mode": + assert report["value"] == "mode.smart" + assert report["instance"] == "humidifier.mode" + assert report["namespace"] == "Alexa.ModeController" + checks += 1 + if report["name"] == "rangeValue": + assert report["value"] == 55 + assert report["instance"] == "humidifier.humidity" + assert report["namespace"] == "Alexa.RangeController" + checks += 1 + assert checks == 2 + + assert call_json["event"]["endpoint"]["endpointId"] == "humidifier#test_humidifier" + + async def test_send_add_or_update_message(hass, aioclient_mock): """Test sending an AddOrUpdateReport message.""" aioclient_mock.post(TEST_URL, text="") From 71eb45072c3b195cc4cd70a689a55b182f5e9feb Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Mon, 28 Nov 2022 14:57:44 -0500 Subject: [PATCH 0826/1033] Add device action for `text` platform (#82773) --- .../components/text/device_action.py | 80 ++++++++ tests/components/text/test_device_action.py | 185 ++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 homeassistant/components/text/device_action.py create mode 100644 tests/components/text/test_device_action.py diff --git a/homeassistant/components/text/device_action.py b/homeassistant/components/text/device_action.py new file mode 100644 index 00000000000..3d14da9bdb8 --- /dev/null +++ b/homeassistant/components/text/device_action.py @@ -0,0 +1,80 @@ +"""Provides device actions for Text.""" +from __future__ import annotations + +import voluptuous as vol + +from homeassistant.const import ( + ATTR_ENTITY_ID, + CONF_DEVICE_ID, + CONF_DOMAIN, + CONF_ENTITY_ID, + CONF_TYPE, +) +from homeassistant.core import Context, HomeAssistant +from homeassistant.helpers import entity_registry +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.typing import ConfigType, TemplateVarsType + +from .const import ATTR_VALUE, DOMAIN, SERVICE_SET_VALUE + +ATYP_SET_VALUE = "set_value" + +ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend( + { + vol.Required(CONF_TYPE): ATYP_SET_VALUE, + vol.Required(CONF_ENTITY_ID): cv.entity_domain(DOMAIN), + vol.Required(ATTR_VALUE): cv.string, + } +) + + +async def async_get_actions( + hass: HomeAssistant, device_id: str +) -> list[dict[str, str]]: + """List device actions for Text.""" + registry = entity_registry.async_get(hass) + actions: list[dict[str, str]] = [] + + # Get all the integrations entities for this device + for entry in entity_registry.async_entries_for_device(registry, device_id): + if entry.domain != DOMAIN: + continue + + actions.append( + { + CONF_DEVICE_ID: device_id, + CONF_DOMAIN: DOMAIN, + CONF_ENTITY_ID: entry.entity_id, + CONF_TYPE: ATYP_SET_VALUE, + } + ) + + return actions + + +async def async_call_action_from_config( + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, +) -> None: + """Execute a device action.""" + await hass.services.async_call( + DOMAIN, + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: config[CONF_ENTITY_ID], + ATTR_VALUE: config[ATTR_VALUE], + }, + blocking=True, + context=context, + ) + + +async def async_get_action_capabilities( + hass: HomeAssistant, config: ConfigType +) -> dict[str, vol.Schema]: + """List action capabilities.""" + fields = {vol.Required(ATTR_VALUE): cv.string} + + return {"extra_fields": vol.Schema(fields)} diff --git a/tests/components/text/test_device_action.py b/tests/components/text/test_device_action.py new file mode 100644 index 00000000000..abddb5092c4 --- /dev/null +++ b/tests/components/text/test_device_action.py @@ -0,0 +1,185 @@ +"""The tests for Text device actions.""" +import pytest +import voluptuous_serialize + +import homeassistant.components.automation as automation +from homeassistant.components.device_automation import DeviceAutomationType +from homeassistant.components.text import DOMAIN, device_action +from homeassistant.helpers import config_validation as cv, device_registry +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_registry import RegistryEntryHider +from homeassistant.setup import async_setup_component + +from tests.common import ( + MockConfigEntry, + assert_lists_same, + async_get_device_automations, + async_mock_service, + mock_device_registry, + mock_registry, +) +from tests.components.blueprint.conftest import stub_blueprint_populate # noqa: F401 + + +@pytest.fixture +def device_reg(hass): + """Return an empty, loaded, registry.""" + return mock_device_registry(hass) + + +@pytest.fixture +def entity_reg(hass): + """Return an empty, loaded, registry.""" + return mock_registry(hass) + + +async def test_get_actions(hass, device_reg, entity_reg): + """Test we get the expected actions for an entity.""" + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + device_entry = device_reg.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + ) + entity_reg.async_get_or_create(DOMAIN, "test", "5678", device_id=device_entry.id) + hass.states.async_set("text.test_5678", 0.5, {"min_value": 0.0, "max_value": 1.0}) + expected_actions = [ + { + "domain": DOMAIN, + "type": "set_value", + "device_id": device_entry.id, + "entity_id": "text.test_5678", + "metadata": {"secondary": False}, + }, + ] + actions = await async_get_device_automations( + hass, DeviceAutomationType.ACTION, device_entry.id + ) + assert_lists_same(actions, expected_actions) + + +@pytest.mark.parametrize( + "hidden_by,entity_category", + ( + (RegistryEntryHider.INTEGRATION, None), + (RegistryEntryHider.USER, None), + (None, EntityCategory.CONFIG), + (None, EntityCategory.DIAGNOSTIC), + ), +) +async def test_get_actions_hidden_auxiliary( + hass, + device_reg, + entity_reg, + hidden_by, + entity_category, +): + """Test we get the expected actions from a hidden or auxiliary entity.""" + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + device_entry = device_reg.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + ) + entity_reg.async_get_or_create( + DOMAIN, + "test", + "5678", + device_id=device_entry.id, + entity_category=entity_category, + hidden_by=hidden_by, + ) + expected_actions = [] + expected_actions += [ + { + "domain": DOMAIN, + "type": action, + "device_id": device_entry.id, + "entity_id": f"{DOMAIN}.test_5678", + "metadata": {"secondary": True}, + } + for action in ["set_value"] + ] + actions = await async_get_device_automations( + hass, DeviceAutomationType.ACTION, device_entry.id + ) + assert_lists_same(actions, expected_actions) + + +async def test_get_action_no_state(hass, device_reg, entity_reg): + """Test we get the expected actions for an entity.""" + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + device_entry = device_reg.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + ) + entity_reg.async_get_or_create(DOMAIN, "test", "5678", device_id=device_entry.id) + expected_actions = [ + { + "domain": DOMAIN, + "type": "set_value", + "device_id": device_entry.id, + "entity_id": "text.test_5678", + "metadata": {"secondary": False}, + }, + ] + actions = await async_get_device_automations( + hass, DeviceAutomationType.ACTION, device_entry.id + ) + assert_lists_same(actions, expected_actions) + + +async def test_action(hass): + """Test for actions.""" + hass.states.async_set("text.entity", 0.5, {"min_value": 0.0, "max_value": 1.0}) + + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + { + "trigger": { + "platform": "event", + "event_type": "test_event_set_value", + }, + "action": { + "domain": DOMAIN, + "device_id": "abcdefgh", + "entity_id": "text.entity", + "type": "set_value", + "value": 0.3, + }, + }, + ] + }, + ) + + calls = async_mock_service(hass, DOMAIN, "set_value") + + assert len(calls) == 0 + + hass.bus.async_fire("test_event_set_value") + await hass.async_block_till_done() + + assert len(calls) == 1 + + +async def test_capabilities(hass): + """Test getting capabilities.""" + capabilities = await device_action.async_get_action_capabilities( + hass, + { + "domain": DOMAIN, + "device_id": "abcdefgh", + "entity_id": "text.entity", + "type": "set_value", + }, + ) + + assert capabilities and "extra_fields" in capabilities + + assert voluptuous_serialize.convert( + capabilities["extra_fields"], custom_serializer=cv.custom_serializer + ) == [{"name": "value", "required": True, "type": "string"}] From eb25968b31dcf76d6790cee99ff30ead3d27b8af Mon Sep 17 00:00:00 2001 From: ark Date: Mon, 28 Nov 2022 13:22:57 -0800 Subject: [PATCH 0827/1033] Add valid country tests to workday sensors (#82799) fixes undefined --- tests/components/workday/test_binary_sensor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/components/workday/test_binary_sensor.py b/tests/components/workday/test_binary_sensor.py index f8ab8794c0d..f0d2d6b0681 100644 --- a/tests/components/workday/test_binary_sensor.py +++ b/tests/components/workday/test_binary_sensor.py @@ -125,6 +125,10 @@ class TestWorkdaySetup: with pytest.raises(vol.Invalid): binary_sensor.valid_country("HomeAssistantLand") + # Valid country code validation must not raise an exception + for country in ("IM", "LI", "US"): + assert binary_sensor.valid_country(country) == country + def test_setup_component_province(self): """Set up workday component.""" with assert_setup_component(1, "binary_sensor"): From 82b2aaaa3ead906f55ee5a0da8e63763a81b4a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 28 Nov 2022 22:38:31 +0100 Subject: [PATCH 0828/1033] Patch entry setup in upcloud test (#82882) --- tests/components/upcloud/test_config_flow.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/components/upcloud/test_config_flow.py b/tests/components/upcloud/test_config_flow.py index 4bbc7c51b9d..5757469fafe 100644 --- a/tests/components/upcloud/test_config_flow.py +++ b/tests/components/upcloud/test_config_flow.py @@ -1,5 +1,7 @@ """Tests for the UpCloud config flow.""" +from unittest.mock import patch + import requests.exceptions from requests_mock import ANY from upcloud_api import UpCloudAPIError @@ -81,6 +83,10 @@ async def test_options(hass): ) config_entry.add_to_hass(hass) + with patch("homeassistant.components.upcloud.async_setup_entry", return_value=True): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + result = await hass.config_entries.options.async_init(config_entry.entry_id) assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "init" From 7c82b78f8c6b4a045ee5274bcd86851f75a26d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 28 Nov 2022 23:35:24 +0100 Subject: [PATCH 0829/1033] Add repair for legacy subscription to cloud integration (#82621) --- homeassistant/components/cloud/__init__.py | 18 ++ homeassistant/components/cloud/http_api.py | 15 +- homeassistant/components/cloud/repairs.py | 121 +++++++++ homeassistant/components/cloud/strings.json | 15 ++ .../components/cloud/subscription.py | 24 ++ .../components/cloud/translations/en.json | 15 ++ tests/components/cloud/conftest.py | 9 + tests/components/cloud/test_http_api.py | 7 - tests/components/cloud/test_repairs.py | 232 ++++++++++++++++++ 9 files changed, 442 insertions(+), 14 deletions(-) create mode 100644 homeassistant/components/cloud/repairs.py create mode 100644 homeassistant/components/cloud/subscription.py create mode 100644 tests/components/cloud/test_repairs.py diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 6a948c0ad15..0662487b765 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio from collections.abc import Awaitable, Callable +from datetime import timedelta from enum import Enum from hass_nabucasa import Cloud @@ -26,6 +27,7 @@ from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, ) +from homeassistant.helpers.event import async_call_later from homeassistant.helpers.service import async_register_admin_service from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass @@ -55,6 +57,8 @@ from .const import ( MODE_PROD, ) from .prefs import CloudPreferences +from .repairs import async_manage_legacy_subscription_issue +from .subscription import async_subscription_info DEFAULT_MODE = MODE_PROD @@ -258,6 +262,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: loaded = False + async def async_startup_repairs(_=None) -> None: + """Create repair issues after startup.""" + if not cloud.is_logged_in: + return + + if subscription_info := await async_subscription_info(cloud): + async_manage_legacy_subscription_issue(hass, subscription_info) + async def _on_connect(): """Discover RemoteUI binary sensor.""" nonlocal loaded @@ -294,6 +306,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: account_link.async_setup(hass) + async_call_later( + hass=hass, + delay=timedelta(hours=1), + action=async_startup_repairs, + ) + return True diff --git a/homeassistant/components/cloud/http_api.py b/homeassistant/components/cloud/http_api.py index 01b6cd17508..76d8bea1664 100644 --- a/homeassistant/components/cloud/http_api.py +++ b/homeassistant/components/cloud/http_api.py @@ -9,7 +9,7 @@ from typing import Any import aiohttp import async_timeout import attr -from hass_nabucasa import Cloud, auth, cloud_api, thingtalk +from hass_nabucasa import Cloud, auth, thingtalk from hass_nabucasa.const import STATE_DISCONNECTED from hass_nabucasa.voice import MAP_VOICE import voluptuous as vol @@ -38,6 +38,8 @@ from .const import ( PREF_TTS_DEFAULT_VOICE, REQUEST_TIMEOUT, ) +from .repairs import async_manage_legacy_subscription_issue +from .subscription import async_subscription_info _LOGGER = logging.getLogger(__name__) @@ -328,15 +330,14 @@ async def websocket_subscription( ) -> None: """Handle request for account info.""" cloud = hass.data[DOMAIN] - try: - async with async_timeout.timeout(REQUEST_TIMEOUT): - data = await cloud_api.async_subscription_info(cloud) - except aiohttp.ClientError: + if (data := await async_subscription_info(cloud)) is None: connection.send_error( msg["id"], "request_failed", "Failed to request subscription" ) - else: - connection.send_result(msg["id"], data) + return + + connection.send_result(msg["id"], data) + async_manage_legacy_subscription_issue(hass, data) @_require_cloud_login diff --git a/homeassistant/components/cloud/repairs.py b/homeassistant/components/cloud/repairs.py new file mode 100644 index 00000000000..779c0eb64b0 --- /dev/null +++ b/homeassistant/components/cloud/repairs.py @@ -0,0 +1,121 @@ +"""Repairs implementation for the cloud integration.""" +from __future__ import annotations + +import asyncio +from typing import Any + +from hass_nabucasa import Cloud +import voluptuous as vol + +from homeassistant.components.repairs import RepairsFlow, repairs_flow_manager +from homeassistant.core import HomeAssistant, callback +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import issue_registry as ir + +from .const import DOMAIN +from .subscription import async_subscription_info + +BACKOFF_TIME = 5 +MAX_RETRIES = 60 # This allows for 10 minutes of retries + + +@callback +def async_manage_legacy_subscription_issue( + hass: HomeAssistant, + subscription_info: dict[str, Any], +) -> None: + """ + Manage the legacy subscription issue. + + If the provider is "legacy" create an issue, + in all other cases remove the issue. + """ + if subscription_info["provider"] == "legacy": + ir.async_create_issue( + hass=hass, + domain=DOMAIN, + issue_id="legacy_subscription", + is_fixable=True, + severity=ir.IssueSeverity.WARNING, + translation_key="legacy_subscription", + ) + return + ir.async_delete_issue(hass=hass, domain=DOMAIN, issue_id="legacy_subscription") + + +class LegacySubscriptionRepairFlow(RepairsFlow): + """Handler for an issue fixing flow.""" + + wait_task: asyncio.Task | None = None + _data: dict[str, Any] | None = None + + async def async_step_init(self, _: None = None) -> FlowResult: + """Handle the first step of a fix flow.""" + return await self.async_step_confirm_change_plan() + + async def async_step_confirm_change_plan( + self, + user_input: dict[str, str] | None = None, + ) -> FlowResult: + """Handle the confirm step of a fix flow.""" + if user_input is not None: + return await self.async_step_change_plan() + + return self.async_show_form( + step_id="confirm_change_plan", data_schema=vol.Schema({}) + ) + + async def async_step_change_plan(self, _: None = None) -> FlowResult: + """Wait for the user to authorize the app installation.""" + + async def _async_wait_for_plan_change() -> None: + flow_manager = repairs_flow_manager(self.hass) + # We can not get here without a flow manager + assert flow_manager is not None + + cloud: Cloud = self.hass.data[DOMAIN] + + retries = 0 + while retries < MAX_RETRIES: + self._data = await async_subscription_info(cloud) + if self._data is not None and self._data["provider"] != "legacy": + break + + retries += 1 + await asyncio.sleep(BACKOFF_TIME) + + self.hass.async_create_task( + flow_manager.async_configure(flow_id=self.flow_id) + ) + + if not self.wait_task: + self.wait_task = self.hass.async_create_task(_async_wait_for_plan_change()) + return self.async_external_step( + step_id="change_plan", + url="https://account.nabucasa.com/", + ) + + await self.wait_task + + if self._data is None or self._data["provider"] == "legacy": + # If we get here we waited too long. + return self.async_external_step_done(next_step_id="timeout") + + return self.async_external_step_done(next_step_id="complete") + + async def async_step_complete(self, _: None = None) -> FlowResult: + """Handle the final step of a fix flow.""" + return self.async_create_entry(title="", data={}) + + async def async_step_timeout(self, _: None = None) -> FlowResult: + """Handle the final step of a fix flow.""" + return self.async_abort(reason="operation_took_too_long") + + +async def async_create_fix_flow( + hass: HomeAssistant, + issue_id: str, + data: dict[str, str | int | float | None] | None, +) -> RepairsFlow: + """Create flow.""" + return LegacySubscriptionRepairFlow() diff --git a/homeassistant/components/cloud/strings.json b/homeassistant/components/cloud/strings.json index a799a8cee59..e437fca9ed3 100644 --- a/homeassistant/components/cloud/strings.json +++ b/homeassistant/components/cloud/strings.json @@ -13,5 +13,20 @@ "logged_in": "Logged In", "subscription_expiration": "Subscription Expiration" } + }, + "issues": { + "legacy_subscription": { + "title": "Legacy subscription detected", + "fix_flow": { + "step": { + "confirm_change_plan": { + "description": "We've recently updated our subscription system. To continue using Home Assistant Cloud you need to one-time approve the change in PayPal.\n\nThis takes 1 minute and will not increase the price." + } + }, + "abort": { + "operation_took_too_long": "The operation took too long. Please try again later." + } + } + } } } diff --git a/homeassistant/components/cloud/subscription.py b/homeassistant/components/cloud/subscription.py new file mode 100644 index 00000000000..9a2e5bd87cf --- /dev/null +++ b/homeassistant/components/cloud/subscription.py @@ -0,0 +1,24 @@ +"""Subscription information.""" +from __future__ import annotations + +import logging +from typing import Any + +from aiohttp.client_exceptions import ClientError +import async_timeout +from hass_nabucasa import Cloud, cloud_api + +from .const import REQUEST_TIMEOUT + +_LOGGER = logging.getLogger(__name__) + + +async def async_subscription_info(cloud: Cloud) -> dict[str, Any] | None: + """Fetch the subscription info.""" + try: + async with async_timeout.timeout(REQUEST_TIMEOUT): + return await cloud_api.async_subscription_info(cloud) + except ClientError: + _LOGGER.error("Failed to fetch subscription information") + + return None diff --git a/homeassistant/components/cloud/translations/en.json b/homeassistant/components/cloud/translations/en.json index 7577a9a51e4..fa7376f80c8 100644 --- a/homeassistant/components/cloud/translations/en.json +++ b/homeassistant/components/cloud/translations/en.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "The operation took too long. Please try again later." + }, + "step": { + "confirm_change_plan": { + "description": "We've recently updated our subscription system. To continue using Home Assistant Cloud you need to one-time approve the change in PayPal.\n\nThis takes 1 minute and will not increase the price." + } + } + }, + "title": "Legacy subscription detected" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa Enabled", diff --git a/tests/components/cloud/conftest.py b/tests/components/cloud/conftest.py index 4bd5868db5f..e16fb63b34a 100644 --- a/tests/components/cloud/conftest.py +++ b/tests/components/cloud/conftest.py @@ -52,6 +52,15 @@ def mock_cloud_login(hass, mock_cloud_setup): yield +@pytest.fixture(name="mock_auth") +def mock_auth_fixture(): + """Mock check token.""" + with patch("hass_nabucasa.auth.CognitoAuth.async_check_token"), patch( + "hass_nabucasa.auth.CognitoAuth.async_renew_access_token" + ): + yield + + @pytest.fixture def mock_expired_cloud_login(hass, mock_cloud_setup): """Mock cloud is logged in.""" diff --git a/tests/components/cloud/test_http_api.py b/tests/components/cloud/test_http_api.py index 4d0729d72b2..1f9af960f08 100644 --- a/tests/components/cloud/test_http_api.py +++ b/tests/components/cloud/test_http_api.py @@ -24,13 +24,6 @@ from tests.components.google_assistant import MockConfig SUBSCRIPTION_INFO_URL = "https://api-test.hass.io/subscription_info" -@pytest.fixture(name="mock_auth") -def mock_auth_fixture(): - """Mock check token.""" - with patch("hass_nabucasa.auth.CognitoAuth.async_check_token"): - yield - - @pytest.fixture(name="mock_cloud_login") def mock_cloud_login_fixture(hass, setup_api): """Mock cloud is logged in.""" diff --git a/tests/components/cloud/test_repairs.py b/tests/components/cloud/test_repairs.py new file mode 100644 index 00000000000..ef96efaa402 --- /dev/null +++ b/tests/components/cloud/test_repairs.py @@ -0,0 +1,232 @@ +"""Test cloud repairs.""" +from collections.abc import Awaitable, Callable, Generator +from datetime import timedelta +from http import HTTPStatus +from unittest.mock import AsyncMock, patch + +from aiohttp import ClientSession + +from homeassistant.components.cloud import DOMAIN +import homeassistant.components.cloud.repairs as cloud_repairs +from homeassistant.components.repairs import DOMAIN as REPAIRS_DOMAIN +from homeassistant.core import HomeAssistant +import homeassistant.helpers.issue_registry as ir +from homeassistant.setup import async_setup_component +from homeassistant.util import dt + +from . import mock_cloud + +from tests.common import async_fire_time_changed +from tests.test_util.aiohttp import AiohttpClientMocker + + +async def test_do_not_create_repair_issues_at_startup_if_not_logged_in( + hass: HomeAssistant, +): + """Test that we create repair issue at startup if we are logged in.""" + issue_registry: ir.IssueRegistry = ir.async_get(hass) + + with patch("homeassistant.components.cloud.Cloud.is_logged_in", False): + await mock_cloud(hass) + + async_fire_time_changed(hass, dt.utcnow() + timedelta(hours=1)) + await hass.async_block_till_done() + + assert not issue_registry.async_get_issue( + domain="cloud", issue_id="legacy_subscription" + ) + + +async def test_create_repair_issues_at_startup_if_logged_in( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + mock_auth: Generator[None, AsyncMock, None], +): + """Test that we create repair issue at startup if we are logged in.""" + issue_registry: ir.IssueRegistry = ir.async_get(hass) + aioclient_mock.get( + "https://accounts.nabucasa.com/payments/subscription_info", + json={"provider": "legacy"}, + ) + + with patch("homeassistant.components.cloud.Cloud.is_logged_in", True): + await mock_cloud(hass) + + async_fire_time_changed(hass, dt.utcnow() + timedelta(hours=1)) + await hass.async_block_till_done() + + assert issue_registry.async_get_issue( + domain="cloud", issue_id="legacy_subscription" + ) + + +async def test_legacy_subscription_delete_issue_if_no_longer_legacy( + hass: HomeAssistant, +): + """Test that we delete the legacy subscription issue if no longer legacy.""" + issue_registry: ir.IssueRegistry = ir.async_get(hass) + cloud_repairs.async_manage_legacy_subscription_issue(hass, {"provider": "legacy"}) + assert issue_registry.async_get_issue( + domain="cloud", issue_id="legacy_subscription" + ) + + cloud_repairs.async_manage_legacy_subscription_issue(hass, {"provider": None}) + assert not issue_registry.async_get_issue( + domain="cloud", issue_id="legacy_subscription" + ) + + +async def test_legacy_subscription_repair_flow( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + mock_auth: Generator[None, AsyncMock, None], + hass_client: Callable[..., Awaitable[ClientSession]], +): + """Test desired flow of the fix flow for legacy subscription.""" + issue_registry: ir.IssueRegistry = ir.async_get(hass) + aioclient_mock.get( + "https://accounts.nabucasa.com/payments/subscription_info", + json={"provider": None}, + ) + + cloud_repairs.async_manage_legacy_subscription_issue(hass, {"provider": "legacy"}) + repair_issue = issue_registry.async_get_issue( + domain="cloud", issue_id="legacy_subscription" + ) + assert repair_issue + + assert await async_setup_component(hass, REPAIRS_DOMAIN, {REPAIRS_DOMAIN: {}}) + await mock_cloud(hass) + await hass.async_block_till_done() + await hass.async_start() + + client = await hass_client() + + resp = await client.post( + "/api/repairs/issues/fix", + json={"handler": DOMAIN, "issue_id": repair_issue.issue_id}, + ) + + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data == { + "type": "form", + "flow_id": flow_id, + "handler": DOMAIN, + "step_id": "confirm_change_plan", + "data_schema": [], + "errors": None, + "description_placeholders": None, + "last_step": None, + } + + resp = await client.post(f"/api/repairs/issues/fix/{flow_id}") + + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data == { + "type": "external", + "flow_id": flow_id, + "handler": DOMAIN, + "step_id": "change_plan", + "url": "https://account.nabucasa.com/", + "description_placeholders": None, + } + + resp = await client.post(f"/api/repairs/issues/fix/{flow_id}") + + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data == { + "version": 1, + "type": "create_entry", + "flow_id": flow_id, + "handler": DOMAIN, + "title": "", + "description": None, + "description_placeholders": None, + } + + assert not issue_registry.async_get_issue( + domain="cloud", issue_id="legacy_subscription" + ) + + +async def test_legacy_subscription_repair_flow_timeout( + hass: HomeAssistant, + hass_client: Callable[..., Awaitable[ClientSession]], +): + """Test timeout flow of the fix flow for legacy subscription.""" + issue_registry: ir.IssueRegistry = ir.async_get(hass) + + cloud_repairs.async_manage_legacy_subscription_issue(hass, {"provider": "legacy"}) + repair_issue = issue_registry.async_get_issue( + domain="cloud", issue_id="legacy_subscription" + ) + assert repair_issue + + assert await async_setup_component(hass, REPAIRS_DOMAIN, {REPAIRS_DOMAIN: {}}) + await mock_cloud(hass) + await hass.async_block_till_done() + await hass.async_start() + + client = await hass_client() + + resp = await client.post( + "/api/repairs/issues/fix", + json={"handler": DOMAIN, "issue_id": repair_issue.issue_id}, + ) + + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data == { + "type": "form", + "flow_id": flow_id, + "handler": DOMAIN, + "step_id": "confirm_change_plan", + "data_schema": [], + "errors": None, + "description_placeholders": None, + "last_step": None, + } + + with patch("homeassistant.components.cloud.repairs.MAX_RETRIES", new=0): + resp = await client.post(f"/api/repairs/issues/fix/{flow_id}") + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data == { + "type": "external", + "flow_id": flow_id, + "handler": DOMAIN, + "step_id": "change_plan", + "url": "https://account.nabucasa.com/", + "description_placeholders": None, + } + + resp = await client.post(f"/api/repairs/issues/fix/{flow_id}") + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data == { + "type": "abort", + "flow_id": flow_id, + "handler": "cloud", + "reason": "operation_took_too_long", + "description_placeholders": None, + "result": None, + } + + assert issue_registry.async_get_issue( + domain="cloud", issue_id="legacy_subscription" + ) From 230b50d099ed3099cc948e3d2ceb24c8d5765825 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 29 Nov 2022 00:26:01 +0000 Subject: [PATCH 0830/1033] [ci skip] Translation update --- .../components/abode/translations/sk.json | 3 +- .../accuweather/translations/fr.json | 5 + .../accuweather/translations/no.json | 6 + .../accuweather/translations/sk.json | 5 + .../components/adguard/translations/sk.json | 1 + .../components/airly/translations/sk.json | 4 +- .../components/airnow/translations/sk.json | 3 +- .../components/airtouch4/translations/sk.json | 6 +- .../components/airvisual/translations/sk.json | 22 ++- .../components/airzone/translations/fr.json | 4 +- .../components/airzone/translations/sk.json | 3 +- .../alarm_control_panel/translations/sk.json | 3 + .../alarmdecoder/translations/sk.json | 18 ++- .../components/almond/translations/sk.json | 3 + .../components/androidtv/translations/sk.json | 8 +- .../components/anthemav/translations/sk.json | 3 +- .../components/apcupsd/translations/sk.json | 3 +- .../components/apple_tv/translations/sk.json | 1 + .../components/aranet/translations/sk.json | 3 +- .../components/asuswrt/translations/sk.json | 7 +- .../aurora_abb_powerone/translations/sk.json | 8 +- .../aussie_broadband/translations/sk.json | 6 +- .../components/auth/translations/sk.json | 9 +- .../components/awair/translations/sk.json | 6 + .../components/axis/translations/sk.json | 3 +- .../azure_event_hub/translations/sk.json | 5 + .../components/balboa/translations/sk.json | 3 +- .../binary_sensor/translations/sk.json | 59 +++++++- .../components/blink/translations/sk.json | 12 +- .../bmw_connected_drive/translations/sk.json | 3 +- .../components/bond/translations/sk.json | 1 + .../components/bosch_shc/translations/sk.json | 20 ++- .../components/braviatv/translations/sk.json | 24 +++- .../components/broadlink/translations/sk.json | 3 + .../components/brother/translations/sk.json | 14 +- .../components/brunt/translations/sk.json | 4 +- .../components/bsblan/translations/sk.json | 3 +- .../components/bthome/translations/sk.json | 3 +- .../components/button/translations/sk.json | 6 +- .../components/camera/translations/de.json | 2 +- .../components/canary/translations/sk.json | 4 +- .../components/cast/translations/sk.json | 3 + .../cert_expiry/translations/sk.json | 10 +- .../climacell/translations/sensor.sk.json | 1 + .../components/cloud/translations/de.json | 15 ++ .../components/cloud/translations/es.json | 15 ++ .../components/cloud/translations/sk.json | 6 +- .../cloudflare/translations/sk.json | 1 + .../components/control4/translations/sk.json | 3 +- .../coolmaster/translations/sk.json | 8 +- .../coronavirus/translations/sk.json | 7 + .../components/cpuspeed/translations/sk.json | 3 +- .../crownstone/translations/sk.json | 24 ++++ .../components/daikin/translations/sk.json | 3 +- .../components/deconz/translations/sk.json | 44 +++++- .../components/demo/translations/fr.json | 8 ++ .../components/demo/translations/no.json | 8 ++ .../components/demo/translations/sk.json | 37 +++++ .../components/denonavr/translations/sk.json | 10 ++ .../derivative/translations/sk.json | 12 +- .../device_tracker/translations/sk.json | 10 ++ .../devolo_home_control/translations/sk.json | 3 +- .../devolo_home_network/translations/sk.json | 1 + .../components/dexcom/translations/sk.json | 7 +- .../diagnostics/translations/sk.json | 3 + .../dialogflow/translations/sk.json | 9 ++ .../components/discord/translations/sk.json | 3 +- .../components/dlna_dmr/translations/sk.json | 6 +- .../components/doorbird/translations/sk.json | 16 ++- .../components/dsmr/translations/sk.json | 32 ++++- .../dsmr_reader/translations/sk.json | 7 + .../components/dunehd/translations/sk.json | 1 + .../components/eafm/translations/sk.json | 7 + .../components/ecobee/translations/sk.json | 6 + .../components/econet/translations/sk.json | 1 + .../eight_sleep/translations/sk.json | 3 +- .../components/elgato/translations/sk.json | 1 + .../components/elkm1/translations/sk.json | 18 ++- .../components/elmax/translations/sk.json | 3 +- .../emulated_roku/translations/sk.json | 3 +- .../components/energy/translations/sk.json | 3 + .../components/enocean/translations/sk.json | 4 + .../enphase_envoy/translations/sk.json | 3 +- .../environment_canada/translations/sk.json | 5 +- .../components/escea/translations/sk.json | 3 +- .../components/esphome/translations/sk.json | 1 + .../evil_genius_labs/translations/sk.json | 1 + .../components/ezviz/translations/sk.json | 10 +- .../components/fan/translations/sk.json | 8 +- .../components/fibaro/translations/sk.json | 6 +- .../components/filesize/translations/sk.json | 10 +- .../fjaraskupan/translations/sk.json | 3 +- .../flick_electric/translations/sk.json | 6 +- .../components/flipr/translations/sk.json | 10 +- .../components/flo/translations/sk.json | 3 +- .../components/flume/translations/sk.json | 9 +- .../components/flux_led/translations/sk.json | 12 ++ .../forecast_solar/translations/fr.json | 3 + .../forecast_solar/translations/no.json | 3 + .../forked_daapd/translations/sk.json | 1 + .../components/foscam/translations/sk.json | 1 + .../freedompro/translations/sk.json | 3 +- .../components/fritz/translations/sk.json | 27 +++- .../components/fritzbox/translations/sk.json | 10 +- .../fritzbox_callmonitor/translations/sk.json | 8 +- .../components/generic/translations/sk.json | 22 ++- .../geocaching/translations/sk.json | 17 ++- .../components/geofency/translations/sk.json | 9 ++ .../geonetnz_quakes/translations/sk.json | 9 ++ .../geonetnz_volcano/translations/sk.json | 8 ++ .../components/gios/translations/sk.json | 7 +- .../components/glances/translations/sk.json | 6 +- .../components/gogogate2/translations/sk.json | 3 +- .../components/goodwe/translations/sk.json | 3 +- .../components/google/translations/sk.json | 6 +- .../google_sheets/translations/sk.json | 1 + .../components/gpslogger/translations/sk.json | 9 ++ .../components/gree/translations/sk.json | 3 +- .../components/group/translations/sk.json | 33 ++++- .../growatt_server/translations/sk.json | 9 +- .../harmony/translations/select.fr.json | 2 +- .../harmony/translations/select.sk.json | 7 + .../components/harmony/translations/sk.json | 9 ++ .../components/hassio/translations/sk.json | 23 ++++ .../components/heos/translations/sk.json | 6 +- .../here_travel_time/translations/sk.json | 24 +++- .../hisense_aehw4a1/translations/sk.json | 8 ++ .../components/hive/translations/sk.json | 31 ++++- .../components/hlk_sw16/translations/sk.json | 3 +- .../home_connect/translations/sk.json | 6 + .../home_plus_control/translations/sk.json | 11 +- .../homeassistant/translations/bg.json | 4 + .../homeassistant/translations/ca.json | 4 + .../homeassistant/translations/de.json | 4 + .../homeassistant/translations/es.json | 4 + .../homeassistant/translations/fr.json | 3 + .../homeassistant/translations/no.json | 4 + .../homeassistant/translations/ru.json | 4 + .../homeassistant/translations/sk.json | 8 ++ .../homeassistant/translations/zh-Hant.json | 4 + .../homeassistant_yellow/translations/fr.json | 3 + .../components/homekit/translations/sk.json | 25 ++++ .../translations/select.sk.json | 9 ++ .../translations/sensor.sk.json | 1 + .../homekit_controller/translations/sk.json | 17 ++- .../homewizard/translations/sk.json | 6 +- .../components/honeywell/translations/sk.json | 3 +- .../huawei_lte/translations/sk.json | 22 ++- .../components/hue/translations/sk.json | 29 +++- .../huisbaasje/translations/sk.json | 4 +- .../humidifier/translations/sk.json | 11 +- .../hvv_departures/translations/sk.json | 9 +- .../components/iaqualink/translations/sk.json | 9 +- .../components/ibeacon/translations/sk.json | 7 + .../components/icloud/translations/sk.json | 13 +- .../components/ifttt/translations/sk.json | 14 ++ .../components/insteon/translations/sk.json | 20 ++- .../integration/translations/sk.json | 10 ++ .../intellifire/translations/sk.json | 3 +- .../components/ios/translations/sk.json | 3 + .../components/iotawatt/translations/sk.json | 3 +- .../components/ipp/translations/sk.json | 12 +- .../components/iqvia/translations/sk.json | 3 +- .../islamic_prayer_times/translations/sk.json | 23 ++++ .../components/iss/translations/sk.json | 8 ++ .../components/isy994/translations/sk.json | 20 ++- .../components/izone/translations/sk.json | 3 +- .../components/jellyfin/translations/sk.json | 6 +- .../components/juicenet/translations/sk.json | 3 +- .../keymitt_ble/translations/sk.json | 9 +- .../components/kmtronic/translations/sk.json | 3 +- .../components/knx/translations/bg.json | 10 ++ .../components/knx/translations/ca.json | 46 ++++++- .../components/knx/translations/de.json | 46 ++++++- .../components/knx/translations/es.json | 46 ++++++- .../components/knx/translations/et.json | 46 ++++++- .../components/knx/translations/fr.json | 6 + .../components/knx/translations/id.json | 46 ++++++- .../components/knx/translations/no.json | 46 ++++++- .../components/knx/translations/ru.json | 46 ++++++- .../components/knx/translations/sk.json | 129 +++++++++++++----- .../components/knx/translations/zh-Hant.json | 46 ++++++- .../components/kodi/translations/sk.json | 12 +- .../components/konnected/translations/sk.json | 17 +++ .../components/kulersky/translations/sk.json | 13 ++ .../lacrosse_view/translations/sk.json | 6 +- .../components/lametric/translations/sk.json | 11 ++ .../landisgyr_heat_meter/translations/sk.json | 5 + .../launch_library/translations/sk.json | 7 + .../components/laundrify/translations/sk.json | 16 ++- .../components/lcn/translations/sk.json | 9 ++ .../components/led_ble/translations/sk.json | 4 + .../lg_soundbar/translations/sk.json | 3 + .../components/life360/translations/sk.json | 28 +++- .../components/lifx/translations/sk.json | 6 +- .../components/light/translations/sk.json | 4 +- .../components/litejet/translations/sk.json | 16 ++- .../litterrobot/translations/sensor.sk.json | 1 + .../litterrobot/translations/sk.json | 13 +- .../components/locative/translations/sk.json | 5 + .../logi_circle/translations/sk.json | 3 +- .../components/luftdaten/translations/sk.json | 1 + .../lutron_caseta/translations/sk.json | 10 +- .../components/lyric/translations/sk.json | 9 ++ .../components/mailgun/translations/sk.json | 15 ++ .../components/mazda/translations/sk.json | 3 +- .../components/meater/translations/sk.json | 6 +- .../media_player/translations/de.json | 4 +- .../media_player/translations/sk.json | 11 +- .../components/melcloud/translations/sk.json | 7 +- .../components/melnor/translations/sk.json | 13 ++ .../meteo_france/translations/sk.json | 22 +++ .../meteoclimatic/translations/sk.json | 7 + .../components/metoffice/translations/sk.json | 3 +- .../components/miflora/translations/sk.json | 7 + .../components/mikrotik/translations/sk.json | 13 +- .../components/mill/translations/sk.json | 8 +- .../components/min_max/translations/no.json | 10 +- .../components/min_max/translations/sk.json | 13 +- .../minecraft_server/translations/sk.json | 9 +- .../components/mjpeg/translations/sk.json | 10 +- .../moehlenhoff_alpha2/translations/sk.json | 3 +- .../components/monoprice/translations/sk.json | 26 +++- .../components/moon/translations/sk.json | 12 ++ .../motion_blinds/translations/sk.json | 8 +- .../components/motioneye/translations/sk.json | 8 +- .../components/mqtt/translations/fr.json | 10 +- .../components/mqtt/translations/no.json | 4 +- .../components/mqtt/translations/sk.json | 16 ++- .../components/mullvad/translations/sk.json | 9 ++ .../components/mutesync/translations/sk.json | 5 + .../components/myq/translations/sk.json | 13 +- .../components/mysensors/translations/sk.json | 32 ++++- .../components/nam/translations/sk.json | 10 +- .../components/nanoleaf/translations/sk.json | 8 +- .../components/neato/translations/sk.json | 6 + .../components/nest/translations/sk.json | 21 ++- .../components/netatmo/translations/sk.json | 25 +++- .../components/netgear/translations/sk.json | 6 +- .../components/nexia/translations/sk.json | 7 +- .../nfandroidtv/translations/sk.json | 7 +- .../nibe_heatpump/translations/sk.json | 17 ++- .../nightscout/translations/sk.json | 7 +- .../components/nina/translations/sk.json | 49 +++++++ .../nmap_tracker/translations/sk.json | 3 + .../components/nobo_hub/translations/sk.json | 7 +- .../components/notion/translations/sk.json | 12 +- .../components/nuheat/translations/sk.json | 11 +- .../components/nuki/translations/sk.json | 7 +- .../components/number/translations/sk.json | 3 + .../components/nws/translations/sk.json | 6 +- .../components/nzbget/translations/sk.json | 14 +- .../components/octoprint/translations/sk.json | 21 ++- .../components/omnilogic/translations/sk.json | 19 ++- .../onboarding/translations/sk.json | 7 + .../components/oncue/translations/sk.json | 4 +- .../ondilo_ico/translations/sk.json | 8 +- .../components/onewire/translations/sk.json | 3 +- .../components/onvif/translations/sk.json | 8 ++ .../open_meteo/translations/sk.json | 9 ++ .../openexchangerates/translations/sk.json | 8 +- .../opengarage/translations/sk.json | 4 +- .../opentherm_gw/translations/sk.json | 13 +- .../components/openuv/translations/sk.json | 6 +- .../openweathermap/translations/sk.json | 16 ++- .../overkiz/translations/sensor.sk.json | 1 + .../components/overkiz/translations/sk.json | 10 +- .../ovo_energy/translations/sk.json | 5 +- .../components/owntracks/translations/sk.json | 13 ++ .../p1_monitor/translations/sk.json | 6 +- .../panasonic_viera/translations/sk.json | 12 +- .../components/peco/translations/sk.json | 7 + .../components/pi_hole/translations/sk.json | 8 +- .../components/picnic/translations/sk.json | 8 +- .../components/plaato/translations/sk.json | 20 ++- .../components/plex/translations/sk.json | 21 ++- .../components/plugwise/translations/no.json | 6 +- .../components/plugwise/translations/sk.json | 11 +- .../plum_lightpad/translations/sk.json | 3 + .../components/point/translations/sk.json | 3 +- .../components/powerwall/translations/sk.json | 2 + .../components/profiler/translations/sk.json | 3 + .../progettihwsw/translations/sk.json | 24 ++++ .../components/prosegur/translations/sk.json | 11 +- .../prusalink/translations/sensor.sk.json | 1 + .../components/prusalink/translations/sk.json | 5 +- .../components/ps4/translations/sk.json | 5 +- .../pure_energie/translations/sk.json | 6 +- .../components/pushover/translations/sk.json | 11 +- .../components/pvoutput/translations/sk.json | 1 + .../components/qnap_qsw/translations/sk.json | 7 +- .../components/rachio/translations/sk.json | 1 + .../components/radarr/translations/no.json | 4 + .../components/radarr/translations/sk.json | 7 +- .../radio_browser/translations/sk.json | 7 + .../radiotherm/translations/sk.json | 4 + .../rainforest_eagle/translations/sk.json | 8 +- .../rainmachine/translations/sk.json | 3 +- .../components/rdw/translations/sk.json | 7 + .../components/recorder/translations/sk.json | 3 +- .../components/renault/translations/sk.json | 3 +- .../components/rfxtrx/translations/sk.json | 24 +++- .../components/rhasspy/translations/sk.json | 7 + .../components/ring/translations/sk.json | 11 +- .../components/risco/translations/sk.json | 15 +- .../translations/sk.json | 4 +- .../components/roku/translations/sk.json | 9 +- .../components/roomba/translations/sk.json | 7 +- .../components/roon/translations/sk.json | 6 +- .../components/rpi_power/translations/sk.json | 3 +- .../ruckus_unleashed/translations/sk.json | 4 +- .../components/sabnzbd/translations/sk.json | 6 +- .../components/samsungtv/translations/sk.json | 1 + .../components/schedule/translations/sk.json | 8 ++ .../components/scrape/translations/sk.json | 18 ++- .../screenlogic/translations/sk.json | 8 ++ .../components/select/translations/sk.json | 3 +- .../components/sense/translations/sk.json | 7 +- .../components/senseme/translations/sk.json | 3 +- .../components/sensibo/translations/sk.json | 7 +- .../components/sentry/translations/sk.json | 6 +- .../components/senz/translations/sk.json | 14 +- .../components/sharkiq/translations/sk.json | 8 +- .../components/shelly/translations/sk.json | 6 + .../components/sia/translations/sk.json | 4 +- .../simplepush/translations/sk.json | 10 ++ .../simplisafe/translations/sk.json | 10 +- .../components/skybell/translations/sk.json | 10 +- .../components/slack/translations/sk.json | 1 + .../components/sleepiq/translations/sk.json | 7 +- .../components/sma/translations/sk.json | 7 +- .../components/smappee/translations/sk.json | 8 +- .../smart_meter_texas/translations/sk.json | 7 +- .../components/smarttub/translations/sk.json | 6 +- .../components/sms/translations/sk.json | 1 + .../components/solaredge/translations/sk.json | 3 +- .../components/solarlog/translations/sk.json | 3 +- .../components/solax/translations/sk.json | 3 + .../components/soma/translations/sk.json | 3 +- .../components/somfy/translations/sk.json | 10 +- .../somfy_mylink/translations/sk.json | 12 +- .../components/sonarr/translations/sk.json | 10 +- .../components/songpal/translations/sk.json | 3 + .../components/sonos/translations/sk.json | 3 +- .../soundtouch/translations/sk.json | 3 + .../speedtestdotnet/translations/sk.json | 3 + .../components/spider/translations/sk.json | 9 +- .../components/spotify/translations/sk.json | 3 + .../components/sql/translations/sk.json | 4 + .../squeezebox/translations/sk.json | 10 +- .../srp_energy/translations/sk.json | 10 +- .../components/starline/translations/sk.json | 12 +- .../steam_online/translations/sk.json | 7 +- .../components/steamist/translations/sk.json | 4 + .../components/subaru/translations/sk.json | 11 +- .../components/sun/translations/sk.json | 3 + .../surepetcare/translations/sk.json | 7 +- .../switch_as_x/translations/sk.json | 3 +- .../switch_as_x/translations/sv.json | 2 +- .../components/switchbot/translations/sk.json | 1 + .../components/syncthing/translations/sk.json | 5 +- .../components/syncthru/translations/sk.json | 1 + .../synology_dsm/translations/sk.json | 25 +++- .../components/tado/translations/sk.json | 4 +- .../components/tailscale/translations/sk.json | 1 + .../tankerkoenig/translations/sk.json | 9 +- .../components/tasmota/translations/sk.json | 7 + .../components/tautulli/translations/sk.json | 10 +- .../tesla_wall_connector/translations/sk.json | 4 + .../components/text/translations/fr.json | 3 + .../components/text/translations/no.json | 3 + .../components/tibber/translations/sk.json | 4 +- .../components/timer/translations/de.json | 2 +- .../components/tod/translations/sk.json | 11 ++ .../components/tolo/translations/sk.json | 3 + .../tomorrowio/translations/sk.json | 5 +- .../components/toon/translations/sk.json | 7 +- .../totalconnect/translations/sk.json | 15 +- .../components/tplink/translations/sk.json | 3 + .../components/traccar/translations/sk.json | 9 ++ .../components/tractive/translations/sk.json | 3 +- .../components/tradfri/translations/sk.json | 10 +- .../trafikverket_ferry/translations/sk.json | 2 + .../trafikverket_train/translations/sk.json | 1 + .../translations/sk.json | 8 +- .../transmission/translations/sk.json | 10 +- .../tuya/translations/select.sk.json | 40 +++++- .../twentemilieu/translations/sk.json | 1 + .../components/twilio/translations/sk.json | 5 + .../components/twinkly/translations/sk.json | 3 + .../ukraine_alarm/translations/sk.json | 22 ++- .../components/unifi/translations/sk.json | 3 +- .../unifiprotect/translations/de.json | 4 + .../unifiprotect/translations/es.json | 4 + .../unifiprotect/translations/sk.json | 4 +- .../components/upb/translations/sk.json | 7 +- .../components/upcloud/translations/sk.json | 13 +- .../components/uptime/translations/sk.json | 3 + .../uptimerobot/translations/sensor.sk.json | 7 + .../uptimerobot/translations/sk.json | 10 +- .../utility_meter/translations/sk.json | 11 ++ .../components/vacuum/translations/de.json | 2 +- .../components/vallox/translations/sk.json | 8 +- .../components/velbus/translations/sk.json | 10 +- .../components/venstar/translations/sk.json | 11 +- .../components/verisure/translations/sk.json | 15 ++ .../components/version/translations/sk.json | 14 ++ .../components/vesync/translations/sk.json | 3 + .../components/vicare/translations/sk.json | 5 + .../components/vilfo/translations/sk.json | 4 +- .../components/vizio/translations/sk.json | 6 +- .../vlc_telnet/translations/sk.json | 5 + .../components/volumio/translations/sk.json | 6 +- .../volvooncall/translations/sk.json | 10 +- .../components/vulcan/translations/sk.json | 6 + .../components/wallbox/translations/sk.json | 8 +- .../water_heater/translations/sk.json | 7 + .../components/watttime/translations/sk.json | 21 ++- .../waze_travel_time/translations/sk.json | 9 ++ .../components/wemo/translations/sk.json | 3 +- .../components/whirlpool/translations/sk.json | 3 +- .../components/wilight/translations/sk.json | 10 +- .../components/withings/translations/sk.json | 21 ++- .../components/wled/translations/sk.json | 3 +- .../wolflink/translations/sensor.sk.json | 4 + .../components/wolflink/translations/sk.json | 3 +- .../components/ws66i/translations/sk.json | 15 ++ .../components/xbox/translations/sk.json | 9 +- .../xiaomi_aqara/translations/sk.json | 3 + .../xiaomi_ble/translations/sk.json | 3 +- .../xiaomi_miio/translations/select.sk.json | 6 + .../xiaomi_miio/translations/sk.json | 24 +++- .../yale_smart_alarm/translations/sk.json | 8 +- .../translations/select.sk.json | 4 + .../components/yeelight/translations/sk.json | 9 ++ .../components/yolink/translations/sk.json | 16 ++- .../components/zamg/translations/sk.json | 9 +- .../components/zerproc/translations/sk.json | 3 +- .../components/zha/translations/sk.json | 38 ++++++ .../components/zone/translations/sk.json | 4 +- .../zoneminder/translations/sk.json | 7 +- .../components/zwave_js/translations/sk.json | 12 +- 442 files changed, 3632 insertions(+), 508 deletions(-) create mode 100644 homeassistant/components/demo/translations/sk.json create mode 100644 homeassistant/components/diagnostics/translations/sk.json create mode 100644 homeassistant/components/dialogflow/translations/sk.json create mode 100644 homeassistant/components/dsmr_reader/translations/sk.json create mode 100644 homeassistant/components/energy/translations/sk.json create mode 100644 homeassistant/components/geofency/translations/sk.json create mode 100644 homeassistant/components/gpslogger/translations/sk.json create mode 100644 homeassistant/components/harmony/translations/select.sk.json create mode 100644 homeassistant/components/hisense_aehw4a1/translations/sk.json create mode 100644 homeassistant/components/homekit_controller/translations/select.sk.json create mode 100644 homeassistant/components/ibeacon/translations/sk.json create mode 100644 homeassistant/components/ifttt/translations/sk.json create mode 100644 homeassistant/components/islamic_prayer_times/translations/sk.json create mode 100644 homeassistant/components/iss/translations/sk.json create mode 100644 homeassistant/components/kulersky/translations/sk.json create mode 100644 homeassistant/components/launch_library/translations/sk.json create mode 100644 homeassistant/components/lcn/translations/sk.json create mode 100644 homeassistant/components/mailgun/translations/sk.json create mode 100644 homeassistant/components/melnor/translations/sk.json create mode 100644 homeassistant/components/miflora/translations/sk.json create mode 100644 homeassistant/components/moon/translations/sk.json create mode 100644 homeassistant/components/nina/translations/sk.json create mode 100644 homeassistant/components/number/translations/sk.json create mode 100644 homeassistant/components/onboarding/translations/sk.json create mode 100644 homeassistant/components/open_meteo/translations/sk.json create mode 100644 homeassistant/components/owntracks/translations/sk.json create mode 100644 homeassistant/components/radio_browser/translations/sk.json create mode 100644 homeassistant/components/rdw/translations/sk.json create mode 100644 homeassistant/components/rhasspy/translations/sk.json create mode 100644 homeassistant/components/schedule/translations/sk.json create mode 100644 homeassistant/components/tasmota/translations/sk.json create mode 100644 homeassistant/components/text/translations/fr.json create mode 100644 homeassistant/components/text/translations/no.json create mode 100644 homeassistant/components/tod/translations/sk.json create mode 100644 homeassistant/components/traccar/translations/sk.json create mode 100644 homeassistant/components/uptimerobot/translations/sensor.sk.json create mode 100644 homeassistant/components/water_heater/translations/sk.json diff --git a/homeassistant/components/abode/translations/sk.json b/homeassistant/components/abode/translations/sk.json index 5dd46ab3755..1d4e1148fb0 100644 --- a/homeassistant/components/abode/translations/sk.json +++ b/homeassistant/components/abode/translations/sk.json @@ -20,7 +20,8 @@ "data": { "password": "Heslo", "username": "Email" - } + }, + "title": "Vypl\u0148te svoje prihlasovacie \u00fadaje do slu\u017eby Abode" }, "user": { "data": { diff --git a/homeassistant/components/accuweather/translations/fr.json b/homeassistant/components/accuweather/translations/fr.json index 75ef209a1cf..187b1fb3e0b 100644 --- a/homeassistant/components/accuweather/translations/fr.json +++ b/homeassistant/components/accuweather/translations/fr.json @@ -24,6 +24,11 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "Pr\u00e9visions m\u00e9t\u00e9orologiques" + } + }, "user": { "data": { "forecast": "Pr\u00e9visions m\u00e9t\u00e9orologiques" diff --git a/homeassistant/components/accuweather/translations/no.json b/homeassistant/components/accuweather/translations/no.json index dc203abe714..c6af3b8320c 100644 --- a/homeassistant/components/accuweather/translations/no.json +++ b/homeassistant/components/accuweather/translations/no.json @@ -24,6 +24,12 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "V\u00e6rmelding" + }, + "description": "P\u00e5 grunn av begrensningene til gratisversjonen av AccuWeather API-n\u00f8kkelen, n\u00e5r du aktiverer v\u00e6rmelding, vil dataoppdateringer utf\u00f8res hvert 80. minutt i stedet for hvert 40. minutt." + }, "user": { "data": { "forecast": "V\u00e6rmelding" diff --git a/homeassistant/components/accuweather/translations/sk.json b/homeassistant/components/accuweather/translations/sk.json index 6891a048069..d39416fdab7 100644 --- a/homeassistant/components/accuweather/translations/sk.json +++ b/homeassistant/components/accuweather/translations/sk.json @@ -21,6 +21,11 @@ }, "options": { "step": { + "init": { + "data": { + "forecast": "Predpove\u010f po\u010dasia" + } + }, "user": { "data": { "forecast": "Predpove\u010f po\u010dasia" diff --git a/homeassistant/components/adguard/translations/sk.json b/homeassistant/components/adguard/translations/sk.json index 0464e2f2f72..10e7c5b3860 100644 --- a/homeassistant/components/adguard/translations/sk.json +++ b/homeassistant/components/adguard/translations/sk.json @@ -9,6 +9,7 @@ }, "step": { "hassio_confirm": { + "description": "Chcete nakonfigurova\u0165 Home Assistant na pripojenie k AdGuard Home poskytovan\u00e9mu doplnkom: {addon}?", "title": "AdGuard Home cez doplnok Home Assistant" }, "user": { diff --git a/homeassistant/components/airly/translations/sk.json b/homeassistant/components/airly/translations/sk.json index 1ff57355d56..0c8474a0725 100644 --- a/homeassistant/components/airly/translations/sk.json +++ b/homeassistant/components/airly/translations/sk.json @@ -21,7 +21,9 @@ }, "system_health": { "info": { - "requests_per_day": "Povolen\u00e9 po\u017eiadavky za de\u0148" + "can_reach_server": "Dosta\u0148te sa na server Airly", + "requests_per_day": "Povolen\u00e9 po\u017eiadavky za de\u0148", + "requests_remaining": "Zost\u00e1vaj\u00face povolen\u00e9 \u017eiadosti" } } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/sk.json b/homeassistant/components/airnow/translations/sk.json index 2cb10dea95c..630e30719bf 100644 --- a/homeassistant/components/airnow/translations/sk.json +++ b/homeassistant/components/airnow/translations/sk.json @@ -14,7 +14,8 @@ "data": { "api_key": "API k\u013e\u00fa\u010d", "latitude": "Zemepisn\u00e1 \u0161\u00edrka", - "longitude": "Zemepisn\u00e1 d\u013a\u017eka" + "longitude": "Zemepisn\u00e1 d\u013a\u017eka", + "radius": "Polomer stanice (m\u00edle; volite\u013en\u00e9)" }, "description": "Ak chcete vygenerova\u0165 k\u013e\u00fa\u010d API, prejdite na https://docs.airnowapi.org/account/request/" } diff --git a/homeassistant/components/airtouch4/translations/sk.json b/homeassistant/components/airtouch4/translations/sk.json index c8e33ffce5e..aaab8c373dc 100644 --- a/homeassistant/components/airtouch4/translations/sk.json +++ b/homeassistant/components/airtouch4/translations/sk.json @@ -4,13 +4,15 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "no_units": "Nepodarilo sa n\u00e1js\u0165 \u017eiadne skupiny AirTouch 4." }, "step": { "user": { "data": { "host": "Hostite\u013e" - } + }, + "title": "Nastavte podrobnosti pripojenia AirTouch 4." } } } diff --git a/homeassistant/components/airvisual/translations/sk.json b/homeassistant/components/airvisual/translations/sk.json index 74551f1c24e..23301cf1742 100644 --- a/homeassistant/components/airvisual/translations/sk.json +++ b/homeassistant/components/airvisual/translations/sk.json @@ -16,7 +16,8 @@ "api_key": "API k\u013e\u00fa\u010d", "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka" - } + }, + "title": "Konfigur\u00e1cia geografie" }, "geography_by_name": { "data": { @@ -24,7 +25,8 @@ "city": "Mesto", "country": "Krajina", "state": "stav" - } + }, + "title": "Konfigur\u00e1cia geografie" }, "node_pro": { "data": { @@ -35,7 +37,21 @@ "reauth_confirm": { "data": { "api_key": "API k\u013e\u00fa\u010d" - } + }, + "title": "Op\u00e4tovn\u00e9 overenie AirVisual" + }, + "user": { + "title": "Nakonfigurujte AirVisual" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "show_on_map": "Zobrazte na mape sledovan\u00fa geografiu" + }, + "title": "Nakonfigurujte AirVisual" } } } diff --git a/homeassistant/components/airzone/translations/fr.json b/homeassistant/components/airzone/translations/fr.json index 2d09bb7f1e4..f525617b2ef 100644 --- a/homeassistant/components/airzone/translations/fr.json +++ b/homeassistant/components/airzone/translations/fr.json @@ -10,13 +10,15 @@ "step": { "discovered_connection": { "data": { - "host": "Serveur", + "host": "H\u00f4te", + "id": "ID syst\u00e8me", "port": "Port" } }, "user": { "data": { "host": "H\u00f4te", + "id": "ID syst\u00e8me", "port": "Port" }, "description": "Configurer l'int\u00e9gration Airzone." diff --git a/homeassistant/components/airzone/translations/sk.json b/homeassistant/components/airzone/translations/sk.json index b256742f48f..2566a9e353e 100644 --- a/homeassistant/components/airzone/translations/sk.json +++ b/homeassistant/components/airzone/translations/sk.json @@ -11,7 +11,8 @@ "discovered_connection": { "data": { "host": "Hostite\u013e", - "id": "ID syst\u00e9mu" + "id": "ID syst\u00e9mu", + "port": "Port" } }, "user": { diff --git a/homeassistant/components/alarm_control_panel/translations/sk.json b/homeassistant/components/alarm_control_panel/translations/sk.json index f60448e3d31..844fd5c2ec1 100644 --- a/homeassistant/components/alarm_control_panel/translations/sk.json +++ b/homeassistant/components/alarm_control_panel/translations/sk.json @@ -2,6 +2,9 @@ "device_automation": { "action_type": { "trigger": "Sp\u00fa\u0161\u0165a\u010d {entity_name}" + }, + "trigger_type": { + "triggered": "{entity_name} spusten\u00fd" } }, "state": { diff --git a/homeassistant/components/alarmdecoder/translations/sk.json b/homeassistant/components/alarmdecoder/translations/sk.json index 46e52c8bcdb..07bceeca884 100644 --- a/homeassistant/components/alarmdecoder/translations/sk.json +++ b/homeassistant/components/alarmdecoder/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "create_entry": { + "default": "\u00daspe\u0161ne pripojen\u00e9 k AlarmDecoder." + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165" }, @@ -29,6 +32,11 @@ "loop_range": "RF Loop mus\u00ed by\u0165 cel\u00e9 \u010d\u00edslo od 1 do 4." }, "step": { + "arm_settings": { + "data": { + "alt_night_mode": "Alternat\u00edvny no\u010dn\u00fd re\u017eim" + } + }, "init": { "data": { "edit_select": "Upravi\u0165" @@ -40,13 +48,17 @@ "zone_loop": "RF slu\u010dka", "zone_name": "N\u00e1zov z\u00f3ny", "zone_relayaddr": "Adresa rel\u00e9", - "zone_rfid": "RF Serial" - } + "zone_relaychan": "Rel\u00e9ov\u00fd kan\u00e1l", + "zone_rfid": "RF Serial", + "zone_type": "Typ z\u00f3ny" + }, + "description": "Zadajte podrobnosti pre z\u00f3nu {zone_number}. Ak chcete odstr\u00e1ni\u0165 z\u00f3nu {zone_number}, ponechajte n\u00e1zov z\u00f3ny pr\u00e1zdny." }, "zone_select": { "data": { "zone_number": "\u010c\u00edslo z\u00f3ny" - } + }, + "description": "Zadajte \u010d\u00edslo z\u00f3ny, ktor\u00fa chcete prida\u0165, upravi\u0165 alebo odstr\u00e1ni\u0165." } } } diff --git a/homeassistant/components/almond/translations/sk.json b/homeassistant/components/almond/translations/sk.json index a2709de3aac..db82c552cb2 100644 --- a/homeassistant/components/almond/translations/sk.json +++ b/homeassistant/components/almond/translations/sk.json @@ -7,6 +7,9 @@ "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "step": { + "hassio_confirm": { + "description": "Chcete nakonfigurova\u0165 dom\u00e1ceho asistenta na pripojenie k Almond poskytovan\u00e9mu doplnkom: {addon}?" + }, "pick_implementation": { "title": "Vyberte met\u00f3du overenia" } diff --git a/homeassistant/components/androidtv/translations/sk.json b/homeassistant/components/androidtv/translations/sk.json index ad94a3ed3bf..509a1b4d0e6 100644 --- a/homeassistant/components/androidtv/translations/sk.json +++ b/homeassistant/components/androidtv/translations/sk.json @@ -1,17 +1,22 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "invalid_unique_id": "Nie je mo\u017en\u00e9 ur\u010di\u0165 platn\u00e9 jedine\u010dn\u00e9 ID zariadenia" }, "error": { + "adbkey_not_file": "S\u00fabor k\u013e\u00fa\u010da ADB sa nena\u0161iel", "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", + "key_and_server": "Poskytnite iba k\u013e\u00fa\u010d ADB alebo server ADB", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { "adb_server_ip": "IP adresa servera ADB (nechajte pr\u00e1zdnu, ak sa nepou\u017e\u00edva)", + "adb_server_port": "Port servera ADB", + "adbkey": "Cesta k s\u00faboru k\u013e\u00fa\u010da ADB (pre automatick\u00e9 vygenerovanie nechajte pr\u00e1zdne)", "device_class": "Typ zariadenia", "host": "Hostite\u013e", "port": "Port" @@ -37,6 +42,7 @@ }, "rules": { "data": { + "rule_id": "ID aplik\u00e1cie", "rule_values": "Zoznam pravidiel zis\u0165ovania stavu (pozri dokument\u00e1ciu)" } } diff --git a/homeassistant/components/anthemav/translations/sk.json b/homeassistant/components/anthemav/translations/sk.json index 8301c6c87af..efae6c31512 100644 --- a/homeassistant/components/anthemav/translations/sk.json +++ b/homeassistant/components/anthemav/translations/sk.json @@ -10,7 +10,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "port": "Port" } } } diff --git a/homeassistant/components/apcupsd/translations/sk.json b/homeassistant/components/apcupsd/translations/sk.json index bc0f2277bf9..c441c50a3a9 100644 --- a/homeassistant/components/apcupsd/translations/sk.json +++ b/homeassistant/components/apcupsd/translations/sk.json @@ -10,7 +10,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "port": "Port" } } } diff --git a/homeassistant/components/apple_tv/translations/sk.json b/homeassistant/components/apple_tv/translations/sk.json index 883dba9bd6d..18961885448 100644 --- a/homeassistant/components/apple_tv/translations/sk.json +++ b/homeassistant/components/apple_tv/translations/sk.json @@ -31,6 +31,7 @@ "title": "P\u00e1rovanie" }, "password": { + "description": "`{protocol}` vy\u017eaduje heslo. Toto zatia\u013e nie je podporovan\u00e9. Ak chcete pokra\u010dova\u0165, deaktivujte heslo.", "title": "Vy\u017eaduje sa heslo" }, "protocol_disabled": { diff --git a/homeassistant/components/aranet/translations/sk.json b/homeassistant/components/aranet/translations/sk.json index a0fed806308..83276dd71fd 100644 --- a/homeassistant/components/aranet/translations/sk.json +++ b/homeassistant/components/aranet/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "outdated_version": "Toto zariadenie pou\u017e\u00edva zastaran\u00fd firmv\u00e9r. Aktualizujte ho aspo\u0148 na verziu 1.2.0 a sk\u00faste to znova." }, "error": { "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" diff --git a/homeassistant/components/asuswrt/translations/sk.json b/homeassistant/components/asuswrt/translations/sk.json index 2adc19c0c1a..5e58d4bc638 100644 --- a/homeassistant/components/asuswrt/translations/sk.json +++ b/homeassistant/components/asuswrt/translations/sk.json @@ -10,11 +10,14 @@ "user": { "data": { "host": "Hostite\u013e", + "mode": "Re\u017eim", "name": "N\u00e1zov", "password": "Heslo", "port": "Port", - "protocol": "Komunika\u010dn\u00fd protokol, ktor\u00fd sa m\u00e1 pou\u017ei\u0165" - } + "protocol": "Komunika\u010dn\u00fd protokol, ktor\u00fd sa m\u00e1 pou\u017ei\u0165", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "description": "Nastavte po\u017eadovan\u00fd parameter na pripojenie k smerova\u010du" } } }, diff --git a/homeassistant/components/aurora_abb_powerone/translations/sk.json b/homeassistant/components/aurora_abb_powerone/translations/sk.json index 0eabe52686c..1163236043d 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/sk.json +++ b/homeassistant/components/aurora_abb_powerone/translations/sk.json @@ -4,10 +4,16 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "no_serial_ports": "Nena\u0161li sa \u017eiadne komunika\u010dn\u00e9 porty. Na komunik\u00e1ciu potrebujete platn\u00e9 zariadenie RS485." }, + "error": { + "cannot_connect": "Nie je mo\u017en\u00e9 sa pripoji\u0165, skontrolujte s\u00e9riov\u00fd port, adresu, elektrick\u00e9 pripojenie a \u010di je meni\u010d zapnut\u00fd (na dennom svetle)", + "cannot_open_serial_port": "Ned\u00e1 sa otvori\u0165 s\u00e9riov\u00fd port, skontrolujte ho a sk\u00faste to znova", + "invalid_serial_port": "S\u00e9riov\u00fd port nie je platn\u00fdm zariaden\u00edm alebo ho nebolo mo\u017en\u00e9 otvori\u0165" + }, "step": { "user": { "data": { - "address": "Adresa meni\u010da" + "address": "Adresa meni\u010da", + "port": "Port adapt\u00e9ra RS485 alebo USB-RS485" } } } diff --git a/homeassistant/components/aussie_broadband/translations/sk.json b/homeassistant/components/aussie_broadband/translations/sk.json index 2aa7bdc0b18..8db55654be4 100644 --- a/homeassistant/components/aussie_broadband/translations/sk.json +++ b/homeassistant/components/aussie_broadband/translations/sk.json @@ -15,7 +15,8 @@ "data": { "password": "Heslo" }, - "description": "Aktualizova\u0165 heslo pre {username}" + "description": "Aktualizova\u0165 heslo pre {username}", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "service": { "data": { @@ -25,7 +26,8 @@ }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/auth/translations/sk.json b/homeassistant/components/auth/translations/sk.json index 9634beff2cc..5d510baa4be 100644 --- a/homeassistant/components/auth/translations/sk.json +++ b/homeassistant/components/auth/translations/sk.json @@ -9,13 +9,18 @@ }, "step": { "init": { - "description": "Vyberte si jednu z notifika\u010dn\u00fdch slu\u017eieb:" + "description": "Vyberte si jednu z notifika\u010dn\u00fdch slu\u017eieb:", + "title": "Nastavenie jednorazov\u00e9ho hesla doru\u010den\u00e9ho prostredn\u00edctvom komponentu notifik\u00e1cie" }, "setup": { - "description": "Jednorazov\u00e9 heslo bolo odoslan\u00e9 prostredn\u00edctvom **notify.{notify_service}**. Zadajte ho, pros\u00edm, ni\u017e\u0161ie:" + "description": "Jednorazov\u00e9 heslo bolo odoslan\u00e9 prostredn\u00edctvom **notify.{notify_service}**. Zadajte ho, pros\u00edm, ni\u017e\u0161ie:", + "title": "Overenie nastavenia" } }, "title": "Ozn\u00e1menie jednorazov\u00e9ho hesla" + }, + "totp": { + "title": "TOTP" } } } \ No newline at end of file diff --git a/homeassistant/components/awair/translations/sk.json b/homeassistant/components/awair/translations/sk.json index b208312589f..b8b771b2463 100644 --- a/homeassistant/components/awair/translations/sk.json +++ b/homeassistant/components/awair/translations/sk.json @@ -34,6 +34,12 @@ "access_token": "Pr\u00edstupov\u00fd token", "email": "Email" } + }, + "user": { + "menu_options": { + "cloud": "Pripojenie cez cloud", + "local": "Lok\u00e1lne pripojenie (preferovan\u00e9)" + } } } } diff --git a/homeassistant/components/axis/translations/sk.json b/homeassistant/components/axis/translations/sk.json index 4529c2fbe90..b4415304f35 100644 --- a/homeassistant/components/axis/translations/sk.json +++ b/homeassistant/components/axis/translations/sk.json @@ -16,7 +16,8 @@ "data": { "host": "Hostite\u013e", "password": "Heslo", - "port": "Port" + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/azure_event_hub/translations/sk.json b/homeassistant/components/azure_event_hub/translations/sk.json index 2f499266544..cf133735e3b 100644 --- a/homeassistant/components/azure_event_hub/translations/sk.json +++ b/homeassistant/components/azure_event_hub/translations/sk.json @@ -12,6 +12,11 @@ "conn_string": { "description": "Zadajte re\u0165azec pripojenia pre: {event_hub_instance_name}", "title": "Met\u00f3da re\u0165azca pripojenia" + }, + "user": { + "data": { + "use_connection_string": "Pou\u017eitie re\u0165azca pripojenia" + } } } } diff --git a/homeassistant/components/balboa/translations/sk.json b/homeassistant/components/balboa/translations/sk.json index bd2e41a926f..be996d9dacf 100644 --- a/homeassistant/components/balboa/translations/sk.json +++ b/homeassistant/components/balboa/translations/sk.json @@ -11,7 +11,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "title": "Pripojte sa k zariadeniu Balboa Wi-Fi" } } } diff --git a/homeassistant/components/binary_sensor/translations/sk.json b/homeassistant/components/binary_sensor/translations/sk.json index 92d0262da88..c6d0666cffb 100644 --- a/homeassistant/components/binary_sensor/translations/sk.json +++ b/homeassistant/components/binary_sensor/translations/sk.json @@ -3,6 +3,12 @@ "condition_type": { "is_bat_low": "{entity_name} bat\u00e9ria je vybit\u00e1", "is_co": "{entity_name} zis\u0165uje oxid uho\u013enat\u00fd", + "is_cold": "{entity_name} je studen\u00e9", + "is_connected": "{entity_name} je pripojen\u00fd", + "is_gas": "{entity_name} detekuje plyn", + "is_hot": "{entity_name} je hor\u00face", + "is_light": "{entity_name} zaznamen\u00e1va svetlo", + "is_locked": "{entity_name} je uzamknut\u00e9", "is_motion": "{entity_name} zaznamen\u00e1va pohyb", "is_moving": "{entity_name} sa h\u00fdbe", "is_no_co": "{entity_name} nezaznamen\u00e1va oxid uho\u013enat\u00fd", @@ -20,23 +26,68 @@ "is_not_hot": "{entity_name} nie je hor\u00face", "is_not_locked": "{entity_name} je odomknut\u00e9", "is_not_moist": "{entity_name} je such\u00e9", - "is_not_moving": "{entity_name} sa neh\u00fdbe" + "is_not_moving": "{entity_name} sa neh\u00fdbe", + "is_not_occupied": "{entity_name} nie je obsaden\u00e1", + "is_not_open": "{entity_name} je zatvoren\u00e9", + "is_not_plugged_in": "{entity_name} je odpojen\u00e9", + "is_not_powered": "{entity_name} nie je nap\u00e1jan\u00e9", + "is_not_present": "{entity_name} nie je pr\u00edtomn\u00e9", + "is_not_running": "{entity_name} nie je spusten\u00e9", + "is_not_tampered": "{entity_name} nedetekuje manipul\u00e1ciu", + "is_occupied": "{entity_name} je obsaden\u00e9", + "is_off": "{entity_name} je vypnut\u00e9", + "is_on": "{entity_name} je zapnut\u00e9", + "is_open": "{entity_name} je otvoren\u00e1", + "is_plugged_in": "{entity_name} je zapojen\u00e9", + "is_powered": "{entity_name} je nap\u00e1jan\u00e9", + "is_present": "{entity_name} je pr\u00edtomn\u00e9", + "is_problem": "{entity_name} detekuje probl\u00e9m", + "is_running": "{entity_name} je spusten\u00e9", + "is_smoke": "{entity_name} zis\u0165uje dym", + "is_sound": "{entity_name} rozpozn\u00e1va zvuk", + "is_tampered": "{entity_name} detekuje manipul\u00e1ciu", + "is_update": "{entity_name} m\u00e1 k dispoz\u00edcii aktualiz\u00e1ciu", + "is_vibration": "{entity_name} zaznamen\u00e1va vibr\u00e1cie" }, "trigger_type": { "bat_low": "{entity_name} bat\u00e9ria vybit\u00e1", + "co": "{entity_name} zis\u0165uje oxid uho\u013enat\u00fd", + "cold": "{entity_name} sa ochladilo", "connected": "{entity_name} je pripojen\u00fd", + "gas": "{entity_name} detekuje plyn", + "hot": "{n\u00e1zov_objektu} sa stal hor\u00facim", + "light": "{entity_name} za\u010dal detekova\u0165 svetlo", + "moist": "{entity_name} sa stal vlhk\u00fdm", + "motion": "{entity_name} za\u010dal zis\u0165ova\u0165 pohyb", "not_bat_low": "{entity_name} bat\u00e9ria norm\u00e1lna", + "not_connected": "{entity_name} odpojen\u00fd", + "not_hot": "{entity_name} prestal by\u0165 hor\u00facim", + "not_locked": "{entity_name} odomknut\u00e9", + "not_moist": "{n\u00e1zov_objektu} sa stal such\u00fdm", + "not_opened": "{entity_name} zatvoren\u00e9", "not_plugged_in": "{entity_name} odpojen\u00fd", - "not_powered": "{entity_name} nie je nap\u00e1jan\u00e9" + "not_powered": "{entity_name} nie je nap\u00e1jan\u00e9", + "not_present": "{entity_name} nie je pr\u00edtomn\u00e9", + "not_running": "{entity_name} u\u017e nie je v prev\u00e1dzke", + "opened": "{entity_name} otvoren\u00e9", + "plugged_in": "{entity_name} zapojen\u00e9", + "powered": "{entity_name} nap\u00e1jan\u00e9", + "problem": "{entity_name} za\u010dal zis\u0165ova\u0165 probl\u00e9m", + "smoke": "{entity_name} za\u010dala zis\u0165ova\u0165 dym", + "sound": "{entity_name} za\u010dala rozpozn\u00e1va\u0165 zvuk", + "update": "{entity_name} m\u00e1 k dispoz\u00edcii aktualiz\u00e1ciu" } }, "device_class": { + "co": "oxid uho\u013enat\u00fd", "cold": "chlad", "gas": "plyn", "heat": "teplo", "moisture": "vlhkos\u0165", "motion": "pohyb", + "occupancy": "obsadenos\u0165", "power": "nap\u00e1janie", + "problem": "probl\u00e9m", "smoke": "dym", "sound": "zvuk", "vibration": "vibr\u00e1cia" @@ -106,6 +157,10 @@ "off": "Zatvoren\u00e9", "on": "Otvoren\u00e9" }, + "plug": { + "off": "Odpojen\u00fd", + "on": "Zapojen\u00fd" + }, "presence": { "off": "Pre\u010d", "on": "Doma" diff --git a/homeassistant/components/blink/translations/sk.json b/homeassistant/components/blink/translations/sk.json index 0eb24922bb2..6057b8ea9d4 100644 --- a/homeassistant/components/blink/translations/sk.json +++ b/homeassistant/components/blink/translations/sk.json @@ -11,12 +11,18 @@ }, "step": { "2fa": { - "description": "Zadajte PIN zaslan\u00fd na v\u00e1\u0161 e-mail" + "data": { + "2fa": "Dvojfaktorov\u00fd k\u00f3d" + }, + "description": "Zadajte PIN zaslan\u00fd na v\u00e1\u0161 e-mail", + "title": "Dvojfaktorov\u00e1 autentifik\u00e1cia" }, "user": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Prihl\u00e1ste sa pomocou \u00fa\u010dtu Blink" } } }, diff --git a/homeassistant/components/bmw_connected_drive/translations/sk.json b/homeassistant/components/bmw_connected_drive/translations/sk.json index 793ea2bdcf1..bf40df63e99 100644 --- a/homeassistant/components/bmw_connected_drive/translations/sk.json +++ b/homeassistant/components/bmw_connected_drive/translations/sk.json @@ -10,7 +10,8 @@ "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/bond/translations/sk.json b/homeassistant/components/bond/translations/sk.json index 1a56678f20d..1c17bead986 100644 --- a/homeassistant/components/bond/translations/sk.json +++ b/homeassistant/components/bond/translations/sk.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", + "old_firmware": "Nepodporovan\u00fd star\u00fd firmv\u00e9r na zariaden\u00ed Bond \u2013 pred pokra\u010dovan\u00edm aktualizujte", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({host})", diff --git a/homeassistant/components/bosch_shc/translations/sk.json b/homeassistant/components/bosch_shc/translations/sk.json index ff58ed1c18e..aa59cecc5fb 100644 --- a/homeassistant/components/bosch_shc/translations/sk.json +++ b/homeassistant/components/bosch_shc/translations/sk.json @@ -5,14 +5,30 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "Bosch SHC: {name}", "step": { + "confirm_discovery": { + "description": "Stla\u010dte tla\u010didlo na prednej strane ovl\u00e1da\u010da Bosch Smart Home Controller, k\u00fdm LED neza\u010dne blika\u0165.\n Ste pripraven\u00ed pokra\u010dova\u0165 v nastavovan\u00ed {model} @ {host} pomocou Home Assistant?" + }, + "credentials": { + "data": { + "password": "Heslo ovl\u00e1da\u010da Smart Home Controller" + } + }, + "reauth_confirm": { + "description": "Integr\u00e1cia bosch_shc mus\u00ed znova overi\u0165 v\u00e1\u0161 \u00fa\u010det", + "title": "Znova overi\u0165 integr\u00e1ciu" + }, "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Nastavte svoj ovl\u00e1da\u010d Bosch Smart Home Controller tak, aby umo\u017e\u0148oval monitorovanie a ovl\u00e1danie pomocou Home Assistant.", + "title": "Parametre autentifik\u00e1cie SHC" } } } diff --git a/homeassistant/components/braviatv/translations/sk.json b/homeassistant/components/braviatv/translations/sk.json index 5add13a86b3..6447484aac8 100644 --- a/homeassistant/components/braviatv/translations/sk.json +++ b/homeassistant/components/braviatv/translations/sk.json @@ -1,24 +1,31 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_ip_control": "Ovl\u00e1danie IP je na va\u0161om telev\u00edzore vypnut\u00e9 alebo telev\u00edzor nie je podporovan\u00fd.", + "not_bravia_device": "Zariadenie nie je telev\u00edzor Bravia.", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", - "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", + "unsupported_model": "V\u00e1\u0161 model TV nie je podporovan\u00fd." }, "step": { "authorize": { "data": { "pin": "PIN k\u00f3d" - } + }, + "title": "Autorizujte telev\u00edzor Sony Bravia" }, "confirm": { "description": "Chcete za\u010da\u0165 nastavova\u0165?" }, "reauth_confirm": { "data": { - "pin": "PIN k\u00f3d" + "pin": "PIN k\u00f3d", + "use_psk": "Pou\u017eite autentifik\u00e1ciu PSK" } }, "user": { @@ -27,5 +34,14 @@ } } } + }, + "options": { + "step": { + "user": { + "data": { + "ignored_sources": "Zoznam ignorovan\u00fdch zdrojov" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/broadlink/translations/sk.json b/homeassistant/components/broadlink/translations/sk.json index 8b133bf55f9..88c402f5a9a 100644 --- a/homeassistant/components/broadlink/translations/sk.json +++ b/homeassistant/components/broadlink/translations/sk.json @@ -24,6 +24,9 @@ "title": "Odomknite zariadenie" }, "unlock": { + "data": { + "unlock": "\u00c1no, urobte to." + }, "title": "Odomknite zariadenie (volite\u013en\u00e9)" }, "user": { diff --git a/homeassistant/components/brother/translations/sk.json b/homeassistant/components/brother/translations/sk.json index 58e173e521d..f666e5524d6 100644 --- a/homeassistant/components/brother/translations/sk.json +++ b/homeassistant/components/brother/translations/sk.json @@ -1,18 +1,28 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "unsupported_model": "Tento model tla\u010diarne nie je podporovan\u00fd." }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", + "snmp_error": "Server SNMP je vypnut\u00fd alebo tla\u010diare\u0148 nie je podporovan\u00e1.", "wrong_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa." }, "flow_title": "{model} {serial_number}", "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "type": "Typ tla\u010diarne" } + }, + "zeroconf_confirm": { + "data": { + "type": "Typ tla\u010diarne" + }, + "description": "Chcete prida\u0165 tla\u010diare\u0148 {model} so s\u00e9riov\u00fdm \u010d\u00edslom `{serial_number}` do Home Assistant?", + "title": "Zisten\u00e1 tla\u010diare\u0148 Brother" } } } diff --git a/homeassistant/components/brunt/translations/sk.json b/homeassistant/components/brunt/translations/sk.json index 506de390bc0..a654a071ab9 100644 --- a/homeassistant/components/brunt/translations/sk.json +++ b/homeassistant/components/brunt/translations/sk.json @@ -13,7 +13,9 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Znova zadajte heslo pre: {username}", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { diff --git a/homeassistant/components/bsblan/translations/sk.json b/homeassistant/components/bsblan/translations/sk.json index eb382b0d15e..40a6e13da9a 100644 --- a/homeassistant/components/bsblan/translations/sk.json +++ b/homeassistant/components/bsblan/translations/sk.json @@ -13,7 +13,8 @@ "data": { "host": "Hostite\u013e", "password": "Heslo", - "port": "Port" + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/bthome/translations/sk.json b/homeassistant/components/bthome/translations/sk.json index 0d4e7c2f929..3767640fbf2 100644 --- a/homeassistant/components/bthome/translations/sk.json +++ b/homeassistant/components/bthome/translations/sk.json @@ -3,7 +3,8 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "expected_32_characters": "O\u010dak\u00e1van\u00fd 32-znakov\u00fd hexadecim\u00e1lny bindkey." diff --git a/homeassistant/components/button/translations/sk.json b/homeassistant/components/button/translations/sk.json index 5dfc88234c6..96f4352529b 100644 --- a/homeassistant/components/button/translations/sk.json +++ b/homeassistant/components/button/translations/sk.json @@ -2,6 +2,10 @@ "device_automation": { "action_type": { "press": "Stla\u010dte tla\u010didlo {entity_name}" + }, + "trigger_type": { + "pressed": "{entity_name} bol stla\u010den\u00fd" } - } + }, + "title": "Tla\u010didlo" } \ No newline at end of file diff --git a/homeassistant/components/camera/translations/de.json b/homeassistant/components/camera/translations/de.json index d6f409f1a0e..d504f5f6217 100644 --- a/homeassistant/components/camera/translations/de.json +++ b/homeassistant/components/camera/translations/de.json @@ -1,7 +1,7 @@ { "state": { "_": { - "idle": "Unt\u00e4tig", + "idle": "inaktiv", "recording": "Aufnehmen", "streaming": "\u00dcbertr\u00e4gt" } diff --git a/homeassistant/components/canary/translations/sk.json b/homeassistant/components/canary/translations/sk.json index afab4d9b25c..4facc537244 100644 --- a/homeassistant/components/canary/translations/sk.json +++ b/homeassistant/components/canary/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { @@ -10,7 +11,8 @@ "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/cast/translations/sk.json b/homeassistant/components/cast/translations/sk.json index 69e7a4ea84b..9fe44a03383 100644 --- a/homeassistant/components/cast/translations/sk.json +++ b/homeassistant/components/cast/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { "invalid_known_hosts": "Zn\u00e1mi hostitelia musia by\u0165 v zozname hostite\u013eov oddelen\u00fd \u010diarkami." }, diff --git a/homeassistant/components/cert_expiry/translations/sk.json b/homeassistant/components/cert_expiry/translations/sk.json index db1aa31eaa5..7147710b357 100644 --- a/homeassistant/components/cert_expiry/translations/sk.json +++ b/homeassistant/components/cert_expiry/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "import_failed": "Import z konfigur\u00e1cie zlyhal" }, "error": { "connection_refused": "Pri prip\u00e1jan\u00ed k hostite\u013eovi bolo pripojenie odmietnut\u00e9", @@ -12,9 +13,12 @@ "user": { "data": { "host": "Hostite\u013e", + "name": "N\u00e1zov certifik\u00e1tu", "port": "Port" - } + }, + "title": "Definujte certifik\u00e1t na testovanie" } } - } + }, + "title": "Platnos\u0165 certifik\u00e1tu" } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.sk.json b/homeassistant/components/climacell/translations/sensor.sk.json index dfe997964cc..f94574da180 100644 --- a/homeassistant/components/climacell/translations/sensor.sk.json +++ b/homeassistant/components/climacell/translations/sensor.sk.json @@ -11,6 +11,7 @@ }, "climacell__precipitation_type": { "freezing_rain": "Mrzn\u00faci d\u00e1\u017e\u010f", + "ice_pellets": "\u013dadovec", "rain": "D\u00e1\u017e\u010f", "snow": "Sneh" } diff --git a/homeassistant/components/cloud/translations/de.json b/homeassistant/components/cloud/translations/de.json index 20ac7ff0fab..ed34cf86fc3 100644 --- a/homeassistant/components/cloud/translations/de.json +++ b/homeassistant/components/cloud/translations/de.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "Der Vorgang hat zu lange gedauert. Bitte versuche es sp\u00e4ter noch einmal." + }, + "step": { + "confirm_change_plan": { + "description": "Wir haben k\u00fcrzlich unser Abonnementsystem aktualisiert. Um die Home Assistant Cloud weiterhin nutzen zu k\u00f6nnen, musst du die \u00c4nderung einmalig in PayPal genehmigen. \n\nDies dauert 1 Minute und erh\u00f6ht den Preis nicht." + } + } + }, + "title": "Legacy-Abonnement erkannt" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa aktiviert", diff --git a/homeassistant/components/cloud/translations/es.json b/homeassistant/components/cloud/translations/es.json index 7eddc5c4109..6789dc4b967 100644 --- a/homeassistant/components/cloud/translations/es.json +++ b/homeassistant/components/cloud/translations/es.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "La operaci\u00f3n ha tardado demasiado. Por favor, int\u00e9ntalo de nuevo m\u00e1s tarde." + }, + "step": { + "confirm_change_plan": { + "description": "Recientemente hemos actualizado nuestro sistema de suscripci\u00f3n. Para continuar usando Home Assistant Cloud, debes aprobar el cambio una sola vez en PayPal. \n\nEsto toma 1 minuto y no aumentar\u00e1 el precio." + } + } + }, + "title": "Suscripci\u00f3n heredada detectada" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa habilitada", diff --git a/homeassistant/components/cloud/translations/sk.json b/homeassistant/components/cloud/translations/sk.json index 8083824df50..f96b0cacfc1 100644 --- a/homeassistant/components/cloud/translations/sk.json +++ b/homeassistant/components/cloud/translations/sk.json @@ -1,7 +1,11 @@ { "system_health": { "info": { - "google_enabled": "Google povolen\u00e9" + "alexa_enabled": "Alexa povolen\u00e1", + "can_reach_cert_server": "Dosiahnutie servera certifik\u00e1tov", + "google_enabled": "Google povolen\u00e9", + "remote_connected": "Vzdialene pripojen\u00e9", + "remote_enabled": "Povolen\u00e9 vzdialen\u00e9 ovl\u00e1danie" } } } \ No newline at end of file diff --git a/homeassistant/components/cloudflare/translations/sk.json b/homeassistant/components/cloudflare/translations/sk.json index 4af875cd1ab..96d8f8061e1 100644 --- a/homeassistant/components/cloudflare/translations/sk.json +++ b/homeassistant/components/cloudflare/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { diff --git a/homeassistant/components/control4/translations/sk.json b/homeassistant/components/control4/translations/sk.json index b011fc12c90..29f13888b39 100644 --- a/homeassistant/components/control4/translations/sk.json +++ b/homeassistant/components/control4/translations/sk.json @@ -12,7 +12,8 @@ "user": { "data": { "host": "IP adresa", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, "description": "Zadajte \u00fadaje o svojom \u00fa\u010dte Control4 a IP adresu miestneho kontrol\u00e9ra." } diff --git a/homeassistant/components/coolmaster/translations/sk.json b/homeassistant/components/coolmaster/translations/sk.json index bf16a99d5e1..4bd1fb9cf1d 100644 --- a/homeassistant/components/coolmaster/translations/sk.json +++ b/homeassistant/components/coolmaster/translations/sk.json @@ -6,8 +6,14 @@ "step": { "user": { "data": { + "cool": "Podpora chladiaceho re\u017eimu", + "dry": "Podpora su\u0161iaceho re\u017eimu", + "fan_only": "Podpora re\u017eimu iba ventil\u00e1tora", + "heat": "Podpora re\u017eimu vykurovania", + "heat_cool": "Podpora automatick\u00e9ho re\u017eimu vykurovania/chladenia", "host": "Hostite\u013e" - } + }, + "title": "Nastavte podrobnosti pripojenia CoolMasterNet." } } } diff --git a/homeassistant/components/coronavirus/translations/sk.json b/homeassistant/components/coronavirus/translations/sk.json index fe468d25520..c9e214a81d2 100644 --- a/homeassistant/components/coronavirus/translations/sk.json +++ b/homeassistant/components/coronavirus/translations/sk.json @@ -3,6 +3,13 @@ "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "step": { + "user": { + "data": { + "country": "Krajina" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/cpuspeed/translations/sk.json b/homeassistant/components/cpuspeed/translations/sk.json index 59f10788754..8b80c1ddb35 100644 --- a/homeassistant/components/cpuspeed/translations/sk.json +++ b/homeassistant/components/cpuspeed/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + "already_configured": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "not_compatible": "Nie je mo\u017en\u00e9 z\u00edska\u0165 inform\u00e1cie o CPU, t\u00e1to integr\u00e1cia nie je kompatibiln\u00e1 s va\u0161\u00edm syst\u00e9mom" }, "step": { "user": { diff --git a/homeassistant/components/crownstone/translations/sk.json b/homeassistant/components/crownstone/translations/sk.json index 60e6ce58a97..3c0ec2452c2 100644 --- a/homeassistant/components/crownstone/translations/sk.json +++ b/homeassistant/components/crownstone/translations/sk.json @@ -9,6 +9,16 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "usb_config": { + "data": { + "usb_path": "Cesta k zariadeniu USB" + } + }, + "usb_manual_config": { + "data": { + "usb_manual_path": "Cesta k zariadeniu USB" + } + }, "user": { "data": { "email": "Email", @@ -16,5 +26,19 @@ } } } + }, + "options": { + "step": { + "usb_config": { + "data": { + "usb_path": "Cesta k zariadeniu USB" + } + }, + "usb_manual_config": { + "data": { + "usb_manual_path": "Cesta k zariadeniu USB" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/daikin/translations/sk.json b/homeassistant/components/daikin/translations/sk.json index 191d0ea32f4..b0c325dc02a 100644 --- a/homeassistant/components/daikin/translations/sk.json +++ b/homeassistant/components/daikin/translations/sk.json @@ -16,7 +16,8 @@ "api_key": "API k\u013e\u00fa\u010d", "host": "Hostite\u013e", "password": "Heslo" - } + }, + "title": "Konfigur\u00e1cia Daikin AC" } } } diff --git a/homeassistant/components/deconz/translations/sk.json b/homeassistant/components/deconz/translations/sk.json index 26fcc552124..ed0cc142a44 100644 --- a/homeassistant/components/deconz/translations/sk.json +++ b/homeassistant/components/deconz/translations/sk.json @@ -5,21 +5,42 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "error": { + "linking_not_possible": "Nepodarilo sa prepoji\u0165 s br\u00e1nou", "no_key": "Nepodarilo sa z\u00edska\u0165 k\u013e\u00fa\u010d API" }, "flow_title": "{host}", "step": { + "hassio_confirm": { + "description": "Chcete nakonfigurova\u0165 Home Assistant na pripojenie k br\u00e1ne deCONZ poskytovanej doplnkom {addon}?" + }, + "link": { + "title": "Prepojenie s deCONZ" + }, "manual_input": { "data": { "host": "Hostite\u013e", "port": "Port" } + }, + "user": { + "data": { + "host": "Vyberte objaven\u00fa br\u00e1nu deCONZ" + } } } }, "device_automation": { "trigger_subtype": { + "both_buttons": "Obe tla\u010didl\u00e1", + "bottom_buttons": "Spodn\u00e9 tla\u010didl\u00e1", + "button_1": "Prv\u00e9 tla\u010didlo", "button_2": "Druh\u00e9 tla\u010didlo", + "button_3": "Tretie tla\u010didlo", + "button_4": "\u0160tvrt\u00e9 tla\u010didlo", + "button_5": "Piate tla\u010didlo", + "button_6": "\u0160ieste tla\u010didlo", + "button_7": "Siedme tla\u010didlo", + "button_8": "\u00d4sme tla\u010didlo", "close": "Zavrie\u0165", "dim_down": "Stlmi\u0165", "dim_up": "Zv\u00fd\u0161i\u0165", @@ -37,7 +58,28 @@ }, "trigger_type": { "remote_awakened": "Zariadenie sa prebudilo", - "remote_button_double_press": "dvojklik na tla\u010didlo \u201e{subtype}\u201c" + "remote_button_double_press": "dvojklik na tla\u010didlo \u201e{subtype}\u201c", + "remote_button_long_press": "Trvalo stla\u010den\u00e9 tla\u010didlo \"{subtype}\"", + "remote_button_long_release": "Tla\u010didlo \"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed", + "remote_button_quadruple_press": "Tla\u010didlo \"{subtype}\" kliknut\u00e9 \u0161tyrikr\u00e1t", + "remote_button_quintuple_press": "Tla\u010didlo \"{subtype}\" kliknut\u00e9 p\u00e4\u0165kr\u00e1t", + "remote_button_short_press": "Stla\u010den\u00e9 tla\u010didlo \"{subtype}\"", + "remote_button_short_release": "Tla\u010didlo \"{subtype}\" bolo uvo\u013enen\u00e9", + "remote_button_triple_press": "Trojklik na tla\u010didlo \"{subtype}\"", + "remote_double_tap": "Zariadenie \"{subtype}\" dvojit\u00e9 klepnutie", + "remote_double_tap_any_side": "Zariadenie dvakr\u00e1t klepnut\u00e9 na \u013eubovo\u013en\u00fa stranu", + "remote_turned_clockwise": "Zariadenie oto\u010den\u00e9 v smere hodinov\u00fdch ru\u010di\u010diek", + "remote_turned_counter_clockwise": "Zariadenie oto\u010den\u00e9 proti smeru hodinov\u00fdch ru\u010di\u010diek" + } + }, + "options": { + "step": { + "deconz_devices": { + "data": { + "allow_clip_sensor": "Povoli\u0165 senzory deCONZ CLIP" + }, + "title": "mo\u017enosti deCONZ" + } } } } \ No newline at end of file diff --git a/homeassistant/components/demo/translations/fr.json b/homeassistant/components/demo/translations/fr.json index 4fa207692f3..754400b5bed 100644 --- a/homeassistant/components/demo/translations/fr.json +++ b/homeassistant/components/demo/translations/fr.json @@ -10,6 +10,14 @@ }, "title": "L'alimentation \u00e9lectrique n'est pas stable" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "Impossible de r\u00e9chauffer le th\u00e9 pour le moment" + } + }, + "title": "Le th\u00e9 est froid" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/demo/translations/no.json b/homeassistant/components/demo/translations/no.json index 396c3a366f6..680ddc11288 100644 --- a/homeassistant/components/demo/translations/no.json +++ b/homeassistant/components/demo/translations/no.json @@ -11,6 +11,14 @@ }, "title": "Str\u00f8mforsyningen er ikke stabil" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "Kan ikke varme opp teen p\u00e5 nytt n\u00e5" + } + }, + "title": "Teen er kald" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/demo/translations/sk.json b/homeassistant/components/demo/translations/sk.json new file mode 100644 index 00000000000..85f8ceb1ea0 --- /dev/null +++ b/homeassistant/components/demo/translations/sk.json @@ -0,0 +1,37 @@ +{ + "issues": { + "bad_psu": { + "fix_flow": { + "step": { + "confirm": { + "title": "Nap\u00e1jac\u00ed zdroj je potrebn\u00e9 vymeni\u0165" + } + } + }, + "title": "Nap\u00e1janie nie je stabiln\u00e9" + }, + "cold_tea": { + "title": "\u010caj je studen\u00fd" + }, + "unfixable_problem": { + "title": "Tento probl\u00e9m sa ned\u00e1 odstr\u00e1ni\u0165" + } + }, + "options": { + "step": { + "options_1": { + "data": { + "constant": "Kon\u0161tanta", + "int": "\u010c\u00edseln\u00fd vstup" + } + }, + "options_2": { + "data": { + "multi": "Viacn\u00e1sobn\u00fd v\u00fdber", + "select": "Vyberte mo\u017enos\u0165", + "string": "Hodnota re\u0165azca" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/denonavr/translations/sk.json b/homeassistant/components/denonavr/translations/sk.json index d1959ed36e3..0957cca2860 100644 --- a/homeassistant/components/denonavr/translations/sk.json +++ b/homeassistant/components/denonavr/translations/sk.json @@ -20,5 +20,15 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "show_all_sources": "Zobrazi\u0165 v\u0161etky zdroje" + }, + "description": "Zadajte volite\u013en\u00e9 nastavenia" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/sk.json b/homeassistant/components/derivative/translations/sk.json index 7fae67e64a0..bd461eb44e8 100644 --- a/homeassistant/components/derivative/translations/sk.json +++ b/homeassistant/components/derivative/translations/sk.json @@ -4,7 +4,10 @@ "user": { "data": { "name": "Meno", - "source": "Vstupn\u00fd sn\u00edma\u010d" + "round": "Presnos\u0165", + "source": "Vstupn\u00fd sn\u00edma\u010d", + "time_window": "\u010casov\u00e9 okno", + "unit_time": "\u010casov\u00e1 jednotka" } } } @@ -12,6 +15,13 @@ "options": { "step": { "init": { + "data": { + "name": "Meno", + "round": "Presnos\u0165", + "source": "Vstupn\u00fd sn\u00edma\u010d", + "time_window": "\u010casov\u00e9 okno", + "unit_time": "\u010casov\u00e1 jednotka" + }, "data_description": { "unit_prefix": "." } diff --git a/homeassistant/components/device_tracker/translations/sk.json b/homeassistant/components/device_tracker/translations/sk.json index 9d52c35e2cb..d77c8c7d915 100644 --- a/homeassistant/components/device_tracker/translations/sk.json +++ b/homeassistant/components/device_tracker/translations/sk.json @@ -1,4 +1,14 @@ { + "device_automation": { + "condition_type": { + "is_home": "{entity_name} je doma", + "is_not_home": "{entity_name} nie je doma" + }, + "trigger_type": { + "enters": "{entity_name} vst\u00fapi do z\u00f3ny", + "leaves": "{entity_name} opust\u00ed z\u00f3nu" + } + }, "state": { "_": { "home": "Doma", diff --git a/homeassistant/components/devolo_home_control/translations/sk.json b/homeassistant/components/devolo_home_control/translations/sk.json index bebb477ff50..94a5289e28f 100644 --- a/homeassistant/components/devolo_home_control/translations/sk.json +++ b/homeassistant/components/devolo_home_control/translations/sk.json @@ -5,7 +5,8 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "reauth_failed": "Pou\u017eite rovnak\u00e9ho pou\u017e\u00edvate\u013ea mydevolo ako predt\u00fdm." }, "step": { "user": { diff --git a/homeassistant/components/devolo_home_network/translations/sk.json b/homeassistant/components/devolo_home_network/translations/sk.json index 22afad80c00..45116ef4db3 100644 --- a/homeassistant/components/devolo_home_network/translations/sk.json +++ b/homeassistant/components/devolo_home_network/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "home_control": "Centr\u00e1lna jednotka devolo Home Control nefunguje s touto integr\u00e1ciou.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/dexcom/translations/sk.json b/homeassistant/components/dexcom/translations/sk.json index 17717e7c7fb..607228fd38e 100644 --- a/homeassistant/components/dexcom/translations/sk.json +++ b/homeassistant/components/dexcom/translations/sk.json @@ -11,8 +11,11 @@ "step": { "user": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "server": "Server", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Nastavte integr\u00e1ciu Dexcom" } } }, diff --git a/homeassistant/components/diagnostics/translations/sk.json b/homeassistant/components/diagnostics/translations/sk.json new file mode 100644 index 00000000000..eb0d3fdde9e --- /dev/null +++ b/homeassistant/components/diagnostics/translations/sk.json @@ -0,0 +1,3 @@ +{ + "title": "Diagnostiky" +} \ No newline at end of file diff --git a/homeassistant/components/dialogflow/translations/sk.json b/homeassistant/components/dialogflow/translations/sk.json new file mode 100644 index 00000000000..933f73976d2 --- /dev/null +++ b/homeassistant/components/dialogflow/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/sk.json b/homeassistant/components/discord/translations/sk.json index 71bc79a1ad6..f20eb067fd9 100644 --- a/homeassistant/components/discord/translations/sk.json +++ b/homeassistant/components/discord/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", diff --git a/homeassistant/components/dlna_dmr/translations/sk.json b/homeassistant/components/dlna_dmr/translations/sk.json index 38e6f0d765e..186ad1deb41 100644 --- a/homeassistant/components/dlna_dmr/translations/sk.json +++ b/homeassistant/components/dlna_dmr/translations/sk.json @@ -17,12 +17,14 @@ "manual": { "data": { "url": "URL" - } + }, + "description": "URL adresa k XML s\u00faboru popisu zariadenia" }, "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Vyberte zariadenie, ktor\u00e9 chcete nakonfigurova\u0165, alebo nechajte pr\u00e1zdne a zadajte adresu URL" } } }, diff --git a/homeassistant/components/doorbird/translations/sk.json b/homeassistant/components/doorbird/translations/sk.json index ce36e211841..b6e9348c1fc 100644 --- a/homeassistant/components/doorbird/translations/sk.json +++ b/homeassistant/components/doorbird/translations/sk.json @@ -5,7 +5,9 @@ "link_local_address": "Lok\u00e1lne adresy odkazov nie s\u00fa podporovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({host})", "step": { @@ -13,7 +15,17 @@ "data": { "host": "Hostite\u013e", "name": "N\u00e1zov zariadenia", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "events": "Zoznam udalost\u00ed oddelen\u00fdch \u010diarkou." } } } diff --git a/homeassistant/components/dsmr/translations/sk.json b/homeassistant/components/dsmr/translations/sk.json index 266b3c98961..d9e7b05f578 100644 --- a/homeassistant/components/dsmr/translations/sk.json +++ b/homeassistant/components/dsmr/translations/sk.json @@ -1,14 +1,19 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_communicate": "Komunik\u00e1cia zlyhala", + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "error": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_communicate": "Komunik\u00e1cia zlyhala", + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { "setup_network": { "data": { + "dsmr_version": "Vyberte verziu DSMR", "host": "Hostite\u013e", "port": "Port" }, @@ -16,9 +21,32 @@ }, "setup_serial": { "data": { + "dsmr_version": "Vyberte verziu DSMR", "port": "Vyberte zariadenie" }, "title": "Zariadenie" + }, + "setup_serial_manual_path": { + "data": { + "port": "Cesta k zariadeniu USB" + }, + "title": "Cesta" + }, + "user": { + "data": { + "type": "Typ pripojenia" + }, + "title": "Vyberte typ pripojenia" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "time_between_update": "Minim\u00e1lny \u010das medzi aktualiz\u00e1ciami entity [s]" + }, + "title": "Mo\u017enosti DSMR" } } } diff --git a/homeassistant/components/dsmr_reader/translations/sk.json b/homeassistant/components/dsmr_reader/translations/sk.json new file mode 100644 index 00000000000..c294bc45d7c --- /dev/null +++ b/homeassistant/components/dsmr_reader/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dunehd/translations/sk.json b/homeassistant/components/dunehd/translations/sk.json index 7d2ebc79c3c..55314bfd4fa 100644 --- a/homeassistant/components/dunehd/translations/sk.json +++ b/homeassistant/components/dunehd/translations/sk.json @@ -5,6 +5,7 @@ }, "error": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, "step": { diff --git a/homeassistant/components/eafm/translations/sk.json b/homeassistant/components/eafm/translations/sk.json index 793f8eff278..a3d22ff7b90 100644 --- a/homeassistant/components/eafm/translations/sk.json +++ b/homeassistant/components/eafm/translations/sk.json @@ -2,6 +2,13 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "step": { + "user": { + "data": { + "station": "Stanica" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/ecobee/translations/sk.json b/homeassistant/components/ecobee/translations/sk.json index b44cc667b4e..619f56c68f3 100644 --- a/homeassistant/components/ecobee/translations/sk.json +++ b/homeassistant/components/ecobee/translations/sk.json @@ -1,9 +1,15 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { "token_request_failed": "Chyba pri vy\u017eiadan\u00ed tokenov od ecobee; pros\u00edm sk\u00faste znova." }, "step": { + "authorize": { + "description": "Autorizujte t\u00fato aplik\u00e1ciu na https://www.ecobee.com/consumerportal/index.html pomocou k\u00f3du PIN: \n\n {pin}\n\n Potom stla\u010dte tla\u010didlo Odosla\u0165." + }, "user": { "data": { "api_key": "API k\u013e\u00fa\u010d" diff --git a/homeassistant/components/econet/translations/sk.json b/homeassistant/components/econet/translations/sk.json index 6aa492da657..aa68b67f678 100644 --- a/homeassistant/components/econet/translations/sk.json +++ b/homeassistant/components/econet/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "error": { diff --git a/homeassistant/components/eight_sleep/translations/sk.json b/homeassistant/components/eight_sleep/translations/sk.json index d1c130a108b..ef0fd942acb 100644 --- a/homeassistant/components/eight_sleep/translations/sk.json +++ b/homeassistant/components/eight_sleep/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/elgato/translations/sk.json b/homeassistant/components/elgato/translations/sk.json index 9276d3ef9be..d44c8833bc3 100644 --- a/homeassistant/components/elgato/translations/sk.json +++ b/homeassistant/components/elgato/translations/sk.json @@ -7,6 +7,7 @@ "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165" }, + "flow_title": "{serial_number}", "step": { "user": { "data": { diff --git a/homeassistant/components/elkm1/translations/sk.json b/homeassistant/components/elkm1/translations/sk.json index 597f97ac51f..3ec7110ece0 100644 --- a/homeassistant/components/elkm1/translations/sk.json +++ b/homeassistant/components/elkm1/translations/sk.json @@ -16,21 +16,29 @@ "discovered_connection": { "data": { "password": "Heslo", - "protocol": "Protokol" + "protocol": "Protokol", + "temperature_unit": "Jednotka teploty pou\u017e\u00edva ElkM1.", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, - "description": "Pripojte sa k objaven\u00e9mu syst\u00e9mu: {mac_address} ({host})" + "description": "Pripojte sa k objaven\u00e9mu syst\u00e9mu: {mac_address} ({host})", + "title": "Pripojte k Elk-M1 Control" }, "manual_connection": { "data": { "address": "IP adresa alebo dom\u00e9na alebo s\u00e9riov\u00fd port, ak sa prip\u00e1jate cez s\u00e9riov\u00fd port.", "password": "Heslo", - "protocol": "Protokol" - } + "prefix": "Jedine\u010dn\u00e1 predpona (ak m\u00e1te iba jeden ElkM1, nechajte pr\u00e1zdne).", + "protocol": "Protokol", + "temperature_unit": "Jednotka teploty pou\u017e\u00edva ElkM1.", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Pripojte k Elk-M1 Control" }, "user": { "data": { "device": "Zariadenie" - } + }, + "title": "Pripojte k Elk-M1 Control" } } } diff --git a/homeassistant/components/elmax/translations/sk.json b/homeassistant/components/elmax/translations/sk.json index 150d8897ff3..43c7507b1b9 100644 --- a/homeassistant/components/elmax/translations/sk.json +++ b/homeassistant/components/elmax/translations/sk.json @@ -18,7 +18,8 @@ }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/emulated_roku/translations/sk.json b/homeassistant/components/emulated_roku/translations/sk.json index 90ef443df16..96ca4e8f40a 100644 --- a/homeassistant/components/emulated_roku/translations/sk.json +++ b/homeassistant/components/emulated_roku/translations/sk.json @@ -8,7 +8,8 @@ "data": { "host_ip": "IP adresa hostite\u013ea", "name": "N\u00e1zov" - } + }, + "title": "Definujte konfigur\u00e1ciu servera" } } } diff --git a/homeassistant/components/energy/translations/sk.json b/homeassistant/components/energy/translations/sk.json new file mode 100644 index 00000000000..c8d85790fdd --- /dev/null +++ b/homeassistant/components/energy/translations/sk.json @@ -0,0 +1,3 @@ +{ + "title": "Energia" +} \ No newline at end of file diff --git a/homeassistant/components/enocean/translations/sk.json b/homeassistant/components/enocean/translations/sk.json index 96528a925df..a550ef6b29a 100644 --- a/homeassistant/components/enocean/translations/sk.json +++ b/homeassistant/components/enocean/translations/sk.json @@ -1,5 +1,9 @@ { "config": { + "abort": { + "invalid_dongle_path": "Nespr\u00e1vna cesta k hardv\u00e9rov\u00e9mu k\u013e\u00fa\u010du", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { "invalid_dongle_path": "Pre t\u00fato cestu nebol n\u00e1jden\u00fd \u017eiadny platn\u00fd k\u013e\u00fa\u010d" } diff --git a/homeassistant/components/enphase_envoy/translations/sk.json b/homeassistant/components/enphase_envoy/translations/sk.json index 9d7ff8d00ac..90056cd5864 100644 --- a/homeassistant/components/enphase_envoy/translations/sk.json +++ b/homeassistant/components/enphase_envoy/translations/sk.json @@ -14,7 +14,8 @@ "user": { "data": { "host": "Hostite\u013e", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/environment_canada/translations/sk.json b/homeassistant/components/environment_canada/translations/sk.json index b87d36cc6e0..7558d93d309 100644 --- a/homeassistant/components/environment_canada/translations/sk.json +++ b/homeassistant/components/environment_canada/translations/sk.json @@ -3,13 +3,16 @@ "error": { "bad_station_id": "ID stanice je neplatn\u00e9, ch\u00fdba alebo sa nenach\u00e1dza v datab\u00e1ze ID stanice", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "too_many_attempts": "Spojenie s Environment Canada m\u00e1 obmedzen\u00fa r\u00fdchlos\u0165; Sk\u00faste to znova o 60 sek\u00fand", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { + "language": "Jazyk inform\u00e1ci\u00ed o po\u010das\u00ed", "latitude": "Zemepisn\u00e1 \u0161\u00edrka", - "longitude": "Zemepisn\u00e1 d\u013a\u017eka" + "longitude": "Zemepisn\u00e1 d\u013a\u017eka", + "station": "ID meteorologickej stanice" } } } diff --git a/homeassistant/components/escea/translations/sk.json b/homeassistant/components/escea/translations/sk.json index 69d8d942494..99798036ffd 100644 --- a/homeassistant/components/escea/translations/sk.json +++ b/homeassistant/components/escea/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/sk.json b/homeassistant/components/esphome/translations/sk.json index 86bf0aa5397..f87d3e7e940 100644 --- a/homeassistant/components/esphome/translations/sk.json +++ b/homeassistant/components/esphome/translations/sk.json @@ -46,6 +46,7 @@ }, "issues": { "ble_firmware_outdated": { + "description": "Ak chcete zlep\u0161i\u0165 spo\u013eahlivos\u0165 a v\u00fdkon Bluetooth, d\u00f4razne odpor\u00fa\u010dame aktualizova\u0165 {name} na ESPHome 2022.11.0 alebo nov\u0161\u00ed.", "title": "Aktualizujte {name} pomocou ESPHome 2022.11.0 alebo nov\u0161ieho" } } diff --git a/homeassistant/components/evil_genius_labs/translations/sk.json b/homeassistant/components/evil_genius_labs/translations/sk.json index 4456ea9c0fc..7041756cda4 100644 --- a/homeassistant/components/evil_genius_labs/translations/sk.json +++ b/homeassistant/components/evil_genius_labs/translations/sk.json @@ -2,6 +2,7 @@ "config": { "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", + "timeout": "\u010casov\u00fd limit na nadviazanie spojenia", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { diff --git a/homeassistant/components/ezviz/translations/sk.json b/homeassistant/components/ezviz/translations/sk.json index 6fb1d443e0c..15dbad718a4 100644 --- a/homeassistant/components/ezviz/translations/sk.json +++ b/homeassistant/components/ezviz/translations/sk.json @@ -9,17 +9,21 @@ "invalid_auth": "Neplatn\u00e9 overenie", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" }, + "flow_title": "{serial}", "step": { "confirm": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "user": { "data": { "password": "Heslo", - "url": "URL" - } + "url": "URL", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Pripojte sa k EZVIZ Cloud" }, "user_custom_url": { "data": { diff --git a/homeassistant/components/fan/translations/sk.json b/homeassistant/components/fan/translations/sk.json index e5343697ccb..9251981601d 100644 --- a/homeassistant/components/fan/translations/sk.json +++ b/homeassistant/components/fan/translations/sk.json @@ -1,11 +1,17 @@ { "device_automation": { + "action_type": { + "turn_off": "Vypn\u00fa\u0165 {entity_name}", + "turn_on": "Zapn\u00fa\u0165 {entity_name}" + }, "condition_type": { "is_off": "{entity_name} je vypnut\u00e9", "is_on": "{entity_name} je zapnut\u00e9" }, "trigger_type": { - "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9" + "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9", + "turned_off": "{entity_name} vypnut\u00e1", + "turned_on": "{entity_name} zapnut\u00e1" } }, "state": { diff --git a/homeassistant/components/fibaro/translations/sk.json b/homeassistant/components/fibaro/translations/sk.json index 42d7661d8ea..33e7cce96e0 100644 --- a/homeassistant/components/fibaro/translations/sk.json +++ b/homeassistant/components/fibaro/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", @@ -13,7 +14,8 @@ "data": { "password": "Heslo" }, - "description": "Aktualizujte svoje heslo pre {username}" + "description": "Aktualizujte svoje heslo pre {username}", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { diff --git a/homeassistant/components/filesize/translations/sk.json b/homeassistant/components/filesize/translations/sk.json index f11a9743f59..6c0218d4a8e 100644 --- a/homeassistant/components/filesize/translations/sk.json +++ b/homeassistant/components/filesize/translations/sk.json @@ -6,6 +6,14 @@ "error": { "not_allowed": "Cesta nie je povolen\u00e1", "not_valid": "Cesta nie je platn\u00e1" + }, + "step": { + "user": { + "data": { + "file_path": "Cesta k s\u00faboru" + } + } } - } + }, + "title": "Ve\u013ekos\u0165 s\u00faboru" } \ No newline at end of file diff --git a/homeassistant/components/fjaraskupan/translations/sk.json b/homeassistant/components/fjaraskupan/translations/sk.json index 69d8d942494..99798036ffd 100644 --- a/homeassistant/components/fjaraskupan/translations/sk.json +++ b/homeassistant/components/fjaraskupan/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." } } } \ No newline at end of file diff --git a/homeassistant/components/flick_electric/translations/sk.json b/homeassistant/components/flick_electric/translations/sk.json index 161864eb4b8..e711bdb9556 100644 --- a/homeassistant/components/flick_electric/translations/sk.json +++ b/homeassistant/components/flick_electric/translations/sk.json @@ -12,8 +12,10 @@ "user": { "data": { "client_id": "ID klienta (volite\u013en\u00e9)", - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Prihlasovacie \u00fadaje Flick" } } } diff --git a/homeassistant/components/flipr/translations/sk.json b/homeassistant/components/flipr/translations/sk.json index ab95d8060d4..b9832f49cc6 100644 --- a/homeassistant/components/flipr/translations/sk.json +++ b/homeassistant/components/flipr/translations/sk.json @@ -9,11 +9,19 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "flipr_id": { + "data": { + "flipr_id": "ID Flipr" + }, + "description": "V zozname vyberte svoje Flipr ID" + }, "user": { "data": { "email": "Email", "password": "Heslo" - } + }, + "description": "Pripojte sa pomocou svojho \u00fa\u010dtu Flipr.", + "title": "Pripoji\u0165 sa k Flipru" } } } diff --git a/homeassistant/components/flo/translations/sk.json b/homeassistant/components/flo/translations/sk.json index 2ee96a72a30..666f6e28840 100644 --- a/homeassistant/components/flo/translations/sk.json +++ b/homeassistant/components/flo/translations/sk.json @@ -12,7 +12,8 @@ "user": { "data": { "host": "Hostite\u013e", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/flume/translations/sk.json b/homeassistant/components/flume/translations/sk.json index 13f019fb4da..44f0b9addf1 100644 --- a/homeassistant/components/flume/translations/sk.json +++ b/homeassistant/components/flume/translations/sk.json @@ -14,13 +14,16 @@ "data": { "password": "Heslo" }, - "description": "Heslo pre {username} u\u017e nie je platn\u00e9." + "description": "Heslo pre {username} u\u017e nie je platn\u00e9.", + "title": "Znova overte svoj \u00fa\u010det Flume" }, "user": { "data": { "client_id": "ID klienta", - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Pripoji\u0165 sa k svojmu \u00fa\u010dtu Flume" } } } diff --git a/homeassistant/components/flux_led/translations/sk.json b/homeassistant/components/flux_led/translations/sk.json index 8ff55f4dbbc..1fe408d6105 100644 --- a/homeassistant/components/flux_led/translations/sk.json +++ b/homeassistant/components/flux_led/translations/sk.json @@ -5,6 +5,9 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{model} {id} ({ipaddr})", "step": { "discovery_confirm": { @@ -16,5 +19,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "mode": "Zvolen\u00fd re\u017eim jasu." + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/forecast_solar/translations/fr.json b/homeassistant/components/forecast_solar/translations/fr.json index 78b4d3f8f88..d2591b6915b 100644 --- a/homeassistant/components/forecast_solar/translations/fr.json +++ b/homeassistant/components/forecast_solar/translations/fr.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Cl\u00e9 d'API non valide" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/no.json b/homeassistant/components/forecast_solar/translations/no.json index 6b3a58574a8..da9368c3646 100644 --- a/homeassistant/components/forecast_solar/translations/no.json +++ b/homeassistant/components/forecast_solar/translations/no.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Ugyldig API-n\u00f8kkel" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forked_daapd/translations/sk.json b/homeassistant/components/forked_daapd/translations/sk.json index ed77a87879e..950f5c33621 100644 --- a/homeassistant/components/forked_daapd/translations/sk.json +++ b/homeassistant/components/forked_daapd/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "unknown_error": "Neo\u010dak\u00e1van\u00e1 chyba", "wrong_host_or_port": "Ned\u00e1 sa pripoji\u0165. Skontrolujte hostite\u013ea a port.", "wrong_password": "Nespr\u00e1vne heslo." }, diff --git a/homeassistant/components/foscam/translations/sk.json b/homeassistant/components/foscam/translations/sk.json index 0a9521e9c76..701645cc48c 100644 --- a/homeassistant/components/foscam/translations/sk.json +++ b/homeassistant/components/foscam/translations/sk.json @@ -15,6 +15,7 @@ "host": "Hostite\u013e", "password": "Heslo", "port": "Port", + "rtsp_port": "RTSP port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } diff --git a/homeassistant/components/freedompro/translations/sk.json b/homeassistant/components/freedompro/translations/sk.json index 52746415ddf..6a9b7374d51 100644 --- a/homeassistant/components/freedompro/translations/sk.json +++ b/homeassistant/components/freedompro/translations/sk.json @@ -11,7 +11,8 @@ "user": { "data": { "api_key": "API k\u013e\u00fa\u010d" - } + }, + "description": "Zadajte k\u013e\u00fa\u010d API z\u00edskan\u00fd z https://home.freedompro.eu" } } } diff --git a/homeassistant/components/fritz/translations/sk.json b/homeassistant/components/fritz/translations/sk.json index 19cd874e55d..2dae4dc6f85 100644 --- a/homeassistant/components/fritz/translations/sk.json +++ b/homeassistant/components/fritz/translations/sk.json @@ -17,19 +17,36 @@ "step": { "confirm": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "description": "Objavil FRITZ!Box: {name} \n\n Nastavte n\u00e1stroje FRITZ!Box na ovl\u00e1danie v\u00e1\u0161ho {name}", + "title": "Nastavte n\u00e1stroje FRITZ!Box" }, "reauth_confirm": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "description": "Aktualizujte poverenia FRITZ!Box Tools pre: {host}. \n\n FRITZ!Box Tools sa nedok\u00e1\u017ee prihl\u00e1si\u0165 do v\u00e1\u0161ho FRITZ!Box.", + "title": "Aktualiz\u00e1cia FRITZ!Box Tools \u2013 poverenia" }, "user": { "data": { "host": "Hostite\u013e", "password": "Heslo", - "port": "Port" + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Nastavte n\u00e1stroje FRITZ!Box" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "old_discovery": "Povoli\u0165 star\u00fa met\u00f3du zis\u0165ovania" } } } diff --git a/homeassistant/components/fritzbox/translations/sk.json b/homeassistant/components/fritzbox/translations/sk.json index d1434894661..c7f02499196 100644 --- a/homeassistant/components/fritzbox/translations/sk.json +++ b/homeassistant/components/fritzbox/translations/sk.json @@ -5,6 +5,7 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "ignore_ip6_link_local": "Lok\u00e1lna adresa odkazu IPv6 nie je podporovan\u00e1.", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "not_supported": "Pripojen\u00e9 k AVM FRITZ!Box, ale nedok\u00e1\u017ee ovl\u00e1da\u0165 zariadenia Smart Home.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -14,20 +15,23 @@ "step": { "confirm": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, "description": "Chcete nastavi\u0165 {name}?" }, "reauth_confirm": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, "description": "Aktualizujte svoje prihlasovacie \u00fadaje pre {name}." }, "user": { "data": { "host": "Hostite\u013e", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, "description": "Zadajte inform\u00e1cie o va\u0161om AVM FRITZ!Box." } diff --git a/homeassistant/components/fritzbox_callmonitor/translations/sk.json b/homeassistant/components/fritzbox_callmonitor/translations/sk.json index db18895a108..632d11fbc78 100644 --- a/homeassistant/components/fritzbox_callmonitor/translations/sk.json +++ b/homeassistant/components/fritzbox_callmonitor/translations/sk.json @@ -9,11 +9,17 @@ }, "flow_title": "{name}", "step": { + "phonebook": { + "data": { + "phonebook": "Telef\u00f3nny zoznam" + } + }, "user": { "data": { "host": "Hostite\u013e", "password": "Heslo", - "port": "Port" + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/generic/translations/sk.json b/homeassistant/components/generic/translations/sk.json index 1ea7cb6a2cc..92f13ab5e49 100644 --- a/homeassistant/components/generic/translations/sk.json +++ b/homeassistant/components/generic/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { "already_exists": "Kamera s t\u00fdmito nastaveniami URL u\u017e existuje.", "invalid_still_image": "Adresa URL nevr\u00e1tila platn\u00fd statick\u00fd obr\u00e1zok", @@ -12,11 +15,18 @@ "step": { "user": { "data": { - "password": "Heslo" + "authentication": "Overenie", + "framerate": "Sn\u00edmkov\u00e1 frekvencia (Hz)", + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" }, "description": "Zadajte nastavenia na pripojenie ku kamere." }, "user_confirm_still": { + "data": { + "confirmed_ok": "Tento obr\u00e1zok vyzer\u00e1 dobre." + }, "description": "![Uk\u00e1\u017eka statick\u00e9ho obr\u00e1zka z fotoapar\u00e1tu]({preview_url})" } } @@ -26,17 +36,25 @@ "already_exists": "Kamera s t\u00fdmito nastaveniami URL u\u017e existuje.", "invalid_still_image": "Adresa URL nevr\u00e1tila platn\u00fd statick\u00fd obr\u00e1zok", "malformed_url": "Chybne vytvoren\u00e1 adresa URL", + "relative_url": "Relat\u00edvne adresy URL nie s\u00fa povolen\u00e9", "stream_no_route_to_host": "Pri pokuse o pripojenie k streamu sa nepodarilo n\u00e1js\u0165 hostite\u013ea", "timeout": "\u010casov\u00fd limit pri na\u010d\u00edtan\u00ed adresy URL", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "confirm_still": { + "data": { + "confirmed_ok": "Tento obr\u00e1zok vyzer\u00e1 dobre." + }, "description": "![Uk\u00e1\u017eka statick\u00e9ho obr\u00e1zka z fotoapar\u00e1tu]({preview_url})" }, "init": { "data": { - "password": "Heslo" + "authentication": "Overenie", + "framerate": "Sn\u00edmkov\u00e1 frekvencia (Hz)", + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/geocaching/translations/sk.json b/homeassistant/components/geocaching/translations/sk.json index 1f9fe121b6d..50a9875493e 100644 --- a/homeassistant/components/geocaching/translations/sk.json +++ b/homeassistant/components/geocaching/translations/sk.json @@ -3,8 +3,23 @@ "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", - "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu." + "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu.", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + }, + "create_entry": { + "default": "\u00daspe\u0161ne overen\u00e9" + }, + "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + }, + "reauth_confirm": { + "description": "Integr\u00e1cia slu\u017eby Geocaching potrebuje op\u00e4tovn\u00e9 overenie v\u00e1\u0161ho konta", + "title": "Znova overi\u0165 integr\u00e1ciu" + } } } } \ No newline at end of file diff --git a/homeassistant/components/geofency/translations/sk.json b/homeassistant/components/geofency/translations/sk.json new file mode 100644 index 00000000000..933f73976d2 --- /dev/null +++ b/homeassistant/components/geofency/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geonetnz_quakes/translations/sk.json b/homeassistant/components/geonetnz_quakes/translations/sk.json index f04d4a327f4..87f55b18c6f 100644 --- a/homeassistant/components/geonetnz_quakes/translations/sk.json +++ b/homeassistant/components/geonetnz_quakes/translations/sk.json @@ -2,6 +2,15 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "step": { + "user": { + "data": { + "mmi": "MMI", + "radius": "R\u00e1dius" + }, + "title": "Vypl\u0148te podrobnosti filtra." + } } } } \ No newline at end of file diff --git a/homeassistant/components/geonetnz_volcano/translations/sk.json b/homeassistant/components/geonetnz_volcano/translations/sk.json index 529d2911584..0e1348ce290 100644 --- a/homeassistant/components/geonetnz_volcano/translations/sk.json +++ b/homeassistant/components/geonetnz_volcano/translations/sk.json @@ -2,6 +2,14 @@ "config": { "abort": { "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + }, + "step": { + "user": { + "data": { + "radius": "R\u00e1dius" + }, + "title": "Vypl\u0148te podrobnosti filtra." + } } } } \ No newline at end of file diff --git a/homeassistant/components/gios/translations/sk.json b/homeassistant/components/gios/translations/sk.json index cae59423a4a..23559b853da 100644 --- a/homeassistant/components/gios/translations/sk.json +++ b/homeassistant/components/gios/translations/sk.json @@ -4,12 +4,15 @@ "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_sensors_data": "Neplatn\u00e9 \u00fadaje sn\u00edma\u010dov pre t\u00fato meraciu stanicu.", + "wrong_station_id": "ID meracej stanice nie je spr\u00e1vne." }, "step": { "user": { "data": { - "name": "N\u00e1zov" + "name": "N\u00e1zov", + "station_id": "ID meracej stanice" } } } diff --git a/homeassistant/components/glances/translations/sk.json b/homeassistant/components/glances/translations/sk.json index 28c47d663dc..ade917ee0ba 100644 --- a/homeassistant/components/glances/translations/sk.json +++ b/homeassistant/components/glances/translations/sk.json @@ -11,7 +11,11 @@ "data": { "host": "Hostite\u013e", "password": "Heslo", - "port": "Port" + "port": "Port", + "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t", + "version": "Verzia API Glances (2 alebo 3)" } } } diff --git a/homeassistant/components/gogogate2/translations/sk.json b/homeassistant/components/gogogate2/translations/sk.json index 48d0d65da03..b704eb5be7f 100644 --- a/homeassistant/components/gogogate2/translations/sk.json +++ b/homeassistant/components/gogogate2/translations/sk.json @@ -12,7 +12,8 @@ "user": { "data": { "ip_address": "IP adresa", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, "description": "Ni\u017e\u0161ie uve\u010fte po\u017eadovan\u00e9 inform\u00e1cie." } diff --git a/homeassistant/components/goodwe/translations/sk.json b/homeassistant/components/goodwe/translations/sk.json index 74df23fc689..9a7539ecc3b 100644 --- a/homeassistant/components/goodwe/translations/sk.json +++ b/homeassistant/components/goodwe/translations/sk.json @@ -11,7 +11,8 @@ "user": { "data": { "host": "IP adresa" - } + }, + "description": "Pripoji\u0165 k meni\u010du" } } } diff --git a/homeassistant/components/google/translations/sk.json b/homeassistant/components/google/translations/sk.json index ded98554d3f..01103448bea 100644 --- a/homeassistant/components/google/translations/sk.json +++ b/homeassistant/components/google/translations/sk.json @@ -7,7 +7,8 @@ "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu.", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "timeout_connect": "\u010casov\u00fd limit na nadviazanie spojenia" }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" @@ -16,6 +17,9 @@ "exchange": "Ak chcete prepoji\u0165 svoj \u00fa\u010det Google, nav\u0161t\u00edvte [{url}]({url}) a zadajte k\u00f3d: \n\n {user_code}" }, "step": { + "auth": { + "title": "Prepojenie konta Google" + }, "pick_implementation": { "title": "Vyberte met\u00f3du overenia" }, diff --git a/homeassistant/components/google_sheets/translations/sk.json b/homeassistant/components/google_sheets/translations/sk.json index 1bbce0d3a0e..37e824e8df9 100644 --- a/homeassistant/components/google_sheets/translations/sk.json +++ b/homeassistant/components/google_sheets/translations/sk.json @@ -10,6 +10,7 @@ "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu.", "open_spreadsheet_failure": "Chyba pri otv\u00e1ran\u00ed tabu\u013eky, podrobnosti n\u00e1jdete v protokole ch\u00fdb", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "timeout_connect": "\u010casov\u00fd limit na nadviazanie spojenia", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "create_entry": { diff --git a/homeassistant/components/gpslogger/translations/sk.json b/homeassistant/components/gpslogger/translations/sk.json new file mode 100644 index 00000000000..933f73976d2 --- /dev/null +++ b/homeassistant/components/gpslogger/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/gree/translations/sk.json b/homeassistant/components/gree/translations/sk.json index 8183bb46360..d4bb209c34c 100644 --- a/homeassistant/components/gree/translations/sk.json +++ b/homeassistant/components/gree/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "step": { "confirm": { diff --git a/homeassistant/components/group/translations/sk.json b/homeassistant/components/group/translations/sk.json index b03f41f896d..5bca7cea930 100644 --- a/homeassistant/components/group/translations/sk.json +++ b/homeassistant/components/group/translations/sk.json @@ -7,49 +7,67 @@ "entities": "\u010clenovia", "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" - } + }, + "title": "Prida\u0165 skupinu" }, "cover": { "data": { "entities": "\u010clenovia", "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" - } + }, + "title": "Prida\u0165 skupinu" }, "fan": { "data": { "entities": "\u010clenovia", "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" - } + }, + "title": "Prida\u0165 skupinu" }, "light": { "data": { "entities": "\u010clenovia", "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" - } + }, + "title": "Prida\u0165 skupinu" }, "lock": { "data": { "entities": "\u010clenovia", "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" - } + }, + "title": "Prida\u0165 skupinu" }, "media_player": { "data": { "entities": "\u010clenovia", "hide_members": "Skry\u0165 \u010dlenov", "name": "Meno" - } + }, + "title": "Prida\u0165 skupinu" }, "switch": { "data": { "entities": "\u010clenovia", "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" - } + }, + "title": "Prida\u0165 skupinu" + }, + "user": { + "menu_options": { + "binary_sensor": "Skupina sn\u00edma\u010d", + "fan": "Skupina ventil\u00e1tor", + "light": "Skupina osvetlenie", + "lock": "Skupina z\u00e1mok", + "media_player": "Skupina Media player", + "switch": "Skupina prep\u00edna\u010d" + }, + "title": "Prida\u0165 skupinu" } } }, @@ -83,6 +101,7 @@ }, "lock": { "data": { + "entities": "\u010clenovia", "hide_members": "Skry\u0165 \u010dlenov" } }, diff --git a/homeassistant/components/growatt_server/translations/sk.json b/homeassistant/components/growatt_server/translations/sk.json index 217ebf99151..256fec69c1a 100644 --- a/homeassistant/components/growatt_server/translations/sk.json +++ b/homeassistant/components/growatt_server/translations/sk.json @@ -13,9 +13,12 @@ "data": { "name": "N\u00e1zov", "password": "Heslo", - "url": "URL" - } + "url": "URL", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Zadajte inform\u00e1cie o svojom Growatte" } } - } + }, + "title": "Server Growatt" } \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.fr.json b/homeassistant/components/harmony/translations/select.fr.json index a3c7eb3ebee..cf895f7fad6 100644 --- a/homeassistant/components/harmony/translations/select.fr.json +++ b/homeassistant/components/harmony/translations/select.fr.json @@ -1,7 +1,7 @@ { "state": { "harmony__activities": { - "power_off": "\u00c9teint" + "power_off": "\u00c9teindre" } } } \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.sk.json b/homeassistant/components/harmony/translations/select.sk.json new file mode 100644 index 00000000000..b5f207e1ca1 --- /dev/null +++ b/homeassistant/components/harmony/translations/select.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Vypn\u00fa\u0165" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/sk.json b/homeassistant/components/harmony/translations/sk.json index ff396ca4d3e..78cba980f6a 100644 --- a/homeassistant/components/harmony/translations/sk.json +++ b/homeassistant/components/harmony/translations/sk.json @@ -18,5 +18,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "activity": "Predvolen\u00e1 aktivita, ktor\u00e1 sa m\u00e1 vykona\u0165, ke\u010f nie je zadan\u00e1 \u017eiadna." + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/hassio/translations/sk.json b/homeassistant/components/hassio/translations/sk.json index 2203b162891..c8c1d58b4c9 100644 --- a/homeassistant/components/hassio/translations/sk.json +++ b/homeassistant/components/hassio/translations/sk.json @@ -1,5 +1,18 @@ { "issues": { + "unsupported": { + "description": "Syst\u00e9m nie je podporovan\u00fd z {reason}. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 {reason}" + }, + "unsupported_cgroup_version": { + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 verzia CGroup" + }, + "unsupported_connectivity_check": { + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 Kontrola pripojenia je vypnut\u00e1" + }, + "unsupported_dbus": { + "title": "Nepodporovan\u00fd syst\u00e9m - probl\u00e9my s D-Bus" + }, "unsupported_dns_server": { "title": "Nepodporovan\u00fd syst\u00e9m \u2013 probl\u00e9my so serverom DNS" }, @@ -8,12 +21,22 @@ }, "unsupported_docker_version": { "title": "Nepodporovan\u00fd syst\u00e9m \u2013 verzia Docker" + }, + "unsupported_lxc": { + "title": "Nepodporovan\u00fd syst\u00e9m - zisten\u00e9 LXC" + }, + "unsupported_software": { + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 Nepodporovan\u00fd softv\u00e9r" } }, "system_health": { "info": { + "agent_version": "Verzia agenta", "docker_version": "Verzia Dockera", "host_os": "Hostite\u013esk\u00fd opera\u010dn\u00fd syst\u00e9m", + "installed_addons": "Nain\u0161talovan\u00e9 doplnky", + "supported": "Podporovan\u00e9", + "update_channel": "Aktualizova\u0165 kan\u00e1l", "version_api": "Verzia API" } } diff --git a/homeassistant/components/heos/translations/sk.json b/homeassistant/components/heos/translations/sk.json index bf16a99d5e1..d57cf8b2883 100644 --- a/homeassistant/components/heos/translations/sk.json +++ b/homeassistant/components/heos/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165" }, @@ -7,7 +10,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "title": "Pripoji\u0165 sa k Heos" } } } diff --git a/homeassistant/components/here_travel_time/translations/sk.json b/homeassistant/components/here_travel_time/translations/sk.json index c22ebc23076..7bb5d95a7e5 100644 --- a/homeassistant/components/here_travel_time/translations/sk.json +++ b/homeassistant/components/here_travel_time/translations/sk.json @@ -13,7 +13,29 @@ }, "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" + "api_key": "API k\u013e\u00fa\u010d", + "name": "N\u00e1zov" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "\u010cas pr\u00edchodu" + }, + "title": "V\u00fdber \u010dasu pr\u00edchodu" + }, + "departure_time": { + "data": { + "departure_time": "\u010cas odchodu" + }, + "title": "V\u00fdber \u010dasu odchodu" + }, + "time_menu": { + "menu_options": { + "no_time": "Nekonfigurujte \u010das" } } } diff --git a/homeassistant/components/hisense_aehw4a1/translations/sk.json b/homeassistant/components/hisense_aehw4a1/translations/sk.json new file mode 100644 index 00000000000..99798036ffd --- /dev/null +++ b/homeassistant/components/hisense_aehw4a1/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hive/translations/sk.json b/homeassistant/components/hive/translations/sk.json index 34cd991751e..18d0c4e60db 100644 --- a/homeassistant/components/hive/translations/sk.json +++ b/homeassistant/components/hive/translations/sk.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "unknown_entry": "Nie je mo\u017en\u00e9 n\u00e1js\u0165 existuj\u00faci z\u00e1znam." }, "error": { "invalid_username": "Nepodarilo sa prihl\u00e1si\u0165 do syst\u00e9mu Hive. Va\u0161a e-mailov\u00e1 adresa nie je rozpoznan\u00e1.", @@ -10,21 +11,36 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "2fa": { + "data": { + "2fa": "Dvojfaktorov\u00fd k\u00f3d" + }, + "description": "Zadajte svoj overovac\u00ed k\u00f3d Hive. \n\n Ak chcete po\u017eiada\u0165 o \u010fal\u0161\u00ed k\u00f3d, zadajte k\u00f3d 0000.", + "title": "Dvojfaktorov\u00e9 overenie Hive." + }, "configuration": { "data": { "device_name": "N\u00e1zov zariadenia" - } + }, + "description": "Zadajte konfigur\u00e1ciu Hive", + "title": "Konfigur\u00e1cia Hive." }, "reauth": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "description": "Znova zadajte svoje prihlasovacie \u00fadaje Hive.", + "title": "Prihl\u00e1senie do Hive" }, "user": { "data": { "password": "Heslo", - "scan_interval": "Interval skenovania (sekundy)" - } + "scan_interval": "Interval skenovania (sekundy)", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "description": "Zadajte svoje prihlasovacie \u00fadaje Hive.", + "title": "Prihl\u00e1senie do Hive" } } }, @@ -33,7 +49,8 @@ "user": { "data": { "scan_interval": "Interval skenovania (sekundy)" - } + }, + "title": "Mo\u017enosti pre Hive" } } } diff --git a/homeassistant/components/hlk_sw16/translations/sk.json b/homeassistant/components/hlk_sw16/translations/sk.json index 2ee96a72a30..666f6e28840 100644 --- a/homeassistant/components/hlk_sw16/translations/sk.json +++ b/homeassistant/components/hlk_sw16/translations/sk.json @@ -12,7 +12,8 @@ "user": { "data": { "host": "Hostite\u013e", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/home_connect/translations/sk.json b/homeassistant/components/home_connect/translations/sk.json index 61bfdf7ad53..370363ab123 100644 --- a/homeassistant/components/home_connect/translations/sk.json +++ b/homeassistant/components/home_connect/translations/sk.json @@ -1,10 +1,16 @@ { "config": { "abort": { + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})" }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" + }, + "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + } } } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/sk.json b/homeassistant/components/home_plus_control/translations/sk.json index 62d09ce813a..3cacfd05b98 100644 --- a/homeassistant/components/home_plus_control/translations/sk.json +++ b/homeassistant/components/home_plus_control/translations/sk.json @@ -2,10 +2,19 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" + }, + "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + } } } } \ No newline at end of file diff --git a/homeassistant/components/homeassistant/translations/bg.json b/homeassistant/components/homeassistant/translations/bg.json index 32a3f445483..d9dca0d27c0 100644 --- a/homeassistant/components/homeassistant/translations/bg.json +++ b/homeassistant/components/homeassistant/translations/bg.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "\u041d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430 \u0434\u044a\u0440\u0436\u0430\u0432\u0430, \u043c\u043e\u043b\u044f, \u0430\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u0439\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430, \u043a\u0430\u0442\u043e \u0449\u0440\u0430\u043a\u043d\u0435\u0442\u0435 \u0432\u044a\u0440\u0445\u0443 \u0431\u0443\u0442\u043e\u043d\u0430 \"\u043d\u0430\u0443\u0447\u0435\u0442\u0435 \u043f\u043e\u0432\u0435\u0447\u0435\" \u043f\u043e-\u0434\u043e\u043b\u0443.", + "title": "\u0414\u044a\u0440\u0436\u0430\u0432\u0430\u0442\u0430 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430" + }, "historic_currency": { "description": "\u0412\u0430\u043b\u0443\u0442\u0430\u0442\u0430 {currency} \u0432\u0435\u0447\u0435 \u043d\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430, \u043c\u043e\u043b\u044f, \u043f\u0440\u0435\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0439\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 \u0432\u0430\u043b\u0443\u0442\u0430\u0442\u0430.", "title": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430\u0442\u0430 \u0432\u0430\u043b\u0443\u0442\u0430 \u0432\u0435\u0447\u0435 \u043d\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430" diff --git a/homeassistant/components/homeassistant/translations/ca.json b/homeassistant/components/homeassistant/translations/ca.json index 01261ca3ea5..3272c2fcbad 100644 --- a/homeassistant/components/homeassistant/translations/ca.json +++ b/homeassistant/components/homeassistant/translations/ca.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "No s'ha configurat cap pa\u00eds, si us plau, actualitza la configuraci\u00f3 fent clic al bot\u00f3 \"m\u00e9s informaci\u00f3\" a continuaci\u00f3.", + "title": "El pa\u00eds no s'ha configurat" + }, "historic_currency": { "description": "El valor de moneda {currency} ja no s'utilitza, modifica la configuraci\u00f3 del valor de moneda.", "title": "El valor de moneda configurat ja no s'utilitza" diff --git a/homeassistant/components/homeassistant/translations/de.json b/homeassistant/components/homeassistant/translations/de.json index 85737129f53..c38585adb19 100644 --- a/homeassistant/components/homeassistant/translations/de.json +++ b/homeassistant/components/homeassistant/translations/de.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "Es wurde kein Land konfiguriert. Bitte aktualisiere die Konfiguration, indem du unten auf die Schaltfl\u00e4che \"Mehr erfahren\" klickst.", + "title": "Das Land wurde nicht konfiguriert" + }, "historic_currency": { "description": "Die W\u00e4hrung {currency} wird nicht mehr verwendet, bitte konfiguriere die W\u00e4hrungskonfiguration neu.", "title": "Die konfigurierte W\u00e4hrung ist nicht mehr in Gebrauch" diff --git a/homeassistant/components/homeassistant/translations/es.json b/homeassistant/components/homeassistant/translations/es.json index 6d64ab4747b..e7fe09ac9b9 100644 --- a/homeassistant/components/homeassistant/translations/es.json +++ b/homeassistant/components/homeassistant/translations/es.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "No se ha configurado ning\u00fan pa\u00eds, por favor, actualiza la configuraci\u00f3n haciendo clic en el bot\u00f3n \"m\u00e1s informaci\u00f3n\" a continuaci\u00f3n.", + "title": "El pa\u00eds no ha sido configurado" + }, "historic_currency": { "description": "La moneda {currency} ya no est\u00e1 en uso, por favor, vuelve a configurar la moneda.", "title": "La moneda configurada ya no est\u00e1 en uso" diff --git a/homeassistant/components/homeassistant/translations/fr.json b/homeassistant/components/homeassistant/translations/fr.json index 19835e38bf1..e4aa7daa31c 100644 --- a/homeassistant/components/homeassistant/translations/fr.json +++ b/homeassistant/components/homeassistant/translations/fr.json @@ -1,5 +1,8 @@ { "issues": { + "country_not_configured": { + "title": "Le pays n\u2019a pas \u00e9t\u00e9 configur\u00e9" + }, "historic_currency": { "description": "La devise {currency} n\u2019est plus utilis\u00e9e, veuillez actualiser la configuration de la devise.", "title": "La devise configur\u00e9e n\u2019est plus utilis\u00e9e" diff --git a/homeassistant/components/homeassistant/translations/no.json b/homeassistant/components/homeassistant/translations/no.json index aadefaca085..2ba575f693b 100644 --- a/homeassistant/components/homeassistant/translations/no.json +++ b/homeassistant/components/homeassistant/translations/no.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "Ingen land er konfigurert, vennligst oppdater konfigurasjonen ved \u00e5 klikke p\u00e5 \"les mer\"-knappen nedenfor.", + "title": "Landet er ikke konfigurert" + }, "historic_currency": { "description": "Valutaen {currency} er ikke lenger i bruk, vennligst konfigurer valutakonfigurasjonen p\u00e5 nytt.", "title": "Tollet er ugyldig eller ikke lenger autorisert." diff --git a/homeassistant/components/homeassistant/translations/ru.json b/homeassistant/components/homeassistant/translations/ru.json index cbeaa3023a8..d368a980a56 100644 --- a/homeassistant/components/homeassistant/translations/ru.json +++ b/homeassistant/components/homeassistant/translations/ru.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0441\u0442\u0440\u0430\u043d\u0435 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u0430. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e, \u043d\u0430\u0436\u0430\u0432 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \"\u0423\u0437\u043d\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435\".", + "title": "\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0441\u0442\u0440\u0430\u043d\u0435 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u0430" + }, "historic_currency": { "description": "\u0412\u0430\u043b\u044e\u0442\u0430 {currency} \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f. \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0432\u0430\u043b\u044e\u0442\u044b.", "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0432\u0430\u043b\u044e\u0442\u0430 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f" diff --git a/homeassistant/components/homeassistant/translations/sk.json b/homeassistant/components/homeassistant/translations/sk.json index 59a5d7f7c50..59d0db98bb1 100644 --- a/homeassistant/components/homeassistant/translations/sk.json +++ b/homeassistant/components/homeassistant/translations/sk.json @@ -1,5 +1,12 @@ { "issues": { + "country_not_configured": { + "description": "Nebola nakonfigurovan\u00e1 \u017eiadna krajina, aktualizujte konfigur\u00e1ciu kliknut\u00edm na tla\u010didlo \u201eviac inform\u00e1ci\u00ed\u201c ni\u017e\u0161ie.", + "title": "Krajina nebola nakonfigurovan\u00e1" + }, + "historic_currency": { + "title": "Konfigurovan\u00e1 mena sa u\u017e nepou\u017e\u00edva" + }, "python_version": { "title": "Podpora pre Python {current_python_version} sa odstra\u0148uje" } @@ -7,6 +14,7 @@ "system_health": { "info": { "arch": "Architekt\u00fara CPU", + "config_dir": "Adres\u00e1r konfigur\u00e1cie", "docker": "Docker", "installation_type": "Typ in\u0161tal\u00e1cie", "os_name": "Rodina opera\u010dn\u00fdch syst\u00e9mov", diff --git a/homeassistant/components/homeassistant/translations/zh-Hant.json b/homeassistant/components/homeassistant/translations/zh-Hant.json index e3227e6d440..d1f518daeaa 100644 --- a/homeassistant/components/homeassistant/translations/zh-Hant.json +++ b/homeassistant/components/homeassistant/translations/zh-Hant.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "\u5c1a\u672a\u8a2d\u5b9a\u570b\u5bb6\u3001\u8acb\u9ede\u9078\u4e0b\u65b9 \"\u4e86\u89e3\u66f4\u591a\" \u6309\u9215\u4ee5\u66f4\u65b0\u8a2d\u5b9a\u3002", + "title": "\u5c1a\u672a\u8a2d\u5b9a\u570b\u5bb6" + }, "historic_currency": { "description": "\u8ca8\u5e63 {currency} \u4e0d\u518d\u4f7f\u7528\u3001\u8acb\u91cd\u65b0\u8a2d\u5b9a\u5176\u4ed6\u8ca8\u5e63\u3002", "title": "\u8a2d\u5b9a\u8ca8\u5e63\u4e0d\u518d\u4f7f\u7528" diff --git a/homeassistant/components/homeassistant_yellow/translations/fr.json b/homeassistant/components/homeassistant_yellow/translations/fr.json index 8e0da06078f..b6aa5ffebd9 100644 --- a/homeassistant/components/homeassistant_yellow/translations/fr.json +++ b/homeassistant/components/homeassistant_yellow/translations/fr.json @@ -1,5 +1,8 @@ { "options": { + "abort": { + "zha_migration_failed": "La migration ZHA n\u2019a pas r\u00e9ussi." + }, "error": { "unknown": "Erreur inattendue" }, diff --git a/homeassistant/components/homekit/translations/sk.json b/homeassistant/components/homekit/translations/sk.json index 211d1141244..4469563f2f3 100644 --- a/homeassistant/components/homekit/translations/sk.json +++ b/homeassistant/components/homekit/translations/sk.json @@ -1,6 +1,31 @@ { + "config": { + "step": { + "pairing": { + "description": "Na dokon\u010denie p\u00e1rovania postupujte pod\u013ea pokynov v \u010dasti \u201eUpozornenia\u201c v \u010dasti \u201eP\u00e1rovanie HomeKit\u201c." + } + } + }, "options": { "step": { + "accessory": { + "data": { + "entities": "Entita" + } + }, + "advanced": { + "title": "Roz\u0161\u00edren\u00e1 konfigur\u00e1cia" + }, + "exclude": { + "data": { + "entities": "Entity" + } + }, + "include": { + "data": { + "entities": "Entity" + } + }, "yaml": { "description": "T\u00e1to polo\u017eka sa ovl\u00e1da pomocou YAML" } diff --git a/homeassistant/components/homekit_controller/translations/select.sk.json b/homeassistant/components/homekit_controller/translations/select.sk.json new file mode 100644 index 00000000000..62277db1800 --- /dev/null +++ b/homeassistant/components/homekit_controller/translations/select.sk.json @@ -0,0 +1,9 @@ +{ + "state": { + "homekit_controller__ecobee_mode": { + "away": "Pre\u010d", + "home": "Doma", + "sleep": "Sp\u00e1nok" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/sensor.sk.json b/homeassistant/components/homekit_controller/translations/sensor.sk.json index 145119e835b..9d22481d838 100644 --- a/homeassistant/components/homekit_controller/translations/sensor.sk.json +++ b/homeassistant/components/homekit_controller/translations/sensor.sk.json @@ -1,6 +1,7 @@ { "state": { "homekit_controller__thread_status": { + "disabled": "Zak\u00e1zan\u00e9", "router": "Router" } } diff --git a/homeassistant/components/homekit_controller/translations/sk.json b/homeassistant/components/homekit_controller/translations/sk.json index 29f900b66d1..a62d9c6334a 100644 --- a/homeassistant/components/homekit_controller/translations/sk.json +++ b/homeassistant/components/homekit_controller/translations/sk.json @@ -3,9 +3,12 @@ "abort": { "accessory_not_found_error": "Nie je mo\u017en\u00e9 prida\u0165 p\u00e1rovanie, preto\u017ee zariadenie u\u017e nie je mo\u017en\u00e9 n\u00e1js\u0165.", "already_configured": "Pr\u00edslu\u0161enstvo je u\u017e nakonfigurovan\u00e9 s t\u00fdmto kontrol\u00e9rom.", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "already_paired": "Toto pr\u00edslu\u0161enstvo je u\u017e sp\u00e1rovan\u00e9 s in\u00fdm zariaden\u00edm. Resetujte pr\u00edslu\u0161enstvo a sk\u00faste to znova.", + "no_devices": "Nena\u0161li sa \u017eiadne nesp\u00e1rovan\u00e9 zariadenia" }, "error": { + "authentication_error": "Nespr\u00e1vny k\u00f3d HomeKit. Skontrolujte to a sk\u00faste to znova.", "pairing_failed": "Pri pokuse o sp\u00e1rovanie s t\u00fdmto zariaden\u00edm do\u0161lo k neobsluhovanej chybe. M\u00f4\u017ee \u00eds\u0165 o do\u010dasn\u00fa chybu alebo va\u0161e zariadenie nemus\u00ed by\u0165 v s\u00fa\u010dasnosti podporovan\u00e9: {error}", "unable_to_pair": "Nie je mo\u017en\u00e9 sp\u00e1rova\u0165, sk\u00faste to znova.", "unknown_error": "Zariadenie hl\u00e1silo nezn\u00e1mu chybu. Sp\u00e1rovanie zlyhalo." @@ -15,6 +18,18 @@ "busy_error": { "title": "Zariadenie sa u\u017e sp\u00e1ruje s in\u00fdm kontrol\u00e9rom" }, + "max_tries_error": { + "title": "Bol prekro\u010den\u00fd maxim\u00e1lny po\u010det pokusov o overenie" + }, + "pair": { + "data": { + "allow_insecure_setup_codes": "Povoli\u0165 p\u00e1rovanie s nezabezpe\u010den\u00fdmi k\u00f3dmi nastavenia.", + "pairing_code": "K\u00f3d p\u00e1rovania" + } + }, + "protocol_error": { + "title": "Chyba pri komunik\u00e1cii s pr\u00edslu\u0161enstvom" + }, "user": { "data": { "device": "Zariadenie" diff --git a/homeassistant/components/homewizard/translations/sk.json b/homeassistant/components/homewizard/translations/sk.json index dc965799bfd..2dc757a5250 100644 --- a/homeassistant/components/homewizard/translations/sk.json +++ b/homeassistant/components/homewizard/translations/sk.json @@ -3,11 +3,13 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "device_not_supported": "Toto zariadenie nie je podporovan\u00e9", - "invalid_discovery_parameters": "Zisten\u00e1 nepodporovan\u00e1 verzia API" + "invalid_discovery_parameters": "Zisten\u00e1 nepodporovan\u00e1 verzia API", + "unknown_error": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "discovery_confirm": { - "description": "Chcete nastavi\u0165 {product_type} ({serial}) na {ip_address}?" + "description": "Chcete nastavi\u0165 {product_type} ({serial}) na {ip_address}?", + "title": "Potvrdi\u0165" }, "user": { "data": { diff --git a/homeassistant/components/honeywell/translations/sk.json b/homeassistant/components/honeywell/translations/sk.json index 1b1e671c054..04cb927ad6f 100644 --- a/homeassistant/components/honeywell/translations/sk.json +++ b/homeassistant/components/honeywell/translations/sk.json @@ -6,7 +6,8 @@ "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/huawei_lte/translations/sk.json b/homeassistant/components/huawei_lte/translations/sk.json index bfc3dbc8a1d..2d0f4eacf54 100644 --- a/homeassistant/components/huawei_lte/translations/sk.json +++ b/homeassistant/components/huawei_lte/translations/sk.json @@ -1,23 +1,35 @@ { "config": { + "abort": { + "not_huawei_lte": "Nie je to zariadenie Huawei LTE", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + }, "error": { + "connection_timeout": "\u010casov\u00fd limit pripojenia", "incorrect_password": "Nespr\u00e1vne heslo", + "incorrect_username": "Nespr\u00e1vne pou\u017e\u00edvate\u013esk\u00e9 meno", "invalid_auth": "Neplatn\u00e9 overenie", "invalid_url": "Neplatn\u00e1 adresa URL", - "response_error": "Nezn\u00e1ma chyba zo zariadenia" + "login_attempts_exceeded": "Bol prekro\u010den\u00fd maxim\u00e1lny po\u010det pokusov o prihl\u00e1senie, sk\u00faste to znova nesk\u00f4r", + "response_error": "Nezn\u00e1ma chyba zo zariadenia", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name}", "step": { "reauth_confirm": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { "password": "Heslo", - "url": "URL" - } + "url": "URL", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Nakonfigurujte Huawei LTE" } } }, diff --git a/homeassistant/components/hue/translations/sk.json b/homeassistant/components/hue/translations/sk.json index 307a09a5a36..520fee9e6e4 100644 --- a/homeassistant/components/hue/translations/sk.json +++ b/homeassistant/components/hue/translations/sk.json @@ -7,6 +7,7 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_host": "Neplatn\u00fd hostite\u013e", "no_bridges": "Neboli objaven\u00fd \u017eiaden Philips Hue bridge", + "not_hue_bridge": "Nie je to most Hue Bridge", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { @@ -34,13 +35,37 @@ "trigger_subtype": { "1": "Prv\u00e9 tla\u010didlo", "2": "Druh\u00e9 tla\u010didlo", + "3": "Tretie tla\u010didlo", + "4": "\u0160tvrt\u00e9 tla\u010didlo", + "button_1": "Prv\u00e9 tla\u010didlo", "button_2": "Druh\u00e9 tla\u010didlo", - "double_buttons_2_4": "Druh\u00e9 a \u0161tvrt\u00e9 tla\u010didlo" + "button_3": "Tretie tla\u010didlo", + "button_4": "\u0160tvrt\u00e9 tla\u010didlo", + "double_buttons_1_3": "Prv\u00e9 a tretie tla\u010didlo", + "double_buttons_2_4": "Druh\u00e9 a \u0161tvrt\u00e9 tla\u010didlo", + "turn_off": "Vypn\u00fa\u0165", + "turn_on": "Zapn\u00fa\u0165" }, "trigger_type": { + "double_short_release": "Obe \u201e{subtype}\u201c boli uvo\u013enen\u00e9", + "long_release": "\"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed", "remote_button_long_release": "\"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed", "remote_button_short_press": "\"{subtype}\" stla\u010den\u00e9", - "remote_button_short_release": "\u201c{subtype}\u201c uvo\u013enen\u00e9" + "remote_button_short_release": "\u201c{subtype}\u201c uvo\u013enen\u00e9", + "remote_double_button_long_press": "Obe \"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed", + "remote_double_button_short_press": "Obe \u201e{subtype}\u201c boli uvo\u013enen\u00e9", + "repeat": "\u201e{subtype}\u201c podr\u017ean\u00e9", + "short_release": "\"{subtype}\" uvo\u013enen\u00e9 po kr\u00e1tkom stla\u010den\u00ed" + } + }, + "options": { + "step": { + "init": { + "data": { + "allow_hue_groups": "Povoli\u0165 skupiny Hue", + "allow_hue_scenes": "Povoli\u0165 sc\u00e9ny Hue" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/huisbaasje/translations/sk.json b/homeassistant/components/huisbaasje/translations/sk.json index 49b712d3ea1..0c9a112e32e 100644 --- a/homeassistant/components/huisbaasje/translations/sk.json +++ b/homeassistant/components/huisbaasje/translations/sk.json @@ -4,13 +4,15 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/humidifier/translations/sk.json b/homeassistant/components/humidifier/translations/sk.json index deac076ac2d..d9cd16a451c 100644 --- a/homeassistant/components/humidifier/translations/sk.json +++ b/homeassistant/components/humidifier/translations/sk.json @@ -1,10 +1,17 @@ { "device_automation": { "action_type": { - "set_humidity": "Nastavi\u0165 vlhkos\u0165 pre {entity_name}" + "set_humidity": "Nastavi\u0165 vlhkos\u0165 pre {entity_name}", + "turn_off": "Vypn\u00fa\u0165 {entity_name}", + "turn_on": "Zapn\u00fa\u0165 {entity_name}" + }, + "condition_type": { + "is_off": "{entity_name} je vypnut\u00e9", + "is_on": "{entity_name} je zapnut\u00e9" }, "trigger_type": { - "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9" + "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9", + "target_humidity_changed": "Cie\u013eov\u00e1 vlhkos\u0165 {entity_name} sa zmenila" } }, "state": { diff --git a/homeassistant/components/hvv_departures/translations/sk.json b/homeassistant/components/hvv_departures/translations/sk.json index df2114b37f9..bacfbd506d5 100644 --- a/homeassistant/components/hvv_departures/translations/sk.json +++ b/homeassistant/components/hvv_departures/translations/sk.json @@ -24,8 +24,10 @@ "user": { "data": { "host": "Hostite\u013e", - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Pripojte sa k HVV API" } } }, @@ -34,7 +36,8 @@ "init": { "data": { "offset": "Posun (v min\u00fatach)" - } + }, + "title": "Mo\u017enosti" } } } diff --git a/homeassistant/components/iaqualink/translations/sk.json b/homeassistant/components/iaqualink/translations/sk.json index 13cce2dcec3..df490c1220b 100644 --- a/homeassistant/components/iaqualink/translations/sk.json +++ b/homeassistant/components/iaqualink/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" @@ -7,9 +10,11 @@ "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, - "description": "Zadajte pou\u017e\u00edvate\u013esk\u00e9 meno a heslo pre v\u00e1\u0161 \u00fa\u010det iAqualink." + "description": "Zadajte pou\u017e\u00edvate\u013esk\u00e9 meno a heslo pre v\u00e1\u0161 \u00fa\u010det iAqualink.", + "title": "Pripojte sa k iAqualink" } } } diff --git a/homeassistant/components/ibeacon/translations/sk.json b/homeassistant/components/ibeacon/translations/sk.json new file mode 100644 index 00000000000..c294bc45d7c --- /dev/null +++ b/homeassistant/components/ibeacon/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/icloud/translations/sk.json b/homeassistant/components/icloud/translations/sk.json index 9711125c5e1..fbd317aec47 100644 --- a/homeassistant/components/icloud/translations/sk.json +++ b/homeassistant/components/icloud/translations/sk.json @@ -13,7 +13,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "title": "Znova overi\u0165 integr\u00e1ciu" }, "trusted_device": { "data": { @@ -26,7 +27,15 @@ "data": { "password": "Heslo", "username": "Email" - } + }, + "title": "iCloud poverenia" + }, + "verification_code": { + "data": { + "verification_code": "Overovac\u00ed k\u00f3d" + }, + "description": "Zadajte overovac\u00ed k\u00f3d, ktor\u00fd ste pr\u00e1ve dostali z iCloud", + "title": "iCloud overovac\u00ed k\u00f3d" } } } diff --git a/homeassistant/components/ifttt/translations/sk.json b/homeassistant/components/ifttt/translations/sk.json new file mode 100644 index 00000000000..97f8b46cbdf --- /dev/null +++ b/homeassistant/components/ifttt/translations/sk.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + }, + "step": { + "user": { + "description": "Naozaj chcete nastavi\u0165 IFTTT?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/insteon/translations/sk.json b/homeassistant/components/insteon/translations/sk.json index d10b7bfdc96..651f7012660 100644 --- a/homeassistant/components/insteon/translations/sk.json +++ b/homeassistant/components/insteon/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", @@ -22,7 +23,13 @@ "data": { "host": "IP adresa", "password": "Heslo", - "port": "Port" + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + } + }, + "plm": { + "data": { + "device": "Cesta k zariadeniu USB" } } } @@ -41,11 +48,18 @@ "subcat": "Podkateg\u00f3ria zariadenia (napr. 0x0a)" } }, + "add_x10": { + "data": { + "housecode": "K\u00f3d domu (a - p)", + "platform": "Platforma" + } + }, "change_hub_config": { "data": { "host": "IP adresa", "password": "Heslo", - "port": "Port" + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "remove_override": { diff --git a/homeassistant/components/integration/translations/sk.json b/homeassistant/components/integration/translations/sk.json index c051b1c5d0d..25cf5102677 100644 --- a/homeassistant/components/integration/translations/sk.json +++ b/homeassistant/components/integration/translations/sk.json @@ -3,10 +3,20 @@ "step": { "user": { "data": { + "name": "N\u00e1zov", "round": "Presnos\u0165", "source": "Vstupn\u00fd sn\u00edma\u010d" } } } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Presnos\u0165" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/intellifire/translations/sk.json b/homeassistant/components/intellifire/translations/sk.json index 70bdb6bfbcc..944a3c43d39 100644 --- a/homeassistant/components/intellifire/translations/sk.json +++ b/homeassistant/components/intellifire/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "api_error": "Prihl\u00e1senie zlyhalo", diff --git a/homeassistant/components/ios/translations/sk.json b/homeassistant/components/ios/translations/sk.json index e227301685b..d9f4f46bffe 100644 --- a/homeassistant/components/ios/translations/sk.json +++ b/homeassistant/components/ios/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "step": { "confirm": { "description": "Chcete za\u010da\u0165 nastavova\u0165?" diff --git a/homeassistant/components/iotawatt/translations/sk.json b/homeassistant/components/iotawatt/translations/sk.json index ff5ef67d0e3..0fcf434a18d 100644 --- a/homeassistant/components/iotawatt/translations/sk.json +++ b/homeassistant/components/iotawatt/translations/sk.json @@ -8,7 +8,8 @@ "step": { "auth": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "user": { diff --git a/homeassistant/components/ipp/translations/sk.json b/homeassistant/components/ipp/translations/sk.json index d2ce15b8904..1f5c5b348ec 100644 --- a/homeassistant/components/ipp/translations/sk.json +++ b/homeassistant/components/ipp/translations/sk.json @@ -3,10 +3,12 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "cannot_connect": "Nepodarilo sa pripoji\u0165", - "ipp_error": "Vyskytla sa chyba IPP." + "ipp_error": "Vyskytla sa chyba IPP.", + "ipp_version_error": "Verzia IPP nie je podporovan\u00e1 tla\u010diar\u0148ou." }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "connection_upgrade": "Nepodarilo sa pripoji\u0165 k tla\u010diarni. Sk\u00faste to znova so za\u010diarknutou mo\u017enos\u0165ou SSL/TLS." }, "flow_title": "{name}", "step": { @@ -16,10 +18,12 @@ "port": "Port", "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" - } + }, + "title": "Prepojte tla\u010diare\u0148" }, "zeroconf_confirm": { - "description": "Chcete nastavi\u0165 {name}?" + "description": "Chcete nastavi\u0165 {name}?", + "title": "Objaven\u00e1 tla\u010diare\u0148" } } } diff --git a/homeassistant/components/iqvia/translations/sk.json b/homeassistant/components/iqvia/translations/sk.json index 4691f8e2262..4d25cac41f9 100644 --- a/homeassistant/components/iqvia/translations/sk.json +++ b/homeassistant/components/iqvia/translations/sk.json @@ -10,7 +10,8 @@ "user": { "data": { "zip_code": "PS\u010c" - } + }, + "description": "Vypl\u0148te PS\u010c v USA alebo Kanade." } } } diff --git a/homeassistant/components/islamic_prayer_times/translations/sk.json b/homeassistant/components/islamic_prayer_times/translations/sk.json new file mode 100644 index 00000000000..10b77b1d0aa --- /dev/null +++ b/homeassistant/components/islamic_prayer_times/translations/sk.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "user": { + "description": "Chcete nastavi\u0165 \u010dasy islamsk\u00fdch modlitieb?", + "title": "Nastavte \u010dasy islamsk\u00fdch modlitieb" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "calculation_method": "Met\u00f3da v\u00fdpo\u010dtu modlitby" + } + } + } + }, + "title": "\u010casy islamsk\u00fdch modlitieb" +} \ No newline at end of file diff --git a/homeassistant/components/iss/translations/sk.json b/homeassistant/components/iss/translations/sk.json new file mode 100644 index 00000000000..0d6627fce15 --- /dev/null +++ b/homeassistant/components/iss/translations/sk.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "latitude_longitude_not_defined": "Zemepisn\u00e1 \u0161\u00edrka a d\u013a\u017eka nie s\u00fa definovan\u00e9 v aplik\u00e1cii Home Assistant.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/isy994/translations/sk.json b/homeassistant/components/isy994/translations/sk.json index 3f3d21841e6..06886d7c6bc 100644 --- a/homeassistant/components/isy994/translations/sk.json +++ b/homeassistant/components/isy994/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "invalid_host": "Z\u00e1znam hostite\u013ea nemal \u00fapln\u00fd form\u00e1t adresy URL, napr. http://192.168.10.100:80", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" @@ -12,21 +13,34 @@ "step": { "reauth_confirm": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, "description": "Poverenia pre {host} u\u017e nie s\u00fa platn\u00e9." }, "user": { "data": { "host": "URL", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, - "description": "Polo\u017eka hostite\u013ea mus\u00ed by\u0165 v \u00faplnom form\u00e1te URL, napr. http://192.168.10.100:80" + "description": "Polo\u017eka hostite\u013ea mus\u00ed by\u0165 v \u00faplnom form\u00e1te URL, napr. http://192.168.10.100:80", + "title": "Pripoji\u0165 sa k ISY" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "restore_light_state": "Obnovenie jasu osvetlenia" + } } } }, "system_health": { "info": { + "device_connected": "ISY pripojen\u00e9", "host_reachable": "Hostite\u013e je dostupn\u00fd" } } diff --git a/homeassistant/components/izone/translations/sk.json b/homeassistant/components/izone/translations/sk.json index 69d8d942494..99798036ffd 100644 --- a/homeassistant/components/izone/translations/sk.json +++ b/homeassistant/components/izone/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." } } } \ No newline at end of file diff --git a/homeassistant/components/jellyfin/translations/sk.json b/homeassistant/components/jellyfin/translations/sk.json index 1334ea2f532..02a53ae31a4 100644 --- a/homeassistant/components/jellyfin/translations/sk.json +++ b/homeassistant/components/jellyfin/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", @@ -9,7 +12,8 @@ "user": { "data": { "password": "Heslo", - "url": "URL" + "url": "URL", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/juicenet/translations/sk.json b/homeassistant/components/juicenet/translations/sk.json index 2c2f08622c0..081a4e8d77f 100644 --- a/homeassistant/components/juicenet/translations/sk.json +++ b/homeassistant/components/juicenet/translations/sk.json @@ -13,7 +13,8 @@ "data": { "api_token": "API token" }, - "description": "Budete potrebova\u0165 API Token z https://home.juice.net/Manage." + "description": "Budete potrebova\u0165 API Token z https://home.juice.net/Manage.", + "title": "Pripojte sa k JuiceNet" } } } diff --git a/homeassistant/components/keymitt_ble/translations/sk.json b/homeassistant/components/keymitt_ble/translations/sk.json index caf350e7f81..f9c4614994b 100644 --- a/homeassistant/components/keymitt_ble/translations/sk.json +++ b/homeassistant/components/keymitt_ble/translations/sk.json @@ -6,12 +6,19 @@ "no_unconfigured_devices": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, + "error": { + "linking": "P\u00e1rovanie zlyhalo, sk\u00faste to znova. Je MicroBot v re\u017eime p\u00e1rovania?" + }, "flow_title": "{name}", "step": { "init": { "data": { - "address": "Adresa zariadenia" + "address": "Adresa zariadenia", + "name": "N\u00e1zov" } + }, + "link": { + "title": "P\u00e1rovanie" } } } diff --git a/homeassistant/components/kmtronic/translations/sk.json b/homeassistant/components/kmtronic/translations/sk.json index 2ee96a72a30..666f6e28840 100644 --- a/homeassistant/components/kmtronic/translations/sk.json +++ b/homeassistant/components/kmtronic/translations/sk.json @@ -12,7 +12,8 @@ "user": { "data": { "host": "Hostite\u013e", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/knx/translations/bg.json b/homeassistant/components/knx/translations/bg.json index 9ce61dfc316..af87f7d99ba 100644 --- a/homeassistant/components/knx/translations/bg.json +++ b/homeassistant/components/knx/translations/bg.json @@ -28,6 +28,11 @@ "local_ip": "\u041e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0437\u0430 \u0434\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435." } }, + "secure_routing_manual": { + "data_description": { + "sync_latency_tolerance": "\u041f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435 \u0435 1000." + } + }, "secure_tunnel_manual": { "data": { "user_id": "ID \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f", @@ -61,6 +66,11 @@ "local_ip": "\u041e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0437\u0430 \u0434\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435." } }, + "secure_routing_manual": { + "data_description": { + "sync_latency_tolerance": "\u041f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435 \u0435 1000." + } + }, "tunnel": { "description": "\u041c\u043e\u043b\u044f, \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0448\u043b\u044e\u0437 \u043e\u0442 \u0441\u043f\u0438\u0441\u044a\u043a\u0430." } diff --git a/homeassistant/components/knx/translations/ca.json b/homeassistant/components/knx/translations/ca.json index c7ebe728b9f..ebd50a5810f 100644 --- a/homeassistant/components/knx/translations/ca.json +++ b/homeassistant/components/knx/translations/ca.json @@ -7,6 +7,7 @@ "error": { "cannot_connect": "Ha fallat la connexi\u00f3", "file_not_found": "No s'ha trobat el fitxer `.knxkeys` especificat a la ruta config/.storage/knx/", + "invalid_backbone_key": "Clau troncal inv\u00e0lida. S'esperen 32 nombres hexadecimals.", "invalid_individual_address": "El valor no coincideix amb el patr\u00f3 d'adre\u00e7a KNX individual.\n'area.line.device'", "invalid_ip_address": "Adre\u00e7a IPv4 inv\u00e0lida.", "invalid_signature": "La contrasenya per desxifrar el fitxer `.knxkeys` \u00e9s incorrecta.", @@ -41,7 +42,8 @@ "individual_address": "Adre\u00e7a individual", "local_ip": "IP local de Home Assistant", "multicast_group": "Grup multidifusi\u00f3", - "multicast_port": "Port multidifusi\u00f3" + "multicast_port": "Port multidifusi\u00f3", + "routing_secure": "Utilitza KNX IP Secure" }, "data_description": { "individual_address": "Adre\u00e7a KNX per utilitzar amb Home Assistant, p. ex. `0.0.4`", @@ -49,6 +51,14 @@ }, "description": "Configura les opcions d'encaminament." }, + "secure_key_source": { + "description": "Selecciona com vols configurar KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Utilitza un fitxer `.knxkeys` que contingui les claus de seguretat IP (IP Secure)", + "secure_routing_manual": "Configura manualment la clau troncal de seguretat IP (IP Secure)", + "secure_tunnel_manual": "Configura manualment les credencials de seguretat IP (IP Secure)" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "Nom del teu fitxer `.knxkeys` (inclosa l'extensi\u00f3)", @@ -60,6 +70,17 @@ }, "description": "Introdueix la informaci\u00f3 del teu fitxer `.knxkeys`." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Clau troncal ('backbone')", + "sync_latency_tolerance": "Toler\u00e0ncia de lat\u00e8ncia de xarxa" + }, + "data_description": { + "backbone_key": "Es pot veure dins l'informe de 'Seguretat' d'un projecte ETS. Per exemple: '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "El valor per defecte \u00e9s 1000." + }, + "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure)." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Contrasenya d'autenticaci\u00f3 del dispositiu", @@ -92,6 +113,7 @@ "error": { "cannot_connect": "Ha fallat la connexi\u00f3", "file_not_found": "No s'ha trobat el fitxer `.knxkeys` especificat a la ruta config/.storage/knx/", + "invalid_backbone_key": "Clau troncal inv\u00e0lida. S'esperen 32 nombres hexadecimals.", "invalid_individual_address": "El valor no coincideix amb el patr\u00f3 d'adre\u00e7a KNX individual.\n'area.line.device'", "invalid_ip_address": "Adre\u00e7a IPv4 inv\u00e0lida.", "invalid_signature": "La contrasenya per desxifrar el fitxer `.knxkeys` \u00e9s incorrecta.", @@ -142,7 +164,8 @@ "individual_address": "Adre\u00e7a individual", "local_ip": "IP local de Home Assistant", "multicast_group": "Grup multidifusi\u00f3", - "multicast_port": "Port multidifusi\u00f3" + "multicast_port": "Port multidifusi\u00f3", + "routing_secure": "Utilitza KNX IP Secure" }, "data_description": { "individual_address": "Adre\u00e7a KNX per utilitzar amb Home Assistant, p. ex. `0.0.4`", @@ -150,6 +173,14 @@ }, "description": "Configura les opcions d'encaminament." }, + "secure_key_source": { + "description": "Selecciona com vols configurar KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Utilitza un fitxer `.knxkeys` que contingui les claus de seguretat IP (IP Secure)", + "secure_routing_manual": "Configura manualment la clau troncal de seguretat IP (IP Secure)", + "secure_tunnel_manual": "Configura manualment les credencials de seguretat IP (IP Secure)" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "Nom del teu fitxer `.knxkeys` (inclosa l'extensi\u00f3)", @@ -161,6 +192,17 @@ }, "description": "Introdueix la informaci\u00f3 del teu fitxer `.knxkeys`." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Clau troncal ('backbone')", + "sync_latency_tolerance": "Toler\u00e0ncia de lat\u00e8ncia de xarxa" + }, + "data_description": { + "backbone_key": "Es pot veure dins l'informe de 'Seguretat' d'un projecte ETS. Per exemple: '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "El valor per defecte \u00e9s 1000." + }, + "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure)." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Contrasenya d'autenticaci\u00f3 del dispositiu", diff --git a/homeassistant/components/knx/translations/de.json b/homeassistant/components/knx/translations/de.json index f6a4e6a9616..1e39eb096b4 100644 --- a/homeassistant/components/knx/translations/de.json +++ b/homeassistant/components/knx/translations/de.json @@ -7,6 +7,7 @@ "error": { "cannot_connect": "Verbindung fehlgeschlagen", "file_not_found": "Die angegebene `.knxkeys`-Datei wurde im Pfad config/.storage/knx/ nicht gefunden.", + "invalid_backbone_key": "Ung\u00fcltiger Backbone-Schl\u00fcssel. 32 Hexadezimalzahlen erwartet.", "invalid_individual_address": "Wert ist keine g\u00fcltige physikalische Adresse. 'Bereich.Linie.Teilnehmer'", "invalid_ip_address": "Ung\u00fcltige IPv4 Adresse.", "invalid_signature": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys`-Datei ist ung\u00fcltig.", @@ -41,7 +42,8 @@ "individual_address": "Physikalische Adresse", "local_ip": "Lokale IP von Home Assistant", "multicast_group": "Multicast-Gruppe", - "multicast_port": "Multicast-Port" + "multicast_port": "Multicast-Port", + "routing_secure": "KNX IP-Secure verwenden" }, "data_description": { "individual_address": "Physikalische Adresse, die von Home Assistant verwendet werden soll, z. B. \u201e0.0.4\u201c.", @@ -49,6 +51,14 @@ }, "description": "Bitte konfiguriere die Routing-Optionen." }, + "secure_key_source": { + "description": "W\u00e4hle aus, wie du KNX/IP-Secure konfigurieren m\u00f6chtest.", + "menu_options": { + "secure_knxkeys": "Verwenden einer \"knxkeys\"-Datei mit IP-Secure-Schl\u00fcsseln", + "secure_routing_manual": "IP-Secure Backbone-Schl\u00fcssel manuell konfigurieren", + "secure_tunnel_manual": "IP-Secure-Anmeldeinformationen manuell konfigurieren" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "Der Dateiname deiner `.knxkeys`-Datei (einschlie\u00dflich Erweiterung)", @@ -60,6 +70,17 @@ }, "description": "Bitte gib die Informationen f\u00fcr deine `.knxkeys`-Datei ein." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Backbone-Schl\u00fcssel", + "sync_latency_tolerance": "Netzwerklatenztoleranz" + }, + "data_description": { + "backbone_key": "Kann im Bericht \"Sicherheit\" eines ETS-Projekts eingesehen werden. Eg. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Der Standardwert ist 1000." + }, + "description": "Bitte gib deine IP-Secure Informationen ein." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Ger\u00e4te-Authentifizierungscode", @@ -92,6 +113,7 @@ "error": { "cannot_connect": "Verbindung fehlgeschlagen", "file_not_found": "Die angegebene `.knxkeys`-Datei wurde im Pfad config/.storage/knx/ nicht gefunden.", + "invalid_backbone_key": "Ung\u00fcltiger Backbone-Schl\u00fcssel. 32 Hexadezimalzahlen erwartet.", "invalid_individual_address": "Wert ist keine g\u00fcltige physikalische Adresse. 'Bereich.Linie.Teilnehmer'", "invalid_ip_address": "Ung\u00fcltige IPv4 Adresse.", "invalid_signature": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys`-Datei ist ung\u00fcltig.", @@ -142,7 +164,8 @@ "individual_address": "Physikalische Adresse", "local_ip": "Lokale IP von Home Assistant", "multicast_group": "Multicast-Gruppe", - "multicast_port": "Multicast-Port" + "multicast_port": "Multicast-Port", + "routing_secure": "KNX IP-Secure verwenden" }, "data_description": { "individual_address": "Physikalische Adresse, die von Home Assistant verwendet werden soll, z. B. \u201e0.0.4\u201c.", @@ -150,6 +173,14 @@ }, "description": "Bitte konfiguriere die Routing-Optionen." }, + "secure_key_source": { + "description": "W\u00e4hle aus, wie du KNX/IP-Secure konfigurieren m\u00f6chtest.", + "menu_options": { + "secure_knxkeys": "Verwenden einer \"knxkeys\"-Datei mit IP-Secure-Schl\u00fcsseln", + "secure_routing_manual": "IP-Secure Backbone-Schl\u00fcssel manuell konfigurieren", + "secure_tunnel_manual": "IP-Secure-Anmeldeinformationen manuell konfigurieren" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "Der Dateiname deiner `.knxkeys`-Datei (einschlie\u00dflich Erweiterung)", @@ -161,6 +192,17 @@ }, "description": "Bitte gib die Informationen f\u00fcr deine `.knxkeys`-Datei ein." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Backbone-Schl\u00fcssel", + "sync_latency_tolerance": "Netzwerklatenztoleranz" + }, + "data_description": { + "backbone_key": "Kann im Bericht \"Sicherheit\" eines ETS-Projekts eingesehen werden. Eg. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Der Standardwert ist 1000." + }, + "description": "Bitte gib deine IP-Secure Informationen ein." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Ger\u00e4te-Authentifizierungscode", diff --git a/homeassistant/components/knx/translations/es.json b/homeassistant/components/knx/translations/es.json index d11b0446701..f710dad5597 100644 --- a/homeassistant/components/knx/translations/es.json +++ b/homeassistant/components/knx/translations/es.json @@ -7,6 +7,7 @@ "error": { "cannot_connect": "No se pudo conectar", "file_not_found": "El archivo `.knxkeys` especificado no se encontr\u00f3 en la ruta config/.storage/knx/", + "invalid_backbone_key": "Clave de red troncal no v\u00e1lida. Se esperan 32 n\u00fameros hexadecimales.", "invalid_individual_address": "El valor no coincide con el patr\u00f3n de la direcci\u00f3n KNX individual. 'area.line.device'", "invalid_ip_address": "Direcci\u00f3n IPv4 no v\u00e1lida.", "invalid_signature": "La contrase\u00f1a para descifrar el archivo `.knxkeys` es incorrecta.", @@ -41,7 +42,8 @@ "individual_address": "Direcci\u00f3n individual", "local_ip": "IP local de Home Assistant", "multicast_group": "Grupo multicast", - "multicast_port": "Puerto multicast" + "multicast_port": "Puerto multicast", + "routing_secure": "Utilizar KNX IP Secure" }, "data_description": { "individual_address": "Direcci\u00f3n KNX que usar\u00e1 Home Assistant, por ejemplo, `0.0.4`", @@ -49,6 +51,14 @@ }, "description": "Por favor, configura las opciones de enrutamiento." }, + "secure_key_source": { + "description": "Selecciona c\u00f3mo deseas configurar KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Utilizar un archivo `.knxkeys` que contenga claves seguras de IP", + "secure_routing_manual": "Configurar la clave de red troncal segura de IP manualmente", + "secure_tunnel_manual": "Configurar las credenciales seguras de IP manualmente" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "El nombre de tu archivo `.knxkeys` (incluyendo la extensi\u00f3n)", @@ -60,6 +70,17 @@ }, "description": "Por favor, introduce la informaci\u00f3n de tu archivo `.knxkeys`." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Clave de la red troncal", + "sync_latency_tolerance": "Tolerancia a la latencia de red" + }, + "data_description": { + "backbone_key": "Se puede ver en el informe de 'Seguridad' de un proyecto ETS. Ej. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "El valor predeterminado es 1000." + }, + "description": "Por favor, introduce tu informaci\u00f3n de IP segura." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Contrase\u00f1a de autenticaci\u00f3n del dispositivo", @@ -92,6 +113,7 @@ "error": { "cannot_connect": "No se pudo conectar", "file_not_found": "El archivo `.knxkeys` especificado no se encontr\u00f3 en la ruta config/.storage/knx/", + "invalid_backbone_key": "Clave de red troncal no v\u00e1lida. Se esperan 32 n\u00fameros hexadecimales.", "invalid_individual_address": "El valor no coincide con el patr\u00f3n de la direcci\u00f3n KNX individual. 'area.line.device'", "invalid_ip_address": "Direcci\u00f3n IPv4 no v\u00e1lida.", "invalid_signature": "La contrase\u00f1a para descifrar el archivo `.knxkeys` es incorrecta.", @@ -142,7 +164,8 @@ "individual_address": "Direcci\u00f3n individual", "local_ip": "IP local de Home Assistant", "multicast_group": "Grupo multicast", - "multicast_port": "Puerto multicast" + "multicast_port": "Puerto multicast", + "routing_secure": "Utilizar KNX IP Secure" }, "data_description": { "individual_address": "Direcci\u00f3n KNX que usar\u00e1 Home Assistant, por ejemplo, `0.0.4`", @@ -150,6 +173,14 @@ }, "description": "Por favor, configura las opciones de enrutamiento." }, + "secure_key_source": { + "description": "Selecciona c\u00f3mo deseas configurar KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Utilizar un archivo `.knxkeys` que contenga claves seguras de IP", + "secure_routing_manual": "Configurar la clave de red troncal segura de IP manualmente", + "secure_tunnel_manual": "Configurar las credenciales seguras de IP manualmente" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "El nombre de tu archivo `.knxkeys` (incluyendo la extensi\u00f3n)", @@ -161,6 +192,17 @@ }, "description": "Por favor, introduce la informaci\u00f3n de tu archivo `.knxkeys`." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Clave de la red troncal", + "sync_latency_tolerance": "Tolerancia a la latencia de red" + }, + "data_description": { + "backbone_key": "Se puede ver en el informe de 'Seguridad' de un proyecto ETS. Ej. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "El valor predeterminado es 1000." + }, + "description": "Por favor, introduce tu informaci\u00f3n de IP segura." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Contrase\u00f1a de autenticaci\u00f3n del dispositivo", diff --git a/homeassistant/components/knx/translations/et.json b/homeassistant/components/knx/translations/et.json index 7693a38106d..1efd61e02ab 100644 --- a/homeassistant/components/knx/translations/et.json +++ b/homeassistant/components/knx/translations/et.json @@ -7,6 +7,7 @@ "error": { "cannot_connect": "\u00dchendamine nurjus", "file_not_found": "M\u00e4\u00e4ratud faili \".knxkeys\" ei leitud asukohas config/.storage/knx/", + "invalid_backbone_key": "Kehtetu magistraalv\u00f5ti. Eeldatakse 32 kuueteistk\u00fcmnendarvu.", "invalid_individual_address": "V\u00e4\u00e4rtus ei \u00fchti KNX-i individuaalse aadressi mustriga.\n 'area.line.device'", "invalid_ip_address": "Kehtetu IPv4 aadress.", "invalid_signature": "Parool faili `.knxkeys` dekr\u00fcpteerimiseks on vale.", @@ -41,7 +42,8 @@ "individual_address": "Individuaalne aadress", "local_ip": "Home Assistanti kohalik IP aadress", "multicast_group": "Multicast grupp", - "multicast_port": "Mulicasti port" + "multicast_port": "Mulicasti port", + "routing_secure": "Kasuta KNX IP Secure'i" }, "data_description": { "individual_address": "Home Assistantis kasutatav KNX-aadress, nt \"0.0.4\".", @@ -49,6 +51,14 @@ }, "description": "Konfigureeri marsruutimissuvandid." }, + "secure_key_source": { + "description": "Vali kuidas soovid KNX/IP Secure'i seadistada.", + "menu_options": { + "secure_knxkeys": "Kasuta knxkeys faili mis sisaldab IP Secure teavet.", + "secure_routing_manual": "Seadista IP secure magistraalv\u00f5ti k\u00e4sitsi", + "secure_tunnel_manual": "Seadista IP secure mandaadid k\u00e4sitsi" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "`.nxkeys` faili t\u00e4ielik nimi (koos laiendiga)", @@ -60,6 +70,17 @@ }, "description": "Sisesta oma `.knxkeys` faili teave." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Backbone v\u00f5ti", + "sync_latency_tolerance": "V\u00f5rguviivituse taluvus" + }, + "data_description": { + "backbone_key": "Kuvatakse ETS projekti 'Turvalisus' vaates. N\u00e4iteks '0011223344...'", + "sync_latency_tolerance": "Vaikev\u00e4\u00e4rtus on 1000." + }, + "description": "Sisesta IP Secure teave." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Seadme autentimise parool", @@ -92,6 +113,7 @@ "error": { "cannot_connect": "\u00dchendamine nurjus", "file_not_found": "M\u00e4\u00e4ratud kirjet '.knxkeys' ei leitud asukohast config/.storage/knx/", + "invalid_backbone_key": "Kehtetu magistraalv\u00f5ti. Eeldatakse 32 kuueteistk\u00fcmnendarvu.", "invalid_individual_address": "V\u00e4\u00e4rtuse mall ei vasta KNX seadme \u00fcksuse aadressile.\n'area.line.device'", "invalid_ip_address": "Vigane IPv4 aadress", "invalid_signature": "'.knxkeys' kirje dekr\u00fcptimisv\u00f5ti on vale.", @@ -142,7 +164,8 @@ "individual_address": "\u00dcksuse aadress", "local_ip": "Home Assistati kohtv\u00f5rgu IP aadress", "multicast_group": "Multicasti grupp", - "multicast_port": "Multicasti port" + "multicast_port": "Multicasti port", + "routing_secure": "Kasuta KNX IP Secure'i" }, "data_description": { "individual_address": "Home Assistantis kasutatav KNX aadress, n\u00e4iteks '0.0.4''", @@ -150,6 +173,14 @@ }, "description": "Seadista marsruutimine" }, + "secure_key_source": { + "description": "Vali kuidas soovid KNX/IP Secure'i seadistada.", + "menu_options": { + "secure_knxkeys": "Kasuta knxkeys faili mis sisaldab IP Secure teavet.", + "secure_routing_manual": "Seadista IP secure magistraalv\u00f5ti k\u00e4sitsi", + "secure_tunnel_manual": "Seadista IP secure mandaadid k\u00e4sitsi" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "'.knxkeys' kirje nimi (koos laiendiga)", @@ -161,6 +192,17 @@ }, "description": "Sisesta oma '.knxkeys' kirje teave" }, + "secure_routing_manual": { + "data": { + "backbone_key": "Backbone v\u00f5ti", + "sync_latency_tolerance": "V\u00f5rguviivituse taluvus" + }, + "data_description": { + "backbone_key": "Kuvatakse ETS projekti 'Turvalisus' vaates. N\u00e4iteks '0011223344...'", + "sync_latency_tolerance": "Vaikev\u00e4\u00e4rtus on 1000." + }, + "description": "Sisesta IP Secure teave." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Seadme tuvastamise salas\u00f5na", diff --git a/homeassistant/components/knx/translations/fr.json b/homeassistant/components/knx/translations/fr.json index 4ca70107530..e4bd4d4b059 100644 --- a/homeassistant/components/knx/translations/fr.json +++ b/homeassistant/components/knx/translations/fr.json @@ -55,6 +55,9 @@ }, "description": "Veuillez saisir les informations relatives \u00e0 votre fichier `.knxkeys`." }, + "secure_routing_manual": { + "description": "Veuillez saisir vos informations de s\u00e9curit\u00e9 IP." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Mot de passe d\u2019authentification de l\u2019appareil", @@ -141,6 +144,9 @@ }, "description": "Veuillez saisir les informations relatives \u00e0 votre fichier `.knxkeys`." }, + "secure_routing_manual": { + "description": "Veuillez saisir vos informations de s\u00e9curit\u00e9 IP." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Mot de passe d\u2019authentification de l\u2019appareil", diff --git a/homeassistant/components/knx/translations/id.json b/homeassistant/components/knx/translations/id.json index 012a027154a..4e4f17f70bd 100644 --- a/homeassistant/components/knx/translations/id.json +++ b/homeassistant/components/knx/translations/id.json @@ -7,6 +7,7 @@ "error": { "cannot_connect": "Gagal terhubung", "file_not_found": "File `.knxkeys` yang ditentukan tidak ditemukan di jalur config/.storage/knx/", + "invalid_backbone_key": "Kunci backbone tidak valid. Diharapkan 32 angka heksadesimal.", "invalid_individual_address": "Nilai tidak cocok dengan pola untuk alamat individual KNX.\n'area.line.device'", "invalid_ip_address": "Alamat IPv4 tidak valid", "invalid_signature": "Kata sandi untuk mendekripsi file `.knxkeys` salah.", @@ -41,7 +42,8 @@ "individual_address": "Alamat individual", "local_ip": "IP lokal Home Assistant", "multicast_group": "Grup multicast", - "multicast_port": "Port multicast" + "multicast_port": "Port multicast", + "routing_secure": "Gunakan KNX IP Secure" }, "data_description": { "individual_address": "Alamat KNX yang akan digunakan oleh Home Assistant, misalnya `0.0.4`", @@ -49,6 +51,14 @@ }, "description": "Konfigurasikan opsi routing." }, + "secure_key_source": { + "description": "Pilih cara Anda ingin mengonfigurasi KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Gunakan file `.knxkeys` yang berisi kunci aman IP", + "secure_routing_manual": "Konfigurasikan kunci backbone aman IP secara manual", + "secure_tunnel_manual": "Konfigurasikan kredensial aman IP secara manual" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "Nama file `.knxkeys` Anda (termasuk ekstensi)", @@ -60,6 +70,17 @@ }, "description": "Masukkan informasi untuk file `.knxkeys` Anda." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Kunci backbone", + "sync_latency_tolerance": "Toleransi latensi jaringan" + }, + "data_description": { + "backbone_key": "Dapat dilihat dalam laporan 'Security' dari proyek ETS. Mis. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Bawaannya bernilai 1000." + }, + "description": "Masukkan informasi IP aman Anda." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Kata sandi autentikasi perangkat", @@ -92,6 +113,7 @@ "error": { "cannot_connect": "Gagal terhubung", "file_not_found": "File `.knxkeys` yang ditentukan tidak ditemukan di jalur config/.storage/knx/", + "invalid_backbone_key": "Kunci backbone tidak valid. Diharapkan 32 angka heksadesimal.", "invalid_individual_address": "Nilai tidak cocok dengan pola untuk alamat individual KNX.\n'area.line.device'", "invalid_ip_address": "Alamat IPv4 tidak valid", "invalid_signature": "Kata sandi untuk mendekripsi file `.knxkeys` salah.", @@ -142,7 +164,8 @@ "individual_address": "Alamat individual", "local_ip": "IP lokal Home Assistant", "multicast_group": "Grup multicast", - "multicast_port": "Port multicast" + "multicast_port": "Port multicast", + "routing_secure": "Gunakan KNX IP Secure" }, "data_description": { "individual_address": "Alamat KNX yang akan digunakan oleh Home Assistant, misalnya `0.0.4`", @@ -150,6 +173,14 @@ }, "description": "Konfigurasikan opsi routing." }, + "secure_key_source": { + "description": "Pilih cara Anda ingin mengonfigurasi KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Gunakan file `.knxkeys` yang berisi kunci aman IP", + "secure_routing_manual": "Konfigurasikan kunci backbone aman IP secara manual", + "secure_tunnel_manual": "Konfigurasikan kredensial aman IP secara manual" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "Nama file `.knxkeys` Anda (termasuk ekstensi)", @@ -161,6 +192,17 @@ }, "description": "Masukkan informasi untuk file `.knxkeys` Anda." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Kunci backbone", + "sync_latency_tolerance": "Toleransi latensi jaringan" + }, + "data_description": { + "backbone_key": "Dapat dilihat dalam laporan 'Security' dari proyek ETS. Mis. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Bawaannya bernilai 1000." + }, + "description": "Masukkan informasi IP aman Anda." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Kata sandi autentikasi perangkat", diff --git a/homeassistant/components/knx/translations/no.json b/homeassistant/components/knx/translations/no.json index 361595394f1..8ddccdf25a3 100644 --- a/homeassistant/components/knx/translations/no.json +++ b/homeassistant/components/knx/translations/no.json @@ -7,6 +7,7 @@ "error": { "cannot_connect": "Tilkobling mislyktes", "file_not_found": "Den angitte `.knxkeys`-filen ble ikke funnet i banen config/.storage/knx/", + "invalid_backbone_key": "Ugyldig ryggradsn\u00f8kkel. 32 heksadesimale tall forventet.", "invalid_individual_address": "Verdien samsvarer ikke med m\u00f8nsteret for individuelle KNX-adresser.\n 'area.line.device'", "invalid_ip_address": "Ugyldig IPv4-adresse.", "invalid_signature": "Passordet for \u00e5 dekryptere `.knxkeys`-filen er feil.", @@ -41,7 +42,8 @@ "individual_address": "Individuell adresse", "local_ip": "Lokal IP for hjemmeassistent", "multicast_group": "Multicast gruppe", - "multicast_port": "Multicast port" + "multicast_port": "Multicast port", + "routing_secure": "Bruk KNX IP Secure" }, "data_description": { "individual_address": "KNX-adresse som skal brukes av Home Assistant, f.eks. `0.0.4`", @@ -49,6 +51,14 @@ }, "description": "Vennligst konfigurer rutealternativene." }, + "secure_key_source": { + "description": "Velg hvordan du vil konfigurere KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Bruk en `.knxkeys`-fil som inneholder sikre IP-n\u00f8kler", + "secure_routing_manual": "Konfigurer IP sikker ryggradsn\u00f8kkel manuelt", + "secure_tunnel_manual": "Konfigurer IP sikker legitimasjon manuelt" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "Filnavnet til `.knxkeys`-filen (inkludert utvidelse)", @@ -60,6 +70,17 @@ }, "description": "Vennligst skriv inn informasjonen for `.knxkeys`-filen." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Ryggraden n\u00f8kkel", + "sync_latency_tolerance": "Toleranse for nettverksventetid" + }, + "data_description": { + "backbone_key": "Kan sees i 'Sikkerhet'-rapporten til et ETS-prosjekt. F.eks. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Standard er 1000." + }, + "description": "Vennligst skriv inn din sikre IP-informasjon." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Enhetsautentiseringspassord", @@ -92,6 +113,7 @@ "error": { "cannot_connect": "Tilkobling mislyktes", "file_not_found": "Den angitte `.knxkeys`-filen ble ikke funnet i banen config/.storage/knx/", + "invalid_backbone_key": "Ugyldig ryggradsn\u00f8kkel. 32 heksadesimale tall forventet.", "invalid_individual_address": "Verdien samsvarer ikke med m\u00f8nsteret for individuelle KNX-adresser.\n 'area.line.device'", "invalid_ip_address": "Ugyldig IPv4-adresse.", "invalid_signature": "Passordet for \u00e5 dekryptere `.knxkeys`-filen er feil.", @@ -142,7 +164,8 @@ "individual_address": "Individuell adresse", "local_ip": "Lokal IP for hjemmeassistent", "multicast_group": "Multicast gruppe", - "multicast_port": "Multicast port" + "multicast_port": "Multicast port", + "routing_secure": "Bruk KNX IP Secure" }, "data_description": { "individual_address": "KNX-adresse som skal brukes av Home Assistant, f.eks. `0.0.4`", @@ -150,6 +173,14 @@ }, "description": "Vennligst konfigurer rutealternativene." }, + "secure_key_source": { + "description": "Velg hvordan du vil konfigurere KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Bruk en `.knxkeys`-fil som inneholder sikre IP-n\u00f8kler", + "secure_routing_manual": "Konfigurer IP sikker ryggradsn\u00f8kkel manuelt", + "secure_tunnel_manual": "Konfigurer IP sikker legitimasjon manuelt" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "Filnavnet til `.knxkeys`-filen (inkludert utvidelse)", @@ -161,6 +192,17 @@ }, "description": "Vennligst skriv inn informasjonen for `.knxkeys`-filen." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Ryggraden n\u00f8kkel", + "sync_latency_tolerance": "Toleranse for nettverksventetid" + }, + "data_description": { + "backbone_key": "Kan sees i 'Sikkerhet'-rapporten til et ETS-prosjekt. F.eks. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Standard er 1000." + }, + "description": "Vennligst skriv inn din sikre IP-informasjon." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Enhetsautentiseringspassord", diff --git a/homeassistant/components/knx/translations/ru.json b/homeassistant/components/knx/translations/ru.json index 406c9b70a1f..a8c9ba00ed2 100644 --- a/homeassistant/components/knx/translations/ru.json +++ b/homeassistant/components/knx/translations/ru.json @@ -7,6 +7,7 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "file_not_found": "\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b `.knxkeys` \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u0432 config/.storage/knx/", + "invalid_backbone_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 backbone. \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f 32 \u0448\u0435\u0441\u0442\u043d\u0430\u0434\u0446\u0430\u0442\u0435\u0440\u0438\u0447\u043d\u044b\u0445 \u0447\u0438\u0441\u043b\u0430.", "invalid_individual_address": "\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d\u0443 \u0434\u043b\u044f \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0430\u0434\u0440\u0435\u0441\u0430 KNX 'area.line.device'.", "invalid_ip_address": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 IPv4.", "invalid_signature": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 `.knxkeys`.", @@ -41,7 +42,8 @@ "individual_address": "\u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441", "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant", "multicast_group": "\u0413\u0440\u0443\u043f\u043f\u0430 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438", - "multicast_port": "\u041f\u043e\u0440\u0442 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438" + "multicast_port": "\u041f\u043e\u0440\u0442 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438", + "routing_secure": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c KNX IP Secure" }, "data_description": { "individual_address": "\u0410\u0434\u0440\u0435\u0441 KNX, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f Home Assistant, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, `0.0.4`", @@ -49,6 +51,14 @@ }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438." }, + "secure_key_source": { + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b `.knxkeys`, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043a\u043b\u044e\u0447\u0438 IP secure", + "secure_routing_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c backbone-\u043a\u043b\u044e\u0447\u0438 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e", + "secure_tunnel_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0443\u0447\u0451\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "\u0418\u043c\u044f \u0444\u0430\u0439\u043b\u0430 `.knxkeys` (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435)", @@ -60,6 +70,17 @@ }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0444\u0430\u0439\u043b\u0435 `.knxkeys`." }, + "secure_routing_manual": { + "data": { + "backbone_key": "\u041a\u043b\u044e\u0447 backbone", + "sync_latency_tolerance": "\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u0430\u044f \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u0441\u0435\u0442\u0438" + }, + "data_description": { + "backbone_key": "\u041c\u043e\u0436\u043d\u043e \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0432 \u043e\u0442\u0447\u0435\u0442\u0435 'Security' \u043f\u0440\u043e\u0435\u043a\u0442\u0430 ETS. Eg. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e - 1000." + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e IP Secure." + }, "secure_tunnel_manual": { "data": { "device_authentication": "\u041f\u0430\u0440\u043e\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", @@ -92,6 +113,7 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "file_not_found": "\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b `.knxkeys` \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u0432 config/.storage/knx/", + "invalid_backbone_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 backbone. \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f 32 \u0448\u0435\u0441\u0442\u043d\u0430\u0434\u0446\u0430\u0442\u0435\u0440\u0438\u0447\u043d\u044b\u0445 \u0447\u0438\u0441\u043b\u0430.", "invalid_individual_address": "\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d\u0443 \u0434\u043b\u044f \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0430\u0434\u0440\u0435\u0441\u0430 KNX 'area.line.device'.", "invalid_ip_address": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 IPv4.", "invalid_signature": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 `.knxkeys`.", @@ -142,7 +164,8 @@ "individual_address": "\u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441", "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant", "multicast_group": "\u0413\u0440\u0443\u043f\u043f\u0430 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438", - "multicast_port": "\u041f\u043e\u0440\u0442 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438" + "multicast_port": "\u041f\u043e\u0440\u0442 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438", + "routing_secure": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c KNX IP Secure" }, "data_description": { "individual_address": "\u0410\u0434\u0440\u0435\u0441 KNX, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f Home Assistant, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, `0.0.4`", @@ -150,6 +173,14 @@ }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438." }, + "secure_key_source": { + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b `.knxkeys`, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043a\u043b\u044e\u0447\u0438 IP secure", + "secure_routing_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c backbone-\u043a\u043b\u044e\u0447\u0438 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e", + "secure_tunnel_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0443\u0447\u0451\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "\u0418\u043c\u044f \u0444\u0430\u0439\u043b\u0430 `.knxkeys` (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435)", @@ -161,6 +192,17 @@ }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0444\u0430\u0439\u043b\u0435 `.knxkeys`." }, + "secure_routing_manual": { + "data": { + "backbone_key": "\u041a\u043b\u044e\u0447 backbone", + "sync_latency_tolerance": "\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u0430\u044f \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u0441\u0435\u0442\u0438" + }, + "data_description": { + "backbone_key": "\u041c\u043e\u0436\u043d\u043e \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0432 \u043e\u0442\u0447\u0435\u0442\u0435 'Security' \u043f\u0440\u043e\u0435\u043a\u0442\u0430 ETS. Eg. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e - 1000." + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e IP Secure." + }, "secure_tunnel_manual": { "data": { "device_authentication": "\u041f\u0430\u0440\u043e\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", diff --git a/homeassistant/components/knx/translations/sk.json b/homeassistant/components/knx/translations/sk.json index 10cf86d539a..b1fa9902a2f 100644 --- a/homeassistant/components/knx/translations/sk.json +++ b/homeassistant/components/knx/translations/sk.json @@ -1,39 +1,9 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, - "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_ip_address": "Neplatn\u00e1 adresa IPv4." - }, - "step": { - "manual_tunnel": { - "data": { - "host": "Hostite\u013e", - "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a", - "port": "Port" - } - }, - "routing": { - "data": { - "individual_address": "Individu\u00e1lna adresa", - "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" - }, - "data_description": { - "individual_address": "Adresa KNX, ktor\u00fa bude pou\u017e\u00edva\u0165 Home Assistant, napr. `0.0.4`" - } - }, - "secure_tunnel_manual": { - "data": { - "device_authentication": "Heslo na overenie zariadenia", - "user_id": "ID pou\u017e\u00edvate\u013ea", - "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" - } - } - } - }, - "options": { "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_ip_address": "Neplatn\u00e1 adresa IPv4." @@ -57,15 +27,108 @@ "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" }, "data_description": { - "individual_address": "Adresa KNX, ktor\u00fa bude pou\u017e\u00edva\u0165 Home Assistant, napr. `0.0.4`" + "individual_address": "Adresa KNX, ktor\u00fa bude pou\u017e\u00edva\u0165 Home Assistant, napr. `0.0.4`", + "local_ip": "Ak chcete pou\u017ei\u0165 automatick\u00e9 zis\u0165ovanie, nechajte pole pr\u00e1zdne." + }, + "description": "Nakonfigurujte mo\u017enosti smerovania." + }, + "secure_key_source": { + "description": "Vyberte, ako chcete nakonfigurova\u0165 KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Pou\u017eite s\u00fabor `.knxkeys` obsahuj\u00faci bezpe\u010dnostn\u00e9 k\u013e\u00fa\u010de IP", + "secure_routing_manual": "Nakonfigurujte zabezpe\u010den\u00fd k\u013e\u00fa\u010d chrbticovej siete IP manu\u00e1lne", + "secure_tunnel_manual": "Manu\u00e1lne nakonfigurujte bezpe\u010dnostn\u00e9 poverenia IP" } }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "N\u00e1zov s\u00faboru v\u00e1\u0161ho s\u00faboru `.knxkeys` (vr\u00e1tane pr\u00edpony)", + "knxkeys_password": "Heslo na de\u0161ifrovanie s\u00faboru `.knxkeys`" + } + }, + "secure_routing_manual": { + "data_description": { + "sync_latency_tolerance": "Predvolen\u00e1 hodnota je 1000." + }, + "description": "Pros\u00edm, zadajte svoje IP zabezpe\u010den\u00e9 inform\u00e1cie." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Heslo na overenie zariadenia", "user_id": "ID pou\u017e\u00edvate\u013ea", "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" + }, + "description": "Pros\u00edm, zadajte svoje IP zabezpe\u010den\u00e9 inform\u00e1cie." + } + } + }, + "options": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_ip_address": "Neplatn\u00e1 adresa IPv4." + }, + "step": { + "communication_settings": { + "data_description": { + "rate_limit": "Maxim\u00e1lny po\u010det odch\u00e1dzaj\u00facich telegramov za sekundu.\n `0` pre deaktiv\u00e1ciu limitu. Odpor\u00fa\u010dan\u00e9: 0 alebo 20 a\u017e 40" } + }, + "connection_type": { + "data": { + "connection_type": "Typ pripojenia KNX" + } + }, + "manual_tunnel": { + "data": { + "host": "Hostite\u013e", + "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a", + "port": "Port" + } + }, + "options_init": { + "menu_options": { + "communication_settings": "Nastavenia komunik\u00e1cie", + "connection_type": "Konfigur\u00e1cia rozhrania KNX" + } + }, + "routing": { + "data": { + "individual_address": "Individu\u00e1lna adresa", + "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" + }, + "data_description": { + "individual_address": "Adresa KNX, ktor\u00fa bude pou\u017e\u00edva\u0165 Home Assistant, napr. `0.0.4`", + "local_ip": "Ak chcete pou\u017ei\u0165 automatick\u00e9 zis\u0165ovanie, nechajte pole pr\u00e1zdne." + }, + "description": "Nakonfigurujte mo\u017enosti smerovania." + }, + "secure_key_source": { + "description": "Vyberte, ako chcete nakonfigurova\u0165 KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Pou\u017eite s\u00fabor `.knxkeys` obsahuj\u00faci bezpe\u010dnostn\u00e9 k\u013e\u00fa\u010de IP", + "secure_routing_manual": "Nakonfigurujte zabezpe\u010den\u00fd k\u013e\u00fa\u010d chrbticovej siete IP manu\u00e1lne", + "secure_tunnel_manual": "Manu\u00e1lne nakonfigurujte bezpe\u010dnostn\u00e9 poverenia IP" + } + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "N\u00e1zov s\u00faboru v\u00e1\u0161ho s\u00faboru `.knxkeys` (vr\u00e1tane pr\u00edpony)", + "knxkeys_password": "Heslo na de\u0161ifrovanie s\u00faboru `.knxkeys`" + } + }, + "secure_routing_manual": { + "data_description": { + "sync_latency_tolerance": "Predvolen\u00e1 hodnota je 1000." + }, + "description": "Pros\u00edm, zadajte svoje IP zabezpe\u010den\u00e9 inform\u00e1cie." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "Heslo na overenie zariadenia", + "user_id": "ID pou\u017e\u00edvate\u013ea", + "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" + }, + "description": "Pros\u00edm, zadajte svoje IP zabezpe\u010den\u00e9 inform\u00e1cie." } } } diff --git a/homeassistant/components/knx/translations/zh-Hant.json b/homeassistant/components/knx/translations/zh-Hant.json index 0d5f3a41c4a..bd11d7f78a0 100644 --- a/homeassistant/components/knx/translations/zh-Hant.json +++ b/homeassistant/components/knx/translations/zh-Hant.json @@ -7,6 +7,7 @@ "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", "file_not_found": "\u8def\u5f91 config/.storage/knx/ \u5167\u627e\u4e0d\u5230\u6307\u5b9a `.knxkeys` \u6a94\u6848", + "invalid_backbone_key": "Backbone \u91d1\u9470\u7121\u6548\u3002\u61c9\u70ba 32 \u500b\u5341\u516d\u9032\u4f4d\u6578\u5b57\u3002", "invalid_individual_address": "\u6578\u503c\u8207 KNX \u500b\u5225\u4f4d\u5740\u4e0d\u76f8\u7b26\u3002\n'area.line.device'", "invalid_ip_address": "IPv4 \u4f4d\u5740\u7121\u6548\u3002", "invalid_signature": "\u52a0\u5bc6 `.knxkeys` \u6a94\u6848\u5bc6\u78bc\u932f\u8aa4\u3002", @@ -41,7 +42,8 @@ "individual_address": "\u500b\u5225\u4f4d\u5740", "local_ip": "Home Assistant \u672c\u5730\u7aef IP", "multicast_group": "Multicast \u7fa4\u7d44", - "multicast_port": "Multicast \u901a\u8a0a\u57e0" + "multicast_port": "Multicast \u901a\u8a0a\u57e0", + "routing_secure": "\u4f7f\u7528 KNX IP \u52a0\u5bc6" }, "data_description": { "individual_address": "Home Assistant \u6240\u4f7f\u7528\u4e4b KNX \u4f4d\u5740\u3002\u4f8b\u5982\uff1a`0.0.4`", @@ -49,6 +51,14 @@ }, "description": "\u8acb\u8a2d\u5b9a\u8def\u7531\u9078\u9805\u3002" }, + "secure_key_source": { + "description": "\u9078\u64c7\u5982\u4f55\u8a2d\u5b9a KNX/IP \u52a0\u5bc6\u3002", + "menu_options": { + "secure_knxkeys": "\u4f7f\u7528\u5305\u542b IP \u52a0\u5bc6\u91d1\u8000\u7684 knxkeys \u6a94\u6848", + "secure_routing_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6 backbone \u91d1\u9470", + "secure_tunnel_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6\u6191\u8b49" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "`.knxkeys` \u6a94\u6848\u5168\u540d\uff08\u5305\u542b\u526f\u6a94\u540d\uff09", @@ -60,6 +70,17 @@ }, "description": "\u8acb\u8f38\u5165 `.knxkeys` \u6a94\u6848\u8cc7\u8a0a\u3002" }, + "secure_routing_manual": { + "data": { + "backbone_key": "Backbone \u91d1\u9470", + "sync_latency_tolerance": "\u7db2\u8def\u5ef6\u9072\u5bb9\u5fcd\u5ea6" + }, + "data_description": { + "backbone_key": "\u65bc ETS \u9805\u76ee\u7684 'Security' \u56de\u5831\u4e2d\u767c\u73fe\u3002\u4f8b\u5982\uff1a'00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "\u9810\u8a2d\u503c\u70ba 1000\u3002" + }, + "description": "\u8acb\u8f38\u5165 IP \u52a0\u5bc6\u8cc7\u8a0a\u3002" + }, "secure_tunnel_manual": { "data": { "device_authentication": "\u88dd\u7f6e\u8a8d\u8b49\u5bc6\u78bc", @@ -92,6 +113,7 @@ "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", "file_not_found": "\u8def\u5f91 config/.storage/knx/ \u5167\u627e\u4e0d\u5230\u6307\u5b9a `.knxkeys` \u6a94\u6848", + "invalid_backbone_key": "Backbone \u91d1\u9470\u7121\u6548\u3002\u61c9\u70ba 32 \u500b\u5341\u516d\u9032\u4f4d\u6578\u5b57\u3002", "invalid_individual_address": "\u6578\u503c\u8207 KNX \u500b\u5225\u4f4d\u5740\u4e0d\u76f8\u7b26\u3002\n'area.line.device'", "invalid_ip_address": "IPv4 \u4f4d\u5740\u7121\u6548\u3002", "invalid_signature": "\u52a0\u5bc6 `.knxkeys` \u6a94\u6848\u5bc6\u78bc\u932f\u8aa4\u3002", @@ -142,7 +164,8 @@ "individual_address": "\u500b\u5225\u4f4d\u5740", "local_ip": "Home Assistant \u672c\u5730\u7aef IP", "multicast_group": "Multicast \u7fa4\u7d44", - "multicast_port": "Multicast \u901a\u8a0a\u57e0" + "multicast_port": "Multicast \u901a\u8a0a\u57e0", + "routing_secure": "\u4f7f\u7528 KNX IP \u52a0\u5bc6" }, "data_description": { "individual_address": "Home Assistant \u6240\u4f7f\u7528\u4e4b KNX \u4f4d\u5740\u3002\u4f8b\u5982\uff1a`0.0.4`", @@ -150,6 +173,14 @@ }, "description": "\u8acb\u8a2d\u5b9a\u8def\u7531\u9078\u9805\u3002" }, + "secure_key_source": { + "description": "\u9078\u64c7\u5982\u4f55\u8a2d\u5b9a KNX/IP \u52a0\u5bc6\u3002", + "menu_options": { + "secure_knxkeys": "\u4f7f\u7528\u5305\u542b IP \u52a0\u5bc6\u91d1\u8000\u7684 knxkeys \u6a94\u6848", + "secure_routing_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6 backbone \u91d1\u9470", + "secure_tunnel_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6\u6191\u8b49" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "`.knxkeys` \u6a94\u6848\u5168\u540d\uff08\u5305\u542b\u526f\u6a94\u540d\uff09", @@ -161,6 +192,17 @@ }, "description": "\u8acb\u8f38\u5165 `.knxkeys` \u6a94\u6848\u8cc7\u8a0a\u3002" }, + "secure_routing_manual": { + "data": { + "backbone_key": "Backbone \u91d1\u9470", + "sync_latency_tolerance": "\u7db2\u8def\u5ef6\u9072\u5bb9\u5fcd\u5ea6" + }, + "data_description": { + "backbone_key": "\u65bc ETS \u9805\u76ee\u7684 'Security' \u56de\u5831\u4e2d\u767c\u73fe\u3002\u4f8b\u5982\uff1a'00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "\u9810\u8a2d\u503c\u70ba 1000\u3002" + }, + "description": "\u8acb\u8f38\u5165 IP \u52a0\u5bc6\u8cc7\u8a0a\u3002" + }, "secure_tunnel_manual": { "data": { "device_authentication": "\u88dd\u7f6e\u8a8d\u8b49\u5bc6\u78bc", diff --git a/homeassistant/components/kodi/translations/sk.json b/homeassistant/components/kodi/translations/sk.json index 534dd4ed347..be6f0128d39 100644 --- a/homeassistant/components/kodi/translations/sk.json +++ b/homeassistant/components/kodi/translations/sk.json @@ -19,10 +19,14 @@ "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, + "discovery_confirm": { + "title": "Objaven\u00e9 Kodi" + }, "user": { "data": { "host": "Hostite\u013e", - "port": "Port" + "port": "Port", + "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t" } }, "ws_port": { @@ -31,5 +35,11 @@ } } } + }, + "device_automation": { + "trigger_type": { + "turn_off": "{entity_name} bola po\u017eiadan\u00e1 o vypnutie", + "turn_on": "{entity_name} bola po\u017eiadan\u00e1 o zapnutie" + } } } \ No newline at end of file diff --git a/homeassistant/components/konnected/translations/sk.json b/homeassistant/components/konnected/translations/sk.json index 720e0991b48..d84d42532f9 100644 --- a/homeassistant/components/konnected/translations/sk.json +++ b/homeassistant/components/konnected/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "not_konn_panel": "Nie je rozpoznan\u00e9 zariadenie Konnected.io", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { @@ -22,12 +23,16 @@ } }, "options": { + "abort": { + "not_konn_panel": "Nie je rozpoznan\u00e9 zariadenie Konnected.io" + }, "error": { "bad_host": "Neplatn\u00e1 adresa URL hostite\u013ea API Override" }, "step": { "options_binary": { "data": { + "inverse": "Invertujte stav otvoren\u00e9/zatvoren\u00e9", "name": "N\u00e1zov (volite\u013en\u00fd)", "type": "Typ sn\u00edma\u010da" }, @@ -41,6 +46,18 @@ }, "description": "mo\u017enosti {zone}" }, + "options_io": { + "data": { + "1": "Z\u00f3na 1", + "2": "Z\u00f3na 2", + "3": "Z\u00f3na 3", + "4": "Z\u00f3na 4", + "5": "Z\u00f3na 5", + "6": "Z\u00f3na 6", + "7": "Z\u00f3na 7" + }, + "title": "Konfigur\u00e1cia I/O" + }, "options_misc": { "data": { "api_host": "Prep\u00edsanie adresy URL hostite\u013ea API" diff --git a/homeassistant/components/kulersky/translations/sk.json b/homeassistant/components/kulersky/translations/sk.json new file mode 100644 index 00000000000..d4bb209c34c --- /dev/null +++ b/homeassistant/components/kulersky/translations/sk.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "confirm": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lacrosse_view/translations/sk.json b/homeassistant/components/lacrosse_view/translations/sk.json index 12787c93e41..af696ee1cfe 100644 --- a/homeassistant/components/lacrosse_view/translations/sk.json +++ b/homeassistant/components/lacrosse_view/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "invalid_auth": "Neplatn\u00e9 overenie", @@ -11,7 +12,8 @@ "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/lametric/translations/sk.json b/homeassistant/components/lametric/translations/sk.json index 35890aecb26..e2a8b9c5051 100644 --- a/homeassistant/components/lametric/translations/sk.json +++ b/homeassistant/components/lametric/translations/sk.json @@ -2,8 +2,11 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", "invalid_discovery_info": "Prijat\u00e9 neplatn\u00e9 inform\u00e1cie o zis\u0165ovan\u00ed", "link_local_address": "Lok\u00e1lne adresy odkazov nie s\u00fa podporovan\u00e9", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { @@ -11,11 +14,19 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "choice_enter_manual_or_fetch_cloud": { + "menu_options": { + "manual_entry": "Zadajte manu\u00e1lne" + } + }, "manual_entry": { "data": { "api_key": "API k\u013e\u00fa\u010d", "host": "Hostite\u013e" } + }, + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" } } } diff --git a/homeassistant/components/landisgyr_heat_meter/translations/sk.json b/homeassistant/components/landisgyr_heat_meter/translations/sk.json index 019769fcac4..86de76ef980 100644 --- a/homeassistant/components/landisgyr_heat_meter/translations/sk.json +++ b/homeassistant/components/landisgyr_heat_meter/translations/sk.json @@ -4,6 +4,11 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "step": { + "setup_serial_manual_path": { + "data": { + "device": "Cesta k zariadeniu USB" + } + }, "user": { "data": { "device": "Vyberte zariadenie" diff --git a/homeassistant/components/launch_library/translations/sk.json b/homeassistant/components/launch_library/translations/sk.json new file mode 100644 index 00000000000..c294bc45d7c --- /dev/null +++ b/homeassistant/components/launch_library/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/sk.json b/homeassistant/components/laundrify/translations/sk.json index 50419cf7edf..f8f52f3f99d 100644 --- a/homeassistant/components/laundrify/translations/sk.json +++ b/homeassistant/components/laundrify/translations/sk.json @@ -1,9 +1,23 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", - "invalid_format": "Neplatn\u00fd form\u00e1t. Zadajte ako xxx-xxx." + "invalid_format": "Neplatn\u00fd form\u00e1t. Zadajte ako xxx-xxx.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "init": { + "data": { + "code": "Autoriza\u010dn\u00fd k\u00f3d (xxx-xxx)" + } + }, + "reauth_confirm": { + "title": "Znova overi\u0165 integr\u00e1ciu" + } } } } \ No newline at end of file diff --git a/homeassistant/components/lcn/translations/sk.json b/homeassistant/components/lcn/translations/sk.json new file mode 100644 index 00000000000..41b4464c18f --- /dev/null +++ b/homeassistant/components/lcn/translations/sk.json @@ -0,0 +1,9 @@ +{ + "device_automation": { + "trigger_type": { + "codelock": "prijat\u00fd k\u00f3d z\u00e1mku", + "fingerprint": "prijat\u00fd k\u00f3d odtla\u010dku prsta", + "send_keys": "odosla\u0165 prijat\u00e9 k\u013e\u00fa\u010de" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/led_ble/translations/sk.json b/homeassistant/components/led_ble/translations/sk.json index afb81ec3123..994e95cb8cd 100644 --- a/homeassistant/components/led_ble/translations/sk.json +++ b/homeassistant/components/led_ble/translations/sk.json @@ -7,6 +7,10 @@ "no_unconfigured_devices": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia.", "not_supported": "Zariadenie nie je podporovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/sk.json b/homeassistant/components/lg_soundbar/translations/sk.json index 3c3d27a6689..c8e33ffce5e 100644 --- a/homeassistant/components/lg_soundbar/translations/sk.json +++ b/homeassistant/components/lg_soundbar/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/life360/translations/sk.json b/homeassistant/components/life360/translations/sk.json index 47e3b3ae618..da9c32711a1 100644 --- a/homeassistant/components/life360/translations/sk.json +++ b/homeassistant/components/life360/translations/sk.json @@ -2,22 +2,40 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Nakonfigurujte \u00fa\u010det Life360" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "driving_speed": "R\u00fdchlos\u0165 jazdy", + "limit_gps_acc": "Obmedzenie presnosti GPS", + "max_gps_accuracy": "Maxim\u00e1lna presnos\u0165 GPS (v metroch)" + }, + "title": "Mo\u017enosti \u00fa\u010dtu" } } } diff --git a/homeassistant/components/lifx/translations/sk.json b/homeassistant/components/lifx/translations/sk.json index 4914a2e64dc..f03ef39a4f3 100644 --- a/homeassistant/components/lifx/translations/sk.json +++ b/homeassistant/components/lifx/translations/sk.json @@ -2,7 +2,11 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "flow_title": "{label} ({host}) {serial}", "step": { diff --git a/homeassistant/components/light/translations/sk.json b/homeassistant/components/light/translations/sk.json index 73f424dd74a..da643696125 100644 --- a/homeassistant/components/light/translations/sk.json +++ b/homeassistant/components/light/translations/sk.json @@ -13,7 +13,9 @@ "is_on": "{entity_name} je zapnut\u00e9" }, "trigger_type": { - "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9" + "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9", + "turned_off": "{entity_name} vypnut\u00e1", + "turned_on": "{entity_name} zapnut\u00e1" } }, "state": { diff --git a/homeassistant/components/litejet/translations/sk.json b/homeassistant/components/litejet/translations/sk.json index 892b8b2cd91..70783638575 100644 --- a/homeassistant/components/litejet/translations/sk.json +++ b/homeassistant/components/litejet/translations/sk.json @@ -1,10 +1,24 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "error": { + "open_failed": "Nie je mo\u017en\u00e9 otvori\u0165 zadan\u00fd s\u00e9riov\u00fd port." + }, "step": { "user": { "data": { "port": "Port" - } + }, + "title": "Pripoji\u0165 sa k LiteJet" + } + } + }, + "options": { + "step": { + "init": { + "title": "Nakonfigurujte LiteJet" } } } diff --git a/homeassistant/components/litterrobot/translations/sensor.sk.json b/homeassistant/components/litterrobot/translations/sensor.sk.json index 1fc7f2128f0..4b9941a9477 100644 --- a/homeassistant/components/litterrobot/translations/sensor.sk.json +++ b/homeassistant/components/litterrobot/translations/sensor.sk.json @@ -1,6 +1,7 @@ { "state": { "litterrobot__status_code": { + "cd": "Zisten\u00e1 ma\u010dka", "off": "Vypnut\u00fd", "offline": "Offline", "p": "Pozastaven\u00fd", diff --git a/homeassistant/components/litterrobot/translations/sk.json b/homeassistant/components/litterrobot/translations/sk.json index db3d1e419d4..3896b31534b 100644 --- a/homeassistant/components/litterrobot/translations/sk.json +++ b/homeassistant/components/litterrobot/translations/sk.json @@ -1,21 +1,26 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { "data": { "password": "Heslo" }, - "description": "Aktualizujte svoje heslo pre {username}" + "description": "Aktualizujte svoje heslo pre {username}", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/locative/translations/sk.json b/homeassistant/components/locative/translations/sk.json index 473cb1c2fa0..04cb32a1c4e 100644 --- a/homeassistant/components/locative/translations/sk.json +++ b/homeassistant/components/locative/translations/sk.json @@ -1,5 +1,10 @@ { "config": { + "abort": { + "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + }, "step": { "user": { "description": "Chcete za\u010da\u0165 nastavova\u0165?" diff --git a/homeassistant/components/logi_circle/translations/sk.json b/homeassistant/components/logi_circle/translations/sk.json index 44e71ddaa41..da1f4892e03 100644 --- a/homeassistant/components/logi_circle/translations/sk.json +++ b/homeassistant/components/logi_circle/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie." }, "error": { "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", diff --git a/homeassistant/components/luftdaten/translations/sk.json b/homeassistant/components/luftdaten/translations/sk.json index 698a3e835fa..b1f2a309ba0 100644 --- a/homeassistant/components/luftdaten/translations/sk.json +++ b/homeassistant/components/luftdaten/translations/sk.json @@ -8,6 +8,7 @@ "step": { "user": { "data": { + "show_on_map": "Zobrazi\u0165 na mape", "station_id": "ID sn\u00edma\u010da" } } diff --git a/homeassistant/components/lutron_caseta/translations/sk.json b/homeassistant/components/lutron_caseta/translations/sk.json index 9aef4795f42..11557dadc82 100644 --- a/homeassistant/components/lutron_caseta/translations/sk.json +++ b/homeassistant/components/lutron_caseta/translations/sk.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "not_lutron_device": "Zisten\u00e9 zariadenie nie je zariadenie Lutron" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165" @@ -13,7 +14,8 @@ "data": { "host": "Hostite\u013e" }, - "description": "Zadajte IP adresu zariadenia." + "description": "Zadajte IP adresu zariadenia.", + "title": "Automaticky sa pripojte k mostu" } } }, @@ -29,6 +31,10 @@ "group_1_button_2": "Prv\u00e1 skupina druh\u00e9 tla\u010didlo", "group_2_button_1": "Druh\u00e1 skupina prv\u00e9 tla\u010didlo", "group_2_button_2": "Druh\u00e1 skupina druh\u00e9 tla\u010didlo" + }, + "trigger_type": { + "press": "\"{subtype}\" stla\u010den\u00e9", + "release": "\u201c{subtype}\u201c uvo\u013enen\u00e9" } } } \ No newline at end of file diff --git a/homeassistant/components/lyric/translations/sk.json b/homeassistant/components/lyric/translations/sk.json index 8862fc534f1..6a514a50f33 100644 --- a/homeassistant/components/lyric/translations/sk.json +++ b/homeassistant/components/lyric/translations/sk.json @@ -2,10 +2,19 @@ "config": { "abort": { "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" + }, + "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + }, + "reauth_confirm": { + "title": "Znova overi\u0165 integr\u00e1ciu" + } } } } \ No newline at end of file diff --git a/homeassistant/components/mailgun/translations/sk.json b/homeassistant/components/mailgun/translations/sk.json new file mode 100644 index 00000000000..27eb9ec5b61 --- /dev/null +++ b/homeassistant/components/mailgun/translations/sk.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + }, + "step": { + "user": { + "description": "Naozaj chcete nastavi\u0165 Mailgun?", + "title": "Nastavte si Mailgun Webhook" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/sk.json b/homeassistant/components/mazda/translations/sk.json index 7a2c6414f82..9b00c29386f 100644 --- a/homeassistant/components/mazda/translations/sk.json +++ b/homeassistant/components/mazda/translations/sk.json @@ -14,7 +14,8 @@ "user": { "data": { "email": "Email", - "password": "Heslo" + "password": "Heslo", + "region": "Oblas\u0165" } } } diff --git a/homeassistant/components/meater/translations/sk.json b/homeassistant/components/meater/translations/sk.json index 41d88c34432..e2216e824b3 100644 --- a/homeassistant/components/meater/translations/sk.json +++ b/homeassistant/components/meater/translations/sk.json @@ -13,11 +13,13 @@ }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, "data_description": { "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno slu\u017eby Meater Cloud, zvy\u010dajne e-mailov\u00e1 adresa." - } + }, + "description": "Nastavte si \u00fa\u010det Meater Cloud." } } } diff --git a/homeassistant/components/media_player/translations/de.json b/homeassistant/components/media_player/translations/de.json index d6468920628..29a9e63d425 100644 --- a/homeassistant/components/media_player/translations/de.json +++ b/homeassistant/components/media_player/translations/de.json @@ -2,7 +2,7 @@ "device_automation": { "condition_type": { "is_buffering": "{entity_name} puffert", - "is_idle": "{entity_name} ist unt\u00e4tig", + "is_idle": "{entity_name} ist inaktiv", "is_off": "{entity_name} ist ausgeschaltet", "is_on": "{entity_name} ist eingeschaltet", "is_paused": "{entity_name} ist pausiert", @@ -21,7 +21,7 @@ "state": { "_": { "buffering": "Puffern", - "idle": "Unt\u00e4tig", + "idle": "inaktiv", "off": "Aus", "on": "An", "paused": "Pausiert", diff --git a/homeassistant/components/media_player/translations/sk.json b/homeassistant/components/media_player/translations/sk.json index af39abe21d4..a2d2813ea7b 100644 --- a/homeassistant/components/media_player/translations/sk.json +++ b/homeassistant/components/media_player/translations/sk.json @@ -1,7 +1,16 @@ { "device_automation": { + "condition_type": { + "is_idle": "{entity_name} je ne\u010dinn\u00e9", + "is_off": "{entity_name} je vypnut\u00e9", + "is_on": "{entity_name} je zapnut\u00e9", + "is_paused": "{entity_name} je pozastaven\u00e9" + }, "trigger_type": { - "changed_states": "{entity_name} zmenil stav" + "changed_states": "{entity_name} zmenil stav", + "paused": "{entity_name} je pozastaven\u00e9", + "turned_off": "{entity_name} vypnut\u00e1", + "turned_on": "{entity_name} zapnut\u00e1" } }, "state": { diff --git a/homeassistant/components/melcloud/translations/sk.json b/homeassistant/components/melcloud/translations/sk.json index 8d7a1129c85..708ebe26ec9 100644 --- a/homeassistant/components/melcloud/translations/sk.json +++ b/homeassistant/components/melcloud/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Integr\u00e1cia MELCloud je pre tento e-mail u\u017e nakonfigurovan\u00e1. Pr\u00edstupov\u00fd token bol obnoven\u00fd." + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", @@ -10,7 +13,9 @@ "data": { "password": "Heslo", "username": "Email" - } + }, + "description": "Pripojte sa pomocou svojho \u00fa\u010dtu MELCloud.", + "title": "Pripojte sa k MELCloud" } } } diff --git a/homeassistant/components/melnor/translations/sk.json b/homeassistant/components/melnor/translations/sk.json new file mode 100644 index 00000000000..50472089229 --- /dev/null +++ b/homeassistant/components/melnor/translations/sk.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "no_devices_found": "V bl\u00edzkosti nie s\u00fa \u017eiadne zariadenia Melnor Bluetooth." + }, + "step": { + "bluetooth_confirm": { + "description": "Chcete prida\u0165 Melnor Bluetooth ventil `{name}` do Home Assistant?", + "title": "Objaven\u00fd Melnor Bluetooth ventil" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meteo_france/translations/sk.json b/homeassistant/components/meteo_france/translations/sk.json index e3bda44ccc6..402d5f65bc7 100644 --- a/homeassistant/components/meteo_france/translations/sk.json +++ b/homeassistant/components/meteo_france/translations/sk.json @@ -3,6 +3,28 @@ "abort": { "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "cities": { + "data": { + "city": "Mesto" + }, + "description": "Vyberte si svoje mesto zo zoznamu" + }, + "user": { + "data": { + "city": "Mesto" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "mode": "Re\u017eim predpovede" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/meteoclimatic/translations/sk.json b/homeassistant/components/meteoclimatic/translations/sk.json index 8ae0056e613..67a321910ac 100644 --- a/homeassistant/components/meteoclimatic/translations/sk.json +++ b/homeassistant/components/meteoclimatic/translations/sk.json @@ -6,6 +6,13 @@ }, "error": { "not_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + }, + "step": { + "user": { + "data": { + "code": "K\u00f3d stanice" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/metoffice/translations/sk.json b/homeassistant/components/metoffice/translations/sk.json index 3ce9c0780c2..d1578eb3042 100644 --- a/homeassistant/components/metoffice/translations/sk.json +++ b/homeassistant/components/metoffice/translations/sk.json @@ -13,7 +13,8 @@ "api_key": "API k\u013e\u00fa\u010d", "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka" - } + }, + "description": "Zemepisn\u00e1 \u0161\u00edrka a d\u013a\u017eka sa pou\u017eije na n\u00e1jdenie najbli\u017e\u0161ej meteorologickej stanice." } } } diff --git a/homeassistant/components/miflora/translations/sk.json b/homeassistant/components/miflora/translations/sk.json new file mode 100644 index 00000000000..ea1bb7d4d8f --- /dev/null +++ b/homeassistant/components/miflora/translations/sk.json @@ -0,0 +1,7 @@ +{ + "issues": { + "replaced": { + "title": "Integr\u00e1cia Mi Flora bola nahraden\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mikrotik/translations/sk.json b/homeassistant/components/mikrotik/translations/sk.json index 4301cfb5c88..82e54c59aac 100644 --- a/homeassistant/components/mikrotik/translations/sk.json +++ b/homeassistant/components/mikrotik/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", @@ -13,15 +14,19 @@ "data": { "password": "Heslo" }, - "description": "Heslo pre {username} je neplatn\u00e9." + "description": "Heslo pre {username} je neplatn\u00e9.", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { "host": "Hostite\u013e", "name": "N\u00e1zov", "password": "Heslo", - "port": "Port" - } + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Pou\u017eite ssl" + }, + "title": "Nastavte Mikrotik router" } } }, diff --git a/homeassistant/components/mill/translations/sk.json b/homeassistant/components/mill/translations/sk.json index 7ee3205fdbf..0331ed11690 100644 --- a/homeassistant/components/mill/translations/sk.json +++ b/homeassistant/components/mill/translations/sk.json @@ -9,7 +9,8 @@ "step": { "cloud": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "local": { @@ -17,6 +18,11 @@ "ip_address": "IP adresa" }, "description": "Lok\u00e1lna IP adresa zariadenia." + }, + "user": { + "data": { + "connection_type": "Vyberte typ pripojenia" + } } } } diff --git a/homeassistant/components/min_max/translations/no.json b/homeassistant/components/min_max/translations/no.json index 5c9391b374f..7dab46b4ac1 100644 --- a/homeassistant/components/min_max/translations/no.json +++ b/homeassistant/components/min_max/translations/no.json @@ -9,10 +9,10 @@ "type": "Statistisk karakteristikk" }, "data_description": { - "round_digits": "Styrer antall desimaler i utdata n\u00e5r statistikkkarakteristikken er gjennomsnitt eller median." + "round_digits": "Styrer antall desimaler i utdata n\u00e5r statistikkkarakteristikken er gjennomsnitt, median eller sum." }, - "description": "Lag en sensor som beregner en min, maks, middelverdi eller medianverdi fra en liste over inngangssensorer.", - "title": "Legg til min / maks / gjennomsnitt / median sensor" + "description": "Lag en sensor som beregner en min, maks, gjennomsnitt, median eller sum fra en liste over inngangssensorer.", + "title": "Kombiner tilstanden til flere sensorer" } } }, @@ -25,10 +25,10 @@ "type": "Statistisk karakteristikk" }, "data_description": { - "round_digits": "Styrer antall desimaler i utdata n\u00e5r statistikkkarakteristikken er gjennomsnitt eller median." + "round_digits": "Styrer antall desimaler i utdata n\u00e5r statistikkkarakteristikken er gjennomsnitt, median eller sum." } } } }, - "title": "Min / maks / gjennomsnitt / median sensor" + "title": "Kombiner tilstanden til flere sensorer" } \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/sk.json b/homeassistant/components/min_max/translations/sk.json index 41019a4abc6..18f720b8c2b 100644 --- a/homeassistant/components/min_max/translations/sk.json +++ b/homeassistant/components/min_max/translations/sk.json @@ -3,8 +3,11 @@ "step": { "user": { "data": { - "entity_ids": "Vstupn\u00e9 entity" - } + "entity_ids": "Vstupn\u00e9 entity", + "name": "N\u00e1zov", + "round_digits": "Presnos\u0165" + }, + "title": "Skombinujte stav nieko\u013ek\u00fdch sn\u00edma\u010dov" } } }, @@ -12,9 +15,11 @@ "step": { "init": { "data": { - "entity_ids": "Vstupn\u00e9 entity" + "entity_ids": "Vstupn\u00e9 entity", + "round_digits": "Presnos\u0165" } } } - } + }, + "title": "Skombinujte stav nieko\u013ek\u00fdch sn\u00edma\u010dov" } \ No newline at end of file diff --git a/homeassistant/components/minecraft_server/translations/sk.json b/homeassistant/components/minecraft_server/translations/sk.json index d93d7f14e5d..6012400f204 100644 --- a/homeassistant/components/minecraft_server/translations/sk.json +++ b/homeassistant/components/minecraft_server/translations/sk.json @@ -3,12 +3,19 @@ "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165 k serveru. Skontrolujte hostite\u013ea a port a sk\u00faste to znova. Tie\u017e sa uistite, \u017ee na svojom serveri pou\u017e\u00edvate aspo\u0148 Minecraft verzie 1.7.", + "invalid_ip": "Adresa IP je neplatn\u00e1 (adresu MAC sa nepodarilo ur\u010di\u0165). Opravte to a sk\u00faste to znova.", + "invalid_port": "Port mus\u00ed by\u0165 v rozsahu od 1024 do 65535. Opravte ho a sk\u00faste to znova." + }, "step": { "user": { "data": { "host": "Hostite\u013e", "name": "N\u00e1zov" - } + }, + "description": "Nastavte in\u0161tanciu servera Minecraft tak, aby umo\u017e\u0148ovala monitorovanie.", + "title": "Prepojenie servera Minecraft" } } } diff --git a/homeassistant/components/mjpeg/translations/sk.json b/homeassistant/components/mjpeg/translations/sk.json index 7f91dfe2158..d669ecdfa1c 100644 --- a/homeassistant/components/mjpeg/translations/sk.json +++ b/homeassistant/components/mjpeg/translations/sk.json @@ -10,8 +10,11 @@ "step": { "user": { "data": { + "mjpeg_url": "MJPEG URL", "name": "N\u00e1zov", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } @@ -25,8 +28,11 @@ "step": { "init": { "data": { + "mjpeg_url": "MJPEG URL", "name": "N\u00e1zov", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/moehlenhoff_alpha2/translations/sk.json b/homeassistant/components/moehlenhoff_alpha2/translations/sk.json index bd2e41a926f..49599b94909 100644 --- a/homeassistant/components/moehlenhoff_alpha2/translations/sk.json +++ b/homeassistant/components/moehlenhoff_alpha2/translations/sk.json @@ -14,5 +14,6 @@ } } } - } + }, + "title": "M\u00f6hlenhoff Alpha2" } \ No newline at end of file diff --git a/homeassistant/components/monoprice/translations/sk.json b/homeassistant/components/monoprice/translations/sk.json index 0c5645b08b5..1626328f220 100644 --- a/homeassistant/components/monoprice/translations/sk.json +++ b/homeassistant/components/monoprice/translations/sk.json @@ -4,15 +4,37 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "port": "Port" + "port": "Port", + "source_1": "N\u00e1zov zdroja #1", + "source_2": "N\u00e1zov zdroja #2", + "source_3": "N\u00e1zov zdroja #3", + "source_4": "N\u00e1zov zdroja #4", + "source_5": "N\u00e1zov zdroja #5", + "source_6": "N\u00e1zov zdroja #6" }, "title": "Pripojte sa k zariadeniu" } } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "N\u00e1zov zdroja #1", + "source_2": "N\u00e1zov zdroja #2", + "source_3": "N\u00e1zov zdroja #3", + "source_4": "N\u00e1zov zdroja #4", + "source_5": "N\u00e1zov zdroja #5", + "source_6": "N\u00e1zov zdroja #6" + }, + "title": "Konfigur\u00e1cia zdrojov" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/moon/translations/sk.json b/homeassistant/components/moon/translations/sk.json new file mode 100644 index 00000000000..6ba11236f08 --- /dev/null +++ b/homeassistant/components/moon/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "user": { + "description": "Chcete za\u010da\u0165 nastavova\u0165?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/motion_blinds/translations/sk.json b/homeassistant/components/motion_blinds/translations/sk.json index aa889b4cd07..baae7e432f9 100644 --- a/homeassistant/components/motion_blinds/translations/sk.json +++ b/homeassistant/components/motion_blinds/translations/sk.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "connection_error": "Nepodarilo sa pripoji\u0165" }, "flow_title": "{short_mac} ({ip_address})", "step": { @@ -11,6 +12,11 @@ "api_key": "API k\u013e\u00fa\u010d" } }, + "select": { + "data": { + "select_ip": "IP adresa" + } + }, "user": { "data": { "host": "IP adresa" diff --git a/homeassistant/components/motioneye/translations/sk.json b/homeassistant/components/motioneye/translations/sk.json index fdf5b73c8f2..f3eb264871f 100644 --- a/homeassistant/components/motioneye/translations/sk.json +++ b/homeassistant/components/motioneye/translations/sk.json @@ -5,14 +5,18 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", - "invalid_url": "Neplatn\u00e1 adresa URL" + "invalid_url": "Neplatn\u00e1 adresa URL", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { "admin_password": "Spr\u00e1vca Heslo", - "surveillance_password": "Doh\u013ead Heslo" + "admin_username": "Admin Pou\u017e\u00edvate\u013esk\u00e9 meno", + "surveillance_password": "Doh\u013ead Heslo", + "url": "URL" } } } diff --git a/homeassistant/components/mqtt/translations/fr.json b/homeassistant/components/mqtt/translations/fr.json index f3d7b4b0ca4..3515b7cbf4c 100644 --- a/homeassistant/components/mqtt/translations/fr.json +++ b/homeassistant/components/mqtt/translations/fr.json @@ -20,7 +20,10 @@ "port": "Port", "protocol": "Protocole MQTT", "set_client_cert": "Utiliser un certificat client", - "username": "Nom d'utilisateur" + "transport": "Transport MQTT", + "username": "Nom d'utilisateur", + "ws_headers": "En-t\u00eates WebSocket au format JSON", + "ws_path": "Chemin WebSocket" }, "description": "Veuillez entrer les informations de connexion de votre broker MQTT." }, @@ -77,7 +80,10 @@ "port": "Port", "protocol": "Protocole MQTT", "set_client_cert": "Utiliser un certificat client", - "username": "Nom d'utilisateur" + "transport": "Transport MQTT", + "username": "Nom d'utilisateur", + "ws_headers": "En-t\u00eates WebSocket au format JSON", + "ws_path": "Chemin WebSocket" }, "description": "Veuillez entrer les informations de connexion de votre broker MQTT.", "title": "Options de courtier" diff --git a/homeassistant/components/mqtt/translations/no.json b/homeassistant/components/mqtt/translations/no.json index fae51fabb20..1e56625fe5c 100644 --- a/homeassistant/components/mqtt/translations/no.json +++ b/homeassistant/components/mqtt/translations/no.json @@ -8,7 +8,7 @@ "bad_birth": "Ugyldig f\u00f8dselsemne", "bad_certificate": "CA-sertifikatet er ugyldig", "bad_client_cert": "Ugyldig klientsertifikat, s\u00f8rg for at en PEM-kodet fil leveres", - "bad_client_cert_key": "Klientsertifikat og privat er ikke noe gyldig par", + "bad_client_cert_key": "Klientsertifikat og privat n\u00f8kkel er ikke et gyldig par", "bad_client_key": "Ugyldig privat n\u00f8kkel, s\u00f8rg for at en PEM-kodet fil leveres uten passord", "bad_discovery_prefix": "Ugyldig oppdagelsesprefiks", "bad_will": "Ugyldig viljeemne", @@ -85,7 +85,7 @@ "bad_birth": "Ugyldig f\u00f8dselsemne", "bad_certificate": "CA-sertifikatet er ugyldig", "bad_client_cert": "Ugyldig klientsertifikat, s\u00f8rg for at en PEM-kodet fil leveres", - "bad_client_cert_key": "Klientsertifikat og privat er ikke noe gyldig par", + "bad_client_cert_key": "Klientsertifikat og privat n\u00f8kkel er ikke et gyldig par", "bad_client_key": "Ugyldig privat n\u00f8kkel, s\u00f8rg for at en PEM-kodet fil leveres uten passord", "bad_discovery_prefix": "Ugyldig oppdagelsesprefiks", "bad_will": "Ugyldig viljeemne", diff --git a/homeassistant/components/mqtt/translations/sk.json b/homeassistant/components/mqtt/translations/sk.json index 873893643b8..25f39bf8191 100644 --- a/homeassistant/components/mqtt/translations/sk.json +++ b/homeassistant/components/mqtt/translations/sk.json @@ -6,8 +6,10 @@ }, "error": { "bad_certificate": "Certifik\u00e1t CA je neplatn\u00fd", + "bad_client_cert_key": "Certifik\u00e1t klienta a s\u00fakromn\u00fd k\u013e\u00fa\u010d nie s\u00fa platn\u00fdm p\u00e1rom", "bad_ws_headers": "Zadajte platn\u00e9 hlavi\u010dky HTTP ako objekt JSON", - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_inclusion": "Certifik\u00e1t klienta a s\u00fakromn\u00fd k\u013e\u00fa\u010d musia by\u0165 nakonfigurovan\u00e9 spolo\u010dne" }, "step": { "broker": { @@ -23,6 +25,7 @@ } }, "hassio_confirm": { + "description": "Chcete nakonfigurova\u0165 dom\u00e1ceho asistenta na pripojenie k brokerovi MQTT poskytovan\u00e9mu doplnkom {addon}?", "title": "MQTT Broker cez doplnok Home Assistant" } } @@ -37,13 +40,18 @@ "button_6": "\u0160ieste tla\u010didlo", "turn_off": "Vypn\u00fa\u0165", "turn_on": "Zapn\u00fa\u0165" + }, + "trigger_type": { + "button_double_press": "\"{subtype}\" kliknut\u00e9 dvakr\u00e1t" } }, "options": { "error": { "bad_certificate": "Certifik\u00e1t CA je neplatn\u00fd", + "bad_client_cert_key": "Certifik\u00e1t klienta a s\u00fakromn\u00fd k\u013e\u00fa\u010d nie s\u00fa platn\u00fdm p\u00e1rom", "bad_ws_headers": "Zadajte platn\u00e9 hlavi\u010dky HTTP ako objekt JSON", - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_inclusion": "Certifik\u00e1t klienta a s\u00fakromn\u00fd k\u013e\u00fa\u010d musia by\u0165 nakonfigurovan\u00e9 spolo\u010dne" }, "step": { "broker": { @@ -59,6 +67,10 @@ } }, "options": { + "data": { + "will_payload": "Odo\u0161le z\u00e1\u0165a\u017e", + "will_qos": "Odo\u0161le QoS" + }, "title": "MQTT mo\u017enosti" } } diff --git a/homeassistant/components/mullvad/translations/sk.json b/homeassistant/components/mullvad/translations/sk.json index 793f8eff278..1fa70439792 100644 --- a/homeassistant/components/mullvad/translations/sk.json +++ b/homeassistant/components/mullvad/translations/sk.json @@ -2,6 +2,15 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "description": "Nastavi\u0165 integr\u00e1ciu Mullvad VPN?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/mutesync/translations/sk.json b/homeassistant/components/mutesync/translations/sk.json index 842ff61bd79..d4923a2f973 100644 --- a/homeassistant/components/mutesync/translations/sk.json +++ b/homeassistant/components/mutesync/translations/sk.json @@ -1,5 +1,10 @@ { "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Povo\u013ete autentifik\u00e1ciu v m\u00fctesync Preferences > Authentication", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/myq/translations/sk.json b/homeassistant/components/myq/translations/sk.json index fe1ebf7793a..7f9c956aab3 100644 --- a/homeassistant/components/myq/translations/sk.json +++ b/homeassistant/components/myq/translations/sk.json @@ -5,19 +5,24 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { "data": { "password": "Heslo" }, - "description": "Heslo pre {username} u\u017e nie je platn\u00e9." + "description": "Heslo pre {username} u\u017e nie je platn\u00e9.", + "title": "Znova overte svoj \u00fa\u010det MyQ" }, "user": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Pripojte sa k MyQ Gateway" } } } diff --git a/homeassistant/components/mysensors/translations/sk.json b/homeassistant/components/mysensors/translations/sk.json index 96d77b5a9f1..b73b76616e6 100644 --- a/homeassistant/components/mysensors/translations/sk.json +++ b/homeassistant/components/mysensors/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "duplicate_persistence_file": "Perzistentn\u00fd s\u00fabor sa u\u017e pou\u017e\u00edva", "duplicate_topic": "T\u00e9ma sa u\u017e pou\u017e\u00edva", "invalid_auth": "Neplatn\u00e9 overenie", @@ -10,7 +11,9 @@ "invalid_port": "Neplatn\u00e9 \u010d\u00edslo portu", "invalid_serial": "Neplatn\u00fd s\u00e9riov\u00fd port", "invalid_version": "Neplatn\u00e1 verzia MySensors", + "mqtt_required": "Integr\u00e1cia MQTT nie je nastaven\u00e1", "not_a_number": "Zadajte \u010d\u00edslo", + "port_out_of_range": "\u010c\u00edslo portu mus\u00ed by\u0165 minim\u00e1lne 1 a maxim\u00e1lne 65535", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { @@ -25,19 +28,44 @@ "invalid_serial": "Neplatn\u00fd s\u00e9riov\u00fd port", "invalid_version": "Neplatn\u00e1 verzia MySensors", "not_a_number": "Zadajte \u010d\u00edslo", + "port_out_of_range": "\u010c\u00edslo portu mus\u00ed by\u0165 minim\u00e1lne 1 a maxim\u00e1lne 65535", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "gw_mqtt": { + "data": { + "version": "Verzia MySensors" + }, + "description": "Nastavenie br\u00e1ny MQTT" + }, "gw_serial": { "data": { - "device": "S\u00e9riov\u00fd port" + "baud_rate": "prenosov\u00e1 r\u00fdchlos\u0165", + "device": "S\u00e9riov\u00fd port", + "version": "Verzia MySensors" } }, "gw_tcp": { "data": { "device": "IP adresa br\u00e1ny", - "tcp_port": "port" + "tcp_port": "port", + "version": "Verzia MySensors" + }, + "description": "Nastavenie ethernetovej br\u00e1ny" + }, + "select_gateway_type": { + "description": "Vyberte br\u00e1nu, ktor\u00fa chcete nakonfigurova\u0165.", + "menu_options": { + "gw_mqtt": "Konfigur\u00e1cia br\u00e1ny MQTT", + "gw_serial": "Konfigur\u00e1cia s\u00e9riovej br\u00e1ny", + "gw_tcp": "Konfigur\u00e1cia br\u00e1ny TCP" } + }, + "user": { + "data": { + "gateway_type": "Typ br\u00e1ny" + }, + "description": "Vyberte sp\u00f4sob pripojenia k br\u00e1ne" } } } diff --git a/homeassistant/components/nam/translations/sk.json b/homeassistant/components/nam/translations/sk.json index e9bcab9374a..78966044e1e 100644 --- a/homeassistant/components/nam/translations/sk.json +++ b/homeassistant/components/nam/translations/sk.json @@ -3,22 +3,26 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "device_unsupported": "Zariadenie nie je podporovan\u00e9.", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "reauth_unsuccessful": "Op\u00e4tovn\u00e1 autentifik\u00e1cia bola ne\u00faspe\u0161n\u00e1, odstr\u00e1\u0148te integr\u00e1ciu a znova ju nastavte." }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "flow_title": "{host}", "step": { "credentials": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, "description": "Zadajte pou\u017e\u00edvate\u013esk\u00e9 meno a heslo." }, "reauth_confirm": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, "description": "Zadajte spr\u00e1vne pou\u017e\u00edvate\u013esk\u00e9 meno a heslo pre hostite\u013ea: {host}" }, diff --git a/homeassistant/components/nanoleaf/translations/sk.json b/homeassistant/components/nanoleaf/translations/sk.json index 8d6e5de117b..876c29b2d6d 100644 --- a/homeassistant/components/nanoleaf/translations/sk.json +++ b/homeassistant/components/nanoleaf/translations/sk.json @@ -2,8 +2,14 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/neato/translations/sk.json b/homeassistant/components/neato/translations/sk.json index 074a1f1164a..7baa84a69fb 100644 --- a/homeassistant/components/neato/translations/sk.json +++ b/homeassistant/components/neato/translations/sk.json @@ -2,12 +2,18 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" }, "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + }, "reauth_confirm": { "title": "Chcete za\u010da\u0165 nastavova\u0165?" } diff --git a/homeassistant/components/nest/translations/sk.json b/homeassistant/components/nest/translations/sk.json index 66a3817501a..c8926d87222 100644 --- a/homeassistant/components/nest/translations/sk.json +++ b/homeassistant/components/nest/translations/sk.json @@ -2,8 +2,12 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "unknown_authorize_url_generation": "Nezn\u00e1ma chyba pri generovan\u00ed autorizovanej adresy URL." }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" @@ -11,7 +15,9 @@ "error": { "internal_error": "Intern\u00e1 chyba pri overovan\u00ed k\u00f3du", "invalid_pin": "Nespr\u00e1vny PIN", - "timeout": "Overovac\u00ed k\u00f3d \u010dasov\u00e9ho limitu" + "subscriber_error": "Nezn\u00e1ma chyba odberate\u013ea, pozrite si denn\u00edky", + "timeout": "Overovac\u00ed k\u00f3d \u010dasov\u00e9ho limitu", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "cloud_project": { @@ -29,12 +35,18 @@ }, "description": "Ak chcete prepoji\u0165 svoje konto Nest, [autorizujte svoje konto]({url}).\n\nPo autoriz\u00e1cii skop\u00edrujte a vlo\u017ete poskytnut\u00fd k\u00f3d PIN uveden\u00fd ni\u017e\u0161ie." }, + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + }, "pubsub": { "data": { "cloud_project_id": "ID projektu Google Cloud" }, "description": "Nav\u0161t\u00edvte [Cloud Console]({url}) a vyh\u013eadajte svoje ID projektu Google Cloud.", "title": "Nakonfigurujte Google Cloud" + }, + "reauth_confirm": { + "title": "Znova overi\u0165 integr\u00e1ciu" } } }, @@ -45,5 +57,10 @@ "camera_sound": "Rozpoznan\u00fd zvuk", "doorbell_chime": "Zvon\u010dek stla\u010den\u00fd" } + }, + "issues": { + "deprecated_yaml": { + "title": "Konfigur\u00e1cia Nest YAML sa odstra\u0148uje" + } } } \ No newline at end of file diff --git a/homeassistant/components/netatmo/translations/sk.json b/homeassistant/components/netatmo/translations/sk.json index 211690bb3b8..80589b6b4a6 100644 --- a/homeassistant/components/netatmo/translations/sk.json +++ b/homeassistant/components/netatmo/translations/sk.json @@ -2,8 +2,10 @@ "config": { "abort": { "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" @@ -11,6 +13,9 @@ "step": { "pick_implementation": { "title": "Vyberte met\u00f3du overenia" + }, + "reauth_confirm": { + "title": "Znova overi\u0165 integr\u00e1ciu" } } }, @@ -23,6 +28,7 @@ "outdoor": "{n\u00e1zov_objektu} zistila vonkaj\u0161iu udalos\u0165", "person": "{entity_name} rozpoznala osobu", "person_away": "{entity_name} zistila, \u017ee osoba odi\u0161la", + "set_point": "Cie\u013eov\u00e1 teplota {entity_name} nastaven\u00e1 manu\u00e1lne", "therm_mode": "{entity_name} prepnut\u00e9 na \u201e{subtype}\u201c", "turned_off": "{entity_name} vypnut\u00e1", "turned_on": "{entity_name} zapnut\u00e1" @@ -32,11 +38,24 @@ "step": { "public_weather": { "data": { + "area_name": "N\u00e1zov oblasti", "lat_ne": "Zemepisn\u00e1 \u0161\u00edrka: severov\u00fdchodn\u00fd roh", "lat_sw": "Zemepisn\u00e1 \u0161\u00edrka: juhoz\u00e1padn\u00fd roh", "lon_ne": "Zemepisn\u00e1 d\u013a\u017eka: severov\u00fdchodn\u00fd roh", - "lon_sw": "Zemepisn\u00e1 d\u013a\u017eka: juhoz\u00e1padn\u00fd roh" - } + "lon_sw": "Zemepisn\u00e1 d\u013a\u017eka: juhoz\u00e1padn\u00fd roh", + "mode": "V\u00fdpo\u010det", + "show_on_map": "Zobrazi\u0165 na mape" + }, + "description": "Nakonfigurujte verejn\u00fd senzor po\u010dasia pre dan\u00fa oblas\u0165.", + "title": "Verejn\u00fd senzor po\u010dasia Netatmo" + }, + "public_weather_areas": { + "data": { + "new_area": "N\u00e1zov oblasti", + "weather_areas": "Poveternostn\u00e9 oblasti" + }, + "description": "Nakonfigurujte verejn\u00e9 senzory po\u010dasia.", + "title": "Verejn\u00fd senzor po\u010dasia Netatmo" } } } diff --git a/homeassistant/components/netgear/translations/sk.json b/homeassistant/components/netgear/translations/sk.json index 15c74aa2ccd..ae8937fb96d 100644 --- a/homeassistant/components/netgear/translations/sk.json +++ b/homeassistant/components/netgear/translations/sk.json @@ -3,11 +3,15 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "config": "Chyba pripojenia alebo prihl\u00e1senia: skontrolujte konfigur\u00e1ciu" + }, "step": { "user": { "data": { "host": "Hostite\u013e (Volite\u013en\u00e9)", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno (volite\u013en\u00e9)" }, "description": "Predvolen\u00fd hostite\u013e: {host}\nPredvolen\u00e9 pou\u017e\u00edvate\u013esk\u00e9 meno: {username}" } diff --git a/homeassistant/components/nexia/translations/sk.json b/homeassistant/components/nexia/translations/sk.json index 8f06d99549b..0c9a112e32e 100644 --- a/homeassistant/components/nexia/translations/sk.json +++ b/homeassistant/components/nexia/translations/sk.json @@ -4,12 +4,15 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/nfandroidtv/translations/sk.json b/homeassistant/components/nfandroidtv/translations/sk.json index 9c5640da7d0..fc84825ae2e 100644 --- a/homeassistant/components/nfandroidtv/translations/sk.json +++ b/homeassistant/components/nfandroidtv/translations/sk.json @@ -3,12 +3,17 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { "host": "Hostite\u013e", "name": "N\u00e1zov" - } + }, + "description": "Pozrite si dokument\u00e1ciu, aby ste sa uistili, \u017ee s\u00fa splnen\u00e9 v\u0161etky po\u017eiadavky." } } } diff --git a/homeassistant/components/nibe_heatpump/translations/sk.json b/homeassistant/components/nibe_heatpump/translations/sk.json index 692f1569f7b..50d472b9c96 100644 --- a/homeassistant/components/nibe_heatpump/translations/sk.json +++ b/homeassistant/components/nibe_heatpump/translations/sk.json @@ -1,17 +1,28 @@ { "config": { "error": { - "address_in_use": "Vybran\u00fd port po\u010d\u00favania sa u\u017e v tomto syst\u00e9me pou\u017e\u00edva." + "address_in_use": "Vybran\u00fd port po\u010d\u00favania sa u\u017e v tomto syst\u00e9me pou\u017e\u00edva.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "modbus": { "data": { - "modbus_url": "Modbus URL" + "modbus_url": "Modbus URL", + "model": "Model tepeln\u00e9ho \u010derpadla" } }, "nibegw": { "data": { - "ip_address": "Vzdialen\u00e1 adresa" + "ip_address": "Vzdialen\u00e1 adresa", + "model": "Model tepeln\u00e9ho \u010derpadla", + "remote_read_port": "Port pre vzdialen\u00e9 \u010d\u00edtanie", + "remote_write_port": "Port pre vzdialen\u00fd z\u00e1pis" + } + }, + "user": { + "menu_options": { + "modbus": "Modbus", + "nibegw": "NibeGW" } } } diff --git a/homeassistant/components/nightscout/translations/sk.json b/homeassistant/components/nightscout/translations/sk.json index 4078f7bcb6c..de3386fca50 100644 --- a/homeassistant/components/nightscout/translations/sk.json +++ b/homeassistant/components/nightscout/translations/sk.json @@ -4,14 +4,17 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", "url": "URL" - } + }, + "title": "Zadajte inform\u00e1cie o serveri Nightscout." } } } diff --git a/homeassistant/components/nina/translations/sk.json b/homeassistant/components/nina/translations/sk.json new file mode 100644 index 00000000000..20fc41d8c6f --- /dev/null +++ b/homeassistant/components/nina/translations/sk.json @@ -0,0 +1,49 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "no_selection": "Vyberte aspo\u0148 jedno mesto/okres", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "_a_to_d": "Mesto/okres (A-D)", + "_e_to_h": "Mesto/okres (E-H)", + "_i_to_l": "Mesto/okres (I-L)", + "_m_to_q": "Mesto/okres (M-Q)", + "_r_to_u": "Mesto/okres (R-U)", + "_v_to_z": "Mesto/okres (V-Z)", + "corona_filter": "Odstr\u00e1\u0148te v\u00fdstrahy Corona", + "slots": "Maxim\u00e1lny po\u010det varovan\u00ed na mesto/okres" + }, + "title": "Vyberte mesto/okres" + } + } + }, + "options": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "no_selection": "Vyberte aspo\u0148 jedno mesto/okres", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "init": { + "data": { + "_a_to_d": "Mesto/okres (A-D)", + "_e_to_h": "Mesto/okres (E-H)", + "_i_to_l": "Mesto/okres (I-L)", + "_m_to_q": "Mesto/okres (M-Q)", + "_r_to_u": "Mesto/okres (R-U)", + "_v_to_z": "Mesto/okres (V-Z)", + "corona_filter": "Odstr\u00e1\u0148te v\u00fdstrahy Corona", + "slots": "Maxim\u00e1lny po\u010det varovan\u00ed na mesto/okres" + }, + "title": "Mo\u017enosti" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nmap_tracker/translations/sk.json b/homeassistant/components/nmap_tracker/translations/sk.json index 19d1a53c160..e2c968a9e58 100644 --- a/homeassistant/components/nmap_tracker/translations/sk.json +++ b/homeassistant/components/nmap_tracker/translations/sk.json @@ -10,6 +10,7 @@ "user": { "data": { "exclude": "Sie\u0165ov\u00e9 adresy (oddelen\u00e9 \u010diarkou), ktor\u00e9 sa maj\u00fa vyl\u00fa\u010di\u0165 z kontroly", + "home_interval": "Minim\u00e1lny po\u010det min\u00fat medzi kontrolami akt\u00edvnych zariaden\u00ed (\u0161etrenie bat\u00e9rie)", "hosts": "Sie\u0165ov\u00e9 adresy (oddelen\u00e9 \u010diarkami), ktor\u00e9 sa maj\u00fa skenova\u0165" } } @@ -22,6 +23,8 @@ "step": { "init": { "data": { + "exclude": "Sie\u0165ov\u00e9 adresy (oddelen\u00e9 \u010diarkou), ktor\u00e9 sa maj\u00fa vyl\u00fa\u010di\u0165 z kontroly", + "home_interval": "Minim\u00e1lny po\u010det min\u00fat medzi kontrolami akt\u00edvnych zariaden\u00ed (\u0161etrenie bat\u00e9rie)", "hosts": "Sie\u0165ov\u00e9 adresy (oddelen\u00e9 \u010diarkami), ktor\u00e9 sa maj\u00fa skenova\u0165", "interval_seconds": "Interval skenovania" } diff --git a/homeassistant/components/nobo_hub/translations/sk.json b/homeassistant/components/nobo_hub/translations/sk.json index 67c314052bd..f862703bd72 100644 --- a/homeassistant/components/nobo_hub/translations/sk.json +++ b/homeassistant/components/nobo_hub/translations/sk.json @@ -4,13 +4,16 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165 - skontrolujte s\u00e9riov\u00e9 \u010d\u00edslo", "invalid_ip": "Neplatn\u00e1 IP adresa", - "invalid_serial": "Neplatn\u00e9 s\u00e9riov\u00e9 \u010d\u00edslo" + "invalid_serial": "Neplatn\u00e9 s\u00e9riov\u00e9 \u010d\u00edslo", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "manual": { "data": { - "ip_address": "IP adresa" + "ip_address": "IP adresa", + "serial": "S\u00e9riov\u00e9 \u010d\u00edslo (12 \u010d\u00edslic)" } } } diff --git a/homeassistant/components/notion/translations/sk.json b/homeassistant/components/notion/translations/sk.json index 5035a9ee157..66a5aae1f52 100644 --- a/homeassistant/components/notion/translations/sk.json +++ b/homeassistant/components/notion/translations/sk.json @@ -5,19 +5,23 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { "data": { "password": "Heslo" }, - "description": "Znova zadajte heslo pre {username}." + "description": "Znova zadajte heslo pre {username}.", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Vypl\u0148te svoje \u00fadaje" } } } diff --git a/homeassistant/components/nuheat/translations/sk.json b/homeassistant/components/nuheat/translations/sk.json index 6ccc4edd5ec..55ec6c3046c 100644 --- a/homeassistant/components/nuheat/translations/sk.json +++ b/homeassistant/components/nuheat/translations/sk.json @@ -4,14 +4,19 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", - "invalid_thermostat": "S\u00e9riov\u00e9 \u010d\u00edslo termostatu je neplatn\u00e9." + "invalid_thermostat": "S\u00e9riov\u00e9 \u010d\u00edslo termostatu je neplatn\u00e9.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "serial_number": "S\u00e9riov\u00e9 \u010d\u00edslo termostatu.", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Pripojte sa k NuHeat" } } } diff --git a/homeassistant/components/nuki/translations/sk.json b/homeassistant/components/nuki/translations/sk.json index 9b0a7b1efd2..bd03a6ec700 100644 --- a/homeassistant/components/nuki/translations/sk.json +++ b/homeassistant/components/nuki/translations/sk.json @@ -4,13 +4,16 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { "data": { "token": "Pr\u00edstupov\u00fd token" - } + }, + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { diff --git a/homeassistant/components/number/translations/sk.json b/homeassistant/components/number/translations/sk.json new file mode 100644 index 00000000000..641f7658bb2 --- /dev/null +++ b/homeassistant/components/number/translations/sk.json @@ -0,0 +1,3 @@ +{ + "title": "\u010c\u00edslo" +} \ No newline at end of file diff --git a/homeassistant/components/nws/translations/sk.json b/homeassistant/components/nws/translations/sk.json index 5a78acc060d..8f8b360f638 100644 --- a/homeassistant/components/nws/translations/sk.json +++ b/homeassistant/components/nws/translations/sk.json @@ -3,12 +3,16 @@ "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", "latitude": "Zemepisn\u00e1 \u0161\u00edrka", - "longitude": "Zemepisn\u00e1 d\u013a\u017eka" + "longitude": "Zemepisn\u00e1 d\u013a\u017eka", + "station": "K\u00f3d stanice METAR" }, "title": "Pripojenie k N\u00e1rodnej meteorologickej slu\u017ebe" } diff --git a/homeassistant/components/nzbget/translations/sk.json b/homeassistant/components/nzbget/translations/sk.json index 8946f220e6b..eaaa5cb6d0a 100644 --- a/homeassistant/components/nzbget/translations/sk.json +++ b/homeassistant/components/nzbget/translations/sk.json @@ -1,5 +1,11 @@ { "config": { + "abort": { + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{name}", "step": { "user": { @@ -7,8 +13,12 @@ "host": "Hostite\u013e", "name": "N\u00e1zov", "password": "Heslo", - "port": "Port" - } + "port": "Port", + "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" + }, + "title": "Pripojte sa k NZBGet" } } }, diff --git a/homeassistant/components/octoprint/translations/sk.json b/homeassistant/components/octoprint/translations/sk.json index ff59ac2967c..4cec5809e7e 100644 --- a/homeassistant/components/octoprint/translations/sk.json +++ b/homeassistant/components/octoprint/translations/sk.json @@ -2,13 +2,30 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "auth_failed": "Nepodarilo sa na\u010d\u00edta\u0165 k\u013e\u00fa\u010d API aplik\u00e1cie" + "auth_failed": "Nepodarilo sa na\u010d\u00edta\u0165 k\u013e\u00fa\u010d API aplik\u00e1cie", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "Tla\u010diare\u0148 OctoPrint: {host}", "step": { + "reauth_confirm": { + "data": { + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + } + }, "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "path": "Cesta aplik\u00e1cie", + "port": "\u010c\u00edslo portu", + "ssl": "Pou\u017eite SSL", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/omnilogic/translations/sk.json b/homeassistant/components/omnilogic/translations/sk.json index 1b1e671c054..2e4ae17997a 100644 --- a/homeassistant/components/omnilogic/translations/sk.json +++ b/homeassistant/components/omnilogic/translations/sk.json @@ -1,12 +1,27 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "ph_offset": "Offset pH (pozit\u00edvny alebo negat\u00edvny)" } } } diff --git a/homeassistant/components/onboarding/translations/sk.json b/homeassistant/components/onboarding/translations/sk.json new file mode 100644 index 00000000000..44f2251e344 --- /dev/null +++ b/homeassistant/components/onboarding/translations/sk.json @@ -0,0 +1,7 @@ +{ + "area": { + "bedroom": "Sp\u00e1l\u0148a", + "kitchen": "Kuchy\u0148a", + "living_room": "Ob\u00fdva\u010dka" + } +} \ No newline at end of file diff --git a/homeassistant/components/oncue/translations/sk.json b/homeassistant/components/oncue/translations/sk.json index 46f45d53967..5d12e6d8fe7 100644 --- a/homeassistant/components/oncue/translations/sk.json +++ b/homeassistant/components/oncue/translations/sk.json @@ -4,13 +4,15 @@ "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/ondilo_ico/translations/sk.json b/homeassistant/components/ondilo_ico/translations/sk.json index 91803ae5155..0b766936f38 100644 --- a/homeassistant/components/ondilo_ico/translations/sk.json +++ b/homeassistant/components/ondilo_ico/translations/sk.json @@ -1,10 +1,16 @@ { "config": { "abort": { - "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie." }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" + }, + "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + } } } } \ No newline at end of file diff --git a/homeassistant/components/onewire/translations/sk.json b/homeassistant/components/onewire/translations/sk.json index 87147f54cd3..636af7a47d1 100644 --- a/homeassistant/components/onewire/translations/sk.json +++ b/homeassistant/components/onewire/translations/sk.json @@ -9,7 +9,8 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "port": "Port" }, "title": "Nastavenie 1-Wire" } diff --git a/homeassistant/components/onvif/translations/sk.json b/homeassistant/components/onvif/translations/sk.json index 1188a84382e..f0230e2e07c 100644 --- a/homeassistant/components/onvif/translations/sk.json +++ b/homeassistant/components/onvif/translations/sk.json @@ -4,6 +4,9 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "configure": { "data": { @@ -13,6 +16,11 @@ "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } + }, + "user": { + "data": { + "auto": "Automatick\u00e9 vyh\u013ead\u00e1vanie" + } } } } diff --git a/homeassistant/components/open_meteo/translations/sk.json b/homeassistant/components/open_meteo/translations/sk.json new file mode 100644 index 00000000000..28ab47956fa --- /dev/null +++ b/homeassistant/components/open_meteo/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "Vyberte miesto, ktor\u00e9 chcete pou\u017ei\u0165 na predpove\u010f po\u010dasia" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/sk.json b/homeassistant/components/openexchangerates/translations/sk.json index 461dced0cd6..7a67ae4ec9b 100644 --- a/homeassistant/components/openexchangerates/translations/sk.json +++ b/homeassistant/components/openexchangerates/translations/sk.json @@ -1,10 +1,14 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "timeout_connect": "\u010casov\u00fd limit na nadviazanie spojenia" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "timeout_connect": "\u010casov\u00fd limit na nadviazanie spojenia" }, "step": { "user": { diff --git a/homeassistant/components/opengarage/translations/sk.json b/homeassistant/components/opengarage/translations/sk.json index b343b9c45d2..ad1df6303e0 100644 --- a/homeassistant/components/opengarage/translations/sk.json +++ b/homeassistant/components/opengarage/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { @@ -11,7 +12,8 @@ "data": { "device_key": "K\u013e\u00fa\u010d zariadenia", "host": "Hostite\u013e", - "port": "Port" + "port": "Port", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/opentherm_gw/translations/sk.json b/homeassistant/components/opentherm_gw/translations/sk.json index ac89d84cb09..2de0fd19743 100644 --- a/homeassistant/components/opentherm_gw/translations/sk.json +++ b/homeassistant/components/opentherm_gw/translations/sk.json @@ -2,7 +2,9 @@ "config": { "error": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "id_exists": "ID br\u00e1ny u\u017e existuje" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "id_exists": "ID br\u00e1ny u\u017e existuje", + "timeout_connect": "\u010casov\u00fd limit na nadviazanie spojenia" }, "step": { "init": { @@ -13,5 +15,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "set_precision": "Nastavi\u0165 presnos\u0165" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/openuv/translations/sk.json b/homeassistant/components/openuv/translations/sk.json index 36fc17456fb..a419a801489 100644 --- a/homeassistant/components/openuv/translations/sk.json +++ b/homeassistant/components/openuv/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" @@ -11,7 +12,8 @@ "data": { "api_key": "API k\u013e\u00fa\u010d" }, - "description": "Znova zadajte k\u013e\u00fa\u010d API pre {latitude}, {longitude}." + "description": "Znova zadajte k\u013e\u00fa\u010d API pre {latitude}, {longitude}.", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { diff --git a/homeassistant/components/openweathermap/translations/sk.json b/homeassistant/components/openweathermap/translations/sk.json index 64b72099594..5afeabd6deb 100644 --- a/homeassistant/components/openweathermap/translations/sk.json +++ b/homeassistant/components/openweathermap/translations/sk.json @@ -4,17 +4,31 @@ "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" }, "step": { "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", + "language": "Jazyk", "latitude": "Zemepisn\u00e1 \u0161\u00edrka", - "longitude": "Zemepisn\u00e1 d\u013a\u017eka" + "longitude": "Zemepisn\u00e1 d\u013a\u017eka", + "mode": "Re\u017eim", + "name": "N\u00e1zov" }, "description": "Ak chcete vygenerova\u0165 k\u013e\u00fa\u010d API, prejdite na https://openweathermap.org/appid" } } + }, + "options": { + "step": { + "init": { + "data": { + "language": "Jazyk", + "mode": "Re\u017eim" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/sensor.sk.json b/homeassistant/components/overkiz/translations/sensor.sk.json index 3ce2cb1aea5..848abd98c72 100644 --- a/homeassistant/components/overkiz/translations/sensor.sk.json +++ b/homeassistant/components/overkiz/translations/sensor.sk.json @@ -14,6 +14,7 @@ }, "overkiz__priority_lock_originator": { "local_user": "Miestny pou\u017e\u00edvate\u013e", + "rain": "D\u00e1\u017e\u010f", "temperature": "Teplota", "timer": "\u010casova\u010d", "ups": "UPS", diff --git a/homeassistant/components/overkiz/translations/sk.json b/homeassistant/components/overkiz/translations/sk.json index 33311c8c92e..2d386b6d45a 100644 --- a/homeassistant/components/overkiz/translations/sk.json +++ b/homeassistant/components/overkiz/translations/sk.json @@ -5,15 +5,21 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", - "too_many_attempts": "Pr\u00edli\u0161 ve\u013ea pokusov s neplatn\u00fdm tokenom, do\u010dasne zak\u00e1zan\u00e9" + "server_in_maintenance": "Server je vypnut\u00fd z d\u00f4vodu \u00fadr\u017eby", + "too_many_attempts": "Pr\u00edli\u0161 ve\u013ea pokusov s neplatn\u00fdm tokenom, do\u010dasne zak\u00e1zan\u00e9", + "too_many_requests": "Pr\u00edli\u0161 ve\u013ea po\u017eiadaviek, sk\u00faste to nesk\u00f4r", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", + "unsupported_hardware": "V\u00e1\u0161 hardv\u00e9r {unsupported_device} nie je podporovan\u00fd touto integr\u00e1ciou." }, "flow_title": "Br\u00e1na: {gateway_id}", "step": { "user": { "data": { "host": "Hostite\u013e", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/ovo_energy/translations/sk.json b/homeassistant/components/ovo_energy/translations/sk.json index 5bec28db5ed..e860c7345ce 100644 --- a/homeassistant/components/ovo_energy/translations/sk.json +++ b/homeassistant/components/ovo_energy/translations/sk.json @@ -2,8 +2,10 @@ "config": { "error": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "{username}", "step": { "reauth": { "data": { @@ -13,7 +15,8 @@ }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/owntracks/translations/sk.json b/homeassistant/components/owntracks/translations/sk.json new file mode 100644 index 00000000000..c9ae6fa2722 --- /dev/null +++ b/homeassistant/components/owntracks/translations/sk.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "user": { + "description": "Naozaj chcete nastavi\u0165 OwnTracks?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/p1_monitor/translations/sk.json b/homeassistant/components/p1_monitor/translations/sk.json index 52ff5c28245..d7ec1bab0b6 100644 --- a/homeassistant/components/p1_monitor/translations/sk.json +++ b/homeassistant/components/p1_monitor/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { @@ -7,7 +10,8 @@ }, "data_description": { "host": "IP adresa alebo n\u00e1zov hostite\u013ea va\u0161ej in\u0161tal\u00e1cie monitora P1." - } + }, + "description": "Nastavte P1 Monitor na integr\u00e1ciu s Home Assistant." } } } diff --git a/homeassistant/components/panasonic_viera/translations/sk.json b/homeassistant/components/panasonic_viera/translations/sk.json index 8b6520976a6..b4bdedf9793 100644 --- a/homeassistant/components/panasonic_viera/translations/sk.json +++ b/homeassistant/components/panasonic_viera/translations/sk.json @@ -1,9 +1,12 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_pin_code": "Zadan\u00fd PIN k\u00f3d bol neplatn\u00fd" }, "step": { @@ -11,13 +14,16 @@ "data": { "pin": "PIN k\u00f3d" }, - "description": "Zadajte PIN k\u00f3d zobrazen\u00fd na va\u0161om telev\u00edzore" + "description": "Zadajte PIN k\u00f3d zobrazen\u00fd na va\u0161om telev\u00edzore", + "title": "P\u00e1rovanie" }, "user": { "data": { "host": "IP adresa", "name": "N\u00e1zov" - } + }, + "description": "Zadajte IP adresa v\u00e1\u0161ho telev\u00edzora Panasonic Viera TV", + "title": "Nastavenie telev\u00edzora" } } } diff --git a/homeassistant/components/peco/translations/sk.json b/homeassistant/components/peco/translations/sk.json index f04d4a327f4..09453d63993 100644 --- a/homeassistant/components/peco/translations/sk.json +++ b/homeassistant/components/peco/translations/sk.json @@ -2,6 +2,13 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "step": { + "user": { + "data": { + "county": "Okres" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/pi_hole/translations/sk.json b/homeassistant/components/pi_hole/translations/sk.json index f3fc592906a..ee03e7a4993 100644 --- a/homeassistant/components/pi_hole/translations/sk.json +++ b/homeassistant/components/pi_hole/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "api_key": { "data": { @@ -15,7 +18,10 @@ "host": "Hostite\u013e", "location": "Umiestnenie", "name": "N\u00e1zov", - "port": "Port" + "port": "Port", + "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", + "statistics_only": "Iba \u0161tatistika", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/picnic/translations/sk.json b/homeassistant/components/picnic/translations/sk.json index 7b3c6e79436..85830a679d5 100644 --- a/homeassistant/components/picnic/translations/sk.json +++ b/homeassistant/components/picnic/translations/sk.json @@ -5,13 +5,17 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "different_account": "\u00da\u010det by mal by\u0165 rovnak\u00fd ako pri nastavovan\u00ed integr\u00e1cie", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { "country_code": "K\u00f3d krajiny", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/plaato/translations/sk.json b/homeassistant/components/plaato/translations/sk.json index b7ae80d4937..c8633ec2895 100644 --- a/homeassistant/components/plaato/translations/sk.json +++ b/homeassistant/components/plaato/translations/sk.json @@ -1,7 +1,13 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + }, + "create_entry": { + "default": "Va\u0161e zariadenie Plaato {device_type} s n\u00e1zvom **{device_name}** bolo \u00faspe\u0161ne nastaven\u00e9!" }, "error": { "no_auth_token": "Mus\u00edte prida\u0165 autoriza\u010dn\u00fd token" @@ -15,9 +21,11 @@ }, "user": { "data": { - "device_name": "Pomenujte svoje zariadenie" + "device_name": "Pomenujte svoje zariadenie", + "device_type": "Typ zariadenia Plaato" }, - "description": "Chcete za\u010da\u0165 nastavova\u0165?" + "description": "Chcete za\u010da\u0165 nastavova\u0165?", + "title": "Nastavte zariadenia Plaato" } } }, @@ -27,7 +35,11 @@ "data": { "update_interval": "Interval aktualiz\u00e1cie (v min\u00fatach)" }, - "description": "Nastavte interval aktualiz\u00e1cie (v min\u00fatach)" + "description": "Nastavte interval aktualiz\u00e1cie (v min\u00fatach)", + "title": "Mo\u017enosti pre Plaato" + }, + "webhook": { + "description": "Inform\u00e1cie o webhooku: \n\n - URL: `{webhook_url}`\n - Met\u00f3da: POST \n\n" } } } diff --git a/homeassistant/components/plex/translations/sk.json b/homeassistant/components/plex/translations/sk.json index 8b81cdd0ccf..db7c9aeb581 100644 --- a/homeassistant/components/plex/translations/sk.json +++ b/homeassistant/components/plex/translations/sk.json @@ -9,7 +9,11 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { - "faulty_credentials": "Autoriz\u00e1cia zlyhala, overte token" + "faulty_credentials": "Autoriz\u00e1cia zlyhala, overte token", + "host_or_token": "Mus\u00ed poskytn\u00fa\u0165 aspo\u0148 jeden z hostite\u013eov alebo tokenov", + "no_servers": "\u017diadne servery prepojen\u00e9 s \u00fa\u010dtom Plex", + "not_found": "Plex server sa nena\u0161iel", + "ssl_error": "Probl\u00e9m s certifik\u00e1tom SSL" }, "flow_title": "{name} ({host})", "step": { @@ -20,6 +24,21 @@ "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", "token": "Token (volite\u013en\u00fd)", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" + }, + "title": "Manu\u00e1lna konfigur\u00e1cia Plex" + }, + "select_server": { + "data": { + "server": "Server" + }, + "title": "Vyberte server Plex" + }, + "user": { + "description": "Pokra\u010dujte na [plex.tv](https://plex.tv) a prepojte server Plex." + }, + "user_advanced": { + "data": { + "setup_method": "Sp\u00f4sob nastavenia" } } } diff --git a/homeassistant/components/plugwise/translations/no.json b/homeassistant/components/plugwise/translations/no.json index 7d2b4628b86..ea676591361 100644 --- a/homeassistant/components/plugwise/translations/no.json +++ b/homeassistant/components/plugwise/translations/no.json @@ -7,8 +7,10 @@ "error": { "cannot_connect": "Tilkobling mislyktes", "invalid_auth": "Ugyldig godkjenning", - "invalid_setup": "Legg til din Adam i stedet for din Anna, se integrasjonsdokumentasjonen for Home Assistant Plugwise for mer informasjon", - "unknown": "Uventet feil" + "invalid_setup": "Legg til din Adam i stedet for din Anna, se dokumentasjonen", + "response_error": "Ugyldige XML-data, eller feilindikasjon mottatt", + "unknown": "Uventet feil", + "unsupported": "Enhet med fastvare som ikke st\u00f8ttes" }, "step": { "user": { diff --git a/homeassistant/components/plugwise/translations/sk.json b/homeassistant/components/plugwise/translations/sk.json index 24b4520c72c..12048de0987 100644 --- a/homeassistant/components/plugwise/translations/sk.json +++ b/homeassistant/components/plugwise/translations/sk.json @@ -4,13 +4,18 @@ "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", + "unsupported": "Zariadenie s nepodporovan\u00fdm firmv\u00e9rom" }, "step": { "user": { "data": { - "host": "IP adresa" - } + "host": "IP adresa", + "port": "Port" + }, + "description": "Pros\u00edm, zadajte" } } } diff --git a/homeassistant/components/plum_lightpad/translations/sk.json b/homeassistant/components/plum_lightpad/translations/sk.json index b36de9a2ce5..a89b9b15d54 100644 --- a/homeassistant/components/plum_lightpad/translations/sk.json +++ b/homeassistant/components/plum_lightpad/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/point/translations/sk.json b/homeassistant/components/point/translations/sk.json index bd6f0ce772d..e650ef4e01f 100644 --- a/homeassistant/components/point/translations/sk.json +++ b/homeassistant/components/point/translations/sk.json @@ -13,7 +13,8 @@ }, "step": { "user": { - "description": "Chcete za\u010da\u0165 nastavova\u0165?" + "description": "Chcete za\u010da\u0165 nastavova\u0165?", + "title": "Vyberte met\u00f3du overenia" } } } diff --git a/homeassistant/components/powerwall/translations/sk.json b/homeassistant/components/powerwall/translations/sk.json index f15f2cd038f..ae43b2fffa0 100644 --- a/homeassistant/components/powerwall/translations/sk.json +++ b/homeassistant/components/powerwall/translations/sk.json @@ -2,9 +2,11 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "flow_title": "{name} ({ip_address})", diff --git a/homeassistant/components/profiler/translations/sk.json b/homeassistant/components/profiler/translations/sk.json index 473cb1c2fa0..6ba11236f08 100644 --- a/homeassistant/components/profiler/translations/sk.json +++ b/homeassistant/components/profiler/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "step": { "user": { "description": "Chcete za\u010da\u0165 nastavova\u0165?" diff --git a/homeassistant/components/progettihwsw/translations/sk.json b/homeassistant/components/progettihwsw/translations/sk.json index 142f47cf1fd..1ad2241b9e5 100644 --- a/homeassistant/components/progettihwsw/translations/sk.json +++ b/homeassistant/components/progettihwsw/translations/sk.json @@ -3,7 +3,31 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { + "relay_modes": { + "data": { + "relay_1": "Rel\u00e9 1", + "relay_10": "Rel\u00e9 10", + "relay_11": "Rel\u00e9 11", + "relay_12": "Rel\u00e9 12", + "relay_13": "Rel\u00e9 13", + "relay_14": "Rel\u00e9 14", + "relay_15": "Rel\u00e9 15", + "relay_16": "Rel\u00e9 16", + "relay_2": "Rel\u00e9 2", + "relay_3": "Rel\u00e9 3", + "relay_4": "Rel\u00e9 4", + "relay_5": "Rel\u00e9 5", + "relay_6": "Rel\u00e9 6", + "relay_7": "Rel\u00e9 7", + "relay_8": "Rel\u00e9 8", + "relay_9": "Rel\u00e9 9" + } + }, "user": { "data": { "host": "Hostite\u013e", diff --git a/homeassistant/components/prosegur/translations/sk.json b/homeassistant/components/prosegur/translations/sk.json index b557456a8fe..7744a00340e 100644 --- a/homeassistant/components/prosegur/translations/sk.json +++ b/homeassistant/components/prosegur/translations/sk.json @@ -5,17 +5,22 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "user": { "data": { - "password": "Heslo" + "country": "Krajina", + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/prusalink/translations/sensor.sk.json b/homeassistant/components/prusalink/translations/sensor.sk.json index 054a2d156d3..d35150afe2e 100644 --- a/homeassistant/components/prusalink/translations/sensor.sk.json +++ b/homeassistant/components/prusalink/translations/sensor.sk.json @@ -1,6 +1,7 @@ { "state": { "prusalink__printer_state": { + "cancelling": "Prebieha ru\u0161enie", "printing": "Tla\u010d" } } diff --git a/homeassistant/components/prusalink/translations/sk.json b/homeassistant/components/prusalink/translations/sk.json index 2c7ec866cf3..1ae350ddfd3 100644 --- a/homeassistant/components/prusalink/translations/sk.json +++ b/homeassistant/components/prusalink/translations/sk.json @@ -1,7 +1,10 @@ { "config": { "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "not_supported": "Podporovan\u00e9 je iba PrusaLink API v2", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/ps4/translations/sk.json b/homeassistant/components/ps4/translations/sk.json index 74600b1e9ad..bb9cdddbaac 100644 --- a/homeassistant/components/ps4/translations/sk.json +++ b/homeassistant/components/ps4/translations/sk.json @@ -2,9 +2,12 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "port_987_bind_error": "Nepodarilo sa naviaza\u0165 na port 987. \u010eal\u0161ie inform\u00e1cie n\u00e1jdete v [dokument\u00e1cii](https://www.home-assistant.io/components/ps4/).", + "port_997_bind_error": "Nepodarilo sa naviaza\u0165 na port 997. \u010eal\u0161ie inform\u00e1cie n\u00e1jdete v [dokument\u00e1cii](https://www.home-assistant.io/components/ps4/)." }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "credential_timeout": "\u010casov\u00fd limit slu\u017eby poveren\u00ed vypr\u0161al. Re\u0161tartujte stla\u010den\u00edm tla\u010didla Odosla\u0165.", "login_failed": "Nepodarilo sa sp\u00e1rova\u0165 s PlayStation 4. Overte, \u010di je PIN k\u00f3d spr\u00e1vny.", "no_ipaddress": "Zadajte IP adresa konzoly PlayStation 4, ktor\u00fa chcete nakonfigurova\u0165." diff --git a/homeassistant/components/pure_energie/translations/sk.json b/homeassistant/components/pure_energie/translations/sk.json index 7b8f057ac6a..42e2c99e2f3 100644 --- a/homeassistant/components/pure_energie/translations/sk.json +++ b/homeassistant/components/pure_energie/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "flow_title": "{model} ({host})", "step": { diff --git a/homeassistant/components/pushover/translations/sk.json b/homeassistant/components/pushover/translations/sk.json index af10343f303..cf835f57ac7 100644 --- a/homeassistant/components/pushover/translations/sk.json +++ b/homeassistant/components/pushover/translations/sk.json @@ -1,9 +1,11 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", "invalid_user_key": "Neplatn\u00fd k\u013e\u00fa\u010d pou\u017e\u00edvate\u013ea" }, @@ -11,11 +13,14 @@ "reauth_confirm": { "data": { "api_key": "API k\u013e\u00fa\u010d" - } + }, + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" + "api_key": "API k\u013e\u00fa\u010d", + "name": "N\u00e1zov", + "user_key": "Pou\u017e\u00edvate\u013esk\u00fd k\u013e\u00fa\u010d" } } } diff --git a/homeassistant/components/pvoutput/translations/sk.json b/homeassistant/components/pvoutput/translations/sk.json index f82a2e35a42..027281e1819 100644 --- a/homeassistant/components/pvoutput/translations/sk.json +++ b/homeassistant/components/pvoutput/translations/sk.json @@ -4,6 +4,7 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { diff --git a/homeassistant/components/qnap_qsw/translations/sk.json b/homeassistant/components/qnap_qsw/translations/sk.json index d3a803a0f68..d1536578450 100644 --- a/homeassistant/components/qnap_qsw/translations/sk.json +++ b/homeassistant/components/qnap_qsw/translations/sk.json @@ -5,18 +5,21 @@ "invalid_id": "Zariadenie vr\u00e1tilo neplatn\u00e9 jedine\u010dn\u00e9 ID" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { "discovered_connection": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "user": { "data": { "password": "Heslo", - "url": "URL" + "url": "URL", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/rachio/translations/sk.json b/homeassistant/components/rachio/translations/sk.json index 14ac4c6c068..89ef7514ac8 100644 --- a/homeassistant/components/rachio/translations/sk.json +++ b/homeassistant/components/rachio/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { diff --git a/homeassistant/components/radarr/translations/no.json b/homeassistant/components/radarr/translations/no.json index f94d72d610c..e74efdaf846 100644 --- a/homeassistant/components/radarr/translations/no.json +++ b/homeassistant/components/radarr/translations/no.json @@ -30,6 +30,10 @@ "deprecated_yaml": { "description": "Konfigurering av Radarr med YAML blir fjernet. \n\n Din eksisterende YAML-konfigurasjon har blitt importert til brukergrensesnittet automatisk. \n\n Fjern Radarr YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", "title": "Radarr YAML-konfigurasjonen blir fjernet" + }, + "removed_yaml": { + "description": "Konfigurering av Radarr med YAML er fjernet. \n\n Din eksisterende YAML-konfigurasjon brukes ikke av Home Assistant. \n\n Fjern YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", + "title": "Radarr YAML-konfigurasjonen er fjernet" } }, "options": { diff --git a/homeassistant/components/radarr/translations/sk.json b/homeassistant/components/radarr/translations/sk.json index 04ba52596e5..76e74a69f4a 100644 --- a/homeassistant/components/radarr/translations/sk.json +++ b/homeassistant/components/radarr/translations/sk.json @@ -4,14 +4,19 @@ "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "zeroconf_failed": "K\u013e\u00fa\u010d API sa nena\u0161iel. Zadajte ho ru\u010dne" }, "step": { + "reauth_confirm": { + "title": "Znova overi\u0165 integr\u00e1ciu" + }, "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", - "url": "URL" + "url": "URL", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/radio_browser/translations/sk.json b/homeassistant/components/radio_browser/translations/sk.json new file mode 100644 index 00000000000..c294bc45d7c --- /dev/null +++ b/homeassistant/components/radio_browser/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/radiotherm/translations/sk.json b/homeassistant/components/radiotherm/translations/sk.json index 060571686c4..4595dcba837 100644 --- a/homeassistant/components/radiotherm/translations/sk.json +++ b/homeassistant/components/radiotherm/translations/sk.json @@ -3,6 +3,10 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "flow_title": "{name} {model} ({host})", "step": { "confirm": { diff --git a/homeassistant/components/rainforest_eagle/translations/sk.json b/homeassistant/components/rainforest_eagle/translations/sk.json index c3d4ba60dd8..9307faaa051 100644 --- a/homeassistant/components/rainforest_eagle/translations/sk.json +++ b/homeassistant/components/rainforest_eagle/translations/sk.json @@ -4,12 +4,16 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "host": "Hostite\u013e" + "cloud_id": "Cloud ID", + "host": "Hostite\u013e", + "install_code": "In\u0161tala\u010dn\u00fd k\u00f3d" } } } diff --git a/homeassistant/components/rainmachine/translations/sk.json b/homeassistant/components/rainmachine/translations/sk.json index ebdda948792..351e0cf97be 100644 --- a/homeassistant/components/rainmachine/translations/sk.json +++ b/homeassistant/components/rainmachine/translations/sk.json @@ -13,7 +13,8 @@ "ip_address": "N\u00e1zov hostite\u013ea alebo IP adresa", "password": "Heslo", "port": "Port" - } + }, + "title": "Vypl\u0148te svoje \u00fadaje" } } } diff --git a/homeassistant/components/rdw/translations/sk.json b/homeassistant/components/rdw/translations/sk.json new file mode 100644 index 00000000000..d6fa5e791b4 --- /dev/null +++ b/homeassistant/components/rdw/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/sk.json b/homeassistant/components/recorder/translations/sk.json index 2e6c96765f9..69acec2ee46 100644 --- a/homeassistant/components/recorder/translations/sk.json +++ b/homeassistant/components/recorder/translations/sk.json @@ -1,7 +1,8 @@ { "system_health": { "info": { - "current_recorder_run": "Aktu\u00e1lny \u010das spustenia" + "current_recorder_run": "Aktu\u00e1lny \u010das spustenia", + "database_version": "Verzia datab\u00e1zy" } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/sk.json b/homeassistant/components/renault/translations/sk.json index facca5e688c..79e529d998a 100644 --- a/homeassistant/components/renault/translations/sk.json +++ b/homeassistant/components/renault/translations/sk.json @@ -12,7 +12,8 @@ "data": { "password": "Heslo" }, - "description": "Aktualizujte svoje heslo pre {username}" + "description": "Aktualizujte svoje heslo pre {username}", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { diff --git a/homeassistant/components/rfxtrx/translations/sk.json b/homeassistant/components/rfxtrx/translations/sk.json index b23f3bc47d2..83867537e65 100644 --- a/homeassistant/components/rfxtrx/translations/sk.json +++ b/homeassistant/components/rfxtrx/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + "already_configured": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { "setup_network": { @@ -16,6 +20,18 @@ "device": "Vyberte zariadenie" }, "title": "Zariadenie" + }, + "setup_serial_manual_path": { + "data": { + "device": "Cesta k zariadeniu USB" + }, + "title": "Cesta" + }, + "user": { + "data": { + "type": "Typ pripojenia" + }, + "title": "Vyberte typ pripojenia" } } }, @@ -31,12 +47,14 @@ }, "options": { "error": { - "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "prompt_options": { "data": { - "device": "Vyberte zariadenie, ktor\u00e9 chcete nakonfigurova\u0165" + "device": "Vyberte zariadenie, ktor\u00e9 chcete nakonfigurova\u0165", + "protocols": "Protokoly" } } } diff --git a/homeassistant/components/rhasspy/translations/sk.json b/homeassistant/components/rhasspy/translations/sk.json new file mode 100644 index 00000000000..c294bc45d7c --- /dev/null +++ b/homeassistant/components/rhasspy/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ring/translations/sk.json b/homeassistant/components/ring/translations/sk.json index 8f06d99549b..805e6449829 100644 --- a/homeassistant/components/ring/translations/sk.json +++ b/homeassistant/components/ring/translations/sk.json @@ -4,12 +4,19 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "2fa": { + "data": { + "2fa": "Dvojfaktorov\u00fd k\u00f3d" + } + }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/risco/translations/sk.json b/homeassistant/components/risco/translations/sk.json index a53e56c8354..b7c83c9709e 100644 --- a/homeassistant/components/risco/translations/sk.json +++ b/homeassistant/components/risco/translations/sk.json @@ -4,28 +4,37 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { "cloud": { "data": { "password": "Heslo", - "pin": "PIN k\u00f3d" + "pin": "PIN k\u00f3d", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "local": { "data": { "host": "Hostite\u013e", - "pin": "PIN k\u00f3d" + "pin": "PIN k\u00f3d", + "port": "Port" } } } }, "options": { "step": { + "init": { + "title": "Konfigur\u00e1cia mo\u017enost\u00ed" + }, "risco_to_ha": { "data": { - "A": "Skupina A" + "A": "Skupina A", + "B": "Skupina B", + "C": "Skupina C", + "D": "Skupina D" } } } diff --git a/homeassistant/components/rituals_perfume_genie/translations/sk.json b/homeassistant/components/rituals_perfume_genie/translations/sk.json index bfca61034ec..ab95d8060d4 100644 --- a/homeassistant/components/rituals_perfume_genie/translations/sk.json +++ b/homeassistant/components/rituals_perfume_genie/translations/sk.json @@ -4,7 +4,9 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/roku/translations/sk.json b/homeassistant/components/roku/translations/sk.json index 5d0572a9984..80f5e0b1cc7 100644 --- a/homeassistant/components/roku/translations/sk.json +++ b/homeassistant/components/roku/translations/sk.json @@ -2,7 +2,11 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "flow_title": "{name}", "step": { @@ -12,7 +16,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Zadajte svoje inform\u00e1cie o Roku." } } } diff --git a/homeassistant/components/roomba/translations/sk.json b/homeassistant/components/roomba/translations/sk.json index 102b76ebec0..946fbadf22a 100644 --- a/homeassistant/components/roomba/translations/sk.json +++ b/homeassistant/components/roomba/translations/sk.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "not_irobot_device": "Zisten\u00e9 zariadenie nie je zariadenie iRobot" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "flow_title": "{name} ({host})", "step": { diff --git a/homeassistant/components/roon/translations/sk.json b/homeassistant/components/roon/translations/sk.json index d38abd69c53..2d0aa74c391 100644 --- a/homeassistant/components/roon/translations/sk.json +++ b/homeassistant/components/roon/translations/sk.json @@ -4,12 +4,14 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "fallback": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "port": "Port" }, "description": "Server Roon sa nepodarilo objavi\u0165, zadajte n\u00e1zov hostite\u013ea a port." } diff --git a/homeassistant/components/rpi_power/translations/sk.json b/homeassistant/components/rpi_power/translations/sk.json index d19ecb22698..a0f761dd5dc 100644 --- a/homeassistant/components/rpi_power/translations/sk.json +++ b/homeassistant/components/rpi_power/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "no_devices_found": "Nie je mo\u017en\u00e9 n\u00e1js\u0165 syst\u00e9mov\u00fa triedu pre t\u00fato komponentu, uistite sa \u017ee v\u00e1\u0161 kernel je aktu\u00e1lny a hardv\u00e9r je podporovan\u00fd" + "no_devices_found": "Nie je mo\u017en\u00e9 n\u00e1js\u0165 syst\u00e9mov\u00fa triedu pre t\u00fato komponentu, uistite sa \u017ee v\u00e1\u0161 kernel je aktu\u00e1lny a hardv\u00e9r je podporovan\u00fd", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "step": { "confirm": { diff --git a/homeassistant/components/ruckus_unleashed/translations/sk.json b/homeassistant/components/ruckus_unleashed/translations/sk.json index e86b8339ba6..3ae0f994a0c 100644 --- a/homeassistant/components/ruckus_unleashed/translations/sk.json +++ b/homeassistant/components/ruckus_unleashed/translations/sk.json @@ -4,7 +4,9 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/sabnzbd/translations/sk.json b/homeassistant/components/sabnzbd/translations/sk.json index 7c6659c2e0b..d87df07af6d 100644 --- a/homeassistant/components/sabnzbd/translations/sk.json +++ b/homeassistant/components/sabnzbd/translations/sk.json @@ -1,12 +1,16 @@ { "config": { "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" }, "step": { "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" + "api_key": "API k\u013e\u00fa\u010d", + "name": "N\u00e1zov", + "path": "Cesta", + "url": "URL" } } } diff --git a/homeassistant/components/samsungtv/translations/sk.json b/homeassistant/components/samsungtv/translations/sk.json index 30592fe63ac..4be02aa40f4 100644 --- a/homeassistant/components/samsungtv/translations/sk.json +++ b/homeassistant/components/samsungtv/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "id_missing": "Toto zariadenie Samsung nem\u00e1 s\u00e9riov\u00e9 \u010d\u00edslo.", "not_supported": "Toto zariadenie Samsung moment\u00e1lne nie je podporovan\u00e9.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", diff --git a/homeassistant/components/schedule/translations/sk.json b/homeassistant/components/schedule/translations/sk.json new file mode 100644 index 00000000000..06788442e27 --- /dev/null +++ b/homeassistant/components/schedule/translations/sk.json @@ -0,0 +1,8 @@ +{ + "state": { + "_": { + "off": "Neakt\u00edvny", + "on": "Akt\u00edvny" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/sk.json b/homeassistant/components/scrape/translations/sk.json index 33439335185..f29805634e1 100644 --- a/homeassistant/components/scrape/translations/sk.json +++ b/homeassistant/components/scrape/translations/sk.json @@ -3,11 +3,15 @@ "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, + "error": { + "resource_error": "Nepodarilo sa aktualizova\u0165 ostatn\u00e9 \u00fadaje. Overte svoju konfigur\u00e1ciu" + }, "step": { "sensor": { "data": { "attribute": "Atrib\u00fat", "name": "N\u00e1zov", + "select": "Vyberte", "unit_of_measurement": "Jednotka merania", "value_template": "\u0160abl\u00f3na hodnoty" }, @@ -17,7 +21,12 @@ }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" + }, + "data_description": { + "timeout": "\u010casov\u00fd limit pre pripojenie k webovej str\u00e1nke" } } } @@ -26,7 +35,12 @@ "step": { "init": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" + }, + "data_description": { + "timeout": "\u010casov\u00fd limit pre pripojenie k webovej str\u00e1nke" } } } diff --git a/homeassistant/components/screenlogic/translations/sk.json b/homeassistant/components/screenlogic/translations/sk.json index e1d8590f264..88a6f2a1c76 100644 --- a/homeassistant/components/screenlogic/translations/sk.json +++ b/homeassistant/components/screenlogic/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{name}", "step": { "gateway_entry": { @@ -10,6 +13,11 @@ "ip_address": "IP adresa", "port": "Port" } + }, + "gateway_select": { + "data": { + "selected_gateway": "Gateway" + } } } } diff --git a/homeassistant/components/select/translations/sk.json b/homeassistant/components/select/translations/sk.json index 96441fb5f0d..220711deee0 100644 --- a/homeassistant/components/select/translations/sk.json +++ b/homeassistant/components/select/translations/sk.json @@ -6,5 +6,6 @@ "trigger_type": { "current_option_changed": "Mo\u017enos\u0165 {entity_name} sa zmenila" } - } + }, + "title": "Vyberte" } \ No newline at end of file diff --git a/homeassistant/components/sense/translations/sk.json b/homeassistant/components/sense/translations/sk.json index bca908bc0c2..1a1a1e64869 100644 --- a/homeassistant/components/sense/translations/sk.json +++ b/homeassistant/components/sense/translations/sk.json @@ -1,10 +1,13 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_validate": { diff --git a/homeassistant/components/senseme/translations/sk.json b/homeassistant/components/senseme/translations/sk.json index a161f40537c..03e144be233 100644 --- a/homeassistant/components/senseme/translations/sk.json +++ b/homeassistant/components/senseme/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165.", diff --git a/homeassistant/components/sensibo/translations/sk.json b/homeassistant/components/sensibo/translations/sk.json index 2aca92ef50f..cc38d3ef6f5 100644 --- a/homeassistant/components/sensibo/translations/sk.json +++ b/homeassistant/components/sensibo/translations/sk.json @@ -1,12 +1,15 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "incorrect_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d pre vybran\u00fd \u00fa\u010det", "invalid_auth": "Neplatn\u00e9 overenie", - "no_devices": "Nena\u0161li sa \u017eiadne zariadenia" + "no_devices": "Nena\u0161li sa \u017eiadne zariadenia", + "no_username": "Nepodarilo sa z\u00edska\u0165 pou\u017e\u00edvate\u013esk\u00e9 meno" }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/sentry/translations/sk.json b/homeassistant/components/sentry/translations/sk.json index 14ad46bdb6e..6be09774065 100644 --- a/homeassistant/components/sentry/translations/sk.json +++ b/homeassistant/components/sentry/translations/sk.json @@ -1,7 +1,11 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { - "bad_dsn": "Neplatn\u00e9 DSN" + "bad_dsn": "Neplatn\u00e9 DSN", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" } } } \ No newline at end of file diff --git a/homeassistant/components/senz/translations/sk.json b/homeassistant/components/senz/translations/sk.json index 1ec9120e295..63bb5647a49 100644 --- a/homeassistant/components/senz/translations/sk.json +++ b/homeassistant/components/senz/translations/sk.json @@ -2,7 +2,19 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", + "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu." + }, + "create_entry": { + "default": "\u00daspe\u0161ne overen\u00e9" + }, + "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + } } } } \ No newline at end of file diff --git a/homeassistant/components/sharkiq/translations/sk.json b/homeassistant/components/sharkiq/translations/sk.json index 5fa187bfe4c..76f7b6b566c 100644 --- a/homeassistant/components/sharkiq/translations/sk.json +++ b/homeassistant/components/sharkiq/translations/sk.json @@ -2,10 +2,14 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth": { diff --git a/homeassistant/components/shelly/translations/sk.json b/homeassistant/components/shelly/translations/sk.json index b40b3c0b06a..1a36257a3a5 100644 --- a/homeassistant/components/shelly/translations/sk.json +++ b/homeassistant/components/shelly/translations/sk.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", + "firmware_not_fully_provisioned": "Zariadenie nie je plne zabezpe\u010den\u00e9. Kontaktujte podporu spolo\u010dnosti Shelly", "invalid_auth": "Neplatn\u00e9 overenie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, @@ -54,5 +55,10 @@ "single_push": "{subtype} stla\u010den\u00e9 raz", "triple": "{subtype} stla\u010den\u00e9 trikr\u00e1t" } + }, + "options": { + "abort": { + "ble_unsupported": "Podpora Bluetooth vy\u017eaduje verziu firmv\u00e9ru {ble_min_version} alebo nov\u0161iu." + } } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/sk.json b/homeassistant/components/sia/translations/sk.json index ff32f90e682..4f1a4b7b38a 100644 --- a/homeassistant/components/sia/translations/sk.json +++ b/homeassistant/components/sia/translations/sk.json @@ -5,11 +5,13 @@ "invalid_account_length": "\u00da\u010det nem\u00e1 spr\u00e1vnu d\u013a\u017eku, mus\u00ed ma\u0165 od 3 do 16 znakov.", "invalid_key_format": "K\u013e\u00fa\u010d nie je hexadecim\u00e1lna hodnota, pou\u017e\u00edvajte len 0-9 a A-F.", "invalid_key_length": "K\u013e\u00fa\u010d nem\u00e1 spr\u00e1vnu d\u013a\u017eku, mus\u00ed ma\u0165 16, 24 alebo 32 hexadecim\u00e1lnych znakov.", - "invalid_ping": "Interval pingu mus\u00ed by\u0165 medzi 1 a 1440 min\u00fatami." + "invalid_ping": "Interval pingu mus\u00ed by\u0165 medzi 1 a 1440 min\u00fatami.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "additional_account": { "data": { + "account": "ID \u00fa\u010dtu", "ping_interval": "Ping Interval (min)" }, "title": "Pridajte \u010fal\u0161\u00ed \u00fa\u010det k aktu\u00e1lnemu portu." diff --git a/homeassistant/components/simplepush/translations/sk.json b/homeassistant/components/simplepush/translations/sk.json index 793f8eff278..6d26a95a166 100644 --- a/homeassistant/components/simplepush/translations/sk.json +++ b/homeassistant/components/simplepush/translations/sk.json @@ -2,6 +2,16 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "step": { + "user": { + "data": { + "name": "N\u00e1zov" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/sk.json b/homeassistant/components/simplisafe/translations/sk.json index c0e740e861a..93b939b88e1 100644 --- a/homeassistant/components/simplisafe/translations/sk.json +++ b/homeassistant/components/simplisafe/translations/sk.json @@ -6,7 +6,15 @@ }, "error": { "identifier_exists": "\u00da\u010det je u\u017e zaregistrovan\u00fd", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "auth_code": "Autoriza\u010dn\u00fd k\u00f3d" + } + } } }, "issues": { diff --git a/homeassistant/components/skybell/translations/sk.json b/homeassistant/components/skybell/translations/sk.json index e9eacedcc19..e3d6831b30d 100644 --- a/homeassistant/components/skybell/translations/sk.json +++ b/homeassistant/components/skybell/translations/sk.json @@ -1,17 +1,21 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { "data": { "password": "Heslo" }, - "description": "Aktualizujte svoje heslo pre {email}" + "description": "Aktualizujte svoje heslo pre {email}", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { diff --git a/homeassistant/components/slack/translations/sk.json b/homeassistant/components/slack/translations/sk.json index cb3801e953b..7d542d6c565 100644 --- a/homeassistant/components/slack/translations/sk.json +++ b/homeassistant/components/slack/translations/sk.json @@ -12,6 +12,7 @@ "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", + "default_channel": "Predvolen\u00fd kan\u00e1l", "icon": "Ikona", "username": "U\u017e\u00edvate\u013esk\u00e9 meno" }, diff --git a/homeassistant/components/sleepiq/translations/sk.json b/homeassistant/components/sleepiq/translations/sk.json index 6dbe2a76791..249ddf45926 100644 --- a/homeassistant/components/sleepiq/translations/sk.json +++ b/homeassistant/components/sleepiq/translations/sk.json @@ -4,17 +4,20 @@ "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/sma/translations/sk.json b/homeassistant/components/sma/translations/sk.json index 1e05cf728e0..0196a2c43f3 100644 --- a/homeassistant/components/sma/translations/sk.json +++ b/homeassistant/components/sma/translations/sk.json @@ -5,13 +5,18 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "cannot_retrieve_device_info": "\u00daspe\u0161ne pripojen\u00e9, ale nie je mo\u017en\u00e9 z\u00edska\u0165 inform\u00e1cie o zariaden\u00ed", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { "user": { "data": { + "group": "Skupina", "host": "Hostite\u013e", - "password": "Heslo" + "password": "Heslo", + "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/smappee/translations/sk.json b/homeassistant/components/smappee/translations/sk.json index 3891c37bb35..d135a72ae63 100644 --- a/homeassistant/components/smappee/translations/sk.json +++ b/homeassistant/components/smappee/translations/sk.json @@ -2,7 +2,10 @@ "config": { "abort": { "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "invalid_mdns": "Nepodporovan\u00e9 zariadenie pre integr\u00e1ciu Smappee." + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_mdns": "Nepodporovan\u00e9 zariadenie pre integr\u00e1ciu Smappee.", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})" }, "flow_title": "{name}", "step": { @@ -10,6 +13,9 @@ "data": { "host": "Hostite\u013e" } + }, + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" } } } diff --git a/homeassistant/components/smart_meter_texas/translations/sk.json b/homeassistant/components/smart_meter_texas/translations/sk.json index 8f06d99549b..0c9a112e32e 100644 --- a/homeassistant/components/smart_meter_texas/translations/sk.json +++ b/homeassistant/components/smart_meter_texas/translations/sk.json @@ -4,12 +4,15 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/smarttub/translations/sk.json b/homeassistant/components/smarttub/translations/sk.json index 610179dfac5..ebc9c699c89 100644 --- a/homeassistant/components/smarttub/translations/sk.json +++ b/homeassistant/components/smarttub/translations/sk.json @@ -8,12 +8,16 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "reauth_confirm": { + "title": "Znova overi\u0165 integr\u00e1ciu" + }, "user": { "data": { "email": "Email", "password": "Heslo" }, - "description": "Na prihl\u00e1senie zadajte svoju e-mailov\u00fa adresu a heslo SmartTub" + "description": "Na prihl\u00e1senie zadajte svoju e-mailov\u00fa adresu a heslo SmartTub", + "title": "Prihl\u00e1si\u0165" } } } diff --git a/homeassistant/components/sms/translations/sk.json b/homeassistant/components/sms/translations/sk.json index 2978b5d4fa3..848d127c43a 100644 --- a/homeassistant/components/sms/translations/sk.json +++ b/homeassistant/components/sms/translations/sk.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "R\u00fdchlos\u0165 prenosu", "device": "Zariadenie" }, "title": "Pripojte sa k modemu" diff --git a/homeassistant/components/solaredge/translations/sk.json b/homeassistant/components/solaredge/translations/sk.json index f960764ffba..c954ca5b347 100644 --- a/homeassistant/components/solaredge/translations/sk.json +++ b/homeassistant/components/solaredge/translations/sk.json @@ -11,7 +11,8 @@ "step": { "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" + "api_key": "API k\u013e\u00fa\u010d", + "name": "N\u00e1zov tejto in\u0161tal\u00e1cie" } } } diff --git a/homeassistant/components/solarlog/translations/sk.json b/homeassistant/components/solarlog/translations/sk.json index cafa14fff99..35dd47181e4 100644 --- a/homeassistant/components/solarlog/translations/sk.json +++ b/homeassistant/components/solarlog/translations/sk.json @@ -4,7 +4,8 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { "user": { diff --git a/homeassistant/components/solax/translations/sk.json b/homeassistant/components/solax/translations/sk.json index fc02a749e8d..ba1e9330c9b 100644 --- a/homeassistant/components/solax/translations/sk.json +++ b/homeassistant/components/solax/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/soma/translations/sk.json b/homeassistant/components/soma/translations/sk.json index 722e55b6294..5afd4e7cbb8 100644 --- a/homeassistant/components/soma/translations/sk.json +++ b/homeassistant/components/soma/translations/sk.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_setup": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", - "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "connection_error": "Nepodarilo sa pripoji\u0165" }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" diff --git a/homeassistant/components/somfy/translations/sk.json b/homeassistant/components/somfy/translations/sk.json index 91803ae5155..bd4f9111c68 100644 --- a/homeassistant/components/somfy/translations/sk.json +++ b/homeassistant/components/somfy/translations/sk.json @@ -1,10 +1,18 @@ { "config": { "abort": { - "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" + }, + "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + } } } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/sk.json b/homeassistant/components/somfy_mylink/translations/sk.json index cd214d8dac2..8fccd88d983 100644 --- a/homeassistant/components/somfy_mylink/translations/sk.json +++ b/homeassistant/components/somfy_mylink/translations/sk.json @@ -4,17 +4,25 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{mac} ({ip})", "step": { "user": { "data": { "host": "Hostite\u013e", - "port": "Port" + "port": "Port", + "system_id": "ID syst\u00e9mu" }, "description": "Syst\u00e9mov\u00e9 ID je mo\u017en\u00e9 z\u00edska\u0165 v aplik\u00e1cii MyLink v \u010dasti Integr\u00e1cia v\u00fdberom akejko\u013evek inej ne\u017e cloudovej slu\u017eby." } } + }, + "options": { + "abort": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + } } } \ No newline at end of file diff --git a/homeassistant/components/sonarr/translations/sk.json b/homeassistant/components/sonarr/translations/sk.json index 5c8678ab195..23b61b9c00f 100644 --- a/homeassistant/components/sonarr/translations/sk.json +++ b/homeassistant/components/sonarr/translations/sk.json @@ -2,17 +2,23 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "flow_title": "{name}", "step": { + "reauth_confirm": { + "title": "Znova overi\u0165 integr\u00e1ciu" + }, "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", - "url": "URL" + "url": "URL", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/songpal/translations/sk.json b/homeassistant/components/songpal/translations/sk.json index 09de84115f3..a81b795afc4 100644 --- a/homeassistant/components/songpal/translations/sk.json +++ b/homeassistant/components/songpal/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{name} ({host})", "step": { "init": { diff --git a/homeassistant/components/sonos/translations/sk.json b/homeassistant/components/sonos/translations/sk.json index 69d8d942494..99798036ffd 100644 --- a/homeassistant/components/sonos/translations/sk.json +++ b/homeassistant/components/sonos/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." } } } \ No newline at end of file diff --git a/homeassistant/components/soundtouch/translations/sk.json b/homeassistant/components/soundtouch/translations/sk.json index 3c3d27a6689..c8e33ffce5e 100644 --- a/homeassistant/components/soundtouch/translations/sk.json +++ b/homeassistant/components/soundtouch/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/speedtestdotnet/translations/sk.json b/homeassistant/components/speedtestdotnet/translations/sk.json index c96ae922bbb..a74820e4d4f 100644 --- a/homeassistant/components/speedtestdotnet/translations/sk.json +++ b/homeassistant/components/speedtestdotnet/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "step": { "user": { "description": "Chcete za\u010da\u0165 nastavova\u0165?" diff --git a/homeassistant/components/spider/translations/sk.json b/homeassistant/components/spider/translations/sk.json index 1b1e671c054..52f9f59ae60 100644 --- a/homeassistant/components/spider/translations/sk.json +++ b/homeassistant/components/spider/translations/sk.json @@ -1,12 +1,17 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/spotify/translations/sk.json b/homeassistant/components/spotify/translations/sk.json index bd8a83c2e34..dc3096178d5 100644 --- a/homeassistant/components/spotify/translations/sk.json +++ b/homeassistant/components/spotify/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." }, + "create_entry": { + "default": "\u00daspe\u0161ne overen\u00e9 v slu\u017ebe Spotify." + }, "step": { "pick_implementation": { "title": "Vyberte met\u00f3du overenia" diff --git a/homeassistant/components/sql/translations/sk.json b/homeassistant/components/sql/translations/sk.json index 115ad51719c..1db0d01443c 100644 --- a/homeassistant/components/sql/translations/sk.json +++ b/homeassistant/components/sql/translations/sk.json @@ -10,7 +10,9 @@ "step": { "user": { "data": { + "column": "St\u013apec", "db_url": "URL datab\u00e1zy", + "name": "N\u00e1zov", "unit_of_measurement": "Mern\u00e1 jednotka" }, "data_description": { @@ -28,7 +30,9 @@ "step": { "init": { "data": { + "column": "St\u013apec", "db_url": "URL datab\u00e1zy", + "name": "N\u00e1zov", "unit_of_measurement": "Mern\u00e1 jednotka" }, "data_description": { diff --git a/homeassistant/components/squeezebox/translations/sk.json b/homeassistant/components/squeezebox/translations/sk.json index b3bce3f6d95..59f8b706503 100644 --- a/homeassistant/components/squeezebox/translations/sk.json +++ b/homeassistant/components/squeezebox/translations/sk.json @@ -4,7 +4,9 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{host}", "step": { @@ -12,8 +14,10 @@ "data": { "host": "Hostite\u013e", "password": "Heslo", - "port": "Port" - } + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Upravte inform\u00e1cie o pripojen\u00ed" }, "user": { "data": { diff --git a/homeassistant/components/srp_energy/translations/sk.json b/homeassistant/components/srp_energy/translations/sk.json index 94d7b1fcdc6..23ac0bfe739 100644 --- a/homeassistant/components/srp_energy/translations/sk.json +++ b/homeassistant/components/srp_energy/translations/sk.json @@ -1,14 +1,20 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_account": "ID \u00fa\u010dtu by malo by\u0165 9-miestne \u010d\u00edslo", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { "id": "ID \u00fa\u010dtu", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/starline/translations/sk.json b/homeassistant/components/starline/translations/sk.json index de6f0da2dc8..79b2db36bb4 100644 --- a/homeassistant/components/starline/translations/sk.json +++ b/homeassistant/components/starline/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "error": { + "error_auth_mfa": "Nespr\u00e1vny k\u00f3d", "error_auth_user": "Nespr\u00e1vne u\u017e\u00edvate\u013esk\u00e9 meno alebo heslo" }, "step": { @@ -9,14 +10,21 @@ "app_id": "ID aplik\u00e1cie" } }, + "auth_captcha": { + "description": "{captcha_img}", + "title": "Captcha" + }, "auth_mfa": { "data": { "mfa_code": "SMS k\u00f3d" - } + }, + "description": "Zadajte k\u00f3d odoslan\u00fd na telef\u00f3n {phone_number}", + "title": "Dvojfaktorov\u00e1 autoriz\u00e1cia" }, "auth_user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/steam_online/translations/sk.json b/homeassistant/components/steam_online/translations/sk.json index 63d3be9f5ed..ef684e5b4ca 100644 --- a/homeassistant/components/steam_online/translations/sk.json +++ b/homeassistant/components/steam_online/translations/sk.json @@ -1,11 +1,14 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_account": "Neplatn\u00e9 ID \u00fa\u010dtu", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/steamist/translations/sk.json b/homeassistant/components/steamist/translations/sk.json index 88164bb680c..65a40c229d6 100644 --- a/homeassistant/components/steamist/translations/sk.json +++ b/homeassistant/components/steamist/translations/sk.json @@ -3,8 +3,12 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{name} ({ipaddress})", "step": { "discovery_confirm": { diff --git a/homeassistant/components/subaru/translations/sk.json b/homeassistant/components/subaru/translations/sk.json index 7d6292fcc12..d3a5c8afd3a 100644 --- a/homeassistant/components/subaru/translations/sk.json +++ b/homeassistant/components/subaru/translations/sk.json @@ -1,11 +1,13 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "error": { "bad_pin_format": "PIN by mal ma\u0165 4 \u010d\u00edslice", "bad_validation_code_format": "Overovac\u00ed k\u00f3d by mal ma\u0165 6 \u010d\u00edslic", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "incorrect_pin": "Nespr\u00e1vny PIN", "incorrect_validation_code": "Nespr\u00e1vny overovac\u00ed k\u00f3d", "invalid_auth": "Neplatn\u00e9 overenie" @@ -16,6 +18,9 @@ "pin": "PIN" } }, + "two_factor": { + "description": "Vy\u017eaduje sa dvojfaktorov\u00e9 overenie" + }, "two_factor_validate": { "data": { "validation_code": "Overovac\u00ed k\u00f3d" @@ -24,7 +29,9 @@ }, "user": { "data": { - "password": "Heslo" + "country": "Vyberte krajinu", + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/sun/translations/sk.json b/homeassistant/components/sun/translations/sk.json index b46baf83a1e..e7cabacaf10 100644 --- a/homeassistant/components/sun/translations/sk.json +++ b/homeassistant/components/sun/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "step": { "user": { "description": "Chcete za\u010da\u0165 nastavova\u0165?" diff --git a/homeassistant/components/surepetcare/translations/sk.json b/homeassistant/components/surepetcare/translations/sk.json index d50ce04d74a..5d12e6d8fe7 100644 --- a/homeassistant/components/surepetcare/translations/sk.json +++ b/homeassistant/components/surepetcare/translations/sk.json @@ -4,12 +4,15 @@ "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/switch_as_x/translations/sk.json b/homeassistant/components/switch_as_x/translations/sk.json index ce94aced1f9..64f2700a3fb 100644 --- a/homeassistant/components/switch_as_x/translations/sk.json +++ b/homeassistant/components/switch_as_x/translations/sk.json @@ -3,7 +3,8 @@ "step": { "user": { "data": { - "entity_id": "Prep\u00edna\u010d" + "entity_id": "Prep\u00edna\u010d", + "target_domain": "Nov\u00fd typ" } } } diff --git a/homeassistant/components/switch_as_x/translations/sv.json b/homeassistant/components/switch_as_x/translations/sv.json index ed9da614178..5f3ceb35165 100644 --- a/homeassistant/components/switch_as_x/translations/sv.json +++ b/homeassistant/components/switch_as_x/translations/sv.json @@ -6,7 +6,7 @@ "entity_id": "Brytare", "target_domain": "Typ" }, - "description": "V\u00e4lj en str\u00f6mbrytare som du vill visa i Home Assistant som lampa, skal eller n\u00e5got annat. Den ursprungliga switchen kommer att d\u00f6ljas." + "description": "V\u00e4lj en brytare som du vill visa i Home Assistant som lampa, gardin eller n\u00e5got annat. Den ursprungliga brytaren kommer att d\u00f6ljas." } } }, diff --git a/homeassistant/components/switchbot/translations/sk.json b/homeassistant/components/switchbot/translations/sk.json index 51b7fcdba41..20403a167ff 100644 --- a/homeassistant/components/switchbot/translations/sk.json +++ b/homeassistant/components/switchbot/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "no_unconfigured_devices": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, diff --git a/homeassistant/components/syncthing/translations/sk.json b/homeassistant/components/syncthing/translations/sk.json index 644bf0f55e1..18c5c12cd5e 100644 --- a/homeassistant/components/syncthing/translations/sk.json +++ b/homeassistant/components/syncthing/translations/sk.json @@ -4,12 +4,15 @@ "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { "user": { "data": { - "url": "URL" + "token": "Token", + "url": "URL", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/syncthru/translations/sk.json b/homeassistant/components/syncthru/translations/sk.json index 70a9f36e83f..5b20b432a32 100644 --- a/homeassistant/components/syncthru/translations/sk.json +++ b/homeassistant/components/syncthru/translations/sk.json @@ -5,6 +5,7 @@ }, "error": { "invalid_url": "Neplatn\u00e1 adresa URL", + "syncthru_not_supported": "Zariadenie nepodporuje SyncThru", "unknown_state": "Nezn\u00e1my stav tla\u010diarne, overte URL a sie\u0165ov\u00e9 pripojenie" }, "flow_title": "{name}", diff --git a/homeassistant/components/synology_dsm/translations/sk.json b/homeassistant/components/synology_dsm/translations/sk.json index ba7397614cb..a530e78e9b8 100644 --- a/homeassistant/components/synology_dsm/translations/sk.json +++ b/homeassistant/components/synology_dsm/translations/sk.json @@ -5,27 +5,42 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({host})", "step": { + "2sa": { + "data": { + "otp_code": "K\u00f3d" + } + }, "link": { "data": { "password": "Heslo", - "port": "Port" + "port": "Port", + "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" }, "description": "Chcete nastavi\u0165 {name} ({host})?" }, "reauth_confirm": { "data": { - "password": "Heslo" - } + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Synology DSM Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { "host": "Hostite\u013e", "password": "Heslo", - "port": "Port" + "port": "Port", + "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/tado/translations/sk.json b/homeassistant/components/tado/translations/sk.json index 8f06d99549b..1ed85afb928 100644 --- a/homeassistant/components/tado/translations/sk.json +++ b/homeassistant/components/tado/translations/sk.json @@ -4,13 +4,15 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { "user": { "data": { "password": "Heslo" - } + }, + "title": "Pripojte sa k svojmu \u00fa\u010dtu Tado" } } } diff --git a/homeassistant/components/tailscale/translations/sk.json b/homeassistant/components/tailscale/translations/sk.json index 4eba3bdc8bb..b09983030d2 100644 --- a/homeassistant/components/tailscale/translations/sk.json +++ b/homeassistant/components/tailscale/translations/sk.json @@ -4,6 +4,7 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { diff --git a/homeassistant/components/tankerkoenig/translations/sk.json b/homeassistant/components/tankerkoenig/translations/sk.json index 52934315c68..0be1d37c18b 100644 --- a/homeassistant/components/tankerkoenig/translations/sk.json +++ b/homeassistant/components/tankerkoenig/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" @@ -12,9 +13,15 @@ "api_key": "API k\u013e\u00fa\u010d" } }, + "select_station": { + "data": { + "stations": "Stanice" + } + }, "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", + "location": "Umiestnenie", "radius": "Polomer vyh\u013ead\u00e1vania" } } diff --git a/homeassistant/components/tasmota/translations/sk.json b/homeassistant/components/tasmota/translations/sk.json new file mode 100644 index 00000000000..c294bc45d7c --- /dev/null +++ b/homeassistant/components/tasmota/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/sk.json b/homeassistant/components/tautulli/translations/sk.json index 9cb2290463a..769b6125412 100644 --- a/homeassistant/components/tautulli/translations/sk.json +++ b/homeassistant/components/tautulli/translations/sk.json @@ -1,10 +1,13 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { @@ -15,7 +18,8 @@ "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", - "url": "URL" + "url": "URL", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/tesla_wall_connector/translations/sk.json b/homeassistant/components/tesla_wall_connector/translations/sk.json index 951b68ac4c7..336708e4a3c 100644 --- a/homeassistant/components/tesla_wall_connector/translations/sk.json +++ b/homeassistant/components/tesla_wall_connector/translations/sk.json @@ -3,6 +3,10 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "flow_title": "{serial_number} ({host})", "step": { "user": { diff --git a/homeassistant/components/text/translations/fr.json b/homeassistant/components/text/translations/fr.json new file mode 100644 index 00000000000..cdb99210efe --- /dev/null +++ b/homeassistant/components/text/translations/fr.json @@ -0,0 +1,3 @@ +{ + "title": "Texte" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/no.json b/homeassistant/components/text/translations/no.json new file mode 100644 index 00000000000..6ac4d0d7b31 --- /dev/null +++ b/homeassistant/components/text/translations/no.json @@ -0,0 +1,3 @@ +{ + "title": "Tekst" +} \ No newline at end of file diff --git a/homeassistant/components/tibber/translations/sk.json b/homeassistant/components/tibber/translations/sk.json index d9a0b7c1426..bb83eca3402 100644 --- a/homeassistant/components/tibber/translations/sk.json +++ b/homeassistant/components/tibber/translations/sk.json @@ -4,7 +4,9 @@ "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" }, "error": { - "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", + "timeout": "Pri prip\u00e1jan\u00ed k Tibberu vypr\u0161al \u010dasov\u00fd limit" }, "step": { "user": { diff --git a/homeassistant/components/timer/translations/de.json b/homeassistant/components/timer/translations/de.json index ba24845aadb..ab36cdd7e93 100644 --- a/homeassistant/components/timer/translations/de.json +++ b/homeassistant/components/timer/translations/de.json @@ -2,7 +2,7 @@ "state": { "_": { "active": "Aktiv", - "idle": "Unt\u00e4tig", + "idle": "inaktiv", "paused": "Pausiert" } } diff --git a/homeassistant/components/tod/translations/sk.json b/homeassistant/components/tod/translations/sk.json new file mode 100644 index 00000000000..af15f92c2f2 --- /dev/null +++ b/homeassistant/components/tod/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "N\u00e1zov" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/sk.json b/homeassistant/components/tolo/translations/sk.json index 9a90be4aa45..dc2e106293a 100644 --- a/homeassistant/components/tolo/translations/sk.json +++ b/homeassistant/components/tolo/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{name}", "step": { "confirm": { diff --git a/homeassistant/components/tomorrowio/translations/sk.json b/homeassistant/components/tomorrowio/translations/sk.json index adcf697b1c2..15048d51d1e 100644 --- a/homeassistant/components/tomorrowio/translations/sk.json +++ b/homeassistant/components/tomorrowio/translations/sk.json @@ -1,13 +1,16 @@ { "config": { "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", - "rate_limited": "Moment\u00e1lne je r\u00fdchlos\u0165 obmedzen\u00e1, sk\u00faste to pros\u00edm nesk\u00f4r." + "rate_limited": "Moment\u00e1lne je r\u00fdchlos\u0165 obmedzen\u00e1, sk\u00faste to pros\u00edm nesk\u00f4r.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", + "location": "Umiestnenie", "name": "Meno" }, "description": "Ak chcete z\u00edska\u0165 k\u013e\u00fa\u010d API, zaregistrujte sa na [Tomorrow.io] (https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/toon/translations/sk.json b/homeassistant/components/toon/translations/sk.json index e5fd9d02aff..7ff07bff835 100644 --- a/homeassistant/components/toon/translations/sk.json +++ b/homeassistant/components/toon/translations/sk.json @@ -3,12 +3,17 @@ "abort": { "already_configured": "Vybran\u00e1 dohoda je u\u017e nakonfigurovan\u00e1.", "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", "unknown_authorize_url_generation": "Nezn\u00e1ma chyba pri generovan\u00ed autorizovanej adresy URL." }, "step": { "agreement": { - "description": "Vyberte adresu zmluvy, ktor\u00fa chcete prida\u0165." + "data": { + "agreement": "Dohoda" + }, + "description": "Vyberte adresu zmluvy, ktor\u00fa chcete prida\u0165.", + "title": "Vyberte svoju dohodu" } } } diff --git a/homeassistant/components/totalconnect/translations/sk.json b/homeassistant/components/totalconnect/translations/sk.json index 7643b634843..e3b18794804 100644 --- a/homeassistant/components/totalconnect/translations/sk.json +++ b/homeassistant/components/totalconnect/translations/sk.json @@ -5,12 +5,23 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "usercode": "Pou\u017e\u00edvate\u013esk\u00fd k\u00f3d nie je platn\u00fd pre tohto pou\u017e\u00edvate\u013ea na tomto mieste" }, "step": { + "locations": { + "data": { + "usercode": "Pou\u017e\u00edvate\u013esk\u00fd k\u00f3d" + }, + "description": "Zadajte pou\u017e\u00edvate\u013esk\u00fd k\u00f3d pre tohto pou\u017e\u00edvate\u013ea na adrese {location_id}" + }, + "reauth_confirm": { + "title": "Znova overi\u0165 integr\u00e1ciu" + }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/tplink/translations/sk.json b/homeassistant/components/tplink/translations/sk.json index a68579f2bf8..db96518e108 100644 --- a/homeassistant/components/tplink/translations/sk.json +++ b/homeassistant/components/tplink/translations/sk.json @@ -4,6 +4,9 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "flow_title": "{name} {model} ({host})", "step": { "discovery_confirm": { diff --git a/homeassistant/components/traccar/translations/sk.json b/homeassistant/components/traccar/translations/sk.json new file mode 100644 index 00000000000..933f73976d2 --- /dev/null +++ b/homeassistant/components/traccar/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/sk.json b/homeassistant/components/tractive/translations/sk.json index 7159735b925..6a01efb5eb7 100644 --- a/homeassistant/components/tractive/translations/sk.json +++ b/homeassistant/components/tractive/translations/sk.json @@ -5,7 +5,8 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/tradfri/translations/sk.json b/homeassistant/components/tradfri/translations/sk.json index 1b796109322..55ad1f9e49d 100644 --- a/homeassistant/components/tradfri/translations/sk.json +++ b/homeassistant/components/tradfri/translations/sk.json @@ -4,11 +4,17 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, + "error": { + "cannot_authenticate": "Nemo\u017eno overi\u0165, je br\u00e1na sp\u00e1rovan\u00e1 s in\u00fdm serverom, ako je napr\u00edklad Homekit?", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "auth": { "data": { - "host": "Hostite\u013e" - } + "host": "Hostite\u013e", + "security_code": "Bezpe\u010dnostn\u00fd k\u00f3d" + }, + "title": "Zadajte bezpe\u010dnostn\u00fd k\u00f3d" } } } diff --git a/homeassistant/components/trafikverket_ferry/translations/sk.json b/homeassistant/components/trafikverket_ferry/translations/sk.json index c6af795c788..6c2b006092a 100644 --- a/homeassistant/components/trafikverket_ferry/translations/sk.json +++ b/homeassistant/components/trafikverket_ferry/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "incorrect_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d pre vybran\u00fd \u00fa\u010det", "invalid_auth": "Neplatn\u00e9 overenie" }, @@ -16,6 +17,7 @@ "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", + "time": "\u010cas", "weekday": "Pracovn\u00e9 dni" } } diff --git a/homeassistant/components/trafikverket_train/translations/sk.json b/homeassistant/components/trafikverket_train/translations/sk.json index 4d320031844..f04bf4d5e32 100644 --- a/homeassistant/components/trafikverket_train/translations/sk.json +++ b/homeassistant/components/trafikverket_train/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "incorrect_api_key": "Neplatn\u00fd k\u013e\u00fa\u010d API pre vybran\u00e9 konto" }, "step": { diff --git a/homeassistant/components/trafikverket_weatherstation/translations/sk.json b/homeassistant/components/trafikverket_weatherstation/translations/sk.json index 1981cb3a60b..46e0695e20d 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/sk.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/sk.json @@ -4,12 +4,16 @@ "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_station": "Nepodarilo sa n\u00e1js\u0165 meteorologick\u00fa stanicu so zadan\u00fdm n\u00e1zvom", + "more_stations": "Na\u0161lo sa viacero meteorologick\u00fdch stan\u00edc so zadan\u00fdm n\u00e1zvom" }, "step": { "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" + "api_key": "API k\u013e\u00fa\u010d", + "station": "Stanica" } } } diff --git a/homeassistant/components/transmission/translations/sk.json b/homeassistant/components/transmission/translations/sk.json index 4e8f7da9460..178b0043359 100644 --- a/homeassistant/components/transmission/translations/sk.json +++ b/homeassistant/components/transmission/translations/sk.json @@ -1,9 +1,11 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "name_exists": "N\u00e1zov u\u017e existuje" }, @@ -12,14 +14,16 @@ "data": { "password": "Heslo" }, - "description": "Heslo pre {username} je neplatn\u00e9." + "description": "Heslo pre {username} je neplatn\u00e9.", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { "host": "Hostite\u013e", "name": "N\u00e1zov", "password": "Heslo", - "port": "Port" + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/tuya/translations/select.sk.json b/homeassistant/components/tuya/translations/select.sk.json index fe62d98b32a..94f4d8ec464 100644 --- a/homeassistant/components/tuya/translations/select.sk.json +++ b/homeassistant/components/tuya/translations/select.sk.json @@ -1,29 +1,65 @@ { "state": { + "tuya__basic_anti_flickr": { + "0": "Zak\u00e1zan\u00e9", + "1": "50 Hz", + "2": "60 Hz" + }, + "tuya__basic_nightvision": { + "1": "Neakt\u00edvny", + "2": "Akt\u00edvny" + }, "tuya__countdown": { "1h": "1 hodina", "2h": "2 hodiny", "3h": "3 hodiny", "4h": "4 hodiny", "5h": "5 hod\u00edn", - "6h": "6 hod\u00edn" + "6h": "6 hod\u00edn", + "cancel": "Zru\u0161i\u0165" + }, + "tuya__curtain_mode": { + "morning": "R\u00e1no", + "night": "Noc" + }, + "tuya__curtain_motor_mode": { + "back": "Sp\u00e4\u0165", + "forward": "Dopredu" }, "tuya__decibel_sensitivity": { "1": "Vysok\u00e1 citlivos\u0165" }, + "tuya__fan_angle": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + }, "tuya__fingerbot_mode": { "switch": "Prep\u00edna\u010d" }, "tuya__humidifier_spray_mode": { "humidity": "Vlhkos\u0165" }, + "tuya__led_type": { + "halogen": "Halog\u00e9n", + "incandescent": "\u017diarovka", + "led": "LED" + }, + "tuya__light_mode": { + "none": "Neakt\u00edvny" + }, "tuya__motion_sensitivity": { "0": "N\u00edzka citlivos\u0165", "1": "Stredn\u00e1 citlivos\u0165", "2": "Vysok\u00e1 citlivos\u0165" }, "tuya__relay_status": { - "memory": "Zapam\u00e4ta\u0165 posledn\u00fd stav" + "last": "Zapam\u00e4ta\u0165 posledn\u00fd stav", + "memory": "Zapam\u00e4ta\u0165 posledn\u00fd stav", + "off": "Neakt\u00edvny", + "on": "Akt\u00edvny", + "power_off": "Neakt\u00edvny", + "power_on": "Akt\u00edvny" }, "tuya__vacuum_mode": { "random": "N\u00e1hodn\u00fd" diff --git a/homeassistant/components/twentemilieu/translations/sk.json b/homeassistant/components/twentemilieu/translations/sk.json index 75368faa73d..41a4042d8b3 100644 --- a/homeassistant/components/twentemilieu/translations/sk.json +++ b/homeassistant/components/twentemilieu/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_address": "Adresa sa nena\u0161la v servisnej oblasti Twente Milieu." } } diff --git a/homeassistant/components/twilio/translations/sk.json b/homeassistant/components/twilio/translations/sk.json index 473cb1c2fa0..04cb32a1c4e 100644 --- a/homeassistant/components/twilio/translations/sk.json +++ b/homeassistant/components/twilio/translations/sk.json @@ -1,5 +1,10 @@ { "config": { + "abort": { + "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + }, "step": { "user": { "description": "Chcete za\u010da\u0165 nastavova\u0165?" diff --git a/homeassistant/components/twinkly/translations/sk.json b/homeassistant/components/twinkly/translations/sk.json index e29eb15ea62..297d67d205d 100644 --- a/homeassistant/components/twinkly/translations/sk.json +++ b/homeassistant/components/twinkly/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "device_exists": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "discovery_confirm": { "description": "Chcete nastavi\u0165 {name} - {model} ({host})?" diff --git a/homeassistant/components/ukraine_alarm/translations/sk.json b/homeassistant/components/ukraine_alarm/translations/sk.json index 529d2911584..04a10161b8b 100644 --- a/homeassistant/components/ukraine_alarm/translations/sk.json +++ b/homeassistant/components/ukraine_alarm/translations/sk.json @@ -1,7 +1,27 @@ { "config": { "abort": { - "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "timeout": "\u010casov\u00fd limit na nadviazanie spojenia", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "community": { + "data": { + "region": "Regi\u00f3n" + } + }, + "district": { + "data": { + "region": "Regi\u00f3n" + } + }, + "user": { + "data": { + "region": "Regi\u00f3n" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/unifi/translations/sk.json b/homeassistant/components/unifi/translations/sk.json index 26b0ac2d57f..e19794c773a 100644 --- a/homeassistant/components/unifi/translations/sk.json +++ b/homeassistant/components/unifi/translations/sk.json @@ -15,7 +15,8 @@ "host": "Hostite\u013e", "password": "Heslo", "port": "Port", - "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/unifiprotect/translations/de.json b/homeassistant/components/unifiprotect/translations/de.json index 843bb839046..5e0dfcb650f 100644 --- a/homeassistant/components/unifiprotect/translations/de.json +++ b/homeassistant/components/unifiprotect/translations/de.json @@ -42,6 +42,10 @@ } }, "issues": { + "deprecate_smart_sensor": { + "description": "Der einheitliche Sensor \u201eErkanntes Objekt\u201c f\u00fcr intelligente Erkennungen ist jetzt veraltet. Er wurde durch einzelne bin\u00e4re Smart-Detection-Sensoren f\u00fcr jeden Smart-Detection-Typ ersetzt. Bitte aktualisiere alle Vorlagen oder Automatisierungen entsprechend.", + "title": "Smart Detection Sensor veraltet" + }, "ea_setup_failed": { "description": "Du verwendest v{version} von UniFi Protect, eine Early-Access-Version. Beim Versuch, die Integration zu laden, ist ein nicht behebbarer Fehler aufgetreten. Bitte f\u00fchre ein [Downgrade auf eine stabile Version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) von UniFi Protect durch, um die Integration weiterhin zu verwenden. \n\nFehler: {error}", "title": "Einrichtungsfehler bei Verwendung der Early-Access-Version" diff --git a/homeassistant/components/unifiprotect/translations/es.json b/homeassistant/components/unifiprotect/translations/es.json index c6e9d9ea7eb..f73a5db7c8b 100644 --- a/homeassistant/components/unifiprotect/translations/es.json +++ b/homeassistant/components/unifiprotect/translations/es.json @@ -42,6 +42,10 @@ } }, "issues": { + "deprecate_smart_sensor": { + "description": "El sensor unificado \"Objeto detectado\" para detecciones inteligentes ahora est\u00e1 obsoleto. Ha sido reemplazado por sensores binarios de detecci\u00f3n inteligente individuales para cada tipo de detecci\u00f3n inteligente. Actualiza cualquier plantilla o automatizaci\u00f3n en consecuencia.", + "title": "Sensor de detecci\u00f3n inteligente obsoleto" + }, "ea_setup_failed": { "description": "Est\u00e1s utilizando v{version} de UniFi Protect, que es una versi\u00f3n de Early Access. Se produjo un error irrecuperable al intentar cargar la integraci\u00f3n. Por favor, [cambia a una versi\u00f3n estable](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) de UniFi Protect para continuar usando la integraci\u00f3n. \n\nError: {error}", "title": "Error de configuraci\u00f3n al usar la versi\u00f3n Early Access" diff --git a/homeassistant/components/unifiprotect/translations/sk.json b/homeassistant/components/unifiprotect/translations/sk.json index 7e6baec983a..e84afc395eb 100644 --- a/homeassistant/components/unifiprotect/translations/sk.json +++ b/homeassistant/components/unifiprotect/translations/sk.json @@ -4,13 +4,15 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "reauth_confirm": { diff --git a/homeassistant/components/upb/translations/sk.json b/homeassistant/components/upb/translations/sk.json index 11e19ce7a72..2174735cca1 100644 --- a/homeassistant/components/upb/translations/sk.json +++ b/homeassistant/components/upb/translations/sk.json @@ -4,13 +4,16 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "address": "Adresa (pozri popis vy\u0161\u0161ie)" - } + "address": "Adresa (pozri popis vy\u0161\u0161ie)", + "protocol": "Protokol" + }, + "title": "Pripojte sa k UPB PIM" } } } diff --git a/homeassistant/components/upcloud/translations/sk.json b/homeassistant/components/upcloud/translations/sk.json index 1b1e671c054..5cfcde03474 100644 --- a/homeassistant/components/upcloud/translations/sk.json +++ b/homeassistant/components/upcloud/translations/sk.json @@ -1,12 +1,23 @@ { "config": { "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Interval aktualiz\u00e1cie v sekund\u00e1ch, minim\u00e1lne 30" } } } diff --git a/homeassistant/components/uptime/translations/sk.json b/homeassistant/components/uptime/translations/sk.json index 473cb1c2fa0..6ba11236f08 100644 --- a/homeassistant/components/uptime/translations/sk.json +++ b/homeassistant/components/uptime/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "step": { "user": { "description": "Chcete za\u010da\u0165 nastavova\u0165?" diff --git a/homeassistant/components/uptimerobot/translations/sensor.sk.json b/homeassistant/components/uptimerobot/translations/sensor.sk.json new file mode 100644 index 00000000000..4177567e40e --- /dev/null +++ b/homeassistant/components/uptimerobot/translations/sensor.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "uptimerobot__monitor_status": { + "not_checked_yet": "Zatia\u013e neskontrolovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/sk.json b/homeassistant/components/uptimerobot/translations/sk.json index 0ba01a3cf33..e9d14e025a2 100644 --- a/homeassistant/components/uptimerobot/translations/sk.json +++ b/homeassistant/components/uptimerobot/translations/sk.json @@ -2,17 +2,21 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", - "not_main_key": "Bol zisten\u00fd nespr\u00e1vny typ k\u013e\u00fa\u010da API, pou\u017eite \u201emain\u201c k\u013e\u00fa\u010d API" + "not_main_key": "Bol zisten\u00fd nespr\u00e1vny typ k\u013e\u00fa\u010da API, pou\u017eite \u201emain\u201c k\u013e\u00fa\u010d API", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { "data": { "api_key": "API k\u013e\u00fa\u010d" - } + }, + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { diff --git a/homeassistant/components/utility_meter/translations/sk.json b/homeassistant/components/utility_meter/translations/sk.json index 33f4006cee6..54c043ce6e2 100644 --- a/homeassistant/components/utility_meter/translations/sk.json +++ b/homeassistant/components/utility_meter/translations/sk.json @@ -2,6 +2,17 @@ "config": { "step": { "user": { + "data": { + "name": "N\u00e1zov", + "source": "Vstupn\u00fd sn\u00edma\u010d", + "tariffs": "Podporovan\u00e9 tarify" + } + } + } + }, + "options": { + "step": { + "init": { "data": { "source": "Vstupn\u00fd sn\u00edma\u010d" } diff --git a/homeassistant/components/vacuum/translations/de.json b/homeassistant/components/vacuum/translations/de.json index 8de386b3506..2a76c2093a9 100644 --- a/homeassistant/components/vacuum/translations/de.json +++ b/homeassistant/components/vacuum/translations/de.json @@ -18,7 +18,7 @@ "cleaning": "Reinigen", "docked": "Angedockt", "error": "Fehler", - "idle": "Unt\u00e4tig", + "idle": "inaktiv", "off": "Aus", "on": "An", "paused": "Pausiert", diff --git a/homeassistant/components/vallox/translations/sk.json b/homeassistant/components/vallox/translations/sk.json index 16a7a7bb845..9a6fb3929c5 100644 --- a/homeassistant/components/vallox/translations/sk.json +++ b/homeassistant/components/vallox/translations/sk.json @@ -2,10 +2,14 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", - "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { - "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/velbus/translations/sk.json b/homeassistant/components/velbus/translations/sk.json index bc7287c7640..6e5e2436adf 100644 --- a/homeassistant/components/velbus/translations/sk.json +++ b/homeassistant/components/velbus/translations/sk.json @@ -4,7 +4,15 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "step": { + "user": { + "data": { + "port": "Re\u0165azec pripojenia" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/venstar/translations/sk.json b/homeassistant/components/venstar/translations/sk.json index a7981d1ae40..15cffb46878 100644 --- a/homeassistant/components/venstar/translations/sk.json +++ b/homeassistant/components/venstar/translations/sk.json @@ -3,13 +3,20 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "step": { "user": { "data": { "host": "Hostite\u013e", "password": "Heslo", - "pin": "PIN k\u00f3d" - } + "pin": "PIN k\u00f3d", + "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Pripojte k termostatu Venstar" } } } diff --git a/homeassistant/components/verisure/translations/sk.json b/homeassistant/components/verisure/translations/sk.json index 35e9a7996b7..33dc3546d09 100644 --- a/homeassistant/components/verisure/translations/sk.json +++ b/homeassistant/components/verisure/translations/sk.json @@ -8,12 +8,27 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "installation": { + "data": { + "giid": "In\u0161tal\u00e1cia" + } + }, + "mfa": { + "data": { + "code": "Overovac\u00ed k\u00f3d" + } + }, "reauth_confirm": { "data": { "email": "Email", "password": "Heslo" } }, + "reauth_mfa": { + "data": { + "code": "Overovac\u00ed k\u00f3d" + } + }, "user": { "data": { "email": "Email", diff --git a/homeassistant/components/version/translations/sk.json b/homeassistant/components/version/translations/sk.json index 793f8eff278..8e621c87efb 100644 --- a/homeassistant/components/version/translations/sk.json +++ b/homeassistant/components/version/translations/sk.json @@ -2,6 +2,20 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "step": { + "user": { + "data": { + "version_source": "Zdroj verzie" + }, + "title": "Vyberte typ in\u0161tal\u00e1cie" + }, + "version_source": { + "data": { + "beta": "Zahrn\u00fa\u0165 beta verzie" + }, + "title": "Konfigurova\u0165" + } } } } \ No newline at end of file diff --git a/homeassistant/components/vesync/translations/sk.json b/homeassistant/components/vesync/translations/sk.json index 98944b83429..4a87b488c17 100644 --- a/homeassistant/components/vesync/translations/sk.json +++ b/homeassistant/components/vesync/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, diff --git a/homeassistant/components/vicare/translations/sk.json b/homeassistant/components/vicare/translations/sk.json index 1f3e08e82e2..a2bbd24a57e 100644 --- a/homeassistant/components/vicare/translations/sk.json +++ b/homeassistant/components/vicare/translations/sk.json @@ -1,5 +1,9 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, @@ -8,6 +12,7 @@ "user": { "data": { "client_id": "API k\u013e\u00fa\u010d", + "heating_type": "Typ vykurovania", "password": "Heslo", "username": "Email" }, diff --git a/homeassistant/components/vilfo/translations/sk.json b/homeassistant/components/vilfo/translations/sk.json index 566ed69ba48..cb89b70e26e 100644 --- a/homeassistant/components/vilfo/translations/sk.json +++ b/homeassistant/components/vilfo/translations/sk.json @@ -4,7 +4,9 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/vizio/translations/sk.json b/homeassistant/components/vizio/translations/sk.json index 0fa210995d9..b43555b8eec 100644 --- a/homeassistant/components/vizio/translations/sk.json +++ b/homeassistant/components/vizio/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { "pair_tv": { diff --git a/homeassistant/components/vlc_telnet/translations/sk.json b/homeassistant/components/vlc_telnet/translations/sk.json index 89e80deac2d..875dff682d3 100644 --- a/homeassistant/components/vlc_telnet/translations/sk.json +++ b/homeassistant/components/vlc_telnet/translations/sk.json @@ -2,14 +2,19 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie" }, "flow_title": "{host}", "step": { + "hassio_confirm": { + "description": "Chcete sa pripoji\u0165 k doplnku {addon}?" + }, "reauth_confirm": { "data": { "password": "Heslo" diff --git a/homeassistant/components/volumio/translations/sk.json b/homeassistant/components/volumio/translations/sk.json index 142f47cf1fd..26921034b40 100644 --- a/homeassistant/components/volumio/translations/sk.json +++ b/homeassistant/components/volumio/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Ned\u00e1 sa pripoji\u0165 k objavenej Volumio" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { "user": { diff --git a/homeassistant/components/volvooncall/translations/sk.json b/homeassistant/components/volvooncall/translations/sk.json index d50ce04d74a..c249a185495 100644 --- a/homeassistant/components/volvooncall/translations/sk.json +++ b/homeassistant/components/volvooncall/translations/sk.json @@ -1,15 +1,19 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "region": "Regi\u00f3n", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/vulcan/translations/sk.json b/homeassistant/components/vulcan/translations/sk.json index a1053c17ee5..0a366f7ef1b 100644 --- a/homeassistant/components/vulcan/translations/sk.json +++ b/homeassistant/components/vulcan/translations/sk.json @@ -5,6 +5,7 @@ "already_configured": "Tento \u0161tudent u\u017e bol pridan\u00fd." }, "error": { + "cannot_connect": "Chyba pripojenia \u2013 skontrolujte svoje internetov\u00e9 pripojenie", "expired_token": "Platnos\u0165 tokenu vypr\u0161ala \u2013 vygenerujte si nov\u00fd token", "invalid_pin": "Neplatn\u00fd k\u00f3d PIN", "invalid_token": "Neplatn\u00fd token", @@ -25,6 +26,11 @@ "pin": "Pin", "token": "Token" } + }, + "select_saved_credentials": { + "data": { + "credentials": "Prihl\u00e1si\u0165" + } } } } diff --git a/homeassistant/components/wallbox/translations/sk.json b/homeassistant/components/wallbox/translations/sk.json index 7081168d334..f0bfe254739 100644 --- a/homeassistant/components/wallbox/translations/sk.json +++ b/homeassistant/components/wallbox/translations/sk.json @@ -7,17 +7,21 @@ "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", + "reauth_invalid": "Op\u00e4tovn\u00e9 overenie zlyhalo; s\u00e9riov\u00e9 \u010d\u00edslo sa nezhoduje s p\u00f4vodn\u00fdm", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "reauth_confirm": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "station": "S\u00e9riov\u00e9 \u010d\u00edslo stanice", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/water_heater/translations/sk.json b/homeassistant/components/water_heater/translations/sk.json new file mode 100644 index 00000000000..61841c12639 --- /dev/null +++ b/homeassistant/components/water_heater/translations/sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "_": { + "off": "Neakt\u00edvny" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/watttime/translations/sk.json b/homeassistant/components/watttime/translations/sk.json index c95e5bab874..a18c17ea0e1 100644 --- a/homeassistant/components/watttime/translations/sk.json +++ b/homeassistant/components/watttime/translations/sk.json @@ -14,25 +14,38 @@ "data": { "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka" - } + }, + "description": "Zadajte zemepisn\u00fa \u0161\u00edrku a d\u013a\u017eku, ktor\u00e9 chcete sledova\u0165:" }, "location": { "data": { "location_type": "Umiestnenie" - } + }, + "description": "Vyberte miesto, ktor\u00e9 chcete monitorova\u0165:" }, "reauth_confirm": { "data": { "password": "Heslo" }, - "description": "Znova zadajte heslo pre {username}:" + "description": "Znova zadajte heslo pre {username}:", + "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, "description": "Zadajte pou\u017e\u00edvate\u013esk\u00e9 meno a heslo:" } } + }, + "options": { + "step": { + "init": { + "data": { + "show_on_map": "Zobrazi\u0165 monitorovan\u00fa polohu na mape" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/waze_travel_time/translations/sk.json b/homeassistant/components/waze_travel_time/translations/sk.json index cae59423a4a..6eef223b1a1 100644 --- a/homeassistant/components/waze_travel_time/translations/sk.json +++ b/homeassistant/components/waze_travel_time/translations/sk.json @@ -13,5 +13,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "units": "Jednotky" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wemo/translations/sk.json b/homeassistant/components/wemo/translations/sk.json index 69d8d942494..99798036ffd 100644 --- a/homeassistant/components/wemo/translations/sk.json +++ b/homeassistant/components/wemo/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." } } } \ No newline at end of file diff --git a/homeassistant/components/whirlpool/translations/sk.json b/homeassistant/components/whirlpool/translations/sk.json index 41d0727a9b4..8beea53674a 100644 --- a/homeassistant/components/whirlpool/translations/sk.json +++ b/homeassistant/components/whirlpool/translations/sk.json @@ -8,7 +8,8 @@ "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/wilight/translations/sk.json b/homeassistant/components/wilight/translations/sk.json index 058e40d91a4..c5b9d1d505c 100644 --- a/homeassistant/components/wilight/translations/sk.json +++ b/homeassistant/components/wilight/translations/sk.json @@ -2,8 +2,14 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "not_supported_device": "Toto WiLight moment\u00e1lne nie je podporovan\u00e9" + "not_supported_device": "Toto WiLight moment\u00e1lne nie je podporovan\u00e9", + "not_wilight_device": "Toto zariadenie nie je WiLight" }, - "flow_title": "{name}" + "flow_title": "{name}", + "step": { + "confirm": { + "description": "Podporovan\u00e9 s\u00fa nasleduj\u00face komponenty: {components}" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/withings/translations/sk.json b/homeassistant/components/withings/translations/sk.json index adfad5b6b97..218c7d4bec7 100644 --- a/homeassistant/components/withings/translations/sk.json +++ b/homeassistant/components/withings/translations/sk.json @@ -2,10 +2,29 @@ "config": { "abort": { "already_configured": "Konfigur\u00e1cia profilu bola aktualizovan\u00e1.", - "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})" + }, + "create_entry": { + "default": "\u00daspe\u0161ne overen\u00e9 pomocou Withings." }, "error": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + }, + "flow_title": "{profile}", + "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + }, + "profile": { + "data": { + "profile": "N\u00e1zov profilu" + } + }, + "reauth_confirm": { + "title": "Znova overi\u0165 integr\u00e1ciu" + } } } } \ No newline at end of file diff --git a/homeassistant/components/wled/translations/sk.json b/homeassistant/components/wled/translations/sk.json index ed248c10965..449f0ae40e4 100644 --- a/homeassistant/components/wled/translations/sk.json +++ b/homeassistant/components/wled/translations/sk.json @@ -12,7 +12,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Nastavte si WLED na integr\u00e1ciu s Home Assistant." } } } diff --git a/homeassistant/components/wolflink/translations/sensor.sk.json b/homeassistant/components/wolflink/translations/sensor.sk.json index 5ce49fcc5d6..0ad86ee29b6 100644 --- a/homeassistant/components/wolflink/translations/sensor.sk.json +++ b/homeassistant/components/wolflink/translations/sensor.sk.json @@ -1,8 +1,11 @@ { "state": { "wolflink__state": { + "aus": "Zak\u00e1zan\u00e9", + "bereit_keine_ladung": "Pripraven\u00e9, nena\u010d\u00edtava sa", "cooling": "Chladenie", "eco": "Eco", + "ein": "Povolen\u00e9", "externe_deaktivierung": "Extern\u00e1 deaktiv\u00e1cia", "fernschalter_ein": "Dia\u013ekov\u00e9 ovl\u00e1danie je povolen\u00e9", "frostschutz": "Ochrana pred mrazom", @@ -10,6 +13,7 @@ "heizung": "Vykurovanie", "initialisierung": "Inicializ\u00e1cia", "kalibration": "Kalibr\u00e1cia", + "nur_heizgerat": "Iba kotol", "urlaubsmodus": "Dovolenkov\u00fd re\u017eim" } } diff --git a/homeassistant/components/wolflink/translations/sk.json b/homeassistant/components/wolflink/translations/sk.json index 61eff649dab..f1d6b3feeb3 100644 --- a/homeassistant/components/wolflink/translations/sk.json +++ b/homeassistant/components/wolflink/translations/sk.json @@ -16,7 +16,8 @@ }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/ws66i/translations/sk.json b/homeassistant/components/ws66i/translations/sk.json index a33cac735e4..5544d1ebd9b 100644 --- a/homeassistant/components/ws66i/translations/sk.json +++ b/homeassistant/components/ws66i/translations/sk.json @@ -12,5 +12,20 @@ "title": "Pripojte sa k zariadeniu" } } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "N\u00e1zov zdroja #1", + "source_2": "N\u00e1zov zdroja #2", + "source_3": "N\u00e1zov zdroja #3", + "source_4": "N\u00e1zov zdroja #4", + "source_5": "N\u00e1zov zdroja #5", + "source_6": "N\u00e1zov zdroja #6" + }, + "title": "Konfigur\u00e1cia zdrojov" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/xbox/translations/sk.json b/homeassistant/components/xbox/translations/sk.json index 91803ae5155..732253dabb4 100644 --- a/homeassistant/components/xbox/translations/sk.json +++ b/homeassistant/components/xbox/translations/sk.json @@ -1,10 +1,17 @@ { "config": { "abort": { - "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" + }, + "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + } } } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_aqara/translations/sk.json b/homeassistant/components/xiaomi_aqara/translations/sk.json index 842d0ddd655..f6fbe6ed65c 100644 --- a/homeassistant/components/xiaomi_aqara/translations/sk.json +++ b/homeassistant/components/xiaomi_aqara/translations/sk.json @@ -17,6 +17,9 @@ "select_ip": "IP adresa" } }, + "settings": { + "title": "Volite\u013en\u00e9 nastavenia" + }, "user": { "data": { "host": "IP adresa (volite\u013en\u00e1)", diff --git a/homeassistant/components/xiaomi_ble/translations/sk.json b/homeassistant/components/xiaomi_ble/translations/sk.json index b121bbc35a3..431454357f8 100644 --- a/homeassistant/components/xiaomi_ble/translations/sk.json +++ b/homeassistant/components/xiaomi_ble/translations/sk.json @@ -3,7 +3,8 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/xiaomi_miio/translations/select.sk.json b/homeassistant/components/xiaomi_miio/translations/select.sk.json index 8745c700fe6..e2021b4e247 100644 --- a/homeassistant/components/xiaomi_miio/translations/select.sk.json +++ b/homeassistant/components/xiaomi_miio/translations/select.sk.json @@ -1,6 +1,12 @@ { "state": { + "xiaomi_miio__display_orientation": { + "left": "V\u013eavo", + "right": "Vpravo" + }, "xiaomi_miio__led_brightness": { + "bright": "Jas", + "dim": "Dim", "off": "Vypnut\u00e9" } } diff --git a/homeassistant/components/xiaomi_miio/translations/sk.json b/homeassistant/components/xiaomi_miio/translations/sk.json index 94cb4db8af6..6c9d42328fa 100644 --- a/homeassistant/components/xiaomi_miio/translations/sk.json +++ b/homeassistant/components/xiaomi_miio/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "incomplete_info": "Ne\u00fapln\u00e9 inform\u00e1cie na nastavenie zariadenia, \u017eiadny hostite\u013e alebo token.", + "not_xiaomi_miio": "Zariadenie nie je (zatia\u013e) podporovan\u00e9 spolo\u010dnos\u0165ou Xiaomi Miio.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, @@ -16,7 +17,10 @@ "step": { "cloud": { "data": { - "cloud_password": "Heslo do cloudu" + "cloud_country": "Krajina cloudov\u00e9ho servera", + "cloud_password": "Heslo do cloudu", + "cloud_username": "Cloudov\u00e9 pou\u017e\u00edvate\u013esk\u00e9 meno", + "manual": "Konfigurova\u0165 manu\u00e1lne (neodpor\u00fa\u010da sa)" } }, "connect": { @@ -29,6 +33,24 @@ "host": "IP adresa", "token": "API token" } + }, + "reauth_confirm": { + "title": "Znova overi\u0165 integr\u00e1ciu" + }, + "select": { + "data": { + "select_device": "Zariadenie Miio" + }, + "description": "Vyberte zariadenie Xiaomi Miio, ktor\u00e9 chcete nastavi\u0165." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "cloud_subdevices": "Pou\u017e\u00edvanie cloudu na z\u00edskanie pripojen\u00fdch podzariaden\u00ed" + } } } } diff --git a/homeassistant/components/yale_smart_alarm/translations/sk.json b/homeassistant/components/yale_smart_alarm/translations/sk.json index 3d43eb45fac..e86404bb51d 100644 --- a/homeassistant/components/yale_smart_alarm/translations/sk.json +++ b/homeassistant/components/yale_smart_alarm/translations/sk.json @@ -11,14 +11,18 @@ "step": { "reauth_confirm": { "data": { + "area_id": "ID oblasti", "name": "N\u00e1zov", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "user": { "data": { + "area_id": "ID oblasti", "name": "N\u00e1zov", - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/yamaha_musiccast/translations/select.sk.json b/homeassistant/components/yamaha_musiccast/translations/select.sk.json index 0311b5fd0d7..1e344784685 100644 --- a/homeassistant/components/yamaha_musiccast/translations/select.sk.json +++ b/homeassistant/components/yamaha_musiccast/translations/select.sk.json @@ -1,5 +1,9 @@ { "state": { + "yamaha_musiccast__zone_link_audio_quality": { + "compressed": "Komprimovan\u00e9", + "uncompressed": "Nekomprimovan\u00e9" + }, "yamaha_musiccast__zone_sleep": { "120 min": "120 min\u00fat", "30 min": "30 min\u00fat", diff --git a/homeassistant/components/yeelight/translations/sk.json b/homeassistant/components/yeelight/translations/sk.json index 69b33c786a5..62861427cf7 100644 --- a/homeassistant/components/yeelight/translations/sk.json +++ b/homeassistant/components/yeelight/translations/sk.json @@ -23,5 +23,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "model": "Model" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/sk.json b/homeassistant/components/yolink/translations/sk.json index 2707fadc8e2..d502e9f3f7b 100644 --- a/homeassistant/components/yolink/translations/sk.json +++ b/homeassistant/components/yolink/translations/sk.json @@ -4,7 +4,21 @@ "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", - "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu." + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", + "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu.", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + }, + "create_entry": { + "default": "\u00daspe\u0161ne overen\u00e9" + }, + "step": { + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + }, + "reauth_confirm": { + "title": "Znova overi\u0165 integr\u00e1ciu" + } } } } \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/sk.json b/homeassistant/components/zamg/translations/sk.json index 6408ace0719..dddfc872188 100644 --- a/homeassistant/components/zamg/translations/sk.json +++ b/homeassistant/components/zamg/translations/sk.json @@ -7,6 +7,13 @@ "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165" }, - "flow_title": "{name}" + "flow_title": "{name}", + "step": { + "user": { + "data": { + "station_id": "ID stanice (predvolen\u00e9 nastavenie na najbli\u017e\u0161iu stanicu)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/zerproc/translations/sk.json b/homeassistant/components/zerproc/translations/sk.json index 8183bb46360..d4bb209c34c 100644 --- a/homeassistant/components/zerproc/translations/sk.json +++ b/homeassistant/components/zerproc/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "step": { "confirm": { diff --git a/homeassistant/components/zha/translations/sk.json b/homeassistant/components/zha/translations/sk.json index 6ed98fcd717..3e8e32d1035 100644 --- a/homeassistant/components/zha/translations/sk.json +++ b/homeassistant/components/zha/translations/sk.json @@ -1,11 +1,27 @@ { "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_backup_json": "Neplatn\u00e1 z\u00e1loha JSON" }, "flow_title": "{name}", "step": { + "choose_automatic_backup": { + "data": { + "choose_automatic_backup": "Vyberte si automatick\u00e9 z\u00e1lohovanie" + }, + "description": "Obnovte nastavenia siete z automatickej z\u00e1lohy", + "title": "Obnovenie automatickej z\u00e1lohy" + }, + "choose_formation_strategy": { + "menu_options": { + "choose_automatic_backup": "Obnovenie automatickej z\u00e1lohy", + "upload_manual_backup": "Nahratie manu\u00e1lnej z\u00e1lohy" + } + }, "confirm": { "description": "Chcete nastavi\u0165 {name}?" }, @@ -29,9 +45,31 @@ } }, "options": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_backup_json": "Neplatn\u00e1 z\u00e1loha JSON" + }, + "flow_title": "{name}", + "step": { + "choose_automatic_backup": { + "data": { + "choose_automatic_backup": "Vyberte si automatick\u00e9 z\u00e1lohovanie" + }, + "description": "Obnovte nastavenia siete z automatickej z\u00e1lohy", + "title": "Obnovenie automatickej z\u00e1lohy" + }, + "choose_formation_strategy": { + "menu_options": { + "choose_automatic_backup": "Obnovenie automatickej z\u00e1lohy", + "upload_manual_backup": "Nahratie manu\u00e1lnej z\u00e1lohy" + } + }, + "prompt_migrate_or_reconfigure": { + "title": "Migr\u00e1cia alebo zmena konfigur\u00e1cie" + } } } } \ No newline at end of file diff --git a/homeassistant/components/zone/translations/sk.json b/homeassistant/components/zone/translations/sk.json index 30992294e6a..d1d6c76ca43 100644 --- a/homeassistant/components/zone/translations/sk.json +++ b/homeassistant/components/zone/translations/sk.json @@ -6,8 +6,10 @@ "step": { "init": { "data": { + "icon": "Ikona", "latitude": "Zemepisn\u00e1 \u0161\u00edrka", - "longitude": "Zemepisn\u00e1 d\u013a\u017eka" + "longitude": "Zemepisn\u00e1 d\u013a\u017eka", + "name": "N\u00e1zov" } } } diff --git a/homeassistant/components/zoneminder/translations/sk.json b/homeassistant/components/zoneminder/translations/sk.json index 6855108262b..0a34af4a435 100644 --- a/homeassistant/components/zoneminder/translations/sk.json +++ b/homeassistant/components/zoneminder/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "auth_fail": "U\u017eivate\u013esk\u00e9 meno alebo heslo je nespr\u00e1vne.", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "connection_error": "Nepodarilo sa pripoji\u0165 k serveru ZoneMinder.", "invalid_auth": "Neplatn\u00e9 overenie" }, "error": { @@ -13,7 +14,11 @@ "step": { "user": { "data": { - "password": "Heslo" + "host": "Hostite\u013e a port (napr. 10.10.0.4:8010)", + "password": "Heslo", + "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } } } diff --git a/homeassistant/components/zwave_js/translations/sk.json b/homeassistant/components/zwave_js/translations/sk.json index 38ba6565be8..f6eb2028a39 100644 --- a/homeassistant/components/zwave_js/translations/sk.json +++ b/homeassistant/components/zwave_js/translations/sk.json @@ -15,12 +15,16 @@ "configure_addon": { "data": { "usb_path": "Cesta k zariadeniu USB" - } + }, + "description": "Ak tieto polia zostan\u00fa pr\u00e1zdne, doplnok vygeneruje bezpe\u010dnostn\u00e9 k\u013e\u00fa\u010de." }, "manual": { "data": { "url": "URL" } + }, + "on_supervisor": { + "title": "Vyberte sp\u00f4sob pripojenia" } } }, @@ -48,12 +52,16 @@ "data": { "log_level": "\u00darove\u0148 denn\u00edka", "usb_path": "Cesta k zariadeniu USB" - } + }, + "description": "Ak tieto polia zostan\u00fa pr\u00e1zdne, doplnok vygeneruje bezpe\u010dnostn\u00e9 k\u013e\u00fa\u010de." }, "manual": { "data": { "url": "URL" } + }, + "on_supervisor": { + "title": "Vyberte sp\u00f4sob pripojenia" } } } From fb98128b9fa4b4e0cc0316d5c1f77157bb12b0f0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 28 Nov 2022 16:56:18 -1000 Subject: [PATCH 0831/1033] Add support for esphome ble client connections v3 (#82815) --- .../components/esphome/bluetooth/client.py | 76 +++++++++++++++++-- .../components/esphome/entry_data.py | 11 +++ .../components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 84 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index ef3a6197a37..7bd893f643e 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -36,6 +36,13 @@ DISCONNECT_TIMEOUT = 5.0 CONNECT_FREE_SLOT_TIMEOUT = 2.0 GATT_READ_TIMEOUT = 30.0 +# CCCD (Characteristic Client Config Descriptor) +CCCD_UUID = "00002902-0000-1000-8000-00805f9b34fb" +CCCD_NOTIFY_BYTES = b"\x01\x00" +CCCD_INDICATE_BYTES = b"\x02\x00" + +MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE = 3 + DEFAULT_MAX_WRITE_WITHOUT_RESPONSE = DEFAULT_MTU - GATT_HEADER_SIZE _LOGGER = logging.getLogger(__name__) @@ -137,6 +144,9 @@ class ESPHomeClient(BaseBleakClient): self._cancel_connection_state: CALLBACK_TYPE | None = None self._notify_cancels: dict[int, Callable[[], Coroutine[Any, Any, None]]] = {} self._disconnected_event: asyncio.Event | None = None + device_info = self.entry_data.device_info + assert device_info is not None + self._connection_version = device_info.bluetooth_proxy_version def __str__(self) -> str: """Return the string representation of the client.""" @@ -206,7 +216,14 @@ class ESPHomeClient(BaseBleakClient): Boolean representing connection status. """ await self._wait_for_free_connection_slot(CONNECT_FREE_SLOT_TIMEOUT) - + entry_data = self.entry_data + self._mtu = entry_data.get_gatt_mtu_cache(self._address_as_int) + has_cache = bool( + dangerous_use_bleak_cache + and self._connection_version >= MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE + and entry_data.get_gatt_services_cache(self._address_as_int) + and self._mtu + ) connected_future: asyncio.Future[bool] = asyncio.Future() def _on_bluetooth_connection_state( @@ -224,7 +241,9 @@ class ESPHomeClient(BaseBleakClient): ) if connected: self._is_connected = True - self._mtu = mtu + if not self._mtu: + self._mtu = mtu + entry_data.set_gatt_mtu_cache(self._address_as_int, mtu) else: self._async_ble_device_disconnected() @@ -258,7 +277,7 @@ class ESPHomeClient(BaseBleakClient): self._ble_device.name, self._ble_device.address, ) - self.entry_data.disconnect_callbacks.append(self._async_esp_disconnected) + entry_data.disconnect_callbacks.append(self._async_esp_disconnected) connected_future.set_result(connected) timeout = kwargs.get("timeout", self._timeout) @@ -271,6 +290,8 @@ class ESPHomeClient(BaseBleakClient): self._address_as_int, _on_bluetooth_connection_state, timeout=timeout, + has_cache=has_cache, + version=self._connection_version, ) ) except Exception: # pylint: disable=broad-except @@ -344,9 +365,13 @@ class ESPHomeClient(BaseBleakClient): """ address_as_int = self._address_as_int entry_data = self.entry_data - if dangerous_use_bleak_cache and ( - cached_services := entry_data.get_gatt_services_cache(address_as_int) - ): + # If the connection version >= 3, we must use the cache + # because the esp has already wiped the services list to + # save memory. + if ( + self._connection_version >= MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE + or dangerous_use_bleak_cache + ) and (cached_services := entry_data.get_gatt_services_cache(address_as_int)): _LOGGER.debug( "%s: %s - %s: Cached services hit", self._source, @@ -516,6 +541,14 @@ class ESPHomeClient(BaseBleakClient): f"characteristic:{characteristic.uuid} " f"handle:{ble_handle}" ) + if ( + "notify" not in characteristic.properties + and "indicate" not in characteristic.properties + ): + raise BleakError( + f"Characteristic {characteristic.uuid} does not have notify or indicate property set." + ) + cancel_coro = await self._client.bluetooth_gatt_start_notify( self._address_as_int, ble_handle, @@ -523,6 +556,37 @@ class ESPHomeClient(BaseBleakClient): ) self._notify_cancels[ble_handle] = cancel_coro + if self._connection_version < MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE: + return + + # For connection v3 we are responsible for enabling notifications + # on the cccd (characteristic client config descriptor) handle since + # the esp32 will not have resolved the characteristic descriptors to + # save memory since doing so can exhaust the memory and cause a soft + # reset + cccd_descriptor = characteristic.get_descriptor(CCCD_UUID) + if not cccd_descriptor: + raise BleakError( + f"Characteristic {characteristic.uuid} does not have a " + "characteristic client config descriptor." + ) + + _LOGGER.debug( + "%s: %s - %s: Writing to CCD descriptor %s for notifications with properties=%s", + self._source, + self._ble_device.name, + self._ble_device.address, + cccd_descriptor.handle, + characteristic.properties, + ) + supports_notify = "notify" in characteristic.properties + await self._client.bluetooth_gatt_write_descriptor( + self._address_as_int, + cccd_descriptor.handle, + CCCD_NOTIFY_BYTES if supports_notify else CCCD_INDICATE_BYTES, + wait_for_response=False, + ) + @api_error_as_bleak_error async def stop_notify( self, diff --git a/homeassistant/components/esphome/entry_data.py b/homeassistant/components/esphome/entry_data.py index faa9074b880..89377ba9a6a 100644 --- a/homeassistant/components/esphome/entry_data.py +++ b/homeassistant/components/esphome/entry_data.py @@ -98,6 +98,9 @@ class RuntimeEntryData: _gatt_services_cache: MutableMapping[int, BleakGATTServiceCollection] = field( default_factory=lambda: LRU(MAX_CACHED_SERVICES) # type: ignore[no-any-return] ) + _gatt_mtu_cache: MutableMapping[int, int] = field( + default_factory=lambda: LRU(MAX_CACHED_SERVICES) # type: ignore[no-any-return] + ) @property def name(self) -> str: @@ -116,6 +119,14 @@ class RuntimeEntryData: """Set the BleakGATTServiceCollection for the given address.""" self._gatt_services_cache[address] = services + def get_gatt_mtu_cache(self, address: int) -> int | None: + """Get the mtu cache for the given address.""" + return self._gatt_mtu_cache.get(address) + + def set_gatt_mtu_cache(self, address: int, mtu: int) -> None: + """Set the mtu cache for the given address.""" + self._gatt_mtu_cache[address] = mtu + @callback def async_update_ble_connection_limits(self, free: int, limit: int) -> None: """Update the BLE connection limits.""" diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 7dbb4e99670..b3885326e90 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==12.0.1"], + "requirements": ["aioesphomeapi==12.1.0"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/requirements_all.txt b/requirements_all.txt index 15a7774bd62..77cacd4e398 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -156,7 +156,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==12.0.1 +aioesphomeapi==12.1.0 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 44bb0e61c47..f4e0969b05c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -143,7 +143,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==12.0.1 +aioesphomeapi==12.1.0 # homeassistant.components.flo aioflo==2021.11.0 From 1748a1924244fec4f96d8bc96b66c0a8fec25fa0 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Mon, 28 Nov 2022 23:30:48 -0500 Subject: [PATCH 0832/1033] Bump pyunifiprotect to 4.5.1 (#82886) * Bump pyunifiprotect to 4.5.0 * Bump version to 4.5.1 --- homeassistant/components/unifiprotect/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index d9d7b76db41..52e5ec31557 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -4,7 +4,7 @@ "integration_type": "hub", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/unifiprotect", - "requirements": ["pyunifiprotect==4.4.1", "unifi-discovery==1.1.7"], + "requirements": ["pyunifiprotect==4.5.1", "unifi-discovery==1.1.7"], "dependencies": ["http", "repairs"], "codeowners": ["@briis", "@AngellusMortis", "@bdraco"], "quality_scale": "platinum", diff --git a/requirements_all.txt b/requirements_all.txt index 77cacd4e398..525943690a8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2092,7 +2092,7 @@ pytrafikverket==0.2.2 pyudev==0.23.2 # homeassistant.components.unifiprotect -pyunifiprotect==4.4.1 +pyunifiprotect==4.5.1 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f4e0969b05c..97fdf6a6f3e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1455,7 +1455,7 @@ pytrafikverket==0.2.2 pyudev==0.23.2 # homeassistant.components.unifiprotect -pyunifiprotect==4.4.1 +pyunifiprotect==4.5.1 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 From 46b18367f7c6f9390a68f2aee0817730670d87a6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 28 Nov 2022 18:54:53 -1000 Subject: [PATCH 0833/1033] Send back BLE address type when connecting via esphome proxies (#82890) --- homeassistant/components/bluetooth/base_scanner.py | 4 +++- homeassistant/components/esphome/bluetooth/client.py | 2 ++ homeassistant/components/esphome/bluetooth/scanner.py | 2 ++ homeassistant/components/esphome/manifest.json | 2 +- homeassistant/components/shelly/bluetooth/scanner.py | 1 + requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/bluetooth/test_base_scanner.py | 4 ++++ tests/components/bluetooth/test_diagnostics.py | 4 ++++ 9 files changed, 19 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/base_scanner.py b/homeassistant/components/bluetooth/base_scanner.py index b8711b0017f..6204897c35c 100644 --- a/homeassistant/components/bluetooth/base_scanner.py +++ b/homeassistant/components/bluetooth/base_scanner.py @@ -73,6 +73,7 @@ class BaseHaScanner: "address": device_adv[0].address, "rssi": device_adv[0].rssi, "advertisement_data": device_adv[1], + "details": device_adv[0].details, } for device_adv in self.discovered_devices_and_advertisement_data.values() ], @@ -162,6 +163,7 @@ class BaseHaRemoteScanner(BaseHaScanner): service_data: dict[str, bytes], manufacturer_data: dict[int, bytes], tx_power: int | None, + details: dict[Any, Any], ) -> None: """Call the registered callback.""" now = MONOTONIC_TIME() @@ -201,7 +203,7 @@ class BaseHaRemoteScanner(BaseHaScanner): device = BLEDevice( # type: ignore[no-untyped-call] address=address, name=local_name, - details=self._details, + details=self._details | details, rssi=rssi, # deprecated, will be removed in newer bleak ) self._discovered_device_advertisement_datas[address] = ( diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index 7bd893f643e..e3730a216b7 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -147,6 +147,7 @@ class ESPHomeClient(BaseBleakClient): device_info = self.entry_data.device_info assert device_info is not None self._connection_version = device_info.bluetooth_proxy_version + self._address_type = address_or_ble_device.details["address_type"] def __str__(self) -> str: """Return the string representation of the client.""" @@ -292,6 +293,7 @@ class ESPHomeClient(BaseBleakClient): timeout=timeout, has_cache=has_cache, version=self._connection_version, + address_type=self._address_type, ) ) except Exception: # pylint: disable=broad-except diff --git a/homeassistant/components/esphome/bluetooth/scanner.py b/homeassistant/components/esphome/bluetooth/scanner.py index 3df9a4c6aaa..b6cd3d2ec35 100644 --- a/homeassistant/components/esphome/bluetooth/scanner.py +++ b/homeassistant/components/esphome/bluetooth/scanner.py @@ -27,6 +27,7 @@ class ESPHomeScanner(BaseHaRemoteScanner): adv.service_data, adv.manufacturer_data, None, + {"address_type": adv.address_type}, ) async def async_diagnostics(self) -> dict[str, Any]: @@ -39,6 +40,7 @@ class ESPHomeScanner(BaseHaRemoteScanner): "address": device_adv[0].address, "rssi": device_adv[0].rssi, "advertisement_data": device_adv[1], + "details": device_adv[0].details, } for device_adv in self.discovered_devices_and_advertisement_data.values() ], diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index b3885326e90..25dc956ea3b 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==12.1.0"], + "requirements": ["aioesphomeapi==12.2.0"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/homeassistant/components/shelly/bluetooth/scanner.py b/homeassistant/components/shelly/bluetooth/scanner.py index a6fb72ecc98..f255d01c78b 100644 --- a/homeassistant/components/shelly/bluetooth/scanner.py +++ b/homeassistant/components/shelly/bluetooth/scanner.py @@ -44,4 +44,5 @@ class ShellyBLEScanner(BaseHaRemoteScanner): parsed.service_data, parsed.manufacturer_data, parsed.tx_power, + {}, ) diff --git a/requirements_all.txt b/requirements_all.txt index 525943690a8..88e519fdc7a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -156,7 +156,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==12.1.0 +aioesphomeapi==12.2.0 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 97fdf6a6f3e..6cb0c2eac79 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -143,7 +143,7 @@ aioecowitt==2022.09.3 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==12.1.0 +aioesphomeapi==12.2.0 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/tests/components/bluetooth/test_base_scanner.py b/tests/components/bluetooth/test_base_scanner.py index a4042ada005..19c47361a6a 100644 --- a/tests/components/bluetooth/test_base_scanner.py +++ b/tests/components/bluetooth/test_base_scanner.py @@ -64,6 +64,7 @@ async def test_remote_scanner(hass, enable_bluetooth): advertisement_data.service_data, advertisement_data.manufacturer_data, advertisement_data.tx_power, + {"scanner_specific_data": "test"}, ) new_info_callback = manager.scanner_adv_received @@ -134,6 +135,7 @@ async def test_remote_scanner_expires_connectable(hass, enable_bluetooth): advertisement_data.service_data, advertisement_data.manufacturer_data, advertisement_data.tx_power, + {"scanner_specific_data": "test"}, ) new_info_callback = manager.scanner_adv_received @@ -204,6 +206,7 @@ async def test_remote_scanner_expires_non_connectable(hass, enable_bluetooth): advertisement_data.service_data, advertisement_data.manufacturer_data, advertisement_data.tx_power, + {"scanner_specific_data": "test"}, ) new_info_callback = manager.scanner_adv_received @@ -297,6 +300,7 @@ async def test_base_scanner_connecting_behavior(hass, enable_bluetooth): advertisement_data.service_data, advertisement_data.manufacturer_data, advertisement_data.tx_power, + {"scanner_specific_data": "test"}, ) new_info_callback = manager.scanner_adv_received diff --git a/tests/components/bluetooth/test_diagnostics.py b/tests/components/bluetooth/test_diagnostics.py index bfd39f74303..417375e9820 100644 --- a/tests/components/bluetooth/test_diagnostics.py +++ b/tests/components/bluetooth/test_diagnostics.py @@ -155,6 +155,7 @@ async def test_diagnostics( ], "name": "x", "rssi": -60, + "details": None, } ], "last_detection": ANY, @@ -179,6 +180,7 @@ async def test_diagnostics( ], "name": "x", "rssi": -60, + "details": None, } ], "last_detection": ANY, @@ -203,6 +205,7 @@ async def test_diagnostics( ], "name": "x", "rssi": -60, + "details": None, } ], "last_detection": ANY, @@ -366,6 +369,7 @@ async def test_diagnostics_macos( ], "name": "x", "rssi": -60, + "details": None, } ], "last_detection": ANY, From 026a222d161175edbc0aea764199dae10c89e2ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 09:38:45 +0100 Subject: [PATCH 0834/1033] Bump home-assistant/builder from 2022.09.0 to 2022.11.0 (#82897) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/builder.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index 2f844314eaa..e4fbd33cd09 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -159,7 +159,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build base image - uses: home-assistant/builder@2022.09.0 + uses: home-assistant/builder@2022.11.0 with: args: | $BUILD_ARGS \ @@ -225,7 +225,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build base image - uses: home-assistant/builder@2022.09.0 + uses: home-assistant/builder@2022.11.0 with: args: | $BUILD_ARGS \ From fcccc44ccbf8880acbf5d8a4135d68f96e408a04 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 29 Nov 2022 10:06:53 +0100 Subject: [PATCH 0835/1033] Cleanup unused AlexaPercentageController code (#82880) --- CODEOWNERS | 4 +- .../components/alexa/capabilities.py | 53 --------------- homeassistant/components/alexa/handlers.py | 68 ------------------- homeassistant/components/alexa/manifest.json | 2 +- 4 files changed, 3 insertions(+), 124 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 51a2c5c6db2..78ab439cf7e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -63,8 +63,8 @@ build.json @home-assistant/supervisor /tests/components/alarm_control_panel/ @home-assistant/core /homeassistant/components/alert/ @home-assistant/core @frenck /tests/components/alert/ @home-assistant/core @frenck -/homeassistant/components/alexa/ @home-assistant/cloud @ochlocracy -/tests/components/alexa/ @home-assistant/cloud @ochlocracy +/homeassistant/components/alexa/ @home-assistant/cloud @ochlocracy @jbouwh +/tests/components/alexa/ @home-assistant/cloud @ochlocracy @jbouwh /homeassistant/components/almond/ @gcampax @balloob /tests/components/almond/ @gcampax @balloob /homeassistant/components/amberelectric/ @madpilot diff --git a/homeassistant/components/alexa/capabilities.py b/homeassistant/components/alexa/capabilities.py index f54f66b814c..56b9e88e27e 100644 --- a/homeassistant/components/alexa/capabilities.py +++ b/homeassistant/components/alexa/capabilities.py @@ -656,59 +656,6 @@ class AlexaColorTemperatureController(AlexaCapability): return None -class AlexaPercentageController(AlexaCapability): - """Implements Alexa.PercentageController. - - https://developer.amazon.com/docs/device-apis/alexa-percentagecontroller.html - """ - - supported_locales = { - "de-DE", - "en-AU", - "en-CA", - "en-GB", - "en-IN", - "en-US", - "es-ES", - "es-US", - "fr-CA", - "fr-FR", - "hi-IN", - "it-IT", - "ja-JP", - "pt-BR", - } - - def name(self): - """Return the Alexa API name of this interface.""" - return "Alexa.PercentageController" - - def properties_supported(self): - """Return what properties this entity supports.""" - return [{"name": "percentage"}] - - def properties_proactively_reported(self): - """Return True if properties asynchronously reported.""" - return True - - def properties_retrievable(self): - """Return True if properties can be retrieved.""" - return True - - def get_property(self, name): - """Read and return a property.""" - if name != "percentage": - raise UnsupportedProperty(name) - - if self.entity.domain == fan.DOMAIN: - return self.entity.attributes.get(fan.ATTR_PERCENTAGE) or 0 - - if self.entity.domain == cover.DOMAIN: - return self.entity.attributes.get(cover.ATTR_CURRENT_POSITION, 0) - - return 0 - - class AlexaSpeaker(AlexaCapability): """Implements Alexa.Speaker. diff --git a/homeassistant/components/alexa/handlers.py b/homeassistant/components/alexa/handlers.py index 3f816501cf3..d9a2e7016e9 100644 --- a/homeassistant/components/alexa/handlers.py +++ b/homeassistant/components/alexa/handlers.py @@ -443,74 +443,6 @@ async def async_api_deactivate( ) -@HANDLERS.register(("Alexa.PercentageController", "SetPercentage")) -async def async_api_set_percentage( - hass: ha.HomeAssistant, - config: AbstractConfig, - directive: AlexaDirective, - context: ha.Context, -) -> AlexaResponse: - """Process a set percentage request.""" - entity = directive.entity - - if entity.domain == fan.DOMAIN: - percentage = int(directive.payload["percentage"]) - service = fan.SERVICE_SET_PERCENTAGE - data = { - ATTR_ENTITY_ID: entity.entity_id, - fan.ATTR_PERCENTAGE: percentage, - } - - await hass.services.async_call( - entity.domain, service, data, blocking=False, context=context - ) - elif entity.domain == humidifier.DOMAIN: - percentage = int(directive.payload["percentage"]) - service = humidifier.SERVICE_SET_HUMIDITY - data = { - ATTR_ENTITY_ID: entity.entity_id, - humidifier.ATTR_HUMIDITY: percentage, - } - - await hass.services.async_call( - entity.domain, service, data, blocking=False, context=context - ) - else: - raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED) - - return directive.response() - - -@HANDLERS.register(("Alexa.PercentageController", "AdjustPercentage")) -async def async_api_adjust_percentage( - hass: ha.HomeAssistant, - config: AbstractConfig, - directive: AlexaDirective, - context: ha.Context, -) -> AlexaResponse: - """Process an adjust percentage request.""" - entity = directive.entity - - if entity.domain != fan.DOMAIN: - raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED) - - percentage_delta = int(directive.payload["percentageDelta"]) - current = entity.attributes.get(fan.ATTR_PERCENTAGE) or 0 - # set percentage - percentage = min(100, max(0, percentage_delta + current)) - service = fan.SERVICE_SET_PERCENTAGE - data = { - ATTR_ENTITY_ID: entity.entity_id, - fan.ATTR_PERCENTAGE: percentage, - } - - await hass.services.async_call( - entity.domain, service, data, blocking=False, context=context - ) - - return directive.response() - - @HANDLERS.register(("Alexa.LockController", "Lock")) async def async_api_lock( hass: ha.HomeAssistant, diff --git a/homeassistant/components/alexa/manifest.json b/homeassistant/components/alexa/manifest.json index 486079b0313..d73fc3590bd 100644 --- a/homeassistant/components/alexa/manifest.json +++ b/homeassistant/components/alexa/manifest.json @@ -4,6 +4,6 @@ "documentation": "https://www.home-assistant.io/integrations/alexa", "dependencies": ["http"], "after_dependencies": ["camera"], - "codeowners": ["@home-assistant/cloud", "@ochlocracy"], + "codeowners": ["@home-assistant/cloud", "@ochlocracy", "@jbouwh"], "iot_class": "cloud_push" } From 258b9fe66327c15b14dcc9ee2edfbe9d286296a0 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Tue, 29 Nov 2022 04:09:41 -0500 Subject: [PATCH 0836/1033] Support restoring TextEntity native_value (#82770) --- homeassistant/components/text/__init__.py | 47 ++++++++- tests/components/text/test_init.py | 99 ++++++++++++++++++- .../custom_components/test/text.py | 86 ++++++++++++++++ 3 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 tests/testing_config/custom_components/test/text.py diff --git a/homeassistant/components/text/__init__.py b/homeassistant/components/text/__init__.py index 35c7d5d94ef..32054734e8e 100644 --- a/homeassistant/components/text/__init__.py +++ b/homeassistant/components/text/__init__.py @@ -1,7 +1,7 @@ """Component to allow setting text as platforms.""" from __future__ import annotations -from dataclasses import dataclass +from dataclasses import asdict, dataclass from datetime import timedelta import logging import re @@ -20,6 +20,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401 ) from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.restore_state import ExtraStoredData, RestoreEntity from homeassistant.helpers.typing import ConfigType from .const import ( @@ -222,3 +223,47 @@ class TextEntity(Entity): async def async_set_value(self, value: str) -> None: """Change the value.""" await self.hass.async_add_executor_job(self.set_value, value) + + +@dataclass +class TextExtraStoredData(ExtraStoredData): + """Object to hold extra stored data.""" + + native_value: str | None + native_min: int + native_max: int + + def as_dict(self) -> dict[str, Any]: + """Return a dict representation of the text data.""" + return asdict(self) + + @classmethod + def from_dict(cls, restored: dict[str, Any]) -> TextExtraStoredData | None: + """Initialize a stored text state from a dict.""" + try: + return cls( + restored["native_value"], + restored["native_min"], + restored["native_max"], + ) + except KeyError: + return None + + +class RestoreText(TextEntity, RestoreEntity): + """Mixin class for restoring previous text state.""" + + @property + def extra_restore_state_data(self) -> TextExtraStoredData: + """Return text specific state data to be restored.""" + return TextExtraStoredData( + self.native_value, + self.native_min, + self.native_max, + ) + + async def async_get_last_text_data(self) -> TextExtraStoredData | None: + """Restore attributes.""" + if (restored_last_extra_data := await self.async_get_last_extra_data()) is None: + return None + return TextExtraStoredData.from_dict(restored_last_extra_data.as_dict()) diff --git a/tests/components/text/test_init.py b/tests/components/text/test_init.py index 1bbb3e1e1b0..dda746fe6a5 100644 --- a/tests/components/text/test_init.py +++ b/tests/components/text/test_init.py @@ -14,7 +14,11 @@ from homeassistant.components.text import ( _async_set_value, ) from homeassistant.const import MAX_LENGTH_STATE_STATE -from homeassistant.core import ServiceCall +from homeassistant.core import ServiceCall, State +from homeassistant.helpers.restore_state import STORAGE_KEY as RESTORE_STATE_KEY +from homeassistant.setup import async_setup_component + +from tests.common import mock_restore_cache_with_extra_data class MockTextEntity(TextEntity): @@ -95,10 +99,99 @@ async def test_text_set_value(hass): async def test_text_value_outside_bounds(hass): """Test text entity with value that is outside min and max.""" with pytest.raises(ValueError): - MockTextEntity( + _ = MockTextEntity( "hello world", native_min=2, native_max=5, pattern=r"[a-z]" ).state with pytest.raises(ValueError): - MockTextEntity( + _ = MockTextEntity( "hello world", native_min=15, native_max=20, pattern=r"[a-z]" ).state + + +RESTORE_DATA = { + "native_max": 5, + "native_min": 1, + # "mode": TextMode.TEXT, + # "pattern": r"[A-Za-z0-9]", + "native_value": "Hello", +} + + +async def test_restore_number_save_state( + hass, + hass_storage, + enable_custom_integrations, +): + """Test RestoreNumber.""" + platform = getattr(hass.components, "test.text") + platform.init(empty=True) + platform.ENTITIES.append( + platform.MockRestoreText( + name="Test", + native_max=5, + native_min=1, + native_value="Hello", + ) + ) + + entity0 = platform.ENTITIES[0] + assert await async_setup_component(hass, "text", {"text": {"platform": "test"}}) + await hass.async_block_till_done() + + # Trigger saving state + await hass.async_stop() + + assert len(hass_storage[RESTORE_STATE_KEY]["data"]) == 1 + state = hass_storage[RESTORE_STATE_KEY]["data"][0]["state"] + assert state["entity_id"] == entity0.entity_id + extra_data = hass_storage[RESTORE_STATE_KEY]["data"][0]["extra_data"] + assert extra_data == RESTORE_DATA + assert isinstance(extra_data["native_value"], str) + + +@pytest.mark.parametrize( + "native_max, native_min, native_value, native_value_type, extra_data", + [ + (5, 1, "Hello", str, RESTORE_DATA), + (255, 1, None, type(None), None), + (255, 1, None, type(None), {}), + (255, 1, None, type(None), {"beer": 123}), + (255, 1, None, type(None), {"native_value": {}}), + ], +) +async def test_restore_number_restore_state( + hass, + enable_custom_integrations, + hass_storage, + native_max, + native_min, + native_value, + native_value_type, + extra_data, +): + """Test RestoreNumber.""" + mock_restore_cache_with_extra_data(hass, ((State("text.test", ""), extra_data),)) + + platform = getattr(hass.components, "test.text") + platform.init(empty=True) + platform.ENTITIES.append( + platform.MockRestoreText( + native_max=native_max, + native_min=native_min, + name="Test", + native_value=None, + ) + ) + + entity0 = platform.ENTITIES[0] + assert await async_setup_component(hass, "text", {"text": {"platform": "test"}}) + await hass.async_block_till_done() + + assert hass.states.get(entity0.entity_id) + + assert entity0.native_max == native_max + assert entity0.native_min == native_min + assert entity0.mode == TextMode.TEXT + assert entity0.pattern is None + assert entity0.native_value == native_value + assert isinstance(entity0.native_value, native_value_type) diff --git a/tests/testing_config/custom_components/test/text.py b/tests/testing_config/custom_components/test/text.py new file mode 100644 index 00000000000..1430259e2e8 --- /dev/null +++ b/tests/testing_config/custom_components/test/text.py @@ -0,0 +1,86 @@ +""" +Provide a mock text platform. + +Call init before using it in your tests to ensure clean test data. +""" +from homeassistant.components.text import RestoreText, TextEntity, TextMode + +from tests.common import MockEntity + +UNIQUE_TEXT = "unique_text" + +ENTITIES = [] + + +class MockTextEntity(MockEntity, TextEntity): + """Mock text class.""" + + @property + def native_max(self): + """Return the native native_max.""" + return self._handle("native_max") + + @property + def native_min(self): + """Return the native native_min.""" + return self._handle("native_min") + + @property + def mode(self): + """Return the mode.""" + return self._handle("mode") + + @property + def pattern(self): + """Return the pattern.""" + return self._handle("pattern") + + @property + def native_value(self): + """Return the native value of this text.""" + return self._handle("native_value") + + def set_native_value(self, value: str) -> None: + """Change the selected option.""" + self._values["native_value"] = value + + +class MockRestoreText(MockTextEntity, RestoreText): + """Mock RestoreText class.""" + + async def async_added_to_hass(self) -> None: + """Restore native_*.""" + await super().async_added_to_hass() + if (last_text_data := await self.async_get_last_text_data()) is None: + return + self._values["native_max"] = last_text_data.native_max + self._values["native_min"] = last_text_data.native_min + self._values["native_value"] = last_text_data.native_value + + +def init(empty=False): + """Initialize the platform with entities.""" + global ENTITIES + + ENTITIES = ( + [] + if empty + else [ + MockTextEntity( + name="test", + native_min=1, + native_max=5, + mode=TextMode.TEXT, + pattern=r"[A-Za-z0-9]", + unique_id=UNIQUE_TEXT, + native_value="Hello", + ), + ] + ) + + +async def async_setup_platform( + hass, config, async_add_entities_callback, discovery_info=None +): + """Return mock entities.""" + async_add_entities_callback(ENTITIES) From 53e05dec027502ae3a5912de39b21441016945f9 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 29 Nov 2022 10:13:38 +0100 Subject: [PATCH 0837/1033] Add ability to add sensors in scrape config flow (#82802) * Add ability to add sensors in scrape config flow * Fix menu * Adjust comment * Use sentinel * Adjust docstring --- .../components/scrape/config_flow.py | 36 ++--- homeassistant/components/scrape/strings.json | 27 ++++ .../components/scrape/translations/en.json | 27 ++++ .../helpers/schema_config_entry_flow.py | 20 ++- tests/components/scrape/test_config_flow.py | 132 ++++++++++++++---- 5 files changed, 196 insertions(+), 46 deletions(-) diff --git a/homeassistant/components/scrape/config_flow.py b/homeassistant/components/scrape/config_flow.py index b53f2f12bd2..8b598ca90be 100644 --- a/homeassistant/components/scrape/config_flow.py +++ b/homeassistant/components/scrape/config_flow.py @@ -40,7 +40,7 @@ from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, SchemaFlowError, SchemaFlowFormStep, - SchemaOptionsFlowHandler, + SchemaFlowMenuStep, ) from homeassistant.helpers.selector import ( BooleanSelector, @@ -130,16 +130,15 @@ def validate_rest_setup( def validate_sensor_setup( handler: SchemaCommonFlowHandler, user_input: dict[str, Any] ) -> dict[str, Any]: - """Validate sensor setup.""" - return { - "sensor": [ - { - **user_input, - CONF_INDEX: int(user_input[CONF_INDEX]), - CONF_UNIQUE_ID: str(uuid.uuid1()), - } - ] - } + """Validate sensor input.""" + user_input[CONF_INDEX] = int(user_input[CONF_INDEX]) + user_input[CONF_UNIQUE_ID] = str(uuid.uuid1()) + + # Standard behavior is to merge the result with the options. + # In this case, we want to add a sub-item so we update the options directly. + sensors: list[dict[str, Any]] = handler.options.setdefault("sensor", []) + sensors.append(user_input) + return {} DATA_SCHEMA_RESOURCE = vol.Schema(RESOURCE_SETUP) @@ -157,7 +156,16 @@ CONFIG_FLOW = { ), } OPTIONS_FLOW = { - "init": SchemaFlowFormStep(DATA_SCHEMA_RESOURCE), + "init": SchemaFlowMenuStep(["resource", "add_sensor"]), + "resource": SchemaFlowFormStep( + DATA_SCHEMA_RESOURCE, + validate_user_input=validate_rest_setup, + ), + "add_sensor": SchemaFlowFormStep( + DATA_SCHEMA_SENSOR, + suggested_values=None, + validate_user_input=validate_sensor_setup, + ), } @@ -170,7 +178,3 @@ class ScrapeConfigFlowHandler(SchemaConfigFlowHandler, domain=DOMAIN): def async_config_entry_title(self, options: Mapping[str, Any]) -> str: """Return config entry title.""" return options[CONF_RESOURCE] - - -class ScrapeOptionsFlowHandler(SchemaOptionsFlowHandler): - """Handle a config flow for Scrape.""" diff --git a/homeassistant/components/scrape/strings.json b/homeassistant/components/scrape/strings.json index 391bfe5ad9f..d14b0916f6b 100644 --- a/homeassistant/components/scrape/strings.json +++ b/homeassistant/components/scrape/strings.json @@ -52,6 +52,33 @@ "options": { "step": { "init": { + "menu_options": { + "add_sensor": "Add sensor", + "resource": "Configure resource" + } + }, + "add_sensor": { + "data": { + "name": "[%key:component::scrape::config::step::sensor::data::name%]", + "attribute": "[%key:component::scrape::config::step::sensor::data::attribute%]", + "index": "[%key:component::scrape::config::step::sensor::data::index%]", + "select": "[%key:component::scrape::config::step::sensor::data::select%]", + "value_template": "[%key:component::scrape::config::step::sensor::data::value_template%]", + "device_class": "[%key:component::scrape::config::step::sensor::data::device_class%]", + "state_class": "[%key:component::scrape::config::step::sensor::data::state_class%]", + "unit_of_measurement": "[%key:component::scrape::config::step::sensor::data::unit_of_measurement%]" + }, + "data_description": { + "select": "[%key:component::scrape::config::step::sensor::data_description::select%]", + "attribute": "[%key:component::scrape::config::step::sensor::data_description::attribute%]", + "index": "[%key:component::scrape::config::step::sensor::data_description::index%]", + "value_template": "[%key:component::scrape::config::step::sensor::data_description::value_template%]", + "device_class": "[%key:component::scrape::config::step::sensor::data_description::device_class%]", + "state_class": "[%key:component::scrape::config::step::sensor::data_description::state_class%]", + "unit_of_measurement": "[%key:component::scrape::config::step::sensor::data_description::unit_of_measurement%]" + } + }, + "resource": { "data": { "resource": "[%key:component::scrape::config::step::user::data::resource%]", "method": "[%key:component::scrape::config::step::user::data::method%]", diff --git a/homeassistant/components/scrape/translations/en.json b/homeassistant/components/scrape/translations/en.json index 8cc642c12a2..868ed3abaa6 100644 --- a/homeassistant/components/scrape/translations/en.json +++ b/homeassistant/components/scrape/translations/en.json @@ -58,6 +58,33 @@ "options": { "step": { "init": { + "menu_options": { + "add_sensor": "Add sensor", + "resource": "Configure resource" + } + }, + "add_sensor": { + "data": { + "attribute": "Attribute", + "device_class": "Device Class", + "index": "Index", + "name": "Name", + "select": "Select", + "state_class": "State Class", + "unit_of_measurement": "Unit of Measurement", + "value_template": "Value Template" + }, + "data_description": { + "attribute": "Get value of an attribute on the selected tag", + "device_class": "The type/class of the sensor to set the icon in the frontend", + "index": "Defines which of the elements returned by the CSS selector to use", + "select": "Defines what tag to search for. Check Beautifulsoup CSS selectors for details", + "state_class": "The state_class of the sensor", + "unit_of_measurement": "Choose temperature measurement or create your own", + "value_template": "Defines a template to get the state of the sensor" + } + }, + "resource": { "data": { "authentication": "Select authentication method", "headers": "Headers", diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 64d3adc5d0c..747915c71e2 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -15,6 +15,7 @@ from homeassistant.core import HomeAssistant, callback, split_entity_id from homeassistant.data_entry_flow import FlowResult, UnknownHandler from . import entity_registry as er, selector +from .typing import UNDEFINED, UndefinedType class SchemaFlowError(Exception): @@ -63,10 +64,12 @@ class SchemaFlowFormStep(SchemaFlowStep): - If `next_step` is None, the flow is ended with `FlowResultType.CREATE_ENTRY`. """ - suggested_values: Callable[[SchemaCommonFlowHandler], dict[str, Any]] | None = None + suggested_values: Callable[ + [SchemaCommonFlowHandler], dict[str, Any] + ] | None | UndefinedType = UNDEFINED """Optional property to populate suggested values. - - If `suggested_values` is None, each key in the schema will get a suggested value + - If `suggested_values` is UNDEFINED, each key in the schema will get a suggested value from an option with the same key. Note: if a step is retried due to a validation failure, then the user input will have @@ -101,6 +104,11 @@ class SchemaCommonFlowHandler: """Return parent handler.""" return self._handler + @property + def options(self) -> dict[str, Any]: + """Return the options linked to the current flow handler.""" + return self._options + async def async_step( self, step_id: str, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -189,10 +197,12 @@ class SchemaCommonFlowHandler: if (data_schema := self._get_schema(form_step)) is None: return self._show_next_step_or_create_entry(form_step) - if form_step.suggested_values: - suggested_values = form_step.suggested_values(self) - else: + suggested_values: dict[str, Any] = {} + if form_step.suggested_values is UNDEFINED: suggested_values = self._options + elif form_step.suggested_values: + suggested_values = form_step.suggested_values(self) + if user_input: # We don't want to mutate the existing options suggested_values = copy.deepcopy(suggested_values) diff --git a/tests/components/scrape/test_config_flow.py b/tests/components/scrape/test_config_flow.py index b097dc7a5a0..9e7a895eadd 100644 --- a/tests/components/scrape/test_config_flow.py +++ b/tests/components/scrape/test_config_flow.py @@ -2,6 +2,7 @@ from __future__ import annotations from unittest.mock import patch +import uuid from homeassistant import config_entries from homeassistant.components.rest.data import DEFAULT_TIMEOUT @@ -156,17 +157,27 @@ async def test_flow_fails(hass: HomeAssistant, get_data: MockRestData) -> None: } -async def test_options_flow(hass: HomeAssistant, loaded_entry: MockConfigEntry) -> None: - """Test options config flow.""" +async def test_options_resource_flow( + hass: HomeAssistant, loaded_entry: MockConfigEntry +) -> None: + """Test options flow for a resource.""" state = hass.states.get("sensor.current_version") assert state.state == "Current Version: 2021.12.10" result = await hass.config_entries.options.async_init(loaded_entry.entry_id) - assert result["type"] == FlowResultType.FORM + assert result["type"] == FlowResultType.MENU assert result["step_id"] == "init" + result = await hass.config_entries.options.async_configure( + result["flow_id"], + {"next_step_id": "resource"}, + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "resource" + mocker = MockRestData("test_scrape_sensor2") with patch("homeassistant.components.rest.RestData", return_value=mocker): result = await hass.config_entries.options.async_configure( @@ -182,29 +193,100 @@ async def test_options_flow(hass: HomeAssistant, loaded_entry: MockConfigEntry) ) await hass.async_block_till_done() - assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["data"] == { - CONF_RESOURCE: "https://www.home-assistant.io", - CONF_METHOD: "GET", - CONF_VERIFY_SSL: True, - CONF_TIMEOUT: 10.0, - CONF_USERNAME: "secret_username", - CONF_PASSWORD: "secret_password", - "sensor": [ - { - CONF_NAME: "Current version", - CONF_SELECT: ".current-version h1", - CONF_INDEX: 0.0, - CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120002", - } - ], - } + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["data"] == { + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: "GET", + CONF_VERIFY_SSL: True, + CONF_TIMEOUT: 10.0, + CONF_USERNAME: "secret_username", + CONF_PASSWORD: "secret_password", + "sensor": [ + { + CONF_NAME: "Current version", + CONF_SELECT: ".current-version h1", + CONF_INDEX: 0.0, + CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120002", + } + ], + } + await hass.async_block_till_done() + + # Check the entity was updated, no new entity was created + assert len(hass.states.async_all()) == 1 + + # Check the state of the entity has changed as expected + state = hass.states.get("sensor.current_version") + assert state.state == "Hidden Version: 2021.12.10" + + +async def test_options_add_sensor_flow( + hass: HomeAssistant, loaded_entry: MockConfigEntry +) -> None: + """Test options flow to add a sensor.""" + + state = hass.states.get("sensor.current_version") + assert state.state == "Current Version: 2021.12.10" + + result = await hass.config_entries.options.async_init(loaded_entry.entry_id) + + assert result["type"] == FlowResultType.MENU + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + {"next_step_id": "add_sensor"}, + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "add_sensor" + + mocker = MockRestData("test_scrape_sensor2") + with patch("homeassistant.components.rest.RestData", return_value=mocker), patch( + "homeassistant.components.scrape.config_flow.uuid.uuid1", + return_value=uuid.UUID("3699ef88-69e6-11ed-a1eb-0242ac120003"), + ): + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_NAME: "Template", + CONF_SELECT: "template", + CONF_INDEX: 0.0, + }, + ) await hass.async_block_till_done() - # Check the entity was updated, no new entity was created - assert len(hass.states.async_all()) == 1 + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["data"] == { + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: "GET", + CONF_VERIFY_SSL: True, + CONF_TIMEOUT: 10, + "sensor": [ + { + CONF_NAME: "Current version", + CONF_SELECT: ".current-version h1", + CONF_INDEX: 0, + CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120002", + }, + { + CONF_NAME: "Template", + CONF_SELECT: "template", + CONF_INDEX: 0, + CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120003", + }, + ], + } - # Check the state of the entity has changed as expected - state = hass.states.get("sensor.current_version") - assert state.state == "Hidden Version: 2021.12.10" + await hass.async_block_till_done() + + # Check the entity was updated, with the new entity + assert len(hass.states.async_all()) == 2 + + # Check the state of the entity has changed as expected + state = hass.states.get("sensor.current_version") + assert state.state == "Hidden Version: 2021.12.10" + + state = hass.states.get("sensor.template") + assert state.state == "Trying to get" From 724a79a8e89e6288ac9d40bd0b5ea905aed716c2 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 29 Nov 2022 10:16:01 +0100 Subject: [PATCH 0838/1033] Add suggested_value helper for FlowHandler (#82491) * Add suggested_value helper for ConfigFlow * Move helper function to FlowHandler * Rename and adjust docstring * Fix rebase --- homeassistant/data_entry_flow.py | 29 +++++++++++++++++++ .../helpers/schema_config_entry_flow.py | 22 ++------------ 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/homeassistant/data_entry_flow.py b/homeassistant/data_entry_flow.py index 629258e01d1..866f1a5db2f 100644 --- a/homeassistant/data_entry_flow.py +++ b/homeassistant/data_entry_flow.py @@ -4,6 +4,7 @@ from __future__ import annotations import abc import asyncio from collections.abc import Iterable, Mapping +import copy from dataclasses import dataclass import logging from types import MappingProxyType @@ -443,6 +444,34 @@ class FlowHandler: """If we should show advanced options.""" return self.context.get("show_advanced_options", False) + def add_suggested_values_to_schema( + self, data_schema: vol.Schema, suggested_values: Mapping[str, Any] + ) -> vol.Schema: + """Make a copy of the schema, populated with suggested values. + + For each schema marker matching items in `suggested_values`, + the `suggested_value` will be set. The existing `suggested_value` will + be left untouched if there is no matching item. + """ + schema = {} + for key, val in data_schema.schema.items(): + if isinstance(key, vol.Marker): + # Exclude advanced field + if ( + key.description + and key.description.get("advanced") + and not self.show_advanced_options + ): + continue + + new_key = key + if key in suggested_values and isinstance(key, vol.Marker): + # Copy the marker to not modify the flow schema + new_key = copy.copy(key) + new_key.description = {"suggested_value": suggested_values[key]} + schema[new_key] = val + return vol.Schema(schema) + @callback def async_show_form( self, diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 747915c71e2..8d72910b6fe 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -210,25 +210,9 @@ class SchemaCommonFlowHandler: if data_schema.schema: # Make a copy of the schema with suggested values set to saved options - schema = {} - for key, val in data_schema.schema.items(): - - if isinstance(key, vol.Marker): - # Exclude advanced field - if ( - key.description - and key.description.get("advanced") - and not self._handler.show_advanced_options - ): - continue - - new_key = key - if key in suggested_values and isinstance(key, vol.Marker): - # Copy the marker to not modify the flow schema - new_key = copy.copy(key) - new_key.description = {"suggested_value": suggested_values[key]} - schema[new_key] = val - data_schema = vol.Schema(schema) + data_schema = self._handler.add_suggested_values_to_schema( + data_schema, suggested_values + ) errors = {"base": str(error)} if error else None From f869ce9d062f42658a12239e55793d044786c955 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 29 Nov 2022 10:16:08 +0100 Subject: [PATCH 0839/1033] Validate common statistics DB schema errors on start (#79707) * Validate common statistics db schema errors on start * Fix test * Add tests * Adjust tests * Disable statistics schema validation in tests * Update after rebase --- homeassistant/components/recorder/core.py | 12 +- .../components/recorder/migration.py | 43 +- .../components/recorder/statistics.py | 406 +++++++++++++++--- tests/components/recorder/test_statistics.py | 197 ++++++++- tests/conftest.py | 31 ++ 5 files changed, 602 insertions(+), 87 deletions(-) diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 61b75783783..a79724f765a 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -591,16 +591,14 @@ class Recorder(threading.Thread): self.hass.add_job(self.async_connection_failed) return - schema_status = migration.validate_db_schema(self.hass, self.get_session) + schema_status = migration.validate_db_schema(self.hass, self, self.get_session) if schema_status is None: # Give up if we could not validate the schema self.hass.add_job(self.async_connection_failed) return self.schema_version = schema_status.current_version - schema_is_valid = migration.schema_is_valid(schema_status) - - if schema_is_valid: + if schema_status.valid: self._setup_run() else: self.migration_in_progress = True @@ -608,8 +606,8 @@ class Recorder(threading.Thread): self.hass.add_job(self.async_connection_success) - if self.migration_is_live or schema_is_valid: - # If the migrate is live or the schema is current, we need to + if self.migration_is_live or schema_status.valid: + # If the migrate is live or the schema is valid, we need to # wait for startup to complete. If its not live, we need to continue # on. self.hass.add_job(self.async_set_db_ready) @@ -626,7 +624,7 @@ class Recorder(threading.Thread): self.hass.add_job(self.async_set_db_ready) return - if not schema_is_valid: + if not schema_status.valid: if self._migrate_schema_and_setup_run(schema_status): self.schema_version = SCHEMA_VERSION if not self._event_listener: diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index 22a3b382c7d..cf56c39a885 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Callable, Iterable import contextlib -from dataclasses import dataclass +from dataclasses import dataclass, replace as dataclass_replace from datetime import timedelta import logging from typing import TYPE_CHECKING @@ -37,9 +37,11 @@ from .db_schema import ( ) from .models import process_timestamp from .statistics import ( + correct_db_schema as statistics_correct_db_schema, delete_statistics_duplicates, delete_statistics_meta_duplicates, get_start_time, + validate_db_schema as statistics_validate_db_schema, ) from .util import session_scope @@ -83,6 +85,8 @@ class SchemaValidationStatus: """Store schema validation status.""" current_version: int + statistics_schema_errors: set[str] + valid: bool def _schema_is_current(current_version: int) -> bool: @@ -90,13 +94,8 @@ def _schema_is_current(current_version: int) -> bool: return current_version == SCHEMA_VERSION -def schema_is_valid(schema_status: SchemaValidationStatus) -> bool: - """Check if the schema is valid.""" - return _schema_is_current(schema_status.current_version) - - def validate_db_schema( - hass: HomeAssistant, session_maker: Callable[[], Session] + hass: HomeAssistant, engine: Engine, session_maker: Callable[[], Session] ) -> SchemaValidationStatus | None: """Check if the schema is valid. @@ -104,11 +103,20 @@ def validate_db_schema( errors caused by manual migration between database engines, for example importing an SQLite database to MariaDB. """ + schema_errors: set[str] = set() + current_version = get_schema_version(session_maker) if current_version is None: return None - return SchemaValidationStatus(current_version) + if is_current := _schema_is_current(current_version): + # We can only check for further errors if the schema is current, because + # columns may otherwise not exist etc. + schema_errors |= statistics_validate_db_schema(hass, engine, session_maker) + + valid = is_current and not schema_errors + + return SchemaValidationStatus(current_version, schema_errors, valid) def live_migration(schema_status: SchemaValidationStatus) -> bool: @@ -125,10 +133,18 @@ def migrate_schema( ) -> None: """Check if the schema needs to be upgraded.""" current_version = schema_status.current_version - _LOGGER.warning("Database is about to upgrade. Schema version: %s", current_version) + if current_version != SCHEMA_VERSION: + _LOGGER.warning( + "Database is about to upgrade from schema version: %s to: %s", + current_version, + SCHEMA_VERSION, + ) db_ready = False for version in range(current_version, SCHEMA_VERSION): - if live_migration(SchemaValidationStatus(version)) and not db_ready: + if ( + live_migration(dataclass_replace(schema_status, current_version=version)) + and not db_ready + ): db_ready = True instance.migration_is_live = True hass.add_job(instance.async_set_db_ready) @@ -140,6 +156,13 @@ def migrate_schema( _LOGGER.info("Upgrade to version %s done", new_version) + if schema_errors := schema_status.statistics_schema_errors: + _LOGGER.warning( + "Database is about to correct DB schema errors: %s", + ", ".join(sorted(schema_errors)), + ) + statistics_correct_db_schema(engine, session_maker, schema_errors) + def _create_index( session_maker: Callable[[], Session], table_name: str, index_name: str diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 6c117b9698d..303a925f9a0 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -2,7 +2,7 @@ from __future__ import annotations from collections import defaultdict -from collections.abc import Callable, Iterable +from collections.abc import Callable, Iterable, Mapping import contextlib import dataclasses from datetime import datetime, timedelta @@ -15,9 +15,10 @@ import re from statistics import mean from typing import TYPE_CHECKING, Any, Literal -from sqlalchemy import bindparam, func, lambda_stmt, select +from sqlalchemy import bindparam, func, lambda_stmt, select, text +from sqlalchemy.engine import Engine from sqlalchemy.engine.row import Row -from sqlalchemy.exc import SQLAlchemyError, StatementError +from sqlalchemy.exc import OperationalError, SQLAlchemyError, StatementError from sqlalchemy.orm.session import Session from sqlalchemy.sql.expression import literal_column, true from sqlalchemy.sql.lambdas import StatementLambdaElement @@ -874,12 +875,17 @@ def get_metadata( ) +def _clear_statistics_with_session(session: Session, statistic_ids: list[str]) -> None: + """Clear statistics for a list of statistic_ids.""" + session.query(StatisticsMeta).filter( + StatisticsMeta.statistic_id.in_(statistic_ids) + ).delete(synchronize_session=False) + + def clear_statistics(instance: Recorder, statistic_ids: list[str]) -> None: """Clear statistics for a list of statistic_ids.""" with session_scope(session=instance.get_session()) as session: - session.query(StatisticsMeta).filter( - StatisticsMeta.statistic_id.in_(statistic_ids) - ).delete(synchronize_session=False) + _clear_statistics_with_session(session, statistic_ids) def update_statistics_metadata( @@ -1562,6 +1568,78 @@ def statistic_during_period( return {key: convert(value) for key, value in result.items()} +def _statistics_during_period_with_session( + hass: HomeAssistant, + session: Session, + start_time: datetime, + end_time: datetime | None, + statistic_ids: list[str] | None, + period: Literal["5minute", "day", "hour", "week", "month"], + units: dict[str, str] | None, + types: set[Literal["last_reset", "max", "mean", "min", "state", "sum"]], +) -> dict[str, list[dict[str, Any]]]: + """Return statistic data points during UTC period start_time - end_time. + + If end_time is omitted, returns statistics newer than or equal to start_time. + If statistic_ids is omitted, returns statistics for all statistics ids. + """ + metadata = None + # Fetch metadata for the given (or all) statistic_ids + metadata = get_metadata_with_session(session, statistic_ids=statistic_ids) + if not metadata: + return {} + + metadata_ids = None + if statistic_ids is not None: + metadata_ids = [metadata_id for metadata_id, _ in metadata.values()] + + table: type[Statistics | StatisticsShortTerm] = ( + Statistics if period != "5minute" else StatisticsShortTerm + ) + stmt = _statistics_during_period_stmt( + start_time, end_time, metadata_ids, table, types + ) + stats = execute_stmt_lambda_element(session, stmt) + + if not stats: + return {} + # Return statistics combined with metadata + if period not in ("day", "week", "month"): + return _sorted_statistics_to_dict( + hass, + session, + stats, + statistic_ids, + metadata, + True, + table, + start_time, + units, + types, + ) + + result = _sorted_statistics_to_dict( + hass, + session, + stats, + statistic_ids, + metadata, + True, + table, + start_time, + units, + types, + ) + + if period == "day": + return _reduce_statistics_per_day(result, types) + + if period == "week": + return _reduce_statistics_per_week(result, types) + + return _reduce_statistics_per_month(result, types) + + def statistics_during_period( hass: HomeAssistant, start_time: datetime, @@ -1576,63 +1654,18 @@ def statistics_during_period( If end_time is omitted, returns statistics newer than or equal to start_time. If statistic_ids is omitted, returns statistics for all statistics ids. """ - metadata = None with session_scope(hass=hass) as session: - # Fetch metadata for the given (or all) statistic_ids - metadata = get_metadata_with_session(session, statistic_ids=statistic_ids) - if not metadata: - return {} - - metadata_ids = None - if statistic_ids is not None: - metadata_ids = [metadata_id for metadata_id, _ in metadata.values()] - - table: type[Statistics | StatisticsShortTerm] = ( - Statistics if period != "5minute" else StatisticsShortTerm - ) - stmt = _statistics_during_period_stmt( - start_time, end_time, metadata_ids, table, types - ) - stats = execute_stmt_lambda_element(session, stmt) - - if not stats: - return {} - # Return statistics combined with metadata - if period not in ("day", "week", "month"): - return _sorted_statistics_to_dict( - hass, - session, - stats, - statistic_ids, - metadata, - True, - table, - start_time, - units, - types, - ) - - result = _sorted_statistics_to_dict( + return _statistics_during_period_with_session( hass, session, - stats, - statistic_ids, - metadata, - True, - table, start_time, + end_time, + statistic_ids, + period, units, types, ) - if period == "day": - return _reduce_statistics_per_day(result, types) - - if period == "week": - return _reduce_statistics_per_week(result, types) - - return _reduce_statistics_per_month(result, types) - def _get_last_statistics_stmt( metadata_id: int, @@ -2047,6 +2080,26 @@ def _filter_unique_constraint_integrity_error( return _filter_unique_constraint_integrity_error +def _import_statistics_with_session( + session: Session, + metadata: StatisticMetaData, + statistics: Iterable[StatisticData], + table: type[Statistics | StatisticsShortTerm], +) -> bool: + """Import statistics to the database.""" + old_metadata_dict = get_metadata_with_session( + session, statistic_ids=[metadata["statistic_id"]] + ) + metadata_id = _update_or_add_metadata(session, metadata, old_metadata_dict) + for stat in statistics: + if stat_id := _statistics_exists(session, table, metadata_id, stat["start"]): + _update_statistics(session, table, stat_id, stat) + else: + _insert_statistics(session, table, metadata_id, stat) + + return True + + @retryable_database_job("statistics") def import_statistics( instance: Recorder, @@ -2060,19 +2113,7 @@ def import_statistics( session=instance.get_session(), exception_filter=_filter_unique_constraint_integrity_error(instance), ) as session: - old_metadata_dict = get_metadata_with_session( - session, statistic_ids=[metadata["statistic_id"]] - ) - metadata_id = _update_or_add_metadata(session, metadata, old_metadata_dict) - for stat in statistics: - if stat_id := _statistics_exists( - session, table, metadata_id, stat["start"] - ): - _update_statistics(session, table, stat_id, stat) - else: - _insert_statistics(session, table, metadata_id, stat) - - return True + return _import_statistics_with_session(session, metadata, statistics, table) @retryable_database_job("adjust_statistics") @@ -2189,3 +2230,232 @@ def async_change_statistics_unit( new_unit_of_measurement=new_unit_of_measurement, old_unit_of_measurement=old_unit_of_measurement, ) + + +def _validate_db_schema_utf8( + instance: Recorder, session_maker: Callable[[], Session] +) -> set[str]: + """Do some basic checks for common schema errors caused by manual migration.""" + schema_errors: set[str] = set() + + # Lack of full utf8 support is only an issue for MySQL / MariaDB + if instance.dialect_name != SupportedDialect.MYSQL: + return schema_errors + + # This name can't be represented unless 4-byte UTF-8 unicode is supported + utf8_name = "𓆚𓃗" + statistic_id = f"{DOMAIN}.db_test" + + metadata: StatisticMetaData = { + "has_mean": True, + "has_sum": True, + "name": utf8_name, + "source": DOMAIN, + "statistic_id": statistic_id, + "unit_of_measurement": None, + } + + # Try inserting some metadata which needs utfmb4 support + try: + with session_scope(session=session_maker()) as session: + old_metadata_dict = get_metadata_with_session( + session, statistic_ids=[statistic_id] + ) + try: + _update_or_add_metadata(session, metadata, old_metadata_dict) + _clear_statistics_with_session(session, statistic_ids=[statistic_id]) + except OperationalError as err: + if err.orig and err.orig.args[0] == 1366: + _LOGGER.debug( + "Database table statistics_meta does not support 4-byte UTF-8" + ) + schema_errors.add("statistics_meta.4-byte UTF-8") + session.rollback() + else: + raise + except Exception as exc: # pylint: disable=broad-except + _LOGGER.exception("Error when validating DB schema: %s", exc) + return schema_errors + + +def _validate_db_schema( + hass: HomeAssistant, instance: Recorder, session_maker: Callable[[], Session] +) -> set[str]: + """Do some basic checks for common schema errors caused by manual migration.""" + schema_errors: set[str] = set() + + # Wrong precision is only an issue for MySQL / MariaDB / PostgreSQL + if instance.dialect_name not in ( + SupportedDialect.MYSQL, + SupportedDialect.POSTGRESQL, + ): + return schema_errors + + # This number can't be accurately represented as a 32-bit float + precise_number = 1.000000000000001 + # This time can't be accurately represented unless datetimes have µs precision + precise_time = datetime(2020, 10, 6, microsecond=1, tzinfo=dt_util.UTC) + + start_time = datetime(2020, 10, 6, tzinfo=dt_util.UTC) + statistic_id = f"{DOMAIN}.db_test" + + metadata: StatisticMetaData = { + "has_mean": True, + "has_sum": True, + "name": None, + "source": DOMAIN, + "statistic_id": statistic_id, + "unit_of_measurement": None, + } + statistics: StatisticData = { + "last_reset": precise_time, + "max": precise_number, + "mean": precise_number, + "min": precise_number, + "start": precise_time, + "state": precise_number, + "sum": precise_number, + } + + def check_columns( + schema_errors: set[str], + stored: Mapping, + expected: Mapping, + columns: tuple[str, ...], + table_name: str, + supports: str, + ) -> None: + for column in columns: + if stored[column] != expected[column]: + schema_errors.add(f"{table_name}.{supports}") + _LOGGER.debug( + "Column %s in database table %s does not support %s (%s != %s)", + column, + table_name, + supports, + stored[column], + expected[column], + ) + + # Insert / adjust a test statistics row in each of the tables + tables: tuple[type[Statistics | StatisticsShortTerm], ...] = ( + Statistics, + StatisticsShortTerm, + ) + try: + with session_scope(session=session_maker()) as session: + for table in tables: + _import_statistics_with_session(session, metadata, (statistics,), table) + stored_statistics = _statistics_during_period_with_session( + hass, + session, + start_time, + None, + [statistic_id], + "hour" if table == Statistics else "5minute", + None, + {"last_reset", "max", "mean", "min", "state", "sum"}, + ) + if not (stored_statistic := stored_statistics.get(statistic_id)): + _LOGGER.warning( + "Schema validation failed for table: %s", table.__tablename__ + ) + continue + + check_columns( + schema_errors, + stored_statistic[0], + statistics, + ("max", "mean", "min", "state", "sum"), + table.__tablename__, + "double precision", + ) + assert statistics["last_reset"] + check_columns( + schema_errors, + stored_statistic[0], + { + "last_reset": statistics["last_reset"], + "start": statistics["start"], + }, + ("start", "last_reset"), + table.__tablename__, + "µs precision", + ) + _clear_statistics_with_session(session, statistic_ids=[statistic_id]) + except Exception as exc: # pylint: disable=broad-except + _LOGGER.exception("Error when validating DB schema: %s", exc) + + return schema_errors + + +def validate_db_schema( + hass: HomeAssistant, instance: Recorder, session_maker: Callable[[], Session] +) -> set[str]: + """Do some basic checks for common schema errors caused by manual migration.""" + schema_errors: set[str] = set() + schema_errors |= _validate_db_schema_utf8(instance, session_maker) + schema_errors |= _validate_db_schema(hass, instance, session_maker) + if schema_errors: + _LOGGER.debug( + "Detected statistics schema errors: %s", ", ".join(sorted(schema_errors)) + ) + return schema_errors + + +def correct_db_schema( + engine: Engine, session_maker: Callable[[], Session], schema_errors: set[str] +) -> None: + """Correct issues detected by validate_db_schema.""" + from .migration import _modify_columns # pylint: disable=import-outside-toplevel + + if "statistics_meta.4-byte UTF-8" in schema_errors: + # Attempt to convert the table to utf8mb4 + _LOGGER.warning( + "Updating character set and collation of table %s to utf8mb4. " + "Note: this can take several minutes on large databases and slow " + "computers. Please be patient!", + "statistics_meta", + ) + with contextlib.suppress(SQLAlchemyError): + with session_scope(session=session_maker()) as session: + connection = session.connection() + connection.execute( + # Using LOCK=EXCLUSIVE to prevent the database from corrupting + # https://github.com/home-assistant/core/issues/56104 + text( + "ALTER TABLE statistics_meta CONVERT TO " + "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, LOCK=EXCLUSIVE" + ) + ) + + tables: tuple[type[Statistics | StatisticsShortTerm], ...] = ( + Statistics, + StatisticsShortTerm, + ) + for table in tables: + if f"{table.__tablename__}.double precision" in schema_errors: + # Attempt to convert float columns to double precision + _modify_columns( + session_maker, + engine, + table.__tablename__, + [ + "mean DOUBLE PRECISION", + "min DOUBLE PRECISION", + "max DOUBLE PRECISION", + "state DOUBLE PRECISION", + "sum DOUBLE PRECISION", + ], + ) + if f"{table.__tablename__}.µs precision" in schema_errors: + # Attempt to convert datetime columns to µs precision + _modify_columns( + session_maker, + engine, + table.__tablename__, + [ + "last_reset DATETIME(6)", + "start DATETIME(6)", + ], + ) diff --git a/tests/components/recorder/test_statistics.py b/tests/components/recorder/test_statistics.py index 8950365fd95..376087fdb1e 100644 --- a/tests/components/recorder/test_statistics.py +++ b/tests/components/recorder/test_statistics.py @@ -1,13 +1,14 @@ """The tests for sensor recorder platform.""" # pylint: disable=protected-access,invalid-name -from datetime import timedelta +from datetime import datetime, timedelta import importlib import sys -from unittest.mock import patch, sentinel +from unittest.mock import ANY, DEFAULT, MagicMock, patch, sentinel import pytest from pytest import approx from sqlalchemy import create_engine +from sqlalchemy.exc import OperationalError from sqlalchemy.orm import Session from homeassistant.components import recorder @@ -16,6 +17,8 @@ from homeassistant.components.recorder.const import SQLITE_URL_PREFIX from homeassistant.components.recorder.db_schema import StatisticsShortTerm from homeassistant.components.recorder.models import process_timestamp from homeassistant.components.recorder.statistics import ( + _statistics_during_period_with_session, + _update_or_add_metadata, async_add_external_statistics, async_import_statistics, delete_statistics_duplicates, @@ -1475,6 +1478,196 @@ def test_delete_metadata_duplicates_no_duplicates(hass_recorder, caplog): assert "duplicated statistics_meta rows" not in caplog.text +@pytest.mark.parametrize("enable_statistics_table_validation", [True]) +@pytest.mark.parametrize("db_engine", ("mysql", "postgresql")) +async def test_validate_db_schema( + async_setup_recorder_instance, hass, caplog, db_engine +): + """Test validating DB schema with MySQL and PostgreSQL. + + Note: The test uses SQLite, the purpose is only to exercise the code. + """ + with patch( + "homeassistant.components.recorder.core.Recorder.dialect_name", db_engine + ): + await async_setup_recorder_instance(hass) + await async_wait_recording_done(hass) + assert "Schema validation failed" not in caplog.text + assert "Detected statistics schema errors" not in caplog.text + assert "Database is about to correct DB schema errors" not in caplog.text + + +@pytest.mark.parametrize("enable_statistics_table_validation", [True]) +async def test_validate_db_schema_fix_utf8_issue( + async_setup_recorder_instance, hass, caplog +): + """Test validating DB schema with MySQL. + + Note: The test uses SQLite, the purpose is only to exercise the code. + """ + orig_error = MagicMock() + orig_error.args = [1366] + utf8_error = OperationalError("", "", orig=orig_error) + with patch( + "homeassistant.components.recorder.core.Recorder.dialect_name", "mysql" + ), patch( + "homeassistant.components.recorder.statistics._update_or_add_metadata", + side_effect=[utf8_error, DEFAULT, DEFAULT], + wraps=_update_or_add_metadata, + ): + await async_setup_recorder_instance(hass) + await async_wait_recording_done(hass) + + assert "Schema validation failed" not in caplog.text + assert ( + "Database is about to correct DB schema errors: statistics_meta.4-byte UTF-8" + in caplog.text + ) + assert ( + "Updating character set and collation of table statistics_meta to utf8mb4" + in caplog.text + ) + + +@pytest.mark.parametrize("enable_statistics_table_validation", [True]) +@pytest.mark.parametrize("db_engine", ("mysql", "postgresql")) +@pytest.mark.parametrize( + "table, replace_index", (("statistics", 0), ("statistics_short_term", 1)) +) +@pytest.mark.parametrize( + "column, value", + (("max", 1.0), ("mean", 1.0), ("min", 1.0), ("state", 1.0), ("sum", 1.0)), +) +async def test_validate_db_schema_fix_float_issue( + async_setup_recorder_instance, + hass, + caplog, + db_engine, + table, + replace_index, + column, + value, +): + """Test validating DB schema with MySQL. + + Note: The test uses SQLite, the purpose is only to exercise the code. + """ + orig_error = MagicMock() + orig_error.args = [1366] + precise_number = 1.000000000000001 + precise_time = datetime(2020, 10, 6, microsecond=1, tzinfo=dt_util.UTC) + statistics = { + "recorder.db_test": [ + { + "last_reset": precise_time, + "max": precise_number, + "mean": precise_number, + "min": precise_number, + "start": precise_time, + "state": precise_number, + "sum": precise_number, + } + ] + } + statistics["recorder.db_test"][0][column] = value + fake_statistics = [DEFAULT, DEFAULT] + fake_statistics[replace_index] = statistics + + with patch( + "homeassistant.components.recorder.core.Recorder.dialect_name", db_engine + ), patch( + "homeassistant.components.recorder.statistics._statistics_during_period_with_session", + side_effect=fake_statistics, + wraps=_statistics_during_period_with_session, + ), patch( + "homeassistant.components.recorder.migration._modify_columns" + ) as modify_columns_mock: + await async_setup_recorder_instance(hass) + await async_wait_recording_done(hass) + + assert "Schema validation failed" not in caplog.text + assert ( + f"Database is about to correct DB schema errors: {table}.double precision" + in caplog.text + ) + modification = [ + "mean DOUBLE PRECISION", + "min DOUBLE PRECISION", + "max DOUBLE PRECISION", + "state DOUBLE PRECISION", + "sum DOUBLE PRECISION", + ] + modify_columns_mock.assert_called_once_with(ANY, ANY, table, modification) + + +@pytest.mark.parametrize("enable_statistics_table_validation", [True]) +@pytest.mark.parametrize("db_engine", ("mysql", "postgresql")) +@pytest.mark.parametrize( + "table, replace_index", (("statistics", 0), ("statistics_short_term", 1)) +) +@pytest.mark.parametrize( + "column, value", + ( + ("last_reset", "2020-10-06T00:00:00+00:00"), + ("start", "2020-10-06T00:00:00+00:00"), + ), +) +async def test_validate_db_schema_fix_statistics_datetime_issue( + async_setup_recorder_instance, + hass, + caplog, + db_engine, + table, + replace_index, + column, + value, +): + """Test validating DB schema with MySQL. + + Note: The test uses SQLite, the purpose is only to exercise the code. + """ + orig_error = MagicMock() + orig_error.args = [1366] + precise_number = 1.000000000000001 + precise_time = datetime(2020, 10, 6, microsecond=1, tzinfo=dt_util.UTC) + statistics = { + "recorder.db_test": [ + { + "last_reset": precise_time, + "max": precise_number, + "mean": precise_number, + "min": precise_number, + "start": precise_time, + "state": precise_number, + "sum": precise_number, + } + ] + } + statistics["recorder.db_test"][0][column] = value + fake_statistics = [DEFAULT, DEFAULT] + fake_statistics[replace_index] = statistics + + with patch( + "homeassistant.components.recorder.core.Recorder.dialect_name", db_engine + ), patch( + "homeassistant.components.recorder.statistics._statistics_during_period_with_session", + side_effect=fake_statistics, + wraps=_statistics_during_period_with_session, + ), patch( + "homeassistant.components.recorder.migration._modify_columns" + ) as modify_columns_mock: + await async_setup_recorder_instance(hass) + await async_wait_recording_done(hass) + + assert "Schema validation failed" not in caplog.text + assert ( + f"Database is about to correct DB schema errors: {table}.µs precision" + in caplog.text + ) + modification = ["last_reset DATETIME(6)", "start DATETIME(6)"] + modify_columns_mock.assert_called_once_with(ANY, ANY, table, modification) + + def record_states(hass): """Record some test states. diff --git a/tests/conftest.py b/tests/conftest.py index a508b8c32ef..b6638968182 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,7 @@ import asyncio from collections.abc import AsyncGenerator, Callable, Generator from contextlib import asynccontextmanager import functools +import itertools from json import JSONDecoder, loads import logging import sqlite3 @@ -860,6 +861,16 @@ def enable_statistics(): return False +@pytest.fixture +def enable_statistics_table_validation(): + """Fixture to control enabling of recorder's statistics table validation. + + To enable statistics table validation, tests can be marked with: + @pytest.mark.parametrize("enable_statistics_table_validation", [True]) + """ + return False + + @pytest.fixture def enable_nightly_purge(): """Fixture to control enabling of recorder's nightly purge job. @@ -902,6 +913,7 @@ def hass_recorder( recorder_db_url, enable_nightly_purge, enable_statistics, + enable_statistics_table_validation, hass_storage, ): """Home Assistant fixture with in-memory recorder.""" @@ -910,6 +922,11 @@ def hass_recorder( hass = get_test_home_assistant() nightly = recorder.Recorder.async_nightly_tasks if enable_nightly_purge else None stats = recorder.Recorder.async_periodic_statistics if enable_statistics else None + stats_validate = ( + recorder.statistics.validate_db_schema + if enable_statistics_table_validation + else itertools.repeat(set()) + ) with patch( "homeassistant.components.recorder.Recorder.async_nightly_tasks", side_effect=nightly, @@ -918,6 +935,10 @@ def hass_recorder( "homeassistant.components.recorder.Recorder.async_periodic_statistics", side_effect=stats, autospec=True, + ), patch( + "homeassistant.components.recorder.migration.statistics_validate_db_schema", + side_effect=stats_validate, + autospec=True, ): def setup_recorder(config=None): @@ -962,12 +983,18 @@ async def async_setup_recorder_instance( hass_fixture_setup, enable_nightly_purge, enable_statistics, + enable_statistics_table_validation, ) -> AsyncGenerator[SetupRecorderInstanceT, None]: """Yield callable to setup recorder instance.""" assert not hass_fixture_setup nightly = recorder.Recorder.async_nightly_tasks if enable_nightly_purge else None stats = recorder.Recorder.async_periodic_statistics if enable_statistics else None + stats_validate = ( + recorder.statistics.validate_db_schema + if enable_statistics_table_validation + else itertools.repeat(set()) + ) with patch( "homeassistant.components.recorder.Recorder.async_nightly_tasks", side_effect=nightly, @@ -976,6 +1003,10 @@ async def async_setup_recorder_instance( "homeassistant.components.recorder.Recorder.async_periodic_statistics", side_effect=stats, autospec=True, + ), patch( + "homeassistant.components.recorder.migration.statistics_validate_db_schema", + side_effect=stats_validate, + autospec=True, ): async def async_setup_recorder( From 1453308bc41eb8caa3310d44b1c72eb1c31c5079 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Tue, 29 Nov 2022 04:27:05 -0500 Subject: [PATCH 0840/1033] Add reproduce state support to `text` platform (#82772) Co-authored-by: Franck Nijhof --- .../components/text/reproduce_state.py | 57 +++++++++++++++++++ tests/components/text/test_reproduce_state.py | 53 +++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 homeassistant/components/text/reproduce_state.py create mode 100644 tests/components/text/test_reproduce_state.py diff --git a/homeassistant/components/text/reproduce_state.py b/homeassistant/components/text/reproduce_state.py new file mode 100644 index 00000000000..99013a63a06 --- /dev/null +++ b/homeassistant/components/text/reproduce_state.py @@ -0,0 +1,57 @@ +"""Reproduce a Text entity state.""" +from __future__ import annotations + +import asyncio +from collections.abc import Iterable +import logging +from typing import Any + +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import Context, HomeAssistant, State + +from .const import ATTR_VALUE, DOMAIN, SERVICE_SET_VALUE + +_LOGGER = logging.getLogger(__name__) + + +async def _async_reproduce_state( + hass: HomeAssistant, + state: State, + *, + context: Context | None = None, + reproduce_options: dict[str, Any] | None = None, +) -> None: + """Reproduce a single state.""" + if (cur_state := hass.states.get(state.entity_id)) is None: + _LOGGER.warning("Unable to find entity %s", state.entity_id) + return + + # Return if we are already at the right state. + if cur_state.state == state.state: + return + + service = SERVICE_SET_VALUE + service_data = {ATTR_ENTITY_ID: state.entity_id, ATTR_VALUE: state.state} + + await hass.services.async_call( + DOMAIN, service, service_data, context=context, blocking=True + ) + + +async def async_reproduce_states( + hass: HomeAssistant, + states: Iterable[State], + *, + context: Context | None = None, + reproduce_options: dict[str, Any] | None = None, +) -> None: + """Reproduce multiple Text states.""" + # Reproduce states in parallel. + await asyncio.gather( + *( + _async_reproduce_state( + hass, state, context=context, reproduce_options=reproduce_options + ) + for state in states + ) + ) diff --git a/tests/components/text/test_reproduce_state.py b/tests/components/text/test_reproduce_state.py new file mode 100644 index 00000000000..fd2bd7b7c90 --- /dev/null +++ b/tests/components/text/test_reproduce_state.py @@ -0,0 +1,53 @@ +"""Test reproduce state for Text entities.""" +from homeassistant.components.text.const import ( + ATTR_MAX, + ATTR_MIN, + ATTR_MODE, + ATTR_PATTERN, + DOMAIN, + SERVICE_SET_VALUE, +) +from homeassistant.core import State +from homeassistant.helpers.state import async_reproduce_state + +from tests.common import async_mock_service + +VALID_TEXT1 = "Hello" +VALID_TEXT2 = "World" + + +async def test_reproducing_states(hass, caplog): + """Test reproducing Text states.""" + + hass.states.async_set( + "text.test_text", + VALID_TEXT1, + {ATTR_MIN: 1, ATTR_MAX: 5, ATTR_MODE: "text", ATTR_PATTERN: None}, + ) + + # These calls should do nothing as entities already in desired state + await async_reproduce_state( + hass, + [ + State("text.test_text", VALID_TEXT1), + # Should not raise + State("text.non_existing", "234"), + ], + ) + + assert hass.states.get("text.test_text").state == VALID_TEXT1 + + # Test reproducing with different state + calls = async_mock_service(hass, DOMAIN, SERVICE_SET_VALUE) + await async_reproduce_state( + hass, + [ + State("text.test_text", VALID_TEXT2), + # Should not raise + State("text.non_existing", "234"), + ], + ) + + assert len(calls) == 1 + assert calls[0].domain == DOMAIN + assert calls[0].data == {"entity_id": "text.test_text", "value": VALID_TEXT2} From f88487be2551655514f631b7536f0cc40bafddf4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 28 Nov 2022 23:27:28 -1000 Subject: [PATCH 0841/1033] Bump yalexs-ble to 1.9.7 (#82888) --- homeassistant/components/yalexs_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/yalexs_ble/manifest.json b/homeassistant/components/yalexs_ble/manifest.json index b345f996998..d3dfcd1b6dd 100644 --- a/homeassistant/components/yalexs_ble/manifest.json +++ b/homeassistant/components/yalexs_ble/manifest.json @@ -3,7 +3,7 @@ "name": "Yale Access Bluetooth", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yalexs_ble", - "requirements": ["yalexs-ble==1.9.6"], + "requirements": ["yalexs-ble==1.9.7"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [ diff --git a/requirements_all.txt b/requirements_all.txt index 88e519fdc7a..ad71455ddab 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2597,7 +2597,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.9.6 +yalexs-ble==1.9.7 # homeassistant.components.august yalexs==1.2.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6cb0c2eac79..40e42afd3fe 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1807,7 +1807,7 @@ xmltodict==0.13.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.9.6 +yalexs-ble==1.9.7 # homeassistant.components.august yalexs==1.2.6 From a5890b237420bd70194e1bdb84d35380d45fb2dc Mon Sep 17 00:00:00 2001 From: an0nfunc <40771419+an0nfunc@users.noreply.github.com> Date: Tue, 29 Nov 2022 10:29:34 +0100 Subject: [PATCH 0842/1033] Support xiaomi_miio Smartmi Air Purifier (zhimi.airpurifier.za1) (#80387) --- homeassistant/components/xiaomi_miio/const.py | 19 ++++++++++++------- homeassistant/components/xiaomi_miio/fan.py | 9 +++++++++ .../components/xiaomi_miio/number.py | 3 +++ .../components/xiaomi_miio/select.py | 4 ++++ .../components/xiaomi_miio/sensor.py | 19 +++++++++++++++++++ .../components/xiaomi_miio/switch.py | 3 +++ 6 files changed, 50 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/xiaomi_miio/const.py b/homeassistant/components/xiaomi_miio/const.py index 0c090a58e02..6621e41e7aa 100644 --- a/homeassistant/components/xiaomi_miio/const.py +++ b/homeassistant/components/xiaomi_miio/const.py @@ -1,8 +1,13 @@ """Constants for the Xiaomi Miio component.""" -from miio.vacuum import ( +from miio.integrations.vacuum.roborock.vacuum import ( + ROCKROBO_E2, + ROCKROBO_S4, + ROCKROBO_S4_MAX, ROCKROBO_S5, + ROCKROBO_S5_MAX, ROCKROBO_S6, ROCKROBO_S6_MAXV, + ROCKROBO_S6_PURE, ROCKROBO_S7, ROCKROBO_S7_MAXV, ROCKROBO_V1, @@ -70,6 +75,7 @@ MODEL_AIRPURIFIER_V1 = "zhimi.airpurifier.v1" MODEL_AIRPURIFIER_V2 = "zhimi.airpurifier.v2" MODEL_AIRPURIFIER_V3 = "zhimi.airpurifier.v3" MODEL_AIRPURIFIER_V5 = "zhimi.airpurifier.v5" +MODEL_AIRPURIFIER_ZA1 = "zhimi.airpurifier.za1" MODEL_AIRHUMIDIFIER_V1 = "zhimi.humidifier.v1" MODEL_AIRHUMIDIFIER_CA1 = "zhimi.humidifier.ca1" @@ -124,6 +130,7 @@ MODELS_PURIFIER_MIOT = [ MODEL_AIRPURIFIER_4_LITE_RMB1, MODEL_AIRPURIFIER_4, MODEL_AIRPURIFIER_4_PRO, + MODEL_AIRPURIFIER_ZA1, ] MODELS_PURIFIER_MIIO = [ MODEL_AIRPURIFIER_V1, @@ -213,12 +220,6 @@ MODELS_LIGHT = ( + MODELS_LIGHT_MONO ) -# TODO: use const from pythonmiio once new release with the constant has been published. # pylint: disable=fixme -ROCKROBO_S4 = "roborock.vacuum.s4" -ROCKROBO_S4_MAX = "roborock.vacuum.a19" -ROCKROBO_S5_MAX = "roborock.vacuum.s5e" -ROCKROBO_S6_PURE = "roborock.vacuum.a08" -ROCKROBO_E2 = "roborock.vacuum.e2" ROBOROCK_GENERIC = "roborock.vacuum" ROCKROBO_GENERIC = "rockrobo.vacuum" MODELS_VACUUM = [ @@ -397,6 +398,10 @@ FEATURE_FLAGS_AIRPURIFIER_V3 = ( FEATURE_SET_BUZZER | FEATURE_SET_CHILD_LOCK | FEATURE_SET_LED ) +FEATURE_FLAGS_AIRPURIFIER_ZA1 = ( + FEATURE_SET_BUZZER | FEATURE_SET_CHILD_LOCK | FEATURE_SET_FAVORITE_LEVEL +) + FEATURE_FLAGS_AIRHUMIDIFIER = ( FEATURE_SET_BUZZER | FEATURE_SET_CHILD_LOCK | FEATURE_SET_TARGET_HUMIDITY ) diff --git a/homeassistant/components/xiaomi_miio/fan.py b/homeassistant/components/xiaomi_miio/fan.py index dbc8c7a66d9..7153247e7d2 100644 --- a/homeassistant/components/xiaomi_miio/fan.py +++ b/homeassistant/components/xiaomi_miio/fan.py @@ -55,6 +55,7 @@ from .const import ( FEATURE_FLAGS_AIRPURIFIER_PRO, FEATURE_FLAGS_AIRPURIFIER_PRO_V7, FEATURE_FLAGS_AIRPURIFIER_V3, + FEATURE_FLAGS_AIRPURIFIER_ZA1, FEATURE_FLAGS_FAN, FEATURE_FLAGS_FAN_1C, FEATURE_FLAGS_FAN_P5, @@ -77,6 +78,7 @@ from .const import ( MODEL_AIRPURIFIER_PRO, MODEL_AIRPURIFIER_PRO_V7, MODEL_AIRPURIFIER_V3, + MODEL_AIRPURIFIER_ZA1, MODEL_FAN_1C, MODEL_FAN_P5, MODEL_FAN_P9, @@ -160,6 +162,7 @@ PRESET_MODES_AIRPURIFIER_PRO = ["Auto", "Silent", "Favorite"] PRESET_MODES_AIRPURIFIER_PRO_V7 = PRESET_MODES_AIRPURIFIER_PRO PRESET_MODES_AIRPURIFIER_2S = ["Auto", "Silent", "Favorite"] PRESET_MODES_AIRPURIFIER_3C = ["Auto", "Silent", "Favorite"] +PRESET_MODES_AIRPURIFIER_ZA1 = ["Auto", "Silent", "Favorite"] PRESET_MODES_AIRPURIFIER_V3 = [ "Auto", "Silent", @@ -449,6 +452,12 @@ class XiaomiAirPurifier(XiaomiGenericAirPurifier): self._preset_modes = PRESET_MODES_AIRPURIFIER_2S self._attr_supported_features = FanEntityFeature.PRESET_MODE self._speed_count = 1 + elif self._model == MODEL_AIRPURIFIER_ZA1: + self._device_features = FEATURE_FLAGS_AIRPURIFIER_ZA1 + self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_MIOT + self._preset_modes = PRESET_MODES_AIRPURIFIER_ZA1 + self._attr_supported_features = FanEntityFeature.PRESET_MODE + self._speed_count = 1 elif self._model in MODELS_PURIFIER_MIOT: self._device_features = FEATURE_FLAGS_AIRPURIFIER_MIOT self._available_attributes = AVAILABLE_ATTRIBUTES_AIRPURIFIER_MIOT diff --git a/homeassistant/components/xiaomi_miio/number.py b/homeassistant/components/xiaomi_miio/number.py index 7c5439d8d35..869379530fc 100644 --- a/homeassistant/components/xiaomi_miio/number.py +++ b/homeassistant/components/xiaomi_miio/number.py @@ -39,6 +39,7 @@ from .const import ( FEATURE_FLAGS_AIRPURIFIER_PRO_V7, FEATURE_FLAGS_AIRPURIFIER_V1, FEATURE_FLAGS_AIRPURIFIER_V3, + FEATURE_FLAGS_AIRPURIFIER_ZA1, FEATURE_FLAGS_FAN, FEATURE_FLAGS_FAN_1C, FEATURE_FLAGS_FAN_P5, @@ -73,6 +74,7 @@ from .const import ( MODEL_AIRPURIFIER_PRO_V7, MODEL_AIRPURIFIER_V1, MODEL_AIRPURIFIER_V3, + MODEL_AIRPURIFIER_ZA1, MODEL_FAN_1C, MODEL_FAN_P5, MODEL_FAN_P9, @@ -249,6 +251,7 @@ MODEL_TO_FEATURES_MAP = { MODEL_AIRPURIFIER_4_LITE_RMB1: FEATURE_FLAGS_AIRPURIFIER_4_LITE, MODEL_AIRPURIFIER_4: FEATURE_FLAGS_AIRPURIFIER_4, MODEL_AIRPURIFIER_4_PRO: FEATURE_FLAGS_AIRPURIFIER_4, + MODEL_AIRPURIFIER_ZA1: FEATURE_FLAGS_AIRPURIFIER_ZA1, MODEL_FAN_1C: FEATURE_FLAGS_FAN_1C, MODEL_FAN_P10: FEATURE_FLAGS_FAN_P10_P11, MODEL_FAN_P11: FEATURE_FLAGS_FAN_P10_P11, diff --git a/homeassistant/components/xiaomi_miio/select.py b/homeassistant/components/xiaomi_miio/select.py index 9b5f0f50a06..87cafe3c58a 100644 --- a/homeassistant/components/xiaomi_miio/select.py +++ b/homeassistant/components/xiaomi_miio/select.py @@ -53,6 +53,7 @@ from .const import ( MODEL_AIRPURIFIER_M1, MODEL_AIRPURIFIER_M2, MODEL_AIRPURIFIER_PROH, + MODEL_AIRPURIFIER_ZA1, MODEL_FAN_SA1, MODEL_FAN_V2, MODEL_FAN_V3, @@ -113,6 +114,9 @@ MODEL_TO_ATTR_MAP: dict[str, list] = { MODEL_AIRPURIFIER_3: [ AttributeEnumMapping(ATTR_LED_BRIGHTNESS, AirpurifierMiotLedBrightness) ], + MODEL_AIRPURIFIER_ZA1: [ + AttributeEnumMapping(ATTR_LED_BRIGHTNESS, AirpurifierMiotLedBrightness) + ], MODEL_AIRPURIFIER_3H: [ AttributeEnumMapping(ATTR_LED_BRIGHTNESS, AirpurifierMiotLedBrightness) ], diff --git a/homeassistant/components/xiaomi_miio/sensor.py b/homeassistant/components/xiaomi_miio/sensor.py index 56938a4bd34..a83f3ef528d 100644 --- a/homeassistant/components/xiaomi_miio/sensor.py +++ b/homeassistant/components/xiaomi_miio/sensor.py @@ -70,6 +70,7 @@ from .const import ( MODEL_AIRPURIFIER_PRO_V7, MODEL_AIRPURIFIER_V2, MODEL_AIRPURIFIER_V3, + MODEL_AIRPURIFIER_ZA1, MODEL_FAN_P5, MODEL_FAN_V2, MODEL_FAN_V3, @@ -97,6 +98,7 @@ UNIT_LUMEN = "lm" ATTR_ACTUAL_SPEED = "actual_speed" ATTR_AIR_QUALITY = "air_quality" +ATTR_TVOC = "tvoc" ATTR_AQI = "aqi" ATTR_BATTERY = "battery" ATTR_CARBON_DIOXIDE = "co2" @@ -262,6 +264,13 @@ SENSOR_TYPES = { icon="mdi:cloud", state_class=SensorStateClass.MEASUREMENT, ), + ATTR_TVOC: XiaomiMiioSensorDescription( + key=ATTR_TVOC, + name="TVOC", + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, + ), ATTR_PM10: XiaomiMiioSensorDescription( key=ATTR_PM10, name="PM10", @@ -452,6 +461,15 @@ PURIFIER_3C_SENSORS = ( ATTR_MOTOR_SPEED, ATTR_PM25, ) +PURIFIER_ZA1_SENSORS = ( + ATTR_FILTER_LIFE_REMAINING, + ATTR_FILTER_USE, + ATTR_MOTOR_SPEED, + ATTR_PM25, + ATTR_TVOC, + ATTR_HUMIDITY, + ATTR_TEMPERATURE, +) PURIFIER_V2_SENSORS = ( ATTR_FILTER_LIFE_REMAINING, ATTR_FILTER_USE, @@ -548,6 +566,7 @@ MODEL_TO_SENSORS_MAP: dict[str, tuple[str, ...]] = { MODEL_AIRPURIFIER_PRO_V7: PURIFIER_PRO_V7_SENSORS, MODEL_AIRPURIFIER_V2: PURIFIER_V2_SENSORS, MODEL_AIRPURIFIER_V3: PURIFIER_V3_SENSORS, + MODEL_AIRPURIFIER_ZA1: PURIFIER_ZA1_SENSORS, MODEL_FAN_V2: FAN_V2_V3_SENSORS, MODEL_FAN_V3: FAN_V2_V3_SENSORS, MODEL_FAN_ZA5: FAN_ZA5_SENSORS, diff --git a/homeassistant/components/xiaomi_miio/switch.py b/homeassistant/components/xiaomi_miio/switch.py index 2f45ba0adca..f2b7b071923 100644 --- a/homeassistant/components/xiaomi_miio/switch.py +++ b/homeassistant/components/xiaomi_miio/switch.py @@ -53,6 +53,7 @@ from .const import ( FEATURE_FLAGS_AIRPURIFIER_PRO_V7, FEATURE_FLAGS_AIRPURIFIER_V1, FEATURE_FLAGS_AIRPURIFIER_V3, + FEATURE_FLAGS_AIRPURIFIER_ZA1, FEATURE_FLAGS_FAN, FEATURE_FLAGS_FAN_1C, FEATURE_FLAGS_FAN_P5, @@ -90,6 +91,7 @@ from .const import ( MODEL_AIRPURIFIER_PRO_V7, MODEL_AIRPURIFIER_V1, MODEL_AIRPURIFIER_V3, + MODEL_AIRPURIFIER_ZA1, MODEL_FAN_1C, MODEL_FAN_P5, MODEL_FAN_P9, @@ -204,6 +206,7 @@ MODEL_TO_FEATURES_MAP = { MODEL_AIRPURIFIER_4_LITE_RMB1: FEATURE_FLAGS_AIRPURIFIER_4_LITE, MODEL_AIRPURIFIER_4: FEATURE_FLAGS_AIRPURIFIER_4, MODEL_AIRPURIFIER_4_PRO: FEATURE_FLAGS_AIRPURIFIER_4, + MODEL_AIRPURIFIER_ZA1: FEATURE_FLAGS_AIRPURIFIER_ZA1, MODEL_FAN_1C: FEATURE_FLAGS_FAN_1C, MODEL_FAN_P10: FEATURE_FLAGS_FAN_P10_P11, MODEL_FAN_P11: FEATURE_FLAGS_FAN_P10_P11, From 596016c2acb95b5d25475ea752e0651f034720f8 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Tue, 29 Nov 2022 04:44:31 -0500 Subject: [PATCH 0843/1033] Add new features from new UniFi Protect (#82892) Co-authored-by: J. Nick Koston --- .../components/unifiprotect/binary_sensor.py | 50 +++++++++++++++ .../components/unifiprotect/media_source.py | 9 ++- .../components/unifiprotect/sensor.py | 29 ++++++++- .../components/unifiprotect/switch.py | 22 ++++++- .../unifiprotect/test_binary_sensor.py | 8 +-- tests/components/unifiprotect/test_sensor.py | 63 +++++++++++++++++-- tests/components/unifiprotect/test_switch.py | 2 + 7 files changed, 166 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/unifiprotect/binary_sensor.py b/homeassistant/components/unifiprotect/binary_sensor.py index 3d018d6eee1..d38fe8582ab 100644 --- a/homeassistant/components/unifiprotect/binary_sensor.py +++ b/homeassistant/components/unifiprotect/binary_sensor.py @@ -14,6 +14,7 @@ from pyunifiprotect.data import ( ProtectAdoptableDeviceModel, ProtectModelWithId, Sensor, + SmartDetectAudioType, SmartDetectObjectType, ) from pyunifiprotect.data.nvr import UOSDisk @@ -201,6 +202,24 @@ CAMERA_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = ( ufp_value="is_package_detection_on", ufp_perm=PermRequired.NO_WRITE, ), + ProtectBinaryEntityDescription( + key="smart_licenseplate", + name="Detections: License Plate", + icon="mdi:car", + entity_category=EntityCategory.DIAGNOSTIC, + ufp_required_field="can_detect_license_plate", + ufp_value="is_license_plate_detection_on", + ufp_perm=PermRequired.NO_WRITE, + ), + ProtectBinaryEntityDescription( + key="smart_smoke", + name="Detections: Smoke/CO", + icon="mdi:fire", + entity_category=EntityCategory.DIAGNOSTIC, + ufp_required_field="can_detect_smoke", + ufp_value="is_smoke_detection_on", + ufp_perm=PermRequired.NO_WRITE, + ), ) LIGHT_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = ( @@ -381,6 +400,37 @@ MOTION_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = ( ufp_event_obj="last_smart_detect_event", ufp_smart_type=SmartDetectObjectType.PACKAGE, ), + ProtectBinaryEventEntityDescription( + key="smart_audio_any", + name="Audio Detected", + icon="mdi:eye", + device_class=DEVICE_CLASS_DETECTION, + ufp_value="is_smart_detected", + ufp_required_field="feature_flags.has_smart_detect", + ufp_event_obj="last_smart_audio_detect_event", + ), + ProtectBinaryEventEntityDescription( + key="smart_audio_smoke", + name="Smoke Alarm Detected", + device_class=DEVICE_CLASS_DETECTION, + icon="mdi:fire", + ufp_value="is_smart_detected", + ufp_required_field="can_detect_smoke", + ufp_enabled="is_smoke_detection_on", + ufp_event_obj="last_smart_audio_detect_event", + ufp_smart_type=SmartDetectAudioType.SMOKE, + ), + ProtectBinaryEventEntityDescription( + key="smart_audio_cmonx", + name="CO Alarm Detected", + device_class=DEVICE_CLASS_DETECTION, + icon="mdi:fire", + ufp_value="is_smart_detected", + ufp_required_field="can_detect_smoke", + ufp_enabled="is_smoke_detection_on", + ufp_event_obj="last_smart_audio_detect_event", + ufp_smart_type=SmartDetectAudioType.CMONX, + ), ) DOORLOCK_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = ( diff --git a/homeassistant/components/unifiprotect/media_source.py b/homeassistant/components/unifiprotect/media_source.py index 6ebb36c11c5..81054d9aff5 100644 --- a/homeassistant/components/unifiprotect/media_source.py +++ b/homeassistant/components/unifiprotect/media_source.py @@ -406,10 +406,13 @@ class ProtectMediaSource(MediaSource): event_text = "Motion Event" elif event_type == EventType.SMART_DETECT.value: if isinstance(event, Event): - smart_type = event.smart_detect_types[0] + smart_types = event.smart_detect_types else: - smart_type = SmartDetectObjectType(event["smartDetectTypes"][0]) - event_text = f"Smart Detection - {smart_type.name.title()}" + smart_types = [ + SmartDetectObjectType(e) for e in event["smartDetectTypes"] + ] + smart_type_names = [s.name.title().replace("_", " ") for s in smart_types] + event_text = f"Smart Detection - {','.join(smart_type_names)}" title += f" {event_text}" nvr = data.api.bootstrap.nvr diff --git a/homeassistant/components/unifiprotect/sensor.py b/homeassistant/components/unifiprotect/sensor.py index 168faebe8ac..621fad2a1a5 100644 --- a/homeassistant/components/unifiprotect/sensor.py +++ b/homeassistant/components/unifiprotect/sensor.py @@ -15,6 +15,7 @@ from pyunifiprotect.data import ( ProtectDeviceModel, ProtectModelWithId, Sensor, + SmartDetectObjectType, ) from homeassistant.components.sensor import ( @@ -527,6 +528,15 @@ MOTION_SENSORS: tuple[ProtectSensorEventEntityDescription, ...] = ( ufp_value="is_smart_detected", ufp_event_obj="last_smart_detect_event", ), + ProtectSensorEventEntityDescription( + key="smart_obj_licenseplate", + name="License Plate Detected", + icon="mdi:car", + device_class=DEVICE_CLASS_DETECTION, + ufp_value="is_smart_detected", + ufp_event_obj="last_smart_detect_event", + ufp_smart_type=SmartDetectObjectType.LICENSE_PLATE, + ), ) @@ -756,7 +766,20 @@ class ProtectEventSensor(EventEntityMixin, SensorEntity): def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None: # do not call ProtectDeviceSensor method since we want event to get value here EventEntityMixin._async_update_device_from_protect(self, device) - if self._event is None: - self._attr_native_value = OBJECT_TYPE_NONE + if ( + self.entity_description.ufp_smart_type + == SmartDetectObjectType.LICENSE_PLATE + ): + if ( + self._event is None + or self._event.metadata is None + or self._event.metadata.license_plate is None + ): + self._attr_native_value = OBJECT_TYPE_NONE + else: + self._attr_native_value = self._event.metadata.license_plate.name else: - self._attr_native_value = self._event.smart_detect_types[0].value + if self._event is None: + self._attr_native_value = OBJECT_TYPE_NONE + else: + self._attr_native_value = self._event.smart_detect_types[0].value diff --git a/homeassistant/components/unifiprotect/switch.py b/homeassistant/components/unifiprotect/switch.py index 65de9f52913..fa501f6a364 100644 --- a/homeassistant/components/unifiprotect/switch.py +++ b/homeassistant/components/unifiprotect/switch.py @@ -126,7 +126,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( ), ProtectSwitchEntityDescription( key="osd_bitrate", - name="Overlay: Show Bitrate", + name="Overlay: Show Nerd Mode", icon="mdi:fullscreen", entity_category=EntityCategory.CONFIG, ufp_value="osd_settings.is_debug_enabled", @@ -182,6 +182,26 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( ufp_set_method="set_package_detection", ufp_perm=PermRequired.WRITE, ), + ProtectSwitchEntityDescription( + key="smart_licenseplate", + name="Detections: License Plate", + icon="mdi:car", + entity_category=EntityCategory.CONFIG, + ufp_required_field="can_detect_license_plate", + ufp_value="is_license_plate_detection_on", + ufp_set_method="set_license_plate_detection", + ufp_perm=PermRequired.WRITE, + ), + ProtectSwitchEntityDescription( + key="smart_smoke", + name="Detections: Smoke/CO", + icon="mdi:fire", + entity_category=EntityCategory.CONFIG, + ufp_required_field="can_detect_smoke", + ufp_value="is_smoke_detection_on", + ufp_set_method="set_smoke_detection", + ufp_perm=PermRequired.WRITE, + ), ) PRIVACY_MODE_SWITCH = ProtectSwitchEntityDescription[Camera]( diff --git a/tests/components/unifiprotect/test_binary_sensor.py b/tests/components/unifiprotect/test_binary_sensor.py index cb8d7bb659c..b2935552855 100644 --- a/tests/components/unifiprotect/test_binary_sensor.py +++ b/tests/components/unifiprotect/test_binary_sensor.py @@ -50,11 +50,11 @@ async def test_binary_sensor_camera_remove( ufp.api.bootstrap.nvr.system_info.ustorage = None await init_entry(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.BINARY_SENSOR, 6, 6) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 7, 7) await remove_entities(hass, ufp, [doorbell, unadopted_camera]) assert_entity_counts(hass, Platform.BINARY_SENSOR, 0, 0) await adopt_devices(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.BINARY_SENSOR, 6, 6) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 7, 7) async def test_binary_sensor_light_remove( @@ -120,7 +120,7 @@ async def test_binary_sensor_setup_camera_all( ufp.api.bootstrap.nvr.system_info.ustorage = None await init_entry(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.BINARY_SENSOR, 6, 6) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 7, 7) entity_registry = er.async_get(hass) @@ -262,7 +262,7 @@ async def test_binary_sensor_update_motion( """Test binary_sensor motion entity.""" await init_entry(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.BINARY_SENSOR, 12, 12) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 13, 13) _, entity_id = ids_from_device_description( Platform.BINARY_SENSOR, doorbell, MOTION_SENSORS[0] diff --git a/tests/components/unifiprotect/test_sensor.py b/tests/components/unifiprotect/test_sensor.py index f095260c38e..0ec50398e7a 100644 --- a/tests/components/unifiprotect/test_sensor.py +++ b/tests/components/unifiprotect/test_sensor.py @@ -13,7 +13,7 @@ from pyunifiprotect.data import ( Sensor, SmartDetectObjectType, ) -from pyunifiprotect.data.nvr import EventMetadata +from pyunifiprotect.data.nvr import EventMetadata, LicensePlateMetadata from homeassistant.components.unifiprotect.const import ( ATTR_EVENT_SCORE, @@ -62,11 +62,11 @@ async def test_sensor_camera_remove( ufp.api.bootstrap.nvr.system_info.ustorage = None await init_entry(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.SENSOR, 25, 12) + assert_entity_counts(hass, Platform.SENSOR, 26, 13) await remove_entities(hass, ufp, [doorbell, unadopted_camera]) assert_entity_counts(hass, Platform.SENSOR, 12, 9) await adopt_devices(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.SENSOR, 25, 12) + assert_entity_counts(hass, Platform.SENSOR, 26, 13) async def test_sensor_sensor_remove( @@ -318,7 +318,7 @@ async def test_sensor_setup_camera( """Test sensor entity setup for camera devices.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SENSOR, 25, 12) + assert_entity_counts(hass, Platform.SENSOR, 26, 13) entity_registry = er.async_get(hass) @@ -424,7 +424,7 @@ async def test_sensor_setup_camera_with_last_trip_time( """Test sensor entity setup for camera devices with last trip time.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SENSOR, 25, 25) + assert_entity_counts(hass, Platform.SENSOR, 26, 26) entity_registry = er.async_get(hass) @@ -452,7 +452,7 @@ async def test_sensor_update_motion( """Test sensor motion entity.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SENSOR, 25, 12) + assert_entity_counts(hass, Platform.SENSOR, 26, 13) _, entity_id = ids_from_device_description( Platform.SENSOR, doorbell, MOTION_SENSORS[0] @@ -565,3 +565,54 @@ async def test_sensor_update_alarm_with_last_trip_time( == (fixed_now - timedelta(hours=1)).replace(microsecond=0).isoformat() ) assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION + + +async def test_camera_update_licenseplate( + hass: HomeAssistant, ufp: MockUFPFixture, camera: Camera, fixed_now: datetime +): + """Test sensor motion entity.""" + + camera.feature_flags.smart_detect_types.append(SmartDetectObjectType.LICENSE_PLATE) + camera.feature_flags.has_smart_detect = True + camera.smart_detect_settings.object_types.append( + SmartDetectObjectType.LICENSE_PLATE + ) + + await init_entry(hass, ufp, [camera]) + assert_entity_counts(hass, Platform.SENSOR, 24, 13) + + _, entity_id = ids_from_device_description( + Platform.SENSOR, camera, MOTION_SENSORS[1] + ) + + event_metadata = EventMetadata( + license_plate=LicensePlateMetadata(name="ABCD1234", confidence_level=95) + ) + event = Event( + id="test_event_id", + type=EventType.SMART_DETECT, + start=fixed_now - timedelta(seconds=1), + end=None, + score=100, + smart_detect_types=[SmartDetectObjectType.LICENSE_PLATE], + smart_detect_event_ids=[], + metadata=event_metadata, + api=ufp.api, + ) + + new_camera = camera.copy() + new_camera.is_smart_detected = True + new_camera.last_smart_detect_event_id = event.id + + mock_msg = Mock() + mock_msg.changed_data = {} + mock_msg.new_obj = new_camera + + ufp.api.bootstrap.cameras = {new_camera.id: new_camera} + ufp.api.bootstrap.events = {event.id: event} + ufp.ws_msg(mock_msg) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state + assert state.state == "ABCD1234" diff --git a/tests/components/unifiprotect/test_switch.py b/tests/components/unifiprotect/test_switch.py index 82bf90eefd4..2ede00e60f2 100644 --- a/tests/components/unifiprotect/test_switch.py +++ b/tests/components/unifiprotect/test_switch.py @@ -35,6 +35,8 @@ CAMERA_SWITCHES_BASIC = [ for d in CAMERA_SWITCHES if d.name != "Detections: Face" and d.name != "Detections: Package" + and d.name != "Detections: License Plate" + and d.name != "Detections: Smoke/CO" and d.name != "SSH Enabled" ] CAMERA_SWITCHES_NO_EXTRA = [ From 987c7a28a98b4487fd392a602b82e44dcb13bc65 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 29 Nov 2022 11:09:12 +0100 Subject: [PATCH 0844/1033] Add tests for suggested_values in SchemaFlowFormStep (#82906) * Add tests * Add test for merging user input --- .../helpers/test_schema_config_entry_flow.py | 116 +++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/tests/helpers/test_schema_config_entry_flow.py b/tests/helpers/test_schema_config_entry_flow.py index a9af60b8ec3..27f4aa0d220 100644 --- a/tests/helpers/test_schema_config_entry_flow.py +++ b/tests/helpers/test_schema_config_entry_flow.py @@ -1,6 +1,7 @@ """Tests for the schema based data entry flows.""" from __future__ import annotations +from typing import Any from unittest.mock import patch import pytest @@ -11,7 +12,9 @@ from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType from homeassistant.helpers import entity_registry as er from homeassistant.helpers.schema_config_entry_flow import ( + SchemaCommonFlowHandler, SchemaConfigFlowHandler, + SchemaFlowError, SchemaFlowFormStep, SchemaFlowMenuStep, wrapped_entity_config_entry_title, @@ -23,8 +26,8 @@ from tests.common import MockConfigEntry, mock_platform TEST_DOMAIN = "test" -@pytest.fixture -def manager(): +@pytest.fixture(name="manager") +def manager_fixture(): """Return a flow manager.""" handlers = Registry() entries = [] @@ -443,3 +446,112 @@ async def test_next_step_function(hass: HomeAssistant) -> None: result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.CREATE_ENTRY + + +async def test_suggested_values( + hass: HomeAssistant, manager: data_entry_flow.FlowManager +) -> None: + """Test suggested_values handling in SchemaFlowFormStep.""" + manager.hass = hass + + OPTIONS_SCHEMA = vol.Schema( + {vol.Optional("option1", default="a very reasonable default"): str} + ) + + def _validate_user_input( + handler: SchemaCommonFlowHandler, user_input: dict[str, Any] + ) -> dict[str, Any]: + if user_input["option1"] == "not a valid value": + raise SchemaFlowError("option1 not using a valid value") + return user_input + + OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA, next_step="step_1"), + "step_1": SchemaFlowFormStep(OPTIONS_SCHEMA, next_step="step_2"), + "step_2": SchemaFlowFormStep( + OPTIONS_SCHEMA, + suggested_values=lambda _: {"option1": "a random override"}, + next_step="step_3", + ), + "step_3": SchemaFlowFormStep( + OPTIONS_SCHEMA, suggested_values=None, next_step="step_4" + ), + "step_4": SchemaFlowFormStep( + OPTIONS_SCHEMA, validate_user_input=_validate_user_input + ), + } + + class TestFlow(SchemaConfigFlowHandler, domain="test"): + config_flow = {} + options_flow = OPTIONS_FLOW + + config_entry = MockConfigEntry( + data={}, + domain="test", + options={"option1": "initial value"}, + ) + config_entry.add_to_hass(hass) + + # Start flow in basic mode, suggested values should be the existing options + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "init" + schema_keys: list[vol.Optional] = list(result["data_schema"].schema.keys()) + assert schema_keys == ["option1"] + assert schema_keys[0].description == {"suggested_value": "initial value"} + + # Go to step 1, suggested values should be the input from init + result = await hass.config_entries.options.async_configure( + result["flow_id"], {"option1": "blublu"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "step_1" + schema_keys: list[vol.Optional] = list(result["data_schema"].schema.keys()) + assert schema_keys == ["option1"] + assert schema_keys[0].description == {"suggested_value": "blublu"} + + # Go to step 2, suggested values should come from the callback function + result = await hass.config_entries.options.async_configure( + result["flow_id"], {"option1": "blabla"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "step_2" + schema_keys: list[vol.Optional] = list(result["data_schema"].schema.keys()) + assert schema_keys == ["option1"] + assert schema_keys[0].description == {"suggested_value": "a random override"} + + # Go to step 3, suggested values should be empty + result = await hass.config_entries.options.async_configure( + result["flow_id"], {"option1": "blabla"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "step_3" + schema_keys: list[vol.Optional] = list(result["data_schema"].schema.keys()) + assert schema_keys == ["option1"] + assert schema_keys[0].description is None + + # Go to step 4, suggested values should be the user input + result = await hass.config_entries.options.async_configure( + result["flow_id"], {"option1": "blabla"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "step_4" + schema_keys: list[vol.Optional] = list(result["data_schema"].schema.keys()) + assert schema_keys == ["option1"] + assert schema_keys[0].description == {"suggested_value": "blabla"} + + # Incorrect value in step 4, suggested values should be the user input + result = await hass.config_entries.options.async_configure( + result["flow_id"], {"option1": "not a valid value"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "step_4" + schema_keys: list[vol.Optional] = list(result["data_schema"].schema.keys()) + assert schema_keys == ["option1"] + assert schema_keys[0].description == {"suggested_value": "not a valid value"} + + # Correct value in step 4, end of flow + result = await hass.config_entries.options.async_configure( + result["flow_id"], {"option1": "blabla"} + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY From 9f8dea10f78e7c4ae232a15bc3160a8f65c5e205 Mon Sep 17 00:00:00 2001 From: hahn-th Date: Tue, 29 Nov 2022 11:13:53 +0100 Subject: [PATCH 0845/1033] Add support for HmIP-eTRV-E to homematicip_cloud (#82876) * Add device HmIP-eTRV-E * Fix test --- .../components/homematicip_cloud/climate.py | 15 ++- .../components/homematicip_cloud/sensor.py | 10 +- .../homematicip_cloud/test_device.py | 2 +- .../homematicip_cloud/test_sensor.py | 44 +++++++++ tests/fixtures/homematicip_cloud.json | 98 +++++++++++++++++++ 5 files changed, 164 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/homematicip_cloud/climate.py b/homeassistant/components/homematicip_cloud/climate.py index 2fc9f8fd12d..b6ac23b5c71 100644 --- a/homeassistant/components/homematicip_cloud/climate.py +++ b/homeassistant/components/homematicip_cloud/climate.py @@ -3,7 +3,11 @@ from __future__ import annotations from typing import Any -from homematicip.aio.device import AsyncHeatingThermostat, AsyncHeatingThermostatCompact +from homematicip.aio.device import ( + AsyncHeatingThermostat, + AsyncHeatingThermostatCompact, + AsyncHeatingThermostatEvo, +) from homematicip.aio.group import AsyncHeatingGroup from homematicip.base.enums import AbsenceType from homematicip.device import Switch @@ -312,11 +316,16 @@ class HomematicipHeatingGroup(HomematicipGenericEntity, ClimateEntity): @property def _first_radiator_thermostat( self, - ) -> AsyncHeatingThermostat | AsyncHeatingThermostatCompact | None: + ) -> AsyncHeatingThermostat | AsyncHeatingThermostatCompact | AsyncHeatingThermostatEvo | None: """Return the first radiator thermostat from the hmip heating group.""" for device in self._device.devices: if isinstance( - device, (AsyncHeatingThermostat, AsyncHeatingThermostatCompact) + device, + ( + AsyncHeatingThermostat, + AsyncHeatingThermostatCompact, + AsyncHeatingThermostatEvo, + ), ): return device diff --git a/homeassistant/components/homematicip_cloud/sensor.py b/homeassistant/components/homematicip_cloud/sensor.py index 03aaa7626b7..9da0bb37ed4 100644 --- a/homeassistant/components/homematicip_cloud/sensor.py +++ b/homeassistant/components/homematicip_cloud/sensor.py @@ -8,6 +8,7 @@ from homematicip.aio.device import ( AsyncFullFlushSwitchMeasuring, AsyncHeatingThermostat, AsyncHeatingThermostatCompact, + AsyncHeatingThermostatEvo, AsyncHomeControlAccessPoint, AsyncLightSensor, AsyncMotionDetectorIndoor, @@ -75,7 +76,14 @@ async def async_setup_entry( for device in hap.home.devices: if isinstance(device, AsyncHomeControlAccessPoint): entities.append(HomematicipAccesspointDutyCycle(hap, device)) - if isinstance(device, (AsyncHeatingThermostat, AsyncHeatingThermostatCompact)): + if isinstance( + device, + ( + AsyncHeatingThermostat, + AsyncHeatingThermostatCompact, + AsyncHeatingThermostatEvo, + ), + ): entities.append(HomematicipHeatingThermostat(hap, device)) entities.append(HomematicipTemperatureSensor(hap, device)) if isinstance( diff --git a/tests/components/homematicip_cloud/test_device.py b/tests/components/homematicip_cloud/test_device.py index 52b92fdff9a..327f53f129c 100644 --- a/tests/components/homematicip_cloud/test_device.py +++ b/tests/components/homematicip_cloud/test_device.py @@ -22,7 +22,7 @@ async def test_hmip_load_all_supported_devices(hass, default_mock_hap_factory): test_devices=None, test_groups=None ) - assert len(mock_hap.hmip_device_by_entity_id) == 264 + assert len(mock_hap.hmip_device_by_entity_id) == 267 async def test_hmip_remove_device(hass, default_mock_hap_factory): diff --git a/tests/components/homematicip_cloud/test_sensor.py b/tests/components/homematicip_cloud/test_sensor.py index 823508d5fee..33da0f217ae 100644 --- a/tests/components/homematicip_cloud/test_sensor.py +++ b/tests/components/homematicip_cloud/test_sensor.py @@ -193,6 +193,50 @@ async def test_hmip_temperature_sensor3(hass, default_mock_hap_factory): assert ha_state.attributes[ATTR_TEMPERATURE_OFFSET] == 10 +async def test_hmip_thermostat_evo_heating(hass, default_mock_hap_factory): + """Test HomematicipHeatingThermostat for HmIP-eTRV-E.""" + entity_id = "sensor.thermostat_evo_heating" + entity_name = "thermostat_evo Heating" + device_model = "HmIP-eTRV-E" + mock_hap = await default_mock_hap_factory.async_get_mock_hap( + test_devices=["thermostat_evo"] + ) + + ha_state, hmip_device = get_and_check_entity_basics( + hass, mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == "33" + await async_manipulate_test_data(hass, hmip_device, "valvePosition", 0.4) + ha_state = hass.states.get(entity_id) + assert ha_state.attributes[ATTR_UNIT_OF_MEASUREMENT] == PERCENTAGE + assert ha_state.state == "40" + + +async def test_hmip_thermostat_evo_temperature(hass, default_mock_hap_factory): + """Test HomematicipTemperatureSensor.""" + entity_id = "sensor.thermostat_evo_temperature" + entity_name = "thermostat_evo Temperature" + device_model = "HmIP-eTRV-E" + mock_hap = await default_mock_hap_factory.async_get_mock_hap( + test_devices=["thermostat_evo"] + ) + + ha_state, hmip_device = get_and_check_entity_basics( + hass, mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == "18.7" + assert ha_state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS + await async_manipulate_test_data(hass, hmip_device, "valveActualTemperature", 23.5) + ha_state = hass.states.get(entity_id) + assert ha_state.state == "23.5" + + await async_manipulate_test_data(hass, hmip_device, "temperatureOffset", 0.7) + ha_state = hass.states.get(entity_id) + assert ha_state.attributes[ATTR_TEMPERATURE_OFFSET] == 0.7 + + async def test_hmip_power_sensor(hass, default_mock_hap_factory): """Test HomematicipPowerSensor.""" entity_id = "sensor.flur_oben_power" diff --git a/tests/fixtures/homematicip_cloud.json b/tests/fixtures/homematicip_cloud.json index 1ca05d29af6..3e3536a2f42 100644 --- a/tests/fixtures/homematicip_cloud.json +++ b/tests/fixtures/homematicip_cloud.json @@ -2736,6 +2736,104 @@ "type": "HEATING_THERMOSTAT_COMPACT", "updateState": "UP_TO_DATE" }, + "3014F7110000000000000E70": { + "automaticValveAdaptionNeeded": false, + "availableFirmwareVersion": "1.0.10", + "connectionType": "HMIP_RF", + "firmwareVersion": "1.0.10", + "firmwareVersionInteger": 65546, + "functionalChannels": { + "0": { + "busConfigMismatch": null, + "coProFaulty": false, + "coProRestartNeeded": false, + "coProUpdateFailure": false, + "configPending": false, + "deviceId": "3014F7110000000000000E70", + "deviceOverheated": false, + "deviceOverloaded": false, + "devicePowerFailureDetected": false, + "deviceUndervoltage": false, + "displayContrast": 5, + "dutyCycle": false, + "functionalChannelType": "DEVICE_OPERATIONLOCK", + "groupIndex": 0, + "groups": ["00000000-0000-0000-0000-000000000012"], + "index": 0, + "label": "", + "lockJammed": null, + "lowBat": false, + "mountingOrientation": "RIGHT", + "multicastRoutingEnabled": false, + "operationLockActive": false, + "particulateMatterSensorCommunicationError": null, + "particulateMatterSensorError": null, + "powerShortCircuit": null, + "profilePeriodLimitReached": null, + "routerModuleEnabled": false, + "routerModuleSupported": false, + "rssiDeviceValue": -64, + "rssiPeerValue": -67, + "shortCircuitDataLine": null, + "supportedOptionalFeatures": { + "IFeatureBusConfigMismatch": false, + "IFeatureDeviceCoProError": false, + "IFeatureDeviceCoProRestart": false, + "IFeatureDeviceCoProUpdate": false, + "IFeatureDeviceIdentify": false, + "IFeatureDeviceOverheated": false, + "IFeatureDeviceOverloaded": false, + "IFeatureDeviceParticulateMatterSensorCommunicationError": false, + "IFeatureDeviceParticulateMatterSensorError": false, + "IFeatureDevicePowerFailure": false, + "IFeatureDeviceTemperatureHumiditySensorCommunicationError": false, + "IFeatureDeviceTemperatureHumiditySensorError": false, + "IFeatureDeviceTemperatureOutOfRange": false, + "IFeatureDeviceUndervoltage": false, + "IFeatureMulticastRouter": false, + "IFeaturePowerShortCircuit": false, + "IFeatureProfilePeriodLimit": false, + "IFeatureRssiValue": true, + "IFeatureShortCircuitDataLine": false, + "IOptionalFeatureDeviceErrorLockJammed": false, + "IOptionalFeatureDisplayContrast": true, + "IOptionalFeatureDutyCycle": true, + "IOptionalFeatureLowBat": true, + "IOptionalFeatureMountingOrientation": true + }, + "temperatureHumiditySensorCommunicationError": null, + "temperatureHumiditySensorError": null, + "temperatureOutOfRange": false, + "unreach": false + }, + "1": { + "deviceId": "3014F7110000000000000E70", + "functionalChannelType": "HEATING_THERMOSTAT_CHANNEL", + "groupIndex": 1, + "groups": ["00000000-0000-0000-0000-000000000013"], + "index": 1, + "label": "", + "setPointTemperature": 19.0, + "temperatureOffset": 0.5, + "valveActualTemperature": 18.7, + "valvePosition": 0.33, + "valveState": "ADAPTION_DONE" + } + }, + "homeId": "00000000-0000-0000-0000-000000000001", + "id": "3014F7110000000000000E70", + "label": "thermostat_evo", + "lastStatusUpdate": 1644406143910, + "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", + "manufacturerCode": 1, + "modelId": 425, + "modelType": "HmIP-eTRV-E", + "oem": "eQ-3", + "permanentlyReachable": true, + "serializedGlobalTradeItemNumber": "3014F7110000000000000E70", + "type": "HEATING_THERMOSTAT_EVO", + "updateState": "UP_TO_DATE" + }, "3014F71100000000000TEST1": { "availableFirmwareVersion": "0.0.0", "connectionType": "HMIP_RF", From 33cd59d3c2e1f945c16b39d929349e3eeb4cfb9a Mon Sep 17 00:00:00 2001 From: Olen Date: Tue, 29 Nov 2022 11:15:30 +0100 Subject: [PATCH 0846/1033] Add Twinkly effects (#82861) * Add Twinkly effects * Remove spurious comment --- homeassistant/components/twinkly/light.py | 63 +++++++++++++++++-- .../components/twinkly/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/twinkly/__init__.py | 22 +++++++ tests/components/twinkly/test_light.py | 29 ++++++++- 6 files changed, 112 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/twinkly/light.py b/homeassistant/components/twinkly/light.py index ba6ac7cf492..05f24411423 100644 --- a/homeassistant/components/twinkly/light.py +++ b/homeassistant/components/twinkly/light.py @@ -2,6 +2,7 @@ from __future__ import annotations import asyncio +from collections.abc import Mapping import logging from typing import Any @@ -10,10 +11,12 @@ from ttls.client import Twinkly from homeassistant.components.light import ( ATTR_BRIGHTNESS, + ATTR_EFFECT, ATTR_RGB_COLOR, ATTR_RGBW_COLOR, ColorMode, LightEntity, + LightEntityFeature, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_MODEL @@ -91,6 +94,8 @@ class TwinklyLight(LightEntity): self._is_on = False self._is_available = False self._attributes: dict[Any, Any] = {} + self._current_movie: dict[Any, Any] = {} + self._movies: list[Any] = [] @property def available(self) -> bool: @@ -127,19 +132,41 @@ class TwinklyLight(LightEntity): name=self.name, ) + @property + def supported_features(self) -> LightEntityFeature: + """Return supported features.""" + return LightEntityFeature.EFFECT + @property def is_on(self) -> bool: """Return true if light is on.""" return self._is_on @property - def extra_state_attributes(self) -> dict: + def extra_state_attributes(self) -> Mapping[str, Any]: """Return device specific state attributes.""" attributes = self._attributes return attributes + @property + def effect(self) -> str | None: + """Return the current effect.""" + if "name" in self._current_movie: + _LOGGER.debug("Current effect '%s'", self._current_movie["name"]) + return f"{self._current_movie['id']} {self._current_movie['name']}" + return None + + @property + def effect_list(self) -> list[str]: + """Return the list of saved effects.""" + effect_list = [] + for movie in self._movies: + effect_list.append(f"{movie['id']} {movie['name']}") + _LOGGER.debug("Effect list '%s'", effect_list) + return effect_list + async def async_turn_on(self, **kwargs: Any) -> None: """Turn device on.""" if ATTR_BRIGHTNESS in kwargs: @@ -160,15 +187,16 @@ class TwinklyLight(LightEntity): if isinstance(self._attr_rgbw_color, tuple): await self._client.interview() - # Reagarrange from rgbw to wrgb + # Static color only supports rgb await self._client.set_static_colour( ( - self._attr_rgbw_color[3], self._attr_rgbw_color[0], self._attr_rgbw_color[1], self._attr_rgbw_color[2], ) ) + await self._client.set_mode("color") + self._client.default_mode = "color" if ATTR_RGB_COLOR in kwargs: if kwargs[ATTR_RGB_COLOR] != self._attr_rgb_color: @@ -177,9 +205,20 @@ class TwinklyLight(LightEntity): if isinstance(self._attr_rgb_color, tuple): await self._client.interview() - # Reagarrange from rgbw to wrgb await self._client.set_static_colour(self._attr_rgb_color) + await self._client.set_mode("color") + self._client.default_mode = "color" + if ATTR_EFFECT in kwargs: + _LOGGER.debug("Setting effect '%s'", kwargs[ATTR_EFFECT]) + movie_id = kwargs[ATTR_EFFECT].split(" ")[0] + if "id" not in self._current_movie or int(movie_id) != int( + self._current_movie["id"] + ): + await self._client.interview() + await self._client.set_current_movie(int(movie_id)) + await self._client.set_mode("movie") + self._client.default_mode = "movie" if not self._is_on: await self._client.turn_on() @@ -232,6 +271,9 @@ class TwinklyLight(LightEntity): if key not in HIDDEN_DEV_VALUES: self._attributes[key] = value + await self.async_update_movies() + await self.async_update_current_movie() + if not self._is_available: _LOGGER.info("Twinkly '%s' is now available", self._client.host) @@ -245,3 +287,16 @@ class TwinklyLight(LightEntity): "Twinkly '%s' is not reachable (client error)", self._client.host ) self._is_available = False + + async def async_update_movies(self) -> None: + """Update the list of movies (effects).""" + movies = await self._client.get_saved_movies() + _LOGGER.debug("Movies: %s", movies) + if "movies" in movies: + self._movies = movies["movies"] + + async def async_update_current_movie(self) -> None: + """Update the current active movie.""" + current_movie = await self._client.get_current_movie() + if "id" in current_movie: + self._current_movie = current_movie diff --git a/homeassistant/components/twinkly/manifest.json b/homeassistant/components/twinkly/manifest.json index e3b97e9385b..b41d9bc1d0a 100644 --- a/homeassistant/components/twinkly/manifest.json +++ b/homeassistant/components/twinkly/manifest.json @@ -2,7 +2,7 @@ "domain": "twinkly", "name": "Twinkly", "documentation": "https://www.home-assistant.io/integrations/twinkly", - "requirements": ["ttls==1.4.3"], + "requirements": ["ttls==1.5.1"], "codeowners": ["@dr1rrb", "@Robbie1221"], "config_flow": true, "dhcp": [{ "hostname": "twinkly_*" }], diff --git a/requirements_all.txt b/requirements_all.txt index ad71455ddab..0a5ea93e512 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2457,7 +2457,7 @@ tp-connected==0.0.4 transmissionrpc==0.11 # homeassistant.components.twinkly -ttls==1.4.3 +ttls==1.5.1 # homeassistant.components.tuya tuya-iot-py-sdk==0.6.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 40e42afd3fe..97b6dd736d1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1694,7 +1694,7 @@ total_connect_client==2022.10 transmissionrpc==0.11 # homeassistant.components.twinkly -ttls==1.4.3 +ttls==1.5.1 # homeassistant.components.tuya tuya-iot-py-sdk==0.6.6 diff --git a/tests/components/twinkly/__init__.py b/tests/components/twinkly/__init__.py index d5440ddb74a..351f5509aa2 100644 --- a/tests/components/twinkly/__init__.py +++ b/tests/components/twinkly/__init__.py @@ -22,6 +22,9 @@ class ClientMock: self.state = True self.brightness = {"mode": "enabled", "value": 10} self.color = None + self.movies = [{"id": 1, "name": "Rainbow"}, {"id": 2, "name": "Flare"}] + self.current_movie = {} + self.default_mode = "movie" self.id = str(uuid4()) self.device_info = { @@ -81,3 +84,22 @@ class ClientMock: async def interview(self) -> None: """Interview.""" + + async def get_saved_movies(self) -> dict: + """Get saved movies.""" + return self.movies + + async def get_current_movie(self) -> dict: + """Get current movie.""" + return self.current_movie + + async def set_current_movie(self, movie_id: int) -> dict: + """Set current movie.""" + self.current_movie = {"id": movie_id} + + async def set_mode(self, mode: str) -> None: + """Set mode.""" + if mode == "off": + self.turn_off + else: + self.turn_on diff --git a/tests/components/twinkly/test_light.py b/tests/components/twinkly/test_light.py index 40fea31a6ba..f6fe9e297f6 100644 --- a/tests/components/twinkly/test_light.py +++ b/tests/components/twinkly/test_light.py @@ -118,7 +118,8 @@ async def test_turn_on_with_color_rgbw(hass: HomeAssistant): state = hass.states.get(entity.entity_id) assert state.state == "on" - assert client.color == (0, 128, 64, 32) + assert client.color == (128, 64, 32) + assert client.default_mode == "color" async def test_turn_on_with_color_rgb(hass: HomeAssistant): @@ -142,6 +143,32 @@ async def test_turn_on_with_color_rgb(hass: HomeAssistant): assert state.state == "on" assert client.color == (128, 64, 32) + assert client.default_mode == "color" + + +async def test_turn_on_with_effect(hass: HomeAssistant): + """Test support of the light.turn_on service with a brightness parameter.""" + client = ClientMock() + client.state = False + client.device_info["led_profile"] = "RGB" + client.brightness = {"mode": "enabled", "value": 255} + entity, _, _, _ = await _create_entries(hass, client) + + assert hass.states.get(entity.entity_id).state == "off" + assert client.current_movie == {} + + await hass.services.async_call( + "light", + "turn_on", + service_data={"entity_id": entity.entity_id, "effect": "1 Rainbow"}, + ) + await hass.async_block_till_done() + + state = hass.states.get(entity.entity_id) + + assert state.state == "on" + assert client.current_movie["id"] == 1 + assert client.default_mode == "movie" async def test_turn_off(hass: HomeAssistant): From 060102832e180173e1c779d6e97739935931d84f Mon Sep 17 00:00:00 2001 From: mlemainque Date: Tue, 29 Nov 2022 11:47:03 +0100 Subject: [PATCH 0847/1033] Update daikin sensors (#82441) fixes undefined --- homeassistant/components/daikin/climate.py | 7 ++--- homeassistant/components/daikin/const.py | 13 ++++++--- homeassistant/components/daikin/sensor.py | 34 +++++++++++++++------- homeassistant/components/daikin/switch.py | 24 +++++---------- 4 files changed, 43 insertions(+), 35 deletions(-) diff --git a/homeassistant/components/daikin/climate.py b/homeassistant/components/daikin/climate.py index 02107205e70..bd4763d3254 100644 --- a/homeassistant/components/daikin/climate.py +++ b/homeassistant/components/daikin/climate.py @@ -116,6 +116,8 @@ def format_target_temperature(target_temperature): class DaikinClimate(ClimateEntity): """Representation of a Daikin HVAC.""" + _attr_name = None + _attr_has_entity_name = True _attr_temperature_unit = TEMP_CELSIUS def __init__(self, api: DaikinApi) -> None: @@ -173,11 +175,6 @@ class DaikinClimate(ClimateEntity): if values: await self._api.device.set(values) - @property - def name(self): - """Return the name of the thermostat, if any.""" - return self._api.name - @property def unique_id(self): """Return a unique ID.""" diff --git a/homeassistant/components/daikin/const.py b/homeassistant/components/daikin/const.py index fbd838b9e47..a978e96178d 100644 --- a/homeassistant/components/daikin/const.py +++ b/homeassistant/components/daikin/const.py @@ -4,12 +4,17 @@ DOMAIN = "daikin" ATTR_TARGET_TEMPERATURE = "target_temperature" ATTR_INSIDE_TEMPERATURE = "inside_temperature" ATTR_OUTSIDE_TEMPERATURE = "outside_temperature" -ATTR_TOTAL_POWER = "total_power" + +ATTR_TARGET_HUMIDITY = "target_humidity" +ATTR_HUMIDITY = "humidity" + +ATTR_COMPRESSOR_FREQUENCY = "compressor_frequency" + +ATTR_ENERGY_TODAY = "energy_today" ATTR_COOL_ENERGY = "cool_energy" ATTR_HEAT_ENERGY = "heat_energy" -ATTR_HUMIDITY = "humidity" -ATTR_TARGET_HUMIDITY = "target_humidity" -ATTR_COMPRESSOR_FREQUENCY = "compressor_frequency" + +ATTR_TOTAL_POWER = "total_power" ATTR_TOTAL_ENERGY_TODAY = "total_energy_today" ATTR_STATE_ON = "on" diff --git a/homeassistant/components/daikin/sensor.py b/homeassistant/components/daikin/sensor.py index 1adacd322cc..3d9deba59ab 100644 --- a/homeassistant/components/daikin/sensor.py +++ b/homeassistant/components/daikin/sensor.py @@ -29,6 +29,7 @@ from . import DOMAIN as DAIKIN_DOMAIN, DaikinApi from .const import ( ATTR_COMPRESSOR_FREQUENCY, ATTR_COOL_ENERGY, + ATTR_ENERGY_TODAY, ATTR_HEAT_ENERGY, ATTR_HUMIDITY, ATTR_INSIDE_TEMPERATURE, @@ -54,7 +55,7 @@ class DaikinSensorEntityDescription(SensorEntityDescription, DaikinRequiredKeysM SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( DaikinSensorEntityDescription( key=ATTR_INSIDE_TEMPERATURE, - name="Inside Temperature", + name="Inside temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=TEMP_CELSIUS, @@ -62,7 +63,7 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( ), DaikinSensorEntityDescription( key=ATTR_OUTSIDE_TEMPERATURE, - name="Outside Temperature", + name="Outside temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=TEMP_CELSIUS, @@ -78,7 +79,7 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( ), DaikinSensorEntityDescription( key=ATTR_TARGET_HUMIDITY, - name="Target Humidity", + name="Target humidity", device_class=SensorDeviceClass.HUMIDITY, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=PERCENTAGE, @@ -86,7 +87,7 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( ), DaikinSensorEntityDescription( key=ATTR_TOTAL_POWER, - name="Estimated Power Consumption", + name="Compressor estimated power consumption", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=POWER_KILO_WATT, @@ -94,35 +95,47 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( ), DaikinSensorEntityDescription( key=ATTR_COOL_ENERGY, - name="Cool Energy Consumption", + name="Cool energy consumption", icon="mdi:snowflake", device_class=SensorDeviceClass.ENERGY, native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + entity_registry_enabled_default=False, value_func=lambda device: round(device.last_hour_cool_energy_consumption, 2), ), DaikinSensorEntityDescription( key=ATTR_HEAT_ENERGY, - name="Heat Energy Consumption", + name="Heat energy consumption", icon="mdi:fire", device_class=SensorDeviceClass.ENERGY, native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + entity_registry_enabled_default=False, value_func=lambda device: round(device.last_hour_heat_energy_consumption, 2), ), + DaikinSensorEntityDescription( + key=ATTR_ENERGY_TODAY, + name="Energy consumption", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL_INCREASING, + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + value_func=lambda device: round(device.today_energy_consumption, 2), + ), DaikinSensorEntityDescription( key=ATTR_COMPRESSOR_FREQUENCY, - name="Compressor Frequency", + name="Compressor frequency", icon="mdi:fan", device_class=SensorDeviceClass.FREQUENCY, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=FREQUENCY_HERTZ, + entity_registry_enabled_default=False, value_func=lambda device: device.compressor_frequency, ), DaikinSensorEntityDescription( key=ATTR_TOTAL_ENERGY_TODAY, - name="Today's Total Energy Consumption", + name="Compressor energy consumption", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + entity_registry_enabled_default=False, value_func=lambda device: round(device.today_total_energy_consumption, 2), ), ) @@ -150,9 +163,10 @@ async def async_setup_entry( if daikin_api.device.support_outside_temperature: sensors.append(ATTR_OUTSIDE_TEMPERATURE) if daikin_api.device.support_energy_consumption: - sensors.append(ATTR_TOTAL_POWER) + sensors.append(ATTR_ENERGY_TODAY) sensors.append(ATTR_COOL_ENERGY) sensors.append(ATTR_HEAT_ENERGY) + sensors.append(ATTR_TOTAL_POWER) sensors.append(ATTR_TOTAL_ENERGY_TODAY) if daikin_api.device.support_humidity: sensors.append(ATTR_HUMIDITY) @@ -171,6 +185,7 @@ async def async_setup_entry( class DaikinSensor(SensorEntity): """Representation of a Sensor.""" + _attr_has_entity_name = True entity_description: DaikinSensorEntityDescription def __init__( @@ -179,7 +194,6 @@ class DaikinSensor(SensorEntity): """Initialize the sensor.""" self.entity_description = description self._api = api - self._attr_name = f"{api.name} {description.name}" @property def unique_id(self) -> str: diff --git a/homeassistant/components/daikin/switch.py b/homeassistant/components/daikin/switch.py index 23b4b526f9a..a7c8b6549e4 100644 --- a/homeassistant/components/daikin/switch.py +++ b/homeassistant/components/daikin/switch.py @@ -56,6 +56,9 @@ async def async_setup_entry( class DaikinZoneSwitch(SwitchEntity): """Representation of a zone.""" + _attr_icon = ZONE_ICON + _attr_has_entity_name = True + def __init__(self, daikin_api: DaikinApi, zone_id): """Initialize the zone.""" self._api = daikin_api @@ -66,15 +69,10 @@ class DaikinZoneSwitch(SwitchEntity): """Return a unique ID.""" return f"{self._api.device.mac}-zone{self._zone_id}" - @property - def icon(self): - """Icon to use in the frontend, if any.""" - return ZONE_ICON - @property def name(self) -> str: """Return the name of the sensor.""" - return f"{self._api.name} {self._api.device.zones[self._zone_id][0]}" + return self._api.device.zones[self._zone_id][0] @property def is_on(self) -> bool: @@ -102,6 +100,10 @@ class DaikinZoneSwitch(SwitchEntity): class DaikinStreamerSwitch(SwitchEntity): """Streamer state.""" + _attr_icon = STREAMER_ICON + _attr_name = "Streamer" + _attr_has_entity_name = True + def __init__(self, daikin_api: DaikinApi) -> None: """Initialize streamer switch.""" self._api = daikin_api @@ -111,16 +113,6 @@ class DaikinStreamerSwitch(SwitchEntity): """Return a unique ID.""" return f"{self._api.device.mac}-streamer" - @property - def icon(self): - """Icon to use in the frontend, if any.""" - return STREAMER_ICON - - @property - def name(self) -> str: - """Return the name of the sensor.""" - return f"{self._api.name} streamer" - @property def is_on(self) -> bool: """Return the state of the sensor.""" From a888ce928228973c4b3c8b0bbe0124dc110f95d9 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 29 Nov 2022 12:37:57 +0100 Subject: [PATCH 0848/1033] Increase timeout of MariaDB CI job (#82911) --- .github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 262229e6ade..e85df0ba4dd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -936,7 +936,7 @@ jobs: . venv/bin/activate pip install mysqlclient sqlalchemy_utils - name: Run pytest (partially) - timeout-minutes: 10 + timeout-minutes: 15 shell: bash run: | . venv/bin/activate @@ -944,7 +944,7 @@ jobs: python3 -X dev -m pytest \ -qq \ - --timeout=9 \ + --timeout=20 \ -n 1 \ --cov="homeassistant.components.recorder" \ --cov-report=xml \ From e5c48edddd85eeaedf0ae14fbefcdeae79c8e2b1 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 29 Nov 2022 12:48:07 +0100 Subject: [PATCH 0849/1033] Bump aioecowitt to 2022.11.0 (#82913) --- homeassistant/components/ecowitt/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ecowitt/manifest.json b/homeassistant/components/ecowitt/manifest.json index 9ba16231867..f6508b4fc57 100644 --- a/homeassistant/components/ecowitt/manifest.json +++ b/homeassistant/components/ecowitt/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/ecowitt", "dependencies": ["webhook"], - "requirements": ["aioecowitt==2022.09.3"], + "requirements": ["aioecowitt==2022.11.0"], "codeowners": ["@pvizeli"], "iot_class": "local_push" } diff --git a/requirements_all.txt b/requirements_all.txt index 0a5ea93e512..f4314347492 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -150,7 +150,7 @@ aioeafm==0.1.2 aioeagle==1.1.0 # homeassistant.components.ecowitt -aioecowitt==2022.09.3 +aioecowitt==2022.11.0 # homeassistant.components.emonitor aioemonitor==1.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 97b6dd736d1..8063d363383 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -137,7 +137,7 @@ aioeafm==0.1.2 aioeagle==1.1.0 # homeassistant.components.ecowitt -aioecowitt==2022.09.3 +aioecowitt==2022.11.0 # homeassistant.components.emonitor aioemonitor==1.0.5 From d885e45ebb801f597bb46957805abf0322fe3b09 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 29 Nov 2022 01:50:54 -1000 Subject: [PATCH 0850/1033] Bump bluetooth-auto-recovery to 0.5.3 (#82909) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index bb090d2cf13..0f7378e00c0 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -9,7 +9,7 @@ "bleak==0.19.2", "bleak-retry-connector==2.8.5", "bluetooth-adapters==0.11.0", - "bluetooth-auto-recovery==0.5.2", + "bluetooth-auto-recovery==0.5.3", "bluetooth-data-tools==0.3.0", "dbus-fast==1.75.0" ], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 0db98c905b2..532bd23428c 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -13,7 +13,7 @@ bcrypt==3.1.7 bleak-retry-connector==2.8.5 bleak==0.19.2 bluetooth-adapters==0.11.0 -bluetooth-auto-recovery==0.5.2 +bluetooth-auto-recovery==0.5.3 bluetooth-data-tools==0.3.0 certifi>=2021.5.30 ciso8601==2.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index f4314347492..ee8423fe8c8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -450,7 +450,7 @@ bluemaestro-ble==0.2.0 bluetooth-adapters==0.11.0 # homeassistant.components.bluetooth -bluetooth-auto-recovery==0.5.2 +bluetooth-auto-recovery==0.5.3 # homeassistant.components.bluetooth # homeassistant.components.led_ble diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8063d363383..dde2a9f9dd1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -364,7 +364,7 @@ bluemaestro-ble==0.2.0 bluetooth-adapters==0.11.0 # homeassistant.components.bluetooth -bluetooth-auto-recovery==0.5.2 +bluetooth-auto-recovery==0.5.3 # homeassistant.components.bluetooth # homeassistant.components.led_ble From 424febbbed5998029dc2602c9bf3d6a648dd5815 Mon Sep 17 00:00:00 2001 From: Olen Date: Tue, 29 Nov 2022 13:01:43 +0100 Subject: [PATCH 0851/1033] Remove twinkly debug-logging (#82915) Remove debug-logging Removing a few debug logging statements --- homeassistant/components/twinkly/light.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/homeassistant/components/twinkly/light.py b/homeassistant/components/twinkly/light.py index 05f24411423..cc2270f25e1 100644 --- a/homeassistant/components/twinkly/light.py +++ b/homeassistant/components/twinkly/light.py @@ -154,7 +154,6 @@ class TwinklyLight(LightEntity): def effect(self) -> str | None: """Return the current effect.""" if "name" in self._current_movie: - _LOGGER.debug("Current effect '%s'", self._current_movie["name"]) return f"{self._current_movie['id']} {self._current_movie['name']}" return None @@ -164,7 +163,6 @@ class TwinklyLight(LightEntity): effect_list = [] for movie in self._movies: effect_list.append(f"{movie['id']} {movie['name']}") - _LOGGER.debug("Effect list '%s'", effect_list) return effect_list async def async_turn_on(self, **kwargs: Any) -> None: @@ -210,7 +208,6 @@ class TwinklyLight(LightEntity): self._client.default_mode = "color" if ATTR_EFFECT in kwargs: - _LOGGER.debug("Setting effect '%s'", kwargs[ATTR_EFFECT]) movie_id = kwargs[ATTR_EFFECT].split(" ")[0] if "id" not in self._current_movie or int(movie_id) != int( self._current_movie["id"] @@ -291,7 +288,6 @@ class TwinklyLight(LightEntity): async def async_update_movies(self) -> None: """Update the list of movies (effects).""" movies = await self._client.get_saved_movies() - _LOGGER.debug("Movies: %s", movies) if "movies" in movies: self._movies = movies["movies"] From 7ebd279e145b41333865f58182678053af2d45d2 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 29 Nov 2022 13:30:26 +0100 Subject: [PATCH 0852/1033] Tweak pytest-durations CI parameters (#82918) --- .github/workflows/ci.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e85df0ba4dd..13ff8ea12e3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -842,7 +842,6 @@ jobs: python3 -X dev -m pytest \ -qq \ --timeout=9 \ - --durations=10 \ -n auto \ --cov="homeassistant.components.${{ matrix.group }}" \ --cov-report=xml \ @@ -950,8 +949,7 @@ jobs: --cov-report=xml \ --cov-report=term-missing \ -o console_output_style=count \ - --durations=0 \ - --durations-min=10 \ + --durations=10 \ -p no:sugar \ --dburl=mysql://root:password@127.0.0.1/homeassistant-test \ tests/components/recorder From ce1b2f45c79f2d3a02c49ee2c7df79ff919d6f9e Mon Sep 17 00:00:00 2001 From: Thijs W Date: Tue, 29 Nov 2022 13:31:49 +0100 Subject: [PATCH 0853/1033] Add BROWSE_MEDIA support to frontier_silicon (#74950) * Add BROWSE_MEDIA support to frontier_silicon * Address review comments * Don't use mediatype to differentiate between channels and presets --- .../frontier_silicon/browse_media.py | 155 ++++++++++++++++++ .../components/frontier_silicon/const.py | 4 +- .../frontier_silicon/media_player.py | 48 +++++- 3 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 homeassistant/components/frontier_silicon/browse_media.py diff --git a/homeassistant/components/frontier_silicon/browse_media.py b/homeassistant/components/frontier_silicon/browse_media.py new file mode 100644 index 00000000000..03b28a025d0 --- /dev/null +++ b/homeassistant/components/frontier_silicon/browse_media.py @@ -0,0 +1,155 @@ +"""Support for media browsing.""" +import logging + +from afsapi import AFSAPI, FSApiException, OutOfRangeException, Preset + +from homeassistant.components.media_player import ( + BrowseError, + BrowseMedia, + MediaClass, + MediaType, +) + +from .const import MEDIA_CONTENT_ID_CHANNELS, MEDIA_CONTENT_ID_PRESET + +TOP_LEVEL_DIRECTORIES = { + MEDIA_CONTENT_ID_CHANNELS: "Channels", + MEDIA_CONTENT_ID_PRESET: "Presets", +} + +FSAPI_ITEM_TYPE_TO_MEDIA_CLASS = { + 0: MediaClass.DIRECTORY, + 1: MediaClass.CHANNEL, + 2: MediaClass.CHANNEL, +} + +_LOGGER = logging.getLogger(__name__) + + +def _item_preset_payload(preset: Preset, player_mode: str) -> BrowseMedia: + """ + Create response payload for a single media item. + + Used by async_browse_media. + """ + return BrowseMedia( + title=preset.name, + media_class=MediaClass.CHANNEL, + media_content_type=MediaType.CHANNEL, + # We add 1 to the preset key to keep it in sync with the numbering shown + # on the interface of the device + media_content_id=f"{player_mode}/{MEDIA_CONTENT_ID_PRESET}/{int(preset.key)+1}", + can_play=True, + can_expand=False, + ) + + +def _item_payload( + key, item: dict[str, str], player_mode: str, parent_keys: list[str] +) -> BrowseMedia: + """ + Create response payload for a single media item. + + Used by async_browse_media. + """ + assert "label" in item or "name" in item + assert "type" in item + + title = item.get("label") or item.get("name") or "Unknown" + title = title.strip() + + media_content_id = "/".join( + [player_mode, MEDIA_CONTENT_ID_CHANNELS, *parent_keys, key] + ) + media_class = ( + FSAPI_ITEM_TYPE_TO_MEDIA_CLASS.get(int(item["type"])) or MediaClass.CHANNEL + ) + + return BrowseMedia( + title=title, + media_class=media_class, + media_content_type=MediaClass.CHANNEL, + media_content_id=media_content_id, + can_play=(media_class != MediaClass.DIRECTORY), + can_expand=(media_class == MediaClass.DIRECTORY), + ) + + +async def browse_top_level(current_mode, afsapi: AFSAPI): + """ + Create response payload to describe contents of a specific library. + + Used by async_browse_media. + """ + + children = [ + BrowseMedia( + title=name, + media_class=MediaClass.DIRECTORY, + media_content_type=MediaType.CHANNELS, + media_content_id=f"{current_mode or 'unknown'}/{top_level_media_content_id}", + can_play=False, + can_expand=True, + ) + for top_level_media_content_id, name in TOP_LEVEL_DIRECTORIES.items() + ] + + library_info = BrowseMedia( + media_class=MediaClass.DIRECTORY, + media_content_id="library", + media_content_type=MediaType.CHANNELS, + title="Media Library", + can_play=False, + can_expand=True, + children=children, + children_media_class=MediaClass.DIRECTORY, + ) + + return library_info + + +async def browse_node( + afsapi: AFSAPI, + media_content_type, + media_content_id, +): + """List the contents of a navigation directory (or preset list) on a Frontier Silicon device.""" + + player_mode, browse_type, *parent_keys = media_content_id.split("/") + + title = TOP_LEVEL_DIRECTORIES.get(browse_type, "Unknown") + + children = [] + try: + if browse_type == MEDIA_CONTENT_ID_PRESET: + # Return the presets + + children = [ + _item_preset_payload(preset, player_mode=player_mode) + for preset in await afsapi.get_presets() + ] + + else: + # Browse to correct folder + await afsapi.nav_select_folder_via_path(parent_keys) + + # Return items in this folder + children = [ + _item_payload(key, item, player_mode, parent_keys=parent_keys) + async for key, item in await afsapi.nav_list() + ] + except OutOfRangeException as err: + raise BrowseError("The requested item is out of range") from err + except FSApiException as err: + raise BrowseError(str(err)) from err + + return BrowseMedia( + title=title, + media_content_id=media_content_id, + media_content_type=MediaType.CHANNELS, + media_class=MediaClass.DIRECTORY, + can_play=False, + can_expand=True, + children=children, + children_media_class=MediaType.CHANNEL, + ) diff --git a/homeassistant/components/frontier_silicon/const.py b/homeassistant/components/frontier_silicon/const.py index 4638e63c2f2..9ee17c0320e 100644 --- a/homeassistant/components/frontier_silicon/const.py +++ b/homeassistant/components/frontier_silicon/const.py @@ -1,6 +1,8 @@ """Constants for the Frontier Silicon Media Player integration.""" - DOMAIN = "frontier_silicon" DEFAULT_PIN = "1234" DEFAULT_PORT = 80 + +MEDIA_CONTENT_ID_PRESET = "preset" +MEDIA_CONTENT_ID_CHANNELS = "channels" diff --git a/homeassistant/components/frontier_silicon/media_player.py b/homeassistant/components/frontier_silicon/media_player.py index 309074c1b26..0e3eb168484 100644 --- a/homeassistant/components/frontier_silicon/media_player.py +++ b/homeassistant/components/frontier_silicon/media_player.py @@ -2,6 +2,7 @@ from __future__ import annotations import logging +from typing import Any from afsapi import ( AFSAPI, @@ -13,6 +14,8 @@ import voluptuous as vol from homeassistant.components.media_player import ( PLATFORM_SCHEMA, + BrowseError, + BrowseMedia, MediaPlayerEntity, MediaPlayerEntityFeature, MediaPlayerState, @@ -25,7 +28,8 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from .const import DEFAULT_PIN, DEFAULT_PORT, DOMAIN +from .browse_media import browse_node, browse_top_level +from .const import DEFAULT_PIN, DEFAULT_PORT, DOMAIN, MEDIA_CONTENT_ID_PRESET _LOGGER = logging.getLogger(__name__) @@ -80,7 +84,7 @@ async def async_setup_platform( class AFSAPIDevice(MediaPlayerEntity): """Representation of a Frontier Silicon device on the network.""" - _attr_media_content_type: str = MediaType.MUSIC + _attr_media_content_type: str = MediaType.CHANNEL _attr_supported_features = ( MediaPlayerEntityFeature.PAUSE @@ -97,6 +101,7 @@ class AFSAPIDevice(MediaPlayerEntity): | MediaPlayerEntityFeature.TURN_OFF | MediaPlayerEntityFeature.SELECT_SOURCE | MediaPlayerEntityFeature.SELECT_SOUND_MODE + | MediaPlayerEntityFeature.BROWSE_MEDIA ) def __init__(self, name: str | None, afsapi: AFSAPI) -> None: @@ -298,3 +303,42 @@ class AFSAPIDevice(MediaPlayerEntity): and (mode := self.__sound_modes_by_label.get(sound_mode)) is not None ): await self.fs_device.set_eq_preset(mode) + + async def async_browse_media( + self, media_content_type: str | None = None, media_content_id: str | None = None + ) -> BrowseMedia: + """Browse media library and preset stations.""" + if not media_content_id: + return await browse_top_level(self._attr_source, self.fs_device) + + return await browse_node(self.fs_device, media_content_type, media_content_id) + + async def async_play_media( + self, media_type: MediaType | str, media_id: str, **kwargs: Any + ) -> None: + """Play selected media or channel.""" + if media_type != MediaType.CHANNEL: + _LOGGER.error( + "Got %s, but frontier_silicon only supports playing channels", + media_type, + ) + return + + player_mode, media_type, *keys = media_id.split("/") + + await self.async_select_source(player_mode) # this also powers on the device + + if media_type == MEDIA_CONTENT_ID_PRESET: + if len(keys) != 1: + raise BrowseError("Presets can only have 1 level") + + # Keys of presets are 0-based, while the list shown on the device starts from 1 + preset = int(keys[0]) - 1 + + result = await self.fs_device.select_preset(preset) + else: + result = await self.fs_device.nav_select_item_via_path(keys) + + await self.async_update() + self._attr_media_content_id = media_id + return result From 23831d746ea322e0d23abca705478963ed741146 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 29 Nov 2022 13:36:33 +0100 Subject: [PATCH 0854/1033] Add ability to remove sensors in scrape config flow (#82912) * Add ability to remove sensors in scrape config flow * Add test * Cleanup registry * Cleanup * Adjust comments * Fix bad cleanup --- .../components/scrape/config_flow.py | 47 +++++++++++++++- homeassistant/components/scrape/strings.json | 1 + .../components/scrape/translations/en.json | 1 + tests/components/scrape/test_config_flow.py | 54 ++++++++++++++++++- 4 files changed, 99 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/scrape/config_flow.py b/homeassistant/components/scrape/config_flow.py index 8b598ca90be..9f683795e08 100644 --- a/homeassistant/components/scrape/config_flow.py +++ b/homeassistant/components/scrape/config_flow.py @@ -12,6 +12,7 @@ from homeassistant.components.rest.data import DEFAULT_TIMEOUT from homeassistant.components.rest.schema import DEFAULT_METHOD, METHODS from homeassistant.components.sensor import ( CONF_STATE_CLASS, + DOMAIN as SENSOR_DOMAIN, SensorDeviceClass, SensorStateClass, ) @@ -35,6 +36,7 @@ from homeassistant.const import ( UnitOfTemperature, ) from homeassistant.core import async_get_hass +from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.schema_config_entry_flow import ( SchemaCommonFlowHandler, SchemaConfigFlowHandler, @@ -136,11 +138,47 @@ def validate_sensor_setup( # Standard behavior is to merge the result with the options. # In this case, we want to add a sub-item so we update the options directly. - sensors: list[dict[str, Any]] = handler.options.setdefault("sensor", []) + sensors: list[dict[str, Any]] = handler.options.setdefault(SENSOR_DOMAIN, []) sensors.append(user_input) return {} +def get_remove_sensor_schema(handler: SchemaCommonFlowHandler) -> vol.Schema: + """Return schema for sensor removal.""" + return vol.Schema( + { + vol.Required(CONF_INDEX): cv.multi_select( + { + str(index): config[CONF_NAME] + for index, config in enumerate(handler.options[SENSOR_DOMAIN]) + }, + ) + } + ) + + +def validate_remove_sensor( + handler: SchemaCommonFlowHandler, user_input: dict[str, Any] +) -> dict[str, Any]: + """Validate remove sensor.""" + removed_indexes: set[str] = set(user_input[CONF_INDEX]) + + # Standard behavior is to merge the result with the options. + # In this case, we want to remove sub-items so we update the options directly. + entity_registry = er.async_get(handler.parent_handler.hass) + sensors: list[dict[str, Any]] = [] + sensor: dict[str, Any] + for index, sensor in enumerate(handler.options[SENSOR_DOMAIN]): + if str(index) not in removed_indexes: + sensors.append(sensor) + elif entity_id := entity_registry.async_get_entity_id( + SENSOR_DOMAIN, DOMAIN, sensor[CONF_UNIQUE_ID] + ): + entity_registry.async_remove(entity_id) + handler.options[SENSOR_DOMAIN] = sensors + return {} + + DATA_SCHEMA_RESOURCE = vol.Schema(RESOURCE_SETUP) DATA_SCHEMA_SENSOR = vol.Schema(SENSOR_SETUP) @@ -156,7 +194,7 @@ CONFIG_FLOW = { ), } OPTIONS_FLOW = { - "init": SchemaFlowMenuStep(["resource", "add_sensor"]), + "init": SchemaFlowMenuStep(["resource", "add_sensor", "remove_sensor"]), "resource": SchemaFlowFormStep( DATA_SCHEMA_RESOURCE, validate_user_input=validate_rest_setup, @@ -166,6 +204,11 @@ OPTIONS_FLOW = { suggested_values=None, validate_user_input=validate_sensor_setup, ), + "remove_sensor": SchemaFlowFormStep( + get_remove_sensor_schema, + suggested_values=None, + validate_user_input=validate_remove_sensor, + ), } diff --git a/homeassistant/components/scrape/strings.json b/homeassistant/components/scrape/strings.json index d14b0916f6b..1ac50c695c6 100644 --- a/homeassistant/components/scrape/strings.json +++ b/homeassistant/components/scrape/strings.json @@ -54,6 +54,7 @@ "init": { "menu_options": { "add_sensor": "Add sensor", + "remove_sensor": "Remove sensor", "resource": "Configure resource" } }, diff --git a/homeassistant/components/scrape/translations/en.json b/homeassistant/components/scrape/translations/en.json index 868ed3abaa6..4fa261d77b3 100644 --- a/homeassistant/components/scrape/translations/en.json +++ b/homeassistant/components/scrape/translations/en.json @@ -60,6 +60,7 @@ "init": { "menu_options": { "add_sensor": "Add sensor", + "remove_sensor": "Remove sensor", "resource": "Configure resource" } }, diff --git a/tests/components/scrape/test_config_flow.py b/tests/components/scrape/test_config_flow.py index 9e7a895eadd..67a55f5a144 100644 --- a/tests/components/scrape/test_config_flow.py +++ b/tests/components/scrape/test_config_flow.py @@ -221,10 +221,10 @@ async def test_options_resource_flow( assert state.state == "Hidden Version: 2021.12.10" -async def test_options_add_sensor_flow( +async def test_options_add_remove_sensor_flow( hass: HomeAssistant, loaded_entry: MockConfigEntry ) -> None: - """Test options flow to add a sensor.""" + """Test options flow to add and remove a sensor.""" state = hass.states.get("sensor.current_version") assert state.state == "Current Version: 2021.12.10" @@ -290,3 +290,53 @@ async def test_options_add_sensor_flow( state = hass.states.get("sensor.template") assert state.state == "Trying to get" + + # Now remove the original sensor + + result = await hass.config_entries.options.async_init(loaded_entry.entry_id) + + assert result["type"] == FlowResultType.MENU + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + {"next_step_id": "remove_sensor"}, + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "remove_sensor" + + mocker = MockRestData("test_scrape_sensor2") + with patch("homeassistant.components.rest.RestData", return_value=mocker): + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_INDEX: ["0"], + }, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["data"] == { + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: "GET", + CONF_VERIFY_SSL: True, + CONF_TIMEOUT: 10, + "sensor": [ + { + CONF_NAME: "Template", + CONF_SELECT: "template", + CONF_INDEX: 0, + CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120003", + }, + ], + } + + await hass.async_block_till_done() + + # Check the original entity was removed, with only the new entity left + assert len(hass.states.async_all()) == 1 + + # Check the state of the new entity + state = hass.states.get("sensor.template") + assert state.state == "Trying to get" From e6333c15eb1729fd5192268a1ba0af330c6afe1b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 29 Nov 2022 13:36:50 +0100 Subject: [PATCH 0855/1033] Use SchemaOptionsFlowHandler in bluetooth (#82900) --- .../components/bluetooth/config_flow.py | 44 +++++++------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/bluetooth/config_flow.py b/homeassistant/components/bluetooth/config_flow.py index ffebce2e521..0a414b36144 100644 --- a/homeassistant/components/bluetooth/config_flow.py +++ b/homeassistant/components/bluetooth/config_flow.py @@ -13,8 +13,12 @@ from bluetooth_adapters import ( import voluptuous as vol from homeassistant.components import onboarding -from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow +from homeassistant.config_entries import ConfigEntry, ConfigFlow from homeassistant.core import callback +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaFlowFormStep, + SchemaOptionsFlowHandler, +) from homeassistant.helpers.typing import DiscoveryInfoType from . import models @@ -23,6 +27,15 @@ from .const import CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, DOMAIN if TYPE_CHECKING: from homeassistant.data_entry_flow import FlowResult +OPTIONS_SCHEMA = vol.Schema( + { + vol.Required(CONF_PASSIVE, default=False): bool, + } +) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), +} + class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN): """Config flow for Bluetooth.""" @@ -127,37 +140,12 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN): @callback def async_get_options_flow( config_entry: ConfigEntry, - ) -> OptionsFlowHandler: + ) -> SchemaOptionsFlowHandler: """Get the options flow for this handler.""" - return OptionsFlowHandler(config_entry) + return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW) @classmethod @callback def async_supports_options_flow(cls, config_entry: ConfigEntry) -> bool: """Return options flow support for this handler.""" return bool(models.MANAGER and models.MANAGER.supports_passive_scan) - - -class OptionsFlowHandler(OptionsFlow): - """Handle the option flow for bluetooth.""" - - def __init__(self, config_entry: ConfigEntry) -> None: - """Initialize options flow.""" - self.config_entry = config_entry - - async def async_step_init( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Handle options flow.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - data_schema = vol.Schema( - { - vol.Required( - CONF_PASSIVE, - default=self.config_entry.options.get(CONF_PASSIVE, False), - ): bool, - } - ) - return self.async_show_form(step_id="init", data_schema=data_schema) From f8e1cb5bcf3436fa57974f1bd2c093e197b6bbe4 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 29 Nov 2022 13:37:05 +0100 Subject: [PATCH 0856/1033] Use SchemaOptionsFlowHandler in balboa (#82899) * Use SchemaOptionsFlowHandler in balboa * Make CONF_SYNC_TIME required --- .../components/balboa/config_flow.py | 44 +++++++------------ 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/balboa/config_flow.py b/homeassistant/components/balboa/config_flow.py index c0301bc9892..e9f94795ea5 100644 --- a/homeassistant/components/balboa/config_flow.py +++ b/homeassistant/components/balboa/config_flow.py @@ -12,11 +12,24 @@ from homeassistant.const import CONF_HOST from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.device_registry import format_mac +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaFlowFormStep, + SchemaOptionsFlowHandler, +) from .const import _LOGGER, CONF_SYNC_TIME, DOMAIN DATA_SCHEMA = vol.Schema({vol.Required(CONF_HOST): str}) +OPTIONS_SCHEMA = vol.Schema( + { + vol.Required(CONF_SYNC_TIME, default=False): bool, + } +) +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), +} + async def validate_input(data: dict[str, Any]) -> dict[str, str]: """Validate the user input allows us to connect.""" @@ -47,9 +60,9 @@ class BalboaSpaClientFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): @callback def async_get_options_flow( config_entry: config_entries.ConfigEntry, - ) -> config_entries.OptionsFlow: + ) -> SchemaOptionsFlowHandler: """Get the options flow for this handler.""" - return BalboaSpaClientOptionsFlowHandler(config_entry) + return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW) async def async_step_user( self, user_input: dict[str, Any] | None = None @@ -77,30 +90,3 @@ class BalboaSpaClientFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): class CannotConnect(exceptions.HomeAssistantError): """Error to indicate we cannot connect.""" - - -class BalboaSpaClientOptionsFlowHandler(config_entries.OptionsFlow): - """Handle Balboa Spa Client options.""" - - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: - """Initialize Balboa Spa Client options flow.""" - self.config_entry = config_entry - - async def async_step_init( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Manage Balboa Spa Client options.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - return self.async_show_form( - step_id="init", - data_schema=vol.Schema( - { - vol.Optional( - CONF_SYNC_TIME, - default=self.config_entry.options.get(CONF_SYNC_TIME, False), - ): bool, - } - ), - ) From 79b400d71b1a9ddfeda4eb388c35a2de51d8f55d Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 29 Nov 2022 13:37:22 +0100 Subject: [PATCH 0857/1033] Improve PluggableActionsEntry typing (#82885) * Improve PluggableActionsEntry typing * Add parameter typing --- homeassistant/helpers/trigger.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/helpers/trigger.py b/homeassistant/helpers/trigger.py index 1054521ee51..1bf9874988d 100644 --- a/homeassistant/helpers/trigger.py +++ b/homeassistant/helpers/trigger.py @@ -78,7 +78,13 @@ class PluggableActionsEntry: """Holder to keep track of all plugs and actions for a given trigger.""" plugs: set[PluggableAction] = field(default_factory=set) - actions: dict[object, tuple[HassJob, dict[str, Any]]] = field(default_factory=dict) + actions: dict[ + object, + tuple[ + HassJob[[dict[str, Any], Context | None], Coroutine[Any, Any, None]], + dict[str, Any], + ], + ] = field(default_factory=dict) class PluggableAction: From d8a7336a3ff5cc0e600c1f2dd24da9f58ac7d22e Mon Sep 17 00:00:00 2001 From: north3221 Date: Tue, 29 Nov 2022 12:58:53 +0000 Subject: [PATCH 0858/1033] Remove as codeowner for tado (#82919) --- CODEOWNERS | 4 ++-- homeassistant/components/tado/manifest.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 78ab439cf7e..83651f4ddc0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1137,8 +1137,8 @@ build.json @home-assistant/supervisor /homeassistant/components/synology_srm/ @aerialls /homeassistant/components/system_bridge/ @timmo001 /tests/components/system_bridge/ @timmo001 -/homeassistant/components/tado/ @michaelarnauts @north3221 -/tests/components/tado/ @michaelarnauts @north3221 +/homeassistant/components/tado/ @michaelarnauts +/tests/components/tado/ @michaelarnauts /homeassistant/components/tag/ @balloob @dmulcahey /tests/components/tag/ @balloob @dmulcahey /homeassistant/components/tailscale/ @frenck diff --git a/homeassistant/components/tado/manifest.json b/homeassistant/components/tado/manifest.json index 078c821eeb4..529b4bcfb97 100644 --- a/homeassistant/components/tado/manifest.json +++ b/homeassistant/components/tado/manifest.json @@ -3,7 +3,7 @@ "name": "Tado", "documentation": "https://www.home-assistant.io/integrations/tado", "requirements": ["python-tado==0.12.0"], - "codeowners": ["@michaelarnauts", "@north3221"], + "codeowners": ["@michaelarnauts"], "config_flow": true, "homekit": { "models": ["tado", "AC02"] From 9805d8a6cc43052e8dbeb4e2298487eaaa003369 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Nov 2022 14:07:30 +0100 Subject: [PATCH 0859/1033] Fix device class typing in Universal Devices ISY994 (#82924) --- homeassistant/components/isy994/binary_sensor.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/isy994/binary_sensor.py b/homeassistant/components/isy994/binary_sensor.py index 23e77ba849d..81efdb5922a 100644 --- a/homeassistant/components/isy994/binary_sensor.py +++ b/homeassistant/components/isy994/binary_sensor.py @@ -70,7 +70,7 @@ async def async_setup_entry( | ISYBinarySensorHeartbeat | ISYBinarySensorProgramEntity, ] = {} - child_nodes: list[tuple[Node, str | None, str | None]] = [] + child_nodes: list[tuple[Node, BinarySensorDeviceClass | None, str | None]] = [] entity: ISYInsteonBinarySensorEntity | ISYBinarySensorEntity | ISYBinarySensorHeartbeat | ISYBinarySensorProgramEntity hass_isy_data = hass.data[ISY994_DOMAIN][entry.entry_id] @@ -192,7 +192,9 @@ async def async_setup_entry( async_add_entities(entities) -def _detect_device_type_and_class(node: Group | Node) -> tuple[str | None, str | None]: +def _detect_device_type_and_class( + node: Group | Node, +) -> tuple[BinarySensorDeviceClass | None, str | None]: try: device_type = node.type except AttributeError: @@ -220,7 +222,7 @@ class ISYBinarySensorEntity(ISYNodeEntity, BinarySensorEntity): def __init__( self, node: Node, - force_device_class: str | None = None, + force_device_class: BinarySensorDeviceClass | None = None, unknown_state: bool | None = None, ) -> None: """Initialize the ISY994 binary sensor device.""" @@ -235,7 +237,7 @@ class ISYBinarySensorEntity(ISYNodeEntity, BinarySensorEntity): return bool(self._node.status) @property - def device_class(self) -> str | None: + def device_class(self) -> BinarySensorDeviceClass | None: """Return the class of this device. This was discovered by parsing the device type code during init @@ -255,7 +257,7 @@ class ISYInsteonBinarySensorEntity(ISYBinarySensorEntity): def __init__( self, node: Node, - force_device_class: str | None = None, + force_device_class: BinarySensorDeviceClass | None = None, unknown_state: bool | None = None, ) -> None: """Initialize the ISY994 binary sensor device.""" @@ -484,7 +486,7 @@ class ISYBinarySensorHeartbeat(ISYNodeEntity, BinarySensorEntity): return bool(self._computed_state) @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Get the class of this device.""" return BinarySensorDeviceClass.BATTERY From 412c12b992049317c85cc140b87743797e515546 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Nov 2022 14:07:56 +0100 Subject: [PATCH 0860/1033] Improve device class handling in ESPHome (#82923) --- homeassistant/components/esphome/binary_sensor.py | 15 ++++++++++----- homeassistant/components/esphome/sensor.py | 10 +++++----- homeassistant/components/esphome/switch.py | 11 ++++++----- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/esphome/binary_sensor.py b/homeassistant/components/esphome/binary_sensor.py index ffe322b6259..96ad9d05238 100644 --- a/homeassistant/components/esphome/binary_sensor.py +++ b/homeassistant/components/esphome/binary_sensor.py @@ -1,9 +1,14 @@ """Support for ESPHome binary sensors.""" from __future__ import annotations +from contextlib import suppress + from aioesphomeapi import BinarySensorInfo, BinarySensorState -from homeassistant.components.binary_sensor import DEVICE_CLASSES, BinarySensorEntity +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -45,11 +50,11 @@ class EsphomeBinarySensor( return self._state.state @property - def device_class(self) -> str | None: + def device_class(self) -> BinarySensorDeviceClass | None: """Return the class of this device, from component DEVICE_CLASSES.""" - if self._static_info.device_class not in DEVICE_CLASSES: - return None - return self._static_info.device_class + with suppress(ValueError): + return BinarySensorDeviceClass(self._static_info.device_class) + return None @property def available(self) -> bool: diff --git a/homeassistant/components/esphome/sensor.py b/homeassistant/components/esphome/sensor.py index 4b316f6a640..29c661f0984 100644 --- a/homeassistant/components/esphome/sensor.py +++ b/homeassistant/components/esphome/sensor.py @@ -1,6 +1,7 @@ """Support for esphome sensors.""" from __future__ import annotations +from contextlib import suppress from datetime import datetime import math @@ -14,7 +15,6 @@ from aioesphomeapi import ( from aioesphomeapi.model import LastResetType from homeassistant.components.sensor import ( - DEVICE_CLASSES, SensorDeviceClass, SensorEntity, SensorStateClass, @@ -96,11 +96,11 @@ class EsphomeSensor(EsphomeEntity[SensorInfo, SensorState], SensorEntity): return self._static_info.unit_of_measurement @property - def device_class(self) -> str | None: + def device_class(self) -> SensorDeviceClass | None: """Return the class of this device, from component DEVICE_CLASSES.""" - if self._static_info.device_class not in DEVICE_CLASSES: - return None - return self._static_info.device_class + with suppress(ValueError): + return SensorDeviceClass(self._static_info.device_class) + return None @property def state_class(self) -> SensorStateClass | None: diff --git a/homeassistant/components/esphome/switch.py b/homeassistant/components/esphome/switch.py index db5084df378..3888053d3fb 100644 --- a/homeassistant/components/esphome/switch.py +++ b/homeassistant/components/esphome/switch.py @@ -1,11 +1,12 @@ """Support for ESPHome switches.""" from __future__ import annotations +from contextlib import suppress from typing import Any from aioesphomeapi import SwitchInfo, SwitchState -from homeassistant.components.switch import DEVICE_CLASSES, SwitchEntity +from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -43,11 +44,11 @@ class EsphomeSwitch(EsphomeEntity[SwitchInfo, SwitchState], SwitchEntity): return self._state.state @property - def device_class(self) -> str | None: + def device_class(self) -> SwitchDeviceClass | None: """Return the class of this device.""" - if self._static_info.device_class not in DEVICE_CLASSES: - return None - return self._static_info.device_class + with suppress(ValueError): + return SwitchDeviceClass(self._static_info.device_class) + return None async def async_turn_on(self, **kwargs: Any) -> None: """Turn the entity on.""" From f0b979556cd57ae18fcaf6ed6c75b5e2927e048b Mon Sep 17 00:00:00 2001 From: Matrix Date: Tue, 29 Nov 2022 21:10:38 +0800 Subject: [PATCH 0861/1033] Add YoLink MultiOutlet support (#82622) --- homeassistant/components/yolink/const.py | 1 + homeassistant/components/yolink/entity.py | 13 ++++ homeassistant/components/yolink/manifest.json | 2 +- homeassistant/components/yolink/switch.py | 72 ++++++++++++++++--- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 6 files changed, 78 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/yolink/const.py b/homeassistant/components/yolink/const.py index 14279ebeae0..c36f01bd4fd 100644 --- a/homeassistant/components/yolink/const.py +++ b/homeassistant/components/yolink/const.py @@ -16,6 +16,7 @@ ATTR_DEVICE_ID = "deviceId" ATTR_DEVICE_DOOR_SENSOR = "DoorSensor" ATTR_DEVICE_TH_SENSOR = "THSensor" ATTR_DEVICE_MOTION_SENSOR = "MotionSensor" +ATTR_DEVICE_MULTI_OUTLET = "MultiOutlet" ATTR_DEVICE_LEAK_SENSOR = "LeakSensor" ATTR_DEVICE_VIBRATION_SENSOR = "VibrationSensor" ATTR_DEVICE_OUTLET = "Outlet" diff --git a/homeassistant/components/yolink/entity.py b/homeassistant/components/yolink/entity.py index 02f063a282a..0ae960d726f 100644 --- a/homeassistant/components/yolink/entity.py +++ b/homeassistant/components/yolink/entity.py @@ -3,6 +3,7 @@ from __future__ import annotations from abc import abstractmethod +from yolink.client_request import ClientRequest from yolink.exception import YoLinkAuthFailError, YoLinkClientError from homeassistant.config_entries import ConfigEntry @@ -70,3 +71,15 @@ class YoLinkEntity(CoordinatorEntity[YoLinkCoordinator]): except YoLinkClientError as yl_client_err: self.coordinator.last_update_success = False raise HomeAssistantError(yl_client_err) from yl_client_err + + async def call_device(self, request: ClientRequest) -> None: + """Call device api.""" + try: + # call_device will check result, fail by raise YoLinkClientError + await self.coordinator.device.call_device(request) + except YoLinkAuthFailError as yl_auth_err: + self.config_entry.async_start_reauth(self.hass) + raise HomeAssistantError(yl_auth_err) from yl_auth_err + except YoLinkClientError as yl_client_err: + self.coordinator.last_update_success = False + raise HomeAssistantError(yl_client_err) from yl_client_err diff --git a/homeassistant/components/yolink/manifest.json b/homeassistant/components/yolink/manifest.json index 665b17d9f22..3a2a1f6a390 100644 --- a/homeassistant/components/yolink/manifest.json +++ b/homeassistant/components/yolink/manifest.json @@ -3,7 +3,7 @@ "name": "YoLink", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yolink", - "requirements": ["yolink-api==0.1.0"], + "requirements": ["yolink-api==0.1.5"], "dependencies": ["auth", "application_credentials"], "codeowners": ["@matrixd2"], "iot_class": "cloud_push" diff --git a/homeassistant/components/yolink/switch.py b/homeassistant/components/yolink/switch.py index 03bb2a26183..2ac322002d5 100644 --- a/homeassistant/components/yolink/switch.py +++ b/homeassistant/components/yolink/switch.py @@ -6,6 +6,7 @@ from dataclasses import dataclass from typing import Any from yolink.device import YoLinkDevice +from yolink.outlet_request_builder import OutletRequestBuilder from homeassistant.components.switch import ( SwitchDeviceClass, @@ -19,6 +20,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( ATTR_COORDINATORS, ATTR_DEVICE_MANIPULATOR, + ATTR_DEVICE_MULTI_OUTLET, ATTR_DEVICE_OUTLET, ATTR_DEVICE_SWITCH, DOMAIN, @@ -32,8 +34,7 @@ class YoLinkSwitchEntityDescription(SwitchEntityDescription): """YoLink SwitchEntityDescription.""" exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True - value: Callable[[Any], bool | None] = lambda _: None - state_key: str = "state" + plug_index: int | None = None DEVICE_TYPES: tuple[YoLinkSwitchEntityDescription, ...] = ( @@ -41,26 +42,63 @@ DEVICE_TYPES: tuple[YoLinkSwitchEntityDescription, ...] = ( key="outlet_state", device_class=SwitchDeviceClass.OUTLET, name="State", - value=lambda value: value == "open" if value is not None else None, exists_fn=lambda device: device.device_type == ATTR_DEVICE_OUTLET, ), YoLinkSwitchEntityDescription( key="manipulator_state", name="State", icon="mdi:pipe", - value=lambda value: value == "open" if value is not None else None, exists_fn=lambda device: device.device_type == ATTR_DEVICE_MANIPULATOR, ), YoLinkSwitchEntityDescription( key="switch_state", name="State", device_class=SwitchDeviceClass.SWITCH, - value=lambda value: value == "open" if value is not None else None, exists_fn=lambda device: device.device_type == ATTR_DEVICE_SWITCH, ), + YoLinkSwitchEntityDescription( + key="multi_outlet_usb_ports", + name="UsbPorts", + device_class=SwitchDeviceClass.OUTLET, + exists_fn=lambda device: device.device_type == ATTR_DEVICE_MULTI_OUTLET, + plug_index=0, + ), + YoLinkSwitchEntityDescription( + key="multi_outlet_plug_1", + name="Plug1", + device_class=SwitchDeviceClass.OUTLET, + exists_fn=lambda device: device.device_type == ATTR_DEVICE_MULTI_OUTLET, + plug_index=1, + ), + YoLinkSwitchEntityDescription( + key="multi_outlet_plug_2", + name="Plug2", + device_class=SwitchDeviceClass.OUTLET, + exists_fn=lambda device: device.device_type == ATTR_DEVICE_MULTI_OUTLET, + plug_index=2, + ), + YoLinkSwitchEntityDescription( + key="multi_outlet_plug_3", + name="Plug3", + device_class=SwitchDeviceClass.OUTLET, + exists_fn=lambda device: device.device_type == ATTR_DEVICE_MULTI_OUTLET, + plug_index=3, + ), + YoLinkSwitchEntityDescription( + key="multi_outlet_plug_4", + name="Plug4", + device_class=SwitchDeviceClass.OUTLET, + exists_fn=lambda device: device.device_type == ATTR_DEVICE_MULTI_OUTLET, + plug_index=4, + ), ) -DEVICE_TYPE = [ATTR_DEVICE_MANIPULATOR, ATTR_DEVICE_OUTLET, ATTR_DEVICE_SWITCH] +DEVICE_TYPE = [ + ATTR_DEVICE_MANIPULATOR, + ATTR_DEVICE_MULTI_OUTLET, + ATTR_DEVICE_OUTLET, + ATTR_DEVICE_SWITCH, +] async def async_setup_entry( @@ -108,18 +146,30 @@ class YoLinkSwitchEntity(YoLinkEntity, SwitchEntity): f"{coordinator.device.device_name} ({self.entity_description.name})" ) + def _get_state( + self, state_value: str | list[str] | None, plug_index: int | None + ) -> bool | None: + """Parse state value.""" + if isinstance(state_value, list) and plug_index is not None: + return state_value[plug_index] == "open" + return state_value == "open" if state_value is not None else None + @callback - def update_entity_state(self, state: dict[str, Any]) -> None: + def update_entity_state(self, state: dict[str, str | list[str]]) -> None: """Update HA Entity State.""" - self._attr_is_on = self.entity_description.value( - state.get(self.entity_description.state_key) + self._attr_is_on = self._get_state( + state.get("state"), self.entity_description.plug_index ) self.async_write_ha_state() async def call_state_change(self, state: str) -> None: """Call setState api to change switch state.""" - await self.call_device_api("setState", {"state": state}) - self._attr_is_on = self.entity_description.value(state) + await self.call_device( + OutletRequestBuilder.set_state_request( + state, self.entity_description.plug_index + ) + ) + self._attr_is_on = self._get_state(state, self.entity_description.plug_index) self.async_write_ha_state() async def async_turn_on(self, **kwargs: Any) -> None: diff --git a/requirements_all.txt b/requirements_all.txt index ee8423fe8c8..11a9922382c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2609,7 +2609,7 @@ yeelight==0.7.10 yeelightsunflower==0.0.10 # homeassistant.components.yolink -yolink-api==0.1.0 +yolink-api==0.1.5 # homeassistant.components.youless youless-api==0.16 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index dde2a9f9dd1..bb2164f68b3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1816,7 +1816,7 @@ yalexs==1.2.6 yeelight==0.7.10 # homeassistant.components.yolink -yolink-api==0.1.0 +yolink-api==0.1.5 # homeassistant.components.youless youless-api==0.16 From 982966a3d921687e72fb39f7e2c7e7d7ad1f422b Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Tue, 29 Nov 2022 14:30:22 +0100 Subject: [PATCH 0862/1033] Use device_registry for HomeWizard device_info (#82921) * Use global device and use didentifiers to get device_info * Remove unused config_entry --- homeassistant/components/homewizard/__init__.py | 13 ++++++++++++- homeassistant/components/homewizard/coordinator.py | 8 ++++++++ homeassistant/components/homewizard/number.py | 4 +--- homeassistant/components/homewizard/sensor.py | 14 ++------------ homeassistant/components/homewizard/switch.py | 8 +------- 5 files changed, 24 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/homewizard/__init__.py b/homeassistant/components/homewizard/__init__.py index ec43cdfdd2e..a236c392c07 100644 --- a/homeassistant/components/homewizard/__init__.py +++ b/homeassistant/components/homewizard/__init__.py @@ -5,7 +5,7 @@ from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import CONF_IP_ADDRESS from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import device_registry as dr, entity_registry as er from .const import DOMAIN, PLATFORMS from .coordinator import HWEnergyDeviceUpdateCoordinator as Coordinator @@ -71,6 +71,17 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await coordinator.api.close() raise + # Register device + device_registry = dr.async_get(hass) + device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + name=entry.title, + manufacturer="HomeWizard", + sw_version=coordinator.data["device"].firmware_version, + model=coordinator.data["device"].product_type, + identifiers={(DOMAIN, coordinator.data["device"].serial)}, + ) + # Finalize hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = coordinator diff --git a/homeassistant/components/homewizard/coordinator.py b/homeassistant/components/homewizard/coordinator.py index 4397d77a3ff..dc441836d9a 100644 --- a/homeassistant/components/homewizard/coordinator.py +++ b/homeassistant/components/homewizard/coordinator.py @@ -8,6 +8,7 @@ from homewizard_energy.errors import DisabledError, RequestError from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import DOMAIN, UPDATE_INTERVAL, DeviceResponseEntry @@ -30,6 +31,13 @@ class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry] super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=UPDATE_INTERVAL) self.api = HomeWizardEnergy(host, clientsession=async_get_clientsession(hass)) + @property + def device_info(self) -> DeviceInfo: + """Return device_info.""" + return DeviceInfo( + identifiers={(DOMAIN, self.data["device"].serial)}, + ) + async def _async_update_data(self) -> DeviceResponseEntry: """Fetch all device and sensor data from api.""" diff --git a/homeassistant/components/homewizard/number.py b/homeassistant/components/homewizard/number.py index 6acbbcfa37b..783841168ed 100644 --- a/homeassistant/components/homewizard/number.py +++ b/homeassistant/components/homewizard/number.py @@ -50,9 +50,7 @@ class HWEnergyNumberEntity( self._attr_name = "Status light brightness" self._attr_native_unit_of_measurement = PERCENTAGE self._attr_icon = "mdi:lightbulb-on" - self._attr_device_info = { - "identifiers": {(DOMAIN, coordinator.data["device"].serial)}, - } + self._attr_device_info = coordinator.device_info async def async_set_native_value(self, value: float) -> None: """Set a new value.""" diff --git a/homeassistant/components/homewizard/sensor.py b/homeassistant/components/homewizard/sensor.py index 4baaff8835d..493b5f1c0e3 100644 --- a/homeassistant/components/homewizard/sensor.py +++ b/homeassistant/components/homewizard/sensor.py @@ -18,7 +18,7 @@ from homeassistant.const import ( VOLUME_CUBIC_METERS, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo, EntityCategory +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -171,6 +171,7 @@ class HWEnergySensor(CoordinatorEntity[HWEnergyDeviceUpdateCoordinator], SensorE # Config attributes. self.data_type = description.key self._attr_unique_id = f"{entry.unique_id}_{description.key}" + self._attr_device_info = coordinator.device_info # Special case for export, not everyone has solarpanels # The chance that 'export' is non-zero when you have solar panels is nil @@ -181,17 +182,6 @@ class HWEnergySensor(CoordinatorEntity[HWEnergyDeviceUpdateCoordinator], SensorE if self.native_value == 0: self._attr_entity_registry_enabled_default = False - @property - def device_info(self) -> DeviceInfo: - """Return device information.""" - return { - "name": self.entry.title, - "manufacturer": "HomeWizard", - "sw_version": self.data["device"].firmware_version, - "model": self.data["device"].product_type, - "identifiers": {(DOMAIN, self.data["device"].serial)}, - } - @property def data(self) -> DeviceResponseEntry: """Return data object from DataUpdateCoordinator.""" diff --git a/homeassistant/components/homewizard/switch.py b/homeassistant/components/homewizard/switch.py index 7bf7d9a741e..8a40087403e 100644 --- a/homeassistant/components/homewizard/switch.py +++ b/homeassistant/components/homewizard/switch.py @@ -54,13 +54,7 @@ class HWEnergySwitchEntity( """Initialize the switch.""" super().__init__(coordinator) self._attr_unique_id = f"{entry.unique_id}_{key}" - self._attr_device_info = { - "name": entry.title, - "manufacturer": "HomeWizard", - "sw_version": coordinator.data["device"].firmware_version, - "model": coordinator.data["device"].product_type, - "identifiers": {(DOMAIN, coordinator.data["device"].serial)}, - } + self._attr_device_info = coordinator.device_info class HWEnergyMainSwitchEntity(HWEnergySwitchEntity): From a3b346b5602c3b8a61d2d46ebe863decb1270886 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Nov 2022 15:49:10 +0100 Subject: [PATCH 0863/1033] Trigger full CI on Text integration changes (#82907) --- .core_files.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.core_files.yaml b/.core_files.yaml index 4082c016d8f..65da342ea50 100644 --- a/.core_files.yaml +++ b/.core_files.yaml @@ -38,6 +38,7 @@ base_platforms: &base_platforms - homeassistant/components/siren/** - homeassistant/components/stt/** - homeassistant/components/switch/** + - homeassistant/components/text/** - homeassistant/components/tts/** - homeassistant/components/update/** - homeassistant/components/vacuum/** From f9364c4e013f9da2b102546440288694b5618546 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Nov 2022 15:54:48 +0100 Subject: [PATCH 0864/1033] Fix 4 misconfigured sensor entity descriptions in Tuya (#82933) --- homeassistant/components/tuya/sensor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/tuya/sensor.py b/homeassistant/components/tuya/sensor.py index 4ad0c8837cf..98969fe4c48 100644 --- a/homeassistant/components/tuya/sensor.py +++ b/homeassistant/components/tuya/sensor.py @@ -90,7 +90,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { key=DPCode.GAS_SENSOR_VALUE, name="Gas", icon="mdi:gas-cylinder", - device_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.MEASUREMENT, ), TuyaSensorEntityDescription( key=DPCode.CH4_SENSOR_VALUE, @@ -157,7 +157,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Smoke amount", icon="mdi:smoke-detector", entity_category=EntityCategory.DIAGNOSTIC, - device_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.MEASUREMENT, ), *BATTERY_SENSORS, ), @@ -514,7 +514,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { TuyaSensorEntityDescription( key=DPCode.GAS_SENSOR_VALUE, icon="mdi:gas-cylinder", - device_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.MEASUREMENT, ), *BATTERY_SENSORS, ), @@ -648,7 +648,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Smoke amount", icon="mdi:smoke-detector", entity_category=EntityCategory.DIAGNOSTIC, - device_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.MEASUREMENT, ), *BATTERY_SENSORS, ), From 9a2b608d855d608edc9a23716d208a861ecdafb2 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Tue, 29 Nov 2022 16:00:52 +0100 Subject: [PATCH 0865/1033] Bump pyoverkiz to 1.7.1 (#82925) --- homeassistant/components/overkiz/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/overkiz/manifest.json b/homeassistant/components/overkiz/manifest.json index 3025e8d060e..49a65b78c06 100644 --- a/homeassistant/components/overkiz/manifest.json +++ b/homeassistant/components/overkiz/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/overkiz", - "requirements": ["pyoverkiz==1.5.6"], + "requirements": ["pyoverkiz==1.7.1"], "zeroconf": [ { "type": "_kizbox._tcp.local.", diff --git a/requirements_all.txt b/requirements_all.txt index 11a9922382c..3ea8d1bfc94 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1806,7 +1806,7 @@ pyotgw==2.1.3 pyotp==2.7.0 # homeassistant.components.overkiz -pyoverkiz==1.5.6 +pyoverkiz==1.7.1 # homeassistant.components.openweathermap pyowm==3.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bb2164f68b3..d0b1433c8ac 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1283,7 +1283,7 @@ pyotgw==2.1.3 pyotp==2.7.0 # homeassistant.components.overkiz -pyoverkiz==1.5.6 +pyoverkiz==1.7.1 # homeassistant.components.openweathermap pyowm==3.2.0 From 211dd64df1f88423703c4f507a3f01efb5613198 Mon Sep 17 00:00:00 2001 From: Marvin Wichmann Date: Tue, 29 Nov 2022 16:03:03 +0100 Subject: [PATCH 0866/1033] Update xknx to 2.1.0 (#82930) --- homeassistant/components/knx/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json index da0753a9220..4bf1b13672b 100644 --- a/homeassistant/components/knx/manifest.json +++ b/homeassistant/components/knx/manifest.json @@ -3,7 +3,7 @@ "name": "KNX", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/knx", - "requirements": ["xknx==2.0.0"], + "requirements": ["xknx==2.1.0"], "codeowners": ["@Julius2342", "@farmio", "@marvin-w"], "quality_scale": "platinum", "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 3ea8d1bfc94..0ba6dc3da05 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2580,7 +2580,7 @@ xboxapi==2.0.1 xiaomi-ble==0.12.2 # homeassistant.components.knx -xknx==2.0.0 +xknx==2.1.0 # homeassistant.components.bluesound # homeassistant.components.fritz diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d0b1433c8ac..9e1f0cb9f92 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1793,7 +1793,7 @@ xbox-webapi==2.0.11 xiaomi-ble==0.12.2 # homeassistant.components.knx -xknx==2.0.0 +xknx==2.1.0 # homeassistant.components.bluesound # homeassistant.components.fritz From 2173075004ebeaa5cbda6db536c52485afac77b8 Mon Sep 17 00:00:00 2001 From: iridris Date: Tue, 29 Nov 2022 10:06:44 -0500 Subject: [PATCH 0867/1033] Bump pycsspeechtts to v1.0.8 (#82922) --- homeassistant/components/microsoft/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/microsoft/manifest.json b/homeassistant/components/microsoft/manifest.json index ec393125d24..4389c0d69b9 100644 --- a/homeassistant/components/microsoft/manifest.json +++ b/homeassistant/components/microsoft/manifest.json @@ -2,7 +2,7 @@ "domain": "microsoft", "name": "Microsoft Text-to-Speech (TTS)", "documentation": "https://www.home-assistant.io/integrations/microsoft", - "requirements": ["pycsspeechtts==1.0.4"], + "requirements": ["pycsspeechtts==1.0.8"], "codeowners": [], "iot_class": "cloud_push", "loggers": ["pycsspeechtts"] diff --git a/requirements_all.txt b/requirements_all.txt index 0ba6dc3da05..21d02743ca2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1516,7 +1516,7 @@ pycomfoconnect==0.4 pycoolmasternet-async==0.1.2 # homeassistant.components.microsoft -pycsspeechtts==1.0.4 +pycsspeechtts==1.0.8 # homeassistant.components.cups # pycups==1.9.73 From 2d7701cab5b5aae84d2185fa8d448baf728932d5 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 29 Nov 2022 16:14:32 +0100 Subject: [PATCH 0868/1033] Use SchemaOptionsFlowHandler in openuv (#82494) * Use suggested_values helper in openuv * Use SchemaOptionsFlowHandler --- .../components/openuv/config_flow.py | 62 +++++++------------ 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/homeassistant/components/openuv/config_flow.py b/homeassistant/components/openuv/config_flow.py index 8852ed677cd..3951a6ffb08 100644 --- a/homeassistant/components/openuv/config_flow.py +++ b/homeassistant/components/openuv/config_flow.py @@ -20,6 +20,10 @@ from homeassistant.const import ( from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import aiohttp_client, config_validation as cv +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaFlowFormStep, + SchemaOptionsFlowHandler, +) from .const import ( CONF_FROM_WINDOW, @@ -35,6 +39,21 @@ STEP_REAUTH_SCHEMA = vol.Schema( } ) +OPTIONS_SCHEMA = vol.Schema( + { + vol.Optional( + CONF_FROM_WINDOW, description={"suggested_value": DEFAULT_FROM_WINDOW} + ): vol.Coerce(float), + vol.Optional( + CONF_TO_WINDOW, description={"suggested_value": DEFAULT_TO_WINDOW} + ): vol.Coerce(float), + } +) + +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(OPTIONS_SCHEMA), +} + @dataclass class OpenUvData: @@ -116,9 +135,9 @@ class OpenUvFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): @staticmethod @callback - def async_get_options_flow(config_entry: ConfigEntry) -> OpenUvOptionsFlowHandler: + def async_get_options_flow(config_entry: ConfigEntry) -> SchemaOptionsFlowHandler: """Define the config flow to handle options.""" - return OpenUvOptionsFlowHandler(config_entry) + return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW) async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: """Handle configuration by re-auth.""" @@ -168,42 +187,3 @@ class OpenUvFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self._abort_if_unique_id_configured() return await self._async_verify(data, "user", self.step_user_schema) - - -class OpenUvOptionsFlowHandler(config_entries.OptionsFlow): - """Handle a OpenUV options flow.""" - - def __init__(self, entry: ConfigEntry) -> None: - """Initialize.""" - self.entry = entry - - async def async_step_init( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Manage the options.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - return self.async_show_form( - step_id="init", - data_schema=vol.Schema( - { - vol.Optional( - CONF_FROM_WINDOW, - description={ - "suggested_value": self.entry.options.get( - CONF_FROM_WINDOW, DEFAULT_FROM_WINDOW - ) - }, - ): vol.Coerce(float), - vol.Optional( - CONF_TO_WINDOW, - description={ - "suggested_value": self.entry.options.get( - CONF_TO_WINDOW, DEFAULT_TO_WINDOW - ) - }, - ): vol.Coerce(float), - } - ), - ) From a69e0defe62005e1f65aae16fa2d21ccb3097a60 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Nov 2022 16:40:59 +0100 Subject: [PATCH 0869/1033] Use device class enum instead of string (#82935) --- homeassistant/components/ring/sensor.py | 4 ++-- homeassistant/components/vultr/binary_sensor.py | 9 ++++++--- homeassistant/components/wirelesstag/sensor.py | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/ring/sensor.py b/homeassistant/components/ring/sensor.py index 1aaa073064f..10736c7b85c 100644 --- a/homeassistant/components/ring/sensor.py +++ b/homeassistant/components/ring/sensor.py @@ -206,7 +206,7 @@ SENSOR_TYPES: tuple[RingSensorEntityDescription, ...] = ( name="Battery", category=["doorbots", "authorized_doorbots", "stickup_cams"], native_unit_of_measurement=PERCENTAGE, - device_class="battery", + device_class=SensorDeviceClass.BATTERY, cls=RingSensor, ), RingSensorEntityDescription( @@ -255,7 +255,7 @@ SENSOR_TYPES: tuple[RingSensorEntityDescription, ...] = ( category=["chimes", "doorbots", "authorized_doorbots", "stickup_cams"], native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, icon="mdi:wifi", - device_class="signal_strength", + device_class=SensorDeviceClass.SIGNAL_STRENGTH, cls=HealthDataRingSensor, ), ) diff --git a/homeassistant/components/vultr/binary_sensor.py b/homeassistant/components/vultr/binary_sensor.py index 4ef35d4f410..1d877216e93 100644 --- a/homeassistant/components/vultr/binary_sensor.py +++ b/homeassistant/components/vultr/binary_sensor.py @@ -5,7 +5,11 @@ import logging import voluptuous as vol -from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity +from homeassistant.components.binary_sensor import ( + PLATFORM_SCHEMA, + BinarySensorDeviceClass, + BinarySensorEntity, +) from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -32,7 +36,6 @@ from . import ( _LOGGER = logging.getLogger(__name__) -DEFAULT_DEVICE_CLASS = "power" DEFAULT_NAME = "Vultr {}" PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { @@ -64,7 +67,7 @@ def setup_platform( class VultrBinarySensor(BinarySensorEntity): """Representation of a Vultr subscription sensor.""" - _attr_device_class = DEFAULT_DEVICE_CLASS + _attr_device_class = BinarySensorDeviceClass.POWER def __init__(self, vultr, subscription, name): """Initialize a new Vultr binary sensor.""" diff --git a/homeassistant/components/wirelesstag/sensor.py b/homeassistant/components/wirelesstag/sensor.py index fb54c90cc1f..cb5f3c59f57 100644 --- a/homeassistant/components/wirelesstag/sensor.py +++ b/homeassistant/components/wirelesstag/sensor.py @@ -47,7 +47,7 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { ), SENSOR_MOISTURE: SensorEntityDescription( key=SENSOR_MOISTURE, - device_class=SENSOR_MOISTURE, + device_class=SensorDeviceClass.MOISTURE, state_class=SensorStateClass.MEASUREMENT, ), SENSOR_LIGHT: SensorEntityDescription( From db9e8d2fa1b19d59cae5b6c267a4a07a4e8c4fb3 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 29 Nov 2022 16:48:51 +0100 Subject: [PATCH 0870/1033] Use OptionsFlowWithConfigEntry in braviatv (#82902) * Use OptionsFlowWithConfigEntry in braviatv * Make use of add_suggested_values_to_schema * Full coverage --- .../components/braviatv/config_flow.py | 29 ++++++-------- tests/components/braviatv/test_config_flow.py | 40 ++++++++++++++++++- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/braviatv/config_flow.py b/homeassistant/components/braviatv/config_flow.py index 45a2bad0036..183e13a19e8 100644 --- a/homeassistant/components/braviatv/config_flow.py +++ b/homeassistant/components/braviatv/config_flow.py @@ -245,22 +245,17 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return uuid, f"{NICKNAME_PREFIX} {uuid[:6]}" -class BraviaTVOptionsFlowHandler(config_entries.OptionsFlow): +class BraviaTVOptionsFlowHandler(config_entries.OptionsFlowWithConfigEntry): """Config flow options for Bravia TV.""" - def __init__(self, config_entry: ConfigEntry) -> None: - """Initialize Bravia TV options flow.""" - self.config_entry = config_entry - self.ignored_sources = config_entry.options.get(CONF_IGNORED_SOURCES) - self.source_list: list[str] = [] + data_schema: vol.Schema async def async_step_init( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Manage the options.""" - coordinator: BraviaTVCoordinator = self.hass.data[DOMAIN][ - self.config_entry.entry_id - ] + coordinator: BraviaTVCoordinator + coordinator = self.hass.data[DOMAIN][self.config_entry.entry_id] try: await coordinator.async_update_sources() @@ -268,7 +263,13 @@ class BraviaTVOptionsFlowHandler(config_entries.OptionsFlow): return self.async_abort(reason="failed_update") sources = coordinator.source_map.values() - self.source_list = [item["title"] for item in sources] + source_list = [item["title"] for item in sources] + self.data_schema = vol.Schema( + { + vol.Optional(CONF_IGNORED_SOURCES): cv.multi_select(source_list), + } + ) + return await self.async_step_user() async def async_step_user( @@ -280,11 +281,7 @@ class BraviaTVOptionsFlowHandler(config_entries.OptionsFlow): return self.async_show_form( step_id="user", - data_schema=vol.Schema( - { - vol.Optional( - CONF_IGNORED_SOURCES, default=self.ignored_sources - ): cv.multi_select(self.source_list) - } + data_schema=self.add_suggested_values_to_schema( + self.data_schema, self.options ), ) diff --git a/tests/components/braviatv/test_config_flow.py b/tests/components/braviatv/test_config_flow.py index 58e684a1378..ee835493e66 100644 --- a/tests/components/braviatv/test_config_flow.py +++ b/tests/components/braviatv/test_config_flow.py @@ -21,6 +21,7 @@ from homeassistant.components.braviatv.const import ( ) from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_SSDP, SOURCE_USER from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PIN +from homeassistant.core import HomeAssistant from homeassistant.helpers import instance_id from tests.common import MockConfigEntry @@ -381,7 +382,7 @@ async def test_create_entry_psk(hass): } -async def test_options_flow(hass): +async def test_options_flow(hass: HomeAssistant) -> None: """Test config flow options.""" config_entry = MockConfigEntry( domain=DOMAIN, @@ -422,6 +423,43 @@ async def test_options_flow(hass): assert config_entry.options == {CONF_IGNORED_SOURCES: ["HDMI 1", "HDMI 2"]} +async def test_options_flow_error(hass: HomeAssistant) -> None: + """Test config flow options.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + unique_id="very_unique_string", + data={ + CONF_HOST: "bravia-host", + CONF_PIN: "1234", + CONF_MAC: "AA:BB:CC:DD:EE:FF", + }, + title="TV-Model", + ) + config_entry.add_to_hass(hass) + + with patch("pybravia.BraviaTV.connect"), patch( + "pybravia.BraviaTV.get_power_status", + return_value="active", + ), patch( + "pybravia.BraviaTV.get_external_status", + return_value=BRAVIA_SOURCES, + ), patch( + "pybravia.BraviaTV.send_rest_req", + return_value={}, + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + with patch( + "pybravia.BraviaTV.send_rest_req", + side_effect=BraviaTVError, + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "failed_update" + + @pytest.mark.parametrize( "user_input", [{CONF_PIN: "mypsk", CONF_USE_PSK: True}, {CONF_PIN: "1234", CONF_USE_PSK: False}], From d769359b4f2f3f4f9be151299eb9f59901e80716 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Nov 2022 17:22:01 +0100 Subject: [PATCH 0871/1033] Remove invalid custom device class from MinutPoint sound sensor (#82934) --- homeassistant/components/point/sensor.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/homeassistant/components/point/sensor.py b/homeassistant/components/point/sensor.py index eba495a6c61..619180c0460 100644 --- a/homeassistant/components/point/sensor.py +++ b/homeassistant/components/point/sensor.py @@ -22,8 +22,6 @@ from .const import DOMAIN as POINT_DOMAIN, POINT_DISCOVERY_NEW _LOGGER = logging.getLogger(__name__) -DEVICE_CLASS_SOUND = "sound_level" - @dataclass class MinutPointRequiredKeysMixin: @@ -55,7 +53,6 @@ SENSOR_TYPES: tuple[MinutPointSensorEntityDescription, ...] = ( MinutPointSensorEntityDescription( key="sound", precision=1, - device_class=DEVICE_CLASS_SOUND, icon="mdi:ear-hearing", native_unit_of_measurement=SOUND_PRESSURE_WEIGHTED_DBA, ), From 0561c14d53b070f7d0957cff78b25a10c6b31e06 Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Tue, 29 Nov 2022 18:05:51 +0100 Subject: [PATCH 0872/1033] Add `text` platform for KNX (#82811) --- homeassistant/components/knx/__init__.py | 2 + homeassistant/components/knx/const.py | 1 + homeassistant/components/knx/schema.py | 21 +++++ homeassistant/components/knx/text.py | 92 +++++++++++++++++++++ tests/components/knx/test_text.py | 100 +++++++++++++++++++++++ 5 files changed, 216 insertions(+) create mode 100644 homeassistant/components/knx/text.py create mode 100644 tests/components/knx/test_text.py diff --git a/homeassistant/components/knx/__init__.py b/homeassistant/components/knx/__init__.py index c8b9ca40bd7..5d2ab031323 100644 --- a/homeassistant/components/knx/__init__.py +++ b/homeassistant/components/knx/__init__.py @@ -83,6 +83,7 @@ from .schema import ( SelectSchema, SensorSchema, SwitchSchema, + TextSchema, WeatherSchema, ga_validator, sensor_type_validator, @@ -133,6 +134,7 @@ CONFIG_SCHEMA = vol.Schema( **SelectSchema.platform_node(), **SensorSchema.platform_node(), **SwitchSchema.platform_node(), + **TextSchema.platform_node(), **WeatherSchema.platform_node(), } ), diff --git a/homeassistant/components/knx/const.py b/homeassistant/components/knx/const.py index f98d42fbd9e..058223bfaa1 100644 --- a/homeassistant/components/knx/const.py +++ b/homeassistant/components/knx/const.py @@ -119,6 +119,7 @@ SUPPORTED_PLATFORMS: Final = [ Platform.SELECT, Platform.SENSOR, Platform.SWITCH, + Platform.TEXT, Platform.WEATHER, ] diff --git a/homeassistant/components/knx/schema.py b/homeassistant/components/knx/schema.py index 370eaa7303e..87a7b6fdab5 100644 --- a/homeassistant/components/knx/schema.py +++ b/homeassistant/components/knx/schema.py @@ -29,6 +29,7 @@ from homeassistant.components.sensor import ( from homeassistant.components.switch import ( DEVICE_CLASSES_SCHEMA as SWITCH_DEVICE_CLASSES_SCHEMA, ) +from homeassistant.components.text import TextMode from homeassistant.const import ( CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, @@ -887,6 +888,26 @@ class SwitchSchema(KNXPlatformSchema): ) +class TextSchema(KNXPlatformSchema): + """Voluptuous schema for KNX text.""" + + PLATFORM = Platform.TEXT + + DEFAULT_NAME = "KNX Text" + + ENTITY_SCHEMA = vol.Schema( + { + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean, + vol.Optional(CONF_TYPE, default="latin_1"): string_type_validator, + vol.Optional(CONF_MODE, default=TextMode.TEXT): vol.Coerce(TextMode), + vol.Required(KNX_ADDRESS): ga_list_validator, + vol.Optional(CONF_STATE_ADDRESS): ga_list_validator, + vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, + } + ) + + class WeatherSchema(KNXPlatformSchema): """Voluptuous schema for KNX weather station.""" diff --git a/homeassistant/components/knx/text.py b/homeassistant/components/knx/text.py new file mode 100644 index 00000000000..abd3f44ae6b --- /dev/null +++ b/homeassistant/components/knx/text.py @@ -0,0 +1,92 @@ +"""Support for KNX/IP text.""" +from __future__ import annotations + +from xknx import XKNX +from xknx.devices import Notification as XknxNotification +from xknx.dpt import DPTLatin1 + +from homeassistant import config_entries +from homeassistant.components.text import TextEntity +from homeassistant.const import ( + CONF_ENTITY_CATEGORY, + CONF_MODE, + CONF_NAME, + CONF_TYPE, + STATE_UNAVAILABLE, + STATE_UNKNOWN, + Platform, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.restore_state import RestoreEntity +from homeassistant.helpers.typing import ConfigType + +from .const import ( + CONF_RESPOND_TO_READ, + CONF_STATE_ADDRESS, + DATA_KNX_CONFIG, + DOMAIN, + KNX_ADDRESS, +) +from .knx_entity import KnxEntity + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: config_entries.ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up sensor(s) for KNX platform.""" + xknx: XKNX = hass.data[DOMAIN].xknx + config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.TEXT] + + async_add_entities(KNXText(xknx, entity_config) for entity_config in config) + + +def _create_notification(xknx: XKNX, config: ConfigType) -> XknxNotification: + """Return a KNX Notification to be used within XKNX.""" + return XknxNotification( + xknx, + name=config[CONF_NAME], + group_address=config[KNX_ADDRESS], + group_address_state=config.get(CONF_STATE_ADDRESS), + respond_to_read=config[CONF_RESPOND_TO_READ], + value_type=config[CONF_TYPE], + ) + + +class KNXText(KnxEntity, TextEntity, RestoreEntity): + """Representation of a KNX text.""" + + _device: XknxNotification + _attr_native_max = 14 + + def __init__(self, xknx: XKNX, config: ConfigType) -> None: + """Initialize a KNX text.""" + super().__init__(_create_notification(xknx, config)) + self._attr_mode = config[CONF_MODE] + self._attr_pattern = ( + r"[\u0000-\u00ff]*" # Latin-1 + if issubclass(self._device.remote_value.dpt_class, DPTLatin1) + else r"[\u0000-\u007f]*" # ASCII + ) + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) + self._attr_unique_id = str(self._device.remote_value.group_address) + + async def async_added_to_hass(self) -> None: + """Restore last state.""" + await super().async_added_to_hass() + if not self._device.remote_value.readable and ( + last_state := await self.async_get_last_state() + ): + if last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE): + self._device.remote_value.value = last_state.state + + @property + def native_value(self) -> str | None: + """Return the value reported by the text.""" + return self._device.message + + async def async_set_value(self, value: str) -> None: + """Change the value.""" + await self._device.set(value) diff --git a/tests/components/knx/test_text.py b/tests/components/knx/test_text.py new file mode 100644 index 00000000000..0f5169054b3 --- /dev/null +++ b/tests/components/knx/test_text.py @@ -0,0 +1,100 @@ +"""Test KNX number.""" +from homeassistant.components.knx.const import CONF_RESPOND_TO_READ, KNX_ADDRESS +from homeassistant.components.knx.schema import TextSchema +from homeassistant.const import CONF_NAME +from homeassistant.core import HomeAssistant, State + +from .conftest import KNXTestKit + +from tests.common import mock_restore_cache + + +async def test_text(hass: HomeAssistant, knx: KNXTestKit): + """Test KNX text.""" + test_address = "1/1/1" + await knx.setup_integration( + { + TextSchema.PLATFORM: { + CONF_NAME: "test", + KNX_ADDRESS: test_address, + } + } + ) + # set value + await hass.services.async_call( + "text", + "set_value", + {"entity_id": "text.test", "value": "hello world"}, + blocking=True, + ) + await knx.assert_write( + test_address, + ( + 0x68, + 0x65, + 0x6C, + 0x6C, + 0x6F, + 0x20, + 0x77, + 0x6F, + 0x72, + 0x6C, + 0x64, + 0x0, + 0x0, + 0x0, + ), + ) + state = hass.states.get("text.test") + assert state.state == "hello world" + + # update from KNX + await knx.receive_write( + test_address, + (0x68, 0x61, 0x6C, 0x6C, 0x6F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0), + ) + state = hass.states.get("text.test") + assert state.state == "hallo" + + +async def test_text_restore_and_respond(hass: HomeAssistant, knx: KNXTestKit): + """Test KNX text with passive_address, restoring state and respond_to_read.""" + test_address = "1/1/1" + test_passive_address = "3/3/3" + + fake_state = State("text.test", "test test") + mock_restore_cache(hass, (fake_state,)) + + await knx.setup_integration( + { + TextSchema.PLATFORM: { + CONF_NAME: "test", + KNX_ADDRESS: [test_address, test_passive_address], + CONF_RESPOND_TO_READ: True, + } + } + ) + # restored state - doesn't send telegram + state = hass.states.get("text.test") + assert state.state == "test test" + await knx.assert_telegram_count(0) + + # respond with restored state + await knx.receive_read(test_address) + await knx.assert_response( + test_address, + (0x74, 0x65, 0x73, 0x74, 0x20, 0x74, 0x65, 0x73, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0), + ) + + # don't respond to passive address + await knx.receive_read(test_passive_address) + await knx.assert_no_telegram() + + # update from KNX passive address + await knx.receive_write( + test_passive_address, + (0x68, 0x61, 0x6C, 0x6C, 0x6F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0), + ) + state = hass.states.get("text.test") + assert state.state == "hallo" From 4ad9633dfb3979489d0a222753feb467b648ac42 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 29 Nov 2022 18:10:31 +0100 Subject: [PATCH 0873/1033] Use new unit enums in weather entity (#82937) --- homeassistant/components/demo/weather.py | 21 ++---- homeassistant/components/weather/__init__.py | 68 ++++++++------------ homeassistant/util/unit_system.py | 5 +- 3 files changed, 38 insertions(+), 56 deletions(-) diff --git a/homeassistant/components/demo/weather.py b/homeassistant/components/demo/weather.py index cd1a3a6258c..e64d0bcc28d 100644 --- a/homeassistant/components/demo/weather.py +++ b/homeassistant/components/demo/weather.py @@ -22,14 +22,7 @@ from homeassistant.components.weather import ( WeatherEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - PRESSURE_HPA, - PRESSURE_INHG, - SPEED_METERS_PER_SECOND, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, -) +from homeassistant.const import UnitOfPressure, UnitOfSpeed, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -78,9 +71,9 @@ def setup_platform( 92, 1099, 0.5, - TEMP_CELSIUS, - PRESSURE_HPA, - SPEED_METERS_PER_SECOND, + UnitOfTemperature.CELSIUS, + UnitOfPressure.HPA, + UnitOfSpeed.METERS_PER_SECOND, [ [ATTR_CONDITION_RAINY, 1, 22, 15, 60], [ATTR_CONDITION_RAINY, 5, 19, 8, 30], @@ -98,9 +91,9 @@ def setup_platform( 54, 987, 4.8, - TEMP_FAHRENHEIT, - PRESSURE_INHG, - SPEED_MILES_PER_HOUR, + UnitOfTemperature.FAHRENHEIT, + UnitOfPressure.INHG, + UnitOfSpeed.MILES_PER_HOUR, [ [ATTR_CONDITION_SNOWY, 2, -10, -15, 60], [ATTR_CONDITION_PARTLYCLOUDY, 1, -13, -14, 25], diff --git a/homeassistant/components/weather/__init__.py b/homeassistant/components/weather/__init__.py index 8ffced6d5d2..610b8f7c355 100644 --- a/homeassistant/components/weather/__init__.py +++ b/homeassistant/components/weather/__init__.py @@ -11,24 +11,14 @@ from typing import Any, Final, TypedDict, final from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - LENGTH_INCHES, - LENGTH_KILOMETERS, - LENGTH_MILES, - LENGTH_MILLIMETERS, PRECISION_HALVES, PRECISION_TENTHS, PRECISION_WHOLE, - PRESSURE_HPA, - PRESSURE_INHG, - PRESSURE_MBAR, - PRESSURE_MMHG, - SPEED_FEET_PER_SECOND, - SPEED_KILOMETERS_PER_HOUR, - SPEED_KNOTS, - SPEED_METERS_PER_SECOND, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfLength, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.config_validation import ( # noqa: F401 @@ -44,7 +34,7 @@ from homeassistant.util.unit_conversion import ( SpeedConverter, TemperatureConverter, ) -from homeassistant.util.unit_system import METRIC_SYSTEM +from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM _LOGGER = logging.getLogger(__name__) @@ -101,29 +91,29 @@ SCAN_INTERVAL = timedelta(seconds=30) ROUNDING_PRECISION = 2 VALID_UNITS_PRESSURE: set[str] = { - PRESSURE_HPA, - PRESSURE_MBAR, - PRESSURE_INHG, - PRESSURE_MMHG, + UnitOfPressure.HPA, + UnitOfPressure.MBAR, + UnitOfPressure.INHG, + UnitOfPressure.MMHG, } VALID_UNITS_TEMPERATURE: set[str] = { - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature.CELSIUS, + UnitOfTemperature.FAHRENHEIT, } VALID_UNITS_PRECIPITATION: set[str] = { - LENGTH_MILLIMETERS, - LENGTH_INCHES, + UnitOfPrecipitationDepth.MILLIMETERS, + UnitOfPrecipitationDepth.INCHES, } VALID_UNITS_VISIBILITY: set[str] = { - LENGTH_KILOMETERS, - LENGTH_MILES, + UnitOfLength.KILOMETERS, + UnitOfLength.MILES, } VALID_UNITS_WIND_SPEED: set[str] = { - SPEED_FEET_PER_SECOND, - SPEED_KILOMETERS_PER_HOUR, - SPEED_KNOTS, - SPEED_METERS_PER_SECOND, - SPEED_MILES_PER_HOUR, + UnitOfSpeed.FEET_PER_SECOND, + UnitOfSpeed.KILOMETERS_PER_HOUR, + UnitOfSpeed.KNOTS, + UnitOfSpeed.METERS_PER_SECOND, + UnitOfSpeed.MILES_PER_HOUR, } UNIT_CONVERSIONS: dict[str, Callable[[float, str, str], float]] = { @@ -420,9 +410,9 @@ class WeatherEntity(Entity): Should not be set by integrations. """ - return ( - PRESSURE_HPA if self.hass.config.units is METRIC_SYSTEM else PRESSURE_INHG - ) + if self.hass.config.units is US_CUSTOMARY_SYSTEM: + return UnitOfPressure.INHG + return UnitOfPressure.HPA @final @property @@ -484,11 +474,9 @@ class WeatherEntity(Entity): Should not be set by integrations. """ - return ( - SPEED_KILOMETERS_PER_HOUR - if self.hass.config.units is METRIC_SYSTEM - else SPEED_MILES_PER_HOUR - ) + if self.hass.config.units is US_CUSTOMARY_SYSTEM: + return UnitOfSpeed.MILES_PER_HOUR + return UnitOfSpeed.KILOMETERS_PER_HOUR @final @property @@ -623,7 +611,7 @@ class WeatherEntity(Entity): return self._attr_precision return ( PRECISION_TENTHS - if self._temperature_unit == TEMP_CELSIUS + if self._temperature_unit == UnitOfTemperature.CELSIUS else PRECISION_WHOLE ) diff --git a/homeassistant/util/unit_system.py b/homeassistant/util/unit_system.py index 6350d80f3d7..9c6b6045cf9 100644 --- a/homeassistant/util/unit_system.py +++ b/homeassistant/util/unit_system.py @@ -17,6 +17,7 @@ from homeassistant.const import ( WIND_SPEED, UnitOfLength, UnitOfMass, + UnitOfPrecipitationDepth, UnitOfPressure, UnitOfSpeed, UnitOfTemperature, @@ -247,7 +248,7 @@ validate_unit_system = vol.All( METRIC_SYSTEM = UnitSystem( _CONF_UNIT_SYSTEM_METRIC, - accumulated_precipitation=UnitOfLength.MILLIMETERS, + accumulated_precipitation=UnitOfPrecipitationDepth.MILLIMETERS, conversions={ # Convert non-metric distances ("distance", UnitOfLength.FEET): UnitOfLength.METERS, @@ -279,7 +280,7 @@ METRIC_SYSTEM = UnitSystem( US_CUSTOMARY_SYSTEM = UnitSystem( _CONF_UNIT_SYSTEM_US_CUSTOMARY, - accumulated_precipitation=UnitOfLength.INCHES, + accumulated_precipitation=UnitOfPrecipitationDepth.INCHES, conversions={ # Convert non-USCS distances ("distance", UnitOfLength.CENTIMETERS): UnitOfLength.INCHES, From c092f2be049d61b97cbfc6a97113d02166c1139b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 29 Nov 2022 18:13:05 +0100 Subject: [PATCH 0874/1033] Use new unit enums in weather integrations (#82938) --- .../components/accuweather/weather.py | 35 ++++++++----------- homeassistant/components/aemet/weather.py | 16 ++++----- .../components/buienradar/weather.py | 20 +++++------ homeassistant/components/darksky/weather.py | 20 +++++------ homeassistant/components/ecobee/weather.py | 16 ++++----- .../components/environment_canada/weather.py | 16 ++++----- .../components/homematicip_cloud/weather.py | 10 +++--- homeassistant/components/ipma/weather.py | 12 +++---- homeassistant/components/knx/weather.py | 12 +++---- homeassistant/components/met/weather.py | 16 ++++----- .../components/met_eireann/weather.py | 16 ++++----- .../components/meteo_france/weather.py | 16 ++++----- .../components/meteoclimatic/weather.py | 8 ++--- homeassistant/components/metoffice/weather.py | 8 ++--- homeassistant/components/nws/weather.py | 24 ++++++------- .../components/open_meteo/weather.py | 12 +++---- .../components/openweathermap/weather.py | 16 ++++----- homeassistant/components/smhi/weather.py | 22 ++++++------ homeassistant/components/zamg/weather.py | 16 ++++----- 19 files changed, 151 insertions(+), 160 deletions(-) diff --git a/homeassistant/components/accuweather/weather.py b/homeassistant/components/accuweather/weather.py index 82db25288b8..5c5ba303ad5 100644 --- a/homeassistant/components/accuweather/weather.py +++ b/homeassistant/components/accuweather/weather.py @@ -18,16 +18,11 @@ from homeassistant.components.weather import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - LENGTH_INCHES, - LENGTH_KILOMETERS, - LENGTH_MILES, - LENGTH_MILLIMETERS, - PRESSURE_HPA, - PRESSURE_INHG, - SPEED_KILOMETERS_PER_HOUR, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfLength, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -72,19 +67,19 @@ class AccuWeatherEntity( # converted, hence the weather entity's native units follow the configured unit # system if coordinator.hass.config.units is METRIC_SYSTEM: - self._attr_native_precipitation_unit = LENGTH_MILLIMETERS - self._attr_native_pressure_unit = PRESSURE_HPA - self._attr_native_temperature_unit = TEMP_CELSIUS - self._attr_native_visibility_unit = LENGTH_KILOMETERS - self._attr_native_wind_speed_unit = SPEED_KILOMETERS_PER_HOUR + self._attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS + self._attr_native_pressure_unit = UnitOfPressure.HPA + self._attr_native_temperature_unit = UnitOfTemperature.CELSIUS + self._attr_native_visibility_unit = UnitOfLength.KILOMETERS + self._attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR self._unit_system = API_METRIC else: self._unit_system = API_IMPERIAL - self._attr_native_precipitation_unit = LENGTH_INCHES - self._attr_native_pressure_unit = PRESSURE_INHG - self._attr_native_temperature_unit = TEMP_FAHRENHEIT - self._attr_native_visibility_unit = LENGTH_MILES - self._attr_native_wind_speed_unit = SPEED_MILES_PER_HOUR + self._attr_native_precipitation_unit = UnitOfPrecipitationDepth.INCHES + self._attr_native_pressure_unit = UnitOfPressure.INHG + self._attr_native_temperature_unit = UnitOfTemperature.FAHRENHEIT + self._attr_native_visibility_unit = UnitOfLength.MILES + self._attr_native_wind_speed_unit = UnitOfSpeed.MILES_PER_HOUR self._attr_unique_id = coordinator.location_key self._attr_attribution = ATTRIBUTION self._attr_device_info = coordinator.device_info diff --git a/homeassistant/components/aemet/weather.py b/homeassistant/components/aemet/weather.py index 695ae283a47..3753d8e33ff 100644 --- a/homeassistant/components/aemet/weather.py +++ b/homeassistant/components/aemet/weather.py @@ -12,10 +12,10 @@ from homeassistant.components.weather import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - LENGTH_MILLIMETERS, - PRESSURE_HPA, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -91,10 +91,10 @@ class AemetWeather(CoordinatorEntity[WeatherUpdateCoordinator], WeatherEntity): """Implementation of an AEMET OpenData sensor.""" _attr_attribution = ATTRIBUTION - _attr_native_precipitation_unit = LENGTH_MILLIMETERS - _attr_native_pressure_unit = PRESSURE_HPA - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_wind_speed_unit = SPEED_KILOMETERS_PER_HOUR + _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS + _attr_native_pressure_unit = UnitOfPressure.HPA + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR def __init__( self, diff --git a/homeassistant/components/buienradar/weather.py b/homeassistant/components/buienradar/weather.py index 6fdf5c166ee..4cee98c07b7 100644 --- a/homeassistant/components/buienradar/weather.py +++ b/homeassistant/components/buienradar/weather.py @@ -41,11 +41,11 @@ from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, - LENGTH_METERS, - LENGTH_MILLIMETERS, - PRESSURE_HPA, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, + UnitOfLength, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -120,11 +120,11 @@ async def async_setup_entry( class BrWeather(WeatherEntity): """Representation of a weather condition.""" - _attr_native_precipitation_unit = LENGTH_MILLIMETERS - _attr_native_pressure_unit = PRESSURE_HPA - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_visibility_unit = LENGTH_METERS - _attr_native_wind_speed_unit = SPEED_METERS_PER_SECOND + _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS + _attr_native_pressure_unit = UnitOfPressure.HPA + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_visibility_unit = UnitOfLength.METERS + _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND def __init__(self, data, config, coordinates): """Initialize the platform with a data instance and station name.""" diff --git a/homeassistant/components/darksky/weather.py b/homeassistant/components/darksky/weather.py index 88f3b6b2bc9..530f1788dd8 100644 --- a/homeassistant/components/darksky/weather.py +++ b/homeassistant/components/darksky/weather.py @@ -36,11 +36,11 @@ from homeassistant.const import ( CONF_LONGITUDE, CONF_MODE, CONF_NAME, - LENGTH_KILOMETERS, - LENGTH_MILLIMETERS, - PRESSURE_MBAR, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, + UnitOfLength, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -113,11 +113,11 @@ def setup_platform( class DarkSkyWeather(WeatherEntity): """Representation of a weather condition.""" - _attr_native_precipitation_unit = LENGTH_MILLIMETERS - _attr_native_pressure_unit = PRESSURE_MBAR - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_visibility_unit = LENGTH_KILOMETERS - _attr_native_wind_speed_unit = SPEED_METERS_PER_SECOND + _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS + _attr_native_pressure_unit = UnitOfPressure.MBAR + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_visibility_unit = UnitOfLength.KILOMETERS + _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND def __init__(self, name, dark_sky, mode): """Initialize Dark Sky weather.""" diff --git a/homeassistant/components/ecobee/weather.py b/homeassistant/components/ecobee/weather.py index 69f02d26294..5610cdb2a9c 100644 --- a/homeassistant/components/ecobee/weather.py +++ b/homeassistant/components/ecobee/weather.py @@ -16,10 +16,10 @@ from homeassistant.components.weather import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - LENGTH_METERS, - PRESSURE_HPA, - SPEED_METERS_PER_SECOND, - TEMP_FAHRENHEIT, + UnitOfLength, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -53,10 +53,10 @@ async def async_setup_entry( class EcobeeWeather(WeatherEntity): """Representation of Ecobee weather data.""" - _attr_native_pressure_unit = PRESSURE_HPA - _attr_native_temperature_unit = TEMP_FAHRENHEIT - _attr_native_visibility_unit = LENGTH_METERS - _attr_native_wind_speed_unit = SPEED_METERS_PER_SECOND + _attr_native_pressure_unit = UnitOfPressure.HPA + _attr_native_temperature_unit = UnitOfTemperature.FAHRENHEIT + _attr_native_visibility_unit = UnitOfLength.METERS + _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND def __init__(self, data, name, index): """Initialize the Ecobee weather platform.""" diff --git a/homeassistant/components/environment_canada/weather.py b/homeassistant/components/environment_canada/weather.py index 8dbf8c15731..74bf9c8ca54 100644 --- a/homeassistant/components/environment_canada/weather.py +++ b/homeassistant/components/environment_canada/weather.py @@ -25,10 +25,10 @@ from homeassistant.components.weather import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - LENGTH_KILOMETERS, - PRESSURE_KPA, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, + UnitOfLength, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -70,10 +70,10 @@ class ECWeather(CoordinatorEntity, WeatherEntity): """Representation of a weather condition.""" _attr_has_entity_name = True - _attr_native_pressure_unit = PRESSURE_KPA - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_visibility_unit = LENGTH_KILOMETERS - _attr_native_wind_speed_unit = SPEED_KILOMETERS_PER_HOUR + _attr_native_pressure_unit = UnitOfPressure.KPA + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_visibility_unit = UnitOfLength.KILOMETERS + _attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR def __init__(self, coordinator, hourly): """Initialize Environment Canada weather.""" diff --git a/homeassistant/components/homematicip_cloud/weather.py b/homeassistant/components/homematicip_cloud/weather.py index a52e99bfa4e..e913e1125f1 100644 --- a/homeassistant/components/homematicip_cloud/weather.py +++ b/homeassistant/components/homematicip_cloud/weather.py @@ -22,7 +22,7 @@ from homeassistant.components.weather import ( WeatherEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import SPEED_KILOMETERS_PER_HOUR, TEMP_CELSIUS +from homeassistant.const import UnitOfSpeed, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -70,8 +70,8 @@ async def async_setup_entry( class HomematicipWeatherSensor(HomematicipGenericEntity, WeatherEntity): """Representation of the HomematicIP weather sensor plus & basic.""" - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_wind_speed_unit = SPEED_KILOMETERS_PER_HOUR + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the weather sensor.""" @@ -126,8 +126,8 @@ class HomematicipWeatherSensorPro(HomematicipWeatherSensor): class HomematicipHomeWeather(HomematicipGenericEntity, WeatherEntity): """Representation of the HomematicIP home weather.""" - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_wind_speed_unit = SPEED_KILOMETERS_PER_HOUR + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR def __init__(self, hap: HomematicipHAP) -> None: """Initialize the home weather.""" diff --git a/homeassistant/components/ipma/weather.py b/homeassistant/components/ipma/weather.py index c448fad592d..467523c5830 100644 --- a/homeassistant/components/ipma/weather.py +++ b/homeassistant/components/ipma/weather.py @@ -38,9 +38,9 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_MODE, CONF_NAME, - PRESSURE_HPA, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry @@ -115,9 +115,9 @@ async def async_setup_entry( class IPMAWeather(WeatherEntity): """Representation of a weather condition.""" - _attr_native_pressure_unit = PRESSURE_HPA - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_wind_speed_unit = SPEED_KILOMETERS_PER_HOUR + _attr_native_pressure_unit = UnitOfPressure.HPA + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR _attr_attribution = ATTRIBUTION diff --git a/homeassistant/components/knx/weather.py b/homeassistant/components/knx/weather.py index 32f37ad2ac2..92034be95ff 100644 --- a/homeassistant/components/knx/weather.py +++ b/homeassistant/components/knx/weather.py @@ -9,10 +9,10 @@ from homeassistant.components.weather import WeatherEntity from homeassistant.const import ( CONF_ENTITY_CATEGORY, CONF_NAME, - PRESSURE_PA, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, Platform, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -75,9 +75,9 @@ class KNXWeather(KnxEntity, WeatherEntity): """Representation of a KNX weather device.""" _device: XknxWeather - _attr_native_pressure_unit = PRESSURE_PA - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_wind_speed_unit = SPEED_METERS_PER_SECOND + _attr_native_pressure_unit = UnitOfPressure.PA + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND def __init__(self, xknx: XKNX, config: ConfigType) -> None: """Initialize of a KNX sensor.""" diff --git a/homeassistant/components/met/weather.py b/homeassistant/components/met/weather.py index 2aa70929795..222d4a040e3 100644 --- a/homeassistant/components/met/weather.py +++ b/homeassistant/components/met/weather.py @@ -20,10 +20,10 @@ from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, - LENGTH_MILLIMETERS, - PRESSURE_HPA, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType @@ -76,10 +76,10 @@ class MetWeather(CoordinatorEntity[MetDataUpdateCoordinator], WeatherEntity): """Implementation of a Met.no weather condition.""" _attr_has_entity_name = True - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_precipitation_unit = LENGTH_MILLIMETERS - _attr_native_pressure_unit = PRESSURE_HPA - _attr_native_wind_speed_unit = SPEED_KILOMETERS_PER_HOUR + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS + _attr_native_pressure_unit = UnitOfPressure.HPA + _attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR def __init__( self, diff --git a/homeassistant/components/met_eireann/weather.py b/homeassistant/components/met_eireann/weather.py index b872e9a8df6..c4d8763efa7 100644 --- a/homeassistant/components/met_eireann/weather.py +++ b/homeassistant/components/met_eireann/weather.py @@ -11,10 +11,10 @@ from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, - LENGTH_MILLIMETERS, - PRESSURE_HPA, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType @@ -55,10 +55,10 @@ async def async_setup_entry( class MetEireannWeather(CoordinatorEntity, WeatherEntity): """Implementation of a Met Éireann weather condition.""" - _attr_native_precipitation_unit = LENGTH_MILLIMETERS - _attr_native_pressure_unit = PRESSURE_HPA - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_wind_speed_unit = SPEED_KILOMETERS_PER_HOUR + _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS + _attr_native_pressure_unit = UnitOfPressure.HPA + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR def __init__(self, coordinator, config, hourly): """Initialise the platform with a data instance and site.""" diff --git a/homeassistant/components/meteo_france/weather.py b/homeassistant/components/meteo_france/weather.py index 787b82854af..bf0ff8c469e 100644 --- a/homeassistant/components/meteo_france/weather.py +++ b/homeassistant/components/meteo_france/weather.py @@ -15,10 +15,10 @@ from homeassistant.components.weather import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_MODE, - LENGTH_MILLIMETERS, - PRESSURE_HPA, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType @@ -77,10 +77,10 @@ async def async_setup_entry( class MeteoFranceWeather(CoordinatorEntity, WeatherEntity): """Representation of a weather condition.""" - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_precipitation_unit = LENGTH_MILLIMETERS - _attr_native_pressure_unit = PRESSURE_HPA - _attr_native_wind_speed_unit = SPEED_METERS_PER_SECOND + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS + _attr_native_pressure_unit = UnitOfPressure.HPA + _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND def __init__(self, coordinator: DataUpdateCoordinator, mode: str) -> None: """Initialise the platform with a data instance and station name.""" diff --git a/homeassistant/components/meteoclimatic/weather.py b/homeassistant/components/meteoclimatic/weather.py index 8044dd04aa8..14b953663d0 100644 --- a/homeassistant/components/meteoclimatic/weather.py +++ b/homeassistant/components/meteoclimatic/weather.py @@ -3,7 +3,7 @@ from meteoclimatic import Condition from homeassistant.components.weather import WeatherEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PRESSURE_HPA, SPEED_KILOMETERS_PER_HOUR, TEMP_CELSIUS +from homeassistant.const import UnitOfPressure, UnitOfSpeed, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo @@ -38,9 +38,9 @@ async def async_setup_entry( class MeteoclimaticWeather(CoordinatorEntity, WeatherEntity): """Representation of a weather condition.""" - _attr_native_pressure_unit = PRESSURE_HPA - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_wind_speed_unit = SPEED_KILOMETERS_PER_HOUR + _attr_native_pressure_unit = UnitOfPressure.HPA + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR def __init__(self, coordinator: DataUpdateCoordinator) -> None: """Initialise the weather platform.""" diff --git a/homeassistant/components/metoffice/weather.py b/homeassistant/components/metoffice/weather.py index 4733ca6ea73..8257c8a3c35 100644 --- a/homeassistant/components/metoffice/weather.py +++ b/homeassistant/components/metoffice/weather.py @@ -15,7 +15,7 @@ from homeassistant.components.weather import ( WeatherEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PRESSURE_HPA, SPEED_MILES_PER_HOUR, TEMP_CELSIUS +from homeassistant.const import UnitOfPressure, UnitOfSpeed, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import ( @@ -82,9 +82,9 @@ class MetOfficeWeather( _attr_attribution = ATTRIBUTION _attr_has_entity_name = True - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_pressure_unit = PRESSURE_HPA - _attr_native_wind_speed_unit = SPEED_MILES_PER_HOUR + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_pressure_unit = UnitOfPressure.HPA + _attr_native_wind_speed_unit = UnitOfSpeed.MILES_PER_HOUR def __init__( self, diff --git a/homeassistant/components/nws/weather.py b/homeassistant/components/nws/weather.py index 7963c1161a9..341f35353ef 100644 --- a/homeassistant/components/nws/weather.py +++ b/homeassistant/components/nws/weather.py @@ -14,12 +14,10 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, - LENGTH_METERS, - PRESSURE_PA, - SPEED_KILOMETERS_PER_HOUR, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfLength, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo @@ -153,7 +151,7 @@ class NWSWeather(WeatherEntity): @property def native_temperature_unit(self): """Return the current temperature unit.""" - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS @property def native_pressure(self): @@ -165,7 +163,7 @@ class NWSWeather(WeatherEntity): @property def native_pressure_unit(self): """Return the current pressure unit.""" - return PRESSURE_PA + return UnitOfPressure.PA @property def humidity(self): @@ -184,7 +182,7 @@ class NWSWeather(WeatherEntity): @property def native_wind_speed_unit(self): """Return the current windspeed.""" - return SPEED_KILOMETERS_PER_HOUR + return UnitOfSpeed.KILOMETERS_PER_HOUR @property def wind_bearing(self): @@ -216,7 +214,7 @@ class NWSWeather(WeatherEntity): @property def native_visibility_unit(self): """Return visibility unit.""" - return LENGTH_METERS + return UnitOfLength.METERS @property def forecast(self): @@ -234,7 +232,7 @@ class NWSWeather(WeatherEntity): if (temp := forecast_entry.get("temperature")) is not None: data[ATTR_FORECAST_NATIVE_TEMP] = TemperatureConverter.convert( - temp, TEMP_FAHRENHEIT, TEMP_CELSIUS + temp, UnitOfTemperature.FAHRENHEIT, UnitOfTemperature.CELSIUS ) else: data[ATTR_FORECAST_NATIVE_TEMP] = None @@ -254,7 +252,9 @@ class NWSWeather(WeatherEntity): wind_speed = forecast_entry.get("windSpeedAvg") if wind_speed is not None: data[ATTR_FORECAST_NATIVE_WIND_SPEED] = SpeedConverter.convert( - wind_speed, SPEED_MILES_PER_HOUR, SPEED_KILOMETERS_PER_HOUR + wind_speed, + UnitOfSpeed.MILES_PER_HOUR, + UnitOfSpeed.KILOMETERS_PER_HOUR, ) else: data[ATTR_FORECAST_NATIVE_WIND_SPEED] = None diff --git a/homeassistant/components/open_meteo/weather.py b/homeassistant/components/open_meteo/weather.py index 6af9900ec15..614aed6eefb 100644 --- a/homeassistant/components/open_meteo/weather.py +++ b/homeassistant/components/open_meteo/weather.py @@ -5,11 +5,7 @@ from open_meteo import Forecast as OpenMeteoForecast from homeassistant.components.weather import Forecast, WeatherEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - LENGTH_MILLIMETERS, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, -) +from homeassistant.const import UnitOfPrecipitationDepth, UnitOfSpeed, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo @@ -38,9 +34,9 @@ class OpenMeteoWeatherEntity( """Defines an Open-Meteo weather entity.""" _attr_has_entity_name = True - _attr_native_precipitation_unit = LENGTH_MILLIMETERS - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_wind_speed_unit = SPEED_KILOMETERS_PER_HOUR + _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR def __init__( self, diff --git a/homeassistant/components/openweathermap/weather.py b/homeassistant/components/openweathermap/weather.py index 814dc7dfd02..da29031d513 100644 --- a/homeassistant/components/openweathermap/weather.py +++ b/homeassistant/components/openweathermap/weather.py @@ -18,10 +18,10 @@ from homeassistant.components.weather import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - LENGTH_MILLIMETERS, - PRESSURE_HPA, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType @@ -89,10 +89,10 @@ class OpenWeatherMapWeather(WeatherEntity): _attr_attribution = ATTRIBUTION _attr_should_poll = False - _attr_native_precipitation_unit = LENGTH_MILLIMETERS - _attr_native_pressure_unit = PRESSURE_HPA - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_wind_speed_unit = SPEED_METERS_PER_SECOND + _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS + _attr_native_pressure_unit = UnitOfPressure.HPA + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND def __init__( self, diff --git a/homeassistant/components/smhi/weather.py b/homeassistant/components/smhi/weather.py index a02a627b7f2..12781df6891 100644 --- a/homeassistant/components/smhi/weather.py +++ b/homeassistant/components/smhi/weather.py @@ -45,11 +45,11 @@ from homeassistant.const import ( CONF_LOCATION, CONF_LONGITUDE, CONF_NAME, - LENGTH_KILOMETERS, - LENGTH_MILLIMETERS, - PRESSURE_HPA, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, + UnitOfLength, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import aiohttp_client @@ -121,11 +121,11 @@ class SmhiWeather(WeatherEntity): """Representation of a weather entity.""" _attr_attribution = "Swedish weather institute (SMHI)" - _attr_native_temperature_unit = TEMP_CELSIUS - _attr_native_visibility_unit = LENGTH_KILOMETERS - _attr_native_precipitation_unit = LENGTH_MILLIMETERS - _attr_native_wind_speed_unit = SPEED_METERS_PER_SECOND - _attr_native_pressure_unit = PRESSURE_HPA + _attr_native_temperature_unit = UnitOfTemperature.CELSIUS + _attr_native_visibility_unit = UnitOfLength.KILOMETERS + _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS + _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND + _attr_native_pressure_unit = UnitOfPressure.HPA _attr_has_entity_name = True @@ -158,7 +158,7 @@ class SmhiWeather(WeatherEntity): if self._forecasts: wind_gust = SpeedConverter.convert( self._forecasts[0].wind_gust, - SPEED_METERS_PER_SECOND, + UnitOfSpeed.METERS_PER_SECOND, self._wind_speed_unit, ) return { diff --git a/homeassistant/components/zamg/weather.py b/homeassistant/components/zamg/weather.py index b9d8ba67bbf..39c9d77f071 100644 --- a/homeassistant/components/zamg/weather.py +++ b/homeassistant/components/zamg/weather.py @@ -9,10 +9,10 @@ from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, - LENGTH_MILLIMETERS, - PRESSURE_HPA, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv @@ -83,10 +83,10 @@ class ZamgWeather(CoordinatorEntity, WeatherEntity): name=coordinator.name, ) # set units of ZAMG API - self._attr_native_temperature_unit = TEMP_CELSIUS - self._attr_native_pressure_unit = PRESSURE_HPA - self._attr_native_wind_speed_unit = SPEED_METERS_PER_SECOND - self._attr_native_precipitation_unit = LENGTH_MILLIMETERS + self._attr_native_temperature_unit = UnitOfTemperature.CELSIUS + self._attr_native_pressure_unit = UnitOfPressure.HPA + self._attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND + self._attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS @property def condition(self) -> str | None: From 4ab6f973411770e2c55e2737cf3d210783696869 Mon Sep 17 00:00:00 2001 From: Matrix Date: Wed, 30 Nov 2022 01:41:01 +0800 Subject: [PATCH 0875/1033] Expose mcu detected temperature & signal (#82905) --- homeassistant/components/yolink/sensor.py | 44 ++++++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/yolink/sensor.py b/homeassistant/components/yolink/sensor.py index 6a7c7ea4cff..e2e5fa2a94e 100644 --- a/homeassistant/components/yolink/sensor.py +++ b/homeassistant/components/yolink/sensor.py @@ -13,8 +13,13 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import ( + PERCENTAGE, + SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + UnitOfTemperature, +) from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util import percentage @@ -72,6 +77,12 @@ BATTERY_POWER_SENSOR = [ ATTR_DEVICE_CO_SMOKE_SENSOR, ] +MCU_DEV_TEMPERATURE_SENSOR = [ + ATTR_DEVICE_LEAK_SENSOR, + ATTR_DEVICE_MOTION_SENSOR, + ATTR_DEVICE_CO_SMOKE_SENSOR, +] + def cvt_battery(val: int | None) -> int | None: """Convert battery to percentage.""" @@ -103,11 +114,30 @@ SENSOR_TYPES: tuple[YoLinkSensorEntityDescription, ...] = ( YoLinkSensorEntityDescription( key="temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, name="Temperature", state_class=SensorStateClass.MEASUREMENT, exists_fn=lambda device: device.device_type in [ATTR_DEVICE_TH_SENSOR], ), + # mcu temperature + YoLinkSensorEntityDescription( + key="devTemperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + name="Temperature", + state_class=SensorStateClass.MEASUREMENT, + exists_fn=lambda device: device.device_type in MCU_DEV_TEMPERATURE_SENSOR, + ), + YoLinkSensorEntityDescription( + key="loraInfo", + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + name="Signal", + value=lambda value: value["signal"] if value is not None else None, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), ) @@ -161,7 +191,11 @@ class YoLinkSensorEntity(YoLinkEntity, SensorEntity): @callback def update_entity_state(self, state: dict) -> None: """Update HA Entity State.""" - self._attr_native_value = self.entity_description.value( - state.get(self.entity_description.key) - ) + if ( + attr_val := self.entity_description.value( + state.get(self.entity_description.key) + ) + ) is None and self.entity_description.key in ["devTemperature", "loraInfo"]: + return + self._attr_native_value = attr_val self.async_write_ha_state() From 10e7d6e04d6feac265d7398914dee9fe04102c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 29 Nov 2022 19:22:36 +0100 Subject: [PATCH 0876/1033] Add supervisor key to homassistant_alerts (#82862) --- .../components/analytics/analytics.py | 6 ++++-- homeassistant/components/hassio/__init__.py | 4 ++-- .../components/hassio/system_health.py | 6 +++--- .../homeassistant_alerts/__init__.py | 19 ++++++++++++++++++ .../fixtures/alerts_1.json | 20 +++++++++++++++++++ .../homeassistant_alerts/test_init.py | 16 ++++++++++++++- 6 files changed, 63 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/analytics/analytics.py b/homeassistant/components/analytics/analytics.py index 5bb1836928c..2e53d9c03d5 100644 --- a/homeassistant/components/analytics/analytics.py +++ b/homeassistant/components/analytics/analytics.py @@ -113,8 +113,10 @@ class Analytics: if stored: self._data = stored - if self.supervisor: - supervisor_info = hassio.get_supervisor_info(self.hass) + if ( + self.supervisor + and (supervisor_info := hassio.get_supervisor_info(self.hass)) is not None + ): if not self.onboarded: # User have not configured analytics, get this setting from the supervisor if supervisor_info[ATTR_DIAGNOSTICS] and not self.preferences.get( diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 467d1e878b9..581ed0e3292 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -271,7 +271,7 @@ def get_store(hass): @callback @bind_hass -def get_supervisor_info(hass): +def get_supervisor_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return Supervisor information. Async friendly. @@ -719,7 +719,7 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator): raise UpdateFailed(f"Error on Supervisor API: {err}") from err new_data: dict[str, Any] = {} - supervisor_info = get_supervisor_info(self.hass) + supervisor_info = get_supervisor_info(self.hass) or {} addons_info = get_addons_info(self.hass) addons_stats = get_addons_stats(self.hass) addons_changelogs = get_addons_changelogs(self.hass) diff --git a/homeassistant/components/hassio/system_health.py b/homeassistant/components/hassio/system_health.py index d8d29f44d68..795d1e325fb 100644 --- a/homeassistant/components/hassio/system_health.py +++ b/homeassistant/components/hassio/system_health.py @@ -27,7 +27,7 @@ async def system_health_info(hass: HomeAssistant): supervisor_info = get_supervisor_info(hass) healthy: bool | dict[str, str] - if supervisor_info.get("healthy"): + if supervisor_info is not None and supervisor_info.get("healthy"): healthy = True else: healthy = { @@ -36,7 +36,7 @@ async def system_health_info(hass: HomeAssistant): } supported: bool | dict[str, str] - if supervisor_info.get("supported"): + if supervisor_info is not None and supervisor_info.get("supported"): supported = True else: supported = { @@ -70,7 +70,7 @@ async def system_health_info(hass: HomeAssistant): information["installed_addons"] = ", ".join( f"{addon['name']} ({addon['version']})" - for addon in supervisor_info.get("addons", []) + for addon in (supervisor_info or {}).get("addons", []) ) return information diff --git a/homeassistant/components/homeassistant_alerts/__init__.py b/homeassistant/components/homeassistant_alerts/__init__.py index ed7957407a8..82631d58ec5 100644 --- a/homeassistant/components/homeassistant_alerts/__init__.py +++ b/homeassistant/components/homeassistant_alerts/__init__.py @@ -9,6 +9,7 @@ import logging import aiohttp from awesomeversion import AwesomeVersion, AwesomeVersionStrategy +from homeassistant.components.hassio import get_supervisor_info, is_hassio from homeassistant.const import __version__ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -142,6 +143,7 @@ class AlertUpdateCoordinator(DataUpdateCoordinator[dict[str, IntegrationAlert]]) __version__, ensure_strategy=AwesomeVersionStrategy.CALVER, ) + self.supervisor = is_hassio(self.hass) async def _async_update_data(self) -> dict[str, IntegrationAlert]: response = await async_get_clientsession(self.hass).get( @@ -170,6 +172,23 @@ class AlertUpdateCoordinator(DataUpdateCoordinator[dict[str, IntegrationAlert]]) if self.ha_version >= resolved_in_version: continue + if self.supervisor and "supervisor" in alert: + if (supervisor_info := get_supervisor_info(self.hass)) is None: + continue + + if "affected_from_version" in alert["supervisor"]: + affected_from_version = AwesomeVersion( + alert["supervisor"]["affected_from_version"], + ) + if supervisor_info["version"] < affected_from_version: + continue + if "resolved_in_version" in alert["supervisor"]: + resolved_in_version = AwesomeVersion( + alert["supervisor"]["resolved_in_version"], + ) + if supervisor_info["version"] >= resolved_in_version: + continue + for integration in alert["integrations"]: if "package" not in integration: continue diff --git a/tests/components/homeassistant_alerts/fixtures/alerts_1.json b/tests/components/homeassistant_alerts/fixtures/alerts_1.json index 381a31d7a5d..0f480f66b31 100644 --- a/tests/components/homeassistant_alerts/fixtures/alerts_1.json +++ b/tests/components/homeassistant_alerts/fixtures/alerts_1.json @@ -30,6 +30,26 @@ "filename": "dark_sky.markdown", "alert_url": "https://alerts.home-assistant.io/#dark_sky.markdown" }, + { + "title": "Supervisor November beta issue impacting users on Home Assistant beta/dev channels", + "created": "2022-11-16T06:00:00.000Z", + "integrations": [ + { + "package": "hassio" + } + ], + "homeassistant": { + "package": "homeassistant", + "affected_from_version": "0.41" + }, + "supervisor": { + "package": "supervisor", + "affected_from_version": "2022.11.0", + "resolved_in_version": "2022.11.1" + }, + "filename": "hassio.markdown", + "alert_url": "https://alerts.home-assistant.io/#hassio.markdown" + }, { "title": "Hikvision Security Vulnerability", "created": "2021-09-20T22:08:00.000Z", diff --git a/tests/components/homeassistant_alerts/test_init.py b/tests/components/homeassistant_alerts/test_init.py index 6b8cb7bf475..41fdff425b3 100644 --- a/tests/components/homeassistant_alerts/test_init.py +++ b/tests/components/homeassistant_alerts/test_init.py @@ -36,13 +36,15 @@ async def setup_repairs(hass): @pytest.mark.parametrize( - "ha_version, expected_alerts", + "ha_version, supervisor_info, expected_alerts", ( ( "2022.7.0", + {"version": "2022.11.0"}, [ ("aladdin_connect.markdown", "aladdin_connect"), ("dark_sky.markdown", "darksky"), + ("hassio.markdown", "hassio"), ("hikvision.markdown", "hikvision"), ("hikvision.markdown", "hikvisioncam"), ("hive_us.markdown", "hive"), @@ -56,6 +58,7 @@ async def setup_repairs(hass): ), ( "2022.8.0", + {"version": "2022.11.1"}, [ ("dark_sky.markdown", "darksky"), ("hikvision.markdown", "hikvision"), @@ -71,6 +74,7 @@ async def setup_repairs(hass): ), ( "2021.10.0", + None, [ ("aladdin_connect.markdown", "aladdin_connect"), ("dark_sky.markdown", "darksky"), @@ -91,6 +95,7 @@ async def test_alerts( hass_ws_client, aioclient_mock: AiohttpClientMocker, ha_version, + supervisor_info, expected_alerts, ) -> None: """Test creating issues based on alerts.""" @@ -119,9 +124,18 @@ async def test_alerts( for domain in activated_components: hass.config.components.add(domain) + if supervisor_info is not None: + hass.config.components.add("hassio") + with patch( "homeassistant.components.homeassistant_alerts.__version__", ha_version, + ), patch( + "homeassistant.components.homeassistant_alerts.is_hassio", + return_value=supervisor_info is not None, + ), patch( + "homeassistant.components.homeassistant_alerts.get_supervisor_info", + return_value=supervisor_info, ): assert await async_setup_component(hass, DOMAIN, {}) From 1c0f9cf941f77d6e3d299f98d5174f0a2953f236 Mon Sep 17 00:00:00 2001 From: Nyro Date: Tue, 29 Nov 2022 19:27:05 +0100 Subject: [PATCH 0877/1033] Add Overkiz Hitachi DHW (#81536) * Port ha-tahome hitachi dhw * Use int for setting temperature * Use value as float when possible * Use device state for current operation * Update homeassistant/components/overkiz/water_heater_entities/hitachi_dhw.py Co-authored-by: Quentame * Update homeassistant/components/overkiz/water_heater_entities/hitachi_dhw.py Co-authored-by: Quentame * Use ON instead of ECO for standard operation mode Co-authored-by: Quentame --- homeassistant/components/overkiz/const.py | 1 + .../overkiz/water_heater_entities/__init__.py | 2 + .../water_heater_entities/hitachi_dhw.py | 101 ++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 homeassistant/components/overkiz/water_heater_entities/hitachi_dhw.py diff --git a/homeassistant/components/overkiz/const.py b/homeassistant/components/overkiz/const.py index f207fc305e0..bf9ca3b343f 100644 --- a/homeassistant/components/overkiz/const.py +++ b/homeassistant/components/overkiz/const.py @@ -70,6 +70,7 @@ OVERKIZ_DEVICE_TO_PLATFORM: dict[UIClass | UIWidget, Platform | None] = { UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.ATLANTIC_PASS_APC_ZONE_CONTROL: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.DOMESTIC_HOT_WATER_TANK: Platform.SWITCH, # widgetName, uiClass is WaterHeatingSystem (not supported) + UIWidget.HITACHI_DHW: Platform.WATER_HEATER, # widgetName, uiClass is HitachiHeatingSystem (not supported) UIWidget.MY_FOX_ALARM_CONTROLLER: Platform.ALARM_CONTROL_PANEL, # widgetName, uiClass is Alarm (not supported) UIWidget.MY_FOX_SECURITY_CAMERA: Platform.SWITCH, # widgetName, uiClass is Camera (not supported) UIWidget.RTD_INDOOR_SIREN: Platform.SWITCH, # widgetName, uiClass is Siren (not supported) diff --git a/homeassistant/components/overkiz/water_heater_entities/__init__.py b/homeassistant/components/overkiz/water_heater_entities/__init__.py index e03585da56d..abf9db78116 100644 --- a/homeassistant/components/overkiz/water_heater_entities/__init__.py +++ b/homeassistant/components/overkiz/water_heater_entities/__init__.py @@ -2,7 +2,9 @@ from pyoverkiz.enums.ui import UIWidget from .atlantic_pass_apc_dhw import AtlanticPassAPCDHW +from .hitachi_dhw import HitachiDHW WIDGET_TO_WATER_HEATER_ENTITY = { UIWidget.ATLANTIC_PASS_APC_DHW: AtlanticPassAPCDHW, + UIWidget.HITACHI_DHW: HitachiDHW, } diff --git a/homeassistant/components/overkiz/water_heater_entities/hitachi_dhw.py b/homeassistant/components/overkiz/water_heater_entities/hitachi_dhw.py new file mode 100644 index 00000000000..fa8c128e0c1 --- /dev/null +++ b/homeassistant/components/overkiz/water_heater_entities/hitachi_dhw.py @@ -0,0 +1,101 @@ +"""Support for Hitachi DHW.""" +from __future__ import annotations + +from typing import Any, cast + +from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState + +from homeassistant.components.water_heater import ( + STATE_HIGH_DEMAND, + WaterHeaterEntity, + WaterHeaterEntityFeature, +) +from homeassistant.const import ( + ATTR_TEMPERATURE, + PRECISION_WHOLE, + STATE_OFF, + STATE_ON, + TEMP_CELSIUS, +) + +from ..entity import OverkizEntity + +OVERKIZ_TO_OPERATION_MODE: dict[str, str] = { + OverkizCommandParam.STANDARD: STATE_ON, + OverkizCommandParam.HIGH_DEMAND: STATE_HIGH_DEMAND, + OverkizCommandParam.STOP: STATE_OFF, +} + +OPERATION_MODE_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_OPERATION_MODE.items()} + + +class HitachiDHW(OverkizEntity, WaterHeaterEntity): + """Representation of Hitachi DHW.""" + + _attr_min_temp = 30.0 + _attr_max_temp = 70.0 + _attr_precision = PRECISION_WHOLE + + _attr_temperature_unit = TEMP_CELSIUS + _attr_supported_features = ( + WaterHeaterEntityFeature.TARGET_TEMPERATURE + | WaterHeaterEntityFeature.OPERATION_MODE + ) + _attr_operation_list = [*OPERATION_MODE_TO_OVERKIZ] + + @property + def current_temperature(self) -> float | None: + """Return the current temperature.""" + current_temperature = self.device.states[OverkizState.CORE_DHW_TEMPERATURE] + if current_temperature: + return current_temperature.value_as_float + return None + + @property + def target_temperature(self) -> float | None: + """Return the temperature we try to reach.""" + target_temperature = self.device.states[ + OverkizState.MODBUS_CONTROL_DHW_SETTING_TEMPERATURE + ] + if target_temperature: + return target_temperature.value_as_float + return None + + async def async_set_temperature(self, **kwargs: Any) -> None: + """Set new target temperature.""" + temperature = cast(float, kwargs.get(ATTR_TEMPERATURE)) + await self.executor.async_execute_command( + OverkizCommand.SET_CONTROL_DHW_SETTING_TEMPERATURE, int(temperature) + ) + + @property + def current_operation(self) -> str | None: + """Return current operation ie. eco, electric, performance, ...""" + modbus_control = self.device.states[OverkizState.MODBUS_CONTROL_DHW] + if modbus_control and modbus_control.value_as_str == OverkizCommandParam.STOP: + return STATE_OFF + + current_mode = self.device.states[OverkizState.MODBUS_DHW_MODE] + if current_mode and current_mode.value_as_str in OVERKIZ_TO_OPERATION_MODE: + return OVERKIZ_TO_OPERATION_MODE[current_mode.value_as_str] + + return None + + async def async_set_operation_mode(self, operation_mode: str) -> None: + """Set new target operation mode.""" + # Turn water heater off + if operation_mode == OverkizCommandParam.OFF: + return await self.executor.async_execute_command( + OverkizCommand.SET_CONTROL_DHW, OverkizCommandParam.STOP + ) + + # Turn water heater on, when off + if self.current_operation == OverkizCommandParam.OFF: + await self.executor.async_execute_command( + OverkizCommand.SET_CONTROL_DHW, OverkizCommandParam.ON + ) + + # Change operation mode + await self.executor.async_execute_command( + OverkizCommand.SET_DHW_MODE, OPERATION_MODE_TO_OVERKIZ[operation_mode] + ) From 573320a0b414704ff2e26f4b69faa84a51403c92 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 29 Nov 2022 19:52:49 +0100 Subject: [PATCH 0878/1033] Add country to detect location info (#82941) --- homeassistant/components/config/core.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/config/core.py b/homeassistant/components/config/core.py index 975cfc590e8..43bce39082d 100644 --- a/homeassistant/components/config/core.py +++ b/homeassistant/components/config/core.py @@ -110,4 +110,7 @@ async def websocket_detect_config( if location_info.currency: info["currency"] = location_info.currency + if location_info.country_code: + info["country"] = location_info.country_code + connection.send_result(msg["id"], info) From c36dd1778022defb11854532851ee5af10734313 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Nov 2022 20:23:57 +0100 Subject: [PATCH 0879/1033] Simplify use of binary sensor device classes in MySensors (#82946) --- homeassistant/components/mysensors/binary_sensor.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/mysensors/binary_sensor.py b/homeassistant/components/mysensors/binary_sensor.py index 07d03c3debd..50ecf70f8fd 100644 --- a/homeassistant/components/mysensors/binary_sensor.py +++ b/homeassistant/components/mysensors/binary_sensor.py @@ -2,7 +2,6 @@ from __future__ import annotations from homeassistant.components.binary_sensor import ( - DEVICE_CLASSES, BinarySensorDeviceClass, BinarySensorEntity, ) @@ -17,9 +16,9 @@ from .const import MYSENSORS_DISCOVERY, DiscoveryInfo from .helpers import on_unload SENSORS = { - "S_DOOR": "door", + "S_DOOR": BinarySensorDeviceClass.DOOR, "S_MOTION": BinarySensorDeviceClass.MOTION, - "S_SMOKE": "smoke", + "S_SMOKE": BinarySensorDeviceClass.SMOKE, "S_SPRINKLER": BinarySensorDeviceClass.SAFETY, "S_WATER_LEAK": BinarySensorDeviceClass.SAFETY, "S_SOUND": BinarySensorDeviceClass.SOUND, @@ -66,10 +65,7 @@ class MySensorsBinarySensor(mysensors.device.MySensorsEntity, BinarySensorEntity return self._values.get(self.value_type) == STATE_ON @property - def device_class(self) -> str | None: + def device_class(self) -> BinarySensorDeviceClass | None: """Return the class of this sensor, from DEVICE_CLASSES.""" pres = self.gateway.const.Presentation - device_class = SENSORS.get(pres(self.child_type).name) - if device_class in DEVICE_CLASSES: - return device_class - return None + return SENSORS.get(pres(self.child_type).name) From b4e30c4033f01c20e28989aaa685a1da4b839e2c Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Nov 2022 20:29:47 +0100 Subject: [PATCH 0880/1033] Use SensorDeviceClass enum in KNX (#82947) --- homeassistant/components/knx/sensor.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/knx/sensor.py b/homeassistant/components/knx/sensor.py index 20bbddf14a1..395b17e44a6 100644 --- a/homeassistant/components/knx/sensor.py +++ b/homeassistant/components/knx/sensor.py @@ -1,6 +1,7 @@ """Support for KNX/IP sensors.""" from __future__ import annotations +from contextlib import suppress from typing import Any from xknx import XKNX @@ -9,7 +10,7 @@ from xknx.devices import Sensor as XknxSensor from homeassistant import config_entries from homeassistant.components.sensor import ( CONF_STATE_CLASS, - DEVICE_CLASSES, + SensorDeviceClass, SensorEntity, ) from homeassistant.const import ( @@ -63,11 +64,10 @@ class KNXSensor(KnxEntity, SensorEntity): if device_class := config.get(CONF_DEVICE_CLASS): self._attr_device_class = device_class else: - self._attr_device_class = ( - self._device.ha_device_class() - if self._device.ha_device_class() in DEVICE_CLASSES - else None - ) + with suppress(ValueError): + self._attr_device_class = SensorDeviceClass( + str(self._device.ha_device_class()) + ) self._attr_force_update = self._device.always_callback self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_unique_id = str(self._device.sensor_value.group_address_state) From 2785b2b52f9630295bda4fd65eaa8cf56903908e Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 29 Nov 2022 20:31:05 +0100 Subject: [PATCH 0881/1033] Add mqtt text platform (#82884) --- .../components/mqtt/abbreviations.py | 1 + .../components/mqtt/config_integration.py | 4 + homeassistant/components/mqtt/const.py | 2 + homeassistant/components/mqtt/discovery.py | 1 + homeassistant/components/mqtt/text.py | 224 ++++++ tests/components/mqtt/test_text.py | 674 ++++++++++++++++++ 6 files changed, 906 insertions(+) create mode 100644 homeassistant/components/mqtt/text.py create mode 100644 tests/components/mqtt/test_text.py diff --git a/homeassistant/components/mqtt/abbreviations.py b/homeassistant/components/mqtt/abbreviations.py index 00f6d357553..5fe0da90feb 100644 --- a/homeassistant/components/mqtt/abbreviations.py +++ b/homeassistant/components/mqtt/abbreviations.py @@ -169,6 +169,7 @@ ABBREVIATIONS = { "pr_mode_stat_t": "preset_mode_state_topic", "pr_mode_val_tpl": "preset_mode_value_template", "pr_modes": "preset_modes", + "ptrn": "pattern", "r_tpl": "red_template", "rel_s": "release_summary", "rel_u": "release_url", diff --git a/homeassistant/components/mqtt/config_integration.py b/homeassistant/components/mqtt/config_integration.py index d487f1b382f..9db18af0718 100644 --- a/homeassistant/components/mqtt/config_integration.py +++ b/homeassistant/components/mqtt/config_integration.py @@ -32,6 +32,7 @@ from . import ( sensor as sensor_platform, siren as siren_platform, switch as switch_platform, + text as text_platform, update as update_platform, vacuum as vacuum_platform, ) @@ -130,6 +131,9 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( Platform.SWITCH.value: vol.All( cv.ensure_list, [switch_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type] ), + Platform.TEXT.value: vol.All( + cv.ensure_list, [text_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type] + ), Platform.UPDATE.value: vol.All( cv.ensure_list, [update_platform.PLATFORM_SCHEMA_MODERN] # type: ignore[has-type] ), diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index 7b4c7378b9e..f7fa93a36d0 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -101,6 +101,7 @@ PLATFORMS = [ Platform.SENSOR, Platform.SIREN, Platform.SWITCH, + Platform.TEXT, Platform.UPDATE, Platform.VACUUM, ] @@ -122,6 +123,7 @@ RELOADABLE_PLATFORMS = [ Platform.SENSOR, Platform.SIREN, Platform.SWITCH, + Platform.TEXT, Platform.UPDATE, Platform.VACUUM, ] diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index 9907de18ee9..2d460a69592 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -63,6 +63,7 @@ SUPPORTED_COMPONENTS = [ "sensor", "switch", "tag", + "text", "update", "vacuum", ] diff --git a/homeassistant/components/mqtt/text.py b/homeassistant/components/mqtt/text.py new file mode 100644 index 00000000000..032dba66719 --- /dev/null +++ b/homeassistant/components/mqtt/text.py @@ -0,0 +1,224 @@ +"""Support for MQTT text platform.""" +from __future__ import annotations + +from collections.abc import Callable +import functools +import logging +import re +from typing import Any + +import voluptuous as vol + +from homeassistant.components import text +from homeassistant.components.text import TextEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + CONF_MODE, + CONF_NAME, + CONF_OPTIMISTIC, + CONF_VALUE_TEMPLATE, + MAX_LENGTH_STATE_STATE, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType + +from . import subscription +from .config import MQTT_RW_SCHEMA +from .const import ( + CONF_COMMAND_TEMPLATE, + CONF_COMMAND_TOPIC, + CONF_ENCODING, + CONF_QOS, + CONF_RETAIN, + CONF_STATE_TOPIC, +) +from .debug_info import log_messages +from .mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from .models import ( + MqttCommandTemplate, + MqttValueTemplate, + PublishPayloadType, + ReceiveMessage, + ReceivePayloadType, +) +from .util import get_mqtt_data + +_LOGGER = logging.getLogger(__name__) + +CONF_MAX = "max" +CONF_MIN = "min" +CONF_PATTERN = "pattern" + +DEFAULT_NAME = "MQTT Text" +DEFAULT_OPTIMISTIC = False +DEFAULT_PAYLOAD_RESET = "None" + +MQTT_TEXT_ATTRIBUTES_BLOCKED = frozenset( + { + text.ATTR_MAX, + text.ATTR_MIN, + text.ATTR_MODE, + text.ATTR_PATTERN, + } +) + + +def valid_text_size_configuration(config: ConfigType) -> ConfigType: + """Validate that the text length configuration is valid, throws if it isn't.""" + if config[CONF_MIN] >= config[CONF_MAX]: + raise ValueError("text length min must be >= max") + if config[CONF_MAX] > MAX_LENGTH_STATE_STATE: + raise ValueError(f"max text length must be <= {MAX_LENGTH_STATE_STATE}") + + return config + + +_PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend( + { + vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_MAX, default=MAX_LENGTH_STATE_STATE): cv.positive_int, + vol.Optional(CONF_MIN, default=0): cv.positive_int, + vol.Optional(CONF_MODE, default=text.TextMode.TEXT): vol.In( + [text.TextMode.TEXT, text.TextMode.PASSWORD] + ), + vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, + vol.Optional(CONF_PATTERN): cv.is_regex, + vol.Optional(CONF_VALUE_TEMPLATE): cv.template, + }, +).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) + + +DISCOVERY_SCHEMA = vol.All( + _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), + valid_text_size_configuration, +) + +PLATFORM_SCHEMA_MODERN = vol.All(_PLATFORM_SCHEMA_BASE, valid_text_size_configuration) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up MQTT text through configuration.yaml and dynamically through MQTT discovery.""" + setup = functools.partial( + _async_setup_entity, hass, async_add_entities, config_entry=config_entry + ) + await async_setup_entry_helper(hass, text.DOMAIN, setup, DISCOVERY_SCHEMA) + + +async def _async_setup_entity( + hass: HomeAssistant, + async_add_entities: AddEntitiesCallback, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, +) -> None: + """Set up the MQTT text.""" + async_add_entities([MqttTextEntity(hass, config, config_entry, discovery_data)]) + + +class MqttTextEntity(MqttEntity, TextEntity): + """Representation of the MQTT text entity.""" + + _attributes_extra_blocked = MQTT_TEXT_ATTRIBUTES_BLOCKED + _entity_id_format = text.ENTITY_ID_FORMAT + + _compiled_pattern: re.Pattern[Any] | None + _optimistic: bool + _command_template: Callable[[PublishPayloadType], PublishPayloadType] + _value_template: Callable[[ReceivePayloadType], ReceivePayloadType] + + def __init__( + self, + hass: HomeAssistant, + config: ConfigType, + config_entry: ConfigEntry, + discovery_data: DiscoveryInfoType | None = None, + ) -> None: + """Initialize MQTT text entity.""" + self._attr_native_value = None + MqttEntity.__init__(self, hass, config, config_entry, discovery_data) + + @staticmethod + def config_schema() -> vol.Schema: + """Return the config schema.""" + return DISCOVERY_SCHEMA + + def _setup_from_config(self, config: ConfigType) -> None: + """(Re)Setup the entity.""" + self._attr_native_max = config[CONF_MAX] + self._attr_native_min = config[CONF_MIN] + self._attr_mode = config[CONF_MODE] + self._compiled_pattern = config.get(CONF_PATTERN) + self._attr_pattern = ( + self._compiled_pattern.pattern if self._compiled_pattern else None + ) + + self._command_template = MqttCommandTemplate( + config.get(CONF_COMMAND_TEMPLATE), + entity=self, + ).async_render + self._value_template = MqttValueTemplate( + config.get(CONF_VALUE_TEMPLATE), + entity=self, + ).async_render_with_possible_json_value + optimistic: bool = config[CONF_OPTIMISTIC] + self._optimistic = optimistic or config.get(CONF_STATE_TOPIC) is None + + def _prepare_subscribe_topics(self) -> None: + """(Re)Subscribe to topics.""" + topics: dict[str, Any] = {} + + def add_subscription( + topics: dict[str, Any], topic: str, msg_callback: Callable + ) -> None: + if self._config.get(topic) is not None: + topics[topic] = { + "topic": self._config[topic], + "msg_callback": msg_callback, + "qos": self._config[CONF_QOS], + "encoding": self._config[CONF_ENCODING] or None, + } + + @callback + @log_messages(self.hass, self.entity_id) + def handle_state_message_received(msg: ReceiveMessage) -> None: + """Handle receiving state message via MQTT.""" + payload = str(self._value_template(msg.payload)) + self._attr_native_value = payload + get_mqtt_data(self.hass).state_write_requests.write_state_request(self) + + add_subscription(topics, CONF_STATE_TOPIC, handle_state_message_received) + + self._sub_state = subscription.async_prepare_subscribe_topics( + self.hass, self._sub_state, topics + ) + + async def _subscribe_topics(self) -> None: + """(Re)Subscribe to topics.""" + await subscription.async_subscribe_topics(self.hass, self._sub_state) + + @property + def assumed_state(self) -> bool: + """Return true if we do optimistic updates.""" + return self._optimistic + + async def async_set_value(self, value: str) -> None: + """Change the text.""" + payload = self._command_template(value) + + await self.async_publish( + self._config[CONF_COMMAND_TOPIC], + payload, + self._config[CONF_QOS], + self._config[CONF_RETAIN], + self._config[CONF_ENCODING], + ) + if self._optimistic: + self._attr_native_value = value + self.async_write_ha_state() diff --git a/tests/components/mqtt/test_text.py b/tests/components/mqtt/test_text.py new file mode 100644 index 00000000000..6b4680bd030 --- /dev/null +++ b/tests/components/mqtt/test_text.py @@ -0,0 +1,674 @@ +"""The tests for the MQTT text platform.""" +from __future__ import annotations + +from unittest.mock import patch + +import pytest + +from homeassistant.components import mqtt, text +from homeassistant.const import ( + ATTR_ASSUMED_STATE, + ATTR_ENTITY_ID, + STATE_UNKNOWN, + Platform, +) +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from .test_common import ( + help_test_availability_when_connection_lost, + help_test_availability_without_topic, + help_test_custom_availability_payload, + help_test_default_availability_payload, + help_test_discovery_broken, + help_test_discovery_removal, + help_test_discovery_update, + help_test_discovery_update_attr, + help_test_discovery_update_unchanged, + help_test_encoding_subscribable_topics, + help_test_entity_debug_info_message, + help_test_entity_device_info_remove, + help_test_entity_device_info_update, + help_test_entity_device_info_with_connection, + help_test_entity_device_info_with_identifier, + help_test_entity_id_update_discovery_update, + help_test_entity_id_update_subscriptions, + help_test_publishing_with_custom_encoding, + help_test_reloadable, + help_test_setting_attribute_via_mqtt_json_message, + help_test_setting_attribute_with_template, + help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, + help_test_unique_id, + help_test_unload_config_entry_with_platform, + help_test_update_with_json_attrs_bad_json, + help_test_update_with_json_attrs_not_dict, +) + +from tests.common import async_fire_mqtt_message + +DEFAULT_CONFIG = { + mqtt.DOMAIN: {text.DOMAIN: {"name": "test", "command_topic": "test-topic"}} +} + + +@pytest.fixture(autouse=True) +def text_platform_only(): + """Only setup the text platform to speed up tests.""" + with patch("homeassistant.components.mqtt.PLATFORMS", [Platform.TEXT]): + yield + + +async def async_set_value( + hass: HomeAssistant, entity_id: str, value: str | None +) -> None: + """Set input_text to value.""" + await hass.services.async_call( + text.DOMAIN, + text.SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: entity_id, text.ATTR_VALUE: value}, + blocking=True, + ) + + +async def test_controlling_state_via_topic( + hass: HomeAssistant, mqtt_mock_entry_with_yaml_config +) -> None: + """Test the controlling state via topic.""" + assert await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + text.DOMAIN: { + "name": "test", + "state_topic": "state-topic", + "command_topic": "command-topic", + "mode": "password", + } + } + }, + ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + + state = hass.states.get("text.test") + assert state.state == STATE_UNKNOWN + assert state.attributes[text.ATTR_MODE] == "password" + assert not state.attributes.get(ATTR_ASSUMED_STATE) + + async_fire_mqtt_message(hass, "state-topic", "some state") + + state = hass.states.get("text.test") + assert state.state == "some state" + + async_fire_mqtt_message(hass, "state-topic", "some other state") + + state = hass.states.get("text.test") + assert state.state == "some other state" + + async_fire_mqtt_message(hass, "state-topic", "") + + state = hass.states.get("text.test") + assert state.state == "" + + +async def test_controlling_validation_state_via_topic( + hass, mqtt_mock_entry_with_yaml_config +) -> None: + """Test the validation of a received state.""" + assert await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + text.DOMAIN: { + "name": "test", + "state_topic": "state-topic", + "command_topic": "command-topic", + "mode": "text", + "min": 2, + "max": 10, + "pattern": "(y|n)", + } + } + }, + ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + + state = hass.states.get("text.test") + assert state.state == STATE_UNKNOWN + assert state.attributes[text.ATTR_MODE] == "text" + + async_fire_mqtt_message(hass, "state-topic", "yes") + state = hass.states.get("text.test") + assert state.state == "yes" + + # test pattern error + with pytest.raises(ValueError): + async_fire_mqtt_message(hass, "state-topic", "other") + await hass.async_block_till_done() + state = hass.states.get("text.test") + assert state.state == "yes" + + # test text size to large + with pytest.raises(ValueError): + async_fire_mqtt_message(hass, "state-topic", "yesyesyesyes") + await hass.async_block_till_done() + state = hass.states.get("text.test") + assert state.state == "yes" + + # test text size to small + with pytest.raises(ValueError): + async_fire_mqtt_message(hass, "state-topic", "y") + await hass.async_block_till_done() + state = hass.states.get("text.test") + assert state.state == "yes" + + async_fire_mqtt_message(hass, "state-topic", "no") + await hass.async_block_till_done() + state = hass.states.get("text.test") + assert state.state == "no" + + +async def test_attribute_validation_max_greater_then_min(hass) -> None: + """Test the validation of min and max configuration attributes.""" + assert not await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + text.DOMAIN: { + "name": "test", + "command_topic": "command-topic", + "min": 20, + "max": 10, + } + } + }, + ) + + +async def test_attribute_validation_max_not_greater_then_max_state_length(hass) -> None: + """Test the max value of of max configuration attribute.""" + assert not await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + text.DOMAIN: { + "name": "test", + "command_topic": "command-topic", + "min": 20, + "max": 257, + } + } + }, + ) + + +async def test_sending_mqtt_commands_and_optimistic( + hass, mqtt_mock_entry_with_yaml_config +): + """Test the sending MQTT commands in optimistic mode.""" + assert await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + text.DOMAIN: { + "name": "test", + "command_topic": "command-topic", + "qos": "2", + } + } + }, + ) + await hass.async_block_till_done() + mqtt_mock = await mqtt_mock_entry_with_yaml_config() + + state = hass.states.get("text.test") + assert state.state == STATE_UNKNOWN + assert state.attributes.get(ATTR_ASSUMED_STATE) + + await async_set_value(hass, "text.test", "some other state") + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + "command-topic", "some other state", 2, False + ) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get("text.test") + assert state.state == "some other state" + + await async_set_value(hass, "text.test", "some new state") + + mqtt_mock.async_publish.assert_called_once_with( + "command-topic", "some new state", 2, False + ) + state = hass.states.get("text.test") + assert state.state == "some new state" + + +async def test_set_text_validation(hass, mqtt_mock_entry_with_yaml_config): + """Test the initial state in optimistic mode.""" + assert await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + text.DOMAIN: { + "name": "test", + "command_topic": "command-topic", + "mode": "text", + "min": 2, + "max": 10, + "pattern": "(y|n)", + } + } + }, + ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + + state = hass.states.get("text.test") + assert state.state == STATE_UNKNOWN + assert state.attributes.get(ATTR_ASSUMED_STATE) + + # text too long + with pytest.raises(ValueError): + await async_set_value(hass, "text.test", "yes yes yes yes") + + # text too short + with pytest.raises(ValueError): + await async_set_value(hass, "text.test", "y") + + # text not matching pattern + with pytest.raises(ValueError): + await async_set_value(hass, "text.test", "other") + + await async_set_value(hass, "text.test", "no") + state = hass.states.get("text.test") + assert state.state == "no" + + +async def test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config +): + """Test availability after MQTT disconnection.""" + await help_test_availability_when_connection_lost( + hass, mqtt_mock_entry_with_yaml_config, text.DOMAIN, DEFAULT_CONFIG + ) + + +async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config): + """Test availability without defined availability topic.""" + await help_test_availability_without_topic( + hass, mqtt_mock_entry_with_yaml_config, text.DOMAIN, DEFAULT_CONFIG + ) + + +async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config): + """Test availability by default payload with defined topic.""" + config = { + mqtt.DOMAIN: { + text.DOMAIN: { + "name": "test", + "state_topic": "state-topic", + "command_topic": "command-topic", + } + } + } + + await help_test_default_availability_payload( + hass, + mqtt_mock_entry_with_yaml_config, + text.DOMAIN, + config, + True, + "state-topic", + "some state", + ) + + +async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config): + """Test availability by custom payload with defined topic.""" + config = { + mqtt.DOMAIN: { + text.DOMAIN: { + "name": "test", + "state_topic": "state-topic", + "command_topic": "command-topic", + } + } + } + + await help_test_custom_availability_payload( + hass, + mqtt_mock_entry_with_yaml_config, + text.DOMAIN, + config, + True, + "state-topic", + "1", + ) + + +async def test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config +): + """Test the setting of attribute via MQTT with JSON payload.""" + await help_test_setting_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_with_yaml_config, text.DOMAIN, DEFAULT_CONFIG + ) + + +async def test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config +): + """Test the setting of attribute via MQTT with JSON payload.""" + await help_test_setting_blocked_attribute_via_mqtt_json_message( + hass, mqtt_mock_entry_no_yaml_config, text.DOMAIN, DEFAULT_CONFIG, {} + ) + + +async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config): + """Test the setting of attribute via MQTT with JSON payload.""" + await help_test_setting_attribute_with_template( + hass, mqtt_mock_entry_with_yaml_config, text.DOMAIN, DEFAULT_CONFIG + ) + + +async def test_update_with_json_attrs_not_dict( + hass, mqtt_mock_entry_with_yaml_config, caplog +): + """Test attributes get extracted from a JSON result.""" + await help_test_update_with_json_attrs_not_dict( + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + text.DOMAIN, + DEFAULT_CONFIG, + ) + + +async def test_update_with_json_attrs_bad_json( + hass, mqtt_mock_entry_with_yaml_config, caplog +): + """Test attributes get extracted from a JSON result.""" + await help_test_update_with_json_attrs_bad_json( + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + text.DOMAIN, + DEFAULT_CONFIG, + ) + + +async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog): + """Test update of discovered MQTTAttributes.""" + await help_test_discovery_update_attr( + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + text.DOMAIN, + DEFAULT_CONFIG, + ) + + +async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config): + """Test unique id option only creates one text per unique_id.""" + config = { + mqtt.DOMAIN: { + text.DOMAIN: [ + { + "name": "Test 1", + "state_topic": "test-topic", + "command_topic": "command-topic", + "unique_id": "TOTALLY_UNIQUE", + }, + { + "name": "Test 2", + "state_topic": "test-topic", + "command_topic": "command-topic", + "unique_id": "TOTALLY_UNIQUE", + }, + ] + } + } + await help_test_unique_id( + hass, mqtt_mock_entry_with_yaml_config, text.DOMAIN, config + ) + + +async def test_discovery_removal_text(hass, mqtt_mock_entry_no_yaml_config, caplog): + """Test removal of discovered text entity.""" + data = ( + '{ "name": "test",' + ' "state_topic": "test_topic",' + ' "command_topic": "test_topic" }' + ) + await help_test_discovery_removal( + hass, mqtt_mock_entry_no_yaml_config, caplog, text.DOMAIN, data + ) + + +async def test_discovery_text_update(hass, mqtt_mock_entry_no_yaml_config, caplog): + """Test update of discovered text entity.""" + config1 = { + "name": "Beer", + "command_topic": "command-topic", + "state_topic": "state-topic", + } + config2 = { + "name": "Milk", + "command_topic": "command-topic", + "state_topic": "state-topic", + } + + await help_test_discovery_update( + hass, mqtt_mock_entry_no_yaml_config, caplog, text.DOMAIN, config1, config2 + ) + + +async def test_discovery_update_unchanged_update( + hass, mqtt_mock_entry_no_yaml_config, caplog +): + """Test update of discovered update.""" + data1 = '{ "name": "Beer", "state_topic": "text-topic", "command_topic": "command-topic"}' + with patch( + "homeassistant.components.mqtt.text.MqttTextEntity.discovery_update" + ) as discovery_update: + await help_test_discovery_update_unchanged( + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + text.DOMAIN, + data1, + discovery_update, + ) + + +async def test_discovery_update_text(hass, mqtt_mock_entry_no_yaml_config, caplog): + """Test update of discovered text entity.""" + config1 = {"name": "Beer", "command_topic": "cmd-topic1"} + config2 = {"name": "Milk", "command_topic": "cmd-topic2"} + await help_test_discovery_update( + hass, mqtt_mock_entry_no_yaml_config, caplog, text.DOMAIN, config1, config2 + ) + + +async def test_discovery_update_unchanged_climate( + hass, mqtt_mock_entry_no_yaml_config, caplog +): + """Test update of discovered text entity.""" + data1 = '{ "name": "Beer", "command_topic": "cmd-topic" }' + with patch( + "homeassistant.components.mqtt.text.MqttTextEntity.discovery_update" + ) as discovery_update: + await help_test_discovery_update_unchanged( + hass, + mqtt_mock_entry_no_yaml_config, + caplog, + text.DOMAIN, + data1, + discovery_update, + ) + + +@pytest.mark.no_fail_on_log_exception +async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog): + """Test handling of bad discovery message.""" + data1 = '{ "name": "Beer" }' + data2 = ( + '{ "name": "Milk",' + ' "state_topic": "test_topic",' + ' "command_topic": "test_topic" }' + ) + await help_test_discovery_broken( + hass, mqtt_mock_entry_no_yaml_config, caplog, text.DOMAIN, data1, data2 + ) + + +async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config): + """Test MQTT text device registry integration.""" + await help_test_entity_device_info_with_connection( + hass, mqtt_mock_entry_no_yaml_config, text.DOMAIN, DEFAULT_CONFIG + ) + + +async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config): + """Test MQTT text device registry integration.""" + await help_test_entity_device_info_with_identifier( + hass, mqtt_mock_entry_no_yaml_config, text.DOMAIN, DEFAULT_CONFIG + ) + + +async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config): + """Test device registry update.""" + await help_test_entity_device_info_update( + hass, mqtt_mock_entry_no_yaml_config, text.DOMAIN, DEFAULT_CONFIG + ) + + +async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config): + """Test device registry remove.""" + await help_test_entity_device_info_remove( + hass, mqtt_mock_entry_no_yaml_config, text.DOMAIN, DEFAULT_CONFIG + ) + + +async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config): + """Test MQTT subscriptions are managed when entity_id is updated.""" + await help_test_entity_id_update_subscriptions( + hass, mqtt_mock_entry_with_yaml_config, text.DOMAIN, DEFAULT_CONFIG + ) + + +async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config): + """Test MQTT discovery update when entity_id is updated.""" + await help_test_entity_id_update_discovery_update( + hass, mqtt_mock_entry_no_yaml_config, text.DOMAIN, DEFAULT_CONFIG + ) + + +async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config): + """Test MQTT debug info.""" + await help_test_entity_debug_info_message( + hass, mqtt_mock_entry_no_yaml_config, text.DOMAIN, DEFAULT_CONFIG, None + ) + + +@pytest.mark.parametrize( + "service,topic,parameters,payload,template", + [ + ( + text.SERVICE_SET_VALUE, + "command_topic", + {text.ATTR_VALUE: "some text"}, + "some text", + "command_template", + ), + ], +) +async def test_publishing_with_custom_encoding( + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + service, + topic, + parameters, + payload, + template, +): + """Test publishing MQTT payload with different encoding.""" + domain = text.DOMAIN + config = DEFAULT_CONFIG + + await help_test_publishing_with_custom_encoding( + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + domain, + config, + service, + topic, + parameters, + payload, + template, + ) + + +async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path): + """Test reloading the MQTT platform.""" + domain = text.DOMAIN + config = DEFAULT_CONFIG + await help_test_reloadable( + hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config + ) + + +@pytest.mark.parametrize( + "topic,value,attribute,attribute_value", + [ + ("state_topic", "some text", None, "some text"), + ], +) +async def test_encoding_subscribable_topics( + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + topic, + value, + attribute, + attribute_value, +): + """Test handling of incoming encoded payload.""" + await help_test_encoding_subscribable_topics( + hass, + mqtt_mock_entry_with_yaml_config, + caplog, + text.DOMAIN, + DEFAULT_CONFIG[mqtt.DOMAIN][text.DOMAIN], + topic, + value, + attribute, + attribute_value, + ) + + +async def test_setup_manual_entity_from_yaml(hass): + """Test setup manual configured MQTT entity.""" + platform = text.DOMAIN + await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG) + assert hass.states.get(f"{platform}.test") + + +async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): + """Test unloading the config entry.""" + domain = text.DOMAIN + config = DEFAULT_CONFIG + await help_test_unload_config_entry_with_platform( + hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config + ) From 368694d6bfb3d96a83f6209754be5d4360bff1cd Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Nov 2022 20:31:25 +0100 Subject: [PATCH 0882/1033] Collection of DeviceClass related typing fixes (#82931) --- .../components/command_line/binary_sensor.py | 5 ++-- .../components/fibaro/binary_sensor.py | 6 +++-- homeassistant/components/geniushub/sensor.py | 2 +- .../components/group/binary_sensor.py | 5 ++-- .../homematicip_cloud/binary_sensor.py | 24 +++++++++---------- .../components/homematicip_cloud/sensor.py | 16 ++++++------- homeassistant/components/huisbaasje/sensor.py | 10 +++++--- homeassistant/components/iaqualink/sensor.py | 2 +- homeassistant/components/neato/sensor.py | 2 +- .../components/nina/binary_sensor.py | 10 ++++---- .../components/simplisafe/binary_sensor.py | 2 +- .../components/switcher_kis/sensor.py | 4 ++-- homeassistant/components/vera/sensor.py | 2 +- 13 files changed, 49 insertions(+), 41 deletions(-) diff --git a/homeassistant/components/command_line/binary_sensor.py b/homeassistant/components/command_line/binary_sensor.py index b2c8b29478a..f4a3a29f29f 100644 --- a/homeassistant/components/command_line/binary_sensor.py +++ b/homeassistant/components/command_line/binary_sensor.py @@ -8,6 +8,7 @@ import voluptuous as vol from homeassistant.components.binary_sensor import ( DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA, + BinarySensorDeviceClass, BinarySensorEntity, ) from homeassistant.const import ( @@ -64,7 +65,7 @@ def setup_platform( command: str = config[CONF_COMMAND] payload_off: str = config[CONF_PAYLOAD_OFF] payload_on: str = config[CONF_PAYLOAD_ON] - device_class: str | None = config.get(CONF_DEVICE_CLASS) + device_class: BinarySensorDeviceClass | None = config.get(CONF_DEVICE_CLASS) value_template: Template | None = config.get(CONF_VALUE_TEMPLATE) command_timeout: int = config[CONF_COMMAND_TIMEOUT] unique_id: str | None = config.get(CONF_UNIQUE_ID) @@ -95,7 +96,7 @@ class CommandBinarySensor(BinarySensorEntity): self, data: CommandSensorData, name: str, - device_class: str | None, + device_class: BinarySensorDeviceClass | None, payload_on: str, payload_off: str, value_template: Template | None, diff --git a/homeassistant/components/fibaro/binary_sensor.py b/homeassistant/components/fibaro/binary_sensor.py index f9baa33c41f..7954334689d 100644 --- a/homeassistant/components/fibaro/binary_sensor.py +++ b/homeassistant/components/fibaro/binary_sensor.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Mapping import json -from typing import Any +from typing import Any, cast from homeassistant.components.binary_sensor import ( ENTITY_ID_FORMAT, @@ -69,7 +69,9 @@ class FibaroBinarySensor(FibaroDevice, BinarySensorEntity): elif fibaro_device.baseType in SENSOR_TYPES: self._fibaro_sensor_type = fibaro_device.baseType if self._fibaro_sensor_type: - self._attr_device_class = SENSOR_TYPES[self._fibaro_sensor_type][2] + self._attr_device_class = cast( + BinarySensorDeviceClass, SENSOR_TYPES[self._fibaro_sensor_type][2] + ) self._attr_icon = SENSOR_TYPES[self._fibaro_sensor_type][1] @property diff --git a/homeassistant/components/geniushub/sensor.py b/homeassistant/components/geniushub/sensor.py index 20acb533a2c..06237b6e8d5 100644 --- a/homeassistant/components/geniushub/sensor.py +++ b/homeassistant/components/geniushub/sensor.py @@ -81,7 +81,7 @@ class GeniusBattery(GeniusDevice, SensorEntity): return icon @property - def device_class(self) -> str: + def device_class(self) -> SensorDeviceClass: """Return the device class of the sensor.""" return SensorDeviceClass.BATTERY diff --git a/homeassistant/components/group/binary_sensor.py b/homeassistant/components/group/binary_sensor.py index 473a5a5e885..815e3b76f0b 100644 --- a/homeassistant/components/group/binary_sensor.py +++ b/homeassistant/components/group/binary_sensor.py @@ -7,6 +7,7 @@ from homeassistant.components.binary_sensor import ( DEVICE_CLASSES_SCHEMA, DOMAIN as BINARY_SENSOR_DOMAIN, PLATFORM_SCHEMA, + BinarySensorDeviceClass, BinarySensorEntity, ) from homeassistant.config_entries import ConfigEntry @@ -94,7 +95,7 @@ class BinarySensorGroup(GroupEntity, BinarySensorEntity): self, unique_id: str | None, name: str, - device_class: str | None, + device_class: BinarySensorDeviceClass | None, entity_ids: list[str], mode: str | None, ) -> None: @@ -149,6 +150,6 @@ class BinarySensorGroup(GroupEntity, BinarySensorEntity): self._attr_is_on = self.mode(state == STATE_ON for state in states) @property - def device_class(self) -> str | None: + def device_class(self) -> BinarySensorDeviceClass | None: """Return the sensor class of the binary sensor.""" return self._device_class diff --git a/homeassistant/components/homematicip_cloud/binary_sensor.py b/homeassistant/components/homematicip_cloud/binary_sensor.py index db35a5d3ee5..fb4bfdd637e 100644 --- a/homeassistant/components/homematicip_cloud/binary_sensor.py +++ b/homeassistant/components/homematicip_cloud/binary_sensor.py @@ -195,7 +195,7 @@ class HomematicipBaseActionSensor(HomematicipGenericEntity, BinarySensorEntity): """Representation of the HomematicIP base action sensor.""" @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Return the class of this sensor.""" return BinarySensorDeviceClass.MOVING @@ -240,7 +240,7 @@ class HomematicipMultiContactInterface(HomematicipGenericEntity, BinarySensorEnt ) @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Return the class of this sensor.""" return BinarySensorDeviceClass.OPENING @@ -274,7 +274,7 @@ class HomematicipShutterContact(HomematicipMultiContactInterface, BinarySensorEn self.has_additional_state = has_additional_state @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Return the class of this sensor.""" return BinarySensorDeviceClass.DOOR @@ -295,7 +295,7 @@ class HomematicipMotionDetector(HomematicipGenericEntity, BinarySensorEntity): """Representation of the HomematicIP motion detector.""" @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Return the class of this sensor.""" return BinarySensorDeviceClass.MOTION @@ -309,7 +309,7 @@ class HomematicipPresenceDetector(HomematicipGenericEntity, BinarySensorEntity): """Representation of the HomematicIP presence detector.""" @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Return the class of this sensor.""" return BinarySensorDeviceClass.PRESENCE @@ -323,7 +323,7 @@ class HomematicipSmokeDetector(HomematicipGenericEntity, BinarySensorEntity): """Representation of the HomematicIP smoke detector.""" @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Return the class of this sensor.""" return BinarySensorDeviceClass.SMOKE @@ -342,7 +342,7 @@ class HomematicipWaterDetector(HomematicipGenericEntity, BinarySensorEntity): """Representation of the HomematicIP water detector.""" @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Return the class of this sensor.""" return BinarySensorDeviceClass.MOISTURE @@ -378,7 +378,7 @@ class HomematicipRainSensor(HomematicipGenericEntity, BinarySensorEntity): super().__init__(hap, device, "Raining") @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Return the class of this sensor.""" return BinarySensorDeviceClass.MOISTURE @@ -396,7 +396,7 @@ class HomematicipSunshineSensor(HomematicipGenericEntity, BinarySensorEntity): super().__init__(hap, device, post="Sunshine") @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Return the class of this sensor.""" return BinarySensorDeviceClass.LIGHT @@ -425,7 +425,7 @@ class HomematicipBatterySensor(HomematicipGenericEntity, BinarySensorEntity): super().__init__(hap, device, post="Battery") @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Return the class of this sensor.""" return BinarySensorDeviceClass.BATTERY @@ -445,7 +445,7 @@ class HomematicipPluggableMainsFailureSurveillanceSensor( super().__init__(hap, device) @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Return the class of this sensor.""" return BinarySensorDeviceClass.POWER @@ -464,7 +464,7 @@ class HomematicipSecurityZoneSensorGroup(HomematicipGenericEntity, BinarySensorE super().__init__(hap, device, post=post) @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass: """Return the class of this sensor.""" return BinarySensorDeviceClass.SAFETY diff --git a/homeassistant/components/homematicip_cloud/sensor.py b/homeassistant/components/homematicip_cloud/sensor.py index 9da0bb37ed4..fdf125dbfec 100644 --- a/homeassistant/components/homematicip_cloud/sensor.py +++ b/homeassistant/components/homematicip_cloud/sensor.py @@ -205,7 +205,7 @@ class HomematicipHumiditySensor(HomematicipGenericEntity, SensorEntity): super().__init__(hap, device, post="Humidity") @property - def device_class(self) -> str: + def device_class(self) -> SensorDeviceClass: """Return the device class of the sensor.""" return SensorDeviceClass.HUMIDITY @@ -230,7 +230,7 @@ class HomematicipTemperatureSensor(HomematicipGenericEntity, SensorEntity): super().__init__(hap, device, post="Temperature") @property - def device_class(self) -> str: + def device_class(self) -> SensorDeviceClass: """Return the device class of the sensor.""" return SensorDeviceClass.TEMPERATURE @@ -269,7 +269,7 @@ class HomematicipIlluminanceSensor(HomematicipGenericEntity, SensorEntity): super().__init__(hap, device, post="Illuminance") @property - def device_class(self) -> str: + def device_class(self) -> SensorDeviceClass: """Return the device class of the sensor.""" return SensorDeviceClass.ILLUMINANCE @@ -308,7 +308,7 @@ class HomematicipPowerSensor(HomematicipGenericEntity, SensorEntity): super().__init__(hap, device, post="Power") @property - def device_class(self) -> str: + def device_class(self) -> SensorDeviceClass: """Return the device class of the sensor.""" return SensorDeviceClass.POWER @@ -333,7 +333,7 @@ class HomematicipEnergySensor(HomematicipGenericEntity, SensorEntity): super().__init__(hap, device, post="Energy") @property - def device_class(self) -> str: + def device_class(self) -> SensorDeviceClass: """Return the device class of the sensor.""" return SensorDeviceClass.ENERGY @@ -411,7 +411,7 @@ class HomematicpTemperatureExternalSensorCh1(HomematicipGenericEntity, SensorEnt super().__init__(hap, device, post="Channel 1 Temperature") @property - def device_class(self) -> str: + def device_class(self) -> SensorDeviceClass: """Return the device class of the sensor.""" return SensorDeviceClass.TEMPERATURE @@ -436,7 +436,7 @@ class HomematicpTemperatureExternalSensorCh2(HomematicipGenericEntity, SensorEnt super().__init__(hap, device, post="Channel 2 Temperature") @property - def device_class(self) -> str: + def device_class(self) -> SensorDeviceClass: """Return the device class of the sensor.""" return SensorDeviceClass.TEMPERATURE @@ -461,7 +461,7 @@ class HomematicpTemperatureExternalSensorDelta(HomematicipGenericEntity, SensorE super().__init__(hap, device, post="Delta Temperature") @property - def device_class(self) -> str: + def device_class(self) -> SensorDeviceClass: """Return the device class of the sensor.""" return SensorDeviceClass.TEMPERATURE diff --git a/homeassistant/components/huisbaasje/sensor.py b/homeassistant/components/huisbaasje/sensor.py index c963f366323..7c0060067c2 100644 --- a/homeassistant/components/huisbaasje/sensor.py +++ b/homeassistant/components/huisbaasje/sensor.py @@ -3,7 +3,11 @@ from __future__ import annotations import logging -from homeassistant.components.sensor import SensorEntity, SensorStateClass +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorStateClass, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_ID, POWER_WATT from homeassistant.core import HomeAssistant @@ -42,7 +46,7 @@ class HuisbaasjeSensor(CoordinatorEntity, SensorEntity): user_id: str, name: str, source_type: str, - device_class: str | None = None, + device_class: SensorDeviceClass | None = None, sensor_type: str = SENSOR_TYPE_RATE, unit_of_measurement: str = POWER_WATT, icon: str = "mdi:lightning-bolt", @@ -72,7 +76,7 @@ class HuisbaasjeSensor(CoordinatorEntity, SensorEntity): return self._name @property - def device_class(self) -> str | None: + def device_class(self) -> SensorDeviceClass | None: """Return the device class of the sensor.""" return self._device_class diff --git a/homeassistant/components/iaqualink/sensor.py b/homeassistant/components/iaqualink/sensor.py index 1c567b04a7f..f1636a09c90 100644 --- a/homeassistant/components/iaqualink/sensor.py +++ b/homeassistant/components/iaqualink/sensor.py @@ -54,7 +54,7 @@ class HassAqualinkSensor(AqualinkEntity, SensorEntity): return float(self.dev.state) @property - def device_class(self) -> str | None: + def device_class(self) -> SensorDeviceClass | None: """Return the class of the sensor.""" if self.dev.name.endswith("_temp"): return SensorDeviceClass.TEMPERATURE diff --git a/homeassistant/components/neato/sensor.py b/homeassistant/components/neato/sensor.py index 2e0e7b56cd6..15a6c454263 100644 --- a/homeassistant/components/neato/sensor.py +++ b/homeassistant/components/neato/sensor.py @@ -79,7 +79,7 @@ class NeatoSensor(SensorEntity): return self._robot_serial @property - def device_class(self) -> str: + def device_class(self) -> SensorDeviceClass: """Return the device class.""" return SensorDeviceClass.BATTERY diff --git a/homeassistant/components/nina/binary_sensor.py b/homeassistant/components/nina/binary_sensor.py index 76280ab159e..bdc79c34d92 100644 --- a/homeassistant/components/nina/binary_sensor.py +++ b/homeassistant/components/nina/binary_sensor.py @@ -62,12 +62,12 @@ class NINAMessage(CoordinatorEntity[NINADataUpdateCoordinator], BinarySensorEnti """Initialize.""" super().__init__(coordinator) - self._region: str = region - self._warning_index: int = slot_id - 1 + self._region = region + self._warning_index = slot_id - 1 - self._attr_name: str = f"Warning: {region_name} {slot_id}" - self._attr_unique_id: str = f"{region}-{slot_id}" - self._attr_device_class: str = BinarySensorDeviceClass.SAFETY + self._attr_name = f"Warning: {region_name} {slot_id}" + self._attr_unique_id = f"{region}-{slot_id}" + self._attr_device_class = BinarySensorDeviceClass.SAFETY @property def is_on(self) -> bool: diff --git a/homeassistant/components/simplisafe/binary_sensor.py b/homeassistant/components/simplisafe/binary_sensor.py index 5ccbfb96afb..054bf0b702b 100644 --- a/homeassistant/components/simplisafe/binary_sensor.py +++ b/homeassistant/components/simplisafe/binary_sensor.py @@ -78,7 +78,7 @@ class TriggeredBinarySensor(SimpliSafeEntity, BinarySensorEntity): simplisafe: SimpliSafe, system: SystemV3, sensor: SensorV3, - device_class: str, + device_class: BinarySensorDeviceClass, ) -> None: """Initialize.""" super().__init__(simplisafe, system, device=sensor) diff --git a/homeassistant/components/switcher_kis/sensor.py b/homeassistant/components/switcher_kis/sensor.py index c8dced8663c..34a4de3e9d3 100644 --- a/homeassistant/components/switcher_kis/sensor.py +++ b/homeassistant/components/switcher_kis/sensor.py @@ -30,8 +30,8 @@ class AttributeDescription: name: str icon: str | None = None unit: str | None = None - device_class: str | None = None - state_class: str | None = None + device_class: SensorDeviceClass | None = None + state_class: SensorStateClass | None = None default_enabled: bool = True diff --git a/homeassistant/components/vera/sensor.py b/homeassistant/components/vera/sensor.py index 8f541bf21b4..9f488fad33e 100644 --- a/homeassistant/components/vera/sensor.py +++ b/homeassistant/components/vera/sensor.py @@ -65,7 +65,7 @@ class VeraSensor(VeraDevice[veraApi.VeraSensor], SensorEntity): return self.current_value @property - def device_class(self) -> str | None: + def device_class(self) -> SensorDeviceClass | None: """Return the class of this entity.""" if self.vera_device.category == veraApi.CATEGORY_TEMPERATURE_SENSOR: return SensorDeviceClass.TEMPERATURE From 205a2ee215d0c9539a430da85e2daf37ddbad476 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Nov 2022 21:37:54 +0100 Subject: [PATCH 0883/1033] Ensure Abode provides valid device classes (#82929) --- homeassistant/components/abode/binary_sensor.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/abode/binary_sensor.py b/homeassistant/components/abode/binary_sensor.py index 4f7af8af640..08ed1925936 100644 --- a/homeassistant/components/abode/binary_sensor.py +++ b/homeassistant/components/abode/binary_sensor.py @@ -1,4 +1,7 @@ """Support for Abode Security System binary sensors.""" +from __future__ import annotations + +from contextlib import suppress from typing import cast from abodepy.devices.binary_sensor import AbodeBinarySensor as ABBinarySensor @@ -47,8 +50,10 @@ class AbodeBinarySensor(AbodeDevice, BinarySensorEntity): return cast(bool, self._device.is_on) @property - def device_class(self) -> str: + def device_class(self) -> BinarySensorDeviceClass | None: """Return the class of the binary sensor.""" if self._device.get_value("is_window") == "1": return BinarySensorDeviceClass.WINDOW - return cast(str, self._device.generic_type) + with suppress(ValueError): + return BinarySensorDeviceClass(cast(str, self._device.generic_type)) + return None From 16fc2972d33b6659dbdeda698c9eeaafeac77c05 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 29 Nov 2022 22:57:58 +0200 Subject: [PATCH 0884/1033] Remove more useless manifest truthiness checks in hassfest (#82914) --- script/hassfest/codeowners.py | 5 +---- script/hassfest/config_flow.py | 4 ++-- script/hassfest/manifest.py | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/script/hassfest/codeowners.py b/script/hassfest/codeowners.py index c300976c193..f95a7b3b542 100644 --- a/script/hassfest/codeowners.py +++ b/script/hassfest/codeowners.py @@ -49,10 +49,7 @@ def generate_and_validate(integrations: dict[str, Integration], config: Config) for domain in sorted(integrations): integration = integrations[domain] - if ( - not integration.manifest - or integration.manifest.get("integration_type") == "virtual" - ): + if integration.integration_type == "virtual": continue codeowners = integration.manifest["codeowners"] diff --git a/script/hassfest/config_flow.py b/script/hassfest/config_flow.py index e37075c5f75..5ede5daaa35 100644 --- a/script/hassfest/config_flow.py +++ b/script/hassfest/config_flow.py @@ -17,7 +17,7 @@ def _validate_integration(config: Config, integration: Integration) -> None: config_flow_file = integration.path / "config_flow.py" if not config_flow_file.is_file(): - if (integration.manifest or {}).get("config_flow"): + if integration.manifest.get("config_flow"): integration.add_error( "config_flow", "Config flows need to be defined in the file config_flow.py", @@ -149,7 +149,7 @@ def _generate_integrations( primary_domains = { domain for domain, integration in integrations.items() - if integration.manifest and domain not in brand_integration_domains + if domain not in brand_integration_domains } # Add all brands to the set primary_domains |= set(brands) diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py index 3c1f90a67d0..130d3288ab6 100644 --- a/script/hassfest/manifest.py +++ b/script/hassfest/manifest.py @@ -292,7 +292,7 @@ def validate_version(integration: Integration) -> None: Will be removed when the version key is no longer optional for custom integrations. """ - if not (integration.manifest and integration.manifest.get("version")): + if not integration.manifest.get("version"): integration.add_error("manifest", "No 'version' key in the manifest file.") return From b7652c78eeb5f759115cf439bf5be8c2e6c5bee4 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 29 Nov 2022 22:34:55 +0100 Subject: [PATCH 0885/1033] Add options flow to enable multiprotocol support on sky connect (#82525) --- .../silabs_multiprotocol_addon.py | 9 +- .../homeassistant_hardware/strings.json | 43 ++++ .../translations/en.json | 43 ++++ .../homeassistant_sky_connect/__init__.py | 80 ++++++- .../homeassistant_sky_connect/config_flow.py | 47 ++++- .../homeassistant_sky_connect/manifest.json | 2 +- .../homeassistant_sky_connect/strings.json | 41 ++++ .../translations/en.json | 41 ++++ .../homeassistant_sky_connect/util.py | 17 ++ .../homeassistant_yellow/__init__.py | 4 +- .../homeassistant_yellow/config_flow.py | 6 +- .../homeassistant_yellow/strings.json | 34 +-- .../homeassistant_yellow/translations/en.json | 2 +- homeassistant/components/zha/radio_manager.py | 34 +-- script/hassfest/translations.py | 18 ++ .../test_silabs_multiprotocol_addon.py | 20 +- .../homeassistant_sky_connect/conftest.py | 126 ++++++++++- .../test_config_flow.py | 196 +++++++++++++++++- .../test_hardware.py | 8 +- .../homeassistant_sky_connect/test_init.py | 171 ++++++++++++++- .../homeassistant_yellow/test_config_flow.py | 4 +- .../homeassistant_yellow/test_init.py | 4 +- tests/components/zha/test_radio_manager.py | 107 ++++++++-- 23 files changed, 962 insertions(+), 95 deletions(-) create mode 100644 homeassistant/components/homeassistant_hardware/strings.json create mode 100644 homeassistant/components/homeassistant_hardware/translations/en.json create mode 100644 homeassistant/components/homeassistant_sky_connect/strings.json create mode 100644 homeassistant/components/homeassistant_sky_connect/translations/en.json create mode 100644 homeassistant/components/homeassistant_sky_connect/util.py diff --git a/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py b/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py index 0724eee9ed5..500555cc6ae 100644 --- a/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py +++ b/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py @@ -98,6 +98,10 @@ class BaseMultiPanFlow(FlowHandler): being migrated. """ + @abstractmethod + def _hardware_name(self) -> str: + """Return the name of the hardware.""" + @abstractmethod def _zha_name(self) -> str: """Return the ZHA name.""" @@ -254,6 +258,7 @@ class OptionsFlowHandler(BaseMultiPanFlow, config_entries.OptionsFlow): data_schema=vol.Schema( {vol.Required(CONF_ENABLE_MULTI_PAN, default=False): bool} ), + description_placeholders={"hardware_name": self._hardware_name()}, ) if not user_input[CONF_ENABLE_MULTI_PAN]: return self.async_create_entry(title="", data={}) @@ -285,10 +290,8 @@ class OptionsFlowHandler(BaseMultiPanFlow, config_entries.OptionsFlow): "name": self._zha_name(), "port": { "path": get_zigbee_socket(self.hass, addon_info), - "baudrate": 115200, - "flow_control": "hardware", }, - "radio_type": "efr32", + "radio_type": "ezsp", }, "old_discovery_info": await self._async_zha_physical_discovery(), } diff --git a/homeassistant/components/homeassistant_hardware/strings.json b/homeassistant/components/homeassistant_hardware/strings.json new file mode 100644 index 00000000000..47549794fc8 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/strings.json @@ -0,0 +1,43 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "step": { + "addon_not_installed": { + "title": "Enable multiprotocol support on the IEEE 802.15.4 radio", + "description": "When multiprotocol support is enabled, the {hardware_name}'s IEEE 802.15.4 radio can be used for both Zigbee and Thread (used by Matter) at the same time. If the radio is already used by the ZHA Zigbee integration, ZHA will be reconfigured to use the multiprotocol firmware.\n\nNote: This is an experimental feature.", + "data": { + "enable_multi_pan": "Enable multiprotocol support" + } + }, + "addon_installed_other_device": { + "title": "Multiprotocol support is already enabled for another device" + }, + "install_addon": { + "title": "The Silicon Labs Multiprotocol add-on installation has started" + }, + "show_revert_guide": { + "title": "Multiprotocol support is enabled for this device", + "description": "If you want to change to Zigbee only firmware, please complete the following manual steps:\n\n * Remove the Silicon Labs Multiprotocol addon\n\n * Flash the Zigbee only firmware, follow the guide at https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Reconfigure ZHA to migrate settings to the reflashed radio" + }, + "start_addon": { + "title": "The Silicon Labs Multiprotocol add-on is starting." + } + }, + "error": { + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "addon_info_failed": "Failed to get Silicon Labs Multiprotocol add-on info.", + "addon_install_failed": "Failed to install the Silicon Labs Multiprotocol add-on.", + "addon_set_config_failed": "Failed to set Silicon Labs Multiprotocol configuration.", + "addon_start_failed": "Failed to start the Silicon Labs Multiprotocol add-on.", + "not_hassio": "The hardware options can only be configured on HassOS installations.", + "zha_migration_failed": "The ZHA migration did not succeed." + }, + "progress": { + "install_addon": "Please wait while the Silicon Labs Multiprotocol add-on installation finishes. This can take several minutes.", + "start_addon": "Please wait while the Silicon Labs Multiprotocol add-on start completes. This may take some seconds." + } + } + } +} diff --git a/homeassistant/components/homeassistant_hardware/translations/en.json b/homeassistant/components/homeassistant_hardware/translations/en.json new file mode 100644 index 00000000000..ec75e234c4d --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/en.json @@ -0,0 +1,43 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "Failed to get Silicon Labs Multiprotocol add-on info.", + "addon_install_failed": "Failed to install the Silicon Labs Multiprotocol add-on.", + "addon_set_config_failed": "Failed to set Silicon Labs Multiprotocol configuration.", + "addon_start_failed": "Failed to start the Silicon Labs Multiprotocol add-on.", + "not_hassio": "The hardware options can only be configured on HassOS installations.", + "zha_migration_failed": "The ZHA migration did not succeed." + }, + "error": { + "unknown": "Unexpected error" + }, + "progress": { + "install_addon": "Please wait while the Silicon Labs Multiprotocol add-on installation finishes. This can take several minutes.", + "start_addon": "Please wait while the Silicon Labs Multiprotocol add-on start completes. This may take some seconds." + }, + "step": { + "addon_installed_other_device": { + "title": "Multiprotocol support is already enabled for another device" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Enable multiprotocol support" + }, + "description": "When multiprotocol support is enabled, the {hardware_name}'s IEEE 802.15.4 radio can be used for both Zigbee and Thread (used by Matter) at the same time. If the radio is already used by the ZHA Zigbee integration, ZHA will be reconfigured to use the multiprotocol firmware.\n\nNote: This is an experimental feature.", + "title": "Enable multiprotocol support on the IEEE 802.15.4 radio" + }, + "install_addon": { + "title": "The Silicon Labs Multiprotocol add-on installation has started" + }, + "show_revert_guide": { + "description": "If you want to change to Zigbee only firmware, please complete the following manual steps:\n\n * Remove the Silicon Labs Multiprotocol addon\n\n * Flash the Zigbee only firmware, follow the guide at https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Reconfigure ZHA to migrate settings to the reflashed radio", + "title": "Multiprotocol support is enabled for this device" + }, + "start_addon": { + "title": "The Silicon Labs Multiprotocol add-on is starting." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/__init__.py b/homeassistant/components/homeassistant_sky_connect/__init__.py index cb00f0e32ec..e65394ca15c 100644 --- a/homeassistant/components/homeassistant_sky_connect/__init__.py +++ b/homeassistant/components/homeassistant_sky_connect/__init__.py @@ -1,12 +1,63 @@ """The Home Assistant Sky Connect integration.""" from __future__ import annotations +import logging + from homeassistant.components import usb +from homeassistant.components.hassio import ( + AddonError, + AddonInfo, + AddonManager, + AddonState, + is_hassio, +) +from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon import ( + get_addon_manager, + get_zigbee_socket, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from .const import DOMAIN +from .util import get_usb_service_info + +_LOGGER = logging.getLogger(__name__) + + +async def _multi_pan_addon_info(hass, entry: ConfigEntry) -> AddonInfo | None: + """Return AddonInfo if the multi-PAN addon is enabled for our SkyConnect.""" + if not is_hassio(hass): + return None + + addon_manager: AddonManager = get_addon_manager(hass) + try: + addon_info: AddonInfo = await addon_manager.async_get_addon_info() + except AddonError as err: + _LOGGER.error(err) + raise ConfigEntryNotReady from err + + # Start the addon if it's not started + if addon_info.state == AddonState.NOT_RUNNING: + await addon_manager.async_start_addon() + + if addon_info.state not in (AddonState.NOT_INSTALLED, AddonState.RUNNING): + _LOGGER.debug( + "Multi pan addon in state %s, delaying yellow config entry setup", + addon_info.state, + ) + raise ConfigEntryNotReady + + if addon_info.state == AddonState.NOT_INSTALLED: + return None + + usb_dev = entry.data["device"] + dev_path = await hass.async_add_executor_job(usb.get_serial_by_id, usb_dev) + + if addon_info.options["device"] != dev_path: + return None + + return addon_info async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -24,19 +75,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # The USB dongle is not plugged in raise ConfigEntryNotReady - usb_info = usb.UsbServiceInfo( - device=entry.data["device"], - vid=entry.data["vid"], - pid=entry.data["pid"], - serial_number=entry.data["serial_number"], - manufacturer=entry.data["manufacturer"], - description=entry.data["description"], - ) + addon_info = await _multi_pan_addon_info(hass, entry) + if not addon_info: + usb_info = get_usb_service_info(entry) + await hass.config_entries.flow.async_init( + "zha", + context={"source": "usb"}, + data=usb_info, + ) + return True + + hw_discovery_data = { + "name": "Sky Connect Multi-PAN", + "port": { + "path": get_zigbee_socket(hass, addon_info), + }, + "radio_type": "ezsp", + } await hass.config_entries.flow.async_init( "zha", - context={"source": "usb"}, - data=usb_info, + context={"source": "hardware"}, + data=hw_discovery_data, ) return True diff --git a/homeassistant/components/homeassistant_sky_connect/config_flow.py b/homeassistant/components/homeassistant_sky_connect/config_flow.py index 21cc5e3ace4..1e4fd8701cd 100644 --- a/homeassistant/components/homeassistant_sky_connect/config_flow.py +++ b/homeassistant/components/homeassistant_sky_connect/config_flow.py @@ -1,11 +1,16 @@ """Config flow for the Home Assistant Sky Connect integration.""" from __future__ import annotations +from typing import Any + from homeassistant.components import usb -from homeassistant.config_entries import ConfigFlow +from homeassistant.components.homeassistant_hardware import silabs_multiprotocol_addon +from homeassistant.config_entries import ConfigEntry, ConfigFlow +from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from .const import DOMAIN +from .util import get_usb_service_info class HomeAssistantSkyConnectConfigFlow(ConfigFlow, domain=DOMAIN): @@ -13,6 +18,14 @@ class HomeAssistantSkyConnectConfigFlow(ConfigFlow, domain=DOMAIN): VERSION = 1 + @staticmethod + @callback + def async_get_options_flow( + config_entry: ConfigEntry, + ) -> HomeAssistantSkyConnectOptionsFlow: + """Return the options flow.""" + return HomeAssistantSkyConnectOptionsFlow(config_entry) + async def async_step_usb(self, discovery_info: usb.UsbServiceInfo) -> FlowResult: """Handle usb discovery.""" device = discovery_info.device @@ -35,3 +48,35 @@ class HomeAssistantSkyConnectConfigFlow(ConfigFlow, domain=DOMAIN): "description": description, }, ) + + +class HomeAssistantSkyConnectOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandler): + """Handle an option flow for Home Assistant Sky Connect.""" + + async def _async_serial_port_settings( + self, + ) -> silabs_multiprotocol_addon.SerialPortSettings: + """Return the radio serial port settings.""" + usb_dev = self.config_entry.data["device"] + dev_path = await self.hass.async_add_executor_job(usb.get_serial_by_id, usb_dev) + return silabs_multiprotocol_addon.SerialPortSettings( + device=dev_path, + baudrate="115200", + flow_control=True, + ) + + async def _async_zha_physical_discovery(self) -> dict[str, Any]: + """Return ZHA discovery data when multiprotocol FW is not used. + + Passed to ZHA do determine if the ZHA config entry is connected to the radio + being migrated. + """ + return {"usb": get_usb_service_info(self.config_entry)} + + def _zha_name(self) -> str: + """Return the ZHA name.""" + return "Sky Connect Multi-PAN" + + def _hardware_name(self) -> str: + """Return the name of the hardware.""" + return "Home Assistant Sky Connect" diff --git a/homeassistant/components/homeassistant_sky_connect/manifest.json b/homeassistant/components/homeassistant_sky_connect/manifest.json index 8fdc1d3c1d1..34bb2ad701c 100644 --- a/homeassistant/components/homeassistant_sky_connect/manifest.json +++ b/homeassistant/components/homeassistant_sky_connect/manifest.json @@ -3,7 +3,7 @@ "name": "Home Assistant Sky Connect", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homeassistant_sky_connect", - "dependencies": ["hardware", "usb"], + "dependencies": ["hardware", "usb", "homeassistant_hardware"], "codeowners": ["@home-assistant/core"], "integration_type": "hardware", "usb": [ diff --git a/homeassistant/components/homeassistant_sky_connect/strings.json b/homeassistant/components/homeassistant_sky_connect/strings.json new file mode 100644 index 00000000000..970f9d97a4c --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/strings.json @@ -0,0 +1,41 @@ +{ + "options": { + "step": { + "addon_not_installed": { + "title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::addon_not_installed::title%]", + "description": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::addon_not_installed::description%]", + "data": { + "enable_multi_pan": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::addon_not_installed::data::enable_multi_pan%]" + } + }, + "addon_installed_other_device": { + "title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::addon_installed_other_device::title%]" + }, + "install_addon": { + "title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::install_addon::title%]" + }, + "show_revert_guide": { + "title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::show_revert_guide::title%]", + "description": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::show_revert_guide::description%]" + }, + "start_addon": { + "title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::start_addon::title%]" + } + }, + "error": { + "unknown": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::error::unknown%]" + }, + "abort": { + "addon_info_failed": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::addon_info_failed%]", + "addon_install_failed": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::addon_install_failed%]", + "addon_set_config_failed": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::addon_set_config_failed%]", + "addon_start_failed": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::addon_start_failed%]", + "not_hassio": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::not_hassio%]", + "zha_migration_failed": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::zha_migration_failed%]" + }, + "progress": { + "install_addon": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::progress::install_addon%]", + "start_addon": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::progress::start_addon%]" + } + } +} diff --git a/homeassistant/components/homeassistant_sky_connect/translations/en.json b/homeassistant/components/homeassistant_sky_connect/translations/en.json new file mode 100644 index 00000000000..8e12173f86a --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/en.json @@ -0,0 +1,41 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Failed to get Silicon Labs Multiprotocol add-on info.", + "addon_install_failed": "Failed to install the Silicon Labs Multiprotocol add-on.", + "addon_set_config_failed": "Failed to set Silicon Labs Multiprotocol configuration.", + "addon_start_failed": "Failed to start the Silicon Labs Multiprotocol add-on.", + "not_hassio": "The hardware options can only be configured on HassOS installations.", + "zha_migration_failed": "The ZHA migration did not succeed." + }, + "error": { + "unknown": "Unexpected error" + }, + "progress": { + "install_addon": "Please wait while the Silicon Labs Multiprotocol add-on installation finishes. This can take several minutes.", + "start_addon": "Please wait while the Silicon Labs Multiprotocol add-on start completes. This may take some seconds." + }, + "step": { + "addon_installed_other_device": { + "title": "Multiprotocol support is already enabled for another device" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Enable multiprotocol support" + }, + "description": "When multiprotocol support is enabled, the {hardware_name}'s IEEE 802.15.4 radio can be used for both Zigbee and Thread (used by Matter) at the same time. If the radio is already used by the ZHA Zigbee integration, ZHA will be reconfigured to use the multiprotocol firmware.\n\nNote: This is an experimental feature.", + "title": "Enable multiprotocol support on the IEEE 802.15.4 radio" + }, + "install_addon": { + "title": "The Silicon Labs Multiprotocol add-on installation has started" + }, + "show_revert_guide": { + "description": "If you want to change to Zigbee only firmware, please complete the following manual steps:\n\n * Remove the Silicon Labs Multiprotocol addon\n\n * Flash the Zigbee only firmware, follow the guide at https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Reconfigure ZHA to migrate settings to the reflashed radio", + "title": "Multiprotocol support is enabled for this device" + }, + "start_addon": { + "title": "The Silicon Labs Multiprotocol add-on is starting." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/util.py b/homeassistant/components/homeassistant_sky_connect/util.py new file mode 100644 index 00000000000..804ce83d063 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/util.py @@ -0,0 +1,17 @@ +"""Utility functions for Home Assistant Sky Connect integration.""" +from __future__ import annotations + +from homeassistant.components import usb +from homeassistant.config_entries import ConfigEntry + + +def get_usb_service_info(config_entry: ConfigEntry) -> usb.UsbServiceInfo: + """Return UsbServiceInfo.""" + return usb.UsbServiceInfo( + device=config_entry.data["device"], + vid=config_entry.data["vid"], + pid=config_entry.data["pid"], + serial_number=config_entry.data["serial_number"], + manufacturer=config_entry.data["manufacturer"], + description=config_entry.data["description"], + ) diff --git a/homeassistant/components/homeassistant_yellow/__init__.py b/homeassistant/components/homeassistant_yellow/__init__.py index 1b61fe7d47f..6099dc014df 100644 --- a/homeassistant/components/homeassistant_yellow/__init__.py +++ b/homeassistant/components/homeassistant_yellow/__init__.py @@ -73,10 +73,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: "name": "Yellow Multi-PAN", "port": { "path": get_zigbee_socket(hass, addon_info), - "baudrate": 115200, - "flow_control": "hardware", }, - "radio_type": "efr32", + "radio_type": "ezsp", } await hass.config_entries.flow.async_init( diff --git a/homeassistant/components/homeassistant_yellow/config_flow.py b/homeassistant/components/homeassistant_yellow/config_flow.py index e5e6bb3b2c6..09cdcc1469a 100644 --- a/homeassistant/components/homeassistant_yellow/config_flow.py +++ b/homeassistant/components/homeassistant_yellow/config_flow.py @@ -51,8 +51,12 @@ class HomeAssistantYellowOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandl Passed to ZHA do determine if the ZHA config entry is connected to the radio being migrated. """ - return ZHA_HW_DISCOVERY_DATA + return {"hw": ZHA_HW_DISCOVERY_DATA} def _zha_name(self) -> str: """Return the ZHA name.""" return "Yellow Multi-PAN" + + def _hardware_name(self) -> str: + """Return the name of the hardware.""" + return "Home Assistant Yellow" diff --git a/homeassistant/components/homeassistant_yellow/strings.json b/homeassistant/components/homeassistant_yellow/strings.json index 1810597b242..970f9d97a4c 100644 --- a/homeassistant/components/homeassistant_yellow/strings.json +++ b/homeassistant/components/homeassistant_yellow/strings.json @@ -2,40 +2,40 @@ "options": { "step": { "addon_not_installed": { - "title": "Enable multiprotocol support on the IEEE 802.15.4 radio", - "description": "When multiprotocol support is enabled, the Home Assistant Yellow's IEEE 802.15.4 radio can be used for both Zigbee and Thread (used by Matter) at the same time. If the radio is already used by the ZHA Zigbee integration, ZHA will be reconfigured to use the multiprotocol firmware.\n\nNote: This is an experimental feature.", + "title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::addon_not_installed::title%]", + "description": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::addon_not_installed::description%]", "data": { - "enable_multi_pan": "Enable multiprotocol support" + "enable_multi_pan": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::addon_not_installed::data::enable_multi_pan%]" } }, "addon_installed_other_device": { - "title": "Multiprotocol support is already enabled for another device" + "title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::addon_installed_other_device::title%]" }, "install_addon": { - "title": "The Silicon Labs Multiprotocol add-on installation has started" + "title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::install_addon::title%]" }, "show_revert_guide": { - "title": "Multiprotocol support is enabled for this device", - "description": "If you want to change to Zigbee only firmware, please complete the following manual steps:\n\n * Remove the Silicon Labs Multiprotocol addon\n\n * Flash the Zigbee only firmware, follow the guide at https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Reconfigure ZHA to migrate settings to the reflashed radio" + "title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::show_revert_guide::title%]", + "description": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::show_revert_guide::description%]" }, "start_addon": { - "title": "The Silicon Labs Multiprotocol add-on is starting." + "title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::start_addon::title%]" } }, "error": { - "unknown": "[%key:common::config_flow::error::unknown%]" + "unknown": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::error::unknown%]" }, "abort": { - "addon_info_failed": "Failed to get Silicon Labs Multiprotocol add-on info.", - "addon_install_failed": "Failed to install the Silicon Labs Multiprotocol add-on.", - "addon_set_config_failed": "Failed to set Silicon Labs Multiprotocol configuration.", - "addon_start_failed": "Failed to start the Silicon Labs Multiprotocol add-on.", - "not_hassio": "The hardware options can only be configured on HassOS installations.", - "zha_migration_failed": "The ZHA migration did not succeed." + "addon_info_failed": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::addon_info_failed%]", + "addon_install_failed": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::addon_install_failed%]", + "addon_set_config_failed": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::addon_set_config_failed%]", + "addon_start_failed": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::addon_start_failed%]", + "not_hassio": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::not_hassio%]", + "zha_migration_failed": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::abort::zha_migration_failed%]" }, "progress": { - "install_addon": "Please wait while the Silicon Labs Multiprotocol add-on installation finishes. This can take several minutes.", - "start_addon": "Please wait while the Silicon Labs Multiprotocol add-on start completes. This may take some seconds." + "install_addon": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::progress::install_addon%]", + "start_addon": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::progress::start_addon%]" } } } diff --git a/homeassistant/components/homeassistant_yellow/translations/en.json b/homeassistant/components/homeassistant_yellow/translations/en.json index 0b074452652..8e12173f86a 100644 --- a/homeassistant/components/homeassistant_yellow/translations/en.json +++ b/homeassistant/components/homeassistant_yellow/translations/en.json @@ -23,7 +23,7 @@ "data": { "enable_multi_pan": "Enable multiprotocol support" }, - "description": "When multiprotocol support is enabled, the Home Assistant Yellow's IEEE 802.15.4 radio can be used for both Zigbee and Thread (used by Matter) at the same time. If the radio is already used by the ZHA Zigbee integration, ZHA will be reconfigured to use the multiprotocol firmware.\n\nNote: This is an experimental feature.", + "description": "When multiprotocol support is enabled, the {hardware_name}'s IEEE 802.15.4 radio can be used for both Zigbee and Thread (used by Matter) at the same time. If the radio is already used by the ZHA Zigbee integration, ZHA will be reconfigured to use the multiprotocol firmware.\n\nNote: This is an experimental feature.", "title": "Enable multiprotocol support on the IEEE 802.15.4 radio" }, "install_addon": { diff --git a/homeassistant/components/zha/radio_manager.py b/homeassistant/components/zha/radio_manager.py index 512f26a139b..2a914dee1d7 100644 --- a/homeassistant/components/zha/radio_manager.py +++ b/homeassistant/components/zha/radio_manager.py @@ -15,6 +15,7 @@ from zigpy.config import CONF_DEVICE, CONF_DEVICE_PATH from zigpy.exceptions import NetworkNotFormed from homeassistant import config_entries +from homeassistant.components import usb from homeassistant.core import HomeAssistant from .core.const import ( @@ -53,7 +54,12 @@ HARDWARE_DISCOVERY_SCHEMA = vol.Schema( HARDWARE_MIGRATION_SCHEMA = vol.Schema( { vol.Required("new_discovery_info"): HARDWARE_DISCOVERY_SCHEMA, - vol.Required("old_discovery_info"): HARDWARE_DISCOVERY_SCHEMA, + vol.Required("old_discovery_info"): vol.Schema( + { + vol.Exclusive("hw", "discovery"): HARDWARE_DISCOVERY_SCHEMA, + vol.Exclusive("usb", "discovery"): usb.UsbServiceInfo, + } + ), } ) @@ -297,21 +303,20 @@ class ZhaMultiPANMigrationHelper: new_radio_type = ZhaRadioManager.parse_radio_type( migration_data["new_discovery_info"]["radio_type"] ) - old_radio_type = ZhaRadioManager.parse_radio_type( - migration_data["old_discovery_info"]["radio_type"] - ) new_device_settings = new_radio_type.controller.SCHEMA_DEVICE( migration_data["new_discovery_info"]["port"] ) - old_device_settings = old_radio_type.controller.SCHEMA_DEVICE( - migration_data["old_discovery_info"]["port"] - ) - if ( - self._config_entry.data[CONF_DEVICE][CONF_DEVICE_PATH] - != old_device_settings[CONF_DEVICE_PATH] - ): + if "hw" in migration_data["old_discovery_info"]: + old_device_path = migration_data["old_discovery_info"]["hw"]["port"]["path"] + else: # usb + device = migration_data["old_discovery_info"]["usb"].device + old_device_path = await self._hass.async_add_executor_job( + usb.get_serial_by_id, device + ) + + if self._config_entry.data[CONF_DEVICE][CONF_DEVICE_PATH] != old_device_path: # ZHA is using another radio, do nothing return False @@ -322,11 +327,12 @@ class ZhaMultiPANMigrationHelper: pass # Temporarily connect to the old radio to read its settings + config_entry_data = self._config_entry.data old_radio_mgr = ZhaRadioManager() old_radio_mgr.hass = self._hass - old_radio_mgr.radio_type = old_radio_type - old_radio_mgr.device_path = old_device_settings[CONF_DEVICE_PATH] - old_radio_mgr.device_settings = old_device_settings + old_radio_mgr.device_path = config_entry_data[CONF_DEVICE][CONF_DEVICE_PATH] + old_radio_mgr.device_settings = config_entry_data[CONF_DEVICE] + old_radio_mgr.radio_type = RadioType[config_entry_data[CONF_RADIO_TYPE]] backup = await old_radio_mgr.async_load_network_settings(create_backup=True) # Then configure the radio manager for the new radio to use the new settings diff --git a/script/hassfest/translations.py b/script/hassfest/translations.py index 8f2816e503c..824ebc6b825 100644 --- a/script/hassfest/translations.py +++ b/script/hassfest/translations.py @@ -275,6 +275,22 @@ def gen_auth_schema(config: Config, integration: Integration) -> vol.Schema: ) +def gen_ha_hardware_schema(config: Config, integration: Integration): + """Generate auth schema.""" + return vol.Schema( + { + str: { + vol.Optional("options"): gen_data_entry_schema( + config=config, + integration=integration, + flow_title=UNDEFINED, + require_step_title=False, + ) + } + } + ) + + def gen_platform_strings_schema(config: Config, integration: Integration) -> vol.Schema: """Generate platform strings schema like strings.sensor.json. @@ -351,6 +367,8 @@ def validate_translation_file( # noqa: C901 ) } ) + elif integration.domain == "homeassistant_hardware": + strings_schema = gen_ha_hardware_schema(config, integration) else: strings_schema = gen_strings_schema(config, integration) diff --git a/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py b/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py index f61fd5e8d76..577ac25eb82 100644 --- a/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py +++ b/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py @@ -61,13 +61,15 @@ class TestOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandler): being migrated. """ return { - "name": "Test", - "port": { - "path": "/dev/ttyTEST123", - "baudrate": 115200, - "flow_control": "hardware", - }, - "radio_type": "efr32", + "hw": { + "name": "Test", + "port": { + "path": "/dev/ttyTEST123", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + } } def _zha_name(self) -> str: @@ -223,8 +225,8 @@ async def test_option_flow_install_multi_pan_addon_zha( assert zha_config_entry.data == { "device": { "path": "socket://core-silabs-multiprotocol:9999", - "baudrate": 115200, - "flow_control": "hardware", + "baudrate": 57600, # ZHA default + "flow_control": "software", # ZHA default }, "radio_type": "ezsp", } diff --git a/tests/components/homeassistant_sky_connect/conftest.py b/tests/components/homeassistant_sky_connect/conftest.py index cc606c9b988..2d333c62b2d 100644 --- a/tests/components/homeassistant_sky_connect/conftest.py +++ b/tests/components/homeassistant_sky_connect/conftest.py @@ -1,14 +1,138 @@ """Test fixtures for the Home Assistant Sky Connect integration.""" -from unittest.mock import patch +from collections.abc import Generator +from unittest.mock import MagicMock, patch import pytest +@pytest.fixture(name="mock_usb_serial_by_id", autouse=True) +def mock_usb_serial_by_id_fixture() -> Generator[MagicMock, None, None]: + """Mock usb serial by id.""" + with patch( + "homeassistant.components.zwave_js.config_flow.usb.get_serial_by_id" + ) as mock_usb_serial_by_id: + mock_usb_serial_by_id.side_effect = lambda x: x + yield mock_usb_serial_by_id + + @pytest.fixture(autouse=True) def mock_zha(): """Mock the zha integration.""" + mock_connect_app = MagicMock() + mock_connect_app.__aenter__.return_value.backups.backups = [MagicMock()] + mock_connect_app.__aenter__.return_value.backups.create_backup.return_value = ( + MagicMock() + ) + with patch( + "homeassistant.components.zha.radio_manager.ZhaRadioManager._connect_zigpy_app", + return_value=mock_connect_app, + ), patch( "homeassistant.components.zha.async_setup_entry", return_value=True, ): yield + + +@pytest.fixture(name="addon_running") +def mock_addon_running(addon_store_info, addon_info): + """Mock add-on already running.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "started", + "version": "1.0.0", + } + addon_info.return_value["hostname"] = "core-silabs-multiprotocol" + addon_info.return_value["state"] = "started" + addon_info.return_value["version"] = "1.0.0" + return addon_info + + +@pytest.fixture(name="addon_installed") +def mock_addon_installed(addon_store_info, addon_info): + """Mock add-on already installed but not running.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "stopped", + "version": "1.0.0", + } + addon_info.return_value["hostname"] = "core-silabs-multiprotocol" + addon_info.return_value["state"] = "stopped" + addon_info.return_value["version"] = "1.0.0" + return addon_info + + +@pytest.fixture(name="addon_store_info") +def addon_store_info_fixture(): + """Mock Supervisor add-on store info.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_get_addon_store_info" + ) as addon_store_info: + addon_store_info.return_value = { + "installed": None, + "state": None, + "version": "1.0.0", + } + yield addon_store_info + + +@pytest.fixture(name="addon_info") +def addon_info_fixture(): + """Mock Supervisor add-on info.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_get_addon_info", + ) as addon_info: + addon_info.return_value = { + "hostname": None, + "options": {}, + "state": None, + "update_available": False, + "version": None, + } + yield addon_info + + +@pytest.fixture(name="set_addon_options") +def set_addon_options_fixture(): + """Mock set add-on options.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_set_addon_options" + ) as set_options: + yield set_options + + +@pytest.fixture(name="install_addon_side_effect") +def install_addon_side_effect_fixture(addon_store_info, addon_info): + """Return the install add-on side effect.""" + + async def install_addon(hass, slug): + """Mock install add-on.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "stopped", + "version": "1.0.0", + } + addon_info.return_value["hostname"] = "core-silabs-multiprotocol" + addon_info.return_value["state"] = "stopped" + addon_info.return_value["version"] = "1.0.0" + + return install_addon + + +@pytest.fixture(name="install_addon") +def mock_install_addon(install_addon_side_effect): + """Mock install add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_install_addon", + side_effect=install_addon_side_effect, + ) as install_addon: + yield install_addon + + +@pytest.fixture(name="start_addon") +def start_addon_fixture(): + """Mock start add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_start_addon" + ) as start_addon: + yield start_addon diff --git a/tests/components/homeassistant_sky_connect/test_config_flow.py b/tests/components/homeassistant_sky_connect/test_config_flow.py index bbde732d201..c38edf00fa7 100644 --- a/tests/components/homeassistant_sky_connect/test_config_flow.py +++ b/tests/components/homeassistant_sky_connect/test_config_flow.py @@ -1,13 +1,18 @@ """Test the Home Assistant Sky Connect config flow.""" import copy -from unittest.mock import patch +from unittest.mock import Mock, patch from homeassistant.components import homeassistant_sky_connect, usb from homeassistant.components.homeassistant_sky_connect.const import DOMAIN +from homeassistant.components.zha.core.const import ( + CONF_DEVICE_PATH, + DOMAIN as ZHA_DOMAIN, + RadioType, +) from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, MockModule, mock_integration USB_DATA = usb.UsbServiceInfo( device="bla_device", @@ -143,3 +148,190 @@ async def test_config_flow_update_device(hass: HomeAssistant) -> None: assert result["reason"] == "already_configured" assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_unload_entry.mock_calls) == 1 + + +async def test_option_flow_install_multi_pan_addon( + hass: HomeAssistant, + addon_store_info, + addon_info, + install_addon, + set_addon_options, + start_addon, +) -> None: + """Test installing the multi pan addon.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={ + "device": USB_DATA.device, + "vid": USB_DATA.vid, + "pid": USB_DATA.pid, + "serial_number": USB_DATA.serial_number, + "manufacturer": USB_DATA.manufacturer, + "description": USB_DATA.description, + }, + domain=DOMAIN, + options={}, + title="Home Assistant Sky Connect", + unique_id=f"{USB_DATA.vid}:{USB_DATA.pid}_{USB_DATA.serial_number}_{USB_DATA.manufacturer}_{USB_DATA.description}", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": True, + }, + ) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + assert result["progress_action"] == "install_addon" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "configure_addon" + install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + set_addon_options.assert_called_once_with( + hass, + "core_silabs_multiprotocol", + { + "options": { + "autoflash_firmware": True, + "device": "bla_device", + "baudrate": "115200", + "flow_control": True, + } + }, + ) + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "finish_addon_setup" + start_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.CREATE_ENTRY + + +def mock_detect_radio_type(radio_type=RadioType.ezsp, ret=True): + """Mock `detect_radio_type` that just sets the appropriate attributes.""" + + async def detect(self): + self.radio_type = radio_type + self.device_settings = radio_type.controller.SCHEMA_DEVICE( + {CONF_DEVICE_PATH: self.device_path} + ) + + return ret + + return detect + + +@patch( + "homeassistant.components.zha.radio_manager.ZhaRadioManager.detect_radio_type", + mock_detect_radio_type(), +) +async def test_option_flow_install_multi_pan_addon_zha( + hass: HomeAssistant, + addon_store_info, + addon_info, + install_addon, + set_addon_options, + start_addon, +) -> None: + """Test installing the multi pan addon when a zha config entry exists.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={ + "device": USB_DATA.device, + "vid": USB_DATA.vid, + "pid": USB_DATA.pid, + "serial_number": USB_DATA.serial_number, + "manufacturer": USB_DATA.manufacturer, + "description": USB_DATA.description, + }, + domain=DOMAIN, + options={}, + title="Home Assistant Sky Connect", + unique_id=f"{USB_DATA.vid}:{USB_DATA.pid}_{USB_DATA.serial_number}_{USB_DATA.manufacturer}_{USB_DATA.description}", + ) + config_entry.add_to_hass(hass) + + zha_config_entry = MockConfigEntry( + data={"device": {"path": "bla_device"}, "radio_type": "ezsp"}, + domain=ZHA_DOMAIN, + options={}, + title="Yellow", + ) + zha_config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon.is_hassio", + side_effect=Mock(return_value=True), + ): + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "addon_not_installed" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "enable_multi_pan": True, + }, + ) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + assert result["progress_action"] == "install_addon" + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "configure_addon" + install_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + set_addon_options.assert_called_once_with( + hass, + "core_silabs_multiprotocol", + { + "options": { + "autoflash_firmware": True, + "device": "bla_device", + "baudrate": "115200", + "flow_control": True, + } + }, + ) + # Check the ZHA config entry data is updated + assert zha_config_entry.data == { + "device": { + "path": "socket://core-silabs-multiprotocol:9999", + "baudrate": 57600, # ZHA default + "flow_control": "software", # ZHA default + }, + "radio_type": "ezsp", + } + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.SHOW_PROGRESS_DONE + assert result["step_id"] == "finish_addon_setup" + start_addon.assert_called_once_with(hass, "core_silabs_multiprotocol") + + result = await hass.config_entries.options.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.CREATE_ENTRY diff --git a/tests/components/homeassistant_sky_connect/test_hardware.py b/tests/components/homeassistant_sky_connect/test_hardware.py index 2ba305afb4a..01f0e6ac5d7 100644 --- a/tests/components/homeassistant_sky_connect/test_hardware.py +++ b/tests/components/homeassistant_sky_connect/test_hardware.py @@ -4,7 +4,7 @@ from unittest.mock import patch from homeassistant.components.homeassistant_sky_connect.const import DOMAIN from homeassistant.core import HomeAssistant -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, MockModule, mock_integration CONFIG_ENTRY_DATA = { "device": "bla_device", @@ -25,8 +25,12 @@ CONFIG_ENTRY_DATA_2 = { } -async def test_hardware_info(hass: HomeAssistant, hass_ws_client) -> None: +async def test_hardware_info( + hass: HomeAssistant, hass_ws_client, addon_store_info +) -> None: """Test we can get the board info.""" + mock_integration(hass, MockModule("usb")) + # Setup the config entry config_entry = MockConfigEntry( data=CONFIG_ENTRY_DATA, diff --git a/tests/components/homeassistant_sky_connect/test_init.py b/tests/components/homeassistant_sky_connect/test_init.py index 5a9d2064a24..ebf1c74d9e0 100644 --- a/tests/components/homeassistant_sky_connect/test_init.py +++ b/tests/components/homeassistant_sky_connect/test_init.py @@ -1,11 +1,12 @@ """Test the Home Assistant Sky Connect integration.""" from collections.abc import Generator from typing import Any -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock, Mock, patch import pytest from homeassistant.components import zha +from homeassistant.components.hassio.handler import HassioAPIError from homeassistant.components.homeassistant_sky_connect.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant @@ -46,7 +47,12 @@ def mock_zha_config_flow_setup() -> Generator[None, None, None]: "onboarded, num_entries, num_flows", ((False, 1, 0), (True, 0, 1)) ) async def test_setup_entry( - mock_zha_config_flow_setup, hass: HomeAssistant, onboarded, num_entries, num_flows + mock_zha_config_flow_setup, + hass: HomeAssistant, + addon_store_info, + onboarded, + num_entries, + num_flows, ) -> None: """Test setup of a config entry, including setup of zha.""" # Setup the config entry @@ -90,7 +96,9 @@ async def test_setup_entry( assert len(hass.config_entries.async_entries("zha")) == num_entries -async def test_setup_zha(mock_zha_config_flow_setup, hass: HomeAssistant) -> None: +async def test_setup_zha( + mock_zha_config_flow_setup, hass: HomeAssistant, addon_store_info +) -> None: """Test zha gets the right config.""" # Setup the config entry config_entry = MockConfigEntry( @@ -134,6 +142,108 @@ async def test_setup_zha(mock_zha_config_flow_setup, hass: HomeAssistant) -> Non assert config_entry.title == CONFIG_ENTRY_DATA["description"] +async def test_setup_zha_multipan( + hass: HomeAssistant, addon_info, addon_running +) -> None: + """Test zha gets the right config.""" + addon_info.return_value["options"]["device"] = CONFIG_ENTRY_DATA["device"] + + # Setup the config entry + config_entry = MockConfigEntry( + data=CONFIG_ENTRY_DATA, + domain=DOMAIN, + options={}, + title="Home Assistant Sky Connect", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.homeassistant_sky_connect.usb.async_is_plugged_in", + return_value=True, + ) as mock_is_plugged_in, patch( + "homeassistant.components.onboarding.async_is_onboarded", return_value=False + ), patch( + "homeassistant.components.homeassistant_sky_connect.is_hassio", + side_effect=Mock(return_value=True), + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert len(mock_is_plugged_in.mock_calls) == 1 + + # Finish setting up ZHA + zha_flows = hass.config_entries.flow.async_progress_by_handler("zha") + assert len(zha_flows) == 1 + assert zha_flows[0]["step_id"] == "choose_formation_strategy" + + await hass.config_entries.flow.async_configure( + zha_flows[0]["flow_id"], + user_input={"next_step_id": zha.config_flow.FORMATION_REUSE_SETTINGS}, + ) + await hass.async_block_till_done() + + config_entry = hass.config_entries.async_entries("zha")[0] + assert config_entry.data == { + "device": { + "baudrate": 57600, # ZHA default + "flow_control": "software", # ZHA default + "path": "socket://core-silabs-multiprotocol:9999", + }, + "radio_type": "ezsp", + } + assert config_entry.options == {} + assert config_entry.title == "Sky Connect Multi-PAN" + + +async def test_setup_zha_multipan_other_device( + mock_zha_config_flow_setup, hass: HomeAssistant, addon_info, addon_running +) -> None: + """Test zha gets the right config.""" + addon_info.return_value["options"]["device"] = "/dev/not_our_sky_connect" + + # Setup the config entry + config_entry = MockConfigEntry( + data=CONFIG_ENTRY_DATA, + domain=DOMAIN, + options={}, + title="Home Assistant Yellow", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.homeassistant_sky_connect.usb.async_is_plugged_in", + return_value=True, + ) as mock_is_plugged_in, patch( + "homeassistant.components.onboarding.async_is_onboarded", return_value=False + ), patch( + "homeassistant.components.homeassistant_sky_connect.is_hassio", + side_effect=Mock(return_value=True), + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert len(mock_is_plugged_in.mock_calls) == 1 + + # Finish setting up ZHA + zha_flows = hass.config_entries.flow.async_progress_by_handler("zha") + assert len(zha_flows) == 1 + assert zha_flows[0]["step_id"] == "choose_formation_strategy" + + await hass.config_entries.flow.async_configure( + zha_flows[0]["flow_id"], + user_input={"next_step_id": zha.config_flow.FORMATION_REUSE_SETTINGS}, + ) + await hass.async_block_till_done() + + config_entry = hass.config_entries.async_entries("zha")[0] + assert config_entry.data == { + "device": { + "baudrate": 115200, + "flow_control": "software", + "path": CONFIG_ENTRY_DATA["device"], + }, + "radio_type": "ezsp", + } + assert config_entry.options == {} + assert config_entry.title == CONFIG_ENTRY_DATA["description"] + + async def test_setup_entry_wait_usb(hass: HomeAssistant) -> None: """Test setup of a config entry when the dongle is not plugged in.""" # Setup the config entry @@ -152,3 +262,58 @@ async def test_setup_entry_wait_usb(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert len(mock_is_plugged_in.mock_calls) == 1 assert config_entry.state == ConfigEntryState.SETUP_RETRY + + +async def test_setup_entry_addon_info_fails( + hass: HomeAssistant, addon_store_info +) -> None: + """Test setup of a config entry when fetching addon info fails.""" + addon_store_info.side_effect = HassioAPIError("Boom") + + # Setup the config entry + config_entry = MockConfigEntry( + data=CONFIG_ENTRY_DATA, + domain=DOMAIN, + options={}, + title="Home Assistant Sky Connect", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.homeassistant_sky_connect.usb.async_is_plugged_in", + return_value=True, + ), patch( + "homeassistant.components.onboarding.async_is_onboarded", return_value=False + ), patch( + "homeassistant.components.homeassistant_sky_connect.is_hassio", + side_effect=Mock(return_value=True), + ): + assert not await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert config_entry.state == ConfigEntryState.SETUP_RETRY + + +async def test_setup_entry_addon_not_running( + hass: HomeAssistant, addon_installed, start_addon +) -> None: + """Test the addon is started if it is not running.""" + # Setup the config entry + config_entry = MockConfigEntry( + data=CONFIG_ENTRY_DATA, + domain=DOMAIN, + options={}, + title="Home Assistant Sky Connect", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.homeassistant_sky_connect.usb.async_is_plugged_in", + return_value=True, + ), patch( + "homeassistant.components.onboarding.async_is_onboarded", return_value=False + ), patch( + "homeassistant.components.homeassistant_sky_connect.is_hassio", + side_effect=Mock(return_value=True), + ): + assert not await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert config_entry.state == ConfigEntryState.SETUP_RETRY + start_addon.assert_called_once() diff --git a/tests/components/homeassistant_yellow/test_config_flow.py b/tests/components/homeassistant_yellow/test_config_flow.py index a956f812d8a..53d1c5e974d 100644 --- a/tests/components/homeassistant_yellow/test_config_flow.py +++ b/tests/components/homeassistant_yellow/test_config_flow.py @@ -197,8 +197,8 @@ async def test_option_flow_install_multi_pan_addon_zha( assert zha_config_entry.data == { "device": { "path": "socket://core-silabs-multiprotocol:9999", - "baudrate": 115200, - "flow_control": "hardware", + "baudrate": 57600, # ZHA default + "flow_control": "software", # ZHA default }, "radio_type": "ezsp", } diff --git a/tests/components/homeassistant_yellow/test_init.py b/tests/components/homeassistant_yellow/test_init.py index 85b027e82ed..4118c6dc654 100644 --- a/tests/components/homeassistant_yellow/test_init.py +++ b/tests/components/homeassistant_yellow/test_init.py @@ -144,8 +144,8 @@ async def test_setup_zha_multipan( config_entry = hass.config_entries.async_entries("zha")[0] assert config_entry.data == { "device": { - "baudrate": 115200, - "flow_control": "hardware", + "baudrate": 57600, # ZHA default + "flow_control": "software", # ZHA default "path": "socket://core-silabs-multiprotocol:9999", }, "radio_type": "ezsp", diff --git a/tests/components/zha/test_radio_manager.py b/tests/components/zha/test_radio_manager.py index 505118524b6..671f831fcce 100644 --- a/tests/components/zha/test_radio_manager.py +++ b/tests/components/zha/test_radio_manager.py @@ -11,6 +11,7 @@ from zigpy.config import CONF_DEVICE_PATH import zigpy.types from homeassistant import config_entries +from homeassistant.components.usb import UsbServiceInfo from homeassistant.components.zha import radio_manager from homeassistant.components.zha.core.const import DOMAIN, RadioType from homeassistant.core import HomeAssistant @@ -125,14 +126,68 @@ async def test_migrate_matching_port( "radio_type": "efr32", }, "old_discovery_info": { - "name": "Test", + "hw": { + "name": "Test", + "port": { + "path": "/dev/ttyTEST123", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + } + }, + } + + migration_helper = radio_manager.ZhaMultiPANMigrationHelper(hass, config_entry) + assert await migration_helper.async_initiate_migration(migration_data) + + # Check the ZHA config entry data is updated + assert config_entry.data == { + "device": { + "path": "socket://some/virtual_port", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "ezsp", + } + assert config_entry.title == "Test Updated" + + await migration_helper.async_finish_migration() + + +@patch( + "homeassistant.components.zha.radio_manager.ZhaRadioManager.detect_radio_type", + mock_detect_radio_type(), +) +@patch("homeassistant.components.zha.async_setup_entry", AsyncMock(return_value=True)) +async def test_migrate_matching_port_usb( + hass: HomeAssistant, + mock_connect_zigpy_app, +) -> None: + """Test automatic migration.""" + # Setup the config entry + config_entry = MockConfigEntry( + data={"device": {"path": "/dev/ttyTEST123"}, "radio_type": "ezsp"}, + domain=DOMAIN, + options={}, + title="Test", + version=3, + ) + config_entry.add_to_hass(hass) + + migration_data = { + "new_discovery_info": { + "name": "Test Updated", "port": { - "path": "/dev/ttyTEST123", + "path": "socket://some/virtual_port", "baudrate": 115200, "flow_control": "hardware", }, "radio_type": "efr32", }, + "old_discovery_info": { + "usb": UsbServiceInfo("/dev/ttyTEST123", "blah", "blah", None, None, None) + }, } migration_helper = radio_manager.ZhaMultiPANMigrationHelper(hass, config_entry) @@ -178,13 +233,15 @@ async def test_migrate_matching_port_config_entry_not_loaded( "radio_type": "efr32", }, "old_discovery_info": { - "name": "Test", - "port": { - "path": "/dev/ttyTEST123", - "baudrate": 115200, - "flow_control": "hardware", - }, - "radio_type": "efr32", + "hw": { + "name": "Test", + "port": { + "path": "/dev/ttyTEST123", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + } }, } @@ -236,13 +293,15 @@ async def test_migrate_matching_port_retry( "radio_type": "efr32", }, "old_discovery_info": { - "name": "Test", - "port": { - "path": "/dev/ttyTEST123", - "baudrate": 115200, - "flow_control": "hardware", - }, - "radio_type": "efr32", + "hw": { + "name": "Test", + "port": { + "path": "/dev/ttyTEST123", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + } }, } @@ -290,13 +349,15 @@ async def test_migrate_non_matching_port( "radio_type": "efr32", }, "old_discovery_info": { - "name": "Test", - "port": { - "path": "/dev/ttyTEST456", - "baudrate": 115200, - "flow_control": "hardware", - }, - "radio_type": "efr32", + "hw": { + "name": "Test", + "port": { + "path": "/dev/ttyTEST456", + "baudrate": 115200, + "flow_control": "hardware", + }, + "radio_type": "efr32", + } }, } From c576a68d336bc91fd82c299d9b3e5dfdc1c14960 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Tue, 29 Nov 2022 22:36:36 +0100 Subject: [PATCH 0886/1033] Upgrade pytest-aiohttp (#82475) * Upgrade pytest-aiohttp * Make sure executors, tasks and timers are closed Some test will trigger warnings on garbage collect, these warnings spills over into next test. Some test trigger tasks that raise errors on shutdown, these spill over into next test. This is to mimic older pytest-aiohttp and it's behaviour on test cleanup. Discussions on similar changes for pytest-aiohttp are here: https://github.com/pytest-dev/pytest-asyncio/pull/309 * Replace loop with event_loop * Make sure time is frozen for tests * Make sure the ConditionType is not async /home-assistant/homeassistant/helpers/template.py:2082: RuntimeWarning: coroutine 'AsyncMockMixin._execute_mock_call' was never awaited def wrapper(*args, **kwargs): Enable tracemalloc to get traceback where the object was allocated. See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info. * Increase litejet press tests with a factor 10 The times are simulated anyway, and we can't stop the normal event from occuring. * Use async handlers for aiohttp tests/components/motioneye/test_camera.py::test_get_still_image_from_camera tests/components/motioneye/test_camera.py::test_get_still_image_from_camera tests/components/motioneye/test_camera.py::test_get_stream_from_camera tests/components/motioneye/test_camera.py::test_get_stream_from_camera tests/components/motioneye/test_camera.py::test_camera_option_stream_url_template tests/components/motioneye/test_camera.py::test_camera_option_stream_url_template /Users/joakim/src/hass/home-assistant/venv/lib/python3.9/site-packages/aiohttp/web_urldispatcher.py:189: DeprecationWarning: Bare functions are deprecated, use async ones warnings.warn( * Switch to freezegun in modbus tests The tests allowed clock to tick in between steps * Make sure skybell object are fully mocked Old tests would trigger attempts to post to could services: ``` DEBUG:aioskybell:HTTP post https://cloud.myskybell.com/api/v3/login/ Request with headers: {'content-type': 'application/json', 'accept': '*/*', 'x-skybell-app-id': 'd2b542c7-a7e4-4e1e-b77d-2b76911c7c46', 'x-skybell-client-id': '1f36a3c0-6dee-4997-a6db-4e1c67338e57'} ``` * Fix sorting that broke after rebase --- homeassistant/package_constraints.txt | 3 - pyproject.toml | 1 + requirements_test.txt | 3 +- script/gen_requirements_all.py | 3 - tests/auth/test_init.py | 2 +- tests/common.py | 2 +- .../components/alexa/test_flash_briefings.py | 3 +- tests/components/alexa/test_intent.py | 3 +- tests/components/auth/conftest.py | 2 +- tests/components/config/test_init.py | 2 +- tests/components/emulated_hue/test_upnp.py | 2 +- .../components/emulated_roku/test_binding.py | 2 +- tests/components/energy/test_sensor.py | 8 + tests/components/fido/test_sensor.py | 2 +- tests/components/frontend/test_init.py | 2 +- tests/components/geofency/test_init.py | 4 +- .../google_assistant/test_google_assistant.py | 7 +- tests/components/gpslogger/test_init.py | 4 +- tests/components/homekit/conftest.py | 12 +- tests/components/http/conftest.py | 2 +- tests/components/http/test_cors.py | 4 +- .../components/image_processing/test_init.py | 2 +- tests/components/litejet/test_trigger.py | 44 ++--- tests/components/locative/test_init.py | 2 +- .../components/meraki/test_device_tracker.py | 3 +- tests/components/modbus/test_init.py | 55 +++---- tests/components/motioneye/test_camera.py | 10 +- tests/components/nest/conftest.py | 2 +- tests/components/nest/test_api.py | 8 +- tests/components/recorder/test_util.py | 2 +- .../components/rss_feed_template/test_init.py | 3 +- tests/components/skybell/__init__.py | 18 --- tests/components/skybell/conftest.py | 25 +++ tests/components/skybell/test_config_flow.py | 152 +++++++++--------- tests/components/traccar/test_init.py | 4 +- tests/conftest.py | 37 ++++- tests/helpers/test_script.py | 3 +- tests/scripts/test_auth.py | 2 +- tests/scripts/test_check_config.py | 12 +- tests/test_bootstrap.py | 14 +- tests/test_core.py | 16 +- tests/util/test_location.py | 2 +- 42 files changed, 263 insertions(+), 226 deletions(-) create mode 100644 tests/components/skybell/conftest.py diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 532bd23428c..dbd749d7ca7 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -98,9 +98,6 @@ hyperframe>=5.2.0 # Ensure we run compatible with musllinux build env numpy==1.23.2 -# pytest_asyncio breaks our test suite. We rely on pytest-aiohttp instead -pytest_asyncio==1000000000.0.0 - # Prevent dependency conflicts between sisyphus-control and aioambient # until upper bounds for sisyphus-control have been updated # https://github.com/jkeljo/sisyphus-control/issues/6 diff --git a/pyproject.toml b/pyproject.toml index a5b6c205c89..710b025bee3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -227,3 +227,4 @@ norecursedirs = [ ] log_format = "%(asctime)s.%(msecs)03d %(levelname)-8s %(threadName)s %(name)s:%(filename)s:%(lineno)s %(message)s" log_date_format = "%Y-%m-%d %H:%M:%S" +asyncio_mode = "auto" diff --git a/requirements_test.txt b/requirements_test.txt index 38ea4aa37ab..7db57fb5bcf 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -16,7 +16,8 @@ mypy==0.991 pre-commit==2.20.0 pylint==2.15.6 pipdeptree==2.3.1 -pytest-aiohttp==0.3.0 +pytest-asyncio==0.20.2 +pytest-aiohttp==1.0.4 pytest-cov==3.0.0 pytest-freezegun==0.4.2 pytest-socket==0.5.1 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 6d547ab9fed..355caa6cb62 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -110,9 +110,6 @@ hyperframe>=5.2.0 # Ensure we run compatible with musllinux build env numpy==1.23.2 -# pytest_asyncio breaks our test suite. We rely on pytest-aiohttp instead -pytest_asyncio==1000000000.0.0 - # Prevent dependency conflicts between sisyphus-control and aioambient # until upper bounds for sisyphus-control have been updated # https://github.com/jkeljo/sisyphus-control/issues/6 diff --git a/tests/auth/test_init.py b/tests/auth/test_init.py index 49c2c776684..f8ba8108f59 100644 --- a/tests/auth/test_init.py +++ b/tests/auth/test_init.py @@ -28,7 +28,7 @@ from tests.common import ( @pytest.fixture -def mock_hass(loop): +def mock_hass(event_loop): """Home Assistant mock with minimum amount of data set to make it work with auth.""" hass = Mock() hass.config.skip_pip = True diff --git a/tests/common.py b/tests/common.py index b7a26cb5f6d..f32dbd11081 100644 --- a/tests/common.py +++ b/tests/common.py @@ -160,7 +160,7 @@ def get_test_home_assistant(): # pylint: disable=protected-access -async def async_test_home_assistant(loop, load_registries=True): +async def async_test_home_assistant(event_loop, load_registries=True): """Return a Home Assistant object pointing at test config dir.""" hass = ha.HomeAssistant() store = auth_store.AuthStore(hass) diff --git a/tests/components/alexa/test_flash_briefings.py b/tests/components/alexa/test_flash_briefings.py index 8b9c91e28b5..499848f01db 100644 --- a/tests/components/alexa/test_flash_briefings.py +++ b/tests/components/alexa/test_flash_briefings.py @@ -21,8 +21,9 @@ NPR_NEWS_MP3_URL = "https://pd.npr.org/anon.npr-mp3/npr/news/newscast.mp3" @pytest.fixture -def alexa_client(loop, hass, hass_client): +def alexa_client(event_loop, hass, hass_client): """Initialize a Home Assistant server for testing this module.""" + loop = event_loop @callback def mock_service(call): diff --git a/tests/components/alexa/test_intent.py b/tests/components/alexa/test_intent.py index 9c71bc32e4d..54708e9d0f0 100644 --- a/tests/components/alexa/test_intent.py +++ b/tests/components/alexa/test_intent.py @@ -27,8 +27,9 @@ NPR_NEWS_MP3_URL = "https://pd.npr.org/anon.npr-mp3/npr/news/newscast.mp3" @pytest.fixture -def alexa_client(loop, hass, hass_client): +def alexa_client(event_loop, hass, hass_client): """Initialize a Home Assistant server for testing this module.""" + loop = event_loop @callback def mock_service(call): diff --git a/tests/components/auth/conftest.py b/tests/components/auth/conftest.py index 867f44d9f15..b7e69a44a0d 100644 --- a/tests/components/auth/conftest.py +++ b/tests/components/auth/conftest.py @@ -3,6 +3,6 @@ import pytest @pytest.fixture -def aiohttp_client(loop, aiohttp_client, socket_enabled): +def aiohttp_client(event_loop, aiohttp_client, socket_enabled): """Return aiohttp_client and allow opening sockets.""" return aiohttp_client diff --git a/tests/components/config/test_init.py b/tests/components/config/test_init.py index 2d5610cadfb..9a54c55ff9e 100644 --- a/tests/components/config/test_init.py +++ b/tests/components/config/test_init.py @@ -2,7 +2,7 @@ from homeassistant.setup import async_setup_component -async def test_config_setup(hass, loop): +async def test_config_setup(hass, event_loop): """Test it sets up hassbian.""" await async_setup_component(hass, "config", {}) assert "config" in hass.config.components diff --git a/tests/components/emulated_hue/test_upnp.py b/tests/components/emulated_hue/test_upnp.py index ce7f013963c..770f636098c 100644 --- a/tests/components/emulated_hue/test_upnp.py +++ b/tests/components/emulated_hue/test_upnp.py @@ -31,7 +31,7 @@ class MockTransport: @pytest.fixture -def aiohttp_client(loop, aiohttp_client, socket_enabled): +def aiohttp_client(event_loop, aiohttp_client, socket_enabled): """Return aiohttp_client and allow opening sockets.""" return aiohttp_client diff --git a/tests/components/emulated_roku/test_binding.py b/tests/components/emulated_roku/test_binding.py index 5afee6f6cc3..8adf1a18f19 100644 --- a/tests/components/emulated_roku/test_binding.py +++ b/tests/components/emulated_roku/test_binding.py @@ -25,7 +25,7 @@ async def test_events_fired_properly(hass): roku_event_handler = None def instantiate( - loop, + event_loop, handler, roku_usn, host_ip, diff --git a/tests/components/energy/test_sensor.py b/tests/components/energy/test_sensor.py index 0108dd1de76..636eb74e4d3 100644 --- a/tests/components/energy/test_sensor.py +++ b/tests/components/energy/test_sensor.py @@ -3,6 +3,7 @@ import copy from datetime import timedelta from unittest.mock import patch +from freezegun import freeze_time import pytest from homeassistant.components.energy import data @@ -41,6 +42,13 @@ async def setup_integration(recorder_mock): return setup_integration +@pytest.fixture(autouse=True) +@freeze_time("2022-04-19 07:53:05") +def frozen_time(): + """Freeze clock for tests.""" + yield + + def get_statistics_for_entity(statistics_results, entity_id): """Get statistics for a certain entity, or None if there is none.""" for statistics_result in statistics_results: diff --git a/tests/components/fido/test_sensor.py b/tests/components/fido/test_sensor.py index 0ab1de94117..13d8515f8f2 100644 --- a/tests/components/fido/test_sensor.py +++ b/tests/components/fido/test_sensor.py @@ -38,7 +38,7 @@ class FidoClientMockError(FidoClientMock): raise PyFidoError("Fake Error") -async def test_fido_sensor(loop, hass): +async def test_fido_sensor(event_loop, hass): """Test the Fido number sensor.""" with patch("homeassistant.components.fido.sensor.FidoClient", new=FidoClientMock): config = { diff --git a/tests/components/frontend/test_init.py b/tests/components/frontend/test_init.py index 661b3ace38a..6bff327d397 100644 --- a/tests/components/frontend/test_init.py +++ b/tests/components/frontend/test_init.py @@ -80,7 +80,7 @@ async def frontend_themes(hass): @pytest.fixture -def aiohttp_client(loop, aiohttp_client, socket_enabled): +def aiohttp_client(event_loop, aiohttp_client, socket_enabled): """Return aiohttp_client and allow opening sockets.""" return aiohttp_client diff --git a/tests/components/geofency/test_init.py b/tests/components/geofency/test_init.py index 9959023e8a6..9aaa356085b 100644 --- a/tests/components/geofency/test_init.py +++ b/tests/components/geofency/test_init.py @@ -117,7 +117,7 @@ def mock_dev_track(mock_device_tracker_conf): @pytest.fixture -async def geofency_client(loop, hass, hass_client_no_auth): +async def geofency_client(event_loop, hass, hass_client_no_auth): """Geofency mock client (unauthenticated).""" assert await async_setup_component( @@ -130,7 +130,7 @@ async def geofency_client(loop, hass, hass_client_no_auth): @pytest.fixture(autouse=True) -async def setup_zones(loop, hass): +async def setup_zones(event_loop, hass): """Set up Zone config in HA.""" assert await async_setup_component( hass, diff --git a/tests/components/google_assistant/test_google_assistant.py b/tests/components/google_assistant/test_google_assistant.py index 3fd35846d7e..97484f31341 100644 --- a/tests/components/google_assistant/test_google_assistant.py +++ b/tests/components/google_assistant/test_google_assistant.py @@ -42,8 +42,9 @@ def auth_header(hass_access_token): @pytest.fixture -def assistant_client(loop, hass, hass_client_no_auth): +def assistant_client(event_loop, hass, hass_client_no_auth): """Create web client for the Google Assistant API.""" + loop = event_loop loop.run_until_complete( setup.async_setup_component( hass, @@ -66,8 +67,10 @@ def assistant_client(loop, hass, hass_client_no_auth): @pytest.fixture -def hass_fixture(loop, hass): +def hass_fixture(event_loop, hass): """Set up a Home Assistant instance for these tests.""" + loop = event_loop + # We need to do this to get access to homeassistant/turn_(on,off) loop.run_until_complete(setup.async_setup_component(hass, core.DOMAIN, {})) diff --git a/tests/components/gpslogger/test_init.py b/tests/components/gpslogger/test_init.py index 4d246cff589..77357065a1a 100644 --- a/tests/components/gpslogger/test_init.py +++ b/tests/components/gpslogger/test_init.py @@ -26,7 +26,7 @@ def mock_dev_track(mock_device_tracker_conf): @pytest.fixture -async def gpslogger_client(loop, hass, hass_client_no_auth): +async def gpslogger_client(event_loop, hass, hass_client_no_auth): """Mock client for GPSLogger (unauthenticated).""" assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) @@ -38,7 +38,7 @@ async def gpslogger_client(loop, hass, hass_client_no_auth): @pytest.fixture(autouse=True) -async def setup_zones(loop, hass): +async def setup_zones(event_loop, hass): """Set up Zone config in HA.""" assert await async_setup_component( hass, diff --git a/tests/components/homekit/conftest.py b/tests/components/homekit/conftest.py index b0422a40f72..2800dfb9be7 100644 --- a/tests/components/homekit/conftest.py +++ b/tests/components/homekit/conftest.py @@ -21,7 +21,7 @@ def iid_storage(hass): @pytest.fixture() -def run_driver(hass, loop, iid_storage): +def run_driver(hass, event_loop, iid_storage): """Return a custom AccessoryDriver instance for HomeKit accessory init. This mock does not mock async_stop, so the driver will not be stopped @@ -41,12 +41,12 @@ def run_driver(hass, loop, iid_storage): bridge_name=BRIDGE_NAME, iid_storage=iid_storage, address="127.0.0.1", - loop=loop, + loop=event_loop, ) @pytest.fixture -def hk_driver(hass, loop, iid_storage): +def hk_driver(hass, event_loop, iid_storage): """Return a custom AccessoryDriver instance for HomeKit accessory init.""" with patch("pyhap.accessory_driver.AsyncZeroconf"), patch( "pyhap.accessory_driver.AccessoryEncoder" @@ -65,12 +65,12 @@ def hk_driver(hass, loop, iid_storage): bridge_name=BRIDGE_NAME, iid_storage=iid_storage, address="127.0.0.1", - loop=loop, + loop=event_loop, ) @pytest.fixture -def mock_hap(hass, loop, iid_storage, mock_zeroconf): +def mock_hap(hass, event_loop, iid_storage, mock_zeroconf): """Return a custom AccessoryDriver instance for HomeKit accessory init.""" with patch("pyhap.accessory_driver.AsyncZeroconf"), patch( "pyhap.accessory_driver.AccessoryEncoder" @@ -93,7 +93,7 @@ def mock_hap(hass, loop, iid_storage, mock_zeroconf): bridge_name=BRIDGE_NAME, iid_storage=iid_storage, address="127.0.0.1", - loop=loop, + loop=event_loop, ) diff --git a/tests/components/http/conftest.py b/tests/components/http/conftest.py index c796ec50b51..ed2c78bafd7 100644 --- a/tests/components/http/conftest.py +++ b/tests/components/http/conftest.py @@ -3,6 +3,6 @@ import pytest @pytest.fixture -def aiohttp_client(loop, aiohttp_client, socket_enabled): +def aiohttp_client(event_loop, aiohttp_client, socket_enabled): """Return aiohttp_client and allow opening sockets.""" return aiohttp_client diff --git a/tests/components/http/test_cors.py b/tests/components/http/test_cors.py index 599df194195..8de081c840a 100644 --- a/tests/components/http/test_cors.py +++ b/tests/components/http/test_cors.py @@ -49,12 +49,12 @@ async def mock_handler(request): @pytest.fixture -def client(loop, aiohttp_client): +def client(event_loop, aiohttp_client): """Fixture to set up a web.Application.""" app = web.Application() setup_cors(app, [TRUSTED_ORIGIN]) app["allow_configured_cors"](app.router.add_get("/", mock_handler)) - return loop.run_until_complete(aiohttp_client(app)) + return event_loop.run_until_complete(aiohttp_client(app)) async def test_cors_requests(client): diff --git a/tests/components/image_processing/test_init.py b/tests/components/image_processing/test_init.py index 9953df041ae..37f84fb971f 100644 --- a/tests/components/image_processing/test_init.py +++ b/tests/components/image_processing/test_init.py @@ -15,7 +15,7 @@ from tests.common import assert_setup_component, async_capture_events @pytest.fixture -def aiohttp_unused_port(loop, aiohttp_unused_port, socket_enabled): +def aiohttp_unused_port(event_loop, aiohttp_unused_port, socket_enabled): """Return aiohttp_unused_port and allow opening sockets.""" return aiohttp_unused_port diff --git a/tests/components/litejet/test_trigger.py b/tests/components/litejet/test_trigger.py index 152d11b0c23..86713b51489 100644 --- a/tests/components/litejet/test_trigger.py +++ b/tests/components/litejet/test_trigger.py @@ -113,12 +113,12 @@ async def test_held_more_than_short(hass, calls, mock_litejet): { "platform": "litejet", "number": ENTITY_OTHER_SWITCH_NUMBER, - "held_more_than": {"milliseconds": "200"}, + "held_more_than": {"milliseconds": "2000"}, }, ) await simulate_press(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) - await simulate_time(hass, mock_litejet, timedelta(seconds=0.1)) + await simulate_time(hass, mock_litejet, timedelta(seconds=1)) await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) assert len(calls) == 0 @@ -130,13 +130,13 @@ async def test_held_more_than_long(hass, calls, mock_litejet): { "platform": "litejet", "number": ENTITY_OTHER_SWITCH_NUMBER, - "held_more_than": {"milliseconds": "200"}, + "held_more_than": {"milliseconds": "2000"}, }, ) await simulate_press(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) assert len(calls) == 0 - await simulate_time(hass, mock_litejet, timedelta(seconds=0.3)) + await simulate_time(hass, mock_litejet, timedelta(seconds=3)) assert len(calls) == 1 assert calls[0].data["id"] == 0 await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) @@ -150,12 +150,12 @@ async def test_held_less_than_short(hass, calls, mock_litejet): { "platform": "litejet", "number": ENTITY_OTHER_SWITCH_NUMBER, - "held_less_than": {"milliseconds": "200"}, + "held_less_than": {"milliseconds": "2000"}, }, ) await simulate_press(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) - await simulate_time(hass, mock_litejet, timedelta(seconds=0.1)) + await simulate_time(hass, mock_litejet, timedelta(seconds=1)) assert len(calls) == 0 await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) assert len(calls) == 1 @@ -169,13 +169,13 @@ async def test_held_less_than_long(hass, calls, mock_litejet): { "platform": "litejet", "number": ENTITY_OTHER_SWITCH_NUMBER, - "held_less_than": {"milliseconds": "200"}, + "held_less_than": {"milliseconds": "2000"}, }, ) await simulate_press(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) assert len(calls) == 0 - await simulate_time(hass, mock_litejet, timedelta(seconds=0.3)) + await simulate_time(hass, mock_litejet, timedelta(seconds=3)) assert len(calls) == 0 await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) assert len(calls) == 0 @@ -188,13 +188,13 @@ async def test_held_in_range_short(hass, calls, mock_litejet): { "platform": "litejet", "number": ENTITY_OTHER_SWITCH_NUMBER, - "held_more_than": {"milliseconds": "100"}, - "held_less_than": {"milliseconds": "300"}, + "held_more_than": {"milliseconds": "1000"}, + "held_less_than": {"milliseconds": "3000"}, }, ) await simulate_press(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) - await simulate_time(hass, mock_litejet, timedelta(seconds=0.05)) + await simulate_time(hass, mock_litejet, timedelta(seconds=0.5)) await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) assert len(calls) == 0 @@ -206,14 +206,14 @@ async def test_held_in_range_just_right(hass, calls, mock_litejet): { "platform": "litejet", "number": ENTITY_OTHER_SWITCH_NUMBER, - "held_more_than": {"milliseconds": "100"}, - "held_less_than": {"milliseconds": "300"}, + "held_more_than": {"milliseconds": "1000"}, + "held_less_than": {"milliseconds": "3000"}, }, ) await simulate_press(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) assert len(calls) == 0 - await simulate_time(hass, mock_litejet, timedelta(seconds=0.2)) + await simulate_time(hass, mock_litejet, timedelta(seconds=2)) assert len(calls) == 0 await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) assert len(calls) == 1 @@ -227,14 +227,14 @@ async def test_held_in_range_long(hass, calls, mock_litejet): { "platform": "litejet", "number": ENTITY_OTHER_SWITCH_NUMBER, - "held_more_than": {"milliseconds": "100"}, - "held_less_than": {"milliseconds": "300"}, + "held_more_than": {"milliseconds": "1000"}, + "held_less_than": {"milliseconds": "3000"}, }, ) await simulate_press(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) assert len(calls) == 0 - await simulate_time(hass, mock_litejet, timedelta(seconds=0.4)) + await simulate_time(hass, mock_litejet, timedelta(seconds=4)) assert len(calls) == 0 await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) assert len(calls) == 0 @@ -247,8 +247,8 @@ async def test_reload(hass, calls, mock_litejet): { "platform": "litejet", "number": ENTITY_OTHER_SWITCH_NUMBER, - "held_more_than": {"milliseconds": "100"}, - "held_less_than": {"milliseconds": "300"}, + "held_more_than": {"milliseconds": "1000"}, + "held_less_than": {"milliseconds": "3000"}, }, ) @@ -260,7 +260,7 @@ async def test_reload(hass, calls, mock_litejet): "trigger": { "platform": "litejet", "number": ENTITY_OTHER_SWITCH_NUMBER, - "held_more_than": {"milliseconds": "1000"}, + "held_more_than": {"milliseconds": "10000"}, }, "action": {"service": "test.automation"}, } @@ -275,7 +275,7 @@ async def test_reload(hass, calls, mock_litejet): await simulate_press(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) assert len(calls) == 0 - await simulate_time(hass, mock_litejet, timedelta(seconds=0.5)) + await simulate_time(hass, mock_litejet, timedelta(seconds=5)) assert len(calls) == 0 - await simulate_time(hass, mock_litejet, timedelta(seconds=1.25)) + await simulate_time(hass, mock_litejet, timedelta(seconds=12.5)) assert len(calls) == 1 diff --git a/tests/components/locative/test_init.py b/tests/components/locative/test_init.py index 5f608803e4a..a0af04145b0 100644 --- a/tests/components/locative/test_init.py +++ b/tests/components/locative/test_init.py @@ -21,7 +21,7 @@ def mock_dev_track(mock_device_tracker_conf): @pytest.fixture -async def locative_client(loop, hass, hass_client): +async def locative_client(event_loop, hass, hass_client): """Locative mock client.""" assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) await hass.async_block_till_done() diff --git a/tests/components/meraki/test_device_tracker.py b/tests/components/meraki/test_device_tracker.py index 47265444105..e95241cbbc7 100644 --- a/tests/components/meraki/test_device_tracker.py +++ b/tests/components/meraki/test_device_tracker.py @@ -15,8 +15,9 @@ from homeassistant.setup import async_setup_component @pytest.fixture -def meraki_client(loop, hass, hass_client): +def meraki_client(event_loop, hass, hass_client): """Meraki mock client.""" + loop = event_loop assert loop.run_until_complete( async_setup_component( hass, diff --git a/tests/components/modbus/test_init.py b/tests/components/modbus/test_init.py index 73b1f9743c2..3b704eff161 100644 --- a/tests/components/modbus/test_init.py +++ b/tests/components/modbus/test_init.py @@ -16,6 +16,7 @@ from datetime import timedelta import logging from unittest import mock +from freezegun.api import FrozenDateTimeFactory from pymodbus.exceptions import ModbusException from pymodbus.pdu import ExceptionResponse, IllegalFunctionRequest import pytest @@ -542,6 +543,7 @@ async def mock_modbus_read_pymodbus_fixture( do_exception, caplog, mock_pymodbus, + freezer: FrozenDateTimeFactory, ): """Load integration modbus using mocked pymodbus.""" caplog.clear() @@ -573,16 +575,13 @@ async def mock_modbus_read_pymodbus_fixture( } ], } - now = dt_util.utcnow() - with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now): - assert await async_setup_component(hass, DOMAIN, config) is True - await hass.async_block_till_done() + assert await async_setup_component(hass, DOMAIN, config) is True + await hass.async_block_till_done() assert DOMAIN in hass.config.components assert caplog.text == "" - now = now + timedelta(seconds=DEFAULT_SCAN_INTERVAL + 60) - with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now): - async_fire_time_changed(hass, now) - await hass.async_block_till_done() + freezer.tick(timedelta(seconds=DEFAULT_SCAN_INTERVAL + 60)) + async_fire_time_changed(hass) + await hass.async_block_till_done() yield mock_pymodbus @@ -690,7 +689,7 @@ async def test_pymodbus_connect_fail(hass, caplog, mock_pymodbus): assert ExceptionMessage in caplog.text -async def test_delay(hass, mock_pymodbus): +async def test_delay(hass, mock_pymodbus, freezer: FrozenDateTimeFactory): """Run test for startup delay.""" # the purpose of this test is to test startup delay @@ -720,11 +719,8 @@ async def test_delay(hass, mock_pymodbus): } mock_pymodbus.read_coils.return_value = ReadResult([0x01]) start_time = dt_util.utcnow() - with mock.patch( - "homeassistant.helpers.event.dt_util.utcnow", return_value=start_time - ): - assert await async_setup_component(hass, DOMAIN, config) is True - await hass.async_block_till_done() + assert await async_setup_component(hass, DOMAIN, config) is True + await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_UNKNOWN time_sensor_active = start_time + timedelta(seconds=2) @@ -736,19 +732,15 @@ async def test_delay(hass, mock_pymodbus): # This test assumed listeners are always fired at 0 # microseconds which is impossible in production so # we use 999999 microseconds to simulate the real world. - now += timedelta(seconds=1, microseconds=999999) - with mock.patch( - "homeassistant.helpers.event.dt_util.utcnow", - return_value=now, - autospec=True, - ): - async_fire_time_changed(hass, now) - await hass.async_block_till_done() - if now > time_sensor_active: - if now <= time_after_delay: - assert hass.states.get(entity_id).state == STATE_UNAVAILABLE - elif now > time_after_scan: - assert hass.states.get(entity_id).state == STATE_ON + freezer.tick(timedelta(seconds=1, microseconds=999999)) + now = dt_util.utcnow() + async_fire_time_changed(hass, now) + await hass.async_block_till_done() + if now > time_sensor_active: + if now <= time_after_delay: + assert hass.states.get(entity_id).state == STATE_UNAVAILABLE + elif now > time_after_scan: + assert hass.states.get(entity_id).state == STATE_ON @pytest.mark.parametrize( @@ -853,19 +845,20 @@ async def test_write_no_client(hass, mock_modbus): @pytest.mark.parametrize("do_config", [{}]) -async def test_integration_reload(hass, caplog, mock_modbus): +async def test_integration_reload( + hass, caplog, mock_modbus, freezer: FrozenDateTimeFactory +): """Run test for integration reload.""" caplog.set_level(logging.INFO) caplog.clear() yaml_path = get_fixture_path("configuration.yaml", "modbus") - now = dt_util.utcnow() with mock.patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path): await hass.services.async_call(DOMAIN, SERVICE_RELOAD, blocking=True) await hass.async_block_till_done() for i in range(4): - now = now + timedelta(seconds=1) - async_fire_time_changed(hass, now) + freezer.tick(timedelta(seconds=1)) + async_fire_time_changed(hass) await hass.async_block_till_done() assert "Modbus reloading" in caplog.text diff --git a/tests/components/motioneye/test_camera.py b/tests/components/motioneye/test_camera.py index 3e2db3bf897..d70405cd297 100644 --- a/tests/components/motioneye/test_camera.py +++ b/tests/components/motioneye/test_camera.py @@ -62,7 +62,7 @@ from tests.common import async_fire_time_changed @pytest.fixture -def aiohttp_server(loop, aiohttp_server, socket_enabled): +def aiohttp_server(event_loop, aiohttp_server, socket_enabled): """Return aiohttp_server and allow opening sockets.""" return aiohttp_server @@ -218,7 +218,7 @@ async def test_get_still_image_from_camera( ) -> None: """Test getting a still image.""" - image_handler = Mock(return_value="") + image_handler = AsyncMock(return_value="") app = web.Application() app.add_routes( @@ -258,7 +258,7 @@ async def test_get_still_image_from_camera( async def test_get_stream_from_camera(aiohttp_server: Any, hass: HomeAssistant) -> None: """Test getting a stream.""" - stream_handler = Mock(return_value="") + stream_handler = AsyncMock(return_value="") app = web.Application() app.add_routes([web.get("/", stream_handler)]) stream_server = await aiohttp_server(app) @@ -341,7 +341,7 @@ async def test_camera_option_stream_url_template( """Verify camera with a stream URL template option.""" client = create_mock_motioneye_client() - stream_handler = Mock(return_value="") + stream_handler = AsyncMock(return_value="") app = web.Application() app.add_routes([web.get(f"/{TEST_CAMERA_NAME}/{TEST_CAMERA_ID}", stream_handler)]) stream_server = await aiohttp_server(app) @@ -371,7 +371,7 @@ async def test_camera_option_stream_url_template( # the expected exception, then verify the right handler was called. with pytest.raises(HTTPBadGateway): await async_get_mjpeg_stream(hass, Mock(), TEST_CAMERA_ENTITY_ID) - assert stream_handler.called + assert AsyncMock.called assert not client.get_camera_stream_url.called diff --git a/tests/components/nest/conftest.py b/tests/components/nest/conftest.py index 458685cde70..db89063553f 100644 --- a/tests/components/nest/conftest.py +++ b/tests/components/nest/conftest.py @@ -82,7 +82,7 @@ class FakeAuth(AbstractAuth): @pytest.fixture -def aiohttp_client(loop, aiohttp_client, socket_enabled): +def aiohttp_client(event_loop, aiohttp_client, socket_enabled): """Return aiohttp_client and allow opening sockets.""" return aiohttp_client diff --git a/tests/components/nest/test_api.py b/tests/components/nest/test_api.py index 7d88ba1d329..a998a08d0c4 100644 --- a/tests/components/nest/test_api.py +++ b/tests/components/nest/test_api.py @@ -53,7 +53,9 @@ async def test_auth(hass, aioclient_mock): # Prepare to capture credentials for Subscriber captured_creds = None - async def async_new_subscriber(creds, subscription_name, loop, async_callback): + async def async_new_subscriber( + creds, subscription_name, event_loop, async_callback + ): """Capture credentials for tests.""" nonlocal captured_creds captured_creds = creds @@ -112,7 +114,9 @@ async def test_auth_expired_token(hass, aioclient_mock): # Prepare to capture credentials for Subscriber captured_creds = None - async def async_new_subscriber(creds, subscription_name, loop, async_callback): + async def async_new_subscriber( + creds, subscription_name, event_loop, async_callback + ): """Capture credentials for tests.""" nonlocal captured_creds captured_creds = creds diff --git a/tests/components/recorder/test_util.py b/tests/components/recorder/test_util.py index 54f9bb9b9f2..ecdd729a163 100644 --- a/tests/components/recorder/test_util.py +++ b/tests/components/recorder/test_util.py @@ -110,7 +110,7 @@ def test_validate_or_move_away_sqlite_database(hass, tmpdir, caplog): async def test_last_run_was_recently_clean( - loop, async_setup_recorder_instance: SetupRecorderInstanceT, tmp_path + event_loop, async_setup_recorder_instance: SetupRecorderInstanceT, tmp_path ): """Test we can check if the last recorder run was recently clean.""" config = { diff --git a/tests/components/rss_feed_template/test_init.py b/tests/components/rss_feed_template/test_init.py index ffdb4e5ba9a..0ed22ac84fa 100644 --- a/tests/components/rss_feed_template/test_init.py +++ b/tests/components/rss_feed_template/test_init.py @@ -8,8 +8,9 @@ from homeassistant.setup import async_setup_component @pytest.fixture -def mock_http_client(loop, hass, hass_client): +def mock_http_client(event_loop, hass, hass_client): """Set up test fixture.""" + loop = event_loop config = { "rss_feed_template": { "testfeed": { diff --git a/tests/components/skybell/__init__.py b/tests/components/skybell/__init__.py index dd162ed5d80..fc049adcc3d 100644 --- a/tests/components/skybell/__init__.py +++ b/tests/components/skybell/__init__.py @@ -1,7 +1,5 @@ """Tests for the SkyBell integration.""" -from unittest.mock import AsyncMock, patch - from homeassistant.const import CONF_EMAIL, CONF_PASSWORD USERNAME = "user" @@ -12,19 +10,3 @@ CONF_CONFIG_FLOW = { CONF_EMAIL: USERNAME, CONF_PASSWORD: PASSWORD, } - - -def _patch_skybell_devices() -> None: - mocked_skybell = AsyncMock() - mocked_skybell.user_id = USER_ID - return patch( - "homeassistant.components.skybell.config_flow.Skybell.async_get_devices", - return_value=[mocked_skybell], - ) - - -def _patch_skybell() -> None: - return patch( - "homeassistant.components.skybell.config_flow.Skybell.async_send_request", - return_value={"id": USER_ID}, - ) diff --git a/tests/components/skybell/conftest.py b/tests/components/skybell/conftest.py new file mode 100644 index 00000000000..46337f612e1 --- /dev/null +++ b/tests/components/skybell/conftest.py @@ -0,0 +1,25 @@ +"""Test setup for the SkyBell integration.""" + +from unittest.mock import AsyncMock, patch + +from aioskybell import Skybell, SkybellDevice +from pytest import fixture + +from . import USER_ID + + +@fixture(autouse=True) +def skybell_mock(): + """Fixture for our skybell tests.""" + mocked_skybell_device = AsyncMock(spec=SkybellDevice) + + mocked_skybell = AsyncMock(spec=Skybell) + mocked_skybell.async_get_devices.return_value = [mocked_skybell_device] + mocked_skybell.async_send_request.return_value = {"id": USER_ID} + mocked_skybell.user_id = USER_ID + + with patch( + "homeassistant.components.skybell.config_flow.Skybell", + return_value=mocked_skybell, + ), patch("homeassistant.components.skybell.Skybell", return_value=mocked_skybell): + yield mocked_skybell diff --git a/tests/components/skybell/test_config_flow.py b/tests/components/skybell/test_config_flow.py index 9b7afd12c93..318819246a3 100644 --- a/tests/components/skybell/test_config_flow.py +++ b/tests/components/skybell/test_config_flow.py @@ -2,6 +2,7 @@ from unittest.mock import patch from aioskybell import exceptions +from pytest import fixture from homeassistant import config_entries from homeassistant.components.skybell.const import DOMAIN @@ -10,43 +11,39 @@ from homeassistant.const import CONF_PASSWORD, CONF_SOURCE from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType -from . import ( - CONF_CONFIG_FLOW, - PASSWORD, - USER_ID, - _patch_skybell, - _patch_skybell_devices, -) +from . import CONF_CONFIG_FLOW, PASSWORD, USER_ID from tests.common import MockConfigEntry -def _patch_setup_entry() -> None: - return patch( +@fixture(autouse=True) +def setup_entry() -> None: + """Make sure component doesn't initialize.""" + with patch( "homeassistant.components.skybell.async_setup_entry", return_value=True, - ) + ): + yield async def test_flow_user(hass: HomeAssistant) -> None: """Test that the user step works.""" - with _patch_skybell(), _patch_skybell_devices(), _patch_setup_entry(): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER} - ) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) - assert result["type"] == FlowResultType.FORM - assert result["step_id"] == "user" + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input=CONF_CONFIG_FLOW, - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input=CONF_CONFIG_FLOW, + ) - assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == "user" - assert result["data"] == CONF_CONFIG_FLOW - assert result["result"].unique_id == USER_ID + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "user" + assert result["data"] == CONF_CONFIG_FLOW + assert result["result"].unique_id == USER_ID async def test_flow_user_already_configured(hass: HomeAssistant) -> None: @@ -57,50 +54,48 @@ async def test_flow_user_already_configured(hass: HomeAssistant) -> None: ) entry.add_to_hass(hass) - with _patch_skybell(), _patch_skybell_devices(): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW - ) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW + ) assert result["type"] == FlowResultType.ABORT assert result["reason"] == "already_configured" -async def test_flow_user_cannot_connect(hass: HomeAssistant) -> None: +async def test_flow_user_cannot_connect(hass: HomeAssistant, skybell_mock) -> None: """Test user initialized flow with unreachable server.""" - with _patch_skybell() as skybell_mock: - skybell_mock.side_effect = exceptions.SkybellException(hass) - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW - ) - assert result["type"] == FlowResultType.FORM - assert result["step_id"] == "user" - assert result["errors"] == {"base": "cannot_connect"} + skybell_mock.async_initialize.side_effect = exceptions.SkybellException(hass) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {"base": "cannot_connect"} -async def test_invalid_credentials(hass: HomeAssistant) -> None: +async def test_invalid_credentials(hass: HomeAssistant, skybell_mock) -> None: """Test that invalid credentials throws an error.""" - with patch("homeassistant.components.skybell.Skybell.async_login") as skybell_mock: - skybell_mock.side_effect = exceptions.SkybellAuthenticationException(hass) - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW - ) + skybell_mock.async_initialize.side_effect = ( + exceptions.SkybellAuthenticationException(hass) + ) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW + ) - assert result["type"] == FlowResultType.FORM - assert result["step_id"] == "user" - assert result["errors"] == {"base": "invalid_auth"} + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {"base": "invalid_auth"} -async def test_flow_user_unknown_error(hass: HomeAssistant) -> None: +async def test_flow_user_unknown_error(hass: HomeAssistant, skybell_mock) -> None: """Test user initialized flow with unreachable server.""" - with _patch_skybell_devices() as skybell_mock: - skybell_mock.side_effect = Exception - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW - ) - assert result["type"] == FlowResultType.FORM - assert result["step_id"] == "user" - assert result["errors"] == {"base": "unknown"} + skybell_mock.async_initialize.side_effect = Exception + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {"base": "unknown"} async def test_step_reauth(hass: HomeAssistant) -> None: @@ -121,17 +116,15 @@ async def test_step_reauth(hass: HomeAssistant) -> None: assert result["type"] == FlowResultType.FORM assert result["step_id"] == "reauth_confirm" - with _patch_skybell(), _patch_skybell_devices(), _patch_setup_entry(): - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={CONF_PASSWORD: PASSWORD}, - ) - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "reauth_successful" + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={CONF_PASSWORD: PASSWORD}, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "reauth_successful" -async def test_step_reauth_failed(hass: HomeAssistant) -> None: +async def test_step_reauth_failed(hass: HomeAssistant, skybell_mock) -> None: """Test the reauth flow fails and recovers.""" entry = MockConfigEntry(domain=DOMAIN, unique_id=USER_ID, data=CONF_CONFIG_FLOW) entry.add_to_hass(hass) @@ -149,21 +142,22 @@ async def test_step_reauth_failed(hass: HomeAssistant) -> None: assert result["type"] == FlowResultType.FORM assert result["step_id"] == "reauth_confirm" - with patch("homeassistant.components.skybell.Skybell.async_login") as skybell_mock: - skybell_mock.side_effect = exceptions.SkybellAuthenticationException(hass) - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={CONF_PASSWORD: PASSWORD}, - ) + skybell_mock.async_initialize.side_effect = ( + exceptions.SkybellAuthenticationException(hass) + ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={CONF_PASSWORD: PASSWORD}, + ) - assert result["type"] == FlowResultType.FORM - assert result["errors"] == {"base": "invalid_auth"} + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {"base": "invalid_auth"} - with _patch_skybell(), _patch_skybell_devices(), _patch_setup_entry(): + skybell_mock.async_initialize.side_effect = None - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={CONF_PASSWORD: PASSWORD}, - ) - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "reauth_successful" + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={CONF_PASSWORD: PASSWORD}, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "reauth_successful" diff --git a/tests/components/traccar/test_init.py b/tests/components/traccar/test_init.py index 830670efc11..4c6c235a3aa 100644 --- a/tests/components/traccar/test_init.py +++ b/tests/components/traccar/test_init.py @@ -24,7 +24,7 @@ def mock_dev_track(mock_device_tracker_conf): @pytest.fixture(name="client") -async def traccar_client(loop, hass, hass_client_no_auth): +async def traccar_client(event_loop, hass, hass_client_no_auth): """Mock client for Traccar (unauthenticated).""" assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) @@ -36,7 +36,7 @@ async def traccar_client(loop, hass, hass_client_no_auth): @pytest.fixture(autouse=True) -async def setup_zones(loop, hass): +async def setup_zones(event_loop, hass): """Set up Zone config in HA.""" assert await async_setup_component( hass, diff --git a/tests/conftest.py b/tests/conftest.py index b6638968182..4dc988db8fc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,7 @@ import asyncio from collections.abc import AsyncGenerator, Callable, Generator from contextlib import asynccontextmanager import functools +import gc import itertools from json import JSONDecoder, loads import logging @@ -13,6 +14,7 @@ import ssl import threading from typing import Any from unittest.mock import AsyncMock, MagicMock, Mock, patch +import warnings from aiohttp import client from aiohttp.pytest_plugin import AiohttpClient @@ -188,12 +190,14 @@ util.get_local_ip = lambda: "127.0.0.1" @pytest.fixture(autouse=True) -def verify_cleanup(): +def verify_cleanup(event_loop: asyncio.AbstractEventLoop): """Verify that the test has cleaned up resources correctly.""" threads_before = frozenset(threading.enumerate()) - + tasks_before = asyncio.all_tasks(event_loop) yield + event_loop.run_until_complete(event_loop.shutdown_default_executor()) + if len(INSTANCES) >= 2: count = len(INSTANCES) for inst in INSTANCES: @@ -204,6 +208,26 @@ def verify_cleanup(): for thread in threads: assert isinstance(thread, threading._DummyThread) + # Warn and clean-up lingering tasks and timers + # before moving on to the next test. + tasks = asyncio.all_tasks(event_loop) - tasks_before + for task in tasks: + warnings.warn(f"Linger task after test {task}") + task.cancel() + if tasks: + event_loop.run_until_complete(asyncio.wait(tasks)) + + for handle in event_loop._scheduled: # pylint: disable=protected-access + if not handle.cancelled(): + warnings.warn(f"Lingering timer after test {handle}") + handle.cancel() + + # Make sure garbage collect run in same test as allocation + # this is to mimic the behavior of pytest-aiohttp, and is + # required to avoid warnings from spilling over into next + # test case. + gc.collect() + @pytest.fixture(autouse=True) def bcrypt_cost(): @@ -278,7 +302,7 @@ def aiohttp_client_cls(): @pytest.fixture def aiohttp_client( - loop: asyncio.AbstractEventLoop, + event_loop: asyncio.AbstractEventLoop, ) -> Generator[AiohttpClient, None, None]: """Override the default aiohttp_client since 3.x does not support aiohttp_client_cls. @@ -289,6 +313,7 @@ def aiohttp_client( aiohttp_client(server, **kwargs) aiohttp_client(raw_server, **kwargs) """ + loop = event_loop clients = [] async def go( @@ -335,9 +360,10 @@ def hass_fixture_setup(): @pytest.fixture -def hass(hass_fixture_setup, loop, load_registries, hass_storage, request): +def hass(hass_fixture_setup, event_loop, load_registries, hass_storage, request): """Fixture to provide a test instance of Home Assistant.""" + loop = event_loop hass_fixture_setup.append(True) orig_tz = dt_util.DEFAULT_TIME_ZONE @@ -382,7 +408,7 @@ def hass(hass_fixture_setup, loop, load_registries, hass_storage, request): @pytest.fixture -async def stop_hass(): +async def stop_hass(event_loop): """Make sure all hass are stopped.""" orig_hass = ha.HomeAssistant @@ -403,6 +429,7 @@ async def stop_hass(): with patch.object(hass_inst.loop, "stop"): await hass_inst.async_block_till_done() await hass_inst.async_stop(force=True) + await event_loop.shutdown_default_executor() @pytest.fixture diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index 519498f2e08..a5f3cc0cc91 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -8,7 +8,7 @@ import logging import operator from types import MappingProxyType from unittest import mock -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock, MagicMock, patch from async_timeout import timeout import pytest @@ -1734,6 +1734,7 @@ async def test_condition_created_once(async_from_config, hass): ) async_from_config.reset_mock() + async_from_config.return_value = MagicMock() hass.states.async_set("test.entity", "hello") await script_obj.async_run(context=Context()) diff --git a/tests/scripts/test_auth.py b/tests/scripts/test_auth.py index 3ab19450879..a5478d149ec 100644 --- a/tests/scripts/test_auth.py +++ b/tests/scripts/test_auth.py @@ -108,7 +108,7 @@ async def test_change_password_invalid_user(hass, provider, capsys, hass_storage data.validate_login("invalid-user", "new-pass") -def test_parsing_args(loop): +def test_parsing_args(event_loop): """Test we parse args correctly.""" called = False diff --git a/tests/scripts/test_check_config.py b/tests/scripts/test_check_config.py index 05ebb8fb0e5..fc35b28f7c9 100644 --- a/tests/scripts/test_check_config.py +++ b/tests/scripts/test_check_config.py @@ -43,7 +43,7 @@ def normalize_yaml_files(check_dict): return [key.replace(root, "...") for key in sorted(check_dict["yaml_files"].keys())] -def test_bad_core_config(mock_is_file, loop): +def test_bad_core_config(mock_is_file, event_loop): """Test a bad core config setup.""" files = {YAML_CONFIG_FILE: BAD_CORE_CONFIG} with patch_yaml_files(files): @@ -52,7 +52,7 @@ def test_bad_core_config(mock_is_file, loop): assert res["except"]["homeassistant"][1] == {"unit_system": "bad"} -def test_config_platform_valid(mock_is_file, loop): +def test_config_platform_valid(mock_is_file, event_loop): """Test a valid platform setup.""" files = {YAML_CONFIG_FILE: BASE_CONFIG + "light:\n platform: demo"} with patch_yaml_files(files): @@ -65,7 +65,7 @@ def test_config_platform_valid(mock_is_file, loop): assert len(res["yaml_files"]) == 1 -def test_component_platform_not_found(mock_is_file, loop): +def test_component_platform_not_found(mock_is_file, event_loop): """Test errors if component or platform not found.""" # Make sure they don't exist files = {YAML_CONFIG_FILE: BASE_CONFIG + "beer:"} @@ -96,7 +96,7 @@ def test_component_platform_not_found(mock_is_file, loop): assert len(res["yaml_files"]) == 1 -def test_secrets(mock_is_file, loop): +def test_secrets(mock_is_file, event_loop): """Test secrets config checking method.""" secrets_path = get_test_config_dir("secrets.yaml") @@ -127,7 +127,7 @@ def test_secrets(mock_is_file, loop): ] -def test_package_invalid(mock_is_file, loop): +def test_package_invalid(mock_is_file, event_loop): """Test an invalid package.""" files = { YAML_CONFIG_FILE: BASE_CONFIG + (" packages:\n p1:\n" ' group: ["a"]') @@ -145,7 +145,7 @@ def test_package_invalid(mock_is_file, loop): assert len(res["yaml_files"]) == 1 -def test_bootstrap_error(loop): +def test_bootstrap_error(event_loop): """Test a valid platform setup.""" files = {YAML_CONFIG_FILE: BASE_CONFIG + "automation: !include no.yaml"} with patch_yaml_files(files): diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index e51f4d315ee..c8365c86a9a 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -460,7 +460,7 @@ async def test_setup_hass( mock_ensure_config_exists, mock_process_ha_config_upgrade, caplog, - loop, + event_loop, ): """Test it works.""" verbose = Mock() @@ -511,7 +511,7 @@ async def test_setup_hass_takes_longer_than_log_slow_startup( mock_ensure_config_exists, mock_process_ha_config_upgrade, caplog, - loop, + event_loop, ): """Test it works.""" verbose = Mock() @@ -553,7 +553,7 @@ async def test_setup_hass_invalid_yaml( mock_mount_local_lib_path, mock_ensure_config_exists, mock_process_ha_config_upgrade, - loop, + event_loop, ): """Test it works.""" with patch( @@ -581,7 +581,7 @@ async def test_setup_hass_config_dir_nonexistent( mock_mount_local_lib_path, mock_ensure_config_exists, mock_process_ha_config_upgrade, - loop, + event_loop, ): """Test it works.""" mock_ensure_config_exists.return_value = False @@ -608,7 +608,7 @@ async def test_setup_hass_safe_mode( mock_mount_local_lib_path, mock_ensure_config_exists, mock_process_ha_config_upgrade, - loop, + event_loop, ): """Test it works.""" with patch("homeassistant.components.browser.setup") as browser_setup, patch( @@ -641,7 +641,7 @@ async def test_setup_hass_invalid_core_config( mock_mount_local_lib_path, mock_ensure_config_exists, mock_process_ha_config_upgrade, - loop, + event_loop, ): """Test it works.""" with patch( @@ -669,7 +669,7 @@ async def test_setup_safe_mode_if_no_frontend( mock_mount_local_lib_path, mock_ensure_config_exists, mock_process_ha_config_upgrade, - loop, + event_loop, ): """Test we setup safe mode if frontend didn't load.""" verbose = Mock() diff --git a/tests/test_core.py b/tests/test_core.py index 80f7c18d254..c5f153ad3bd 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -87,9 +87,9 @@ def test_async_add_hass_job_schedule_partial_callback(): assert len(hass.add_job.mock_calls) == 0 -def test_async_add_hass_job_schedule_coroutinefunction(loop): +def test_async_add_hass_job_schedule_coroutinefunction(event_loop): """Test that we schedule coroutines and add jobs to the job pool.""" - hass = MagicMock(loop=MagicMock(wraps=loop)) + hass = MagicMock(loop=MagicMock(wraps=event_loop)) async def job(): pass @@ -100,9 +100,9 @@ def test_async_add_hass_job_schedule_coroutinefunction(loop): assert len(hass.add_job.mock_calls) == 0 -def test_async_add_hass_job_schedule_partial_coroutinefunction(loop): +def test_async_add_hass_job_schedule_partial_coroutinefunction(event_loop): """Test that we schedule partial coros and add jobs to the job pool.""" - hass = MagicMock(loop=MagicMock(wraps=loop)) + hass = MagicMock(loop=MagicMock(wraps=event_loop)) async def job(): pass @@ -128,9 +128,9 @@ def test_async_add_job_add_hass_threaded_job_to_pool(): assert len(hass.loop.run_in_executor.mock_calls) == 1 -def test_async_create_task_schedule_coroutine(loop): +def test_async_create_task_schedule_coroutine(event_loop): """Test that we schedule coroutines and add jobs to the job pool.""" - hass = MagicMock(loop=MagicMock(wraps=loop)) + hass = MagicMock(loop=MagicMock(wraps=event_loop)) async def job(): pass @@ -1079,7 +1079,7 @@ async def test_bad_timezone_raises_value_error(hass): await hass.config.async_update(time_zone="not_a_timezone") -async def test_start_taking_too_long(loop, caplog): +async def test_start_taking_too_long(event_loop, caplog): """Test when async_start takes too long.""" hass = ha.HomeAssistant() caplog.set_level(logging.WARNING) @@ -1098,7 +1098,7 @@ async def test_start_taking_too_long(loop, caplog): assert hass.state == ha.CoreState.stopped -async def test_track_task_functions(loop): +async def test_track_task_functions(event_loop): """Test function to start/stop track task and initial state.""" hass = ha.HomeAssistant() try: diff --git a/tests/util/test_location.py b/tests/util/test_location.py index d8d86965733..51cd8d4388f 100644 --- a/tests/util/test_location.py +++ b/tests/util/test_location.py @@ -32,7 +32,7 @@ async def session(hass): @pytest.fixture -async def raising_session(loop): +async def raising_session(event_loop): """Return an aioclient session that only fails.""" return Mock(get=Mock(side_effect=aiohttp.ClientError)) From 73cd2636ddf2dd616e416ca59d48878e0b57a7c5 Mon Sep 17 00:00:00 2001 From: Nyro Date: Tue, 29 Nov 2022 23:08:13 +0100 Subject: [PATCH 0887/1033] Add Overkiz DomesticHotWaterProduction (#81538) * Port DomesticHotWaterProduction from HA-Tahoma * Add new domestic hot water controllable_name * Update DHWP to detect existing commands instead of devices * Update homeassistant/components/overkiz/water_heater_entities/domestic_hot_water_production.py Co-authored-by: Quentame * Update homeassistant/components/overkiz/water_heater_entities/domestic_hot_water_production.py Co-authored-by: Quentame * Improve read states and comment for overkiz DHWP * Remove DHWP_BOOST_MODES * Update DHWP to handle more device correctly * Improve current_temperature for bogus device * Init overkiz_to_operation_mode for DHWP * Update command to be correct * Upgrade pyoverkiz and use new constants for DHWP * Remove .tow/.package.lock Co-authored-by: Quentame --- homeassistant/components/overkiz/const.py | 1 + homeassistant/components/overkiz/executor.py | 9 +- .../overkiz/water_heater_entities/__init__.py | 2 + .../domestic_hot_water_production.py | 335 ++++++++++++++++++ 4 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/overkiz/water_heater_entities/domestic_hot_water_production.py diff --git a/homeassistant/components/overkiz/const.py b/homeassistant/components/overkiz/const.py index bf9ca3b343f..d176a137544 100644 --- a/homeassistant/components/overkiz/const.py +++ b/homeassistant/components/overkiz/const.py @@ -69,6 +69,7 @@ OVERKIZ_DEVICE_TO_PLATFORM: dict[UIClass | UIWidget, Platform | None] = { UIWidget.ATLANTIC_PASS_APC_DHW: Platform.WATER_HEATER, # widgetName, uiClass is WaterHeatingSystem (not supported) UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) UIWidget.ATLANTIC_PASS_APC_ZONE_CONTROL: Platform.CLIMATE, # widgetName, uiClass is HeatingSystem (not supported) + UIWidget.DOMESTIC_HOT_WATER_PRODUCTION: Platform.WATER_HEATER, # widgetName, uiClass is WaterHeatingSystem (not supported) UIWidget.DOMESTIC_HOT_WATER_TANK: Platform.SWITCH, # widgetName, uiClass is WaterHeatingSystem (not supported) UIWidget.HITACHI_DHW: Platform.WATER_HEATER, # widgetName, uiClass is HitachiHeatingSystem (not supported) UIWidget.MY_FOX_ALARM_CONTROLLER: Platform.ALARM_CONTROL_PANEL, # widgetName, uiClass is Alarm (not supported) diff --git a/homeassistant/components/overkiz/executor.py b/homeassistant/components/overkiz/executor.py index e82a6e21f63..7b0f03e446c 100644 --- a/homeassistant/components/overkiz/executor.py +++ b/homeassistant/components/overkiz/executor.py @@ -5,7 +5,7 @@ from typing import Any, cast from urllib.parse import urlparse from pyoverkiz.enums import OverkizCommand, Protocol -from pyoverkiz.models import Command, Device +from pyoverkiz.models import Command, Device, StateDefinition from pyoverkiz.types import StateType as OverkizStateType from .coordinator import OverkizDataUpdateCoordinator @@ -50,6 +50,13 @@ class OverkizExecutor: """Return True if a command exists in a list of commands.""" return self.select_command(*commands) is not None + def select_definition_state(self, *states: str) -> StateDefinition | None: + """Select first existing definition state in a list of states.""" + for existing_state in self.device.definition.states: + if existing_state.qualified_name in states: + return existing_state + return None + def select_state(self, *states: str) -> OverkizStateType: """Select first existing active state in a list of states.""" for state in states: diff --git a/homeassistant/components/overkiz/water_heater_entities/__init__.py b/homeassistant/components/overkiz/water_heater_entities/__init__.py index abf9db78116..71b66f6ea93 100644 --- a/homeassistant/components/overkiz/water_heater_entities/__init__.py +++ b/homeassistant/components/overkiz/water_heater_entities/__init__.py @@ -2,9 +2,11 @@ from pyoverkiz.enums.ui import UIWidget from .atlantic_pass_apc_dhw import AtlanticPassAPCDHW +from .domestic_hot_water_production import DomesticHotWaterProduction from .hitachi_dhw import HitachiDHW WIDGET_TO_WATER_HEATER_ENTITY = { UIWidget.ATLANTIC_PASS_APC_DHW: AtlanticPassAPCDHW, + UIWidget.DOMESTIC_HOT_WATER_PRODUCTION: DomesticHotWaterProduction, UIWidget.HITACHI_DHW: HitachiDHW, } diff --git a/homeassistant/components/overkiz/water_heater_entities/domestic_hot_water_production.py b/homeassistant/components/overkiz/water_heater_entities/domestic_hot_water_production.py new file mode 100644 index 00000000000..49524e19373 --- /dev/null +++ b/homeassistant/components/overkiz/water_heater_entities/domestic_hot_water_production.py @@ -0,0 +1,335 @@ +"""Support for DomesticHotWaterProduction.""" +from __future__ import annotations + +from typing import Any, cast + +from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState + +from homeassistant.components.water_heater import ( + STATE_ECO, + STATE_HIGH_DEMAND, + STATE_PERFORMANCE, + WaterHeaterEntity, + WaterHeaterEntityFeature, +) +from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF, STATE_ON, TEMP_CELSIUS + +from ..coordinator import OverkizDataUpdateCoordinator +from ..entity import OverkizEntity + +OVERKIZ_TO_OPERATION_MODE: dict[str, str] = { + OverkizCommandParam.STANDARD: STATE_ON, + OverkizCommandParam.HIGH_DEMAND: STATE_HIGH_DEMAND, + OverkizCommandParam.STOP: STATE_OFF, + OverkizCommandParam.MANUAL_ECO_ACTIVE: STATE_ECO, + OverkizCommandParam.MANUAL_ECO_INACTIVE: STATE_OFF, + OverkizCommandParam.ECO: STATE_ECO, + OverkizCommandParam.AUTO: STATE_ECO, + OverkizCommandParam.AUTO_MODE: STATE_ECO, + OverkizCommandParam.BOOST: STATE_PERFORMANCE, +} + +OPERATION_MODE_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_OPERATION_MODE.items()} + +DHWP_AWAY_MODES = [ + OverkizCommandParam.ABSENCE, + OverkizCommandParam.AWAY, + OverkizCommandParam.FROSTPROTECTION, +] + +DEFAULT_MIN_TEMP: float = 30 +DEFAULT_MAX_TEMP: float = 70 + + +class DomesticHotWaterProduction(OverkizEntity, WaterHeaterEntity): + """Representation of a DomesticHotWaterProduction Water Heater.""" + + _attr_temperature_unit = TEMP_CELSIUS + _attr_supported_features = ( + WaterHeaterEntityFeature.TARGET_TEMPERATURE + | WaterHeaterEntityFeature.OPERATION_MODE + ) + _attr_operation_list = [*OPERATION_MODE_TO_OVERKIZ] + + def __init__( + self, device_url: str, coordinator: OverkizDataUpdateCoordinator + ) -> None: + """Init method.""" + super().__init__(device_url, coordinator) + + # Init operation mode to set for this specific device + self.overkiz_to_operation_mode: dict[str, str] = {} + state_mode_definition = self.executor.select_definition_state( + OverkizState.IO_DHW_MODE, OverkizState.MODBUSLINK_DHW_MODE + ) + if state_mode_definition and state_mode_definition.values: + # Filter only for mode allowed by this device + for param, mode in OVERKIZ_TO_OPERATION_MODE.items(): + if param in state_mode_definition.values: + self.overkiz_to_operation_mode[param] = mode + else: + self.overkiz_to_operation_mode = OVERKIZ_TO_OPERATION_MODE + + @property + def _is_boost_mode_on(self) -> bool: + """Return true if boost mode is on.""" + + if self.executor.has_state(OverkizState.IO_DHW_BOOST_MODE): + return ( + self.executor.select_state(OverkizState.IO_DHW_BOOST_MODE) + == OverkizCommandParam.ON + ) + + if self.executor.has_state(OverkizState.MODBUSLINK_DHW_BOOST_MODE): + return ( + self.executor.select_state(OverkizState.MODBUSLINK_DHW_BOOST_MODE) + == OverkizCommandParam.ON + ) + + if self.executor.has_state(OverkizState.CORE_BOOST_MODE_DURATION): + return ( + cast( + float, + self.executor.select_state(OverkizState.CORE_BOOST_MODE_DURATION), + ) + > 0 + ) + + operating_mode = self.executor.select_state(OverkizState.CORE_OPERATING_MODE) + + if operating_mode: + if isinstance(operating_mode, dict): + if operating_mode.get(OverkizCommandParam.RELAUNCH): + return ( + cast( + str, + operating_mode.get(OverkizCommandParam.RELAUNCH), + ) + == OverkizCommandParam.ON + ) + return False + + return cast(str, operating_mode) == OverkizCommandParam.BOOST + + return False + + @property + def is_away_mode_on(self) -> bool | None: + """Return true if away mode is on.""" + + if self.executor.has_state(OverkizState.IO_DHW_ABSENCE_MODE): + return ( + self.executor.select_state(OverkizState.IO_DHW_ABSENCE_MODE) + == OverkizCommandParam.ON + ) + + if self.executor.has_state(OverkizState.MODBUSLINK_DHW_ABSENCE_MODE): + return ( + self.executor.select_state(OverkizState.MODBUSLINK_DHW_ABSENCE_MODE) + == OverkizCommandParam.ON + ) + + operating_mode = self.executor.select_state(OverkizState.CORE_OPERATING_MODE) + + if operating_mode: + if isinstance(operating_mode, dict): + if operating_mode.get(OverkizCommandParam.ABSENCE): + return ( + cast( + str, + operating_mode.get(OverkizCommandParam.ABSENCE), + ) + == OverkizCommandParam.ON + ) + if operating_mode.get(OverkizCommandParam.AWAY): + return ( + cast( + str, + operating_mode.get(OverkizCommandParam.AWAY), + ) + == OverkizCommandParam.ON + ) + return False + + return cast(str, operating_mode) in DHWP_AWAY_MODES + + return None + + @property + def min_temp(self) -> float: + """Return the minimum temperature.""" + min_temp = self.device.states[OverkizState.CORE_MINIMAL_TEMPERATURE_MANUAL_MODE] + if min_temp: + return cast(float, min_temp.value_as_float) + return DEFAULT_MIN_TEMP + + @property + def max_temp(self) -> float: + """Return the maximum temperature.""" + max_temp = self.device.states[OverkizState.CORE_MAXIMAL_TEMPERATURE_MANUAL_MODE] + if max_temp: + return cast(float, max_temp.value_as_float) + return DEFAULT_MAX_TEMP + + @property + def current_temperature(self) -> float | None: + """Return the current temperature.""" + current_temperature = self.device.states[ + OverkizState.IO_MIDDLE_WATER_TEMPERATURE + ] + if current_temperature: + return current_temperature.value_as_float + current_temperature = self.device.states[ + OverkizState.MODBUSLINK_MIDDLE_WATER_TEMPERATURE + ] + if current_temperature: + return current_temperature.value_as_float + return None + + @property + def target_temperature(self) -> float | None: + """Return the temperature we try to reach.""" + + target_temperature = self.device.states[ + OverkizState.CORE_WATER_TARGET_TEMPERATURE + ] + if target_temperature: + return target_temperature.value_as_float + + target_temperature = self.device.states[ + OverkizState.CORE_TARGET_DWH_TEMPERATURE + ] + if target_temperature: + return target_temperature.value_as_float + + target_temperature = self.device.states[OverkizState.CORE_TARGET_TEMPERATURE] + if target_temperature: + return target_temperature.value_as_float + + return None + + @property + def target_temperature_high(self) -> float | None: + """Return the highbound target temperature we try to reach.""" + target_temperature_high = self.device.states[ + OverkizState.CORE_MAXIMAL_TEMPERATURE_MANUAL_MODE + ] + if target_temperature_high: + return target_temperature_high.value_as_float + return None + + @property + def target_temperature_low(self) -> float | None: + """Return the lowbound target temperature we try to reach.""" + target_temperature_low = self.device.states[ + OverkizState.CORE_MINIMAL_TEMPERATURE_MANUAL_MODE + ] + if target_temperature_low: + return target_temperature_low.value_as_float + return None + + async def async_set_temperature(self, **kwargs: Any) -> None: + """Set new target temperature.""" + target_temperature = kwargs.get(ATTR_TEMPERATURE) + + if self.executor.has_command(OverkizCommand.SET_TARGET_TEMPERATURE): + await self.executor.async_execute_command( + OverkizCommand.SET_TARGET_TEMPERATURE, target_temperature + ) + elif self.executor.has_command(OverkizCommand.SET_WATER_TARGET_TEMPERATURE): + await self.executor.async_execute_command( + OverkizCommand.SET_WATER_TARGET_TEMPERATURE, target_temperature + ) + + if self.executor.has_command(OverkizCommand.REFRESH_TARGET_TEMPERATURE): + await self.executor.async_execute_command( + OverkizCommand.REFRESH_TARGET_TEMPERATURE + ) + elif self.executor.has_command(OverkizCommand.REFRESH_WATER_TARGET_TEMPERATURE): + await self.executor.async_execute_command( + OverkizCommand.REFRESH_WATER_TARGET_TEMPERATURE + ) + + @property + def current_operation(self) -> str: + """Return current operation ie. eco, electric, performance, ...""" + if self._is_boost_mode_on: + return OVERKIZ_TO_OPERATION_MODE[OverkizCommandParam.BOOST] + + return OVERKIZ_TO_OPERATION_MODE[ + cast( + str, + self.executor.select_state( + OverkizState.IO_DHW_MODE, OverkizState.MODBUSLINK_DHW_MODE + ), + ) + ] + + async def async_set_operation_mode(self, operation_mode: str) -> None: + """Set new target operation mode.""" + + if operation_mode == STATE_PERFORMANCE: + if self.executor.has_command(OverkizCommand.SET_BOOST_MODE): + await self.executor.async_execute_command( + OverkizCommand.SET_BOOST_MODE, OverkizCommand.ON + ) + + if self.executor.has_command(OverkizCommand.SET_BOOST_MODE_DURATION): + await self.executor.async_execute_command( + OverkizCommand.SET_BOOST_MODE_DURATION, 7 + ) + await self.executor.async_execute_command( + OverkizCommand.REFRESH_BOOST_MODE_DURATION + ) + + if self.executor.has_command(OverkizCommand.SET_CURRENT_OPERATING_MODE): + current_operating_mode = self.executor.select_state( + OverkizState.CORE_OPERATING_MODE + ) + + if current_operating_mode and isinstance(current_operating_mode, dict): + await self.executor.async_execute_command( + OverkizCommand.SET_CURRENT_OPERATING_MODE, + { + OverkizCommandParam.RELAUNCH: OverkizCommandParam.ON, + OverkizCommandParam.ABSENCE: OverkizCommandParam.OFF, + }, + ) + + return + + if self._is_boost_mode_on: + # We're setting a non Boost mode and the device is currently in Boost mode, the following code remove all boost operations + if self.executor.has_command(OverkizCommand.SET_BOOST_MODE): + await self.executor.async_execute_command( + OverkizCommand.SET_BOOST_MODE, OverkizCommand.OFF + ) + + if self.executor.has_command(OverkizCommand.SET_BOOST_MODE_DURATION): + await self.executor.async_execute_command( + OverkizCommand.SET_BOOST_MODE_DURATION, 0 + ) + await self.executor.async_execute_command( + OverkizCommand.REFRESH_BOOST_MODE_DURATION + ) + + if self.executor.has_command(OverkizCommand.SET_CURRENT_OPERATING_MODE): + current_operating_mode = self.executor.select_state( + OverkizState.CORE_OPERATING_MODE + ) + + if current_operating_mode and isinstance(current_operating_mode, dict): + await self.executor.async_execute_command( + OverkizCommand.SET_CURRENT_OPERATING_MODE, + { + OverkizCommandParam.RELAUNCH: OverkizCommandParam.OFF, + OverkizCommandParam.ABSENCE: OverkizCommandParam.OFF, + }, + ) + + await self.executor.async_execute_command( + OverkizCommand.SET_DHW_MODE, self.overkiz_to_operation_mode[operation_mode] + ) + + if self.executor.has_command(OverkizCommand.REFRESH_DHW_MODE): + await self.executor.async_execute_command(OverkizCommand.REFRESH_DHW_MODE) From 197e71203bcd98066bcbe8e96f94575c98d353dd Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 30 Nov 2022 00:26:16 +0000 Subject: [PATCH 0888/1033] [ci skip] Translation update --- .../accuweather/translations/de.json | 2 +- .../components/airzone/translations/de.json | 4 +- .../alarmdecoder/translations/de.json | 2 +- .../components/apple_tv/translations/de.json | 4 +- .../components/aranet/translations/de.json | 2 +- .../components/auth/translations/de.json | 2 +- .../components/awair/translations/de.json | 2 +- .../components/blebox/translations/de.json | 2 +- .../components/bluetooth/translations/de.json | 2 +- .../components/braviatv/translations/de.json | 4 +- .../components/camera/translations/de.json | 2 +- .../components/cloud/translations/de.json | 2 +- .../components/cloud/translations/et.json | 15 ++++++ .../components/cloud/translations/id.json | 15 ++++++ .../components/cloud/translations/no.json | 15 ++++++ .../components/cloud/translations/pt-BR.json | 15 ++++++ .../components/cloud/translations/ru.json | 15 ++++++ .../cloud/translations/zh-Hant.json | 15 ++++++ .../components/co2signal/translations/de.json | 2 +- .../components/coinbase/translations/de.json | 4 +- .../components/cover/translations/de.json | 4 +- .../crownstone/translations/de.json | 10 ++-- .../components/demo/translations/de.json | 4 +- .../device_tracker/translations/de.json | 4 +- .../components/dnsip/translations/de.json | 10 ++-- .../dsmr_reader/translations/de.json | 2 +- .../components/ecobee/translations/de.json | 2 +- .../components/ecowitt/translations/de.json | 2 +- .../components/elkm1/translations/de.json | 2 +- .../components/fivem/translations/de.json | 4 +- .../components/fritz/translations/de.json | 2 +- .../components/fritzbox/translations/de.json | 2 +- .../components/github/translations/de.json | 4 +- .../components/goalzero/translations/de.json | 2 +- .../components/goodwe/translations/de.json | 2 +- .../google_travel_time/translations/de.json | 2 +- .../components/hassio/translations/de.json | 12 ++--- .../components/hive/translations/de.json | 4 +- .../homeassistant/translations/et.json | 4 ++ .../homeassistant/translations/id.json | 4 ++ .../homeassistant/translations/pt-BR.json | 4 ++ .../homeassistant_yellow/translations/de.json | 24 +++++----- .../components/homekit/translations/de.json | 4 +- .../homekit_controller/translations/de.json | 2 +- .../translations/sensor.de.json | 2 +- .../homewizard/translations/de.json | 2 +- .../components/insteon/translations/de.json | 2 +- .../integration/translations/de.json | 4 +- .../intellifire/translations/de.json | 2 +- .../components/iotawatt/translations/de.json | 2 +- .../keymitt_ble/translations/de.json | 2 +- .../components/knx/translations/de.json | 44 ++++++++--------- .../components/knx/translations/pt-BR.json | 46 +++++++++++++++++- .../components/lametric/translations/de.json | 8 ++-- .../components/laundrify/translations/de.json | 4 +- .../components/lidarr/translations/de.json | 4 +- .../litterrobot/translations/de.json | 4 +- .../logi_circle/translations/de.json | 2 +- .../lutron_caseta/translations/de.json | 12 ++--- .../media_player/translations/de.json | 2 +- .../components/motioneye/translations/de.json | 2 +- .../components/mqtt/translations/de.json | 12 ++--- .../components/nest/translations/de.json | 10 ++-- .../components/nextdns/translations/de.json | 2 +- .../nfandroidtv/translations/de.json | 2 +- .../nibe_heatpump/translations/de.json | 20 ++++---- .../components/nina/translations/de.json | 2 +- .../nmap_tracker/translations/de.json | 2 +- .../components/onewire/translations/de.json | 4 +- .../openalpr_local/translations/de.json | 2 +- .../components/openuv/translations/de.json | 2 +- .../components/overkiz/translations/de.json | 2 +- .../overkiz/translations/sensor.de.json | 2 +- .../ovo_energy/translations/de.json | 2 +- .../components/point/translations/de.json | 2 +- .../pushbullet/translations/de.json | 2 +- .../components/pushover/translations/de.json | 2 +- .../components/radarr/translations/de.json | 8 ++-- .../components/rfxtrx/translations/de.json | 2 +- .../components/rhasspy/translations/de.json | 2 +- .../components/risco/translations/de.json | 4 +- .../components/roon/translations/de.json | 2 +- .../rtsp_to_webrtc/translations/de.json | 14 +++--- .../components/scrape/translations/ca.json | 41 ++++++++++++++++ .../components/scrape/translations/de.json | 47 ++++++++++++++++++- .../components/scrape/translations/en.json | 31 +++++++++--- .../components/scrape/translations/es.json | 47 ++++++++++++++++++- .../components/scrape/translations/et.json | 45 ++++++++++++++++++ .../components/scrape/translations/id.json | 45 ++++++++++++++++++ .../components/scrape/translations/no.json | 45 ++++++++++++++++++ .../components/scrape/translations/pt-BR.json | 41 ++++++++++++++++ .../components/scrape/translations/ru.json | 45 ++++++++++++++++++ .../scrape/translations/zh-Hant.json | 45 ++++++++++++++++++ .../components/senseme/translations/de.json | 4 +- .../components/shelly/translations/de.json | 2 +- .../simplisafe/translations/de.json | 4 +- .../soundtouch/translations/de.json | 4 +- .../components/spotify/translations/de.json | 2 +- .../statistics/translations/de.json | 2 +- .../steam_online/translations/de.json | 4 +- .../components/steamist/translations/de.json | 4 +- .../components/subaru/translations/de.json | 2 +- .../components/switchbee/translations/de.json | 2 +- .../synology_dsm/translations/de.json | 2 +- .../components/tasmota/translations/de.json | 8 ++-- .../components/tautulli/translations/de.json | 4 +- .../tellduslive/translations/de.json | 2 +- .../components/threshold/translations/de.json | 4 +- .../components/timer/translations/de.json | 2 +- .../components/tod/translations/de.json | 2 +- .../transmission/translations/de.json | 2 +- .../components/twinkly/translations/de.json | 2 +- .../unifiprotect/translations/de.json | 20 ++++---- .../unifiprotect/translations/et.json | 4 ++ .../unifiprotect/translations/id.json | 4 ++ .../unifiprotect/translations/no.json | 4 ++ .../unifiprotect/translations/pt-BR.json | 4 ++ .../unifiprotect/translations/ru.json | 4 ++ .../unifiprotect/translations/zh-Hant.json | 4 ++ .../utility_meter/translations/de.json | 2 +- .../components/vacuum/translations/de.json | 2 +- .../components/verisure/translations/de.json | 4 +- .../components/vizio/translations/de.json | 2 +- .../components/vulcan/translations/de.json | 8 ++-- .../waze_travel_time/translations/de.json | 2 +- .../components/webostv/translations/de.json | 2 +- .../components/whois/translations/de.json | 6 +-- .../components/wiz/translations/de.json | 2 +- .../wolflink/translations/sensor.de.json | 2 +- .../xiaomi_ble/translations/de.json | 2 +- .../yale_smart_alarm/translations/de.json | 2 +- .../yalexs_ble/translations/de.json | 2 +- .../components/yolink/translations/de.json | 2 +- .../components/zamg/translations/de.json | 2 +- .../components/zha/translations/de.json | 6 +-- .../components/zwave_js/translations/de.json | 6 +-- .../components/zwave_me/translations/de.json | 2 +- 137 files changed, 830 insertions(+), 248 deletions(-) diff --git a/homeassistant/components/accuweather/translations/de.json b/homeassistant/components/accuweather/translations/de.json index 5958332676a..6a3c887d881 100644 --- a/homeassistant/components/accuweather/translations/de.json +++ b/homeassistant/components/accuweather/translations/de.json @@ -28,7 +28,7 @@ "data": { "forecast": "Wettervorhersage" }, - "description": "Aufgrund der Einschr\u00e4nkungen der kostenlosen Version des AccuWeather-API-Schl\u00fcssels werden bei aktivierter Wettervorhersage Datenaktualisierungen alle 80 Minuten statt alle 40 Minuten durchgef\u00fchrt." + "description": "Aufgrund der Einschr\u00e4nkungen der kostenlosen Version des AccuWeather API-Schl\u00fcssels werden bei aktivierter Wettervorhersage Datenaktualisierungen alle 80 Minuten statt alle 40 Minuten durchgef\u00fchrt." }, "user": { "data": { diff --git a/homeassistant/components/airzone/translations/de.json b/homeassistant/components/airzone/translations/de.json index 8cd14ac4f6d..38b56e70308 100644 --- a/homeassistant/components/airzone/translations/de.json +++ b/homeassistant/components/airzone/translations/de.json @@ -5,7 +5,7 @@ }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", - "invalid_system_id": "Ung\u00fcltige Airzone-System-ID" + "invalid_system_id": "Ung\u00fcltige Airzone System-ID" }, "step": { "discovered_connection": { @@ -21,7 +21,7 @@ "id": "System-ID", "port": "Port" }, - "description": "Richte die Airzone-Integration ein." + "description": "Richte die Airzone Integration ein." } } } diff --git a/homeassistant/components/alarmdecoder/translations/de.json b/homeassistant/components/alarmdecoder/translations/de.json index f324772c909..73c1c82b6c1 100644 --- a/homeassistant/components/alarmdecoder/translations/de.json +++ b/homeassistant/components/alarmdecoder/translations/de.json @@ -66,7 +66,7 @@ "data": { "zone_number": "Zonennummer" }, - "description": "Gib die die Zonennummer ein, die du hinzuf\u00fcgen, bearbeiten oder entfernen m\u00f6chtest.", + "description": "Gib die Zonennummer ein, die du hinzuf\u00fcgen, bearbeiten oder entfernen m\u00f6chtest.", "title": "AlarmDecoder konfigurieren" } } diff --git a/homeassistant/components/apple_tv/translations/de.json b/homeassistant/components/apple_tv/translations/de.json index 27ecc3452f0..1a4155977ad 100644 --- a/homeassistant/components/apple_tv/translations/de.json +++ b/homeassistant/components/apple_tv/translations/de.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", - "backoff": "Das Ger\u00e4t akzeptiert zur Zeit keine Kopplungsanfragen (Du hast m\u00f6glicherweise zu oft einen ung\u00fcltigen PIN-Code eingegeben), versuche es sp\u00e4ter erneut.", + "backoff": "Das Ger\u00e4t akzeptiert zurzeit keine Kopplungsanfragen (Du hast m\u00f6glicherweise zu oft einen ung\u00fcltigen PIN-Code eingegeben), versuche es sp\u00e4ter erneut.", "device_did_not_pair": "Es wurde kein Versuch unternommen, den Kopplungsvorgang vom Ger\u00e4t aus abzuschlie\u00dfen.", "device_not_found": "Das Ger\u00e4t wurde bei der Erkennung nicht gefunden. Bitte versuche es erneut hinzuzuf\u00fcgen.", "inconsistent_device": "Die erwarteten Protokolle wurden bei der Erkennung nicht gefunden. Dies deutet normalerweise auf ein Problem mit Multicast-DNS (Zeroconf) hin. Bitte versuche das Ger\u00e4t erneut hinzuzuf\u00fcgen.", @@ -41,7 +41,7 @@ "title": "Passwort erforderlich" }, "protocol_disabled": { - "description": "Die Kopplung ist f\u00fcr `{protocol}` erforderlich, aber auf dem Ger\u00e4t deaktiviert. Bitte \u00fcberpr\u00fcfe m\u00f6gliche Zugriffsbeschr\u00e4nkungen (z. B. Verbindung aller Ger\u00e4te im lokalen Netzwerk zulassen) auf dem Ger\u00e4t. \n\nDu kannst fortfahren, ohne dieses Protokoll zu koppeln, aber einige Funktionen sind eingeschr\u00e4nkt.", + "description": "Die Kopplung ist f\u00fcr `{protocol}` erforderlich, aber auf dem Ger\u00e4t deaktiviert. Bitte \u00fcberpr\u00fcfe m\u00f6gliche Zugriffsbeschr\u00e4nkungen (z.B. Verbindung aller Ger\u00e4te im lokalen Netzwerk zulassen) auf dem Ger\u00e4t. \n\nDu kannst fortfahren, ohne dieses Protokoll zu koppeln, aber einige Funktionen sind eingeschr\u00e4nkt.", "title": "Kopplung nicht m\u00f6glich" }, "reconfigure": { diff --git a/homeassistant/components/aranet/translations/de.json b/homeassistant/components/aranet/translations/de.json index cc8a35e3761..b9b37b32e22 100644 --- a/homeassistant/components/aranet/translations/de.json +++ b/homeassistant/components/aranet/translations/de.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", "integrations_diabled": "Auf diesem Ger\u00e4t sind keine Integrationen aktiviert. Bitte aktiviere die Smart-Home-Integration \u00fcber die App und versuche es erneut.", - "no_devices_found": "Keine unkonfigurierten Aranet-Ger\u00e4te gefunden.", + "no_devices_found": "Keine unkonfigurierten Aranet Ger\u00e4te gefunden.", "outdated_version": "Dieses Ger\u00e4t verwendet eine veraltete Firmware. Bitte aktualisiere es auf mindestens v1.2.0 und versuche es erneut." }, "error": { diff --git a/homeassistant/components/auth/translations/de.json b/homeassistant/components/auth/translations/de.json index e33536bcc1c..ec97c449ac4 100644 --- a/homeassistant/components/auth/translations/de.json +++ b/homeassistant/components/auth/translations/de.json @@ -17,7 +17,7 @@ "title": "\u00dcberpr\u00fcfe das Setup" } }, - "title": "Benachrichtig f\u00fcr One-Time Password" + "title": "Benachrichtigen f\u00fcr One-Time Password" }, "totp": { "error": { diff --git a/homeassistant/components/awair/translations/de.json b/homeassistant/components/awair/translations/de.json index 51d86b35618..fc1baa30a46 100644 --- a/homeassistant/components/awair/translations/de.json +++ b/homeassistant/components/awair/translations/de.json @@ -25,7 +25,7 @@ "description": "M\u00f6chtest du {model} ({device_id}) einrichten?" }, "local": { - "description": "Befolge [diese Anweisungen]({url}), um die Awair Local API zu aktivieren. \n\nDr\u00fccke abschlie\u00dfend auf Senden." + "description": "Befolge [diese Anweisungen]({url}), um die Awair Lokal API zu aktivieren. \n\nDr\u00fccke abschlie\u00dfend auf Senden." }, "local_pick": { "data": { diff --git a/homeassistant/components/blebox/translations/de.json b/homeassistant/components/blebox/translations/de.json index c104a96fe46..2b13adf2d69 100644 --- a/homeassistant/components/blebox/translations/de.json +++ b/homeassistant/components/blebox/translations/de.json @@ -9,7 +9,7 @@ "unknown": "Unerwarteter Fehler", "unsupported_version": "Das BleBox-Ger\u00e4t hat eine veraltete Firmware. Bitte aktualisiere es zuerst." }, - "flow_title": "{name} ( {host} )", + "flow_title": "{name} ({host})", "step": { "user": { "data": { diff --git a/homeassistant/components/bluetooth/translations/de.json b/homeassistant/components/bluetooth/translations/de.json index 2ddbc4496ac..6c7a5c15139 100644 --- a/homeassistant/components/bluetooth/translations/de.json +++ b/homeassistant/components/bluetooth/translations/de.json @@ -28,7 +28,7 @@ }, "issues": { "haos_outdated": { - "description": "Zur Verbesserung der Bluetooth-Zuverl\u00e4ssigkeit und -Leistung empfehlen wir dir dringend ein Update auf Version 9.0 oder h\u00f6her des Home Assistant-Betriebssystems.", + "description": "Zur Verbesserung der Bluetooth Zuverl\u00e4ssigkeit und Leistung empfehlen wir dir dringend ein Update auf Version 9.0 oder h\u00f6her des Home Assistant-Betriebssystems.", "title": "Aktualisiere auf das Home Assistant-Betriebssystem 9.0 oder h\u00f6her" } }, diff --git a/homeassistant/components/braviatv/translations/de.json b/homeassistant/components/braviatv/translations/de.json index 492e647f302..8f376cdce9e 100644 --- a/homeassistant/components/braviatv/translations/de.json +++ b/homeassistant/components/braviatv/translations/de.json @@ -19,7 +19,7 @@ "pin": "PIN-Code", "use_psk": "PSK-Authentifizierung verwenden" }, - "description": "Gib den auf dem Sony Bravia-Fernseher angezeigten PIN-Code ein. \n\nWenn der PIN-Code nicht angezeigt wird, musst du die Registrierung von Home Assistant auf Ihrem Fernseher aufheben, gehe zu: Einstellungen - > Netzwerk - > Remote-Ger\u00e4teeinstellungen - > Remote-Ger\u00e4t abmelden. \n\nDu kannst PSK (Pre-Shared-Key) anstelle der PIN verwenden. PSK ist ein benutzerdefinierter geheimer Schl\u00fcssel, der f\u00fcr die Zugriffskontrolle verwendet wird. Diese Authentifizierungsmethode wird als stabiler empfohlen. Um PSK auf deinem Fernseher zu aktivieren, gehe zu: Einstellungen - > Netzwerk - > Heimnetzwerk-Setup - > IP-Steuerung. Aktiviere dann das Kontrollk\u00e4stchen \u00abPSK-Authentifizierung verwenden\u00bb und gib deinen PSK anstelle der PIN ein.", + "description": "Gib den auf dem Sony Bravia-Fernseher angezeigten PIN-Code ein. \n\nWenn der PIN-Code nicht angezeigt wird, musst du die Registrierung von Home Assistant auf deinem Fernseher aufheben, gehe zu: Einstellungen \u2192 Netzwerk \u2192 Remote-Ger\u00e4teeinstellungen \u2192 Remote-Ger\u00e4t abmelden. \n\nDu kannst PSK (Pre-Shared-Key) anstelle der PIN verwenden. PSK ist ein benutzerdefinierter geheimer Schl\u00fcssel, der f\u00fcr die Zugriffskontrolle verwendet wird. Diese Authentifizierungsmethode wird als stabiler empfohlen. Um PSK auf deinem Fernseher zu aktivieren, gehe zu: Einstellungen \u2192 Netzwerk \u2192 Heimnetzwerk-Setup \u2192 IP-Steuerung. Aktiviere dann das Kontrollk\u00e4stchen \u00abPSK-Authentifizierung verwenden\u00bb und gib deinen PSK anstelle der PIN ein.", "title": "Autorisiere Sony Bravia TV" }, "confirm": { @@ -30,7 +30,7 @@ "pin": "PIN-Code", "use_psk": "PSK-Authentifizierung verwenden" }, - "description": "Gib den auf dem Sony Bravia-Fernseher angezeigten PIN-Code ein. \n\nWenn der PIN-Code nicht angezeigt wird, musst du die Registrierung von Home Assistant auf Ihrem Fernseher aufheben, gehe zu: Einstellungen - > Netzwerk - > Remote-Ger\u00e4teeinstellungen - > Remote-Ger\u00e4t abmelden. \n\nDu kannst PSK (Pre-Shared-Key) anstelle der PIN verwenden. PSK ist ein benutzerdefinierter geheimer Schl\u00fcssel, der f\u00fcr die Zugriffskontrolle verwendet wird. Diese Authentifizierungsmethode wird als stabiler empfohlen. Um PSK auf deinem Fernseher zu aktivieren, gehe zu: Einstellungen - > Netzwerk - > Heimnetzwerk-Setup - > IP-Steuerung. Aktiviere dann das Kontrollk\u00e4stchen \u00abPSK-Authentifizierung verwenden\u00bb und gib deinen PSK anstelle der PIN ein." + "description": "Gib den auf dem Sony Bravia-Fernseher angezeigten PIN-Code ein. \n\nWenn der PIN-Code nicht angezeigt wird, musst du die Registrierung von Home Assistant auf deinem Fernseher aufheben, gehe zu: Einstellungen \u2192 Netzwerk \u2192 Remote-Ger\u00e4teeinstellungen \u2192 Remote-Ger\u00e4t abmelden. \n\nDu kannst PSK (Pre-Shared-Key) anstelle der PIN verwenden. PSK ist ein benutzerdefinierter geheimer Schl\u00fcssel, der f\u00fcr die Zugriffskontrolle verwendet wird. Diese Authentifizierungsmethode wird als stabiler empfohlen. Um PSK auf deinem Fernseher zu aktivieren, gehe zu: Einstellungen \u2192 Netzwerk \u2192 Heimnetzwerk-Setup \u2192 IP-Steuerung. Aktiviere dann das Kontrollk\u00e4stchen \u00abPSK-Authentifizierung verwenden\u00bb und gib deinen PSK anstelle der PIN ein." }, "user": { "data": { diff --git a/homeassistant/components/camera/translations/de.json b/homeassistant/components/camera/translations/de.json index d504f5f6217..2dd79596cc3 100644 --- a/homeassistant/components/camera/translations/de.json +++ b/homeassistant/components/camera/translations/de.json @@ -1,7 +1,7 @@ { "state": { "_": { - "idle": "inaktiv", + "idle": "Inaktiv", "recording": "Aufnehmen", "streaming": "\u00dcbertr\u00e4gt" } diff --git a/homeassistant/components/cloud/translations/de.json b/homeassistant/components/cloud/translations/de.json index ed34cf86fc3..4528ab9bd67 100644 --- a/homeassistant/components/cloud/translations/de.json +++ b/homeassistant/components/cloud/translations/de.json @@ -11,7 +11,7 @@ } } }, - "title": "Legacy-Abonnement erkannt" + "title": "Legacy Abonnement erkannt" } }, "system_health": { diff --git a/homeassistant/components/cloud/translations/et.json b/homeassistant/components/cloud/translations/et.json index 59c2b8c6e82..f4201ed0c34 100644 --- a/homeassistant/components/cloud/translations/et.json +++ b/homeassistant/components/cloud/translations/et.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "Toiming v\u00f5ttis liiga kaua aega. Palun proovi hiljem uuesti." + }, + "step": { + "confirm_change_plan": { + "description": "Oleme hiljuti uuendanud oma tellimiss\u00fcsteemi. Home Assistant Cloudi kasutamise j\u00e4tkamiseks pead \u00fchekordselt kinnitama muudatuse PayPalis.\n\nSee v\u00f5tab aega 1 minut ja ei t\u00f5sta hinda." + } + } + }, + "title": "Tuvastati p\u00e4randtellimus" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa on lubatud", diff --git a/homeassistant/components/cloud/translations/id.json b/homeassistant/components/cloud/translations/id.json index a8f6d7b4b67..2b1f105d08c 100644 --- a/homeassistant/components/cloud/translations/id.json +++ b/homeassistant/components/cloud/translations/id.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "Operasi terlalu lama. Coba lagi nanti." + }, + "step": { + "confirm_change_plan": { + "description": "Kami baru saja memperbarui sistem langganan kami. Untuk terus menggunakan Home Assistant Cloud, Anda perlu menyetujui perubahan tersebut satu kali di PayPal.\n\nIni membutuhkan waktu 1 menit dan tidak akan menaikkan harga." + } + } + }, + "title": "Langganan lawas terdeteksi" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa Diaktifkan", diff --git a/homeassistant/components/cloud/translations/no.json b/homeassistant/components/cloud/translations/no.json index e3ae7a4f766..c3a08f2d1f3 100644 --- a/homeassistant/components/cloud/translations/no.json +++ b/homeassistant/components/cloud/translations/no.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "Operasjonen tok for lang tid. Pr\u00f8v igjen senere." + }, + "step": { + "confirm_change_plan": { + "description": "Vi har nylig oppdatert abonnementssystemet v\u00e5rt. For \u00e5 fortsette \u00e5 bruke Home Assistant Cloud m\u00e5 du en gang godkjenne endringen i PayPal. \n\n Dette tar 1 minutt og vil ikke \u00f8ke prisen." + } + } + }, + "title": "Eldre abonnement oppdaget" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa aktivert", diff --git a/homeassistant/components/cloud/translations/pt-BR.json b/homeassistant/components/cloud/translations/pt-BR.json index 7e9a1f71c06..4723adf6048 100644 --- a/homeassistant/components/cloud/translations/pt-BR.json +++ b/homeassistant/components/cloud/translations/pt-BR.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "A opera\u00e7\u00e3o demorou muito. Por favor, tente novamente mais tarde." + }, + "step": { + "confirm_change_plan": { + "description": "Recentemente, atualizamos nosso sistema de assinatura. Para continuar usando o Home Assistant Cloud, voc\u00ea precisa aprovar uma vez a altera\u00e7\u00e3o no PayPal. \n\n Isso leva 1 minuto e n\u00e3o aumentar\u00e1 o pre\u00e7o." + } + } + }, + "title": "Assinatura herdada detectada" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa habilitada", diff --git a/homeassistant/components/cloud/translations/ru.json b/homeassistant/components/cloud/translations/ru.json index aa3c34ad700..ac57b0ca750 100644 --- a/homeassistant/components/cloud/translations/ru.json +++ b/homeassistant/components/cloud/translations/ru.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "\u041e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u0434\u043b\u0438\u043b\u0430\u0441\u044c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0434\u043e\u043b\u0433\u043e. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043f\u043e\u0437\u0436\u0435." + }, + "step": { + "confirm_change_plan": { + "description": "\u041d\u0435\u0434\u0430\u0432\u043d\u043e \u043c\u044b \u043e\u0431\u043d\u043e\u0432\u0438\u043b\u0438 \u043d\u0430\u0448\u0443 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438. \u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 Home Assistant Cloud, \u0412\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0434\u043d\u043e\u043a\u0440\u0430\u0442\u043d\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0432 PayPal. \n\n\u042d\u0442\u043e \u0437\u0430\u0439\u043c\u0451\u0442 1 \u043c\u0438\u043d\u0443\u0442\u0443 \u0438 \u043d\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0451\u0442 \u043a \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u044e \u0446\u0435\u043d\u044b." + } + } + }, + "title": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0430 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0430\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430" + } + }, "system_health": { "info": { "alexa_enabled": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 Alexa", diff --git a/homeassistant/components/cloud/translations/zh-Hant.json b/homeassistant/components/cloud/translations/zh-Hant.json index 619b0dde71c..0fbfd6d00bf 100644 --- a/homeassistant/components/cloud/translations/zh-Hant.json +++ b/homeassistant/components/cloud/translations/zh-Hant.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "\u4f5c\u696d\u8017\u6642\u904e\u4e45\u3001\u8acb\u7a0d\u5019\u518d\u8a66\u3002" + }, + "step": { + "confirm_change_plan": { + "description": "\u6211\u5011\u6700\u8fd1\u66f4\u65b0\u4e86\u8a02\u95b1\u7cfb\u7d71\u3001\u6b32\u7e7c\u7e8c\u4f7f\u7528 Home Assistant \u96f2\u670d\u52d9\u3001\u9700\u8981\u91cd\u65b0\u540c\u610f PayPal \u4e0a\u7684\u8b8a\u66f4\u3002\n\n\u5927\u6982\u9700\u8981 1 \u5206\u9418\u3001\u540c\u6642\u4e0d\u6703\u589e\u52a0\u4efb\u4f55\u8cbb\u7528\u3002" + } + } + }, + "title": "\u767c\u73fe\u820a\u8a02\u95b1\u6a21\u5f0f" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa \u5df2\u555f\u7528", diff --git a/homeassistant/components/co2signal/translations/de.json b/homeassistant/components/co2signal/translations/de.json index e35b991566f..f88316ba6fc 100644 --- a/homeassistant/components/co2signal/translations/de.json +++ b/homeassistant/components/co2signal/translations/de.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", - "api_ratelimit": "API Ratelimit \u00fcberschritten", + "api_ratelimit": "API Ratenlimit \u00fcberschritten", "unknown": "Unerwarteter Fehler" }, "error": { diff --git a/homeassistant/components/coinbase/translations/de.json b/homeassistant/components/coinbase/translations/de.json index f6200633950..60518f0140c 100644 --- a/homeassistant/components/coinbase/translations/de.json +++ b/homeassistant/components/coinbase/translations/de.json @@ -30,12 +30,12 @@ "step": { "init": { "data": { - "account_balance_currencies": "Zu meldende Wallet-Guthaben.", + "account_balance_currencies": "Zu meldendes Wallet-Guthaben.", "exchange_base": "Basisw\u00e4hrung f\u00fcr Wechselkurssensoren.", "exchange_rate_currencies": "Zu meldende Wechselkurse.", "exchnage_rate_precision": "Anzahl der Dezimalstellen f\u00fcr Wechselkurse." }, - "description": "Coinbase-Optionen anpassen" + "description": "Coinbase Optionen anpassen" } } } diff --git a/homeassistant/components/cover/translations/de.json b/homeassistant/components/cover/translations/de.json index bf320e07f9e..81fb1fb211c 100644 --- a/homeassistant/components/cover/translations/de.json +++ b/homeassistant/components/cover/translations/de.json @@ -14,8 +14,8 @@ "is_closing": "{entity_name} wird geschlossen", "is_open": "{entity_name} ist offen", "is_opening": "{entity_name} wird ge\u00f6ffnet", - "is_position": "Die Aktuelle Position von {entity_name} ist", - "is_tilt_position": "Die Aktuelle Neigungsposition von {entity_name} ist" + "is_position": "Die aktuelle Position von {entity_name} ist", + "is_tilt_position": "Die aktuelle Neigungsposition von {entity_name} ist" }, "trigger_type": { "closed": "{entity_name} geschlossen", diff --git a/homeassistant/components/crownstone/translations/de.json b/homeassistant/components/crownstone/translations/de.json index 054d6987c29..1719fa9ace7 100644 --- a/homeassistant/components/crownstone/translations/de.json +++ b/homeassistant/components/crownstone/translations/de.json @@ -15,8 +15,8 @@ "data": { "usb_path": "USB-Ger\u00e4te-Pfad" }, - "description": "W\u00e4hle den seriellen Anschluss des Crownstone-USB-Dongles aus, oder w\u00e4hle \"Don't use USB\", wenn du keinen USB-Dongle einrichten m\u00f6chtest.\n\nSuche nach einem Ger\u00e4t mit VID 10C4 und PID EA60.", - "title": "Crownstone USB-Dongle-Konfiguration" + "description": "W\u00e4hle den seriellen Anschluss des Crownstone USB-Dongles aus oder w\u00e4hle \"Don't use USB\", wenn du keinen USB-Dongle einrichten m\u00f6chtest.\n\nSuche nach einem Ger\u00e4t mit VID 10C4 und PID EA60.", + "title": "Crownstone USB-Dongle Konfiguration" }, "usb_manual_config": { "data": { @@ -37,7 +37,7 @@ "email": "E-Mail", "password": "Passwort" }, - "title": "Crownstone-Konto" + "title": "Crownstone Konto" } } }, @@ -53,8 +53,8 @@ "data": { "usb_path": "USB-Ger\u00e4te-Pfad" }, - "description": "W\u00e4hle den seriellen Anschluss des Crownstone-USB-Dongles.\n\nSuche nach einem Ger\u00e4t mit VID 10C4 und PID EA60.", - "title": "Crownstone USB-Dongle-Konfiguration" + "description": "W\u00e4hle den seriellen Anschluss des Crownstone USB-Dongles.\n\nSuche nach einem Ger\u00e4t mit VID 10C4 und PID EA60.", + "title": "Crownstone USB-Dongle Konfiguration" }, "usb_manual_config": { "data": { diff --git a/homeassistant/components/demo/translations/de.json b/homeassistant/components/demo/translations/de.json index 6848443e0b3..0318ba7e0cf 100644 --- a/homeassistant/components/demo/translations/de.json +++ b/homeassistant/components/demo/translations/de.json @@ -31,8 +31,8 @@ "title": "Die Blinkerfl\u00fcssigkeit ist leer und muss nachgef\u00fcllt werden" }, "transmogrifier_deprecated": { - "description": "Die Transmogrifier-Komponente ist jetzt veraltet, da die neue API keine lokale Kontrolle mehr bietet.", - "title": "Die Transmogrifier-Komponente ist veraltet" + "description": "Die Transmogrifier Komponente ist jetzt veraltet, da die neue API keine lokale Kontrolle mehr bietet.", + "title": "Die Transmogrifier Komponente ist veraltet" }, "unfixable_problem": { "description": "Dieses Problem wird niemals aufgeben.", diff --git a/homeassistant/components/device_tracker/translations/de.json b/homeassistant/components/device_tracker/translations/de.json index fe59183e67a..06ea2406bd5 100644 --- a/homeassistant/components/device_tracker/translations/de.json +++ b/homeassistant/components/device_tracker/translations/de.json @@ -1,8 +1,8 @@ { "device_automation": { "condition_type": { - "is_home": "{entity_name} ist zuhause", - "is_not_home": "{entity_name} ist nicht zuhause" + "is_home": "{entity_name} ist zu Hause", + "is_not_home": "{entity_name} ist nicht zu Hause" }, "trigger_type": { "enters": "{entity_name} betritt einen Bereich", diff --git a/homeassistant/components/dnsip/translations/de.json b/homeassistant/components/dnsip/translations/de.json index 9d82d5c7655..93a4bf8fb26 100644 --- a/homeassistant/components/dnsip/translations/de.json +++ b/homeassistant/components/dnsip/translations/de.json @@ -7,21 +7,21 @@ "user": { "data": { "hostname": "Der Hostname, f\u00fcr den die DNS-Abfrage durchgef\u00fchrt werden soll", - "resolver": "Resolver f\u00fcr IPV4-Lookup", - "resolver_ipv6": "Resolver f\u00fcr IPV6-Lookup" + "resolver": "Aufl\u00f6ser f\u00fcr IPv4-Suche", + "resolver_ipv6": "Aufl\u00f6ser f\u00fcr IPv6-Suche" } } } }, "options": { "error": { - "invalid_resolver": "Ung\u00fcltige IP-Adresse f\u00fcr Resolver" + "invalid_resolver": "Ung\u00fcltige IP-Adresse f\u00fcr Aufl\u00f6ser" }, "step": { "init": { "data": { - "resolver": "Resolver f\u00fcr IPV4-Lookup", - "resolver_ipv6": "Resolver f\u00fcr IPV6-Lookup" + "resolver": "Aufl\u00f6ser f\u00fcr IPv4-Suche", + "resolver_ipv6": "Aufl\u00f6ser f\u00fcr IPv6-Suche" } } } diff --git a/homeassistant/components/dsmr_reader/translations/de.json b/homeassistant/components/dsmr_reader/translations/de.json index 963a3a74e2e..05a69559286 100644 --- a/homeassistant/components/dsmr_reader/translations/de.json +++ b/homeassistant/components/dsmr_reader/translations/de.json @@ -12,7 +12,7 @@ "issues": { "deprecated_yaml": { "description": "Die Konfiguration von DSMR Reader mit YAML wird entfernt. \n\nDeine vorhandene YAML-Konfiguration wurde automatisch in die Benutzeroberfl\u00e4che importiert. \n\nEntferne die DSMR Reader YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die DSMR-Reader-Konfiguration wird entfernt" + "title": "Die DSMR-Reader Konfiguration wird entfernt" } } } \ No newline at end of file diff --git a/homeassistant/components/ecobee/translations/de.json b/homeassistant/components/ecobee/translations/de.json index 10edbd4ecd1..d9d138936cb 100644 --- a/homeassistant/components/ecobee/translations/de.json +++ b/homeassistant/components/ecobee/translations/de.json @@ -5,7 +5,7 @@ }, "error": { "pin_request_failed": "Fehler beim Anfordern der PIN von ecobee; Bitte \u00fcberpr\u00fcfe, ob der API-Schl\u00fcssel korrekt ist.", - "token_request_failed": "Fehler beim Anfordern eines Token von ecobee; Bitte versuche es erneut." + "token_request_failed": "Fehler beim Anfordern eines Tokens von ecobee; Bitte versuche es erneut." }, "step": { "authorize": { diff --git a/homeassistant/components/ecowitt/translations/de.json b/homeassistant/components/ecowitt/translations/de.json index 4a3bfa9f31f..37f5c588fcf 100644 --- a/homeassistant/components/ecowitt/translations/de.json +++ b/homeassistant/components/ecowitt/translations/de.json @@ -1,7 +1,7 @@ { "config": { "create_entry": { - "default": "Um die Integration abzuschlie\u00dfen, verwende die Ecowitt App (auf deinem Telefon) oder rufe die Ecowitt WebUI in einem Browser unter der IP-Adresse der Station auf.\n\nW\u00e4hle deine Station -> Men\u00fc Andere -> DIY Upload Servers. Klicke auf \"Weiter\" und w\u00e4hle \"Angepasst\".\n\n- Server IP: `{server}`\n- Pfad: `{path}`\n- Anschluss: `{port}`\n\nKlicke auf \"Speichern\"." + "default": "Um die Integration abzuschlie\u00dfen, verwende die Ecowitt App (auf deinem Telefon) oder rufe die Ecowitt Web UI in einem Browser unter der IP-Adresse der Station auf.\n\nW\u00e4hle deine Station \u2192 Men\u00fc Andere \u2192 DIY Upload Servers. Klicke auf \"Weiter\" und w\u00e4hle \"Angepasst\".\n\n- Server IP: `{server}`\n- Pfad: `{path}`\n- Anschluss: `{port}`\n\nKlicke auf \"Speichern\"." }, "step": { "user": { diff --git a/homeassistant/components/elkm1/translations/de.json b/homeassistant/components/elkm1/translations/de.json index 7c08a9b254d..976cd88938d 100644 --- a/homeassistant/components/elkm1/translations/de.json +++ b/homeassistant/components/elkm1/translations/de.json @@ -22,7 +22,7 @@ "temperature_unit": "Die von ElkM1 verwendete Temperatureinheit.", "username": "Benutzername" }, - "description": "Verbinde dich mit dem ermittelten System: {mac_address} ( {host} )", + "description": "Verbinde dich mit dem ermittelten System: {mac_address} ({host})", "title": "Stelle eine Verbindung zur Elk-M1-Steuerung her" }, "manual_connection": { diff --git a/homeassistant/components/fivem/translations/de.json b/homeassistant/components/fivem/translations/de.json index bb3b3439cbf..bdb2c817e68 100644 --- a/homeassistant/components/fivem/translations/de.json +++ b/homeassistant/components/fivem/translations/de.json @@ -4,8 +4,8 @@ "already_configured": "Der Dienst ist bereits konfiguriert" }, "error": { - "cannot_connect": "Verbindung fehlgeschlagen. Bitte \u00fcberpr\u00fcfe den Host und den Port und versuche es erneut. Vergewissere dich auch, dass du den neuesten FiveM-Server verwendest.", - "invalid_game_name": "Die API des Spiels, mit dem du dich verbinden willst, ist kein FiveM-Spiel.", + "cannot_connect": "Verbindung fehlgeschlagen. Bitte \u00fcberpr\u00fcfe den Host und den Port und versuche es erneut. Vergewissere dich auch, dass du den neuesten FiveM Server verwendest.", + "invalid_game_name": "Die API des Spiels, mit dem du dich verbinden willst, ist kein FiveM Spiel.", "unknown_error": "Unerwarteter Fehler" }, "step": { diff --git a/homeassistant/components/fritz/translations/de.json b/homeassistant/components/fritz/translations/de.json index 16d2be68adb..fc5e522e18c 100644 --- a/homeassistant/components/fritz/translations/de.json +++ b/homeassistant/components/fritz/translations/de.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", - "ignore_ip6_link_local": "IPv6 link local address wird nicht unterst\u00fctzt.", + "ignore_ip6_link_local": "IPv6 Link-Local-Adresse wird nicht unterst\u00fctzt.", "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { diff --git a/homeassistant/components/fritzbox/translations/de.json b/homeassistant/components/fritzbox/translations/de.json index ef2a6083608..167c6fe8de8 100644 --- a/homeassistant/components/fritzbox/translations/de.json +++ b/homeassistant/components/fritzbox/translations/de.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", - "ignore_ip6_link_local": "IPv6 link local address wird nicht unterst\u00fctzt.", + "ignore_ip6_link_local": "IPv6 Link-Local-Adresse wird nicht unterst\u00fctzt.", "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", "not_supported": "Verbunden mit AVM FRITZ!Box, kann jedoch keine Smart Home-Ger\u00e4te steuern.", "reauth_successful": "Die erneute Authentifizierung war erfolgreich" diff --git a/homeassistant/components/github/translations/de.json b/homeassistant/components/github/translations/de.json index 0d195210744..8446b35f201 100644 --- a/homeassistant/components/github/translations/de.json +++ b/homeassistant/components/github/translations/de.json @@ -10,9 +10,9 @@ "step": { "repositories": { "data": { - "repositories": "W\u00e4hle die zu verfolgenden Repositories aus." + "repositories": "W\u00e4hle die zu verfolgenden Repositorien aus." }, - "title": "Repositories konfigurieren" + "title": "Repositorien konfigurieren" } } } diff --git a/homeassistant/components/goalzero/translations/de.json b/homeassistant/components/goalzero/translations/de.json index d41a2238854..3d551367e73 100644 --- a/homeassistant/components/goalzero/translations/de.json +++ b/homeassistant/components/goalzero/translations/de.json @@ -19,7 +19,7 @@ "host": "Host", "name": "Name" }, - "description": "Bitte lies die Dokumentation, um sicherzustellen, dass alle Anforderungen erf\u00fcllt sind." + "description": "Bitte lese die Dokumentation, um sicherzustellen, dass alle Anforderungen erf\u00fcllt sind." } } } diff --git a/homeassistant/components/goodwe/translations/de.json b/homeassistant/components/goodwe/translations/de.json index 2eac1136e3c..7a2e04e2aaa 100644 --- a/homeassistant/components/goodwe/translations/de.json +++ b/homeassistant/components/goodwe/translations/de.json @@ -13,7 +13,7 @@ "host": "IP-Adresse" }, "description": "Mit Wechselrichter verbinden", - "title": "GoodWe-Wechselrichter" + "title": "GoodWe Wechselrichter" } } } diff --git a/homeassistant/components/google_travel_time/translations/de.json b/homeassistant/components/google_travel_time/translations/de.json index 13ecac74e3c..c35d258b137 100644 --- a/homeassistant/components/google_travel_time/translations/de.json +++ b/homeassistant/components/google_travel_time/translations/de.json @@ -15,7 +15,7 @@ "name": "Name", "origin": "Startort" }, - "description": "Bei der Angabe von Ursprung und Ziel kannst du einen oder mehrere durch das Pipe-Zeichen getrennte Orte in Form einer Adresse, L\u00e4ngen- / Breitengradkoordinaten oder einer Google-Orts-ID angeben. Wenn du den Standort mithilfe einer Google-Orts-ID angibst, muss der ID \"place_id:\" vorangestellt werden." + "description": "Bei der Angabe von Ursprung und Ziel kannst du einen oder mehrere durch das Pipe-Zeichen getrennte Orte in Form einer Adresse, L\u00e4ngen- / Breitengradkoordinaten oder einer Google-Orts-ID angeben. Wenn du den Standort mithilfe einer Google-Orts-ID angibst, muss der ID 'place_id:' vorangestellt werden." } } }, diff --git a/homeassistant/components/hassio/translations/de.json b/homeassistant/components/hassio/translations/de.json index 325009e4814..025d63ceacf 100644 --- a/homeassistant/components/hassio/translations/de.json +++ b/homeassistant/components/hassio/translations/de.json @@ -30,11 +30,11 @@ }, "unsupported_apparmor": { "description": "Das System wird nicht unterst\u00fctzt, da AppArmor nicht ordnungsgem\u00e4\u00df funktioniert und Add-Ons ungesch\u00fctzt und unsicher ausgef\u00fchrt werden. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", - "title": "Nicht unterst\u00fctztes System - AppArmor-Probleme" + "title": "Nicht unterst\u00fctztes System - AppArmor Probleme" }, "unsupported_cgroup_version": { "description": "Das System wird nicht unterst\u00fctzt, da die falsche Version von Docker CGroup verwendet wird. Verwende den Link, um die richtige Version zu erfahren und wie du dies beheben kannst.", - "title": "Nicht unterst\u00fctztes System \u2013 CGroup-Version" + "title": "Nicht unterst\u00fctztes System \u2013 CGroup Version" }, "unsupported_connectivity_check": { "description": "Das System wird nicht unterst\u00fctzt, weil Home Assistant nicht feststellen kann, wann eine Internetverbindung verf\u00fcgbar ist. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", @@ -49,7 +49,7 @@ "title": "Nicht unterst\u00fctztes System \u2013 D-Bus-Probleme" }, "unsupported_dns_server": { - "description": "Das System wird nicht unterst\u00fctzt, da der bereitgestellte DNS-Server nicht ordnungsgem\u00e4\u00df funktioniert und die Fallback-DNS-Option deaktiviert wurde. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "description": "Das System wird nicht unterst\u00fctzt, da der bereitgestellte DNS-Server nicht ordnungsgem\u00e4\u00df funktioniert und die Fallback DNS Option deaktiviert wurde. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", "title": "Nicht unterst\u00fctztes System - DNS-Server-Probleme" }, "unsupported_docker_configuration": { @@ -65,7 +65,7 @@ "title": "Nicht unterst\u00fctztes System \u2013 Schutz deaktiviert" }, "unsupported_lxc": { - "description": "Das System wird nicht unterst\u00fctzt, da es in einer virtuellen LXC-Maschine ausgef\u00fchrt wird. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "description": "Das System wird nicht unterst\u00fctzt, da es in einer virtuellen LXC Maschine ausgef\u00fchrt wird. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", "title": "Nicht unterst\u00fctztes System \u2013 LXC erkannt" }, "unsupported_network_manager": { @@ -98,10 +98,10 @@ }, "unsupported_systemd": { "description": "System wird nicht unterst\u00fctzt, weil Systemd fehlt, inaktiv oder falsch konfiguriert ist. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", - "title": "Nicht unterst\u00fctztes System - Systemd-Probleme" + "title": "Nicht unterst\u00fctztes System - Systemd Probleme" }, "unsupported_systemd_journal": { - "description": "Das System wird nicht unterst\u00fctzt, da das Systemd-Journal und/oder der Gateway-Dienst fehlt, inaktiv oder falsch konfiguriert ist. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", + "description": "Das System wird nicht unterst\u00fctzt, da das Systemd Journal und/oder der Gateway-Dienst fehlt, inaktiv oder falsch konfiguriert ist. Verwende den Link, um mehr zu erfahren und wie du dies beheben kannst.", "title": "Nicht unterst\u00fctztes System \u2013 Systemd Journal-Probleme" }, "unsupported_systemd_resolved": { diff --git a/homeassistant/components/hive/translations/de.json b/homeassistant/components/hive/translations/de.json index e40c7a1adcb..1e1453df99e 100644 --- a/homeassistant/components/hive/translations/de.json +++ b/homeassistant/components/hive/translations/de.json @@ -24,8 +24,8 @@ "data": { "device_name": "Ger\u00e4tename" }, - "description": "Gib deine Hive-Konfiguration ein", - "title": "Hive-Konfiguration." + "description": "Gib deine Hive Konfiguration ein", + "title": "Hive Konfiguration." }, "reauth": { "data": { diff --git a/homeassistant/components/homeassistant/translations/et.json b/homeassistant/components/homeassistant/translations/et.json index 82dcb4948a3..a84e7ec3736 100644 --- a/homeassistant/components/homeassistant/translations/et.json +++ b/homeassistant/components/homeassistant/translations/et.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "\u00dchtegi riiki pole m\u00e4\u00e4ratud, v\u00e4rskenda s\u00e4tteid kl\u00f5psates alloleval nupul \"Lisateave\".", + "title": "Riik pole m\u00e4\u00e4ratud" + }, "historic_currency": { "description": "Valuuta {currency} ei ole enam kasutusel, seadista valuuta uuesti.", "title": "Seadistatud valuutat enam ei kasutata" diff --git a/homeassistant/components/homeassistant/translations/id.json b/homeassistant/components/homeassistant/translations/id.json index a5b079caf8f..ae90a09471f 100644 --- a/homeassistant/components/homeassistant/translations/id.json +++ b/homeassistant/components/homeassistant/translations/id.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "Belum ada negara yang dikonfigurasi, perbarui konfigurasi dengan mengeklik tombol \"pelajari selengkapnya\" di bawah ini.", + "title": "Negara belum dikonfigurasi" + }, "historic_currency": { "description": "Mata uang {currency} tidak lagi digunakan, konfigurasikan ulang konfigurasi mata uang.", "title": "Mata uang yang dikonfigurasi tidak lagi digunakan" diff --git a/homeassistant/components/homeassistant/translations/pt-BR.json b/homeassistant/components/homeassistant/translations/pt-BR.json index 35e1bf1838b..fa31c37dd58 100644 --- a/homeassistant/components/homeassistant/translations/pt-BR.json +++ b/homeassistant/components/homeassistant/translations/pt-BR.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "Nenhum pa\u00eds foi configurado, atualize a configura\u00e7\u00e3o clicando no bot\u00e3o \"saiba mais\" abaixo.", + "title": "O pa\u00eds n\u00e3o foi configurado" + }, "historic_currency": { "description": "A moeda {currency} n\u00e3o est\u00e1 mais em uso, reconfigure a configura\u00e7\u00e3o da moeda.", "title": "A moeda configurada n\u00e3o est\u00e1 mais em uso" diff --git a/homeassistant/components/homeassistant_yellow/translations/de.json b/homeassistant/components/homeassistant_yellow/translations/de.json index 92f3438e2dc..0b406d0f486 100644 --- a/homeassistant/components/homeassistant_yellow/translations/de.json +++ b/homeassistant/components/homeassistant_yellow/translations/de.json @@ -2,9 +2,9 @@ "options": { "abort": { "addon_info_failed": "Silicon Labs Multiprotokoll-Zusatzinformationen konnten nicht abgerufen werden.", - "addon_install_failed": "Die Installation des Silicon Labs Multiprotokoll-Add-Ons ist fehlgeschlagen.", - "addon_set_config_failed": "Die Silicon Labs Multiprotokoll-Konfiguration konnte nicht eingestellt werden.", - "addon_start_failed": "Das Silicon Labs Multiprotokoll-Add-On konnte nicht gestartet werden.", + "addon_install_failed": "Die Installation des Silicon Labs Multiprotokoll Add-Ons ist fehlgeschlagen.", + "addon_set_config_failed": "Die Silicon Labs Multiprotokoll Konfiguration konnte nicht eingestellt werden.", + "addon_start_failed": "Das Silicon Labs Multiprotokoll Add-On konnte nicht gestartet werden.", "not_hassio": "Die Hardwareoptionen k\u00f6nnen nur auf HassOS-Installationen konfiguriert werden.", "zha_migration_failed": "Die ZHA Migration war nicht erfolgreich." }, @@ -12,29 +12,29 @@ "unknown": "Unerwarteter Fehler" }, "progress": { - "install_addon": "Bitte warte, bis die Installation des Silicon Labs Multiprotokoll-Add-ons abgeschlossen ist. Dies kann einige Minuten dauern.", - "start_addon": "Bitte warte, bis der Start des Silicon Labs Multiprotokoll-Add-Ons abgeschlossen ist. Dies kann einige Sekunden dauern." + "install_addon": "Bitte warte, bis die Installation des Silicon Labs Multiprotokoll Add-ons abgeschlossen ist. Dies kann einige Minuten dauern.", + "start_addon": "Bitte warte, bis der Start des Silicon Labs Multiprotokoll Add-Ons abgeschlossen ist. Dies kann einige Sekunden dauern." }, "step": { "addon_installed_other_device": { - "title": "Die Multiprotokollunterst\u00fctzung ist bereits f\u00fcr ein anderes Ger\u00e4t aktiviert" + "title": "Die Multiprotokoll Unterst\u00fctzung ist bereits f\u00fcr ein anderes Ger\u00e4t aktiviert" }, "addon_not_installed": { "data": { - "enable_multi_pan": "Multiprotokollunterst\u00fctzung aktivieren" + "enable_multi_pan": "Multiprotokoll Unterst\u00fctzung aktivieren" }, - "description": "Wenn die Multiprotokoll-Unterst\u00fctzung aktiviert ist, kann das IEEE 802.15.4-Radio des Home Assistant Yellow gleichzeitig f\u00fcr Zigbee und Thread (von Matter verwendet) verwendet werden. Wenn das Funkger\u00e4t bereits von der ZHA-Zigbee-Integration verwendet wird, wird ZHA neu konfiguriert, um die Multiprotokoll-Firmware zu verwenden. \n\nHinweis: Dies ist eine experimentelle Funktion.", - "title": "Aktiviere die Multiprotokollunterst\u00fctzung auf dem IEEE 802.15.4-Funkger\u00e4t" + "description": "Wenn die Multiprotokoll Unterst\u00fctzung aktiviert ist, kann das IEEE 802.15.4-Radio des Home Assistant Yellow gleichzeitig f\u00fcr Zigbee und Thread (von Matter verwendet) verwendet werden. Wenn das Funkger\u00e4t bereits von der ZHA-Zigbee-Integration verwendet wird, wird ZHA neu konfiguriert, um die Multiprotokoll-Firmware zu verwenden. \n\nHinweis: Dies ist eine experimentelle Funktion.", + "title": "Aktiviere die Multiprotokoll Unterst\u00fctzung auf dem IEEE 802.15.4-Funkger\u00e4t" }, "install_addon": { - "title": "Die Installation des Silicon Labs Multiprotokoll-Add-Ons hat begonnen" + "title": "Die Installation des Silicon Labs Multiprotokoll Add-Ons hat begonnen" }, "show_revert_guide": { "description": "Wenn du zu einer reinen Zigbee-Firmware wechseln m\u00f6chtest, f\u00fchre bitte die folgenden manuellen Schritte aus:\n\n * Entferne das Silicon Labs Multiprotokoll-Add-On\n\n * Flashe die reine Zigbee-Firmware, folge der Anleitung unter https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Rekonfiguriere ZHA, um die Einstellungen auf das neu geflashte Funkger\u00e4t zu migrieren.", - "title": "Multiprotokoll-Unterst\u00fctzung ist f\u00fcr dieses Ger\u00e4t aktiviert" + "title": "Multiprotokoll Unterst\u00fctzung ist f\u00fcr dieses Ger\u00e4t aktiviert" }, "start_addon": { - "title": "Das Silicon Labs Multiprotokoll-Add-on wird gestartet." + "title": "Das Silicon Labs Multiprotokoll Add-on wird gestartet." } } } diff --git a/homeassistant/components/homekit/translations/de.json b/homeassistant/components/homekit/translations/de.json index d145d826337..5fa4b01b85b 100644 --- a/homeassistant/components/homekit/translations/de.json +++ b/homeassistant/components/homekit/translations/de.json @@ -44,14 +44,14 @@ "data": { "entities": "Entit\u00e4ten" }, - "description": "Alle \"{domains}\"-Entit\u00e4ten werden einbezogen, mit Ausnahme der ausgeschlossenen Entit\u00e4ten und kategorisierten Entit\u00e4ten.", + "description": "Alle \"{domains}\" Entit\u00e4ten werden einbezogen, mit Ausnahme der ausgeschlossenen Entit\u00e4ten und kategorisierten Entit\u00e4ten.", "title": "W\u00e4hle die auszuschlie\u00dfenden Einheiten aus" }, "include": { "data": { "entities": "Entit\u00e4ten" }, - "description": "Alle \"{domains}\"-Entit\u00e4ten werden einbezogen, sofern nicht bestimmte Entit\u00e4ten ausgew\u00e4hlt werden.", + "description": "Alle \"{domains}\" Entit\u00e4ten werden einbezogen, sofern nicht bestimmte Entit\u00e4ten ausgew\u00e4hlt werden.", "title": "W\u00e4hle die einzuschlie\u00dfenden Entit\u00e4ten aus" }, "init": { diff --git a/homeassistant/components/homekit_controller/translations/de.json b/homeassistant/components/homekit_controller/translations/de.json index 687d6702715..904a438d699 100644 --- a/homeassistant/components/homekit_controller/translations/de.json +++ b/homeassistant/components/homekit_controller/translations/de.json @@ -14,7 +14,7 @@ "authentication_error": "Ung\u00fcltiger HomeKit Code, \u00fcberpr\u00fcfe bitte den Code und versuche es erneut.", "insecure_setup_code": "Der angeforderte Setup-Code ist unsicher, da er zu trivial ist. Dieses Zubeh\u00f6r erf\u00fcllt nicht die grundlegenden Sicherheitsanforderungen.", "max_peers_error": "Das Ger\u00e4t weigerte sich, die Kopplung durchzuf\u00fchren, da es keinen freien Kopplungs-Speicher hat.", - "pairing_failed": "Beim Koppeln mit diesem Ger\u00e4t ist ein nicht behandelter Fehler aufgetreten. Dies kann ein vor\u00fcbergehender Fehler sein oder das oder Ihr Ger\u00e4t wird derzeit nicht unterst\u00fctzt: {error}", + "pairing_failed": "Beim Koppeln mit diesem Ger\u00e4t ist ein nicht behandelter Fehler aufgetreten. Dies kann ein vor\u00fcbergehender Fehler sein oder das oder dein Ger\u00e4t wird derzeit nicht unterst\u00fctzt: {error}", "unable_to_pair": "Koppeln fehltgeschlagen, bitte versuche es erneut", "unknown_error": "Das Ger\u00e4t meldete einen unbekannten Fehler. Die Kopplung ist fehlgeschlagen." }, diff --git a/homeassistant/components/homekit_controller/translations/sensor.de.json b/homeassistant/components/homekit_controller/translations/sensor.de.json index 2aef45f5303..27a6d2a5e0b 100644 --- a/homeassistant/components/homekit_controller/translations/sensor.de.json +++ b/homeassistant/components/homekit_controller/translations/sensor.de.json @@ -9,7 +9,7 @@ "sleepy": "Sleepy Endger\u00e4t" }, "homekit_controller__thread_status": { - "border_router": "Border-Router", + "border_router": "Border Router", "child": "Kind", "detached": "Freistehend", "disabled": "Deaktiviert", diff --git a/homeassistant/components/homewizard/translations/de.json b/homeassistant/components/homewizard/translations/de.json index 782ac2bf6fe..f9dc268f99b 100644 --- a/homeassistant/components/homewizard/translations/de.json +++ b/homeassistant/components/homewizard/translations/de.json @@ -16,7 +16,7 @@ "data": { "ip_address": "IP-Adresse" }, - "description": "Gib die IP-Adresse deines HomeWizard Energy-Ger\u00e4ts ein, um es in Home Assistant zu integrieren.", + "description": "Gib die IP-Adresse deines HomeWizard Energy Ger\u00e4ts ein, um es in Home Assistant zu integrieren.", "title": "Ger\u00e4t konfigurieren" } } diff --git a/homeassistant/components/insteon/translations/de.json b/homeassistant/components/insteon/translations/de.json index ffbad247632..798dba41984 100644 --- a/homeassistant/components/insteon/translations/de.json +++ b/homeassistant/components/insteon/translations/de.json @@ -2,7 +2,7 @@ "config": { "abort": { "cannot_connect": "Verbindung fehlgeschlagen", - "not_insteon_device": "Erkanntes Ger\u00e4t ist kein Insteon-Ger\u00e4t", + "not_insteon_device": "Erkanntes Ger\u00e4t ist kein Insteon Ger\u00e4t", "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." }, "error": { diff --git a/homeassistant/components/integration/translations/de.json b/homeassistant/components/integration/translations/de.json index af26ec446a2..c01af414971 100644 --- a/homeassistant/components/integration/translations/de.json +++ b/homeassistant/components/integration/translations/de.json @@ -16,7 +16,7 @@ "unit_time": "Die Ausgabe wird entsprechend der gew\u00e4hlten Zeiteinheit skaliert." }, "description": "Erstelle einen Sensor, der eine Riemann-Summe berechnet, um das Integral eines Sensors zu sch\u00e4tzen.", - "title": "Riemann-Summenintegralsensor hinzuf\u00fcgen" + "title": "Riemann Summenintegralsensor hinzuf\u00fcgen" } } }, @@ -32,5 +32,5 @@ } } }, - "title": "Integration - Riemann-Summenintegralsensor" + "title": "Integration - Riemann Summenintegralsensor" } \ No newline at end of file diff --git a/homeassistant/components/intellifire/translations/de.json b/homeassistant/components/intellifire/translations/de.json index d9427853cdd..2840db3a1e3 100644 --- a/homeassistant/components/intellifire/translations/de.json +++ b/homeassistant/components/intellifire/translations/de.json @@ -31,7 +31,7 @@ "data": { "host": "Host" }, - "description": "Die folgenden IntelliFire-Ger\u00e4te wurden gefunden. Bitte w\u00e4hle aus, welche du konfigurieren m\u00f6chtest.", + "description": "Die folgenden IntelliFire Ger\u00e4te wurden gefunden. Bitte w\u00e4hle aus, welche du konfigurieren m\u00f6chtest.", "title": "Ger\u00e4teauswahl" } } diff --git a/homeassistant/components/iotawatt/translations/de.json b/homeassistant/components/iotawatt/translations/de.json index d5626c7e135..c30bb01d875 100644 --- a/homeassistant/components/iotawatt/translations/de.json +++ b/homeassistant/components/iotawatt/translations/de.json @@ -11,7 +11,7 @@ "password": "Passwort", "username": "Benutzername" }, - "description": "Das IoTawatt-Ger\u00e4t erfordert eine Authentifizierung. Bitte gib den Benutzernamen und das Passwort ein und dr\u00fccke auf die Schaltfl\u00e4che Senden." + "description": "Das IoTawatt Ger\u00e4t erfordert eine Authentifizierung. Bitte gib den Benutzernamen und das Passwort ein und dr\u00fccke auf die Schaltfl\u00e4che Senden." }, "user": { "data": { diff --git a/homeassistant/components/keymitt_ble/translations/de.json b/homeassistant/components/keymitt_ble/translations/de.json index a03d9c725be..2e77e757565 100644 --- a/homeassistant/components/keymitt_ble/translations/de.json +++ b/homeassistant/components/keymitt_ble/translations/de.json @@ -16,7 +16,7 @@ "address": "Ger\u00e4teadresse", "name": "Name" }, - "title": "MicroBot-Ger\u00e4t einrichten" + "title": "MicroBot Ger\u00e4t einrichten" }, "link": { "description": "Dr\u00fccke die Taste am MicroBot Push, wenn die LED durchgehend rosa oder gr\u00fcn leuchtet, um sich bei Home Assistant zu registrieren.", diff --git a/homeassistant/components/knx/translations/de.json b/homeassistant/components/knx/translations/de.json index 1e39eb096b4..9c64e5b6edb 100644 --- a/homeassistant/components/knx/translations/de.json +++ b/homeassistant/components/knx/translations/de.json @@ -6,11 +6,11 @@ }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", - "file_not_found": "Die angegebene `.knxkeys`-Datei wurde im Pfad config/.storage/knx/ nicht gefunden.", + "file_not_found": "Die angegebene `.knxkeys` Datei wurde im Pfad config/.storage/knx/ nicht gefunden.", "invalid_backbone_key": "Ung\u00fcltiger Backbone-Schl\u00fcssel. 32 Hexadezimalzahlen erwartet.", "invalid_individual_address": "Wert ist keine g\u00fcltige physikalische Adresse. 'Bereich.Linie.Teilnehmer'", "invalid_ip_address": "Ung\u00fcltige IPv4 Adresse.", - "invalid_signature": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys`-Datei ist ung\u00fcltig.", + "invalid_signature": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys` Datei ist ung\u00fcltig.", "no_router_discovered": "Es wurde kein KNXnet/IP-Router im Netzwerk gefunden.", "no_tunnel_discovered": "Es konnte kein KNX Tunneling Server in deinem Netzwerk gefunden werden." }, @@ -33,7 +33,7 @@ "host": "IP-Adresse der KNX/IP-Tunneling Schnittstelle.", "local_ip": "Lasse das Feld leer, um die automatische Erkennung zu verwenden.", "port": "Port der KNX/IP-Tunneling Schnittstelle.", - "route_back": "Aktiviere diese Option, wenn sich Ihr KNXnet/IP-Tunnelserver hinter NAT befindet. Gilt nur f\u00fcr UDP-Verbindungen." + "route_back": "Aktiviere diese Option, wenn sich dein KNXnet/IP-Tunnelserver hinter NAT befindet. Gilt nur f\u00fcr UDP-Verbindungen." }, "description": "Bitte gib die Verbindungsinformationen deiner Tunnel-Schnittstelle ein." }, @@ -41,12 +41,12 @@ "data": { "individual_address": "Physikalische Adresse", "local_ip": "Lokale IP von Home Assistant", - "multicast_group": "Multicast-Gruppe", - "multicast_port": "Multicast-Port", + "multicast_group": "Multicast Gruppe", + "multicast_port": "Multicast Port", "routing_secure": "KNX IP-Secure verwenden" }, "data_description": { - "individual_address": "Physikalische Adresse, die von Home Assistant verwendet werden soll, z. B. \u201e0.0.4\u201c.", + "individual_address": "Physikalische Adresse, die von Home Assistant verwendet werden soll, z.B. \u201e0.0.4\u201c.", "local_ip": "Lasse das Feld leer, um die automatische Erkennung zu verwenden." }, "description": "Bitte konfiguriere die Routing-Optionen." @@ -61,14 +61,14 @@ }, "secure_knxkeys": { "data": { - "knxkeys_filename": "Der Dateiname deiner `.knxkeys`-Datei (einschlie\u00dflich Erweiterung)", - "knxkeys_password": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys`-Datei" + "knxkeys_filename": "Der Dateiname deiner `.knxkeys` Datei (einschlie\u00dflich Erweiterung)", + "knxkeys_password": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys` Datei" }, "data_description": { "knxkeys_filename": "Die Datei wird in deinem Konfigurationsverzeichnis unter `.storage/knx/` erwartet.\nIm Home Assistant OS w\u00e4re dies `/config/.storage/knx/`\nBeispiel: `my_project.knxkeys`", "knxkeys_password": "Dies wurde beim Exportieren der Datei aus ETS gesetzt." }, - "description": "Bitte gib die Informationen f\u00fcr deine `.knxkeys`-Datei ein." + "description": "Bitte gib die Informationen f\u00fcr deine `.knxkeys` Datei ein." }, "secure_routing_manual": { "data": { @@ -76,7 +76,7 @@ "sync_latency_tolerance": "Netzwerklatenztoleranz" }, "data_description": { - "backbone_key": "Kann im Bericht \"Sicherheit\" eines ETS-Projekts eingesehen werden. Eg. '00112233445566778899AABBCCDDEEFF'", + "backbone_key": "Kann im Bericht \"Sicherheit\" eines ETS-Projekts eingesehen werden. z.B. '00112233445566778899AABBCCDDEEFF'", "sync_latency_tolerance": "Der Standardwert ist 1000." }, "description": "Bitte gib deine IP-Secure Informationen ein." @@ -97,7 +97,7 @@ "secure_tunneling": { "description": "W\u00e4hle aus, wie du KNX/IP-Secure konfigurieren m\u00f6chtest.", "menu_options": { - "secure_knxkeys": "Verwende eine `.knxkeys`-Datei, die IP-Secure-Schl\u00fcssel enth\u00e4lt", + "secure_knxkeys": "Verwende eine `.knxkeys` Datei, die IP-Secure Schl\u00fcssel enth\u00e4lt", "secure_tunnel_manual": "IP-Secure Schl\u00fcssel manuell konfigurieren" } }, @@ -112,11 +112,11 @@ "options": { "error": { "cannot_connect": "Verbindung fehlgeschlagen", - "file_not_found": "Die angegebene `.knxkeys`-Datei wurde im Pfad config/.storage/knx/ nicht gefunden.", + "file_not_found": "Die angegebene `.knxkeys` Datei wurde im Pfad config/.storage/knx/ nicht gefunden.", "invalid_backbone_key": "Ung\u00fcltiger Backbone-Schl\u00fcssel. 32 Hexadezimalzahlen erwartet.", "invalid_individual_address": "Wert ist keine g\u00fcltige physikalische Adresse. 'Bereich.Linie.Teilnehmer'", "invalid_ip_address": "Ung\u00fcltige IPv4 Adresse.", - "invalid_signature": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys`-Datei ist ung\u00fcltig.", + "invalid_signature": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys` Datei ist ung\u00fcltig.", "no_router_discovered": "Es wurde kein KNXnet/IP-Router im Netzwerk gefunden.", "no_tunnel_discovered": "Es konnte kein KNX Tunneling Server in deinem Netzwerk gefunden werden." }, @@ -149,7 +149,7 @@ "host": "IP-Adresse der KNX/IP-Tunneling Schnittstelle.", "local_ip": "Lasse das Feld leer, um die automatische Erkennung zu verwenden.", "port": "Port der KNX/IP-Tunneling Schnittstelle.", - "route_back": "Aktiviere diese Option, wenn sich Ihr KNXnet/IP-Tunnelserver hinter NAT befindet. Gilt nur f\u00fcr UDP-Verbindungen." + "route_back": "Aktiviere diese Option, wenn sich dein KNXnet/IP-Tunnelserver hinter NAT befindet. Gilt nur f\u00fcr UDP-Verbindungen." }, "description": "Bitte gib die Verbindungsinformationen deiner Tunnel-Schnittstelle ein." }, @@ -163,12 +163,12 @@ "data": { "individual_address": "Physikalische Adresse", "local_ip": "Lokale IP von Home Assistant", - "multicast_group": "Multicast-Gruppe", - "multicast_port": "Multicast-Port", + "multicast_group": "Multicast Gruppe", + "multicast_port": "Multicast Port", "routing_secure": "KNX IP-Secure verwenden" }, "data_description": { - "individual_address": "Physikalische Adresse, die von Home Assistant verwendet werden soll, z. B. \u201e0.0.4\u201c.", + "individual_address": "Physikalische Adresse, die von Home Assistant verwendet werden soll, z.B. \u201e0.0.4\u201c.", "local_ip": "Lasse das Feld leer, um die automatische Erkennung zu verwenden." }, "description": "Bitte konfiguriere die Routing-Optionen." @@ -183,14 +183,14 @@ }, "secure_knxkeys": { "data": { - "knxkeys_filename": "Der Dateiname deiner `.knxkeys`-Datei (einschlie\u00dflich Erweiterung)", - "knxkeys_password": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys`-Datei" + "knxkeys_filename": "Der Dateiname deiner `.knxkeys` Datei (einschlie\u00dflich Erweiterung)", + "knxkeys_password": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys` Datei" }, "data_description": { "knxkeys_filename": "Die Datei wird in deinem Konfigurationsverzeichnis unter `.storage/knx/` erwartet.\nIm Home Assistant OS w\u00e4re dies `/config/.storage/knx/`\nBeispiel: `my_project.knxkeys`", "knxkeys_password": "Dies wurde beim Exportieren der Datei aus ETS gesetzt." }, - "description": "Bitte gib die Informationen f\u00fcr deine `.knxkeys`-Datei ein." + "description": "Bitte gib die Informationen f\u00fcr deine `.knxkeys` Datei ein." }, "secure_routing_manual": { "data": { @@ -198,7 +198,7 @@ "sync_latency_tolerance": "Netzwerklatenztoleranz" }, "data_description": { - "backbone_key": "Kann im Bericht \"Sicherheit\" eines ETS-Projekts eingesehen werden. Eg. '00112233445566778899AABBCCDDEEFF'", + "backbone_key": "Kann im Bericht \"Sicherheit\" eines ETS-Projekts eingesehen werden. z.B. '00112233445566778899AABBCCDDEEFF'", "sync_latency_tolerance": "Der Standardwert ist 1000." }, "description": "Bitte gib deine IP-Secure Informationen ein." @@ -219,7 +219,7 @@ "secure_tunneling": { "description": "W\u00e4hle aus, wie du KNX/IP-Secure konfigurieren m\u00f6chtest.", "menu_options": { - "secure_knxkeys": "Verwende eine `.knxkeys`-Datei, die IP-Secure-Schl\u00fcssel enth\u00e4lt", + "secure_knxkeys": "Verwende eine `.knxkeys` Datei, die IP-Secure Schl\u00fcssel enth\u00e4lt", "secure_tunnel_manual": "IP-Secure Schl\u00fcssel manuell konfigurieren" } }, diff --git a/homeassistant/components/knx/translations/pt-BR.json b/homeassistant/components/knx/translations/pt-BR.json index 4c6d2189742..5217c4315ea 100644 --- a/homeassistant/components/knx/translations/pt-BR.json +++ b/homeassistant/components/knx/translations/pt-BR.json @@ -7,6 +7,7 @@ "error": { "cannot_connect": "Falha ao conectar", "file_not_found": "O arquivo `.knxkeys` especificado n\u00e3o foi encontrado no caminho config/.storage/knx/", + "invalid_backbone_key": "Chave de backbone inv\u00e1lida. 32 n\u00fameros hexadecimais esperados.", "invalid_individual_address": "O valor n\u00e3o corresponde ao padr\u00e3o do endere\u00e7o individual KNX.\n '\u00e1rea.linha.dispositivo'", "invalid_ip_address": "Endere\u00e7o IPv4 inv\u00e1lido.", "invalid_signature": "A senha para descriptografar o arquivo `.knxkeys` est\u00e1 errada.", @@ -41,7 +42,8 @@ "individual_address": "Endere\u00e7o individual", "local_ip": "IP local do Home Assistant", "multicast_group": "Grupo multicast", - "multicast_port": "Porta multicast" + "multicast_port": "Porta multicast", + "routing_secure": "Usar KNX IP Secure" }, "data_description": { "individual_address": "Endere\u00e7o KNX a ser usado pelo Home Assistant, por exemplo, `0.0.4`", @@ -49,6 +51,14 @@ }, "description": "Por favor, configure as op\u00e7\u00f5es de roteamento." }, + "secure_key_source": { + "description": "Selecione como deseja configurar o KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Use um arquivo `.knxkeys` contendo chaves IP seguras", + "secure_routing_manual": "Configure a chave de backbone IP segura manualmente", + "secure_tunnel_manual": "Configurar credenciais seguras de IP manualmente" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "O nome do seu arquivo `.knxkeys` (incluindo extens\u00e3o)", @@ -60,6 +70,17 @@ }, "description": "Por favor, insira as informa\u00e7\u00f5es para o seu arquivo `.knxkeys`." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Chave de backbone", + "sync_latency_tolerance": "Toler\u00e2ncia de lat\u00eancia de rede" + }, + "data_description": { + "backbone_key": "Pode ser visto no relat\u00f3rio 'Seguran\u00e7a' de um projeto ETS. Por exemplo. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "O padr\u00e3o \u00e9 1000." + }, + "description": "Por favor, insira suas informa\u00e7\u00f5es seguras de IP." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Senha de autentica\u00e7\u00e3o do dispositivo", @@ -92,6 +113,7 @@ "error": { "cannot_connect": "Falha ao conectar", "file_not_found": "O arquivo `.knxkeys` especificado n\u00e3o foi encontrado no caminho config/.storage/knx/", + "invalid_backbone_key": "Chave de backbone inv\u00e1lida. 32 n\u00fameros hexadecimais esperados.", "invalid_individual_address": "O valor n\u00e3o corresponde ao padr\u00e3o do endere\u00e7o individual KNX.\n '\u00e1rea.linha.dispositivo'", "invalid_ip_address": "Endere\u00e7o IPv4 inv\u00e1lido.", "invalid_signature": "A senha para descriptografar o arquivo `.knxkeys` est\u00e1 errada.", @@ -142,7 +164,8 @@ "individual_address": "Endere\u00e7o individual", "local_ip": "IP local do Home Assistant", "multicast_group": "Grupo multicast", - "multicast_port": "Porta multicast" + "multicast_port": "Porta multicast", + "routing_secure": "Usar KNX IP Secure" }, "data_description": { "individual_address": "Endere\u00e7o KNX a ser usado pelo Home Assistant, por exemplo, `0.0.4`", @@ -150,6 +173,14 @@ }, "description": "Por favor, configure as op\u00e7\u00f5es de roteamento." }, + "secure_key_source": { + "description": "Selecione como deseja configurar o KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Use um arquivo `.knxkeys` contendo chaves IP seguras", + "secure_routing_manual": "Configure a chave de backbone IP segura manualmente", + "secure_tunnel_manual": "Configurar credenciais seguras de IP manualmente" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "O nome do seu arquivo `.knxkeys` (incluindo extens\u00e3o)", @@ -161,6 +192,17 @@ }, "description": "Por favor, insira as informa\u00e7\u00f5es para o seu arquivo `.knxkeys`." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Chave de backbone", + "sync_latency_tolerance": "Toler\u00e2ncia de lat\u00eancia de rede" + }, + "data_description": { + "backbone_key": "Pode ser visto no relat\u00f3rio 'Seguran\u00e7a' de um projeto ETS. Por exemplo. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "O padr\u00e3o \u00e9 1000." + }, + "description": "Por favor, insira suas informa\u00e7\u00f5es seguras de IP." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Senha de autentica\u00e7\u00e3o do dispositivo", diff --git a/homeassistant/components/lametric/translations/de.json b/homeassistant/components/lametric/translations/de.json index 15d98a45dbb..5b7c2534c5d 100644 --- a/homeassistant/components/lametric/translations/de.json +++ b/homeassistant/components/lametric/translations/de.json @@ -5,7 +5,7 @@ "authorize_url_timeout": "Zeit\u00fcberschreitung beim Erstellen der Authorisierungs-URL.", "invalid_discovery_info": "Ung\u00fcltige Suchinformationen erhalten", "link_local_address": "Lokale Linkadressen werden nicht unterst\u00fctzt", - "missing_configuration": "Die LaMetric-Integration ist nicht konfiguriert. Bitte folge der Dokumentation.", + "missing_configuration": "Die LaMetric Integration ist nicht konfiguriert. Bitte folge der Dokumentation.", "no_devices": "Der autorisierte Benutzer hat keine LaMetric Ger\u00e4te", "no_url_available": "Keine URL verf\u00fcgbar. Informationen zu diesem Fehler findest du [im Hilfebereich]({docs_url}).", "reauth_device_not_found": "Das Ger\u00e4t, das du erneut authentifizieren m\u00f6chtest, wird in diesem LaMetric-Konto nicht gefunden", @@ -18,7 +18,7 @@ }, "step": { "choice_enter_manual_or_fetch_cloud": { - "description": "Ein LaMetric-Ger\u00e4t kann im Home Assistant auf zwei verschiedene Arten eingerichtet werden.\n\nDu kannst alle Ger\u00e4teinformationen und API-Tokens selbst eingeben, oder Home Assistant kann sie von deinem LaMetric.com-Konto importieren.", + "description": "Ein LaMetric Ger\u00e4t kann im Home Assistant auf zwei verschiedene Arten eingerichtet werden.\n\nDu kannst alle Ger\u00e4teinformationen und API-Tokens selbst eingeben oder Home Assistant kann sie von deinem LaMetric.com Konto importieren.", "menu_options": { "manual_entry": "Manuell eintragen", "pick_implementation": "Import von LaMetric.com (empfohlen)" @@ -30,7 +30,7 @@ "host": "Host" }, "data_description": { - "api_key": "Du findest diesen API-Schl\u00fcssel auf der [Ger\u00e4teseite in deinem LaMetric-Entwicklerkonto](https://developer.lametric.com/user/devices).", + "api_key": "Du findest diesen API-Schl\u00fcssel auf der [Ger\u00e4teseite in deinem LaMetric Entwicklerkonto](https://developer.lametric.com/user/devices).", "host": "Die IP-Adresse oder der Hostname deines LaMetric TIME in deinem Netzwerk." } }, @@ -39,7 +39,7 @@ }, "user_cloud_select_device": { "data": { - "device": "W\u00e4hle das hinzuzuf\u00fcgende LaMetric-Ger\u00e4t aus" + "device": "W\u00e4hle das hinzuzuf\u00fcgende LaMetric Ger\u00e4t aus" } } } diff --git a/homeassistant/components/laundrify/translations/de.json b/homeassistant/components/laundrify/translations/de.json index 016f345e13d..5df595b2dd2 100644 --- a/homeassistant/components/laundrify/translations/de.json +++ b/homeassistant/components/laundrify/translations/de.json @@ -14,10 +14,10 @@ "data": { "code": "Auth-Code (xxx-xxx)" }, - "description": "Bitte gib deinen pers\u00f6nlichen Auth-Code ein, der in der laundrify-App angezeigt wird." + "description": "Bitte gib deinen pers\u00f6nlichen Auth-Code ein, der in der laundrify App angezeigt wird." }, "reauth_confirm": { - "description": "Die laundrify-Integration muss sich neu authentifizieren.", + "description": "Die laundrify Integration muss sich neu authentifizieren.", "title": "Integration erneut authentifizieren" } } diff --git a/homeassistant/components/lidarr/translations/de.json b/homeassistant/components/lidarr/translations/de.json index e586992af2a..87282f13d10 100644 --- a/homeassistant/components/lidarr/translations/de.json +++ b/homeassistant/components/lidarr/translations/de.json @@ -16,7 +16,7 @@ "data": { "api_key": "API-Schl\u00fcssel" }, - "description": "Die Lidarr-Integration muss manuell erneut mit der Lidarr-API authentifiziert werden", + "description": "Die Lidarr Integration muss manuell erneut mit der Lidarr-API authentifiziert werden", "title": "Integration erneut authentifizieren" }, "user": { @@ -25,7 +25,7 @@ "url": "URL", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" }, - "description": "Der API-Schl\u00fcssel kann automatisch abgerufen werden, wenn in der Anwendung keine Anmeldeinformationen festgelegt wurden.\nDeinen API-Schl\u00fcssel findest du unter Einstellungen > Allgemein in der Lidarr-Web-Benutzeroberfl\u00e4che." + "description": "Der API-Schl\u00fcssel kann automatisch abgerufen werden, wenn in der Anwendung keine Anmeldeinformationen festgelegt wurden.\nDeinen API-Schl\u00fcssel findest du unter Einstellungen \u2192 Allgemein in der Lidarr-Web-Benutzeroberfl\u00e4che." } } } diff --git a/homeassistant/components/litterrobot/translations/de.json b/homeassistant/components/litterrobot/translations/de.json index 8259aa1d16c..15f7a2107b2 100644 --- a/homeassistant/components/litterrobot/translations/de.json +++ b/homeassistant/components/litterrobot/translations/de.json @@ -27,8 +27,8 @@ }, "issues": { "migrated_attributes": { - "description": "Die Vakuumentit\u00e4tsattribute sind jetzt als Diagnosesensoren verf\u00fcgbar. \n\nBitte passe eventuell vorhandene Automatisierungen oder Skripte an, die diese Attribute verwenden.", - "title": "Litter-Robot-Attribute sind jetzt ihre eigenen Sensoren" + "description": "Die Staubsaugerentit\u00e4tsattribute sind jetzt als Diagnosesensoren verf\u00fcgbar. \n\nBitte passe eventuell vorhandene Automatisierungen oder Skripte an, die diese Attribute verwenden.", + "title": "Litter-Robot Attribute sind jetzt ihre eigenen Sensoren" } } } \ No newline at end of file diff --git a/homeassistant/components/logi_circle/translations/de.json b/homeassistant/components/logi_circle/translations/de.json index bed8328c92e..fba8106c937 100644 --- a/homeassistant/components/logi_circle/translations/de.json +++ b/homeassistant/components/logi_circle/translations/de.json @@ -13,7 +13,7 @@ }, "step": { "auth": { - "description": "Folge dem Link unten und dr\u00fccke **Akzeptieren** um auf dein Logi Circle-Konto zuzugreifen. Kehre dann zur\u00fcck und dr\u00fccke unten auf **Senden** . \n\n [Link] ({authorization_url})", + "description": "Folge dem Link unten und dr\u00fccke **Akzeptieren** um auf dein Logi Circle-Konto zuzugreifen. Kehre dann zur\u00fcck und dr\u00fccke unten auf **Senden**. \n\n [Link] ({authorization_url})", "title": "Authentifizierung mit Logi Circle" }, "user": { diff --git a/homeassistant/components/lutron_caseta/translations/de.json b/homeassistant/components/lutron_caseta/translations/de.json index ff4f89aa512..05256098c62 100644 --- a/homeassistant/components/lutron_caseta/translations/de.json +++ b/homeassistant/components/lutron_caseta/translations/de.json @@ -58,17 +58,17 @@ "open_3": "\u00d6ffnen 3", "open_4": "\u00d6ffnen 4", "open_all": "Alle \u00f6ffnen", - "raise": "Raise", + "raise": "Erh\u00f6hen", "raise_1": "Anheben 1", "raise_2": "Anheben 2", "raise_3": "Anheben 3", "raise_4": "Anheben 4", "raise_all": "Erhebe alle", - "stop": "Stop (Favorit)", - "stop_1": "Stop 1", - "stop_2": "Stop 2", - "stop_3": "Stop 3", - "stop_4": "Stop 4", + "stop": "Stopp (Favorit)", + "stop_1": "Stopp 1", + "stop_2": "Stopp 2", + "stop_3": "Stopp 3", + "stop_4": "Stopp 4", "stop_all": "Alle anhalten" }, "trigger_type": { diff --git a/homeassistant/components/media_player/translations/de.json b/homeassistant/components/media_player/translations/de.json index 29a9e63d425..4fc136e5743 100644 --- a/homeassistant/components/media_player/translations/de.json +++ b/homeassistant/components/media_player/translations/de.json @@ -21,7 +21,7 @@ "state": { "_": { "buffering": "Puffern", - "idle": "inaktiv", + "idle": "Inaktiv", "off": "Aus", "on": "An", "paused": "Pausiert", diff --git a/homeassistant/components/motioneye/translations/de.json b/homeassistant/components/motioneye/translations/de.json index e4d72b07398..cb60930bb45 100644 --- a/homeassistant/components/motioneye/translations/de.json +++ b/homeassistant/components/motioneye/translations/de.json @@ -31,7 +31,7 @@ "init": { "data": { "stream_url_template": "Stream-URL-Vorlage", - "webhook_set": "MotionEye-Webhooks konfigurieren, um Ereignisse an Home Assistant zu melden", + "webhook_set": "MotionEye Webhooks konfigurieren, um Ereignisse an Home Assistant zu melden", "webhook_set_overwrite": "\u00dcberschreiben von nicht bekannten Webhooks" } } diff --git a/homeassistant/components/mqtt/translations/de.json b/homeassistant/components/mqtt/translations/de.json index 12c85528f5d..677bf5f4038 100644 --- a/homeassistant/components/mqtt/translations/de.json +++ b/homeassistant/components/mqtt/translations/de.json @@ -34,8 +34,8 @@ "tls_insecure": "Validierung des Broker-Zertifikats ignorieren", "transport": "MQTT-Transport", "username": "Benutzername", - "ws_headers": "WebSocket-Header im JSON-Format", - "ws_path": "WebSocket-Pfad" + "ws_headers": "WebSocket Header im JSON-Format", + "ws_path": "WebSocket Pfad" }, "description": "Bitte gib die Verbindungsinformationen deines MQTT-Brokers ein." }, @@ -44,7 +44,7 @@ "discovery": "Suche aktivieren" }, "description": "M\u00f6chtest du Home Assistant so konfigurieren, dass er eine Verbindung mit dem MQTT-Broker herstellt, der vom Supervisor Add-on {addon} bereitgestellt wird?", - "title": "MQTT Broker per Supervisor add-on" + "title": "MQTT Broker per Supervisor Add-on" } } }, @@ -76,7 +76,7 @@ "title": "Deine manuell konfigurierte(n) MQTT-{platform}(en) erfordert Aufmerksamkeit" }, "deprecated_yaml_broker_settings": { - "description": "Die folgenden Einstellungen in \u201econfiguration.yaml\u201c wurden in den MQTT-Konfigurationseintrag migriert und \u00fcberschreiben nun die Einstellungen in \u201econfiguration.yaml\u201c:\n\u201e{deprecated_settings}\u201c \n\nBitte entferne diese Einstellungen aus \u201econfiguration.yaml\u201c und starte Home Assistant neu, um dieses Problem zu beheben. Weitere Informationen findest du in der [Dokumentation]( {more_info_url} ).", + "description": "Die folgenden Einstellungen in \u201econfiguration.yaml\u201c wurden in den MQTT-Konfigurationseintrag migriert und \u00fcberschreiben nun die Einstellungen in \u201econfiguration.yaml\u201c:\n\u201e{deprecated_settings}\u201c \n\nBitte entferne diese Einstellungen aus \u201econfiguration.yaml\u201c und starte Home Assistant neu, um dieses Problem zu beheben. Weitere Informationen findest du in der [Dokumentation]({more_info_url}).", "title": "Veraltete MQTT-Einstellungen in \u201econfiguration.yaml\u201c gefunden" } }, @@ -111,8 +111,8 @@ "tls_insecure": "Validierung des Broker-Zertifikats ignorieren", "transport": "MQTT-Transport", "username": "Benutzername", - "ws_headers": "WebSocket-Header im JSON-Format", - "ws_path": "WebSocket-Pfad" + "ws_headers": "WebSocket Header im JSON-Format", + "ws_path": "WebSocket Pfad" }, "description": "Bitte gib die Verbindungsinformationen deines MQTT-Brokers ein.", "title": "Broker-Optionen" diff --git a/homeassistant/components/nest/translations/de.json b/homeassistant/components/nest/translations/de.json index 448c103ae17..7a9f068443c 100644 --- a/homeassistant/components/nest/translations/de.json +++ b/homeassistant/components/nest/translations/de.json @@ -20,7 +20,7 @@ "internal_error": "Ein interner Fehler ist aufgetreten", "invalid_pin": "Ung\u00fcltiger PIN-Code", "subscriber_error": "Unbekannter Abonnentenfehler, siehe Protokolle", - "timeout": "Ein zeit\u00fcberschreitungs Fehler ist aufgetreten", + "timeout": "Ein Zeit\u00fcberschreitungsfehler ist aufgetreten", "unknown": "Unerwarteter Fehler", "wrong_project_id": "Gib eine g\u00fcltige Cloud-Projekt-ID ein (identisch mit der Projekt-ID f\u00fcr den Ger\u00e4tezugriff)." }, @@ -62,7 +62,7 @@ "data": { "code": "PIN-Code" }, - "description": "[Autorisiere dein Konto] ( {url} ), um deinen Nest-Account zu verkn\u00fcpfen.\n\n F\u00fcge anschlie\u00dfend den erhaltenen PIN Code hier ein.", + "description": "[Autorisiere dein Konto] ( {url} ), um deinen Nest-Account zu verkn\u00fcpfen.\n\nF\u00fcge anschlie\u00dfend den erhaltenen PIN Code hier ein.", "title": "Nest-Konto verkn\u00fcpfen" }, "pick_implementation": { @@ -72,7 +72,7 @@ "data": { "cloud_project_id": "Google Cloud-Projekt-ID" }, - "description": "Rufe die [Cloud Console]( {url} ) auf, um deine Google Cloud-Projekt-ID zu finden.", + "description": "Rufe die [Cloud Console]({url}) auf, um deine Google Cloud-Projekt-ID zu finden.", "title": "Google Cloud konfigurieren" }, "reauth_confirm": { @@ -92,10 +92,10 @@ "issues": { "deprecated_yaml": { "description": "Das Konfigurieren von Nest in configuration.yaml wird in Home Assistant 2022.10 entfernt. \n\nDeine bestehenden OAuth-Anwendungsdaten und Zugriffseinstellungen wurden automatisch in die Benutzeroberfl\u00e4che importiert. Entferne die YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Nest-YAML-Konfiguration wird entfernt" + "title": "Die Nest YAML-Konfiguration wird entfernt" }, "removed_app_auth": { - "description": "Um die Sicherheit zu verbessern und das Phishing-Risiko zu verringern, hat Google die von Home Assistant verwendete Authentifizierungsmethode eingestellt. \n\n **Zur L\u00f6sung sind Ma\u00dfnahmen deinerseits erforderlich** ([more info]( {more_info_url} )) \n\n 1. Besuche die Integrationsseite\n 1. Dr\u00fccke in der Nest-Integration auf Neu konfigurieren.\n 1. Home Assistant f\u00fchrt dich durch die Schritte zum Upgrade auf die Webauthentifizierung. \n\n Informationen zur Fehlerbehebung findest du in der Nest [Integrationsanleitung]( {documentation_url} ).", + "description": "Um die Sicherheit zu verbessern und das Phishing-Risiko zu verringern, hat Google die von Home Assistant verwendete Authentifizierungsmethode eingestellt. \n\n **Zur L\u00f6sung sind Ma\u00dfnahmen deinerseits erforderlich** ([mehr Information]({more_info_url})) \n\n 1. Besuche die Integrationsseite\n 1. Dr\u00fccke in der Nest-Integration auf Neu konfigurieren.\n 1. Home Assistant f\u00fchrt dich durch die Schritte zum Upgrade auf die Webauthentifizierung. \n\n Informationen zur Fehlerbehebung findest du in der Nest [Integrationsanleitung]({documentation_url}).", "title": "Nest-Authentifizierungsdaten m\u00fcssen aktualisiert werden" } } diff --git a/homeassistant/components/nextdns/translations/de.json b/homeassistant/components/nextdns/translations/de.json index 51a5eaf5edd..e8a50c3dba9 100644 --- a/homeassistant/components/nextdns/translations/de.json +++ b/homeassistant/components/nextdns/translations/de.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Dieses NextDNS-Profil ist bereits konfiguriert." + "already_configured": "Dieses NextDNS Profil ist bereits konfiguriert." }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", diff --git a/homeassistant/components/nfandroidtv/translations/de.json b/homeassistant/components/nfandroidtv/translations/de.json index fef64459261..63161696967 100644 --- a/homeassistant/components/nfandroidtv/translations/de.json +++ b/homeassistant/components/nfandroidtv/translations/de.json @@ -13,7 +13,7 @@ "host": "Host", "name": "Name" }, - "description": "Bitte lies die Dokumentation, um sicherzustellen, dass alle Anforderungen erf\u00fcllt sind." + "description": "Bitte lese die Dokumentation, um sicherzustellen, dass alle Anforderungen erf\u00fcllt sind." } } } diff --git a/homeassistant/components/nibe_heatpump/translations/de.json b/homeassistant/components/nibe_heatpump/translations/de.json index 0e885e730f0..886513c0104 100644 --- a/homeassistant/components/nibe_heatpump/translations/de.json +++ b/homeassistant/components/nibe_heatpump/translations/de.json @@ -2,18 +2,18 @@ "config": { "error": { "address": "Ung\u00fcltige Remote-Adresse angegeben. Die Adresse muss eine IP-Adresse oder ein aufl\u00f6sbarer Hostname sein.", - "address_in_use": "Der ausgew\u00e4hlte Listening-Port wird auf diesem System bereits verwendet.", + "address_in_use": "Der ausgew\u00e4hlte Listening Port wird auf diesem System bereits verwendet.", "model": "Das ausgew\u00e4hlte Modell scheint MODBUS40 nicht zu unterst\u00fctzen", "read": "Fehler bei Leseanforderung von Pumpe. \u00dcberpr\u00fcfe deinen \u201eRemote-Leseport\u201c oder \u201eRemote-Adresse\u201c.", "unknown": "Unerwarteter Fehler", - "url": "Die angegebene URL ist weder wohlgeformt noch wird sie unterst\u00fctzt.", + "url": "Die angegebene URL ist weder wohlgeformt, noch wird sie unterst\u00fctzt.", "write": "Fehler bei Schreibanforderung an Pumpe. \u00dcberpr\u00fcfe deinen \u201eRemote-Schreibport\u201c oder \u201eRemote-Adresse\u201c." }, "step": { "modbus": { "data": { - "modbus_unit": "Modbus-Einheitenkennung", - "modbus_url": "Modbus-URL", + "modbus_unit": "Modbus Einheitenkennung", + "modbus_url": "Modbus URL", "model": "Modell der W\u00e4rmepumpe" }, "data_description": { @@ -30,15 +30,15 @@ "remote_write_port": "Remote-Schreibport" }, "data_description": { - "ip_address": "Die Adresse des NibeGW-Ger\u00e4ts. Das Ger\u00e4t sollte mit einer statischen Adresse konfiguriert worden sein.", - "listening_port": "Der lokale Port auf diesem System, an den das NibeGW-Ger\u00e4t Daten senden soll.", - "remote_read_port": "Der Port, an dem das NibeGW-Ger\u00e4t auf Leseanfragen wartet.", - "remote_write_port": "Der Port, an dem das NibeGW-Ger\u00e4t auf Schreibanfragen wartet." + "ip_address": "Die Adresse des NibeGW Ger\u00e4ts. Das Ger\u00e4t sollte mit einer statischen Adresse konfiguriert worden sein.", + "listening_port": "Der lokale Port auf diesem System, an den das NibeGW Ger\u00e4t Daten senden soll.", + "remote_read_port": "Der Port, an dem das NibeGW Ger\u00e4t auf Leseanfragen wartet.", + "remote_write_port": "Der Port, an dem das NibeGW Ger\u00e4t auf Schreibanfragen wartet." }, - "description": "Bevor du versuchst, die Integration zu konfigurieren, \u00fcberpr\u00fcfe folgendes:\n - Das NibeGW-Ger\u00e4t ist an eine W\u00e4rmepumpe angeschlossen.\n - Das MODBUS40-Zubeh\u00f6r wurde in der Konfiguration der W\u00e4rmepumpe aktiviert.\n - Die Pumpe ist nicht in einen Alarmzustand wegen fehlendem MODBUS40-Zubeh\u00f6r \u00fcbergegangen." + "description": "Bevor du versuchst, die Integration zu konfigurieren, \u00fcberpr\u00fcfe folgendes:\n - Das NibeGW Ger\u00e4t ist an eine W\u00e4rmepumpe angeschlossen.\n - Das MODBUS40-Zubeh\u00f6r wurde in der Konfiguration der W\u00e4rmepumpe aktiviert.\n - Die Pumpe ist nicht in einen Alarmzustand wegen fehlendem MODBUS40-Zubeh\u00f6r \u00fcbergegangen." }, "user": { - "description": "W\u00e4hle die Verbindungsmethode zu deiner Pumpe. Im Allgemeinen erfordern Pumpen der F-Serie ein kundenspezifisches NibeGW-Zubeh\u00f6r, w\u00e4hrend eine Pumpe der S-Serie \u00fcber eine integrierte Modbus-Unterst\u00fctzung verf\u00fcgt.", + "description": "W\u00e4hle die Verbindungsmethode zu deiner Pumpe. Im Allgemeinen erfordern Pumpen der F-Serie ein kundenspezifisches NibeGW- Zubeh\u00f6r, w\u00e4hrend eine Pumpe der S-Serie \u00fcber eine integrierte Modbus-Unterst\u00fctzung verf\u00fcgt.", "menu_options": { "modbus": "Modbus", "nibegw": "NibeGW" diff --git a/homeassistant/components/nina/translations/de.json b/homeassistant/components/nina/translations/de.json index 4e6b881b051..f1aad460975 100644 --- a/homeassistant/components/nina/translations/de.json +++ b/homeassistant/components/nina/translations/de.json @@ -17,7 +17,7 @@ "_m_to_q": "Stadt/Landkreis (M-Q)", "_r_to_u": "Stadt/Landkreis (R-U)", "_v_to_z": "Stadt/Landkreis (V-Z)", - "corona_filter": "Corona-Warnungen entfernen", + "corona_filter": "Corona Warnungen entfernen", "slots": "Maximale Warnungen pro Stadt/Landkreis" }, "title": "Stadt/Landkreis ausw\u00e4hlen" diff --git a/homeassistant/components/nmap_tracker/translations/de.json b/homeassistant/components/nmap_tracker/translations/de.json index cfab5b828fd..074bdac0e4a 100644 --- a/homeassistant/components/nmap_tracker/translations/de.json +++ b/homeassistant/components/nmap_tracker/translations/de.json @@ -29,7 +29,7 @@ "exclude": "Netzwerkadressen (kommagetrennt), die von der \u00dcberpr\u00fcfung ausgeschlossen werden sollen", "home_interval": "Mindestanzahl von Minuten zwischen den Scans aktiver Ger\u00e4te (Batterie schonen)", "hosts": "Netzwerkadressen (kommagetrennt) zum Scannen", - "interval_seconds": "Scanintervall", + "interval_seconds": "Scan Intervall", "scan_options": "Raw konfigurierbare Scan-Optionen f\u00fcr Nmap" }, "description": "Konfiguriere die Hosts, die von Nmap gescannt werden sollen. Netzwerkadresse und Ausschl\u00fcsse k\u00f6nnen IP-Adressen (192.168.1.1), IP-Netzwerke (192.168.0.0/24) oder IP-Bereiche (192.168.1.0-32) sein." diff --git a/homeassistant/components/onewire/translations/de.json b/homeassistant/components/onewire/translations/de.json index feab77f3ec7..81430fd816b 100644 --- a/homeassistant/components/onewire/translations/de.json +++ b/homeassistant/components/onewire/translations/de.json @@ -26,7 +26,7 @@ "precision": "Sensorgenauigkeit" }, "description": "Sensorgenauigkeit f\u00fcr {sensor_id} ausw\u00e4hlen", - "title": "OneWire-Sensorpr\u00e4zision" + "title": "OneWire Sensorpr\u00e4zision" }, "device_selection": { "data": { @@ -34,7 +34,7 @@ "device_selection": "Zu konfigurierende Ger\u00e4te ausw\u00e4hlen" }, "description": "W\u00e4hle die zu verarbeitenden Konfigurationsschritte aus", - "title": "OneWire-Ger\u00e4teoptionen" + "title": "OneWire Ger\u00e4teoptionen" } } } diff --git a/homeassistant/components/openalpr_local/translations/de.json b/homeassistant/components/openalpr_local/translations/de.json index d517fe0b37f..5b5dfb52773 100644 --- a/homeassistant/components/openalpr_local/translations/de.json +++ b/homeassistant/components/openalpr_local/translations/de.json @@ -2,7 +2,7 @@ "issues": { "pending_removal": { "description": "Die lokale OpenALPR-Integration wird derzeit aus dem Home Assistant entfernt und wird ab Home Assistant 2022.10 nicht mehr verf\u00fcgbar sein.\n\nEntferne die YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die lokale OpenALPR-Integration wird entfernt" + "title": "Die lokale OpenALPR Integration wird entfernt" } } } \ No newline at end of file diff --git a/homeassistant/components/openuv/translations/de.json b/homeassistant/components/openuv/translations/de.json index 9e5c9d42b62..c38c2d33cc8 100644 --- a/homeassistant/components/openuv/translations/de.json +++ b/homeassistant/components/openuv/translations/de.json @@ -28,7 +28,7 @@ }, "issues": { "deprecated_service_multiple_alternate_targets": { - "description": "Aktualisiere alle Automatisierungen oder Skripte, die diesen Dienst verwenden, um stattdessen den Dienst `{alternate_service}` mit einer dieser Entit\u00e4ts-IDs als Ziel zu verwenden: `{alternate_targets}`.", + "description": "Aktualisiere alle Automatisierungen oder Skripte, die diesen Dienst verwenden, um stattdessen den Dienst `{alternate_service}` mit einer dieser Entit\u00e4ts IDs als Ziel zu verwenden: `{alternate_targets}`.", "title": "Der Dienst {deprecated_service} wird entfernt" }, "deprecated_service_single_alternate_target": { diff --git a/homeassistant/components/overkiz/translations/de.json b/homeassistant/components/overkiz/translations/de.json index a1e9ea43dea..7b69582fe25 100644 --- a/homeassistant/components/overkiz/translations/de.json +++ b/homeassistant/components/overkiz/translations/de.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Konto wurde bereits konfiguriert", "reauth_successful": "Die erneute Authentifizierung war erfolgreich", - "reauth_wrong_account": "Du kannst diesen Eintrag nur mit demselben Overkiz-Konto und -Hub erneut authentifizieren" + "reauth_wrong_account": "Du kannst diesen Eintrag nur mit demselben Overkiz Konto und Hub erneut authentifizieren" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", diff --git a/homeassistant/components/overkiz/translations/sensor.de.json b/homeassistant/components/overkiz/translations/sensor.de.json index 216df8f0cff..ec73c192a95 100644 --- a/homeassistant/components/overkiz/translations/sensor.de.json +++ b/homeassistant/components/overkiz/translations/sensor.de.json @@ -28,7 +28,7 @@ "wind": "Wind" }, "overkiz__sensor_defect": { - "dead": "nicht Erreichbar", + "dead": "Nicht Erreichbar", "low_battery": "Niedriger Batteriestatus", "maintenance_required": "Wartung erforderlich", "no_defect": "Kein Fehler" diff --git a/homeassistant/components/ovo_energy/translations/de.json b/homeassistant/components/ovo_energy/translations/de.json index 9ddfa873cd9..bc1a8187a29 100644 --- a/homeassistant/components/ovo_energy/translations/de.json +++ b/homeassistant/components/ovo_energy/translations/de.json @@ -16,7 +16,7 @@ }, "user": { "data": { - "account": "OVO-Konto-ID (nur hinzuf\u00fcgen, wenn mehrere Konten vorhanden sind)", + "account": "OVO Konto-ID (nur hinzuf\u00fcgen, wenn mehrere Konten vorhanden sind)", "password": "Passwort", "username": "Benutzername" }, diff --git a/homeassistant/components/point/translations/de.json b/homeassistant/components/point/translations/de.json index e902da5ab4c..ff9a3bf6c7b 100644 --- a/homeassistant/components/point/translations/de.json +++ b/homeassistant/components/point/translations/de.json @@ -3,7 +3,7 @@ "abort": { "already_setup": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich.", "authorize_url_timeout": "Zeit\u00fcberschreitung beim Erstellen der Authorisierungs-URL.", - "external_setup": "Pointt erfolgreich von einem anderen Flow konfiguriert.", + "external_setup": "Point erfolgreich von einem anderen Flow konfiguriert.", "no_flows": "Die Komponente ist nicht konfiguriert. Bitte der Dokumentation folgen.", "unknown_authorize_url_generation": "Beim Generieren einer Authentifizierungs-URL ist ein unbekannter Fehler aufgetreten" }, diff --git a/homeassistant/components/pushbullet/translations/de.json b/homeassistant/components/pushbullet/translations/de.json index 7e7fa2f0e14..31679134a31 100644 --- a/homeassistant/components/pushbullet/translations/de.json +++ b/homeassistant/components/pushbullet/translations/de.json @@ -19,7 +19,7 @@ "issues": { "deprecated_yaml": { "description": "Die Konfiguration von Pushbullet mit YAML wird entfernt. \n\nDeine vorhandene YAML-Konfiguration wurde automatisch in die Benutzeroberfl\u00e4che importiert. \n\nEntferne die Pushbullet-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Pushbullet-YAML-Konfiguration wird entfernt" + "title": "Die Pushbullet YAML-Konfiguration wird entfernt" } } } \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/de.json b/homeassistant/components/pushover/translations/de.json index 4dbb017e6d3..3791b0981dd 100644 --- a/homeassistant/components/pushover/translations/de.json +++ b/homeassistant/components/pushover/translations/de.json @@ -28,7 +28,7 @@ "issues": { "removed_yaml": { "description": "Das Konfigurieren von Pushover mit YAML wurde entfernt. \n\nDeine vorhandene YAML-Konfiguration wird von Home Assistant nicht verwendet. \n\nEntferne die Pushover-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Pushover-YAML-Konfiguration wurde entfernt" + "title": "Die Pushover YAML-Konfiguration wurde entfernt" } } } \ No newline at end of file diff --git a/homeassistant/components/radarr/translations/de.json b/homeassistant/components/radarr/translations/de.json index 33ce6eeba68..9e6a805ad94 100644 --- a/homeassistant/components/radarr/translations/de.json +++ b/homeassistant/components/radarr/translations/de.json @@ -13,7 +13,7 @@ }, "step": { "reauth_confirm": { - "description": "Die Radarr-Integration muss manuell erneut mit der Radarr-API authentifiziert werden", + "description": "Die Radarr Integration muss manuell erneut mit der Radarr-API authentifiziert werden", "title": "Integration erneut authentifizieren" }, "user": { @@ -22,17 +22,17 @@ "url": "URL", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" }, - "description": "Der API-Schl\u00fcssel kann automatisch abgerufen werden, wenn in der Anwendung keine Anmeldeinformationen festgelegt wurden.\nDeinen API-Schl\u00fcssel findest unter Einstellungen > Allgemein in der Radarr-Web-Benutzeroberfl\u00e4che." + "description": "Der API-Schl\u00fcssel kann automatisch abgerufen werden, wenn in der Anwendung keine Anmeldeinformationen festgelegt wurden.\nDeinen API-Schl\u00fcssel findest du unter Einstellungen \u2192 Allgemein in der Radarr-Web-Benutzeroberfl\u00e4che." } } }, "issues": { "deprecated_yaml": { "description": "Die Konfiguration von Radarr mit YAML wird entfernt. \n\nDeine vorhandene YAML-Konfiguration wurde automatisch in die Benutzeroberfl\u00e4che importiert. \n\nEntferne die Radarr-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Radarr-YAML-Konfiguration wird entfernt" + "title": "Die Radarr YAML-Konfiguration wird entfernt" }, "removed_yaml": { - "description": "Die Konfiguration von Radarr mittels YAML wurde entfernt.\n\nIhre bestehende YAML-Konfiguration wird vom Home Assistant nicht verwendet.\n\nEntfernen Sie die YAML-Konfiguration aus Ihrer configuration.yaml Datei und starten Sie Home Assistant neu, um dieses Problem zu beheben.", + "description": "Die Konfiguration von Radarr mittels YAML wurde entfernt.\n\nDeine bestehende YAML-Konfiguration wird vom Home Assistant nicht verwendet.\n\nEntferne die YAML-Konfiguration aus deiner configuration.yaml Datei und starte Home Assistant neu, um dieses Problem zu beheben.", "title": "Die Radarr YAML-Konfiguration wurde entfernt" } }, diff --git a/homeassistant/components/rfxtrx/translations/de.json b/homeassistant/components/rfxtrx/translations/de.json index 445a93b7e76..2661fc41211 100644 --- a/homeassistant/components/rfxtrx/translations/de.json +++ b/homeassistant/components/rfxtrx/translations/de.json @@ -68,7 +68,7 @@ "set_device_options": { "data": { "command_off": "Datenbitwert f\u00fcr den Befehl \"aus\"", - "command_on": "Datenbitwert f\u00fcr den Befehl \"ein\"", + "command_on": "Datenbitwert f\u00fcr den Befehl \"an\"", "data_bit": "Anzahl der Datenbits", "off_delay": "Ausschaltverz\u00f6gerung", "off_delay_enabled": "Ausschaltverz\u00f6gerung aktivieren", diff --git a/homeassistant/components/rhasspy/translations/de.json b/homeassistant/components/rhasspy/translations/de.json index 953cb89400a..00b09289479 100644 --- a/homeassistant/components/rhasspy/translations/de.json +++ b/homeassistant/components/rhasspy/translations/de.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "M\u00f6chtest du die Rhasspy-Unterst\u00fctzung aktivieren?" + "description": "M\u00f6chtest du die Rhasspy Unterst\u00fctzung aktivieren?" } } } diff --git a/homeassistant/components/risco/translations/de.json b/homeassistant/components/risco/translations/de.json index c4704b1d92d..3699b0ecd8f 100644 --- a/homeassistant/components/risco/translations/de.json +++ b/homeassistant/components/risco/translations/de.json @@ -25,8 +25,8 @@ }, "user": { "menu_options": { - "cloud": "Risco-Cloud (empfohlen)", - "local": "Lokales Risco-Panel (fortgeschritten)" + "cloud": "Risco Cloud (empfohlen)", + "local": "Lokales Risco Panel (fortgeschritten)" } } } diff --git a/homeassistant/components/roon/translations/de.json b/homeassistant/components/roon/translations/de.json index 8d7c53a28ee..5b839262424 100644 --- a/homeassistant/components/roon/translations/de.json +++ b/homeassistant/components/roon/translations/de.json @@ -13,7 +13,7 @@ "host": "Host", "port": "Port" }, - "description": "Der Roon-Server konnte nicht gefunden werden, bitte gib deinen Hostnamen und Port ein." + "description": "Der Roon Server konnte nicht gefunden werden, bitte gib deinen Hostnamen und Port ein." }, "link": { "description": "Du musst den Home Assistant in Roon autorisieren. Nachdem du auf \"Senden\" gedr\u00fcckt hast, gehe zur Roon Core-Anwendung, \u00f6ffne die Einstellungen und aktiviere HomeAssistant auf der Registerkarte \"Extensions\".", diff --git a/homeassistant/components/rtsp_to_webrtc/translations/de.json b/homeassistant/components/rtsp_to_webrtc/translations/de.json index 75c7d590aa8..256e3574cbd 100644 --- a/homeassistant/components/rtsp_to_webrtc/translations/de.json +++ b/homeassistant/components/rtsp_to_webrtc/translations/de.json @@ -1,25 +1,25 @@ { "config": { "abort": { - "server_failure": "Der RTSPtoWebRTC-Server hat einen Fehler gemeldet. Pr\u00fcfe die Protokolle f\u00fcr weitere Informationen.", - "server_unreachable": "Die Kommunikation mit dem RTSPtoWebRTC-Server ist nicht m\u00f6glich. Pr\u00fcfe die Protokolle f\u00fcr weitere Informationen.", + "server_failure": "Der RTSPtoWebRTC Server hat einen Fehler gemeldet. Pr\u00fcfe die Protokolle f\u00fcr weitere Informationen.", + "server_unreachable": "Die Kommunikation mit dem RTSPtoWebRTC Server ist nicht m\u00f6glich. Pr\u00fcfe die Protokolle f\u00fcr weitere Informationen.", "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." }, "error": { - "invalid_url": "Muss eine g\u00fcltige RTSPtoWebRTC-Server-URL sein, z. B. https://example.com", - "server_failure": "Der RTSPtoWebRTC-Server hat einen Fehler gemeldet. Pr\u00fcfe die Protokolle f\u00fcr weitere Informationen.", - "server_unreachable": "Die Kommunikation mit dem RTSPtoWebRTC-Server ist nicht m\u00f6glich. Pr\u00fcfe die Protokolle f\u00fcr weitere Informationen." + "invalid_url": "Muss eine g\u00fcltige RTSPtoWebRTC Server-URL sein, z. B. https://example.com", + "server_failure": "Der RTSPtoWebRTC Server hat einen Fehler gemeldet. Pr\u00fcfe die Protokolle f\u00fcr weitere Informationen.", + "server_unreachable": "Die Kommunikation mit dem RTSPtoWebRTC Server ist nicht m\u00f6glich. Pr\u00fcfe die Protokolle f\u00fcr weitere Informationen." }, "step": { "hassio_confirm": { - "description": "M\u00f6chtest du Home Assistant so konfigurieren, dass er sich mit dem RTSPtoWebRTC-Server verbindet, der vom Add-on bereitgestellt wird? {addon}?", + "description": "M\u00f6chtest du Home Assistant so konfigurieren, dass er sich mit dem RTSPtoWebRTC Server verbindet, der vom Add-on bereitgestellt wird? {addon}?", "title": "RTSPtoWebRTC \u00fcber das Home Assistant-Add-on" }, "user": { "data": { "server_url": "RTSPtoWebRTC Server URL z.B. https://example.com" }, - "description": "Die RTSPtoWebRTC-Integration erfordert einen Server, der RTSP-Streams in WebRTC \u00fcbersetzt. Gib die URL f\u00fcr den RTSPtoWebRTC-Server ein.", + "description": "Die RTSPtoWebRTC Integration erfordert einen Server, der RTSP Streams in WebRTC \u00fcbersetzt. Gib die URL f\u00fcr den RTSPtoWebRTC Server ein.", "title": "RTSPtoWebRTC konfigurieren" } } diff --git a/homeassistant/components/scrape/translations/ca.json b/homeassistant/components/scrape/translations/ca.json index 2d53d459611..5b3688f2e2d 100644 --- a/homeassistant/components/scrape/translations/ca.json +++ b/homeassistant/components/scrape/translations/ca.json @@ -57,6 +57,26 @@ }, "options": { "step": { + "add_sensor": { + "data": { + "attribute": "Atribut", + "device_class": "Classe de dispositiu", + "index": "\u00cdndex", + "select": "Selecciona", + "state_class": "Classe d'estat", + "unit_of_measurement": "Unitat de mesura", + "value_template": "Plantilla de valor" + }, + "data_description": { + "attribute": "Obt\u00e9 el valor d'un atribut de l'etiqueta seleccionada", + "device_class": "Tipus/classe del sensor per configurar-ne la icona a la interf\u00edcie d'usuari", + "index": "Defineix quins dels elements retornats pel selector CSS utilitzar", + "select": "Defineix quina etiqueta s'ha de buscar. Consulta els selectors CSS de Beautifulsoup per m\u00e9s informaci\u00f3", + "state_class": "'state_class' del sensor", + "unit_of_measurement": "Tria la unitat de mesura de temperatura o crea la teva", + "value_template": "Plantilla per obtenir l'estat del sensor" + } + }, "init": { "data": { "authentication": "Autenticaci\u00f3", @@ -68,6 +88,27 @@ "username": "Nom d'usuari", "verify_ssl": "Verifica el certificat SSL" }, + "data_description": { + "authentication": "Tipus d'autenticaci\u00f3 HTTP. O b\u00e0sica o 'digest'", + "headers": "Cap\u00e7aleres a utilitzar per a la sol\u00b7licitud web", + "resource": "URL del lloc web que cont\u00e9 el valor", + "timeout": "Temps d'espera de connexi\u00f3 al lloc web", + "verify_ssl": "Activa/desactiva la verificaci\u00f3 del certificat SSL/TLS, per exemple, si est\u00e0 autosignat" + }, + "menu_options": { + "add_sensor": "Afegeix sensor", + "remove_sensor": "Elimina sensor", + "resource": "Configura recurs" + } + }, + "resource": { + "data": { + "authentication": "Autenticaci\u00f3", + "headers": "Cap\u00e7aleres", + "method": "M\u00e8tode", + "resource": "Recurs", + "timeout": "Temps d'espera" + }, "data_description": { "authentication": "Tipus d'autenticaci\u00f3 HTTP. O b\u00e0sica o 'digest'", "headers": "Cap\u00e7aleres a utilitzar per a la sol\u00b7licitud web", diff --git a/homeassistant/components/scrape/translations/de.json b/homeassistant/components/scrape/translations/de.json index 7d1af5b3a93..9deb92dec32 100644 --- a/homeassistant/components/scrape/translations/de.json +++ b/homeassistant/components/scrape/translations/de.json @@ -52,12 +52,57 @@ "issues": { "moved_yaml": { "description": "Die Konfiguration von Scrape mit YAML wurde in den Integrationsschl\u00fcssel verschoben. \n\nDeine vorhandene YAML-Konfiguration funktioniert f\u00fcr zwei weitere Versionen.\n\nMigriere deine YAML-Konfiguration gem\u00e4\u00df der Dokumentation zum Integrationsschl\u00fcssel.", - "title": "Die Scrape-YAML-Konfiguration wurde verschoben" + "title": "Die Scrape YAML-Konfiguration wurde verschoben" } }, "options": { "step": { + "add_sensor": { + "data": { + "attribute": "Attribut", + "device_class": "Ger\u00e4teklasse", + "index": "Index", + "name": "Name", + "select": "Ausw\u00e4hlen", + "state_class": "Zustandsklasse", + "unit_of_measurement": "Ma\u00dfeinheit", + "value_template": "Wertvorlage" + }, + "data_description": { + "attribute": "Wert eines Attributs auf dem ausgew\u00e4hlten Tag abrufen", + "device_class": "Der Typ/die Klasse des Sensors, um das Symbol im Frontend festzulegen", + "index": "Definiert, welche der vom CSS-Selektor zur\u00fcckgegebenen Elemente verwendet werden sollen", + "select": "Legt fest, nach welchem Tag gesucht werden soll. Siehe Beautifulsoup CSS-Selektoren f\u00fcr Details", + "state_class": "Die state_class des Sensors", + "unit_of_measurement": "W\u00e4hle die Temperaturmessung oder erstelle deine eigene", + "value_template": "Definiert eine Vorlage, um den Zustand des Sensors zu ermitteln" + } + }, "init": { + "data": { + "authentication": "Authentifizierungsmethode ausw\u00e4hlen", + "headers": "Header", + "method": "Methode", + "password": "Passwort", + "resource": "Ressource", + "timeout": "Zeit\u00fcberschreitung", + "username": "Benutzername", + "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" + }, + "data_description": { + "authentication": "Typ der HTTP-Authentifizierung. Entweder basic oder digest", + "headers": "F\u00fcr die Webanforderung zu verwendende Header", + "resource": "Die URL der Website, die den Wert enth\u00e4lt", + "timeout": "Zeit\u00fcberschreitung f\u00fcr die Verbindung zur Website", + "verify_ssl": "Aktiviert/deaktiviert die \u00dcberpr\u00fcfung des SSL/TLS-Zertifikats, z.B. wenn es selbst signiert ist" + }, + "menu_options": { + "add_sensor": "Sensor hinzuf\u00fcgen", + "remove_sensor": "Sensor entfernen", + "resource": "Ressource konfigurieren" + } + }, + "resource": { "data": { "authentication": "Authentifizierungsmethode ausw\u00e4hlen", "headers": "Header", diff --git a/homeassistant/components/scrape/translations/en.json b/homeassistant/components/scrape/translations/en.json index 4fa261d77b3..ce66908a75d 100644 --- a/homeassistant/components/scrape/translations/en.json +++ b/homeassistant/components/scrape/translations/en.json @@ -57,13 +57,6 @@ }, "options": { "step": { - "init": { - "menu_options": { - "add_sensor": "Add sensor", - "remove_sensor": "Remove sensor", - "resource": "Configure resource" - } - }, "add_sensor": { "data": { "attribute": "Attribute", @@ -85,6 +78,30 @@ "value_template": "Defines a template to get the state of the sensor" } }, + "init": { + "data": { + "authentication": "Select authentication method", + "headers": "Headers", + "method": "Method", + "password": "Password", + "resource": "Resource", + "timeout": "Timeout", + "username": "Username", + "verify_ssl": "Verify SSL certificate" + }, + "data_description": { + "authentication": "Type of the HTTP authentication. Either basic or digest", + "headers": "Headers to use for the web request", + "resource": "The URL to the website that contains the value", + "timeout": "Timeout for connection to website", + "verify_ssl": "Enables/disables verification of SSL/TLS certificate, for example if it is self-signed" + }, + "menu_options": { + "add_sensor": "Add sensor", + "remove_sensor": "Remove sensor", + "resource": "Configure resource" + } + }, "resource": { "data": { "authentication": "Select authentication method", diff --git a/homeassistant/components/scrape/translations/es.json b/homeassistant/components/scrape/translations/es.json index f7573ef9406..a53e6545a54 100644 --- a/homeassistant/components/scrape/translations/es.json +++ b/homeassistant/components/scrape/translations/es.json @@ -13,7 +13,7 @@ "device_class": "Clase de dispositivo", "index": "\u00cdndice", "name": "Nombre", - "select": "Selecciona", + "select": "Seleccionar", "state_class": "Clase de estado", "unit_of_measurement": "Unidad de medida", "value_template": "Plantilla de valor" @@ -57,7 +57,52 @@ }, "options": { "step": { + "add_sensor": { + "data": { + "attribute": "Atributo", + "device_class": "Clase de dispositivo", + "index": "\u00cdndice", + "name": "Nombre", + "select": "Seleccionar", + "state_class": "Clase de estado", + "unit_of_measurement": "Unidad de medida", + "value_template": "Plantilla de valor" + }, + "data_description": { + "attribute": "Obtener el valor de un atributo en la etiqueta seleccionada", + "device_class": "El tipo/clase del sensor para establecer el icono en la interfaz", + "index": "Define cu\u00e1l de los elementos devueltos por el selector CSS usar", + "select": "Define qu\u00e9 etiqueta buscar. Revisa los selectores CSS de Beautifulsoup para obtener m\u00e1s informaci\u00f3n.", + "state_class": "El state_class del sensor", + "unit_of_measurement": "Elige la medici\u00f3n de temperatura o crea la suya propia", + "value_template": "Define una plantilla para obtener el estado del sensor" + } + }, "init": { + "data": { + "authentication": "Selecciona el m\u00e9todo de autenticaci\u00f3n", + "headers": "Cabeceras", + "method": "M\u00e9todo", + "password": "Contrase\u00f1a", + "resource": "Recurso", + "timeout": "Tiempo de espera", + "username": "Nombre de usuario", + "verify_ssl": "Verificar el certificado SSL" + }, + "data_description": { + "authentication": "Tipo de autenticaci\u00f3n HTTP. Puede ser basic o digest", + "headers": "Cabeceras a usar para la petici\u00f3n web", + "resource": "La URL del sitio web que contiene el valor.", + "timeout": "Tiempo de espera para la conexi\u00f3n al sitio web", + "verify_ssl": "Habilita/deshabilita la verificaci\u00f3n del certificado SSL/TLS, por ejemplo, si est\u00e1 autofirmado" + }, + "menu_options": { + "add_sensor": "A\u00f1adir sensor", + "remove_sensor": "Eliminar sensor", + "resource": "Configurar recurso" + } + }, + "resource": { "data": { "authentication": "Selecciona el m\u00e9todo de autenticaci\u00f3n", "headers": "Cabeceras", diff --git a/homeassistant/components/scrape/translations/et.json b/homeassistant/components/scrape/translations/et.json index 02d8ba32c4e..bd60ffcbcc0 100644 --- a/homeassistant/components/scrape/translations/et.json +++ b/homeassistant/components/scrape/translations/et.json @@ -57,6 +57,27 @@ }, "options": { "step": { + "add_sensor": { + "data": { + "attribute": "Attribuut", + "device_class": "Seadme klass", + "index": "Indeks", + "name": "", + "select": "Vali", + "state_class": "Olekuklass", + "unit_of_measurement": "M\u00f5\u00f5t\u00fchik", + "value_template": "V\u00e4\u00e4rtuse mall" + }, + "data_description": { + "attribute": "Hangi valitud m\u00e4rgise attribuudi v\u00e4\u00e4rtus", + "device_class": "Kuvatava ikooni t\u00fc\u00fcp/klass", + "index": "M\u00e4\u00e4rab milliseid CSS m\u00e4rgise esitatud elemente kasutada.", + "select": "Valib otsitava m\u00e4rgise. T\u00e4psem teave Beatifulsoup CSS valikutes.", + "state_class": "Anduri olekuklass", + "unit_of_measurement": "Vali temperatuuri m\u00f5\u00f5tmine v\u00f5i loo uus", + "value_template": "M\u00e4\u00e4rab malli anduri v\u00e4\u00e4rtuse jaoks" + } + }, "init": { "data": { "authentication": "Tuvastamine", @@ -74,6 +95,30 @@ "resource": "Veebilehe URL ei sisalda soovitud v\u00e4\u00e4rtusi", "timeout": "Veebilehe ajal\u00f5pp", "verify_ssl": "Lubab v\u00f5i keelab SSL/TLS serdi tuvastamise n\u00e4iteks juhul kui sert on ise allkirjastatud" + }, + "menu_options": { + "add_sensor": "Lisa andur", + "remove_sensor": "Eemalda andur", + "resource": "Seadista resurss" + } + }, + "resource": { + "data": { + "authentication": "Vali tuvastusmeetod", + "headers": "P\u00e4ised", + "method": "Meetod", + "password": "", + "resource": "Resurss", + "timeout": "Ajal\u00f5pp", + "username": "", + "verify_ssl": "" + }, + "data_description": { + "authentication": "HTTP tuvastusmeetod- kas tavaline v\u00f5i k\u00fcsitlus", + "headers": "Veebip\u00e4ringus kasutatavad p\u00e4ised", + "resource": "V\u00e4\u00e4rtust sisaldava veebilehe aadress", + "timeout": "Veebi\u00fchenduse ajal\u00f5pp", + "verify_ssl": "Keelab/lubab SSL/TLS serdi kontrolli n\u00e4iteks ise loodud sert" } } } diff --git a/homeassistant/components/scrape/translations/id.json b/homeassistant/components/scrape/translations/id.json index 1eb23995e61..f0b017d651e 100644 --- a/homeassistant/components/scrape/translations/id.json +++ b/homeassistant/components/scrape/translations/id.json @@ -54,7 +54,52 @@ }, "options": { "step": { + "add_sensor": { + "data": { + "attribute": "Atribut", + "device_class": "Kelas Perangkat", + "index": "Indeks", + "name": "Nama", + "select": "Pilih", + "state_class": "Kelas Status", + "unit_of_measurement": "Satuan Pengukuran", + "value_template": "Nilai Templat" + }, + "data_description": { + "attribute": "Dapatkan nilai atribut pada tag yang dipilih", + "device_class": "Jenis/kelas sensor untuk mengatur ikon di antarmuka", + "index": "Menentukan elemen mana yang dikembalikan oleh selektor CSS untuk digunakan", + "select": "Menentukan tag yang harus dicari. Periksa selektor CSS Beautifulsoup untuk melihat detailnya", + "state_class": "Nilai state_class dari sensor", + "unit_of_measurement": "Pilih pengukuran suhu atau buat sendiri", + "value_template": "Mendefinisikan templat untuk mendapatkan status sensor" + } + }, "init": { + "data": { + "authentication": "Pilih metode autentikasi", + "headers": "Header", + "method": "Metode", + "password": "Kata Sandi", + "resource": "Sumber Daya", + "timeout": "Tenggang waktu", + "username": "Nama Pengguna", + "verify_ssl": "Verifikasi sertifikat SSL" + }, + "data_description": { + "authentication": "Jenis autentikasi HTTP. Salah satu dari basic atau digest", + "headers": "Header yang digunakan untuk permintaan web", + "resource": "URL ke situs web yang mengandung nilai", + "timeout": "Tenggang waktu untuk koneksi ke situs web", + "verify_ssl": "Mengaktifkan/menonaktifkan verifikasi sertifikat SSL/TLS, misalnya jika sertifikat ditandatangani sendiri" + }, + "menu_options": { + "add_sensor": "Tambahkan sensor", + "remove_sensor": "Hapus sensor", + "resource": "Konfigurasikan sumber daya" + } + }, + "resource": { "data": { "authentication": "Pilih metode autentikasi", "headers": "Header", diff --git a/homeassistant/components/scrape/translations/no.json b/homeassistant/components/scrape/translations/no.json index 8b18c7f1b8c..0050ead5208 100644 --- a/homeassistant/components/scrape/translations/no.json +++ b/homeassistant/components/scrape/translations/no.json @@ -57,7 +57,52 @@ }, "options": { "step": { + "add_sensor": { + "data": { + "attribute": "Attributt", + "device_class": "Enhetsklasse", + "index": "Indeks", + "name": "Navn", + "select": "Velg", + "state_class": "Statsklasse", + "unit_of_measurement": "M\u00e5leenhet", + "value_template": "Verdimal" + }, + "data_description": { + "attribute": "F\u00e5 verdien av et attributt p\u00e5 den valgte taggen", + "device_class": "Type/klasse av sensoren for \u00e5 angi ikonet i frontend", + "index": "Definerer hvilke av elementene som returneres av CSS-velgeren som skal brukes", + "select": "Definerer hvilken tag som skal s\u00f8kes etter. Sjekk Beautifulsoup CSS-velgere for detaljer", + "state_class": "Sensorens state_class", + "unit_of_measurement": "Velg temperaturm\u00e5ling eller lag din egen", + "value_template": "Definerer en mal for \u00e5 f\u00e5 tilstanden til sensoren" + } + }, "init": { + "data": { + "authentication": "Velg autentiseringsmetode", + "headers": "Overskrifter", + "method": "Metode", + "password": "Passord", + "resource": "Ressurs", + "timeout": "Tidsavbrudd", + "username": "Brukernavn", + "verify_ssl": "Verifisere SSL-sertifikat" + }, + "data_description": { + "authentication": "Type HTTP-godkjenning. Enten grunnleggende eller ufullstendig", + "headers": "Overskrifter som skal brukes for nettforesp\u00f8rselen", + "resource": "URL-en til nettstedet som inneholder verdien", + "timeout": "Tidsavbrudd for tilkobling til nettside", + "verify_ssl": "Aktiverer/deaktiverer verifisering av SSL/TLS-sertifikat, for eksempel hvis det er selvsignert" + }, + "menu_options": { + "add_sensor": "Legg til sensor", + "remove_sensor": "Fjern sensoren", + "resource": "Konfigurer ressurs" + } + }, + "resource": { "data": { "authentication": "Velg autentiseringsmetode", "headers": "Overskrifter", diff --git a/homeassistant/components/scrape/translations/pt-BR.json b/homeassistant/components/scrape/translations/pt-BR.json index 5654259df77..729aa589102 100644 --- a/homeassistant/components/scrape/translations/pt-BR.json +++ b/homeassistant/components/scrape/translations/pt-BR.json @@ -57,6 +57,26 @@ }, "options": { "step": { + "add_sensor": { + "data": { + "attribute": "Atributo", + "device_class": "Classe de dispositivo", + "index": "\u00cdndice", + "select": "Selecione", + "state_class": "Classe do estado", + "unit_of_measurement": "Unidade de medida", + "value_template": "Modelo de valor" + }, + "data_description": { + "attribute": "Obtenha o valor de um atributo na tag selecionada", + "device_class": "O tipo/classe do sensor para definir o \u00edcone no frontend", + "index": "Define qual dos elementos retornados pelo seletor CSS usar", + "select": "Define qual tag procurar. Verifique os seletores CSS do Beautifulsoup para obter detalhes", + "state_class": "O estado_classe do sensor", + "unit_of_measurement": "Escolha a medi\u00e7\u00e3o de temperatura ou crie a sua pr\u00f3pria", + "value_template": "Define um modelo para obter o estado do sensor" + } + }, "init": { "data": { "authentication": "Selecione o m\u00e9todo de autentica\u00e7\u00e3o", @@ -74,6 +94,27 @@ "resource": "A URL para o site que cont\u00e9m o valor", "timeout": "Tempo limite para conex\u00e3o com o site", "verify_ssl": "Ativa/desativa a verifica\u00e7\u00e3o do certificado SSL/TLS, por exemplo, se for autoassinado" + }, + "menu_options": { + "add_sensor": "Adicionar sensor", + "remove_sensor": "Remover sensor", + "resource": "Configurar recurso" + } + }, + "resource": { + "data": { + "authentication": "Selecione o m\u00e9todo de autentica\u00e7\u00e3o", + "headers": "Cabe\u00e7alhos", + "method": "M\u00e9todo", + "resource": "Recurso", + "timeout": "Tempo esgotado" + }, + "data_description": { + "authentication": "Tipo da autentica\u00e7\u00e3o HTTP. Seja b\u00e1sico ou resumido", + "headers": "Cabe\u00e7alhos a serem usados para a solicita\u00e7\u00e3o da web", + "resource": "A URL para o site que cont\u00e9m o valor", + "timeout": "Tempo esgotado para conex\u00e3o com o site", + "verify_ssl": "Ativa/desativa a verifica\u00e7\u00e3o do certificado SSL/TLS, por exemplo, se for autoassinado" } } } diff --git a/homeassistant/components/scrape/translations/ru.json b/homeassistant/components/scrape/translations/ru.json index d901123236f..e212e487f98 100644 --- a/homeassistant/components/scrape/translations/ru.json +++ b/homeassistant/components/scrape/translations/ru.json @@ -51,7 +51,52 @@ }, "options": { "step": { + "add_sensor": { + "data": { + "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", + "device_class": "\u041a\u043b\u0430\u0441\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", + "index": "\u0418\u043d\u0434\u0435\u043a\u0441", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "select": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c", + "state_class": "\u041a\u043b\u0430\u0441\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f", + "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f", + "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f" + }, + "data_description": { + "attribute": "\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u0442\u0435\u0433\u0430.", + "device_class": "\u0422\u0438\u043f/\u043a\u043b\u0430\u0441\u0441 \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435.", + "index": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0438\u0437 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u043c CSS \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c.", + "select": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0442\u0435\u0433 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 CSS Beautifulsoup.", + "state_class": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 state_class \u0434\u043b\u044f \u0441\u0435\u043d\u0441\u043e\u0440\u0430.", + "unit_of_measurement": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u0435 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b \u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0441\u0432\u043e\u0435 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435", + "value_template": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u0430." + } + }, "init": { + "data": { + "authentication": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438", + "headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", + "method": "\u041c\u0435\u0442\u043e\u0434", + "password": "\u041f\u0430\u0440\u043e\u043b\u044c", + "resource": "\u0420\u0435\u0441\u0443\u0440\u0441", + "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442", + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", + "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL" + }, + "data_description": { + "authentication": "\u0422\u0438\u043f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 HTTP: basic \u0438\u043b\u0438 digest.", + "headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0434\u043b\u044f \u0432\u0435\u0431-\u0437\u0430\u043f\u0440\u043e\u0441\u0430.", + "resource": "URL-\u0430\u0434\u0440\u0435\u0441 \u0432\u0435\u0431-\u0441\u0430\u0439\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.", + "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0441\u0430\u0439\u0442\u0443", + "verify_ssl": "\u0412\u043a\u043b\u044e\u0447\u0430\u0435\u0442/\u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 SSL/TLS. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0435\u0441\u043b\u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u0441\u0430\u043c\u043e\u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439." + }, + "menu_options": { + "add_sensor": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u043d\u0441\u043e\u0440", + "remove_sensor": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0441\u0435\u043d\u0441\u043e\u0440", + "resource": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441" + } + }, + "resource": { "data": { "authentication": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438", "headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", diff --git a/homeassistant/components/scrape/translations/zh-Hant.json b/homeassistant/components/scrape/translations/zh-Hant.json index f923208c232..9c101ee5df2 100644 --- a/homeassistant/components/scrape/translations/zh-Hant.json +++ b/homeassistant/components/scrape/translations/zh-Hant.json @@ -57,7 +57,52 @@ }, "options": { "step": { + "add_sensor": { + "data": { + "attribute": "\u5c6c\u6027", + "device_class": "\u88dd\u7f6e\u985e\u5225", + "index": "\u6307\u6578", + "name": "\u540d\u7a31", + "select": "\u9078\u64c7", + "state_class": "\u72c0\u614b\u985e\u5225", + "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d", + "value_template": "\u6578\u503c\u6a21\u677f" + }, + "data_description": { + "attribute": "\u7372\u53d6\u6240\u9078\u6a19\u7c64\u5c6c\u6027\u6578\u503c", + "device_class": "\u65bc Frontend \u4e2d\u8a2d\u5b9a\u4e4b\u50b3\u611f\u5668\u985e\u578b/\u985e\u5225\u5716\u793a", + "index": "\u5b9a\u7fa9\u4f7f\u7528 CSS selector \u56de\u8986\u5143\u7d20", + "select": "\u5b9a\u7fa9\u8981\u7d22\u7684\u6a19\u7c64\u3002\u53c3\u95b1 Beautifulsoup CSS selector \u4ee5\u7372\u5f97\u8a73\u7d30\u8cc7\u8a0a", + "state_class": "\u611f\u6e2c\u5668 state_class", + "unit_of_measurement": "\u9078\u64c7\u6eab\u5ea6\u6e2c\u91cf\u6216\u8005\u5275\u5efa\u81ea\u5b9a\u7fa9", + "value_template": "\u5b9a\u7fa9\u6a21\u677f\u4ee5\u53d6\u5f97\u611f\u6e2c\u5668\u72c0\u614b" + } + }, "init": { + "data": { + "authentication": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f", + "headers": "Headers", + "method": "\u65b9\u5f0f", + "password": "\u5bc6\u78bc", + "resource": "\u4f86\u6e90", + "timeout": "\u903e\u6642", + "username": "\u4f7f\u7528\u8005\u540d\u7a31", + "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" + }, + "data_description": { + "authentication": "HTTP \u9a57\u8b49\u985e\u578b\u3002\u57fa\u672c\u6216\u6458\u8981", + "headers": "\u7528\u65bc Web \u8acb\u6c42\u4e4b Headers", + "resource": "\u5305\u542b\u6578\u503c\u7684\u7db2\u7ad9 URL", + "timeout": "\u7db2\u7ad9\u9023\u7dda\u903e\u6642", + "verify_ssl": "\u958b\u555f/\u95dc\u9589 SSL/TLS \u9a57\u8b49\u8a8d\u8b49\uff0c\u4f8b\u5982\u81ea\u7c3d\u7ae0\u6191\u8b49" + }, + "menu_options": { + "add_sensor": "\u65b0\u589e\u611f\u6e2c\u5668", + "remove_sensor": "\u79fb\u9664\u611f\u6e2c\u5668", + "resource": "\u8a2d\u5b9a\u4f86\u6e90" + } + }, + "resource": { "data": { "authentication": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f", "headers": "Headers", diff --git a/homeassistant/components/senseme/translations/de.json b/homeassistant/components/senseme/translations/de.json index 01463118ec0..316b7b3b437 100644 --- a/homeassistant/components/senseme/translations/de.json +++ b/homeassistant/components/senseme/translations/de.json @@ -11,7 +11,7 @@ "flow_title": "{name} - {model} ({host})", "step": { "discovery_confirm": { - "description": "M\u00f6chtest du {name} - {model} ( {host} ) einrichten?" + "description": "M\u00f6chtest du {name} - {model} ({host}) einrichten?" }, "manual": { "data": { @@ -23,7 +23,7 @@ "data": { "device": "Ger\u00e4t" }, - "description": "W\u00e4hle ein Ger\u00e4t aus, oder w\u00e4hle \"IP-Adresse\", um eine IP-Adresse manuell einzugeben." + "description": "W\u00e4hle ein Ger\u00e4t aus oder w\u00e4hle \"IP-Adresse\", um eine IP-Adresse manuell einzugeben." } } } diff --git a/homeassistant/components/shelly/translations/de.json b/homeassistant/components/shelly/translations/de.json index cf030e13a11..c0cc533d92d 100644 --- a/homeassistant/components/shelly/translations/de.json +++ b/homeassistant/components/shelly/translations/de.json @@ -66,7 +66,7 @@ "step": { "init": { "data": { - "ble_scanner_mode": "Bluetooth-Scannermodus" + "ble_scanner_mode": "Bluetooth Scannermodus" }, "description": "Bluetooth-Scannen kann aktiv oder passiv sein. Bei aktiv fordert Shelly Daten von Ger\u00e4ten in der N\u00e4he an; Mit Passiv empf\u00e4ngt Shelly unaufgefordert Daten von Ger\u00e4ten in der N\u00e4he." } diff --git a/homeassistant/components/simplisafe/translations/de.json b/homeassistant/components/simplisafe/translations/de.json index 7815189cb0e..6ec9e183040 100644 --- a/homeassistant/components/simplisafe/translations/de.json +++ b/homeassistant/components/simplisafe/translations/de.json @@ -16,13 +16,13 @@ "data": { "auth_code": "Autorisierungscode" }, - "description": "SimpliSafe authentifiziert die Benutzer \u00fcber seine Web-App. Aufgrund technischer Beschr\u00e4nkungen gibt es am Ende dieses Prozesses einen manuellen Schritt; bitte stelle sicher, dass du die [Dokumentation] (http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) liest, bevor du beginnst.\n\nWenn du bereit bist, dr\u00fccke [hier]({url}), um die SimpliSafe-Webanwendung zu \u00f6ffnen und deine Anmeldedaten einzugeben. Wenn du dich bereits bei SimpliSafe in deinem Browser angemeldet hast, kannst du eine neue Registerkarte \u00f6ffnen und dann die oben genannte URL in diese Registerkarte kopieren/einf\u00fcgen.\n\nWenn der Vorgang abgeschlossen ist, kehre hierher zur\u00fcck und gib den Autorisierungscode von der URL \"com.simplisafe.mobile\" ein." + "description": "SimpliSafe authentifiziert die Benutzer \u00fcber seine Web-App. Aufgrund technischer Beschr\u00e4nkungen gibt es am Ende dieses Prozesses einen manuellen Schritt; bitte stelle sicher, dass du die [Dokumentation] (http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code) liest, bevor du beginnst.\n\nWenn du bereit bist, dr\u00fccke [hier]({url}), um die SimpliSafe-Webanwendung zu \u00f6ffnen und deine Anmeldedaten einzugeben. Wenn du dich bereits bei SimpliSafe in deinem Browser angemeldet hast, kannst du eine neue Registerkarte \u00f6ffnen und dann die oben genannte URL in diese Registerkarte kopieren/einf\u00fcgen.\n\nSobald der Vorgang abgeschlossen ist, kehre hierher zur\u00fcck und gib den Autorisierungscode von der URL \"com.simplisafe.mobile\" ein." } } }, "issues": { "deprecated_service": { - "description": "Aktualisiere alle Automatisierungen oder Skripte, die diesen Dienst verwenden, um stattdessen den Dienst `{alternate_service}` mit einer Ziel-Entit\u00e4ts-ID von `{alternate_target}` zu verwenden. Dr\u00fccke dann unten auf SENDEN, um dieses Problem als behoben zu markieren.", + "description": "Aktualisiere alle Automatisierungen oder Skripte, die diesen Dienst verwenden, um stattdessen den Dienst `{alternate_service}` mit einer Ziel-Entit\u00e4ts ID von `{alternate_target}` zu verwenden. Dr\u00fccke dann unten auf SENDEN, um dieses Problem als behoben zu markieren.", "title": "Der Dienst {deprecated_service} wird entfernt" } }, diff --git a/homeassistant/components/soundtouch/translations/de.json b/homeassistant/components/soundtouch/translations/de.json index 379516e31be..8ddf597c477 100644 --- a/homeassistant/components/soundtouch/translations/de.json +++ b/homeassistant/components/soundtouch/translations/de.json @@ -13,8 +13,8 @@ } }, "zeroconf_confirm": { - "description": "Du bist im Begriff, das SoundTouch-Ger\u00e4t mit dem Namen `{name}` zu Home Assistant hinzuzuf\u00fcgen.", - "title": "Best\u00e4tige das Hinzuf\u00fcgen des Bose SoundTouch-Ger\u00e4ts" + "description": "Du bist im Begriff, das SoundTouch Ger\u00e4t mit dem Namen `{name}` zu Home Assistant hinzuzuf\u00fcgen.", + "title": "Best\u00e4tige das Hinzuf\u00fcgen des Bose SoundTouch Ger\u00e4ts" } } }, diff --git a/homeassistant/components/spotify/translations/de.json b/homeassistant/components/spotify/translations/de.json index 9d48e7b58e2..866590d00cb 100644 --- a/homeassistant/components/spotify/translations/de.json +++ b/homeassistant/components/spotify/translations/de.json @@ -22,7 +22,7 @@ "issues": { "removed_yaml": { "description": "Die Konfiguration von Spotify \u00fcber YAML wurde entfernt.\n\nDeine bestehende YAML-Konfiguration wird von Home Assistant nicht verwendet.\n\nEntferne die YAML-Konfiguration aus deiner configuration.yaml-Datei und starte den Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Spotify-YAML-Konfiguration wurde entfernt" + "title": "Die Spotify YAML-Konfiguration wurde entfernt" } }, "system_health": { diff --git a/homeassistant/components/statistics/translations/de.json b/homeassistant/components/statistics/translations/de.json index f29b161ec39..54e95c647c1 100644 --- a/homeassistant/components/statistics/translations/de.json +++ b/homeassistant/components/statistics/translations/de.json @@ -5,7 +5,7 @@ "title": "Obligatorisches 'state_characteristic' wird f\u00fcr eine Statistikentit\u00e4t angenommen" }, "deprecation_warning_size": { - "description": "Der Konfigurationsparameter `sampling_size` der Statistikintegration war bisher standardm\u00e4\u00dfig auf den Wert 20 eingestellt, was sich \u00e4ndern wird.\n\nBitte \u00fcberpr\u00fcfe die Konfiguration f\u00fcr Sensor `{entity}` und f\u00fcge geeignete Grenzen hinzu, zB `sampling_size: 20`, um das aktuelle Verhalten beizubehalten. Die Konfiguration der Statistikintegration wird mit Version 2022.12.0 flexibler und akzeptiert entweder \u201esampling_size\u201c oder \u201emax_age\u201c oder beide Einstellungen. Die obige Anfrage bereitet deine Konfiguration auf diese ansonsten bahnbrechende \u00c4nderung vor. \n\nLies die Dokumentation der Statistikintegration f\u00fcr weitere Details: https://www.home-assistant.io/integrations/statistics/", + "description": "Der Konfigurationsparameter `sampling_size` der Statistikintegration war bisher standardm\u00e4\u00dfig auf den Wert 20 eingestellt, was sich \u00e4ndern wird.\n\nBitte \u00fcberpr\u00fcfe die Konfiguration f\u00fcr Sensor `{entity}` und f\u00fcge geeignete Grenzen hinzu, z.B. `sampling_size: 20`, um das aktuelle Verhalten beizubehalten. Die Konfiguration der Statistikintegration wird mit Version 2022.12.0 flexibler und akzeptiert entweder \u201esampling_size\u201c oder \u201emax_age\u201c oder beide Einstellungen. Die obige Anfrage bereitet deine Konfiguration auf diese ansonsten bahnbrechende \u00c4nderung vor. \n\nLies die Dokumentation der Statistikintegration f\u00fcr weitere Details: https://www.home-assistant.io/integrations/statistics/", "title": "Implizite 'sampling_size' angenommen f\u00fcr eine Statistikentit\u00e4t" } } diff --git a/homeassistant/components/steam_online/translations/de.json b/homeassistant/components/steam_online/translations/de.json index aef08bfe269..26658f85abc 100644 --- a/homeassistant/components/steam_online/translations/de.json +++ b/homeassistant/components/steam_online/translations/de.json @@ -27,12 +27,12 @@ "issues": { "removed_yaml": { "description": "Die Konfiguration von Steam \u00fcber YAML wurde entfernt.\n\nDeine bestehende YAML-Konfiguration wird von Home Assistant nicht verwendet.\n\nEntferne die YAML-Konfiguration aus deiner configuration.yaml-Datei und starte den Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Steam-YAML-Konfiguration wurde entfernt" + "title": "Die Steam YAML-Konfiguration wurde entfernt" } }, "options": { "error": { - "unauthorized": "Freundesliste eingeschr\u00e4nkt: Bitte lies in der Dokumentation nach, wie du alle anderen Freunde sehen kannst" + "unauthorized": "Freundesliste eingeschr\u00e4nkt: Bitte lese in der Dokumentation nach, wie du alle anderen Freunde sehen kannst" }, "step": { "init": { diff --git a/homeassistant/components/steamist/translations/de.json b/homeassistant/components/steamist/translations/de.json index 30aa55c998e..41522562a0c 100644 --- a/homeassistant/components/steamist/translations/de.json +++ b/homeassistant/components/steamist/translations/de.json @@ -5,7 +5,7 @@ "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", "cannot_connect": "Verbindung fehlgeschlagen", "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", - "not_steamist_device": "Kein Steamist-Ger\u00e4t" + "not_steamist_device": "Kein Steamist Ger\u00e4t" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", @@ -14,7 +14,7 @@ "flow_title": "{name} ({ipaddress})", "step": { "discovery_confirm": { - "description": "M\u00f6chtest du {name} ( {ipaddress} ) einrichten?" + "description": "M\u00f6chtest du {name} ({ipaddress}) einrichten?" }, "pick_device": { "data": { diff --git a/homeassistant/components/subaru/translations/de.json b/homeassistant/components/subaru/translations/de.json index 2156df0d1a8..bfb5ad316be 100644 --- a/homeassistant/components/subaru/translations/de.json +++ b/homeassistant/components/subaru/translations/de.json @@ -11,7 +11,7 @@ "incorrect_pin": "Falsche PIN", "incorrect_validation_code": "Falscher Validierungscode", "invalid_auth": "Ung\u00fcltige Authentifizierung", - "two_factor_request_failed": "Anfrage f\u00fcr 2FA-Code fehlgeschlagen, bitte versuche es erneut" + "two_factor_request_failed": "Anfrage f\u00fcr 2FA Code fehlgeschlagen, bitte versuche es erneut" }, "step": { "pin": { diff --git a/homeassistant/components/switchbee/translations/de.json b/homeassistant/components/switchbee/translations/de.json index 4507dfd8ff9..a943e05be6f 100644 --- a/homeassistant/components/switchbee/translations/de.json +++ b/homeassistant/components/switchbee/translations/de.json @@ -15,7 +15,7 @@ "password": "Passwort", "username": "Benutzername" }, - "description": "Einrichten der SwitchBee-Integration mit Home Assistant." + "description": "Einrichten der SwitchBee Integration mit Home Assistant." } } } diff --git a/homeassistant/components/synology_dsm/translations/de.json b/homeassistant/components/synology_dsm/translations/de.json index 247eba408c9..e4b9041441d 100644 --- a/homeassistant/components/synology_dsm/translations/de.json +++ b/homeassistant/components/synology_dsm/translations/de.json @@ -54,7 +54,7 @@ "init": { "data": { "scan_interval": "Minuten zwischen den Scans", - "snap_profile_type": "Qualit\u00e4tsstufe der Kamera-Schnappsch\u00fcsse (0:hoch 1:mittel 2:niedrig)", + "snap_profile_type": "Qualit\u00e4tsstufe der Kamera-Schnappsch\u00fcsse (0: hoch, 1: mittel, 2: niedrig)", "timeout": "Timeout (Sekunden)" } } diff --git a/homeassistant/components/tasmota/translations/de.json b/homeassistant/components/tasmota/translations/de.json index cd1da24e22c..b8a2cf9a2a7 100644 --- a/homeassistant/components/tasmota/translations/de.json +++ b/homeassistant/components/tasmota/translations/de.json @@ -19,12 +19,12 @@ }, "issues": { "topic_duplicated": { - "description": "Mehrere Tasmota-Ger\u00e4te teilen das Topic {topic} . \n\nTasmota-Ger\u00e4te mit diesem Problem: {offenders} .", - "title": "Mehrere Tasmota-Ger\u00e4te teilen das gleiche Topic" + "description": "Mehrere Tasmota Ger\u00e4te teilen das Topic {topic}. \n\nTasmota Ger\u00e4te mit diesem Problem: {offenders}.", + "title": "Mehrere Tasmota Ger\u00e4te teilen das gleiche Topic" }, "topic_no_prefix": { - "description": "Tasmota-Ger\u00e4t {name} mit IP {ip} enth\u00e4lt `%prefix%` nicht in seinem vollst\u00e4ndigen Topic. \n\nEntit\u00e4ten f\u00fcr diese Ger\u00e4te sind deaktiviert, bis die Konfiguration korrigiert wurde.", - "title": "Tasmota-Ger\u00e4t {name} hat ein ung\u00fcltiges MQTT-Topic" + "description": "Tasmota Ger\u00e4t {name} mit IP {ip} enth\u00e4lt `%prefix%` nicht in seinem vollst\u00e4ndigen Topic. \n\nEntit\u00e4ten f\u00fcr diese Ger\u00e4te sind deaktiviert, bis die Konfiguration korrigiert wurde.", + "title": "Tasmota Ger\u00e4t {name} hat ein ung\u00fcltiges MQTT-Topic" } } } \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/de.json b/homeassistant/components/tautulli/translations/de.json index d58c60f88fc..874d4c9aaab 100644 --- a/homeassistant/components/tautulli/translations/de.json +++ b/homeassistant/components/tautulli/translations/de.json @@ -14,7 +14,7 @@ "data": { "api_key": "API-Schl\u00fcssel" }, - "description": "Um deinen API-Schl\u00fcssel zu finden, \u00f6ffne die Tautulli-Webseite und navigiere zu Einstellungen und dann zu Webinterface. Der API-Schl\u00fcssel befindet sich unten auf dieser Seite.", + "description": "Um deinen API-Schl\u00fcssel zu finden, \u00f6ffne die Tautulli Webseite und navigiere zu Einstellungen und dann zu Webinterface. Der API-Schl\u00fcssel befindet sich unten auf dieser Seite.", "title": "Tautulli erneut authentifizieren" }, "user": { @@ -23,7 +23,7 @@ "url": "URL", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" }, - "description": "Um deinen API-Schl\u00fcssel zu finden, \u00f6ffne die Tautulli-Webseite und navigiere zu Einstellungen und dann zu Webinterface. Der API-Schl\u00fcssel befindet sich unten auf dieser Seite.\n\nBeispiel f\u00fcr die URL: ```http://192.168.0.10:8181`` mit 8181 als Standard-Port." + "description": "Um deinen API-Schl\u00fcssel zu finden, \u00f6ffne die Tautulli Webseite und navigiere zu Einstellungen und dann zu Webinterface. Der API-Schl\u00fcssel befindet sich unten auf dieser Seite.\n\nBeispiel f\u00fcr die URL: ```http://192.168.0.10:8181`` mit 8181 als Standard-Port." } } } diff --git a/homeassistant/components/tellduslive/translations/de.json b/homeassistant/components/tellduslive/translations/de.json index d77265bb351..78662ca4536 100644 --- a/homeassistant/components/tellduslive/translations/de.json +++ b/homeassistant/components/tellduslive/translations/de.json @@ -11,7 +11,7 @@ }, "step": { "auth": { - "description": "So verkn\u00fcpfest du dein TelldusLive-Konto: \n 1. Dr\u00fccke auf den Link unten \n 2. Melde dich bei Telldus Live an \n 3. Autorisiere ** {app_name} ** (dr\u00fccke auf ** Yes **). \n 4. Komme hierher zur\u00fcck und kdr\u00fccke auf **SENDEN**. \n\n [Link TelldusLive-Konto]({auth_url})", + "description": "So verkn\u00fcpfst du dein TelldusLive-Konto: \n 1. Dr\u00fccke auf den Link unten \n 2. Melde dich bei Telldus Live an \n 3. Autorisiere ** {app_name} ** (dr\u00fccke auf ** Yes **). \n 4. Komme hierher zur\u00fcck und kdr\u00fccke auf **SENDEN**. \n\n [Link TelldusLive-Konto]({auth_url})", "title": "Authentifiziere dich gegen TelldusLive" }, "user": { diff --git a/homeassistant/components/threshold/translations/de.json b/homeassistant/components/threshold/translations/de.json index f8f805742ca..9a8d650482c 100644 --- a/homeassistant/components/threshold/translations/de.json +++ b/homeassistant/components/threshold/translations/de.json @@ -12,7 +12,7 @@ "name": "Name", "upper": "Obergrenze" }, - "description": "Erstellen eines bin\u00e4ren Sensors, der sich je nach Wert eines Sensors ein- und ausschaltet\n\nNur unterer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors kleiner als der untere Grenzwert ist.\nNur oberer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors gr\u00f6\u00dfer als der obere Grenzwert ist.\nSowohl untere als auch obere Grenze konfiguriert - Einschalten, wenn der Wert des Eingangssensors im Bereich [untere Grenze ... obere Grenze] liegt.", + "description": "Erstellen eines bin\u00e4ren Sensors, der sich je nach Wert eines Sensors ein- und ausschaltet\n\nNur unterer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors kleiner als der untere Grenzwert ist.\nNur oberer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors gr\u00f6\u00dfer als der obere Grenzwert ist.\nSowohl untere als auch obere Grenze konfiguriert - Einschalten, wenn der Wert des Eingangssensors im Bereich [untere Grenze \u2026 obere Grenze] liegt.", "title": "Schwellenwertsensor hinzuf\u00fcgen" } } @@ -30,7 +30,7 @@ "name": "Name", "upper": "Obergrenze" }, - "description": "Nur unterer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors kleiner als der untere Grenzwert ist.\nNur oberer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors gr\u00f6\u00dfer als der obere Grenzwert ist.\nSowohl unterer als auch oberer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors im Bereich [unterer Grenzwert ... oberer Grenzwert] liegt." + "description": "Nur unterer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors kleiner als der untere Grenzwert ist.\nNur oberer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors gr\u00f6\u00dfer als der obere Grenzwert ist.\nSowohl unterer als auch oberer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors im Bereich [unterer Grenzwert \u2026 oberer Grenzwert] liegt." } } }, diff --git a/homeassistant/components/timer/translations/de.json b/homeassistant/components/timer/translations/de.json index ab36cdd7e93..daf3df59514 100644 --- a/homeassistant/components/timer/translations/de.json +++ b/homeassistant/components/timer/translations/de.json @@ -2,7 +2,7 @@ "state": { "_": { "active": "Aktiv", - "idle": "inaktiv", + "idle": "Inaktiv", "paused": "Pausiert" } } diff --git a/homeassistant/components/tod/translations/de.json b/homeassistant/components/tod/translations/de.json index 663dc21c993..9907cdd3406 100644 --- a/homeassistant/components/tod/translations/de.json +++ b/homeassistant/components/tod/translations/de.json @@ -22,5 +22,5 @@ } } }, - "title": "Tageszeitensensor" + "title": "Tageszeitsensor" } \ No newline at end of file diff --git a/homeassistant/components/transmission/translations/de.json b/homeassistant/components/transmission/translations/de.json index 3f0a2364ce8..1d04d0674a7 100644 --- a/homeassistant/components/transmission/translations/de.json +++ b/homeassistant/components/transmission/translations/de.json @@ -34,7 +34,7 @@ "fix_flow": { "step": { "confirm": { - "description": "Aktualisiere alle Automatisierungen oder Skripte, die diesen Dienst verwenden, und ersetze den Namensschl\u00fcssel durch den Entry_id-Schl\u00fcssel.", + "description": "Aktualisiere alle Automatisierungen oder Skripte, die diesen Dienst verwenden, und ersetze den Namensschl\u00fcssel durch den entry_id Schl\u00fcssel.", "title": "Der Namensschl\u00fcssel in den \u00dcbertragungsdiensten wird entfernt" } } diff --git a/homeassistant/components/twinkly/translations/de.json b/homeassistant/components/twinkly/translations/de.json index 5d096c5b594..6512565c072 100644 --- a/homeassistant/components/twinkly/translations/de.json +++ b/homeassistant/components/twinkly/translations/de.json @@ -8,7 +8,7 @@ }, "step": { "discovery_confirm": { - "description": "M\u00f6chtest du {name} - {model} ( {host} ) einrichten?" + "description": "M\u00f6chtest du {name} - {model} ({host}) einrichten?" }, "user": { "data": { diff --git a/homeassistant/components/unifiprotect/translations/de.json b/homeassistant/components/unifiprotect/translations/de.json index 5e0dfcb650f..8cb94d2cc01 100644 --- a/homeassistant/components/unifiprotect/translations/de.json +++ b/homeassistant/components/unifiprotect/translations/de.json @@ -21,12 +21,12 @@ }, "reauth_confirm": { "data": { - "host": "IP/Host des UniFi Protect-Servers", + "host": "IP/Host des UniFi Protect Servers", "password": "Passwort", "port": "Port", "username": "Benutzername" }, - "title": "UniFi Protect Reauth" + "title": "UniFi Protect Reauthentifizierung" }, "user": { "data": { @@ -37,33 +37,33 @@ "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" }, "description": "Du ben\u00f6tigst einen lokalen Benutzer, den du in deiner UniFi OS-Konsole angelegt hast, um sich damit anzumelden. Ubiquiti Cloud-Benutzer funktionieren nicht. F\u00fcr weitere Informationen: {local_user_documentation_url}", - "title": "UniFi Protect-Einrichtung" + "title": "UniFi Protect Einrichtung" } } }, "issues": { "deprecate_smart_sensor": { - "description": "Der einheitliche Sensor \u201eErkanntes Objekt\u201c f\u00fcr intelligente Erkennungen ist jetzt veraltet. Er wurde durch einzelne bin\u00e4re Smart-Detection-Sensoren f\u00fcr jeden Smart-Detection-Typ ersetzt. Bitte aktualisiere alle Vorlagen oder Automatisierungen entsprechend.", + "description": "Der einheitliche Sensor \u201eErkanntes Objekt\u201c f\u00fcr intelligente Erkennungen ist jetzt veraltet. Er wurde durch einzelne bin\u00e4re Smart Detection Sensoren f\u00fcr jeden Smart Detection Typ ersetzt. Bitte aktualisiere alle Vorlagen oder Automatisierungen entsprechend.", "title": "Smart Detection Sensor veraltet" }, "ea_setup_failed": { "description": "Du verwendest v{version} von UniFi Protect, eine Early-Access-Version. Beim Versuch, die Integration zu laden, ist ein nicht behebbarer Fehler aufgetreten. Bitte f\u00fchre ein [Downgrade auf eine stabile Version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) von UniFi Protect durch, um die Integration weiterhin zu verwenden. \n\nFehler: {error}", - "title": "Einrichtungsfehler bei Verwendung der Early-Access-Version" + "title": "Einrichtungsfehler bei Verwendung der Early-Access Version" }, "ea_warning": { "fix_flow": { "step": { "confirm": { "description": "M\u00f6chtest du wirklich nicht unterst\u00fctzte Versionen von UniFi Protect ausf\u00fchren? Dies kann dazu f\u00fchren, dass deine Home Assistant-Integration nicht mehr funktioniert.", - "title": "v{version} ist eine Early-Access-Version" + "title": "v{version} ist eine Early-Access Version" }, "start": { - "description": "Du verwendest v{version} von UniFi Protect, eine Early-Access-Version. [Early-Access-Versionen werden von Home Assistant nicht unterst\u00fctzt](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) und es wird empfohlen, sobald wie m\u00f6glich zu einer stabilen Version zur\u00fcckzukehren. \n\nDurch das Absenden dieses Formulars hast du entweder [UniFi Protect heruntergestuft](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) oder du stimmst zu, eine nicht unterst\u00fctzte Version von UniFi Protect auszuf\u00fchren.", - "title": "v{version} ist eine Early-Access-Version" + "description": "Du verwendest v{version} von UniFi Protect, eine Early-Access-Version. [Early-Access-Versionen werden von Home Assistant nicht unterst\u00fctzt](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) und es wird empfohlen, so bald wie m\u00f6glich zu einer stabilen Version zur\u00fcckzukehren. \n\nDurch das Absenden dieses Formulars hast du entweder [UniFi Protect heruntergestuft](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) oder du stimmst zu, eine nicht unterst\u00fctzte Version von UniFi Protect auszuf\u00fchren.", + "title": "v{version} ist eine Early-Access Version" } } }, - "title": "UniFi Protect v{version} ist eine Early-Access-Version" + "title": "UniFi Protect v{version} ist eine Early Access Version" } }, "options": { @@ -77,7 +77,7 @@ "override_connection_host": "Verbindungshost \u00fcberschreiben" }, "description": "Die Option Echtzeit-Metriken sollte nur aktiviert werden, wenn du die Diagnosesensoren aktiviert hast und diese in Echtzeit aktualisiert werden sollen. Wenn sie nicht aktiviert ist, werden sie nur einmal alle 15 Minuten aktualisiert.", - "title": "UniFi Protect-Optionen" + "title": "UniFi Protect Optionen" } } } diff --git a/homeassistant/components/unifiprotect/translations/et.json b/homeassistant/components/unifiprotect/translations/et.json index 1ae73482a13..f1a3ca6cf12 100644 --- a/homeassistant/components/unifiprotect/translations/et.json +++ b/homeassistant/components/unifiprotect/translations/et.json @@ -42,6 +42,10 @@ } }, "issues": { + "deprecate_smart_sensor": { + "description": "\u00dchtse \"Avastatud objekti\" andur arukate tuvastuste jaoks on n\u00fc\u00fcdseks kaotanud kehtivuse. See on asendatud iga aruka avastamise t\u00fc\u00fcbi jaoks eraldi aruka avastamise binaarsete anduritega. Palun ajakohasta vastavalt k\u00f5ik mallid v\u00f5i automaatika.", + "title": "Nutika tuvastamise andur on aegunud" + }, "ea_setup_failed": { "description": "Kasutad UniFi Protecti v {version} mis on varajase juurdep\u00e4\u00e4su versioon. Sidumise laadimisel ilmnes parandamatu viga. Sidumise kasutamise j\u00e4tkamiseks [alanda UniFi Protecti stabiilsele versioonile](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect). \n\n Viga: {error}", "title": "Varajase juurdep\u00e4\u00e4su versiooni h\u00e4\u00e4lestamise t\u00f5rge" diff --git a/homeassistant/components/unifiprotect/translations/id.json b/homeassistant/components/unifiprotect/translations/id.json index 6453d8c8d86..6fcaf1c19fb 100644 --- a/homeassistant/components/unifiprotect/translations/id.json +++ b/homeassistant/components/unifiprotect/translations/id.json @@ -42,6 +42,10 @@ } }, "issues": { + "deprecate_smart_sensor": { + "description": "Sensor \"Objek Terdeteksi\" terpadu untuk deteksi cerdas sekarang sudah tidak digunakan lagi. Sensor ini telah diganti dengan sensor biner deteksi cerdas individual untuk setiap jenis deteksi cerdas. Perbarui semua templat atau otomasi yang terkait.", + "title": "Sensor Deteksi Cerdas Tidak Digunakan Lagi" + }, "ea_setup_failed": { "description": "Anda menggunakan v{version} dari UniFi Protect yang merupakan versi Early Access. Terjadi kesalahan yang tidak dapat dipulihkan saat mencoba memuat integrasi. Silakan [turunkan ke versi stabil](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) dari UniFi Protect untuk terus menggunakan integrasi.\n\nKesalahan: {error}", "title": "Kesalahan penyiapan menggunakan versi Early Access" diff --git a/homeassistant/components/unifiprotect/translations/no.json b/homeassistant/components/unifiprotect/translations/no.json index 74b59ebb9b4..195b7da31a4 100644 --- a/homeassistant/components/unifiprotect/translations/no.json +++ b/homeassistant/components/unifiprotect/translations/no.json @@ -42,6 +42,10 @@ } }, "issues": { + "deprecate_smart_sensor": { + "description": "Den enhetlige \u00abDetected Object\u00bb-sensoren for smarte deteksjoner er n\u00e5 avviklet. Den er erstattet med individuelle smartdeteksjonsbin\u00e6re sensorer for hver smartdeteksjonstype. Oppdater eventuelle maler eller automatiseringer tilsvarende.", + "title": "Smart deteksjonssensor avviklet" + }, "ea_setup_failed": { "description": "Du bruker v {version} av UniFi Protect som er en tidlig tilgangsversjon. Det oppstod en uopprettelig feil under fors\u00f8k p\u00e5 \u00e5 laste integrasjonen. Vennligst [nedgrader til en stabil versjon](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) av UniFi Protect for \u00e5 fortsette \u00e5 bruke integrasjonen. \n\n Feil: {error}", "title": "Konfigurasjonsfeil ved bruk av tidlig tilgangsversjon" diff --git a/homeassistant/components/unifiprotect/translations/pt-BR.json b/homeassistant/components/unifiprotect/translations/pt-BR.json index 57be639a7f5..c9fc3beb2db 100644 --- a/homeassistant/components/unifiprotect/translations/pt-BR.json +++ b/homeassistant/components/unifiprotect/translations/pt-BR.json @@ -42,6 +42,10 @@ } }, "issues": { + "deprecate_smart_sensor": { + "description": "O sensor unificado de \"objeto detectado\" para detec\u00e7\u00f5es inteligentes agora est\u00e1 obsoleto. Ele foi substitu\u00eddo por sensores bin\u00e1rios de detec\u00e7\u00e3o inteligente individuais para cada tipo de detec\u00e7\u00e3o inteligente. Atualize quaisquer modelos ou automa\u00e7\u00f5es de acordo.", + "title": "Sensor de detec\u00e7\u00e3o inteligente obsoleto" + }, "ea_setup_failed": { "description": "Voc\u00ea est\u00e1 usando v {version} do UniFi Protect, que \u00e9 uma vers\u00e3o de acesso antecipado. Ocorreu um erro irrecuper\u00e1vel ao tentar carregar a integra\u00e7\u00e3o. Fa\u00e7a [downgrade para uma vers\u00e3o est\u00e1vel](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) do UniFi Protect para continuar usando a integra\u00e7\u00e3o. \n\n Erro: {error}", "title": "Erro de configura\u00e7\u00e3o usando a vers\u00e3o de acesso antecipado" diff --git a/homeassistant/components/unifiprotect/translations/ru.json b/homeassistant/components/unifiprotect/translations/ru.json index 5472a1b2037..51e453aebb3 100644 --- a/homeassistant/components/unifiprotect/translations/ru.json +++ b/homeassistant/components/unifiprotect/translations/ru.json @@ -42,6 +42,10 @@ } }, "issues": { + "deprecate_smart_sensor": { + "description": "\u0421\u0435\u043d\u0441\u043e\u0440 \"Detected Object\" \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0439 \u0442\u0435\u043f\u0435\u0440\u044c \u0443\u0441\u0442\u0430\u0440\u0435\u043b. \u041e\u043d \u0437\u0430\u043c\u0435\u043d\u0451\u043d \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u043c\u0438 \u0441\u0435\u043d\u0441\u043e\u0440\u0430\u043c\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f. \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0448\u0430\u0431\u043b\u043e\u043d\u044b \u0438\u043b\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0435 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b.", + "title": "\u0421\u0435\u043d\u0441\u043e\u0440 Smart Detection \u0443\u0441\u0442\u0430\u0440\u0435\u043b" + }, "ea_setup_failed": { "description": "\u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 UniFi Protect v{version}, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0435\u0440\u0441\u0438\u0435\u0439 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430. \u041f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u0443\u0441\u0442\u0440\u0430\u043d\u0438\u043c\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, [\u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect), \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.\n\n\u041e\u0448\u0438\u0431\u043a\u0430: {error}", "title": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0432\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430" diff --git a/homeassistant/components/unifiprotect/translations/zh-Hant.json b/homeassistant/components/unifiprotect/translations/zh-Hant.json index 8c1fbe273da..ca8f1297d25 100644 --- a/homeassistant/components/unifiprotect/translations/zh-Hant.json +++ b/homeassistant/components/unifiprotect/translations/zh-Hant.json @@ -42,6 +42,10 @@ } }, "issues": { + "deprecate_smart_sensor": { + "description": "\u7528\u65bc\u667a\u80fd\u5075\u6e2c\u7684\u7d71\u4e00 \"\u5075\u6e2c\u7269\u4ef6\" \u611f\u6e2c\u5668\u5df2\u68c4\u7528\uff0c\u6539\u4ee5\u500b\u5225\u667a\u80fd\u5075\u6e2c\u985e\u578b\u7684\u667a\u80fd\u5075\u6e2c\u4e8c\u9032\u4f4d\u611f\u6e2c\u5668\u4f86\u53d6\u4ee3\u3002\u8acb\u66f4\u65b0\u76f8\u5c0d\u61c9\u7684\u6a23\u677f\u6216\u81ea\u52d5\u5316\u3002", + "title": "\u667a\u80fd\u5075\u6e2c\u611f\u6e2c\u5668\u5df2\u505c\u7528" + }, "ea_setup_failed": { "description": "\u6b63\u5728\u4f7f\u7528\u7684 UniFi Protect v{version} \u7248\u6436\u5148\u9ad4\u9a57\u7248\u3002\u5617\u8a66\u8f09\u5165\u6574\u5408\u6642\u767c\u751f\u4e0d\u53ef\u6062\u5fa9\u7684\u932f\u8aa4\u3002\u8acb[\u964d\u7d1a\u81f3\u7a69\u5b9a\u7248\u672c](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) \u4e4b UniFi Protect \u4ee5\u7e7c\u7e8c\u4f7f\u7528\u6b64\u6574\u5408\u3002\n\n\u932f\u8aa4\uff1a{error}", "title": "\u4f7f\u7528\u6436\u5148\u9ad4\u9a57\u7248\u8a2d\u5b9a\u932f\u8aa4" diff --git a/homeassistant/components/utility_meter/translations/de.json b/homeassistant/components/utility_meter/translations/de.json index 870772e0dbe..798d5616edb 100644 --- a/homeassistant/components/utility_meter/translations/de.json +++ b/homeassistant/components/utility_meter/translations/de.json @@ -17,7 +17,7 @@ "offset": "Versetzen des Tages einer monatlichen Z\u00e4hlerr\u00fccksetzung.", "tariffs": "Eine Liste der unterst\u00fctzten Tarife; leer lassen, wenn nur ein einziger Tarif ben\u00f6tigt wird." }, - "description": "Erstelle einen Sensor, der den Verbrauch verschiedener Versorgungsleistungen (z. B. Energie, Gas, Wasser, Heizung) \u00fcber einen konfigurierten Zeitraum, in der Regel monatlich, erfasst. Der Sensor f\u00fcr den Verbrauchsz\u00e4hler unterst\u00fctzt optional die Aufteilung des Verbrauchs nach Tarifen. In diesem Fall wird ein Sensor f\u00fcr jeden Tarif sowie eine Auswahlm\u00f6glichkeit zur Auswahl des aktuellen Tarifs erstellt.", + "description": "Erstelle einen Sensor, der den Verbrauch verschiedener Versorgungsleistungen (z.B. Energie, Gas, Wasser, Heizung) \u00fcber einen konfigurierten Zeitraum, in der Regel monatlich, erfasst. Der Sensor f\u00fcr den Verbrauchsz\u00e4hler unterst\u00fctzt optional die Aufteilung des Verbrauchs nach Tarifen. In diesem Fall wird ein Sensor f\u00fcr jeden Tarif sowie eine Auswahlm\u00f6glichkeit zur Auswahl des aktuellen Tarifs erstellt.", "title": "Verbrauchsz\u00e4hler hinzuf\u00fcgen" } } diff --git a/homeassistant/components/vacuum/translations/de.json b/homeassistant/components/vacuum/translations/de.json index 2a76c2093a9..cb9a1ef5d83 100644 --- a/homeassistant/components/vacuum/translations/de.json +++ b/homeassistant/components/vacuum/translations/de.json @@ -18,7 +18,7 @@ "cleaning": "Reinigen", "docked": "Angedockt", "error": "Fehler", - "idle": "inaktiv", + "idle": "Inaktiv", "off": "Aus", "on": "An", "paused": "Pausiert", diff --git a/homeassistant/components/verisure/translations/de.json b/homeassistant/components/verisure/translations/de.json index b5e544e579b..d306064d121 100644 --- a/homeassistant/components/verisure/translations/de.json +++ b/homeassistant/components/verisure/translations/de.json @@ -14,7 +14,7 @@ "data": { "giid": "Installation" }, - "description": "Home Assistant hat mehrere Verisure-Installationen in deinen My Pages-Konto gefunden. Bitte w\u00e4hle die Installation aus, die du zu Home Assistant hinzuf\u00fcgen m\u00f6chtest." + "description": "Home Assistant hat mehrere Verisure-Installationen in deinem My Pages-Konto gefunden. Bitte w\u00e4hle die Installation aus, die du zu Home Assistant hinzuf\u00fcgen m\u00f6chtest." }, "mfa": { "data": { @@ -52,7 +52,7 @@ "init": { "data": { "lock_code_digits": "Anzahl der Ziffern im PIN-Code f\u00fcr Schl\u00f6sser", - "lock_default_code": "Standard-PIN-Code f\u00fcr Schl\u00f6sser, wird verwendet wenn keiner angegeben wird" + "lock_default_code": "Standard-PIN-Code f\u00fcr Schl\u00f6sser, wird verwendet, wenn keiner angegeben wird" } } } diff --git a/homeassistant/components/vizio/translations/de.json b/homeassistant/components/vizio/translations/de.json index a47c2e0f036..5d8049583de 100644 --- a/homeassistant/components/vizio/translations/de.json +++ b/homeassistant/components/vizio/translations/de.json @@ -7,7 +7,7 @@ }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", - "complete_pairing_failed": "Das Pairing konnte nicht abgeschlossen werden. Vergewissere dich, dass der eingegebene PIN korrekt ist und dass der Fernseher noch mit Strom versorgt wird und mit dem Netzwerk verbunden ist, bevor du es erneut versuchst.", + "complete_pairing_failed": "Das Pairing konnte nicht abgeschlossen werden. Vergewissere dich, dass die eingegebene PIN korrekt ist und dass der Fernseher noch mit Strom versorgt wird und mit dem Netzwerk verbunden ist, bevor du es erneut versuchst.", "existing_config_entry_found": "Ein bestehender VIZIO SmartCast-Ger\u00e4t Config-Eintrag mit der gleichen Seriennummer wurde bereits konfiguriert. Du musst den vorhandenen Eintrag l\u00f6schen, um diesen zu konfigurieren." }, "step": { diff --git a/homeassistant/components/vulcan/translations/de.json b/homeassistant/components/vulcan/translations/de.json index 66de1c8ec75..b6736e3c096 100644 --- a/homeassistant/components/vulcan/translations/de.json +++ b/homeassistant/components/vulcan/translations/de.json @@ -4,7 +4,7 @@ "all_student_already_configured": "Alle Sch\u00fcler wurden bereits hinzugef\u00fcgt.", "already_configured": "Dieser Sch\u00fcler wurde bereits hinzugef\u00fcgt.", "no_matching_entries": "Keine \u00fcbereinstimmenden Eintr\u00e4ge gefunden, bitte verwende ein anderes Konto oder entferne die Integration mit veraltetem Sch\u00fcler.", - "reauth_successful": "Reauth erfolgreich" + "reauth_successful": "Reauthentifizierung erfolgreich" }, "error": { "cannot_connect": "Verbindungsfehler - Bitte \u00fcberpr\u00fcfe deine Internetverbindung", @@ -28,7 +28,7 @@ "region": "Symbol", "token": "Token" }, - "description": "Melde dich bei deinem Vulcan-Konto \u00fcber die Registrierungsseite der mobilen App an." + "description": "Melde dich bei deinem Vulcan Konto \u00fcber die Registrierungsseite der mobilen App an." }, "reauth_confirm": { "data": { @@ -36,7 +36,7 @@ "region": "Symbol", "token": "Token" }, - "description": "Melde dich bei deinem Vulcan-Konto \u00fcber die Registrierungsseite der mobilen App an." + "description": "Melde dich bei deinem Vulcan Konto \u00fcber die Registrierungsseite der mobilen App an." }, "select_saved_credentials": { "data": { @@ -48,7 +48,7 @@ "data": { "student_name": "Sch\u00fcler ausw\u00e4hlen" }, - "description": "W\u00e4hle Sch\u00fcler aus, Du kannst weitere Sch\u00fcler hinzuf\u00fcgen, indem du die Integration erneut hinzuf\u00fcgst." + "description": "W\u00e4hle Sch\u00fcler aus, du kannst weitere Sch\u00fcler hinzuf\u00fcgen, indem du die Integration erneut hinzuf\u00fcgst." } } } diff --git a/homeassistant/components/waze_travel_time/translations/de.json b/homeassistant/components/waze_travel_time/translations/de.json index 42ef4698151..a12552c4870 100644 --- a/homeassistant/components/waze_travel_time/translations/de.json +++ b/homeassistant/components/waze_travel_time/translations/de.json @@ -31,7 +31,7 @@ "units": "Einheiten", "vehicle_type": "Fahrzeugtyp" }, - "description": "Mit den \"Substring\"-Eintr\u00e4gen kannst du die Integration zwingen, eine bestimmte Route zu verwenden oder eine bestimmte Route bei der Zeitreiseberechnung zu vermeiden." + "description": "Mit den 'Substring'-Eintr\u00e4gen kannst du die Integration zwingen, eine bestimmte Route zu verwenden oder eine bestimmte Route bei der Zeitreiseberechnung zu vermeiden." } } }, diff --git a/homeassistant/components/webostv/translations/de.json b/homeassistant/components/webostv/translations/de.json index f88412a1d67..22ac8b87663 100644 --- a/homeassistant/components/webostv/translations/de.json +++ b/homeassistant/components/webostv/translations/de.json @@ -19,7 +19,7 @@ "host": "Host", "name": "Name" }, - "description": "Schalte den TV ein, f\u00fclle die folgenden Felder aus und dr\u00fccke auf Senden", + "description": "Schalte den Fernseher ein, f\u00fclle die folgenden Felder aus und dr\u00fccke auf Senden", "title": "Mit webOS TV verbinden" } } diff --git a/homeassistant/components/whois/translations/de.json b/homeassistant/components/whois/translations/de.json index 3288dca40eb..8aff31a5281 100644 --- a/homeassistant/components/whois/translations/de.json +++ b/homeassistant/components/whois/translations/de.json @@ -4,10 +4,10 @@ "already_configured": "Der Dienst ist bereits konfiguriert" }, "error": { - "unexpected_response": "Unerwartete Antwort vom Whois-Server", - "unknown_date_format": "Unbekanntes Datumsformat in Antwort des Whois-Servers", + "unexpected_response": "Unerwartete Antwort vom Whois Server", + "unknown_date_format": "Unbekanntes Datumsformat in Antwort des Whois Servers", "unknown_tld": "Die angegebene TLD ist unbekannt oder f\u00fcr diese Integration nicht verf\u00fcgbar", - "whois_command_failed": "Whois-Befehl fehlgeschlagen: Whois-Informationen konnten nicht abgerufen werden" + "whois_command_failed": "Whois Befehl fehlgeschlagen: Whois Informationen konnten nicht abgerufen werden" }, "step": { "user": { diff --git a/homeassistant/components/wiz/translations/de.json b/homeassistant/components/wiz/translations/de.json index af503506c59..2fc23e92fdc 100644 --- a/homeassistant/components/wiz/translations/de.json +++ b/homeassistant/components/wiz/translations/de.json @@ -9,7 +9,7 @@ "bulb_time_out": "Es kann keine Verbindung zur Gl\u00fchbirne hergestellt werden. Vielleicht ist die Gl\u00fchbirne offline oder es wurde eine falsche IP eingegeben. Bitte schalte das Licht ein und versuche es erneut!", "cannot_connect": "Verbindung fehlgeschlagen", "no_ip": "Keine g\u00fcltige IP-Adresse.", - "no_wiz_light": "Die Gl\u00fchbirne kann nicht \u00fcber die Integration der WiZ-Plattform verbunden werden.", + "no_wiz_light": "Die Gl\u00fchbirne kann nicht \u00fcber die Integration der WiZ Plattform verbunden werden.", "unknown": "Unerwarteter Fehler" }, "flow_title": "{name} ({host})", diff --git a/homeassistant/components/wolflink/translations/sensor.de.json b/homeassistant/components/wolflink/translations/sensor.de.json index 497f559a9de..7c9cdc2c6f3 100644 --- a/homeassistant/components/wolflink/translations/sensor.de.json +++ b/homeassistant/components/wolflink/translations/sensor.de.json @@ -4,7 +4,7 @@ "1_x_warmwasser": "1 x Warmwasser", "abgasklappe": "Abgasklappe", "absenkbetrieb": "Absenkbetrieb", - "absenkstop": "Absenkstop", + "absenkstop": "Absenkstopp", "aktiviert": "Aktiviert", "antilegionellenfunktion": "Anti-Legionellen-Funktion", "at_abschaltung": "AT Abschaltung", diff --git a/homeassistant/components/xiaomi_ble/translations/de.json b/homeassistant/components/xiaomi_ble/translations/de.json index 07cd3312a1c..911e4c5d9ae 100644 --- a/homeassistant/components/xiaomi_ble/translations/de.json +++ b/homeassistant/components/xiaomi_ble/translations/de.json @@ -17,7 +17,7 @@ "description": "M\u00f6chtest du {name} einrichten?" }, "confirm_slow": { - "description": "Von diesem Ger\u00e4t wurde in der letzten Minute kein Broadcast gesendet, so dass wir nicht sicher sind, ob dieses Ger\u00e4t Verschl\u00fcsselung verwendet oder nicht. Dies kann daran liegen, dass das Ger\u00e4t ein langsames Sendeintervall verwendet. Best\u00e4tige, dass du das Ger\u00e4t trotzdem hinzuf\u00fcgen m\u00f6chtest. Wenn das n\u00e4chste Mal ein Broadcast empfangen wird, wirst du aufgefordert, den Bindkey einzugeben, falls er ben\u00f6tigt wird." + "description": "Von diesem Ger\u00e4t wurde in der letzten Minute kein Broadcast gesendet, sodass wir nicht sicher sind, ob dieses Ger\u00e4t Verschl\u00fcsselung verwendet oder nicht. Dies kann daran liegen, dass das Ger\u00e4t ein langsames Sendeintervall verwendet. Best\u00e4tige, dass du das Ger\u00e4t trotzdem hinzuf\u00fcgen m\u00f6chtest. Wenn das n\u00e4chste Mal ein Broadcast empfangen wird, wirst du aufgefordert, den Bindkey einzugeben, falls er ben\u00f6tigt wird." }, "get_encryption_key_4_5": { "data": { diff --git a/homeassistant/components/yale_smart_alarm/translations/de.json b/homeassistant/components/yale_smart_alarm/translations/de.json index 8f24160663f..f05e54f6ffe 100644 --- a/homeassistant/components/yale_smart_alarm/translations/de.json +++ b/homeassistant/components/yale_smart_alarm/translations/de.json @@ -11,7 +11,7 @@ "step": { "reauth_confirm": { "data": { - "area_id": "Area ID", + "area_id": "Bereich-ID", "name": "Name", "password": "Passwort", "username": "Benutzername" diff --git a/homeassistant/components/yalexs_ble/translations/de.json b/homeassistant/components/yalexs_ble/translations/de.json index 416761bd91a..3fffdc25cf3 100644 --- a/homeassistant/components/yalexs_ble/translations/de.json +++ b/homeassistant/components/yalexs_ble/translations/de.json @@ -21,7 +21,7 @@ "user": { "data": { "address": "Bluetooth-Adresse", - "key": "Offline-Schl\u00fcssel (32-Byte-Hex-String)", + "key": "Offline-Schl\u00fcssel (32-Byte-Hex-Zeichenfolge)", "slot": "Offline-Schl\u00fcssel-Slot (Ganzzahl zwischen 0 und 255)" }, "description": "Lies in der Dokumentation nach, wie du den Offline-Schl\u00fcssel finden kannst." diff --git a/homeassistant/components/yolink/translations/de.json b/homeassistant/components/yolink/translations/de.json index d9d5c9a6efa..2ab37ac0367 100644 --- a/homeassistant/components/yolink/translations/de.json +++ b/homeassistant/components/yolink/translations/de.json @@ -17,7 +17,7 @@ "title": "W\u00e4hle die Authentifizierungsmethode" }, "reauth_confirm": { - "description": "Die yolink-Integration muss dein Konto neu authentifizieren", + "description": "Die yolink Integration muss dein Konto neu authentifizieren", "title": "Integration erneut authentifizieren" } } diff --git a/homeassistant/components/zamg/translations/de.json b/homeassistant/components/zamg/translations/de.json index abccc76860c..9aeb4483a81 100644 --- a/homeassistant/components/zamg/translations/de.json +++ b/homeassistant/components/zamg/translations/de.json @@ -11,7 +11,7 @@ "step": { "user": { "data": { - "station_id": "Stations-ID (standardm\u00e4\u00dfig n\u00e4chste Station)" + "station_id": "Station-ID (standardm\u00e4\u00dfig n\u00e4chste Station)" }, "description": "Richte ZAMG f\u00fcr die Integration mit Home Assistant ein." } diff --git a/homeassistant/components/zha/translations/de.json b/homeassistant/components/zha/translations/de.json index 78a3b36626b..40112b0c36c 100644 --- a/homeassistant/components/zha/translations/de.json +++ b/homeassistant/components/zha/translations/de.json @@ -68,7 +68,7 @@ "data": { "uploaded_backup_file": "Datei hochladen" }, - "description": "Stelle deine Netzwerkeinstellungen aus einer hochgeladenen Backup-JSON-Datei wieder her. Du kannst eine von einer anderen ZHA-Installation unter **Netzwerkeinstellungen** herunterladen oder eine Zigbee2MQTT-Datei \"coordinator_backup.json\" verwenden.", + "description": "Stelle deine Netzwerkeinstellungen aus einer hochgeladenen Backup-JSON-Datei wieder her. Du kannst eine von einer anderen ZHA Installation unter **Netzwerkeinstellungen** herunterladen oder eine Zigbee2MQTT-Datei \"coordinator_backup.json\" verwenden.", "title": "Manuelles Backup hochladen" } } @@ -81,7 +81,7 @@ "title": "Optionen f\u00fcr die Alarmsteuerung" }, "zha_options": { - "always_prefer_xy_color_mode": "Immer den XY-Farbmodus bevorzugen", + "always_prefer_xy_color_mode": "Immer den XY Farbmodus bevorzugen", "consider_unavailable_battery": "Batteriebetriebene Ger\u00e4te als nicht verf\u00fcgbar betrachten nach (Sekunden)", "consider_unavailable_mains": "Netzbetriebene Ger\u00e4te als nicht verf\u00fcgbar betrachten nach (Sekunden)", "default_light_transition": "Standardlicht\u00fcbergangszeit (Sekunden)", @@ -232,7 +232,7 @@ "data": { "uploaded_backup_file": "Datei hochladen" }, - "description": "Stelle deine Netzwerkeinstellungen aus einer hochgeladenen Backup-JSON-Datei wieder her. Du kannst eine von einer anderen ZHA-Installation unter **Netzwerkeinstellungen** herunterladen oder eine Zigbee2MQTT-Datei \"coordinator_backup.json\" verwenden.", + "description": "Stelle deine Netzwerkeinstellungen aus einer hochgeladenen Backup-JSON-Datei wieder her. Du kannst eine von einer anderen ZHA Installation unter **Netzwerkeinstellungen** herunterladen oder eine Zigbee2MQTT-Datei \"coordinator_backup.json\" verwenden.", "title": "Manuelles Backup hochladen" } } diff --git a/homeassistant/components/zwave_js/translations/de.json b/homeassistant/components/zwave_js/translations/de.json index fb3c7e1a69d..3e1616c77c7 100644 --- a/homeassistant/components/zwave_js/translations/de.json +++ b/homeassistant/components/zwave_js/translations/de.json @@ -61,7 +61,7 @@ "description": "M\u00f6chtest du {name} mit dem Z-Wave JS Add-on einrichten?" }, "zeroconf_confirm": { - "description": "M\u00f6chtest du den Z-Wave JS-Server mit der Home-ID {home_id} , gefunden unter {url} , zu Home Assistant hinzuf\u00fcgen?", + "description": "M\u00f6chtest du den Z-Wave JS-Server mit der Home-ID {home_id}, gefunden unter {url}, zu Home Assistant hinzuf\u00fcgen?", "title": "Z-Wave JS-Server entdeckt" } } @@ -85,8 +85,8 @@ "event.notification.entry_control": "Benachrichtigung zur Zugangskontrolle gesendet", "event.notification.notification": "Benachrichtigung gesendet", "event.value_notification.basic": "Grundlegendes CC-Ereignis auf {subtype}", - "event.value_notification.central_scene": "Zentrale Szenenaktion auf {subtype}", - "event.value_notification.scene_activation": "Szenenaktivierung auf {subtype}", + "event.value_notification.central_scene": "Zentrale Szeneaktion auf {subtype}", + "event.value_notification.scene_activation": "Szeneaktivierung auf {subtype}", "state.node_status": "Knotenstatus ge\u00e4ndert", "zwave_js.value_updated.config_parameter": "Wert\u00e4nderung des Konfigurationsparameters {subtype}", "zwave_js.value_updated.value": "Wert\u00e4nderung bei einem Z-Wave JS Wert" diff --git a/homeassistant/components/zwave_me/translations/de.json b/homeassistant/components/zwave_me/translations/de.json index 6e20c28ce07..af0249fe45b 100644 --- a/homeassistant/components/zwave_me/translations/de.json +++ b/homeassistant/components/zwave_me/translations/de.json @@ -13,7 +13,7 @@ "token": "API-Token", "url": "URL" }, - "description": "Gib die IP-Adresse mit Port und Zugangs-Token des Z-Way-Servers ein. Um das Token zu erhalten, gehe zur Z-Way-Benutzeroberfl\u00e4che Smart Home UI \u2192 Men\u00fc \u2192 Einstellungen \u2192 Benutzer \u2192 Administrator \u2192 API-Token.\n\nBeispiel f\u00fcr die Verbindung zu Z-Way im lokalen Netzwerk:\nURL: {local_url}\nToken: {local_token}\n\nBeispiel f\u00fcr die Verbindung zu Z-Way \u00fcber den Fernzugriff find.z-wave.me:\nURL: {find_url}\nToken: {find_token}\n\nBeispiel f\u00fcr eine Verbindung zu Z-Way mit einer statischen \u00f6ffentlichen IP-Adresse:\nURL: {remote_url}\nToken: {local_token}\n\nWenn du dich \u00fcber find.z-wave.me verbindest, musst du ein Token mit globalem Geltungsbereich verwenden (logge dich dazu \u00fcber find.z-wave.me bei Z-Way ein)." + "description": "Gib die IP-Adresse mit Port und Zugangs-Token des Z-Way Servers ein. Um das Token zu erhalten, gehe zur Z-Way Benutzeroberfl\u00e4che Smart Home UI \u2192 Men\u00fc \u2192 Einstellungen \u2192 Benutzer \u2192 Administrator \u2192 API-Token.\n\nBeispiel f\u00fcr die Verbindung zu Z-Way im lokalen Netzwerk:\nURL: {local_url}\nToken: {local_token}\n\nBeispiel f\u00fcr die Verbindung zu Z-Way \u00fcber den Fernzugriff find.z-wave.me:\nURL: {find_url}\nToken: {find_token}\n\nBeispiel f\u00fcr eine Verbindung zu Z-Way mit einer statischen \u00f6ffentlichen IP-Adresse:\nURL: {remote_url}\nToken: {local_token}\n\nWenn du dich \u00fcber find.z-wave.me verbindest, musst du ein Token mit globalem Geltungsbereich verwenden (logge dich dazu \u00fcber find.z-wave.me bei Z-Way ein)." } } } From 43c8adc5ec93f9b90e8fc5cbb8c0ebfcb2d453d9 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Tue, 29 Nov 2022 18:35:35 -0700 Subject: [PATCH 0889/1033] Bump `simplisafe-python` to 2022.11.2 (#82943) --- homeassistant/components/simplisafe/__init__.py | 4 ++-- .../components/simplisafe/alarm_control_panel.py | 12 +++++++++--- homeassistant/components/simplisafe/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index b04dcfbd984..55f7adc860b 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -298,7 +298,7 @@ def _async_register_base_station( device_registry = dr.async_get(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, - identifiers={(DOMAIN, system.system_id)}, + identifiers={(DOMAIN, str(system.system_id))}, manufacturer="SimpliSafe", model=system.version, name=system.address, @@ -731,7 +731,7 @@ class SimpliSafeEntity(CoordinatorEntity): manufacturer="SimpliSafe", model=model, name=device_name, - via_device=(DOMAIN, system.system_id), + via_device=(DOMAIN, str(system.system_id)), ) self._attr_unique_id = serial diff --git a/homeassistant/components/simplisafe/alarm_control_panel.py b/homeassistant/components/simplisafe/alarm_control_panel.py index ef3e09bc70f..1137420bd82 100644 --- a/homeassistant/components/simplisafe/alarm_control_panel.py +++ b/homeassistant/components/simplisafe/alarm_control_panel.py @@ -224,9 +224,7 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity): self._attr_extra_state_attributes.update( { ATTR_ALARM_DURATION: self._system.alarm_duration, - ATTR_ALARM_VOLUME: self._system.alarm_volume.name.lower(), ATTR_BATTERY_BACKUP_POWER_LEVEL: self._system.battery_backup_power_level, - ATTR_CHIME_VOLUME: self._system.chime_volume.name.lower(), ATTR_ENTRY_DELAY_AWAY: self._system.entry_delay_away, ATTR_ENTRY_DELAY_HOME: self._system.entry_delay_home, ATTR_EXIT_DELAY_AWAY: self._system.exit_delay_away, @@ -234,12 +232,20 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity): ATTR_GSM_STRENGTH: self._system.gsm_strength, ATTR_LIGHT: self._system.light, ATTR_RF_JAMMING: self._system.rf_jamming, - ATTR_VOICE_PROMPT_VOLUME: self._system.voice_prompt_volume.name.lower(), ATTR_WALL_POWER_LEVEL: self._system.wall_power_level, ATTR_WIFI_STRENGTH: self._system.wifi_strength, } ) + for key, volume_prop in ( + (ATTR_ALARM_VOLUME, self._system.alarm_volume), + (ATTR_CHIME_VOLUME, self._system.chime_volume), + (ATTR_VOICE_PROMPT_VOLUME, self._system.voice_prompt_volume), + ): + if not volume_prop: + continue + self._attr_extra_state_attributes[key] = volume_prop.name.lower() + self._set_state_from_system_data() @callback diff --git a/homeassistant/components/simplisafe/manifest.json b/homeassistant/components/simplisafe/manifest.json index dcb06aa2825..99c332955c3 100644 --- a/homeassistant/components/simplisafe/manifest.json +++ b/homeassistant/components/simplisafe/manifest.json @@ -3,7 +3,7 @@ "name": "SimpliSafe", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/simplisafe", - "requirements": ["simplisafe-python==2022.07.1"], + "requirements": ["simplisafe-python==2022.11.2"], "codeowners": ["@bachya"], "iot_class": "cloud_polling", "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index 21d02743ca2..a74b4713fcd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2282,7 +2282,7 @@ simplehound==0.3 simplepush==2.1.1 # homeassistant.components.simplisafe -simplisafe-python==2022.07.1 +simplisafe-python==2022.11.2 # homeassistant.components.sisyphus sisyphus-control==3.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9e1f0cb9f92..98e255f30f8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1579,7 +1579,7 @@ simplehound==0.3 simplepush==2.1.1 # homeassistant.components.simplisafe -simplisafe-python==2022.07.1 +simplisafe-python==2022.11.2 # homeassistant.components.slack slackclient==2.5.0 From a3ec9529ecfa1b0031e676cefb13de30f3e2f889 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 29 Nov 2022 15:46:01 -1000 Subject: [PATCH 0890/1033] Fix lutron caseta triggers when device fails to setup before startup finishes (#82714) Refactor the trigger attach so its not dependant on the intergration being already setup fixes #82495 & fixes #81999 --- .../components/lutron_caseta/__init__.py | 104 ++++++++++++------ .../components/lutron_caseta/const.py | 1 + .../lutron_caseta/device_trigger.py | 70 +++--------- .../components/lutron_caseta/models.py | 23 +++- .../lutron_caseta/test_device_trigger.py | 59 ++++++++++ 5 files changed, 170 insertions(+), 87 deletions(-) diff --git a/homeassistant/components/lutron_caseta/__init__.py b/homeassistant/components/lutron_caseta/__init__.py index 385fdf94a62..5ee64a687bf 100644 --- a/homeassistant/components/lutron_caseta/__init__.py +++ b/homeassistant/components/lutron_caseta/__init__.py @@ -6,7 +6,7 @@ import contextlib from itertools import chain import logging import ssl -from typing import Any +from typing import Any, cast import async_timeout from pylutron_caseta import BUTTON_STATUS_PRESSED @@ -28,6 +28,7 @@ from .const import ( ATTR_ACTION, ATTR_AREA_NAME, ATTR_BUTTON_NUMBER, + ATTR_BUTTON_TYPE, ATTR_DEVICE_NAME, ATTR_LEAP_BUTTON_NUMBER, ATTR_SERIAL, @@ -50,7 +51,21 @@ from .device_trigger import ( LEAP_TO_DEVICE_TYPE_SUBTYPE_MAP, LUTRON_BUTTON_TRIGGER_SCHEMA, ) -from .models import LutronButton, LutronCasetaData, LutronKeypad, LutronKeypadData +from .models import ( + LUTRON_BUTTON_LEAP_BUTTON_NUMBER, + LUTRON_KEYPAD_AREA_NAME, + LUTRON_KEYPAD_BUTTONS, + LUTRON_KEYPAD_DEVICE_REGISTRY_DEVICE_ID, + LUTRON_KEYPAD_LUTRON_DEVICE_ID, + LUTRON_KEYPAD_MODEL, + LUTRON_KEYPAD_NAME, + LUTRON_KEYPAD_SERIAL, + LUTRON_KEYPAD_TYPE, + LutronButton, + LutronCasetaData, + LutronKeypad, + LutronKeypadData, +) from .util import serial_to_unique_id _LOGGER = logging.getLogger(__name__) @@ -225,57 +240,77 @@ def _async_setup_keypads( hass: HomeAssistant, config_entry_id: str, bridge: Smartbridge, - bridge_device: dict[str, Any], + bridge_device: dict[str, str | int], ) -> LutronKeypadData: """Register keypad devices (Keypads and Pico Remotes) in the device registry.""" device_registry = dr.async_get(hass) - bridge_devices = bridge.get_devices() - bridge_buttons = bridge.buttons + bridge_devices: dict[str, dict[str, str | int]] = bridge.get_devices() + bridge_buttons: dict[str, dict[str, str | int]] = bridge.buttons dr_device_id_to_keypad: dict[str, LutronKeypad] = {} keypads: dict[int, LutronKeypad] = {} keypad_buttons: dict[int, LutronButton] = {} keypad_button_names_to_leap: dict[int, dict[str, int]] = {} + leap_to_keypad_button_names: dict[int, dict[int, str]] = {} for bridge_button in bridge_buttons.values(): - bridge_keypad = bridge_devices[bridge_button["parent_device"]] - keypad_device_id = bridge_keypad["device_id"] - button_device_id = bridge_button["device_id"] + parent_device = cast(str, bridge_button["parent_device"]) + bridge_keypad = bridge_devices[parent_device] + keypad_lutron_device_id = cast(int, bridge_keypad["device_id"]) + button_lutron_device_id = cast(int, bridge_button["device_id"]) + leap_button_number = cast(int, bridge_button["button_number"]) + button_led_device_id = None + if "button_led" in bridge_button: + button_led_device_id = cast(str, bridge_button["button_led"]) - if not (keypad := keypads.get(keypad_device_id)): + if not (keypad := keypads.get(keypad_lutron_device_id)): # First time seeing this keypad, build keypad data and store in keypads - keypad = keypads[keypad_device_id] = _async_build_lutron_keypad( - bridge, bridge_device, bridge_keypad, keypad_device_id + keypad = keypads[keypad_lutron_device_id] = _async_build_lutron_keypad( + bridge, bridge_device, bridge_keypad, keypad_lutron_device_id ) # Register the keypad device dr_device = device_registry.async_get_or_create( **keypad["device_info"], config_entry_id=config_entry_id ) - keypad["dr_device_id"] = dr_device.id + keypad[LUTRON_KEYPAD_DEVICE_REGISTRY_DEVICE_ID] = dr_device.id dr_device_id_to_keypad[dr_device.id] = keypad + button_name = _get_button_name(keypad, bridge_button) + keypad_lutron_device_id = keypad[LUTRON_KEYPAD_LUTRON_DEVICE_ID] + # Add button to parent keypad, and build keypad_buttons and keypad_button_names_to_leap - button = keypad_buttons[button_device_id] = LutronButton( - lutron_device_id=button_device_id, - leap_button_number=bridge_button["button_number"], - button_name=_get_button_name(keypad, bridge_button), - led_device_id=bridge_button.get("button_led"), - parent_keypad=keypad["lutron_device_id"], + keypad_buttons[button_lutron_device_id] = LutronButton( + lutron_device_id=button_lutron_device_id, + leap_button_number=leap_button_number, + button_name=button_name, + led_device_id=button_led_device_id, + parent_keypad=keypad_lutron_device_id, ) - keypad["buttons"].append(button["lutron_device_id"]) + keypad[LUTRON_KEYPAD_BUTTONS].append(button_lutron_device_id) - keypad_button_names_to_leap.setdefault(keypad["lutron_device_id"], {}).update( - {button["button_name"]: int(button["leap_button_number"])} + button_name_to_leap = keypad_button_names_to_leap.setdefault( + keypad_lutron_device_id, {} ) + button_name_to_leap[button_name] = leap_button_number + leap_to_button_name = leap_to_keypad_button_names.setdefault( + keypad_lutron_device_id, {} + ) + leap_to_button_name[leap_button_number] = button_name keypad_trigger_schemas = _async_build_trigger_schemas(keypad_button_names_to_leap) - _async_subscribe_keypad_events(hass, bridge, keypads, keypad_buttons) + _async_subscribe_keypad_events( + hass=hass, + bridge=bridge, + keypads=keypads, + keypad_buttons=keypad_buttons, + leap_to_keypad_button_names=leap_to_keypad_button_names, + ) return LutronKeypadData( dr_device_id_to_keypad, @@ -312,7 +347,6 @@ def _async_build_lutron_keypad( keypad_device_id: int, ) -> LutronKeypad: # First time seeing this keypad, build keypad data and store in keypads - area_name = _area_name_from_id(bridge.areas, bridge_keypad["area"]) keypad_name = bridge_keypad["name"].split("_")[-1] keypad_serial = _handle_none_keypad_serial(bridge_keypad, bridge_device["serial"]) @@ -350,7 +384,7 @@ def _get_button_name(keypad: LutronKeypad, bridge_button: dict[str, Any]) -> str # This is a Caseta Button retrieve name from hardcoded trigger definitions. return _get_button_name_from_triggers(keypad, button_number) - keypad_model = keypad["model"] + keypad_model = keypad[LUTRON_KEYPAD_MODEL] if keypad_model_override := KEYPAD_LEAP_BUTTON_NAME_OVERRIDE.get(keypad_model): if alt_button_name := keypad_model_override.get(button_number): return alt_button_name @@ -412,8 +446,9 @@ def async_get_lip_button(device_type: str, leap_button: int) -> int | None: def _async_subscribe_keypad_events( hass: HomeAssistant, bridge: Smartbridge, - keypads: dict[int, Any], - keypad_buttons: dict[int, Any], + keypads: dict[int, LutronKeypad], + keypad_buttons: dict[int, LutronButton], + leap_to_keypad_button_names: dict[int, dict[int, str]], ): """Subscribe to lutron events.""" @@ -429,20 +464,25 @@ def _async_subscribe_keypad_events( else: action = ACTION_RELEASE - keypad_type = keypad["type"] - leap_button_number = button["leap_button_number"] + keypad_type = keypad[LUTRON_KEYPAD_TYPE] + keypad_device_id = keypad[LUTRON_KEYPAD_LUTRON_DEVICE_ID] + leap_button_number = button[LUTRON_BUTTON_LEAP_BUTTON_NUMBER] lip_button_number = async_get_lip_button(keypad_type, leap_button_number) + button_type = LEAP_TO_DEVICE_TYPE_SUBTYPE_MAP.get( + keypad_type, leap_to_keypad_button_names[keypad_device_id] + )[leap_button_number] hass.bus.async_fire( LUTRON_CASETA_BUTTON_EVENT, { - ATTR_SERIAL: keypad["serial"], + ATTR_SERIAL: keypad[LUTRON_KEYPAD_SERIAL], ATTR_TYPE: keypad_type, ATTR_BUTTON_NUMBER: lip_button_number, ATTR_LEAP_BUTTON_NUMBER: leap_button_number, - ATTR_DEVICE_NAME: keypad["name"], - ATTR_DEVICE_ID: keypad["dr_device_id"], - ATTR_AREA_NAME: keypad["area_name"], + ATTR_DEVICE_NAME: keypad[LUTRON_KEYPAD_NAME], + ATTR_DEVICE_ID: keypad[LUTRON_KEYPAD_DEVICE_REGISTRY_DEVICE_ID], + ATTR_AREA_NAME: keypad[LUTRON_KEYPAD_AREA_NAME], + ATTR_BUTTON_TYPE: button_type, ATTR_ACTION: action, }, ) diff --git a/homeassistant/components/lutron_caseta/const.py b/homeassistant/components/lutron_caseta/const.py index ae8dc0a505a..af06bf0e0f0 100644 --- a/homeassistant/components/lutron_caseta/const.py +++ b/homeassistant/components/lutron_caseta/const.py @@ -18,6 +18,7 @@ MANUFACTURER = "Lutron Electronics Co., Inc" ATTR_SERIAL = "serial" ATTR_TYPE = "type" +ATTR_BUTTON_TYPE = "button_type" ATTR_LEAP_BUTTON_NUMBER = "leap_button_number" ATTR_BUTTON_NUMBER = "button_number" # LIP button number ATTR_DEVICE_NAME = "device_name" diff --git a/homeassistant/components/lutron_caseta/device_trigger.py b/homeassistant/components/lutron_caseta/device_trigger.py index 2dfbe526c93..7e178698afe 100644 --- a/homeassistant/components/lutron_caseta/device_trigger.py +++ b/homeassistant/components/lutron_caseta/device_trigger.py @@ -6,9 +6,6 @@ import logging import voluptuous as vol from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA -from homeassistant.components.device_automation.exceptions import ( - InvalidDeviceAutomationConfig, -) from homeassistant.components.homeassistant.triggers import event as event_trigger from homeassistant.const import ( CONF_DEVICE_ID, @@ -18,7 +15,6 @@ from homeassistant.const import ( CONF_TYPE, ) from homeassistant.core import CALLBACK_TYPE, HomeAssistant -from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo from homeassistant.helpers.typing import ConfigType @@ -26,8 +22,7 @@ from .const import ( ACTION_PRESS, ACTION_RELEASE, ATTR_ACTION, - ATTR_LEAP_BUTTON_NUMBER, - ATTR_SERIAL, + ATTR_BUTTON_TYPE, CONF_SUBTYPE, DOMAIN, LUTRON_CASETA_BUTTON_EVENT, @@ -317,7 +312,7 @@ DEVICE_TYPE_SUBTYPE_MAP_TO_LEAP = { "FourGroupRemote": FOUR_GROUP_REMOTE_BUTTON_TYPES_TO_LEAP, } -LEAP_TO_DEVICE_TYPE_SUBTYPE_MAP = { +LEAP_TO_DEVICE_TYPE_SUBTYPE_MAP: dict[str, dict[int, str]] = { k: _reverse_dict(v) for k, v in DEVICE_TYPE_SUBTYPE_MAP_TO_LEAP.items() } @@ -421,53 +416,22 @@ async def async_attach_trigger( trigger_info: TriggerInfo, ) -> CALLBACK_TYPE: """Attach a trigger.""" - device_id = config[CONF_DEVICE_ID] - subtype = config[CONF_SUBTYPE] - if not (data := get_lutron_data_by_dr_id(hass, device_id)) or not ( - keypad := data.keypad_data.dr_device_id_to_keypad[device_id] - ): - raise HomeAssistantError( - f"Cannot attach trigger {config} because device with id {device_id} is missing or invalid" - ) - - keypad_trigger_schemas = data.keypad_data.trigger_schemas - keypad_button_names_to_leap = data.keypad_data.button_names_to_leap - - device_type = keypad["type"] - serial = keypad["serial"] - lutron_device_id = keypad["lutron_device_id"] - - # Retrieve trigger schema, preferring hard-coded triggers from device_trigger.py - schema = DEVICE_TYPE_SCHEMA_MAP.get( - device_type, - keypad_trigger_schemas[lutron_device_id], - ) - - # Retrieve list of valid buttons, preferring hard-coded triggers from device_trigger.py - valid_buttons = DEVICE_TYPE_SUBTYPE_MAP_TO_LEAP.get( - device_type, - keypad_button_names_to_leap[lutron_device_id], - ) - - if subtype not in valid_buttons: - raise InvalidDeviceAutomationConfig( - f"Cannot attach trigger {config} because subtype {subtype} is invalid" - ) - - config = schema(config) - event_config = { - event_trigger.CONF_PLATFORM: CONF_EVENT, - event_trigger.CONF_EVENT_TYPE: LUTRON_CASETA_BUTTON_EVENT, - event_trigger.CONF_EVENT_DATA: { - ATTR_SERIAL: serial, - ATTR_LEAP_BUTTON_NUMBER: valid_buttons[subtype], - ATTR_ACTION: config[CONF_TYPE], - }, - } - event_config = event_trigger.TRIGGER_SCHEMA(event_config) - return await event_trigger.async_attach_trigger( - hass, event_config, action, trigger_info, platform_type="device" + hass, + event_trigger.TRIGGER_SCHEMA( + { + event_trigger.CONF_PLATFORM: CONF_EVENT, + event_trigger.CONF_EVENT_TYPE: LUTRON_CASETA_BUTTON_EVENT, + event_trigger.CONF_EVENT_DATA: { + CONF_DEVICE_ID: config[CONF_DEVICE_ID], + ATTR_ACTION: config[CONF_TYPE], + ATTR_BUTTON_TYPE: config[CONF_SUBTYPE], + }, + } + ), + action, + trigger_info, + platform_type="device", ) diff --git a/homeassistant/components/lutron_caseta/models.py b/homeassistant/components/lutron_caseta/models.py index 576387bd36b..61f00a1b09f 100644 --- a/homeassistant/components/lutron_caseta/models.py +++ b/homeassistant/components/lutron_caseta/models.py @@ -2,7 +2,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any, TypedDict +from typing import Any, Final, TypedDict from pylutron_caseta.smartbridge import Smartbridge import voluptuous as vol @@ -45,11 +45,30 @@ class LutronKeypad(TypedDict): buttons: list[int] +LUTRON_KEYPAD_LUTRON_DEVICE_ID: Final = "lutron_device_id" +LUTRON_KEYPAD_DEVICE_REGISTRY_DEVICE_ID: Final = "dr_device_id" +LUTRON_KEYPAD_AREA_ID: Final = "area_id" +LUTRON_KEYPAD_AREA_NAME: Final = "area_name" +LUTRON_KEYPAD_NAME: Final = "name" +LUTRON_KEYPAD_SERIAL: Final = "serial" +LUTRON_KEYPAD_DEVICE_INFO: Final = "device_info" +LUTRON_KEYPAD_MODEL: Final = "model" +LUTRON_KEYPAD_TYPE: Final = "type" +LUTRON_KEYPAD_BUTTONS: Final = "buttons" + + class LutronButton(TypedDict): """A lutron_caseta button.""" lutron_device_id: int leap_button_number: int button_name: str - led_device_id: int + led_device_id: str | None parent_keypad: int + + +LUTRON_BUTTON_LUTRON_DEVICE_ID: Final = "lutron_device_id" +LUTRON_BUTTON_LEAP_BUTTON_NUMBER: Final = "leap_button_number" +LUTRON_BUTTON_BUTTON_NAME: Final = "button_name" +LUTRON_BUTTON_LED_DEVICE_ID: Final = "led_device_id" +LUTRON_BUTTON_PARENT_KEYPAD: Final = "parent_keypad" diff --git a/tests/components/lutron_caseta/test_device_trigger.py b/tests/components/lutron_caseta/test_device_trigger.py index a1558822619..e7688f62f06 100644 --- a/tests/components/lutron_caseta/test_device_trigger.py +++ b/tests/components/lutron_caseta/test_device_trigger.py @@ -13,6 +13,7 @@ from homeassistant.components.lutron_caseta import ( ATTR_TYPE, ) from homeassistant.components.lutron_caseta.const import ( + ATTR_BUTTON_TYPE, ATTR_LEAP_BUTTON_NUMBER, CONF_CA_CERTS, CONF_CERTFILE, @@ -23,6 +24,7 @@ from homeassistant.components.lutron_caseta.const import ( from homeassistant.components.lutron_caseta.device_trigger import CONF_SUBTYPE from homeassistant.components.lutron_caseta.models import LutronCasetaData from homeassistant.const import ( + ATTR_DEVICE_ID, CONF_DEVICE_ID, CONF_DOMAIN, CONF_HOST, @@ -254,6 +256,8 @@ async def test_if_fires_on_button_event(hass, calls, device_reg): ATTR_DEVICE_NAME: device["Name"], ATTR_AREA_NAME: device.get("Area", {}).get("Name"), ATTR_ACTION: "press", + ATTR_DEVICE_ID: device_id, + ATTR_BUTTON_TYPE: "on", } hass.bus.async_fire(LUTRON_CASETA_BUTTON_EVENT, message) await hass.async_block_till_done() @@ -298,6 +302,8 @@ async def test_if_fires_on_button_event_without_lip(hass, calls, device_reg): ATTR_DEVICE_NAME: device["Name"], ATTR_AREA_NAME: device.get("Area", {}).get("Name"), ATTR_ACTION: "press", + ATTR_DEVICE_ID: device_id, + ATTR_BUTTON_TYPE: "Kitchen Pendants", } hass.bus.async_fire(LUTRON_CASETA_BUTTON_EVENT, message) await hass.async_block_till_done() @@ -420,3 +426,56 @@ async def test_validate_trigger_invalid_triggers(hass, device_reg): ] }, ) + + +async def test_if_fires_on_button_event_late_setup(hass, calls): + """Test for press trigger firing with integration getting setup late.""" + config_entry_id = await _async_setup_lutron_with_picos(hass) + await hass.config_entries.async_unload(config_entry_id) + await hass.async_block_till_done() + + device = MOCK_BUTTON_DEVICES[0] + dr = device_registry.async_get(hass) + dr_device = dr.async_get_device(identifiers={(DOMAIN, device["serial"])}) + device_id = dr_device.id + + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + { + "trigger": { + CONF_PLATFORM: "device", + CONF_DOMAIN: DOMAIN, + CONF_DEVICE_ID: device_id, + CONF_TYPE: "press", + CONF_SUBTYPE: "on", + }, + "action": { + "service": "test.automation", + "data_template": {"some": "test_trigger_button_press"}, + }, + }, + ] + }, + ) + + await hass.config_entries.async_setup(config_entry_id) + await hass.async_block_till_done() + + message = { + ATTR_SERIAL: device.get("serial"), + ATTR_TYPE: device.get("type"), + ATTR_LEAP_BUTTON_NUMBER: 0, + ATTR_DEVICE_NAME: device["Name"], + ATTR_AREA_NAME: device.get("Area", {}).get("Name"), + ATTR_ACTION: "press", + ATTR_DEVICE_ID: device_id, + ATTR_BUTTON_TYPE: "on", + } + hass.bus.async_fire(LUTRON_CASETA_BUTTON_EVENT, message) + await hass.async_block_till_done() + + assert len(calls) == 1 + assert calls[0].data["some"] == "test_trigger_button_press" From 1908feab7964a3810b6210b58bb869eaadb6b68d Mon Sep 17 00:00:00 2001 From: Marvin Wichmann Date: Wed, 30 Nov 2022 02:46:34 +0100 Subject: [PATCH 0891/1033] Increase file upload limit to 100 MB (#77117) * Increase file upload limit to 100 MB * Remove comment * Add test and fix chunk processing * Add test for wrong field * Add review suggestions * Use nonlocal and remove unneeded executor task * Use Janus to process chunk uploading * Address review comments * Address review comments #2 * Improve tests * Fix discovery test * Fix tests --- .../components/file_upload/__init__.py | 54 +++++++++++++------ .../components/file_upload/manifest.json | 1 + homeassistant/package_constraints.txt | 1 + requirements_all.txt | 3 ++ requirements_test_all.txt | 3 ++ tests/components/file_upload/conftest.py | 13 +++++ tests/components/file_upload/test_init.py | 46 ++++++++++++++++ tests/test_requirements.py | 2 +- 8 files changed, 107 insertions(+), 16 deletions(-) create mode 100644 tests/components/file_upload/conftest.py diff --git a/homeassistant/components/file_upload/__init__.py b/homeassistant/components/file_upload/__init__.py index 9f548e14459..73f8465b1df 100644 --- a/homeassistant/components/file_upload/__init__.py +++ b/homeassistant/components/file_upload/__init__.py @@ -9,7 +9,8 @@ from pathlib import Path import shutil import tempfile -from aiohttp import web +from aiohttp import BodyPartReader, web +import janus import voluptuous as vol from homeassistant.components.http import HomeAssistantView @@ -22,9 +23,8 @@ from homeassistant.util.ulid import ulid_hex DOMAIN = "file_upload" -# If increased, change upload view to streaming -# https://docs.aiohttp.org/en/stable/web_quickstart.html#file-uploads -MAX_SIZE = 1024 * 1024 * 10 +ONE_MEGABYTE = 1024 * 1024 +MAX_SIZE = 100 * ONE_MEGABYTE TEMP_DIR_NAME = f"home-assistant-{DOMAIN}" @@ -126,14 +126,18 @@ class FileUploadView(HomeAssistantView): # Increase max payload request._client_max_size = MAX_SIZE # pylint: disable=protected-access - data = await request.post() - file_field = data.get("file") + reader = await request.multipart() + file_field_reader = await reader.next() - if not isinstance(file_field, web.FileField): + if ( + not isinstance(file_field_reader, BodyPartReader) + or file_field_reader.name != "file" + or file_field_reader.filename is None + ): raise vol.Invalid("Expected a file") try: - raise_if_invalid_filename(file_field.filename) + raise_if_invalid_filename(file_field_reader.filename) except ValueError as err: raise web.HTTPBadRequest from err @@ -145,19 +149,39 @@ class FileUploadView(HomeAssistantView): file_upload_data: FileUploadData = hass.data[DOMAIN] file_dir = file_upload_data.file_dir(file_id) + queue: janus.Queue[bytes | None] = janus.Queue() - def _sync_work() -> None: + def _sync_queue_consumer( + sync_q: janus.SyncQueue[bytes | None], _file_name: str + ) -> None: file_dir.mkdir() + with (file_dir / _file_name).open("wb") as file_handle: + while True: + _chunk = sync_q.get() + if _chunk is None: + break - # MyPy forgets about the isinstance check because we're in a function scope - assert isinstance(file_field, web.FileField) + file_handle.write(_chunk) + sync_q.task_done() - with (file_dir / file_field.filename).open("wb") as target_fileobj: - shutil.copyfileobj(file_field.file, target_fileobj) + fut: asyncio.Future[None] | None = None + try: + fut = hass.async_add_executor_job( + _sync_queue_consumer, + queue.sync_q, + file_field_reader.filename, + ) - await hass.async_add_executor_job(_sync_work) + while chunk := await file_field_reader.read_chunk(ONE_MEGABYTE): + queue.async_q.put_nowait(chunk) + if queue.async_q.qsize() > 5: # Allow up to 5 MB buffer size + await queue.async_q.join() + queue.async_q.put_nowait(None) # terminate queue consumer + finally: + if fut is not None: + await fut - file_upload_data.files[file_id] = file_field.filename + file_upload_data.files[file_id] = file_field_reader.filename return self.json({"file_id": file_id}) diff --git a/homeassistant/components/file_upload/manifest.json b/homeassistant/components/file_upload/manifest.json index d2b4f88a279..62f7a1f2b27 100644 --- a/homeassistant/components/file_upload/manifest.json +++ b/homeassistant/components/file_upload/manifest.json @@ -2,6 +2,7 @@ "domain": "file_upload", "name": "File Upload", "documentation": "https://www.home-assistant.io/integrations/file_upload", + "requirements": ["janus==1.0.0"], "dependencies": ["http"], "codeowners": ["@home-assistant/core"], "quality_scale": "internal", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index dbd749d7ca7..7958b776f10 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -25,6 +25,7 @@ home-assistant-bluetooth==1.8.1 home-assistant-frontend==20221108.0 httpx==0.23.1 ifaddr==0.1.7 +janus==1.0.0 jinja2==3.1.2 lru-dict==1.1.8 orjson==3.8.1 diff --git a/requirements_all.txt b/requirements_all.txt index a74b4713fcd..ac8bbd9b144 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -964,6 +964,9 @@ iperf3==0.1.11 # homeassistant.components.gogogate2 ismartgate==4.0.4 +# homeassistant.components.file_upload +janus==1.0.0 + # homeassistant.components.jellyfin jellyfin-apiclient-python==1.9.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 98e255f30f8..c62ca320ee0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -717,6 +717,9 @@ iotawattpy==0.1.0 # homeassistant.components.gogogate2 ismartgate==4.0.4 +# homeassistant.components.file_upload +janus==1.0.0 + # homeassistant.components.jellyfin jellyfin-apiclient-python==1.9.2 diff --git a/tests/components/file_upload/conftest.py b/tests/components/file_upload/conftest.py new file mode 100644 index 00000000000..ab9965c1914 --- /dev/null +++ b/tests/components/file_upload/conftest.py @@ -0,0 +1,13 @@ +"""Fixtures for FileUpload integration.""" +from io import StringIO + +import pytest + + +@pytest.fixture +def large_file_io() -> StringIO: + """Generate a file on the fly. Simulates a large file.""" + return StringIO( + 2 + * "Home Assistant is awesome. Open source home automation that puts local control and privacy first." + ) diff --git a/tests/components/file_upload/test_init.py b/tests/components/file_upload/test_init.py index ba3485c96e1..699fb6f9b84 100644 --- a/tests/components/file_upload/test_init.py +++ b/tests/components/file_upload/test_init.py @@ -64,3 +64,49 @@ async def test_removed_on_stop(hass: HomeAssistant, hass_client, uploaded_file_d # Test it's removed assert not uploaded_file_dir.exists() + + +async def test_upload_large_file(hass: HomeAssistant, hass_client, large_file_io): + """Test uploading large file.""" + assert await async_setup_component(hass, "file_upload", {}) + client = await hass_client() + + with patch( + # Patch temp dir name to avoid tests fail running in parallel + "homeassistant.components.file_upload.TEMP_DIR_NAME", + file_upload.TEMP_DIR_NAME + f"-{getrandbits(10):03x}", + ), patch( + # Patch one megabyte to 8 bytes to prevent having to use big files in tests + "homeassistant.components.file_upload.ONE_MEGABYTE", + 8, + ): + res = await client.post("/api/file_upload", data={"file": large_file_io}) + + assert res.status == 200 + response = await res.json() + + file_dir = hass.data[file_upload.DOMAIN].file_dir(response["file_id"]) + assert file_dir.is_dir() + + large_file_io.seek(0) + with file_upload.process_uploaded_file(hass, file_dir.name) as file_path: + assert file_path.is_file() + assert file_path.parent == file_dir + assert file_path.read_bytes() == large_file_io.read().encode("utf-8") + + +async def test_upload_with_wrong_key_fails( + hass: HomeAssistant, hass_client, large_file_io +): + """Test uploading fails.""" + assert await async_setup_component(hass, "file_upload", {}) + client = await hass_client() + + with patch( + # Patch temp dir name to avoid tests fail running in parallel + "homeassistant.components.file_upload.TEMP_DIR_NAME", + file_upload.TEMP_DIR_NAME + f"-{getrandbits(10):03x}", + ): + res = await client.post("/api/file_upload", data={"wrong_key": large_file_io}) + + assert res.status == 400 diff --git a/tests/test_requirements.py b/tests/test_requirements.py index 826150e47c4..c10b49aa110 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -401,7 +401,7 @@ async def test_discovery_requirements_mqtt(hass): ) as mock_process: await async_get_integration_with_requirements(hass, "mqtt_comp") - assert len(mock_process.mock_calls) == 2 # mqtt also depends on http + assert len(mock_process.mock_calls) == 3 # mqtt also depends on http assert mock_process.mock_calls[0][1][1] == mqtt.requirements From 6c5aa3b8f9b1b5ea3371fd38b819e61de27b9a9d Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Tue, 29 Nov 2022 23:49:16 -0500 Subject: [PATCH 0892/1033] Make UniFI Protect views more versatile (#82928) --- .../components/unifiprotect/views.py | 30 +++++++- tests/components/unifiprotect/test_views.py | 68 ++++++++++++++++++- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/unifiprotect/views.py b/homeassistant/components/unifiprotect/views.py index a8a767c8879..e05dcde1751 100644 --- a/homeassistant/components/unifiprotect/views.py +++ b/homeassistant/components/unifiprotect/views.py @@ -8,11 +8,12 @@ from typing import Any from urllib.parse import urlencode from aiohttp import web -from pyunifiprotect.data import Event +from pyunifiprotect.data import Camera, Event from pyunifiprotect.exceptions import ClientError from homeassistant.components.http import HomeAssistantView from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import device_registry as dr, entity_registry as er from .const import DOMAIN from .data import ProtectData @@ -104,8 +105,10 @@ class ProtectProxyView(HomeAssistantView): def _get_data_or_404(self, nvr_id: str) -> ProtectData | web.Response: all_data: list[ProtectData] = [] - for data in self.data.values(): + for entry_id, data in self.data.items(): if isinstance(data, ProtectData): + if nvr_id == entry_id: + return data if data.api.bootstrap.nvr.id == nvr_id: return data all_data.append(data) @@ -160,6 +163,27 @@ class VideoProxyView(ProtectProxyView): url = "/api/unifiprotect/video/{nvr_id}/{camera_id}/{start}/{end}" name = "api:unifiprotect_thumbnail" + @callback + def _async_get_camera(self, data: ProtectData, camera_id: str) -> Camera | None: + if (camera := data.api.bootstrap.cameras.get(camera_id)) is not None: + return camera + + entity_registry = er.async_get(self.hass) + device_registry = dr.async_get(self.hass) + + if (entity := entity_registry.async_get(camera_id)) is None or ( + device := device_registry.async_get(entity.device_id or "") + ) is None: + return None + + macs = [c[1] for c in device.connections if c[0] == dr.CONNECTION_NETWORK_MAC] + for mac in macs: + if (ufp_device := data.api.bootstrap.get_device_from_mac(mac)) is not None: + if isinstance(ufp_device, Camera): + camera = ufp_device + break + return camera + async def get( self, request: web.Request, nvr_id: str, camera_id: str, start: str, end: str ) -> web.StreamResponse: @@ -169,7 +193,7 @@ class VideoProxyView(ProtectProxyView): if isinstance(data, web.Response): return data - camera = data.api.bootstrap.cameras.get(camera_id) + camera = self._async_get_camera(data, camera_id) if camera is None: return _404(f"Invalid camera ID: {camera_id}") if not camera.can_read_media(data.api.bootstrap.auth_user): diff --git a/tests/components/unifiprotect/test_views.py b/tests/components/unifiprotect/test_views.py index 0768252f6c9..1c774a9b921 100644 --- a/tests/components/unifiprotect/test_views.py +++ b/tests/components/unifiprotect/test_views.py @@ -111,7 +111,7 @@ async def test_thumbnail( ufp: MockUFPFixture, camera: Camera, ) -> None: - """Test invalid NVR ID in URL.""" + """Test NVR ID in URL.""" ufp.api.get_event_thumbnail = AsyncMock(return_value=b"testtest") @@ -127,6 +127,28 @@ async def test_thumbnail( ufp.api.get_event_thumbnail.assert_called_with("test_id", width=None, height=None) +async def test_thumbnail_entry_id( + hass: HomeAssistant, + hass_client: mock_aiohttp_client, + ufp: MockUFPFixture, + camera: Camera, +) -> None: + """Test config entry ID in URL.""" + + ufp.api.get_event_thumbnail = AsyncMock(return_value=b"testtest") + + await init_entry(hass, ufp, [camera]) + url = async_generate_thumbnail_url("test_id", ufp.entry.entry_id) + + http_client = await hass_client() + response = cast(ClientResponse, await http_client.get(url)) + + assert response.status == 200 + assert response.content_type == "image/jpeg" + assert await response.content.read() == b"testtest" + ufp.api.get_event_thumbnail.assert_called_with("test_id", width=None, height=None) + + async def test_video_bad_event( hass: HomeAssistant, ufp: MockUFPFixture, @@ -425,3 +447,47 @@ async def test_video( assert response.status == 200 ufp.api.request.assert_called_once + + +async def test_video_entity_id( + hass: HomeAssistant, + hass_client: mock_aiohttp_client, + ufp: MockUFPFixture, + camera: Camera, + fixed_now: datetime, +) -> None: + """Test video URL with no video.""" + + content = Mock() + content.__anext__ = AsyncMock(side_effect=[b"test", b"test", StopAsyncIteration()]) + content.__aiter__ = Mock(return_value=content) + + mock_response = Mock() + mock_response.content_length = 8 + mock_response.content.iter_chunked = Mock(return_value=content) + + ufp.api.request = AsyncMock(return_value=mock_response) + await init_entry(hass, ufp, [camera]) + + event_start = fixed_now - timedelta(seconds=30) + event = Event( + api=ufp.api, + camera_id=camera.id, + start=event_start, + end=fixed_now, + id="test_id", + type=EventType.MOTION, + score=100, + smart_detect_types=[], + smart_detect_event_ids=[], + ) + + url = async_generate_event_video_url(event) + url = url.replace(camera.id, "camera.test_camera_high") + + http_client = await hass_client() + response = cast(ClientResponse, await http_client.get(url)) + assert await response.content.read() == b"testtest" + + assert response.status == 200 + ufp.api.request.assert_called_once From a28214caec53b9a4596df6b418fac354e8e13fd7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 29 Nov 2022 19:56:31 -1000 Subject: [PATCH 0893/1033] Fix bluetooth remote connections not picking the best path (#82957) --- .../components/bluetooth/wrappers.py | 68 ++++++------------ tests/components/bluetooth/test_models.py | 69 ++++++++++++++++--- .../keymitt_ble/test_config_flow.py | 55 ++++++++------- 3 files changed, 113 insertions(+), 79 deletions(-) diff --git a/homeassistant/components/bluetooth/wrappers.py b/homeassistant/components/bluetooth/wrappers.py index 9565886c9a3..ca4f2314b65 100644 --- a/homeassistant/components/bluetooth/wrappers.py +++ b/homeassistant/components/bluetooth/wrappers.py @@ -12,7 +12,7 @@ from bleak import BleakClient, BleakError from bleak.backends.client import BaseBleakClient, get_platform_client_backend_type from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementDataCallback, BaseBleakScanner -from bleak_retry_connector import NO_RSSI_VALUE, freshen_ble_device +from bleak_retry_connector import NO_RSSI_VALUE from homeassistant.core import CALLBACK_TYPE, callback as hass_callback from homeassistant.helpers.frame import report @@ -182,25 +182,17 @@ class HaBleakClientWrapper(BleakClient): async def connect(self, **kwargs: Any) -> bool: """Connect to the specified GATT server.""" - if ( - not self._backend - or not self.__ble_device - or not self._async_get_backend_for_ble_device(self.__ble_device) - ): - assert models.MANAGER is not None - wrapped_backend = ( - self._async_get_backend() or self._async_get_fallback_backend() - ) - self.__ble_device = ( - await freshen_ble_device(wrapped_backend.device) - or wrapped_backend.device - ) - self._backend = wrapped_backend.client( - self.__ble_device, - disconnected_callback=self.__disconnected_callback, - timeout=self.__timeout, - hass=models.MANAGER.hass, - ) + assert models.MANAGER is not None + ( + wrapped_backend, + self.__ble_device, + ) = self._async_get_best_available_backend_and_device() + self._backend = wrapped_backend.client( + self.__ble_device, + disconnected_callback=self.__disconnected_callback, + timeout=self.__timeout, + hass=models.MANAGER.hass, + ) return await super().connect(**kwargs) @hass_callback @@ -224,29 +216,14 @@ class HaBleakClientWrapper(BleakClient): return _HaWrappedBleakBackend(ble_device, connector.client) @hass_callback - def _async_get_backend(self) -> _HaWrappedBleakBackend | None: - """Get the bleak backend for the given address.""" - assert models.MANAGER is not None - address = self.__address - ble_device = models.MANAGER.async_ble_device_from_address(address, True) - if ble_device is None: - raise BleakError(f"No device found for address {address}") + def _async_get_best_available_backend_and_device( + self, + ) -> tuple[_HaWrappedBleakBackend, BLEDevice]: + """Get a best available backend and device for the given address. - if backend := self._async_get_backend_for_ble_device(ble_device): - return backend - - return None - - @hass_callback - def _async_get_fallback_backend(self) -> _HaWrappedBleakBackend: - """Get a fallback backend for the given address.""" - # - # The preferred backend cannot currently connect the device - # because it is likely out of connection slots. - # - # We need to try all backends to find one that can - # connect to the device. - # + This method will return the backend with the best rssi + that has a free connection slot. + """ assert models.MANAGER is not None address = self.__address device_advertisement_datas = models.MANAGER.async_get_discovered_devices_and_advertisement_data_by_address( @@ -258,10 +235,9 @@ class HaBleakClientWrapper(BleakClient): or NO_RSSI_VALUE, reverse=True, ): - if backend := self._async_get_backend_for_ble_device( - device_advertisement_data[0] - ): - return backend + ble_device = device_advertisement_data[0] + if backend := self._async_get_backend_for_ble_device(ble_device): + return backend, ble_device raise BleakError( f"No backend with an available connection slot that can reach address {address} was found" diff --git a/tests/components/bluetooth/test_models.py b/tests/components/bluetooth/test_models.py index 05f67b97daa..612f33a68bd 100644 --- a/tests/components/bluetooth/test_models.py +++ b/tests/components/bluetooth/test_models.py @@ -60,22 +60,73 @@ async def test_wrapped_bleak_client_set_disconnected_callback_after_connected( hass, enable_bluetooth, one_adapter ): """Test wrapped bleak client can set a disconnected callback after connected.""" + manager = _get_manager() + + switchbot_proxy_device_has_connection_slot = BLEDevice( + "44:44:33:11:23:45", + "wohand", + { + "connector": HaBluetoothConnector( + MockBleakClient, "mock_bleak_client", lambda: True + ), + "path": "/org/bluez/hci0/dev_44_44_33_11_23_45", + }, + rssi=-40, + ) + switchbot_proxy_device_adv_has_connection_slot = generate_advertisement_data( + local_name="wohand", + service_uuids=[], + manufacturer_data={1: b"\x01"}, + rssi=-40, + ) switchbot_device = BLEDevice( - "44:44:33:11:23:45", "wohand", {"path": "/org/bluez/hci0/dev_44_44_33_11_23_45"} + "44:44:33:11:23:45", + "wohand", + {"path": "/org/bluez/hci0/dev_44_44_33_11_23_45"}, ) switchbot_adv = generate_advertisement_data( - local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"} + local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}, rssi=-100 ) - inject_advertisement(hass, switchbot_device, switchbot_adv) - client = HaBleakClientWrapper(switchbot_device) - with patch( - "bleak.backends.bluezdbus.client.BleakClientBlueZDBus.connect" - ) as connect: + + inject_advertisement_with_source( + hass, switchbot_device, switchbot_adv, "00:00:00:00:00:01" + ) + inject_advertisement_with_source( + hass, + switchbot_proxy_device_has_connection_slot, + switchbot_proxy_device_adv_has_connection_slot, + "esp32_has_connection_slot", + ) + + class FakeScanner(BaseHaScanner): + @property + def discovered_devices_and_advertisement_data( + self, + ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: + """Return a list of discovered devices.""" + return { + switchbot_proxy_device_has_connection_slot.address: ( + switchbot_proxy_device_has_connection_slot, + switchbot_proxy_device_adv_has_connection_slot, + ) + } + + async def async_get_device_by_address(self, address: str) -> BLEDevice | None: + """Return a list of discovered devices.""" + if address == switchbot_proxy_device_has_connection_slot.address: + return switchbot_proxy_device_has_connection_slot + return None + + scanner = FakeScanner(hass, "esp32", "esp32") + cancel = manager.async_register_scanner(scanner, True) + + client = HaBleakClientWrapper(switchbot_proxy_device_has_connection_slot) + with patch("bleak.backends.bluezdbus.client.BleakClientBlueZDBus.connect"): await client.connect() - assert len(connect.mock_calls) == 1 - assert client._backend is not None + assert client.is_connected is True client.set_disconnected_callback(lambda client: None) await client.disconnect() + cancel() async def test_ble_device_with_proxy_client_out_of_connections( diff --git a/tests/components/keymitt_ble/test_config_flow.py b/tests/components/keymitt_ble/test_config_flow.py index 81e6a0be8e7..7729f71f08c 100644 --- a/tests/components/keymitt_ble/test_config_flow.py +++ b/tests/components/keymitt_ble/test_config_flow.py @@ -1,6 +1,6 @@ """Test the MicroBot config flow.""" -from unittest.mock import ANY, patch +from unittest.mock import ANY, AsyncMock, patch from homeassistant.config_entries import SOURCE_BLUETOOTH, SOURCE_USER from homeassistant.const import CONF_ACCESS_TOKEN, CONF_ADDRESS @@ -9,7 +9,6 @@ from homeassistant.data_entry_flow import FlowResultType from . import ( SERVICE_INFO, USER_INPUT, - MockMicroBotApiClient, MockMicroBotApiClientFail, patch_async_setup_entry, ) @@ -19,6 +18,13 @@ from tests.common import MockConfigEntry DOMAIN = "keymitt_ble" +def patch_microbot_api(): + """Patch MicroBot API.""" + return patch( + "homeassistant.components.keymitt_ble.config_flow.MicroBotApiClient", AsyncMock + ) + + async def test_bluetooth_discovery(hass): """Test discovery via bluetooth with a valid device.""" result = await hass.config_entries.flow.async_init( @@ -29,7 +35,7 @@ async def test_bluetooth_discovery(hass): assert result["type"] == FlowResultType.FORM assert result["step_id"] == "init" - with patch_async_setup_entry() as mock_setup_entry: + with patch_async_setup_entry() as mock_setup_entry, patch_microbot_api(): result = await hass.config_entries.flow.async_configure( result["flow_id"], USER_INPUT, @@ -51,13 +57,14 @@ async def test_bluetooth_discovery_already_setup(hass): unique_id="aa:bb:cc:dd:ee:ff", ) entry.add_to_hass(hass) - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_BLUETOOTH}, - data=SERVICE_INFO, - ) - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "already_configured" + with patch_microbot_api(): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_BLUETOOTH}, + data=SERVICE_INFO, + ) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_configured" async def test_user_setup(hass): @@ -74,19 +81,18 @@ async def test_user_setup(hass): assert result["step_id"] == "init" assert result["errors"] == {} - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - USER_INPUT, - ) + with patch_microbot_api(): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + USER_INPUT, + ) + await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "link" assert result2["errors"] is None - with patch( - "homeassistant.components.keymitt_ble.config_flow.MicroBotApiClient", - MockMicroBotApiClient, - ), patch_async_setup_entry() as mock_setup_entry: + with patch_microbot_api(), patch_async_setup_entry() as mock_setup_entry: result3 = await hass.config_entries.flow.async_configure( result2["flow_id"], USER_INPUT, @@ -124,7 +130,7 @@ async def test_user_setup_already_configured(hass): async def test_user_no_devices(hass): """Test the user initiated form with valid mac.""" - with patch( + with patch_microbot_api(), patch( "homeassistant.components.keymitt_ble.config_flow.async_discovered_service_info", return_value=[], ): @@ -138,7 +144,7 @@ async def test_user_no_devices(hass): async def test_no_link(hass): """Test the user initiated form with invalid response.""" - with patch( + with patch_microbot_api(), patch( "homeassistant.components.keymitt_ble.config_flow.async_discovered_service_info", return_value=[SERVICE_INFO], ): @@ -149,10 +155,11 @@ async def test_no_link(hass): assert result["step_id"] == "init" assert result["errors"] == {} - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - USER_INPUT, - ) + with patch_microbot_api(): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + USER_INPUT, + ) assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "link" From fb57785ca606ace767b5910566e356e8ef059b8e Mon Sep 17 00:00:00 2001 From: yeahme49 <58564160+yeahme49@users.noreply.github.com> Date: Wed, 30 Nov 2022 01:11:04 -0600 Subject: [PATCH 0894/1033] Add Tuya DPCode for fsd (#82792) Co-authored-by: Franck Nijhof --- homeassistant/components/tuya/light.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeassistant/components/tuya/light.py b/homeassistant/components/tuya/light.py index 9b78008af55..51517007f71 100644 --- a/homeassistant/components/tuya/light.py +++ b/homeassistant/components/tuya/light.py @@ -123,6 +123,10 @@ LIGHTS: dict[str, tuple[TuyaLightEntityDescription, ...]] = { color_temp=DPCode.TEMP_VALUE, color_data=DPCode.COLOUR_DATA, ), + # Some ceiling fan lights use LIGHT for DPCode instead of SWITCH_LED + TuyaLightEntityDescription( + key=DPCode.LIGHT, + ), ), # Ambient Light # https://developer.tuya.com/en/docs/iot/ambient-light?id=Kaiuz06amhe6g From fcf60a3b53e57ac3f4b0ad1fcc22633ff1252ce4 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 30 Nov 2022 08:35:19 +0100 Subject: [PATCH 0895/1033] Use SchemaOptionsFlowHandler in buienradar (#82901) * Use SchemaOptionsFlowHandler in buienradar * Use NumberSelector * Add mode and unit of measurement --- .../components/buienradar/config_flow.py | 100 +++++++++--------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/homeassistant/components/buienradar/config_flow.py b/homeassistant/components/buienradar/config_flow.py index 445c6cacbc8..aa5482358fc 100644 --- a/homeassistant/components/buienradar/config_flow.py +++ b/homeassistant/components/buienradar/config_flow.py @@ -1,7 +1,8 @@ """Config flow for buienradar integration.""" from __future__ import annotations -from typing import Any +import copy +from typing import Any, cast import voluptuous as vol @@ -10,7 +11,13 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import selector import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaCommonFlowHandler, + SchemaFlowFormStep, + SchemaOptionsFlowHandler, +) from .const import ( CONF_COUNTRY, @@ -23,6 +30,47 @@ from .const import ( SUPPORTED_COUNTRY_CODES, ) +OPTIONS_SCHEMA = vol.Schema( + { + vol.Optional(CONF_COUNTRY, default=DEFAULT_COUNTRY): vol.In( + SUPPORTED_COUNTRY_CODES + ), + vol.Optional(CONF_DELTA, default=DEFAULT_DELTA): selector.NumberSelector( + selector.NumberSelectorConfig( + min=0, + step=1, + mode=selector.NumberSelectorMode.BOX, + unit_of_measurement="seconds", + ), + ), + vol.Optional( + CONF_TIMEFRAME, default=DEFAULT_TIMEFRAME + ): selector.NumberSelector( + selector.NumberSelectorConfig( + min=5, + max=120, + step=5, + mode=selector.NumberSelectorMode.BOX, + unit_of_measurement="minutes", + ), + ), + } +) + + +def _options_suggested_values(handler: SchemaCommonFlowHandler) -> dict[str, Any]: + parent_handler = cast(SchemaOptionsFlowHandler, handler.parent_handler) + suggested_values = copy.deepcopy(dict(parent_handler.config_entry.data)) + suggested_values.update(parent_handler.options) + return suggested_values + + +OPTIONS_FLOW = { + "init": SchemaFlowFormStep( + OPTIONS_SCHEMA, suggested_values=_options_suggested_values + ), +} + class BuienradarFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for buienradar.""" @@ -33,9 +81,9 @@ class BuienradarFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): @callback def async_get_options_flow( config_entry: ConfigEntry, - ) -> BuienradarOptionFlowHandler: + ) -> SchemaOptionsFlowHandler: """Get the options flow for this handler.""" - return BuienradarOptionFlowHandler(config_entry) + return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW) async def async_step_user( self, user_input: dict[str, Any] | None = None @@ -66,49 +114,3 @@ class BuienradarFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): data_schema=data_schema, errors={}, ) - - -class BuienradarOptionFlowHandler(config_entries.OptionsFlow): - """Handle options.""" - - def __init__(self, config_entry: ConfigEntry) -> None: - """Initialize options flow.""" - self.config_entry = config_entry - - async def async_step_init( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Manage the options.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - return self.async_show_form( - step_id="init", - data_schema=vol.Schema( - { - vol.Optional( - CONF_COUNTRY, - default=self.config_entry.options.get( - CONF_COUNTRY, - self.config_entry.data.get(CONF_COUNTRY, DEFAULT_COUNTRY), - ), - ): vol.In(SUPPORTED_COUNTRY_CODES), - vol.Optional( - CONF_DELTA, - default=self.config_entry.options.get( - CONF_DELTA, - self.config_entry.data.get(CONF_DELTA, DEFAULT_DELTA), - ), - ): vol.All(vol.Coerce(int), vol.Range(min=0)), - vol.Optional( - CONF_TIMEFRAME, - default=self.config_entry.options.get( - CONF_TIMEFRAME, - self.config_entry.data.get( - CONF_TIMEFRAME, DEFAULT_TIMEFRAME - ), - ), - ): vol.All(vol.Coerce(int), vol.Range(min=5, max=120)), - } - ), - ) From 8c8994352dd72107ae551bc980dad56921020ca7 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 30 Nov 2022 02:38:52 -0500 Subject: [PATCH 0896/1033] Allow only specific packages to be skipped during startup dependency installation (#82758) --- homeassistant/__main__.py | 13 ++++++++++++- homeassistant/bootstrap.py | 4 +++- homeassistant/core.py | 3 +++ homeassistant/requirements.py | 15 +++++++++++++++ homeassistant/runner.py | 1 + tests/common.py | 1 + tests/test_core.py | 1 + tests/test_main.py | 24 ++++++++++++++++++++++++ tests/test_requirements.py | 18 ++++++++++++++++++ 9 files changed, 78 insertions(+), 2 deletions(-) diff --git a/homeassistant/__main__.py b/homeassistant/__main__.py index cfe93a8fa1a..9dfe4f4f9ed 100644 --- a/homeassistant/__main__.py +++ b/homeassistant/__main__.py @@ -89,11 +89,21 @@ def get_arguments() -> argparse.Namespace: parser.add_argument( "--open-ui", action="store_true", help="Open the webinterface in a browser" ) - parser.add_argument( + + skip_pip_group = parser.add_mutually_exclusive_group() + skip_pip_group.add_argument( "--skip-pip", action="store_true", help="Skips pip install of required packages on startup", ) + skip_pip_group.add_argument( + "--skip-pip-packages", + metavar="package_names", + type=lambda arg: arg.split(","), + default=[], + help="Skip pip install of specific packages on startup", + ) + parser.add_argument( "-v", "--verbose", action="store_true", help="Enable verbose logging to file." ) @@ -180,6 +190,7 @@ def main() -> int: log_file=args.log_file, log_no_color=args.log_no_color, skip_pip=args.skip_pip, + skip_pip_packages=args.skip_pip_packages, safe_mode=args.safe_mode, debug=args.debug, open_ui=args.open_ui, diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 7d1ea91b9aa..6b91557a476 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -118,7 +118,8 @@ async def async_setup_hass( ) hass.config.skip_pip = runtime_config.skip_pip - if runtime_config.skip_pip: + hass.config.skip_pip_packages = runtime_config.skip_pip_packages + if runtime_config.skip_pip or runtime_config.skip_pip_packages: _LOGGER.warning( "Skipping pip installation of required modules. This may cause issues" ) @@ -176,6 +177,7 @@ async def async_setup_hass( if old_logging: hass.data[DATA_LOGGING] = old_logging hass.config.skip_pip = old_config.skip_pip + hass.config.skip_pip_packages = old_config.skip_pip_packages hass.config.internal_url = old_config.internal_url hass.config.external_url = old_config.external_url hass.config.config_dir = old_config.config_dir diff --git a/homeassistant/core.py b/homeassistant/core.py index c5a7599c369..b5ea7fdf194 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -1816,6 +1816,9 @@ class Config: # If True, pip install is skipped for requirements on startup self.skip_pip: bool = False + # List of packages to skip when installing requirements on startup + self.skip_pip_packages: list[str] = [] + # List of loaded components self.components: set[str] = set() diff --git a/homeassistant/requirements.py b/homeassistant/requirements.py index bd06cf61e4b..27472a2bbd8 100644 --- a/homeassistant/requirements.py +++ b/homeassistant/requirements.py @@ -7,6 +7,8 @@ import logging import os from typing import Any, cast +import pkg_resources + from .core import HomeAssistant, callback from .exceptions import HomeAssistantError from .helpers.typing import UNDEFINED, UndefinedType @@ -225,6 +227,19 @@ class RequirementsManager: This method is a coroutine. It will raise RequirementsNotFound if an requirement can't be satisfied. """ + if self.hass.config.skip_pip_packages: + skipped_requirements = [ + req + for req in requirements + if pkg_resources.Requirement.parse(req).project_name + in self.hass.config.skip_pip_packages + ] + + for req in skipped_requirements: + _LOGGER.warning("Skipping requirement %s. This may cause issues", req) + + requirements = [r for r in requirements if r not in skipped_requirements] + if not (missing := self._find_missing_requirements(requirements)): return self._raise_for_failed_requirements(name, missing) diff --git a/homeassistant/runner.py b/homeassistant/runner.py index 5788a6b155a..51b47e0fe2d 100644 --- a/homeassistant/runner.py +++ b/homeassistant/runner.py @@ -36,6 +36,7 @@ class RuntimeConfig: config_dir: str skip_pip: bool = False + skip_pip_packages: list[str] = dataclasses.field(default_factory=list) safe_mode: bool = False verbose: bool = False diff --git a/tests/common.py b/tests/common.py index f32dbd11081..46022022df6 100644 --- a/tests/common.py +++ b/tests/common.py @@ -289,6 +289,7 @@ async def async_test_home_assistant(event_loop, load_registries=True): hass.config.units = METRIC_SYSTEM hass.config.media_dirs = {"local": get_test_config_dir("media")} hass.config.skip_pip = True + hass.config.skip_pip_packages = [] hass.config_entries = config_entries.ConfigEntries( hass, diff --git a/tests/test_core.py b/tests/test_core.py index c5f153ad3bd..2f8db7fc0d6 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -939,6 +939,7 @@ async def test_config_defaults(): assert config.external_url is None assert config.config_source is ha.ConfigSource.DEFAULT assert config.skip_pip is False + assert config.skip_pip_packages == [] assert config.components == set() assert config.api is None assert config.config_dir is None diff --git a/tests/test_main.py b/tests/test_main.py index 5ec6460301f..522515b0d31 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -61,3 +61,27 @@ def test_validate_python(mock_exit): assert mock_exit.called is False mock_exit.reset_mock() + + +@patch("sys.exit") +def test_skip_pip_mutually_exclusive(mock_exit): + """Test --skip-pip and --skip-pip-package are mutually exclusive.""" + + def parse_args(*args): + with patch("sys.argv", ["python"] + list(args)): + return main.get_arguments() + + args = parse_args("--skip-pip") + assert args.skip_pip is True + + args = parse_args("--skip-pip-packages", "foo") + assert args.skip_pip is False + assert args.skip_pip_packages == ["foo"] + + args = parse_args("--skip-pip-packages", "foo-asd,bar-xyz") + assert args.skip_pip is False + assert args.skip_pip_packages == ["foo-asd", "bar-xyz"] + + assert mock_exit.called is False + args = parse_args("--skip-pip", "--skip-pip-packages", "foo") + assert mock_exit.called is True diff --git a/tests/test_requirements.py b/tests/test_requirements.py index c10b49aa110..6dadeba3797 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -1,4 +1,5 @@ """Test requirements module.""" +import logging import os from unittest.mock import call, patch @@ -93,6 +94,23 @@ async def test_install_missing_package(hass): assert len(mock_inst.mock_calls) == 3 +async def test_install_skipped_package(hass, caplog): + """Test an install attempt on a dependency that should be skipped.""" + with patch( + "homeassistant.util.package.install_package", return_value=True + ) as mock_inst: + hass.config.skip_pip_packages = ["hello"] + with caplog.at_level(logging.WARNING): + await async_process_requirements( + hass, "test_component", ["hello==1.0.0", "not_skipped==1.2.3"] + ) + + assert "Skipping requirement hello==1.0.0" in caplog.text + + assert len(mock_inst.mock_calls) == 1 + assert mock_inst.mock_calls[0].args[0] == "not_skipped==1.2.3" + + async def test_get_integration_with_requirements(hass): """Check getting an integration with loaded requirements.""" hass.config.skip_pip = False From f2180de7c0f69d5b1cb3aa562552d4a15df96546 Mon Sep 17 00:00:00 2001 From: Thijs W Date: Wed, 30 Nov 2022 09:38:37 +0100 Subject: [PATCH 0897/1033] Adjust code comment in Frontier Silicon (#82969) --- homeassistant/components/frontier_silicon/browse_media.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/frontier_silicon/browse_media.py b/homeassistant/components/frontier_silicon/browse_media.py index 03b28a025d0..53deb399b87 100644 --- a/homeassistant/components/frontier_silicon/browse_media.py +++ b/homeassistant/components/frontier_silicon/browse_media.py @@ -113,7 +113,7 @@ async def browse_node( media_content_type, media_content_id, ): - """List the contents of a navigation directory (or preset list) on a Frontier Silicon device.""" + """List the contents of a navigation directory (or preset list).""" player_mode, browse_type, *parent_keys = media_content_id.split("/") From e88e8862db9c570dbf02876061458707785b58f4 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Wed, 30 Nov 2022 03:40:24 -0500 Subject: [PATCH 0898/1033] Add missing device action string for text (#82963) Co-authored-by: Franck Nijhof --- homeassistant/components/text/strings.json | 7 ++++++- homeassistant/components/text/translations/en.json | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/text/strings.json b/homeassistant/components/text/strings.json index 8d4d14669a0..0f5ddf5b331 100644 --- a/homeassistant/components/text/strings.json +++ b/homeassistant/components/text/strings.json @@ -1,3 +1,8 @@ { - "title": "Text" + "title": "Text", + "device_automation": { + "action_type": { + "set_value": "Set value for {entity_name}" + } + } } diff --git a/homeassistant/components/text/translations/en.json b/homeassistant/components/text/translations/en.json index 19b840cdb8f..5d0d038c05c 100644 --- a/homeassistant/components/text/translations/en.json +++ b/homeassistant/components/text/translations/en.json @@ -1,3 +1,8 @@ { + "device_automation": { + "action_type": { + "set_value": "Set value for {entity_name}" + } + }, "title": "Text" } \ No newline at end of file From b5aac9a1c78663d834da5d4ca02ca845ac6182af Mon Sep 17 00:00:00 2001 From: Matrix Date: Wed, 30 Nov 2022 17:11:02 +0800 Subject: [PATCH 0899/1033] Add entity update condition to yolink (#82970) add entity update condition --- homeassistant/components/yolink/sensor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/yolink/sensor.py b/homeassistant/components/yolink/sensor.py index e2e5fa2a94e..4b658246bc9 100644 --- a/homeassistant/components/yolink/sensor.py +++ b/homeassistant/components/yolink/sensor.py @@ -53,6 +53,7 @@ class YoLinkSensorEntityDescription( """YoLink SensorEntityDescription.""" value: Callable = lambda state: state + should_update_entity: Callable = lambda state: True SENSOR_DEVICE_TYPE = [ @@ -127,6 +128,7 @@ SENSOR_TYPES: tuple[YoLinkSensorEntityDescription, ...] = ( name="Temperature", state_class=SensorStateClass.MEASUREMENT, exists_fn=lambda device: device.device_type in MCU_DEV_TEMPERATURE_SENSOR, + should_update_entity=lambda value: value is not None, ), YoLinkSensorEntityDescription( key="loraInfo", @@ -137,6 +139,7 @@ SENSOR_TYPES: tuple[YoLinkSensorEntityDescription, ...] = ( state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, + should_update_entity=lambda value: value is not None, ), ) @@ -195,7 +198,7 @@ class YoLinkSensorEntity(YoLinkEntity, SensorEntity): attr_val := self.entity_description.value( state.get(self.entity_description.key) ) - ) is None and self.entity_description.key in ["devTemperature", "loraInfo"]: + ) is None and self.entity_description.should_update_entity(attr_val) is False: return self._attr_native_value = attr_val self.async_write_ha_state() From 200f29563aec16b6dee2d401c5071acf91e5b27c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 30 Nov 2022 10:13:14 +0100 Subject: [PATCH 0900/1033] Enforce MediaPlayerState in group (#78465) * Enforce MediaPlayerState in group * Adjust * Use self._attr_state * Add warning * Add group name to logger warning * Remove logger warning * Remove duplicate code * Add type hints to tests * Improve coverage * Improve coverage * suppress ValueError --- .../components/group/media_player.py | 19 ++++++++----------- tests/components/group/test_media_player.py | 19 +++++++++++++------ 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/group/media_player.py b/homeassistant/components/group/media_player.py index e966016d63d..a349a628004 100644 --- a/homeassistant/components/group/media_player.py +++ b/homeassistant/components/group/media_player.py @@ -1,6 +1,7 @@ """This platform allows several media players to be grouped into one media player.""" from __future__ import annotations +from contextlib import suppress from typing import Any import voluptuous as vol @@ -108,7 +109,6 @@ class MediaPlayerGroup(MediaPlayerEntity): def __init__(self, unique_id: str | None, name: str, entities: list[str]) -> None: """Initialize a Media Group entity.""" self._name = name - self._state: str | None = None self._attr_unique_id = unique_id self._entities = entities @@ -206,11 +206,6 @@ class MediaPlayerGroup(MediaPlayerEntity): """Return the name of the entity.""" return self._name - @property - def state(self) -> str | None: - """Return the state of the media group.""" - return self._state - @property def extra_state_attributes(self) -> dict: """Return the state attributes for the media group.""" @@ -395,15 +390,17 @@ class MediaPlayerGroup(MediaPlayerEntity): ) if not valid_state: # Set as unknown if all members are unknown or unavailable - self._state = None + self._attr_state = None else: off_values = {MediaPlayerState.OFF, STATE_UNAVAILABLE, STATE_UNKNOWN} - if states.count(states[0]) == len(states): - self._state = states[0] + if states.count(single_state := states[0]) == len(states): + self._attr_state = None + with suppress(ValueError): + self._attr_state = MediaPlayerState(single_state) elif any(state for state in states if state not in off_values): - self._state = MediaPlayerState.ON + self._attr_state = MediaPlayerState.ON else: - self._state = MediaPlayerState.OFF + self._attr_state = MediaPlayerState.OFF supported_features = MediaPlayerEntityFeature(0) if self._features[KEY_CLEAR_PLAYLIST]: diff --git a/tests/components/group/test_media_player.py b/tests/components/group/test_media_player.py index 42bc96dbc6e..4549a7f5fec 100644 --- a/tests/components/group/test_media_player.py +++ b/tests/components/group/test_media_player.py @@ -1,5 +1,5 @@ """The tests for the Media group platform.""" -from unittest.mock import patch +from unittest.mock import Mock, patch import async_timeout import pytest @@ -43,6 +43,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) +from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component @@ -57,7 +58,7 @@ def media_player_media_seek_fixture(): yield seek -async def test_default_state(hass): +async def test_default_state(hass: HomeAssistant) -> None: """Test media group default state.""" hass.states.async_set("media_player.player_1", "on") await async_setup_component( @@ -91,7 +92,7 @@ async def test_default_state(hass): assert entry.unique_id == "unique_identifier" -async def test_state_reporting(hass): +async def test_state_reporting(hass: HomeAssistant) -> None: """Test the state reporting. The group state is unavailable if all group members are unavailable. @@ -170,6 +171,12 @@ async def test_state_reporting(hass): await hass.async_block_till_done() assert hass.states.get("media_player.media_group").state == STATE_OFF + # All group members in same invalid state -> unknown + hass.states.async_set("media_player.player_1", "invalid_state") + hass.states.async_set("media_player.player_2", "invalid_state") + await hass.async_block_till_done() + assert hass.states.get("media_player.media_group").state == STATE_UNKNOWN + # All group members removed from the state machine -> unavailable hass.states.async_remove("media_player.player_1") hass.states.async_remove("media_player.player_2") @@ -177,7 +184,7 @@ async def test_state_reporting(hass): assert hass.states.get("media_player.media_group").state == STATE_UNAVAILABLE -async def test_supported_features(hass): +async def test_supported_features(hass: HomeAssistant) -> None: """Test supported features reporting.""" pause_play_stop = ( MediaPlayerEntityFeature.PAUSE @@ -241,7 +248,7 @@ async def test_supported_features(hass): assert state.attributes[ATTR_SUPPORTED_FEATURES] == pause_play_stop | play_media -async def test_service_calls(hass, mock_media_seek): +async def test_service_calls(hass: HomeAssistant, mock_media_seek: Mock) -> None: """Test service calls.""" await async_setup_component( hass, @@ -533,7 +540,7 @@ async def test_service_calls(hass, mock_media_seek): assert hass.states.get("media_player.living_room").state == STATE_OFF -async def test_nested_group(hass): +async def test_nested_group(hass: HomeAssistant) -> None: """Test nested media group.""" await async_setup_component( hass, From 663482fb10e0525ea1a5efdb49ea3f7a183a2f6d Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 30 Nov 2022 10:18:57 +0100 Subject: [PATCH 0901/1033] Add flow_state to SchemaCommonFlowHandler (#82967) * Add local_context to SchemaCommonFlowHandler * Rename to context * Rename to flow_state --- .../helpers/schema_config_entry_flow.py | 10 +++ .../helpers/test_schema_config_entry_flow.py | 77 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 8d72910b6fe..a730a311db9 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -98,6 +98,7 @@ class SchemaCommonFlowHandler: self._flow = flow self._handler = handler self._options = options if options is not None else {} + self._flow_state: dict[str, Any] = {} @property def parent_handler(self) -> SchemaConfigFlowHandler | SchemaOptionsFlowHandler: @@ -109,6 +110,15 @@ class SchemaCommonFlowHandler: """Return the options linked to the current flow handler.""" return self._options + @property + def flow_state(self) -> dict[str, Any]: + """Return the flow state, used to store temporary data. + + It can be used for example to store the key or the index of a sub-item + that will be edited in the next step. + """ + return self._flow_state + async def async_step( self, step_id: str, user_input: dict[str, Any] | None = None ) -> FlowResult: diff --git a/tests/helpers/test_schema_config_entry_flow.py b/tests/helpers/test_schema_config_entry_flow.py index 27f4aa0d220..29d01b2d2b6 100644 --- a/tests/helpers/test_schema_config_entry_flow.py +++ b/tests/helpers/test_schema_config_entry_flow.py @@ -17,6 +17,7 @@ from homeassistant.helpers.schema_config_entry_flow import ( SchemaFlowError, SchemaFlowFormStep, SchemaFlowMenuStep, + SchemaOptionsFlowHandler, wrapped_entity_config_entry_title, ) from homeassistant.util.decorator import Registry @@ -555,3 +556,79 @@ async def test_suggested_values( result["flow_id"], {"option1": "blabla"} ) assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + + +async def test_options_flow_state(hass: HomeAssistant) -> None: + """Test flow_state handling in SchemaFlowFormStep.""" + + OPTIONS_SCHEMA = vol.Schema( + {vol.Optional("option1", default="a very reasonable default"): str} + ) + + def _init_schema(handler: SchemaCommonFlowHandler) -> None: + handler.flow_state["idx"] = None + + def _validate_step1_input( + handler: SchemaCommonFlowHandler, user_input: dict[str, Any] + ) -> dict[str, Any]: + handler.flow_state["idx"] = user_input["option1"] + return user_input + + def _validate_step2_input( + handler: SchemaCommonFlowHandler, user_input: dict[str, Any] + ) -> dict[str, Any]: + user_input["idx_from_flow_state"] = handler.flow_state["idx"] + return user_input + + OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { + "init": SchemaFlowFormStep(_init_schema, next_step="step_1"), + "step_1": SchemaFlowFormStep( + OPTIONS_SCHEMA, + validate_user_input=_validate_step1_input, + next_step="step_2", + ), + "step_2": SchemaFlowFormStep( + OPTIONS_SCHEMA, + validate_user_input=_validate_step2_input, + ), + } + + class TestFlow(SchemaConfigFlowHandler, domain="test"): + config_flow = {} + options_flow = OPTIONS_FLOW + + config_entry = MockConfigEntry( + data={}, + domain="test", + options={"option1": "initial value"}, + ) + config_entry.add_to_hass(hass) + + # Start flow in basic mode, flow state is initialised with None value + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "step_1" + + options_handler: SchemaOptionsFlowHandler + options_handler = hass.config_entries.options._progress[result["flow_id"]] + assert options_handler._common_handler.flow_state == {"idx": None} + + # In step 1, flow state is updated with user input + result = await hass.config_entries.options.async_configure( + result["flow_id"], {"option1": "blublu"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "step_2" + + options_handler = hass.config_entries.options._progress[result["flow_id"]] + assert options_handler._common_handler.flow_state == {"idx": "blublu"} + + # In step 2, options were updated from flow state + result = await hass.config_entries.options.async_configure( + result["flow_id"], {"option1": "blabla"} + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"] == { + "idx_from_flow_state": "blublu", + "option1": "blabla", + } From c2d372dd13bd0892f6fd3f4386efac6f7b5ce418 Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Wed, 30 Nov 2022 10:28:28 +0100 Subject: [PATCH 0902/1033] Fix HomeWizard code quality issues (#82973) * Sort platforms a-z * Collect all switch entities and load in a single call * Add test for button press --- homeassistant/components/homewizard/const.py | 2 +- homeassistant/components/homewizard/switch.py | 18 ++++------ tests/components/homewizard/test_button.py | 34 ++++++++++++++++++- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/homewizard/const.py b/homeassistant/components/homewizard/const.py index 8e24bd19e44..e5ceb5ab23d 100644 --- a/homeassistant/components/homewizard/const.py +++ b/homeassistant/components/homewizard/const.py @@ -10,7 +10,7 @@ from homewizard_energy.models import Data, Device, State, System from homeassistant.const import Platform DOMAIN = "homewizard" -PLATFORMS = [Platform.SENSOR, Platform.SWITCH, Platform.NUMBER, Platform.BUTTON] +PLATFORMS = [Platform.BUTTON, Platform.NUMBER, Platform.SENSOR, Platform.SWITCH] # Platform config. CONF_API_ENABLED = "api_enabled" diff --git a/homeassistant/components/homewizard/switch.py b/homeassistant/components/homewizard/switch.py index 8a40087403e..3b255d195b1 100644 --- a/homeassistant/components/homewizard/switch.py +++ b/homeassistant/components/homewizard/switch.py @@ -22,20 +22,16 @@ async def async_setup_entry( """Set up switches.""" coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + entities: list[SwitchEntity] = [] + if coordinator.data["state"]: - async_add_entities( - [ - HWEnergyMainSwitchEntity(coordinator, entry), - HWEnergySwitchLockEntity(coordinator, entry), - ] - ) + entities.append(HWEnergyMainSwitchEntity(coordinator, entry)) + entities.append(HWEnergySwitchLockEntity(coordinator, entry)) if coordinator.data["system"]: - async_add_entities( - [ - HWEnergyEnableCloudEntity(hass, coordinator, entry), - ] - ) + entities.append(HWEnergyEnableCloudEntity(hass, coordinator, entry)) + + async_add_entities(entities) class HWEnergySwitchEntity( diff --git a/tests/components/homewizard/test_button.py b/tests/components/homewizard/test_button.py index 5d1481f4c1d..79c6fe3c4a9 100644 --- a/tests/components/homewizard/test_button.py +++ b/tests/components/homewizard/test_button.py @@ -1,7 +1,8 @@ """Test the identify button for HomeWizard.""" from unittest.mock import patch -from homeassistant.const import ATTR_FRIENDLY_NAME +from homeassistant.components import button +from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_UNKNOWN from homeassistant.helpers import entity_registry as er from .generator import get_mock_device @@ -57,3 +58,34 @@ async def test_identify_button_is_loaded( entry = entity_registry.async_get("button.product_name_aabbccddeeff_identify") assert entry assert entry.unique_id == "aabbccddeeff_identify" + + +async def test_cloud_connection_on_off(hass, mock_config_entry_data, mock_config_entry): + """Test the creation and values of the Litter-Robot button.""" + + api = get_mock_device(product_type="HWE-SKT", firmware_version="3.02") + + with patch( + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", + return_value=api, + ): + entry = mock_config_entry + entry.data = mock_config_entry_data + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert ( + hass.states.get("button.product_name_aabbccddeeff_identify").state + == STATE_UNKNOWN + ) + + assert api.identify.call_count == 0 + await hass.services.async_call( + button.DOMAIN, + button.SERVICE_PRESS, + {"entity_id": "button.product_name_aabbccddeeff_identify"}, + blocking=True, + ) + assert api.identify.call_count == 1 From 2f91e2d6a5f8d2ac5338f1adacc2b2f7e6e359bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 30 Nov 2022 10:43:53 +0100 Subject: [PATCH 0903/1033] Bump pycfdns from 2.0.0 to 2.0.1 (#82971) --- homeassistant/components/cloudflare/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cloudflare/manifest.json b/homeassistant/components/cloudflare/manifest.json index 55e43eb4fde..b1d62bb2813 100644 --- a/homeassistant/components/cloudflare/manifest.json +++ b/homeassistant/components/cloudflare/manifest.json @@ -2,7 +2,7 @@ "domain": "cloudflare", "name": "Cloudflare", "documentation": "https://www.home-assistant.io/integrations/cloudflare", - "requirements": ["pycfdns==2.0.0"], + "requirements": ["pycfdns==2.0.1"], "codeowners": ["@ludeeus", "@ctalkington"], "config_flow": true, "iot_class": "cloud_push", diff --git a/requirements_all.txt b/requirements_all.txt index ac8bbd9b144..4e3aa5cc52f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1495,7 +1495,7 @@ pybravia==0.2.3 pycarwings2==2.13 # homeassistant.components.cloudflare -pycfdns==2.0.0 +pycfdns==2.0.1 # homeassistant.components.channels pychannels==1.2.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c62ca320ee0..acfc7af20ac 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1071,7 +1071,7 @@ pybotvac==0.0.23 pybravia==0.2.3 # homeassistant.components.cloudflare -pycfdns==2.0.0 +pycfdns==2.0.1 # homeassistant.components.cast pychromecast==13.0.1 From d243da99c73db23a0bf3d56a2220a37b1cc3ad47 Mon Sep 17 00:00:00 2001 From: muppet3000 Date: Wed, 30 Nov 2022 09:56:47 +0000 Subject: [PATCH 0904/1033] Bump growattServer to 1.3.0 (#82953) * Growatt library bump and init tweak (#81951) * Addressing review comments (#81951) --- homeassistant/components/growatt_server/manifest.json | 2 +- homeassistant/components/growatt_server/sensor.py | 7 ++++++- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/growatt_server/manifest.json b/homeassistant/components/growatt_server/manifest.json index e3b63f7c8b3..3bb2317e0ef 100644 --- a/homeassistant/components/growatt_server/manifest.json +++ b/homeassistant/components/growatt_server/manifest.json @@ -3,7 +3,7 @@ "name": "Growatt", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/growatt_server/", - "requirements": ["growattServer==1.2.4"], + "requirements": ["growattServer==1.3.0"], "codeowners": ["@indykoning", "@muppet3000", "@JasperPlant"], "iot_class": "cloud_polling", "loggers": ["growattServer"] diff --git a/homeassistant/components/growatt_server/sensor.py b/homeassistant/components/growatt_server/sensor.py index d6b74b78475..d9ca800131f 100644 --- a/homeassistant/components/growatt_server/sensor.py +++ b/homeassistant/components/growatt_server/sensor.py @@ -80,7 +80,12 @@ async def async_setup_entry( config[CONF_URL] = url hass.config_entries.async_update_entry(config_entry, data=config) - api = growattServer.GrowattApi() + # Initialise the library with a random user id each time it is started, + # also extend the library's default identifier to include 'home-assistant' + api = growattServer.GrowattApi( + add_random_user_id=True, + agent_identifier=f"{growattServer.GrowattApi.agent_identifier} - home-assistant", + ) api.server_url = url devices, plant_id = await hass.async_add_executor_job(get_device_list, api, config) diff --git a/requirements_all.txt b/requirements_all.txt index 4e3aa5cc52f..5908a0eb94d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -817,7 +817,7 @@ greenwavereality==0.5.1 gridnet==4.0.0 # homeassistant.components.growatt_server -growattServer==1.2.4 +growattServer==1.3.0 # homeassistant.components.google_sheets gspread==5.5.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index acfc7af20ac..44582937de4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -612,7 +612,7 @@ greeneye_monitor==3.0.3 gridnet==4.0.0 # homeassistant.components.growatt_server -growattServer==1.2.4 +growattServer==1.3.0 # homeassistant.components.google_sheets gspread==5.5.0 From 0316800347c235823ae3c9d602deb8c1e50cce64 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 30 Nov 2022 11:18:49 +0100 Subject: [PATCH 0905/1033] Enforce MediaPlayerState in media-player entities (#78467) --- homeassistant/components/media_player/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 1f3204c3df1..2337990933a 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -491,7 +491,7 @@ class MediaPlayerEntity(Entity): _attr_sound_mode: str | None = None _attr_source_list: list[str] | None = None _attr_source: str | None = None - _attr_state: MediaPlayerState | str | None = None + _attr_state: MediaPlayerState | None = None _attr_supported_features: MediaPlayerEntityFeature = MediaPlayerEntityFeature(0) _attr_volume_level: float | None = None @@ -506,7 +506,7 @@ class MediaPlayerEntity(Entity): return None @property - def state(self) -> MediaPlayerState | str | None: + def state(self) -> MediaPlayerState | None: """State of the player.""" return self._attr_state From 490aec0b11225ec75becc943c81fc85dcbdcfd3c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 30 Nov 2022 12:20:18 +0100 Subject: [PATCH 0906/1033] Update pylint to 2.15.7 (#82977) --- homeassistant/components/esphome/bluetooth/client.py | 2 +- homeassistant/components/knx/config_flow.py | 2 +- homeassistant/core.py | 1 - requirements_test.txt | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index e3730a216b7..da4d5fb3d26 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -296,7 +296,7 @@ class ESPHomeClient(BaseBleakClient): address_type=self._address_type, ) ) - except Exception: # pylint: disable=broad-except + except Exception: with contextlib.suppress(BleakError): # If the connect call throws an exception, # we need to make sure we await the future diff --git a/homeassistant/components/knx/config_flow.py b/homeassistant/components/knx/config_flow.py index 4ee46939f2d..30e48cd4d6a 100644 --- a/homeassistant/components/knx/config_flow.py +++ b/homeassistant/components/knx/config_flow.py @@ -151,7 +151,7 @@ class KNXCommonFlow(ABC, FlowHandler): # keep a reference to the generator to scan in background until user selects a connection type self._async_scan_gen = self._gatewayscanner.async_scan() try: - await self._async_scan_gen.__anext__() # pylint: disable=unnecessary-dunder-call + await self._async_scan_gen.__anext__() except StopAsyncIteration: pass # scan finished, no interfaces discovered else: diff --git a/homeassistant/core.py b/homeassistant/core.py index b5ea7fdf194..82ee5216f4f 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -2101,7 +2101,6 @@ class Config: and "language" in owner_data["language"] ): with suppress(vol.InInvalid): - # pylint: disable-next=protected-access data["language"] = cv.language( owner_data["language"]["language"] ) diff --git a/requirements_test.txt b/requirements_test.txt index 7db57fb5bcf..075eefcf892 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -14,7 +14,7 @@ freezegun==1.2.2 mock-open==1.4.0 mypy==0.991 pre-commit==2.20.0 -pylint==2.15.6 +pylint==2.15.7 pipdeptree==2.3.1 pytest-asyncio==0.20.2 pytest-aiohttp==1.0.4 From 98f263c28956c0d17bd211d2d39654b01499792c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 30 Nov 2022 12:26:52 +0100 Subject: [PATCH 0907/1033] Make SchemaFlowFormStep functions async (#82962) * Make validate async in SchemaOptionsFlowHandler * Adjust group * Adjust tests * Move all to async * Adjust integrations * Missed an integration * Missed one * Rebase to fix conflict --- .../components/asuswrt/config_flow.py | 2 +- .../components/buienradar/config_flow.py | 2 +- homeassistant/components/group/config_flow.py | 45 +++++++++++-------- .../components/scrape/config_flow.py | 8 ++-- .../components/threshold/config_flow.py | 2 +- .../components/utility_meter/config_flow.py | 2 +- .../helpers/schema_config_entry_flow.py | 40 +++++++++-------- .../helpers/test_schema_config_entry_flow.py | 33 ++++++++++---- 8 files changed, 80 insertions(+), 54 deletions(-) diff --git a/homeassistant/components/asuswrt/config_flow.py b/homeassistant/components/asuswrt/config_flow.py index 414dbc65d8b..6b0056b14fa 100644 --- a/homeassistant/components/asuswrt/config_flow.py +++ b/homeassistant/components/asuswrt/config_flow.py @@ -69,7 +69,7 @@ OPTIONS_SCHEMA = vol.Schema( ) -def get_options_schema(handler: SchemaCommonFlowHandler) -> vol.Schema: +async def get_options_schema(handler: SchemaCommonFlowHandler) -> vol.Schema: """Get options schema.""" options_flow: SchemaOptionsFlowHandler options_flow = cast(SchemaOptionsFlowHandler, handler.parent_handler) diff --git a/homeassistant/components/buienradar/config_flow.py b/homeassistant/components/buienradar/config_flow.py index aa5482358fc..87810edda2e 100644 --- a/homeassistant/components/buienradar/config_flow.py +++ b/homeassistant/components/buienradar/config_flow.py @@ -58,7 +58,7 @@ OPTIONS_SCHEMA = vol.Schema( ) -def _options_suggested_values(handler: SchemaCommonFlowHandler) -> dict[str, Any]: +async def _options_suggested_values(handler: SchemaCommonFlowHandler) -> dict[str, Any]: parent_handler = cast(SchemaOptionsFlowHandler, handler.parent_handler) suggested_values = copy.deepcopy(dict(parent_handler.config_entry.data)) suggested_values.update(parent_handler.options) diff --git a/homeassistant/components/group/config_flow.py b/homeassistant/components/group/config_flow.py index 4d689914378..9a084cde685 100644 --- a/homeassistant/components/group/config_flow.py +++ b/homeassistant/components/group/config_flow.py @@ -1,7 +1,7 @@ """Config flow for Group integration.""" from __future__ import annotations -from collections.abc import Callable, Mapping +from collections.abc import Callable, Coroutine, Mapping from functools import partial from typing import Any, cast @@ -24,7 +24,7 @@ from .binary_sensor import CONF_ALL from .const import CONF_HIDE_MEMBERS -def basic_group_options_schema( +async def basic_group_options_schema( domain: str, handler: SchemaCommonFlowHandler ) -> vol.Schema: """Generate options schema.""" @@ -52,9 +52,9 @@ def basic_group_config_schema(domain: str) -> vol.Schema: ) -def binary_sensor_options_schema(handler: SchemaCommonFlowHandler) -> vol.Schema: +async def binary_sensor_options_schema(handler: SchemaCommonFlowHandler) -> vol.Schema: """Generate options schema.""" - return basic_group_options_schema("binary_sensor", handler).extend( + return (await basic_group_options_schema("binary_sensor", handler)).extend( { vol.Required(CONF_ALL, default=False): selector.BooleanSelector(), } @@ -68,11 +68,11 @@ BINARY_SENSOR_CONFIG_SCHEMA = basic_group_config_schema("binary_sensor").extend( ) -def light_switch_options_schema( +async def light_switch_options_schema( domain: str, handler: SchemaCommonFlowHandler ) -> vol.Schema: """Generate options schema.""" - return basic_group_options_schema(domain, handler).extend( + return (await basic_group_options_schema(domain, handler)).extend( { vol.Required( CONF_ALL, default=False, description={"advanced": True} @@ -92,19 +92,19 @@ GROUP_TYPES = [ ] -@callback -def choose_options_step(options: dict[str, Any]) -> str: +async def choose_options_step(options: dict[str, Any]) -> str: """Return next step_id for options flow according to group_type.""" return cast(str, options["group_type"]) def set_group_type( group_type: str, -) -> Callable[[SchemaCommonFlowHandler, dict[str, Any]], dict[str, Any]]: +) -> Callable[ + [SchemaCommonFlowHandler, dict[str, Any]], Coroutine[Any, Any, dict[str, Any]] +]: """Set group type.""" - @callback - def _set_group_type( + async def _set_group_type( handler: SchemaCommonFlowHandler, user_input: dict[str, Any] ) -> dict[str, Any]: """Add group type to user input.""" @@ -116,23 +116,32 @@ def set_group_type( CONFIG_FLOW = { "user": SchemaFlowMenuStep(GROUP_TYPES), "binary_sensor": SchemaFlowFormStep( - BINARY_SENSOR_CONFIG_SCHEMA, set_group_type("binary_sensor") + BINARY_SENSOR_CONFIG_SCHEMA, + validate_user_input=set_group_type("binary_sensor"), ), "cover": SchemaFlowFormStep( - basic_group_config_schema("cover"), set_group_type("cover") + basic_group_config_schema("cover"), + validate_user_input=set_group_type("cover"), + ), + "fan": SchemaFlowFormStep( + basic_group_config_schema("fan"), + validate_user_input=set_group_type("fan"), ), - "fan": SchemaFlowFormStep(basic_group_config_schema("fan"), set_group_type("fan")), "light": SchemaFlowFormStep( - basic_group_config_schema("light"), set_group_type("light") + basic_group_config_schema("light"), + validate_user_input=set_group_type("light"), ), "lock": SchemaFlowFormStep( - basic_group_config_schema("lock"), set_group_type("lock") + basic_group_config_schema("lock"), + validate_user_input=set_group_type("lock"), ), "media_player": SchemaFlowFormStep( - basic_group_config_schema("media_player"), set_group_type("media_player") + basic_group_config_schema("media_player"), + validate_user_input=set_group_type("media_player"), ), "switch": SchemaFlowFormStep( - basic_group_config_schema("switch"), set_group_type("switch") + basic_group_config_schema("switch"), + validate_user_input=set_group_type("switch"), ), } diff --git a/homeassistant/components/scrape/config_flow.py b/homeassistant/components/scrape/config_flow.py index 9f683795e08..d28d2feb40d 100644 --- a/homeassistant/components/scrape/config_flow.py +++ b/homeassistant/components/scrape/config_flow.py @@ -116,7 +116,7 @@ SENSOR_SETUP = { } -def validate_rest_setup( +async def validate_rest_setup( handler: SchemaCommonFlowHandler, user_input: dict[str, Any] ) -> dict[str, Any]: """Validate rest setup.""" @@ -129,7 +129,7 @@ def validate_rest_setup( return user_input -def validate_sensor_setup( +async def validate_sensor_setup( handler: SchemaCommonFlowHandler, user_input: dict[str, Any] ) -> dict[str, Any]: """Validate sensor input.""" @@ -143,7 +143,7 @@ def validate_sensor_setup( return {} -def get_remove_sensor_schema(handler: SchemaCommonFlowHandler) -> vol.Schema: +async def get_remove_sensor_schema(handler: SchemaCommonFlowHandler) -> vol.Schema: """Return schema for sensor removal.""" return vol.Schema( { @@ -157,7 +157,7 @@ def get_remove_sensor_schema(handler: SchemaCommonFlowHandler) -> vol.Schema: ) -def validate_remove_sensor( +async def validate_remove_sensor( handler: SchemaCommonFlowHandler, user_input: dict[str, Any] ) -> dict[str, Any]: """Validate remove sensor.""" diff --git a/homeassistant/components/threshold/config_flow.py b/homeassistant/components/threshold/config_flow.py index 373e48e7ba3..fbb12872306 100644 --- a/homeassistant/components/threshold/config_flow.py +++ b/homeassistant/components/threshold/config_flow.py @@ -19,7 +19,7 @@ from homeassistant.helpers.schema_config_entry_flow import ( from .const import CONF_HYSTERESIS, CONF_LOWER, CONF_UPPER, DEFAULT_HYSTERESIS, DOMAIN -def _validate_mode( +async def _validate_mode( handler: SchemaCommonFlowHandler, user_input: dict[str, Any] ) -> dict[str, Any]: """Validate the threshold mode, and set limits to None if not set.""" diff --git a/homeassistant/components/utility_meter/config_flow.py b/homeassistant/components/utility_meter/config_flow.py index 59bde5ac300..5424d8a55ad 100644 --- a/homeassistant/components/utility_meter/config_flow.py +++ b/homeassistant/components/utility_meter/config_flow.py @@ -47,7 +47,7 @@ METER_TYPES = [ ] -def _validate_config( +async def _validate_config( handler: SchemaCommonFlowHandler, user_input: dict[str, Any] ) -> dict[str, Any]: """Validate config.""" diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index a730a311db9..8980abbb466 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -2,7 +2,7 @@ from __future__ import annotations from abc import abstractmethod -from collections.abc import Callable, Mapping +from collections.abc import Callable, Coroutine, Mapping import copy from dataclasses import dataclass import types @@ -32,7 +32,7 @@ class SchemaFlowFormStep(SchemaFlowStep): """Define a config or options flow form step.""" schema: vol.Schema | Callable[ - [SchemaCommonFlowHandler], vol.Schema | None + [SchemaCommonFlowHandler], Coroutine[Any, Any, vol.Schema | None] ] | None = None """Optional voluptuous schema, or function which returns a schema or None, for requesting and validating user input. @@ -44,7 +44,7 @@ class SchemaFlowFormStep(SchemaFlowStep): """ validate_user_input: Callable[ - [SchemaCommonFlowHandler, dict[str, Any]], dict[str, Any] + [SchemaCommonFlowHandler, dict[str, Any]], Coroutine[Any, Any, dict[str, Any]] ] | None = None """Optional function to validate user input. @@ -54,7 +54,9 @@ class SchemaFlowFormStep(SchemaFlowStep): - The `validate_user_input` should raise `SchemaFlowError` if user input is invalid. """ - next_step: Callable[[dict[str, Any]], str | None] | str | None = None + next_step: Callable[ + [dict[str, Any]], Coroutine[Any, Any, str | None] + ] | str | None = None """Optional property to identify next step. - If `next_step` is a function, it is called if the schema validates successfully or @@ -65,7 +67,7 @@ class SchemaFlowFormStep(SchemaFlowStep): """ suggested_values: Callable[ - [SchemaCommonFlowHandler], dict[str, Any] + [SchemaCommonFlowHandler], Coroutine[Any, Any, dict[str, Any]] ] | None | UndefinedType = UNDEFINED """Optional property to populate suggested values. @@ -127,12 +129,12 @@ class SchemaCommonFlowHandler: return await self._async_form_step(step_id, user_input) return await self._async_menu_step(step_id, user_input) - def _get_schema(self, form_step: SchemaFlowFormStep) -> vol.Schema | None: + async def _get_schema(self, form_step: SchemaFlowFormStep) -> vol.Schema | None: if form_step.schema is None: return None if isinstance(form_step.schema, vol.Schema): return form_step.schema - return form_step.schema(self) + return await form_step.schema(self) async def _async_form_step( self, step_id: str, user_input: dict[str, Any] | None = None @@ -142,7 +144,7 @@ class SchemaCommonFlowHandler: if ( user_input is not None - and (data_schema := self._get_schema(form_step)) + and (data_schema := await self._get_schema(form_step)) and data_schema.schema and not self._handler.show_advanced_options ): @@ -160,35 +162,35 @@ class SchemaCommonFlowHandler: if user_input is not None and form_step.validate_user_input is not None: # Do extra validation of user input try: - user_input = form_step.validate_user_input(self, user_input) + user_input = await form_step.validate_user_input(self, user_input) except SchemaFlowError as exc: - return self._show_next_step(step_id, exc, user_input) + return await self._show_next_step(step_id, exc, user_input) if user_input is not None: # User input was validated successfully, update options self._options.update(user_input) if user_input is not None or form_step.schema is None: - return self._show_next_step_or_create_entry(form_step) + return await self._show_next_step_or_create_entry(form_step) - return self._show_next_step(step_id) + return await self._show_next_step(step_id) - def _show_next_step_or_create_entry( + async def _show_next_step_or_create_entry( self, form_step: SchemaFlowFormStep ) -> FlowResult: next_step_id_or_end_flow: str | None if callable(form_step.next_step): - next_step_id_or_end_flow = form_step.next_step(self._options) + next_step_id_or_end_flow = await form_step.next_step(self._options) else: next_step_id_or_end_flow = form_step.next_step if next_step_id_or_end_flow is None: # Flow done, create entry or update config entry options return self._handler.async_create_entry(data=self._options) - return self._show_next_step(next_step_id_or_end_flow) + return await self._show_next_step(next_step_id_or_end_flow) - def _show_next_step( + async def _show_next_step( self, next_step_id: str, error: SchemaFlowError | None = None, @@ -204,14 +206,14 @@ class SchemaCommonFlowHandler: form_step = cast(SchemaFlowFormStep, self._flow[next_step_id]) - if (data_schema := self._get_schema(form_step)) is None: - return self._show_next_step_or_create_entry(form_step) + if (data_schema := await self._get_schema(form_step)) is None: + return await self._show_next_step_or_create_entry(form_step) suggested_values: dict[str, Any] = {} if form_step.suggested_values is UNDEFINED: suggested_values = self._options elif form_step.suggested_values: - suggested_values = form_step.suggested_values(self) + suggested_values = await form_step.suggested_values(self) if user_input: # We don't want to mutate the existing options diff --git a/tests/helpers/test_schema_config_entry_flow.py b/tests/helpers/test_schema_config_entry_flow.py index 29d01b2d2b6..b01da8d3d2a 100644 --- a/tests/helpers/test_schema_config_entry_flow.py +++ b/tests/helpers/test_schema_config_entry_flow.py @@ -303,9 +303,12 @@ async def test_menu_step(hass: HomeAssistant) -> None: MENU_1 = ["option1", "option2"] MENU_2 = ["option3", "option4"] + async def _option1_next_step(_: dict[str, Any]) -> str: + return "menu2" + CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { "user": SchemaFlowMenuStep(MENU_1), - "option1": SchemaFlowFormStep(vol.Schema({}), next_step=lambda _: "menu2"), + "option1": SchemaFlowFormStep(vol.Schema({}), next_step=_option1_next_step), "menu2": SchemaFlowMenuStep(MENU_2), "option3": SchemaFlowFormStep(vol.Schema({}), next_step="option4"), "option4": SchemaFlowFormStep(vol.Schema({})), @@ -384,10 +387,13 @@ async def test_schema_none(hass: HomeAssistant) -> None: async def test_last_step(hass: HomeAssistant) -> None: """Test SchemaFlowFormStep with schema set to None.""" + async def _step2_next_step(_: dict[str, Any]) -> str: + return "step3" + CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { "user": SchemaFlowFormStep(next_step="step1"), "step1": SchemaFlowFormStep(vol.Schema({}), next_step="step2"), - "step2": SchemaFlowFormStep(vol.Schema({}), next_step=lambda _: "step3"), + "step2": SchemaFlowFormStep(vol.Schema({}), next_step=_step2_next_step), "step3": SchemaFlowFormStep(vol.Schema({}), next_step=None), } @@ -422,10 +428,16 @@ async def test_last_step(hass: HomeAssistant) -> None: async def test_next_step_function(hass: HomeAssistant) -> None: """Test SchemaFlowFormStep with a next_step function.""" + async def _step1_next_step(_: dict[str, Any]) -> str: + return "step2" + + async def _step2_next_step(_: dict[str, Any]) -> None: + return None + CONFIG_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { "user": SchemaFlowFormStep(next_step="step1"), - "step1": SchemaFlowFormStep(vol.Schema({}), next_step=lambda _: "step2"), - "step2": SchemaFlowFormStep(vol.Schema({}), next_step=lambda _: None), + "step1": SchemaFlowFormStep(vol.Schema({}), next_step=_step1_next_step), + "step2": SchemaFlowFormStep(vol.Schema({}), next_step=_step2_next_step), } class TestConfigFlow(SchemaConfigFlowHandler, domain=TEST_DOMAIN): @@ -459,19 +471,22 @@ async def test_suggested_values( {vol.Optional("option1", default="a very reasonable default"): str} ) - def _validate_user_input( + async def _validate_user_input( handler: SchemaCommonFlowHandler, user_input: dict[str, Any] ) -> dict[str, Any]: if user_input["option1"] == "not a valid value": raise SchemaFlowError("option1 not using a valid value") return user_input + async def _step_2_suggested_values(_: SchemaCommonFlowHandler) -> dict[str, Any]: + return {"option1": "a random override"} + OPTIONS_FLOW: dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { "init": SchemaFlowFormStep(OPTIONS_SCHEMA, next_step="step_1"), "step_1": SchemaFlowFormStep(OPTIONS_SCHEMA, next_step="step_2"), "step_2": SchemaFlowFormStep( OPTIONS_SCHEMA, - suggested_values=lambda _: {"option1": "a random override"}, + suggested_values=_step_2_suggested_values, next_step="step_3", ), "step_3": SchemaFlowFormStep( @@ -565,16 +580,16 @@ async def test_options_flow_state(hass: HomeAssistant) -> None: {vol.Optional("option1", default="a very reasonable default"): str} ) - def _init_schema(handler: SchemaCommonFlowHandler) -> None: + async def _init_schema(handler: SchemaCommonFlowHandler) -> None: handler.flow_state["idx"] = None - def _validate_step1_input( + async def _validate_step1_input( handler: SchemaCommonFlowHandler, user_input: dict[str, Any] ) -> dict[str, Any]: handler.flow_state["idx"] = user_input["option1"] return user_input - def _validate_step2_input( + async def _validate_step2_input( handler: SchemaCommonFlowHandler, user_input: dict[str, Any] ) -> dict[str, Any]: user_input["idx_from_flow_state"] = handler.flow_state["idx"] From 4167edc52d92577d9bceb800d6699167a1a8b4c8 Mon Sep 17 00:00:00 2001 From: Christopher McCurdy Date: Wed, 30 Nov 2022 06:53:49 -0500 Subject: [PATCH 0908/1033] Create a UUID from given LG soundbar device name (#81918) Co-authored-by: Martin Hjelmare Fixes https://github.com/home-assistant/core/issues/77524 fixes undefined --- .coveragerc | 1 + .../components/lg_soundbar/config_flow.py | 64 ++-- .../components/lg_soundbar/media_player.py | 4 +- .../components/lg_soundbar/strings.json | 3 +- .../lg_soundbar/translations/en.json | 5 +- .../lg_soundbar/test_config_flow.py | 354 +++++++++++++----- 6 files changed, 320 insertions(+), 111 deletions(-) diff --git a/.coveragerc b/.coveragerc index b2b23757014..8582f2daed8 100644 --- a/.coveragerc +++ b/.coveragerc @@ -672,6 +672,7 @@ omit = homeassistant/components/led_ble/__init__.py homeassistant/components/led_ble/light.py homeassistant/components/lg_netcast/media_player.py + homeassistant/components/lg_soundbar/__init__.py homeassistant/components/lg_soundbar/media_player.py homeassistant/components/lidarr/__init__.py homeassistant/components/lidarr/coordinator.py diff --git a/homeassistant/components/lg_soundbar/config_flow.py b/homeassistant/components/lg_soundbar/config_flow.py index 0606bad2d67..d2cb1749689 100644 --- a/homeassistant/components/lg_soundbar/config_flow.py +++ b/homeassistant/components/lg_soundbar/config_flow.py @@ -1,5 +1,6 @@ """Config flow to configure the LG Soundbar integration.""" -from queue import Full, Queue +import logging +from queue import Empty, Full, Queue import socket import temescal @@ -7,6 +8,7 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_HOST, CONF_PORT +from homeassistant.data_entry_flow import FlowResult from .const import DEFAULT_PORT, DOMAIN @@ -14,50 +16,64 @@ DATA_SCHEMA = { vol.Required(CONF_HOST): str, } +_LOGGER = logging.getLogger(__name__) + +QUEUE_TIMEOUT = 10 + def test_connect(host, port): """LG Soundbar config flow test_connect.""" uuid_q = Queue(maxsize=1) name_q = Queue(maxsize=1) + def check_msg_response(response, msgs, attr): + msg = response["msg"] + if msg == msgs or msg in msgs: + if "data" in response and attr in response["data"]: + return True + _LOGGER.debug( + "[%s] msg did not contain expected attr [%s]: %s", msg, attr, response + ) + return False + def queue_add(attr_q, data): try: attr_q.put_nowait(data) except Full: - pass + _LOGGER.debug("attempted to add [%s] to full queue", data) def msg_callback(response): - if ( - response["msg"] in ["MAC_INFO_DEV", "PRODUCT_INFO"] - and "s_uuid" in response["data"] - ): + if check_msg_response(response, ["MAC_INFO_DEV", "PRODUCT_INFO"], "s_uuid"): queue_add(uuid_q, response["data"]["s_uuid"]) - if ( - response["msg"] == "SPK_LIST_VIEW_INFO" - and "s_user_name" in response["data"] - ): + if check_msg_response(response, "SPK_LIST_VIEW_INFO", "s_user_name"): queue_add(name_q, response["data"]["s_user_name"]) + details = {} + try: connection = temescal.temescal(host, port=port, callback=msg_callback) + connection.get_info() connection.get_mac_info() if uuid_q.empty(): connection.get_product_info() - connection.get_info() - details = {"name": name_q.get(timeout=10), "uuid": uuid_q.get(timeout=10)} - return details + details["name"] = name_q.get(timeout=QUEUE_TIMEOUT) + details["uuid"] = uuid_q.get(timeout=QUEUE_TIMEOUT) + except Empty: + pass except socket.timeout as err: raise ConnectionError(f"Connection timeout with server: {host}:{port}") from err except OSError as err: raise ConnectionError(f"Cannot resolve hostname: {host}") from err + return details + class LGSoundbarConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """LG Soundbar config flow.""" VERSION = 1 - async def async_step_user(self, user_input=None): + async def async_step_user(self, user_input=None) -> FlowResult: """Handle a flow initiated by the user.""" if user_input is None: return self._show_form() @@ -70,13 +86,19 @@ class LGSoundbarConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): except ConnectionError: errors["base"] = "cannot_connect" else: - await self.async_set_unique_id(details["uuid"]) - self._abort_if_unique_id_configured() - info = { - CONF_HOST: user_input[CONF_HOST], - CONF_PORT: DEFAULT_PORT, - } - return self.async_create_entry(title=details["name"], data=info) + if len(details) != 0: + info = { + CONF_HOST: user_input[CONF_HOST], + CONF_PORT: DEFAULT_PORT, + } + if "uuid" in details: + unique_id = details["uuid"] + await self.async_set_unique_id(unique_id) + self._abort_if_unique_id_configured() + else: + self._async_abort_entries_match(info) + return self.async_create_entry(title=details["name"], data=info) + errors["base"] = "no_data" return self._show_form(errors) diff --git a/homeassistant/components/lg_soundbar/media_player.py b/homeassistant/components/lg_soundbar/media_player.py index c4491a1d257..577b4a8811a 100644 --- a/homeassistant/components/lg_soundbar/media_player.py +++ b/homeassistant/components/lg_soundbar/media_player.py @@ -25,7 +25,7 @@ async def async_setup_entry( LGDevice( config_entry.data[CONF_HOST], config_entry.data[CONF_PORT], - config_entry.unique_id, + config_entry.unique_id or config_entry.entry_id, ) ] ) @@ -82,7 +82,7 @@ class LGDevice(MediaPlayerEntity): def handle_event(self, response): """Handle responses from the speakers.""" - data = response["data"] + data = response["data"] if "data" in response else {} if response["msg"] == "EQ_VIEW_INFO": if "i_bass" in data: self._bass = data["i_bass"] diff --git a/homeassistant/components/lg_soundbar/strings.json b/homeassistant/components/lg_soundbar/strings.json index 52d57eda809..8c6a9909ff5 100644 --- a/homeassistant/components/lg_soundbar/strings.json +++ b/homeassistant/components/lg_soundbar/strings.json @@ -8,7 +8,8 @@ } }, "error": { - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "no_data": "Device did not return any data required to an entry." }, "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" diff --git a/homeassistant/components/lg_soundbar/translations/en.json b/homeassistant/components/lg_soundbar/translations/en.json index 10441d21536..19f5027d470 100644 --- a/homeassistant/components/lg_soundbar/translations/en.json +++ b/homeassistant/components/lg_soundbar/translations/en.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Device is already configured" + "already_configured": "Device is already configured", + "no_uuid": "Device missing unique identification required for discovery." }, "error": { "cannot_connect": "Failed to connect" @@ -14,4 +15,4 @@ } } } -} \ No newline at end of file +} diff --git a/tests/components/lg_soundbar/test_config_flow.py b/tests/components/lg_soundbar/test_config_flow.py index 8bcf817cbba..2ac1912723b 100644 --- a/tests/components/lg_soundbar/test_config_flow.py +++ b/tests/components/lg_soundbar/test_config_flow.py @@ -1,5 +1,10 @@ """Test the lg_soundbar config flow.""" -from unittest.mock import DEFAULT, MagicMock, Mock, call, patch +from __future__ import annotations + +from collections.abc import Callable +import socket +from typing import Any +from unittest.mock import DEFAULT, patch from homeassistant import config_entries from homeassistant.components.lg_soundbar.const import DEFAULT_PORT, DOMAIN @@ -8,6 +13,43 @@ from homeassistant.const import CONF_HOST, CONF_PORT from tests.common import MockConfigEntry +def setup_mock_temescal( + hass, mock_temescal, mac_info_dev=None, product_info=None, info=None +): + """Set up a mock of the temescal object to craft our expected responses.""" + tmock = mock_temescal.temescal + instance = tmock.return_value + + def create_temescal_response(msg: str, data: dict | None = None) -> dict[str, Any]: + response: dict[str, Any] = {"msg": msg} + if data is not None: + response["data"] = data + return response + + def temescal_side_effect( + addr: str, port: int, callback: Callable[[dict[str, Any]], None] + ): + mac_info_response = create_temescal_response( + msg="MAC_INFO_DEV", data=mac_info_dev + ) + product_info_response = create_temescal_response( + msg="PRODUCT_INFO", data=product_info + ) + info_response = create_temescal_response(msg="SPK_LIST_VIEW_INFO", data=info) + + instance.get_mac_info.side_effect = lambda: hass.add_job( + callback, mac_info_response + ) + instance.get_product_info.side_effect = lambda: hass.add_job( + callback, product_info_response + ) + instance.get_info.side_effect = lambda: hass.add_job(callback, info_response) + + return DEFAULT + + tmock.side_effect = temescal_side_effect + + async def test_form(hass): """Test we get the form.""" @@ -18,14 +60,16 @@ async def test_form(hass): assert result["errors"] == {} with patch( - "homeassistant.components.lg_soundbar.config_flow.temescal", - return_value=MagicMock(), - ), patch( - "homeassistant.components.lg_soundbar.config_flow.test_connect", - return_value={"uuid": "uuid", "name": "name"}, - ), patch( + "homeassistant.components.lg_soundbar.config_flow.temescal" + ) as mock_temescal, patch( "homeassistant.components.lg_soundbar.async_setup_entry", return_value=True ) as mock_setup_entry: + setup_mock_temescal( + hass=hass, + mock_temescal=mock_temescal, + mac_info_dev={"s_uuid": "uuid"}, + info={"s_user_name": "name"}, + ) result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { @@ -36,6 +80,7 @@ async def test_form(hass): assert result2["type"] == "create_entry" assert result2["title"] == "name" + assert result2["result"].unique_id == "uuid" assert result2["data"] == { CONF_HOST: "1.1.1.1", CONF_PORT: DEFAULT_PORT, @@ -43,8 +88,8 @@ async def test_form(hass): assert len(mock_setup_entry.mock_calls) == 1 -async def test_form_uuid_missing_from_mac_info(hass): - """Test we get the form, but uuid is missing from the initial get_mac_info function call.""" +async def test_form_mac_info_response_empty(hass): + """Test we get the form, but response from the initial get_mac_info function call is empty.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -53,23 +98,16 @@ async def test_form_uuid_missing_from_mac_info(hass): assert result["errors"] == {} with patch( - "homeassistant.components.lg_soundbar.config_flow.temescal", return_value=Mock() + "homeassistant.components.lg_soundbar.config_flow.temescal" ) as mock_temescal, patch( "homeassistant.components.lg_soundbar.async_setup_entry", return_value=True ) as mock_setup_entry: - tmock = mock_temescal.temescal - tmock.return_value = Mock() - instance = tmock.return_value - - def temescal_side_effect(addr, port, callback): - product_info = {"msg": "PRODUCT_INFO", "data": {"s_uuid": "uuid"}} - instance.get_product_info.side_effect = lambda: callback(product_info) - info = {"msg": "SPK_LIST_VIEW_INFO", "data": {"s_user_name": "name"}} - instance.get_info.side_effect = lambda: callback(info) - return DEFAULT - - tmock.side_effect = temescal_side_effect - + setup_mock_temescal( + hass=hass, + mock_temescal=mock_temescal, + mac_info_dev={"s_uuid": "uuid"}, + info={"s_user_name": "name"}, + ) result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { @@ -80,6 +118,7 @@ async def test_form_uuid_missing_from_mac_info(hass): assert result2["type"] == "create_entry" assert result2["title"] == "name" + assert result2["result"].unique_id == "uuid" assert result2["data"] == { CONF_HOST: "1.1.1.1", CONF_PORT: DEFAULT_PORT, @@ -99,35 +138,18 @@ async def test_form_uuid_present_in_both_functions_uuid_q_empty(hass): assert result["type"] == "form" assert result["errors"] == {} - mock_uuid_q = MagicMock() - mock_name_q = MagicMock() - with patch( - "homeassistant.components.lg_soundbar.config_flow.temescal", return_value=Mock() + "homeassistant.components.lg_soundbar.config_flow.temescal" ) as mock_temescal, patch( - "homeassistant.components.lg_soundbar.config_flow.Queue", - return_value=MagicMock(), - ) as mock_q, patch( "homeassistant.components.lg_soundbar.async_setup_entry", return_value=True ) as mock_setup_entry: - mock_q.side_effect = [mock_uuid_q, mock_name_q] - mock_uuid_q.empty.return_value = True - mock_uuid_q.get.return_value = "uuid" - mock_name_q.get.return_value = "name" - tmock = mock_temescal.temescal - tmock.return_value = Mock() - instance = tmock.return_value - - def temescal_side_effect(addr, port, callback): - mac_info = {"msg": "MAC_INFO_DEV", "data": {"s_uuid": "uuid"}} - instance.get_mac_info.side_effect = lambda: callback(mac_info) - product_info = {"msg": "PRODUCT_INFO", "data": {"s_uuid": "uuid"}} - instance.get_product_info.side_effect = lambda: callback(product_info) - info = {"msg": "SPK_LIST_VIEW_INFO", "data": {"s_user_name": "name"}} - instance.get_info.side_effect = lambda: callback(info) - return DEFAULT - - tmock.side_effect = temescal_side_effect + setup_mock_temescal( + hass=hass, + mock_temescal=mock_temescal, + mac_info_dev={"s_uuid": "uuid"}, + product_info={"s_uuid": "uuid"}, + info={"s_user_name": "name"}, + ) result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -139,14 +161,12 @@ async def test_form_uuid_present_in_both_functions_uuid_q_empty(hass): assert result2["type"] == "create_entry" assert result2["title"] == "name" + assert result2["result"].unique_id == "uuid" assert result2["data"] == { CONF_HOST: "1.1.1.1", CONF_PORT: DEFAULT_PORT, } assert len(mock_setup_entry.mock_calls) == 1 - mock_uuid_q.empty.assert_called_once() - mock_uuid_q.put_nowait.has_calls([call("uuid"), call("uuid")]) - mock_uuid_q.get.assert_called_once() async def test_form_uuid_present_in_both_functions_uuid_q_not_empty(hass): @@ -161,33 +181,21 @@ async def test_form_uuid_present_in_both_functions_uuid_q_not_empty(hass): assert result["type"] == "form" assert result["errors"] == {} - mock_uuid_q = MagicMock() - mock_name_q = MagicMock() - with patch( - "homeassistant.components.lg_soundbar.config_flow.temescal", return_value=Mock() + "homeassistant.components.lg_soundbar.config_flow.QUEUE_TIMEOUT", + new=0.1, + ), patch( + "homeassistant.components.lg_soundbar.config_flow.temescal" ) as mock_temescal, patch( - "homeassistant.components.lg_soundbar.config_flow.Queue", - return_value=MagicMock(), - ) as mock_q, patch( "homeassistant.components.lg_soundbar.async_setup_entry", return_value=True ) as mock_setup_entry: - mock_q.side_effect = [mock_uuid_q, mock_name_q] - mock_uuid_q.empty.return_value = False - mock_uuid_q.get.return_value = "uuid" - mock_name_q.get.return_value = "name" - tmock = mock_temescal.temescal - tmock.return_value = Mock() - instance = tmock.return_value - - def temescal_side_effect(addr, port, callback): - mac_info = {"msg": "MAC_INFO_DEV", "data": {"s_uuid": "uuid"}} - instance.get_mac_info.side_effect = lambda: callback(mac_info) - info = {"msg": "SPK_LIST_VIEW_INFO", "data": {"s_user_name": "name"}} - instance.get_info.side_effect = lambda: callback(info) - return DEFAULT - - tmock.side_effect = temescal_side_effect + setup_mock_temescal( + hass=hass, + mock_temescal=mock_temescal, + mac_info_dev={"s_uuid": "uuid"}, + product_info={"s_uuid": "uuid"}, + info={"s_user_name": "name"}, + ) result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -199,26 +207,196 @@ async def test_form_uuid_present_in_both_functions_uuid_q_not_empty(hass): assert result2["type"] == "create_entry" assert result2["title"] == "name" + assert result2["result"].unique_id == "uuid" assert result2["data"] == { CONF_HOST: "1.1.1.1", CONF_PORT: DEFAULT_PORT, } assert len(mock_setup_entry.mock_calls) == 1 - mock_uuid_q.empty.assert_called_once() - mock_uuid_q.put_nowait.assert_called_once() - mock_uuid_q.get.assert_called_once() -async def test_form_cannot_connect(hass): - """Test we handle cannot connect error.""" +async def test_form_uuid_missing_from_mac_info(hass): + """Test we get the form, but uuid is missing from the initial get_mac_info function call.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + assert result["errors"] == {} + + with patch( + "homeassistant.components.lg_soundbar.config_flow.temescal" + ) as mock_temescal, patch( + "homeassistant.components.lg_soundbar.async_setup_entry", return_value=True + ) as mock_setup_entry: + setup_mock_temescal( + hass=hass, + mock_temescal=mock_temescal, + product_info={"s_uuid": "uuid"}, + info={"s_user_name": "name"}, + ) + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: "1.1.1.1", + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == "create_entry" + assert result2["title"] == "name" + assert result2["result"].unique_id == "uuid" + assert result2["data"] == { + CONF_HOST: "1.1.1.1", + CONF_PORT: DEFAULT_PORT, + } + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_form_uuid_not_provided_by_api(hass): + """Test we get the form, but uuid is missing from the all API messages.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + assert result["errors"] == {} + + with patch( + "homeassistant.components.lg_soundbar.config_flow.QUEUE_TIMEOUT", + new=0.1, + ), patch( + "homeassistant.components.lg_soundbar.config_flow.temescal" + ) as mock_temescal, patch( + "homeassistant.components.lg_soundbar.async_setup_entry", return_value=True + ) as mock_setup_entry: + setup_mock_temescal( + hass=hass, + mock_temescal=mock_temescal, + product_info={"i_model_no": "8", "i_model_type": 0}, + info={"s_user_name": "name"}, + ) + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: "1.1.1.1", + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == "create_entry" + assert result2["title"] == "name" + assert result2["result"].unique_id is None + assert result2["data"] == { + CONF_HOST: "1.1.1.1", + CONF_PORT: DEFAULT_PORT, + } + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_form_both_queues_empty(hass): + """Test we get the form, but none of the data we want is provided by the API.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + assert result["errors"] == {} + + with patch( + "homeassistant.components.lg_soundbar.config_flow.QUEUE_TIMEOUT", + new=0.1, + ), patch( + "homeassistant.components.lg_soundbar.config_flow.temescal" + ) as mock_temescal, patch( + "homeassistant.components.lg_soundbar.async_setup_entry", return_value=True + ) as mock_setup_entry: + setup_mock_temescal(hass=hass, mock_temescal=mock_temescal) + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: "1.1.1.1", + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == "form" + assert result2["errors"] == {"base": "no_data"} + assert len(mock_setup_entry.mock_calls) == 0 + + +async def test_no_uuid_host_already_configured(hass): + """Test we handle if the device has no UUID and the host has already been configured.""" + + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_HOST: "1.1.1.1", + CONF_PORT: DEFAULT_PORT, + }, + ) + mock_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == "form" + assert result["errors"] == {} + + with patch( + "homeassistant.components.lg_soundbar.config_flow.QUEUE_TIMEOUT", + new=0.1, + ), patch( + "homeassistant.components.lg_soundbar.config_flow.temescal" + ) as mock_temescal: + setup_mock_temescal( + hass=hass, mock_temescal=mock_temescal, info={"s_user_name": "name"} + ) + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: "1.1.1.1", + }, + ) + + assert result2["type"] == "abort" + assert result2["reason"] == "already_configured" + + +async def test_form_socket_timeout(hass): + """Test we handle socket.timeout error.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) with patch( - "homeassistant.components.lg_soundbar.config_flow.test_connect", - side_effect=ConnectionError, - ): + "homeassistant.components.lg_soundbar.config_flow.temescal" + ) as mock_temescal: + mock_temescal.temescal.side_effect = socket.timeout + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: "1.1.1.1", + }, + ) + + assert result2["type"] == "form" + assert result2["errors"] == {"base": "cannot_connect"} + + +async def test_form_os_error(hass): + """Test we handle OSError.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.lg_soundbar.config_flow.temescal" + ) as mock_temescal: + mock_temescal.temescal.side_effect = OSError result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { @@ -247,9 +425,15 @@ async def test_form_already_configured(hass): ) with patch( - "homeassistant.components.lg_soundbar.config_flow.test_connect", - return_value={"uuid": "uuid", "name": "name"}, - ): + "homeassistant.components.lg_soundbar.config_flow.temescal" + ) as mock_temescal: + setup_mock_temescal( + hass=hass, + mock_temescal=mock_temescal, + mac_info_dev={"s_uuid": "uuid"}, + info={"s_user_name": "name"}, + ) + result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { From f180f15bde61aeed3a26223e7084f0249384b288 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 30 Nov 2022 13:14:41 +0100 Subject: [PATCH 0909/1033] Cleanup removed mqtt fan speed config variables (#82978) --- .../components/mqtt/abbreviations.py | 8 ----- homeassistant/components/mqtt/fan.py | 31 ------------------- 2 files changed, 39 deletions(-) diff --git a/homeassistant/components/mqtt/abbreviations.py b/homeassistant/components/mqtt/abbreviations.py index 5fe0da90feb..913a6e13400 100644 --- a/homeassistant/components/mqtt/abbreviations.py +++ b/homeassistant/components/mqtt/abbreviations.py @@ -130,16 +130,12 @@ ABBREVIATIONS = { "pl_cln_sp": "payload_clean_spot", "pl_cls": "payload_close", "pl_disarm": "payload_disarm", - "pl_hi_spd": "payload_high_speed", "pl_home": "payload_home", "pl_lock": "payload_lock", "pl_loc": "payload_locate", - "pl_lo_spd": "payload_low_speed", - "pl_med_spd": "payload_medium_speed", "pl_not_avail": "payload_not_available", "pl_not_home": "payload_not_home", "pl_off": "payload_off", - "pl_off_spd": "payload_off_speed", "pl_on": "payload_on", "pl_open": "payload_open", "pl_osc_off": "payload_oscillation_off", @@ -193,12 +189,8 @@ ABBREVIATIONS = { "set_pos_t": "set_position_topic", "pos_t": "position_topic", "pos_tpl": "position_template", - "spd_cmd_t": "speed_command_topic", - "spd_stat_t": "speed_state_topic", "spd_rng_min": "speed_range_min", "spd_rng_max": "speed_range_max", - "spd_val_tpl": "speed_value_template", - "spds": "speeds", "src_type": "source_type", "stat_cla": "state_class", "stat_clsd": "state_closed", diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 2623845fffd..da061da14e4 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -77,20 +77,12 @@ CONF_PRESET_MODE_VALUE_TEMPLATE = "preset_mode_value_template" CONF_PRESET_MODE_COMMAND_TEMPLATE = "preset_mode_command_template" CONF_PRESET_MODES_LIST = "preset_modes" CONF_PAYLOAD_RESET_PRESET_MODE = "payload_reset_preset_mode" -CONF_SPEED_STATE_TOPIC = "speed_state_topic" -CONF_SPEED_COMMAND_TOPIC = "speed_command_topic" -CONF_SPEED_VALUE_TEMPLATE = "speed_value_template" CONF_OSCILLATION_STATE_TOPIC = "oscillation_state_topic" CONF_OSCILLATION_COMMAND_TOPIC = "oscillation_command_topic" CONF_OSCILLATION_VALUE_TEMPLATE = "oscillation_value_template" CONF_OSCILLATION_COMMAND_TEMPLATE = "oscillation_command_template" CONF_PAYLOAD_OSCILLATION_ON = "payload_oscillation_on" CONF_PAYLOAD_OSCILLATION_OFF = "payload_oscillation_off" -CONF_PAYLOAD_OFF_SPEED = "payload_off_speed" -CONF_PAYLOAD_LOW_SPEED = "payload_low_speed" -CONF_PAYLOAD_MEDIUM_SPEED = "payload_medium_speed" -CONF_PAYLOAD_HIGH_SPEED = "payload_high_speed" -CONF_SPEED_LIST = "speeds" DEFAULT_NAME = "MQTT Fan" DEFAULT_PAYLOAD_ON = "ON" @@ -176,9 +168,6 @@ _PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend( vol.Optional( CONF_PAYLOAD_OSCILLATION_ON, default=OSCILLATE_ON_PAYLOAD ): cv.string, - vol.Optional(CONF_SPEED_COMMAND_TOPIC): valid_publish_topic, - vol.Optional(CONF_SPEED_STATE_TOPIC): valid_subscribe_topic, - vol.Optional(CONF_SPEED_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_STATE_VALUE_TEMPLATE): cv.template, } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) @@ -190,32 +179,12 @@ PLATFORM_SCHEMA = vol.All( ) PLATFORM_SCHEMA_MODERN = vol.All( - # CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_LIST, CONF_SPEED_STATE_TOPIC, CONF_SPEED_VALUE_TEMPLATE and - # Speeds SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH SPEED_OFF, - # are no longer supported, support was removed in release 2021.12 - cv.removed(CONF_PAYLOAD_HIGH_SPEED), - cv.removed(CONF_PAYLOAD_LOW_SPEED), - cv.removed(CONF_PAYLOAD_MEDIUM_SPEED), - cv.removed(CONF_SPEED_COMMAND_TOPIC), - cv.removed(CONF_SPEED_LIST), - cv.removed(CONF_SPEED_STATE_TOPIC), - cv.removed(CONF_SPEED_VALUE_TEMPLATE), _PLATFORM_SCHEMA_BASE, valid_speed_range_configuration, valid_preset_mode_configuration, ) DISCOVERY_SCHEMA = vol.All( - # CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_LIST, CONF_SPEED_STATE_TOPIC, CONF_SPEED_VALUE_TEMPLATE and - # Speeds SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH SPEED_OFF, - # are no longer supported, support was removed in release 2021.12 - cv.removed(CONF_PAYLOAD_HIGH_SPEED), - cv.removed(CONF_PAYLOAD_LOW_SPEED), - cv.removed(CONF_PAYLOAD_MEDIUM_SPEED), - cv.removed(CONF_SPEED_COMMAND_TOPIC), - cv.removed(CONF_SPEED_LIST), - cv.removed(CONF_SPEED_STATE_TOPIC), - cv.removed(CONF_SPEED_VALUE_TEMPLATE), _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), valid_speed_range_configuration, valid_preset_mode_configuration, From cd2bb292ab88a638c8b8f4b04668e3c1ab27c926 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Wed, 30 Nov 2022 14:32:45 +0200 Subject: [PATCH 0910/1033] Address late review of Switcher button (#82981) --- homeassistant/components/switcher_kis/button.py | 3 +-- homeassistant/components/switcher_kis/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/switcher_kis/button.py b/homeassistant/components/switcher_kis/button.py index 3668f11f037..756acc1366e 100644 --- a/homeassistant/components/switcher_kis/button.py +++ b/homeassistant/components/switcher_kis/button.py @@ -16,7 +16,7 @@ from aioswitcher.device import DeviceCategory from homeassistant.components.button import ButtonEntity, ButtonEntityDescription from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import device_registry from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -93,7 +93,6 @@ async def async_setup_entry( ) -> None: """Set up Switcher button from config entry.""" - @callback async def async_add_buttons(coordinator: SwitcherDataUpdateCoordinator) -> None: """Get remote and add button from Switcher device.""" if coordinator.data.device_type.category == DeviceCategory.THERMOSTAT: diff --git a/homeassistant/components/switcher_kis/manifest.json b/homeassistant/components/switcher_kis/manifest.json index 0dafb840dfa..9206b08c197 100644 --- a/homeassistant/components/switcher_kis/manifest.json +++ b/homeassistant/components/switcher_kis/manifest.json @@ -3,7 +3,7 @@ "name": "Switcher", "documentation": "https://www.home-assistant.io/integrations/switcher_kis/", "codeowners": ["@tomerfi", "@thecode"], - "requirements": ["aioswitcher==3.2.0"], + "requirements": ["aioswitcher==3.2.1"], "quality_scale": "platinum", "iot_class": "local_push", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 5908a0eb94d..b074d8f6609 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -273,7 +273,7 @@ aioslimproto==2.1.1 aiosteamist==0.3.2 # homeassistant.components.switcher_kis -aioswitcher==3.2.0 +aioswitcher==3.2.1 # homeassistant.components.syncthing aiosyncthing==0.5.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 44582937de4..dc787936298 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -248,7 +248,7 @@ aioslimproto==2.1.1 aiosteamist==0.3.2 # homeassistant.components.switcher_kis -aioswitcher==3.2.0 +aioswitcher==3.2.1 # homeassistant.components.syncthing aiosyncthing==0.5.1 From 92fef0f2ba7078782b23431ddd7acfe2c5aee50c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 30 Nov 2022 14:05:01 +0100 Subject: [PATCH 0911/1033] Improve resource validation in scrape config flow (#82965) * Improve resource validation in scrape * Coverage --- homeassistant/components/scrape/config_flow.py | 5 ++++- tests/components/scrape/test_config_flow.py | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/scrape/config_flow.py b/homeassistant/components/scrape/config_flow.py index d28d2feb40d..f4b6420df66 100644 --- a/homeassistant/components/scrape/config_flow.py +++ b/homeassistant/components/scrape/config_flow.py @@ -123,9 +123,12 @@ async def validate_rest_setup( hass = async_get_hass() rest_config: dict[str, Any] = COMBINED_SCHEMA(user_input) try: - create_rest_data_from_config(hass, rest_config) + rest = create_rest_data_from_config(hass, rest_config) + await rest.async_update() except Exception as err: raise SchemaFlowError("resource_error") from err + if rest.data is None: + raise SchemaFlowError("resource_error") return user_input diff --git a/tests/components/scrape/test_config_flow.py b/tests/components/scrape/test_config_flow.py index 67a55f5a144..883d9ec4ab9 100644 --- a/tests/components/scrape/test_config_flow.py +++ b/tests/components/scrape/test_config_flow.py @@ -115,6 +115,22 @@ async def test_flow_fails(hass: HomeAssistant, get_data: MockRestData) -> None: assert result2["errors"] == {"base": "resource_error"} + with patch( + "homeassistant.components.rest.RestData", + return_value=MockRestData("test_scrape_sensor_no_data"), + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: "GET", + CONF_VERIFY_SSL: True, + CONF_TIMEOUT: 10.0, + }, + ) + + assert result2["errors"] == {"base": "resource_error"} + with patch("homeassistant.components.rest.RestData", return_value=get_data,), patch( "homeassistant.components.scrape.async_setup_entry", return_value=True, From cbf8a41eede0f66e12dde5c535a7b4c862c97f03 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 30 Nov 2022 14:16:28 +0100 Subject: [PATCH 0912/1033] Add ability to edit sensors in scrape config flow (#82926) * Add ability to edit sensors in scrape config flow * Fix docstring * Update homeassistant/components/scrape/config_flow.py Co-authored-by: Erik Montnemery * docstring Co-authored-by: Erik Montnemery --- .../components/scrape/config_flow.py | 67 ++++++++++++++++++- homeassistant/components/scrape/strings.json | 22 ++++++ .../components/scrape/translations/en.json | 35 +++++----- tests/components/scrape/test_config_flow.py | 66 ++++++++++++++++++ 4 files changed, 172 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/scrape/config_flow.py b/homeassistant/components/scrape/config_flow.py index f4b6420df66..cbd0ed7d525 100644 --- a/homeassistant/components/scrape/config_flow.py +++ b/homeassistant/components/scrape/config_flow.py @@ -87,7 +87,6 @@ RESOURCE_SETUP = { } SENSOR_SETUP = { - vol.Optional(CONF_NAME, default=DEFAULT_NAME): TextSelector(), vol.Required(CONF_SELECT): TextSelector(), vol.Optional(CONF_INDEX, default=0): NumberSelector( NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX) @@ -146,6 +145,49 @@ async def validate_sensor_setup( return {} +async def validate_select_sensor( + handler: SchemaCommonFlowHandler, user_input: dict[str, Any] +) -> dict[str, Any]: + """Store sensor index in flow state.""" + handler.flow_state["_idx"] = int(user_input[CONF_INDEX]) + return {} + + +async def get_select_sensor_schema(handler: SchemaCommonFlowHandler) -> vol.Schema: + """Return schema for selecting a sensor.""" + return vol.Schema( + { + vol.Required(CONF_INDEX): vol.In( + { + str(index): config[CONF_NAME] + for index, config in enumerate(handler.options[SENSOR_DOMAIN]) + }, + ) + } + ) + + +async def get_edit_sensor_suggested_values( + handler: SchemaCommonFlowHandler, +) -> dict[str, Any]: + """Return suggested values for sensor editing.""" + idx: int = handler.flow_state["_idx"] + return handler.options[SENSOR_DOMAIN][idx] + + +async def validate_sensor_edit( + handler: SchemaCommonFlowHandler, user_input: dict[str, Any] +) -> dict[str, Any]: + """Update edited sensor.""" + user_input[CONF_INDEX] = int(user_input[CONF_INDEX]) + + # Standard behavior is to merge the result with the options. + # In this case, we want to add a sub-item so we update the options directly. + idx: int = handler.flow_state["_idx"] + handler.options[SENSOR_DOMAIN][idx].update(user_input) + return {} + + async def get_remove_sensor_schema(handler: SchemaCommonFlowHandler) -> vol.Schema: """Return schema for sensor removal.""" return vol.Schema( @@ -183,7 +225,13 @@ async def validate_remove_sensor( DATA_SCHEMA_RESOURCE = vol.Schema(RESOURCE_SETUP) -DATA_SCHEMA_SENSOR = vol.Schema(SENSOR_SETUP) +DATA_SCHEMA_EDIT_SENSOR = vol.Schema(SENSOR_SETUP) +DATA_SCHEMA_SENSOR = vol.Schema( + { + vol.Optional(CONF_NAME, default=DEFAULT_NAME): TextSelector(), + **SENSOR_SETUP, + } +) CONFIG_FLOW = { "user": SchemaFlowFormStep( @@ -197,7 +245,9 @@ CONFIG_FLOW = { ), } OPTIONS_FLOW = { - "init": SchemaFlowMenuStep(["resource", "add_sensor", "remove_sensor"]), + "init": SchemaFlowMenuStep( + ["resource", "add_sensor", "select_edit_sensor", "remove_sensor"] + ), "resource": SchemaFlowFormStep( DATA_SCHEMA_RESOURCE, validate_user_input=validate_rest_setup, @@ -207,6 +257,17 @@ OPTIONS_FLOW = { suggested_values=None, validate_user_input=validate_sensor_setup, ), + "select_edit_sensor": SchemaFlowFormStep( + get_select_sensor_schema, + suggested_values=None, + validate_user_input=validate_select_sensor, + next_step="edit_sensor", + ), + "edit_sensor": SchemaFlowFormStep( + DATA_SCHEMA_EDIT_SENSOR, + suggested_values=get_edit_sensor_suggested_values, + validate_user_input=validate_sensor_edit, + ), "remove_sensor": SchemaFlowFormStep( get_remove_sensor_schema, suggested_values=None, diff --git a/homeassistant/components/scrape/strings.json b/homeassistant/components/scrape/strings.json index 1ac50c695c6..907aa2a9dfd 100644 --- a/homeassistant/components/scrape/strings.json +++ b/homeassistant/components/scrape/strings.json @@ -54,6 +54,7 @@ "init": { "menu_options": { "add_sensor": "Add sensor", + "select_edit_sensor": "Configure sensor", "remove_sensor": "Remove sensor", "resource": "Configure resource" } @@ -79,6 +80,27 @@ "unit_of_measurement": "[%key:component::scrape::config::step::sensor::data_description::unit_of_measurement%]" } }, + "edit_sensor": { + "data": { + "name": "[%key:component::scrape::config::step::sensor::data::name%]", + "attribute": "[%key:component::scrape::config::step::sensor::data::attribute%]", + "index": "[%key:component::scrape::config::step::sensor::data::index%]", + "select": "[%key:component::scrape::config::step::sensor::data::select%]", + "value_template": "[%key:component::scrape::config::step::sensor::data::value_template%]", + "device_class": "[%key:component::scrape::config::step::sensor::data::device_class%]", + "state_class": "[%key:component::scrape::config::step::sensor::data::state_class%]", + "unit_of_measurement": "[%key:component::scrape::config::step::sensor::data::unit_of_measurement%]" + }, + "data_description": { + "select": "[%key:component::scrape::config::step::sensor::data_description::select%]", + "attribute": "[%key:component::scrape::config::step::sensor::data_description::attribute%]", + "index": "[%key:component::scrape::config::step::sensor::data_description::index%]", + "value_template": "[%key:component::scrape::config::step::sensor::data_description::value_template%]", + "device_class": "[%key:component::scrape::config::step::sensor::data_description::device_class%]", + "state_class": "[%key:component::scrape::config::step::sensor::data_description::state_class%]", + "unit_of_measurement": "[%key:component::scrape::config::step::sensor::data_description::unit_of_measurement%]" + } + }, "resource": { "data": { "resource": "[%key:component::scrape::config::step::user::data::resource%]", diff --git a/homeassistant/components/scrape/translations/en.json b/homeassistant/components/scrape/translations/en.json index ce66908a75d..4b0e96da680 100644 --- a/homeassistant/components/scrape/translations/en.json +++ b/homeassistant/components/scrape/translations/en.json @@ -78,26 +78,31 @@ "value_template": "Defines a template to get the state of the sensor" } }, - "init": { + "edit_sensor": { "data": { - "authentication": "Select authentication method", - "headers": "Headers", - "method": "Method", - "password": "Password", - "resource": "Resource", - "timeout": "Timeout", - "username": "Username", - "verify_ssl": "Verify SSL certificate" + "attribute": "Attribute", + "device_class": "Device Class", + "index": "Index", + "name": "Name", + "select": "Select", + "state_class": "State Class", + "unit_of_measurement": "Unit of Measurement", + "value_template": "Value Template" }, "data_description": { - "authentication": "Type of the HTTP authentication. Either basic or digest", - "headers": "Headers to use for the web request", - "resource": "The URL to the website that contains the value", - "timeout": "Timeout for connection to website", - "verify_ssl": "Enables/disables verification of SSL/TLS certificate, for example if it is self-signed" - }, + "attribute": "Get value of an attribute on the selected tag", + "device_class": "The type/class of the sensor to set the icon in the frontend", + "index": "Defines which of the elements returned by the CSS selector to use", + "select": "Defines what tag to search for. Check Beautifulsoup CSS selectors for details", + "state_class": "The state_class of the sensor", + "unit_of_measurement": "Choose temperature measurement or create your own", + "value_template": "Defines a template to get the state of the sensor" + } + }, + "init": { "menu_options": { "add_sensor": "Add sensor", + "select_edit_sensor": "Configure sensor", "remove_sensor": "Remove sensor", "resource": "Configure resource" } diff --git a/tests/components/scrape/test_config_flow.py b/tests/components/scrape/test_config_flow.py index 883d9ec4ab9..f6df8a80f21 100644 --- a/tests/components/scrape/test_config_flow.py +++ b/tests/components/scrape/test_config_flow.py @@ -356,3 +356,69 @@ async def test_options_add_remove_sensor_flow( # Check the state of the new entity state = hass.states.get("sensor.template") assert state.state == "Trying to get" + + +async def test_options_edit_sensor_flow( + hass: HomeAssistant, loaded_entry: MockConfigEntry +) -> None: + """Test options flow to edit a sensor.""" + + state = hass.states.get("sensor.current_version") + assert state.state == "Current Version: 2021.12.10" + + result = await hass.config_entries.options.async_init(loaded_entry.entry_id) + + assert result["type"] == FlowResultType.MENU + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + {"next_step_id": "select_edit_sensor"}, + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "select_edit_sensor" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + {"index": "0"}, + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "edit_sensor" + + mocker = MockRestData("test_scrape_sensor2") + with patch("homeassistant.components.rest.RestData", return_value=mocker): + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_SELECT: "template", + CONF_INDEX: 0.0, + }, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["data"] == { + CONF_RESOURCE: "https://www.home-assistant.io", + CONF_METHOD: "GET", + CONF_VERIFY_SSL: True, + CONF_TIMEOUT: 10, + "sensor": [ + { + CONF_NAME: "Current version", + CONF_SELECT: "template", + CONF_INDEX: 0, + CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120002", + }, + ], + } + + await hass.async_block_till_done() + + # Check the entity was updated + assert len(hass.states.async_all()) == 1 + + # Check the state of the entity has changed as expected + state = hass.states.get("sensor.current_version") + assert state.state == "Trying to get" From 89a7d5fecc21e2c226cb32b8b525448fdd999110 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 30 Nov 2022 14:55:31 +0100 Subject: [PATCH 0913/1033] Update codeowners for mqtt integration (#82979) Add @jbouwh to codeowners --- CODEOWNERS | 4 ++-- homeassistant/components/mqtt/manifest.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 83651f4ddc0..9e1cd87ca80 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -721,8 +721,8 @@ build.json @home-assistant/supervisor /tests/components/motion_blinds/ @starkillerOG /homeassistant/components/motioneye/ @dermotduffy /tests/components/motioneye/ @dermotduffy -/homeassistant/components/mqtt/ @emontnemery -/tests/components/mqtt/ @emontnemery +/homeassistant/components/mqtt/ @emontnemery @jbouwh +/tests/components/mqtt/ @emontnemery @jbouwh /homeassistant/components/msteams/ @peroyvind /homeassistant/components/mullvad/ @meichthys /tests/components/mullvad/ @meichthys diff --git a/homeassistant/components/mqtt/manifest.json b/homeassistant/components/mqtt/manifest.json index fa6dc8f655b..f90d02b11f4 100644 --- a/homeassistant/components/mqtt/manifest.json +++ b/homeassistant/components/mqtt/manifest.json @@ -5,7 +5,7 @@ "documentation": "https://www.home-assistant.io/integrations/mqtt", "requirements": ["paho-mqtt==1.6.1"], "dependencies": ["file_upload", "http"], - "codeowners": ["@emontnemery"], + "codeowners": ["@emontnemery", "@jbouwh"], "iot_class": "local_push", "quality_scale": "gold" } From 5be961f07f8a2339047ace84f6f3a29b2b85d954 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 30 Nov 2022 14:59:26 +0100 Subject: [PATCH 0914/1033] Use SchemaOptionsFlowHandler in blink (#82903) * Use SchemaOptionsFlowHandler in blink * Use config entry update listener * Adjust tests * Simplify options init * Don't reload * Use selector --- homeassistant/components/blink/__init__.py | 13 +++- homeassistant/components/blink/config_flow.py | 67 +++++++------------ tests/components/blink/test_config_flow.py | 22 +++--- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/homeassistant/components/blink/__init__.py b/homeassistant/components/blink/__init__.py index 0c18950da66..ef5a99356bc 100644 --- a/homeassistant/components/blink/__init__.py +++ b/homeassistant/components/blink/__init__.py @@ -30,7 +30,7 @@ SERVICE_SAVE_VIDEO_SCHEMA = vol.Schema( SERVICE_SEND_PIN_SCHEMA = vol.Schema({vol.Optional(CONF_PIN): cv.string}) -def _blink_startup_wrapper(hass, entry): +def _blink_startup_wrapper(hass: HomeAssistant, entry: ConfigEntry) -> Blink: """Startup wrapper for blink.""" blink = Blink() auth_data = deepcopy(dict(entry.data)) @@ -87,6 +87,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryNotReady await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + entry.async_on_unload(entry.add_update_listener(update_listener)) def blink_refresh(event_time=None): """Call blink to refresh info.""" @@ -116,7 +117,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @callback -def _async_import_options_from_data_if_missing(hass, entry): +def _async_import_options_from_data_if_missing( + hass: HomeAssistant, entry: ConfigEntry +) -> None: options = dict(entry.options) if CONF_SCAN_INTERVAL not in entry.options: options[CONF_SCAN_INTERVAL] = entry.data.get( @@ -144,6 +147,12 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True +async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Handle options update.""" + blink: Blink = hass.data[DOMAIN][entry.entry_id] + blink.refresh_rate = entry.options[CONF_SCAN_INTERVAL] + + async def async_handle_save_video_service(hass, entry, call): """Handle save video service calls.""" camera_name = call.data[CONF_NAME] diff --git a/homeassistant/components/blink/config_flow.py b/homeassistant/components/blink/config_flow.py index 4f1c1997cad..30ef294f515 100644 --- a/homeassistant/components/blink/config_flow.py +++ b/homeassistant/components/blink/config_flow.py @@ -18,11 +18,35 @@ from homeassistant.const import ( ) from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import selector +from homeassistant.helpers.schema_config_entry_flow import ( + SchemaFlowFormStep, + SchemaOptionsFlowHandler, +) from .const import DEFAULT_SCAN_INTERVAL, DEVICE_ID, DOMAIN _LOGGER = logging.getLogger(__name__) +SIMPLE_OPTIONS_SCHEMA = vol.Schema( + { + vol.Optional( + CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL + ): selector.NumberSelector( + selector.NumberSelectorConfig( + mode=selector.NumberSelectorMode.BOX, + unit_of_measurement="seconds", + ), + ), + } +) + + +OPTIONS_FLOW = { + "init": SchemaFlowFormStep(next_step="simple_options"), + "simple_options": SchemaFlowFormStep(SIMPLE_OPTIONS_SCHEMA), +} + def validate_input(hass: core.HomeAssistant, auth): """Validate the user input allows us to connect.""" @@ -56,9 +80,9 @@ class BlinkConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @callback def async_get_options_flow( config_entry: config_entries.ConfigEntry, - ) -> BlinkOptionsFlowHandler: + ) -> SchemaOptionsFlowHandler: """Get options flow for this handler.""" - return BlinkOptionsFlowHandler(config_entry) + return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW) async def async_step_user(self, user_input=None): """Handle a flow initiated by the user.""" @@ -133,45 +157,6 @@ class BlinkConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_create_entry(title=DOMAIN, data=self.auth.login_attributes) -class BlinkOptionsFlowHandler(config_entries.OptionsFlow): - """Handle Blink options.""" - - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: - """Initialize Blink options flow.""" - self.config_entry = config_entry - self.options = dict(config_entry.options) - self.blink = None - - async def async_step_init(self, user_input=None): - """Manage the Blink options.""" - self.blink = self.hass.data[DOMAIN][self.config_entry.entry_id] - self.options[CONF_SCAN_INTERVAL] = self.blink.refresh_rate - - return await self.async_step_simple_options() - - async def async_step_simple_options(self, user_input=None): - """For simple options.""" - if user_input is not None: - self.options.update(user_input) - self.blink.refresh_rate = user_input[CONF_SCAN_INTERVAL] - return self.async_create_entry(title="", data=self.options) - - options = self.config_entry.options - scan_interval = options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) - - return self.async_show_form( - step_id="simple_options", - data_schema=vol.Schema( - { - vol.Optional( - CONF_SCAN_INTERVAL, - default=scan_interval, - ): int - } - ), - ) - - class Require2FA(exceptions.HomeAssistantError): """Error to indicate we require 2FA.""" diff --git a/tests/components/blink/test_config_flow.py b/tests/components/blink/test_config_flow.py index 999204a2d91..9d5d92f8dd2 100644 --- a/tests/components/blink/test_config_flow.py +++ b/tests/components/blink/test_config_flow.py @@ -6,6 +6,7 @@ from blinkpy.blinkpy import BlinkSetupError from homeassistant import config_entries, data_entry_flow from homeassistant.components.blink import DOMAIN +from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry @@ -254,7 +255,7 @@ async def test_reauth_shows_user_step(hass): assert result["step_id"] == "user" -async def test_options_flow(hass): +async def test_options_flow(hass: HomeAssistant) -> None: """Test config flow options.""" config_entry = MockConfigEntry( domain=DOMAIN, @@ -283,11 +284,16 @@ async def test_options_flow(hass): assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "simple_options" - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={"scan_interval": 5}, - ) + with patch("homeassistant.components.blink.Auth", return_value=mock_auth), patch( + "homeassistant.components.blink.Blink", return_value=mock_blink + ): + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={"scan_interval": 5}, + ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["data"] == {"scan_interval": 5} - assert mock_blink.refresh_rate == 5 + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"] == {"scan_interval": 5} + await hass.async_block_till_done() + + assert mock_blink.refresh_rate == 5 From 5599ab6e4a10a070de76da6a1a5dd360b31a492c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 30 Nov 2022 15:02:21 +0100 Subject: [PATCH 0915/1033] Use OptionsFlowWithConfigEntry in bmw connected drive (#82904) --- .../components/bmw_connected_drive/config_flow.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/homeassistant/components/bmw_connected_drive/config_flow.py b/homeassistant/components/bmw_connected_drive/config_flow.py index 3994b0732a8..4f05794e311 100644 --- a/homeassistant/components/bmw_connected_drive/config_flow.py +++ b/homeassistant/components/bmw_connected_drive/config_flow.py @@ -94,14 +94,9 @@ class BMWConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return BMWOptionsFlow(config_entry) -class BMWOptionsFlow(config_entries.OptionsFlow): +class BMWOptionsFlow(config_entries.OptionsFlowWithConfigEntry): """Handle a option flow for MyBMW.""" - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: - """Initialize MyBMW option flow.""" - self.config_entry = config_entry - self.options = dict(config_entry.options) - async def async_step_init( self, user_input: dict[str, Any] | None = None ) -> FlowResult: From a12568440da8843166c18765792fe454df11b782 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 30 Nov 2022 16:04:37 +0100 Subject: [PATCH 0916/1033] Adjust precipitation unit in tomorrowio (#82982) Adjust precipitation_unit in tomorrowio --- homeassistant/components/tomorrowio/weather.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/tomorrowio/weather.py b/homeassistant/components/tomorrowio/weather.py index 1acc42e900b..d92ac401f92 100644 --- a/homeassistant/components/tomorrowio/weather.py +++ b/homeassistant/components/tomorrowio/weather.py @@ -22,6 +22,7 @@ from homeassistant.const import ( CONF_API_KEY, CONF_NAME, UnitOfLength, + UnitOfPrecipitationDepth, UnitOfPressure, UnitOfSpeed, UnitOfTemperature, @@ -73,7 +74,7 @@ async def async_setup_entry( class TomorrowioWeatherEntity(TomorrowioEntity, WeatherEntity): """Entity that talks to Tomorrow.io v4 API to retrieve weather data.""" - _attr_native_precipitation_unit = UnitOfLength.MILLIMETERS + _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS _attr_native_pressure_unit = UnitOfPressure.HPA _attr_native_temperature_unit = UnitOfTemperature.CELSIUS _attr_native_visibility_unit = UnitOfLength.KILOMETERS From dffdc78915ad9d25f54be90ef62659b2c68de347 Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Wed, 30 Nov 2022 16:13:11 +0100 Subject: [PATCH 0917/1033] Make HERETravelTimeSensor extend RestoreSensor (#82400) --- .../components/here_travel_time/sensor.py | 21 ++- .../here_travel_time/test_sensor.py | 145 +++++++++++++++++- 2 files changed, 157 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/here_travel_time/sensor.py b/homeassistant/components/here_travel_time/sensor.py index e69d1efe4a4..18433a86560 100644 --- a/homeassistant/components/here_travel_time/sensor.py +++ b/homeassistant/components/here_travel_time/sensor.py @@ -6,8 +6,8 @@ from datetime import timedelta from typing import Any from homeassistant.components.sensor import ( + RestoreSensor, SensorDeviceClass, - SensorEntity, SensorEntityDescription, SensorStateClass, ) @@ -99,7 +99,7 @@ async def async_setup_entry( async_add_entities(sensors) -class HERETravelTimeSensor(SensorEntity, CoordinatorEntity): +class HERETravelTimeSensor(RestoreSensor, CoordinatorEntity): """Representation of a HERE travel time sensor.""" def __init__( @@ -121,8 +121,14 @@ class HERETravelTimeSensor(SensorEntity, CoordinatorEntity): ) self._attr_has_entity_name = True + async def _async_restore_state(self) -> None: + """Restore state.""" + if restored_data := await self.async_get_last_sensor_data(): + self._attr_native_value = restored_data.native_value + async def async_added_to_hass(self) -> None: """Wait for start so origin and destination entities can be resolved.""" + await self._async_restore_state() await super().async_added_to_hass() async def _update_at_start(_): @@ -130,12 +136,13 @@ class HERETravelTimeSensor(SensorEntity, CoordinatorEntity): self.async_on_remove(async_at_start(self.hass, _update_at_start)) - @property - def native_value(self) -> str | float | None: - """Return the state of the sensor.""" + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" if self.coordinator.data is not None: - return self.coordinator.data.get(self.entity_description.key) - return None + self._attr_native_value = self.coordinator.data.get( + self.entity_description.key + ) + self.async_write_ha_state() @property def attribution(self) -> str | None: diff --git a/tests/components/here_travel_time/test_sensor.py b/tests/components/here_travel_time/test_sensor.py index 435d24d7b1d..0ad86a04992 100644 --- a/tests/components/here_travel_time/test_sensor.py +++ b/tests/components/here_travel_time/test_sensor.py @@ -34,29 +34,37 @@ from homeassistant.components.here_travel_time.const import ( TRAVEL_MODE_PUBLIC, TRAVEL_MODE_TRUCK, ) +from homeassistant.components.sensor import ( + ATTR_LAST_RESET, + ATTR_STATE_CLASS, + SensorStateClass, +) from homeassistant.const import ( ATTR_ATTRIBUTION, ATTR_ICON, ATTR_LATITUDE, ATTR_LONGITUDE, + ATTR_UNIT_OF_MEASUREMENT, CONF_API_KEY, CONF_MODE, CONF_NAME, EVENT_HOMEASSISTANT_START, TIME_MINUTES, + UnitOfLength, ) -from homeassistant.core import HomeAssistant +from homeassistant.core import CoreState, HomeAssistant, State from homeassistant.setup import async_setup_component from .const import ( API_KEY, + DEFAULT_CONFIG, DESTINATION_LATITUDE, DESTINATION_LONGITUDE, ORIGIN_LATITUDE, ORIGIN_LONGITUDE, ) -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, mock_restore_cache_with_extra_data @pytest.mark.parametrize( @@ -440,3 +448,136 @@ async def test_route_not_found(hass: HomeAssistant, caplog): await hass.async_block_till_done() assert "Route calculation failed: Couldn't find a route." in caplog.text + + +@pytest.mark.usefixtures("valid_response") +async def test_restore_state(hass): + """Test sensor restore state.""" + # Home assistant is not running yet + hass.state = CoreState.not_running + last_reset = "2022-11-29T00:00:00.000000+00:00" + mock_restore_cache_with_extra_data( + hass, + [ + ( + State( + "sensor.test_duration", + "1234", + attributes={ + ATTR_LAST_RESET: last_reset, + ATTR_UNIT_OF_MEASUREMENT: TIME_MINUTES, + ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, + }, + ), + { + "native_value": 1234, + "native_unit_of_measurement": TIME_MINUTES, + "icon": "mdi:car", + "last_reset": last_reset, + }, + ), + ( + State( + "sensor.test_duration_in_traffic", + "5678", + attributes={ + ATTR_LAST_RESET: last_reset, + ATTR_UNIT_OF_MEASUREMENT: TIME_MINUTES, + ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, + }, + ), + { + "native_value": 5678, + "native_unit_of_measurement": TIME_MINUTES, + "icon": "mdi:car", + "last_reset": last_reset, + }, + ), + ( + State( + "sensor.test_distance", + "123", + attributes={ + ATTR_LAST_RESET: last_reset, + ATTR_UNIT_OF_MEASUREMENT: UnitOfLength.KILOMETERS, + ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, + }, + ), + { + "native_value": 123, + "native_unit_of_measurement": UnitOfLength.KILOMETERS, + "icon": "mdi:car", + "last_reset": last_reset, + }, + ), + ( + State( + "sensor.test_origin", + "Origin Address 1", + attributes={ + ATTR_LAST_RESET: last_reset, + ATTR_LATITUDE: ORIGIN_LATITUDE, + ATTR_LONGITUDE: ORIGIN_LONGITUDE, + }, + ), + { + "native_value": "Origin Address 1", + "native_unit_of_measurement": None, + ATTR_LATITUDE: ORIGIN_LATITUDE, + ATTR_LONGITUDE: ORIGIN_LONGITUDE, + "icon": "mdi:store-marker", + "last_reset": last_reset, + }, + ), + ( + State( + "sensor.test_destination", + "Destination Address 1", + attributes={ + ATTR_LAST_RESET: last_reset, + ATTR_LATITUDE: DESTINATION_LATITUDE, + ATTR_LONGITUDE: DESTINATION_LONGITUDE, + }, + ), + { + "native_value": "Destination Address 1", + "native_unit_of_measurement": None, + "icon": "mdi:store-marker", + "last_reset": last_reset, + }, + ), + ], + ) + + # create and add entry + mock_entry = MockConfigEntry( + domain=DOMAIN, unique_id=DOMAIN, data=DEFAULT_CONFIG, options=DEFAULT_OPTIONS + ) + mock_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + + print(hass.states.async_all()) + + # restore from cache + state = hass.states.get("sensor.test_duration") + assert state.state == "1234" + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TIME_MINUTES + assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT + + state = hass.states.get("sensor.test_duration_in_traffic") + assert state.state == "5678" + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TIME_MINUTES + assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT + + state = hass.states.get("sensor.test_distance") + assert state.state == "123" + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfLength.KILOMETERS + assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT + + state = hass.states.get("sensor.test_origin") + assert state.state == "Origin Address 1" + + state = hass.states.get("sensor.test_destination") + assert state.state == "Destination Address 1" From cad04bff99cb82ec6163a94e0035d206527520bf Mon Sep 17 00:00:00 2001 From: Nyro Date: Wed, 30 Nov 2022 16:32:07 +0100 Subject: [PATCH 0918/1033] Use same Overkiz (Atlantic) climate entity for 2 different ui widget name (#82955) * Use same overkiz entity for 2 different ui widget name * Update homeassistant/components/overkiz/climate_entities/__init__.py Co-authored-by: Mick Vleeshouwer Co-authored-by: Mick Vleeshouwer --- .../components/overkiz/climate_entities/__init__.py | 8 ++++---- ..._cooling_zone.py => atlantic_pass_apc_heating_zone.py} | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) rename homeassistant/components/overkiz/climate_entities/{atlantic_pass_apc_heating_and_cooling_zone.py => atlantic_pass_apc_heating_zone.py} (97%) diff --git a/homeassistant/components/overkiz/climate_entities/__init__.py b/homeassistant/components/overkiz/climate_entities/__init__.py index 5d676e49747..e70315e099d 100644 --- a/homeassistant/components/overkiz/climate_entities/__init__.py +++ b/homeassistant/components/overkiz/climate_entities/__init__.py @@ -7,9 +7,7 @@ from .atlantic_electrical_heater_with_adjustable_temperature_setpoint import ( ) from .atlantic_electrical_towel_dryer import AtlanticElectricalTowelDryer from .atlantic_heat_recovery_ventilation import AtlanticHeatRecoveryVentilation -from .atlantic_pass_apc_heating_and_cooling_zone import ( - AtlanticPassAPCHeatingAndCoolingZone, -) +from .atlantic_pass_apc_heating_zone import AtlanticPassAPCHeatingZone from .atlantic_pass_apc_zone_control import AtlanticPassAPCZoneControl from .somfy_thermostat import SomfyThermostat @@ -18,7 +16,9 @@ WIDGET_TO_CLIMATE_ENTITY = { UIWidget.ATLANTIC_ELECTRICAL_HEATER_WITH_ADJUSTABLE_TEMPERATURE_SETPOINT: AtlanticElectricalHeaterWithAdjustableTemperatureSetpoint, UIWidget.ATLANTIC_ELECTRICAL_TOWEL_DRYER: AtlanticElectricalTowelDryer, UIWidget.ATLANTIC_HEAT_RECOVERY_VENTILATION: AtlanticHeatRecoveryVentilation, - UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: AtlanticPassAPCHeatingAndCoolingZone, + # ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE works exactly the same as ATLANTIC_PASS_APC_HEATING_ZONE + UIWidget.ATLANTIC_PASS_APC_HEATING_AND_COOLING_ZONE: AtlanticPassAPCHeatingZone, + UIWidget.ATLANTIC_PASS_APC_HEATING_ZONE: AtlanticPassAPCHeatingZone, UIWidget.ATLANTIC_PASS_APC_ZONE_CONTROL: AtlanticPassAPCZoneControl, UIWidget.SOMFY_THERMOSTAT: SomfyThermostat, } diff --git a/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_and_cooling_zone.py b/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_zone.py similarity index 97% rename from homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_and_cooling_zone.py rename to homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_zone.py index efdbf94d9f7..3d1f9038356 100644 --- a/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_and_cooling_zone.py +++ b/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_zone.py @@ -1,4 +1,4 @@ -"""Support for Atlantic Pass APC Heating And Cooling Zone Control.""" +"""Support for Atlantic Pass APC Heating Control.""" from __future__ import annotations from typing import Any, cast @@ -61,8 +61,8 @@ OVERKIZ_TEMPERATURE_STATE_BY_PROFILE: dict[str, str] = { } -class AtlanticPassAPCHeatingAndCoolingZone(OverkizEntity, ClimateEntity): - """Representation of Atlantic Pass APC Heating And Cooling Zone Control.""" +class AtlanticPassAPCHeatingZone(OverkizEntity, ClimateEntity): + """Representation of Atlantic Pass APC Heating Zone Control.""" _attr_hvac_modes = [*HVAC_MODE_TO_OVERKIZ] _attr_preset_modes = [*PRESET_MODES_TO_OVERKIZ] From 949dede16b01093b3dbb5b71e5c1b71c2f913f97 Mon Sep 17 00:00:00 2001 From: Willem-Jan van Rootselaar Date: Wed, 30 Nov 2022 16:36:33 +0100 Subject: [PATCH 0919/1033] Add support for BSBLAN firmware v3 (#82288) * bump python-bsblan version to 0.5.8 * add static_state this holds values that only needs retrieving once in a while * update diagnostics json with the right info --- homeassistant/components/bsblan/__init__.py | 5 ++- homeassistant/components/bsblan/climate.py | 22 ++++++------ homeassistant/components/bsblan/entity.py | 3 +- homeassistant/components/bsblan/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../bsblan/fixtures/diagnostics.json | 35 ------------------- 7 files changed, 21 insertions(+), 50 deletions(-) diff --git a/homeassistant/components/bsblan/__init__.py b/homeassistant/components/bsblan/__init__.py index 6ee58989150..0ef3ed159a6 100644 --- a/homeassistant/components/bsblan/__init__.py +++ b/homeassistant/components/bsblan/__init__.py @@ -1,7 +1,7 @@ """The BSB-Lan integration.""" import dataclasses -from bsblan import BSBLAN, Device, Info, State +from bsblan import BSBLAN, Device, Info, State, StaticState from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -28,6 +28,7 @@ class HomeAssistantBSBLANData: client: BSBLAN device: Device info: Info + static: StaticState async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -54,11 +55,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: device = await bsblan.device() info = await bsblan.info() + static = await bsblan.static_values() hass.data.setdefault(DOMAIN, {})[entry.entry_id] = HomeAssistantBSBLANData( client=bsblan, coordinator=coordinator, device=device, info=info, + static=static, ) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) diff --git a/homeassistant/components/bsblan/climate.py b/homeassistant/components/bsblan/climate.py index e9774055a85..acf9ee25c57 100644 --- a/homeassistant/components/bsblan/climate.py +++ b/homeassistant/components/bsblan/climate.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Any -from bsblan import BSBLAN, BSBLANError, Device, Info, State +from bsblan import BSBLAN, BSBLANError, Device, Info, State, StaticState from homeassistant.components.climate import ( ATTR_HVAC_MODE, @@ -15,7 +15,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import format_mac from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -56,6 +56,7 @@ async def async_setup_entry( data.client, data.device, data.info, + data.static, entry, ) ], @@ -83,20 +84,21 @@ class BSBLANClimate(BSBLANEntity, CoordinatorEntity, ClimateEntity): client: BSBLAN, device: Device, info: Info, + static: StaticState, entry: ConfigEntry, ) -> None: """Initialize BSBLAN climate device.""" - super().__init__(client, device, info, entry) + super().__init__(client, device, info, static, entry) CoordinatorEntity.__init__(self, coordinator) self._attr_unique_id = f"{format_mac(device.MAC)}-climate" - self._attr_min_temp = float(self.coordinator.data.min_temp.value) - self._attr_max_temp = float(self.coordinator.data.max_temp.value) - self._attr_temperature_unit = ( - TEMP_CELSIUS - if self.coordinator.data.current_temperature.unit == "°C" - else TEMP_FAHRENHEIT - ) + self._attr_min_temp = float(static.min_temp.value) + self._attr_max_temp = float(static.max_temp.value) + # check if self.coordinator.data.current_temperature.unit is "°C" or "°C" + if self.coordinator.data.current_temperature.unit in ("°C", "°C"): + self._attr_temperature_unit = UnitOfTemperature.CELSIUS + else: + self._attr_temperature_unit = UnitOfTemperature.FAHRENHEIT @property def current_temperature(self) -> float | None: diff --git a/homeassistant/components/bsblan/entity.py b/homeassistant/components/bsblan/entity.py index 3e8a493d53b..c9b2a2ae9ae 100644 --- a/homeassistant/components/bsblan/entity.py +++ b/homeassistant/components/bsblan/entity.py @@ -1,7 +1,7 @@ """Base entity for the BSBLAN integration.""" from __future__ import annotations -from bsblan import BSBLAN, Device, Info +from bsblan import BSBLAN, Device, Info, StaticState from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST @@ -19,6 +19,7 @@ class BSBLANEntity(Entity): client: BSBLAN, device: Device, info: Info, + static: StaticState, entry: ConfigEntry, ) -> None: """Initialize an BSBLAN entity.""" diff --git a/homeassistant/components/bsblan/manifest.json b/homeassistant/components/bsblan/manifest.json index 87fb7f8c08f..810e78872f3 100644 --- a/homeassistant/components/bsblan/manifest.json +++ b/homeassistant/components/bsblan/manifest.json @@ -3,7 +3,7 @@ "name": "BSB-Lan", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/bsblan", - "requirements": ["python-bsblan==0.5.7"], + "requirements": ["python-bsblan==0.5.8"], "codeowners": ["@liudger"], "iot_class": "local_polling", "loggers": ["bsblan"] diff --git a/requirements_all.txt b/requirements_all.txt index b074d8f6609..efd2532b1d7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1970,7 +1970,7 @@ pythinkingcleaner==0.0.3 python-blockchain-api==0.0.2 # homeassistant.components.bsblan -python-bsblan==0.5.7 +python-bsblan==0.5.8 # homeassistant.components.clementine python-clementine-remote==1.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index dc787936298..3aa97a375c0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1390,7 +1390,7 @@ pytankerkoenig==0.0.6 pytautulli==21.11.0 # homeassistant.components.bsblan -python-bsblan==0.5.7 +python-bsblan==0.5.8 # homeassistant.components.ecobee python-ecobee-api==0.2.14 diff --git a/tests/components/bsblan/fixtures/diagnostics.json b/tests/components/bsblan/fixtures/diagnostics.json index b805a3b0b1b..bd05aca56d5 100644 --- a/tests/components/bsblan/fixtures/diagnostics.json +++ b/tests/components/bsblan/fixtures/diagnostics.json @@ -50,34 +50,6 @@ "value": "18.5", "dataType": 0 }, - "target_temperature_high": { - "name": "Komfortsollwert Maximum", - "unit": "°C", - "desc": "", - "value": "23.0", - "dataType": 0 - }, - "target_temperature_low": { - "name": "Room temp reduced setpoint", - "unit": "°C", - "desc": "", - "value": "17.0", - "dataType": 0 - }, - "min_temp": { - "name": "Room temp frost protection setpoint", - "unit": "°C", - "desc": "", - "value": "8.0", - "dataType": 0 - }, - "max_temp": { - "name": "Summer/winter changeover temp heat circuit 1", - "unit": "°C", - "desc": "", - "value": "20.0", - "dataType": 0 - }, "hvac_action": { "name": "Status heating circuit 1", "unit": "", @@ -98,13 +70,6 @@ "desc": "Kein Bedarf", "value": "0", "dataType": 1 - }, - "outside_temperature": { - "name": "Outside temp sensor local", - "unit": "°C", - "desc": "", - "value": "6.1", - "dataType": 0 } } } From a43d94430967a1c051be8630e8769a744f8b7646 Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Wed, 30 Nov 2022 18:54:06 +0100 Subject: [PATCH 0920/1033] Address late KNX flow tests review (#82975) * KNX flow tests review * patch out async_setup_entry in options-flow tests * remove unneeded hass.async_block_till_done() * Update test_config_flow.py * autouse setup mock * patch out async_setup too * rename fixture according to pytest docs * test call count to async_setup_entry * dict access instead of .get() --- tests/components/knx/test_config_flow.py | 459 ++++++++++------------- 1 file changed, 206 insertions(+), 253 deletions(-) diff --git a/tests/components/knx/test_config_flow.py b/tests/components/knx/test_config_flow.py index c5e26561b92..9d6eaf68c54 100644 --- a/tests/components/knx/test_config_flow.py +++ b/tests/components/knx/test_config_flow.py @@ -1,5 +1,5 @@ """Test the KNX config flow.""" -from unittest.mock import Mock, patch +from unittest.mock import patch import pytest from xknx.exceptions.exception import InvalidSecureConfiguration @@ -45,6 +45,15 @@ from homeassistant.data_entry_flow import FlowResult, FlowResultType from tests.common import MockConfigEntry +@pytest.fixture(name="knx_setup", autouse=True) +def fixture_knx_setup(): + """Mock KNX entry setup.""" + with patch("homeassistant.components.knx.async_setup", return_value=True), patch( + "homeassistant.components.knx.async_setup_entry", return_value=True + ) as mock_async_setup_entry: + yield mock_async_setup_entry + + def _gateway_descriptor( ip: str, port: int, @@ -100,7 +109,9 @@ async def test_user_single_instance(hass): "homeassistant.components.knx.config_flow.GatewayScanner", return_value=GatewayScannerMock(), ) -async def test_routing_setup(gateway_scanner_mock, hass: HomeAssistant) -> None: +async def test_routing_setup( + gateway_scanner_mock, hass: HomeAssistant, knx_setup +) -> None: """Test routing setup.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -114,36 +125,30 @@ async def test_routing_setup(gateway_scanner_mock, hass: HomeAssistant) -> None: CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING, }, ) - await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "routing" assert result2["errors"] == {"base": "no_router_discovered"} - with patch( - "homeassistant.components.knx.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result3 = await hass.config_entries.flow.async_configure( - result2["flow_id"], - { - CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "1.1.110", - }, - ) - await hass.async_block_till_done() - assert result3["type"] == FlowResultType.CREATE_ENTRY - assert result3["title"] == "Routing as 1.1.110" - assert result3["data"] == { - **DEFAULT_ENTRY_DATA, - CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING, + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + { CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_LOCAL_IP: None, CONF_KNX_INDIVIDUAL_ADDRESS: "1.1.110", - } - - assert len(mock_setup_entry.mock_calls) == 1 + }, + ) + await hass.async_block_till_done() + assert result3["type"] == FlowResultType.CREATE_ENTRY + assert result3["title"] == "Routing as 1.1.110" + assert result3["data"] == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING, + CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, + CONF_KNX_MCAST_PORT: 3675, + CONF_KNX_LOCAL_IP: None, + CONF_KNX_INDIVIDUAL_ADDRESS: "1.1.110", + } + knx_setup.assert_called_once() @patch( @@ -151,7 +156,7 @@ async def test_routing_setup(gateway_scanner_mock, hass: HomeAssistant) -> None: return_value=GatewayScannerMock(), ) async def test_routing_setup_advanced( - gateway_scanner_mock, hass: HomeAssistant + gateway_scanner_mock, hass: HomeAssistant, knx_setup ) -> None: """Test routing setup with advanced options.""" result = await hass.config_entries.flow.async_init( @@ -170,7 +175,6 @@ async def test_routing_setup_advanced( CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING, }, ) - await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "routing" assert result2["errors"] == {"base": "no_router_discovered"} @@ -185,7 +189,6 @@ async def test_routing_setup_advanced( CONF_KNX_LOCAL_IP: "no_local_ip", }, ) - await hass.async_block_till_done() assert result_invalid_input["type"] == FlowResultType.FORM assert result_invalid_input["step_id"] == "routing" assert result_invalid_input["errors"] == { @@ -196,32 +199,27 @@ async def test_routing_setup_advanced( } # valid user input - with patch( - "homeassistant.components.knx.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result3 = await hass.config_entries.flow.async_configure( - result2["flow_id"], - { - CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, - CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "1.1.110", - CONF_KNX_LOCAL_IP: "192.168.1.112", - }, - ) - await hass.async_block_till_done() - assert result3["type"] == FlowResultType.CREATE_ENTRY - assert result3["title"] == "Routing as 1.1.110" - assert result3["data"] == { - **DEFAULT_ENTRY_DATA, - CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING, + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + { CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, CONF_KNX_MCAST_PORT: 3675, - CONF_KNX_LOCAL_IP: "192.168.1.112", CONF_KNX_INDIVIDUAL_ADDRESS: "1.1.110", - } - - assert len(mock_setup_entry.mock_calls) == 1 + CONF_KNX_LOCAL_IP: "192.168.1.112", + }, + ) + await hass.async_block_till_done() + assert result3["type"] == FlowResultType.CREATE_ENTRY + assert result3["title"] == "Routing as 1.1.110" + assert result3["data"] == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING, + CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP, + CONF_KNX_MCAST_PORT: 3675, + CONF_KNX_LOCAL_IP: "192.168.1.112", + CONF_KNX_INDIVIDUAL_ADDRESS: "1.1.110", + } + knx_setup.assert_called_once() @patch( @@ -229,7 +227,7 @@ async def test_routing_setup_advanced( return_value=GatewayScannerMock(), ) async def test_routing_secure_manual_setup( - gateway_scanner_mock, hass: HomeAssistant + gateway_scanner_mock, hass: HomeAssistant, knx_setup ) -> None: """Test routing secure setup with manual key config.""" result = await hass.config_entries.flow.async_init( @@ -244,7 +242,6 @@ async def test_routing_secure_manual_setup( CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING, }, ) - await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "routing" assert result2["errors"] == {"base": "no_router_discovered"} @@ -291,28 +288,24 @@ async def test_routing_secure_manual_setup( assert result_invalid_key2["step_id"] == "secure_routing_manual" assert result_invalid_key2["errors"] == {"backbone_key": "invalid_backbone_key"} - with patch( - "homeassistant.components.knx.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - secure_routing_manual = await hass.config_entries.flow.async_configure( - result_invalid_key2["flow_id"], - { - CONF_KNX_ROUTING_BACKBONE_KEY: "bbaacc44bbaacc44bbaacc44bbaacc44", - CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: 2000, - }, - ) - await hass.async_block_till_done() - assert secure_routing_manual["type"] == FlowResultType.CREATE_ENTRY - assert secure_routing_manual["title"] == "Secure Routing as 0.0.123" - assert secure_routing_manual["data"] == { - **DEFAULT_ENTRY_DATA, - CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING_SECURE, + secure_routing_manual = await hass.config_entries.flow.async_configure( + result_invalid_key2["flow_id"], + { CONF_KNX_ROUTING_BACKBONE_KEY: "bbaacc44bbaacc44bbaacc44bbaacc44", CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: 2000, - CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.123", - } - assert len(mock_setup_entry.mock_calls) == 1 + }, + ) + await hass.async_block_till_done() + assert secure_routing_manual["type"] == FlowResultType.CREATE_ENTRY + assert secure_routing_manual["title"] == "Secure Routing as 0.0.123" + assert secure_routing_manual["data"] == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING_SECURE, + CONF_KNX_ROUTING_BACKBONE_KEY: "bbaacc44bbaacc44bbaacc44bbaacc44", + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: 2000, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.123", + } + knx_setup.assert_called_once() @patch( @@ -320,7 +313,7 @@ async def test_routing_secure_manual_setup( return_value=GatewayScannerMock(), ) async def test_routing_secure_keyfile( - gateway_scanner_mock, hass: HomeAssistant + gateway_scanner_mock, hass: HomeAssistant, knx_setup ) -> None: """Test routing secure setup with keyfile.""" result = await hass.config_entries.flow.async_init( @@ -335,7 +328,6 @@ async def test_routing_secure_keyfile( CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING, }, ) - await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "routing" assert result2["errors"] == {"base": "no_router_discovered"} @@ -361,9 +353,6 @@ async def test_routing_secure_keyfile( assert not result4["errors"] with patch( - "homeassistant.components.knx.async_setup_entry", - return_value=True, - ) as mock_setup_entry, patch( "homeassistant.components.knx.config_flow.load_keyring", return_value=True ): routing_secure_knxkeys = await hass.config_entries.flow.async_configure( @@ -374,21 +363,21 @@ async def test_routing_secure_keyfile( }, ) await hass.async_block_till_done() - assert routing_secure_knxkeys["type"] == FlowResultType.CREATE_ENTRY - assert routing_secure_knxkeys["title"] == "Secure Routing as 0.0.123" - assert routing_secure_knxkeys["data"] == { - **DEFAULT_ENTRY_DATA, - CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING_SECURE, - CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", - CONF_KNX_KNXKEY_PASSWORD: "password", - CONF_KNX_ROUTING_BACKBONE_KEY: None, - CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, - CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None, - CONF_KNX_SECURE_USER_ID: None, - CONF_KNX_SECURE_USER_PASSWORD: None, - CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.123", - } - assert len(mock_setup_entry.mock_calls) == 1 + assert routing_secure_knxkeys["type"] == FlowResultType.CREATE_ENTRY + assert routing_secure_knxkeys["title"] == "Secure Routing as 0.0.123" + assert routing_secure_knxkeys["data"] == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_ROUTING_SECURE, + CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", + CONF_KNX_KNXKEY_PASSWORD: "password", + CONF_KNX_ROUTING_BACKBONE_KEY: None, + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, + CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None, + CONF_KNX_SECURE_USER_ID: None, + CONF_KNX_SECURE_USER_PASSWORD: None, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.123", + } + knx_setup.assert_called_once() @pytest.mark.parametrize( @@ -452,7 +441,7 @@ async def test_routing_secure_keyfile( return_value=GatewayScannerMock(), ) async def test_tunneling_setup_manual( - gateway_scanner_mock, hass: HomeAssistant, user_input, config_entry_data + gateway_scanner_mock, hass: HomeAssistant, knx_setup, user_input, config_entry_data ) -> None: """Test tunneling if no gateway was found found (or `manual` option was chosen).""" result = await hass.config_entries.flow.async_init( @@ -467,25 +456,19 @@ async def test_tunneling_setup_manual( CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, }, ) - await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "manual_tunnel" assert result2["errors"] == {"base": "no_tunnel_discovered"} - with patch( - "homeassistant.components.knx.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result3 = await hass.config_entries.flow.async_configure( - result2["flow_id"], - user_input, - ) - await hass.async_block_till_done() - assert result3["type"] == FlowResultType.CREATE_ENTRY - assert result3["title"] == "Tunneling @ 192.168.0.1" - assert result3["data"] == config_entry_data - - assert len(mock_setup_entry.mock_calls) == 1 + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + user_input, + ) + await hass.async_block_till_done() + assert result3["type"] == FlowResultType.CREATE_ENTRY + assert result3["title"] == "Tunneling @ 192.168.0.1" + assert result3["data"] == config_entry_data + knx_setup.assert_called_once() @patch( @@ -493,7 +476,7 @@ async def test_tunneling_setup_manual( return_value=GatewayScannerMock(), ) async def test_tunneling_setup_for_local_ip( - gateway_scanner_mock, hass: HomeAssistant + gateway_scanner_mock, hass: HomeAssistant, knx_setup ) -> None: """Test tunneling if only one gateway is found.""" result = await hass.config_entries.flow.async_init( @@ -512,7 +495,6 @@ async def test_tunneling_setup_for_local_ip( CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, }, ) - await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "manual_tunnel" assert result2["errors"] == {"base": "no_tunnel_discovered"} @@ -527,7 +509,6 @@ async def test_tunneling_setup_for_local_ip( CONF_KNX_LOCAL_IP: "192.168.1.112", }, ) - await hass.async_block_till_done() assert result_invalid_host["type"] == FlowResultType.FORM assert result_invalid_host["step_id"] == "manual_tunnel" assert result_invalid_host["errors"] == { @@ -544,7 +525,6 @@ async def test_tunneling_setup_for_local_ip( CONF_KNX_LOCAL_IP: "asdf", }, ) - await hass.async_block_till_done() assert result_invalid_local["type"] == FlowResultType.FORM assert result_invalid_local["step_id"] == "manual_tunnel" assert result_invalid_local["errors"] == { @@ -553,36 +533,33 @@ async def test_tunneling_setup_for_local_ip( } # valid user input - with patch( - "homeassistant.components.knx.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result3 = await hass.config_entries.flow.async_configure( - result2["flow_id"], - { - CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING, - CONF_HOST: "192.168.0.2", - CONF_PORT: 3675, - CONF_KNX_LOCAL_IP: "192.168.1.112", - }, - ) - await hass.async_block_till_done() - assert result3["type"] == FlowResultType.CREATE_ENTRY - assert result3["title"] == "Tunneling @ 192.168.0.2" - assert result3["data"] == { - **DEFAULT_ENTRY_DATA, - CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + { + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING, CONF_HOST: "192.168.0.2", CONF_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", - CONF_KNX_ROUTE_BACK: False, CONF_KNX_LOCAL_IP: "192.168.1.112", - } - - assert len(mock_setup_entry.mock_calls) == 1 + }, + ) + await hass.async_block_till_done() + assert result3["type"] == FlowResultType.CREATE_ENTRY + assert result3["title"] == "Tunneling @ 192.168.0.2" + assert result3["data"] == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, + CONF_HOST: "192.168.0.2", + CONF_PORT: 3675, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", + CONF_KNX_ROUTE_BACK: False, + CONF_KNX_LOCAL_IP: "192.168.1.112", + } + knx_setup.assert_called_once() -async def test_tunneling_setup_for_multiple_found_gateways(hass: HomeAssistant) -> None: +async def test_tunneling_setup_for_multiple_found_gateways( + hass: HomeAssistant, knx_setup +) -> None: """Test tunneling if multiple gateways are found.""" gateway = _gateway_descriptor("192.168.0.1", 3675) gateway2 = _gateway_descriptor("192.168.1.100", 3675) @@ -602,32 +579,26 @@ async def test_tunneling_setup_for_multiple_found_gateways(hass: HomeAssistant) CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, }, ) - await hass.async_block_till_done() assert tunnel_flow["type"] == FlowResultType.FORM assert tunnel_flow["step_id"] == "tunnel" assert not tunnel_flow["errors"] - with patch( - "homeassistant.components.knx.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result = await hass.config_entries.flow.async_configure( - tunnel_flow["flow_id"], - {CONF_KNX_GATEWAY: str(gateway)}, - ) - await hass.async_block_till_done() - assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["data"] == { - **DEFAULT_ENTRY_DATA, - CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, - CONF_HOST: "192.168.0.1", - CONF_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", - CONF_KNX_ROUTE_BACK: False, - CONF_KNX_LOCAL_IP: None, - } - - assert len(mock_setup_entry.mock_calls) == 1 + result = await hass.config_entries.flow.async_configure( + tunnel_flow["flow_id"], + {CONF_KNX_GATEWAY: str(gateway)}, + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["data"] == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, + CONF_HOST: "192.168.0.1", + CONF_PORT: 3675, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", + CONF_KNX_ROUTE_BACK: False, + CONF_KNX_LOCAL_IP: None, + } + knx_setup.assert_called_once() @pytest.mark.parametrize( @@ -660,7 +631,6 @@ async def test_manual_tunnel_step_with_found_gateway( CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, }, ) - await hass.async_block_till_done() assert tunnel_flow["type"] == FlowResultType.FORM assert tunnel_flow["step_id"] == "tunnel" assert not tunnel_flow["errors"] @@ -671,13 +641,14 @@ async def test_manual_tunnel_step_with_found_gateway( CONF_KNX_GATEWAY: OPTION_MANUAL_TUNNEL, }, ) - await hass.async_block_till_done() assert manual_tunnel_flow["type"] == FlowResultType.FORM assert manual_tunnel_flow["step_id"] == "manual_tunnel" assert not manual_tunnel_flow["errors"] -async def test_form_with_automatic_connection_handling(hass: HomeAssistant) -> None: +async def test_form_with_automatic_connection_handling( + hass: HomeAssistant, knx_setup +) -> None: """Test we get the form.""" with patch( "homeassistant.components.knx.config_flow.GatewayScanner" @@ -691,26 +662,20 @@ async def test_form_with_automatic_connection_handling(hass: HomeAssistant) -> N assert result["type"] == FlowResultType.FORM assert not result["errors"] - with patch( - "homeassistant.components.knx.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC, - }, - ) - await hass.async_block_till_done() - + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC, + }, + ) + await hass.async_block_till_done() assert result2["type"] == FlowResultType.CREATE_ENTRY assert result2["title"] == CONF_KNX_AUTOMATIC.capitalize() assert result2["data"] == { **DEFAULT_ENTRY_DATA, CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC, } - - assert len(mock_setup_entry.mock_calls) == 1 + knx_setup.assert_called_once() async def _get_menu_step(hass: HomeAssistant) -> FlowResult: @@ -737,7 +702,6 @@ async def _get_menu_step(hass: HomeAssistant) -> FlowResult: CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, }, ) - await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "tunnel" assert not result2["errors"] @@ -746,7 +710,6 @@ async def _get_menu_step(hass: HomeAssistant) -> FlowResult: result2["flow_id"], {CONF_KNX_GATEWAY: str(gateway)}, ) - await hass.async_block_till_done() assert result3["type"] == FlowResultType.MENU assert result3["step_id"] == "secure_key_source" return result3 @@ -778,7 +741,6 @@ async def test_get_secure_menu_step_manual_tunnelling( CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, }, ) - await hass.async_block_till_done() assert result2["type"] == FlowResultType.FORM assert result2["step_id"] == "tunnel" assert not result2["errors"] @@ -798,12 +760,11 @@ async def test_get_secure_menu_step_manual_tunnelling( CONF_PORT: 3675, }, ) - await hass.async_block_till_done() assert result3["type"] == FlowResultType.MENU assert result3["step_id"] == "secure_key_source" -async def test_configure_secure_tunnel_manual(hass: HomeAssistant): +async def test_configure_secure_tunnel_manual(hass: HomeAssistant, knx_setup): """Test configure tunnelling secure keys manually.""" menu_step = await _get_menu_step(hass) @@ -815,37 +776,32 @@ async def test_configure_secure_tunnel_manual(hass: HomeAssistant): assert result["step_id"] == "secure_tunnel_manual" assert not result["errors"] - with patch( - "homeassistant.components.knx.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - secure_tunnel_manual = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - CONF_KNX_SECURE_USER_ID: 2, - CONF_KNX_SECURE_USER_PASSWORD: "password", - CONF_KNX_SECURE_DEVICE_AUTHENTICATION: "device_auth", - }, - ) - await hass.async_block_till_done() - assert secure_tunnel_manual["type"] == FlowResultType.CREATE_ENTRY - assert secure_tunnel_manual["data"] == { - **DEFAULT_ENTRY_DATA, - CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, + secure_tunnel_manual = await hass.config_entries.flow.async_configure( + result["flow_id"], + { CONF_KNX_SECURE_USER_ID: 2, CONF_KNX_SECURE_USER_PASSWORD: "password", CONF_KNX_SECURE_DEVICE_AUTHENTICATION: "device_auth", - CONF_HOST: "192.168.0.1", - CONF_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", - CONF_KNX_ROUTE_BACK: False, - CONF_KNX_LOCAL_IP: None, - } - - assert len(mock_setup_entry.mock_calls) == 1 + }, + ) + await hass.async_block_till_done() + assert secure_tunnel_manual["type"] == FlowResultType.CREATE_ENTRY + assert secure_tunnel_manual["data"] == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, + CONF_KNX_SECURE_USER_ID: 2, + CONF_KNX_SECURE_USER_PASSWORD: "password", + CONF_KNX_SECURE_DEVICE_AUTHENTICATION: "device_auth", + CONF_HOST: "192.168.0.1", + CONF_PORT: 3675, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", + CONF_KNX_ROUTE_BACK: False, + CONF_KNX_LOCAL_IP: None, + } + knx_setup.assert_called_once() -async def test_configure_secure_knxkeys(hass: HomeAssistant): +async def test_configure_secure_knxkeys(hass: HomeAssistant, knx_setup): """Test configure secure knxkeys.""" menu_step = await _get_menu_step(hass) @@ -858,9 +814,6 @@ async def test_configure_secure_knxkeys(hass: HomeAssistant): assert not result["errors"] with patch( - "homeassistant.components.knx.async_setup_entry", - return_value=True, - ) as mock_setup_entry, patch( "homeassistant.components.knx.config_flow.load_keyring", return_value=True ): secure_knxkeys = await hass.config_entries.flow.async_configure( @@ -871,25 +824,24 @@ async def test_configure_secure_knxkeys(hass: HomeAssistant): }, ) await hass.async_block_till_done() - assert secure_knxkeys["type"] == FlowResultType.CREATE_ENTRY - assert secure_knxkeys["data"] == { - **DEFAULT_ENTRY_DATA, - CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, - CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", - CONF_KNX_KNXKEY_PASSWORD: "password", - CONF_KNX_ROUTING_BACKBONE_KEY: None, - CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, - CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None, - CONF_KNX_SECURE_USER_ID: None, - CONF_KNX_SECURE_USER_PASSWORD: None, - CONF_HOST: "192.168.0.1", - CONF_PORT: 3675, - CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", - CONF_KNX_ROUTE_BACK: False, - CONF_KNX_LOCAL_IP: None, - } - - assert len(mock_setup_entry.mock_calls) == 1 + assert secure_knxkeys["type"] == FlowResultType.CREATE_ENTRY + assert secure_knxkeys["data"] == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, + CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", + CONF_KNX_KNXKEY_PASSWORD: "password", + CONF_KNX_ROUTING_BACKBONE_KEY: None, + CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, + CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None, + CONF_KNX_SECURE_USER_ID: None, + CONF_KNX_SECURE_USER_PASSWORD: None, + CONF_HOST: "192.168.0.1", + CONF_PORT: 3675, + CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", + CONF_KNX_ROUTE_BACK: False, + CONF_KNX_LOCAL_IP: None, + } + knx_setup.assert_called_once() async def test_configure_secure_knxkeys_file_not_found(hass: HomeAssistant): @@ -915,7 +867,6 @@ async def test_configure_secure_knxkeys_file_not_found(hass: HomeAssistant): CONF_KNX_KNXKEY_PASSWORD: "password", }, ) - await hass.async_block_till_done() assert secure_knxkeys["type"] == FlowResultType.FORM assert secure_knxkeys["errors"] assert secure_knxkeys["errors"][CONF_KNX_KNXKEY_FILENAME] == "file_not_found" @@ -944,20 +895,19 @@ async def test_configure_secure_knxkeys_invalid_signature(hass: HomeAssistant): CONF_KNX_KNXKEY_PASSWORD: "password", }, ) - await hass.async_block_till_done() assert secure_knxkeys["type"] == FlowResultType.FORM assert secure_knxkeys["errors"] assert secure_knxkeys["errors"][CONF_KNX_KNXKEY_PASSWORD] == "invalid_signature" async def test_options_flow_connection_type( - hass: HomeAssistant, mock_config_entry: MockConfigEntry + hass: HomeAssistant, knx_setup, mock_config_entry: MockConfigEntry ) -> None: """Test options flow changing interface.""" mock_config_entry.add_to_hass(hass) - hass.data[DOMAIN] = Mock() # GatewayScanner uses running XKNX() instance gateway = _gateway_descriptor("192.168.0.1", 3675) + await hass.config_entries.async_setup(mock_config_entry.entry_id) menu_step = await hass.config_entries.options.async_init(mock_config_entry.entry_id) with patch( @@ -968,9 +918,8 @@ async def test_options_flow_connection_type( menu_step["flow_id"], {"next_step_id": "connection_type"}, ) - - assert result.get("type") == FlowResultType.FORM - assert result.get("step_id") == "connection_type" + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "connection_type" result2 = await hass.config_entries.options.async_configure( result["flow_id"], @@ -978,8 +927,8 @@ async def test_options_flow_connection_type( CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, }, ) - assert result2.get("type") == FlowResultType.FORM - assert result2.get("step_id") == "tunnel" + assert result2["type"] == FlowResultType.FORM + assert result2["step_id"] == "tunnel" result3 = await hass.config_entries.options.async_configure( result2["flow_id"], @@ -988,9 +937,8 @@ async def test_options_flow_connection_type( }, ) await hass.async_block_till_done() - assert result3.get("type") == FlowResultType.CREATE_ENTRY - assert not result3.get("data") - + assert result3["type"] == FlowResultType.CREATE_ENTRY + assert not result3["data"] assert mock_config_entry.data == { CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240", @@ -1003,9 +951,12 @@ async def test_options_flow_connection_type( CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, CONF_KNX_ROUTE_BACK: False, } + knx_setup.assert_called_once() -async def test_options_flow_secure_manual_to_keyfile(hass: HomeAssistant) -> None: +async def test_options_flow_secure_manual_to_keyfile( + hass: HomeAssistant, knx_setup +) -> None: """Test options flow changing secure credential source.""" mock_config_entry = MockConfigEntry( title="KNX", @@ -1025,7 +976,6 @@ async def test_options_flow_secure_manual_to_keyfile(hass: HomeAssistant) -> Non CONF_KNX_LOCAL_IP: None, }, ) - mock_config_entry.add_to_hass(hass) gateway = _gateway_descriptor( "192.168.0.1", 3675, @@ -1033,6 +983,8 @@ async def test_options_flow_secure_manual_to_keyfile(hass: HomeAssistant) -> Non requires_secure=True, ) + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) menu_step = await hass.config_entries.options.async_init(mock_config_entry.entry_id) with patch( "homeassistant.components.knx.config_flow.GatewayScanner" @@ -1042,8 +994,8 @@ async def test_options_flow_secure_manual_to_keyfile(hass: HomeAssistant) -> Non menu_step["flow_id"], {"next_step_id": "connection_type"}, ) - assert result.get("type") == FlowResultType.FORM - assert result.get("step_id") == "connection_type" + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "connection_type" result2 = await hass.config_entries.options.async_configure( result["flow_id"], @@ -1080,6 +1032,7 @@ async def test_options_flow_secure_manual_to_keyfile(hass: HomeAssistant) -> Non CONF_KNX_KNXKEY_PASSWORD: "password", }, ) + await hass.async_block_till_done() assert secure_knxkeys["type"] == FlowResultType.CREATE_ENTRY assert mock_config_entry.data == { **DEFAULT_ENTRY_DATA, @@ -1097,22 +1050,23 @@ async def test_options_flow_secure_manual_to_keyfile(hass: HomeAssistant) -> Non CONF_KNX_ROUTE_BACK: False, CONF_KNX_LOCAL_IP: None, } + knx_setup.assert_called_once() async def test_options_communication_settings( - hass: HomeAssistant, mock_config_entry: MockConfigEntry + hass: HomeAssistant, knx_setup, mock_config_entry: MockConfigEntry ) -> None: """Test options flow changing communication settings.""" mock_config_entry.add_to_hass(hass) - + await hass.config_entries.async_setup(mock_config_entry.entry_id) menu_step = await hass.config_entries.options.async_init(mock_config_entry.entry_id) result = await hass.config_entries.options.async_configure( menu_step["flow_id"], {"next_step_id": "communication_settings"}, ) - assert result.get("type") == FlowResultType.FORM - assert result.get("step_id") == "communication_settings" + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "communication_settings" result2 = await hass.config_entries.options.async_configure( result["flow_id"], @@ -1121,14 +1075,13 @@ async def test_options_communication_settings( CONF_KNX_RATE_LIMIT: 40, }, ) - await hass.async_block_till_done() - assert result2.get("type") == FlowResultType.CREATE_ENTRY + assert result2["type"] == FlowResultType.CREATE_ENTRY assert not result2.get("data") - assert mock_config_entry.data == { **DEFAULT_ENTRY_DATA, CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC, CONF_KNX_STATE_UPDATER: False, CONF_KNX_RATE_LIMIT: 40, } + knx_setup.assert_called_once() From 182c4bbc056c03e92613ac436b6c2ff9b140c057 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 30 Nov 2022 19:04:14 +0100 Subject: [PATCH 0921/1033] Bump aiounifi to v42 (#82995) --- homeassistant/components/unifi/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/unifi/manifest.json b/homeassistant/components/unifi/manifest.json index 5b96560f8c5..c9e9464a317 100644 --- a/homeassistant/components/unifi/manifest.json +++ b/homeassistant/components/unifi/manifest.json @@ -3,7 +3,7 @@ "name": "UniFi Network", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/unifi", - "requirements": ["aiounifi==41"], + "requirements": ["aiounifi==42"], "codeowners": ["@Kane610"], "quality_scale": "platinum", "ssdp": [ diff --git a/requirements_all.txt b/requirements_all.txt index efd2532b1d7..99ef4db121f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -282,7 +282,7 @@ aiosyncthing==0.5.1 aiotractive==0.5.5 # homeassistant.components.unifi -aiounifi==41 +aiounifi==42 # homeassistant.components.vlc_telnet aiovlc==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3aa97a375c0..57b4c51ac52 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -257,7 +257,7 @@ aiosyncthing==0.5.1 aiotractive==0.5.5 # homeassistant.components.unifi -aiounifi==41 +aiounifi==42 # homeassistant.components.vlc_telnet aiovlc==0.1.0 From 7f96fbb0353a8d0792d6f1d3dad0103d07b3f8ed Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 30 Nov 2022 14:56:07 -0500 Subject: [PATCH 0922/1033] Bump ZHA dependencies (#82999) --- homeassistant/components/zha/core/const.py | 1 + homeassistant/components/zha/core/device.py | 35 ++++++++++++++------- homeassistant/components/zha/manifest.json | 8 ++--- requirements_all.txt | 8 ++--- requirements_test_all.txt | 8 ++--- tests/components/zha/conftest.py | 1 + 6 files changed, 38 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index 180be2d8830..c6958abc742 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -49,6 +49,7 @@ ATTR_POWER_SOURCE = "power_source" ATTR_PROFILE_ID = "profile_id" ATTR_QUIRK_APPLIED = "quirk_applied" ATTR_QUIRK_CLASS = "quirk_class" +ATTR_ROUTES = "routes" ATTR_RSSI = "rssi" ATTR_SIGNATURE = "signature" ATTR_TYPE = "type" diff --git a/homeassistant/components/zha/core/device.py b/homeassistant/components/zha/core/device.py index 5eb436cbe53..dfed1ce0ebe 100644 --- a/homeassistant/components/zha/core/device.py +++ b/homeassistant/components/zha/core/device.py @@ -57,6 +57,7 @@ from .const import ( ATTR_POWER_SOURCE, ATTR_QUIRK_APPLIED, ATTR_QUIRK_CLASS, + ATTR_ROUTES, ATTR_RSSI, ATTR_SIGNATURE, ATTR_VALUE, @@ -523,20 +524,32 @@ class ZHADevice(LogMixin): for entity_ref in self.gateway.device_registry[self.ieee] ] - # Return the neighbor information + topology = self.gateway.application_controller.topology device_info[ATTR_NEIGHBORS] = [ { - "device_type": neighbor.neighbor.device_type.name, - "rx_on_when_idle": neighbor.neighbor.rx_on_when_idle.name, - "relationship": neighbor.neighbor.relationship.name, - "extended_pan_id": str(neighbor.neighbor.extended_pan_id), - "ieee": str(neighbor.neighbor.ieee), - "nwk": str(neighbor.neighbor.nwk), - "permit_joining": neighbor.neighbor.permit_joining.name, - "depth": str(neighbor.neighbor.depth), - "lqi": str(neighbor.neighbor.lqi), + "device_type": neighbor.device_type.name, + "rx_on_when_idle": neighbor.rx_on_when_idle.name, + "relationship": neighbor.relationship.name, + "extended_pan_id": str(neighbor.extended_pan_id), + "ieee": str(neighbor.ieee), + "nwk": str(neighbor.nwk), + "permit_joining": neighbor.permit_joining.name, + "depth": str(neighbor.depth), + "lqi": str(neighbor.lqi), } - for neighbor in self._zigpy_device.neighbors + for neighbor in topology.neighbors[self.ieee] + ] + + device_info[ATTR_ROUTES] = [ + { + "dest_nwk": str(route.DstNWK), + "route_status": str(route.RouteStatus.name), + "memory_constrained": bool(route.MemoryConstrained), + "many_to_one": bool(route.ManyToOne), + "route_record_required": bool(route.RouteRecordRequired), + "next_hop": str(route.NextHop), + } + for route in topology.routes[self.ieee] ] # Return endpoint device type Names diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index d2b19fe8893..d617b3474f9 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -4,15 +4,15 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zha", "requirements": [ - "bellows==0.34.4", + "bellows==0.34.5", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.87", - "zigpy-deconz==0.19.1", - "zigpy==0.51.6", + "zigpy-deconz==0.19.2", + "zigpy==0.52.2", "zigpy-xbee==0.16.2", "zigpy-zigate==0.10.3", - "zigpy-znp==0.9.1" + "zigpy-znp==0.9.2" ], "usb": [ { diff --git a/requirements_all.txt b/requirements_all.txt index 99ef4db121f..449b5541365 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -413,7 +413,7 @@ beautifulsoup4==4.11.1 # beewi_smartclim==0.0.10 # homeassistant.components.zha -bellows==0.34.4 +bellows==0.34.5 # homeassistant.components.bmw_connected_drive bimmer_connected==0.10.4 @@ -2639,7 +2639,7 @@ zhong_hong_hvac==1.0.9 ziggo-mediabox-xl==1.1.0 # homeassistant.components.zha -zigpy-deconz==0.19.1 +zigpy-deconz==0.19.2 # homeassistant.components.zha zigpy-xbee==0.16.2 @@ -2648,10 +2648,10 @@ zigpy-xbee==0.16.2 zigpy-zigate==0.10.3 # homeassistant.components.zha -zigpy-znp==0.9.1 +zigpy-znp==0.9.2 # homeassistant.components.zha -zigpy==0.51.6 +zigpy==0.52.2 # homeassistant.components.zoneminder zm-py==0.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 57b4c51ac52..fbcec3f9af2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -340,7 +340,7 @@ base36==0.1.1 beautifulsoup4==4.11.1 # homeassistant.components.zha -bellows==0.34.4 +bellows==0.34.5 # homeassistant.components.bmw_connected_drive bimmer_connected==0.10.4 @@ -1834,7 +1834,7 @@ zeroconf==0.39.4 zha-quirks==0.0.87 # homeassistant.components.zha -zigpy-deconz==0.19.1 +zigpy-deconz==0.19.2 # homeassistant.components.zha zigpy-xbee==0.16.2 @@ -1843,10 +1843,10 @@ zigpy-xbee==0.16.2 zigpy-zigate==0.10.3 # homeassistant.components.zha -zigpy-znp==0.9.1 +zigpy-znp==0.9.2 # homeassistant.components.zha -zigpy==0.51.6 +zigpy==0.52.2 # homeassistant.components.zwave_js zwave-js-server-python==0.43.0 diff --git a/tests/components/zha/conftest.py b/tests/components/zha/conftest.py index a4d29224b74..e002d8ce03b 100644 --- a/tests/components/zha/conftest.py +++ b/tests/components/zha/conftest.py @@ -57,6 +57,7 @@ def zigpy_app_controller(): type(app).nwk = PropertyMock(return_value=zigpy.types.NWK(0x0000)) type(app).devices = PropertyMock(return_value={}) type(app).backups = zigpy.backups.BackupManager(app) + type(app).topology = zigpy.topology.Topology(app) state = State() state.node_info.ieee = app.ieee.return_value From 0d00e9c2d064a7d74176272c1f6dffac0108d40c Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Wed, 30 Nov 2022 22:01:34 +0200 Subject: [PATCH 0923/1033] Fix Shelly addon analog input sensor (#83005) --- homeassistant/components/shelly/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index 7d99f015c5e..6bc3b52b65f 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -417,7 +417,7 @@ RPC_SENSORS: Final = { available=lambda status: status is not None, ), "analoginput": RpcSensorDescription( - key="analoginput", + key="input", sub_key="percent", name="Analog Input", native_unit_of_measurement=PERCENTAGE, From 8acc114cd9ce809b279ac8d84eba9c22fcbd0b9b Mon Sep 17 00:00:00 2001 From: Artem Draft Date: Wed, 30 Nov 2022 23:08:15 +0300 Subject: [PATCH 0924/1033] Fix BraviaTV fake reauthentication (#82986) --- .../components/braviatv/coordinator.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/braviatv/coordinator.py b/homeassistant/components/braviatv/coordinator.py index 1262e7bf7cc..3d57850a648 100644 --- a/homeassistant/components/braviatv/coordinator.py +++ b/homeassistant/components/braviatv/coordinator.py @@ -125,13 +125,18 @@ class BraviaTVCoordinator(DataUpdateCoordinator[None]): """Connect and fetch data.""" try: if not self.connected: - if self.use_psk: - await self.client.connect(psk=self.pin) - else: - await self.client.connect( - pin=self.pin, clientid=self.client_id, nickname=self.nickname - ) - self.connected = True + try: + if self.use_psk: + await self.client.connect(psk=self.pin) + else: + await self.client.connect( + pin=self.pin, + clientid=self.client_id, + nickname=self.nickname, + ) + self.connected = True + except BraviaTVAuthError as err: + raise ConfigEntryAuthFailed from err power_status = await self.client.get_power_status() self.is_on = power_status == "active" @@ -151,8 +156,6 @@ class BraviaTVCoordinator(DataUpdateCoordinator[None]): _LOGGER.debug("Update skipped, Bravia API service is reloading") return raise UpdateFailed("Error communicating with device") from err - except BraviaTVAuthError as err: - raise ConfigEntryAuthFailed from err except (BraviaTVConnectionError, BraviaTVConnectionTimeout, BraviaTVTurnedOff): self.is_on = False self.connected = False From 532ab12a48b6832180599088250fc23446a45d1e Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 30 Nov 2022 12:20:21 -0800 Subject: [PATCH 0925/1033] Local calendar integration (#79601) --- CODEOWNERS | 2 + homeassistant/components/calendar/__init__.py | 154 ++++- homeassistant/components/calendar/const.py | 24 + .../components/local_calendar/__init__.py | 41 ++ .../components/local_calendar/calendar.py | 149 +++++ .../components/local_calendar/config_flow.py | 36 + .../components/local_calendar/const.py | 5 + .../components/local_calendar/manifest.json | 10 + .../components/local_calendar/store.py | 38 ++ .../components/local_calendar/strings.json | 12 + .../local_calendar/translations/en.json | 12 + homeassistant/generated/config_flows.py | 1 + homeassistant/generated/integrations.json | 6 + requirements_all.txt | 3 + requirements_test.txt | 1 + requirements_test_all.txt | 3 + tests/components/caldav/test_calendar.py | 3 + tests/components/local_calendar/__init__.py | 1 + .../local_calendar/test_calendar.py | 614 ++++++++++++++++++ .../local_calendar/test_config_flow.py | 35 + .../components/twentemilieu/test_calendar.py | 3 + 21 files changed, 1152 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/calendar/const.py create mode 100644 homeassistant/components/local_calendar/__init__.py create mode 100644 homeassistant/components/local_calendar/calendar.py create mode 100644 homeassistant/components/local_calendar/config_flow.py create mode 100644 homeassistant/components/local_calendar/const.py create mode 100644 homeassistant/components/local_calendar/manifest.json create mode 100644 homeassistant/components/local_calendar/store.py create mode 100644 homeassistant/components/local_calendar/strings.json create mode 100644 homeassistant/components/local_calendar/translations/en.json create mode 100644 tests/components/local_calendar/__init__.py create mode 100644 tests/components/local_calendar/test_calendar.py create mode 100644 tests/components/local_calendar/test_config_flow.py diff --git a/CODEOWNERS b/CODEOWNERS index 9e1cd87ca80..8920d85defe 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -639,6 +639,8 @@ build.json @home-assistant/supervisor /tests/components/litterrobot/ @natekspencer @tkdrob /homeassistant/components/livisi/ @StefanIacobLivisi /tests/components/livisi/ @StefanIacobLivisi +/homeassistant/components/local_calendar/ @allenporter +/tests/components/local_calendar/ @allenporter /homeassistant/components/local_ip/ @issacg /tests/components/local_ip/ @issacg /homeassistant/components/lock/ @home-assistant/core diff --git a/homeassistant/components/calendar/__init__.py b/homeassistant/components/calendar/__init__.py index cfc09df667a..b5d605b9f6f 100644 --- a/homeassistant/components/calendar/__init__.py +++ b/homeassistant/components/calendar/__init__.py @@ -10,12 +10,17 @@ import re from typing import Any, cast, final from aiohttp import web +from dateutil.rrule import rrulestr +import voluptuous as vol -from homeassistant.components import frontend, http +from homeassistant.components import frontend, http, websocket_api +from homeassistant.components.websocket_api import ERR_NOT_FOUND, ERR_NOT_SUPPORTED +from homeassistant.components.websocket_api.connection import ActiveConnection from homeassistant.config_entries import ConfigEntry from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.config_validation import ( # noqa: F401 PLATFORM_SCHEMA, PLATFORM_SCHEMA_BASE, @@ -27,12 +32,29 @@ from homeassistant.helpers.template import DATE_STR_FORMAT from homeassistant.helpers.typing import ConfigType from homeassistant.util import dt +from .const import ( + CONF_EVENT, + EVENT_DESCRIPTION, + EVENT_END, + EVENT_RECURRENCE_ID, + EVENT_RECURRENCE_RANGE, + EVENT_RRULE, + EVENT_START, + EVENT_SUMMARY, + EVENT_UID, + CalendarEntityFeature, +) + _LOGGER = logging.getLogger(__name__) DOMAIN = "calendar" ENTITY_ID_FORMAT = DOMAIN + ".{}" SCAN_INTERVAL = datetime.timedelta(seconds=60) +# Don't support rrules more often than daily +VALID_FREQS = {"DAILY", "WEEKLY", "MONTHLY", "YEARLY"} + + # mypy: disallow-any-generics @@ -49,6 +71,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass, "calendar", "calendar", "hass:calendar" ) + websocket_api.async_register_command(hass, handle_calendar_event_create) + websocket_api.async_register_command(hass, handle_calendar_event_delete) + await component.async_setup(config) return True @@ -88,6 +113,10 @@ class CalendarEvent: description: str | None = None location: str | None = None + uid: str | None = None + recurrence_id: str | None = None + rrule: str | None = None + @property def start_datetime_local(self) -> datetime.datetime: """Return event start time as a local datetime.""" @@ -183,6 +212,30 @@ def is_offset_reached( return start + offset_time <= dt.now(start.tzinfo) +def _validate_rrule(value: Any) -> str: + """Validate a recurrence rule string.""" + if value is None: + raise vol.Invalid("rrule value is None") + + if not isinstance(value, str): + raise vol.Invalid("rrule value expected a string") + + try: + rrulestr(value) + except ValueError as err: + raise vol.Invalid(f"Invalid rrule: {str(err)}") from err + + # Example format: FREQ=DAILY;UNTIL=... + rule_parts = dict(s.split("=", 1) for s in value.split(";")) + if not (freq := rule_parts.get("FREQ")): + raise vol.Invalid("rrule did not contain FREQ") + + if freq not in VALID_FREQS: + raise vol.Invalid(f"Invalid frequency for rule: {value}") + + return str(value) + + class CalendarEntity(Entity): """Base class for calendar event entities.""" @@ -230,6 +283,19 @@ class CalendarEntity(Entity): """Return calendar events within a datetime range.""" raise NotImplementedError() + async def async_create_event(self, **kwargs: Any) -> None: + """Add a new event to calendar.""" + raise NotImplementedError() + + async def async_delete_event( + self, + uid: str, + recurrence_id: str | None = None, + recurrence_range: str | None = None, + ) -> None: + """Delete an event on the calendar.""" + raise NotImplementedError() + class CalendarEventView(http.HomeAssistantView): """View to retrieve calendar content.""" @@ -297,3 +363,89 @@ class CalendarListView(http.HomeAssistantView): calendar_list.append({"name": state.name, "entity_id": entity.entity_id}) return self.json(sorted(calendar_list, key=lambda x: cast(str, x["name"]))) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "calendar/event/create", + vol.Required("entity_id"): cv.entity_id, + vol.Required(CONF_EVENT): { + vol.Required(EVENT_START): vol.Any(cv.date, cv.datetime), + vol.Required(EVENT_END): vol.Any(cv.date, cv.datetime), + vol.Required(EVENT_SUMMARY): cv.string, + vol.Optional(EVENT_DESCRIPTION): cv.string, + vol.Optional(EVENT_RRULE): _validate_rrule, + }, + } +) +@websocket_api.async_response +async def handle_calendar_event_create( + hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] +) -> None: + """Handle creation of a calendar event.""" + component: EntityComponent[CalendarEntity] = hass.data[DOMAIN] + if not (entity := component.get_entity(msg["entity_id"])): + connection.send_error(msg["id"], ERR_NOT_FOUND, "Entity not found") + return + + if ( + not entity.supported_features + or not entity.supported_features & CalendarEntityFeature.CREATE_EVENT + ): + connection.send_message( + websocket_api.error_message( + msg["id"], ERR_NOT_SUPPORTED, "Calendar does not support event creation" + ) + ) + return + + try: + await entity.async_create_event(**msg[CONF_EVENT]) + except HomeAssistantError as ex: + connection.send_error(msg["id"], "failed", str(ex)) + else: + connection.send_result(msg["id"]) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "calendar/event/delete", + vol.Required("entity_id"): cv.entity_id, + vol.Required(EVENT_UID): cv.string, + vol.Optional(EVENT_RECURRENCE_ID): cv.string, + vol.Optional(EVENT_RECURRENCE_RANGE): cv.string, + } +) +@websocket_api.async_response +async def handle_calendar_event_delete( + hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] +) -> None: + """Handle delete of a calendar event.""" + + component: EntityComponent[CalendarEntity] = hass.data[DOMAIN] + if not (entity := component.get_entity(msg["entity_id"])): + connection.send_error(msg["id"], ERR_NOT_FOUND, "Entity not found") + return + + if ( + not entity.supported_features + or not entity.supported_features & CalendarEntityFeature.DELETE_EVENT + ): + connection.send_message( + websocket_api.error_message( + msg["id"], ERR_NOT_SUPPORTED, "Calendar does not support event deletion" + ) + ) + return + + try: + await entity.async_delete_event( + msg[EVENT_UID], + recurrence_id=msg.get(EVENT_RECURRENCE_ID), + recurrence_range=msg.get(EVENT_RECURRENCE_RANGE), + ) + except (HomeAssistantError, ValueError) as ex: + _LOGGER.error("Error handling Calendar Event call: %s", ex) + connection.send_error(msg["id"], "failed", str(ex)) + else: + connection.send_result(msg["id"]) diff --git a/homeassistant/components/calendar/const.py b/homeassistant/components/calendar/const.py new file mode 100644 index 00000000000..adee190200a --- /dev/null +++ b/homeassistant/components/calendar/const.py @@ -0,0 +1,24 @@ +"""Constants for calendar components.""" + +from enum import IntEnum + +CONF_EVENT = "event" + + +class CalendarEntityFeature(IntEnum): + """Supported features of the calendar entity.""" + + CREATE_EVENT = 1 + DELETE_EVENT = 2 + + +# rfc5545 fields +EVENT_UID = "uid" +EVENT_START = "dtstart" +EVENT_END = "dtend" +EVENT_SUMMARY = "summary" +EVENT_DESCRIPTION = "description" +EVENT_LOCATION = "location" +EVENT_RECURRENCE_ID = "recurrence_id" +EVENT_RECURRENCE_RANGE = "recurrence_range" +EVENT_RRULE = "rrule" diff --git a/homeassistant/components/local_calendar/__init__.py b/homeassistant/components/local_calendar/__init__.py new file mode 100644 index 00000000000..33ad67cc81a --- /dev/null +++ b/homeassistant/components/local_calendar/__init__.py @@ -0,0 +1,41 @@ +"""The Local Calendar integration.""" +from __future__ import annotations + +import logging +from pathlib import Path + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.util import slugify + +from .const import CONF_CALENDAR_NAME, DOMAIN +from .store import LocalCalendarStore + +_LOGGER = logging.getLogger(__name__) + + +PLATFORMS: list[Platform] = [Platform.CALENDAR] + +STORAGE_PATH = ".storage/local_calendar.{key}.ics" + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Local Calendar from a config entry.""" + hass.data.setdefault(DOMAIN, {}) + + key = slugify(entry.data[CONF_CALENDAR_NAME]) + path = Path(hass.config.path(STORAGE_PATH.format(key=key))) + hass.data[DOMAIN][entry.entry_id] = LocalCalendarStore(hass, path) + + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_ok diff --git a/homeassistant/components/local_calendar/calendar.py b/homeassistant/components/local_calendar/calendar.py new file mode 100644 index 00000000000..79d16634883 --- /dev/null +++ b/homeassistant/components/local_calendar/calendar.py @@ -0,0 +1,149 @@ +"""Calendar platform for a Local Calendar.""" + +from __future__ import annotations + +from datetime import datetime +import logging +from typing import Any + +from ical.calendar import Calendar +from ical.calendar_stream import IcsCalendarStream +from ical.event import Event +from ical.store import EventStore +from ical.types import Range, Recur + +from homeassistant.components.calendar import ( + EVENT_DESCRIPTION, + EVENT_END, + EVENT_RRULE, + EVENT_START, + EVENT_SUMMARY, + CalendarEntity, + CalendarEntityFeature, + CalendarEvent, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.util import dt as dt_util + +from .const import CONF_CALENDAR_NAME, DOMAIN +from .store import LocalCalendarStore + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the local calendar platform.""" + store = hass.data[DOMAIN][config_entry.entry_id] + ics = await store.async_load() + calendar = IcsCalendarStream.calendar_from_ics(ics) + + name = config_entry.data[CONF_CALENDAR_NAME] + entity = LocalCalendarEntity(store, calendar, name, unique_id=config_entry.entry_id) + async_add_entities([entity], True) + + +class LocalCalendarEntity(CalendarEntity): + """A calendar entity backed by a local iCalendar file.""" + + _attr_has_entity_name = True + _attr_supported_features = ( + CalendarEntityFeature.CREATE_EVENT | CalendarEntityFeature.DELETE_EVENT + ) + + def __init__( + self, + store: LocalCalendarStore, + calendar: Calendar, + name: str, + unique_id: str, + ) -> None: + """Initialize LocalCalendarEntity.""" + self._store = store + self._calendar = calendar + self._event: CalendarEvent | None = None + self._attr_name = name.capitalize() + self._attr_unique_id = unique_id + + @property + def event(self) -> CalendarEvent | None: + """Return the next upcoming event.""" + return self._event + + async def async_get_events( + self, hass: HomeAssistant, start_date: datetime, end_date: datetime + ) -> list[CalendarEvent]: + """Get all events in a specific time frame.""" + events = self._calendar.timeline_tz(dt_util.DEFAULT_TIME_ZONE).overlapping( + dt_util.as_local(start_date), + dt_util.as_local(end_date), + ) + return [_get_calendar_event(event) for event in events] + + async def async_update(self) -> None: + """Update entity state with the next upcoming event.""" + events = self._calendar.timeline_tz(dt_util.DEFAULT_TIME_ZONE).active_after( + dt_util.now() + ) + if event := next(events, None): + self._event = _get_calendar_event(event) + else: + self._event = None + + async def _async_store(self) -> None: + """Persist the calendar to disk.""" + content = IcsCalendarStream.calendar_to_ics(self._calendar) + await self._store.async_store(content) + + async def async_create_event(self, **kwargs: Any) -> None: + """Add a new event to calendar.""" + event = Event.parse_obj( + { + EVENT_SUMMARY: kwargs[EVENT_SUMMARY], + EVENT_START: kwargs[EVENT_START], + EVENT_END: kwargs[EVENT_END], + EVENT_DESCRIPTION: kwargs.get(EVENT_DESCRIPTION), + } + ) + if rrule := kwargs.get(EVENT_RRULE): + event.rrule = Recur.from_rrule(rrule) + + EventStore(self._calendar).add(event) + await self._async_store() + await self.async_update_ha_state(force_refresh=True) + + async def async_delete_event( + self, + uid: str, + recurrence_id: str | None = None, + recurrence_range: str | None = None, + ) -> None: + """Delete an event on the calendar.""" + range_value: Range = Range.NONE + if recurrence_range == Range.THIS_AND_FUTURE: + range_value = Range.THIS_AND_FUTURE + EventStore(self._calendar).delete( + uid, + recurrence_id=recurrence_id, + recurrence_range=range_value, + ) + await self._async_store() + await self.async_update_ha_state(force_refresh=True) + + +def _get_calendar_event(event: Event) -> CalendarEvent: + """Return a CalendarEvent from an API event.""" + return CalendarEvent( + summary=event.summary, + start=event.start, + end=event.end, + description=event.description, + uid=event.uid, + rrule=event.rrule.as_rrule_str() if event.rrule else None, + recurrence_id=event.recurrence_id, + ) diff --git a/homeassistant/components/local_calendar/config_flow.py b/homeassistant/components/local_calendar/config_flow.py new file mode 100644 index 00000000000..2bde06820b6 --- /dev/null +++ b/homeassistant/components/local_calendar/config_flow.py @@ -0,0 +1,36 @@ +"""Config flow for Local Calendar integration.""" +from __future__ import annotations + +from typing import Any + +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.data_entry_flow import FlowResult + +from .const import CONF_CALENDAR_NAME, DOMAIN + +STEP_USER_DATA_SCHEMA = vol.Schema( + { + vol.Required(CONF_CALENDAR_NAME): str, + } +) + + +class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for Local Calendar.""" + + VERSION = 1 + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the initial step.""" + if user_input is None: + return self.async_show_form( + step_id="user", data_schema=STEP_USER_DATA_SCHEMA + ) + + return self.async_create_entry( + title=user_input[CONF_CALENDAR_NAME], data=user_input + ) diff --git a/homeassistant/components/local_calendar/const.py b/homeassistant/components/local_calendar/const.py new file mode 100644 index 00000000000..49cd5dc22a4 --- /dev/null +++ b/homeassistant/components/local_calendar/const.py @@ -0,0 +1,5 @@ +"""Constants for the Local Calendar integration.""" + +DOMAIN = "local_calendar" + +CONF_CALENDAR_NAME = "calendar_name" diff --git a/homeassistant/components/local_calendar/manifest.json b/homeassistant/components/local_calendar/manifest.json new file mode 100644 index 00000000000..016d2fee052 --- /dev/null +++ b/homeassistant/components/local_calendar/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "local_calendar", + "name": "Local Calendar", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/local_calendar", + "requirements": ["ical==4.1.1"], + "codeowners": ["@allenporter"], + "iot_class": "local_polling", + "loggers": ["ical"] +} diff --git a/homeassistant/components/local_calendar/store.py b/homeassistant/components/local_calendar/store.py new file mode 100644 index 00000000000..3955717a066 --- /dev/null +++ b/homeassistant/components/local_calendar/store.py @@ -0,0 +1,38 @@ +"""Local storage for the Local Calendar integration.""" + +import asyncio +from pathlib import Path + +from homeassistant.core import HomeAssistant + +STORAGE_PATH = ".storage/{key}.ics" + + +class LocalCalendarStore: + """Local calendar storage.""" + + def __init__(self, hass: HomeAssistant, path: Path) -> None: + """Initialize LocalCalendarStore.""" + self._hass = hass + self._path = path + self._lock = asyncio.Lock() + + async def async_load(self) -> str: + """Load the calendar from disk.""" + async with self._lock: + return await self._hass.async_add_executor_job(self._load) + + def _load(self) -> str: + """Load the calendar from disk.""" + if not self._path.exists(): + return "" + return self._path.read_text() + + async def async_store(self, ics_content: str) -> None: + """Persist the calendar to storage.""" + async with self._lock: + await self._hass.async_add_executor_job(self._store, ics_content) + + def _store(self, ics_content: str) -> None: + """Persist the calendar to storage.""" + self._path.write_text(ics_content) diff --git a/homeassistant/components/local_calendar/strings.json b/homeassistant/components/local_calendar/strings.json new file mode 100644 index 00000000000..f49c92e5438 --- /dev/null +++ b/homeassistant/components/local_calendar/strings.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "description": "Please choose a name for your new calendar", + "data": { + "calendar_name": "Calendar Name" + } + } + } + } +} diff --git a/homeassistant/components/local_calendar/translations/en.json b/homeassistant/components/local_calendar/translations/en.json new file mode 100644 index 00000000000..4bceb75616a --- /dev/null +++ b/homeassistant/components/local_calendar/translations/en.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "Calendar Name" + }, + "description": "Please choose a name for your new calendar" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 4d7ed69257d..97c7b925378 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -226,6 +226,7 @@ FLOWS = { "litejet", "litterrobot", "livisi", + "local_calendar", "local_ip", "locative", "logi_circle", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index ebd7d4f934e..5b24a1ae02e 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -2878,6 +2878,12 @@ "config_flow": false, "iot_class": "cloud_push" }, + "local_calendar": { + "name": "Local Calendar", + "integration_type": "hub", + "config_flow": true, + "iot_class": "local_polling" + }, "local_file": { "name": "Local File", "integration_type": "hub", diff --git a/requirements_all.txt b/requirements_all.txt index 449b5541365..2f22c833df4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -925,6 +925,9 @@ ibm-watson==5.2.2 # homeassistant.components.watson_iot ibmiotf==0.3.4 +# homeassistant.components.local_calendar +ical==4.1.1 + # homeassistant.components.ping icmplib==3.0 diff --git a/requirements_test.txt b/requirements_test.txt index 075eefcf892..2bed839649c 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -40,6 +40,7 @@ types-decorator==0.1.7 types-enum34==0.1.8 types-ipaddress==0.1.5 types-pkg-resources==0.1.3 +types-python-dateutil==2.8.19.2 types-python-slugify==0.1.2 types-pytz==2021.1.2 types-PyYAML==5.4.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fbcec3f9af2..24767a1d0fa 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -690,6 +690,9 @@ iaqualink==0.5.0 # homeassistant.components.ibeacon ibeacon_ble==1.0.1 +# homeassistant.components.local_calendar +ical==4.1.1 + # homeassistant.components.ping icmplib==3.0 diff --git a/tests/components/caldav/test_calendar.py b/tests/components/caldav/test_calendar.py index f35fa609e4a..b936a02db87 100644 --- a/tests/components/caldav/test_calendar.py +++ b/tests/components/caldav/test_calendar.py @@ -939,5 +939,8 @@ async def test_get_events_custom_calendars(hass, calendar, get_api_events): "summary": "This is a normal event", "location": "Hamburg", "description": "Surprisingly rainy", + "uid": None, + "recurrence_id": None, + "rrule": None, } ] diff --git a/tests/components/local_calendar/__init__.py b/tests/components/local_calendar/__init__.py new file mode 100644 index 00000000000..b7326472757 --- /dev/null +++ b/tests/components/local_calendar/__init__.py @@ -0,0 +1 @@ +"""Tests for the Local Calendar integration.""" diff --git a/tests/components/local_calendar/test_calendar.py b/tests/components/local_calendar/test_calendar.py new file mode 100644 index 00000000000..77632c8bfe1 --- /dev/null +++ b/tests/components/local_calendar/test_calendar.py @@ -0,0 +1,614 @@ +"""Tests for calendar platform of local calendar.""" + +from collections.abc import Awaitable, Callable +import datetime +from http import HTTPStatus +from pathlib import Path +from typing import Any +from unittest.mock import patch +import urllib + +from aiohttp import ClientSession, ClientWebSocketResponse +import pytest + +from homeassistant.components.local_calendar import LocalCalendarStore +from homeassistant.components.local_calendar.const import CONF_CALENDAR_NAME, DOMAIN +from homeassistant.const import STATE_OFF, STATE_ON +from homeassistant.core import HomeAssistant +from homeassistant.helpers.template import DATE_STR_FORMAT +from homeassistant.setup import async_setup_component +import homeassistant.util.dt as dt_util + +from tests.common import MockConfigEntry + +CALENDAR_NAME = "Light Schedule" +FRIENDLY_NAME = "Light schedule" +TEST_ENTITY = "calendar.light_schedule" + + +class FakeStore(LocalCalendarStore): + """Mock storage implementation.""" + + def __init__(self, hass: HomeAssistant, path: Path) -> None: + """Initialize FakeStore.""" + super().__init__(hass, path) + self._content = "" + + def _load(self) -> str: + """Read from calendar storage.""" + return self._content + + def _store(self, ics_content: str) -> None: + """Persist the calendar storage.""" + self._content = ics_content + + +@pytest.fixture(name="store", autouse=True) +def mock_store() -> None: + """Test cleanup, remove any media storage persisted during the test.""" + + def new_store(hass: HomeAssistant, path: Path) -> FakeStore: + return FakeStore(hass, path) + + with patch( + "homeassistant.components.local_calendar.LocalCalendarStore", new=new_store + ): + yield + + +@pytest.fixture(name="time_zone") +def mock_time_zone() -> str: + """Fixture for time zone to use in tests.""" + # Set our timezone to CST/Regina so we can check calculations + # This keeps UTC-6 all year round + return "America/Regina" + + +@pytest.fixture(autouse=True) +def set_time_zone(hass: HomeAssistant, time_zone: str): + """Set the time zone for the tests.""" + # Set our timezone to CST/Regina so we can check calculations + # This keeps UTC-6 all year round + hass.config.set_time_zone(time_zone) + + +@pytest.fixture(name="config_entry") +def mock_config_entry() -> MockConfigEntry: + """Fixture for mock configuration entry.""" + return MockConfigEntry(domain=DOMAIN, data={CONF_CALENDAR_NAME: CALENDAR_NAME}) + + +@pytest.fixture(name="setup_integration") +async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None: + """Set up the integration.""" + config_entry.add_to_hass(hass) + assert await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + + +GetEventsFn = Callable[[str, str], Awaitable[dict[str, Any]]] + + +@pytest.fixture(name="get_events") +def get_events_fixture( + hass_client: Callable[..., Awaitable[ClientSession]] +) -> GetEventsFn: + """Fetch calendar events from the HTTP API.""" + + async def _fetch(start: str, end: str) -> None: + client = await hass_client() + response = await client.get( + f"/api/calendars/{TEST_ENTITY}?start={urllib.parse.quote(start)}&end={urllib.parse.quote(end)}" + ) + assert response.status == HTTPStatus.OK + return await response.json() + + return _fetch + + +def event_fields(data: dict[str, str]) -> dict[str, str]: + """Filter event API response to minimum fields.""" + return { + k: data.get(k) + for k in ["summary", "start", "end", "recurrence_id"] + if data.get(k) + } + + +class Client: + """Test client with helper methods for calendar websocket.""" + + def __init__(self, client): + """Initialize Client.""" + self.client = client + self.id = 0 + + async def cmd(self, cmd: str, payload: dict[str, Any] = None) -> dict[str, Any]: + """Send a command and receive the json result.""" + self.id += 1 + await self.client.send_json( + { + "id": self.id, + "type": f"calendar/event/{cmd}", + **(payload if payload is not None else {}), + } + ) + resp = await self.client.receive_json() + assert resp.get("id") == self.id + return resp + + async def cmd_result(self, cmd: str, payload: dict[str, Any] = None) -> Any: + """Send a command and parse the result.""" + resp = await self.cmd(cmd, payload) + assert resp.get("success") + assert resp.get("type") == "result" + return resp.get("result") + + +ClientFixture = Callable[[], Awaitable[Client]] + + +@pytest.fixture +async def ws_client( + hass: HomeAssistant, + hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]], +) -> ClientFixture: + """Fixture for creating the test websocket client.""" + + async def create_client() -> Client: + ws_client = await hass_ws_client(hass) + return Client(ws_client) + + return create_client + + +async def test_empty_calendar( + hass: HomeAssistant, setup_integration: None, get_events: GetEventsFn +): + """Test querying the API and fetching events.""" + events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00") + assert len(events) == 0 + + state = hass.states.get(TEST_ENTITY) + assert state.name == FRIENDLY_NAME + assert state.state == STATE_OFF + assert dict(state.attributes) == { + "friendly_name": FRIENDLY_NAME, + "supported_features": 3, + } + + +async def test_api_date_time_event( + ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn +): + """Test an event with a start/end date time.""" + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-14T17:00:00+00:00", + "dtend": "1997-07-15T04:00:00+00:00", + }, + }, + ) + + events = await get_events("1997-07-14T00:00:00Z", "1997-07-16T00:00:00Z") + assert list(map(event_fields, events)) == [ + { + "summary": "Bastille Day Party", + "start": {"dateTime": "1997-07-14T11:00:00-06:00"}, + "end": {"dateTime": "1997-07-14T22:00:00-06:00"}, + } + ] + + # Time range before event + events = await get_events("1997-07-13T00:00:00Z", "1997-07-14T16:00:00Z") + assert len(events) == 0 + # Time range after event + events = await get_events("1997-07-15T05:00:00Z", "1997-07-15T06:00:00Z") + assert len(events) == 0 + + # Overlap with event start + events = await get_events("1997-07-13T00:00:00Z", "1997-07-14T18:00:00Z") + assert len(events) == 1 + # Overlap with event end + events = await get_events("1997-07-15T03:00:00Z", "1997-07-15T06:00:00Z") + assert len(events) == 1 + + +async def test_api_date_event( + ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn +): + """Test an event with a start/end date all day event.""" + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Festival International de Jazz de Montreal", + "dtstart": "2007-06-28", + "dtend": "2007-07-09", + }, + }, + ) + + events = await get_events("2007-06-20T00:00:00", "2007-07-20T00:00:00") + assert list(map(event_fields, events)) == [ + { + "summary": "Festival International de Jazz de Montreal", + "start": {"date": "2007-06-28"}, + "end": {"date": "2007-07-09"}, + } + ] + + # Time range before event (timezone is -6) + events = await get_events("2007-06-26T00:00:00Z", "2007-06-28T01:00:00Z") + assert len(events) == 0 + # Time range after event + events = await get_events("2007-07-10T00:00:00Z", "2007-07-11T00:00:00Z") + assert len(events) == 0 + + # Overlap with event start (timezone is -6) + events = await get_events("2007-06-26T00:00:00Z", "2007-06-28T08:00:00Z") + assert len(events) == 1 + # Overlap with event end + events = await get_events("2007-07-09T00:00:00Z", "2007-07-11T00:00:00Z") + assert len(events) == 1 + + +async def test_active_event( + hass: HomeAssistant, + ws_client: ClientFixture, + setup_integration: None, +): + """Test an event with a start/end date time.""" + start = dt_util.now() - datetime.timedelta(minutes=30) + end = dt_util.now() + datetime.timedelta(minutes=30) + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Evening lights", + "dtstart": start.isoformat(), + "dtend": end.isoformat(), + }, + }, + ) + + state = hass.states.get(TEST_ENTITY) + assert state.name == FRIENDLY_NAME + assert state.state == STATE_ON + assert dict(state.attributes) == { + "friendly_name": FRIENDLY_NAME, + "message": "Evening lights", + "all_day": False, + "description": "", + "location": "", + "start_time": start.strftime(DATE_STR_FORMAT), + "end_time": end.strftime(DATE_STR_FORMAT), + "supported_features": 3, + } + + +async def test_upcoming_event( + hass: HomeAssistant, + ws_client: ClientFixture, + setup_integration: None, +): + """Test an event with a start/end date time.""" + start = dt_util.now() + datetime.timedelta(days=1) + end = dt_util.now() + datetime.timedelta(days=1, hours=1) + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Evening lights", + "dtstart": start.isoformat(), + "dtend": end.isoformat(), + }, + }, + ) + + state = hass.states.get(TEST_ENTITY) + assert state.name == FRIENDLY_NAME + assert state.state == STATE_OFF + assert dict(state.attributes) == { + "friendly_name": FRIENDLY_NAME, + "message": "Evening lights", + "all_day": False, + "description": "", + "location": "", + "message": "Evening lights", + "start_time": start.strftime(DATE_STR_FORMAT), + "end_time": end.strftime(DATE_STR_FORMAT), + "supported_features": 3, + } + + +async def test_recurring_event( + ws_client: ClientFixture, + setup_integration: None, + hass: HomeAssistant, + get_events: GetEventsFn, +): + """Test an event with a recurrence rule.""" + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Monday meeting", + "dtstart": "2022-08-29T09:00:00", + "dtend": "2022-08-29T10:00:00", + "rrule": "FREQ=WEEKLY", + }, + }, + ) + + events = await get_events("2022-08-20T00:00:00", "2022-09-20T00:00:00") + assert list(map(event_fields, events)) == [ + { + "summary": "Monday meeting", + "start": {"dateTime": "2022-08-29T09:00:00-06:00"}, + "end": {"dateTime": "2022-08-29T10:00:00-06:00"}, + "recurrence_id": "20220829T090000", + }, + { + "summary": "Monday meeting", + "start": {"dateTime": "2022-09-05T09:00:00-06:00"}, + "end": {"dateTime": "2022-09-05T10:00:00-06:00"}, + "recurrence_id": "20220905T090000", + }, + { + "summary": "Monday meeting", + "start": {"dateTime": "2022-09-12T09:00:00-06:00"}, + "end": {"dateTime": "2022-09-12T10:00:00-06:00"}, + "recurrence_id": "20220912T090000", + }, + { + "summary": "Monday meeting", + "start": {"dateTime": "2022-09-19T09:00:00-06:00"}, + "end": {"dateTime": "2022-09-19T10:00:00-06:00"}, + "recurrence_id": "20220919T090000", + }, + ] + + +async def test_websocket_delete( + ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn +): + """Test websocket delete command.""" + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-14T17:00:00+00:00", + "dtend": "1997-07-15T04:00:00+00:00", + }, + }, + ) + + events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00") + assert list(map(event_fields, events)) == [ + { + "summary": "Bastille Day Party", + "start": {"dateTime": "1997-07-14T11:00:00-06:00"}, + "end": {"dateTime": "1997-07-14T22:00:00-06:00"}, + } + ] + uid = events[0]["uid"] + + # Delete the event + await client.cmd_result( + "delete", + { + "entity_id": TEST_ENTITY, + "uid": uid, + }, + ) + events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00") + assert list(map(event_fields, events)) == [] + + +async def test_websocket_delete_recurring( + ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn +): + """Test deleting a recurring event.""" + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Morning Routine", + "dtstart": "2022-08-22T08:30:00", + "dtend": "2022-08-22T09:00:00", + "rrule": "FREQ=DAILY", + }, + }, + ) + + events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00") + assert list(map(event_fields, events)) == [ + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-22T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-22T09:00:00-06:00"}, + "recurrence_id": "20220822T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-23T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-23T09:00:00-06:00"}, + "recurrence_id": "20220823T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-24T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-24T09:00:00-06:00"}, + "recurrence_id": "20220824T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-25T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-25T09:00:00-06:00"}, + "recurrence_id": "20220825T083000", + }, + ] + uid = events[0]["uid"] + assert [event["uid"] for event in events] == [uid] * 4 + + # Cancel a single instance and confirm it was removed + await client.cmd_result( + "delete", + { + "entity_id": TEST_ENTITY, + "uid": uid, + "recurrence_id": "20220824T083000", + }, + ) + events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00") + assert list(map(event_fields, events)) == [ + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-22T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-22T09:00:00-06:00"}, + "recurrence_id": "20220822T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-23T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-23T09:00:00-06:00"}, + "recurrence_id": "20220823T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-25T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-25T09:00:00-06:00"}, + "recurrence_id": "20220825T083000", + }, + ] + + # Delete all and future and confirm multiple were removed + await client.cmd_result( + "delete", + { + "entity_id": TEST_ENTITY, + "uid": uid, + "recurrence_id": "20220823T083000", + "recurrence_range": "THISANDFUTURE", + }, + ) + events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00") + assert list(map(event_fields, events)) == [ + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-22T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-22T09:00:00-06:00"}, + "recurrence_id": "20220822T083000", + }, + ] + + +@pytest.mark.parametrize( + "rrule", + [ + "FREQ=SECONDLY", + "FREQ=MINUTELY", + "FREQ=HOURLY", + "invalid", + "", + ], +) +async def test_invalid_rrule( + ws_client: ClientFixture, + setup_integration: None, + hass: HomeAssistant, + get_events: GetEventsFn, + rrule: str, +): + """Test an event with a recurrence rule.""" + client = await ws_client() + resp = await client.cmd( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Monday meeting", + "dtstart": "2022-08-29T09:00:00", + "dtend": "2022-08-29T10:00:00", + "rrule": rrule, + }, + }, + ) + assert not resp.get("success") + assert "error" in resp + assert resp.get("error").get("code") == "invalid_format" + + +@pytest.mark.parametrize( + "time_zone,event_order", + [ + ("America/Los_Angeles", ["One", "Two", "All Day Event"]), + ("America/Regina", ["One", "Two", "All Day Event"]), + ("UTC", ["One", "All Day Event", "Two"]), + ("Asia/Tokyo", ["All Day Event", "One", "Two"]), + ], +) +async def test_all_day_iter_order( + hass: HomeAssistant, + ws_client: ClientFixture, + setup_integration: None, + get_events: GetEventsFn, + event_order: list[str], +): + """Test the sort order of an all day events depending on the time zone.""" + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "All Day Event", + "dtstart": "2022-10-08", + "dtend": "2022-10-09", + }, + }, + ) + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "One", + "dtstart": "2022-10-07T23:00:00+00:00", + "dtend": "2022-10-07T23:30:00+00:00", + }, + }, + ) + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Two", + "dtstart": "2022-10-08T01:00:00+00:00", + "dtend": "2022-10-08T02:00:00+00:00", + }, + }, + ) + + events = await get_events("2022-10-06T00:00:00Z", "2022-10-09T00:00:00Z") + assert [event["summary"] for event in events] == event_order diff --git a/tests/components/local_calendar/test_config_flow.py b/tests/components/local_calendar/test_config_flow.py new file mode 100644 index 00000000000..25049326762 --- /dev/null +++ b/tests/components/local_calendar/test_config_flow.py @@ -0,0 +1,35 @@ +"""Test the Local Calendar config flow.""" +from unittest.mock import patch + +from homeassistant import config_entries +from homeassistant.components.local_calendar.const import CONF_CALENDAR_NAME, DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType + + +async def test_form(hass: HomeAssistant) -> None: + """Test we get the form.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert result["errors"] is None + + with patch( + "homeassistant.components.local_calendar.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_CALENDAR_NAME: "My Calendar", + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["title"] == "My Calendar" + assert result2["data"] == { + CONF_CALENDAR_NAME: "My Calendar", + } + assert len(mock_setup_entry.mock_calls) == 1 diff --git a/tests/components/twentemilieu/test_calendar.py b/tests/components/twentemilieu/test_calendar.py index 11f8a1abd75..79c24e5970d 100644 --- a/tests/components/twentemilieu/test_calendar.py +++ b/tests/components/twentemilieu/test_calendar.py @@ -81,4 +81,7 @@ async def test_api_events( "summary": "Christmas tree pickup", "description": None, "location": None, + "uid": None, + "recurrence_id": None, + "rrule": None, } From 4239923ea2e4dc8872b4eafc95127f68eb03b411 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 30 Nov 2022 10:40:59 -1000 Subject: [PATCH 0926/1033] Fix memory leak in onvif (#83006) fixes https://github.com/home-assistant/core/issues/82504 --- homeassistant/components/onvif/device.py | 4 +-- homeassistant/components/onvif/event.py | 36 ++++++++++++++++-------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/onvif/device.py b/homeassistant/components/onvif/device.py index d7f50f6744e..8c000852c48 100644 --- a/homeassistant/components/onvif/device.py +++ b/homeassistant/components/onvif/device.py @@ -11,7 +11,7 @@ from httpx import RequestError import onvif from onvif import ONVIFCamera from onvif.exceptions import ONVIFError -from zeep.exceptions import Fault +from zeep.exceptions import Fault, XMLParseError from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -284,7 +284,7 @@ class ONVIFDevice: snapshot = media_capabilities and media_capabilities.SnapshotUri pullpoint = False - with suppress(ONVIFError, Fault, RequestError): + with suppress(ONVIFError, Fault, RequestError, XMLParseError): pullpoint = await self.events.async_start() ptz = False diff --git a/homeassistant/components/onvif/event.py b/homeassistant/components/onvif/event.py index 710e84f93bf..8b09ee99079 100644 --- a/homeassistant/components/onvif/event.py +++ b/homeassistant/components/onvif/event.py @@ -5,6 +5,7 @@ import asyncio from collections.abc import Callable from contextlib import suppress import datetime as dt +from logging import DEBUG, WARNING from httpx import RemoteProtocolError, TransportError from onvif import ONVIFCamera, ONVIFService @@ -20,7 +21,6 @@ from .parsers import PARSERS UNHANDLED_TOPICS = set() SUBSCRIPTION_ERRORS = ( - XMLParseError, Fault, asyncio.TimeoutError, TransportError, @@ -122,20 +122,32 @@ class EventManager: if self._subscription: # Suppressed. The subscription may no longer exist. - with suppress(*SUBSCRIPTION_ERRORS): + try: await self._subscription.Unsubscribe() + except (XMLParseError, *SUBSCRIPTION_ERRORS) as err: + LOGGER.debug( + "Failed to unsubscribe ONVIF PullPoint subscription for '%s';" + " This is normal if the device restarted: %s", + self.unique_id, + err, + ) self._subscription = None try: restarted = await self.async_start() - except SUBSCRIPTION_ERRORS: + except (XMLParseError, *SUBSCRIPTION_ERRORS) as err: restarted = False + # Device may not support subscriptions so log at debug level + # when we get an XMLParseError + LOGGER.log( + DEBUG if isinstance(err, XMLParseError) else WARNING, + "Failed to restart ONVIF PullPoint subscription for '%s'; " + "Retrying later: %s", + self.unique_id, + err, + ) if not restarted: - LOGGER.warning( - "Failed to restart ONVIF PullPoint subscription for '%s'. Retrying", - self.unique_id, - ) # Try again in a minute self._unsub_refresh = async_call_later(self.hass, 60, self.async_restart) elif self._listeners: @@ -154,8 +166,7 @@ class EventManager: .isoformat(timespec="seconds") .replace("+00:00", "Z") ) - with suppress(*SUBSCRIPTION_ERRORS): - await self._subscription.Renew(termination_time) + await self._subscription.Renew(termination_time) def async_schedule_pull(self) -> None: """Schedule async_pull_messages to run.""" @@ -178,8 +189,11 @@ class EventManager: except RemoteProtocolError: # Likely a shutdown event, nothing to see here return - except SUBSCRIPTION_ERRORS as err: - LOGGER.warning( + except (XMLParseError, *SUBSCRIPTION_ERRORS) as err: + # Device may not support subscriptions so log at debug level + # when we get an XMLParseError + LOGGER.log( + DEBUG if isinstance(err, XMLParseError) else WARNING, "Failed to fetch ONVIF PullPoint subscription messages for '%s': %s", self.unique_id, err, From 671e6b3832e96515036007343bf83edb9ec679fe Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 30 Nov 2022 14:10:56 -0700 Subject: [PATCH 0927/1033] Remove old system ID format from SimpliSafe base stations (#82993) --- .../components/simplisafe/__init__.py | 17 +- tests/components/simplisafe/conftest.py | 5 +- .../fixtures/subscription_data.json | 640 +++++++++--------- .../components/simplisafe/test_diagnostics.py | 2 +- tests/components/simplisafe/test_init.py | 40 ++ 5 files changed, 379 insertions(+), 325 deletions(-) create mode 100644 tests/components/simplisafe/test_init.py diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index 55f7adc860b..27cee778655 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -296,7 +296,8 @@ def _async_register_base_station( ) -> None: """Register a new bridge.""" device_registry = dr.async_get(hass) - device_registry.async_get_or_create( + + base_station = device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, str(system.system_id))}, manufacturer="SimpliSafe", @@ -304,6 +305,20 @@ def _async_register_base_station( name=system.address, ) + # Check for an old system ID format and remove it: + if old_base_station := device_registry.async_get_device( + {(DOMAIN, system.system_id)} # type: ignore[arg-type] + ): + # Update the new base station with any properties the user might have configured + # on the old base station: + device_registry.async_update_device( + base_station.id, + area_id=old_base_station.area_id, + disabled_by=old_base_station.disabled_by, + name_by_user=old_base_station.name_by_user, + ) + device_registry.async_remove_device(old_base_station.id) + @callback def _async_standardize_config_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: diff --git a/tests/components/simplisafe/conftest.py b/tests/components/simplisafe/conftest.py index 165f71cde04..4b8686d7a7f 100644 --- a/tests/components/simplisafe/conftest.py +++ b/tests/components/simplisafe/conftest.py @@ -15,7 +15,7 @@ from tests.common import MockConfigEntry, load_fixture CODE = "12345" PASSWORD = "password" -SYSTEM_ID = "system_123" +SYSTEM_ID = 12345 @pytest.fixture(name="api") @@ -79,7 +79,8 @@ def data_settings_fixture(): @pytest.fixture(name="data_subscription", scope="package") def data_subscription_fixture(): """Define subscription data.""" - return json.loads(load_fixture("subscription_data.json", "simplisafe")) + data = json.loads(load_fixture("subscription_data.json", "simplisafe")) + return {SYSTEM_ID: data} @pytest.fixture(name="reauth_config") diff --git a/tests/components/simplisafe/fixtures/subscription_data.json b/tests/components/simplisafe/fixtures/subscription_data.json index ad83f7a29a2..f0f6f84b17b 100644 --- a/tests/components/simplisafe/fixtures/subscription_data.json +++ b/tests/components/simplisafe/fixtures/subscription_data.json @@ -1,335 +1,333 @@ { - "system_123": { + "uid": 12345, + "sid": 12345, + "sStatus": 20, + "activated": 1445034752, + "planSku": "SSEDSM2", + "planName": "Interactive Monitoring", + "price": 24.99, + "currency": "USD", + "country": "US", + "expires": 1602887552, + "canceled": 0, + "extraTime": 0, + "creditCard": { + "lastFour": "", + "type": "", + "ppid": "ABCDE12345", + "uid": 12345 + }, + "time": 2628000, + "paymentProfileId": "ABCDE12345", + "features": { + "monitoring": true, + "alerts": true, + "online": true, + "hazard": true, + "video": true, + "cameras": 10, + "dispatch": true, + "proInstall": false, + "discount": 0, + "vipCS": false, + "medical": true, + "careVisit": false, + "storageDays": 30 + }, + "status": { + "hasBaseStation": true, + "isActive": true, + "monitoring": "Active" + }, + "subscriptionFeatures": { + "monitoredSensorsTypes": [ + "Entry", + "Motion", + "GlassBreak", + "Smoke", + "CO", + "Freeze", + "Water" + ], + "monitoredPanicConditions": ["Fire", "Medical", "Duress"], + "dispatchTypes": ["Police", "Fire", "Medical", "Guard"], + "remoteControl": [ + "ArmDisarm", + "LockUnlock", + "ViewSettings", + "ConfigureSettings" + ], + "cameraFeatures": { + "liveView": true, + "maxRecordingCameras": 10, + "recordingStorageDays": 30, + "videoVerification": true + }, + "support": { + "level": "Basic", + "annualVisit": false, + "professionalInstall": false + }, + "cellCommunicationBackup": true, + "alertChannels": ["Push", "SMS", "Email"], + "alertTypes": ["Alarm", "Error", "Activity", "Camera"], + "alarmModes": ["Alarm", "SecretAlert", "Disabled"], + "supportedIntegrations": ["GoogleAssistant", "AmazonAlexa", "AugustLock"], + "timeline": {} + }, + "dispatcher": "cops", + "dcid": 0, + "location": { + "sid": 12345, "uid": 12345, - "sid": "system_123", - "sStatus": 20, - "activated": 1445034752, - "planSku": "SSEDSM2", - "planName": "Interactive Monitoring", - "price": 24.99, - "currency": "USD", + "lStatus": 10, + "account": "1234ABCD", + "street1": "1234 Main Street", + "street2": "", + "locationName": "", + "city": "Atlantis", + "county": "SEA", + "state": "UW", + "zip": "12345", "country": "US", - "expires": 1602887552, - "canceled": 0, - "extraTime": 0, - "creditCard": { - "lastFour": "", - "type": "", - "ppid": "ABCDE12345", - "uid": 12345 - }, - "time": 2628000, - "paymentProfileId": "ABCDE12345", - "features": { - "monitoring": true, - "alerts": true, - "online": true, - "hazard": true, - "video": true, - "cameras": 10, - "dispatch": true, - "proInstall": false, - "discount": 0, - "vipCS": false, - "medical": true, - "careVisit": false, - "storageDays": 30 - }, - "status": { - "hasBaseStation": true, - "isActive": true, - "monitoring": "Active" - }, - "subscriptionFeatures": { - "monitoredSensorsTypes": [ - "Entry", - "Motion", - "GlassBreak", - "Smoke", - "CO", - "Freeze", - "Water" - ], - "monitoredPanicConditions": ["Fire", "Medical", "Duress"], - "dispatchTypes": ["Police", "Fire", "Medical", "Guard"], - "remoteControl": [ - "ArmDisarm", - "LockUnlock", - "ViewSettings", - "ConfigureSettings" - ], - "cameraFeatures": { - "liveView": true, - "maxRecordingCameras": 10, - "recordingStorageDays": 30, - "videoVerification": true + "crossStreet": "River 1 and River 2", + "notes": "", + "residenceType": 2, + "numAdults": 2, + "numChildren": 0, + "locationOffset": -360, + "safeWord": "TRITON", + "signature": "Atlantis Citizen 1", + "timeZone": 2, + "primaryContacts": [ + { + "name": "John Doe", + "phone": "1234567890" + } + ], + "secondaryContacts": [ + { + "name": "Jane Doe", + "phone": "9876543210" + } + ], + "copsOptIn": false, + "certificateUri": "https://simplisafe.com/account2/12345/alarm-certificate/12345", + "nestStructureId": "", + "system": { + "serial": "1234ABCD", + "alarmState": "OFF", + "alarmStateTimestamp": 0, + "isAlarming": false, + "version": 3, + "capabilities": { + "setWifiOverCell": true, + "setDoorbellChimeVolume": true, + "outdoorBattCamera": true }, - "support": { - "level": "Basic", - "annualVisit": false, - "professionalInstall": false - }, - "cellCommunicationBackup": true, - "alertChannels": ["Push", "SMS", "Email"], - "alertTypes": ["Alarm", "Error", "Activity", "Camera"], - "alarmModes": ["Alarm", "SecretAlert", "Disabled"], - "supportedIntegrations": ["GoogleAssistant", "AmazonAlexa", "AugustLock"], - "timeline": {} - }, - "dispatcher": "cops", - "dcid": 0, - "location": { - "sid": 12345, - "uid": 12345, - "lStatus": 10, - "account": "1234ABCD", - "street1": "1234 Main Street", - "street2": "", - "locationName": "", - "city": "Atlantis", - "county": "SEA", - "state": "UW", - "zip": "12345", - "country": "US", - "crossStreet": "River 1 and River 2", - "notes": "", - "residenceType": 2, - "numAdults": 2, - "numChildren": 0, - "locationOffset": -360, - "safeWord": "TRITON", - "signature": "Atlantis Citizen 1", - "timeZone": 2, - "primaryContacts": [ + "temperature": 67, + "exitDelayRemaining": 60, + "cameras": [ { - "name": "John Doe", - "phone": "1234567890" - } - ], - "secondaryContacts": [ - { - "name": "Jane Doe", - "phone": "9876543210" - } - ], - "copsOptIn": false, - "certificateUri": "https://simplisafe.com/account2/12345/alarm-certificate/12345", - "nestStructureId": "", - "system": { - "serial": "1234ABCD", - "alarmState": "OFF", - "alarmStateTimestamp": 0, - "isAlarming": false, - "version": 3, - "capabilities": { - "setWifiOverCell": true, - "setDoorbellChimeVolume": true, - "outdoorBattCamera": true - }, - "temperature": 67, - "exitDelayRemaining": 60, - "cameras": [ - { - "staleSettingsTypes": [], - "upgradeWhitelisted": false, - "model": "SS001", - "uuid": "1234567890", - "uid": 12345, - "sid": 12345, - "cameraSettings": { - "cameraName": "Camera", - "pictureQuality": "720p", - "nightVision": "auto", - "statusLight": "off", - "micSensitivity": 100, - "micEnable": true, - "speakerVolume": 75, - "motionSensitivity": 0, - "shutterHome": "closedAlarmOnly", - "shutterAway": "open", - "shutterOff": "closedAlarmOnly", - "wifiSsid": "", - "canStream": false, - "canRecord": false, - "pirEnable": true, - "vaEnable": true, - "notificationsEnable": false, - "enableDoorbellNotification": true, - "doorbellChimeVolume": "off", - "privacyEnable": false, - "hdr": false, - "vaZoningEnable": false, - "vaZoningRows": 0, - "vaZoningCols": 0, - "vaZoningMask": [], - "maxDigitalZoom": 10, - "supportedResolutions": ["480p", "720p"], - "admin": { - "IRLED": 0, - "pirSens": 0, - "statusLEDState": 1, - "lux": "lowLux", - "motionDetectionEnabled": false, - "motionThresholdZero": 0, - "motionThresholdOne": 10000, - "levelChangeDelayZero": 30, - "levelChangeDelayOne": 10, - "audioDetectionEnabled": false, - "audioChannelNum": 2, - "audioSampleRate": 16000, - "audioChunkBytes": 2048, - "audioSampleFormat": 3, - "audioSensitivity": 50, - "audioThreshold": 50, - "audioDirection": 0, - "bitRate": 284, - "longPress": 2000, - "kframe": 1, - "gopLength": 40, - "idr": 1, - "fps": 20, - "firmwareVersion": "2.6.1.107", - "netConfigVersion": "", - "camAgentVersion": "", - "lastLogin": 1600639997, - "lastLogout": 1600639944, - "pirSampleRateMs": 800, - "pirHysteresisHigh": 2, - "pirHysteresisLow": 10, - "pirFilterCoefficient": 1, - "logEnabled": true, - "logLevel": 3, - "logQDepth": 20, - "firmwareGroup": "public", - "irOpenThreshold": 445, - "irCloseThreshold": 840, - "irOpenDelay": 3, - "irCloseDelay": 3, - "irThreshold1x": 388, - "irThreshold2x": 335, - "irThreshold3x": 260, - "rssi": [[1600935204, -43]], - "battery": [], - "dbm": 0, - "vmUse": 161592, - "resSet": 10540, - "uptime": 810043.74, - "wifiDisconnects": 1, - "wifiDriverReloads": 1, - "statsPeriod": 3600000, - "sarlaccDebugLogTypes": 0, - "odProcessingFps": 8, - "odObjectMinWidthPercent": 6, - "odObjectMinHeightPercent": 24, - "odEnableObjectDetection": true, - "odClassificationMask": 2, - "odClassificationConfidenceThreshold": 0.95, - "odEnableOverlay": false, - "odAnalyticsLib": 2, - "odSensitivity": 85, - "odEventObjectMask": 2, - "odLuxThreshold": 445, - "odLuxHysteresisHigh": 4, - "odLuxHysteresisLow": 4, - "odLuxSamplingFrequency": 30, - "odFGExtractorMode": 2, - "odVideoScaleFactor": 1, - "odSceneType": 1, - "odCameraView": 3, - "odCameraFOV": 2, - "odBackgroundLearnStationary": true, - "odBackgroundLearnStationarySpeed": 15, - "odClassifierQualityProfile": 1, - "odEnableVideoAnalyticsWhileStreaming": false, - "wlanMac": "XX:XX:XX:XX:XX:XX", - "region": "us-east-1", - "enableWifiAnalyticsLib": false, - "ivLicense": "" - }, - "pirLevel": "medium", - "odLevel": "medium" - }, - "__v": 0, - "cameraStatus": { + "staleSettingsTypes": [], + "upgradeWhitelisted": false, + "model": "SS001", + "uuid": "1234567890", + "uid": 12345, + "sid": 12345, + "cameraSettings": { + "cameraName": "Camera", + "pictureQuality": "720p", + "nightVision": "auto", + "statusLight": "off", + "micSensitivity": 100, + "micEnable": true, + "speakerVolume": 75, + "motionSensitivity": 0, + "shutterHome": "closedAlarmOnly", + "shutterAway": "open", + "shutterOff": "closedAlarmOnly", + "wifiSsid": "", + "canStream": false, + "canRecord": false, + "pirEnable": true, + "vaEnable": true, + "notificationsEnable": false, + "enableDoorbellNotification": true, + "doorbellChimeVolume": "off", + "privacyEnable": false, + "hdr": false, + "vaZoningEnable": false, + "vaZoningRows": 0, + "vaZoningCols": 0, + "vaZoningMask": [], + "maxDigitalZoom": 10, + "supportedResolutions": ["480p", "720p"], + "admin": { + "IRLED": 0, + "pirSens": 0, + "statusLEDState": 1, + "lux": "lowLux", + "motionDetectionEnabled": false, + "motionThresholdZero": 0, + "motionThresholdOne": 10000, + "levelChangeDelayZero": 30, + "levelChangeDelayOne": 10, + "audioDetectionEnabled": false, + "audioChannelNum": 2, + "audioSampleRate": 16000, + "audioChunkBytes": 2048, + "audioSampleFormat": 3, + "audioSensitivity": 50, + "audioThreshold": 50, + "audioDirection": 0, + "bitRate": 284, + "longPress": 2000, + "kframe": 1, + "gopLength": 40, + "idr": 1, + "fps": 20, "firmwareVersion": "2.6.1.107", "netConfigVersion": "", "camAgentVersion": "", "lastLogin": 1600639997, "lastLogout": 1600639944, + "pirSampleRateMs": 800, + "pirHysteresisHigh": 2, + "pirHysteresisLow": 10, + "pirFilterCoefficient": 1, + "logEnabled": true, + "logLevel": 3, + "logQDepth": 20, + "firmwareGroup": "public", + "irOpenThreshold": 445, + "irCloseThreshold": 840, + "irOpenDelay": 3, + "irCloseDelay": 3, + "irThreshold1x": 388, + "irThreshold2x": 335, + "irThreshold3x": 260, + "rssi": [[1600935204, -43]], + "battery": [], + "dbm": 0, + "vmUse": 161592, + "resSet": 10540, + "uptime": 810043.74, + "wifiDisconnects": 1, + "wifiDriverReloads": 1, + "statsPeriod": 3600000, + "sarlaccDebugLogTypes": 0, + "odProcessingFps": 8, + "odObjectMinWidthPercent": 6, + "odObjectMinHeightPercent": 24, + "odEnableObjectDetection": true, + "odClassificationMask": 2, + "odClassificationConfidenceThreshold": 0.95, + "odEnableOverlay": false, + "odAnalyticsLib": 2, + "odSensitivity": 85, + "odEventObjectMask": 2, + "odLuxThreshold": 445, + "odLuxHysteresisHigh": 4, + "odLuxHysteresisLow": 4, + "odLuxSamplingFrequency": 30, + "odFGExtractorMode": 2, + "odVideoScaleFactor": 1, + "odSceneType": 1, + "odCameraView": 3, + "odCameraFOV": 2, + "odBackgroundLearnStationary": true, + "odBackgroundLearnStationarySpeed": 15, + "odClassifierQualityProfile": 1, + "odEnableVideoAnalyticsWhileStreaming": false, "wlanMac": "XX:XX:XX:XX:XX:XX", - "fwDownloadVersion": "", - "fwDownloadPercentage": 0, - "recovered": false, - "recoveredFromVersion": "", - "_id": "1234567890", - "initErrors": [], - "speedTestTokenCreated": 1600235629 + "region": "us-east-1", + "enableWifiAnalyticsLib": false, + "ivLicense": "" }, - "supportedFeatures": { - "providers": { - "webrtc": "none", - "recording": "simplisafe", - "live": "simplisafe" - }, - "audioEncodings": ["speex"], - "resolutions": ["480p", "720p"], - "_id": "1234567890", - "pir": true, - "videoAnalytics": false, - "privacyShutter": true, - "microphone": true, - "fullDuplexAudio": false, - "wired": true, - "networkSpeedTest": false, - "videoEncoding": "h264" + "pirLevel": "medium", + "odLevel": "medium" + }, + "__v": 0, + "cameraStatus": { + "firmwareVersion": "2.6.1.107", + "netConfigVersion": "", + "camAgentVersion": "", + "lastLogin": 1600639997, + "lastLogout": 1600639944, + "wlanMac": "XX:XX:XX:XX:XX:XX", + "fwDownloadVersion": "", + "fwDownloadPercentage": 0, + "recovered": false, + "recoveredFromVersion": "", + "_id": "1234567890", + "initErrors": [], + "speedTestTokenCreated": 1600235629 + }, + "supportedFeatures": { + "providers": { + "webrtc": "none", + "recording": "simplisafe", + "live": "simplisafe" }, - "subscription": { - "enabled": true, - "freeTrialActive": false, - "freeTrialUsed": true, - "freeTrialEnds": 0, - "freeTrialExpires": 0, - "planSku": "SSVM1", - "price": 0, - "expires": 0, - "storageDays": 30, - "trialUsed": true, - "trialActive": false, - "trialExpires": 0 - }, - "status": "online" - } - ], - "connType": "wifi", - "stateUpdated": 1601502948, - "messages": [ - { - "_id": "xxxxxxxxxxxxxxxxxxxxxxxx", - "id": "xxxxxxxxxxxxxxxxxxxxxxxx", - "textTemplate": "Power Outage - Backup battery in use.", - "data": { - "time": "2020-02-16T03:20:28+00:00" - }, - "text": "Power Outage - Backup battery in use.", - "code": "2000", - "filters": [], - "link": "http://link.to.info", - "linkLabel": "More Info", - "expiration": 0, - "category": "error", - "timestamp": 1581823228 - } - ], - "powerOutage": false, - "lastPowerOutage": 1581991064, - "lastSuccessfulWifiTS": 1601424776, - "isOffline": false - } - }, - "pinUnlocked": true, - "billDate": 1602887552, - "billInterval": 2628000, - "pinUnlockedBy": "pin", - "autoActivation": null - } + "audioEncodings": ["speex"], + "resolutions": ["480p", "720p"], + "_id": "1234567890", + "pir": true, + "videoAnalytics": false, + "privacyShutter": true, + "microphone": true, + "fullDuplexAudio": false, + "wired": true, + "networkSpeedTest": false, + "videoEncoding": "h264" + }, + "subscription": { + "enabled": true, + "freeTrialActive": false, + "freeTrialUsed": true, + "freeTrialEnds": 0, + "freeTrialExpires": 0, + "planSku": "SSVM1", + "price": 0, + "expires": 0, + "storageDays": 30, + "trialUsed": true, + "trialActive": false, + "trialExpires": 0 + }, + "status": "online" + } + ], + "connType": "wifi", + "stateUpdated": 1601502948, + "messages": [ + { + "_id": "xxxxxxxxxxxxxxxxxxxxxxxx", + "id": "xxxxxxxxxxxxxxxxxxxxxxxx", + "textTemplate": "Power Outage - Backup battery in use.", + "data": { + "time": "2020-02-16T03:20:28+00:00" + }, + "text": "Power Outage - Backup battery in use.", + "code": "2000", + "filters": [], + "link": "http://link.to.info", + "linkLabel": "More Info", + "expiration": 0, + "category": "error", + "timestamp": 1581823228 + } + ], + "powerOutage": false, + "lastPowerOutage": 1581991064, + "lastSuccessfulWifiTS": 1601424776, + "isOffline": false + } + }, + "pinUnlocked": true, + "billDate": 1602887552, + "billInterval": 2628000, + "pinUnlockedBy": "pin", + "autoActivation": null } diff --git a/tests/components/simplisafe/test_diagnostics.py b/tests/components/simplisafe/test_diagnostics.py index f7a88fe0d06..30e52021f6f 100644 --- a/tests/components/simplisafe/test_diagnostics.py +++ b/tests/components/simplisafe/test_diagnostics.py @@ -21,7 +21,7 @@ async def test_entry_diagnostics(hass, config_entry, hass_client, setup_simplisa "disabled_by": None, }, "subscription_data": { - "system_123": { + "12345": { "uid": REDACTED, "sid": REDACTED, "sStatus": 20, diff --git a/tests/components/simplisafe/test_init.py b/tests/components/simplisafe/test_init.py new file mode 100644 index 00000000000..6b81bbe7943 --- /dev/null +++ b/tests/components/simplisafe/test_init.py @@ -0,0 +1,40 @@ +"""Define tests for SimpliSafe setup.""" +from unittest.mock import patch + +from homeassistant.components.simplisafe import DOMAIN +from homeassistant.helpers import device_registry as dr +from homeassistant.setup import async_setup_component + + +async def test_base_station_migration(hass, api, config, config_entry): + """Test that errors are shown when duplicates are added.""" + old_identifers = (DOMAIN, 12345) + new_identifiers = (DOMAIN, "12345") + + device_registry = dr.async_get(hass) + device_registry.async_get_or_create( + config_entry_id=config_entry.entry_id, + identifiers={old_identifers}, + manufacturer="SimpliSafe", + name="old", + ) + + with patch( + "homeassistant.components.simplisafe.config_flow.API.async_from_auth", + return_value=api, + ), patch( + "homeassistant.components.simplisafe.API.async_from_auth", + return_value=api, + ), patch( + "homeassistant.components.simplisafe.API.async_from_refresh_token", + return_value=api, + ), patch( + "homeassistant.components.simplisafe.SimpliSafe._async_start_websocket_loop" + ), patch( + "homeassistant.components.simplisafe.PLATFORMS", [] + ): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + assert device_registry.async_get_device(identifiers={old_identifers}) is None + assert device_registry.async_get_device(identifiers={new_identifiers}) is not None From e158546425b51378a9319058d2eebbdfc0e697f4 Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Wed, 30 Nov 2022 22:40:17 +0100 Subject: [PATCH 0928/1033] Add missing @callback decorator in here_travel_time sensor (#83009) --- homeassistant/components/here_travel_time/sensor.py | 5 +++-- tests/components/here_travel_time/test_sensor.py | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/here_travel_time/sensor.py b/homeassistant/components/here_travel_time/sensor.py index 18433a86560..e432511c486 100644 --- a/homeassistant/components/here_travel_time/sensor.py +++ b/homeassistant/components/here_travel_time/sensor.py @@ -21,7 +21,7 @@ from homeassistant.const import ( TIME_MINUTES, UnitOfLength, ) -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -99,7 +99,7 @@ async def async_setup_entry( async_add_entities(sensors) -class HERETravelTimeSensor(RestoreSensor, CoordinatorEntity): +class HERETravelTimeSensor(CoordinatorEntity, RestoreSensor): """Representation of a HERE travel time sensor.""" def __init__( @@ -136,6 +136,7 @@ class HERETravelTimeSensor(RestoreSensor, CoordinatorEntity): self.async_on_remove(async_at_start(self.hass, _update_at_start)) + @callback def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" if self.coordinator.data is not None: diff --git a/tests/components/here_travel_time/test_sensor.py b/tests/components/here_travel_time/test_sensor.py index 0ad86a04992..34859956032 100644 --- a/tests/components/here_travel_time/test_sensor.py +++ b/tests/components/here_travel_time/test_sensor.py @@ -558,8 +558,6 @@ async def test_restore_state(hass): await hass.config_entries.async_setup(mock_entry.entry_id) await hass.async_block_till_done() - print(hass.states.async_all()) - # restore from cache state = hass.states.get("sensor.test_duration") assert state.state == "1234" From b3deb476ef41e20e98774df3a585b9755db1bcf4 Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Wed, 30 Nov 2022 16:54:14 -0500 Subject: [PATCH 0929/1033] Allow is_state to accept a list of values (#81877) * Allow is_state to accept a list of values * Slightly more efficient * Fix typing of state Co-authored-by: Franck Nijhof --- homeassistant/helpers/template.py | 6 ++++-- tests/helpers/test_template.py | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 8d284f042c2..73e1400cedf 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -1368,10 +1368,12 @@ def distance(hass, *args): ) -def is_state(hass: HomeAssistant, entity_id: str, state: State) -> bool: +def is_state(hass: HomeAssistant, entity_id: str, state: str | list[str]) -> bool: """Test if a state is a specific value.""" state_obj = _get_state(hass, entity_id) - return state_obj is not None and state_obj.state == state + return state_obj is not None and ( + state_obj.state == state or isinstance(state, list) and state_obj.state in state + ) def is_state_attr(hass: HomeAssistant, entity_id: str, name: str, value: Any) -> bool: diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 07463787a8b..e19daf00627 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -1389,6 +1389,14 @@ def test_is_state(hass: HomeAssistant) -> None: ) assert tpl.async_render() == "test.object" + tpl = template.Template( + """ +{{ is_state("test.object", ["on", "off", "available"]) }} + """, + hass, + ) + assert tpl.async_render() is True + def test_is_state_attr(hass: HomeAssistant) -> None: """Test is_state_attr method.""" From d620b199fcfe7b7f02ed694c2628327263efd1bb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 30 Nov 2022 12:10:06 -1000 Subject: [PATCH 0930/1033] Bump bleak-retry-connector to 2.8.6 (#83011) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 0f7378e00c0..982edf10df4 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.2", - "bleak-retry-connector==2.8.5", + "bleak-retry-connector==2.8.6", "bluetooth-adapters==0.11.0", "bluetooth-auto-recovery==0.5.3", "bluetooth-data-tools==0.3.0", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 7958b776f10..b95425b15ed 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.8.5 +bleak-retry-connector==2.8.6 bleak==0.19.2 bluetooth-adapters==0.11.0 bluetooth-auto-recovery==0.5.3 diff --git a/requirements_all.txt b/requirements_all.txt index 2f22c833df4..7f8af46d8a0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -422,7 +422,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.5 +bleak-retry-connector==2.8.6 # homeassistant.components.bluetooth bleak==0.19.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 24767a1d0fa..484efe399f6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -346,7 +346,7 @@ bellows==0.34.5 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.5 +bleak-retry-connector==2.8.6 # homeassistant.components.bluetooth bleak==0.19.2 From b28301598749898d6cd978604e764c5ee61355b4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 30 Nov 2022 12:23:29 -1000 Subject: [PATCH 0931/1033] Bump yalexs-ble to 1.9.8 (#83012) --- homeassistant/components/yalexs_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/yalexs_ble/manifest.json b/homeassistant/components/yalexs_ble/manifest.json index d3dfcd1b6dd..73c802261d4 100644 --- a/homeassistant/components/yalexs_ble/manifest.json +++ b/homeassistant/components/yalexs_ble/manifest.json @@ -3,7 +3,7 @@ "name": "Yale Access Bluetooth", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yalexs_ble", - "requirements": ["yalexs-ble==1.9.7"], + "requirements": ["yalexs-ble==1.9.8"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [ diff --git a/requirements_all.txt b/requirements_all.txt index 7f8af46d8a0..b6ef6f359b9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2603,7 +2603,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.9.7 +yalexs-ble==1.9.8 # homeassistant.components.august yalexs==1.2.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 484efe399f6..8010fa35a63 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1813,7 +1813,7 @@ xmltodict==0.13.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.9.7 +yalexs-ble==1.9.8 # homeassistant.components.august yalexs==1.2.6 From a3fe08b62b0c7ecf1447316fc630746a71832cc4 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 30 Nov 2022 23:28:24 +0100 Subject: [PATCH 0932/1033] Update frontend to 20221130.0 (#83014) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index ec7001006b1..5f46c6d334d 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20221108.0"], + "requirements": ["home-assistant-frontend==20221130.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index b95425b15ed..39bd8e35504 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -22,7 +22,7 @@ dbus-fast==1.75.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.8.1 -home-assistant-frontend==20221108.0 +home-assistant-frontend==20221130.0 httpx==0.23.1 ifaddr==0.1.7 janus==1.0.0 diff --git a/requirements_all.txt b/requirements_all.txt index b6ef6f359b9..af7c28d68a8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -884,7 +884,7 @@ hole==0.7.0 holidays==0.17.2 # homeassistant.components.frontend -home-assistant-frontend==20221108.0 +home-assistant-frontend==20221130.0 # homeassistant.components.home_connect homeconnect==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8010fa35a63..098c44976fb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -664,7 +664,7 @@ hole==0.7.0 holidays==0.17.2 # homeassistant.components.frontend -home-assistant-frontend==20221108.0 +home-assistant-frontend==20221130.0 # homeassistant.components.home_connect homeconnect==0.7.2 From f4ee38687c0cc433c3bcea0385373b5a531f4680 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 30 Nov 2022 14:10:40 -1000 Subject: [PATCH 0933/1033] Bump aioesphomeapi to 12.2.1 (#83017) --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 25dc956ea3b..42ebc471b7c 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==12.2.0"], + "requirements": ["aioesphomeapi==12.2.1"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/requirements_all.txt b/requirements_all.txt index af7c28d68a8..cc0bb13bafe 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -156,7 +156,7 @@ aioecowitt==2022.11.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==12.2.0 +aioesphomeapi==12.2.1 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 098c44976fb..790c067a3b1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -143,7 +143,7 @@ aioecowitt==2022.11.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==12.2.0 +aioesphomeapi==12.2.1 # homeassistant.components.flo aioflo==2021.11.0 From a5afe47df7a1ac498fb5813877a51f5875128e6b Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 1 Dec 2022 01:21:06 +0100 Subject: [PATCH 0934/1033] Bumped version to 2022.12.0b0 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index ca69307a485..32c79bbc889 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -8,7 +8,7 @@ from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "0.dev0" +PATCH_VERSION: Final = "0b0" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/pyproject.toml b/pyproject.toml index 710b025bee3..729892e41cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.12.0.dev0" +version = "2022.12.0b0" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From cc2ab25916a0257fa26ae16ed2821a1892de1453 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 30 Nov 2022 15:07:22 -1000 Subject: [PATCH 0935/1033] Bump aiohomekit to 2.3.2 (#83019) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 54d5c5b854d..1b7061146ea 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.3.1"], + "requirements": ["aiohomekit==2.3.2"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index cc0bb13bafe..507d3658a83 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.1 +aiohomekit==2.3.2 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 790c067a3b1..1d2eb9388f3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.1 +aiohomekit==2.3.2 # homeassistant.components.emulated_hue # homeassistant.components.http From b14dc306cff56aa6633fd3c1f241eb67d07bd41d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 30 Nov 2022 16:36:02 -1000 Subject: [PATCH 0936/1033] Fix yale access bluetooth locks delaying startup when key changes (#83024) If the keys changed for the yale locks, the locks would be slow to setup. Because august had yalexs_ble as an after dep, it would be waiting with the new keys but not able to setup because it was waiting for the locks to setup which would be trying over and over until they failed because the key had changed out from under it. This change moves some more code into the lib to avoid the dep and allows both to startup at the same time so the cloud service can feed the new keys in if needed without waiting for the lock to fail to setup changelog: https://github.com/bdraco/yalexs-ble/compare/v1.9.8...v1.10.0 --- homeassistant/components/august/__init__.py | 13 ++++++---- homeassistant/components/august/manifest.json | 5 ++-- .../components/yalexs_ble/__init__.py | 25 +------------------ .../components/yalexs_ble/manifest.json | 2 +- requirements_all.txt | 5 +++- requirements_test_all.txt | 5 +++- tests/components/august/test_init.py | 4 +-- 7 files changed, 22 insertions(+), 37 deletions(-) diff --git a/homeassistant/components/august/__init__.py b/homeassistant/components/august/__init__.py index f4a0f57eb76..249d9e51a85 100644 --- a/homeassistant/components/august/__init__.py +++ b/homeassistant/components/august/__init__.py @@ -12,9 +12,9 @@ from yalexs.exceptions import AugustApiAIOHTTPError from yalexs.lock import Lock, LockDetail from yalexs.pubnub_activity import activities_from_pubnub_message from yalexs.pubnub_async import AugustPubNub, async_create_pubnub +from yalexs_ble import YaleXSBLEDiscovery -from homeassistant.components import yalexs_ble -from homeassistant.config_entries import ConfigEntry +from homeassistant.config_entries import SOURCE_INTEGRATION_DISCOVERY, ConfigEntry from homeassistant.const import CONF_PASSWORD from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ( @@ -22,7 +22,7 @@ from homeassistant.exceptions import ( ConfigEntryNotReady, HomeAssistantError, ) -from homeassistant.helpers import device_registry as dr +from homeassistant.helpers import device_registry as dr, discovery_flow from .activity import ActivityStream from .const import DOMAIN, MIN_TIME_BETWEEN_DETAIL_UPDATES, PLATFORMS @@ -38,6 +38,7 @@ API_CACHED_ATTRS = { "lock_status", "lock_status_datetime", } +YALEXS_BLE_DOMAIN = "yalexs_ble" async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -100,9 +101,11 @@ def _async_trigger_ble_lock_discovery( ): """Update keys for the yalexs-ble integration if available.""" for lock_detail in locks_with_offline_keys: - yalexs_ble.async_discovery( + discovery_flow.async_create_flow( hass, - yalexs_ble.YaleXSBLEDiscovery( + YALEXS_BLE_DOMAIN, + context={"source": SOURCE_INTEGRATION_DISCOVERY}, + data=YaleXSBLEDiscovery( { "name": lock_detail.device_name, "address": lock_detail.mac_address, diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index b7dde070049..5470f03eb4c 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -2,7 +2,7 @@ "domain": "august", "name": "August", "documentation": "https://www.home-assistant.io/integrations/august", - "requirements": ["yalexs==1.2.6"], + "requirements": ["yalexs==1.2.6", "yalexs_ble==1.10.0"], "codeowners": ["@bdraco"], "dhcp": [ { @@ -24,6 +24,5 @@ ], "config_flow": true, "iot_class": "cloud_push", - "loggers": ["pubnub", "yalexs"], - "after_dependencies": ["yalexs_ble"] + "loggers": ["pubnub", "yalexs"] } diff --git a/homeassistant/components/yalexs_ble/__init__.py b/homeassistant/components/yalexs_ble/__init__.py index 7a2b3146265..fcecec19e6b 100644 --- a/homeassistant/components/yalexs_ble/__init__.py +++ b/homeassistant/components/yalexs_ble/__init__.py @@ -2,17 +2,15 @@ from __future__ import annotations import asyncio -from typing import TypedDict import async_timeout from yalexs_ble import PushLock, local_name_is_unique from homeassistant.components import bluetooth -from homeassistant.config_entries import SOURCE_INTEGRATION_DISCOVERY, ConfigEntry +from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_ADDRESS, Platform from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers import discovery_flow from .const import CONF_KEY, CONF_LOCAL_NAME, CONF_SLOT, DEVICE_TIMEOUT, DOMAIN from .models import YaleXSBLEData @@ -21,27 +19,6 @@ from .util import async_find_existing_service_info, bluetooth_callback_matcher PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.LOCK, Platform.SENSOR] -class YaleXSBLEDiscovery(TypedDict): - """A validated discovery of a Yale XS BLE device.""" - - name: str - address: str - serial: str - key: str - slot: int - - -@callback -def async_discovery(hass: HomeAssistant, discovery: YaleXSBLEDiscovery) -> None: - """Update keys for the yalexs-ble integration if available.""" - discovery_flow.async_create_flow( - hass, - DOMAIN, - context={"source": SOURCE_INTEGRATION_DISCOVERY}, - data=discovery, - ) - - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Yale Access Bluetooth from a config entry.""" local_name = entry.data[CONF_LOCAL_NAME] diff --git a/homeassistant/components/yalexs_ble/manifest.json b/homeassistant/components/yalexs_ble/manifest.json index 73c802261d4..b54dda37548 100644 --- a/homeassistant/components/yalexs_ble/manifest.json +++ b/homeassistant/components/yalexs_ble/manifest.json @@ -3,7 +3,7 @@ "name": "Yale Access Bluetooth", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yalexs_ble", - "requirements": ["yalexs-ble==1.9.8"], + "requirements": ["yalexs-ble==1.10.0"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [ diff --git a/requirements_all.txt b/requirements_all.txt index 507d3658a83..24979f18203 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2603,11 +2603,14 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.9.8 +yalexs-ble==1.10.0 # homeassistant.components.august yalexs==1.2.6 +# homeassistant.components.august +yalexs_ble==1.10.0 + # homeassistant.components.yeelight yeelight==0.7.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1d2eb9388f3..2d5b57dc410 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1813,11 +1813,14 @@ xmltodict==0.13.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.9.8 +yalexs-ble==1.10.0 # homeassistant.components.august yalexs==1.2.6 +# homeassistant.components.august +yalexs_ble==1.10.0 + # homeassistant.components.yeelight yeelight==0.7.10 diff --git a/tests/components/august/test_init.py b/tests/components/august/test_init.py index a4bc3d7b16f..ef82efae177 100644 --- a/tests/components/august/test_init.py +++ b/tests/components/august/test_init.py @@ -332,7 +332,7 @@ async def test_load_triggers_ble_discovery(hass): august_lock_without_key = await _mock_operative_august_lock_detail(hass) with patch( - "homeassistant.components.august.yalexs_ble.async_discovery" + "homeassistant.components.august.discovery_flow.async_create_flow" ) as mock_discovery: config_entry = await _create_august_with_devices( hass, [august_lock_with_key, august_lock_without_key] @@ -341,7 +341,7 @@ async def test_load_triggers_ble_discovery(hass): assert config_entry.state is ConfigEntryState.LOADED assert len(mock_discovery.mock_calls) == 1 - assert mock_discovery.mock_calls[0][1][1] == { + assert mock_discovery.mock_calls[0].kwargs["data"] == { "name": "Front Door Lock", "address": None, "serial": "X2FSW05DGA", From 5147beb475f4fc6d739ba0f78be1498eb3809dd5 Mon Sep 17 00:00:00 2001 From: Keilin Bickar Date: Thu, 1 Dec 2022 04:53:48 -0500 Subject: [PATCH 0937/1033] Bump sense_api to 0.11.0 and add refresh token support (#83030) fixes undefined --- homeassistant/components/emulated_kasa/manifest.json | 2 +- homeassistant/components/sense/__init__.py | 5 ++++- homeassistant/components/sense/config_flow.py | 2 ++ homeassistant/components/sense/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/sense/test_config_flow.py | 4 ++++ 7 files changed, 14 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/emulated_kasa/manifest.json b/homeassistant/components/emulated_kasa/manifest.json index 31319a656cd..86e8f2fc2ca 100644 --- a/homeassistant/components/emulated_kasa/manifest.json +++ b/homeassistant/components/emulated_kasa/manifest.json @@ -2,7 +2,7 @@ "domain": "emulated_kasa", "name": "Emulated Kasa", "documentation": "https://www.home-assistant.io/integrations/emulated_kasa", - "requirements": ["sense_energy==0.10.4"], + "requirements": ["sense_energy==0.11.0"], "codeowners": ["@kbickar"], "quality_scale": "internal", "iot_class": "local_push", diff --git a/homeassistant/components/sense/__init__.py b/homeassistant/components/sense/__init__.py index 82b25802b02..316cbb841ed 100644 --- a/homeassistant/components/sense/__init__.py +++ b/homeassistant/components/sense/__init__.py @@ -66,6 +66,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: access_token = entry_data.get("access_token", "") user_id = entry_data.get("user_id", "") + device_id = entry_data.get("device_id", "") + refresh_token = entry_data.get("refresh_token", "") monitor_id = entry_data.get("monitor_id", "") client_session = async_get_clientsession(hass) @@ -76,7 +78,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: gateway.rate_limit = ACTIVE_UPDATE_RATE try: - gateway.load_auth(access_token, user_id, monitor_id) + gateway.load_auth(access_token, user_id, device_id, refresh_token) + gateway.set_monitor_id(monitor_id) await gateway.get_monitor_data() except (SenseAuthenticationException, SenseMFARequiredException) as err: _LOGGER.warning("Sense authentication expired") diff --git a/homeassistant/components/sense/config_flow.py b/homeassistant/components/sense/config_flow.py index 0690344ccf1..d7f7588beb2 100644 --- a/homeassistant/components/sense/config_flow.py +++ b/homeassistant/components/sense/config_flow.py @@ -60,6 +60,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Create the entry from the config data.""" self._auth_data["access_token"] = self._gateway.sense_access_token self._auth_data["user_id"] = self._gateway.sense_user_id + self._auth_data["device_id"] = self._gateway.device_id + self._auth_data["refresh_token"] = self._gateway.refresh_token self._auth_data["monitor_id"] = self._gateway.sense_monitor_id existing_entry = await self.async_set_unique_id(self._auth_data[CONF_EMAIL]) if not existing_entry: diff --git a/homeassistant/components/sense/manifest.json b/homeassistant/components/sense/manifest.json index 192a0f6defe..158ef7cae61 100644 --- a/homeassistant/components/sense/manifest.json +++ b/homeassistant/components/sense/manifest.json @@ -2,7 +2,7 @@ "domain": "sense", "name": "Sense", "documentation": "https://www.home-assistant.io/integrations/sense", - "requirements": ["sense_energy==0.10.4"], + "requirements": ["sense_energy==0.11.0"], "codeowners": ["@kbickar"], "config_flow": true, "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index 24979f18203..5f52fa49204 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2258,7 +2258,7 @@ sendgrid==6.8.2 # homeassistant.components.emulated_kasa # homeassistant.components.sense -sense_energy==0.10.4 +sense_energy==0.11.0 # homeassistant.components.sensirion_ble sensirion-ble==0.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2d5b57dc410..0ac98658296 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1561,7 +1561,7 @@ securetar==2022.2.0 # homeassistant.components.emulated_kasa # homeassistant.components.sense -sense_energy==0.10.4 +sense_energy==0.11.0 # homeassistant.components.sensirion_ble sensirion-ble==0.0.1 diff --git a/tests/components/sense/test_config_flow.py b/tests/components/sense/test_config_flow.py index 690e5d2e530..7dc2b2469ec 100644 --- a/tests/components/sense/test_config_flow.py +++ b/tests/components/sense/test_config_flow.py @@ -22,6 +22,8 @@ MOCK_CONFIG = { "access_token": "ABC", "user_id": "123", "monitor_id": "456", + "device_id": "789", + "refresh_token": "XYZ", } @@ -36,6 +38,8 @@ def mock_sense(): mock_sense.return_value.sense_access_token = "ABC" mock_sense.return_value.sense_user_id = "123" mock_sense.return_value.sense_monitor_id = "456" + mock_sense.return_value.device_id = "789" + mock_sense.return_value.refresh_token = "XYZ" yield mock_sense From 88072512d8a697c600f66d4aeb534cca6450dbf9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 30 Nov 2022 18:11:01 -1000 Subject: [PATCH 0938/1033] Fix incorrect mapping of sensitive mode on oralb 4000 series (#83031) --- homeassistant/components/oralb/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/oralb/manifest.json b/homeassistant/components/oralb/manifest.json index 8868330a7e7..94abb85a7b8 100644 --- a/homeassistant/components/oralb/manifest.json +++ b/homeassistant/components/oralb/manifest.json @@ -8,7 +8,7 @@ "manufacturer_id": 220 } ], - "requirements": ["oralb-ble==0.14.2"], + "requirements": ["oralb-ble==0.14.3"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 5f52fa49204..ca3a13dffce 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1261,7 +1261,7 @@ openwrt-luci-rpc==1.1.11 openwrt-ubus-rpc==0.0.2 # homeassistant.components.oralb -oralb-ble==0.14.2 +oralb-ble==0.14.3 # homeassistant.components.oru oru==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0ac98658296..adce784642c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -906,7 +906,7 @@ open-meteo==0.2.1 openerz-api==0.1.0 # homeassistant.components.oralb -oralb-ble==0.14.2 +oralb-ble==0.14.3 # homeassistant.components.ovo_energy ovoenergy==1.2.0 From a9e1c14024767ac20e900f5ea9b95c05f2bc50e5 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Thu, 1 Dec 2022 08:14:42 +0000 Subject: [PATCH 0939/1033] Fix getting updated thread IP at startup in homekit_controller (#83037) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 1b7061146ea..041f2ad293e 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.3.2"], + "requirements": ["aiohomekit==2.3.3"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index ca3a13dffce..bc28123eec6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.2 +aiohomekit==2.3.3 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index adce784642c..4f0a91e933d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.2 +aiohomekit==2.3.3 # homeassistant.components.emulated_hue # homeassistant.components.http From 47af4a675367c70fc6e65215b9399ab3331d2a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Thu, 1 Dec 2022 19:26:30 +0100 Subject: [PATCH 0940/1033] Fix hassfest serializer injecting commas in empty lists (#83052) * No trailing comma for empty lists * Update script/hassfest/serializer.py Co-authored-by: Aarni Koskela Co-authored-by: Paulus Schoutsen Co-authored-by: Aarni Koskela --- script/hassfest/serializer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/script/hassfest/serializer.py b/script/hassfest/serializer.py index 101aea6837e..41f6a554aff 100644 --- a/script/hassfest/serializer.py +++ b/script/hassfest/serializer.py @@ -21,7 +21,9 @@ def _wrap_items( # on one line and some on multiple. if sort: items = sorted(items) - return f"{opener}{','.join(items)},{closer}" + + joined_items = ", ".join(items) + return f"{opener}{joined_items}{',' if joined_items else ''}{closer}" def _mapping_to_str(data: Mapping[Any, Any]) -> str: From 1bf5925cdfe2621b375127c4ab237f08295cf06b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 1 Dec 2022 22:35:53 +0100 Subject: [PATCH 0941/1033] Update frontend to 20221201.1 (#83062) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 5f46c6d334d..74408788d49 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20221130.0"], + "requirements": ["home-assistant-frontend==20221201.1"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 39bd8e35504..323cc927c96 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -22,7 +22,7 @@ dbus-fast==1.75.0 fnvhash==0.1.0 hass-nabucasa==0.56.0 home-assistant-bluetooth==1.8.1 -home-assistant-frontend==20221130.0 +home-assistant-frontend==20221201.1 httpx==0.23.1 ifaddr==0.1.7 janus==1.0.0 diff --git a/requirements_all.txt b/requirements_all.txt index bc28123eec6..0eee6b15f39 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -884,7 +884,7 @@ hole==0.7.0 holidays==0.17.2 # homeassistant.components.frontend -home-assistant-frontend==20221130.0 +home-assistant-frontend==20221201.1 # homeassistant.components.home_connect homeconnect==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4f0a91e933d..fd43ce5ab4c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -664,7 +664,7 @@ hole==0.7.0 holidays==0.17.2 # homeassistant.components.frontend -home-assistant-frontend==20221130.0 +home-assistant-frontend==20221201.1 # homeassistant.components.home_connect homeconnect==0.7.2 From 095cc77bf98e9445a00ffe6d121db58032cf9f89 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Thu, 1 Dec 2022 20:44:56 +0100 Subject: [PATCH 0942/1033] Add matter integration BETA (#83064) * Add matter base (#79372) Co-authored-by: Marcel van der Veldt * Add matter server add-on flow (#82698) * Add matter server add-on flow * Fix stale error argument * Clean docstrings * Use localhost as default address * Add matter websocket api foundation (#82848) * Add matter config entry add-on management (#82865) * Use matter refactored server/client library (#83003) Co-authored-by: Martin Hjelmare * Bump python-matter-server to 1.0.6 (#83059) * Extend matter websocket api (#82948) * Extend matter websocket api * Finish docstring * Fix pin type * Adjust api after new client * Adjust api to frontend for now Co-authored-by: Martin Hjelmare --- .coveragerc | 3 + .strict-typing | 1 + CODEOWNERS | 2 + homeassistant/components/matter/__init__.py | 351 +++++++ homeassistant/components/matter/adapter.py | 141 +++ homeassistant/components/matter/addon.py | 17 + homeassistant/components/matter/api.py | 152 +++ .../components/matter/config_flow.py | 325 ++++++ homeassistant/components/matter/const.py | 10 + .../components/matter/device_platform.py | 24 + homeassistant/components/matter/entity.py | 118 +++ homeassistant/components/matter/light.py | 173 ++++ homeassistant/components/matter/manifest.json | 10 + homeassistant/components/matter/services.yaml | 66 ++ homeassistant/components/matter/strings.json | 47 + .../components/matter/translations/en.json | 47 + homeassistant/components/matter/util.py | 11 + homeassistant/generated/config_flows.py | 1 + homeassistant/generated/integrations.json | 6 + mypy.ini | 10 + requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/matter/__init__.py | 1 + tests/components/matter/common.py | 146 +++ tests/components/matter/conftest.py | 187 ++++ .../fixtures/nodes/fake-bridge-two-light.json | 174 ++++ .../fixtures/nodes/lighting-example-app.json | 882 ++++++++++++++++ tests/components/matter/test_adapter.py | 78 ++ tests/components/matter/test_api.py | 179 ++++ tests/components/matter/test_config_flow.py | 979 ++++++++++++++++++ tests/components/matter/test_init.py | 398 +++++++ tests/components/matter/test_light.py | 82 ++ 32 files changed, 4627 insertions(+) create mode 100644 homeassistant/components/matter/__init__.py create mode 100644 homeassistant/components/matter/adapter.py create mode 100644 homeassistant/components/matter/addon.py create mode 100644 homeassistant/components/matter/api.py create mode 100644 homeassistant/components/matter/config_flow.py create mode 100644 homeassistant/components/matter/const.py create mode 100644 homeassistant/components/matter/device_platform.py create mode 100644 homeassistant/components/matter/entity.py create mode 100644 homeassistant/components/matter/light.py create mode 100644 homeassistant/components/matter/manifest.json create mode 100644 homeassistant/components/matter/services.yaml create mode 100644 homeassistant/components/matter/strings.json create mode 100644 homeassistant/components/matter/translations/en.json create mode 100644 homeassistant/components/matter/util.py create mode 100644 tests/components/matter/__init__.py create mode 100644 tests/components/matter/common.py create mode 100644 tests/components/matter/conftest.py create mode 100644 tests/components/matter/fixtures/nodes/fake-bridge-two-light.json create mode 100644 tests/components/matter/fixtures/nodes/lighting-example-app.json create mode 100644 tests/components/matter/test_adapter.py create mode 100644 tests/components/matter/test_api.py create mode 100644 tests/components/matter/test_config_flow.py create mode 100644 tests/components/matter/test_init.py create mode 100644 tests/components/matter/test_light.py diff --git a/.coveragerc b/.coveragerc index 8582f2daed8..94e1667bad8 100644 --- a/.coveragerc +++ b/.coveragerc @@ -724,6 +724,9 @@ omit = homeassistant/components/map/* homeassistant/components/mastodon/notify.py homeassistant/components/matrix/* + homeassistant/components/matter/__init__.py + homeassistant/components/matter/adapter.py + homeassistant/components/matter/entity.py homeassistant/components/meater/__init__.py homeassistant/components/meater/const.py homeassistant/components/meater/sensor.py diff --git a/.strict-typing b/.strict-typing index baa7f9e24f7..e47533d6ca9 100644 --- a/.strict-typing +++ b/.strict-typing @@ -179,6 +179,7 @@ homeassistant.components.logger.* homeassistant.components.lookin.* homeassistant.components.luftdaten.* homeassistant.components.mailbox.* +homeassistant.components.matter.* homeassistant.components.media_player.* homeassistant.components.media_source.* homeassistant.components.metoffice.* diff --git a/CODEOWNERS b/CODEOWNERS index 8920d85defe..2966d69b032 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -666,6 +666,8 @@ build.json @home-assistant/supervisor /tests/components/lyric/ @timmo001 /homeassistant/components/mastodon/ @fabaff /homeassistant/components/matrix/ @tinloaf +/homeassistant/components/matter/ @MartinHjelmare @marcelveldt +/tests/components/matter/ @MartinHjelmare @marcelveldt /homeassistant/components/mazda/ @bdr99 /tests/components/mazda/ @bdr99 /homeassistant/components/meater/ @Sotolotl @emontnemery diff --git a/homeassistant/components/matter/__init__.py b/homeassistant/components/matter/__init__.py new file mode 100644 index 00000000000..845d48ea883 --- /dev/null +++ b/homeassistant/components/matter/__init__.py @@ -0,0 +1,351 @@ +"""The Matter integration.""" +from __future__ import annotations + +import asyncio +from typing import cast + +import async_timeout +from matter_server.client import MatterClient +from matter_server.client.exceptions import ( + CannotConnect, + FailedCommand, + InvalidServerVersion, +) +import voluptuous as vol + +from homeassistant.components.hassio import AddonError, AddonManager, AddonState +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_URL, EVENT_HOMEASSISTANT_STOP +from homeassistant.core import Event, HomeAssistant, ServiceCall, callback +from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError +from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.issue_registry import ( + IssueSeverity, + async_create_issue, + async_delete_issue, +) +from homeassistant.helpers.service import async_register_admin_service + +from .adapter import MatterAdapter +from .addon import get_addon_manager +from .api import async_register_api +from .const import CONF_INTEGRATION_CREATED_ADDON, CONF_USE_ADDON, DOMAIN, LOGGER +from .device_platform import DEVICE_PLATFORM + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Matter from a config entry.""" + if use_addon := entry.data.get(CONF_USE_ADDON): + await _async_ensure_addon_running(hass, entry) + + matter_client = MatterClient(entry.data[CONF_URL], async_get_clientsession(hass)) + try: + await matter_client.connect() + except CannotConnect as err: + raise ConfigEntryNotReady("Failed to connect to matter server") from err + except InvalidServerVersion as err: + if use_addon: + addon_manager = _get_addon_manager(hass) + addon_manager.async_schedule_update_addon(catch_error=True) + else: + async_create_issue( + hass, + DOMAIN, + "invalid_server_version", + is_fixable=False, + severity=IssueSeverity.ERROR, + translation_key="invalid_server_version", + ) + raise ConfigEntryNotReady(f"Invalid server version: {err}") from err + + except Exception as err: + matter_client.logger.exception("Failed to connect to matter server") + raise ConfigEntryNotReady( + "Unknown error connecting to the Matter server" + ) from err + else: + async_delete_issue(hass, DOMAIN, "invalid_server_version") + + async def on_hass_stop(event: Event) -> None: + """Handle incoming stop event from Home Assistant.""" + await matter_client.disconnect() + + entry.async_on_unload( + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop) + ) + + # register websocket api + async_register_api(hass) + + # launch the matter client listen task in the background + # use the init_ready event to keep track if it did initialize successfully + init_ready = asyncio.Event() + listen_task = asyncio.create_task(matter_client.start_listening(init_ready)) + + try: + async with async_timeout.timeout(30): + await init_ready.wait() + except asyncio.TimeoutError as err: + listen_task.cancel() + raise ConfigEntryNotReady("Matter client not ready") from err + + if DOMAIN not in hass.data: + hass.data[DOMAIN] = {} + _async_init_services(hass) + + # we create an intermediate layer (adapter) which keeps track of our nodes + # and discovery of platform entities from the node's attributes + matter = MatterAdapter(hass, matter_client, entry) + hass.data[DOMAIN][entry.entry_id] = matter + + # forward platform setup to all platforms in the discovery schema + await hass.config_entries.async_forward_entry_setups(entry, DEVICE_PLATFORM) + + # start discovering of node entities as task + asyncio.create_task(matter.setup_nodes()) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + unload_ok = await hass.config_entries.async_unload_platforms(entry, DEVICE_PLATFORM) + + if unload_ok: + matter: MatterAdapter = hass.data[DOMAIN].pop(entry.entry_id) + await matter.matter_client.disconnect() + + if entry.data.get(CONF_USE_ADDON) and entry.disabled_by: + addon_manager: AddonManager = get_addon_manager(hass) + LOGGER.debug("Stopping Matter Server add-on") + try: + await addon_manager.async_stop_addon() + except AddonError as err: + LOGGER.error("Failed to stop the Matter Server add-on: %s", err) + return False + + return unload_ok + + +async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Config entry is being removed.""" + + if not entry.data.get(CONF_INTEGRATION_CREATED_ADDON): + return + + addon_manager: AddonManager = get_addon_manager(hass) + try: + await addon_manager.async_stop_addon() + except AddonError as err: + LOGGER.error(err) + return + try: + await addon_manager.async_create_backup() + except AddonError as err: + LOGGER.error(err) + return + try: + await addon_manager.async_uninstall_addon() + except AddonError as err: + LOGGER.error(err) + + +async def async_remove_config_entry_device( + hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry +) -> bool: + """Remove a config entry from a device.""" + unique_id = None + + for ident in device_entry.identifiers: + if ident[0] == DOMAIN: + unique_id = ident[1] + break + + if not unique_id: + return True + + matter: MatterAdapter = hass.data[DOMAIN][config_entry.entry_id] + + for node in await matter.matter_client.get_nodes(): + if node.unique_id == unique_id: + await matter.matter_client.remove_node(node.node_id) + break + + return True + + +@callback +def get_matter(hass: HomeAssistant) -> MatterAdapter: + """Return MatterAdapter instance.""" + # NOTE: This assumes only one Matter connection/fabric can exist. + # Shall we support connecting to multiple servers in the client or by config entries? + # In case of the config entry we need to fix this. + matter: MatterAdapter = next(iter(hass.data[DOMAIN].values())) + return matter + + +@callback +def _async_init_services(hass: HomeAssistant) -> None: + """Init services.""" + + async def commission(call: ServiceCall) -> None: + """Handle commissioning.""" + matter_client = get_matter(hass).matter_client + try: + await matter_client.commission_with_code(call.data["code"]) + except FailedCommand as err: + raise HomeAssistantError(str(err)) from err + + async_register_admin_service( + hass, + DOMAIN, + "commission", + commission, + vol.Schema({"code": str}), + ) + + async def accept_shared_device(call: ServiceCall) -> None: + """Accept a shared device.""" + matter_client = get_matter(hass).matter_client + try: + await matter_client.commission_on_network(call.data["pin"]) + except FailedCommand as err: + raise HomeAssistantError(str(err)) from err + + async_register_admin_service( + hass, + DOMAIN, + "accept_shared_device", + accept_shared_device, + vol.Schema({"pin": vol.Coerce(int)}), + ) + + async def set_wifi(call: ServiceCall) -> None: + """Handle set wifi creds.""" + matter_client = get_matter(hass).matter_client + try: + await matter_client.set_wifi_credentials( + call.data["ssid"], call.data["password"] + ) + except FailedCommand as err: + raise HomeAssistantError(str(err)) from err + + async_register_admin_service( + hass, + DOMAIN, + "set_wifi", + set_wifi, + vol.Schema( + { + "ssid": str, + "password": str, + } + ), + ) + + async def set_thread_dataset(call: ServiceCall) -> None: + """Handle set Thread creds.""" + matter_client = get_matter(hass).matter_client + thread_dataset = bytes.fromhex(call.data["dataset"]) + try: + await matter_client.set_thread_operational_dataset(thread_dataset) + except FailedCommand as err: + raise HomeAssistantError(str(err)) from err + + async_register_admin_service( + hass, + DOMAIN, + "set_thread", + set_thread_dataset, + vol.Schema({"dataset": str}), + ) + + async def _node_id_from_ha_device_id(ha_device_id: str) -> int | None: + """Get node id from ha device id.""" + dev_reg = dr.async_get(hass) + device = dev_reg.async_get(ha_device_id) + + if device is None: + return None + + matter_id = next( + ( + identifier + for identifier in device.identifiers + if identifier[0] == DOMAIN + ), + None, + ) + + if not matter_id: + return None + + unique_id = matter_id[1] + + matter_client = get_matter(hass).matter_client + + # This could be more efficient + for node in await matter_client.get_nodes(): + if node.unique_id == unique_id: + return cast(int, node.node_id) + + return None + + async def open_commissioning_window(call: ServiceCall) -> None: + """Open commissioning window on specific node.""" + node_id = await _node_id_from_ha_device_id(call.data["device_id"]) + + if node_id is None: + raise HomeAssistantError("This is not a Matter device") + + matter_client = get_matter(hass).matter_client + + # We are sending device ID . + + try: + await matter_client.open_commissioning_window(node_id) + except FailedCommand as err: + raise HomeAssistantError(str(err)) from err + + async_register_admin_service( + hass, + DOMAIN, + "open_commissioning_window", + open_commissioning_window, + vol.Schema({"device_id": str}), + ) + + +async def _async_ensure_addon_running(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Ensure that Matter Server add-on is installed and running.""" + addon_manager = _get_addon_manager(hass) + try: + addon_info = await addon_manager.async_get_addon_info() + except AddonError as err: + raise ConfigEntryNotReady(err) from err + + addon_state = addon_info.state + + if addon_state == AddonState.NOT_INSTALLED: + addon_manager.async_schedule_install_setup_addon( + addon_info.options, + catch_error=True, + ) + raise ConfigEntryNotReady + + if addon_state == AddonState.NOT_RUNNING: + addon_manager.async_schedule_start_addon(catch_error=True) + raise ConfigEntryNotReady + + +@callback +def _get_addon_manager(hass: HomeAssistant) -> AddonManager: + """Ensure that Matter Server add-on is updated and running. + + May only be used as part of async_setup_entry above. + """ + addon_manager: AddonManager = get_addon_manager(hass) + if addon_manager.task_in_progress(): + raise ConfigEntryNotReady + return addon_manager diff --git a/homeassistant/components/matter/adapter.py b/homeassistant/components/matter/adapter.py new file mode 100644 index 00000000000..c2ad11cb10f --- /dev/null +++ b/homeassistant/components/matter/adapter.py @@ -0,0 +1,141 @@ +"""Matter to Home Assistant adapter.""" +from __future__ import annotations + +import asyncio +import logging +from typing import TYPE_CHECKING + +from chip.clusters import Objects as all_clusters +from matter_server.common.models.node_device import AbstractMatterNodeDevice + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .device_platform import DEVICE_PLATFORM + +if TYPE_CHECKING: + from matter_server.client import MatterClient + from matter_server.common.models.node import MatterNode + + +class MatterAdapter: + """Connect Matter into Home Assistant.""" + + def __init__( + self, + hass: HomeAssistant, + matter_client: MatterClient, + config_entry: ConfigEntry, + ) -> None: + """Initialize the adapter.""" + self.matter_client = matter_client + self.hass = hass + self.config_entry = config_entry + self.logger = logging.getLogger(__name__) + self.platform_handlers: dict[Platform, AddEntitiesCallback] = {} + self._platforms_set_up = asyncio.Event() + + def register_platform_handler( + self, platform: Platform, add_entities: AddEntitiesCallback + ) -> None: + """Register a platform handler.""" + self.platform_handlers[platform] = add_entities + if len(self.platform_handlers) == len(DEVICE_PLATFORM): + self._platforms_set_up.set() + + async def setup_nodes(self) -> None: + """Set up all existing nodes.""" + await self._platforms_set_up.wait() + for node in await self.matter_client.get_nodes(): + await self._setup_node(node) + + async def _setup_node(self, node: MatterNode) -> None: + """Set up an node.""" + self.logger.debug("Setting up entities for node %s", node.node_id) + + bridge_unique_id: str | None = None + + if node.aggregator_device_type_instance is not None: + node_info = node.root_device_type_instance.get_cluster(all_clusters.Basic) + self._create_device_registry( + node_info, node_info.nodeLabel or "Hub device", None + ) + bridge_unique_id = node_info.uniqueID + + for node_device in node.node_devices: + self._setup_node_device(node_device, bridge_unique_id) + + def _create_device_registry( + self, + info: all_clusters.Basic | all_clusters.BridgedDeviceBasic, + name: str, + bridge_unique_id: str | None, + ) -> None: + """Create a device registry entry.""" + dr.async_get(self.hass).async_get_or_create( + name=name, + config_entry_id=self.config_entry.entry_id, + identifiers={(DOMAIN, info.uniqueID)}, + hw_version=info.hardwareVersionString, + sw_version=info.softwareVersionString, + manufacturer=info.vendorName, + model=info.productName, + via_device=(DOMAIN, bridge_unique_id) if bridge_unique_id else None, + ) + + def _setup_node_device( + self, node_device: AbstractMatterNodeDevice, bridge_unique_id: str | None + ) -> None: + """Set up a node device.""" + node = node_device.node() + basic_info = node_device.device_info() + device_type_instances = node_device.device_type_instances() + + name = basic_info.nodeLabel + if not name and device_type_instances: + name = f"{device_type_instances[0].device_type.__doc__[:-1]} {node.node_id}" + + self._create_device_registry(basic_info, name, bridge_unique_id) + + for instance in device_type_instances: + created = False + + for platform, devices in DEVICE_PLATFORM.items(): + entity_descriptions = devices.get(instance.device_type) + + if entity_descriptions is None: + continue + + if not isinstance(entity_descriptions, list): + entity_descriptions = [entity_descriptions] + + entities = [] + for entity_description in entity_descriptions: + self.logger.debug( + "Creating %s entity for %s (%s)", + platform, + instance.device_type.__name__, + hex(instance.device_type.device_type), + ) + entities.append( + entity_description.entity_cls( + self.matter_client, + node_device, + instance, + entity_description, + ) + ) + + self.platform_handlers[platform](entities) + created = True + + if not created: + self.logger.warning( + "Found unsupported device %s (%s)", + type(instance).__name__, + hex(instance.device_type.device_type), + ) diff --git a/homeassistant/components/matter/addon.py b/homeassistant/components/matter/addon.py new file mode 100644 index 00000000000..84f430a58d8 --- /dev/null +++ b/homeassistant/components/matter/addon.py @@ -0,0 +1,17 @@ +"""Provide add-on management.""" +from __future__ import annotations + +from homeassistant.components.hassio import AddonManager +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.singleton import singleton + +from .const import ADDON_SLUG, DOMAIN, LOGGER + +DATA_ADDON_MANAGER = f"{DOMAIN}_addon_manager" + + +@singleton(DATA_ADDON_MANAGER) +@callback +def get_addon_manager(hass: HomeAssistant) -> AddonManager: + """Get the add-on manager.""" + return AddonManager(hass, LOGGER, "Matter Server", ADDON_SLUG) diff --git a/homeassistant/components/matter/api.py b/homeassistant/components/matter/api.py new file mode 100644 index 00000000000..36cf83fd0da --- /dev/null +++ b/homeassistant/components/matter/api.py @@ -0,0 +1,152 @@ +"""Handle websocket api for Matter.""" +from __future__ import annotations + +from collections.abc import Callable +from functools import wraps +from typing import Any + +from matter_server.client.exceptions import FailedCommand +import voluptuous as vol + +from homeassistant.components import websocket_api +from homeassistant.components.websocket_api import ActiveConnection +from homeassistant.core import HomeAssistant, callback + +from .adapter import MatterAdapter +from .const import DOMAIN + +ID = "id" +TYPE = "type" + + +@callback +def async_register_api(hass: HomeAssistant) -> None: + """Register all of our api endpoints.""" + websocket_api.async_register_command(hass, websocket_commission) + websocket_api.async_register_command(hass, websocket_commission_on_network) + websocket_api.async_register_command(hass, websocket_set_thread_dataset) + websocket_api.async_register_command(hass, websocket_set_wifi_credentials) + + +def async_get_matter_adapter(func: Callable) -> Callable: + """Decorate function to get the MatterAdapter.""" + + @wraps(func) + async def _get_matter( + hass: HomeAssistant, connection: ActiveConnection, msg: dict + ) -> None: + """Provide the Matter client to the function.""" + matter: MatterAdapter = next(iter(hass.data[DOMAIN].values())) + + await func(hass, connection, msg, matter) + + return _get_matter + + +def async_handle_failed_command(func: Callable) -> Callable: + """Decorate function to handle FailedCommand and send relevant error.""" + + @wraps(func) + async def async_handle_failed_command_func( + hass: HomeAssistant, + connection: ActiveConnection, + msg: dict[str, Any], + *args: Any, + **kwargs: Any, + ) -> None: + """Handle FailedCommand within function and send relevant error.""" + try: + await func(hass, connection, msg, *args, **kwargs) + except FailedCommand as err: + connection.send_error(msg[ID], err.error_code, err.args[0]) + + return async_handle_failed_command_func + + +@websocket_api.require_admin +@websocket_api.websocket_command( + { + vol.Required(TYPE): "matter/commission", + vol.Required("code"): str, + } +) +@websocket_api.async_response +@async_handle_failed_command +@async_get_matter_adapter +async def websocket_commission( + hass: HomeAssistant, + connection: ActiveConnection, + msg: dict[str, Any], + matter: MatterAdapter, +) -> None: + """Add a device to the network and commission the device.""" + await matter.matter_client.commission_with_code(msg["code"]) + connection.send_result(msg[ID]) + + +@websocket_api.require_admin +@websocket_api.websocket_command( + { + vol.Required(TYPE): "matter/commission_on_network", + vol.Required("pin"): int, + } +) +@websocket_api.async_response +@async_handle_failed_command +@async_get_matter_adapter +async def websocket_commission_on_network( + hass: HomeAssistant, + connection: ActiveConnection, + msg: dict[str, Any], + matter: MatterAdapter, +) -> None: + """Commission a device already on the network.""" + await matter.matter_client.commission_on_network(msg["pin"]) + connection.send_result(msg[ID]) + + +@websocket_api.require_admin +@websocket_api.websocket_command( + { + vol.Required(TYPE): "matter/set_thread", + vol.Required("thread_operation_dataset"): str, + } +) +@websocket_api.async_response +@async_handle_failed_command +@async_get_matter_adapter +async def websocket_set_thread_dataset( + hass: HomeAssistant, + connection: ActiveConnection, + msg: dict[str, Any], + matter: MatterAdapter, +) -> None: + """Set thread dataset.""" + await matter.matter_client.set_thread_operational_dataset( + msg["thread_operation_dataset"] + ) + connection.send_result(msg[ID]) + + +@websocket_api.require_admin +@websocket_api.websocket_command( + { + vol.Required(TYPE): "matter/set_wifi_credentials", + vol.Required("network_name"): str, + vol.Required("password"): str, + } +) +@websocket_api.async_response +@async_handle_failed_command +@async_get_matter_adapter +async def websocket_set_wifi_credentials( + hass: HomeAssistant, + connection: ActiveConnection, + msg: dict[str, Any], + matter: MatterAdapter, +) -> None: + """Set WiFi credentials for a device.""" + await matter.matter_client.set_wifi_credentials( + ssid=msg["network_name"], credentials=msg["password"] + ) + connection.send_result(msg[ID]) diff --git a/homeassistant/components/matter/config_flow.py b/homeassistant/components/matter/config_flow.py new file mode 100644 index 00000000000..f570b0cf14c --- /dev/null +++ b/homeassistant/components/matter/config_flow.py @@ -0,0 +1,325 @@ +"""Config flow for Matter integration.""" +from __future__ import annotations + +import asyncio +from typing import Any + +from matter_server.client import MatterClient +from matter_server.client.exceptions import CannotConnect, InvalidServerVersion +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.components.hassio import ( + AddonError, + AddonInfo, + AddonManager, + AddonState, + HassioServiceInfo, + is_hassio, +) +from homeassistant.const import CONF_URL +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import AbortFlow, FlowResult +from homeassistant.helpers import aiohttp_client + +from .addon import get_addon_manager +from .const import ( + ADDON_SLUG, + CONF_INTEGRATION_CREATED_ADDON, + CONF_USE_ADDON, + DOMAIN, + LOGGER, +) + +ADDON_SETUP_TIMEOUT = 5 +ADDON_SETUP_TIMEOUT_ROUNDS = 40 +DEFAULT_URL = "ws://localhost:5580/ws" +ON_SUPERVISOR_SCHEMA = vol.Schema({vol.Optional(CONF_USE_ADDON, default=True): bool}) + + +def get_manual_schema(user_input: dict[str, Any]) -> vol.Schema: + """Return a schema for the manual step.""" + default_url = user_input.get(CONF_URL, DEFAULT_URL) + return vol.Schema({vol.Required(CONF_URL, default=default_url): str}) + + +async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None: + """Validate the user input allows us to connect.""" + client = MatterClient(data[CONF_URL], aiohttp_client.async_get_clientsession(hass)) + await client.connect() + + +def build_ws_address(host: str, port: int) -> str: + """Return the websocket address.""" + return f"ws://{host}:{port}/ws" + + +class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for Matter.""" + + VERSION = 1 + + def __init__(self) -> None: + """Set up flow instance.""" + self.ws_address: str | None = None + # If we install the add-on we should uninstall it on entry remove. + self.integration_created_addon = False + self.install_task: asyncio.Task | None = None + self.start_task: asyncio.Task | None = None + self.use_addon = False + + async def async_step_install_addon( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Install Matter Server add-on.""" + if not self.install_task: + self.install_task = self.hass.async_create_task(self._async_install_addon()) + return self.async_show_progress( + step_id="install_addon", progress_action="install_addon" + ) + + try: + await self.install_task + except AddonError as err: + self.install_task = None + LOGGER.error(err) + return self.async_show_progress_done(next_step_id="install_failed") + + self.integration_created_addon = True + self.install_task = None + + return self.async_show_progress_done(next_step_id="start_addon") + + async def async_step_install_failed( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Add-on installation failed.""" + return self.async_abort(reason="addon_install_failed") + + async def _async_install_addon(self) -> None: + """Install the Matter Server add-on.""" + addon_manager: AddonManager = get_addon_manager(self.hass) + try: + await addon_manager.async_schedule_install_addon() + finally: + # Continue the flow after show progress when the task is done. + self.hass.async_create_task( + self.hass.config_entries.flow.async_configure(flow_id=self.flow_id) + ) + + async def _async_get_addon_discovery_info(self) -> dict: + """Return add-on discovery info.""" + addon_manager: AddonManager = get_addon_manager(self.hass) + try: + discovery_info_config = await addon_manager.async_get_addon_discovery_info() + except AddonError as err: + LOGGER.error(err) + raise AbortFlow("addon_get_discovery_info_failed") from err + + return discovery_info_config + + async def async_step_start_addon( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Start Matter Server add-on.""" + if not self.start_task: + self.start_task = self.hass.async_create_task(self._async_start_addon()) + return self.async_show_progress( + step_id="start_addon", progress_action="start_addon" + ) + + try: + await self.start_task + except (CannotConnect, AddonError, AbortFlow) as err: + self.start_task = None + LOGGER.error(err) + return self.async_show_progress_done(next_step_id="start_failed") + + self.start_task = None + return self.async_show_progress_done(next_step_id="finish_addon_setup") + + async def async_step_start_failed( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Add-on start failed.""" + return self.async_abort(reason="addon_start_failed") + + async def _async_start_addon(self) -> None: + """Start the Matter Server add-on.""" + addon_manager: AddonManager = get_addon_manager(self.hass) + + try: + await addon_manager.async_schedule_start_addon() + # Sleep some seconds to let the add-on start properly before connecting. + for _ in range(ADDON_SETUP_TIMEOUT_ROUNDS): + await asyncio.sleep(ADDON_SETUP_TIMEOUT) + try: + if not (ws_address := self.ws_address): + discovery_info = await self._async_get_addon_discovery_info() + ws_address = self.ws_address = build_ws_address( + discovery_info["host"], discovery_info["port"] + ) + await validate_input(self.hass, {CONF_URL: ws_address}) + except (AbortFlow, CannotConnect) as err: + LOGGER.debug( + "Add-on not ready yet, waiting %s seconds: %s", + ADDON_SETUP_TIMEOUT, + err, + ) + else: + break + else: + raise CannotConnect("Failed to start Matter Server add-on: timeout") + finally: + # Continue the flow after show progress when the task is done. + self.hass.async_create_task( + self.hass.config_entries.flow.async_configure(flow_id=self.flow_id) + ) + + async def _async_get_addon_info(self) -> AddonInfo: + """Return Matter Server add-on info.""" + addon_manager: AddonManager = get_addon_manager(self.hass) + try: + addon_info: AddonInfo = await addon_manager.async_get_addon_info() + except AddonError as err: + LOGGER.error(err) + raise AbortFlow("addon_info_failed") from err + + return addon_info + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the initial step.""" + if is_hassio(self.hass): + return await self.async_step_on_supervisor() + + return await self.async_step_manual() + + async def async_step_manual( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle a manual configuration.""" + if user_input is None: + return self.async_show_form( + step_id="manual", data_schema=get_manual_schema({}) + ) + + errors = {} + + try: + await validate_input(self.hass, user_input) + except CannotConnect: + errors["base"] = "cannot_connect" + except InvalidServerVersion: + errors["base"] = "invalid_server_version" + except Exception: # pylint: disable=broad-except + LOGGER.exception("Unexpected exception") + errors["base"] = "unknown" + else: + self.ws_address = user_input[CONF_URL] + + return await self._async_create_entry_or_abort() + + return self.async_show_form( + step_id="manual", data_schema=get_manual_schema(user_input), errors=errors + ) + + async def async_step_hassio(self, discovery_info: HassioServiceInfo) -> FlowResult: + """Receive configuration from add-on discovery info. + + This flow is triggered by the Matter Server add-on. + """ + if discovery_info.slug != ADDON_SLUG: + return self.async_abort(reason="not_matter_addon") + + await self._async_handle_discovery_without_unique_id() + + self.ws_address = build_ws_address( + discovery_info.config["host"], discovery_info.config["port"] + ) + + return await self.async_step_hassio_confirm() + + async def async_step_hassio_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Confirm the add-on discovery.""" + if user_input is not None: + return await self.async_step_on_supervisor( + user_input={CONF_USE_ADDON: True} + ) + + return self.async_show_form(step_id="hassio_confirm") + + async def async_step_on_supervisor( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle logic when on Supervisor host.""" + if user_input is None: + return self.async_show_form( + step_id="on_supervisor", data_schema=ON_SUPERVISOR_SCHEMA + ) + if not user_input[CONF_USE_ADDON]: + return await self.async_step_manual() + + self.use_addon = True + + addon_info = await self._async_get_addon_info() + + if addon_info.state == AddonState.RUNNING: + return await self.async_step_finish_addon_setup() + + if addon_info.state == AddonState.NOT_RUNNING: + return await self.async_step_start_addon() + + return await self.async_step_install_addon() + + async def async_step_finish_addon_setup( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Prepare info needed to complete the config entry.""" + if not self.ws_address: + discovery_info = await self._async_get_addon_discovery_info() + ws_address = self.ws_address = build_ws_address( + discovery_info["host"], discovery_info["port"] + ) + # Check that we can connect to the address. + try: + await validate_input(self.hass, {CONF_URL: ws_address}) + except CannotConnect: + return self.async_abort(reason="cannot_connect") + + return await self._async_create_entry_or_abort() + + async def _async_create_entry_or_abort(self) -> FlowResult: + """Return a config entry for the flow or abort if already configured.""" + assert self.ws_address is not None + + if existing_config_entries := self._async_current_entries(): + config_entry = existing_config_entries[0] + self.hass.config_entries.async_update_entry( + config_entry, + data={ + **config_entry.data, + CONF_URL: self.ws_address, + CONF_USE_ADDON: self.use_addon, + CONF_INTEGRATION_CREATED_ADDON: self.integration_created_addon, + }, + title=self.ws_address, + ) + await self.hass.config_entries.async_reload(config_entry.entry_id) + raise AbortFlow("reconfiguration_successful") + + # Abort any other flows that may be in progress + for progress in self._async_in_progress(): + self.hass.config_entries.flow.async_abort(progress["flow_id"]) + + return self.async_create_entry( + title=self.ws_address, + data={ + CONF_URL: self.ws_address, + CONF_USE_ADDON: self.use_addon, + CONF_INTEGRATION_CREATED_ADDON: self.integration_created_addon, + }, + ) diff --git a/homeassistant/components/matter/const.py b/homeassistant/components/matter/const.py new file mode 100644 index 00000000000..c5ec1173ac0 --- /dev/null +++ b/homeassistant/components/matter/const.py @@ -0,0 +1,10 @@ +"""Constants for the Matter integration.""" +import logging + +ADDON_SLUG = "core_matter_server" + +CONF_INTEGRATION_CREATED_ADDON = "integration_created_addon" +CONF_USE_ADDON = "use_addon" + +DOMAIN = "matter" +LOGGER = logging.getLogger(__package__) diff --git a/homeassistant/components/matter/device_platform.py b/homeassistant/components/matter/device_platform.py new file mode 100644 index 00000000000..25a83d28b98 --- /dev/null +++ b/homeassistant/components/matter/device_platform.py @@ -0,0 +1,24 @@ +"""All mappings of Matter devices to Home Assistant platforms.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +from homeassistant.const import Platform + +from .light import DEVICE_ENTITY as LIGHT_DEVICE_ENTITY + +if TYPE_CHECKING: + from matter_server.common.models.device_types import DeviceType + + from .entity import MatterEntityDescriptionBaseClass + + +DEVICE_PLATFORM: dict[ + Platform, + dict[ + type[DeviceType], + MatterEntityDescriptionBaseClass | list[MatterEntityDescriptionBaseClass], + ], +] = { + Platform.LIGHT: LIGHT_DEVICE_ENTITY, +} diff --git a/homeassistant/components/matter/entity.py b/homeassistant/components/matter/entity.py new file mode 100644 index 00000000000..019631750f4 --- /dev/null +++ b/homeassistant/components/matter/entity.py @@ -0,0 +1,118 @@ +"""Matter entity base class.""" +from __future__ import annotations + +from abc import abstractmethod +from collections.abc import Callable +from dataclasses import dataclass +import logging +from typing import TYPE_CHECKING, Any + +from matter_server.common.models.device_type_instance import MatterDeviceTypeInstance +from matter_server.common.models.events import EventType +from matter_server.common.models.node_device import AbstractMatterNodeDevice + +from homeassistant.core import callback +from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription + +from .const import DOMAIN + +if TYPE_CHECKING: + from matter_server.client import MatterClient + from matter_server.common.models.node import MatterAttribute + +LOGGER = logging.getLogger(__name__) + + +@dataclass +class MatterEntityDescription: + """Mixin to map a matter device to a Home Assistant entity.""" + + entity_cls: type[MatterEntity] + subscribe_attributes: tuple + + +@dataclass +class MatterEntityDescriptionBaseClass(EntityDescription, MatterEntityDescription): + """For typing a base class that inherits from both entity descriptions.""" + + +class MatterEntity(Entity): + """Entity class for Matter devices.""" + + entity_description: MatterEntityDescriptionBaseClass + _attr_should_poll = False + _attr_has_entity_name = True + + def __init__( + self, + matter_client: MatterClient, + node_device: AbstractMatterNodeDevice, + device_type_instance: MatterDeviceTypeInstance, + entity_description: MatterEntityDescriptionBaseClass, + ) -> None: + """Initialize the entity.""" + self.matter_client = matter_client + self._node_device = node_device + self._device_type_instance = device_type_instance + self.entity_description = entity_description + node = device_type_instance.node + self._unsubscribes: list[Callable] = [] + # for fast lookups we create a mapping to the attribute paths + self._attributes_map: dict[type, str] = {} + self._attr_unique_id = f"{matter_client.server_info.compressed_fabric_id}-{node.unique_id}-{device_type_instance.endpoint}-{device_type_instance.device_type.device_type}" + + @property + def device_info(self) -> DeviceInfo | None: + """Return device info for device registry.""" + return {"identifiers": {(DOMAIN, self._node_device.device_info().uniqueID)}} + + async def async_added_to_hass(self) -> None: + """Handle being added to Home Assistant.""" + await super().async_added_to_hass() + + # Subscribe to attribute updates. + for attr_cls in self.entity_description.subscribe_attributes: + if matter_attr := self.get_matter_attribute(attr_cls): + self._attributes_map[attr_cls] = matter_attr.path + self._unsubscribes.append( + self.matter_client.subscribe( + self._on_matter_event, + EventType.ATTRIBUTE_UPDATED, + self._device_type_instance.node.node_id, + matter_attr.path, + ) + ) + continue + # not sure if this can happen, but just in case log it. + LOGGER.warning("Attribute not found on device: %s", attr_cls) + + # make sure to update the attributes once + self._update_from_device() + + async def async_will_remove_from_hass(self) -> None: + """Run when entity will be removed from hass.""" + for unsub in self._unsubscribes: + unsub() + + @callback + def _on_matter_event(self, event: EventType, data: Any = None) -> None: + """Call on update.""" + self._update_from_device() + self.async_write_ha_state() + + @callback + @abstractmethod + def _update_from_device(self) -> None: + """Update data from Matter device.""" + + @callback + def get_matter_attribute(self, attribute: type) -> MatterAttribute | None: + """Lookup MatterAttribute instance on device instance by providing the attribute class.""" + return next( + ( + x + for x in self._device_type_instance.attributes + if x.attribute_type == attribute + ), + None, + ) diff --git a/homeassistant/components/matter/light.py b/homeassistant/components/matter/light.py new file mode 100644 index 00000000000..37454e3005c --- /dev/null +++ b/homeassistant/components/matter/light.py @@ -0,0 +1,173 @@ +"""Matter light.""" +from __future__ import annotations + +from dataclasses import dataclass +from functools import partial +from typing import TYPE_CHECKING, Any + +from chip.clusters import Objects as clusters +from matter_server.common.models import device_types + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, + ColorMode, + LightEntity, + LightEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .entity import MatterEntity, MatterEntityDescriptionBaseClass +from .util import renormalize + +if TYPE_CHECKING: + from .adapter import MatterAdapter + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Matter Light from Config Entry.""" + matter: MatterAdapter = hass.data[DOMAIN][config_entry.entry_id] + matter.register_platform_handler(Platform.LIGHT, async_add_entities) + + +class MatterLight(MatterEntity, LightEntity): + """Representation of a Matter light.""" + + entity_description: MatterLightEntityDescription + + def _supports_brightness(self) -> bool: + """Return if device supports brightness.""" + return ( + clusters.LevelControl.Attributes.CurrentLevel + in self.entity_description.subscribe_attributes + ) + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn light on.""" + if ATTR_BRIGHTNESS not in kwargs or not self._supports_brightness(): + await self.matter_client.send_device_command( + node_id=self._device_type_instance.node.node_id, + endpoint=self._device_type_instance.endpoint, + command=clusters.OnOff.Commands.On(), + ) + return + + level_control = self._device_type_instance.get_cluster(clusters.LevelControl) + level = round( + renormalize( + kwargs[ATTR_BRIGHTNESS], + (0, 255), + (level_control.minLevel, level_control.maxLevel), + ) + ) + + await self.matter_client.send_device_command( + node_id=self._device_type_instance.node.node_id, + endpoint=self._device_type_instance.endpoint, + command=clusters.LevelControl.Commands.MoveToLevelWithOnOff( + level=level, + # It's required in TLV. We don't implement transition time yet. + transitionTime=0, + ), + ) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn light off.""" + await self.matter_client.send_device_command( + node_id=self._device_type_instance.node.node_id, + endpoint=self._device_type_instance.endpoint, + command=clusters.OnOff.Commands.Off(), + ) + + @callback + def _update_from_device(self) -> None: + """Update from device.""" + if self._attr_supported_color_modes is None: + if self._supports_brightness(): + self._attr_supported_color_modes = {ColorMode.BRIGHTNESS} + + if attr := self.get_matter_attribute(clusters.OnOff.Attributes.OnOff): + self._attr_is_on = attr.value + + if ( + clusters.LevelControl.Attributes.CurrentLevel + in self.entity_description.subscribe_attributes + ): + level_control = self._device_type_instance.get_cluster( + clusters.LevelControl + ) + + # Convert brightness to Home Assistant = 0..255 + self._attr_brightness = round( + renormalize( + level_control.currentLevel, + (level_control.minLevel, level_control.maxLevel), + (0, 255), + ) + ) + + +@dataclass +class MatterLightEntityDescription( + LightEntityDescription, + MatterEntityDescriptionBaseClass, +): + """Matter light entity description.""" + + +# You can't set default values on inherited data classes +MatterLightEntityDescriptionFactory = partial( + MatterLightEntityDescription, entity_cls=MatterLight +) + +# Mapping of a Matter Device type to Light Entity Description. +# A Matter device type (instance) can consist of multiple attributes. +# For example a Color Light which has an attribute to control brightness +# but also for color. + +DEVICE_ENTITY: dict[ + type[device_types.DeviceType], + MatterEntityDescriptionBaseClass | list[MatterEntityDescriptionBaseClass], +] = { + device_types.OnOffLight: MatterLightEntityDescriptionFactory( + key=device_types.OnOffLight, + subscribe_attributes=(clusters.OnOff.Attributes.OnOff,), + ), + device_types.DimmableLight: MatterLightEntityDescriptionFactory( + key=device_types.DimmableLight, + subscribe_attributes=( + clusters.OnOff.Attributes.OnOff, + clusters.LevelControl.Attributes.CurrentLevel, + ), + ), + device_types.DimmablePlugInUnit: MatterLightEntityDescriptionFactory( + key=device_types.DimmablePlugInUnit, + subscribe_attributes=( + clusters.OnOff.Attributes.OnOff, + clusters.LevelControl.Attributes.CurrentLevel, + ), + ), + device_types.ColorTemperatureLight: MatterLightEntityDescriptionFactory( + key=device_types.ColorTemperatureLight, + subscribe_attributes=( + clusters.OnOff.Attributes.OnOff, + clusters.LevelControl.Attributes.CurrentLevel, + clusters.ColorControl, + ), + ), + device_types.ExtendedColorLight: MatterLightEntityDescriptionFactory( + key=device_types.ExtendedColorLight, + subscribe_attributes=( + clusters.OnOff.Attributes.OnOff, + clusters.LevelControl.Attributes.CurrentLevel, + clusters.ColorControl, + ), + ), +} diff --git a/homeassistant/components/matter/manifest.json b/homeassistant/components/matter/manifest.json new file mode 100644 index 00000000000..aa64ac4755e --- /dev/null +++ b/homeassistant/components/matter/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "matter", + "name": "Matter (BETA)", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/matter", + "requirements": ["python-matter-server==1.0.6"], + "dependencies": ["websocket_api"], + "codeowners": ["@MartinHjelmare", "@marcelveldt"], + "iot_class": "local_push" +} diff --git a/homeassistant/components/matter/services.yaml b/homeassistant/components/matter/services.yaml new file mode 100644 index 00000000000..18bb4be6452 --- /dev/null +++ b/homeassistant/components/matter/services.yaml @@ -0,0 +1,66 @@ +commission: + name: Commission device + description: > + Add a new device to your Matter network. + fields: + code: + name: Pairing code + description: The pairing code for the device. + required: true + selector: + text: +accept_shared_device: + name: Accept shared device + description: > + Add a shared device to your Matter network. + fields: + pin: + name: Pin code + description: The pin code for the device. + required: true + selector: + text: + +set_wifi: + name: Set Wi-Fi credentials + description: > + The Wi-Fi credentials will be sent as part of commissioning to a Matter device so it can connect to the Wi-Fi network. + fields: + ssid: + name: Network name + description: The SSID network name. + required: true + selector: + text: + password: + name: Password + description: The Wi-Fi network password. + required: true + selector: + text: + type: password +set_thread: + name: Set Thread network operational dataset + description: > + The Thread keys will be used as part of commissioning to let a Matter device join the Thread network. + + Get keys by running `ot-ctl dataset active -x` on the Open Thread Border Router. + fields: + thread_operation_dataset: + name: Thread Operational Dataset + description: The Thread Operational Dataset to set. + required: true + selector: + text: +open_commissioning_window: + name: Open Commissioning Window + description: > + Allow adding one of your devices to another Matter network by opening the commissioning window for this Matter device for 60 seconds. + fields: + device_id: + name: Device + description: The Matter device to add to the other Matter network. + required: true + selector: + device: + integration: matter diff --git a/homeassistant/components/matter/strings.json b/homeassistant/components/matter/strings.json new file mode 100644 index 00000000000..594998c236f --- /dev/null +++ b/homeassistant/components/matter/strings.json @@ -0,0 +1,47 @@ +{ + "config": { + "flow_title": "{name}", + "step": { + "manual": { + "data": { + "url": "[%key:common::config_flow::data::url%]" + } + }, + "on_supervisor": { + "title": "Select connection method", + "description": "Do you want to use the official Matter Server Supervisor add-on?\n\nIf you are already running the Matter Server in another add-on, in a custom container, natively etc., then do not select this option.", + "data": { + "use_addon": "Use the official Matter Server Supervisor add-on" + } + }, + "install_addon": { + "title": "The add-on installation has started" + }, + "start_addon": { + "title": "Starting add-on." + }, + "hassio_confirm": { + "title": "Set up the Matter integration with the Matter Server add-on" + } + }, + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_server_version": "The Matter server is not the correct version", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "addon_get_discovery_info_failed": "Failed to get Matter Server add-on discovery info.", + "addon_info_failed": "Failed to get Matter Server add-on info.", + "addon_install_failed": "Failed to install the Matter Server add-on.", + "addon_start_failed": "Failed to start the Matter Server add-on.", + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", + "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", + "not_matter_addon": "Discovered add-on is not the official Matter Server add-on.", + "reconfiguration_successful": "Successfully reconfigured the Matter integration." + }, + "progress": { + "install_addon": "Please wait while the Matter Server add-on installation finishes. This can take several minutes.", + "start_addon": "Please wait while the Matter Server add-on starts. This add-on is what powers Matter in Home Assistant. This may take some seconds." + } + } +} diff --git a/homeassistant/components/matter/translations/en.json b/homeassistant/components/matter/translations/en.json new file mode 100644 index 00000000000..a812772142f --- /dev/null +++ b/homeassistant/components/matter/translations/en.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "Failed to get Matter Server add-on discovery info.", + "addon_info_failed": "Failed to get Matter Server add-on info.", + "addon_install_failed": "Failed to install the Matter Server add-on.", + "addon_start_failed": "Failed to start the Matter Server add-on.", + "already_configured": "Device is already configured", + "already_in_progress": "Configuration flow is already in progress", + "not_matter_addon": "Discovered add-on is not the official Matter Server add-on.", + "reconfiguration_successful": "Successfully reconfigured the Matter integration." + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_server_version": "The Matter server is not the correct version", + "unknown": "Unexpected error" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Please wait while the Matter Server add-on installation finishes. This can take several minutes.", + "start_addon": "Please wait while the Matter Server add-on starts. This add-on is what powers Matter in Home Assistant. This may take some seconds." + }, + "step": { + "hassio_confirm": { + "title": "Set up the Matter integration with the Matter Server add-on" + }, + "install_addon": { + "title": "The add-on installation has started" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "Use the official Matter Server Supervisor add-on" + }, + "description": "Do you want to use the official Matter Server Supervisor add-on?\n\nIf you are already running the Matter Server in another add-on, in a custom container, natively etc., then do not select this option.", + "title": "Select connection method" + }, + "start_addon": { + "title": "Starting add-on." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/util.py b/homeassistant/components/matter/util.py new file mode 100644 index 00000000000..9f51ee0c0e6 --- /dev/null +++ b/homeassistant/components/matter/util.py @@ -0,0 +1,11 @@ +"""Provide integration utilities.""" +from __future__ import annotations + + +def renormalize( + number: float, from_range: tuple[float, float], to_range: tuple[float, float] +) -> float: + """Change value from from_range to to_range.""" + delta1 = from_range[1] - from_range[0] + delta2 = to_range[1] - to_range[0] + return (delta2 * (number - from_range[0]) / delta1) + to_range[0] diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 97c7b925378..5875c9021f6 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -235,6 +235,7 @@ FLOWS = { "lutron_caseta", "lyric", "mailgun", + "matter", "mazda", "meater", "melcloud", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 5b24a1ae02e..02068ecafa5 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -3046,6 +3046,12 @@ "config_flow": false, "iot_class": "cloud_push" }, + "matter": { + "name": "Matter (BETA)", + "integration_type": "hub", + "config_flow": true, + "iot_class": "local_push" + }, "mazda": { "name": "Mazda Connected Services", "integration_type": "hub", diff --git a/mypy.ini b/mypy.ini index 8d15bee1460..5a6615d0f48 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1543,6 +1543,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.matter.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.media_player.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/requirements_all.txt b/requirements_all.txt index 0eee6b15f39..6b97c0c5868 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2029,6 +2029,9 @@ python-kasa==0.5.0 # homeassistant.components.lirc # python-lirc==1.2.3 +# homeassistant.components.matter +python-matter-server==1.0.6 + # homeassistant.components.xiaomi_miio python-miio==0.5.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fd43ce5ab4c..44eddd3a92f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1416,6 +1416,9 @@ python-juicenet==1.1.0 # homeassistant.components.tplink python-kasa==0.5.0 +# homeassistant.components.matter +python-matter-server==1.0.6 + # homeassistant.components.xiaomi_miio python-miio==0.5.12 diff --git a/tests/components/matter/__init__.py b/tests/components/matter/__init__.py new file mode 100644 index 00000000000..a2452274b14 --- /dev/null +++ b/tests/components/matter/__init__.py @@ -0,0 +1 @@ +"""Tests for the Matter integration.""" diff --git a/tests/components/matter/common.py b/tests/components/matter/common.py new file mode 100644 index 00000000000..d26842a728f --- /dev/null +++ b/tests/components/matter/common.py @@ -0,0 +1,146 @@ +"""Provide common test tools.""" +from __future__ import annotations + +import asyncio +from functools import cache +import json +import logging +from typing import TYPE_CHECKING, Any +from unittest.mock import Mock, patch + +from matter_server.client import MatterClient +from matter_server.common.models.node import MatterNode +from matter_server.common.models.server_information import ServerInfo +import pytest + +from tests.common import MockConfigEntry, load_fixture + +if TYPE_CHECKING: + from homeassistant.core import HomeAssistant + +MOCK_FABRIC_ID = 12341234 +MOCK_COMPR_FABRIC_ID = 1234 + +# TEMP: Tests need to be fixed +pytestmark = pytest.mark.skip("all tests still WIP") + + +class MockClient(MatterClient): + """Represent a mock Matter client.""" + + mock_client_disconnect: asyncio.Event + mock_commands: dict[type, Any] = {} + mock_sent_commands: list[dict[str, Any]] = [] + + def __init__(self) -> None: + """Initialize the mock client.""" + super().__init__("mock-url", None) + self.mock_commands: dict[type, Any] = {} + self.mock_sent_commands = [] + self.server_info = ServerInfo( + fabric_id=MOCK_FABRIC_ID, compressed_fabric_id=MOCK_COMPR_FABRIC_ID + ) + + async def connect(self) -> None: + """Connect to the Matter server.""" + self.server_info = Mock(compressed_abric_d=MOCK_COMPR_FABRIC_ID) + + async def listen(self, driver_ready: asyncio.Event) -> None: + """Listen for events.""" + driver_ready.set() + self.mock_client_disconnect = asyncio.Event() + await self.mock_client_disconnect.wait() + + def mock_command(self, command_type: type, response: Any) -> None: + """Mock a command.""" + self.mock_commands[command_type] = response + + async def async_send_command( + self, + command: str, + args: dict[str, Any], + require_schema: int | None = None, + ) -> dict: + """Send mock commands.""" + if command == "device_controller.SendCommand" and ( + (cmd_type := type(args.get("payload"))) in self.mock_commands + ): + self.mock_sent_commands.append(args) + return self.mock_commands[cmd_type] + + return await super().async_send_command(command, args, require_schema) + + async def async_send_command_no_wait( + self, command: str, args: dict[str, Any], require_schema: int | None = None + ) -> None: + """Send a command without waiting for the response.""" + if command == "SendCommand" and ( + (cmd_type := type(args.get("payload"))) in self.mock_commands + ): + self.mock_sent_commands.append(args) + return self.mock_commands[cmd_type] + + return await super().async_send_command_no_wait(command, args, require_schema) + + +@pytest.fixture +async def mock_matter() -> Mock: + """Mock matter fixture.""" + return await get_mock_matter() + + +async def get_mock_matter() -> Mock: + """Get mock Matter.""" + return Mock( + adapter=Mock(logger=logging.getLogger("mock_matter")), client=MockClient() + ) + + +@cache +def load_node_fixture(fixture: str) -> str: + """Load a fixture.""" + return load_fixture(f"matter/nodes/{fixture}.json") + + +def load_and_parse_node_fixture(fixture: str) -> dict[str, Any]: + """Load and parse a node fixture.""" + return json.loads(load_node_fixture(fixture)) + + +async def setup_integration_with_node_fixture( + hass: HomeAssistant, hass_storage: dict[str, Any], node_fixture: str +) -> MatterNode: + """Set up Matter integration with fixture as node.""" + node_data = load_and_parse_node_fixture(node_fixture) + node = MatterNode( + await get_mock_matter(), + node_data, + ) + config_entry = MockConfigEntry( + domain="matter", data={"url": "http://mock-matter-server-url"} + ) + config_entry.add_to_hass(hass) + + storage_key = f"matter_{config_entry.entry_id}" + hass_storage[storage_key] = { + "version": 1, + "minor_version": 0, + "key": storage_key, + "data": { + "compressed_fabric_id": MOCK_COMPR_FABRIC_ID, + "next_node_id": 4339, + "nodes": {str(node.node_id): node_data}, + }, + } + + with patch( + "matter_server.client.matter.Client", return_value=node.matter.client + ), patch( + "matter_server.client.model.node.MatterDeviceTypeInstance.subscribe_updates", + ), patch( + "matter_server.client.model.node.MatterDeviceTypeInstance.update_attributes" + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + return node diff --git a/tests/components/matter/conftest.py b/tests/components/matter/conftest.py new file mode 100644 index 00000000000..6a8ffd152bc --- /dev/null +++ b/tests/components/matter/conftest.py @@ -0,0 +1,187 @@ +"""Provide common fixtures.""" +from __future__ import annotations + +import asyncio +from collections.abc import AsyncGenerator, Generator +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry + + +@pytest.fixture(name="matter_client") +async def matter_client_fixture() -> AsyncGenerator[MagicMock, None]: + """Fixture for a Matter client.""" + with patch( + "homeassistant.components.matter.MatterClient", autospec=True + ) as client_class: + client = client_class.return_value + + async def connect() -> None: + """Mock connect.""" + await asyncio.sleep(0) + client.connected = True + + async def listen(init_ready: asyncio.Event | None) -> None: + """Mock listen.""" + if init_ready is not None: + init_ready.set() + + client.connect = AsyncMock(side_effect=connect) + client.start_listening = AsyncMock(side_effect=listen) + + yield client + + +@pytest.fixture(name="integration") +async def integration_fixture( + hass: HomeAssistant, matter_client: MagicMock +) -> MockConfigEntry: + """Set up the Matter integration.""" + entry = MockConfigEntry(domain="matter", data={"url": "ws://localhost:5580/ws"}) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + return entry + + +@pytest.fixture(name="create_backup") +def create_backup_fixture() -> Generator[AsyncMock, None, None]: + """Mock Supervisor create backup of add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_create_backup" + ) as create_backup: + yield create_backup + + +@pytest.fixture(name="addon_store_info") +def addon_store_info_fixture() -> Generator[AsyncMock, None, None]: + """Mock Supervisor add-on store info.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_get_addon_store_info" + ) as addon_store_info: + addon_store_info.return_value = { + "installed": None, + "state": None, + "version": "1.0.0", + } + yield addon_store_info + + +@pytest.fixture(name="addon_info") +def addon_info_fixture() -> Generator[AsyncMock, None, None]: + """Mock Supervisor add-on info.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_get_addon_info", + ) as addon_info: + addon_info.return_value = { + "hostname": None, + "options": {}, + "state": None, + "update_available": False, + "version": None, + } + yield addon_info + + +@pytest.fixture(name="addon_not_installed") +def addon_not_installed_fixture( + addon_store_info: AsyncMock, addon_info: AsyncMock +) -> AsyncMock: + """Mock add-on not installed.""" + return addon_info + + +@pytest.fixture(name="addon_installed") +def addon_installed_fixture( + addon_store_info: AsyncMock, addon_info: AsyncMock +) -> AsyncMock: + """Mock add-on already installed but not running.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "stopped", + "version": "1.0.0", + } + addon_info.return_value["hostname"] = "core-matter-server" + addon_info.return_value["state"] = "stopped" + addon_info.return_value["version"] = "1.0.0" + return addon_info + + +@pytest.fixture(name="addon_running") +def addon_running_fixture( + addon_store_info: AsyncMock, addon_info: AsyncMock +) -> AsyncMock: + """Mock add-on already running.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "started", + "version": "1.0.0", + } + addon_info.return_value["hostname"] = "core-matter-server" + addon_info.return_value["state"] = "started" + addon_info.return_value["version"] = "1.0.0" + return addon_info + + +@pytest.fixture(name="install_addon") +def install_addon_fixture( + addon_store_info: AsyncMock, addon_info: AsyncMock +) -> Generator[AsyncMock, None, None]: + """Mock install add-on.""" + + async def install_addon_side_effect(hass: HomeAssistant, slug: str) -> None: + """Mock install add-on.""" + addon_store_info.return_value = { + "installed": "1.0.0", + "state": "stopped", + "version": "1.0.0", + } + addon_info.return_value["state"] = "stopped" + addon_info.return_value["version"] = "1.0.0" + + with patch( + "homeassistant.components.hassio.addon_manager.async_install_addon" + ) as install_addon: + install_addon.side_effect = install_addon_side_effect + yield install_addon + + +@pytest.fixture(name="start_addon") +def start_addon_fixture() -> Generator[AsyncMock, None, None]: + """Mock start add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_start_addon" + ) as start_addon: + yield start_addon + + +@pytest.fixture(name="stop_addon") +def stop_addon_fixture() -> Generator[AsyncMock, None, None]: + """Mock stop add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_stop_addon" + ) as stop_addon: + yield stop_addon + + +@pytest.fixture(name="uninstall_addon") +def uninstall_addon_fixture() -> Generator[AsyncMock, None, None]: + """Mock uninstall add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_uninstall_addon" + ) as uninstall_addon: + yield uninstall_addon + + +@pytest.fixture(name="update_addon") +def update_addon_fixture() -> Generator[AsyncMock, None, None]: + """Mock update add-on.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_update_addon" + ) as update_addon: + yield update_addon diff --git a/tests/components/matter/fixtures/nodes/fake-bridge-two-light.json b/tests/components/matter/fixtures/nodes/fake-bridge-two-light.json new file mode 100644 index 00000000000..49cc1e4f217 --- /dev/null +++ b/tests/components/matter/fixtures/nodes/fake-bridge-two-light.json @@ -0,0 +1,174 @@ +{ + "attributes": { + "0": { + "Descriptor": { + "deviceTypeList": [ + { + "type": 22, + "revision": 1, + "_type": "chip.clusters.Objects.Descriptor.Structs.DeviceTypeStruct" + }, + { + "type": 14, + "revision": 1, + "_type": "chip.clusters.Objects.Descriptor.Structs.DeviceTypeStruct" + } + ], + "serverList": [29, 37, 40, 48, 49, 50, 51, 60, 62, 64, 65], + "clientList": [], + "partsList": [9, 10], + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.Descriptor" + }, + "Basic": { + "dataModelRevision": 0, + "vendorName": "Mock Vendor", + "vendorID": 1234, + "productName": "Mock Bridge", + "productID": 2, + "nodeLabel": "My Mock Bridge", + "location": "nl", + "hardwareVersion": 123, + "hardwareVersionString": "TEST_VERSION", + "softwareVersion": 12345, + "softwareVersionString": "123.4.5", + "manufacturingDate": null, + "partNumber": null, + "productURL": null, + "productLabel": null, + "serialNumber": null, + "localConfigDisabled": null, + "reachable": null, + "uniqueID": "mock-hub-id", + "capabilityMinima": null, + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 18, 65528, 65529, 65531, 65532, + 65533 + ], + "featureMap": 0, + "clusterRevision": 3, + "_type": "chip.clusters.Objects.Basic" + } + }, + "9": { + "OnOff": { + "onOff": true, + "globalSceneControl": true, + "onTime": 0, + "offWaitTime": 0, + "startUpOnOff": 0, + "generatedCommandList": [], + "acceptedCommandList": [0, 1, 2, 64, 65, 66], + "attributeList": [ + 0, 16384, 16385, 16386, 16387, 65528, 65529, 65531, 65532, 65533 + ], + "featureMap": 1, + "clusterRevision": 4, + "_type": "chip.clusters.Objects.OnOff" + }, + "Descriptor": { + "deviceTypeList": [ + { + "type": 256, + "revision": 1, + "_type": "chip.clusters.Objects.Descriptor.Structs.DeviceTypeStruct" + }, + { + "type": 19, + "revision": 1, + "_type": "chip.clusters.Objects.Descriptor.Structs.DeviceTypeStruct" + } + ], + "serverList": [6, 29, 57, 768, 8, 40], + "clientList": [], + "partsList": [], + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [0, 1, 2, 3, 65528, 65529, 65531, 65533], + "featureMap": null, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.Descriptor" + }, + "BridgedDeviceBasic": { + "nodeLabel": "Kitchen Ceiling", + "reachable": true, + "vendorID": 1234, + "softwareVersionString": "67.8.9", + "softwareVersion": 6789, + "vendorName": "Mock Vendor", + "productName": "Mock Light", + "uniqueID": "mock-id-kitchen-ceiling", + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [ + 5, 17, 2, 4, 10, 9, 1, 3, 18, 65528, 65529, 65531, 65532, 65533 + ], + "_type": "chip.clusters.Objects.BridgedDeviceBasic" + } + }, + "10": { + "OnOff": { + "onOff": false, + "globalSceneControl": true, + "onTime": 0, + "offWaitTime": 0, + "startUpOnOff": 0, + "generatedCommandList": [], + "acceptedCommandList": [0, 1, 2, 64, 65, 66], + "attributeList": [ + 0, 16384, 16385, 16386, 16387, 65528, 65529, 65531, 65532, 65533 + ], + "featureMap": 1, + "clusterRevision": 4, + "_type": "chip.clusters.Objects.OnOff" + }, + "Descriptor": { + "deviceTypeList": [ + { + "type": 256, + "revision": 1, + "_type": "chip.clusters.Objects.Descriptor.Structs.DeviceTypeStruct" + }, + { + "type": 19, + "revision": 1, + "_type": "chip.clusters.Objects.Descriptor.Structs.DeviceTypeStruct" + } + ], + "serverList": [6, 29, 57, 768, 40], + "clientList": [], + "partsList": [], + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [0, 1, 2, 3, 65528, 65529, 65531, 65533], + "featureMap": null, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.Descriptor" + }, + "BridgedDeviceBasic": { + "nodeLabel": "Living Room Ceiling", + "reachable": true, + "vendorID": 1234, + "softwareVersionString": "1.49.1", + "softwareVersion": 19988481, + "vendorName": "Mock Vendor", + "productName": "Mock Light", + "uniqueID": "mock-id-living-room-ceiling", + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [ + 5, 17, 2, 4, 10, 9, 1, 3, 18, 65528, 65529, 65531, 65532, 65533 + ], + "_type": "chip.clusters.Objects.BridgedDeviceBasic" + } + } + }, + "events": [], + "node_id": 4338 +} diff --git a/tests/components/matter/fixtures/nodes/lighting-example-app.json b/tests/components/matter/fixtures/nodes/lighting-example-app.json new file mode 100644 index 00000000000..06e903d866c --- /dev/null +++ b/tests/components/matter/fixtures/nodes/lighting-example-app.json @@ -0,0 +1,882 @@ +{ + "attributes": { + "0": { + "Groups": { + "nameSupport": 128, + "generatedCommandList": [0, 1, 2, 3], + "acceptedCommandList": [0, 1, 2, 3, 4, 5], + "attributeList": [0, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 4, + "_type": "chip.clusters.Objects.Groups" + }, + "Descriptor": { + "deviceTypeList": [ + { + "type": 22, + "revision": 1, + "_type": "chip.clusters.Objects.Descriptor.Structs.DeviceTypeStruct" + } + ], + "serverList": [ + 4, 29, 31, 40, 42, 43, 44, 48, 49, 50, 51, 52, 53, 54, 55, 59, 60, 62, + 63, 64, 65 + ], + "clientList": [41], + "partsList": [1], + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.Descriptor" + }, + "AccessControl": { + "acl": [ + { + "privilege": 5, + "authMode": 2, + "subjects": [1], + "targets": null, + "fabricIndex": 1, + "_type": "chip.clusters.Objects.AccessControl.Structs.AccessControlEntry" + } + ], + "extension": [], + "subjectsPerAccessControlEntry": 4, + "targetsPerAccessControlEntry": 3, + "accessControlEntriesPerFabric": 3, + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.AccessControl" + }, + "Basic": { + "dataModelRevision": 1, + "vendorName": "Nabu Casa", + "vendorID": 65521, + "productName": "M5STAMP Lighting App", + "productID": 32768, + "nodeLabel": "My Cool Light", + "location": "XX", + "hardwareVersion": 0, + "hardwareVersionString": "v1.0", + "softwareVersion": 1, + "softwareVersionString": "55ab764bea", + "manufacturingDate": "20200101", + "partNumber": "", + "productURL": "", + "productLabel": "", + "serialNumber": "", + "localConfigDisabled": false, + "reachable": true, + "uniqueID": "BE8F70AA40DDAE41", + "capabilityMinima": { + "caseSessionsPerFabric": 3, + "subscriptionsPerFabric": 65506, + "_type": "chip.clusters.Objects.Basic.Structs.CapabilityMinimaStruct" + }, + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 65528, 65529, 65531, 65532, 65533 + ], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.Basic" + }, + "OtaSoftwareUpdateRequestor": { + "defaultOtaProviders": [], + "updatePossible": true, + "updateState": 0, + "updateStateProgress": 0, + "generatedCommandList": [], + "acceptedCommandList": [0], + "attributeList": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor" + }, + "LocalizationConfiguration": { + "activeLocale": "en-US", + "supportedLocales": [ + "en-US", + "de-DE", + "fr-FR", + "en-GB", + "es-ES", + "zh-CN", + "it-IT", + "ja-JP" + ], + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [0, 1, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.LocalizationConfiguration" + }, + "TimeFormatLocalization": { + "hourFormat": 0, + "activeCalendarType": 0, + "supportedCalendarTypes": [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 7], + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [0, 1, 2, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.TimeFormatLocalization" + }, + "GeneralCommissioning": { + "breadcrumb": 0, + "basicCommissioningInfo": { + "failSafeExpiryLengthSeconds": 60, + "_type": "chip.clusters.Objects.GeneralCommissioning.Structs.BasicCommissioningInfo" + }, + "regulatoryConfig": 0, + "locationCapability": 0, + "supportsConcurrentConnection": true, + "generatedCommandList": [1, 3, 5], + "acceptedCommandList": [0, 2, 4], + "attributeList": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533], + "featureMap": 6, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.GeneralCommissioning" + }, + "NetworkCommissioning": { + "maxNetworks": 1, + "networks": [ + { + "networkID": { + "_type": "bytes", + "value": "TGF6eUlvVA==" + }, + "connected": true, + "_type": "chip.clusters.Objects.NetworkCommissioning.Structs.NetworkInfo" + } + ], + "scanMaxTimeSeconds": 10, + "connectMaxTimeSeconds": 30, + "interfaceEnabled": true, + "lastNetworkingStatus": 0, + "lastNetworkID": { + "_type": "bytes", + "value": "TGF6eUlvVA==" + }, + "lastConnectErrorValue": null, + "generatedCommandList": [1, 5, 7], + "acceptedCommandList": [0, 2, 4, 6, 8], + "attributeList": [ + 0, 1, 2, 3, 4, 5, 6, 7, 65528, 65529, 65531, 65532, 65533 + ], + "featureMap": 1, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.NetworkCommissioning" + }, + "DiagnosticLogs": { + "generatedCommandList": [1], + "acceptedCommandList": [0], + "attributeList": [65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.DiagnosticLogs" + }, + "GeneralDiagnostics": { + "networkInterfaces": [ + { + "name": "WIFI_AP_DEF", + "isOperational": true, + "offPremiseServicesReachableIPv4": null, + "offPremiseServicesReachableIPv6": null, + "hardwareAddress": { + "_type": "bytes", + "value": "AAAAAAAA" + }, + "IPv4Addresses": [], + "IPv6Addresses": [], + "type": 1, + "_type": "chip.clusters.Objects.GeneralDiagnostics.Structs.NetworkInterfaceType" + }, + { + "name": "WIFI_STA_DEF", + "isOperational": true, + "offPremiseServicesReachableIPv4": null, + "offPremiseServicesReachableIPv6": null, + "hardwareAddress": { + "_type": "bytes", + "value": "hPcDJ8rI" + }, + "IPv4Addresses": [], + "IPv6Addresses": [], + "type": 1, + "_type": "chip.clusters.Objects.GeneralDiagnostics.Structs.NetworkInterfaceType" + } + ], + "rebootCount": 12, + "upTime": 458, + "totalOperationalHours": 0, + "bootReasons": 1, + "activeHardwareFaults": [], + "activeRadioFaults": [], + "activeNetworkFaults": [], + "testEventTriggersEnabled": false, + "generatedCommandList": [], + "acceptedCommandList": [0], + "attributeList": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 65528, 65529, 65531, 65532, 65533 + ], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.GeneralDiagnostics" + }, + "SoftwareDiagnostics": { + "threadMetrics": [], + "currentHeapFree": 116140, + "currentHeapUsed": 138932, + "currentHeapHighWatermark": 153796, + "generatedCommandList": [], + "acceptedCommandList": [0], + "attributeList": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.SoftwareDiagnostics" + }, + "ThreadNetworkDiagnostics": { + "TLVValue": { + "0": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "1": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "2": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "3": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "4": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "5": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "6": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "7": [], + "8": [], + "9": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "10": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "11": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "12": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "13": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "14": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "15": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "16": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "17": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "18": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "19": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "20": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "21": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "22": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "23": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "24": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "25": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "26": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "27": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "28": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "29": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "30": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "31": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "32": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "33": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "34": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "35": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "36": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "37": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "38": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "39": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "40": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "41": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "42": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "43": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "44": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "45": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "46": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "47": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "48": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "49": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "50": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "51": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "52": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "53": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "54": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "55": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "56": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "57": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "58": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "59": [], + "60": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + }, + "61": [], + "62": [], + "65532": 15, + "65533": 1, + "65528": [], + "65529": [0], + "65531": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 65528, 65529, 65531, 65532, + 65533 + ] + }, + "Reason": "Failed to decode field [].channel, expected type , got " + }, + "WiFiNetworkDiagnostics": { + "bssid": { + "_type": "bytes", + "value": "1iH5ZUbu" + }, + "securityType": 4, + "wiFiVersion": 3, + "channelNumber": 1, + "rssi": -38, + "beaconLostCount": 0, + "beaconRxCount": 0, + "packetMulticastRxCount": 0, + "packetMulticastTxCount": 0, + "packetUnicastRxCount": 0, + "packetUnicastTxCount": 0, + "currentMaxRate": 0, + "overrunCount": 0, + "generatedCommandList": [], + "acceptedCommandList": [0], + "attributeList": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 65528, 65529, 65531, 65532, + 65533 + ], + "featureMap": 3, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.WiFiNetworkDiagnostics" + }, + "EthernetNetworkDiagnostics": { + "PHYRate": null, + "fullDuplex": null, + "packetRxCount": 0, + "packetTxCount": 0, + "txErrCount": 0, + "collisionCount": 0, + "overrunCount": 0, + "carrierDetect": null, + "timeSinceReset": 0, + "generatedCommandList": [], + "acceptedCommandList": [0], + "attributeList": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 65528, 65529, 65531, 65532, 65533 + ], + "featureMap": 3, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.EthernetNetworkDiagnostics" + }, + "Switch": { + "numberOfPositions": null, + "currentPosition": null, + "multiPressMax": null, + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.Switch" + }, + "AdministratorCommissioning": { + "windowStatus": 0, + "adminFabricIndex": 0, + "adminVendorId": 0, + "generatedCommandList": [], + "acceptedCommandList": [0, 1, 2], + "attributeList": [0, 1, 2, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.AdministratorCommissioning" + }, + "OperationalCredentials": { + "NOCs": [ + { + "noc": { + "_type": "bytes", + "value": "FTABAQEkAgE3AyQTARgmBIAigScmBYAlTTo3BiQVASUR8RAYJAcBJAgBMAlBBHQsjZ/8Hpm4iqznEv0dAO03bZx8LDgqpIOpBsHeysZu8KAmI0K+p6B8FuI1h3wld1V+tIj5OHVHtrigg6Ssl043CjUBKAEYJAIBNgMEAgQBGDAEFEWrZiyeoUgEIXz4c40+Nzq9cfxHMAUUSTs2LnMMrX7nj+dns0cSq3SmK3MYMAtAoFdxyvsbLm6VekNCQ6yqJOucAcRSVY3Si4ov1alKPK9CaIPl+u5dvBWNfyEPXSLsPmzyfd2njl8WRz8e7CBiSRg=" + }, + "icac": { + "_type": "bytes", + "value": "FTABAQAkAgE3AyQUABgmBIAigScmBYAlTTo3BiQTARgkBwEkCAEwCUEE09c6S9xVbf3/blpXSgRAZzKXx/4KQC274cEfa2tFjdVAJYJUvM/8PMurRHEroPpA3FXpJ8/hfabkNvHGi2l8tTcKNQEpARgkAmAwBBRJOzYucwytfueP52ezRxKrdKYrczAFFBf0ohq+KHQlEVBIMgEeZCBPR72hGDALQNwd63sOjWKYhjlvDJmcPtIzljSsXlQ10vFrB5j9V9CdiZHDfy537G39fo0RJmpU63EGXYEtXVrEfSMiafshKVcY" + }, + "fabricIndex": 1, + "_type": "chip.clusters.Objects.OperationalCredentials.Structs.NOCStruct" + } + ], + "fabrics": [ + { + "rootPublicKey": { + "_type": "bytes", + "value": "BBGg+O3i3tDVYryXkUmEXk1fnSMHN06+poGIfZODdvbZW4JvxHnrQVAxvZWIE6poLa0sKA8X8A7jmJsVFMUqLFM=" + }, + "vendorId": 35328, + "fabricId": 1, + "nodeId": 4337, + "label": "", + "fabricIndex": 1, + "_type": "chip.clusters.Objects.OperationalCredentials.Structs.FabricDescriptor" + } + ], + "supportedFabrics": 5, + "commissionedFabrics": 1, + "trustedRootCertificates": [ + { + "_type": "bytes", + "value": "FTABAQAkAgE3AyQUABgmBIAigScmBYAlTTo3BiQUABgkBwEkCAEwCUEEEaD47eLe0NVivJeRSYReTV+dIwc3Tr6mgYh9k4N29tlbgm/EeetBUDG9lYgTqmgtrSwoDxfwDuOYmxUUxSosUzcKNQEpARgkAmAwBBQX9KIavih0JRFQSDIBHmQgT0e9oTAFFBf0ohq+KHQlEVBIMgEeZCBPR72hGDALQO3xFiF2cEXl+/kk0CQfedzHJxSJiziHEjWCMjIj7SVlDVx4CpvNYHnheq+9vJFgcL8JQhAEdz6p6C3INBDL7dsY" + } + ], + "currentFabricIndex": 1, + "generatedCommandList": [1, 3, 5, 8], + "acceptedCommandList": [0, 2, 4, 6, 7, 9, 10, 11], + "attributeList": [0, 1, 2, 3, 4, 5, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.OperationalCredentials" + }, + "GroupKeyManagement": { + "groupKeyMap": [], + "groupTable": [], + "maxGroupsPerFabric": 3, + "maxGroupKeysPerFabric": 2, + "generatedCommandList": [2, 5], + "acceptedCommandList": [0, 1, 3, 4], + "attributeList": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.GroupKeyManagement" + }, + "FixedLabel": { + "labelList": [ + { + "label": "room", + "value": "bedroom 2", + "_type": "chip.clusters.Objects.FixedLabel.Structs.LabelStruct" + }, + { + "label": "orientation", + "value": "North", + "_type": "chip.clusters.Objects.FixedLabel.Structs.LabelStruct" + }, + { + "label": "floor", + "value": "2", + "_type": "chip.clusters.Objects.FixedLabel.Structs.LabelStruct" + }, + { + "label": "direction", + "value": "up", + "_type": "chip.clusters.Objects.FixedLabel.Structs.LabelStruct" + } + ], + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [0, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.FixedLabel" + }, + "UserLabel": { + "labelList": [], + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [0, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.UserLabel" + } + }, + "1": { + "Identify": { + "identifyTime": 0, + "identifyType": 0, + "generatedCommandList": [], + "acceptedCommandList": [0, 64], + "attributeList": [0, 1, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 4, + "_type": "chip.clusters.Objects.Identify" + }, + "Groups": { + "nameSupport": 128, + "generatedCommandList": [0, 1, 2, 3], + "acceptedCommandList": [0, 1, 2, 3, 4, 5], + "attributeList": [0, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 4, + "_type": "chip.clusters.Objects.Groups" + }, + "OnOff": { + "onOff": false, + "globalSceneControl": true, + "onTime": 0, + "offWaitTime": 0, + "startUpOnOff": null, + "generatedCommandList": [], + "acceptedCommandList": [0, 1, 2, 64, 65, 66], + "attributeList": [ + 0, 16384, 16385, 16386, 16387, 65528, 65529, 65531, 65532, 65533 + ], + "featureMap": 1, + "clusterRevision": 4, + "_type": "chip.clusters.Objects.OnOff" + }, + "LevelControl": { + "currentLevel": 254, + "remainingTime": 0, + "minLevel": 0, + "maxLevel": 254, + "currentFrequency": 0, + "minFrequency": 0, + "maxFrequency": 0, + "options": 0, + "onOffTransitionTime": 0, + "onLevel": null, + "onTransitionTime": 0, + "offTransitionTime": 0, + "defaultMoveRate": 50, + "startUpCurrentLevel": null, + "generatedCommandList": [], + "acceptedCommandList": [0, 1, 2, 3, 4, 5, 6, 7], + "attributeList": [ + 0, 1, 2, 3, 4, 5, 6, 15, 16, 17, 18, 19, 20, 16384, 65528, 65529, + 65531, 65532, 65533 + ], + "featureMap": 3, + "clusterRevision": 5, + "_type": "chip.clusters.Objects.LevelControl" + }, + "Descriptor": { + "deviceTypeList": [ + { + "type": 257, + "revision": 1, + "_type": "chip.clusters.Objects.Descriptor.Structs.DeviceTypeStruct" + } + ], + "serverList": [3, 4, 6, 8, 29, 768, 1030], + "clientList": [], + "partsList": [], + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 1, + "_type": "chip.clusters.Objects.Descriptor" + }, + "ColorControl": { + "currentHue": 0, + "currentSaturation": 0, + "remainingTime": 0, + "currentX": 24939, + "currentY": 24701, + "driftCompensation": null, + "compensationText": null, + "colorTemperatureMireds": 0, + "colorMode": 2, + "options": 0, + "numberOfPrimaries": 0, + "primary1X": null, + "primary1Y": null, + "primary1Intensity": null, + "primary2X": null, + "primary2Y": null, + "primary2Intensity": null, + "primary3X": null, + "primary3Y": null, + "primary3Intensity": null, + "primary4X": null, + "primary4Y": null, + "primary4Intensity": null, + "primary5X": null, + "primary5Y": null, + "primary5Intensity": null, + "primary6X": null, + "primary6Y": null, + "primary6Intensity": null, + "whitePointX": null, + "whitePointY": null, + "colorPointRX": null, + "colorPointRY": null, + "colorPointRIntensity": null, + "colorPointGX": null, + "colorPointGY": null, + "colorPointGIntensity": null, + "colorPointBX": null, + "colorPointBY": null, + "colorPointBIntensity": null, + "enhancedCurrentHue": 0, + "enhancedColorMode": 2, + "colorLoopActive": 0, + "colorLoopDirection": 0, + "colorLoopTime": 25, + "colorLoopStartEnhancedHue": 8960, + "colorLoopStoredEnhancedHue": 0, + "colorCapabilities": 0, + "colorTempPhysicalMinMireds": 0, + "colorTempPhysicalMaxMireds": 65279, + "coupleColorTempToLevelMinMireds": 0, + "startUpColorTemperatureMireds": 0, + "generatedCommandList": [], + "acceptedCommandList": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 64, 65, 66, 67, 68, 71, 75, 76 + ], + "attributeList": [ + 0, 1, 2, 3, 4, 7, 8, 15, 16, 16384, 16385, 16386, 16387, 16388, 16389, + 16390, 16394, 16395, 16396, 16397, 16400, 65528, 65529, 65531, 65532, + 65533 + ], + "featureMap": 31, + "clusterRevision": 5, + "_type": "chip.clusters.Objects.ColorControl" + }, + "OccupancySensing": { + "occupancy": 0, + "occupancySensorType": 0, + "occupancySensorTypeBitmap": 1, + "pirOccupiedToUnoccupiedDelay": null, + "pirUnoccupiedToOccupiedDelay": null, + "pirUnoccupiedToOccupiedThreshold": null, + "ultrasonicOccupiedToUnoccupiedDelay": null, + "ultrasonicUnoccupiedToOccupiedDelay": null, + "ultrasonicUnoccupiedToOccupiedThreshold": null, + "physicalContactOccupiedToUnoccupiedDelay": null, + "physicalContactUnoccupiedToOccupiedDelay": null, + "physicalContactUnoccupiedToOccupiedThreshold": null, + "generatedCommandList": [], + "acceptedCommandList": [], + "attributeList": [0, 1, 2, 65528, 65529, 65531, 65532, 65533], + "featureMap": 0, + "clusterRevision": 3, + "_type": "chip.clusters.Objects.OccupancySensing" + } + } + }, + "events": [ + { + "Header": { + "EndpointId": 0, + "ClusterId": 40, + "EventId": 0, + "EventNumber": 262144, + "Priority": 2, + "Timestamp": 2019, + "TimestampType": 0 + }, + "Status": 0, + "Data": { + "softwareVersion": 1, + "_type": "chip.clusters.Objects.Basic.Events.StartUp" + } + }, + { + "Header": { + "EndpointId": 0, + "ClusterId": 51, + "EventId": 3, + "EventNumber": 262145, + "Priority": 2, + "Timestamp": 2020, + "TimestampType": 0 + }, + "Status": 0, + "Data": { + "bootReason": 1, + "_type": "chip.clusters.Objects.GeneralDiagnostics.Events.BootReason" + } + }, + { + "Header": { + "EndpointId": 0, + "ClusterId": 54, + "EventId": 2, + "EventNumber": 262146, + "Priority": 1, + "Timestamp": 2216, + "TimestampType": 0 + }, + "Status": 0, + "Data": { + "connectionStatus": 0, + "_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Events.ConnectionStatus" + } + } + ], + "node_id": 4337 +} diff --git a/tests/components/matter/test_adapter.py b/tests/components/matter/test_adapter.py new file mode 100644 index 00000000000..33a439d2b09 --- /dev/null +++ b/tests/components/matter/test_adapter.py @@ -0,0 +1,78 @@ +"""Test the adapter.""" +from __future__ import annotations + +from typing import Any + +import pytest + +from homeassistant.components.matter.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr + +from .common import setup_integration_with_node_fixture + +# TEMP: Tests need to be fixed +pytestmark = pytest.mark.skip("all tests still WIP") + + +async def test_device_registry_single_node_device( + hass: HomeAssistant, hass_storage: dict[str, Any] +) -> None: + """Test bridge devices are set up correctly with via_device.""" + await setup_integration_with_node_fixture( + hass, hass_storage, "lighting-example-app" + ) + + dev_reg = dr.async_get(hass) + + entry = dev_reg.async_get_device({(DOMAIN, "BE8F70AA40DDAE41")}) + assert entry is not None + + assert entry.name == "My Cool Light" + assert entry.manufacturer == "Nabu Casa" + assert entry.model == "M5STAMP Lighting App" + assert entry.hw_version == "v1.0" + assert entry.sw_version == "55ab764bea" + + +async def test_device_registry_bridge( + hass: HomeAssistant, hass_storage: dict[str, Any] +) -> None: + """Test bridge devices are set up correctly with via_device.""" + await setup_integration_with_node_fixture( + hass, hass_storage, "fake-bridge-two-light" + ) + + dev_reg = dr.async_get(hass) + + # Validate bridge + bridge_entry = dev_reg.async_get_device({(DOMAIN, "mock-hub-id")}) + assert bridge_entry is not None + + assert bridge_entry.name == "My Mock Bridge" + assert bridge_entry.manufacturer == "Mock Vendor" + assert bridge_entry.model == "Mock Bridge" + assert bridge_entry.hw_version == "TEST_VERSION" + assert bridge_entry.sw_version == "123.4.5" + + # Device 1 + device1_entry = dev_reg.async_get_device({(DOMAIN, "mock-id-kitchen-ceiling")}) + assert device1_entry is not None + + assert device1_entry.via_device_id == bridge_entry.id + assert device1_entry.name == "Kitchen Ceiling" + assert device1_entry.manufacturer == "Mock Vendor" + assert device1_entry.model == "Mock Light" + assert device1_entry.hw_version is None + assert device1_entry.sw_version == "67.8.9" + + # Device 2 + device2_entry = dev_reg.async_get_device({(DOMAIN, "mock-id-living-room-ceiling")}) + assert device2_entry is not None + + assert device2_entry.via_device_id == bridge_entry.id + assert device2_entry.name == "Living Room Ceiling" + assert device2_entry.manufacturer == "Mock Vendor" + assert device2_entry.model == "Mock Light" + assert device2_entry.hw_version is None + assert device2_entry.sw_version == "1.49.1" diff --git a/tests/components/matter/test_api.py b/tests/components/matter/test_api.py new file mode 100644 index 00000000000..6fe18d7c3b1 --- /dev/null +++ b/tests/components/matter/test_api.py @@ -0,0 +1,179 @@ +"""Test the api module.""" +from collections.abc import Awaitable, Callable +from unittest.mock import MagicMock, call + +from aiohttp import ClientWebSocketResponse +from matter_server.client.exceptions import FailedCommand + +from homeassistant.components.matter.api import ID, TYPE +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry + + +async def test_commission( + hass: HomeAssistant, + hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]], + matter_client: MagicMock, + integration: MockConfigEntry, +) -> None: + """Test the commission command.""" + ws_client = await hass_ws_client(hass) + + await ws_client.send_json( + { + ID: 1, + TYPE: "matter/commission", + "code": "12345678", + } + ) + msg = await ws_client.receive_json() + + assert msg["success"] + matter_client.commission_with_code.assert_called_once_with("12345678") + + matter_client.commission_with_code.reset_mock() + matter_client.commission_with_code.side_effect = FailedCommand( + "test_id", "test_code", "Failed to commission" + ) + + await ws_client.send_json( + { + ID: 2, + TYPE: "matter/commission", + "code": "12345678", + } + ) + msg = await ws_client.receive_json() + + assert not msg["success"] + assert msg["error"]["code"] == "test_code" + matter_client.commission_with_code.assert_called_once_with("12345678") + + +async def test_commission_on_network( + hass: HomeAssistant, + hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]], + matter_client: MagicMock, + integration: MockConfigEntry, +) -> None: + """Test the commission on network command.""" + ws_client = await hass_ws_client(hass) + + await ws_client.send_json( + { + ID: 1, + TYPE: "matter/commission_on_network", + "pin": 1234, + } + ) + msg = await ws_client.receive_json() + + assert msg["success"] + matter_client.commission_on_network.assert_called_once_with(1234) + + matter_client.commission_on_network.reset_mock() + matter_client.commission_on_network.side_effect = FailedCommand( + "test_id", "test_code", "Failed to commission on network" + ) + + await ws_client.send_json( + { + ID: 2, + TYPE: "matter/commission_on_network", + "pin": 1234, + } + ) + msg = await ws_client.receive_json() + + assert not msg["success"] + assert msg["error"]["code"] == "test_code" + matter_client.commission_on_network.assert_called_once_with(1234) + + +async def test_set_thread_dataset( + hass: HomeAssistant, + hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]], + matter_client: MagicMock, + integration: MockConfigEntry, +) -> None: + """Test the set thread dataset command.""" + ws_client = await hass_ws_client(hass) + + await ws_client.send_json( + { + ID: 1, + TYPE: "matter/set_thread", + "thread_operation_dataset": "test_dataset", + } + ) + msg = await ws_client.receive_json() + + assert msg["success"] + matter_client.set_thread_operational_dataset.assert_called_once_with("test_dataset") + + matter_client.set_thread_operational_dataset.reset_mock() + matter_client.set_thread_operational_dataset.side_effect = FailedCommand( + "test_id", "test_code", "Failed to commission" + ) + + await ws_client.send_json( + { + ID: 2, + TYPE: "matter/set_thread", + "thread_operation_dataset": "test_dataset", + } + ) + msg = await ws_client.receive_json() + + assert not msg["success"] + assert msg["error"]["code"] == "test_code" + matter_client.set_thread_operational_dataset.assert_called_once_with("test_dataset") + + +async def test_set_wifi_credentials( + hass: HomeAssistant, + hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]], + matter_client: MagicMock, + integration: MockConfigEntry, +) -> None: + """Test the set WiFi credentials command.""" + ws_client = await hass_ws_client(hass) + + await ws_client.send_json( + { + ID: 1, + TYPE: "matter/set_wifi_credentials", + "network_name": "test_network", + "password": "test_password", + } + ) + msg = await ws_client.receive_json() + + assert msg["success"] + assert matter_client.set_wifi_credentials.call_count == 1 + assert matter_client.set_wifi_credentials.call_args == call( + ssid="test_network", credentials="test_password" + ) + + matter_client.set_wifi_credentials.reset_mock() + matter_client.set_wifi_credentials.side_effect = FailedCommand( + "test_id", "test_code", "Failed to commission on network" + ) + + await ws_client.send_json( + { + ID: 2, + TYPE: "matter/set_wifi_credentials", + "network_name": "test_network", + "password": "test_password", + } + ) + msg = await ws_client.receive_json() + + assert not msg["success"] + assert msg["error"]["code"] == "test_code" + assert matter_client.set_wifi_credentials.call_count == 1 + assert matter_client.set_wifi_credentials.call_args == call( + ssid="test_network", credentials="test_password" + ) diff --git a/tests/components/matter/test_config_flow.py b/tests/components/matter/test_config_flow.py new file mode 100644 index 00000000000..cad8cd91eb0 --- /dev/null +++ b/tests/components/matter/test_config_flow.py @@ -0,0 +1,979 @@ +"""Test the Matter config flow.""" +from __future__ import annotations + +from collections.abc import Generator +from typing import Any +from unittest.mock import DEFAULT, AsyncMock, MagicMock, call, patch + +from matter_server.client.exceptions import CannotConnect, InvalidServerVersion +import pytest + +from homeassistant import config_entries +from homeassistant.components.hassio import HassioAPIError, HassioServiceInfo +from homeassistant.components.matter.const import ADDON_SLUG, DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType + +from tests.common import MockConfigEntry + +ADDON_DISCOVERY_INFO = { + "addon": "Matter Server", + "host": "host1", + "port": 5581, +} + + +@pytest.fixture(name="setup_entry", autouse=True) +def setup_entry_fixture() -> Generator[AsyncMock, None, None]: + """Mock entry setup.""" + with patch( + "homeassistant.components.matter.async_setup_entry", return_value=True + ) as mock_setup_entry: + yield mock_setup_entry + + +@pytest.fixture(name="client_connect", autouse=True) +def client_connect_fixture() -> Generator[AsyncMock, None, None]: + """Mock server version.""" + with patch( + "homeassistant.components.matter.config_flow.MatterClient.connect" + ) as client_connect: + yield client_connect + + +@pytest.fixture(name="supervisor") +def supervisor_fixture() -> Generator[MagicMock, None, None]: + """Mock Supervisor.""" + with patch( + "homeassistant.components.matter.config_flow.is_hassio", return_value=True + ) as is_hassio: + yield is_hassio + + +@pytest.fixture(name="discovery_info") +def discovery_info_fixture() -> Any: + """Return the discovery info from the supervisor.""" + return DEFAULT + + +@pytest.fixture(name="get_addon_discovery_info", autouse=True) +def get_addon_discovery_info_fixture( + discovery_info: Any, +) -> Generator[AsyncMock, None, None]: + """Mock get add-on discovery info.""" + with patch( + "homeassistant.components.hassio.addon_manager.async_get_addon_discovery_info", + return_value=discovery_info, + ) as get_addon_discovery_info: + yield get_addon_discovery_info + + +@pytest.fixture(name="addon_setup_time", autouse=True) +def addon_setup_time_fixture() -> Generator[int, None, None]: + """Mock add-on setup sleep time.""" + with patch( + "homeassistant.components.matter.config_flow.ADDON_SETUP_TIMEOUT", new=0 + ) as addon_setup_time: + yield addon_setup_time + + +async def test_manual_create_entry( + hass: HomeAssistant, + client_connect: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test user step create entry.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert result["errors"] is None + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "url": "ws://localhost:5580/ws", + }, + ) + await hass.async_block_till_done() + + assert client_connect.call_count == 1 + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "ws://localhost:5580/ws" + assert result["data"] == { + "url": "ws://localhost:5580/ws", + "integration_created_addon": False, + "use_addon": False, + } + assert setup_entry.call_count == 1 + + +@pytest.mark.parametrize( + "error, side_effect", + [ + ("cannot_connect", CannotConnect(Exception("Boom"))), + ("invalid_server_version", InvalidServerVersion("Invalid version")), + ("unknown", Exception("Unknown boom")), + ], +) +async def test_manual_errors( + hass: HomeAssistant, + client_connect: AsyncMock, + error: str, + side_effect: Exception, +) -> None: + """Test user step cannot connect error.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + client_connect.side_effect = side_effect + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "url": "ws://localhost:5580/ws", + }, + ) + + assert client_connect.call_count == 1 + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {"base": error} + + +async def test_manual_already_configured( + hass: HomeAssistant, + client_connect: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test manual step abort if already configured.""" + entry = MockConfigEntry( + domain=DOMAIN, data={"url": "ws://host1:5581/ws"}, title="Matter" + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["errors"] is None + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "url": "ws://localhost:5580/ws", + }, + ) + await hass.async_block_till_done() + + assert client_connect.call_count == 1 + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "reconfiguration_successful" + assert entry.data["url"] == "ws://localhost:5580/ws" + assert entry.data["use_addon"] is False + assert entry.data["integration_created_addon"] is False + assert entry.title == "ws://localhost:5580/ws" + assert setup_entry.call_count == 1 + + +@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) +async def test_supervisor_discovery( + hass: HomeAssistant, + supervisor: MagicMock, + addon_running: AsyncMock, + addon_info: AsyncMock, + client_connect: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test flow started from Supervisor discovery.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HASSIO}, + data=HassioServiceInfo( + config=ADDON_DISCOVERY_INFO, + name="Matter Server", + slug=ADDON_SLUG, + ), + ) + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + await hass.async_block_till_done() + + assert addon_info.call_count == 1 + assert client_connect.call_count == 0 + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "ws://host1:5581/ws" + assert result["data"] == { + "url": "ws://host1:5581/ws", + "use_addon": True, + "integration_created_addon": False, + } + assert setup_entry.call_count == 1 + + +@pytest.mark.parametrize( + "discovery_info, error", + [({"config": ADDON_DISCOVERY_INFO}, HassioAPIError())], +) +async def test_supervisor_discovery_addon_info_failed( + hass: HomeAssistant, + supervisor: MagicMock, + addon_running: AsyncMock, + addon_info: AsyncMock, + error: Exception, +) -> None: + """Test Supervisor discovery and addon info failed.""" + addon_info.side_effect = error + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HASSIO}, + data=HassioServiceInfo( + config=ADDON_DISCOVERY_INFO, + name="Matter Server", + slug=ADDON_SLUG, + ), + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "hassio_confirm" + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + + assert addon_info.call_count == 1 + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "addon_info_failed" + + +@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) +async def test_clean_supervisor_discovery_on_user_create( + hass: HomeAssistant, + supervisor: MagicMock, + addon_running: AsyncMock, + addon_info: AsyncMock, + client_connect: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test discovery flow is cleaned up when a user flow is finished.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HASSIO}, + data=HassioServiceInfo( + config=ADDON_DISCOVERY_INFO, + name="Matter Server", + slug=ADDON_SLUG, + ), + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "hassio_confirm" + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "on_supervisor" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"use_addon": False} + ) + + assert addon_info.call_count == 0 + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "manual" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "url": "ws://localhost:5580/ws", + }, + ) + await hass.async_block_till_done() + + assert len(hass.config_entries.flow.async_progress()) == 0 + assert client_connect.call_count == 1 + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "ws://localhost:5580/ws" + assert result["data"] == { + "url": "ws://localhost:5580/ws", + "use_addon": False, + "integration_created_addon": False, + } + assert setup_entry.call_count == 1 + + +async def test_abort_supervisor_discovery_with_existing_entry( + hass: HomeAssistant, + supervisor: MagicMock, + addon_running: AsyncMock, + addon_info: AsyncMock, +) -> None: + """Test discovery flow is aborted if an entry already exists.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={"url": "ws://localhost:5580/ws"}, + title="ws://localhost:5580/ws", + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HASSIO}, + data=HassioServiceInfo( + config=ADDON_DISCOVERY_INFO, + name="Matter Server", + slug=ADDON_SLUG, + ), + ) + + assert addon_info.call_count == 0 + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_configured" + + +async def test_abort_supervisor_discovery_with_existing_flow( + hass: HomeAssistant, + supervisor: MagicMock, + addon_installed: AsyncMock, + addon_info: AsyncMock, +) -> None: + """Test hassio discovery flow is aborted when another flow is in progress.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "on_supervisor" + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HASSIO}, + data=HassioServiceInfo( + config=ADDON_DISCOVERY_INFO, + name="Matter Server", + slug=ADDON_SLUG, + ), + ) + + assert addon_info.call_count == 0 + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "already_in_progress" + + +async def test_abort_supervisor_discovery_for_other_addon( + hass: HomeAssistant, + supervisor: MagicMock, + addon_installed: AsyncMock, + addon_info: AsyncMock, +) -> None: + """Test hassio discovery flow is aborted for a non official add-on discovery.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HASSIO}, + data=HassioServiceInfo( + config={ + "addon": "Other Matter Server", + "host": "host1", + "port": 3001, + }, + name="Other Matter Server", + slug="other_addon", + ), + ) + + assert addon_info.call_count == 0 + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "not_matter_addon" + + +async def test_supervisor_discovery_addon_not_running( + hass: HomeAssistant, + supervisor: MagicMock, + addon_installed: AsyncMock, + addon_info: AsyncMock, + start_addon: AsyncMock, + client_connect: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test discovery with add-on already installed but not running.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HASSIO}, + data=HassioServiceInfo( + config=ADDON_DISCOVERY_INFO, + name="Matter Server", + slug=ADDON_SLUG, + ), + ) + + assert addon_info.call_count == 0 + assert result["step_id"] == "hassio_confirm" + assert result["type"] == FlowResultType.FORM + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + + assert addon_info.call_count == 1 + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + await hass.async_block_till_done() + + assert start_addon.call_args == call(hass, "core_matter_server") + assert client_connect.call_count == 1 + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "ws://host1:5581/ws" + assert result["data"] == { + "url": "ws://host1:5581/ws", + "use_addon": True, + "integration_created_addon": False, + } + assert setup_entry.call_count == 1 + + +async def test_supervisor_discovery_addon_not_installed( + hass: HomeAssistant, + supervisor: MagicMock, + addon_not_installed: AsyncMock, + install_addon: AsyncMock, + addon_info: AsyncMock, + addon_store_info: AsyncMock, + start_addon: AsyncMock, + client_connect: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test discovery with add-on not installed.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HASSIO}, + data=HassioServiceInfo( + config=ADDON_DISCOVERY_INFO, + name="Matter Server", + slug=ADDON_SLUG, + ), + ) + + assert addon_info.call_count == 0 + assert addon_store_info.call_count == 0 + assert result["step_id"] == "hassio_confirm" + assert result["type"] == FlowResultType.FORM + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + + assert addon_info.call_count == 0 + assert addon_store_info.call_count == 1 + assert result["step_id"] == "install_addon" + assert result["type"] == FlowResultType.SHOW_PROGRESS + + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert install_addon.call_args == call(hass, "core_matter_server") + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + await hass.async_block_till_done() + + assert start_addon.call_args == call(hass, "core_matter_server") + assert client_connect.call_count == 1 + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "ws://host1:5581/ws" + assert result["data"] == { + "url": "ws://host1:5581/ws", + "use_addon": True, + "integration_created_addon": True, + } + assert setup_entry.call_count == 1 + + +async def test_not_addon( + hass: HomeAssistant, + supervisor: MagicMock, + client_connect: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test opting out of add-on on Supervisor.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "on_supervisor" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"use_addon": False} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "manual" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "url": "ws://localhost:5581/ws", + }, + ) + await hass.async_block_till_done() + + assert client_connect.call_count == 1 + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "ws://localhost:5581/ws" + assert result["data"] == { + "url": "ws://localhost:5581/ws", + "use_addon": False, + "integration_created_addon": False, + } + assert setup_entry.call_count == 1 + + +@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) +async def test_addon_running( + hass: HomeAssistant, + supervisor: MagicMock, + addon_running: AsyncMock, + addon_info: AsyncMock, + client_connect: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test add-on already running on Supervisor.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "on_supervisor" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"use_addon": True} + ) + await hass.async_block_till_done() + + assert addon_info.call_count == 1 + assert client_connect.call_count == 1 + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "ws://host1:5581/ws" + assert result["data"] == { + "url": "ws://host1:5581/ws", + "use_addon": True, + "integration_created_addon": False, + } + assert setup_entry.call_count == 1 + + +@pytest.mark.parametrize( + "discovery_info, discovery_info_error, client_connect_error, addon_info_error, " + "abort_reason, discovery_info_called, client_connect_called", + [ + ( + {"config": ADDON_DISCOVERY_INFO}, + HassioAPIError(), + None, + None, + "addon_get_discovery_info_failed", + True, + False, + ), + ( + {"config": ADDON_DISCOVERY_INFO}, + None, + CannotConnect(Exception("Boom")), + None, + "cannot_connect", + True, + True, + ), + ( + None, + None, + None, + None, + "addon_get_discovery_info_failed", + True, + False, + ), + ( + {"config": ADDON_DISCOVERY_INFO}, + None, + None, + HassioAPIError(), + "addon_info_failed", + False, + False, + ), + ], +) +async def test_addon_running_failures( + hass: HomeAssistant, + supervisor: MagicMock, + addon_running: AsyncMock, + addon_info: AsyncMock, + get_addon_discovery_info: AsyncMock, + client_connect: AsyncMock, + discovery_info_error: Exception | None, + client_connect_error: Exception | None, + addon_info_error: Exception | None, + abort_reason: str, + discovery_info_called: bool, + client_connect_called: bool, +) -> None: + """Test all failures when add-on is running.""" + get_addon_discovery_info.side_effect = discovery_info_error + client_connect.side_effect = client_connect_error + addon_info.side_effect = addon_info_error + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "on_supervisor" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"use_addon": True} + ) + + assert addon_info.call_count == 1 + assert get_addon_discovery_info.called is discovery_info_called + assert client_connect.called is client_connect_called + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == abort_reason + + +@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) +async def test_addon_running_already_configured( + hass: HomeAssistant, + supervisor: MagicMock, + addon_running: AsyncMock, + addon_info: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test that only one instance is allowed when add-on is running.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={ + "url": "ws://localhost:5580/ws", + }, + title="ws://localhost:5580/ws", + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "on_supervisor" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"use_addon": True} + ) + await hass.async_block_till_done() + + assert addon_info.call_count == 1 + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "reconfiguration_successful" + assert entry.data["url"] == "ws://host1:5581/ws" + assert entry.title == "ws://host1:5581/ws" + assert setup_entry.call_count == 1 + + +@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) +async def test_addon_installed( + hass: HomeAssistant, + supervisor: MagicMock, + addon_installed: AsyncMock, + addon_info: AsyncMock, + start_addon: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test add-on already installed but not running on Supervisor.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "on_supervisor" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"use_addon": True} + ) + + assert addon_info.call_count == 1 + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + await hass.async_block_till_done() + + assert start_addon.call_args == call(hass, "core_matter_server") + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "ws://host1:5581/ws" + assert result["data"] == { + "url": "ws://host1:5581/ws", + "use_addon": True, + "integration_created_addon": False, + } + assert setup_entry.call_count == 1 + + +@pytest.mark.parametrize( + "discovery_info, start_addon_error, client_connect_error, " + "discovery_info_called, client_connect_called", + [ + ( + {"config": ADDON_DISCOVERY_INFO}, + HassioAPIError(), + None, + False, + False, + ), + ( + {"config": ADDON_DISCOVERY_INFO}, + None, + CannotConnect(Exception("Boom")), + True, + True, + ), + ( + None, + None, + None, + True, + False, + ), + ], +) +async def test_addon_installed_failures( + hass: HomeAssistant, + supervisor: MagicMock, + addon_installed: AsyncMock, + addon_info: AsyncMock, + start_addon: AsyncMock, + get_addon_discovery_info: AsyncMock, + client_connect: AsyncMock, + start_addon_error: Exception | None, + client_connect_error: Exception | None, + discovery_info_called: bool, + client_connect_called: bool, +) -> None: + """Test add-on start failure when add-on is installed.""" + start_addon.side_effect = start_addon_error + client_connect.side_effect = client_connect_error + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "on_supervisor" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"use_addon": True} + ) + + assert addon_info.call_count == 1 + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert start_addon.call_args == call(hass, "core_matter_server") + assert get_addon_discovery_info.called is discovery_info_called + assert client_connect.called is client_connect_called + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "addon_start_failed" + + +@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) +async def test_addon_installed_already_configured( + hass: HomeAssistant, + supervisor: MagicMock, + addon_installed: AsyncMock, + addon_info: AsyncMock, + start_addon: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test that only one instance is allowed when add-on is installed.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={ + "url": "ws://localhost:5580/ws", + }, + title="ws://localhost:5580/ws", + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "on_supervisor" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"use_addon": True} + ) + + assert addon_info.call_count == 1 + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + await hass.async_block_till_done() + + assert start_addon.call_args == call(hass, "core_matter_server") + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "reconfiguration_successful" + assert entry.data["url"] == "ws://host1:5581/ws" + assert entry.title == "ws://host1:5581/ws" + assert setup_entry.call_count == 1 + + +@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) +async def test_addon_not_installed( + hass: HomeAssistant, + supervisor: MagicMock, + addon_not_installed: AsyncMock, + install_addon: AsyncMock, + addon_info: AsyncMock, + addon_store_info: AsyncMock, + start_addon: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test add-on not installed.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "on_supervisor" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"use_addon": True} + ) + + assert addon_info.call_count == 0 + assert addon_store_info.call_count == 1 + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + + # Make sure the flow continues when the progress task is done. + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert install_addon.call_args == call(hass, "core_matter_server") + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + await hass.async_block_till_done() + + assert start_addon.call_args == call(hass, "core_matter_server") + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "ws://host1:5581/ws" + assert result["data"] == { + "url": "ws://host1:5581/ws", + "use_addon": True, + "integration_created_addon": True, + } + assert setup_entry.call_count == 1 + + +async def test_addon_not_installed_failures( + hass: HomeAssistant, + supervisor: MagicMock, + addon_not_installed: AsyncMock, + addon_info: AsyncMock, + install_addon: AsyncMock, +) -> None: + """Test add-on install failure.""" + install_addon.side_effect = HassioAPIError() + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "on_supervisor" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"use_addon": True} + ) + + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + + # Make sure the flow continues when the progress task is done. + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert install_addon.call_args == call(hass, "core_matter_server") + assert addon_info.call_count == 0 + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "addon_install_failed" + + +@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}]) +async def test_addon_not_installed_already_configured( + hass: HomeAssistant, + supervisor: MagicMock, + addon_not_installed: AsyncMock, + addon_info: AsyncMock, + addon_store_info: AsyncMock, + install_addon: AsyncMock, + start_addon: AsyncMock, + client_connect: AsyncMock, + setup_entry: AsyncMock, +) -> None: + """Test that only one instance is allowed when add-on is not installed.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={ + "url": "ws://localhost:5580/ws", + }, + title="ws://localhost:5580/ws", + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "on_supervisor" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"use_addon": True} + ) + + assert addon_info.call_count == 0 + assert addon_store_info.call_count == 1 + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "install_addon" + + # Make sure the flow continues when the progress task is done. + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert install_addon.call_args == call(hass, "core_matter_server") + assert result["type"] == FlowResultType.SHOW_PROGRESS + assert result["step_id"] == "start_addon" + + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + await hass.async_block_till_done() + + assert start_addon.call_args == call(hass, "core_matter_server") + assert client_connect.call_count == 1 + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "reconfiguration_successful" + assert entry.data["url"] == "ws://host1:5581/ws" + assert entry.title == "ws://host1:5581/ws" + assert setup_entry.call_count == 1 diff --git a/tests/components/matter/test_init.py b/tests/components/matter/test_init.py new file mode 100644 index 00000000000..f34e428ecc0 --- /dev/null +++ b/tests/components/matter/test_init.py @@ -0,0 +1,398 @@ +"""Test the Matter integration init.""" +from __future__ import annotations + +import asyncio +from unittest.mock import AsyncMock, MagicMock, call + +from matter_server.client.exceptions import InvalidServerVersion +import pytest + +from homeassistant.components.hassio import HassioAPIError +from homeassistant.components.matter.const import DOMAIN +from homeassistant.config_entries import ConfigEntryDisabler, ConfigEntryState +from homeassistant.core import HomeAssistant +from homeassistant.helpers import issue_registry as ir + +from tests.common import MockConfigEntry + + +async def test_raise_addon_task_in_progress( + hass: HomeAssistant, + addon_not_installed: AsyncMock, + install_addon: AsyncMock, + start_addon: AsyncMock, +) -> None: + """Test raise ConfigEntryNotReady if an add-on task is in progress.""" + install_event = asyncio.Event() + + install_addon_original_side_effect = install_addon.side_effect + + async def install_addon_side_effect(hass: HomeAssistant, slug: str) -> None: + """Mock install add-on.""" + await install_event.wait() + await install_addon_original_side_effect(hass, slug) + + install_addon.side_effect = install_addon_side_effect + + entry = MockConfigEntry( + domain=DOMAIN, + title="Matter", + data={ + "url": "ws://host1:5581/ws", + "use_addon": True, + }, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await asyncio.sleep(0.05) + + assert entry.state is ConfigEntryState.SETUP_RETRY + assert install_addon.call_count == 1 + assert start_addon.call_count == 0 + + # Check that we only call install add-on once if a task is in progress. + await hass.config_entries.async_reload(entry.entry_id) + await asyncio.sleep(0.05) + + assert entry.state is ConfigEntryState.SETUP_RETRY + assert install_addon.call_count == 1 + assert start_addon.call_count == 0 + + install_event.set() + await hass.async_block_till_done() + + assert install_addon.call_count == 1 + assert start_addon.call_count == 1 + + +async def test_start_addon( + hass: HomeAssistant, + addon_installed: AsyncMock, + addon_info: AsyncMock, + install_addon: AsyncMock, + start_addon: AsyncMock, +) -> None: + """Test start the Matter Server add-on during entry setup.""" + entry = MockConfigEntry( + domain=DOMAIN, + title="Matter", + data={ + "url": "ws://host1:5581/ws", + "use_addon": True, + }, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.SETUP_RETRY + assert addon_info.call_count == 1 + assert install_addon.call_count == 0 + assert start_addon.call_count == 1 + assert start_addon.call_args == call(hass, "core_matter_server") + + +async def test_install_addon( + hass: HomeAssistant, + addon_not_installed: AsyncMock, + addon_store_info: AsyncMock, + install_addon: AsyncMock, + start_addon: AsyncMock, +) -> None: + """Test install and start the Matter add-on during entry setup.""" + entry = MockConfigEntry( + domain=DOMAIN, + title="Matter", + data={ + "url": "ws://host1:5581/ws", + "use_addon": True, + }, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.SETUP_RETRY + assert addon_store_info.call_count == 2 + assert install_addon.call_count == 1 + assert install_addon.call_args == call(hass, "core_matter_server") + assert start_addon.call_count == 1 + assert start_addon.call_args == call(hass, "core_matter_server") + + +async def test_addon_info_failure( + hass: HomeAssistant, + addon_installed: AsyncMock, + addon_info: AsyncMock, + install_addon: AsyncMock, + start_addon: AsyncMock, +) -> None: + """Test failure to get add-on info for Matter add-on during entry setup.""" + addon_info.side_effect = HassioAPIError("Boom") + entry = MockConfigEntry( + domain=DOMAIN, + title="Matter", + data={ + "url": "ws://host1:5581/ws", + "use_addon": True, + }, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.SETUP_RETRY + assert addon_info.call_count == 1 + assert install_addon.call_count == 0 + assert start_addon.call_count == 0 + + +@pytest.mark.parametrize( + "addon_version, update_available, update_calls, backup_calls, " + "update_addon_side_effect, create_backup_side_effect", + [ + ("1.0.0", True, 1, 1, None, None), + ("1.0.0", False, 0, 0, None, None), + ("1.0.0", True, 1, 1, HassioAPIError("Boom"), None), + ("1.0.0", True, 0, 1, None, HassioAPIError("Boom")), + ], +) +async def test_update_addon( + hass: HomeAssistant, + addon_installed: AsyncMock, + addon_running: AsyncMock, + addon_info: AsyncMock, + install_addon: AsyncMock, + start_addon: AsyncMock, + create_backup: AsyncMock, + update_addon: AsyncMock, + matter_client: MagicMock, + addon_version: str, + update_available: bool, + update_calls: int, + backup_calls: int, + update_addon_side_effect: Exception | None, + create_backup_side_effect: Exception | None, +): + """Test update the Matter add-on during entry setup.""" + addon_info.return_value["version"] = addon_version + addon_info.return_value["update_available"] = update_available + create_backup.side_effect = create_backup_side_effect + update_addon.side_effect = update_addon_side_effect + matter_client.connect.side_effect = InvalidServerVersion("Invalid version") + entry = MockConfigEntry( + domain=DOMAIN, + title="Matter", + data={ + "url": "ws://host1:5581/ws", + "use_addon": True, + }, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.SETUP_RETRY + assert create_backup.call_count == backup_calls + assert update_addon.call_count == update_calls + + +async def test_issue_registry_invalid_version( + hass: HomeAssistant, + matter_client: MagicMock, +) -> None: + """Test issue registry for invalid version.""" + original_connect_side_effect = matter_client.connect.side_effect + matter_client.connect.side_effect = InvalidServerVersion("Invalid version") + entry = MockConfigEntry( + domain=DOMAIN, + title="Matter", + data={ + "url": "ws://host1:5581/ws", + "use_addon": False, + }, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + issue_reg = ir.async_get(hass) + entry_state = entry.state + assert entry_state is ConfigEntryState.SETUP_RETRY + assert issue_reg.async_get_issue(DOMAIN, "invalid_server_version") + + matter_client.connect.side_effect = original_connect_side_effect + + await hass.config_entries.async_reload(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.LOADED + assert not issue_reg.async_get_issue(DOMAIN, "invalid_server_version") + + +@pytest.mark.parametrize( + "stop_addon_side_effect, entry_state", + [ + (None, ConfigEntryState.NOT_LOADED), + (HassioAPIError("Boom"), ConfigEntryState.LOADED), + ], +) +async def test_stop_addon( + hass, + matter_client: MagicMock, + addon_installed: AsyncMock, + addon_running: AsyncMock, + addon_info: AsyncMock, + stop_addon: AsyncMock, + stop_addon_side_effect: Exception | None, + entry_state: ConfigEntryState, +): + """Test stop the Matter add-on on entry unload if entry is disabled.""" + stop_addon.side_effect = stop_addon_side_effect + entry = MockConfigEntry( + domain=DOMAIN, + title="Matter", + data={ + "url": "ws://host1:5581/ws", + "use_addon": True, + }, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.LOADED + assert addon_info.call_count == 1 + addon_info.reset_mock() + + await hass.config_entries.async_set_disabled_by( + entry.entry_id, ConfigEntryDisabler.USER + ) + await hass.async_block_till_done() + + assert entry.state == entry_state + assert stop_addon.call_count == 1 + assert stop_addon.call_args == call(hass, "core_matter_server") + + +async def test_remove_entry( + hass: HomeAssistant, + addon_installed: AsyncMock, + stop_addon: AsyncMock, + create_backup: AsyncMock, + uninstall_addon: AsyncMock, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test remove the config entry.""" + # test successful remove without created add-on + entry = MockConfigEntry( + domain=DOMAIN, + title="Matter", + data={"integration_created_addon": False}, + ) + entry.add_to_hass(hass) + assert entry.state is ConfigEntryState.NOT_LOADED + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + + await hass.config_entries.async_remove(entry.entry_id) + + assert entry.state is ConfigEntryState.NOT_LOADED + assert len(hass.config_entries.async_entries(DOMAIN)) == 0 + + # test successful remove with created add-on + entry = MockConfigEntry( + domain=DOMAIN, + title="Matter", + data={"integration_created_addon": True}, + ) + entry.add_to_hass(hass) + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + + await hass.config_entries.async_remove(entry.entry_id) + + assert stop_addon.call_count == 1 + assert stop_addon.call_args == call(hass, "core_matter_server") + assert create_backup.call_count == 1 + assert create_backup.call_args == call( + hass, + {"name": "addon_core_matter_server_1.0.0", "addons": ["core_matter_server"]}, + partial=True, + ) + assert uninstall_addon.call_count == 1 + assert uninstall_addon.call_args == call(hass, "core_matter_server") + assert entry.state is ConfigEntryState.NOT_LOADED + assert len(hass.config_entries.async_entries(DOMAIN)) == 0 + stop_addon.reset_mock() + create_backup.reset_mock() + uninstall_addon.reset_mock() + + # test add-on stop failure + entry.add_to_hass(hass) + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + stop_addon.side_effect = HassioAPIError() + + await hass.config_entries.async_remove(entry.entry_id) + + assert stop_addon.call_count == 1 + assert stop_addon.call_args == call(hass, "core_matter_server") + assert create_backup.call_count == 0 + assert uninstall_addon.call_count == 0 + assert entry.state is ConfigEntryState.NOT_LOADED + assert len(hass.config_entries.async_entries(DOMAIN)) == 0 + assert "Failed to stop the Matter Server add-on" in caplog.text + stop_addon.side_effect = None + stop_addon.reset_mock() + create_backup.reset_mock() + uninstall_addon.reset_mock() + + # test create backup failure + entry.add_to_hass(hass) + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + create_backup.side_effect = HassioAPIError() + + await hass.config_entries.async_remove(entry.entry_id) + + assert stop_addon.call_count == 1 + assert stop_addon.call_args == call(hass, "core_matter_server") + assert create_backup.call_count == 1 + assert create_backup.call_args == call( + hass, + {"name": "addon_core_matter_server_1.0.0", "addons": ["core_matter_server"]}, + partial=True, + ) + assert uninstall_addon.call_count == 0 + assert entry.state is ConfigEntryState.NOT_LOADED + assert len(hass.config_entries.async_entries(DOMAIN)) == 0 + assert "Failed to create a backup of the Matter Server add-on" in caplog.text + create_backup.side_effect = None + stop_addon.reset_mock() + create_backup.reset_mock() + uninstall_addon.reset_mock() + + # test add-on uninstall failure + entry.add_to_hass(hass) + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + uninstall_addon.side_effect = HassioAPIError() + + await hass.config_entries.async_remove(entry.entry_id) + + assert stop_addon.call_count == 1 + assert stop_addon.call_args == call(hass, "core_matter_server") + assert create_backup.call_count == 1 + assert create_backup.call_args == call( + hass, + {"name": "addon_core_matter_server_1.0.0", "addons": ["core_matter_server"]}, + partial=True, + ) + assert uninstall_addon.call_count == 1 + assert uninstall_addon.call_args == call(hass, "core_matter_server") + assert entry.state is ConfigEntryState.NOT_LOADED + assert len(hass.config_entries.async_entries(DOMAIN)) == 0 + assert "Failed to uninstall the Matter Server add-on" in caplog.text diff --git a/tests/components/matter/test_light.py b/tests/components/matter/test_light.py new file mode 100644 index 00000000000..eeebbac6414 --- /dev/null +++ b/tests/components/matter/test_light.py @@ -0,0 +1,82 @@ +"""Test Matter lights.""" +from typing import Any + +from chip.clusters import Objects as clusters +from matter_server.common.models.node import MatterNode +import pytest + +from homeassistant.core import HomeAssistant + +from .common import setup_integration_with_node_fixture + +# TEMP: Tests need to be fixed +pytestmark = pytest.mark.skip("all tests still WIP") + + +@pytest.fixture(name="light_node") +async def light_node_fixture( + hass: HomeAssistant, hass_storage: dict[str, Any] +) -> MatterNode: + """Fixture for a light node.""" + return await setup_integration_with_node_fixture( + hass, hass_storage, "lighting-example-app" + ) + + +async def test_turn_on(hass: HomeAssistant, light_node: MatterNode) -> None: + """Test turning on a light.""" + light_node.matter.client.mock_command(clusters.OnOff.Commands.On, None) + + await hass.services.async_call( + "light", + "turn_on", + { + "entity_id": "light.my_cool_light", + }, + blocking=True, + ) + + assert len(light_node.matter.client.mock_sent_commands) == 1 + args = light_node.matter.client.mock_sent_commands[0] + assert args["nodeid"] == light_node.node_id + assert args["endpoint"] == 1 + + light_node.matter.client.mock_command( + clusters.LevelControl.Commands.MoveToLevelWithOnOff, None + ) + + await hass.services.async_call( + "light", + "turn_on", + { + "entity_id": "light.my_cool_light", + "brightness": 128, + }, + blocking=True, + ) + + assert len(light_node.matter.client.mock_sent_commands) == 2 + args = light_node.matter.client.mock_sent_commands[1] + assert args["nodeid"] == light_node.node_id + assert args["endpoint"] == 1 + assert args["payload"].level == 127 + assert args["payload"].transitionTime == 0 + + +async def test_turn_off(hass: HomeAssistant, light_node: MatterNode) -> None: + """Test turning off a light.""" + light_node.matter.client.mock_command(clusters.OnOff.Commands.Off, None) + + await hass.services.async_call( + "light", + "turn_off", + { + "entity_id": "light.my_cool_light", + }, + blocking=True, + ) + + assert len(light_node.matter.client.mock_sent_commands) == 1 + args = light_node.matter.client.mock_sent_commands[0] + assert args["nodeid"] == light_node.node_id + assert args["endpoint"] == 1 From 5192b20a7c6dd3a617a15e74633a788d8ac745fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Thu, 1 Dec 2022 19:36:46 +0100 Subject: [PATCH 0943/1033] Tibber, fix date parsing (#83067) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Hjelseth Høyer Signed-off-by: Daniel Hjelseth Høyer --- homeassistant/components/tibber/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/tibber/sensor.py b/homeassistant/components/tibber/sensor.py index 066e1f921c5..31106990a03 100644 --- a/homeassistant/components/tibber/sensor.py +++ b/homeassistant/components/tibber/sensor.py @@ -613,7 +613,7 @@ class TibberDataCoordinator(DataUpdateCoordinator): else home.hourly_consumption_data ) - from_time = hourly_data[0]["from"] + from_time = dt_util.parse_datetime(hourly_data[0]["from"]) if from_time is None: continue start = from_time - timedelta(hours=1) From 90dfa9d2af97ff2e8d3361de7991e9145118bd99 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Thu, 1 Dec 2022 16:37:44 -0500 Subject: [PATCH 0944/1033] Assorted fixes for UniFi Protect (#83032) --- .../components/unifiprotect/binary_sensor.py | 18 +++++++----------- homeassistant/components/unifiprotect/const.py | 2 -- .../components/unifiprotect/entity.py | 1 - .../components/unifiprotect/sensor.py | 10 ++++++++-- .../unifiprotect/strings.sensor.json | 7 +++++++ .../unifiprotect/translations/sensor.en.json | 7 +++++++ tests/components/unifiprotect/test_sensor.py | 10 +++++----- 7 files changed, 34 insertions(+), 21 deletions(-) create mode 100644 homeassistant/components/unifiprotect/strings.sensor.json create mode 100644 homeassistant/components/unifiprotect/translations/sensor.en.json diff --git a/homeassistant/components/unifiprotect/binary_sensor.py b/homeassistant/components/unifiprotect/binary_sensor.py index d38fe8582ab..bf6e89c3caf 100644 --- a/homeassistant/components/unifiprotect/binary_sensor.py +++ b/homeassistant/components/unifiprotect/binary_sensor.py @@ -30,7 +30,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DEVICE_CLASS_DETECTION, DISPATCH_ADOPT, DOMAIN +from .const import DISPATCH_ADOPT, DOMAIN from .data import ProtectData from .entity import ( EventEntityMixin, @@ -351,7 +351,6 @@ MOTION_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = ( key="smart_obj_any", name="Object Detected", icon="mdi:eye", - device_class=DEVICE_CLASS_DETECTION, ufp_value="is_smart_detected", ufp_required_field="feature_flags.has_smart_detect", ufp_event_obj="last_smart_detect_event", @@ -360,7 +359,6 @@ MOTION_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = ( key="smart_obj_person", name="Person Detected", icon="mdi:walk", - device_class=DEVICE_CLASS_DETECTION, ufp_value="is_smart_detected", ufp_required_field="can_detect_person", ufp_enabled="is_person_detection_on", @@ -371,7 +369,6 @@ MOTION_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = ( key="smart_obj_vehicle", name="Vehicle Detected", icon="mdi:car", - device_class=DEVICE_CLASS_DETECTION, ufp_value="is_smart_detected", ufp_required_field="can_detect_vehicle", ufp_enabled="is_vehicle_detection_on", @@ -381,7 +378,6 @@ MOTION_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = ( ProtectBinaryEventEntityDescription( key="smart_obj_face", name="Face Detected", - device_class=DEVICE_CLASS_DETECTION, icon="mdi:mdi-face", ufp_value="is_smart_detected", ufp_required_field="can_detect_face", @@ -392,7 +388,6 @@ MOTION_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = ( ProtectBinaryEventEntityDescription( key="smart_obj_package", name="Package Detected", - device_class=DEVICE_CLASS_DETECTION, icon="mdi:package-variant-closed", ufp_value="is_smart_detected", ufp_required_field="can_detect_package", @@ -402,9 +397,8 @@ MOTION_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = ( ), ProtectBinaryEventEntityDescription( key="smart_audio_any", - name="Audio Detected", + name="Audio Object Detected", icon="mdi:eye", - device_class=DEVICE_CLASS_DETECTION, ufp_value="is_smart_detected", ufp_required_field="feature_flags.has_smart_detect", ufp_event_obj="last_smart_audio_detect_event", @@ -412,7 +406,6 @@ MOTION_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = ( ProtectBinaryEventEntityDescription( key="smart_audio_smoke", name="Smoke Alarm Detected", - device_class=DEVICE_CLASS_DETECTION, icon="mdi:fire", ufp_value="is_smart_detected", ufp_required_field="can_detect_smoke", @@ -423,7 +416,6 @@ MOTION_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = ( ProtectBinaryEventEntityDescription( key="smart_audio_cmonx", name="CO Alarm Detected", - device_class=DEVICE_CLASS_DETECTION, icon="mdi:fire", ufp_value="is_smart_detected", ufp_required_field="can_detect_smoke", @@ -629,4 +621,8 @@ class ProtectEventBinarySensor(EventEntityMixin, BinarySensorEntity): @callback def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None: super()._async_update_device_from_protect(device) - self._attr_is_on = self.entity_description.get_ufp_value(self.device) + is_on = self.entity_description.get_is_on(device) + self._attr_is_on: bool | None = is_on + if not is_on: + self._event = None + self._attr_extra_state_attributes = {} diff --git a/homeassistant/components/unifiprotect/const.py b/homeassistant/components/unifiprotect/const.py index cbf15c076c5..df4d8f77d99 100644 --- a/homeassistant/components/unifiprotect/const.py +++ b/homeassistant/components/unifiprotect/const.py @@ -68,5 +68,3 @@ PLATFORMS = [ DISPATCH_ADD = "add_device" DISPATCH_ADOPT = "adopt_device" DISPATCH_CHANNELS = "new_camera_channels" - -DEVICE_CLASS_DETECTION = "unifiprotect__detection" diff --git a/homeassistant/components/unifiprotect/entity.py b/homeassistant/components/unifiprotect/entity.py index a733f6ea1af..134b55c4b0d 100644 --- a/homeassistant/components/unifiprotect/entity.py +++ b/homeassistant/components/unifiprotect/entity.py @@ -325,7 +325,6 @@ class EventEntityMixin(ProtectDeviceEntity): @callback def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None: super()._async_update_device_from_protect(device) - self._attr_is_on: bool | None = self.entity_description.get_is_on(device) self._event = self.entity_description.get_event_obj(device) attrs = self.extra_state_attributes or {} diff --git a/homeassistant/components/unifiprotect/sensor.py b/homeassistant/components/unifiprotect/sensor.py index 621fad2a1a5..d608907fa83 100644 --- a/homeassistant/components/unifiprotect/sensor.py +++ b/homeassistant/components/unifiprotect/sensor.py @@ -41,7 +41,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DEVICE_CLASS_DETECTION, DISPATCH_ADOPT, DOMAIN +from .const import DISPATCH_ADOPT, DOMAIN from .data import ProtectData from .entity import ( EventEntityMixin, @@ -54,6 +54,8 @@ from .utils import async_dispatch_id as _ufpd, async_get_light_motion_current _LOGGER = logging.getLogger(__name__) OBJECT_TYPE_NONE = "none" +DEVICE_CLASS_DETECTION = "unifiprotect__detection" +DEVICE_CLASS_LICENSE_PLATE = "unifiprotect__license_plate" @dataclass @@ -532,8 +534,9 @@ MOTION_SENSORS: tuple[ProtectSensorEventEntityDescription, ...] = ( key="smart_obj_licenseplate", name="License Plate Detected", icon="mdi:car", - device_class=DEVICE_CLASS_DETECTION, + device_class=DEVICE_CLASS_LICENSE_PLATE, ufp_value="is_smart_detected", + ufp_required_field="can_detect_license_plate", ufp_event_obj="last_smart_detect_event", ufp_smart_type=SmartDetectObjectType.LICENSE_PLATE, ), @@ -685,6 +688,9 @@ def _async_motion_entities( continue for event_desc in MOTION_SENSORS: + if not event_desc.has_required(device): + continue + entities.append(ProtectEventSensor(data, device, event_desc)) _LOGGER.debug( "Adding sensor entity %s for %s", diff --git a/homeassistant/components/unifiprotect/strings.sensor.json b/homeassistant/components/unifiprotect/strings.sensor.json new file mode 100644 index 00000000000..07b1955ba81 --- /dev/null +++ b/homeassistant/components/unifiprotect/strings.sensor.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "Clear" + } + } +} diff --git a/homeassistant/components/unifiprotect/translations/sensor.en.json b/homeassistant/components/unifiprotect/translations/sensor.en.json new file mode 100644 index 00000000000..25d99e9ce4a --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.en.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "Clear" + } + } +} \ No newline at end of file diff --git a/tests/components/unifiprotect/test_sensor.py b/tests/components/unifiprotect/test_sensor.py index 0ec50398e7a..e67195df5e9 100644 --- a/tests/components/unifiprotect/test_sensor.py +++ b/tests/components/unifiprotect/test_sensor.py @@ -62,11 +62,11 @@ async def test_sensor_camera_remove( ufp.api.bootstrap.nvr.system_info.ustorage = None await init_entry(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.SENSOR, 26, 13) + assert_entity_counts(hass, Platform.SENSOR, 25, 12) await remove_entities(hass, ufp, [doorbell, unadopted_camera]) assert_entity_counts(hass, Platform.SENSOR, 12, 9) await adopt_devices(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.SENSOR, 26, 13) + assert_entity_counts(hass, Platform.SENSOR, 25, 12) async def test_sensor_sensor_remove( @@ -318,7 +318,7 @@ async def test_sensor_setup_camera( """Test sensor entity setup for camera devices.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SENSOR, 26, 13) + assert_entity_counts(hass, Platform.SENSOR, 25, 12) entity_registry = er.async_get(hass) @@ -424,7 +424,7 @@ async def test_sensor_setup_camera_with_last_trip_time( """Test sensor entity setup for camera devices with last trip time.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SENSOR, 26, 26) + assert_entity_counts(hass, Platform.SENSOR, 25, 25) entity_registry = er.async_get(hass) @@ -452,7 +452,7 @@ async def test_sensor_update_motion( """Test sensor motion entity.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SENSOR, 26, 13) + assert_entity_counts(hass, Platform.SENSOR, 25, 12) _, entity_id = ids_from_device_description( Platform.SENSOR, doorbell, MOTION_SENSORS[0] From 9cf2bd079443f0bc5a85afd83b30bbacec75b4c5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 1 Dec 2022 11:37:22 -1000 Subject: [PATCH 0945/1033] Defer xiaomi ble polling until after startup (#83074) fixes https://github.com/home-assistant/core/issues/77178 --- homeassistant/components/xiaomi_ble/__init__.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/xiaomi_ble/__init__.py b/homeassistant/components/xiaomi_ble/__init__.py index f899600a8d1..201e4f14582 100644 --- a/homeassistant/components/xiaomi_ble/__init__.py +++ b/homeassistant/components/xiaomi_ble/__init__.py @@ -17,7 +17,7 @@ from homeassistant.components.bluetooth.active_update_coordinator import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform -from homeassistant.core import HomeAssistant +from homeassistant.core import CoreState, HomeAssistant from .const import DOMAIN @@ -60,7 +60,17 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: def _needs_poll( service_info: BluetoothServiceInfoBleak, last_poll: float | None ) -> bool: - return data.poll_needed(service_info, last_poll) + # Only poll if hass is running, we need to poll, + # and we actually have a way to connect to the device + return ( + hass.state == CoreState.running + and data.poll_needed(service_info, last_poll) + and bool( + async_ble_device_from_address( + hass, service_info.device.address, connectable=True + ) + ) + ) async def _async_poll(service_info: BluetoothServiceInfoBleak): # BluetoothServiceInfoBleak is defined in HA, otherwise would just pass it From 68f5a3a82cf6ccb7c46e6c869a5e333115626baa Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 1 Dec 2022 16:38:12 -0500 Subject: [PATCH 0946/1033] Bumped version to 2022.12.0b1 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 32c79bbc889..16dfdc644d3 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -8,7 +8,7 @@ from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "0b0" +PATCH_VERSION: Final = "0b1" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/pyproject.toml b/pyproject.toml index 729892e41cc..260d7553cb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.12.0b0" +version = "2022.12.0b1" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From e6b0d93c1d52a96ea99783237ae1edfaf8dbb63a Mon Sep 17 00:00:00 2001 From: Alex Yao <33379584+alexyao2015@users.noreply.github.com> Date: Fri, 2 Dec 2022 19:51:16 -0600 Subject: [PATCH 0947/1033] Fix html5 Firefox Notifications (#82556) Co-authored-by: Paulus Schoutsen fixes undefined --- homeassistant/components/html5/notify.py | 25 ++++++- tests/components/html5/test_notify.py | 89 ++++++++++-------------- 2 files changed, 59 insertions(+), 55 deletions(-) diff --git a/homeassistant/components/html5/notify.py b/homeassistant/components/html5/notify.py index a6ca769250f..30465c9bd81 100644 --- a/homeassistant/components/html5/notify.py +++ b/homeassistant/components/html5/notify.py @@ -99,6 +99,7 @@ SCHEMA_WS_APPKEY = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( # The number of days after the moment a notification is sent that a JWT # is valid. JWT_VALID_DAYS = 7 +VAPID_CLAIM_VALID_HOURS = 12 KEYS_SCHEMA = vol.All( dict, @@ -514,7 +515,10 @@ class HTML5NotificationService(BaseNotificationService): webpusher = WebPusher(info[ATTR_SUBSCRIPTION]) if self._vapid_prv and self._vapid_email: vapid_headers = create_vapid_headers( - self._vapid_email, info[ATTR_SUBSCRIPTION], self._vapid_prv + self._vapid_email, + info[ATTR_SUBSCRIPTION], + self._vapid_prv, + timestamp, ) vapid_headers.update({"urgency": priority, "priority": priority}) response = webpusher.send( @@ -540,6 +544,12 @@ class HTML5NotificationService(BaseNotificationService): _LOGGER.error("Error saving registration") else: _LOGGER.info("Configuration saved") + elif response.status_code > 399: + _LOGGER.error( + "There was an issue sending the notification %s: %s", + response.status, + response.text, + ) def add_jwt(timestamp, target, tag, jwt_secret): @@ -556,14 +566,23 @@ def add_jwt(timestamp, target, tag, jwt_secret): return jwt.encode(jwt_claims, jwt_secret) -def create_vapid_headers(vapid_email, subscription_info, vapid_private_key): +def create_vapid_headers(vapid_email, subscription_info, vapid_private_key, timestamp): """Create encrypted headers to send to WebPusher.""" - if vapid_email and vapid_private_key and ATTR_ENDPOINT in subscription_info: + if ( + vapid_email + and vapid_private_key + and ATTR_ENDPOINT in subscription_info + and timestamp + ): + vapid_exp = datetime.fromtimestamp(timestamp) + timedelta( + hours=VAPID_CLAIM_VALID_HOURS + ) url = urlparse(subscription_info.get(ATTR_ENDPOINT)) vapid_claims = { "sub": f"mailto:{vapid_email}", "aud": f"{url.scheme}://{url.netloc}", + "exp": int(vapid_exp.timestamp()), } vapid = Vapid.from_string(private_key=vapid_private_key) return vapid.sign(vapid_claims) diff --git a/tests/components/html5/test_notify.py b/tests/components/html5/test_notify.py index 1614555c493..b77986441ed 100644 --- a/tests/components/html5/test_notify.py +++ b/tests/components/html5/test_notify.py @@ -93,6 +93,7 @@ class TestHtml5Notify: def test_dismissing_message(self, mock_wp): """Test dismissing message.""" hass = MagicMock() + mock_wp().send().status_code = 201 data = {"device": SUBSCRIPTION_1} @@ -104,15 +105,13 @@ class TestHtml5Notify: service.dismiss(target=["device", "non_existing"], data={"tag": "test"}) - assert len(mock_wp.mock_calls) == 3 + assert len(mock_wp.mock_calls) == 4 # WebPusher constructor - assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_1["subscription"] - # Third mock_call checks the status_code of the response. - assert mock_wp.mock_calls[2][0] == "().send().status_code.__eq__" + assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_1["subscription"] # Call to send - payload = json.loads(mock_wp.mock_calls[1][1][0]) + payload = json.loads(mock_wp.mock_calls[3][1][0]) assert payload["dismiss"] is True assert payload["tag"] == "test" @@ -121,6 +120,7 @@ class TestHtml5Notify: def test_sending_message(self, mock_wp): """Test sending message.""" hass = MagicMock() + mock_wp().send().status_code = 201 data = {"device": SUBSCRIPTION_1} @@ -134,15 +134,13 @@ class TestHtml5Notify: "Hello", target=["device", "non_existing"], data={"icon": "beer.png"} ) - assert len(mock_wp.mock_calls) == 3 + assert len(mock_wp.mock_calls) == 4 # WebPusher constructor - assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_1["subscription"] - # Third mock_call checks the status_code of the response. - assert mock_wp.mock_calls[2][0] == "().send().status_code.__eq__" + assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_1["subscription"] # Call to send - payload = json.loads(mock_wp.mock_calls[1][1][0]) + payload = json.loads(mock_wp.mock_calls[3][1][0]) assert payload["body"] == "Hello" assert payload["icon"] == "beer.png" @@ -151,6 +149,7 @@ class TestHtml5Notify: def test_gcm_key_include(self, mock_wp): """Test if the gcm_key is only included for GCM endpoints.""" hass = MagicMock() + mock_wp().send().status_code = 201 data = {"chrome": SUBSCRIPTION_1, "firefox": SUBSCRIPTION_2} @@ -167,21 +166,18 @@ class TestHtml5Notify: assert len(mock_wp.mock_calls) == 6 # WebPusher constructor - assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_1["subscription"] - assert mock_wp.mock_calls[3][1][0] == SUBSCRIPTION_2["subscription"] - - # Third mock_call checks the status_code of the response. - assert mock_wp.mock_calls[2][0] == "().send().status_code.__eq__" - assert mock_wp.mock_calls[5][0] == "().send().status_code.__eq__" + assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_1["subscription"] + assert mock_wp.mock_calls[4][1][0] == SUBSCRIPTION_2["subscription"] # Get the keys passed to the WebPusher's send method - assert mock_wp.mock_calls[1][2]["gcm_key"] is not None - assert mock_wp.mock_calls[4][2]["gcm_key"] is None + assert mock_wp.mock_calls[3][2]["gcm_key"] is not None + assert mock_wp.mock_calls[5][2]["gcm_key"] is None @patch("homeassistant.components.html5.notify.WebPusher") def test_fcm_key_include(self, mock_wp): """Test if the FCM header is included.""" hass = MagicMock() + mock_wp().send().status_code = 201 data = {"chrome": SUBSCRIPTION_5} @@ -193,20 +189,18 @@ class TestHtml5Notify: service.send_message("Hello", target=["chrome"]) - assert len(mock_wp.mock_calls) == 3 + assert len(mock_wp.mock_calls) == 4 # WebPusher constructor - assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_5["subscription"] - - # Third mock_call checks the status_code of the response. - assert mock_wp.mock_calls[2][0] == "().send().status_code.__eq__" + assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"] # Get the keys passed to the WebPusher's send method - assert mock_wp.mock_calls[1][2]["headers"]["Authorization"] is not None + assert mock_wp.mock_calls[3][2]["headers"]["Authorization"] is not None @patch("homeassistant.components.html5.notify.WebPusher") def test_fcm_send_with_unknown_priority(self, mock_wp): """Test if the gcm_key is only included for GCM endpoints.""" hass = MagicMock() + mock_wp().send().status_code = 201 data = {"chrome": SUBSCRIPTION_5} @@ -218,20 +212,18 @@ class TestHtml5Notify: service.send_message("Hello", target=["chrome"], priority="undefined") - assert len(mock_wp.mock_calls) == 3 + assert len(mock_wp.mock_calls) == 4 # WebPusher constructor - assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_5["subscription"] - - # Third mock_call checks the status_code of the response. - assert mock_wp.mock_calls[2][0] == "().send().status_code.__eq__" + assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"] # Get the keys passed to the WebPusher's send method - assert mock_wp.mock_calls[1][2]["headers"]["priority"] == "normal" + assert mock_wp.mock_calls[3][2]["headers"]["priority"] == "normal" @patch("homeassistant.components.html5.notify.WebPusher") def test_fcm_no_targets(self, mock_wp): """Test if the gcm_key is only included for GCM endpoints.""" hass = MagicMock() + mock_wp().send().status_code = 201 data = {"chrome": SUBSCRIPTION_5} @@ -243,20 +235,18 @@ class TestHtml5Notify: service.send_message("Hello") - assert len(mock_wp.mock_calls) == 3 + assert len(mock_wp.mock_calls) == 4 # WebPusher constructor - assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_5["subscription"] - - # Third mock_call checks the status_code of the response. - assert mock_wp.mock_calls[2][0] == "().send().status_code.__eq__" + assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"] # Get the keys passed to the WebPusher's send method - assert mock_wp.mock_calls[1][2]["headers"]["priority"] == "normal" + assert mock_wp.mock_calls[3][2]["headers"]["priority"] == "normal" @patch("homeassistant.components.html5.notify.WebPusher") def test_fcm_additional_data(self, mock_wp): """Test if the gcm_key is only included for GCM endpoints.""" hass = MagicMock() + mock_wp().send().status_code = 201 data = {"chrome": SUBSCRIPTION_5} @@ -268,21 +258,18 @@ class TestHtml5Notify: service.send_message("Hello", data={"mykey": "myvalue"}) - assert len(mock_wp.mock_calls) == 3 + assert len(mock_wp.mock_calls) == 4 # WebPusher constructor - assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_5["subscription"] - - # Third mock_call checks the status_code of the response. - assert mock_wp.mock_calls[2][0] == "().send().status_code.__eq__" + assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"] # Get the keys passed to the WebPusher's send method - assert mock_wp.mock_calls[1][2]["headers"]["priority"] == "normal" + assert mock_wp.mock_calls[3][2]["headers"]["priority"] == "normal" def test_create_vapid_withoutvapid(): """Test creating empty vapid.""" resp = html5.create_vapid_headers( - vapid_email=None, vapid_private_key=None, subscription_info=None + vapid_email=None, vapid_private_key=None, subscription_info=None, timestamp=None ) assert resp is None @@ -478,6 +465,7 @@ async def test_callback_view_with_jwt(hass, hass_client): client = await mock_client(hass, hass_client, registrations) with patch("homeassistant.components.html5.notify.WebPusher") as mock_wp: + mock_wp().send().status_code = 201 await hass.services.async_call( "notify", "notify", @@ -485,15 +473,13 @@ async def test_callback_view_with_jwt(hass, hass_client): blocking=True, ) - assert len(mock_wp.mock_calls) == 3 + assert len(mock_wp.mock_calls) == 4 # WebPusher constructor - assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_1["subscription"] - # Third mock_call checks the status_code of the response. - assert mock_wp.mock_calls[2][0] == "().send().status_code.__eq__" + assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_1["subscription"] # Call to send - push_payload = json.loads(mock_wp.mock_calls[1][1][0]) + push_payload = json.loads(mock_wp.mock_calls[3][1][0]) assert push_payload["body"] == "Hello" assert push_payload["icon"] == "beer.png" @@ -514,6 +500,7 @@ async def test_send_fcm_without_targets(hass, hass_client): registrations = {"device": SUBSCRIPTION_5} await mock_client(hass, hass_client, registrations) with patch("homeassistant.components.html5.notify.WebPusher") as mock_wp: + mock_wp().send().status_code = 201 await hass.services.async_call( "notify", "notify", @@ -521,12 +508,10 @@ async def test_send_fcm_without_targets(hass, hass_client): blocking=True, ) - assert len(mock_wp.mock_calls) == 3 + assert len(mock_wp.mock_calls) == 4 # WebPusher constructor - assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_5["subscription"] - # Third mock_call checks the status_code of the response. - assert mock_wp.mock_calls[2][0] == "().send().status_code.__eq__" + assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"] async def test_send_fcm_expired(hass, hass_client): From 305dfda9aca5b254f9108a575594340b76dbba51 Mon Sep 17 00:00:00 2001 From: majuss Date: Fri, 2 Dec 2022 17:30:25 +0100 Subject: [PATCH 0948/1033] Bump lupupy to 0.2.1 (#83071) --- homeassistant/components/lupusec/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/lupusec/manifest.json b/homeassistant/components/lupusec/manifest.json index cb526b004de..5792f186798 100644 --- a/homeassistant/components/lupusec/manifest.json +++ b/homeassistant/components/lupusec/manifest.json @@ -2,7 +2,7 @@ "domain": "lupusec", "name": "Lupus Electronics LUPUSEC", "documentation": "https://www.home-assistant.io/integrations/lupusec", - "requirements": ["lupupy==0.1.9"], + "requirements": ["lupupy==0.2.1"], "codeowners": ["@majuss"], "iot_class": "local_polling", "loggers": ["lupupy"] diff --git a/requirements_all.txt b/requirements_all.txt index 6b97c0c5868..cede4f28175 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1052,7 +1052,7 @@ london-tube-status==0.5 luftdaten==0.7.4 # homeassistant.components.lupusec -lupupy==0.1.9 +lupupy==0.2.1 # homeassistant.components.lw12wifi lw12==0.9.2 From ee467e0f3a1663cebb02a179c6f176964b6ae0f1 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 1 Dec 2022 23:54:12 +0100 Subject: [PATCH 0949/1033] Fix prettier [ci] (#83077) --- homeassistant/components/unifiprotect/strings.sensor.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/unifiprotect/strings.sensor.json b/homeassistant/components/unifiprotect/strings.sensor.json index 07b1955ba81..ccb16e2d445 100644 --- a/homeassistant/components/unifiprotect/strings.sensor.json +++ b/homeassistant/components/unifiprotect/strings.sensor.json @@ -1,7 +1,7 @@ { - "state": { - "unifiprotect__license_plate": { - "none": "Clear" - } + "state": { + "unifiprotect__license_plate": { + "none": "Clear" } + } } From 52b3a309f871913c4231a92c025cd5ffb2a8446a Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Fri, 2 Dec 2022 18:27:33 -0700 Subject: [PATCH 0950/1033] Return empty data when OpenUV API call fails (#83089) Co-authored-by: Paulus Schoutsen fixes undefined --- homeassistant/components/openuv/coordinator.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/openuv/coordinator.py b/homeassistant/components/openuv/coordinator.py index 1df6a91b398..f89a9c696a8 100644 --- a/homeassistant/components/openuv/coordinator.py +++ b/homeassistant/components/openuv/coordinator.py @@ -32,10 +32,10 @@ class InvalidApiKeyMonitor: async def async_increment(self) -> None: """Increment the counter.""" - LOGGER.debug("Invalid API key response detected (number %s)", self._count) async with self._lock: self._count += 1 if self._count > self.DEFAULT_FAILED_API_CALL_THRESHOLD: + LOGGER.info("Starting reauth after multiple failed API calls") self._reauth_flow_manager.start_reauth() async def async_reset(self) -> None: @@ -114,10 +114,11 @@ class OpenUvCoordinator(DataUpdateCoordinator): """Fetch data from OpenUV.""" try: data = await self.update_method() - except InvalidApiKeyError: + except InvalidApiKeyError as err: await self._invalid_api_key_monitor.async_increment() + raise UpdateFailed(str(err)) from err except OpenUvError as err: - raise UpdateFailed(f"Error during protection data update: {err}") from err + raise UpdateFailed(str(err)) from err await self._invalid_api_key_monitor.async_reset() return cast(dict[str, Any], data["result"]) From 1f804e2eedf0764fe84ce6d1795ac0a40d6f8edb Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Fri, 2 Dec 2022 03:15:24 -0500 Subject: [PATCH 0951/1033] Bump pyunifiprotect to 4.5.2 (#83090) --- homeassistant/components/unifiprotect/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index 52e5ec31557..c7259356b66 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -4,7 +4,7 @@ "integration_type": "hub", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/unifiprotect", - "requirements": ["pyunifiprotect==4.5.1", "unifi-discovery==1.1.7"], + "requirements": ["pyunifiprotect==4.5.2", "unifi-discovery==1.1.7"], "dependencies": ["http", "repairs"], "codeowners": ["@briis", "@AngellusMortis", "@bdraco"], "quality_scale": "platinum", diff --git a/requirements_all.txt b/requirements_all.txt index cede4f28175..012ea4ca3d4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2101,7 +2101,7 @@ pytrafikverket==0.2.2 pyudev==0.23.2 # homeassistant.components.unifiprotect -pyunifiprotect==4.5.1 +pyunifiprotect==4.5.2 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 44eddd3a92f..515d74a831c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1464,7 +1464,7 @@ pytrafikverket==0.2.2 pyudev==0.23.2 # homeassistant.components.unifiprotect -pyunifiprotect==4.5.1 +pyunifiprotect==4.5.2 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 From f13f165d6428e5717e47d48e94c38aec4da887fe Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 1 Dec 2022 21:08:35 -1000 Subject: [PATCH 0952/1033] Fix wrong ble device being logged (#83091) This code can be simplified a bit now that we have the fast bleak lookup. We do connect via the correct path, but we logged the wrong one --- homeassistant/components/bluetooth/wrappers.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/bluetooth/wrappers.py b/homeassistant/components/bluetooth/wrappers.py index ca4f2314b65..d81331af331 100644 --- a/homeassistant/components/bluetooth/wrappers.py +++ b/homeassistant/components/bluetooth/wrappers.py @@ -162,7 +162,6 @@ class HaBleakClientWrapper(BleakClient): self.__address = address_or_ble_device self.__disconnected_callback = disconnected_callback self.__timeout = timeout - self.__ble_device: BLEDevice | None = None self._backend: BaseBleakClient | None = None # type: ignore[assignment] @property @@ -183,12 +182,9 @@ class HaBleakClientWrapper(BleakClient): async def connect(self, **kwargs: Any) -> bool: """Connect to the specified GATT server.""" assert models.MANAGER is not None - ( - wrapped_backend, - self.__ble_device, - ) = self._async_get_best_available_backend_and_device() + wrapped_backend = self._async_get_best_available_backend_and_device() self._backend = wrapped_backend.client( - self.__ble_device, + wrapped_backend.device, disconnected_callback=self.__disconnected_callback, timeout=self.__timeout, hass=models.MANAGER.hass, @@ -218,7 +214,7 @@ class HaBleakClientWrapper(BleakClient): @hass_callback def _async_get_best_available_backend_and_device( self, - ) -> tuple[_HaWrappedBleakBackend, BLEDevice]: + ) -> _HaWrappedBleakBackend: """Get a best available backend and device for the given address. This method will return the backend with the best rssi @@ -235,9 +231,10 @@ class HaBleakClientWrapper(BleakClient): or NO_RSSI_VALUE, reverse=True, ): - ble_device = device_advertisement_data[0] - if backend := self._async_get_backend_for_ble_device(ble_device): - return backend, ble_device + if backend := self._async_get_backend_for_ble_device( + device_advertisement_data[0] + ): + return backend raise BleakError( f"No backend with an available connection slot that can reach address {address} was found" From 83af8434cda193d0489d8cd6e7915c1d69a0a71b Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Fri, 2 Dec 2022 01:56:29 -0500 Subject: [PATCH 0953/1033] Bump zwave-js-server-python to 0.43.1 (#83093) --- homeassistant/components/zwave_js/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zwave_js/manifest.json b/homeassistant/components/zwave_js/manifest.json index 38c1e8b181f..4dc86fed92c 100644 --- a/homeassistant/components/zwave_js/manifest.json +++ b/homeassistant/components/zwave_js/manifest.json @@ -3,7 +3,7 @@ "name": "Z-Wave", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zwave_js", - "requirements": ["pyserial==3.5", "zwave-js-server-python==0.43.0"], + "requirements": ["pyserial==3.5", "zwave-js-server-python==0.43.1"], "codeowners": ["@home-assistant/z-wave"], "dependencies": ["usb", "http", "websocket_api"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 012ea4ca3d4..d9715751a07 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2666,7 +2666,7 @@ zigpy==0.52.2 zm-py==0.5.2 # homeassistant.components.zwave_js -zwave-js-server-python==0.43.0 +zwave-js-server-python==0.43.1 # homeassistant.components.zwave_me zwave_me_ws==0.3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 515d74a831c..3b7e782c3b6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1858,7 +1858,7 @@ zigpy-znp==0.9.2 zigpy==0.52.2 # homeassistant.components.zwave_js -zwave-js-server-python==0.43.0 +zwave-js-server-python==0.43.1 # homeassistant.components.zwave_me zwave_me_ws==0.3.0 From 67d03031d5a2f0bdbbdccfdca356d0482c460a18 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 1 Dec 2022 22:07:31 -1000 Subject: [PATCH 0954/1033] Bump aiohomekit to 2.3.4 (#83094) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 041f2ad293e..234108d616d 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.3.3"], + "requirements": ["aiohomekit==2.3.4"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index d9715751a07..db94c694628 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.3 +aiohomekit==2.3.4 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3b7e782c3b6..d64914f56ce 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.3 +aiohomekit==2.3.4 # homeassistant.components.emulated_hue # homeassistant.components.http From f635751020d88bd46ce6720e37fbfbeb73d28407 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 1 Dec 2022 21:08:56 -1000 Subject: [PATCH 0955/1033] Bump bleak-retry-connector to 2.8.7 (#83095) changelog: https://github.com/Bluetooth-Devices/bleak-retry-connector/compare/v2.8.6...v2.8.7 --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 982edf10df4..a59411aae5a 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.2", - "bleak-retry-connector==2.8.6", + "bleak-retry-connector==2.8.7", "bluetooth-adapters==0.11.0", "bluetooth-auto-recovery==0.5.3", "bluetooth-data-tools==0.3.0", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 323cc927c96..dc9172c78bf 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.8.6 +bleak-retry-connector==2.8.7 bleak==0.19.2 bluetooth-adapters==0.11.0 bluetooth-auto-recovery==0.5.3 diff --git a/requirements_all.txt b/requirements_all.txt index db94c694628..c453f3ded39 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -422,7 +422,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.6 +bleak-retry-connector==2.8.7 # homeassistant.components.bluetooth bleak==0.19.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d64914f56ce..ed8a8f51c76 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -346,7 +346,7 @@ bellows==0.34.5 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.6 +bleak-retry-connector==2.8.7 # homeassistant.components.bluetooth bleak==0.19.2 From daad93dd311c8b4b9b0407577ae1a8b35ed26d5e Mon Sep 17 00:00:00 2001 From: Olen Date: Fri, 2 Dec 2022 09:58:29 +0100 Subject: [PATCH 0956/1033] Fix twinkly effects (#83104) Adding additional checks --- homeassistant/components/twinkly/light.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/twinkly/light.py b/homeassistant/components/twinkly/light.py index cc2270f25e1..8f8e8218d87 100644 --- a/homeassistant/components/twinkly/light.py +++ b/homeassistant/components/twinkly/light.py @@ -288,11 +288,13 @@ class TwinklyLight(LightEntity): async def async_update_movies(self) -> None: """Update the list of movies (effects).""" movies = await self._client.get_saved_movies() - if "movies" in movies: + _LOGGER.debug("Movies: %s", movies) + if movies and "movies" in movies: self._movies = movies["movies"] async def async_update_current_movie(self) -> None: """Update the current active movie.""" current_movie = await self._client.get_current_movie() - if "id" in current_movie: + _LOGGER.debug("Current movie: %s", current_movie) + if current_movie and "id" in current_movie: self._current_movie = current_movie From 0be9391d7954d7f875f4847454b6a66af95c3544 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 2 Dec 2022 14:45:49 -1000 Subject: [PATCH 0957/1033] Fix esphome ble client leaking notify on disconnect (#83106) * Fix esphome ble client leaking notify on disconnect needs: https://github.com/esphome/aioesphomeapi/pull/329 * leak * more cleanup * more cleanup * bump --- .../components/esphome/bluetooth/client.py | 32 ++++++++++++------- .../components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index da4d5fb3d26..1ec3b585fae 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -142,7 +142,9 @@ class ESPHomeClient(BaseBleakClient): self._is_connected = False self._mtu: int | None = None self._cancel_connection_state: CALLBACK_TYPE | None = None - self._notify_cancels: dict[int, Callable[[], Coroutine[Any, Any, None]]] = {} + self._notify_cancels: dict[ + int, tuple[Callable[[], Coroutine[Any, Any, None]], Callable[[], None]] + ] = {} self._disconnected_event: asyncio.Event | None = None device_info = self.entry_data.device_info assert device_info is not None @@ -169,15 +171,22 @@ class ESPHomeClient(BaseBleakClient): ) self._cancel_connection_state = None - def _async_ble_device_disconnected(self) -> None: - """Handle the BLE device disconnecting from the ESP.""" - was_connected = self._is_connected + def _async_disconnected_cleanup(self) -> None: + """Clean up on disconnect.""" self.services = BleakGATTServiceCollection() # type: ignore[no-untyped-call] self._is_connected = False + for _, notify_abort in self._notify_cancels.values(): + notify_abort() self._notify_cancels.clear() if self._disconnected_event: self._disconnected_event.set() self._disconnected_event = None + self._unsubscribe_connection_state() + + def _async_ble_device_disconnected(self) -> None: + """Handle the BLE device disconnecting from the ESP.""" + was_connected = self._is_connected + self._async_disconnected_cleanup() if was_connected: _LOGGER.debug( "%s: %s - %s: BLE device disconnected", @@ -186,7 +195,6 @@ class ESPHomeClient(BaseBleakClient): self._ble_device.address, ) self._async_call_bleak_disconnected_callback() - self._unsubscribe_connection_state() def _async_esp_disconnected(self) -> None: """Handle the esp32 client disconnecting from hass.""" @@ -316,7 +324,7 @@ class ESPHomeClient(BaseBleakClient): @api_error_as_bleak_error async def disconnect(self) -> bool: """Disconnect from the peripheral device.""" - self._unsubscribe_connection_state() + self._async_disconnected_cleanup() await self._client.bluetooth_device_disconnect(self._address_as_int) await self._wait_for_free_connection_slot(DISCONNECT_TIMEOUT) return True @@ -551,12 +559,13 @@ class ESPHomeClient(BaseBleakClient): f"Characteristic {characteristic.uuid} does not have notify or indicate property set." ) - cancel_coro = await self._client.bluetooth_gatt_start_notify( + self._notify_cancels[ + ble_handle + ] = await self._client.bluetooth_gatt_start_notify( self._address_as_int, ble_handle, lambda handle, data: callback(data), ) - self._notify_cancels[ble_handle] = cancel_coro if self._connection_version < MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE: return @@ -604,8 +613,9 @@ class ESPHomeClient(BaseBleakClient): characteristic = self._resolve_characteristic(char_specifier) # Do not raise KeyError if notifications are not enabled on this characteristic # to be consistent with the behavior of the BlueZ backend - if coro := self._notify_cancels.pop(characteristic.handle, None): - await coro() + if notify_cancel := self._notify_cancels.pop(characteristic.handle, None): + notify_stop, _ = notify_cancel + await notify_stop() def __del__(self) -> None: """Destructor to make sure the connection state is unsubscribed.""" @@ -617,4 +627,4 @@ class ESPHomeClient(BaseBleakClient): self._ble_device.address, ) if not self._hass.loop.is_closed(): - self._hass.loop.call_soon_threadsafe(self._unsubscribe_connection_state) + self._hass.loop.call_soon_threadsafe(self._async_disconnected_cleanup) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 42ebc471b7c..6d9796b8541 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==12.2.1"], + "requirements": ["aioesphomeapi==13.0.0"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/requirements_all.txt b/requirements_all.txt index c453f3ded39..113b41fe2e9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -156,7 +156,7 @@ aioecowitt==2022.11.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==12.2.1 +aioesphomeapi==13.0.0 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ed8a8f51c76..2ddf911f16a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -143,7 +143,7 @@ aioecowitt==2022.11.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==12.2.1 +aioesphomeapi==13.0.0 # homeassistant.components.flo aioflo==2021.11.0 From e13413ee0938fcf83f2dec97eccdf9597c854d9b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 2 Dec 2022 14:48:06 -1000 Subject: [PATCH 0958/1033] Bump bluetooth-auto-recovery to 0.5.4 (#83155) changelog: https://github.com/Bluetooth-Devices/bluetooth-auto-recovery/compare/v0.5.3...v0.5.4 --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index a59411aae5a..8968d03267b 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -9,7 +9,7 @@ "bleak==0.19.2", "bleak-retry-connector==2.8.7", "bluetooth-adapters==0.11.0", - "bluetooth-auto-recovery==0.5.3", + "bluetooth-auto-recovery==0.5.4", "bluetooth-data-tools==0.3.0", "dbus-fast==1.75.0" ], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index dc9172c78bf..e1e88d2ebca 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -13,7 +13,7 @@ bcrypt==3.1.7 bleak-retry-connector==2.8.7 bleak==0.19.2 bluetooth-adapters==0.11.0 -bluetooth-auto-recovery==0.5.3 +bluetooth-auto-recovery==0.5.4 bluetooth-data-tools==0.3.0 certifi>=2021.5.30 ciso8601==2.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index 113b41fe2e9..406cf36c859 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -450,7 +450,7 @@ bluemaestro-ble==0.2.0 bluetooth-adapters==0.11.0 # homeassistant.components.bluetooth -bluetooth-auto-recovery==0.5.3 +bluetooth-auto-recovery==0.5.4 # homeassistant.components.bluetooth # homeassistant.components.led_ble diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2ddf911f16a..cd7502476ea 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -364,7 +364,7 @@ bluemaestro-ble==0.2.0 bluetooth-adapters==0.11.0 # homeassistant.components.bluetooth -bluetooth-auto-recovery==0.5.3 +bluetooth-auto-recovery==0.5.4 # homeassistant.components.bluetooth # homeassistant.components.led_ble From 81c7a2413348358fde2e30c8505b4c14a5f23d28 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 2 Dec 2022 13:24:12 -1000 Subject: [PATCH 0959/1033] Fix logging the wrong bluetooth adapter while connecting and out of slots (#83158) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/components/bluetooth/wrappers.py | 12 ++++++++++-- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 8968d03267b..0ba5768d37c 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.2", - "bleak-retry-connector==2.8.7", + "bleak-retry-connector==2.8.9", "bluetooth-adapters==0.11.0", "bluetooth-auto-recovery==0.5.4", "bluetooth-data-tools==0.3.0", diff --git a/homeassistant/components/bluetooth/wrappers.py b/homeassistant/components/bluetooth/wrappers.py index d81331af331..beaa2acc78a 100644 --- a/homeassistant/components/bluetooth/wrappers.py +++ b/homeassistant/components/bluetooth/wrappers.py @@ -12,7 +12,7 @@ from bleak import BleakClient, BleakError from bleak.backends.client import BaseBleakClient, get_platform_client_backend_type from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementDataCallback, BaseBleakScanner -from bleak_retry_connector import NO_RSSI_VALUE +from bleak_retry_connector import NO_RSSI_VALUE, ble_device_description from homeassistant.core import CALLBACK_TYPE, callback as hass_callback from homeassistant.helpers.frame import report @@ -189,7 +189,15 @@ class HaBleakClientWrapper(BleakClient): timeout=self.__timeout, hass=models.MANAGER.hass, ) - return await super().connect(**kwargs) + if debug_logging := _LOGGER.isEnabledFor(logging.DEBUG): + # Only lookup the description if we are going to log it + description = ble_device_description(wrapped_backend.device) + rssi = wrapped_backend.device.rssi + _LOGGER.debug("%s: Connecting (last rssi: %s)", description, rssi) + connected = await super().connect(**kwargs) + if debug_logging: + _LOGGER.debug("%s: Connected (last rssi: %s)", description, rssi) + return connected @hass_callback def _async_get_backend_for_ble_device( diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index e1e88d2ebca..f41444de531 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.8.7 +bleak-retry-connector==2.8.9 bleak==0.19.2 bluetooth-adapters==0.11.0 bluetooth-auto-recovery==0.5.4 diff --git a/requirements_all.txt b/requirements_all.txt index 406cf36c859..428b8e9a1fd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -422,7 +422,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.7 +bleak-retry-connector==2.8.9 # homeassistant.components.bluetooth bleak==0.19.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cd7502476ea..f9a42dac94e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -346,7 +346,7 @@ bellows==0.34.5 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.7 +bleak-retry-connector==2.8.9 # homeassistant.components.bluetooth bleak==0.19.2 From f5de016f25e0c4b12b56c9bfc04a03790ee57954 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 2 Dec 2022 14:49:13 -1000 Subject: [PATCH 0960/1033] Fix reauth with esphome when adding noise encryption (#83164) * Fix reauth with esphome when adding noise encryption fixes #80813 * fix with unique id --- .../components/esphome/config_flow.py | 16 ++++--- tests/components/esphome/test_config_flow.py | 47 +++++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/esphome/config_flow.py b/homeassistant/components/esphome/config_flow.py index ea64fb7fb7f..542aa011a7b 100644 --- a/homeassistant/components/esphome/config_flow.py +++ b/homeassistant/components/esphome/config_flow.py @@ -17,7 +17,7 @@ from aioesphomeapi import ( import voluptuous as vol from homeassistant.components import dhcp, zeroconf -from homeassistant.config_entries import ConfigFlow +from homeassistant.config_entries import ConfigEntry, ConfigFlow from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult @@ -40,6 +40,7 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN): self._password: str | None = None self._noise_psk: str | None = None self._device_info: DeviceInfo | None = None + self._reauth_entry: ConfigEntry | None = None async def _async_step_user_base( self, user_input: dict[str, Any] | None = None, error: str | None = None @@ -72,6 +73,7 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN): """Handle a flow initialized by a reauth event.""" entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) assert entry is not None + self._reauth_entry = entry self._host = entry.data[CONF_HOST] self._port = entry.data[CONF_PORT] self._password = entry.data[CONF_PASSWORD] @@ -245,10 +247,11 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN): CONF_PASSWORD: self._password or "", CONF_NOISE_PSK: self._noise_psk or "", } - if "entry_id" in self.context: - entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) - assert entry is not None - self.hass.config_entries.async_update_entry(entry, data=config_data) + if self._reauth_entry: + entry = self._reauth_entry + self.hass.config_entries.async_update_entry( + entry, data=self._reauth_entry.data | config_data + ) # Reload the config entry to notify of updated config self.hass.async_create_task( self.hass.config_entries.async_reload(entry.entry_id) @@ -332,7 +335,8 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN): self._name = self._device_info.name await self.async_set_unique_id(self._name, raise_on_progress=False) - self._abort_if_unique_id_configured(updates={CONF_HOST: self._host}) + if not self._reauth_entry: + self._abort_if_unique_id_configured(updates={CONF_HOST: self._host}) return None diff --git a/tests/components/esphome/test_config_flow.py b/tests/components/esphome/test_config_flow.py index a4d1f416868..efeb2d376cf 100644 --- a/tests/components/esphome/test_config_flow.py +++ b/tests/components/esphome/test_config_flow.py @@ -559,6 +559,53 @@ async def test_reauth_confirm_invalid(hass, mock_client, mock_zeroconf): assert result["errors"] assert result["errors"]["base"] == "invalid_psk" + mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(False, "test")) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_NOISE_PSK: VALID_NOISE_PSK} + ) + + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "reauth_successful" + assert entry.data[CONF_NOISE_PSK] == VALID_NOISE_PSK + + +async def test_reauth_confirm_invalid_with_unique_id(hass, mock_client, mock_zeroconf): + """Test reauth initiation with invalid PSK.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_HOST: "127.0.0.1", CONF_PORT: 6053, CONF_PASSWORD: ""}, + unique_id="test", + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + "esphome", + context={ + "source": config_entries.SOURCE_REAUTH, + "entry_id": entry.entry_id, + "unique_id": entry.unique_id, + }, + ) + + mock_client.device_info.side_effect = InvalidEncryptionKeyAPIError + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_NOISE_PSK: INVALID_NOISE_PSK} + ) + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "reauth_confirm" + assert result["errors"] + assert result["errors"]["base"] == "invalid_psk" + + mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(False, "test")) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_NOISE_PSK: VALID_NOISE_PSK} + ) + + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "reauth_successful" + assert entry.data[CONF_NOISE_PSK] == VALID_NOISE_PSK + async def test_discovery_dhcp_updates_host(hass, mock_client): """Test dhcp discovery updates host and aborts.""" From 2b4587a7a8c9254664907254e0cc96a3e95e6593 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 2 Dec 2022 13:49:14 -1000 Subject: [PATCH 0961/1033] Fix race setting up homekit controller triggers (#83166) fixes https://github.com/home-assistant/core/issues/83165 --- .../components/homekit_controller/__init__.py | 3 +-- .../components/homekit_controller/device_trigger.py | 11 +++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index 54da6e71c8c..aa56bbbda78 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -21,7 +21,7 @@ from homeassistant.helpers.typing import ConfigType from .config_flow import normalize_hkid from .connection import HKDevice -from .const import KNOWN_DEVICES, TRIGGERS +from .const import KNOWN_DEVICES from .utils import async_get_controller _LOGGER = logging.getLogger(__name__) @@ -59,7 +59,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: await async_get_controller(hass) hass.data[KNOWN_DEVICES] = {} - hass.data[TRIGGERS] = {} async def _async_stop_homekit_controller(event: Event) -> None: await asyncio.gather( diff --git a/homeassistant/components/homekit_controller/device_trigger.py b/homeassistant/components/homekit_controller/device_trigger.py index bc1434f4bd9..229c8aecc00 100644 --- a/homeassistant/components/homekit_controller/device_trigger.py +++ b/homeassistant/components/homekit_controller/device_trigger.py @@ -224,7 +224,7 @@ async def async_setup_triggers_for_entry( # They have to be different accessories (they can be on the same bridge) # In practice, this is inline with what iOS actually supports AFAWCT. device_id = conn.devices[aid] - if device_id in hass.data[TRIGGERS]: + if TRIGGERS in hass.data and device_id in hass.data[TRIGGERS]: return False # Just because we recognize the service type doesn't mean we can actually @@ -246,15 +246,18 @@ def async_get_or_create_trigger_source( hass: HomeAssistant, device_id: str ) -> TriggerSource: """Get or create a trigger source for a device id.""" - if not (source := hass.data[TRIGGERS].get(device_id)): + trigger_sources: dict[str, TriggerSource] = hass.data.setdefault(TRIGGERS, {}) + if not (source := trigger_sources.get(device_id)): source = TriggerSource(hass) - hass.data[TRIGGERS][device_id] = source + trigger_sources[device_id] = source return source def async_fire_triggers(conn: HKDevice, events: dict[tuple[int, int], dict[str, Any]]): """Process events generated by a HomeKit accessory into automation triggers.""" - trigger_sources: dict[str, TriggerSource] = conn.hass.data[TRIGGERS] + trigger_sources: dict[str, TriggerSource] = conn.hass.data.get(TRIGGERS, {}) + if not trigger_sources: + return for (aid, iid), ev in events.items(): if aid in conn.devices: device_id = conn.devices[aid] From 5c8ccc89b13dc34a566254ee6ecaf0ce06d8488a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 2 Dec 2022 16:00:49 -1000 Subject: [PATCH 0962/1033] Bump aiohomekit to 2.3.5 (#83168) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 234108d616d..d67eeac1fd3 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.3.4"], + "requirements": ["aiohomekit==2.3.5"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index 428b8e9a1fd..42b8b05c137 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.4 +aiohomekit==2.3.5 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f9a42dac94e..273cc72c140 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.4 +aiohomekit==2.3.5 # homeassistant.components.emulated_hue # homeassistant.components.http From 627f337e1e302d384c57ccef710cd7326bd3f500 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 2 Dec 2022 21:01:38 -0500 Subject: [PATCH 0963/1033] Bumped version to 2022.12.0b2 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 16dfdc644d3..333b3076dfc 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -8,7 +8,7 @@ from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "0b1" +PATCH_VERSION: Final = "0b2" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/pyproject.toml b/pyproject.toml index 260d7553cb6..441c4e95e91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.12.0b1" +version = "2022.12.0b2" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From 96cb856308ceef921b3652a8a1a3f7cb4a6d24bd Mon Sep 17 00:00:00 2001 From: Lars Date: Sat, 3 Dec 2022 19:24:06 +0100 Subject: [PATCH 0964/1033] Add integration_type to AVM FRITZ!SmartHome (#81096) --- homeassistant/components/fritzbox/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/fritzbox/manifest.json b/homeassistant/components/fritzbox/manifest.json index 422db12b68a..d9b8b1795af 100644 --- a/homeassistant/components/fritzbox/manifest.json +++ b/homeassistant/components/fritzbox/manifest.json @@ -1,6 +1,7 @@ { "domain": "fritzbox", "name": "AVM FRITZ!SmartHome", + "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/fritzbox", "requirements": ["pyfritzhome==0.6.7"], "ssdp": [ From fcb3445b8edc0cb89603189052576a133d445cee Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Sat, 3 Dec 2022 12:53:12 +0100 Subject: [PATCH 0965/1033] KNX Config/OptionsFlow: Test connection to manually configured tunnel (#82872) --- homeassistant/components/knx/config_flow.py | 39 ++++- homeassistant/components/knx/strings.json | 6 +- tests/components/knx/test_config_flow.py | 181 +++++++++++++++++++- 3 files changed, 208 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/knx/config_flow.py b/homeassistant/components/knx/config_flow.py index 30e48cd4d6a..f7ef0d943a9 100644 --- a/homeassistant/components/knx/config_flow.py +++ b/homeassistant/components/knx/config_flow.py @@ -7,9 +7,10 @@ from typing import Any, Final import voluptuous as vol from xknx import XKNX -from xknx.exceptions.exception import InvalidSecureConfiguration +from xknx.exceptions.exception import CommunicationError, InvalidSecureConfiguration from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT from xknx.io.gateway_scanner import GatewayDescriptor, GatewayScanner +from xknx.io.self_description import request_description from xknx.secure import load_keyring from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow @@ -204,8 +205,11 @@ class KNXCommonFlow(ABC, FlowHandler): return await self.async_step_manual_tunnel() errors: dict = {} - tunnel_options = [str(tunnel) for tunnel in self._found_tunnels] - tunnel_options.append(OPTION_MANUAL_TUNNEL) + tunnel_options = { + str(tunnel): f"{tunnel}{' 🔐' if tunnel.tunnelling_requires_secure else ''}" + for tunnel in self._found_tunnels + } + tunnel_options |= {OPTION_MANUAL_TUNNEL: OPTION_MANUAL_TUNNEL} fields = {vol.Required(CONF_KNX_GATEWAY): vol.In(tunnel_options)} return self.async_show_form( @@ -230,17 +234,38 @@ class KNXCommonFlow(ABC, FlowHandler): except vol.Invalid: errors[CONF_KNX_LOCAL_IP] = "invalid_ip_address" + selected_tunnelling_type = user_input[CONF_KNX_TUNNELING_TYPE] + if not errors: + try: + self._selected_tunnel = await request_description( + gateway_ip=_host, + gateway_port=user_input[CONF_PORT], + local_ip=_local_ip, + route_back=user_input[CONF_KNX_ROUTE_BACK], + ) + except CommunicationError: + errors["base"] = "cannot_connect" + else: + if bool(self._selected_tunnel.tunnelling_requires_secure) is not ( + selected_tunnelling_type == CONF_KNX_TUNNELING_TCP_SECURE + ): + errors[CONF_KNX_TUNNELING_TYPE] = "unsupported_tunnel_type" + elif ( + selected_tunnelling_type == CONF_KNX_TUNNELING_TCP + and not self._selected_tunnel.supports_tunnelling_tcp + ): + errors[CONF_KNX_TUNNELING_TYPE] = "unsupported_tunnel_type" + if not errors: - connection_type = user_input[CONF_KNX_TUNNELING_TYPE] self.new_entry_data = KNXConfigEntryData( + connection_type=selected_tunnelling_type, host=_host, port=user_input[CONF_PORT], route_back=user_input[CONF_KNX_ROUTE_BACK], local_ip=_local_ip, - connection_type=connection_type, ) - if connection_type == CONF_KNX_TUNNELING_TCP_SECURE: + if selected_tunnelling_type == CONF_KNX_TUNNELING_TCP_SECURE: return self.async_show_menu( step_id="secure_key_source", menu_options=["secure_knxkeys", "secure_routing_manual"], @@ -299,7 +324,7 @@ class KNXCommonFlow(ABC, FlowHandler): if self.show_advanced_options: fields[vol.Optional(CONF_KNX_LOCAL_IP)] = _IP_SELECTOR - if not self._found_tunnels: + if not self._found_tunnels and not errors.get("base"): errors["base"] = "no_tunnel_discovered" return self.async_show_form( step_id="manual_tunnel", data_schema=vol.Schema(fields), errors=errors diff --git a/homeassistant/components/knx/strings.json b/homeassistant/components/knx/strings.json index 0fbb7fc3ae6..632af9961dc 100644 --- a/homeassistant/components/knx/strings.json +++ b/homeassistant/components/knx/strings.json @@ -99,7 +99,8 @@ "invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", "file_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/", "no_router_discovered": "No KNXnet/IP router was discovered on the network.", - "no_tunnel_discovered": "Could not find a KNX tunneling server on your network." + "no_tunnel_discovered": "Could not find a KNX tunneling server on your network.", + "unsupported_tunnel_type": "Selected tunnelling type not supported by gateway." } }, "options": { @@ -214,7 +215,8 @@ "invalid_signature": "[%key:component::knx::config::error::invalid_signature%]", "file_not_found": "[%key:component::knx::config::error::file_not_found%]", "no_router_discovered": "[%key:component::knx::config::error::no_router_discovered%]", - "no_tunnel_discovered": "[%key:component::knx::config::error::no_tunnel_discovered%]" + "no_tunnel_discovered": "[%key:component::knx::config::error::no_tunnel_discovered%]", + "unsupported_tunnel_type": "[%key:component::knx::config::error::unsupported_tunnel_type%]" } } } diff --git a/tests/components/knx/test_config_flow.py b/tests/components/knx/test_config_flow.py index 9d6eaf68c54..55f7b2a8891 100644 --- a/tests/components/knx/test_config_flow.py +++ b/tests/components/knx/test_config_flow.py @@ -1,8 +1,8 @@ """Test the KNX config flow.""" -from unittest.mock import patch +from unittest.mock import Mock, patch import pytest -from xknx.exceptions.exception import InvalidSecureConfiguration +from xknx.exceptions.exception import CommunicationError, InvalidSecureConfiguration from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT from xknx.io.gateway_scanner import GatewayDescriptor @@ -441,7 +441,11 @@ async def test_routing_secure_keyfile( return_value=GatewayScannerMock(), ) async def test_tunneling_setup_manual( - gateway_scanner_mock, hass: HomeAssistant, knx_setup, user_input, config_entry_data + _gateway_scanner_mock, + hass: HomeAssistant, + knx_setup, + user_input, + config_entry_data, ) -> None: """Test tunneling if no gateway was found found (or `manual` option was chosen).""" result = await hass.config_entries.flow.async_init( @@ -460,11 +464,21 @@ async def test_tunneling_setup_manual( assert result2["step_id"] == "manual_tunnel" assert result2["errors"] == {"base": "no_tunnel_discovered"} - result3 = await hass.config_entries.flow.async_configure( - result2["flow_id"], - user_input, - ) - await hass.async_block_till_done() + with patch( + "homeassistant.components.knx.config_flow.request_description", + return_value=_gateway_descriptor( + user_input[CONF_HOST], + user_input[CONF_PORT], + supports_tunnelling_tcp=( + user_input[CONF_KNX_TUNNELING_TYPE] == CONF_KNX_TUNNELING_TCP + ), + ), + ): + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + user_input, + ) + await hass.async_block_till_done() assert result3["type"] == FlowResultType.CREATE_ENTRY assert result3["title"] == "Tunneling @ 192.168.0.1" assert result3["data"] == config_entry_data @@ -475,8 +489,146 @@ async def test_tunneling_setup_manual( "homeassistant.components.knx.config_flow.GatewayScanner", return_value=GatewayScannerMock(), ) +async def test_tunneling_setup_manual_request_description_error( + _gateway_scanner_mock, + hass: HomeAssistant, + knx_setup, +) -> None: + """Test tunneling if no gateway was found found (or `manual` option was chosen).""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING, + }, + ) + assert result["step_id"] == "manual_tunnel" + assert result["errors"] == {"base": "no_tunnel_discovered"} + + # TCP configured but not supported by gateway + with patch( + "homeassistant.components.knx.config_flow.request_description", + return_value=_gateway_descriptor( + "192.168.0.1", + 3671, + supports_tunnelling_tcp=False, + ), + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING_TCP, + CONF_HOST: "192.168.0.1", + CONF_PORT: 3671, + }, + ) + assert result["step_id"] == "manual_tunnel" + assert result["errors"] == { + "base": "no_tunnel_discovered", + "tunneling_type": "unsupported_tunnel_type", + } + # TCP configured but Secure required by gateway + with patch( + "homeassistant.components.knx.config_flow.request_description", + return_value=_gateway_descriptor( + "192.168.0.1", + 3671, + supports_tunnelling_tcp=True, + requires_secure=True, + ), + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING_TCP, + CONF_HOST: "192.168.0.1", + CONF_PORT: 3671, + }, + ) + assert result["step_id"] == "manual_tunnel" + assert result["errors"] == { + "base": "no_tunnel_discovered", + "tunneling_type": "unsupported_tunnel_type", + } + # Secure configured but not enabled on gateway + with patch( + "homeassistant.components.knx.config_flow.request_description", + return_value=_gateway_descriptor( + "192.168.0.1", + 3671, + supports_tunnelling_tcp=True, + requires_secure=False, + ), + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, + CONF_HOST: "192.168.0.1", + CONF_PORT: 3671, + }, + ) + assert result["step_id"] == "manual_tunnel" + assert result["errors"] == { + "base": "no_tunnel_discovered", + "tunneling_type": "unsupported_tunnel_type", + } + # No connection to gateway + with patch( + "homeassistant.components.knx.config_flow.request_description", + side_effect=CommunicationError(""), + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING_TCP, + CONF_HOST: "192.168.0.1", + CONF_PORT: 3671, + }, + ) + assert result["step_id"] == "manual_tunnel" + assert result["errors"] == {"base": "cannot_connect"} + # OK configuration + with patch( + "homeassistant.components.knx.config_flow.request_description", + return_value=_gateway_descriptor( + "192.168.0.1", + 3671, + supports_tunnelling_tcp=True, + ), + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KNX_TUNNELING_TYPE: CONF_KNX_TUNNELING_TCP, + CONF_HOST: "192.168.0.1", + CONF_PORT: 3671, + }, + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "Tunneling @ 192.168.0.1" + assert result["data"] == { + **DEFAULT_ENTRY_DATA, + CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP, + CONF_HOST: "192.168.0.1", + CONF_PORT: 3671, + } + knx_setup.assert_called_once() + + +@patch( + "homeassistant.components.knx.config_flow.GatewayScanner", + return_value=GatewayScannerMock(), +) +@patch( + "homeassistant.components.knx.config_flow.request_description", + return_value=_gateway_descriptor("192.168.0.2", 3675), +) async def test_tunneling_setup_for_local_ip( - gateway_scanner_mock, hass: HomeAssistant, knx_setup + _request_description_mock, _gateway_scanner_mock, hass: HomeAssistant, knx_setup ) -> None: """Test tunneling if only one gateway is found.""" result = await hass.config_entries.flow.async_init( @@ -715,7 +867,17 @@ async def _get_menu_step(hass: HomeAssistant) -> FlowResult: return result3 +@patch( + "homeassistant.components.knx.config_flow.request_description", + return_value=_gateway_descriptor( + "192.168.0.1", + 3675, + supports_tunnelling_tcp=True, + requires_secure=True, + ), +) async def test_get_secure_menu_step_manual_tunnelling( + _request_description_mock, hass: HomeAssistant, ): """Test flow reaches secure_tunnellinn menu step from manual tunnelling configuration.""" @@ -908,6 +1070,7 @@ async def test_options_flow_connection_type( gateway = _gateway_descriptor("192.168.0.1", 3675) await hass.config_entries.async_setup(mock_config_entry.entry_id) + hass.data[DOMAIN] = Mock() # GatewayScanner uses running XKNX() in options flow menu_step = await hass.config_entries.options.async_init(mock_config_entry.entry_id) with patch( From a7993e06402f6667c5b67952d3d03213e0cdcda3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Thu, 1 Dec 2022 09:08:39 +0100 Subject: [PATCH 0966/1033] Bump hass-nabucasa from 0.56.0 to 0.59.0 (#82987) * Bump hass-nabucasa from 0.56.0 to 0.58.0 * 0.59.0 --- homeassistant/components/cloud/__init__.py | 38 ++++++++++--------- homeassistant/components/cloud/const.py | 20 +++++----- homeassistant/components/cloud/manifest.json | 2 +- .../components/cloud/system_health.py | 5 +-- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/cloud/test_alexa_config.py | 12 +++--- tests/components/cloud/test_http_api.py | 6 +-- tests/components/cloud/test_init.py | 33 ++++++++-------- tests/components/cloud/test_system_health.py | 6 +-- 11 files changed, 64 insertions(+), 64 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 0662487b765..c5918dcf28f 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -36,22 +36,23 @@ from homeassistant.util.aiohttp import MockRequest from . import account_link, http_api from .client import CloudClient from .const import ( - CONF_ACCOUNT_LINK_URL, - CONF_ACME_DIRECTORY_SERVER, + CONF_ACCOUNT_LINK_SERVER, + CONF_ACCOUNTS_SERVER, + CONF_ACME_SERVER, CONF_ALEXA, - CONF_ALEXA_ACCESS_TOKEN_URL, + CONF_ALEXA_SERVER, CONF_ALIASES, - CONF_CLOUDHOOK_CREATE_URL, + CONF_CLOUDHOOK_SERVER, CONF_COGNITO_CLIENT_ID, CONF_ENTITY_CONFIG, CONF_FILTER, CONF_GOOGLE_ACTIONS, - CONF_GOOGLE_ACTIONS_REPORT_STATE_URL, - CONF_RELAYER, - CONF_REMOTE_API_URL, - CONF_SUBSCRIPTION_INFO_URL, + CONF_RELAYER_SERVER, + CONF_REMOTE_SNI_SERVER, + CONF_REMOTESTATE_SERVER, + CONF_THINGTALK_SERVER, CONF_USER_POOL_ID, - CONF_VOICE_API_URL, + CONF_VOICE_SERVER, DOMAIN, MODE_DEV, MODE_PROD, @@ -107,17 +108,18 @@ CONFIG_SCHEMA = vol.Schema( vol.Optional(CONF_COGNITO_CLIENT_ID): str, vol.Optional(CONF_USER_POOL_ID): str, vol.Optional(CONF_REGION): str, - vol.Optional(CONF_RELAYER): str, - vol.Optional(CONF_SUBSCRIPTION_INFO_URL): vol.Url(), - vol.Optional(CONF_CLOUDHOOK_CREATE_URL): vol.Url(), - vol.Optional(CONF_REMOTE_API_URL): vol.Url(), - vol.Optional(CONF_ACME_DIRECTORY_SERVER): vol.Url(), vol.Optional(CONF_ALEXA): ALEXA_SCHEMA, vol.Optional(CONF_GOOGLE_ACTIONS): GACTIONS_SCHEMA, - vol.Optional(CONF_ALEXA_ACCESS_TOKEN_URL): vol.Url(), - vol.Optional(CONF_GOOGLE_ACTIONS_REPORT_STATE_URL): vol.Url(), - vol.Optional(CONF_ACCOUNT_LINK_URL): vol.Url(), - vol.Optional(CONF_VOICE_API_URL): vol.Url(), + vol.Optional(CONF_ACCOUNT_LINK_SERVER): str, + vol.Optional(CONF_ACCOUNTS_SERVER): str, + vol.Optional(CONF_ACME_SERVER): str, + vol.Optional(CONF_ALEXA_SERVER): str, + vol.Optional(CONF_CLOUDHOOK_SERVER): str, + vol.Optional(CONF_RELAYER_SERVER): str, + vol.Optional(CONF_REMOTE_SNI_SERVER): str, + vol.Optional(CONF_REMOTESTATE_SERVER): str, + vol.Optional(CONF_THINGTALK_SERVER): str, + vol.Optional(CONF_VOICE_SERVER): str, } ) }, diff --git a/homeassistant/components/cloud/const.py b/homeassistant/components/cloud/const.py index ea0240acccf..9fb4ffc7047 100644 --- a/homeassistant/components/cloud/const.py +++ b/homeassistant/components/cloud/const.py @@ -47,16 +47,18 @@ CONF_COGNITO_CLIENT_ID = "cognito_client_id" CONF_ENTITY_CONFIG = "entity_config" CONF_FILTER = "filter" CONF_GOOGLE_ACTIONS = "google_actions" -CONF_RELAYER = "relayer" CONF_USER_POOL_ID = "user_pool_id" -CONF_SUBSCRIPTION_INFO_URL = "subscription_info_url" -CONF_CLOUDHOOK_CREATE_URL = "cloudhook_create_url" -CONF_REMOTE_API_URL = "remote_api_url" -CONF_ACME_DIRECTORY_SERVER = "acme_directory_server" -CONF_ALEXA_ACCESS_TOKEN_URL = "alexa_access_token_url" -CONF_GOOGLE_ACTIONS_REPORT_STATE_URL = "google_actions_report_state_url" -CONF_ACCOUNT_LINK_URL = "account_link_url" -CONF_VOICE_API_URL = "voice_api_url" + +CONF_ACCOUNT_LINK_SERVER = "account_link_server" +CONF_ACCOUNTS_SERVER = "accounts_server" +CONF_ACME_SERVER = "acme_server" +CONF_ALEXA_SERVER = "alexa_server" +CONF_CLOUDHOOK_SERVER = "cloudhook_server" +CONF_RELAYER_SERVER = "relayer_server" +CONF_REMOTE_SNI_SERVER = "remote_sni_server" +CONF_REMOTESTATE_SERVER = "remotestate_server" +CONF_THINGTALK_SERVER = "thingtalk_server" +CONF_VOICE_SERVER = "voice_server" MODE_DEV = "development" MODE_PROD = "production" diff --git a/homeassistant/components/cloud/manifest.json b/homeassistant/components/cloud/manifest.json index 97f581d3bf0..23d72f7cb03 100644 --- a/homeassistant/components/cloud/manifest.json +++ b/homeassistant/components/cloud/manifest.json @@ -2,7 +2,7 @@ "domain": "cloud", "name": "Home Assistant Cloud", "documentation": "https://www.home-assistant.io/integrations/cloud", - "requirements": ["hass-nabucasa==0.56.0"], + "requirements": ["hass-nabucasa==0.59.0"], "dependencies": ["http", "webhook"], "after_dependencies": ["google_assistant", "alexa"], "codeowners": ["@home-assistant/cloud"], diff --git a/homeassistant/components/cloud/system_health.py b/homeassistant/components/cloud/system_health.py index 4d8a6eab64c..9f836114b3e 100644 --- a/homeassistant/components/cloud/system_health.py +++ b/homeassistant/components/cloud/system_health.py @@ -1,6 +1,5 @@ """Provide info to system health.""" from hass_nabucasa import Cloud -from yarl import URL from homeassistant.components import system_health from homeassistant.core import HomeAssistant, callback @@ -36,14 +35,14 @@ async def system_health_info(hass): data["remote_server"] = cloud.remote.snitun_server data["can_reach_cert_server"] = system_health.async_check_can_reach_url( - hass, cloud.acme_directory_server + hass, f"https://{cloud.acme_server}/directory" ) data["can_reach_cloud_auth"] = system_health.async_check_can_reach_url( hass, f"https://cognito-idp.{cloud.region}.amazonaws.com/{cloud.user_pool_id}/.well-known/jwks.json", ) data["can_reach_cloud"] = system_health.async_check_can_reach_url( - hass, URL(cloud.relayer).with_scheme("https").with_path("/status") + hass, f"https://{cloud.relayer_server}/status" ) return data diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index f41444de531..d281d0cedcd 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -20,7 +20,7 @@ ciso8601==2.2.0 cryptography==38.0.3 dbus-fast==1.75.0 fnvhash==0.1.0 -hass-nabucasa==0.56.0 +hass-nabucasa==0.59.0 home-assistant-bluetooth==1.8.1 home-assistant-frontend==20221201.1 httpx==0.23.1 diff --git a/requirements_all.txt b/requirements_all.txt index 42b8b05c137..e225bfdc68e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -848,7 +848,7 @@ ha-philipsjs==2.9.0 habitipy==0.2.0 # homeassistant.components.cloud -hass-nabucasa==0.56.0 +hass-nabucasa==0.59.0 # homeassistant.components.splunk hass_splunk==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 273cc72c140..00001f1c723 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -640,7 +640,7 @@ ha-philipsjs==2.9.0 habitipy==0.2.0 # homeassistant.components.cloud -hass-nabucasa==0.56.0 +hass-nabucasa==0.59.0 # homeassistant.components.tasmota hatasmota==0.6.1 diff --git a/tests/components/cloud/test_alexa_config.py b/tests/components/cloud/test_alexa_config.py index 4e0df3c8ee3..b7637780b12 100644 --- a/tests/components/cloud/test_alexa_config.py +++ b/tests/components/cloud/test_alexa_config.py @@ -129,7 +129,7 @@ async def test_alexa_config_report_state(hass, cloud_prefs, cloud_stub): async def test_alexa_config_invalidate_token(hass, cloud_prefs, aioclient_mock): """Test Alexa config should expose using prefs.""" aioclient_mock.post( - "http://example/alexa_token", + "https://example/access_token", json={ "access_token": "mock-token", "event_endpoint": "http://example.com/alexa_endpoint", @@ -142,7 +142,7 @@ async def test_alexa_config_invalidate_token(hass, cloud_prefs, aioclient_mock): "mock-user-id", cloud_prefs, Mock( - alexa_access_token_url="http://example/alexa_token", + alexa_server="example", auth=Mock(async_check_token=AsyncMock()), websession=async_get_clientsession(hass), ), @@ -181,7 +181,7 @@ async def test_alexa_config_fail_refresh_token( """Test Alexa config failing to refresh token.""" aioclient_mock.post( - "http://example/alexa_token", + "https://example/access_token", json={ "access_token": "mock-token", "event_endpoint": "http://example.com/alexa_endpoint", @@ -198,7 +198,7 @@ async def test_alexa_config_fail_refresh_token( "mock-user-id", cloud_prefs, Mock( - alexa_access_token_url="http://example/alexa_token", + alexa_server="example", auth=Mock(async_check_token=AsyncMock()), websession=async_get_clientsession(hass), ), @@ -228,7 +228,7 @@ async def test_alexa_config_fail_refresh_token( conf.async_invalidate_access_token() aioclient_mock.clear_requests() aioclient_mock.post( - "http://example/alexa_token", + "https://example/access_token", json={"reason": reject_reason}, status=400, ) @@ -254,7 +254,7 @@ async def test_alexa_config_fail_refresh_token( # State reporting should now be re-enabled for Alexa aioclient_mock.clear_requests() aioclient_mock.post( - "http://example/alexa_token", + "https://example/access_token", json={ "access_token": "mock-token", "event_endpoint": "http://example.com/alexa_endpoint", diff --git a/tests/components/cloud/test_http_api.py b/tests/components/cloud/test_http_api.py index 1f9af960f08..0dbc20d4f91 100644 --- a/tests/components/cloud/test_http_api.py +++ b/tests/components/cloud/test_http_api.py @@ -21,7 +21,7 @@ from . import mock_cloud, mock_cloud_prefs from tests.components.google_assistant import MockConfig -SUBSCRIPTION_INFO_URL = "https://api-test.hass.io/subscription_info" +SUBSCRIPTION_INFO_URL = "https://api-test.hass.io/payments/subscription_info" @pytest.fixture(name="mock_cloud_login") @@ -48,8 +48,8 @@ def setup_api_fixture(hass, aioclient_mock): "cognito_client_id": "cognito_client_id", "user_pool_id": "user_pool_id", "region": "region", - "relayer": "relayer", - "subscription_info_url": SUBSCRIPTION_INFO_URL, + "relayer_server": "relayer", + "accounts_server": "api-test.hass.io", "google_actions": {"filter": {"include_domains": "light"}}, "alexa": { "filter": {"include_entities": ["light.kitchen", "switch.ac"]} diff --git a/tests/components/cloud/test_init.py b/tests/components/cloud/test_init.py index 78a8f83eef6..48caf27dfe2 100644 --- a/tests/components/cloud/test_init.py +++ b/tests/components/cloud/test_init.py @@ -26,13 +26,13 @@ async def test_constructor_loads_info_from_config(hass): "cognito_client_id": "test-cognito_client_id", "user_pool_id": "test-user_pool_id", "region": "test-region", - "relayer": "test-relayer", - "subscription_info_url": "http://test-subscription-info-url", - "cloudhook_create_url": "http://test-cloudhook_create_url", - "remote_api_url": "http://test-remote_api_url", - "alexa_access_token_url": "http://test-alexa-token-url", - "acme_directory_server": "http://test-acme-directory-server", - "google_actions_report_state_url": "http://test-google-actions-report-state-url", + "relayer_server": "test-relayer-server", + "accounts_server": "test-acounts-server", + "cloudhook_server": "test-cloudhook-server", + "remote_sni_server": "test-remote-sni-server", + "alexa_server": "test-alexa-server", + "acme_server": "test-acme-server", + "remotestate_server": "test-remotestate-server", }, }, ) @@ -43,16 +43,13 @@ async def test_constructor_loads_info_from_config(hass): assert cl.cognito_client_id == "test-cognito_client_id" assert cl.user_pool_id == "test-user_pool_id" assert cl.region == "test-region" - assert cl.relayer == "test-relayer" - assert cl.subscription_info_url == "http://test-subscription-info-url" - assert cl.cloudhook_create_url == "http://test-cloudhook_create_url" - assert cl.remote_api_url == "http://test-remote_api_url" - assert cl.alexa_access_token_url == "http://test-alexa-token-url" - assert cl.acme_directory_server == "http://test-acme-directory-server" - assert ( - cl.google_actions_report_state_url - == "http://test-google-actions-report-state-url" - ) + assert cl.relayer_server == "test-relayer-server" + assert cl.iot.ws_server_url == "wss://test-relayer-server/websocket" + assert cl.accounts_server == "test-acounts-server" + assert cl.cloudhook_server == "test-cloudhook-server" + assert cl.alexa_server == "test-alexa-server" + assert cl.acme_server == "test-acme-server" + assert cl.remotestate_server == "test-remotestate-server" async def test_remote_services(hass, mock_cloud_fixture, hass_read_only_user): @@ -120,7 +117,7 @@ async def test_setup_existing_cloud_user(hass, hass_storage): "cognito_client_id": "test-cognito_client_id", "user_pool_id": "test-user_pool_id", "region": "test-region", - "relayer": "test-relayer", + "relayer_server": "test-relayer-serer", }, }, ) diff --git a/tests/components/cloud/test_system_health.py b/tests/components/cloud/test_system_health.py index cc37788bc4c..dae5ac4b4cc 100644 --- a/tests/components/cloud/test_system_health.py +++ b/tests/components/cloud/test_system_health.py @@ -13,7 +13,7 @@ from tests.common import get_system_health_info async def test_cloud_system_health(hass, aioclient_mock): """Test cloud system health.""" aioclient_mock.get("https://cloud.bla.com/status", text="") - aioclient_mock.get("https://cert-server", text="") + aioclient_mock.get("https://cert-server/directory", text="") aioclient_mock.get( "https://cognito-idp.us-east-1.amazonaws.com/AAAA/.well-known/jwks.json", exc=ClientError, @@ -25,8 +25,8 @@ async def test_cloud_system_health(hass, aioclient_mock): hass.data["cloud"] = Mock( region="us-east-1", user_pool_id="AAAA", - relayer="wss://cloud.bla.com/websocket_api", - acme_directory_server="https://cert-server", + relayer_server="cloud.bla.com", + acme_server="cert-server", is_logged_in=True, remote=Mock(is_connected=False, snitun_server="us-west-1"), expiration_date=now, From bb827a60ed27fd74797bd1f5276f2653d7bd3d57 Mon Sep 17 00:00:00 2001 From: Olen Date: Sat, 3 Dec 2022 19:23:29 +0100 Subject: [PATCH 0967/1033] Support older twinkly devices without effects (#83145) fixes undefined --- homeassistant/components/twinkly/const.py | 4 + homeassistant/components/twinkly/light.py | 96 ++++++++++----- tests/components/twinkly/__init__.py | 18 ++- tests/components/twinkly/test_light.py | 135 +++++++++++++++++++--- 4 files changed, 204 insertions(+), 49 deletions(-) diff --git a/homeassistant/components/twinkly/const.py b/homeassistant/components/twinkly/const.py index e48ff165c67..d0d905a5752 100644 --- a/homeassistant/components/twinkly/const.py +++ b/homeassistant/components/twinkly/const.py @@ -9,6 +9,7 @@ CONF_NAME = "name" # Strongly named HA attributes keys ATTR_HOST = "host" +ATTR_VERSION = "version" # Keys of attributes read from the get_device_info DEV_ID = "uuid" @@ -27,3 +28,6 @@ HIDDEN_DEV_VALUES = ( "copyright", # We should not display a copyright "LEDWORKS 2018" in the Home-Assistant UI "mac", # Does not report the actual device mac address ) + +# Minimum version required to support effects +MIN_EFFECT_VERSION = "2.7.1" diff --git a/homeassistant/components/twinkly/light.py b/homeassistant/components/twinkly/light.py index 8f8e8218d87..c7cf13b6563 100644 --- a/homeassistant/components/twinkly/light.py +++ b/homeassistant/components/twinkly/light.py @@ -7,6 +7,7 @@ import logging from typing import Any from aiohttp import ClientError +from packaging import version from ttls.client import Twinkly from homeassistant.components.light import ( @@ -25,6 +26,7 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( + ATTR_VERSION, CONF_HOST, CONF_ID, CONF_NAME, @@ -37,6 +39,7 @@ from .const import ( DEV_PROFILE_RGBW, DOMAIN, HIDDEN_DEV_VALUES, + MIN_EFFECT_VERSION, ) _LOGGER = logging.getLogger(__name__) @@ -96,6 +99,9 @@ class TwinklyLight(LightEntity): self._attributes: dict[Any, Any] = {} self._current_movie: dict[Any, Any] = {} self._movies: list[Any] = [] + self._software_version = "" + # We guess that most devices are "new" and support effects + self._attr_supported_features = LightEntityFeature.EFFECT @property def available(self) -> bool: @@ -130,13 +136,9 @@ class TwinklyLight(LightEntity): manufacturer="LEDWORKS", model=self.model, name=self.name, + sw_version=self._software_version, ) - @property - def supported_features(self) -> LightEntityFeature: - """Return supported features.""" - return LightEntityFeature.EFFECT - @property def is_on(self) -> bool: """Return true if light is on.""" @@ -165,6 +167,19 @@ class TwinklyLight(LightEntity): effect_list.append(f"{movie['id']} {movie['name']}") return effect_list + async def async_added_to_hass(self) -> None: + """Device is added to hass.""" + software_version = await self._client.get_firmware_version() + if ATTR_VERSION in software_version: + self._software_version = software_version[ATTR_VERSION] + + if version.parse(self._software_version) < version.parse( + MIN_EFFECT_VERSION + ): + self._attr_supported_features = ( + self.supported_features & ~LightEntityFeature.EFFECT + ) + async def async_turn_on(self, **kwargs: Any) -> None: """Turn device on.""" if ATTR_BRIGHTNESS in kwargs: @@ -178,36 +193,54 @@ class TwinklyLight(LightEntity): await self._client.set_brightness(brightness) - if ATTR_RGBW_COLOR in kwargs: - if kwargs[ATTR_RGBW_COLOR] != self._attr_rgbw_color: - self._attr_rgbw_color = kwargs[ATTR_RGBW_COLOR] + if ( + ATTR_RGBW_COLOR in kwargs + and kwargs[ATTR_RGBW_COLOR] != self._attr_rgbw_color + ): - if isinstance(self._attr_rgbw_color, tuple): - - await self._client.interview() - # Static color only supports rgb - await self._client.set_static_colour( - ( - self._attr_rgbw_color[0], - self._attr_rgbw_color[1], - self._attr_rgbw_color[2], - ) + await self._client.interview() + if LightEntityFeature.EFFECT & self.supported_features: + # Static color only supports rgb + await self._client.set_static_colour( + ( + kwargs[ATTR_RGBW_COLOR][0], + kwargs[ATTR_RGBW_COLOR][1], + kwargs[ATTR_RGBW_COLOR][2], ) - await self._client.set_mode("color") - self._client.default_mode = "color" + ) + await self._client.set_mode("color") + self._client.default_mode = "color" + else: + await self._client.set_cycle_colours( + ( + kwargs[ATTR_RGBW_COLOR][3], + kwargs[ATTR_RGBW_COLOR][0], + kwargs[ATTR_RGBW_COLOR][1], + kwargs[ATTR_RGBW_COLOR][2], + ) + ) + await self._client.set_mode("movie") + self._client.default_mode = "movie" + self._attr_rgbw_color = kwargs[ATTR_RGBW_COLOR] - if ATTR_RGB_COLOR in kwargs: - if kwargs[ATTR_RGB_COLOR] != self._attr_rgb_color: - self._attr_rgb_color = kwargs[ATTR_RGB_COLOR] + if ATTR_RGB_COLOR in kwargs and kwargs[ATTR_RGB_COLOR] != self._attr_rgb_color: - if isinstance(self._attr_rgb_color, tuple): + await self._client.interview() + if LightEntityFeature.EFFECT & self.supported_features: + await self._client.set_static_colour(kwargs[ATTR_RGB_COLOR]) + await self._client.set_mode("color") + self._client.default_mode = "color" + else: + await self._client.set_cycle_colours(kwargs[ATTR_RGB_COLOR]) + await self._client.set_mode("movie") + self._client.default_mode = "movie" - await self._client.interview() - await self._client.set_static_colour(self._attr_rgb_color) - await self._client.set_mode("color") - self._client.default_mode = "color" + self._attr_rgb_color = kwargs[ATTR_RGB_COLOR] - if ATTR_EFFECT in kwargs: + if ( + ATTR_EFFECT in kwargs + and LightEntityFeature.EFFECT & self.supported_features + ): movie_id = kwargs[ATTR_EFFECT].split(" ")[0] if "id" not in self._current_movie or int(movie_id) != int( self._current_movie["id"] @@ -268,8 +301,9 @@ class TwinklyLight(LightEntity): if key not in HIDDEN_DEV_VALUES: self._attributes[key] = value - await self.async_update_movies() - await self.async_update_current_movie() + if LightEntityFeature.EFFECT & self.supported_features: + await self.async_update_movies() + await self.async_update_current_movie() if not self._is_available: _LOGGER.info("Twinkly '%s' is now available", self._client.host) diff --git a/tests/components/twinkly/__init__.py b/tests/components/twinkly/__init__.py index 351f5509aa2..31d1eff2a61 100644 --- a/tests/components/twinkly/__init__.py +++ b/tests/components/twinkly/__init__.py @@ -25,6 +25,8 @@ class ClientMock: self.movies = [{"id": 1, "name": "Rainbow"}, {"id": 2, "name": "Flare"}] self.current_movie = {} self.default_mode = "movie" + self.mode = None + self.version = "2.8.10" self.id = str(uuid4()) self.device_info = { @@ -55,6 +57,7 @@ class ClientMock: if self.is_offline: raise ClientConnectionError() self.state = True + self.mode = self.default_mode async def turn_off(self) -> None: """Set the mocked off state.""" @@ -81,6 +84,12 @@ class ClientMock: async def set_static_colour(self, colour) -> None: """Set static color.""" self.color = colour + self.default_mode = "color" + + async def set_cycle_colours(self, colour) -> None: + """Set static color.""" + self.color = colour + self.default_mode = "movie" async def interview(self) -> None: """Interview.""" @@ -100,6 +109,11 @@ class ClientMock: async def set_mode(self, mode: str) -> None: """Set mode.""" if mode == "off": - self.turn_off + await self.turn_off() else: - self.turn_on + await self.turn_on() + self.mode = mode + + async def get_firmware_version(self) -> dict: + """Get firmware version.""" + return {"version": self.version} diff --git a/tests/components/twinkly/test_light.py b/tests/components/twinkly/test_light.py index f6fe9e297f6..53e589c564b 100644 --- a/tests/components/twinkly/test_light.py +++ b/tests/components/twinkly/test_light.py @@ -3,7 +3,7 @@ from __future__ import annotations from unittest.mock import patch -from homeassistant.components.light import ATTR_BRIGHTNESS +from homeassistant.components.light import ATTR_BRIGHTNESS, LightEntityFeature from homeassistant.components.twinkly.const import ( CONF_HOST, CONF_ID, @@ -55,9 +55,8 @@ async def test_turn_on_off(hass: HomeAssistant): assert hass.states.get(entity.entity_id).state == "off" await hass.services.async_call( - "light", "turn_on", service_data={"entity_id": entity.entity_id} + "light", "turn_on", service_data={"entity_id": entity.entity_id}, blocking=True ) - await hass.async_block_till_done() state = hass.states.get(entity.entity_id) @@ -78,8 +77,8 @@ async def test_turn_on_with_brightness(hass: HomeAssistant): "light", "turn_on", service_data={"entity_id": entity.entity_id, "brightness": 255}, + blocking=True, ) - await hass.async_block_till_done() state = hass.states.get(entity.entity_id) @@ -90,8 +89,8 @@ async def test_turn_on_with_brightness(hass: HomeAssistant): "light", "turn_on", service_data={"entity_id": entity.entity_id, "brightness": 1}, + blocking=True, ) - await hass.async_block_till_done() state = hass.states.get(entity.entity_id) @@ -99,7 +98,7 @@ async def test_turn_on_with_brightness(hass: HomeAssistant): async def test_turn_on_with_color_rgbw(hass: HomeAssistant): - """Test support of the light.turn_on service with a brightness parameter.""" + """Test support of the light.turn_on service with a rgbw parameter.""" client = ClientMock() client.state = False client.device_info["led_profile"] = "RGBW" @@ -107,23 +106,28 @@ async def test_turn_on_with_color_rgbw(hass: HomeAssistant): entity, _, _, _ = await _create_entries(hass, client) assert hass.states.get(entity.entity_id).state == "off" + assert ( + LightEntityFeature.EFFECT + & hass.states.get(entity.entity_id).attributes["supported_features"] + ) await hass.services.async_call( "light", "turn_on", service_data={"entity_id": entity.entity_id, "rgbw_color": (128, 64, 32, 0)}, + blocking=True, ) - await hass.async_block_till_done() state = hass.states.get(entity.entity_id) assert state.state == "on" assert client.color == (128, 64, 32) assert client.default_mode == "color" + assert client.mode == "color" async def test_turn_on_with_color_rgb(hass: HomeAssistant): - """Test support of the light.turn_on service with a brightness parameter.""" + """Test support of the light.turn_on service with a rgb parameter.""" client = ClientMock() client.state = False client.device_info["led_profile"] = "RGB" @@ -131,23 +135,28 @@ async def test_turn_on_with_color_rgb(hass: HomeAssistant): entity, _, _, _ = await _create_entries(hass, client) assert hass.states.get(entity.entity_id).state == "off" + assert ( + LightEntityFeature.EFFECT + & hass.states.get(entity.entity_id).attributes["supported_features"] + ) await hass.services.async_call( "light", "turn_on", service_data={"entity_id": entity.entity_id, "rgb_color": (128, 64, 32)}, + blocking=True, ) - await hass.async_block_till_done() state = hass.states.get(entity.entity_id) assert state.state == "on" assert client.color == (128, 64, 32) assert client.default_mode == "color" + assert client.mode == "color" async def test_turn_on_with_effect(hass: HomeAssistant): - """Test support of the light.turn_on service with a brightness parameter.""" + """Test support of the light.turn_on service with effects.""" client = ClientMock() client.state = False client.device_info["led_profile"] = "RGB" @@ -155,20 +164,116 @@ async def test_turn_on_with_effect(hass: HomeAssistant): entity, _, _, _ = await _create_entries(hass, client) assert hass.states.get(entity.entity_id).state == "off" - assert client.current_movie == {} + assert not client.current_movie + assert ( + LightEntityFeature.EFFECT + & hass.states.get(entity.entity_id).attributes["supported_features"] + ) await hass.services.async_call( "light", "turn_on", service_data={"entity_id": entity.entity_id, "effect": "1 Rainbow"}, + blocking=True, ) - await hass.async_block_till_done() state = hass.states.get(entity.entity_id) assert state.state == "on" assert client.current_movie["id"] == 1 assert client.default_mode == "movie" + assert client.mode == "movie" + + +async def test_turn_on_with_color_rgbw_and_missing_effect(hass: HomeAssistant): + """Test support of the light.turn_on service with rgbw color and missing effect support.""" + client = ClientMock() + client.state = False + client.device_info["led_profile"] = "RGBW" + client.brightness = {"mode": "enabled", "value": 255} + client.version = "2.7.0" + entity, _, _, _ = await _create_entries(hass, client) + + assert hass.states.get(entity.entity_id).state == "off" + assert ( + not LightEntityFeature.EFFECT + & hass.states.get(entity.entity_id).attributes["supported_features"] + ) + + await hass.services.async_call( + "light", + "turn_on", + service_data={"entity_id": entity.entity_id, "rgbw_color": (128, 64, 32, 0)}, + blocking=True, + ) + + state = hass.states.get(entity.entity_id) + + assert state.state == "on" + assert client.color == (0, 128, 64, 32) + assert client.mode == "movie" + assert client.default_mode == "movie" + + +async def test_turn_on_with_color_rgb_and_missing_effect(hass: HomeAssistant): + """Test support of the light.turn_on service with rgb color and missing effect support.""" + client = ClientMock() + client.state = False + client.device_info["led_profile"] = "RGB" + client.brightness = {"mode": "enabled", "value": 255} + client.version = "2.7.0" + entity, _, _, _ = await _create_entries(hass, client) + + assert hass.states.get(entity.entity_id).state == "off" + assert ( + not LightEntityFeature.EFFECT + & hass.states.get(entity.entity_id).attributes["supported_features"] + ) + + await hass.services.async_call( + "light", + "turn_on", + service_data={"entity_id": entity.entity_id, "rgb_color": (128, 64, 32)}, + blocking=True, + ) + + state = hass.states.get(entity.entity_id) + + assert state.state == "on" + assert client.color == (128, 64, 32) + assert client.mode == "movie" + assert client.default_mode == "movie" + + +async def test_turn_on_with_effect_missing_effects(hass: HomeAssistant): + """Test support of the light.turn_on service with effect set even if effects are not supported.""" + client = ClientMock() + client.state = False + client.device_info["led_profile"] = "RGB" + client.brightness = {"mode": "enabled", "value": 255} + client.version = "2.7.0" + entity, _, _, _ = await _create_entries(hass, client) + + assert hass.states.get(entity.entity_id).state == "off" + assert not client.current_movie + assert ( + not LightEntityFeature.EFFECT + & hass.states.get(entity.entity_id).attributes["supported_features"] + ) + + await hass.services.async_call( + "light", + "turn_on", + service_data={"entity_id": entity.entity_id, "effect": "1 Rainbow"}, + blocking=True, + ) + + state = hass.states.get(entity.entity_id) + + assert state.state == "on" + assert not client.current_movie + assert client.default_mode == "movie" + assert client.mode == "movie" async def test_turn_off(hass: HomeAssistant): @@ -178,9 +283,8 @@ async def test_turn_off(hass: HomeAssistant): assert hass.states.get(entity.entity_id).state == "on" await hass.services.async_call( - "light", "turn_off", service_data={"entity_id": entity.entity_id} + "light", "turn_off", service_data={"entity_id": entity.entity_id}, blocking=True ) - await hass.async_block_till_done() state = hass.states.get(entity.entity_id) @@ -199,9 +303,8 @@ async def test_update_name(hass: HomeAssistant): client.change_name("new_device_name") await hass.services.async_call( - "light", "turn_off", service_data={"entity_id": entity.entity_id} + "light", "turn_off", service_data={"entity_id": entity.entity_id}, blocking=True ) # We call turn_off which will automatically cause an async_update - await hass.async_block_till_done() state = hass.states.get(entity.entity_id) From 75038d420ca30b8f13cfee4ec2ea62d6be0bec3d Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Sat, 3 Dec 2022 19:06:10 +0100 Subject: [PATCH 0968/1033] Raise UpdateFailed when here_travel_time cannot find_location (#83157) Fixes https://github.com/home-assistant/core/issues/83100 fixes undefined --- .../components/here_travel_time/coordinator.py | 14 ++++++-------- .../components/here_travel_time/sensor.py | 4 ++-- tests/components/here_travel_time/test_sensor.py | 12 ++++++++---- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/here_travel_time/coordinator.py b/homeassistant/components/here_travel_time/coordinator.py index 1d3a4edb6b0..97759510d36 100644 --- a/homeassistant/components/here_travel_time/coordinator.py +++ b/homeassistant/components/here_travel_time/coordinator.py @@ -14,7 +14,7 @@ from homeassistant.const import ATTR_ATTRIBUTION, UnitOfLength from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.location import find_coordinates -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.util import dt from homeassistant.util.unit_conversion import DistanceConverter @@ -215,13 +215,15 @@ def prepare_parameters( def _from_entity_id(entity_id: str) -> list[str]: coordinates = find_coordinates(hass, entity_id) if coordinates is None: - raise InvalidCoordinatesException(f"No coordinates found for {entity_id}") + raise UpdateFailed(f"No coordinates found for {entity_id}") + if coordinates is entity_id: + raise UpdateFailed(f"Could not find entity {entity_id}") try: formatted_coordinates = coordinates.split(",") vol.Schema(cv.gps(formatted_coordinates)) except (AttributeError, vol.ExactSequenceInvalid) as ex: - raise InvalidCoordinatesException( - f"{coordinates} are not valid coordinates" + raise UpdateFailed( + f"{entity_id} does not have valid coordinates: {coordinates}" ) from ex return formatted_coordinates @@ -275,7 +277,3 @@ def next_datetime(simple_time: time) -> datetime: if combined < datetime.now(): combined = combined + timedelta(days=1) return combined - - -class InvalidCoordinatesException(Exception): - """Coordinates for origin or destination are malformed.""" diff --git a/homeassistant/components/here_travel_time/sensor.py b/homeassistant/components/here_travel_time/sensor.py index e432511c486..7e1c93bcfb5 100644 --- a/homeassistant/components/here_travel_time/sensor.py +++ b/homeassistant/components/here_travel_time/sensor.py @@ -25,7 +25,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.start import async_at_start +from homeassistant.helpers.start import async_at_started from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import ( @@ -134,7 +134,7 @@ class HERETravelTimeSensor(CoordinatorEntity, RestoreSensor): async def _update_at_start(_): await self.async_update() - self.async_on_remove(async_at_start(self.hass, _update_at_start)) + self.async_on_remove(async_at_started(self.hass, _update_at_start)) @callback def _handle_coordinator_update(self) -> None: diff --git a/tests/components/here_travel_time/test_sensor.py b/tests/components/here_travel_time/test_sensor.py index 34859956032..144ac063040 100644 --- a/tests/components/here_travel_time/test_sensor.py +++ b/tests/components/here_travel_time/test_sensor.py @@ -330,7 +330,7 @@ async def test_destination_entity_not_found(hass: HomeAssistant, caplog): hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() - assert "device_tracker.test are not valid coordinates" in caplog.text + assert "Could not find entity device_tracker.test" in caplog.text @pytest.mark.usefixtures("valid_response") @@ -356,7 +356,7 @@ async def test_origin_entity_not_found(hass: HomeAssistant, caplog): hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() - assert "device_tracker.test are not valid coordinates" in caplog.text + assert "Could not find entity device_tracker.test" in caplog.text @pytest.mark.usefixtures("valid_response") @@ -386,7 +386,9 @@ async def test_invalid_destination_entity_state(hass: HomeAssistant, caplog): hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() - assert "test_state are not valid coordinates" in caplog.text + assert ( + "device_tracker.test does not have valid coordinates: test_state" in caplog.text + ) @pytest.mark.usefixtures("valid_response") @@ -416,7 +418,9 @@ async def test_invalid_origin_entity_state(hass: HomeAssistant, caplog): hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() - assert "test_state are not valid coordinates" in caplog.text + assert ( + "device_tracker.test does not have valid coordinates: test_state" in caplog.text + ) async def test_route_not_found(hass: HomeAssistant, caplog): From 968a3c4eb227742eaa326188a01063cdb5dfc61b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 3 Dec 2022 00:14:39 -1000 Subject: [PATCH 0969/1033] Bump pySwitchbot to 0.20.7 (#83170) changelog: https://github.com/Danielhiversen/pySwitchbot/compare/0.20.5...0.20.7 --- homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index 274c5784b2f..87d62b09c39 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.20.5"], + "requirements": ["PySwitchbot==0.20.7"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index e225bfdc68e..175de49b5c6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -37,7 +37,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.20.5 +PySwitchbot==0.20.7 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 00001f1c723..ac6220248c8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -33,7 +33,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.20.5 +PySwitchbot==0.20.7 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From 31725df4d5eb7e9d1ba5f69866b51c76871236be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Sat, 3 Dec 2022 12:30:03 +0100 Subject: [PATCH 0970/1033] Update pyTibber to 0.26.3 (#83175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update Tibber lib 0.26.3 Signed-off-by: Daniel Hjelseth Høyer Signed-off-by: Daniel Hjelseth Høyer --- homeassistant/components/tibber/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tibber/manifest.json b/homeassistant/components/tibber/manifest.json index 892b47f39ca..81b3c1274d2 100644 --- a/homeassistant/components/tibber/manifest.json +++ b/homeassistant/components/tibber/manifest.json @@ -3,7 +3,7 @@ "domain": "tibber", "name": "Tibber", "documentation": "https://www.home-assistant.io/integrations/tibber", - "requirements": ["pyTibber==0.26.1"], + "requirements": ["pyTibber==0.26.3"], "codeowners": ["@danielhiversen"], "quality_scale": "silver", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 175de49b5c6..9e3df1f81a9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1432,7 +1432,7 @@ pyRFXtrx==0.30.0 pySwitchmate==0.5.1 # homeassistant.components.tibber -pyTibber==0.26.1 +pyTibber==0.26.3 # homeassistant.components.dlink pyW215==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ac6220248c8..18b77af31e1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1032,7 +1032,7 @@ pyMetno==0.9.0 pyRFXtrx==0.30.0 # homeassistant.components.tibber -pyTibber==0.26.1 +pyTibber==0.26.3 # homeassistant.components.nextbus py_nextbusnext==0.1.5 From b015c5ab0c08ae952a3ae0bd8b54d8871bf81b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sat, 3 Dec 2022 16:19:16 +0100 Subject: [PATCH 0971/1033] Add CLOSED as an expected type (#83180) --- homeassistant/components/websocket_api/http.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/websocket_api/http.py b/homeassistant/components/websocket_api/http.py index 23c8fddd56c..acb5bf48131 100644 --- a/homeassistant/components/websocket_api/http.py +++ b/homeassistant/components/websocket_api/http.py @@ -214,7 +214,7 @@ class WebSocketHandler: disconnect_warn = "Did not receive auth message within 10 seconds" raise Disconnect from err - if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING): + if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSED, WSMsgType.CLOSING): raise Disconnect if msg.type != WSMsgType.TEXT: @@ -238,7 +238,7 @@ class WebSocketHandler: while not wsock.closed: msg = await wsock.receive() - if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING): + if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSED, WSMsgType.CLOSING): break if msg.type != WSMsgType.TEXT: From 1a2f23f110a6f8bab18ea470897c0cb091e1c137 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 3 Dec 2022 08:50:59 -1000 Subject: [PATCH 0972/1033] Fix missing services with esp32 proxies (#83192) --- .../components/bluetooth/manifest.json | 2 +- .../components/bluetooth/wrappers.py | 8 ++- .../components/esphome/bluetooth/client.py | 5 ++ .../components/esphome/entry_data.py | 8 +++ homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/bluetooth/__init__.py | 4 ++ tests/components/bluetooth/test_models.py | 57 +++++++++++++++++++ 9 files changed, 85 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 0ba5768d37c..4f5e1bd1b64 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.2", - "bleak-retry-connector==2.8.9", + "bleak-retry-connector==2.9.0", "bluetooth-adapters==0.11.0", "bluetooth-auto-recovery==0.5.4", "bluetooth-data-tools==0.3.0", diff --git a/homeassistant/components/bluetooth/wrappers.py b/homeassistant/components/bluetooth/wrappers.py index beaa2acc78a..b1b06d43e31 100644 --- a/homeassistant/components/bluetooth/wrappers.py +++ b/homeassistant/components/bluetooth/wrappers.py @@ -12,7 +12,7 @@ from bleak import BleakClient, BleakError from bleak.backends.client import BaseBleakClient, get_platform_client_backend_type from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementDataCallback, BaseBleakScanner -from bleak_retry_connector import NO_RSSI_VALUE, ble_device_description +from bleak_retry_connector import NO_RSSI_VALUE, ble_device_description, clear_cache from homeassistant.core import CALLBACK_TYPE, callback as hass_callback from homeassistant.helpers.frame import report @@ -169,6 +169,12 @@ class HaBleakClientWrapper(BleakClient): """Return True if the client is connected to a device.""" return self._backend is not None and self._backend.is_connected + async def clear_cache(self) -> bool: + """Clear the GATT cache.""" + if self._backend is not None and hasattr(self._backend, "clear_cache"): + return await self._backend.clear_cache() # type: ignore[no-any-return] + return await clear_cache(self.__address) + def set_disconnected_callback( self, callback: Callable[[BleakClient], None] | None, diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index 1ec3b585fae..d4200c24215 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -449,6 +449,11 @@ class ESPHomeClient(BaseBleakClient): raise BleakError(f"Characteristic {char_specifier} was not found!") return characteristic + async def clear_cache(self) -> None: + """Clear the GATT cache.""" + self.entry_data.clear_gatt_services_cache(self._address_as_int) + self.entry_data.clear_gatt_mtu_cache(self._address_as_int) + @verify_connected @api_error_as_bleak_error async def read_gatt_char( diff --git a/homeassistant/components/esphome/entry_data.py b/homeassistant/components/esphome/entry_data.py index 89377ba9a6a..2e05e01309e 100644 --- a/homeassistant/components/esphome/entry_data.py +++ b/homeassistant/components/esphome/entry_data.py @@ -119,6 +119,10 @@ class RuntimeEntryData: """Set the BleakGATTServiceCollection for the given address.""" self._gatt_services_cache[address] = services + def clear_gatt_services_cache(self, address: int) -> None: + """Clear the BleakGATTServiceCollection for the given address.""" + self._gatt_services_cache.pop(address, None) + def get_gatt_mtu_cache(self, address: int) -> int | None: """Get the mtu cache for the given address.""" return self._gatt_mtu_cache.get(address) @@ -127,6 +131,10 @@ class RuntimeEntryData: """Set the mtu cache for the given address.""" self._gatt_mtu_cache[address] = mtu + def clear_gatt_mtu_cache(self, address: int) -> None: + """Clear the mtu cache for the given address.""" + self._gatt_mtu_cache.pop(address, None) + @callback def async_update_ble_connection_limits(self, free: int, limit: int) -> None: """Update the BLE connection limits.""" diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index d281d0cedcd..caed4e23b53 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.8.9 +bleak-retry-connector==2.9.0 bleak==0.19.2 bluetooth-adapters==0.11.0 bluetooth-auto-recovery==0.5.4 diff --git a/requirements_all.txt b/requirements_all.txt index 9e3df1f81a9..0d3d27ede0f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -422,7 +422,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.9 +bleak-retry-connector==2.9.0 # homeassistant.components.bluetooth bleak==0.19.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 18b77af31e1..20a42af4b3d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -346,7 +346,7 @@ bellows==0.34.5 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.8.9 +bleak-retry-connector==2.9.0 # homeassistant.components.bluetooth bleak==0.19.2 diff --git a/tests/components/bluetooth/__init__.py b/tests/components/bluetooth/__init__.py index e4dd2a8bb57..e36d1d4b644 100644 --- a/tests/components/bluetooth/__init__.py +++ b/tests/components/bluetooth/__init__.py @@ -219,3 +219,7 @@ class MockBleakClient(BleakClient): async def get_services(self, *args, **kwargs): """Mock get_services.""" return [] + + async def clear_cache(self, *args, **kwargs): + """Mock clear_cache.""" + return True diff --git a/tests/components/bluetooth/test_models.py b/tests/components/bluetooth/test_models.py index 612f33a68bd..e200450f656 100644 --- a/tests/components/bluetooth/test_models.py +++ b/tests/components/bluetooth/test_models.py @@ -45,6 +45,7 @@ async def test_wrapped_bleak_client_raises_device_missing(hass, enable_bluetooth await client.connect() assert client.is_connected is False await client.disconnect() + assert await client.clear_cache() is False async def test_wrapped_bleak_client_set_disconnected_callback_before_connected( @@ -168,6 +169,62 @@ async def test_ble_device_with_proxy_client_out_of_connections( await client.disconnect() +async def test_ble_device_with_proxy_clear_cache(hass, enable_bluetooth, one_adapter): + """Test we can clear cache on the proxy.""" + manager = _get_manager() + + switchbot_proxy_device_with_connection_slot = BLEDevice( + "44:44:33:11:23:45", + "wohand", + { + "connector": HaBluetoothConnector( + MockBleakClient, "mock_bleak_client", lambda: True + ), + "path": "/org/bluez/hci0/dev_44_44_33_11_23_45", + }, + rssi=-30, + ) + switchbot_adv = generate_advertisement_data( + local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"} + ) + + class FakeScanner(BaseHaScanner): + @property + def discovered_devices_and_advertisement_data( + self, + ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: + """Return a list of discovered devices.""" + return { + switchbot_proxy_device_with_connection_slot.address: ( + switchbot_proxy_device_with_connection_slot, + switchbot_adv, + ) + } + + async def async_get_device_by_address(self, address: str) -> BLEDevice | None: + """Return a list of discovered devices.""" + if address == switchbot_proxy_device_with_connection_slot.address: + return switchbot_adv + return None + + scanner = FakeScanner(hass, "esp32", "esp32") + cancel = manager.async_register_scanner(scanner, True) + inject_advertisement_with_source( + hass, switchbot_proxy_device_with_connection_slot, switchbot_adv, "esp32" + ) + + assert manager.async_discovered_devices(True) == [ + switchbot_proxy_device_with_connection_slot + ] + + client = HaBleakClientWrapper(switchbot_proxy_device_with_connection_slot) + await client.connect() + assert client.is_connected is True + assert await client.clear_cache() is True + await client.disconnect() + cancel() + + async def test_ble_device_with_proxy_client_out_of_connections_uses_best_available( hass, enable_bluetooth, one_adapter ): From 5c918e9053365fcb16ea8476b19deb6e0b77d4c6 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 3 Dec 2022 11:02:25 -0800 Subject: [PATCH 0973/1033] Bump ical to 4.1.2 to fix emoji in local calendar (#83193) Bump ical to 4.1.2 --- homeassistant/components/local_calendar/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/local_calendar/manifest.json b/homeassistant/components/local_calendar/manifest.json index 016d2fee052..037c9ff67bd 100644 --- a/homeassistant/components/local_calendar/manifest.json +++ b/homeassistant/components/local_calendar/manifest.json @@ -3,7 +3,7 @@ "name": "Local Calendar", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/local_calendar", - "requirements": ["ical==4.1.1"], + "requirements": ["ical==4.1.2"], "codeowners": ["@allenporter"], "iot_class": "local_polling", "loggers": ["ical"] diff --git a/requirements_all.txt b/requirements_all.txt index 0d3d27ede0f..42d9704cbc8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -926,7 +926,7 @@ ibm-watson==5.2.2 ibmiotf==0.3.4 # homeassistant.components.local_calendar -ical==4.1.1 +ical==4.1.2 # homeassistant.components.ping icmplib==3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 20a42af4b3d..90c63ecd7e3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -691,7 +691,7 @@ iaqualink==0.5.0 ibeacon_ble==1.0.1 # homeassistant.components.local_calendar -ical==4.1.1 +ical==4.1.2 # homeassistant.components.ping icmplib==3.0 From 284c226cc05dbec80f4d353a0935f0f50e139a63 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 3 Dec 2022 09:34:11 -1000 Subject: [PATCH 0974/1033] Bump aiohomekit to 2.3.6 (#83196) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index d67eeac1fd3..4ac8578eff5 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.3.5"], + "requirements": ["aiohomekit==2.3.6"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index 42d9704cbc8..a1ef6f4dfe6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.5 +aiohomekit==2.3.6 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 90c63ecd7e3..229f7d8e0d5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.5 +aiohomekit==2.3.6 # homeassistant.components.emulated_hue # homeassistant.components.http From c63bb0e1d50675c9a3cfcb12c4249b19dc50edfe Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 3 Dec 2022 09:21:19 -1000 Subject: [PATCH 0975/1033] Bump pySwitchbot to 0.20.8 (#83197) fixes https://github.com/home-assistant/core/issues/80491 --- homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index 87d62b09c39..c96b2884d3b 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.20.7"], + "requirements": ["PySwitchbot==0.20.8"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index a1ef6f4dfe6..4280a19a54f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -37,7 +37,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.20.7 +PySwitchbot==0.20.8 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 229f7d8e0d5..41a05e3acb4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -33,7 +33,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.20.7 +PySwitchbot==0.20.8 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From f19322b1f38ad2950960e74b4f7126cd4de5b7fd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 3 Dec 2022 09:55:53 -1000 Subject: [PATCH 0976/1033] Bump yalexs-ble to 1.10.2 (#83199) --- homeassistant/components/august/manifest.json | 2 +- homeassistant/components/yalexs_ble/manifest.json | 2 +- requirements_all.txt | 4 ++-- requirements_test_all.txt | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index 5470f03eb4c..e8f8736b522 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -2,7 +2,7 @@ "domain": "august", "name": "August", "documentation": "https://www.home-assistant.io/integrations/august", - "requirements": ["yalexs==1.2.6", "yalexs_ble==1.10.0"], + "requirements": ["yalexs==1.2.6", "yalexs_ble==1.10.2"], "codeowners": ["@bdraco"], "dhcp": [ { diff --git a/homeassistant/components/yalexs_ble/manifest.json b/homeassistant/components/yalexs_ble/manifest.json index b54dda37548..ce86f7f7ea1 100644 --- a/homeassistant/components/yalexs_ble/manifest.json +++ b/homeassistant/components/yalexs_ble/manifest.json @@ -3,7 +3,7 @@ "name": "Yale Access Bluetooth", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yalexs_ble", - "requirements": ["yalexs-ble==1.10.0"], + "requirements": ["yalexs-ble==1.10.2"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [ diff --git a/requirements_all.txt b/requirements_all.txt index 4280a19a54f..aa92eb6d87c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2606,13 +2606,13 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.10.0 +yalexs-ble==1.10.2 # homeassistant.components.august yalexs==1.2.6 # homeassistant.components.august -yalexs_ble==1.10.0 +yalexs_ble==1.10.2 # homeassistant.components.yeelight yeelight==0.7.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 41a05e3acb4..9bf0e586502 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1816,13 +1816,13 @@ xmltodict==0.13.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.10.0 +yalexs-ble==1.10.2 # homeassistant.components.august yalexs==1.2.6 # homeassistant.components.august -yalexs_ble==1.10.0 +yalexs_ble==1.10.2 # homeassistant.components.yeelight yeelight==0.7.10 From ccd5783daf96944d582333ff23e5e5f29b07d19b Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sat, 3 Dec 2022 12:20:53 -0700 Subject: [PATCH 0977/1033] Fix SimpliSafe service calls that require a device selector (#83200) fixes undefined --- homeassistant/components/simplisafe/__init__.py | 3 ++- homeassistant/components/simplisafe/services.yaml | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index 27cee778655..7b431368328 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -237,11 +237,12 @@ def _async_get_system_for_service_call( ) is None: raise ValueError("No base station registered for alarm control panel") - [system_id] = [ + [system_id_str] = [ identity[1] for identity in base_station_device_entry.identifiers if identity[0] == DOMAIN ] + system_id = int(system_id_str) for entry_id in base_station_device_entry.config_entries: if (simplisafe := hass.data[DOMAIN].get(entry_id)) is None: diff --git a/homeassistant/components/simplisafe/services.yaml b/homeassistant/components/simplisafe/services.yaml index 6f9cedc77cb..8aeefcf7846 100644 --- a/homeassistant/components/simplisafe/services.yaml +++ b/homeassistant/components/simplisafe/services.yaml @@ -10,7 +10,8 @@ remove_pin: selector: device: integration: simplisafe - model: alarm_control_panel + entity: + domain: alarm_control_panel label_or_pin: name: Label/PIN description: The label/value to remove. @@ -29,7 +30,8 @@ set_pin: selector: device: integration: simplisafe - model: alarm_control_panel + entity: + domain: alarm_control_panel label: name: Label description: The label of the PIN @@ -55,7 +57,8 @@ set_system_properties: selector: device: integration: simplisafe - model: alarm_control_panel + entity: + domain: alarm_control_panel alarm_duration: name: Alarm duration description: The length of a triggered alarm From 7f5ae00d58490e656996fdce8087e991168412ab Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 3 Dec 2022 14:56:41 -0500 Subject: [PATCH 0978/1033] Bumped version to 2022.12.0b3 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 333b3076dfc..bdd8b4640ca 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -8,7 +8,7 @@ from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "0b2" +PATCH_VERSION: Final = "0b3" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/pyproject.toml b/pyproject.toml index 441c4e95e91..239052d5285 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.12.0b2" +version = "2022.12.0b3" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From f396d663861a38b3ee25a7bbe9d0f088ed0b4866 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 3 Dec 2022 21:26:21 +0000 Subject: [PATCH 0979/1033] Update aiolyric to 1.0.9 (#83190) --- homeassistant/components/lyric/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/lyric/manifest.json b/homeassistant/components/lyric/manifest.json index c0d9168f46f..6101101bf70 100644 --- a/homeassistant/components/lyric/manifest.json +++ b/homeassistant/components/lyric/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/lyric", "dependencies": ["application_credentials"], - "requirements": ["aiolyric==1.0.8"], + "requirements": ["aiolyric==1.0.9"], "codeowners": ["@timmo001"], "quality_scale": "silver", "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index aa92eb6d87c..9f0688b3a13 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -208,7 +208,7 @@ aiolivisi==0.0.14 aiolookin==0.1.1 # homeassistant.components.lyric -aiolyric==1.0.8 +aiolyric==1.0.9 # homeassistant.components.modern_forms aiomodernforms==0.1.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9bf0e586502..85db3a8434c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -186,7 +186,7 @@ aiolivisi==0.0.14 aiolookin==0.1.1 # homeassistant.components.lyric -aiolyric==1.0.8 +aiolyric==1.0.9 # homeassistant.components.modern_forms aiomodernforms==0.1.8 From fe170ccd440f27cba79871b2b25cae3b20078ade Mon Sep 17 00:00:00 2001 From: Olen Date: Sat, 3 Dec 2022 22:43:10 +0100 Subject: [PATCH 0980/1033] Switch Twinkly to awesomeversion (#83205) Switch to awesomeversion --- homeassistant/components/twinkly/light.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/twinkly/light.py b/homeassistant/components/twinkly/light.py index c7cf13b6563..3174f60edf8 100644 --- a/homeassistant/components/twinkly/light.py +++ b/homeassistant/components/twinkly/light.py @@ -7,7 +7,7 @@ import logging from typing import Any from aiohttp import ClientError -from packaging import version +from awesomeversion import AwesomeVersion from ttls.client import Twinkly from homeassistant.components.light import ( @@ -173,7 +173,7 @@ class TwinklyLight(LightEntity): if ATTR_VERSION in software_version: self._software_version = software_version[ATTR_VERSION] - if version.parse(self._software_version) < version.parse( + if AwesomeVersion(self._software_version) < AwesomeVersion( MIN_EFFECT_VERSION ): self._attr_supported_features = ( From be94e67ecc4d0761934f3b19d45858821548172b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Dec 2022 07:01:37 -1000 Subject: [PATCH 0981/1033] Restore HomeKit Controller BLE GSN at startup (#83206) --- .../components/homekit_controller/button.py | 2 +- .../homekit_controller/config_flow.py | 1 + .../homekit_controller/connection.py | 70 +++++++++++-------- .../homekit_controller/manifest.json | 2 +- .../components/homekit_controller/number.py | 2 +- .../components/homekit_controller/storage.py | 6 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 8 files changed, 50 insertions(+), 37 deletions(-) diff --git a/homeassistant/components/homekit_controller/button.py b/homeassistant/components/homekit_controller/button.py index 4ce2b425a5e..11e935df455 100644 --- a/homeassistant/components/homekit_controller/button.py +++ b/homeassistant/components/homekit_controller/button.py @@ -84,7 +84,7 @@ async def async_setup_entry( entity.old_unique_id, entity.unique_id, Platform.BUTTON ) - async_add_entities(entities, True) + async_add_entities(entities) return True conn.add_char_factory(async_add_characteristic) diff --git a/homeassistant/components/homekit_controller/config_flow.py b/homeassistant/components/homekit_controller/config_flow.py index 10d3ed0cfa8..1cfedb05847 100644 --- a/homeassistant/components/homekit_controller/config_flow.py +++ b/homeassistant/components/homekit_controller/config_flow.py @@ -583,6 +583,7 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): accessories_state.config_num, accessories_state.accessories.serialize(), serialize_broadcast_key(accessories_state.broadcast_key), + accessories_state.state_num, ) return self.async_create_entry(title=name, data=pairing_data) diff --git a/homeassistant/components/homekit_controller/connection.py b/homeassistant/components/homekit_controller/connection.py index dbcd8b28fc2..d230fe64517 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -20,7 +20,7 @@ from aiohomekit.model.services import Service from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_VIA_DEVICE, EVENT_HOMEASSISTANT_STARTED -from homeassistant.core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback +from homeassistant.core import CoreState, Event, HomeAssistant, callback from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.dispatcher import async_dispatcher_send @@ -116,11 +116,6 @@ class HKDevice: self.pollable_characteristics: list[tuple[int, int]] = [] - # If this is set polling is active and can be disabled by calling - # this method. - self._polling_interval_remover: CALLBACK_TYPE | None = None - self._ble_available_interval_remover: CALLBACK_TYPE | None = None - # Never allow concurrent polling of the same accessory or bridge self._polling_lock = asyncio.Lock() self._polling_lock_warned = False @@ -185,8 +180,8 @@ class HKDevice: self.available = available async_dispatcher_send(self.hass, self.signal_state_updated) - async def _async_retry_populate_ble_accessory_state(self, event: Event) -> None: - """Try again to populate the BLE accessory state. + async def _async_populate_ble_accessory_state(self, event: Event) -> None: + """Populate the BLE accessory state without blocking startup. If the accessory was asleep at startup we need to retry since we continued on to allow startup to proceed. @@ -194,6 +189,7 @@ class HKDevice: If this fails the state may be inconsistent, but will get corrected as soon as the accessory advertises again. """ + self._async_start_polling() try: await self.pairing.async_populate_accessories_state(force_update=True) except STARTUP_EXCEPTIONS as ex: @@ -221,20 +217,28 @@ class HKDevice: # so we only poll those chars but that is not possible # yet. attempts = None if self.hass.state == CoreState.running else 1 - try: - await self.pairing.async_populate_accessories_state( - force_update=True, attempts=attempts - ) - except AccessoryNotFoundError: - if transport != Transport.BLE or not pairing.accessories: - # BLE devices may sleep and we can't force a connection - raise + if ( + transport == Transport.BLE + and pairing.accessories + and pairing.accessories.has_aid(1) + ): + # The GSN gets restored and a catch up poll will be + # triggered via disconnected events automatically + # if we are out of sync. To be sure we are in sync; + # If for some reason the BLE connection failed + # previously we force an update after startup + # is complete. entry.async_on_unload( self.hass.bus.async_listen( EVENT_HOMEASSISTANT_STARTED, - self._async_retry_populate_ble_accessory_state, + self._async_populate_ble_accessory_state, ) ) + else: + await self.pairing.async_populate_accessories_state( + force_update=True, attempts=attempts + ) + self._async_start_polling() entry.async_on_unload(pairing.dispatcher_connect(self.process_new_events)) entry.async_on_unload( @@ -252,27 +256,34 @@ class HKDevice: self.async_set_available_state(self.pairing.is_available) - # We use async_request_update to avoid multiple updates - # at the same time which would generate a spurious warning - # in the log about concurrent polling. - self._polling_interval_remover = async_track_time_interval( - self.hass, self.async_request_update, self.pairing.poll_interval - ) - if transport == Transport.BLE: # If we are using BLE, we need to periodically check of the # BLE device is available since we won't get callbacks # when it goes away since we HomeKit supports disconnected # notifications and we cannot treat a disconnect as unavailability. - self._ble_available_interval_remover = async_track_time_interval( - self.hass, - self.async_update_available_state, - timedelta(seconds=BLE_AVAILABILITY_CHECK_INTERVAL), + entry.async_on_unload( + async_track_time_interval( + self.hass, + self.async_update_available_state, + timedelta(seconds=BLE_AVAILABILITY_CHECK_INTERVAL), + ) ) # BLE devices always get an RSSI sensor as well if "sensor" not in self.platforms: await self.async_load_platform("sensor") + @callback + def _async_start_polling(self) -> None: + """Start polling for updates.""" + # We use async_request_update to avoid multiple updates + # at the same time which would generate a spurious warning + # in the log about concurrent polling. + self.config_entry.async_on_unload( + async_track_time_interval( + self.hass, self.async_request_update, self.pairing.poll_interval + ) + ) + async def async_add_new_entities(self) -> None: """Add new entities to Home Assistant.""" await self.async_load_platforms() @@ -529,9 +540,6 @@ class HKDevice: async def async_unload(self) -> None: """Stop interacting with device and prepare for removal from hass.""" - if self._polling_interval_remover: - self._polling_interval_remover() - await self.pairing.shutdown() await self.hass.config_entries.async_unload_platforms( diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 4ac8578eff5..c5047f9d215 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.3.6"], + "requirements": ["aiohomekit==2.4.0"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/homeassistant/components/homekit_controller/number.py b/homeassistant/components/homekit_controller/number.py index a20ba83e80a..5d72516bc06 100644 --- a/homeassistant/components/homekit_controller/number.py +++ b/homeassistant/components/homekit_controller/number.py @@ -78,7 +78,7 @@ async def async_setup_entry( entity.old_unique_id, entity.unique_id, Platform.NUMBER ) - async_add_entities(entities, True) + async_add_entities(entities) return True conn.add_char_factory(async_add_characteristic) diff --git a/homeassistant/components/homekit_controller/storage.py b/homeassistant/components/homekit_controller/storage.py index a5afb07620a..de4f23ad8da 100644 --- a/homeassistant/components/homekit_controller/storage.py +++ b/homeassistant/components/homekit_controller/storage.py @@ -61,11 +61,15 @@ class EntityMapStorage: config_num: int, accessories: list[Any], broadcast_key: str | None = None, + state_num: int | None = None, ) -> Pairing: """Create a new pairing cache.""" _LOGGER.debug("Creating or updating entity map for %s", homekit_id) data = Pairing( - config_num=config_num, accessories=accessories, broadcast_key=broadcast_key + config_num=config_num, + accessories=accessories, + broadcast_key=broadcast_key, + state_num=state_num, ) self.storage_data[homekit_id] = data self._async_schedule_save() diff --git a/requirements_all.txt b/requirements_all.txt index 9f0688b3a13..a2ce759123a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.6 +aiohomekit==2.4.0 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 85db3a8434c..389a1521d59 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.3.6 +aiohomekit==2.4.0 # homeassistant.components.emulated_hue # homeassistant.components.http From d32c32608b6095699ff1417956ef49a513357acb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Dec 2022 09:05:46 -1000 Subject: [PATCH 0982/1033] Raise an exception when an esp proxy gets empty services during connection so callers know to retry (#83211) --- .../components/esphome/bluetooth/client.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index d4200c24215..541eb831ca5 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -317,7 +317,20 @@ class ESPHomeClient(BaseBleakClient): connected_future.cancel() raise await connected_future - await self.get_services(dangerous_use_bleak_cache=dangerous_use_bleak_cache) + + try: + await self.get_services(dangerous_use_bleak_cache=dangerous_use_bleak_cache) + except asyncio.CancelledError: + # On cancel we must still raise cancelled error + # to avoid blocking the cancellation even if the + # disconnect call fails. + with contextlib.suppress(Exception): + await self.disconnect() + raise + except Exception: + await self.disconnect() + raise + self._disconnected_event = asyncio.Event() return True @@ -427,6 +440,12 @@ class ESPHomeClient(BaseBleakClient): characteristic.handle, ) ) + + if not esphome_services.services: + # If we got no services, we must have disconnected + # or something went wrong on the ESP32's BLE stack. + raise BleakError("Failed to get services from remote esp") + self.services = services _LOGGER.debug( "%s: %s - %s: Cached services saved", From d14324c7922d0d46b7447c55ac37387dcf2bdf92 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Mon, 5 Dec 2022 05:05:53 +1100 Subject: [PATCH 0983/1033] Update async-upnp-client to 0.32.3 (#83215) --- homeassistant/components/dlna_dmr/manifest.json | 2 +- homeassistant/components/dlna_dms/manifest.json | 2 +- homeassistant/components/samsungtv/manifest.json | 2 +- homeassistant/components/ssdp/manifest.json | 2 +- homeassistant/components/upnp/manifest.json | 2 +- homeassistant/components/yeelight/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/dlna_dmr/manifest.json b/homeassistant/components/dlna_dmr/manifest.json index e4620386b98..3da3de8434f 100644 --- a/homeassistant/components/dlna_dmr/manifest.json +++ b/homeassistant/components/dlna_dmr/manifest.json @@ -3,7 +3,7 @@ "name": "DLNA Digital Media Renderer", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dlna_dmr", - "requirements": ["async-upnp-client==0.32.2"], + "requirements": ["async-upnp-client==0.32.3"], "dependencies": ["ssdp"], "after_dependencies": ["media_source"], "ssdp": [ diff --git a/homeassistant/components/dlna_dms/manifest.json b/homeassistant/components/dlna_dms/manifest.json index 7c7a312159b..3630e5ce309 100644 --- a/homeassistant/components/dlna_dms/manifest.json +++ b/homeassistant/components/dlna_dms/manifest.json @@ -3,7 +3,7 @@ "name": "DLNA Digital Media Server", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dlna_dms", - "requirements": ["async-upnp-client==0.32.2"], + "requirements": ["async-upnp-client==0.32.3"], "dependencies": ["ssdp"], "after_dependencies": ["media_source"], "ssdp": [ diff --git a/homeassistant/components/samsungtv/manifest.json b/homeassistant/components/samsungtv/manifest.json index 0a65a47bd23..8a922fbbaf0 100644 --- a/homeassistant/components/samsungtv/manifest.json +++ b/homeassistant/components/samsungtv/manifest.json @@ -8,7 +8,7 @@ "samsungctl[websocket]==0.7.1", "samsungtvws[async,encrypted]==2.5.0", "wakeonlan==2.1.0", - "async-upnp-client==0.32.2" + "async-upnp-client==0.32.3" ], "ssdp": [ { diff --git a/homeassistant/components/ssdp/manifest.json b/homeassistant/components/ssdp/manifest.json index 3b30146e756..d532dc8f292 100644 --- a/homeassistant/components/ssdp/manifest.json +++ b/homeassistant/components/ssdp/manifest.json @@ -2,7 +2,7 @@ "domain": "ssdp", "name": "Simple Service Discovery Protocol (SSDP)", "documentation": "https://www.home-assistant.io/integrations/ssdp", - "requirements": ["async-upnp-client==0.32.2"], + "requirements": ["async-upnp-client==0.32.3"], "dependencies": ["network"], "after_dependencies": ["zeroconf"], "codeowners": [], diff --git a/homeassistant/components/upnp/manifest.json b/homeassistant/components/upnp/manifest.json index 4c45b099193..214f5ce2931 100644 --- a/homeassistant/components/upnp/manifest.json +++ b/homeassistant/components/upnp/manifest.json @@ -3,7 +3,7 @@ "name": "UPnP/IGD", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/upnp", - "requirements": ["async-upnp-client==0.32.2", "getmac==0.8.2"], + "requirements": ["async-upnp-client==0.32.3", "getmac==0.8.2"], "dependencies": ["network", "ssdp"], "codeowners": ["@StevenLooman"], "ssdp": [ diff --git a/homeassistant/components/yeelight/manifest.json b/homeassistant/components/yeelight/manifest.json index 6c450548135..f3d9d8b24ca 100644 --- a/homeassistant/components/yeelight/manifest.json +++ b/homeassistant/components/yeelight/manifest.json @@ -2,7 +2,7 @@ "domain": "yeelight", "name": "Yeelight", "documentation": "https://www.home-assistant.io/integrations/yeelight", - "requirements": ["yeelight==0.7.10", "async-upnp-client==0.32.2"], + "requirements": ["yeelight==0.7.10", "async-upnp-client==0.32.3"], "codeowners": ["@zewelor", "@shenxn", "@starkillerOG", "@alexyao2015"], "config_flow": true, "dependencies": ["network"], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index caed4e23b53..e42e7e2b9aa 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -4,7 +4,7 @@ aiodiscover==1.4.13 aiohttp==3.8.3 aiohttp_cors==0.7.0 astral==2.2 -async-upnp-client==0.32.2 +async-upnp-client==0.32.3 async_timeout==4.0.2 atomicwrites-homeassistant==1.4.1 attrs==21.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index a2ce759123a..a60b3b74aca 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -362,7 +362,7 @@ asterisk_mbox==0.5.0 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.32.2 +async-upnp-client==0.32.3 # homeassistant.components.supla asyncpysupla==0.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 389a1521d59..1215dcbf604 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -316,7 +316,7 @@ arcam-fmj==1.0.1 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.32.2 +async-upnp-client==0.32.3 # homeassistant.components.sleepiq asyncsleepiq==1.2.3 From eb0450ad0c8289f3982a9e028c288bedc08c037d Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@users.noreply.github.com> Date: Sun, 4 Dec 2022 15:52:48 -0500 Subject: [PATCH 0984/1033] Handle numeric versions in mqtt update (#83218) * Handle numeric versions in mqtt update * Remove need for type:ignore Co-authored-by: Jan Bouwhuis Co-authored-by: Jan Bouwhuis --- homeassistant/components/mqtt/update.py | 20 ++++-- tests/components/mqtt/test_update.py | 94 +++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mqtt/update.py b/homeassistant/components/mqtt/update.py index 0875778059f..874f6024a37 100644 --- a/homeassistant/components/mqtt/update.py +++ b/homeassistant/components/mqtt/update.py @@ -172,14 +172,22 @@ class MqttUpdate(MqttEntity, UpdateEntity, RestoreEntity): ) return - json_payload = {} + json_payload: Any | dict = {} try: json_payload = json_loads(payload) - _LOGGER.debug( - "JSON payload detected after processing payload '%s' on topic %s", - json_payload, - msg.topic, - ) + if isinstance(json_payload, dict): + _LOGGER.debug( + "JSON payload detected after processing payload '%s' on topic %s", + json_payload, + msg.topic, + ) + else: + _LOGGER.debug( + "Non-dictionary JSON payload detected after processing payload '%s' on topic %s", + payload, + msg.topic, + ) + json_payload = {"installed_version": payload} except JSON_DECODE_EXCEPTIONS: _LOGGER.debug( "No valid (JSON) payload detected after processing payload '%s' on topic %s", diff --git a/tests/components/mqtt/test_update.py b/tests/components/mqtt/test_update.py index 5746f2ac471..a633b99fb1a 100644 --- a/tests/components/mqtt/test_update.py +++ b/tests/components/mqtt/test_update.py @@ -110,6 +110,54 @@ async def test_run_update_setup(hass, mqtt_mock_entry_with_yaml_config): assert state.attributes.get("latest_version") == "2.0.0" +async def test_run_update_setup_float(hass, mqtt_mock_entry_with_yaml_config): + """Test that it fetches the given payload when the version is parsable as a number.""" + installed_version_topic = "test/installed-version" + latest_version_topic = "test/latest-version" + await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + update.DOMAIN: { + "state_topic": installed_version_topic, + "latest_version_topic": latest_version_topic, + "name": "Test Update", + "release_summary": "Test release summary", + "release_url": "https://example.com/release", + "title": "Test Update Title", + "entity_picture": "https://example.com/icon.png", + } + } + }, + ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + + async_fire_mqtt_message(hass, installed_version_topic, "1.9") + async_fire_mqtt_message(hass, latest_version_topic, "1.9") + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_OFF + assert state.attributes.get("installed_version") == "1.9" + assert state.attributes.get("latest_version") == "1.9" + assert state.attributes.get("release_summary") == "Test release summary" + assert state.attributes.get("release_url") == "https://example.com/release" + assert state.attributes.get("title") == "Test Update Title" + assert state.attributes.get("entity_picture") == "https://example.com/icon.png" + + async_fire_mqtt_message(hass, latest_version_topic, "2.0") + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_ON + assert state.attributes.get("installed_version") == "1.9" + assert state.attributes.get("latest_version") == "2.0" + + async def test_value_template(hass, mqtt_mock_entry_with_yaml_config): """Test that it fetches the given payload with a template.""" installed_version_topic = "test/installed-version" @@ -156,6 +204,52 @@ async def test_value_template(hass, mqtt_mock_entry_with_yaml_config): assert state.attributes.get("latest_version") == "2.0.0" +async def test_value_template_float(hass, mqtt_mock_entry_with_yaml_config): + """Test that it fetches the given payload with a template when the version is parsable as a number.""" + installed_version_topic = "test/installed-version" + latest_version_topic = "test/latest-version" + await async_setup_component( + hass, + mqtt.DOMAIN, + { + mqtt.DOMAIN: { + update.DOMAIN: { + "state_topic": installed_version_topic, + "value_template": "{{ value_json.installed }}", + "latest_version_topic": latest_version_topic, + "latest_version_template": "{{ value_json.latest }}", + "name": "Test Update", + } + } + }, + ) + await hass.async_block_till_done() + await mqtt_mock_entry_with_yaml_config() + + async_fire_mqtt_message(hass, installed_version_topic, '{"installed":"1.9"}') + async_fire_mqtt_message(hass, latest_version_topic, '{"latest":"1.9"}') + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_OFF + assert state.attributes.get("installed_version") == "1.9" + assert state.attributes.get("latest_version") == "1.9" + assert ( + state.attributes.get("entity_picture") + == "https://brands.home-assistant.io/_/mqtt/icon.png" + ) + + async_fire_mqtt_message(hass, latest_version_topic, '{"latest":"2.0"}') + + await hass.async_block_till_done() + + state = hass.states.get("update.test_update") + assert state.state == STATE_ON + assert state.attributes.get("installed_version") == "1.9" + assert state.attributes.get("latest_version") == "2.0" + + async def test_empty_json_state_message(hass, mqtt_mock_entry_with_yaml_config): """Test an empty JSON payload.""" state_topic = "test/state-topic" From d6b691e3e1cdd1eacb226a934c831d3350ac6f15 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Dec 2022 16:09:10 -1000 Subject: [PATCH 0985/1033] Bump aioesphomeapi to 13.0.1 (#83223) fixes https://github.com/home-assistant/core/issues/83212 --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 6d9796b8541..ae11aa59fce 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==13.0.0"], + "requirements": ["aioesphomeapi==13.0.1"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/requirements_all.txt b/requirements_all.txt index a60b3b74aca..c751343bb28 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -156,7 +156,7 @@ aioecowitt==2022.11.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==13.0.0 +aioesphomeapi==13.0.1 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1215dcbf604..ac69bdf4144 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -143,7 +143,7 @@ aioecowitt==2022.11.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==13.0.0 +aioesphomeapi==13.0.1 # homeassistant.components.flo aioflo==2021.11.0 From c1e808cc843717b1af8f4b918e0565bbc9d6b50e Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Sun, 4 Dec 2022 19:11:24 +0100 Subject: [PATCH 0986/1033] Fix missing title placeholders in Synology DSM reauth flow (#83238) * Fix missing title placeholders in reauth flow * fix tests Co-authored-by: mib1185 --- homeassistant/components/synology_dsm/config_flow.py | 5 ++++- tests/components/synology_dsm/test_config_flow.py | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/synology_dsm/config_flow.py b/homeassistant/components/synology_dsm/config_flow.py index 0314165eb41..ba332ca7e7d 100644 --- a/homeassistant/components/synology_dsm/config_flow.py +++ b/homeassistant/components/synology_dsm/config_flow.py @@ -164,6 +164,7 @@ class SynologyDSMFlowHandler(ConfigFlow, domain=DOMAIN): use_ssl = user_input.get(CONF_SSL, DEFAULT_USE_SSL) verify_ssl = user_input.get(CONF_VERIFY_SSL, DEFAULT_VERIFY_SSL) otp_code = user_input.get(CONF_OTP_CODE) + friendly_name = user_input.get(CONF_NAME) if not port: if use_ssl is True: @@ -229,7 +230,7 @@ class SynologyDSMFlowHandler(ConfigFlow, domain=DOMAIN): return self.async_abort(reason="reauth_successful") return self.async_abort(reason="reconfigure_successful") - return self.async_create_entry(title=host, data=config_data) + return self.async_create_entry(title=friendly_name or host, data=config_data) async def async_step_user( self, user_input: dict[str, Any] | None = None @@ -303,6 +304,8 @@ class SynologyDSMFlowHandler(ConfigFlow, domain=DOMAIN): async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: """Perform reauth upon an API authentication error.""" self.reauth_conf = entry_data + self.context["title_placeholders"][CONF_HOST] = entry_data[CONF_HOST] + return await self.async_step_reauth_confirm() async def async_step_reauth_confirm( diff --git a/tests/components/synology_dsm/test_config_flow.py b/tests/components/synology_dsm/test_config_flow.py index 015c3a2ab16..25259ac7ee9 100644 --- a/tests/components/synology_dsm/test_config_flow.py +++ b/tests/components/synology_dsm/test_config_flow.py @@ -281,6 +281,7 @@ async def test_reauth(hass: HomeAssistant, service: MagicMock): "source": SOURCE_REAUTH, "entry_id": entry.entry_id, "unique_id": entry.unique_id, + "title_placeholders": {"name": entry.title}, }, data={ CONF_HOST: HOST, @@ -409,7 +410,7 @@ async def test_form_ssdp(hass: HomeAssistant, service: MagicMock): assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert result["result"].unique_id == SERIAL - assert result["title"] == "192.168.1.5" + assert result["title"] == "mydsm" assert result["data"][CONF_HOST] == "192.168.1.5" assert result["data"][CONF_PORT] == 5001 assert result["data"][CONF_SSL] == DEFAULT_USE_SSL From 1aa2820c639612c5cb1e272ad42f90d895b4644a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Dec 2022 08:08:21 -1000 Subject: [PATCH 0987/1033] Bump pySwitchbot to 0.22.0 (#83243) fixes https://github.com/home-assistant/core/issues/82961 --- homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index c96b2884d3b..831d14d8459 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.20.8"], + "requirements": ["PySwitchbot==0.22.0"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index c751343bb28..fc0e10b671b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -37,7 +37,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.20.8 +PySwitchbot==0.22.0 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ac69bdf4144..28362613fe9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -33,7 +33,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.20.8 +PySwitchbot==0.22.0 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From 5d8650484b5ac118d3245d289eb4e3964e20a3e6 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 4 Dec 2022 10:18:27 -0800 Subject: [PATCH 0988/1033] Bump ical to 4.2.0 (#83244) --- homeassistant/components/local_calendar/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/local_calendar/manifest.json b/homeassistant/components/local_calendar/manifest.json index 037c9ff67bd..88324e30f54 100644 --- a/homeassistant/components/local_calendar/manifest.json +++ b/homeassistant/components/local_calendar/manifest.json @@ -3,7 +3,7 @@ "name": "Local Calendar", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/local_calendar", - "requirements": ["ical==4.1.2"], + "requirements": ["ical==4.2.0"], "codeowners": ["@allenporter"], "iot_class": "local_polling", "loggers": ["ical"] diff --git a/requirements_all.txt b/requirements_all.txt index fc0e10b671b..b5a5b7231e2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -926,7 +926,7 @@ ibm-watson==5.2.2 ibmiotf==0.3.4 # homeassistant.components.local_calendar -ical==4.1.2 +ical==4.2.0 # homeassistant.components.ping icmplib==3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 28362613fe9..26dc265dba3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -691,7 +691,7 @@ iaqualink==0.5.0 ibeacon_ble==1.0.1 # homeassistant.components.local_calendar -ical==4.1.2 +ical==4.2.0 # homeassistant.components.ping icmplib==3.0 From 7da31f6ee423f006e2afcedcc7d4e786ab4f4773 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 4 Dec 2022 14:16:51 -0800 Subject: [PATCH 0989/1033] Bump gcal_sync to 4.0.4 (#83245) --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index bc6c719c8fd..542eb72206a 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==4.0.3", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==4.0.4", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index b5a5b7231e2..f565c68aa1f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -738,7 +738,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==4.0.3 +gcal-sync==4.0.4 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 26dc265dba3..3665adea181 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -554,7 +554,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==4.0.3 +gcal-sync==4.0.4 # homeassistant.components.geocaching geocachingapi==0.2.1 From 17cd7d0a854f2bdc628bc15e156f75384cfb32b6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Dec 2022 16:09:45 -1000 Subject: [PATCH 0990/1033] Avoid regex overhead in processing esphome bluetooth advertisements (#83246) --- homeassistant/components/esphome/bluetooth/scanner.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/esphome/bluetooth/scanner.py b/homeassistant/components/esphome/bluetooth/scanner.py index b6cd3d2ec35..ea44ff45d1c 100644 --- a/homeassistant/components/esphome/bluetooth/scanner.py +++ b/homeassistant/components/esphome/bluetooth/scanner.py @@ -1,7 +1,6 @@ """Bluetooth scanner for esphome.""" from __future__ import annotations -import re from typing import Any from aioesphomeapi import BluetoothLEAdvertisement @@ -9,8 +8,6 @@ from aioesphomeapi import BluetoothLEAdvertisement from homeassistant.components.bluetooth import BaseHaRemoteScanner from homeassistant.core import callback -TWO_CHAR = re.compile("..") - class ESPHomeScanner(BaseHaRemoteScanner): """Scanner for esphome.""" @@ -18,9 +15,10 @@ class ESPHomeScanner(BaseHaRemoteScanner): @callback def async_on_advertisement(self, adv: BluetoothLEAdvertisement) -> None: """Call the registered callback.""" - address = ":".join(TWO_CHAR.findall("%012X" % adv.address)) # must be upper + # The mac address is a uint64, but we need a string + mac_hex = f"{adv.address:012X}" self._async_on_advertisement( - address, + f"{mac_hex[0:2]}:{mac_hex[2:4]}:{mac_hex[4:6]}:{mac_hex[6:8]}:{mac_hex[8:10]}:{mac_hex[10:12]}", adv.rssi, adv.name, adv.service_uuids, From 8c92f99cda9ee93e61fb3ed751b0e6c37e0d2e5f Mon Sep 17 00:00:00 2001 From: jjlawren Date: Sun, 4 Dec 2022 20:11:30 -0600 Subject: [PATCH 0991/1033] Do not discard zero positions for Sonos media players (#83249) fixes undefined --- homeassistant/components/sonos/media.py | 6 +++--- homeassistant/components/sonos/media_player.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/sonos/media.py b/homeassistant/components/sonos/media.py index 24233b1316f..ab34457e3fc 100644 --- a/homeassistant/components/sonos/media.py +++ b/homeassistant/components/sonos/media.py @@ -43,11 +43,11 @@ DURATION_SECONDS = "duration_in_s" POSITION_SECONDS = "position_in_s" -def _timespan_secs(timespan: str | None) -> None | float: +def _timespan_secs(timespan: str | None) -> None | int: """Parse a time-span into number of seconds.""" if timespan in UNAVAILABLE_VALUES: return None - return time_period_str(timespan).total_seconds() # type: ignore[arg-type] + return int(time_period_str(timespan).total_seconds()) # type: ignore[arg-type] class SonosMedia: @@ -73,7 +73,7 @@ class SonosMedia: self.title: str | None = None self.uri: str | None = None - self.position: float | None = None + self.position: int | None = None self.position_updated_at: datetime.datetime | None = None def clear(self) -> None: diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index 4195f284ffe..bd50a090175 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -323,7 +323,7 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): @property def media_position(self) -> int | None: """Position of current playing media in seconds.""" - return int(self.media.position) if self.media.position else None + return self.media.position @property def media_position_updated_at(self) -> datetime.datetime | None: From d89d2d3b3371e2d926669fceb7624075028ae197 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 4 Dec 2022 14:05:35 -0800 Subject: [PATCH 0992/1033] Bump ical to 4.2.1 (#83254) --- homeassistant/components/local_calendar/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/local_calendar/manifest.json b/homeassistant/components/local_calendar/manifest.json index 88324e30f54..fa258c389ab 100644 --- a/homeassistant/components/local_calendar/manifest.json +++ b/homeassistant/components/local_calendar/manifest.json @@ -3,7 +3,7 @@ "name": "Local Calendar", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/local_calendar", - "requirements": ["ical==4.2.0"], + "requirements": ["ical==4.2.1"], "codeowners": ["@allenporter"], "iot_class": "local_polling", "loggers": ["ical"] diff --git a/requirements_all.txt b/requirements_all.txt index f565c68aa1f..e5bfa674a9b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -926,7 +926,7 @@ ibm-watson==5.2.2 ibmiotf==0.3.4 # homeassistant.components.local_calendar -ical==4.2.0 +ical==4.2.1 # homeassistant.components.ping icmplib==3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3665adea181..f3c92e59e47 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -691,7 +691,7 @@ iaqualink==0.5.0 ibeacon_ble==1.0.1 # homeassistant.components.local_calendar -ical==4.2.0 +ical==4.2.1 # homeassistant.components.ping icmplib==3.0 From 8d433aa48130af6589796a01b25afc51f752308a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joris=20Pelgr=C3=B6m?= Date: Mon, 5 Dec 2022 03:10:26 +0100 Subject: [PATCH 0993/1033] Fix mobile_app returning cloud URL when not subscribed (#83257) fixes undefined --- homeassistant/components/mobile_app/http_api.py | 5 +++-- homeassistant/components/mobile_app/webhook.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mobile_app/http_api.py b/homeassistant/components/mobile_app/http_api.py index ea8c56d1a7c..3c34a291df1 100644 --- a/homeassistant/components/mobile_app/http_api.py +++ b/homeassistant/components/mobile_app/http_api.py @@ -91,8 +91,9 @@ class RegistrationsView(HomeAssistantView): ) remote_ui_url = None - with suppress(hass.components.cloud.CloudNotAvailable): - remote_ui_url = cloud.async_remote_ui_url(hass) + if cloud.async_active_subscription(hass): + with suppress(hass.components.cloud.CloudNotAvailable): + remote_ui_url = cloud.async_remote_ui_url(hass) return self.json( { diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index cd54c4216b5..2dd578a3fea 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -693,8 +693,9 @@ async def webhook_get_config( if CONF_CLOUDHOOK_URL in config_entry.data: resp[CONF_CLOUDHOOK_URL] = config_entry.data[CONF_CLOUDHOOK_URL] - with suppress(hass.components.cloud.CloudNotAvailable): - resp[CONF_REMOTE_UI_URL] = cloud.async_remote_ui_url(hass) + if cloud.async_active_subscription(hass): + with suppress(hass.components.cloud.CloudNotAvailable): + resp[CONF_REMOTE_UI_URL] = cloud.async_remote_ui_url(hass) webhook_id = config_entry.data[CONF_WEBHOOK_ID] From e862caa7041ce73857680bf5fb3375796dff0f8d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 4 Dec 2022 21:17:16 -0500 Subject: [PATCH 0994/1033] Bumped version to 2022.12.0b4 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index bdd8b4640ca..856c0202396 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -8,7 +8,7 @@ from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "0b3" +PATCH_VERSION: Final = "0b4" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/pyproject.toml b/pyproject.toml index 239052d5285..b423f299811 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.12.0b3" +version = "2022.12.0b4" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From 8c02c778d42bfbdb5754c8f6d1dc02330e9a9b0c Mon Sep 17 00:00:00 2001 From: Stephan Singer <3062329+SteSinger@users.noreply.github.com> Date: Mon, 5 Dec 2022 00:48:41 +0100 Subject: [PATCH 0995/1033] Add IPv6 sensor to fritz component (#75708) * Add IPv6 sensor to fritz component * Cast return type to string * Make ipv6 sensor suitable * simplify cast to str * use extisting property Co-authored-by: chemelli74 Co-authored-by: mib1185 --- homeassistant/components/fritz/common.py | 10 ++++++++++ homeassistant/components/fritz/sensor.py | 12 ++++++++++++ tests/components/fritz/const.py | 1 + tests/components/fritz/test_sensor.py | 4 ++++ 4 files changed, 27 insertions(+) diff --git a/homeassistant/components/fritz/common.py b/homeassistant/components/fritz/common.py index 6364ada9fb2..5af4f0c2239 100644 --- a/homeassistant/components/fritz/common.py +++ b/homeassistant/components/fritz/common.py @@ -663,6 +663,14 @@ class AvmWrapper(FritzBoxTools): partial(self.get_wan_link_properties) ) + async def async_ipv6_active(self) -> bool: + """Check ip an ipv6 is active on the WAn interface.""" + + def wrap_external_ipv6() -> str: + return str(self.fritz_status.external_ipv6) + + return bool(await self.hass.async_add_executor_job(wrap_external_ipv6)) + async def async_get_connection_info(self) -> ConnectionInfo: """Return ConnectionInfo data.""" @@ -671,6 +679,7 @@ class AvmWrapper(FritzBoxTools): connection=link_properties.get("NewWANAccessType", "").lower(), mesh_role=self.mesh_role, wan_enabled=self.device_is_router, + ipv6_active=await self.async_ipv6_active(), ) _LOGGER.debug( "ConnectionInfo for FritzBox %s: %s", @@ -1011,3 +1020,4 @@ class ConnectionInfo: connection: str mesh_role: MeshRoles wan_enabled: bool + ipv6_active: bool diff --git a/homeassistant/components/fritz/sensor.py b/homeassistant/components/fritz/sensor.py index 0f3d8cb1ae0..821a0000a5e 100644 --- a/homeassistant/components/fritz/sensor.py +++ b/homeassistant/components/fritz/sensor.py @@ -66,6 +66,11 @@ def _retrieve_external_ip_state(status: FritzStatus, last_value: str) -> str: return status.external_ip # type: ignore[no-any-return] +def _retrieve_external_ipv6_state(status: FritzStatus, last_value: str) -> str: + """Return external ipv6 from device.""" + return str(status.external_ipv6) + + def _retrieve_kb_s_sent_state(status: FritzStatus, last_value: str) -> float: """Return upload transmission rate.""" return round(status.transmission_rate[0] / 1000, 1) # type: ignore[no-any-return] @@ -155,6 +160,13 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = ( icon="mdi:earth", value_fn=_retrieve_external_ip_state, ), + FritzSensorEntityDescription( + key="external_ipv6", + name="External IPv6", + icon="mdi:earth", + value_fn=_retrieve_external_ipv6_state, + is_suitable=lambda info: info.ipv6_active, + ), FritzSensorEntityDescription( key="device_uptime", name="Device Uptime", diff --git a/tests/components/fritz/const.py b/tests/components/fritz/const.py index 32f2211d16b..fb62e14bc6f 100644 --- a/tests/components/fritz/const.py +++ b/tests/components/fritz/const.py @@ -138,6 +138,7 @@ MOCK_FB_SERVICES: dict[str, dict] = { "NewUptime": 35307, }, "GetExternalIPAddress": {"NewExternalIPAddress": "1.2.3.4"}, + "X_AVM_DE_GetExternalIPv6Address": {"NewExternalIPv6Address": "fec0::1"}, }, "WANPPPConnection1": { "GetInfo": { diff --git a/tests/components/fritz/test_sensor.py b/tests/components/fritz/test_sensor.py index 2a3435210e7..117e9d31967 100644 --- a/tests/components/fritz/test_sensor.py +++ b/tests/components/fritz/test_sensor.py @@ -35,6 +35,10 @@ SENSOR_STATES: dict[str, dict[str, Any]] = { ATTR_STATE: "1.2.3.4", ATTR_ICON: "mdi:earth", }, + "sensor.mock_title_external_ipv6": { + ATTR_STATE: "fec0::1", + ATTR_ICON: "mdi:earth", + }, "sensor.mock_title_device_uptime": { # ATTR_STATE: "2022-02-05T17:46:04+00:00", ATTR_DEVICE_CLASS: SensorDeviceClass.TIMESTAMP, From b30c98c525d9070f133997c47f372975b5b0e576 Mon Sep 17 00:00:00 2001 From: Garrett <7310260+G-Two@users.noreply.github.com> Date: Mon, 5 Dec 2022 04:53:05 -0500 Subject: [PATCH 0996/1033] Bump subarulink to v0.7.0 (#83213) fixes undefined --- homeassistant/components/subaru/manifest.json | 2 +- homeassistant/components/subaru/sensor.py | 16 ------- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/subaru/api_responses.py | 48 ++++++++----------- .../fixtures/diagnostics_config_entry.json | 14 +++--- .../subaru/fixtures/diagnostics_device.json | 14 +++--- 7 files changed, 36 insertions(+), 62 deletions(-) diff --git a/homeassistant/components/subaru/manifest.json b/homeassistant/components/subaru/manifest.json index df3a97cbda3..d0e1193f92b 100644 --- a/homeassistant/components/subaru/manifest.json +++ b/homeassistant/components/subaru/manifest.json @@ -3,7 +3,7 @@ "name": "Subaru", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/subaru", - "requirements": ["subarulink==0.6.1"], + "requirements": ["subarulink==0.7.0"], "codeowners": ["@G-Two"], "iot_class": "cloud_polling", "loggers": ["stdiomask", "subarulink"] diff --git a/homeassistant/components/subaru/sensor.py b/homeassistant/components/subaru/sensor.py index cae5a7b14a4..9db2d329210 100644 --- a/homeassistant/components/subaru/sensor.py +++ b/homeassistant/components/subaru/sensor.py @@ -14,12 +14,10 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_POTENTIAL_VOLT, LENGTH_KILOMETERS, LENGTH_MILES, PERCENTAGE, PRESSURE_HPA, - TEMP_CELSIUS, VOLUME_GALLONS, VOLUME_LITERS, ) @@ -117,20 +115,6 @@ API_GEN_2_SENSORS = [ native_unit_of_measurement=PRESSURE_HPA, state_class=SensorStateClass.MEASUREMENT, ), - SensorEntityDescription( - key=sc.EXTERNAL_TEMP, - device_class=SensorDeviceClass.TEMPERATURE, - name="External temp", - native_unit_of_measurement=TEMP_CELSIUS, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=sc.BATTERY_VOLTAGE, - device_class=SensorDeviceClass.VOLTAGE, - name="12V battery voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - state_class=SensorStateClass.MEASUREMENT, - ), ] # Sensors available to "Subaru Safety Plus" subscribers with PHEV vehicles diff --git a/requirements_all.txt b/requirements_all.txt index e5bfa674a9b..0fee807b5c4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2373,7 +2373,7 @@ streamlabswater==1.0.1 stringcase==1.2.0 # homeassistant.components.subaru -subarulink==0.6.1 +subarulink==0.7.0 # homeassistant.components.solarlog sunwatcher==0.2.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f3c92e59e47..a1012f29e1e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1652,7 +1652,7 @@ stookalert==0.1.4 stringcase==1.2.0 # homeassistant.components.subaru -subarulink==0.6.1 +subarulink==0.7.0 # homeassistant.components.solarlog sunwatcher==0.2.1 diff --git a/tests/components/subaru/api_responses.py b/tests/components/subaru/api_responses.py index bd107f4bb37..315530c15b3 100644 --- a/tests/components/subaru/api_responses.py +++ b/tests/components/subaru/api_responses.py @@ -53,7 +53,6 @@ MOCK_DATETIME = datetime.fromtimestamp(1595560000, timezone.utc) VEHICLE_STATUS_EV = { "status": { "AVG_FUEL_CONSUMPTION": 2.3, - "BATTERY_VOLTAGE": 12.0, "DISTANCE_TO_EMPTY_FUEL": 707, "DOOR_BOOT_LOCK_STATUS": "UNKNOWN", "DOOR_BOOT_POSITION": "CLOSED", @@ -75,7 +74,6 @@ VEHICLE_STATUS_EV = { "EV_STATE_OF_CHARGE_MODE": "EV_MODE", "EV_STATE_OF_CHARGE_PERCENT": 20, "EV_TIME_TO_FULLY_CHARGED_UTC": MOCK_DATETIME, - "EXT_EXTERNAL_TEMP": 21.5, "ODOMETER": 1234, "POSITION_HEADING_DEGREE": 150, "POSITION_SPEED_KMPH": "0", @@ -103,7 +101,7 @@ VEHICLE_STATUS_EV = { "TYRE_PRESSURE_FRONT_LEFT": 0, "TYRE_PRESSURE_FRONT_RIGHT": 2550, "TYRE_PRESSURE_REAR_LEFT": 2450, - "TYRE_PRESSURE_REAR_RIGHT": 2350, + "TYRE_PRESSURE_REAR_RIGHT": None, "TYRE_STATUS_FRONT_LEFT": "UNKNOWN", "TYRE_STATUS_FRONT_RIGHT": "UNKNOWN", "TYRE_STATUS_REAR_LEFT": "UNKNOWN", @@ -115,9 +113,9 @@ VEHICLE_STATUS_EV = { "WINDOW_REAR_LEFT_STATUS": "UNKNOWN", "WINDOW_REAR_RIGHT_STATUS": "UNKNOWN", "WINDOW_SUNROOF_STATUS": "UNKNOWN", - "heading": 170, - "latitude": 40.0, - "longitude": -100.0, + "HEADING": 170, + "LATITUDE": 40.0, + "LONGITUDE": -100.0, } } @@ -125,7 +123,6 @@ VEHICLE_STATUS_EV = { VEHICLE_STATUS_G2 = { "status": { "AVG_FUEL_CONSUMPTION": 2.3, - "BATTERY_VOLTAGE": 12.0, "DISTANCE_TO_EMPTY_FUEL": 707, "DOOR_BOOT_LOCK_STATUS": "UNKNOWN", "DOOR_BOOT_POSITION": "CLOSED", @@ -139,7 +136,6 @@ VEHICLE_STATUS_G2 = { "DOOR_REAR_LEFT_POSITION": "CLOSED", "DOOR_REAR_RIGHT_LOCK_STATUS": "UNKNOWN", "DOOR_REAR_RIGHT_POSITION": "CLOSED", - "EXT_EXTERNAL_TEMP": None, "ODOMETER": 1234, "POSITION_HEADING_DEGREE": 150, "POSITION_SPEED_KMPH": "0", @@ -167,7 +163,7 @@ VEHICLE_STATUS_G2 = { "TYRE_PRESSURE_FRONT_LEFT": 2550, "TYRE_PRESSURE_FRONT_RIGHT": 2550, "TYRE_PRESSURE_REAR_LEFT": 2450, - "TYRE_PRESSURE_REAR_RIGHT": 2350, + "TYRE_PRESSURE_REAR_RIGHT": None, "TYRE_STATUS_FRONT_LEFT": "UNKNOWN", "TYRE_STATUS_FRONT_RIGHT": "UNKNOWN", "TYRE_STATUS_REAR_LEFT": "UNKNOWN", @@ -179,15 +175,14 @@ VEHICLE_STATUS_G2 = { "WINDOW_REAR_LEFT_STATUS": "UNKNOWN", "WINDOW_REAR_RIGHT_STATUS": "UNKNOWN", "WINDOW_SUNROOF_STATUS": "UNKNOWN", - "heading": 170, - "latitude": 40.0, - "longitude": -100.0, + "HEADING": 170, + "LATITUDE": 40.0, + "LONGITUDE": -100.0, } } EXPECTED_STATE_EV_IMPERIAL = { "AVG_FUEL_CONSUMPTION": "102.3", - "BATTERY_VOLTAGE": "12.0", "DISTANCE_TO_EMPTY_FUEL": "439.3", "EV_CHARGER_STATE_TYPE": "CHARGING", "EV_CHARGE_SETTING_AMPERE_TYPE": "MAXIMUM", @@ -197,7 +192,6 @@ EXPECTED_STATE_EV_IMPERIAL = { "EV_STATE_OF_CHARGE_MODE": "EV_MODE", "EV_STATE_OF_CHARGE_PERCENT": "20", "EV_TIME_TO_FULLY_CHARGED_UTC": "2020-07-24T03:06:40+00:00", - "EXT_EXTERNAL_TEMP": "70.7", "ODOMETER": "766.8", "POSITION_HEADING_DEGREE": "150", "POSITION_SPEED_KMPH": "0", @@ -207,16 +201,15 @@ EXPECTED_STATE_EV_IMPERIAL = { "TYRE_PRESSURE_FRONT_LEFT": "0.0", "TYRE_PRESSURE_FRONT_RIGHT": "37.0", "TYRE_PRESSURE_REAR_LEFT": "35.5", - "TYRE_PRESSURE_REAR_RIGHT": "34.1", + "TYRE_PRESSURE_REAR_RIGHT": "unknown", "VEHICLE_STATE_TYPE": "IGNITION_OFF", - "heading": 170, - "latitude": 40.0, - "longitude": -100.0, + "HEADING": 170, + "LATITUDE": 40.0, + "LONGITUDE": -100.0, } EXPECTED_STATE_EV_METRIC = { "AVG_FUEL_CONSUMPTION": "2.3", - "BATTERY_VOLTAGE": "12.0", "DISTANCE_TO_EMPTY_FUEL": "707", "EV_CHARGER_STATE_TYPE": "CHARGING", "EV_CHARGE_SETTING_AMPERE_TYPE": "MAXIMUM", @@ -226,7 +219,6 @@ EXPECTED_STATE_EV_METRIC = { "EV_STATE_OF_CHARGE_MODE": "EV_MODE", "EV_STATE_OF_CHARGE_PERCENT": "20", "EV_TIME_TO_FULLY_CHARGED_UTC": "2020-07-24T03:06:40+00:00", - "EXT_EXTERNAL_TEMP": "21.5", "ODOMETER": "1234", "POSITION_HEADING_DEGREE": "150", "POSITION_SPEED_KMPH": "0", @@ -236,17 +228,16 @@ EXPECTED_STATE_EV_METRIC = { "TYRE_PRESSURE_FRONT_LEFT": "0", "TYRE_PRESSURE_FRONT_RIGHT": "2550", "TYRE_PRESSURE_REAR_LEFT": "2450", - "TYRE_PRESSURE_REAR_RIGHT": "2350", + "TYRE_PRESSURE_REAR_RIGHT": "unknown", "VEHICLE_STATE_TYPE": "IGNITION_OFF", - "heading": 170, - "latitude": 40.0, - "longitude": -100.0, + "HEADING": 170, + "LATITUDE": 40.0, + "LONGITUDE": -100.0, } EXPECTED_STATE_EV_UNAVAILABLE = { "AVG_FUEL_CONSUMPTION": "unavailable", - "BATTERY_VOLTAGE": "unavailable", "DISTANCE_TO_EMPTY_FUEL": "unavailable", "EV_CHARGER_STATE_TYPE": "unavailable", "EV_CHARGE_SETTING_AMPERE_TYPE": "unavailable", @@ -256,7 +247,6 @@ EXPECTED_STATE_EV_UNAVAILABLE = { "EV_STATE_OF_CHARGE_MODE": "unavailable", "EV_STATE_OF_CHARGE_PERCENT": "unavailable", "EV_TIME_TO_FULLY_CHARGED_UTC": "unavailable", - "EXT_EXTERNAL_TEMP": "unavailable", "ODOMETER": "unavailable", "POSITION_HEADING_DEGREE": "unavailable", "POSITION_SPEED_KMPH": "unavailable", @@ -268,7 +258,7 @@ EXPECTED_STATE_EV_UNAVAILABLE = { "TYRE_PRESSURE_REAR_LEFT": "unavailable", "TYRE_PRESSURE_REAR_RIGHT": "unavailable", "VEHICLE_STATE_TYPE": "unavailable", - "heading": "unavailable", - "latitude": "unavailable", - "longitude": "unavailable", + "HEADING": "unavailable", + "LATITUDE": "unavailable", + "LONGITUDE": "unavailable", } diff --git a/tests/components/subaru/fixtures/diagnostics_config_entry.json b/tests/components/subaru/fixtures/diagnostics_config_entry.json index 09e8119d669..32e9ac070be 100644 --- a/tests/components/subaru/fixtures/diagnostics_config_entry.json +++ b/tests/components/subaru/fixtures/diagnostics_config_entry.json @@ -6,12 +6,13 @@ "pin": "**REDACTED**", "device_id": "**REDACTED**" }, - "options": { "update_enabled": true }, + "options": { + "update_enabled": true + }, "data": [ { "status": { "AVG_FUEL_CONSUMPTION": 2.3, - "BATTERY_VOLTAGE": 12.0, "DISTANCE_TO_EMPTY_FUEL": 707, "DOOR_BOOT_LOCK_STATUS": "UNKNOWN", "DOOR_BOOT_POSITION": "CLOSED", @@ -33,7 +34,6 @@ "EV_STATE_OF_CHARGE_MODE": "EV_MODE", "EV_STATE_OF_CHARGE_PERCENT": 20, "EV_TIME_TO_FULLY_CHARGED_UTC": "2020-07-24T03:06:40+00:00", - "EXT_EXTERNAL_TEMP": 21.5, "ODOMETER": "**REDACTED**", "POSITION_HEADING_DEGREE": 150, "POSITION_SPEED_KMPH": "0", @@ -61,7 +61,7 @@ "TYRE_PRESSURE_FRONT_LEFT": 0, "TYRE_PRESSURE_FRONT_RIGHT": 2550, "TYRE_PRESSURE_REAR_LEFT": 2450, - "TYRE_PRESSURE_REAR_RIGHT": 2350, + "TYRE_PRESSURE_REAR_RIGHT": null, "TYRE_STATUS_FRONT_LEFT": "UNKNOWN", "TYRE_STATUS_FRONT_RIGHT": "UNKNOWN", "TYRE_STATUS_REAR_LEFT": "UNKNOWN", @@ -73,9 +73,9 @@ "WINDOW_REAR_LEFT_STATUS": "UNKNOWN", "WINDOW_REAR_RIGHT_STATUS": "UNKNOWN", "WINDOW_SUNROOF_STATUS": "UNKNOWN", - "heading": 170, - "latitude": "**REDACTED**", - "longitude": "**REDACTED**" + "HEADING": 170, + "LATITUDE": "**REDACTED**", + "LONGITUDE": "**REDACTED**" } } ] diff --git a/tests/components/subaru/fixtures/diagnostics_device.json b/tests/components/subaru/fixtures/diagnostics_device.json index 83b2c2a836e..c3762925d04 100644 --- a/tests/components/subaru/fixtures/diagnostics_device.json +++ b/tests/components/subaru/fixtures/diagnostics_device.json @@ -6,11 +6,12 @@ "pin": "**REDACTED**", "device_id": "**REDACTED**" }, - "options": { "update_enabled": true }, + "options": { + "update_enabled": true + }, "data": { "status": { "AVG_FUEL_CONSUMPTION": 2.3, - "BATTERY_VOLTAGE": 12.0, "DISTANCE_TO_EMPTY_FUEL": 707, "DOOR_BOOT_LOCK_STATUS": "UNKNOWN", "DOOR_BOOT_POSITION": "CLOSED", @@ -32,7 +33,6 @@ "EV_STATE_OF_CHARGE_MODE": "EV_MODE", "EV_STATE_OF_CHARGE_PERCENT": 20, "EV_TIME_TO_FULLY_CHARGED_UTC": "2020-07-24T03:06:40+00:00", - "EXT_EXTERNAL_TEMP": 21.5, "ODOMETER": "**REDACTED**", "POSITION_HEADING_DEGREE": 150, "POSITION_SPEED_KMPH": "0", @@ -60,7 +60,7 @@ "TYRE_PRESSURE_FRONT_LEFT": 0, "TYRE_PRESSURE_FRONT_RIGHT": 2550, "TYRE_PRESSURE_REAR_LEFT": 2450, - "TYRE_PRESSURE_REAR_RIGHT": 2350, + "TYRE_PRESSURE_REAR_RIGHT": null, "TYRE_STATUS_FRONT_LEFT": "UNKNOWN", "TYRE_STATUS_FRONT_RIGHT": "UNKNOWN", "TYRE_STATUS_REAR_LEFT": "UNKNOWN", @@ -72,9 +72,9 @@ "WINDOW_REAR_LEFT_STATUS": "UNKNOWN", "WINDOW_REAR_RIGHT_STATUS": "UNKNOWN", "WINDOW_SUNROOF_STATUS": "UNKNOWN", - "heading": 170, - "latitude": "**REDACTED**", - "longitude": "**REDACTED**" + "HEADING": 170, + "LATITUDE": "**REDACTED**", + "LONGITUDE": "**REDACTED**" } } } From 8985a3cc06a4452b9abf8f16460e0e5df12c329c Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Sun, 4 Dec 2022 22:06:55 -0500 Subject: [PATCH 0997/1033] Update UniFi Protect ring entity to use event entity (#83270) --- .../components/unifiprotect/binary_sensor.py | 27 ++++++++++--------- .../components/unifiprotect/sensor.py | 10 +++---- .../unifiprotect/test_binary_sensor.py | 12 ++++----- tests/components/unifiprotect/test_sensor.py | 8 +++--- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/unifiprotect/binary_sensor.py b/homeassistant/components/unifiprotect/binary_sensor.py index bf6e89c3caf..bf53dc8d206 100644 --- a/homeassistant/components/unifiprotect/binary_sensor.py +++ b/homeassistant/components/unifiprotect/binary_sensor.py @@ -67,14 +67,6 @@ MOUNT_DEVICE_CLASS_MAP = { CAMERA_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = ( - ProtectBinaryEntityDescription( - key="doorbell", - name="Doorbell", - device_class=BinarySensorDeviceClass.OCCUPANCY, - icon="mdi:doorbell-video", - ufp_required_field="feature_flags.has_chime", - ufp_value="is_ringing", - ), ProtectBinaryEntityDescription( key="dark", name="Is Dark", @@ -339,7 +331,16 @@ SENSE_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = ( ), ) -MOTION_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = ( +EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = ( + ProtectBinaryEventEntityDescription( + key="doorbell", + name="Doorbell", + device_class=BinarySensorDeviceClass.OCCUPANCY, + icon="mdi:doorbell-video", + ufp_required_field="feature_flags.has_chime", + ufp_value="is_ringing", + ufp_event_obj="last_ring_event", + ), ProtectBinaryEventEntityDescription( key="motion", name="Motion", @@ -485,7 +486,7 @@ async def async_setup_entry( ufp_device=device, ) if device.is_adopted and isinstance(device, Camera): - entities += _async_motion_entities(data, ufp_device=device) + entities += _async_event_entities(data, ufp_device=device) async_add_entities(entities) entry.async_on_unload( @@ -501,14 +502,14 @@ async def async_setup_entry( lock_descs=DOORLOCK_SENSORS, viewer_descs=VIEWER_SENSORS, ) - entities += _async_motion_entities(data) + entities += _async_event_entities(data) entities += _async_nvr_entities(data) async_add_entities(entities) @callback -def _async_motion_entities( +def _async_event_entities( data: ProtectData, ufp_device: ProtectAdoptableDeviceModel | None = None, ) -> list[ProtectDeviceEntity]: @@ -517,7 +518,7 @@ def _async_motion_entities( data.get_by_types({ModelType.CAMERA}) if ufp_device is None else [ufp_device] ) for device in devices: - for description in MOTION_SENSORS: + for description in EVENT_SENSORS: if not description.has_required(device): continue entities.append(ProtectEventBinarySensor(data, device, description)) diff --git a/homeassistant/components/unifiprotect/sensor.py b/homeassistant/components/unifiprotect/sensor.py index d608907fa83..fa08892e2d3 100644 --- a/homeassistant/components/unifiprotect/sensor.py +++ b/homeassistant/components/unifiprotect/sensor.py @@ -521,7 +521,7 @@ NVR_DISABLED_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( ), ) -MOTION_SENSORS: tuple[ProtectSensorEventEntityDescription, ...] = ( +EVENT_SENSORS: tuple[ProtectSensorEventEntityDescription, ...] = ( ProtectSensorEventEntityDescription( key="detected_object", name="Detected Object", @@ -641,7 +641,7 @@ async def async_setup_entry( ufp_device=device, ) if device.is_adopted_by_us and isinstance(device, Camera): - entities += _async_motion_entities(data, ufp_device=device) + entities += _async_event_entities(data, ufp_device=device) async_add_entities(entities) entry.async_on_unload( @@ -659,14 +659,14 @@ async def async_setup_entry( chime_descs=CHIME_SENSORS, viewer_descs=VIEWER_SENSORS, ) - entities += _async_motion_entities(data) + entities += _async_event_entities(data) entities += _async_nvr_entities(data) async_add_entities(entities) @callback -def _async_motion_entities( +def _async_event_entities( data: ProtectData, ufp_device: Camera | None = None, ) -> list[ProtectDeviceEntity]: @@ -687,7 +687,7 @@ def _async_motion_entities( if not device.feature_flags.has_smart_detect: continue - for event_desc in MOTION_SENSORS: + for event_desc in EVENT_SENSORS: if not event_desc.has_required(device): continue diff --git a/tests/components/unifiprotect/test_binary_sensor.py b/tests/components/unifiprotect/test_binary_sensor.py index b2935552855..152628c75f9 100644 --- a/tests/components/unifiprotect/test_binary_sensor.py +++ b/tests/components/unifiprotect/test_binary_sensor.py @@ -11,8 +11,8 @@ from pyunifiprotect.data.nvr import EventMetadata from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.unifiprotect.binary_sensor import ( CAMERA_SENSORS, + EVENT_SENSORS, LIGHT_SENSORS, - MOTION_SENSORS, SENSE_SENSORS, ) from homeassistant.components.unifiprotect.const import ( @@ -124,7 +124,7 @@ async def test_binary_sensor_setup_camera_all( entity_registry = er.async_get(hass) - description = CAMERA_SENSORS[0] + description = EVENT_SENSORS[0] unique_id, entity_id = ids_from_device_description( Platform.BINARY_SENSOR, doorbell, description ) @@ -139,7 +139,7 @@ async def test_binary_sensor_setup_camera_all( assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION # Is Dark - description = CAMERA_SENSORS[1] + description = CAMERA_SENSORS[0] unique_id, entity_id = ids_from_device_description( Platform.BINARY_SENSOR, doorbell, description ) @@ -154,7 +154,7 @@ async def test_binary_sensor_setup_camera_all( assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION # Motion - description = MOTION_SENSORS[0] + description = EVENT_SENSORS[1] unique_id, entity_id = ids_from_device_description( Platform.BINARY_SENSOR, doorbell, description ) @@ -179,7 +179,7 @@ async def test_binary_sensor_setup_camera_none( assert_entity_counts(hass, Platform.BINARY_SENSOR, 2, 2) entity_registry = er.async_get(hass) - description = CAMERA_SENSORS[1] + description = CAMERA_SENSORS[0] unique_id, entity_id = ids_from_device_description( Platform.BINARY_SENSOR, camera, description @@ -265,7 +265,7 @@ async def test_binary_sensor_update_motion( assert_entity_counts(hass, Platform.BINARY_SENSOR, 13, 13) _, entity_id = ids_from_device_description( - Platform.BINARY_SENSOR, doorbell, MOTION_SENSORS[0] + Platform.BINARY_SENSOR, doorbell, EVENT_SENSORS[1] ) event = Event( diff --git a/tests/components/unifiprotect/test_sensor.py b/tests/components/unifiprotect/test_sensor.py index e67195df5e9..f5779e78b1c 100644 --- a/tests/components/unifiprotect/test_sensor.py +++ b/tests/components/unifiprotect/test_sensor.py @@ -23,7 +23,7 @@ from homeassistant.components.unifiprotect.sensor import ( ALL_DEVICES_SENSORS, CAMERA_DISABLED_SENSORS, CAMERA_SENSORS, - MOTION_SENSORS, + EVENT_SENSORS, MOTION_TRIP_SENSORS, NVR_DISABLED_SENSORS, NVR_SENSORS, @@ -399,7 +399,7 @@ async def test_sensor_setup_camera( # Detected Object unique_id, entity_id = ids_from_device_description( - Platform.SENSOR, doorbell, MOTION_SENSORS[0] + Platform.SENSOR, doorbell, EVENT_SENSORS[0] ) entity = entity_registry.async_get(entity_id) @@ -455,7 +455,7 @@ async def test_sensor_update_motion( assert_entity_counts(hass, Platform.SENSOR, 25, 12) _, entity_id = ids_from_device_description( - Platform.SENSOR, doorbell, MOTION_SENSORS[0] + Platform.SENSOR, doorbell, EVENT_SENSORS[0] ) await enable_entity(hass, ufp.entry.entry_id, entity_id) @@ -582,7 +582,7 @@ async def test_camera_update_licenseplate( assert_entity_counts(hass, Platform.SENSOR, 24, 13) _, entity_id = ids_from_device_description( - Platform.SENSOR, camera, MOTION_SENSORS[1] + Platform.SENSOR, camera, EVENT_SENSORS[1] ) event_metadata = EventMetadata( From c34f8dc246294144a275baf3b4503fb35d9f8de6 Mon Sep 17 00:00:00 2001 From: Maikel Punie Date: Mon, 5 Dec 2022 17:12:27 +0100 Subject: [PATCH 0998/1033] Small fix for the velbus clear-cache service (#83279) --- homeassistant/components/velbus/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/velbus/__init__.py b/homeassistant/components/velbus/__init__.py index 2ac751e283d..0da64227fd3 100644 --- a/homeassistant/components/velbus/__init__.py +++ b/homeassistant/components/velbus/__init__.py @@ -139,7 +139,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Handle a clear cache service call.""" # clear the cache with suppress(FileNotFoundError): - if call.data[CONF_ADDRESS]: + if CONF_ADDRESS in call.data and call.data[CONF_ADDRESS]: await hass.async_add_executor_job( os.unlink, hass.config.path( From f951808863f1dbe5124eafb486a1d6ed482ddf84 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 5 Dec 2022 17:34:21 -1000 Subject: [PATCH 0999/1033] Fix bluetooth device connection failure when device is seen by dbus but not bleak (#83281) --- homeassistant/components/bluetooth/manifest.json | 4 ++-- homeassistant/components/bluetooth/scanner.py | 2 ++ homeassistant/package_constraints.txt | 4 ++-- requirements_all.txt | 4 ++-- requirements_test_all.txt | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 4f5e1bd1b64..a8e1c02c8b0 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,8 +7,8 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.2", - "bleak-retry-connector==2.9.0", - "bluetooth-adapters==0.11.0", + "bleak-retry-connector==2.10.1", + "bluetooth-adapters==0.12.0", "bluetooth-auto-recovery==0.5.4", "bluetooth-data-tools==0.3.0", "dbus-fast==1.75.0" diff --git a/homeassistant/components/bluetooth/scanner.py b/homeassistant/components/bluetooth/scanner.py index 6f61e16c583..09032715c74 100644 --- a/homeassistant/components/bluetooth/scanner.py +++ b/homeassistant/components/bluetooth/scanner.py @@ -16,6 +16,7 @@ from bleak.backends.bluezdbus.advertisement_monitor import OrPattern from bleak.backends.bluezdbus.scanner import BlueZScannerArgs from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData, AdvertisementDataCallback +from bleak_retry_connector import restore_discoveries from bluetooth_adapters import DEFAULT_ADDRESS from dbus_fast import InvalidMessageError @@ -314,6 +315,7 @@ class HaScanner(BaseHaScanner): self.scanning = True self._async_setup_scanner_watchdog() + await restore_discoveries(self.scanner, self.adapter) @hass_callback def _async_setup_scanner_watchdog(self) -> None: diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index e42e7e2b9aa..b63615111cd 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,9 +10,9 @@ atomicwrites-homeassistant==1.4.1 attrs==21.2.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.9.0 +bleak-retry-connector==2.10.1 bleak==0.19.2 -bluetooth-adapters==0.11.0 +bluetooth-adapters==0.12.0 bluetooth-auto-recovery==0.5.4 bluetooth-data-tools==0.3.0 certifi>=2021.5.30 diff --git a/requirements_all.txt b/requirements_all.txt index 0fee807b5c4..d36e6951aa1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -422,7 +422,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.9.0 +bleak-retry-connector==2.10.1 # homeassistant.components.bluetooth bleak==0.19.2 @@ -447,7 +447,7 @@ bluemaestro-ble==0.2.0 # bluepy==1.3.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.11.0 +bluetooth-adapters==0.12.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.5.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a1012f29e1e..23f7c17a56e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -346,7 +346,7 @@ bellows==0.34.5 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.9.0 +bleak-retry-connector==2.10.1 # homeassistant.components.bluetooth bleak==0.19.2 @@ -361,7 +361,7 @@ blinkpy==0.19.2 bluemaestro-ble==0.2.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.11.0 +bluetooth-adapters==0.12.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.5.4 From fed08a9d8053da6e022c7b0801070aac3c8c6913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ajduk?= Date: Mon, 5 Dec 2022 16:55:21 +0100 Subject: [PATCH 1000/1033] Fix Tuya number native value raw return (#83333) fixes undefined --- homeassistant/components/tuya/number.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/tuya/number.py b/homeassistant/components/tuya/number.py index 39874d0ae8d..21c6dcee6e1 100644 --- a/homeassistant/components/tuya/number.py +++ b/homeassistant/components/tuya/number.py @@ -410,7 +410,7 @@ class TuyaNumberEntity(TuyaEntity, NumberEntity): return None # Raw value - if not (value := self.device.status.get(self.entity_description.key)): + if (value := self.device.status.get(self.entity_description.key)) is None: return None return self._number.scale_value(value) From 218046bfa5d859e7f054d7f0b640a7a6488a986e Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Mon, 5 Dec 2022 16:54:36 +0100 Subject: [PATCH 1001/1033] Deprecate the Xbox Live integration (#83339) --- homeassistant/components/xbox_live/sensor.py | 14 ++++++++++++++ homeassistant/components/xbox_live/strings.json | 8 ++++++++ .../components/xbox_live/translations/en.json | 8 ++++++++ 3 files changed, 30 insertions(+) create mode 100644 homeassistant/components/xbox_live/strings.json create mode 100644 homeassistant/components/xbox_live/translations/en.json diff --git a/homeassistant/components/xbox_live/sensor.py b/homeassistant/components/xbox_live/sensor.py index 07adcbeb5cc..f75dbe6ba4e 100644 --- a/homeassistant/components/xbox_live/sensor.py +++ b/homeassistant/components/xbox_live/sensor.py @@ -13,6 +13,7 @@ from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_track_time_interval +from homeassistant.helpers.issue_registry import IssueSeverity, create_issue from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType _LOGGER = logging.getLogger(__name__) @@ -36,6 +37,19 @@ def setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Xbox platform.""" + create_issue( + hass, + "xbox_live", + "pending_removal", + breaks_in_ha_version="2023.2.0", + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key="pending_removal", + ) + _LOGGER.warning( + "The Xbox Live integration is deprecated " + "and will be removed in Home Assistant 2023.2" + ) api = Client(api_key=config[CONF_API_KEY]) entities = [] diff --git a/homeassistant/components/xbox_live/strings.json b/homeassistant/components/xbox_live/strings.json new file mode 100644 index 00000000000..0f73f851bd7 --- /dev/null +++ b/homeassistant/components/xbox_live/strings.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "title": "The Xbox Live integration is being removed", + "description": "The Xbox Live integration is pending removal from Home Assistant and will no longer be available as of Home Assistant 2023.2.\n\nThe integration is being removed, because it is only useful for the legacy device Xbox 360 and the upstream API now requires a paid subscription. Newer consoles are supported by the Xbox integration for free.\n\nRemove the Xbox Live YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue." + } + } +} diff --git a/homeassistant/components/xbox_live/translations/en.json b/homeassistant/components/xbox_live/translations/en.json new file mode 100644 index 00000000000..cb2ca622a22 --- /dev/null +++ b/homeassistant/components/xbox_live/translations/en.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "The Xbox Live integration is pending removal from Home Assistant and will no longer be available as of Home Assistant 2023.2.\n\nThe integration is being removed, because it is only useful for the legacy device Xbox 360 and the upstream API now requires a paid subscription. Newer consoles are supported by the Xbox integration for free.\n\nRemove the Xbox Live YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", + "title": "The Xbox Live integration is being removed" + } + } +} \ No newline at end of file From 604c4588ccb639ee6de5a4ff83a46cc25e0cb405 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 5 Dec 2022 08:54:27 -1000 Subject: [PATCH 1002/1033] Bump aiohomekit to 2.4.1 (#83341) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index c5047f9d215..47112d3bd50 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.4.0"], + "requirements": ["aiohomekit==2.4.1"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index d36e6951aa1..3e8d0bc4aa3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -174,7 +174,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.4.0 +aiohomekit==2.4.1 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 23f7c17a56e..300bea75067 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -158,7 +158,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.4.0 +aiohomekit==2.4.1 # homeassistant.components.emulated_hue # homeassistant.components.http From 351bdff5313826d53e6f08d58596ed63088fc406 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk <11290930+bouwew@users.noreply.github.com> Date: Mon, 5 Dec 2022 20:17:19 +0100 Subject: [PATCH 1003/1033] Update plugwise to v0.25.14, improve number detection (#83345) fixes undefined --- homeassistant/components/plugwise/manifest.json | 2 +- homeassistant/components/plugwise/number.py | 2 +- homeassistant/components/plugwise/sensor.py | 16 ++++++++++++++++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../fixtures/anna_heatpump_heating/all_data.json | 3 ++- .../m_anna_heatpump_cooling/all_data.json | 11 ++++++----- tests/components/plugwise/test_sensor.py | 4 ++++ 8 files changed, 32 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/plugwise/manifest.json b/homeassistant/components/plugwise/manifest.json index 6a11b47da57..e10d86f2779 100644 --- a/homeassistant/components/plugwise/manifest.json +++ b/homeassistant/components/plugwise/manifest.json @@ -2,7 +2,7 @@ "domain": "plugwise", "name": "Plugwise", "documentation": "https://www.home-assistant.io/integrations/plugwise", - "requirements": ["plugwise==0.25.12"], + "requirements": ["plugwise==0.25.14"], "codeowners": ["@CoMPaTech", "@bouwew", "@brefra", "@frenck"], "zeroconf": ["_plugwise._tcp.local."], "config_flow": true, diff --git a/homeassistant/components/plugwise/number.py b/homeassistant/components/plugwise/number.py index 9100e006968..0b8c7f820b4 100644 --- a/homeassistant/components/plugwise/number.py +++ b/homeassistant/components/plugwise/number.py @@ -71,7 +71,7 @@ async def async_setup_entry( entities: list[PlugwiseNumberEntity] = [] for device_id, device in coordinator.data.devices.items(): for description in NUMBER_TYPES: - if description.key in device: + if description.key in device and "setpoint" in device[description.key]: entities.append( PlugwiseNumberEntity(coordinator, device_id, description) ) diff --git a/homeassistant/components/plugwise/sensor.py b/homeassistant/components/plugwise/sensor.py index b6e2a3aa413..a59eb0cfc39 100644 --- a/homeassistant/components/plugwise/sensor.py +++ b/homeassistant/components/plugwise/sensor.py @@ -301,6 +301,22 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, ), + SensorEntityDescription( + key="domestic_hot_water_setpoint", + name="DHW setpoint", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key="maximum_boiler_temperature", + name="Maximum boiler temperature", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.MEASUREMENT, + ), ) diff --git a/requirements_all.txt b/requirements_all.txt index 3e8d0bc4aa3..fbc04681462 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1335,7 +1335,7 @@ plexauth==0.0.6 plexwebsocket==0.0.13 # homeassistant.components.plugwise -plugwise==0.25.12 +plugwise==0.25.14 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 300bea75067..8133c7080f1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -962,7 +962,7 @@ plexauth==0.0.6 plexwebsocket==0.0.13 # homeassistant.components.plugwise -plugwise==0.25.12 +plugwise==0.25.14 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/tests/components/plugwise/fixtures/anna_heatpump_heating/all_data.json b/tests/components/plugwise/fixtures/anna_heatpump_heating/all_data.json index 1dc0fb6f2ad..f00293a6554 100644 --- a/tests/components/plugwise/fixtures/anna_heatpump_heating/all_data.json +++ b/tests/components/plugwise/fixtures/anna_heatpump_heating/all_data.json @@ -30,8 +30,9 @@ }, "sensors": { "water_temperature": 29.1, + "domestic_hot_water_setpoint": 60.0, "dhw_temperature": 46.3, - "intended_boiler_temperature": 0.0, + "intended_boiler_temperature": 35.0, "modulation_level": 52, "return_temperature": 25.1, "water_pressure": 1.57, diff --git a/tests/components/plugwise/fixtures/m_anna_heatpump_cooling/all_data.json b/tests/components/plugwise/fixtures/m_anna_heatpump_cooling/all_data.json index cb9e340efb8..ba980a7fce3 100644 --- a/tests/components/plugwise/fixtures/m_anna_heatpump_cooling/all_data.json +++ b/tests/components/plugwise/fixtures/m_anna_heatpump_cooling/all_data.json @@ -30,13 +30,14 @@ "flame_state": false }, "sensors": { - "water_temperature": 29.1, - "dhw_temperature": 46.3, + "water_temperature": 22.7, + "domestic_hot_water_setpoint": 60.0, + "dhw_temperature": 41.5, "intended_boiler_temperature": 0.0, - "modulation_level": 52, - "return_temperature": 25.1, + "modulation_level": 40, + "return_temperature": 23.8, "water_pressure": 1.57, - "outdoor_air_temperature": 3.0 + "outdoor_air_temperature": 28.0 }, "switches": { "dhw_cm_switch": false diff --git a/tests/components/plugwise/test_sensor.py b/tests/components/plugwise/test_sensor.py index 9039c5a476e..b08c2113d80 100644 --- a/tests/components/plugwise/test_sensor.py +++ b/tests/components/plugwise/test_sensor.py @@ -47,6 +47,10 @@ async def test_anna_as_smt_climate_sensor_entities( assert state assert float(state.state) == 29.1 + state = hass.states.get("sensor.opentherm_dhw_setpoint") + assert state + assert float(state.state) == 60.0 + state = hass.states.get("sensor.opentherm_dhw_temperature") assert state assert float(state.state) == 46.3 From 7fc3708f7656e5b75ec82c2df3ff691ff7a09087 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 5 Dec 2022 23:59:54 +0100 Subject: [PATCH 1004/1033] Update frontend to 20221205.0 (#83349) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 74408788d49..76da0335769 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20221201.1"], + "requirements": ["home-assistant-frontend==20221205.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index b63615111cd..52f37d2451f 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -22,7 +22,7 @@ dbus-fast==1.75.0 fnvhash==0.1.0 hass-nabucasa==0.59.0 home-assistant-bluetooth==1.8.1 -home-assistant-frontend==20221201.1 +home-assistant-frontend==20221205.0 httpx==0.23.1 ifaddr==0.1.7 janus==1.0.0 diff --git a/requirements_all.txt b/requirements_all.txt index fbc04681462..d0e23c811d2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -884,7 +884,7 @@ hole==0.7.0 holidays==0.17.2 # homeassistant.components.frontend -home-assistant-frontend==20221201.1 +home-assistant-frontend==20221205.0 # homeassistant.components.home_connect homeconnect==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8133c7080f1..2b66e19dbeb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -664,7 +664,7 @@ hole==0.7.0 holidays==0.17.2 # homeassistant.components.frontend -home-assistant-frontend==20221201.1 +home-assistant-frontend==20221205.0 # homeassistant.components.home_connect homeconnect==0.7.2 From afd27792da5fddcd51728599dea81ce909396b9f Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Mon, 5 Dec 2022 22:34:39 -0500 Subject: [PATCH 1005/1033] Bump ZHA dependencies (#83350) --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index d617b3474f9..8493d8678a7 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -9,7 +9,7 @@ "pyserial-asyncio==0.6", "zha-quirks==0.0.87", "zigpy-deconz==0.19.2", - "zigpy==0.52.2", + "zigpy==0.52.3", "zigpy-xbee==0.16.2", "zigpy-zigate==0.10.3", "zigpy-znp==0.9.2" diff --git a/requirements_all.txt b/requirements_all.txt index d0e23c811d2..67c7e1ff56d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2660,7 +2660,7 @@ zigpy-zigate==0.10.3 zigpy-znp==0.9.2 # homeassistant.components.zha -zigpy==0.52.2 +zigpy==0.52.3 # homeassistant.components.zoneminder zm-py==0.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2b66e19dbeb..8bd3b438994 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1855,7 +1855,7 @@ zigpy-zigate==0.10.3 zigpy-znp==0.9.2 # homeassistant.components.zha -zigpy==0.52.2 +zigpy==0.52.3 # homeassistant.components.zwave_js zwave-js-server-python==0.43.1 From 1f31e621c85cce8ce3ad2d4fef9b0ff3b2f25463 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 6 Dec 2022 04:33:06 +0100 Subject: [PATCH 1006/1033] Fix repairing datetime precision for PostgreSQL (#83351) --- homeassistant/components/recorder/migration.py | 2 +- homeassistant/components/recorder/statistics.py | 13 ++++++++++--- tests/components/recorder/test_statistics.py | 16 ++++++++++++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index cf56c39a885..28952f127e2 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -161,7 +161,7 @@ def migrate_schema( "Database is about to correct DB schema errors: %s", ", ".join(sorted(schema_errors)), ) - statistics_correct_db_schema(engine, session_maker, schema_errors) + statistics_correct_db_schema(instance, engine, session_maker, schema_errors) def _create_index( diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 303a925f9a0..dea454a5a33 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -2404,7 +2404,10 @@ def validate_db_schema( def correct_db_schema( - engine: Engine, session_maker: Callable[[], Session], schema_errors: set[str] + instance: Recorder, + engine: Engine, + session_maker: Callable[[], Session], + schema_errors: set[str], ) -> None: """Correct issues detected by validate_db_schema.""" from .migration import _modify_columns # pylint: disable=import-outside-toplevel @@ -2450,12 +2453,16 @@ def correct_db_schema( ) if f"{table.__tablename__}.µs precision" in schema_errors: # Attempt to convert datetime columns to µs precision + if instance.dialect_name == SupportedDialect.MYSQL: + datetime_type = "DATETIME(6)" + else: + datetime_type = "TIMESTAMP(6) WITH TIME ZONE" _modify_columns( session_maker, engine, table.__tablename__, [ - "last_reset DATETIME(6)", - "start DATETIME(6)", + f"last_reset {datetime_type}", + f"start {datetime_type}", ], ) diff --git a/tests/components/recorder/test_statistics.py b/tests/components/recorder/test_statistics.py index 376087fdb1e..d3a1c8b7fe0 100644 --- a/tests/components/recorder/test_statistics.py +++ b/tests/components/recorder/test_statistics.py @@ -1601,7 +1601,19 @@ async def test_validate_db_schema_fix_float_issue( @pytest.mark.parametrize("enable_statistics_table_validation", [True]) -@pytest.mark.parametrize("db_engine", ("mysql", "postgresql")) +@pytest.mark.parametrize( + "db_engine, modification", + ( + ("mysql", ["last_reset DATETIME(6)", "start DATETIME(6)"]), + ( + "postgresql", + [ + "last_reset TIMESTAMP(6) WITH TIME ZONE", + "start TIMESTAMP(6) WITH TIME ZONE", + ], + ), + ), +) @pytest.mark.parametrize( "table, replace_index", (("statistics", 0), ("statistics_short_term", 1)) ) @@ -1617,6 +1629,7 @@ async def test_validate_db_schema_fix_statistics_datetime_issue( hass, caplog, db_engine, + modification, table, replace_index, column, @@ -1664,7 +1677,6 @@ async def test_validate_db_schema_fix_statistics_datetime_issue( f"Database is about to correct DB schema errors: {table}.µs precision" in caplog.text ) - modification = ["last_reset DATETIME(6)", "start DATETIME(6)"] modify_columns_mock.assert_called_once_with(ANY, ANY, table, modification) From d14655d89188e07716b5f07daf6a4b302041334c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 5 Dec 2022 22:43:59 -0500 Subject: [PATCH 1007/1033] Bumped version to 2022.12.0b5 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 856c0202396..32a0b85ac96 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -8,7 +8,7 @@ from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "0b4" +PATCH_VERSION: Final = "0b5" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/pyproject.toml b/pyproject.toml index b423f299811..4b63472ef20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.12.0b4" +version = "2022.12.0b5" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From 2b0c0cc6d2669e0e3d7c3579cc1029ffc8c71bf9 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Tue, 6 Dec 2022 18:38:13 +0100 Subject: [PATCH 1008/1033] Replace fixtures for Matter tests (#83328) Co-authored-by: Martin Hjelmare --- tests/components/matter/common.py | 150 +- tests/components/matter/conftest.py | 12 + .../matter/fixtures/nodes/contact-sensor.json | 545 +++ .../matter/fixtures/nodes/dimmable-light.json | 4220 +++++++++++++++++ .../fixtures/nodes/fake-bridge-two-light.json | 174 - .../matter/fixtures/nodes/flow-sensor.json | 518 ++ .../fixtures/nodes/humidity-sensor.json | 507 ++ .../matter/fixtures/nodes/light-sensor.json | 595 +++ .../fixtures/nodes/lighting-example-app.json | 882 ---- .../fixtures/nodes/occupancy-sensor.json | 633 +++ .../fixtures/nodes/on-off-plugin-unit.json | 1055 +++++ .../matter/fixtures/nodes/onoff-light.json | 4216 ++++++++++++++++ .../fixtures/nodes/pressure-sensor.json | 507 ++ .../fixtures/nodes/temperature-sensor.json | 574 +++ tests/components/matter/test_light.py | 86 +- 15 files changed, 13476 insertions(+), 1198 deletions(-) create mode 100644 tests/components/matter/fixtures/nodes/contact-sensor.json create mode 100644 tests/components/matter/fixtures/nodes/dimmable-light.json delete mode 100644 tests/components/matter/fixtures/nodes/fake-bridge-two-light.json create mode 100644 tests/components/matter/fixtures/nodes/flow-sensor.json create mode 100644 tests/components/matter/fixtures/nodes/humidity-sensor.json create mode 100644 tests/components/matter/fixtures/nodes/light-sensor.json delete mode 100644 tests/components/matter/fixtures/nodes/lighting-example-app.json create mode 100644 tests/components/matter/fixtures/nodes/occupancy-sensor.json create mode 100644 tests/components/matter/fixtures/nodes/on-off-plugin-unit.json create mode 100644 tests/components/matter/fixtures/nodes/onoff-light.json create mode 100644 tests/components/matter/fixtures/nodes/pressure-sensor.json create mode 100644 tests/components/matter/fixtures/nodes/temperature-sensor.json diff --git a/tests/components/matter/common.py b/tests/components/matter/common.py index d26842a728f..92a4cd4c8f1 100644 --- a/tests/components/matter/common.py +++ b/tests/components/matter/common.py @@ -1,100 +1,19 @@ """Provide common test tools.""" from __future__ import annotations -import asyncio from functools import cache import json -import logging -from typing import TYPE_CHECKING, Any -from unittest.mock import Mock, patch +from typing import Any +from unittest.mock import MagicMock -from matter_server.client import MatterClient +from matter_server.common.helpers.util import dataclass_from_dict +from matter_server.common.models.events import EventType from matter_server.common.models.node import MatterNode -from matter_server.common.models.server_information import ServerInfo -import pytest + +from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry, load_fixture -if TYPE_CHECKING: - from homeassistant.core import HomeAssistant - -MOCK_FABRIC_ID = 12341234 -MOCK_COMPR_FABRIC_ID = 1234 - -# TEMP: Tests need to be fixed -pytestmark = pytest.mark.skip("all tests still WIP") - - -class MockClient(MatterClient): - """Represent a mock Matter client.""" - - mock_client_disconnect: asyncio.Event - mock_commands: dict[type, Any] = {} - mock_sent_commands: list[dict[str, Any]] = [] - - def __init__(self) -> None: - """Initialize the mock client.""" - super().__init__("mock-url", None) - self.mock_commands: dict[type, Any] = {} - self.mock_sent_commands = [] - self.server_info = ServerInfo( - fabric_id=MOCK_FABRIC_ID, compressed_fabric_id=MOCK_COMPR_FABRIC_ID - ) - - async def connect(self) -> None: - """Connect to the Matter server.""" - self.server_info = Mock(compressed_abric_d=MOCK_COMPR_FABRIC_ID) - - async def listen(self, driver_ready: asyncio.Event) -> None: - """Listen for events.""" - driver_ready.set() - self.mock_client_disconnect = asyncio.Event() - await self.mock_client_disconnect.wait() - - def mock_command(self, command_type: type, response: Any) -> None: - """Mock a command.""" - self.mock_commands[command_type] = response - - async def async_send_command( - self, - command: str, - args: dict[str, Any], - require_schema: int | None = None, - ) -> dict: - """Send mock commands.""" - if command == "device_controller.SendCommand" and ( - (cmd_type := type(args.get("payload"))) in self.mock_commands - ): - self.mock_sent_commands.append(args) - return self.mock_commands[cmd_type] - - return await super().async_send_command(command, args, require_schema) - - async def async_send_command_no_wait( - self, command: str, args: dict[str, Any], require_schema: int | None = None - ) -> None: - """Send a command without waiting for the response.""" - if command == "SendCommand" and ( - (cmd_type := type(args.get("payload"))) in self.mock_commands - ): - self.mock_sent_commands.append(args) - return self.mock_commands[cmd_type] - - return await super().async_send_command_no_wait(command, args, require_schema) - - -@pytest.fixture -async def mock_matter() -> Mock: - """Mock matter fixture.""" - return await get_mock_matter() - - -async def get_mock_matter() -> Mock: - """Get mock Matter.""" - return Mock( - adapter=Mock(logger=logging.getLogger("mock_matter")), client=MockClient() - ) - @cache def load_node_fixture(fixture: str) -> str: @@ -108,39 +27,48 @@ def load_and_parse_node_fixture(fixture: str) -> dict[str, Any]: async def setup_integration_with_node_fixture( - hass: HomeAssistant, hass_storage: dict[str, Any], node_fixture: str + hass: HomeAssistant, + node_fixture: str, + client: MagicMock, ) -> MatterNode: """Set up Matter integration with fixture as node.""" node_data = load_and_parse_node_fixture(node_fixture) - node = MatterNode( - await get_mock_matter(), + node = dataclass_from_dict( + MatterNode, node_data, ) + client.get_nodes.return_value = [node] + client.get_node.return_value = node config_entry = MockConfigEntry( domain="matter", data={"url": "http://mock-matter-server-url"} ) config_entry.add_to_hass(hass) - storage_key = f"matter_{config_entry.entry_id}" - hass_storage[storage_key] = { - "version": 1, - "minor_version": 0, - "key": storage_key, - "data": { - "compressed_fabric_id": MOCK_COMPR_FABRIC_ID, - "next_node_id": 4339, - "nodes": {str(node.node_id): node_data}, - }, - } - - with patch( - "matter_server.client.matter.Client", return_value=node.matter.client - ), patch( - "matter_server.client.model.node.MatterDeviceTypeInstance.subscribe_updates", - ), patch( - "matter_server.client.model.node.MatterDeviceTypeInstance.update_attributes" - ): - assert await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() return node + + +def set_node_attribute( + node: MatterNode, + endpoint: int, + cluster_id: int, + attribute_id: int, + value: Any, +) -> None: + """Set a node attribute.""" + attribute = node.attributes[f"{endpoint}/{cluster_id}/{attribute_id}"] + attribute.value = value + + +async def trigger_subscription_callback( + hass: HomeAssistant, + client: MagicMock, + event: EventType = EventType.ATTRIBUTE_UPDATED, + data: Any = None, +) -> None: + """Trigger a subscription callback.""" + callback = client.subscribe.call_args[0][0] + callback(event, data) + await hass.async_block_till_done() diff --git a/tests/components/matter/conftest.py b/tests/components/matter/conftest.py index 6a8ffd152bc..03c8bc35687 100644 --- a/tests/components/matter/conftest.py +++ b/tests/components/matter/conftest.py @@ -5,12 +5,16 @@ import asyncio from collections.abc import AsyncGenerator, Generator from unittest.mock import AsyncMock, MagicMock, patch +from matter_server.common.models.server_information import ServerInfo import pytest from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry +MOCK_FABRIC_ID = 12341234 +MOCK_COMPR_FABRIC_ID = 1234 + @pytest.fixture(name="matter_client") async def matter_client_fixture() -> AsyncGenerator[MagicMock, None]: @@ -32,6 +36,14 @@ async def matter_client_fixture() -> AsyncGenerator[MagicMock, None]: client.connect = AsyncMock(side_effect=connect) client.start_listening = AsyncMock(side_effect=listen) + client.server_info = ServerInfo( + fabric_id=MOCK_FABRIC_ID, + compressed_fabric_id=MOCK_COMPR_FABRIC_ID, + schema_version=1, + sdk_version="2022.11.1", + wifi_credentials_set=True, + thread_credentials_set=True, + ) yield client diff --git a/tests/components/matter/fixtures/nodes/contact-sensor.json b/tests/components/matter/fixtures/nodes/contact-sensor.json new file mode 100644 index 00000000000..6890b38e8f1 --- /dev/null +++ b/tests/components/matter/fixtures/nodes/contact-sensor.json @@ -0,0 +1,545 @@ +{ + "node_id": 1, + "date_commissioned": "2022-11-29T21:23:48.485051", + "last_interview": "2022-11-29T21:23:48.485057", + "interview_version": 1, + "attributes": { + "0/40/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.DataModelRevision", + "attribute_name": "DataModelRevision", + "value": 1 + }, + "0/40/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.VendorName", + "attribute_name": "VendorName", + "value": "Nabu Casa" + }, + "0/40/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.VendorID", + "attribute_name": "VendorID", + "value": 65521 + }, + "0/40/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductName", + "attribute_name": "ProductName", + "value": "Mock ContactSensor" + }, + "0/40/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductID", + "attribute_name": "ProductID", + "value": 32768 + }, + "0/40/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.NodeLabel", + "attribute_name": "NodeLabel", + "value": "Mock Contact sensor" + }, + "0/40/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.Location", + "attribute_name": "Location", + "value": "XX" + }, + "0/40/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.HardwareVersion", + "attribute_name": "HardwareVersion", + "value": 0 + }, + "0/40/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.HardwareVersionString", + "attribute_name": "HardwareVersionString", + "value": "v1.0" + }, + "0/40/9": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 9, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SoftwareVersion", + "attribute_name": "SoftwareVersion", + "value": 1 + }, + "0/40/10": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 10, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SoftwareVersionString", + "attribute_name": "SoftwareVersionString", + "value": "v1.0" + }, + "0/40/11": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 11, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ManufacturingDate", + "attribute_name": "ManufacturingDate", + "value": "20221206" + }, + "0/40/12": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 12, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.PartNumber", + "attribute_name": "PartNumber", + "value": "" + }, + "0/40/13": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 13, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductURL", + "attribute_name": "ProductURL", + "value": "" + }, + "0/40/14": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 14, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductLabel", + "attribute_name": "ProductLabel", + "value": "" + }, + "0/40/15": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 15, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SerialNumber", + "attribute_name": "SerialNumber", + "value": "TEST_SN" + }, + "0/40/16": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 16, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.LocalConfigDisabled", + "attribute_name": "LocalConfigDisabled", + "value": false + }, + "0/40/17": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 17, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.Reachable", + "attribute_name": "Reachable", + "value": true + }, + "0/40/18": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 18, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.UniqueID", + "attribute_name": "UniqueID", + "value": "mock-contact-sensor" + }, + "0/40/19": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 19, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.CapabilityMinima", + "attribute_name": "CapabilityMinima", + "value": { + "caseSessionsPerFabric": 3, + "subscriptionsPerFabric": 3 + } + }, + "0/40/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/40/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/40/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/40/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/40/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 65528, 65529, 65531, 65532, 65533 + ] + }, + + "1/3/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.IdentifyTime", + "attribute_name": "IdentifyTime", + "value": 0 + }, + "1/3/1": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.IdentifyType", + "attribute_name": "IdentifyType", + "value": 2 + }, + "1/3/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "1/3/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 4 + }, + "1/3/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "1/3/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 64] + }, + "1/3/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 65528, 65529, 65531, 65532, 65533] + }, + + "1/29/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 21, + "revision": 1 + } + ] + }, + "1/29/1": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 3, 4, 5, 6, 7, 8, 15, 29, 30, 37, 47, 59, 64, 65, 69, 80, 257, 258, 259, + 512, 513, 514, 516, 768, 1024, 1026, 1027, 1028, 1029, 1030, 1283, 1284, + 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 2820, + 4294048773 + ] + }, + "1/29/2": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [] + }, + "1/29/3": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [] + }, + "1/29/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "1/29/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "1/29/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "1/29/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "1/29/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, + "1/69/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 69, + "cluster_type": "chip.clusters.Objects.BooleanState", + "cluster_name": "BooleanState", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.BooleanState.Attributes.StateValue", + "attribute_name": "StateValue", + "value": true + }, + "1/69/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 69, + "cluster_type": "chip.clusters.Objects.BooleanState", + "cluster_name": "BooleanState", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.BooleanState.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "1/69/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 69, + "cluster_type": "chip.clusters.Objects.BooleanState", + "cluster_name": "BooleanState", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.BooleanState.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "1/69/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 69, + "cluster_type": "chip.clusters.Objects.BooleanState", + "cluster_name": "BooleanState", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.BooleanState.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "1/69/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 69, + "cluster_type": "chip.clusters.Objects.BooleanState", + "cluster_name": "BooleanState", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.BooleanState.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "1/69/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 69, + "cluster_type": "chip.clusters.Objects.BooleanState", + "cluster_name": "BooleanState", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.BooleanState.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 65528, 65529, 65531, 65532, 65533] + } + }, + "endpoints": [0, 1], + "_type": "matter_server.common.models.node.MatterNode" +} diff --git a/tests/components/matter/fixtures/nodes/dimmable-light.json b/tests/components/matter/fixtures/nodes/dimmable-light.json new file mode 100644 index 00000000000..03067468f24 --- /dev/null +++ b/tests/components/matter/fixtures/nodes/dimmable-light.json @@ -0,0 +1,4220 @@ +{ + "node_id": 1, + "date_commissioned": "2022-11-29T21:23:48.485051", + "last_interview": "2022-11-29T21:23:48.485057", + "interview_version": 1, + "attributes": { + "0/4/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.NameSupport", + "attribute_name": "NameSupport", + "value": 128 + }, + "0/4/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 1 + }, + "0/4/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 4 + }, + "0/4/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [0, 1, 2, 3] + }, + "0/4/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 1, 2, 3, 4, 5] + }, + "0/4/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 65528, 65529, 65531, 65532, 65533] + }, + "0/29/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 22, + "revision": 1 + } + ] + }, + "0/29/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 4, 29, 31, 40, 42, 43, 44, 48, 49, 50, 51, 52, 53, 54, 55, 59, 60, 62, + 63, 64, 65 + ] + }, + "0/29/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [41] + }, + "0/29/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [1] + }, + "0/29/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/29/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/29/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/29/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/29/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, + "0/31/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.Acl", + "attribute_name": "Acl", + "value": [ + { + "privilege": 5, + "authMode": 2, + "subjects": [112233], + "targets": null, + "fabricIndex": 1 + } + ] + }, + "0/31/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.Extension", + "attribute_name": "Extension", + "value": [] + }, + "0/31/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.SubjectsPerAccessControlEntry", + "attribute_name": "SubjectsPerAccessControlEntry", + "value": 4 + }, + "0/31/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.TargetsPerAccessControlEntry", + "attribute_name": "TargetsPerAccessControlEntry", + "value": 3 + }, + "0/31/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.AccessControlEntriesPerFabric", + "attribute_name": "AccessControlEntriesPerFabric", + "value": 3 + }, + "0/31/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/31/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/31/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/31/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/31/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533] + }, + "0/40/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.DataModelRevision", + "attribute_name": "DataModelRevision", + "value": 1 + }, + "0/40/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.VendorName", + "attribute_name": "VendorName", + "value": "Nabu Casa" + }, + "0/40/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.VendorID", + "attribute_name": "VendorID", + "value": 65521 + }, + "0/40/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductName", + "attribute_name": "ProductName", + "value": "Mock Dimmable Light" + }, + "0/40/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductID", + "attribute_name": "ProductID", + "value": 32768 + }, + "0/40/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.NodeLabel", + "attribute_name": "NodeLabel", + "value": "Mock Dimmable Light" + }, + "0/40/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.Location", + "attribute_name": "Location", + "value": "XX" + }, + "0/40/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.HardwareVersion", + "attribute_name": "HardwareVersion", + "value": 0 + }, + "0/40/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.HardwareVersionString", + "attribute_name": "HardwareVersionString", + "value": "v1.0" + }, + "0/40/9": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 9, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SoftwareVersion", + "attribute_name": "SoftwareVersion", + "value": 1 + }, + "0/40/10": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 10, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SoftwareVersionString", + "attribute_name": "SoftwareVersionString", + "value": "v1.0" + }, + "0/40/11": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 11, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ManufacturingDate", + "attribute_name": "ManufacturingDate", + "value": "20200101" + }, + "0/40/12": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 12, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.PartNumber", + "attribute_name": "PartNumber", + "value": "" + }, + "0/40/13": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 13, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductURL", + "attribute_name": "ProductURL", + "value": "" + }, + "0/40/14": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 14, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductLabel", + "attribute_name": "ProductLabel", + "value": "" + }, + "0/40/15": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 15, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SerialNumber", + "attribute_name": "SerialNumber", + "value": "TEST_SN" + }, + "0/40/16": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 16, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.LocalConfigDisabled", + "attribute_name": "LocalConfigDisabled", + "value": false + }, + "0/40/17": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 17, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.Reachable", + "attribute_name": "Reachable", + "value": true + }, + "0/40/18": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 18, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.UniqueID", + "attribute_name": "UniqueID", + "value": "mock-dimmable-light" + }, + "0/40/19": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 19, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.CapabilityMinima", + "attribute_name": "CapabilityMinima", + "value": { + "caseSessionsPerFabric": 3, + "subscriptionsPerFabric": 3 + } + }, + "0/40/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/40/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/40/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/40/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/40/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 65528, 65529, 65531, 65532, 65533 + ] + }, + "0/42/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.DefaultOtaProviders", + "attribute_name": "DefaultOtaProviders", + "value": [] + }, + "0/42/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.UpdatePossible", + "attribute_name": "UpdatePossible", + "value": true + }, + "0/42/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.UpdateState", + "attribute_name": "UpdateState", + "value": 0 + }, + "0/42/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.UpdateStateProgress", + "attribute_name": "UpdateStateProgress", + "value": 0 + }, + "0/42/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/42/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/42/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/42/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0] + }, + "0/42/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, + "0/43/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.ActiveLocale", + "attribute_name": "ActiveLocale", + "value": "en-US" + }, + "0/43/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.SupportedLocales", + "attribute_name": "SupportedLocales", + "value": [ + "en-US", + "de-DE", + "fr-FR", + "en-GB", + "es-ES", + "zh-CN", + "it-IT", + "ja-JP" + ] + }, + "0/43/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/43/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/43/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/43/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/43/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 65528, 65529, 65531, 65532, 65533] + }, + "0/44/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.HourFormat", + "attribute_name": "HourFormat", + "value": 0 + }, + "0/44/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.ActiveCalendarType", + "attribute_name": "ActiveCalendarType", + "value": 0 + }, + "0/44/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.SupportedCalendarTypes", + "attribute_name": "SupportedCalendarTypes", + "value": [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 7] + }, + "0/44/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/44/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/44/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/44/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/44/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 65528, 65529, 65531, 65532, 65533] + }, + "0/48/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.Breadcrumb", + "attribute_name": "Breadcrumb", + "value": 0 + }, + "0/48/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.BasicCommissioningInfo", + "attribute_name": "BasicCommissioningInfo", + "value": { + "failSafeExpiryLengthSeconds": 60, + "maxCumulativeFailsafeSeconds": 900 + } + }, + "0/48/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.RegulatoryConfig", + "attribute_name": "RegulatoryConfig", + "value": 0 + }, + "0/48/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.LocationCapability", + "attribute_name": "LocationCapability", + "value": 0 + }, + "0/48/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.SupportsConcurrentConnection", + "attribute_name": "SupportsConcurrentConnection", + "value": true + }, + "0/48/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/48/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/48/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [1, 3, 5] + }, + "0/48/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 2, 4] + }, + "0/48/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533] + }, + "0/49/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.MaxNetworks", + "attribute_name": "MaxNetworks", + "value": 1 + }, + "0/49/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.Networks", + "attribute_name": "Networks", + "value": [ + { + "networkID": "b'wifi-sm'", + "connected": true + } + ] + }, + "0/49/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.ScanMaxTimeSeconds", + "attribute_name": "ScanMaxTimeSeconds", + "value": 10 + }, + "0/49/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.ConnectMaxTimeSeconds", + "attribute_name": "ConnectMaxTimeSeconds", + "value": 30 + }, + "0/49/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.InterfaceEnabled", + "attribute_name": "InterfaceEnabled", + "value": true + }, + "0/49/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.LastNetworkingStatus", + "attribute_name": "LastNetworkingStatus", + "value": 0 + }, + "0/49/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.LastNetworkID", + "attribute_name": "LastNetworkID", + "value": "b'wifi-sm'" + }, + "0/49/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.LastConnectErrorValue", + "attribute_name": "LastConnectErrorValue", + "value": null + }, + "0/49/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 1 + }, + "0/49/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/49/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [1, 5, 7] + }, + "0/49/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 2, 4, 6, 8] + }, + "0/49/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 4, 5, 6, 7, 65528, 65529, 65531, 65532, 65533] + }, + "0/50/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 50, + "cluster_type": "chip.clusters.Objects.DiagnosticLogs", + "cluster_name": "DiagnosticLogs", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.DiagnosticLogs.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/50/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 50, + "cluster_type": "chip.clusters.Objects.DiagnosticLogs", + "cluster_name": "DiagnosticLogs", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.DiagnosticLogs.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/50/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 50, + "cluster_type": "chip.clusters.Objects.DiagnosticLogs", + "cluster_name": "DiagnosticLogs", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.DiagnosticLogs.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [1] + }, + "0/50/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 50, + "cluster_type": "chip.clusters.Objects.DiagnosticLogs", + "cluster_name": "DiagnosticLogs", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.DiagnosticLogs.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0] + }, + "0/50/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 50, + "cluster_type": "chip.clusters.Objects.DiagnosticLogs", + "cluster_name": "DiagnosticLogs", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.DiagnosticLogs.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [65528, 65529, 65531, 65532, 65533] + }, + "0/51/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.NetworkInterfaces", + "attribute_name": "NetworkInterfaces", + "value": [ + { + "name": "WIFI_STA_DEF", + "isOperational": true, + "offPremiseServicesReachableIPv4": null, + "offPremiseServicesReachableIPv6": null, + "hardwareAddress": "b\"\\x84\\xf7\\x03'\\xb5,\"", + "IPv4Addresses": ["b'\\xc0\\xa8\\x01\\x84'"], + "IPv6Addresses": [ + "b\"\\xfe\\x80\\x00\\x00\\x00\\x00\\x00\\x00\\x86\\xf7\\x03\\xff\\xfe'\\xb5,\"", + "b\"*\\x00\\xbb\\xa0\\x12\\x12\\x8f\\x00\\x86\\xf7\\x03\\xff\\xfe'\\xb5,\"", + "b\"\\xfd\\xf5\\nn\\xf0cO\\xed\\x86\\xf7\\x03\\xff\\xfe'\\xb5,\"" + ], + "type": 1 + } + ] + }, + "0/51/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.RebootCount", + "attribute_name": "RebootCount", + "value": 6 + }, + "0/51/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.UpTime", + "attribute_name": "UpTime", + "value": 31279 + }, + "0/51/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.TotalOperationalHours", + "attribute_name": "TotalOperationalHours", + "value": 8 + }, + "0/51/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.BootReasons", + "attribute_name": "BootReasons", + "value": 1 + }, + "0/51/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.ActiveHardwareFaults", + "attribute_name": "ActiveHardwareFaults", + "value": [] + }, + "0/51/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.ActiveRadioFaults", + "attribute_name": "ActiveRadioFaults", + "value": [] + }, + "0/51/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.ActiveNetworkFaults", + "attribute_name": "ActiveNetworkFaults", + "value": [] + }, + "0/51/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.TestEventTriggersEnabled", + "attribute_name": "TestEventTriggersEnabled", + "value": false + }, + "0/51/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/51/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/51/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/51/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0] + }, + "0/51/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 4, 5, 6, 7, 8, 65528, 65529, 65531, 65532, 65533] + }, + "0/52/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.ThreadMetrics", + "attribute_name": "ThreadMetrics", + "value": [] + }, + "0/52/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.CurrentHeapFree", + "attribute_name": "CurrentHeapFree", + "value": 166480 + }, + "0/52/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.CurrentHeapUsed", + "attribute_name": "CurrentHeapUsed", + "value": 86512 + }, + "0/52/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.CurrentHeapHighWatermark", + "attribute_name": "CurrentHeapHighWatermark", + "value": 157052 + }, + "0/52/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/52/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/52/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/52/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/52/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, + "0/53/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.Channel", + "attribute_name": "Channel", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RoutingRole", + "attribute_name": "RoutingRole", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.NetworkName", + "attribute_name": "NetworkName", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.PanId", + "attribute_name": "PanId", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ExtendedPanId", + "attribute_name": "ExtendedPanId", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.MeshLocalPrefix", + "attribute_name": "MeshLocalPrefix", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.OverrunCount", + "attribute_name": "OverrunCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.NeighborTableList", + "attribute_name": "NeighborTableList", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RouteTableList", + "attribute_name": "RouteTableList", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/9": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 9, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.PartitionId", + "attribute_name": "PartitionId", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/10": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 10, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.Weighting", + "attribute_name": "Weighting", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/11": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 11, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.DataVersion", + "attribute_name": "DataVersion", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/12": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 12, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.StableDataVersion", + "attribute_name": "StableDataVersion", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/13": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 13, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.LeaderRouterId", + "attribute_name": "LeaderRouterId", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/14": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 14, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.DetachedRoleCount", + "attribute_name": "DetachedRoleCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/15": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 15, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ChildRoleCount", + "attribute_name": "ChildRoleCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/16": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 16, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RouterRoleCount", + "attribute_name": "RouterRoleCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/17": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 17, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.LeaderRoleCount", + "attribute_name": "LeaderRoleCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/18": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 18, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.AttachAttemptCount", + "attribute_name": "AttachAttemptCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/19": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 19, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.PartitionIdChangeCount", + "attribute_name": "PartitionIdChangeCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/20": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 20, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.BetterPartitionAttachAttemptCount", + "attribute_name": "BetterPartitionAttachAttemptCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/21": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 21, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ParentChangeCount", + "attribute_name": "ParentChangeCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/22": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 22, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxTotalCount", + "attribute_name": "TxTotalCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/23": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 23, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxUnicastCount", + "attribute_name": "TxUnicastCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/24": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 24, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxBroadcastCount", + "attribute_name": "TxBroadcastCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/25": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 25, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxAckRequestedCount", + "attribute_name": "TxAckRequestedCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/26": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 26, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxAckedCount", + "attribute_name": "TxAckedCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/27": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 27, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxNoAckRequestedCount", + "attribute_name": "TxNoAckRequestedCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/28": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 28, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxDataCount", + "attribute_name": "TxDataCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/29": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 29, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxDataPollCount", + "attribute_name": "TxDataPollCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/30": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 30, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxBeaconCount", + "attribute_name": "TxBeaconCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/31": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 31, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxBeaconRequestCount", + "attribute_name": "TxBeaconRequestCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/32": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 32, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxOtherCount", + "attribute_name": "TxOtherCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/33": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 33, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxRetryCount", + "attribute_name": "TxRetryCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/34": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 34, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxDirectMaxRetryExpiryCount", + "attribute_name": "TxDirectMaxRetryExpiryCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/35": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 35, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxIndirectMaxRetryExpiryCount", + "attribute_name": "TxIndirectMaxRetryExpiryCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/36": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 36, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxErrCcaCount", + "attribute_name": "TxErrCcaCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/37": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 37, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxErrAbortCount", + "attribute_name": "TxErrAbortCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/38": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 38, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxErrBusyChannelCount", + "attribute_name": "TxErrBusyChannelCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/39": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 39, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxTotalCount", + "attribute_name": "RxTotalCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/40": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 40, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxUnicastCount", + "attribute_name": "RxUnicastCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/41": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 41, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxBroadcastCount", + "attribute_name": "RxBroadcastCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/42": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 42, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxDataCount", + "attribute_name": "RxDataCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/43": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 43, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxDataPollCount", + "attribute_name": "RxDataPollCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/44": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 44, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxBeaconCount", + "attribute_name": "RxBeaconCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/45": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 45, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxBeaconRequestCount", + "attribute_name": "RxBeaconRequestCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/46": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 46, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxOtherCount", + "attribute_name": "RxOtherCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/47": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 47, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxAddressFilteredCount", + "attribute_name": "RxAddressFilteredCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/48": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 48, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxDestAddrFilteredCount", + "attribute_name": "RxDestAddrFilteredCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/49": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 49, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxDuplicatedCount", + "attribute_name": "RxDuplicatedCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/50": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 50, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxErrNoFrameCount", + "attribute_name": "RxErrNoFrameCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/51": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 51, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxErrUnknownNeighborCount", + "attribute_name": "RxErrUnknownNeighborCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/52": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 52, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxErrInvalidSrcAddrCount", + "attribute_name": "RxErrInvalidSrcAddrCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/53": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 53, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxErrSecCount", + "attribute_name": "RxErrSecCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/54": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 54, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxErrFcsCount", + "attribute_name": "RxErrFcsCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/55": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 55, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxErrOtherCount", + "attribute_name": "RxErrOtherCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/56": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 56, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ActiveTimestamp", + "attribute_name": "ActiveTimestamp", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/57": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 57, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.PendingTimestamp", + "attribute_name": "PendingTimestamp", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/58": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 58, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.Delay", + "attribute_name": "Delay", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/59": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 59, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.SecurityPolicy", + "attribute_name": "SecurityPolicy", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/60": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 60, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ChannelPage0Mask", + "attribute_name": "ChannelPage0Mask", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/61": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 61, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.OperationalDatasetComponents", + "attribute_name": "OperationalDatasetComponents", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/62": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 62, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ActiveNetworkFaultsList", + "attribute_name": "ActiveNetworkFaultsList", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 15 + }, + "0/53/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/53/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/53/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0] + }, + "0/53/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 65528, 65529, 65531, 65532, 65533 + ] + }, + "0/54/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.Bssid", + "attribute_name": "Bssid", + "value": "b'r\\xa7A\\x91\\xf1!'" + }, + "0/54/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.SecurityType", + "attribute_name": "SecurityType", + "value": 4 + }, + "0/54/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.WiFiVersion", + "attribute_name": "WiFiVersion", + "value": 3 + }, + "0/54/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.ChannelNumber", + "attribute_name": "ChannelNumber", + "value": 6 + }, + "0/54/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.Rssi", + "attribute_name": "Rssi", + "value": -61 + }, + "0/54/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.BeaconLostCount", + "attribute_name": "BeaconLostCount", + "value": null + }, + "0/54/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.BeaconRxCount", + "attribute_name": "BeaconRxCount", + "value": null + }, + "0/54/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.PacketMulticastRxCount", + "attribute_name": "PacketMulticastRxCount", + "value": null + }, + "0/54/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.PacketMulticastTxCount", + "attribute_name": "PacketMulticastTxCount", + "value": null + }, + "0/54/9": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 9, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.PacketUnicastRxCount", + "attribute_name": "PacketUnicastRxCount", + "value": null + }, + "0/54/10": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 10, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.PacketUnicastTxCount", + "attribute_name": "PacketUnicastTxCount", + "value": null + }, + "0/54/11": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 11, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.CurrentMaxRate", + "attribute_name": "CurrentMaxRate", + "value": null + }, + "0/54/12": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 12, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.OverrunCount", + "attribute_name": "OverrunCount", + "value": null + }, + "0/54/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 3 + }, + "0/54/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/54/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/54/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0] + }, + "0/54/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 65528, 65529, 65531, 65532, + 65533 + ] + }, + "0/55/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.PHYRate", + "attribute_name": "PHYRate", + "value": null + }, + "0/55/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.FullDuplex", + "attribute_name": "FullDuplex", + "value": null + }, + "0/55/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.PacketRxCount", + "attribute_name": "PacketRxCount", + "value": 0 + }, + "0/55/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.PacketTxCount", + "attribute_name": "PacketTxCount", + "value": 0 + }, + "0/55/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.TxErrCount", + "attribute_name": "TxErrCount", + "value": 0 + }, + "0/55/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.CollisionCount", + "attribute_name": "CollisionCount", + "value": 0 + }, + "0/55/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.OverrunCount", + "attribute_name": "OverrunCount", + "value": 0 + }, + "0/55/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.CarrierDetect", + "attribute_name": "CarrierDetect", + "value": null + }, + "0/55/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.TimeSinceReset", + "attribute_name": "TimeSinceReset", + "value": 0 + }, + "0/55/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 3 + }, + "0/55/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/55/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/55/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0] + }, + "0/55/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 4, 5, 6, 7, 8, 65528, 65529, 65531, 65532, 65533] + }, + "0/59/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 59, + "cluster_type": "chip.clusters.Objects.Switch", + "cluster_name": "Switch", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Switch.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/59/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 59, + "cluster_type": "chip.clusters.Objects.Switch", + "cluster_name": "Switch", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Switch.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/59/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 59, + "cluster_type": "chip.clusters.Objects.Switch", + "cluster_name": "Switch", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Switch.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/59/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 59, + "cluster_type": "chip.clusters.Objects.Switch", + "cluster_name": "Switch", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Switch.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/59/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 59, + "cluster_type": "chip.clusters.Objects.Switch", + "cluster_name": "Switch", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Switch.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [65528, 65529, 65531, 65532, 65533] + }, + "0/60/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.WindowStatus", + "attribute_name": "WindowStatus", + "value": 0 + }, + "0/60/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.AdminFabricIndex", + "attribute_name": "AdminFabricIndex", + "value": null + }, + "0/60/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.AdminVendorId", + "attribute_name": "AdminVendorId", + "value": null + }, + "0/60/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/60/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/60/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/60/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 1, 2] + }, + "0/60/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 65528, 65529, 65531, 65532, 65533] + }, + "0/62/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 62, + "cluster_type": "chip.clusters.Objects.OperationalCredentials", + "cluster_name": "OperationalCredentials", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.OperationalCredentials.Attributes.NOCs", + "attribute_name": "NOCs", + "value": [ + { + "noc": "b'\\x150\\x01\\x01\\x01$\\x02\\x017\\x03$\\x13\\x02\\x18&\\x04\\x80\"\\x81\\'&\\x05\\x80%M:7\\x06$\\x15\\x01$\\x11\\x01\\x18$\\x07\\x01$\\x08\\x010\\tA\\x04Z\\xec\\xd9\\x11h\\'J[%&\\xe1\\xd8\\xa0\\xb15\\x01\\x9c\\x83\\xf8\\x1a\\x9bg\\xbb\\x81e\\x18\\x8b\\xc8\\x9dr\\xed\\xe7\\xc3\\\\\\xd21\\x99\\xc1\\xdb\\xfe\\xd0}\\xf5)\\xaeMq\\xd5\\xeeRg\\x16\\xd5%\\x9c\\xfcD\\t\\xcbQ\\xc8\\x83*\\x997\\n5\\x01(\\x01\\x18$\\x02\\x016\\x03\\x04\\x02\\x04\\x01\\x180\\x04\\x14\\xca\\xe2\\x07\\xdb\\x89, got " - }, - "WiFiNetworkDiagnostics": { - "bssid": { - "_type": "bytes", - "value": "1iH5ZUbu" - }, - "securityType": 4, - "wiFiVersion": 3, - "channelNumber": 1, - "rssi": -38, - "beaconLostCount": 0, - "beaconRxCount": 0, - "packetMulticastRxCount": 0, - "packetMulticastTxCount": 0, - "packetUnicastRxCount": 0, - "packetUnicastTxCount": 0, - "currentMaxRate": 0, - "overrunCount": 0, - "generatedCommandList": [], - "acceptedCommandList": [0], - "attributeList": [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 65528, 65529, 65531, 65532, - 65533 - ], - "featureMap": 3, - "clusterRevision": 1, - "_type": "chip.clusters.Objects.WiFiNetworkDiagnostics" - }, - "EthernetNetworkDiagnostics": { - "PHYRate": null, - "fullDuplex": null, - "packetRxCount": 0, - "packetTxCount": 0, - "txErrCount": 0, - "collisionCount": 0, - "overrunCount": 0, - "carrierDetect": null, - "timeSinceReset": 0, - "generatedCommandList": [], - "acceptedCommandList": [0], - "attributeList": [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 65528, 65529, 65531, 65532, 65533 - ], - "featureMap": 3, - "clusterRevision": 1, - "_type": "chip.clusters.Objects.EthernetNetworkDiagnostics" - }, - "Switch": { - "numberOfPositions": null, - "currentPosition": null, - "multiPressMax": null, - "generatedCommandList": [], - "acceptedCommandList": [], - "attributeList": [65528, 65529, 65531, 65532, 65533], - "featureMap": 0, - "clusterRevision": 1, - "_type": "chip.clusters.Objects.Switch" - }, - "AdministratorCommissioning": { - "windowStatus": 0, - "adminFabricIndex": 0, - "adminVendorId": 0, - "generatedCommandList": [], - "acceptedCommandList": [0, 1, 2], - "attributeList": [0, 1, 2, 65528, 65529, 65531, 65532, 65533], - "featureMap": 0, - "clusterRevision": 1, - "_type": "chip.clusters.Objects.AdministratorCommissioning" - }, - "OperationalCredentials": { - "NOCs": [ - { - "noc": { - "_type": "bytes", - "value": "FTABAQEkAgE3AyQTARgmBIAigScmBYAlTTo3BiQVASUR8RAYJAcBJAgBMAlBBHQsjZ/8Hpm4iqznEv0dAO03bZx8LDgqpIOpBsHeysZu8KAmI0K+p6B8FuI1h3wld1V+tIj5OHVHtrigg6Ssl043CjUBKAEYJAIBNgMEAgQBGDAEFEWrZiyeoUgEIXz4c40+Nzq9cfxHMAUUSTs2LnMMrX7nj+dns0cSq3SmK3MYMAtAoFdxyvsbLm6VekNCQ6yqJOucAcRSVY3Si4ov1alKPK9CaIPl+u5dvBWNfyEPXSLsPmzyfd2njl8WRz8e7CBiSRg=" - }, - "icac": { - "_type": "bytes", - "value": "FTABAQAkAgE3AyQUABgmBIAigScmBYAlTTo3BiQTARgkBwEkCAEwCUEE09c6S9xVbf3/blpXSgRAZzKXx/4KQC274cEfa2tFjdVAJYJUvM/8PMurRHEroPpA3FXpJ8/hfabkNvHGi2l8tTcKNQEpARgkAmAwBBRJOzYucwytfueP52ezRxKrdKYrczAFFBf0ohq+KHQlEVBIMgEeZCBPR72hGDALQNwd63sOjWKYhjlvDJmcPtIzljSsXlQ10vFrB5j9V9CdiZHDfy537G39fo0RJmpU63EGXYEtXVrEfSMiafshKVcY" - }, - "fabricIndex": 1, - "_type": "chip.clusters.Objects.OperationalCredentials.Structs.NOCStruct" - } - ], - "fabrics": [ - { - "rootPublicKey": { - "_type": "bytes", - "value": "BBGg+O3i3tDVYryXkUmEXk1fnSMHN06+poGIfZODdvbZW4JvxHnrQVAxvZWIE6poLa0sKA8X8A7jmJsVFMUqLFM=" - }, - "vendorId": 35328, - "fabricId": 1, - "nodeId": 4337, - "label": "", - "fabricIndex": 1, - "_type": "chip.clusters.Objects.OperationalCredentials.Structs.FabricDescriptor" - } - ], - "supportedFabrics": 5, - "commissionedFabrics": 1, - "trustedRootCertificates": [ - { - "_type": "bytes", - "value": "FTABAQAkAgE3AyQUABgmBIAigScmBYAlTTo3BiQUABgkBwEkCAEwCUEEEaD47eLe0NVivJeRSYReTV+dIwc3Tr6mgYh9k4N29tlbgm/EeetBUDG9lYgTqmgtrSwoDxfwDuOYmxUUxSosUzcKNQEpARgkAmAwBBQX9KIavih0JRFQSDIBHmQgT0e9oTAFFBf0ohq+KHQlEVBIMgEeZCBPR72hGDALQO3xFiF2cEXl+/kk0CQfedzHJxSJiziHEjWCMjIj7SVlDVx4CpvNYHnheq+9vJFgcL8JQhAEdz6p6C3INBDL7dsY" - } - ], - "currentFabricIndex": 1, - "generatedCommandList": [1, 3, 5, 8], - "acceptedCommandList": [0, 2, 4, 6, 7, 9, 10, 11], - "attributeList": [0, 1, 2, 3, 4, 5, 65528, 65529, 65531, 65532, 65533], - "featureMap": 0, - "clusterRevision": 1, - "_type": "chip.clusters.Objects.OperationalCredentials" - }, - "GroupKeyManagement": { - "groupKeyMap": [], - "groupTable": [], - "maxGroupsPerFabric": 3, - "maxGroupKeysPerFabric": 2, - "generatedCommandList": [2, 5], - "acceptedCommandList": [0, 1, 3, 4], - "attributeList": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533], - "featureMap": 0, - "clusterRevision": 1, - "_type": "chip.clusters.Objects.GroupKeyManagement" - }, - "FixedLabel": { - "labelList": [ - { - "label": "room", - "value": "bedroom 2", - "_type": "chip.clusters.Objects.FixedLabel.Structs.LabelStruct" - }, - { - "label": "orientation", - "value": "North", - "_type": "chip.clusters.Objects.FixedLabel.Structs.LabelStruct" - }, - { - "label": "floor", - "value": "2", - "_type": "chip.clusters.Objects.FixedLabel.Structs.LabelStruct" - }, - { - "label": "direction", - "value": "up", - "_type": "chip.clusters.Objects.FixedLabel.Structs.LabelStruct" - } - ], - "generatedCommandList": [], - "acceptedCommandList": [], - "attributeList": [0, 65528, 65529, 65531, 65532, 65533], - "featureMap": 0, - "clusterRevision": 1, - "_type": "chip.clusters.Objects.FixedLabel" - }, - "UserLabel": { - "labelList": [], - "generatedCommandList": [], - "acceptedCommandList": [], - "attributeList": [0, 65528, 65529, 65531, 65532, 65533], - "featureMap": 0, - "clusterRevision": 1, - "_type": "chip.clusters.Objects.UserLabel" - } - }, - "1": { - "Identify": { - "identifyTime": 0, - "identifyType": 0, - "generatedCommandList": [], - "acceptedCommandList": [0, 64], - "attributeList": [0, 1, 65528, 65529, 65531, 65532, 65533], - "featureMap": 0, - "clusterRevision": 4, - "_type": "chip.clusters.Objects.Identify" - }, - "Groups": { - "nameSupport": 128, - "generatedCommandList": [0, 1, 2, 3], - "acceptedCommandList": [0, 1, 2, 3, 4, 5], - "attributeList": [0, 65528, 65529, 65531, 65532, 65533], - "featureMap": 0, - "clusterRevision": 4, - "_type": "chip.clusters.Objects.Groups" - }, - "OnOff": { - "onOff": false, - "globalSceneControl": true, - "onTime": 0, - "offWaitTime": 0, - "startUpOnOff": null, - "generatedCommandList": [], - "acceptedCommandList": [0, 1, 2, 64, 65, 66], - "attributeList": [ - 0, 16384, 16385, 16386, 16387, 65528, 65529, 65531, 65532, 65533 - ], - "featureMap": 1, - "clusterRevision": 4, - "_type": "chip.clusters.Objects.OnOff" - }, - "LevelControl": { - "currentLevel": 254, - "remainingTime": 0, - "minLevel": 0, - "maxLevel": 254, - "currentFrequency": 0, - "minFrequency": 0, - "maxFrequency": 0, - "options": 0, - "onOffTransitionTime": 0, - "onLevel": null, - "onTransitionTime": 0, - "offTransitionTime": 0, - "defaultMoveRate": 50, - "startUpCurrentLevel": null, - "generatedCommandList": [], - "acceptedCommandList": [0, 1, 2, 3, 4, 5, 6, 7], - "attributeList": [ - 0, 1, 2, 3, 4, 5, 6, 15, 16, 17, 18, 19, 20, 16384, 65528, 65529, - 65531, 65532, 65533 - ], - "featureMap": 3, - "clusterRevision": 5, - "_type": "chip.clusters.Objects.LevelControl" - }, - "Descriptor": { - "deviceTypeList": [ - { - "type": 257, - "revision": 1, - "_type": "chip.clusters.Objects.Descriptor.Structs.DeviceTypeStruct" - } - ], - "serverList": [3, 4, 6, 8, 29, 768, 1030], - "clientList": [], - "partsList": [], - "generatedCommandList": [], - "acceptedCommandList": [], - "attributeList": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533], - "featureMap": 0, - "clusterRevision": 1, - "_type": "chip.clusters.Objects.Descriptor" - }, - "ColorControl": { - "currentHue": 0, - "currentSaturation": 0, - "remainingTime": 0, - "currentX": 24939, - "currentY": 24701, - "driftCompensation": null, - "compensationText": null, - "colorTemperatureMireds": 0, - "colorMode": 2, - "options": 0, - "numberOfPrimaries": 0, - "primary1X": null, - "primary1Y": null, - "primary1Intensity": null, - "primary2X": null, - "primary2Y": null, - "primary2Intensity": null, - "primary3X": null, - "primary3Y": null, - "primary3Intensity": null, - "primary4X": null, - "primary4Y": null, - "primary4Intensity": null, - "primary5X": null, - "primary5Y": null, - "primary5Intensity": null, - "primary6X": null, - "primary6Y": null, - "primary6Intensity": null, - "whitePointX": null, - "whitePointY": null, - "colorPointRX": null, - "colorPointRY": null, - "colorPointRIntensity": null, - "colorPointGX": null, - "colorPointGY": null, - "colorPointGIntensity": null, - "colorPointBX": null, - "colorPointBY": null, - "colorPointBIntensity": null, - "enhancedCurrentHue": 0, - "enhancedColorMode": 2, - "colorLoopActive": 0, - "colorLoopDirection": 0, - "colorLoopTime": 25, - "colorLoopStartEnhancedHue": 8960, - "colorLoopStoredEnhancedHue": 0, - "colorCapabilities": 0, - "colorTempPhysicalMinMireds": 0, - "colorTempPhysicalMaxMireds": 65279, - "coupleColorTempToLevelMinMireds": 0, - "startUpColorTemperatureMireds": 0, - "generatedCommandList": [], - "acceptedCommandList": [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 64, 65, 66, 67, 68, 71, 75, 76 - ], - "attributeList": [ - 0, 1, 2, 3, 4, 7, 8, 15, 16, 16384, 16385, 16386, 16387, 16388, 16389, - 16390, 16394, 16395, 16396, 16397, 16400, 65528, 65529, 65531, 65532, - 65533 - ], - "featureMap": 31, - "clusterRevision": 5, - "_type": "chip.clusters.Objects.ColorControl" - }, - "OccupancySensing": { - "occupancy": 0, - "occupancySensorType": 0, - "occupancySensorTypeBitmap": 1, - "pirOccupiedToUnoccupiedDelay": null, - "pirUnoccupiedToOccupiedDelay": null, - "pirUnoccupiedToOccupiedThreshold": null, - "ultrasonicOccupiedToUnoccupiedDelay": null, - "ultrasonicUnoccupiedToOccupiedDelay": null, - "ultrasonicUnoccupiedToOccupiedThreshold": null, - "physicalContactOccupiedToUnoccupiedDelay": null, - "physicalContactUnoccupiedToOccupiedDelay": null, - "physicalContactUnoccupiedToOccupiedThreshold": null, - "generatedCommandList": [], - "acceptedCommandList": [], - "attributeList": [0, 1, 2, 65528, 65529, 65531, 65532, 65533], - "featureMap": 0, - "clusterRevision": 3, - "_type": "chip.clusters.Objects.OccupancySensing" - } - } - }, - "events": [ - { - "Header": { - "EndpointId": 0, - "ClusterId": 40, - "EventId": 0, - "EventNumber": 262144, - "Priority": 2, - "Timestamp": 2019, - "TimestampType": 0 - }, - "Status": 0, - "Data": { - "softwareVersion": 1, - "_type": "chip.clusters.Objects.Basic.Events.StartUp" - } - }, - { - "Header": { - "EndpointId": 0, - "ClusterId": 51, - "EventId": 3, - "EventNumber": 262145, - "Priority": 2, - "Timestamp": 2020, - "TimestampType": 0 - }, - "Status": 0, - "Data": { - "bootReason": 1, - "_type": "chip.clusters.Objects.GeneralDiagnostics.Events.BootReason" - } - }, - { - "Header": { - "EndpointId": 0, - "ClusterId": 54, - "EventId": 2, - "EventNumber": 262146, - "Priority": 1, - "Timestamp": 2216, - "TimestampType": 0 - }, - "Status": 0, - "Data": { - "connectionStatus": 0, - "_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Events.ConnectionStatus" - } - } - ], - "node_id": 4337 -} diff --git a/tests/components/matter/fixtures/nodes/occupancy-sensor.json b/tests/components/matter/fixtures/nodes/occupancy-sensor.json new file mode 100644 index 00000000000..2944853f9e1 --- /dev/null +++ b/tests/components/matter/fixtures/nodes/occupancy-sensor.json @@ -0,0 +1,633 @@ +{ + "node_id": 1, + "date_commissioned": "2022-11-29T21:23:48.485051", + "last_interview": "2022-11-29T21:23:48.485057", + "interview_version": 1, + "attributes": { + "0/40/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.DataModelRevision", + "attribute_name": "DataModelRevision", + "value": 1 + }, + "0/40/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.VendorName", + "attribute_name": "VendorName", + "value": "Nabu Casa" + }, + "0/40/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.VendorID", + "attribute_name": "VendorID", + "value": 65521 + }, + "0/40/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductName", + "attribute_name": "ProductName", + "value": "Mock OccupancySensor" + }, + "0/40/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductID", + "attribute_name": "ProductID", + "value": 32768 + }, + "0/40/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.NodeLabel", + "attribute_name": "NodeLabel", + "value": "Mock Occupancy Sensor" + }, + "0/40/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.Location", + "attribute_name": "Location", + "value": "XX" + }, + "0/40/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.HardwareVersion", + "attribute_name": "HardwareVersion", + "value": 0 + }, + "0/40/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.HardwareVersionString", + "attribute_name": "HardwareVersionString", + "value": "v1.0" + }, + "0/40/9": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 9, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SoftwareVersion", + "attribute_name": "SoftwareVersion", + "value": 1 + }, + "0/40/10": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 10, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SoftwareVersionString", + "attribute_name": "SoftwareVersionString", + "value": "v1.0" + }, + "0/40/11": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 11, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ManufacturingDate", + "attribute_name": "ManufacturingDate", + "value": "20221206" + }, + "0/40/12": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 12, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.PartNumber", + "attribute_name": "PartNumber", + "value": "" + }, + "0/40/13": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 13, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductURL", + "attribute_name": "ProductURL", + "value": "" + }, + "0/40/14": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 14, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductLabel", + "attribute_name": "ProductLabel", + "value": "" + }, + "0/40/15": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 15, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SerialNumber", + "attribute_name": "SerialNumber", + "value": "TEST_SN" + }, + "0/40/16": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 16, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.LocalConfigDisabled", + "attribute_name": "LocalConfigDisabled", + "value": false + }, + "0/40/17": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 17, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.Reachable", + "attribute_name": "Reachable", + "value": true + }, + "0/40/18": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 18, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.UniqueID", + "attribute_name": "UniqueID", + "value": "mock-temperature-sensor" + }, + "0/40/19": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 19, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.CapabilityMinima", + "attribute_name": "CapabilityMinima", + "value": { + "caseSessionsPerFabric": 3, + "subscriptionsPerFabric": 3 + } + }, + "0/40/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/40/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/40/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/40/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/40/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 65528, 65529, 65531, 65532, 65533 + ] + }, + + "1/3/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.IdentifyTime", + "attribute_name": "IdentifyTime", + "value": 0 + }, + "1/3/1": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.IdentifyType", + "attribute_name": "IdentifyType", + "value": 2 + }, + "1/3/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "1/3/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 4 + }, + "1/3/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "1/3/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 64] + }, + "1/3/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 65528, 65529, 65531, 65532, 65533] + }, + + "1/29/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 263, + "revision": 1 + } + ] + }, + "1/29/1": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 3, 4, 5, 6, 7, 8, 15, 29, 30, 37, 47, 59, 64, 65, 69, 80, 257, 258, 259, + 512, 513, 514, 516, 768, 1024, 1026, 1027, 1028, 1029, 1030, 1283, 1284, + 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 2820, + 4294048773 + ] + }, + "1/29/2": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [] + }, + "1/29/3": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [] + }, + "1/29/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "1/29/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "1/29/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "1/29/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "1/29/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, + "1/30/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 30, + "cluster_type": "chip.clusters.Objects.Binding", + "cluster_name": "Binding", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Binding.Attributes.Binding", + "attribute_name": "Binding", + "value": [] + }, + "1/30/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 30, + "cluster_type": "chip.clusters.Objects.Binding", + "cluster_name": "Binding", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Binding.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "1/30/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 30, + "cluster_type": "chip.clusters.Objects.Binding", + "cluster_name": "Binding", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Binding.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "1/30/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 30, + "cluster_type": "chip.clusters.Objects.Binding", + "cluster_name": "Binding", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Binding.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "1/30/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 30, + "cluster_type": "chip.clusters.Objects.Binding", + "cluster_name": "Binding", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Binding.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "1/30/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 30, + "cluster_type": "chip.clusters.Objects.Binding", + "cluster_name": "Binding", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Binding.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 65528, 65529, 65531, 65532, 65533] + }, + "1/1030/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 1030, + "cluster_type": "chip.clusters.Objects.OccupancySensing", + "cluster_name": "OccupancySensing", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.OccupancySensing.Attributes.Occupancy", + "attribute_name": "Occupancy", + "value": 1 + }, + "1/1030/1": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 1030, + "cluster_type": "chip.clusters.Objects.OccupancySensing", + "cluster_name": "OccupancySensing", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.OccupancySensing.Attributes.OccupancySensorType", + "attribute_name": "OccupancySensorType", + "value": 0 + }, + "1/1030/2": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 1030, + "cluster_type": "chip.clusters.Objects.OccupancySensing", + "cluster_name": "OccupancySensing", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.OccupancySensing.Attributes.OccupancySensorTypeBitmap", + "attribute_name": "OccupancySensorTypeBitmap", + "value": 1 + }, + "1/1030/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 1030, + "cluster_type": "chip.clusters.Objects.OccupancySensing", + "cluster_name": "OccupancySensing", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.OccupancySensing.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "1/1030/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 1030, + "cluster_type": "chip.clusters.Objects.OccupancySensing", + "cluster_name": "OccupancySensing", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.OccupancySensing.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 3 + }, + "1/1030/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 1030, + "cluster_type": "chip.clusters.Objects.OccupancySensing", + "cluster_name": "OccupancySensing", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.OccupancySensing.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "1/1030/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 1030, + "cluster_type": "chip.clusters.Objects.OccupancySensing", + "cluster_name": "OccupancySensing", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.OccupancySensing.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "1/1030/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 1030, + "cluster_type": "chip.clusters.Objects.OccupancySensing", + "cluster_name": "OccupancySensing", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.OccupancySensing.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 65528, 65529, 65531, 65532, 65533] + } + }, + "endpoints": [0, 1], + "_type": "matter_server.common.models.node.MatterNode" +} diff --git a/tests/components/matter/fixtures/nodes/on-off-plugin-unit.json b/tests/components/matter/fixtures/nodes/on-off-plugin-unit.json new file mode 100644 index 00000000000..dd1c005bdf8 --- /dev/null +++ b/tests/components/matter/fixtures/nodes/on-off-plugin-unit.json @@ -0,0 +1,1055 @@ +{ + "node_id": 1, + "date_commissioned": "2022-11-29T21:23:48.485051", + "last_interview": "2022-11-29T21:23:48.485057", + "interview_version": 1, + "attributes": { + "0/40/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.DataModelRevision", + "attribute_name": "DataModelRevision", + "value": 1 + }, + "0/40/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.VendorName", + "attribute_name": "VendorName", + "value": "Nabu Casa" + }, + "0/40/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.VendorID", + "attribute_name": "VendorID", + "value": 65521 + }, + "0/40/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductName", + "attribute_name": "ProductName", + "value": "Mock OnOffPluginUnit (powerplug/switch)" + }, + "0/40/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductID", + "attribute_name": "ProductID", + "value": 32768 + }, + "0/40/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.NodeLabel", + "attribute_name": "NodeLabel", + "value": "Mock OnOff Plugin Unit" + }, + "0/40/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.Location", + "attribute_name": "Location", + "value": "XX" + }, + "0/40/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.HardwareVersion", + "attribute_name": "HardwareVersion", + "value": 0 + }, + "0/40/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.HardwareVersionString", + "attribute_name": "HardwareVersionString", + "value": "v1.0" + }, + "0/40/9": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 9, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SoftwareVersion", + "attribute_name": "SoftwareVersion", + "value": 1 + }, + "0/40/10": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 10, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SoftwareVersionString", + "attribute_name": "SoftwareVersionString", + "value": "v1.0" + }, + "0/40/11": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 11, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ManufacturingDate", + "attribute_name": "ManufacturingDate", + "value": "20221206" + }, + "0/40/12": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 12, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.PartNumber", + "attribute_name": "PartNumber", + "value": "" + }, + "0/40/13": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 13, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductURL", + "attribute_name": "ProductURL", + "value": "" + }, + "0/40/14": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 14, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductLabel", + "attribute_name": "ProductLabel", + "value": "" + }, + "0/40/15": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 15, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SerialNumber", + "attribute_name": "SerialNumber", + "value": "TEST_SN" + }, + "0/40/16": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 16, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.LocalConfigDisabled", + "attribute_name": "LocalConfigDisabled", + "value": false + }, + "0/40/17": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 17, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.Reachable", + "attribute_name": "Reachable", + "value": true + }, + "0/40/18": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 18, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.UniqueID", + "attribute_name": "UniqueID", + "value": "mock-onoff-plugin-unit" + }, + "0/40/19": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 19, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.CapabilityMinima", + "attribute_name": "CapabilityMinima", + "value": { + "caseSessionsPerFabric": 3, + "subscriptionsPerFabric": 3 + } + }, + "0/40/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/40/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/40/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/40/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/40/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 65528, 65529, 65531, 65532, 65533 + ] + }, + + "1/3/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.IdentifyTime", + "attribute_name": "IdentifyTime", + "value": 0 + }, + "1/3/1": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.IdentifyType", + "attribute_name": "IdentifyType", + "value": 2 + }, + "1/3/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "1/3/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 4 + }, + "1/3/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "1/3/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 64] + }, + "1/3/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 3, + "cluster_type": "chip.clusters.Objects.Identify", + "cluster_name": "Identify", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Identify.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 65528, 65529, 65531, 65532, 65533] + }, + "1/4/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.NameSupport", + "attribute_name": "NameSupport", + "value": 128 + }, + "1/4/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 1 + }, + "1/4/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 4 + }, + "1/4/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [0, 1, 2, 3] + }, + "1/4/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 1, 2, 3, 4, 5] + }, + "1/4/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 65528, 65529, 65531, 65532, 65533] + }, + "1/5/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 5, + "cluster_type": "chip.clusters.Objects.Scenes", + "cluster_name": "Scenes", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Scenes.Attributes.SceneCount", + "attribute_name": "SceneCount", + "value": 0 + }, + "1/5/1": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 5, + "cluster_type": "chip.clusters.Objects.Scenes", + "cluster_name": "Scenes", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Scenes.Attributes.CurrentScene", + "attribute_name": "CurrentScene", + "value": 0 + }, + "1/5/2": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 5, + "cluster_type": "chip.clusters.Objects.Scenes", + "cluster_name": "Scenes", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Scenes.Attributes.CurrentGroup", + "attribute_name": "CurrentGroup", + "value": 0 + }, + "1/5/3": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 5, + "cluster_type": "chip.clusters.Objects.Scenes", + "cluster_name": "Scenes", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Scenes.Attributes.SceneValid", + "attribute_name": "SceneValid", + "value": false + }, + "1/5/4": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 5, + "cluster_type": "chip.clusters.Objects.Scenes", + "cluster_name": "Scenes", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.Scenes.Attributes.NameSupport", + "attribute_name": "NameSupport", + "value": 0 + }, + "1/5/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 5, + "cluster_type": "chip.clusters.Objects.Scenes", + "cluster_name": "Scenes", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Scenes.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "1/5/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 5, + "cluster_type": "chip.clusters.Objects.Scenes", + "cluster_name": "Scenes", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Scenes.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 4 + }, + "1/5/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 5, + "cluster_type": "chip.clusters.Objects.Scenes", + "cluster_name": "Scenes", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Scenes.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [0, 1, 2, 3, 4, 6] + }, + "1/5/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 5, + "cluster_type": "chip.clusters.Objects.Scenes", + "cluster_name": "Scenes", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Scenes.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 1, 2, 3, 4, 5, 6] + }, + "1/5/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 5, + "cluster_type": "chip.clusters.Objects.Scenes", + "cluster_name": "Scenes", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Scenes.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533] + }, + "1/6/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 6, + "cluster_type": "chip.clusters.Objects.OnOff", + "cluster_name": "OnOff", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.OnOff.Attributes.OnOff", + "attribute_name": "OnOff", + "value": false + }, + "1/6/16384": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 6, + "cluster_type": "chip.clusters.Objects.OnOff", + "cluster_name": "OnOff", + "attribute_id": 16384, + "attribute_type": "chip.clusters.Objects.OnOff.Attributes.GlobalSceneControl", + "attribute_name": "GlobalSceneControl", + "value": true + }, + "1/6/16385": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 6, + "cluster_type": "chip.clusters.Objects.OnOff", + "cluster_name": "OnOff", + "attribute_id": 16385, + "attribute_type": "chip.clusters.Objects.OnOff.Attributes.OnTime", + "attribute_name": "OnTime", + "value": 0 + }, + "1/6/16386": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 6, + "cluster_type": "chip.clusters.Objects.OnOff", + "cluster_name": "OnOff", + "attribute_id": 16386, + "attribute_type": "chip.clusters.Objects.OnOff.Attributes.OffWaitTime", + "attribute_name": "OffWaitTime", + "value": 0 + }, + "1/6/16387": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 6, + "cluster_type": "chip.clusters.Objects.OnOff", + "cluster_name": "OnOff", + "attribute_id": 16387, + "attribute_type": "chip.clusters.Objects.OnOff.Attributes.StartUpOnOff", + "attribute_name": "StartUpOnOff", + "value": null + }, + "1/6/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 6, + "cluster_type": "chip.clusters.Objects.OnOff", + "cluster_name": "OnOff", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.OnOff.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 1 + }, + "1/6/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 6, + "cluster_type": "chip.clusters.Objects.OnOff", + "cluster_name": "OnOff", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.OnOff.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 4 + }, + "1/6/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 6, + "cluster_type": "chip.clusters.Objects.OnOff", + "cluster_name": "OnOff", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.OnOff.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "1/6/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 6, + "cluster_type": "chip.clusters.Objects.OnOff", + "cluster_name": "OnOff", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.OnOff.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 1, 2, 64, 65, 66] + }, + "1/6/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 6, + "cluster_type": "chip.clusters.Objects.OnOff", + "cluster_name": "OnOff", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.OnOff.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [ + 0, 16384, 16385, 16386, 16387, 65528, 65529, 65531, 65532, 65533 + ] + }, + "1/7/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 7, + "cluster_type": "chip.clusters.Objects.OnOffSwitchConfiguration", + "cluster_name": "OnOffSwitchConfiguration", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.OnOffSwitchConfiguration.Attributes.SwitchType", + "attribute_name": "SwitchType", + "value": 0 + }, + "1/7/16": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 7, + "cluster_type": "chip.clusters.Objects.OnOffSwitchConfiguration", + "cluster_name": "OnOffSwitchConfiguration", + "attribute_id": 16, + "attribute_type": "chip.clusters.Objects.OnOffSwitchConfiguration.Attributes.SwitchActions", + "attribute_name": "SwitchActions", + "value": 0 + }, + "1/7/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 7, + "cluster_type": "chip.clusters.Objects.OnOffSwitchConfiguration", + "cluster_name": "OnOffSwitchConfiguration", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.OnOffSwitchConfiguration.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "1/7/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 7, + "cluster_type": "chip.clusters.Objects.OnOffSwitchConfiguration", + "cluster_name": "OnOffSwitchConfiguration", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.OnOffSwitchConfiguration.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "1/7/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 7, + "cluster_type": "chip.clusters.Objects.OnOffSwitchConfiguration", + "cluster_name": "OnOffSwitchConfiguration", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.OnOffSwitchConfiguration.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "1/7/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 7, + "cluster_type": "chip.clusters.Objects.OnOffSwitchConfiguration", + "cluster_name": "OnOffSwitchConfiguration", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.OnOffSwitchConfiguration.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "1/7/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 7, + "cluster_type": "chip.clusters.Objects.OnOffSwitchConfiguration", + "cluster_name": "OnOffSwitchConfiguration", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.OnOffSwitchConfiguration.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 16, 65528, 65529, 65531, 65532, 65533] + }, + "1/8/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.CurrentLevel", + "attribute_name": "CurrentLevel", + "value": 254 + }, + "1/8/1": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.RemainingTime", + "attribute_name": "RemainingTime", + "value": 0 + }, + "1/8/2": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.MinLevel", + "attribute_name": "MinLevel", + "value": 1 + }, + "1/8/3": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.MaxLevel", + "attribute_name": "MaxLevel", + "value": 254 + }, + "1/8/4": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.CurrentFrequency", + "attribute_name": "CurrentFrequency", + "value": 0 + }, + "1/8/5": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.MinFrequency", + "attribute_name": "MinFrequency", + "value": 0 + }, + "1/8/6": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.MaxFrequency", + "attribute_name": "MaxFrequency", + "value": 0 + }, + "1/8/15": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 15, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.Options", + "attribute_name": "Options", + "value": 0 + }, + "1/8/16": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 16, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.OnOffTransitionTime", + "attribute_name": "OnOffTransitionTime", + "value": 0 + }, + "1/8/17": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 17, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.OnLevel", + "attribute_name": "OnLevel", + "value": null + }, + "1/8/18": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 18, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.OnTransitionTime", + "attribute_name": "OnTransitionTime", + "value": 0 + }, + "1/8/19": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 19, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.OffTransitionTime", + "attribute_name": "OffTransitionTime", + "value": 0 + }, + "1/8/20": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 20, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.DefaultMoveRate", + "attribute_name": "DefaultMoveRate", + "value": 50 + }, + "1/8/16384": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 16384, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.StartUpCurrentLevel", + "attribute_name": "StartUpCurrentLevel", + "value": null + }, + "1/8/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 3 + }, + "1/8/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 5 + }, + "1/8/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "1/8/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 1, 2, 3, 4, 5, 6, 7] + }, + "1/8/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 8, + "cluster_type": "chip.clusters.Objects.LevelControl", + "cluster_name": "LevelControl", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.LevelControl.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [ + 0, 1, 2, 3, 4, 5, 6, 15, 16, 17, 18, 19, 20, 16384, 65528, 65529, 65531, + 65532, 65533 + ] + }, + "1/29/0": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 266, + "revision": 1 + } + ] + }, + "1/29/1": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 3, 4, 5, 6, 7, 8, 15, 29, 30, 37, 47, 59, 64, 65, 69, 80, 257, 258, 259, + 512, 513, 514, 516, 768, 1024, 1026, 1027, 1028, 1029, 1030, 1283, 1284, + 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 2820, + 4294048773 + ] + }, + "1/29/2": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [] + }, + "1/29/3": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [] + }, + "1/29/65532": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "1/29/65533": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "1/29/65528": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "1/29/65529": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "1/29/65531": { + "node_id": 1, + "endpoint": 1, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + } + }, + "endpoints": [0, 1], + "_type": "matter_server.common.models.node.MatterNode" +} diff --git a/tests/components/matter/fixtures/nodes/onoff-light.json b/tests/components/matter/fixtures/nodes/onoff-light.json new file mode 100644 index 00000000000..cc6521aa2e3 --- /dev/null +++ b/tests/components/matter/fixtures/nodes/onoff-light.json @@ -0,0 +1,4216 @@ +{ + "node_id": 1, + "date_commissioned": "2022-11-29T21:23:48.485051", + "last_interview": "2022-11-29T21:23:48.485057", + "interview_version": 1, + "attributes": { + "0/4/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.NameSupport", + "attribute_name": "NameSupport", + "value": 128 + }, + "0/4/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 1 + }, + "0/4/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 4 + }, + "0/4/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [0, 1, 2, 3] + }, + "0/4/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 1, 2, 3, 4, 5] + }, + "0/4/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 4, + "cluster_type": "chip.clusters.Objects.Groups", + "cluster_name": "Groups", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Groups.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 65528, 65529, 65531, 65532, 65533] + }, + "0/29/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 22, + "revision": 1 + } + ] + }, + "0/29/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 4, 29, 31, 40, 42, 43, 44, 48, 49, 50, 51, 52, 53, 54, 55, 59, 60, 62, + 63, 64, 65 + ] + }, + "0/29/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [41] + }, + "0/29/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [1] + }, + "0/29/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/29/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/29/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/29/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/29/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, + "0/31/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.Acl", + "attribute_name": "Acl", + "value": [ + { + "privilege": 5, + "authMode": 2, + "subjects": [112233], + "targets": null, + "fabricIndex": 1 + } + ] + }, + "0/31/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.Extension", + "attribute_name": "Extension", + "value": [] + }, + "0/31/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.SubjectsPerAccessControlEntry", + "attribute_name": "SubjectsPerAccessControlEntry", + "value": 4 + }, + "0/31/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.TargetsPerAccessControlEntry", + "attribute_name": "TargetsPerAccessControlEntry", + "value": 3 + }, + "0/31/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.AccessControlEntriesPerFabric", + "attribute_name": "AccessControlEntriesPerFabric", + "value": 3 + }, + "0/31/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/31/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/31/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/31/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/31/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 31, + "cluster_type": "chip.clusters.Objects.AccessControl", + "cluster_name": "AccessControl", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.AccessControl.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533] + }, + "0/40/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.DataModelRevision", + "attribute_name": "DataModelRevision", + "value": 1 + }, + "0/40/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.VendorName", + "attribute_name": "VendorName", + "value": "Nabu Casa" + }, + "0/40/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.VendorID", + "attribute_name": "VendorID", + "value": 65521 + }, + "0/40/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductName", + "attribute_name": "ProductName", + "value": "Mock Light" + }, + "0/40/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductID", + "attribute_name": "ProductID", + "value": 32768 + }, + "0/40/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.NodeLabel", + "attribute_name": "NodeLabel", + "value": "Mock OnOff Light" + }, + "0/40/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.Location", + "attribute_name": "Location", + "value": "XX" + }, + "0/40/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.HardwareVersion", + "attribute_name": "HardwareVersion", + "value": 0 + }, + "0/40/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.HardwareVersionString", + "attribute_name": "HardwareVersionString", + "value": "v1.0" + }, + "0/40/9": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 9, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SoftwareVersion", + "attribute_name": "SoftwareVersion", + "value": 1 + }, + "0/40/10": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 10, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SoftwareVersionString", + "attribute_name": "SoftwareVersionString", + "value": "v1.0" + }, + "0/40/11": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 11, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ManufacturingDate", + "attribute_name": "ManufacturingDate", + "value": "20200101" + }, + "0/40/12": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 12, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.PartNumber", + "attribute_name": "PartNumber", + "value": "" + }, + "0/40/13": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 13, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductURL", + "attribute_name": "ProductURL", + "value": "" + }, + "0/40/14": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 14, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ProductLabel", + "attribute_name": "ProductLabel", + "value": "" + }, + "0/40/15": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 15, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.SerialNumber", + "attribute_name": "SerialNumber", + "value": "TEST_SN" + }, + "0/40/16": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 16, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.LocalConfigDisabled", + "attribute_name": "LocalConfigDisabled", + "value": false + }, + "0/40/17": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 17, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.Reachable", + "attribute_name": "Reachable", + "value": true + }, + "0/40/18": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 18, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.UniqueID", + "attribute_name": "UniqueID", + "value": "mock-onoff-light" + }, + "0/40/19": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 19, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.CapabilityMinima", + "attribute_name": "CapabilityMinima", + "value": { + "caseSessionsPerFabric": 3, + "subscriptionsPerFabric": 3 + } + }, + "0/40/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/40/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/40/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/40/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/40/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 40, + "cluster_type": "chip.clusters.Objects.Basic", + "cluster_name": "Basic", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Basic.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 65528, 65529, 65531, 65532, 65533 + ] + }, + "0/42/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.DefaultOtaProviders", + "attribute_name": "DefaultOtaProviders", + "value": [] + }, + "0/42/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.UpdatePossible", + "attribute_name": "UpdatePossible", + "value": true + }, + "0/42/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.UpdateState", + "attribute_name": "UpdateState", + "value": 0 + }, + "0/42/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.UpdateStateProgress", + "attribute_name": "UpdateStateProgress", + "value": 0 + }, + "0/42/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/42/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/42/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/42/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0] + }, + "0/42/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 42, + "cluster_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor", + "cluster_name": "OtaSoftwareUpdateRequestor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.OtaSoftwareUpdateRequestor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, + "0/43/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.ActiveLocale", + "attribute_name": "ActiveLocale", + "value": "en-US" + }, + "0/43/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.SupportedLocales", + "attribute_name": "SupportedLocales", + "value": [ + "en-US", + "de-DE", + "fr-FR", + "en-GB", + "es-ES", + "zh-CN", + "it-IT", + "ja-JP" + ] + }, + "0/43/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/43/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/43/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/43/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/43/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 43, + "cluster_type": "chip.clusters.Objects.LocalizationConfiguration", + "cluster_name": "LocalizationConfiguration", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.LocalizationConfiguration.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 65528, 65529, 65531, 65532, 65533] + }, + "0/44/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.HourFormat", + "attribute_name": "HourFormat", + "value": 0 + }, + "0/44/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.ActiveCalendarType", + "attribute_name": "ActiveCalendarType", + "value": 0 + }, + "0/44/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.SupportedCalendarTypes", + "attribute_name": "SupportedCalendarTypes", + "value": [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 7] + }, + "0/44/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/44/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/44/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/44/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/44/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 44, + "cluster_type": "chip.clusters.Objects.TimeFormatLocalization", + "cluster_name": "TimeFormatLocalization", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.TimeFormatLocalization.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 65528, 65529, 65531, 65532, 65533] + }, + "0/48/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.Breadcrumb", + "attribute_name": "Breadcrumb", + "value": 0 + }, + "0/48/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.BasicCommissioningInfo", + "attribute_name": "BasicCommissioningInfo", + "value": { + "failSafeExpiryLengthSeconds": 60, + "maxCumulativeFailsafeSeconds": 900 + } + }, + "0/48/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.RegulatoryConfig", + "attribute_name": "RegulatoryConfig", + "value": 0 + }, + "0/48/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.LocationCapability", + "attribute_name": "LocationCapability", + "value": 0 + }, + "0/48/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.SupportsConcurrentConnection", + "attribute_name": "SupportsConcurrentConnection", + "value": true + }, + "0/48/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/48/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/48/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [1, 3, 5] + }, + "0/48/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 2, 4] + }, + "0/48/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 48, + "cluster_type": "chip.clusters.Objects.GeneralCommissioning", + "cluster_name": "GeneralCommissioning", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.GeneralCommissioning.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533] + }, + "0/49/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.MaxNetworks", + "attribute_name": "MaxNetworks", + "value": 1 + }, + "0/49/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.Networks", + "attribute_name": "Networks", + "value": [ + { + "networkID": "b'wifi-sm'", + "connected": true + } + ] + }, + "0/49/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.ScanMaxTimeSeconds", + "attribute_name": "ScanMaxTimeSeconds", + "value": 10 + }, + "0/49/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.ConnectMaxTimeSeconds", + "attribute_name": "ConnectMaxTimeSeconds", + "value": 30 + }, + "0/49/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.InterfaceEnabled", + "attribute_name": "InterfaceEnabled", + "value": true + }, + "0/49/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.LastNetworkingStatus", + "attribute_name": "LastNetworkingStatus", + "value": 0 + }, + "0/49/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.LastNetworkID", + "attribute_name": "LastNetworkID", + "value": "b'wifi-sm'" + }, + "0/49/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.LastConnectErrorValue", + "attribute_name": "LastConnectErrorValue", + "value": null + }, + "0/49/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 1 + }, + "0/49/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/49/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [1, 5, 7] + }, + "0/49/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 2, 4, 6, 8] + }, + "0/49/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 49, + "cluster_type": "chip.clusters.Objects.NetworkCommissioning", + "cluster_name": "NetworkCommissioning", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.NetworkCommissioning.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 4, 5, 6, 7, 65528, 65529, 65531, 65532, 65533] + }, + "0/50/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 50, + "cluster_type": "chip.clusters.Objects.DiagnosticLogs", + "cluster_name": "DiagnosticLogs", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.DiagnosticLogs.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/50/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 50, + "cluster_type": "chip.clusters.Objects.DiagnosticLogs", + "cluster_name": "DiagnosticLogs", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.DiagnosticLogs.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/50/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 50, + "cluster_type": "chip.clusters.Objects.DiagnosticLogs", + "cluster_name": "DiagnosticLogs", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.DiagnosticLogs.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [1] + }, + "0/50/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 50, + "cluster_type": "chip.clusters.Objects.DiagnosticLogs", + "cluster_name": "DiagnosticLogs", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.DiagnosticLogs.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0] + }, + "0/50/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 50, + "cluster_type": "chip.clusters.Objects.DiagnosticLogs", + "cluster_name": "DiagnosticLogs", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.DiagnosticLogs.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [65528, 65529, 65531, 65532, 65533] + }, + "0/51/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.NetworkInterfaces", + "attribute_name": "NetworkInterfaces", + "value": [ + { + "name": "WIFI_STA_DEF", + "isOperational": true, + "offPremiseServicesReachableIPv4": null, + "offPremiseServicesReachableIPv6": null, + "hardwareAddress": "b\"\\x84\\xf7\\x03'\\xb5,\"", + "IPv4Addresses": ["b'\\xc0\\xa8\\x01\\x84'"], + "IPv6Addresses": [ + "b\"\\xfe\\x80\\x00\\x00\\x00\\x00\\x00\\x00\\x86\\xf7\\x03\\xff\\xfe'\\xb5,\"", + "b\"*\\x00\\xbb\\xa0\\x12\\x12\\x8f\\x00\\x86\\xf7\\x03\\xff\\xfe'\\xb5,\"", + "b\"\\xfd\\xf5\\nn\\xf0cO\\xed\\x86\\xf7\\x03\\xff\\xfe'\\xb5,\"" + ], + "type": 1 + } + ] + }, + "0/51/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.RebootCount", + "attribute_name": "RebootCount", + "value": 6 + }, + "0/51/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.UpTime", + "attribute_name": "UpTime", + "value": 31279 + }, + "0/51/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.TotalOperationalHours", + "attribute_name": "TotalOperationalHours", + "value": 8 + }, + "0/51/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.BootReasons", + "attribute_name": "BootReasons", + "value": 1 + }, + "0/51/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.ActiveHardwareFaults", + "attribute_name": "ActiveHardwareFaults", + "value": [] + }, + "0/51/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.ActiveRadioFaults", + "attribute_name": "ActiveRadioFaults", + "value": [] + }, + "0/51/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.ActiveNetworkFaults", + "attribute_name": "ActiveNetworkFaults", + "value": [] + }, + "0/51/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.TestEventTriggersEnabled", + "attribute_name": "TestEventTriggersEnabled", + "value": false + }, + "0/51/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/51/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/51/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/51/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0] + }, + "0/51/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 51, + "cluster_type": "chip.clusters.Objects.GeneralDiagnostics", + "cluster_name": "GeneralDiagnostics", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.GeneralDiagnostics.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 4, 5, 6, 7, 8, 65528, 65529, 65531, 65532, 65533] + }, + "0/52/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.ThreadMetrics", + "attribute_name": "ThreadMetrics", + "value": [] + }, + "0/52/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.CurrentHeapFree", + "attribute_name": "CurrentHeapFree", + "value": 166480 + }, + "0/52/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.CurrentHeapUsed", + "attribute_name": "CurrentHeapUsed", + "value": 86512 + }, + "0/52/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.CurrentHeapHighWatermark", + "attribute_name": "CurrentHeapHighWatermark", + "value": 157052 + }, + "0/52/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/52/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/52/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/52/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/52/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 52, + "cluster_type": "chip.clusters.Objects.SoftwareDiagnostics", + "cluster_name": "SoftwareDiagnostics", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.SoftwareDiagnostics.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, + "0/53/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.Channel", + "attribute_name": "Channel", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RoutingRole", + "attribute_name": "RoutingRole", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.NetworkName", + "attribute_name": "NetworkName", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.PanId", + "attribute_name": "PanId", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ExtendedPanId", + "attribute_name": "ExtendedPanId", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.MeshLocalPrefix", + "attribute_name": "MeshLocalPrefix", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.OverrunCount", + "attribute_name": "OverrunCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.NeighborTableList", + "attribute_name": "NeighborTableList", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RouteTableList", + "attribute_name": "RouteTableList", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/9": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 9, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.PartitionId", + "attribute_name": "PartitionId", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/10": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 10, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.Weighting", + "attribute_name": "Weighting", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/11": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 11, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.DataVersion", + "attribute_name": "DataVersion", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/12": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 12, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.StableDataVersion", + "attribute_name": "StableDataVersion", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/13": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 13, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.LeaderRouterId", + "attribute_name": "LeaderRouterId", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/14": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 14, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.DetachedRoleCount", + "attribute_name": "DetachedRoleCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/15": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 15, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ChildRoleCount", + "attribute_name": "ChildRoleCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/16": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 16, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RouterRoleCount", + "attribute_name": "RouterRoleCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/17": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 17, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.LeaderRoleCount", + "attribute_name": "LeaderRoleCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/18": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 18, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.AttachAttemptCount", + "attribute_name": "AttachAttemptCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/19": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 19, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.PartitionIdChangeCount", + "attribute_name": "PartitionIdChangeCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/20": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 20, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.BetterPartitionAttachAttemptCount", + "attribute_name": "BetterPartitionAttachAttemptCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/21": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 21, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ParentChangeCount", + "attribute_name": "ParentChangeCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/22": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 22, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxTotalCount", + "attribute_name": "TxTotalCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/23": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 23, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxUnicastCount", + "attribute_name": "TxUnicastCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/24": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 24, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxBroadcastCount", + "attribute_name": "TxBroadcastCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/25": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 25, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxAckRequestedCount", + "attribute_name": "TxAckRequestedCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/26": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 26, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxAckedCount", + "attribute_name": "TxAckedCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/27": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 27, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxNoAckRequestedCount", + "attribute_name": "TxNoAckRequestedCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/28": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 28, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxDataCount", + "attribute_name": "TxDataCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/29": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 29, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxDataPollCount", + "attribute_name": "TxDataPollCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/30": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 30, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxBeaconCount", + "attribute_name": "TxBeaconCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/31": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 31, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxBeaconRequestCount", + "attribute_name": "TxBeaconRequestCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/32": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 32, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxOtherCount", + "attribute_name": "TxOtherCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/33": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 33, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxRetryCount", + "attribute_name": "TxRetryCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/34": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 34, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxDirectMaxRetryExpiryCount", + "attribute_name": "TxDirectMaxRetryExpiryCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/35": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 35, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxIndirectMaxRetryExpiryCount", + "attribute_name": "TxIndirectMaxRetryExpiryCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/36": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 36, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxErrCcaCount", + "attribute_name": "TxErrCcaCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/37": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 37, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxErrAbortCount", + "attribute_name": "TxErrAbortCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/38": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 38, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.TxErrBusyChannelCount", + "attribute_name": "TxErrBusyChannelCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/39": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 39, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxTotalCount", + "attribute_name": "RxTotalCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/40": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 40, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxUnicastCount", + "attribute_name": "RxUnicastCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/41": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 41, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxBroadcastCount", + "attribute_name": "RxBroadcastCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/42": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 42, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxDataCount", + "attribute_name": "RxDataCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/43": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 43, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxDataPollCount", + "attribute_name": "RxDataPollCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/44": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 44, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxBeaconCount", + "attribute_name": "RxBeaconCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/45": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 45, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxBeaconRequestCount", + "attribute_name": "RxBeaconRequestCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/46": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 46, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxOtherCount", + "attribute_name": "RxOtherCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/47": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 47, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxAddressFilteredCount", + "attribute_name": "RxAddressFilteredCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/48": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 48, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxDestAddrFilteredCount", + "attribute_name": "RxDestAddrFilteredCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/49": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 49, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxDuplicatedCount", + "attribute_name": "RxDuplicatedCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/50": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 50, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxErrNoFrameCount", + "attribute_name": "RxErrNoFrameCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/51": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 51, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxErrUnknownNeighborCount", + "attribute_name": "RxErrUnknownNeighborCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/52": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 52, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxErrInvalidSrcAddrCount", + "attribute_name": "RxErrInvalidSrcAddrCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/53": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 53, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxErrSecCount", + "attribute_name": "RxErrSecCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/54": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 54, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxErrFcsCount", + "attribute_name": "RxErrFcsCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/55": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 55, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.RxErrOtherCount", + "attribute_name": "RxErrOtherCount", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/56": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 56, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ActiveTimestamp", + "attribute_name": "ActiveTimestamp", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/57": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 57, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.PendingTimestamp", + "attribute_name": "PendingTimestamp", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/58": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 58, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.Delay", + "attribute_name": "Delay", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/59": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 59, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.SecurityPolicy", + "attribute_name": "SecurityPolicy", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/60": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 60, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ChannelPage0Mask", + "attribute_name": "ChannelPage0Mask", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/61": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 61, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.OperationalDatasetComponents", + "attribute_name": "OperationalDatasetComponents", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/62": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 62, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ActiveNetworkFaultsList", + "attribute_name": "ActiveNetworkFaultsList", + "value": { + "TLVValue": null, + "Reason": "InteractionModelError: Failure (0x1)" + } + }, + "0/53/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 15 + }, + "0/53/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/53/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/53/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0] + }, + "0/53/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 53, + "cluster_type": "chip.clusters.Objects.ThreadNetworkDiagnostics", + "cluster_name": "ThreadNetworkDiagnostics", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.ThreadNetworkDiagnostics.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 65528, 65529, 65531, 65532, 65533 + ] + }, + "0/54/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.Bssid", + "attribute_name": "Bssid", + "value": "b'r\\xa7A\\x91\\xf1!'" + }, + "0/54/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.SecurityType", + "attribute_name": "SecurityType", + "value": 4 + }, + "0/54/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.WiFiVersion", + "attribute_name": "WiFiVersion", + "value": 3 + }, + "0/54/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.ChannelNumber", + "attribute_name": "ChannelNumber", + "value": 6 + }, + "0/54/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.Rssi", + "attribute_name": "Rssi", + "value": -61 + }, + "0/54/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.BeaconLostCount", + "attribute_name": "BeaconLostCount", + "value": null + }, + "0/54/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.BeaconRxCount", + "attribute_name": "BeaconRxCount", + "value": null + }, + "0/54/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.PacketMulticastRxCount", + "attribute_name": "PacketMulticastRxCount", + "value": null + }, + "0/54/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.PacketMulticastTxCount", + "attribute_name": "PacketMulticastTxCount", + "value": null + }, + "0/54/9": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 9, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.PacketUnicastRxCount", + "attribute_name": "PacketUnicastRxCount", + "value": null + }, + "0/54/10": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 10, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.PacketUnicastTxCount", + "attribute_name": "PacketUnicastTxCount", + "value": null + }, + "0/54/11": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 11, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.CurrentMaxRate", + "attribute_name": "CurrentMaxRate", + "value": null + }, + "0/54/12": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 12, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.OverrunCount", + "attribute_name": "OverrunCount", + "value": null + }, + "0/54/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 3 + }, + "0/54/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/54/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/54/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0] + }, + "0/54/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 54, + "cluster_type": "chip.clusters.Objects.WiFiNetworkDiagnostics", + "cluster_name": "WiFiNetworkDiagnostics", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.WiFiNetworkDiagnostics.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 65528, 65529, 65531, 65532, + 65533 + ] + }, + "0/55/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.PHYRate", + "attribute_name": "PHYRate", + "value": null + }, + "0/55/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.FullDuplex", + "attribute_name": "FullDuplex", + "value": null + }, + "0/55/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.PacketRxCount", + "attribute_name": "PacketRxCount", + "value": 0 + }, + "0/55/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.PacketTxCount", + "attribute_name": "PacketTxCount", + "value": 0 + }, + "0/55/4": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 4, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.TxErrCount", + "attribute_name": "TxErrCount", + "value": 0 + }, + "0/55/5": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 5, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.CollisionCount", + "attribute_name": "CollisionCount", + "value": 0 + }, + "0/55/6": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 6, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.OverrunCount", + "attribute_name": "OverrunCount", + "value": 0 + }, + "0/55/7": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 7, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.CarrierDetect", + "attribute_name": "CarrierDetect", + "value": null + }, + "0/55/8": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 8, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.TimeSinceReset", + "attribute_name": "TimeSinceReset", + "value": 0 + }, + "0/55/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 3 + }, + "0/55/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/55/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/55/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0] + }, + "0/55/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 55, + "cluster_type": "chip.clusters.Objects.EthernetNetworkDiagnostics", + "cluster_name": "EthernetNetworkDiagnostics", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.EthernetNetworkDiagnostics.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 4, 5, 6, 7, 8, 65528, 65529, 65531, 65532, 65533] + }, + "0/59/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 59, + "cluster_type": "chip.clusters.Objects.Switch", + "cluster_name": "Switch", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Switch.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/59/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 59, + "cluster_type": "chip.clusters.Objects.Switch", + "cluster_name": "Switch", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Switch.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/59/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 59, + "cluster_type": "chip.clusters.Objects.Switch", + "cluster_name": "Switch", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Switch.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/59/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 59, + "cluster_type": "chip.clusters.Objects.Switch", + "cluster_name": "Switch", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Switch.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/59/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 59, + "cluster_type": "chip.clusters.Objects.Switch", + "cluster_name": "Switch", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Switch.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [65528, 65529, 65531, 65532, 65533] + }, + "0/60/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.WindowStatus", + "attribute_name": "WindowStatus", + "value": 0 + }, + "0/60/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.AdminFabricIndex", + "attribute_name": "AdminFabricIndex", + "value": null + }, + "0/60/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.AdminVendorId", + "attribute_name": "AdminVendorId", + "value": null + }, + "0/60/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/60/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/60/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/60/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [0, 1, 2] + }, + "0/60/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 60, + "cluster_type": "chip.clusters.Objects.AdministratorCommissioning", + "cluster_name": "AdministratorCommissioning", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.AdministratorCommissioning.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 65528, 65529, 65531, 65532, 65533] + }, + "0/62/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 62, + "cluster_type": "chip.clusters.Objects.OperationalCredentials", + "cluster_name": "OperationalCredentials", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.OperationalCredentials.Attributes.NOCs", + "attribute_name": "NOCs", + "value": [ + { + "noc": "b'\\x150\\x01\\x01\\x01$\\x02\\x017\\x03$\\x13\\x02\\x18&\\x04\\x80\"\\x81\\'&\\x05\\x80%M:7\\x06$\\x15\\x01$\\x11\\x01\\x18$\\x07\\x01$\\x08\\x010\\tA\\x04Z\\xec\\xd9\\x11h\\'J[%&\\xe1\\xd8\\xa0\\xb15\\x01\\x9c\\x83\\xf8\\x1a\\x9bg\\xbb\\x81e\\x18\\x8b\\xc8\\x9dr\\xed\\xe7\\xc3\\\\\\xd21\\x99\\xc1\\xdb\\xfe\\xd0}\\xf5)\\xaeMq\\xd5\\xeeRg\\x16\\xd5%\\x9c\\xfcD\\t\\xcbQ\\xc8\\x83*\\x997\\n5\\x01(\\x01\\x18$\\x02\\x016\\x03\\x04\\x02\\x04\\x01\\x180\\x04\\x14\\xca\\xe2\\x07\\xdb\\x89 MatterNode: """Fixture for a light node.""" return await setup_integration_with_node_fixture( - hass, hass_storage, "lighting-example-app" + hass, "dimmable-light", matter_client ) -async def test_turn_on(hass: HomeAssistant, light_node: MatterNode) -> None: +async def test_turn_on( + hass: HomeAssistant, + matter_client: MagicMock, + light_node: MatterNode, +) -> None: """Test turning on a light.""" - light_node.matter.client.mock_command(clusters.OnOff.Commands.On, None) + state = hass.states.get("light.mock_dimmable_light") + assert state + assert state.state == "on" + + set_node_attribute(light_node, 1, 6, 0, False) + await trigger_subscription_callback(hass, matter_client) + + state = hass.states.get("light.mock_dimmable_light") + assert state + assert state.state == "off" await hass.services.async_call( "light", "turn_on", { - "entity_id": "light.my_cool_light", + "entity_id": "light.mock_dimmable_light", }, blocking=True, ) - assert len(light_node.matter.client.mock_sent_commands) == 1 - args = light_node.matter.client.mock_sent_commands[0] - assert args["nodeid"] == light_node.node_id - assert args["endpoint"] == 1 - - light_node.matter.client.mock_command( - clusters.LevelControl.Commands.MoveToLevelWithOnOff, None + assert matter_client.send_device_command.call_count == 1 + assert matter_client.send_device_command.call_args == call( + node_id=light_node.node_id, + endpoint=1, + command=clusters.OnOff.Commands.On(), ) + matter_client.send_device_command.reset_mock() await hass.services.async_call( "light", "turn_on", { - "entity_id": "light.my_cool_light", + "entity_id": "light.mock_dimmable_light", "brightness": 128, }, blocking=True, ) - assert len(light_node.matter.client.mock_sent_commands) == 2 - args = light_node.matter.client.mock_sent_commands[1] - assert args["nodeid"] == light_node.node_id - assert args["endpoint"] == 1 - assert args["payload"].level == 127 - assert args["payload"].transitionTime == 0 + assert matter_client.send_device_command.call_count == 1 + assert matter_client.send_device_command.call_args == call( + node_id=light_node.node_id, + endpoint=1, + command=clusters.LevelControl.Commands.MoveToLevelWithOnOff( + level=128, + transitionTime=0, + ), + ) -async def test_turn_off(hass: HomeAssistant, light_node: MatterNode) -> None: +async def test_turn_off( + hass: HomeAssistant, + matter_client: MagicMock, + light_node: MatterNode, +) -> None: """Test turning off a light.""" - light_node.matter.client.mock_command(clusters.OnOff.Commands.Off, None) + state = hass.states.get("light.mock_dimmable_light") + assert state + assert state.state == "on" await hass.services.async_call( "light", "turn_off", { - "entity_id": "light.my_cool_light", + "entity_id": "light.mock_dimmable_light", }, blocking=True, ) - assert len(light_node.matter.client.mock_sent_commands) == 1 - args = light_node.matter.client.mock_sent_commands[0] - assert args["nodeid"] == light_node.node_id - assert args["endpoint"] == 1 + assert matter_client.send_device_command.call_count == 1 + assert matter_client.send_device_command.call_args == call( + node_id=light_node.node_id, + endpoint=1, + command=clusters.OnOff.Commands.Off(), + ) From 861a8ee3c77def91138c99fd8f2158f4226c840e Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Tue, 6 Dec 2022 04:25:37 -0500 Subject: [PATCH 1009/1033] Fix zwave_js_value_updated event (#83358) * Fix zwave_js_value_updated event * Fix zwave_js_value_updated event --- homeassistant/components/zwave_js/__init__.py | 2 +- tests/components/zwave_js/test_events.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zwave_js/__init__.py b/homeassistant/components/zwave_js/__init__.py index c492cd8618f..e7b17b30b03 100644 --- a/homeassistant/components/zwave_js/__init__.py +++ b/homeassistant/components/zwave_js/__init__.py @@ -717,7 +717,7 @@ class NodeEvents: raw_value = value_ = value.value if value.metadata.states: - value_ = value.metadata.states.get(str(value), value_) + value_ = value.metadata.states.get(str(value_), value_) self.hass.bus.async_fire( ZWAVE_JS_VALUE_UPDATED_EVENT, diff --git a/tests/components/zwave_js/test_events.py b/tests/components/zwave_js/test_events.py index 8552f69936d..8201016b9d9 100644 --- a/tests/components/zwave_js/test_events.py +++ b/tests/components/zwave_js/test_events.py @@ -234,6 +234,8 @@ async def test_notifications(hass, hank_binary_switch, integration, client): async def test_value_updated(hass, vision_security_zl7432, integration, client): """Test value updated events.""" node = vision_security_zl7432 + # Add states to the value we are updating to ensure the translation happens + node.values["7-37-1-currentValue"].metadata.data["states"] = {"1": "on", "0": "off"} events = async_capture_events(hass, "zwave_js_value_updated") event = Event( @@ -266,7 +268,7 @@ async def test_value_updated(hass, vision_security_zl7432, integration, client): assert events[0].data["endpoint"] == 1 assert events[0].data["property_name"] == "currentValue" assert events[0].data["property"] == "currentValue" - assert events[0].data["value"] == 1 + assert events[0].data["value"] == "on" assert events[0].data["value_raw"] == 1 # Try a value updated event on a value we aren't watching to make sure From 3a7efddb4b705c270b674f54b2fb179a766ba037 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 6 Dec 2022 10:29:23 +0100 Subject: [PATCH 1010/1033] Fix sensor schema for device classes (#83378) --- homeassistant/components/sensor/device_condition.py | 8 +++++++- homeassistant/components/sensor/device_trigger.py | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/device_condition.py b/homeassistant/components/sensor/device_condition.py index 55e993f46ba..34a2590ce8e 100644 --- a/homeassistant/components/sensor/device_condition.py +++ b/homeassistant/components/sensor/device_condition.py @@ -128,16 +128,18 @@ CONDITION_SCHEMA = vol.All( CONF_IS_GAS, CONF_IS_HUMIDITY, CONF_IS_ILLUMINANCE, - CONF_IS_OZONE, CONF_IS_MOISTURE, CONF_IS_NITROGEN_DIOXIDE, CONF_IS_NITROGEN_MONOXIDE, CONF_IS_NITROUS_OXIDE, + CONF_IS_OZONE, CONF_IS_POWER, CONF_IS_POWER_FACTOR, CONF_IS_PM1, CONF_IS_PM10, CONF_IS_PM25, + CONF_IS_PRECIPITATION, + CONF_IS_PRECIPITATION_INTENSITY, CONF_IS_PRESSURE, CONF_IS_REACTIVE_POWER, CONF_IS_SIGNAL_STRENGTH, @@ -145,6 +147,10 @@ CONDITION_SCHEMA = vol.All( CONF_IS_TEMPERATURE, CONF_IS_VOLATILE_ORGANIC_COMPOUNDS, CONF_IS_VOLTAGE, + CONF_IS_VOLUME, + CONF_IS_WATER, + CONF_IS_WEIGHT, + CONF_IS_WIND_SPEED, CONF_IS_VALUE, ] ), diff --git a/homeassistant/components/sensor/device_trigger.py b/homeassistant/components/sensor/device_trigger.py index ef724881c52..c1b0699664e 100644 --- a/homeassistant/components/sensor/device_trigger.py +++ b/homeassistant/components/sensor/device_trigger.py @@ -138,6 +138,8 @@ TRIGGER_SCHEMA = vol.All( CONF_PM25, CONF_POWER, CONF_POWER_FACTOR, + CONF_PRECIPITATION, + CONF_PRECIPITATION_INTENSITY, CONF_PRESSURE, CONF_REACTIVE_POWER, CONF_SIGNAL_STRENGTH, @@ -145,6 +147,10 @@ TRIGGER_SCHEMA = vol.All( CONF_TEMPERATURE, CONF_VOLATILE_ORGANIC_COMPOUNDS, CONF_VOLTAGE, + CONF_VOLUME, + CONF_WATER, + CONF_WEIGHT, + CONF_WIND_SPEED, CONF_VALUE, ] ), From 048553cd02a45956c3b7608cb405314517553fa1 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 6 Dec 2022 12:14:15 +0100 Subject: [PATCH 1011/1033] Fix Goalzero time to empty sensor device class (#83389) * Fix Goalzero time to empty sensor device class * Fix tests --- homeassistant/components/goalzero/sensor.py | 2 +- tests/components/goalzero/test_sensor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/goalzero/sensor.py b/homeassistant/components/goalzero/sensor.py index 345c3b41f7d..2538639883c 100644 --- a/homeassistant/components/goalzero/sensor.py +++ b/homeassistant/components/goalzero/sensor.py @@ -91,7 +91,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="timeToEmptyFull", name="Time to empty/full", - device_class=TIME_MINUTES, + device_class=SensorDeviceClass.DURATION, native_unit_of_measurement=TIME_MINUTES, ), SensorEntityDescription( diff --git a/tests/components/goalzero/test_sensor.py b/tests/components/goalzero/test_sensor.py index e61015a4925..402c03f2e51 100644 --- a/tests/components/goalzero/test_sensor.py +++ b/tests/components/goalzero/test_sensor.py @@ -77,7 +77,7 @@ async def test_sensors( assert state.attributes.get(ATTR_STATE_CLASS) is None state = hass.states.get(f"sensor.{DEFAULT_NAME}_time_to_empty_full") assert state.state == "-1" - assert state.attributes.get(ATTR_DEVICE_CLASS) == TIME_MINUTES + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.DURATION assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TIME_MINUTES assert state.attributes.get(ATTR_STATE_CLASS) is None state = hass.states.get(f"sensor.{DEFAULT_NAME}_temperature") From 1cfd292075fbb3a4cf413ed05c14d6821db35ff3 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Tue, 6 Dec 2022 18:41:09 +0100 Subject: [PATCH 1012/1033] Bypass zwave_js config validation if driver not ready (#83410) --- .../zwave_js/device_automation_helpers.py | 10 ++- .../zwave_js/triggers/trigger_helpers.py | 21 ++++-- .../zwave_js/test_device_trigger.py | 68 +++++++++++++++++++ tests/components/zwave_js/test_trigger.py | 59 ++++++++++++++++ 4 files changed, 149 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/zwave_js/device_automation_helpers.py b/homeassistant/components/zwave_js/device_automation_helpers.py index 25cce978df1..11c4fde3137 100644 --- a/homeassistant/components/zwave_js/device_automation_helpers.py +++ b/homeassistant/components/zwave_js/device_automation_helpers.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import cast import voluptuous as vol +from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.const import ConfigurationValueType from zwave_js_server.model.node import Node from zwave_js_server.model.value import ConfigurationValue @@ -12,7 +13,7 @@ from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry as dr -from .const import DOMAIN +from .const import DATA_CLIENT, DOMAIN NODE_STATUSES = ["asleep", "awake", "dead", "alive"] @@ -66,4 +67,9 @@ def async_bypass_dynamic_config_validation(hass: HomeAssistant, device_id: str) ), None, ) - return not entry + if not entry: + return True + + # The driver may not be ready when the config entry is loaded. + client: ZwaveClient = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] + return client.driver is None diff --git a/homeassistant/components/zwave_js/triggers/trigger_helpers.py b/homeassistant/components/zwave_js/triggers/trigger_helpers.py index 706c4fc0aca..0fd9c3b4291 100644 --- a/homeassistant/components/zwave_js/triggers/trigger_helpers.py +++ b/homeassistant/components/zwave_js/triggers/trigger_helpers.py @@ -1,11 +1,13 @@ """Helpers for Z-Wave JS custom triggers.""" +from zwave_js_server.client import Client as ZwaveClient + from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.typing import ConfigType -from ..const import ATTR_CONFIG_ENTRY_ID, DOMAIN +from ..const import ATTR_CONFIG_ENTRY_ID, DATA_CLIENT, DOMAIN @callback @@ -19,9 +21,8 @@ def async_bypass_dynamic_config_validation( ent_reg = er.async_get(hass) trigger_devices = config.get(ATTR_DEVICE_ID, []) trigger_entities = config.get(ATTR_ENTITY_ID, []) - return any( - entry.state != ConfigEntryState.LOADED - and ( + for entry in hass.config_entries.async_entries(DOMAIN): + if entry.state != ConfigEntryState.LOADED and ( entry.entry_id == config.get(ATTR_CONFIG_ENTRY_ID) or any( device.id in trigger_devices @@ -31,6 +32,12 @@ def async_bypass_dynamic_config_validation( entity.entity_id in trigger_entities for entity in er.async_entries_for_config_entry(ent_reg, entry.entry_id) ) - ) - for entry in hass.config_entries.async_entries(DOMAIN) - ) + ): + return True + + # The driver may not be ready when the config entry is loaded. + client: ZwaveClient = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] + if client.driver is None: + return True + + return False diff --git a/tests/components/zwave_js/test_device_trigger.py b/tests/components/zwave_js/test_device_trigger.py index 859164aa4c3..84d87efed63 100644 --- a/tests/components/zwave_js/test_device_trigger.py +++ b/tests/components/zwave_js/test_device_trigger.py @@ -1080,6 +1080,74 @@ async def test_if_value_updated_value_fires( ) +async def test_value_updated_value_no_driver( + hass, client, lock_schlage_be469, integration, calls +): + """Test zwave_js.value_updated.value trigger with missing driver.""" + node: Node = lock_schlage_be469 + dev_reg = async_get_dev_reg(hass) + device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + driver = client.driver + client.driver = None + + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + { + "trigger": { + "platform": "device", + "domain": DOMAIN, + "device_id": device.id, + "type": "zwave_js.value_updated.value", + "command_class": CommandClass.DOOR_LOCK.value, + "property": "latchStatus", + "property_key": None, + "endpoint": None, + "from": "open", + }, + "action": { + "service": "test.automation", + "data_template": { + "some": ( + "zwave_js.value_updated.value - " + "{{ trigger.platform}} - " + "{{ trigger.previous_value }}" + ) + }, + }, + }, + ] + }, + ) + await hass.async_block_till_done() + + client.driver = driver + + # No trigger as automation failed to setup. + event = Event( + type="value updated", + data={ + "source": "node", + "event": "value updated", + "nodeId": node.node_id, + "args": { + "commandClassName": "Door Lock", + "commandClass": 98, + "endpoint": 0, + "property": "latchStatus", + "newValue": "closed", + "prevValue": "open", + "propertyName": "latchStatus", + }, + }, + ) + node.receive_event(event) + await hass.async_block_till_done() + assert len(calls) == 0 + + async def test_get_trigger_capabilities_value_updated_value( hass, client, lock_schlage_be469, integration ): diff --git a/tests/components/zwave_js/test_trigger.py b/tests/components/zwave_js/test_trigger.py index a5c226057d4..b3b6910e5f5 100644 --- a/tests/components/zwave_js/test_trigger.py +++ b/tests/components/zwave_js/test_trigger.py @@ -385,6 +385,65 @@ async def test_zwave_js_value_updated_bypass_dynamic_validation_no_nodes( assert len(no_value_filter) == 0 +async def test_zwave_js_value_updated_bypass_dynamic_validation_no_driver( + hass, client, lock_schlage_be469, integration +): + """Test zwave_js.value_updated trigger without driver.""" + trigger_type = f"{DOMAIN}.value_updated" + node: Node = lock_schlage_be469 + driver = client.driver + client.driver = None + + no_value_filter = async_capture_events(hass, "no_value_filter") + + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + # no value filter + { + "trigger": { + "platform": trigger_type, + "entity_id": SCHLAGE_BE469_LOCK_ENTITY, + "command_class": CommandClass.DOOR_LOCK.value, + "property": "latchStatus", + }, + "action": { + "event": "no_value_filter", + }, + }, + ] + }, + ) + await hass.async_block_till_done() + + client.driver = driver + + # Test that no value filter is NOT triggered because automation failed setup + event = Event( + type="value updated", + data={ + "source": "node", + "event": "value updated", + "nodeId": node.node_id, + "args": { + "commandClassName": "Door Lock", + "commandClass": 98, + "endpoint": 0, + "property": "latchStatus", + "newValue": "boo", + "prevValue": "hiss", + "propertyName": "latchStatus", + }, + }, + ) + node.receive_event(event) + await hass.async_block_till_done() + + assert len(no_value_filter) == 0 + + async def test_zwave_js_event(hass, client, lock_schlage_be469, integration): """Test for zwave_js.event automation trigger.""" trigger_type = f"{DOMAIN}.event" From a2ba126be16e1853287bed00975979a5a9392250 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Tue, 6 Dec 2022 10:04:32 -0800 Subject: [PATCH 1013/1033] Tighten validation on calendar create event websocket (#83413) --- homeassistant/components/calendar/__init__.py | 67 +++++++++++-- .../local_calendar/test_calendar.py | 93 +++++++++++++++++++ 2 files changed, 152 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/calendar/__init__.py b/homeassistant/components/calendar/__init__.py index b5d605b9f6f..c89b36ce636 100644 --- a/homeassistant/components/calendar/__init__.py +++ b/homeassistant/components/calendar/__init__.py @@ -1,10 +1,11 @@ """Support for Google Calendar event device sensors.""" from __future__ import annotations -from collections.abc import Iterable +from collections.abc import Callable, Iterable import dataclasses import datetime from http import HTTPStatus +from itertools import groupby import logging import re from typing import Any, cast, final @@ -365,17 +366,67 @@ class CalendarListView(http.HomeAssistantView): return self.json(sorted(calendar_list, key=lambda x: cast(str, x["name"]))) +def _has_same_type(*keys: Any) -> Callable[[dict[str, Any]], dict[str, Any]]: + """Verify that all values are of the same type.""" + + def validate(obj: dict[str, Any]) -> dict[str, Any]: + """Test that all keys in the dict have values of the same type.""" + uniq_values = groupby(type(obj[k]) for k in keys) + if len(list(uniq_values)) > 1: + raise vol.Invalid(f"Expected all values to be the same type: {keys}") + return obj + + return validate + + +def _has_consistent_timezone(*keys: Any) -> Callable[[dict[str, Any]], dict[str, Any]]: + """Verify that all datetime values have a consistent timezone.""" + + def validate(obj: dict[str, Any]) -> dict[str, Any]: + """Test that all keys that are datetime values have the same timezone.""" + values = [obj[k] for k in keys] + if all(isinstance(value, datetime.datetime) for value in values): + uniq_values = groupby(value.tzinfo for value in values) + if len(list(uniq_values)) > 1: + raise vol.Invalid( + f"Expected all values to have the same timezone: {values}" + ) + return obj + + return validate + + +def _is_sorted(*keys: Any) -> Callable[[dict[str, Any]], dict[str, Any]]: + """Verify that the specified values are sequential.""" + + def validate(obj: dict[str, Any]) -> dict[str, Any]: + """Test that all keys in the dict are in order.""" + values = [obj[k] for k in keys] + if values != sorted(values): + raise vol.Invalid(f"Values were not in order: {values}") + return obj + + return validate + + @websocket_api.websocket_command( { vol.Required("type"): "calendar/event/create", vol.Required("entity_id"): cv.entity_id, - vol.Required(CONF_EVENT): { - vol.Required(EVENT_START): vol.Any(cv.date, cv.datetime), - vol.Required(EVENT_END): vol.Any(cv.date, cv.datetime), - vol.Required(EVENT_SUMMARY): cv.string, - vol.Optional(EVENT_DESCRIPTION): cv.string, - vol.Optional(EVENT_RRULE): _validate_rrule, - }, + CONF_EVENT: vol.Schema( + vol.All( + { + vol.Required(EVENT_START): vol.Any(cv.date, cv.datetime), + vol.Required(EVENT_END): vol.Any(cv.date, cv.datetime), + vol.Required(EVENT_SUMMARY): cv.string, + vol.Optional(EVENT_DESCRIPTION): cv.string, + vol.Optional(EVENT_RRULE): _validate_rrule, + }, + _has_same_type(EVENT_START, EVENT_END), + _has_consistent_timezone(EVENT_START, EVENT_END), + _is_sorted(EVENT_START, EVENT_END), + ) + ), } ) @websocket_api.async_response diff --git a/tests/components/local_calendar/test_calendar.py b/tests/components/local_calendar/test_calendar.py index 77632c8bfe1..092fcb1c1fb 100644 --- a/tests/components/local_calendar/test_calendar.py +++ b/tests/components/local_calendar/test_calendar.py @@ -612,3 +612,96 @@ async def test_all_day_iter_order( events = await get_events("2022-10-06T00:00:00Z", "2022-10-09T00:00:00Z") assert [event["summary"] for event in events] == event_order + + +async def test_start_end_types( + ws_client: ClientFixture, + setup_integration: None, +): + """Test a start and end with different date and date time types.""" + client = await ws_client() + result = await client.cmd( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-15", + "dtend": "1997-07-14T17:00:00+00:00", + }, + }, + ) + assert not result.get("success") + assert "error" in result + assert "code" in result.get("error") + assert result["error"]["code"] == "invalid_format" + + +async def test_end_before_start( + ws_client: ClientFixture, + setup_integration: None, +): + """Test an event with a start/end date time.""" + client = await ws_client() + result = await client.cmd( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-15T04:00:00+00:00", + "dtend": "1997-07-14T17:00:00+00:00", + }, + }, + ) + assert not result.get("success") + assert "error" in result + assert "code" in result.get("error") + assert result["error"]["code"] == "invalid_format" + + +async def test_invalid_recurrence_rule( + ws_client: ClientFixture, + setup_integration: None, +): + """Test an event with a recurrence rule.""" + client = await ws_client() + result = await client.cmd( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Monday meeting", + "dtstart": "2022-08-29T09:00:00", + "dtend": "2022-08-29T10:00:00", + "rrule": "FREQ=invalid;'", + }, + }, + ) + assert not result.get("success") + assert "error" in result + assert "code" in result.get("error") + assert result["error"]["code"] == "invalid_format" + + +async def test_invalid_date_formats( + ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn +): + """Exercises a validation error within rfc5545 parsing in ical.""" + client = await ws_client() + result = await client.cmd( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Bastille Day Party", + # Can't mix offset aware and floating dates + "dtstart": "1997-07-15T04:00:00+08:00", + "dtend": "1997-07-14T17:00:00", + }, + }, + ) + assert not result.get("success") + assert "error" in result + assert "code" in result.get("error") + assert result["error"]["code"] == "invalid_format" From 52ed121970ed93fdd2e81ab340405244ccb2b381 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 6 Dec 2022 19:21:28 +0100 Subject: [PATCH 1014/1033] Update frontend to 20221206.0 (#83415) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 76da0335769..3093928f21f 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20221205.0"], + "requirements": ["home-assistant-frontend==20221206.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 52f37d2451f..406b9972aca 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -22,7 +22,7 @@ dbus-fast==1.75.0 fnvhash==0.1.0 hass-nabucasa==0.59.0 home-assistant-bluetooth==1.8.1 -home-assistant-frontend==20221205.0 +home-assistant-frontend==20221206.0 httpx==0.23.1 ifaddr==0.1.7 janus==1.0.0 diff --git a/requirements_all.txt b/requirements_all.txt index 67c7e1ff56d..130349a5d40 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -884,7 +884,7 @@ hole==0.7.0 holidays==0.17.2 # homeassistant.components.frontend -home-assistant-frontend==20221205.0 +home-assistant-frontend==20221206.0 # homeassistant.components.home_connect homeconnect==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8bd3b438994..6b1a3326184 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -664,7 +664,7 @@ hole==0.7.0 holidays==0.17.2 # homeassistant.components.frontend -home-assistant-frontend==20221205.0 +home-assistant-frontend==20221206.0 # homeassistant.components.home_connect homeconnect==0.7.2 From ced924805199ec611a3b2814d7f228519092accb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 6 Dec 2022 13:22:52 -0500 Subject: [PATCH 1015/1033] Bumped version to 2022.12.0b6 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 32a0b85ac96..cd915aee3e0 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -8,7 +8,7 @@ from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "0b5" +PATCH_VERSION: Final = "0b6" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/pyproject.toml b/pyproject.toml index 4b63472ef20..3cf775d2039 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.12.0b5" +version = "2022.12.0b6" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From 7903f01f0c5dd7d00a2ed700e8d3ae42962af58f Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Tue, 6 Dec 2022 20:28:06 +0100 Subject: [PATCH 1016/1033] Add matter binary sensor platform (#83144) --- .../components/matter/binary_sensor.py | 95 +++++++++++++++ .../components/matter/device_platform.py | 2 + .../matter/fixtures/nodes/contact-sensor.json | 111 ++++++++++++++++++ .../fixtures/nodes/occupancy-sensor.json | 107 +++++++++++++++++ tests/components/matter/test_binary_sensor.py | 69 +++++++++++ 5 files changed, 384 insertions(+) create mode 100644 homeassistant/components/matter/binary_sensor.py create mode 100644 tests/components/matter/test_binary_sensor.py diff --git a/homeassistant/components/matter/binary_sensor.py b/homeassistant/components/matter/binary_sensor.py new file mode 100644 index 00000000000..a4ca54920fb --- /dev/null +++ b/homeassistant/components/matter/binary_sensor.py @@ -0,0 +1,95 @@ +"""Matter binary sensors.""" +from __future__ import annotations + +from dataclasses import dataclass +from functools import partial +from typing import TYPE_CHECKING + +from chip.clusters import Objects as clusters +from matter_server.common.models import device_types + +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, + BinarySensorEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .entity import MatterEntity, MatterEntityDescriptionBaseClass + +if TYPE_CHECKING: + from .adapter import MatterAdapter + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Matter binary sensor from Config Entry.""" + matter: MatterAdapter = hass.data[DOMAIN][config_entry.entry_id] + matter.register_platform_handler(Platform.BINARY_SENSOR, async_add_entities) + + +class MatterBinarySensor(MatterEntity, BinarySensorEntity): + """Representation of a Matter binary sensor.""" + + entity_description: MatterBinarySensorEntityDescription + + @callback + def _update_from_device(self) -> None: + """Update from device.""" + self._attr_is_on = self._device_type_instance.get_cluster( + clusters.BooleanState + ).stateValue + + +class MatterOccupancySensor(MatterBinarySensor): + """Representation of a Matter occupancy sensor.""" + + _attr_device_class = BinarySensorDeviceClass.OCCUPANCY + + @callback + def _update_from_device(self) -> None: + """Update from device.""" + occupancy = self._device_type_instance.get_cluster( + clusters.OccupancySensing + ).occupancy + # The first bit = if occupied + self._attr_is_on = occupancy & 1 == 1 + + +@dataclass +class MatterBinarySensorEntityDescription( + BinarySensorEntityDescription, + MatterEntityDescriptionBaseClass, +): + """Matter Binary Sensor entity description.""" + + +# You can't set default values on inherited data classes +MatterSensorEntityDescriptionFactory = partial( + MatterBinarySensorEntityDescription, entity_cls=MatterBinarySensor +) + +DEVICE_ENTITY: dict[ + type[device_types.DeviceType], + MatterEntityDescriptionBaseClass | list[MatterEntityDescriptionBaseClass], +] = { + device_types.ContactSensor: MatterSensorEntityDescriptionFactory( + key=device_types.ContactSensor, + name="Contact", + subscribe_attributes=(clusters.BooleanState.Attributes.StateValue,), + device_class=BinarySensorDeviceClass.DOOR, + ), + device_types.OccupancySensor: MatterSensorEntityDescriptionFactory( + key=device_types.OccupancySensor, + name="Occupancy", + entity_cls=MatterOccupancySensor, + subscribe_attributes=(clusters.OccupancySensing.Attributes.Occupancy,), + ), +} diff --git a/homeassistant/components/matter/device_platform.py b/homeassistant/components/matter/device_platform.py index 25a83d28b98..3a4d11ab95f 100644 --- a/homeassistant/components/matter/device_platform.py +++ b/homeassistant/components/matter/device_platform.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING from homeassistant.const import Platform +from .binary_sensor import DEVICE_ENTITY as BINARY_SENSOR_DEVICE_ENTITY from .light import DEVICE_ENTITY as LIGHT_DEVICE_ENTITY if TYPE_CHECKING: @@ -20,5 +21,6 @@ DEVICE_PLATFORM: dict[ MatterEntityDescriptionBaseClass | list[MatterEntityDescriptionBaseClass], ], ] = { + Platform.BINARY_SENSOR: BINARY_SENSOR_DEVICE_ENTITY, Platform.LIGHT: LIGHT_DEVICE_ENTITY, } diff --git a/tests/components/matter/fixtures/nodes/contact-sensor.json b/tests/components/matter/fixtures/nodes/contact-sensor.json index 6890b38e8f1..2aec6a32516 100644 --- a/tests/components/matter/fixtures/nodes/contact-sensor.json +++ b/tests/components/matter/fixtures/nodes/contact-sensor.json @@ -4,6 +4,113 @@ "last_interview": "2022-11-29T21:23:48.485057", "interview_version": 1, "attributes": { + "0/29/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 22, + "revision": 1 + } + ] + }, + "0/29/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 4, 29, 31, 40, 42, 43, 44, 48, 49, 50, 51, 52, 53, 54, 55, 59, 60, 62, + 63, 64, 65 + ] + }, + "0/29/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [41] + }, + "0/29/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [1] + }, + "0/29/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/29/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/29/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/29/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/29/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, "0/40/0": { "node_id": 1, "endpoint": 0, @@ -541,5 +648,9 @@ } }, "endpoints": [0, 1], + "root_device_type_instance": null, + "aggregator_device_type_instance": null, + "device_type_instances": [null], + "node_devices": [null], "_type": "matter_server.common.models.node.MatterNode" } diff --git a/tests/components/matter/fixtures/nodes/occupancy-sensor.json b/tests/components/matter/fixtures/nodes/occupancy-sensor.json index 2944853f9e1..3e16b92f261 100644 --- a/tests/components/matter/fixtures/nodes/occupancy-sensor.json +++ b/tests/components/matter/fixtures/nodes/occupancy-sensor.json @@ -4,6 +4,113 @@ "last_interview": "2022-11-29T21:23:48.485057", "interview_version": 1, "attributes": { + "0/29/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 22, + "revision": 1 + } + ] + }, + "0/29/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 4, 29, 31, 40, 42, 43, 44, 48, 49, 50, 51, 52, 53, 54, 55, 59, 60, 62, + 63, 64, 65 + ] + }, + "0/29/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [41] + }, + "0/29/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [1] + }, + "0/29/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/29/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/29/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/29/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/29/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, "0/40/0": { "node_id": 1, "endpoint": 0, diff --git a/tests/components/matter/test_binary_sensor.py b/tests/components/matter/test_binary_sensor.py new file mode 100644 index 00000000000..522cda2dccc --- /dev/null +++ b/tests/components/matter/test_binary_sensor.py @@ -0,0 +1,69 @@ +"""Test Matter binary sensors.""" +from unittest.mock import MagicMock + +from matter_server.common.models.node import MatterNode +import pytest + +from homeassistant.core import HomeAssistant + +from .common import ( + set_node_attribute, + setup_integration_with_node_fixture, + trigger_subscription_callback, +) + + +@pytest.fixture(name="contact_sensor_node") +async def contact_sensor_node_fixture( + hass: HomeAssistant, matter_client: MagicMock +) -> MatterNode: + """Fixture for a contact sensor node.""" + return await setup_integration_with_node_fixture( + hass, "contact-sensor", matter_client + ) + + +async def test_contact_sensor( + hass: HomeAssistant, + matter_client: MagicMock, + contact_sensor_node: MatterNode, +) -> None: + """Test contact sensor.""" + state = hass.states.get("binary_sensor.mock_contact_sensor_contact") + assert state + assert state.state == "on" + + set_node_attribute(contact_sensor_node, 1, 69, 0, False) + await trigger_subscription_callback(hass, matter_client) + + state = hass.states.get("binary_sensor.mock_contact_sensor_contact") + assert state + assert state.state == "off" + + +@pytest.fixture(name="occupancy_sensor_node") +async def occupancy_sensor_node_fixture( + hass: HomeAssistant, matter_client: MagicMock +) -> MatterNode: + """Fixture for a occupancy sensor node.""" + return await setup_integration_with_node_fixture( + hass, "occupancy-sensor", matter_client + ) + + +async def test_occupancy_sensor( + hass: HomeAssistant, + matter_client: MagicMock, + occupancy_sensor_node: MatterNode, +) -> None: + """Test occupancy sensor.""" + state = hass.states.get("binary_sensor.mock_occupancy_sensor_occupancy") + assert state + assert state.state == "on" + + set_node_attribute(occupancy_sensor_node, 1, 1030, 0, 0) + await trigger_subscription_callback(hass, matter_client) + + state = hass.states.get("binary_sensor.mock_occupancy_sensor_occupancy") + assert state + assert state.state == "off" From 172d587f84988fc429058ddb085b54520e1a02e0 Mon Sep 17 00:00:00 2001 From: Guido Pio Mariotti Date: Wed, 7 Dec 2022 00:42:08 +0100 Subject: [PATCH 1017/1033] Update pyvesync to 2.1.1 (#83146) Update pyvesync to 2.1.1 to close #70420 --- homeassistant/components/vesync/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/vesync/manifest.json b/homeassistant/components/vesync/manifest.json index 49be473b748..730da2d586b 100644 --- a/homeassistant/components/vesync/manifest.json +++ b/homeassistant/components/vesync/manifest.json @@ -3,7 +3,7 @@ "name": "VeSync", "documentation": "https://www.home-assistant.io/integrations/vesync", "codeowners": ["@markperdue", "@webdjoe", "@thegardenmonkey"], - "requirements": ["pyvesync==2.0.3"], + "requirements": ["pyvesync==2.1.1"], "config_flow": true, "iot_class": "cloud_polling", "loggers": ["pyvesync"] diff --git a/requirements_all.txt b/requirements_all.txt index 130349a5d40..ffd8b562d88 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2116,7 +2116,7 @@ pyvera==0.3.13 pyversasense==0.0.6 # homeassistant.components.vesync -pyvesync==2.0.3 +pyvesync==2.1.1 # homeassistant.components.vizio pyvizio==0.1.57 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6b1a3326184..c487cf916e6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1473,7 +1473,7 @@ pyuptimerobot==22.2.0 pyvera==0.3.13 # homeassistant.components.vesync -pyvesync==2.0.3 +pyvesync==2.1.1 # homeassistant.components.vizio pyvizio==0.1.57 From 7795cc7fe8e1ab3ca5042e2987c8658de8e9027e Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Tue, 6 Dec 2022 21:39:00 +0100 Subject: [PATCH 1018/1033] Add matter sensor platform (#83147) --- .../components/matter/device_platform.py | 2 + homeassistant/components/matter/sensor.py | 167 +++++++++++++++++ .../matter/fixtures/nodes/flow-sensor.json | 107 +++++++++++ .../fixtures/nodes/humidity-sensor.json | 157 +++++++++++++--- .../matter/fixtures/nodes/light-sensor.json | 107 +++++++++++ .../fixtures/nodes/pressure-sensor.json | 107 +++++++++++ .../fixtures/nodes/temperature-sensor.json | 107 +++++++++++ tests/components/matter/test_sensor.py | 169 ++++++++++++++++++ 8 files changed, 898 insertions(+), 25 deletions(-) create mode 100644 homeassistant/components/matter/sensor.py create mode 100644 tests/components/matter/test_sensor.py diff --git a/homeassistant/components/matter/device_platform.py b/homeassistant/components/matter/device_platform.py index 3a4d11ab95f..02267b02880 100644 --- a/homeassistant/components/matter/device_platform.py +++ b/homeassistant/components/matter/device_platform.py @@ -7,6 +7,7 @@ from homeassistant.const import Platform from .binary_sensor import DEVICE_ENTITY as BINARY_SENSOR_DEVICE_ENTITY from .light import DEVICE_ENTITY as LIGHT_DEVICE_ENTITY +from .sensor import DEVICE_ENTITY as SENSOR_DEVICE_ENTITY if TYPE_CHECKING: from matter_server.common.models.device_types import DeviceType @@ -23,4 +24,5 @@ DEVICE_PLATFORM: dict[ ] = { Platform.BINARY_SENSOR: BINARY_SENSOR_DEVICE_ENTITY, Platform.LIGHT: LIGHT_DEVICE_ENTITY, + Platform.SENSOR: SENSOR_DEVICE_ENTITY, } diff --git a/homeassistant/components/matter/sensor.py b/homeassistant/components/matter/sensor.py new file mode 100644 index 00000000000..2b659fc1304 --- /dev/null +++ b/homeassistant/components/matter/sensor.py @@ -0,0 +1,167 @@ +"""Matter sensors.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from functools import partial +from typing import TYPE_CHECKING, Any + +from chip.clusters import Objects as clusters +from chip.clusters.Types import Nullable, NullValue +from matter_server.common.models import device_types +from matter_server.common.models.device_type_instance import MatterDeviceTypeInstance + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + LIGHT_LUX, + PERCENTAGE, + VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + Platform, + UnitOfPressure, + UnitOfTemperature, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .entity import MatterEntity, MatterEntityDescriptionBaseClass + +if TYPE_CHECKING: + from .adapter import MatterAdapter + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Matter sensors from Config Entry.""" + matter: MatterAdapter = hass.data[DOMAIN][config_entry.entry_id] + matter.register_platform_handler(Platform.SENSOR, async_add_entities) + + +class MatterSensor(MatterEntity, SensorEntity): + """Representation of a Matter sensor.""" + + _attr_state_class = SensorStateClass.MEASUREMENT + entity_description: MatterSensorEntityDescription + + @callback + def _update_from_device(self) -> None: + """Update from device.""" + measurement: Nullable | float | None + measurement = _get_attribute_value( + self._device_type_instance, + # We always subscribe to a single value + self.entity_description.subscribe_attributes[0], + ) + + if measurement is NullValue or measurement is None: + measurement = None + else: + measurement = self.entity_description.measurement_to_ha(measurement) + + self._attr_native_value = measurement + + +def _get_attribute_value( + device_type_instance: MatterDeviceTypeInstance, + attribute: clusters.ClusterAttributeDescriptor, +) -> Any: + """Return the value of an attribute.""" + # Find the cluster for this attribute. We don't have a lookup table yet. + cluster_cls: clusters.Cluster = next( + cluster + for cluster in device_type_instance.device_type.clusters + if cluster.id == attribute.cluster_id + ) + + # Find the attribute descriptor so we know the instance variable to fetch + attribute_descriptor: clusters.ClusterObjectFieldDescriptor = next( + descriptor + for descriptor in cluster_cls.descriptor.Fields + if descriptor.Tag == attribute.attribute_id + ) + + cluster_data = device_type_instance.get_cluster(cluster_cls) + return getattr(cluster_data, attribute_descriptor.Label) + + +@dataclass +class MatterSensorEntityDescriptionMixin: + """Required fields for sensor device mapping.""" + + measurement_to_ha: Callable[[float], float] + + +@dataclass +class MatterSensorEntityDescription( + SensorEntityDescription, + MatterEntityDescriptionBaseClass, + MatterSensorEntityDescriptionMixin, +): + """Matter Sensor entity description.""" + + +# You can't set default values on inherited data classes +MatterSensorEntityDescriptionFactory = partial( + MatterSensorEntityDescription, entity_cls=MatterSensor +) + + +DEVICE_ENTITY: dict[ + type[device_types.DeviceType], + MatterEntityDescriptionBaseClass | list[MatterEntityDescriptionBaseClass], +] = { + device_types.TemperatureSensor: MatterSensorEntityDescriptionFactory( + key=device_types.TemperatureSensor, + name="Temperature", + measurement_to_ha=lambda x: x / 100, + subscribe_attributes=( + clusters.TemperatureMeasurement.Attributes.MeasuredValue, + ), + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + ), + device_types.PressureSensor: MatterSensorEntityDescriptionFactory( + key=device_types.PressureSensor, + name="Pressure", + measurement_to_ha=lambda x: x / 10, + subscribe_attributes=(clusters.PressureMeasurement.Attributes.MeasuredValue,), + native_unit_of_measurement=UnitOfPressure.KPA, + device_class=SensorDeviceClass.PRESSURE, + ), + device_types.FlowSensor: MatterSensorEntityDescriptionFactory( + key=device_types.FlowSensor, + name="Flow", + measurement_to_ha=lambda x: x / 10, + subscribe_attributes=(clusters.FlowMeasurement.Attributes.MeasuredValue,), + native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + ), + device_types.HumiditySensor: MatterSensorEntityDescriptionFactory( + key=device_types.HumiditySensor, + name="Humidity", + measurement_to_ha=lambda x: x / 100, + subscribe_attributes=( + clusters.RelativeHumidityMeasurement.Attributes.MeasuredValue, + ), + native_unit_of_measurement=PERCENTAGE, + device_class=SensorDeviceClass.HUMIDITY, + ), + device_types.LightSensor: MatterSensorEntityDescriptionFactory( + key=device_types.LightSensor, + name="Light", + measurement_to_ha=lambda x: round(pow(10, ((x - 1) / 10000)), 1), + subscribe_attributes=( + clusters.IlluminanceMeasurement.Attributes.MeasuredValue, + ), + native_unit_of_measurement=LIGHT_LUX, + device_class=SensorDeviceClass.ILLUMINANCE, + ), +} diff --git a/tests/components/matter/fixtures/nodes/flow-sensor.json b/tests/components/matter/fixtures/nodes/flow-sensor.json index 04135f8aa82..92f4c5b73b2 100644 --- a/tests/components/matter/fixtures/nodes/flow-sensor.json +++ b/tests/components/matter/fixtures/nodes/flow-sensor.json @@ -4,6 +4,113 @@ "last_interview": "2022-11-29T21:23:48.485057", "interview_version": 1, "attributes": { + "0/29/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 22, + "revision": 1 + } + ] + }, + "0/29/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 4, 29, 31, 40, 42, 43, 44, 48, 49, 50, 51, 52, 53, 54, 55, 59, 60, 62, + 63, 64, 65 + ] + }, + "0/29/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [41] + }, + "0/29/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [1] + }, + "0/29/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/29/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/29/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/29/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/29/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, "0/40/0": { "node_id": 1, "endpoint": 0, diff --git a/tests/components/matter/fixtures/nodes/humidity-sensor.json b/tests/components/matter/fixtures/nodes/humidity-sensor.json index f29227ce14b..34b95263de5 100644 --- a/tests/components/matter/fixtures/nodes/humidity-sensor.json +++ b/tests/components/matter/fixtures/nodes/humidity-sensor.json @@ -4,6 +4,113 @@ "last_interview": "2022-11-29T21:23:48.485057", "interview_version": 1, "attributes": { + "0/29/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 22, + "revision": 1 + } + ] + }, + "0/29/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 4, 29, 31, 40, 42, 43, 44, 48, 49, 50, 51, 52, 53, 54, 55, 59, 60, 62, + 63, 64, 65 + ] + }, + "0/29/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [41] + }, + "0/29/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [1] + }, + "0/29/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/29/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/29/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/29/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/29/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, "0/40/0": { "node_id": 1, "endpoint": 0, @@ -404,7 +511,7 @@ }, "1/29/65531": { "node_id": 1, - "endpoint": 0, + "endpoint": 1, "cluster_id": 29, "cluster_type": "chip.clusters.Objects.Descriptor", "cluster_name": "Descriptor", @@ -413,9 +520,9 @@ "attribute_name": "AttributeList", "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] }, - "0/1029/0": { - "node_id": 3, - "endpoint": 0, + "1/1029/0": { + "node_id": 1, + "endpoint": 1, "cluster_id": 1029, "cluster_type": "chip.clusters.Objects.RelativeHumidityMeasurement", "cluster_name": "RelativeHumidityMeasurement", @@ -424,9 +531,9 @@ "attribute_name": "MeasuredValue", "value": 0 }, - "0/1029/1": { - "node_id": 3, - "endpoint": 0, + "1/1029/1": { + "node_id": 1, + "endpoint": 1, "cluster_id": 1029, "cluster_type": "chip.clusters.Objects.RelativeHumidityMeasurement", "cluster_name": "RelativeHumidityMeasurement", @@ -435,9 +542,9 @@ "attribute_name": "MinMeasuredValue", "value": 0 }, - "0/1029/2": { - "node_id": 3, - "endpoint": 0, + "1/1029/2": { + "node_id": 1, + "endpoint": 1, "cluster_id": 1029, "cluster_type": "chip.clusters.Objects.RelativeHumidityMeasurement", "cluster_name": "RelativeHumidityMeasurement", @@ -446,9 +553,9 @@ "attribute_name": "MaxMeasuredValue", "value": 10000 }, - "0/1029/65532": { - "node_id": 3, - "endpoint": 0, + "1/1029/65532": { + "node_id": 1, + "endpoint": 1, "cluster_id": 1029, "cluster_type": "chip.clusters.Objects.RelativeHumidityMeasurement", "cluster_name": "RelativeHumidityMeasurement", @@ -457,9 +564,9 @@ "attribute_name": "FeatureMap", "value": 0 }, - "0/1029/65533": { - "node_id": 3, - "endpoint": 0, + "1/1029/65533": { + "node_id": 1, + "endpoint": 1, "cluster_id": 1029, "cluster_type": "chip.clusters.Objects.RelativeHumidityMeasurement", "cluster_name": "RelativeHumidityMeasurement", @@ -468,9 +575,9 @@ "attribute_name": "ClusterRevision", "value": 3 }, - "0/1029/65528": { - "node_id": 3, - "endpoint": 0, + "1/1029/65528": { + "node_id": 1, + "endpoint": 1, "cluster_id": 1029, "cluster_type": "chip.clusters.Objects.RelativeHumidityMeasurement", "cluster_name": "RelativeHumidityMeasurement", @@ -479,9 +586,9 @@ "attribute_name": "GeneratedCommandList", "value": [] }, - "0/1029/65529": { - "node_id": 3, - "endpoint": 0, + "1/1029/65529": { + "node_id": 1, + "endpoint": 1, "cluster_id": 1029, "cluster_type": "chip.clusters.Objects.RelativeHumidityMeasurement", "cluster_name": "RelativeHumidityMeasurement", @@ -490,9 +597,9 @@ "attribute_name": "AcceptedCommandList", "value": [] }, - "0/1029/65531": { - "node_id": 3, - "endpoint": 0, + "1/1029/65531": { + "node_id": 1, + "endpoint": 1, "cluster_id": 1029, "cluster_type": "chip.clusters.Objects.RelativeHumidityMeasurement", "cluster_name": "RelativeHumidityMeasurement", diff --git a/tests/components/matter/fixtures/nodes/light-sensor.json b/tests/components/matter/fixtures/nodes/light-sensor.json index 1e7b09c752b..152292d4589 100644 --- a/tests/components/matter/fixtures/nodes/light-sensor.json +++ b/tests/components/matter/fixtures/nodes/light-sensor.json @@ -4,6 +4,113 @@ "last_interview": "2022-11-29T21:23:48.485057", "interview_version": 1, "attributes": { + "0/29/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 22, + "revision": 1 + } + ] + }, + "0/29/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 4, 29, 31, 40, 42, 43, 44, 48, 49, 50, 51, 52, 53, 54, 55, 59, 60, 62, + 63, 64, 65 + ] + }, + "0/29/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [41] + }, + "0/29/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [1] + }, + "0/29/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/29/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/29/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/29/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/29/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, "0/40/0": { "node_id": 1, "endpoint": 0, diff --git a/tests/components/matter/fixtures/nodes/pressure-sensor.json b/tests/components/matter/fixtures/nodes/pressure-sensor.json index 9422f0c659b..e338933fbc8 100644 --- a/tests/components/matter/fixtures/nodes/pressure-sensor.json +++ b/tests/components/matter/fixtures/nodes/pressure-sensor.json @@ -4,6 +4,113 @@ "last_interview": "2022-11-29T21:23:48.485057", "interview_version": 1, "attributes": { + "0/29/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 22, + "revision": 1 + } + ] + }, + "0/29/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 4, 29, 31, 40, 42, 43, 44, 48, 49, 50, 51, 52, 53, 54, 55, 59, 60, 62, + 63, 64, 65 + ] + }, + "0/29/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [41] + }, + "0/29/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [1] + }, + "0/29/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/29/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/29/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/29/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/29/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, "0/40/0": { "node_id": 1, "endpoint": 0, diff --git a/tests/components/matter/fixtures/nodes/temperature-sensor.json b/tests/components/matter/fixtures/nodes/temperature-sensor.json index 3631992def8..5e451f31fd2 100644 --- a/tests/components/matter/fixtures/nodes/temperature-sensor.json +++ b/tests/components/matter/fixtures/nodes/temperature-sensor.json @@ -4,6 +4,113 @@ "last_interview": "2022-11-29T21:23:48.485057", "interview_version": 1, "attributes": { + "0/29/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 22, + "revision": 1 + } + ] + }, + "0/29/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 4, 29, 31, 40, 42, 43, 44, 48, 49, 50, 51, 52, 53, 54, 55, 59, 60, 62, + 63, 64, 65 + ] + }, + "0/29/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [41] + }, + "0/29/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [1] + }, + "0/29/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/29/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/29/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/29/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/29/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, "0/40/0": { "node_id": 1, "endpoint": 0, diff --git a/tests/components/matter/test_sensor.py b/tests/components/matter/test_sensor.py new file mode 100644 index 00000000000..ccdf09b8dc9 --- /dev/null +++ b/tests/components/matter/test_sensor.py @@ -0,0 +1,169 @@ +"""Test Matter sensors.""" +from unittest.mock import MagicMock + +from matter_server.common.models.node import MatterNode +import pytest + +from homeassistant.core import HomeAssistant + +from .common import ( + set_node_attribute, + setup_integration_with_node_fixture, + trigger_subscription_callback, +) + + +@pytest.fixture(name="flow_sensor_node") +async def flow_sensor_node_fixture( + hass: HomeAssistant, matter_client: MagicMock +) -> MatterNode: + """Fixture for a flow sensor node.""" + return await setup_integration_with_node_fixture(hass, "flow-sensor", matter_client) + + +@pytest.fixture(name="humidity_sensor_node") +async def humidity_sensor_node_fixture( + hass: HomeAssistant, matter_client: MagicMock +) -> MatterNode: + """Fixture for a humidity sensor node.""" + return await setup_integration_with_node_fixture( + hass, "humidity-sensor", matter_client + ) + + +@pytest.fixture(name="light_sensor_node") +async def light_sensor_node_fixture( + hass: HomeAssistant, matter_client: MagicMock +) -> MatterNode: + """Fixture for a light sensor node.""" + return await setup_integration_with_node_fixture( + hass, "light-sensor", matter_client + ) + + +@pytest.fixture(name="pressure_sensor_node") +async def pressure_sensor_node_fixture( + hass: HomeAssistant, matter_client: MagicMock +) -> MatterNode: + """Fixture for a pressure sensor node.""" + return await setup_integration_with_node_fixture( + hass, "pressure-sensor", matter_client + ) + + +@pytest.fixture(name="temperature_sensor_node") +async def temperature_sensor_node_fixture( + hass: HomeAssistant, matter_client: MagicMock +) -> MatterNode: + """Fixture for a temperature sensor node.""" + return await setup_integration_with_node_fixture( + hass, "temperature-sensor", matter_client + ) + + +async def test_sensor_null_value( + hass: HomeAssistant, + matter_client: MagicMock, + flow_sensor_node: MatterNode, +) -> None: + """Test flow sensor.""" + state = hass.states.get("sensor.mock_flow_sensor_flow") + assert state + assert state.state == "0.0" + + set_node_attribute(flow_sensor_node, 1, 1028, 0, None) + await trigger_subscription_callback(hass, matter_client) + + state = hass.states.get("sensor.mock_flow_sensor_flow") + assert state + assert state.state == "unknown" + + +async def test_flow_sensor( + hass: HomeAssistant, + matter_client: MagicMock, + flow_sensor_node: MatterNode, +) -> None: + """Test flow sensor.""" + state = hass.states.get("sensor.mock_flow_sensor_flow") + assert state + assert state.state == "0.0" + + set_node_attribute(flow_sensor_node, 1, 1028, 0, 20) + await trigger_subscription_callback(hass, matter_client) + + state = hass.states.get("sensor.mock_flow_sensor_flow") + assert state + assert state.state == "2.0" + + +async def test_humidity_sensor( + hass: HomeAssistant, + matter_client: MagicMock, + humidity_sensor_node: MatterNode, +) -> None: + """Test humidity sensor.""" + state = hass.states.get("sensor.mock_humidity_sensor_humidity") + assert state + assert state.state == "0.0" + + set_node_attribute(humidity_sensor_node, 1, 1029, 0, 4000) + await trigger_subscription_callback(hass, matter_client) + + state = hass.states.get("sensor.mock_humidity_sensor_humidity") + assert state + assert state.state == "40.0" + + +async def test_light_sensor( + hass: HomeAssistant, + matter_client: MagicMock, + light_sensor_node: MatterNode, +) -> None: + """Test light sensor.""" + state = hass.states.get("sensor.mock_light_sensor_light") + assert state + assert state.state == "1.3" + + set_node_attribute(light_sensor_node, 1, 1024, 0, 3000) + await trigger_subscription_callback(hass, matter_client) + + state = hass.states.get("sensor.mock_light_sensor_light") + assert state + assert state.state == "2.0" + + +async def test_pressure_sensor( + hass: HomeAssistant, + matter_client: MagicMock, + pressure_sensor_node: MatterNode, +) -> None: + """Test pressure sensor.""" + state = hass.states.get("sensor.mock_pressure_sensor_pressure") + assert state + assert state.state == "0.0" + + set_node_attribute(pressure_sensor_node, 1, 1027, 0, 1010) + await trigger_subscription_callback(hass, matter_client) + + state = hass.states.get("sensor.mock_pressure_sensor_pressure") + assert state + assert state.state == "101.0" + + +async def test_temperature_sensor( + hass: HomeAssistant, + matter_client: MagicMock, + temperature_sensor_node: MatterNode, +) -> None: + """Test temperature sensor.""" + state = hass.states.get("sensor.mock_temperature_sensor_temperature") + assert state + assert state.state == "21.0" + + set_node_attribute(temperature_sensor_node, 1, 1026, 0, 2500) + await trigger_subscription_callback(hass, matter_client) + + state = hass.states.get("sensor.mock_temperature_sensor_temperature") + assert state + assert state.state == "25.0" From 07c60686b1b190ff9cca60b2b8e6622290089660 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Tue, 6 Dec 2022 21:57:24 +0100 Subject: [PATCH 1019/1033] Add matter switch platform (#83149) --- .../components/matter/device_platform.py | 2 + homeassistant/components/matter/switch.py | 88 ++++++++++++++ .../fixtures/nodes/on-off-plugin-unit.json | 107 ++++++++++++++++++ tests/components/matter/test_switch.py | 85 ++++++++++++++ 4 files changed, 282 insertions(+) create mode 100644 homeassistant/components/matter/switch.py create mode 100644 tests/components/matter/test_switch.py diff --git a/homeassistant/components/matter/device_platform.py b/homeassistant/components/matter/device_platform.py index 02267b02880..24e7f8b5dc4 100644 --- a/homeassistant/components/matter/device_platform.py +++ b/homeassistant/components/matter/device_platform.py @@ -8,6 +8,7 @@ from homeassistant.const import Platform from .binary_sensor import DEVICE_ENTITY as BINARY_SENSOR_DEVICE_ENTITY from .light import DEVICE_ENTITY as LIGHT_DEVICE_ENTITY from .sensor import DEVICE_ENTITY as SENSOR_DEVICE_ENTITY +from .switch import DEVICE_ENTITY as SWITCH_DEVICE_ENTITY if TYPE_CHECKING: from matter_server.common.models.device_types import DeviceType @@ -25,4 +26,5 @@ DEVICE_PLATFORM: dict[ Platform.BINARY_SENSOR: BINARY_SENSOR_DEVICE_ENTITY, Platform.LIGHT: LIGHT_DEVICE_ENTITY, Platform.SENSOR: SENSOR_DEVICE_ENTITY, + Platform.SWITCH: SWITCH_DEVICE_ENTITY, } diff --git a/homeassistant/components/matter/switch.py b/homeassistant/components/matter/switch.py new file mode 100644 index 00000000000..b7c2b999918 --- /dev/null +++ b/homeassistant/components/matter/switch.py @@ -0,0 +1,88 @@ +"""Matter switches.""" +from __future__ import annotations + +from dataclasses import dataclass +from functools import partial +from typing import TYPE_CHECKING, Any + +from chip.clusters import Objects as clusters +from matter_server.common.models import device_types + +from homeassistant.components.switch import ( + SwitchDeviceClass, + SwitchEntity, + SwitchEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .entity import MatterEntity, MatterEntityDescriptionBaseClass + +if TYPE_CHECKING: + from .adapter import MatterAdapter + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Matter switches from Config Entry.""" + matter: MatterAdapter = hass.data[DOMAIN][config_entry.entry_id] + matter.register_platform_handler(Platform.SWITCH, async_add_entities) + + +class MatterSwitch(MatterEntity, SwitchEntity): + """Representation of a Matter switch.""" + + entity_description: MatterSwitchEntityDescription + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn switch on.""" + await self.matter_client.send_device_command( + node_id=self._device_type_instance.node.node_id, + endpoint=self._device_type_instance.endpoint, + command=clusters.OnOff.Commands.On(), + ) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn switch off.""" + await self.matter_client.send_device_command( + node_id=self._device_type_instance.node.node_id, + endpoint=self._device_type_instance.endpoint, + command=clusters.OnOff.Commands.Off(), + ) + + @callback + def _update_from_device(self) -> None: + """Update from device.""" + self._attr_is_on = self._device_type_instance.get_cluster(clusters.OnOff).onOff + + +@dataclass +class MatterSwitchEntityDescription( + SwitchEntityDescription, + MatterEntityDescriptionBaseClass, +): + """Matter Switch entity description.""" + + +# You can't set default values on inherited data classes +MatterSwitchEntityDescriptionFactory = partial( + MatterSwitchEntityDescription, entity_cls=MatterSwitch +) + + +DEVICE_ENTITY: dict[ + type[device_types.DeviceType], + MatterEntityDescriptionBaseClass | list[MatterEntityDescriptionBaseClass], +] = { + device_types.OnOffPlugInUnit: MatterSwitchEntityDescriptionFactory( + key=device_types.OnOffPlugInUnit, + subscribe_attributes=(clusters.OnOff.Attributes.OnOff,), + device_class=SwitchDeviceClass.OUTLET, + ), +} diff --git a/tests/components/matter/fixtures/nodes/on-off-plugin-unit.json b/tests/components/matter/fixtures/nodes/on-off-plugin-unit.json index dd1c005bdf8..cbbe39b1f09 100644 --- a/tests/components/matter/fixtures/nodes/on-off-plugin-unit.json +++ b/tests/components/matter/fixtures/nodes/on-off-plugin-unit.json @@ -4,6 +4,113 @@ "last_interview": "2022-11-29T21:23:48.485057", "interview_version": 1, "attributes": { + "0/29/0": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 0, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.DeviceTypeList", + "attribute_name": "DeviceTypeList", + "value": [ + { + "type": 22, + "revision": 1 + } + ] + }, + "0/29/1": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 1, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ServerList", + "attribute_name": "ServerList", + "value": [ + 4, 29, 31, 40, 42, 43, 44, 48, 49, 50, 51, 52, 53, 54, 55, 59, 60, 62, + 63, 64, 65 + ] + }, + "0/29/2": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 2, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClientList", + "attribute_name": "ClientList", + "value": [41] + }, + "0/29/3": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 3, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.PartsList", + "attribute_name": "PartsList", + "value": [1] + }, + "0/29/65532": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65532, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.FeatureMap", + "attribute_name": "FeatureMap", + "value": 0 + }, + "0/29/65533": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65533, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.ClusterRevision", + "attribute_name": "ClusterRevision", + "value": 1 + }, + "0/29/65528": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65528, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.GeneratedCommandList", + "attribute_name": "GeneratedCommandList", + "value": [] + }, + "0/29/65529": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65529, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AcceptedCommandList", + "attribute_name": "AcceptedCommandList", + "value": [] + }, + "0/29/65531": { + "node_id": 1, + "endpoint": 0, + "cluster_id": 29, + "cluster_type": "chip.clusters.Objects.Descriptor", + "cluster_name": "Descriptor", + "attribute_id": 65531, + "attribute_type": "chip.clusters.Objects.Descriptor.Attributes.AttributeList", + "attribute_name": "AttributeList", + "value": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533] + }, "0/40/0": { "node_id": 1, "endpoint": 0, diff --git a/tests/components/matter/test_switch.py b/tests/components/matter/test_switch.py new file mode 100644 index 00000000000..a79edd6010b --- /dev/null +++ b/tests/components/matter/test_switch.py @@ -0,0 +1,85 @@ +"""Test Matter switches.""" +from unittest.mock import MagicMock, call + +from chip.clusters import Objects as clusters +from matter_server.common.models.node import MatterNode +import pytest + +from homeassistant.core import HomeAssistant + +from .common import ( + set_node_attribute, + setup_integration_with_node_fixture, + trigger_subscription_callback, +) + + +@pytest.fixture(name="switch_node") +async def switch_node_fixture( + hass: HomeAssistant, matter_client: MagicMock +) -> MatterNode: + """Fixture for a switch node.""" + return await setup_integration_with_node_fixture( + hass, "on-off-plugin-unit", matter_client + ) + + +async def test_turn_on( + hass: HomeAssistant, + matter_client: MagicMock, + switch_node: MatterNode, +) -> None: + """Test turning on a switch.""" + state = hass.states.get("switch.mock_onoff_plugin_unit") + assert state + assert state.state == "off" + + await hass.services.async_call( + "switch", + "turn_on", + { + "entity_id": "switch.mock_onoff_plugin_unit", + }, + blocking=True, + ) + + assert matter_client.send_device_command.call_count == 1 + assert matter_client.send_device_command.call_args == call( + node_id=switch_node.node_id, + endpoint=1, + command=clusters.OnOff.Commands.On(), + ) + + set_node_attribute(switch_node, 1, 6, 0, True) + await trigger_subscription_callback(hass, matter_client) + + state = hass.states.get("switch.mock_onoff_plugin_unit") + assert state + assert state.state == "on" + + +async def test_turn_off( + hass: HomeAssistant, + matter_client: MagicMock, + switch_node: MatterNode, +) -> None: + """Test turning off a switch.""" + state = hass.states.get("switch.mock_onoff_plugin_unit") + assert state + assert state.state == "off" + + await hass.services.async_call( + "switch", + "turn_off", + { + "entity_id": "switch.mock_onoff_plugin_unit", + }, + blocking=True, + ) + + assert matter_client.send_device_command.call_count == 1 + assert matter_client.send_device_command.call_args == call( + node_id=switch_node.node_id, + endpoint=1, + command=clusters.OnOff.Commands.Off(), + ) From c8d560946b3e6817cf695cbc06ec3a84a9c92b55 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Tue, 6 Dec 2022 22:56:33 -0500 Subject: [PATCH 1020/1033] Add via_device support to zwave_js (#83219) Co-authored-by: Paulus Schoutsen --- homeassistant/components/zwave_js/__init__.py | 31 +++- .../components/zwave_js/device_action.py | 3 + .../components/zwave_js/device_condition.py | 5 +- .../components/zwave_js/device_trigger.py | 9 +- tests/components/zwave_js/conftest.py | 14 +- tests/components/zwave_js/test_button.py | 1 - .../components/zwave_js/test_device_action.py | 17 +- .../zwave_js/test_device_condition.py | 73 ++++++--- .../zwave_js/test_device_trigger.py | 151 ++++++++++++++---- tests/components/zwave_js/test_init.py | 8 +- tests/components/zwave_js/test_sensor.py | 4 +- tests/components/zwave_js/test_services.py | 13 +- tests/components/zwave_js/test_trigger.py | 21 ++- tests/components/zwave_js/test_update.py | 1 - 14 files changed, 251 insertions(+), 100 deletions(-) diff --git a/homeassistant/components/zwave_js/__init__.py b/homeassistant/components/zwave_js/__init__.py index e7b17b30b03..2d6e7350899 100644 --- a/homeassistant/components/zwave_js/__init__.py +++ b/homeassistant/components/zwave_js/__init__.py @@ -230,6 +230,7 @@ class DriverEvents: async def setup(self, driver: Driver) -> None: """Set up devices using the ready driver.""" self.driver = driver + controller = driver.controller # If opt in preference hasn't been specified yet, we do nothing, otherwise # we apply the preference @@ -244,7 +245,7 @@ class DriverEvents: ) known_devices = [ self.dev_reg.async_get_device({get_device_id(driver, node)}) - for node in driver.controller.nodes.values() + for node in controller.nodes.values() ] # Devices that are in the device registry that are not known by the controller can be removed @@ -252,17 +253,24 @@ class DriverEvents: if device not in known_devices: self.dev_reg.async_remove_device(device.id) - # run discovery on all ready nodes + # run discovery on controller node + c_node_id = controller.own_node_id + controller_node = controller.nodes.get(c_node_id) if c_node_id else None + if controller_node: + await self.controller_events.async_on_node_added(controller_node) + + # run discovery on all other ready nodes await asyncio.gather( *( self.controller_events.async_on_node_added(node) - for node in driver.controller.nodes.values() + for node in controller.nodes.values() + if controller_node is None or node != controller_node ) ) # listen for new nodes being added to the mesh self.config_entry.async_on_unload( - driver.controller.on( + controller.on( "node added", lambda event: self.hass.async_create_task( self.controller_events.async_on_node_added(event["node"]) @@ -272,9 +280,7 @@ class DriverEvents: # listen for nodes being removed from the mesh # NOTE: This will not remove nodes that were removed when HA was not running self.config_entry.async_on_unload( - driver.controller.on( - "node removed", self.controller_events.async_on_node_removed - ) + controller.on("node removed", self.controller_events.async_on_node_removed) ) async def async_setup_platform(self, platform: Platform) -> None: @@ -383,6 +389,16 @@ class ControllerEvents: device_id = get_device_id(driver, node) device_id_ext = get_device_id_ext(driver, node) device = self.dev_reg.async_get_device({device_id}) + via_device_id = None + controller = driver.controller + # Get the controller node device ID if this node is not the controller + if ( + controller.own_node_id is not None + and controller.own_node_id != node.node_id + ): + via_device_id = get_device_id( + driver, controller.nodes[controller.own_node_id] + ) # Replace the device if it can be determined that this node is not the # same product as it was previously. @@ -408,6 +424,7 @@ class ControllerEvents: model=node.device_config.label, manufacturer=node.device_config.manufacturer, suggested_area=node.location if node.location else UNDEFINED, + via_device=via_device_id, ) async_dispatcher_send(self.hass, EVENT_DEVICE_ADDED_TO_REGISTRY, device) diff --git a/homeassistant/components/zwave_js/device_action.py b/homeassistant/components/zwave_js/device_action.py index 004e4cc2aae..54dd17b7b83 100644 --- a/homeassistant/components/zwave_js/device_action.py +++ b/homeassistant/components/zwave_js/device_action.py @@ -150,6 +150,9 @@ async def async_get_actions( node = async_get_node_from_device_id(hass, device_id) + if node.client.driver and node.client.driver.controller.own_node_id == node.node_id: + return actions + base_action = { CONF_DEVICE_ID: device_id, CONF_DOMAIN: DOMAIN, diff --git a/homeassistant/components/zwave_js/device_condition.py b/homeassistant/components/zwave_js/device_condition.py index c42b5af71c4..87967c21dd5 100644 --- a/homeassistant/components/zwave_js/device_condition.py +++ b/homeassistant/components/zwave_js/device_condition.py @@ -126,7 +126,7 @@ async def async_get_conditions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device conditions for Z-Wave JS devices.""" - conditions = [] + conditions: list[dict] = [] base_condition = { CONF_CONDITION: "device", CONF_DEVICE_ID: device_id, @@ -134,6 +134,9 @@ async def async_get_conditions( } node = async_get_node_from_device_id(hass, device_id) + if node.client.driver and node.client.driver.controller.own_node_id == node.node_id: + return conditions + # Any value's value condition conditions.append({**base_condition, CONF_TYPE: VALUE_TYPE}) diff --git a/homeassistant/components/zwave_js/device_trigger.py b/homeassistant/components/zwave_js/device_trigger.py index 76a7f134d17..348346680d7 100644 --- a/homeassistant/components/zwave_js/device_trigger.py +++ b/homeassistant/components/zwave_js/device_trigger.py @@ -248,9 +248,6 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Z-Wave JS devices.""" - dev_reg = device_registry.async_get(hass) - node = async_get_node_from_device_id(hass, device_id, dev_reg) - triggers: list[dict] = [] base_trigger = { CONF_PLATFORM: "device", @@ -258,6 +255,12 @@ async def async_get_triggers( CONF_DOMAIN: DOMAIN, } + dev_reg = device_registry.async_get(hass) + node = async_get_node_from_device_id(hass, device_id, dev_reg) + + if node.client.driver and node.client.driver.controller.own_node_id == node.node_id: + return triggers + # We can add a node status trigger if the node status sensor is enabled ent_reg = entity_registry.async_get(hass) entity_id = async_get_node_status_sensor_entity_id( diff --git a/tests/components/zwave_js/conftest.py b/tests/components/zwave_js/conftest.py index e5a759ba0a0..ba97cfe4c36 100644 --- a/tests/components/zwave_js/conftest.py +++ b/tests/components/zwave_js/conftest.py @@ -583,7 +583,9 @@ def lock_home_connect_620_state_fixture(): @pytest.fixture(name="client") -def mock_client_fixture(controller_state, version_state, log_config_state): +def mock_client_fixture( + controller_state, controller_node_state, version_state, log_config_state +): """Mock a client.""" with patch( @@ -608,6 +610,8 @@ def mock_client_fixture(controller_state, version_state, log_config_state): client.listen = AsyncMock(side_effect=listen) client.disconnect = AsyncMock(side_effect=disconnect) client.driver = Driver(client, controller_state, log_config_state) + node = Node(client, copy.deepcopy(controller_node_state)) + client.driver.controller.nodes[node.node_id] = node client.version = VersionInfo.from_message(version_state) client.ws_server_url = "ws://test:3000/zjs" @@ -615,14 +619,6 @@ def mock_client_fixture(controller_state, version_state, log_config_state): yield client -@pytest.fixture(name="controller_node") -def controller_node_fixture(client, controller_node_state): - """Mock a controller node.""" - node = Node(client, copy.deepcopy(controller_node_state)) - client.driver.controller.nodes[node.node_id] = node - return node - - @pytest.fixture(name="multisensor_6") def multisensor_6_fixture(client, multisensor_6_state): """Mock a multisensor 6 node.""" diff --git a/tests/components/zwave_js/test_button.py b/tests/components/zwave_js/test_button.py index 336d3688988..27bd41b3142 100644 --- a/tests/components/zwave_js/test_button.py +++ b/tests/components/zwave_js/test_button.py @@ -10,7 +10,6 @@ async def test_ping_entity( hass, client, climate_radio_thermostat_ct100_plus_different_endpoints, - controller_node, integration, caplog, ): diff --git a/tests/components/zwave_js/test_device_action.py b/tests/components/zwave_js/test_device_action.py index ad9d61b3f33..a60f10761b6 100644 --- a/tests/components/zwave_js/test_device_action.py +++ b/tests/components/zwave_js/test_device_action.py @@ -91,6 +91,16 @@ async def test_get_actions( for action in expected_actions: assert action in actions + # Test that we don't return actions for a controller node + device = dev_reg.async_get_device( + {get_device_id(driver, client.driver.controller.nodes[1])} + ) + assert device + assert ( + await async_get_device_automations(hass, DeviceAutomationType.ACTION, device.id) + == [] + ) + async def test_get_actions_meter( hass: HomeAssistant, @@ -408,9 +418,10 @@ async def test_get_action_capabilities( ): """Test we get the expected action capabilities.""" dev_reg = device_registry.async_get(hass) - device = device_registry.async_entries_for_config_entry( - dev_reg, integration.entry_id - )[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, climate_radio_thermostat_ct100_plus)} + ) + assert device # Test refresh_value capabilities = await device_action.async_get_action_capabilities( diff --git a/tests/components/zwave_js/test_device_condition.py b/tests/components/zwave_js/test_device_condition.py index 8b538a3c48b..b42573f3aa9 100644 --- a/tests/components/zwave_js/test_device_condition.py +++ b/tests/components/zwave_js/test_device_condition.py @@ -15,7 +15,10 @@ from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) from homeassistant.components.zwave_js import DOMAIN, device_condition -from homeassistant.components.zwave_js.helpers import get_zwave_value_from_config +from homeassistant.components.zwave_js.helpers import ( + get_device_id, + get_zwave_value_from_config, +) from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv, device_registry from homeassistant.setup import async_setup_component @@ -32,9 +35,10 @@ def calls(hass): async def test_get_conditions(hass, client, lock_schlage_be469, integration) -> None: """Test we get the expected onditions from a zwave_js.""" dev_reg = device_registry.async_get(hass) - device = device_registry.async_entries_for_config_entry( - dev_reg, integration.entry_id - )[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device config_value = list(lock_schlage_be469.get_configuration_values().values())[0] value_id = config_value.value_id name = config_value.property_name @@ -70,15 +74,28 @@ async def test_get_conditions(hass, client, lock_schlage_be469, integration) -> for condition in expected_conditions: assert condition in conditions + # Test that we don't return actions for a controller node + device = dev_reg.async_get_device( + {get_device_id(client.driver, client.driver.controller.nodes[1])} + ) + assert device + assert ( + await async_get_device_automations( + hass, DeviceAutomationType.CONDITION, device.id + ) + == [] + ) + async def test_node_status_state( hass, client, lock_schlage_be469, integration, calls ) -> None: """Test for node_status conditions.""" dev_reg = device_registry.async_get(hass) - device = device_registry.async_entries_for_config_entry( - dev_reg, integration.entry_id - )[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device assert await async_setup_component( hass, @@ -224,9 +241,10 @@ async def test_config_parameter_state( ) -> None: """Test for config_parameter conditions.""" dev_reg = device_registry.async_get(hass) - device = device_registry.async_entries_for_config_entry( - dev_reg, integration.entry_id - )[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device assert await async_setup_component( hass, @@ -333,9 +351,10 @@ async def test_value_state( ) -> None: """Test for value conditions.""" dev_reg = device_registry.async_get(hass) - device = device_registry.async_entries_for_config_entry( - dev_reg, integration.entry_id - )[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device assert await async_setup_component( hass, @@ -377,9 +396,10 @@ async def test_get_condition_capabilities_node_status( ): """Test we don't get capabilities from a node_status condition.""" dev_reg = device_registry.async_get(hass) - device = device_registry.async_entries_for_config_entry( - dev_reg, integration.entry_id - )[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device capabilities = await device_condition.async_get_condition_capabilities( hass, @@ -413,9 +433,10 @@ async def test_get_condition_capabilities_value( ): """Test we get the expected capabilities from a value condition.""" dev_reg = device_registry.async_get(hass) - device = device_registry.async_entries_for_config_entry( - dev_reg, integration.entry_id - )[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device capabilities = await device_condition.async_get_condition_capabilities( hass, @@ -462,9 +483,10 @@ async def test_get_condition_capabilities_config_parameter( """Test we get the expected capabilities from a config_parameter condition.""" node = climate_radio_thermostat_ct100_plus dev_reg = device_registry.async_get(hass) - device = device_registry.async_entries_for_config_entry( - dev_reg, integration.entry_id - )[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, climate_radio_thermostat_ct100_plus)} + ) + assert device # Test enumerated type param capabilities = await device_condition.async_get_condition_capabilities( @@ -541,9 +563,10 @@ async def test_get_condition_capabilities_config_parameter( async def test_failure_scenarios(hass, client, hank_binary_switch, integration): """Test failure scenarios.""" dev_reg = device_registry.async_get(hass) - device = device_registry.async_entries_for_config_entry( - dev_reg, integration.entry_id - )[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, hank_binary_switch)} + ) + assert device with pytest.raises(HomeAssistantError): await device_condition.async_condition_from_config( diff --git a/tests/components/zwave_js/test_device_trigger.py b/tests/components/zwave_js/test_device_trigger.py index 84d87efed63..e9bc319fe4d 100644 --- a/tests/components/zwave_js/test_device_trigger.py +++ b/tests/components/zwave_js/test_device_trigger.py @@ -15,13 +15,11 @@ from homeassistant.components.device_automation.exceptions import ( from homeassistant.components.zwave_js import DOMAIN, device_trigger from homeassistant.components.zwave_js.helpers import ( async_get_node_status_sensor_entity_id, + get_device_id, ) from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.device_registry import ( - async_entries_for_config_entry, - async_get as async_get_dev_reg, -) +from homeassistant.helpers.device_registry import async_get as async_get_dev_reg from homeassistant.helpers.entity_registry import async_get as async_get_ent_reg from homeassistant.setup import async_setup_component @@ -38,12 +36,30 @@ def calls(hass): return async_mock_service(hass, "test", "automation") +async def test_no_controller_triggers(hass, client, integration): + """Test that we do not get triggers for the controller.""" + dev_reg = async_get_dev_reg(hass) + device = dev_reg.async_get_device( + {get_device_id(client.driver, client.driver.controller.nodes[1])} + ) + assert device + assert ( + await async_get_device_automations( + hass, DeviceAutomationType.TRIGGER, device.id + ) + == [] + ) + + async def test_get_notification_notification_triggers( hass, client, lock_schlage_be469, integration ): """Test we get the expected triggers from a zwave_js device with the Notification CC.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device expected_trigger = { "platform": "device", "domain": DOMAIN, @@ -64,7 +80,10 @@ async def test_if_notification_notification_fires( """Test for event.notification.notification trigger firing.""" node: Node = lock_schlage_be469 dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device assert await async_setup_component( hass, @@ -157,7 +176,10 @@ async def test_get_trigger_capabilities_notification_notification( ): """Test we get the expected capabilities from a notification.notification trigger.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device capabilities = await device_trigger.async_get_trigger_capabilities( hass, { @@ -189,7 +211,10 @@ async def test_if_entry_control_notification_fires( """Test for notification.entry_control trigger firing.""" node: Node = lock_schlage_be469 dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device assert await async_setup_component( hass, @@ -281,7 +306,10 @@ async def test_get_trigger_capabilities_entry_control_notification( ): """Test we get the expected capabilities from a notification.entry_control trigger.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device capabilities = await device_trigger.async_get_trigger_capabilities( hass, { @@ -308,7 +336,10 @@ async def test_get_trigger_capabilities_entry_control_notification( async def test_get_node_status_triggers(hass, client, lock_schlage_be469, integration): """Test we get the expected triggers from a device with node status sensor enabled.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device ent_reg = async_get_ent_reg(hass) entity_id = async_get_node_status_sensor_entity_id( hass, device.id, ent_reg, dev_reg @@ -337,7 +368,10 @@ async def test_if_node_status_change_fires( """Test for node_status trigger firing.""" node: Node = lock_schlage_be469 dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device ent_reg = async_get_ent_reg(hass) entity_id = async_get_node_status_sensor_entity_id( hass, device.id, ent_reg, dev_reg @@ -412,7 +446,10 @@ async def test_get_trigger_capabilities_node_status( ): """Test we get the expected capabilities from a node_status trigger.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device ent_reg = async_get_ent_reg(hass) entity_id = async_get_node_status_sensor_entity_id( hass, device.id, ent_reg, dev_reg @@ -467,7 +504,10 @@ async def test_get_basic_value_notification_triggers( ): """Test we get the expected triggers from a zwave_js device with the Basic CC.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, ge_in_wall_dimmer_switch)} + ) + assert device expected_trigger = { "platform": "device", "domain": DOMAIN, @@ -492,7 +532,10 @@ async def test_if_basic_value_notification_fires( """Test for event.value_notification.basic trigger firing.""" node: Node = ge_in_wall_dimmer_switch dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, ge_in_wall_dimmer_switch)} + ) + assert device assert await async_setup_component( hass, @@ -600,7 +643,10 @@ async def test_get_trigger_capabilities_basic_value_notification( ): """Test we get the expected capabilities from a value_notification.basic trigger.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, ge_in_wall_dimmer_switch)} + ) + assert device capabilities = await device_trigger.async_get_trigger_capabilities( hass, { @@ -635,7 +681,10 @@ async def test_get_central_scene_value_notification_triggers( ): """Test we get the expected triggers from a zwave_js device with the Central Scene CC.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, wallmote_central_scene)} + ) + assert device expected_trigger = { "platform": "device", "domain": DOMAIN, @@ -660,7 +709,10 @@ async def test_if_central_scene_value_notification_fires( """Test for event.value_notification.central_scene trigger firing.""" node: Node = wallmote_central_scene dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, wallmote_central_scene)} + ) + assert device assert await async_setup_component( hass, @@ -775,7 +827,10 @@ async def test_get_trigger_capabilities_central_scene_value_notification( ): """Test we get the expected capabilities from a value_notification.central_scene trigger.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, wallmote_central_scene)} + ) + assert device capabilities = await device_trigger.async_get_trigger_capabilities( hass, { @@ -809,7 +864,10 @@ async def test_get_scene_activation_value_notification_triggers( ): """Test we get the expected triggers from a zwave_js device with the SceneActivation CC.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, hank_binary_switch)} + ) + assert device expected_trigger = { "platform": "device", "domain": DOMAIN, @@ -834,7 +892,10 @@ async def test_if_scene_activation_value_notification_fires( """Test for event.value_notification.scene_activation trigger firing.""" node: Node = hank_binary_switch dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, hank_binary_switch)} + ) + assert device assert await async_setup_component( hass, @@ -942,7 +1003,10 @@ async def test_get_trigger_capabilities_scene_activation_value_notification( ): """Test we get the expected capabilities from a value_notification.scene_activation trigger.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, hank_binary_switch)} + ) + assert device capabilities = await device_trigger.async_get_trigger_capabilities( hass, { @@ -977,7 +1041,10 @@ async def test_get_value_updated_value_triggers( ): """Test we get the zwave_js.value_updated.value trigger from a zwave_js device.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device expected_trigger = { "platform": "device", "domain": DOMAIN, @@ -997,7 +1064,10 @@ async def test_if_value_updated_value_fires( """Test for zwave_js.value_updated.value trigger firing.""" node: Node = lock_schlage_be469 dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device assert await async_setup_component( hass, @@ -1086,7 +1156,10 @@ async def test_value_updated_value_no_driver( """Test zwave_js.value_updated.value trigger with missing driver.""" node: Node = lock_schlage_be469 dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device driver = client.driver client.driver = None @@ -1153,7 +1226,10 @@ async def test_get_trigger_capabilities_value_updated_value( ): """Test we get the expected capabilities from a zwave_js.value_updated.value trigger.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device capabilities = await device_trigger.async_get_trigger_capabilities( hass, { @@ -1201,7 +1277,10 @@ async def test_get_value_updated_config_parameter_triggers( ): """Test we get the zwave_js.value_updated.config_parameter trigger from a zwave_js device.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device expected_trigger = { "platform": "device", "domain": DOMAIN, @@ -1226,7 +1305,10 @@ async def test_if_value_updated_config_parameter_fires( """Test for zwave_js.value_updated.config_parameter trigger firing.""" node: Node = lock_schlage_be469 dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device assert await async_setup_component( hass, @@ -1293,7 +1375,10 @@ async def test_get_trigger_capabilities_value_updated_config_parameter_range( ): """Test we get the expected capabilities from a range zwave_js.value_updated.config_parameter trigger.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device capabilities = await device_trigger.async_get_trigger_capabilities( hass, { @@ -1335,7 +1420,10 @@ async def test_get_trigger_capabilities_value_updated_config_parameter_enumerate ): """Test we get the expected capabilities from an enumerated zwave_js.value_updated.config_parameter trigger.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device capabilities = await device_trigger.async_get_trigger_capabilities( hass, { @@ -1386,7 +1474,10 @@ async def test_failure_scenarios(hass, client, hank_binary_switch, integration): ) dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, hank_binary_switch)} + ) + assert device with pytest.raises(HomeAssistantError): await device_trigger.async_attach_trigger( diff --git a/tests/components/zwave_js/test_init.py b/tests/components/zwave_js/test_init.py index 4f58c87febb..0d22484bea6 100644 --- a/tests/components/zwave_js/test_init.py +++ b/tests/components/zwave_js/test_init.py @@ -199,7 +199,7 @@ async def test_on_node_added_not_ready( device_id = f"{client.driver.controller.home_id}-{zp3111_not_ready_state['nodeId']}" assert len(hass.states.async_all()) == 0 - assert not dev_reg.devices + assert len(dev_reg.devices) == 1 node_state = deepcopy(zp3111_not_ready_state) node_state["isSecure"] = False @@ -911,12 +911,12 @@ async def test_removed_device( driver = client.driver assert driver # Verify how many nodes are available - assert len(driver.controller.nodes) == 2 + assert len(driver.controller.nodes) == 3 # Make sure there are the same number of devices dev_reg = dr.async_get(hass) device_entries = dr.async_entries_for_config_entry(dev_reg, integration.entry_id) - assert len(device_entries) == 2 + assert len(device_entries) == 3 # Check how many entities there are ent_reg = er.async_get(hass) @@ -931,7 +931,7 @@ async def test_removed_device( # Assert that the node and all of it's entities were removed from the device and # entity registry device_entries = dr.async_entries_for_config_entry(dev_reg, integration.entry_id) - assert len(device_entries) == 1 + assert len(device_entries) == 2 entity_entries = er.async_entries_for_config_entry(ent_reg, integration.entry_id) assert len(entity_entries) == 18 assert dev_reg.async_get_device({get_device_id(driver, old_node)}) is None diff --git a/tests/components/zwave_js/test_sensor.py b/tests/components/zwave_js/test_sensor.py index 848c4d7b0e5..a32537b1d0d 100644 --- a/tests/components/zwave_js/test_sensor.py +++ b/tests/components/zwave_js/test_sensor.py @@ -156,9 +156,7 @@ async def test_config_parameter_sensor(hass, lock_id_lock_as_id150, integration) assert entity_entry.disabled -async def test_node_status_sensor( - hass, client, controller_node, lock_id_lock_as_id150, integration -): +async def test_node_status_sensor(hass, client, lock_id_lock_as_id150, integration): """Test node status sensor is created and gets updated on node state changes.""" NODE_STATUS_ENTITY = "sensor.z_wave_module_for_id_lock_150_and_101_node_status" node = lock_id_lock_as_id150 diff --git a/tests/components/zwave_js/test_services.py b/tests/components/zwave_js/test_services.py index 6e425bff042..710892c4741 100644 --- a/tests/components/zwave_js/test_services.py +++ b/tests/components/zwave_js/test_services.py @@ -34,10 +34,7 @@ from homeassistant.components.zwave_js.helpers import get_device_id from homeassistant.const import ATTR_AREA_ID, ATTR_DEVICE_ID, ATTR_ENTITY_ID from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.area_registry import async_get as async_get_area_reg -from homeassistant.helpers.device_registry import ( - async_entries_for_config_entry, - async_get as async_get_dev_reg, -) +from homeassistant.helpers.device_registry import async_get as async_get_dev_reg from homeassistant.helpers.entity_registry import async_get as async_get_ent_reg from homeassistant.setup import async_setup_component @@ -408,7 +405,8 @@ async def test_set_config_parameter_gather( async def test_bulk_set_config_parameters(hass, client, multisensor_6, integration): """Test the bulk_set_partial_config_parameters service.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device({get_device_id(client.driver, multisensor_6)}) + assert device # Test setting config parameter by property and property_key await hass.services.async_call( DOMAIN, @@ -736,7 +734,10 @@ async def test_refresh_value( async def test_set_value(hass, client, climate_danfoss_lc_13, integration): """Test set_value service.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, climate_danfoss_lc_13)} + ) + assert device await hass.services.async_call( DOMAIN, diff --git a/tests/components/zwave_js/test_trigger.py b/tests/components/zwave_js/test_trigger.py index b3b6910e5f5..56a8e63b439 100644 --- a/tests/components/zwave_js/test_trigger.py +++ b/tests/components/zwave_js/test_trigger.py @@ -9,15 +9,13 @@ from zwave_js_server.model.node import Node from homeassistant.components import automation from homeassistant.components.zwave_js import DOMAIN +from homeassistant.components.zwave_js.helpers import get_device_id from homeassistant.components.zwave_js.trigger import async_validate_trigger_config from homeassistant.components.zwave_js.triggers.trigger_helpers import ( async_bypass_dynamic_config_validation, ) from homeassistant.const import SERVICE_RELOAD -from homeassistant.helpers.device_registry import ( - async_entries_for_config_entry, - async_get as async_get_dev_reg, -) +from homeassistant.helpers.device_registry import async_get as async_get_dev_reg from homeassistant.setup import async_setup_component from .common import SCHLAGE_BE469_LOCK_ENTITY @@ -30,7 +28,10 @@ async def test_zwave_js_value_updated(hass, client, lock_schlage_be469, integrat trigger_type = f"{DOMAIN}.value_updated" node: Node = lock_schlage_be469 dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device no_value_filter = async_capture_events(hass, "no_value_filter") single_from_value_filter = async_capture_events(hass, "single_from_value_filter") @@ -449,7 +450,10 @@ async def test_zwave_js_event(hass, client, lock_schlage_be469, integration): trigger_type = f"{DOMAIN}.event" node: Node = lock_schlage_be469 dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device node_no_event_data_filter = async_capture_events(hass, "node_no_event_data_filter") node_event_data_filter = async_capture_events(hass, "node_event_data_filter") @@ -992,7 +996,10 @@ async def test_zwave_js_trigger_config_entry_unloaded( ): """Test zwave_js triggers bypass dynamic validation when needed.""" dev_reg = async_get_dev_reg(hass) - device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + device = dev_reg.async_get_device( + {get_device_id(client.driver, lock_schlage_be469)} + ) + assert device # Test bypass check is False assert not async_bypass_dynamic_config_validation( diff --git a/tests/components/zwave_js/test_update.py b/tests/components/zwave_js/test_update.py index 4c00c1c9a3a..1650b7e8edd 100644 --- a/tests/components/zwave_js/test_update.py +++ b/tests/components/zwave_js/test_update.py @@ -72,7 +72,6 @@ async def test_update_entity_states( hass, client, climate_radio_thermostat_ct100_plus_different_endpoints, - controller_node, integration, caplog, hass_ws_client, From 54748cec821c800ce71707965edac4119c8008e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Tue, 6 Dec 2022 20:08:27 +0100 Subject: [PATCH 1021/1033] Update tibber lib to 0.26.4, improve logging (#83418) --- homeassistant/components/tibber/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tibber/manifest.json b/homeassistant/components/tibber/manifest.json index 81b3c1274d2..05bfe96b071 100644 --- a/homeassistant/components/tibber/manifest.json +++ b/homeassistant/components/tibber/manifest.json @@ -3,7 +3,7 @@ "domain": "tibber", "name": "Tibber", "documentation": "https://www.home-assistant.io/integrations/tibber", - "requirements": ["pyTibber==0.26.3"], + "requirements": ["pyTibber==0.26.4"], "codeowners": ["@danielhiversen"], "quality_scale": "silver", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index ffd8b562d88..d18865533ac 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1432,7 +1432,7 @@ pyRFXtrx==0.30.0 pySwitchmate==0.5.1 # homeassistant.components.tibber -pyTibber==0.26.3 +pyTibber==0.26.4 # homeassistant.components.dlink pyW215==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c487cf916e6..7e623d368a5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1032,7 +1032,7 @@ pyMetno==0.9.0 pyRFXtrx==0.30.0 # homeassistant.components.tibber -pyTibber==0.26.3 +pyTibber==0.26.4 # homeassistant.components.nextbus py_nextbusnext==0.1.5 From 599236171746f5d115b9bb055a8a036297d6dca5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Dec 2022 17:57:54 -1000 Subject: [PATCH 1022/1033] Do not try to stop the shelly ble scanner if not connected (#83424) Co-authored-by: Shay Levy --- .../components/shelly/coordinator.py | 3 +- tests/components/shelly/test_init.py | 32 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py index 6190181dca9..867cacb3647 100644 --- a/homeassistant/components/shelly/coordinator.py +++ b/homeassistant/components/shelly/coordinator.py @@ -564,7 +564,8 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): async def shutdown(self) -> None: """Shutdown the coordinator.""" - await async_stop_scanner(self.device) + if self.device.connected: + await async_stop_scanner(self.device) await self.device.shutdown() await self._async_disconnected() diff --git a/tests/components/shelly/test_init.py b/tests/components/shelly/test_init.py index 251dc8a8da7..48ea978e5d9 100644 --- a/tests/components/shelly/test_init.py +++ b/tests/components/shelly/test_init.py @@ -1,12 +1,16 @@ """Test cases for the Shelly component.""" from __future__ import annotations -from unittest.mock import AsyncMock +from unittest.mock import AsyncMock, patch from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError import pytest -from homeassistant.components.shelly.const import DOMAIN +from homeassistant.components.shelly.const import ( + CONF_BLE_SCANNER_MODE, + DOMAIN, + BLEScannerMode, +) from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState from homeassistant.const import STATE_ON, STATE_UNAVAILABLE from homeassistant.helpers import device_registry @@ -183,3 +187,27 @@ async def test_entry_unload_device_not_ready( await hass.async_block_till_done() assert entry.state is ConfigEntryState.NOT_LOADED + + +async def test_entry_unload_not_connected(hass, mock_rpc_device, monkeypatch): + """Test entry unload when not connected.""" + with patch( + "homeassistant.components.shelly.coordinator.async_stop_scanner" + ) as mock_stop_scanner: + + entry = await init_integration( + hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} + ) + entity_id = "switch.test_switch_0" + + assert entry.state is ConfigEntryState.LOADED + assert hass.states.get(entity_id).state is STATE_ON + assert not mock_stop_scanner.call_count + + monkeypatch.setattr(mock_rpc_device, "connected", False) + + await hass.config_entries.async_reload(entry.entry_id) + await hass.async_block_till_done() + + assert not mock_stop_scanner.call_count + assert entry.state is ConfigEntryState.LOADED From 8703e14cabad3a99b1679075638ec0abfaa3f9f8 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 6 Dec 2022 22:58:47 -0500 Subject: [PATCH 1023/1033] Bumped version to 2022.12.0b7 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index cd915aee3e0..5d9de18db12 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -8,7 +8,7 @@ from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "0b6" +PATCH_VERSION: Final = "0b7" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/pyproject.toml b/pyproject.toml index 3cf775d2039..d20fe2965c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.12.0b6" +version = "2022.12.0b7" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From 047012d167818e54e7d522178f44596a902a1443 Mon Sep 17 00:00:00 2001 From: mbo18 Date: Wed, 7 Dec 2022 13:13:32 +0100 Subject: [PATCH 1024/1033] Add Child lock support to Tuya devices (#83233) * Add Child lock support to Tuya devices * flake/black * Add attribute to general.py * apply only to TS011F * also update general.py --- .../components/zha/core/channels/general.py | 8 +++++++- homeassistant/components/zha/switch.py | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index 3756e3c1233..fbafbdc03e7 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -348,7 +348,6 @@ class OnOffChannel(ZigbeeChannel): self._off_listener = None if self.cluster.endpoint.model in ( - "TS011F", "TS0121", "TS0001", "TS0002", @@ -360,6 +359,13 @@ class OnOffChannel(ZigbeeChannel): ) self.ZCL_INIT_ATTRS["backlight_mode"] = True self.ZCL_INIT_ATTRS["power_on_state"] = True + elif self.cluster.endpoint.model == "TS011F": + self.ZCL_INIT_ATTRS = ( # pylint: disable=invalid-name + self.ZCL_INIT_ATTRS.copy() + ) + self.ZCL_INIT_ATTRS["backlight_mode"] = True + self.ZCL_INIT_ATTRS["child_lock"] = True + self.ZCL_INIT_ATTRS["power_on_state"] = True @property def on_off(self) -> bool | None: diff --git a/homeassistant/components/zha/switch.py b/homeassistant/components/zha/switch.py index ac285700a27..c9469747a3f 100644 --- a/homeassistant/components/zha/switch.py +++ b/homeassistant/components/zha/switch.py @@ -457,3 +457,15 @@ class AqaraPetFeederChildLock(ZHASwitchConfigurationEntity, id_suffix="child_loc _zcl_attribute: str = "child_lock" _attr_name = "Child lock" _attr_icon: str = "mdi:account-lock" + + +@CONFIG_DIAGNOSTIC_MATCH( + channel_names=CHANNEL_ON_OFF, + models={"TS011F"}, +) +class TuyaChildLockSwitch(ZHASwitchConfigurationEntity, id_suffix="child_lock"): + """Representation of a child lock configuration entity.""" + + _zcl_attribute: str = "child_lock" + _attr_name = "Child lock" + _attr_icon: str = "mdi:account-lock" From 9a15494e695f3f19c614738486cb194a553fd861 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Wed, 7 Dec 2022 08:11:18 +0100 Subject: [PATCH 1025/1033] Fix restored temperature values in Shelly climate platform (#83428) * Set last_target_temp value according the unit system * Convert restored temperature values * Add test * Improve comments * Move _last_target_temp value to constants --- homeassistant/components/shelly/climate.py | 33 +++++++++++++-- homeassistant/components/shelly/const.py | 1 + tests/components/shelly/test_climate.py | 48 ++++++++++++++++++++++ 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index cc831c10ee0..d313d6b5757 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -24,6 +24,8 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.util.unit_conversion import TemperatureConverter +from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM from .const import LOGGER, SHTRV_01_TEMPERATURE_SETTINGS from .coordinator import ShellyBlockCoordinator, get_entry_data @@ -126,7 +128,14 @@ class BlockSleepingClimate( self.last_state: State | None = None self.last_state_attributes: Mapping[str, Any] self._preset_modes: list[str] = [] - self._last_target_temp = 20.0 + if coordinator.hass.config.units is US_CUSTOMARY_SYSTEM: + self._last_target_temp = TemperatureConverter.convert( + SHTRV_01_TEMPERATURE_SETTINGS["default"], + UnitOfTemperature.CELSIUS, + UnitOfTemperature.FAHRENHEIT, + ) + else: + self._last_target_temp = SHTRV_01_TEMPERATURE_SETTINGS["default"] if self.block is not None and self.device_block is not None: self._unique_id = f"{self.coordinator.mac}-{self.block.description}" @@ -157,14 +166,32 @@ class BlockSleepingClimate( """Set target temperature.""" if self.block is not None: return cast(float, self.block.targetTemp) - return self.last_state_attributes.get("temperature") + # The restored value can be in Fahrenheit so we have to convert it to Celsius + # because we use this unit internally in integration. + target_temp = self.last_state_attributes.get("temperature") + if self.hass.config.units is US_CUSTOMARY_SYSTEM and target_temp: + return TemperatureConverter.convert( + cast(float, target_temp), + UnitOfTemperature.FAHRENHEIT, + UnitOfTemperature.CELSIUS, + ) + return target_temp @property def current_temperature(self) -> float | None: """Return current temperature.""" if self.block is not None: return cast(float, self.block.temp) - return self.last_state_attributes.get("current_temperature") + # The restored value can be in Fahrenheit so we have to convert it to Celsius + # because we use this unit internally in integration. + current_temp = self.last_state_attributes.get("current_temperature") + if self.hass.config.units is US_CUSTOMARY_SYSTEM and current_temp: + return TemperatureConverter.convert( + cast(float, current_temp), + UnitOfTemperature.FAHRENHEIT, + UnitOfTemperature.CELSIUS, + ) + return current_temp @property def available(self) -> bool: diff --git a/homeassistant/components/shelly/const.py b/homeassistant/components/shelly/const.py index 81d4bfd7b14..41656bbcd6f 100644 --- a/homeassistant/components/shelly/const.py +++ b/homeassistant/components/shelly/const.py @@ -147,6 +147,7 @@ SHTRV_01_TEMPERATURE_SETTINGS: Final = { "min": 4, "max": 31, "step": 0.5, + "default": 20.0, } # Kelvin value for colorTemp diff --git a/tests/components/shelly/test_climate.py b/tests/components/shelly/test_climate.py index 56effa156e6..0d43ae118cf 100644 --- a/tests/components/shelly/test_climate.py +++ b/tests/components/shelly/test_climate.py @@ -21,6 +21,7 @@ from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_UNAVAILABLE from homeassistant.core import State from homeassistant.exceptions import HomeAssistantError +from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM from . import init_integration, register_device, register_entity @@ -212,6 +213,53 @@ async def test_block_restored_climate(hass, mock_block_device, device_reg, monke assert hass.states.get(entity_id).state == HVACMode.OFF +async def test_block_restored_climate_us_customery( + hass, mock_block_device, device_reg, monkeypatch +): + """Test block restored climate with US CUSTOMATY unit system.""" + hass.config.units = US_CUSTOMARY_SYSTEM + monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp") + monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "valveError", 0) + entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True) + register_device(device_reg, entry) + entity_id = register_entity( + hass, + CLIMATE_DOMAIN, + "test_name", + "sensor_0", + entry, + ) + attrs = {"current_temperature": 67, "temperature": 68} + mock_restore_cache(hass, [State(entity_id, HVACMode.HEAT, attributes=attrs)]) + + monkeypatch.setattr(mock_block_device, "initialized", False) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == HVACMode.HEAT + assert hass.states.get(entity_id).attributes.get("temperature") == 68 + assert hass.states.get(entity_id).attributes.get("current_temperature") == 67 + + # Partial update, should not change state + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == HVACMode.HEAT + assert hass.states.get(entity_id).attributes.get("temperature") == 68 + assert hass.states.get(entity_id).attributes.get("current_temperature") == 67 + + # Make device online + monkeypatch.setattr(mock_block_device, "initialized", True) + monkeypatch.setattr(mock_block_device.blocks[SENSOR_BLOCK_ID], "targetTemp", 19.7) + monkeypatch.setattr(mock_block_device.blocks[SENSOR_BLOCK_ID], "temp", 18.2) + mock_block_device.mock_update() + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == HVACMode.HEAT + assert hass.states.get(entity_id).attributes.get("temperature") == 67 + assert hass.states.get(entity_id).attributes.get("current_temperature") == 65 + + async def test_block_restored_climate_unavailable( hass, mock_block_device, device_reg, monkeypatch ): From 886525112b5c2541a857fafa1e7768f02ec14d26 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 7 Dec 2022 16:46:28 +0100 Subject: [PATCH 1026/1033] Remove doubtful repairs issue from UniFi Protect (#83463) --- .../components/unifiprotect/migrate.py | 22 +------- .../components/unifiprotect/strings.json | 4 -- .../unifiprotect/translations/en.json | 4 -- tests/components/unifiprotect/test_repairs.py | 54 +------------------ 4 files changed, 2 insertions(+), 82 deletions(-) diff --git a/homeassistant/components/unifiprotect/migrate.py b/homeassistant/components/unifiprotect/migrate.py index b40f78d0ccf..893ca3e458a 100644 --- a/homeassistant/components/unifiprotect/migrate.py +++ b/homeassistant/components/unifiprotect/migrate.py @@ -12,10 +12,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers import entity_registry as er, issue_registry as ir -from homeassistant.helpers.issue_registry import IssueSeverity - -from .const import DOMAIN +from homeassistant.helpers import entity_registry as er _LOGGER = logging.getLogger(__name__) @@ -33,23 +30,6 @@ async def async_migrate_data( await async_migrate_device_ids(hass, entry, protect) _LOGGER.debug("Completed Migrate: async_migrate_device_ids") - entity_registry = er.async_get(hass) - for entity in er.async_entries_for_config_entry(entity_registry, entry.entry_id): - if ( - entity.domain == Platform.SENSOR - and entity.disabled_by is None - and "detected_object" in entity.unique_id - ): - ir.async_create_issue( - hass, - DOMAIN, - "deprecate_smart_sensor", - is_fixable=False, - breaks_in_ha_version="2023.2.0", - severity=IssueSeverity.WARNING, - translation_key="deprecate_smart_sensor", - ) - async def async_get_bootstrap(protect: ProtectApiClient) -> Bootstrap: """Get UniFi Protect bootstrap or raise appropriate HA error.""" diff --git a/homeassistant/components/unifiprotect/strings.json b/homeassistant/components/unifiprotect/strings.json index 8bdd844fdbf..abac7701279 100644 --- a/homeassistant/components/unifiprotect/strings.json +++ b/homeassistant/components/unifiprotect/strings.json @@ -75,10 +75,6 @@ "ea_setup_failed": { "title": "Setup error using Early Access version", "description": "You are using v{version} of UniFi Protect which is an Early Access version. An unrecoverable error occurred while trying to load the integration. Please [downgrade to a stable version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) of UniFi Protect to continue using the integration.\n\nError: {error}" - }, - "deprecate_smart_sensor": { - "title": "Smart Detection Sensor Deprecated", - "description": "The unified \"Detected Object\" sensor for smart detections is now deprecated. It has been replaced with individual smart detection binary sensors for each smart detection type. Please update any templates or automations accordingly." } } } diff --git a/homeassistant/components/unifiprotect/translations/en.json b/homeassistant/components/unifiprotect/translations/en.json index 21b3cd64360..65a398375fe 100644 --- a/homeassistant/components/unifiprotect/translations/en.json +++ b/homeassistant/components/unifiprotect/translations/en.json @@ -42,10 +42,6 @@ } }, "issues": { - "deprecate_smart_sensor": { - "description": "The unified \"Detected Object\" sensor for smart detections is now deprecated. It has been replaced with individual smart detection binary sensors for each smart detection type. Please update any templates or automations accordingly.", - "title": "Smart Detection Sensor Deprecated" - }, "ea_setup_failed": { "description": "You are using v{version} of UniFi Protect which is an Early Access version. An unrecoverable error occurred while trying to load the integration. Please [downgrade to a stable version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) of UniFi Protect to continue using the integration.\n\nError: {error}", "title": "Setup error using Early Access version" diff --git a/tests/components/unifiprotect/test_repairs.py b/tests/components/unifiprotect/test_repairs.py index a1279dbed84..3ffd2ea4a43 100644 --- a/tests/components/unifiprotect/test_repairs.py +++ b/tests/components/unifiprotect/test_repairs.py @@ -6,7 +6,7 @@ from copy import copy from http import HTTPStatus from unittest.mock import Mock -from pyunifiprotect.data import Camera, Version +from pyunifiprotect.data import Version from homeassistant.components.repairs.issue_handler import ( async_process_repairs_platforms, @@ -16,9 +16,7 @@ from homeassistant.components.repairs.websocket_api import ( RepairsFlowResourceView, ) from homeassistant.components.unifiprotect.const import DOMAIN -from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er from .utils import MockUFPFixture, init_entry @@ -126,53 +124,3 @@ async def test_ea_warning_fix( data = await resp.json() assert data["type"] == "create_entry" - - -async def test_deprecate_smart_default( - hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera -): - """Test Deprecate Sensor repair does not exist by default (new installs).""" - - await init_entry(hass, ufp, [doorbell]) - - await async_process_repairs_platforms(hass) - ws_client = await hass_ws_client(hass) - - await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) - msg = await ws_client.receive_json() - - assert msg["success"] - issue = None - for i in msg["result"]["issues"]: - if i["issue_id"] == "deprecate_smart_sensor": - issue = i - assert issue is None - - -async def test_deprecate_smart_active( - hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera -): - """Test Deprecate Sensor repair exists for existing installs.""" - - registry = er.async_get(hass) - registry.async_get_or_create( - Platform.SENSOR, - DOMAIN, - f"{doorbell.mac}_detected_object", - config_entry=ufp.entry, - ) - - await init_entry(hass, ufp, [doorbell]) - - await async_process_repairs_platforms(hass) - ws_client = await hass_ws_client(hass) - - await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) - msg = await ws_client.receive_json() - - assert msg["success"] - issue = None - for i in msg["result"]["issues"]: - if i["issue_id"] == "deprecate_smart_sensor": - issue = i - assert issue is not None From ebf133ef802dd20c295db61ec2ed66e875c0195a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 7 Dec 2022 13:17:45 +0100 Subject: [PATCH 1027/1033] Bump hass-nabucasa from 0.59.0 to 0.61.0 (#83466) --- homeassistant/components/cloud/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/cloud/manifest.json b/homeassistant/components/cloud/manifest.json index 23d72f7cb03..70049e2a426 100644 --- a/homeassistant/components/cloud/manifest.json +++ b/homeassistant/components/cloud/manifest.json @@ -2,7 +2,7 @@ "domain": "cloud", "name": "Home Assistant Cloud", "documentation": "https://www.home-assistant.io/integrations/cloud", - "requirements": ["hass-nabucasa==0.59.0"], + "requirements": ["hass-nabucasa==0.61.0"], "dependencies": ["http", "webhook"], "after_dependencies": ["google_assistant", "alexa"], "codeowners": ["@home-assistant/cloud"], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 406b9972aca..7fc929ff2c8 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -20,7 +20,7 @@ ciso8601==2.2.0 cryptography==38.0.3 dbus-fast==1.75.0 fnvhash==0.1.0 -hass-nabucasa==0.59.0 +hass-nabucasa==0.61.0 home-assistant-bluetooth==1.8.1 home-assistant-frontend==20221206.0 httpx==0.23.1 diff --git a/requirements_all.txt b/requirements_all.txt index d18865533ac..9b7c3816d8a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -848,7 +848,7 @@ ha-philipsjs==2.9.0 habitipy==0.2.0 # homeassistant.components.cloud -hass-nabucasa==0.59.0 +hass-nabucasa==0.61.0 # homeassistant.components.splunk hass_splunk==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7e623d368a5..06891e4cf24 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -640,7 +640,7 @@ ha-philipsjs==2.9.0 habitipy==0.2.0 # homeassistant.components.cloud -hass-nabucasa==0.59.0 +hass-nabucasa==0.61.0 # homeassistant.components.tasmota hatasmota==0.6.1 From f97795fbb94bef4fa0a639542df1e23df1db3bba Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Wed, 7 Dec 2022 10:48:13 -0500 Subject: [PATCH 1028/1033] Bump the ZHA quirks lib to 0.0.88 (#83468) --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 8493d8678a7..50a30142bc5 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -7,7 +7,7 @@ "bellows==0.34.5", "pyserial==3.5", "pyserial-asyncio==0.6", - "zha-quirks==0.0.87", + "zha-quirks==0.0.88", "zigpy-deconz==0.19.2", "zigpy==0.52.3", "zigpy-xbee==0.16.2", diff --git a/requirements_all.txt b/requirements_all.txt index 9b7c3816d8a..81085056c00 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2639,7 +2639,7 @@ zengge==0.2 zeroconf==0.39.4 # homeassistant.components.zha -zha-quirks==0.0.87 +zha-quirks==0.0.88 # homeassistant.components.zhong_hong zhong_hong_hvac==1.0.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 06891e4cf24..f9b6609d03a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1840,7 +1840,7 @@ zamg==0.1.1 zeroconf==0.39.4 # homeassistant.components.zha -zha-quirks==0.0.87 +zha-quirks==0.0.88 # homeassistant.components.zha zigpy-deconz==0.19.2 From 141f37504d95a507b56b2457dff810fe763b8b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 7 Dec 2022 16:48:34 +0100 Subject: [PATCH 1029/1033] When an account exist without a subscription "provider" will not exist (#83472) --- homeassistant/components/cloud/repairs.py | 2 +- tests/components/cloud/test_repairs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/cloud/repairs.py b/homeassistant/components/cloud/repairs.py index 779c0eb64b0..0d217521c21 100644 --- a/homeassistant/components/cloud/repairs.py +++ b/homeassistant/components/cloud/repairs.py @@ -30,7 +30,7 @@ def async_manage_legacy_subscription_issue( If the provider is "legacy" create an issue, in all other cases remove the issue. """ - if subscription_info["provider"] == "legacy": + if subscription_info.get("provider") == "legacy": ir.async_create_issue( hass=hass, domain=DOMAIN, diff --git a/tests/components/cloud/test_repairs.py b/tests/components/cloud/test_repairs.py index ef96efaa402..052cdde0d0d 100644 --- a/tests/components/cloud/test_repairs.py +++ b/tests/components/cloud/test_repairs.py @@ -70,7 +70,7 @@ async def test_legacy_subscription_delete_issue_if_no_longer_legacy( domain="cloud", issue_id="legacy_subscription" ) - cloud_repairs.async_manage_legacy_subscription_issue(hass, {"provider": None}) + cloud_repairs.async_manage_legacy_subscription_issue(hass, {}) assert not issue_registry.async_get_issue( domain="cloud", issue_id="legacy_subscription" ) From 1631d10365678e9dd7a66b92cc3c135a6788b63c Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 7 Dec 2022 16:53:08 +0100 Subject: [PATCH 1030/1033] Cleanup ZHA initialization for TS011F child_lock (#83478) --- homeassistant/components/zha/core/channels/general.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index fbafbdc03e7..47d0cafb01c 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -348,6 +348,7 @@ class OnOffChannel(ZigbeeChannel): self._off_listener = None if self.cluster.endpoint.model in ( + "TS011F", "TS0121", "TS0001", "TS0002", @@ -359,13 +360,8 @@ class OnOffChannel(ZigbeeChannel): ) self.ZCL_INIT_ATTRS["backlight_mode"] = True self.ZCL_INIT_ATTRS["power_on_state"] = True - elif self.cluster.endpoint.model == "TS011F": - self.ZCL_INIT_ATTRS = ( # pylint: disable=invalid-name - self.ZCL_INIT_ATTRS.copy() - ) - self.ZCL_INIT_ATTRS["backlight_mode"] = True - self.ZCL_INIT_ATTRS["child_lock"] = True - self.ZCL_INIT_ATTRS["power_on_state"] = True + if self.cluster.endpoint.model == "TS011F": + self.ZCL_INIT_ATTRS["child_lock"] = True @property def on_off(self) -> bool | None: From 8553faf3c891b6b789b05a869a1c4b071bc5194e Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 7 Dec 2022 17:46:42 +0100 Subject: [PATCH 1031/1033] Update frontend to 20221207.0 (#83479) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 3093928f21f..83514976793 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20221206.0"], + "requirements": ["home-assistant-frontend==20221207.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 7fc929ff2c8..367eb4f6a8a 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -22,7 +22,7 @@ dbus-fast==1.75.0 fnvhash==0.1.0 hass-nabucasa==0.61.0 home-assistant-bluetooth==1.8.1 -home-assistant-frontend==20221206.0 +home-assistant-frontend==20221207.0 httpx==0.23.1 ifaddr==0.1.7 janus==1.0.0 diff --git a/requirements_all.txt b/requirements_all.txt index 81085056c00..58decaa1158 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -884,7 +884,7 @@ hole==0.7.0 holidays==0.17.2 # homeassistant.components.frontend -home-assistant-frontend==20221206.0 +home-assistant-frontend==20221207.0 # homeassistant.components.home_connect homeconnect==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f9b6609d03a..4f1a6bc31bf 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -664,7 +664,7 @@ hole==0.7.0 holidays==0.17.2 # homeassistant.components.frontend -home-assistant-frontend==20221206.0 +home-assistant-frontend==20221207.0 # homeassistant.components.home_connect homeconnect==0.7.2 From 54dd55645949609138eca2275fa67c20adcfd522 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 7 Dec 2022 17:49:50 +0100 Subject: [PATCH 1032/1033] Bumped version to 2022.12.0 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 5d9de18db12..70b1f7799e6 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -8,7 +8,7 @@ from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "0b7" +PATCH_VERSION: Final = "0" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/pyproject.toml b/pyproject.toml index d20fe2965c2..3c65b01d26d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.12.0b7" +version = "2022.12.0" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From 92bc93466eb5e5bfdf259a8e5a317a43198be71d Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 7 Dec 2022 12:45:16 -0500 Subject: [PATCH 1033/1033] Fix missing Shelly `UnitOfTemperature` import (#83483) --- homeassistant/components/shelly/climate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index d313d6b5757..a624ba341af 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -16,7 +16,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant, State, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import device_registry, entity_registry @@ -110,7 +110,7 @@ class BlockSleepingClimate( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) _attr_target_temperature_step = SHTRV_01_TEMPERATURE_SETTINGS["step"] - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__( self,